| Index: src/arm/lithium-codegen-arm.cc
 | 
| ===================================================================
 | 
| --- src/arm/lithium-codegen-arm.cc	(revision 9531)
 | 
| +++ src/arm/lithium-codegen-arm.cc	(working copy)
 | 
| @@ -82,6 +82,14 @@
 | 
|    status_ = GENERATING;
 | 
|    CpuFeatures::Scope scope1(VFP3);
 | 
|    CpuFeatures::Scope scope2(ARMv7);
 | 
| +
 | 
| +  CodeStub::GenerateFPStubs();
 | 
| +
 | 
| +  // Open a frame scope to indicate that there is a frame on the stack.  The
 | 
| +  // NONE indicates that the scope shouldn't actually generate code to set up
 | 
| +  // the frame (that is done in GeneratePrologue).
 | 
| +  FrameScope frame_scope(masm_, StackFrame::NONE);
 | 
| +
 | 
|    return GeneratePrologue() &&
 | 
|        GenerateBody() &&
 | 
|        GenerateDeferredCode() &&
 | 
| @@ -206,13 +214,11 @@
 | 
|          // Load parameter from stack.
 | 
|          __ ldr(r0, MemOperand(fp, parameter_offset));
 | 
|          // Store it in the context.
 | 
| -        __ mov(r1, Operand(Context::SlotOffset(var->index())));
 | 
| -        __ str(r0, MemOperand(cp, r1));
 | 
| -        // Update the write barrier. This clobbers all involved
 | 
| -        // registers, so we have to use two more registers to avoid
 | 
| -        // clobbering cp.
 | 
| -        __ mov(r2, Operand(cp));
 | 
| -        __ RecordWrite(r2, Operand(r1), r3, r0);
 | 
| +        MemOperand target = ContextOperand(cp, var->index());
 | 
| +        __ str(r0, target);
 | 
| +        // Update the write barrier. This clobbers r3 and r0.
 | 
| +        __ RecordWriteContextSlot(
 | 
| +            cp, target.offset(), r0, r3, kLRHasBeenSaved, kSaveFPRegs);
 | 
|        }
 | 
|      }
 | 
|      Comment(";;; End allocate local context");
 | 
| @@ -262,6 +268,9 @@
 | 
|      for (int i = 0; !is_aborted() && i < deferred_.length(); i++) {
 | 
|        LDeferredCode* code = deferred_[i];
 | 
|        __ bind(code->entry());
 | 
| +      Comment(";;; Deferred code @%d: %s.",
 | 
| +              code->instruction_index(),
 | 
| +              code->instr()->Mnemonic());
 | 
|        code->Generate();
 | 
|        __ jmp(code->exit());
 | 
|      }
 | 
| @@ -739,7 +748,7 @@
 | 
