blob: bfc63d14da32b07b3e8a683b80a3a476da072230 [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
Andreas Gampe542451c2016-07-26 09:02:02 -070029static_assert(static_cast<size_t>(kMipsPointerSize) == kMipsWordSize,
30 "Unexpected Mips pointer size.");
31static_assert(kMipsPointerSize == PointerSize::k32, "Unexpected Mips pointer size.");
32
33
jeffhao7fbee072012-08-24 17:56:54 -070034std::ostream& operator<<(std::ostream& os, const DRegister& rhs) {
35 if (rhs >= D0 && rhs < kNumberOfDRegisters) {
36 os << "d" << static_cast<int>(rhs);
37 } else {
38 os << "DRegister[" << static_cast<int>(rhs) << "]";
39 }
40 return os;
41}
42
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +020043void MipsAssembler::FinalizeCode() {
44 for (auto& exception_block : exception_blocks_) {
45 EmitExceptionPoll(&exception_block);
46 }
Alexey Frunzee3fb2452016-05-10 16:08:05 -070047 EmitLiterals();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +020048 PromoteBranches();
49}
50
51void MipsAssembler::FinalizeInstructions(const MemoryRegion& region) {
Vladimir Marko10ef6942015-10-22 15:25:54 +010052 size_t number_of_delayed_adjust_pcs = cfi().NumberOfDelayedAdvancePCs();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +020053 EmitBranches();
54 Assembler::FinalizeInstructions(region);
Vladimir Marko10ef6942015-10-22 15:25:54 +010055 PatchCFI(number_of_delayed_adjust_pcs);
56}
57
58void MipsAssembler::PatchCFI(size_t number_of_delayed_adjust_pcs) {
59 if (cfi().NumberOfDelayedAdvancePCs() == 0u) {
60 DCHECK_EQ(number_of_delayed_adjust_pcs, 0u);
61 return;
62 }
63
64 typedef DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC DelayedAdvancePC;
65 const auto data = cfi().ReleaseStreamAndPrepareForDelayedAdvancePC();
66 const std::vector<uint8_t>& old_stream = data.first;
67 const std::vector<DelayedAdvancePC>& advances = data.second;
68
69 // PCs recorded before EmitBranches() need to be adjusted.
70 // PCs recorded during EmitBranches() are already adjusted.
71 // Both ranges are separately sorted but they may overlap.
72 if (kIsDebugBuild) {
73 auto cmp = [](const DelayedAdvancePC& lhs, const DelayedAdvancePC& rhs) {
74 return lhs.pc < rhs.pc;
75 };
76 CHECK(std::is_sorted(advances.begin(), advances.begin() + number_of_delayed_adjust_pcs, cmp));
77 CHECK(std::is_sorted(advances.begin() + number_of_delayed_adjust_pcs, advances.end(), cmp));
78 }
79
80 // Append initial CFI data if any.
81 size_t size = advances.size();
82 DCHECK_NE(size, 0u);
83 cfi().AppendRawData(old_stream, 0u, advances[0].stream_pos);
84 // Emit PC adjustments interleaved with the old CFI stream.
85 size_t adjust_pos = 0u;
86 size_t late_emit_pos = number_of_delayed_adjust_pcs;
87 while (adjust_pos != number_of_delayed_adjust_pcs || late_emit_pos != size) {
88 size_t adjusted_pc = (adjust_pos != number_of_delayed_adjust_pcs)
89 ? GetAdjustedPosition(advances[adjust_pos].pc)
90 : static_cast<size_t>(-1);
91 size_t late_emit_pc = (late_emit_pos != size)
92 ? advances[late_emit_pos].pc
93 : static_cast<size_t>(-1);
94 size_t advance_pc = std::min(adjusted_pc, late_emit_pc);
95 DCHECK_NE(advance_pc, static_cast<size_t>(-1));
96 size_t entry = (adjusted_pc <= late_emit_pc) ? adjust_pos : late_emit_pos;
97 if (adjusted_pc <= late_emit_pc) {
98 ++adjust_pos;
99 } else {
100 ++late_emit_pos;
101 }
102 cfi().AdvancePC(advance_pc);
103 size_t end_pos = (entry + 1u == size) ? old_stream.size() : advances[entry + 1u].stream_pos;
104 cfi().AppendRawData(old_stream, advances[entry].stream_pos, end_pos);
105 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200106}
107
108void MipsAssembler::EmitBranches() {
109 CHECK(!overwriting_);
110 // Switch from appending instructions at the end of the buffer to overwriting
111 // existing instructions (branch placeholders) in the buffer.
112 overwriting_ = true;
113 for (auto& branch : branches_) {
114 EmitBranch(&branch);
115 }
116 overwriting_ = false;
117}
118
119void MipsAssembler::Emit(uint32_t value) {
120 if (overwriting_) {
121 // Branches to labels are emitted into their placeholders here.
122 buffer_.Store<uint32_t>(overwrite_location_, value);
123 overwrite_location_ += sizeof(uint32_t);
124 } else {
125 // Other instructions are simply appended at the end here.
126 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
127 buffer_.Emit<uint32_t>(value);
128 }
jeffhao7fbee072012-08-24 17:56:54 -0700129}
130
131void MipsAssembler::EmitR(int opcode, Register rs, Register rt, Register rd, int shamt, int funct) {
132 CHECK_NE(rs, kNoRegister);
133 CHECK_NE(rt, kNoRegister);
134 CHECK_NE(rd, kNoRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200135 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
136 static_cast<uint32_t>(rs) << kRsShift |
137 static_cast<uint32_t>(rt) << kRtShift |
138 static_cast<uint32_t>(rd) << kRdShift |
139 shamt << kShamtShift |
140 funct;
jeffhao7fbee072012-08-24 17:56:54 -0700141 Emit(encoding);
142}
143
144void MipsAssembler::EmitI(int opcode, Register rs, Register rt, uint16_t imm) {
145 CHECK_NE(rs, kNoRegister);
146 CHECK_NE(rt, kNoRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200147 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
148 static_cast<uint32_t>(rs) << kRsShift |
149 static_cast<uint32_t>(rt) << kRtShift |
150 imm;
jeffhao7fbee072012-08-24 17:56:54 -0700151 Emit(encoding);
152}
153
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200154void MipsAssembler::EmitI21(int opcode, Register rs, uint32_t imm21) {
155 CHECK_NE(rs, kNoRegister);
156 CHECK(IsUint<21>(imm21)) << imm21;
157 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
158 static_cast<uint32_t>(rs) << kRsShift |
159 imm21;
jeffhao7fbee072012-08-24 17:56:54 -0700160 Emit(encoding);
161}
162
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200163void MipsAssembler::EmitI26(int opcode, uint32_t imm26) {
164 CHECK(IsUint<26>(imm26)) << imm26;
165 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | imm26;
166 Emit(encoding);
167}
168
169void MipsAssembler::EmitFR(int opcode, int fmt, FRegister ft, FRegister fs, FRegister fd,
170 int funct) {
jeffhao7fbee072012-08-24 17:56:54 -0700171 CHECK_NE(ft, kNoFRegister);
172 CHECK_NE(fs, kNoFRegister);
173 CHECK_NE(fd, kNoFRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200174 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
175 fmt << kFmtShift |
176 static_cast<uint32_t>(ft) << kFtShift |
177 static_cast<uint32_t>(fs) << kFsShift |
178 static_cast<uint32_t>(fd) << kFdShift |
179 funct;
jeffhao7fbee072012-08-24 17:56:54 -0700180 Emit(encoding);
181}
182
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200183void MipsAssembler::EmitFI(int opcode, int fmt, FRegister ft, uint16_t imm) {
184 CHECK_NE(ft, kNoFRegister);
185 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
186 fmt << kFmtShift |
187 static_cast<uint32_t>(ft) << kFtShift |
188 imm;
jeffhao7fbee072012-08-24 17:56:54 -0700189 Emit(encoding);
190}
191
jeffhao7fbee072012-08-24 17:56:54 -0700192void MipsAssembler::Addu(Register rd, Register rs, Register rt) {
193 EmitR(0, rs, rt, rd, 0, 0x21);
194}
195
jeffhao7fbee072012-08-24 17:56:54 -0700196void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) {
197 EmitI(0x9, rs, rt, imm16);
198}
199
jeffhao7fbee072012-08-24 17:56:54 -0700200void MipsAssembler::Subu(Register rd, Register rs, Register rt) {
201 EmitR(0, rs, rt, rd, 0, 0x23);
202}
203
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200204void MipsAssembler::MultR2(Register rs, Register rt) {
205 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700206 EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x18);
207}
208
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200209void MipsAssembler::MultuR2(Register rs, Register rt) {
210 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700211 EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x19);
212}
213
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200214void MipsAssembler::DivR2(Register rs, Register rt) {
215 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700216 EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1a);
217}
218
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200219void MipsAssembler::DivuR2(Register rs, Register rt) {
220 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700221 EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1b);
222}
223
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200224void MipsAssembler::MulR2(Register rd, Register rs, Register rt) {
225 CHECK(!IsR6());
226 EmitR(0x1c, rs, rt, rd, 0, 2);
227}
228
229void MipsAssembler::DivR2(Register rd, Register rs, Register rt) {
230 CHECK(!IsR6());
231 DivR2(rs, rt);
232 Mflo(rd);
233}
234
235void MipsAssembler::ModR2(Register rd, Register rs, Register rt) {
236 CHECK(!IsR6());
237 DivR2(rs, rt);
238 Mfhi(rd);
239}
240
241void MipsAssembler::DivuR2(Register rd, Register rs, Register rt) {
242 CHECK(!IsR6());
243 DivuR2(rs, rt);
244 Mflo(rd);
245}
246
247void MipsAssembler::ModuR2(Register rd, Register rs, Register rt) {
248 CHECK(!IsR6());
249 DivuR2(rs, rt);
250 Mfhi(rd);
251}
252
253void MipsAssembler::MulR6(Register rd, Register rs, Register rt) {
254 CHECK(IsR6());
255 EmitR(0, rs, rt, rd, 2, 0x18);
256}
257
Alexey Frunze7e99e052015-11-24 19:28:01 -0800258void MipsAssembler::MuhR6(Register rd, Register rs, Register rt) {
259 CHECK(IsR6());
260 EmitR(0, rs, rt, rd, 3, 0x18);
261}
262
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200263void MipsAssembler::MuhuR6(Register rd, Register rs, Register rt) {
264 CHECK(IsR6());
265 EmitR(0, rs, rt, rd, 3, 0x19);
266}
267
268void MipsAssembler::DivR6(Register rd, Register rs, Register rt) {
269 CHECK(IsR6());
270 EmitR(0, rs, rt, rd, 2, 0x1a);
271}
272
273void MipsAssembler::ModR6(Register rd, Register rs, Register rt) {
274 CHECK(IsR6());
275 EmitR(0, rs, rt, rd, 3, 0x1a);
276}
277
278void MipsAssembler::DivuR6(Register rd, Register rs, Register rt) {
279 CHECK(IsR6());
280 EmitR(0, rs, rt, rd, 2, 0x1b);
281}
282
283void MipsAssembler::ModuR6(Register rd, Register rs, Register rt) {
284 CHECK(IsR6());
285 EmitR(0, rs, rt, rd, 3, 0x1b);
286}
287
jeffhao7fbee072012-08-24 17:56:54 -0700288void MipsAssembler::And(Register rd, Register rs, Register rt) {
289 EmitR(0, rs, rt, rd, 0, 0x24);
290}
291
292void MipsAssembler::Andi(Register rt, Register rs, uint16_t imm16) {
293 EmitI(0xc, rs, rt, imm16);
294}
295
296void MipsAssembler::Or(Register rd, Register rs, Register rt) {
297 EmitR(0, rs, rt, rd, 0, 0x25);
298}
299
300void MipsAssembler::Ori(Register rt, Register rs, uint16_t imm16) {
301 EmitI(0xd, rs, rt, imm16);
302}
303
304void MipsAssembler::Xor(Register rd, Register rs, Register rt) {
305 EmitR(0, rs, rt, rd, 0, 0x26);
306}
307
308void MipsAssembler::Xori(Register rt, Register rs, uint16_t imm16) {
309 EmitI(0xe, rs, rt, imm16);
310}
311
312void MipsAssembler::Nor(Register rd, Register rs, Register rt) {
313 EmitR(0, rs, rt, rd, 0, 0x27);
314}
315
Chris Larsene3845472015-11-18 12:27:15 -0800316void MipsAssembler::Movz(Register rd, Register rs, Register rt) {
317 CHECK(!IsR6());
318 EmitR(0, rs, rt, rd, 0, 0x0A);
319}
320
321void MipsAssembler::Movn(Register rd, Register rs, Register rt) {
322 CHECK(!IsR6());
323 EmitR(0, rs, rt, rd, 0, 0x0B);
324}
325
326void MipsAssembler::Seleqz(Register rd, Register rs, Register rt) {
327 CHECK(IsR6());
328 EmitR(0, rs, rt, rd, 0, 0x35);
329}
330
331void MipsAssembler::Selnez(Register rd, Register rs, Register rt) {
332 CHECK(IsR6());
333 EmitR(0, rs, rt, rd, 0, 0x37);
334}
335
336void MipsAssembler::ClzR6(Register rd, Register rs) {
337 CHECK(IsR6());
338 EmitR(0, rs, static_cast<Register>(0), rd, 0x01, 0x10);
339}
340
341void MipsAssembler::ClzR2(Register rd, Register rs) {
342 CHECK(!IsR6());
343 EmitR(0x1C, rs, rd, rd, 0, 0x20);
344}
345
346void MipsAssembler::CloR6(Register rd, Register rs) {
347 CHECK(IsR6());
348 EmitR(0, rs, static_cast<Register>(0), rd, 0x01, 0x11);
349}
350
351void MipsAssembler::CloR2(Register rd, Register rs) {
352 CHECK(!IsR6());
353 EmitR(0x1C, rs, rd, rd, 0, 0x21);
354}
355
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200356void MipsAssembler::Seb(Register rd, Register rt) {
357 EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x10, 0x20);
jeffhao7fbee072012-08-24 17:56:54 -0700358}
359
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200360void MipsAssembler::Seh(Register rd, Register rt) {
361 EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x18, 0x20);
jeffhao7fbee072012-08-24 17:56:54 -0700362}
363
Chris Larsen3f8bf652015-10-28 10:08:56 -0700364void MipsAssembler::Wsbh(Register rd, Register rt) {
365 EmitR(0x1f, static_cast<Register>(0), rt, rd, 2, 0x20);
366}
367
Chris Larsen70014c82015-11-18 12:26:08 -0800368void MipsAssembler::Bitswap(Register rd, Register rt) {
369 CHECK(IsR6());
370 EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x0, 0x20);
371}
372
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200373void MipsAssembler::Sll(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, 0x00);
jeffhao7fbee072012-08-24 17:56:54 -0700376}
377
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200378void MipsAssembler::Srl(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700379 CHECK(IsUint<5>(shamt)) << shamt;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200380 EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x02);
381}
382
Chris Larsen3f8bf652015-10-28 10:08:56 -0700383void MipsAssembler::Rotr(Register rd, Register rt, int shamt) {
384 CHECK(IsUint<5>(shamt)) << shamt;
385 EmitR(0, static_cast<Register>(1), rt, rd, shamt, 0x02);
386}
387
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200388void MipsAssembler::Sra(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700389 CHECK(IsUint<5>(shamt)) << shamt;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200390 EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x03);
391}
392
393void MipsAssembler::Sllv(Register rd, Register rt, Register rs) {
jeffhao7fbee072012-08-24 17:56:54 -0700394 EmitR(0, rs, rt, rd, 0, 0x04);
395}
396
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200397void MipsAssembler::Srlv(Register rd, Register rt, Register rs) {
jeffhao7fbee072012-08-24 17:56:54 -0700398 EmitR(0, rs, rt, rd, 0, 0x06);
399}
400
Chris Larsene16ce5a2015-11-18 12:30:20 -0800401void MipsAssembler::Rotrv(Register rd, Register rt, Register rs) {
402 EmitR(0, rs, rt, rd, 1, 0x06);
403}
404
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200405void MipsAssembler::Srav(Register rd, Register rt, Register rs) {
jeffhao7fbee072012-08-24 17:56:54 -0700406 EmitR(0, rs, rt, rd, 0, 0x07);
407}
408
Alexey Frunze5c7aed32015-11-25 19:41:54 -0800409void MipsAssembler::Ext(Register rd, Register rt, int pos, int size) {
410 CHECK(IsUint<5>(pos)) << pos;
411 CHECK(0 < size && size <= 32) << size;
412 CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size;
413 EmitR(0x1f, rt, rd, static_cast<Register>(size - 1), pos, 0x00);
414}
415
416void MipsAssembler::Ins(Register rd, Register rt, int pos, int size) {
417 CHECK(IsUint<5>(pos)) << pos;
418 CHECK(0 < size && size <= 32) << size;
419 CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size;
420 EmitR(0x1f, rt, rd, static_cast<Register>(pos + size - 1), pos, 0x04);
421}
422
jeffhao7fbee072012-08-24 17:56:54 -0700423void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) {
424 EmitI(0x20, rs, rt, imm16);
425}
426
427void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) {
428 EmitI(0x21, rs, rt, imm16);
429}
430
431void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) {
432 EmitI(0x23, rs, rt, imm16);
433}
434
Chris Larsen3acee732015-11-18 13:31:08 -0800435void MipsAssembler::Lwl(Register rt, Register rs, uint16_t imm16) {
436 CHECK(!IsR6());
437 EmitI(0x22, rs, rt, imm16);
438}
439
440void MipsAssembler::Lwr(Register rt, Register rs, uint16_t imm16) {
441 CHECK(!IsR6());
442 EmitI(0x26, rs, rt, imm16);
443}
444
jeffhao7fbee072012-08-24 17:56:54 -0700445void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) {
446 EmitI(0x24, rs, rt, imm16);
447}
448
449void MipsAssembler::Lhu(Register rt, Register rs, uint16_t imm16) {
450 EmitI(0x25, rs, rt, imm16);
451}
452
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700453void MipsAssembler::Lwpc(Register rs, uint32_t imm19) {
454 CHECK(IsR6());
455 CHECK(IsUint<19>(imm19)) << imm19;
456 EmitI21(0x3B, rs, (0x01 << 19) | imm19);
457}
458
jeffhao7fbee072012-08-24 17:56:54 -0700459void MipsAssembler::Lui(Register rt, uint16_t imm16) {
460 EmitI(0xf, static_cast<Register>(0), rt, imm16);
461}
462
Alexey Frunzecad3a4c2016-06-07 23:40:37 -0700463void MipsAssembler::Aui(Register rt, Register rs, uint16_t imm16) {
464 CHECK(IsR6());
465 EmitI(0xf, rs, rt, imm16);
466}
467
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200468void MipsAssembler::Sync(uint32_t stype) {
469 EmitR(0, static_cast<Register>(0), static_cast<Register>(0), static_cast<Register>(0),
470 stype & 0x1f, 0xf);
471}
472
jeffhao7fbee072012-08-24 17:56:54 -0700473void MipsAssembler::Mfhi(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, 0x10);
476}
477
478void MipsAssembler::Mflo(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200479 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700480 EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rd, 0, 0x12);
481}
482
483void MipsAssembler::Sb(Register rt, Register rs, uint16_t imm16) {
484 EmitI(0x28, rs, rt, imm16);
485}
486
487void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) {
488 EmitI(0x29, rs, rt, imm16);
489}
490
491void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) {
492 EmitI(0x2b, rs, rt, imm16);
493}
494
Chris Larsen3acee732015-11-18 13:31:08 -0800495void MipsAssembler::Swl(Register rt, Register rs, uint16_t imm16) {
496 CHECK(!IsR6());
497 EmitI(0x2a, rs, rt, imm16);
498}
499
500void MipsAssembler::Swr(Register rt, Register rs, uint16_t imm16) {
501 CHECK(!IsR6());
502 EmitI(0x2e, rs, rt, imm16);
503}
504
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700505void MipsAssembler::LlR2(Register rt, Register base, int16_t imm16) {
506 CHECK(!IsR6());
507 EmitI(0x30, base, rt, imm16);
508}
509
510void MipsAssembler::ScR2(Register rt, Register base, int16_t imm16) {
511 CHECK(!IsR6());
512 EmitI(0x38, base, rt, imm16);
513}
514
515void MipsAssembler::LlR6(Register rt, Register base, int16_t imm9) {
516 CHECK(IsR6());
517 CHECK(IsInt<9>(imm9));
518 EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36);
519}
520
521void MipsAssembler::ScR6(Register rt, Register base, int16_t imm9) {
522 CHECK(IsR6());
523 CHECK(IsInt<9>(imm9));
524 EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26);
525}
526
jeffhao7fbee072012-08-24 17:56:54 -0700527void MipsAssembler::Slt(Register rd, Register rs, Register rt) {
528 EmitR(0, rs, rt, rd, 0, 0x2a);
529}
530
531void MipsAssembler::Sltu(Register rd, Register rs, Register rt) {
532 EmitR(0, rs, rt, rd, 0, 0x2b);
533}
534
535void MipsAssembler::Slti(Register rt, Register rs, uint16_t imm16) {
536 EmitI(0xa, rs, rt, imm16);
537}
538
539void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) {
540 EmitI(0xb, rs, rt, imm16);
541}
542
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200543void MipsAssembler::B(uint16_t imm16) {
544 EmitI(0x4, static_cast<Register>(0), static_cast<Register>(0), imm16);
545}
546
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700547void MipsAssembler::Bal(uint16_t imm16) {
548 EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x11), imm16);
549}
550
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200551void MipsAssembler::Beq(Register rs, Register rt, uint16_t imm16) {
jeffhao7fbee072012-08-24 17:56:54 -0700552 EmitI(0x4, rs, rt, imm16);
553}
554
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200555void MipsAssembler::Bne(Register rs, Register rt, uint16_t imm16) {
jeffhao7fbee072012-08-24 17:56:54 -0700556 EmitI(0x5, rs, rt, imm16);
557}
558
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200559void MipsAssembler::Beqz(Register rt, uint16_t imm16) {
560 Beq(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700561}
562
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200563void MipsAssembler::Bnez(Register rt, uint16_t imm16) {
564 Bne(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700565}
566
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200567void MipsAssembler::Bltz(Register rt, uint16_t imm16) {
568 EmitI(0x1, rt, static_cast<Register>(0), imm16);
569}
570
571void MipsAssembler::Bgez(Register rt, uint16_t imm16) {
572 EmitI(0x1, rt, static_cast<Register>(0x1), imm16);
573}
574
575void MipsAssembler::Blez(Register rt, uint16_t imm16) {
576 EmitI(0x6, rt, static_cast<Register>(0), imm16);
577}
578
579void MipsAssembler::Bgtz(Register rt, uint16_t imm16) {
580 EmitI(0x7, rt, static_cast<Register>(0), imm16);
581}
582
Chris Larsenb74353a2015-11-20 09:07:09 -0800583void MipsAssembler::Bc1f(uint16_t imm16) {
584 Bc1f(0, imm16);
585}
586
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800587void MipsAssembler::Bc1f(int cc, uint16_t imm16) {
588 CHECK(!IsR6());
589 CHECK(IsUint<3>(cc)) << cc;
590 EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>(cc << 2), imm16);
591}
592
Chris Larsenb74353a2015-11-20 09:07:09 -0800593void MipsAssembler::Bc1t(uint16_t imm16) {
594 Bc1t(0, imm16);
595}
596
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800597void MipsAssembler::Bc1t(int cc, uint16_t imm16) {
598 CHECK(!IsR6());
599 CHECK(IsUint<3>(cc)) << cc;
600 EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>((cc << 2) | 1), imm16);
601}
602
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200603void MipsAssembler::J(uint32_t addr26) {
604 EmitI26(0x2, addr26);
605}
606
607void MipsAssembler::Jal(uint32_t addr26) {
608 EmitI26(0x3, addr26);
609}
610
611void MipsAssembler::Jalr(Register rd, Register rs) {
612 EmitR(0, rs, static_cast<Register>(0), rd, 0, 0x09);
jeffhao7fbee072012-08-24 17:56:54 -0700613}
614
615void MipsAssembler::Jalr(Register rs) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200616 Jalr(RA, rs);
617}
618
619void MipsAssembler::Jr(Register rs) {
620 Jalr(ZERO, rs);
621}
622
623void MipsAssembler::Nal() {
624 EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x10), 0);
625}
626
627void MipsAssembler::Auipc(Register rs, uint16_t imm16) {
628 CHECK(IsR6());
629 EmitI(0x3B, rs, static_cast<Register>(0x1E), imm16);
630}
631
632void MipsAssembler::Addiupc(Register rs, uint32_t imm19) {
633 CHECK(IsR6());
634 CHECK(IsUint<19>(imm19)) << imm19;
635 EmitI21(0x3B, rs, imm19);
636}
637
638void MipsAssembler::Bc(uint32_t imm26) {
639 CHECK(IsR6());
640 EmitI26(0x32, imm26);
641}
642
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700643void MipsAssembler::Balc(uint32_t imm26) {
644 CHECK(IsR6());
645 EmitI26(0x3A, imm26);
646}
647
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200648void MipsAssembler::Jic(Register rt, uint16_t imm16) {
649 CHECK(IsR6());
650 EmitI(0x36, static_cast<Register>(0), rt, imm16);
651}
652
653void MipsAssembler::Jialc(Register rt, uint16_t imm16) {
654 CHECK(IsR6());
655 EmitI(0x3E, static_cast<Register>(0), rt, imm16);
656}
657
658void MipsAssembler::Bltc(Register rs, Register rt, uint16_t imm16) {
659 CHECK(IsR6());
660 CHECK_NE(rs, ZERO);
661 CHECK_NE(rt, ZERO);
662 CHECK_NE(rs, rt);
663 EmitI(0x17, rs, rt, imm16);
664}
665
666void MipsAssembler::Bltzc(Register rt, uint16_t imm16) {
667 CHECK(IsR6());
668 CHECK_NE(rt, ZERO);
669 EmitI(0x17, rt, rt, imm16);
670}
671
672void MipsAssembler::Bgtzc(Register rt, uint16_t imm16) {
673 CHECK(IsR6());
674 CHECK_NE(rt, ZERO);
675 EmitI(0x17, static_cast<Register>(0), rt, imm16);
676}
677
678void MipsAssembler::Bgec(Register rs, Register rt, uint16_t imm16) {
679 CHECK(IsR6());
680 CHECK_NE(rs, ZERO);
681 CHECK_NE(rt, ZERO);
682 CHECK_NE(rs, rt);
683 EmitI(0x16, rs, rt, imm16);
684}
685
686void MipsAssembler::Bgezc(Register rt, uint16_t imm16) {
687 CHECK(IsR6());
688 CHECK_NE(rt, ZERO);
689 EmitI(0x16, rt, rt, imm16);
690}
691
692void MipsAssembler::Blezc(Register rt, uint16_t imm16) {
693 CHECK(IsR6());
694 CHECK_NE(rt, ZERO);
695 EmitI(0x16, static_cast<Register>(0), rt, imm16);
696}
697
698void MipsAssembler::Bltuc(Register rs, Register rt, uint16_t imm16) {
699 CHECK(IsR6());
700 CHECK_NE(rs, ZERO);
701 CHECK_NE(rt, ZERO);
702 CHECK_NE(rs, rt);
703 EmitI(0x7, rs, rt, imm16);
704}
705
706void MipsAssembler::Bgeuc(Register rs, Register rt, uint16_t imm16) {
707 CHECK(IsR6());
708 CHECK_NE(rs, ZERO);
709 CHECK_NE(rt, ZERO);
710 CHECK_NE(rs, rt);
711 EmitI(0x6, rs, rt, imm16);
712}
713
714void MipsAssembler::Beqc(Register rs, Register rt, uint16_t imm16) {
715 CHECK(IsR6());
716 CHECK_NE(rs, ZERO);
717 CHECK_NE(rt, ZERO);
718 CHECK_NE(rs, rt);
719 EmitI(0x8, std::min(rs, rt), std::max(rs, rt), imm16);
720}
721
722void MipsAssembler::Bnec(Register rs, Register rt, uint16_t imm16) {
723 CHECK(IsR6());
724 CHECK_NE(rs, ZERO);
725 CHECK_NE(rt, ZERO);
726 CHECK_NE(rs, rt);
727 EmitI(0x18, std::min(rs, rt), std::max(rs, rt), imm16);
728}
729
730void MipsAssembler::Beqzc(Register rs, uint32_t imm21) {
731 CHECK(IsR6());
732 CHECK_NE(rs, ZERO);
733 EmitI21(0x36, rs, imm21);
734}
735
736void MipsAssembler::Bnezc(Register rs, uint32_t imm21) {
737 CHECK(IsR6());
738 CHECK_NE(rs, ZERO);
739 EmitI21(0x3E, rs, imm21);
740}
741
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800742void MipsAssembler::Bc1eqz(FRegister ft, uint16_t imm16) {
743 CHECK(IsR6());
744 EmitFI(0x11, 0x9, ft, imm16);
745}
746
747void MipsAssembler::Bc1nez(FRegister ft, uint16_t imm16) {
748 CHECK(IsR6());
749 EmitFI(0x11, 0xD, ft, imm16);
750}
751
752void MipsAssembler::EmitBcondR2(BranchCondition cond, Register rs, Register rt, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200753 switch (cond) {
754 case kCondLTZ:
755 CHECK_EQ(rt, ZERO);
756 Bltz(rs, imm16);
757 break;
758 case kCondGEZ:
759 CHECK_EQ(rt, ZERO);
760 Bgez(rs, imm16);
761 break;
762 case kCondLEZ:
763 CHECK_EQ(rt, ZERO);
764 Blez(rs, imm16);
765 break;
766 case kCondGTZ:
767 CHECK_EQ(rt, ZERO);
768 Bgtz(rs, imm16);
769 break;
770 case kCondEQ:
771 Beq(rs, rt, imm16);
772 break;
773 case kCondNE:
774 Bne(rs, rt, imm16);
775 break;
776 case kCondEQZ:
777 CHECK_EQ(rt, ZERO);
778 Beqz(rs, imm16);
779 break;
780 case kCondNEZ:
781 CHECK_EQ(rt, ZERO);
782 Bnez(rs, imm16);
783 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800784 case kCondF:
785 CHECK_EQ(rt, ZERO);
786 Bc1f(static_cast<int>(rs), imm16);
787 break;
788 case kCondT:
789 CHECK_EQ(rt, ZERO);
790 Bc1t(static_cast<int>(rs), imm16);
791 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200792 case kCondLT:
793 case kCondGE:
794 case kCondLE:
795 case kCondGT:
796 case kCondLTU:
797 case kCondGEU:
798 case kUncond:
799 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
800 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
801 LOG(FATAL) << "Unexpected branch condition " << cond;
802 UNREACHABLE();
803 }
804}
805
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800806void MipsAssembler::EmitBcondR6(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200807 switch (cond) {
808 case kCondLT:
809 Bltc(rs, rt, imm16_21);
810 break;
811 case kCondGE:
812 Bgec(rs, rt, imm16_21);
813 break;
814 case kCondLE:
815 Bgec(rt, rs, imm16_21);
816 break;
817 case kCondGT:
818 Bltc(rt, rs, imm16_21);
819 break;
820 case kCondLTZ:
821 CHECK_EQ(rt, ZERO);
822 Bltzc(rs, imm16_21);
823 break;
824 case kCondGEZ:
825 CHECK_EQ(rt, ZERO);
826 Bgezc(rs, imm16_21);
827 break;
828 case kCondLEZ:
829 CHECK_EQ(rt, ZERO);
830 Blezc(rs, imm16_21);
831 break;
832 case kCondGTZ:
833 CHECK_EQ(rt, ZERO);
834 Bgtzc(rs, imm16_21);
835 break;
836 case kCondEQ:
837 Beqc(rs, rt, imm16_21);
838 break;
839 case kCondNE:
840 Bnec(rs, rt, imm16_21);
841 break;
842 case kCondEQZ:
843 CHECK_EQ(rt, ZERO);
844 Beqzc(rs, imm16_21);
845 break;
846 case kCondNEZ:
847 CHECK_EQ(rt, ZERO);
848 Bnezc(rs, imm16_21);
849 break;
850 case kCondLTU:
851 Bltuc(rs, rt, imm16_21);
852 break;
853 case kCondGEU:
854 Bgeuc(rs, rt, imm16_21);
855 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800856 case kCondF:
857 CHECK_EQ(rt, ZERO);
858 Bc1eqz(static_cast<FRegister>(rs), imm16_21);
859 break;
860 case kCondT:
861 CHECK_EQ(rt, ZERO);
862 Bc1nez(static_cast<FRegister>(rs), imm16_21);
863 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200864 case kUncond:
865 LOG(FATAL) << "Unexpected branch condition " << cond;
866 UNREACHABLE();
867 }
jeffhao7fbee072012-08-24 17:56:54 -0700868}
869
870void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) {
871 EmitFR(0x11, 0x10, ft, fs, fd, 0x0);
872}
873
874void MipsAssembler::SubS(FRegister fd, FRegister fs, FRegister ft) {
875 EmitFR(0x11, 0x10, ft, fs, fd, 0x1);
876}
877
878void MipsAssembler::MulS(FRegister fd, FRegister fs, FRegister ft) {
879 EmitFR(0x11, 0x10, ft, fs, fd, 0x2);
880}
881
882void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) {
883 EmitFR(0x11, 0x10, ft, fs, fd, 0x3);
884}
885
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200886void MipsAssembler::AddD(FRegister fd, FRegister fs, FRegister ft) {
887 EmitFR(0x11, 0x11, ft, fs, fd, 0x0);
jeffhao7fbee072012-08-24 17:56:54 -0700888}
889
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200890void MipsAssembler::SubD(FRegister fd, FRegister fs, FRegister ft) {
891 EmitFR(0x11, 0x11, ft, fs, fd, 0x1);
jeffhao7fbee072012-08-24 17:56:54 -0700892}
893
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200894void MipsAssembler::MulD(FRegister fd, FRegister fs, FRegister ft) {
895 EmitFR(0x11, 0x11, ft, fs, fd, 0x2);
jeffhao7fbee072012-08-24 17:56:54 -0700896}
897
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200898void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) {
899 EmitFR(0x11, 0x11, ft, fs, fd, 0x3);
jeffhao7fbee072012-08-24 17:56:54 -0700900}
901
Chris Larsenb74353a2015-11-20 09:07:09 -0800902void MipsAssembler::SqrtS(FRegister fd, FRegister fs) {
903 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x4);
904}
905
906void MipsAssembler::SqrtD(FRegister fd, FRegister fs) {
907 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x4);
908}
909
910void MipsAssembler::AbsS(FRegister fd, FRegister fs) {
911 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x5);
912}
913
914void MipsAssembler::AbsD(FRegister fd, FRegister fs) {
915 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x5);
916}
917
jeffhao7fbee072012-08-24 17:56:54 -0700918void MipsAssembler::MovS(FRegister fd, FRegister fs) {
919 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6);
920}
921
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200922void MipsAssembler::MovD(FRegister fd, FRegister fs) {
923 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x6);
924}
925
926void MipsAssembler::NegS(FRegister fd, FRegister fs) {
927 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x7);
928}
929
930void MipsAssembler::NegD(FRegister fd, FRegister fs) {
931 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7);
932}
933
Chris Larsenb74353a2015-11-20 09:07:09 -0800934void MipsAssembler::CunS(FRegister fs, FRegister ft) {
935 CunS(0, fs, ft);
936}
937
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800938void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) {
939 CHECK(!IsR6());
940 CHECK(IsUint<3>(cc)) << cc;
941 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x31);
942}
943
Chris Larsenb74353a2015-11-20 09:07:09 -0800944void MipsAssembler::CeqS(FRegister fs, FRegister ft) {
945 CeqS(0, fs, ft);
946}
947
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800948void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) {
949 CHECK(!IsR6());
950 CHECK(IsUint<3>(cc)) << cc;
951 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x32);
952}
953
Chris Larsenb74353a2015-11-20 09:07:09 -0800954void MipsAssembler::CueqS(FRegister fs, FRegister ft) {
955 CueqS(0, fs, ft);
956}
957
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800958void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) {
959 CHECK(!IsR6());
960 CHECK(IsUint<3>(cc)) << cc;
961 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x33);
962}
963
Chris Larsenb74353a2015-11-20 09:07:09 -0800964void MipsAssembler::ColtS(FRegister fs, FRegister ft) {
965 ColtS(0, fs, ft);
966}
967
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800968void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) {
969 CHECK(!IsR6());
970 CHECK(IsUint<3>(cc)) << cc;
971 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x34);
972}
973
Chris Larsenb74353a2015-11-20 09:07:09 -0800974void MipsAssembler::CultS(FRegister fs, FRegister ft) {
975 CultS(0, fs, ft);
976}
977
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800978void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) {
979 CHECK(!IsR6());
980 CHECK(IsUint<3>(cc)) << cc;
981 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x35);
982}
983
Chris Larsenb74353a2015-11-20 09:07:09 -0800984void MipsAssembler::ColeS(FRegister fs, FRegister ft) {
985 ColeS(0, fs, ft);
986}
987
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800988void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) {
989 CHECK(!IsR6());
990 CHECK(IsUint<3>(cc)) << cc;
991 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x36);
992}
993
Chris Larsenb74353a2015-11-20 09:07:09 -0800994void MipsAssembler::CuleS(FRegister fs, FRegister ft) {
995 CuleS(0, fs, ft);
996}
997
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800998void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) {
999 CHECK(!IsR6());
1000 CHECK(IsUint<3>(cc)) << cc;
1001 EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x37);
1002}
1003
Chris Larsenb74353a2015-11-20 09:07:09 -08001004void MipsAssembler::CunD(FRegister fs, FRegister ft) {
1005 CunD(0, fs, ft);
1006}
1007
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001008void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) {
1009 CHECK(!IsR6());
1010 CHECK(IsUint<3>(cc)) << cc;
1011 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x31);
1012}
1013
Chris Larsenb74353a2015-11-20 09:07:09 -08001014void MipsAssembler::CeqD(FRegister fs, FRegister ft) {
1015 CeqD(0, fs, ft);
1016}
1017
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001018void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) {
1019 CHECK(!IsR6());
1020 CHECK(IsUint<3>(cc)) << cc;
1021 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x32);
1022}
1023
Chris Larsenb74353a2015-11-20 09:07:09 -08001024void MipsAssembler::CueqD(FRegister fs, FRegister ft) {
1025 CueqD(0, fs, ft);
1026}
1027
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001028void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) {
1029 CHECK(!IsR6());
1030 CHECK(IsUint<3>(cc)) << cc;
1031 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x33);
1032}
1033
Chris Larsenb74353a2015-11-20 09:07:09 -08001034void MipsAssembler::ColtD(FRegister fs, FRegister ft) {
1035 ColtD(0, fs, ft);
1036}
1037
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001038void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) {
1039 CHECK(!IsR6());
1040 CHECK(IsUint<3>(cc)) << cc;
1041 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x34);
1042}
1043
Chris Larsenb74353a2015-11-20 09:07:09 -08001044void MipsAssembler::CultD(FRegister fs, FRegister ft) {
1045 CultD(0, fs, ft);
1046}
1047
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001048void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) {
1049 CHECK(!IsR6());
1050 CHECK(IsUint<3>(cc)) << cc;
1051 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x35);
1052}
1053
Chris Larsenb74353a2015-11-20 09:07:09 -08001054void MipsAssembler::ColeD(FRegister fs, FRegister ft) {
1055 ColeD(0, fs, ft);
1056}
1057
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001058void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) {
1059 CHECK(!IsR6());
1060 CHECK(IsUint<3>(cc)) << cc;
1061 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x36);
1062}
1063
Chris Larsenb74353a2015-11-20 09:07:09 -08001064void MipsAssembler::CuleD(FRegister fs, FRegister ft) {
1065 CuleD(0, fs, ft);
1066}
1067
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001068void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) {
1069 CHECK(!IsR6());
1070 CHECK(IsUint<3>(cc)) << cc;
1071 EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x37);
1072}
1073
1074void MipsAssembler::CmpUnS(FRegister fd, FRegister fs, FRegister ft) {
1075 CHECK(IsR6());
1076 EmitFR(0x11, 0x14, ft, fs, fd, 0x01);
1077}
1078
1079void MipsAssembler::CmpEqS(FRegister fd, FRegister fs, FRegister ft) {
1080 CHECK(IsR6());
1081 EmitFR(0x11, 0x14, ft, fs, fd, 0x02);
1082}
1083
1084void MipsAssembler::CmpUeqS(FRegister fd, FRegister fs, FRegister ft) {
1085 CHECK(IsR6());
1086 EmitFR(0x11, 0x14, ft, fs, fd, 0x03);
1087}
1088
1089void MipsAssembler::CmpLtS(FRegister fd, FRegister fs, FRegister ft) {
1090 CHECK(IsR6());
1091 EmitFR(0x11, 0x14, ft, fs, fd, 0x04);
1092}
1093
1094void MipsAssembler::CmpUltS(FRegister fd, FRegister fs, FRegister ft) {
1095 CHECK(IsR6());
1096 EmitFR(0x11, 0x14, ft, fs, fd, 0x05);
1097}
1098
1099void MipsAssembler::CmpLeS(FRegister fd, FRegister fs, FRegister ft) {
1100 CHECK(IsR6());
1101 EmitFR(0x11, 0x14, ft, fs, fd, 0x06);
1102}
1103
1104void MipsAssembler::CmpUleS(FRegister fd, FRegister fs, FRegister ft) {
1105 CHECK(IsR6());
1106 EmitFR(0x11, 0x14, ft, fs, fd, 0x07);
1107}
1108
1109void MipsAssembler::CmpOrS(FRegister fd, FRegister fs, FRegister ft) {
1110 CHECK(IsR6());
1111 EmitFR(0x11, 0x14, ft, fs, fd, 0x11);
1112}
1113
1114void MipsAssembler::CmpUneS(FRegister fd, FRegister fs, FRegister ft) {
1115 CHECK(IsR6());
1116 EmitFR(0x11, 0x14, ft, fs, fd, 0x12);
1117}
1118
1119void MipsAssembler::CmpNeS(FRegister fd, FRegister fs, FRegister ft) {
1120 CHECK(IsR6());
1121 EmitFR(0x11, 0x14, ft, fs, fd, 0x13);
1122}
1123
1124void MipsAssembler::CmpUnD(FRegister fd, FRegister fs, FRegister ft) {
1125 CHECK(IsR6());
1126 EmitFR(0x11, 0x15, ft, fs, fd, 0x01);
1127}
1128
1129void MipsAssembler::CmpEqD(FRegister fd, FRegister fs, FRegister ft) {
1130 CHECK(IsR6());
1131 EmitFR(0x11, 0x15, ft, fs, fd, 0x02);
1132}
1133
1134void MipsAssembler::CmpUeqD(FRegister fd, FRegister fs, FRegister ft) {
1135 CHECK(IsR6());
1136 EmitFR(0x11, 0x15, ft, fs, fd, 0x03);
1137}
1138
1139void MipsAssembler::CmpLtD(FRegister fd, FRegister fs, FRegister ft) {
1140 CHECK(IsR6());
1141 EmitFR(0x11, 0x15, ft, fs, fd, 0x04);
1142}
1143
1144void MipsAssembler::CmpUltD(FRegister fd, FRegister fs, FRegister ft) {
1145 CHECK(IsR6());
1146 EmitFR(0x11, 0x15, ft, fs, fd, 0x05);
1147}
1148
1149void MipsAssembler::CmpLeD(FRegister fd, FRegister fs, FRegister ft) {
1150 CHECK(IsR6());
1151 EmitFR(0x11, 0x15, ft, fs, fd, 0x06);
1152}
1153
1154void MipsAssembler::CmpUleD(FRegister fd, FRegister fs, FRegister ft) {
1155 CHECK(IsR6());
1156 EmitFR(0x11, 0x15, ft, fs, fd, 0x07);
1157}
1158
1159void MipsAssembler::CmpOrD(FRegister fd, FRegister fs, FRegister ft) {
1160 CHECK(IsR6());
1161 EmitFR(0x11, 0x15, ft, fs, fd, 0x11);
1162}
1163
1164void MipsAssembler::CmpUneD(FRegister fd, FRegister fs, FRegister ft) {
1165 CHECK(IsR6());
1166 EmitFR(0x11, 0x15, ft, fs, fd, 0x12);
1167}
1168
1169void MipsAssembler::CmpNeD(FRegister fd, FRegister fs, FRegister ft) {
1170 CHECK(IsR6());
1171 EmitFR(0x11, 0x15, ft, fs, fd, 0x13);
1172}
1173
1174void MipsAssembler::Movf(Register rd, Register rs, int cc) {
1175 CHECK(!IsR6());
1176 CHECK(IsUint<3>(cc)) << cc;
1177 EmitR(0, rs, static_cast<Register>(cc << 2), rd, 0, 0x01);
1178}
1179
1180void MipsAssembler::Movt(Register rd, Register rs, int cc) {
1181 CHECK(!IsR6());
1182 CHECK(IsUint<3>(cc)) << cc;
1183 EmitR(0, rs, static_cast<Register>((cc << 2) | 1), rd, 0, 0x01);
1184}
1185
Chris Larsenb74353a2015-11-20 09:07:09 -08001186void MipsAssembler::MovfS(FRegister fd, FRegister fs, int cc) {
1187 CHECK(!IsR6());
1188 CHECK(IsUint<3>(cc)) << cc;
1189 EmitFR(0x11, 0x10, static_cast<FRegister>(cc << 2), fs, fd, 0x11);
1190}
1191
1192void MipsAssembler::MovfD(FRegister fd, FRegister fs, int cc) {
1193 CHECK(!IsR6());
1194 CHECK(IsUint<3>(cc)) << cc;
1195 EmitFR(0x11, 0x11, static_cast<FRegister>(cc << 2), fs, fd, 0x11);
1196}
1197
1198void MipsAssembler::MovtS(FRegister fd, FRegister fs, int cc) {
1199 CHECK(!IsR6());
1200 CHECK(IsUint<3>(cc)) << cc;
1201 EmitFR(0x11, 0x10, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11);
1202}
1203
1204void MipsAssembler::MovtD(FRegister fd, FRegister fs, int cc) {
1205 CHECK(!IsR6());
1206 CHECK(IsUint<3>(cc)) << cc;
1207 EmitFR(0x11, 0x11, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11);
1208}
1209
1210void MipsAssembler::SelS(FRegister fd, FRegister fs, FRegister ft) {
1211 CHECK(IsR6());
1212 EmitFR(0x11, 0x10, ft, fs, fd, 0x10);
1213}
1214
1215void MipsAssembler::SelD(FRegister fd, FRegister fs, FRegister ft) {
1216 CHECK(IsR6());
1217 EmitFR(0x11, 0x11, ft, fs, fd, 0x10);
1218}
1219
1220void MipsAssembler::ClassS(FRegister fd, FRegister fs) {
1221 CHECK(IsR6());
1222 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x1b);
1223}
1224
1225void MipsAssembler::ClassD(FRegister fd, FRegister fs) {
1226 CHECK(IsR6());
1227 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x1b);
1228}
1229
1230void MipsAssembler::MinS(FRegister fd, FRegister fs, FRegister ft) {
1231 CHECK(IsR6());
1232 EmitFR(0x11, 0x10, ft, fs, fd, 0x1c);
1233}
1234
1235void MipsAssembler::MinD(FRegister fd, FRegister fs, FRegister ft) {
1236 CHECK(IsR6());
1237 EmitFR(0x11, 0x11, ft, fs, fd, 0x1c);
1238}
1239
1240void MipsAssembler::MaxS(FRegister fd, FRegister fs, FRegister ft) {
1241 CHECK(IsR6());
1242 EmitFR(0x11, 0x10, ft, fs, fd, 0x1e);
1243}
1244
1245void MipsAssembler::MaxD(FRegister fd, FRegister fs, FRegister ft) {
1246 CHECK(IsR6());
1247 EmitFR(0x11, 0x11, ft, fs, fd, 0x1e);
1248}
1249
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001250void MipsAssembler::TruncLS(FRegister fd, FRegister fs) {
1251 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x09);
1252}
1253
1254void MipsAssembler::TruncLD(FRegister fd, FRegister fs) {
1255 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x09);
1256}
1257
1258void MipsAssembler::TruncWS(FRegister fd, FRegister fs) {
1259 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x0D);
1260}
1261
1262void MipsAssembler::TruncWD(FRegister fd, FRegister fs) {
1263 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x0D);
1264}
1265
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001266void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) {
1267 EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x20);
1268}
1269
1270void MipsAssembler::Cvtdw(FRegister fd, FRegister fs) {
1271 EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x21);
1272}
1273
1274void MipsAssembler::Cvtsd(FRegister fd, FRegister fs) {
1275 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x20);
1276}
1277
1278void MipsAssembler::Cvtds(FRegister fd, FRegister fs) {
1279 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x21);
jeffhao7fbee072012-08-24 17:56:54 -07001280}
1281
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001282void MipsAssembler::Cvtsl(FRegister fd, FRegister fs) {
1283 EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x20);
1284}
1285
1286void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) {
1287 EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x21);
1288}
1289
Chris Larsenb74353a2015-11-20 09:07:09 -08001290void MipsAssembler::FloorWS(FRegister fd, FRegister fs) {
1291 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0xf);
1292}
1293
1294void MipsAssembler::FloorWD(FRegister fd, FRegister fs) {
1295 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0xf);
1296}
1297
jeffhao7fbee072012-08-24 17:56:54 -07001298void MipsAssembler::Mfc1(Register rt, FRegister fs) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001299 EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
jeffhao7fbee072012-08-24 17:56:54 -07001300}
1301
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001302void MipsAssembler::Mtc1(Register rt, FRegister fs) {
1303 EmitFR(0x11, 0x04, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
1304}
1305
1306void MipsAssembler::Mfhc1(Register rt, FRegister fs) {
1307 EmitFR(0x11, 0x03, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
1308}
1309
1310void MipsAssembler::Mthc1(Register rt, FRegister fs) {
1311 EmitFR(0x11, 0x07, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
jeffhao7fbee072012-08-24 17:56:54 -07001312}
1313
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001314void MipsAssembler::MoveFromFpuHigh(Register rt, FRegister fs) {
1315 if (Is32BitFPU()) {
1316 CHECK_EQ(fs % 2, 0) << fs;
1317 Mfc1(rt, static_cast<FRegister>(fs + 1));
1318 } else {
1319 Mfhc1(rt, fs);
1320 }
1321}
1322
1323void MipsAssembler::MoveToFpuHigh(Register rt, FRegister fs) {
1324 if (Is32BitFPU()) {
1325 CHECK_EQ(fs % 2, 0) << fs;
1326 Mtc1(rt, static_cast<FRegister>(fs + 1));
1327 } else {
1328 Mthc1(rt, fs);
1329 }
1330}
1331
jeffhao7fbee072012-08-24 17:56:54 -07001332void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001333 EmitI(0x31, rs, static_cast<Register>(ft), imm16);
jeffhao7fbee072012-08-24 17:56:54 -07001334}
1335
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001336void MipsAssembler::Ldc1(FRegister ft, Register rs, uint16_t imm16) {
1337 EmitI(0x35, rs, static_cast<Register>(ft), imm16);
jeffhao7fbee072012-08-24 17:56:54 -07001338}
1339
1340void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001341 EmitI(0x39, rs, static_cast<Register>(ft), imm16);
jeffhao7fbee072012-08-24 17:56:54 -07001342}
1343
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001344void MipsAssembler::Sdc1(FRegister ft, Register rs, uint16_t imm16) {
1345 EmitI(0x3d, rs, static_cast<Register>(ft), imm16);
jeffhao7fbee072012-08-24 17:56:54 -07001346}
1347
1348void MipsAssembler::Break() {
1349 EmitR(0, static_cast<Register>(0), static_cast<Register>(0),
1350 static_cast<Register>(0), 0, 0xD);
1351}
1352
jeffhao07030602012-09-26 14:33:14 -07001353void MipsAssembler::Nop() {
1354 EmitR(0x0, static_cast<Register>(0), static_cast<Register>(0), static_cast<Register>(0), 0, 0x0);
1355}
1356
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001357void MipsAssembler::Move(Register rd, Register rs) {
1358 Or(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001359}
1360
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001361void MipsAssembler::Clear(Register rd) {
1362 Move(rd, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001363}
1364
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001365void MipsAssembler::Not(Register rd, Register rs) {
1366 Nor(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001367}
1368
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001369void MipsAssembler::Push(Register rs) {
1370 IncreaseFrameSize(kMipsWordSize);
1371 Sw(rs, SP, 0);
jeffhao7fbee072012-08-24 17:56:54 -07001372}
1373
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001374void MipsAssembler::Pop(Register rd) {
1375 Lw(rd, SP, 0);
1376 DecreaseFrameSize(kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07001377}
1378
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001379void MipsAssembler::PopAndReturn(Register rd, Register rt) {
1380 Lw(rd, SP, 0);
1381 Jr(rt);
1382 DecreaseFrameSize(kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07001383}
1384
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001385void MipsAssembler::LoadConst32(Register rd, int32_t value) {
1386 if (IsUint<16>(value)) {
1387 // Use OR with (unsigned) immediate to encode 16b unsigned int.
1388 Ori(rd, ZERO, value);
1389 } else if (IsInt<16>(value)) {
1390 // Use ADD with (signed) immediate to encode 16b signed int.
1391 Addiu(rd, ZERO, value);
jeffhao7fbee072012-08-24 17:56:54 -07001392 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001393 Lui(rd, High16Bits(value));
1394 if (value & 0xFFFF)
1395 Ori(rd, rd, Low16Bits(value));
1396 }
1397}
1398
1399void MipsAssembler::LoadConst64(Register reg_hi, Register reg_lo, int64_t value) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001400 uint32_t low = Low32Bits(value);
1401 uint32_t high = High32Bits(value);
1402 LoadConst32(reg_lo, low);
1403 if (high != low) {
1404 LoadConst32(reg_hi, high);
1405 } else {
1406 Move(reg_hi, reg_lo);
1407 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001408}
1409
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001410void MipsAssembler::LoadSConst32(FRegister r, int32_t value, Register temp) {
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 Mtc1(temp, r);
1417}
1418
1419void MipsAssembler::LoadDConst64(FRegister rd, int64_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001420 uint32_t low = Low32Bits(value);
1421 uint32_t high = High32Bits(value);
1422 if (low == 0) {
1423 Mtc1(ZERO, rd);
1424 } else {
1425 LoadConst32(temp, low);
1426 Mtc1(temp, rd);
1427 }
1428 if (high == 0) {
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001429 MoveToFpuHigh(ZERO, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001430 } else {
1431 LoadConst32(temp, high);
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001432 MoveToFpuHigh(temp, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001433 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001434}
1435
1436void MipsAssembler::Addiu32(Register rt, Register rs, int32_t value, Register temp) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001437 CHECK_NE(rs, temp); // Must not overwrite the register `rs` while loading `value`.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001438 if (IsInt<16>(value)) {
1439 Addiu(rt, rs, value);
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001440 } else if (IsR6()) {
1441 int16_t high = High16Bits(value);
1442 int16_t low = Low16Bits(value);
1443 high += (low < 0) ? 1 : 0; // Account for sign extension in addiu.
1444 if (low != 0) {
1445 Aui(temp, rs, high);
1446 Addiu(rt, temp, low);
1447 } else {
1448 Aui(rt, rs, high);
1449 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001450 } else {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001451 // Do not load the whole 32-bit `value` if it can be represented as
1452 // a sum of two 16-bit signed values. This can save an instruction.
1453 constexpr int32_t kMinValueForSimpleAdjustment = std::numeric_limits<int16_t>::min() * 2;
1454 constexpr int32_t kMaxValueForSimpleAdjustment = std::numeric_limits<int16_t>::max() * 2;
1455 if (0 <= value && value <= kMaxValueForSimpleAdjustment) {
1456 Addiu(temp, rs, kMaxValueForSimpleAdjustment / 2);
1457 Addiu(rt, temp, value - kMaxValueForSimpleAdjustment / 2);
1458 } else if (kMinValueForSimpleAdjustment <= value && value < 0) {
1459 Addiu(temp, rs, kMinValueForSimpleAdjustment / 2);
1460 Addiu(rt, temp, value - kMinValueForSimpleAdjustment / 2);
1461 } else {
1462 // Now that all shorter options have been exhausted, load the full 32-bit value.
1463 LoadConst32(temp, value);
1464 Addu(rt, rs, temp);
1465 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001466 }
1467}
1468
1469void MipsAssembler::Branch::InitShortOrLong(MipsAssembler::Branch::OffsetBits offset_size,
1470 MipsAssembler::Branch::Type short_type,
1471 MipsAssembler::Branch::Type long_type) {
1472 type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type;
1473}
1474
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001475void MipsAssembler::Branch::InitializeType(bool is_call, bool is_literal, bool is_r6) {
1476 CHECK_EQ(is_call && is_literal, false);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001477 OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_);
1478 if (is_r6) {
1479 // R6
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001480 if (is_literal) {
1481 CHECK(!IsResolved());
1482 type_ = kR6Literal;
1483 } else if (is_call) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001484 InitShortOrLong(offset_size, kR6Call, kR6LongCall);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001485 } else {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001486 switch (condition_) {
1487 case kUncond:
1488 InitShortOrLong(offset_size, kR6UncondBranch, kR6LongUncondBranch);
1489 break;
1490 case kCondEQZ:
1491 case kCondNEZ:
1492 // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions.
1493 type_ = (offset_size <= kOffset23) ? kR6CondBranch : kR6LongCondBranch;
1494 break;
1495 default:
1496 InitShortOrLong(offset_size, kR6CondBranch, kR6LongCondBranch);
1497 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001498 }
1499 }
1500 } else {
1501 // R2
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001502 if (is_literal) {
1503 CHECK(!IsResolved());
1504 type_ = kLiteral;
1505 } else if (is_call) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001506 InitShortOrLong(offset_size, kCall, kLongCall);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001507 } else {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001508 switch (condition_) {
1509 case kUncond:
1510 InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch);
1511 break;
1512 default:
1513 InitShortOrLong(offset_size, kCondBranch, kLongCondBranch);
1514 break;
1515 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001516 }
1517 }
1518 old_type_ = type_;
1519}
1520
1521bool MipsAssembler::Branch::IsNop(BranchCondition condition, Register lhs, Register rhs) {
1522 switch (condition) {
1523 case kCondLT:
1524 case kCondGT:
1525 case kCondNE:
1526 case kCondLTU:
1527 return lhs == rhs;
1528 default:
1529 return false;
1530 }
1531}
1532
1533bool MipsAssembler::Branch::IsUncond(BranchCondition condition, Register lhs, Register rhs) {
1534 switch (condition) {
1535 case kUncond:
1536 return true;
1537 case kCondGE:
1538 case kCondLE:
1539 case kCondEQ:
1540 case kCondGEU:
1541 return lhs == rhs;
1542 default:
1543 return false;
1544 }
1545}
1546
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001547MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target, bool is_call)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001548 : old_location_(location),
1549 location_(location),
1550 target_(target),
1551 lhs_reg_(0),
1552 rhs_reg_(0),
1553 condition_(kUncond) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001554 InitializeType(is_call, /* is_literal */ false, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001555}
1556
1557MipsAssembler::Branch::Branch(bool is_r6,
1558 uint32_t location,
1559 uint32_t target,
1560 MipsAssembler::BranchCondition condition,
1561 Register lhs_reg,
1562 Register rhs_reg)
1563 : old_location_(location),
1564 location_(location),
1565 target_(target),
1566 lhs_reg_(lhs_reg),
1567 rhs_reg_(rhs_reg),
1568 condition_(condition) {
1569 CHECK_NE(condition, kUncond);
1570 switch (condition) {
1571 case kCondLT:
1572 case kCondGE:
1573 case kCondLE:
1574 case kCondGT:
1575 case kCondLTU:
1576 case kCondGEU:
1577 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
1578 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
1579 // We leave this up to the caller.
1580 CHECK(is_r6);
1581 FALLTHROUGH_INTENDED;
1582 case kCondEQ:
1583 case kCondNE:
1584 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1585 // To compare with 0, use dedicated kCond*Z conditions.
1586 CHECK_NE(lhs_reg, ZERO);
1587 CHECK_NE(rhs_reg, ZERO);
1588 break;
1589 case kCondLTZ:
1590 case kCondGEZ:
1591 case kCondLEZ:
1592 case kCondGTZ:
1593 case kCondEQZ:
1594 case kCondNEZ:
1595 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1596 CHECK_NE(lhs_reg, ZERO);
1597 CHECK_EQ(rhs_reg, ZERO);
1598 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001599 case kCondF:
1600 case kCondT:
1601 CHECK_EQ(rhs_reg, ZERO);
1602 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001603 case kUncond:
1604 UNREACHABLE();
1605 }
1606 CHECK(!IsNop(condition, lhs_reg, rhs_reg));
1607 if (IsUncond(condition, lhs_reg, rhs_reg)) {
1608 // Branch condition is always true, make the branch unconditional.
1609 condition_ = kUncond;
1610 }
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001611 InitializeType(/* is_call */ false, /* is_literal */ false, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001612}
1613
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001614MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, Register dest_reg, Register base_reg)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001615 : old_location_(location),
1616 location_(location),
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001617 target_(kUnresolved),
1618 lhs_reg_(dest_reg),
1619 rhs_reg_(base_reg),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001620 condition_(kUncond) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001621 CHECK_NE(dest_reg, ZERO);
1622 if (is_r6) {
1623 CHECK_EQ(base_reg, ZERO);
1624 } else {
1625 CHECK_NE(base_reg, ZERO);
1626 }
1627 InitializeType(/* is_call */ false, /* is_literal */ true, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001628}
1629
1630MipsAssembler::BranchCondition MipsAssembler::Branch::OppositeCondition(
1631 MipsAssembler::BranchCondition cond) {
1632 switch (cond) {
1633 case kCondLT:
1634 return kCondGE;
1635 case kCondGE:
1636 return kCondLT;
1637 case kCondLE:
1638 return kCondGT;
1639 case kCondGT:
1640 return kCondLE;
1641 case kCondLTZ:
1642 return kCondGEZ;
1643 case kCondGEZ:
1644 return kCondLTZ;
1645 case kCondLEZ:
1646 return kCondGTZ;
1647 case kCondGTZ:
1648 return kCondLEZ;
1649 case kCondEQ:
1650 return kCondNE;
1651 case kCondNE:
1652 return kCondEQ;
1653 case kCondEQZ:
1654 return kCondNEZ;
1655 case kCondNEZ:
1656 return kCondEQZ;
1657 case kCondLTU:
1658 return kCondGEU;
1659 case kCondGEU:
1660 return kCondLTU;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001661 case kCondF:
1662 return kCondT;
1663 case kCondT:
1664 return kCondF;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001665 case kUncond:
1666 LOG(FATAL) << "Unexpected branch condition " << cond;
1667 }
1668 UNREACHABLE();
1669}
1670
1671MipsAssembler::Branch::Type MipsAssembler::Branch::GetType() const {
1672 return type_;
1673}
1674
1675MipsAssembler::BranchCondition MipsAssembler::Branch::GetCondition() const {
1676 return condition_;
1677}
1678
1679Register MipsAssembler::Branch::GetLeftRegister() const {
1680 return static_cast<Register>(lhs_reg_);
1681}
1682
1683Register MipsAssembler::Branch::GetRightRegister() const {
1684 return static_cast<Register>(rhs_reg_);
1685}
1686
1687uint32_t MipsAssembler::Branch::GetTarget() const {
1688 return target_;
1689}
1690
1691uint32_t MipsAssembler::Branch::GetLocation() const {
1692 return location_;
1693}
1694
1695uint32_t MipsAssembler::Branch::GetOldLocation() const {
1696 return old_location_;
1697}
1698
1699uint32_t MipsAssembler::Branch::GetLength() const {
1700 return branch_info_[type_].length;
1701}
1702
1703uint32_t MipsAssembler::Branch::GetOldLength() const {
1704 return branch_info_[old_type_].length;
1705}
1706
1707uint32_t MipsAssembler::Branch::GetSize() const {
1708 return GetLength() * sizeof(uint32_t);
1709}
1710
1711uint32_t MipsAssembler::Branch::GetOldSize() const {
1712 return GetOldLength() * sizeof(uint32_t);
1713}
1714
1715uint32_t MipsAssembler::Branch::GetEndLocation() const {
1716 return GetLocation() + GetSize();
1717}
1718
1719uint32_t MipsAssembler::Branch::GetOldEndLocation() const {
1720 return GetOldLocation() + GetOldSize();
1721}
1722
1723bool MipsAssembler::Branch::IsLong() const {
1724 switch (type_) {
1725 // R2 short branches.
1726 case kUncondBranch:
1727 case kCondBranch:
1728 case kCall:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001729 // R2 near literal.
1730 case kLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001731 // R6 short branches.
1732 case kR6UncondBranch:
1733 case kR6CondBranch:
1734 case kR6Call:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001735 // R6 near literal.
1736 case kR6Literal:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001737 return false;
1738 // R2 long branches.
1739 case kLongUncondBranch:
1740 case kLongCondBranch:
1741 case kLongCall:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001742 // R2 far literal.
1743 case kFarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001744 // R6 long branches.
1745 case kR6LongUncondBranch:
1746 case kR6LongCondBranch:
1747 case kR6LongCall:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001748 // R6 far literal.
1749 case kR6FarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001750 return true;
1751 }
1752 UNREACHABLE();
1753}
1754
1755bool MipsAssembler::Branch::IsResolved() const {
1756 return target_ != kUnresolved;
1757}
1758
1759MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSize() const {
1760 OffsetBits offset_size =
1761 (type_ == kR6CondBranch && (condition_ == kCondEQZ || condition_ == kCondNEZ))
1762 ? kOffset23
1763 : branch_info_[type_].offset_size;
1764 return offset_size;
1765}
1766
1767MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSizeNeeded(uint32_t location,
1768 uint32_t target) {
1769 // For unresolved targets assume the shortest encoding
1770 // (later it will be made longer if needed).
1771 if (target == kUnresolved)
1772 return kOffset16;
1773 int64_t distance = static_cast<int64_t>(target) - location;
1774 // To simplify calculations in composite branches consisting of multiple instructions
1775 // bump up the distance by a value larger than the max byte size of a composite branch.
1776 distance += (distance >= 0) ? kMaxBranchSize : -kMaxBranchSize;
1777 if (IsInt<kOffset16>(distance))
1778 return kOffset16;
1779 else if (IsInt<kOffset18>(distance))
1780 return kOffset18;
1781 else if (IsInt<kOffset21>(distance))
1782 return kOffset21;
1783 else if (IsInt<kOffset23>(distance))
1784 return kOffset23;
1785 else if (IsInt<kOffset28>(distance))
1786 return kOffset28;
1787 return kOffset32;
1788}
1789
1790void MipsAssembler::Branch::Resolve(uint32_t target) {
1791 target_ = target;
1792}
1793
1794void MipsAssembler::Branch::Relocate(uint32_t expand_location, uint32_t delta) {
1795 if (location_ > expand_location) {
1796 location_ += delta;
1797 }
1798 if (!IsResolved()) {
1799 return; // Don't know the target yet.
1800 }
1801 if (target_ > expand_location) {
1802 target_ += delta;
1803 }
1804}
1805
1806void MipsAssembler::Branch::PromoteToLong() {
1807 switch (type_) {
1808 // R2 short branches.
1809 case kUncondBranch:
1810 type_ = kLongUncondBranch;
1811 break;
1812 case kCondBranch:
1813 type_ = kLongCondBranch;
1814 break;
1815 case kCall:
1816 type_ = kLongCall;
1817 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001818 // R2 near literal.
1819 case kLiteral:
1820 type_ = kFarLiteral;
1821 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001822 // R6 short branches.
1823 case kR6UncondBranch:
1824 type_ = kR6LongUncondBranch;
1825 break;
1826 case kR6CondBranch:
1827 type_ = kR6LongCondBranch;
1828 break;
1829 case kR6Call:
1830 type_ = kR6LongCall;
1831 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001832 // R6 near literal.
1833 case kR6Literal:
1834 type_ = kR6FarLiteral;
1835 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001836 default:
1837 // Note: 'type_' is already long.
1838 break;
1839 }
1840 CHECK(IsLong());
1841}
1842
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001843uint32_t MipsAssembler::GetBranchLocationOrPcRelBase(const MipsAssembler::Branch* branch) const {
1844 switch (branch->GetType()) {
1845 case Branch::kLiteral:
1846 case Branch::kFarLiteral:
1847 return GetLabelLocation(&pc_rel_base_label_);
1848 default:
1849 return branch->GetLocation();
1850 }
1851}
1852
1853uint32_t MipsAssembler::Branch::PromoteIfNeeded(uint32_t location, uint32_t max_short_distance) {
1854 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 literals or
1855 // `this->GetLocation()` for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001856 // If the branch is still unresolved or already long, nothing to do.
1857 if (IsLong() || !IsResolved()) {
1858 return 0;
1859 }
1860 // Promote the short branch to long if the offset size is too small
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001861 // to hold the distance between location and target_.
1862 if (GetOffsetSizeNeeded(location, target_) > GetOffsetSize()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001863 PromoteToLong();
1864 uint32_t old_size = GetOldSize();
1865 uint32_t new_size = GetSize();
1866 CHECK_GT(new_size, old_size);
1867 return new_size - old_size;
1868 }
1869 // The following logic is for debugging/testing purposes.
1870 // Promote some short branches to long when it's not really required.
1871 if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max())) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001872 int64_t distance = static_cast<int64_t>(target_) - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001873 distance = (distance >= 0) ? distance : -distance;
1874 if (distance >= max_short_distance) {
1875 PromoteToLong();
1876 uint32_t old_size = GetOldSize();
1877 uint32_t new_size = GetSize();
1878 CHECK_GT(new_size, old_size);
1879 return new_size - old_size;
1880 }
1881 }
1882 return 0;
1883}
1884
1885uint32_t MipsAssembler::Branch::GetOffsetLocation() const {
1886 return location_ + branch_info_[type_].instr_offset * sizeof(uint32_t);
1887}
1888
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001889uint32_t MipsAssembler::GetBranchOrPcRelBaseForEncoding(const MipsAssembler::Branch* branch) const {
1890 switch (branch->GetType()) {
1891 case Branch::kLiteral:
1892 case Branch::kFarLiteral:
1893 return GetLabelLocation(&pc_rel_base_label_);
1894 default:
1895 return branch->GetOffsetLocation() +
1896 Branch::branch_info_[branch->GetType()].pc_org * sizeof(uint32_t);
1897 }
1898}
1899
1900uint32_t MipsAssembler::Branch::GetOffset(uint32_t location) const {
1901 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 literals or
1902 // `this->GetOffsetLocation() + branch_info_[this->GetType()].pc_org * sizeof(uint32_t)`
1903 // for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001904 CHECK(IsResolved());
1905 uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize());
1906 // Calculate the byte distance between instructions and also account for
1907 // different PC-relative origins.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001908 uint32_t offset = target_ - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001909 // Prepare the offset for encoding into the instruction(s).
1910 offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift;
1911 return offset;
1912}
1913
1914MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) {
1915 CHECK_LT(branch_id, branches_.size());
1916 return &branches_[branch_id];
1917}
1918
1919const MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) const {
1920 CHECK_LT(branch_id, branches_.size());
1921 return &branches_[branch_id];
1922}
1923
1924void MipsAssembler::Bind(MipsLabel* label) {
1925 CHECK(!label->IsBound());
1926 uint32_t bound_pc = buffer_.Size();
1927
1928 // Walk the list of branches referring to and preceding this label.
1929 // Store the previously unknown target addresses in them.
1930 while (label->IsLinked()) {
1931 uint32_t branch_id = label->Position();
1932 Branch* branch = GetBranch(branch_id);
1933 branch->Resolve(bound_pc);
1934
1935 uint32_t branch_location = branch->GetLocation();
1936 // Extract the location of the previous branch in the list (walking the list backwards;
1937 // the previous branch ID was stored in the space reserved for this branch).
1938 uint32_t prev = buffer_.Load<uint32_t>(branch_location);
1939
1940 // On to the previous branch in the list...
1941 label->position_ = prev;
1942 }
1943
1944 // Now make the label object contain its own location (relative to the end of the preceding
1945 // branch, if any; it will be used by the branches referring to and following this label).
1946 label->prev_branch_id_plus_one_ = branches_.size();
1947 if (label->prev_branch_id_plus_one_) {
1948 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
1949 const Branch* branch = GetBranch(branch_id);
1950 bound_pc -= branch->GetEndLocation();
1951 }
1952 label->BindTo(bound_pc);
1953}
1954
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001955uint32_t MipsAssembler::GetLabelLocation(const MipsLabel* label) const {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001956 CHECK(label->IsBound());
1957 uint32_t target = label->Position();
1958 if (label->prev_branch_id_plus_one_) {
1959 // Get label location based on the branch preceding it.
1960 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
1961 const Branch* branch = GetBranch(branch_id);
1962 target += branch->GetEndLocation();
1963 }
1964 return target;
1965}
1966
1967uint32_t MipsAssembler::GetAdjustedPosition(uint32_t old_position) {
1968 // We can reconstruct the adjustment by going through all the branches from the beginning
1969 // up to the old_position. Since we expect AdjustedPosition() to be called in a loop
1970 // with increasing old_position, we can use the data from last AdjustedPosition() to
1971 // continue where we left off and the whole loop should be O(m+n) where m is the number
1972 // of positions to adjust and n is the number of branches.
1973 if (old_position < last_old_position_) {
1974 last_position_adjustment_ = 0;
1975 last_old_position_ = 0;
1976 last_branch_id_ = 0;
1977 }
1978 while (last_branch_id_ != branches_.size()) {
1979 const Branch* branch = GetBranch(last_branch_id_);
1980 if (branch->GetLocation() >= old_position + last_position_adjustment_) {
1981 break;
1982 }
1983 last_position_adjustment_ += branch->GetSize() - branch->GetOldSize();
1984 ++last_branch_id_;
1985 }
1986 last_old_position_ = old_position;
1987 return old_position + last_position_adjustment_;
1988}
1989
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001990void MipsAssembler::BindPcRelBaseLabel() {
1991 Bind(&pc_rel_base_label_);
1992}
1993
Alexey Frunze06a46c42016-07-19 15:00:40 -07001994uint32_t MipsAssembler::GetPcRelBaseLabelLocation() const {
1995 return GetLabelLocation(&pc_rel_base_label_);
1996}
1997
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001998void MipsAssembler::FinalizeLabeledBranch(MipsLabel* label) {
1999 uint32_t length = branches_.back().GetLength();
2000 if (!label->IsBound()) {
2001 // Branch forward (to a following label), distance is unknown.
2002 // The first branch forward will contain 0, serving as the terminator of
2003 // the list of forward-reaching branches.
2004 Emit(label->position_);
2005 length--;
2006 // Now make the label object point to this branch
2007 // (this forms a linked list of branches preceding this label).
2008 uint32_t branch_id = branches_.size() - 1;
2009 label->LinkTo(branch_id);
2010 }
2011 // Reserve space for the branch.
2012 while (length--) {
2013 Nop();
2014 }
2015}
2016
2017void MipsAssembler::Buncond(MipsLabel* label) {
2018 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002019 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ false);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002020 FinalizeLabeledBranch(label);
2021}
2022
2023void MipsAssembler::Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs) {
2024 // If lhs = rhs, this can be a NOP.
2025 if (Branch::IsNop(condition, lhs, rhs)) {
2026 return;
2027 }
2028 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
2029 branches_.emplace_back(IsR6(), buffer_.Size(), target, condition, lhs, rhs);
2030 FinalizeLabeledBranch(label);
2031}
2032
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002033void MipsAssembler::Call(MipsLabel* label) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002034 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002035 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ true);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002036 FinalizeLabeledBranch(label);
2037}
2038
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002039Literal* MipsAssembler::NewLiteral(size_t size, const uint8_t* data) {
2040 DCHECK(size == 4u || size == 8u) << size;
2041 literals_.emplace_back(size, data);
2042 return &literals_.back();
2043}
2044
2045void MipsAssembler::LoadLiteral(Register dest_reg, Register base_reg, Literal* literal) {
2046 // Literal loads are treated as pseudo branches since they require very similar handling.
2047 DCHECK_EQ(literal->GetSize(), 4u);
2048 MipsLabel* label = literal->GetLabel();
2049 DCHECK(!label->IsBound());
2050 branches_.emplace_back(IsR6(),
2051 buffer_.Size(),
2052 dest_reg,
2053 base_reg);
2054 FinalizeLabeledBranch(label);
2055}
2056
2057void MipsAssembler::EmitLiterals() {
2058 if (!literals_.empty()) {
2059 // We don't support byte and half-word literals.
2060 // TODO: proper alignment for 64-bit literals when they're implemented.
2061 for (Literal& literal : literals_) {
2062 MipsLabel* label = literal.GetLabel();
2063 Bind(label);
2064 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
2065 DCHECK(literal.GetSize() == 4u || literal.GetSize() == 8u);
2066 for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
2067 buffer_.Emit<uint8_t>(literal.GetData()[i]);
2068 }
2069 }
2070 }
2071}
2072
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002073void MipsAssembler::PromoteBranches() {
2074 // Promote short branches to long as necessary.
2075 bool changed;
2076 do {
2077 changed = false;
2078 for (auto& branch : branches_) {
2079 CHECK(branch.IsResolved());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002080 uint32_t base = GetBranchLocationOrPcRelBase(&branch);
2081 uint32_t delta = branch.PromoteIfNeeded(base);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002082 // If this branch has been promoted and needs to expand in size,
2083 // relocate all branches by the expansion size.
2084 if (delta) {
2085 changed = true;
2086 uint32_t expand_location = branch.GetLocation();
2087 for (auto& branch2 : branches_) {
2088 branch2.Relocate(expand_location, delta);
2089 }
2090 }
2091 }
2092 } while (changed);
2093
2094 // Account for branch expansion by resizing the code buffer
2095 // and moving the code in it to its final location.
2096 size_t branch_count = branches_.size();
2097 if (branch_count > 0) {
2098 // Resize.
2099 Branch& last_branch = branches_[branch_count - 1];
2100 uint32_t size_delta = last_branch.GetEndLocation() - last_branch.GetOldEndLocation();
2101 uint32_t old_size = buffer_.Size();
2102 buffer_.Resize(old_size + size_delta);
2103 // Move the code residing between branch placeholders.
2104 uint32_t end = old_size;
2105 for (size_t i = branch_count; i > 0; ) {
2106 Branch& branch = branches_[--i];
2107 uint32_t size = end - branch.GetOldEndLocation();
2108 buffer_.Move(branch.GetEndLocation(), branch.GetOldEndLocation(), size);
2109 end = branch.GetOldLocation();
2110 }
2111 }
2112}
2113
2114// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
2115const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = {
2116 // R2 short branches.
2117 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kUncondBranch
2118 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002119 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCall
2120 // R2 near literal.
2121 { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002122 // R2 long branches.
2123 { 9, 3, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongUncondBranch
2124 { 10, 4, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCondBranch
2125 { 6, 1, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCall
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002126 // R2 far literal.
2127 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002128 // R6 short branches.
2129 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6UncondBranch
2130 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kR6CondBranch
2131 // Exception: kOffset23 for beqzc/bnezc.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002132 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6Call
2133 // R6 near literal.
2134 { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Literal
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002135 // R6 long branches.
2136 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongUncondBranch
2137 { 3, 1, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002138 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCall
2139 // R6 far literal.
2140 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002141};
2142
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002143// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002144void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
2145 CHECK_EQ(overwriting_, true);
2146 overwrite_location_ = branch->GetLocation();
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002147 uint32_t offset = branch->GetOffset(GetBranchOrPcRelBaseForEncoding(branch));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002148 BranchCondition condition = branch->GetCondition();
2149 Register lhs = branch->GetLeftRegister();
2150 Register rhs = branch->GetRightRegister();
2151 switch (branch->GetType()) {
2152 // R2 short branches.
2153 case Branch::kUncondBranch:
2154 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2155 B(offset);
2156 Nop(); // TODO: improve by filling the delay slot.
2157 break;
2158 case Branch::kCondBranch:
2159 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002160 EmitBcondR2(condition, lhs, rhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002161 Nop(); // TODO: improve by filling the delay slot.
2162 break;
2163 case Branch::kCall:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002164 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002165 Bal(offset);
2166 Nop(); // TODO: improve by filling the delay slot.
2167 break;
2168
2169 // R2 near literal.
2170 case Branch::kLiteral:
2171 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2172 Lw(lhs, rhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002173 break;
2174
2175 // R2 long branches.
2176 case Branch::kLongUncondBranch:
2177 // To get the value of the PC register we need to use the NAL instruction.
2178 // NAL clobbers the RA register. However, RA must be preserved if the
2179 // method is compiled without the entry/exit sequences that would take care
2180 // of preserving RA (typically, leaf methods don't preserve RA explicitly).
2181 // So, we need to preserve RA in some temporary storage ourselves. The AT
2182 // register can't be used for this because we need it to load a constant
2183 // which will be added to the value that NAL stores in RA. And we can't
2184 // use T9 for this in the context of the JNI compiler, which uses it
2185 // as a scratch register (see InterproceduralScratchRegister()).
2186 // If we were to add a 32-bit constant to RA using two ADDIU instructions,
2187 // we'd also need to use the ROTR instruction, which requires no less than
2188 // MIPSR2.
2189 // Perhaps, we could use T8 or one of R2's multiplier/divider registers
2190 // (LO or HI) or even a floating-point register, but that doesn't seem
2191 // like a nice solution. We may want this to work on both R6 and pre-R6.
2192 // For now simply use the stack for RA. This should be OK since for the
2193 // vast majority of code a short PC-relative branch is sufficient.
2194 // TODO: can this be improved?
2195 Push(RA);
2196 Nal();
2197 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2198 Lui(AT, High16Bits(offset));
2199 Ori(AT, AT, Low16Bits(offset));
2200 Addu(AT, AT, RA);
2201 Lw(RA, SP, 0);
2202 Jr(AT);
2203 DecreaseFrameSize(kMipsWordSize);
2204 break;
2205 case Branch::kLongCondBranch:
2206 // The comment on case 'Branch::kLongUncondBranch' applies here as well.
2207 // Note: the opposite condition branch encodes 8 as the distance, which is equal to the
2208 // number of instructions skipped:
2209 // (PUSH(IncreaseFrameSize(ADDIU) + SW) + NAL + LUI + ORI + ADDU + LW + JR).
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002210 EmitBcondR2(Branch::OppositeCondition(condition), lhs, rhs, 8);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002211 Push(RA);
2212 Nal();
2213 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2214 Lui(AT, High16Bits(offset));
2215 Ori(AT, AT, Low16Bits(offset));
2216 Addu(AT, AT, RA);
2217 Lw(RA, SP, 0);
2218 Jr(AT);
2219 DecreaseFrameSize(kMipsWordSize);
2220 break;
2221 case Branch::kLongCall:
2222 Nal();
2223 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2224 Lui(AT, High16Bits(offset));
2225 Ori(AT, AT, Low16Bits(offset));
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002226 Addu(AT, AT, RA);
2227 Jalr(AT);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002228 Nop();
2229 break;
2230
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002231 // R2 far literal.
2232 case Branch::kFarLiteral:
2233 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2234 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2235 Lui(AT, High16Bits(offset));
2236 Addu(AT, AT, rhs);
2237 Lw(lhs, AT, Low16Bits(offset));
2238 break;
2239
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002240 // R6 short branches.
2241 case Branch::kR6UncondBranch:
2242 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2243 Bc(offset);
2244 break;
2245 case Branch::kR6CondBranch:
2246 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002247 EmitBcondR6(condition, lhs, rhs, offset);
2248 Nop(); // TODO: improve by filling the forbidden/delay slot.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002249 break;
2250 case Branch::kR6Call:
2251 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002252 Balc(offset);
2253 break;
2254
2255 // R6 near literal.
2256 case Branch::kR6Literal:
2257 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2258 Lwpc(lhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002259 break;
2260
2261 // R6 long branches.
2262 case Branch::kR6LongUncondBranch:
2263 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2264 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2265 Auipc(AT, High16Bits(offset));
2266 Jic(AT, Low16Bits(offset));
2267 break;
2268 case Branch::kR6LongCondBranch:
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002269 EmitBcondR6(Branch::OppositeCondition(condition), lhs, rhs, 2);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002270 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2271 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2272 Auipc(AT, High16Bits(offset));
2273 Jic(AT, Low16Bits(offset));
2274 break;
2275 case Branch::kR6LongCall:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002276 offset += (offset & 0x8000) << 1; // Account for sign extension in jialc.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002277 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002278 Auipc(AT, High16Bits(offset));
2279 Jialc(AT, Low16Bits(offset));
2280 break;
2281
2282 // R6 far literal.
2283 case Branch::kR6FarLiteral:
2284 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2285 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2286 Auipc(AT, High16Bits(offset));
2287 Lw(lhs, AT, Low16Bits(offset));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002288 break;
2289 }
2290 CHECK_EQ(overwrite_location_, branch->GetEndLocation());
2291 CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize));
2292}
2293
2294void MipsAssembler::B(MipsLabel* label) {
2295 Buncond(label);
2296}
2297
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002298void MipsAssembler::Bal(MipsLabel* label) {
2299 Call(label);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002300}
2301
2302void MipsAssembler::Beq(Register rs, Register rt, MipsLabel* label) {
2303 Bcond(label, kCondEQ, rs, rt);
2304}
2305
2306void MipsAssembler::Bne(Register rs, Register rt, MipsLabel* label) {
2307 Bcond(label, kCondNE, rs, rt);
2308}
2309
2310void MipsAssembler::Beqz(Register rt, MipsLabel* label) {
2311 Bcond(label, kCondEQZ, rt);
2312}
2313
2314void MipsAssembler::Bnez(Register rt, MipsLabel* label) {
2315 Bcond(label, kCondNEZ, rt);
2316}
2317
2318void MipsAssembler::Bltz(Register rt, MipsLabel* label) {
2319 Bcond(label, kCondLTZ, rt);
2320}
2321
2322void MipsAssembler::Bgez(Register rt, MipsLabel* label) {
2323 Bcond(label, kCondGEZ, rt);
2324}
2325
2326void MipsAssembler::Blez(Register rt, MipsLabel* label) {
2327 Bcond(label, kCondLEZ, rt);
2328}
2329
2330void MipsAssembler::Bgtz(Register rt, MipsLabel* label) {
2331 Bcond(label, kCondGTZ, rt);
2332}
2333
2334void MipsAssembler::Blt(Register rs, Register rt, MipsLabel* label) {
2335 if (IsR6()) {
2336 Bcond(label, kCondLT, rs, rt);
2337 } else if (!Branch::IsNop(kCondLT, rs, rt)) {
2338 // Synthesize the instruction (not available on R2).
2339 Slt(AT, rs, rt);
2340 Bnez(AT, label);
2341 }
2342}
2343
2344void MipsAssembler::Bge(Register rs, Register rt, MipsLabel* label) {
2345 if (IsR6()) {
2346 Bcond(label, kCondGE, rs, rt);
2347 } else if (Branch::IsUncond(kCondGE, rs, rt)) {
2348 B(label);
2349 } else {
2350 // Synthesize the instruction (not available on R2).
2351 Slt(AT, rs, rt);
2352 Beqz(AT, label);
2353 }
2354}
2355
2356void MipsAssembler::Bltu(Register rs, Register rt, MipsLabel* label) {
2357 if (IsR6()) {
2358 Bcond(label, kCondLTU, rs, rt);
2359 } else if (!Branch::IsNop(kCondLTU, rs, rt)) {
2360 // Synthesize the instruction (not available on R2).
2361 Sltu(AT, rs, rt);
2362 Bnez(AT, label);
2363 }
2364}
2365
2366void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) {
2367 if (IsR6()) {
2368 Bcond(label, kCondGEU, rs, rt);
2369 } else if (Branch::IsUncond(kCondGEU, rs, rt)) {
2370 B(label);
2371 } else {
2372 // Synthesize the instruction (not available on R2).
2373 Sltu(AT, rs, rt);
2374 Beqz(AT, label);
jeffhao7fbee072012-08-24 17:56:54 -07002375 }
2376}
2377
Chris Larsenb74353a2015-11-20 09:07:09 -08002378void MipsAssembler::Bc1f(MipsLabel* label) {
2379 Bc1f(0, label);
2380}
2381
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002382void MipsAssembler::Bc1f(int cc, MipsLabel* label) {
2383 CHECK(IsUint<3>(cc)) << cc;
2384 Bcond(label, kCondF, static_cast<Register>(cc), ZERO);
2385}
2386
Chris Larsenb74353a2015-11-20 09:07:09 -08002387void MipsAssembler::Bc1t(MipsLabel* label) {
2388 Bc1t(0, label);
2389}
2390
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002391void MipsAssembler::Bc1t(int cc, MipsLabel* label) {
2392 CHECK(IsUint<3>(cc)) << cc;
2393 Bcond(label, kCondT, static_cast<Register>(cc), ZERO);
2394}
2395
2396void MipsAssembler::Bc1eqz(FRegister ft, MipsLabel* label) {
2397 Bcond(label, kCondF, static_cast<Register>(ft), ZERO);
2398}
2399
2400void MipsAssembler::Bc1nez(FRegister ft, MipsLabel* label) {
2401 Bcond(label, kCondT, static_cast<Register>(ft), ZERO);
2402}
2403
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002404void MipsAssembler::AdjustBaseAndOffset(Register& base,
2405 int32_t& offset,
2406 bool is_doubleword,
2407 bool is_float) {
2408 // This method is used to adjust the base register and offset pair
2409 // for a load/store when the offset doesn't fit into int16_t.
2410 // It is assumed that `base + offset` is sufficiently aligned for memory
2411 // operands that are machine word in size or smaller. For doubleword-sized
2412 // operands it's assumed that `base` is a multiple of 8, while `offset`
2413 // may be a multiple of 4 (e.g. 4-byte-aligned long and double arguments
2414 // and spilled variables on the stack accessed relative to the stack
2415 // pointer register).
2416 // We preserve the "alignment" of `offset` by adjusting it by a multiple of 8.
2417 CHECK_NE(base, AT); // Must not overwrite the register `base` while loading `offset`.
2418
2419 bool doubleword_aligned = IsAligned<kMipsDoublewordSize>(offset);
2420 bool two_accesses = is_doubleword && (!is_float || !doubleword_aligned);
2421
2422 // IsInt<16> must be passed a signed value, hence the static cast below.
2423 if (IsInt<16>(offset) &&
2424 (!two_accesses || IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
2425 // Nothing to do: `offset` (and, if needed, `offset + 4`) fits into int16_t.
2426 return;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002427 }
2428
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002429 // Remember the "(mis)alignment" of `offset`, it will be checked at the end.
2430 uint32_t misalignment = offset & (kMipsDoublewordSize - 1);
2431
2432 // Do not load the whole 32-bit `offset` if it can be represented as
2433 // a sum of two 16-bit signed offsets. This can save an instruction or two.
2434 // To simplify matters, only do this for a symmetric range of offsets from
2435 // about -64KB to about +64KB, allowing further addition of 4 when accessing
2436 // 64-bit variables with two 32-bit accesses.
2437 constexpr int32_t kMinOffsetForSimpleAdjustment = 0x7ff8; // Max int16_t that's a multiple of 8.
2438 constexpr int32_t kMaxOffsetForSimpleAdjustment = 2 * kMinOffsetForSimpleAdjustment;
2439 if (0 <= offset && offset <= kMaxOffsetForSimpleAdjustment) {
2440 Addiu(AT, base, kMinOffsetForSimpleAdjustment);
2441 offset -= kMinOffsetForSimpleAdjustment;
2442 } else if (-kMaxOffsetForSimpleAdjustment <= offset && offset < 0) {
2443 Addiu(AT, base, -kMinOffsetForSimpleAdjustment);
2444 offset += kMinOffsetForSimpleAdjustment;
2445 } else if (IsR6()) {
2446 // On R6 take advantage of the aui instruction, e.g.:
2447 // aui AT, base, offset_high
2448 // lw reg_lo, offset_low(AT)
2449 // lw reg_hi, (offset_low+4)(AT)
2450 // or when offset_low+4 overflows int16_t:
2451 // aui AT, base, offset_high
2452 // addiu AT, AT, 8
2453 // lw reg_lo, (offset_low-8)(AT)
2454 // lw reg_hi, (offset_low-4)(AT)
2455 int16_t offset_high = High16Bits(offset);
2456 int16_t offset_low = Low16Bits(offset);
2457 offset_high += (offset_low < 0) ? 1 : 0; // Account for offset sign extension in load/store.
2458 Aui(AT, base, offset_high);
2459 if (two_accesses && !IsInt<16>(static_cast<int32_t>(offset_low + kMipsWordSize))) {
2460 // Avoid overflow in the 16-bit offset of the load/store instruction when adding 4.
2461 Addiu(AT, AT, kMipsDoublewordSize);
2462 offset_low -= kMipsDoublewordSize;
2463 }
2464 offset = offset_low;
2465 } else {
2466 // Do not load the whole 32-bit `offset` if it can be represented as
2467 // a sum of three 16-bit signed offsets. This can save an instruction.
2468 // To simplify matters, only do this for a symmetric range of offsets from
2469 // about -96KB to about +96KB, allowing further addition of 4 when accessing
2470 // 64-bit variables with two 32-bit accesses.
2471 constexpr int32_t kMinOffsetForMediumAdjustment = 2 * kMinOffsetForSimpleAdjustment;
2472 constexpr int32_t kMaxOffsetForMediumAdjustment = 3 * kMinOffsetForSimpleAdjustment;
2473 if (0 <= offset && offset <= kMaxOffsetForMediumAdjustment) {
2474 Addiu(AT, base, kMinOffsetForMediumAdjustment / 2);
2475 Addiu(AT, AT, kMinOffsetForMediumAdjustment / 2);
2476 offset -= kMinOffsetForMediumAdjustment;
2477 } else if (-kMaxOffsetForMediumAdjustment <= offset && offset < 0) {
2478 Addiu(AT, base, -kMinOffsetForMediumAdjustment / 2);
2479 Addiu(AT, AT, -kMinOffsetForMediumAdjustment / 2);
2480 offset += kMinOffsetForMediumAdjustment;
2481 } else {
2482 // Now that all shorter options have been exhausted, load the full 32-bit offset.
2483 int32_t loaded_offset = RoundDown(offset, kMipsDoublewordSize);
2484 LoadConst32(AT, loaded_offset);
2485 Addu(AT, AT, base);
2486 offset -= loaded_offset;
2487 }
2488 }
2489 base = AT;
2490
2491 CHECK(IsInt<16>(offset));
2492 if (two_accesses) {
2493 CHECK(IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)));
2494 }
2495 CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
2496}
2497
Alexey Frunze2923db72016-08-20 01:55:47 -07002498void MipsAssembler::LoadFromOffset(LoadOperandType type,
2499 Register reg,
2500 Register base,
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002501 int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07002502 LoadFromOffset<>(type, reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07002503}
2504
2505void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07002506 LoadSFromOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07002507}
2508
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002509void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07002510 LoadDFromOffset<>(reg, base, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002511}
2512
2513void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
2514 size_t size) {
2515 MipsManagedRegister dst = m_dst.AsMips();
2516 if (dst.IsNoRegister()) {
2517 CHECK_EQ(0u, size) << dst;
2518 } else if (dst.IsCoreRegister()) {
2519 CHECK_EQ(kMipsWordSize, size) << dst;
2520 LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
2521 } else if (dst.IsRegisterPair()) {
2522 CHECK_EQ(kMipsDoublewordSize, size) << dst;
2523 LoadFromOffset(kLoadDoubleword, dst.AsRegisterPairLow(), src_register, src_offset);
2524 } else if (dst.IsFRegister()) {
2525 if (size == kMipsWordSize) {
2526 LoadSFromOffset(dst.AsFRegister(), src_register, src_offset);
2527 } else {
2528 CHECK_EQ(kMipsDoublewordSize, size) << dst;
2529 LoadDFromOffset(dst.AsFRegister(), src_register, src_offset);
2530 }
2531 }
jeffhao7fbee072012-08-24 17:56:54 -07002532}
2533
Alexey Frunze2923db72016-08-20 01:55:47 -07002534void MipsAssembler::StoreToOffset(StoreOperandType type,
2535 Register reg,
2536 Register base,
jeffhao7fbee072012-08-24 17:56:54 -07002537 int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07002538 StoreToOffset<>(type, reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07002539}
2540
Goran Jakovljevicff734982015-08-24 12:58:55 +00002541void MipsAssembler::StoreSToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07002542 StoreSToOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07002543}
2544
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002545void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07002546 StoreDToOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07002547}
2548
David Srbeckydd973932015-04-07 20:29:48 +01002549static dwarf::Reg DWARFReg(Register reg) {
2550 return dwarf::Reg::MipsCore(static_cast<int>(reg));
2551}
2552
Ian Rogers790a6b72014-04-01 10:36:00 -07002553constexpr size_t kFramePointerSize = 4;
2554
Vladimir Marko32248382016-05-19 10:37:24 +01002555void MipsAssembler::BuildFrame(size_t frame_size,
2556 ManagedRegister method_reg,
2557 ArrayRef<const ManagedRegister> callee_save_regs,
Dmitry Petrochenkofca82202014-03-21 11:21:37 +07002558 const ManagedRegisterEntrySpills& entry_spills) {
jeffhao7fbee072012-08-24 17:56:54 -07002559 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01002560 DCHECK(!overwriting_);
jeffhao7fbee072012-08-24 17:56:54 -07002561
2562 // Increase frame to required size.
2563 IncreaseFrameSize(frame_size);
2564
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002565 // Push callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07002566 int stack_offset = frame_size - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07002567 StoreToOffset(kStoreWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01002568 cfi_.RelOffset(DWARFReg(RA), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07002569 for (int i = callee_save_regs.size() - 1; i >= 0; --i) {
Ian Rogers790a6b72014-04-01 10:36:00 -07002570 stack_offset -= kFramePointerSize;
Vladimir Marko32248382016-05-19 10:37:24 +01002571 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07002572 StoreToOffset(kStoreWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01002573 cfi_.RelOffset(DWARFReg(reg), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07002574 }
2575
2576 // Write out Method*.
2577 StoreToOffset(kStoreWord, method_reg.AsMips().AsCoreRegister(), SP, 0);
2578
2579 // Write out entry spills.
Goran Jakovljevicff734982015-08-24 12:58:55 +00002580 int32_t offset = frame_size + kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07002581 for (size_t i = 0; i < entry_spills.size(); ++i) {
Goran Jakovljevicff734982015-08-24 12:58:55 +00002582 MipsManagedRegister reg = entry_spills.at(i).AsMips();
2583 if (reg.IsNoRegister()) {
2584 ManagedRegisterSpill spill = entry_spills.at(i);
2585 offset += spill.getSize();
2586 } else if (reg.IsCoreRegister()) {
2587 StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002588 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00002589 } else if (reg.IsFRegister()) {
2590 StoreSToOffset(reg.AsFRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002591 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00002592 } else if (reg.IsDRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002593 StoreDToOffset(reg.AsOverlappingDRegisterLow(), SP, offset);
2594 offset += kMipsDoublewordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00002595 }
jeffhao7fbee072012-08-24 17:56:54 -07002596 }
2597}
2598
2599void MipsAssembler::RemoveFrame(size_t frame_size,
Vladimir Marko32248382016-05-19 10:37:24 +01002600 ArrayRef<const ManagedRegister> callee_save_regs) {
jeffhao7fbee072012-08-24 17:56:54 -07002601 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01002602 DCHECK(!overwriting_);
David Srbeckydd973932015-04-07 20:29:48 +01002603 cfi_.RememberState();
jeffhao7fbee072012-08-24 17:56:54 -07002604
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002605 // Pop callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07002606 int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07002607 for (size_t i = 0; i < callee_save_regs.size(); ++i) {
Vladimir Marko32248382016-05-19 10:37:24 +01002608 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07002609 LoadFromOffset(kLoadWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01002610 cfi_.Restore(DWARFReg(reg));
Ian Rogers790a6b72014-04-01 10:36:00 -07002611 stack_offset += kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07002612 }
2613 LoadFromOffset(kLoadWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01002614 cfi_.Restore(DWARFReg(RA));
jeffhao7fbee072012-08-24 17:56:54 -07002615
2616 // Decrease frame to required size.
2617 DecreaseFrameSize(frame_size);
jeffhao07030602012-09-26 14:33:14 -07002618
2619 // Then jump to the return address.
2620 Jr(RA);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002621 Nop();
David Srbeckydd973932015-04-07 20:29:48 +01002622
2623 // The CFI should be restored for any code that follows the exit block.
2624 cfi_.RestoreState();
2625 cfi_.DefCFAOffset(frame_size);
jeffhao7fbee072012-08-24 17:56:54 -07002626}
2627
2628void MipsAssembler::IncreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002629 CHECK_ALIGNED(adjust, kFramePointerSize);
2630 Addiu32(SP, SP, -adjust);
David Srbeckydd973932015-04-07 20:29:48 +01002631 cfi_.AdjustCFAOffset(adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01002632 if (overwriting_) {
2633 cfi_.OverrideDelayedPC(overwrite_location_);
2634 }
jeffhao7fbee072012-08-24 17:56:54 -07002635}
2636
2637void MipsAssembler::DecreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002638 CHECK_ALIGNED(adjust, kFramePointerSize);
2639 Addiu32(SP, SP, adjust);
David Srbeckydd973932015-04-07 20:29:48 +01002640 cfi_.AdjustCFAOffset(-adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01002641 if (overwriting_) {
2642 cfi_.OverrideDelayedPC(overwrite_location_);
2643 }
jeffhao7fbee072012-08-24 17:56:54 -07002644}
2645
2646void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
2647 MipsManagedRegister src = msrc.AsMips();
2648 if (src.IsNoRegister()) {
2649 CHECK_EQ(0u, size);
2650 } else if (src.IsCoreRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002651 CHECK_EQ(kMipsWordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07002652 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
2653 } else if (src.IsRegisterPair()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002654 CHECK_EQ(kMipsDoublewordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07002655 StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
2656 StoreToOffset(kStoreWord, src.AsRegisterPairHigh(),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002657 SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002658 } else if (src.IsFRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002659 if (size == kMipsWordSize) {
2660 StoreSToOffset(src.AsFRegister(), SP, dest.Int32Value());
2661 } else {
2662 CHECK_EQ(kMipsDoublewordSize, size);
2663 StoreDToOffset(src.AsFRegister(), SP, dest.Int32Value());
2664 }
jeffhao7fbee072012-08-24 17:56:54 -07002665 }
2666}
2667
2668void MipsAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
2669 MipsManagedRegister src = msrc.AsMips();
2670 CHECK(src.IsCoreRegister());
2671 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
2672}
2673
2674void MipsAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
2675 MipsManagedRegister src = msrc.AsMips();
2676 CHECK(src.IsCoreRegister());
2677 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
2678}
2679
2680void MipsAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
2681 ManagedRegister mscratch) {
2682 MipsManagedRegister scratch = mscratch.AsMips();
2683 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002684 LoadConst32(scratch.AsCoreRegister(), imm);
jeffhao7fbee072012-08-24 17:56:54 -07002685 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
2686}
2687
Andreas Gampe3b165bc2016-08-01 22:07:04 -07002688void MipsAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs,
2689 FrameOffset fr_offs,
jeffhao7fbee072012-08-24 17:56:54 -07002690 ManagedRegister mscratch) {
2691 MipsManagedRegister scratch = mscratch.AsMips();
2692 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002693 Addiu32(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07002694 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
2695 S1, thr_offs.Int32Value());
2696}
2697
Andreas Gampe3b165bc2016-08-01 22:07:04 -07002698void MipsAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
jeffhao7fbee072012-08-24 17:56:54 -07002699 StoreToOffset(kStoreWord, SP, S1, thr_offs.Int32Value());
2700}
2701
2702void MipsAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
2703 FrameOffset in_off, ManagedRegister mscratch) {
2704 MipsManagedRegister src = msrc.AsMips();
2705 MipsManagedRegister scratch = mscratch.AsMips();
2706 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
2707 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002708 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002709}
2710
2711void MipsAssembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) {
2712 return EmitLoad(mdest, SP, src.Int32Value(), size);
2713}
2714
Andreas Gampe3b165bc2016-08-01 22:07:04 -07002715void MipsAssembler::LoadFromThread(ManagedRegister mdest, ThreadOffset32 src, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07002716 return EmitLoad(mdest, S1, src.Int32Value(), size);
2717}
2718
2719void MipsAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) {
2720 MipsManagedRegister dest = mdest.AsMips();
2721 CHECK(dest.IsCoreRegister());
2722 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value());
2723}
2724
Mathieu Chartiere401d142015-04-22 13:56:20 -07002725void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs,
Roland Levillain4d027112015-07-01 15:41:14 +01002726 bool unpoison_reference) {
jeffhao7fbee072012-08-24 17:56:54 -07002727 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002728 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07002729 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
2730 base.AsMips().AsCoreRegister(), offs.Int32Value());
Roland Levillain4d027112015-07-01 15:41:14 +01002731 if (kPoisonHeapReferences && unpoison_reference) {
Hiroshi Yamauchie63a7452014-02-27 14:44:36 -08002732 Subu(dest.AsCoreRegister(), ZERO, dest.AsCoreRegister());
2733 }
jeffhao7fbee072012-08-24 17:56:54 -07002734}
2735
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002736void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) {
jeffhao7fbee072012-08-24 17:56:54 -07002737 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002738 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07002739 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
2740 base.AsMips().AsCoreRegister(), offs.Int32Value());
2741}
2742
Andreas Gampe3b165bc2016-08-01 22:07:04 -07002743void MipsAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset32 offs) {
jeffhao7fbee072012-08-24 17:56:54 -07002744 MipsManagedRegister dest = mdest.AsMips();
2745 CHECK(dest.IsCoreRegister());
2746 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), S1, offs.Int32Value());
2747}
2748
2749void MipsAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
2750 UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips";
2751}
2752
2753void MipsAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
2754 UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips";
2755}
2756
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002757void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07002758 MipsManagedRegister dest = mdest.AsMips();
2759 MipsManagedRegister src = msrc.AsMips();
2760 if (!dest.Equals(src)) {
2761 if (dest.IsCoreRegister()) {
2762 CHECK(src.IsCoreRegister()) << src;
2763 Move(dest.AsCoreRegister(), src.AsCoreRegister());
2764 } else if (dest.IsFRegister()) {
2765 CHECK(src.IsFRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002766 if (size == kMipsWordSize) {
2767 MovS(dest.AsFRegister(), src.AsFRegister());
2768 } else {
2769 CHECK_EQ(kMipsDoublewordSize, size);
2770 MovD(dest.AsFRegister(), src.AsFRegister());
2771 }
jeffhao7fbee072012-08-24 17:56:54 -07002772 } else if (dest.IsDRegister()) {
2773 CHECK(src.IsDRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002774 MovD(dest.AsOverlappingDRegisterLow(), src.AsOverlappingDRegisterLow());
jeffhao7fbee072012-08-24 17:56:54 -07002775 } else {
2776 CHECK(dest.IsRegisterPair()) << dest;
2777 CHECK(src.IsRegisterPair()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002778 // Ensure that the first move doesn't clobber the input of the second.
jeffhao7fbee072012-08-24 17:56:54 -07002779 if (src.AsRegisterPairHigh() != dest.AsRegisterPairLow()) {
2780 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
2781 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
2782 } else {
2783 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
2784 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
2785 }
2786 }
2787 }
2788}
2789
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002790void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07002791 MipsManagedRegister scratch = mscratch.AsMips();
2792 CHECK(scratch.IsCoreRegister()) << scratch;
2793 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
2794 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
2795}
2796
Andreas Gampe3b165bc2016-08-01 22:07:04 -07002797void MipsAssembler::CopyRawPtrFromThread(FrameOffset fr_offs,
2798 ThreadOffset32 thr_offs,
2799 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07002800 MipsManagedRegister scratch = mscratch.AsMips();
2801 CHECK(scratch.IsCoreRegister()) << scratch;
2802 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
2803 S1, thr_offs.Int32Value());
2804 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
2805 SP, fr_offs.Int32Value());
2806}
2807
Andreas Gampe3b165bc2016-08-01 22:07:04 -07002808void MipsAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs,
2809 FrameOffset fr_offs,
2810 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07002811 MipsManagedRegister scratch = mscratch.AsMips();
2812 CHECK(scratch.IsCoreRegister()) << scratch;
2813 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
2814 SP, fr_offs.Int32Value());
2815 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
2816 S1, thr_offs.Int32Value());
2817}
2818
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002819void MipsAssembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07002820 MipsManagedRegister scratch = mscratch.AsMips();
2821 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002822 CHECK(size == kMipsWordSize || size == kMipsDoublewordSize) << size;
2823 if (size == kMipsWordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07002824 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
2825 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002826 } else if (size == kMipsDoublewordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07002827 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
2828 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002829 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + kMipsWordSize);
2830 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002831 }
2832}
2833
2834void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
2835 ManagedRegister mscratch, size_t size) {
2836 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002837 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002838 LoadFromOffset(kLoadWord, scratch, src_base.AsMips().AsCoreRegister(), src_offset.Int32Value());
2839 StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
2840}
2841
2842void MipsAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
2843 ManagedRegister mscratch, size_t size) {
2844 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002845 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002846 LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
2847 StoreToOffset(kStoreWord, scratch, dest_base.AsMips().AsCoreRegister(), dest_offset.Int32Value());
2848}
2849
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002850void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
2851 FrameOffset src_base ATTRIBUTE_UNUSED,
2852 Offset src_offset ATTRIBUTE_UNUSED,
2853 ManagedRegister mscratch ATTRIBUTE_UNUSED,
2854 size_t size ATTRIBUTE_UNUSED) {
2855 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002856}
2857
2858void MipsAssembler::Copy(ManagedRegister dest, Offset dest_offset,
2859 ManagedRegister src, Offset src_offset,
2860 ManagedRegister mscratch, size_t size) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002861 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002862 Register scratch = mscratch.AsMips().AsCoreRegister();
2863 LoadFromOffset(kLoadWord, scratch, src.AsMips().AsCoreRegister(), src_offset.Int32Value());
2864 StoreToOffset(kStoreWord, scratch, dest.AsMips().AsCoreRegister(), dest_offset.Int32Value());
2865}
2866
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002867void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
2868 Offset dest_offset ATTRIBUTE_UNUSED,
2869 FrameOffset src ATTRIBUTE_UNUSED,
2870 Offset src_offset ATTRIBUTE_UNUSED,
2871 ManagedRegister mscratch ATTRIBUTE_UNUSED,
2872 size_t size ATTRIBUTE_UNUSED) {
2873 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002874}
2875
2876void MipsAssembler::MemoryBarrier(ManagedRegister) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002877 // TODO: sync?
2878 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002879}
2880
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002881void MipsAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002882 FrameOffset handle_scope_offset,
2883 ManagedRegister min_reg,
2884 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07002885 MipsManagedRegister out_reg = mout_reg.AsMips();
2886 MipsManagedRegister in_reg = min_reg.AsMips();
2887 CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
2888 CHECK(out_reg.IsCoreRegister()) << out_reg;
2889 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002890 MipsLabel null_arg;
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002891 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
2892 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002893 // E.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset).
jeffhao7fbee072012-08-24 17:56:54 -07002894 if (in_reg.IsNoRegister()) {
2895 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002896 SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07002897 in_reg = out_reg;
2898 }
2899 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002900 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07002901 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002902 Beqz(in_reg.AsCoreRegister(), &null_arg);
2903 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
2904 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07002905 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002906 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07002907 }
2908}
2909
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002910void MipsAssembler::CreateHandleScopeEntry(FrameOffset out_off,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002911 FrameOffset handle_scope_offset,
2912 ManagedRegister mscratch,
2913 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07002914 MipsManagedRegister scratch = mscratch.AsMips();
2915 CHECK(scratch.IsCoreRegister()) << scratch;
2916 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002917 MipsLabel null_arg;
2918 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002919 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
2920 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002921 // E.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset).
2922 Beqz(scratch.AsCoreRegister(), &null_arg);
2923 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
2924 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07002925 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002926 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07002927 }
2928 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
2929}
2930
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002931// Given a handle scope entry, load the associated reference.
2932void MipsAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002933 ManagedRegister min_reg) {
jeffhao7fbee072012-08-24 17:56:54 -07002934 MipsManagedRegister out_reg = mout_reg.AsMips();
2935 MipsManagedRegister in_reg = min_reg.AsMips();
2936 CHECK(out_reg.IsCoreRegister()) << out_reg;
2937 CHECK(in_reg.IsCoreRegister()) << in_reg;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002938 MipsLabel null_arg;
jeffhao7fbee072012-08-24 17:56:54 -07002939 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002940 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07002941 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002942 Beqz(in_reg.AsCoreRegister(), &null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07002943 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
2944 in_reg.AsCoreRegister(), 0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002945 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07002946}
2947
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002948void MipsAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,
2949 bool could_be_null ATTRIBUTE_UNUSED) {
2950 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07002951}
2952
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002953void MipsAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED,
2954 bool could_be_null ATTRIBUTE_UNUSED) {
2955 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07002956}
2957
2958void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) {
2959 MipsManagedRegister base = mbase.AsMips();
2960 MipsManagedRegister scratch = mscratch.AsMips();
2961 CHECK(base.IsCoreRegister()) << base;
2962 CHECK(scratch.IsCoreRegister()) << scratch;
2963 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
2964 base.AsCoreRegister(), offset.Int32Value());
2965 Jalr(scratch.AsCoreRegister());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002966 Nop();
2967 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07002968}
2969
2970void MipsAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
2971 MipsManagedRegister scratch = mscratch.AsMips();
2972 CHECK(scratch.IsCoreRegister()) << scratch;
2973 // Call *(*(SP + base) + offset)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002974 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, base.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07002975 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
2976 scratch.AsCoreRegister(), offset.Int32Value());
2977 Jalr(scratch.AsCoreRegister());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002978 Nop();
2979 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07002980}
2981
Andreas Gampe3b165bc2016-08-01 22:07:04 -07002982void MipsAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UNUSED,
2983 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
Ian Rogers468532e2013-08-05 10:56:33 -07002984 UNIMPLEMENTED(FATAL) << "no mips implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002985}
2986
2987void MipsAssembler::GetCurrentThread(ManagedRegister tr) {
2988 Move(tr.AsMips().AsCoreRegister(), S1);
2989}
2990
2991void MipsAssembler::GetCurrentThread(FrameOffset offset,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002992 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
jeffhao7fbee072012-08-24 17:56:54 -07002993 StoreToOffset(kStoreWord, S1, SP, offset.Int32Value());
2994}
2995
jeffhao7fbee072012-08-24 17:56:54 -07002996void MipsAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
2997 MipsManagedRegister scratch = mscratch.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002998 exception_blocks_.emplace_back(scratch, stack_adjust);
jeffhao7fbee072012-08-24 17:56:54 -07002999 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
Andreas Gampe542451c2016-07-26 09:02:02 -07003000 S1, Thread::ExceptionOffset<kMipsPointerSize>().Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003001 // TODO: on MIPS32R6 prefer Bnezc(scratch.AsCoreRegister(), slow.Entry());
3002 // as the NAL instruction (occurring in long R2 branches) may become deprecated.
3003 // For now use common for R2 and R6 instructions as this code must execute on both.
3004 Bnez(scratch.AsCoreRegister(), exception_blocks_.back().Entry());
jeffhao7fbee072012-08-24 17:56:54 -07003005}
3006
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003007void MipsAssembler::EmitExceptionPoll(MipsExceptionSlowPath* exception) {
3008 Bind(exception->Entry());
3009 if (exception->stack_adjust_ != 0) { // Fix up the frame.
3010 DecreaseFrameSize(exception->stack_adjust_);
jeffhao7fbee072012-08-24 17:56:54 -07003011 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003012 // Pass exception object as argument.
3013 // Don't care about preserving A0 as this call won't return.
3014 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
3015 Move(A0, exception->scratch_.AsCoreRegister());
3016 // Set up call to Thread::Current()->pDeliverException.
3017 LoadFromOffset(kLoadWord, T9, S1,
Andreas Gampe542451c2016-07-26 09:02:02 -07003018 QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, pDeliverException).Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003019 Jr(T9);
3020 Nop();
3021
3022 // Call never returns.
3023 Break();
jeffhao7fbee072012-08-24 17:56:54 -07003024}
3025
3026} // namespace mips
3027} // namespace art