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

Side by Side Diff: runtime/vm/flow_graph_compiler_mips.cc

Issue 2858623002: Remove MIPS support (Closed)
Patch Set: Merge and cleanup Created 3 years, 5 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 unified diff | Download patch
« no previous file with comments | « runtime/vm/disassembler_mips.cc ('k') | runtime/vm/instructions.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_MIPS.
6 #if defined(TARGET_ARCH_MIPS)
7
8 #include "vm/flow_graph_compiler.h"
9
10 #include "vm/ast_printer.h"
11 #include "vm/compiler.h"
12 #include "vm/dart_entry.h"
13 #include "vm/deopt_instructions.h"
14 #include "vm/il_printer.h"
15 #include "vm/instructions.h"
16 #include "vm/locations.h"
17 #include "vm/object_store.h"
18 #include "vm/parser.h"
19 #include "vm/stack_frame.h"
20 #include "vm/stub_code.h"
21 #include "vm/symbols.h"
22
23 namespace dart {
24
25 DEFINE_FLAG(bool, trap_on_deoptimization, false, "Trap on deoptimization.");
26
27
28 FlowGraphCompiler::~FlowGraphCompiler() {
29 // BlockInfos are zone-allocated, so their destructors are not called.
30 // Verify the labels explicitly here.
31 for (int i = 0; i < block_info_.length(); ++i) {
32 ASSERT(!block_info_[i]->jump_label()->IsLinked());
33 }
34 }
35
36
37 bool FlowGraphCompiler::SupportsUnboxedDoubles() {
38 return true;
39 }
40
41
42 bool FlowGraphCompiler::SupportsUnboxedMints() {
43 return true;
44 }
45
46
47 bool FlowGraphCompiler::SupportsUnboxedSimd128() {
48 return false;
49 }
50
51
52 bool FlowGraphCompiler::SupportsHardwareDivision() {
53 return true;
54 }
55
56
57 bool FlowGraphCompiler::CanConvertUnboxedMintToDouble() {
58 // TODO(johnmccutchan): Investigate possibility on MIPS once
59 // mints are implemented there.
60 return false;
61 }
62
63
64 void FlowGraphCompiler::EnterIntrinsicMode() {
65 ASSERT(!intrinsic_mode());
66 intrinsic_mode_ = true;
67 assembler()->set_constant_pool_allowed(false);
68 }
69
70
71 void FlowGraphCompiler::ExitIntrinsicMode() {
72 ASSERT(intrinsic_mode());
73 intrinsic_mode_ = false;
74 assembler()->set_constant_pool_allowed(true);
75 }
76
77
78 RawTypedData* CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler,
79 DeoptInfoBuilder* builder,
80 const Array& deopt_table) {
81 if (deopt_env_ == NULL) {
82 ++builder->current_info_number_;
83 return TypedData::null();
84 }
85
86 intptr_t stack_height = compiler->StackSize();
87 AllocateIncomingParametersRecursive(deopt_env_, &stack_height);
88
89 intptr_t slot_ix = 0;
90 Environment* current = deopt_env_;
91
92 // Emit all kMaterializeObject instructions describing objects to be
93 // materialized on the deoptimization as a prefix to the deoptimization info.
94 EmitMaterializations(deopt_env_, builder);
95
96 // The real frame starts here.
97 builder->MarkFrameStart();
98
99 Zone* zone = compiler->zone();
100
101 builder->AddPp(current->function(), slot_ix++);
102 builder->AddPcMarker(Function::ZoneHandle(zone), slot_ix++);
103 builder->AddCallerFp(slot_ix++);
104 builder->AddReturnAddress(current->function(), deopt_id(), slot_ix++);
105
106
107 // Emit all values that are needed for materialization as a part of the
108 // expression stack for the bottom-most frame. This guarantees that GC
109 // will be able to find them during materialization.
110 slot_ix = builder->EmitMaterializationArguments(slot_ix);
111
112 // For the innermost environment, set outgoing arguments and the locals.
113 for (intptr_t i = current->Length() - 1;
114 i >= current->fixed_parameter_count(); i--) {
115 builder->AddCopy(current->ValueAt(i), current->LocationAt(i), slot_ix++);
116 }
117
118 Environment* previous = current;
119 current = current->outer();
120 while (current != NULL) {
121 builder->AddPp(current->function(), slot_ix++);
122 builder->AddPcMarker(previous->function(), slot_ix++);
123 builder->AddCallerFp(slot_ix++);
124
125 // For any outer environment the deopt id is that of the call instruction
126 // which is recorded in the outer environment.
127 builder->AddReturnAddress(current->function(),
128 Thread::ToDeoptAfter(current->deopt_id()),
129 slot_ix++);
130
131 // The values of outgoing arguments can be changed from the inlined call so
132 // we must read them from the previous environment.
133 for (intptr_t i = previous->fixed_parameter_count() - 1; i >= 0; i--) {
134 builder->AddCopy(previous->ValueAt(i), previous->LocationAt(i),
135 slot_ix++);
136 }
137
138 // Set the locals, note that outgoing arguments are not in the environment.
139 for (intptr_t i = current->Length() - 1;
140 i >= current->fixed_parameter_count(); i--) {
141 builder->AddCopy(current->ValueAt(i), current->LocationAt(i), slot_ix++);
142 }
143
144 // Iterate on the outer environment.
145 previous = current;
146 current = current->outer();
147 }
148 // The previous pointer is now the outermost environment.
149 ASSERT(previous != NULL);
150
151 // Set slots for the outermost environment.
152 builder->AddCallerPp(slot_ix++);
153 builder->AddPcMarker(previous->function(), slot_ix++);
154 builder->AddCallerFp(slot_ix++);
155 builder->AddCallerPc(slot_ix++);
156
157 // For the outermost environment, set the incoming arguments.
158 for (intptr_t i = previous->fixed_parameter_count() - 1; i >= 0; i--) {
159 builder->AddCopy(previous->ValueAt(i), previous->LocationAt(i), slot_ix++);
160 }
161
162 return builder->CreateDeoptInfo(deopt_table);
163 }
164
165
166 void CompilerDeoptInfoWithStub::GenerateCode(FlowGraphCompiler* compiler,
167 intptr_t stub_ix) {
168 // Calls do not need stubs, they share a deoptimization trampoline.
169 ASSERT(reason() != ICData::kDeoptAtCall);
170 Assembler* assembler = compiler->assembler();
171 #define __ assembler->
172 __ Comment("%s", Name());
173 __ Bind(entry_label());
174 if (FLAG_trap_on_deoptimization) {
175 __ break_(0);
176 }
177
178 ASSERT(deopt_env() != NULL);
179 __ Push(CODE_REG);
180 __ BranchLink(*StubCode::Deoptimize_entry());
181 set_pc_offset(assembler->CodeSize());
182 #undef __
183 }
184
185
186 #define __ assembler()->
187
188
189 // Fall through if bool_register contains null.
190 void FlowGraphCompiler::GenerateBoolToJump(Register bool_register,
191 Label* is_true,
192 Label* is_false) {
193 __ Comment("BoolToJump");
194 Label fall_through;
195 __ BranchEqual(bool_register, Object::null_object(), &fall_through);
196 __ BranchEqual(bool_register, Bool::True(), is_true);
197 __ b(is_false);
198 __ Bind(&fall_through);
199 }
200
201
202 // A0: instance (must be preserved).
203 // A1: instantiator type arguments (if used).
204 // A2: function type arguments (if used).
205 // Clobbers A3.
206 RawSubtypeTestCache* FlowGraphCompiler::GenerateCallSubtypeTestStub(
207 TypeTestStubKind test_kind,
208 Register instance_reg,
209 Register instantiator_type_arguments_reg,
210 Register function_type_arguments_reg,
211 Register temp_reg,
212 Label* is_instance_lbl,
213 Label* is_not_instance_lbl) {
214 __ Comment("CallSubtypeTestStub");
215 ASSERT(instance_reg == A0);
216 ASSERT(temp_reg == kNoRegister); // Unused on MIPS.
217 const SubtypeTestCache& type_test_cache =
218 SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New());
219 __ LoadUniqueObject(A3, type_test_cache);
220 if (test_kind == kTestTypeOneArg) {
221 ASSERT(instantiator_type_arguments_reg == kNoRegister);
222 ASSERT(function_type_arguments_reg == kNoRegister);
223 __ BranchLink(*StubCode::Subtype1TestCache_entry());
224 } else if (test_kind == kTestTypeTwoArgs) {
225 ASSERT(instantiator_type_arguments_reg == kNoRegister);
226 ASSERT(function_type_arguments_reg == kNoRegister);
227 __ BranchLink(*StubCode::Subtype2TestCache_entry());
228 } else if (test_kind == kTestTypeFourArgs) {
229 ASSERT(instantiator_type_arguments_reg == A1);
230 ASSERT(function_type_arguments_reg == A2);
231 __ BranchLink(*StubCode::Subtype4TestCache_entry());
232 } else {
233 UNREACHABLE();
234 }
235 // Result is in V0: null -> not found, otherwise Bool::True or Bool::False.
236 GenerateBoolToJump(V0, is_instance_lbl, is_not_instance_lbl);
237 return type_test_cache.raw();
238 }
239
240
241 // Jumps to labels 'is_instance' or 'is_not_instance' respectively, if
242 // type test is conclusive, otherwise fallthrough if a type test could not
243 // be completed.
244 // A0: instance being type checked (preserved).
245 // Clobbers T0.
246 RawSubtypeTestCache*
247 FlowGraphCompiler::GenerateInstantiatedTypeWithArgumentsTest(
248 TokenPosition token_pos,
249 const AbstractType& type,
250 Label* is_instance_lbl,
251 Label* is_not_instance_lbl) {
252 __ Comment("InstantiatedTypeWithArgumentsTest");
253 ASSERT(type.IsInstantiated());
254 const Class& type_class = Class::ZoneHandle(zone(), type.type_class());
255 ASSERT(type.IsFunctionType() || (type_class.NumTypeArguments() > 0));
256 const Register kInstanceReg = A0;
257 Error& bound_error = Error::Handle(zone());
258 const Type& int_type = Type::Handle(zone(), Type::IntType());
259 const bool smi_is_ok =
260 int_type.IsSubtypeOf(type, &bound_error, NULL, Heap::kOld);
261 // Malformed type should have been handled at graph construction time.
262 ASSERT(smi_is_ok || bound_error.IsNull());
263 __ andi(CMPRES1, kInstanceReg, Immediate(kSmiTagMask));
264 if (smi_is_ok) {
265 __ beq(CMPRES1, ZR, is_instance_lbl);
266 } else {
267 __ beq(CMPRES1, ZR, is_not_instance_lbl);
268 }
269 // A function type test requires checking the function signature.
270 if (!type.IsFunctionType()) {
271 const intptr_t num_type_args = type_class.NumTypeArguments();
272 const intptr_t num_type_params = type_class.NumTypeParameters();
273 const intptr_t from_index = num_type_args - num_type_params;
274 const TypeArguments& type_arguments =
275 TypeArguments::ZoneHandle(zone(), type.arguments());
276 const bool is_raw_type = type_arguments.IsNull() ||
277 type_arguments.IsRaw(from_index, num_type_params);
278 if (is_raw_type) {
279 const Register kClassIdReg = T0;
280 // dynamic type argument, check only classes.
281 __ LoadClassId(kClassIdReg, kInstanceReg);
282 __ BranchEqual(kClassIdReg, Immediate(type_class.id()), is_instance_lbl);
283 // List is a very common case.
284 if (IsListClass(type_class)) {
285 GenerateListTypeCheck(kClassIdReg, is_instance_lbl);
286 }
287 return GenerateSubtype1TestCacheLookup(
288 token_pos, type_class, is_instance_lbl, is_not_instance_lbl);
289 }
290 // If one type argument only, check if type argument is Object or dynamic.
291 if (type_arguments.Length() == 1) {
292 const AbstractType& tp_argument =
293 AbstractType::ZoneHandle(zone(), type_arguments.TypeAt(0));
294 ASSERT(!tp_argument.IsMalformed());
295 if (tp_argument.IsType()) {
296 ASSERT(tp_argument.HasResolvedTypeClass());
297 // Check if type argument is dynamic or Object.
298 const Type& object_type = Type::Handle(zone(), Type::ObjectType());
299 if (object_type.IsSubtypeOf(tp_argument, NULL, NULL, Heap::kOld)) {
300 // Instance class test only necessary.
301 return GenerateSubtype1TestCacheLookup(
302 token_pos, type_class, is_instance_lbl, is_not_instance_lbl);
303 }
304 }
305 }
306 }
307 // Regular subtype test cache involving instance's type arguments.
308 const Register kInstantiatorTypeArgumentsReg = kNoRegister;
309 const Register kFunctionTypeArgumentsReg = kNoRegister;
310 const Register kTempReg = kNoRegister;
311 // A0: instance (must be preserved).
312 return GenerateCallSubtypeTestStub(kTestTypeTwoArgs, kInstanceReg,
313 kInstantiatorTypeArgumentsReg,
314 kFunctionTypeArgumentsReg, kTempReg,
315 is_instance_lbl, is_not_instance_lbl);
316 }
317
318
319 void FlowGraphCompiler::CheckClassIds(Register class_id_reg,
320 const GrowableArray<intptr_t>& class_ids,
321 Label* is_equal_lbl,
322 Label* is_not_equal_lbl) {
323 __ Comment("CheckClassIds");
324 for (intptr_t i = 0; i < class_ids.length(); i++) {
325 __ BranchEqual(class_id_reg, Immediate(class_ids[i]), is_equal_lbl);
326 }
327 __ b(is_not_equal_lbl);
328 }
329
330
331 // Testing against an instantiated type with no arguments, without
332 // SubtypeTestCache.
333 // A0: instance being type checked (preserved).
334 // Clobbers: T0, T1, T2
335 // Returns true if there is a fallthrough.
336 bool FlowGraphCompiler::GenerateInstantiatedTypeNoArgumentsTest(
337 TokenPosition token_pos,
338 const AbstractType& type,
339 Label* is_instance_lbl,
340 Label* is_not_instance_lbl) {
341 __ Comment("InstantiatedTypeNoArgumentsTest");
342 ASSERT(type.IsInstantiated());
343 if (type.IsFunctionType()) {
344 // Fallthrough.
345 return true;
346 }
347 const Class& type_class = Class::Handle(zone(), type.type_class());
348 ASSERT(type_class.NumTypeArguments() == 0);
349
350 const Register kInstanceReg = A0;
351 __ andi(T0, A0, Immediate(kSmiTagMask));
352 // If instance is Smi, check directly.
353 const Class& smi_class = Class::Handle(zone(), Smi::Class());
354 if (smi_class.IsSubtypeOf(Object::null_type_arguments(), type_class,
355 Object::null_type_arguments(), NULL, NULL,
356 Heap::kOld)) {
357 __ beq(T0, ZR, is_instance_lbl);
358 } else {
359 __ beq(T0, ZR, is_not_instance_lbl);
360 }
361 const Register kClassIdReg = T0;
362 __ LoadClassId(kClassIdReg, kInstanceReg);
363 // See ClassFinalizer::ResolveSuperTypeAndInterfaces for list of restricted
364 // interfaces.
365 // Bool interface can be implemented only by core class Bool.
366 if (type.IsBoolType()) {
367 __ BranchEqual(kClassIdReg, Immediate(kBoolCid), is_instance_lbl);
368 __ b(is_not_instance_lbl);
369 return false;
370 }
371 // Custom checking for numbers (Smi, Mint, Bigint and Double).
372 // Note that instance is not Smi (checked above).
373 if (type.IsNumberType() || type.IsIntType() || type.IsDoubleType()) {
374 GenerateNumberTypeCheck(kClassIdReg, type, is_instance_lbl,
375 is_not_instance_lbl);
376 return false;
377 }
378 if (type.IsStringType()) {
379 GenerateStringTypeCheck(kClassIdReg, is_instance_lbl, is_not_instance_lbl);
380 return false;
381 }
382 if (type.IsDartFunctionType()) {
383 // Check if instance is a closure.
384 __ BranchEqual(kClassIdReg, Immediate(kClosureCid), is_instance_lbl);
385 return true; // Fall through
386 }
387 // Compare if the classes are equal.
388 if (!type_class.is_abstract()) {
389 __ BranchEqual(kClassIdReg, Immediate(type_class.id()), is_instance_lbl);
390 }
391 // Otherwise fallthrough.
392 return true;
393 }
394
395
396 // Uses SubtypeTestCache to store instance class and result.
397 // A0: instance to test.
398 // Clobbers A1-A3, T0-T3.
399 // Immediate class test already done.
400 // TODO(srdjan): Implement a quicker subtype check, as type test
401 // arrays can grow too high, but they may be useful when optimizing
402 // code (type-feedback).
403 RawSubtypeTestCache* FlowGraphCompiler::GenerateSubtype1TestCacheLookup(
404 TokenPosition token_pos,
405 const Class& type_class,
406 Label* is_instance_lbl,
407 Label* is_not_instance_lbl) {
408 __ Comment("Subtype1TestCacheLookup");
409 const Register kInstanceReg = A0;
410 __ LoadClass(T0, kInstanceReg);
411 // T0: instance class.
412 // Check immediate superclass equality.
413 __ lw(T0, FieldAddress(T0, Class::super_type_offset()));
414 __ lw(T0, FieldAddress(T0, Type::type_class_id_offset()));
415 __ BranchEqual(T0, Immediate(Smi::RawValue(type_class.id())),
416 is_instance_lbl);
417
418 const Register kInstantiatorTypeArgumentsReg = kNoRegister;
419 const Register kFunctionTypeArgumentsReg = kNoRegister;
420 const Register kTempReg = kNoRegister;
421 return GenerateCallSubtypeTestStub(kTestTypeOneArg, kInstanceReg,
422 kInstantiatorTypeArgumentsReg,
423 kFunctionTypeArgumentsReg, kTempReg,
424 is_instance_lbl, is_not_instance_lbl);
425 }
426
427
428 // Generates inlined check if 'type' is a type parameter or type itself
429 // A0: instance (preserved).
430 RawSubtypeTestCache* FlowGraphCompiler::GenerateUninstantiatedTypeTest(
431 TokenPosition token_pos,
432 const AbstractType& type,
433 Label* is_instance_lbl,
434 Label* is_not_instance_lbl) {
435 __ Comment("UninstantiatedTypeTest");
436 ASSERT(!type.IsInstantiated());
437 // Skip check if destination is a dynamic type.
438 if (type.IsTypeParameter()) {
439 const TypeParameter& type_param = TypeParameter::Cast(type);
440 __ lw(A1, Address(SP, 1 * kWordSize)); // Get instantiator type args.
441 __ lw(A2, Address(SP, 0 * kWordSize)); // Get function type args.
442 // A1: instantiator type arguments.
443 // A2: function type arguments.
444 const Register kTypeArgumentsReg =
445 type_param.IsClassTypeParameter() ? A1 : A2;
446 // Check if type arguments are null, i.e. equivalent to vector of dynamic.
447 __ LoadObject(T7, Object::null_object());
448 __ beq(kTypeArgumentsReg, T7, is_instance_lbl);
449 __ lw(T2, FieldAddress(kTypeArgumentsReg,
450 TypeArguments::type_at_offset(type_param.index())));
451 // T2: concrete type of type.
452 // Check if type argument is dynamic.
453 __ BranchEqual(T2, Object::dynamic_type(), is_instance_lbl);
454 __ BranchEqual(T2, Type::ZoneHandle(zone(), Type::ObjectType()),
455 is_instance_lbl);
456 // TODO(regis): Optimize void type as well once allowed as type argument.
457
458 // For Smi check quickly against int and num interfaces.
459 Label not_smi;
460 __ andi(CMPRES1, A0, Immediate(kSmiTagMask));
461 __ bne(CMPRES1, ZR, &not_smi); // Value is Smi?
462 __ BranchEqual(T2, Type::ZoneHandle(zone(), Type::IntType()),
463 is_instance_lbl);
464 __ BranchEqual(T2, Type::ZoneHandle(zone(), Type::Number()),
465 is_instance_lbl);
466 // Smi must be handled in runtime.
467 Label fall_through;
468 __ b(&fall_through);
469
470 __ Bind(&not_smi);
471 // A0: instance.
472 // A1: instantiator type arguments.
473 // A2: function type arguments.
474 const Register kInstanceReg = A0;
475 const Register kInstantiatorTypeArgumentsReg = A1;
476 const Register kFunctionTypeArgumentsReg = A2;
477 const Register kTempReg = kNoRegister;
478 const SubtypeTestCache& type_test_cache = SubtypeTestCache::ZoneHandle(
479 zone(), GenerateCallSubtypeTestStub(
480 kTestTypeFourArgs, kInstanceReg,
481 kInstantiatorTypeArgumentsReg, kFunctionTypeArgumentsReg,
482 kTempReg, is_instance_lbl, is_not_instance_lbl));
483 __ Bind(&fall_through);
484 return type_test_cache.raw();
485 }
486 if (type.IsType()) {
487 const Register kInstanceReg = A0;
488 const Register kInstantiatorTypeArgumentsReg = A1;
489 const Register kFunctionTypeArgumentsReg = A2;
490 __ andi(CMPRES1, kInstanceReg, Immediate(kSmiTagMask));
491 __ beq(CMPRES1, ZR, is_not_instance_lbl); // Is instance Smi?
492 __ lw(kInstantiatorTypeArgumentsReg, Address(SP, 1 * kWordSize));
493 __ lw(kFunctionTypeArgumentsReg, Address(SP, 0 * kWordSize));
494 // Uninstantiated type class is known at compile time, but the type
495 // arguments are determined at runtime by the instantiator.
496 const Register kTempReg = kNoRegister;
497 return GenerateCallSubtypeTestStub(kTestTypeFourArgs, kInstanceReg,
498 kInstantiatorTypeArgumentsReg,
499 kFunctionTypeArgumentsReg, kTempReg,
500 is_instance_lbl, is_not_instance_lbl);
501 }
502 return SubtypeTestCache::null();
503 }
504
505
506 // Inputs:
507 // - A0: instance being type checked (preserved).
508 // - A1: optional instantiator type arguments (preserved).
509 // - A2: optional function type arguments (preserved).
510 // Returns:
511 // - preserved instance in A0, optional instantiator type arguments in A1, and
512 // optional function type arguments in A2.
513 // Clobbers: T0, T1, T2
514 // Note that this inlined code must be followed by the runtime_call code, as it
515 // may fall through to it. Otherwise, this inline code will jump to the label
516 // is_instance or to the label is_not_instance.
517 RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof(
518 TokenPosition token_pos,
519 const AbstractType& type,
520 Label* is_instance_lbl,
521 Label* is_not_instance_lbl) {
522 __ Comment("InlineInstanceof");
523 if (type.IsInstantiated()) {
524 const Class& type_class = Class::ZoneHandle(zone(), type.type_class());
525 // A class equality check is only applicable with a dst type (not a
526 // function type) of a non-parameterized class or with a raw dst type of
527 // a parameterized class.
528 if (type.IsFunctionType() || (type_class.NumTypeArguments() > 0)) {
529 return GenerateInstantiatedTypeWithArgumentsTest(
530 token_pos, type, is_instance_lbl, is_not_instance_lbl);
531 // Fall through to runtime call.
532 }
533 const bool has_fall_through = GenerateInstantiatedTypeNoArgumentsTest(
534 token_pos, type, is_instance_lbl, is_not_instance_lbl);
535 if (has_fall_through) {
536 // If test non-conclusive so far, try the inlined type-test cache.
537 // 'type' is known at compile time.
538 return GenerateSubtype1TestCacheLookup(
539 token_pos, type_class, is_instance_lbl, is_not_instance_lbl);
540 } else {
541 return SubtypeTestCache::null();
542 }
543 }
544 return GenerateUninstantiatedTypeTest(token_pos, type, is_instance_lbl,
545 is_not_instance_lbl);
546 }
547
548
549 // If instanceof type test cannot be performed successfully at compile time and
550 // therefore eliminated, optimize it by adding inlined tests for:
551 // - NULL -> return type == Null (type is not Object or dynamic).
552 // - Smi -> compile time subtype check (only if dst class is not parameterized).
553 // - Class equality (only if class is not parameterized).
554 // Inputs:
555 // - A0: object.
556 // - A1: instantiator type arguments or raw_null.
557 // - A2: function type arguments or raw_null.
558 // Returns:
559 // - true or false in V0.
560 void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
561 intptr_t deopt_id,
562 const AbstractType& type,
563 LocationSummary* locs) {
564 ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded());
565 ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType());
566
567 // Preserve instantiator type arguments (A1) and function type arguments (A2).
568 __ addiu(SP, SP, Immediate(-2 * kWordSize));
569 __ sw(A1, Address(SP, 1 * kWordSize));
570 __ sw(A2, Address(SP, 0 * kWordSize));
571
572 Label is_instance, is_not_instance;
573 // If type is instantiated and non-parameterized, we can inline code
574 // checking whether the tested instance is a Smi.
575 if (type.IsInstantiated()) {
576 // A null object is only an instance of Null, Object, and dynamic.
577 // Object and dynamic have already been checked above (if the type is
578 // instantiated). So we can return false here if the instance is null,
579 // unless the type is Null (and if the type is instantiated).
580 // We can only inline this null check if the type is instantiated at compile
581 // time, since an uninstantiated type at compile time could be Null, Object,
582 // or dynamic at run time.
583 __ BranchEqual(A0, Object::null_object(),
584 type.IsNullType() ? &is_instance : &is_not_instance);
585 }
586
587 // Generate inline instanceof test.
588 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone());
589 test_cache =
590 GenerateInlineInstanceof(token_pos, type, &is_instance, &is_not_instance);
591
592 // test_cache is null if there is no fall-through.
593 Label done;
594 if (!test_cache.IsNull()) {
595 // Generate runtime call.
596 __ lw(A1, Address(SP, 1 * kWordSize)); // Get instantiator type args.
597 __ lw(A2, Address(SP, 0 * kWordSize)); // Get function type args.
598 __ addiu(SP, SP, Immediate(-6 * kWordSize));
599 __ LoadObject(TMP, Object::null_object());
600 __ sw(TMP, Address(SP, 5 * kWordSize)); // Make room for the result.
601 __ sw(A0, Address(SP, 4 * kWordSize)); // Push the instance.
602 __ LoadObject(TMP, type);
603 __ sw(TMP, Address(SP, 3 * kWordSize)); // Push the type.
604 __ sw(A1, Address(SP, 2 * kWordSize)); // Push instantiator type args.
605 __ sw(A2, Address(SP, 1 * kWordSize)); // Push function type args.
606 __ LoadUniqueObject(A0, test_cache);
607 __ sw(A0, Address(SP, 0 * kWordSize));
608 GenerateRuntimeCall(token_pos, deopt_id, kInstanceofRuntimeEntry, 5, locs);
609 // Pop the parameters supplied to the runtime entry. The result of the
610 // instanceof runtime call will be left as the result of the operation.
611 __ lw(V0, Address(SP, 5 * kWordSize));
612 __ b(&done);
613 __ delay_slot()->addiu(SP, SP, Immediate(6 * kWordSize));
614 }
615 __ Bind(&is_not_instance);
616 __ LoadObject(V0, Bool::Get(false));
617 __ b(&done);
618
619 __ Bind(&is_instance);
620 __ LoadObject(V0, Bool::Get(true));
621 __ Bind(&done);
622 // Remove instantiator type arguments and function type arguments.
623 __ Drop(2);
624 }
625
626
627 // Optimize assignable type check by adding inlined tests for:
628 // - NULL -> return NULL.
629 // - Smi -> compile time subtype check (only if dst class is not parameterized).
630 // - Class equality (only if class is not parameterized).
631 // Inputs:
632 // - A0: instance being type checked.
633 // - A1: instantiator type arguments or raw_null.
634 // - A2: function type arguments or raw_null.
635 // Returns:
636 // - object in A0 for successful assignable check (or throws TypeError).
637 // Clobbers: T0, T1, T2
638 // Performance notes: positive checks must be quick, negative checks can be slow
639 // as they throw an exception.
640 void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
641 intptr_t deopt_id,
642 const AbstractType& dst_type,
643 const String& dst_name,
644 LocationSummary* locs) {
645 __ Comment("AssertAssignable");
646 ASSERT(!token_pos.IsClassifying());
647 ASSERT(!dst_type.IsNull());
648 ASSERT(dst_type.IsFinalized());
649 // Assignable check is skipped in FlowGraphBuilder, not here.
650 ASSERT(dst_type.IsMalformedOrMalbounded() ||
651 (!dst_type.IsDynamicType() && !dst_type.IsObjectType() &&
652 !dst_type.IsVoidType()));
653
654 // Preserve instantiator type arguments (A1) and function type arguments (A2).
655 __ addiu(SP, SP, Immediate(-2 * kWordSize));
656 __ sw(A1, Address(SP, 1 * kWordSize));
657 __ sw(A2, Address(SP, 0 * kWordSize));
658
659 // A null object is always assignable and is returned as result.
660 Label is_assignable, runtime_call;
661
662 __ BranchEqual(A0, Object::null_object(), &is_assignable);
663
664 // Generate throw new TypeError() if the type is malformed or malbounded.
665 if (dst_type.IsMalformedOrMalbounded()) {
666 __ addiu(SP, SP, Immediate(-4 * kWordSize));
667 __ LoadObject(TMP, Object::null_object());
668 __ sw(TMP, Address(SP, 3 * kWordSize)); // Make room for the result.
669 __ sw(A0, Address(SP, 2 * kWordSize)); // Push the source object.
670 __ LoadObject(TMP, dst_name);
671 __ sw(TMP, Address(SP, 1 * kWordSize)); // Push the destination name.
672 __ LoadObject(TMP, dst_type);
673 __ sw(TMP, Address(SP, 0 * kWordSize)); // Push the destination type.
674
675 GenerateRuntimeCall(token_pos, deopt_id, kBadTypeErrorRuntimeEntry, 3,
676 locs);
677 // We should never return here.
678 __ break_(0);
679
680 __ Bind(&is_assignable); // For a null object.
681 __ lw(A1, Address(SP, 1 * kWordSize)); // Restore instantiator type args.
682 __ lw(A2, Address(SP, 0 * kWordSize)); // Restore function type args.
683 __ addiu(SP, SP, Immediate(2 * kWordSize));
684 return;
685 }
686
687 // Generate inline type check, linking to runtime call if not assignable.
688 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(zone());
689 test_cache = GenerateInlineInstanceof(token_pos, dst_type, &is_assignable,
690 &runtime_call);
691
692 __ Bind(&runtime_call);
693 __ lw(A1, Address(SP, 1 * kWordSize)); // Load instantiator type args.
694 __ lw(A2, Address(SP, 0 * kWordSize)); // Load function type args.
695
696 __ addiu(SP, SP, Immediate(-7 * kWordSize));
697 __ LoadObject(TMP, Object::null_object());
698 __ sw(TMP, Address(SP, 6 * kWordSize)); // Make room for the result.
699 __ sw(A0, Address(SP, 5 * kWordSize)); // Push the source object.
700 __ LoadObject(TMP, dst_type);
701 __ sw(TMP, Address(SP, 4 * kWordSize)); // Push the type of the destination.
702 __ sw(A1, Address(SP, 3 * kWordSize)); // Push instantiator type args.
703 __ sw(A2, Address(SP, 2 * kWordSize)); // Push function type args.
704 __ LoadObject(TMP, dst_name);
705 __ sw(TMP, Address(SP, 1 * kWordSize)); // Push the name of the destination.
706 __ LoadUniqueObject(T0, test_cache);
707 __ sw(T0, Address(SP, 0 * kWordSize));
708
709 GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs);
710 // Pop the parameters supplied to the runtime entry. The result of the
711 // type check runtime call is the checked value.
712 __ lw(A0, Address(SP, 6 * kWordSize));
713 __ addiu(SP, SP, Immediate(7 * kWordSize));
714
715 __ Bind(&is_assignable);
716 __ lw(A1, Address(SP, 1 * kWordSize)); // Restore instantiator type args.
717 __ lw(A2, Address(SP, 0 * kWordSize)); // Restore function type args.
718 __ addiu(SP, SP, Immediate(2 * kWordSize));
719 }
720
721
722 void FlowGraphCompiler::EmitInstructionEpilogue(Instruction* instr) {
723 if (is_optimizing()) return;
724 Definition* defn = instr->AsDefinition();
725 if ((defn != NULL) && defn->HasTemp()) {
726 __ Push(defn->locs()->out(0).reg());
727 }
728 }
729
730
731 // Input parameters:
732 // S4: arguments descriptor array.
733 void FlowGraphCompiler::CopyParameters() {
734 __ Comment("Copy parameters");
735 const Function& function = parsed_function().function();
736 LocalScope* scope = parsed_function().node_sequence()->scope();
737 const int num_fixed_params = function.num_fixed_parameters();
738 const int num_opt_pos_params = function.NumOptionalPositionalParameters();
739 const int num_opt_named_params = function.NumOptionalNamedParameters();
740 const int num_params =
741 num_fixed_params + num_opt_pos_params + num_opt_named_params;
742 ASSERT(function.NumParameters() == num_params);
743 ASSERT(parsed_function().first_parameter_index() == kFirstLocalSlotFromFp);
744
745 // Check that min_num_pos_args <= num_pos_args <= max_num_pos_args,
746 // where num_pos_args is the number of positional arguments passed in.
747 const int min_num_pos_args = num_fixed_params;
748 const int max_num_pos_args = num_fixed_params + num_opt_pos_params;
749
750 __ lw(T2, FieldAddress(S4, ArgumentsDescriptor::positional_count_offset()));
751 // Check that min_num_pos_args <= num_pos_args.
752 Label wrong_num_arguments;
753 __ BranchSignedLess(T2, Immediate(Smi::RawValue(min_num_pos_args)),
754 &wrong_num_arguments);
755
756 // Check that num_pos_args <= max_num_pos_args.
757 __ BranchSignedGreater(T2, Immediate(Smi::RawValue(max_num_pos_args)),
758 &wrong_num_arguments);
759
760 // Copy positional arguments.
761 // Argument i passed at fp[kParamEndSlotFromFp + num_args - i] is copied
762 // to fp[kFirstLocalSlotFromFp - i].
763
764 __ lw(T1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
765 // Since T1 and T2 are Smi, use sll 1 instead of sll 2.
766 // Let T1 point to the last passed positional argument, i.e. to
767 // fp[kParamEndSlotFromFp + num_args - (num_pos_args - 1)].
768 __ subu(T1, T1, T2);
769 __ sll(T1, T1, 1);
770 __ addu(T1, FP, T1);
771 __ AddImmediate(T1, (kParamEndSlotFromFp + 1) * kWordSize);
772
773 // Let T0 point to the last copied positional argument, i.e. to
774 // fp[kFirstLocalSlotFromFp - (num_pos_args - 1)].
775 __ AddImmediate(T0, FP, (kFirstLocalSlotFromFp + 1) * kWordSize);
776 __ sll(T2, T2, 1); // T2 is a Smi.
777
778 __ Comment("Argument Copy Loop");
779 Label loop, loop_exit;
780 __ blez(T2, &loop_exit);
781 __ delay_slot()->subu(T0, T0, T2);
782 __ Bind(&loop);
783 __ addu(T4, T1, T2);
784 __ lw(T3, Address(T4, -kWordSize));
785 __ addiu(T2, T2, Immediate(-kWordSize));
786 __ addu(T5, T0, T2);
787 __ bgtz(T2, &loop);
788 __ delay_slot()->sw(T3, Address(T5));
789 __ Bind(&loop_exit);
790
791 // Copy or initialize optional named arguments.
792 Label all_arguments_processed;
793 #ifdef DEBUG
794 const bool check_correct_named_args = true;
795 #else
796 const bool check_correct_named_args = function.IsClosureFunction();
797 #endif
798 if (num_opt_named_params > 0) {
799 __ Comment("There are named parameters");
800 // Start by alphabetically sorting the names of the optional parameters.
801 LocalVariable** opt_param = new LocalVariable*[num_opt_named_params];
802 int* opt_param_position = new int[num_opt_named_params];
803 for (int pos = num_fixed_params; pos < num_params; pos++) {
804 LocalVariable* parameter = scope->VariableAt(pos);
805 const String& opt_param_name = parameter->name();
806 int i = pos - num_fixed_params;
807 while (--i >= 0) {
808 LocalVariable* param_i = opt_param[i];
809 const intptr_t result = opt_param_name.CompareTo(param_i->name());
810 ASSERT(result != 0);
811 if (result > 0) break;
812 opt_param[i + 1] = opt_param[i];
813 opt_param_position[i + 1] = opt_param_position[i];
814 }
815 opt_param[i + 1] = parameter;
816 opt_param_position[i + 1] = pos;
817 }
818 // Generate code handling each optional parameter in alphabetical order.
819 __ lw(T1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
820 // Let T1 point to the first passed argument, i.e. to
821 // fp[kParamEndSlotFromFp + num_args - 0]; num_args (T1) is Smi.
822 __ sll(T3, T1, 1);
823 __ addu(T1, FP, T3);
824 __ AddImmediate(T1, kParamEndSlotFromFp * kWordSize);
825 // Let T0 point to the entry of the first named argument.
826 __ AddImmediate(T0, S4, ArgumentsDescriptor::first_named_entry_offset() -
827 kHeapObjectTag);
828 for (int i = 0; i < num_opt_named_params; i++) {
829 Label load_default_value, assign_optional_parameter;
830 const int param_pos = opt_param_position[i];
831 // Check if this named parameter was passed in.
832 // Load T3 with the name of the argument.
833 __ lw(T3, Address(T0, ArgumentsDescriptor::name_offset()));
834 ASSERT(opt_param[i]->name().IsSymbol());
835 __ BranchNotEqual(T3, opt_param[i]->name(), &load_default_value);
836
837 // Load T3 with passed-in argument at provided arg_pos, i.e. at
838 // fp[kParamEndSlotFromFp + num_args - arg_pos].
839 __ lw(T3, Address(T0, ArgumentsDescriptor::position_offset()));
840 // T3 is arg_pos as Smi.
841 // Point to next named entry.
842 __ AddImmediate(T0, ArgumentsDescriptor::named_entry_size());
843 __ subu(T3, ZR, T3);
844 __ sll(T3, T3, 1);
845 __ addu(T3, T1, T3);
846 __ b(&assign_optional_parameter);
847 __ delay_slot()->lw(T3, Address(T3));
848
849 __ Bind(&load_default_value);
850 // Load T3 with default argument.
851 const Instance& value = parsed_function().DefaultParameterValueAt(
852 param_pos - num_fixed_params);
853 __ LoadObject(T3, value);
854 __ Bind(&assign_optional_parameter);
855 // Assign T3 to fp[kFirstLocalSlotFromFp - param_pos].
856 // We do not use the final allocation index of the variable here, i.e.
857 // scope->VariableAt(i)->index(), because captured variables still need
858 // to be copied to the context that is not yet allocated.
859 const intptr_t computed_param_pos = kFirstLocalSlotFromFp - param_pos;
860 __ sw(T3, Address(FP, computed_param_pos * kWordSize));
861 }
862 delete[] opt_param;
863 delete[] opt_param_position;
864 if (check_correct_named_args) {
865 // Check that T0 now points to the null terminator in the arguments
866 // descriptor.
867 __ lw(T3, Address(T0));
868 __ BranchEqual(T3, Object::null_object(), &all_arguments_processed);
869 }
870 } else {
871 ASSERT(num_opt_pos_params > 0);
872 __ Comment("There are optional positional parameters");
873 __ lw(T2, FieldAddress(S4, ArgumentsDescriptor::positional_count_offset()));
874 __ SmiUntag(T2);
875 for (int i = 0; i < num_opt_pos_params; i++) {
876 Label next_parameter;
877 // Handle this optional positional parameter only if k or fewer positional
878 // arguments have been passed, where k is param_pos, the position of this
879 // optional parameter in the formal parameter list.
880 const int param_pos = num_fixed_params + i;
881 __ BranchSignedGreater(T2, Immediate(param_pos), &next_parameter);
882 // Load T3 with default argument.
883 const Object& value = parsed_function().DefaultParameterValueAt(i);
884 __ LoadObject(T3, value);
885 // Assign T3 to fp[kFirstLocalSlotFromFp - param_pos].
886 // We do not use the final allocation index of the variable here, i.e.
887 // scope->VariableAt(i)->index(), because captured variables still need
888 // to be copied to the context that is not yet allocated.
889 const intptr_t computed_param_pos = kFirstLocalSlotFromFp - param_pos;
890 __ sw(T3, Address(FP, computed_param_pos * kWordSize));
891 __ Bind(&next_parameter);
892 }
893 if (check_correct_named_args) {
894 __ lw(T1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
895 __ SmiUntag(T1);
896 // Check that T2 equals T1, i.e. no named arguments passed.
897 __ beq(T2, T1, &all_arguments_processed);
898 }
899 }
900
901 __ Bind(&wrong_num_arguments);
902 if (function.IsClosureFunction()) {
903 __ LeaveDartFrame(kKeepCalleePP); // Arguments are still on the stack.
904 __ Branch(*StubCode::CallClosureNoSuchMethod_entry());
905 // The noSuchMethod call may return to the caller, but not here.
906 } else if (check_correct_named_args) {
907 __ Stop("Wrong arguments");
908 }
909
910 __ Bind(&all_arguments_processed);
911 // Nullify originally passed arguments only after they have been copied and
912 // checked, otherwise noSuchMethod would not see their original values.
913 // This step can be skipped in case we decide that formal parameters are
914 // implicitly final, since garbage collecting the unmodified value is not
915 // an issue anymore.
916
917 // S4 : arguments descriptor array.
918 __ lw(T2, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
919 __ sll(T2, T2, 1); // T2 is a Smi.
920
921 __ Comment("Null arguments loop");
922 Label null_args_loop, null_args_loop_exit;
923 __ blez(T2, &null_args_loop_exit);
924 __ delay_slot()->addiu(T1, FP,
925 Immediate((kParamEndSlotFromFp + 1) * kWordSize));
926 __ Bind(&null_args_loop);
927 __ addiu(T2, T2, Immediate(-kWordSize));
928 __ addu(T3, T1, T2);
929 __ LoadObject(T5, Object::null_object());
930 __ bgtz(T2, &null_args_loop);
931 __ delay_slot()->sw(T5, Address(T3));
932 __ Bind(&null_args_loop_exit);
933 }
934
935
936 void FlowGraphCompiler::GenerateInlinedGetter(intptr_t offset) {
937 // RA: return address.
938 // SP: receiver.
939 // Sequence node has one return node, its input is load field node.
940 __ Comment("Inlined Getter");
941 __ lw(V0, Address(SP, 0 * kWordSize));
942 __ LoadFieldFromOffset(V0, V0, offset);
943 __ Ret();
944 }
945
946
947 void FlowGraphCompiler::GenerateInlinedSetter(intptr_t offset) {
948 // RA: return address.
949 // SP+1: receiver.
950 // SP+0: value.
951 // Sequence node has one store node and one return NULL node.
952 __ Comment("Inlined Setter");
953 __ lw(T0, Address(SP, 1 * kWordSize)); // Receiver.
954 __ lw(T1, Address(SP, 0 * kWordSize)); // Value.
955 __ StoreIntoObjectOffset(T0, offset, T1);
956 __ LoadObject(V0, Object::null_object());
957 __ Ret();
958 }
959
960
961 static const Register new_pp = T7;
962
963
964 void FlowGraphCompiler::EmitFrameEntry() {
965 const Function& function = parsed_function().function();
966 if (CanOptimizeFunction() && function.IsOptimizable() &&
967 (!is_optimizing() || may_reoptimize())) {
968 __ Comment("Invocation Count Check");
969 const Register function_reg = T0;
970
971 // Temporarily setup pool pointer for this dart function.
972 __ LoadPoolPointer(new_pp);
973 // Load function object from object pool.
974 __ LoadFunctionFromCalleePool(function_reg, function, new_pp);
975
976 __ lw(T1, FieldAddress(function_reg, Function::usage_counter_offset()));
977 // Reoptimization of an optimized function is triggered by counting in
978 // IC stubs, but not at the entry of the function.
979 if (!is_optimizing()) {
980 __ addiu(T1, T1, Immediate(1));
981 __ sw(T1, FieldAddress(function_reg, Function::usage_counter_offset()));
982 }
983
984 // Skip Branch if T1 is less than the threshold.
985 Label dont_branch;
986 __ BranchSignedLess(T1, Immediate(GetOptimizationThreshold()),
987 &dont_branch);
988
989 ASSERT(function_reg == T0);
990 __ Branch(*StubCode::OptimizeFunction_entry(), new_pp);
991
992 __ Bind(&dont_branch);
993 }
994 __ Comment("Enter frame");
995 if (flow_graph().IsCompiledForOsr()) {
996 intptr_t extra_slots = StackSize() - flow_graph().num_stack_locals() -
997 flow_graph().num_copied_params();
998 ASSERT(extra_slots >= 0);
999 __ EnterOsrFrame(extra_slots * kWordSize);
1000 } else {
1001 ASSERT(StackSize() >= 0);
1002 __ EnterDartFrame(StackSize() * kWordSize);
1003 }
1004 }
1005
1006
1007 // Input parameters:
1008 // RA: return address.
1009 // SP: address of last argument.
1010 // FP: caller's frame pointer.
1011 // PP: caller's pool pointer.
1012 // S5: ic-data.
1013 // S4: arguments descriptor array.
1014 void FlowGraphCompiler::CompileGraph() {
1015 InitCompiler();
1016 const Function& function = parsed_function().function();
1017
1018 #ifdef DART_PRECOMPILER
1019 if (function.IsDynamicFunction()) {
1020 __ MonomorphicCheckedEntry();
1021 }
1022 #endif // DART_PRECOMPILER
1023
1024 if (TryIntrinsify()) {
1025 // Skip regular code generation.
1026 return;
1027 }
1028
1029 EmitFrameEntry();
1030 ASSERT(assembler()->constant_pool_allowed());
1031
1032 const int num_fixed_params = function.num_fixed_parameters();
1033 const int num_copied_params = parsed_function().num_copied_params();
1034 const int num_locals = parsed_function().num_stack_locals();
1035
1036 // We check the number of passed arguments when we have to copy them due to
1037 // the presence of optional parameters.
1038 // No such checking code is generated if only fixed parameters are declared,
1039 // unless we are in debug mode or unless we are compiling a closure.
1040 if (num_copied_params == 0) {
1041 const bool check_arguments =
1042 function.IsClosureFunction() && !flow_graph().IsCompiledForOsr();
1043 if (check_arguments) {
1044 __ Comment("Check argument count");
1045 // Check that exactly num_fixed arguments are passed in.
1046 Label correct_num_arguments, wrong_num_arguments;
1047 __ lw(T0, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
1048 __ BranchNotEqual(T0, Immediate(Smi::RawValue(num_fixed_params)),
1049 &wrong_num_arguments);
1050
1051 __ lw(T1,
1052 FieldAddress(S4, ArgumentsDescriptor::positional_count_offset()));
1053 __ beq(T0, T1, &correct_num_arguments);
1054 __ Bind(&wrong_num_arguments);
1055 __ LeaveDartFrame(kKeepCalleePP); // Arguments are still on the stack.
1056 __ Branch(*StubCode::CallClosureNoSuchMethod_entry());
1057 // The noSuchMethod call may return to the caller, but not here.
1058 __ Bind(&correct_num_arguments);
1059 }
1060 } else if (!flow_graph().IsCompiledForOsr()) {
1061 CopyParameters();
1062 }
1063
1064 if (function.IsClosureFunction() && !flow_graph().IsCompiledForOsr()) {
1065 // Load context from the closure object (first argument).
1066 LocalScope* scope = parsed_function().node_sequence()->scope();
1067 LocalVariable* closure_parameter = scope->VariableAt(0);
1068 __ lw(CTX, Address(FP, closure_parameter->index() * kWordSize));
1069 __ lw(CTX, FieldAddress(CTX, Closure::context_offset()));
1070 }
1071
1072 // In unoptimized code, initialize (non-argument) stack allocated slots to
1073 // null.
1074 if (!is_optimizing()) {
1075 ASSERT(num_locals > 0); // There is always at least context_var.
1076 __ Comment("Initialize spill slots");
1077 const intptr_t slot_base = parsed_function().first_stack_local_index();
1078 const intptr_t context_index =
1079 parsed_function().current_context_var()->index();
1080 if (num_locals > 1) {
1081 __ LoadObject(V0, Object::null_object());
1082 }
1083 for (intptr_t i = 0; i < num_locals; ++i) {
1084 // Subtract index i (locals lie at lower addresses than FP).
1085 if (((slot_base - i) == context_index)) {
1086 if (function.IsClosureFunction()) {
1087 __ sw(CTX, Address(FP, (slot_base - i) * kWordSize));
1088 } else {
1089 __ LoadObject(V1, Object::empty_context());
1090 __ sw(V1, Address(FP, (slot_base - i) * kWordSize));
1091 }
1092 } else {
1093 ASSERT(num_locals > 1);
1094 __ sw(V0, Address(FP, (slot_base - i) * kWordSize));
1095 }
1096 }
1097 }
1098
1099 // Check for a passed type argument vector if the function is generic.
1100 if (FLAG_reify_generic_functions && function.IsGeneric()) {
1101 __ Comment("Check passed-in type args");
1102 Label store_type_args, ok;
1103 __ lw(T1, FieldAddress(S4, ArgumentsDescriptor::type_args_len_offset()));
1104 if (is_optimizing()) {
1105 // Initialize type_args to null if none passed in.
1106 __ LoadObject(T0, Object::null_object());
1107 __ BranchEqual(T1, Immediate(0), &store_type_args);
1108 } else {
1109 __ BranchEqual(T1, Immediate(0), &ok); // Already initialized to null.
1110 }
1111 // TODO(regis): Verify that type_args_len is correct.
1112 // Load the passed type args vector in T0 from
1113 // fp[kParamEndSlotFromFp + num_args + 1]; num_args (T1) is Smi.
1114 __ lw(T1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
1115 __ sll(T1, T1, 1);
1116 __ addu(T1, FP, T1);
1117 __ lw(T0, Address(T1, (kParamEndSlotFromFp + 1) * kWordSize));
1118 // Store T0 into the stack slot reserved for the function type arguments.
1119 // If the function type arguments variable is captured, a copy will happen
1120 // after the context is allocated.
1121 const intptr_t slot_base = parsed_function().first_stack_local_index();
1122 ASSERT(parsed_function().function_type_arguments()->is_captured() ||
1123 parsed_function().function_type_arguments()->index() == slot_base);
1124 __ Bind(&store_type_args);
1125 __ sw(T0, Address(FP, slot_base * kWordSize));
1126 __ Bind(&ok);
1127 }
1128
1129 // TODO(regis): Verify that no vector is passed if not generic, unless already
1130 // checked during resolution.
1131
1132 EndCodeSourceRange(TokenPosition::kDartCodePrologue);
1133 VisitBlocks();
1134
1135 __ break_(0);
1136 GenerateDeferredCode();
1137 }
1138
1139
1140 void FlowGraphCompiler::GenerateCall(TokenPosition token_pos,
1141 const StubEntry& stub_entry,
1142 RawPcDescriptors::Kind kind,
1143 LocationSummary* locs) {
1144 __ BranchLink(stub_entry);
1145 EmitCallsiteMetaData(token_pos, Thread::kNoDeoptId, kind, locs);
1146 }
1147
1148
1149 void FlowGraphCompiler::GeneratePatchableCall(TokenPosition token_pos,
1150 const StubEntry& stub_entry,
1151 RawPcDescriptors::Kind kind,
1152 LocationSummary* locs) {
1153 __ BranchLinkPatchable(stub_entry);
1154 EmitCallsiteMetaData(token_pos, Thread::kNoDeoptId, kind, locs);
1155 }
1156
1157
1158 void FlowGraphCompiler::GenerateDartCall(intptr_t deopt_id,
1159 TokenPosition token_pos,
1160 const StubEntry& stub_entry,
1161 RawPcDescriptors::Kind kind,
1162 LocationSummary* locs) {
1163 __ BranchLinkPatchable(stub_entry);
1164 EmitCallsiteMetaData(token_pos, deopt_id, kind, locs);
1165 // Marks either the continuation point in unoptimized code or the
1166 // deoptimization point in optimized code, after call.
1167 const intptr_t deopt_id_after = Thread::ToDeoptAfter(deopt_id);
1168 if (is_optimizing()) {
1169 AddDeoptIndexAtCall(deopt_id_after);
1170 } else {
1171 // Add deoptimization continuation point after the call and before the
1172 // arguments are removed.
1173 AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after, token_pos);
1174 }
1175 }
1176
1177
1178 void FlowGraphCompiler::GenerateStaticDartCall(intptr_t deopt_id,
1179 TokenPosition token_pos,
1180 const StubEntry& stub_entry,
1181 RawPcDescriptors::Kind kind,
1182 LocationSummary* locs,
1183 const Function& target) {
1184 // Call sites to the same target can share object pool entries. These
1185 // call sites are never patched for breakpoints: the function is deoptimized
1186 // and the unoptimized code with IC calls for static calls is patched instead.
1187 ASSERT(is_optimizing());
1188 __ BranchLinkWithEquivalence(stub_entry, target);
1189
1190 EmitCallsiteMetaData(token_pos, deopt_id, kind, locs);
1191 // Marks either the continuation point in unoptimized code or the
1192 // deoptimization point in optimized code, after call.
1193 const intptr_t deopt_id_after = Thread::ToDeoptAfter(deopt_id);
1194 if (is_optimizing()) {
1195 AddDeoptIndexAtCall(deopt_id_after);
1196 } else {
1197 // Add deoptimization continuation point after the call and before the
1198 // arguments are removed.
1199 AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after, token_pos);
1200 }
1201 AddStaticCallTarget(target);
1202 }
1203
1204
1205 void FlowGraphCompiler::GenerateRuntimeCall(TokenPosition token_pos,
1206 intptr_t deopt_id,
1207 const RuntimeEntry& entry,
1208 intptr_t argument_count,
1209 LocationSummary* locs) {
1210 __ CallRuntime(entry, argument_count);
1211 EmitCallsiteMetaData(token_pos, deopt_id, RawPcDescriptors::kOther, locs);
1212 if (deopt_id != Thread::kNoDeoptId) {
1213 // Marks either the continuation point in unoptimized code or the
1214 // deoptimization point in optimized code, after call.
1215 const intptr_t deopt_id_after = Thread::ToDeoptAfter(deopt_id);
1216 if (is_optimizing()) {
1217 AddDeoptIndexAtCall(deopt_id_after);
1218 } else {
1219 // Add deoptimization continuation point after the call and before the
1220 // arguments are removed.
1221 AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after, token_pos);
1222 }
1223 }
1224 }
1225
1226
1227 void FlowGraphCompiler::EmitEdgeCounter(intptr_t edge_id) {
1228 // We do not check for overflow when incrementing the edge counter. The
1229 // function should normally be optimized long before the counter can
1230 // overflow; and though we do not reset the counters when we optimize or
1231 // deoptimize, there is a bound on the number of
1232 // optimization/deoptimization cycles we will attempt.
1233 ASSERT(!edge_counters_array_.IsNull());
1234 __ Comment("Edge counter");
1235 __ LoadObject(T0, edge_counters_array_);
1236 __ LoadFieldFromOffset(T1, T0, Array::element_offset(edge_id));
1237 __ AddImmediate(T1, T1, Smi::RawValue(1));
1238 __ StoreFieldToOffset(T1, T0, Array::element_offset(edge_id));
1239 }
1240
1241
1242 void FlowGraphCompiler::EmitOptimizedInstanceCall(const StubEntry& stub_entry,
1243 const ICData& ic_data,
1244 intptr_t argument_count,
1245 intptr_t deopt_id,
1246 TokenPosition token_pos,
1247 LocationSummary* locs) {
1248 ASSERT(Array::Handle(zone(), ic_data.arguments_descriptor()).Length() > 0);
1249 // Each ICData propagated from unoptimized to optimized code contains the
1250 // function that corresponds to the Dart function of that IC call. Due
1251 // to inlining in optimized code, that function may not correspond to the
1252 // top-level function (parsed_function().function()) which could be
1253 // reoptimized and which counter needs to be incremented.
1254 // Pass the function explicitly, it is used in IC stub.
1255 __ Comment("OptimizedInstanceCall");
1256 __ LoadObject(T0, parsed_function().function());
1257 __ LoadUniqueObject(S5, ic_data);
1258 GenerateDartCall(deopt_id, token_pos, stub_entry, RawPcDescriptors::kIcCall,
1259 locs);
1260 __ Drop(argument_count);
1261 }
1262
1263
1264 void FlowGraphCompiler::EmitInstanceCall(const StubEntry& stub_entry,
1265 const ICData& ic_data,
1266 intptr_t argument_count,
1267 intptr_t deopt_id,
1268 TokenPosition token_pos,
1269 LocationSummary* locs) {
1270 ASSERT(Array::Handle(zone(), ic_data.arguments_descriptor()).Length() > 0);
1271 __ Comment("InstanceCall");
1272 __ LoadUniqueObject(S5, ic_data);
1273 GenerateDartCall(deopt_id, token_pos, stub_entry, RawPcDescriptors::kIcCall,
1274 locs);
1275 __ Comment("InstanceCall return");
1276 __ Drop(argument_count);
1277 }
1278
1279
1280 void FlowGraphCompiler::EmitMegamorphicInstanceCall(
1281 const String& name,
1282 const Array& arguments_descriptor,
1283 intptr_t argument_count,
1284 intptr_t deopt_id,
1285 TokenPosition token_pos,
1286 LocationSummary* locs,
1287 intptr_t try_index,
1288 intptr_t slow_path_argument_count) {
1289 ASSERT(!arguments_descriptor.IsNull() && (arguments_descriptor.Length() > 0));
1290 const MegamorphicCache& cache = MegamorphicCache::ZoneHandle(
1291 zone(),
1292 MegamorphicCacheTable::Lookup(isolate(), name, arguments_descriptor));
1293
1294 __ Comment("MegamorphicCall");
1295 // Load receiver into T0,
1296 __ lw(T0, Address(SP, (argument_count - 1) * kWordSize));
1297 __ LoadObject(S5, cache);
1298 __ lw(T9, Address(THR, Thread::megamorphic_call_checked_entry_offset()));
1299 __ jalr(T9);
1300
1301 RecordSafepoint(locs, slow_path_argument_count);
1302 const intptr_t deopt_id_after = Thread::ToDeoptAfter(deopt_id);
1303 if (FLAG_precompiled_mode) {
1304 // Megamorphic calls may occur in slow path stubs.
1305 // If valid use try_index argument.
1306 if (try_index == CatchClauseNode::kInvalidTryIndex) {
1307 try_index = CurrentTryIndex();
1308 }
1309 AddDescriptor(RawPcDescriptors::kOther, assembler()->CodeSize(),
1310 Thread::kNoDeoptId, token_pos, try_index);
1311 } else if (is_optimizing()) {
1312 AddCurrentDescriptor(RawPcDescriptors::kOther, Thread::kNoDeoptId,
1313 token_pos);
1314 AddDeoptIndexAtCall(deopt_id_after);
1315 } else {
1316 AddCurrentDescriptor(RawPcDescriptors::kOther, Thread::kNoDeoptId,
1317 token_pos);
1318 // Add deoptimization continuation point after the call and before the
1319 // arguments are removed.
1320 AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after, token_pos);
1321 }
1322 EmitCatchEntryState(pending_deoptimization_env_, try_index);
1323 __ Drop(argument_count);
1324 }
1325
1326
1327 void FlowGraphCompiler::EmitSwitchableInstanceCall(const ICData& ic_data,
1328 intptr_t argument_count,
1329 intptr_t deopt_id,
1330 TokenPosition token_pos,
1331 LocationSummary* locs) {
1332 ASSERT(ic_data.NumArgsTested() == 1);
1333 const Code& initial_stub =
1334 Code::ZoneHandle(StubCode::ICCallThroughFunction_entry()->code());
1335
1336 __ Comment("SwitchableCall");
1337 __ lw(T0, Address(SP, (argument_count - 1) * kWordSize));
1338 __ LoadUniqueObject(CODE_REG, initial_stub);
1339 __ lw(T9, FieldAddress(CODE_REG, Code::checked_entry_point_offset()));
1340 __ LoadUniqueObject(S5, ic_data);
1341 __ jalr(T9);
1342
1343 EmitCallsiteMetaData(token_pos, Thread::kNoDeoptId, RawPcDescriptors::kOther,
1344 locs);
1345 const intptr_t deopt_id_after = Thread::ToDeoptAfter(deopt_id);
1346 if (is_optimizing()) {
1347 AddDeoptIndexAtCall(deopt_id_after);
1348 } else {
1349 // Add deoptimization continuation point after the call and before the
1350 // arguments are removed.
1351 AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after, token_pos);
1352 }
1353 __ Drop(argument_count);
1354 }
1355
1356
1357 void FlowGraphCompiler::EmitUnoptimizedStaticCall(intptr_t argument_count,
1358 intptr_t deopt_id,
1359 TokenPosition token_pos,
1360 LocationSummary* locs,
1361 const ICData& ic_data) {
1362 const StubEntry* stub_entry =
1363 StubCode::UnoptimizedStaticCallEntry(ic_data.NumArgsTested());
1364 __ LoadObject(S5, ic_data);
1365 GenerateDartCall(deopt_id, token_pos, *stub_entry,
1366 RawPcDescriptors::kUnoptStaticCall, locs);
1367 __ Drop(argument_count);
1368 }
1369
1370
1371 void FlowGraphCompiler::EmitOptimizedStaticCall(
1372 const Function& function,
1373 const Array& arguments_descriptor,
1374 intptr_t argument_count,
1375 intptr_t deopt_id,
1376 TokenPosition token_pos,
1377 LocationSummary* locs) {
1378 __ Comment("StaticCall");
1379 ASSERT(!function.IsClosureFunction());
1380 if (function.HasOptionalParameters() ||
1381 (FLAG_reify_generic_functions && function.IsGeneric())) {
1382 __ LoadObject(S4, arguments_descriptor);
1383 } else {
1384 __ LoadImmediate(S4, 0); // GC safe smi zero because of stub.
1385 }
1386 // Do not use the code from the function, but let the code be patched so that
1387 // we can record the outgoing edges to other code.
1388 GenerateStaticDartCall(deopt_id, token_pos,
1389 *StubCode::CallStaticFunction_entry(),
1390 RawPcDescriptors::kOther, locs, function);
1391 __ Drop(argument_count);
1392 }
1393
1394
1395 Condition FlowGraphCompiler::EmitEqualityRegConstCompare(
1396 Register reg,
1397 const Object& obj,
1398 bool needs_number_check,
1399 TokenPosition token_pos,
1400 intptr_t deopt_id) {
1401 __ Comment("EqualityRegConstCompare");
1402 ASSERT(!needs_number_check ||
1403 (!obj.IsMint() && !obj.IsDouble() && !obj.IsBigint()));
1404 if (needs_number_check) {
1405 ASSERT(!obj.IsMint() && !obj.IsDouble() && !obj.IsBigint());
1406 __ addiu(SP, SP, Immediate(-2 * kWordSize));
1407 __ sw(reg, Address(SP, 1 * kWordSize));
1408 __ LoadObject(TMP, obj);
1409 __ sw(TMP, Address(SP, 0 * kWordSize));
1410 if (is_optimizing()) {
1411 __ BranchLinkPatchable(
1412 *StubCode::OptimizedIdenticalWithNumberCheck_entry());
1413 } else {
1414 __ BranchLinkPatchable(
1415 *StubCode::UnoptimizedIdenticalWithNumberCheck_entry());
1416 }
1417 AddCurrentDescriptor(RawPcDescriptors::kRuntimeCall, deopt_id, token_pos);
1418 __ Comment("EqualityRegConstCompare return");
1419 // Stub returns result in CMPRES1 (if it is 0, then reg and obj are equal).
1420 __ lw(reg, Address(SP, 1 * kWordSize)); // Restore 'reg'.
1421 __ addiu(SP, SP, Immediate(2 * kWordSize)); // Discard constant.
1422 return Condition(CMPRES1, ZR, EQ);
1423 } else {
1424 int16_t imm = 0;
1425 const Register obj_reg = __ LoadConditionOperand(CMPRES1, obj, &imm);
1426 return Condition(reg, obj_reg, EQ, imm);
1427 }
1428 }
1429
1430
1431 Condition FlowGraphCompiler::EmitEqualityRegRegCompare(Register left,
1432 Register right,
1433 bool needs_number_check,
1434 TokenPosition token_pos,
1435 intptr_t deopt_id) {
1436 __ Comment("EqualityRegRegCompare");
1437 if (needs_number_check) {
1438 __ addiu(SP, SP, Immediate(-2 * kWordSize));
1439 __ sw(left, Address(SP, 1 * kWordSize));
1440 __ sw(right, Address(SP, 0 * kWordSize));
1441 if (is_optimizing()) {
1442 __ BranchLinkPatchable(
1443 *StubCode::OptimizedIdenticalWithNumberCheck_entry());
1444 } else {
1445 __ BranchLinkPatchable(
1446 *StubCode::UnoptimizedIdenticalWithNumberCheck_entry());
1447 }
1448 if (token_pos.IsReal()) {
1449 AddCurrentDescriptor(RawPcDescriptors::kRuntimeCall, Thread::kNoDeoptId,
1450 token_pos);
1451 }
1452 __ Comment("EqualityRegRegCompare return");
1453 // Stub returns result in CMPRES1 (if it is 0, then left and right are
1454 // equal).
1455 __ lw(right, Address(SP, 0 * kWordSize));
1456 __ lw(left, Address(SP, 1 * kWordSize));
1457 __ addiu(SP, SP, Immediate(2 * kWordSize));
1458 return Condition(CMPRES1, ZR, EQ);
1459 } else {
1460 return Condition(left, right, EQ);
1461 }
1462 }
1463
1464
1465 // This function must be in sync with FlowGraphCompiler::RecordSafepoint and
1466 // FlowGraphCompiler::SlowPathEnvironmentFor.
1467 void FlowGraphCompiler::SaveLiveRegisters(LocationSummary* locs) {
1468 #if defined(DEBUG)
1469 locs->CheckWritableInputs();
1470 ClobberDeadTempRegisters(locs);
1471 #endif
1472
1473 __ Comment("SaveLiveRegisters");
1474 // TODO(vegorov): consider saving only caller save (volatile) registers.
1475 const intptr_t fpu_regs_count = locs->live_registers()->FpuRegisterCount();
1476 if (fpu_regs_count > 0) {
1477 __ AddImmediate(SP, -(fpu_regs_count * kFpuRegisterSize));
1478 // Store fpu registers with the lowest register number at the lowest
1479 // address.
1480 intptr_t offset = 0;
1481 for (intptr_t i = 0; i < kNumberOfFpuRegisters; ++i) {
1482 DRegister fpu_reg = static_cast<DRegister>(i);
1483 if (locs->live_registers()->ContainsFpuRegister(fpu_reg)) {
1484 __ StoreDToOffset(fpu_reg, SP, offset);
1485 offset += kFpuRegisterSize;
1486 }
1487 }
1488 ASSERT(offset == (fpu_regs_count * kFpuRegisterSize));
1489 }
1490
1491 // The order in which the registers are pushed must match the order
1492 // in which the registers are encoded in the safe point's stack map.
1493 const intptr_t cpu_registers = locs->live_registers()->cpu_registers();
1494 ASSERT((cpu_registers & ~kAllCpuRegistersList) == 0);
1495 const int register_count = Utils::CountOneBits(cpu_registers);
1496 if (register_count > 0) {
1497 __ addiu(SP, SP, Immediate(-register_count * kWordSize));
1498 intptr_t offset = register_count * kWordSize;
1499 for (int i = kNumberOfCpuRegisters - 1; i >= 0; --i) {
1500 Register r = static_cast<Register>(i);
1501 if (locs->live_registers()->ContainsRegister(r)) {
1502 offset -= kWordSize;
1503 __ sw(r, Address(SP, offset));
1504 }
1505 }
1506 ASSERT(offset == 0);
1507 }
1508 }
1509
1510
1511 void FlowGraphCompiler::RestoreLiveRegisters(LocationSummary* locs) {
1512 __ Comment("RestoreLiveRegisters");
1513 const intptr_t cpu_registers = locs->live_registers()->cpu_registers();
1514 ASSERT((cpu_registers & ~kAllCpuRegistersList) == 0);
1515 const int register_count = Utils::CountOneBits(cpu_registers);
1516 if (register_count > 0) {
1517 intptr_t offset = register_count * kWordSize;
1518 for (int i = kNumberOfCpuRegisters - 1; i >= 0; --i) {
1519 Register r = static_cast<Register>(i);
1520 if (locs->live_registers()->ContainsRegister(r)) {
1521 offset -= kWordSize;
1522 __ lw(r, Address(SP, offset));
1523 }
1524 }
1525 ASSERT(offset == 0);
1526 __ addiu(SP, SP, Immediate(register_count * kWordSize));
1527 }
1528
1529 const intptr_t fpu_regs_count = locs->live_registers()->FpuRegisterCount();
1530 if (fpu_regs_count > 0) {
1531 // Fpu registers have the lowest register number at the lowest address.
1532 intptr_t offset = 0;
1533 for (intptr_t i = 0; i < kNumberOfFpuRegisters; ++i) {
1534 DRegister fpu_reg = static_cast<DRegister>(i);
1535 if (locs->live_registers()->ContainsFpuRegister(fpu_reg)) {
1536 __ LoadDFromOffset(fpu_reg, SP, offset);
1537 offset += kFpuRegisterSize;
1538 }
1539 }
1540 ASSERT(offset == (fpu_regs_count * kFpuRegisterSize));
1541 __ AddImmediate(SP, offset);
1542 }
1543 }
1544
1545
1546 #if defined(DEBUG)
1547 void FlowGraphCompiler::ClobberDeadTempRegisters(LocationSummary* locs) {
1548 // Clobber temporaries that have not been manually preserved.
1549 for (intptr_t i = 0; i < locs->temp_count(); ++i) {
1550 Location tmp = locs->temp(i);
1551 // TODO(zerny): clobber non-live temporary FPU registers.
1552 if (tmp.IsRegister() &&
1553 !locs->live_registers()->ContainsRegister(tmp.reg())) {
1554 __ LoadImmediate(tmp.reg(), 0xf7);
1555 }
1556 }
1557 }
1558 #endif
1559
1560
1561 void FlowGraphCompiler::EmitTestAndCallLoadReceiver(
1562 intptr_t argument_count,
1563 const Array& arguments_descriptor) {
1564 __ Comment("EmitTestAndCall");
1565 // Load receiver into T0.
1566 __ LoadFromOffset(T0, SP, (argument_count - 1) * kWordSize);
1567 __ LoadObject(S4, arguments_descriptor);
1568 }
1569
1570
1571 void FlowGraphCompiler::EmitTestAndCallSmiBranch(Label* label, bool if_smi) {
1572 __ andi(CMPRES1, T0, Immediate(kSmiTagMask));
1573 if (if_smi) {
1574 // Jump if receiver is Smi.
1575 __ beq(CMPRES1, ZR, label);
1576 } else {
1577 // Jump if receiver is not Smi.
1578 __ bne(CMPRES1, ZR, label);
1579 }
1580 }
1581
1582
1583 void FlowGraphCompiler::EmitTestAndCallLoadCid() {
1584 __ LoadClassId(T2, T0);
1585 }
1586
1587
1588 int FlowGraphCompiler::EmitTestAndCallCheckCid(Label* next_label,
1589 const CidRange& range,
1590 int bias) {
1591 intptr_t cid_start = range.cid_start;
1592 if (range.IsSingleCid()) {
1593 __ BranchNotEqual(T2, Immediate(cid_start - bias), next_label);
1594 } else {
1595 __ AddImmediate(T2, T2, bias - cid_start);
1596 bias = cid_start;
1597 // TODO(erikcorry): We should use sltiu instead of the temporary TMP if
1598 // the range is small enough.
1599 __ LoadImmediate(TMP, range.Extent());
1600 // Reverse comparison so we get 1 if biased cid > tmp ie cid is out of
1601 // range.
1602 __ sltu(TMP, TMP, T2);
1603 __ bne(TMP, ZR, next_label);
1604 }
1605 return bias;
1606 }
1607
1608
1609 #undef __
1610 #define __ compiler_->assembler()->
1611
1612
1613 void ParallelMoveResolver::EmitMove(int index) {
1614 MoveOperands* move = moves_[index];
1615 const Location source = move->src();
1616 const Location destination = move->dest();
1617 __ Comment("ParallelMoveResolver::EmitMove");
1618
1619 if (source.IsRegister()) {
1620 if (destination.IsRegister()) {
1621 __ mov(destination.reg(), source.reg());
1622 } else {
1623 ASSERT(destination.IsStackSlot());
1624 const intptr_t dest_offset = destination.ToStackSlotOffset();
1625 __ StoreToOffset(source.reg(), destination.base_reg(), dest_offset);
1626 }
1627 } else if (source.IsStackSlot()) {
1628 if (destination.IsRegister()) {
1629 const intptr_t source_offset = source.ToStackSlotOffset();
1630 __ LoadFromOffset(destination.reg(), source.base_reg(), source_offset);
1631 } else {
1632 ASSERT(destination.IsStackSlot());
1633 const intptr_t source_offset = source.ToStackSlotOffset();
1634 const intptr_t dest_offset = destination.ToStackSlotOffset();
1635 ScratchRegisterScope tmp(this, kNoRegister);
1636 __ LoadFromOffset(tmp.reg(), source.base_reg(), source_offset);
1637 __ StoreToOffset(tmp.reg(), destination.base_reg(), dest_offset);
1638 }
1639 } else if (source.IsFpuRegister()) {
1640 if (destination.IsFpuRegister()) {
1641 DRegister dst = destination.fpu_reg();
1642 DRegister src = source.fpu_reg();
1643 __ movd(dst, src);
1644 } else {
1645 ASSERT(destination.IsDoubleStackSlot());
1646 const intptr_t dest_offset = destination.ToStackSlotOffset();
1647 DRegister src = source.fpu_reg();
1648 __ StoreDToOffset(src, destination.base_reg(), dest_offset);
1649 }
1650 } else if (source.IsDoubleStackSlot()) {
1651 if (destination.IsFpuRegister()) {
1652 const intptr_t source_offset = source.ToStackSlotOffset();
1653 DRegister dst = destination.fpu_reg();
1654 __ LoadDFromOffset(dst, source.base_reg(), source_offset);
1655 } else {
1656 ASSERT(destination.IsDoubleStackSlot());
1657 const intptr_t source_offset = source.ToStackSlotOffset();
1658 const intptr_t dest_offset = destination.ToStackSlotOffset();
1659 __ LoadDFromOffset(DTMP, source.base_reg(), source_offset);
1660 __ StoreDToOffset(DTMP, destination.base_reg(), dest_offset);
1661 }
1662 } else {
1663 ASSERT(source.IsConstant());
1664 const Object& constant = source.constant();
1665 if (destination.IsRegister()) {
1666 if (constant.IsSmi() &&
1667 (source.constant_instruction()->representation() == kUnboxedInt32)) {
1668 __ LoadImmediate(destination.reg(), Smi::Cast(constant).Value());
1669 } else {
1670 __ LoadObject(destination.reg(), constant);
1671 }
1672 } else if (destination.IsFpuRegister()) {
1673 __ LoadObject(TMP, constant);
1674 __ LoadDFromOffset(destination.fpu_reg(), TMP,
1675 Double::value_offset() - kHeapObjectTag);
1676 } else if (destination.IsDoubleStackSlot()) {
1677 const intptr_t dest_offset = destination.ToStackSlotOffset();
1678 __ LoadObject(TMP, constant);
1679 __ LoadDFromOffset(DTMP, TMP, Double::value_offset() - kHeapObjectTag);
1680 __ StoreDToOffset(DTMP, destination.base_reg(), dest_offset);
1681 } else {
1682 ASSERT(destination.IsStackSlot());
1683 const intptr_t dest_offset = destination.ToStackSlotOffset();
1684 ScratchRegisterScope tmp(this, kNoRegister);
1685 if (constant.IsSmi() &&
1686 (source.constant_instruction()->representation() == kUnboxedInt32)) {
1687 __ LoadImmediate(tmp.reg(), Smi::Cast(constant).Value());
1688 } else {
1689 __ LoadObject(tmp.reg(), constant);
1690 }
1691 __ StoreToOffset(tmp.reg(), destination.base_reg(), dest_offset);
1692 }
1693 }
1694
1695 move->Eliminate();
1696 }
1697
1698
1699 void ParallelMoveResolver::EmitSwap(int index) {
1700 MoveOperands* move = moves_[index];
1701 const Location source = move->src();
1702 const Location destination = move->dest();
1703
1704 if (source.IsRegister() && destination.IsRegister()) {
1705 ASSERT(source.reg() != TMP);
1706 ASSERT(destination.reg() != TMP);
1707 __ mov(TMP, source.reg());
1708 __ mov(source.reg(), destination.reg());
1709 __ mov(destination.reg(), TMP);
1710 } else if (source.IsRegister() && destination.IsStackSlot()) {
1711 Exchange(source.reg(), destination.base_reg(),
1712 destination.ToStackSlotOffset());
1713 } else if (source.IsStackSlot() && destination.IsRegister()) {
1714 Exchange(destination.reg(), source.base_reg(), source.ToStackSlotOffset());
1715 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
1716 Exchange(source.base_reg(), source.ToStackSlotOffset(),
1717 destination.base_reg(), destination.ToStackSlotOffset());
1718 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
1719 DRegister dst = destination.fpu_reg();
1720 DRegister src = source.fpu_reg();
1721 __ movd(DTMP, src);
1722 __ movd(src, dst);
1723 __ movd(dst, DTMP);
1724 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
1725 ASSERT(destination.IsDoubleStackSlot() || source.IsDoubleStackSlot());
1726 DRegister reg =
1727 source.IsFpuRegister() ? source.fpu_reg() : destination.fpu_reg();
1728 Register base_reg =
1729 source.IsFpuRegister() ? destination.base_reg() : source.base_reg();
1730 const intptr_t slot_offset = source.IsFpuRegister()
1731 ? destination.ToStackSlotOffset()
1732 : source.ToStackSlotOffset();
1733 __ LoadDFromOffset(DTMP, base_reg, slot_offset);
1734 __ StoreDToOffset(reg, base_reg, slot_offset);
1735 __ movd(reg, DTMP);
1736 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
1737 const intptr_t source_offset = source.ToStackSlotOffset();
1738 const intptr_t dest_offset = destination.ToStackSlotOffset();
1739
1740 ScratchFpuRegisterScope ensure_scratch(this, DTMP);
1741 DRegister scratch = ensure_scratch.reg();
1742 __ LoadDFromOffset(DTMP, source.base_reg(), source_offset);
1743 __ LoadDFromOffset(scratch, destination.base_reg(), dest_offset);
1744 __ StoreDToOffset(DTMP, destination.base_reg(), dest_offset);
1745 __ StoreDToOffset(scratch, source.base_reg(), source_offset);
1746 } else {
1747 UNREACHABLE();
1748 }
1749
1750 // The swap of source and destination has executed a move from source to
1751 // destination.
1752 move->Eliminate();
1753
1754 // Any unperformed (including pending) move with a source of either
1755 // this move's source or destination needs to have their source
1756 // changed to reflect the state of affairs after the swap.
1757 for (int i = 0; i < moves_.length(); ++i) {
1758 const MoveOperands& other_move = *moves_[i];
1759 if (other_move.Blocks(source)) {
1760 moves_[i]->set_src(destination);
1761 } else if (other_move.Blocks(destination)) {
1762 moves_[i]->set_src(source);
1763 }
1764 }
1765 }
1766
1767
1768 void ParallelMoveResolver::MoveMemoryToMemory(const Address& dst,
1769 const Address& src) {
1770 __ Comment("ParallelMoveResolver::MoveMemoryToMemory");
1771 __ lw(TMP, src);
1772 __ sw(TMP, dst);
1773 }
1774
1775
1776 void ParallelMoveResolver::StoreObject(const Address& dst, const Object& obj) {
1777 __ Comment("ParallelMoveResolver::StoreObject");
1778 __ LoadObject(TMP, obj);
1779 __ sw(TMP, dst);
1780 }
1781
1782
1783 // Do not call or implement this function. Instead, use the form below that
1784 // uses an offset from the frame pointer instead of an Address.
1785 void ParallelMoveResolver::Exchange(Register reg, const Address& mem) {
1786 UNREACHABLE();
1787 }
1788
1789
1790 // Do not call or implement this function. Instead, use the form below that
1791 // uses offsets from the frame pointer instead of Addresses.
1792 void ParallelMoveResolver::Exchange(const Address& mem1, const Address& mem2) {
1793 UNREACHABLE();
1794 }
1795
1796
1797 void ParallelMoveResolver::Exchange(Register reg,
1798 Register base_reg,
1799 intptr_t stack_offset) {
1800 ScratchRegisterScope tmp(this, reg);
1801 __ mov(tmp.reg(), reg);
1802 __ LoadFromOffset(reg, base_reg, stack_offset);
1803 __ StoreToOffset(tmp.reg(), base_reg, stack_offset);
1804 }
1805
1806
1807 void ParallelMoveResolver::Exchange(Register base_reg1,
1808 intptr_t stack_offset1,
1809 Register base_reg2,
1810 intptr_t stack_offset2) {
1811 ScratchRegisterScope tmp1(this, kNoRegister);
1812 ScratchRegisterScope tmp2(this, tmp1.reg());
1813 __ LoadFromOffset(tmp1.reg(), base_reg1, stack_offset1);
1814 __ LoadFromOffset(tmp2.reg(), base_reg2, stack_offset2);
1815 __ StoreToOffset(tmp1.reg(), base_reg1, stack_offset2);
1816 __ StoreToOffset(tmp2.reg(), base_reg2, stack_offset1);
1817 }
1818
1819
1820 void ParallelMoveResolver::SpillScratch(Register reg) {
1821 __ Comment("ParallelMoveResolver::SpillScratch");
1822 __ Push(reg);
1823 }
1824
1825
1826 void ParallelMoveResolver::RestoreScratch(Register reg) {
1827 __ Comment("ParallelMoveResolver::RestoreScratch");
1828 __ Pop(reg);
1829 }
1830
1831
1832 void ParallelMoveResolver::SpillFpuScratch(FpuRegister reg) {
1833 __ Comment("ParallelMoveResolver::SpillFpuScratch");
1834 __ AddImmediate(SP, -kDoubleSize);
1835 __ StoreDToOffset(reg, SP, 0);
1836 }
1837
1838
1839 void ParallelMoveResolver::RestoreFpuScratch(FpuRegister reg) {
1840 __ Comment("ParallelMoveResolver::RestoreFpuScratch");
1841 __ LoadDFromOffset(reg, SP, 0);
1842 __ AddImmediate(SP, kDoubleSize);
1843 }
1844
1845
1846 #undef __
1847
1848
1849 } // namespace dart
1850
1851 #endif // defined TARGET_ARCH_MIPS
OLDNEW
« no previous file with comments | « runtime/vm/disassembler_mips.cc ('k') | runtime/vm/instructions.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698