|      int deoptimization_index) {
 | 
|    ASSERT(expected_safepoint_kind_ == kind);
 | 
|  
 | 
| -  const ZoneList<LOperand*>* operands = pointers->operands();
 | 
| +  const ZoneList<LOperand*>* operands = pointers->GetNormalizedOperands();
 | 
|    Safepoint safepoint = safepoints_.DefineSafepoint(masm(),
 | 
|        kind, arguments, deoptimization_index);
 | 
|    for (int i = 0; i < operands->length(); i++) {
 | 
| @@ -1032,6 +1041,7 @@
 | 
|      virtual void Generate() {
 | 
|        codegen()->DoDeferredBinaryOpStub(instr_, Token::DIV);
 | 
|      }
 | 
| +    virtual LInstruction* instr() { return instr_; }
 | 
|     private:
 | 
|      LDivI* instr_;
 | 
|    };
 | 
| @@ -1743,25 +1753,35 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| -void LCodeGen::DoIsNullAndBranch(LIsNullAndBranch* instr) {
 | 
| +void LCodeGen::DoIsNilAndBranch(LIsNilAndBranch* instr) {
 | 
|    Register scratch = scratch0();
 | 
|    Register reg = ToRegister(instr->InputAt(0));
 | 
| +  int false_block = chunk_->LookupDestination(instr->false_block_id());
 | 
|  
 | 
| -  // TODO(fsc): If the expression is known to be a smi, then it's
 | 
| -  // definitely not null. Jump to the false block.
 | 
| +  // If the expression is known to be untagged or a smi, then it's definitely
 | 
| +  // not null, and it can't be a an undetectable object.
 | 
| +  if (instr->hydrogen()->representation().IsSpecialization() ||
 | 
| +      instr->hydrogen()->type().IsSmi()) {
 | 
| +    EmitGoto(false_block);
 | 
| +    return;
 | 
| +  }
 | 
|  
 | 
|    int true_block = chunk_->LookupDestination(instr->true_block_id());
 | 
| -  int false_block = chunk_->LookupDestination(instr->false_block_id());
 | 
| -
 | 
| -  __ LoadRoot(ip, Heap::kNullValueRootIndex);
 | 
| +  Heap::RootListIndex nil_value = instr->nil() == kNullValue ?
 | 
| +      Heap::kNullValueRootIndex :
 | 
| +      Heap::kUndefinedValueRootIndex;
 | 
| +  __ LoadRoot(ip, nil_value);
 | 
|    __ cmp(reg, ip);
 | 
| -  if (instr->is_strict()) {
 | 
| +  if (instr->kind() == kStrictEquality) {
 | 
|      EmitBranch(true_block, false_block, eq);
 | 
|    } else {
 | 
| +    Heap::RootListIndex other_nil_value = instr->nil() == kNullValue ?
 | 
| +        Heap::kUndefinedValueRootIndex :
 | 
| +        Heap::kNullValueRootIndex;
 | 
|      Label* true_label = chunk_->GetAssemblyLabel(true_block);
 | 
|      Label* false_label = chunk_->GetAssemblyLabel(false_block);
 | 
|      __ b(eq, true_label);
 | 
| -    __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
 | 
| +    __ LoadRoot(ip, other_nil_value);
 | 
|      __ cmp(reg, ip);
 | 
|      __ b(eq, true_label);
 | 
|      __ JumpIfSmi(reg, false_label);
 | 
| @@ -1918,28 +1938,36 @@
 | 
|    ASSERT(!input.is(temp));
 | 
|    ASSERT(!temp.is(temp2));  // But input and temp2 may be the same register.
 | 
|    __ JumpIfSmi(input, is_false);
 | 
| -  __ CompareObjectType(input, temp, temp2, FIRST_SPEC_OBJECT_TYPE);
 | 
| -  __ b(lt, is_false);
 | 
|  
 | 
| -  // Map is now in temp.
 | 
| -  // Functions have class 'Function'.
 | 
| -  __ CompareInstanceType(temp, temp2, FIRST_CALLABLE_SPEC_OBJECT_TYPE);
 | 
|    if (class_name->IsEqualTo(CStrVector("Function"))) {
 | 
| -    __ b(ge, is_true);
 | 
| +    // Assuming the following assertions, we can use the same compares to test
 | 
| +    // for both being a function type and being in the object type range.
 | 
| +    STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2);
 | 
| +    STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE ==
 | 
| +                  FIRST_SPEC_OBJECT_TYPE + 1);
 | 
| +    STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE ==
 | 
| +                  LAST_SPEC_OBJECT_TYPE - 1);
 | 
| +    STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
 | 
| +    __ CompareObjectType(input, temp, temp2, FIRST_SPEC_OBJECT_TYPE);
 | 
| +    __ b(lt, is_false);
 | 
| +    __ b(eq, is_true);
 | 
| +    __ cmp(temp2, Operand(LAST_SPEC_OBJECT_TYPE));
 | 
| +    __ b(eq, is_true);
 | 
|    } else {
 | 
| -    __ b(ge, is_false);
 | 
| +    // Faster code path to avoid two compares: subtract lower bound from the
 | 
| +    // actual type and do a signed compare with the width of the type range.
 | 
| +    __ ldr(temp, FieldMemOperand(input, HeapObject::kMapOffset));
 | 
| +    __ ldrb(temp2, FieldMemOperand(temp, Map::kInstanceTypeOffset));
 | 
| +    __ sub(temp2, temp2, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
 | 
| +    __ cmp(temp2, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE -
 | 
| +                          FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
 | 
| +    __ b(gt, is_false);
 | 
|    }
 | 
|  
 | 
| +  // Now we are in the FIRST-LAST_NONCALLABLE_SPEC_OBJECT_TYPE range.
 | 
|    // Check if the constructor in the map is a function.
 | 
|    __ ldr(temp, FieldMemOperand(temp, Map::kConstructorOffset));
 | 
|  
 | 
| -  // 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);
 | 
| -
 | 
|    // Objects with a non-function constructor have class 'Object'.
 | 
|    __ CompareObjectType(temp, temp2, temp2, JS_FUNCTION_TYPE);
 | 
|    if (class_name->IsEqualTo(CStrVector("Object"))) {
 | 
| @@ -2016,9 +2044,8 @@
 | 
|      virtual void Generate() {
 | 
|        codegen()->DoDeferredLInstanceOfKnownGlobal(instr_, &map_check_);
 | 
|      }
 | 
| -
 | 
| +    virtual LInstruction* instr() { return instr_; }
 | 
|      Label* map_check() { return &map_check_; }
 | 
| -
 | 
|     private:
 | 
|      LInstanceOfKnownGlobal* instr_;
 | 
|      Label map_check_;
 | 
| @@ -2180,7 +2207,7 @@
 | 
|    Register result = ToRegister(instr->result());
 | 
|    __ mov(ip, Operand(Handle<Object>(instr->hydrogen()->cell())));
 | 
|    __ ldr(result, FieldMemOperand(ip, JSGlobalPropertyCell::kValueOffset));
 | 
| -  if (instr->hydrogen()->check_hole_value()) {
 | 
| +  if (instr->hydrogen()->RequiresHoleCheck()) {
 | 
|      __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
 | 
|      __ cmp(result, ip);
 | 
|      DeoptimizeIf(eq, instr->environment());
 | 
| @@ -2203,6 +2230,7 @@
 | 
|  void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) {
 | 
|    Register value = ToRegister(instr->InputAt(0));
 | 
|    Register scratch = scratch0();
 | 
| +  Register scratch2 = ToRegister(instr->TempAt(0));
 | 
|  
 | 
|    // Load the cell.
 | 
|    __ mov(scratch, Operand(Handle<Object>(instr->hydrogen()->cell())));
 | 
| @@ -2211,8 +2239,7 @@
 | 
|    // been deleted from the property dictionary. In that case, we need
 | 
|    // to update the property details in the property dictionary to mark
 | 
|    // it as no longer deleted.
 | 
| -  if (instr->hydrogen()->check_hole_value()) {
 | 
| -    Register scratch2 = ToRegister(instr->TempAt(0));
 | 
| +  if (instr->hydrogen()->RequiresHoleCheck()) {
 | 
|      __ ldr(scratch2,
 | 
|             FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset));
 | 
|      __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
 | 
| @@ -2222,6 +2249,15 @@
 | 
|  
 | 
|    // Store the value.
 | 
|    __ str(value, FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset));
 | 
| +
 | 
| +  // Cells are always in the remembered set.
 | 
| +  __ RecordWriteField(scratch,
 | 
| +                      JSGlobalPropertyCell::kValueOffset,
 | 
| +                      value,
 | 
| +                      scratch2,
 | 
| +                      kLRHasBeenSaved,
 | 
| +                      kSaveFPRegs,
 | 
| +                      OMIT_REMEMBERED_SET);
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -2247,10 +2283,15 @@
 | 
|  void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
 | 
|    Register context = ToRegister(instr->context());
 | 
|    Register value = ToRegister(instr->value());
 | 
| -  __ str(value, ContextOperand(context, instr->slot_index()));
 | 
| +  MemOperand target = ContextOperand(context, instr->slot_index());
 | 
| +  __ str(value, target);
 | 
|    if (instr->needs_write_barrier()) {
 | 
| -    int offset = Context::SlotOffset(instr->slot_index());
 | 
| -    __ RecordWrite(context, Operand(offset), value, scratch0());
 | 
| +    __ RecordWriteContextSlot(context,
 | 
| +                              target.offset(),
 | 
| +                              value,
 | 
| +                              scratch0(),
 | 
| +                              kLRHasBeenSaved,
 | 
| +                              kSaveFPRegs);
 | 
|    }
 | 
|  }
 | 
|  
 | 
| @@ -2500,13 +2541,9 @@
 | 
|             Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag));
 | 
|    }
 | 
|  
 | 
| -  if (instr->hydrogen()->RequiresHoleCheck()) {
 | 
| -    // TODO(danno): If no hole check is required, there is no need to allocate
 | 
| -    // elements into a temporary register, instead scratch can be used.
 | 
| -    __ ldr(scratch, MemOperand(elements, sizeof(kHoleNanLower32)));
 | 
| -    __ cmp(scratch, Operand(kHoleNanUpper32));
 | 
| -    DeoptimizeIf(eq, instr->environment());
 | 
| -  }
 | 
| +  __ ldr(scratch, MemOperand(elements, sizeof(kHoleNanLower32)));
 | 
| +  __ cmp(scratch, Operand(kHoleNanUpper32));
 | 
| +  DeoptimizeIf(eq, instr->environment());
 | 
|  
 | 
|    __ vldr(result, elements, 0);
 | 
|  }
 | 
| @@ -2577,6 +2614,7 @@
 | 
|        case EXTERNAL_DOUBLE_ELEMENTS:
 | 
|        case FAST_DOUBLE_ELEMENTS:
 | 
|        case FAST_ELEMENTS:
 | 
| +      case FAST_SMI_ONLY_ELEMENTS:
 | 
|        case DICTIONARY_ELEMENTS:
 | 
|        case NON_STRICT_ARGUMENTS_ELEMENTS:
 | 
|          UNREACHABLE();
 | 
| @@ -2906,6 +2944,7 @@
 | 
|      virtual void Generate() {
 | 
|        codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_);
 | 
|      }
 | 
| +    virtual LInstruction* instr() { return instr_; }
 | 
|     private:
 | 
|      LUnaryMathOperation* instr_;
 | 
|    };
 | 
