Version 2.4.3.
Made Date.parse properly handle TZ offsets (issue 857).
Performance improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@5447 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index f32da6d..0f8f6d4 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -493,7 +493,7 @@
int context_chain_length =
scope()->ContextChainLength(slot->var()->scope());
__ LoadContext(scratch, context_chain_length);
- return CodeGenerator::ContextOperand(scratch, slot->index());
+ return ContextOperand(scratch, slot->index());
}
case Slot::LOOKUP:
UNREACHABLE();
@@ -557,19 +557,17 @@
ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
if (FLAG_debug_code) {
// Check if we have the correct context pointer.
- __ ldr(r1,
- CodeGenerator::ContextOperand(cp, Context::FCONTEXT_INDEX));
+ __ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX));
__ cmp(r1, cp);
__ Check(eq, "Unexpected declaration in current context.");
}
if (mode == Variable::CONST) {
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
- __ str(ip, CodeGenerator::ContextOperand(cp, slot->index()));
+ __ str(ip, ContextOperand(cp, slot->index()));
// No write barrier since the_hole_value is in old space.
} else if (function != NULL) {
VisitForValue(function, kAccumulator);
- __ str(result_register(),
- CodeGenerator::ContextOperand(cp, slot->index()));
+ __ str(result_register(), ContextOperand(cp, slot->index()));
int offset = Context::SlotOffset(slot->index());
// We know that we have written a function, which is not a smi.
__ mov(r1, Operand(cp));
@@ -746,11 +744,10 @@
__ bind(&done_convert);
__ push(r0);
- // TODO(kasperl): 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.
+ // BUG(867): 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.
// Get the set of properties to enumerate.
__ push(r0); // Duplicate the enumerable object on the stack.
@@ -881,6 +878,150 @@
}
+MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(
+ Slot* slot,
+ Label* slow) {
+ ASSERT(slot->type() == Slot::CONTEXT);
+ Register current = cp;
+ Register next = r3;
+ Register temp = r4;
+
+ for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_eval()) {
+ // Check that extension is NULL.
+ __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX));
+ __ tst(temp, temp);
+ __ b(ne, slow);
+ }
+ __ ldr(next, ContextOperand(current, Context::CLOSURE_INDEX));
+ __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset));
+ // Walk the rest of the chain without clobbering cp.
+ current = next;
+ }
+ }
+ // Check that last extension is NULL.
+ __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX));
+ __ tst(temp, temp);
+ __ b(ne, slow);
+ __ ldr(temp, ContextOperand(current, Context::FCONTEXT_INDEX));
+ return ContextOperand(temp, slot->index());
+}
+
+
+void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase(
+ Slot* slot,
+ TypeofState typeof_state,
+ Label* slow,
+ Label* done) {
+ // Generate fast-case code for variables that might be shadowed by
+ // eval-introduced variables. Eval is used a lot without
+ // introducing variables. In those cases, we do not want to
+ // perform a runtime call for all variables in the scope
+ // containing the eval.
+ if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
+ EmitLoadGlobalSlotCheckExtensions(slot, typeof_state, slow);
+ __ jmp(done);
+ } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
+ Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
+ Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
+ if (potential_slot != NULL) {
+ // Generate fast case for locals that rewrite to slots.
+ __ ldr(r0, ContextSlotOperandCheckExtensions(potential_slot, slow));
+ if (potential_slot->var()->mode() == Variable::CONST) {
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(r0, ip);
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
+ }
+ __ jmp(done);
+ } else if (rewrite != NULL) {
+ // Generate fast case for calls of an argument function.
+ Property* property = rewrite->AsProperty();
+ if (property != NULL) {
+ VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+ Literal* key_literal = property->key()->AsLiteral();
+ if (obj_proxy != NULL &&
+ key_literal != NULL &&
+ obj_proxy->IsArguments() &&
+ key_literal->handle()->IsSmi()) {
+ // Load arguments object if there are no eval-introduced
+ // variables. Then load the argument from the arguments
+ // object using keyed load.
+ __ ldr(r1,
+ ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
+ slow));
+ __ mov(r0, Operand(key_literal->handle()));
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+ __ Call(ic, RelocInfo::CODE_TARGET);
+ __ jmp(done);
+ }
+ }
+ }
+ }
+}
+
+
+void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions(
+ Slot* slot,
+ TypeofState typeof_state,
+ Label* slow) {
+ Register current = cp;
+ Register next = r1;
+ Register temp = r2;
+
+ Scope* s = scope();
+ while (s != NULL) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_eval()) {
+ // Check that extension is NULL.
+ __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX));
+ __ tst(temp, temp);
+ __ b(ne, slow);
+ }
+ // Load next context in chain.
+ __ ldr(next, ContextOperand(current, Context::CLOSURE_INDEX));
+ __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset));
+ // Walk the rest of the chain without clobbering cp.
+ current = next;
+ }
+ // If no outer scope calls eval, we do not need to check more
+ // context extensions.
+ if (!s->outer_scope_calls_eval() || s->is_eval_scope()) break;
+ s = s->outer_scope();
+ }
+
+ if (s->is_eval_scope()) {
+ Label loop, fast;
+ if (!current.is(next)) {
+ __ Move(next, current);
+ }
+ __ bind(&loop);
+ // Terminate at global context.
+ __ ldr(temp, FieldMemOperand(next, HeapObject::kMapOffset));
+ __ LoadRoot(ip, Heap::kGlobalContextMapRootIndex);
+ __ cmp(temp, ip);
+ __ b(eq, &fast);
+ // Check that extension is NULL.
+ __ ldr(temp, ContextOperand(next, Context::EXTENSION_INDEX));
+ __ tst(temp, temp);
+ __ b(ne, slow);
+ // Load next context in chain.
+ __ ldr(next, ContextOperand(next, Context::CLOSURE_INDEX));
+ __ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset));
+ __ b(&loop);
+ __ bind(&fast);
+ }
+
+ __ ldr(r0, CodeGenerator::GlobalObject());
+ __ mov(r2, Operand(slot->var()->name()));
+ RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
+ ? RelocInfo::CODE_TARGET
+ : RelocInfo::CODE_TARGET_CONTEXT;
+ Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
+ __ Call(ic, mode);
+}
+
+
void FullCodeGenerator::EmitVariableLoad(Variable* var,
Expression::Context context) {
// Four cases: non-this global variables, lookup slots, all other
@@ -900,10 +1041,19 @@
Apply(context, r0);
} else if (slot != NULL && slot->type() == Slot::LOOKUP) {
+ Label done, slow;
+
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLoadFromSlotFastCase(slot, NOT_INSIDE_TYPEOF, &slow, &done);
+
+ __ bind(&slow);
Comment cmnt(masm_, "Lookup slot");
__ mov(r1, Operand(var->name()));
__ Push(cp, r1); // Context and name.
__ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ bind(&done);
+
Apply(context, r0);
} else if (slot != NULL) {
@@ -913,14 +1063,11 @@
if (var->mode() == Variable::CONST) {
// Constants may be the hole value if they have not been initialized.
// Unhole them.
- Label done;
MemOperand slot_operand = EmitSlotSearch(slot, r0);
__ ldr(r0, slot_operand);
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
__ cmp(r0, ip);
- __ b(ne, &done);
- __ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
- __ bind(&done);
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
Apply(context, r0);
} else {
Apply(context, slot);
@@ -1647,15 +1794,41 @@
EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT);
} else if (var != NULL && var->slot() != NULL &&
var->slot()->type() == Slot::LOOKUP) {
- // Call to a lookup slot (dynamically introduced variable). Call the
- // runtime to find the function to call (returned in eax) and the object
- // holding it (returned in edx).
+ // Call to a lookup slot (dynamically introduced variable).
+ Label slow, done;
+
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLoadFromSlotFastCase(var->slot(),
+ NOT_INSIDE_TYPEOF,
+ &slow,
+ &done);
+
+ __ bind(&slow);
+ // Call the runtime to find the function to call (returned in eax)
+ // and the object holding it (returned in edx).
__ push(context_register());
__ mov(r2, Operand(var->name()));
__ push(r2);
__ CallRuntime(Runtime::kLoadContextSlot, 2);
- __ push(r0); // Function.
- __ push(r1); // Receiver.
+ __ Push(r0, r1); // Function, receiver.
+
+ // If fast case code has been generated, emit code to push the
+ // function and receiver and have the slow path jump around this
+ // code.
+ if (done.is_linked()) {
+ Label call;
+ __ b(&call);
+ __ bind(&done);
+ // Push function.
+ __ push(r0);
+ // Push global receiver.
+ __ ldr(r1, CodeGenerator::GlobalObject());
+ __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
+ __ push(r1);
+ __ bind(&call);
+ }
+
EmitCallWithStub(expr);
} else if (fun->AsProperty() != NULL) {
// Call to an object property.
@@ -1678,12 +1851,9 @@
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
- // Push result (function).
- __ push(r0);
- // Push Global receiver.
__ ldr(r1, CodeGenerator::GlobalObject());
__ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
- __ push(r1);
+ __ Push(r0, r1); // Function, receiver.
EmitCallWithStub(expr);
} else {
EmitKeyedCallWithIC(expr, prop->key(), RelocInfo::CODE_TARGET);
@@ -2464,11 +2634,9 @@
Register key = r0;
Register cache = r1;
- __ ldr(cache, CodeGenerator::ContextOperand(cp, Context::GLOBAL_INDEX));
+ __ ldr(cache, ContextOperand(cp, Context::GLOBAL_INDEX));
__ ldr(cache, FieldMemOperand(cache, GlobalObject::kGlobalContextOffset));
- __ ldr(cache,
- CodeGenerator::ContextOperand(
- cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
+ __ ldr(cache, ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
__ ldr(cache,
FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
@@ -2929,9 +3097,19 @@
} else if (proxy != NULL &&
proxy->var()->slot() != NULL &&
proxy->var()->slot()->type() == Slot::LOOKUP) {
+ Label done, slow;
+
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ Slot* slot = proxy->var()->slot();
+ EmitDynamicLoadFromSlotFastCase(slot, INSIDE_TYPEOF, &slow, &done);
+
+ __ bind(&slow);
__ mov(r0, Operand(proxy->name()));
__ Push(cp, r0);
__ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
+ __ bind(&done);
+
if (where == kStack) __ push(r0);
} else {
// This expression cannot throw a reference error at the top level.
@@ -3187,7 +3365,7 @@
void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
- __ ldr(dst, CodeGenerator::ContextOperand(cp, context_index));
+ __ ldr(dst, ContextOperand(cp, context_index));
}