'LINKFLAGS': ['-m32'],
'mipsabi:softfloat': {
'CPPDEFINES': ['__mips_soft_float=1'],
+ },
+ 'mipsabi:hardfloat': {
+ 'CPPDEFINES': ['__mips_hard_float=1'],
}
},
'arch:x64': {
},
'arch:mips': {
'CPPDEFINES': ['V8_TARGET_ARCH_MIPS'],
+ 'mips_arch_variant:mips32r2': {
+ 'CPPDEFINES': ['_MIPS_ARCH_MIPS32R2']
+ },
'simulator:none': {
- 'CCFLAGS': ['-EL', '-mips32r2', '-Wa,-mips32r2', '-fno-inline'],
+ 'CCFLAGS': ['-EL'],
'LINKFLAGS': ['-EL'],
- 'LDFLAGS': ['-EL']
+ 'mips_arch_variant:mips32r2': {
+ 'CCFLAGS': ['-mips32r2', '-Wa,-mips32r2']
+ },
+ 'mips_arch_variant:mips32r1': {
+ 'CCFLAGS': ['-mips32', '-Wa,-mips32']
+ },
+ 'library:static': {
+ 'LINKFLAGS': ['-static', '-static-libgcc']
+ },
+ 'mipsabi:softfloat': {
+ 'CCFLAGS': ['-msoft-float'],
+ 'LINKFLAGS': ['-msoft-float']
+ },
+ 'mipsabi:hardfloat': {
+ 'CCFLAGS': ['-mhard-float'],
+ 'LINKFLAGS': ['-mhard-float']
+ }
}
},
'simulator:arm': {
'LINKFLAGS': ['-m32'],
'mipsabi:softfloat': {
'CPPDEFINES': ['__mips_soft_float=1'],
+ },
+ 'mipsabi:hardfloat': {
+ 'CPPDEFINES': ['__mips_hard_float=1'],
}
},
'mode:release': {
if 'msvcltcg' in ARGUMENTS:
print "Warning: forcing msvcltcg on as it is required for pgo (%s)" % options['pgo']
options['msvcltcg'] = 'on'
- if (options['simulator'] == 'mips' and options['mipsabi'] != 'softfloat'):
- # Print a warning if soft-float ABI is not selected for mips simulator
- print "Warning: forcing soft-float mips ABI when running on simulator"
- options['mipsabi'] = 'softfloat'
- if (options['mipsabi'] != 'none') and (options['arch'] != 'mips') and (options['simulator'] != 'mips'):
- options['mipsabi'] = 'none'
+ if (options['mipsabi'] != 'none') and (options['arch'] != 'mips') and (options['simulator'] != 'mips'):
+ options['mipsabi'] = 'none'
if options['liveobjectlist'] == 'on':
if (options['debuggersupport'] != 'on') or (options['mode'] == 'release'):
# Print a warning that liveobjectlist will implicitly enable the debugger
__ mtc1(scratch1, f12);
__ cvt_d_w(f12, f12);
if (destination == kCoreRegisters) {
- __ mfc1(a2, f14);
- __ mfc1(a3, f15);
-
- __ mfc1(a0, f12);
- __ mfc1(a1, f13);
+ __ Move(a2, a3, f14);
+ __ Move(a0, a1, f12);
}
} else {
ASSERT(destination == kCoreRegisters);
__ cvt_d_w(dst, dst);
if (destination == kCoreRegisters) {
// Load the converted smi to dst1 and dst2 in double format.
- __ mfc1(dst1, dst);
- __ mfc1(dst2, FPURegister::from_code(dst.code() + 1));
+ __ Move(dst1, dst2, dst);
}
} else {
ASSERT(destination == kCoreRegisters);
Register scratch2,
FPURegister single_scratch) {
ASSERT(!int_scratch.is(scratch2));
+ ASSERT(!int_scratch.is(dst1));
+ ASSERT(!int_scratch.is(dst2));
Label done;
__ mtc1(int_scratch, single_scratch);
__ cvt_d_w(double_dst, single_scratch);
if (destination == kCoreRegisters) {
- __ mfc1(dst1, double_dst);
- __ mfc1(dst2, FPURegister::from_code(double_dst.code() + 1));
+ __ Move(dst1, dst2, double_dst);
}
} else {
Label fewer_than_20_useful_bits;
__ Branch(not_int32, ne, scratch2, Operand(zero_reg));
if (destination == kCoreRegisters) {
- __ mfc1(dst1, double_dst);
- __ mfc1(dst2, FPURegister::from_code(double_dst.code() + 1));
+ __ Move(dst1, dst2, double_dst);
}
} else {
// calling is compiled with hard-float flag and expecting hard float ABI
// (parameters in f12/f14 registers). We need to copy parameters from
// a0-a3 registers to f12/f14 register pairs.
- __ mtc1(a0, f12);
- __ mtc1(a1, f13);
- __ mtc1(a2, f14);
- __ mtc1(a3, f15);
+ __ Move(f12, a0, a1);
+ __ Move(f14, a2, a3);
}
// Call C routine that may not cause GC or other trouble.
__ CallCFunction(ExternalReference::double_fp_operation(op, masm->isolate()),
if (CpuFeatures::IsSupported(FPU)) {
CpuFeatures::Scope scope(FPU);
// Lhs and rhs are already loaded to f12 and f14 register pairs.
- __ mfc1(t0, f14); // f14 has LS 32 bits of rhs.
- __ mfc1(t1, f15); // f15 has MS 32 bits of rhs.
- __ mfc1(t2, f12); // f12 has LS 32 bits of lhs.
- __ mfc1(t3, f13); // f13 has MS 32 bits of lhs.
+ __ Move(t0, t1, f14);
+ __ Move(t2, t3, f12);
} else {
// Lhs and rhs are already loaded to GP registers.
__ mov(t0, a0); // a0 has LS 32 bits of rhs.
// Exception: 0 and -0.
bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
if (CpuFeatures::IsSupported(FPU)) {
- CpuFeatures::Scope scope(FPU);
+ CpuFeatures::Scope scope(FPU);
// Lhs and rhs are already loaded to f12 and f14 register pairs.
- __ mfc1(t0, f14); // f14 has LS 32 bits of rhs.
- __ mfc1(t1, f15); // f15 has MS 32 bits of rhs.
- __ mfc1(t2, f12); // f12 has LS 32 bits of lhs.
- __ mfc1(t3, f13); // f13 has MS 32 bits of lhs.
+ __ Move(t0, t1, f14);
+ __ Move(t2, t3, f12);
} else {
// Lhs and rhs are already loaded to GP registers.
__ mov(t0, a0); // a0 has LS 32 bits of rhs.
// calling is compiled with hard-float flag and expecting hard float ABI
// (parameters in f12/f14 registers). We need to copy parameters from
// a0-a3 registers to f12/f14 register pairs.
- __ mtc1(a0, f12);
- __ mtc1(a1, f13);
- __ mtc1(a2, f14);
- __ mtc1(a3, f15);
+ __ Move(f12, a0, a1);
+ __ Move(f14, a2, a3);
}
__ CallCFunction(ExternalReference::compare_doubles(masm->isolate()), 4);
__ pop(ra); // Because this function returns int, result is in v0.
__ sra(t0, a0, kSmiTagSize);
__ mtc1(t0, f4);
__ cvt_d_w(f4, f4);
- __ mfc1(a2, f4);
- __ mfc1(a3, f5);
+ __ Move(a2, a3, f4);
__ Branch(&loaded);
__ bind(&input_not_smi);
__ lw(a3, FieldMemOperand(a0, HeapNumber::kValueOffset + 4));
} else {
// Input is untagged double in f4. Output goes to f4.
- __ mfc1(a2, f4);
- __ mfc1(a3, f5);
+ __ Move(a2, a3, f4);
}
__ bind(&loaded);
// a2 = low 32 bits of double value.
Register scratch) {
__ push(ra);
__ PrepareCallCFunction(2, scratch);
- __ mfc1(v0, f4);
- __ mfc1(v1, f5);
+ if (IsMipsSoftFloatABI) {
+ __ Move(v0, v1, f4);
+ } else {
+ __ mov_d(f12, f4);
+ }
switch (type_) {
case TranscendentalCache::SIN:
__ CallCFunction(
&call_runtime);
__ push(ra);
__ PrepareCallCFunction(3, scratch);
- // ABI (o32) for func(double d, int x): d in f12, x in a2.
- ASSERT(double_base.is(f12));
- ASSERT(exponent.is(a2));
- if (IsMipsSoftFloatABI) {
- // Simulator case, supports FPU, but with soft-float passing.
- __ mfc1(a0, double_base);
- __ mfc1(a1, FPURegister::from_code(double_base.code() + 1));
- }
+ __ SetCallCDoubleArguments(double_base, double_exponent);
__ CallCFunction(
- ExternalReference::power_double_int_function(masm->isolate()), 3);
+ ExternalReference::power_double_int_function(masm->isolate()), 4);
__ pop(ra);
__ GetCFunctionDoubleResult(double_result);
__ sdc1(double_result,
// ABI (o32) for func(double a, double b): a in f12, b in f14.
ASSERT(double_base.is(f12));
ASSERT(double_exponent.is(f14));
- if (IsMipsSoftFloatABI) {
- __ mfc1(a0, double_base);
- __ mfc1(a1, FPURegister::from_code(double_base.code() + 1));
- __ mfc1(a2, double_exponent);
- __ mfc1(a3, FPURegister::from_code(double_exponent.code() + 1));
- }
+ __ SetCallCDoubleArguments(double_base, double_exponent);
__ CallCFunction(
ExternalReference::power_double_double_function(masm->isolate()), 4);
__ pop(ra);
}
-// Uses registers a0 to t0. Expected input is
-// object in a0 (or at sp+1*kPointerSize) and function in
-// a1 (or at sp), depending on whether or not
-// args_in_registers() is true.
+// Uses registers a0 to t0.
+// Expected input (depending on whether args are in registers or on the stack):
+// * object: a0 or at sp + 1 * kPointerSize.
+// * function: a1 or at sp.
+//
+// Inlined call site patching is a crankshaft-specific feature that is not
+// implemented on MIPS.
void InstanceofStub::Generate(MacroAssembler* masm) {
+ // This is a crankshaft-specific feature that has not been implemented yet.
+ ASSERT(!HasCallSiteInlineCheck());
+ // Call site inlining and patching implies arguments in registers.
+ ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
+ // ReturnTrueFalse is only implemented for inlined call sites.
+ ASSERT(!ReturnTrueFalseObject() || HasCallSiteInlineCheck());
+
// Fixed register usage throughout the stub:
const Register object = a0; // Object (lhs).
- const Register map = a3; // Map of the object.
+ Register map = a3; // Map of the object.
const Register function = a1; // Function (rhs).
const Register prototype = t0; // Prototype of the function.
+ const Register inline_site = t5;
const Register scratch = a2;
+
Label slow, loop, is_instance, is_not_instance, not_js_object;
+
if (!HasArgsInRegisters()) {
__ lw(object, MemOperand(sp, 1 * kPointerSize));
__ lw(function, MemOperand(sp, 0));
__ JumpIfSmi(object, ¬_js_object);
__ IsObjectJSObjectType(object, map, scratch, ¬_js_object);
- // Look up the function and the map in the instanceof cache.
- Label miss;
- __ LoadRoot(t1, Heap::kInstanceofCacheFunctionRootIndex);
- __ Branch(&miss, ne, function, Operand(t1));
- __ LoadRoot(t1, Heap::kInstanceofCacheMapRootIndex);
- __ Branch(&miss, ne, map, Operand(t1));
- __ LoadRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
- __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+ // If there is a call site cache don't look in the global cache, but do the
+ // real lookup and update the call site cache.
+ if (!HasCallSiteInlineCheck()) {
+ Label miss;
+ __ LoadRoot(t1, Heap::kInstanceofCacheFunctionRootIndex);
+ __ Branch(&miss, ne, function, Operand(t1));
+ __ LoadRoot(t1, Heap::kInstanceofCacheMapRootIndex);
+ __ Branch(&miss, ne, map, Operand(t1));
+ __ LoadRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
+
+ __ bind(&miss);
+ }
- __ bind(&miss);
+ // Get the prototype of the function.
__ TryGetFunctionPrototype(function, prototype, scratch, &slow);
// Check that the function prototype is a JS object.
__ JumpIfSmi(prototype, &slow);
__ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
- __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
- __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex);
+ // Update the global instanceof or call site inlined cache with the current
+ // map and function. The cached answer will be set when it is known below.
+ if (!HasCallSiteInlineCheck()) {
+ __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
+ __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex);
+ } else {
+ UNIMPLEMENTED_MIPS();
+ }
// Register mapping: a3 is object map and t0 is function prototype.
// Get prototype of object into a2.
__ lw(scratch, FieldMemOperand(map, Map::kPrototypeOffset));
+ // We don't need map any more. Use it as a scratch register.
+ Register scratch2 = map;
+ map = no_reg;
+
// Loop through the prototype chain looking for the function prototype.
+ __ LoadRoot(scratch2, Heap::kNullValueRootIndex);
__ bind(&loop);
__ Branch(&is_instance, eq, scratch, Operand(prototype));
- __ LoadRoot(t1, Heap::kNullValueRootIndex);
- __ Branch(&is_not_instance, eq, scratch, Operand(t1));
+ __ Branch(&is_not_instance, eq, scratch, Operand(scratch2));
__ lw(scratch, FieldMemOperand(scratch, HeapObject::kMapOffset));
__ lw(scratch, FieldMemOperand(scratch, Map::kPrototypeOffset));
__ Branch(&loop);
__ bind(&is_instance);
ASSERT(Smi::FromInt(0) == 0);
- __ mov(v0, zero_reg);
- __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
+ if (!HasCallSiteInlineCheck()) {
+ __ mov(v0, zero_reg);
+ __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
+ } else {
+ UNIMPLEMENTED_MIPS();
+ }
__ DropAndRet(HasArgsInRegisters() ? 0 : 2);
__ bind(&is_not_instance);
- __ li(v0, Operand(Smi::FromInt(1)));
- __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
+ if (!HasCallSiteInlineCheck()) {
+ __ li(v0, Operand(Smi::FromInt(1)));
+ __ StoreRoot(v0, Heap::kInstanceofCacheAnswerRootIndex);
+ } else {
+ UNIMPLEMENTED_MIPS();
+ }
__ DropAndRet(HasArgsInRegisters() ? 0 : 2);
Label object_not_null, object_not_null_or_smi;
// Before null, smi and string value checks, check that the rhs is a function
// as for a non-function rhs an exception needs to be thrown.
__ JumpIfSmi(function, &slow);
- __ GetObjectType(function, map, scratch);
+ __ GetObjectType(function, scratch2, scratch);
__ Branch(&slow, ne, scratch, Operand(JS_FUNCTION_TYPE));
// Null is not instance of anything.
// Slow-case. Tail call builtin.
__ bind(&slow);
- if (HasArgsInRegisters()) {
+ if (!ReturnTrueFalseObject()) {
+ if (HasArgsInRegisters()) {
+ __ Push(a0, a1);
+ }
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
+ } else {
+ __ EnterInternalFrame();
__ Push(a0, a1);
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
+ __ LeaveInternalFrame();
+ __ mov(a0, v0);
+ __ LoadRoot(v0, Heap::kTrueValueRootIndex);
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2, eq, a0, Operand(zero_reg));
+ __ LoadRoot(v0, Heap::kFalseValueRootIndex);
+ __ DropAndRet(HasArgsInRegisters() ? 0 : 2);
}
- __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
}
+Register InstanceofStub::left() { return a0; }
+
+
+Register InstanceofStub::right() { return a1; }
+
+
void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
// The displacement is the offset of the last parameter (if any)
// relative to the frame pointer.
namespace v8 {
namespace internal {
-#if(defined(__mips_hard_float) && __mips_hard_float != 0)
-// Use floating-point coprocessor instructions. This flag is raised when
-// -mhard-float is passed to the compiler.
-static const bool IsMipsSoftFloatABI = false;
-#elif(defined(__mips_soft_float) && __mips_soft_float != 0)
-// Not using floating-point coprocessor instructions. This flag is raised when
-// -msoft-float is passed to the compiler.
-static const bool IsMipsSoftFloatABI = true;
-#else
-static const bool IsMipsSoftFloatABI = true;
-#endif
-
// Forward declarations
class CompilationInfo;
#endif
+#if(defined(__mips_hard_float) && __mips_hard_float != 0)
+// Use floating-point coprocessor instructions. This flag is raised when
+// -mhard-float is passed to the compiler.
+static const bool IsMipsSoftFloatABI = false;
+#elif(defined(__mips_soft_float) && __mips_soft_float != 0)
+// Not using floating-point coprocessor instructions. This flag is raised when
+// -msoft-float is passed to the compiler.
+static const bool IsMipsSoftFloatABI = true;
+#else
+static const bool IsMipsSoftFloatABI = true;
+#endif
+
+
// Defines constants and accessor classes to assemble, disassemble and
// simulate MIPS32 instructions.
//
// call t9 (jalr t9 / nop instruction pair)
CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
patcher.masm()->li(v8::internal::t9, Operand(reinterpret_cast<int32_t>(
- Isolate::Current()->debug()->debug_break_return()->entry())));
+ Isolate::Current()->debug()->debug_break_slot()->entry())));
patcher.masm()->Call(v8::internal::t9);
}
typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved];
+static const int kUndefIndex = -1;
+// Map with indexes on stack that corresponds to codes of saved registers.
+static const int kSafepointRegisterStackIndexMap[kNumRegs] = {
+ kUndefIndex,
+ kUndefIndex,
+ 0, // v0
+ kUndefIndex,
+ 1, // a0
+ 2, // a1
+ 3, // a2
+ 4, // a3
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ 5, // Saved temporaries.
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ kUndefIndex,
+ 13, // gp
+ 14, // sp
+ 15, // fp
+ kUndefIndex
+};
+
// ----------------------------------------------------
// check for an enum cache. Leave the map in a2 for the subsequent
// prototype load.
__ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset));
- __ lw(a3, FieldMemOperand(a2, Map::kInstanceDescriptorsOffset));
- __ Branch(&call_runtime, eq, a3, Operand(empty_descriptor_array_value));
+ __ lw(a3, FieldMemOperand(a2, Map::kInstanceDescriptorsOrBitField3Offset));
+ __ JumpIfSmi(a3, &call_runtime);
// Check that there is an enum cache in the non-empty instance
// descriptors (a3). This is the case if the next enumeration
// We got a map in register v0. Get the enumeration cache from it.
__ bind(&use_cache);
- __ lw(a1, FieldMemOperand(v0, Map::kInstanceDescriptorsOffset));
+ __ LoadInstanceDescriptors(v0, a1);
__ lw(a1, FieldMemOperand(a1, DescriptorArray::kEnumerationIndexOffset));
__ lw(a2, FieldMemOperand(a1, DescriptorArray::kEnumCacheBridgeCacheOffset));
// Look for valueOf symbol in the descriptor array, and indicate false if
// found. The type is not checked, so if it is a transition it is a false
// negative.
- __ lw(t0, FieldMemOperand(a1, Map::kInstanceDescriptorsOffset));
+ __ LoadInstanceDescriptors(a1, t0);
__ lw(a3, FieldMemOperand(t0, FixedArray::kLengthOffset));
// t0: descriptor array
// a3: length of descriptor array
// 0x41300000 is the top half of 1.0 x 2^20 as a double.
__ li(a1, Operand(0x41300000));
// Move 0x41300000xxxxxxxx (x = random bits in v0) to FPU.
- __ mtc1(a1, f13);
- __ mtc1(v0, f12);
+ __ Move(f12, v0, a1);
// Move 0x4130000000000000 to FPU.
- __ mtc1(a1, f15);
- __ mtc1(zero_reg, f14);
+ __ Move(f14, zero_reg, a1);
// Subtract and store the result in the heap number.
__ sub_d(f0, f12, f14);
__ sdc1(f0, MemOperand(s0, HeapNumber::kValueOffset - kHeapObjectTag));
sw(scratch, MemOperand(object, Page::kDirtyFlagOffset));
}
+// Push and pop all registers that can hold pointers.
+void MacroAssembler::PushSafepointRegisters() {
+ // Safepoints expect a block of kNumSafepointRegisters values on the
+ // 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));
+ MultiPush(kSafepointSavedRegisters);
+}
+
+void MacroAssembler::PopSafepointRegisters() {
+ const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
+ MultiPop(kSafepointSavedRegisters);
+ Addu(sp, sp, Operand(num_unsaved * kPointerSize));
+}
+
+void MacroAssembler::PushSafepointRegistersAndDoubles() {
+ PushSafepointRegisters();
+ Subu(sp, sp, Operand(FPURegister::kNumAllocatableRegisters * kDoubleSize));
+ for (int i = 0; i < FPURegister::kNumAllocatableRegisters; i+=2) {
+ FPURegister reg = FPURegister::FromAllocationIndex(i);
+ sdc1(reg, MemOperand(sp, i * kDoubleSize));
+ }
+}
+
+void MacroAssembler::PopSafepointRegistersAndDoubles() {
+ for (int i = 0; i < FPURegister::kNumAllocatableRegisters; i+=2) {
+ FPURegister reg = FPURegister::FromAllocationIndex(i);
+ ldc1(reg, MemOperand(sp, i * kDoubleSize));
+ }
+ Addu(sp, sp, Operand(FPURegister::kNumAllocatableRegisters * kDoubleSize));
+ PopSafepointRegisters();
+}
+
+void MacroAssembler::StoreToSafepointRegistersAndDoublesSlot(Register src,
+ Register dst) {
+ sw(src, SafepointRegistersAndDoublesSlot(dst));
+}
+
+
+void MacroAssembler::StoreToSafepointRegisterSlot(Register src, Register dst) {
+ sw(src, SafepointRegisterSlot(dst));
+}
+
+
+void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) {
+ lw(dst, SafepointRegisterSlot(src));
+}
+
+
+int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
+ // The registers are pushed starting with the highest encoding,
+ // which means that lowest encodings are closest to the stack pointer.
+ return kSafepointRegisterStackIndexMap[reg_code];
+}
+
+
+MemOperand MacroAssembler::SafepointRegisterSlot(Register reg) {
+ return MemOperand(sp, SafepointRegisterStackIndex(reg.code()) * kPointerSize);
+}
+
+
+MemOperand MacroAssembler::SafepointRegistersAndDoublesSlot(Register reg) {
+ // General purpose registers are pushed last on the stack.
+ int doubles_size = FPURegister::kNumAllocatableRegisters * kDoubleSize;
+ int register_offset = SafepointRegisterStackIndex(reg.code()) * kPointerSize;
+ return MemOperand(sp, doubles_size + register_offset);
+}
+
+
+
void MacroAssembler::InNewSpace(Register object,
Register scratch,
}
-void MacroAssembler::Move(Register dst, Register src) {
- if (!dst.is(src)) {
- mov(dst, src);
- }
-}
-
-
#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::DebugBreak() {
void MacroAssembler::GetCFunctionDoubleResult(const DoubleRegister dst) {
+ CpuFeatures::Scope scope(FPU);
if (IsMipsSoftFloatABI) {
- mtc1(v0, dst);
- mtc1(v1, FPURegister::from_code(dst.code() + 1));
+ Move(v0, v1, dst);
+ } else {
+ Move(f0, dst); // Reg f0 is o32 ABI FP return value.
+ }
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg) {
+ CpuFeatures::Scope scope(FPU);
+ if (!IsMipsSoftFloatABI) {
+ Move(f12, dreg);
} else {
- if (!dst.is(f0)) {
- mov_d(dst, f0); // Reg f0 is o32 ABI FP return value.
+ Move(a0, a1, dreg);
+ }
+}
+
+
+void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg1,
+ DoubleRegister dreg2) {
+ CpuFeatures::Scope scope(FPU);
+ if (!IsMipsSoftFloatABI) {
+ if (dreg2.is(f12)) {
+ ASSERT(!dreg1.is(f14));
+ Move(f14, dreg2);
+ Move(f12, dreg1);
+ } else {
+ Move(f12, dreg1);
+ Move(f14, dreg2);
}
+ } else {
+ Move(a0, a1, dreg1);
+ Move(a2, a3, dreg2);
}
}
+void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg,
+ Register reg) {
+ CpuFeatures::Scope scope(FPU);
+ if (!IsMipsSoftFloatABI) {
+ Move(f12, dreg);
+ Move(a2, reg);
+ } else {
+ Move(a2, reg);
+ Move(a0, a1, dreg);
+ }
+}
+
// -----------------------------------------------------------------------------
// JavaScript invokes.
li(t8, Operand(ExternalReference(Isolate::k_context_address, isolate())));
sw(cp, MemOperand(t8));
- // Ensure we are not saving doubles, since it's not implemented yet.
- ASSERT(save_doubles == 0);
+ const int frame_alignment = MacroAssembler::ActivationFrameAlignment();
+ if (save_doubles) {
+ // The stack must be allign to 0 modulo 8 for stores with sdc1.
+ ASSERT(kDoubleSize == frame_alignment);
+ if (frame_alignment > 0) {
+ ASSERT(IsPowerOf2(frame_alignment));
+ And(sp, sp, Operand(-frame_alignment)); // Align stack.
+ }
+ int space = FPURegister::kNumRegisters * kDoubleSize;
+ 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));
+ }
+ }
// 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));
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);
+ // Optionally restore all double registers.
+ if (save_doubles) {
+ // Remember: we only need to restore every 2nd double FPU value.
+ lw(t8, MemOperand(fp, ExitFrameConstants::kSPOffset));
+ for (int i = 0; i < FPURegister::kNumRegisters; i+=2) {
+ FPURegister reg = FPURegister::from_code(i);
+ ldc1(reg, MemOperand(t8, i * kDoubleSize + kPointerSize));
+ }
+ }
// Clear top frame.
li(t8, Operand(ExternalReference(Isolate::k_c_entry_fp_address, isolate())));
#undef BRANCH_ARGS_CHECK
+void MacroAssembler::LoadInstanceDescriptors(Register map,
+ Register descriptors) {
+ lw(descriptors,
+ FieldMemOperand(map, Map::kInstanceDescriptorsOrBitField3Offset));
+ Label not_smi;
+ JumpIfNotSmi(descriptors, ¬_smi);
+ li(descriptors, Operand(FACTORY->empty_descriptor_array()));
+ bind(¬_smi);
+}
+
+
CodePatcher::CodePatcher(byte* address, int instructions)
: address_(address),
instructions_(instructions),
void Swap(Register reg1, Register reg2, Register scratch = no_reg);
void Call(Label* target);
- // May do nothing if the registers are identical.
- void Move(Register dst, Register src);
+ inline void Move(Register dst, Register src) {
+ if (!dst.is(src)) {
+ mov(dst, src);
+ }
+ }
+
+ inline void Move(FPURegister dst, FPURegister src) {
+ if (!dst.is(src)) {
+ mov_d(dst, src);
+ }
+ }
+
+ inline void Move(Register dst_low, Register dst_high, FPURegister src) {
+ mfc1(dst_low, src);
+ mfc1(dst_high, FPURegister::from_code(src.code() + 1));
+ }
+
+ inline void Move(FPURegister dst, Register src_low, Register src_high) {
+ mtc1(src_low, dst);
+ mtc1(src_high, FPURegister::from_code(dst.code() + 1));
+ }
// Jump unconditionally to given label.
// We NEED a nop in the branch delay slot, as it used by v8, for example in
Addu(sp, sp, Operand(count * kPointerSize));
}
- // ---------------------------------------------------------------------------
- // These functions are only used by crankshaft, so they are currently
- // unimplemented.
-
// Push and pop the registers that can hold pointers, as defined by the
// RegList constant kSafepointSavedRegisters.
- void PushSafepointRegisters() {
- UNIMPLEMENTED_MIPS();
- }
-
- void PopSafepointRegisters() {
- UNIMPLEMENTED_MIPS();
- }
-
- void PushSafepointRegistersAndDoubles() {
- UNIMPLEMENTED_MIPS();
- }
-
- void PopSafepointRegistersAndDoubles() {
- UNIMPLEMENTED_MIPS();
- }
-
- static int SafepointRegisterStackIndex(int reg_code) {
- UNIMPLEMENTED_MIPS();
- return 0;
- }
-
- // ---------------------------------------------------------------------------
+ void PushSafepointRegisters();
+ void PopSafepointRegisters();
+ void PushSafepointRegistersAndDoubles();
+ void PopSafepointRegistersAndDoubles();
+ // Store value in register src in the safepoint stack slot for
+ // register dst.
+ void StoreToSafepointRegisterSlot(Register src, Register dst);
+ void StoreToSafepointRegistersAndDoublesSlot(Register src, Register dst);
+ // Load the value of the src register from its safepoint stack slot
+ // into register dst.
+ void LoadFromSafepointRegisterSlot(Register dst, Register src);
// MIPS32 R2 instruction macro.
void Ins(Register rt, Register rs, uint16_t pos, uint16_t size);
void CallCFunction(Register function, Register scratch, int num_arguments);
void GetCFunctionDoubleResult(const DoubleRegister dst);
+ // There are two ways of passing double arguments on MIPS, depending on
+ // whether soft or hard floating point ABI is used. These functions
+ // abstract parameter passing for the three different ways we call
+ // C functions from generated code.
+ void SetCallCDoubleArguments(DoubleRegister dreg);
+ void SetCallCDoubleArguments(DoubleRegister dreg1, DoubleRegister dreg2);
+ void SetCallCDoubleArguments(DoubleRegister dreg, Register reg);
+
// Calls an API function. Allocates HandleScope, extracts returned value
// from handle and propagates exceptions. Restores context.
MaybeObject* TryCallApiFunctionAndReturn(ExternalReference function,
Register scratch2,
Label* failure);
+ void LoadInstanceDescriptors(Register map, Register descriptors);
+
private:
void CallCFunctionHelper(Register function,
ExternalReference function_reference,
Register scratch1,
Register scratch2);
+ // Compute memory operands for safepoint stack slots.
+ static int SafepointRegisterStackIndex(int reg_code);
+ MemOperand SafepointRegisterSlot(Register reg);
+ MemOperand SafepointRegistersAndDoublesSlot(Register reg);
bool generating_stub_;
bool allow_stub_calls_;
// This handle will be patched with the code object on installation.
Handle<Object> code_object_;
+
+ // Needs access to SafepointRegisterStackIndex for optimized frame
+ // traversal.
+ friend class OptimizedFrame;
};
} } // namespace v8::internal
#endif // V8_MIPS_MACRO_ASSEMBLER_MIPS_H_
-
end = cur + words;
while (cur < end) {
- PrintF(" 0x%08x: 0x%08x %10d\n",
+ PrintF(" 0x%08x: 0x%08x %10d",
reinterpret_cast<intptr_t>(cur), *cur, *cur);
+ HeapObject* obj = reinterpret_cast<HeapObject*>(*cur);
+ int value = *cur;
+ Heap* current_heap = v8::internal::Isolate::Current()->heap();
+ if (current_heap->Contains(obj) || ((value & 1) == 0)) {
+ PrintF(" (");
+ if ((value & 1) == 0) {
+ PrintF("smi %d", value / 2);
+ } else {
+ obj->ShortPrint();
+ }
+ PrintF(")");
+ }
+ PrintF("\n");
cur++;
}
- } else if ((strcmp(cmd, "disasm") == 0) || (strcmp(cmd, "dpc") == 0)) {
+ } else if ((strcmp(cmd, "disasm") == 0) ||
+ (strcmp(cmd, "dpc") == 0) ||
+ (strcmp(cmd, "di") == 0)) {
disasm::NameConverter converter;
disasm::Disassembler dasm(converter);
// Use a reasonably large buffer.
cur = reinterpret_cast<byte*>(sim_->get_pc());
end = cur + (10 * Instruction::kInstrSize);
} else if (argc == 2) {
- int32_t value;
- if (GetValue(arg1, &value)) {
- cur = reinterpret_cast<byte*>(value);
- // No length parameter passed, assume 10 instructions.
- end = cur + (10 * Instruction::kInstrSize);
+ int regnum = Registers::Number(arg1);
+ if (regnum != kInvalidRegister || strncmp(arg1, "0x", 2) == 0) {
+ // The argument is an address or a register name.
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ cur = reinterpret_cast<byte*>(value);
+ // Disassemble 10 instructions at <arg1>.
+ end = cur + (10 * Instruction::kInstrSize);
+ }
+ } else {
+ // The argument is the number of instructions.
+ int32_t value;
+ if (GetValue(arg1, &value)) {
+ cur = reinterpret_cast<byte*>(sim_->get_pc());
+ // Disassemble <arg1> instructions.
+ end = cur + (value * Instruction::kInstrSize);
+ }
}
} else {
int32_t value1;
PrintF("flags\n");
PrintF(" print flags\n");
PrintF("disasm [<instructions>]\n");
- PrintF("disasm [[<address>] <instructions>]\n");
- PrintF(" disassemble code, default is 10 instructions from pc\n");
+ PrintF("disasm [<address/register>]\n");
+ PrintF("disasm [[<address/register>] <instructions>]\n");
+ PrintF(" disassemble code, default is 10 instructions\n");
+ PrintF(" from pc (alias 'di')\n");
PrintF("gdb\n");
PrintF(" enter gdb\n");
PrintF("break <address>\n");
}
+// For use in calls that take two double values, constructed either
+// from a0-a3 or f12 and f14.
+void Simulator::GetFpArgs(double* x, double* y) {
+ if (!IsMipsSoftFloatABI) {
+ *x = get_fpu_register_double(12);
+ *y = get_fpu_register_double(14);
+ } else {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[sizeof(*x)];
+ int32_t* reg_buffer = reinterpret_cast<int32_t*>(buffer);
+
+ // Registers a0 and a1 -> x.
+ reg_buffer[0] = get_register(a0);
+ reg_buffer[1] = get_register(a1);
+ memcpy(x, buffer, sizeof(buffer));
+
+ // Registers a2 and a3 -> y.
+ reg_buffer[0] = get_register(a2);
+ reg_buffer[1] = get_register(a3);
+ memcpy(y, buffer, sizeof(buffer));
+ }
+}
+
+
+// For use in calls that take one double value, constructed either
+// from a0 and a1 or f12.
+void Simulator::GetFpArgs(double* x) {
+ if (!IsMipsSoftFloatABI) {
+ *x = get_fpu_register_double(12);
+ } else {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[sizeof(*x)];
+ int32_t* reg_buffer = reinterpret_cast<int32_t*>(buffer);
+ // Registers a0 and a1 -> x.
+ reg_buffer[0] = get_register(a0);
+ reg_buffer[1] = get_register(a1);
+ memcpy(x, buffer, sizeof(buffer));
+ }
+}
+
+
+// For use in calls that take one double value constructed either
+// from a0 and a1 or f12 and one integer value.
+void Simulator::GetFpArgs(double* x, int32_t* y) {
+ if (!IsMipsSoftFloatABI) {
+ *x = get_fpu_register_double(12);
+ *y = get_register(a2);
+ } else {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[sizeof(*x)];
+ int32_t* reg_buffer = reinterpret_cast<int32_t*>(buffer);
+ // Registers 0 and 1 -> x.
+ reg_buffer[0] = get_register(a0);
+ reg_buffer[1] = get_register(a1);
+ memcpy(x, buffer, sizeof(buffer));
+
+ // Register 2 -> y.
+ reg_buffer[0] = get_register(a2);
+ memcpy(y, buffer, sizeof(*y));
+ }
+}
+
+
+// The return value is either in v0/v1 or f0.
+void Simulator::SetFpResult(const double& result) {
+ if (!IsMipsSoftFloatABI) {
+ set_fpu_register_double(0, result);
+ } else {
+ char buffer[2 * sizeof(registers_[0])];
+ int32_t* reg_buffer = reinterpret_cast<int32_t*>(buffer);
+ memcpy(buffer, &result, sizeof(buffer));
+ // Copy result to v0 and v1.
+ set_register(v0, reg_buffer[0]);
+ set_register(v1, reg_buffer[1]);
+ }
+}
+
+
// Helper functions for setting and testing the FCSR register's bits.
void Simulator::set_fcsr_bit(uint32_t cc, bool value) {
if (value) {
(redirection->type() == ExternalReference::BUILTIN_FP_CALL) ||
(redirection->type() == ExternalReference::BUILTIN_FP_INT_CALL);
+ if (!IsMipsSoftFloatABI) {
+ // With the hard floating point calling convention, double
+ // arguments are passed in FPU registers. Fetch the arguments
+ // from there and call the builtin using soft floating point
+ // convention.
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_FP_FP_CALL:
+ case ExternalReference::BUILTIN_COMPARE_CALL:
+ arg0 = get_fpu_register(f12);
+ arg1 = get_fpu_register(f13);
+ arg2 = get_fpu_register(f14);
+ arg3 = get_fpu_register(f15);
+ break;
+ case ExternalReference::BUILTIN_FP_CALL:
+ arg0 = get_fpu_register(f12);
+ arg1 = get_fpu_register(f13);
+ break;
+ case ExternalReference::BUILTIN_FP_INT_CALL:
+ arg0 = get_fpu_register(f12);
+ arg1 = get_fpu_register(f13);
+ arg2 = get_register(a2);
+ break;
+ default:
+ break;
+ }
+ }
+
// This is dodgy but it works because the C entry stubs are never moved.
// See comment in codegen-arm.cc and bug 1242173.
int32_t saved_ra = get_register(ra);
// Based on CpuFeatures::IsSupported(FPU), Mips will use either hardware
// FPU, or gcc soft-float routines. Hardware FPU is simulated in this
// simulator. Soft-float has additional abstraction of ExternalReference,
- // to support serialization. Finally, when simulated on x86 host, the
- // x86 softfloat routines are used, and this Redirection infrastructure
- // lets simulated-mips make calls into x86 C code.
- // When doing that, the 'double' return type must be handled differently
- // than the usual int64_t return. The data is returned in different
- // registers and cannot be cast from one type to the other. However, the
- // calling arguments are passed the same way in both cases.
+ // to support serialization.
if (fp_call) {
SimulatorRuntimeFPCall target =
reinterpret_cast<SimulatorRuntimeFPCall>(external);
if (::v8::internal::FLAG_trace_sim) {
- PrintF(
- "Call to host function at %p args %08x, %08x, %08x, %08x\n",
- FUNCTION_ADDR(target),
- arg0,
- arg1,
- arg2,
- arg3);
- }
+ double dval0, dval1;
+ int32_t ival;
+ switch (redirection->type()) {
+ case ExternalReference::BUILTIN_FP_FP_CALL:
+ case ExternalReference::BUILTIN_COMPARE_CALL:
+ GetFpArgs(&dval0, &dval1);
+ PrintF("Call to host function at %p with args %f, %f",
+ FUNCTION_ADDR(target), dval0, dval1);
+ break;
+ case ExternalReference::BUILTIN_FP_CALL:
+ GetFpArgs(&dval0);
+ PrintF("Call to host function at %p with arg %f",
+ FUNCTION_ADDR(target), dval1);
+ break;
+ case ExternalReference::BUILTIN_FP_INT_CALL:
+ GetFpArgs(&dval0, &ival);
+ PrintF("Call to host function at %p with args %f, %d",
+ FUNCTION_ADDR(target), dval0, ival);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
double result = target(arg0, arg1, arg2, arg3);
- // fp result -> registers v0 and v1.
- int32_t gpreg_pair[2];
- memcpy(&gpreg_pair[0], &result, 2 * sizeof(int32_t));
- set_register(v0, gpreg_pair[0]);
- set_register(v1, gpreg_pair[1]);
+ if (redirection->type() != ExternalReference::BUILTIN_COMPARE_CALL) {
+ SetFpResult(result);
+ } else {
+ int32_t gpreg_pair[2];
+ memcpy(&gpreg_pair[0], &result, 2 * sizeof(int32_t));
+ set_register(v0, gpreg_pair[0]);
+ set_register(v1, gpreg_pair[1]);
+ }
} else if (redirection->type() == ExternalReference::DIRECT_API_CALL) {
// See DirectCEntryStub::GenerateCall for explanation of register usage.
SimulatorRuntimeDirectApiCall target =
static void* RedirectExternalReference(void* external_function,
ExternalReference::Type type);
- // Used for real time calls that takes two double values as arguments and
- // returns a double.
- void SetFpResult(double result);
+ // For use in calls that take double value arguments.
+ void GetFpArgs(double* x, double* y);
+ void GetFpArgs(double* x);
+ void GetFpArgs(double* x, int32_t* y);
+ void SetFpResult(const double& result);
+
// Architecture state.
// Registers.
a3,
Handle<Map>(receiver->map()),
Handle<Code>(stub),
- DONT_DO_SMI_CHECK);
+ DO_SMI_CHECK);
Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
__ Jump(ic, RelocInfo::CODE_TARGET);
__ sdc1(f0, MemOperand(v0, HeapNumber::kValueOffset - kHeapObjectTag));
__ Ret();
} else {
- WriteInt32ToHeapNumberStub stub(value, v0, t2, t3);
- __ TailCallStub(&stub);
+ Register dst1 = t2;
+ Register dst2 = t3;
+ FloatingPointHelper::Destination dest =
+ FloatingPointHelper::kCoreRegisters;
+ FloatingPointHelper::ConvertIntToDouble(masm,
+ value,
+ dest,
+ f0,
+ dst1,
+ dst2,
+ t1,
+ f2);
+ __ sw(dst1, FieldMemOperand(v0, HeapNumber::kMantissaOffset));
+ __ sw(dst2, FieldMemOperand(v0, HeapNumber::kExponentOffset));
+ __ Ret();
}
} else if (array_type == kExternalUnsignedIntArray) {
// The test is different for unsigned int values. Since we need