| @@ -3202,7 +3241,7 @@
 | 
|    ASSERT(ToRegister(instr->result()).is(r0));
 | 
|  
 | 
|    int arity = instr->arity();
 | 
| -  CallFunctionStub stub(arity, RECEIVER_MIGHT_BE_IMPLICIT);
 | 
| +  CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
 | 
|    CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
 | 
|    __ Drop(1);
 | 
|    __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
 | 
| @@ -3262,7 +3301,8 @@
 | 
|      __ str(value, FieldMemOperand(object, offset));
 | 
|      if (instr->needs_write_barrier()) {
 | 
|        // Update the write barrier for the object for in-object properties.
 | 
| -      __ RecordWrite(object, Operand(offset), value, scratch);
 | 
| +      __ RecordWriteField(
 | 
| +          object, offset, value, scratch, kLRHasBeenSaved, kSaveFPRegs);
 | 
|      }
 | 
|    } else {
 | 
|      __ ldr(scratch, FieldMemOperand(object, JSObject::kPropertiesOffset));
 | 
| @@ -3270,7 +3310,8 @@
 | 
|      if (instr->needs_write_barrier()) {
 | 
|        // Update the write barrier for the properties array.
 | 
|        // object is used as a scratch register.
 | 
| -      __ RecordWrite(scratch, Operand(offset), value, object);
 | 
| +      __ RecordWriteField(
 | 
| +          scratch, offset, value, object, kLRHasBeenSaved, kSaveFPRegs);
 | 
|      }
 | 
|    }
 | 
