blob: ebaf1c0cabd61a1b1e14c2fb29f4ada210d98205 [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
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002027void MipsAssembler::FinalizeLabeledBranch(MipsLabel* label) {
2028 uint32_t length = branches_.back().GetLength();
2029 if (!label->IsBound()) {
2030 // Branch forward (to a following label), distance is unknown.
2031 // The first branch forward will contain 0, serving as the terminator of
2032 // the list of forward-reaching branches.
2033 Emit(label->position_);
2034 length--;
2035 // Now make the label object point to this branch
2036 // (this forms a linked list of branches preceding this label).
2037 uint32_t branch_id = branches_.size() - 1;
2038 label->LinkTo(branch_id);
2039 }
2040 // Reserve space for the branch.
2041 while (length--) {
2042 Nop();
2043 }
2044}
2045
2046void MipsAssembler::Buncond(MipsLabel* label) {
2047 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002048 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ false);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002049 FinalizeLabeledBranch(label);
2050}
2051
2052void MipsAssembler::Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs) {
2053 // If lhs = rhs, this can be a NOP.
2054 if (Branch::IsNop(condition, lhs, rhs)) {
2055 return;
2056 }
2057 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
2058 branches_.emplace_back(IsR6(), buffer_.Size(), target, condition, lhs, rhs);
2059 FinalizeLabeledBranch(label);
2060}
2061
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002062void MipsAssembler::Call(MipsLabel* label) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002063 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002064 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ true);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002065 FinalizeLabeledBranch(label);
2066}
2067
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002068Literal* MipsAssembler::NewLiteral(size_t size, const uint8_t* data) {
2069 DCHECK(size == 4u || size == 8u) << size;
2070 literals_.emplace_back(size, data);
2071 return &literals_.back();
2072}
2073
2074void MipsAssembler::LoadLiteral(Register dest_reg, Register base_reg, Literal* literal) {
2075 // Literal loads are treated as pseudo branches since they require very similar handling.
2076 DCHECK_EQ(literal->GetSize(), 4u);
2077 MipsLabel* label = literal->GetLabel();
2078 DCHECK(!label->IsBound());
2079 branches_.emplace_back(IsR6(),
2080 buffer_.Size(),
2081 dest_reg,
2082 base_reg);
2083 FinalizeLabeledBranch(label);
2084}
2085
2086void MipsAssembler::EmitLiterals() {
2087 if (!literals_.empty()) {
2088 // We don't support byte and half-word literals.
2089 // TODO: proper alignment for 64-bit literals when they're implemented.
2090 for (Literal& literal : literals_) {
2091 MipsLabel* label = literal.GetLabel();
2092 Bind(label);
2093 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
2094 DCHECK(literal.GetSize() == 4u || literal.GetSize() == 8u);
2095 for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
2096 buffer_.Emit<uint8_t>(literal.GetData()[i]);
2097 }
2098 }
2099 }
2100}
2101
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002102void MipsAssembler::PromoteBranches() {
2103 // Promote short branches to long as necessary.
2104 bool changed;
2105 do {
2106 changed = false;
2107 for (auto& branch : branches_) {
2108 CHECK(branch.IsResolved());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002109 uint32_t base = GetBranchLocationOrPcRelBase(&branch);
2110 uint32_t delta = branch.PromoteIfNeeded(base);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002111 // If this branch has been promoted and needs to expand in size,
2112 // relocate all branches by the expansion size.
2113 if (delta) {
2114 changed = true;
2115 uint32_t expand_location = branch.GetLocation();
2116 for (auto& branch2 : branches_) {
2117 branch2.Relocate(expand_location, delta);
2118 }
2119 }
2120 }
2121 } while (changed);
2122
2123 // Account for branch expansion by resizing the code buffer
2124 // and moving the code in it to its final location.
2125 size_t branch_count = branches_.size();
2126 if (branch_count > 0) {
2127 // Resize.
2128 Branch& last_branch = branches_[branch_count - 1];
2129 uint32_t size_delta = last_branch.GetEndLocation() - last_branch.GetOldEndLocation();
2130 uint32_t old_size = buffer_.Size();
2131 buffer_.Resize(old_size + size_delta);
2132 // Move the code residing between branch placeholders.
2133 uint32_t end = old_size;
2134 for (size_t i = branch_count; i > 0; ) {
2135 Branch& branch = branches_[--i];
2136 uint32_t size = end - branch.GetOldEndLocation();
2137 buffer_.Move(branch.GetEndLocation(), branch.GetOldEndLocation(), size);
2138 end = branch.GetOldLocation();
2139 }
2140 }
2141}
2142
2143// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
2144const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = {
2145 // R2 short branches.
2146 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kUncondBranch
2147 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002148 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCall
2149 // R2 near literal.
2150 { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002151 // R2 long branches.
2152 { 9, 3, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongUncondBranch
2153 { 10, 4, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCondBranch
2154 { 6, 1, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCall
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002155 // R2 far literal.
2156 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002157 // R6 short branches.
2158 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6UncondBranch
2159 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kR6CondBranch
2160 // Exception: kOffset23 for beqzc/bnezc.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002161 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6Call
2162 // R6 near literal.
2163 { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Literal
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002164 // R6 long branches.
2165 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongUncondBranch
2166 { 3, 1, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002167 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCall
2168 // R6 far literal.
2169 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002170};
2171
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002172// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002173void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
2174 CHECK_EQ(overwriting_, true);
2175 overwrite_location_ = branch->GetLocation();
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002176 uint32_t offset = branch->GetOffset(GetBranchOrPcRelBaseForEncoding(branch));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002177 BranchCondition condition = branch->GetCondition();
2178 Register lhs = branch->GetLeftRegister();
2179 Register rhs = branch->GetRightRegister();
2180 switch (branch->GetType()) {
2181 // R2 short branches.
2182 case Branch::kUncondBranch:
2183 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2184 B(offset);
2185 Nop(); // TODO: improve by filling the delay slot.
2186 break;
2187 case Branch::kCondBranch:
2188 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002189 EmitBcondR2(condition, lhs, rhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002190 Nop(); // TODO: improve by filling the delay slot.
2191 break;
2192 case Branch::kCall:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002193 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002194 Bal(offset);
2195 Nop(); // TODO: improve by filling the delay slot.
2196 break;
2197
2198 // R2 near literal.
2199 case Branch::kLiteral:
2200 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2201 Lw(lhs, rhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002202 break;
2203
2204 // R2 long branches.
2205 case Branch::kLongUncondBranch:
2206 // To get the value of the PC register we need to use the NAL instruction.
2207 // NAL clobbers the RA register. However, RA must be preserved if the
2208 // method is compiled without the entry/exit sequences that would take care
2209 // of preserving RA (typically, leaf methods don't preserve RA explicitly).
2210 // So, we need to preserve RA in some temporary storage ourselves. The AT
2211 // register can't be used for this because we need it to load a constant
2212 // which will be added to the value that NAL stores in RA. And we can't
2213 // use T9 for this in the context of the JNI compiler, which uses it
2214 // as a scratch register (see InterproceduralScratchRegister()).
2215 // If we were to add a 32-bit constant to RA using two ADDIU instructions,
2216 // we'd also need to use the ROTR instruction, which requires no less than
2217 // MIPSR2.
2218 // Perhaps, we could use T8 or one of R2's multiplier/divider registers
2219 // (LO or HI) or even a floating-point register, but that doesn't seem
2220 // like a nice solution. We may want this to work on both R6 and pre-R6.
2221 // For now simply use the stack for RA. This should be OK since for the
2222 // vast majority of code a short PC-relative branch is sufficient.
2223 // TODO: can this be improved?
2224 Push(RA);
2225 Nal();
2226 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2227 Lui(AT, High16Bits(offset));
2228 Ori(AT, AT, Low16Bits(offset));
2229 Addu(AT, AT, RA);
2230 Lw(RA, SP, 0);
2231 Jr(AT);
2232 DecreaseFrameSize(kMipsWordSize);
2233 break;
2234 case Branch::kLongCondBranch:
2235 // The comment on case 'Branch::kLongUncondBranch' applies here as well.
2236 // Note: the opposite condition branch encodes 8 as the distance, which is equal to the
2237 // number of instructions skipped:
2238 // (PUSH(IncreaseFrameSize(ADDIU) + SW) + NAL + LUI + ORI + ADDU + LW + JR).
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002239 EmitBcondR2(Branch::OppositeCondition(condition), lhs, rhs, 8);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002240 Push(RA);
2241 Nal();
2242 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2243 Lui(AT, High16Bits(offset));
2244 Ori(AT, AT, Low16Bits(offset));
2245 Addu(AT, AT, RA);
2246 Lw(RA, SP, 0);
2247 Jr(AT);
2248 DecreaseFrameSize(kMipsWordSize);
2249 break;
2250 case Branch::kLongCall:
2251 Nal();
2252 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2253 Lui(AT, High16Bits(offset));
2254 Ori(AT, AT, Low16Bits(offset));
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002255 Addu(AT, AT, RA);
2256 Jalr(AT);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002257 Nop();
2258 break;
2259
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002260 // R2 far literal.
2261 case Branch::kFarLiteral:
2262 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2263 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2264 Lui(AT, High16Bits(offset));
2265 Addu(AT, AT, rhs);
2266 Lw(lhs, AT, Low16Bits(offset));
2267 break;
2268
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002269 // R6 short branches.
2270 case Branch::kR6UncondBranch:
2271 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2272 Bc(offset);
2273 break;
2274 case Branch::kR6CondBranch:
2275 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002276 EmitBcondR6(condition, lhs, rhs, offset);
2277 Nop(); // TODO: improve by filling the forbidden/delay slot.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002278 break;
2279 case Branch::kR6Call:
2280 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002281 Balc(offset);
2282 break;
2283
2284 // R6 near literal.
2285 case Branch::kR6Literal:
2286 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2287 Lwpc(lhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002288 break;
2289
2290 // R6 long branches.
2291 case Branch::kR6LongUncondBranch:
2292 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2293 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2294 Auipc(AT, High16Bits(offset));
2295 Jic(AT, Low16Bits(offset));
2296 break;
2297 case Branch::kR6LongCondBranch:
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002298 EmitBcondR6(Branch::OppositeCondition(condition), lhs, rhs, 2);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002299 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2300 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2301 Auipc(AT, High16Bits(offset));
2302 Jic(AT, Low16Bits(offset));
2303 break;
2304 case Branch::kR6LongCall:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002305 offset += (offset & 0x8000) << 1; // Account for sign extension in jialc.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002306 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002307 Auipc(AT, High16Bits(offset));
2308 Jialc(AT, Low16Bits(offset));
2309 break;
2310
2311 // R6 far literal.
2312 case Branch::kR6FarLiteral:
2313 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2314 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2315 Auipc(AT, High16Bits(offset));
2316 Lw(lhs, AT, Low16Bits(offset));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002317 break;
2318 }
2319 CHECK_EQ(overwrite_location_, branch->GetEndLocation());
2320 CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize));
2321}
2322
2323void MipsAssembler::B(MipsLabel* label) {
2324 Buncond(label);
2325}
2326
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002327void MipsAssembler::Bal(MipsLabel* label) {
2328 Call(label);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002329}
2330
2331void MipsAssembler::Beq(Register rs, Register rt, MipsLabel* label) {
2332 Bcond(label, kCondEQ, rs, rt);
2333}
2334
2335void MipsAssembler::Bne(Register rs, Register rt, MipsLabel* label) {
2336 Bcond(label, kCondNE, rs, rt);
2337}
2338
2339void MipsAssembler::Beqz(Register rt, MipsLabel* label) {
2340 Bcond(label, kCondEQZ, rt);
2341}
2342
2343void MipsAssembler::Bnez(Register rt, MipsLabel* label) {
2344 Bcond(label, kCondNEZ, rt);
2345}
2346
2347void MipsAssembler::Bltz(Register rt, MipsLabel* label) {
2348 Bcond(label, kCondLTZ, rt);
2349}
2350
2351void MipsAssembler::Bgez(Register rt, MipsLabel* label) {
2352 Bcond(label, kCondGEZ, rt);
2353}
2354
2355void MipsAssembler::Blez(Register rt, MipsLabel* label) {
2356 Bcond(label, kCondLEZ, rt);
2357}
2358
2359void MipsAssembler::Bgtz(Register rt, MipsLabel* label) {
2360 Bcond(label, kCondGTZ, rt);
2361}
2362
2363void MipsAssembler::Blt(Register rs, Register rt, MipsLabel* label) {
2364 if (IsR6()) {
2365 Bcond(label, kCondLT, rs, rt);
2366 } else if (!Branch::IsNop(kCondLT, rs, rt)) {
2367 // Synthesize the instruction (not available on R2).
2368 Slt(AT, rs, rt);
2369 Bnez(AT, label);
2370 }
2371}
2372
2373void MipsAssembler::Bge(Register rs, Register rt, MipsLabel* label) {
2374 if (IsR6()) {
2375 Bcond(label, kCondGE, rs, rt);
2376 } else if (Branch::IsUncond(kCondGE, rs, rt)) {
2377 B(label);
2378 } else {
2379 // Synthesize the instruction (not available on R2).
2380 Slt(AT, rs, rt);
2381 Beqz(AT, label);
2382 }
2383}
2384
2385void MipsAssembler::Bltu(Register rs, Register rt, MipsLabel* label) {
2386 if (IsR6()) {
2387 Bcond(label, kCondLTU, rs, rt);
2388 } else if (!Branch::IsNop(kCondLTU, rs, rt)) {
2389 // Synthesize the instruction (not available on R2).
2390 Sltu(AT, rs, rt);
2391 Bnez(AT, label);
2392 }
2393}
2394
2395void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) {
2396 if (IsR6()) {
2397 Bcond(label, kCondGEU, rs, rt);
2398 } else if (Branch::IsUncond(kCondGEU, rs, rt)) {
2399 B(label);
2400 } else {
2401 // Synthesize the instruction (not available on R2).
2402 Sltu(AT, rs, rt);
2403 Beqz(AT, label);
jeffhao7fbee072012-08-24 17:56:54 -07002404 }
2405}
2406
Chris Larsenb74353a2015-11-20 09:07:09 -08002407void MipsAssembler::Bc1f(MipsLabel* label) {
2408 Bc1f(0, label);
2409}
2410
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002411void MipsAssembler::Bc1f(int cc, MipsLabel* label) {
2412 CHECK(IsUint<3>(cc)) << cc;
2413 Bcond(label, kCondF, static_cast<Register>(cc), ZERO);
2414}
2415
Chris Larsenb74353a2015-11-20 09:07:09 -08002416void MipsAssembler::Bc1t(MipsLabel* label) {
2417 Bc1t(0, label);
2418}
2419
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002420void MipsAssembler::Bc1t(int cc, MipsLabel* label) {
2421 CHECK(IsUint<3>(cc)) << cc;
2422 Bcond(label, kCondT, static_cast<Register>(cc), ZERO);
2423}
2424
2425void MipsAssembler::Bc1eqz(FRegister ft, MipsLabel* label) {
2426 Bcond(label, kCondF, static_cast<Register>(ft), ZERO);
2427}
2428
2429void MipsAssembler::Bc1nez(FRegister ft, MipsLabel* label) {
2430 Bcond(label, kCondT, static_cast<Register>(ft), ZERO);
2431}
2432
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002433void MipsAssembler::AdjustBaseAndOffset(Register& base,
2434 int32_t& offset,
2435 bool is_doubleword,
2436 bool is_float) {
2437 // This method is used to adjust the base register and offset pair
2438 // for a load/store when the offset doesn't fit into int16_t.
2439 // It is assumed that `base + offset` is sufficiently aligned for memory
2440 // operands that are machine word in size or smaller. For doubleword-sized
2441 // operands it's assumed that `base` is a multiple of 8, while `offset`
2442 // may be a multiple of 4 (e.g. 4-byte-aligned long and double arguments
2443 // and spilled variables on the stack accessed relative to the stack
2444 // pointer register).
2445 // We preserve the "alignment" of `offset` by adjusting it by a multiple of 8.
2446 CHECK_NE(base, AT); // Must not overwrite the register `base` while loading `offset`.
2447
2448 bool doubleword_aligned = IsAligned<kMipsDoublewordSize>(offset);
2449 bool two_accesses = is_doubleword && (!is_float || !doubleword_aligned);
2450
2451 // IsInt<16> must be passed a signed value, hence the static cast below.
2452 if (IsInt<16>(offset) &&
2453 (!two_accesses || IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
2454 // Nothing to do: `offset` (and, if needed, `offset + 4`) fits into int16_t.
2455 return;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002456 }
2457
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002458 // Remember the "(mis)alignment" of `offset`, it will be checked at the end.
2459 uint32_t misalignment = offset & (kMipsDoublewordSize - 1);
2460
2461 // Do not load the whole 32-bit `offset` if it can be represented as
2462 // a sum of two 16-bit signed offsets. This can save an instruction or two.
2463 // To simplify matters, only do this for a symmetric range of offsets from
2464 // about -64KB to about +64KB, allowing further addition of 4 when accessing
2465 // 64-bit variables with two 32-bit accesses.
2466 constexpr int32_t kMinOffsetForSimpleAdjustment = 0x7ff8; // Max int16_t that's a multiple of 8.
2467 constexpr int32_t kMaxOffsetForSimpleAdjustment = 2 * kMinOffsetForSimpleAdjustment;
2468 if (0 <= offset && offset <= kMaxOffsetForSimpleAdjustment) {
2469 Addiu(AT, base, kMinOffsetForSimpleAdjustment);
2470 offset -= kMinOffsetForSimpleAdjustment;
2471 } else if (-kMaxOffsetForSimpleAdjustment <= offset && offset < 0) {
2472 Addiu(AT, base, -kMinOffsetForSimpleAdjustment);
2473 offset += kMinOffsetForSimpleAdjustment;
2474 } else if (IsR6()) {
2475 // On R6 take advantage of the aui instruction, e.g.:
2476 // aui AT, base, offset_high
2477 // lw reg_lo, offset_low(AT)
2478 // lw reg_hi, (offset_low+4)(AT)
2479 // or when offset_low+4 overflows int16_t:
2480 // aui AT, base, offset_high
2481 // addiu AT, AT, 8
2482 // lw reg_lo, (offset_low-8)(AT)
2483 // lw reg_hi, (offset_low-4)(AT)
2484 int16_t offset_high = High16Bits(offset);
2485 int16_t offset_low = Low16Bits(offset);
2486 offset_high += (offset_low < 0) ? 1 : 0; // Account for offset sign extension in load/store.
2487 Aui(AT, base, offset_high);
2488 if (two_accesses && !IsInt<16>(static_cast<int32_t>(offset_low + kMipsWordSize))) {
2489 // Avoid overflow in the 16-bit offset of the load/store instruction when adding 4.
2490 Addiu(AT, AT, kMipsDoublewordSize);
2491 offset_low -= kMipsDoublewordSize;
2492 }
2493 offset = offset_low;
2494 } else {
2495 // Do not load the whole 32-bit `offset` if it can be represented as
2496 // a sum of three 16-bit signed offsets. This can save an instruction.
2497 // To simplify matters, only do this for a symmetric range of offsets from
2498 // about -96KB to about +96KB, allowing further addition of 4 when accessing
2499 // 64-bit variables with two 32-bit accesses.
2500 constexpr int32_t kMinOffsetForMediumAdjustment = 2 * kMinOffsetForSimpleAdjustment;
2501 constexpr int32_t kMaxOffsetForMediumAdjustment = 3 * kMinOffsetForSimpleAdjustment;
2502 if (0 <= offset && offset <= kMaxOffsetForMediumAdjustment) {
2503 Addiu(AT, base, kMinOffsetForMediumAdjustment / 2);
2504 Addiu(AT, AT, kMinOffsetForMediumAdjustment / 2);
2505 offset -= kMinOffsetForMediumAdjustment;
2506 } else if (-kMaxOffsetForMediumAdjustment <= offset && offset < 0) {
2507 Addiu(AT, base, -kMinOffsetForMediumAdjustment / 2);
2508 Addiu(AT, AT, -kMinOffsetForMediumAdjustment / 2);
2509 offset += kMinOffsetForMediumAdjustment;
2510 } else {
2511 // Now that all shorter options have been exhausted, load the full 32-bit offset.
2512 int32_t loaded_offset = RoundDown(offset, kMipsDoublewordSize);
2513 LoadConst32(AT, loaded_offset);
2514 Addu(AT, AT, base);
2515 offset -= loaded_offset;
2516 }
2517 }
2518 base = AT;
2519
2520 CHECK(IsInt<16>(offset));
2521 if (two_accesses) {
2522 CHECK(IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)));
2523 }
2524 CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
2525}
2526
2527void MipsAssembler::LoadFromOffset(LoadOperandType type, Register reg, Register base,
2528 int32_t offset) {
2529 AdjustBaseAndOffset(base, offset, /* is_doubleword */ (type == kLoadDoubleword));
jeffhao7fbee072012-08-24 17:56:54 -07002530 switch (type) {
2531 case kLoadSignedByte:
2532 Lb(reg, base, offset);
2533 break;
2534 case kLoadUnsignedByte:
2535 Lbu(reg, base, offset);
2536 break;
2537 case kLoadSignedHalfword:
2538 Lh(reg, base, offset);
2539 break;
2540 case kLoadUnsignedHalfword:
2541 Lhu(reg, base, offset);
2542 break;
2543 case kLoadWord:
2544 Lw(reg, base, offset);
2545 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002546 case kLoadDoubleword:
2547 if (reg == base) {
2548 // This will clobber the base when loading the lower register. Since we have to load the
2549 // higher register as well, this will fail. Solution: reverse the order.
2550 Lw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
2551 Lw(reg, base, offset);
2552 } else {
2553 Lw(reg, base, offset);
2554 Lw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
2555 }
jeffhao7fbee072012-08-24 17:56:54 -07002556 break;
2557 default:
2558 LOG(FATAL) << "UNREACHABLE";
2559 }
2560}
2561
2562void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002563 AdjustBaseAndOffset(base, offset, /* is_doubleword */ false, /* is_float */ true);
jeffhao7fbee072012-08-24 17:56:54 -07002564 Lwc1(reg, base, offset);
2565}
2566
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002567void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002568 AdjustBaseAndOffset(base, offset, /* is_doubleword */ true, /* is_float */ true);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002569 if (offset & 0x7) {
2570 if (Is32BitFPU()) {
2571 Lwc1(reg, base, offset);
2572 Lwc1(static_cast<FRegister>(reg + 1), base, offset + kMipsWordSize);
2573 } else {
2574 // 64-bit FPU.
2575 Lwc1(reg, base, offset);
2576 Lw(T8, base, offset + kMipsWordSize);
2577 Mthc1(T8, reg);
2578 }
2579 } else {
2580 Ldc1(reg, base, offset);
2581 }
2582}
2583
2584void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
2585 size_t size) {
2586 MipsManagedRegister dst = m_dst.AsMips();
2587 if (dst.IsNoRegister()) {
2588 CHECK_EQ(0u, size) << dst;
2589 } else if (dst.IsCoreRegister()) {
2590 CHECK_EQ(kMipsWordSize, size) << dst;
2591 LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
2592 } else if (dst.IsRegisterPair()) {
2593 CHECK_EQ(kMipsDoublewordSize, size) << dst;
2594 LoadFromOffset(kLoadDoubleword, dst.AsRegisterPairLow(), src_register, src_offset);
2595 } else if (dst.IsFRegister()) {
2596 if (size == kMipsWordSize) {
2597 LoadSFromOffset(dst.AsFRegister(), src_register, src_offset);
2598 } else {
2599 CHECK_EQ(kMipsDoublewordSize, size) << dst;
2600 LoadDFromOffset(dst.AsFRegister(), src_register, src_offset);
2601 }
2602 }
jeffhao7fbee072012-08-24 17:56:54 -07002603}
2604
2605void MipsAssembler::StoreToOffset(StoreOperandType type, Register reg, Register base,
2606 int32_t offset) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002607 // Must not use AT as `reg`, so as not to overwrite the value being stored
2608 // with the adjusted `base`.
2609 CHECK_NE(reg, AT);
2610 AdjustBaseAndOffset(base, offset, /* is_doubleword */ (type == kStoreDoubleword));
jeffhao7fbee072012-08-24 17:56:54 -07002611 switch (type) {
2612 case kStoreByte:
2613 Sb(reg, base, offset);
2614 break;
2615 case kStoreHalfword:
2616 Sh(reg, base, offset);
2617 break;
2618 case kStoreWord:
2619 Sw(reg, base, offset);
2620 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002621 case kStoreDoubleword:
2622 CHECK_NE(reg, base);
2623 CHECK_NE(static_cast<Register>(reg + 1), base);
2624 Sw(reg, base, offset);
2625 Sw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002626 break;
2627 default:
2628 LOG(FATAL) << "UNREACHABLE";
2629 }
2630}
2631
Goran Jakovljevicff734982015-08-24 12:58:55 +00002632void MipsAssembler::StoreSToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002633 AdjustBaseAndOffset(base, offset, /* is_doubleword */ false, /* is_float */ true);
jeffhao7fbee072012-08-24 17:56:54 -07002634 Swc1(reg, base, offset);
2635}
2636
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002637void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002638 AdjustBaseAndOffset(base, offset, /* is_doubleword */ true, /* is_float */ true);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002639 if (offset & 0x7) {
2640 if (Is32BitFPU()) {
2641 Swc1(reg, base, offset);
2642 Swc1(static_cast<FRegister>(reg + 1), base, offset + kMipsWordSize);
2643 } else {
2644 // 64-bit FPU.
2645 Mfhc1(T8, reg);
2646 Swc1(reg, base, offset);
2647 Sw(T8, base, offset + kMipsWordSize);
2648 }
2649 } else {
2650 Sdc1(reg, base, offset);
2651 }
jeffhao7fbee072012-08-24 17:56:54 -07002652}
2653
David Srbeckydd973932015-04-07 20:29:48 +01002654static dwarf::Reg DWARFReg(Register reg) {
2655 return dwarf::Reg::MipsCore(static_cast<int>(reg));
2656}
2657
Ian Rogers790a6b72014-04-01 10:36:00 -07002658constexpr size_t kFramePointerSize = 4;
2659
Vladimir Marko32248382016-05-19 10:37:24 +01002660void MipsAssembler::BuildFrame(size_t frame_size,
2661 ManagedRegister method_reg,
2662 ArrayRef<const ManagedRegister> callee_save_regs,
Dmitry Petrochenkofca82202014-03-21 11:21:37 +07002663 const ManagedRegisterEntrySpills& entry_spills) {
jeffhao7fbee072012-08-24 17:56:54 -07002664 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01002665 DCHECK(!overwriting_);
jeffhao7fbee072012-08-24 17:56:54 -07002666
2667 // Increase frame to required size.
2668 IncreaseFrameSize(frame_size);
2669
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002670 // Push callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07002671 int stack_offset = frame_size - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07002672 StoreToOffset(kStoreWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01002673 cfi_.RelOffset(DWARFReg(RA), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07002674 for (int i = callee_save_regs.size() - 1; i >= 0; --i) {
Ian Rogers790a6b72014-04-01 10:36:00 -07002675 stack_offset -= kFramePointerSize;
Vladimir Marko32248382016-05-19 10:37:24 +01002676 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07002677 StoreToOffset(kStoreWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01002678 cfi_.RelOffset(DWARFReg(reg), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07002679 }
2680
2681 // Write out Method*.
2682 StoreToOffset(kStoreWord, method_reg.AsMips().AsCoreRegister(), SP, 0);
2683
2684 // Write out entry spills.
Goran Jakovljevicff734982015-08-24 12:58:55 +00002685 int32_t offset = frame_size + kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07002686 for (size_t i = 0; i < entry_spills.size(); ++i) {
Goran Jakovljevicff734982015-08-24 12:58:55 +00002687 MipsManagedRegister reg = entry_spills.at(i).AsMips();
2688 if (reg.IsNoRegister()) {
2689 ManagedRegisterSpill spill = entry_spills.at(i);
2690 offset += spill.getSize();
2691 } else if (reg.IsCoreRegister()) {
2692 StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002693 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00002694 } else if (reg.IsFRegister()) {
2695 StoreSToOffset(reg.AsFRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002696 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00002697 } else if (reg.IsDRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002698 StoreDToOffset(reg.AsOverlappingDRegisterLow(), SP, offset);
2699 offset += kMipsDoublewordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00002700 }
jeffhao7fbee072012-08-24 17:56:54 -07002701 }
2702}
2703
2704void MipsAssembler::RemoveFrame(size_t frame_size,
Vladimir Marko32248382016-05-19 10:37:24 +01002705 ArrayRef<const ManagedRegister> callee_save_regs) {
jeffhao7fbee072012-08-24 17:56:54 -07002706 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01002707 DCHECK(!overwriting_);
David Srbeckydd973932015-04-07 20:29:48 +01002708 cfi_.RememberState();
jeffhao7fbee072012-08-24 17:56:54 -07002709
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002710 // Pop callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07002711 int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07002712 for (size_t i = 0; i < callee_save_regs.size(); ++i) {
Vladimir Marko32248382016-05-19 10:37:24 +01002713 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07002714 LoadFromOffset(kLoadWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01002715 cfi_.Restore(DWARFReg(reg));
Ian Rogers790a6b72014-04-01 10:36:00 -07002716 stack_offset += kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07002717 }
2718 LoadFromOffset(kLoadWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01002719 cfi_.Restore(DWARFReg(RA));
jeffhao7fbee072012-08-24 17:56:54 -07002720
2721 // Decrease frame to required size.
2722 DecreaseFrameSize(frame_size);
jeffhao07030602012-09-26 14:33:14 -07002723
2724 // Then jump to the return address.
2725 Jr(RA);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002726 Nop();
David Srbeckydd973932015-04-07 20:29:48 +01002727
2728 // The CFI should be restored for any code that follows the exit block.
2729 cfi_.RestoreState();
2730 cfi_.DefCFAOffset(frame_size);
jeffhao7fbee072012-08-24 17:56:54 -07002731}
2732
2733void MipsAssembler::IncreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002734 CHECK_ALIGNED(adjust, kFramePointerSize);
2735 Addiu32(SP, SP, -adjust);
David Srbeckydd973932015-04-07 20:29:48 +01002736 cfi_.AdjustCFAOffset(adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01002737 if (overwriting_) {
2738 cfi_.OverrideDelayedPC(overwrite_location_);
2739 }
jeffhao7fbee072012-08-24 17:56:54 -07002740}
2741
2742void MipsAssembler::DecreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002743 CHECK_ALIGNED(adjust, kFramePointerSize);
2744 Addiu32(SP, SP, adjust);
David Srbeckydd973932015-04-07 20:29:48 +01002745 cfi_.AdjustCFAOffset(-adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01002746 if (overwriting_) {
2747 cfi_.OverrideDelayedPC(overwrite_location_);
2748 }
jeffhao7fbee072012-08-24 17:56:54 -07002749}
2750
2751void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
2752 MipsManagedRegister src = msrc.AsMips();
2753 if (src.IsNoRegister()) {
2754 CHECK_EQ(0u, size);
2755 } else if (src.IsCoreRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002756 CHECK_EQ(kMipsWordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07002757 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
2758 } else if (src.IsRegisterPair()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002759 CHECK_EQ(kMipsDoublewordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07002760 StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
2761 StoreToOffset(kStoreWord, src.AsRegisterPairHigh(),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002762 SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002763 } else if (src.IsFRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002764 if (size == kMipsWordSize) {
2765 StoreSToOffset(src.AsFRegister(), SP, dest.Int32Value());
2766 } else {
2767 CHECK_EQ(kMipsDoublewordSize, size);
2768 StoreDToOffset(src.AsFRegister(), SP, dest.Int32Value());
2769 }
jeffhao7fbee072012-08-24 17:56:54 -07002770 }
2771}
2772
2773void MipsAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
2774 MipsManagedRegister src = msrc.AsMips();
2775 CHECK(src.IsCoreRegister());
2776 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
2777}
2778
2779void MipsAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
2780 MipsManagedRegister src = msrc.AsMips();
2781 CHECK(src.IsCoreRegister());
2782 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
2783}
2784
2785void MipsAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
2786 ManagedRegister mscratch) {
2787 MipsManagedRegister scratch = mscratch.AsMips();
2788 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002789 LoadConst32(scratch.AsCoreRegister(), imm);
jeffhao7fbee072012-08-24 17:56:54 -07002790 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
2791}
2792
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002793void MipsAssembler::StoreImmediateToThread32(ThreadOffset<kMipsWordSize> dest, uint32_t imm,
jeffhao7fbee072012-08-24 17:56:54 -07002794 ManagedRegister mscratch) {
2795 MipsManagedRegister scratch = mscratch.AsMips();
2796 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002797 // Is this function even referenced anywhere else in the code?
2798 LoadConst32(scratch.AsCoreRegister(), imm);
2799 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), S1, dest.Int32Value());
2800}
2801
2802void MipsAssembler::StoreStackOffsetToThread32(ThreadOffset<kMipsWordSize> thr_offs,
2803 FrameOffset fr_offs,
2804 ManagedRegister mscratch) {
2805 MipsManagedRegister scratch = mscratch.AsMips();
2806 CHECK(scratch.IsCoreRegister()) << scratch;
2807 Addiu32(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07002808 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
2809 S1, thr_offs.Int32Value());
2810}
2811
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002812void MipsAssembler::StoreStackPointerToThread32(ThreadOffset<kMipsWordSize> thr_offs) {
jeffhao7fbee072012-08-24 17:56:54 -07002813 StoreToOffset(kStoreWord, SP, S1, thr_offs.Int32Value());
2814}
2815
2816void MipsAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
2817 FrameOffset in_off, ManagedRegister mscratch) {
2818 MipsManagedRegister src = msrc.AsMips();
2819 MipsManagedRegister scratch = mscratch.AsMips();
2820 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
2821 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002822 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002823}
2824
2825void MipsAssembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) {
2826 return EmitLoad(mdest, SP, src.Int32Value(), size);
2827}
2828
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002829void MipsAssembler::LoadFromThread32(ManagedRegister mdest,
2830 ThreadOffset<kMipsWordSize> src, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07002831 return EmitLoad(mdest, S1, src.Int32Value(), size);
2832}
2833
2834void MipsAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) {
2835 MipsManagedRegister dest = mdest.AsMips();
2836 CHECK(dest.IsCoreRegister());
2837 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value());
2838}
2839
Mathieu Chartiere401d142015-04-22 13:56:20 -07002840void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs,
Roland Levillain4d027112015-07-01 15:41:14 +01002841 bool unpoison_reference) {
jeffhao7fbee072012-08-24 17:56:54 -07002842 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002843 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07002844 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
2845 base.AsMips().AsCoreRegister(), offs.Int32Value());
Roland Levillain4d027112015-07-01 15:41:14 +01002846 if (kPoisonHeapReferences && unpoison_reference) {
Hiroshi Yamauchie63a7452014-02-27 14:44:36 -08002847 Subu(dest.AsCoreRegister(), ZERO, dest.AsCoreRegister());
2848 }
jeffhao7fbee072012-08-24 17:56:54 -07002849}
2850
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002851void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) {
jeffhao7fbee072012-08-24 17:56:54 -07002852 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002853 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07002854 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
2855 base.AsMips().AsCoreRegister(), offs.Int32Value());
2856}
2857
Ian Rogersdd7624d2014-03-14 17:43:00 -07002858void MipsAssembler::LoadRawPtrFromThread32(ManagedRegister mdest,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002859 ThreadOffset<kMipsWordSize> offs) {
jeffhao7fbee072012-08-24 17:56:54 -07002860 MipsManagedRegister dest = mdest.AsMips();
2861 CHECK(dest.IsCoreRegister());
2862 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), S1, offs.Int32Value());
2863}
2864
2865void MipsAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
2866 UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips";
2867}
2868
2869void MipsAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
2870 UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips";
2871}
2872
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002873void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07002874 MipsManagedRegister dest = mdest.AsMips();
2875 MipsManagedRegister src = msrc.AsMips();
2876 if (!dest.Equals(src)) {
2877 if (dest.IsCoreRegister()) {
2878 CHECK(src.IsCoreRegister()) << src;
2879 Move(dest.AsCoreRegister(), src.AsCoreRegister());
2880 } else if (dest.IsFRegister()) {
2881 CHECK(src.IsFRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002882 if (size == kMipsWordSize) {
2883 MovS(dest.AsFRegister(), src.AsFRegister());
2884 } else {
2885 CHECK_EQ(kMipsDoublewordSize, size);
2886 MovD(dest.AsFRegister(), src.AsFRegister());
2887 }
jeffhao7fbee072012-08-24 17:56:54 -07002888 } else if (dest.IsDRegister()) {
2889 CHECK(src.IsDRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002890 MovD(dest.AsOverlappingDRegisterLow(), src.AsOverlappingDRegisterLow());
jeffhao7fbee072012-08-24 17:56:54 -07002891 } else {
2892 CHECK(dest.IsRegisterPair()) << dest;
2893 CHECK(src.IsRegisterPair()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002894 // Ensure that the first move doesn't clobber the input of the second.
jeffhao7fbee072012-08-24 17:56:54 -07002895 if (src.AsRegisterPairHigh() != dest.AsRegisterPairLow()) {
2896 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
2897 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
2898 } else {
2899 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
2900 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
2901 }
2902 }
2903 }
2904}
2905
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002906void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07002907 MipsManagedRegister scratch = mscratch.AsMips();
2908 CHECK(scratch.IsCoreRegister()) << scratch;
2909 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
2910 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
2911}
2912
Ian Rogersdd7624d2014-03-14 17:43:00 -07002913void MipsAssembler::CopyRawPtrFromThread32(FrameOffset fr_offs,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002914 ThreadOffset<kMipsWordSize> thr_offs,
2915 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07002916 MipsManagedRegister scratch = mscratch.AsMips();
2917 CHECK(scratch.IsCoreRegister()) << scratch;
2918 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
2919 S1, thr_offs.Int32Value());
2920 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
2921 SP, fr_offs.Int32Value());
2922}
2923
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002924void MipsAssembler::CopyRawPtrToThread32(ThreadOffset<kMipsWordSize> thr_offs,
2925 FrameOffset fr_offs,
2926 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07002927 MipsManagedRegister scratch = mscratch.AsMips();
2928 CHECK(scratch.IsCoreRegister()) << scratch;
2929 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
2930 SP, fr_offs.Int32Value());
2931 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
2932 S1, thr_offs.Int32Value());
2933}
2934
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002935void MipsAssembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07002936 MipsManagedRegister scratch = mscratch.AsMips();
2937 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002938 CHECK(size == kMipsWordSize || size == kMipsDoublewordSize) << size;
2939 if (size == kMipsWordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07002940 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
2941 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002942 } else if (size == kMipsDoublewordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07002943 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
2944 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002945 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + kMipsWordSize);
2946 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002947 }
2948}
2949
2950void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
2951 ManagedRegister mscratch, size_t size) {
2952 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002953 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002954 LoadFromOffset(kLoadWord, scratch, src_base.AsMips().AsCoreRegister(), src_offset.Int32Value());
2955 StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
2956}
2957
2958void MipsAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
2959 ManagedRegister mscratch, size_t size) {
2960 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002961 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002962 LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
2963 StoreToOffset(kStoreWord, scratch, dest_base.AsMips().AsCoreRegister(), dest_offset.Int32Value());
2964}
2965
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002966void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
2967 FrameOffset src_base ATTRIBUTE_UNUSED,
2968 Offset src_offset ATTRIBUTE_UNUSED,
2969 ManagedRegister mscratch ATTRIBUTE_UNUSED,
2970 size_t size ATTRIBUTE_UNUSED) {
2971 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002972}
2973
2974void MipsAssembler::Copy(ManagedRegister dest, Offset dest_offset,
2975 ManagedRegister src, Offset src_offset,
2976 ManagedRegister mscratch, size_t size) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002977 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002978 Register scratch = mscratch.AsMips().AsCoreRegister();
2979 LoadFromOffset(kLoadWord, scratch, src.AsMips().AsCoreRegister(), src_offset.Int32Value());
2980 StoreToOffset(kStoreWord, scratch, dest.AsMips().AsCoreRegister(), dest_offset.Int32Value());
2981}
2982
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002983void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
2984 Offset dest_offset ATTRIBUTE_UNUSED,
2985 FrameOffset src ATTRIBUTE_UNUSED,
2986 Offset src_offset ATTRIBUTE_UNUSED,
2987 ManagedRegister mscratch ATTRIBUTE_UNUSED,
2988 size_t size ATTRIBUTE_UNUSED) {
2989 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002990}
2991
2992void MipsAssembler::MemoryBarrier(ManagedRegister) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002993 // TODO: sync?
2994 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002995}
2996
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002997void MipsAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002998 FrameOffset handle_scope_offset,
2999 ManagedRegister min_reg,
3000 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003001 MipsManagedRegister out_reg = mout_reg.AsMips();
3002 MipsManagedRegister in_reg = min_reg.AsMips();
3003 CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
3004 CHECK(out_reg.IsCoreRegister()) << out_reg;
3005 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003006 MipsLabel null_arg;
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003007 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3008 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003009 // E.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset).
jeffhao7fbee072012-08-24 17:56:54 -07003010 if (in_reg.IsNoRegister()) {
3011 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003012 SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003013 in_reg = out_reg;
3014 }
3015 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003016 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003017 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003018 Beqz(in_reg.AsCoreRegister(), &null_arg);
3019 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3020 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003021 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003022 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003023 }
3024}
3025
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003026void MipsAssembler::CreateHandleScopeEntry(FrameOffset out_off,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003027 FrameOffset handle_scope_offset,
3028 ManagedRegister mscratch,
3029 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003030 MipsManagedRegister scratch = mscratch.AsMips();
3031 CHECK(scratch.IsCoreRegister()) << scratch;
3032 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003033 MipsLabel null_arg;
3034 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003035 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3036 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003037 // E.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset).
3038 Beqz(scratch.AsCoreRegister(), &null_arg);
3039 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3040 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003041 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003042 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003043 }
3044 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
3045}
3046
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003047// Given a handle scope entry, load the associated reference.
3048void MipsAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003049 ManagedRegister min_reg) {
jeffhao7fbee072012-08-24 17:56:54 -07003050 MipsManagedRegister out_reg = mout_reg.AsMips();
3051 MipsManagedRegister in_reg = min_reg.AsMips();
3052 CHECK(out_reg.IsCoreRegister()) << out_reg;
3053 CHECK(in_reg.IsCoreRegister()) << in_reg;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003054 MipsLabel null_arg;
jeffhao7fbee072012-08-24 17:56:54 -07003055 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003056 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003057 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003058 Beqz(in_reg.AsCoreRegister(), &null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003059 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
3060 in_reg.AsCoreRegister(), 0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003061 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003062}
3063
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003064void MipsAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,
3065 bool could_be_null ATTRIBUTE_UNUSED) {
3066 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003067}
3068
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003069void MipsAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED,
3070 bool could_be_null ATTRIBUTE_UNUSED) {
3071 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003072}
3073
3074void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) {
3075 MipsManagedRegister base = mbase.AsMips();
3076 MipsManagedRegister scratch = mscratch.AsMips();
3077 CHECK(base.IsCoreRegister()) << base;
3078 CHECK(scratch.IsCoreRegister()) << scratch;
3079 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3080 base.AsCoreRegister(), offset.Int32Value());
3081 Jalr(scratch.AsCoreRegister());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003082 Nop();
3083 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003084}
3085
3086void MipsAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
3087 MipsManagedRegister scratch = mscratch.AsMips();
3088 CHECK(scratch.IsCoreRegister()) << scratch;
3089 // Call *(*(SP + base) + offset)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003090 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, base.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003091 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3092 scratch.AsCoreRegister(), offset.Int32Value());
3093 Jalr(scratch.AsCoreRegister());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003094 Nop();
3095 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003096}
3097
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003098void MipsAssembler::CallFromThread32(ThreadOffset<kMipsWordSize> offset ATTRIBUTE_UNUSED,
3099 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
Ian Rogers468532e2013-08-05 10:56:33 -07003100 UNIMPLEMENTED(FATAL) << "no mips implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003101}
3102
3103void MipsAssembler::GetCurrentThread(ManagedRegister tr) {
3104 Move(tr.AsMips().AsCoreRegister(), S1);
3105}
3106
3107void MipsAssembler::GetCurrentThread(FrameOffset offset,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003108 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
jeffhao7fbee072012-08-24 17:56:54 -07003109 StoreToOffset(kStoreWord, S1, SP, offset.Int32Value());
3110}
3111
jeffhao7fbee072012-08-24 17:56:54 -07003112void MipsAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
3113 MipsManagedRegister scratch = mscratch.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003114 exception_blocks_.emplace_back(scratch, stack_adjust);
jeffhao7fbee072012-08-24 17:56:54 -07003115 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003116 S1, Thread::ExceptionOffset<kMipsWordSize>().Int32Value());
3117 // TODO: on MIPS32R6 prefer Bnezc(scratch.AsCoreRegister(), slow.Entry());
3118 // as the NAL instruction (occurring in long R2 branches) may become deprecated.
3119 // For now use common for R2 and R6 instructions as this code must execute on both.
3120 Bnez(scratch.AsCoreRegister(), exception_blocks_.back().Entry());
jeffhao7fbee072012-08-24 17:56:54 -07003121}
3122
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003123void MipsAssembler::EmitExceptionPoll(MipsExceptionSlowPath* exception) {
3124 Bind(exception->Entry());
3125 if (exception->stack_adjust_ != 0) { // Fix up the frame.
3126 DecreaseFrameSize(exception->stack_adjust_);
jeffhao7fbee072012-08-24 17:56:54 -07003127 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003128 // Pass exception object as argument.
3129 // Don't care about preserving A0 as this call won't return.
3130 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
3131 Move(A0, exception->scratch_.AsCoreRegister());
3132 // Set up call to Thread::Current()->pDeliverException.
3133 LoadFromOffset(kLoadWord, T9, S1,
3134 QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pDeliverException).Int32Value());
3135 Jr(T9);
3136 Nop();
3137
3138 // Call never returns.
3139 Break();
jeffhao7fbee072012-08-24 17:56:54 -07003140}
3141
3142} // namespace mips
3143} // namespace art