Index: src/arm/full-codegen-arm.cc |
=================================================================== |
--- src/arm/full-codegen-arm.cc (revision 4715) |
+++ src/arm/full-codegen-arm.cc (working copy) |
@@ -64,7 +64,7 @@ |
if (mode == PRIMARY) { |
int locals_count = scope()->num_stack_slots(); |
- __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit()); |
+ __ Push(lr, fp, cp, r1); |
if (locals_count > 0) { |
// Load undefined value here, so the value is ready for the loop |
// below. |
@@ -82,11 +82,17 @@ |
bool function_in_register = true; |
// Possibly allocate a local context. |
- if (scope()->num_heap_slots() > 0) { |
+ int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS; |
+ if (heap_slots > 0) { |
Comment cmnt(masm_, "[ Allocate local context"); |
// Argument to NewContext is the function, which is in r1. |
__ push(r1); |
- __ CallRuntime(Runtime::kNewContext, 1); |
+ if (heap_slots <= FastNewContextStub::kMaximumSlots) { |
+ FastNewContextStub stub(heap_slots); |
+ __ CallStub(&stub); |
+ } else { |
+ __ CallRuntime(Runtime::kNewContext, 1); |
+ } |
function_in_register = false; |
// Context is returned in both r0 and cp. It replaces the context |
// passed to us. It's saved in the stack and kept live in cp. |
@@ -144,6 +150,21 @@ |
} |
} |
+ { Comment cmnt(masm_, "[ Declarations"); |
+ // For named function expressions, declare the function name as a |
+ // constant. |
+ if (scope()->is_function_scope() && scope()->function() != NULL) { |
+ EmitDeclaration(scope()->function(), Variable::CONST, NULL); |
+ } |
+ // Visit all the explicit declarations unless there is an illegal |
+ // redeclaration. |
+ if (scope()->HasIllegalRedeclaration()) { |
+ scope()->VisitIllegalRedeclaration(this); |
+ } else { |
+ VisitDeclarations(scope()->declarations()); |
+ } |
+ } |
+ |
// Check the stack for overflow or break request. |
// Put the lr setup instruction in the delay slot. The kInstrSize is |
// added to the implicit 8 byte offset that always applies to operations |
@@ -160,10 +181,6 @@ |
lo); |
} |
- { Comment cmnt(masm_, "[ Declarations"); |
- VisitDeclarations(scope()->declarations()); |
- } |
- |
if (FLAG_trace) { |
__ CallRuntime(Runtime::kTraceEnter, 0); |
} |
@@ -384,7 +401,39 @@ |
} |
} |
+void FullCodeGenerator::PrepareTest(Label* materialize_true, |
+ Label* materialize_false, |
+ Label** if_true, |
+ Label** if_false) { |
+ switch (context_) { |
+ case Expression::kUninitialized: |
+ UNREACHABLE(); |
+ break; |
+ case Expression::kEffect: |
+ // In an effect context, the true and the false case branch to the |
+ // same label. |
+ *if_true = *if_false = materialize_true; |
+ break; |
+ case Expression::kValue: |
+ *if_true = materialize_true; |
+ *if_false = materialize_false; |
+ break; |
+ case Expression::kTest: |
+ *if_true = true_label_; |
+ *if_false = false_label_; |
+ break; |
+ case Expression::kValueTest: |
+ *if_true = materialize_true; |
+ *if_false = false_label_; |
+ break; |
+ case Expression::kTestValue: |
+ *if_true = true_label_; |
+ *if_false = materialize_false; |
+ break; |
+ } |
+} |
+ |
void FullCodeGenerator::Apply(Expression::Context context, |
Label* materialize_true, |
Label* materialize_false) { |
@@ -398,19 +447,25 @@ |
case Expression::kValue: { |
Label done; |
- __ bind(materialize_true); |
- __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); |
- __ jmp(&done); |
- __ bind(materialize_false); |
- __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); |
- __ bind(&done); |
switch (location_) { |
case kAccumulator: |
+ __ bind(materialize_true); |
+ __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); |
+ __ jmp(&done); |
+ __ bind(materialize_false); |
+ __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); |
break; |
case kStack: |
- __ push(result_register()); |
+ __ bind(materialize_true); |
+ __ LoadRoot(ip, Heap::kTrueValueRootIndex); |
+ __ push(ip); |
+ __ jmp(&done); |
+ __ bind(materialize_false); |
+ __ LoadRoot(ip, Heap::kFalseValueRootIndex); |
+ __ push(ip); |
break; |
} |
+ __ bind(&done); |
break; |
} |
@@ -419,12 +474,13 @@ |
case Expression::kValueTest: |
__ bind(materialize_true); |
- __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); |
switch (location_) { |
case kAccumulator: |
+ __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); |
break; |
case kStack: |
- __ push(result_register()); |
+ __ LoadRoot(ip, Heap::kTrueValueRootIndex); |
+ __ push(ip); |
break; |
} |
__ jmp(true_label_); |
@@ -432,12 +488,13 @@ |
case Expression::kTestValue: |
__ bind(materialize_false); |
- __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); |
switch (location_) { |
case kAccumulator: |
+ __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); |
break; |
case kStack: |
- __ push(result_register()); |
+ __ LoadRoot(ip, Heap::kFalseValueRootIndex); |
+ __ push(ip); |
break; |
} |
__ jmp(false_label_); |
@@ -446,6 +503,68 @@ |
} |
+// Convert constant control flow (true or false) to the result expected for |
+// a given expression context. |
+void FullCodeGenerator::Apply(Expression::Context context, bool flag) { |
+ switch (context) { |
+ case Expression::kUninitialized: |
+ UNREACHABLE(); |
+ break; |
+ case Expression::kEffect: |
+ break; |
+ case Expression::kValue: { |
+ Heap::RootListIndex value_root_index = |
+ flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex; |
+ switch (location_) { |
+ case kAccumulator: |
+ __ LoadRoot(result_register(), value_root_index); |
+ break; |
+ case kStack: |
+ __ LoadRoot(ip, value_root_index); |
+ __ push(ip); |
+ break; |
+ } |
+ break; |
+ } |
+ case Expression::kTest: |
+ __ b(flag ? true_label_ : false_label_); |
+ break; |
+ case Expression::kTestValue: |
+ switch (location_) { |
+ case kAccumulator: |
+ // If value is false it's needed. |
+ if (!flag) __ LoadRoot(result_register(), Heap::kFalseValueRootIndex); |
+ break; |
+ case kStack: |
+ // If value is false it's needed. |
+ if (!flag) { |
+ __ LoadRoot(ip, Heap::kFalseValueRootIndex); |
+ __ push(ip); |
+ } |
+ break; |
+ } |
+ __ b(flag ? true_label_ : false_label_); |
+ break; |
+ case Expression::kValueTest: |
+ switch (location_) { |
+ case kAccumulator: |
+ // If value is true it's needed. |
+ if (flag) __ LoadRoot(result_register(), Heap::kTrueValueRootIndex); |
+ break; |
+ case kStack: |
+ // If value is true it's needed. |
+ if (flag) { |
+ __ LoadRoot(ip, Heap::kTrueValueRootIndex); |
+ __ push(ip); |
+ } |
+ break; |
+ } |
+ __ b(flag ? true_label_ : false_label_); |
+ break; |
+ } |
+} |
+ |
+ |
void FullCodeGenerator::DoTest(Expression::Context context) { |
// The value to test is pushed on the stack, and duplicated on the stack |
// if necessary (for value/test and test/value contexts). |
@@ -551,22 +670,23 @@ |
} |
-void FullCodeGenerator::VisitDeclaration(Declaration* decl) { |
+void FullCodeGenerator::EmitDeclaration(Variable* variable, |
+ Variable::Mode mode, |
+ FunctionLiteral* function) { |
Comment cmnt(masm_, "[ Declaration"); |
- Variable* var = decl->proxy()->var(); |
- ASSERT(var != NULL); // Must have been resolved. |
- Slot* slot = var->slot(); |
- Property* prop = var->AsProperty(); |
+ ASSERT(variable != NULL); // Must have been resolved. |
+ Slot* slot = variable->slot(); |
+ Property* prop = variable->AsProperty(); |
if (slot != NULL) { |
switch (slot->type()) { |
case Slot::PARAMETER: |
case Slot::LOCAL: |
- if (decl->mode() == Variable::CONST) { |
+ if (mode == Variable::CONST) { |
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
__ str(ip, MemOperand(fp, SlotOffset(slot))); |
- } else if (decl->fun() != NULL) { |
- VisitForValue(decl->fun(), kAccumulator); |
+ } else if (function != NULL) { |
+ VisitForValue(function, kAccumulator); |
__ str(result_register(), MemOperand(fp, SlotOffset(slot))); |
} |
break; |
@@ -576,7 +696,7 @@ |
// this specific context. |
// The variable in the decl always resides in the current context. |
- ASSERT_EQ(0, scope()->ContextChainLength(var->scope())); |
+ ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); |
if (FLAG_debug_code) { |
// Check if we have the correct context pointer. |
__ ldr(r1, |
@@ -584,12 +704,12 @@ |
__ cmp(r1, cp); |
__ Check(eq, "Unexpected declaration in current context."); |
} |
- if (decl->mode() == Variable::CONST) { |
+ if (mode == Variable::CONST) { |
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
__ str(ip, CodeGenerator::ContextOperand(cp, slot->index())); |
// No write barrier since the_hole_value is in old space. |
- } else if (decl->fun() != NULL) { |
- VisitForValue(decl->fun(), kAccumulator); |
+ } else if (function != NULL) { |
+ VisitForValue(function, kAccumulator); |
__ str(result_register(), |
CodeGenerator::ContextOperand(cp, slot->index())); |
int offset = Context::SlotOffset(slot->index()); |
@@ -601,27 +721,27 @@ |
break; |
case Slot::LOOKUP: { |
- __ mov(r2, Operand(var->name())); |
+ __ mov(r2, Operand(variable->name())); |
// Declaration nodes are always introduced in one of two modes. |
- ASSERT(decl->mode() == Variable::VAR || |
- decl->mode() == Variable::CONST); |
+ ASSERT(mode == Variable::VAR || |
+ mode == Variable::CONST); |
PropertyAttributes attr = |
- (decl->mode() == Variable::VAR) ? NONE : READ_ONLY; |
+ (mode == Variable::VAR) ? NONE : READ_ONLY; |
__ mov(r1, Operand(Smi::FromInt(attr))); |
// Push initial value, if any. |
// Note: For variables we must not push an initial value (such as |
// 'undefined') because we may have a (legal) redeclaration and we |
// must not destroy the current value. |
- if (decl->mode() == Variable::CONST) { |
+ if (mode == Variable::CONST) { |
__ LoadRoot(r0, Heap::kTheHoleValueRootIndex); |
- __ stm(db_w, sp, cp.bit() | r2.bit() | r1.bit() | r0.bit()); |
- } else if (decl->fun() != NULL) { |
- __ stm(db_w, sp, cp.bit() | r2.bit() | r1.bit()); |
+ __ Push(cp, r2, r1, r0); |
+ } else if (function != NULL) { |
+ __ Push(cp, r2, r1); |
// Push initial value for function declaration. |
- VisitForValue(decl->fun(), kStack); |
+ VisitForValue(function, kStack); |
} else { |
__ mov(r0, Operand(Smi::FromInt(0))); // No initial value! |
- __ stm(db_w, sp, cp.bit() | r2.bit() | r1.bit() | r0.bit()); |
+ __ Push(cp, r2, r1, r0); |
} |
__ CallRuntime(Runtime::kDeclareContextSlot, 4); |
break; |
@@ -629,47 +749,257 @@ |
} |
} else if (prop != NULL) { |
- if (decl->fun() != NULL || decl->mode() == Variable::CONST) { |
+ if (function != NULL || mode == Variable::CONST) { |
// 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(decl->fun(), kAccumulator); |
+ if (function != NULL) { |
+ VisitForValue(prop->key(), kStack); |
+ VisitForValue(function, kAccumulator); |
+ __ pop(r1); // Key. |
} else { |
+ VisitForValue(prop->key(), kAccumulator); |
+ __ mov(r1, result_register()); // Key. |
__ LoadRoot(result_register(), Heap::kTheHoleValueRootIndex); |
} |
+ __ pop(r2); // Receiver. |
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); |
- __ pop(r1); // Key. |
- __ pop(r2); // Receiver. |
__ Call(ic, RelocInfo::CODE_TARGET); |
- |
// Value in r0 is ignored (declarations are statements). |
} |
} |
} |
+void FullCodeGenerator::VisitDeclaration(Declaration* decl) { |
+ EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); |
+} |
+ |
+ |
void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { |
// Call the runtime to declare the globals. |
// The context is the first argument. |
__ mov(r1, Operand(pairs)); |
__ mov(r0, Operand(Smi::FromInt(is_eval() ? 1 : 0))); |
- __ stm(db_w, sp, cp.bit() | r1.bit() | r0.bit()); |
+ __ Push(cp, r1, r0); |
__ CallRuntime(Runtime::kDeclareGlobals, 3); |
// Return value is ignored. |
} |
void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { |
- UNREACHABLE(); |
+ Comment cmnt(masm_, "[ SwitchStatement"); |
+ Breakable nested_statement(this, stmt); |
+ SetStatementPosition(stmt); |
+ // Keep the switch value on the stack until a case matches. |
+ VisitForValue(stmt->tag(), kStack); |
+ |
+ ZoneList<CaseClause*>* clauses = stmt->cases(); |
+ CaseClause* default_clause = NULL; // Can occur anywhere in the list. |
+ |
+ Label next_test; // Recycled for each test. |
+ // Compile all the tests with branches to their bodies. |
+ for (int i = 0; i < clauses->length(); i++) { |
+ CaseClause* clause = clauses->at(i); |
+ // The default is not a test, but remember it as final fall through. |
+ if (clause->is_default()) { |
+ default_clause = clause; |
+ continue; |
+ } |
+ |
+ Comment cmnt(masm_, "[ Case comparison"); |
+ __ bind(&next_test); |
+ next_test.Unuse(); |
+ |
+ // Compile the label expression. |
+ VisitForValue(clause->label(), kAccumulator); |
+ |
+ // Perform the comparison as if via '==='. The comparison stub expects |
+ // the smi vs. smi case to be handled before it is called. |
+ Label slow_case; |
+ __ ldr(r1, MemOperand(sp, 0)); // Switch value. |
+ __ mov(r2, r1); |
+ __ orr(r2, r2, r0); |
+ __ tst(r2, Operand(kSmiTagMask)); |
+ __ b(ne, &slow_case); |
+ __ cmp(r1, r0); |
+ __ b(ne, &next_test); |
+ __ Drop(1); // Switch value is no longer needed. |
+ __ b(clause->body_target()->entry_label()); |
+ |
+ __ bind(&slow_case); |
+ CompareStub stub(eq, true); |
+ __ CallStub(&stub); |
+ __ tst(r0, r0); |
+ __ b(ne, &next_test); |
+ __ Drop(1); // Switch value is no longer needed. |
+ __ b(clause->body_target()->entry_label()); |
+ } |
+ |
+ // Discard the test value and jump to the default if present, otherwise to |
+ // the end of the statement. |
+ __ bind(&next_test); |
+ __ Drop(1); // Switch value is no longer needed. |
+ if (default_clause == NULL) { |
+ __ b(nested_statement.break_target()); |
+ } else { |
+ __ b(default_clause->body_target()->entry_label()); |
+ } |
+ |
+ // Compile all the case bodies. |
+ for (int i = 0; i < clauses->length(); i++) { |
+ Comment cmnt(masm_, "[ Case body"); |
+ CaseClause* clause = clauses->at(i); |
+ __ bind(clause->body_target()->entry_label()); |
+ VisitStatements(clause->statements()); |
+ } |
+ |
+ __ bind(nested_statement.break_target()); |
} |
void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { |
- UNREACHABLE(); |
+ Comment cmnt(masm_, "[ ForInStatement"); |
+ SetStatementPosition(stmt); |
+ |
+ Label loop, exit; |
+ ForIn loop_statement(this, stmt); |
+ increment_loop_depth(); |
+ |
+ // Get the object to enumerate over. Both SpiderMonkey and JSC |
+ // ignore null and undefined in contrast to the specification; see |
+ // ECMA-262 section 12.6.4. |
+ VisitForValue(stmt->enumerable(), kAccumulator); |
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); |
+ __ cmp(r0, ip); |
+ __ b(eq, &exit); |
+ __ LoadRoot(ip, Heap::kNullValueRootIndex); |
+ __ cmp(r0, ip); |
+ __ b(eq, &exit); |
+ |
+ // Convert the object to a JS object. |
+ Label convert, done_convert; |
+ __ BranchOnSmi(r0, &convert); |
+ __ CompareObjectType(r0, r1, r1, FIRST_JS_OBJECT_TYPE); |
+ __ b(hs, &done_convert); |
+ __ bind(&convert); |
+ __ push(r0); |
+ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS); |
+ __ 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. |
+ |
+ // Get the set of properties to enumerate. |
+ __ push(r0); // Duplicate the enumerable object on the stack. |
+ __ CallRuntime(Runtime::kGetPropertyNamesFast, 1); |
+ |
+ // If we got a map from the runtime call, we can do a fast |
+ // modification check. Otherwise, we got a fixed array, and we have |
+ // to do a slow check. |
+ Label fixed_array; |
+ __ mov(r2, r0); |
+ __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); |
+ __ LoadRoot(ip, Heap::kMetaMapRootIndex); |
+ __ cmp(r1, ip); |
+ __ b(ne, &fixed_array); |
+ |
+ // We got a map in register r0. Get the enumeration cache from it. |
+ __ ldr(r1, FieldMemOperand(r0, Map::kInstanceDescriptorsOffset)); |
+ __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset)); |
+ __ ldr(r2, FieldMemOperand(r1, DescriptorArray::kEnumCacheBridgeCacheOffset)); |
+ |
+ // Setup the four remaining stack slots. |
+ __ push(r0); // Map. |
+ __ ldr(r1, FieldMemOperand(r2, FixedArray::kLengthOffset)); |
+ __ mov(r0, Operand(Smi::FromInt(0))); |
+ // Push enumeration cache, enumeration cache length (as smi) and zero. |
+ __ Push(r2, r1, r0); |
+ __ jmp(&loop); |
+ |
+ // We got a fixed array in register r0. Iterate through that. |
+ __ bind(&fixed_array); |
+ __ mov(r1, Operand(Smi::FromInt(0))); // Map (0) - force slow check. |
+ __ Push(r1, r0); |
+ __ ldr(r1, FieldMemOperand(r0, FixedArray::kLengthOffset)); |
+ __ mov(r0, Operand(Smi::FromInt(0))); |
+ __ Push(r1, r0); // Fixed array length (as smi) and initial index. |
+ |
+ // Generate code for doing the condition check. |
+ __ bind(&loop); |
+ // Load the current count to r0, load the length to r1. |
+ __ ldrd(r0, MemOperand(sp, 0 * kPointerSize)); |
+ __ cmp(r0, r1); // Compare to the array length. |
+ __ b(hs, loop_statement.break_target()); |
+ |
+ // Get the current entry of the array into register r3. |
+ __ ldr(r2, MemOperand(sp, 2 * kPointerSize)); |
+ __ add(r2, r2, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); |
+ __ ldr(r3, MemOperand(r2, r0, LSL, kPointerSizeLog2 - kSmiTagSize)); |
+ |
+ // Get the expected map from the stack or a zero map in the |
+ // permanent slow case into register r2. |
+ __ ldr(r2, MemOperand(sp, 3 * kPointerSize)); |
+ |
+ // Check if the expected map still matches that of the enumerable. |
+ // If not, we have to filter the key. |
+ Label update_each; |
+ __ ldr(r1, MemOperand(sp, 4 * kPointerSize)); |
+ __ ldr(r4, FieldMemOperand(r1, HeapObject::kMapOffset)); |
+ __ cmp(r4, Operand(r2)); |
+ __ b(eq, &update_each); |
+ |
+ // Convert the entry to a string or null if it isn't a property |
+ // anymore. If the property has been removed while iterating, we |
+ // just skip it. |
+ __ push(r1); // Enumerable. |
+ __ push(r3); // Current entry. |
+ __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_JS); |
+ __ mov(r3, Operand(r0)); |
+ __ LoadRoot(ip, Heap::kNullValueRootIndex); |
+ __ cmp(r3, ip); |
+ __ b(eq, loop_statement.continue_target()); |
+ |
+ // Update the 'each' property or variable from the possibly filtered |
+ // entry in register r3. |
+ __ bind(&update_each); |
+ __ mov(result_register(), r3); |
+ // Perform the assignment as if via '='. |
+ EmitAssignment(stmt->each()); |
+ |
+ // Generate code for the body of the loop. |
+ Label stack_limit_hit, stack_check_done; |
+ Visit(stmt->body()); |
+ |
+ __ StackLimitCheck(&stack_limit_hit); |
+ __ bind(&stack_check_done); |
+ |
+ // Generate code for the going to the next element by incrementing |
+ // the index (smi) stored on top of the stack. |
+ __ bind(loop_statement.continue_target()); |
+ __ pop(r0); |
+ __ add(r0, r0, Operand(Smi::FromInt(1))); |
+ __ push(r0); |
+ __ b(&loop); |
+ |
+ // Slow case for the stack limit check. |
+ StackCheckStub stack_check_stub; |
+ __ bind(&stack_limit_hit); |
+ __ CallStub(&stack_check_stub); |
+ __ b(&stack_check_done); |
+ |
+ // Remove the pointers stored on the stack. |
+ __ bind(loop_statement.break_target()); |
+ __ Drop(5); |
+ |
+ // Exit and decrement the loop depth. |
+ __ bind(&exit); |
+ decrement_loop_depth(); |
} |
@@ -683,7 +1013,7 @@ |
__ CallStub(&stub); |
} else { |
__ mov(r0, Operand(info)); |
- __ stm(db_w, sp, cp.bit() | r0.bit()); |
+ __ Push(cp, r0); |
__ CallRuntime(Runtime::kNewClosure, 2); |
} |
Apply(context_, r0); |
@@ -717,7 +1047,7 @@ |
} else if (slot != NULL && slot->type() == Slot::LOOKUP) { |
Comment cmnt(masm_, "Lookup slot"); |
__ mov(r1, Operand(var->name())); |
- __ stm(db_w, sp, cp.bit() | r1.bit()); // Context and name. |
+ __ Push(cp, r1); // Context and name. |
__ CallRuntime(Runtime::kLoadContextSlot, 2); |
Apply(context, r0); |
@@ -725,8 +1055,21 @@ |
Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) |
? "Context slot" |
: "Stack slot"); |
- Apply(context, slot); |
- |
+ 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); |
+ Apply(context, r0); |
+ } else { |
+ Apply(context, slot); |
+ } |
} else { |
Comment cmnt(masm_, "Rewritten parameter"); |
ASSERT_NOT_NULL(property); |
@@ -862,6 +1205,10 @@ |
void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { |
Comment cmnt(masm_, "[ ArrayLiteral"); |
+ |
+ ZoneList<Expression*>* subexprs = expr->values(); |
+ int length = subexprs->length(); |
+ |
__ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset)); |
__ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset)); |
__ mov(r2, Operand(Smi::FromInt(expr->literal_index()))); |
@@ -869,16 +1216,18 @@ |
__ Push(r3, r2, r1); |
if (expr->depth() > 1) { |
__ CallRuntime(Runtime::kCreateArrayLiteral, 3); |
+ } else if (length > FastCloneShallowArrayStub::kMaximumLength) { |
+ __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); |
} else { |
- __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); |
+ FastCloneShallowArrayStub stub(length); |
+ __ CallStub(&stub); |
} |
bool result_saved = false; // Is the result saved to the stack? |
// Emit code to evaluate all the non-constant subexpressions and to store |
// them into the newly cloned array. |
- ZoneList<Expression*>* subexprs = expr->values(); |
- for (int i = 0, len = subexprs->length(); i < len; i++) { |
+ for (int i = 0; i < length; i++) { |
Expression* subexpr = subexprs->at(i); |
// If the subexpression is a literal or a simple materialized literal it |
// is already set in the cloned array. |
@@ -1041,6 +1390,56 @@ |
} |
+void FullCodeGenerator::EmitAssignment(Expression* expr) { |
+ // Invalid left-hand sides are rewritten to have a 'throw |
+ // ReferenceError' on the left-hand side. |
+ if (!expr->IsValidLeftHandSide()) { |
+ VisitForEffect(expr); |
+ return; |
+ } |
+ |
+ // Left-hand side can only be a property, a global or a (parameter or local) |
+ // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. |
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; |
+ LhsKind assign_type = VARIABLE; |
+ Property* prop = expr->AsProperty(); |
+ if (prop != NULL) { |
+ assign_type = (prop->key()->IsPropertyName()) |
+ ? NAMED_PROPERTY |
+ : KEYED_PROPERTY; |
+ } |
+ |
+ switch (assign_type) { |
+ case VARIABLE: { |
+ Variable* var = expr->AsVariableProxy()->var(); |
+ EmitVariableAssignment(var, Token::ASSIGN, Expression::kEffect); |
+ break; |
+ } |
+ case NAMED_PROPERTY: { |
+ __ push(r0); // Preserve value. |
+ VisitForValue(prop->obj(), kAccumulator); |
+ __ mov(r1, r0); |
+ __ pop(r0); // Restore value. |
+ __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); |
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); |
+ __ Call(ic, RelocInfo::CODE_TARGET); |
+ break; |
+ } |
+ case KEYED_PROPERTY: { |
+ __ push(r0); // Preserve value. |
+ VisitForValue(prop->obj(), kStack); |
+ VisitForValue(prop->key(), kAccumulator); |
+ __ mov(r1, r0); |
+ __ pop(r2); |
+ __ pop(r0); // Restore value. |
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); |
+ __ Call(ic, RelocInfo::CODE_TARGET); |
+ break; |
+ } |
+ } |
+} |
+ |
+ |
void FullCodeGenerator::EmitVariableAssignment(Variable* var, |
Token::Value op, |
Expression::Context context) { |
@@ -1082,9 +1481,9 @@ |
MemOperand target = EmitSlotSearch(slot, r1); |
if (op == Token::INIT_CONST) { |
// Detect const reinitialization by checking for the hole value. |
- __ ldr(r1, target); |
+ __ ldr(r2, target); |
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
- __ cmp(r1, ip); |
+ __ cmp(r2, ip); |
__ b(ne, &done); |
} |
// Perform the assignment and issue the write barrier. |
@@ -1256,7 +1655,8 @@ |
} |
// Record source position for debugger. |
SetSourcePosition(expr->position()); |
- CallFunctionStub stub(arg_count, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE); |
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; |
+ CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); |
__ CallStub(&stub); |
// Restore context register. |
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); |
@@ -1270,8 +1670,51 @@ |
Variable* var = fun->AsVariableProxy()->AsVariable(); |
if (var != NULL && var->is_possibly_eval()) { |
- // Call to the identifier 'eval'. |
- UNREACHABLE(); |
+ // In a call to eval, we first call %ResolvePossiblyDirectEval to |
+ // resolve the function we need to call and the receiver of the |
+ // call. Then we call the resolved function using the given |
+ // arguments. |
+ VisitForValue(fun, kStack); |
+ __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); |
+ __ push(r2); // Reserved receiver slot. |
+ |
+ // Push the arguments. |
+ ZoneList<Expression*>* args = expr->arguments(); |
+ int arg_count = args->length(); |
+ for (int i = 0; i < arg_count; i++) { |
+ VisitForValue(args->at(i), kStack); |
+ } |
+ |
+ // Push copy of the function - found below the arguments. |
+ __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); |
+ __ push(r1); |
+ |
+ // Push copy of the first argument or undefined if it doesn't exist. |
+ if (arg_count > 0) { |
+ __ ldr(r1, MemOperand(sp, arg_count * kPointerSize)); |
+ __ push(r1); |
+ } else { |
+ __ push(r2); |
+ } |
+ |
+ // Push the receiver of the enclosing function and do runtime call. |
+ __ ldr(r1, MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize)); |
+ __ push(r1); |
+ __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3); |
+ |
+ // The runtime call returns a pair of values in r0 (function) and |
+ // r1 (receiver). Touch up the stack with the right values. |
+ __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize)); |
+ __ str(r1, MemOperand(sp, arg_count * kPointerSize)); |
+ |
+ // Record source position for debugger. |
+ SetSourcePosition(expr->position()); |
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; |
+ CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); |
+ __ CallStub(&stub); |
+ // Restore context register. |
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); |
+ DropAndApply(1, context_, r0); |
} else if (var != NULL && !var->is_this() && var->is_global()) { |
// Push global object as receiver for the call IC. |
__ ldr(r0, CodeGenerator::GlobalObject()); |
@@ -1279,8 +1722,16 @@ |
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. |
- UNREACHABLE(); |
+ // 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). |
+ __ push(context_register()); |
+ __ mov(r2, Operand(var->name())); |
+ __ push(r2); |
+ __ CallRuntime(Runtime::kLoadContextSlot, 2); |
+ __ push(r0); // Function. |
+ __ push(r1); // Receiver. |
+ EmitCallWithStub(expr); |
} else if (fun->AsProperty() != NULL) { |
// Call to an object property. |
Property* prop = fun->AsProperty(); |
@@ -1376,7 +1827,720 @@ |
} |
+void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) { |
+ Handle<String> name = expr->name(); |
+ if (strcmp("_IsSmi", *name->ToCString()) == 0) { |
+ EmitIsSmi(expr->arguments()); |
+ } else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) { |
+ EmitIsNonNegativeSmi(expr->arguments()); |
+ } else if (strcmp("_IsObject", *name->ToCString()) == 0) { |
+ EmitIsObject(expr->arguments()); |
+ } else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) { |
+ EmitIsUndetectableObject(expr->arguments()); |
+ } else if (strcmp("_IsFunction", *name->ToCString()) == 0) { |
+ EmitIsFunction(expr->arguments()); |
+ } else if (strcmp("_IsArray", *name->ToCString()) == 0) { |
+ EmitIsArray(expr->arguments()); |
+ } else if (strcmp("_IsRegExp", *name->ToCString()) == 0) { |
+ EmitIsRegExp(expr->arguments()); |
+ } else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) { |
+ EmitIsConstructCall(expr->arguments()); |
+ } else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) { |
+ EmitObjectEquals(expr->arguments()); |
+ } else if (strcmp("_Arguments", *name->ToCString()) == 0) { |
+ EmitArguments(expr->arguments()); |
+ } else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) { |
+ EmitArgumentsLength(expr->arguments()); |
+ } else if (strcmp("_ClassOf", *name->ToCString()) == 0) { |
+ EmitClassOf(expr->arguments()); |
+ } else if (strcmp("_Log", *name->ToCString()) == 0) { |
+ EmitLog(expr->arguments()); |
+ } else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) { |
+ EmitRandomHeapNumber(expr->arguments()); |
+ } else if (strcmp("_SubString", *name->ToCString()) == 0) { |
+ EmitSubString(expr->arguments()); |
+ } else if (strcmp("_RegExpExec", *name->ToCString()) == 0) { |
+ EmitRegExpExec(expr->arguments()); |
+ } else if (strcmp("_ValueOf", *name->ToCString()) == 0) { |
+ EmitValueOf(expr->arguments()); |
+ } else if (strcmp("_SetValueOf", *name->ToCString()) == 0) { |
+ EmitSetValueOf(expr->arguments()); |
+ } else if (strcmp("_NumberToString", *name->ToCString()) == 0) { |
+ EmitNumberToString(expr->arguments()); |
+ } else if (strcmp("_CharFromCode", *name->ToCString()) == 0) { |
+ EmitCharFromCode(expr->arguments()); |
+ } else if (strcmp("_FastCharCodeAt", *name->ToCString()) == 0) { |
+ EmitFastCharCodeAt(expr->arguments()); |
+ } else if (strcmp("_StringAdd", *name->ToCString()) == 0) { |
+ EmitStringAdd(expr->arguments()); |
+ } else if (strcmp("_StringCompare", *name->ToCString()) == 0) { |
+ EmitStringCompare(expr->arguments()); |
+ } else if (strcmp("_MathPow", *name->ToCString()) == 0) { |
+ EmitMathPow(expr->arguments()); |
+ } else if (strcmp("_MathSin", *name->ToCString()) == 0) { |
+ EmitMathSin(expr->arguments()); |
+ } else if (strcmp("_MathCos", *name->ToCString()) == 0) { |
+ EmitMathCos(expr->arguments()); |
+ } else if (strcmp("_MathSqrt", *name->ToCString()) == 0) { |
+ EmitMathSqrt(expr->arguments()); |
+ } else if (strcmp("_CallFunction", *name->ToCString()) == 0) { |
+ EmitCallFunction(expr->arguments()); |
+ } else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) { |
+ EmitRegExpConstructResult(expr->arguments()); |
+ } else if (strcmp("_SwapElements", *name->ToCString()) == 0) { |
+ EmitSwapElements(expr->arguments()); |
+ } else if (strcmp("_GetFromCache", *name->ToCString()) == 0) { |
+ EmitGetFromCache(expr->arguments()); |
+ } else { |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
+void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 1); |
+ |
+ VisitForValue(args->at(0), kAccumulator); |
+ |
+ Label materialize_true, materialize_false; |
+ Label* if_true = NULL; |
+ Label* if_false = NULL; |
+ PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
+ |
+ __ BranchOnSmi(r0, if_true); |
+ __ b(if_false); |
+ |
+ Apply(context_, if_true, if_false); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 1); |
+ |
+ VisitForValue(args->at(0), kAccumulator); |
+ |
+ Label materialize_true, materialize_false; |
+ Label* if_true = NULL; |
+ Label* if_false = NULL; |
+ PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
+ |
+ __ tst(r0, Operand(kSmiTagMask | 0x80000000)); |
+ __ b(eq, if_true); |
+ __ b(if_false); |
+ |
+ Apply(context_, if_true, if_false); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 1); |
+ |
+ VisitForValue(args->at(0), kAccumulator); |
+ |
+ Label materialize_true, materialize_false; |
+ Label* if_true = NULL; |
+ Label* if_false = NULL; |
+ PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
+ __ BranchOnSmi(r0, if_false); |
+ __ LoadRoot(ip, Heap::kNullValueRootIndex); |
+ __ cmp(r0, ip); |
+ __ b(eq, if_true); |
+ __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); |
+ // Undetectable objects behave like undefined when tested with typeof. |
+ __ ldrb(r1, FieldMemOperand(r2, Map::kBitFieldOffset)); |
+ __ tst(r1, Operand(1 << Map::kIsUndetectable)); |
+ __ b(ne, if_false); |
+ __ ldrb(r1, FieldMemOperand(r2, Map::kInstanceTypeOffset)); |
+ __ cmp(r1, Operand(FIRST_JS_OBJECT_TYPE)); |
+ __ b(lt, if_false); |
+ __ cmp(r1, Operand(LAST_JS_OBJECT_TYPE)); |
+ __ b(le, if_true); |
+ __ b(if_false); |
+ |
+ Apply(context_, if_true, if_false); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 1); |
+ |
+ VisitForValue(args->at(0), kAccumulator); |
+ |
+ Label materialize_true, materialize_false; |
+ Label* if_true = NULL; |
+ Label* if_false = NULL; |
+ PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
+ |
+ __ BranchOnSmi(r0, if_false); |
+ __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset)); |
+ __ ldrb(r1, FieldMemOperand(r1, Map::kBitFieldOffset)); |
+ __ tst(r1, Operand(1 << Map::kIsUndetectable)); |
+ __ b(ne, if_true); |
+ __ b(if_false); |
+ |
+ Apply(context_, if_true, if_false); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 1); |
+ |
+ VisitForValue(args->at(0), kAccumulator); |
+ |
+ Label materialize_true, materialize_false; |
+ Label* if_true = NULL; |
+ Label* if_false = NULL; |
+ PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
+ |
+ __ BranchOnSmi(r0, if_false); |
+ __ CompareObjectType(r0, r1, r1, JS_FUNCTION_TYPE); |
+ __ b(eq, if_true); |
+ __ b(if_false); |
+ |
+ Apply(context_, if_true, if_false); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 1); |
+ |
+ VisitForValue(args->at(0), kAccumulator); |
+ |
+ Label materialize_true, materialize_false; |
+ Label* if_true = NULL; |
+ Label* if_false = NULL; |
+ PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
+ |
+ __ BranchOnSmi(r0, if_false); |
+ __ CompareObjectType(r0, r1, r1, JS_ARRAY_TYPE); |
+ __ b(eq, if_true); |
+ __ b(if_false); |
+ |
+ Apply(context_, if_true, if_false); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 1); |
+ |
+ VisitForValue(args->at(0), kAccumulator); |
+ |
+ Label materialize_true, materialize_false; |
+ Label* if_true = NULL; |
+ Label* if_false = NULL; |
+ PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
+ |
+ __ BranchOnSmi(r0, if_false); |
+ __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE); |
+ __ b(eq, if_true); |
+ __ b(if_false); |
+ |
+ Apply(context_, if_true, if_false); |
+} |
+ |
+ |
+ |
+void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 0); |
+ |
+ Label materialize_true, materialize_false; |
+ Label* if_true = NULL; |
+ Label* if_false = NULL; |
+ PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
+ |
+ // Get the frame pointer for the calling frame. |
+ __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); |
+ |
+ // Skip the arguments adaptor frame if it exists. |
+ Label check_frame_marker; |
+ __ ldr(r1, MemOperand(r2, StandardFrameConstants::kContextOffset)); |
+ __ cmp(r1, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); |
+ __ b(ne, &check_frame_marker); |
+ __ ldr(r2, MemOperand(r2, StandardFrameConstants::kCallerFPOffset)); |
+ |
+ // Check the marker in the calling frame. |
+ __ bind(&check_frame_marker); |
+ __ ldr(r1, MemOperand(r2, StandardFrameConstants::kMarkerOffset)); |
+ __ cmp(r1, Operand(Smi::FromInt(StackFrame::CONSTRUCT))); |
+ __ b(eq, if_true); |
+ __ b(if_false); |
+ |
+ Apply(context_, if_true, if_false); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 2); |
+ |
+ // Load the two objects into registers and perform the comparison. |
+ VisitForValue(args->at(0), kStack); |
+ VisitForValue(args->at(1), kAccumulator); |
+ |
+ Label materialize_true, materialize_false; |
+ Label* if_true = NULL; |
+ Label* if_false = NULL; |
+ PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
+ |
+ __ pop(r1); |
+ __ cmp(r0, r1); |
+ __ b(eq, if_true); |
+ __ b(if_false); |
+ |
+ Apply(context_, if_true, if_false); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 1); |
+ |
+ // ArgumentsAccessStub expects the key in edx and the formal |
+ // parameter count in eax. |
+ VisitForValue(args->at(0), kAccumulator); |
+ __ mov(r1, r0); |
+ __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters()))); |
+ ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT); |
+ __ CallStub(&stub); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 0); |
+ |
+ Label exit; |
+ // Get the number of formal parameters. |
+ __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters()))); |
+ |
+ // Check if the calling frame is an arguments adaptor frame. |
+ __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); |
+ __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset)); |
+ __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); |
+ __ b(ne, &exit); |
+ |
+ // Arguments adaptor case: Read the arguments length from the |
+ // adaptor frame. |
+ __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset)); |
+ |
+ __ bind(&exit); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 1); |
+ Label done, null, function, non_function_constructor; |
+ |
+ VisitForValue(args->at(0), kAccumulator); |
+ |
+ // If the object is a smi, we return null. |
+ __ BranchOnSmi(r0, &null); |
+ |
+ // Check that the object is a JS object but take special care of JS |
+ // functions to make sure they have 'Function' as their class. |
+ __ CompareObjectType(r0, r0, r1, FIRST_JS_OBJECT_TYPE); // Map is now in r0. |
+ __ b(lt, &null); |
+ |
+ // As long as JS_FUNCTION_TYPE is the last instance type and it is |
+ // right after LAST_JS_OBJECT_TYPE, we can avoid checking for |
+ // LAST_JS_OBJECT_TYPE. |
+ ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); |
+ ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1); |
+ __ cmp(r1, Operand(JS_FUNCTION_TYPE)); |
+ __ b(eq, &function); |
+ |
+ // Check if the constructor in the map is a function. |
+ __ ldr(r0, FieldMemOperand(r0, Map::kConstructorOffset)); |
+ __ CompareObjectType(r0, r1, r1, JS_FUNCTION_TYPE); |
+ __ b(ne, &non_function_constructor); |
+ |
+ // r0 now contains the constructor function. Grab the |
+ // instance class name from there. |
+ __ ldr(r0, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset)); |
+ __ ldr(r0, FieldMemOperand(r0, SharedFunctionInfo::kInstanceClassNameOffset)); |
+ __ b(&done); |
+ |
+ // Functions have class 'Function'. |
+ __ bind(&function); |
+ __ LoadRoot(r0, Heap::kfunction_class_symbolRootIndex); |
+ __ jmp(&done); |
+ |
+ // Objects with a non-function constructor have class 'Object'. |
+ __ bind(&non_function_constructor); |
+ __ LoadRoot(r0, Heap::kfunction_class_symbolRootIndex); |
+ __ jmp(&done); |
+ |
+ // Non-JS objects have class null. |
+ __ bind(&null); |
+ __ LoadRoot(r0, Heap::kNullValueRootIndex); |
+ |
+ // All done. |
+ __ bind(&done); |
+ |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) { |
+ // Conditionally generate a log call. |
+ // Args: |
+ // 0 (literal string): The type of logging (corresponds to the flags). |
+ // This is used to determine whether or not to generate the log call. |
+ // 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. |
+ ASSERT_EQ(args->length(), 3); |
+#ifdef ENABLE_LOGGING_AND_PROFILING |
+ if (CodeGenerator::ShouldGenerateLog(args->at(0))) { |
+ VisitForValue(args->at(1), kStack); |
+ VisitForValue(args->at(2), kStack); |
+ __ CallRuntime(Runtime::kLog, 2); |
+ } |
+#endif |
+ // Finally, we're expected to leave a value on the top of the stack. |
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 0); |
+ |
+ Label slow_allocate_heapnumber; |
+ Label heapnumber_allocated; |
+ |
+ __ AllocateHeapNumber(r4, r1, r2, &slow_allocate_heapnumber); |
+ __ jmp(&heapnumber_allocated); |
+ |
+ __ 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. |
+ __ mov(r0, Operand(Smi::FromInt(0))); |
+ __ push(r0); |
+ __ CallRuntime(Runtime::kNumberUnaryMinus, 1); |
+ __ mov(r4, Operand(r0)); |
+ |
+ __ bind(&heapnumber_allocated); |
+ |
+ // Convert 32 random bits in r0 to 0.(32 random bits) in a double |
+ // by computing: |
+ // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)). |
+ if (CpuFeatures::IsSupported(VFP3)) { |
+ __ PrepareCallCFunction(0, r1); |
+ __ CallCFunction(ExternalReference::random_uint32_function(), 0); |
+ |
+ CpuFeatures::Scope scope(VFP3); |
+ // 0x41300000 is the top half of 1.0 x 2^20 as a double. |
+ // Create this constant using mov/orr to avoid PC relative load. |
+ __ mov(r1, Operand(0x41000000)); |
+ __ orr(r1, r1, Operand(0x300000)); |
+ // Move 0x41300000xxxxxxxx (x = random bits) to VFP. |
+ __ vmov(d7, r0, r1); |
+ // Move 0x4130000000000000 to VFP. |
+ __ mov(r0, Operand(0)); |
+ __ vmov(d8, r0, r1); |
+ // Subtract and store the result in the heap number. |
+ __ vsub(d7, d7, d8); |
+ __ sub(r0, r4, Operand(kHeapObjectTag)); |
+ __ vstr(d7, r0, HeapNumber::kValueOffset); |
+ __ mov(r0, r4); |
+ } else { |
+ __ mov(r0, Operand(r4)); |
+ __ PrepareCallCFunction(1, r1); |
+ __ CallCFunction( |
+ ExternalReference::fill_heap_number_with_random_function(), 1); |
+ } |
+ |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) { |
+ // Load the arguments on the stack and call the stub. |
+ SubStringStub stub; |
+ ASSERT(args->length() == 3); |
+ VisitForValue(args->at(0), kStack); |
+ VisitForValue(args->at(1), kStack); |
+ VisitForValue(args->at(2), kStack); |
+ __ CallStub(&stub); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) { |
+ // Load the arguments on the stack and call the stub. |
+ RegExpExecStub stub; |
+ ASSERT(args->length() == 4); |
+ VisitForValue(args->at(0), kStack); |
+ VisitForValue(args->at(1), kStack); |
+ VisitForValue(args->at(2), kStack); |
+ VisitForValue(args->at(3), kStack); |
+ __ CallStub(&stub); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 1); |
+ |
+ VisitForValue(args->at(0), kAccumulator); // Load the object. |
+ |
+ Label done; |
+ // If the object is a smi return the object. |
+ __ BranchOnSmi(r0, &done); |
+ // If the object is not a value type, return the object. |
+ __ CompareObjectType(r0, r1, r1, JS_VALUE_TYPE); |
+ __ b(ne, &done); |
+ __ ldr(r0, FieldMemOperand(r0, JSValue::kValueOffset)); |
+ |
+ __ bind(&done); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) { |
+ // Load the arguments on the stack and call the runtime function. |
+ ASSERT(args->length() == 2); |
+ VisitForValue(args->at(0), kStack); |
+ VisitForValue(args->at(1), kStack); |
+ __ CallRuntime(Runtime::kMath_pow, 2); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 2); |
+ |
+ VisitForValue(args->at(0), kStack); // Load the object. |
+ VisitForValue(args->at(1), kAccumulator); // Load the value. |
+ __ pop(r1); // r0 = value. r1 = object. |
+ |
+ Label done; |
+ // If the object is a smi, return the value. |
+ __ BranchOnSmi(r1, &done); |
+ |
+ // If the object is not a value type, return the value. |
+ __ CompareObjectType(r1, r2, r2, JS_VALUE_TYPE); |
+ __ b(ne, &done); |
+ |
+ // Store the value. |
+ __ str(r0, FieldMemOperand(r1, JSValue::kValueOffset)); |
+ // Update the write barrier. Save the value as it will be |
+ // overwritten by the write barrier code and is needed afterward. |
+ __ mov(r2, Operand(JSValue::kValueOffset - kHeapObjectTag)); |
+ __ RecordWrite(r1, r2, r3); |
+ |
+ __ bind(&done); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) { |
+ ASSERT_EQ(args->length(), 1); |
+ |
+ // Load the argument on the stack and call the stub. |
+ VisitForValue(args->at(0), kStack); |
+ |
+ NumberToStringStub stub; |
+ __ CallStub(&stub); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitCharFromCode(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 1); |
+ |
+ VisitForValue(args->at(0), kAccumulator); |
+ |
+ Label slow_case, done; |
+ // Fast case of Heap::LookupSingleCharacterStringFromCode. |
+ ASSERT(kSmiTag == 0); |
+ ASSERT(kSmiShiftSize == 0); |
+ ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1)); |
+ __ tst(r0, Operand(kSmiTagMask | |
+ ((~String::kMaxAsciiCharCode) << kSmiTagSize))); |
+ __ b(nz, &slow_case); |
+ __ mov(r1, Operand(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. |
+ __ add(r1, r1, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize)); |
+ __ ldr(r1, MemOperand(r1, FixedArray::kHeaderSize - kHeapObjectTag)); |
+ __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); |
+ __ cmp(r1, r2); |
+ __ b(eq, &slow_case); |
+ __ mov(r0, r1); |
+ __ b(&done); |
+ |
+ __ bind(&slow_case); |
+ __ push(r0); |
+ __ CallRuntime(Runtime::kCharFromCode, 1); |
+ |
+ __ bind(&done); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitFastCharCodeAt(ZoneList<Expression*>* args) { |
+ // TODO(fsc): Port the complete implementation from the classic back-end. |
+ // Move the undefined value into the result register, which will |
+ // trigger the slow case. |
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); |
+ Apply(context_, r0); |
+} |
+ |
+void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) { |
+ ASSERT_EQ(2, args->length()); |
+ |
+ VisitForValue(args->at(0), kStack); |
+ VisitForValue(args->at(1), kStack); |
+ |
+ StringAddStub stub(NO_STRING_ADD_FLAGS); |
+ __ CallStub(&stub); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) { |
+ ASSERT_EQ(2, args->length()); |
+ |
+ VisitForValue(args->at(0), kStack); |
+ VisitForValue(args->at(1), kStack); |
+ |
+ StringCompareStub stub; |
+ __ CallStub(&stub); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) { |
+ // Load the argument on the stack and call the runtime. |
+ ASSERT(args->length() == 1); |
+ VisitForValue(args->at(0), kStack); |
+ __ CallRuntime(Runtime::kMath_sin, 1); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) { |
+ // Load the argument on the stack and call the runtime. |
+ ASSERT(args->length() == 1); |
+ VisitForValue(args->at(0), kStack); |
+ __ CallRuntime(Runtime::kMath_cos, 1); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) { |
+ // Load the argument on the stack and call the runtime function. |
+ ASSERT(args->length() == 1); |
+ VisitForValue(args->at(0), kStack); |
+ __ CallRuntime(Runtime::kMath_sqrt, 1); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() >= 2); |
+ |
+ int arg_count = args->length() - 2; // For receiver and function. |
+ VisitForValue(args->at(0), kStack); // Receiver. |
+ for (int i = 0; i < arg_count; i++) { |
+ VisitForValue(args->at(i + 1), kStack); |
+ } |
+ VisitForValue(args->at(arg_count + 1), kAccumulator); // Function. |
+ |
+ // InvokeFunction requires function in r1. Move it in there. |
+ if (!result_register().is(r1)) __ mov(r1, result_register()); |
+ ParameterCount count(arg_count); |
+ __ InvokeFunction(r1, count, CALL_FUNCTION); |
+ __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 3); |
+ VisitForValue(args->at(0), kStack); |
+ VisitForValue(args->at(1), kStack); |
+ VisitForValue(args->at(2), kStack); |
+ __ CallRuntime(Runtime::kRegExpConstructResult, 3); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) { |
+ ASSERT(args->length() == 3); |
+ VisitForValue(args->at(0), kStack); |
+ VisitForValue(args->at(1), kStack); |
+ VisitForValue(args->at(2), kStack); |
+ __ CallRuntime(Runtime::kSwapElements, 3); |
+ Apply(context_, r0); |
+} |
+ |
+ |
+void FullCodeGenerator::EmitGetFromCache(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."); |
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); |
+ Apply(context_, r0); |
+ return; |
+ } |
+ |
+ VisitForValue(args->at(1), kAccumulator); |
+ |
+ Register key = r0; |
+ Register cache = r1; |
+ __ ldr(cache, CodeGenerator::ContextOperand(cp, Context::GLOBAL_INDEX)); |
+ __ ldr(cache, FieldMemOperand(cache, GlobalObject::kGlobalContextOffset)); |
+ __ ldr(cache, |
+ CodeGenerator::ContextOperand( |
+ cache, Context::JSFUNCTION_RESULT_CACHES_INDEX)); |
+ __ ldr(cache, |
+ FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id))); |
+ |
+ |
+ Label done, not_found; |
+ // tmp now holds finger offset as a smi. |
+ ASSERT(kSmiTag == 0 && kSmiTagSize == 1); |
+ __ ldr(r2, FieldMemOperand(cache, JSFunctionResultCache::kFingerOffset)); |
+ // r2 now holds finger offset as a smi. |
+ __ add(r3, cache, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); |
+ // r3 now points to the start of fixed array elements. |
+ __ ldr(r2, MemOperand(r3, r2, LSL, kPointerSizeLog2 - kSmiTagSize, PreIndex)); |
+ // Note side effect of PreIndex: r3 now points to the key of the pair. |
+ __ cmp(key, r2); |
+ __ b(ne, ¬_found); |
+ |
+ __ ldr(r0, MemOperand(r3, kPointerSize)); |
+ __ b(&done); |
+ |
+ __ bind(¬_found); |
+ // Call runtime to perform the lookup. |
+ __ Push(cache, key); |
+ __ CallRuntime(Runtime::kGetFromCache, 2); |
+ |
+ __ bind(&done); |
+ Apply(context_, r0); |
+} |
+ |
+ |
void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { |
+ Handle<String> name = expr->name(); |
+ if (name->length() > 0 && name->Get(0) == '_') { |
+ Comment cmnt(masm_, "[ InlineRuntimeCall"); |
+ EmitInlineRuntimeCall(expr); |
+ return; |
+ } |
+ |
Comment cmnt(masm_, "[ CallRuntime"); |
ZoneList<Expression*>* args = expr->arguments(); |
@@ -1411,6 +2575,49 @@ |
void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { |
switch (expr->op()) { |
+ case Token::DELETE: { |
+ Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); |
+ Property* prop = expr->expression()->AsProperty(); |
+ Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); |
+ if (prop == NULL && var == NULL) { |
+ // Result of deleting non-property, non-variable reference is true. |
+ // The subexpression may have side effects. |
+ VisitForEffect(expr->expression()); |
+ Apply(context_, true); |
+ } else if (var != NULL && |
+ !var->is_global() && |
+ var->slot() != NULL && |
+ var->slot()->type() != Slot::LOOKUP) { |
+ // Result of deleting non-global, non-dynamic variables is false. |
+ // The subexpression does not have side effects. |
+ Apply(context_, false); |
+ } else { |
+ // Property or variable reference. Call the delete builtin with |
+ // object and property name as arguments. |
+ if (prop != NULL) { |
+ VisitForValue(prop->obj(), kStack); |
+ VisitForValue(prop->key(), kStack); |
+ } else if (var->is_global()) { |
+ __ ldr(r1, CodeGenerator::GlobalObject()); |
+ __ mov(r0, Operand(var->name())); |
+ __ Push(r1, r0); |
+ } else { |
+ // Non-global variable. Call the runtime to look up the context |
+ // where the variable was introduced. |
+ __ push(context_register()); |
+ __ mov(r2, Operand(var->name())); |
+ __ push(r2); |
+ __ CallRuntime(Runtime::kLookupContext, 2); |
+ __ push(r0); |
+ __ mov(r2, Operand(var->name())); |
+ __ push(r2); |
+ } |
+ __ InvokeBuiltin(Builtins::DELETE, CALL_JS); |
+ Apply(context_, r0); |
+ } |
+ break; |
+ } |
+ |
case Token::VOID: { |
Comment cmnt(masm_, "[ UnaryOperation (VOID)"); |
VisitForEffect(expr->expression()); |
@@ -1451,33 +2658,15 @@ |
case Token::NOT: { |
Comment cmnt(masm_, "[ UnaryOperation (NOT)"); |
- Label materialize_true, materialize_false, done; |
- // Initially assume a pure test context. Notice that the labels are |
- // swapped. |
- Label* if_true = false_label_; |
- Label* if_false = true_label_; |
- switch (context_) { |
- case Expression::kUninitialized: |
- UNREACHABLE(); |
- break; |
- case Expression::kEffect: |
- if_true = &done; |
- if_false = &done; |
- break; |
- case Expression::kValue: |
- if_true = &materialize_false; |
- if_false = &materialize_true; |
- break; |
- case Expression::kTest: |
- break; |
- case Expression::kValueTest: |
- if_false = &materialize_true; |
- break; |
- case Expression::kTestValue: |
- if_true = &materialize_false; |
- break; |
- } |
+ Label materialize_true, materialize_false; |
+ Label* if_true = NULL; |
+ Label* if_false = NULL; |
+ |
+ // Notice that the labels are swapped. |
+ PrepareTest(&materialize_true, &materialize_false, &if_false, &if_true); |
+ |
VisitForControl(expr->expression(), if_true, if_false); |
+ |
Apply(context_, if_false, if_true); // Labels swapped. |
break; |
} |
@@ -1500,7 +2689,7 @@ |
proxy->var()->slot() != NULL && |
proxy->var()->slot()->type() == Slot::LOOKUP) { |
__ mov(r0, Operand(proxy->name())); |
- __ stm(db_w, sp, cp.bit() | r0.bit()); |
+ __ Push(cp, r0); |
__ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); |
__ push(r0); |
} else { |
@@ -1551,8 +2740,7 @@ |
VisitForValue(expr->expression(), kAccumulator); |
// Avoid calling the stub for Smis. |
Label smi, done; |
- __ tst(result_register(), Operand(kSmiTagMask)); |
- __ b(eq, &smi); |
+ __ BranchOnSmi(result_register(), &smi); |
// Non-smi: call stub leaving result in accumulator register. |
__ CallStub(&stub); |
__ b(&done); |
@@ -1574,6 +2762,12 @@ |
void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { |
Comment cmnt(masm_, "[ CountOperation"); |
+ // Invalid left-hand sides are rewritten to have a 'throw ReferenceError' |
+ // as the left-hand side. |
+ if (!expr->expression()->IsValidLeftHandSide()) { |
+ VisitForEffect(expr->expression()); |
+ return; |
+ } |
// Expression can only be a property, a global or a (parameter or local) |
// slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. |
@@ -1617,8 +2811,7 @@ |
// Call ToNumber only if operand is not a smi. |
Label no_conversion; |
- __ tst(r0, Operand(kSmiTagMask)); |
- __ b(eq, &no_conversion); |
+ __ BranchOnSmi(r0, &no_conversion); |
__ push(r0); |
__ InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS); |
__ bind(&no_conversion); |
@@ -1662,8 +2855,7 @@ |
__ b(vs, &stub_call); |
// We could eliminate this smi check if we split the code at |
// the first smi check before calling ToNumber. |
- __ tst(r0, Operand(kSmiTagMask)); |
- __ b(eq, &done); |
+ __ BranchOnSmi(r0, &done); |
__ bind(&stub_call); |
// Call stub. Undo operation first. |
__ sub(r0, r0, Operand(Smi::FromInt(count_value))); |
@@ -1758,37 +2950,42 @@ |
} |
+void FullCodeGenerator::EmitNullCompare(bool strict, |
+ Register obj, |
+ Register null_const, |
+ Label* if_true, |
+ Label* if_false, |
+ Register scratch) { |
+ __ cmp(obj, null_const); |
+ if (strict) { |
+ __ b(eq, if_true); |
+ } else { |
+ __ b(eq, if_true); |
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); |
+ __ cmp(obj, ip); |
+ __ b(eq, if_true); |
+ __ BranchOnSmi(obj, if_false); |
+ // It can be an undetectable object. |
+ __ ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); |
+ __ ldrb(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset)); |
+ __ tst(scratch, Operand(1 << Map::kIsUndetectable)); |
+ __ b(ne, if_true); |
+ } |
+ __ jmp(if_false); |
+} |
+ |
+ |
void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { |
Comment cmnt(masm_, "[ CompareOperation"); |
// 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, done; |
- // Initially assume we are in a test context. |
- Label* if_true = true_label_; |
- Label* if_false = false_label_; |
- switch (context_) { |
- case Expression::kUninitialized: |
- UNREACHABLE(); |
- break; |
- case Expression::kEffect: |
- if_true = &done; |
- if_false = &done; |
- break; |
- case Expression::kValue: |
- if_true = &materialize_true; |
- if_false = &materialize_false; |
- break; |
- case Expression::kTest: |
- break; |
- case Expression::kValueTest: |
- if_true = &materialize_true; |
- break; |
- case Expression::kTestValue: |
- if_false = &materialize_false; |
- break; |
- } |
+ Label materialize_true, materialize_false; |
+ Label* if_true = NULL; |
+ Label* if_false = NULL; |
+ PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false); |
+ |
VisitForValue(expr->left(), kStack); |
switch (expr->op()) { |
case Token::IN: |
@@ -1818,10 +3015,24 @@ |
case Token::EQ_STRICT: |
strict = true; |
// Fall through |
- case Token::EQ: |
+ case Token::EQ: { |
cc = eq; |
__ pop(r1); |
+ // If either operand is constant null we do a fast compare |
+ // against null. |
+ Literal* right_literal = expr->right()->AsLiteral(); |
+ Literal* left_literal = expr->left()->AsLiteral(); |
+ if (right_literal != NULL && right_literal->handle()->IsNull()) { |
+ EmitNullCompare(strict, r1, r0, if_true, if_false, r2); |
+ Apply(context_, if_true, if_false); |
+ return; |
+ } else if (left_literal != NULL && left_literal->handle()->IsNull()) { |
+ EmitNullCompare(strict, r0, r1, if_true, if_false, r2); |
+ Apply(context_, if_true, if_false); |
+ return; |
+ } |
break; |
+ } |
case Token::LT: |
cc = lt; |
__ pop(r1); |
@@ -1852,8 +3063,7 @@ |
// before it is called. |
Label slow_case; |
__ orr(r2, r0, Operand(r1)); |
- __ tst(r2, Operand(kSmiTagMask)); |
- __ b(ne, &slow_case); |
+ __ BranchOnNotSmi(r2, &slow_case); |
__ cmp(r1, r0); |
__ b(cc, if_true); |
__ jmp(if_false); |