|  }
 | 
| @@ -3301,6 +3342,13 @@
 | 
|    Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg;
 | 
|    Register scratch = scratch0();
 | 
|  
 | 
| +  // This instruction cannot handle the FAST_SMI_ONLY_ELEMENTS -> FAST_ELEMENTS
 | 
| +  // conversion, so it deopts in that case.
 | 
| +  if (instr->hydrogen()->ValueNeedsSmiCheck()) {
 | 
| +    __ tst(value, Operand(kSmiTagMask));
 | 
| +    DeoptimizeIf(ne, instr->environment());
 | 
| +  }
 | 
| +
 | 
|    // Do the store.
 | 
|    if (instr->key()->IsConstantOperand()) {
 | 
|      ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
 | 
| @@ -3315,8 +3363,8 @@
 | 
|  
 | 
|    if (instr->hydrogen()->NeedsWriteBarrier()) {
 | 
|      // Compute address of modified element and store it into key register.
 | 
| -    __ add(key, scratch, Operand(FixedArray::kHeaderSize));
 | 
| -    __ RecordWrite(elements, key, value);
 | 
| +    __ add(key, scratch, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
 | 
| +    __ RecordWrite(elements, key, value, kLRHasBeenSaved, kSaveFPRegs);
 | 
|    }
 | 
|  }
 | 
|  
 | 
| @@ -3417,6 +3465,7 @@
 | 
|        case EXTERNAL_DOUBLE_ELEMENTS:
 | 
|        case FAST_DOUBLE_ELEMENTS:
 | 
|        case FAST_ELEMENTS:
 | 
| +      case FAST_SMI_ONLY_ELEMENTS:
 | 
|        case DICTIONARY_ELEMENTS:
 | 
|        case NON_STRICT_ARGUMENTS_ELEMENTS:
 | 
|          UNREACHABLE();
 | 
| @@ -3452,6 +3501,7 @@
 | 
|      DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr)
 | 
