blob: 608b3bc23c015c745fd4c5fde78f8737ed1c3a02 [file] [log] [blame]
jeffhao7fbee072012-08-24 17:56:54 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "assembler_mips.h"
18
Vladimir Marko80afd022015-05-19 18:08:00 +010019#include "base/bit_utils.h"
Elliott Hughes1aa246d2012-12-13 09:29:36 -080020#include "base/casts.h"
Ian Rogers166db042013-07-26 12:05:57 -070021#include "entrypoints/quick/quick_entrypoints.h"
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +020022#include "entrypoints/quick/quick_entrypoints_enum.h"
jeffhao7fbee072012-08-24 17:56:54 -070023#include "memory_region.h"
jeffhao7fbee072012-08-24 17:56:54 -070024#include "thread.h"
25
26namespace art {
27namespace mips {
jeffhao7fbee072012-08-24 17:56:54 -070028
jeffhao7fbee072012-08-24 17:56:54 -070029std::ostream& operator<<(std::ostream& os, const DRegister& rhs) {
30 if (rhs >= D0 && rhs < kNumberOfDRegisters) {
31 os << "d" << static_cast<int>(rhs);
32 } else {
33 os << "DRegister[" << static_cast<int>(rhs) << "]";
34 }
35 return os;
36}
37
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +020038void MipsAssembler::FinalizeCode() {
39 for (auto& exception_block : exception_blocks_) {
40 EmitExceptionPoll(&exception_block);
41 }
Alexey Frunzee3fb2452016-05-10 16:08:05 -070042 EmitLiterals();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +020043 PromoteBranches();
44}
45
46void MipsAssembler::FinalizeInstructions(const MemoryRegion& region) {
Vladimir Marko10ef6942015-10-22 15:25:54 +010047 size_t number_of_delayed_adjust_pcs = cfi().NumberOfDelayedAdvancePCs();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +020048 EmitBranches();
49 Assembler::FinalizeInstructions(region);
Vladimir Marko10ef6942015-10-22 15:25:54 +010050 PatchCFI(number_of_delayed_adjust_pcs);
51}
52
53void MipsAssembler::PatchCFI(size_t number_of_delayed_adjust_pcs) {
54 if (cfi().NumberOfDelayedAdvancePCs() == 0u) {
55 DCHECK_EQ(number_of_delayed_adjust_pcs, 0u);
56 return;
57 }
58
59 typedef DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC DelayedAdvancePC;
60 const auto data = cfi().ReleaseStreamAndPrepareForDelayedAdvancePC();
61 const std::vector<uint8_t>& old_stream = data.first;
62 const std::vector<DelayedAdvancePC>& advances = data.second;
63
64 // PCs recorded before EmitBranches() need to be adjusted.
65 // PCs recorded during EmitBranches() are already adjusted.
66 // Both ranges are separately sorted but they may overlap.
67 if (kIsDebugBuild) {
68 auto cmp = [](const DelayedAdvancePC& lhs, const DelayedAdvancePC& rhs) {
69 return lhs.pc < rhs.pc;
70 };
71 CHECK(std::is_sorted(advances.begin(), advances.begin() + number_of_delayed_adjust_pcs, cmp));
72 CHECK(std::is_sorted(advances.begin() + number_of_delayed_adjust_pcs, advances.end(), cmp));
73 }
74
75 // Append initial CFI data if any.
76 size_t size = advances.size();
77 DCHECK_NE(size, 0u);
78 cfi().AppendRawData(old_stream, 0u, advances[0].stream_pos);
79 // Emit PC adjustments interleaved with the old CFI stream.
80 size_t adjust_pos = 0u;
81 size_t late_emit_pos = number_of_delayed_adjust_pcs;
82 while (adjust_pos != number_of_delayed_adjust_pcs || late_emit_pos != size) {
83 size_t adjusted_pc = (adjust_pos != number_of_delayed_adjust_pcs)
84 ? GetAdjustedPosition(advances[adjust_pos].pc)
85 : static_cast<size_t>(-1);
86 size_t late_emit_pc = (late_emit_pos != size)
87 ? advances[late_emit_pos].pc
88 : static_cast<size_t>(-1);
89 size_t advance_pc = std::min(adjusted_pc, late_emit_pc);
90 DCHECK_NE(advance_pc, static_cast<size_t>(-1));
91 size_t entry = (adjusted_pc <= late_emit_pc) ? adjust_pos : late_emit_pos;
92 if (adjusted_pc <= late_emit_pc) {
93 ++adjust_pos;
94 } else {
95 ++late_emit_pos;
96 }
97 cfi().AdvancePC(advance_pc);
98 size_t end_pos = (entry + 1u == size) ? old_stream.size() : advances[entry + 1u].stream_pos;
99 cfi().AppendRawData(old_stream, advances[entry].stream_pos, end_pos);
100 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200101}
102
103void MipsAssembler::EmitBranches() {
104 CHECK(!overwriting_);
105 // Switch from appending instructions at the end of the buffer to overwriting
106 // existing instructions (branch placeholders) in the buffer.
107 overwriting_ = true;
108 for (auto& branch : branches_) {
109 EmitBranch(&branch);
110 }
111 overwriting_ = false;
112}
113
114void MipsAssembler::Emit(uint32_t value) {
115 if (overwriting_) {
116 // Branches to labels are emitted into their placeholders here.
117 buffer_.Store<uint32_t>(overwrite_location_, value);
118 overwrite_location_ += sizeof(uint32_t);
119 } else {
120 // Other instructions are simply appended at the end here.
121 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
122 buffer_.Emit<uint32_t>(value);
123 }
jeffhao7fbee072012-08-24 17:56:54 -0700124}
125
126void MipsAssembler::EmitR(int opcode, Register rs, Register rt, Register rd, int shamt, int funct) {
127 CHECK_NE(rs, kNoRegister);
128 CHECK_NE(rt, kNoRegister);
129 CHECK_NE(rd, kNoRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200130 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
131 static_cast<uint32_t>(rs) << kRsShift |
132 static_cast<uint32_t>(rt) << kRtShift |
133 static_cast<uint32_t>(rd) << kRdShift |
134 shamt << kShamtShift |
135 funct;
jeffhao7fbee072012-08-24 17:56:54 -0700136 Emit(encoding);
137}
138
139void MipsAssembler::EmitI(int opcode, Register rs, Register rt, uint16_t imm) {
140 CHECK_NE(rs, kNoRegister);
141 CHECK_NE(rt, kNoRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200142 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
143 static_cast<uint32_t>(rs) << kRsShift |
144 static_cast<uint32_t>(rt) << kRtShift |
145 imm;
jeffhao7fbee072012-08-24 17:56:54 -0700146 Emit(encoding);
147}
148
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200149void MipsAssembler::EmitI21(int opcode, Register rs, uint32_t imm21) {
150 CHECK_NE(rs, kNoRegister);
151 CHECK(IsUint<21>(imm21)) << imm21;
152 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
153 static_cast<uint32_t>(rs) << kRsShift |
154 imm21;
jeffhao7fbee072012-08-24 17:56:54 -0700155 Emit(encoding);
156}
157
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200158void MipsAssembler::EmitI26(int opcode, uint32_t imm26) {
159 CHECK(IsUint<26>(imm26)) << imm26;
160 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | imm26;
161 Emit(encoding);
162}
163
164void MipsAssembler::EmitFR(int opcode, int fmt, FRegister ft, FRegister fs, FRegister fd,
165 int funct) {
jeffhao7fbee072012-08-24 17:56:54 -0700166 CHECK_NE(ft, kNoFRegister);
167 CHECK_NE(fs, kNoFRegister);
168 CHECK_NE(fd, kNoFRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200169 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
170 fmt << kFmtShift |
171 static_cast<uint32_t>(ft) << kFtShift |
172 static_cast<uint32_t>(fs) << kFsShift |
173 static_cast<uint32_t>(fd) << kFdShift |
174 funct;
jeffhao7fbee072012-08-24 17:56:54 -0700175 Emit(encoding);
176}
177
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200178void MipsAssembler::EmitFI(int opcode, int fmt, FRegister ft, uint16_t imm) {
179 CHECK_NE(ft, kNoFRegister);
180 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
181 fmt << kFmtShift |
182 static_cast<uint32_t>(ft) << kFtShift |
183 imm;
jeffhao7fbee072012-08-24 17:56:54 -0700184 Emit(encoding);
185}
186
jeffhao7fbee072012-08-24 17:56:54 -0700187void MipsAssembler::Addu(Register rd, Register rs, Register rt) {
188 EmitR(0, rs, rt, rd, 0, 0x21);
189}
190
jeffhao7fbee072012-08-24 17:56:54 -0700191void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) {
192 EmitI(0x9, rs, rt, imm16);
193}
194
jeffhao7fbee072012-08-24 17:56:54 -0700195void MipsAssembler::Subu(Register rd, Register rs, Register rt) {
196 EmitR(0, rs, rt, rd, 0, 0x23);
197}
198
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200199void MipsAssembler::MultR2(Register rs, Register rt) {
200 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700201 EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x18);
202}
203
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200204void MipsAssembler::MultuR2(Register rs, Register rt) {
205 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700206 EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x19);
207}
208
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200209void MipsAssembler::DivR2(Register rs, Register rt) {
210 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700211 EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1a);
212}
213
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200214void MipsAssembler::DivuR2(Register rs, Register rt) {
215 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700216 EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1b);
217}
218
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200219void MipsAssembler::MulR2(Register rd, Register rs, Register rt) {
220 CHECK(!IsR6());
221 EmitR(0x1c, rs, rt, rd, 0, 2);
222}
223
224void MipsAssembler::DivR2(Register rd, Register rs, Register rt) {
225 CHECK(!IsR6());
226 DivR2(rs, rt);
227 Mflo(rd);
228}
229
230void MipsAssembler::ModR2(Register rd, Register rs, Register rt) {
231 CHECK(!IsR6());
232 DivR2(rs, rt);
233 Mfhi(rd);
234}
235
236void MipsAssembler::DivuR2(Register rd, Register rs, Register rt) {
237 CHECK(!IsR6());
238 DivuR2(rs, rt);
239 Mflo(rd);
240}
241
242void MipsAssembler::ModuR2(Register rd, Register rs, Register rt) {
243 CHECK(!IsR6());
244 DivuR2(rs, rt);
245 Mfhi(rd);
246}
247
248void MipsAssembler::MulR6(Register rd, Register rs, Register rt) {
249 CHECK(IsR6());
250 EmitR(0, rs, rt, rd, 2, 0x18);
251}
252
Alexey Frunze7e99e052015-11-24 19:28:01 -0800253void MipsAssembler::MuhR6(Register rd, Register rs, Register rt) {
254 CHECK(IsR6());
255 EmitR(0, rs, rt, rd, 3, 0x18);
256}
257
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200258void MipsAssembler::MuhuR6(Register rd, Register rs, Register rt) {
259 CHECK(IsR6());
260 EmitR(0, rs, rt, rd, 3, 0x19);
261}
262
263void MipsAssembler::DivR6(Register rd, Register rs, Register rt) {
264 CHECK(IsR6());
265 EmitR(0, rs, rt, rd, 2, 0x1a);
266}
267
268void MipsAssembler::ModR6(Register rd, Register rs, Register rt) {
269 CHECK(IsR6());
270 EmitR(0, rs, rt, rd, 3, 0x1a);
271}
272
273void MipsAssembler::DivuR6(Register rd, Register rs, Register rt) {
274 CHECK(IsR6());
275 EmitR(0, rs, rt, rd, 2, 0x1b);
276}
277
278void MipsAssembler::ModuR6(Register rd, Register rs, Register rt) {
279 CHECK(IsR6());
280 EmitR(0, rs, rt, rd, 3, 0x1b);
281}
282
jeffhao7fbee072012-08-24 17:56:54 -0700283void MipsAssembler::And(Register rd, Register rs, Register rt) {
284 EmitR(0, rs, rt, rd, 0, 0x24);
285}
286
287void MipsAssembler::Andi(Register rt, Register rs, uint16_t imm16) {
288 EmitI(0xc, rs, rt, imm16);
289}
290
291void MipsAssembler::Or(Register rd, Register rs, Register rt) {
292 EmitR(0, rs, rt, rd, 0, 0x25);
293}
294
295void MipsAssembler::Ori(Register rt, Register rs, uint16_t imm16) {
296 EmitI(0xd, rs, rt, imm16);
297}
298
299void MipsAssembler::Xor(Register rd, Register rs, Register rt) {
300 EmitR(0, rs, rt, rd, 0, 0x26);
301}
302
303void MipsAssembler::Xori(Register rt, Register rs, uint16_t imm16) {
304 EmitI(0xe, rs, rt, imm16);
305}
306
307void MipsAssembler::Nor(Register rd, Register rs, Register rt) {
308 EmitR(0, rs, rt, rd, 0, 0x27);
309}
310
Chris Larsene3845472015-11-18 12:27:15 -0800311void MipsAssembler::Movz(Register rd, Register rs, Register rt) {
312 CHECK(!IsR6());
313 EmitR(0, rs, rt, rd, 0, 0x0A);
314}
315
316void MipsAssembler::Movn(Register rd, Register rs, Register rt) {
317 CHECK(!IsR6());
318 EmitR(0, rs, rt, rd, 0, 0x0B);
319}
320
321void MipsAssembler::Seleqz(Register rd, Register rs, Register rt) {
322 CHECK(IsR6());
323 EmitR(0, rs, rt, rd, 0, 0x35);
324}
325
326void MipsAssembler::Selnez(Register rd, Register rs, Register rt) {
327 CHECK(IsR6());
328 EmitR(0, rs, rt, rd, 0, 0x37);
329}
330
331void MipsAssembler::ClzR6(Register rd, Register rs) {
332 CHECK(IsR6());
333 EmitR(0, rs, static_cast<Register>(0), rd, 0x01, 0x10);
334}
335
336void MipsAssembler::ClzR2(Register rd, Register rs) {
337 CHECK(!IsR6());
338 EmitR(0x1C, rs, rd, rd, 0, 0x20);
339}
340
341void MipsAssembler::CloR6(Register rd, Register rs) {
342 CHECK(IsR6());
343 EmitR(0, rs, static_cast<Register>(0), rd, 0x01, 0x11);
344}
345
346void MipsAssembler::CloR2(Register rd, Register rs) {
347 CHECK(!IsR6());
348 EmitR(0x1C, rs, rd, rd, 0, 0x21);
349}
350
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200351void MipsAssembler::Seb(Register rd, Register rt) {
352 EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x10, 0x20);
jeffhao7fbee072012-08-24 17:56:54 -0700353}
354
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200355void MipsAssembler::Seh(Register rd, Register rt) {
356 EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x18, 0x20);
jeffhao7fbee072012-08-24 17:56:54 -0700357}
358
Chris Larsen3f8bf652015-10-28 10:08:56 -0700359void MipsAssembler::Wsbh(Register rd, Register rt) {
360 EmitR(0x1f, static_cast<Register>(0), rt, rd, 2, 0x20);
361}
362
Chris Larsen70014c82015-11-18 12:26:08 -0800363void MipsAssembler::Bitswap(Register rd, Register rt) {
364 CHECK(IsR6());
365 EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x0, 0x20);
366}
367
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200368void MipsAssembler::Sll(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700369 CHECK(IsUint<5>(shamt)) << shamt;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200370 EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x00);
jeffhao7fbee072012-08-24 17:56:54 -0700371}
372
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200373void MipsAssembler::Srl(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700374 CHECK(IsUint<5>(shamt)) << shamt;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200375 EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x02);
376}
377
Chris Larsen3f8bf652015-10-28 10:08:56 -0700378void MipsAssembler::Rotr(Register rd, Register rt, int shamt) {
379 CHECK(IsUint<5>(shamt)) << shamt;
380 EmitR(0, static_cast<Register>(1), rt, rd, shamt, 0x02);
381}
382
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200383void MipsAssembler::Sra(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700384 CHECK(IsUint<5>(shamt)) << shamt;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200385 EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x03);
386}
387
388void MipsAssembler::Sllv(Register rd, Register rt, Register rs) {
jeffhao7fbee072012-08-24 17:56:54 -0700389 EmitR(0, rs, rt, rd, 0, 0x04);
390}
391
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200392void MipsAssembler::Srlv(Register rd, Register rt, Register rs) {
jeffhao7fbee072012-08-24 17:56:54 -0700393 EmitR(0, rs, rt, rd, 0, 0x06);
394}
395
Chris Larsene16ce5a2015-11-18 12:30:20 -0800396void MipsAssembler::Rotrv(Register rd, Register rt, Register rs) {
397 EmitR(0, rs, rt, rd, 1, 0x06);
398}
399
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200400void MipsAssembler::Srav(Register rd, Register rt, Register rs) {
jeffhao7fbee072012-08-24 17:56:54 -0700401 EmitR(0, rs, rt, rd, 0, 0x07);
402}
403
Alexey Frunze5c7aed32015-11-25 19:41:54 -0800404void MipsAssembler::Ext(Register rd, Register rt, int pos, int size) {
405 CHECK(IsUint<5>(pos)) << pos;
406 CHECK(0 < size && size <= 32) << size;
407 CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size;
408 EmitR(0x1f, rt, rd, static_cast<Register>(size - 1), pos, 0x00);
409}
410
411void MipsAssembler::Ins(Register rd, Register rt, int pos, int size) {
412 CHECK(IsUint<5>(pos)) << pos;
413 CHECK(0 < size && size <= 32) << size;
414 CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size;
415 EmitR(0x1f, rt, rd, static_cast<Register>(pos + size - 1), pos, 0x04);
416}
417
jeffhao7fbee072012-08-24 17:56:54 -0700418void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) {
419 EmitI(0x20, rs, rt, imm16);
420}
421
422void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) {
423 EmitI(0x21, rs, rt, imm16);
424}
425
426void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) {
427 EmitI(0x23, rs, rt, imm16);
428}
429
Chris Larsen3acee732015-11-18 13:31:08 -0800430void MipsAssembler::Lwl(Register rt, Register rs, uint16_t imm16) {
431 CHECK(!IsR6());
432 EmitI(0x22, rs, rt, imm16);
433}
434
435void MipsAssembler::Lwr(Register rt, Register rs, uint16_t imm16) {
436 CHECK(!IsR6());
437 EmitI(0x26, rs, rt, imm16);
438}
439
jeffhao7fbee072012-08-24 17:56:54 -0700440void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) {
441 EmitI(0x24, rs, rt, imm16);
442}
443
444void MipsAssembler::Lhu(Register rt, Register rs, uint16_t imm16) {
445 EmitI(0x25, rs, rt, imm16);
446}
447
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700448void MipsAssembler::Lwpc(Register rs, uint32_t imm19) {
449 CHECK(IsR6());
450 CHECK(IsUint<19>(imm19)) << imm19;
451 EmitI21(0x3B, rs, (0x01 << 19) | imm19);
452}
453
jeffhao7fbee072012-08-24 17:56:54 -0700454void MipsAssembler::Lui(Register rt, uint16_t imm16) {
455 EmitI(0xf, static_cast<Register>(0), rt, imm16);
456}
457
Alexey Frunzecad3a4c2016-06-07 23:40:37 -0700458void MipsAssembler::Aui(Register rt, Register rs, uint16_t imm16) {
459 CHECK(IsR6());
460 EmitI(0xf, rs, rt, imm16);
461}
462
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200463void MipsAssembler::Sync(uint32_t stype) {
464 EmitR(0, static_cast<Register>(0), static_cast<Register>(0), static_cast<Register>(0),
465 stype & 0x1f, 0xf);
466}
467
jeffhao7fbee072012-08-24 17:56:54 -0700468void MipsAssembler::Mfhi(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200469 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700470 EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rd, 0, 0x10);
471}
472
473void MipsAssembler::Mflo(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200474 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700475 EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rd, 0, 0x12);
476}
477
478void MipsAssembler::Sb(Register rt, Register rs, uint16_t imm16) {
479 EmitI(0x28, rs, rt, imm16);
480}
481
482void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) {
483 EmitI(0x29, rs, rt, imm16);
484}
485
486void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) {
487 EmitI(0x2b, rs, rt, imm16);
488}
489
Chris Larsen3acee732015-11-18 13:31:08 -0800490void MipsAssembler::Swl(Register rt, Register rs, uint16_t imm16) {
491 CHECK(!IsR6());
492 EmitI(0x2a, rs, rt, imm16);
493}
494
495void MipsAssembler::Swr(Register rt, Register rs, uint16_t imm16) {
496 CHECK(!IsR6());
497 EmitI(0x2e, rs, rt, imm16);
498}
499
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700500void MipsAssembler::LlR2(Register rt, Register base, int16_t imm16) {
501 CHECK(!IsR6());
502 EmitI(0x30, base, rt, imm16);
503}
504
505void MipsAssembler::ScR2(Register rt, Register base, int16_t imm16) {
506 CHECK(!IsR6());
507 EmitI(0x38, base, rt, imm16);
508}
509
510void MipsAssembler::LlR6(Register rt, Register base, int16_t imm9) {
511 CHECK(IsR6());
512 CHECK(IsInt<9>(imm9));
513 EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36);
514}
515
516void MipsAssembler::ScR6(Register rt, Register base, int16_t imm9) {
517 CHECK(IsR6());
518 CHECK(IsInt<9>(imm9));
519 EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26);
520}
521
jeffhao7fbee072012-08-24 17:56:54 -0700522void MipsAssembler::Slt(Register rd, Register rs, Register rt) {
523 EmitR(0, rs, rt, rd, 0, 0x2a);
524}
525
526void MipsAssembler::Sltu(Register rd, Register rs, Register rt) {
527 EmitR(0, rs, rt, rd, 0, 0x2b);
528}
529
530void MipsAssembler::Slti(Register rt, Register rs, uint16_t imm16) {
531 EmitI(0xa, rs, rt, imm16);
532}
533
534void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) {
535 EmitI(0xb, rs, rt, imm16);
536}
537
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200538void MipsAssembler::B(uint16_t imm16) {
539 EmitI(0x4, static_cast<Register>(0), static_cast<Register>(0), imm16);
540}
541
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700542void MipsAssembler::Bal(uint16_t imm16) {
543 EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x11), imm16);
544}
545
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200546void MipsAssembler::Beq(Register rs, Register rt, uint16_t imm16) {
jeffhao7fbee072012-08-24 17:56:54 -0700547 EmitI(0x4, rs, rt, imm16);
548}
549
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200550void MipsAssembler::Bne(Register rs, Register rt, uint16_t imm16) {
jeffhao7fbee072012-08-24 17:56:54 -0700551 EmitI(0x5, rs, rt, imm16);
552}
553
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200554void MipsAssembler::Beqz(Register rt, uint16_t imm16) {
555 Beq(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700556}
557
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200558void MipsAssembler::Bnez(Register rt, uint16_t imm16) {
559 Bne(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700560}
561
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200562void MipsAssembler::Bltz(Register rt, uint16_t imm16) {
563 EmitI(0x1, rt, static_cast<Register>(0), imm16);
564}
565
566void MipsAssembler::Bgez(Register rt, uint16_t imm16) {
567 EmitI(0x1, rt, static_cast<Register>(0x1), imm16);
568}
569
570void MipsAssembler::Blez(Register rt, uint16_t imm16) {
571 EmitI(0x6, rt, static_cast<Register>(0), imm16);
572}
573
574void MipsAssembler::Bgtz(Register rt, uint16_t imm16) {
575 EmitI(0x7, rt, static_cast<Register>(0), imm16);
576}
577
Chris Larsenb74353a2015-11-20 09:07:09 -0800578void MipsAssembler::Bc1f(uint16_t imm16) {
579 Bc1f(0, imm16);
580}
581
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800582void MipsAssembler::Bc1f(int cc, uint16_t imm16) {
583 CHECK(!IsR6());
584 CHECK(IsUint<3>(cc)) << cc;
585 EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>(cc << 2), imm16);
586}
587
Chris Larsenb74353a2015-11-20 09:07:09 -0800588void MipsAssembler::Bc1t(uint16_t imm16) {
589 Bc1t(0, imm16);
590}
591
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800592void MipsAssembler::Bc1t(int cc, uint16_t imm16) {
593 CHECK(!IsR6());
594 CHECK(IsUint<3>(cc)) << cc;
595 EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>((cc << 2) | 1), imm16);
596}
597
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200598void MipsAssembler::J(uint32_t addr26) {
599 EmitI26(0x2, addr26);
600}
601
602void MipsAssembler::Jal(uint32_t addr26) {
603 EmitI26(0x3, addr26);
604}
605
606void MipsAssembler::Jalr(Register rd, Register rs) {
607 EmitR(0, rs, static_cast<Register>(0), rd, 0, 0x09);
jeffhao7fbee072012-08-24 17:56:54 -0700608}
609
610void MipsAssembler::Jalr(Register rs) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200611 Jalr(RA, rs);
612}
613
614void MipsAssembler::Jr(Register rs) {
615 Jalr(ZERO, rs);
616}
617
618void MipsAssembler::Nal() {
619 EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x10), 0);
620}
621
622void MipsAssembler::Auipc(Register rs, uint16_t imm16) {
623 CHECK(IsR6());
624 EmitI(0x3B, rs, static_cast<Register>(0x1E), imm16);
625}
626
627void MipsAssembler::Addiupc(Register rs, uint32_t imm19) {
628 CHECK(IsR6());
629 CHECK(IsUint<19>(imm19)) << imm19;
630 EmitI21(0x3B, rs, imm19);
631}
632
633void MipsAssembler::Bc(uint32_t imm26) {
634 CHECK(IsR6());
635 EmitI26(0x32, imm26);
636}
637
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700638void MipsAssembler::Balc(uint32_t imm26) {
639 CHECK(IsR6());
640 EmitI26(0x3A, imm26);
641}
642
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200643void MipsAssembler::Jic(Register rt, uint16_t imm16) {
644 CHECK(IsR6());
645 EmitI(0x36, static_cast<Register>(0), rt, imm16);
646}
647
648void MipsAssembler::Jialc(Register rt, uint16_t imm16) {
649 CHECK(IsR6());
650 EmitI(0x3E, static_cast<Register>(0), rt, imm16);
651}
652
653void MipsAssembler::Bltc(Register rs, Register rt, uint16_t imm16) {
654 CHECK(IsR6());
655 CHECK_NE(rs, ZERO);
656 CHECK_NE(rt, ZERO);
657 CHECK_NE(rs, rt);
658 EmitI(0x17, rs, rt, imm16);
659}
660
661void MipsAssembler::Bltzc(Register rt, uint16_t imm16) {
662 CHECK(IsR6());
663 CHECK_NE(rt, ZERO);
664 EmitI(0x17, rt, rt, imm16);
665}
666
667void MipsAssembler::Bgtzc(Register rt, uint16_t imm16) {
668 CHECK(IsR6());
669 CHECK_NE(rt, ZERO);
670 EmitI(0x17, static_cast<Register>(0), rt, imm16);
671}
672
673void MipsAssembler::Bgec(Register rs, Register rt, uint16_t imm16) {
674 CHECK(IsR6());
675 CHECK_NE(rs, ZERO);
676 CHECK_NE(rt, ZERO);
677 CHECK_NE(rs, rt);
678 EmitI(0x16, rs, rt, imm16);
679}
680
681void MipsAssembler::Bgezc(Register rt, uint16_t imm16) {
682 CHECK(IsR6());
683 CHECK_NE(rt, ZERO);
684 EmitI(0x16, rt, rt, imm16);
685}
686
687void MipsAssembler::Blezc(Register rt, uint16_t imm16) {
688 CHECK(IsR6());
689 CHECK_NE(rt, ZERO);
690 EmitI(0x16, static_cast<Register>(0), rt, imm16);
691}
692
693void MipsAssembler::Bltuc(Register rs, Register rt, uint16_t imm16) {
694 CHECK(IsR6());
695 CHECK_NE(rs, ZERO);
696 CHECK_NE(rt, ZERO);
697 CHECK_NE(rs, rt);
698 EmitI(0x7, rs, rt, imm16);
699}
700
701void MipsAssembler::Bgeuc(Register rs, Register rt, uint16_t imm16) {
702 CHECK(IsR6());
703 CHECK_NE(rs, ZERO);
704 CHECK_NE(rt, ZERO);
705 CHECK_NE(rs, rt);
706 EmitI(0x6, rs, rt, imm16);
707}
708
709void MipsAssembler::Beqc(Register rs, Register rt, uint16_t imm16) {
710 CHECK(IsR6());
711 CHECK_NE(rs, ZERO);
712 CHECK_NE(rt, ZERO);
713 CHECK_NE(rs, rt);
714 EmitI(0x8, std::min(rs, rt), std::max(rs, rt), imm16);
715}
716
717void MipsAssembler::Bnec(Register rs, Register rt, uint16_t imm16) {
718 CHECK(IsR6());
719 CHECK_NE(rs, ZERO);
720 CHECK_NE(rt, ZERO);
721 CHECK_NE(rs, rt);
722 EmitI(0x18, std::min(rs, rt), std::max(rs, rt), imm16);
723}
724
725void MipsAssembler::Beqzc(Register rs, uint32_t imm21) {
726 CHECK(IsR6());
727 CHECK_NE(rs, ZERO);
728 EmitI21(0x36, rs, imm21);
729}
730
731void MipsAssembler::Bnezc(Register rs, uint32_t imm21) {
732 CHECK(IsR6());
733 CHECK_NE(rs, ZERO);
734 EmitI21(0x3E, rs, imm21);
735}
736
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800737void MipsAssembler::Bc1eqz(FRegister ft, uint16_t imm16) {
738 CHECK(IsR6());
739 EmitFI(0x11, 0x9, ft, imm16);
740}
741
742void MipsAssembler::Bc1nez(FRegister ft, uint16_t imm16) {
743 CHECK(IsR6());
744 EmitFI(0x11, 0xD, ft, imm16);
745}
746
747void MipsAssembler::EmitBcondR2(BranchCondition cond, Register rs, Register rt, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200748 switch (cond) {
749 case kCondLTZ:
750 CHECK_EQ(rt, ZERO);
751 Bltz(rs, imm16);
752 break;
753 case kCondGEZ:
754 CHECK_EQ(rt, ZERO);
755 Bgez(rs, imm16);
756 break;
757 case kCondLEZ:
758 CHECK_EQ(rt, ZERO);
759 Blez(rs, imm16);
760 break;
761 case kCondGTZ:
762 CHECK_EQ(rt, ZERO);
763 Bgtz(rs, imm16);
764 break;
765 case kCondEQ:
766 Beq(rs, rt, imm16);
767 break;
768 case kCondNE:
769 Bne(rs, rt, imm16);
770 break;
771 case kCondEQZ:
772 CHECK_EQ(rt, ZERO);
773 Beqz(rs, imm16);
774 break;
775 case kCondNEZ:
776 CHECK_EQ(rt, ZERO);
777 Bnez(rs, imm16);
778 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800779 case kCondF:
780 CHECK_EQ(rt, ZERO);
781 Bc1f(static_cast<int>(rs), imm16);
782 break;
783 case kCondT:
784 CHECK_EQ(rt, ZERO);
785 Bc1t(static_cast<int>(rs), imm16);
786 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200787 case kCondLT:
788 case kCondGE:
789 case kCondLE:
790 case kCondGT:
791 case kCondLTU:
792 case kCondGEU:
793 case kUncond:
794 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
795 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
796 LOG(FATAL) << "Unexpected branch condition " << cond;
797 UNREACHABLE();
798 }
799}
800
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800801void MipsAssembler::EmitBcondR6(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200802 switch (cond) {
803 case kCondLT:
804 Bltc(rs, rt, imm16_21);
805 break;
806 case kCondGE:
807 Bgec(rs, rt, imm16_21);
808 break;
809 case kCondLE:
810 Bgec(rt, rs, imm16_21);
811 break;
812 case kCondGT:
813 Bltc(rt, rs, imm16_21);
814 break;
815 case kCondLTZ:
816 CHECK_EQ(rt, ZERO);
817 Bltzc(rs, imm16_21);
818 break;
819 case kCondGEZ:
820 CHECK_EQ(rt, ZERO);
821 Bgezc(rs, imm16_21);
822 break;
823 case kCondLEZ:
824 CHECK_EQ(rt, ZERO);
825 Blezc(rs, imm16_21);
826 break;
827 case kCondGTZ:
828 CHECK_EQ(rt, ZERO);
829 Bgtzc(rs, imm16_21);
830 break;
831 case kCondEQ:
832 Beqc(rs, rt, imm16_21);
833 break;
834 case kCondNE:
835 Bnec(rs, rt, imm16_21);
836 break;
837 case kCondEQZ:
838 CHECK_EQ(rt, ZERO);
839 Beqzc(rs, imm16_21);
840 break;
841 case kCondNEZ:
842 CHECK_EQ(rt, ZERO);
843 Bnezc(rs, imm16_21);
844 break;
845 case kCondLTU:
846 Bltuc(rs, rt, imm16_21);
847 break;
848 case kCondGEU:
849 Bgeuc(rs, rt, imm16_21);
850 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800851 case kCondF:
852 CHECK_EQ(rt, ZERO);
853 Bc1eqz(static_cast<FRegister>(rs), imm16_21);
854 break;
855 case kCondT:
856 CHECK_EQ(rt, ZERO);
857 Bc1nez(static_cast<FRegister>(rs), imm16_21);
858 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200859 case kUncond:
860 LOG(FATAL) << "Unexpected branch condition " << cond;
861 UNREACHABLE();
862 }
jeffhao7fbee072012-08-24 17:56:54 -0700863}
864
865void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) {
866 EmitFR(0x11, 0x10, ft, fs, fd, 0x0);
867}
868
869void MipsAssembler::SubS(FRegister fd, FRegister fs, FRegister ft) {
870 EmitFR(0x11, 0x10, ft, fs, fd, 0x1);
871}
872
873void MipsAssembler::MulS(FRegister fd, FRegister fs, FRegister ft) {
874 EmitFR(0x11, 0x10, ft, fs, fd, 0x2);
875}
876
877void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) {
878 EmitFR(0x11, 0x10, ft, fs, fd, 0x3);
879}
880
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200881void MipsAssembler::AddD(FRegister fd, FRegister fs, FRegister ft) {
882 EmitFR(0x11, 0x11, ft, fs, fd, 0x0);
jeffhao7fbee072012-08-24 17:56:54 -0700883}
884
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200885void MipsAssembler::SubD(FRegister fd, FRegister fs, FRegister ft) {
886 EmitFR(0x11, 0x11, ft, fs, fd, 0x1);
jeffhao7fbee072012-08-24 17:56:54 -0700887}
888
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200889void MipsAssembler::MulD(FRegister fd, FRegister fs, FRegister ft) {
890 EmitFR(0x11, 0x11, ft, fs, fd, 0x2);
jeffhao7fbee072012-08-24 17:56:54 -0700891}
892
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200893void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) {
894 EmitFR(0x11, 0x11, ft, fs, fd, 0x3);
jeffhao7fbee072012-08-24 17:56:54 -0700895}
896
Chris Larsenb74353a2015-11-20 09:07:09 -0800897void MipsAssembler::SqrtS(FRegister fd, FRegister fs) {
898 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x4);
899}
900
901void MipsAssembler::SqrtD(FRegister fd, FRegister fs) {
902 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x4);
903}
904
905void MipsAssembler::AbsS(FRegister fd, FRegister fs) {
906 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x5);
907}
908
909void MipsAssembler::AbsD(FRegister fd, FRegister fs) {
910 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x5);
911}
912
jeffhao7fbee072012-08-24 17:56:54 -0700913void MipsAssembler::MovS(FRegister fd, FRegister fs) {
914 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6);
915}
916
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200917void MipsAssembler::MovD(FRegister fd, FRegister fs) {
918 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x6);
919}
920
921void MipsAssembler::NegS(FRegister fd, FRegister fs) {
922 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x7);
923}
924
925void MipsAssembler::NegD(FRegister fd, FRegister fs) {
926 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7);
927}
928
Chris Larsenb74353a2015-11-20 09:07:09 -0800929void MipsAssembler::CunS(FRegister fs, FRegister ft) {
930 CunS(0, fs, ft);
931}
932
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800933void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) {
934 CHECK(!IsR6());
935 CHECK(IsUint<3>(cc)) << cc;
936 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x31);
937}
938
Chris Larsenb74353a2015-11-20 09:07:09 -0800939void MipsAssembler::CeqS(FRegister fs, FRegister ft) {
940 CeqS(0, fs, ft);
941}
942
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800943void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) {
944 CHECK(!IsR6());
945 CHECK(IsUint<3>(cc)) << cc;
946 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x32);
947}
948
Chris Larsenb74353a2015-11-20 09:07:09 -0800949void MipsAssembler::CueqS(FRegister fs, FRegister ft) {
950 CueqS(0, fs, ft);
951}
952
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800953void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) {
954 CHECK(!IsR6());
955 CHECK(IsUint<3>(cc)) << cc;
956 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x33);
957}
958
Chris Larsenb74353a2015-11-20 09:07:09 -0800959void MipsAssembler::ColtS(FRegister fs, FRegister ft) {
960 ColtS(0, fs, ft);
961}
962
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800963void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) {
964 CHECK(!IsR6());
965 CHECK(IsUint<3>(cc)) << cc;
966 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x34);
967}
968
Chris Larsenb74353a2015-11-20 09:07:09 -0800969void MipsAssembler::CultS(FRegister fs, FRegister ft) {
970 CultS(0, fs, ft);
971}
972
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800973void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) {
974 CHECK(!IsR6());
975 CHECK(IsUint<3>(cc)) << cc;
976 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x35);
977}
978
Chris Larsenb74353a2015-11-20 09:07:09 -0800979void MipsAssembler::ColeS(FRegister fs, FRegister ft) {
980 ColeS(0, fs, ft);
981}
982
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800983void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) {
984 CHECK(!IsR6());
985 CHECK(IsUint<3>(cc)) << cc;
986 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x36);
987}
988
Chris Larsenb74353a2015-11-20 09:07:09 -0800989void MipsAssembler::CuleS(FRegister fs, FRegister ft) {
990 CuleS(0, fs, ft);
991}
992
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800993void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) {
994 CHECK(!IsR6());
995 CHECK(IsUint<3>(cc)) << cc;
996 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x37);
997}
998
Chris Larsenb74353a2015-11-20 09:07:09 -0800999void MipsAssembler::CunD(FRegister fs, FRegister ft) {
1000 CunD(0, fs, ft);
1001}
1002
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001003void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) {
1004 CHECK(!IsR6());
1005 CHECK(IsUint<3>(cc)) << cc;
1006 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x31);
1007}
1008
Chris Larsenb74353a2015-11-20 09:07:09 -08001009void MipsAssembler::CeqD(FRegister fs, FRegister ft) {
1010 CeqD(0, fs, ft);
1011}
1012
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001013void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) {
1014 CHECK(!IsR6());
1015 CHECK(IsUint<3>(cc)) << cc;
1016 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x32);
1017}
1018
Chris Larsenb74353a2015-11-20 09:07:09 -08001019void MipsAssembler::CueqD(FRegister fs, FRegister ft) {
1020 CueqD(0, fs, ft);
1021}
1022
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001023void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) {
1024 CHECK(!IsR6());
1025 CHECK(IsUint<3>(cc)) << cc;
1026 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x33);
1027}
1028
Chris Larsenb74353a2015-11-20 09:07:09 -08001029void MipsAssembler::ColtD(FRegister fs, FRegister ft) {
1030 ColtD(0, fs, ft);
1031}
1032
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001033void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) {
1034 CHECK(!IsR6());
1035 CHECK(IsUint<3>(cc)) << cc;
1036 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x34);
1037}
1038
Chris Larsenb74353a2015-11-20 09:07:09 -08001039void MipsAssembler::CultD(FRegister fs, FRegister ft) {
1040 CultD(0, fs, ft);
1041}
1042
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001043void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) {
1044 CHECK(!IsR6());
1045 CHECK(IsUint<3>(cc)) << cc;
1046 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x35);
1047}
1048
Chris Larsenb74353a2015-11-20 09:07:09 -08001049void MipsAssembler::ColeD(FRegister fs, FRegister ft) {
1050 ColeD(0, fs, ft);
1051}
1052
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001053void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) {
1054 CHECK(!IsR6());
1055 CHECK(IsUint<3>(cc)) << cc;
1056 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x36);
1057}
1058
Chris Larsenb74353a2015-11-20 09:07:09 -08001059void MipsAssembler::CuleD(FRegister fs, FRegister ft) {
1060 CuleD(0, fs, ft);
1061}
1062
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001063void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) {
1064 CHECK(!IsR6());
1065 CHECK(IsUint<3>(cc)) << cc;
1066 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x37);
1067}
1068
1069void MipsAssembler::CmpUnS(FRegister fd, FRegister fs, FRegister ft) {
1070 CHECK(IsR6());
1071 EmitFR(0x11, 0x14, ft, fs, fd, 0x01);
1072}
1073
1074void MipsAssembler::CmpEqS(FRegister fd, FRegister fs, FRegister ft) {
1075 CHECK(IsR6());
1076 EmitFR(0x11, 0x14, ft, fs, fd, 0x02);
1077}
1078
1079void MipsAssembler::CmpUeqS(FRegister fd, FRegister fs, FRegister ft) {
1080 CHECK(IsR6());
1081 EmitFR(0x11, 0x14, ft, fs, fd, 0x03);
1082}
1083
1084void MipsAssembler::CmpLtS(FRegister fd, FRegister fs, FRegister ft) {
1085 CHECK(IsR6());
1086 EmitFR(0x11, 0x14, ft, fs, fd, 0x04);
1087}
1088
1089void MipsAssembler::CmpUltS(FRegister fd, FRegister fs, FRegister ft) {
1090 CHECK(IsR6());
1091 EmitFR(0x11, 0x14, ft, fs, fd, 0x05);
1092}
1093
1094void MipsAssembler::CmpLeS(FRegister fd, FRegister fs, FRegister ft) {
1095 CHECK(IsR6());
1096 EmitFR(0x11, 0x14, ft, fs, fd, 0x06);
1097}
1098
1099void MipsAssembler::CmpUleS(FRegister fd, FRegister fs, FRegister ft) {
1100 CHECK(IsR6());
1101 EmitFR(0x11, 0x14, ft, fs, fd, 0x07);
1102}
1103
1104void MipsAssembler::CmpOrS(FRegister fd, FRegister fs, FRegister ft) {
1105 CHECK(IsR6());
1106 EmitFR(0x11, 0x14, ft, fs, fd, 0x11);
1107}
1108
1109void MipsAssembler::CmpUneS(FRegister fd, FRegister fs, FRegister ft) {
1110 CHECK(IsR6());
1111 EmitFR(0x11, 0x14, ft, fs, fd, 0x12);
1112}
1113
1114void MipsAssembler::CmpNeS(FRegister fd, FRegister fs, FRegister ft) {
1115 CHECK(IsR6());
1116 EmitFR(0x11, 0x14, ft, fs, fd, 0x13);
1117}
1118
1119void MipsAssembler::CmpUnD(FRegister fd, FRegister fs, FRegister ft) {
1120 CHECK(IsR6());
1121 EmitFR(0x11, 0x15, ft, fs, fd, 0x01);
1122}
1123
1124void MipsAssembler::CmpEqD(FRegister fd, FRegister fs, FRegister ft) {
1125 CHECK(IsR6());
1126 EmitFR(0x11, 0x15, ft, fs, fd, 0x02);
1127}
1128
1129void MipsAssembler::CmpUeqD(FRegister fd, FRegister fs, FRegister ft) {
1130 CHECK(IsR6());
1131 EmitFR(0x11, 0x15, ft, fs, fd, 0x03);
1132}
1133
1134void MipsAssembler::CmpLtD(FRegister fd, FRegister fs, FRegister ft) {
1135 CHECK(IsR6());
1136 EmitFR(0x11, 0x15, ft, fs, fd, 0x04);
1137}
1138
1139void MipsAssembler::CmpUltD(FRegister fd, FRegister fs, FRegister ft) {
1140 CHECK(IsR6());
1141 EmitFR(0x11, 0x15, ft, fs, fd, 0x05);
1142}
1143
1144void MipsAssembler::CmpLeD(FRegister fd, FRegister fs, FRegister ft) {
1145 CHECK(IsR6());
1146 EmitFR(0x11, 0x15, ft, fs, fd, 0x06);
1147}
1148
1149void MipsAssembler::CmpUleD(FRegister fd, FRegister fs, FRegister ft) {
1150 CHECK(IsR6());
1151 EmitFR(0x11, 0x15, ft, fs, fd, 0x07);
1152}
1153
1154void MipsAssembler::CmpOrD(FRegister fd, FRegister fs, FRegister ft) {
1155 CHECK(IsR6());
1156 EmitFR(0x11, 0x15, ft, fs, fd, 0x11);
1157}
1158
1159void MipsAssembler::CmpUneD(FRegister fd, FRegister fs, FRegister ft) {
1160 CHECK(IsR6());
1161 EmitFR(0x11, 0x15, ft, fs, fd, 0x12);
1162}
1163
1164void MipsAssembler::CmpNeD(FRegister fd, FRegister fs, FRegister ft) {
1165 CHECK(IsR6());
1166 EmitFR(0x11, 0x15, ft, fs, fd, 0x13);
1167}
1168
1169void MipsAssembler::Movf(Register rd, Register rs, int cc) {
1170 CHECK(!IsR6());
1171 CHECK(IsUint<3>(cc)) << cc;
1172 EmitR(0, rs, static_cast<Register>(cc << 2), rd, 0, 0x01);
1173}
1174
1175void MipsAssembler::Movt(Register rd, Register rs, int cc) {
1176 CHECK(!IsR6());
1177 CHECK(IsUint<3>(cc)) << cc;
1178 EmitR(0, rs, static_cast<Register>((cc << 2) | 1), rd, 0, 0x01);
1179}
1180
Chris Larsenb74353a2015-11-20 09:07:09 -08001181void MipsAssembler::MovfS(FRegister fd, FRegister fs, int cc) {
1182 CHECK(!IsR6());
1183 CHECK(IsUint<3>(cc)) << cc;
1184 EmitFR(0x11, 0x10, static_cast<FRegister>(cc << 2), fs, fd, 0x11);
1185}
1186
1187void MipsAssembler::MovfD(FRegister fd, FRegister fs, int cc) {
1188 CHECK(!IsR6());
1189 CHECK(IsUint<3>(cc)) << cc;
1190 EmitFR(0x11, 0x11, static_cast<FRegister>(cc << 2), fs, fd, 0x11);
1191}
1192
1193void MipsAssembler::MovtS(FRegister fd, FRegister fs, int cc) {
1194 CHECK(!IsR6());
1195 CHECK(IsUint<3>(cc)) << cc;
1196 EmitFR(0x11, 0x10, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11);
1197}
1198
1199void MipsAssembler::MovtD(FRegister fd, FRegister fs, int cc) {
1200 CHECK(!IsR6());
1201 CHECK(IsUint<3>(cc)) << cc;
1202 EmitFR(0x11, 0x11, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11);
1203}
1204
1205void MipsAssembler::SelS(FRegister fd, FRegister fs, FRegister ft) {
1206 CHECK(IsR6());
1207 EmitFR(0x11, 0x10, ft, fs, fd, 0x10);
1208}
1209
1210void MipsAssembler::SelD(FRegister fd, FRegister fs, FRegister ft) {
1211 CHECK(IsR6());
1212 EmitFR(0x11, 0x11, ft, fs, fd, 0x10);
1213}
1214
1215void MipsAssembler::ClassS(FRegister fd, FRegister fs) {
1216 CHECK(IsR6());
1217 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x1b);
1218}
1219
1220void MipsAssembler::ClassD(FRegister fd, FRegister fs) {
1221 CHECK(IsR6());
1222 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x1b);
1223}
1224
1225void MipsAssembler::MinS(FRegister fd, FRegister fs, FRegister ft) {
1226 CHECK(IsR6());
1227 EmitFR(0x11, 0x10, ft, fs, fd, 0x1c);
1228}
1229
1230void MipsAssembler::MinD(FRegister fd, FRegister fs, FRegister ft) {
1231 CHECK(IsR6());
1232 EmitFR(0x11, 0x11, ft, fs, fd, 0x1c);
1233}
1234
1235void MipsAssembler::MaxS(FRegister fd, FRegister fs, FRegister ft) {
1236 CHECK(IsR6());
1237 EmitFR(0x11, 0x10, ft, fs, fd, 0x1e);
1238}
1239
1240void MipsAssembler::MaxD(FRegister fd, FRegister fs, FRegister ft) {
1241 CHECK(IsR6());
1242 EmitFR(0x11, 0x11, ft, fs, fd, 0x1e);
1243}
1244
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001245void MipsAssembler::TruncLS(FRegister fd, FRegister fs) {
1246 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x09);
1247}
1248
1249void MipsAssembler::TruncLD(FRegister fd, FRegister fs) {
1250 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x09);
1251}
1252
1253void MipsAssembler::TruncWS(FRegister fd, FRegister fs) {
1254 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x0D);
1255}
1256
1257void MipsAssembler::TruncWD(FRegister fd, FRegister fs) {
1258 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x0D);
1259}
1260
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001261void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) {
1262 EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x20);
1263}
1264
1265void MipsAssembler::Cvtdw(FRegister fd, FRegister fs) {
1266 EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x21);
1267}
1268
1269void MipsAssembler::Cvtsd(FRegister fd, FRegister fs) {
1270 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x20);
1271}
1272
1273void MipsAssembler::Cvtds(FRegister fd, FRegister fs) {
1274 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x21);
jeffhao7fbee072012-08-24 17:56:54 -07001275}
1276
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001277void MipsAssembler::Cvtsl(FRegister fd, FRegister fs) {
1278 EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x20);
1279}
1280
1281void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) {
1282 EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x21);
1283}
1284
Chris Larsenb74353a2015-11-20 09:07:09 -08001285void MipsAssembler::FloorWS(FRegister fd, FRegister fs) {
1286 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0xf);
1287}
1288
1289void MipsAssembler::FloorWD(FRegister fd, FRegister fs) {
1290 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0xf);
1291}
1292
jeffhao7fbee072012-08-24 17:56:54 -07001293void MipsAssembler::Mfc1(Register rt, FRegister fs) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001294 EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
jeffhao7fbee072012-08-24 17:56:54 -07001295}
1296
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001297void MipsAssembler::Mtc1(Register rt, FRegister fs) {
1298 EmitFR(0x11, 0x04, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
1299}
1300
1301void MipsAssembler::Mfhc1(Register rt, FRegister fs) {
1302 EmitFR(0x11, 0x03, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
1303}
1304
1305void MipsAssembler::Mthc1(Register rt, FRegister fs) {
1306 EmitFR(0x11, 0x07, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
jeffhao7fbee072012-08-24 17:56:54 -07001307}
1308
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001309void MipsAssembler::MoveFromFpuHigh(Register rt, FRegister fs) {
1310 if (Is32BitFPU()) {
1311 CHECK_EQ(fs % 2, 0) << fs;
1312 Mfc1(rt, static_cast<FRegister>(fs + 1));
1313 } else {
1314 Mfhc1(rt, fs);
1315 }
1316}
1317
1318void MipsAssembler::MoveToFpuHigh(Register rt, FRegister fs) {
1319 if (Is32BitFPU()) {
1320 CHECK_EQ(fs % 2, 0) << fs;
1321 Mtc1(rt, static_cast<FRegister>(fs + 1));
1322 } else {
1323 Mthc1(rt, fs);
1324 }
1325}
1326
jeffhao7fbee072012-08-24 17:56:54 -07001327void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001328 EmitI(0x31, rs, static_cast<Register>(ft), imm16);
jeffhao7fbee072012-08-24 17:56:54 -07001329}
1330
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001331void MipsAssembler::Ldc1(FRegister ft, Register rs, uint16_t imm16) {
1332 EmitI(0x35, rs, static_cast<Register>(ft), imm16);
jeffhao7fbee072012-08-24 17:56:54 -07001333}
1334
1335void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001336 EmitI(0x39, rs, static_cast<Register>(ft), imm16);
jeffhao7fbee072012-08-24 17:56:54 -07001337}
1338
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001339void MipsAssembler::Sdc1(FRegister ft, Register rs, uint16_t imm16) {
1340 EmitI(0x3d, rs, static_cast<Register>(ft), imm16);
jeffhao7fbee072012-08-24 17:56:54 -07001341}
1342
1343void MipsAssembler::Break() {
1344 EmitR(0, static_cast<Register>(0), static_cast<Register>(0),
1345 static_cast<Register>(0), 0, 0xD);
1346}
1347
jeffhao07030602012-09-26 14:33:14 -07001348void MipsAssembler::Nop() {
1349 EmitR(0x0, static_cast<Register>(0), static_cast<Register>(0), static_cast<Register>(0), 0, 0x0);
1350}
1351
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001352void MipsAssembler::Move(Register rd, Register rs) {
1353 Or(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001354}
1355
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001356void MipsAssembler::Clear(Register rd) {
1357 Move(rd, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001358}
1359
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001360void MipsAssembler::Not(Register rd, Register rs) {
1361 Nor(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001362}
1363
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001364void MipsAssembler::Push(Register rs) {
1365 IncreaseFrameSize(kMipsWordSize);
1366 Sw(rs, SP, 0);
jeffhao7fbee072012-08-24 17:56:54 -07001367}
1368
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001369void MipsAssembler::Pop(Register rd) {
1370 Lw(rd, SP, 0);
1371 DecreaseFrameSize(kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07001372}
1373
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001374void MipsAssembler::PopAndReturn(Register rd, Register rt) {
1375 Lw(rd, SP, 0);
1376 Jr(rt);
1377 DecreaseFrameSize(kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07001378}
1379
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001380void MipsAssembler::LoadConst32(Register rd, int32_t value) {
1381 if (IsUint<16>(value)) {
1382 // Use OR with (unsigned) immediate to encode 16b unsigned int.
1383 Ori(rd, ZERO, value);
1384 } else if (IsInt<16>(value)) {
1385 // Use ADD with (signed) immediate to encode 16b signed int.
1386 Addiu(rd, ZERO, value);
jeffhao7fbee072012-08-24 17:56:54 -07001387 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001388 Lui(rd, High16Bits(value));
1389 if (value & 0xFFFF)
1390 Ori(rd, rd, Low16Bits(value));
1391 }
1392}
1393
1394void MipsAssembler::LoadConst64(Register reg_hi, Register reg_lo, int64_t value) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001395 uint32_t low = Low32Bits(value);
1396 uint32_t high = High32Bits(value);
1397 LoadConst32(reg_lo, low);
1398 if (high != low) {
1399 LoadConst32(reg_hi, high);
1400 } else {
1401 Move(reg_hi, reg_lo);
1402 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001403}
1404
1405void MipsAssembler::StoreConst32ToOffset(int32_t value,
1406 Register base,
1407 int32_t offset,
1408 Register temp) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001409 CHECK_NE(temp, AT); // Must not use AT as temp, so as not to overwrite the adjusted base.
1410 AdjustBaseAndOffset(base, offset, /* is_doubleword */ false);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001411 if (value == 0) {
1412 temp = ZERO;
1413 } else {
1414 LoadConst32(temp, value);
1415 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001416 Sw(temp, base, offset);
1417}
1418
1419void MipsAssembler::StoreConst64ToOffset(int64_t value,
1420 Register base,
1421 int32_t offset,
1422 Register temp) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001423 CHECK_NE(temp, AT); // Must not use AT as temp, so as not to overwrite the adjusted base.
1424 AdjustBaseAndOffset(base, offset, /* is_doubleword */ true);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001425 uint32_t low = Low32Bits(value);
1426 uint32_t high = High32Bits(value);
1427 if (low == 0) {
1428 Sw(ZERO, base, offset);
1429 } else {
1430 LoadConst32(temp, low);
1431 Sw(temp, base, offset);
1432 }
1433 if (high == 0) {
1434 Sw(ZERO, base, offset + kMipsWordSize);
1435 } else {
1436 if (high != low) {
1437 LoadConst32(temp, high);
1438 }
1439 Sw(temp, base, offset + kMipsWordSize);
1440 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001441}
1442
1443void MipsAssembler::LoadSConst32(FRegister r, int32_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001444 if (value == 0) {
1445 temp = ZERO;
1446 } else {
1447 LoadConst32(temp, value);
1448 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001449 Mtc1(temp, r);
1450}
1451
1452void MipsAssembler::LoadDConst64(FRegister rd, int64_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001453 uint32_t low = Low32Bits(value);
1454 uint32_t high = High32Bits(value);
1455 if (low == 0) {
1456 Mtc1(ZERO, rd);
1457 } else {
1458 LoadConst32(temp, low);
1459 Mtc1(temp, rd);
1460 }
1461 if (high == 0) {
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001462 MoveToFpuHigh(ZERO, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001463 } else {
1464 LoadConst32(temp, high);
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001465 MoveToFpuHigh(temp, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001466 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001467}
1468
1469void MipsAssembler::Addiu32(Register rt, Register rs, int32_t value, Register temp) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001470 CHECK_NE(rs, temp); // Must not overwrite the register `rs` while loading `value`.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001471 if (IsInt<16>(value)) {
1472 Addiu(rt, rs, value);
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001473 } else if (IsR6()) {
1474 int16_t high = High16Bits(value);
1475 int16_t low = Low16Bits(value);
1476 high += (low < 0) ? 1 : 0; // Account for sign extension in addiu.
1477 if (low != 0) {
1478 Aui(temp, rs, high);
1479 Addiu(rt, temp, low);
1480 } else {
1481 Aui(rt, rs, high);
1482 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001483 } else {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001484 // Do not load the whole 32-bit `value` if it can be represented as
1485 // a sum of two 16-bit signed values. This can save an instruction.
1486 constexpr int32_t kMinValueForSimpleAdjustment = std::numeric_limits<int16_t>::min() * 2;
1487 constexpr int32_t kMaxValueForSimpleAdjustment = std::numeric_limits<int16_t>::max() * 2;
1488 if (0 <= value && value <= kMaxValueForSimpleAdjustment) {
1489 Addiu(temp, rs, kMaxValueForSimpleAdjustment / 2);
1490 Addiu(rt, temp, value - kMaxValueForSimpleAdjustment / 2);
1491 } else if (kMinValueForSimpleAdjustment <= value && value < 0) {
1492 Addiu(temp, rs, kMinValueForSimpleAdjustment / 2);
1493 Addiu(rt, temp, value - kMinValueForSimpleAdjustment / 2);
1494 } else {
1495 // Now that all shorter options have been exhausted, load the full 32-bit value.
1496 LoadConst32(temp, value);
1497 Addu(rt, rs, temp);
1498 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001499 }
1500}
1501
1502void MipsAssembler::Branch::InitShortOrLong(MipsAssembler::Branch::OffsetBits offset_size,
1503 MipsAssembler::Branch::Type short_type,
1504 MipsAssembler::Branch::Type long_type) {
1505 type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type;
1506}
1507
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001508void MipsAssembler::Branch::InitializeType(bool is_call, bool is_literal, bool is_r6) {
1509 CHECK_EQ(is_call && is_literal, false);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001510 OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_);
1511 if (is_r6) {
1512 // R6
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001513 if (is_literal) {
1514 CHECK(!IsResolved());
1515 type_ = kR6Literal;
1516 } else if (is_call) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001517 InitShortOrLong(offset_size, kR6Call, kR6LongCall);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001518 } else {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001519 switch (condition_) {
1520 case kUncond:
1521 InitShortOrLong(offset_size, kR6UncondBranch, kR6LongUncondBranch);
1522 break;
1523 case kCondEQZ:
1524 case kCondNEZ:
1525 // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions.
1526 type_ = (offset_size <= kOffset23) ? kR6CondBranch : kR6LongCondBranch;
1527 break;
1528 default:
1529 InitShortOrLong(offset_size, kR6CondBranch, kR6LongCondBranch);
1530 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001531 }
1532 }
1533 } else {
1534 // R2
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001535 if (is_literal) {
1536 CHECK(!IsResolved());
1537 type_ = kLiteral;
1538 } else if (is_call) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001539 InitShortOrLong(offset_size, kCall, kLongCall);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001540 } else {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001541 switch (condition_) {
1542 case kUncond:
1543 InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch);
1544 break;
1545 default:
1546 InitShortOrLong(offset_size, kCondBranch, kLongCondBranch);
1547 break;
1548 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001549 }
1550 }
1551 old_type_ = type_;
1552}
1553
1554bool MipsAssembler::Branch::IsNop(BranchCondition condition, Register lhs, Register rhs) {
1555 switch (condition) {
1556 case kCondLT:
1557 case kCondGT:
1558 case kCondNE:
1559 case kCondLTU:
1560 return lhs == rhs;
1561 default:
1562 return false;
1563 }
1564}
1565
1566bool MipsAssembler::Branch::IsUncond(BranchCondition condition, Register lhs, Register rhs) {
1567 switch (condition) {
1568 case kUncond:
1569 return true;
1570 case kCondGE:
1571 case kCondLE:
1572 case kCondEQ:
1573 case kCondGEU:
1574 return lhs == rhs;
1575 default:
1576 return false;
1577 }
1578}
1579
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001580MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target, bool is_call)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001581 : old_location_(location),
1582 location_(location),
1583 target_(target),
1584 lhs_reg_(0),
1585 rhs_reg_(0),
1586 condition_(kUncond) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001587 InitializeType(is_call, /* is_literal */ false, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001588}
1589
1590MipsAssembler::Branch::Branch(bool is_r6,
1591 uint32_t location,
1592 uint32_t target,
1593 MipsAssembler::BranchCondition condition,
1594 Register lhs_reg,
1595 Register rhs_reg)
1596 : old_location_(location),
1597 location_(location),
1598 target_(target),
1599 lhs_reg_(lhs_reg),
1600 rhs_reg_(rhs_reg),
1601 condition_(condition) {
1602 CHECK_NE(condition, kUncond);
1603 switch (condition) {
1604 case kCondLT:
1605 case kCondGE:
1606 case kCondLE:
1607 case kCondGT:
1608 case kCondLTU:
1609 case kCondGEU:
1610 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
1611 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
1612 // We leave this up to the caller.
1613 CHECK(is_r6);
1614 FALLTHROUGH_INTENDED;
1615 case kCondEQ:
1616 case kCondNE:
1617 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1618 // To compare with 0, use dedicated kCond*Z conditions.
1619 CHECK_NE(lhs_reg, ZERO);
1620 CHECK_NE(rhs_reg, ZERO);
1621 break;
1622 case kCondLTZ:
1623 case kCondGEZ:
1624 case kCondLEZ:
1625 case kCondGTZ:
1626 case kCondEQZ:
1627 case kCondNEZ:
1628 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1629 CHECK_NE(lhs_reg, ZERO);
1630 CHECK_EQ(rhs_reg, ZERO);
1631 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001632 case kCondF:
1633 case kCondT:
1634 CHECK_EQ(rhs_reg, ZERO);
1635 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001636 case kUncond:
1637 UNREACHABLE();
1638 }
1639 CHECK(!IsNop(condition, lhs_reg, rhs_reg));
1640 if (IsUncond(condition, lhs_reg, rhs_reg)) {
1641 // Branch condition is always true, make the branch unconditional.
1642 condition_ = kUncond;
1643 }
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001644 InitializeType(/* is_call */ false, /* is_literal */ false, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001645}
1646
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001647MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, Register dest_reg, Register base_reg)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001648 : old_location_(location),
1649 location_(location),
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001650 target_(kUnresolved),
1651 lhs_reg_(dest_reg),
1652 rhs_reg_(base_reg),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001653 condition_(kUncond) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001654 CHECK_NE(dest_reg, ZERO);
1655 if (is_r6) {
1656 CHECK_EQ(base_reg, ZERO);
1657 } else {
1658 CHECK_NE(base_reg, ZERO);
1659 }
1660 InitializeType(/* is_call */ false, /* is_literal */ true, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001661}
1662
1663MipsAssembler::BranchCondition MipsAssembler::Branch::OppositeCondition(
1664 MipsAssembler::BranchCondition cond) {
1665 switch (cond) {
1666 case kCondLT:
1667 return kCondGE;
1668 case kCondGE:
1669 return kCondLT;
1670 case kCondLE:
1671 return kCondGT;
1672 case kCondGT:
1673 return kCondLE;
1674 case kCondLTZ:
1675 return kCondGEZ;
1676 case kCondGEZ:
1677 return kCondLTZ;
1678 case kCondLEZ:
1679 return kCondGTZ;
1680 case kCondGTZ:
1681 return kCondLEZ;
1682 case kCondEQ:
1683 return kCondNE;
1684 case kCondNE:
1685 return kCondEQ;
1686 case kCondEQZ:
1687 return kCondNEZ;
1688 case kCondNEZ:
1689 return kCondEQZ;
1690 case kCondLTU:
1691 return kCondGEU;
1692 case kCondGEU:
1693 return kCondLTU;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001694 case kCondF:
1695 return kCondT;
1696 case kCondT:
1697 return kCondF;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001698 case kUncond:
1699 LOG(FATAL) << "Unexpected branch condition " << cond;
1700 }
1701 UNREACHABLE();
1702}
1703
1704MipsAssembler::Branch::Type MipsAssembler::Branch::GetType() const {
1705 return type_;
1706}
1707
1708MipsAssembler::BranchCondition MipsAssembler::Branch::GetCondition() const {
1709 return condition_;
1710}
1711
1712Register MipsAssembler::Branch::GetLeftRegister() const {
1713 return static_cast<Register>(lhs_reg_);
1714}
1715
1716Register MipsAssembler::Branch::GetRightRegister() const {
1717 return static_cast<Register>(rhs_reg_);
1718}
1719
1720uint32_t MipsAssembler::Branch::GetTarget() const {
1721 return target_;
1722}
1723
1724uint32_t MipsAssembler::Branch::GetLocation() const {
1725 return location_;
1726}
1727
1728uint32_t MipsAssembler::Branch::GetOldLocation() const {
1729 return old_location_;
1730}
1731
1732uint32_t MipsAssembler::Branch::GetLength() const {
1733 return branch_info_[type_].length;
1734}
1735
1736uint32_t MipsAssembler::Branch::GetOldLength() const {
1737 return branch_info_[old_type_].length;
1738}
1739
1740uint32_t MipsAssembler::Branch::GetSize() const {
1741 return GetLength() * sizeof(uint32_t);
1742}
1743
1744uint32_t MipsAssembler::Branch::GetOldSize() const {
1745 return GetOldLength() * sizeof(uint32_t);
1746}
1747
1748uint32_t MipsAssembler::Branch::GetEndLocation() const {
1749 return GetLocation() + GetSize();
1750}
1751
1752uint32_t MipsAssembler::Branch::GetOldEndLocation() const {
1753 return GetOldLocation() + GetOldSize();
1754}
1755
1756bool MipsAssembler::Branch::IsLong() const {
1757 switch (type_) {
1758 // R2 short branches.
1759 case kUncondBranch:
1760 case kCondBranch:
1761 case kCall:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001762 // R2 near literal.
1763 case kLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001764 // R6 short branches.
1765 case kR6UncondBranch:
1766 case kR6CondBranch:
1767 case kR6Call:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001768 // R6 near literal.
1769 case kR6Literal:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001770 return false;
1771 // R2 long branches.
1772 case kLongUncondBranch:
1773 case kLongCondBranch:
1774 case kLongCall:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001775 // R2 far literal.
1776 case kFarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001777 // R6 long branches.
1778 case kR6LongUncondBranch:
1779 case kR6LongCondBranch:
1780 case kR6LongCall:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001781 // R6 far literal.
1782 case kR6FarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001783 return true;
1784 }
1785 UNREACHABLE();
1786}
1787
1788bool MipsAssembler::Branch::IsResolved() const {
1789 return target_ != kUnresolved;
1790}
1791
1792MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSize() const {
1793 OffsetBits offset_size =
1794 (type_ == kR6CondBranch && (condition_ == kCondEQZ || condition_ == kCondNEZ))
1795 ? kOffset23
1796 : branch_info_[type_].offset_size;
1797 return offset_size;
1798}
1799
1800MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSizeNeeded(uint32_t location,
1801 uint32_t target) {
1802 // For unresolved targets assume the shortest encoding
1803 // (later it will be made longer if needed).
1804 if (target == kUnresolved)
1805 return kOffset16;
1806 int64_t distance = static_cast<int64_t>(target) - location;
1807 // To simplify calculations in composite branches consisting of multiple instructions
1808 // bump up the distance by a value larger than the max byte size of a composite branch.
1809 distance += (distance >= 0) ? kMaxBranchSize : -kMaxBranchSize;
1810 if (IsInt<kOffset16>(distance))
1811 return kOffset16;
1812 else if (IsInt<kOffset18>(distance))
1813 return kOffset18;
1814 else if (IsInt<kOffset21>(distance))
1815 return kOffset21;
1816 else if (IsInt<kOffset23>(distance))
1817 return kOffset23;
1818 else if (IsInt<kOffset28>(distance))
1819 return kOffset28;
1820 return kOffset32;
1821}
1822
1823void MipsAssembler::Branch::Resolve(uint32_t target) {
1824 target_ = target;
1825}
1826
1827void MipsAssembler::Branch::Relocate(uint32_t expand_location, uint32_t delta) {
1828 if (location_ > expand_location) {
1829 location_ += delta;
1830 }
1831 if (!IsResolved()) {
1832 return; // Don't know the target yet.
1833 }
1834 if (target_ > expand_location) {
1835 target_ += delta;
1836 }
1837}
1838
1839void MipsAssembler::Branch::PromoteToLong() {
1840 switch (type_) {
1841 // R2 short branches.
1842 case kUncondBranch:
1843 type_ = kLongUncondBranch;
1844 break;
1845 case kCondBranch:
1846 type_ = kLongCondBranch;
1847 break;
1848 case kCall:
1849 type_ = kLongCall;
1850 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001851 // R2 near literal.
1852 case kLiteral:
1853 type_ = kFarLiteral;
1854 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001855 // R6 short branches.
1856 case kR6UncondBranch:
1857 type_ = kR6LongUncondBranch;
1858 break;
1859 case kR6CondBranch:
1860 type_ = kR6LongCondBranch;
1861 break;
1862 case kR6Call:
1863 type_ = kR6LongCall;
1864 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001865 // R6 near literal.
1866 case kR6Literal:
1867 type_ = kR6FarLiteral;
1868 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001869 default:
1870 // Note: 'type_' is already long.
1871 break;
1872 }
1873 CHECK(IsLong());
1874}
1875
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001876uint32_t MipsAssembler::GetBranchLocationOrPcRelBase(const MipsAssembler::Branch* branch) const {
1877 switch (branch->GetType()) {
1878 case Branch::kLiteral:
1879 case Branch::kFarLiteral:
1880 return GetLabelLocation(&pc_rel_base_label_);
1881 default:
1882 return branch->GetLocation();
1883 }
1884}
1885
1886uint32_t MipsAssembler::Branch::PromoteIfNeeded(uint32_t location, uint32_t max_short_distance) {
1887 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 literals or
1888 // `this->GetLocation()` for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001889 // If the branch is still unresolved or already long, nothing to do.
1890 if (IsLong() || !IsResolved()) {
1891 return 0;
1892 }
1893 // Promote the short branch to long if the offset size is too small
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001894 // to hold the distance between location and target_.
1895 if (GetOffsetSizeNeeded(location, target_) > GetOffsetSize()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001896 PromoteToLong();
1897 uint32_t old_size = GetOldSize();
1898 uint32_t new_size = GetSize();
1899 CHECK_GT(new_size, old_size);
1900 return new_size - old_size;
1901 }
1902 // The following logic is for debugging/testing purposes.
1903 // Promote some short branches to long when it's not really required.
1904 if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max())) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001905 int64_t distance = static_cast<int64_t>(target_) - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001906 distance = (distance >= 0) ? distance : -distance;
1907 if (distance >= max_short_distance) {
1908 PromoteToLong();
1909 uint32_t old_size = GetOldSize();
1910 uint32_t new_size = GetSize();
1911 CHECK_GT(new_size, old_size);
1912 return new_size - old_size;
1913 }
1914 }
1915 return 0;
1916}
1917
1918uint32_t MipsAssembler::Branch::GetOffsetLocation() const {
1919 return location_ + branch_info_[type_].instr_offset * sizeof(uint32_t);
1920}
1921
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001922uint32_t MipsAssembler::GetBranchOrPcRelBaseForEncoding(const MipsAssembler::Branch* branch) const {
1923 switch (branch->GetType()) {
1924 case Branch::kLiteral:
1925 case Branch::kFarLiteral:
1926 return GetLabelLocation(&pc_rel_base_label_);
1927 default:
1928 return branch->GetOffsetLocation() +
1929 Branch::branch_info_[branch->GetType()].pc_org * sizeof(uint32_t);
1930 }
1931}
1932
1933uint32_t MipsAssembler::Branch::GetOffset(uint32_t location) const {
1934 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 literals or
1935 // `this->GetOffsetLocation() + branch_info_[this->GetType()].pc_org * sizeof(uint32_t)`
1936 // for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001937 CHECK(IsResolved());
1938 uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize());
1939 // Calculate the byte distance between instructions and also account for
1940 // different PC-relative origins.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001941 uint32_t offset = target_ - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001942 // Prepare the offset for encoding into the instruction(s).
1943 offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift;
1944 return offset;
1945}
1946
1947MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) {
1948 CHECK_LT(branch_id, branches_.size());
1949 return &branches_[branch_id];
1950}
1951
1952const MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) const {
1953 CHECK_LT(branch_id, branches_.size());
1954 return &branches_[branch_id];
1955}
1956
1957void MipsAssembler::Bind(MipsLabel* label) {
1958 CHECK(!label->IsBound());
1959 uint32_t bound_pc = buffer_.Size();
1960
1961 // Walk the list of branches referring to and preceding this label.
1962 // Store the previously unknown target addresses in them.
1963 while (label->IsLinked()) {
1964 uint32_t branch_id = label->Position();
1965 Branch* branch = GetBranch(branch_id);
1966 branch->Resolve(bound_pc);
1967
1968 uint32_t branch_location = branch->GetLocation();
1969 // Extract the location of the previous branch in the list (walking the list backwards;
1970 // the previous branch ID was stored in the space reserved for this branch).
1971 uint32_t prev = buffer_.Load<uint32_t>(branch_location);
1972
1973 // On to the previous branch in the list...
1974 label->position_ = prev;
1975 }
1976
1977 // Now make the label object contain its own location (relative to the end of the preceding
1978 // branch, if any; it will be used by the branches referring to and following this label).
1979 label->prev_branch_id_plus_one_ = branches_.size();
1980 if (label->prev_branch_id_plus_one_) {
1981 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
1982 const Branch* branch = GetBranch(branch_id);
1983 bound_pc -= branch->GetEndLocation();
1984 }
1985 label->BindTo(bound_pc);
1986}
1987
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001988uint32_t MipsAssembler::GetLabelLocation(const MipsLabel* label) const {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001989 CHECK(label->IsBound());
1990 uint32_t target = label->Position();
1991 if (label->prev_branch_id_plus_one_) {
1992 // Get label location based on the branch preceding it.
1993 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
1994 const Branch* branch = GetBranch(branch_id);
1995 target += branch->GetEndLocation();
1996 }
1997 return target;
1998}
1999
2000uint32_t MipsAssembler::GetAdjustedPosition(uint32_t old_position) {
2001 // We can reconstruct the adjustment by going through all the branches from the beginning
2002 // up to the old_position. Since we expect AdjustedPosition() to be called in a loop
2003 // with increasing old_position, we can use the data from last AdjustedPosition() to
2004 // continue where we left off and the whole loop should be O(m+n) where m is the number
2005 // of positions to adjust and n is the number of branches.
2006 if (old_position < last_old_position_) {
2007 last_position_adjustment_ = 0;
2008 last_old_position_ = 0;
2009 last_branch_id_ = 0;
2010 }
2011 while (last_branch_id_ != branches_.size()) {
2012 const Branch* branch = GetBranch(last_branch_id_);
2013 if (branch->GetLocation() >= old_position + last_position_adjustment_) {
2014 break;
2015 }
2016 last_position_adjustment_ += branch->GetSize() - branch->GetOldSize();
2017 ++last_branch_id_;
2018 }
2019 last_old_position_ = old_position;
2020 return old_position + last_position_adjustment_;
2021}
2022
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002023void MipsAssembler::BindPcRelBaseLabel() {
2024 Bind(&pc_rel_base_label_);
2025}
2026
Alexey Frunze06a46c42016-07-19 15:00:40 -07002027uint32_t MipsAssembler::GetPcRelBaseLabelLocation() const {
2028 return GetLabelLocation(&pc_rel_base_label_);
2029}
2030
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002031void MipsAssembler::FinalizeLabeledBranch(MipsLabel* label) {
2032 uint32_t length = branches_.back().GetLength();
2033 if (!label->IsBound()) {
2034 // Branch forward (to a following label), distance is unknown.
2035 // The first branch forward will contain 0, serving as the terminator of
2036 // the list of forward-reaching branches.
2037 Emit(label->position_);
2038 length--;
2039 // Now make the label object point to this branch
2040 // (this forms a linked list of branches preceding this label).
2041 uint32_t branch_id = branches_.size() - 1;
2042 label->LinkTo(branch_id);
2043 }
2044 // Reserve space for the branch.
2045 while (length--) {
2046 Nop();
2047 }
2048}
2049
2050void MipsAssembler::Buncond(MipsLabel* label) {
2051 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002052 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ false);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002053 FinalizeLabeledBranch(label);
2054}
2055
2056void MipsAssembler::Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs) {
2057 // If lhs = rhs, this can be a NOP.
2058 if (Branch::IsNop(condition, lhs, rhs)) {
2059 return;
2060 }
2061 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
2062 branches_.emplace_back(IsR6(), buffer_.Size(), target, condition, lhs, rhs);
2063 FinalizeLabeledBranch(label);
2064}
2065
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002066void MipsAssembler::Call(MipsLabel* label) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002067 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002068 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ true);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002069 FinalizeLabeledBranch(label);
2070}
2071
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002072Literal* MipsAssembler::NewLiteral(size_t size, const uint8_t* data) {
2073 DCHECK(size == 4u || size == 8u) << size;
2074 literals_.emplace_back(size, data);
2075 return &literals_.back();
2076}
2077
2078void MipsAssembler::LoadLiteral(Register dest_reg, Register base_reg, Literal* literal) {
2079 // Literal loads are treated as pseudo branches since they require very similar handling.
2080 DCHECK_EQ(literal->GetSize(), 4u);
2081 MipsLabel* label = literal->GetLabel();
2082 DCHECK(!label->IsBound());
2083 branches_.emplace_back(IsR6(),
2084 buffer_.Size(),
2085 dest_reg,
2086 base_reg);
2087 FinalizeLabeledBranch(label);
2088}
2089
2090void MipsAssembler::EmitLiterals() {
2091 if (!literals_.empty()) {
2092 // We don't support byte and half-word literals.
2093 // TODO: proper alignment for 64-bit literals when they're implemented.
2094 for (Literal& literal : literals_) {
2095 MipsLabel* label = literal.GetLabel();
2096 Bind(label);
2097 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
2098 DCHECK(literal.GetSize() == 4u || literal.GetSize() == 8u);
2099 for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
2100 buffer_.Emit<uint8_t>(literal.GetData()[i]);
2101 }
2102 }
2103 }
2104}
2105
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002106void MipsAssembler::PromoteBranches() {
2107 // Promote short branches to long as necessary.
2108 bool changed;
2109 do {
2110 changed = false;
2111 for (auto& branch : branches_) {
2112 CHECK(branch.IsResolved());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002113 uint32_t base = GetBranchLocationOrPcRelBase(&branch);
2114 uint32_t delta = branch.PromoteIfNeeded(base);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002115 // If this branch has been promoted and needs to expand in size,
2116 // relocate all branches by the expansion size.
2117 if (delta) {
2118 changed = true;
2119 uint32_t expand_location = branch.GetLocation();
2120 for (auto& branch2 : branches_) {
2121 branch2.Relocate(expand_location, delta);
2122 }
2123 }
2124 }
2125 } while (changed);
2126
2127 // Account for branch expansion by resizing the code buffer
2128 // and moving the code in it to its final location.
2129 size_t branch_count = branches_.size();
2130 if (branch_count > 0) {
2131 // Resize.
2132 Branch& last_branch = branches_[branch_count - 1];
2133 uint32_t size_delta = last_branch.GetEndLocation() - last_branch.GetOldEndLocation();
2134 uint32_t old_size = buffer_.Size();
2135 buffer_.Resize(old_size + size_delta);
2136 // Move the code residing between branch placeholders.
2137 uint32_t end = old_size;
2138 for (size_t i = branch_count; i > 0; ) {
2139 Branch& branch = branches_[--i];
2140 uint32_t size = end - branch.GetOldEndLocation();
2141 buffer_.Move(branch.GetEndLocation(), branch.GetOldEndLocation(), size);
2142 end = branch.GetOldLocation();
2143 }
2144 }
2145}
2146
2147// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
2148const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = {
2149 // R2 short branches.
2150 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kUncondBranch
2151 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002152 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCall
2153 // R2 near literal.
2154 { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002155 // R2 long branches.
2156 { 9, 3, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongUncondBranch
2157 { 10, 4, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCondBranch
2158 { 6, 1, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCall
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002159 // R2 far literal.
2160 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002161 // R6 short branches.
2162 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6UncondBranch
2163 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kR6CondBranch
2164 // Exception: kOffset23 for beqzc/bnezc.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002165 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6Call
2166 // R6 near literal.
2167 { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Literal
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002168 // R6 long branches.
2169 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongUncondBranch
2170 { 3, 1, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002171 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCall
2172 // R6 far literal.
2173 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002174};
2175
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002176// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002177void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
2178 CHECK_EQ(overwriting_, true);
2179 overwrite_location_ = branch->GetLocation();
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002180 uint32_t offset = branch->GetOffset(GetBranchOrPcRelBaseForEncoding(branch));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002181 BranchCondition condition = branch->GetCondition();
2182 Register lhs = branch->GetLeftRegister();
2183 Register rhs = branch->GetRightRegister();
2184 switch (branch->GetType()) {
2185 // R2 short branches.
2186 case Branch::kUncondBranch:
2187 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2188 B(offset);
2189 Nop(); // TODO: improve by filling the delay slot.
2190 break;
2191 case Branch::kCondBranch:
2192 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002193 EmitBcondR2(condition, lhs, rhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002194 Nop(); // TODO: improve by filling the delay slot.
2195 break;
2196 case Branch::kCall:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002197 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002198 Bal(offset);
2199 Nop(); // TODO: improve by filling the delay slot.
2200 break;
2201
2202 // R2 near literal.
2203 case Branch::kLiteral:
2204 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2205 Lw(lhs, rhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002206 break;
2207
2208 // R2 long branches.
2209 case Branch::kLongUncondBranch:
2210 // To get the value of the PC register we need to use the NAL instruction.
2211 // NAL clobbers the RA register. However, RA must be preserved if the
2212 // method is compiled without the entry/exit sequences that would take care
2213 // of preserving RA (typically, leaf methods don't preserve RA explicitly).
2214 // So, we need to preserve RA in some temporary storage ourselves. The AT
2215 // register can't be used for this because we need it to load a constant
2216 // which will be added to the value that NAL stores in RA. And we can't
2217 // use T9 for this in the context of the JNI compiler, which uses it
2218 // as a scratch register (see InterproceduralScratchRegister()).
2219 // If we were to add a 32-bit constant to RA using two ADDIU instructions,
2220 // we'd also need to use the ROTR instruction, which requires no less than
2221 // MIPSR2.
2222 // Perhaps, we could use T8 or one of R2's multiplier/divider registers
2223 // (LO or HI) or even a floating-point register, but that doesn't seem
2224 // like a nice solution. We may want this to work on both R6 and pre-R6.
2225 // For now simply use the stack for RA. This should be OK since for the
2226 // vast majority of code a short PC-relative branch is sufficient.
2227 // TODO: can this be improved?
2228 Push(RA);
2229 Nal();
2230 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2231 Lui(AT, High16Bits(offset));
2232 Ori(AT, AT, Low16Bits(offset));
2233 Addu(AT, AT, RA);
2234 Lw(RA, SP, 0);
2235 Jr(AT);
2236 DecreaseFrameSize(kMipsWordSize);
2237 break;
2238 case Branch::kLongCondBranch:
2239 // The comment on case 'Branch::kLongUncondBranch' applies here as well.
2240 // Note: the opposite condition branch encodes 8 as the distance, which is equal to the
2241 // number of instructions skipped:
2242 // (PUSH(IncreaseFrameSize(ADDIU) + SW) + NAL + LUI + ORI + ADDU + LW + JR).
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002243 EmitBcondR2(Branch::OppositeCondition(condition), lhs, rhs, 8);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002244 Push(RA);
2245 Nal();
2246 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2247 Lui(AT, High16Bits(offset));
2248 Ori(AT, AT, Low16Bits(offset));
2249 Addu(AT, AT, RA);
2250 Lw(RA, SP, 0);
2251 Jr(AT);
2252 DecreaseFrameSize(kMipsWordSize);
2253 break;
2254 case Branch::kLongCall:
2255 Nal();
2256 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2257 Lui(AT, High16Bits(offset));
2258 Ori(AT, AT, Low16Bits(offset));
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002259 Addu(AT, AT, RA);
2260 Jalr(AT);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002261 Nop();
2262 break;
2263
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002264 // R2 far literal.
2265 case Branch::kFarLiteral:
2266 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2267 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2268 Lui(AT, High16Bits(offset));
2269 Addu(AT, AT, rhs);
2270 Lw(lhs, AT, Low16Bits(offset));
2271 break;
2272
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002273 // R6 short branches.
2274 case Branch::kR6UncondBranch:
2275 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2276 Bc(offset);
2277 break;
2278 case Branch::kR6CondBranch:
2279 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002280 EmitBcondR6(condition, lhs, rhs, offset);
2281 Nop(); // TODO: improve by filling the forbidden/delay slot.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002282 break;
2283 case Branch::kR6Call:
2284 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002285 Balc(offset);
2286 break;
2287
2288 // R6 near literal.
2289 case Branch::kR6Literal:
2290 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2291 Lwpc(lhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002292 break;
2293
2294 // R6 long branches.
2295 case Branch::kR6LongUncondBranch:
2296 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2297 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2298 Auipc(AT, High16Bits(offset));
2299 Jic(AT, Low16Bits(offset));
2300 break;
2301 case Branch::kR6LongCondBranch:
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002302 EmitBcondR6(Branch::OppositeCondition(condition), lhs, rhs, 2);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002303 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2304 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2305 Auipc(AT, High16Bits(offset));
2306 Jic(AT, Low16Bits(offset));
2307 break;
2308 case Branch::kR6LongCall:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002309 offset += (offset & 0x8000) << 1; // Account for sign extension in jialc.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002310 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002311 Auipc(AT, High16Bits(offset));
2312 Jialc(AT, Low16Bits(offset));
2313 break;
2314
2315 // R6 far literal.
2316 case Branch::kR6FarLiteral:
2317 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2318 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2319 Auipc(AT, High16Bits(offset));
2320 Lw(lhs, AT, Low16Bits(offset));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002321 break;
2322 }
2323 CHECK_EQ(overwrite_location_, branch->GetEndLocation());
2324 CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize));
2325}
2326
2327void MipsAssembler::B(MipsLabel* label) {
2328 Buncond(label);
2329}
2330
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002331void MipsAssembler::Bal(MipsLabel* label) {
2332 Call(label);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002333}
2334
2335void MipsAssembler::Beq(Register rs, Register rt, MipsLabel* label) {
2336 Bcond(label, kCondEQ, rs, rt);
2337}
2338
2339void MipsAssembler::Bne(Register rs, Register rt, MipsLabel* label) {
2340 Bcond(label, kCondNE, rs, rt);
2341}
2342
2343void MipsAssembler::Beqz(Register rt, MipsLabel* label) {
2344 Bcond(label, kCondEQZ, rt);
2345}
2346
2347void MipsAssembler::Bnez(Register rt, MipsLabel* label) {
2348 Bcond(label, kCondNEZ, rt);
2349}
2350
2351void MipsAssembler::Bltz(Register rt, MipsLabel* label) {
2352 Bcond(label, kCondLTZ, rt);
2353}
2354
2355void MipsAssembler::Bgez(Register rt, MipsLabel* label) {
2356 Bcond(label, kCondGEZ, rt);
2357}
2358
2359void MipsAssembler::Blez(Register rt, MipsLabel* label) {
2360 Bcond(label, kCondLEZ, rt);
2361}
2362
2363void MipsAssembler::Bgtz(Register rt, MipsLabel* label) {
2364 Bcond(label, kCondGTZ, rt);
2365}
2366
2367void MipsAssembler::Blt(Register rs, Register rt, MipsLabel* label) {
2368 if (IsR6()) {
2369 Bcond(label, kCondLT, rs, rt);
2370 } else if (!Branch::IsNop(kCondLT, rs, rt)) {
2371 // Synthesize the instruction (not available on R2).
2372 Slt(AT, rs, rt);
2373 Bnez(AT, label);
2374 }
2375}
2376
2377void MipsAssembler::Bge(Register rs, Register rt, MipsLabel* label) {
2378 if (IsR6()) {
2379 Bcond(label, kCondGE, rs, rt);
2380 } else if (Branch::IsUncond(kCondGE, rs, rt)) {
2381 B(label);
2382 } else {
2383 // Synthesize the instruction (not available on R2).
2384 Slt(AT, rs, rt);
2385 Beqz(AT, label);
2386 }
2387}
2388
2389void MipsAssembler::Bltu(Register rs, Register rt, MipsLabel* label) {
2390 if (IsR6()) {
2391 Bcond(label, kCondLTU, rs, rt);
2392 } else if (!Branch::IsNop(kCondLTU, rs, rt)) {
2393 // Synthesize the instruction (not available on R2).
2394 Sltu(AT, rs, rt);
2395 Bnez(AT, label);
2396 }
2397}
2398
2399void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) {
2400 if (IsR6()) {
2401 Bcond(label, kCondGEU, rs, rt);
2402 } else if (Branch::IsUncond(kCondGEU, rs, rt)) {
2403 B(label);
2404 } else {
2405 // Synthesize the instruction (not available on R2).
2406 Sltu(AT, rs, rt);
2407 Beqz(AT, label);
jeffhao7fbee072012-08-24 17:56:54 -07002408 }
2409}
2410
Chris Larsenb74353a2015-11-20 09:07:09 -08002411void MipsAssembler::Bc1f(MipsLabel* label) {
2412 Bc1f(0, label);
2413}
2414
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002415void MipsAssembler::Bc1f(int cc, MipsLabel* label) {
2416 CHECK(IsUint<3>(cc)) << cc;
2417 Bcond(label, kCondF, static_cast<Register>(cc), ZERO);
2418}
2419
Chris Larsenb74353a2015-11-20 09:07:09 -08002420void MipsAssembler::Bc1t(MipsLabel* label) {
2421 Bc1t(0, label);
2422}
2423
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002424void MipsAssembler::Bc1t(int cc, MipsLabel* label) {
2425 CHECK(IsUint<3>(cc)) << cc;
2426 Bcond(label, kCondT, static_cast<Register>(cc), ZERO);
2427}
2428
2429void MipsAssembler::Bc1eqz(FRegister ft, MipsLabel* label) {
2430 Bcond(label, kCondF, static_cast<Register>(ft), ZERO);
2431}
2432
2433void MipsAssembler::Bc1nez(FRegister ft, MipsLabel* label) {
2434 Bcond(label, kCondT, static_cast<Register>(ft), ZERO);
2435}
2436
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002437void MipsAssembler::AdjustBaseAndOffset(Register& base,
2438 int32_t& offset,
2439 bool is_doubleword,
2440 bool is_float) {
2441 // This method is used to adjust the base register and offset pair
2442 // for a load/store when the offset doesn't fit into int16_t.
2443 // It is assumed that `base + offset` is sufficiently aligned for memory
2444 // operands that are machine word in size or smaller. For doubleword-sized
2445 // operands it's assumed that `base` is a multiple of 8, while `offset`
2446 // may be a multiple of 4 (e.g. 4-byte-aligned long and double arguments
2447 // and spilled variables on the stack accessed relative to the stack
2448 // pointer register).
2449 // We preserve the "alignment" of `offset` by adjusting it by a multiple of 8.
2450 CHECK_NE(base, AT); // Must not overwrite the register `base` while loading `offset`.
2451
2452 bool doubleword_aligned = IsAligned<kMipsDoublewordSize>(offset);
2453 bool two_accesses = is_doubleword && (!is_float || !doubleword_aligned);
2454
2455 // IsInt<16> must be passed a signed value, hence the static cast below.
2456 if (IsInt<16>(offset) &&
2457 (!two_accesses || IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
2458 // Nothing to do: `offset` (and, if needed, `offset + 4`) fits into int16_t.
2459 return;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002460 }
2461
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002462 // Remember the "(mis)alignment" of `offset`, it will be checked at the end.
2463 uint32_t misalignment = offset & (kMipsDoublewordSize - 1);
2464
2465 // Do not load the whole 32-bit `offset` if it can be represented as
2466 // a sum of two 16-bit signed offsets. This can save an instruction or two.
2467 // To simplify matters, only do this for a symmetric range of offsets from
2468 // about -64KB to about +64KB, allowing further addition of 4 when accessing
2469 // 64-bit variables with two 32-bit accesses.
2470 constexpr int32_t kMinOffsetForSimpleAdjustment = 0x7ff8; // Max int16_t that's a multiple of 8.
2471 constexpr int32_t kMaxOffsetForSimpleAdjustment = 2 * kMinOffsetForSimpleAdjustment;
2472 if (0 <= offset && offset <= kMaxOffsetForSimpleAdjustment) {
2473 Addiu(AT, base, kMinOffsetForSimpleAdjustment);
2474 offset -= kMinOffsetForSimpleAdjustment;
2475 } else if (-kMaxOffsetForSimpleAdjustment <= offset && offset < 0) {
2476 Addiu(AT, base, -kMinOffsetForSimpleAdjustment);
2477 offset += kMinOffsetForSimpleAdjustment;
2478 } else if (IsR6()) {
2479 // On R6 take advantage of the aui instruction, e.g.:
2480 // aui AT, base, offset_high
2481 // lw reg_lo, offset_low(AT)
2482 // lw reg_hi, (offset_low+4)(AT)
2483 // or when offset_low+4 overflows int16_t:
2484 // aui AT, base, offset_high
2485 // addiu AT, AT, 8
2486 // lw reg_lo, (offset_low-8)(AT)
2487 // lw reg_hi, (offset_low-4)(AT)
2488 int16_t offset_high = High16Bits(offset);
2489 int16_t offset_low = Low16Bits(offset);
2490 offset_high += (offset_low < 0) ? 1 : 0; // Account for offset sign extension in load/store.
2491 Aui(AT, base, offset_high);
2492 if (two_accesses && !IsInt<16>(static_cast<int32_t>(offset_low + kMipsWordSize))) {
2493 // Avoid overflow in the 16-bit offset of the load/store instruction when adding 4.
2494 Addiu(AT, AT, kMipsDoublewordSize);
2495 offset_low -= kMipsDoublewordSize;
2496 }
2497 offset = offset_low;
2498 } else {
2499 // Do not load the whole 32-bit `offset` if it can be represented as
2500 // a sum of three 16-bit signed offsets. This can save an instruction.
2501 // To simplify matters, only do this for a symmetric range of offsets from
2502 // about -96KB to about +96KB, allowing further addition of 4 when accessing
2503 // 64-bit variables with two 32-bit accesses.
2504 constexpr int32_t kMinOffsetForMediumAdjustment = 2 * kMinOffsetForSimpleAdjustment;
2505 constexpr int32_t kMaxOffsetForMediumAdjustment = 3 * kMinOffsetForSimpleAdjustment;
2506 if (0 <= offset && offset <= kMaxOffsetForMediumAdjustment) {
2507 Addiu(AT, base, kMinOffsetForMediumAdjustment / 2);
2508 Addiu(AT, AT, kMinOffsetForMediumAdjustment / 2);
2509 offset -= kMinOffsetForMediumAdjustment;
2510 } else if (-kMaxOffsetForMediumAdjustment <= offset && offset < 0) {
2511 Addiu(AT, base, -kMinOffsetForMediumAdjustment / 2);
2512 Addiu(AT, AT, -kMinOffsetForMediumAdjustment / 2);
2513 offset += kMinOffsetForMediumAdjustment;
2514 } else {
2515 // Now that all shorter options have been exhausted, load the full 32-bit offset.
2516 int32_t loaded_offset = RoundDown(offset, kMipsDoublewordSize);
2517 LoadConst32(AT, loaded_offset);
2518 Addu(AT, AT, base);
2519 offset -= loaded_offset;
2520 }
2521 }
2522 base = AT;
2523
2524 CHECK(IsInt<16>(offset));
2525 if (two_accesses) {
2526 CHECK(IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)));
2527 }
2528 CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
2529}
2530
2531void MipsAssembler::LoadFromOffset(LoadOperandType type, Register reg, Register base,
2532 int32_t offset) {
2533 AdjustBaseAndOffset(base, offset, /* is_doubleword */ (type == kLoadDoubleword));
jeffhao7fbee072012-08-24 17:56:54 -07002534 switch (type) {
2535 case kLoadSignedByte:
2536 Lb(reg, base, offset);
2537 break;
2538 case kLoadUnsignedByte:
2539 Lbu(reg, base, offset);
2540 break;
2541 case kLoadSignedHalfword:
2542 Lh(reg, base, offset);
2543 break;
2544 case kLoadUnsignedHalfword:
2545 Lhu(reg, base, offset);
2546 break;
2547 case kLoadWord:
2548 Lw(reg, base, offset);
2549 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002550 case kLoadDoubleword:
2551 if (reg == base) {
2552 // This will clobber the base when loading the lower register. Since we have to load the
2553 // higher register as well, this will fail. Solution: reverse the order.
2554 Lw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
2555 Lw(reg, base, offset);
2556 } else {
2557 Lw(reg, base, offset);
2558 Lw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
2559 }
jeffhao7fbee072012-08-24 17:56:54 -07002560 break;
2561 default:
2562 LOG(FATAL) << "UNREACHABLE";
2563 }
2564}
2565
2566void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002567 AdjustBaseAndOffset(base, offset, /* is_doubleword */ false, /* is_float */ true);
jeffhao7fbee072012-08-24 17:56:54 -07002568 Lwc1(reg, base, offset);
2569}
2570
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002571void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002572 AdjustBaseAndOffset(base, offset, /* is_doubleword */ true, /* is_float */ true);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002573 if (offset & 0x7) {
2574 if (Is32BitFPU()) {
2575 Lwc1(reg, base, offset);
2576 Lwc1(static_cast<FRegister>(reg + 1), base, offset + kMipsWordSize);
2577 } else {
2578 // 64-bit FPU.
2579 Lwc1(reg, base, offset);
2580 Lw(T8, base, offset + kMipsWordSize);
2581 Mthc1(T8, reg);
2582 }
2583 } else {
2584 Ldc1(reg, base, offset);
2585 }
2586}
2587
2588void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
2589 size_t size) {
2590 MipsManagedRegister dst = m_dst.AsMips();
2591 if (dst.IsNoRegister()) {
2592 CHECK_EQ(0u, size) << dst;
2593 } else if (dst.IsCoreRegister()) {
2594 CHECK_EQ(kMipsWordSize, size) << dst;
2595 LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
2596 } else if (dst.IsRegisterPair()) {
2597 CHECK_EQ(kMipsDoublewordSize, size) << dst;
2598 LoadFromOffset(kLoadDoubleword, dst.AsRegisterPairLow(), src_register, src_offset);
2599 } else if (dst.IsFRegister()) {
2600 if (size == kMipsWordSize) {
2601 LoadSFromOffset(dst.AsFRegister(), src_register, src_offset);
2602 } else {
2603 CHECK_EQ(kMipsDoublewordSize, size) << dst;
2604 LoadDFromOffset(dst.AsFRegister(), src_register, src_offset);
2605 }
2606 }
jeffhao7fbee072012-08-24 17:56:54 -07002607}
2608
2609void MipsAssembler::StoreToOffset(StoreOperandType type, Register reg, Register base,
2610 int32_t offset) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002611 // Must not use AT as `reg`, so as not to overwrite the value being stored
2612 // with the adjusted `base`.
2613 CHECK_NE(reg, AT);
2614 AdjustBaseAndOffset(base, offset, /* is_doubleword */ (type == kStoreDoubleword));
jeffhao7fbee072012-08-24 17:56:54 -07002615 switch (type) {
2616 case kStoreByte:
2617 Sb(reg, base, offset);
2618 break;
2619 case kStoreHalfword:
2620 Sh(reg, base, offset);
2621 break;
2622 case kStoreWord:
2623 Sw(reg, base, offset);
2624 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002625 case kStoreDoubleword:
2626 CHECK_NE(reg, base);
2627 CHECK_NE(static_cast<Register>(reg + 1), base);
2628 Sw(reg, base, offset);
2629 Sw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002630 break;
2631 default:
2632 LOG(FATAL) << "UNREACHABLE";
2633 }
2634}
2635
Goran Jakovljevicff734982015-08-24 12:58:55 +00002636void MipsAssembler::StoreSToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002637 AdjustBaseAndOffset(base, offset, /* is_doubleword */ false, /* is_float */ true);
jeffhao7fbee072012-08-24 17:56:54 -07002638 Swc1(reg, base, offset);
2639}
2640
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002641void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002642 AdjustBaseAndOffset(base, offset, /* is_doubleword */ true, /* is_float */ true);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002643 if (offset & 0x7) {
2644 if (Is32BitFPU()) {
2645 Swc1(reg, base, offset);
2646 Swc1(static_cast<FRegister>(reg + 1), base, offset + kMipsWordSize);
2647 } else {
2648 // 64-bit FPU.
2649 Mfhc1(T8, reg);
2650 Swc1(reg, base, offset);
2651 Sw(T8, base, offset + kMipsWordSize);
2652 }
2653 } else {
2654 Sdc1(reg, base, offset);
2655 }
jeffhao7fbee072012-08-24 17:56:54 -07002656}
2657
David Srbeckydd973932015-04-07 20:29:48 +01002658static dwarf::Reg DWARFReg(Register reg) {
2659 return dwarf::Reg::MipsCore(static_cast<int>(reg));
2660}
2661
Ian Rogers790a6b72014-04-01 10:36:00 -07002662constexpr size_t kFramePointerSize = 4;
2663
Vladimir Marko32248382016-05-19 10:37:24 +01002664void MipsAssembler::BuildFrame(size_t frame_size,
2665 ManagedRegister method_reg,
2666 ArrayRef<const ManagedRegister> callee_save_regs,
Dmitry Petrochenkofca82202014-03-21 11:21:37 +07002667 const ManagedRegisterEntrySpills& entry_spills) {
jeffhao7fbee072012-08-24 17:56:54 -07002668 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01002669 DCHECK(!overwriting_);
jeffhao7fbee072012-08-24 17:56:54 -07002670
2671 // Increase frame to required size.
2672 IncreaseFrameSize(frame_size);
2673
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002674 // Push callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07002675 int stack_offset = frame_size - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07002676 StoreToOffset(kStoreWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01002677 cfi_.RelOffset(DWARFReg(RA), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07002678 for (int i = callee_save_regs.size() - 1; i >= 0; --i) {
Ian Rogers790a6b72014-04-01 10:36:00 -07002679 stack_offset -= kFramePointerSize;
Vladimir Marko32248382016-05-19 10:37:24 +01002680 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07002681 StoreToOffset(kStoreWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01002682 cfi_.RelOffset(DWARFReg(reg), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07002683 }
2684
2685 // Write out Method*.
2686 StoreToOffset(kStoreWord, method_reg.AsMips().AsCoreRegister(), SP, 0);
2687
2688 // Write out entry spills.
Goran Jakovljevicff734982015-08-24 12:58:55 +00002689 int32_t offset = frame_size + kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07002690 for (size_t i = 0; i < entry_spills.size(); ++i) {
Goran Jakovljevicff734982015-08-24 12:58:55 +00002691 MipsManagedRegister reg = entry_spills.at(i).AsMips();
2692 if (reg.IsNoRegister()) {
2693 ManagedRegisterSpill spill = entry_spills.at(i);
2694 offset += spill.getSize();
2695 } else if (reg.IsCoreRegister()) {
2696 StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002697 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00002698 } else if (reg.IsFRegister()) {
2699 StoreSToOffset(reg.AsFRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002700 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00002701 } else if (reg.IsDRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002702 StoreDToOffset(reg.AsOverlappingDRegisterLow(), SP, offset);
2703 offset += kMipsDoublewordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00002704 }
jeffhao7fbee072012-08-24 17:56:54 -07002705 }
2706}
2707
2708void MipsAssembler::RemoveFrame(size_t frame_size,
Vladimir Marko32248382016-05-19 10:37:24 +01002709 ArrayRef<const ManagedRegister> callee_save_regs) {
jeffhao7fbee072012-08-24 17:56:54 -07002710 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01002711 DCHECK(!overwriting_);
David Srbeckydd973932015-04-07 20:29:48 +01002712 cfi_.RememberState();
jeffhao7fbee072012-08-24 17:56:54 -07002713
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002714 // Pop callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07002715 int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07002716 for (size_t i = 0; i < callee_save_regs.size(); ++i) {
Vladimir Marko32248382016-05-19 10:37:24 +01002717 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07002718 LoadFromOffset(kLoadWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01002719 cfi_.Restore(DWARFReg(reg));
Ian Rogers790a6b72014-04-01 10:36:00 -07002720 stack_offset += kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07002721 }
2722 LoadFromOffset(kLoadWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01002723 cfi_.Restore(DWARFReg(RA));
jeffhao7fbee072012-08-24 17:56:54 -07002724
2725 // Decrease frame to required size.
2726 DecreaseFrameSize(frame_size);
jeffhao07030602012-09-26 14:33:14 -07002727
2728 // Then jump to the return address.
2729 Jr(RA);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002730 Nop();
David Srbeckydd973932015-04-07 20:29:48 +01002731
2732 // The CFI should be restored for any code that follows the exit block.
2733 cfi_.RestoreState();
2734 cfi_.DefCFAOffset(frame_size);
jeffhao7fbee072012-08-24 17:56:54 -07002735}
2736
2737void MipsAssembler::IncreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002738 CHECK_ALIGNED(adjust, kFramePointerSize);
2739 Addiu32(SP, SP, -adjust);
David Srbeckydd973932015-04-07 20:29:48 +01002740 cfi_.AdjustCFAOffset(adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01002741 if (overwriting_) {
2742 cfi_.OverrideDelayedPC(overwrite_location_);
2743 }
jeffhao7fbee072012-08-24 17:56:54 -07002744}
2745
2746void MipsAssembler::DecreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002747 CHECK_ALIGNED(adjust, kFramePointerSize);
2748 Addiu32(SP, SP, adjust);
David Srbeckydd973932015-04-07 20:29:48 +01002749 cfi_.AdjustCFAOffset(-adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01002750 if (overwriting_) {
2751 cfi_.OverrideDelayedPC(overwrite_location_);
2752 }
jeffhao7fbee072012-08-24 17:56:54 -07002753}
2754
2755void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
2756 MipsManagedRegister src = msrc.AsMips();
2757 if (src.IsNoRegister()) {
2758 CHECK_EQ(0u, size);
2759 } else if (src.IsCoreRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002760 CHECK_EQ(kMipsWordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07002761 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
2762 } else if (src.IsRegisterPair()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002763 CHECK_EQ(kMipsDoublewordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07002764 StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
2765 StoreToOffset(kStoreWord, src.AsRegisterPairHigh(),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002766 SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002767 } else if (src.IsFRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002768 if (size == kMipsWordSize) {
2769 StoreSToOffset(src.AsFRegister(), SP, dest.Int32Value());
2770 } else {
2771 CHECK_EQ(kMipsDoublewordSize, size);
2772 StoreDToOffset(src.AsFRegister(), SP, dest.Int32Value());
2773 }
jeffhao7fbee072012-08-24 17:56:54 -07002774 }
2775}
2776
2777void MipsAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
2778 MipsManagedRegister src = msrc.AsMips();
2779 CHECK(src.IsCoreRegister());
2780 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
2781}
2782
2783void MipsAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
2784 MipsManagedRegister src = msrc.AsMips();
2785 CHECK(src.IsCoreRegister());
2786 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
2787}
2788
2789void MipsAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
2790 ManagedRegister mscratch) {
2791 MipsManagedRegister scratch = mscratch.AsMips();
2792 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002793 LoadConst32(scratch.AsCoreRegister(), imm);
jeffhao7fbee072012-08-24 17:56:54 -07002794 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
2795}
2796
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002797void MipsAssembler::StoreImmediateToThread32(ThreadOffset<kMipsWordSize> dest, uint32_t imm,
jeffhao7fbee072012-08-24 17:56:54 -07002798 ManagedRegister mscratch) {
2799 MipsManagedRegister scratch = mscratch.AsMips();
2800 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002801 // Is this function even referenced anywhere else in the code?
2802 LoadConst32(scratch.AsCoreRegister(), imm);
2803 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), S1, dest.Int32Value());
2804}
2805
2806void MipsAssembler::StoreStackOffsetToThread32(ThreadOffset<kMipsWordSize> thr_offs,
2807 FrameOffset fr_offs,
2808 ManagedRegister mscratch) {
2809 MipsManagedRegister scratch = mscratch.AsMips();
2810 CHECK(scratch.IsCoreRegister()) << scratch;
2811 Addiu32(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07002812 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
2813 S1, thr_offs.Int32Value());
2814}
2815
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002816void MipsAssembler::StoreStackPointerToThread32(ThreadOffset<kMipsWordSize> thr_offs) {
jeffhao7fbee072012-08-24 17:56:54 -07002817 StoreToOffset(kStoreWord, SP, S1, thr_offs.Int32Value());
2818}
2819
2820void MipsAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
2821 FrameOffset in_off, ManagedRegister mscratch) {
2822 MipsManagedRegister src = msrc.AsMips();
2823 MipsManagedRegister scratch = mscratch.AsMips();
2824 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
2825 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002826 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002827}
2828
2829void MipsAssembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) {
2830 return EmitLoad(mdest, SP, src.Int32Value(), size);
2831}
2832
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002833void MipsAssembler::LoadFromThread32(ManagedRegister mdest,
2834 ThreadOffset<kMipsWordSize> src, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07002835 return EmitLoad(mdest, S1, src.Int32Value(), size);
2836}
2837
2838void MipsAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) {
2839 MipsManagedRegister dest = mdest.AsMips();
2840 CHECK(dest.IsCoreRegister());
2841 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value());
2842}
2843
Mathieu Chartiere401d142015-04-22 13:56:20 -07002844void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs,
Roland Levillain4d027112015-07-01 15:41:14 +01002845 bool unpoison_reference) {
jeffhao7fbee072012-08-24 17:56:54 -07002846 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002847 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07002848 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
2849 base.AsMips().AsCoreRegister(), offs.Int32Value());
Roland Levillain4d027112015-07-01 15:41:14 +01002850 if (kPoisonHeapReferences && unpoison_reference) {
Hiroshi Yamauchie63a7452014-02-27 14:44:36 -08002851 Subu(dest.AsCoreRegister(), ZERO, dest.AsCoreRegister());
2852 }
jeffhao7fbee072012-08-24 17:56:54 -07002853}
2854
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002855void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) {
jeffhao7fbee072012-08-24 17:56:54 -07002856 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002857 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07002858 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
2859 base.AsMips().AsCoreRegister(), offs.Int32Value());
2860}
2861
Ian Rogersdd7624d2014-03-14 17:43:00 -07002862void MipsAssembler::LoadRawPtrFromThread32(ManagedRegister mdest,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002863 ThreadOffset<kMipsWordSize> offs) {
jeffhao7fbee072012-08-24 17:56:54 -07002864 MipsManagedRegister dest = mdest.AsMips();
2865 CHECK(dest.IsCoreRegister());
2866 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), S1, offs.Int32Value());
2867}
2868
2869void MipsAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
2870 UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips";
2871}
2872
2873void MipsAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
2874 UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips";
2875}
2876
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002877void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07002878 MipsManagedRegister dest = mdest.AsMips();
2879 MipsManagedRegister src = msrc.AsMips();
2880 if (!dest.Equals(src)) {
2881 if (dest.IsCoreRegister()) {
2882 CHECK(src.IsCoreRegister()) << src;
2883 Move(dest.AsCoreRegister(), src.AsCoreRegister());
2884 } else if (dest.IsFRegister()) {
2885 CHECK(src.IsFRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002886 if (size == kMipsWordSize) {
2887 MovS(dest.AsFRegister(), src.AsFRegister());
2888 } else {
2889 CHECK_EQ(kMipsDoublewordSize, size);
2890 MovD(dest.AsFRegister(), src.AsFRegister());
2891 }
jeffhao7fbee072012-08-24 17:56:54 -07002892 } else if (dest.IsDRegister()) {
2893 CHECK(src.IsDRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002894 MovD(dest.AsOverlappingDRegisterLow(), src.AsOverlappingDRegisterLow());
jeffhao7fbee072012-08-24 17:56:54 -07002895 } else {
2896 CHECK(dest.IsRegisterPair()) << dest;
2897 CHECK(src.IsRegisterPair()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002898 // Ensure that the first move doesn't clobber the input of the second.
jeffhao7fbee072012-08-24 17:56:54 -07002899 if (src.AsRegisterPairHigh() != dest.AsRegisterPairLow()) {
2900 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
2901 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
2902 } else {
2903 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
2904 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
2905 }
2906 }
2907 }
2908}
2909
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002910void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07002911 MipsManagedRegister scratch = mscratch.AsMips();
2912 CHECK(scratch.IsCoreRegister()) << scratch;
2913 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
2914 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
2915}
2916
Ian Rogersdd7624d2014-03-14 17:43:00 -07002917void MipsAssembler::CopyRawPtrFromThread32(FrameOffset fr_offs,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002918 ThreadOffset<kMipsWordSize> thr_offs,
2919 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07002920 MipsManagedRegister scratch = mscratch.AsMips();
2921 CHECK(scratch.IsCoreRegister()) << scratch;
2922 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
2923 S1, thr_offs.Int32Value());
2924 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
2925 SP, fr_offs.Int32Value());
2926}
2927
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002928void MipsAssembler::CopyRawPtrToThread32(ThreadOffset<kMipsWordSize> thr_offs,
2929 FrameOffset fr_offs,
2930 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07002931 MipsManagedRegister scratch = mscratch.AsMips();
2932 CHECK(scratch.IsCoreRegister()) << scratch;
2933 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
2934 SP, fr_offs.Int32Value());
2935 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
2936 S1, thr_offs.Int32Value());
2937}
2938
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002939void MipsAssembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07002940 MipsManagedRegister scratch = mscratch.AsMips();
2941 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002942 CHECK(size == kMipsWordSize || size == kMipsDoublewordSize) << size;
2943 if (size == kMipsWordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07002944 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
2945 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002946 } else if (size == kMipsDoublewordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07002947 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
2948 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002949 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + kMipsWordSize);
2950 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002951 }
2952}
2953
2954void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
2955 ManagedRegister mscratch, size_t size) {
2956 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002957 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002958 LoadFromOffset(kLoadWord, scratch, src_base.AsMips().AsCoreRegister(), src_offset.Int32Value());
2959 StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
2960}
2961
2962void MipsAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
2963 ManagedRegister mscratch, size_t size) {
2964 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002965 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002966 LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
2967 StoreToOffset(kStoreWord, scratch, dest_base.AsMips().AsCoreRegister(), dest_offset.Int32Value());
2968}
2969
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002970void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
2971 FrameOffset src_base ATTRIBUTE_UNUSED,
2972 Offset src_offset ATTRIBUTE_UNUSED,
2973 ManagedRegister mscratch ATTRIBUTE_UNUSED,
2974 size_t size ATTRIBUTE_UNUSED) {
2975 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002976}
2977
2978void MipsAssembler::Copy(ManagedRegister dest, Offset dest_offset,
2979 ManagedRegister src, Offset src_offset,
2980 ManagedRegister mscratch, size_t size) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002981 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002982 Register scratch = mscratch.AsMips().AsCoreRegister();
2983 LoadFromOffset(kLoadWord, scratch, src.AsMips().AsCoreRegister(), src_offset.Int32Value());
2984 StoreToOffset(kStoreWord, scratch, dest.AsMips().AsCoreRegister(), dest_offset.Int32Value());
2985}
2986
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002987void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
2988 Offset dest_offset ATTRIBUTE_UNUSED,
2989 FrameOffset src ATTRIBUTE_UNUSED,
2990 Offset src_offset ATTRIBUTE_UNUSED,
2991 ManagedRegister mscratch ATTRIBUTE_UNUSED,
2992 size_t size ATTRIBUTE_UNUSED) {
2993 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002994}
2995
2996void MipsAssembler::MemoryBarrier(ManagedRegister) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002997 // TODO: sync?
2998 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002999}
3000
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003001void MipsAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003002 FrameOffset handle_scope_offset,
3003 ManagedRegister min_reg,
3004 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003005 MipsManagedRegister out_reg = mout_reg.AsMips();
3006 MipsManagedRegister in_reg = min_reg.AsMips();
3007 CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
3008 CHECK(out_reg.IsCoreRegister()) << out_reg;
3009 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003010 MipsLabel null_arg;
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003011 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3012 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003013 // E.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset).
jeffhao7fbee072012-08-24 17:56:54 -07003014 if (in_reg.IsNoRegister()) {
3015 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003016 SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003017 in_reg = out_reg;
3018 }
3019 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003020 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003021 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003022 Beqz(in_reg.AsCoreRegister(), &null_arg);
3023 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3024 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003025 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003026 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003027 }
3028}
3029
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003030void MipsAssembler::CreateHandleScopeEntry(FrameOffset out_off,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003031 FrameOffset handle_scope_offset,
3032 ManagedRegister mscratch,
3033 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003034 MipsManagedRegister scratch = mscratch.AsMips();
3035 CHECK(scratch.IsCoreRegister()) << scratch;
3036 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003037 MipsLabel null_arg;
3038 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003039 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3040 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003041 // E.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset).
3042 Beqz(scratch.AsCoreRegister(), &null_arg);
3043 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3044 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003045 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003046 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003047 }
3048 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
3049}
3050
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003051// Given a handle scope entry, load the associated reference.
3052void MipsAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003053 ManagedRegister min_reg) {
jeffhao7fbee072012-08-24 17:56:54 -07003054 MipsManagedRegister out_reg = mout_reg.AsMips();
3055 MipsManagedRegister in_reg = min_reg.AsMips();
3056 CHECK(out_reg.IsCoreRegister()) << out_reg;
3057 CHECK(in_reg.IsCoreRegister()) << in_reg;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003058 MipsLabel null_arg;
jeffhao7fbee072012-08-24 17:56:54 -07003059 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003060 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003061 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003062 Beqz(in_reg.AsCoreRegister(), &null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003063 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
3064 in_reg.AsCoreRegister(), 0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003065 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003066}
3067
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003068void MipsAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,
3069 bool could_be_null ATTRIBUTE_UNUSED) {
3070 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003071}
3072
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003073void MipsAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED,
3074 bool could_be_null ATTRIBUTE_UNUSED) {
3075 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003076}
3077
3078void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) {
3079 MipsManagedRegister base = mbase.AsMips();
3080 MipsManagedRegister scratch = mscratch.AsMips();
3081 CHECK(base.IsCoreRegister()) << base;
3082 CHECK(scratch.IsCoreRegister()) << scratch;
3083 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3084 base.AsCoreRegister(), offset.Int32Value());
3085 Jalr(scratch.AsCoreRegister());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003086 Nop();
3087 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003088}
3089
3090void MipsAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
3091 MipsManagedRegister scratch = mscratch.AsMips();
3092 CHECK(scratch.IsCoreRegister()) << scratch;
3093 // Call *(*(SP + base) + offset)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003094 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, base.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003095 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3096 scratch.AsCoreRegister(), offset.Int32Value());
3097 Jalr(scratch.AsCoreRegister());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003098 Nop();
3099 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003100}
3101
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003102void MipsAssembler::CallFromThread32(ThreadOffset<kMipsWordSize> offset ATTRIBUTE_UNUSED,
3103 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
Ian Rogers468532e2013-08-05 10:56:33 -07003104 UNIMPLEMENTED(FATAL) << "no mips implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003105}
3106
3107void MipsAssembler::GetCurrentThread(ManagedRegister tr) {
3108 Move(tr.AsMips().AsCoreRegister(), S1);
3109}
3110
3111void MipsAssembler::GetCurrentThread(FrameOffset offset,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003112 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
jeffhao7fbee072012-08-24 17:56:54 -07003113 StoreToOffset(kStoreWord, S1, SP, offset.Int32Value());
3114}
3115
jeffhao7fbee072012-08-24 17:56:54 -07003116void MipsAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
3117 MipsManagedRegister scratch = mscratch.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003118 exception_blocks_.emplace_back(scratch, stack_adjust);
jeffhao7fbee072012-08-24 17:56:54 -07003119 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003120 S1, Thread::ExceptionOffset<kMipsWordSize>().Int32Value());
3121 // TODO: on MIPS32R6 prefer Bnezc(scratch.AsCoreRegister(), slow.Entry());
3122 // as the NAL instruction (occurring in long R2 branches) may become deprecated.
3123 // For now use common for R2 and R6 instructions as this code must execute on both.
3124 Bnez(scratch.AsCoreRegister(), exception_blocks_.back().Entry());
jeffhao7fbee072012-08-24 17:56:54 -07003125}
3126
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003127void MipsAssembler::EmitExceptionPoll(MipsExceptionSlowPath* exception) {
3128 Bind(exception->Entry());
3129 if (exception->stack_adjust_ != 0) { // Fix up the frame.
3130 DecreaseFrameSize(exception->stack_adjust_);
jeffhao7fbee072012-08-24 17:56:54 -07003131 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003132 // Pass exception object as argument.
3133 // Don't care about preserving A0 as this call won't return.
3134 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
3135 Move(A0, exception->scratch_.AsCoreRegister());
3136 // Set up call to Thread::Current()->pDeliverException.
3137 LoadFromOffset(kLoadWord, T9, S1,
3138 QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pDeliverException).Int32Value());
3139 Jr(T9);
3140 Nop();
3141
3142 // Call never returns.
3143 Break();
jeffhao7fbee072012-08-24 17:56:54 -07003144}
3145
3146} // namespace mips
3147} // namespace art