Enables language tests for SIMMIPS.
R=regis@google.com
Review URL: https://codereview.chromium.org//17131002
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@24098 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/runtime/vm/assembler_mips.cc b/runtime/vm/assembler_mips.cc
index 8f43046..f4f3de1 100644
--- a/runtime/vm/assembler_mips.cc
+++ b/runtime/vm/assembler_mips.cc
@@ -346,14 +346,14 @@
// Setup pool pointer for this stub.
Label next;
bal(&next);
- delay_slot()->mov(T0, RA);
+ delay_slot()->mov(TMP1, RA);
const intptr_t object_pool_pc_dist =
Instructions::HeaderSize() - Instructions::object_pool_offset() +
CodeSize();
Bind(&next);
- lw(PP, Address(T0, -object_pool_pc_dist));
+ lw(PP, Address(TMP1, -object_pool_pc_dist));
} else {
addiu(SP, SP, Immediate(-3 * kWordSize));
sw(ZR, Address(SP, 2 * kWordSize)); // PC marker is 0 in stubs.
diff --git a/runtime/vm/assembler_mips.h b/runtime/vm/assembler_mips.h
index 9b22f3e..0b74365 100644
--- a/runtime/vm/assembler_mips.h
+++ b/runtime/vm/assembler_mips.h
@@ -67,6 +67,9 @@
return Utils::IsInt(kImmBits, offset);
}
+ Register base() const { return base_; }
+ int32_t offset() const { return offset_; }
+
private:
Register base_;
int32_t offset_;
@@ -439,6 +442,12 @@
EmitRType(SPECIAL2, rs, rd, rd, 0, CLZ);
}
+ // Convert a 32-bit float in fs to a 64-bit double in dd.
+ void cvtds(DRegister dd, FRegister fs) {
+ FRegister fd = static_cast<FRegister>(dd * 2);
+ EmitFpuRType(COP1, FMT_S, F0, fs, fd, COP1_CVT_D);
+ }
+
// Converts a 32-bit signed int in fs to a double in fd.
void cvtdw(DRegister dd, FRegister fs) {
FRegister fd = static_cast<FRegister>(dd * 2);
@@ -553,10 +562,20 @@
EmitFpuRType(COP1, FMT_D, F0, fs, fd, COP1_MOV);
}
+ // Move if floating point false.
+ void movf(Register rd, Register rs) {
+ EmitRType(SPECIAL, rs, R0, rd, 0, MOVCI);
+ }
+
void movn(Register rd, Register rs, Register rt) {
EmitRType(SPECIAL, rs, rt, rd, 0, MOVN);
}
+ // Move if floating point true.
+ void movt(Register rd, Register rs) {
+ EmitRType(SPECIAL, rs, R1, rd, 0, MOVCI);
+ }
+
void movz(Register rd, Register rs, Register rt) {
EmitRType(SPECIAL, rs, rt, rd, 0, MOVZ);
}
@@ -972,6 +991,20 @@
sra(reg, reg, kSmiTagSize);
}
+ void StoreDToOffset(DRegister reg, Register base, int32_t offset) {
+ FRegister lo = static_cast<FRegister>(reg * 2);
+ FRegister hi = static_cast<FRegister>(reg * 2 + 1);
+ swc1(lo, Address(base, offset));
+ swc1(hi, Address(base, offset + kWordSize));
+ }
+
+ void LoadDFromOffset(DRegister reg, Register base, int32_t offset) {
+ FRegister lo = static_cast<FRegister>(reg * 2);
+ FRegister hi = static_cast<FRegister>(reg * 2 + 1);
+ lwc1(lo, Address(base, offset));
+ lwc1(hi, Address(base, offset + kWordSize));
+ }
+
void ReserveAlignedFrameSpace(intptr_t frame_space);
// Create a frame for calling into runtime that preserves all volatile
diff --git a/runtime/vm/code_patcher_mips.cc b/runtime/vm/code_patcher_mips.cc
index 96088fd..5cd9108 100644
--- a/runtime/vm/code_patcher_mips.cc
+++ b/runtime/vm/code_patcher_mips.cc
@@ -47,7 +47,9 @@
void CodePatcher::InsertCallAt(uword start, uword target) {
- UNIMPLEMENTED();
+ // The inserted call should not overlap the lazy deopt jump code.
+ ASSERT(start + CallPattern::kFixedLengthInBytes <= target);
+ CallPattern::InsertAt(start, target);
}
diff --git a/runtime/vm/constants_mips.h b/runtime/vm/constants_mips.h
index e016f7b..b1f910a 100644
--- a/runtime/vm/constants_mips.h
+++ b/runtime/vm/constants_mips.h
@@ -156,9 +156,13 @@
kNoDRegister = -1,
};
+const DRegister DTMP = D9;
+const FRegister STMP1 = F18;
+const FRegister STMP2 = F19;
+
// Architecture independent aliases.
typedef DRegister FpuRegister;
-const FpuRegister FpuTMP = D0;
+const FpuRegister FpuTMP = DTMP;
const int kNumberOfFpuRegisters = kNumberOfDRegisters;
const FpuRegister kNoFpuRegister = kNoDRegister;
@@ -172,13 +176,6 @@
const Register SPREG = SP; // Stack pointer register.
const Register FPREG = FP; // Frame pointer register.
-// NULLREG holds reinterpret_cast<intptr_t>(Object::null()).
-// TODO(zra): Is it worthwhile to devote a register to this? Investigate
-// performance effects when we are running on real hardware. Same with
-// CMPRES. Try moving CTX and PP to T8 and T9 and shifting kLastCpuRegister
-// down to S7.
-const Register NULLREG = T8;
-
// The code that generates a comparison can be far away from the code that
// generates the branch that uses the result of that comparison. In this case,
// CMPRES is used for the result of the comparison.
diff --git a/runtime/vm/debugger_mips.cc b/runtime/vm/debugger_mips.cc
index c35df1b..a8721ac 100644
--- a/runtime/vm/debugger_mips.cc
+++ b/runtime/vm/debugger_mips.cc
@@ -48,7 +48,6 @@
Instr* instr5 = Instr::At(pc_ - 1 * Instr::kInstrSize);
#if defined(DEBUG)
-
instr1->AssertIsImmInstr(LW, SP, RA, 2 * kWordSize);
instr2->AssertIsImmInstr(LW, SP, FP, 1 * kWordSize);
instr3->AssertIsImmInstr(LW, SP, PP, 0 * kWordSize);
diff --git a/runtime/vm/disassembler_mips.cc b/runtime/vm/disassembler_mips.cc
index ecbea4f..e97b981 100644
--- a/runtime/vm/disassembler_mips.cc
+++ b/runtime/vm/disassembler_mips.cc
@@ -329,6 +329,14 @@
Format(instr, "mflo 'rd");
break;
}
+ case MOVCI: {
+ if (instr->Bit(16)) {
+ Format(instr, "movt 'rd, 'rs");
+ } else {
+ Format(instr, "movf 'rd, 'rs");
+ }
+ break;
+ }
case MOVN: {
Format(instr, "movn 'rd, 'rs, 'rt");
break;
@@ -523,35 +531,35 @@
break;
}
case COP1_C_F: {
- Format(instr, "c.f.'fmt 'fd, 'fs");
+ Format(instr, "c.f.'fmt 'fs, 'ft");
break;
}
case COP1_C_UN: {
- Format(instr, "c.un.'fmt 'fd, 'fs");
+ Format(instr, "c.un.'fmt 'fs, 'ft");
break;
}
case COP1_C_EQ: {
- Format(instr, "c.eq.'fmt 'fd, 'fs");
+ Format(instr, "c.eq.'fmt 'fs, 'ft");
break;
}
case COP1_C_UEQ: {
- Format(instr, "c.ueq.'fmt 'fd, 'fs");
+ Format(instr, "c.ueq.'fmt 'fs, 'ft");
break;
}
case COP1_C_OLT: {
- Format(instr, "c.olt.'fmt 'fd, 'fs");
+ Format(instr, "c.olt.'fmt 'fs, 'ft");
break;
}
case COP1_C_ULT: {
- Format(instr, "c.ult.'fmt 'fd, 'fs");
+ Format(instr, "c.ult.'fmt 'fs, 'ft");
break;
}
case COP1_C_OLE: {
- Format(instr, "c.ole.'fmt 'fd, 'fs");
+ Format(instr, "c.ole.'fmt 'fs, 'ft");
break;
}
case COP1_C_ULE: {
- Format(instr, "c.ule.'fmt 'fd, 'fs");
+ Format(instr, "c.ule.'fmt 'fs, 'ft");
break;
}
case COP1_CVT_D: {
diff --git a/runtime/vm/flow_graph_compiler_mips.cc b/runtime/vm/flow_graph_compiler_mips.cc
index 27abae0..a088cc6 100644
--- a/runtime/vm/flow_graph_compiler_mips.cc
+++ b/runtime/vm/flow_graph_compiler_mips.cc
@@ -166,7 +166,8 @@
Label* is_false) {
__ TraceSimMsg("BoolToJump");
Label fall_through;
- __ beq(bool_register, NULLREG, &fall_through);
+ __ BranchEqual(bool_register, reinterpret_cast<int32_t>(Object::null()),
+ &fall_through);
__ BranchEqual(bool_register, Bool::True(), is_true);
__ b(is_false);
__ Bind(&fall_through);
@@ -190,12 +191,12 @@
__ LoadObject(A2, type_test_cache);
if (test_kind == kTestTypeOneArg) {
ASSERT(type_arguments_reg == kNoRegister);
+ __ LoadImmediate(A1, reinterpret_cast<int32_t>(Object::null()));
__ BranchLink(&StubCode::Subtype1TestCacheLabel());
- __ delay_slot()->mov(A1, NULLREG);
} else if (test_kind == kTestTypeTwoArgs) {
ASSERT(type_arguments_reg == kNoRegister);
+ __ LoadImmediate(A1, reinterpret_cast<int32_t>(Object::null()));
__ BranchLink(&StubCode::Subtype2TestCacheLabel());
- __ delay_slot()->mov(A1, NULLREG);
} else if (test_kind == kTestTypeThreeArgs) {
ASSERT(type_arguments_reg == A1);
__ BranchLink(&StubCode::Subtype3TestCacheLabel());
@@ -329,7 +330,8 @@
// Check if instance is a closure.
__ LoadClassById(T1, kClassIdReg);
__ lw(T1, FieldAddress(T1, Class::signature_function_offset()));
- __ bne(T1, NULLREG, is_instance_lbl);
+ __ BranchNotEqual(T1, reinterpret_cast<int32_t>(Object::null()),
+ is_instance_lbl);
}
// Custom checking for numbers (Smi, Mint, Bigint and Double).
// Note that instance is not Smi (checked above).
@@ -397,7 +399,8 @@
__ lw(A1, Address(SP, 0)); // Get instantiator type arguments.
// A1: instantiator type arguments.
// Check if type argument is dynamic.
- __ beq(A1, NULLREG, is_instance_lbl);
+ __ LoadImmediate(T7, reinterpret_cast<int32_t>(Object::null()));
+ __ beq(A1, T7, is_instance_lbl);
// Can handle only type arguments that are instances of TypeArguments.
// (runtime checks canonicalize type arguments).
Label fall_through;
@@ -408,7 +411,7 @@
// R2: concrete type of type.
// Check if type argument is dynamic.
__ BranchEqual(T2, Type::ZoneHandle(Type::DynamicType()), is_instance_lbl);
- __ beq(T2, NULLREG, is_instance_lbl);
+ __ beq(T2, T7, is_instance_lbl);
const Type& object_type = Type::ZoneHandle(Type::ObjectType());
__ BranchEqual(T2, object_type, is_instance_lbl);
@@ -483,11 +486,11 @@
if (TypeCheckAsClassEquality(type)) {
const intptr_t type_cid = Class::Handle(type.type_class()).id();
const Register kInstanceReg = A0;
- __ andi(T0, kInstanceReg, Immediate(kSmiTagMask));
+ __ andi(CMPRES, kInstanceReg, Immediate(kSmiTagMask));
if (type_cid == kSmiCid) {
- __ beq(T0, ZR, is_instance_lbl);
+ __ beq(CMPRES, ZR, is_instance_lbl);
} else {
- __ beq(T0, ZR, is_not_instance_lbl);
+ __ beq(CMPRES, ZR, is_not_instance_lbl);
__ LoadClassId(T0, kInstanceReg);
__ BranchEqual(T0, type_cid, is_instance_lbl);
}
@@ -527,12 +530,90 @@
}
+// If instanceof type test cannot be performed successfully at compile time and
+// therefore eliminated, optimize it by adding inlined tests for:
+// - NULL -> return false.
+// - Smi -> compile time subtype check (only if dst class is not parameterized).
+// - Class equality (only if class is not parameterized).
+// Inputs:
+// - A0: object.
+// - A1: instantiator type arguments or raw_null.
+// - A2: instantiator or raw_null.
+// Returns:
+// - true or false in V0.
void FlowGraphCompiler::GenerateInstanceOf(intptr_t token_pos,
intptr_t deopt_id,
const AbstractType& type,
bool negate_result,
LocationSummary* locs) {
- UNIMPLEMENTED();
+ ASSERT(type.IsFinalized() && !type.IsMalformed());
+
+ // Preserve instantiator (A2) and its type arguments (A1).
+ __ addiu(SP, SP, Immediate(-2 * kWordSize));
+ __ sw(A2, Address(SP, 1 * kWordSize));
+ __ sw(A1, Address(SP, 0 * kWordSize));
+
+ Label is_instance, is_not_instance;
+ // If type is instantiated and non-parameterized, we can inline code
+ // checking whether the tested instance is a Smi.
+ if (type.IsInstantiated()) {
+ // A null object is only an instance of Object and dynamic, which has
+ // already been checked above (if the type is instantiated). So we can
+ // return false here if the instance is null (and if the type is
+ // instantiated).
+ // We can only inline this null check if the type is instantiated at compile
+ // time, since an uninstantiated type at compile time could be Object or
+ // dynamic at run time.
+ __ BranchEqual(A0, reinterpret_cast<int32_t>(Object::null()),
+ &is_not_instance);
+ }
+
+ // Generate inline instanceof test.
+ SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle();
+ test_cache = GenerateInlineInstanceof(token_pos, type,
+ &is_instance, &is_not_instance);
+
+ // test_cache is null if there is no fall-through.
+ Label done;
+ if (!test_cache.IsNull()) {
+ // Generate runtime call.
+ // Load instantiator (A2) and its type arguments (A1).
+ __ lw(A1, Address(SP, 0 * kWordSize));
+ __ lw(A2, Address(SP, 1 * kWordSize));
+
+ __ addiu(SP, SP, Immediate(-6 * kWordSize));
+ __ LoadObject(TMP, Object::ZoneHandle());
+ __ sw(TMP, Address(SP, 5 * kWordSize)); // Make room for the result.
+ __ sw(A0, Address(SP, 4 * kWordSize)); // Push the instance.
+ __ LoadObject(TMP, type);
+ __ sw(TMP, Address(SP, 3 * kWordSize)); // Push the type.
+ __ sw(A2, Address(SP, 2 * kWordSize)); // Push instantiator.
+ __ sw(A1, Address(SP, 1 * kWordSize)); // Push type arguments.
+ __ LoadObject(A0, test_cache);
+ __ sw(A0, Address(SP, 0 * kWordSize));
+ GenerateCallRuntime(token_pos, deopt_id, kInstanceofRuntimeEntry, locs);
+ // Pop the parameters supplied to the runtime entry. The result of the
+ // instanceof runtime call will be left as the result of the operation.
+ __ lw(T0, Address(SP, 5 * kWordSize));
+ __ addiu(SP, SP, Immediate(6 * kWordSize));
+ if (negate_result) {
+ __ LoadObject(V0, Bool::True());
+ __ bne(T0, V0, &done);
+ __ LoadObject(V0, Bool::False());
+ } else {
+ __ mov(V0, T0);
+ }
+ __ b(&done);
+ }
+ __ Bind(&is_not_instance);
+ __ LoadObject(V0, negate_result ? Bool::True() : Bool::False());
+ __ b(&done);
+
+ __ Bind(&is_instance);
+ __ LoadObject(V0, negate_result ? Bool::False() : Bool::True());
+ __ Bind(&done);
+ // Remove instantiator (A2) and its type arguments (A1).
+ __ Drop(2);
}
@@ -567,7 +648,8 @@
// A null object is always assignable and is returned as result.
Label is_assignable, runtime_call;
- __ beq(A0, NULLREG, &is_assignable);
+
+ __ BranchEqual(A0, reinterpret_cast<int32_t>(Object::null()), &is_assignable);
__ delay_slot()->sw(A1, Address(SP, 0 * kWordSize));
if (!FLAG_eliminate_type_checks) {
@@ -804,7 +886,8 @@
delete[] opt_param_position;
// Check that T0 now points to the null terminator in the array descriptor.
__ lw(T3, Address(T0));
- __ beq(T3, NULLREG, &all_arguments_processed);
+ __ BranchEqual(T3, reinterpret_cast<int32_t>(Object::null()),
+ &all_arguments_processed);
} else {
ASSERT(num_opt_pos_params > 0);
__ Comment("There are optional positional parameters");
@@ -894,8 +977,9 @@
__ Bind(&null_args_loop);
__ addiu(T2, T2, Immediate(-kWordSize));
__ addu(T3, T1, T2);
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
__ bgtz(T2, &null_args_loop);
- __ delay_slot()->sw(NULLREG, Address(T3));
+ __ delay_slot()->sw(TMP, Address(T3));
__ Bind(&null_args_loop_exit);
}
@@ -918,8 +1002,9 @@
__ lw(T0, Address(SP, 1 * kWordSize)); // Receiver.
__ lw(T1, Address(SP, 0 * kWordSize)); // Value.
__ StoreIntoObject(T0, FieldAddress(T0, offset), T1);
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
__ Ret();
- __ delay_slot()->mov(V0, NULLREG);
+ __ delay_slot()->mov(V0, TMP);
}
@@ -1113,7 +1198,8 @@
const intptr_t slot_base = parsed_function().first_stack_local_index();
for (intptr_t i = 0; i < num_locals; ++i) {
// Subtract index i (locals lie at lower addresses than FP).
- __ sw(NULLREG, Address(FP, (slot_base - i) * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
+ __ sw(TMP, Address(FP, (slot_base - i) * kWordSize));
}
}
@@ -1348,11 +1434,11 @@
__ addiu(SP, SP, Immediate(-2 * kWordSize));
__ sw(reg, Address(SP, 1 * kWordSize));
__ LoadObject(TMP1, obj);
+ __ sw(TMP1, Address(SP, 0 * kWordSize));
__ BranchLink(&StubCode::IdenticalWithNumberCheckLabel());
AddCurrentDescriptor(PcDescriptors::kRuntimeCall,
Isolate::kNoDeoptId,
token_pos);
- __ delay_slot()->sw(TMP1, Address(SP, 0 * kWordSize));
__ TraceSimMsg("EqualityRegConstCompare return");
__ lw(reg, Address(SP, 1 * kWordSize)); // Restore 'reg'.
__ addiu(SP, SP, Immediate(2 * kWordSize)); // Discard constant.
@@ -1367,14 +1453,15 @@
bool needs_number_check,
intptr_t token_pos) {
__ TraceSimMsg("EqualityRegRegCompare");
+ __ Comment("EqualityRegRegCompare");
if (needs_number_check) {
__ addiu(SP, SP, Immediate(-2 * kWordSize));
__ sw(left, Address(SP, 1 * kWordSize));
+ __ sw(right, Address(SP, 0 * kWordSize));
__ BranchLink(&StubCode::IdenticalWithNumberCheckLabel());
AddCurrentDescriptor(PcDescriptors::kRuntimeCall,
Isolate::kNoDeoptId,
token_pos);
- __ delay_slot()->sw(right, Address(SP, 0 * kWordSize));
__ TraceSimMsg("EqualityRegRegCompare return");
// Stub returns result in CMPRES. If it is 0, then left and right are equal.
__ lw(right, Address(SP, 0 * kWordSize));
@@ -1395,8 +1482,9 @@
__ TraceSimMsg("SuperEqualityCallPrologue");
__ lw(result, Address(SP, 0 * kWordSize)); // Load right operand.
__ lw(TMP1, Address(SP, 1 * kWordSize)); // Load left operand.
- __ beq(result, NULLREG, &check_identity); // Is right null?
- __ bne(TMP1, NULLREG, &fall_through); // If right is non-null, check left.
+ __ LoadImmediate(CMPRES, reinterpret_cast<int32_t>(Object::null()));
+ __ beq(result, CMPRES, &check_identity); // Is right null?
+ __ bne(TMP1, CMPRES, &fall_through); // If right is non-null, check left.
__ Bind(&check_identity);
__ bne(result, TMP1, &is_false);
@@ -1414,9 +1502,20 @@
void FlowGraphCompiler::SaveLiveRegisters(LocationSummary* locs) {
__ TraceSimMsg("SaveLiveRegisters");
// TODO(vegorov): consider saving only caller save (volatile) registers.
- const intptr_t fpu_registers = locs->live_registers()->fpu_registers();
- if (fpu_registers > 0) {
- UNIMPLEMENTED();
+ const intptr_t fpu_regs_count= locs->live_registers()->fpu_regs_count();
+ if (fpu_regs_count > 0) {
+ __ AddImmediate(SP, -(fpu_regs_count * kFpuRegisterSize));
+ // Store fpu registers with the lowest register number at the lowest
+ // address.
+ intptr_t offset = 0;
+ for (intptr_t reg_idx = 0; reg_idx < kNumberOfFpuRegisters; ++reg_idx) {
+ DRegister fpu_reg = static_cast<DRegister>(reg_idx);
+ if (locs->live_registers()->ContainsFpuRegister(fpu_reg)) {
+ __ StoreDToOffset(fpu_reg, SP, offset);
+ offset += kFpuRegisterSize;
+ }
+ }
+ ASSERT(offset == (fpu_regs_count * kFpuRegisterSize));
}
// Store general purpose registers with the lowest register number at the
@@ -1455,9 +1554,19 @@
}
__ addiu(SP, SP, Immediate(register_count * kWordSize));
- const intptr_t fpu_registers = locs->live_registers()->fpu_registers();
- if (fpu_registers > 0) {
- UNIMPLEMENTED();
+ const intptr_t fpu_regs_count = locs->live_registers()->fpu_regs_count();
+ if (fpu_regs_count > 0) {
+ // Fpu registers have the lowest register number at the lowest address.
+ intptr_t offset = 0;
+ for (intptr_t reg_idx = 0; reg_idx < kNumberOfFpuRegisters; ++reg_idx) {
+ DRegister fpu_reg = static_cast<DRegister>(reg_idx);
+ if (locs->live_registers()->ContainsFpuRegister(fpu_reg)) {
+ __ LoadDFromOffset(fpu_reg, SP, offset);
+ offset += kFpuRegisterSize;
+ }
+ }
+ ASSERT(offset == (fpu_regs_count * kFpuRegisterSize));
+ __ AddImmediate(SP, offset);
}
}
@@ -1481,6 +1590,7 @@
Array::ZoneHandle(ArgumentsDescriptor::New(argument_count,
argument_names));
__ TraceSimMsg("EmitTestAndCall");
+ __ Comment("EmitTestAndCall");
__ LoadObject(S4, arguments_descriptor);
for (intptr_t i = 0; i < len; i++) {
const bool is_last_check = (i == (len - 1));
@@ -1513,7 +1623,33 @@
FpuRegister left,
FpuRegister right,
BranchInstr* branch) {
- UNIMPLEMENTED();
+ ASSERT(branch != NULL);
+ __ Comment("DoubleCompareBranch");
+ assembler()->cund(left, right);
+ BlockEntryInstr* nan_result = (true_condition == NE) ?
+ branch->true_successor() : branch->false_successor();
+ assembler()->bc1t(GetJumpLabel(nan_result));
+
+ switch (true_condition) {
+ case EQ: assembler()->ceqd(left, right); break;
+ case LT: assembler()->coltd(left, right); break;
+ case LE: assembler()->coled(left, right); break;
+ case GT: assembler()->coltd(right, left); break;
+ case GE: assembler()->coled(right, left); break;
+ default: {
+ // Should only passing the above conditions to this function.
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ assembler()->LoadImmediate(TMP, 1);
+ assembler()->movf(CMPRES, TMP);
+ assembler()->movt(CMPRES, ZR);
+ assembler()->mov(TMP, ZR);
+
+ // EmitBranchOnCondition expects ordering to be described by CMPRES, TMP1.
+ branch->EmitBranchOnCondition(this, EQ);
}
@@ -1521,7 +1657,28 @@
FpuRegister left,
FpuRegister right,
Register result) {
- UNIMPLEMENTED();
+ Label done;
+ __ Comment("DoubleCompareBool");
+ assembler()->LoadObject(result, Bool::False());
+ assembler()->cund(left, right);
+ assembler()->bc1t(&done);
+
+ switch (true_condition) {
+ case EQ: assembler()->ceqd(left, right); break;
+ case LT: assembler()->coltd(left, right); break;
+ case LE: assembler()->coled(left, right); break;
+ case GT: assembler()->coltd(right, left); break;
+ case GE: assembler()->coled(right, left); break;
+ default: {
+ // Should only passing the above conditions to this function.
+ UNREACHABLE();
+ break;
+ }
+ }
+
+ assembler()->bc1f(&done); // False is already in result.
+ assembler()->LoadObject(result, Bool::True());
+ assembler()->Bind(&done);
}
@@ -1591,7 +1748,9 @@
__ movd(destination.fpu_reg(), source.fpu_reg());
} else {
if (destination.IsDoubleStackSlot()) {
- __ sdc1(source.fpu_reg(), destination.ToStackSlotAddress());
+ const Address& addr = destination.ToStackSlotAddress();
+ int32_t offset = addr.offset();
+ __ StoreDToOffset(source.fpu_reg(), FP, offset);
} else {
ASSERT(destination.IsQuadStackSlot());
UNIMPLEMENTED();
@@ -1599,11 +1758,18 @@
}
} else if (source.IsDoubleStackSlot()) {
if (destination.IsFpuRegister()) {
- __ ldc1(destination.fpu_reg(), source.ToStackSlotAddress());
+ const Address &addr = source.ToStackSlotAddress();
+ const Register base = addr.base();
+ const int32_t offset = addr.offset();
+ __ LoadDFromOffset(destination.fpu_reg(), base, offset);
} else {
ASSERT(destination.IsDoubleStackSlot());
- __ ldc1(FpuTMP, source.ToStackSlotAddress());
- __ sdc1(FpuTMP, destination.ToStackSlotAddress());
+ const Address& saddr = source.ToStackSlotAddress();
+ const Address& daddr = destination.ToStackSlotAddress();
+ int32_t soffset = saddr.offset();
+ int32_t doffset = daddr.offset();
+ __ LoadDFromOffset(FpuTMP, FP, soffset);
+ __ StoreDToOffset(FpuTMP, FP, doffset);
}
} else if (source.IsQuadStackSlot()) {
UNIMPLEMENTED();
@@ -1658,8 +1824,10 @@
: source.ToStackSlotAddress();
if (double_width) {
- __ ldc1(FpuTMP, slot_address);
- __ sdc1(reg, slot_address);
+ const Register base = slot_address.base();
+ const int32_t offset = slot_address.offset();
+ __ LoadDFromOffset(FpuTMP, base, offset);
+ __ StoreDToOffset(reg, base, offset);
__ movd(reg, FpuTMP);
} else {
UNIMPLEMENTED();
@@ -1667,12 +1835,16 @@
} else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
const Address& source_slot_address = source.ToStackSlotAddress();
const Address& destination_slot_address = destination.ToStackSlotAddress();
+ const Register sbase = source_slot_address.base();
+ const int32_t soffset = source_slot_address.offset();
+ const Register dbase = destination_slot_address.base();
+ const int32_t doffset = destination_slot_address.offset();
ScratchFpuRegisterScope ensure_scratch(this, FpuTMP);
- __ ldc1(FpuTMP, source_slot_address);
- __ ldc1(ensure_scratch.reg(), destination_slot_address);
- __ sdc1(FpuTMP, destination_slot_address);
- __ sdc1(ensure_scratch.reg(), source_slot_address);
+ __ LoadDFromOffset(FpuTMP, sbase, soffset);
+ __ LoadDFromOffset(ensure_scratch.reg(), dbase, doffset);
+ __ StoreDToOffset(FpuTMP, dbase, doffset);
+ __ StoreDToOffset(ensure_scratch.reg(), sbase, soffset);
} else if (source.IsQuadStackSlot() && destination.IsQuadStackSlot()) {
UNIMPLEMENTED();
} else {
@@ -1746,13 +1918,13 @@
void ParallelMoveResolver::SpillFpuScratch(FpuRegister reg) {
__ TraceSimMsg("ParallelMoveResolver::SpillFpuScratch");
__ AddImmediate(SP, -kDoubleSize);
- __ sdc1(reg, Address(SP));
+ __ StoreDToOffset(reg, SP, 0);
}
void ParallelMoveResolver::RestoreFpuScratch(FpuRegister reg) {
__ TraceSimMsg("ParallelMoveResolver::RestoreFpuScratch");
- __ ldc1(reg, Address(SP));
+ __ LoadDFromOffset(reg, SP, 0);
__ AddImmediate(SP, kDoubleSize);
}
diff --git a/runtime/vm/instructions_mips.cc b/runtime/vm/instructions_mips.cc
index 41648b1..197efd1 100644
--- a/runtime/vm/instructions_mips.cc
+++ b/runtime/vm/instructions_mips.cc
@@ -6,6 +6,7 @@
#if defined(TARGET_ARCH_MIPS)
#include "vm/constants_mips.h"
+#include "vm/cpu.h"
#include "vm/instructions.h"
#include "vm/object.h"
@@ -158,6 +159,24 @@
}
+void CallPattern::InsertAt(uword pc, uword target_address) {
+ Instr* lui = Instr::At(pc + (0 * Instr::kInstrSize));
+ Instr* ori = Instr::At(pc + (1 * Instr::kInstrSize));
+ Instr* jr = Instr::At(pc + (2 * Instr::kInstrSize));
+ Instr* nop = Instr::At(pc + (3 * Instr::kInstrSize));
+ uint16_t target_lo = target_address & 0xffff;
+ uint16_t target_hi = target_address >> 16;
+
+ lui->SetImmInstrBits(LUI, ZR, TMP1, target_hi);
+ ori->SetImmInstrBits(ORI, TMP1, TMP1, target_lo);
+ jr->SetSpecialInstrBits(JALR, TMP1, ZR, RA);
+ nop->SetInstructionBits(Instr::kNopInstruction);
+
+ ASSERT(kFixedLengthInBytes == 4 * Instr::kInstrSize);
+ CPU::FlushICache(pc, kFixedLengthInBytes);
+}
+
+
JumpPattern::JumpPattern(uword pc) : pc_(pc) { }
diff --git a/runtime/vm/instructions_mips.h b/runtime/vm/instructions_mips.h
index 5567ae6..f9d7908 100644
--- a/runtime/vm/instructions_mips.h
+++ b/runtime/vm/instructions_mips.h
@@ -27,7 +27,9 @@
// This constant length is only valid for inserted call patterns used for
// lazy deoptimization. Regular call pattern may vary in length.
- static const int kFixedLengthInBytes = 3 * Instr::kInstrSize;
+ static const int kFixedLengthInBytes = 4 * Instr::kInstrSize;
+
+ static void InsertAt(uword pc, uword target_address);
private:
uword Back(int n) const;
diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc
index 8bf386e..813378d 100644
--- a/runtime/vm/intermediate_language_mips.cc
+++ b/runtime/vm/intermediate_language_mips.cc
@@ -387,11 +387,13 @@
const int kNumArgumentsChecked = 2;
__ TraceSimMsg("EmitEqualityAsInstanceCall");
+ __ Comment("EmitEqualityAsInstanceCall");
Label check_identity;
__ lw(A1, Address(SP, 1 * kWordSize));
__ lw(A0, Address(SP, 0 * kWordSize));
- __ beq(A1, NULLREG, &check_identity);
- __ beq(A0, NULLREG, &check_identity);
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
+ __ beq(A1, TMP, &check_identity);
+ __ beq(A0, TMP, &check_identity);
ICData& equality_ic_data = ICData::ZoneHandle();
if (compiler->is_optimizing() && FLAG_propagate_ic_data) {
@@ -536,6 +538,7 @@
Register temp = locs->temp(0).reg();
__ TraceSimMsg("EmitEqualityAsPolymorphicCall");
+ __ Comment("EmitEqualityAsPolymorphicCall");
LoadValueCid(compiler, temp, left,
(ic_data.GetReceiverClassIdAt(0) == kSmiCid) ? NULL : deopt);
@@ -608,12 +611,57 @@
// Emit code when ICData's targets are all Object == (which is ===).
static void EmitCheckedStrictEqual(FlowGraphCompiler* compiler,
- const ICData& ic_data,
+ const ICData& orig_ic_data,
const LocationSummary& locs,
Token::Kind kind,
BranchInstr* branch,
intptr_t deopt_id) {
- UNIMPLEMENTED();
+ ASSERT((kind == Token::kEQ) || (kind == Token::kNE));
+ Register left = locs.in(0).reg();
+ Register right = locs.in(1).reg();
+ Register temp = locs.temp(0).reg();
+ Label* deopt = compiler->AddDeoptStub(deopt_id, kDeoptEquality);
+
+ __ Comment("CheckedStrictEqual");
+
+ __ andi(CMPRES, left, Immediate(kSmiTagMask));
+ __ beq(CMPRES, ZR, deopt);
+ // 'left' is not Smi.
+ Label identity_compare;
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
+ __ beq(right, TMP, &identity_compare);
+ __ beq(left, TMP, &identity_compare);
+
+ __ LoadClassId(temp, left);
+ const ICData& ic_data = ICData::Handle(orig_ic_data.AsUnaryClassChecks());
+ const intptr_t len = ic_data.NumberOfChecks();
+ for (intptr_t i = 0; i < len; i++) {
+ if (i == (len - 1)) {
+ __ BranchNotEqual(temp, ic_data.GetReceiverClassIdAt(i), deopt);
+ } else {
+ __ BranchEqual(temp, ic_data.GetReceiverClassIdAt(i), &identity_compare);
+ }
+ }
+ __ Bind(&identity_compare);
+ __ subu(CMPRES, left, right);
+ if (branch == NULL) {
+ Label done, is_equal;
+ Register result = locs.out().reg();
+ __ beq(CMPRES, ZR, &is_equal);
+ // Not equal.
+ __ LoadObject(result,
+ (kind == Token::kEQ) ? Bool::False() : Bool::True());
+ __ b(&done);
+ __ Bind(&is_equal);
+ __ LoadObject(result,
+ (kind == Token::kEQ) ? Bool::True() : Bool::False());
+ __ Bind(&done);
+
+ } else {
+ Condition cond = TokenKindToSmiCondition(kind);
+ __ mov(TMP, ZR);
+ branch->EmitBranchOnCondition(compiler, cond);
+ }
}
@@ -633,8 +681,10 @@
Register right = locs->in(1).reg();
Label done, identity_compare, non_null_compare;
__ TraceSimMsg("EmitGenericEqualityCompare");
- __ beq(right, NULLREG, &identity_compare);
- __ bne(left, NULLREG, &non_null_compare);
+ __ Comment("EmitGenericEqualityCompare");
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
+ __ beq(right, TMP, &identity_compare);
+ __ bne(left, TMP, &non_null_compare);
// Comparison with NULL is "===".
__ Bind(&identity_compare);
@@ -685,6 +735,7 @@
Token::Kind kind,
BranchInstr* branch) {
__ TraceSimMsg("EmitSmiComparisonOp");
+ __ Comment("EmitSmiComparisonOp");
Location left = locs.in(0);
Location right = locs.in(1);
ASSERT(!left.IsConstant() || !right.IsConstant());
@@ -732,17 +783,45 @@
}
+static Condition TokenKindToDoubleCondition(Token::Kind kind) {
+ switch (kind) {
+ case Token::kEQ: return EQ;
+ case Token::kNE: return NE;
+ case Token::kLT: return LT;
+ case Token::kGT: return GT;
+ case Token::kLTE: return LE;
+ case Token::kGTE: return GE;
+ default:
+ UNREACHABLE();
+ return VS;
+ }
+}
+
+
static void EmitDoubleComparisonOp(FlowGraphCompiler* compiler,
const LocationSummary& locs,
Token::Kind kind,
BranchInstr* branch) {
- UNIMPLEMENTED();
+ DRegister left = locs.in(0).fpu_reg();
+ DRegister right = locs.in(1).fpu_reg();
+
+ __ Comment("DoubleComparisonOp(left=%d, right=%d)", left, right);
+
+ Condition true_condition = TokenKindToDoubleCondition(kind);
+ if (branch != NULL) {
+ compiler->EmitDoubleCompareBranch(
+ true_condition, left, right, branch);
+ } else {
+ compiler->EmitDoubleCompareBool(
+ true_condition, left, right, locs.out().reg());
+ }
}
void EqualityCompareInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
ASSERT((kind() == Token::kNE) || (kind() == Token::kEQ));
BranchInstr* kNoBranch = NULL;
+ __ Comment("EqualityCompareInstr");
if (receiver_class_id() == kSmiCid) {
EmitSmiComparisonOp(compiler, *locs(), kind(), kNoBranch);
return;
@@ -785,6 +864,7 @@
void EqualityCompareInstr::EmitBranchCode(FlowGraphCompiler* compiler,
BranchInstr* branch) {
__ TraceSimMsg("EqualityCompareInstr");
+ __ Comment("EqualityCompareInstr:BranchCode");
ASSERT((kind() == Token::kNE) || (kind() == Token::kEQ));
if (receiver_class_id() == kSmiCid) {
// Deoptimizes if both arguments not Smi.
@@ -1001,7 +1081,9 @@
// into the runtime system.
uword entry = reinterpret_cast<uword>(native_c_function());
#if defined(USING_SIMULATOR)
- entry = Simulator::RedirectExternalReference(entry, Simulator::kNativeCall);
+ entry = Simulator::RedirectExternalReference(entry,
+ Simulator::kNativeCall,
+ function().NumParameters());
#endif
__ LoadImmediate(T5, entry);
__ LoadImmediate(A1, NativeArguments::ComputeArgcTag(function()));
@@ -1205,7 +1287,28 @@
if ((representation() == kUnboxedDouble) ||
(representation() == kUnboxedMint) ||
(representation() == kUnboxedFloat32x4)) {
- UNIMPLEMENTED();
+ DRegister result = locs()->out().fpu_reg();
+ switch (class_id()) {
+ case kTypedDataInt32ArrayCid:
+ UNIMPLEMENTED();
+ break;
+ case kTypedDataUint32ArrayCid:
+ UNIMPLEMENTED();
+ break;
+ case kTypedDataFloat32ArrayCid:
+ // Load single precision float and promote to double.
+ __ lwc1(STMP1, element_address);
+ __ cvtds(result, STMP1);
+ break;
+ case kTypedDataFloat64ArrayCid:
+ __ LoadDFromOffset(result, index.reg(),
+ FlowGraphCompiler::DataOffsetFor(class_id()) - kHeapObjectTag);
+ break;
+ case kTypedDataFloat32x4ArrayCid:
+ UNIMPLEMENTED();
+ break;
+ }
+ return;
}
Register result = locs()->out().reg();
@@ -1582,7 +1685,8 @@
if (field().is_nullable() && (field_cid != kNullCid)) {
__ beq(CMPRES, ZR, &ok);
- __ subu(CMPRES, value_reg, NULLREG);
+ __ LoadImmediate(TMP, reinterpret_cast<int32_t>(Object::null()));
+ __ subu(CMPRES, value_reg, TMP);
}
if (ok_is_fall_through) {
@@ -1703,13 +1807,30 @@
LocationSummary* InstanceOfInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ const intptr_t kNumInputs = 3;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
+ summary->set_in(0, Location::RegisterLocation(A0));
+ summary->set_in(1, Location::RegisterLocation(A2));
+ summary->set_in(2, Location::RegisterLocation(A1));
+ summary->set_out(Location::RegisterLocation(V0));
+ return summary;
}
void InstanceOfInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ ASSERT(locs()->in(0).reg() == A0); // Value.
+ ASSERT(locs()->in(1).reg() == A2); // Instantiator.
+ ASSERT(locs()->in(2).reg() == A1); // Instantiator type arguments.
+
+ __ Comment("InstanceOfInstr");
+ compiler->GenerateInstanceOf(token_pos(),
+ deopt_id(),
+ type(),
+ negate_result(),
+ locs());
+ ASSERT(locs()->out().reg() == V0);
}
@@ -1739,14 +1860,19 @@
LocationSummary*
AllocateObjectWithBoundsCheckInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ return MakeCallSummary();
}
void AllocateObjectWithBoundsCheckInstr::EmitNativeCode(
FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ compiler->GenerateCallRuntime(token_pos(),
+ deopt_id(),
+ kAllocateObjectWithBoundsCheckRuntimeEntry,
+ locs());
+ __ Drop(3);
+ ASSERT(locs()->out().reg() == V0);
+ __ Pop(V0); // Pop new instance.
}
@@ -1793,7 +1919,8 @@
Label type_arguments_instantiated;
const intptr_t len = type_arguments().Length();
if (type_arguments().IsRawInstantiatedRaw(len)) {
- __ beq(instantiator_reg, NULLREG, &type_arguments_instantiated);
+ __ BranchEqual(instantiator_reg, reinterpret_cast<int32_t>(Object::null()),
+ &type_arguments_instantiated);
}
// Instantiate non-null type arguments.
// A runtime call to instantiate the type arguments is required.
@@ -1846,7 +1973,8 @@
// the type arguments.
Label type_arguments_instantiated;
ASSERT(type_arguments().IsRawInstantiatedRaw(type_arguments().Length()));
- __ beq(instantiator_reg, NULLREG, &type_arguments_instantiated);
+ __ BranchEqual(instantiator_reg, reinterpret_cast<int32_t>(Object::null()),
+ &type_arguments_instantiated);
// Instantiate non-null type arguments.
// In the non-factory case, we rely on the allocation stub to
// instantiate the type arguments.
@@ -1886,7 +2014,8 @@
// the type arguments and do not pass the instantiator.
ASSERT(type_arguments().IsRawInstantiatedRaw(type_arguments().Length()));
Label instantiator_not_null;
- __ bne(instantiator_reg, NULLREG, &instantiator_not_null);
+ __ BranchNotEqual(instantiator_reg, reinterpret_cast<int32_t>(Object::null()),
+ &instantiator_not_null);
// Null was used in VisitExtractConstructorTypeArguments as the
// instantiated type arguments, no proper instantiator needed.
__ LoadImmediate(instantiator_reg,
@@ -2067,7 +2196,9 @@
const intptr_t kCountLimit = 0x1F;
const intptr_t value = Smi::Cast(constant).Value();
if (value == 0) {
- // No code needed.
+ if (result != left) {
+ __ mov(result, left);
+ }
} else if ((value < 0) || (value >= kCountLimit)) {
// This condition may not be known earlier in some cases because
// of constant propagation, inlining, etc.
@@ -2101,6 +2232,7 @@
const intptr_t left_int = Smi::Cast(obj).Value();
if (left_int == 0) {
__ bltz(right, deopt);
+ __ mov(result, ZR);
return;
}
const intptr_t max_right = kSmiBits - Utils::HighestBit(left_int);
@@ -2163,24 +2295,33 @@
LocationSummary* BinarySmiOpInstr::MakeLocationSummary() const {
const intptr_t kNumInputs = 2;
+ const intptr_t kNumTemps = op_kind() == Token::kADD ? 1 : 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
if (op_kind() == Token::kTRUNCDIV) {
- UNIMPLEMENTED();
- return NULL;
- } else {
- const intptr_t kNumTemps = op_kind() == Token::kADD ? 1 : 0;
- LocationSummary* summary =
- new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
- summary->set_in(0, Location::RequiresRegister());
- summary->set_in(1, Location::RegisterOrSmiConstant(right()));
- if (op_kind() == Token::kADD) {
- // Need an extra temp for the overflow detection code.
- summary->set_temp(0, Location::RequiresRegister());
+ if (RightIsPowerOfTwoConstant()) {
+ summary->set_in(0, Location::RequiresRegister());
+ ConstantInstr* right_constant = right()->definition()->AsConstant();
+ summary->set_in(1, Location::Constant(right_constant->value()));
+ summary->set_out(Location::RequiresRegister());
+ } else {
+ // Both inputs must be writable because they will be untagged.
+ summary->set_in(0, Location::WritableRegister());
+ summary->set_in(1, Location::WritableRegister());
+ summary->set_out(Location::RequiresRegister());
}
- // We make use of 3-operand instructions by not requiring result register
- // to be identical to first input register as on Intel.
- summary->set_out(Location::RequiresRegister());
return summary;
}
+ summary->set_in(0, Location::RequiresRegister());
+ summary->set_in(1, Location::RegisterOrSmiConstant(right()));
+ if (op_kind() == Token::kADD) {
+ // Need an extra temp for the overflow detection code.
+ summary->set_temp(0, Location::RequiresRegister());
+ }
+ // We make use of 3-operand instructions by not requiring result register
+ // to be identical to first input register as on Intel.
+ summary->set_out(Location::RequiresRegister());
+ return summary;
}
@@ -2251,7 +2392,33 @@
break;
}
case Token::kTRUNCDIV: {
- UNIMPLEMENTED();
+ const intptr_t value = Smi::Cast(constant).Value();
+ if (value == 1) {
+ if (result != left) {
+ __ mov(result, left);
+ }
+ break;
+ } else if (value == -1) {
+ // Check the corner case of dividing the 'MIN_SMI' with -1, in which
+ // case we cannot negate the result.
+ __ BranchEqual(left, 0x80000000, deopt);
+ __ subu(result, ZR, left);
+ break;
+ }
+ ASSERT((value != 0) && Utils::IsPowerOfTwo(Utils::Abs(value)));
+ const intptr_t shift_count =
+ Utils::ShiftForPowerOfTwo(Utils::Abs(value)) + kSmiTagSize;
+ ASSERT(kSmiTagSize == 1);
+ __ sra(TMP, left, 31);
+ ASSERT(shift_count > 1); // 1, -1 case handled above.
+ __ sll(TMP, TMP, 32 - shift_count);
+ __ addu(left, left, TMP);
+ ASSERT(shift_count > 0);
+ __ sra(result, left, shift_count);
+ if (value < 0) {
+ __ subu(result, ZR, result);
+ }
+ __ SmiTag(result);
break;
}
case Token::kBIT_AND: {
@@ -2293,7 +2460,9 @@
if (value == 0) {
// TODO(vegorov): should be handled outside.
- __ break_(0);
+ if (result != left) {
+ __ mov(result, left);
+ }
break;
} else if (value < 0) {
// TODO(vegorov): should be handled outside.
@@ -2366,7 +2535,16 @@
break;
}
case Token::kTRUNCDIV: {
- UNIMPLEMENTED();
+ // Handle divide by zero in runtime.
+ __ beq(right, ZR, deopt);
+ __ SmiUntag(left);
+ __ SmiUntag(right);
+ __ div(left, right);
+ __ mflo(result);
+ // Check the corner case of dividing the 'MIN_SMI' with -1, in which
+ // case we cannot tag the result.
+ __ BranchEqual(V0, 0x40000000, deopt);
+ __ SmiTag(result);
break;
}
case Token::kSHR: {
@@ -2399,35 +2577,142 @@
LocationSummary* CheckEitherNonSmiInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ intptr_t left_cid = left()->Type()->ToCid();
+ intptr_t right_cid = right()->Type()->ToCid();
+ ASSERT((left_cid != kDoubleCid) && (right_cid != kDoubleCid));
+ const intptr_t kNumInputs = 2;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, Location::RequiresRegister());
+ summary->set_in(1, Location::RequiresRegister());
+ return summary;
}
void CheckEitherNonSmiInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ Label* deopt = compiler->AddDeoptStub(deopt_id(), kDeoptBinaryDoubleOp);
+ intptr_t left_cid = left()->Type()->ToCid();
+ intptr_t right_cid = right()->Type()->ToCid();
+ Register left = locs()->in(0).reg();
+ Register right = locs()->in(1).reg();
+ if (left_cid == kSmiCid) {
+ __ andi(CMPRES, right, Immediate(kSmiTagMask));
+ } else if (right_cid == kSmiCid) {
+ __ andi(CMPRES, left, Immediate(kSmiTagMask));
+ } else {
+ __ or_(TMP, left, right);
+ __ andi(CMPRES, TMP, Immediate(kSmiTagMask));
+ }
+ __ beq(CMPRES, ZR, deopt);
}
LocationSummary* BoxDoubleInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ const intptr_t kNumInputs = 1;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs,
+ kNumTemps,
+ LocationSummary::kCallOnSlowPath);
+ summary->set_in(0, Location::RequiresFpuRegister());
+ summary->set_out(Location::RequiresRegister());
+ return summary;
}
+class BoxDoubleSlowPath : public SlowPathCode {
+ public:
+ explicit BoxDoubleSlowPath(BoxDoubleInstr* instruction)
+ : instruction_(instruction) { }
+
+ virtual void EmitNativeCode(FlowGraphCompiler* compiler) {
+ __ Comment("BoxDoubleSlowPath");
+ __ Bind(entry_label());
+ const Class& double_class = compiler->double_class();
+ const Code& stub =
+ Code::Handle(StubCode::GetAllocationStubForClass(double_class));
+ const ExternalLabel label(double_class.ToCString(), stub.EntryPoint());
+
+ LocationSummary* locs = instruction_->locs();
+ locs->live_registers()->Remove(locs->out());
+
+ compiler->SaveLiveRegisters(locs);
+ compiler->GenerateCall(Scanner::kDummyTokenIndex, // No token position.
+ &label,
+ PcDescriptors::kOther,
+ locs);
+ if (locs->out().reg() != V0) {
+ __ mov(locs->out().reg(), V0);
+ }
+ compiler->RestoreLiveRegisters(locs);
+
+ __ b(exit_label());
+ }
+
+ private:
+ BoxDoubleInstr* instruction_;
+};
+
+
void BoxDoubleInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ BoxDoubleSlowPath* slow_path = new BoxDoubleSlowPath(this);
+ compiler->AddSlowPathCode(slow_path);
+
+ Register out_reg = locs()->out().reg();
+ DRegister value = locs()->in(0).fpu_reg();
+
+ __ TryAllocate(compiler->double_class(),
+ slow_path->entry_label(),
+ out_reg);
+ __ Bind(slow_path->exit_label());
+ __ StoreDToOffset(value, out_reg, Double::value_offset() - kHeapObjectTag);
}
LocationSummary* UnboxDoubleInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ const intptr_t kNumInputs = 1;
+ const intptr_t value_cid = value()->Type()->ToCid();
+ const bool needs_writable_input = (value_cid == kSmiCid);
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, needs_writable_input
+ ? Location::WritableRegister()
+ : Location::RequiresRegister());
+ summary->set_out(Location::RequiresFpuRegister());
+ return summary;
}
void UnboxDoubleInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ const intptr_t value_cid = value()->Type()->ToCid();
+ const Register value = locs()->in(0).reg();
+ const DRegister result = locs()->out().fpu_reg();
+
+ if (value_cid == kDoubleCid) {
+ __ LoadDFromOffset(result, value, Double::value_offset() - kHeapObjectTag);
+ } else if (value_cid == kSmiCid) {
+ __ SmiUntag(value); // Untag input before conversion.
+ __ mtc1(value, STMP1);
+ __ cvtdw(result, STMP1);
+ } else {
+ Label* deopt = compiler->AddDeoptStub(deopt_id_, kDeoptBinaryDoubleOp);
+ Label is_smi, done;
+
+ __ andi(CMPRES, value, Immediate(kSmiTagMask));
+ __ beq(CMPRES, ZR, &is_smi);
+ __ LoadClassId(TMP, value);
+ __ BranchNotEqual(TMP, kDoubleCid, deopt);
+ __ LoadDFromOffset(result, value, Double::value_offset() - kHeapObjectTag);
+ __ b(&done);
+ __ Bind(&is_smi);
+ // TODO(regis): Why do we preserve value here but not above?
+ __ sra(TMP, value, 1);
+ __ mtc1(TMP, STMP1);
+ __ cvtdw(result, STMP1);
+ __ Bind(&done);
+ }
}
@@ -2476,13 +2761,28 @@
LocationSummary* BinaryDoubleOpInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ const intptr_t kNumInputs = 2;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, Location::RequiresFpuRegister());
+ summary->set_in(1, Location::RequiresFpuRegister());
+ summary->set_out(Location::RequiresFpuRegister());
+ return summary;
}
void BinaryDoubleOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ DRegister left = locs()->in(0).fpu_reg();
+ DRegister right = locs()->in(1).fpu_reg();
+ DRegister result = locs()->out().fpu_reg();
+ switch (op_kind()) {
+ case Token::kADD: __ addd(result, left, right); break;
+ case Token::kSUB: __ subd(result, left, right); break;
+ case Token::kMUL: __ muld(result, left, right); break;
+ case Token::kDIV: __ divd(result, left, right); break;
+ default: UNREACHABLE();
+ }
}
@@ -2707,24 +3007,56 @@
LocationSummary* UnarySmiOpInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ const intptr_t kNumInputs = 1;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* summary =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
+ summary->set_in(0, Location::RequiresRegister());
+ // We make use of 3-operand instructions by not requiring result register
+ // to be identical to first input register as on Intel.
+ summary->set_out(Location::RequiresRegister());
+ return summary;
}
void UnarySmiOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ Register value = locs()->in(0).reg();
+ Register result = locs()->out().reg();
+ switch (op_kind()) {
+ case Token::kNEGATE: {
+ Label* deopt = compiler->AddDeoptStub(deopt_id(),
+ kDeoptUnaryOp);
+ __ SubuDetectOverflow(result, ZR, value, CMPRES);
+ __ bltz(CMPRES, deopt);
+ break;
+ }
+ case Token::kBIT_NOT:
+ __ nor(result, value, ZR);
+ __ addiu(result, result, Immediate(-1)); // Remove inverted smi-tag.
+ break;
+ default:
+ UNREACHABLE();
+ }
}
LocationSummary* SmiToDoubleInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ const intptr_t kNumInputs = 1;
+ const intptr_t kNumTemps = 0;
+ LocationSummary* result =
+ new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
+ result->set_in(0, Location::WritableRegister());
+ result->set_out(Location::RequiresFpuRegister());
+ return result;
}
void SmiToDoubleInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ Register value = locs()->in(0).reg();
+ FpuRegister result = locs()->out().fpu_reg();
+ __ SmiUntag(value);
+ __ mtc1(value, STMP1);
+ __ cvtdw(result, STMP1);
}
@@ -2762,13 +3094,34 @@
LocationSummary* InvokeMathCFunctionInstr::MakeLocationSummary() const {
- UNIMPLEMENTED();
- return NULL;
+ ASSERT((InputCount() == 1) || (InputCount() == 2));
+ const intptr_t kNumTemps = 0;
+ LocationSummary* result =
+ new LocationSummary(InputCount(), kNumTemps, LocationSummary::kCall);
+ result->set_in(0, Location::FpuRegisterLocation(D6));
+ if (InputCount() == 2) {
+ result->set_in(1, Location::FpuRegisterLocation(D7));
+ }
+ result->set_out(Location::FpuRegisterLocation(D0));
+ return result;
}
void InvokeMathCFunctionInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
- UNIMPLEMENTED();
+ // For pow-function return NAN if exponent is NAN.
+ Label do_call, skip_call;
+ if (recognized_kind() == MethodRecognizer::kDoublePow) {
+ DRegister exp = locs()->in(1).fpu_reg();
+ __ cund(exp, exp);
+ __ bc1f(&do_call);
+ // Exponent is NaN, return NaN.
+ __ movd(locs()->out().fpu_reg(), exp);
+ __ b(&skip_call);
+ }
+ __ Bind(&do_call);
+ // double values are passed and returned in vfp registers.
+ __ CallRuntime(TargetFunction());
+ __ Bind(&skip_call);
}
@@ -2844,7 +3197,8 @@
if (null_check()) {
Label* deopt = compiler->AddDeoptStub(deopt_id(),
kDeoptCheckClass);
- __ beq(locs()->in(0).reg(), NULLREG, deopt);
+ __ BranchEqual(locs()->in(0).reg(),
+ reinterpret_cast<int32_t>(Object::null()), deopt);
return;
}
@@ -3131,6 +3485,7 @@
// Special code for numbers (compare values instead of references.)
void StrictCompareInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ TraceSimMsg("StrictCompareInstr");
+ __ Comment("StrictCompareInstr");
ASSERT(kind() == Token::kEQ_STRICT || kind() == Token::kNE_STRICT);
Location left = locs()->in(0);
Location right = locs()->in(1);
@@ -3281,6 +3636,7 @@
void AllocateObjectInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ TraceSimMsg("AllocateObjectInstr");
+ __ Comment("AllocateObjectInstr");
const Class& cls = Class::ZoneHandle(constructor().Owner());
const Code& stub = Code::Handle(StubCode::GetAllocationStubForClass(cls));
const ExternalLabel label(cls.ToCString(), stub.EntryPoint());
@@ -3298,6 +3654,7 @@
void CreateClosureInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
+ __ Comment("CreateClosureInstr");
const Function& closure_function = function();
ASSERT(!closure_function.IsImplicitStaticClosureFunction());
const Code& stub = Code::Handle(
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index 9ba29ff..4f8588b 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -109,19 +109,20 @@
FieldAddress(T0, Array::length_offset()),
T2);
+ __ LoadImmediate(T7, reinterpret_cast<int32_t>(Object::null()));
// Initialize all array elements to raw_null.
// T0: new object start as a tagged pointer.
// T1: new object end address.
// T2: iterator which initially points to the start of the variable
// data area to be initialized.
- // NULLREG: null
+ // T7: null
__ AddImmediate(T2, T0, sizeof(RawArray) - kHeapObjectTag);
Label done;
Label init_loop;
__ Bind(&init_loop);
__ BranchUnsignedGreaterEqual(T2, T1, &done);
- __ sw(NULLREG, Address(T2, 0));
+ __ sw(T7, Address(T2, 0));
__ b(&init_loop);
__ delay_slot()->addiu(T2, T2, Immediate(kWordSize));
__ Bind(&done);
@@ -201,13 +202,14 @@
__ lw(T2, Address(SP, 0 * kWordSize)); // Value.
// Null value is valid for any type.
- __ beq(T2, NULLREG, &checked_ok);
+ __ LoadImmediate(T7, reinterpret_cast<int32_t>(Object::null()));
+ __ beq(T2, T7, &checked_ok);
__ delay_slot()->lw(T1, Address(SP, 2 * kWordSize)); // Array.
__ lw(T1, FieldAddress(T1, type_args_field_offset));
// T1: Type arguments of array.
- __ beq(T1, NULLREG, &checked_ok);
+ __ beq(T1, T7, &checked_ok);
// Check if it's dynamic.
// For now handle only TypeArguments and bail out if InstantiatedTypeArgs.
@@ -462,8 +464,9 @@
__ StoreIntoObject(T2,
FieldAddress(T1, Array::data_offset()),
T0);
+ __ LoadImmediate(T7, reinterpret_cast<int32_t>(Object::null()));
__ Ret();
- __ delay_slot()->mov(V0, NULLREG);
+ __ delay_slot()->mov(V0, T7);
__ Bind(&fall_through);
return false;
}
@@ -836,7 +839,6 @@
bool Intrinsifier::Integer_bitXorFromInteger(Assembler* assembler) {
Label fall_through;
- __ Untested("Intrinsifier::Integer_bitXorFromInteger");
TestBothArgumentsSmis(assembler, &fall_through); // Checks two smis.
__ Ret();
@@ -1138,24 +1140,15 @@
// returns false. Any non-double arg1 causes control flow to fall through to the
// slow case (compiled method body).
static bool CompareDoubles(Assembler* assembler, Condition true_condition) {
- Label is_smi, no_conversion, no_NaN, fall_through;
+ Label is_smi, double_op, no_NaN, fall_through;
+ __ Comment("CompareDoubles Intrinsic");
TestLastArgumentIsDouble(assembler, &is_smi, &fall_through);
// Both arguments are double, right operand is in T0.
- __ lwc1(F2, FieldAddress(T0, Double::value_offset()));
- __ b(&no_conversion);
- __ delay_slot()->lwc1(F3,
- FieldAddress(T0, Double::value_offset() + kWordSize));
-
- __ Bind(&is_smi);
- __ SmiUntag(T0);
- __ mtc1(T0, F4);
- __ cvtdw(D1, F4);
-
- __ Bind(&no_conversion);
+ __ LoadDFromOffset(D1, T0, Double::value_offset() - kHeapObjectTag);
+ __ Bind(&double_op);
__ lw(T0, Address(SP, 1 * kWordSize)); // Left argument.
- __ lwc1(F0, FieldAddress(T0, Double::value_offset()));
- __ lwc1(F1, FieldAddress(T0, Double::value_offset() + kWordSize));
+ __ LoadDFromOffset(D0, T0, Double::value_offset() - kHeapObjectTag);
// Now, left is in D0, right is in D1.
__ cund(D0, D1); // Check for NaN.
@@ -1165,11 +1158,11 @@
__ Bind(&no_NaN);
switch (true_condition) {
- case EQ: __ ceqd(D1, D0); break;
- case LT: __ coltd(D1, D0); break;
- case LE: __ coled(D1, D0); break;
- case GT: __ coltd(D0, D1); break;
- case GE: __ coled(D0, D1); break;
+ case EQ: __ ceqd(D0, D1); break;
+ case LT: __ coltd(D0, D1); break;
+ case LE: __ coled(D0, D1); break;
+ case GT: __ coltd(D1, D0); break;
+ case GE: __ coled(D1, D0); break;
default: {
// Only passing the above conditions to this function.
UNREACHABLE();
@@ -1184,6 +1177,14 @@
__ Bind(&is_true);
__ LoadObject(V0, Bool::True());
__ Ret();
+
+
+ __ Bind(&is_smi);
+ __ SmiUntag(T0);
+ __ mtc1(T0, STMP1);
+ __ cvtdw(D1, STMP1);
+ __ b(&double_op);
+
__ Bind(&fall_through);
return false;
}
@@ -1268,7 +1269,6 @@
// Left is double right is integer (Bigint, Mint or Smi)
bool Intrinsifier::Double_mulFromInteger(Assembler* assembler) {
Label fall_through;
- __ Untested("Intrinsifier::Double_mulFromInteger");
// Only Smi-s allowed.
__ lw(T0, Address(SP, 0 * kWordSize));
__ andi(CMPRES, T0, Immediate(kSmiTagMask));
@@ -1320,7 +1320,7 @@
bool Intrinsifier::Double_getIsNaN(Assembler* assembler) {
Label is_true;
- __ Untested("Intrinsifier::Double_getIsNaN");
+
__ lw(T0, Address(SP, 0 * kWordSize));
__ lwc1(F0, FieldAddress(T0, Double::value_offset()));
__ lwc1(F1, FieldAddress(T0, Double::value_offset() + kWordSize));
@@ -1337,7 +1337,6 @@
bool Intrinsifier::Double_getIsNegative(Assembler* assembler) {
Label is_false, is_true, is_zero;
- __ Untested("Intrinsifier::Double_getIsNegative");
__ lw(T0, Address(SP, 0 * kWordSize));
__ lwc1(F0, FieldAddress(T0, Double::value_offset()));
__ lwc1(F1, FieldAddress(T0, Double::value_offset() + kWordSize));
@@ -1390,11 +1389,9 @@
bool Intrinsifier::Math_sqrt(Assembler* assembler) {
Label fall_through, is_smi, double_op;
- __ Untested("Intrinsifier::Math_sqrt");
TestLastArgumentIsDouble(assembler, &is_smi, &fall_through);
// Argument is double and is in T0.
- __ lwc1(F0, FieldAddress(T0, Double::value_offset()));
- __ lwc1(F1, FieldAddress(T0, Double::value_offset() + kWordSize));
+ __ LoadDFromOffset(D1, T0, Double::value_offset() - kHeapObjectTag);
__ Bind(&double_op);
__ sqrtd(D0, D1);
const Class& double_class = Class::Handle(
@@ -1447,8 +1444,6 @@
ASSERT(Utils::IsUint(32, a_int_value));
int32_t a_int32_value = static_cast<int32_t>(a_int_value);
- __ Untested("Random_nextState");
-
__ lw(T0, Address(SP, 0 * kWordSize)); // Receiver.
__ lw(T1, FieldAddress(T0, state_field.Offset())); // Field '_state'.
diff --git a/runtime/vm/runtime_entry_mips.cc b/runtime/vm/runtime_entry_mips.cc
index 622f24f..f9d2b2f 100644
--- a/runtime/vm/runtime_entry_mips.cc
+++ b/runtime/vm/runtime_entry_mips.cc
@@ -28,11 +28,17 @@
uword entry = GetEntryPoint();
#if defined(USING_SIMULATOR)
// Redirection to leaf runtime calls supports a maximum of 4 arguments passed
- // in registers.
- ASSERT(!is_leaf() || (argument_count() <= 4));
+ // in registers (maximum 2 double arguments for leaf float runtime calls).
+ ASSERT(argument_count() >= 0);
+ ASSERT(!is_leaf() ||
+ (!is_float() && (argument_count() <= 4)) ||
+ (argument_count() <= 2));
Simulator::CallKind call_kind =
- is_leaf() ? Simulator::kLeafRuntimeCall : Simulator::kRuntimeCall;
- entry = Simulator::RedirectExternalReference(entry, call_kind);
+ is_leaf() ? (is_float() ? Simulator::kLeafFloatRuntimeCall
+ : Simulator::kLeafRuntimeCall)
+ : Simulator::kRuntimeCall;
+ entry =
+ Simulator::RedirectExternalReference(entry, call_kind, argument_count());
#endif
if (is_leaf()) {
ExternalLabel label(name(), entry);
diff --git a/runtime/vm/simulator_mips.cc b/runtime/vm/simulator_mips.cc
index 3687bc9..0a12de3 100644
--- a/runtime/vm/simulator_mips.cc
+++ b/runtime/vm/simulator_mips.cc
@@ -92,6 +92,7 @@
bool GetValue(char* desc, uint32_t* value);
bool GetFValue(char* desc, double* value);
+ bool GetDValue(char* desc, double* value);
// Set or delete a breakpoint. Returns true if successful.
bool SetBreakpoint(Instr* breakpc);
@@ -226,6 +227,26 @@
}
+bool SimulatorDebugger::GetDValue(char* desc, double* value) {
+ FRegister freg = LookupFRegisterByName(desc);
+ if (freg != kNoFRegister) {
+ *value = sim_->get_fregister_double(freg);
+ return true;
+ }
+ if (desc[0] == '*') {
+ uint32_t addr;
+ if (GetValue(desc + 1, &addr)) {
+ if (Simulator::IsIllegalAddress(addr)) {
+ return false;
+ }
+ *value = *(reinterpret_cast<double*>(addr));
+ return true;
+ }
+ }
+ return false;
+}
+
+
bool SimulatorDebugger::SetBreakpoint(Instr* breakpc) {
// Check if a breakpoint can be set. If not return without any side-effects.
if (sim_->break_pc_ != NULL) {
@@ -361,6 +382,20 @@
} else {
OS::Print("printfloat <dreg or *addr>\n");
}
+ } else if ((strcmp(cmd, "pd") == 0) ||
+ (strcmp(cmd, "printdouble") == 0)) {
+ if (args == 2) {
+ double dvalue;
+ if (GetDValue(arg1, &dvalue)) {
+ uint64_t long_value = bit_cast<uint64_t, double>(dvalue);
+ OS::Print("%s: %llu 0x%llx %.8g\n",
+ arg1, long_value, long_value, dvalue);
+ } else {
+ OS::Print("%s unrecognized\n", arg1);
+ }
+ } else {
+ OS::Print("printfloat <dreg or *addr>\n");
+ }
} else if ((strcmp(cmd, "po") == 0) ||
(strcmp(cmd, "printobject") == 0)) {
if (args == 2) {
@@ -589,13 +624,16 @@
Simulator::CallKind call_kind() const { return call_kind_; }
+ int argument_count() const { return argument_count_; }
+
static Redirection* Get(uword external_function,
- Simulator::CallKind call_kind) {
+ Simulator::CallKind call_kind,
+ int argument_count) {
Redirection* current;
for (current = list_; current != NULL; current = current->next_) {
if (current->external_function_ == external_function) return current;
}
- return new Redirection(external_function, call_kind);
+ return new Redirection(external_function, call_kind, argument_count);
}
static Redirection* FromBreakInstruction(Instr* break_instruction) {
@@ -609,9 +647,12 @@
static const int32_t kRedirectInstruction =
Instr::kBreakPointInstruction | (Instr::kRedirectCode << kBreakCodeShift);
- Redirection(uword external_function, Simulator::CallKind call_kind)
+ Redirection(uword external_function,
+ Simulator::CallKind call_kind,
+ int argument_count)
: external_function_(external_function),
call_kind_(call_kind),
+ argument_count_(argument_count),
break_instruction_(kRedirectInstruction),
next_(list_) {
list_ = this;
@@ -619,6 +660,7 @@
uword external_function_;
Simulator::CallKind call_kind_;
+ int argument_count_;
uint32_t break_instruction_;
Redirection* next_;
static Redirection* list_;
@@ -628,8 +670,11 @@
Redirection* Redirection::list_ = NULL;
-uword Simulator::RedirectExternalReference(uword function, CallKind call_kind) {
- Redirection* redirection = Redirection::Get(function, call_kind);
+uword Simulator::RedirectExternalReference(uword function,
+ CallKind call_kind,
+ int argument_count) {
+ Redirection* redirection =
+ Redirection::Get(function, call_kind, argument_count);
return redirection->address_of_break_instruction();
}
@@ -890,6 +935,9 @@
typedef int32_t (*SimulatorLeafRuntimeCall)(
int32_t r0, int32_t r1, int32_t r2, int32_t r3);
+// Calls to leaf float Dart runtime functions are based on this interface.
+typedef double (*SimulatorLeafFloatRuntimeCall)(double d0, double d1);
+
// Calls to native Dart functions are based on this interface.
typedef void (*SimulatorNativeCall)(NativeArguments* arguments);
@@ -925,7 +973,8 @@
OS::Print("Call to host function at 0x%"Pd"\n", external);
}
- if (redirection->call_kind() != kLeafRuntimeCall) {
+ if ((redirection->call_kind() == kRuntimeCall) ||
+ (redirection->call_kind() == kNativeCall)) {
// The top_exit_frame_info of the current isolate points to the top of
// the simulator stack.
ASSERT((StackTop() - Isolate::Current()->top_exit_frame_info()) <
@@ -953,6 +1002,17 @@
reinterpret_cast<SimulatorLeafRuntimeCall>(external);
a0 = target(a0, a1, a2, a3);
set_register(V0, a0); // Set returned result from function.
+ } else if (redirection->call_kind() == kLeafFloatRuntimeCall) {
+ ASSERT((0 <= redirection->argument_count()) &&
+ (redirection->argument_count() <= 2));
+ // double values are passed and returned in floating point registers.
+ SimulatorLeafFloatRuntimeCall target =
+ reinterpret_cast<SimulatorLeafFloatRuntimeCall>(external);
+ double d0 = 0.0;
+ double d6 = get_fregister_double(F12);
+ double d7 = get_fregister_double(F14);
+ d0 = target(d6, d7);
+ set_fregister_double(F0, d0);
} else {
ASSERT(redirection->call_kind() == kNativeCall);
NativeArguments* arguments;
@@ -986,7 +1046,7 @@
// Zap floating point registers.
int32_t zap_dvalue = icount_;
- for (int i = F0; i <= F31; i++) {
+ for (int i = F4; i <= F18; i++) {
set_fregister(static_cast<FRegister>(i), zap_dvalue);
}
@@ -1109,6 +1169,20 @@
set_register(instr->RdField(), get_lo_register());
break;
}
+ case MOVCI: {
+ ASSERT(instr->SaField() == 0);
+ ASSERT(instr->Bit(17) == 0);
+ int32_t rs_val = get_register(instr->RsField());
+ uint32_t cc, fcsr_cc, test, status;
+ cc = instr->Bits(18, 3);
+ fcsr_cc = get_fcsr_condition_bit(cc);
+ test = instr->Bit(16);
+ status = test_fcsr_bit(fcsr_cc);
+ if (test == status) {
+ set_register(instr->RdField(), rs_val);
+ }
+ break;
+ }
case MOVN: {
ASSERT(instr->SaField() == 0);
// Format(instr, "movn 'rd, 'rs, 'rt");
@@ -1520,6 +1594,12 @@
set_fregister_double(instr->FdField(), fs_dbl);
break;
}
+ case FMT_S: {
+ float fs_flt = get_fregister_float(instr->FsField());
+ double fs_dbl = static_cast<double>(fs_flt);
+ set_fregister_double(instr->FdField(), fs_dbl);
+ break;
+ }
case FMT_L: {
int64_t fs_int = get_fregister_long(instr->FsField());
double fs_dbl = static_cast<double>(fs_int);
diff --git a/runtime/vm/simulator_mips.h b/runtime/vm/simulator_mips.h
index eca271d..9b685b2 100644
--- a/runtime/vm/simulator_mips.h
+++ b/runtime/vm/simulator_mips.h
@@ -106,9 +106,12 @@
enum CallKind {
kRuntimeCall,
kLeafRuntimeCall,
+ kLeafFloatRuntimeCall,
kNativeCall
};
- static uword RedirectExternalReference(uword function, CallKind call_kind);
+ static uword RedirectExternalReference(uword function,
+ CallKind call_kind,
+ int argument_count);
void Longjmp(uword pc,
uword sp,
diff --git a/runtime/vm/stub_code_mips.cc b/runtime/vm/stub_code_mips.cc
index e5cf95c..4bfd8a1 100644
--- a/runtime/vm/stub_code_mips.cc
+++ b/runtime/vm/stub_code_mips.cc
@@ -95,11 +95,11 @@
// Load Context pointer from Isolate structure into A2.
__ lw(A2, Address(CTX, Isolate::top_context_offset()));
- // Reload NULLREG.
- __ LoadImmediate(NULLREG, reinterpret_cast<intptr_t>(Object::null()));
+ // Load null.
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
// Reset Context pointer in Isolate structure.
- __ sw(NULLREG, Address(CTX, Isolate::top_context_offset()));
+ __ sw(TMP, Address(CTX, Isolate::top_context_offset()));
// Cache Context pointer into CTX while executing Dart code.
__ mov(CTX, A2);
@@ -202,11 +202,11 @@
// Load Context pointer from Isolate structure into A2.
__ lw(A2, Address(CTX, Isolate::top_context_offset()));
- // Reload NULLREG.
- __ LoadImmediate(NULLREG, reinterpret_cast<intptr_t>(Object::null()));
+ // Load null.
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
// Reset Context pointer in Isolate structure.
- __ sw(NULLREG, Address(CTX, Isolate::top_context_offset()));
+ __ sw(TMP, Address(CTX, Isolate::top_context_offset()));
// Cache Context pointer into CTX while executing Dart code.
__ mov(CTX, A2);
@@ -228,7 +228,8 @@
__ addiu(SP, SP, Immediate(-2 * kWordSize));
__ sw(S4, Address(SP, 1 * kWordSize));
- __ sw(NULLREG, Address(SP, 0 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 0 * kWordSize));
__ CallRuntime(kPatchStaticCallRuntimeEntry);
__ TraceSimMsg("CallStaticFunctionStub return");
@@ -257,7 +258,8 @@
// Setup space on stack for return value and preserve arguments descriptor.
__ addiu(SP, SP, Immediate(-2 * kWordSize));
__ sw(S4, Address(SP, 1 * kWordSize));
- __ sw(NULLREG, Address(SP, 0 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 0 * kWordSize));
__ CallRuntime(kFixCallersTargetRuntimeEntry);
// Get Code object result and restore arguments descriptor array.
__ lw(T0, Address(SP, 0 * kWordSize));
@@ -279,7 +281,7 @@
static void PushArgumentsArray(Assembler* assembler) {
__ TraceSimMsg("PushArgumentsArray");
// Allocate array to store arguments of caller.
- __ mov(A0, NULLREG);
+ __ LoadImmediate(A0, reinterpret_cast<intptr_t>(Object::null()));
// A0: Null element type for raw Array.
// A1: Smi-tagged argument count, may be zero.
__ BranchLink(&StubCode::AllocateArrayLabel());
@@ -329,7 +331,8 @@
// Push TMP1 data object.
// Push arguments descriptor array.
__ addiu(SP, SP, Immediate(-4 * kWordSize));
- __ sw(NULLREG, Address(SP, 3 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 3 * kWordSize));
__ sw(T1, Address(SP, 2 * kWordSize));
__ sw(S5, Address(SP, 1 * kWordSize));
__ sw(S4, Address(SP, 0 * kWordSize));
@@ -498,8 +501,44 @@
void StubCode::GenerateMegamorphicMissStub(Assembler* assembler) {
- __ Unimplemented("MegamorphicMiss stub");
- return;
+ __ EnterStubFrame();
+
+ // Load the receiver.
+ __ lw(T2, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
+ __ sll(T2, T2, 1); // T2 is a Smi.
+ __ addu(TMP, FP, T2);
+ __ lw(T6, Address(TMP, kParamEndSlotFromFp * kWordSize));
+
+ // Preserve IC data and arguments descriptor.
+ __ addiu(SP, SP, Immediate(-6 * kWordSize));
+ __ sw(S5, Address(SP, 5 * kWordSize));
+ __ sw(S4, Address(SP, 4 * kWordSize));
+
+ // Push space for the return value.
+ // Push the receiver.
+ // Push IC data object.
+ // Push arguments descriptor array.
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 3 * kWordSize));
+ __ sw(T6, Address(SP, 2 * kWordSize));
+ __ sw(S5, Address(SP, 1 * kWordSize));
+ __ sw(S4, Address(SP, 0 * kWordSize));
+
+ __ CallRuntime(kMegamorphicCacheMissHandlerRuntimeEntry);
+
+ __ lw(T0, Address(SP, 3 * kWordSize)); // Get result.
+ __ lw(S4, Address(SP, 4 * kWordSize)); // Restore argument descriptor.
+ __ lw(S5, Address(SP, 5 * kWordSize)); // Restore IC data.
+ __ addiu(SP, SP, Immediate(6 * kWordSize));
+
+ __ LeaveStubFrame();
+
+ Label nonnull;
+ __ BranchNotEqual(T0, reinterpret_cast<int32_t>(Object::null()), &nonnull);
+ __ Branch(&StubCode::InstanceFunctionLookupLabel());
+ __ Bind(&nonnull);
+ __ AddImmediate(T0, Instructions::HeaderSize() - kHeapObjectTag);
+ __ jr(T0);
}
@@ -604,12 +643,13 @@
// T3: iterator which initially points to the start of the variable
// data area to be initialized.
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
Label loop, loop_exit;
__ BranchUnsignedGreaterEqual(T3, T2, &loop_exit);
__ Bind(&loop);
__ addiu(T3, T3, Immediate(kWordSize));
__ bne(T3, T2, &loop);
- __ delay_slot()->sw(NULLREG, Address(T3, -kWordSize));
+ __ delay_slot()->sw(T7, Address(T3, -kWordSize));
__ Bind(&loop_exit);
// Done allocating and initializing the array.
@@ -627,17 +667,17 @@
// Setup space on stack for return value.
// Push array length as Smi and element type.
__ addiu(SP, SP, Immediate(-3 * kWordSize));
- __ sw(NULLREG, Address(SP, 2 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 2 * kWordSize));
__ sw(A1, Address(SP, 1 * kWordSize));
- __ sw(T3, Address(SP, 0 * kWordSize));
+ __ sw(A0, Address(SP, 0 * kWordSize));
__ CallRuntime(kAllocateArrayRuntimeEntry);
__ TraceSimMsg("AllocateArrayStub return");
// Pop arguments; result is popped in IP.
- __ lw(TMP1, Address(SP, 2 * kWordSize));
+ __ lw(V0, Address(SP, 2 * kWordSize));
__ lw(A1, Address(SP, 1 * kWordSize));
- __ lw(T3, Address(SP, 0 * kWordSize));
+ __ lw(A0, Address(SP, 0 * kWordSize));
__ addiu(SP, SP, Immediate(3 * kWordSize));
- __ mov(V0, TMP1);
__ LeaveStubFrameAndReturn();
}
@@ -666,8 +706,10 @@
// Verify that T1 is a closure by checking its class.
Label not_closure;
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
+
// See if it is not a closure, but null object.
- __ beq(T1, NULLREG, ¬_closure);
+ __ beq(T1, T7, ¬_closure);
__ andi(CMPRES, T1, Immediate(kSmiTagMask));
__ beq(CMPRES, ZR, ¬_closure); // Not a closure, but a smi.
@@ -678,7 +720,7 @@
__ lw(T0, FieldAddress(T0, Class::signature_function_offset()));
// See if actual class is not a closure class.
- __ beq(T0, NULLREG, ¬_closure);
+ __ beq(T0, T7, ¬_closure);
// T0 is just the signature function. Load the actual closure function.
__ lw(T2, FieldAddress(T1, Closure::function_offset()));
@@ -689,7 +731,7 @@
Label function_compiled;
// Load closure function code in T0.
__ lw(T0, FieldAddress(T2, Function::code_offset()));
- __ bne(T0, NULLREG, &function_compiled);
+ __ bne(T0, T7, &function_compiled);
// Create a stub frame as we are pushing some objects on the stack before
// calling into the runtime.
@@ -731,9 +773,9 @@
__ EnterStubFrame();
// Setup space on stack for result from error reporting.
- __ addiu(SP, SP, Immediate(2 * kWordSize));
+ __ addiu(SP, SP, Immediate(-2 * kWordSize));
// Arguments descriptor and raw null.
- __ sw(NULLREG, Address(SP, 1 * kWordSize));
+ __ sw(T7, Address(SP, 1 * kWordSize));
__ sw(S4, Address(SP, 0 * kWordSize));
// Load smi-tagged arguments array length, including the non-closure.
@@ -825,9 +867,6 @@
// Compute address of 'arguments array' data area into A2.
__ lw(A2, Address(A2, VMHandles::kOffsetOfRawPtrInHandle));
- // Load the null Object into NULLREG for easy comparisons.
- __ LoadImmediate(NULLREG, reinterpret_cast<intptr_t>(Object::null()));
-
// Set up arguments for the Dart call.
Label push_arguments;
Label done_push_arguments;
@@ -960,20 +999,22 @@
// T2: isolate, not an object.
__ sw(T2, FieldAddress(V0, Context::isolate_offset()));
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
+
// Initialize the context variables.
// V0: new object.
// T1: number of context variables.
Label loop, loop_exit;
__ blez(T1, &loop_exit);
// Setup the parent field.
- __ delay_slot()->sw(NULLREG, FieldAddress(V0, Context::parent_offset()));
+ __ delay_slot()->sw(T7, FieldAddress(V0, Context::parent_offset()));
__ AddImmediate(T3, V0, Context::variable_offset(0) - kHeapObjectTag);
__ sll(T1, T1, 2);
__ Bind(&loop);
__ addiu(T1, T1, Immediate(-kWordSize));
__ addu(TMP1, T3, T1);
__ bgtz(T1, &loop);
- __ delay_slot()->sw(NULLREG, Address(TMP1));
+ __ delay_slot()->sw(T7, Address(TMP1));
__ Bind(&loop_exit);
// Done allocating and initializing the context.
@@ -988,7 +1029,8 @@
// Setup space on stack for return value.
__ SmiTag(T1);
__ addiu(SP, SP, Immediate(-2 * kWordSize));
- __ sw(NULLREG, Address(SP, 1 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 1 * kWordSize)); // Store null.
__ sw(T1, Address(SP, 0 * kWordSize));
__ CallRuntime(kAllocateContextRuntimeEntry); // Allocate context.
__ lw(V0, Address(SP, 1 * kWordSize)); // Get the new context.
@@ -1172,6 +1214,8 @@
__ LoadImmediate(T0, tags);
__ sw(T0, Address(T2, Instance::tags_offset()));
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
+
// Initialize the remaining words of the object.
// T2: new object start.
// T3: next object start.
@@ -1183,7 +1227,7 @@
for (intptr_t current_offset = sizeof(RawObject);
current_offset < instance_size;
current_offset += kWordSize) {
- __ sw(NULLREG, Address(T2, current_offset));
+ __ sw(T7, Address(T2, current_offset));
}
} else {
__ addiu(T4, T2, Immediate(sizeof(RawObject)));
@@ -1197,7 +1241,7 @@
__ Bind(&loop);
__ addiu(T4, T4, Immediate(kWordSize));
__ bne(T4, T3, &loop);
- __ delay_slot()->sw(NULLREG, Address(T4, -kWordSize));
+ __ delay_slot()->sw(T7, Address(T4, -kWordSize));
__ Bind(&loop_exit);
}
if (is_cls_parameterized) {
@@ -1223,7 +1267,8 @@
__ addiu(SP, SP, Immediate(-4 * kWordSize));
// Space on stack for return value.
- __ sw(NULLREG, Address(SP, 3 * kWordSize));
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(T7, Address(SP, 3 * kWordSize));
__ sw(TMP1, Address(SP, 2 * kWordSize)); // Class of object to be allocated.
if (is_cls_parameterized) {
@@ -1233,7 +1278,7 @@
} else {
// Push null type arguments and kNoInstantiator.
__ LoadImmediate(T1, Smi::RawValue(StubCode::kNoInstantiator));
- __ sw(NULLREG, Address(SP, 1 * kWordSize));
+ __ sw(T7, Address(SP, 1 * kWordSize));
__ sw(T1, Address(SP, 0 * kWordSize));
}
__ CallRuntime(kAllocateObjectRuntimeEntry); // Allocate object.
@@ -1264,8 +1309,8 @@
__ TraceSimMsg("AllocationStubForClosure");
__ EnterStubFrame(true); // Uses pool pointer to refer to function.
- const intptr_t kTypeArgumentsFPOffset = 4 * kWordSize;
- const intptr_t kReceiverFPOffset = 5 * kWordSize;
+ const intptr_t kTypeArgumentsFPOffset = 3 * kWordSize;
+ const intptr_t kReceiverFPOffset = 4 * kWordSize;
const intptr_t closure_size = Closure::InstanceSize();
const intptr_t context_size = Context::InstanceSize(1); // Captured receiver.
if (FLAG_inline_alloc &&
@@ -1337,7 +1382,8 @@
__ sw(T0, Address(T4, Context::isolate_offset()));
// Set the parent to null.
- __ sw(NULLREG, Address(T4, Context::parent_offset()));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(T4, Context::parent_offset()));
// Initialize the context variable to the receiver.
__ lw(T0, Address(FP, kReceiverFPOffset));
@@ -1370,24 +1416,24 @@
num_slots = is_implicit_instance_closure ? 4 : 3;
}
__ addiu(SP, SP, Immediate(-num_slots * kWordSize));
- __ LoadObject(TMP1, func);
// Setup space on stack for return value.
- __ sw(NULLREG, Address(SP, (num_slots - 1) * kWordSize));
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(T7, Address(SP, (num_slots - 1) * kWordSize));
+ __ LoadObject(TMP1, func);
__ sw(TMP1, Address(SP, (num_slots - 2) * kWordSize));
if (is_implicit_static_closure) {
__ CallRuntime(kAllocateImplicitStaticClosureRuntimeEntry);
__ TraceSimMsg("AllocationStubForClosure return");
} else {
+ __ mov(T2, T7);
if (is_implicit_instance_closure) {
__ lw(T1, Address(FP, kReceiverFPOffset));
__ sw(T1, Address(SP, (num_slots - 3) * kWordSize)); // Receiver.
- __ sw(NULLREG, Address(SP, (num_slots - 4) * kWordSize)); // Push null.
}
if (has_type_arguments) {
- __ lw(V0, Address(FP, kTypeArgumentsFPOffset));
- // Push type arguments of closure.
- __ sw(V0, Address(SP, (num_slots - 3) * kWordSize));
+ __ lw(T2, Address(FP, kTypeArgumentsFPOffset));
}
+ __ sw(T2, Address(SP, 0 * kWordSize));
if (is_implicit_instance_closure) {
__ CallRuntime(kAllocateImplicitInstanceClosureRuntimeEntry);
@@ -1407,8 +1453,37 @@
}
+// The target function was not found, so invoke method
+// "dynamic noSuchMethod(Invocation invocation)".
+// S5: inline cache data object.
+// S4: arguments descriptor array.
void StubCode::GenerateCallNoSuchMethodFunctionStub(Assembler* assembler) {
- __ Unimplemented("CallNoSuchMethodFunction stub");
+ __ EnterStubFrame();
+
+ // Load the receiver.
+ __ lw(A1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
+ __ sll(TMP, A1, 1); // A1 is a Smi.
+ __ addu(TMP, FP, TMP);
+ __ lw(T6, Address(TMP, kParamEndSlotFromFp * kWordSize));
+
+ // Push space for the return value.
+ // Push the receiver.
+ // Push IC data object.
+ // Push arguments descriptor array.
+ __ addiu(SP, SP, Immediate(-4 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 3 * kWordSize));
+ __ sw(T6, Address(SP, 2 * kWordSize));
+ __ sw(S5, Address(SP, 1 * kWordSize));
+ __ sw(S4, Address(SP, 0 * kWordSize));
+
+ // A1: Smi-tagged arguments array length.
+ PushArgumentsArray(assembler);
+
+ __ CallRuntime(kInvokeNoSuchMethodFunctionRuntimeEntry);
+
+ __ lw(V0, Address(SP, 4 * kWordSize)); // Get result into V0.
+ __ LeaveStubFrameAndReturn();
}
@@ -1585,7 +1660,8 @@
__ addiu(SP, SP, Immediate(-num_slots * kWordSize));
__ sw(S5, Address(SP, (num_slots - 1) * kWordSize));
__ sw(S4, Address(SP, (num_slots - 2) * kWordSize));
- __ sw(NULLREG, Address(SP, (num_slots - 3) * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, (num_slots - 3) * kWordSize));
// Push call arguments.
for (intptr_t i = 0; i < num_args; i++) {
__ lw(TMP1, Address(T1, -i * kWordSize));
@@ -1615,7 +1691,8 @@
__ addiu(SP, SP, Immediate(num_slots * kWordSize));
__ LeaveStubFrame();
Label call_target_function;
- __ bne(T3, NULLREG, &call_target_function);
+ __ BranchNotEqual(T3, reinterpret_cast<int32_t>(Object::null()),
+ &call_target_function);
// NoSuchMethod or closure.
// Mark IC call that it may be a closure call that does not collect
@@ -1737,7 +1814,8 @@
// Preserve arguments descriptor and make room for result.
__ addiu(SP, SP, Immediate(-2 * kWordSize));
__ sw(S4, Address(SP, 1 * kWordSize));
- __ sw(NULLREG, Address(SP, 0 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 0 * kWordSize));
__ CallRuntime(kBreakpointStaticHandlerRuntimeEntry);
// Pop code object result and restore arguments descriptor.
__ lw(T0, Address(SP, 0 * kWordSize));
@@ -1837,14 +1915,17 @@
__ lw(T2, FieldAddress(A2, SubtypeTestCache::cache_offset()));
__ AddImmediate(T2, Array::data_offset() - kHeapObjectTag);
+ __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
+
Label loop, found, not_found, next_iteration;
// T0: instance class id.
// T1: instance type arguments.
// T2: Entry start.
+ // T7: null.
__ SmiTag(T0);
__ Bind(&loop);
__ lw(T3, Address(T2, kWordSize * SubtypeTestCache::kInstanceClassId));
- __ beq(T3, NULLREG, ¬_found);
+ __ beq(T3, T7, ¬_found);
if (n == 1) {
__ beq(T3, T0, &found);
@@ -1868,7 +1949,7 @@
// Fall through to not found.
__ Bind(¬_found);
__ Ret();
- __ delay_slot()->mov(V0, NULLREG);
+ __ delay_slot()->mov(V0, T7);
__ Bind(&found);
__ Ret();
@@ -1947,6 +2028,7 @@
// TODO(srdjan): Move to VM stubs once Boolean objects become VM objects.
void StubCode::GenerateEqualityWithNullArgStub(Assembler* assembler) {
__ TraceSimMsg("EqualityWithNullArgStub");
+ __ Comment("EqualityWithNullArgStub");
__ EnterStubFrame();
static const intptr_t kNumArgsTested = 2;
#if defined(DEBUG)
@@ -2048,7 +2130,8 @@
__ addiu(SP, SP, Immediate(-3 * kWordSize));
__ sw(S4, Address(SP, 2 * kWordSize));
// Setup space on stack for return value.
- __ sw(NULLREG, Address(SP, 1 * kWordSize));
+ __ LoadImmediate(TMP, reinterpret_cast<intptr_t>(Object::null()));
+ __ sw(TMP, Address(SP, 1 * kWordSize));
__ sw(T0, Address(SP, 0 * kWordSize));
__ CallRuntime(kOptimizeInvokedFunctionRuntimeEntry);
__ TraceSimMsg("OptimizeFunctionStub return");
@@ -2079,21 +2162,23 @@
// cannot contain a value that fits in Mint or Smi.
void StubCode::GenerateIdenticalWithNumberCheckStub(Assembler* assembler) {
__ TraceSimMsg("IdenticalWithNumberCheckStub");
- const Register ret = CMPRES;
+ __ Comment("IdenticalWithNumberCheckStub");
const Register temp1 = T2;
const Register temp2 = T3;
const Register left = T1;
const Register right = T0;
// Preserve left, right.
- __ addiu(SP, SP, Immediate(-2 * kWordSize));
- __ sw(T1, Address(SP, 1 * kWordSize));
- __ sw(T0, Address(SP, 0 * kWordSize));
+ __ addiu(SP, SP, Immediate(-4 * kWordSize));
+ __ sw(temp1, Address(SP, 3 * kWordSize));
+ __ sw(temp2, Address(SP, 2 * kWordSize));
+ __ sw(left, Address(SP, 1 * kWordSize));
+ __ sw(right, Address(SP, 0 * kWordSize));
// TOS + 3: left argument.
// TOS + 2: right argument.
// TOS + 1: saved left
// TOS + 0: saved right
- __ lw(left, Address(SP, 3 * kWordSize));
- __ lw(right, Address(SP, 2 * kWordSize));
+ __ lw(left, Address(SP, 5 * kWordSize));
+ __ lw(right, Address(SP, 4 * kWordSize));
Label reference_compare, done, check_mint, check_bigint;
// If any of the arguments is Smi do reference compare.
__ andi(temp1, left, Immediate(kSmiTagMask));
@@ -2106,48 +2191,50 @@
__ LoadClassId(temp2, left);
__ bne(temp1, temp2, &check_mint);
__ LoadClassId(temp2, right);
- __ subu(ret, temp1, temp2);
- __ bne(ret, ZR, &done);
+ __ subu(CMPRES, temp1, temp2);
+ __ bne(CMPRES, ZR, &done);
// Double values bitwise compare.
__ lw(temp1, FieldAddress(left, Double::value_offset() + 0 * kWordSize));
- __ lw(temp1, FieldAddress(right, Double::value_offset() + 0 * kWordSize));
- __ subu(ret, temp1, temp2);
- __ bne(ret, ZR, &done);
+ __ lw(temp2, FieldAddress(right, Double::value_offset() + 0 * kWordSize));
+ __ subu(CMPRES, temp1, temp2);
+ __ bne(CMPRES, ZR, &done);
__ lw(temp1, FieldAddress(left, Double::value_offset() + 1 * kWordSize));
__ lw(temp2, FieldAddress(right, Double::value_offset() + 1 * kWordSize));
__ b(&done);
- __ delay_slot()->subu(ret, temp1, temp2);
+ __ delay_slot()->subu(CMPRES, temp1, temp2);
__ Bind(&check_mint);
__ LoadImmediate(temp1, kMintCid);
__ LoadClassId(temp2, left);
__ bne(temp1, temp2, &check_bigint);
__ LoadClassId(temp2, right);
- __ subu(ret, temp1, temp2);
- __ bne(ret, ZR, &done);
+ __ subu(CMPRES, temp1, temp2);
+ __ bne(CMPRES, ZR, &done);
__ lw(temp1, FieldAddress(left, Mint::value_offset() + 0 * kWordSize));
__ lw(temp2, FieldAddress(right, Mint::value_offset() + 0 * kWordSize));
- __ subu(ret, temp1, temp2);
- __ bne(ret, ZR, &done);
+ __ subu(CMPRES, temp1, temp2);
+ __ bne(CMPRES, ZR, &done);
__ lw(temp1, FieldAddress(left, Mint::value_offset() + 1 * kWordSize));
__ lw(temp2, FieldAddress(right, Mint::value_offset() + 1 * kWordSize));
__ b(&done);
- __ delay_slot()->subu(ret, temp1, temp2);
+ __ delay_slot()->subu(CMPRES, temp1, temp2);
__ Bind(&check_bigint);
__ LoadImmediate(temp1, kBigintCid);
__ LoadClassId(temp2, left);
__ bne(temp1, temp2, &reference_compare);
__ LoadClassId(temp2, right);
- __ subu(ret, temp1, temp2);
- __ bne(ret, ZR, &done);
+ __ subu(CMPRES, temp1, temp2);
+ __ bne(CMPRES, ZR, &done);
- __ EnterStubFrame(0);
+ __ EnterStubFrame();
__ ReserveAlignedFrameSpace(2 * kWordSize);
- __ sw(T1, Address(SP, 1 * kWordSize));
- __ sw(T0, Address(SP, 0 * kWordSize));
+ __ sw(left, Address(SP, 1 * kWordSize));
+ __ sw(right, Address(SP, 0 * kWordSize));
+ __ mov(A0, left);
+ __ mov(A1, right);
__ CallRuntime(kBigintCompareRuntimeEntry);
__ TraceSimMsg("IdenticalWithNumberCheckStub return");
// Result in V0, 0 means equal.
@@ -2156,14 +2243,16 @@
__ delay_slot()->mov(CMPRES, V0);
__ Bind(&reference_compare);
- __ subu(ret, left, right);
+ __ subu(CMPRES, left, right);
__ Bind(&done);
// A branch or test after this comparison will check CMPRES == TMP1.
__ mov(TMP1, ZR);
- __ lw(T0, Address(SP, 0 * kWordSize));
- __ lw(T1, Address(SP, 1 * kWordSize));
+ __ lw(right, Address(SP, 0 * kWordSize));
+ __ lw(left, Address(SP, 1 * kWordSize));
+ __ lw(temp2, Address(SP, 2 * kWordSize));
+ __ lw(temp1, Address(SP, 3 * kWordSize));
__ Ret();
- __ delay_slot()->addiu(SP, SP, Immediate(2 * kWordSize));
+ __ delay_slot()->addiu(SP, SP, Immediate(4 * kWordSize));
}
} // namespace dart
diff --git a/tests/language/language.status b/tests/language/language.status
index 7554f25..e55fc34 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -553,4 +553,17 @@
*: Skip
[ $arch == simmips ]
-*: Skip
+arithmetic_test: Fail, Crash
+bit_operations_test: Crash, Fail
+char_escape_test: Pass, Crash
+deopt_smi_op_test: Fail
+div_with_power_of_two_test: Fail
+gc_test: Crash
+invocation_mirror_test: Fail
+load_to_load_forwarding_vm_test: Fail
+named_parameters_with_conversions_test: Pass, Crash
+positive_bit_operations_test: Pass, Fail, Crash
+left_shift_test: Pass, Fail
+large_implicit_getter_test: Crash, Pass
+stack_overflow_test: Crash, Pass
+stack_overflow_stacktrace_test: Crash, Pass