Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(249)

Unified Diff: runtime/vm/flow_graph_compiler_mips.cc

Issue 17131002: Enables language tests for SIMMIPS. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/disassembler_mips.cc ('k') | runtime/vm/instructions_mips.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/flow_graph_compiler_mips.cc
===================================================================
--- runtime/vm/flow_graph_compiler_mips.cc (revision 24063)
+++ runtime/vm/flow_graph_compiler_mips.cc (working copy)
@@ -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);
}
« no previous file with comments | « runtime/vm/disassembler_mips.cc ('k') | runtime/vm/instructions_mips.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698