| Index: src/mips/macro-assembler-mips.cc
|
| ===================================================================
|
| --- src/mips/macro-assembler-mips.cc (revision 7948)
|
| +++ src/mips/macro-assembler-mips.cc (working copy)
|
| @@ -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::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 @@
|
|
|
| // 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 @@
|
|
|
| // 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 @@
|
|
|
|
|
| // -----------------------------------------------------------------------------
|
| -// Allocation support
|
| +// Allocation support.
|
|
|
|
|
| void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
|
| @@ -297,15 +300,15 @@
|
| 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 @@
|
| 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 @@
|
| 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 @@
|
|
|
|
|
| // ---------------------------------------------------------------------------
|
| -// Instruction macros
|
| +// Instruction macros.
|
|
|
| void MacroAssembler::Addu(Register rd, Register rs, const Operand& rt) {
|
| if (rt.is_reg()) {
|
| @@ -500,6 +503,15 @@
|
| }
|
|
|
|
|
| +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 @@
|
| }
|
| // 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 @@
|
| 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 @@
|
| 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 @@
|
| 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::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 @@
|
| 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 @@
|
| 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 @@
|
| 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 @@
|
| 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 @@
|
| bal(offset);
|
| break;
|
|
|
| - // Signed comparison
|
| + // Signed comparison.
|
| case greater:
|
| slt(scratch, r2, rs);
|
| addiu(scratch, scratch, -1);
|
| @@ -1539,7 +1636,7 @@
|
| bal(offset);
|
| break;
|
|
|
| - // Signed comparison
|
| + // Signed comparison.
|
| case greater:
|
| slt(scratch, r2, rs);
|
| addiu(scratch, scratch, -1);
|
| @@ -1642,7 +1739,7 @@
|
| 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 @@
|
|
|
|
|
| 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 @@
|
| 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 @@
|
| 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::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 @@
|
|
|
|
|
| // ---------------------------------------------------------------------------
|
| -// Exception handling
|
| +// Exception handling.
|
|
|
| void MacroAssembler::PushTryHandler(CodeLocation try_location,
|
| HandlerType type) {
|
| @@ -1880,6 +1992,159 @@
|
| }
|
|
|
|
|
| +void MacroAssembler::Throw(Register value) {
|
| + // v0 is expected to hold the exception.
|
| + Move(v0, value);
|
| +
|
| + // Adjust this code if not the case.
|
| + STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
|
| +
|
| + // Drop the sp to the top of the handler.
|
| + li(a3, Operand(ExternalReference(Isolate::k_handler_address,
|
| + isolate())));
|
| + lw(sp, MemOperand(a3));
|
| +
|
| + // Restore the next handler and frame pointer, discard handler state.
|
| + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
| + pop(a2);
|
| + sw(a2, MemOperand(a3));
|
| + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
|
| + MultiPop(a3.bit() | fp.bit());
|
| +
|
| + // Before returning we restore the context from the frame pointer if
|
| + // not NULL. The frame pointer is NULL in the exception handler of a
|
| + // JS entry frame.
|
| + // Set cp to NULL if fp is NULL.
|
| + Label done;
|
| + Branch(USE_DELAY_SLOT, &done, eq, fp, Operand(zero_reg));
|
| + mov(cp, zero_reg); // In branch delay slot.
|
| + lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
| + bind(&done);
|
| +
|
| +#ifdef DEBUG
|
| + // When emitting debug_code, set ra as return address for the jump.
|
| + // 5 instructions: add: 1, pop: 2, jump: 2.
|
| + const int kOffsetRaInstructions = 5;
|
| + Label find_ra;
|
| +
|
| + if (emit_debug_code()) {
|
| + // Compute ra for the Jump(t9).
|
| + const int kOffsetRaBytes = kOffsetRaInstructions * Assembler::kInstrSize;
|
| +
|
| + // This branch-and-link sequence is needed to get the current PC on mips,
|
| + // saved to the ra register. Then adjusted for instruction count.
|
| + bal(&find_ra); // bal exposes branch-delay.
|
| + nop(); // Branch delay slot nop.
|
| + bind(&find_ra);
|
| + addiu(ra, ra, kOffsetRaBytes);
|
| + }
|
| +#endif
|
| +
|
| + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
|
| + pop(t9); // 2 instructions: lw, add sp.
|
| + Jump(t9); // 2 instructions: jr, nop (in delay slot).
|
| +
|
| + if (emit_debug_code()) {
|
| + // Make sure that the expected number of instructions were generated.
|
| + ASSERT_EQ(kOffsetRaInstructions,
|
| + InstructionsGeneratedSince(&find_ra));
|
| + }
|
| +}
|
| +
|
| +
|
| +void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type,
|
| + Register value) {
|
| + // Adjust this code if not the case.
|
| + STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize);
|
| +
|
| + // v0 is expected to hold the exception.
|
| + Move(v0, value);
|
| +
|
| + // Drop sp to the top stack handler.
|
| + li(a3, Operand(ExternalReference(Isolate::k_handler_address, isolate())));
|
| + lw(sp, MemOperand(a3));
|
| +
|
| + // Unwind the handlers until the ENTRY handler is found.
|
| + Label loop, done;
|
| + bind(&loop);
|
| + // Load the type of the current stack handler.
|
| + const int kStateOffset = StackHandlerConstants::kStateOffset;
|
| + lw(a2, MemOperand(sp, kStateOffset));
|
| + Branch(&done, eq, a2, Operand(StackHandler::ENTRY));
|
| + // Fetch the next handler in the list.
|
| + const int kNextOffset = StackHandlerConstants::kNextOffset;
|
| + lw(sp, MemOperand(sp, kNextOffset));
|
| + jmp(&loop);
|
| + bind(&done);
|
| +
|
| + // Set the top handler address to next handler past the current ENTRY handler.
|
| + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
| + pop(a2);
|
| + sw(a2, MemOperand(a3));
|
| +
|
| + if (type == OUT_OF_MEMORY) {
|
| + // Set external caught exception to false.
|
| + ExternalReference external_caught(
|
| + Isolate::k_external_caught_exception_address, isolate());
|
| + li(a0, Operand(false, RelocInfo::NONE));
|
| + li(a2, Operand(external_caught));
|
| + sw(a0, MemOperand(a2));
|
| +
|
| + // Set pending exception and v0 to out of memory exception.
|
| + Failure* out_of_memory = Failure::OutOfMemoryException();
|
| + li(v0, Operand(reinterpret_cast<int32_t>(out_of_memory)));
|
| + li(a2, Operand(ExternalReference(Isolate::k_pending_exception_address,
|
| + isolate())));
|
| + sw(v0, MemOperand(a2));
|
| + }
|
| +
|
| + // Stack layout at this point. See also StackHandlerConstants.
|
| + // sp -> state (ENTRY)
|
| + // fp
|
| + // ra
|
| +
|
| + // Discard handler state (a2 is not used) and restore frame pointer.
|
| + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
|
| + MultiPop(a2.bit() | fp.bit()); // a2: discarded state.
|
| + // Before returning we restore the context from the frame pointer if
|
| + // not NULL. The frame pointer is NULL in the exception handler of a
|
| + // JS entry frame.
|
| + Label cp_null;
|
| + Branch(USE_DELAY_SLOT, &cp_null, eq, fp, Operand(zero_reg));
|
| + mov(cp, zero_reg); // In the branch delay slot.
|
| + lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
| + bind(&cp_null);
|
| +
|
| +#ifdef DEBUG
|
| + // When emitting debug_code, set ra as return address for the jump.
|
| + // 5 instructions: add: 1, pop: 2, jump: 2.
|
| + const int kOffsetRaInstructions = 5;
|
| + Label find_ra;
|
| +
|
| + if (emit_debug_code()) {
|
| + // Compute ra for the Jump(t9).
|
| + const int kOffsetRaBytes = kOffsetRaInstructions * Assembler::kInstrSize;
|
| +
|
| + // This branch-and-link sequence is needed to get the current PC on mips,
|
| + // saved to the ra register. Then adjusted for instruction count.
|
| + bal(&find_ra); // bal exposes branch-delay slot.
|
| + nop(); // Branch delay slot nop.
|
| + bind(&find_ra);
|
| + addiu(ra, ra, kOffsetRaBytes);
|
| + }
|
| +#endif
|
| + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize);
|
| + pop(t9); // 2 instructions: lw, add sp.
|
| + Jump(t9); // 2 instructions: jr, nop (in delay slot).
|
| +
|
| + if (emit_debug_code()) {
|
| + // Make sure that the expected number of instructions were generated.
|
| + ASSERT_EQ(kOffsetRaInstructions,
|
| + InstructionsGeneratedSince(&find_ra));
|
| + }
|
| +}
|
| +
|
| +
|
| void MacroAssembler::AllocateInNewSpace(int object_size,
|
| Register result,
|
| Register scratch1,
|
| @@ -1887,7 +2152,7 @@
|
| 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 +2200,7 @@
|
| 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 +2231,7 @@
|
| 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 +2269,7 @@
|
| 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 +2292,7 @@
|
| 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 +2483,64 @@
|
| }
|
|
|
|
|
| +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 (emit_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 +2569,20 @@
|
| }
|
|
|
|
|
| +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 +2625,11 @@
|
| 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 +2824,7 @@
|
|
|
|
|
| // -----------------------------------------------------------------------------
|
| -// Runtime calls
|
| +// Runtime calls.
|
|
|
| void MacroAssembler::CallStub(CodeStub* stub, Condition cond,
|
| Register r1, const Operand& r2) {
|
| @@ -2500,12 +2833,137 @@
|
| }
|
|
|
|
|
| +MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub, 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;
|
| + }
|
| + Call(Handle<Code>(Code::cast(result)), RelocInfo::CODE_TARGET, cond, r1, r2);
|
| + return result;
|
| +}
|
| +
|
| +
|
| +
|
| 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);
|
| }
|
|
|
| +MaybeObject* MacroAssembler::TryTailCallStub(CodeStub* stub,
|
| + 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;
|
| + }
|
| + Jump(stub->GetCode(), RelocInfo::CODE_TARGET, cond, r1, r2);
|
| + return result;
|
| +}
|
|
|
| +
|
| +static int AddressOffset(ExternalReference ref0, ExternalReference ref1) {
|
| + return ref0.address() - ref1.address();
|
| +}
|
| +
|
| +
|
| +MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(
|
| + ExternalReference function, int stack_space) {
|
| + ExternalReference next_address =
|
| + ExternalReference::handle_scope_next_address();
|
| + const int kNextOffset = 0;
|
| + const int kLimitOffset = AddressOffset(
|
| + ExternalReference::handle_scope_limit_address(),
|
| + next_address);
|
| + const int kLevelOffset = AddressOffset(
|
| + ExternalReference::handle_scope_level_address(),
|
| + next_address);
|
| +
|
| + // Allocate HandleScope in callee-save registers.
|
| + li(s3, Operand(next_address));
|
| + lw(s0, MemOperand(s3, kNextOffset));
|
| + lw(s1, MemOperand(s3, kLimitOffset));
|
| + lw(s2, MemOperand(s3, kLevelOffset));
|
| + Addu(s2, s2, Operand(1));
|
| + sw(s2, MemOperand(s3, kLevelOffset));
|
| +
|
| + // The O32 ABI requires us to pass a pointer in a0 where the returned struct
|
| + // (4 bytes) will be placed. This is also built into the Simulator.
|
| + // Set up the pointer to the returned value (a0). It was allocated in
|
| + // EnterExitFrame.
|
| + addiu(a0, fp, ExitFrameConstants::kStackSpaceOffset);
|
| +
|
| + // Native call returns to the DirectCEntry stub which redirects to the
|
| + // return address pushed on stack (could have moved after GC).
|
| + // DirectCEntry stub itself is generated early and never moves.
|
| + DirectCEntryStub stub;
|
| + stub.GenerateCall(this, function);
|
| +
|
| + // As mentioned above, on MIPS a pointer is returned - we need to dereference
|
| + // it to get the actual return value (which is also a pointer).
|
| + lw(v0, MemOperand(v0));
|
| +
|
| + Label promote_scheduled_exception;
|
| + Label delete_allocated_handles;
|
| + Label leave_exit_frame;
|
| +
|
| + // If result is non-zero, dereference to get the result value
|
| + // otherwise set it to undefined.
|
| + Label skip;
|
| + LoadRoot(a0, Heap::kUndefinedValueRootIndex);
|
| + Branch(&skip, eq, v0, Operand(zero_reg));
|
| + lw(a0, MemOperand(v0));
|
| + bind(&skip);
|
| + mov(v0, a0);
|
| +
|
| + // No more valid handles (the result handle was the last one). Restore
|
| + // previous handle scope.
|
| + sw(s0, MemOperand(s3, kNextOffset));
|
| + if (emit_debug_code()) {
|
| + lw(a1, MemOperand(s3, kLevelOffset));
|
| + Check(eq, "Unexpected level after return from api call", a1, Operand(s2));
|
| + }
|
| + Subu(s2, s2, Operand(1));
|
| + sw(s2, MemOperand(s3, kLevelOffset));
|
| + lw(at, MemOperand(s3, kLimitOffset));
|
| + Branch(&delete_allocated_handles, ne, s1, Operand(at));
|
| +
|
| + // Check if the function scheduled an exception.
|
| + bind(&leave_exit_frame);
|
| + LoadRoot(t0, Heap::kTheHoleValueRootIndex);
|
| + li(at, Operand(ExternalReference::scheduled_exception_address(isolate())));
|
| + lw(t1, MemOperand(at));
|
| + Branch(&promote_scheduled_exception, ne, t0, Operand(t1));
|
| + li(s0, Operand(stack_space));
|
| + LeaveExitFrame(false, s0);
|
| + Ret();
|
| +
|
| + bind(&promote_scheduled_exception);
|
| + MaybeObject* result = TryTailCallExternalReference(
|
| + ExternalReference(Runtime::kPromoteScheduledException, isolate()), 0, 1);
|
| + if (result->IsFailure()) {
|
| + return result;
|
| + }
|
| +
|
| + // HandleScope limit has changed. Delete allocated extensions.
|
| + bind(&delete_allocated_handles);
|
| + sw(s1, MemOperand(s3, kLimitOffset));
|
| + mov(s0, v0);
|
| + mov(a0, v0);
|
| + PrepareCallCFunction(1, s1);
|
| + li(a0, Operand(ExternalReference::isolate_address()));
|
| + CallCFunction(ExternalReference::delete_handle_scope_extensions(isolate()),
|
| + 1);
|
| + mov(v0, s0);
|
| + jmp(&leave_exit_frame);
|
| +
|
| + return result;
|
| +}
|
| +
|
| +
|
| void MacroAssembler::IllegalOperation(int num_arguments) {
|
| if (num_arguments > 0) {
|
| addiu(sp, sp, num_arguments * kPointerSize);
|
| @@ -2567,7 +3025,6 @@
|
| }
|
|
|
|
|
| -
|
| void MacroAssembler::SmiToDoubleFPURegister(Register smi,
|
| FPURegister value,
|
| Register scratch1) {
|
| @@ -2577,6 +3034,84 @@
|
| }
|
|
|
|
|
| +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.
|
| @@ -2636,7 +3171,17 @@
|
| JumpToExternalReference(ext);
|
| }
|
|
|
| +MaybeObject* MacroAssembler::TryTailCallExternalReference(
|
| + const ExternalReference& ext, int num_arguments, int result_size) {
|
| + // TODO(1236192): Most runtime routines don't need the number of
|
| + // arguments passed in because it is constant. At some point we
|
| + // should remove this need and make the runtime routine entry code
|
| + // smarter.
|
| + li(a0, num_arguments);
|
| + return TryJumpToExternalReference(ext);
|
| +}
|
|
|
| +
|
| void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
|
| int num_arguments,
|
| int result_size) {
|
| @@ -2653,6 +3198,14 @@
|
| }
|
|
|
|
|
| +MaybeObject* MacroAssembler::TryJumpToExternalReference(
|
| + const ExternalReference& builtin) {
|
| + li(a1, Operand(builtin));
|
| + CEntryStub stub(1);
|
| + return TryTailCallStub(&stub);
|
| +}
|
| +
|
| +
|
| void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
|
| InvokeFlag flag,
|
| const CallWrapper& call_wrapper) {
|
| @@ -2722,18 +3275,18 @@
|
|
|
|
|
| // -----------------------------------------------------------------------------
|
| -// 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 +3294,10 @@
|
|
|
|
|
| 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 +3305,7 @@
|
| Branch(&ok, eq, elements, Operand(at));
|
| Abort("JSObject with fast elements map has slow elements");
|
| bind(&ok);
|
| - Pop(elements);
|
| + pop(elements);
|
| }
|
| }
|
|
|
| @@ -2762,7 +3315,7 @@
|
| Label L;
|
| Branch(&L, cc, rs, rt);
|
| Abort(msg);
|
| - // will not return here
|
| + // Will not return here.
|
| bind(&L);
|
| }
|
|
|
| @@ -2788,11 +3341,11 @@
|
| 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,12 +3372,23 @@
|
| 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 +3408,7 @@
|
| 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);
|
| @@ -2876,88 +3440,66 @@
|
| }
|
|
|
|
|
| -void MacroAssembler::EnterExitFrame(Register hold_argc,
|
| - Register hold_argv,
|
| - Register hold_function,
|
| - bool save_doubles) {
|
| - // a0 is argc.
|
| - sll(t8, a0, kPointerSizeLog2);
|
| - addu(hold_argv, sp, t8);
|
| - addiu(hold_argv, hold_argv, -kPointerSize);
|
| +void MacroAssembler::EnterExitFrame(bool save_doubles,
|
| + int stack_space) {
|
| + // Setup the frame structure on the stack.
|
| + STATIC_ASSERT(2 * kPointerSize == ExitFrameConstants::kCallerSPDisplacement);
|
| + STATIC_ASSERT(1 * kPointerSize == ExitFrameConstants::kCallerPCOffset);
|
| + STATIC_ASSERT(0 * kPointerSize == ExitFrameConstants::kCallerFPOffset);
|
|
|
| - // Compute callee's stack pointer before making changes and save it as
|
| - // t9 register so that it is restored as sp register on exit, thereby
|
| - // popping the args.
|
| - // t9 = sp + kPointerSize * #args
|
| - addu(t9, sp, t8);
|
| + // This is how the stack will look:
|
| + // fp + 2 (==kCallerSPDisplacement) - old stack's end
|
| + // [fp + 1 (==kCallerPCOffset)] - saved old ra
|
| + // [fp + 0 (==kCallerFPOffset)] - saved old fp
|
| + // [fp - 1 (==kSPOffset)] - sp of the called function
|
| + // [fp - 2 (==kCodeOffset)] - CodeObject
|
| + // fp - (2 + stack_space + alignment) == sp == [fp - kSPOffset] - top of the
|
| + // new stack (will contain saved ra)
|
|
|
| - // 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));
|
| + // Save registers.
|
| + addiu(sp, sp, -4 * kPointerSize);
|
| + sw(ra, MemOperand(sp, 3 * kPointerSize));
|
| + sw(fp, MemOperand(sp, 2 * kPointerSize));
|
| + addiu(fp, sp, 2 * kPointerSize); // Setup new frame pointer.
|
|
|
| - // Align the stack at this point.
|
| - AlignStack(0);
|
| + if (emit_debug_code()) {
|
| + sw(zero_reg, MemOperand(fp, ExitFrameConstants::kSPOffset));
|
| + }
|
|
|
| - // Save registers.
|
| - addiu(sp, sp, -12);
|
| - sw(t9, MemOperand(sp, 8));
|
| - sw(ra, MemOperand(sp, 4));
|
| - sw(fp, MemOperand(sp, 0));
|
| - mov(fp, sp); // Setup new frame pointer.
|
| + li(t8, Operand(CodeObject())); // Accessed from ExitFrame::code_slot.
|
| + sw(t8, MemOperand(fp, ExitFrameConstants::kCodeOffset));
|
|
|
| - li(t8, Operand(CodeObject()));
|
| - 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())));
|
| sw(fp, MemOperand(t8));
|
| li(t8, Operand(ExternalReference(Isolate::k_context_address, isolate())));
|
| sw(cp, MemOperand(t8));
|
|
|
| - // Setup argc and the builtin function in callee-saved registers.
|
| - mov(hold_argc, a0);
|
| - mov(hold_function, a1);
|
| + // Ensure we are not saving doubles, since it's not implemented yet.
|
| + ASSERT(save_doubles == 0);
|
|
|
| - // Optionally save all double registers.
|
| - if (save_doubles) {
|
| -#ifdef DEBUG
|
| - int frame_alignment = ActivationFrameAlignment();
|
| -#endif
|
| - // The stack alignment code above made sp unaligned, so add space for one
|
| - // more double register and use aligned addresses.
|
| - ASSERT(kDoubleSize == frame_alignment);
|
| - // Mark the frame as containing doubles by pushing a non-valid return
|
| - // address, i.e. 0.
|
| - ASSERT(ExitFrameConstants::kMarkerOffset == -2 * kPointerSize);
|
| - push(zero_reg); // Marker and alignment word.
|
| - int space = FPURegister::kNumRegisters * kDoubleSize + kPointerSize;
|
| - Subu(sp, sp, Operand(space));
|
| - // Remember: we only need to save every 2nd double FPU value.
|
| - for (int i = 0; i < FPURegister::kNumRegisters; i+=2) {
|
| - FPURegister reg = FPURegister::from_code(i);
|
| - sdc1(reg, MemOperand(sp, i * kDoubleSize + kPointerSize));
|
| - }
|
| - // Note that f0 will be accessible at fp - 2*kPointerSize -
|
| - // FPURegister::kNumRegisters * kDoubleSize, since the code slot and the
|
| - // alignment word were pushed after the fp.
|
| + // Reserve place for the return address, stack space and an optional slot
|
| + // (used by the DirectCEntryStub to hold the return value if a struct is
|
| + // returned) and align the frame preparing for calling the runtime function.
|
| + ASSERT(stack_space >= 0);
|
| + const int frame_alignment = MacroAssembler::ActivationFrameAlignment();
|
| + Subu(sp, sp, Operand((stack_space + 2) * kPointerSize));
|
| + if (frame_alignment > 0) {
|
| + ASSERT(IsPowerOf2(frame_alignment));
|
| + And(sp, sp, Operand(-frame_alignment)); // Align stack.
|
| }
|
| +
|
| + // Set the exit frame sp value to point just before the return address
|
| + // location.
|
| + addiu(at, sp, kPointerSize);
|
| + sw(at, MemOperand(fp, ExitFrameConstants::kSPOffset));
|
| }
|
|
|
|
|
| -void MacroAssembler::LeaveExitFrame(bool save_doubles) {
|
| - // Optionally restore all double registers.
|
| - if (save_doubles) {
|
| - // TODO(regis): Use vldrm instruction.
|
| - // Remember: we only need to restore every 2nd double FPU value.
|
| - for (int i = 0; i < FPURegister::kNumRegisters; i+=2) {
|
| - FPURegister reg = FPURegister::from_code(i);
|
| - // Register f30-f31 is just below the marker.
|
| - const int offset = ExitFrameConstants::kMarkerOffset;
|
| - ldc1(reg, MemOperand(fp,
|
| - (i - FPURegister::kNumRegisters) * kDoubleSize + offset));
|
| - }
|
| - }
|
| +void MacroAssembler::LeaveExitFrame(bool save_doubles,
|
| + Register argument_count) {
|
| + // Ensure we are not restoring doubles, since it's not implemented yet.
|
| + ASSERT(save_doubles == 0);
|
|
|
| // Clear top frame.
|
| li(t8, Operand(ExternalReference(Isolate::k_c_entry_fp_address, isolate())));
|
| @@ -2972,11 +3514,13 @@
|
|
|
| // Pop the arguments, restore registers, and return.
|
| mov(sp, fp); // Respect ABI stack constraint.
|
| - lw(fp, MemOperand(sp, 0));
|
| - lw(ra, MemOperand(sp, 4));
|
| - lw(sp, MemOperand(sp, 8));
|
| - jr(ra);
|
| - nop(); // Branch delay slot nop.
|
| + lw(fp, MemOperand(sp, ExitFrameConstants::kCallerFPOffset));
|
| + lw(ra, MemOperand(sp, ExitFrameConstants::kCallerPCOffset));
|
| + addiu(sp, sp, 8);
|
| + if (argument_count.is_valid()) {
|
| + sll(t8, argument_count, kPointerSizeLog2);
|
| + addu(sp, sp, t8);
|
| + }
|
| }
|
|
|
|
|
| @@ -3010,39 +3554,24 @@
|
| #endif // defined(V8_HOST_ARCH_MIPS)
|
| }
|
|
|
| +void MacroAssembler::AssertStackIsAligned() {
|
| + if (emit_debug_code()) {
|
| + const int frame_alignment = ActivationFrameAlignment();
|
| + const int frame_alignment_mask = frame_alignment - 1;
|
|
|
| -void MacroAssembler::AlignStack(int offset) {
|
| - // On MIPS an offset of 0 aligns to 0 modulo 8 bytes,
|
| - // and an offset of 1 aligns to 4 modulo 8 bytes.
|
| -#if defined(V8_HOST_ARCH_MIPS)
|
| - // Running on the real platform. Use the alignment as mandated by the local
|
| - // environment.
|
| - // Note: This will break if we ever start generating snapshots on one MIPS
|
| - // platform for another MIPS platform with a different alignment.
|
| - int activation_frame_alignment = OS::ActivationFrameAlignment();
|
| -#else // defined(V8_HOST_ARCH_MIPS)
|
| - // If we are using the simulator then we should always align to the expected
|
| - // alignment. As the simulator is used to generate snapshots we do not know
|
| - // if the target platform will need alignment, so we will always align at
|
| - // this point here.
|
| - int activation_frame_alignment = 2 * kPointerSize;
|
| -#endif // defined(V8_HOST_ARCH_MIPS)
|
| - if (activation_frame_alignment != kPointerSize) {
|
| - // This code needs to be made more general if this assert doesn't hold.
|
| - ASSERT(activation_frame_alignment == 2 * kPointerSize);
|
| - if (offset == 0) {
|
| - andi(t8, sp, activation_frame_alignment - 1);
|
| - Push(zero_reg, eq, t8, zero_reg);
|
| - } else {
|
| - andi(t8, sp, activation_frame_alignment - 1);
|
| - addiu(t8, t8, -4);
|
| - Push(zero_reg, eq, t8, zero_reg);
|
| + if (frame_alignment > kPointerSize) {
|
| + Label alignment_as_expected;
|
| + ASSERT(IsPowerOf2(frame_alignment));
|
| + andi(at, sp, frame_alignment_mask);
|
| + Branch(&alignment_as_expected, eq, at, Operand(zero_reg));
|
| + // Don't use Check here, as it will call Runtime_Abort re-entering here.
|
| + stop("Unexpected stack alignment");
|
| + bind(&alignment_as_expected);
|
| + }
|
| }
|
| - }
|
| }
|
|
|
|
|
| -
|
| void MacroAssembler::JumpIfNotPowerOfTwoOrZero(
|
| Register reg,
|
| Register scratch,
|
| @@ -3092,6 +3621,18 @@
|
| }
|
|
|
|
|
| +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 +3724,6 @@
|
| 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 +3750,7 @@
|
|
|
| void MacroAssembler::CallCFunction(ExternalReference function,
|
| int num_arguments) {
|
| - CallCFunctionHelper(no_reg, function, at, num_arguments);
|
| + CallCFunctionHelper(no_reg, function, t8, num_arguments);
|
| }
|
|
|
|
|
| @@ -3230,21 +3768,6 @@
|
| 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,14 +3794,13 @@
|
| // 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;
|
| }
|
|
|
| Call(function);
|
| @@ -3300,12 +3822,11 @@
|
| #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 +3844,8 @@
|
| }
|
|
|
|
|
| -void CodePatcher::Emit(Instr x) {
|
| - masm()->emit(x);
|
| +void CodePatcher::Emit(Instr instr) {
|
| + masm()->emit(instr);
|
| }
|
|
|
|
|
| @@ -3333,7 +3854,26 @@
|
| }
|
|
|
|
|
| -#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
|
|
|