Update V8 to r4588
We're using WebKit r58033, as used by
http://src.chromium.org/svn/releases/5.0.387.0/DEPS
This requires http://v8.googlecode.com/svn/trunk@4465 but this version has a
crashing bug for ARM. Instead we use http://v8.googlecode.com/svn/trunk@4588,
which is used by http://src.chromium.org/svn/releases/6.0.399.0/DEPS
Note that a trivial bug fix was required in arm/codegen-arm.cc. This is guarded
with ANDROID. See http://code.google.com/p/v8/issues/detail?id=703
Change-Id: I459647a8286c4f8c7405f0c5581ecbf051a6f1e8
diff --git a/src/ia32/assembler-ia32-inl.h b/src/ia32/assembler-ia32-inl.h
index 69f2a8d..6dc584e 100644
--- a/src/ia32/assembler-ia32-inl.h
+++ b/src/ia32/assembler-ia32-inl.h
@@ -314,6 +314,12 @@
}
+Operand::Operand(XMMRegister xmm_reg) {
+ Register reg = { xmm_reg.code() };
+ set_modrm(3, reg);
+}
+
+
Operand::Operand(int32_t disp, RelocInfo::Mode rmode) {
// [disp/r]
set_modrm(0, ebp);
diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc
index 89708aa..26e40b1 100644
--- a/src/ia32/assembler-ia32.cc
+++ b/src/ia32/assembler-ia32.cc
@@ -123,8 +123,8 @@
Code::ComputeFlags(Code::STUB),
Handle<Code>::null());
if (!code->IsCode()) return;
- LOG(CodeCreateEvent(Logger::BUILTIN_TAG,
- Code::cast(code), "CpuFeatures::Probe"));
+ PROFILE(CodeCreateEvent(Logger::BUILTIN_TAG,
+ Code::cast(code), "CpuFeatures::Probe"));
typedef uint64_t (*F0)();
F0 probe = FUNCTION_CAST<F0>(Code::cast(code)->entry());
supported_ = probe();
@@ -753,6 +753,13 @@
}
+void Assembler::cld() {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xFC);
+}
+
+
void Assembler::rep_movs() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -761,6 +768,14 @@
}
+void Assembler::rep_stos() {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xF3);
+ EMIT(0xAB);
+}
+
+
void Assembler::xchg(Register dst, Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -2035,6 +2050,17 @@
}
+void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xF3);
+ EMIT(0x0F);
+ EMIT(0x5A);
+ emit_sse_operand(dst, src);
+}
+
+
void Assembler::addsd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
@@ -2090,6 +2116,16 @@
}
+void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x51);
+ emit_sse_operand(dst, src);
+}
+
+
void Assembler::comisd(XMMRegister dst, XMMRegister src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
@@ -2101,6 +2137,28 @@
}
+void Assembler::ucomisd(XMMRegister dst, XMMRegister src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x2E);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movmskpd(Register dst, XMMRegister src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x50);
+ emit_sse_operand(dst, src);
+}
+
+
void Assembler::movdqa(const Operand& dst, XMMRegister src ) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
@@ -2180,6 +2238,50 @@
emit_sse_operand(dst, src);
}
+void Assembler::movsd(XMMRegister dst, XMMRegister src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xF2);
+ EMIT(0x0F);
+ EMIT(0x10);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::movd(XMMRegister dst, const Operand& src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x6E);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::pxor(XMMRegister dst, XMMRegister src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0xEF);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::ptest(XMMRegister dst, XMMRegister src) {
+ ASSERT(CpuFeatures::IsEnabled(SSE2));
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0x66);
+ EMIT(0x0F);
+ EMIT(0x38);
+ EMIT(0x17);
+ emit_sse_operand(dst, src);
+}
+
void Assembler::emit_sse_operand(XMMRegister reg, const Operand& adr) {
Register ireg = { reg.code() };
@@ -2192,6 +2294,11 @@
}
+void Assembler::emit_sse_operand(Register dst, XMMRegister src) {
+ EMIT(0xC0 | dst.code() << 3 | src.code());
+}
+
+
void Assembler::Print() {
Disassembler::Decode(stdout, buffer_, pc_);
}
diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h
index 36aad5e..6a7effd 100644
--- a/src/ia32/assembler-ia32.h
+++ b/src/ia32/assembler-ia32.h
@@ -93,7 +93,7 @@
struct XMMRegister {
- bool is_valid() const { return 0 <= code_ && code_ < 2; } // currently
+ bool is_valid() const { return 0 <= code_ && code_ < 8; }
int code() const {
ASSERT(is_valid());
return code_;
@@ -241,6 +241,9 @@
// reg
INLINE(explicit Operand(Register reg));
+ // XMM reg
+ INLINE(explicit Operand(XMMRegister xmm_reg));
+
// [disp/r]
INLINE(explicit Operand(int32_t disp, RelocInfo::Mode rmode));
// disp only must always be relocated
@@ -542,8 +545,12 @@
void cmov(Condition cc, Register dst, Handle<Object> handle);
void cmov(Condition cc, Register dst, const Operand& src);
+ // Flag management.
+ void cld();
+
// Repetitive string instructions.
void rep_movs();
+ void rep_stos();
// Exchange two registers
void xchg(Register dst, Register src);
@@ -705,6 +712,7 @@
void fistp_s(const Operand& adr);
void fistp_d(const Operand& adr);
+ // The fisttp instructions require SSE3.
void fisttp_s(const Operand& adr);
void fisttp_d(const Operand& adr);
@@ -754,14 +762,18 @@
void cvttsd2si(Register dst, const Operand& src);
void cvtsi2sd(XMMRegister dst, const Operand& src);
+ void cvtss2sd(XMMRegister dst, XMMRegister src);
void addsd(XMMRegister dst, XMMRegister src);
void subsd(XMMRegister dst, XMMRegister src);
void mulsd(XMMRegister dst, XMMRegister src);
void divsd(XMMRegister dst, XMMRegister src);
void xorpd(XMMRegister dst, XMMRegister src);
+ void sqrtsd(XMMRegister dst, XMMRegister src);
void comisd(XMMRegister dst, XMMRegister src);
+ void ucomisd(XMMRegister dst, XMMRegister src);
+ void movmskpd(Register dst, XMMRegister src);
void movdqa(XMMRegister dst, const Operand& src);
void movdqa(const Operand& dst, XMMRegister src);
@@ -772,6 +784,12 @@
void movdbl(XMMRegister dst, const Operand& src);
void movdbl(const Operand& dst, XMMRegister src);
+ void movd(XMMRegister dst, const Operand& src);
+ void movsd(XMMRegister dst, XMMRegister src);
+
+ void pxor(XMMRegister dst, XMMRegister src);
+ void ptest(XMMRegister dst, XMMRegister src);
+
// Debugging
void Print();
@@ -815,7 +833,7 @@
void emit_sse_operand(XMMRegister reg, const Operand& adr);
void emit_sse_operand(XMMRegister dst, XMMRegister src);
-
+ void emit_sse_operand(Register dst, XMMRegister src);
private:
byte* addr_at(int pos) { return buffer_ + pos; }
diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc
index 54ef382..80e421b 100644
--- a/src/ia32/builtins-ia32.cc
+++ b/src/ia32/builtins-ia32.cc
@@ -63,10 +63,10 @@
ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
}
- // JumpToRuntime expects eax to contain the number of arguments
+ // JumpToExternalReference expects eax to contain the number of arguments
// including the receiver and the extra arguments.
__ add(Operand(eax), Immediate(num_extra_args + 1));
- __ JumpToRuntime(ExternalReference(id));
+ __ JumpToExternalReference(ExternalReference(id));
}
@@ -797,38 +797,23 @@
// register elements_array is scratched.
static void AllocateJSArray(MacroAssembler* masm,
Register array_function, // Array function.
- Register array_size, // As a smi.
+ Register array_size, // As a smi, cannot be 0.
Register result,
Register elements_array,
Register elements_array_end,
Register scratch,
bool fill_with_hole,
Label* gc_required) {
- Label not_empty, allocated;
+ ASSERT(scratch.is(edi)); // rep stos destination
+ ASSERT(!fill_with_hole || array_size.is(ecx)); // rep stos count
// Load the initial map from the array function.
__ mov(elements_array,
FieldOperand(array_function,
JSFunction::kPrototypeOrInitialMapOffset));
- // Check whether an empty sized array is requested.
- __ test(array_size, Operand(array_size));
- __ j(not_zero, ¬_empty);
-
- // If an empty array is requested allocate a small elements array anyway. This
- // keeps the code below free of special casing for the empty array.
- int size = JSArray::kSize + FixedArray::SizeFor(kPreallocatedArrayElements);
- __ AllocateInNewSpace(size,
- result,
- elements_array_end,
- scratch,
- gc_required,
- TAG_OBJECT);
- __ jmp(&allocated);
-
// Allocate the JSArray object together with space for a FixedArray with the
// requested elements.
- __ bind(¬_empty);
ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
__ AllocateInNewSpace(JSArray::kSize + FixedArray::kHeaderSize,
times_half_pointer_size, // array_size is a smi.
@@ -845,7 +830,6 @@
// elements_array: initial map
// elements_array_end: start of next object
// array_size: size of array (smi)
- __ bind(&allocated);
__ mov(FieldOperand(result, JSObject::kMapOffset), elements_array);
__ mov(elements_array, Factory::empty_fixed_array());
__ mov(FieldOperand(result, JSArray::kPropertiesOffset), elements_array);
@@ -869,15 +853,6 @@
__ SmiUntag(array_size); // Convert from smi to value.
__ mov(FieldOperand(elements_array, JSObject::kMapOffset),
Factory::fixed_array_map());
- Label not_empty_2, fill_array;
- __ test(array_size, Operand(array_size));
- __ j(not_zero, ¬_empty_2);
- // Length of the FixedArray is the number of pre-allocated elements even
- // though the actual JSArray has length 0.
- __ mov(FieldOperand(elements_array, Array::kLengthOffset),
- Immediate(kPreallocatedArrayElements));
- __ jmp(&fill_array);
- __ bind(¬_empty_2);
// For non-empty JSArrays the length of the FixedArray and the JSArray is the
// same.
__ mov(FieldOperand(elements_array, Array::kLengthOffset), array_size);
@@ -885,20 +860,18 @@
// Fill the allocated FixedArray with the hole value if requested.
// result: JSObject
// elements_array: elements array
- // elements_array_end: start of next object
- __ bind(&fill_array);
if (fill_with_hole) {
- Label loop, entry;
- __ mov(scratch, Factory::the_hole_value());
- __ lea(elements_array, Operand(elements_array,
- FixedArray::kHeaderSize - kHeapObjectTag));
- __ jmp(&entry);
- __ bind(&loop);
- __ mov(Operand(elements_array, 0), scratch);
- __ add(Operand(elements_array), Immediate(kPointerSize));
- __ bind(&entry);
- __ cmp(elements_array, Operand(elements_array_end));
- __ j(below, &loop);
+ __ lea(edi, Operand(elements_array,
+ FixedArray::kHeaderSize - kHeapObjectTag));
+
+ __ push(eax);
+ __ mov(eax, Factory::the_hole_value());
+
+ __ cld();
+ __ rep_stos();
+
+ // Restore saved registers.
+ __ pop(eax);
}
}
@@ -920,7 +893,8 @@
static void ArrayNativeCode(MacroAssembler* masm,
bool construct_call,
Label* call_generic_code) {
- Label argc_one_or_more, argc_two_or_more, prepare_generic_code_call;
+ Label argc_one_or_more, argc_two_or_more, prepare_generic_code_call,
+ empty_array, not_empty_array;
// Push the constructor and argc. No need to tag argc as a smi, as there will
// be no garbage collection with this on the stack.
@@ -936,6 +910,7 @@
__ test(eax, Operand(eax));
__ j(not_zero, &argc_one_or_more);
+ __ bind(&empty_array);
// Handle construction of an empty array.
AllocateEmptyJSArray(masm,
edi,
@@ -958,30 +933,46 @@
__ cmp(eax, 1);
__ j(not_equal, &argc_two_or_more);
ASSERT(kSmiTag == 0);
- __ test(Operand(esp, (push_count + 1) * kPointerSize),
- Immediate(kIntptrSignBit | kSmiTagMask));
+ __ mov(ecx, Operand(esp, (push_count + 1) * kPointerSize));
+ __ test(ecx, Operand(ecx));
+ __ j(not_zero, ¬_empty_array);
+
+ // The single argument passed is zero, so we jump to the code above used to
+ // handle the case of no arguments passed. To adapt the stack for that we move
+ // the return address and the pushed constructor (if pushed) one stack slot up
+ // thereby removing the passed argument. Argc is also on the stack - at the
+ // bottom - and it needs to be changed from 1 to 0 to have the call into the
+ // runtime system work in case a GC is required.
+ for (int i = push_count; i > 0; i--) {
+ __ mov(eax, Operand(esp, i * kPointerSize));
+ __ mov(Operand(esp, (i + 1) * kPointerSize), eax);
+ }
+ __ add(Operand(esp), Immediate(2 * kPointerSize)); // Drop two stack slots.
+ __ push(Immediate(0)); // Treat this as a call with argc of zero.
+ __ jmp(&empty_array);
+
+ __ bind(¬_empty_array);
+ __ test(ecx, Immediate(kIntptrSignBit | kSmiTagMask));
__ j(not_zero, &prepare_generic_code_call);
// Handle construction of an empty array of a certain size. Get the size from
// the stack and bail out if size is to large to actually allocate an elements
// array.
- __ mov(edx, Operand(esp, (push_count + 1) * kPointerSize));
- ASSERT(kSmiTag == 0);
- __ cmp(edx, JSObject::kInitialMaxFastElementArray << kSmiTagSize);
+ __ cmp(ecx, JSObject::kInitialMaxFastElementArray << kSmiTagSize);
__ j(greater_equal, &prepare_generic_code_call);
// edx: array_size (smi)
// edi: constructor
- // esp[0]: argc
+ // esp[0]: argc (cannot be 0 here)
// esp[4]: constructor (only if construct_call)
// esp[8]: return address
// esp[C]: argument
AllocateJSArray(masm,
edi,
- edx,
+ ecx,
eax,
ebx,
- ecx,
+ edx,
edi,
true,
&prepare_generic_code_call);
diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc
index ecb4c49..83060c1 100644
--- a/src/ia32/codegen-ia32.cc
+++ b/src/ia32/codegen-ia32.cc
@@ -39,6 +39,7 @@
#include "register-allocator-inl.h"
#include "runtime.h"
#include "scopes.h"
+#include "virtual-frame-inl.h"
namespace v8 {
namespace internal {
@@ -111,14 +112,13 @@
allocator_(NULL),
state_(NULL),
loop_nesting_(0),
+ in_safe_int32_mode_(false),
+ safe_int32_mode_enabled_(true),
function_return_is_shadowed_(false),
in_spilled_code_(false) {
}
-Scope* CodeGenerator::scope() { return info_->function()->scope(); }
-
-
// Calling conventions:
// ebp: caller's frame pointer
// esp: stack pointer
@@ -128,6 +128,7 @@
void CodeGenerator::Generate(CompilationInfo* info) {
// Record the position for debugging purposes.
CodeForFunctionPosition(info->function());
+ Comment cmnt(masm_, "[ function compiled by virtual frame code generator");
// Initialize state.
info_ = info;
@@ -139,7 +140,8 @@
set_in_spilled_code(false);
// Adjust for function-level loop nesting.
- loop_nesting_ += info->loop_nesting();
+ ASSERT_EQ(0, loop_nesting_);
+ loop_nesting_ = info->loop_nesting();
JumpTarget::set_compiling_deferred_code(false);
@@ -152,8 +154,7 @@
#endif
// New scope to get automatic timing calculation.
- { // NOLINT
- HistogramTimerScope codegen_timer(&Counters::code_generation);
+ { HistogramTimerScope codegen_timer(&Counters::code_generation);
CodeGenState state(this);
// Entry:
@@ -332,7 +333,8 @@
}
// Adjust for function-level loop nesting.
- loop_nesting_ -= info->loop_nesting();
+ ASSERT_EQ(info->loop_nesting(), loop_nesting_);
+ loop_nesting_ = 0;
// Code generation state must be reset.
ASSERT(state_ == NULL);
@@ -438,14 +440,14 @@
// frame. If the expression is boolean-valued it may be compiled (or
// partially compiled) into control flow to the control destination.
// If force_control is true, control flow is forced.
-void CodeGenerator::LoadCondition(Expression* x,
+void CodeGenerator::LoadCondition(Expression* expr,
ControlDestination* dest,
bool force_control) {
ASSERT(!in_spilled_code());
int original_height = frame_->height();
{ CodeGenState new_state(this, dest);
- Visit(x);
+ Visit(expr);
// If we hit a stack overflow, we may not have actually visited
// the expression. In that case, we ensure that we have a
@@ -482,64 +484,175 @@
}
+void CodeGenerator::LoadInSafeInt32Mode(Expression* expr,
+ BreakTarget* unsafe_bailout) {
+ set_unsafe_bailout(unsafe_bailout);
+ set_in_safe_int32_mode(true);
+ Load(expr);
+ Result value = frame_->Pop();
+ ASSERT(frame_->HasNoUntaggedInt32Elements());
+ if (expr->GuaranteedSmiResult()) {
+ ConvertInt32ResultToSmi(&value);
+ } else {
+ ConvertInt32ResultToNumber(&value);
+ }
+ set_in_safe_int32_mode(false);
+ set_unsafe_bailout(NULL);
+ frame_->Push(&value);
+}
+
+
+void CodeGenerator::LoadWithSafeInt32ModeDisabled(Expression* expr) {
+ set_safe_int32_mode_enabled(false);
+ Load(expr);
+ set_safe_int32_mode_enabled(true);
+}
+
+
+void CodeGenerator::ConvertInt32ResultToSmi(Result* value) {
+ ASSERT(value->is_untagged_int32());
+ if (value->is_register()) {
+ __ add(value->reg(), Operand(value->reg()));
+ } else {
+ ASSERT(value->is_constant());
+ ASSERT(value->handle()->IsSmi());
+ }
+ value->set_untagged_int32(false);
+ value->set_type_info(TypeInfo::Smi());
+}
+
+
+void CodeGenerator::ConvertInt32ResultToNumber(Result* value) {
+ ASSERT(value->is_untagged_int32());
+ if (value->is_register()) {
+ Register val = value->reg();
+ JumpTarget done;
+ __ add(val, Operand(val));
+ done.Branch(no_overflow, value);
+ __ sar(val, 1);
+ // If there was an overflow, bits 30 and 31 of the original number disagree.
+ __ xor_(val, 0x80000000u);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatures::Scope fscope(SSE2);
+ __ cvtsi2sd(xmm0, Operand(val));
+ } else {
+ // Move val to ST[0] in the FPU
+ // Push and pop are safe with respect to the virtual frame because
+ // all synced elements are below the actual stack pointer.
+ __ push(val);
+ __ fild_s(Operand(esp, 0));
+ __ pop(val);
+ }
+ Result scratch = allocator_->Allocate();
+ ASSERT(scratch.is_register());
+ Label allocation_failed;
+ __ AllocateHeapNumber(val, scratch.reg(),
+ no_reg, &allocation_failed);
+ VirtualFrame* clone = new VirtualFrame(frame_);
+ scratch.Unuse();
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatures::Scope fscope(SSE2);
+ __ movdbl(FieldOperand(val, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ fstp_d(FieldOperand(val, HeapNumber::kValueOffset));
+ }
+ done.Jump(value);
+
+ // Establish the virtual frame, cloned from where AllocateHeapNumber
+ // jumped to allocation_failed.
+ RegisterFile empty_regs;
+ SetFrame(clone, &empty_regs);
+ __ bind(&allocation_failed);
+ unsafe_bailout_->Jump();
+
+ done.Bind(value);
+ } else {
+ ASSERT(value->is_constant());
+ }
+ value->set_untagged_int32(false);
+ value->set_type_info(TypeInfo::Integer32());
+}
+
+
void CodeGenerator::Load(Expression* expr) {
#ifdef DEBUG
int original_height = frame_->height();
#endif
ASSERT(!in_spilled_code());
- JumpTarget true_target;
- JumpTarget false_target;
- ControlDestination dest(&true_target, &false_target, true);
- LoadCondition(expr, &dest, false);
- if (dest.false_was_fall_through()) {
- // The false target was just bound.
- JumpTarget loaded;
- frame_->Push(Factory::false_value());
- // There may be dangling jumps to the true target.
- if (true_target.is_linked()) {
- loaded.Jump();
- true_target.Bind();
- frame_->Push(Factory::true_value());
- loaded.Bind();
+ // If the expression should be a side-effect-free 32-bit int computation,
+ // compile that SafeInt32 path, and a bailout path.
+ if (!in_safe_int32_mode() &&
+ safe_int32_mode_enabled() &&
+ expr->side_effect_free() &&
+ expr->num_bit_ops() > 2 &&
+ CpuFeatures::IsSupported(SSE2)) {
+ BreakTarget unsafe_bailout;
+ JumpTarget done;
+ unsafe_bailout.set_expected_height(frame_->height());
+ LoadInSafeInt32Mode(expr, &unsafe_bailout);
+ done.Jump();
+
+ if (unsafe_bailout.is_linked()) {
+ unsafe_bailout.Bind();
+ LoadWithSafeInt32ModeDisabled(expr);
}
-
- } else if (dest.is_used()) {
- // There is true, and possibly false, control flow (with true as
- // the fall through).
- JumpTarget loaded;
- frame_->Push(Factory::true_value());
- if (false_target.is_linked()) {
- loaded.Jump();
- false_target.Bind();
- frame_->Push(Factory::false_value());
- loaded.Bind();
- }
-
+ done.Bind();
} else {
- // We have a valid value on top of the frame, but we still may
- // have dangling jumps to the true and false targets from nested
- // subexpressions (eg, the left subexpressions of the
- // short-circuited boolean operators).
- ASSERT(has_valid_frame());
- if (true_target.is_linked() || false_target.is_linked()) {
+ JumpTarget true_target;
+ JumpTarget false_target;
+
+ ControlDestination dest(&true_target, &false_target, true);
+ LoadCondition(expr, &dest, false);
+
+ if (dest.false_was_fall_through()) {
+ // The false target was just bound.
JumpTarget loaded;
- loaded.Jump(); // Don't lose the current TOS.
+ frame_->Push(Factory::false_value());
+ // There may be dangling jumps to the true target.
if (true_target.is_linked()) {
+ loaded.Jump();
true_target.Bind();
frame_->Push(Factory::true_value());
- if (false_target.is_linked()) {
- loaded.Jump();
- }
+ loaded.Bind();
}
+
+ } else if (dest.is_used()) {
+ // There is true, and possibly false, control flow (with true as
+ // the fall through).
+ JumpTarget loaded;
+ frame_->Push(Factory::true_value());
if (false_target.is_linked()) {
+ loaded.Jump();
false_target.Bind();
frame_->Push(Factory::false_value());
+ loaded.Bind();
}
- loaded.Bind();
+
+ } else {
+ // We have a valid value on top of the frame, but we still may
+ // have dangling jumps to the true and false targets from nested
+ // subexpressions (eg, the left subexpressions of the
+ // short-circuited boolean operators).
+ ASSERT(has_valid_frame());
+ if (true_target.is_linked() || false_target.is_linked()) {
+ JumpTarget loaded;
+ loaded.Jump(); // Don't lose the current TOS.
+ if (true_target.is_linked()) {
+ true_target.Bind();
+ frame_->Push(Factory::true_value());
+ if (false_target.is_linked()) {
+ loaded.Jump();
+ }
+ }
+ if (false_target.is_linked()) {
+ false_target.Bind();
+ frame_->Push(Factory::false_value());
+ }
+ loaded.Bind();
+ }
}
}
-
ASSERT(has_valid_frame());
ASSERT(frame_->height() == original_height + 1);
}
@@ -734,11 +847,31 @@
Result value = frame_->Pop();
value.ToRegister();
- if (value.is_number()) {
- Comment cmnt(masm_, "ONLY_NUMBER");
- // Fast case if NumberInfo indicates only numbers.
+ if (value.is_integer32()) { // Also takes Smi case.
+ Comment cmnt(masm_, "ONLY_INTEGER_32");
if (FLAG_debug_code) {
- __ AbortIfNotNumber(value.reg(), "ToBoolean operand is not a number.");
+ Label ok;
+ __ AbortIfNotNumber(value.reg());
+ __ test(value.reg(), Immediate(kSmiTagMask));
+ __ j(zero, &ok);
+ __ fldz();
+ __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset));
+ __ FCmp();
+ __ j(not_zero, &ok);
+ __ Abort("Smi was wrapped in HeapNumber in output from bitop");
+ __ bind(&ok);
+ }
+ // In the integer32 case there are no Smis hidden in heap numbers, so we
+ // need only test for Smi zero.
+ __ test(value.reg(), Operand(value.reg()));
+ dest->false_target()->Branch(zero);
+ value.Unuse();
+ dest->Split(not_zero);
+ } else if (value.is_number()) {
+ Comment cmnt(masm_, "ONLY_NUMBER");
+ // Fast case if TypeInfo indicates only numbers.
+ if (FLAG_debug_code) {
+ __ AbortIfNotNumber(value.reg());
}
// Smi => false iff zero.
ASSERT(kSmiTag == 0);
@@ -797,6 +930,7 @@
// operand in register number. Returns operand as floating point number
// on FPU stack.
static void LoadFloatOperand(MacroAssembler* masm, Register number);
+
// Code pattern for loading floating point values. Input values must
// be either smi or heap number objects (fp values). Requirements:
// operand_1 on TOS+1 or in edx, operand_2 on TOS+2 or in eax.
@@ -815,15 +949,26 @@
static void CheckFloatOperands(MacroAssembler* masm,
Label* non_float,
Register scratch);
+
// Takes the operands in edx and eax and loads them as integers in eax
// and ecx.
static void LoadAsIntegers(MacroAssembler* masm,
+ TypeInfo type_info,
bool use_sse3,
Label* operand_conversion_failure);
+ static void LoadNumbersAsIntegers(MacroAssembler* masm,
+ TypeInfo type_info,
+ bool use_sse3,
+ Label* operand_conversion_failure);
+ static void LoadUnknownsAsIntegers(MacroAssembler* masm,
+ bool use_sse3,
+ Label* operand_conversion_failure);
+
// Test if operands are smis or heap numbers and load them
// into xmm0 and xmm1 if they are. Operands are in edx and eax.
// Leaves operands unchanged.
static void LoadSSE2Operands(MacroAssembler* masm);
+
// Test if operands are numbers (smi or HeapNumber objects), and load
// them into xmm0 and xmm1 if they are. Jump to label not_numbers if
// either operand is not a number. Operands are in edx and eax.
@@ -851,13 +996,14 @@
}
OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
- "GenericBinaryOpStub_%s_%s%s_%s%s_%s",
+ "GenericBinaryOpStub_%s_%s%s_%s%s_%s_%s",
op_name,
overwrite_name,
(flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "",
args_in_registers_ ? "RegArgs" : "StackArgs",
args_reversed_ ? "_R" : "",
- NumberInfo::ToString(operands_type_));
+ static_operands_type_.ToString(),
+ BinaryOpIC::GetName(runtime_operands_type_));
return name_;
}
@@ -869,8 +1015,11 @@
Register dst,
Register left,
Register right,
+ TypeInfo left_info,
+ TypeInfo right_info,
OverwriteMode mode)
- : op_(op), dst_(dst), left_(left), right_(right), mode_(mode) {
+ : op_(op), dst_(dst), left_(left), right_(right),
+ left_info_(left_info), right_info_(right_info), mode_(mode) {
set_comment("[ DeferredInlineBinaryOperation");
}
@@ -881,6 +1030,8 @@
Register dst_;
Register left_;
Register right_;
+ TypeInfo left_info_;
+ TypeInfo right_info_;
OverwriteMode mode_;
};
@@ -894,18 +1045,24 @@
CpuFeatures::Scope use_sse2(SSE2);
Label call_runtime, after_alloc_failure;
Label left_smi, right_smi, load_right, do_op;
- __ test(left_, Immediate(kSmiTagMask));
- __ j(zero, &left_smi);
- __ cmp(FieldOperand(left_, HeapObject::kMapOffset),
- Factory::heap_number_map());
- __ j(not_equal, &call_runtime);
- __ movdbl(xmm0, FieldOperand(left_, HeapNumber::kValueOffset));
- if (mode_ == OVERWRITE_LEFT) {
- __ mov(dst_, left_);
- }
- __ jmp(&load_right);
+ if (!left_info_.IsSmi()) {
+ __ test(left_, Immediate(kSmiTagMask));
+ __ j(zero, &left_smi);
+ if (!left_info_.IsNumber()) {
+ __ cmp(FieldOperand(left_, HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ __ j(not_equal, &call_runtime);
+ }
+ __ movdbl(xmm0, FieldOperand(left_, HeapNumber::kValueOffset));
+ if (mode_ == OVERWRITE_LEFT) {
+ __ mov(dst_, left_);
+ }
+ __ jmp(&load_right);
- __ bind(&left_smi);
+ __ bind(&left_smi);
+ } else {
+ if (FLAG_debug_code) __ AbortIfNotSmi(left_);
+ }
__ SmiUntag(left_);
__ cvtsi2sd(xmm0, Operand(left_));
__ SmiTag(left_);
@@ -917,23 +1074,29 @@
}
__ bind(&load_right);
- __ test(right_, Immediate(kSmiTagMask));
- __ j(zero, &right_smi);
- __ cmp(FieldOperand(right_, HeapObject::kMapOffset),
- Factory::heap_number_map());
- __ j(not_equal, &call_runtime);
- __ movdbl(xmm1, FieldOperand(right_, HeapNumber::kValueOffset));
- if (mode_ == OVERWRITE_RIGHT) {
- __ mov(dst_, right_);
- } else if (mode_ == NO_OVERWRITE) {
- Label alloc_failure;
- __ push(left_);
- __ AllocateHeapNumber(dst_, left_, no_reg, &after_alloc_failure);
- __ pop(left_);
- }
- __ jmp(&do_op);
+ if (!right_info_.IsSmi()) {
+ __ test(right_, Immediate(kSmiTagMask));
+ __ j(zero, &right_smi);
+ if (!right_info_.IsNumber()) {
+ __ cmp(FieldOperand(right_, HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ __ j(not_equal, &call_runtime);
+ }
+ __ movdbl(xmm1, FieldOperand(right_, HeapNumber::kValueOffset));
+ if (mode_ == OVERWRITE_RIGHT) {
+ __ mov(dst_, right_);
+ } else if (mode_ == NO_OVERWRITE) {
+ Label alloc_failure;
+ __ push(left_);
+ __ AllocateHeapNumber(dst_, left_, no_reg, &after_alloc_failure);
+ __ pop(left_);
+ }
+ __ jmp(&do_op);
- __ bind(&right_smi);
+ __ bind(&right_smi);
+ } else {
+ if (FLAG_debug_code) __ AbortIfNotSmi(right_);
+ }
__ SmiUntag(right_);
__ cvtsi2sd(xmm1, Operand(right_));
__ SmiTag(right_);
@@ -959,17 +1122,117 @@
__ pop(left_);
__ bind(&call_runtime);
}
- GenericBinaryOpStub stub(op_, mode_, NO_SMI_CODE_IN_STUB);
+ GenericBinaryOpStub stub(op_,
+ mode_,
+ NO_SMI_CODE_IN_STUB,
+ TypeInfo::Combine(left_info_, right_info_));
stub.GenerateCall(masm_, left_, right_);
if (!dst_.is(eax)) __ mov(dst_, eax);
__ bind(&done);
}
-void CodeGenerator::GenericBinaryOperation(Token::Value op,
- StaticType* type,
+static TypeInfo CalculateTypeInfo(TypeInfo operands_type,
+ Token::Value op,
+ const Result& right,
+ const Result& left) {
+ // Set TypeInfo of result according to the operation performed.
+ // Rely on the fact that smis have a 31 bit payload on ia32.
+ ASSERT(kSmiValueSize == 31);
+ switch (op) {
+ case Token::COMMA:
+ return right.type_info();
+ case Token::OR:
+ case Token::AND:
+ // Result type can be either of the two input types.
+ return operands_type;
+ case Token::BIT_AND: {
+ // Anding with positive Smis will give you a Smi.
+ if (right.is_constant() && right.handle()->IsSmi() &&
+ Smi::cast(*right.handle())->value() >= 0) {
+ return TypeInfo::Smi();
+ } else if (left.is_constant() && left.handle()->IsSmi() &&
+ Smi::cast(*left.handle())->value() >= 0) {
+ return TypeInfo::Smi();
+ }
+ return (operands_type.IsSmi())
+ ? TypeInfo::Smi()
+ : TypeInfo::Integer32();
+ }
+ case Token::BIT_OR: {
+ // Oring with negative Smis will give you a Smi.
+ if (right.is_constant() && right.handle()->IsSmi() &&
+ Smi::cast(*right.handle())->value() < 0) {
+ return TypeInfo::Smi();
+ } else if (left.is_constant() && left.handle()->IsSmi() &&
+ Smi::cast(*left.handle())->value() < 0) {
+ return TypeInfo::Smi();
+ }
+ return (operands_type.IsSmi())
+ ? TypeInfo::Smi()
+ : TypeInfo::Integer32();
+ }
+ case Token::BIT_XOR:
+ // Result is always a 32 bit integer. Smi property of inputs is preserved.
+ return (operands_type.IsSmi())
+ ? TypeInfo::Smi()
+ : TypeInfo::Integer32();
+ case Token::SAR:
+ if (left.is_smi()) return TypeInfo::Smi();
+ // Result is a smi if we shift by a constant >= 1, otherwise an integer32.
+ // Shift amount is masked with 0x1F (ECMA standard 11.7.2).
+ return (right.is_constant() && right.handle()->IsSmi()
+ && (Smi::cast(*right.handle())->value() & 0x1F) >= 1)
+ ? TypeInfo::Smi()
+ : TypeInfo::Integer32();
+ case Token::SHR:
+ // Result is a smi if we shift by a constant >= 2, an integer32 if
+ // we shift by 1, and an unsigned 32-bit integer if we shift by 0.
+ if (right.is_constant() && right.handle()->IsSmi()) {
+ int shift_amount = Smi::cast(*right.handle())->value() & 0x1F;
+ if (shift_amount > 1) {
+ return TypeInfo::Smi();
+ } else if (shift_amount > 0) {
+ return TypeInfo::Integer32();
+ }
+ }
+ return TypeInfo::Number();
+ case Token::ADD:
+ if (operands_type.IsSmi()) {
+ // The Integer32 range is big enough to take the sum of any two Smis.
+ return TypeInfo::Integer32();
+ } else if (operands_type.IsNumber()) {
+ return TypeInfo::Number();
+ } else if (left.type_info().IsString() || right.type_info().IsString()) {
+ return TypeInfo::String();
+ } else {
+ return TypeInfo::Unknown();
+ }
+ case Token::SHL:
+ return TypeInfo::Integer32();
+ case Token::SUB:
+ // The Integer32 range is big enough to take the difference of any two
+ // Smis.
+ return (operands_type.IsSmi()) ?
+ TypeInfo::Integer32() :
+ TypeInfo::Number();
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD:
+ // Result is always a number.
+ return TypeInfo::Number();
+ default:
+ UNREACHABLE();
+ }
+ UNREACHABLE();
+ return TypeInfo::Unknown();
+}
+
+
+void CodeGenerator::GenericBinaryOperation(BinaryOperation* expr,
OverwriteMode overwrite_mode) {
Comment cmnt(masm_, "[ BinaryOperation");
+ Token::Value op = expr->op();
Comment cmnt_token(masm_, Token::String(op));
if (op == Token::COMMA) {
@@ -982,17 +1245,21 @@
Result left = frame_->Pop();
if (op == Token::ADD) {
- bool left_is_string = left.is_constant() && left.handle()->IsString();
- bool right_is_string = right.is_constant() && right.handle()->IsString();
+ const bool left_is_string = left.type_info().IsString();
+ const bool right_is_string = right.type_info().IsString();
+ // Make sure constant strings have string type info.
+ ASSERT(!(left.is_constant() && left.handle()->IsString()) ||
+ left_is_string);
+ ASSERT(!(right.is_constant() && right.handle()->IsString()) ||
+ right_is_string);
if (left_is_string || right_is_string) {
frame_->Push(&left);
frame_->Push(&right);
Result answer;
if (left_is_string) {
if (right_is_string) {
- // TODO(lrn): if both are constant strings
- // -- do a compile time cons, if allocation during codegen is allowed.
- answer = frame_->CallRuntime(Runtime::kStringAdd, 2);
+ StringAddStub stub(NO_STRING_CHECK_IN_STUB);
+ answer = frame_->CallStub(&stub, 2);
} else {
answer =
frame_->InvokeBuiltin(Builtins::STRING_ADD_LEFT, CALL_FUNCTION, 2);
@@ -1001,6 +1268,7 @@
answer =
frame_->InvokeBuiltin(Builtins::STRING_ADD_RIGHT, CALL_FUNCTION, 2);
}
+ answer.set_type_info(TypeInfo::String());
frame_->Push(&answer);
return;
}
@@ -1021,8 +1289,10 @@
}
// Get number type of left and right sub-expressions.
- NumberInfo::Type operands_type =
- NumberInfo::Combine(left.number_info(), right.number_info());
+ TypeInfo operands_type =
+ TypeInfo::Combine(left.type_info(), right.type_info());
+
+ TypeInfo result_type = CalculateTypeInfo(operands_type, op, right, left);
Result answer;
if (left_is_non_smi_constant || right_is_non_smi_constant) {
@@ -1033,19 +1303,22 @@
operands_type);
answer = stub.GenerateCall(masm_, frame_, &left, &right);
} else if (right_is_smi_constant) {
- answer = ConstantSmiBinaryOperation(op, &left, right.handle(),
- type, false, overwrite_mode);
+ answer = ConstantSmiBinaryOperation(expr, &left, right.handle(),
+ false, overwrite_mode);
} else if (left_is_smi_constant) {
- answer = ConstantSmiBinaryOperation(op, &right, left.handle(),
- type, true, overwrite_mode);
+ answer = ConstantSmiBinaryOperation(expr, &right, left.handle(),
+ true, overwrite_mode);
} else {
// Set the flags based on the operation, type and loop nesting level.
// Bit operations always assume they likely operate on Smis. Still only
// generate the inline Smi check code if this operation is part of a loop.
// For all other operations only inline the Smi check code for likely smis
// if the operation is part of a loop.
- if (loop_nesting() > 0 && (Token::IsBitOp(op) || type->IsLikelySmi())) {
- answer = LikelySmiBinaryOperation(op, &left, &right, overwrite_mode);
+ if (loop_nesting() > 0 &&
+ (Token::IsBitOp(op) ||
+ operands_type.IsInteger32() ||
+ expr->type()->IsLikelySmi())) {
+ answer = LikelySmiBinaryOperation(expr, &left, &right, overwrite_mode);
} else {
GenericBinaryOpStub stub(op,
overwrite_mode,
@@ -1055,59 +1328,7 @@
}
}
- // Set NumberInfo of result according to the operation performed.
- // Rely on the fact that smis have a 31 bit payload on ia32.
- ASSERT(kSmiValueSize == 31);
- NumberInfo::Type result_type = NumberInfo::kUnknown;
- switch (op) {
- case Token::COMMA:
- result_type = right.number_info();
- break;
- case Token::OR:
- case Token::AND:
- // Result type can be either of the two input types.
- result_type = operands_type;
- break;
- case Token::BIT_OR:
- case Token::BIT_XOR:
- case Token::BIT_AND:
- // Result is always a number. Smi property of inputs is preserved.
- result_type = (operands_type == NumberInfo::kSmi)
- ? NumberInfo::kSmi
- : NumberInfo::kNumber;
- break;
- case Token::SAR:
- // Result is a smi if we shift by a constant >= 1, otherwise a number.
- result_type = (right.is_constant() && right.handle()->IsSmi()
- && Smi::cast(*right.handle())->value() >= 1)
- ? NumberInfo::kSmi
- : NumberInfo::kNumber;
- break;
- case Token::SHR:
- // Result is a smi if we shift by a constant >= 2, otherwise a number.
- result_type = (right.is_constant() && right.handle()->IsSmi()
- && Smi::cast(*right.handle())->value() >= 2)
- ? NumberInfo::kSmi
- : NumberInfo::kNumber;
- break;
- case Token::ADD:
- // Result could be a string or a number. Check types of inputs.
- result_type = NumberInfo::IsNumber(operands_type)
- ? NumberInfo::kNumber
- : NumberInfo::kUnknown;
- break;
- case Token::SHL:
- case Token::SUB:
- case Token::MUL:
- case Token::DIV:
- case Token::MOD:
- // Result is always a number.
- result_type = NumberInfo::kNumber;
- break;
- default:
- UNREACHABLE();
- }
- answer.set_number_info(result_type);
+ answer.set_type_info(result_type);
frame_->Push(&answer);
}
@@ -1193,12 +1414,19 @@
}
+static void CheckTwoForSminess(MacroAssembler* masm,
+ Register left, Register right, Register scratch,
+ TypeInfo left_info, TypeInfo right_info,
+ DeferredInlineBinaryOperation* deferred);
+
+
// Implements a binary operation using a deferred code object and some
// inline code to operate on smis quickly.
-Result CodeGenerator::LikelySmiBinaryOperation(Token::Value op,
+Result CodeGenerator::LikelySmiBinaryOperation(BinaryOperation* expr,
Result* left,
Result* right,
OverwriteMode overwrite_mode) {
+ Token::Value op = expr->op();
Result answer;
// Special handling of div and mod because they use fixed registers.
if (op == Token::DIV || op == Token::MOD) {
@@ -1273,6 +1501,8 @@
(op == Token::DIV) ? eax : edx,
left->reg(),
right->reg(),
+ left->type_info(),
+ right->type_info(),
overwrite_mode);
if (left->reg().is(right->reg())) {
__ test(left->reg(), Immediate(kSmiTagMask));
@@ -1300,13 +1530,16 @@
// Check for negative zero result. If result is zero, and divisor
// is negative, return a floating point negative zero. The
// virtual frame is unchanged in this block, so local control flow
- // can use a Label rather than a JumpTarget.
- Label non_zero_result;
- __ test(left->reg(), Operand(left->reg()));
- __ j(not_zero, &non_zero_result);
- __ test(right->reg(), Operand(right->reg()));
- deferred->Branch(negative);
- __ bind(&non_zero_result);
+ // can use a Label rather than a JumpTarget. If the context of this
+ // expression will treat -0 like 0, do not do this test.
+ if (!expr->no_negative_zero()) {
+ Label non_zero_result;
+ __ test(left->reg(), Operand(left->reg()));
+ __ j(not_zero, &non_zero_result);
+ __ test(right->reg(), Operand(right->reg()));
+ deferred->Branch(negative);
+ __ bind(&non_zero_result);
+ }
// Check for the corner case of dividing the most negative smi by
// -1. We cannot use the overflow flag, since it is not set by
// idiv instruction.
@@ -1328,12 +1561,14 @@
// the dividend is negative, return a floating point negative
// zero. The frame is unchanged in this block, so local control
// flow can use a Label rather than a JumpTarget.
- Label non_zero_result;
- __ test(edx, Operand(edx));
- __ j(not_zero, &non_zero_result, taken);
- __ test(left->reg(), Operand(left->reg()));
- deferred->Branch(negative);
- __ bind(&non_zero_result);
+ if (!expr->no_negative_zero()) {
+ Label non_zero_result;
+ __ test(edx, Operand(edx));
+ __ j(not_zero, &non_zero_result, taken);
+ __ test(left->reg(), Operand(left->reg()));
+ deferred->Branch(negative);
+ __ bind(&non_zero_result);
+ }
deferred->BindExit();
left->Unuse();
right->Unuse();
@@ -1370,15 +1605,49 @@
answer.reg(),
left->reg(),
ecx,
+ left->type_info(),
+ right->type_info(),
overwrite_mode);
- __ mov(answer.reg(), left->reg());
- __ or_(answer.reg(), Operand(ecx));
- __ test(answer.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- // Untag both operands.
- __ mov(answer.reg(), left->reg());
- __ SmiUntag(answer.reg());
+ Label do_op, left_nonsmi;
+ // If right is a smi we make a fast case if left is either a smi
+ // or a heapnumber.
+ if (CpuFeatures::IsSupported(SSE2) && right->type_info().IsSmi()) {
+ CpuFeatures::Scope use_sse2(SSE2);
+ __ mov(answer.reg(), left->reg());
+ // Fast case - both are actually smis.
+ if (!left->type_info().IsSmi()) {
+ __ test(answer.reg(), Immediate(kSmiTagMask));
+ __ j(not_zero, &left_nonsmi);
+ } else {
+ if (FLAG_debug_code) __ AbortIfNotSmi(left->reg());
+ }
+ if (FLAG_debug_code) __ AbortIfNotSmi(right->reg());
+ __ SmiUntag(answer.reg());
+ __ jmp(&do_op);
+
+ __ bind(&left_nonsmi);
+ // Branch if not a heapnumber.
+ __ cmp(FieldOperand(answer.reg(), HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ deferred->Branch(not_equal);
+
+ // Load integer value into answer register using truncation.
+ __ cvttsd2si(answer.reg(),
+ FieldOperand(answer.reg(), HeapNumber::kValueOffset));
+ // Branch if we do not fit in a smi.
+ __ cmp(answer.reg(), 0xc0000000);
+ deferred->Branch(negative);
+ } else {
+ CheckTwoForSminess(masm_, left->reg(), right->reg(), answer.reg(),
+ left->type_info(), right->type_info(), deferred);
+
+ // Untag both operands.
+ __ mov(answer.reg(), left->reg());
+ __ SmiUntag(answer.reg());
+ }
+
+ __ bind(&do_op);
__ SmiUntag(ecx);
// Perform the operation.
switch (op) {
@@ -1444,16 +1713,12 @@
answer.reg(),
left->reg(),
right->reg(),
+ left->type_info(),
+ right->type_info(),
overwrite_mode);
- if (left->reg().is(right->reg())) {
- __ test(left->reg(), Immediate(kSmiTagMask));
- } else {
- __ mov(answer.reg(), left->reg());
- __ or_(answer.reg(), Operand(right->reg()));
- ASSERT(kSmiTag == 0); // Adjust test if not the case.
- __ test(answer.reg(), Immediate(kSmiTagMask));
- }
- deferred->Branch(not_zero);
+ CheckTwoForSminess(masm_, left->reg(), right->reg(), answer.reg(),
+ left->type_info(), right->type_info(), deferred);
+
__ mov(answer.reg(), left->reg());
switch (op) {
case Token::ADD:
@@ -1480,14 +1745,16 @@
// argument is negative, go to slow case. The frame is unchanged
// in this block, so local control flow can use a Label rather
// than a JumpTarget.
- Label non_zero_result;
- __ test(answer.reg(), Operand(answer.reg()));
- __ j(not_zero, &non_zero_result, taken);
- __ mov(answer.reg(), left->reg());
- __ or_(answer.reg(), Operand(right->reg()));
- deferred->Branch(negative);
- __ xor_(answer.reg(), Operand(answer.reg())); // Positive 0 is correct.
- __ bind(&non_zero_result);
+ if (!expr->no_negative_zero()) {
+ Label non_zero_result;
+ __ test(answer.reg(), Operand(answer.reg()));
+ __ j(not_zero, &non_zero_result, taken);
+ __ mov(answer.reg(), left->reg());
+ __ or_(answer.reg(), Operand(right->reg()));
+ deferred->Branch(negative);
+ __ xor_(answer.reg(), Operand(answer.reg())); // Positive 0 is correct.
+ __ bind(&non_zero_result);
+ }
break;
}
@@ -1522,13 +1789,16 @@
DeferredInlineSmiOperation(Token::Value op,
Register dst,
Register src,
+ TypeInfo type_info,
Smi* value,
OverwriteMode overwrite_mode)
: op_(op),
dst_(dst),
src_(src),
+ type_info_(type_info),
value_(value),
overwrite_mode_(overwrite_mode) {
+ if (type_info.IsSmi()) overwrite_mode_ = NO_OVERWRITE;
set_comment("[ DeferredInlineSmiOperation");
}
@@ -1538,6 +1808,7 @@
Token::Value op_;
Register dst_;
Register src_;
+ TypeInfo type_info_;
Smi* value_;
OverwriteMode overwrite_mode_;
};
@@ -1548,7 +1819,8 @@
GenericBinaryOpStub stub(
op_,
overwrite_mode_,
- (op_ == Token::MOD) ? NO_GENERIC_BINARY_FLAGS : NO_SMI_CODE_IN_STUB);
+ (op_ == Token::MOD) ? NO_GENERIC_BINARY_FLAGS : NO_SMI_CODE_IN_STUB,
+ TypeInfo::Combine(TypeInfo::Smi(), type_info_));
stub.GenerateCall(masm_, src_, value_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1562,9 +1834,11 @@
Register dst,
Smi* value,
Register src,
+ TypeInfo type_info,
OverwriteMode overwrite_mode)
: op_(op),
dst_(dst),
+ type_info_(type_info),
value_(value),
src_(src),
overwrite_mode_(overwrite_mode) {
@@ -1576,6 +1850,7 @@
private:
Token::Value op_;
Register dst_;
+ TypeInfo type_info_;
Smi* value_;
Register src_;
OverwriteMode overwrite_mode_;
@@ -1583,7 +1858,11 @@
void DeferredInlineSmiOperationReversed::Generate() {
- GenericBinaryOpStub igostub(op_, overwrite_mode_, NO_SMI_CODE_IN_STUB);
+ GenericBinaryOpStub igostub(
+ op_,
+ overwrite_mode_,
+ NO_SMI_CODE_IN_STUB,
+ TypeInfo::Combine(TypeInfo::Smi(), type_info_));
igostub.GenerateCall(masm_, value_, src_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1595,9 +1874,14 @@
class DeferredInlineSmiAdd: public DeferredCode {
public:
DeferredInlineSmiAdd(Register dst,
+ TypeInfo type_info,
Smi* value,
OverwriteMode overwrite_mode)
- : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
+ : dst_(dst),
+ type_info_(type_info),
+ value_(value),
+ overwrite_mode_(overwrite_mode) {
+ if (type_info_.IsSmi()) overwrite_mode_ = NO_OVERWRITE;
set_comment("[ DeferredInlineSmiAdd");
}
@@ -1605,6 +1889,7 @@
private:
Register dst_;
+ TypeInfo type_info_;
Smi* value_;
OverwriteMode overwrite_mode_;
};
@@ -1613,7 +1898,11 @@
void DeferredInlineSmiAdd::Generate() {
// Undo the optimistic add operation and call the shared stub.
__ sub(Operand(dst_), Immediate(value_));
- GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, NO_SMI_CODE_IN_STUB);
+ GenericBinaryOpStub igostub(
+ Token::ADD,
+ overwrite_mode_,
+ NO_SMI_CODE_IN_STUB,
+ TypeInfo::Combine(TypeInfo::Smi(), type_info_));
igostub.GenerateCall(masm_, dst_, value_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1625,9 +1914,13 @@
class DeferredInlineSmiAddReversed: public DeferredCode {
public:
DeferredInlineSmiAddReversed(Register dst,
+ TypeInfo type_info,
Smi* value,
OverwriteMode overwrite_mode)
- : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
+ : dst_(dst),
+ type_info_(type_info),
+ value_(value),
+ overwrite_mode_(overwrite_mode) {
set_comment("[ DeferredInlineSmiAddReversed");
}
@@ -1635,6 +1928,7 @@
private:
Register dst_;
+ TypeInfo type_info_;
Smi* value_;
OverwriteMode overwrite_mode_;
};
@@ -1643,7 +1937,11 @@
void DeferredInlineSmiAddReversed::Generate() {
// Undo the optimistic add operation and call the shared stub.
__ sub(Operand(dst_), Immediate(value_));
- GenericBinaryOpStub igostub(Token::ADD, overwrite_mode_, NO_SMI_CODE_IN_STUB);
+ GenericBinaryOpStub igostub(
+ Token::ADD,
+ overwrite_mode_,
+ NO_SMI_CODE_IN_STUB,
+ TypeInfo::Combine(TypeInfo::Smi(), type_info_));
igostub.GenerateCall(masm_, value_, dst_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -1656,9 +1954,14 @@
class DeferredInlineSmiSub: public DeferredCode {
public:
DeferredInlineSmiSub(Register dst,
+ TypeInfo type_info,
Smi* value,
OverwriteMode overwrite_mode)
- : dst_(dst), value_(value), overwrite_mode_(overwrite_mode) {
+ : dst_(dst),
+ type_info_(type_info),
+ value_(value),
+ overwrite_mode_(overwrite_mode) {
+ if (type_info.IsSmi()) overwrite_mode_ = NO_OVERWRITE;
set_comment("[ DeferredInlineSmiSub");
}
@@ -1666,6 +1969,7 @@
private:
Register dst_;
+ TypeInfo type_info_;
Smi* value_;
OverwriteMode overwrite_mode_;
};
@@ -1674,18 +1978,22 @@
void DeferredInlineSmiSub::Generate() {
// Undo the optimistic sub operation and call the shared stub.
__ add(Operand(dst_), Immediate(value_));
- GenericBinaryOpStub igostub(Token::SUB, overwrite_mode_, NO_SMI_CODE_IN_STUB);
+ GenericBinaryOpStub igostub(
+ Token::SUB,
+ overwrite_mode_,
+ NO_SMI_CODE_IN_STUB,
+ TypeInfo::Combine(TypeInfo::Smi(), type_info_));
igostub.GenerateCall(masm_, dst_, value_);
if (!dst_.is(eax)) __ mov(dst_, eax);
}
-Result CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
- Result* operand,
- Handle<Object> value,
- StaticType* type,
- bool reversed,
- OverwriteMode overwrite_mode) {
+Result CodeGenerator::ConstantSmiBinaryOperation(
+ BinaryOperation* expr,
+ Result* operand,
+ Handle<Object> value,
+ bool reversed,
+ OverwriteMode overwrite_mode) {
// NOTE: This is an attempt to inline (a bit) more of the code for
// some possible smi operations (like + and -) when (at least) one
// of the operands is a constant smi.
@@ -1695,10 +2003,10 @@
if (IsUnsafeSmi(value)) {
Result unsafe_operand(value);
if (reversed) {
- return LikelySmiBinaryOperation(op, &unsafe_operand, operand,
+ return LikelySmiBinaryOperation(expr, &unsafe_operand, operand,
overwrite_mode);
} else {
- return LikelySmiBinaryOperation(op, operand, &unsafe_operand,
+ return LikelySmiBinaryOperation(expr, operand, &unsafe_operand,
overwrite_mode);
}
}
@@ -1707,6 +2015,7 @@
Smi* smi_value = Smi::cast(*value);
int int_value = smi_value->value();
+ Token::Value op = expr->op();
Result answer;
switch (op) {
case Token::ADD: {
@@ -1718,17 +2027,23 @@
DeferredCode* deferred = NULL;
if (reversed) {
deferred = new DeferredInlineSmiAddReversed(operand->reg(),
+ operand->type_info(),
smi_value,
overwrite_mode);
} else {
deferred = new DeferredInlineSmiAdd(operand->reg(),
+ operand->type_info(),
smi_value,
overwrite_mode);
}
__ add(Operand(operand->reg()), Immediate(value));
deferred->Branch(overflow);
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (!operand->type_info().IsSmi()) {
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ } else if (FLAG_debug_code) {
+ __ AbortIfNotSmi(operand->reg());
+ }
deferred->BindExit();
answer = *operand;
break;
@@ -1743,24 +2058,31 @@
answer = allocator()->Allocate();
ASSERT(answer.is_valid());
__ Set(answer.reg(), Immediate(value));
- deferred = new DeferredInlineSmiOperationReversed(op,
- answer.reg(),
- smi_value,
- operand->reg(),
- overwrite_mode);
+ deferred =
+ new DeferredInlineSmiOperationReversed(op,
+ answer.reg(),
+ smi_value,
+ operand->reg(),
+ operand->type_info(),
+ overwrite_mode);
__ sub(answer.reg(), Operand(operand->reg()));
} else {
operand->ToRegister();
frame_->Spill(operand->reg());
answer = *operand;
deferred = new DeferredInlineSmiSub(operand->reg(),
+ operand->type_info(),
smi_value,
overwrite_mode);
__ sub(Operand(operand->reg()), Immediate(value));
}
deferred->Branch(overflow);
- __ test(answer.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (!operand->type_info().IsSmi()) {
+ __ test(answer.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ } else if (FLAG_debug_code) {
+ __ AbortIfNotSmi(operand->reg());
+ }
deferred->BindExit();
operand->Unuse();
break;
@@ -1769,7 +2091,7 @@
case Token::SAR:
if (reversed) {
Result constant_operand(value);
- answer = LikelySmiBinaryOperation(op, &constant_operand, operand,
+ answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
overwrite_mode);
} else {
// Only the least significant 5 bits of the shift value are used.
@@ -1777,19 +2099,30 @@
int shift_value = int_value & 0x1f;
operand->ToRegister();
frame_->Spill(operand->reg());
- DeferredInlineSmiOperation* deferred =
- new DeferredInlineSmiOperation(op,
- operand->reg(),
- operand->reg(),
- smi_value,
- overwrite_mode);
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- if (shift_value > 0) {
- __ sar(operand->reg(), shift_value);
- __ and_(operand->reg(), ~kSmiTagMask);
+ if (!operand->type_info().IsSmi()) {
+ DeferredInlineSmiOperation* deferred =
+ new DeferredInlineSmiOperation(op,
+ operand->reg(),
+ operand->reg(),
+ operand->type_info(),
+ smi_value,
+ overwrite_mode);
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ if (shift_value > 0) {
+ __ sar(operand->reg(), shift_value);
+ __ and_(operand->reg(), ~kSmiTagMask);
+ }
+ deferred->BindExit();
+ } else {
+ if (FLAG_debug_code) {
+ __ AbortIfNotSmi(operand->reg());
+ }
+ if (shift_value > 0) {
+ __ sar(operand->reg(), shift_value);
+ __ and_(operand->reg(), ~kSmiTagMask);
+ }
}
- deferred->BindExit();
answer = *operand;
}
break;
@@ -1797,7 +2130,7 @@
case Token::SHR:
if (reversed) {
Result constant_operand(value);
- answer = LikelySmiBinaryOperation(op, &constant_operand, operand,
+ answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
overwrite_mode);
} else {
// Only the least significant 5 bits of the shift value are used.
@@ -1810,10 +2143,15 @@
new DeferredInlineSmiOperation(op,
answer.reg(),
operand->reg(),
+ operand->type_info(),
smi_value,
overwrite_mode);
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (!operand->type_info().IsSmi()) {
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ } else if (FLAG_debug_code) {
+ __ AbortIfNotSmi(operand->reg());
+ }
__ mov(answer.reg(), operand->reg());
__ SmiUntag(answer.reg());
__ shr(answer.reg(), shift_value);
@@ -1830,12 +2168,12 @@
case Token::SHL:
if (reversed) {
+ // Move operand into ecx and also into a second register.
+ // If operand is already in a register, take advantage of that.
+ // This lets us modify ecx, but still bail out to deferred code.
Result right;
Result right_copy_in_ecx;
-
- // Make sure to get a copy of the right operand into ecx. This
- // allows us to modify it without having to restore it in the
- // deferred code.
+ TypeInfo right_type_info = operand->type_info();
operand->ToRegister();
if (operand->reg().is(ecx)) {
right = allocator()->Allocate();
@@ -1855,10 +2193,15 @@
answer.reg(),
smi_value,
right.reg(),
+ right_type_info,
overwrite_mode);
__ mov(answer.reg(), Immediate(int_value));
__ sar(ecx, kSmiTagSize);
- deferred->Branch(carry);
+ if (!right_type_info.IsSmi()) {
+ deferred->Branch(carry);
+ } else if (FLAG_debug_code) {
+ __ AbortIfNotSmi(right.reg());
+ }
__ shl_cl(answer.reg());
__ cmp(answer.reg(), 0xc0000000);
deferred->Branch(sign);
@@ -1877,6 +2220,7 @@
new DeferredInlineSmiOperation(op,
operand->reg(),
operand->reg(),
+ operand->type_info(),
smi_value,
overwrite_mode);
__ test(operand->reg(), Immediate(kSmiTagMask));
@@ -1891,10 +2235,15 @@
new DeferredInlineSmiOperation(op,
answer.reg(),
operand->reg(),
+ operand->type_info(),
smi_value,
overwrite_mode);
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (!operand->type_info().IsSmi()) {
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ } else if (FLAG_debug_code) {
+ __ AbortIfNotSmi(operand->reg());
+ }
__ mov(answer.reg(), operand->reg());
ASSERT(kSmiTag == 0); // adjust code if not the case
// We do no shifts, only the Smi conversion, if shift_value is 1.
@@ -1918,20 +2267,27 @@
frame_->Spill(operand->reg());
DeferredCode* deferred = NULL;
if (reversed) {
- deferred = new DeferredInlineSmiOperationReversed(op,
- operand->reg(),
- smi_value,
- operand->reg(),
- overwrite_mode);
+ deferred =
+ new DeferredInlineSmiOperationReversed(op,
+ operand->reg(),
+ smi_value,
+ operand->reg(),
+ operand->type_info(),
+ overwrite_mode);
} else {
deferred = new DeferredInlineSmiOperation(op,
operand->reg(),
operand->reg(),
+ operand->type_info(),
smi_value,
overwrite_mode);
}
- __ test(operand->reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (!operand->type_info().IsSmi()) {
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ } else if (FLAG_debug_code) {
+ __ AbortIfNotSmi(operand->reg());
+ }
if (op == Token::BIT_AND) {
__ and_(Operand(operand->reg()), Immediate(value));
} else if (op == Token::BIT_XOR) {
@@ -1958,6 +2314,7 @@
new DeferredInlineSmiOperation(op,
operand->reg(),
operand->reg(),
+ operand->type_info(),
smi_value,
overwrite_mode);
// Check that lowest log2(value) bits of operand are zero, and test
@@ -1974,14 +2331,15 @@
// default case here.
Result constant_operand(value);
if (reversed) {
- answer = LikelySmiBinaryOperation(op, &constant_operand, operand,
+ answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
overwrite_mode);
} else {
- answer = LikelySmiBinaryOperation(op, operand, &constant_operand,
+ answer = LikelySmiBinaryOperation(expr, operand, &constant_operand,
overwrite_mode);
}
}
break;
+
// Generate inline code for mod of powers of 2 and negative powers of 2.
case Token::MOD:
if (!reversed &&
@@ -1989,13 +2347,15 @@
(IsPowerOf2(int_value) || IsPowerOf2(-int_value))) {
operand->ToRegister();
frame_->Spill(operand->reg());
- DeferredCode* deferred = new DeferredInlineSmiOperation(op,
- operand->reg(),
- operand->reg(),
- smi_value,
- overwrite_mode);
+ DeferredCode* deferred =
+ new DeferredInlineSmiOperation(op,
+ operand->reg(),
+ operand->reg(),
+ operand->type_info(),
+ smi_value,
+ overwrite_mode);
// Check for negative or non-Smi left hand side.
- __ test(operand->reg(), Immediate(kSmiTagMask | 0x80000000));
+ __ test(operand->reg(), Immediate(kSmiTagMask | kSmiSignMask));
deferred->Branch(not_zero);
if (int_value < 0) int_value = -int_value;
if (int_value == 1) {
@@ -2012,10 +2372,10 @@
default: {
Result constant_operand(value);
if (reversed) {
- answer = LikelySmiBinaryOperation(op, &constant_operand, operand,
+ answer = LikelySmiBinaryOperation(expr, &constant_operand, operand,
overwrite_mode);
} else {
- answer = LikelySmiBinaryOperation(op, operand, &constant_operand,
+ answer = LikelySmiBinaryOperation(expr, operand, &constant_operand,
overwrite_mode);
}
break;
@@ -2027,12 +2387,30 @@
static bool CouldBeNaN(const Result& result) {
+ if (result.type_info().IsSmi()) return false;
+ if (result.type_info().IsInteger32()) return false;
if (!result.is_constant()) return true;
if (!result.handle()->IsHeapNumber()) return false;
return isnan(HeapNumber::cast(*result.handle())->value());
}
+// Convert from signed to unsigned comparison to match the way EFLAGS are set
+// by FPU and XMM compare instructions.
+static Condition DoubleCondition(Condition cc) {
+ switch (cc) {
+ case less: return below;
+ case equal: return equal;
+ case less_equal: return below_equal;
+ case greater: return above;
+ case greater_equal: return above_equal;
+ default: UNREACHABLE();
+ }
+ UNREACHABLE();
+ return equal;
+}
+
+
void CodeGenerator::Comparison(AstNode* node,
Condition cc,
bool strict,
@@ -2063,7 +2441,8 @@
left_side_constant_null = left_side.handle()->IsNull();
left_side_constant_1_char_string =
(left_side.handle()->IsString() &&
- (String::cast(*left_side.handle())->length() == 1));
+ String::cast(*left_side.handle())->length() == 1 &&
+ String::cast(*left_side.handle())->IsAsciiRepresentation());
}
bool right_side_constant_smi = false;
bool right_side_constant_null = false;
@@ -2073,7 +2452,8 @@
right_side_constant_null = right_side.handle()->IsNull();
right_side_constant_1_char_string =
(right_side.handle()->IsString() &&
- (String::cast(*right_side.handle())->length() == 1));
+ String::cast(*right_side.handle())->length() == 1 &&
+ String::cast(*right_side.handle())->IsAsciiRepresentation());
}
if (left_side_constant_smi || right_side_constant_smi) {
@@ -2103,7 +2483,7 @@
left_side = right_side;
right_side = temp;
cc = ReverseCondition(cc);
- // This may reintroduce greater or less_equal as the value of cc.
+ // This may re-introduce greater or less_equal as the value of cc.
// CompareStub and the inline code both support all values of cc.
}
// Implement comparison against a constant Smi, inlining the case
@@ -2117,61 +2497,58 @@
// a jump target and branching to duplicate the virtual frame at
// the first split. We manually handle the off-frame references
// by reconstituting them on the non-fall-through path.
- JumpTarget is_smi;
- __ test(left_side.reg(), Immediate(kSmiTagMask));
- is_smi.Branch(zero, taken);
- bool is_for_loop_compare = (node->AsCompareOperation() != NULL)
- && node->AsCompareOperation()->is_for_loop_condition();
- if (!is_for_loop_compare
- && CpuFeatures::IsSupported(SSE2)
- && right_val->IsSmi()) {
- // Right side is a constant smi and left side has been checked
- // not to be a smi.
- CpuFeatures::Scope use_sse2(SSE2);
- JumpTarget not_number;
- __ cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
- Immediate(Factory::heap_number_map()));
- not_number.Branch(not_equal, &left_side);
- __ movdbl(xmm1,
- FieldOperand(left_reg, HeapNumber::kValueOffset));
- int value = Smi::cast(*right_val)->value();
- if (value == 0) {
- __ xorpd(xmm0, xmm0);
- } else {
- Result temp = allocator()->Allocate();
- __ mov(temp.reg(), Immediate(value));
- __ cvtsi2sd(xmm0, Operand(temp.reg()));
- temp.Unuse();
+ if (left_side.is_smi()) {
+ if (FLAG_debug_code) __ AbortIfNotSmi(left_side.reg());
+ } else {
+ JumpTarget is_smi;
+ __ test(left_side.reg(), Immediate(kSmiTagMask));
+ is_smi.Branch(zero, taken);
+
+ bool is_loop_condition = (node->AsExpression() != NULL) &&
+ node->AsExpression()->is_loop_condition();
+ if (!is_loop_condition &&
+ CpuFeatures::IsSupported(SSE2) &&
+ right_val->IsSmi()) {
+ // Right side is a constant smi and left side has been checked
+ // not to be a smi.
+ CpuFeatures::Scope use_sse2(SSE2);
+ JumpTarget not_number;
+ __ cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
+ Immediate(Factory::heap_number_map()));
+ not_number.Branch(not_equal, &left_side);
+ __ movdbl(xmm1,
+ FieldOperand(left_reg, HeapNumber::kValueOffset));
+ int value = Smi::cast(*right_val)->value();
+ if (value == 0) {
+ __ xorpd(xmm0, xmm0);
+ } else {
+ Result temp = allocator()->Allocate();
+ __ mov(temp.reg(), Immediate(value));
+ __ cvtsi2sd(xmm0, Operand(temp.reg()));
+ temp.Unuse();
+ }
+ __ comisd(xmm1, xmm0);
+ // Jump to builtin for NaN.
+ not_number.Branch(parity_even, &left_side);
+ left_side.Unuse();
+ dest->true_target()->Branch(DoubleCondition(cc));
+ dest->false_target()->Jump();
+ not_number.Bind(&left_side);
}
- __ comisd(xmm1, xmm0);
- // Jump to builtin for NaN.
- not_number.Branch(parity_even, &left_side);
- left_side.Unuse();
- Condition double_cc = cc;
- switch (cc) {
- case less: double_cc = below; break;
- case equal: double_cc = equal; break;
- case less_equal: double_cc = below_equal; break;
- case greater: double_cc = above; break;
- case greater_equal: double_cc = above_equal; break;
- default: UNREACHABLE();
- }
- dest->true_target()->Branch(double_cc);
+
+ // Setup and call the compare stub.
+ CompareStub stub(cc, strict, kCantBothBeNaN);
+ Result result = frame_->CallStub(&stub, &left_side, &right_side);
+ result.ToRegister();
+ __ cmp(result.reg(), 0);
+ result.Unuse();
+ dest->true_target()->Branch(cc);
dest->false_target()->Jump();
- not_number.Bind(&left_side);
+
+ is_smi.Bind();
}
- // Setup and call the compare stub.
- CompareStub stub(cc, strict, kCantBothBeNaN);
- Result result = frame_->CallStub(&stub, &left_side, &right_side);
- result.ToRegister();
- __ cmp(result.reg(), 0);
- result.Unuse();
- dest->true_target()->Branch(cc);
- dest->false_target()->Jump();
-
- is_smi.Bind();
left_side = Result(left_reg);
right_side = Result(right_val);
// Test smi equality and comparison by signed int comparison.
@@ -2265,6 +2642,7 @@
JumpTarget is_not_string, is_string;
Register left_reg = left_side.reg();
Handle<Object> right_val = right_side.handle();
+ ASSERT(StringShape(String::cast(*right_val)).IsSymbol());
__ test(left_side.reg(), Immediate(kSmiTagMask));
is_not_string.Branch(zero, &left_side);
Result temp = allocator_->Allocate();
@@ -2289,7 +2667,7 @@
dest->false_target()->Branch(not_equal);
__ bind(¬_a_symbol);
}
- // If the receiver is not a string of the type we handle call the stub.
+ // Call the compare stub if the left side is not a flat ascii string.
__ and_(temp.reg(),
kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask);
__ cmp(temp.reg(), kStringTag | kSeqStringTag | kAsciiStringTag);
@@ -2307,7 +2685,7 @@
dest->false_target()->Jump();
is_string.Bind(&left_side);
- // Here we know we have a sequential ASCII string.
+ // left_side is a sequential ASCII string.
left_side = Result(left_reg);
right_side = Result(right_val);
Result temp2 = allocator_->Allocate();
@@ -2316,36 +2694,37 @@
if (cc == equal) {
Label comparison_done;
__ cmp(FieldOperand(left_side.reg(), String::kLengthOffset),
- Immediate(1));
+ Immediate(Smi::FromInt(1)));
__ j(not_equal, &comparison_done);
uint8_t char_value =
- static_cast<uint8_t>(String::cast(*right_side.handle())->Get(0));
+ static_cast<uint8_t>(String::cast(*right_val)->Get(0));
__ cmpb(FieldOperand(left_side.reg(), SeqAsciiString::kHeaderSize),
char_value);
__ bind(&comparison_done);
} else {
__ mov(temp2.reg(),
FieldOperand(left_side.reg(), String::kLengthOffset));
+ __ SmiUntag(temp2.reg());
__ sub(Operand(temp2.reg()), Immediate(1));
Label comparison;
- // If the length is 0 then our subtraction gave -1 which compares less
+ // If the length is 0 then the subtraction gave -1 which compares less
// than any character.
__ j(negative, &comparison);
// Otherwise load the first character.
__ movzx_b(temp2.reg(),
FieldOperand(left_side.reg(), SeqAsciiString::kHeaderSize));
__ bind(&comparison);
- // Compare the first character of the string with out constant
- // 1-character string.
+ // Compare the first character of the string with the
+ // constant 1-character string.
uint8_t char_value =
- static_cast<uint8_t>(String::cast(*right_side.handle())->Get(0));
+ static_cast<uint8_t>(String::cast(*right_val)->Get(0));
__ cmp(Operand(temp2.reg()), Immediate(char_value));
Label characters_were_different;
__ j(not_equal, &characters_were_different);
// If the first character is the same then the long string sorts after
// the short one.
__ cmp(FieldOperand(left_side.reg(), String::kLengthOffset),
- Immediate(1));
+ Immediate(Smi::FromInt(1)));
__ bind(&characters_were_different);
}
temp2.Unuse();
@@ -2354,27 +2733,55 @@
dest->Split(cc);
}
} else {
- // Neither side is a constant Smi or null.
- // If either side is a non-smi constant, skip the smi check.
+ // Neither side is a constant Smi, constant 1-char string or constant null.
+ // If either side is a non-smi constant, or known to be a heap number skip
+ // the smi check.
bool known_non_smi =
(left_side.is_constant() && !left_side.handle()->IsSmi()) ||
- (right_side.is_constant() && !right_side.handle()->IsSmi());
+ (right_side.is_constant() && !right_side.handle()->IsSmi()) ||
+ left_side.type_info().IsDouble() ||
+ right_side.type_info().IsDouble();
NaNInformation nan_info =
(CouldBeNaN(left_side) && CouldBeNaN(right_side)) ?
kBothCouldBeNaN :
kCantBothBeNaN;
+
+ // Inline number comparison handling any combination of smi's and heap
+ // numbers if:
+ // code is in a loop
+ // the compare operation is different from equal
+ // compare is not a for-loop comparison
+ // The reason for excluding equal is that it will most likely be done
+ // with smi's (not heap numbers) and the code to comparing smi's is inlined
+ // separately. The same reason applies for for-loop comparison which will
+ // also most likely be smi comparisons.
+ bool is_loop_condition = (node->AsExpression() != NULL)
+ && node->AsExpression()->is_loop_condition();
+ bool inline_number_compare =
+ loop_nesting() > 0 && cc != equal && !is_loop_condition;
+
+ // Left and right needed in registers for the following code.
left_side.ToRegister();
right_side.ToRegister();
if (known_non_smi) {
- // When non-smi, call out to the compare stub.
- CompareStub stub(cc, strict, nan_info);
- Result answer = frame_->CallStub(&stub, &left_side, &right_side);
- if (cc == equal) {
- __ test(answer.reg(), Operand(answer.reg()));
- } else {
- __ cmp(answer.reg(), 0);
+ // Inline the equality check if both operands can't be a NaN. If both
+ // objects are the same they are equal.
+ if (nan_info == kCantBothBeNaN && cc == equal) {
+ __ cmp(left_side.reg(), Operand(right_side.reg()));
+ dest->true_target()->Branch(equal);
}
+
+ // Inline number comparison.
+ if (inline_number_compare) {
+ GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
+ }
+
+ // End of in-line compare, call out to the compare stub. Don't include
+ // number comparison in the stub if it was inlined.
+ CompareStub stub(cc, strict, nan_info, !inline_number_compare);
+ Result answer = frame_->CallStub(&stub, &left_side, &right_side);
+ __ test(answer.reg(), Operand(answer.reg()));
answer.Unuse();
dest->Split(cc);
} else {
@@ -2387,6 +2794,7 @@
Register left_reg = left_side.reg();
Register right_reg = right_side.reg();
+ // In-line check for comparing two smis.
Result temp = allocator_->Allocate();
ASSERT(temp.is_valid());
__ mov(temp.reg(), left_side.reg());
@@ -2394,8 +2802,22 @@
__ test(temp.reg(), Immediate(kSmiTagMask));
temp.Unuse();
is_smi.Branch(zero, taken);
- // When non-smi, call out to the compare stub.
- CompareStub stub(cc, strict, nan_info);
+
+ // Inline the equality check if both operands can't be a NaN. If both
+ // objects are the same they are equal.
+ if (nan_info == kCantBothBeNaN && cc == equal) {
+ __ cmp(left_side.reg(), Operand(right_side.reg()));
+ dest->true_target()->Branch(equal);
+ }
+
+ // Inline number comparison.
+ if (inline_number_compare) {
+ GenerateInlineNumberComparison(&left_side, &right_side, cc, dest);
+ }
+
+ // End of in-line compare, call out to the compare stub. Don't include
+ // number comparison in the stub if it was inlined.
+ CompareStub stub(cc, strict, nan_info, !inline_number_compare);
Result answer = frame_->CallStub(&stub, &left_side, &right_side);
if (cc == equal) {
__ test(answer.reg(), Operand(answer.reg()));
@@ -2418,6 +2840,148 @@
}
+// Check that the comparison operand is a number. Jump to not_numbers jump
+// target passing the left and right result if the operand is not a number.
+static void CheckComparisonOperand(MacroAssembler* masm_,
+ Result* operand,
+ Result* left_side,
+ Result* right_side,
+ JumpTarget* not_numbers) {
+ // Perform check if operand is not known to be a number.
+ if (!operand->type_info().IsNumber()) {
+ Label done;
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ __ j(zero, &done);
+ __ cmp(FieldOperand(operand->reg(), HeapObject::kMapOffset),
+ Immediate(Factory::heap_number_map()));
+ not_numbers->Branch(not_equal, left_side, right_side, not_taken);
+ __ bind(&done);
+ }
+}
+
+
+// Load a comparison operand to the FPU stack. This assumes that the operand has
+// already been checked and is a number.
+static void LoadComparisonOperand(MacroAssembler* masm_,
+ Result* operand) {
+ Label done;
+ if (operand->type_info().IsDouble()) {
+ // Operand is known to be a heap number, just load it.
+ __ fld_d(FieldOperand(operand->reg(), HeapNumber::kValueOffset));
+ } else if (operand->type_info().IsSmi()) {
+ // Operand is known to be a smi. Convert it to double and keep the original
+ // smi.
+ __ SmiUntag(operand->reg());
+ __ push(operand->reg());
+ __ fild_s(Operand(esp, 0));
+ __ pop(operand->reg());
+ __ SmiTag(operand->reg());
+ } else {
+ // Operand type not known, check for smi otherwise assume heap number.
+ Label smi;
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ __ j(zero, &smi);
+ __ fld_d(FieldOperand(operand->reg(), HeapNumber::kValueOffset));
+ __ jmp(&done);
+ __ bind(&smi);
+ __ SmiUntag(operand->reg());
+ __ push(operand->reg());
+ __ fild_s(Operand(esp, 0));
+ __ pop(operand->reg());
+ __ SmiTag(operand->reg());
+ __ jmp(&done);
+ }
+ __ bind(&done);
+}
+
+
+// Load a comparison operand into into a XMM register. Jump to not_numbers jump
+// target passing the left and right result if the operand is not a number.
+static void LoadComparisonOperandSSE2(MacroAssembler* masm_,
+ Result* operand,
+ XMMRegister reg,
+ Result* left_side,
+ Result* right_side,
+ JumpTarget* not_numbers) {
+ Label done;
+ if (operand->type_info().IsDouble()) {
+ // Operand is known to be a heap number, just load it.
+ __ movdbl(reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
+ } else if (operand->type_info().IsSmi()) {
+ // Operand is known to be a smi. Convert it to double and keep the original
+ // smi.
+ __ SmiUntag(operand->reg());
+ __ cvtsi2sd(reg, Operand(operand->reg()));
+ __ SmiTag(operand->reg());
+ } else {
+ // Operand type not known, check for smi or heap number.
+ Label smi;
+ __ test(operand->reg(), Immediate(kSmiTagMask));
+ __ j(zero, &smi);
+ if (!operand->type_info().IsNumber()) {
+ __ cmp(FieldOperand(operand->reg(), HeapObject::kMapOffset),
+ Immediate(Factory::heap_number_map()));
+ not_numbers->Branch(not_equal, left_side, right_side, taken);
+ }
+ __ movdbl(reg, FieldOperand(operand->reg(), HeapNumber::kValueOffset));
+ __ jmp(&done);
+
+ __ bind(&smi);
+ // Comvert smi to float and keep the original smi.
+ __ SmiUntag(operand->reg());
+ __ cvtsi2sd(reg, Operand(operand->reg()));
+ __ SmiTag(operand->reg());
+ __ jmp(&done);
+ }
+ __ bind(&done);
+}
+
+
+void CodeGenerator::GenerateInlineNumberComparison(Result* left_side,
+ Result* right_side,
+ Condition cc,
+ ControlDestination* dest) {
+ ASSERT(left_side->is_register());
+ ASSERT(right_side->is_register());
+
+ JumpTarget not_numbers;
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatures::Scope use_sse2(SSE2);
+
+ // Load left and right operand into registers xmm0 and xmm1 and compare.
+ LoadComparisonOperandSSE2(masm_, left_side, xmm0, left_side, right_side,
+ ¬_numbers);
+ LoadComparisonOperandSSE2(masm_, right_side, xmm1, left_side, right_side,
+ ¬_numbers);
+ __ comisd(xmm0, xmm1);
+ } else {
+ Label check_right, compare;
+
+ // Make sure that both comparison operands are numbers.
+ CheckComparisonOperand(masm_, left_side, left_side, right_side,
+ ¬_numbers);
+ CheckComparisonOperand(masm_, right_side, left_side, right_side,
+ ¬_numbers);
+
+ // Load right and left operand to FPU stack and compare.
+ LoadComparisonOperand(masm_, right_side);
+ LoadComparisonOperand(masm_, left_side);
+ __ FCmp();
+ }
+
+ // Bail out if a NaN is involved.
+ not_numbers.Branch(parity_even, left_side, right_side, not_taken);
+
+ // Split to destination targets based on comparison.
+ left_side->Unuse();
+ right_side->Unuse();
+ dest->true_target()->Branch(DoubleCondition(cc));
+ dest->false_target()->Jump();
+
+ not_numbers.Bind(left_side, right_side);
+}
+
+
// Call the function just below TOS on the stack with the given
// arguments. The receiver is the TOS.
void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
@@ -3318,6 +3882,26 @@
}
+void CodeGenerator::SetTypeForStackSlot(Slot* slot, TypeInfo info) {
+ ASSERT(slot->type() == Slot::LOCAL || slot->type() == Slot::PARAMETER);
+ if (slot->type() == Slot::LOCAL) {
+ frame_->SetTypeForLocalAt(slot->index(), info);
+ } else {
+ frame_->SetTypeForParamAt(slot->index(), info);
+ }
+ if (FLAG_debug_code && info.IsSmi()) {
+ if (slot->type() == Slot::LOCAL) {
+ frame_->PushLocalAt(slot->index());
+ } else {
+ frame_->PushParameterAt(slot->index());
+ }
+ Result var = frame_->Pop();
+ var.ToRegister();
+ __ AbortIfNotSmi(var.reg());
+ }
+}
+
+
void CodeGenerator::VisitForStatement(ForStatement* node) {
ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ ForStatement");
@@ -3410,6 +3994,17 @@
}
CheckStack(); // TODO(1222600): ignore if body contains calls.
+
+ // We know that the loop index is a smi if it is not modified in the
+ // loop body and it is checked against a constant limit in the loop
+ // condition. In this case, we reset the static type information of the
+ // loop index to smi before compiling the body, the update expression, and
+ // the bottom check of the loop condition.
+ if (node->is_fast_smi_loop()) {
+ // Set number type of the loop variable to smi.
+ SetTypeForStackSlot(node->loop_variable()->slot(), TypeInfo::Smi());
+ }
+
Visit(node->body());
// If there is an update expression, compile it if necessary.
@@ -3429,6 +4024,13 @@
}
}
+ // Set the type of the loop variable to smi before compiling the test
+ // expression if we are in a fast smi loop condition.
+ if (node->is_fast_smi_loop() && has_valid_frame()) {
+ // Set number type of the loop variable to smi.
+ SetTypeForStackSlot(node->loop_variable()->slot(), TypeInfo::Smi());
+ }
+
// Based on the condition analysis, compile the backward jump as
// necessary.
switch (info) {
@@ -4049,9 +4651,8 @@
}
-Result CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) {
- ASSERT(boilerplate->IsBoilerplate());
-
+Result CodeGenerator::InstantiateFunction(
+ Handle<SharedFunctionInfo> function_info) {
// The inevitable call will sync frame elements to memory anyway, so
// we do it eagerly to allow us to push the arguments directly into
// place.
@@ -4059,15 +4660,15 @@
// Use the fast case closure allocation code that allocates in new
// space for nested functions that don't need literals cloning.
- if (scope()->is_function_scope() && boilerplate->NumberOfLiterals() == 0) {
+ if (scope()->is_function_scope() && function_info->num_literals() == 0) {
FastNewClosureStub stub;
- frame()->EmitPush(Immediate(boilerplate));
+ frame()->EmitPush(Immediate(function_info));
return frame()->CallStub(&stub, 1);
} else {
- // Call the runtime to instantiate the function boilerplate
- // object.
+ // Call the runtime to instantiate the function based on the
+ // shared function info.
frame()->EmitPush(esi);
- frame()->EmitPush(Immediate(boilerplate));
+ frame()->EmitPush(Immediate(function_info));
return frame()->CallRuntime(Runtime::kNewClosure, 2);
}
}
@@ -4075,27 +4676,29 @@
void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
Comment cmnt(masm_, "[ FunctionLiteral");
-
- // Build the function boilerplate and instantiate it.
- Handle<JSFunction> boilerplate =
- Compiler::BuildBoilerplate(node, script(), this);
+ ASSERT(!in_safe_int32_mode());
+ // Build the function info and instantiate it.
+ Handle<SharedFunctionInfo> function_info =
+ Compiler::BuildFunctionInfo(node, script(), this);
// Check for stack-overflow exception.
if (HasStackOverflow()) return;
- Result result = InstantiateBoilerplate(boilerplate);
+ Result result = InstantiateFunction(function_info);
frame()->Push(&result);
}
-void CodeGenerator::VisitFunctionBoilerplateLiteral(
- FunctionBoilerplateLiteral* node) {
- Comment cmnt(masm_, "[ FunctionBoilerplateLiteral");
- Result result = InstantiateBoilerplate(node->boilerplate());
+void CodeGenerator::VisitSharedFunctionInfoLiteral(
+ SharedFunctionInfoLiteral* node) {
+ ASSERT(!in_safe_int32_mode());
+ Comment cmnt(masm_, "[ SharedFunctionInfoLiteral");
+ Result result = InstantiateFunction(node->shared_function_info());
frame()->Push(&result);
}
void CodeGenerator::VisitConditional(Conditional* node) {
Comment cmnt(masm_, "[ Conditional");
+ ASSERT(!in_safe_int32_mode());
JumpTarget then;
JumpTarget else_;
JumpTarget exit;
@@ -4266,6 +4869,7 @@
Slot* slot,
TypeofState typeof_state,
JumpTarget* slow) {
+ ASSERT(!in_safe_int32_mode());
// Check that no extension objects have been created by calls to
// eval from the current scope to the global scope.
Register context = esi;
@@ -4434,10 +5038,20 @@
}
-void CodeGenerator::VisitSlot(Slot* node) {
+void CodeGenerator::VisitSlot(Slot* slot) {
Comment cmnt(masm_, "[ Slot");
- Result result = LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
- frame()->Push(&result);
+ if (in_safe_int32_mode()) {
+ if ((slot->type() == Slot::LOCAL && !slot->is_arguments())) {
+ frame()->UntaggedPushLocalAt(slot->index());
+ } else if (slot->type() == Slot::PARAMETER) {
+ frame()->UntaggedPushParameterAt(slot->index());
+ } else {
+ UNREACHABLE();
+ }
+ } else {
+ Result result = LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
+ frame()->Push(&result);
+ }
}
@@ -4449,6 +5063,7 @@
Visit(expr);
} else {
ASSERT(var->is_global());
+ ASSERT(!in_safe_int32_mode());
Reference ref(this, node);
ref.GetValue();
}
@@ -4457,7 +5072,11 @@
void CodeGenerator::VisitLiteral(Literal* node) {
Comment cmnt(masm_, "[ Literal");
- frame_->Push(node->handle());
+ if (in_safe_int32_mode()) {
+ frame_->PushUntaggedElement(node->handle());
+ } else {
+ frame_->Push(node->handle());
+ }
}
@@ -4531,6 +5150,7 @@
void CodeGenerator::VisitRegExpLiteral(RegExpLiteral* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ RegExp Literal");
// Retrieve the literals array and check the allocated entry. Begin
@@ -4567,6 +5187,7 @@
void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ ObjectLiteral");
// Load a writable copy of the function of this activation in a
@@ -4585,11 +5206,13 @@
frame_->Push(Smi::FromInt(node->literal_index()));
// Constant properties.
frame_->Push(node->constant_properties());
+ // Should the object literal have fast elements?
+ frame_->Push(Smi::FromInt(node->fast_elements() ? 1 : 0));
Result clone;
if (node->depth() > 1) {
- clone = frame_->CallRuntime(Runtime::kCreateObjectLiteral, 3);
+ clone = frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
} else {
- clone = frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 3);
+ clone = frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
}
frame_->Push(&clone);
@@ -4649,6 +5272,7 @@
void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ ArrayLiteral");
// Load a writable copy of the function of this activation in a
@@ -4720,6 +5344,7 @@
void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
+ ASSERT(!in_safe_int32_mode());
ASSERT(!in_spilled_code());
// Call runtime routine to allocate the catch extension object and
// assign the exception value to the catch variable.
@@ -4744,17 +5369,23 @@
// Evaluate the right-hand side.
if (node->is_compound()) {
+ // For a compound assignment the right-hand side is a binary operation
+ // between the current property value and the actual right-hand side.
Result result = LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
frame()->Push(&result);
Load(node->value());
+ // Perform the binary operation.
bool overwrite_value =
(node->value()->AsBinaryOperation() != NULL &&
node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
- GenericBinaryOperation(node->binary_op(),
- node->type(),
+ // Construct the implicit binary operation.
+ BinaryOperation expr(node, node->binary_op(), node->target(),
+ node->value());
+ GenericBinaryOperation(&expr,
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
} else {
+ // For non-compound assignment just load the right-hand side.
Load(node->value());
}
@@ -4777,7 +5408,9 @@
Property* prop = node->target()->AsProperty();
ASSERT(var == NULL || (prop == NULL && var->is_global()));
- // Initialize name and evaluate the receiver subexpression if necessary.
+ // Initialize name and evaluate the receiver sub-expression if necessary. If
+ // the receiver is trivial it is not placed on the stack at this point, but
+ // loaded whenever actually needed.
Handle<String> name;
bool is_trivial_receiver = false;
if (var != NULL) {
@@ -4791,10 +5424,13 @@
if (!is_trivial_receiver) Load(prop->obj());
}
+ // Change to slow case in the beginning of an initialization block to
+ // avoid the quadratic behavior of repeatedly adding fast properties.
if (node->starts_initialization_block()) {
+ // Initialization block consists of assignments of the form expr.x = ..., so
+ // this will never be an assignment to a variable, so there must be a
+ // receiver object.
ASSERT_EQ(NULL, var);
- // Change to slow case in the beginning of an initialization block to
- // avoid the quadratic behavior of repeatedly adding fast properties.
if (is_trivial_receiver) {
frame()->Push(prop->obj());
} else {
@@ -4803,14 +5439,21 @@
Result ignored = frame()->CallRuntime(Runtime::kToSlowProperties, 1);
}
+ // Change to fast case at the end of an initialization block. To prepare for
+ // that add an extra copy of the receiver to the frame, so that it can be
+ // converted back to fast case after the assignment.
if (node->ends_initialization_block() && !is_trivial_receiver) {
- // Add an extra copy of the receiver to the frame, so that it can be
- // converted back to fast case after the assignment.
frame()->Dup();
}
+ // Stack layout:
+ // [tos] : receiver (only materialized if non-trivial)
+ // [tos+1] : receiver if at the end of an initialization block
+
// Evaluate the right-hand side.
if (node->is_compound()) {
+ // For a compound assignment the right-hand side is a binary operation
+ // between the current property value and the actual right-hand side.
if (is_trivial_receiver) {
frame()->Push(prop->obj());
} else if (var != NULL) {
@@ -4828,13 +5471,21 @@
bool overwrite_value =
(node->value()->AsBinaryOperation() != NULL &&
node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
- GenericBinaryOperation(node->binary_op(),
- node->type(),
+ // Construct the implicit binary operation.
+ BinaryOperation expr(node, node->binary_op(), node->target(),
+ node->value());
+ GenericBinaryOperation(&expr,
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
} else {
+ // For non-compound assignment just load the right-hand side.
Load(node->value());
}
+ // Stack layout:
+ // [tos] : value
+ // [tos+1] : receiver (only materialized if non-trivial)
+ // [tos+2] : receiver if at the end of an initialization block
+
// Perform the assignment. It is safe to ignore constants here.
ASSERT(var == NULL || var->mode() != Variable::CONST);
ASSERT_NE(Token::INIT_CONST, node->op());
@@ -4848,6 +5499,10 @@
Result answer = EmitNamedStore(name, is_contextual);
frame()->Push(&answer);
+ // Stack layout:
+ // [tos] : result
+ // [tos+1] : receiver if at the end of an initialization block
+
if (node->ends_initialization_block()) {
ASSERT_EQ(NULL, var);
// The argument to the runtime call is the receiver.
@@ -4864,6 +5519,9 @@
Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
}
+ // Stack layout:
+ // [tos] : result
+
ASSERT_EQ(frame()->height(), original_height + 1);
}
@@ -4872,54 +5530,76 @@
#ifdef DEBUG
int original_height = frame()->height();
#endif
- Comment cmnt(masm_, "[ Named Property Assignment");
+ Comment cmnt(masm_, "[ Keyed Property Assignment");
Property* prop = node->target()->AsProperty();
ASSERT_NOT_NULL(prop);
// Evaluate the receiver subexpression.
Load(prop->obj());
+ // Change to slow case in the beginning of an initialization block to
+ // avoid the quadratic behavior of repeatedly adding fast properties.
if (node->starts_initialization_block()) {
- // Change to slow case in the beginning of an initialization block to
- // avoid the quadratic behavior of repeatedly adding fast properties.
frame_->Dup();
Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1);
}
+ // Change to fast case at the end of an initialization block. To prepare for
+ // that add an extra copy of the receiver to the frame, so that it can be
+ // converted back to fast case after the assignment.
if (node->ends_initialization_block()) {
- // Add an extra copy of the receiver to the frame, so that it can be
- // converted back to fast case after the assignment.
frame_->Dup();
}
// Evaluate the key subexpression.
Load(prop->key());
+ // Stack layout:
+ // [tos] : key
+ // [tos+1] : receiver
+ // [tos+2] : receiver if at the end of an initialization block
+
// Evaluate the right-hand side.
if (node->is_compound()) {
- // Duplicate receiver and key.
+ // For a compound assignment the right-hand side is a binary operation
+ // between the current property value and the actual right-hand side.
+ // Duplicate receiver and key for loading the current property value.
frame()->PushElementAt(1);
frame()->PushElementAt(1);
Result value = EmitKeyedLoad();
frame()->Push(&value);
Load(node->value());
+ // Perform the binary operation.
bool overwrite_value =
(node->value()->AsBinaryOperation() != NULL &&
node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
- GenericBinaryOperation(node->binary_op(),
- node->type(),
+ BinaryOperation expr(node, node->binary_op(), node->target(),
+ node->value());
+ GenericBinaryOperation(&expr,
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
} else {
+ // For non-compound assignment just load the right-hand side.
Load(node->value());
}
+ // Stack layout:
+ // [tos] : value
+ // [tos+1] : key
+ // [tos+2] : receiver
+ // [tos+3] : receiver if at the end of an initialization block
+
// Perform the assignment. It is safe to ignore constants here.
ASSERT(node->op() != Token::INIT_CONST);
CodeForSourcePosition(node->position());
Result answer = EmitKeyedStore(prop->key()->type());
frame()->Push(&answer);
+ // Stack layout:
+ // [tos] : result
+ // [tos+1] : receiver if at the end of an initialization block
+
+ // Change to fast case at the end of an initialization block.
if (node->ends_initialization_block()) {
// The argument to the runtime call is the extra copy of the receiver,
// which is below the value of the assignment. Swap the receiver and
@@ -4931,11 +5611,15 @@
Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
}
+ // Stack layout:
+ // [tos] : result
+
ASSERT(frame()->height() == original_height + 1);
}
void CodeGenerator::VisitAssignment(Assignment* node) {
+ ASSERT(!in_safe_int32_mode());
#ifdef DEBUG
int original_height = frame()->height();
#endif
@@ -4971,6 +5655,7 @@
void CodeGenerator::VisitThrow(Throw* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ Throw");
Load(node->exception());
Result result = frame_->CallRuntime(Runtime::kThrow, 1);
@@ -4979,6 +5664,7 @@
void CodeGenerator::VisitProperty(Property* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ Property");
Reference property(this, node);
property.GetValue();
@@ -4986,6 +5672,7 @@
void CodeGenerator::VisitCall(Call* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ Call");
Expression* function = node->expression();
@@ -5201,6 +5888,7 @@
void CodeGenerator::VisitCallNew(CallNew* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ CallNew");
// According to ECMA-262, section 11.2.2, page 44, the function
@@ -5270,7 +5958,7 @@
Result value = frame_->Pop();
value.ToRegister();
ASSERT(value.is_valid());
- __ test(value.reg(), Immediate(kSmiTagMask | 0x80000000));
+ __ test(value.reg(), Immediate(kSmiTagMask | kSmiSignMask));
value.Unuse();
destination()->Split(zero);
}
@@ -5286,43 +5974,11 @@
Comment(masm_, "[ GenerateFastCharCodeAt");
ASSERT(args->length() == 2);
- Label slow_case;
- Label end;
- Label not_a_flat_string;
- Label try_again_with_new_string;
- Label ascii_string;
- Label got_char_code;
-
Load(args->at(0));
Load(args->at(1));
Result index = frame_->Pop();
Result object = frame_->Pop();
- // Get register ecx to use as shift amount later.
- Result shift_amount;
- if (object.is_register() && object.reg().is(ecx)) {
- Result fresh = allocator_->Allocate();
- shift_amount = object;
- object = fresh;
- __ mov(object.reg(), ecx);
- }
- if (index.is_register() && index.reg().is(ecx)) {
- Result fresh = allocator_->Allocate();
- shift_amount = index;
- index = fresh;
- __ mov(index.reg(), ecx);
- }
- // There could be references to ecx in the frame. Allocating will
- // spill them, otherwise spill explicitly.
- if (shift_amount.is_valid()) {
- frame_->Spill(ecx);
- } else {
- shift_amount = allocator()->Allocate(ecx);
- }
- ASSERT(shift_amount.is_register());
- ASSERT(shift_amount.reg().is(ecx));
- ASSERT(allocator_->count(ecx) == 1);
-
// We will mutate the index register and possibly the object register.
// The case where they are somehow the same register is handled
// because we only mutate them in the case where the receiver is a
@@ -5332,93 +5988,58 @@
frame_->Spill(object.reg());
frame_->Spill(index.reg());
- // We need a single extra temporary register.
- Result temp = allocator()->Allocate();
- ASSERT(temp.is_valid());
+ // We need two extra registers.
+ Result result = allocator()->Allocate();
+ ASSERT(result.is_valid());
+ Result scratch = allocator()->Allocate();
+ ASSERT(scratch.is_valid());
// There is no virtual frame effect from here up to the final result
// push.
-
- // If the receiver is a smi trigger the slow case.
- ASSERT(kSmiTag == 0);
- __ test(object.reg(), Immediate(kSmiTagMask));
- __ j(zero, &slow_case);
-
- // If the index is negative or non-smi trigger the slow case.
- ASSERT(kSmiTag == 0);
- __ test(index.reg(), Immediate(kSmiTagMask | 0x80000000));
- __ j(not_zero, &slow_case);
- // Untag the index.
- __ SmiUntag(index.reg());
-
- __ bind(&try_again_with_new_string);
- // Fetch the instance type of the receiver into ecx.
- __ mov(ecx, FieldOperand(object.reg(), HeapObject::kMapOffset));
- __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
- // If the receiver is not a string trigger the slow case.
- __ test(ecx, Immediate(kIsNotStringMask));
- __ j(not_zero, &slow_case);
-
- // Fetch the length field into the temporary register.
- __ mov(temp.reg(), FieldOperand(object.reg(), String::kLengthOffset));
- // Check for index out of range.
- __ cmp(index.reg(), Operand(temp.reg()));
- __ j(greater_equal, &slow_case);
- // Reload the instance type (into the temp register this time)..
- __ mov(temp.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset));
- __ movzx_b(temp.reg(), FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
-
- // We need special handling for non-flat strings.
- ASSERT(kSeqStringTag == 0);
- __ test(temp.reg(), Immediate(kStringRepresentationMask));
- __ j(not_zero, ¬_a_flat_string);
- // Check for 1-byte or 2-byte string.
- __ test(temp.reg(), Immediate(kStringEncodingMask));
- __ j(not_zero, &ascii_string);
-
- // 2-byte string.
- // Load the 2-byte character code into the temp register.
- __ movzx_w(temp.reg(), FieldOperand(object.reg(),
- index.reg(),
- times_2,
- SeqTwoByteString::kHeaderSize));
- __ jmp(&got_char_code);
-
- // ASCII string.
- __ bind(&ascii_string);
- // Load the byte into the temp register.
- __ movzx_b(temp.reg(), FieldOperand(object.reg(),
- index.reg(),
- times_1,
- SeqAsciiString::kHeaderSize));
- __ bind(&got_char_code);
- __ SmiTag(temp.reg());
- __ jmp(&end);
-
- // Handle non-flat strings.
- __ bind(¬_a_flat_string);
- __ and_(temp.reg(), kStringRepresentationMask);
- __ cmp(temp.reg(), kConsStringTag);
- __ j(not_equal, &slow_case);
-
- // ConsString.
- // Check that the right hand side is the empty string (ie if this is really a
- // flat string in a cons string). If that is not the case we would rather go
- // to the runtime system now, to flatten the string.
- __ mov(temp.reg(), FieldOperand(object.reg(), ConsString::kSecondOffset));
- __ cmp(Operand(temp.reg()), Factory::empty_string());
- __ j(not_equal, &slow_case);
- // Get the first of the two strings.
- __ mov(object.reg(), FieldOperand(object.reg(), ConsString::kFirstOffset));
- __ jmp(&try_again_with_new_string);
+ Label slow_case;
+ Label exit;
+ StringHelper::GenerateFastCharCodeAt(masm_,
+ object.reg(),
+ index.reg(),
+ scratch.reg(),
+ result.reg(),
+ &slow_case,
+ &slow_case,
+ &slow_case,
+ &slow_case);
+ __ jmp(&exit);
__ bind(&slow_case);
// Move the undefined value into the result register, which will
// trigger the slow case.
- __ Set(temp.reg(), Immediate(Factory::undefined_value()));
+ __ Set(result.reg(), Immediate(Factory::undefined_value()));
- __ bind(&end);
- frame_->Push(&temp);
+ __ bind(&exit);
+ frame_->Push(&result);
+}
+
+
+void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
+ Comment(masm_, "[ GenerateCharFromCode");
+ ASSERT(args->length() == 1);
+
+ Load(args->at(0));
+
+ Result code = frame_->Pop();
+ code.ToRegister();
+ ASSERT(code.is_valid());
+
+ // StringHelper::GenerateCharFromCode may do a runtime call.
+ frame_->SpillAll();
+
+ Result result = allocator()->Allocate();
+ ASSERT(result.is_valid());
+
+ StringHelper::GenerateCharFromCode(masm_,
+ code.reg(),
+ result.reg(),
+ CALL_FUNCTION);
+ frame_->Push(&result);
}
@@ -5554,12 +6175,30 @@
void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
ASSERT(args->length() == 0);
- // ArgumentsAccessStub takes the parameter count as an input argument
- // in register eax. Create a constant result for it.
- Result count(Handle<Smi>(Smi::FromInt(scope()->num_parameters())));
- // Call the shared stub to get to the arguments.length.
- ArgumentsAccessStub stub(ArgumentsAccessStub::READ_LENGTH);
- Result result = frame_->CallStub(&stub, &count);
+
+ Result fp = allocator_->Allocate();
+ Result result = allocator_->Allocate();
+ ASSERT(fp.is_valid() && result.is_valid());
+
+ Label exit;
+
+ // Get the number of formal parameters.
+ __ Set(result.reg(), Immediate(Smi::FromInt(scope()->num_parameters())));
+
+ // Check if the calling frame is an arguments adaptor frame.
+ __ mov(fp.reg(), Operand(ebp, StandardFrameConstants::kCallerFPOffset));
+ __ cmp(Operand(fp.reg(), StandardFrameConstants::kContextOffset),
+ Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
+ __ j(not_equal, &exit);
+
+ // Arguments adaptor case: Read the arguments length from the
+ // adaptor frame.
+ __ mov(result.reg(),
+ Operand(fp.reg(), ArgumentsAdaptorFrameConstants::kLengthOffset));
+
+ __ bind(&exit);
+ result.set_type_info(TypeInfo::Smi());
+ if (FLAG_debug_code) __ AbortIfNotSmi(result.reg());
frame_->Push(&result);
}
@@ -5695,7 +6334,7 @@
}
-void CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) {
+void CodeGenerator::GenerateArguments(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
// ArgumentsAccessStub expects the key in edx and the formal
@@ -5738,25 +6377,55 @@
}
-void CodeGenerator::GenerateRandomPositiveSmi(ZoneList<Expression*>* args) {
+void CodeGenerator::GenerateRandomHeapNumber(
+ ZoneList<Expression*>* args) {
ASSERT(args->length() == 0);
frame_->SpillAll();
- // Make sure the frame is aligned like the OS expects.
- static const int kFrameAlignment = OS::ActivationFrameAlignment();
- if (kFrameAlignment > 0) {
- ASSERT(IsPowerOf2(kFrameAlignment));
- __ mov(edi, Operand(esp)); // Save in callee-saved register.
- __ and_(esp, -kFrameAlignment);
- }
+ Label slow_allocate_heapnumber;
+ Label heapnumber_allocated;
- // Call V8::RandomPositiveSmi().
- __ call(FUNCTION_ADDR(V8::RandomPositiveSmi), RelocInfo::RUNTIME_ENTRY);
+ __ AllocateHeapNumber(edi, ebx, ecx, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
- // Restore stack pointer from callee-saved register edi.
- if (kFrameAlignment > 0) {
- __ mov(esp, Operand(edi));
+ __ bind(&slow_allocate_heapnumber);
+ // To allocate a heap number, and ensure that it is not a smi, we
+ // call the runtime function FUnaryMinus on 0, returning the double
+ // -0.0. A new, distinct heap number is returned each time.
+ __ push(Immediate(Smi::FromInt(0)));
+ __ CallRuntime(Runtime::kNumberUnaryMinus, 1);
+ __ mov(edi, eax);
+
+ __ bind(&heapnumber_allocated);
+
+ __ PrepareCallCFunction(0, ebx);
+ __ CallCFunction(ExternalReference::random_uint32_function(), 0);
+
+ // Convert 32 random bits in eax to 0.(32 random bits) in a double
+ // by computing:
+ // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
+ // This is implemented on both SSE2 and FPU.
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatures::Scope fscope(SSE2);
+ __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
+ __ movd(xmm1, Operand(ebx));
+ __ movd(xmm0, Operand(eax));
+ __ cvtss2sd(xmm1, xmm1);
+ __ pxor(xmm0, xmm1);
+ __ subsd(xmm0, xmm1);
+ __ movdbl(FieldOperand(edi, HeapNumber::kValueOffset), xmm0);
+ } else {
+ // 0x4130000000000000 is 1.0 x 2^20 as a double.
+ __ mov(FieldOperand(edi, HeapNumber::kExponentOffset),
+ Immediate(0x41300000));
+ __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), eax);
+ __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
+ __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), Immediate(0));
+ __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
+ __ fsubp(1);
+ __ fstp_d(FieldOperand(edi, HeapNumber::kValueOffset));
}
+ __ mov(eax, edi);
Result result = allocator_->Allocate(eax);
frame_->Push(&result);
@@ -5801,7 +6470,7 @@
void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
- ASSERT_EQ(args->length(), 4);
+ ASSERT_EQ(4, args->length());
// Load the arguments on the stack and call the stub.
Load(args->at(0));
@@ -5814,6 +6483,175 @@
}
+void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
+ // No stub. This code only occurs a few times in regexp.js.
+ const int kMaxInlineLength = 100;
+ ASSERT_EQ(3, args->length());
+ Load(args->at(0)); // Size of array, smi.
+ Load(args->at(1)); // "index" property value.
+ Load(args->at(2)); // "input" property value.
+ {
+ VirtualFrame::SpilledScope spilled_scope;
+
+ Label slowcase;
+ Label done;
+ __ mov(ebx, Operand(esp, kPointerSize * 2));
+ __ test(ebx, Immediate(kSmiTagMask));
+ __ j(not_zero, &slowcase);
+ __ cmp(Operand(ebx), Immediate(Smi::FromInt(kMaxInlineLength)));
+ __ j(above, &slowcase);
+ // Smi-tagging is equivalent to multiplying by 2.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+ // Allocate RegExpResult followed by FixedArray with size in ebx.
+ // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
+ // Elements: [Map][Length][..elements..]
+ __ AllocateInNewSpace(JSRegExpResult::kSize + FixedArray::kHeaderSize,
+ times_half_pointer_size,
+ ebx, // In: Number of elements (times 2, being a smi)
+ eax, // Out: Start of allocation (tagged).
+ ecx, // Out: End of allocation.
+ edx, // Scratch register
+ &slowcase,
+ TAG_OBJECT);
+ // eax: Start of allocated area, object-tagged.
+
+ // Set JSArray map to global.regexp_result_map().
+ // Set empty properties FixedArray.
+ // Set elements to point to FixedArray allocated right after the JSArray.
+ // Interleave operations for better latency.
+ __ mov(edx, ContextOperand(esi, Context::GLOBAL_INDEX));
+ __ mov(ecx, Immediate(Factory::empty_fixed_array()));
+ __ lea(ebx, Operand(eax, JSRegExpResult::kSize));
+ __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalContextOffset));
+ __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
+ __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ecx);
+ __ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
+ __ mov(FieldOperand(eax, HeapObject::kMapOffset), edx);
+
+ // Set input, index and length fields from arguments.
+ __ pop(FieldOperand(eax, JSRegExpResult::kInputOffset));
+ __ pop(FieldOperand(eax, JSRegExpResult::kIndexOffset));
+ __ pop(ecx);
+ __ mov(FieldOperand(eax, JSArray::kLengthOffset), ecx);
+
+ // Fill out the elements FixedArray.
+ // eax: JSArray.
+ // ebx: FixedArray.
+ // ecx: Number of elements in array, as smi.
+
+ // Set map.
+ __ mov(FieldOperand(ebx, HeapObject::kMapOffset),
+ Immediate(Factory::fixed_array_map()));
+ // Set length.
+ __ SmiUntag(ecx);
+ __ mov(FieldOperand(ebx, FixedArray::kLengthOffset), ecx);
+ // Fill contents of fixed-array with the-hole.
+ __ mov(edx, Immediate(Factory::the_hole_value()));
+ __ lea(ebx, FieldOperand(ebx, FixedArray::kHeaderSize));
+ // Fill fixed array elements with hole.
+ // eax: JSArray.
+ // ecx: Number of elements to fill.
+ // ebx: Start of elements in FixedArray.
+ // edx: the hole.
+ Label loop;
+ __ test(ecx, Operand(ecx));
+ __ bind(&loop);
+ __ j(less_equal, &done); // Jump if ecx is negative or zero.
+ __ sub(Operand(ecx), Immediate(1));
+ __ mov(Operand(ebx, ecx, times_pointer_size, 0), edx);
+ __ jmp(&loop);
+
+ __ bind(&slowcase);
+ __ CallRuntime(Runtime::kRegExpConstructResult, 3);
+
+ __ bind(&done);
+ }
+ frame_->Forget(3);
+ frame_->Push(eax);
+}
+
+
+class DeferredSearchCache: public DeferredCode {
+ public:
+ DeferredSearchCache(Register dst, Register cache, Register key)
+ : dst_(dst), cache_(cache), key_(key) {
+ set_comment("[ DeferredSearchCache");
+ }
+
+ virtual void Generate();
+
+ private:
+ Register dst_, cache_, key_;
+};
+
+
+void DeferredSearchCache::Generate() {
+ __ push(cache_);
+ __ push(key_);
+ __ CallRuntime(Runtime::kGetFromCache, 2);
+ if (!dst_.is(eax)) {
+ __ mov(dst_, eax);
+ }
+}
+
+
+void CodeGenerator::GenerateGetFromCache(ZoneList<Expression*>* args) {
+ ASSERT_EQ(2, args->length());
+
+ ASSERT_NE(NULL, args->at(0)->AsLiteral());
+ int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
+
+ Handle<FixedArray> jsfunction_result_caches(
+ Top::global_context()->jsfunction_result_caches());
+ if (jsfunction_result_caches->length() <= cache_id) {
+ __ Abort("Attempt to use undefined cache.");
+ frame_->Push(Factory::undefined_value());
+ return;
+ }
+
+ Load(args->at(1));
+ Result key = frame_->Pop();
+ key.ToRegister();
+
+ Result cache = allocator()->Allocate();
+ ASSERT(cache.is_valid());
+ __ mov(cache.reg(), ContextOperand(esi, Context::GLOBAL_INDEX));
+ __ mov(cache.reg(),
+ FieldOperand(cache.reg(), GlobalObject::kGlobalContextOffset));
+ __ mov(cache.reg(),
+ ContextOperand(cache.reg(), Context::JSFUNCTION_RESULT_CACHES_INDEX));
+ __ mov(cache.reg(),
+ FieldOperand(cache.reg(), FixedArray::OffsetOfElementAt(cache_id)));
+
+ Result tmp = allocator()->Allocate();
+ ASSERT(tmp.is_valid());
+
+ DeferredSearchCache* deferred = new DeferredSearchCache(tmp.reg(),
+ cache.reg(),
+ key.reg());
+
+ const int kFingerOffset =
+ FixedArray::OffsetOfElementAt(JSFunctionResultCache::kFingerIndex);
+ // tmp.reg() now holds finger offset as a smi.
+ ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
+ __ mov(tmp.reg(), FieldOperand(cache.reg(), kFingerOffset));
+ __ cmp(key.reg(), FieldOperand(cache.reg(),
+ tmp.reg(), // as smi
+ times_half_pointer_size,
+ FixedArray::kHeaderSize));
+ deferred->Branch(not_equal);
+
+ __ mov(tmp.reg(), FieldOperand(cache.reg(),
+ tmp.reg(), // as smi
+ times_half_pointer_size,
+ kPointerSize + FixedArray::kHeaderSize));
+
+ deferred->BindExit();
+ frame_->Push(&tmp);
+}
+
+
void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1);
@@ -5825,6 +6663,322 @@
}
+class DeferredSwapElements: public DeferredCode {
+ public:
+ DeferredSwapElements(Register object, Register index1, Register index2)
+ : object_(object), index1_(index1), index2_(index2) {
+ set_comment("[ DeferredSwapElements");
+ }
+
+ virtual void Generate();
+
+ private:
+ Register object_, index1_, index2_;
+};
+
+
+void DeferredSwapElements::Generate() {
+ __ push(object_);
+ __ push(index1_);
+ __ push(index2_);
+ __ CallRuntime(Runtime::kSwapElements, 3);
+}
+
+
+void CodeGenerator::GenerateSwapElements(ZoneList<Expression*>* args) {
+ // Note: this code assumes that indices are passed are within
+ // elements' bounds and refer to valid (not holes) values.
+ Comment cmnt(masm_, "[ GenerateSwapElements");
+
+ ASSERT_EQ(3, args->length());
+
+ Load(args->at(0));
+ Load(args->at(1));
+ Load(args->at(2));
+
+ Result index2 = frame_->Pop();
+ index2.ToRegister();
+
+ Result index1 = frame_->Pop();
+ index1.ToRegister();
+
+ Result object = frame_->Pop();
+ object.ToRegister();
+
+ Result tmp1 = allocator()->Allocate();
+ tmp1.ToRegister();
+ Result tmp2 = allocator()->Allocate();
+ tmp2.ToRegister();
+
+ frame_->Spill(object.reg());
+ frame_->Spill(index1.reg());
+ frame_->Spill(index2.reg());
+
+ DeferredSwapElements* deferred = new DeferredSwapElements(object.reg(),
+ index1.reg(),
+ index2.reg());
+
+ // Fetch the map and check if array is in fast case.
+ // Check that object doesn't require security checks and
+ // has no indexed interceptor.
+ __ CmpObjectType(object.reg(), FIRST_JS_OBJECT_TYPE, tmp1.reg());
+ deferred->Branch(less);
+ __ movzx_b(tmp1.reg(), FieldOperand(tmp1.reg(), Map::kBitFieldOffset));
+ __ test(tmp1.reg(), Immediate(KeyedLoadIC::kSlowCaseBitFieldMask));
+ deferred->Branch(not_zero);
+
+ // Check the object's elements are in fast case.
+ __ mov(tmp1.reg(), FieldOperand(object.reg(), JSObject::kElementsOffset));
+ __ cmp(FieldOperand(tmp1.reg(), HeapObject::kMapOffset),
+ Immediate(Factory::fixed_array_map()));
+ deferred->Branch(not_equal);
+
+ // Smi-tagging is equivalent to multiplying by 2.
+ STATIC_ASSERT(kSmiTag == 0);
+ STATIC_ASSERT(kSmiTagSize == 1);
+
+ // Check that both indices are smis.
+ __ mov(tmp2.reg(), index1.reg());
+ __ or_(tmp2.reg(), Operand(index2.reg()));
+ __ test(tmp2.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+
+ // Bring addresses into index1 and index2.
+ __ lea(index1.reg(), FieldOperand(tmp1.reg(),
+ index1.reg(),
+ times_half_pointer_size, // index1 is Smi
+ FixedArray::kHeaderSize));
+ __ lea(index2.reg(), FieldOperand(tmp1.reg(),
+ index2.reg(),
+ times_half_pointer_size, // index2 is Smi
+ FixedArray::kHeaderSize));
+
+ // Swap elements.
+ __ mov(object.reg(), Operand(index1.reg(), 0));
+ __ mov(tmp2.reg(), Operand(index2.reg(), 0));
+ __ mov(Operand(index2.reg(), 0), object.reg());
+ __ mov(Operand(index1.reg(), 0), tmp2.reg());
+
+ Label done;
+ __ InNewSpace(tmp1.reg(), tmp2.reg(), equal, &done);
+ // Possible optimization: do a check that both values are Smis
+ // (or them and test against Smi mask.)
+
+ __ mov(tmp2.reg(), tmp1.reg());
+ RecordWriteStub recordWrite1(tmp2.reg(), index1.reg(), object.reg());
+ __ CallStub(&recordWrite1);
+
+ RecordWriteStub recordWrite2(tmp1.reg(), index2.reg(), object.reg());
+ __ CallStub(&recordWrite2);
+
+ __ bind(&done);
+
+ deferred->BindExit();
+ frame_->Push(Factory::undefined_value());
+}
+
+
+void CodeGenerator::GenerateCallFunction(ZoneList<Expression*>* args) {
+ Comment cmnt(masm_, "[ GenerateCallFunction");
+
+ ASSERT(args->length() >= 2);
+
+ int n_args = args->length() - 2; // for receiver and function.
+ Load(args->at(0)); // receiver
+ for (int i = 0; i < n_args; i++) {
+ Load(args->at(i + 1));
+ }
+ Load(args->at(n_args + 1)); // function
+ Result result = frame_->CallJSFunction(n_args);
+ frame_->Push(&result);
+}
+
+
+// Generates the Math.pow method. Only handles special cases and
+// branches to the runtime system for everything else. Please note
+// that this function assumes that the callsite has executed ToNumber
+// on both arguments.
+void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 2);
+ Load(args->at(0));
+ Load(args->at(1));
+ if (!CpuFeatures::IsSupported(SSE2)) {
+ Result res = frame_->CallRuntime(Runtime::kMath_pow, 2);
+ frame_->Push(&res);
+ } else {
+ CpuFeatures::Scope use_sse2(SSE2);
+ Label allocate_return;
+ // Load the two operands while leaving the values on the frame.
+ frame()->Dup();
+ Result exponent = frame()->Pop();
+ exponent.ToRegister();
+ frame()->Spill(exponent.reg());
+ frame()->PushElementAt(1);
+ Result base = frame()->Pop();
+ base.ToRegister();
+ frame()->Spill(base.reg());
+
+ Result answer = allocator()->Allocate();
+ ASSERT(answer.is_valid());
+ ASSERT(!exponent.reg().is(base.reg()));
+ JumpTarget call_runtime;
+
+ // Save 1 in xmm3 - we need this several times later on.
+ __ mov(answer.reg(), Immediate(1));
+ __ cvtsi2sd(xmm3, Operand(answer.reg()));
+
+ Label exponent_nonsmi;
+ Label base_nonsmi;
+ // If the exponent is a heap number go to that specific case.
+ __ test(exponent.reg(), Immediate(kSmiTagMask));
+ __ j(not_zero, &exponent_nonsmi);
+ __ test(base.reg(), Immediate(kSmiTagMask));
+ __ j(not_zero, &base_nonsmi);
+
+ // Optimized version when y is an integer.
+ Label powi;
+ __ SmiUntag(base.reg());
+ __ cvtsi2sd(xmm0, Operand(base.reg()));
+ __ jmp(&powi);
+ // exponent is smi and base is a heapnumber.
+ __ bind(&base_nonsmi);
+ __ cmp(FieldOperand(base.reg(), HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ call_runtime.Branch(not_equal);
+
+ __ movdbl(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
+
+ // Optimized version of pow if y is an integer.
+ __ bind(&powi);
+ __ SmiUntag(exponent.reg());
+
+ // Save exponent in base as we need to check if exponent is negative later.
+ // We know that base and exponent are in different registers.
+ __ mov(base.reg(), exponent.reg());
+
+ // Get absolute value of exponent.
+ Label no_neg;
+ __ cmp(exponent.reg(), 0);
+ __ j(greater_equal, &no_neg);
+ __ neg(exponent.reg());
+ __ bind(&no_neg);
+
+ // Load xmm1 with 1.
+ __ movsd(xmm1, xmm3);
+ Label while_true;
+ Label no_multiply;
+
+ __ bind(&while_true);
+ __ shr(exponent.reg(), 1);
+ __ j(not_carry, &no_multiply);
+ __ mulsd(xmm1, xmm0);
+ __ bind(&no_multiply);
+ __ test(exponent.reg(), Operand(exponent.reg()));
+ __ mulsd(xmm0, xmm0);
+ __ j(not_zero, &while_true);
+
+ // x has the original value of y - if y is negative return 1/result.
+ __ test(base.reg(), Operand(base.reg()));
+ __ j(positive, &allocate_return);
+ // Special case if xmm1 has reached infinity.
+ __ mov(answer.reg(), Immediate(0x7FB00000));
+ __ movd(xmm0, Operand(answer.reg()));
+ __ cvtss2sd(xmm0, xmm0);
+ __ ucomisd(xmm0, xmm1);
+ call_runtime.Branch(equal);
+ __ divsd(xmm3, xmm1);
+ __ movsd(xmm1, xmm3);
+ __ jmp(&allocate_return);
+
+ // exponent (or both) is a heapnumber - no matter what we should now work
+ // on doubles.
+ __ bind(&exponent_nonsmi);
+ __ cmp(FieldOperand(exponent.reg(), HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ call_runtime.Branch(not_equal);
+ __ movdbl(xmm1, FieldOperand(exponent.reg(), HeapNumber::kValueOffset));
+ // Test if exponent is nan.
+ __ ucomisd(xmm1, xmm1);
+ call_runtime.Branch(parity_even);
+
+ Label base_not_smi;
+ Label handle_special_cases;
+ __ test(base.reg(), Immediate(kSmiTagMask));
+ __ j(not_zero, &base_not_smi);
+ __ SmiUntag(base.reg());
+ __ cvtsi2sd(xmm0, Operand(base.reg()));
+ __ jmp(&handle_special_cases);
+ __ bind(&base_not_smi);
+ __ cmp(FieldOperand(base.reg(), HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ call_runtime.Branch(not_equal);
+ __ mov(answer.reg(), FieldOperand(base.reg(), HeapNumber::kExponentOffset));
+ __ and_(answer.reg(), HeapNumber::kExponentMask);
+ __ cmp(Operand(answer.reg()), Immediate(HeapNumber::kExponentMask));
+ // base is NaN or +/-Infinity
+ call_runtime.Branch(greater_equal);
+ __ movdbl(xmm0, FieldOperand(base.reg(), HeapNumber::kValueOffset));
+
+ // base is in xmm0 and exponent is in xmm1.
+ __ bind(&handle_special_cases);
+ Label not_minus_half;
+ // Test for -0.5.
+ // Load xmm2 with -0.5.
+ __ mov(answer.reg(), Immediate(0xBF000000));
+ __ movd(xmm2, Operand(answer.reg()));
+ __ cvtss2sd(xmm2, xmm2);
+ // xmm2 now has -0.5.
+ __ ucomisd(xmm2, xmm1);
+ __ j(not_equal, ¬_minus_half);
+
+ // Calculates reciprocal of square root.
+ // Note that 1/sqrt(x) = sqrt(1/x))
+ __ divsd(xmm3, xmm0);
+ __ movsd(xmm1, xmm3);
+ __ sqrtsd(xmm1, xmm1);
+ __ jmp(&allocate_return);
+
+ // Test for 0.5.
+ __ bind(¬_minus_half);
+ // Load xmm2 with 0.5.
+ // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 + xmm3.
+ __ addsd(xmm2, xmm3);
+ // xmm2 now has 0.5.
+ __ comisd(xmm2, xmm1);
+ call_runtime.Branch(not_equal);
+ // Calculates square root.
+ __ movsd(xmm1, xmm0);
+ __ sqrtsd(xmm1, xmm1);
+
+ JumpTarget done;
+ Label failure, success;
+ __ bind(&allocate_return);
+ // Make a copy of the frame to enable us to handle allocation
+ // failure after the JumpTarget jump.
+ VirtualFrame* clone = new VirtualFrame(frame());
+ __ AllocateHeapNumber(answer.reg(), exponent.reg(),
+ base.reg(), &failure);
+ __ movdbl(FieldOperand(answer.reg(), HeapNumber::kValueOffset), xmm1);
+ // Remove the two original values from the frame - we only need those
+ // in the case where we branch to runtime.
+ frame()->Drop(2);
+ exponent.Unuse();
+ base.Unuse();
+ done.Jump(&answer);
+ // Use the copy of the original frame as our current frame.
+ RegisterFile empty_regs;
+ SetFrame(clone, &empty_regs);
+ // If we experience an allocation failure we branch to runtime.
+ __ bind(&failure);
+ call_runtime.Bind();
+ answer = frame()->CallRuntime(Runtime::kMath_pow_cfunction, 2);
+
+ done.Bind(&answer);
+ frame()->Push(&answer);
+ }
+}
+
+
void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1);
Load(args->at(0));
@@ -5843,7 +6997,65 @@
}
+// Generates the Math.sqrt method. Please note - this function assumes that
+// the callsite has executed ToNumber on the argument.
+void CodeGenerator::GenerateMathSqrt(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ Load(args->at(0));
+
+ if (!CpuFeatures::IsSupported(SSE2)) {
+ Result result = frame()->CallRuntime(Runtime::kMath_sqrt, 1);
+ frame()->Push(&result);
+ } else {
+ CpuFeatures::Scope use_sse2(SSE2);
+ // Leave original value on the frame if we need to call runtime.
+ frame()->Dup();
+ Result result = frame()->Pop();
+ result.ToRegister();
+ frame()->Spill(result.reg());
+ Label runtime;
+ Label non_smi;
+ Label load_done;
+ JumpTarget end;
+
+ __ test(result.reg(), Immediate(kSmiTagMask));
+ __ j(not_zero, &non_smi);
+ __ SmiUntag(result.reg());
+ __ cvtsi2sd(xmm0, Operand(result.reg()));
+ __ jmp(&load_done);
+ __ bind(&non_smi);
+ __ cmp(FieldOperand(result.reg(), HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ __ j(not_equal, &runtime);
+ __ movdbl(xmm0, FieldOperand(result.reg(), HeapNumber::kValueOffset));
+
+ __ bind(&load_done);
+ __ sqrtsd(xmm0, xmm0);
+ // A copy of the virtual frame to allow us to go to runtime after the
+ // JumpTarget jump.
+ Result scratch = allocator()->Allocate();
+ VirtualFrame* clone = new VirtualFrame(frame());
+ __ AllocateHeapNumber(result.reg(), scratch.reg(), no_reg, &runtime);
+
+ __ movdbl(FieldOperand(result.reg(), HeapNumber::kValueOffset), xmm0);
+ frame()->Drop(1);
+ scratch.Unuse();
+ end.Jump(&result);
+ // We only branch to runtime if we have an allocation error.
+ // Use the copy of the original frame as our current frame.
+ RegisterFile empty_regs;
+ SetFrame(clone, &empty_regs);
+ __ bind(&runtime);
+ result = frame()->CallRuntime(Runtime::kMath_sqrt, 1);
+
+ end.Bind(&result);
+ frame()->Push(&result);
+ }
+}
+
+
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
+ ASSERT(!in_safe_int32_mode());
if (CheckForInlineRuntimeCall(node)) {
return;
}
@@ -5970,64 +7182,123 @@
}
} else {
- Load(node->expression());
- bool overwrite =
- (node->expression()->AsBinaryOperation() != NULL &&
- node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
- switch (op) {
- case Token::SUB: {
- GenericUnaryOpStub stub(Token::SUB, overwrite);
- Result operand = frame_->Pop();
- Result answer = frame_->CallStub(&stub, &operand);
- frame_->Push(&answer);
- break;
+ if (in_safe_int32_mode()) {
+ Visit(node->expression());
+ Result value = frame_->Pop();
+ ASSERT(value.is_untagged_int32());
+ // Registers containing an int32 value are not multiply used.
+ ASSERT(!value.is_register() || !frame_->is_used(value.reg()));
+ value.ToRegister();
+ switch (op) {
+ case Token::SUB: {
+ __ neg(value.reg());
+ if (node->no_negative_zero()) {
+ // -MIN_INT is MIN_INT with the overflow flag set.
+ unsafe_bailout_->Branch(overflow);
+ } else {
+ // MIN_INT and 0 both have bad negations. They both have 31 zeros.
+ __ test(value.reg(), Immediate(0x7FFFFFFF));
+ unsafe_bailout_->Branch(zero);
+ }
+ break;
+ }
+ case Token::BIT_NOT: {
+ __ not_(value.reg());
+ break;
+ }
+ case Token::ADD: {
+ // Unary plus has no effect on int32 values.
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
}
+ frame_->Push(&value);
+ } else {
+ Load(node->expression());
+ bool overwrite =
+ (node->expression()->AsBinaryOperation() != NULL &&
+ node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
+ switch (op) {
+ case Token::NOT:
+ case Token::DELETE:
+ case Token::TYPEOF:
+ UNREACHABLE(); // handled above
+ break;
- case Token::BIT_NOT: {
- // Smi check.
- JumpTarget smi_label;
- JumpTarget continue_label;
- Result operand = frame_->Pop();
- operand.ToRegister();
- __ test(operand.reg(), Immediate(kSmiTagMask));
- smi_label.Branch(zero, &operand, taken);
+ case Token::SUB: {
+ GenericUnaryOpStub stub(Token::SUB, overwrite);
+ Result operand = frame_->Pop();
+ Result answer = frame_->CallStub(&stub, &operand);
+ answer.set_type_info(TypeInfo::Number());
+ frame_->Push(&answer);
+ break;
+ }
+ case Token::BIT_NOT: {
+ // Smi check.
+ JumpTarget smi_label;
+ JumpTarget continue_label;
+ Result operand = frame_->Pop();
+ TypeInfo operand_info = operand.type_info();
+ operand.ToRegister();
+ if (operand_info.IsSmi()) {
+ if (FLAG_debug_code) __ AbortIfNotSmi(operand.reg());
+ frame_->Spill(operand.reg());
+ // Set smi tag bit. It will be reset by the not operation.
+ __ lea(operand.reg(), Operand(operand.reg(), kSmiTagMask));
+ __ not_(operand.reg());
+ Result answer = operand;
+ answer.set_type_info(TypeInfo::Smi());
+ frame_->Push(&answer);
+ } else {
+ __ test(operand.reg(), Immediate(kSmiTagMask));
+ smi_label.Branch(zero, &operand, taken);
- GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
- Result answer = frame_->CallStub(&stub, &operand);
- continue_label.Jump(&answer);
+ GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
+ Result answer = frame_->CallStub(&stub, &operand);
+ continue_label.Jump(&answer);
- smi_label.Bind(&answer);
- answer.ToRegister();
- frame_->Spill(answer.reg());
- __ not_(answer.reg());
- __ and_(answer.reg(), ~kSmiTagMask); // Remove inverted smi-tag.
+ smi_label.Bind(&answer);
+ answer.ToRegister();
+ frame_->Spill(answer.reg());
+ // Set smi tag bit. It will be reset by the not operation.
+ __ lea(answer.reg(), Operand(answer.reg(), kSmiTagMask));
+ __ not_(answer.reg());
- continue_label.Bind(&answer);
- frame_->Push(&answer);
- break;
- }
+ continue_label.Bind(&answer);
+ answer.set_type_info(TypeInfo::Integer32());
+ frame_->Push(&answer);
+ }
+ break;
+ }
+ case Token::ADD: {
+ // Smi check.
+ JumpTarget continue_label;
+ Result operand = frame_->Pop();
+ TypeInfo operand_info = operand.type_info();
+ operand.ToRegister();
+ __ test(operand.reg(), Immediate(kSmiTagMask));
+ continue_label.Branch(zero, &operand, taken);
- case Token::ADD: {
- // Smi check.
- JumpTarget continue_label;
- Result operand = frame_->Pop();
- operand.ToRegister();
- __ test(operand.reg(), Immediate(kSmiTagMask));
- continue_label.Branch(zero, &operand, taken);
-
- frame_->Push(&operand);
- Result answer = frame_->InvokeBuiltin(Builtins::TO_NUMBER,
+ frame_->Push(&operand);
+ Result answer = frame_->InvokeBuiltin(Builtins::TO_NUMBER,
CALL_FUNCTION, 1);
- continue_label.Bind(&answer);
- frame_->Push(&answer);
- break;
+ continue_label.Bind(&answer);
+ if (operand_info.IsSmi()) {
+ answer.set_type_info(TypeInfo::Smi());
+ } else if (operand_info.IsInteger32()) {
+ answer.set_type_info(TypeInfo::Integer32());
+ } else {
+ answer.set_type_info(TypeInfo::Number());
+ }
+ frame_->Push(&answer);
+ break;
+ }
+ default:
+ UNREACHABLE();
}
-
- default:
- // NOT, DELETE, TYPEOF, and VOID are handled outside the
- // switch.
- UNREACHABLE();
}
}
}
@@ -6039,8 +7310,10 @@
// specialized add or subtract stub. The result is left in dst.
class DeferredPrefixCountOperation: public DeferredCode {
public:
- DeferredPrefixCountOperation(Register dst, bool is_increment)
- : dst_(dst), is_increment_(is_increment) {
+ DeferredPrefixCountOperation(Register dst,
+ bool is_increment,
+ TypeInfo input_type)
+ : dst_(dst), is_increment_(is_increment), input_type_(input_type) {
set_comment("[ DeferredCountOperation");
}
@@ -6049,6 +7322,7 @@
private:
Register dst_;
bool is_increment_;
+ TypeInfo input_type_;
};
@@ -6059,15 +7333,21 @@
} else {
__ add(Operand(dst_), Immediate(Smi::FromInt(1)));
}
- __ push(dst_);
- __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
- __ push(eax);
- __ push(Immediate(Smi::FromInt(1)));
- if (is_increment_) {
- __ CallRuntime(Runtime::kNumberAdd, 2);
+ Register left;
+ if (input_type_.IsNumber()) {
+ left = dst_;
} else {
- __ CallRuntime(Runtime::kNumberSub, 2);
+ __ push(dst_);
+ __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
+ left = eax;
}
+
+ GenericBinaryOpStub stub(is_increment_ ? Token::ADD : Token::SUB,
+ NO_OVERWRITE,
+ NO_GENERIC_BINARY_FLAGS,
+ TypeInfo::Number());
+ stub.GenerateCall(masm_, left, Smi::FromInt(1));
+
if (!dst_.is(eax)) __ mov(dst_, eax);
}
@@ -6079,8 +7359,14 @@
// The result is left in dst.
class DeferredPostfixCountOperation: public DeferredCode {
public:
- DeferredPostfixCountOperation(Register dst, Register old, bool is_increment)
- : dst_(dst), old_(old), is_increment_(is_increment) {
+ DeferredPostfixCountOperation(Register dst,
+ Register old,
+ bool is_increment,
+ TypeInfo input_type)
+ : dst_(dst),
+ old_(old),
+ is_increment_(is_increment),
+ input_type_(input_type) {
set_comment("[ DeferredCountOperation");
}
@@ -6090,6 +7376,7 @@
Register dst_;
Register old_;
bool is_increment_;
+ TypeInfo input_type_;
};
@@ -6100,26 +7387,30 @@
} else {
__ add(Operand(dst_), Immediate(Smi::FromInt(1)));
}
- __ push(dst_);
- __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
-
- // Save the result of ToNumber to use as the old value.
- __ push(eax);
-
- // Call the runtime for the addition or subtraction.
- __ push(eax);
- __ push(Immediate(Smi::FromInt(1)));
- if (is_increment_) {
- __ CallRuntime(Runtime::kNumberAdd, 2);
+ Register left;
+ if (input_type_.IsNumber()) {
+ __ push(dst_); // Save the input to use as the old value.
+ left = dst_;
} else {
- __ CallRuntime(Runtime::kNumberSub, 2);
+ __ push(dst_);
+ __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
+ __ push(eax); // Save the result of ToNumber to use as the old value.
+ left = eax;
}
+
+ GenericBinaryOpStub stub(is_increment_ ? Token::ADD : Token::SUB,
+ NO_OVERWRITE,
+ NO_GENERIC_BINARY_FLAGS,
+ TypeInfo::Number());
+ stub.GenerateCall(masm_, left, Smi::FromInt(1));
+
if (!dst_.is(eax)) __ mov(dst_, eax);
__ pop(old_);
}
void CodeGenerator::VisitCountOperation(CountOperation* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ CountOperation");
bool is_postfix = node->is_postfix();
@@ -6154,31 +7445,35 @@
old_value = allocator_->Allocate();
ASSERT(old_value.is_valid());
__ mov(old_value.reg(), new_value.reg());
+
+ // The return value for postfix operations is ToNumber(input).
+ // Keep more precise type info if the input is some kind of
+ // number already. If the input is not a number we have to wait
+ // for the deferred code to convert it.
+ if (new_value.type_info().IsNumber()) {
+ old_value.set_type_info(new_value.type_info());
+ }
}
+
// Ensure the new value is writable.
frame_->Spill(new_value.reg());
- // In order to combine the overflow and the smi tag check, we need
- // to be able to allocate a byte register. We attempt to do so
- // without spilling. If we fail, we will generate separate overflow
- // and smi tag checks.
- //
- // We allocate and clear the temporary byte register before
- // performing the count operation since clearing the register using
- // xor will clear the overflow flag.
- Result tmp = allocator_->AllocateByteRegisterWithoutSpilling();
- if (tmp.is_valid()) {
- __ Set(tmp.reg(), Immediate(0));
- }
-
- DeferredCode* deferred = NULL;
- if (is_postfix) {
- deferred = new DeferredPostfixCountOperation(new_value.reg(),
- old_value.reg(),
- is_increment);
+ Result tmp;
+ if (new_value.is_smi()) {
+ if (FLAG_debug_code) __ AbortIfNotSmi(new_value.reg());
} else {
- deferred = new DeferredPrefixCountOperation(new_value.reg(),
- is_increment);
+ // We don't know statically if the input is a smi.
+ // In order to combine the overflow and the smi tag check, we need
+ // to be able to allocate a byte register. We attempt to do so
+ // without spilling. If we fail, we will generate separate overflow
+ // and smi tag checks.
+ // We allocate and clear a temporary byte register before performing
+ // the count operation since clearing the register using xor will clear
+ // the overflow flag.
+ tmp = allocator_->AllocateByteRegisterWithoutSpilling();
+ if (tmp.is_valid()) {
+ __ Set(tmp.reg(), Immediate(0));
+ }
}
if (is_increment) {
@@ -6187,25 +7482,57 @@
__ sub(Operand(new_value.reg()), Immediate(Smi::FromInt(1)));
}
- // If the count operation didn't overflow and the result is a valid
- // smi, we're done. Otherwise, we jump to the deferred slow-case
- // code.
- if (tmp.is_valid()) {
+ DeferredCode* deferred = NULL;
+ if (is_postfix) {
+ deferred = new DeferredPostfixCountOperation(new_value.reg(),
+ old_value.reg(),
+ is_increment,
+ new_value.type_info());
+ } else {
+ deferred = new DeferredPrefixCountOperation(new_value.reg(),
+ is_increment,
+ new_value.type_info());
+ }
+
+ if (new_value.is_smi()) {
+ // In case we have a smi as input just check for overflow.
+ deferred->Branch(overflow);
+ } else {
+ // If the count operation didn't overflow and the result is a valid
+ // smi, we're done. Otherwise, we jump to the deferred slow-case
+ // code.
// We combine the overflow and the smi tag check if we could
// successfully allocate a temporary byte register.
- __ setcc(overflow, tmp.reg());
- __ or_(Operand(tmp.reg()), new_value.reg());
- __ test(tmp.reg(), Immediate(kSmiTagMask));
- tmp.Unuse();
- deferred->Branch(not_zero);
- } else {
- // Otherwise we test separately for overflow and smi tag.
- deferred->Branch(overflow);
- __ test(new_value.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (tmp.is_valid()) {
+ __ setcc(overflow, tmp.reg());
+ __ or_(Operand(tmp.reg()), new_value.reg());
+ __ test(tmp.reg(), Immediate(kSmiTagMask));
+ tmp.Unuse();
+ deferred->Branch(not_zero);
+ } else {
+ // Otherwise we test separately for overflow and smi tag.
+ deferred->Branch(overflow);
+ __ test(new_value.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ }
}
deferred->BindExit();
+ // Postfix count operations return their input converted to
+ // number. The case when the input is already a number is covered
+ // above in the allocation code for old_value.
+ if (is_postfix && !new_value.type_info().IsNumber()) {
+ old_value.set_type_info(TypeInfo::Number());
+ }
+
+ // The result of ++ or -- is an Integer32 if the
+ // input is a smi. Otherwise it is a number.
+ if (new_value.is_smi()) {
+ new_value.set_type_info(TypeInfo::Integer32());
+ } else {
+ new_value.set_type_info(TypeInfo::Number());
+ }
+
// Postfix: store the old value in the allocated slot under the
// reference.
if (is_postfix) frame_->SetElementAt(target.size(), &old_value);
@@ -6220,10 +7547,224 @@
}
-void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
- Comment cmnt(masm_, "[ BinaryOperation");
+void CodeGenerator::Int32BinaryOperation(BinaryOperation* node) {
Token::Value op = node->op();
+ Comment cmnt(masm_, "[ Int32BinaryOperation");
+ ASSERT(in_safe_int32_mode());
+ ASSERT(safe_int32_mode_enabled());
+ ASSERT(FLAG_safe_int32_compiler);
+ if (op == Token::COMMA) {
+ // Discard left value.
+ frame_->Nip(1);
+ return;
+ }
+
+ Result right = frame_->Pop();
+ Result left = frame_->Pop();
+
+ ASSERT(right.is_untagged_int32());
+ ASSERT(left.is_untagged_int32());
+ // Registers containing an int32 value are not multiply used.
+ ASSERT(!left.is_register() || !frame_->is_used(left.reg()));
+ ASSERT(!right.is_register() || !frame_->is_used(right.reg()));
+
+ switch (op) {
+ case Token::COMMA:
+ case Token::OR:
+ case Token::AND:
+ UNREACHABLE();
+ break;
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ if (left.is_constant() || right.is_constant()) {
+ int32_t value; // Put constant in value, non-constant in left.
+ // Constants are known to be int32 values, from static analysis,
+ // or else will be converted to int32 by implicit ECMA [[ToInt32]].
+ if (left.is_constant()) {
+ ASSERT(left.handle()->IsSmi() || left.handle()->IsHeapNumber());
+ value = NumberToInt32(*left.handle());
+ left = right;
+ } else {
+ ASSERT(right.handle()->IsSmi() || right.handle()->IsHeapNumber());
+ value = NumberToInt32(*right.handle());
+ }
+
+ left.ToRegister();
+ if (op == Token::BIT_OR) {
+ __ or_(Operand(left.reg()), Immediate(value));
+ } else if (op == Token::BIT_XOR) {
+ __ xor_(Operand(left.reg()), Immediate(value));
+ } else {
+ ASSERT(op == Token::BIT_AND);
+ __ and_(Operand(left.reg()), Immediate(value));
+ }
+ } else {
+ ASSERT(left.is_register());
+ ASSERT(right.is_register());
+ if (op == Token::BIT_OR) {
+ __ or_(left.reg(), Operand(right.reg()));
+ } else if (op == Token::BIT_XOR) {
+ __ xor_(left.reg(), Operand(right.reg()));
+ } else {
+ ASSERT(op == Token::BIT_AND);
+ __ and_(left.reg(), Operand(right.reg()));
+ }
+ }
+ frame_->Push(&left);
+ right.Unuse();
+ break;
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR: {
+ bool test_shr_overflow = false;
+ left.ToRegister();
+ if (right.is_constant()) {
+ ASSERT(right.handle()->IsSmi() || right.handle()->IsHeapNumber());
+ int shift_amount = NumberToInt32(*right.handle()) & 0x1F;
+ if (op == Token::SAR) {
+ __ sar(left.reg(), shift_amount);
+ } else if (op == Token::SHL) {
+ __ shl(left.reg(), shift_amount);
+ } else {
+ ASSERT(op == Token::SHR);
+ __ shr(left.reg(), shift_amount);
+ if (shift_amount == 0) test_shr_overflow = true;
+ }
+ } else {
+ // Move right to ecx
+ if (left.is_register() && left.reg().is(ecx)) {
+ right.ToRegister();
+ __ xchg(left.reg(), right.reg());
+ left = right; // Left is unused here, copy of right unused by Push.
+ } else {
+ right.ToRegister(ecx);
+ left.ToRegister();
+ }
+ if (op == Token::SAR) {
+ __ sar_cl(left.reg());
+ } else if (op == Token::SHL) {
+ __ shl_cl(left.reg());
+ } else {
+ ASSERT(op == Token::SHR);
+ __ shr_cl(left.reg());
+ test_shr_overflow = true;
+ }
+ }
+ {
+ Register left_reg = left.reg();
+ frame_->Push(&left);
+ right.Unuse();
+ if (test_shr_overflow && !node->to_int32()) {
+ // Uint32 results with top bit set are not Int32 values.
+ // If they will be forced to Int32, skip the test.
+ // Test is needed because shr with shift amount 0 does not set flags.
+ __ test(left_reg, Operand(left_reg));
+ unsafe_bailout_->Branch(sign);
+ }
+ }
+ break;
+ }
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ if ((left.is_constant() && op != Token::SUB) || right.is_constant()) {
+ int32_t value; // Put constant in value, non-constant in left.
+ if (right.is_constant()) {
+ ASSERT(right.handle()->IsSmi() || right.handle()->IsHeapNumber());
+ value = NumberToInt32(*right.handle());
+ } else {
+ ASSERT(left.handle()->IsSmi() || left.handle()->IsHeapNumber());
+ value = NumberToInt32(*left.handle());
+ left = right;
+ }
+
+ left.ToRegister();
+ if (op == Token::ADD) {
+ __ add(Operand(left.reg()), Immediate(value));
+ } else if (op == Token::SUB) {
+ __ sub(Operand(left.reg()), Immediate(value));
+ } else {
+ ASSERT(op == Token::MUL);
+ __ imul(left.reg(), left.reg(), value);
+ }
+ } else {
+ left.ToRegister();
+ ASSERT(left.is_register());
+ ASSERT(right.is_register());
+ if (op == Token::ADD) {
+ __ add(left.reg(), Operand(right.reg()));
+ } else if (op == Token::SUB) {
+ __ sub(left.reg(), Operand(right.reg()));
+ } else {
+ ASSERT(op == Token::MUL);
+ // We have statically verified that a negative zero can be ignored.
+ __ imul(left.reg(), Operand(right.reg()));
+ }
+ }
+ right.Unuse();
+ frame_->Push(&left);
+ if (!node->to_int32()) {
+ // If ToInt32 is called on the result of ADD, SUB, or MUL, we don't
+ // care about overflows.
+ unsafe_bailout_->Branch(overflow);
+ }
+ break;
+ case Token::DIV:
+ case Token::MOD: {
+ if (right.is_register() && (right.reg().is(eax) || right.reg().is(edx))) {
+ if (left.is_register() && left.reg().is(edi)) {
+ right.ToRegister(ebx);
+ } else {
+ right.ToRegister(edi);
+ }
+ }
+ left.ToRegister(eax);
+ Result edx_reg = allocator_->Allocate(edx);
+ right.ToRegister();
+ // The results are unused here because BreakTarget::Branch cannot handle
+ // live results.
+ Register right_reg = right.reg();
+ left.Unuse();
+ right.Unuse();
+ edx_reg.Unuse();
+ __ cmp(right_reg, 0);
+ // Ensure divisor is positive: no chance of non-int32 or -0 result.
+ unsafe_bailout_->Branch(less_equal);
+ __ cdq(); // Sign-extend eax into edx:eax
+ __ idiv(right_reg);
+ if (op == Token::MOD) {
+ // Negative zero can arise as a negative divident with a zero result.
+ if (!node->no_negative_zero()) {
+ Label not_negative_zero;
+ __ test(edx, Operand(edx));
+ __ j(not_zero, ¬_negative_zero);
+ __ test(eax, Operand(eax));
+ unsafe_bailout_->Branch(negative);
+ __ bind(¬_negative_zero);
+ }
+ Result edx_result(edx, TypeInfo::Integer32());
+ edx_result.set_untagged_int32(true);
+ frame_->Push(&edx_result);
+ } else {
+ ASSERT(op == Token::DIV);
+ __ test(edx, Operand(edx));
+ unsafe_bailout_->Branch(not_equal);
+ Result eax_result(eax, TypeInfo::Integer32());
+ eax_result.set_untagged_int32(true);
+ frame_->Push(&eax_result);
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
+
+void CodeGenerator::GenerateLogicalBooleanOperation(BinaryOperation* node) {
// According to ECMA-262 section 11.11, page 58, the binary logical
// operators must yield the result of one of the two expressions
// before any ToBoolean() conversions. This means that the value
@@ -6233,7 +7774,8 @@
// control flow), we force the right hand side to do the same. This
// is necessary because we assume that if we get control flow on the
// last path out of an expression we got it on all paths.
- if (op == Token::AND) {
+ if (node->op() == Token::AND) {
+ ASSERT(!in_safe_int32_mode());
JumpTarget is_true;
ControlDestination dest(&is_true, destination()->false_target(), true);
LoadCondition(node->left(), &dest, false);
@@ -6296,7 +7838,9 @@
exit.Bind();
}
- } else if (op == Token::OR) {
+ } else {
+ ASSERT(node->op() == Token::OR);
+ ASSERT(!in_safe_int32_mode());
JumpTarget is_false;
ControlDestination dest(destination()->true_target(), &is_false, false);
LoadCondition(node->left(), &dest, false);
@@ -6357,7 +7901,19 @@
// Exit (always with a materialized value).
exit.Bind();
}
+ }
+}
+
+void CodeGenerator::VisitBinaryOperation(BinaryOperation* node) {
+ Comment cmnt(masm_, "[ BinaryOperation");
+
+ if (node->op() == Token::AND || node->op() == Token::OR) {
+ GenerateLogicalBooleanOperation(node);
+ } else if (in_safe_int32_mode()) {
+ Visit(node->left());
+ Visit(node->right());
+ Int32BinaryOperation(node);
} else {
// NOTE: The code below assumes that the slow cases (calls to runtime)
// never return a constant/immutable object.
@@ -6370,19 +7926,28 @@
overwrite_mode = OVERWRITE_RIGHT;
}
- Load(node->left());
- Load(node->right());
- GenericBinaryOperation(node->op(), node->type(), overwrite_mode);
+ if (node->left()->IsTrivial()) {
+ Load(node->right());
+ Result right = frame_->Pop();
+ frame_->Push(node->left());
+ frame_->Push(&right);
+ } else {
+ Load(node->left());
+ Load(node->right());
+ }
+ GenericBinaryOperation(node, overwrite_mode);
}
}
void CodeGenerator::VisitThisFunction(ThisFunction* node) {
+ ASSERT(!in_safe_int32_mode());
frame_->PushFunction();
}
void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
+ ASSERT(!in_safe_int32_mode());
Comment cmnt(masm_, "[ CompareOperation");
bool left_already_loaded = false;
@@ -6571,8 +8136,20 @@
default:
UNREACHABLE();
}
- if (!left_already_loaded) Load(left);
- Load(right);
+
+ if (left->IsTrivial()) {
+ if (!left_already_loaded) {
+ Load(right);
+ Result right_result = frame_->Pop();
+ frame_->Push(left);
+ frame_->Push(&right_result);
+ } else {
+ Load(right);
+ }
+ } else {
+ if (!left_already_loaded) Load(left);
+ Load(right);
+ }
Comparison(node, cc, strict, destination());
}
@@ -6702,8 +8279,12 @@
public:
DeferredReferenceSetKeyedValue(Register value,
Register key,
- Register receiver)
- : value_(value), key_(key), receiver_(receiver) {
+ Register receiver,
+ Register scratch)
+ : value_(value),
+ key_(key),
+ receiver_(receiver),
+ scratch_(scratch) {
set_comment("[ DeferredReferenceSetKeyedValue");
}
@@ -6715,17 +8296,65 @@
Register value_;
Register key_;
Register receiver_;
+ Register scratch_;
Label patch_site_;
};
void DeferredReferenceSetKeyedValue::Generate() {
__ IncrementCounter(&Counters::keyed_store_inline_miss, 1);
- // Push receiver and key arguments on the stack.
- __ push(receiver_);
- __ push(key_);
- // Move value argument to eax as expected by the IC stub.
- if (!value_.is(eax)) __ mov(eax, value_);
+ // Move value_ to eax, key_ to ecx, and receiver_ to edx.
+ Register old_value = value_;
+
+ // First, move value to eax.
+ if (!value_.is(eax)) {
+ if (key_.is(eax)) {
+ // Move key_ out of eax, preferably to ecx.
+ if (!value_.is(ecx) && !receiver_.is(ecx)) {
+ __ mov(ecx, key_);
+ key_ = ecx;
+ } else {
+ __ mov(scratch_, key_);
+ key_ = scratch_;
+ }
+ }
+ if (receiver_.is(eax)) {
+ // Move receiver_ out of eax, preferably to edx.
+ if (!value_.is(edx) && !key_.is(edx)) {
+ __ mov(edx, receiver_);
+ receiver_ = edx;
+ } else {
+ // Both moves to scratch are from eax, also, no valid path hits both.
+ __ mov(scratch_, receiver_);
+ receiver_ = scratch_;
+ }
+ }
+ __ mov(eax, value_);
+ value_ = eax;
+ }
+
+ // Now value_ is in eax. Move the other two to the right positions.
+ // We do not update the variables key_ and receiver_ to ecx and edx.
+ if (key_.is(ecx)) {
+ if (!receiver_.is(edx)) {
+ __ mov(edx, receiver_);
+ }
+ } else if (key_.is(edx)) {
+ if (receiver_.is(ecx)) {
+ __ xchg(edx, ecx);
+ } else {
+ __ mov(ecx, key_);
+ if (!receiver_.is(edx)) {
+ __ mov(edx, receiver_);
+ }
+ }
+ } else { // Key is not in edx or ecx.
+ if (!receiver_.is(edx)) {
+ __ mov(edx, receiver_);
+ }
+ __ mov(ecx, key_);
+ }
+
// Call the IC stub.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
@@ -6738,11 +8367,8 @@
// Here we use masm_-> instead of the __ macro because this is the
// instruction that gets patched and coverage code gets in the way.
masm_->test(eax, Immediate(-delta_to_patch_site));
- // Restore value (returned from store IC), key and receiver
- // registers.
- if (!value_.is(eax)) __ mov(value_, eax);
- __ pop(key_);
- __ pop(receiver_);
+ // Restore value (returned from store IC) register.
+ if (!old_value.is(eax)) __ mov(old_value, eax);
}
@@ -6866,8 +8492,12 @@
deferred->Branch(not_equal);
// Check that the key is a smi.
- __ test(key.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
+ if (!key.is_smi()) {
+ __ test(key.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ } else {
+ if (FLAG_debug_code) __ AbortIfNotSmi(key.reg());
+ }
// Get the elements array from the receiver and check that it
// is not a dictionary.
@@ -6878,7 +8508,7 @@
deferred->Branch(not_equal);
// Shift the key to get the actual index value and check that
- // it is within bounds.
+ // it is within bounds. Use unsigned comparison to handle negative keys.
__ mov(result.reg(), key.reg());
__ SmiUntag(result.reg());
__ cmp(result.reg(),
@@ -6928,6 +8558,8 @@
Result tmp = allocator_->Allocate();
ASSERT(tmp.is_valid());
+ Result tmp2 = allocator_->Allocate();
+ ASSERT(tmp2.is_valid());
// Determine whether the value is a constant before putting it in a
// register.
@@ -6941,41 +8573,47 @@
DeferredReferenceSetKeyedValue* deferred =
new DeferredReferenceSetKeyedValue(result.reg(),
key.reg(),
- receiver.reg());
-
- // Check that the value is a smi if it is not a constant. We can skip
- // the write barrier for smis and constants.
- if (!value_is_constant) {
- __ test(result.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- }
-
- // Check that the key is a non-negative smi.
- __ test(key.reg(), Immediate(kSmiTagMask | 0x80000000));
- deferred->Branch(not_zero);
+ receiver.reg(),
+ tmp.reg());
// Check that the receiver is not a smi.
__ test(receiver.reg(), Immediate(kSmiTagMask));
deferred->Branch(zero);
+ // Check that the key is a smi.
+ if (!key.is_smi()) {
+ __ test(key.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ } else {
+ if (FLAG_debug_code) __ AbortIfNotSmi(key.reg());
+ }
+
// Check that the receiver is a JSArray.
- __ mov(tmp.reg(),
- FieldOperand(receiver.reg(), HeapObject::kMapOffset));
- __ movzx_b(tmp.reg(),
- FieldOperand(tmp.reg(), Map::kInstanceTypeOffset));
- __ cmp(tmp.reg(), JS_ARRAY_TYPE);
+ __ CmpObjectType(receiver.reg(), JS_ARRAY_TYPE, tmp.reg());
deferred->Branch(not_equal);
// Check that the key is within bounds. Both the key and the length of
- // the JSArray are smis.
+ // the JSArray are smis. Use unsigned comparison to handle negative keys.
__ cmp(key.reg(),
FieldOperand(receiver.reg(), JSArray::kLengthOffset));
- deferred->Branch(greater_equal);
+ deferred->Branch(above_equal);
// Get the elements array from the receiver and check that it is not a
// dictionary.
__ mov(tmp.reg(),
- FieldOperand(receiver.reg(), JSObject::kElementsOffset));
+ FieldOperand(receiver.reg(), JSArray::kElementsOffset));
+
+ // Check whether it is possible to omit the write barrier. If the elements
+ // array is in new space or the value written is a smi we can safely update
+ // the elements array without updating the remembered set.
+ Label in_new_space;
+ __ InNewSpace(tmp.reg(), tmp2.reg(), equal, &in_new_space);
+ if (!value_is_constant) {
+ __ test(result.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ }
+
+ __ bind(&in_new_space);
// Bind the deferred code patch site to be able to locate the fixed
// array map comparison. When debugging, we patch this comparison to
// always fail so that we will hit the IC call in the deferred code
@@ -7001,7 +8639,6 @@
// indicate that we have generated an inline version of the
// keyed store.
__ nop();
- frame()->Drop(2);
}
ASSERT(frame()->height() == original_height - 3);
return result;
@@ -7012,6 +8649,40 @@
#define __ ACCESS_MASM(masm)
+static void CheckTwoForSminess(MacroAssembler* masm,
+ Register left, Register right, Register scratch,
+ TypeInfo left_info, TypeInfo right_info,
+ DeferredInlineBinaryOperation* deferred) {
+ if (left.is(right)) {
+ if (!left_info.IsSmi()) {
+ __ test(left, Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ } else {
+ if (FLAG_debug_code) __ AbortIfNotSmi(left);
+ }
+ } else if (!left_info.IsSmi()) {
+ if (!right_info.IsSmi()) {
+ __ mov(scratch, left);
+ __ or_(scratch, Operand(right));
+ __ test(scratch, Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ } else {
+ __ test(left, Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ if (FLAG_debug_code) __ AbortIfNotSmi(right);
+ }
+ } else {
+ if (FLAG_debug_code) __ AbortIfNotSmi(left);
+ if (!right_info.IsSmi()) {
+ __ test(right, Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ } else {
+ if (FLAG_debug_code) __ AbortIfNotSmi(right);
+ }
+ }
+}
+
+
Handle<String> Reference::GetName() {
ASSERT(type_ == NAMED);
Property* property = expression_->AsProperty();
@@ -7143,6 +8814,7 @@
Comment cmnt(masm, "[ Store to keyed Property");
Property* property = expression()->AsProperty();
ASSERT(property != NULL);
+
Result answer = cgen_->EmitKeyedStore(property->key()->type());
cgen_->frame()->Push(&answer);
set_unloaded();
@@ -7157,12 +8829,12 @@
void FastNewClosureStub::Generate(MacroAssembler* masm) {
- // Clone the boilerplate in new space. Set the context to the
- // current context in esi.
+ // Create a new closure from the given function info in new
+ // space. Set the context to the current context in esi.
Label gc;
__ AllocateInNewSpace(JSFunction::kSize, eax, ebx, ecx, &gc, TAG_OBJECT);
- // Get the boilerplate function from the stack.
+ // Get the function info from the stack.
__ mov(edx, Operand(esp, 1 * kPointerSize));
// Compute the function map in the current global context and set that
@@ -7172,18 +8844,16 @@
__ mov(ecx, Operand(ecx, Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
__ mov(FieldOperand(eax, JSObject::kMapOffset), ecx);
- // Clone the rest of the boilerplate fields. We don't have to update
- // the write barrier because the allocated object is in new space.
- for (int offset = kPointerSize;
- offset < JSFunction::kSize;
- offset += kPointerSize) {
- if (offset == JSFunction::kContextOffset) {
- __ mov(FieldOperand(eax, offset), esi);
- } else {
- __ mov(ebx, FieldOperand(edx, offset));
- __ mov(FieldOperand(eax, offset), ebx);
- }
- }
+ // Initialize the rest of the function. We don't have to update the
+ // write barrier because the allocated object is in new space.
+ __ mov(ebx, Immediate(Factory::empty_fixed_array()));
+ __ mov(FieldOperand(eax, JSObject::kPropertiesOffset), ebx);
+ __ mov(FieldOperand(eax, JSObject::kElementsOffset), ebx);
+ __ mov(FieldOperand(eax, JSFunction::kPrototypeOrInitialMapOffset),
+ Immediate(Factory::the_hole_value()));
+ __ mov(FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset), edx);
+ __ mov(FieldOperand(eax, JSFunction::kContextOffset), esi);
+ __ mov(FieldOperand(eax, JSFunction::kLiteralsOffset), ebx);
// Return and remove the on-stack parameter.
__ ret(1 * kPointerSize);
@@ -7195,7 +8865,7 @@
__ push(esi);
__ push(edx);
__ push(ecx); // Restore return address.
- __ TailCallRuntime(ExternalReference(Runtime::kNewClosure), 2, 1);
+ __ TailCallRuntime(Runtime::kNewClosure, 2, 1);
}
@@ -7239,7 +8909,7 @@
// Need to collect. Call into runtime system.
__ bind(&gc);
- __ TailCallRuntime(ExternalReference(Runtime::kNewContext), 1, 1);
+ __ TailCallRuntime(Runtime::kNewContext, 1, 1);
}
@@ -7294,8 +8964,7 @@
__ ret(3 * kPointerSize);
__ bind(&slow_case);
- ExternalReference runtime(Runtime::kCreateArrayLiteralShallow);
- __ TailCallRuntime(runtime, 3, 1);
+ __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
}
@@ -7325,6 +8994,7 @@
__ cmp(ecx, FIRST_NONSTRING_TYPE);
__ j(above_equal, ¬_string);
__ mov(edx, FieldOperand(eax, String::kLengthOffset));
+ ASSERT(kSmiTag == 0);
__ test(edx, Operand(edx));
__ j(zero, &false_result);
__ jmp(&true_result);
@@ -7512,6 +9182,26 @@
__ mov(left, Operand(esp, 2 * kPointerSize));
}
+ if (static_operands_type_.IsSmi()) {
+ if (FLAG_debug_code) {
+ __ AbortIfNotSmi(left);
+ __ AbortIfNotSmi(right);
+ }
+ if (op_ == Token::BIT_OR) {
+ __ or_(right, Operand(left));
+ GenerateReturn(masm);
+ return;
+ } else if (op_ == Token::BIT_AND) {
+ __ and_(right, Operand(left));
+ GenerateReturn(masm);
+ return;
+ } else if (op_ == Token::BIT_XOR) {
+ __ xor_(right, Operand(left));
+ GenerateReturn(masm);
+ return;
+ }
+ }
+
// 2. Prepare the smi check of both operands by oring them together.
Comment smi_check_comment(masm, "-- Smi check arguments");
Label not_smis;
@@ -7820,146 +9510,181 @@
// Generate fast case smi code if requested. This flag is set when the fast
// case smi code is not generated by the caller. Generating it here will speed
// up common operations.
- if (HasSmiCodeInStub()) {
+ if (ShouldGenerateSmiCode()) {
GenerateSmiCode(masm, &call_runtime);
} else if (op_ != Token::MOD) { // MOD goes straight to runtime.
- GenerateLoadArguments(masm);
+ if (!HasArgsInRegisters()) {
+ GenerateLoadArguments(masm);
+ }
}
// Floating point case.
- switch (op_) {
- case Token::ADD:
- case Token::SUB:
- case Token::MUL:
- case Token::DIV: {
- if (CpuFeatures::IsSupported(SSE2)) {
- CpuFeatures::Scope use_sse2(SSE2);
- if (NumberInfo::IsNumber(operands_type_)) {
- if (FLAG_debug_code) {
- // Assert at runtime that inputs are only numbers.
- __ AbortIfNotNumber(edx,
- "GenericBinaryOpStub operand not a number.");
- __ AbortIfNotNumber(eax,
- "GenericBinaryOpStub operand not a number.");
- }
- FloatingPointHelper::LoadSSE2Operands(masm);
- } else {
- FloatingPointHelper::LoadSSE2Operands(masm, &call_runtime);
+ if (ShouldGenerateFPCode()) {
+ switch (op_) {
+ case Token::ADD:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV: {
+ if (runtime_operands_type_ == BinaryOpIC::DEFAULT &&
+ HasSmiCodeInStub()) {
+ // Execution reaches this point when the first non-smi argument occurs
+ // (and only if smi code is generated). This is the right moment to
+ // patch to HEAP_NUMBERS state. The transition is attempted only for
+ // the four basic operations. The stub stays in the DEFAULT state
+ // forever for all other operations (also if smi code is skipped).
+ GenerateTypeTransition(masm);
}
- switch (op_) {
- case Token::ADD: __ addsd(xmm0, xmm1); break;
- case Token::SUB: __ subsd(xmm0, xmm1); break;
- case Token::MUL: __ mulsd(xmm0, xmm1); break;
- case Token::DIV: __ divsd(xmm0, xmm1); break;
- default: UNREACHABLE();
- }
- GenerateHeapResultAllocation(masm, &call_runtime);
- __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
- GenerateReturn(masm);
- } else { // SSE2 not available, use FPU.
- if (NumberInfo::IsNumber(operands_type_)) {
- if (FLAG_debug_code) {
- // Assert at runtime that inputs are only numbers.
- __ AbortIfNotNumber(edx,
- "GenericBinaryOpStub operand not a number.");
- __ AbortIfNotNumber(eax,
- "GenericBinaryOpStub operand not a number.");
- }
- } else {
- FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
- }
- FloatingPointHelper::LoadFloatOperands(
- masm,
- ecx,
- FloatingPointHelper::ARGS_IN_REGISTERS);
- switch (op_) {
- case Token::ADD: __ faddp(1); break;
- case Token::SUB: __ fsubp(1); break;
- case Token::MUL: __ fmulp(1); break;
- case Token::DIV: __ fdivp(1); break;
- default: UNREACHABLE();
- }
- Label after_alloc_failure;
- GenerateHeapResultAllocation(masm, &after_alloc_failure);
- __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
- GenerateReturn(masm);
- __ bind(&after_alloc_failure);
- __ ffree();
- __ jmp(&call_runtime);
- }
- }
- case Token::MOD: {
- // For MOD we go directly to runtime in the non-smi case.
- break;
- }
- case Token::BIT_OR:
- case Token::BIT_AND:
- case Token::BIT_XOR:
- case Token::SAR:
- case Token::SHL:
- case Token::SHR: {
- Label non_smi_result;
- FloatingPointHelper::LoadAsIntegers(masm, use_sse3_, &call_runtime);
- switch (op_) {
- case Token::BIT_OR: __ or_(eax, Operand(ecx)); break;
- case Token::BIT_AND: __ and_(eax, Operand(ecx)); break;
- case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break;
- case Token::SAR: __ sar_cl(eax); break;
- case Token::SHL: __ shl_cl(eax); break;
- case Token::SHR: __ shr_cl(eax); break;
- default: UNREACHABLE();
- }
- if (op_ == Token::SHR) {
- // Check if result is non-negative and fits in a smi.
- __ test(eax, Immediate(0xc0000000));
- __ j(not_zero, &call_runtime);
- } else {
- // Check if result fits in a smi.
- __ cmp(eax, 0xc0000000);
- __ j(negative, &non_smi_result);
- }
- // Tag smi result and return.
- __ SmiTag(eax);
- GenerateReturn(masm);
-
- // All ops except SHR return a signed int32 that we load in a HeapNumber.
- if (op_ != Token::SHR) {
- __ bind(&non_smi_result);
- // Allocate a heap number if needed.
- __ mov(ebx, Operand(eax)); // ebx: result
- Label skip_allocation;
- switch (mode_) {
- case OVERWRITE_LEFT:
- case OVERWRITE_RIGHT:
- // If the operand was an object, we skip the
- // allocation of a heap number.
- __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
- 1 * kPointerSize : 2 * kPointerSize));
- __ test(eax, Immediate(kSmiTagMask));
- __ j(not_zero, &skip_allocation, not_taken);
- // Fall through!
- case NO_OVERWRITE:
- __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
- __ bind(&skip_allocation);
- break;
- default: UNREACHABLE();
- }
- // Store the result in the HeapNumber and return.
+ Label not_floats;
if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
- __ cvtsi2sd(xmm0, Operand(ebx));
+ if (static_operands_type_.IsNumber()) {
+ if (FLAG_debug_code) {
+ // Assert at runtime that inputs are only numbers.
+ __ AbortIfNotNumber(edx);
+ __ AbortIfNotNumber(eax);
+ }
+ if (static_operands_type_.IsSmi()) {
+ if (FLAG_debug_code) {
+ __ AbortIfNotSmi(edx);
+ __ AbortIfNotSmi(eax);
+ }
+ FloatingPointHelper::LoadSSE2Smis(masm, ecx);
+ } else {
+ FloatingPointHelper::LoadSSE2Operands(masm);
+ }
+ } else {
+ FloatingPointHelper::LoadSSE2Operands(masm, &call_runtime);
+ }
+
+ switch (op_) {
+ case Token::ADD: __ addsd(xmm0, xmm1); break;
+ case Token::SUB: __ subsd(xmm0, xmm1); break;
+ case Token::MUL: __ mulsd(xmm0, xmm1); break;
+ case Token::DIV: __ divsd(xmm0, xmm1); break;
+ default: UNREACHABLE();
+ }
+ GenerateHeapResultAllocation(masm, &call_runtime);
__ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
- } else {
- __ mov(Operand(esp, 1 * kPointerSize), ebx);
- __ fild_s(Operand(esp, 1 * kPointerSize));
+ GenerateReturn(masm);
+ } else { // SSE2 not available, use FPU.
+ if (static_operands_type_.IsNumber()) {
+ if (FLAG_debug_code) {
+ // Assert at runtime that inputs are only numbers.
+ __ AbortIfNotNumber(edx);
+ __ AbortIfNotNumber(eax);
+ }
+ } else {
+ FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
+ }
+ FloatingPointHelper::LoadFloatOperands(
+ masm,
+ ecx,
+ FloatingPointHelper::ARGS_IN_REGISTERS);
+ switch (op_) {
+ case Token::ADD: __ faddp(1); break;
+ case Token::SUB: __ fsubp(1); break;
+ case Token::MUL: __ fmulp(1); break;
+ case Token::DIV: __ fdivp(1); break;
+ default: UNREACHABLE();
+ }
+ Label after_alloc_failure;
+ GenerateHeapResultAllocation(masm, &after_alloc_failure);
__ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ GenerateReturn(masm);
+ __ bind(&after_alloc_failure);
+ __ ffree();
+ __ jmp(&call_runtime);
}
- GenerateReturn(masm);
+ __ bind(¬_floats);
+ if (runtime_operands_type_ == BinaryOpIC::DEFAULT &&
+ !HasSmiCodeInStub()) {
+ // Execution reaches this point when the first non-number argument
+ // occurs (and only if smi code is skipped from the stub, otherwise
+ // the patching has already been done earlier in this case branch).
+ // Try patching to STRINGS for ADD operation.
+ if (op_ == Token::ADD) {
+ GenerateTypeTransition(masm);
+ }
+ }
+ break;
}
- break;
+ case Token::MOD: {
+ // For MOD we go directly to runtime in the non-smi case.
+ break;
+ }
+ case Token::BIT_OR:
+ case Token::BIT_AND:
+ case Token::BIT_XOR:
+ case Token::SAR:
+ case Token::SHL:
+ case Token::SHR: {
+ Label non_smi_result;
+ FloatingPointHelper::LoadAsIntegers(masm,
+ static_operands_type_,
+ use_sse3_,
+ &call_runtime);
+ switch (op_) {
+ case Token::BIT_OR: __ or_(eax, Operand(ecx)); break;
+ case Token::BIT_AND: __ and_(eax, Operand(ecx)); break;
+ case Token::BIT_XOR: __ xor_(eax, Operand(ecx)); break;
+ case Token::SAR: __ sar_cl(eax); break;
+ case Token::SHL: __ shl_cl(eax); break;
+ case Token::SHR: __ shr_cl(eax); break;
+ default: UNREACHABLE();
+ }
+ if (op_ == Token::SHR) {
+ // Check if result is non-negative and fits in a smi.
+ __ test(eax, Immediate(0xc0000000));
+ __ j(not_zero, &call_runtime);
+ } else {
+ // Check if result fits in a smi.
+ __ cmp(eax, 0xc0000000);
+ __ j(negative, &non_smi_result);
+ }
+ // Tag smi result and return.
+ __ SmiTag(eax);
+ GenerateReturn(masm);
+
+ // All ops except SHR return a signed int32 that we load in
+ // a HeapNumber.
+ if (op_ != Token::SHR) {
+ __ bind(&non_smi_result);
+ // Allocate a heap number if needed.
+ __ mov(ebx, Operand(eax)); // ebx: result
+ Label skip_allocation;
+ switch (mode_) {
+ case OVERWRITE_LEFT:
+ case OVERWRITE_RIGHT:
+ // If the operand was an object, we skip the
+ // allocation of a heap number.
+ __ mov(eax, Operand(esp, mode_ == OVERWRITE_RIGHT ?
+ 1 * kPointerSize : 2 * kPointerSize));
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(not_zero, &skip_allocation, not_taken);
+ // Fall through!
+ case NO_OVERWRITE:
+ __ AllocateHeapNumber(eax, ecx, edx, &call_runtime);
+ __ bind(&skip_allocation);
+ break;
+ default: UNREACHABLE();
+ }
+ // Store the result in the HeapNumber and return.
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatures::Scope use_sse2(SSE2);
+ __ cvtsi2sd(xmm0, Operand(ebx));
+ __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
+ } else {
+ __ mov(Operand(esp, 1 * kPointerSize), ebx);
+ __ fild_s(Operand(esp, 1 * kPointerSize));
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ }
+ GenerateReturn(masm);
+ }
+ break;
+ }
+ default: UNREACHABLE(); break;
}
- default: UNREACHABLE(); break;
}
// If all else fails, use the runtime system to get the correct
@@ -7967,30 +9692,40 @@
// stack in the correct order below the return address.
__ bind(&call_runtime);
if (HasArgsInRegisters()) {
- __ pop(ecx);
- if (HasArgsReversed()) {
- __ push(eax);
- __ push(edx);
- } else {
- __ push(edx);
- __ push(eax);
- }
- __ push(ecx);
+ GenerateRegisterArgsPush(masm);
}
+
switch (op_) {
case Token::ADD: {
// Test for string arguments before calling runtime.
Label not_strings, not_string1, string1, string1_smi2;
- Result answer;
- __ test(edx, Immediate(kSmiTagMask));
+
+ // If this stub has already generated FP-specific code then the arguments
+ // are already in edx, eax
+ if (!ShouldGenerateFPCode() && !HasArgsInRegisters()) {
+ GenerateLoadArguments(masm);
+ }
+
+ // Registers containing left and right operands respectively.
+ Register lhs, rhs;
+ if (HasArgsReversed()) {
+ lhs = eax;
+ rhs = edx;
+ } else {
+ lhs = edx;
+ rhs = eax;
+ }
+
+ // Test if first argument is a string.
+ __ test(lhs, Immediate(kSmiTagMask));
__ j(zero, ¬_string1);
- __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ecx);
+ __ CmpObjectType(lhs, FIRST_NONSTRING_TYPE, ecx);
__ j(above_equal, ¬_string1);
// First argument is a string, test second.
- __ test(eax, Immediate(kSmiTagMask));
+ __ test(rhs, Immediate(kSmiTagMask));
__ j(zero, &string1_smi2);
- __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ecx);
+ __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, ecx);
__ j(above_equal, &string1);
// First and second argument are strings. Jump to the string add stub.
@@ -8001,36 +9736,26 @@
// 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, eax, edi, ebx, ecx, true, &string1);
+ masm, rhs, edi, ebx, ecx, true, &string1);
- // Call the string add stub to make the result.
- __ EnterInternalFrame();
- __ push(edx); // Original first argument.
- __ push(edi); // Number to string result for second argument.
- __ CallStub(&string_add_stub);
- __ LeaveInternalFrame();
- __ ret(2 * kPointerSize);
+ // Replace second argument on stack and tailcall string add stub to make
+ // the result.
+ __ mov(Operand(esp, 1 * kPointerSize), edi);
+ __ TailCallStub(&string_add_stub);
+ // Only first argument is a string.
__ bind(&string1);
- __ InvokeBuiltin(
- HasArgsReversed() ?
- Builtins::STRING_ADD_RIGHT :
- Builtins::STRING_ADD_LEFT,
- JUMP_FUNCTION);
+ __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION);
// First argument was not a string, test second.
__ bind(¬_string1);
- __ test(eax, Immediate(kSmiTagMask));
+ __ test(rhs, Immediate(kSmiTagMask));
__ j(zero, ¬_strings);
- __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ecx);
+ __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, ecx);
__ j(above_equal, ¬_strings);
// Only second argument is a string.
- __ InvokeBuiltin(
- HasArgsReversed() ?
- Builtins::STRING_ADD_LEFT :
- Builtins::STRING_ADD_RIGHT,
- JUMP_FUNCTION);
+ __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION);
__ bind(¬_strings);
// Neither argument is a string.
@@ -8123,10 +9848,9 @@
void GenericBinaryOpStub::GenerateLoadArguments(MacroAssembler* masm) {
// If arguments are not passed in registers read them from the stack.
- if (!HasArgsInRegisters()) {
- __ mov(eax, Operand(esp, 1 * kPointerSize));
- __ mov(edx, Operand(esp, 2 * kPointerSize));
- }
+ ASSERT(!HasArgsInRegisters());
+ __ mov(eax, Operand(esp, 1 * kPointerSize));
+ __ mov(edx, Operand(esp, 2 * kPointerSize));
}
@@ -8141,6 +9865,75 @@
}
+void GenericBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+ ASSERT(HasArgsInRegisters());
+ __ pop(ecx);
+ if (HasArgsReversed()) {
+ __ push(eax);
+ __ push(edx);
+ } else {
+ __ push(edx);
+ __ push(eax);
+ }
+ __ push(ecx);
+}
+
+
+void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ Label get_result;
+
+ // Keep a copy of operands on the stack and make sure they are also in
+ // edx, eax.
+ if (HasArgsInRegisters()) {
+ GenerateRegisterArgsPush(masm);
+ } else {
+ GenerateLoadArguments(masm);
+ }
+
+ // Internal frame is necessary to handle exceptions properly.
+ __ EnterInternalFrame();
+
+ // Push arguments on stack if the stub expects them there.
+ if (!HasArgsInRegisters()) {
+ __ push(edx);
+ __ push(eax);
+ }
+ // Call the stub proper to get the result in eax.
+ __ call(&get_result);
+ __ LeaveInternalFrame();
+
+ __ pop(ecx); // Return address.
+ // Left and right arguments are now on top.
+ // Push the operation result. The tail call to BinaryOp_Patch will
+ // return it to the original caller.
+ __ push(eax);
+ // Push this stub's key. Although the operation and the type info are
+ // encoded into the key, the encoding is opaque, so push them too.
+ __ push(Immediate(Smi::FromInt(MinorKey())));
+ __ push(Immediate(Smi::FromInt(op_)));
+ __ push(Immediate(Smi::FromInt(runtime_operands_type_)));
+
+ __ push(ecx); // Return address.
+
+ // Patch the caller to an appropriate specialized stub
+ // and return the operation result.
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
+ 6,
+ 1);
+
+ // The entry point for the result calculation is assumed to be immediately
+ // after this sequence.
+ __ bind(&get_result);
+}
+
+
+Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) {
+ GenericBinaryOpStub stub(key, type_info);
+ return stub.GetCode();
+}
+
+
void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
// Input on stack:
// esp[4]: argument (should be number).
@@ -8205,8 +9998,7 @@
__ j(zero, &runtime_call_clear_stack);
#ifdef DEBUG
// Check that the layout of cache elements match expectations.
- { // NOLINT - doesn't like a single brace on a line.
- TranscendentalCache::Element test_elem[2];
+ { TranscendentalCache::Element test_elem[2];
char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
@@ -8247,7 +10039,7 @@
__ bind(&runtime_call_clear_stack);
__ fstp(0);
__ bind(&runtime_call);
- __ TailCallRuntime(ExternalReference(RuntimeFunction()), 1, 1);
+ __ TailCallExternalReference(ExternalReference(RuntimeFunction()), 1, 1);
}
@@ -8353,24 +10145,29 @@
// trashed registers.
void IntegerConvert(MacroAssembler* masm,
Register source,
+ TypeInfo type_info,
bool use_sse3,
Label* conversion_failure) {
ASSERT(!source.is(ecx) && !source.is(edi) && !source.is(ebx));
Label done, right_exponent, normal_exponent;
Register scratch = ebx;
Register scratch2 = edi;
- // Get exponent word.
- __ mov(scratch, FieldOperand(source, HeapNumber::kExponentOffset));
- // Get exponent alone in scratch2.
- __ mov(scratch2, scratch);
- __ and_(scratch2, HeapNumber::kExponentMask);
+ if (!type_info.IsInteger32() || !use_sse3) {
+ // Get exponent word.
+ __ mov(scratch, FieldOperand(source, HeapNumber::kExponentOffset));
+ // Get exponent alone in scratch2.
+ __ mov(scratch2, scratch);
+ __ and_(scratch2, HeapNumber::kExponentMask);
+ }
if (use_sse3) {
CpuFeatures::Scope scope(SSE3);
- // Check whether the exponent is too big for a 64 bit signed integer.
- static const uint32_t kTooBigExponent =
- (HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
- __ cmp(Operand(scratch2), Immediate(kTooBigExponent));
- __ j(greater_equal, conversion_failure);
+ if (!type_info.IsInteger32()) {
+ // Check whether the exponent is too big for a 64 bit signed integer.
+ static const uint32_t kTooBigExponent =
+ (HeapNumber::kExponentBias + 63) << HeapNumber::kExponentShift;
+ __ cmp(Operand(scratch2), Immediate(kTooBigExponent));
+ __ j(greater_equal, conversion_failure);
+ }
// Load x87 register with heap number.
__ fld_d(FieldOperand(source, HeapNumber::kValueOffset));
// Reserve space for 64 bit answer.
@@ -8484,16 +10281,70 @@
// Input: edx, eax are the left and right objects of a bit op.
// Output: eax, ecx are left and right integers for a bit op.
-void FloatingPointHelper::LoadAsIntegers(MacroAssembler* masm,
- bool use_sse3,
- Label* conversion_failure) {
+void FloatingPointHelper::LoadNumbersAsIntegers(MacroAssembler* masm,
+ TypeInfo type_info,
+ bool use_sse3,
+ Label* conversion_failure) {
// Check float operands.
Label arg1_is_object, check_undefined_arg1;
Label arg2_is_object, check_undefined_arg2;
Label load_arg2, done;
+ if (!type_info.IsDouble()) {
+ if (!type_info.IsSmi()) {
+ __ test(edx, Immediate(kSmiTagMask));
+ __ j(not_zero, &arg1_is_object);
+ } else {
+ if (FLAG_debug_code) __ AbortIfNotSmi(edx);
+ }
+ __ SmiUntag(edx);
+ __ jmp(&load_arg2);
+ }
+
+ __ bind(&arg1_is_object);
+
+ // Get the untagged integer version of the edx heap number in ecx.
+ IntegerConvert(masm, edx, type_info, use_sse3, conversion_failure);
+ __ mov(edx, ecx);
+
+ // Here edx has the untagged integer, eax has a Smi or a heap number.
+ __ bind(&load_arg2);
+ if (!type_info.IsDouble()) {
+ // Test if arg2 is a Smi.
+ if (!type_info.IsSmi()) {
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(not_zero, &arg2_is_object);
+ } else {
+ if (FLAG_debug_code) __ AbortIfNotSmi(eax);
+ }
+ __ SmiUntag(eax);
+ __ mov(ecx, eax);
+ __ jmp(&done);
+ }
+
+ __ bind(&arg2_is_object);
+
+ // Get the untagged integer version of the eax heap number in ecx.
+ IntegerConvert(masm, eax, type_info, use_sse3, conversion_failure);
+ __ bind(&done);
+ __ mov(eax, edx);
+}
+
+
+// Input: edx, eax are the left and right objects of a bit op.
+// Output: eax, ecx are left and right integers for a bit op.
+void FloatingPointHelper::LoadUnknownsAsIntegers(MacroAssembler* masm,
+ bool use_sse3,
+ Label* conversion_failure) {
+ // Check float operands.
+ Label arg1_is_object, check_undefined_arg1;
+ Label arg2_is_object, check_undefined_arg2;
+ Label load_arg2, done;
+
+ // Test if arg1 is a Smi.
__ test(edx, Immediate(kSmiTagMask));
__ j(not_zero, &arg1_is_object);
+
__ SmiUntag(edx);
__ jmp(&load_arg2);
@@ -8508,15 +10359,22 @@
__ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
__ cmp(ebx, Factory::heap_number_map());
__ j(not_equal, &check_undefined_arg1);
+
// Get the untagged integer version of the edx heap number in ecx.
- IntegerConvert(masm, edx, use_sse3, conversion_failure);
+ IntegerConvert(masm,
+ edx,
+ TypeInfo::Unknown(),
+ use_sse3,
+ conversion_failure);
__ mov(edx, ecx);
// Here edx has the untagged integer, eax has a Smi or a heap number.
__ bind(&load_arg2);
+
// Test if arg2 is a Smi.
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &arg2_is_object);
+
__ SmiUntag(eax);
__ mov(ecx, eax);
__ jmp(&done);
@@ -8532,13 +10390,30 @@
__ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
__ cmp(ebx, Factory::heap_number_map());
__ j(not_equal, &check_undefined_arg2);
+
// Get the untagged integer version of the eax heap number in ecx.
- IntegerConvert(masm, eax, use_sse3, conversion_failure);
+ IntegerConvert(masm,
+ eax,
+ TypeInfo::Unknown(),
+ use_sse3,
+ conversion_failure);
__ bind(&done);
__ mov(eax, edx);
}
+void FloatingPointHelper::LoadAsIntegers(MacroAssembler* masm,
+ TypeInfo type_info,
+ bool use_sse3,
+ Label* conversion_failure) {
+ if (type_info.IsNumber()) {
+ LoadNumbersAsIntegers(masm, type_info, use_sse3, conversion_failure);
+ } else {
+ LoadUnknownsAsIntegers(masm, use_sse3, conversion_failure);
+ }
+}
+
+
void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
Register number) {
Label load_smi, done;
@@ -8775,7 +10650,11 @@
__ j(not_equal, &slow, not_taken);
// Convert the heap number in eax to an untagged integer in ecx.
- IntegerConvert(masm, eax, CpuFeatures::IsSupported(SSE3), &slow);
+ IntegerConvert(masm,
+ eax,
+ TypeInfo::Unknown(),
+ CpuFeatures::IsSupported(SSE3),
+ &slow);
// Do the bitwise operation and check if the result fits in a smi.
Label try_float;
@@ -8833,30 +10712,6 @@
}
-void ArgumentsAccessStub::GenerateReadLength(MacroAssembler* masm) {
- // Check if the calling frame is an arguments adaptor frame.
- __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
- __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
- __ cmp(Operand(ecx), Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
-
- // Arguments adaptor case: Read the arguments length from the
- // adaptor frame and return it.
- // Otherwise nothing to do: The number of formal parameters has already been
- // passed in register eax by calling function. Just return it.
- if (CpuFeatures::IsSupported(CMOV)) {
- CpuFeatures::Scope use_cmov(CMOV);
- __ cmov(equal, eax,
- Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
- } else {
- Label exit;
- __ j(not_equal, &exit);
- __ mov(eax, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
- __ bind(&exit);
- }
- __ ret(0);
-}
-
-
void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
// The key is in edx and the parameter count is in eax.
@@ -8911,7 +10766,7 @@
__ pop(ebx); // Return address.
__ push(edx);
__ push(ebx);
- __ TailCallRuntime(ExternalReference(Runtime::kGetArgumentsProperty), 1, 1);
+ __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
}
@@ -9012,7 +10867,7 @@
// Do the runtime call to allocate the arguments object.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3, 1);
+ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
}
@@ -9020,11 +10875,11 @@
// Just jump directly to runtime if native RegExp is not selected at compile
// time or if regexp entry in generated code is turned off runtime switch or
// at compilation.
-#ifndef V8_NATIVE_REGEXP
- __ TailCallRuntime(ExternalReference(Runtime::kRegExpExec), 4, 1);
-#else // V8_NATIVE_REGEXP
+#ifdef V8_INTERPRETED_REGEXP
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+#else // V8_INTERPRETED_REGEXP
if (!FLAG_regexp_entry_native) {
- __ TailCallRuntime(ExternalReference(Runtime::kRegExpExec), 4, 1);
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
return;
}
@@ -9096,16 +10951,16 @@
// Get the length of the string to ebx.
__ mov(ebx, FieldOperand(eax, String::kLengthOffset));
- // ebx: Length of subject string
+ // ebx: Length of subject string as a smi
// ecx: RegExp data (FixedArray)
// edx: Number of capture registers
- // Check that the third argument is a positive smi.
// Check that the third argument is a positive smi less than the subject
- // string length. A negative value will be greater (usigned comparison).
+ // string length. A negative value will be greater (unsigned comparison).
__ mov(eax, Operand(esp, kPreviousIndexOffset));
- __ SmiUntag(eax);
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(zero, &runtime);
__ cmp(eax, Operand(ebx));
- __ j(above, &runtime);
+ __ j(above_equal, &runtime);
// ecx: RegExp data (FixedArray)
// edx: Number of capture registers
@@ -9148,9 +11003,8 @@
// string. In that case the subject string is just the first part of the cons
// string. Also in this case the first part of the cons string is known to be
// a sequential string or an external string.
- __ mov(edx, ebx);
- __ and_(edx, kStringRepresentationMask);
- __ cmp(edx, kConsStringTag);
+ __ and_(ebx, kStringRepresentationMask);
+ __ cmp(ebx, kConsStringTag);
__ j(not_equal, &runtime);
__ mov(edx, FieldOperand(eax, ConsString::kSecondOffset));
__ cmp(Operand(edx), Factory::empty_string());
@@ -9169,7 +11023,8 @@
// ecx: RegExp data (FixedArray)
// Check that the irregexp code has been generated for an ascii string. If
// it has, the field contains a code object otherwise it contains the hole.
- __ cmp(ebx, kStringTag | kSeqStringTag | kTwoByteStringTag);
+ const int kSeqTwoByteString = kStringTag | kSeqStringTag | kTwoByteStringTag;
+ __ cmp(ebx, kSeqTwoByteString);
__ j(equal, &seq_two_byte_string);
if (FLAG_debug_code) {
__ cmp(ebx, kStringTag | kSeqStringTag | kAsciiStringTag);
@@ -9207,48 +11062,52 @@
// All checks done. Now push arguments for native regexp code.
__ IncrementCounter(&Counters::regexp_entry_native, 1);
+ static const int kRegExpExecuteArguments = 7;
+ __ PrepareCallCFunction(kRegExpExecuteArguments, ecx);
+
// Argument 7: Indicate that this is a direct call from JavaScript.
- __ push(Immediate(1));
+ __ mov(Operand(esp, 6 * kPointerSize), Immediate(1));
// Argument 6: Start (high end) of backtracking stack memory area.
__ mov(ecx, Operand::StaticVariable(address_of_regexp_stack_memory_address));
__ add(ecx, Operand::StaticVariable(address_of_regexp_stack_memory_size));
- __ push(ecx);
+ __ mov(Operand(esp, 5 * kPointerSize), ecx);
// Argument 5: static offsets vector buffer.
- __ push(Immediate(ExternalReference::address_of_static_offsets_vector()));
+ __ mov(Operand(esp, 4 * kPointerSize),
+ Immediate(ExternalReference::address_of_static_offsets_vector()));
// Argument 4: End of string data
// Argument 3: Start of string data
- Label push_two_byte, push_rest;
+ Label setup_two_byte, setup_rest;
__ test(edi, Operand(edi));
__ mov(edi, FieldOperand(eax, String::kLengthOffset));
- __ j(zero, &push_two_byte);
+ __ j(zero, &setup_two_byte);
+ __ SmiUntag(edi);
__ lea(ecx, FieldOperand(eax, edi, times_1, SeqAsciiString::kHeaderSize));
- __ push(ecx); // Argument 4.
+ __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
__ lea(ecx, FieldOperand(eax, ebx, times_1, SeqAsciiString::kHeaderSize));
- __ push(ecx); // Argument 3.
- __ jmp(&push_rest);
+ __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
+ __ jmp(&setup_rest);
- __ bind(&push_two_byte);
- __ lea(ecx, FieldOperand(eax, edi, times_2, SeqTwoByteString::kHeaderSize));
- __ push(ecx); // Argument 4.
+ __ bind(&setup_two_byte);
+ ASSERT(kSmiTag == 0 && kSmiTagSize == 1); // edi is smi (powered by 2).
+ __ lea(ecx, FieldOperand(eax, edi, times_1, SeqTwoByteString::kHeaderSize));
+ __ mov(Operand(esp, 3 * kPointerSize), ecx); // Argument 4.
__ lea(ecx, FieldOperand(eax, ebx, times_2, SeqTwoByteString::kHeaderSize));
- __ push(ecx); // Argument 3.
+ __ mov(Operand(esp, 2 * kPointerSize), ecx); // Argument 3.
- __ bind(&push_rest);
+ __ bind(&setup_rest);
// Argument 2: Previous index.
- __ push(ebx);
+ __ mov(Operand(esp, 1 * kPointerSize), ebx);
// Argument 1: Subject string.
- __ push(eax);
+ __ mov(Operand(esp, 0 * kPointerSize), eax);
// Locate the code entry and call it.
__ add(Operand(edx), Immediate(Code::kHeaderSize - kHeapObjectTag));
- __ call(Operand(edx));
- // Remove arguments.
- __ add(Operand(esp), Immediate(7 * kPointerSize));
+ __ CallCFunction(edx, kRegExpExecuteArguments);
// Check the result.
Label success;
@@ -9263,7 +11122,7 @@
// Result must now be exception. If there is no pending exception already a
// stack overflow (on the backtrack stack) was detected in RegExp code but
// haven't created the exception yet. Handle that in the runtime system.
- // TODO(592) Rerunning the RegExp to get the stack overflow exception.
+ // TODO(592): Rerunning the RegExp to get the stack overflow exception.
ExternalReference pending_exception(Top::k_pending_exception_address);
__ mov(eax,
Operand::StaticVariable(ExternalReference::the_hole_value_location()));
@@ -9314,7 +11173,6 @@
// ecx: offsets vector
// edx: number of capture registers
Label next_capture, done;
- __ mov(eax, Operand(esp, kPreviousIndexOffset));
// Capture register counter starts from number of capture registers and
// counts down until wraping after zero.
__ bind(&next_capture);
@@ -9322,15 +11180,7 @@
__ j(negative, &done);
// Read the value from the static offsets vector buffer.
__ mov(edi, Operand(ecx, edx, times_int_size, 0));
- // Perform explicit shift
- ASSERT_EQ(0, kSmiTag);
- __ shl(edi, kSmiTagSize);
- // Add previous index (from its stack slot) if value is not negative.
- Label capture_negative;
- // Carry flag set by shift above.
- __ j(negative, &capture_negative, not_taken);
- __ add(edi, Operand(eax)); // Add previous index (adding smi to smi).
- __ bind(&capture_negative);
+ __ SmiTag(edi);
// Store the smi value in the last match info.
__ mov(FieldOperand(ebx,
edx,
@@ -9346,8 +11196,8 @@
// Do the runtime call to execute the regexp.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kRegExpExec), 4, 1);
-#endif // V8_NATIVE_REGEXP
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
+#endif // V8_INTERPRETED_REGEXP
}
@@ -9358,14 +11208,6 @@
Register scratch2,
bool object_is_smi,
Label* not_found) {
- // Currently only lookup for smis. Check for smi if object is not known to be
- // a smi.
- if (!object_is_smi) {
- ASSERT(kSmiTag == 0);
- __ test(object, Immediate(kSmiTagMask));
- __ j(not_zero, not_found);
- }
-
// Use of registers. Register result is used as a temporary.
Register number_string_cache = result;
Register mask = scratch1;
@@ -9381,23 +11223,74 @@
__ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset));
__ shr(mask, 1); // Divide length by two (length is not a smi).
__ sub(Operand(mask), Immediate(1)); // Make mask.
+
// Calculate the entry in the number string cache. The hash value in the
- // number string cache for smis is just the smi value.
- __ mov(scratch, object);
- __ SmiUntag(scratch);
+ // number string cache for smis is just the smi value, and the hash for
+ // doubles is the xor of the upper and lower words. See
+ // Heap::GetNumberStringCache.
+ Label smi_hash_calculated;
+ Label load_result_from_cache;
+ if (object_is_smi) {
+ __ mov(scratch, object);
+ __ SmiUntag(scratch);
+ } else {
+ Label not_smi, hash_calculated;
+ ASSERT(kSmiTag == 0);
+ __ test(object, Immediate(kSmiTagMask));
+ __ j(not_zero, ¬_smi);
+ __ mov(scratch, object);
+ __ SmiUntag(scratch);
+ __ jmp(&smi_hash_calculated);
+ __ bind(¬_smi);
+ __ cmp(FieldOperand(object, HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ __ j(not_equal, not_found);
+ ASSERT_EQ(8, kDoubleSize);
+ __ mov(scratch, FieldOperand(object, HeapNumber::kValueOffset));
+ __ xor_(scratch, FieldOperand(object, HeapNumber::kValueOffset + 4));
+ // Object is heap number and hash is now in scratch. Calculate cache index.
+ __ and_(scratch, Operand(mask));
+ Register index = scratch;
+ Register probe = mask;
+ __ mov(probe,
+ FieldOperand(number_string_cache,
+ index,
+ times_twice_pointer_size,
+ FixedArray::kHeaderSize));
+ __ test(probe, Immediate(kSmiTagMask));
+ __ j(zero, not_found);
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatures::Scope fscope(SSE2);
+ __ movdbl(xmm0, FieldOperand(object, HeapNumber::kValueOffset));
+ __ movdbl(xmm1, FieldOperand(probe, HeapNumber::kValueOffset));
+ __ comisd(xmm0, xmm1);
+ } else {
+ __ fld_d(FieldOperand(object, HeapNumber::kValueOffset));
+ __ fld_d(FieldOperand(probe, HeapNumber::kValueOffset));
+ __ FCmp();
+ }
+ __ j(parity_even, not_found); // Bail out if NaN is involved.
+ __ j(not_equal, not_found); // The cache did not contain this value.
+ __ jmp(&load_result_from_cache);
+ }
+
+ __ bind(&smi_hash_calculated);
+ // Object is smi and hash is now in scratch. Calculate cache index.
__ and_(scratch, Operand(mask));
+ Register index = scratch;
// Check if the entry is the smi we are looking for.
__ cmp(object,
FieldOperand(number_string_cache,
- scratch,
+ index,
times_twice_pointer_size,
FixedArray::kHeaderSize));
__ j(not_equal, not_found);
// Get the result from the cache.
+ __ bind(&load_result_from_cache);
__ mov(result,
FieldOperand(number_string_cache,
- scratch,
+ index,
times_twice_pointer_size,
FixedArray::kHeaderSize + kPointerSize));
__ IncrementCounter(&Counters::number_to_string_native, 1);
@@ -9415,7 +11308,21 @@
__ bind(&runtime);
// Handle number to string in the runtime system if not found in the cache.
- __ TailCallRuntime(ExternalReference(Runtime::kNumberToString), 1, 1);
+ __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
+}
+
+
+void RecordWriteStub::Generate(MacroAssembler* masm) {
+ masm->RecordWriteHelper(object_, addr_, scratch_);
+ masm->ret(0);
+}
+
+
+static int NegativeComparisonResult(Condition cc) {
+ ASSERT(cc != equal);
+ ASSERT((cc == less) || (cc == less_equal)
+ || (cc == greater) || (cc == greater_equal));
+ return (cc == greater || cc == greater_equal) ? LESS : GREATER;
}
@@ -9425,56 +11332,80 @@
// NOTICE! This code is only reached after a smi-fast-case check, so
// it is certain that at least one operand isn't a smi.
- if (cc_ == equal) { // Both strict and non-strict.
- Label slow; // Fallthrough label.
- // Equality is almost reflexive (everything but NaN), so start by testing
- // for "identity and not NaN".
- {
- Label not_identical;
- __ cmp(eax, Operand(edx));
- __ j(not_equal, ¬_identical);
- // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
- // so we do the second best thing - test it ourselves.
+ // Identical objects can be compared fast, but there are some tricky cases
+ // for NaN and undefined.
+ {
+ Label not_identical;
+ __ cmp(eax, Operand(edx));
+ __ j(not_equal, ¬_identical);
- if (never_nan_nan_) {
- __ Set(eax, Immediate(0));
- __ ret(0);
- } else {
- Label return_equal;
- Label heap_number;
- // If it's not a heap number, then return equal.
- __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
- Immediate(Factory::heap_number_map()));
- __ j(equal, &heap_number);
- __ bind(&return_equal);
- __ Set(eax, Immediate(0));
- __ ret(0);
+ if (cc_ != equal) {
+ // Check for undefined. undefined OP undefined is false even though
+ // undefined == undefined.
+ Label check_for_nan;
+ __ cmp(edx, Factory::undefined_value());
+ __ j(not_equal, &check_for_nan);
+ __ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc_))));
+ __ ret(0);
+ __ bind(&check_for_nan);
+ }
- __ bind(&heap_number);
- // It is a heap number, so return non-equal if it's NaN and equal if
- // it's not NaN.
- // The representation of NaN values has all exponent bits (52..62) set,
- // and not all mantissa bits (0..51) clear.
- // We only accept QNaNs, which have bit 51 set.
- // Read top bits of double representation (second word of value).
+ // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
+ // so we do the second best thing - test it ourselves.
+ // Note: if cc_ != equal, never_nan_nan_ is not used.
+ if (never_nan_nan_ && (cc_ == equal)) {
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ ret(0);
+ } else {
+ Label return_equal;
+ Label heap_number;
+ // If it's not a heap number, then return equal.
+ __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
+ Immediate(Factory::heap_number_map()));
+ __ j(equal, &heap_number);
+ __ bind(&return_equal);
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ ret(0);
- // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
- // all bits in the mask are set. We only need to check the word
- // that contains the exponent and high bit of the mantissa.
- ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
- __ mov(edx, FieldOperand(edx, HeapNumber::kExponentOffset));
- __ xor_(eax, Operand(eax));
- // Shift value and mask so kQuietNaNHighBitsMask applies to topmost
- // bits.
- __ add(edx, Operand(edx));
- __ cmp(edx, kQuietNaNHighBitsMask << 1);
+ __ bind(&heap_number);
+ // It is a heap number, so return non-equal if it's NaN and equal if
+ // it's not NaN.
+ // The representation of NaN values has all exponent bits (52..62) set,
+ // and not all mantissa bits (0..51) clear.
+ // We only accept QNaNs, which have bit 51 set.
+ // Read top bits of double representation (second word of value).
+
+ // Value is a QNaN if value & kQuietNaNMask == kQuietNaNMask, i.e.,
+ // all bits in the mask are set. We only need to check the word
+ // that contains the exponent and high bit of the mantissa.
+ ASSERT_NE(0, (kQuietNaNHighBitsMask << 1) & 0x80000000u);
+ __ mov(edx, FieldOperand(edx, HeapNumber::kExponentOffset));
+ __ xor_(eax, Operand(eax));
+ // Shift value and mask so kQuietNaNHighBitsMask applies to topmost
+ // bits.
+ __ add(edx, Operand(edx));
+ __ cmp(edx, kQuietNaNHighBitsMask << 1);
+ if (cc_ == equal) {
+ ASSERT_NE(1, EQUAL);
__ setcc(above_equal, eax);
__ ret(0);
+ } else {
+ Label nan;
+ __ j(above_equal, &nan);
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ ret(0);
+ __ bind(&nan);
+ __ Set(eax, Immediate(Smi::FromInt(NegativeComparisonResult(cc_))));
+ __ ret(0);
}
-
- __ bind(¬_identical);
}
+ __ bind(¬_identical);
+ }
+
+ if (cc_ == equal) { // Both strict and non-strict.
+ Label slow; // Fallthrough label.
+
// If we're doing a strict equality comparison, we don't have to do
// type conversion, so we generate code to do fast comparison for objects
// and oddballs. Non-smi numbers and strings still go through the usual
@@ -9560,63 +11491,70 @@
__ push(edx);
__ push(ecx);
- // Inlined floating point compare.
- // Call builtin if operands are not floating point or smi.
- Label check_for_symbols;
- Label unordered;
- if (CpuFeatures::IsSupported(SSE2)) {
- CpuFeatures::Scope use_sse2(SSE2);
- CpuFeatures::Scope use_cmov(CMOV);
+ // Generate the number comparison code.
+ if (include_number_compare_) {
+ Label non_number_comparison;
+ Label unordered;
+ if (CpuFeatures::IsSupported(SSE2)) {
+ CpuFeatures::Scope use_sse2(SSE2);
+ CpuFeatures::Scope use_cmov(CMOV);
- FloatingPointHelper::LoadSSE2Operands(masm, &check_for_symbols);
- __ comisd(xmm0, xmm1);
+ FloatingPointHelper::LoadSSE2Operands(masm, &non_number_comparison);
+ __ comisd(xmm0, xmm1);
- // Jump to builtin for NaN.
- __ j(parity_even, &unordered, not_taken);
- __ mov(eax, 0); // equal
- __ mov(ecx, Immediate(Smi::FromInt(1)));
- __ cmov(above, eax, Operand(ecx));
- __ mov(ecx, Immediate(Smi::FromInt(-1)));
- __ cmov(below, eax, Operand(ecx));
- __ ret(2 * kPointerSize);
- } else {
- FloatingPointHelper::CheckFloatOperands(masm, &check_for_symbols, ebx);
- FloatingPointHelper::LoadFloatOperands(masm, ecx);
- __ FCmp();
+ // Don't base result on EFLAGS when a NaN is involved.
+ __ j(parity_even, &unordered, not_taken);
+ // Return a result of -1, 0, or 1, based on EFLAGS.
+ __ mov(eax, 0); // equal
+ __ mov(ecx, Immediate(Smi::FromInt(1)));
+ __ cmov(above, eax, Operand(ecx));
+ __ mov(ecx, Immediate(Smi::FromInt(-1)));
+ __ cmov(below, eax, Operand(ecx));
+ __ ret(2 * kPointerSize);
+ } else {
+ FloatingPointHelper::CheckFloatOperands(
+ masm, &non_number_comparison, ebx);
+ FloatingPointHelper::LoadFloatOperands(masm, ecx);
+ __ FCmp();
- // Jump to builtin for NaN.
- __ j(parity_even, &unordered, not_taken);
+ // Don't base result on EFLAGS when a NaN is involved.
+ __ j(parity_even, &unordered, not_taken);
- Label below_lbl, above_lbl;
- // Return a result of -1, 0, or 1, to indicate result of comparison.
- __ j(below, &below_lbl, not_taken);
- __ j(above, &above_lbl, not_taken);
+ Label below_label, above_label;
+ // Return a result of -1, 0, or 1, based on EFLAGS. In all cases remove
+ // two arguments from the stack as they have been pushed in preparation
+ // of a possible runtime call.
+ __ j(below, &below_label, not_taken);
+ __ j(above, &above_label, not_taken);
- __ xor_(eax, Operand(eax)); // equal
- // Both arguments were pushed in case a runtime call was needed.
- __ ret(2 * kPointerSize);
+ __ xor_(eax, Operand(eax));
+ __ ret(2 * kPointerSize);
- __ bind(&below_lbl);
- __ mov(eax, Immediate(Smi::FromInt(-1)));
- __ ret(2 * kPointerSize);
+ __ bind(&below_label);
+ __ mov(eax, Immediate(Smi::FromInt(-1)));
+ __ ret(2 * kPointerSize);
- __ bind(&above_lbl);
- __ mov(eax, Immediate(Smi::FromInt(1)));
+ __ bind(&above_label);
+ __ mov(eax, Immediate(Smi::FromInt(1)));
+ __ ret(2 * kPointerSize);
+ }
+
+ // If one of the numbers was NaN, then the result is always false.
+ // The cc is never not-equal.
+ __ bind(&unordered);
+ ASSERT(cc_ != not_equal);
+ if (cc_ == less || cc_ == less_equal) {
+ __ mov(eax, Immediate(Smi::FromInt(1)));
+ } else {
+ __ mov(eax, Immediate(Smi::FromInt(-1)));
+ }
__ ret(2 * kPointerSize); // eax, edx were pushed
+
+ // The number comparison code did not provide a valid result.
+ __ bind(&non_number_comparison);
}
- // If one of the numbers was NaN, then the result is always false.
- // The cc is never not-equal.
- __ bind(&unordered);
- ASSERT(cc_ != not_equal);
- if (cc_ == less || cc_ == less_equal) {
- __ mov(eax, Immediate(Smi::FromInt(1)));
- } else {
- __ mov(eax, Immediate(Smi::FromInt(-1)));
- }
- __ ret(2 * kPointerSize); // eax, edx were pushed
// Fast negative check for symbol-to-symbol equality.
- __ bind(&check_for_symbols);
Label check_for_strings;
if (cc_ == equal) {
BranchIfNonSymbol(masm, &check_for_strings, eax, ecx);
@@ -9657,14 +11595,7 @@
builtin = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
} else {
builtin = Builtins::COMPARE;
- int ncr; // NaN compare result
- if (cc_ == less || cc_ == less_equal) {
- ncr = GREATER;
- } else {
- ASSERT(cc_ == greater || cc_ == greater_equal); // remaining cases
- ncr = LESS;
- }
- __ push(Immediate(Smi::FromInt(ncr)));
+ __ push(Immediate(Smi::FromInt(NegativeComparisonResult(cc_))));
}
// Restore return address on the stack.
@@ -9700,7 +11631,7 @@
__ push(eax);
// Do tail-call to runtime routine.
- __ TailCallRuntime(ExternalReference(Runtime::kStackGuard), 1, 1);
+ __ TailCallRuntime(Runtime::kStackGuard, 1, 1);
}
@@ -9797,7 +11728,7 @@
// If true, a Handle<T> passed by value is passed and returned by
// using the location_ field directly. If false, it is passed and
// returned as a pointer to a handle.
-#ifdef USING_MAC_ABI
+#ifdef USING_BSD_ABI
static const bool kPassHandlesDirectly = true;
#else
static const bool kPassHandlesDirectly = false;
@@ -9861,9 +11792,7 @@
__ LeaveExitFrame(ExitFrame::MODE_NORMAL);
__ ret(0);
__ bind(&promote_scheduled_exception);
- __ TailCallRuntime(ExternalReference(Runtime::kPromoteScheduledException),
- 0,
- 1);
+ __ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
}
@@ -9872,7 +11801,8 @@
Label* throw_termination_exception,
Label* throw_out_of_memory_exception,
bool do_gc,
- bool always_allocate_scope) {
+ bool always_allocate_scope,
+ int /* alignment_skew */) {
// eax: result parameter for PerformGC, if any
// ebx: pointer to C function (C callee-saved)
// ebp: frame pointer (restored after C call)
@@ -9882,7 +11812,17 @@
// Result returned in eax, or eax+edx if result_size_ is 2.
+ // Check stack alignment.
+ if (FLAG_debug_code) {
+ __ CheckStackAlignment();
+ }
+
if (do_gc) {
+ // Pass failure code returned from last attempt as first argument to
+ // PerformGC. No need to use PrepareCallCFunction/CallCFunction here as the
+ // stack alignment is known to be correct. This function takes one argument
+ // which is passed on the stack, and we know that the stack has been
+ // prepared to pass at least one argument.
__ mov(Operand(esp, 0 * kPointerSize), eax); // Result.
__ call(FUNCTION_ADDR(Runtime::PerformGC), RelocInfo::RUNTIME_ENTRY);
}
@@ -10228,55 +12168,208 @@
}
-// Unfortunately you have to run without snapshots to see most of these
-// names in the profile since most compare stubs end up in the snapshot.
-const char* CompareStub::GetName() {
- switch (cc_) {
- case less: return "CompareStub_LT";
- case greater: return "CompareStub_GT";
- case less_equal: return "CompareStub_LE";
- case greater_equal: return "CompareStub_GE";
- case not_equal: {
- if (strict_) {
- if (never_nan_nan_) {
- return "CompareStub_NE_STRICT_NO_NAN";
- } else {
- return "CompareStub_NE_STRICT";
- }
- } else {
- if (never_nan_nan_) {
- return "CompareStub_NE_NO_NAN";
- } else {
- return "CompareStub_NE";
- }
- }
- }
- case equal: {
- if (strict_) {
- if (never_nan_nan_) {
- return "CompareStub_EQ_STRICT_NO_NAN";
- } else {
- return "CompareStub_EQ_STRICT";
- }
- } else {
- if (never_nan_nan_) {
- return "CompareStub_EQ_NO_NAN";
- } else {
- return "CompareStub_EQ";
- }
- }
- }
- default: return "CompareStub";
- }
+int CompareStub::MinorKey() {
+ // Encode the three parameters in a unique 16 bit value. To avoid duplicate
+ // stubs the never NaN NaN condition is only taken into account if the
+ // condition is equals.
+ ASSERT(static_cast<unsigned>(cc_) < (1 << 13));
+ return ConditionField::encode(static_cast<unsigned>(cc_))
+ | StrictField::encode(strict_)
+ | NeverNanNanField::encode(cc_ == equal ? never_nan_nan_ : false)
+ | IncludeNumberCompareField::encode(include_number_compare_);
}
-int CompareStub::MinorKey() {
- // Encode the three parameters in a unique 16 bit value.
- ASSERT(static_cast<unsigned>(cc_) < (1 << 14));
- int nnn_value = (never_nan_nan_ ? 2 : 0);
- if (cc_ != equal) nnn_value = 0; // Avoid duplicate stubs.
- return (static_cast<unsigned>(cc_) << 2) | nnn_value | (strict_ ? 1 : 0);
+// Unfortunately you have to run without snapshots to see most of these
+// names in the profile since most compare stubs end up in the snapshot.
+const char* CompareStub::GetName() {
+ if (name_ != NULL) return name_;
+ const int kMaxNameLength = 100;
+ name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
+ if (name_ == NULL) return "OOM";
+
+ const char* cc_name;
+ switch (cc_) {
+ case less: cc_name = "LT"; break;
+ case greater: cc_name = "GT"; break;
+ case less_equal: cc_name = "LE"; break;
+ case greater_equal: cc_name = "GE"; break;
+ case equal: cc_name = "EQ"; break;
+ case not_equal: cc_name = "NE"; break;
+ default: cc_name = "UnknownCondition"; break;
+ }
+
+ const char* strict_name = "";
+ if (strict_ && (cc_ == equal || cc_ == not_equal)) {
+ strict_name = "_STRICT";
+ }
+
+ const char* never_nan_nan_name = "";
+ if (never_nan_nan_ && (cc_ == equal || cc_ == not_equal)) {
+ never_nan_nan_name = "_NO_NAN";
+ }
+
+ const char* include_number_compare_name = "";
+ if (!include_number_compare_) {
+ include_number_compare_name = "_NO_NUMBER";
+ }
+
+ OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
+ "CompareStub_%s%s%s%s",
+ cc_name,
+ strict_name,
+ never_nan_nan_name,
+ include_number_compare_name);
+ return name_;
+}
+
+
+void StringHelper::GenerateFastCharCodeAt(MacroAssembler* masm,
+ Register object,
+ Register index,
+ Register scratch,
+ Register result,
+ Label* receiver_not_string,
+ Label* index_not_smi,
+ Label* index_out_of_range,
+ Label* slow_case) {
+ Label not_a_flat_string;
+ Label try_again_with_new_string;
+ Label ascii_string;
+ Label got_char_code;
+
+ // If the receiver is a smi trigger the non-string case.
+ ASSERT(kSmiTag == 0);
+ __ test(object, Immediate(kSmiTagMask));
+ __ j(zero, receiver_not_string);
+
+ // Fetch the instance type of the receiver into result register.
+ __ mov(result, FieldOperand(object, HeapObject::kMapOffset));
+ __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
+ // If the receiver is not a string trigger the non-string case.
+ __ test(result, Immediate(kIsNotStringMask));
+ __ j(not_zero, receiver_not_string);
+
+ // If the index is non-smi trigger the non-smi case.
+ ASSERT(kSmiTag == 0);
+ __ test(index, Immediate(kSmiTagMask));
+ __ j(not_zero, index_not_smi);
+
+ // Check for index out of range.
+ __ cmp(index, FieldOperand(object, String::kLengthOffset));
+ __ j(above_equal, index_out_of_range);
+
+ __ bind(&try_again_with_new_string);
+ // ----------- S t a t e -------------
+ // -- object : string to access
+ // -- result : instance type of the string
+ // -- scratch : non-negative index < length
+ // -----------------------------------
+
+ // We need special handling for non-flat strings.
+ ASSERT(kSeqStringTag == 0);
+ __ test(result, Immediate(kStringRepresentationMask));
+ __ j(not_zero, ¬_a_flat_string);
+
+ // Check for 1-byte or 2-byte string.
+ ASSERT(kAsciiStringTag != 0);
+ __ test(result, Immediate(kStringEncodingMask));
+ __ j(not_zero, &ascii_string);
+
+ // 2-byte string.
+ // Load the 2-byte character code into the result register.
+ ASSERT(kSmiTag == 0 && kSmiTagSize == 1); // index is smi (powered by 2).
+ __ movzx_w(result, FieldOperand(object,
+ index, times_1,
+ SeqTwoByteString::kHeaderSize));
+ __ jmp(&got_char_code);
+
+ // Handle non-flat strings.
+ __ bind(¬_a_flat_string);
+ __ and_(result, kStringRepresentationMask);
+ __ cmp(result, kConsStringTag);
+ __ j(not_equal, slow_case);
+
+ // ConsString.
+ // Check whether the right hand side is the empty string (i.e. if
+ // this is really a flat string in a cons string). If that is not
+ // the case we would rather go to the runtime system now to flatten
+ // the string.
+ __ mov(result, FieldOperand(object, ConsString::kSecondOffset));
+ __ cmp(Operand(result), Factory::empty_string());
+ __ j(not_equal, slow_case);
+ // Get the first of the two strings and load its instance type.
+ __ mov(object, FieldOperand(object, ConsString::kFirstOffset));
+ __ mov(result, FieldOperand(object, HeapObject::kMapOffset));
+ __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
+ __ jmp(&try_again_with_new_string);
+
+ // ASCII string.
+ __ bind(&ascii_string);
+ // Put untagged index into scratch register.
+ __ mov(scratch, index);
+ __ SmiUntag(scratch);
+
+ // Load the byte into the result register.
+ __ movzx_b(result, FieldOperand(object,
+ scratch, times_1,
+ SeqAsciiString::kHeaderSize));
+ __ bind(&got_char_code);
+ __ SmiTag(result);
+}
+
+
+void StringHelper::GenerateCharFromCode(MacroAssembler* masm,
+ Register code,
+ Register result,
+ InvokeFlag flag) {
+ ASSERT(!code.is(result));
+
+ Label slow_case;
+ Label exit;
+
+ // Fast case of Heap::LookupSingleCharacterStringFromCode.
+ ASSERT(kSmiTag == 0);
+ ASSERT(kSmiShiftSize == 0);
+ ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
+ __ test(code,
+ Immediate(kSmiTagMask |
+ ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
+ __ j(not_zero, &slow_case, not_taken);
+
+ __ Set(result, Immediate(Factory::single_character_string_cache()));
+ ASSERT(kSmiTag == 0);
+ ASSERT(kSmiTagSize == 1);
+ ASSERT(kSmiShiftSize == 0);
+ // At this point code register contains smi tagged ascii char code.
+ __ mov(result, FieldOperand(result,
+ code, times_half_pointer_size,
+ FixedArray::kHeaderSize));
+ __ cmp(result, Factory::undefined_value());
+ __ j(equal, &slow_case, not_taken);
+ __ jmp(&exit);
+
+ __ bind(&slow_case);
+ if (flag == CALL_FUNCTION) {
+ __ push(code);
+ __ CallRuntime(Runtime::kCharFromCode, 1);
+ if (!result.is(eax)) {
+ __ mov(result, eax);
+ }
+ } else {
+ ASSERT(flag == JUMP_FUNCTION);
+ ASSERT(result.is(eax));
+ __ pop(eax); // Save return address.
+ __ push(code);
+ __ push(eax); // Restore return address.
+ __ TailCallRuntime(Runtime::kCharFromCode, 1, 1);
+ }
+
+ __ bind(&exit);
+ if (flag == JUMP_FUNCTION) {
+ ASSERT(result.is(eax));
+ __ ret(0);
+ }
}
@@ -10307,6 +12400,7 @@
// Check if either of the strings are empty. In that case return the other.
Label second_not_zero_length, both_not_zero_length;
__ mov(ecx, FieldOperand(edx, String::kLengthOffset));
+ ASSERT(kSmiTag == 0);
__ test(ecx, Operand(ecx));
__ j(not_zero, &second_not_zero_length);
// Second string is empty, result is first string which is already in eax.
@@ -10314,6 +12408,7 @@
__ ret(2 * kPointerSize);
__ bind(&second_not_zero_length);
__ mov(ebx, FieldOperand(eax, String::kLengthOffset));
+ ASSERT(kSmiTag == 0);
__ test(ebx, Operand(ebx));
__ j(not_zero, &both_not_zero_length);
// First string is empty, result is second string which is in edx.
@@ -10323,16 +12418,19 @@
// Both strings are non-empty.
// eax: first string
- // ebx: length of first string
- // ecx: length of second string
+ // ebx: length of first string as a smi
+ // ecx: length of second string as a smi
// edx: second string
// Look at the length of the result of adding the two strings.
Label string_add_flat_result, longer_than_two;
__ bind(&both_not_zero_length);
__ add(ebx, Operand(ecx));
+ ASSERT(Smi::kMaxValue == String::kMaxLength);
+ // Handle exceptionally long strings in the runtime system.
+ __ j(overflow, &string_add_runtime);
// Use the runtime system when adding two one character strings, as it
// contains optimizations for this specific case using the symbol table.
- __ cmp(ebx, 2);
+ __ cmp(Operand(ebx), Immediate(Smi::FromInt(2)));
__ j(not_equal, &longer_than_two);
// Check that both strings are non-external ascii strings.
@@ -10346,22 +12444,19 @@
// Try to lookup two character string in symbol table. If it is not found
// just allocate a new one.
Label make_two_character_string, make_flat_ascii_string;
- GenerateTwoCharacterSymbolTableProbe(masm, ebx, ecx, eax, edx, edi,
- &make_two_character_string);
+ StringHelper::GenerateTwoCharacterSymbolTableProbe(
+ masm, ebx, ecx, eax, edx, edi, &make_two_character_string);
+ __ IncrementCounter(&Counters::string_add_native, 1);
__ ret(2 * kPointerSize);
__ bind(&make_two_character_string);
- __ Set(ebx, Immediate(2));
+ __ Set(ebx, Immediate(Smi::FromInt(2)));
__ jmp(&make_flat_ascii_string);
__ bind(&longer_than_two);
// Check if resulting string will be flat.
- __ cmp(ebx, String::kMinNonFlatLength);
+ __ cmp(Operand(ebx), Immediate(Smi::FromInt(String::kMinNonFlatLength)));
__ j(below, &string_add_flat_result);
- // Handle exceptionally long strings in the runtime system.
- ASSERT((String::kMaxLength & 0x80000000) == 0);
- __ cmp(ebx, String::kMaxLength);
- __ j(above, &string_add_runtime);
// 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.
@@ -10378,6 +12473,7 @@
__ AllocateAsciiConsString(ecx, edi, no_reg, &string_add_runtime);
__ bind(&allocated);
// Fill the fields of the cons string.
+ if (FLAG_debug_code) __ AbortIfNotSmi(ebx);
__ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx);
__ mov(FieldOperand(ecx, ConsString::kHashFieldOffset),
Immediate(String::kEmptyHashField));
@@ -10394,7 +12490,7 @@
// Handle creating a flat result. First check that both strings are not
// external strings.
// eax: first string
- // ebx: length of resulting flat string
+ // ebx: length of resulting flat string as a smi
// edx: second string
__ bind(&string_add_flat_result);
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
@@ -10409,7 +12505,7 @@
__ j(equal, &string_add_runtime);
// Now check if both strings are ascii strings.
// eax: first string
- // ebx: length of resulting flat string
+ // ebx: length of resulting flat string as a smi
// edx: second string
Label non_ascii_string_add_flat_result;
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
@@ -10424,7 +12520,8 @@
__ bind(&make_flat_ascii_string);
// Both strings are ascii strings. As they are short they are both flat.
- // ebx: length of resulting flat string
+ // ebx: length of resulting flat string as a smi
+ __ SmiUntag(ebx);
__ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime);
// eax: result string
__ mov(ecx, eax);
@@ -10433,27 +12530,29 @@
// Load first argument and locate first character.
__ mov(edx, Operand(esp, 2 * kPointerSize));
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
+ __ SmiUntag(edi);
__ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
// eax: result string
// ecx: first character of result
// edx: first char of first argument
// edi: length of first argument
- GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
+ StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
// Load second argument and locate first character.
__ mov(edx, Operand(esp, 1 * kPointerSize));
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
+ __ SmiUntag(edi);
__ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
// eax: result string
// ecx: next character of result
// edx: first char of second argument
// edi: length of second argument
- GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
+ StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
__ IncrementCounter(&Counters::string_add_native, 1);
__ ret(2 * kPointerSize);
// Handle creating a flat two byte result.
// eax: first string - known to be two byte
- // ebx: length of resulting flat string
+ // ebx: length of resulting flat string as a smi
// edx: second string
__ bind(&non_ascii_string_add_flat_result);
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
@@ -10462,6 +12561,7 @@
__ j(not_zero, &string_add_runtime);
// Both strings are two byte strings. As they are short they are both
// flat.
+ __ SmiUntag(ebx);
__ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &string_add_runtime);
// eax: result string
__ mov(ecx, eax);
@@ -10471,37 +12571,39 @@
// Load first argument and locate first character.
__ mov(edx, Operand(esp, 2 * kPointerSize));
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
+ __ SmiUntag(edi);
__ add(Operand(edx),
Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
// eax: result string
// ecx: first character of result
// edx: first char of first argument
// edi: length of first argument
- GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
+ StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
// Load second argument and locate first character.
__ mov(edx, Operand(esp, 1 * kPointerSize));
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
+ __ SmiUntag(edi);
__ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
// eax: result string
// ecx: next character of result
// edx: first char of second argument
// edi: length of second argument
- GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
+ StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
__ IncrementCounter(&Counters::string_add_native, 1);
__ ret(2 * kPointerSize);
// Just jump to runtime to add the two strings.
__ bind(&string_add_runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
+ __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
}
-void StringStubBase::GenerateCopyCharacters(MacroAssembler* masm,
- Register dest,
- Register src,
- Register count,
- Register scratch,
- bool ascii) {
+void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch,
+ bool ascii) {
Label loop;
__ bind(&loop);
// This loop just copies one character at a time, as it is only used for very
@@ -10522,12 +12624,12 @@
}
-void StringStubBase::GenerateCopyCharactersREP(MacroAssembler* masm,
- Register dest,
- Register src,
- Register count,
- Register scratch,
- bool ascii) {
+void StringHelper::GenerateCopyCharactersREP(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch,
+ bool ascii) {
// Copy characters using rep movs of doublewords. Align destination on 4 byte
// boundary before starting rep movs. Copy remaining characters after running
// rep movs.
@@ -10556,6 +12658,7 @@
// Copy from edi to esi using rep movs instruction.
__ mov(scratch, count);
__ sar(count, 2); // Number of doublewords to copy.
+ __ cld();
__ rep_movs();
// Find number of bytes left.
@@ -10581,13 +12684,13 @@
}
-void StringStubBase::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
- Register c1,
- Register c2,
- Register scratch1,
- Register scratch2,
- Register scratch3,
- Label* not_found) {
+void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* not_found) {
// Register scratch3 is the general scratch register in this function.
Register scratch = scratch3;
@@ -10627,10 +12730,7 @@
// Calculate capacity mask from the symbol table capacity.
Register mask = scratch2;
- static const int kCapacityOffset =
- FixedArray::kHeaderSize +
- SymbolTable::kCapacityIndex * kPointerSize;
- __ mov(mask, FieldOperand(symbol_table, kCapacityOffset));
+ __ mov(mask, FieldOperand(symbol_table, SymbolTable::kCapacityOffset));
__ SmiUntag(mask);
__ sub(Operand(mask), Immediate(1));
@@ -10655,23 +12755,20 @@
// Load the entry from the symble table.
Register candidate = scratch; // Scratch register contains candidate.
- ASSERT_EQ(1, SymbolTableShape::kEntrySize);
- static const int kFirstElementOffset =
- FixedArray::kHeaderSize +
- SymbolTable::kPrefixStartIndex * kPointerSize +
- SymbolTableShape::kPrefixSize * kPointerSize;
+ ASSERT_EQ(1, SymbolTable::kEntrySize);
__ mov(candidate,
FieldOperand(symbol_table,
scratch,
times_pointer_size,
- kFirstElementOffset));
+ SymbolTable::kElementsStartOffset));
// If entry is undefined no string with this hash can be found.
__ cmp(candidate, Factory::undefined_value());
__ j(equal, not_found);
// If length is not 2 the string is not a candidate.
- __ cmp(FieldOperand(candidate, String::kLengthOffset), Immediate(2));
+ __ cmp(FieldOperand(candidate, String::kLengthOffset),
+ Immediate(Smi::FromInt(2)));
__ j(not_equal, &next_probe[i]);
// As we are out of registers save the mask on the stack and use that
@@ -10708,10 +12805,10 @@
}
-void StringStubBase::GenerateHashInit(MacroAssembler* masm,
- Register hash,
- Register character,
- Register scratch) {
+void StringHelper::GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch) {
// hash = character + (character << 10);
__ mov(hash, character);
__ shl(hash, 10);
@@ -10723,10 +12820,10 @@
}
-void StringStubBase::GenerateHashAddCharacter(MacroAssembler* masm,
- Register hash,
- Register character,
- Register scratch) {
+void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch) {
// hash += character;
__ add(hash, Operand(character));
// hash += hash << 10;
@@ -10740,9 +12837,9 @@
}
-void StringStubBase::GenerateHashGetHash(MacroAssembler* masm,
- Register hash,
- Register scratch) {
+void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
+ Register hash,
+ Register scratch) {
// hash += hash << 3;
__ mov(scratch, hash);
__ shl(scratch, 3);
@@ -10816,9 +12913,9 @@
// Try to lookup two character string in symbol table.
Label make_two_character_string;
- GenerateTwoCharacterSymbolTableProbe(masm, ebx, ecx, eax, edx, edi,
- &make_two_character_string);
- __ ret(2 * kPointerSize);
+ StringHelper::GenerateTwoCharacterSymbolTableProbe(
+ masm, ebx, ecx, eax, edx, edi, &make_two_character_string);
+ __ ret(3 * kPointerSize);
__ bind(&make_two_character_string);
// Setup registers for allocating the two character string.
@@ -10856,7 +12953,7 @@
// edx: original value of esi
// edi: first character of result
// esi: character of sub string start
- GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true);
+ StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true);
__ mov(esi, edx); // Restore esi.
__ IncrementCounter(&Counters::sub_string_native, 1);
__ ret(3 * kPointerSize);
@@ -10895,14 +12992,14 @@
// edx: original value of esi
// edi: first character of result
// esi: character of sub string start
- GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false);
+ StringHelper::GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false);
__ mov(esi, edx); // Restore esi.
__ IncrementCounter(&Counters::sub_string_native, 1);
__ ret(3 * kPointerSize);
// Just jump to runtime to create the sub string.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kSubString), 3, 1);
+ __ TailCallRuntime(Runtime::kSubString, 3, 1);
}
@@ -10915,6 +13012,9 @@
Label result_not_equal;
Label result_greater;
Label compare_lengths;
+
+ __ IncrementCounter(&Counters::string_compare_native, 1);
+
// Find minimum length.
Label left_shorter;
__ mov(scratch1, FieldOperand(left, String::kLengthOffset));
@@ -10937,6 +13037,7 @@
// Change index to run from -min_length to -1 by adding min_length
// to string start. This means that loop ends when index reaches zero,
// which doesn't need an additional compare.
+ __ SmiUntag(min_length);
__ lea(left,
FieldOperand(left,
min_length, times_1,
@@ -11012,13 +13113,12 @@
__ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &runtime);
// Compare flat ascii strings.
- __ IncrementCounter(&Counters::string_compare_native, 1);
GenerateCompareFlatAsciiStrings(masm, edx, eax, ecx, ebx, edi);
// Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
// tagged as a small integer.
__ bind(&runtime);
- __ TailCallRuntime(ExternalReference(Runtime::kStringCompare), 2, 1);
+ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
}
#undef __
diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h
index b84a6bb..0d3fee5 100644
--- a/src/ia32/codegen-ia32.h
+++ b/src/ia32/codegen-ia32.h
@@ -28,6 +28,8 @@
#ifndef V8_IA32_CODEGEN_IA32_H_
#define V8_IA32_CODEGEN_IA32_H_
+#include "ic-inl.h"
+
namespace v8 {
namespace internal {
@@ -337,13 +339,17 @@
bool in_spilled_code() const { return in_spilled_code_; }
void set_in_spilled_code(bool flag) { in_spilled_code_ = flag; }
+ // If the name is an inline runtime function call return the number of
+ // expected arguments. Otherwise return -1.
+ static int InlineRuntimeCallArgumentsCount(Handle<String> name);
+
private:
// Construction/Destruction
explicit CodeGenerator(MacroAssembler* masm);
// Accessors
inline bool is_eval();
- Scope* scope();
+ inline Scope* scope();
// Generating deferred code.
void ProcessDeferred();
@@ -351,6 +357,24 @@
// State
ControlDestination* destination() const { return state_->destination(); }
+ // Control of side-effect-free int32 expression compilation.
+ bool in_safe_int32_mode() { return in_safe_int32_mode_; }
+ void set_in_safe_int32_mode(bool value) { in_safe_int32_mode_ = value; }
+ bool safe_int32_mode_enabled() {
+ return FLAG_safe_int32_compiler && safe_int32_mode_enabled_;
+ }
+ void set_safe_int32_mode_enabled(bool value) {
+ safe_int32_mode_enabled_ = value;
+ }
+ void set_unsafe_bailout(BreakTarget* unsafe_bailout) {
+ unsafe_bailout_ = unsafe_bailout;
+ }
+
+ // Take the Result that is an untagged int32, and convert it to a tagged
+ // Smi or HeapNumber. Remove the untagged_int32 flag from the result.
+ void ConvertInt32ResultToNumber(Result* value);
+ void ConvertInt32ResultToSmi(Result* value);
+
// Track loop nesting level.
int loop_nesting() const { return loop_nesting_; }
void IncrementLoopNesting() { loop_nesting_++; }
@@ -407,7 +431,7 @@
return ContextOperand(esi, Context::GLOBAL_INDEX);
}
- void LoadCondition(Expression* x,
+ void LoadCondition(Expression* expr,
ControlDestination* destination,
bool force_control);
void Load(Expression* expr);
@@ -419,6 +443,11 @@
// temporarily while the code generator is being transformed.
void LoadAndSpill(Expression* expression);
+ // Evaluate an expression and place its value on top of the frame,
+ // using, or not using, the side-effect-free expression compiler.
+ void LoadInSafeInt32Mode(Expression* expr, BreakTarget* unsafe_bailout);
+ void LoadWithSafeInt32ModeDisabled(Expression* expr);
+
// Read a value from a slot and leave it on top of the expression stack.
Result LoadFromSlot(Slot* slot, TypeofState typeof_state);
Result LoadFromSlotCheckForArguments(Slot* slot, TypeofState typeof_state);
@@ -460,10 +489,11 @@
// control destination.
void ToBoolean(ControlDestination* destination);
- void GenericBinaryOperation(
- Token::Value op,
- StaticType* type,
- OverwriteMode overwrite_mode);
+ // Generate code that computes a shortcutting logical operation.
+ void GenerateLogicalBooleanOperation(BinaryOperation* node);
+
+ void GenericBinaryOperation(BinaryOperation* expr,
+ OverwriteMode overwrite_mode);
// If possible, combine two constant smi values using op to produce
// a smi result, and push it on the virtual frame, all at compile time.
@@ -471,31 +501,40 @@
bool FoldConstantSmis(Token::Value op, int left, int right);
// Emit code to perform a binary operation on a constant
- // smi and a likely smi. Consumes the Result *operand.
- Result ConstantSmiBinaryOperation(Token::Value op,
+ // smi and a likely smi. Consumes the Result operand.
+ Result ConstantSmiBinaryOperation(BinaryOperation* expr,
Result* operand,
Handle<Object> constant_operand,
- StaticType* type,
bool reversed,
OverwriteMode overwrite_mode);
// Emit code to perform a binary operation on two likely smis.
// The code to handle smi arguments is produced inline.
- // Consumes the Results *left and *right.
- Result LikelySmiBinaryOperation(Token::Value op,
+ // Consumes the Results left and right.
+ Result LikelySmiBinaryOperation(BinaryOperation* expr,
Result* left,
Result* right,
OverwriteMode overwrite_mode);
+
+ // Emit code to perform a binary operation on two untagged int32 values.
+ // The values are on top of the frame, and the result is pushed on the frame.
+ void Int32BinaryOperation(BinaryOperation* node);
+
+
void Comparison(AstNode* node,
Condition cc,
bool strict,
ControlDestination* destination);
+ void GenerateInlineNumberComparison(Result* left_side,
+ Result* right_side,
+ Condition cc,
+ ControlDestination* dest);
// To prevent long attacker-controlled byte sequences, integer constants
// from the JavaScript source are loaded in two parts if they are larger
- // than 16 bits.
- static const int kMaxSmiInlinedBits = 16;
+ // than 17 bits.
+ static const int kMaxSmiInlinedBits = 17;
bool IsUnsafeSmi(Handle<Object> value);
// Load an integer constant x into a register target or into the stack using
// at most 16 bits of user-controlled data per assembly operation.
@@ -520,6 +559,7 @@
struct InlineRuntimeLUT {
void (CodeGenerator::*method)(ZoneList<Expression*>*);
const char* name;
+ int nargs;
};
static InlineRuntimeLUT* FindInlineRuntimeLUT(Handle<String> name);
@@ -536,8 +576,8 @@
// name/value pairs.
void DeclareGlobals(Handle<FixedArray> pairs);
- // Instantiate the function boilerplate.
- Result InstantiateBoilerplate(Handle<JSFunction> boilerplate);
+ // Instantiate the function based on the shared function info.
+ Result InstantiateFunction(Handle<SharedFunctionInfo> function_info);
// Support for type checks.
void GenerateIsSmi(ZoneList<Expression*>* args);
@@ -553,7 +593,7 @@
// Support for arguments.length and arguments[?].
void GenerateArgumentsLength(ZoneList<Expression*>* args);
- void GenerateArgumentsAccess(ZoneList<Expression*>* args);
+ void GenerateArguments(ZoneList<Expression*>* args);
// Support for accessing the class and value fields of an object.
void GenerateClassOf(ZoneList<Expression*>* args);
@@ -563,6 +603,9 @@
// Fast support for charCodeAt(n).
void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
+ // Fast support for string.charAt(n) and string[n].
+ void GenerateCharFromCode(ZoneList<Expression*>* args);
+
// Fast support for object equality testing.
void GenerateObjectEquals(ZoneList<Expression*>* args);
@@ -571,7 +614,7 @@
void GenerateGetFramePointer(ZoneList<Expression*>* args);
// Fast support for Math.random().
- void GenerateRandomPositiveSmi(ZoneList<Expression*>* args);
+ void GenerateRandomHeapNumber(ZoneList<Expression*>* args);
// Fast support for StringAdd.
void GenerateStringAdd(ZoneList<Expression*>* args);
@@ -585,12 +628,27 @@
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
+ void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
+
+ // Support for fast native caches.
+ void GenerateGetFromCache(ZoneList<Expression*>* args);
+
// Fast support for number to string.
void GenerateNumberToString(ZoneList<Expression*>* args);
- // Fast call to transcendental functions.
+ // Fast swapping of elements. Takes three expressions, the object and two
+ // indices. This should only be used if the indices are known to be
+ // non-negative and within bounds of the elements array at the call site.
+ void GenerateSwapElements(ZoneList<Expression*>* args);
+
+ // Fast call for custom callbacks.
+ void GenerateCallFunction(ZoneList<Expression*>* args);
+
+ // Fast call to math functions.
+ void GenerateMathPow(ZoneList<Expression*>* args);
void GenerateMathSin(ZoneList<Expression*>* args);
void GenerateMathCos(ZoneList<Expression*>* args);
+ void GenerateMathSqrt(ZoneList<Expression*>* args);
// Simple condition analysis.
enum ConditionAnalysis {
@@ -609,6 +667,8 @@
void CodeForDoWhileConditionPosition(DoWhileStatement* stmt);
void CodeForSourcePosition(int pos);
+ void SetTypeForStackSlot(Slot* slot, TypeInfo info);
+
#ifdef DEBUG
// True if the registers are valid for entry to a block. There should
// be no frame-external references to (non-reserved) registers.
@@ -627,10 +687,14 @@
RegisterAllocator* allocator_;
CodeGenState* state_;
int loop_nesting_;
+ bool in_safe_int32_mode_;
+ bool safe_int32_mode_enabled_;
// Jump targets.
// The target of the return from the function.
BreakTarget function_return_;
+ // The target of the bailout from a side-effect-free int32 subexpression.
+ BreakTarget* unsafe_bailout_;
// True if the function return is shadowed (ie, jumping to the target
// function_return_ does not jump to the true function return, but rather
@@ -687,18 +751,35 @@
GenericBinaryOpStub(Token::Value op,
OverwriteMode mode,
GenericBinaryFlags flags,
- NumberInfo::Type operands_type = NumberInfo::kUnknown)
+ TypeInfo operands_type)
: op_(op),
mode_(mode),
flags_(flags),
args_in_registers_(false),
args_reversed_(false),
- name_(NULL),
- operands_type_(operands_type) {
+ static_operands_type_(operands_type),
+ runtime_operands_type_(BinaryOpIC::DEFAULT),
+ name_(NULL) {
+ if (static_operands_type_.IsSmi()) {
+ mode_ = NO_OVERWRITE;
+ }
use_sse3_ = CpuFeatures::IsSupported(SSE3);
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
+ GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo runtime_operands_type)
+ : op_(OpBits::decode(key)),
+ mode_(ModeBits::decode(key)),
+ flags_(FlagBits::decode(key)),
+ args_in_registers_(ArgsInRegistersBits::decode(key)),
+ args_reversed_(ArgsReversedBits::decode(key)),
+ use_sse3_(SSE3Bits::decode(key)),
+ static_operands_type_(TypeInfo::ExpandedRepresentation(
+ StaticTypeInfoBits::decode(key))),
+ runtime_operands_type_(runtime_operands_type),
+ name_(NULL) {
+ }
+
// Generate code to call the stub with the supplied arguments. This will add
// code at the call site to prepare arguments either in registers or on the
// stack together with the actual call.
@@ -718,44 +799,53 @@
bool args_in_registers_; // Arguments passed in registers not on the stack.
bool args_reversed_; // Left and right argument are swapped.
bool use_sse3_;
+
+ // Number type information of operands, determined by code generator.
+ TypeInfo static_operands_type_;
+
+ // Operand type information determined at runtime.
+ BinaryOpIC::TypeInfo runtime_operands_type_;
+
char* name_;
- NumberInfo::Type operands_type_; // Number type information of operands.
const char* GetName();
#ifdef DEBUG
void Print() {
PrintF("GenericBinaryOpStub %d (op %s), "
- "(mode %d, flags %d, registers %d, reversed %d, number_info %s)\n",
+ "(mode %d, flags %d, registers %d, reversed %d, type_info %s)\n",
MinorKey(),
Token::String(op_),
static_cast<int>(mode_),
static_cast<int>(flags_),
static_cast<int>(args_in_registers_),
static_cast<int>(args_reversed_),
- NumberInfo::ToString(operands_type_));
+ static_operands_type_.ToString());
}
#endif
- // Minor key encoding in 16 bits NNNFRASOOOOOOOMM.
+ // Minor key encoding in 18 bits RRNNNFRASOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
class OpBits: public BitField<Token::Value, 2, 7> {};
class SSE3Bits: public BitField<bool, 9, 1> {};
class ArgsInRegistersBits: public BitField<bool, 10, 1> {};
class ArgsReversedBits: public BitField<bool, 11, 1> {};
class FlagBits: public BitField<GenericBinaryFlags, 12, 1> {};
- class NumberInfoBits: public BitField<NumberInfo::Type, 13, 3> {};
+ class StaticTypeInfoBits: public BitField<int, 13, 3> {};
+ class RuntimeTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 16, 2> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
- // Encode the parameters in a unique 16 bit value.
+ // Encode the parameters in a unique 18 bit value.
return OpBits::encode(op_)
| ModeBits::encode(mode_)
| FlagBits::encode(flags_)
| SSE3Bits::encode(use_sse3_)
| ArgsInRegistersBits::encode(args_in_registers_)
| ArgsReversedBits::encode(args_reversed_)
- | NumberInfoBits::encode(operands_type_);
+ | StaticTypeInfoBits::encode(
+ static_operands_type_.ThreeBitRepresentation())
+ | RuntimeTypeInfoBits::encode(runtime_operands_type_);
}
void Generate(MacroAssembler* masm);
@@ -763,6 +853,8 @@
void GenerateLoadArguments(MacroAssembler* masm);
void GenerateReturn(MacroAssembler* masm);
void GenerateHeapResultAllocation(MacroAssembler* masm, Label* alloc_failure);
+ void GenerateRegisterArgsPush(MacroAssembler* masm);
+ void GenerateTypeTransition(MacroAssembler* masm);
bool ArgsInRegistersSupported() {
return op_ == Token::ADD || op_ == Token::SUB
@@ -777,56 +869,106 @@
bool HasSmiCodeInStub() { return (flags_ & NO_SMI_CODE_IN_STUB) == 0; }
bool HasArgsInRegisters() { return args_in_registers_; }
bool HasArgsReversed() { return args_reversed_; }
+
+ bool ShouldGenerateSmiCode() {
+ return HasSmiCodeInStub() &&
+ runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS &&
+ runtime_operands_type_ != BinaryOpIC::STRINGS;
+ }
+
+ bool ShouldGenerateFPCode() {
+ return runtime_operands_type_ != BinaryOpIC::STRINGS;
+ }
+
+ virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
+
+ virtual InlineCacheState GetICState() {
+ return BinaryOpIC::ToState(runtime_operands_type_);
+ }
};
-class StringStubBase: public CodeStub {
+class StringHelper : public AllStatic {
public:
+ // Generates fast code for getting a char code out of a string
+ // object at the given index. May bail out for four reasons (in the
+ // listed order):
+ // * Receiver is not a string (receiver_not_string label).
+ // * Index is not a smi (index_not_smi label).
+ // * Index is out of range (index_out_of_range).
+ // * Some other reason (slow_case label). In this case it's
+ // guaranteed that the above conditions are not violated,
+ // e.g. it's safe to assume the receiver is a string and the
+ // index is a non-negative smi < length.
+ // When successful, object, index, and scratch are clobbered.
+ // Otherwise, scratch and result are clobbered.
+ static void GenerateFastCharCodeAt(MacroAssembler* masm,
+ Register object,
+ Register index,
+ Register scratch,
+ Register result,
+ Label* receiver_not_string,
+ Label* index_not_smi,
+ Label* index_out_of_range,
+ Label* slow_case);
+
+ // Generates code for creating a one-char string from the given char
+ // code. May do a runtime call, so any register can be clobbered
+ // and, if the given invoke flag specifies a call, an internal frame
+ // is required. In tail call mode the result must be eax register.
+ static void GenerateCharFromCode(MacroAssembler* masm,
+ Register code,
+ Register result,
+ InvokeFlag flag);
+
// Generate code for copying characters using a simple loop. This should only
// be used in places where the number of characters is small and the
// additional setup and checking in GenerateCopyCharactersREP adds too much
// overhead. Copying of overlapping regions is not supported.
- void GenerateCopyCharacters(MacroAssembler* masm,
- Register dest,
- Register src,
- Register count,
- Register scratch,
- bool ascii);
+ static void GenerateCopyCharacters(MacroAssembler* masm,
+ Register dest,
+ Register src,
+ Register count,
+ Register scratch,
+ bool ascii);
// Generate code for copying characters using the rep movs instruction.
// Copies ecx characters from esi to edi. Copying of overlapping regions is
// not supported.
- void GenerateCopyCharactersREP(MacroAssembler* masm,
- Register dest, // Must be edi.
- Register src, // Must be esi.
- Register count, // Must be ecx.
- Register scratch, // Neither of the above.
- bool ascii);
+ static void GenerateCopyCharactersREP(MacroAssembler* masm,
+ Register dest, // Must be edi.
+ Register src, // Must be esi.
+ Register count, // Must be ecx.
+ Register scratch, // Neither of above.
+ bool ascii);
// Probe the symbol table for a two character string. If the string is
// not found by probing a jump to the label not_found is performed. This jump
// does not guarantee that the string is not in the symbol table. If the
// string is found the code falls through with the string in register eax.
- void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
- Register c1,
- Register c2,
- Register scratch1,
- Register scratch2,
- Register scratch3,
- Label* not_found);
+ static void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* not_found);
// Generate string hash.
- void GenerateHashInit(MacroAssembler* masm,
- Register hash,
- Register character,
- Register scratch);
- void GenerateHashAddCharacter(MacroAssembler* masm,
- Register hash,
- Register character,
- Register scratch);
- void GenerateHashGetHash(MacroAssembler* masm,
- Register hash,
- Register scratch);
+ static void GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch);
+ static void GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch);
+ static void GenerateHashGetHash(MacroAssembler* masm,
+ Register hash,
+ Register scratch);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
};
@@ -837,7 +979,7 @@
};
-class StringAddStub: public StringStubBase {
+class StringAddStub: public CodeStub {
public:
explicit StringAddStub(StringAddFlags flags) {
string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0);
@@ -854,7 +996,7 @@
};
-class SubStringStub: public StringStubBase {
+class SubStringStub: public CodeStub {
public:
SubStringStub() {}
@@ -866,7 +1008,7 @@
};
-class StringCompareStub: public StringStubBase {
+class StringCompareStub: public CodeStub {
public:
explicit StringCompareStub() {
}
@@ -921,6 +1063,42 @@
};
+class RecordWriteStub : public CodeStub {
+ public:
+ RecordWriteStub(Register object, Register addr, Register scratch)
+ : object_(object), addr_(addr), scratch_(scratch) { }
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ Register object_;
+ Register addr_;
+ Register scratch_;
+
+#ifdef DEBUG
+ void Print() {
+ PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n",
+ object_.code(), addr_.code(), scratch_.code());
+ }
+#endif
+
+ // Minor key encoding in 12 bits. 4 bits for each of the three
+ // registers (object, address and scratch) OOOOAAAASSSS.
+ class ScratchBits: public BitField<uint32_t, 0, 4> {};
+ class AddressBits: public BitField<uint32_t, 4, 4> {};
+ class ObjectBits: public BitField<uint32_t, 8, 4> {};
+
+ Major MajorKey() { return RecordWrite; }
+
+ int MinorKey() {
+ // Encode the registers.
+ return ObjectBits::encode(object_.code()) |
+ AddressBits::encode(addr_.code()) |
+ ScratchBits::encode(scratch_.code());
+ }
+};
+
+
} } // namespace v8::internal
#endif // V8_IA32_CODEGEN_IA32_H_
diff --git a/src/ia32/debug-ia32.cc b/src/ia32/debug-ia32.cc
index a9e2626..d142b11 100644
--- a/src/ia32/debug-ia32.cc
+++ b/src/ia32/debug-ia32.cc
@@ -146,9 +146,10 @@
void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
// Register state for keyed IC load call (from ic-ia32.cc).
// ----------- S t a t e -------------
- // No registers used on entry.
+ // -- edx : receiver
+ // -- eax : key
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, 0, false);
+ Generate_DebugBreakCallHelper(masm, eax.bit() | edx.bit(), false);
}
@@ -156,10 +157,12 @@
// Register state for keyed IC load call (from ic-ia32.cc).
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -----------------------------------
// Register eax contains an object that needs to be pushed on the
// expression stack of the fake JS frame.
- Generate_DebugBreakCallHelper(masm, eax.bit(), false);
+ Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit() | edx.bit(), false);
}
@@ -203,8 +206,58 @@
}
+void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
+ masm->ret(0);
+}
+
+// FrameDropper is a code replacement for a JavaScript frame with possibly
+// several frames above.
+// There is no calling conventions here, because it never actually gets called,
+// it only gets returned to.
+// Frame structure (conforms InternalFrame structure):
+// -- JSFunction
+// -- code
+// -- SMI maker
+// -- context
+// -- frame base
+void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
+ // We do not know our frame height, but set esp based on ebp.
+ __ lea(esp, Operand(ebp, -4 * kPointerSize));
+
+ __ pop(edi); // function
+
+ // Skip code self-reference and marker.
+ __ add(Operand(esp), Immediate(2 * kPointerSize));
+
+ __ pop(esi); // Context.
+ __ pop(ebp);
+
+ // Get function code.
+ __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
+ __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
+
+ // Re-run JSFunction, edi is function, esi is context.
+ __ jmp(Operand(edx));
+}
+
#undef __
+
+void Debug::SetUpFrameDropperFrame(StackFrame* bottom_js_frame,
+ Handle<Code> code) {
+ ASSERT(bottom_js_frame->is_java_script());
+
+ Address fp = bottom_js_frame->fp();
+ Memory::Object_at(fp - 4 * kPointerSize) =
+ Memory::Object_at(fp - 2 * kPointerSize); // Move edi (function).
+
+ Memory::Object_at(fp - 3 * kPointerSize) = *code;
+ Memory::Object_at(fp - 2 * kPointerSize) = Smi::FromInt(StackFrame::INTERNAL);
+}
+const int Debug::kFrameDropperFrameSize = 5;
+
+
#endif // ENABLE_DEBUGGER_SUPPORT
} } // namespace v8::internal
diff --git a/src/ia32/disasm-ia32.cc b/src/ia32/disasm-ia32.cc
index a085900..8d342e0 100644
--- a/src/ia32/disasm-ia32.cc
+++ b/src/ia32/disasm-ia32.cc
@@ -89,6 +89,7 @@
{0x9E, "sahf", UNSET_OP_ORDER},
{0x99, "cdq", UNSET_OP_ORDER},
{0x9B, "fwait", UNSET_OP_ORDER},
+ {0xFC, "cld", UNSET_OP_ORDER},
{-1, "", UNSET_OP_ORDER}
};
@@ -1055,12 +1056,39 @@
AppendToBuffer(",%s", NameOfCPURegister(regop));
} else if (*data == 0x0F) {
data++;
- if (*data == 0x2F) {
+ if (*data == 0x38) {
+ data++;
+ if (*data == 0x17) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, ®op, &rm);
+ AppendToBuffer("ptest %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else {
+ UnimplementedInstruction();
+ }
+ } else if (*data == 0x2E || *data == 0x2F) {
+ const char* mnem = (*data == 0x2E) ? "ucomisd" : "comisd";
data++;
int mod, regop, rm;
get_modrm(*data, &mod, ®op, &rm);
- AppendToBuffer("comisd %s,%s",
- NameOfXMMRegister(regop),
+ if (mod == 0x3) {
+ AppendToBuffer("%s %s,%s", mnem,
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ } else {
+ AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
+ data += PrintRightOperand(data);
+ }
+ } else if (*data == 0x50) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, ®op, &rm);
+ AppendToBuffer("movmskpd %s,%s",
+ NameOfCPURegister(regop),
NameOfXMMRegister(rm));
data++;
} else if (*data == 0x57) {
@@ -1071,6 +1099,12 @@
NameOfXMMRegister(regop),
NameOfXMMRegister(rm));
data++;
+ } else if (*data == 0x6E) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, ®op, &rm);
+ AppendToBuffer("movd %s,", NameOfXMMRegister(regop));
+ data += PrintRightOperand(data);
} else if (*data == 0x6F) {
data++;
int mod, regop, rm;
@@ -1084,6 +1118,14 @@
get_modrm(*data, &mod, ®op, &rm);
data += PrintRightOperand(data);
AppendToBuffer(",%s", NameOfXMMRegister(regop));
+ } else if (*data == 0xEF) {
+ data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, ®op, &rm);
+ AppendToBuffer("pxor %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
} else {
UnimplementedInstruction();
}
@@ -1170,6 +1212,8 @@
const char* mnem = "?";
switch (b2) {
case 0x2A: mnem = "cvtsi2sd"; break;
+ case 0x2C: mnem = "cvttsd2si"; break;
+ case 0x51: mnem = "sqrtsd"; break;
case 0x58: mnem = "addsd"; break;
case 0x59: mnem = "mulsd"; break;
case 0x5C: mnem = "subsd"; break;
@@ -1179,14 +1223,38 @@
int mod, regop, rm;
get_modrm(*data, &mod, ®op, &rm);
if (b2 == 0x2A) {
- AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
- data += PrintRightOperand(data);
+ if (mod != 0x3) {
+ AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
+ data += PrintRightOperand(data);
+ } else {
+ AppendToBuffer("%s %s,%s",
+ mnem,
+ NameOfXMMRegister(regop),
+ NameOfCPURegister(rm));
+ data++;
+ }
+ } else if (b2 == 0x2C) {
+ if (mod != 0x3) {
+ AppendToBuffer("%s %s,", mnem, NameOfCPURegister(regop));
+ data += PrintRightOperand(data);
+ } else {
+ AppendToBuffer("%s %s,%s",
+ mnem,
+ NameOfCPURegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ }
} else {
- AppendToBuffer("%s %s,%s",
- mnem,
- NameOfXMMRegister(regop),
- NameOfXMMRegister(rm));
- data++;
+ if (mod != 0x3) {
+ AppendToBuffer("%s %s,", mnem, NameOfXMMRegister(regop));
+ data += PrintRightOperand(data);
+ } else {
+ AppendToBuffer("%s %s,%s",
+ mnem,
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
+ }
}
}
} else {
@@ -1199,6 +1267,14 @@
if (*(data+2) == 0x2C) {
data += 3;
data += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, data);
+ } else if (*(data+2) == 0x5A) {
+ data += 3;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, ®op, &rm);
+ AppendToBuffer("cvtss2sd %s,%s",
+ NameOfXMMRegister(regop),
+ NameOfXMMRegister(rm));
+ data++;
} else if (*(data+2) == 0x6F) {
data += 3;
int mod, regop, rm;
@@ -1218,6 +1294,9 @@
} else if (*(data+1) == 0xA5) {
data += 2;
AppendToBuffer("rep_movs");
+ } else if (*(data+1) == 0xAB) {
+ data += 2;
+ AppendToBuffer("rep_stos");
} else {
UnimplementedInstruction();
}
diff --git a/src/ia32/fast-codegen-ia32.cc b/src/ia32/fast-codegen-ia32.cc
index f1c2507..61e2b5e 100644
--- a/src/ia32/fast-codegen-ia32.cc
+++ b/src/ia32/fast-codegen-ia32.cc
@@ -195,9 +195,9 @@
}
-void FastCodeGenSyntaxChecker::VisitFunctionBoilerplateLiteral(
- FunctionBoilerplateLiteral* expr) {
- BAILOUT("FunctionBoilerplateLiteral");
+void FastCodeGenSyntaxChecker::VisitSharedFunctionInfoLiteral(
+ SharedFunctionInfoLiteral* expr) {
+ BAILOUT("SharedFunctionInfoLiteral");
}
@@ -436,9 +436,6 @@
AstLabeler labeler;
labeler.Label(info);
- LivenessAnalyzer analyzer;
- analyzer.Analyze(info->function());
-
CodeGenerator::MakeCodePrologue(info);
const int kInitialBufferSize = 4 * KB;
@@ -621,6 +618,7 @@
void FastCodeGenerator::Generate(CompilationInfo* compilation_info) {
ASSERT(info_ == NULL);
info_ = compilation_info;
+ Comment cmnt(masm_, "[ function compiled by fast code generator");
// Save the caller's frame pointer and set up our own.
Comment prologue_cmnt(masm(), ";; Prologue");
@@ -766,8 +764,8 @@
}
-void FastCodeGenerator::VisitFunctionBoilerplateLiteral(
- FunctionBoilerplateLiteral* expr) {
+void FastCodeGenerator::VisitSharedFunctionInfoLiteral(
+ SharedFunctionInfoLiteral* expr) {
UNREACHABLE();
}
@@ -801,8 +799,8 @@
Comment cmnt(masm(), ";; Global");
if (FLAG_print_ir) {
SmartPointer<char> name = expr->name()->ToCString();
- PrintF("%d: t%d = Global(%s) // last_use = %d\n", expr->num(),
- expr->num(), *name, expr->var_def()->last_use()->num());
+ PrintF("%d: t%d = Global(%s)\n", expr->num(),
+ expr->num(), *name);
}
EmitGlobalVariableLoad(cell);
}
@@ -856,9 +854,8 @@
SmartPointer<char> name_string = name->ToCString();
PrintF("%d: ", expr->num());
if (!destination().is(no_reg)) PrintF("t%d = ", expr->num());
- PrintF("Store(this, \"%s\", t%d) // last_use(this) = %d\n", *name_string,
- expr->value()->num(),
- expr->var_def()->last_use()->num());
+ PrintF("Store(this, \"%s\", t%d)\n", *name_string,
+ expr->value()->num());
}
EmitThisPropertyStore(name);
@@ -881,9 +878,8 @@
Comment cmnt(masm(), ";; Load from this");
if (FLAG_print_ir) {
SmartPointer<char> name_string = name->ToCString();
- PrintF("%d: t%d = Load(this, \"%s\") // last_use(this) = %d\n",
- expr->num(), expr->num(), *name_string,
- expr->var_def()->last_use()->num());
+ PrintF("%d: t%d = Load(this, \"%s\")\n",
+ expr->num(), expr->num(), *name_string);
}
EmitThisPropertyLoad(name);
}
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index 2394bed..e9838ad 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -32,6 +32,7 @@
#include "debug.h"
#include "full-codegen.h"
#include "parser.h"
+#include "scopes.h"
namespace v8 {
namespace internal {
@@ -55,6 +56,7 @@
ASSERT(info_ == NULL);
info_ = info;
SetFunctionPosition(function());
+ Comment cmnt(masm_, "[ function compiled by full code generator");
if (mode == PRIMARY) {
__ push(ebp); // Caller's frame pointer.
@@ -740,23 +742,22 @@
// We are declaring a function or constant that rewrites to a
// property. Use (keyed) IC to set the initial value.
VisitForValue(prop->obj(), kStack);
- VisitForValue(prop->key(), kStack);
-
if (decl->fun() != NULL) {
+ VisitForValue(prop->key(), kStack);
VisitForValue(decl->fun(), kAccumulator);
+ __ pop(ecx);
} else {
+ VisitForValue(prop->key(), kAccumulator);
+ __ mov(ecx, result_register());
__ mov(result_register(), Factory::the_hole_value());
}
+ __ pop(edx);
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// Absence of a test eax instruction following the call
// indicates that none of the load was inlined.
__ nop();
-
- // Value in eax is ignored (declarations are statements). Receiver
- // and key on stack are discarded.
- __ Drop(2);
}
}
}
@@ -775,16 +776,15 @@
void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
Comment cmnt(masm_, "[ FunctionLiteral");
- // Build the function boilerplate and instantiate it.
- Handle<JSFunction> boilerplate =
- Compiler::BuildBoilerplate(expr, script(), this);
+ // Build the shared function info and instantiate the function based
+ // on it.
+ Handle<SharedFunctionInfo> function_info =
+ Compiler::BuildFunctionInfo(expr, script(), this);
if (HasStackOverflow()) return;
- ASSERT(boilerplate->IsBoilerplate());
-
// Create a new closure.
__ push(esi);
- __ push(Immediate(boilerplate));
+ __ push(Immediate(function_info));
__ CallRuntime(Runtime::kNewClosure, 2);
Apply(context_, eax);
}
@@ -900,10 +900,11 @@
__ push(FieldOperand(edi, JSFunction::kLiteralsOffset));
__ push(Immediate(Smi::FromInt(expr->literal_index())));
__ push(Immediate(expr->constant_properties()));
+ __ push(Immediate(Smi::FromInt(expr->fast_elements() ? 1 : 0)));
if (expr->depth() > 1) {
- __ CallRuntime(Runtime::kCreateObjectLiteral, 3);
+ __ CallRuntime(Runtime::kCreateObjectLiteral, 4);
} else {
- __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 3);
+ __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
}
// If result_saved is true the result is on top of the stack. If
@@ -1129,7 +1130,8 @@
__ push(result_register());
GenericBinaryOpStub stub(op,
NO_OVERWRITE,
- NO_GENERIC_BINARY_FLAGS);
+ NO_GENERIC_BINARY_FLAGS,
+ TypeInfo::Unknown());
__ CallStub(&stub);
Apply(context, eax);
}
@@ -1250,6 +1252,12 @@
__ pop(result_register());
}
+ __ pop(ecx);
+ if (expr->ends_initialization_block()) {
+ __ mov(edx, Operand(esp, 0)); // Leave receiver on the stack for later.
+ } else {
+ __ pop(edx);
+ }
// Record source code position before IC call.
SetSourcePosition(expr->position());
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
@@ -1260,15 +1268,14 @@
// If the assignment ends an initialization block, revert to fast case.
if (expr->ends_initialization_block()) {
+ __ pop(edx);
__ push(eax); // Result of assignment, saved even if not needed.
- // Receiver is under the key and value.
- __ push(Operand(esp, 2 * kPointerSize));
+ __ push(edx);
__ CallRuntime(Runtime::kToFastProperties, 1);
__ pop(eax);
}
- // Receiver and key are still on stack.
- DropAndApply(2, context_, eax);
+ Apply(context_, eax);
}
@@ -1738,7 +1745,8 @@
// Call stub for +1/-1.
GenericBinaryOpStub stub(expr->binary_op(),
NO_OVERWRITE,
- NO_GENERIC_BINARY_FLAGS);
+ NO_GENERIC_BINARY_FLAGS,
+ TypeInfo::Unknown());
stub.GenerateCall(masm(), eax, Smi::FromInt(1));
__ bind(&done);
@@ -1776,18 +1784,20 @@
break;
}
case KEYED_PROPERTY: {
+ __ pop(ecx);
+ __ pop(edx);
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// This nop signals to the IC that there is no inlined code at the call
// site for it to patch.
__ nop();
if (expr->is_postfix()) {
- __ Drop(2); // Result is on the stack under the key and the receiver.
+ // Result is on the stack
if (context_ != Expression::kEffect) {
ApplyTOS(context_);
}
} else {
- DropAndApply(2, context_, eax);
+ Apply(context_, eax);
}
break;
}
diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc
index 0d79c54..bc7a33c 100644
--- a/src/ia32/ic-ia32.cc
+++ b/src/ia32/ic-ia32.cc
@@ -73,11 +73,10 @@
// Check for the absence of an interceptor.
// Load the map into r0.
__ mov(r0, FieldOperand(receiver, JSObject::kMapOffset));
- // Test the has_named_interceptor bit in the map.
- __ test(FieldOperand(r0, Map::kInstanceAttributesOffset),
- Immediate(1 << (Map::kHasNamedInterceptor + (3 * 8))));
- // Jump to miss if the interceptor bit is set.
+ // Bail out if the receiver has a named interceptor.
+ __ test(FieldOperand(r0, Map::kBitFieldOffset),
+ Immediate(1 << Map::kHasNamedInterceptor));
__ j(not_zero, miss_label, not_taken);
// Bail out if we have a JS global proxy object.
@@ -152,6 +151,103 @@
}
+static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
+ Label* miss,
+ Register elements,
+ Register key,
+ Register r0,
+ Register r1,
+ Register r2) {
+ // Register use:
+ //
+ // elements - holds the slow-case elements of the receiver and is unchanged.
+ //
+ // key - holds the smi key on entry and is unchanged if a branch is
+ // performed to the miss label. If the load succeeds and we
+ // fall through, key holds the result on exit.
+ //
+ // Scratch registers:
+ //
+ // r0 - holds the untagged key on entry and holds the hash once computed.
+ //
+ // r1 - used to hold the capacity mask of the dictionary
+ //
+ // r2 - used for the index into the dictionary.
+ Label done;
+
+ // Compute the hash code from the untagged key. This must be kept in sync
+ // with ComputeIntegerHash in utils.h.
+ //
+ // hash = ~hash + (hash << 15);
+ __ mov(r1, r0);
+ __ not_(r0);
+ __ shl(r1, 15);
+ __ add(r0, Operand(r1));
+ // hash = hash ^ (hash >> 12);
+ __ mov(r1, r0);
+ __ shr(r1, 12);
+ __ xor_(r0, Operand(r1));
+ // hash = hash + (hash << 2);
+ __ lea(r0, Operand(r0, r0, times_4, 0));
+ // hash = hash ^ (hash >> 4);
+ __ mov(r1, r0);
+ __ shr(r1, 4);
+ __ xor_(r0, Operand(r1));
+ // hash = hash * 2057;
+ __ imul(r0, r0, 2057);
+ // hash = hash ^ (hash >> 16);
+ __ mov(r1, r0);
+ __ shr(r1, 16);
+ __ xor_(r0, Operand(r1));
+
+ // Compute capacity mask.
+ __ mov(r1, FieldOperand(elements, NumberDictionary::kCapacityOffset));
+ __ shr(r1, kSmiTagSize); // convert smi to int
+ __ dec(r1);
+
+ // Generate an unrolled loop that performs a few probes before giving up.
+ const int kProbes = 4;
+ for (int i = 0; i < kProbes; i++) {
+ // Use r2 for index calculations and keep the hash intact in r0.
+ __ mov(r2, r0);
+ // Compute the masked index: (hash + i + i * i) & mask.
+ if (i > 0) {
+ __ add(Operand(r2), Immediate(NumberDictionary::GetProbeOffset(i)));
+ }
+ __ and_(r2, Operand(r1));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(NumberDictionary::kEntrySize == 3);
+ __ lea(r2, Operand(r2, r2, times_2, 0)); // r2 = r2 * 3
+
+ // Check if the key matches.
+ __ cmp(key, FieldOperand(elements,
+ r2,
+ times_pointer_size,
+ NumberDictionary::kElementsStartOffset));
+ if (i != (kProbes - 1)) {
+ __ j(equal, &done, taken);
+ } else {
+ __ j(not_equal, miss, not_taken);
+ }
+ }
+
+ __ bind(&done);
+ // Check that the value is a normal propety.
+ const int kDetailsOffset =
+ NumberDictionary::kElementsStartOffset + 2 * kPointerSize;
+ ASSERT_EQ(NORMAL, 0);
+ __ test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset),
+ Immediate(PropertyDetails::TypeField::mask() << kSmiTagSize));
+ __ j(not_zero, miss);
+
+ // Get the value at the masked, scaled index.
+ const int kValueOffset =
+ NumberDictionary::kElementsStartOffset + kPointerSize;
+ __ mov(key, FieldOperand(elements, r2, times_pointer_size, kValueOffset));
+}
+
+
// The offset from the inlined patch site to the start of the
// inlined load instruction. It is 7 bytes (test eax, imm) plus
// 6 bytes (jne slow_label).
@@ -208,6 +304,7 @@
// -----------------------------------
Label slow, check_string, index_int, index_string;
Label check_pixel_array, probe_dictionary;
+ Label check_number_dictionary;
// Check that the object isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
@@ -256,7 +353,7 @@
// ebx: untagged index
// eax: key
// ecx: elements
- __ CheckMap(ecx, Factory::pixel_array_map(), &slow, true);
+ __ CheckMap(ecx, Factory::pixel_array_map(), &check_number_dictionary, true);
__ cmp(ebx, FieldOperand(ecx, PixelArray::kLengthOffset));
__ j(above_equal, &slow);
__ mov(eax, FieldOperand(ecx, PixelArray::kExternalPointerOffset));
@@ -264,6 +361,32 @@
__ SmiTag(eax);
__ ret(0);
+ __ bind(&check_number_dictionary);
+ // Check whether the elements is a number dictionary.
+ // edx: receiver
+ // ebx: untagged index
+ // eax: key
+ // ecx: elements
+ __ CheckMap(ecx, Factory::hash_table_map(), &slow, true);
+ Label slow_pop_receiver;
+ // Push receiver on the stack to free up a register for the dictionary
+ // probing.
+ __ push(edx);
+ GenerateNumberDictionaryLoad(masm,
+ &slow_pop_receiver,
+ ecx,
+ eax,
+ ebx,
+ edx,
+ edi);
+ // Pop receiver before returning.
+ __ pop(edx);
+ __ ret(0);
+
+ __ bind(&slow_pop_receiver);
+ // Pop the receiver from the stack and jump to runtime.
+ __ pop(edx);
+
__ bind(&slow);
// Slow case: jump to runtime.
// edx: receiver
@@ -349,7 +472,7 @@
ecx,
edi,
DICTIONARY_CHECK_DONE);
- __ mov(eax, Operand(ecx));
+ __ mov(eax, ecx);
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1);
__ ret(0);
@@ -368,39 +491,70 @@
void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
// ----------- S t a t e -------------
- // -- eax : key
+ // -- eax : key (index)
// -- edx : receiver
// -- esp[0] : return address
// -----------------------------------
- Label miss, index_ok;
+ Label miss;
+ Label index_not_smi;
+ Label index_out_of_range;
+ Label slow_char_code;
+ Label got_char_code;
- // Pop return address.
- // Performing the load early is better in the common case.
- __ pop(ebx);
+ Register receiver = edx;
+ Register index = eax;
+ Register code = ebx;
+ Register scratch = ecx;
- __ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &miss);
- __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
- __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
- __ test(ecx, Immediate(kIsNotStringMask));
- __ j(not_zero, &miss);
+ StringHelper::GenerateFastCharCodeAt(masm,
+ receiver,
+ index,
+ scratch,
+ code,
+ &miss, // When not a string.
+ &index_not_smi,
+ &index_out_of_range,
+ &slow_char_code);
+ // If we didn't bail out, code register contains smi tagged char
+ // code.
+ __ bind(&got_char_code);
+ StringHelper::GenerateCharFromCode(masm, code, eax, JUMP_FUNCTION);
+#ifdef DEBUG
+ __ Abort("Unexpected fall-through from char from code tail call");
+#endif
- // Check if key is a smi or a heap number.
- __ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &index_ok);
- __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
- __ cmp(ecx, Factory::heap_number_map());
- __ j(not_equal, &miss);
+ // Check if key is a heap number.
+ __ bind(&index_not_smi);
+ __ CheckMap(index, Factory::heap_number_map(), &miss, true);
- __ bind(&index_ok);
- // Push receiver and key on the stack, and make a tail call.
- __ push(edx); // receiver
- __ push(eax); // key
- __ push(ebx); // return address
- __ InvokeBuiltin(Builtins::STRING_CHAR_AT, JUMP_FUNCTION);
+ // Push receiver and key on the stack (now that we know they are a
+ // string and a number), and call runtime.
+ __ bind(&slow_char_code);
+ __ EnterInternalFrame();
+ __ push(receiver);
+ __ push(index);
+ __ CallRuntime(Runtime::kStringCharCodeAt, 2);
+ ASSERT(!code.is(eax));
+ __ mov(code, eax);
+ __ LeaveInternalFrame();
+
+ // Check if the runtime call returned NaN char code. If yes, return
+ // undefined. Otherwise, we can continue.
+ if (FLAG_debug_code) {
+ ASSERT(kSmiTag == 0);
+ __ test(code, Immediate(kSmiTagMask));
+ __ j(zero, &got_char_code);
+ __ mov(scratch, FieldOperand(code, HeapObject::kMapOffset));
+ __ cmp(scratch, Factory::heap_number_map());
+ __ Assert(equal, "StringCharCodeAt must return smi or heap number");
+ }
+ __ cmp(code, Factory::nan_value());
+ __ j(not_equal, &got_char_code);
+ __ bind(&index_out_of_range);
+ __ Set(eax, Immediate(Factory::undefined_value()));
+ __ ret(0);
__ bind(&miss);
- __ push(ebx);
GenerateMiss(masm);
}
@@ -592,8 +746,9 @@
__ push(ecx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(
- IC_Utility(kKeyedLoadPropertyWithInterceptor)), 2, 1);
+ ExternalReference ref = ExternalReference(
+ IC_Utility(kKeyedLoadPropertyWithInterceptor));
+ __ TailCallExternalReference(ref, 2, 1);
__ bind(&slow);
GenerateMiss(masm);
@@ -603,54 +758,41 @@
void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
Label slow, fast, array, extra, check_pixel_array;
- // Get the receiver from the stack.
- __ mov(edx, Operand(esp, 2 * kPointerSize)); // 2 ~ return address, key
// Check that the object isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, &slow, not_taken);
// Get the map from the receiver.
- __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
// Check that the receiver does not require access checks. We need
// to do this because this generic stub does not perform map checks.
- __ movzx_b(ebx, FieldOperand(ecx, Map::kBitFieldOffset));
+ __ movzx_b(ebx, FieldOperand(edi, Map::kBitFieldOffset));
__ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &slow, not_taken);
- // Get the key from the stack.
- __ mov(ebx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address
// Check that the key is a smi.
- __ test(ebx, Immediate(kSmiTagMask));
+ __ test(ecx, Immediate(kSmiTagMask));
__ j(not_zero, &slow, not_taken);
- // Get the instance type from the map of the receiver.
- __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
- // Check if the object is a JS array or not.
- __ cmp(ecx, JS_ARRAY_TYPE);
+ __ CmpInstanceType(edi, JS_ARRAY_TYPE);
__ j(equal, &array);
// Check that the object is some kind of JS object.
- __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
- __ j(less, &slow, not_taken);
+ __ CmpInstanceType(edi, FIRST_JS_OBJECT_TYPE);
+ __ j(below, &slow, not_taken);
// Object case: Check key against length in the elements array.
// eax: value
// edx: JSObject
- // ebx: index (as a smi)
- __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
+ // ecx: key (a smi)
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
// Check that the object is in fast mode (not dictionary).
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(Factory::fixed_array_map()));
- __ j(not_equal, &check_pixel_array, not_taken);
- // Untag the key (for checking against untagged length in the fixed array).
- __ mov(edx, Operand(ebx));
- __ sar(edx, kSmiTagSize); // untag the index and use it for the comparison
- __ cmp(edx, FieldOperand(ecx, Array::kLengthOffset));
- // eax: value
- // ecx: FixedArray
- // ebx: index (as a smi)
+ __ CheckMap(edi, Factory::fixed_array_map(), &check_pixel_array, true);
+ __ mov(ebx, Operand(ecx));
+ __ SmiUntag(ebx);
+ __ cmp(ebx, FieldOperand(edi, Array::kLengthOffset));
__ j(below, &fast, taken);
// Slow case: call runtime.
@@ -658,52 +800,51 @@
GenerateRuntimeSetProperty(masm);
// Check whether the elements is a pixel array.
- // eax: value
- // ecx: elements array
- // ebx: index (as a smi)
__ bind(&check_pixel_array);
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(Factory::pixel_array_map()));
- __ j(not_equal, &slow);
+ // eax: value
+ // ecx: key
+ // edx: receiver
+ // edi: elements array
+ __ CheckMap(edi, Factory::pixel_array_map(), &slow, true);
// Check that the value is a smi. If a conversion is needed call into the
// runtime to convert and clamp.
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &slow);
- __ sar(ebx, kSmiTagSize); // Untag the index.
- __ cmp(ebx, FieldOperand(ecx, PixelArray::kLengthOffset));
+ __ mov(ebx, ecx);
+ __ SmiUntag(ebx);
+ __ cmp(ebx, FieldOperand(edi, PixelArray::kLengthOffset));
__ j(above_equal, &slow);
- __ mov(edx, eax); // Save the value.
- __ sar(eax, kSmiTagSize); // Untag the value.
+ __ mov(ecx, eax); // Save the value. Key is not longer needed.
+ __ SmiUntag(ecx);
{ // Clamp the value to [0..255].
Label done;
- __ test(eax, Immediate(0xFFFFFF00));
+ __ test(ecx, Immediate(0xFFFFFF00));
__ j(zero, &done);
- __ setcc(negative, eax); // 1 if negative, 0 if positive.
- __ dec_b(eax); // 0 if negative, 255 if positive.
+ __ setcc(negative, ecx); // 1 if negative, 0 if positive.
+ __ dec_b(ecx); // 0 if negative, 255 if positive.
__ bind(&done);
}
- __ mov(ecx, FieldOperand(ecx, PixelArray::kExternalPointerOffset));
- __ mov_b(Operand(ecx, ebx, times_1, 0), eax);
- __ mov(eax, edx); // Return the original value.
- __ ret(0);
+ __ mov(edi, FieldOperand(edi, PixelArray::kExternalPointerOffset));
+ __ mov_b(Operand(edi, ebx, times_1, 0), ecx);
+ __ ret(0); // Return value in eax.
// Extra capacity case: Check if there is extra capacity to
// perform the store and update the length. Used for adding one
// element to the array by writing to array[array.length].
__ bind(&extra);
// eax: value
- // edx: JSArray
- // ecx: FixedArray
- // ebx: index (as a smi)
- // flags: compare (ebx, edx.length())
+ // edx: receiver, a JSArray
+ // ecx: key, a smi.
+ // edi: receiver->elements, a FixedArray
+ // flags: compare (ecx, edx.length())
__ j(not_equal, &slow, not_taken); // do not leave holes in the array
- __ sar(ebx, kSmiTagSize); // untag
- __ cmp(ebx, FieldOperand(ecx, Array::kLengthOffset));
+ __ mov(ebx, ecx);
+ __ SmiUntag(ebx); // untag
+ __ cmp(ebx, FieldOperand(edi, Array::kLengthOffset));
__ j(above_equal, &slow, not_taken);
- // Restore tag and increment.
- __ lea(ebx, Operand(ebx, times_2, 1 << kSmiTagSize));
- __ mov(FieldOperand(edx, JSArray::kLengthOffset), ebx);
- __ sub(Operand(ebx), Immediate(1 << kSmiTagSize)); // decrement ebx again
+ // Add 1 to receiver->length, and go to fast array write.
+ __ add(FieldOperand(edx, JSArray::kLengthOffset),
+ Immediate(1 << kSmiTagSize));
__ jmp(&fast);
// Array case: Get the length and the elements array from the JS
@@ -711,28 +852,26 @@
// length is always a smi.
__ bind(&array);
// eax: value
- // edx: JSArray
- // ebx: index (as a smi)
- __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(Factory::fixed_array_map()));
- __ j(not_equal, &check_pixel_array);
+ // edx: receiver, a JSArray
+ // ecx: key, a smi.
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ CheckMap(edi, Factory::fixed_array_map(), &check_pixel_array, true);
// Check the key against the length in the array, compute the
// address to store into and fall through to fast case.
- __ cmp(ebx, FieldOperand(edx, JSArray::kLengthOffset));
+ __ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // Compare smis.
__ j(above_equal, &extra, not_taken);
// Fast case: Do the store.
__ bind(&fast);
// eax: value
- // ecx: FixedArray
- // ebx: index (as a smi)
- __ mov(Operand(ecx, ebx, times_2, FixedArray::kHeaderSize - kHeapObjectTag),
- eax);
+ // ecx: key (a smi)
+ // edx: receiver
+ // edi: FixedArray receiver->elements
+ __ mov(FieldOperand(edi, ecx, times_2, FixedArray::kHeaderSize), eax);
// Update write barrier for the elements array address.
__ mov(edx, Operand(eax));
- __ RecordWrite(ecx, 0, edx, ebx);
+ __ RecordWrite(edi, 0, edx, ecx);
__ ret(0);
}
@@ -741,92 +880,91 @@
ExternalArrayType array_type) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
Label slow, check_heap_number;
- // Get the receiver from the stack.
- __ mov(edx, Operand(esp, 2 * kPointerSize));
// Check that the object isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, &slow);
// Get the map from the receiver.
- __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
// Check that the receiver does not require access checks. We need
// to do this because this generic stub does not perform map checks.
- __ movzx_b(ebx, FieldOperand(ecx, Map::kBitFieldOffset));
+ __ movzx_b(ebx, FieldOperand(edi, Map::kBitFieldOffset));
__ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &slow);
- // Get the key from the stack.
- __ mov(ebx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address
// Check that the key is a smi.
- __ test(ebx, Immediate(kSmiTagMask));
+ __ test(ecx, Immediate(kSmiTagMask));
__ j(not_zero, &slow);
// Get the instance type from the map of the receiver.
- __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
- // Check that the object is a JS object.
- __ cmp(ecx, JS_OBJECT_TYPE);
+ __ CmpInstanceType(edi, JS_OBJECT_TYPE);
__ j(not_equal, &slow);
// Check that the elements array is the appropriate type of
// ExternalArray.
// eax: value
- // edx: JSObject
- // ebx: index (as a smi)
- __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
- Handle<Map> map(Heap::MapForExternalArrayType(array_type));
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(map));
- __ j(not_equal, &slow);
+ // edx: receiver, a JSObject
+ // ecx: key, a smi
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ CheckMap(edi, Handle<Map>(Heap::MapForExternalArrayType(array_type)),
+ &slow, true);
// Check that the index is in range.
- __ sar(ebx, kSmiTagSize); // Untag the index.
- __ cmp(ebx, FieldOperand(ecx, ExternalArray::kLengthOffset));
+ __ mov(ebx, ecx);
+ __ SmiUntag(ebx);
+ __ cmp(ebx, FieldOperand(edi, ExternalArray::kLengthOffset));
// Unsigned comparison catches both negative and too-large values.
__ j(above_equal, &slow);
// Handle both smis and HeapNumbers in the fast path. Go to the
// runtime for all other kinds of values.
// eax: value
- // ecx: elements array
+ // edx: receiver
+ // ecx: key
+ // edi: elements array
// ebx: untagged index
__ test(eax, Immediate(kSmiTagMask));
__ j(not_equal, &check_heap_number);
// smi case
- __ mov(edx, eax); // Save the value.
- __ sar(eax, kSmiTagSize); // Untag the value.
- __ mov(ecx, FieldOperand(ecx, ExternalArray::kExternalPointerOffset));
+ __ mov(ecx, eax); // Preserve the value in eax. Key is no longer needed.
+ __ SmiUntag(ecx);
+ __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset));
// ecx: base pointer of external storage
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
- __ mov_b(Operand(ecx, ebx, times_1, 0), eax);
+ __ mov_b(Operand(edi, ebx, times_1, 0), ecx);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ mov_w(Operand(ecx, ebx, times_2, 0), eax);
+ __ mov_w(Operand(edi, ebx, times_2, 0), ecx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
- __ mov(Operand(ecx, ebx, times_4, 0), eax);
+ __ mov(Operand(edi, ebx, times_4, 0), ecx);
break;
case kExternalFloatArray:
// Need to perform int-to-float conversion.
- __ push(eax);
+ __ push(ecx);
__ fild_s(Operand(esp, 0));
- __ pop(eax);
- __ fstp_s(Operand(ecx, ebx, times_4, 0));
+ __ pop(ecx);
+ __ fstp_s(Operand(edi, ebx, times_4, 0));
break;
default:
UNREACHABLE();
break;
}
- __ mov(eax, edx); // Return the original value.
- __ ret(0);
+ __ ret(0); // Return the original value.
__ bind(&check_heap_number);
+ // eax: value
+ // edx: receiver
+ // ecx: key
+ // edi: elements array
+ // ebx: untagged index
__ cmp(FieldOperand(eax, HeapObject::kMapOffset),
Immediate(Factory::heap_number_map()));
__ j(not_equal, &slow);
@@ -835,14 +973,12 @@
// +/-Infinity into integer arrays basically undefined. For more
// reproducible behavior, convert these to zero.
__ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
- __ mov(edx, eax); // Save the value.
- __ mov(ecx, FieldOperand(ecx, ExternalArray::kExternalPointerOffset));
+ __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset));
// ebx: untagged index
- // ecx: base pointer of external storage
+ // edi: base pointer of external storage
// top of FPU stack: value
if (array_type == kExternalFloatArray) {
- __ fstp_s(Operand(ecx, ebx, times_4, 0));
- __ mov(eax, edx); // Return the original value.
+ __ fstp_s(Operand(edi, ebx, times_4, 0));
__ ret(0);
} else {
// Need to perform float-to-int conversion.
@@ -852,29 +988,27 @@
__ j(parity_even, &is_nan);
if (array_type != kExternalUnsignedIntArray) {
- __ push(eax); // Make room on stack
+ __ push(ecx); // Make room on stack
__ fistp_s(Operand(esp, 0));
- __ pop(eax);
+ __ pop(ecx);
} else {
// fistp stores values as signed integers.
// To represent the entire range, we need to store as a 64-bit
// int and discard the high 32 bits.
- __ push(eax); // Make room on stack
- __ push(eax); // Make room on stack
+ __ sub(Operand(esp), Immediate(2 * kPointerSize));
__ fistp_d(Operand(esp, 0));
- __ pop(eax);
- __ mov(Operand(esp, 0), eax);
- __ pop(eax);
+ __ pop(ecx);
+ __ add(Operand(esp), Immediate(kPointerSize));
}
- // eax: untagged integer value
+ // ecx: untagged integer value
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
- __ mov_b(Operand(ecx, ebx, times_1, 0), eax);
+ __ mov_b(Operand(edi, ebx, times_1, 0), ecx);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ mov_w(Operand(ecx, ebx, times_2, 0), eax);
+ __ mov_w(Operand(edi, ebx, times_2, 0), ecx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray: {
@@ -885,21 +1019,20 @@
// This test would apparently detect both NaN and Infinity,
// but we've already checked for NaN using the FPU hardware
// above.
- __ mov_w(edi, FieldOperand(edx, HeapNumber::kValueOffset + 6));
- __ and_(edi, 0x7FF0);
- __ cmp(edi, 0x7FF0);
+ __ mov_w(edx, FieldOperand(eax, HeapNumber::kValueOffset + 6));
+ __ and_(edx, 0x7FF0);
+ __ cmp(edx, 0x7FF0);
__ j(not_equal, ¬_infinity);
- __ mov(eax, 0);
+ __ mov(ecx, 0);
__ bind(¬_infinity);
- __ mov(Operand(ecx, ebx, times_4, 0), eax);
+ __ mov(Operand(edi, ebx, times_4, 0), ecx);
break;
}
default:
UNREACHABLE();
break;
}
- __ mov(eax, edx); // Return the original value.
- __ ret(0);
+ __ ret(0); // Return original value.
__ bind(&is_nan);
__ ffree();
@@ -907,23 +1040,22 @@
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
- __ mov_b(Operand(ecx, ebx, times_1, 0), 0);
+ __ mov_b(Operand(edi, ebx, times_1, 0), 0);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ mov(eax, 0);
- __ mov_w(Operand(ecx, ebx, times_2, 0), eax);
+ __ xor_(ecx, Operand(ecx));
+ __ mov_w(Operand(edi, ebx, times_2, 0), ecx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
- __ mov(Operand(ecx, ebx, times_4, 0), Immediate(0));
+ __ mov(Operand(edi, ebx, times_4, 0), Immediate(0));
break;
default:
UNREACHABLE();
break;
}
- __ mov(eax, edx); // Return the original value.
- __ ret(0);
+ __ ret(0); // Return the original value.
}
// Slow case: call runtime.
@@ -1238,7 +1370,8 @@
__ push(ebx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(IC_Utility(kLoadIC_Miss)), 2, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kLoadIC_Miss));
+ __ TailCallExternalReference(ref, 2, 1);
}
@@ -1353,7 +1486,8 @@
__ push(ebx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedLoadIC_Miss)), 2, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kKeyedLoadIC_Miss));
+ __ TailCallExternalReference(ref, 2, 1);
}
@@ -1370,7 +1504,7 @@
__ push(ebx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(Runtime::kKeyedGetProperty), 2, 1);
+ __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
}
@@ -1407,7 +1541,60 @@
__ push(ebx);
// Perform tail call to the entry.
- __ TailCallRuntime(ExternalReference(IC_Utility(kStoreIC_Miss)), 3, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_Miss));
+ __ TailCallExternalReference(ref, 3, 1);
+}
+
+
+void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ //
+ // This accepts as a receiver anything JSObject::SetElementsLength accepts
+ // (currently anything except for external and pixel arrays which means
+ // anything with elements of FixedArray type.), but currently is restricted
+ // to JSArray.
+ // Value must be a number, but only smis are accepted as the most common case.
+
+ Label miss;
+
+ Register receiver = edx;
+ Register value = eax;
+ Register scratch = ebx;
+
+ // Check that the receiver isn't a smi.
+ __ test(receiver, Immediate(kSmiTagMask));
+ __ j(zero, &miss, not_taken);
+
+ // Check that the object is a JS array.
+ __ CmpObjectType(receiver, JS_ARRAY_TYPE, scratch);
+ __ j(not_equal, &miss, not_taken);
+
+ // Check that elements are FixedArray.
+ __ mov(scratch, FieldOperand(receiver, JSArray::kElementsOffset));
+ __ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch);
+ __ j(not_equal, &miss, not_taken);
+
+ // Check that value is a smi.
+ __ test(value, Immediate(kSmiTagMask));
+ __ j(not_zero, &miss, not_taken);
+
+ // Prepare tail call to StoreIC_ArrayLength.
+ __ pop(scratch);
+ __ push(receiver);
+ __ push(value);
+ __ push(scratch); // return address
+
+ ExternalReference ref = ExternalReference(IC_Utility(kStoreIC_ArrayLength));
+ __ TailCallExternalReference(ref, 2, 1);
+
+ __ bind(&miss);
+
+ GenerateMiss(masm);
}
@@ -1417,38 +1604,39 @@
void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
- __ pop(ecx);
- __ push(Operand(esp, 1 * kPointerSize));
- __ push(Operand(esp, 1 * kPointerSize));
- __ push(eax);
+ __ pop(ebx);
+ __ push(edx);
__ push(ecx);
+ __ push(eax);
+ __ push(ebx);
// Do tail-call to runtime routine.
- __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1);
+ __ TailCallRuntime(Runtime::kSetProperty, 3, 1);
}
void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
- __ pop(ecx);
- __ push(Operand(esp, 1 * kPointerSize));
- __ push(Operand(esp, 1 * kPointerSize));
- __ push(eax);
+ __ pop(ebx);
+ __ push(edx);
__ push(ecx);
+ __ push(eax);
+ __ push(ebx);
// Do tail-call to runtime routine.
- __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedStoreIC_Miss)), 3, 1);
+ ExternalReference ref = ExternalReference(IC_Utility(kKeyedStoreIC_Miss));
+ __ TailCallExternalReference(ref, 3, 1);
}
#undef __
diff --git a/src/ia32/jump-target-ia32.cc b/src/ia32/jump-target-ia32.cc
index c3f2bc1..cba6508 100644
--- a/src/ia32/jump-target-ia32.cc
+++ b/src/ia32/jump-target-ia32.cc
@@ -30,6 +30,7 @@
#include "codegen-inl.h"
#include "jump-target-inl.h"
#include "register-allocator-inl.h"
+#include "virtual-frame-inl.h"
namespace v8 {
namespace internal {
diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc
index 1f08e87..a7d2834 100644
--- a/src/ia32/macro-assembler-ia32.cc
+++ b/src/ia32/macro-assembler-ia32.cc
@@ -47,33 +47,40 @@
}
-static void RecordWriteHelper(MacroAssembler* masm,
- Register object,
- Register addr,
- Register scratch) {
+void MacroAssembler::RecordWriteHelper(Register object,
+ Register addr,
+ Register scratch) {
+ if (FLAG_debug_code) {
+ // Check that the object is not in new space.
+ Label not_in_new_space;
+ InNewSpace(object, scratch, not_equal, ¬_in_new_space);
+ Abort("new-space object passed to RecordWriteHelper");
+ bind(¬_in_new_space);
+ }
+
Label fast;
// Compute the page start address from the heap object pointer, and reuse
// the 'object' register for it.
- masm->and_(object, ~Page::kPageAlignmentMask);
+ and_(object, ~Page::kPageAlignmentMask);
Register page_start = object;
// Compute the bit addr in the remembered set/index of the pointer in the
// page. Reuse 'addr' as pointer_offset.
- masm->sub(addr, Operand(page_start));
- masm->shr(addr, kObjectAlignmentBits);
+ sub(addr, Operand(page_start));
+ shr(addr, kObjectAlignmentBits);
Register pointer_offset = addr;
// If the bit offset lies beyond the normal remembered set range, it is in
// the extra remembered set area of a large object.
- masm->cmp(pointer_offset, Page::kPageSize / kPointerSize);
- masm->j(less, &fast);
+ cmp(pointer_offset, Page::kPageSize / kPointerSize);
+ j(less, &fast);
// Adjust 'page_start' so that addressing using 'pointer_offset' hits the
// extra remembered set after the large object.
// Find the length of the large object (FixedArray).
- masm->mov(scratch, Operand(page_start, Page::kObjectStartOffset
+ mov(scratch, Operand(page_start, Page::kObjectStartOffset
+ FixedArray::kLengthOffset));
Register array_length = scratch;
@@ -83,59 +90,41 @@
// Add the delta between the end of the normal RSet and the start of the
// extra RSet to 'page_start', so that addressing the bit using
// 'pointer_offset' hits the extra RSet words.
- masm->lea(page_start,
- Operand(page_start, array_length, times_pointer_size,
- Page::kObjectStartOffset + FixedArray::kHeaderSize
- - Page::kRSetEndOffset));
+ lea(page_start,
+ Operand(page_start, array_length, times_pointer_size,
+ Page::kObjectStartOffset + FixedArray::kHeaderSize
+ - Page::kRSetEndOffset));
// NOTE: For now, we use the bit-test-and-set (bts) x86 instruction
// to limit code size. We should probably evaluate this decision by
// measuring the performance of an equivalent implementation using
// "simpler" instructions
- masm->bind(&fast);
- masm->bts(Operand(page_start, Page::kRSetOffset), pointer_offset);
+ bind(&fast);
+ bts(Operand(page_start, Page::kRSetOffset), pointer_offset);
}
-class RecordWriteStub : public CodeStub {
- public:
- RecordWriteStub(Register object, Register addr, Register scratch)
- : object_(object), addr_(addr), scratch_(scratch) { }
-
- void Generate(MacroAssembler* masm);
-
- private:
- Register object_;
- Register addr_;
- Register scratch_;
-
-#ifdef DEBUG
- void Print() {
- PrintF("RecordWriteStub (object reg %d), (addr reg %d), (scratch reg %d)\n",
- object_.code(), addr_.code(), scratch_.code());
+void MacroAssembler::InNewSpace(Register object,
+ Register scratch,
+ Condition cc,
+ Label* branch) {
+ ASSERT(cc == equal || cc == not_equal);
+ if (Serializer::enabled()) {
+ // Can't do arithmetic on external references if it might get serialized.
+ mov(scratch, Operand(object));
+ // The mask isn't really an address. We load it as an external reference in
+ // case the size of the new space is different between the snapshot maker
+ // and the running system.
+ and_(Operand(scratch), Immediate(ExternalReference::new_space_mask()));
+ cmp(Operand(scratch), Immediate(ExternalReference::new_space_start()));
+ j(cc, branch);
+ } else {
+ int32_t new_space_start = reinterpret_cast<int32_t>(
+ ExternalReference::new_space_start().address());
+ lea(scratch, Operand(object, -new_space_start));
+ and_(scratch, Heap::NewSpaceMask());
+ j(cc, branch);
}
-#endif
-
- // Minor key encoding in 12 bits of three registers (object, address and
- // scratch) OOOOAAAASSSS.
- class ScratchBits: public BitField<uint32_t, 0, 4> {};
- class AddressBits: public BitField<uint32_t, 4, 4> {};
- class ObjectBits: public BitField<uint32_t, 8, 4> {};
-
- Major MajorKey() { return RecordWrite; }
-
- int MinorKey() {
- // Encode the registers.
- return ObjectBits::encode(object_.code()) |
- AddressBits::encode(addr_.code()) |
- ScratchBits::encode(scratch_.code());
- }
-};
-
-
-void RecordWriteStub::Generate(MacroAssembler* masm) {
- RecordWriteHelper(masm, object_, addr_, scratch_);
- masm->ret(0);
}
@@ -153,7 +142,7 @@
// First, check if a remembered set write is even needed. The tests below
// catch stores of Smis and stores into young gen (which does not have space
- // for the remembered set bits.
+ // for the remembered set bits).
Label done;
// Skip barrier if writing a smi.
@@ -161,24 +150,19 @@
test(value, Immediate(kSmiTagMask));
j(zero, &done);
- if (Serializer::enabled()) {
- // Can't do arithmetic on external references if it might get serialized.
- mov(value, Operand(object));
- // The mask isn't really an address. We load it as an external reference in
- // case the size of the new space is different between the snapshot maker
- // and the running system.
- and_(Operand(value), Immediate(ExternalReference::new_space_mask()));
- cmp(Operand(value), Immediate(ExternalReference::new_space_start()));
- j(equal, &done);
- } else {
- int32_t new_space_start = reinterpret_cast<int32_t>(
- ExternalReference::new_space_start().address());
- lea(value, Operand(object, -new_space_start));
- and_(value, Heap::NewSpaceMask());
- j(equal, &done);
- }
+ InNewSpace(object, value, equal, &done);
- if ((offset > 0) && (offset < Page::kMaxHeapObjectSize)) {
+ // The offset is relative to a tagged or untagged HeapObject pointer,
+ // so either offset or offset + kHeapObjectTag must be a
+ // multiple of kPointerSize.
+ ASSERT(IsAligned(offset, kPointerSize) ||
+ IsAligned(offset + kHeapObjectTag, kPointerSize));
+
+ // We use optimized write barrier code if the word being written to is not in
+ // a large object chunk or is in the first page of a large object chunk.
+ // We make sure that an offset is inside the right limits whether it is
+ // tagged or untagged.
+ if ((offset > 0) && (offset < Page::kMaxHeapObjectSize - kHeapObjectTag)) {
// Compute the bit offset in the remembered set, leave it in 'value'.
lea(value, Operand(object, offset));
and_(value, Page::kPageAlignmentMask);
@@ -209,7 +193,7 @@
// If we are already generating a shared stub, not inlining the
// record write code isn't going to save us any memory.
if (generating_stub()) {
- RecordWriteHelper(this, object, dst, value);
+ RecordWriteHelper(object, dst, value);
} else {
RecordWriteStub stub(object, dst, value);
CallStub(&stub);
@@ -221,9 +205,9 @@
// Clobber all input registers when running with the debug-code flag
// turned on to provoke errors.
if (FLAG_debug_code) {
- mov(object, Immediate(bit_cast<int32_t>(kZapValue)));
- mov(value, Immediate(bit_cast<int32_t>(kZapValue)));
- mov(scratch, Immediate(bit_cast<int32_t>(kZapValue)));
+ mov(object, Immediate(BitCast<int32_t>(kZapValue)));
+ mov(value, Immediate(BitCast<int32_t>(kZapValue)));
+ mov(scratch, Immediate(BitCast<int32_t>(kZapValue)));
}
}
@@ -386,17 +370,23 @@
}
-void MacroAssembler::AbortIfNotNumber(Register object, const char* msg) {
+void MacroAssembler::AbortIfNotNumber(Register object) {
Label ok;
test(object, Immediate(kSmiTagMask));
j(zero, &ok);
cmp(FieldOperand(object, HeapObject::kMapOffset),
Factory::heap_number_map());
- Assert(equal, msg);
+ Assert(equal, "Operand not a number");
bind(&ok);
}
+void MacroAssembler::AbortIfNotSmi(Register object) {
+ test(object, Immediate(kSmiTagMask));
+ Assert(equal, "Operand not a smi");
+}
+
+
void MacroAssembler::EnterFrame(StackFrame::Type type) {
push(ebp);
mov(ebp, Operand(esp));
@@ -920,7 +910,9 @@
// Set the map, length and hash field.
mov(FieldOperand(result, HeapObject::kMapOffset),
Immediate(Factory::string_map()));
- mov(FieldOperand(result, String::kLengthOffset), length);
+ mov(scratch1, length);
+ SmiTag(scratch1);
+ mov(FieldOperand(result, String::kLengthOffset), scratch1);
mov(FieldOperand(result, String::kHashFieldOffset),
Immediate(String::kEmptyHashField));
}
@@ -953,7 +945,9 @@
// Set the map, length and hash field.
mov(FieldOperand(result, HeapObject::kMapOffset),
Immediate(Factory::ascii_string_map()));
- mov(FieldOperand(result, String::kLengthOffset), length);
+ mov(scratch1, length);
+ SmiTag(scratch1);
+ mov(FieldOperand(result, String::kLengthOffset), scratch1);
mov(FieldOperand(result, String::kHashFieldOffset),
Immediate(String::kEmptyHashField));
}
@@ -1189,15 +1183,22 @@
}
-void MacroAssembler::TailCallRuntime(const ExternalReference& ext,
- int num_arguments,
- int result_size) {
+void MacroAssembler::TailCallExternalReference(const ExternalReference& ext,
+ int num_arguments,
+ int result_size) {
// TODO(1236192): Most runtime routines don't need the number of
// arguments passed in because it is constant. At some point we
// should remove this need and make the runtime routine entry code
// smarter.
Set(eax, Immediate(num_arguments));
- JumpToRuntime(ext);
+ JumpToExternalReference(ext);
+}
+
+
+void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
+ int num_arguments,
+ int result_size) {
+ TailCallExternalReference(ExternalReference(fid), num_arguments, result_size);
}
@@ -1267,7 +1268,7 @@
}
-void MacroAssembler::JumpToRuntime(const ExternalReference& ext) {
+void MacroAssembler::JumpToExternalReference(const ExternalReference& ext) {
// Set the entry point and jump to the C entry runtime stub.
mov(ebx, Immediate(ext));
CEntryStub ces(1);
@@ -1418,16 +1419,28 @@
void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
+ ASSERT(!target.is(edi));
+
+ // Load the builtins object into target register.
+ mov(target, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ mov(target, FieldOperand(target, GlobalObject::kBuiltinsOffset));
+
// Load the JavaScript builtin function from the builtins object.
- mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
- mov(edi, FieldOperand(edi, GlobalObject::kBuiltinsOffset));
- int builtins_offset =
- JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize);
- mov(edi, FieldOperand(edi, builtins_offset));
- // Load the code entry point from the function into the target register.
- mov(target, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
- mov(target, FieldOperand(target, SharedFunctionInfo::kCodeOffset));
- add(Operand(target), Immediate(Code::kHeaderSize - kHeapObjectTag));
+ mov(edi, FieldOperand(target, JSBuiltinsObject::OffsetOfFunctionWithId(id)));
+
+ // Load the code entry point from the builtins object.
+ mov(target, FieldOperand(target, JSBuiltinsObject::OffsetOfCodeWithId(id)));
+ if (FLAG_debug_code) {
+ // Make sure the code objects in the builtins object and in the
+ // builtin function are the same.
+ push(target);
+ mov(target, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ mov(target, FieldOperand(target, SharedFunctionInfo::kCodeOffset));
+ cmp(target, Operand(esp, 0));
+ Assert(equal, "Builtin code object changed");
+ pop(target);
+ }
+ lea(target, FieldOperand(target, Code::kHeaderSize));
}
@@ -1545,6 +1558,21 @@
}
+void MacroAssembler::CheckStackAlignment() {
+ int frame_alignment = OS::ActivationFrameAlignment();
+ int frame_alignment_mask = frame_alignment - 1;
+ if (frame_alignment > kPointerSize) {
+ ASSERT(IsPowerOf2(frame_alignment));
+ Label alignment_as_expected;
+ test(esp, Immediate(frame_alignment_mask));
+ j(zero, &alignment_as_expected);
+ // Abort if stack is not aligned.
+ int3();
+ bind(&alignment_as_expected);
+ }
+}
+
+
void MacroAssembler::Abort(const char* msg) {
// We want to pass the msg string like a smi to avoid GC
// problems, however msg is not guaranteed to be aligned
@@ -1575,7 +1603,7 @@
void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(
Register instance_type,
Register scratch,
- Label *failure) {
+ Label* failure) {
if (!scratch.is(instance_type)) {
mov(scratch, instance_type);
}
@@ -1618,6 +1646,46 @@
}
+void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
+ int frameAlignment = OS::ActivationFrameAlignment();
+ if (frameAlignment != 0) {
+ // Make stack end at alignment and make room for num_arguments words
+ // and the original value of esp.
+ mov(scratch, esp);
+ sub(Operand(esp), Immediate((num_arguments + 1) * kPointerSize));
+ ASSERT(IsPowerOf2(frameAlignment));
+ and_(esp, -frameAlignment);
+ mov(Operand(esp, num_arguments * kPointerSize), scratch);
+ } else {
+ sub(Operand(esp), Immediate(num_arguments * kPointerSize));
+ }
+}
+
+
+void MacroAssembler::CallCFunction(ExternalReference function,
+ int num_arguments) {
+ // Trashing eax is ok as it will be the return value.
+ mov(Operand(eax), Immediate(function));
+ CallCFunction(eax, num_arguments);
+}
+
+
+void MacroAssembler::CallCFunction(Register function,
+ int num_arguments) {
+ // Check stack alignment.
+ if (FLAG_debug_code) {
+ CheckStackAlignment();
+ }
+
+ call(Operand(function));
+ if (OS::ActivationFrameAlignment() != 0) {
+ mov(esp, Operand(esp, num_arguments * kPointerSize));
+ } else {
+ add(Operand(esp), Immediate(num_arguments * sizeof(int32_t)));
+ }
+}
+
+
CodePatcher::CodePatcher(byte* address, int size)
: address_(address), size_(size), masm_(address, size + Assembler::kGap) {
// Create a new macro assembler pointing to the address of the code to patch.
diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h
index 69dc54c..c3a019b 100644
--- a/src/ia32/macro-assembler-ia32.h
+++ b/src/ia32/macro-assembler-ia32.h
@@ -48,6 +48,20 @@
// ---------------------------------------------------------------------------
// GC Support
+ // Set the remebered set bit for an address which points into an
+ // object. RecordWriteHelper only works if the object is not in new
+ // space.
+ void RecordWriteHelper(Register object,
+ Register addr,
+ Register scratch);
+
+ // Check if object is in new space.
+ // scratch can be object itself, but it will be clobbered.
+ void InNewSpace(Register object,
+ Register scratch,
+ Condition cc, // equal for new space, not_equal otherwise.
+ Label* branch);
+
// Set the remembered set bit for [object+offset].
// object is the object being stored into, value is the object being stored.
// If offset is zero, then the scratch register contains the array index into
@@ -170,14 +184,18 @@
// Smi tagging support.
void SmiTag(Register reg) {
ASSERT(kSmiTag == 0);
- shl(reg, kSmiTagSize);
+ ASSERT(kSmiTagSize == 1);
+ add(reg, Operand(reg));
}
void SmiUntag(Register reg) {
sar(reg, kSmiTagSize);
}
// Abort execution if argument is not a number. Used in debug code.
- void AbortIfNotNumber(Register object, const char* msg);
+ void AbortIfNotNumber(Register object);
+
+ // Abort execution if argument is not a smi. Used in debug code.
+ void AbortIfNotSmi(Register object);
// ---------------------------------------------------------------------------
// Exception handling
@@ -349,7 +367,6 @@
void StubReturn(int argc);
// Call a runtime routine.
- // Eventually this should be used for all C calls.
void CallRuntime(Runtime::Function* f, int num_arguments);
// Call a runtime function, returning the CodeStub object called.
@@ -367,12 +384,34 @@
Object* TryCallRuntime(Runtime::FunctionId id, int num_arguments);
// Tail call of a runtime routine (jump).
- // Like JumpToRuntime, but also takes care of passing the number
- // of arguments.
- void TailCallRuntime(const ExternalReference& ext,
+ // Like JumpToExternalReference, but also takes care of passing the number
+ // of parameters.
+ void TailCallExternalReference(const ExternalReference& ext,
+ int num_arguments,
+ int result_size);
+
+ // Convenience function: tail call a runtime routine (jump).
+ void TailCallRuntime(Runtime::FunctionId fid,
int num_arguments,
int result_size);
+ // Before calling a C-function from generated code, align arguments on stack.
+ // After aligning the frame, arguments must be stored in esp[0], esp[4],
+ // etc., not pushed. The argument count assumes all arguments are word sized.
+ // Some compilers/platforms require the stack to be aligned when calling
+ // C++ code.
+ // Needs a scratch register to do some arithmetic. This register will be
+ // trashed.
+ void PrepareCallCFunction(int num_arguments, Register scratch);
+
+ // Calls a C function and cleans up the space for arguments allocated
+ // by PrepareCallCFunction. The called function is not allowed to trigger a
+ // garbage collection, since that might move the code and invalidate the
+ // return address (unless this is somehow accounted for by the called
+ // function).
+ void CallCFunction(ExternalReference function, int num_arguments);
+ void CallCFunction(Register function, int num_arguments);
+
void PushHandleScope(Register scratch);
// Pops a handle scope using the specified scratch register and
@@ -384,7 +423,7 @@
Object* TryPopHandleScope(Register saved, Register scratch);
// Jump to a runtime routine.
- void JumpToRuntime(const ExternalReference& ext);
+ void JumpToExternalReference(const ExternalReference& ext);
// ---------------------------------------------------------------------------
@@ -426,6 +465,9 @@
// Print a message to stdout and abort execution.
void Abort(const char* msg);
+ // Check that the stack is aligned.
+ void CheckStackAlignment();
+
// Verify restrictions about code generated in stubs.
void set_generating_stub(bool value) { generating_stub_ = value; }
bool generating_stub() { return generating_stub_; }
@@ -440,7 +482,7 @@
// for both instance type and scratch.
void JumpIfInstanceTypeIsNotSequentialAscii(Register instance_type,
Register scratch,
- Label *on_not_flat_ascii_string);
+ Label* on_not_flat_ascii_string);
// Checks if both objects are sequential ASCII strings, and jumps to label
// if either is not.
@@ -448,7 +490,7 @@
Register object2,
Register scratch1,
Register scratch2,
- Label *on_not_flat_ascii_strings);
+ Label* on_not_flat_ascii_strings);
private:
bool generating_stub_;
diff --git a/src/ia32/regexp-macro-assembler-ia32.cc b/src/ia32/regexp-macro-assembler-ia32.cc
index f6da693..fdf3b9f 100644
--- a/src/ia32/regexp-macro-assembler-ia32.cc
+++ b/src/ia32/regexp-macro-assembler-ia32.cc
@@ -38,7 +38,7 @@
namespace v8 {
namespace internal {
-#ifdef V8_NATIVE_REGEXP
+#ifndef V8_INTERPRETED_REGEXP
/*
* This assembler uses the following register assignment convention
* - edx : current character. Must be loaded using LoadCurrentCharacter
@@ -324,8 +324,8 @@
__ push(backtrack_stackpointer());
__ push(ebx);
- const int argument_count = 3;
- FrameAlign(argument_count, ecx);
+ static const int argument_count = 3;
+ __ PrepareCallCFunction(argument_count, ecx);
// Put arguments into allocated stack area, last argument highest on stack.
// Parameters are
// Address byte_offset1 - Address captured substring's start.
@@ -346,7 +346,7 @@
ExternalReference compare =
ExternalReference::re_case_insensitive_compare_uc16();
- CallCFunction(compare, argument_count);
+ __ CallCFunction(compare, argument_count);
// Pop original values before reacting on result value.
__ pop(ebx);
__ pop(backtrack_stackpointer());
@@ -653,6 +653,8 @@
__ j(not_zero, &exit_label_);
__ bind(&stack_ok);
+ // Load start index for later use.
+ __ mov(ebx, Operand(ebp, kStartIndex));
// Allocate space on stack for registers.
__ sub(Operand(esp), Immediate(num_registers_ * kPointerSize));
@@ -662,17 +664,23 @@
__ mov(edi, Operand(ebp, kInputStart));
// Set up edi to be negative offset from string end.
__ sub(edi, Operand(esi));
- // Set eax to address of char before start of input
+
+ // Set eax to address of char before start of the string.
// (effectively string position -1).
- __ lea(eax, Operand(edi, -char_size()));
+ __ neg(ebx);
+ if (mode_ == UC16) {
+ __ lea(eax, Operand(edi, ebx, times_2, -char_size()));
+ } else {
+ __ lea(eax, Operand(edi, ebx, times_1, -char_size()));
+ }
// Store this value in a local variable, for use when clearing
// position registers.
__ mov(Operand(ebp, kInputStartMinusOne), eax);
// Determine whether the start index is zero, that is at the start of the
// string, and store that value in a local variable.
- __ mov(ebx, Operand(ebp, kStartIndex));
__ xor_(Operand(ecx), ecx); // setcc only operates on cl (lower byte of ecx).
+ // Register ebx still holds -stringIndex.
__ test(ebx, Operand(ebx));
__ setcc(zero, ecx); // 1 if 0 (start of string), 0 if positive.
__ mov(Operand(ebp, kAtStart), ecx);
@@ -721,10 +729,17 @@
// copy captures to output
__ mov(ebx, Operand(ebp, kRegisterOutput));
__ mov(ecx, Operand(ebp, kInputEnd));
+ __ mov(edx, Operand(ebp, kStartIndex));
__ sub(ecx, Operand(ebp, kInputStart));
+ if (mode_ == UC16) {
+ __ lea(ecx, Operand(ecx, edx, times_2, 0));
+ } else {
+ __ add(ecx, Operand(edx));
+ }
for (int i = 0; i < num_saved_registers_; i++) {
__ mov(eax, register_location(i));
- __ add(eax, Operand(ecx)); // Convert to index from start, not end.
+ // Convert to index from start of string, not end.
+ __ add(eax, Operand(ecx));
if (mode_ == UC16) {
__ sar(eax, 1); // Convert byte index to character index.
}
@@ -784,13 +799,13 @@
__ push(edi);
// Call GrowStack(backtrack_stackpointer())
- int num_arguments = 2;
- FrameAlign(num_arguments, ebx);
+ static const int num_arguments = 2;
+ __ PrepareCallCFunction(num_arguments, ebx);
__ lea(eax, Operand(ebp, kStackHighEnd));
__ mov(Operand(esp, 1 * kPointerSize), eax);
__ mov(Operand(esp, 0 * kPointerSize), backtrack_stackpointer());
ExternalReference grow_stack = ExternalReference::re_grow_stack();
- CallCFunction(grow_stack, num_arguments);
+ __ CallCFunction(grow_stack, num_arguments);
// If return NULL, we have failed to grow the stack, and
// must exit with a stack-overflow exception.
__ or_(eax, Operand(eax));
@@ -817,7 +832,7 @@
NULL,
Code::ComputeFlags(Code::REGEXP),
masm_->CodeObject());
- LOG(RegExpCodeCreateEvent(*code, *source));
+ PROFILE(RegExpCodeCreateEvent(*code, *source));
return Handle<Object>::cast(code);
}
@@ -951,8 +966,8 @@
// Private methods:
void RegExpMacroAssemblerIA32::CallCheckStackGuardState(Register scratch) {
- int num_arguments = 3;
- FrameAlign(num_arguments, scratch);
+ static const int num_arguments = 3;
+ __ PrepareCallCFunction(num_arguments, scratch);
// RegExp code frame pointer.
__ mov(Operand(esp, 2 * kPointerSize), ebp);
// Code* of self.
@@ -962,7 +977,7 @@
__ mov(Operand(esp, 0 * kPointerSize), eax);
ExternalReference check_stack_guard =
ExternalReference::re_check_stack_guard_state();
- CallCFunction(check_stack_guard, num_arguments);
+ __ CallCFunction(check_stack_guard, num_arguments);
}
@@ -1087,19 +1102,22 @@
void RegExpMacroAssemblerIA32::SafeCall(Label* to) {
- __ call(to);
+ Label return_to;
+ __ push(Immediate::CodeRelativeOffset(&return_to));
+ __ jmp(to);
+ __ bind(&return_to);
}
void RegExpMacroAssemblerIA32::SafeReturn() {
- __ add(Operand(esp, 0), Immediate(masm_->CodeObject()));
- __ ret(0);
+ __ pop(ebx);
+ __ add(Operand(ebx), Immediate(masm_->CodeObject()));
+ __ jmp(Operand(ebx));
}
void RegExpMacroAssemblerIA32::SafeCallTarget(Label* name) {
__ bind(name);
- __ sub(Operand(esp, 0), Immediate(masm_->CodeObject()));
}
@@ -1153,37 +1171,6 @@
}
-void RegExpMacroAssemblerIA32::FrameAlign(int num_arguments, Register scratch) {
- // TODO(lrn): Since we no longer use the system stack arbitrarily (but we do
- // use it, e.g., for SafeCall), we know the number of elements on the stack
- // since the last frame alignment. We might be able to do this simpler then.
- int frameAlignment = OS::ActivationFrameAlignment();
- if (frameAlignment != 0) {
- // Make stack end at alignment and make room for num_arguments words
- // and the original value of esp.
- __ mov(scratch, esp);
- __ sub(Operand(esp), Immediate((num_arguments + 1) * kPointerSize));
- ASSERT(IsPowerOf2(frameAlignment));
- __ and_(esp, -frameAlignment);
- __ mov(Operand(esp, num_arguments * kPointerSize), scratch);
- } else {
- __ sub(Operand(esp), Immediate(num_arguments * kPointerSize));
- }
-}
-
-
-void RegExpMacroAssemblerIA32::CallCFunction(ExternalReference function,
- int num_arguments) {
- __ mov(Operand(eax), Immediate(function));
- __ call(Operand(eax));
- if (OS::ActivationFrameAlignment() != 0) {
- __ mov(esp, Operand(esp, num_arguments * kPointerSize));
- } else {
- __ add(Operand(esp), Immediate(num_arguments * sizeof(int32_t)));
- }
-}
-
-
void RegExpMacroAssemblerIA32::LoadCurrentCharacterUnchecked(int cp_offset,
int characters) {
if (mode_ == ASCII) {
@@ -1211,6 +1198,6 @@
#undef __
-#endif // V8_NATIVE_REGEXP
+#endif // V8_INTERPRETED_REGEXP
}} // namespace v8::internal
diff --git a/src/ia32/regexp-macro-assembler-ia32.h b/src/ia32/regexp-macro-assembler-ia32.h
index d9866b7..823bc03 100644
--- a/src/ia32/regexp-macro-assembler-ia32.h
+++ b/src/ia32/regexp-macro-assembler-ia32.h
@@ -31,14 +31,14 @@
namespace v8 {
namespace internal {
-#ifndef V8_NATIVE_REGEXP
+#ifdef V8_INTERPRETED_REGEXP
class RegExpMacroAssemblerIA32: public RegExpMacroAssembler {
public:
RegExpMacroAssemblerIA32() { }
virtual ~RegExpMacroAssemblerIA32() { }
};
-#else
+#else // V8_INTERPRETED_REGEXP
class RegExpMacroAssemblerIA32: public NativeRegExpMacroAssembler {
public:
RegExpMacroAssemblerIA32(Mode mode, int registers_to_save);
@@ -187,21 +187,6 @@
// (ecx) and increments it by a word size.
inline void Pop(Register target);
- // Before calling a C-function from generated code, align arguments on stack.
- // After aligning the frame, arguments must be stored in esp[0], esp[4],
- // etc., not pushed. The argument count assumes all arguments are word sized.
- // Some compilers/platforms require the stack to be aligned when calling
- // C++ code.
- // Needs a scratch register to do some arithmetic. This register will be
- // trashed.
- inline void FrameAlign(int num_arguments, Register scratch);
-
- // Calls a C function and cleans up the space for arguments allocated
- // by FrameAlign. The called function is not allowed to trigger a garbage
- // collection, since that might move the code and invalidate the return
- // address (unless this is somehow accounted for).
- inline void CallCFunction(ExternalReference function, int num_arguments);
-
MacroAssembler* masm_;
// Which mode to generate code for (ASCII or UC16).
@@ -223,7 +208,7 @@
Label check_preempt_label_;
Label stack_overflow_label_;
};
-#endif // V8_NATIVE_REGEXP
+#endif // V8_INTERPRETED_REGEXP
}} // namespace v8::internal
diff --git a/src/ia32/register-allocator-ia32.cc b/src/ia32/register-allocator-ia32.cc
index 0bad87d..73fefb3 100644
--- a/src/ia32/register-allocator-ia32.cc
+++ b/src/ia32/register-allocator-ia32.cc
@@ -29,6 +29,7 @@
#include "codegen-inl.h"
#include "register-allocator-inl.h"
+#include "virtual-frame-inl.h"
namespace v8 {
namespace internal {
@@ -41,13 +42,40 @@
if (is_constant()) {
Result fresh = CodeGeneratorScope::Current()->allocator()->Allocate();
ASSERT(fresh.is_valid());
- if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) {
+ if (is_untagged_int32()) {
+ fresh.set_untagged_int32(true);
+ if (handle()->IsSmi()) {
+ CodeGeneratorScope::Current()->masm()->Set(
+ fresh.reg(),
+ Immediate(Smi::cast(*handle())->value()));
+ } else if (handle()->IsHeapNumber()) {
+ double double_value = HeapNumber::cast(*handle())->value();
+ int32_t value = DoubleToInt32(double_value);
+ if (double_value == 0 && signbit(double_value)) {
+ // Negative zero must not be converted to an int32 unless
+ // the context allows it.
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+ } else if (double_value == value) {
+ CodeGeneratorScope::Current()->masm()->Set(
+ fresh.reg(), Immediate(value));
+ } else {
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+ }
+ } else {
+ // Constant is not a number. This was not predicted by AST analysis.
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+ }
+ } else if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) {
CodeGeneratorScope::Current()->MoveUnsafeSmi(fresh.reg(), handle());
} else {
CodeGeneratorScope::Current()->masm()->Set(fresh.reg(),
Immediate(handle()));
}
// This result becomes a copy of the fresh one.
+ fresh.set_type_info(type_info());
*this = fresh;
}
ASSERT(is_register());
@@ -63,13 +91,39 @@
CodeGeneratorScope::Current()->masm()->mov(fresh.reg(), reg());
} else {
ASSERT(is_constant());
- if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) {
- CodeGeneratorScope::Current()->MoveUnsafeSmi(fresh.reg(), handle());
+ if (is_untagged_int32()) {
+ if (handle()->IsSmi()) {
+ CodeGeneratorScope::Current()->masm()->Set(
+ fresh.reg(),
+ Immediate(Smi::cast(*handle())->value()));
+ } else {
+ ASSERT(handle()->IsHeapNumber());
+ double double_value = HeapNumber::cast(*handle())->value();
+ int32_t value = DoubleToInt32(double_value);
+ if (double_value == 0 && signbit(double_value)) {
+ // Negative zero must not be converted to an int32 unless
+ // the context allows it.
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+ } else if (double_value == value) {
+ CodeGeneratorScope::Current()->masm()->Set(
+ fresh.reg(), Immediate(value));
+ } else {
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(equal);
+ CodeGeneratorScope::Current()->unsafe_bailout_->Branch(not_equal);
+ }
+ }
} else {
- CodeGeneratorScope::Current()->masm()->Set(fresh.reg(),
- Immediate(handle()));
+ if (CodeGeneratorScope::Current()->IsUnsafeSmi(handle())) {
+ CodeGeneratorScope::Current()->MoveUnsafeSmi(fresh.reg(), handle());
+ } else {
+ CodeGeneratorScope::Current()->masm()->Set(fresh.reg(),
+ Immediate(handle()));
+ }
}
}
+ fresh.set_type_info(type_info());
+ fresh.set_untagged_int32(is_untagged_int32());
*this = fresh;
} else if (is_register() && reg().is(target)) {
ASSERT(CodeGeneratorScope::Current()->has_valid_frame());
diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc
index 5729d9d..809228c 100644
--- a/src/ia32/stub-cache-ia32.cc
+++ b/src/ia32/stub-cache-ia32.cc
@@ -221,7 +221,6 @@
// Load length from the string and convert to a smi.
__ mov(eax, FieldOperand(receiver, String::kLengthOffset));
- __ SmiTag(eax);
__ ret(0);
// Check if the object is a JSValue wrapper.
@@ -234,7 +233,6 @@
__ mov(scratch2, FieldOperand(receiver, JSValue::kValueOffset));
GenerateStringCheck(masm, scratch2, scratch1, miss, miss);
__ mov(eax, FieldOperand(scratch2, String::kLengthOffset));
- __ SmiTag(eax);
__ ret(0);
}
@@ -276,14 +274,15 @@
Register holder,
Register name,
JSObject* holder_obj) {
- __ push(receiver);
- __ push(holder);
__ push(name);
InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor();
ASSERT(!Heap::InNewSpace(interceptor));
- __ mov(receiver, Immediate(Handle<Object>(interceptor)));
+ Register scratch = name;
+ __ mov(scratch, Immediate(Handle<Object>(interceptor)));
+ __ push(scratch);
__ push(receiver);
- __ push(FieldOperand(receiver, InterceptorInfo::kDataOffset));
+ __ push(holder);
+ __ push(FieldOperand(scratch, InterceptorInfo::kDataOffset));
}
@@ -446,7 +445,7 @@
ExternalReference ref =
ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
- __ TailCallRuntime(ref, 5, 1);
+ __ TailCallExternalReference(ref, 5, 1);
__ bind(&cleanup);
__ pop(scratch1);
@@ -468,7 +467,7 @@
ExternalReference ref = ExternalReference(
IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
- __ TailCallRuntime(ref, 5, 1);
+ __ TailCallExternalReference(ref, 5, 1);
}
private:
@@ -476,108 +475,6 @@
};
-// Holds information about possible function call optimizations.
-class CallOptimization BASE_EMBEDDED {
- public:
- explicit CallOptimization(LookupResult* lookup)
- : constant_function_(NULL),
- is_simple_api_call_(false),
- expected_receiver_type_(NULL),
- api_call_info_(NULL) {
- if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
-
- // We only optimize constant function calls.
- if (lookup->type() != CONSTANT_FUNCTION) return;
-
- Initialize(lookup->GetConstantFunction());
- }
-
- explicit CallOptimization(JSFunction* function) {
- Initialize(function);
- }
-
- bool is_constant_call() const {
- return constant_function_ != NULL;
- }
-
- JSFunction* constant_function() const {
- ASSERT(constant_function_ != NULL);
- return constant_function_;
- }
-
- bool is_simple_api_call() const {
- return is_simple_api_call_;
- }
-
- FunctionTemplateInfo* expected_receiver_type() const {
- ASSERT(is_simple_api_call_);
- return expected_receiver_type_;
- }
-
- CallHandlerInfo* api_call_info() const {
- ASSERT(is_simple_api_call_);
- return api_call_info_;
- }
-
- // Returns the depth of the object having the expected type in the
- // prototype chain between the two arguments.
- int GetPrototypeDepthOfExpectedType(JSObject* object,
- JSObject* holder) const {
- ASSERT(is_simple_api_call_);
- if (expected_receiver_type_ == NULL) return 0;
- int depth = 0;
- while (object != holder) {
- if (object->IsInstanceOf(expected_receiver_type_)) return depth;
- object = JSObject::cast(object->GetPrototype());
- ++depth;
- }
- if (holder->IsInstanceOf(expected_receiver_type_)) return depth;
- return kInvalidProtoDepth;
- }
-
- private:
- void Initialize(JSFunction* function) {
- if (!function->is_compiled()) return;
-
- constant_function_ = function;
- is_simple_api_call_ = false;
-
- AnalyzePossibleApiFunction(function);
- }
-
- // Determines whether the given function can be called using the
- // fast api call builtin.
- void AnalyzePossibleApiFunction(JSFunction* function) {
- SharedFunctionInfo* sfi = function->shared();
- if (sfi->function_data()->IsUndefined()) return;
- FunctionTemplateInfo* info =
- FunctionTemplateInfo::cast(sfi->function_data());
-
- // Require a C++ callback.
- if (info->call_code()->IsUndefined()) return;
- api_call_info_ = CallHandlerInfo::cast(info->call_code());
-
- // Accept signatures that either have no restrictions at all or
- // only have restrictions on the receiver.
- if (!info->signature()->IsUndefined()) {
- SignatureInfo* signature = SignatureInfo::cast(info->signature());
- if (!signature->args()->IsUndefined()) return;
- if (!signature->receiver()->IsUndefined()) {
- expected_receiver_type_ =
- FunctionTemplateInfo::cast(signature->receiver());
- }
- }
-
- is_simple_api_call_ = true;
- }
-
- JSFunction* constant_function_;
- bool is_simple_api_call_;
- FunctionTemplateInfo* expected_receiver_type_;
- CallHandlerInfo* api_call_info_;
-};
-
-
// Reserves space for the extra arguments to FastHandleApiCall in the
// caller's frame.
//
@@ -698,8 +595,7 @@
CallOptimization optimization(lookup);
- if (optimization.is_constant_call() &&
- !Top::CanHaveSpecialFunctions(holder)) {
+ if (optimization.is_constant_call()) {
CompileCacheable(masm,
object,
receiver,
@@ -907,7 +803,7 @@
__ push(Immediate(Handle<Map>(transition)));
__ push(eax);
__ push(scratch);
- __ TailCallRuntime(
+ __ TailCallExternalReference(
ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1);
return;
}
@@ -951,6 +847,26 @@
}
+// Generate code to check that a global property cell is empty. Create
+// the property cell at compilation time if no cell exists for the
+// property.
+static Object* GenerateCheckPropertyCell(MacroAssembler* masm,
+ GlobalObject* global,
+ String* name,
+ Register scratch,
+ Label* miss) {
+ Object* probe = global->EnsurePropertyCell(name);
+ if (probe->IsFailure()) return probe;
+ JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(probe);
+ ASSERT(cell->value()->IsTheHole());
+ __ mov(scratch, Immediate(Handle<Object>(cell)));
+ __ cmp(FieldOperand(scratch, JSGlobalPropertyCell::kValueOffset),
+ Immediate(Factory::the_hole_value()));
+ __ j(not_equal, miss, not_taken);
+ return cell;
+}
+
+
#undef __
#define __ ACCESS_MASM(masm())
@@ -969,21 +885,19 @@
push_at_depth, miss);
// If we've skipped any global objects, it's not enough to verify
- // that their maps haven't changed.
+ // that their maps haven't changed. We also need to check that the
+ // property cell for the property is still empty.
while (object != holder) {
if (object->IsGlobalObject()) {
- GlobalObject* global = GlobalObject::cast(object);
- Object* probe = global->EnsurePropertyCell(name);
- if (probe->IsFailure()) {
- set_failure(Failure::cast(probe));
+ Object* cell = GenerateCheckPropertyCell(masm(),
+ GlobalObject::cast(object),
+ name,
+ scratch,
+ miss);
+ if (cell->IsFailure()) {
+ set_failure(Failure::cast(cell));
return result;
}
- JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(probe);
- ASSERT(cell->value()->IsTheHole());
- __ mov(scratch, Immediate(Handle<Object>(cell)));
- __ cmp(FieldOperand(scratch, JSGlobalPropertyCell::kValueOffset),
- Immediate(Factory::the_hole_value()));
- __ j(not_equal, miss, not_taken);
}
object = JSObject::cast(object->GetPrototype());
}
@@ -1047,17 +961,16 @@
__ push(receiver); // receiver
__ push(reg); // holder
__ mov(other, Immediate(callback_handle));
- __ push(other);
__ push(FieldOperand(other, AccessorInfo::kDataOffset)); // data
__ push(name_reg); // name
// Save a pointer to where we pushed the arguments pointer.
- // This will be passed as the const Arguments& to the C++ callback.
+ // This will be passed as the const AccessorInfo& to the C++ callback.
__ mov(eax, esp);
- __ add(Operand(eax), Immediate(5 * kPointerSize));
+ __ add(Operand(eax), Immediate(4 * kPointerSize));
__ mov(ebx, esp);
// Do call through the api.
- ASSERT_EQ(6, ApiGetterEntryStub::kStackSpace);
+ ASSERT_EQ(5, ApiGetterEntryStub::kStackSpace);
Address getter_address = v8::ToCData<Address>(callback->getter());
ApiFunction fun(getter_address);
ApiGetterEntryStub stub(callback_handle, &fun);
@@ -1211,6 +1124,246 @@
}
+Object* CallStubCompiler::CompileArrayPushCall(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+ ASSERT(check == RECEIVER_MAP_CHECK);
+
+ // If object is not an array, bail out to regular call.
+ if (!object->IsJSArray()) {
+ return Heap::undefined_value();
+ }
+
+ Label miss;
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ test(edx, Immediate(kSmiTagMask));
+ __ j(zero, &miss);
+
+ CheckPrototypes(JSObject::cast(object), edx,
+ holder, ebx,
+ eax, name, &miss);
+
+ if (argc == 0) {
+ // Noop, return the length.
+ __ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
+ __ ret((argc + 1) * kPointerSize);
+ } else {
+ // Get the elements array of the object.
+ __ mov(ebx, FieldOperand(edx, JSArray::kElementsOffset));
+
+ // Check that the elements are in fast mode (not dictionary).
+ __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
+ Immediate(Factory::fixed_array_map()));
+ __ j(not_equal, &miss);
+
+ if (argc == 1) { // Otherwise fall through to call builtin.
+ Label call_builtin, exit, with_rset_update, attempt_to_grow_elements;
+
+ // Get the array's length into eax and calculate new length.
+ __ mov(eax, FieldOperand(edx, JSArray::kLengthOffset));
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ add(Operand(eax), Immediate(Smi::FromInt(argc)));
+
+ // Get the element's length into ecx.
+ __ mov(ecx, FieldOperand(ebx, FixedArray::kLengthOffset));
+ __ SmiTag(ecx);
+
+ // Check if we could survive without allocation.
+ __ cmp(eax, Operand(ecx));
+ __ j(greater, &attempt_to_grow_elements);
+
+ // Save new length.
+ __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
+
+ // Push the element.
+ __ lea(edx, FieldOperand(ebx,
+ eax, times_half_pointer_size,
+ FixedArray::kHeaderSize - argc * kPointerSize));
+ __ mov(ecx, Operand(esp, argc * kPointerSize));
+ __ mov(Operand(edx, 0), ecx);
+
+ // Check if value is a smi.
+ __ test(ecx, Immediate(kSmiTagMask));
+ __ j(not_zero, &with_rset_update);
+
+ __ bind(&exit);
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&with_rset_update);
+
+ __ InNewSpace(ebx, ecx, equal, &exit);
+
+ RecordWriteStub stub(ebx, edx, ecx);
+ __ CallStub(&stub);
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&attempt_to_grow_elements);
+ ExternalReference new_space_allocation_top =
+ ExternalReference::new_space_allocation_top_address();
+ ExternalReference new_space_allocation_limit =
+ ExternalReference::new_space_allocation_limit_address();
+
+ const int kAllocationDelta = 4;
+ // Load top.
+ __ mov(ecx, Operand::StaticVariable(new_space_allocation_top));
+
+ // Check if it's the end of elements.
+ __ lea(edx, FieldOperand(ebx,
+ eax, times_half_pointer_size,
+ FixedArray::kHeaderSize - argc * kPointerSize));
+ __ cmp(edx, Operand(ecx));
+ __ j(not_equal, &call_builtin);
+ __ add(Operand(ecx), Immediate(kAllocationDelta * kPointerSize));
+ __ cmp(ecx, Operand::StaticVariable(new_space_allocation_limit));
+ __ j(above, &call_builtin);
+
+ // We fit and could grow elements.
+ __ mov(Operand::StaticVariable(new_space_allocation_top), ecx);
+ __ mov(ecx, Operand(esp, argc * kPointerSize));
+
+ // Push the argument...
+ __ mov(Operand(edx, 0), ecx);
+ // ... and fill the rest with holes.
+ for (int i = 1; i < kAllocationDelta; i++) {
+ __ mov(Operand(edx, i * kPointerSize),
+ Immediate(Factory::the_hole_value()));
+ }
+
+ // Restore receiver to edx as finish sequence assumes it's here.
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ // Increment element's and array's sizes.
+ __ add(FieldOperand(ebx, FixedArray::kLengthOffset),
+ Immediate(kAllocationDelta));
+ __ mov(FieldOperand(edx, JSArray::kLengthOffset), eax);
+
+ // Elements are in new space, so no remembered set updates are necessary.
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&call_builtin);
+ }
+
+ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush),
+ argc + 1,
+ 1);
+ }
+
+ __ bind(&miss);
+
+ Handle<Code> ic = ComputeCallMiss(arguments().immediate());
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ String* function_name = NULL;
+ if (function->shared()->name()->IsString()) {
+ function_name = String::cast(function->shared()->name());
+ }
+ return GetCode(CONSTANT_FUNCTION, function_name);
+}
+
+
+Object* CallStubCompiler::CompileArrayPopCall(Object* object,
+ JSObject* holder,
+ JSFunction* function,
+ String* name,
+ CheckType check) {
+ // ----------- S t a t e -------------
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -- esp[(argc - n) * 4] : arg[n] (zero-based)
+ // -- ...
+ // -- esp[(argc + 1) * 4] : receiver
+ // -----------------------------------
+ ASSERT(check == RECEIVER_MAP_CHECK);
+
+ // If object is not an array, bail out to regular call.
+ if (!object->IsJSArray()) {
+ return Heap::undefined_value();
+ }
+
+ Label miss, return_undefined, call_builtin;
+
+ // Get the receiver from the stack.
+ const int argc = arguments().immediate();
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ test(edx, Immediate(kSmiTagMask));
+ __ j(zero, &miss);
+ CheckPrototypes(JSObject::cast(object), edx,
+ holder, ebx,
+ eax, name, &miss);
+
+ // Get the elements array of the object.
+ __ mov(ebx, FieldOperand(edx, JSArray::kElementsOffset));
+
+ // Check that the elements are in fast mode (not dictionary).
+ __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
+ Immediate(Factory::fixed_array_map()));
+ __ j(not_equal, &miss);
+
+ // Get the array's length into ecx and calculate new length.
+ __ mov(ecx, FieldOperand(edx, JSArray::kLengthOffset));
+ __ sub(Operand(ecx), Immediate(Smi::FromInt(1)));
+ __ j(negative, &return_undefined);
+
+ // Get the last element.
+ STATIC_ASSERT(kSmiTagSize == 1);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ mov(eax, FieldOperand(ebx,
+ ecx, times_half_pointer_size,
+ FixedArray::kHeaderSize));
+ __ cmp(Operand(eax), Immediate(Factory::the_hole_value()));
+ __ j(equal, &call_builtin);
+
+ // Set the array's length.
+ __ mov(FieldOperand(edx, JSArray::kLengthOffset), ecx);
+
+ // Fill with the hole.
+ __ mov(FieldOperand(ebx,
+ ecx, times_half_pointer_size,
+ FixedArray::kHeaderSize),
+ Immediate(Factory::the_hole_value()));
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&return_undefined);
+ __ mov(eax, Immediate(Factory::undefined_value()));
+ __ ret((argc + 1) * kPointerSize);
+
+ __ bind(&call_builtin);
+ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop),
+ argc + 1,
+ 1);
+
+ __ bind(&miss);
+
+ Handle<Code> ic = ComputeCallMiss(arguments().immediate());
+ __ jmp(ic, RelocInfo::CODE_TARGET);
+
+ // Return the generated code.
+ String* function_name = NULL;
+ if (function->shared()->name()->IsString()) {
+ function_name = String::cast(function->shared()->name());
+ }
+ return GetCode(CONSTANT_FUNCTION, function_name);
+}
+
+
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
@@ -1223,7 +1376,19 @@
// -- ...
// -- esp[(argc + 1) * 4] : receiver
// -----------------------------------
- Label miss;
+
+ SharedFunctionInfo* function_info = function->shared();
+ if (function_info->HasCustomCallGenerator()) {
+ CustomCallGenerator generator =
+ ToCData<CustomCallGenerator>(function_info->function_data());
+ Object* result = generator(this, object, holder, function, name, check);
+ // undefined means bail out to regular compiler.
+ if (!result->IsUndefined()) {
+ return result;
+ }
+ }
+
+ Label miss_in_smi_check;
// Get the receiver from the stack.
const int argc = arguments().immediate();
@@ -1232,7 +1397,7 @@
// Check that the receiver isn't a smi.
if (check != NUMBER_CHECK) {
__ test(edx, Immediate(kSmiTagMask));
- __ j(zero, &miss, not_taken);
+ __ j(zero, &miss_in_smi_check, not_taken);
}
// Make sure that it's okay not to patch the on stack receiver
@@ -1241,6 +1406,7 @@
CallOptimization optimization(function);
int depth = kInvalidProtoDepth;
+ Label miss;
switch (check) {
case RECEIVER_MAP_CHECK:
@@ -1332,18 +1498,6 @@
break;
}
- case JSARRAY_HAS_FAST_ELEMENTS_CHECK:
- CheckPrototypes(JSObject::cast(object), edx, holder,
- ebx, eax, name, &miss);
- // Make sure object->HasFastElements().
- // Get the elements array of the object.
- __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
- // Check that the object is in fast mode (not dictionary).
- __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
- Immediate(Factory::fixed_array_map()));
- __ j(not_equal, &miss, not_taken);
- break;
-
default:
UNREACHABLE();
}
@@ -1359,6 +1513,7 @@
if (depth != kInvalidProtoDepth) {
FreeSpaceForFastApiCall(masm(), eax);
}
+ __ bind(&miss_in_smi_check);
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
__ jmp(ic, RelocInfo::CODE_TARGET);
@@ -1587,7 +1742,7 @@
// Do tail-call to the runtime system.
ExternalReference store_callback_property =
ExternalReference(IC_Utility(IC::kStoreCallbackProperty));
- __ TailCallRuntime(store_callback_property, 4, 1);
+ __ TailCallExternalReference(store_callback_property, 4, 1);
// Handle store cache miss.
__ bind(&miss);
@@ -1636,7 +1791,7 @@
// Do tail-call to the runtime system.
ExternalReference store_ic_property =
ExternalReference(IC_Utility(IC::kStoreInterceptorProperty));
- __ TailCallRuntime(store_ic_property, 3, 1);
+ __ TailCallExternalReference(store_ic_property, 3, 1);
// Handle store cache miss.
__ bind(&miss);
@@ -1689,23 +1844,18 @@
String* name) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
Label miss;
__ IncrementCounter(&Counters::keyed_store_field, 1);
- // Get the name from the stack.
- __ mov(ecx, Operand(esp, 1 * kPointerSize));
// Check that the name has not changed.
__ cmp(Operand(ecx), Immediate(Handle<String>(name)));
__ j(not_equal, &miss, not_taken);
- // Get the object from the stack.
- __ mov(edx, Operand(esp, 2 * kPointerSize));
-
// Generate store field code. Trashes the name register.
GenerateStoreField(masm(),
object,
@@ -1725,6 +1875,48 @@
}
+Object* LoadStubCompiler::CompileLoadNonexistent(String* name,
+ JSObject* object,
+ JSObject* last) {
+ // ----------- S t a t e -------------
+ // -- eax : receiver
+ // -- ecx : name
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label miss;
+
+ // Check that the receiver isn't a smi.
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(zero, &miss, not_taken);
+
+ // Check the maps of the full prototype chain. Also check that
+ // global property cells up to (but not including) the last object
+ // in the prototype chain are empty.
+ CheckPrototypes(object, eax, last, ebx, edx, name, &miss);
+
+ // If the last object in the prototype chain is a global object,
+ // check that the global property cell is empty.
+ if (last->IsGlobalObject()) {
+ Object* cell = GenerateCheckPropertyCell(masm(),
+ GlobalObject::cast(last),
+ name,
+ edx,
+ &miss);
+ if (cell->IsFailure()) return cell;
+ }
+
+ // Return undefined if maps of the full prototype chain are still the
+ // same and no global property with this name contains a value.
+ __ mov(eax, Factory::undefined_value());
+ __ ret(0);
+
+ __ bind(&miss);
+ GenerateLoadMiss(masm(), Code::LOAD_IC);
+
+ // Return the generated code.
+ return GetCode(NONEXISTENT, Heap::empty_string());
+}
+
Object* LoadStubCompiler::CompileLoadField(JSObject* object,
JSObject* holder,
diff --git a/src/ia32/virtual-frame-ia32.cc b/src/ia32/virtual-frame-ia32.cc
index 7df028e..10aaa52 100644
--- a/src/ia32/virtual-frame-ia32.cc
+++ b/src/ia32/virtual-frame-ia32.cc
@@ -30,29 +30,13 @@
#include "codegen-inl.h"
#include "register-allocator-inl.h"
#include "scopes.h"
+#include "virtual-frame-inl.h"
namespace v8 {
namespace internal {
#define __ ACCESS_MASM(masm())
-// -------------------------------------------------------------------------
-// VirtualFrame implementation.
-
-// On entry to a function, the virtual frame already contains the receiver,
-// the parameters, and a return address. All frame elements are in memory.
-VirtualFrame::VirtualFrame()
- : elements_(parameter_count() + local_count() + kPreallocatedElements),
- stack_pointer_(parameter_count() + 1) { // 0-based index of TOS.
- for (int i = 0; i <= stack_pointer_; i++) {
- elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown));
- }
- for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
- register_locations_[i] = kIllegalIndex;
- }
-}
-
-
void VirtualFrame::SyncElementBelowStackPointer(int index) {
// Emit code to write elements below the stack pointer to their
// (already allocated) stack address.
@@ -178,7 +162,7 @@
if (element.is_constant() || element.is_copy()) {
if (element.is_synced()) {
// Just spill.
- elements_[i] = FrameElement::MemoryElement(NumberInfo::kUnknown);
+ elements_[i] = FrameElement::MemoryElement(TypeInfo::Unknown());
} else {
// Allocate to a register.
FrameElement backing_element; // Invalid if not a copy.
@@ -190,7 +174,7 @@
elements_[i] =
FrameElement::RegisterElement(fresh.reg(),
FrameElement::NOT_SYNCED,
- NumberInfo::kUnknown);
+ TypeInfo::Unknown());
Use(fresh.reg(), i);
// Emit a move.
@@ -223,7 +207,7 @@
// The copy flag is not relied on before the end of this loop,
// including when registers are spilled.
elements_[i].clear_copied();
- elements_[i].set_number_info(NumberInfo::kUnknown);
+ elements_[i].set_type_info(TypeInfo::Unknown());
}
}
}
@@ -613,12 +597,12 @@
elements_[new_backing_index] =
FrameElement::RegisterElement(backing_reg,
FrameElement::SYNCED,
- original.number_info());
+ original.type_info());
} else {
elements_[new_backing_index] =
FrameElement::RegisterElement(backing_reg,
FrameElement::NOT_SYNCED,
- original.number_info());
+ original.type_info());
}
// Update the other copies.
for (int i = new_backing_index + 1; i < element_count(); i++) {
@@ -650,7 +634,7 @@
FrameElement new_element =
FrameElement::RegisterElement(fresh.reg(),
FrameElement::NOT_SYNCED,
- original.number_info());
+ original.type_info());
Use(fresh.reg(), element_count());
elements_.Add(new_element);
__ mov(fresh.reg(), Operand(ebp, fp_relative(index)));
@@ -791,6 +775,89 @@
}
+void VirtualFrame::UntaggedPushFrameSlotAt(int index) {
+ ASSERT(index >= 0);
+ ASSERT(index <= element_count());
+ FrameElement original = elements_[index];
+ if (original.is_copy()) {
+ original = elements_[original.index()];
+ index = original.index();
+ }
+
+ switch (original.type()) {
+ case FrameElement::MEMORY:
+ case FrameElement::REGISTER: {
+ Label done;
+ // Emit code to load the original element's data into a register.
+ // Push that register as a FrameElement on top of the frame.
+ Result fresh = cgen()->allocator()->Allocate();
+ ASSERT(fresh.is_valid());
+ Register fresh_reg = fresh.reg();
+ FrameElement new_element =
+ FrameElement::RegisterElement(fresh_reg,
+ FrameElement::NOT_SYNCED,
+ original.type_info());
+ new_element.set_untagged_int32(true);
+ Use(fresh_reg, element_count());
+ fresh.Unuse(); // BreakTarget does not handle a live Result well.
+ elements_.Add(new_element);
+ if (original.is_register()) {
+ __ mov(fresh_reg, original.reg());
+ } else {
+ ASSERT(original.is_memory());
+ __ mov(fresh_reg, Operand(ebp, fp_relative(index)));
+ }
+ // Now convert the value to int32, or bail out.
+ if (original.type_info().IsSmi()) {
+ __ SmiUntag(fresh_reg);
+ // Pushing the element is completely done.
+ } else {
+ __ test(fresh_reg, Immediate(kSmiTagMask));
+ Label not_smi;
+ __ j(not_zero, ¬_smi);
+ __ SmiUntag(fresh_reg);
+ __ jmp(&done);
+
+ __ bind(¬_smi);
+ if (!original.type_info().IsNumber()) {
+ __ cmp(FieldOperand(fresh_reg, HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ cgen()->unsafe_bailout_->Branch(not_equal);
+ }
+
+ if (!CpuFeatures::IsSupported(SSE2)) {
+ UNREACHABLE();
+ } else {
+ CpuFeatures::Scope use_sse2(SSE2);
+ __ movdbl(xmm0, FieldOperand(fresh_reg, HeapNumber::kValueOffset));
+ __ cvttsd2si(fresh_reg, Operand(xmm0));
+ __ cvtsi2sd(xmm1, Operand(fresh_reg));
+ __ ucomisd(xmm0, xmm1);
+ cgen()->unsafe_bailout_->Branch(not_equal);
+ cgen()->unsafe_bailout_->Branch(parity_even); // NaN.
+ // Test for negative zero.
+ __ test(fresh_reg, Operand(fresh_reg));
+ __ j(not_zero, &done);
+ __ movmskpd(fresh_reg, xmm0);
+ __ and_(fresh_reg, 0x1);
+ cgen()->unsafe_bailout_->Branch(not_equal);
+ }
+ __ bind(&done);
+ }
+ break;
+ }
+ case FrameElement::CONSTANT:
+ elements_.Add(CopyElementAt(index));
+ elements_[element_count() - 1].set_untagged_int32(true);
+ break;
+ case FrameElement::COPY:
+ case FrameElement::INVALID:
+ UNREACHABLE();
+ break;
+ }
+}
+
+
void VirtualFrame::PushTryHandler(HandlerType type) {
ASSERT(cgen()->HasValidEntryRegisters());
// Grow the expression stack by handler size less one (the return
@@ -842,6 +909,25 @@
}
+Result VirtualFrame::CallJSFunction(int arg_count) {
+ Result function = Pop();
+
+ // InvokeFunction requires function in edi. Move it in there.
+ function.ToRegister(edi);
+ function.Unuse();
+
+ // +1 for receiver.
+ PrepareForCall(arg_count + 1, arg_count + 1);
+ ASSERT(cgen()->HasValidEntryRegisters());
+ ParameterCount count(arg_count);
+ __ InvokeFunction(edi, count, CALL_FUNCTION);
+ RestoreContextRegister();
+ Result result = cgen()->allocator()->Allocate(eax);
+ ASSERT(result.is_valid());
+ return result;
+}
+
+
Result VirtualFrame::CallRuntime(Runtime::Function* f, int arg_count) {
PrepareForCall(arg_count, arg_count);
ASSERT(cgen()->HasValidEntryRegisters());
@@ -895,30 +981,39 @@
}
+// This function assumes that the only results that could be in a_reg or b_reg
+// are a and b. Other results can be live, but must not be in a_reg or b_reg.
+void VirtualFrame::MoveResultsToRegisters(Result* a,
+ Result* b,
+ Register a_reg,
+ Register b_reg) {
+ if (a->is_register() && a->reg().is(a_reg)) {
+ b->ToRegister(b_reg);
+ } else if (!cgen()->allocator()->is_used(a_reg)) {
+ a->ToRegister(a_reg);
+ b->ToRegister(b_reg);
+ } else if (cgen()->allocator()->is_used(b_reg)) {
+ // a must be in b_reg, b in a_reg.
+ __ xchg(a_reg, b_reg);
+ // Results a and b will be invalidated, so it is ok if they are switched.
+ } else {
+ b->ToRegister(b_reg);
+ a->ToRegister(a_reg);
+ }
+ a->Unuse();
+ b->Unuse();
+}
+
+
Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) {
// Name and receiver are on the top of the frame. The IC expects
// name in ecx and receiver in eax.
- Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
Result name = Pop();
Result receiver = Pop();
PrepareForCall(0, 0); // No stack arguments.
- // Move results to the right registers:
- if (name.is_register() && name.reg().is(eax)) {
- if (receiver.is_register() && receiver.reg().is(ecx)) {
- // Wrong registers.
- __ xchg(eax, ecx);
- } else {
- // Register ecx is free for name, which frees eax for receiver.
- name.ToRegister(ecx);
- receiver.ToRegister(eax);
- }
- } else {
- // Register eax is free for receiver, which frees ecx for name.
- receiver.ToRegister(eax);
- name.ToRegister(ecx);
- }
- name.Unuse();
- receiver.Unuse();
+ MoveResultsToRegisters(&name, &receiver, ecx, eax);
+
+ Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
return RawCallCodeObject(ic, mode);
}
@@ -928,20 +1023,7 @@
Result key = Pop();
Result receiver = Pop();
PrepareForCall(0, 0);
-
- if (!key.is_register() || !key.reg().is(edx)) {
- // Register edx is available for receiver.
- receiver.ToRegister(edx);
- key.ToRegister(eax);
- } else if (!receiver.is_register() || !receiver.reg().is(eax)) {
- // Register eax is available for key.
- key.ToRegister(eax);
- receiver.ToRegister(edx);
- } else {
- __ xchg(edx, eax);
- }
- key.Unuse();
- receiver.Unuse();
+ MoveResultsToRegisters(&key, &receiver, eax, edx);
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
return RawCallCodeObject(ic, mode);
@@ -957,42 +1039,62 @@
PrepareForCall(0, 0);
value.ToRegister(eax);
__ mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
- __ mov(ecx, name);
+ value.Unuse();
} else {
Result receiver = Pop();
PrepareForCall(0, 0);
-
- if (value.is_register() && value.reg().is(edx)) {
- if (receiver.is_register() && receiver.reg().is(eax)) {
- // Wrong registers.
- __ xchg(eax, edx);
- } else {
- // Register eax is free for value, which frees edx for receiver.
- value.ToRegister(eax);
- receiver.ToRegister(edx);
- }
- } else {
- // Register edx is free for receiver, which guarantees eax is free for
- // value.
- receiver.ToRegister(edx);
- value.ToRegister(eax);
- }
+ MoveResultsToRegisters(&value, &receiver, eax, edx);
}
__ mov(ecx, name);
- value.Unuse();
return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
}
Result VirtualFrame::CallKeyedStoreIC() {
// Value, key, and receiver are on the top of the frame. The IC
- // expects value in eax and key and receiver on the stack. It does
- // not drop the key and receiver.
- Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
+ // expects value in eax, key in ecx, and receiver in edx.
Result value = Pop();
- PrepareForCall(2, 0); // Two stack args, neither callee-dropped.
- value.ToRegister(eax);
- value.Unuse();
+ Result key = Pop();
+ Result receiver = Pop();
+ PrepareForCall(0, 0);
+ if (!cgen()->allocator()->is_used(eax) ||
+ (value.is_register() && value.reg().is(eax))) {
+ if (!cgen()->allocator()->is_used(eax)) {
+ value.ToRegister(eax);
+ }
+ MoveResultsToRegisters(&key, &receiver, ecx, edx);
+ value.Unuse();
+ } else if (!cgen()->allocator()->is_used(ecx) ||
+ (key.is_register() && key.reg().is(ecx))) {
+ if (!cgen()->allocator()->is_used(ecx)) {
+ key.ToRegister(ecx);
+ }
+ MoveResultsToRegisters(&value, &receiver, eax, edx);
+ key.Unuse();
+ } else if (!cgen()->allocator()->is_used(edx) ||
+ (receiver.is_register() && receiver.reg().is(edx))) {
+ if (!cgen()->allocator()->is_used(edx)) {
+ receiver.ToRegister(edx);
+ }
+ MoveResultsToRegisters(&key, &value, ecx, eax);
+ receiver.Unuse();
+ } else {
+ // All three registers are used, and no value is in the correct place.
+ // We have one of the two circular permutations of eax, ecx, edx.
+ ASSERT(value.is_register());
+ if (value.reg().is(ecx)) {
+ __ xchg(eax, edx);
+ __ xchg(eax, ecx);
+ } else {
+ __ xchg(eax, ecx);
+ __ xchg(eax, edx);
+ }
+ value.Unuse();
+ key.Unuse();
+ receiver.Unuse();
+ }
+
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
}
@@ -1065,13 +1167,14 @@
FrameElement element = elements_.RemoveLast();
int index = element_count();
ASSERT(element.is_valid());
+ ASSERT(element.is_untagged_int32() == cgen()->in_safe_int32_mode());
// Get number type information of the result.
- NumberInfo::Type info;
+ TypeInfo info;
if (!element.is_copy()) {
- info = element.number_info();
+ info = element.type_info();
} else {
- info = elements_[element.index()].number_info();
+ info = elements_[element.index()].type_info();
}
bool pop_needed = (stack_pointer_ == index);
@@ -1081,7 +1184,8 @@
Result temp = cgen()->allocator()->Allocate();
ASSERT(temp.is_valid());
__ pop(temp.reg());
- temp.set_number_info(info);
+ temp.set_type_info(info);
+ temp.set_untagged_int32(element.is_untagged_int32());
return temp;
}
@@ -1094,6 +1198,7 @@
if (element.is_register()) {
Unuse(element.reg());
} else if (element.is_copy()) {
+ ASSERT(!element.is_untagged_int32());
ASSERT(element.index() < index);
index = element.index();
element = elements_[index];
@@ -1105,23 +1210,28 @@
// Memory elements could only be the backing store of a copy.
// Allocate the original to a register.
ASSERT(index <= stack_pointer_);
+ ASSERT(!element.is_untagged_int32());
Result temp = cgen()->allocator()->Allocate();
ASSERT(temp.is_valid());
Use(temp.reg(), index);
FrameElement new_element =
FrameElement::RegisterElement(temp.reg(),
FrameElement::SYNCED,
- element.number_info());
+ element.type_info());
// Preserve the copy flag on the element.
if (element.is_copied()) new_element.set_copied();
elements_[index] = new_element;
__ mov(temp.reg(), Operand(ebp, fp_relative(index)));
return Result(temp.reg(), info);
} else if (element.is_register()) {
- return Result(element.reg(), info);
+ Result return_value(element.reg(), info);
+ return_value.set_untagged_int32(element.is_untagged_int32());
+ return return_value;
} else {
ASSERT(element.is_constant());
- return Result(element.handle());
+ Result return_value(element.handle());
+ return_value.set_untagged_int32(element.is_untagged_int32());
+ return return_value;
}
}
@@ -1142,7 +1252,7 @@
}
-void VirtualFrame::EmitPush(Register reg, NumberInfo::Type info) {
+void VirtualFrame::EmitPush(Register reg, TypeInfo info) {
ASSERT(stack_pointer_ == element_count() - 1);
elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
@@ -1150,7 +1260,7 @@
}
-void VirtualFrame::EmitPush(Operand operand, NumberInfo::Type info) {
+void VirtualFrame::EmitPush(Operand operand, TypeInfo info) {
ASSERT(stack_pointer_ == element_count() - 1);
elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
@@ -1158,7 +1268,7 @@
}
-void VirtualFrame::EmitPush(Immediate immediate, NumberInfo::Type info) {
+void VirtualFrame::EmitPush(Immediate immediate, TypeInfo info) {
ASSERT(stack_pointer_ == element_count() - 1);
elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
@@ -1166,6 +1276,12 @@
}
+void VirtualFrame::PushUntaggedElement(Handle<Object> value) {
+ elements_.Add(FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED));
+ elements_[element_count() - 1].set_untagged_int32(true);
+}
+
+
void VirtualFrame::Push(Expression* expr) {
ASSERT(expr->IsTrivial());
@@ -1176,11 +1292,17 @@
}
VariableProxy* proxy = expr->AsVariableProxy();
- if (proxy != NULL && proxy->is_this()) {
- PushParameterAt(-1);
- return;
+ if (proxy != NULL) {
+ Slot* slot = proxy->var()->slot();
+ if (slot->type() == Slot::LOCAL) {
+ PushLocalAt(slot->index());
+ return;
+ }
+ if (slot->type() == Slot::PARAMETER) {
+ PushParameterAt(slot->index());
+ return;
+ }
}
-
UNREACHABLE();
}
diff --git a/src/ia32/virtual-frame-ia32.h b/src/ia32/virtual-frame-ia32.h
index 7be593c..14fe466 100644
--- a/src/ia32/virtual-frame-ia32.h
+++ b/src/ia32/virtual-frame-ia32.h
@@ -28,7 +28,7 @@
#ifndef V8_IA32_VIRTUAL_FRAME_IA32_H_
#define V8_IA32_VIRTUAL_FRAME_IA32_H_
-#include "number-info.h"
+#include "type-info.h"
#include "register-allocator.h"
#include "scopes.h"
@@ -73,10 +73,10 @@
static const int kIllegalIndex = -1;
// Construct an initial virtual frame on entry to a JS function.
- VirtualFrame();
+ inline VirtualFrame();
// Construct a virtual frame as a clone of an existing one.
- explicit VirtualFrame(VirtualFrame* original);
+ explicit inline VirtualFrame(VirtualFrame* original);
CodeGenerator* cgen() { return CodeGeneratorScope::Current(); }
@@ -84,7 +84,7 @@
// Create a duplicate of an existing valid frame element.
FrameElement CopyElementAt(int index,
- NumberInfo::Type info = NumberInfo::kUninitialized);
+ TypeInfo info = TypeInfo::Uninitialized());
// The number of elements on the virtual frame.
int element_count() { return elements_.length(); }
@@ -138,7 +138,7 @@
void ForgetElements(int count);
// Spill all values from the frame to memory.
- void SpillAll();
+ inline void SpillAll();
// Spill all occurrences of a specific register from the frame.
void Spill(Register reg) {
@@ -199,7 +199,7 @@
// Prepare for returning from the frame by spilling locals. This
// avoids generating unnecessary merge code when jumping to the
// shared return site. Emits code for spills.
- void PrepareForReturn();
+ inline void PrepareForReturn();
// Number of local variables after when we use a loop for allocating.
static const int kLocalVarBound = 10;
@@ -242,6 +242,11 @@
PushFrameSlotAt(local0_index() + index);
}
+ // Push a copy of the value of a local frame slot on top of the frame.
+ void UntaggedPushLocalAt(int index) {
+ UntaggedPushFrameSlotAt(local0_index() + index);
+ }
+
// Push the value of a local frame slot on top of the frame and invalidate
// the local slot. The slot should be written to before trying to read
// from it again.
@@ -282,6 +287,11 @@
PushFrameSlotAt(param0_index() + index);
}
+ // Push a copy of the value of a parameter frame slot on top of the frame.
+ void UntaggedPushParameterAt(int index) {
+ UntaggedPushFrameSlotAt(param0_index() + index);
+ }
+
// Push the value of a paramter frame slot on top of the frame and
// invalidate the parameter slot. The slot should be written to before
// trying to read from it again.
@@ -321,6 +331,10 @@
// arguments are consumed by the call.
Result CallStub(CodeStub* stub, Result* arg0, Result* arg1);
+ // Call JS function from top of the stack with arguments
+ // taken from the stack.
+ Result CallJSFunction(int arg_count);
+
// Call runtime given the number of arguments expected on (and
// removed from) the stack.
Result CallRuntime(Runtime::Function* f, int arg_count);
@@ -388,18 +402,18 @@
// Push an element on top of the expression stack and emit a
// corresponding push instruction.
void EmitPush(Register reg,
- NumberInfo::Type info = NumberInfo::kUnknown);
+ TypeInfo info = TypeInfo::Unknown());
void EmitPush(Operand operand,
- NumberInfo::Type info = NumberInfo::kUnknown);
+ TypeInfo info = TypeInfo::Unknown());
void EmitPush(Immediate immediate,
- NumberInfo::Type info = NumberInfo::kUnknown);
+ TypeInfo info = TypeInfo::Unknown());
// Push an element on the virtual frame.
- void Push(Register reg, NumberInfo::Type info = NumberInfo::kUnknown);
- void Push(Handle<Object> value);
- void Push(Smi* value) {
- Push(Handle<Object> (value));
- }
+ inline void Push(Register reg, TypeInfo info = TypeInfo::Unknown());
+ inline void Push(Handle<Object> value);
+ inline void Push(Smi* value);
+
+ void PushUntaggedElement(Handle<Object> value);
// Pushing a result invalidates it (its contents become owned by the
// frame).
@@ -407,11 +421,15 @@
// This assert will trigger if you try to push the same value twice.
ASSERT(result->is_valid());
if (result->is_register()) {
- Push(result->reg(), result->number_info());
+ Push(result->reg(), result->type_info());
} else {
ASSERT(result->is_constant());
Push(result->handle());
}
+ if (cgen()->in_safe_int32_mode()) {
+ ASSERT(result->is_untagged_int32());
+ elements_[element_count() - 1].set_untagged_int32(true);
+ }
result->Unuse();
}
@@ -422,7 +440,19 @@
// Nip removes zero or more elements from immediately below the top
// of the frame, leaving the previous top-of-frame value on top of
// the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
- void Nip(int num_dropped);
+ inline void Nip(int num_dropped);
+
+ // Check that the frame has no elements containing untagged int32 elements.
+ bool HasNoUntaggedInt32Elements() {
+ for (int i = 0; i < element_count(); ++i) {
+ if (elements_[i].is_untagged_int32()) return false;
+ }
+ return true;
+ }
+
+ // Update the type information of a variable frame element directly.
+ inline void SetTypeForLocalAt(int index, TypeInfo info);
+ inline void SetTypeForParamAt(int index, TypeInfo info);
private:
static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
@@ -530,7 +560,12 @@
// Push a copy of a frame slot (typically a local or parameter) on top of
// the frame.
- void PushFrameSlotAt(int index);
+ inline void PushFrameSlotAt(int index);
+
+ // Push a copy of a frame slot (typically a local or parameter) on top of
+ // the frame, at an untagged int32 value. Bails out if the value is not
+ // an int32.
+ void UntaggedPushFrameSlotAt(int index);
// Push a the value of a frame slot (typically a local or parameter) on
// top of the frame and invalidate the slot.
@@ -573,6 +608,14 @@
// Register counts are correctly updated.
int InvalidateFrameSlotAt(int index);
+ // This function assumes that a and b are the only results that could be in
+ // the registers a_reg or b_reg. Other results can be live, but must not
+ // be in the registers a_reg or b_reg. The results a and b are invalidated.
+ void MoveResultsToRegisters(Result* a,
+ Result* b,
+ Register a_reg,
+ Register b_reg);
+
// Call a code stub that has already been prepared for calling (via
// PrepareForCall).
Result RawCallStub(CodeStub* stub);
@@ -581,7 +624,7 @@
// (via PrepareForCall).
Result RawCallCodeObject(Handle<Code> code, RelocInfo::Mode rmode);
- bool Equals(VirtualFrame* other);
+ inline bool Equals(VirtualFrame* other);
// Classes that need raw access to the elements_ array.
friend class DeferredCode;