| Index: src/mips/macro-assembler-mips.cc
|
| diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc
|
| index f24ed6ff0e2cf68e88ea80be7198cb3ebad1cb3a..e95335d74925821ce904e15a4a2ff6ad5e50f9db 100644
|
| --- a/src/mips/macro-assembler-mips.cc
|
| +++ b/src/mips/macro-assembler-mips.cc
|
| @@ -25,29 +25,32 @@
|
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
| -#include <limits.h> // For LONG_MIN, LONG_MAX
|
| +#include <limits.h> // For LONG_MIN, LONG_MAX.
|
|
|
| #include "v8.h"
|
|
|
| #if defined(V8_TARGET_ARCH_MIPS)
|
|
|
| #include "bootstrapper.h"
|
| -#include "codegen-inl.h"
|
| +#include "codegen.h"
|
| #include "debug.h"
|
| #include "runtime.h"
|
|
|
| namespace v8 {
|
| namespace internal {
|
|
|
| -MacroAssembler::MacroAssembler(void* buffer, int size)
|
| - : Assembler(buffer, size),
|
| +MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size)
|
| + : Assembler(arg_isolate, buffer, size),
|
| generating_stub_(false),
|
| - allow_stub_calls_(true),
|
| - code_object_(HEAP->undefined_value()) {
|
| + allow_stub_calls_(true) {
|
| + if (isolate() != NULL) {
|
| + code_object_ = Handle<Object>(isolate()->heap()->undefined_value(),
|
| + isolate());
|
| + }
|
| }
|
|
|
|
|
| -// Arguments macros
|
| +// Arguments macros.
|
| #define COND_TYPED_ARGS Condition cond, Register r1, const Operand& r2
|
| #define COND_ARGS cond, r1, r2
|
|
|
| @@ -161,7 +164,7 @@ void MacroAssembler::StoreRoot(Register source,
|
| void MacroAssembler::RecordWriteHelper(Register object,
|
| Register address,
|
| Register scratch) {
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| // Check that the object is not in new space.
|
| Label not_in_new_space;
|
| InNewSpace(object, scratch, ne, ¬_in_new_space);
|
| @@ -230,7 +233,7 @@ void MacroAssembler::RecordWrite(Register object,
|
|
|
| // Clobber all input registers when running with the debug-code flag
|
| // turned on to provoke errors.
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| li(object, Operand(BitCast<int32_t>(kZapValue)));
|
| li(scratch0, Operand(BitCast<int32_t>(kZapValue)));
|
| li(scratch1, Operand(BitCast<int32_t>(kZapValue)));
|
| @@ -262,7 +265,7 @@ void MacroAssembler::RecordWrite(Register object,
|
|
|
| // Clobber all input registers when running with the debug-code flag
|
| // turned on to provoke errors.
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| li(object, Operand(BitCast<int32_t>(kZapValue)));
|
| li(address, Operand(BitCast<int32_t>(kZapValue)));
|
| li(scratch, Operand(BitCast<int32_t>(kZapValue)));
|
| @@ -271,7 +274,7 @@ void MacroAssembler::RecordWrite(Register object,
|
|
|
|
|
| // -----------------------------------------------------------------------------
|
| -// Allocation support
|
| +// Allocation support.
|
|
|
|
|
| void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
|
| @@ -297,15 +300,15 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
|
| lw(scratch, FieldMemOperand(scratch, GlobalObject::kGlobalContextOffset));
|
|
|
| // Check the context is a global context.
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| // TODO(119): Avoid push(holder_reg)/pop(holder_reg).
|
| - Push(holder_reg); // Temporarily save holder on the stack.
|
| + push(holder_reg); // Temporarily save holder on the stack.
|
| // Read the first word and compare to the global_context_map.
|
| lw(holder_reg, FieldMemOperand(scratch, HeapObject::kMapOffset));
|
| LoadRoot(at, Heap::kGlobalContextMapRootIndex);
|
| Check(eq, "JSGlobalObject::global_context should be a global context.",
|
| holder_reg, Operand(at));
|
| - Pop(holder_reg); // Restore holder.
|
| + pop(holder_reg); // Restore holder.
|
| }
|
|
|
| // Check if both contexts are the same.
|
| @@ -313,9 +316,9 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
|
| Branch(&same_contexts, eq, scratch, Operand(at));
|
|
|
| // Check the context is a global context.
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| // TODO(119): Avoid push(holder_reg)/pop(holder_reg).
|
| - Push(holder_reg); // Temporarily save holder on the stack.
|
| + push(holder_reg); // Temporarily save holder on the stack.
|
| mov(holder_reg, at); // Move at to its holding place.
|
| LoadRoot(at, Heap::kNullValueRootIndex);
|
| Check(ne, "JSGlobalProxy::context() should not be null.",
|
| @@ -326,7 +329,7 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
|
| Check(eq, "JSGlobalObject::global_context should be a global context.",
|
| holder_reg, Operand(at));
|
| // Restore at is not needed. at is reloaded below.
|
| - Pop(holder_reg); // Restore holder.
|
| + pop(holder_reg); // Restore holder.
|
| // Restore at to holder's context.
|
| lw(at, FieldMemOperand(holder_reg, JSGlobalProxy::kContextOffset));
|
| }
|
| @@ -346,7 +349,7 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
|
|
|
|
|
| // ---------------------------------------------------------------------------
|
| -// Instruction macros
|
| +// Instruction macros.
|
|
|
| void MacroAssembler::Addu(Register rd, Register rs, const Operand& rt) {
|
| if (rt.is_reg()) {
|
| @@ -500,6 +503,15 @@ void MacroAssembler::Nor(Register rd, Register rs, const Operand& rt) {
|
| }
|
|
|
|
|
| +void MacroAssembler::Neg(Register rs, const Operand& rt) {
|
| + ASSERT(rt.is_reg());
|
| + ASSERT(!at.is(rs));
|
| + ASSERT(!at.is(rt.rm()));
|
| + li(at, -1);
|
| + xor_(rs, rt.rm(), at);
|
| +}
|
| +
|
| +
|
| void MacroAssembler::Slt(Register rd, Register rs, const Operand& rt) {
|
| if (rt.is_reg()) {
|
| slt(rd, rs, rt.rm());
|
| @@ -581,24 +593,13 @@ void MacroAssembler::li(Register rd, Operand j, bool gen2instr) {
|
| }
|
| // We need always the same number of instructions as we may need to patch
|
| // this code to load another value which may need 2 instructions to load.
|
| - if (is_int16(j.imm32_)) {
|
| - nop();
|
| - addiu(rd, zero_reg, j.imm32_);
|
| - } else if (!(j.imm32_ & kHiMask)) {
|
| - nop();
|
| - ori(rd, zero_reg, j.imm32_);
|
| - } else if (!(j.imm32_ & kImm16Mask)) {
|
| - nop();
|
| - lui(rd, (j.imm32_ & kHiMask) >> kLuiShift);
|
| - } else {
|
| - lui(rd, (j.imm32_ & kHiMask) >> kLuiShift);
|
| - ori(rd, rd, (j.imm32_ & kImm16Mask));
|
| - }
|
| + lui(rd, (j.imm32_ & kHiMask) >> kLuiShift);
|
| + ori(rd, rd, (j.imm32_ & kImm16Mask));
|
| }
|
| }
|
|
|
|
|
| -// Exception-generating instructions and debugging support
|
| +// Exception-generating instructions and debugging support.
|
| void MacroAssembler::stop(const char* msg) {
|
| // TO_UPGRADE: Just a break for now. Maybe we could upgrade it.
|
| // We use the 0x54321 value to be able to find it easily when reading memory.
|
| @@ -727,11 +728,11 @@ void MacroAssembler::Cvt_d_uw(FPURegister fd, Register rs) {
|
| ASSERT(!rs.is(t9));
|
| ASSERT(!rs.is(t8));
|
|
|
| - // Save rs's MSB to t8
|
| + // Save rs's MSB to t8.
|
| And(t8, rs, 0x80000000);
|
| // Remove rs's MSB.
|
| And(t9, rs, 0x7FFFFFFF);
|
| - // Move t9 to fd
|
| + // Move t9 to fd.
|
| mtc1(t9, fd);
|
|
|
| // Convert fd to a real FP value.
|
| @@ -839,7 +840,7 @@ void MacroAssembler::ConvertToInt32(Register source,
|
| Subu(scratch2, scratch2, Operand(zero_exponent));
|
| // Dest already has a Smi zero.
|
| Branch(&done, lt, scratch2, Operand(zero_reg));
|
| - if (!Isolate::Current()->cpu_features()->IsSupported(FPU)) {
|
| + if (!CpuFeatures::IsSupported(FPU)) {
|
| // We have a shifted exponent between 0 and 30 in scratch2.
|
| srl(dest, scratch2, HeapNumber::kExponentShift);
|
| // We now have the exponent in dest. Subtract from 30 to get
|
| @@ -848,7 +849,7 @@ void MacroAssembler::ConvertToInt32(Register source,
|
| subu(dest, at, dest);
|
| }
|
| bind(&right_exponent);
|
| - if (Isolate::Current()->cpu_features()->IsSupported(FPU)) {
|
| + if (CpuFeatures::IsSupported(FPU)) {
|
| CpuFeatures::Scope scope(FPU);
|
| // MIPS FPU instructions implementing double precision to integer
|
| // conversion using round to zero. Since the FP value was qualified
|
| @@ -898,6 +899,102 @@ void MacroAssembler::ConvertToInt32(Register source,
|
| }
|
|
|
|
|
| +void MacroAssembler::EmitOutOfInt32RangeTruncate(Register result,
|
| + Register input_high,
|
| + Register input_low,
|
| + Register scratch) {
|
| + Label done, normal_exponent, restore_sign;
|
| + // Extract the biased exponent in result.
|
| + Ext(result,
|
| + input_high,
|
| + HeapNumber::kExponentShift,
|
| + HeapNumber::kExponentBits);
|
| +
|
| + // Check for Infinity and NaNs, which should return 0.
|
| + Subu(scratch, result, HeapNumber::kExponentMask);
|
| + movz(result, zero_reg, scratch);
|
| + Branch(&done, eq, scratch, Operand(zero_reg));
|
| +
|
| + // Express exponent as delta to (number of mantissa bits + 31).
|
| + Subu(result,
|
| + result,
|
| + Operand(HeapNumber::kExponentBias + HeapNumber::kMantissaBits + 31));
|
| +
|
| + // If the delta is strictly positive, all bits would be shifted away,
|
| + // which means that we can return 0.
|
| + Branch(&normal_exponent, le, result, Operand(zero_reg));
|
| + mov(result, zero_reg);
|
| + Branch(&done);
|
| +
|
| + bind(&normal_exponent);
|
| + const int kShiftBase = HeapNumber::kNonMantissaBitsInTopWord - 1;
|
| + // Calculate shift.
|
| + Addu(scratch, result, Operand(kShiftBase + HeapNumber::kMantissaBits));
|
| +
|
| + // Save the sign.
|
| + Register sign = result;
|
| + result = no_reg;
|
| + And(sign, input_high, Operand(HeapNumber::kSignMask));
|
| +
|
| + // On ARM shifts > 31 bits are valid and will result in zero. On MIPS we need
|
| + // to check for this specific case.
|
| + Label high_shift_needed, high_shift_done;
|
| + Branch(&high_shift_needed, lt, scratch, Operand(32));
|
| + mov(input_high, zero_reg);
|
| + Branch(&high_shift_done);
|
| + bind(&high_shift_needed);
|
| +
|
| + // Set the implicit 1 before the mantissa part in input_high.
|
| + Or(input_high,
|
| + input_high,
|
| + Operand(1 << HeapNumber::kMantissaBitsInTopWord));
|
| + // Shift the mantissa bits to the correct position.
|
| + // We don't need to clear non-mantissa bits as they will be shifted away.
|
| + // If they weren't, it would mean that the answer is in the 32bit range.
|
| + sllv(input_high, input_high, scratch);
|
| +
|
| + bind(&high_shift_done);
|
| +
|
| + // Replace the shifted bits with bits from the lower mantissa word.
|
| + Label pos_shift, shift_done;
|
| + li(at, 32);
|
| + subu(scratch, at, scratch);
|
| + Branch(&pos_shift, ge, scratch, Operand(zero_reg));
|
| +
|
| + // Negate scratch.
|
| + Subu(scratch, zero_reg, scratch);
|
| + sllv(input_low, input_low, scratch);
|
| + Branch(&shift_done);
|
| +
|
| + bind(&pos_shift);
|
| + srlv(input_low, input_low, scratch);
|
| +
|
| + bind(&shift_done);
|
| + Or(input_high, input_high, Operand(input_low));
|
| + // Restore sign if necessary.
|
| + mov(scratch, sign);
|
| + result = sign;
|
| + sign = no_reg;
|
| + Subu(result, zero_reg, input_high);
|
| + movz(result, input_high, scratch);
|
| + bind(&done);
|
| +}
|
| +
|
| +
|
| +void MacroAssembler::GetLeastBitsFromSmi(Register dst,
|
| + Register src,
|
| + int num_least_bits) {
|
| + Ext(dst, src, kSmiTagSize, num_least_bits);
|
| +}
|
| +
|
| +
|
| +void MacroAssembler::GetLeastBitsFromInt32(Register dst,
|
| + Register src,
|
| + int num_least_bits) {
|
| + And(dst, src, Operand((1 << num_least_bits) - 1));
|
| +}
|
| +
|
| +
|
| // Emulated condtional branches do not emit a nop in the branch delay slot.
|
| //
|
| // BRANCH_ARGS_CHECK checks that conditional jump arguments are correct.
|
| @@ -937,7 +1034,7 @@ void MacroAssembler::Branch(int16_t offset, Condition cond, Register rs,
|
| case ne:
|
| bne(rs, r2, offset);
|
| break;
|
| - // Signed comparison
|
| + // Signed comparison.
|
| case greater:
|
| if (r2.is(zero_reg)) {
|
| bgtz(rs, offset);
|
| @@ -1028,7 +1125,7 @@ void MacroAssembler::Branch(int16_t offset, Condition cond, Register rs,
|
| li(r2, rt);
|
| bne(rs, r2, offset);
|
| break;
|
| - // Signed comparison
|
| + // Signed comparison.
|
| case greater:
|
| if (rt.imm32_ == 0) {
|
| bgtz(rs, offset);
|
| @@ -1170,7 +1267,7 @@ void MacroAssembler::Branch(Label* L, Condition cond, Register rs,
|
| offset = shifted_branch_offset(L, false);
|
| bne(rs, r2, offset);
|
| break;
|
| - // Signed comparison
|
| + // Signed comparison.
|
| case greater:
|
| if (r2.is(zero_reg)) {
|
| offset = shifted_branch_offset(L, false);
|
| @@ -1276,7 +1373,7 @@ void MacroAssembler::Branch(Label* L, Condition cond, Register rs,
|
| offset = shifted_branch_offset(L, false);
|
| bne(rs, r2, offset);
|
| break;
|
| - // Signed comparison
|
| + // Signed comparison.
|
| case greater:
|
| if (rt.imm32_ == 0) {
|
| offset = shifted_branch_offset(L, false);
|
| @@ -1444,7 +1541,7 @@ void MacroAssembler::BranchAndLink(int16_t offset, Condition cond, Register rs,
|
| bal(offset);
|
| break;
|
|
|
| - // Signed comparison
|
| + // Signed comparison.
|
| case greater:
|
| slt(scratch, r2, rs);
|
| addiu(scratch, scratch, -1);
|
| @@ -1539,7 +1636,7 @@ void MacroAssembler::BranchAndLink(Label* L, Condition cond, Register rs,
|
| bal(offset);
|
| break;
|
|
|
| - // Signed comparison
|
| + // Signed comparison.
|
| case greater:
|
| slt(scratch, r2, rs);
|
| addiu(scratch, scratch, -1);
|
| @@ -1642,7 +1739,7 @@ void MacroAssembler::Jump(const Operand& target,
|
| Branch(2, NegateCondition(cond), rs, rt);
|
| j(target.imm32_); // Will generate only one instruction.
|
| }
|
| - } else { // MustUseReg(target)
|
| + } else { // MustUseReg(target).
|
| li(t9, target);
|
| if (cond == cc_always) {
|
| jr(t9);
|
| @@ -1659,14 +1756,12 @@ void MacroAssembler::Jump(const Operand& target,
|
|
|
|
|
| int MacroAssembler::CallSize(Handle<Code> code, RelocInfo::Mode rmode) {
|
| - UNIMPLEMENTED_MIPS();
|
| - return 0;
|
| + return 4 * kInstrSize;
|
| }
|
|
|
|
|
| int MacroAssembler::CallSize(Register reg) {
|
| - UNIMPLEMENTED_MIPS();
|
| - return 0;
|
| + return 2 * kInstrSize;
|
| }
|
|
|
|
|
| @@ -1675,10 +1770,13 @@ void MacroAssembler::Call(const Operand& target, BranchDelaySlot bdslot) {
|
| BlockTrampolinePoolScope block_trampoline_pool(this);
|
| if (target.is_reg()) {
|
| jalr(target.rm());
|
| - } else { // !target.is_reg()
|
| + } else { // !target.is_reg().
|
| if (!MustUseReg(target.rmode_)) {
|
| jal(target.imm32_);
|
| - } else { // MustUseReg(target)
|
| + } else { // MustUseReg(target).
|
| + // Must record previous source positions before the
|
| + // li() generates a new code target.
|
| + positions_recorder()->WriteRecordedPositions();
|
| li(t9, target);
|
| jalr(t9);
|
| }
|
| @@ -1702,7 +1800,7 @@ void MacroAssembler::Call(const Operand& target,
|
| Branch(2, NegateCondition(cond), rs, rt);
|
| jalr(target.rm());
|
| }
|
| - } else { // !target.is_reg()
|
| + } else { // !target.is_reg().
|
| if (!MustUseReg(target.rmode_)) {
|
| if (cond == cc_always) {
|
| jal(target.imm32_);
|
| @@ -1726,6 +1824,20 @@ void MacroAssembler::Call(const Operand& target,
|
| }
|
|
|
|
|
| +void MacroAssembler::CallWithAstId(Handle<Code> code,
|
| + RelocInfo::Mode rmode,
|
| + unsigned ast_id,
|
| + Condition cond,
|
| + Register r1,
|
| + const Operand& r2) {
|
| + ASSERT(rmode == RelocInfo::CODE_TARGET_WITH_ID);
|
| + ASSERT(ast_id != kNoASTId);
|
| + ASSERT(ast_id_for_reloc_info_ == kNoASTId);
|
| + ast_id_for_reloc_info_ = ast_id;
|
| + Call(reinterpret_cast<intptr_t>(code.location()), rmode, cond, r1, r2);
|
| +}
|
| +
|
| +
|
| void MacroAssembler::Drop(int count,
|
| Condition cond,
|
| Register reg,
|
| @@ -1812,7 +1924,7 @@ void MacroAssembler::DebugBreak() {
|
|
|
|
|
| // ---------------------------------------------------------------------------
|
| -// Exception handling
|
| +// Exception handling.
|
|
|
| void MacroAssembler::PushTryHandler(CodeLocation try_location,
|
| HandlerType type) {
|
| @@ -1887,7 +1999,7 @@ void MacroAssembler::AllocateInNewSpace(int object_size,
|
| Label* gc_required,
|
| AllocationFlags flags) {
|
| if (!FLAG_inline_new) {
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| // Trash the registers to simulate an allocation failure.
|
| li(result, 0x7091);
|
| li(scratch1, 0x7191);
|
| @@ -1935,7 +2047,7 @@ void MacroAssembler::AllocateInNewSpace(int object_size,
|
| lw(result, MemOperand(topaddr));
|
| lw(t9, MemOperand(topaddr, kPointerSize));
|
| } else {
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| // Assert that result actually contains top on entry. t9 is used
|
| // immediately below so this use of t9 does not cause difference with
|
| // respect to register content between debug and release mode.
|
| @@ -1966,7 +2078,7 @@ void MacroAssembler::AllocateInNewSpace(Register object_size,
|
| Label* gc_required,
|
| AllocationFlags flags) {
|
| if (!FLAG_inline_new) {
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| // Trash the registers to simulate an allocation failure.
|
| li(result, 0x7091);
|
| li(scratch1, 0x7191);
|
| @@ -2004,7 +2116,7 @@ void MacroAssembler::AllocateInNewSpace(Register object_size,
|
| lw(result, MemOperand(topaddr));
|
| lw(t9, MemOperand(topaddr, kPointerSize));
|
| } else {
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| // Assert that result actually contains top on entry. t9 is used
|
| // immediately below so this use of t9 does not cause difference with
|
| // respect to register content between debug and release mode.
|
| @@ -2027,7 +2139,7 @@ void MacroAssembler::AllocateInNewSpace(Register object_size,
|
| Branch(gc_required, Ugreater, scratch2, Operand(t9));
|
|
|
| // Update allocation top. result temporarily holds the new top.
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| And(t9, scratch2, Operand(kObjectAlignmentMask));
|
| Check(eq, "Unaligned allocation in new space", t9, Operand(zero_reg));
|
| }
|
| @@ -2218,6 +2330,64 @@ void MacroAssembler::CopyFields(Register dst,
|
| }
|
|
|
|
|
| +void MacroAssembler::CopyBytes(Register src,
|
| + Register dst,
|
| + Register length,
|
| + Register scratch) {
|
| + Label align_loop, align_loop_1, word_loop, byte_loop, byte_loop_1, done;
|
| +
|
| + // Align src before copying in word size chunks.
|
| + bind(&align_loop);
|
| + Branch(&done, eq, length, Operand(zero_reg));
|
| + bind(&align_loop_1);
|
| + And(scratch, src, kPointerSize - 1);
|
| + Branch(&word_loop, eq, scratch, Operand(zero_reg));
|
| + lbu(scratch, MemOperand(src));
|
| + Addu(src, src, 1);
|
| + sb(scratch, MemOperand(dst));
|
| + Addu(dst, dst, 1);
|
| + Subu(length, length, Operand(1));
|
| + Branch(&byte_loop_1, ne, length, Operand(zero_reg));
|
| +
|
| + // Copy bytes in word size chunks.
|
| + bind(&word_loop);
|
| + if (FLAG_debug_code) {
|
| + And(scratch, src, kPointerSize - 1);
|
| + Assert(eq, "Expecting alignment for CopyBytes",
|
| + scratch, Operand(zero_reg));
|
| + }
|
| + Branch(&byte_loop, lt, length, Operand(kPointerSize));
|
| + lw(scratch, MemOperand(src));
|
| + Addu(src, src, kPointerSize);
|
| +
|
| + // TODO(kalmard) check if this can be optimized to use sw in most cases.
|
| + // Can't use unaligned access - copy byte by byte.
|
| + sb(scratch, MemOperand(dst, 0));
|
| + srl(scratch, scratch, 8);
|
| + sb(scratch, MemOperand(dst, 1));
|
| + srl(scratch, scratch, 8);
|
| + sb(scratch, MemOperand(dst, 2));
|
| + srl(scratch, scratch, 8);
|
| + sb(scratch, MemOperand(dst, 3));
|
| + Addu(dst, dst, 4);
|
| +
|
| + Subu(length, length, Operand(kPointerSize));
|
| + Branch(&word_loop);
|
| +
|
| + // Copy the last bytes if any left.
|
| + bind(&byte_loop);
|
| + Branch(&done, eq, length, Operand(zero_reg));
|
| + bind(&byte_loop_1);
|
| + lbu(scratch, MemOperand(src));
|
| + Addu(src, src, 1);
|
| + sb(scratch, MemOperand(dst));
|
| + Addu(dst, dst, 1);
|
| + Subu(length, length, Operand(1));
|
| + Branch(&byte_loop_1, ne, length, Operand(zero_reg));
|
| + bind(&done);
|
| +}
|
| +
|
| +
|
| void MacroAssembler::CheckMap(Register obj,
|
| Register scratch,
|
| Handle<Map> map,
|
| @@ -2246,8 +2416,20 @@ void MacroAssembler::CheckMap(Register obj,
|
| }
|
|
|
|
|
| +void MacroAssembler::GetCFunctionDoubleResult(const DoubleRegister dst) {
|
| + if (IsMipsSoftFloatABI) {
|
| + mtc1(v0, dst);
|
| + mtc1(v1, FPURegister::from_code(dst.code() + 1));
|
| + } else {
|
| + if (!dst.is(f0)) {
|
| + mov_d(dst, f0); // Reg f0 is o32 ABI FP return value.
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| // -----------------------------------------------------------------------------
|
| -// JavaScript invokes
|
| +// JavaScript invokes.
|
|
|
| void MacroAssembler::InvokePrologue(const ParameterCount& expected,
|
| const ParameterCount& actual,
|
| @@ -2290,13 +2472,11 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
|
| li(a2, Operand(expected.immediate()));
|
| }
|
| }
|
| + } else if (actual.is_immediate()) {
|
| + Branch(®ular_invoke, eq, expected.reg(), Operand(actual.immediate()));
|
| + li(a0, Operand(actual.immediate()));
|
| } else {
|
| - if (actual.is_immediate()) {
|
| - Branch(®ular_invoke, eq, expected.reg(), Operand(actual.immediate()));
|
| - li(a0, Operand(actual.immediate()));
|
| - } else {
|
| - Branch(®ular_invoke, eq, expected.reg(), Operand(actual.reg()));
|
| - }
|
| + Branch(®ular_invoke, eq, expected.reg(), Operand(actual.reg()));
|
| }
|
|
|
| if (!definitely_matches) {
|
| @@ -2491,7 +2671,7 @@ void MacroAssembler::GetObjectType(Register object,
|
|
|
|
|
| // -----------------------------------------------------------------------------
|
| -// Runtime calls
|
| +// Runtime calls.
|
|
|
| void MacroAssembler::CallStub(CodeStub* stub, Condition cond,
|
| Register r1, const Operand& r2) {
|
| @@ -2501,7 +2681,7 @@ void MacroAssembler::CallStub(CodeStub* stub, Condition cond,
|
|
|
|
|
| void MacroAssembler::TailCallStub(CodeStub* stub) {
|
| - ASSERT(allow_stub_calls()); // stub calls are not allowed in some stubs
|
| + ASSERT(allow_stub_calls()); // Stub calls are not allowed in some stubs.
|
| Jump(stub->GetCode(), RelocInfo::CODE_TARGET);
|
| }
|
|
|
| @@ -2567,7 +2747,6 @@ void MacroAssembler::ObjectToDoubleFPURegister(Register object,
|
| }
|
|
|
|
|
| -
|
| void MacroAssembler::SmiToDoubleFPURegister(Register smi,
|
| FPURegister value,
|
| Register scratch1) {
|
| @@ -2577,6 +2756,84 @@ void MacroAssembler::SmiToDoubleFPURegister(Register smi,
|
| }
|
|
|
|
|
| +void MacroAssembler::AdduAndCheckForOverflow(Register dst,
|
| + Register left,
|
| + Register right,
|
| + Register overflow_dst,
|
| + Register scratch) {
|
| + ASSERT(!dst.is(overflow_dst));
|
| + ASSERT(!dst.is(scratch));
|
| + ASSERT(!overflow_dst.is(scratch));
|
| + ASSERT(!overflow_dst.is(left));
|
| + ASSERT(!overflow_dst.is(right));
|
| + ASSERT(!left.is(right));
|
| +
|
| + // TODO(kalmard) There must be a way to optimize dst == left and dst == right
|
| + // cases.
|
| +
|
| + if (dst.is(left)) {
|
| + addu(overflow_dst, left, right);
|
| + xor_(dst, overflow_dst, left);
|
| + xor_(scratch, overflow_dst, right);
|
| + and_(scratch, scratch, dst);
|
| + mov(dst, overflow_dst);
|
| + mov(overflow_dst, scratch);
|
| + } else if (dst.is(right)) {
|
| + addu(overflow_dst, left, right);
|
| + xor_(dst, overflow_dst, right);
|
| + xor_(scratch, overflow_dst, left);
|
| + and_(scratch, scratch, dst);
|
| + mov(dst, overflow_dst);
|
| + mov(overflow_dst, scratch);
|
| + } else {
|
| + addu(dst, left, right);
|
| + xor_(overflow_dst, dst, left);
|
| + xor_(scratch, dst, right);
|
| + and_(overflow_dst, scratch, overflow_dst);
|
| + }
|
| +}
|
| +
|
| +
|
| +void MacroAssembler::SubuAndCheckForOverflow(Register dst,
|
| + Register left,
|
| + Register right,
|
| + Register overflow_dst,
|
| + Register scratch) {
|
| + ASSERT(!dst.is(overflow_dst));
|
| + ASSERT(!dst.is(scratch));
|
| + 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));
|
| +
|
| + // TODO(kalmard) There must be a way to optimize dst == left and dst == right
|
| + // cases.
|
| +
|
| + if (dst.is(left)) {
|
| + subu(overflow_dst, left, right);
|
| + xor_(scratch, overflow_dst, left);
|
| + xor_(dst, left, right);
|
| + and_(scratch, scratch, dst);
|
| + mov(dst, overflow_dst);
|
| + mov(overflow_dst, scratch);
|
| + } else if (dst.is(right)) {
|
| + subu(overflow_dst, left, right);
|
| + xor_(dst, left, right);
|
| + xor_(scratch, overflow_dst, left);
|
| + and_(scratch, scratch, dst);
|
| + mov(dst, overflow_dst);
|
| + mov(overflow_dst, scratch);
|
| + } else {
|
| + subu(dst, left, right);
|
| + xor_(overflow_dst, dst, left);
|
| + xor_(scratch, left, right);
|
| + and_(overflow_dst, scratch, overflow_dst);
|
| + }
|
| +}
|
| +
|
| +
|
| void MacroAssembler::CallRuntime(const Runtime::Function* f,
|
| int num_arguments) {
|
| // All parameters are on the stack. v0 has the return value after call.
|
| @@ -2722,18 +2979,18 @@ void MacroAssembler::DecrementCounter(StatsCounter* counter, int value,
|
|
|
|
|
| // -----------------------------------------------------------------------------
|
| -// Debugging
|
| +// Debugging.
|
|
|
| void MacroAssembler::Assert(Condition cc, const char* msg,
|
| Register rs, Operand rt) {
|
| - if (FLAG_debug_code)
|
| + if (emit_debug_code())
|
| Check(cc, msg, rs, rt);
|
| }
|
|
|
|
|
| void MacroAssembler::AssertRegisterIsRoot(Register reg,
|
| Heap::RootListIndex index) {
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| LoadRoot(at, index);
|
| Check(eq, "Register did not match expected root", reg, Operand(at));
|
| }
|
| @@ -2741,10 +2998,10 @@ void MacroAssembler::AssertRegisterIsRoot(Register reg,
|
|
|
|
|
| void MacroAssembler::AssertFastElements(Register elements) {
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| ASSERT(!elements.is(at));
|
| Label ok;
|
| - Push(elements);
|
| + push(elements);
|
| lw(elements, FieldMemOperand(elements, HeapObject::kMapOffset));
|
| LoadRoot(at, Heap::kFixedArrayMapRootIndex);
|
| Branch(&ok, eq, elements, Operand(at));
|
| @@ -2752,7 +3009,7 @@ void MacroAssembler::AssertFastElements(Register elements) {
|
| Branch(&ok, eq, elements, Operand(at));
|
| Abort("JSObject with fast elements map has slow elements");
|
| bind(&ok);
|
| - Pop(elements);
|
| + pop(elements);
|
| }
|
| }
|
|
|
| @@ -2762,7 +3019,7 @@ void MacroAssembler::Check(Condition cc, const char* msg,
|
| Label L;
|
| Branch(&L, cc, rs, rt);
|
| Abort(msg);
|
| - // will not return here
|
| + // Will not return here.
|
| bind(&L);
|
| }
|
|
|
| @@ -2788,11 +3045,11 @@ void MacroAssembler::Abort(const char* msg) {
|
| AllowStubCallsScope allow_scope(this, true);
|
|
|
| li(a0, Operand(p0));
|
| - Push(a0);
|
| + push(a0);
|
| li(a0, Operand(Smi::FromInt(p1 - p0)));
|
| - Push(a0);
|
| + push(a0);
|
| CallRuntime(Runtime::kAbort, 2);
|
| - // will not return here
|
| + // Will not return here.
|
| if (is_trampoline_pool_blocked()) {
|
| // If the calling code cares about the exact number of
|
| // instructions generated, we insert padding here to keep the size
|
| @@ -2819,11 +3076,22 @@ void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
|
| lw(dst, MemOperand(dst, Context::SlotOffset(Context::CLOSURE_INDEX)));
|
| lw(dst, FieldMemOperand(dst, JSFunction::kContextOffset));
|
| }
|
| - // The context may be an intermediate context, not a function context.
|
| - lw(dst, MemOperand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX)));
|
| - } else { // Slot is in the current function context.
|
| - // The context may be an intermediate context, not a function context.
|
| - lw(dst, MemOperand(cp, Context::SlotOffset(Context::FCONTEXT_INDEX)));
|
| + } else {
|
| + // Slot is in the current function context. Move it into the
|
| + // destination register in case we store into it (the write barrier
|
| + // cannot be allowed to destroy the context in esi).
|
| + Move(dst, cp);
|
| + }
|
| +
|
| + // We should not have found a 'with' context by walking the context chain
|
| + // (i.e., the static scope chain and runtime context chain do not agree).
|
| + // A variable occurring in such a scope should have slot type LOOKUP and
|
| + // not CONTEXT.
|
| + if (emit_debug_code()) {
|
| + lw(t9, MemOperand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX)));
|
| + Check(eq, "Yo dawg, I heard you liked function contexts "
|
| + "so I put function contexts in all your contexts",
|
| + dst, Operand(t9));
|
| }
|
| }
|
|
|
| @@ -2844,7 +3112,7 @@ void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
|
| Register scratch) {
|
| // Load the initial map. The global functions all have initial maps.
|
| lw(map, FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
|
| - if (FLAG_debug_code) {
|
| + if (emit_debug_code()) {
|
| Label ok, fail;
|
| CheckMap(map, scratch, Heap::kMetaMapRootIndex, &fail, false);
|
| Branch(&ok);
|
| @@ -2891,11 +3159,6 @@ void MacroAssembler::EnterExitFrame(Register hold_argc,
|
| // t9 = sp + kPointerSize * #args
|
| addu(t9, sp, t8);
|
|
|
| - // Compute the argv pointer and keep it in a callee-saved register.
|
| - // This only seems to be needed for crankshaft and may cause problems
|
| - // so it's disabled for now.
|
| - // Subu(s6, t9, Operand(kPointerSize));
|
| -
|
| // Align the stack at this point.
|
| AlignStack(0);
|
|
|
| @@ -2907,7 +3170,7 @@ void MacroAssembler::EnterExitFrame(Register hold_argc,
|
| mov(fp, sp); // Setup new frame pointer.
|
|
|
| li(t8, Operand(CodeObject()));
|
| - Push(t8); // Accessed from ExitFrame::code_slot.
|
| + push(t8); // Accessed from ExitFrame::code_slot.
|
|
|
| // Save the frame pointer and the context in top.
|
| li(t8, Operand(ExternalReference(Isolate::k_c_entry_fp_address, isolate())));
|
| @@ -3092,6 +3355,18 @@ void MacroAssembler::AbortIfNotSmi(Register object) {
|
| }
|
|
|
|
|
| +void MacroAssembler::AbortIfNotString(Register object) {
|
| + STATIC_ASSERT(kSmiTag == 0);
|
| + And(t0, object, Operand(kSmiTagMask));
|
| + Assert(ne, "Operand is not a string", t0, Operand(zero_reg));
|
| + push(object);
|
| + lw(object, FieldMemOperand(object, HeapObject::kMapOffset));
|
| + lbu(object, FieldMemOperand(object, Map::kInstanceTypeOffset));
|
| + Assert(lo, "Operand is not a string", object, Operand(FIRST_NONSTRING_TYPE));
|
| + pop(object);
|
| +}
|
| +
|
| +
|
| void MacroAssembler::AbortIfNotRootValue(Register src,
|
| Heap::RootListIndex root_value_index,
|
| const char* message) {
|
| @@ -3183,9 +3458,6 @@ static const int kRegisterPassedArguments = 4;
|
| void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
|
| int frame_alignment = ActivationFrameAlignment();
|
|
|
| - // Reserve space for Isolate address which is always passed as last parameter
|
| - num_arguments += 1;
|
| -
|
| // Up to four simple arguments are passed in registers a0..a3.
|
| // Those four arguments must have reserved argument slots on the stack for
|
| // mips, even though those argument slots are not normally used.
|
| @@ -3212,7 +3484,7 @@ void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) {
|
|
|
| void MacroAssembler::CallCFunction(ExternalReference function,
|
| int num_arguments) {
|
| - CallCFunctionHelper(no_reg, function, at, num_arguments);
|
| + CallCFunctionHelper(no_reg, function, t8, num_arguments);
|
| }
|
|
|
|
|
| @@ -3230,21 +3502,6 @@ void MacroAssembler::CallCFunctionHelper(Register function,
|
| ExternalReference function_reference,
|
| Register scratch,
|
| int num_arguments) {
|
| - // Push Isolate address as the last argument.
|
| - if (num_arguments < kRegisterPassedArguments) {
|
| - Register arg_to_reg[] = {a0, a1, a2, a3};
|
| - Register r = arg_to_reg[num_arguments];
|
| - li(r, Operand(ExternalReference::isolate_address()));
|
| - } else {
|
| - int stack_passed_arguments = num_arguments - kRegisterPassedArguments +
|
| - (StandardFrameConstants::kCArgsSlotsSize /
|
| - kPointerSize);
|
| - // Push Isolate address on the stack after the arguments.
|
| - li(scratch, Operand(ExternalReference::isolate_address()));
|
| - sw(scratch, MemOperand(sp, stack_passed_arguments * kPointerSize));
|
| - }
|
| - num_arguments += 1;
|
| -
|
| // 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.
|
| @@ -3271,13 +3528,12 @@ void MacroAssembler::CallCFunctionHelper(Register function,
|
| // Just call directly. The function called cannot cause a GC, or
|
| // allow preemption, so the return address in the link register
|
| // stays correct.
|
| - if (!function.is(t9)) {
|
| - mov(t9, function);
|
| - function = t9;
|
| - }
|
|
|
| if (function.is(no_reg)) {
|
| - li(t9, Operand(function_reference));
|
| + function = t9;
|
| + li(function, Operand(function_reference));
|
| + } else if (!function.is(t9)) {
|
| + mov(t9, function);
|
| function = t9;
|
| }
|
|
|
| @@ -3300,12 +3556,11 @@ void MacroAssembler::CallCFunctionHelper(Register function,
|
| #undef BRANCH_ARGS_CHECK
|
|
|
|
|
| -#ifdef ENABLE_DEBUGGER_SUPPORT
|
| CodePatcher::CodePatcher(byte* address, int instructions)
|
| : address_(address),
|
| instructions_(instructions),
|
| size_(instructions * Assembler::kInstrSize),
|
| - masm_(address, size_ + Assembler::kGap) {
|
| + masm_(Isolate::Current(), address, size_ + Assembler::kGap) {
|
| // Create a new macro assembler pointing to the address of the code to patch.
|
| // The size is adjusted with kGap on order for the assembler to generate size
|
| // bytes of instructions without failing with buffer size constraints.
|
| @@ -3323,8 +3578,8 @@ CodePatcher::~CodePatcher() {
|
| }
|
|
|
|
|
| -void CodePatcher::Emit(Instr x) {
|
| - masm()->emit(x);
|
| +void CodePatcher::Emit(Instr instr) {
|
| + masm()->emit(instr);
|
| }
|
|
|
|
|
| @@ -3333,7 +3588,26 @@ void CodePatcher::Emit(Address addr) {
|
| }
|
|
|
|
|
| -#endif // ENABLE_DEBUGGER_SUPPORT
|
| +void CodePatcher::ChangeBranchCondition(Condition cond) {
|
| + Instr instr = Assembler::instr_at(masm_.pc_);
|
| + ASSERT(Assembler::IsBranch(instr));
|
| + uint32_t opcode = Assembler::GetOpcodeField(instr);
|
| + // Currently only the 'eq' and 'ne' cond values are supported and the simple
|
| + // branch instructions (with opcode being the branch type).
|
| + // There are some special cases (see Assembler::IsBranch()) so extending this
|
| + // would be tricky.
|
| + ASSERT(opcode == BEQ ||
|
| + opcode == BNE ||
|
| + opcode == BLEZ ||
|
| + opcode == BGTZ ||
|
| + opcode == BEQL ||
|
| + opcode == BNEL ||
|
| + opcode == BLEZL ||
|
| + opcode == BGTZL);
|
| + opcode = (cond == eq) ? BEQ : BNE;
|
| + instr = (instr & ~kOpcodeMask) | opcode;
|
| + masm_.emit(instr);
|
| +}
|
|
|
|
|
| } } // namespace v8::internal
|
|
|