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

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

Issue 2858623002: Remove MIPS support (Closed)
Patch Set: Merge and cleanup Created 3 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 unified diff | Download patch
« no previous file with comments | « runtime/vm/assembler_mips.h ('k') | runtime/vm/assembler_mips_test.cc » ('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" // NOLINT
6 #if defined(TARGET_ARCH_MIPS)
7
8 #include "vm/assembler.h"
9 #include "vm/longjump.h"
10 #include "vm/runtime_entry.h"
11 #include "vm/simulator.h"
12 #include "vm/stack_frame.h"
13 #include "vm/stub_code.h"
14
15 namespace dart {
16
17 DECLARE_FLAG(bool, check_code_pointer);
18 DECLARE_FLAG(bool, inline_alloc);
19 #if defined(USING_SIMULATOR)
20 DECLARE_FLAG(int, trace_sim_after);
21 #endif
22
23 void Assembler::InitializeMemoryWithBreakpoints(uword data, intptr_t length) {
24 ASSERT(Utils::IsAligned(data, 4));
25 ASSERT(Utils::IsAligned(length, 4));
26 const uword end = data + length;
27 while (data < end) {
28 *reinterpret_cast<int32_t*>(data) = Instr::kBreakPointInstruction;
29 data += 4;
30 }
31 }
32
33
34 void Assembler::GetNextPC(Register dest, Register temp) {
35 if (temp != kNoRegister) {
36 mov(temp, RA);
37 }
38 EmitRegImmType(REGIMM, R0, BGEZAL, 1);
39 mov(dest, RA);
40 if (temp != kNoRegister) {
41 mov(RA, temp);
42 }
43 }
44
45
46 static bool CanEncodeBranchOffset(int32_t offset) {
47 ASSERT(Utils::IsAligned(offset, 4));
48 return Utils::IsInt(18, offset);
49 }
50
51
52 int32_t Assembler::EncodeBranchOffset(int32_t offset, int32_t instr) {
53 if (!CanEncodeBranchOffset(offset)) {
54 ASSERT(!use_far_branches());
55 Thread::Current()->long_jump_base()->Jump(1, Object::branch_offset_error());
56 }
57
58 // Properly preserve only the bits supported in the instruction.
59 offset >>= 2;
60 offset &= kBranchOffsetMask;
61 return (instr & ~kBranchOffsetMask) | offset;
62 }
63
64
65 static intptr_t DecodeBranchOffset(int32_t instr) {
66 // Sign-extend, left-shift by 2.
67 return (((instr & kBranchOffsetMask) << 16) >> 14);
68 }
69
70
71 static int32_t DecodeLoadImmediate(int32_t ori_instr, int32_t lui_instr) {
72 return (((lui_instr & kBranchOffsetMask) << 16) |
73 (ori_instr & kBranchOffsetMask));
74 }
75
76
77 static int32_t EncodeLoadImmediate(int32_t dest, int32_t instr) {
78 return ((instr & ~kBranchOffsetMask) | (dest & kBranchOffsetMask));
79 }
80
81
82 class PatchFarJump : public AssemblerFixup {
83 public:
84 PatchFarJump() {}
85
86 void Process(const MemoryRegion& region, intptr_t position) {
87 const int32_t high = region.Load<int32_t>(position);
88 const int32_t low = region.Load<int32_t>(position + Instr::kInstrSize);
89 const int32_t offset = DecodeLoadImmediate(low, high);
90 const int32_t dest = region.start() + offset;
91
92 if ((Instr::At(reinterpret_cast<uword>(&high))->OpcodeField() == LUI) &&
93 (Instr::At(reinterpret_cast<uword>(&low))->OpcodeField() == ORI)) {
94 // Change the offset to the absolute value.
95 const int32_t encoded_low =
96 EncodeLoadImmediate(dest & kBranchOffsetMask, low);
97 const int32_t encoded_high = EncodeLoadImmediate(dest >> 16, high);
98
99 region.Store<int32_t>(position, encoded_high);
100 region.Store<int32_t>(position + Instr::kInstrSize, encoded_low);
101 return;
102 }
103 // If the offset loading instructions aren't there, we must have replaced
104 // the far branch with a near one, and so these instructions should be NOPs.
105 ASSERT((high == Instr::kNopInstruction) && (low == Instr::kNopInstruction));
106 }
107
108 virtual bool IsPointerOffset() const { return false; }
109 };
110
111
112 void Assembler::EmitFarJump(int32_t offset, bool link) {
113 ASSERT(!in_delay_slot_);
114 ASSERT(use_far_branches());
115 const uint16_t low = Utils::Low16Bits(offset);
116 const uint16_t high = Utils::High16Bits(offset);
117 buffer_.EmitFixup(new PatchFarJump());
118 lui(T9, Immediate(high));
119 ori(T9, T9, Immediate(low));
120 if (link) {
121 EmitRType(SPECIAL, T9, R0, RA, 0, JALR);
122 } else {
123 EmitRType(SPECIAL, T9, R0, R0, 0, JR);
124 }
125 }
126
127
128 static Opcode OppositeBranchOpcode(Opcode b) {
129 switch (b) {
130 case BEQ:
131 return BNE;
132 case BNE:
133 return BEQ;
134 case BGTZ:
135 return BLEZ;
136 case BLEZ:
137 return BGTZ;
138 case BEQL:
139 return BNEL;
140 case BNEL:
141 return BEQL;
142 case BGTZL:
143 return BLEZL;
144 case BLEZL:
145 return BGTZL;
146 default:
147 UNREACHABLE();
148 break;
149 }
150 return BNE;
151 }
152
153
154 void Assembler::EmitFarBranch(Opcode b,
155 Register rs,
156 Register rt,
157 int32_t offset) {
158 ASSERT(!in_delay_slot_);
159 EmitIType(b, rs, rt, 4);
160 nop();
161 EmitFarJump(offset, false);
162 }
163
164
165 static RtRegImm OppositeBranchNoLink(RtRegImm b) {
166 switch (b) {
167 case BLTZ:
168 return BGEZ;
169 case BGEZ:
170 return BLTZ;
171 case BLTZAL:
172 return BGEZ;
173 case BGEZAL:
174 return BLTZ;
175 default:
176 UNREACHABLE();
177 break;
178 }
179 return BLTZ;
180 }
181
182
183 void Assembler::EmitFarRegImmBranch(RtRegImm b, Register rs, int32_t offset) {
184 ASSERT(!in_delay_slot_);
185 EmitRegImmType(REGIMM, rs, b, 4);
186 nop();
187 EmitFarJump(offset, (b == BLTZAL) || (b == BGEZAL));
188 }
189
190
191 void Assembler::EmitFarFpuBranch(bool kind, int32_t offset) {
192 ASSERT(!in_delay_slot_);
193 const uint32_t b16 = kind ? (1 << 16) : 0;
194 Emit(COP1 << kOpcodeShift | COP1_BC << kCop1SubShift | b16 | 4);
195 nop();
196 EmitFarJump(offset, false);
197 }
198
199
200 void Assembler::EmitBranch(Opcode b, Register rs, Register rt, Label* label) {
201 ASSERT(!in_delay_slot_);
202 if (label->IsBound()) {
203 // Relative destination from an instruction after the branch.
204 const int32_t dest =
205 label->Position() - (buffer_.Size() + Instr::kInstrSize);
206 if (use_far_branches() && !CanEncodeBranchOffset(dest)) {
207 EmitFarBranch(OppositeBranchOpcode(b), rs, rt, label->Position());
208 } else {
209 const uint16_t dest_off = EncodeBranchOffset(dest, 0);
210 EmitIType(b, rs, rt, dest_off);
211 }
212 } else {
213 const intptr_t position = buffer_.Size();
214 if (use_far_branches()) {
215 const uint32_t dest_off = label->position_;
216 EmitFarBranch(b, rs, rt, dest_off);
217 } else {
218 const uint16_t dest_off = EncodeBranchOffset(label->position_, 0);
219 EmitIType(b, rs, rt, dest_off);
220 }
221 label->LinkTo(position);
222 }
223 }
224
225
226 void Assembler::EmitRegImmBranch(RtRegImm b, Register rs, Label* label) {
227 ASSERT(!in_delay_slot_);
228 if (label->IsBound()) {
229 // Relative destination from an instruction after the branch.
230 const int32_t dest =
231 label->Position() - (buffer_.Size() + Instr::kInstrSize);
232 if (use_far_branches() && !CanEncodeBranchOffset(dest)) {
233 EmitFarRegImmBranch(OppositeBranchNoLink(b), rs, label->Position());
234 } else {
235 const uint16_t dest_off = EncodeBranchOffset(dest, 0);
236 EmitRegImmType(REGIMM, rs, b, dest_off);
237 }
238 } else {
239 const intptr_t position = buffer_.Size();
240 if (use_far_branches()) {
241 const uint32_t dest_off = label->position_;
242 EmitFarRegImmBranch(b, rs, dest_off);
243 } else {
244 const uint16_t dest_off = EncodeBranchOffset(label->position_, 0);
245 EmitRegImmType(REGIMM, rs, b, dest_off);
246 }
247 label->LinkTo(position);
248 }
249 }
250
251
252 void Assembler::EmitFpuBranch(bool kind, Label* label) {
253 ASSERT(!in_delay_slot_);
254 const int32_t b16 = kind ? (1 << 16) : 0; // Bit 16 set for branch on true.
255 if (label->IsBound()) {
256 // Relative destination from an instruction after the branch.
257 const int32_t dest =
258 label->Position() - (buffer_.Size() + Instr::kInstrSize);
259 if (use_far_branches() && !CanEncodeBranchOffset(dest)) {
260 EmitFarFpuBranch(kind, label->Position());
261 } else {
262 const uint16_t dest_off = EncodeBranchOffset(dest, 0);
263 Emit(COP1 << kOpcodeShift | COP1_BC << kCop1SubShift | b16 | dest_off);
264 }
265 } else {
266 const intptr_t position = buffer_.Size();
267 if (use_far_branches()) {
268 const uint32_t dest_off = label->position_;
269 EmitFarFpuBranch(kind, dest_off);
270 } else {
271 const uint16_t dest_off = EncodeBranchOffset(label->position_, 0);
272 Emit(COP1 << kOpcodeShift | COP1_BC << kCop1SubShift | b16 | dest_off);
273 }
274 label->LinkTo(position);
275 }
276 }
277
278
279 static int32_t FlipBranchInstruction(int32_t instr) {
280 Instr* i = Instr::At(reinterpret_cast<uword>(&instr));
281 if (i->OpcodeField() == REGIMM) {
282 RtRegImm b = OppositeBranchNoLink(i->RegImmFnField());
283 i->SetRegImmFnField(b);
284 return i->InstructionBits();
285 } else if (i->OpcodeField() == COP1) {
286 return instr ^ (1 << 16);
287 }
288 Opcode b = OppositeBranchOpcode(i->OpcodeField());
289 i->SetOpcodeField(b);
290 return i->InstructionBits();
291 }
292
293
294 void Assembler::Bind(Label* label) {
295 ASSERT(!label->IsBound());
296 intptr_t bound_pc = buffer_.Size();
297
298 while (label->IsLinked()) {
299 int32_t position = label->Position();
300 int32_t dest = bound_pc - (position + Instr::kInstrSize);
301
302 if (use_far_branches() && !CanEncodeBranchOffset(dest)) {
303 // Far branches are enabled and we can't encode the branch offset.
304
305 // Grab the branch instruction. We'll need to flip it later.
306 const int32_t branch = buffer_.Load<int32_t>(position);
307
308 // Grab instructions that load the offset.
309 const int32_t high =
310 buffer_.Load<int32_t>(position + 2 * Instr::kInstrSize);
311 const int32_t low =
312 buffer_.Load<int32_t>(position + 3 * Instr::kInstrSize);
313
314 // Change from relative to the branch to relative to the assembler buffer.
315 dest = buffer_.Size();
316 const int32_t encoded_low =
317 EncodeLoadImmediate(dest & kBranchOffsetMask, low);
318 const int32_t encoded_high = EncodeLoadImmediate(dest >> 16, high);
319
320 // Skip the unconditional far jump if the test fails by flipping the
321 // sense of the branch instruction.
322 buffer_.Store<int32_t>(position, FlipBranchInstruction(branch));
323 buffer_.Store<int32_t>(position + 2 * Instr::kInstrSize, encoded_high);
324 buffer_.Store<int32_t>(position + 3 * Instr::kInstrSize, encoded_low);
325 label->position_ = DecodeLoadImmediate(low, high);
326 } else if (use_far_branches() && CanEncodeBranchOffset(dest)) {
327 // We assembled a far branch, but we don't need it. Replace with a near
328 // branch.
329
330 // Grab the link to the next branch.
331 const int32_t high =
332 buffer_.Load<int32_t>(position + 2 * Instr::kInstrSize);
333 const int32_t low =
334 buffer_.Load<int32_t>(position + 3 * Instr::kInstrSize);
335
336 // Grab the original branch instruction.
337 int32_t branch = buffer_.Load<int32_t>(position);
338
339 // Clear out the old (far) branch.
340 for (int i = 0; i < 5; i++) {
341 buffer_.Store<int32_t>(position + i * Instr::kInstrSize,
342 Instr::kNopInstruction);
343 }
344
345 // Calculate the new offset.
346 dest = dest - 4 * Instr::kInstrSize;
347 const int32_t encoded = EncodeBranchOffset(dest, branch);
348 buffer_.Store<int32_t>(position + 4 * Instr::kInstrSize, encoded);
349 label->position_ = DecodeLoadImmediate(low, high);
350 } else {
351 const int32_t next = buffer_.Load<int32_t>(position);
352 const int32_t encoded = EncodeBranchOffset(dest, next);
353 buffer_.Store<int32_t>(position, encoded);
354 label->position_ = DecodeBranchOffset(next);
355 }
356 }
357 label->BindTo(bound_pc);
358 delay_slot_available_ = false;
359 }
360
361
362 void Assembler::LoadWordFromPoolOffset(Register rd,
363 int32_t offset,
364 Register pp) {
365 ASSERT((pp != PP) || constant_pool_allowed());
366 ASSERT(!in_delay_slot_);
367 ASSERT(rd != pp);
368 if (Address::CanHoldOffset(offset)) {
369 lw(rd, Address(pp, offset));
370 } else {
371 const int16_t offset_low = Utils::Low16Bits(offset); // Signed.
372 offset -= offset_low;
373 const uint16_t offset_high = Utils::High16Bits(offset); // Unsigned.
374 if (offset_high != 0) {
375 lui(rd, Immediate(offset_high));
376 addu(rd, rd, pp);
377 lw(rd, Address(rd, offset_low));
378 } else {
379 lw(rd, Address(pp, offset_low));
380 }
381 }
382 }
383
384
385 void Assembler::AdduDetectOverflow(Register rd,
386 Register rs,
387 Register rt,
388 Register ro,
389 Register scratch) {
390 ASSERT(!in_delay_slot_);
391 ASSERT(rd != ro);
392 ASSERT(rd != TMP);
393 ASSERT(ro != TMP);
394 ASSERT(ro != rs);
395 ASSERT(ro != rt);
396
397 if ((rs == rt) && (rd == rs)) {
398 ASSERT(scratch != kNoRegister);
399 ASSERT(scratch != TMP);
400 ASSERT(rd != scratch);
401 ASSERT(ro != scratch);
402 ASSERT(rs != scratch);
403 ASSERT(rt != scratch);
404 mov(scratch, rt);
405 rt = scratch;
406 }
407
408 if (rd == rs) {
409 mov(TMP, rs); // Preserve rs.
410 addu(rd, rs, rt); // rs is overwritten.
411 xor_(TMP, rd, TMP); // Original rs.
412 xor_(ro, rd, rt);
413 and_(ro, ro, TMP);
414 } else if (rd == rt) {
415 mov(TMP, rt); // Preserve rt.
416 addu(rd, rs, rt); // rt is overwritten.
417 xor_(TMP, rd, TMP); // Original rt.
418 xor_(ro, rd, rs);
419 and_(ro, ro, TMP);
420 } else {
421 addu(rd, rs, rt);
422 xor_(ro, rd, rs);
423 xor_(TMP, rd, rt);
424 and_(ro, TMP, ro);
425 }
426 }
427
428
429 void Assembler::SubuDetectOverflow(Register rd,
430 Register rs,
431 Register rt,
432 Register ro) {
433 ASSERT(!in_delay_slot_);
434 ASSERT(rd != ro);
435 ASSERT(rd != TMP);
436 ASSERT(ro != TMP);
437 ASSERT(ro != rs);
438 ASSERT(ro != rt);
439 ASSERT(rs != TMP);
440 ASSERT(rt != TMP);
441
442 // This happens with some crankshaft code. Since Subu works fine if
443 // left == right, let's not make that restriction here.
444 if (rs == rt) {
445 mov(rd, ZR);
446 mov(ro, ZR);
447 return;
448 }
449
450 if (rd == rs) {
451 mov(TMP, rs); // Preserve left.
452 subu(rd, rs, rt); // Left is overwritten.
453 xor_(ro, rd, TMP); // scratch is original left.
454 xor_(TMP, TMP, rs); // scratch is original left.
455 and_(ro, TMP, ro);
456 } else if (rd == rt) {
457 mov(TMP, rt); // Preserve right.
458 subu(rd, rs, rt); // Right is overwritten.
459 xor_(ro, rd, rs);
460 xor_(TMP, rs, TMP); // Original right.
461 and_(ro, TMP, ro);
462 } else {
463 subu(rd, rs, rt);
464 xor_(ro, rd, rs);
465 xor_(TMP, rs, rt);
466 and_(ro, TMP, ro);
467 }
468 }
469
470
471 void Assembler::CheckCodePointer() {
472 #ifdef DEBUG
473 if (!FLAG_check_code_pointer) {
474 return;
475 }
476 Comment("CheckCodePointer");
477 Label cid_ok, instructions_ok;
478 Push(CMPRES1);
479 Push(CMPRES2);
480 LoadClassId(CMPRES1, CODE_REG);
481 BranchEqual(CMPRES1, Immediate(kCodeCid), &cid_ok);
482 break_(0);
483 Bind(&cid_ok);
484 GetNextPC(CMPRES1, TMP);
485 const intptr_t entry_offset = CodeSize() - Instr::kInstrSize +
486 Instructions::HeaderSize() - kHeapObjectTag;
487 AddImmediate(CMPRES1, CMPRES1, -entry_offset);
488 lw(CMPRES2, FieldAddress(CODE_REG, Code::saved_instructions_offset()));
489 BranchEqual(CMPRES1, CMPRES2, &instructions_ok);
490 break_(1);
491 Bind(&instructions_ok);
492 Pop(CMPRES2);
493 Pop(CMPRES1);
494 #endif
495 }
496
497
498 void Assembler::RestoreCodePointer() {
499 lw(CODE_REG, Address(FP, kPcMarkerSlotFromFp * kWordSize));
500 CheckCodePointer();
501 }
502
503
504 void Assembler::Branch(const StubEntry& stub_entry, Register pp) {
505 ASSERT(!in_delay_slot_);
506 const Code& target_code = Code::ZoneHandle(stub_entry.code());
507 const int32_t offset = ObjectPool::element_offset(
508 object_pool_wrapper_.AddObject(target_code, kPatchable));
509 LoadWordFromPoolOffset(CODE_REG, offset - kHeapObjectTag, pp);
510 lw(TMP, FieldAddress(CODE_REG, Code::entry_point_offset()));
511 jr(TMP);
512 }
513
514
515 void Assembler::BranchLink(const ExternalLabel* label) {
516 ASSERT(!in_delay_slot_);
517 LoadImmediate(T9, label->address());
518 jalr(T9);
519 }
520
521
522 void Assembler::BranchLink(const Code& target, Patchability patchable) {
523 ASSERT(!in_delay_slot_);
524 const int32_t offset = ObjectPool::element_offset(
525 object_pool_wrapper_.FindObject(target, patchable));
526 LoadWordFromPoolOffset(CODE_REG, offset - kHeapObjectTag);
527 lw(T9, FieldAddress(CODE_REG, Code::entry_point_offset()));
528 jalr(T9);
529 if (patchable == kPatchable) {
530 delay_slot_available_ = false; // CodePatcher expects a nop.
531 }
532 }
533
534
535 void Assembler::BranchLink(const StubEntry& stub_entry,
536 Patchability patchable) {
537 BranchLink(Code::ZoneHandle(stub_entry.code()), patchable);
538 }
539
540
541 void Assembler::BranchLinkPatchable(const StubEntry& stub_entry) {
542 BranchLink(Code::ZoneHandle(stub_entry.code()), kPatchable);
543 }
544
545
546 void Assembler::BranchLinkToRuntime() {
547 lw(T9, Address(THR, Thread::call_to_runtime_entry_point_offset()));
548 jalr(T9);
549 delay_slot()->lw(CODE_REG,
550 Address(THR, Thread::call_to_runtime_stub_offset()));
551 }
552
553
554 void Assembler::BranchLinkWithEquivalence(const StubEntry& stub_entry,
555 const Object& equivalence) {
556 const Code& target = Code::ZoneHandle(stub_entry.code());
557 ASSERT(!in_delay_slot_);
558 const int32_t offset = ObjectPool::element_offset(
559 object_pool_wrapper_.FindObject(target, equivalence));
560 LoadWordFromPoolOffset(CODE_REG, offset - kHeapObjectTag);
561 lw(T9, FieldAddress(CODE_REG, Code::entry_point_offset()));
562 jalr(T9);
563 delay_slot_available_ = false; // CodePatcher expects a nop.
564 }
565
566
567 bool Assembler::CanLoadFromObjectPool(const Object& object) const {
568 ASSERT(!object.IsICData() || ICData::Cast(object).IsOriginal());
569 ASSERT(!object.IsField() || Field::Cast(object).IsOriginal());
570 ASSERT(!Thread::CanLoadFromThread(object));
571 if (!constant_pool_allowed()) {
572 return false;
573 }
574
575 ASSERT(object.IsNotTemporaryScopedHandle());
576 ASSERT(object.IsOld());
577 return true;
578 }
579
580
581 void Assembler::LoadObjectHelper(Register rd,
582 const Object& object,
583 bool is_unique) {
584 ASSERT(!object.IsICData() || ICData::Cast(object).IsOriginal());
585 ASSERT(!object.IsField() || Field::Cast(object).IsOriginal());
586 ASSERT(!in_delay_slot_);
587 if (Thread::CanLoadFromThread(object)) {
588 // Load common VM constants from the thread. This works also in places where
589 // no constant pool is set up (e.g. intrinsic code).
590 lw(rd, Address(THR, Thread::OffsetFromThread(object)));
591 } else if (object.IsSmi()) {
592 // Relocation doesn't apply to Smis.
593 LoadImmediate(rd, reinterpret_cast<int32_t>(object.raw()));
594 } else if (CanLoadFromObjectPool(object)) {
595 // Make sure that class CallPattern is able to decode this load from the
596 // object pool.
597 const int32_t offset = ObjectPool::element_offset(
598 is_unique ? object_pool_wrapper_.AddObject(object)
599 : object_pool_wrapper_.FindObject(object));
600 LoadWordFromPoolOffset(rd, offset - kHeapObjectTag);
601 } else {
602 UNREACHABLE();
603 }
604 }
605
606
607 void Assembler::LoadObject(Register rd, const Object& object) {
608 LoadObjectHelper(rd, object, false);
609 }
610
611
612 void Assembler::LoadUniqueObject(Register rd, const Object& object) {
613 LoadObjectHelper(rd, object, true);
614 }
615
616
617 void Assembler::LoadFunctionFromCalleePool(Register dst,
618 const Function& function,
619 Register new_pp) {
620 const int32_t offset =
621 ObjectPool::element_offset(object_pool_wrapper_.FindObject(function));
622 LoadWordFromPoolOffset(dst, offset - kHeapObjectTag, new_pp);
623 }
624
625
626 void Assembler::LoadNativeEntry(Register rd,
627 const ExternalLabel* label,
628 Patchability patchable) {
629 const int32_t offset = ObjectPool::element_offset(
630 object_pool_wrapper_.FindNativeEntry(label, patchable));
631 LoadWordFromPoolOffset(rd, offset - kHeapObjectTag);
632 }
633
634
635 void Assembler::PushObject(const Object& object) {
636 ASSERT(!object.IsICData() || ICData::Cast(object).IsOriginal());
637 ASSERT(!object.IsField() || Field::Cast(object).IsOriginal());
638 ASSERT(!in_delay_slot_);
639 LoadObject(TMP, object);
640 Push(TMP);
641 }
642
643
644 // Preserves object and value registers.
645 void Assembler::StoreIntoObjectFilterNoSmi(Register object,
646 Register value,
647 Label* no_update) {
648 ASSERT(!in_delay_slot_);
649 COMPILE_ASSERT((kNewObjectAlignmentOffset == kWordSize) &&
650 (kOldObjectAlignmentOffset == 0));
651
652 // Write-barrier triggers if the value is in the new space (has bit set) and
653 // the object is in the old space (has bit cleared).
654 // To check that, we compute value & ~object and skip the write barrier
655 // if the bit is not set. We can't destroy the object.
656 nor(TMP, ZR, object);
657 and_(TMP, value, TMP);
658 andi(CMPRES1, TMP, Immediate(kNewObjectAlignmentOffset));
659 beq(CMPRES1, ZR, no_update);
660 }
661
662
663 // Preserves object and value registers.
664 void Assembler::StoreIntoObjectFilter(Register object,
665 Register value,
666 Label* no_update) {
667 ASSERT(!in_delay_slot_);
668 // For the value we are only interested in the new/old bit and the tag bit.
669 // And the new bit with the tag bit. The resulting bit will be 0 for a Smi.
670 sll(TMP, value, kObjectAlignmentLog2 - 1);
671 and_(TMP, value, TMP);
672 // And the result with the negated space bit of the object.
673 nor(CMPRES1, ZR, object);
674 and_(TMP, TMP, CMPRES1);
675 andi(CMPRES1, TMP, Immediate(kNewObjectAlignmentOffset));
676 beq(CMPRES1, ZR, no_update);
677 }
678
679
680 void Assembler::StoreIntoObject(Register object,
681 const Address& dest,
682 Register value,
683 bool can_value_be_smi) {
684 ASSERT(!in_delay_slot_);
685 ASSERT(object != value);
686 sw(value, dest);
687 Label done;
688 if (can_value_be_smi) {
689 StoreIntoObjectFilter(object, value, &done);
690 } else {
691 StoreIntoObjectFilterNoSmi(object, value, &done);
692 }
693 // A store buffer update is required.
694 if (value != T0) {
695 // Preserve T0.
696 addiu(SP, SP, Immediate(-2 * kWordSize));
697 sw(T0, Address(SP, 1 * kWordSize));
698 } else {
699 addiu(SP, SP, Immediate(-1 * kWordSize));
700 }
701 sw(RA, Address(SP, 0 * kWordSize));
702 if (object != T0) {
703 mov(T0, object);
704 }
705 lw(T9, Address(THR, Thread::update_store_buffer_entry_point_offset()));
706 jalr(T9);
707 delay_slot()->lw(CODE_REG,
708 Address(THR, Thread::update_store_buffer_code_offset()));
709 lw(RA, Address(SP, 0 * kWordSize));
710 if (value != T0) {
711 // Restore T0.
712 lw(T0, Address(SP, 1 * kWordSize));
713 addiu(SP, SP, Immediate(2 * kWordSize));
714 } else {
715 addiu(SP, SP, Immediate(1 * kWordSize));
716 }
717 Bind(&done);
718 }
719
720
721 void Assembler::StoreIntoObjectOffset(Register object,
722 int32_t offset,
723 Register value,
724 bool can_value_be_smi) {
725 if (Address::CanHoldOffset(offset - kHeapObjectTag)) {
726 StoreIntoObject(object, FieldAddress(object, offset), value,
727 can_value_be_smi);
728 } else {
729 AddImmediate(TMP, object, offset - kHeapObjectTag);
730 StoreIntoObject(object, Address(TMP), value, can_value_be_smi);
731 }
732 }
733
734
735 void Assembler::StoreIntoObjectNoBarrier(Register object,
736 const Address& dest,
737 Register value) {
738 ASSERT(!in_delay_slot_);
739 sw(value, dest);
740 #if defined(DEBUG)
741 Label done;
742 StoreIntoObjectFilter(object, value, &done);
743 Stop("Store buffer update is required");
744 Bind(&done);
745 #endif // defined(DEBUG)
746 // No store buffer update.
747 }
748
749
750 void Assembler::StoreIntoObjectNoBarrierOffset(Register object,
751 int32_t offset,
752 Register value) {
753 if (Address::CanHoldOffset(offset - kHeapObjectTag)) {
754 StoreIntoObjectNoBarrier(object, FieldAddress(object, offset), value);
755 } else {
756 AddImmediate(TMP, object, offset - kHeapObjectTag);
757 StoreIntoObjectNoBarrier(object, Address(TMP), value);
758 }
759 }
760
761
762 void Assembler::StoreIntoObjectNoBarrier(Register object,
763 const Address& dest,
764 const Object& value) {
765 ASSERT(!value.IsICData() || ICData::Cast(value).IsOriginal());
766 ASSERT(!value.IsField() || Field::Cast(value).IsOriginal());
767 ASSERT(!in_delay_slot_);
768 ASSERT(value.IsSmi() || value.InVMHeap() ||
769 (value.IsOld() && value.IsNotTemporaryScopedHandle()));
770 // No store buffer update.
771 LoadObject(TMP, value);
772 sw(TMP, dest);
773 }
774
775
776 void Assembler::StoreIntoObjectNoBarrierOffset(Register object,
777 int32_t offset,
778 const Object& value) {
779 if (Address::CanHoldOffset(offset - kHeapObjectTag)) {
780 StoreIntoObjectNoBarrier(object, FieldAddress(object, offset), value);
781 } else {
782 AddImmediate(TMP, object, offset - kHeapObjectTag);
783 StoreIntoObjectNoBarrier(object, Address(TMP), value);
784 }
785 }
786
787
788 void Assembler::LoadIsolate(Register result) {
789 lw(result, Address(THR, Thread::isolate_offset()));
790 }
791
792
793 void Assembler::LoadClassId(Register result, Register object) {
794 ASSERT(RawObject::kClassIdTagPos == 16);
795 ASSERT(RawObject::kClassIdTagSize == 16);
796 const intptr_t class_id_offset =
797 Object::tags_offset() + RawObject::kClassIdTagPos / kBitsPerByte;
798 lhu(result, FieldAddress(object, class_id_offset));
799 }
800
801
802 void Assembler::LoadClassById(Register result, Register class_id) {
803 ASSERT(!in_delay_slot_);
804 ASSERT(result != class_id);
805 LoadIsolate(result);
806 const intptr_t offset =
807 Isolate::class_table_offset() + ClassTable::table_offset();
808 lw(result, Address(result, offset));
809 sll(TMP, class_id, 2);
810 addu(result, result, TMP);
811 lw(result, Address(result));
812 }
813
814
815 void Assembler::LoadClass(Register result, Register object) {
816 ASSERT(!in_delay_slot_);
817 ASSERT(TMP != result);
818 LoadClassId(TMP, object);
819 LoadClassById(result, TMP);
820 }
821
822
823 void Assembler::LoadClassIdMayBeSmi(Register result, Register object) {
824 Label heap_object, done;
825 andi(CMPRES1, object, Immediate(kSmiTagMask));
826 bne(CMPRES1, ZR, &heap_object);
827 LoadImmediate(result, kSmiCid);
828 b(&done);
829 Bind(&heap_object);
830 LoadClassId(result, object);
831 Bind(&done);
832 }
833
834
835 void Assembler::LoadTaggedClassIdMayBeSmi(Register result, Register object) {
836 LoadClassIdMayBeSmi(result, object);
837 SmiTag(result);
838 }
839
840
841 void Assembler::EnterFrame() {
842 ASSERT(!in_delay_slot_);
843 addiu(SP, SP, Immediate(-2 * kWordSize));
844 sw(RA, Address(SP, 1 * kWordSize));
845 sw(FP, Address(SP, 0 * kWordSize));
846 mov(FP, SP);
847 }
848
849
850 void Assembler::LeaveFrameAndReturn() {
851 ASSERT(!in_delay_slot_);
852 mov(SP, FP);
853 lw(RA, Address(SP, 1 * kWordSize));
854 lw(FP, Address(SP, 0 * kWordSize));
855 Ret();
856 delay_slot()->addiu(SP, SP, Immediate(2 * kWordSize));
857 }
858
859
860 void Assembler::EnterStubFrame(intptr_t frame_size) {
861 EnterDartFrame(frame_size);
862 }
863
864
865 void Assembler::LeaveStubFrame() {
866 LeaveDartFrame();
867 }
868
869
870 void Assembler::LeaveStubFrameAndReturn(Register ra) {
871 LeaveDartFrameAndReturn(ra);
872 }
873
874
875 // T0 receiver, S5 guarded cid as Smi
876 void Assembler::MonomorphicCheckedEntry() {
877 ASSERT(has_single_entry_point_);
878 has_single_entry_point_ = false;
879 bool saved_use_far_branches = use_far_branches();
880 set_use_far_branches(false);
881
882 Label have_cid, miss;
883 Bind(&miss);
884 lw(T9, Address(THR, Thread::monomorphic_miss_entry_offset()));
885 jr(T9);
886
887 Comment("MonomorphicCheckedEntry");
888 ASSERT(CodeSize() == Instructions::kCheckedEntryOffset);
889 SmiUntag(S5);
890 LoadClassIdMayBeSmi(S4, T0);
891 bne(S4, S5, &miss);
892
893 // Fall through to unchecked entry.
894 ASSERT(CodeSize() == Instructions::kUncheckedEntryOffset);
895
896 set_use_far_branches(saved_use_far_branches);
897 }
898
899
900 #ifndef PRODUCT
901 void Assembler::MaybeTraceAllocation(intptr_t cid,
902 Register temp_reg,
903 Label* trace) {
904 ASSERT(cid > 0);
905 ASSERT(!in_delay_slot_);
906 ASSERT(temp_reg != kNoRegister);
907 ASSERT(temp_reg != TMP);
908 intptr_t state_offset = ClassTable::StateOffsetFor(cid);
909 LoadIsolate(temp_reg);
910 intptr_t table_offset =
911 Isolate::class_table_offset() + ClassTable::TableOffsetFor(cid);
912 lw(temp_reg, Address(temp_reg, table_offset));
913 AddImmediate(temp_reg, state_offset);
914 lw(temp_reg, Address(temp_reg, 0));
915 andi(CMPRES1, temp_reg, Immediate(ClassHeapStats::TraceAllocationMask()));
916 bne(CMPRES1, ZR, trace);
917 }
918
919
920 void Assembler::UpdateAllocationStats(intptr_t cid,
921 Register temp_reg,
922 Heap::Space space) {
923 ASSERT(!in_delay_slot_);
924 ASSERT(temp_reg != kNoRegister);
925 ASSERT(temp_reg != TMP);
926 ASSERT(cid > 0);
927 intptr_t counter_offset =
928 ClassTable::CounterOffsetFor(cid, space == Heap::kNew);
929 LoadIsolate(temp_reg);
930 intptr_t table_offset =
931 Isolate::class_table_offset() + ClassTable::TableOffsetFor(cid);
932 lw(temp_reg, Address(temp_reg, table_offset));
933 AddImmediate(temp_reg, counter_offset);
934 lw(TMP, Address(temp_reg, 0));
935 AddImmediate(TMP, 1);
936 sw(TMP, Address(temp_reg, 0));
937 }
938
939
940 void Assembler::UpdateAllocationStatsWithSize(intptr_t cid,
941 Register size_reg,
942 Register temp_reg,
943 Heap::Space space) {
944 ASSERT(!in_delay_slot_);
945 ASSERT(temp_reg != kNoRegister);
946 ASSERT(cid > 0);
947 ASSERT(temp_reg != TMP);
948 const uword class_offset = ClassTable::ClassOffsetFor(cid);
949 const uword count_field_offset =
950 (space == Heap::kNew)
951 ? ClassHeapStats::allocated_since_gc_new_space_offset()
952 : ClassHeapStats::allocated_since_gc_old_space_offset();
953 const uword size_field_offset =
954 (space == Heap::kNew)
955 ? ClassHeapStats::allocated_size_since_gc_new_space_offset()
956 : ClassHeapStats::allocated_size_since_gc_old_space_offset();
957 LoadIsolate(temp_reg);
958 intptr_t table_offset =
959 Isolate::class_table_offset() + ClassTable::TableOffsetFor(cid);
960 lw(temp_reg, Address(temp_reg, table_offset));
961 AddImmediate(temp_reg, class_offset);
962 lw(TMP, Address(temp_reg, count_field_offset));
963 AddImmediate(TMP, 1);
964 sw(TMP, Address(temp_reg, count_field_offset));
965 lw(TMP, Address(temp_reg, size_field_offset));
966 addu(TMP, TMP, size_reg);
967 sw(TMP, Address(temp_reg, size_field_offset));
968 }
969 #endif // !PRODUCT
970
971
972 void Assembler::TryAllocate(const Class& cls,
973 Label* failure,
974 Register instance_reg,
975 Register temp_reg) {
976 ASSERT(!in_delay_slot_);
977 ASSERT(failure != NULL);
978 if (FLAG_inline_alloc) {
979 // If this allocation is traced, program will jump to failure path
980 // (i.e. the allocation stub) which will allocate the object and trace the
981 // allocation call site.
982 NOT_IN_PRODUCT(MaybeTraceAllocation(cls.id(), temp_reg, failure));
983 const intptr_t instance_size = cls.instance_size();
984 Heap::Space space = Heap::kNew;
985 lw(temp_reg, Address(THR, Thread::heap_offset()));
986 lw(instance_reg, Address(temp_reg, Heap::TopOffset(space)));
987 // TODO(koda): Protect against unsigned overflow here.
988 AddImmediate(instance_reg, instance_size);
989
990 // instance_reg: potential next object start.
991 lw(TMP, Address(temp_reg, Heap::EndOffset(space)));
992 // Fail if heap end unsigned less than or equal to instance_reg.
993 BranchUnsignedLessEqual(TMP, instance_reg, failure);
994
995 // Successfully allocated the object, now update top to point to
996 // next object start and store the class in the class field of object.
997 sw(instance_reg, Address(temp_reg, Heap::TopOffset(space)));
998
999 ASSERT(instance_size >= kHeapObjectTag);
1000 AddImmediate(instance_reg, -instance_size + kHeapObjectTag);
1001 NOT_IN_PRODUCT(UpdateAllocationStats(cls.id(), temp_reg, space));
1002 uint32_t tags = 0;
1003 tags = RawObject::SizeTag::update(instance_size, tags);
1004 ASSERT(cls.id() != kIllegalCid);
1005 tags = RawObject::ClassIdTag::update(cls.id(), tags);
1006 LoadImmediate(TMP, tags);
1007 sw(TMP, FieldAddress(instance_reg, Object::tags_offset()));
1008 } else {
1009 b(failure);
1010 }
1011 }
1012
1013
1014 void Assembler::TryAllocateArray(intptr_t cid,
1015 intptr_t instance_size,
1016 Label* failure,
1017 Register instance,
1018 Register end_address,
1019 Register temp1,
1020 Register temp2) {
1021 if (FLAG_inline_alloc) {
1022 // If this allocation is traced, program will jump to failure path
1023 // (i.e. the allocation stub) which will allocate the object and trace the
1024 // allocation call site.
1025 NOT_IN_PRODUCT(MaybeTraceAllocation(cid, temp1, failure));
1026 Isolate* isolate = Isolate::Current();
1027 Heap* heap = isolate->heap();
1028 Heap::Space space = Heap::kNew;
1029 lw(temp1, Address(THR, Thread::heap_offset()));
1030 // Potential new object start.
1031 lw(instance, Address(temp1, heap->TopOffset(space)));
1032 // Potential next object start.
1033 AddImmediate(end_address, instance, instance_size);
1034 // Branch on unsigned overflow.
1035 BranchUnsignedLess(end_address, instance, failure);
1036
1037 // Check if the allocation fits into the remaining space.
1038 // instance: potential new object start, /* inline_isolate = */ false.
1039 // end_address: potential next object start.
1040 lw(temp2, Address(temp1, Heap::EndOffset(space)));
1041 BranchUnsignedGreaterEqual(end_address, temp2, failure);
1042
1043 // Successfully allocated the object(s), now update top to point to
1044 // next object start and initialize the object.
1045 sw(end_address, Address(temp1, Heap::TopOffset(space)));
1046 addiu(instance, instance, Immediate(kHeapObjectTag));
1047 LoadImmediate(temp1, instance_size);
1048 NOT_IN_PRODUCT(UpdateAllocationStatsWithSize(cid, temp1, temp2, space));
1049
1050 // Initialize the tags.
1051 // instance: new object start as a tagged pointer.
1052 uint32_t tags = 0;
1053 tags = RawObject::ClassIdTag::update(cid, tags);
1054 tags = RawObject::SizeTag::update(instance_size, tags);
1055 LoadImmediate(temp1, tags);
1056 sw(temp1, FieldAddress(instance, Array::tags_offset())); // Store tags.
1057 } else {
1058 b(failure);
1059 }
1060 }
1061
1062
1063 void Assembler::CallRuntime(const RuntimeEntry& entry,
1064 intptr_t argument_count) {
1065 entry.Call(this, argument_count);
1066 }
1067
1068
1069 void Assembler::EnterDartFrame(intptr_t frame_size) {
1070 ASSERT(!in_delay_slot_);
1071
1072 SetPrologueOffset();
1073
1074 addiu(SP, SP, Immediate(-4 * kWordSize));
1075 sw(RA, Address(SP, 3 * kWordSize));
1076 sw(FP, Address(SP, 2 * kWordSize));
1077 sw(CODE_REG, Address(SP, 1 * kWordSize));
1078 sw(PP, Address(SP, 0 * kWordSize));
1079
1080 // Set FP to the saved previous FP.
1081 addiu(FP, SP, Immediate(2 * kWordSize));
1082
1083 LoadPoolPointer();
1084
1085 // Reserve space for locals.
1086 AddImmediate(SP, -frame_size);
1087 }
1088
1089
1090 // On entry to a function compiled for OSR, the caller's frame pointer, the
1091 // stack locals, and any copied parameters are already in place. The frame
1092 // pointer is already set up. The PC marker is not correct for the
1093 // optimized function and there may be extra space for spill slots to
1094 // allocate. We must also set up the pool pointer for the function.
1095 void Assembler::EnterOsrFrame(intptr_t extra_size) {
1096 ASSERT(!in_delay_slot_);
1097 Comment("EnterOsrFrame");
1098
1099 // Restore return address.
1100 lw(RA, Address(FP, 1 * kWordSize));
1101
1102 // Load the pool pointer. offset has already been subtracted from temp.
1103 RestoreCodePointer();
1104 LoadPoolPointer();
1105
1106 // Reserve space for locals.
1107 AddImmediate(SP, -extra_size);
1108 }
1109
1110
1111 void Assembler::LeaveDartFrame(RestorePP restore_pp) {
1112 ASSERT(!in_delay_slot_);
1113 addiu(SP, FP, Immediate(-2 * kWordSize));
1114
1115 lw(RA, Address(SP, 3 * kWordSize));
1116 lw(FP, Address(SP, 2 * kWordSize));
1117 if (restore_pp == kRestoreCallerPP) {
1118 lw(PP, Address(SP, 0 * kWordSize));
1119 }
1120
1121 // Adjust SP for PC, RA, FP, PP pushed in EnterDartFrame.
1122 addiu(SP, SP, Immediate(4 * kWordSize));
1123 }
1124
1125
1126 void Assembler::LeaveDartFrameAndReturn(Register ra) {
1127 ASSERT(!in_delay_slot_);
1128 addiu(SP, FP, Immediate(-2 * kWordSize));
1129
1130 lw(RA, Address(SP, 3 * kWordSize));
1131 lw(FP, Address(SP, 2 * kWordSize));
1132 lw(PP, Address(SP, 0 * kWordSize));
1133
1134 // Adjust SP for PC, RA, FP, PP pushed in EnterDartFrame, and return.
1135 jr(ra);
1136 delay_slot()->addiu(SP, SP, Immediate(4 * kWordSize));
1137 }
1138
1139
1140 void Assembler::ReserveAlignedFrameSpace(intptr_t frame_space) {
1141 ASSERT(!in_delay_slot_);
1142 // Reserve space for arguments and align frame before entering
1143 // the C++ world.
1144 AddImmediate(SP, -frame_space);
1145 if (OS::ActivationFrameAlignment() > 1) {
1146 LoadImmediate(TMP, ~(OS::ActivationFrameAlignment() - 1));
1147 and_(SP, SP, TMP);
1148 }
1149 }
1150
1151
1152 void Assembler::EnterCallRuntimeFrame(intptr_t frame_space) {
1153 ASSERT(!in_delay_slot_);
1154 const intptr_t kPushedRegistersSize = kDartVolatileCpuRegCount * kWordSize +
1155 3 * kWordSize + // PP, FP and RA.
1156 kDartVolatileFpuRegCount * kWordSize;
1157
1158 SetPrologueOffset();
1159
1160 Comment("EnterCallRuntimeFrame");
1161
1162 // Save volatile CPU and FPU registers on the stack:
1163 // -------------
1164 // FPU Registers
1165 // CPU Registers
1166 // RA
1167 // FP
1168 // -------------
1169 // TODO(zra): It may be a problem for walking the stack that FP is below
1170 // the saved registers. If it turns out to be a problem in the
1171 // future, try pushing RA and FP before the volatile registers.
1172 addiu(SP, SP, Immediate(-kPushedRegistersSize));
1173 for (int i = kDartFirstVolatileFpuReg; i <= kDartLastVolatileFpuReg; i++) {
1174 // These go above the volatile CPU registers.
1175 const int slot =
1176 (i - kDartFirstVolatileFpuReg) + kDartVolatileCpuRegCount + 3;
1177 FRegister reg = static_cast<FRegister>(i);
1178 swc1(reg, Address(SP, slot * kWordSize));
1179 }
1180 for (int i = kDartFirstVolatileCpuReg; i <= kDartLastVolatileCpuReg; i++) {
1181 // + 2 because FP goes in slot 0.
1182 const int slot = (i - kDartFirstVolatileCpuReg) + 3;
1183 Register reg = static_cast<Register>(i);
1184 sw(reg, Address(SP, slot * kWordSize));
1185 }
1186 sw(RA, Address(SP, 2 * kWordSize));
1187 sw(FP, Address(SP, 1 * kWordSize));
1188 sw(PP, Address(SP, 0 * kWordSize));
1189 LoadPoolPointer();
1190
1191 mov(FP, SP);
1192
1193 ReserveAlignedFrameSpace(frame_space);
1194 }
1195
1196
1197 void Assembler::LeaveCallRuntimeFrame() {
1198 ASSERT(!in_delay_slot_);
1199 const intptr_t kPushedRegistersSize = kDartVolatileCpuRegCount * kWordSize +
1200 3 * kWordSize + // FP and RA.
1201 kDartVolatileFpuRegCount * kWordSize;
1202
1203 Comment("LeaveCallRuntimeFrame");
1204
1205 // SP might have been modified to reserve space for arguments
1206 // and ensure proper alignment of the stack frame.
1207 // We need to restore it before restoring registers.
1208 mov(SP, FP);
1209
1210 // Restore volatile CPU and FPU registers from the stack.
1211 lw(PP, Address(SP, 0 * kWordSize));
1212 lw(FP, Address(SP, 1 * kWordSize));
1213 lw(RA, Address(SP, 2 * kWordSize));
1214 for (int i = kDartFirstVolatileCpuReg; i <= kDartLastVolatileCpuReg; i++) {
1215 // + 2 because FP goes in slot 0.
1216 const int slot = (i - kDartFirstVolatileCpuReg) + 3;
1217 Register reg = static_cast<Register>(i);
1218 lw(reg, Address(SP, slot * kWordSize));
1219 }
1220 for (int i = kDartFirstVolatileFpuReg; i <= kDartLastVolatileFpuReg; i++) {
1221 // These go above the volatile CPU registers.
1222 const int slot =
1223 (i - kDartFirstVolatileFpuReg) + kDartVolatileCpuRegCount + 3;
1224 FRegister reg = static_cast<FRegister>(i);
1225 lwc1(reg, Address(SP, slot * kWordSize));
1226 }
1227 addiu(SP, SP, Immediate(kPushedRegistersSize));
1228 }
1229
1230
1231 Address Assembler::ElementAddressForIntIndex(bool is_external,
1232 intptr_t cid,
1233 intptr_t index_scale,
1234 Register array,
1235 intptr_t index) const {
1236 const int64_t offset =
1237 index * index_scale +
1238 (is_external ? 0 : (Instance::DataOffsetFor(cid) - kHeapObjectTag));
1239 ASSERT(Utils::IsInt(32, offset));
1240 ASSERT(Address::CanHoldOffset(offset));
1241 return Address(array, static_cast<int32_t>(offset));
1242 }
1243
1244
1245 void Assembler::LoadElementAddressForIntIndex(Register address,
1246 bool is_external,
1247 intptr_t cid,
1248 intptr_t index_scale,
1249 Register array,
1250 intptr_t index) {
1251 const int64_t offset =
1252 index * index_scale +
1253 (is_external ? 0 : (Instance::DataOffsetFor(cid) - kHeapObjectTag));
1254 AddImmediate(address, array, offset);
1255 }
1256
1257
1258 Address Assembler::ElementAddressForRegIndex(bool is_load,
1259 bool is_external,
1260 intptr_t cid,
1261 intptr_t index_scale,
1262 Register array,
1263 Register index) {
1264 // Note that index is expected smi-tagged, (i.e, LSL 1) for all arrays.
1265 const intptr_t shift = Utils::ShiftForPowerOfTwo(index_scale) - kSmiTagShift;
1266 const int32_t offset =
1267 is_external ? 0 : (Instance::DataOffsetFor(cid) - kHeapObjectTag);
1268 ASSERT(array != TMP);
1269 ASSERT(index != TMP);
1270 const Register base = is_load ? TMP : index;
1271 if (shift < 0) {
1272 ASSERT(shift == -1);
1273 sra(TMP, index, 1);
1274 addu(base, array, TMP);
1275 } else if (shift == 0) {
1276 addu(base, array, index);
1277 } else {
1278 sll(TMP, index, shift);
1279 addu(base, array, TMP);
1280 }
1281 ASSERT(Address::CanHoldOffset(offset));
1282 return Address(base, offset);
1283 }
1284
1285
1286 void Assembler::LoadElementAddressForRegIndex(Register address,
1287 bool is_load,
1288 bool is_external,
1289 intptr_t cid,
1290 intptr_t index_scale,
1291 Register array,
1292 Register index) {
1293 // Note that index is expected smi-tagged, (i.e, LSL 1) for all arrays.
1294 const intptr_t shift = Utils::ShiftForPowerOfTwo(index_scale) - kSmiTagShift;
1295 const int32_t offset =
1296 is_external ? 0 : (Instance::DataOffsetFor(cid) - kHeapObjectTag);
1297 if (shift < 0) {
1298 ASSERT(shift == -1);
1299 sra(address, index, 1);
1300 addu(address, array, address);
1301 } else if (shift == 0) {
1302 addu(address, array, index);
1303 } else {
1304 sll(address, index, shift);
1305 addu(address, array, address);
1306 }
1307 if (offset != 0) {
1308 AddImmediate(address, offset);
1309 }
1310 }
1311
1312
1313 void Assembler::LoadHalfWordUnaligned(Register dst,
1314 Register addr,
1315 Register tmp) {
1316 ASSERT(dst != addr);
1317 lbu(dst, Address(addr, 0));
1318 lb(tmp, Address(addr, 1));
1319 sll(tmp, tmp, 8);
1320 or_(dst, dst, tmp);
1321 }
1322
1323
1324 void Assembler::LoadHalfWordUnsignedUnaligned(Register dst,
1325 Register addr,
1326 Register tmp) {
1327 ASSERT(dst != addr);
1328 lbu(dst, Address(addr, 0));
1329 lbu(tmp, Address(addr, 1));
1330 sll(tmp, tmp, 8);
1331 or_(dst, dst, tmp);
1332 }
1333
1334
1335 void Assembler::StoreHalfWordUnaligned(Register src,
1336 Register addr,
1337 Register tmp) {
1338 sb(src, Address(addr, 0));
1339 srl(tmp, src, 8);
1340 sb(tmp, Address(addr, 1));
1341 }
1342
1343
1344 void Assembler::LoadWordUnaligned(Register dst, Register addr, Register tmp) {
1345 // TODO(rmacnak): LWL + LWR
1346 ASSERT(dst != addr);
1347 lbu(dst, Address(addr, 0));
1348 lbu(tmp, Address(addr, 1));
1349 sll(tmp, tmp, 8);
1350 or_(dst, dst, tmp);
1351 lbu(tmp, Address(addr, 2));
1352 sll(tmp, tmp, 16);
1353 or_(dst, dst, tmp);
1354 lbu(tmp, Address(addr, 3));
1355 sll(tmp, tmp, 24);
1356 or_(dst, dst, tmp);
1357 }
1358
1359
1360 void Assembler::StoreWordUnaligned(Register src, Register addr, Register tmp) {
1361 // TODO(rmacnak): SWL + SWR
1362 sb(src, Address(addr, 0));
1363 srl(tmp, src, 8);
1364 sb(tmp, Address(addr, 1));
1365 srl(tmp, src, 16);
1366 sb(tmp, Address(addr, 2));
1367 srl(tmp, src, 24);
1368 sb(tmp, Address(addr, 3));
1369 }
1370
1371
1372 static const char* cpu_reg_names[kNumberOfCpuRegisters] = {
1373 "zr", "tmp", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2",
1374 "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5",
1375 "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra",
1376 };
1377
1378
1379 const char* Assembler::RegisterName(Register reg) {
1380 ASSERT((0 <= reg) && (reg < kNumberOfCpuRegisters));
1381 return cpu_reg_names[reg];
1382 }
1383
1384
1385 static const char* fpu_reg_names[kNumberOfFpuRegisters] = {
1386 "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
1387 "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
1388 };
1389
1390
1391 const char* Assembler::FpuRegisterName(FpuRegister reg) {
1392 ASSERT((0 <= reg) && (reg < kNumberOfFpuRegisters));
1393 return fpu_reg_names[reg];
1394 }
1395
1396
1397 void Assembler::Stop(const char* message) {
1398 if (FLAG_print_stop_message) {
1399 UNIMPLEMENTED();
1400 }
1401 Label stop;
1402 b(&stop);
1403 Emit(reinterpret_cast<int32_t>(message));
1404 Bind(&stop);
1405 break_(Instr::kStopMessageCode);
1406 }
1407
1408 } // namespace dart
1409
1410 #endif // defined TARGET_ARCH_MIPS
OLDNEW
« no previous file with comments | « runtime/vm/assembler_mips.h ('k') | runtime/vm/assembler_mips_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698