| Index: src/mips/macro-assembler-mips.cc
 | 
| ===================================================================
 | 
| --- src/mips/macro-assembler-mips.cc	(revision 9531)
 | 
| +++ src/mips/macro-assembler-mips.cc	(working copy)
 | 
| @@ -42,7 +42,8 @@
 | 
|  MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size)
 | 
|      : Assembler(arg_isolate, buffer, size),
 | 
|        generating_stub_(false),
 | 
| -      allow_stub_calls_(true) {
 | 
| +      allow_stub_calls_(true),
 | 
| +      has_frame_(false) {
 | 
|    if (isolate() != NULL) {
 | 
|      code_object_ = Handle<Object>(isolate()->heap()->undefined_value(),
 | 
|                                    isolate());
 | 
| @@ -119,7 +120,9 @@
 | 
|    // stack, so adjust the stack for unsaved registers.
 | 
|    const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
 | 
|    ASSERT(num_unsaved >= 0);
 | 
| -  Subu(sp, sp, Operand(num_unsaved * kPointerSize));
 | 
| +  if (num_unsaved > 0) {
 | 
| +    Subu(sp, sp, Operand(num_unsaved * kPointerSize));
 | 
| +  }
 | 
|    MultiPush(kSafepointSavedRegisters);
 | 
|  }
 | 
|  
 | 
| @@ -127,7 +130,9 @@
 | 
|  void MacroAssembler::PopSafepointRegisters() {
 | 
|    const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
 | 
|    MultiPop(kSafepointSavedRegisters);
 | 
| -  Addu(sp, sp, Operand(num_unsaved * kPointerSize));
 | 
| +  if (num_unsaved > 0) {
 | 
| +    Addu(sp, sp, Operand(num_unsaved * kPointerSize));
 | 
| +  }
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -180,6 +185,7 @@
 | 
|  
 | 
|  
 | 
|  MemOperand MacroAssembler::SafepointRegistersAndDoublesSlot(Register reg) {
 | 
| +  UNIMPLEMENTED_MIPS();
 | 
|    // General purpose registers are pushed last on the stack.
 | 
|    int doubles_size = FPURegister::kNumAllocatableRegisters * kDoubleSize;
 | 
|    int register_offset = SafepointRegisterStackIndex(reg.code()) * kPointerSize;
 | 
| @@ -187,8 +193,6 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| -
 | 
| -
 | 
|  void MacroAssembler::InNewSpace(Register object,
 | 
|                                  Register scratch,
 | 
|                                  Condition cc,
 | 
| @@ -707,7 +711,7 @@
 | 
|    int16_t stack_offset = num_to_push * kPointerSize;
 | 
|  
 | 
|    Subu(sp, sp, Operand(stack_offset));
 | 
| -  for (int16_t i = kNumRegisters; i > 0; i--) {
 | 
| +  for (int16_t i = kNumRegisters - 1; i >= 0; i--) {
 | 
|      if ((regs & (1 << i)) != 0) {
 | 
|        stack_offset -= kPointerSize;
 | 
|        sw(ToRegister(i), MemOperand(sp, stack_offset));
 | 
| @@ -746,7 +750,7 @@
 | 
|  void MacroAssembler::MultiPopReversed(RegList regs) {
 | 
|    int16_t stack_offset = 0;
 | 
|  
 | 
| -  for (int16_t i = kNumRegisters; i > 0; i--) {
 | 
| +  for (int16_t i = kNumRegisters - 1; i >= 0; i--) {
 | 
|      if ((regs & (1 << i)) != 0) {
 | 
|        lw(ToRegister(i), MemOperand(sp, stack_offset));
 | 
|        stack_offset += kPointerSize;
 | 
| @@ -814,6 +818,21 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +void MacroAssembler::FlushICache(Register address, unsigned instructions) {
 | 
| +  RegList saved_regs = kJSCallerSaved | ra.bit();
 | 
| +  MultiPush(saved_regs);
 | 
| +  AllowExternalCallThatCantCauseGC scope(this);
 | 
| +
 | 
| +  // Save to a0 in case address == t0.
 | 
| +  Move(a0, address);
 | 
| +  PrepareCallCFunction(2, t0);
 | 
| +
 | 
| +  li(a1, instructions * kInstrSize);
 | 
| +  CallCFunction(ExternalReference::flush_icache_function(isolate()), 2);
 | 
| +  MultiPop(saved_regs);
 | 
| +}
 | 
| +
 | 
| +
 | 
|  void MacroAssembler::Ext(Register rt,
 | 
|                           Register rs,
 | 
|                           uint16_t pos,
 | 
| @@ -940,11 +959,9 @@
 | 
|    mtc1(at, FPURegister::from_code(scratch.code() + 1));
 | 
|    mtc1(zero_reg, scratch);
 | 
|    // Test if scratch > fd.
 | 
| -  c(OLT, D, fd, scratch);
 | 
| -
 | 
| +  // If fd < 2^31 we can convert it normally.
 | 
|    Label simple_convert;
 | 
| -  // If fd < 2^31 we can convert it normally.
 | 
| -  bc1t(&simple_convert);
 | 
| +  BranchF(&simple_convert, NULL, lt, fd, scratch);
 | 
|  
 | 
|    // First we subtract 2^31 from fd, then trunc it to rs
 | 
|    // and add 2^31 to rs.
 | 
| @@ -964,6 +981,102 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +void MacroAssembler::BranchF(Label* target,
 | 
| +                             Label* nan,
 | 
| +                             Condition cc,
 | 
| +                             FPURegister cmp1,
 | 
| +                             FPURegister cmp2,
 | 
| +                             BranchDelaySlot bd) {
 | 
| +  if (cc == al) {
 | 
| +    Branch(bd, target);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  ASSERT(nan || target);
 | 
| +  // Check for unordered (NaN) cases.
 | 
| +  if (nan) {
 | 
| +    c(UN, D, cmp1, cmp2);
 | 
| +    bc1t(nan);
 | 
| +  }
 | 
| +
 | 
| +  if (target) {
 | 
| +    // Here NaN cases were either handled by this function or are assumed to
 | 
| +    // have been handled by the caller.
 | 
| +    // Unsigned conditions are treated as their signed counterpart.
 | 
| +    switch (cc) {
 | 
| +      case Uless:
 | 
| +      case less:
 | 
| +        c(OLT, D, cmp1, cmp2);
 | 
| +        bc1t(target);
 | 
| +        break;
 | 
| +      case Ugreater:
 | 
| +      case greater:
 | 
| +        c(ULE, D, cmp1, cmp2);
 | 
| +        bc1f(target);
 | 
| +        break;
 | 
| +      case Ugreater_equal:
 | 
| +      case greater_equal:
 | 
| +        c(ULT, D, cmp1, cmp2);
 | 
| +        bc1f(target);
 | 
| +        break;
 | 
| +      case Uless_equal:
 | 
| +      case less_equal:
 | 
| +        c(OLE, D, cmp1, cmp2);
 | 
| +        bc1t(target);
 | 
| +        break;
 | 
| +      case eq:
 | 
| +        c(EQ, D, cmp1, cmp2);
 | 
| +        bc1t(target);
 | 
| +        break;
 | 
| +      case ne:
 | 
| +        c(EQ, D, cmp1, cmp2);
 | 
| +        bc1f(target);
 | 
| +        break;
 | 
| +      default:
 | 
| +        CHECK(0);
 | 
| +    };
 | 
| +  }
 | 
| +
 | 
| +  if (bd == PROTECT) {
 | 
| +    nop();
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
| +void MacroAssembler::Move(FPURegister dst, double imm) {
 | 
| +  ASSERT(CpuFeatures::IsEnabled(FPU));
 | 
| +  static const DoubleRepresentation minus_zero(-0.0);
 | 
| +  static const DoubleRepresentation zero(0.0);
 | 
| +  DoubleRepresentation value(imm);
 | 
| +  // Handle special values first.
 | 
| +  bool force_load = dst.is(kDoubleRegZero);
 | 
| +  if (value.bits == zero.bits && !force_load) {
 | 
| +    mov_d(dst, kDoubleRegZero);
 | 
| +  } else if (value.bits == minus_zero.bits && !force_load) {
 | 
| +    neg_d(dst, kDoubleRegZero);
 | 
| +  } else {
 | 
| +    uint32_t lo, hi;
 | 
| +    DoubleAsTwoUInt32(imm, &lo, &hi);
 | 
| +    // Move the low part of the double into the lower of the corresponding FPU
 | 
| +    // register of FPU register pair.
 | 
| +    if (lo != 0) {
 | 
| +      li(at, Operand(lo));
 | 
| +      mtc1(at, dst);
 | 
| +    } else {
 | 
| +      mtc1(zero_reg, dst);
 | 
| +    }
 | 
| +    // Move the high part of the double into the higher of the corresponding FPU
 | 
| +    // register of FPU register pair.
 | 
| +    if (hi != 0) {
 | 
| +      li(at, Operand(hi));
 | 
| +      mtc1(at, dst.high());
 | 
| +    } else {
 | 
| +      mtc1(zero_reg, dst.high());
 | 
| +    }
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
|  // Tries to get a signed int32 out of a double precision floating point heap
 | 
|  // number. Rounds towards 0. Branch to 'not_int32' if the double is out of the
 | 
|  // 32bits signed integer range.
 | 
| @@ -1062,6 +1175,53 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +void MacroAssembler::EmitFPUTruncate(FPURoundingMode rounding_mode,
 | 
| +                                     FPURegister result,
 | 
| +                                     DoubleRegister double_input,
 | 
| +                                     Register scratch1,
 | 
| +                                     Register except_flag,
 | 
| +                                     CheckForInexactConversion check_inexact) {
 | 
| +  ASSERT(CpuFeatures::IsSupported(FPU));
 | 
| +  CpuFeatures::Scope scope(FPU);
 | 
| +
 | 
| +  int32_t except_mask = kFCSRFlagMask;  // Assume interested in all exceptions.
 | 
| +
 | 
| +  if (check_inexact == kDontCheckForInexactConversion) {
 | 
| +    // Ingore inexact exceptions.
 | 
| +    except_mask &= ~kFCSRInexactFlagMask;
 | 
| +  }
 | 
| +
 | 
| +  // Save FCSR.
 | 
| +  cfc1(scratch1, FCSR);
 | 
| +  // Disable FPU exceptions.
 | 
| +  ctc1(zero_reg, FCSR);
 | 
| +
 | 
| +  // Do operation based on rounding mode.
 | 
| +  switch (rounding_mode) {
 | 
| +    case kRoundToNearest:
 | 
| +      round_w_d(result, double_input);
 | 
| +      break;
 | 
| +    case kRoundToZero:
 | 
| +      trunc_w_d(result, double_input);
 | 
| +      break;
 | 
| +    case kRoundToPlusInf:
 | 
| +      ceil_w_d(result, double_input);
 | 
| +      break;
 | 
| +    case kRoundToMinusInf:
 | 
| +      floor_w_d(result, double_input);
 | 
| +      break;
 | 
| +  }  // End of switch-statement.
 | 
| +
 | 
| +  // Retrieve FCSR.
 | 
| +  cfc1(except_flag, FCSR);
 | 
| +  // Restore FCSR.
 | 
| +  ctc1(scratch1, FCSR);
 | 
| +
 | 
| +  // Check for fpu exceptions.
 | 
| +  And(except_flag, except_flag, Operand(except_mask));
 | 
| +}
 | 
| +
 | 
| +
 | 
|  void MacroAssembler::EmitOutOfInt32RangeTruncate(Register result,
 | 
|                                                   Register input_high,
 | 
|                                                   Register input_low,
 | 
| @@ -1148,22 +1308,21 @@
 | 
|                                        FPURegister double_input,
 | 
|                                        FPURegister single_scratch,
 | 
|                                        Register scratch,
 | 
| -                                      Register input_high,
 | 
| -                                      Register input_low) {
 | 
| +                                      Register scratch2,
 | 
| +                                      Register scratch3) {
 | 
|    CpuFeatures::Scope scope(FPU);
 | 
| -  ASSERT(!input_high.is(result));
 | 
| -  ASSERT(!input_low.is(result));
 | 
| -  ASSERT(!input_low.is(input_high));
 | 
| +  ASSERT(!scratch2.is(result));
 | 
| +  ASSERT(!scratch3.is(result));
 | 
| +  ASSERT(!scratch3.is(scratch2));
 | 
|    ASSERT(!scratch.is(result) &&
 | 
| -         !scratch.is(input_high) &&
 | 
| -         !scratch.is(input_low));
 | 
| +         !scratch.is(scratch2) &&
 | 
| +         !scratch.is(scratch3));
 | 
|    ASSERT(!single_scratch.is(double_input));
 | 
|  
 | 
|    Label done;
 | 
|    Label manual;
 | 
|  
 | 
|    // Clear cumulative exception flags and save the FCSR.
 | 
| -  Register scratch2 = input_high;
 | 
|    cfc1(scratch2, FCSR);
 | 
|    ctc1(zero_reg, FCSR);
 | 
|    // Try a conversion to a signed integer.
 | 
| @@ -1180,6 +1339,8 @@
 | 
|    Branch(&done, eq, scratch, Operand(zero_reg));
 | 
|  
 | 
|    // Load the double value and perform a manual truncation.
 | 
| +  Register input_high = scratch2;
 | 
| +  Register input_low = scratch3;
 | 
|    Move(input_low, input_high, double_input);
 | 
|    EmitOutOfInt32RangeTruncate(result,
 | 
|                                input_high,
 | 
| @@ -1211,15 +1372,6 @@
 | 
|      (cond != cc_always && (!rs.is(zero_reg) || !rt.rm().is(zero_reg))))
 | 
|  
 | 
|  
 | 
| -bool MacroAssembler::UseAbsoluteCodePointers() {
 | 
| -  if (is_trampoline_emitted()) {
 | 
| -    return true;
 | 
| -  } else {
 | 
| -    return false;
 | 
| -  }
 | 
| -}
 | 
| -
 | 
| -
 | 
|  void MacroAssembler::Branch(int16_t offset, BranchDelaySlot bdslot) {
 | 
|    BranchShort(offset, bdslot);
 | 
|  }
 | 
| @@ -1233,11 +1385,18 @@
 | 
|  
 | 
|  
 | 
|  void MacroAssembler::Branch(Label* L, BranchDelaySlot bdslot) {
 | 
| -  bool is_label_near = is_near(L);
 | 
| -  if (UseAbsoluteCodePointers() && !is_label_near) {
 | 
| -    Jr(L, bdslot);
 | 
| +  if (L->is_bound()) {
 | 
| +    if (is_near(L)) {
 | 
| +      BranchShort(L, bdslot);
 | 
| +    } else {
 | 
| +      Jr(L, bdslot);
 | 
| +    }
 | 
|    } else {
 | 
| -    BranchShort(L, bdslot);
 | 
| +    if (is_trampoline_emitted()) {
 | 
| +      Jr(L, bdslot);
 | 
| +    } else {
 | 
| +      BranchShort(L, bdslot);
 | 
| +    }
 | 
|    }
 | 
|  }
 | 
|  
 | 
| @@ -1245,15 +1404,26 @@
 | 
|  void MacroAssembler::Branch(Label* L, Condition cond, Register rs,
 | 
|                              const Operand& rt,
 | 
|                              BranchDelaySlot bdslot) {
 | 
| -  bool is_label_near = is_near(L);
 | 
| -  if (UseAbsoluteCodePointers() && !is_label_near) {
 | 
| -    Label skip;
 | 
| -    Condition neg_cond = NegateCondition(cond);
 | 
| -    BranchShort(&skip, neg_cond, rs, rt);
 | 
| -    Jr(L, bdslot);
 | 
| -    bind(&skip);
 | 
| +  if (L->is_bound()) {
 | 
| +    if (is_near(L)) {
 | 
| +      BranchShort(L, cond, rs, rt, bdslot);
 | 
| +    } else {
 | 
| +      Label skip;
 | 
| +      Condition neg_cond = NegateCondition(cond);
 | 
| +      BranchShort(&skip, neg_cond, rs, rt);
 | 
| +      Jr(L, bdslot);
 | 
| +      bind(&skip);
 | 
| +    }
 | 
|    } else {
 | 
| -    BranchShort(L, cond, rs, rt, bdslot);
 | 
| +    if (is_trampoline_emitted()) {
 | 
| +      Label skip;
 | 
| +      Condition neg_cond = NegateCondition(cond);
 | 
| +      BranchShort(&skip, neg_cond, rs, rt);
 | 
| +      Jr(L, bdslot);
 | 
| +      bind(&skip);
 | 
| +    } else {
 | 
| +      BranchShort(L, cond, rs, rt, bdslot);
 | 
| +    }
 | 
|    }
 | 
|  }
 | 
|  
 | 
| @@ -1276,8 +1446,8 @@
 | 
|    Register scratch = at;
 | 
|  
 | 
|    if (rt.is_reg()) {
 | 
| -    // We don't want any other register but scratch clobbered.
 | 
| -    ASSERT(!scratch.is(rs) && !scratch.is(rt.rm_));
 | 
| +    // NOTE: 'at' can be clobbered by Branch but it is legal to use it as rs or
 | 
| +    // rt.
 | 
|      r2 = rt.rm_;
 | 
|      switch (cond) {
 | 
|        case cc_always:
 | 
| @@ -1779,11 +1949,18 @@
 | 
|  
 | 
|  
 | 
|  void MacroAssembler::BranchAndLink(Label* L, BranchDelaySlot bdslot) {
 | 
| -  bool is_label_near = is_near(L);
 | 
| -  if (UseAbsoluteCodePointers() && !is_label_near) {
 | 
| -    Jalr(L, bdslot);
 | 
| +  if (L->is_bound()) {
 | 
| +    if (is_near(L)) {
 | 
| +      BranchAndLinkShort(L, bdslot);
 | 
| +    } else {
 | 
| +      Jalr(L, bdslot);
 | 
| +    }
 | 
|    } else {
 | 
| -    BranchAndLinkShort(L, bdslot);
 | 
| +    if (is_trampoline_emitted()) {
 | 
| +      Jalr(L, bdslot);
 | 
| +    } else {
 | 
| +      BranchAndLinkShort(L, bdslot);
 | 
| +    }
 | 
|    }
 | 
|  }
 | 
|  
 | 
| @@ -1791,15 +1968,26 @@
 | 
|  void MacroAssembler::BranchAndLink(Label* L, Condition cond, Register rs,
 | 
|                                     const Operand& rt,
 | 
|                                     BranchDelaySlot bdslot) {
 | 
| -  bool is_label_near = is_near(L);
 | 
| -  if (UseAbsoluteCodePointers() && !is_label_near) {
 | 
| -    Label skip;
 | 
| -    Condition neg_cond = NegateCondition(cond);
 | 
| -    BranchShort(&skip, neg_cond, rs, rt);
 | 
| -    Jalr(L, bdslot);
 | 
| -    bind(&skip);
 | 
| +  if (L->is_bound()) {
 | 
| +    if (is_near(L)) {
 | 
| +      BranchAndLinkShort(L, cond, rs, rt, bdslot);
 | 
| +    } else {
 | 
| +      Label skip;
 | 
| +      Condition neg_cond = NegateCondition(cond);
 | 
| +      BranchShort(&skip, neg_cond, rs, rt);
 | 
| +      Jalr(L, bdslot);
 | 
| +      bind(&skip);
 | 
| +    }
 | 
|    } else {
 | 
| -    BranchAndLinkShort(L, cond, rs, rt, bdslot);
 | 
| +    if (is_trampoline_emitted()) {
 | 
| +      Label skip;
 | 
| +      Condition neg_cond = NegateCondition(cond);
 | 
| +      BranchShort(&skip, neg_cond, rs, rt);
 | 
| +      Jalr(L, bdslot);
 | 
| +      bind(&skip);
 | 
| +    } else {
 | 
| +      BranchAndLinkShort(L, cond, rs, rt, bdslot);
 | 
| +    }
 | 
|    }
 | 
|  }
 | 
|  
 | 
| @@ -2306,10 +2494,10 @@
 | 
|  #ifdef ENABLE_DEBUGGER_SUPPORT
 | 
|  
 | 
|  void MacroAssembler::DebugBreak() {
 | 
| -  ASSERT(allow_stub_calls());
 | 
|    mov(a0, zero_reg);
 | 
|    li(a1, Operand(ExternalReference(Runtime::kDebugBreak, isolate())));
 | 
|    CEntryStub ces(1);
 | 
| +  ASSERT(AllowThisStubCall(&ces));
 | 
|    Call(ces.GetCode(), RelocInfo::DEBUG_BREAK);
 | 
|  }
 | 
|  
 | 
| @@ -2975,7 +3163,8 @@
 | 
|  void MacroAssembler::CheckFastElements(Register map,
 | 
|                                         Register scratch,
 | 
|                                         Label* fail) {
 | 
| -  STATIC_ASSERT(FAST_ELEMENTS == 0);
 | 
| +  STATIC_ASSERT(FAST_SMI_ONLY_ELEMENTS == 0);
 | 
| +  STATIC_ASSERT(FAST_ELEMENTS == 1);
 | 
|    lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset));
 | 
|    Branch(fail, hi, scratch, Operand(Map::kMaximumBitField2FastElementValue));
 | 
|  }
 | 
| @@ -3171,13 +3360,18 @@
 | 
|                                  InvokeFlag flag,
 | 
|                                  const CallWrapper& call_wrapper,
 | 
|                                  CallKind call_kind) {
 | 
| +  // You can't call a function without a valid frame.
 | 
| +  ASSERT(flag == JUMP_FUNCTION || has_frame());
 | 
| +
 | 
|    Label done;
 | 
|  
 | 
|    InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag,
 | 
|                   call_wrapper, call_kind);
 | 
|    if (flag == CALL_FUNCTION) {
 | 
| +    call_wrapper.BeforeCall(CallSize(code));
 | 
|      SetCallKind(t1, call_kind);
 | 
|      Call(code);
 | 
| +    call_wrapper.AfterCall();
 | 
|    } else {
 | 
|      ASSERT(flag == JUMP_FUNCTION);
 | 
|      SetCallKind(t1, call_kind);
 | 
| @@ -3195,6 +3389,9 @@
 | 
|                                  RelocInfo::Mode rmode,
 | 
|                                  InvokeFlag flag,
 | 
|                                  CallKind call_kind) {
 | 
| +  // You can't call a function without a valid frame.
 | 
| +  ASSERT(flag == JUMP_FUNCTION || has_frame());
 | 
| +
 | 
|    Label done;
 | 
|  
 | 
|    InvokePrologue(expected, actual, code, no_reg, &done, flag,
 | 
| @@ -3217,6 +3414,9 @@
 | 
|                                      InvokeFlag flag,
 | 
|                                      const CallWrapper& call_wrapper,
 | 
|                                      CallKind call_kind) {
 | 
| +  // You can't call a function without a valid frame.
 | 
| +  ASSERT(flag == JUMP_FUNCTION || has_frame());
 | 
| +
 | 
|    // Contract with called JS functions requires that function is passed in a1.
 | 
|    ASSERT(function.is(a1));
 | 
|    Register expected_reg = a2;
 | 
| @@ -3239,6 +3439,9 @@
 | 
|                                      const ParameterCount& actual,
 | 
|                                      InvokeFlag flag,
 | 
|                                      CallKind call_kind) {
 | 
| +  // You can't call a function without a valid frame.
 | 
| +  ASSERT(flag == JUMP_FUNCTION || has_frame());
 | 
| +
 | 
|    ASSERT(function->is_compiled());
 | 
|  
 | 
|    // Get the function and setup the context.
 | 
| @@ -3249,7 +3452,11 @@
 | 
|    Handle<Code> code(function->code());
 | 
|    ParameterCount expected(function->shared()->formal_parameter_count());
 | 
|    if (V8::UseCrankshaft()) {
 | 
| -    UNIMPLEMENTED_MIPS();
 | 
| +    // TODO(kasperl): For now, we always call indirectly through the
 | 
| +    // code field in the function to allow recompilation to take effect
 | 
| +    // without changing any of the call sites.
 | 
| +    lw(a3, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
 | 
| +    InvokeCode(a3, expected, actual, flag, NullCallWrapper(), call_kind);
 | 
|    } else {
 | 
|      InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag, call_kind);
 | 
|    }
 | 
| @@ -3349,14 +3556,14 @@
 | 
|  
 | 
|  void MacroAssembler::CallStub(CodeStub* stub, Condition cond,
 | 
|                                Register r1, const Operand& r2) {
 | 
| -  ASSERT(allow_stub_calls());  // Stub calls are not allowed in some stubs.
 | 
| +  ASSERT(AllowThisStubCall(stub));  // Stub calls are not allowed in some stubs.
 | 
|    Call(stub->GetCode(), RelocInfo::CODE_TARGET, kNoASTId, cond, r1, r2);
 | 
|  }
 | 
|  
 | 
|  
 | 
|  MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub, Condition cond,
 | 
|                                           Register r1, const Operand& r2) {
 | 
| -  ASSERT(allow_stub_calls());  // Stub calls are not allowed in some stubs.
 | 
| +  ASSERT(AllowThisStubCall(stub));  // Stub calls are not allowed in some stubs.
 | 
|    Object* result;
 | 
|    { MaybeObject* maybe_result = stub->TryGetCode();
 | 
|      if (!maybe_result->ToObject(&result)) return maybe_result;
 | 
| @@ -3368,7 +3575,7 @@
 | 
|  
 | 
|  
 | 
|  void MacroAssembler::TailCallStub(CodeStub* stub) {
 | 
| -  ASSERT(allow_stub_calls());  // Stub calls are not allowed in some stubs.
 | 
| +  ASSERT(allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe());
 | 
|    Jump(stub->GetCode(), RelocInfo::CODE_TARGET);
 | 
|  }
 | 
|  
 | 
| @@ -3377,7 +3584,6 @@
 | 
|                                               Condition cond,
 | 
|                                               Register r1,
 | 
|                                               const Operand& r2) {
 | 
| -  ASSERT(allow_stub_calls());  // Stub calls are not allowed in some stubs.
 | 
|    Object* result;
 | 
|    { MaybeObject* maybe_result = stub->TryGetCode();
 | 
|      if (!maybe_result->ToObject(&result)) return maybe_result;
 | 
| @@ -3486,6 +3692,12 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +bool MacroAssembler::AllowThisStubCall(CodeStub* stub) {
 | 
| +  if (!has_frame_ && stub->SometimesSetsUpAFrame()) return false;
 | 
| +  return allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe();
 | 
| +}
 | 
| +
 | 
| +
 | 
|  void MacroAssembler::IllegalOperation(int num_arguments) {
 | 
|    if (num_arguments > 0) {
 | 
|      addiu(sp, sp, num_arguments * kPointerSize);
 | 
| @@ -3566,8 +3778,17 @@
 | 
|    ASSERT(!overflow_dst.is(scratch));
 | 
|    ASSERT(!overflow_dst.is(left));
 | 
|    ASSERT(!overflow_dst.is(right));
 | 
| -  ASSERT(!left.is(right));
 | 
|  
 | 
| +  if (left.is(right) && dst.is(left)) {
 | 
| +    ASSERT(!dst.is(t9));
 | 
| +    ASSERT(!scratch.is(t9));
 | 
| +    ASSERT(!left.is(t9));
 | 
| +    ASSERT(!right.is(t9));
 | 
| +    ASSERT(!overflow_dst.is(t9));
 | 
| +    mov(t9, right);
 | 
| +    right = t9;
 | 
| +  }
 | 
| +
 | 
|    if (dst.is(left)) {
 | 
|      mov(scratch, left);  // Preserve left.
 | 
|      addu(dst, left, right);  // Left is overwritten.
 | 
| @@ -3599,10 +3820,17 @@
 | 
|    ASSERT(!overflow_dst.is(scratch));
 | 
|    ASSERT(!overflow_dst.is(left));
 | 
|    ASSERT(!overflow_dst.is(right));
 | 
| -  ASSERT(!left.is(right));
 | 
|    ASSERT(!scratch.is(left));
 | 
|    ASSERT(!scratch.is(right));
 | 
|  
 | 
| +  // This happens with some crankshaft code. Since Subu works fine if
 | 
| +  // left == right, let's not make that restriction here.
 | 
| +  if (left.is(right)) {
 | 
| +    mov(dst, zero_reg);
 | 
| +    mov(overflow_dst, zero_reg);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
|    if (dst.is(left)) {
 | 
|      mov(scratch, left);  // Preserve left.
 | 
|      subu(dst, left, right);  // Left is overwritten.
 | 
| @@ -3722,6 +3950,9 @@
 | 
|  void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
 | 
|                                     InvokeFlag flag,
 | 
|                                     const CallWrapper& call_wrapper) {
 | 
| +  // You can't call a builtin without a valid frame.
 | 
| +  ASSERT(flag == JUMP_FUNCTION || has_frame());
 | 
| +
 | 
|    GetBuiltinEntry(t9, id);
 | 
|    if (flag == CALL_FUNCTION) {
 | 
|      call_wrapper.BeforeCall(CallSize(t9));
 | 
| @@ -3854,14 +4085,20 @@
 | 
|      RecordComment(msg);
 | 
|    }
 | 
|  #endif
 | 
| -  // Disable stub call restrictions to always allow calls to abort.
 | 
| -  AllowStubCallsScope allow_scope(this, true);
 | 
|  
 | 
|    li(a0, Operand(p0));
 | 
|    push(a0);
 | 
|    li(a0, Operand(Smi::FromInt(p1 - p0)));
 | 
|    push(a0);
 | 
| -  CallRuntime(Runtime::kAbort, 2);
 | 
| +  // Disable stub call restrictions to always allow calls to abort.
 | 
| +  if (!has_frame_) {
 | 
| +    // We don't actually want to generate a pile of code for this, so just
 | 
| +    // claim there is a stack frame, without generating one.
 | 
| +    FrameScope scope(this, StackFrame::NONE);
 | 
| +    CallRuntime(Runtime::kAbort, 2);
 | 
| +  } else {
 | 
| +    CallRuntime(Runtime::kAbort, 2);
 | 
| +  }
 | 
|    // Will not return here.
 | 
|    if (is_trampoline_pool_blocked()) {
 | 
|      // If the calling code cares about the exact number of
 | 
| @@ -4245,7 +4482,23 @@
 | 
|  
 | 
|  static const int kRegisterPassedArguments = 4;
 | 
|  
 | 
| -void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
 | 
| +int MacroAssembler::CalculateStackPassedWords(int num_reg_arguments,
 | 
| +                                              int num_double_arguments) {
 | 
| +  int stack_passed_words = 0;
 | 
| +  num_reg_arguments += 2 * num_double_arguments;
 | 
| +
 | 
| +  // Up to four simple arguments are passed in registers a0..a3.
 | 
| +  if (num_reg_arguments > kRegisterPassedArguments) {
 | 
| +    stack_passed_words += num_reg_arguments - kRegisterPassedArguments;
 | 
| +  }
 | 
| +  stack_passed_words += kCArgSlotCount;
 | 
| +  return stack_passed_words;
 | 
| +}
 | 
| +
 | 
| +
 | 
| +void MacroAssembler::PrepareCallCFunction(int num_reg_arguments,
 | 
| +                                          int num_double_arguments,
 | 
| +                                          Register scratch) {
 | 
|    int frame_alignment = ActivationFrameAlignment();
 | 
|  
 | 
|    // Up to four simple arguments are passed in registers a0..a3.
 | 
| @@ -4253,9 +4506,8 @@
 | 
|    // mips, even though those argument slots are not normally used.
 | 
|    // Remaining arguments are pushed on the stack, above (higher address than)
 | 
|    // the argument slots.
 | 
| -  int stack_passed_arguments = ((num_arguments <= kRegisterPassedArguments) ?
 | 
| -                                 0 : num_arguments - kRegisterPassedArguments) +
 | 
| -                                kCArgSlotCount;
 | 
| +  int stack_passed_arguments = CalculateStackPassedWords(
 | 
| +      num_reg_arguments, num_double_arguments);
 | 
|    if (frame_alignment > kPointerSize) {
 | 
|      // Make stack end at alignment and make room for num_arguments - 4 words
 | 
|      // and the original value of sp.
 | 
| @@ -4270,26 +4522,54 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +void MacroAssembler::PrepareCallCFunction(int num_reg_arguments,
 | 
| +                                          Register scratch) {
 | 
| +  PrepareCallCFunction(num_reg_arguments, 0, scratch);
 | 
| +}
 | 
| +
 | 
| +
 | 
|  void MacroAssembler::CallCFunction(ExternalReference function,
 | 
| -                                   int num_arguments) {
 | 
| -  CallCFunctionHelper(no_reg, function, t8, num_arguments);
 | 
| +                                   int num_reg_arguments,
 | 
| +                                   int num_double_arguments) {
 | 
| +  CallCFunctionHelper(no_reg,
 | 
| +                      function,
 | 
| +                      t8,
 | 
| +                      num_reg_arguments,
 | 
| +                      num_double_arguments);
 | 
|  }
 | 
|  
 | 
|  
 | 
|  void MacroAssembler::CallCFunction(Register function,
 | 
|                                     Register scratch,
 | 
| -                                   int num_arguments) {
 | 
| +                                   int num_reg_arguments,
 | 
| +                                   int num_double_arguments) {
 | 
|    CallCFunctionHelper(function,
 | 
|                        ExternalReference::the_hole_value_location(isolate()),
 | 
|                        scratch,
 | 
| -                      num_arguments);
 | 
| +                      num_reg_arguments,
 | 
| +                      num_double_arguments);
 | 
|  }
 | 
|  
 | 
|  
 | 
| +void MacroAssembler::CallCFunction(ExternalReference function,
 | 
| +                                   int num_arguments) {
 | 
| +  CallCFunction(function, num_arguments, 0);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +void MacroAssembler::CallCFunction(Register function,
 | 
| +                                   Register scratch,
 | 
| +                                   int num_arguments) {
 | 
| +  CallCFunction(function, scratch, num_arguments, 0);
 | 
| +}
 | 
| +
 | 
| +
 | 
|  void MacroAssembler::CallCFunctionHelper(Register function,
 | 
|                                           ExternalReference function_reference,
 | 
|                                           Register scratch,
 | 
| -                                         int num_arguments) {
 | 
| +                                         int num_reg_arguments,
 | 
| +                                         int num_double_arguments) {
 | 
| +  ASSERT(has_frame());
 | 
|    // Make sure that the stack is aligned before calling a C function unless
 | 
|    // running in the simulator. The simulator has its own alignment check which
 | 
|    // provides more information.
 | 
| @@ -4327,9 +4607,8 @@
 | 
|  
 | 
|    Call(function);
 | 
|  
 | 
| -  int stack_passed_arguments = ((num_arguments <= kRegisterPassedArguments) ?
 | 
| -                                0 : num_arguments - kRegisterPassedArguments) +
 | 
| -                               kCArgSlotCount;
 | 
| +  int stack_passed_arguments = CalculateStackPassedWords(
 | 
| +      num_reg_arguments, num_double_arguments);
 | 
|  
 | 
|    if (OS::ActivationFrameAlignment() > kPointerSize) {
 | 
|      lw(sp, MemOperand(sp, stack_passed_arguments * kPointerSize));
 | 
| @@ -4342,6 +4621,37 @@
 | 
|  #undef BRANCH_ARGS_CHECK
 | 
|  
 | 
|  
 | 
| +void MacroAssembler::PatchRelocatedValue(Register li_location,
 | 
| +                                         Register scratch,
 | 
| +                                         Register new_value) {
 | 
| +  lw(scratch, MemOperand(li_location));
 | 
| +  // At this point scratch is a lui(at, ...) instruction.
 | 
| +  if (emit_debug_code()) {
 | 
| +    And(scratch, scratch, kOpcodeMask);
 | 
| +    Check(eq, "The instruction to patch should be a lui.",
 | 
| +        scratch, Operand(LUI));
 | 
| +    lw(scratch, MemOperand(li_location));
 | 
| +  }
 | 
| +  srl(t9, new_value, kImm16Bits);
 | 
| +  Ins(scratch, t9, 0, kImm16Bits);
 | 
| +  sw(scratch, MemOperand(li_location));
 | 
| +
 | 
| +  lw(scratch, MemOperand(li_location, kInstrSize));
 | 
| +  // scratch is now ori(at, ...).
 | 
| +  if (emit_debug_code()) {
 | 
| +    And(scratch, scratch, kOpcodeMask);
 | 
| +    Check(eq, "The instruction to patch should be an ori.",
 | 
| +        scratch, Operand(ORI));
 | 
| +    lw(scratch, MemOperand(li_location, kInstrSize));
 | 
| +  }
 | 
| +  Ins(scratch, new_value, 0, kImm16Bits);
 | 
| +  sw(scratch, MemOperand(li_location, kInstrSize));
 | 
| +
 | 
| +  // Update the I-cache so the new lui and ori can be executed.
 | 
| +  FlushICache(li_location, 2);
 | 
| +}
 | 
| +
 | 
| +
 | 
|  void MacroAssembler::LoadInstanceDescriptors(Register map,
 | 
|                                               Register descriptors) {
 | 
|    lw(descriptors,
 | 
| @@ -4353,6 +4663,49 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +void MacroAssembler::ClampUint8(Register output_reg, Register input_reg) {
 | 
| +  ASSERT(!output_reg.is(input_reg));
 | 
| +  Label done;
 | 
| +  li(output_reg, Operand(255));
 | 
| +  // Normal branch: nop in delay slot.
 | 
| +  Branch(&done, gt, input_reg, Operand(output_reg));
 | 
| +  // Use delay slot in this branch.
 | 
| +  Branch(USE_DELAY_SLOT, &done, lt, input_reg, Operand(zero_reg));
 | 
| +  mov(output_reg, zero_reg);  // In delay slot.
 | 
| +  mov(output_reg, input_reg);  // Value is in range 0..255.
 | 
| +  bind(&done);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +void MacroAssembler::ClampDoubleToUint8(Register result_reg,
 | 
| +                                        DoubleRegister input_reg,
 | 
| +                                        DoubleRegister temp_double_reg) {
 | 
| +  Label above_zero;
 | 
| +  Label done;
 | 
| +  Label in_bounds;
 | 
| +
 | 
| +  Move(temp_double_reg, 0.0);
 | 
| +  BranchF(&above_zero, NULL, gt, input_reg, temp_double_reg);
 | 
| +
 | 
| +  // Double value is less than zero, NaN or Inf, return 0.
 | 
| +  mov(result_reg, zero_reg);
 | 
| +  Branch(&done);
 | 
| +
 | 
| +  // Double value is >= 255, return 255.
 | 
| +  bind(&above_zero);
 | 
| +  Move(temp_double_reg, 255.0);
 | 
| +  BranchF(&in_bounds, NULL, le, input_reg, temp_double_reg);
 | 
| +  li(result_reg, Operand(255));
 | 
| +  Branch(&done);
 | 
| +
 | 
| +  // In 0-255 range, round and truncate.
 | 
| +  bind(&in_bounds);
 | 
| +  round_w_d(temp_double_reg, input_reg);
 | 
| +  mfc1(result_reg, temp_double_reg);
 | 
| +  bind(&done);
 | 
| +}
 | 
| +
 | 
| +
 | 
|  CodePatcher::CodePatcher(byte* address, int instructions)
 | 
|      : address_(address),
 | 
|        instructions_(instructions),
 | 
| 
 |