|          : LDeferredCode(codegen), instr_(instr) { }
 | 
|      virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); }
 | 
| +    virtual LInstruction* instr() { return instr_; }
 | 
|     private:
 | 
|      LStringCharCodeAt* instr_;
 | 
|    };
 | 
| @@ -3575,6 +3625,7 @@
 | 
|      DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr)
 | 
|          : LDeferredCode(codegen), instr_(instr) { }
 | 
|      virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); }
 | 
| +    virtual LInstruction* instr() { return instr_; }
 | 
|     private:
 | 
|      LStringCharFromCode* instr_;
 | 
|    };
 | 
| @@ -3646,6 +3697,7 @@
 | 
|      DeferredNumberTagI(LCodeGen* codegen, LNumberTagI* instr)
 | 
|          : LDeferredCode(codegen), instr_(instr) { }
 | 
|      virtual void Generate() { codegen()->DoDeferredNumberTagI(instr_); }
 | 
| +    virtual LInstruction* instr() { return instr_; }
 | 
|     private:
 | 
|      LNumberTagI* instr_;
 | 
|    };
 | 
| @@ -3711,6 +3763,7 @@
 | 
|      DeferredNumberTagD(LCodeGen* codegen, LNumberTagD* instr)
 | 
|          : LDeferredCode(codegen), instr_(instr) { }
 | 
|      virtual void Generate() { codegen()->DoDeferredNumberTagD(instr_); }
 | 
| +    virtual LInstruction* instr() { return instr_; }
 | 
|     private:
 | 
|      LNumberTagD* instr_;
 | 
|    };
 | 
| @@ -3819,16 +3872,6 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| -class DeferredTaggedToI: public LDeferredCode {
 | 
| - public:
 | 
| -  DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr)
 | 
| -      : LDeferredCode(codegen), instr_(instr) { }
 | 
| -  virtual void Generate() { codegen()->DoDeferredTaggedToI(instr_); }
 | 
| - private:
 | 
| -  LTaggedToI* instr_;
 | 
| -};
 | 
| -
 | 
| -
 | 
|  void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
 | 
|    Register input_reg = ToRegister(instr->InputAt(0));
 | 
|    Register scratch1 = scratch0();
 | 
| @@ -3911,6 +3954,16 @@
 | 
|  
 | 
|  
 | 
|  void LCodeGen::DoTaggedToI(LTaggedToI* instr) {
 | 
| +  class DeferredTaggedToI: public LDeferredCode {
 | 
| +   public:
 | 
| +    DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr)
 | 
| +        : LDeferredCode(codegen), instr_(instr) { }
 | 
| +    virtual void Generate() { codegen()->DoDeferredTaggedToI(instr_); }
 | 
| +    virtual LInstruction* instr() { return instr_; }
 | 
| +   private:
 | 
| +    LTaggedToI* instr_;
 | 
| +  };
 | 
| +
 | 
|    LOperand* input = instr->InputAt(0);
 | 
|    ASSERT(input->IsRegister());
 | 
|    ASSERT(input->Equals(instr->result()));
 | 
| @@ -4343,10 +4396,12 @@
 | 
|      final_branch_condition = ne;
 | 
|  
 | 
|    } else if (type_name->Equals(heap()->function_symbol())) {
 | 
| +    STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2);
 | 
|      __ JumpIfSmi(input, false_label);
 | 
| -    __ CompareObjectType(input, input, scratch,
 | 
| -                         FIRST_CALLABLE_SPEC_OBJECT_TYPE);
 | 
| -    final_branch_condition = ge;
 | 
| +    __ CompareObjectType(input, scratch, input, JS_FUNCTION_TYPE);
 | 
| +    __ b(eq, true_label);
 | 
| +    __ cmp(input, Operand(JS_FUNCTION_PROXY_TYPE));
 | 
| +    final_branch_condition = eq;
 | 
|  
 | 
|    } else if (type_name->Equals(heap()->object_symbol())) {
 | 
|      __ JumpIfSmi(input, false_label);
 | 
| @@ -4468,6 +4523,7 @@
 | 
|      DeferredStackCheck(LCodeGen* codegen, LStackCheck* instr)
 | 
|          : LDeferredCode(codegen), instr_(instr) { }
 | 
|      virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); }
 | 
| +    virtual LInstruction* instr() { return instr_; }
 | 
|     private:
 | 
|      LStackCheck* instr_;
 | 
|    };
 | 
| 
 |