diff options
Diffstat (limited to 'deps/v8/src/mips/full-codegen-mips.cc')
-rw-r--r-- | deps/v8/src/mips/full-codegen-mips.cc | 829 |
1 files changed, 520 insertions, 309 deletions
diff --git a/deps/v8/src/mips/full-codegen-mips.cc b/deps/v8/src/mips/full-codegen-mips.cc index 9a210c49e..201e6b8e1 100644 --- a/deps/v8/src/mips/full-codegen-mips.cc +++ b/deps/v8/src/mips/full-codegen-mips.cc @@ -47,6 +47,7 @@ #include "stub-cache.h" #include "mips/code-stubs-mips.h" +#include "mips/macro-assembler-mips.h" namespace v8 { namespace internal { @@ -54,17 +55,14 @@ namespace internal { #define __ ACCESS_MASM(masm_) -static unsigned GetPropertyId(Property* property) { - return property->id(); -} - - // A patch site is a location in the code which it is possible to patch. This // class has a number of methods to emit the code which is patchable and the // method EmitPatchInfo to record a marker back to the patchable code. This -// marker is a andi at, rx, #yyy instruction, and x * 0x0000ffff + yyy (raw 16 -// bit immediate value is used) is the delta from the pc to the first +// marker is a andi zero_reg, rx, #yyyy instruction, and rx * 0x0000ffff + yyyy +// (raw 16 bit immediate value is used) is the delta from the pc to the first // instruction of the patchable code. +// The marker instruction is effectively a NOP (dest is zero_reg) and will +// never be emitted by normal code. class JumpPatchSite BASE_EMBEDDED { public: explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) { @@ -103,7 +101,7 @@ class JumpPatchSite BASE_EMBEDDED { if (patch_site_.is_bound()) { int delta_to_patch_site = masm_->InstructionsGeneratedSince(&patch_site_); Register reg = Register::from_code(delta_to_patch_site / kImm16Mask); - __ andi(at, reg, delta_to_patch_site % kImm16Mask); + __ andi(zero_reg, reg, delta_to_patch_site % kImm16Mask); #ifdef DEBUG info_emitted_ = true; #endif @@ -139,6 +137,8 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { ASSERT(info_ == NULL); info_ = info; scope_ = info->scope(); + handler_table_ = + isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED); SetFunctionPosition(function()); Comment cmnt(masm_, "[ function compiled by full code generator"); @@ -153,7 +153,7 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // with undefined when called as functions (without an explicit // receiver object). t1 is zero for method calls and non-zero for // function calls. - if (info->is_strict_mode() || info->is_native()) { + if (!info->is_classic_mode() || info->is_native()) { Label ok; __ Branch(&ok, eq, t1, Operand(zero_reg)); int receiver_offset = info->scope()->num_parameters() * kPointerSize; @@ -162,6 +162,11 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { __ bind(&ok); } + // Open a frame scope to indicate that there is a frame on the stack. The + // MANUAL indicates that the scope shouldn't actually generate code to set up + // the frame (that is done below). + FrameScope frame_scope(masm_, StackFrame::MANUAL); + int locals_count = info->scope()->num_stack_slots(); __ Push(ra, fp, cp, a1); @@ -207,14 +212,12 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // Load parameter from stack. __ lw(a0, MemOperand(fp, parameter_offset)); // Store it in the context. - __ li(a1, Operand(Context::SlotOffset(var->index()))); - __ addu(a2, cp, a1); - __ sw(a0, MemOperand(a2, 0)); - // Update the write barrier. This clobbers all involved - // registers, so we have to use two more registers to avoid - // clobbering cp. - __ mov(a2, cp); - __ RecordWrite(a2, a1, a3); + MemOperand target = ContextOperand(cp, var->index()); + __ sw(a0, target); + + // Update the write barrier. + __ RecordWriteContextSlot( + cp, target.offset(), a0, a3, kRAHasBeenSaved, kDontSaveFPRegs); } } } @@ -242,7 +245,7 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // The stub will rewrite receiever and parameter count if the previous // stack frame was an arguments adapter frame. ArgumentsAccessStub::Type type; - if (is_strict_mode()) { + if (!is_classic_mode()) { type = ArgumentsAccessStub::NEW_STRICT; } else if (function()->has_duplicate_parameters()) { type = ArgumentsAccessStub::NEW_NON_STRICT_SLOW; @@ -272,7 +275,10 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // constant. if (scope()->is_function_scope() && scope()->function() != NULL) { int ignored = 0; - EmitDeclaration(scope()->function(), Variable::CONST, NULL, &ignored); + VariableProxy* proxy = scope()->function(); + ASSERT(proxy->var()->mode() == CONST || + proxy->var()->mode() == CONST_HARMONY); + EmitDeclaration(proxy, proxy->var()->mode(), NULL, &ignored); } VisitDeclarations(scope()->declarations()); } @@ -310,17 +316,25 @@ void FullCodeGenerator::ClearAccumulator() { void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt) { + // The generated code is used in Deoptimizer::PatchStackCheckCodeAt so we need + // to make sure it is constant. Branch may emit a skip-or-jump sequence + // instead of the normal Branch. It seems that the "skip" part of that + // sequence is about as long as this Branch would be so it is safe to ignore + // that. + Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_); Comment cmnt(masm_, "[ Stack check"); Label ok; __ LoadRoot(t0, Heap::kStackLimitRootIndex); - __ Branch(&ok, hs, sp, Operand(t0)); + __ sltu(at, sp, t0); + __ beq(at, zero_reg, &ok); + // CallStub will emit a li t9, ... first, so it is safe to use the delay slot. StackCheckStub stub; + __ CallStub(&stub); // Record a mapping of this PC offset to the OSR id. This is used to find // the AST id from the unoptimized code in order to use it as a key into // the deoptimization input data found in the optimized code. RecordStackCheck(stmt->OsrEntryId()); - __ CallStub(&stub); __ bind(&ok); PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); // Record a mapping of the OSR id to this PC. This is used if the OSR @@ -393,7 +407,7 @@ void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { void FullCodeGenerator::TestContext::Plug(Variable* var) const { // For simplicity we always test the accumulator register. codegen()->GetVar(result_register(), var); - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); codegen()->DoTest(this); } @@ -416,7 +430,7 @@ void FullCodeGenerator::StackValueContext::Plug( void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -451,7 +465,7 @@ void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const { void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -510,7 +524,7 @@ void FullCodeGenerator::TestContext::DropAndPlug(int count, // For simplicity we always test the accumulator register. __ Drop(count); __ Move(result_register(), reg); - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL); codegen()->DoTest(this); } @@ -577,7 +591,7 @@ void FullCodeGenerator::StackValueContext::Plug(bool flag) const { void FullCodeGenerator::TestContext::Plug(bool flag) const { - codegen()->PrepareForBailoutBeforeSplit(TOS_REG, + codegen()->PrepareForBailoutBeforeSplit(condition(), true, true_label_, false_label_); @@ -670,15 +684,17 @@ void FullCodeGenerator::SetVar(Variable* var, __ sw(src, location); // Emit the write barrier code if the location is in the heap. if (var->IsContextSlot()) { - __ RecordWrite(scratch0, - Operand(Context::SlotOffset(var->index())), - scratch1, - src); + __ RecordWriteContextSlot(scratch0, + location.offset(), + src, + scratch1, + kRAHasBeenSaved, + kDontSaveFPRegs); } } -void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, +void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr, bool should_normalize, Label* if_true, Label* if_false) { @@ -689,13 +705,7 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, Label skip; if (should_normalize) __ Branch(&skip); - - ForwardBailoutStack* current = forward_bailout_stack_; - while (current != NULL) { - PrepareForBailout(current->expr(), state); - current = current->parent(); - } - + PrepareForBailout(expr, TOS_REG); if (should_normalize) { __ LoadRoot(t0, Heap::kTrueValueRootIndex); Split(eq, a0, Operand(t0), if_true, if_false, NULL); @@ -705,13 +715,15 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, - Variable::Mode mode, + VariableMode mode, FunctionLiteral* function, int* global_count) { // If it was not possible to allocate the variable at compile time, we // need to "declare" it at runtime to make sure it actually exists in the // local context. Variable* variable = proxy->var(); + bool binding_needs_init = (function == NULL) && + (mode == CONST || mode == CONST_HARMONY || mode == LET); switch (variable->location()) { case Variable::UNALLOCATED: ++(*global_count); @@ -723,7 +735,7 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); __ sw(result_register(), StackOperand(variable)); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { Comment cmnt(masm_, "[ Declaration"); __ LoadRoot(t0, Heap::kTheHoleValueRootIndex); __ sw(t0, StackOperand(variable)); @@ -750,10 +762,16 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, __ sw(result_register(), ContextOperand(cp, variable->index())); int offset = Context::SlotOffset(variable->index()); // We know that we have written a function, which is not a smi. - __ mov(a1, cp); - __ RecordWrite(a1, Operand(offset), a2, result_register()); + __ RecordWriteContextSlot(cp, + offset, + result_register(), + a2, + kRAHasBeenSaved, + kDontSaveFPRegs, + EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); PrepareForBailoutForId(proxy->id(), NO_REGISTERS); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { Comment cmnt(masm_, "[ Declaration"); __ LoadRoot(at, Heap::kTheHoleValueRootIndex); __ sw(at, ContextOperand(cp, variable->index())); @@ -765,11 +783,13 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, case Variable::LOOKUP: { Comment cmnt(masm_, "[ Declaration"); __ li(a2, Operand(variable->name())); - // Declaration nodes are always introduced in one of three modes. - ASSERT(mode == Variable::VAR || - mode == Variable::CONST || - mode == Variable::LET); - PropertyAttributes attr = (mode == Variable::CONST) ? READ_ONLY : NONE; + // Declaration nodes are always introduced in one of four modes. + ASSERT(mode == VAR || + mode == CONST || + mode == CONST_HARMONY || + mode == LET); + PropertyAttributes attr = (mode == CONST || mode == CONST_HARMONY) + ? READ_ONLY : NONE; __ li(a1, Operand(Smi::FromInt(attr))); // Push initial value, if any. // Note: For variables we must not push an initial value (such as @@ -779,7 +799,7 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, __ Push(cp, a2, a1); // Push initial value for function declaration. VisitForStackValue(function); - } else if (mode == Variable::CONST || mode == Variable::LET) { + } else if (binding_needs_init) { __ LoadRoot(a0, Heap::kTheHoleValueRootIndex); __ Push(cp, a2, a1, a0); } else { @@ -922,11 +942,17 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ bind(&done_convert); __ push(a0); + // Check for proxies. + Label call_runtime; + STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); + __ GetObjectType(a0, a1, a1); + __ Branch(&call_runtime, le, a1, Operand(LAST_JS_PROXY_TYPE)); + // Check cache validity in generated code. This is a fast case for // the JSObject::IsSimpleEnum cache validity checks. If we cannot // guarantee cache validity, call the runtime system to check cache // validity or get the property names in a fixed array. - Label next, call_runtime; + Label next; // Preload a couple of values used in the loop. Register empty_fixed_array_value = t2; __ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); @@ -1000,9 +1026,16 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ jmp(&loop); // We got a fixed array in register v0. Iterate through that. + Label non_proxy; __ bind(&fixed_array); - __ li(a1, Operand(Smi::FromInt(0))); // Map (0) - force slow check. - __ Push(a1, v0); + __ li(a1, Operand(Smi::FromInt(1))); // Smi indicates slow check + __ lw(a2, MemOperand(sp, 0 * kPointerSize)); // Get enumerated object + STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); + __ GetObjectType(a2, a3, a3); + __ Branch(&non_proxy, gt, a3, Operand(LAST_JS_PROXY_TYPE)); + __ li(a1, Operand(Smi::FromInt(0))); // Zero indicates proxy + __ bind(&non_proxy); + __ Push(a1, v0); // Smi and array __ lw(a1, FieldMemOperand(v0, FixedArray::kLengthOffset)); __ li(a0, Operand(Smi::FromInt(0))); __ Push(a1, a0); // Fixed array length (as smi) and initial index. @@ -1021,17 +1054,22 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ addu(t0, a2, t0); // Array base + scaled (smi) index. __ lw(a3, MemOperand(t0)); // Current entry. - // Get the expected map from the stack or a zero map in the + // Get the expected map from the stack or a smi in the // permanent slow case into register a2. __ lw(a2, MemOperand(sp, 3 * kPointerSize)); // Check if the expected map still matches that of the enumerable. - // If not, we have to filter the key. + // If not, we may have to filter the key. Label update_each; __ lw(a1, MemOperand(sp, 4 * kPointerSize)); __ lw(t0, FieldMemOperand(a1, HeapObject::kMapOffset)); __ Branch(&update_each, eq, t0, Operand(a2)); + // For proxies, no filtering is done. + // TODO(rossberg): What if only a prototype is a proxy? Not specified yet. + ASSERT_EQ(Smi::FromInt(0), 0); + __ Branch(&update_each, eq, a2, Operand(zero_reg)); + // Convert the entry to a string or (smi) 0 if it isn't a property // any more. If the property has been removed while iterating, we // just skip it. @@ -1086,7 +1124,7 @@ void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, !pretenure && scope()->is_function_scope() && info->num_literals() == 0) { - FastNewClosureStub stub(info->strict_mode() ? kStrictMode : kNonStrictMode); + FastNewClosureStub stub(info->language_mode()); __ li(a0, Operand(info)); __ push(a0); __ CallStub(&stub); @@ -1117,7 +1155,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, Scope* s = scope(); while (s != NULL) { if (s->num_heap_slots() > 0) { - if (s->calls_eval()) { + if (s->calls_non_strict_eval()) { // Check that extension is NULL. __ lw(temp, ContextOperand(current, Context::EXTENSION_INDEX)); __ Branch(slow, ne, temp, Operand(zero_reg)); @@ -1129,7 +1167,7 @@ void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, } // If no outer scope calls eval, we do not need to check more // context extensions. - if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break; + if (!s->outer_scope_calls_non_strict_eval() || s->is_eval_scope()) break; s = s->outer_scope(); } @@ -1171,7 +1209,7 @@ MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var, for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) { if (s->num_heap_slots() > 0) { - if (s->calls_eval()) { + if (s->calls_non_strict_eval()) { // Check that extension is NULL. __ lw(temp, ContextOperand(context, Context::EXTENSION_INDEX)); __ Branch(slow, ne, temp, Operand(zero_reg)); @@ -1201,17 +1239,26 @@ void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var, // introducing variables. In those cases, we do not want to // perform a runtime call for all variables in the scope // containing the eval. - if (var->mode() == Variable::DYNAMIC_GLOBAL) { + if (var->mode() == DYNAMIC_GLOBAL) { EmitLoadGlobalCheckExtensions(var, typeof_state, slow); __ Branch(done); - } else if (var->mode() == Variable::DYNAMIC_LOCAL) { + } else if (var->mode() == DYNAMIC_LOCAL) { Variable* local = var->local_if_not_shadowed(); __ lw(v0, ContextSlotOperandCheckExtensions(local, slow)); - if (local->mode() == Variable::CONST) { + if (local->mode() == CONST || + local->mode() == CONST_HARMONY || + local->mode() == LET) { __ LoadRoot(at, Heap::kTheHoleValueRootIndex); __ subu(at, v0, at); // Sub as compare: at == 0 on eq. - __ LoadRoot(a0, Heap::kUndefinedValueRootIndex); - __ movz(v0, a0, at); // Conditional move: return Undefined if TheHole. + if (local->mode() == CONST) { + __ LoadRoot(a0, Heap::kUndefinedValueRootIndex); + __ movz(v0, a0, at); // Conditional move: return Undefined if TheHole. + } else { // LET || CONST_HARMONY + __ Branch(done, ne, at, Operand(zero_reg)); + __ li(a0, Operand(var->name())); + __ push(a0); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + } } __ Branch(done); } @@ -1244,26 +1291,66 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { Comment cmnt(masm_, var->IsContextSlot() ? "Context variable" : "Stack variable"); - if (var->mode() != Variable::LET && var->mode() != Variable::CONST) { - context()->Plug(var); - } else { - // Let and const need a read barrier. - GetVar(v0, var); - __ LoadRoot(at, Heap::kTheHoleValueRootIndex); - __ subu(at, v0, at); // Sub as compare: at == 0 on eq. - if (var->mode() == Variable::LET) { - Label done; - __ Branch(&done, ne, at, Operand(zero_reg)); - __ li(a0, Operand(var->name())); - __ push(a0); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - __ bind(&done); + if (var->binding_needs_init()) { + // var->scope() may be NULL when the proxy is located in eval code and + // refers to a potential outside binding. Currently those bindings are + // always looked up dynamically, i.e. in that case + // var->location() == LOOKUP. + // always holds. + ASSERT(var->scope() != NULL); + + // Check if the binding really needs an initialization check. The check + // can be skipped in the following situation: we have a LET or CONST + // binding in harmony mode, both the Variable and the VariableProxy have + // the same declaration scope (i.e. they are both in global code, in the + // same function or in the same eval code) and the VariableProxy is in + // the source physically located after the initializer of the variable. + // + // We cannot skip any initialization checks for CONST in non-harmony + // mode because const variables may be declared but never initialized: + // if (false) { const x; }; var y = x; + // + // The condition on the declaration scopes is a conservative check for + // nested functions that access a binding and are called before the + // binding is initialized: + // function() { f(); let x = 1; function f() { x = 2; } } + // + bool skip_init_check; + if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) { + skip_init_check = false; } else { - __ LoadRoot(a0, Heap::kUndefinedValueRootIndex); - __ movz(v0, a0, at); // Conditional move: Undefined if TheHole. + // Check that we always have valid source position. + ASSERT(var->initializer_position() != RelocInfo::kNoPosition); + ASSERT(proxy->position() != RelocInfo::kNoPosition); + skip_init_check = var->mode() != CONST && + var->initializer_position() < proxy->position(); + } + + if (!skip_init_check) { + // Let and const need a read barrier. + GetVar(v0, var); + __ LoadRoot(at, Heap::kTheHoleValueRootIndex); + __ subu(at, v0, at); // Sub as compare: at == 0 on eq. + if (var->mode() == LET || var->mode() == CONST_HARMONY) { + // Throw a reference error when using an uninitialized let/const + // binding in harmony mode. + Label done; + __ Branch(&done, ne, at, Operand(zero_reg)); + __ li(a0, Operand(var->name())); + __ push(a0); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ bind(&done); + } else { + // Uninitalized const bindings outside of harmony mode are unholed. + ASSERT(var->mode() == CONST); + __ LoadRoot(a0, Heap::kUndefinedValueRootIndex); + __ movz(v0, a0, at); // Conditional move: Undefined if TheHole. + } + context()->Plug(v0); + break; } - context()->Plug(v0); } + context()->Plug(var); break; } @@ -1387,9 +1474,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ mov(a0, result_register()); __ li(a2, Operand(key->handle())); __ lw(a1, MemOperand(sp)); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, key->id()); PrepareForBailoutForId(key->id(), NO_REGISTERS); } else { @@ -1448,13 +1535,21 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { ZoneList<Expression*>* subexprs = expr->values(); int length = subexprs->length(); + + Handle<FixedArray> constant_elements = expr->constant_elements(); + ASSERT_EQ(2, constant_elements->length()); + ElementsKind constant_elements_kind = + static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value()); + Handle<FixedArrayBase> constant_elements_values( + FixedArrayBase::cast(constant_elements->get(1))); + __ mov(a0, result_register()); __ lw(a3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); __ lw(a3, FieldMemOperand(a3, JSFunction::kLiteralsOffset)); __ li(a2, Operand(Smi::FromInt(expr->literal_index()))); - __ li(a1, Operand(expr->constant_elements())); + __ li(a1, Operand(constant_elements)); __ Push(a3, a2, a1); - if (expr->constant_elements()->map() == + if (constant_elements_values->map() == isolate()->heap()->fixed_cow_array_map()) { FastCloneShallowArrayStub stub( FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length); @@ -1466,8 +1561,14 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) { __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); } else { - FastCloneShallowArrayStub stub( - FastCloneShallowArrayStub::CLONE_ELEMENTS, length); + ASSERT(constant_elements_kind == FAST_ELEMENTS || + constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || + FLAG_smi_only_arrays); + FastCloneShallowArrayStub::Mode mode = + constant_elements_kind == FAST_DOUBLE_ELEMENTS + ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS + : FastCloneShallowArrayStub::CLONE_ELEMENTS; + FastCloneShallowArrayStub stub(mode, length); __ CallStub(&stub); } @@ -1490,15 +1591,59 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } VisitForAccumulatorValue(subexpr); - // Store the subexpression value in the array's elements. - __ lw(a1, MemOperand(sp)); // Copy of array literal. - __ lw(a1, FieldMemOperand(a1, JSObject::kElementsOffset)); + __ lw(t6, MemOperand(sp)); // Copy of array literal. + __ lw(a1, FieldMemOperand(t6, JSObject::kElementsOffset)); + __ lw(a2, FieldMemOperand(t6, JSObject::kMapOffset)); int offset = FixedArray::kHeaderSize + (i * kPointerSize); + + Label element_done; + Label double_elements; + Label smi_element; + Label slow_elements; + Label fast_elements; + __ CheckFastElements(a2, a3, &double_elements); + + // FAST_SMI_ONLY_ELEMENTS or FAST_ELEMENTS + __ JumpIfSmi(result_register(), &smi_element); + __ CheckFastSmiOnlyElements(a2, a3, &fast_elements); + + // Store into the array literal requires a elements transition. Call into + // the runtime. + __ bind(&slow_elements); + __ push(t6); // Copy of array literal. + __ li(a1, Operand(Smi::FromInt(i))); + __ li(a2, Operand(Smi::FromInt(NONE))); // PropertyAttributes + StrictModeFlag strict_mode_flag = (language_mode() == CLASSIC_MODE) + ? kNonStrictMode : kStrictMode; + __ li(a3, Operand(Smi::FromInt(strict_mode_flag))); // Strict mode. + __ Push(a1, result_register(), a2, a3); + __ CallRuntime(Runtime::kSetProperty, 5); + __ Branch(&element_done); + + // Array literal has ElementsKind of FAST_DOUBLE_ELEMENTS. + __ bind(&double_elements); + __ li(a3, Operand(Smi::FromInt(i))); + __ StoreNumberToDoubleElements(result_register(), a3, t6, a1, t0, t1, t5, + t3, &slow_elements); + __ Branch(&element_done); + + // Array literal has ElementsKind of FAST_ELEMENTS and value is an object. + __ bind(&fast_elements); __ sw(result_register(), FieldMemOperand(a1, offset)); + // Update the write barrier for the array store. - // Update the write barrier for the array store with v0 as the scratch - // register. - __ RecordWrite(a1, Operand(offset), a2, result_register()); + __ RecordWriteField( + a1, offset, result_register(), a2, kRAHasBeenSaved, kDontSaveFPRegs, + EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); + __ Branch(&element_done); + + // Array literal has ElementsKind of FAST_SMI_ONLY_ELEMENTS or + // FAST_ELEMENTS, and value is Smi. + __ bind(&smi_element); + __ sw(result_register(), FieldMemOperand(a1, offset)); + // Fall through + + __ bind(&element_done); PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS); } @@ -1632,7 +1777,7 @@ void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { __ li(a2, Operand(key->handle())); // Call load IC. It has arguments receiver and property name a0 and a2. Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); - __ Call(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop)); + __ Call(ic, RelocInfo::CODE_TARGET, prop->id()); } @@ -1641,7 +1786,7 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { __ mov(a0, result_register()); // Call keyed load IC. It has arguments key and receiver in a0 and a1. Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize(); - __ Call(ic, RelocInfo::CODE_TARGET, GetPropertyId(prop)); + __ Call(ic, RelocInfo::CODE_TARGET, prop->id()); } @@ -1790,9 +1935,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ mov(a1, result_register()); __ pop(a0); // Restore value. __ li(a2, Operand(prop->key()->AsLiteral()->handle())); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic); break; } @@ -1803,9 +1948,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ mov(a1, result_register()); __ pop(a2); __ pop(a0); // Restore value. - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ Call(ic); break; } @@ -1822,9 +1967,9 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ mov(a0, result_register()); __ li(a2, Operand(var->name())); __ lw(a1, GlobalObjectOperand()); - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); } else if (op == Token::INIT_CONST) { @@ -1850,12 +1995,12 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); } - } else if (var->mode() == Variable::LET && op != Token::INIT_LET) { + } else if (var->mode() == LET && op != Token::INIT_LET) { // Non-initializing assignment to let variable needs a write barrier. if (var->IsLookupSlot()) { __ push(v0); // Value. __ li(a1, Operand(var->name())); - __ li(a0, Operand(Smi::FromInt(strict_mode_flag()))); + __ li(a0, Operand(Smi::FromInt(language_mode()))); __ Push(cp, a1, a0); // Context, name, strict mode. __ CallRuntime(Runtime::kStoreContextSlot, 4); } else { @@ -1875,12 +2020,14 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // RecordWrite may destroy all its register arguments. __ mov(a3, result_register()); int offset = Context::SlotOffset(var->index()); - __ RecordWrite(a1, Operand(offset), a2, a3); + __ RecordWriteContextSlot( + a1, offset, a3, a2, kRAHasBeenSaved, kDontSaveFPRegs); } } - } else if (var->mode() != Variable::CONST) { - // Assignment to var or initializing assignment to let. + } else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) { + // Assignment to var or initializing assignment to let/const + // in harmony mode. if (var->IsStackAllocated() || var->IsContextSlot()) { MemOperand location = VarOperand(var, a1); if (FLAG_debug_code && op == Token::INIT_LET) { @@ -1893,13 +2040,15 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ sw(v0, location); if (var->IsContextSlot()) { __ mov(a3, v0); - __ RecordWrite(a1, Operand(Context::SlotOffset(var->index())), a2, a3); + int offset = Context::SlotOffset(var->index()); + __ RecordWriteContextSlot( + a1, offset, a3, a2, kRAHasBeenSaved, kDontSaveFPRegs); } } else { ASSERT(var->IsLookupSlot()); __ push(v0); // Value. __ li(a1, Operand(var->name())); - __ li(a0, Operand(Smi::FromInt(strict_mode_flag()))); + __ li(a0, Operand(Smi::FromInt(language_mode()))); __ Push(cp, a1, a0); // Context, name, strict mode. __ CallRuntime(Runtime::kStoreContextSlot, 4); } @@ -1937,9 +2086,9 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { __ pop(a1); } - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); // If the assignment ends an initialization block, revert to fast case. @@ -1989,9 +2138,9 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { __ pop(a2); } - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); // If the assignment ends an initialization block, revert to fast case. @@ -2097,6 +2246,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { // Record source position for debugger. SetSourcePosition(expr->position()); CallFunctionStub stub(arg_count, flags); + __ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ CallStub(&stub); RecordJSReturnSite(expr); // Restore context register. @@ -2105,8 +2255,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { } -void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, - int arg_count) { +void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) { // Push copy of the first argument or undefined if it doesn't exist. if (arg_count > 0) { __ lw(a1, MemOperand(sp, arg_count * kPointerSize)); @@ -2115,22 +2264,20 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, } __ push(a1); - // Push the receiver of the enclosing function and do runtime call. + // Push the receiver of the enclosing function. int receiver_offset = 2 + info_->scope()->num_parameters(); __ lw(a1, MemOperand(fp, receiver_offset * kPointerSize)); __ push(a1); - // Push the strict mode flag. In harmony mode every eval call - // is a strict mode eval call. - StrictModeFlag strict_mode = strict_mode_flag(); - if (FLAG_harmony_block_scoping) { - strict_mode = kStrictMode; - } - __ li(a1, Operand(Smi::FromInt(strict_mode))); + // Push the language mode. + __ li(a1, Operand(Smi::FromInt(language_mode()))); __ push(a1); - __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP - ? Runtime::kResolvePossiblyDirectEvalNoLookup - : Runtime::kResolvePossiblyDirectEval, 4); + // Push the start position of the scope the calls resides in. + __ li(a1, Operand(Smi::FromInt(scope()->start_position()))); + __ push(a1); + + // Do the runtime call. + __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5); } @@ -2164,28 +2311,11 @@ void FullCodeGenerator::VisitCall(Call* expr) { VisitForStackValue(args->at(i)); } - // If we know that eval can only be shadowed by eval-introduced - // variables we attempt to load the global eval function directly - // in generated code. If we succeed, there is no need to perform a - // context lookup in the runtime system. - Label done; - Variable* var = proxy->var(); - if (!var->IsUnallocated() && var->mode() == Variable::DYNAMIC_GLOBAL) { - Label slow; - EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow); - // Push the function and resolve eval. - __ push(v0); - EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); - __ jmp(&done); - __ bind(&slow); - } - // Push a copy of the function (found below the arguments) and // resolve eval. __ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ push(a1); - EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); - __ bind(&done); + EmitResolvePossiblyDirectEval(arg_count); // The runtime call returns a pair of values in v0 (function) and // v1 (receiver). Touch up the stack with the right values. @@ -2195,6 +2325,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Record source position for debugger. SetSourcePosition(expr->position()); CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT); + __ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ CallStub(&stub); RecordJSReturnSite(expr); // Restore context register. @@ -2308,7 +2439,8 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { } -void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2320,7 +2452,7 @@ void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ And(t0, v0, Operand(kSmiTagMask)); Split(eq, t0, Operand(zero_reg), if_true, if_false, fall_through); @@ -2328,7 +2460,8 @@ void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsNonNegativeSmi(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2340,7 +2473,7 @@ void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ And(at, v0, Operand(kSmiTagMask | 0x80000000)); Split(eq, at, Operand(zero_reg), if_true, if_false, fall_through); @@ -2348,7 +2481,8 @@ void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2370,7 +2504,7 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { __ Branch(if_false, ne, at, Operand(zero_reg)); __ lbu(a1, FieldMemOperand(a2, Map::kInstanceTypeOffset)); __ Branch(if_false, lt, a1, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(le, a1, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE), if_true, if_false, fall_through); @@ -2378,7 +2512,8 @@ void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2392,7 +2527,7 @@ void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { __ JumpIfSmi(v0, if_false); __ GetObjectType(v0, a1, a1); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(ge, a1, Operand(FIRST_SPEC_OBJECT_TYPE), if_true, if_false, fall_through); @@ -2400,7 +2535,8 @@ void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsUndetectableObject(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2416,7 +2552,7 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { __ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset)); __ lbu(a1, FieldMemOperand(a1, Map::kBitFieldOffset)); __ And(at, a1, Operand(1 << Map::kIsUndetectable)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(ne, at, Operand(zero_reg), if_true, if_false, fall_through); context()->Plug(if_true, if_false); @@ -2424,8 +2560,8 @@ void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( - ZoneList<Expression*>* args) { - + CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2501,12 +2637,13 @@ void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf( __ sb(a2, FieldMemOperand(a1, Map::kBitField2Offset)); __ jmp(if_true); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2520,7 +2657,7 @@ void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { __ JumpIfSmi(v0, if_false); __ GetObjectType(v0, a1, a2); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); __ Branch(if_true, eq, a2, Operand(JS_FUNCTION_TYPE)); __ Branch(if_false); @@ -2528,7 +2665,8 @@ void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsArray(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2542,7 +2680,7 @@ void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { __ JumpIfSmi(v0, if_false); __ GetObjectType(v0, a1, a1); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, a1, Operand(JS_ARRAY_TYPE), if_true, if_false, fall_through); @@ -2550,7 +2688,8 @@ void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2564,15 +2703,15 @@ void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { __ JumpIfSmi(v0, if_false); __ GetObjectType(v0, a1, a1); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, a1, Operand(JS_REGEXP_TYPE), if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); +void FullCodeGenerator::EmitIsConstructCall(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label materialize_true, materialize_false; Label* if_true = NULL; @@ -2594,7 +2733,7 @@ void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { // Check the marker in the calling frame. __ bind(&check_frame_marker); __ lw(a1, MemOperand(a2, StandardFrameConstants::kMarkerOffset)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, a1, Operand(Smi::FromInt(StackFrame::CONSTRUCT)), if_true, if_false, fall_through); @@ -2602,7 +2741,8 @@ void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitObjectEquals(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); // Load the two objects into registers and perform the comparison. @@ -2617,14 +2757,15 @@ void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { &if_true, &if_false, &fall_through); __ pop(a1); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, v0, Operand(a1), if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitArguments(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); // ArgumentsAccessStub expects the key in a1 and the formal @@ -2638,9 +2779,8 @@ void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); - +void FullCodeGenerator::EmitArgumentsLength(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label exit; // Get the number of formal parameters. __ li(v0, Operand(Smi::FromInt(info_->scope()->num_parameters()))); @@ -2660,7 +2800,8 @@ void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitClassOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); Label done, null, function, non_function_constructor; @@ -2671,18 +2812,23 @@ void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { // Check that the object is a JS object but take special care of JS // functions to make sure they have 'Function' as their class. + // Assume that there are only two callable types, and one of them is at + // either end of the type range for JS object types. Saves extra comparisons. + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); __ GetObjectType(v0, v0, a1); // Map is now in v0. __ Branch(&null, lt, a1, Operand(FIRST_SPEC_OBJECT_TYPE)); - // As long as LAST_CALLABLE_SPEC_OBJECT_TYPE is the last instance type, and - // FIRST_CALLABLE_SPEC_OBJECT_TYPE comes right after - // LAST_NONCALLABLE_SPEC_OBJECT_TYPE, we can avoid checking for the latter. - STATIC_ASSERT(LAST_TYPE == LAST_CALLABLE_SPEC_OBJECT_TYPE); - STATIC_ASSERT(FIRST_CALLABLE_SPEC_OBJECT_TYPE == - LAST_NONCALLABLE_SPEC_OBJECT_TYPE + 1); - __ Branch(&function, ge, a1, Operand(FIRST_CALLABLE_SPEC_OBJECT_TYPE)); + STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE == + FIRST_SPEC_OBJECT_TYPE + 1); + __ Branch(&function, eq, a1, Operand(FIRST_SPEC_OBJECT_TYPE)); + + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == + LAST_SPEC_OBJECT_TYPE - 1); + __ Branch(&function, eq, a1, Operand(LAST_SPEC_OBJECT_TYPE)); + // Assume that there is no larger type. + STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == LAST_TYPE - 1); - // Check if the constructor in the map is a function. + // Check if the constructor in the map is a JS function. __ lw(v0, FieldMemOperand(v0, Map::kConstructorOffset)); __ GetObjectType(v0, a1, a1); __ Branch(&non_function_constructor, ne, a1, Operand(JS_FUNCTION_TYPE)); @@ -2714,7 +2860,7 @@ void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitLog(CallRuntime* expr) { // Conditionally generate a log call. // Args: // 0 (literal string): The type of logging (corresponds to the flags). @@ -2722,6 +2868,7 @@ void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { // 1 (string): Format string. Access the string at argument index 2 // with '%2s' (see Logger::LogRuntime for all the formats). // 2 (array): Arguments to the format string. + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(args->length(), 3); if (CodeGenerator::ShouldGenerateLog(args->at(0))) { VisitForStackValue(args->at(1)); @@ -2735,9 +2882,8 @@ void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { - ASSERT(args->length() == 0); - +void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) { + ASSERT(expr->arguments()->length() == 0); Label slow_allocate_heapnumber; Label heapnumber_allocated; @@ -2760,10 +2906,10 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). if (CpuFeatures::IsSupported(FPU)) { __ PrepareCallCFunction(1, a0); - __ li(a0, Operand(ExternalReference::isolate_address())); + __ lw(a0, ContextOperand(cp, Context::GLOBAL_INDEX)); + __ lw(a0, FieldMemOperand(a0, GlobalObject::kGlobalContextOffset)); __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1); - CpuFeatures::Scope scope(FPU); // 0x41300000 is the top half of 1.0 x 2^20 as a double. __ li(a1, Operand(0x41300000)); @@ -2778,7 +2924,8 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { } else { __ PrepareCallCFunction(2, a0); __ mov(a0, s0); - __ li(a1, Operand(ExternalReference::isolate_address())); + __ lw(a1, ContextOperand(cp, Context::GLOBAL_INDEX)); + __ lw(a1, FieldMemOperand(a1, GlobalObject::kGlobalContextOffset)); __ CallCFunction( ExternalReference::fill_heap_number_with_random_function(isolate()), 2); } @@ -2787,9 +2934,10 @@ void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSubString(CallRuntime* expr) { // Load the arguments on the stack and call the stub. SubStringStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -2799,9 +2947,10 @@ void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitRegExpExec(CallRuntime* expr) { // Load the arguments on the stack and call the stub. RegExpExecStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 4); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -2812,7 +2961,8 @@ void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitValueOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); // Load the object. @@ -2831,8 +2981,9 @@ void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathPow(CallRuntime* expr) { // Load the arguments on the stack and call the runtime function. + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -2842,7 +2993,8 @@ void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSetValueOf(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); // Load the object. @@ -2861,14 +3013,17 @@ void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { __ sw(v0, FieldMemOperand(a1, JSValue::kValueOffset)); // Update the write barrier. Save the value as it will be // overwritten by the write barrier code and is needed afterward. - __ RecordWrite(a1, Operand(JSValue::kValueOffset - kHeapObjectTag), a2, a3); + __ mov(a2, v0); + __ RecordWriteField( + a1, JSValue::kValueOffset, a2, a3, kRAHasBeenSaved, kDontSaveFPRegs); __ bind(&done); context()->Plug(v0); } -void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(args->length(), 1); // Load the argument on the stack and call the stub. @@ -2880,7 +3035,8 @@ void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -2898,7 +3054,8 @@ void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); @@ -2907,7 +3064,6 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { Register object = a1; Register index = a0; - Register scratch = a2; Register result = v0; __ pop(object); @@ -2917,7 +3073,6 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { Label done; StringCharCodeAtGenerator generator(object, index, - scratch, result, &need_conversion, &need_conversion, @@ -2946,7 +3101,8 @@ void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); @@ -2955,8 +3111,7 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { Register object = a1; Register index = a0; - Register scratch1 = a2; - Register scratch2 = a3; + Register scratch = a3; Register result = v0; __ pop(object); @@ -2966,8 +3121,7 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { Label done; StringCharAtGenerator generator(object, index, - scratch1, - scratch2, + scratch, result, &need_conversion, &need_conversion, @@ -2996,9 +3150,9 @@ void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); - VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -3008,7 +3162,8 @@ void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); VisitForStackValue(args->at(0)); @@ -3020,10 +3175,11 @@ void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathSin(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::SIN, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos. @@ -3032,10 +3188,24 @@ void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathCos(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::COS, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); + ASSERT(args->length() == 1); + VisitForStackValue(args->at(0)); + __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos. + __ CallStub(&stub); + context()->Plug(v0); +} + + +void FullCodeGenerator::EmitMathTan(CallRuntime* expr) { + // Load the argument on the stack and call the stub. + TranscendentalCacheStub stub(TranscendentalCache::TAN, + TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos. @@ -3044,10 +3214,11 @@ void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathLog(CallRuntime* expr) { // Load the argument on the stack and call the stub. TranscendentalCacheStub stub(TranscendentalCache::LOG, TranscendentalCacheStub::TAGGED); + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ mov(a0, result_register()); // Stub requires parameter in a0 and on tos. @@ -3056,8 +3227,9 @@ void FullCodeGenerator::EmitMathLog(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitMathSqrt(CallRuntime* expr) { // Load the argument on the stack and call the runtime function. + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForStackValue(args->at(0)); __ CallRuntime(Runtime::kMath_sqrt, 1); @@ -3065,7 +3237,8 @@ void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() >= 2); int arg_count = args->length() - 2; // 2 ~ receiver and function. @@ -3074,18 +3247,31 @@ void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { } VisitForAccumulatorValue(args->last()); // Function. + // Check for proxy. + Label proxy, done; + __ GetObjectType(v0, a1, a1); + __ Branch(&proxy, eq, a1, Operand(JS_FUNCTION_PROXY_TYPE)); + // InvokeFunction requires the function in a1. Move it in there. __ mov(a1, result_register()); ParameterCount count(arg_count); __ InvokeFunction(a1, count, CALL_FUNCTION, NullCallWrapper(), CALL_AS_METHOD); __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + __ jmp(&done); + + __ bind(&proxy); + __ push(v0); + __ CallRuntime(Runtime::kCall, args->length()); + __ bind(&done); + context()->Plug(v0); } -void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitRegExpConstructResult(CallRuntime* expr) { RegExpConstructResultStub stub; + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -3095,7 +3281,8 @@ void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitSwapElements(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 3); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); @@ -3154,16 +3341,31 @@ void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { __ sw(scratch1, MemOperand(index2, 0)); __ sw(scratch2, MemOperand(index1, 0)); - Label new_space; - __ InNewSpace(elements, scratch1, eq, &new_space); + Label no_remembered_set; + __ CheckPageFlag(elements, + scratch1, + 1 << MemoryChunk::SCAN_ON_SCAVENGE, + ne, + &no_remembered_set); // Possible optimization: do a check that both values are Smis // (or them and test against Smi mask). - __ mov(scratch1, elements); - __ RecordWriteHelper(elements, index1, scratch2); - __ RecordWriteHelper(scratch1, index2, scratch2); // scratch1 holds elements. + // We are swapping two objects in an array and the incremental marker never + // pauses in the middle of scanning a single object. Therefore the + // incremental marker is not disturbed, so we don't need to call the + // RecordWrite stub that notifies the incremental marker. + __ RememberedSetHelper(elements, + index1, + scratch2, + kDontSaveFPRegs, + MacroAssembler::kFallThroughAtEnd); + __ RememberedSetHelper(elements, + index2, + scratch2, + kDontSaveFPRegs, + MacroAssembler::kFallThroughAtEnd); - __ bind(&new_space); + __ bind(&no_remembered_set); // We are done. Drop elements from the stack, and return undefined. __ Drop(3); __ LoadRoot(v0, Heap::kUndefinedValueRootIndex); @@ -3177,7 +3379,8 @@ void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); ASSERT_NE(NULL, args->at(0)->AsLiteral()); @@ -3230,7 +3433,8 @@ void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitIsRegExpEquivalent(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT_EQ(2, args->length()); Register right = v0; @@ -3246,8 +3450,7 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { __ Branch(&ok, eq, left, Operand(right)); // Fail if either is a non-HeapObject. __ And(tmp, left, Operand(right)); - __ And(at, tmp, Operand(kSmiTagMask)); - __ Branch(&fail, eq, at, Operand(zero_reg)); + __ JumpIfSmi(tmp, &fail); __ lw(tmp, FieldMemOperand(left, HeapObject::kMapOffset)); __ lbu(tmp2, FieldMemOperand(tmp, Map::kInstanceTypeOffset)); __ Branch(&fail, ne, tmp2, Operand(JS_REGEXP_TYPE)); @@ -3267,7 +3470,8 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); VisitForAccumulatorValue(args->at(0)); Label materialize_true, materialize_false; @@ -3280,14 +3484,15 @@ void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) { __ lw(a0, FieldMemOperand(v0, String::kHashFieldOffset)); __ And(a0, a0, Operand(String::kContainsCachedArrayIndexMask)); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(eq, a0, Operand(zero_reg), if_true, if_false, fall_through); context()->Plug(if_true, if_false); } -void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) { + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 1); VisitForAccumulatorValue(args->at(0)); @@ -3302,12 +3507,12 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { } -void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { +void FullCodeGenerator::EmitFastAsciiArrayJoin(CallRuntime* expr) { Label bailout, done, one_char_separator, long_separator, non_trivial_array, not_size_one_array, loop, empty_separator_loop, one_char_separator_loop, one_char_separator_loop_entry, long_separator_loop; - + ZoneList<Expression*>* args = expr->arguments(); ASSERT(args->length() == 2); VisitForStackValue(args->at(1)); VisitForAccumulatorValue(args->at(0)); @@ -3592,7 +3797,9 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { if (property != NULL) { VisitForStackValue(property->obj()); VisitForStackValue(property->key()); - __ li(a1, Operand(Smi::FromInt(strict_mode_flag()))); + StrictModeFlag strict_mode_flag = (language_mode() == CLASSIC_MODE) + ? kNonStrictMode : kStrictMode; + __ li(a1, Operand(Smi::FromInt(strict_mode_flag))); __ push(a1); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); context()->Plug(v0); @@ -3600,7 +3807,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { Variable* var = proxy->var(); // Delete of an unqualified identifier is disallowed in strict mode // but "delete this" is allowed. - ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this()); + ASSERT(language_mode() == CLASSIC_MODE || var->is_this()); if (var->IsUnallocated()) { __ lw(a2, GlobalObjectOperand()); __ li(a1, Operand(var->name())); @@ -3643,18 +3850,35 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { // Unary NOT has no side effects so it's only necessary to visit the // subexpression. Match the optimizing compiler by not branching. VisitForEffect(expr->expression()); + } else if (context()->IsTest()) { + const TestContext* test = TestContext::cast(context()); + // The labels are swapped for the recursive call. + VisitForControl(expr->expression(), + test->false_label(), + test->true_label(), + test->fall_through()); + context()->Plug(test->true_label(), test->false_label()); } else { - Label materialize_true, materialize_false; - Label* if_true = NULL; - Label* if_false = NULL; - Label* fall_through = NULL; - - // Notice that the labels are swapped. - context()->PrepareTest(&materialize_true, &materialize_false, - &if_false, &if_true, &fall_through); - if (context()->IsTest()) ForwardBailoutToChild(expr); - VisitForControl(expr->expression(), if_true, if_false, fall_through); - context()->Plug(if_false, if_true); // Labels swapped. + // We handle value contexts explicitly rather than simply visiting + // for control and plugging the control flow into the context, + // because we need to prepare a pair of extra administrative AST ids + // for the optimizing compiler. + ASSERT(context()->IsAccumulatorValue() || context()->IsStackValue()); + Label materialize_true, materialize_false, done; + VisitForControl(expr->expression(), + &materialize_false, + &materialize_true, + &materialize_true); + __ bind(&materialize_true); + PrepareForBailoutForId(expr->MaterializeTrueId(), NO_REGISTERS); + __ LoadRoot(v0, Heap::kTrueValueRootIndex); + if (context()->IsStackValue()) __ push(v0); + __ jmp(&done); + __ bind(&materialize_false); + PrepareForBailoutForId(expr->MaterializeFalseId(), NO_REGISTERS); + __ LoadRoot(v0, Heap::kFalseValueRootIndex); + if (context()->IsStackValue()) __ push(v0); + __ bind(&done); } break; } @@ -3849,9 +4073,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ mov(a0, result_register()); // Value. __ li(a2, Operand(prop->key()->AsLiteral()->handle())); // Name. __ pop(a1); // Receiver. - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->StoreIC_Initialize_Strict() - : isolate()->builtins()->StoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->StoreIC_Initialize() + : isolate()->builtins()->StoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -3867,9 +4091,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { __ mov(a0, result_register()); // Value. __ pop(a1); // Key. __ pop(a2); // Receiver. - Handle<Code> ic = is_strict_mode() - ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict() - : isolate()->builtins()->KeyedStoreIC_Initialize(); + Handle<Code> ic = is_classic_mode() + ? isolate()->builtins()->KeyedStoreIC_Initialize() + : isolate()->builtins()->KeyedStoreIC_Initialize_Strict(); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -3916,19 +4140,24 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { context()->Plug(v0); } else { // This expression cannot throw a reference error at the top level. - VisitInCurrentContext(expr); + VisitInDuplicateContext(expr); } } void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, - Handle<String> check, - Label* if_true, - Label* if_false, - Label* fall_through) { + Expression* sub_expr, + Handle<String> check) { + Label materialize_true, materialize_false; + Label* if_true = NULL; + Label* if_false = NULL; + Label* fall_through = NULL; + context()->PrepareTest(&materialize_true, &materialize_false, + &if_true, &if_false, &fall_through); + { AccumulatorValueContext context(this); - VisitForTypeofValue(expr); + VisitForTypeofValue(sub_expr); } - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); if (check->Equals(isolate()->heap()->number_symbol())) { __ JumpIfSmi(v0, if_true); @@ -3964,10 +4193,11 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, Split(ne, a1, Operand(zero_reg), if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->function_symbol())) { __ JumpIfSmi(v0, if_false); - __ GetObjectType(v0, a1, v0); // Leave map in a1. - Split(ge, v0, Operand(FIRST_CALLABLE_SPEC_OBJECT_TYPE), - if_true, if_false, fall_through); - + STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2); + __ GetObjectType(v0, v0, a1); + __ Branch(if_true, eq, a1, Operand(JS_FUNCTION_TYPE)); + Split(eq, a1, Operand(JS_FUNCTION_PROXY_TYPE), + if_true, if_false, fall_through); } else if (check->Equals(isolate()->heap()->object_symbol())) { __ JumpIfSmi(v0, if_false); if (!FLAG_harmony_typeof) { @@ -3986,18 +4216,7 @@ void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr, } else { if (if_false != fall_through) __ jmp(if_false); } -} - - -void FullCodeGenerator::EmitLiteralCompareUndefined(Expression* expr, - Label* if_true, - Label* if_false, - Label* fall_through) { - VisitForAccumulatorValue(expr); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); - - __ LoadRoot(at, Heap::kUndefinedValueRootIndex); - Split(eq, v0, Operand(at), if_true, if_false, fall_through); + context()->Plug(if_true, if_false); } @@ -4005,9 +4224,12 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { Comment cmnt(masm_, "[ CompareOperation"); SetSourcePosition(expr->position()); + // First we try a fast inlined version of the compare when one of + // the operands is a literal. + if (TryLiteralCompare(expr)) return; + // Always perform the comparison for its control flow. Pack the result // into the expression's context after the comparison is performed. - Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; @@ -4015,20 +4237,13 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - // First we try a fast inlined version of the compare when one of - // the operands is a literal. - if (TryLiteralCompare(expr, if_true, if_false, fall_through)) { - context()->Plug(if_true, if_false); - return; - } - Token::Value op = expr->op(); VisitForStackValue(expr->left()); switch (op) { case Token::IN: VisitForStackValue(expr->right()); __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); - PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); + PrepareForBailoutBeforeSplit(expr, false, NULL, NULL); __ LoadRoot(t0, Heap::kTrueValueRootIndex); Split(eq, v0, Operand(t0), if_true, if_false, fall_through); break; @@ -4037,7 +4252,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { VisitForStackValue(expr->right()); InstanceofStub stub(InstanceofStub::kNoFlags); __ CallStub(&stub); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); // The stub returns 0 for true. Split(eq, v0, Operand(zero_reg), if_true, if_false, fall_through); break; @@ -4050,36 +4265,26 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { case Token::EQ_STRICT: case Token::EQ: cc = eq; - __ mov(a0, result_register()); - __ pop(a1); break; case Token::LT: cc = lt; - __ mov(a0, result_register()); - __ pop(a1); break; case Token::GT: - // Reverse left and right sides to obtain ECMA-262 conversion order. - cc = lt; - __ mov(a1, result_register()); - __ pop(a0); + cc = gt; break; case Token::LTE: - // Reverse left and right sides to obtain ECMA-262 conversion order. - cc = ge; - __ mov(a1, result_register()); - __ pop(a0); + cc = le; break; case Token::GTE: cc = ge; - __ mov(a0, result_register()); - __ pop(a1); break; case Token::IN: case Token::INSTANCEOF: default: UNREACHABLE(); } + __ mov(a0, result_register()); + __ pop(a1); bool inline_smi_code = ShouldInlineSmiCase(op); JumpPatchSite patch_site(masm_); @@ -4095,7 +4300,7 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { Handle<Code> ic = CompareIC::GetUninitialized(op); __ Call(ic, RelocInfo::CODE_TARGET, expr->id()); patch_site.EmitPatchInfo(); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); Split(cc, v0, Operand(zero_reg), if_true, if_false, fall_through); } } @@ -4106,8 +4311,9 @@ void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { } -void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) { - Comment cmnt(masm_, "[ CompareToNull"); +void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr, + Expression* sub_expr, + NilValue nil) { Label materialize_true, materialize_false; Label* if_true = NULL; Label* if_false = NULL; @@ -4115,18 +4321,23 @@ void FullCodeGenerator::VisitCompareToNull(CompareToNull* expr) { context()->PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false, &fall_through); - VisitForAccumulatorValue(expr->expression()); - PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false); + VisitForAccumulatorValue(sub_expr); + PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); + Heap::RootListIndex nil_value = nil == kNullValue ? + Heap::kNullValueRootIndex : + Heap::kUndefinedValueRootIndex; __ mov(a0, result_register()); - __ LoadRoot(a1, Heap::kNullValueRootIndex); - if (expr->is_strict()) { + __ LoadRoot(a1, nil_value); + if (expr->op() == Token::EQ_STRICT) { Split(eq, a0, Operand(a1), if_true, if_false, fall_through); } else { + Heap::RootListIndex other_nil_value = nil == kNullValue ? + Heap::kUndefinedValueRootIndex : + Heap::kNullValueRootIndex; __ Branch(if_true, eq, a0, Operand(a1)); - __ LoadRoot(a1, Heap::kUndefinedValueRootIndex); + __ LoadRoot(a1, other_nil_value); __ Branch(if_true, eq, a0, Operand(a1)); - __ And(at, a0, Operand(kSmiTagMask)); - __ Branch(if_false, eq, at, Operand(zero_reg)); + __ JumpIfSmi(a0, if_false); // It can be an undetectable object. __ lw(a1, FieldMemOperand(a0, HeapObject::kMapOffset)); __ lbu(a1, FieldMemOperand(a1, Map::kBitFieldOffset)); |