blob: 86e5762f9cdcdf1fb0a55050eba66bbfda239d27 [file] [log] [blame]
jeffhao7fbee072012-08-24 17:56:54 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "assembler_mips.h"
18
Vladimir Marko80afd022015-05-19 18:08:00 +010019#include "base/bit_utils.h"
Elliott Hughes1aa246d2012-12-13 09:29:36 -080020#include "base/casts.h"
Ian Rogers166db042013-07-26 12:05:57 -070021#include "entrypoints/quick/quick_entrypoints.h"
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +020022#include "entrypoints/quick/quick_entrypoints_enum.h"
jeffhao7fbee072012-08-24 17:56:54 -070023#include "memory_region.h"
jeffhao7fbee072012-08-24 17:56:54 -070024#include "thread.h"
25
26namespace art {
27namespace mips {
jeffhao7fbee072012-08-24 17:56:54 -070028
jeffhao7fbee072012-08-24 17:56:54 -070029std::ostream& operator<<(std::ostream& os, const DRegister& rhs) {
30 if (rhs >= D0 && rhs < kNumberOfDRegisters) {
31 os << "d" << static_cast<int>(rhs);
32 } else {
33 os << "DRegister[" << static_cast<int>(rhs) << "]";
34 }
35 return os;
36}
37
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +020038void MipsAssembler::FinalizeCode() {
39 for (auto& exception_block : exception_blocks_) {
40 EmitExceptionPoll(&exception_block);
41 }
42 PromoteBranches();
43}
44
45void MipsAssembler::FinalizeInstructions(const MemoryRegion& region) {
Vladimir Marko10ef6942015-10-22 15:25:54 +010046 size_t number_of_delayed_adjust_pcs = cfi().NumberOfDelayedAdvancePCs();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +020047 EmitBranches();
48 Assembler::FinalizeInstructions(region);
Vladimir Marko10ef6942015-10-22 15:25:54 +010049 PatchCFI(number_of_delayed_adjust_pcs);
50}
51
52void MipsAssembler::PatchCFI(size_t number_of_delayed_adjust_pcs) {
53 if (cfi().NumberOfDelayedAdvancePCs() == 0u) {
54 DCHECK_EQ(number_of_delayed_adjust_pcs, 0u);
55 return;
56 }
57
58 typedef DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC DelayedAdvancePC;
59 const auto data = cfi().ReleaseStreamAndPrepareForDelayedAdvancePC();
60 const std::vector<uint8_t>& old_stream = data.first;
61 const std::vector<DelayedAdvancePC>& advances = data.second;
62
63 // PCs recorded before EmitBranches() need to be adjusted.
64 // PCs recorded during EmitBranches() are already adjusted.
65 // Both ranges are separately sorted but they may overlap.
66 if (kIsDebugBuild) {
67 auto cmp = [](const DelayedAdvancePC& lhs, const DelayedAdvancePC& rhs) {
68 return lhs.pc < rhs.pc;
69 };
70 CHECK(std::is_sorted(advances.begin(), advances.begin() + number_of_delayed_adjust_pcs, cmp));
71 CHECK(std::is_sorted(advances.begin() + number_of_delayed_adjust_pcs, advances.end(), cmp));
72 }
73
74 // Append initial CFI data if any.
75 size_t size = advances.size();
76 DCHECK_NE(size, 0u);
77 cfi().AppendRawData(old_stream, 0u, advances[0].stream_pos);
78 // Emit PC adjustments interleaved with the old CFI stream.
79 size_t adjust_pos = 0u;
80 size_t late_emit_pos = number_of_delayed_adjust_pcs;
81 while (adjust_pos != number_of_delayed_adjust_pcs || late_emit_pos != size) {
82 size_t adjusted_pc = (adjust_pos != number_of_delayed_adjust_pcs)
83 ? GetAdjustedPosition(advances[adjust_pos].pc)
84 : static_cast<size_t>(-1);
85 size_t late_emit_pc = (late_emit_pos != size)
86 ? advances[late_emit_pos].pc
87 : static_cast<size_t>(-1);
88 size_t advance_pc = std::min(adjusted_pc, late_emit_pc);
89 DCHECK_NE(advance_pc, static_cast<size_t>(-1));
90 size_t entry = (adjusted_pc <= late_emit_pc) ? adjust_pos : late_emit_pos;
91 if (adjusted_pc <= late_emit_pc) {
92 ++adjust_pos;
93 } else {
94 ++late_emit_pos;
95 }
96 cfi().AdvancePC(advance_pc);
97 size_t end_pos = (entry + 1u == size) ? old_stream.size() : advances[entry + 1u].stream_pos;
98 cfi().AppendRawData(old_stream, advances[entry].stream_pos, end_pos);
99 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200100}
101
102void MipsAssembler::EmitBranches() {
103 CHECK(!overwriting_);
104 // Switch from appending instructions at the end of the buffer to overwriting
105 // existing instructions (branch placeholders) in the buffer.
106 overwriting_ = true;
107 for (auto& branch : branches_) {
108 EmitBranch(&branch);
109 }
110 overwriting_ = false;
111}
112
113void MipsAssembler::Emit(uint32_t value) {
114 if (overwriting_) {
115 // Branches to labels are emitted into their placeholders here.
116 buffer_.Store<uint32_t>(overwrite_location_, value);
117 overwrite_location_ += sizeof(uint32_t);
118 } else {
119 // Other instructions are simply appended at the end here.
120 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
121 buffer_.Emit<uint32_t>(value);
122 }
jeffhao7fbee072012-08-24 17:56:54 -0700123}
124
125void MipsAssembler::EmitR(int opcode, Register rs, Register rt, Register rd, int shamt, int funct) {
126 CHECK_NE(rs, kNoRegister);
127 CHECK_NE(rt, kNoRegister);
128 CHECK_NE(rd, kNoRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200129 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
130 static_cast<uint32_t>(rs) << kRsShift |
131 static_cast<uint32_t>(rt) << kRtShift |
132 static_cast<uint32_t>(rd) << kRdShift |
133 shamt << kShamtShift |
134 funct;
jeffhao7fbee072012-08-24 17:56:54 -0700135 Emit(encoding);
136}
137
138void MipsAssembler::EmitI(int opcode, Register rs, Register rt, uint16_t imm) {
139 CHECK_NE(rs, kNoRegister);
140 CHECK_NE(rt, kNoRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200141 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
142 static_cast<uint32_t>(rs) << kRsShift |
143 static_cast<uint32_t>(rt) << kRtShift |
144 imm;
jeffhao7fbee072012-08-24 17:56:54 -0700145 Emit(encoding);
146}
147
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200148void MipsAssembler::EmitI21(int opcode, Register rs, uint32_t imm21) {
149 CHECK_NE(rs, kNoRegister);
150 CHECK(IsUint<21>(imm21)) << imm21;
151 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
152 static_cast<uint32_t>(rs) << kRsShift |
153 imm21;
jeffhao7fbee072012-08-24 17:56:54 -0700154 Emit(encoding);
155}
156
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200157void MipsAssembler::EmitI26(int opcode, uint32_t imm26) {
158 CHECK(IsUint<26>(imm26)) << imm26;
159 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | imm26;
160 Emit(encoding);
161}
162
163void MipsAssembler::EmitFR(int opcode, int fmt, FRegister ft, FRegister fs, FRegister fd,
164 int funct) {
jeffhao7fbee072012-08-24 17:56:54 -0700165 CHECK_NE(ft, kNoFRegister);
166 CHECK_NE(fs, kNoFRegister);
167 CHECK_NE(fd, kNoFRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200168 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
169 fmt << kFmtShift |
170 static_cast<uint32_t>(ft) << kFtShift |
171 static_cast<uint32_t>(fs) << kFsShift |
172 static_cast<uint32_t>(fd) << kFdShift |
173 funct;
jeffhao7fbee072012-08-24 17:56:54 -0700174 Emit(encoding);
175}
176
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200177void MipsAssembler::EmitFI(int opcode, int fmt, FRegister ft, uint16_t imm) {
178 CHECK_NE(ft, kNoFRegister);
179 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
180 fmt << kFmtShift |
181 static_cast<uint32_t>(ft) << kFtShift |
182 imm;
jeffhao7fbee072012-08-24 17:56:54 -0700183 Emit(encoding);
184}
185
jeffhao7fbee072012-08-24 17:56:54 -0700186void MipsAssembler::Addu(Register rd, Register rs, Register rt) {
187 EmitR(0, rs, rt, rd, 0, 0x21);
188}
189
jeffhao7fbee072012-08-24 17:56:54 -0700190void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) {
191 EmitI(0x9, rs, rt, imm16);
192}
193
jeffhao7fbee072012-08-24 17:56:54 -0700194void MipsAssembler::Subu(Register rd, Register rs, Register rt) {
195 EmitR(0, rs, rt, rd, 0, 0x23);
196}
197
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200198void MipsAssembler::MultR2(Register rs, Register rt) {
199 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700200 EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x18);
201}
202
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200203void MipsAssembler::MultuR2(Register rs, Register rt) {
204 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700205 EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x19);
206}
207
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200208void MipsAssembler::DivR2(Register rs, Register rt) {
209 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700210 EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1a);
211}
212
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200213void MipsAssembler::DivuR2(Register rs, Register rt) {
214 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700215 EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1b);
216}
217
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200218void MipsAssembler::MulR2(Register rd, Register rs, Register rt) {
219 CHECK(!IsR6());
220 EmitR(0x1c, rs, rt, rd, 0, 2);
221}
222
223void MipsAssembler::DivR2(Register rd, Register rs, Register rt) {
224 CHECK(!IsR6());
225 DivR2(rs, rt);
226 Mflo(rd);
227}
228
229void MipsAssembler::ModR2(Register rd, Register rs, Register rt) {
230 CHECK(!IsR6());
231 DivR2(rs, rt);
232 Mfhi(rd);
233}
234
235void MipsAssembler::DivuR2(Register rd, Register rs, Register rt) {
236 CHECK(!IsR6());
237 DivuR2(rs, rt);
238 Mflo(rd);
239}
240
241void MipsAssembler::ModuR2(Register rd, Register rs, Register rt) {
242 CHECK(!IsR6());
243 DivuR2(rs, rt);
244 Mfhi(rd);
245}
246
247void MipsAssembler::MulR6(Register rd, Register rs, Register rt) {
248 CHECK(IsR6());
249 EmitR(0, rs, rt, rd, 2, 0x18);
250}
251
252void MipsAssembler::MuhuR6(Register rd, Register rs, Register rt) {
253 CHECK(IsR6());
254 EmitR(0, rs, rt, rd, 3, 0x19);
255}
256
257void MipsAssembler::DivR6(Register rd, Register rs, Register rt) {
258 CHECK(IsR6());
259 EmitR(0, rs, rt, rd, 2, 0x1a);
260}
261
262void MipsAssembler::ModR6(Register rd, Register rs, Register rt) {
263 CHECK(IsR6());
264 EmitR(0, rs, rt, rd, 3, 0x1a);
265}
266
267void MipsAssembler::DivuR6(Register rd, Register rs, Register rt) {
268 CHECK(IsR6());
269 EmitR(0, rs, rt, rd, 2, 0x1b);
270}
271
272void MipsAssembler::ModuR6(Register rd, Register rs, Register rt) {
273 CHECK(IsR6());
274 EmitR(0, rs, rt, rd, 3, 0x1b);
275}
276
jeffhao7fbee072012-08-24 17:56:54 -0700277void MipsAssembler::And(Register rd, Register rs, Register rt) {
278 EmitR(0, rs, rt, rd, 0, 0x24);
279}
280
281void MipsAssembler::Andi(Register rt, Register rs, uint16_t imm16) {
282 EmitI(0xc, rs, rt, imm16);
283}
284
285void MipsAssembler::Or(Register rd, Register rs, Register rt) {
286 EmitR(0, rs, rt, rd, 0, 0x25);
287}
288
289void MipsAssembler::Ori(Register rt, Register rs, uint16_t imm16) {
290 EmitI(0xd, rs, rt, imm16);
291}
292
293void MipsAssembler::Xor(Register rd, Register rs, Register rt) {
294 EmitR(0, rs, rt, rd, 0, 0x26);
295}
296
297void MipsAssembler::Xori(Register rt, Register rs, uint16_t imm16) {
298 EmitI(0xe, rs, rt, imm16);
299}
300
301void MipsAssembler::Nor(Register rd, Register rs, Register rt) {
302 EmitR(0, rs, rt, rd, 0, 0x27);
303}
304
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200305void MipsAssembler::Seb(Register rd, Register rt) {
306 EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x10, 0x20);
jeffhao7fbee072012-08-24 17:56:54 -0700307}
308
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200309void MipsAssembler::Seh(Register rd, Register rt) {
310 EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x18, 0x20);
jeffhao7fbee072012-08-24 17:56:54 -0700311}
312
Chris Larsen3f8bf652015-10-28 10:08:56 -0700313void MipsAssembler::Wsbh(Register rd, Register rt) {
314 EmitR(0x1f, static_cast<Register>(0), rt, rd, 2, 0x20);
315}
316
Chris Larsen70014c82015-11-18 12:26:08 -0800317void MipsAssembler::Bitswap(Register rd, Register rt) {
318 CHECK(IsR6());
319 EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x0, 0x20);
320}
321
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200322void MipsAssembler::Sll(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700323 CHECK(IsUint<5>(shamt)) << shamt;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200324 EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x00);
jeffhao7fbee072012-08-24 17:56:54 -0700325}
326
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200327void MipsAssembler::Srl(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700328 CHECK(IsUint<5>(shamt)) << shamt;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200329 EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x02);
330}
331
Chris Larsen3f8bf652015-10-28 10:08:56 -0700332void MipsAssembler::Rotr(Register rd, Register rt, int shamt) {
333 CHECK(IsUint<5>(shamt)) << shamt;
334 EmitR(0, static_cast<Register>(1), rt, rd, shamt, 0x02);
335}
336
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200337void MipsAssembler::Sra(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700338 CHECK(IsUint<5>(shamt)) << shamt;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200339 EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x03);
340}
341
342void MipsAssembler::Sllv(Register rd, Register rt, Register rs) {
jeffhao7fbee072012-08-24 17:56:54 -0700343 EmitR(0, rs, rt, rd, 0, 0x04);
344}
345
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200346void MipsAssembler::Srlv(Register rd, Register rt, Register rs) {
jeffhao7fbee072012-08-24 17:56:54 -0700347 EmitR(0, rs, rt, rd, 0, 0x06);
348}
349
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200350void MipsAssembler::Srav(Register rd, Register rt, Register rs) {
jeffhao7fbee072012-08-24 17:56:54 -0700351 EmitR(0, rs, rt, rd, 0, 0x07);
352}
353
354void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) {
355 EmitI(0x20, rs, rt, imm16);
356}
357
358void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) {
359 EmitI(0x21, rs, rt, imm16);
360}
361
362void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) {
363 EmitI(0x23, rs, rt, imm16);
364}
365
366void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) {
367 EmitI(0x24, rs, rt, imm16);
368}
369
370void MipsAssembler::Lhu(Register rt, Register rs, uint16_t imm16) {
371 EmitI(0x25, rs, rt, imm16);
372}
373
374void MipsAssembler::Lui(Register rt, uint16_t imm16) {
375 EmitI(0xf, static_cast<Register>(0), rt, imm16);
376}
377
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200378void MipsAssembler::Sync(uint32_t stype) {
379 EmitR(0, static_cast<Register>(0), static_cast<Register>(0), static_cast<Register>(0),
380 stype & 0x1f, 0xf);
381}
382
jeffhao7fbee072012-08-24 17:56:54 -0700383void MipsAssembler::Mfhi(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200384 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700385 EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rd, 0, 0x10);
386}
387
388void MipsAssembler::Mflo(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200389 CHECK(!IsR6());
jeffhao7fbee072012-08-24 17:56:54 -0700390 EmitR(0, static_cast<Register>(0), static_cast<Register>(0), rd, 0, 0x12);
391}
392
393void MipsAssembler::Sb(Register rt, Register rs, uint16_t imm16) {
394 EmitI(0x28, rs, rt, imm16);
395}
396
397void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) {
398 EmitI(0x29, rs, rt, imm16);
399}
400
401void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) {
402 EmitI(0x2b, rs, rt, imm16);
403}
404
405void MipsAssembler::Slt(Register rd, Register rs, Register rt) {
406 EmitR(0, rs, rt, rd, 0, 0x2a);
407}
408
409void MipsAssembler::Sltu(Register rd, Register rs, Register rt) {
410 EmitR(0, rs, rt, rd, 0, 0x2b);
411}
412
413void MipsAssembler::Slti(Register rt, Register rs, uint16_t imm16) {
414 EmitI(0xa, rs, rt, imm16);
415}
416
417void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) {
418 EmitI(0xb, rs, rt, imm16);
419}
420
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200421void MipsAssembler::B(uint16_t imm16) {
422 EmitI(0x4, static_cast<Register>(0), static_cast<Register>(0), imm16);
423}
424
425void MipsAssembler::Beq(Register rs, Register rt, uint16_t imm16) {
jeffhao7fbee072012-08-24 17:56:54 -0700426 EmitI(0x4, rs, rt, imm16);
427}
428
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200429void MipsAssembler::Bne(Register rs, Register rt, uint16_t imm16) {
jeffhao7fbee072012-08-24 17:56:54 -0700430 EmitI(0x5, rs, rt, imm16);
431}
432
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200433void MipsAssembler::Beqz(Register rt, uint16_t imm16) {
434 Beq(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700435}
436
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200437void MipsAssembler::Bnez(Register rt, uint16_t imm16) {
438 Bne(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700439}
440
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200441void MipsAssembler::Bltz(Register rt, uint16_t imm16) {
442 EmitI(0x1, rt, static_cast<Register>(0), imm16);
443}
444
445void MipsAssembler::Bgez(Register rt, uint16_t imm16) {
446 EmitI(0x1, rt, static_cast<Register>(0x1), imm16);
447}
448
449void MipsAssembler::Blez(Register rt, uint16_t imm16) {
450 EmitI(0x6, rt, static_cast<Register>(0), imm16);
451}
452
453void MipsAssembler::Bgtz(Register rt, uint16_t imm16) {
454 EmitI(0x7, rt, static_cast<Register>(0), imm16);
455}
456
457void MipsAssembler::J(uint32_t addr26) {
458 EmitI26(0x2, addr26);
459}
460
461void MipsAssembler::Jal(uint32_t addr26) {
462 EmitI26(0x3, addr26);
463}
464
465void MipsAssembler::Jalr(Register rd, Register rs) {
466 EmitR(0, rs, static_cast<Register>(0), rd, 0, 0x09);
jeffhao7fbee072012-08-24 17:56:54 -0700467}
468
469void MipsAssembler::Jalr(Register rs) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200470 Jalr(RA, rs);
471}
472
473void MipsAssembler::Jr(Register rs) {
474 Jalr(ZERO, rs);
475}
476
477void MipsAssembler::Nal() {
478 EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x10), 0);
479}
480
481void MipsAssembler::Auipc(Register rs, uint16_t imm16) {
482 CHECK(IsR6());
483 EmitI(0x3B, rs, static_cast<Register>(0x1E), imm16);
484}
485
486void MipsAssembler::Addiupc(Register rs, uint32_t imm19) {
487 CHECK(IsR6());
488 CHECK(IsUint<19>(imm19)) << imm19;
489 EmitI21(0x3B, rs, imm19);
490}
491
492void MipsAssembler::Bc(uint32_t imm26) {
493 CHECK(IsR6());
494 EmitI26(0x32, imm26);
495}
496
497void MipsAssembler::Jic(Register rt, uint16_t imm16) {
498 CHECK(IsR6());
499 EmitI(0x36, static_cast<Register>(0), rt, imm16);
500}
501
502void MipsAssembler::Jialc(Register rt, uint16_t imm16) {
503 CHECK(IsR6());
504 EmitI(0x3E, static_cast<Register>(0), rt, imm16);
505}
506
507void MipsAssembler::Bltc(Register rs, Register rt, uint16_t imm16) {
508 CHECK(IsR6());
509 CHECK_NE(rs, ZERO);
510 CHECK_NE(rt, ZERO);
511 CHECK_NE(rs, rt);
512 EmitI(0x17, rs, rt, imm16);
513}
514
515void MipsAssembler::Bltzc(Register rt, uint16_t imm16) {
516 CHECK(IsR6());
517 CHECK_NE(rt, ZERO);
518 EmitI(0x17, rt, rt, imm16);
519}
520
521void MipsAssembler::Bgtzc(Register rt, uint16_t imm16) {
522 CHECK(IsR6());
523 CHECK_NE(rt, ZERO);
524 EmitI(0x17, static_cast<Register>(0), rt, imm16);
525}
526
527void MipsAssembler::Bgec(Register rs, Register rt, uint16_t imm16) {
528 CHECK(IsR6());
529 CHECK_NE(rs, ZERO);
530 CHECK_NE(rt, ZERO);
531 CHECK_NE(rs, rt);
532 EmitI(0x16, rs, rt, imm16);
533}
534
535void MipsAssembler::Bgezc(Register rt, uint16_t imm16) {
536 CHECK(IsR6());
537 CHECK_NE(rt, ZERO);
538 EmitI(0x16, rt, rt, imm16);
539}
540
541void MipsAssembler::Blezc(Register rt, uint16_t imm16) {
542 CHECK(IsR6());
543 CHECK_NE(rt, ZERO);
544 EmitI(0x16, static_cast<Register>(0), rt, imm16);
545}
546
547void MipsAssembler::Bltuc(Register rs, Register rt, uint16_t imm16) {
548 CHECK(IsR6());
549 CHECK_NE(rs, ZERO);
550 CHECK_NE(rt, ZERO);
551 CHECK_NE(rs, rt);
552 EmitI(0x7, rs, rt, imm16);
553}
554
555void MipsAssembler::Bgeuc(Register rs, Register rt, uint16_t imm16) {
556 CHECK(IsR6());
557 CHECK_NE(rs, ZERO);
558 CHECK_NE(rt, ZERO);
559 CHECK_NE(rs, rt);
560 EmitI(0x6, rs, rt, imm16);
561}
562
563void MipsAssembler::Beqc(Register rs, Register rt, uint16_t imm16) {
564 CHECK(IsR6());
565 CHECK_NE(rs, ZERO);
566 CHECK_NE(rt, ZERO);
567 CHECK_NE(rs, rt);
568 EmitI(0x8, std::min(rs, rt), std::max(rs, rt), imm16);
569}
570
571void MipsAssembler::Bnec(Register rs, Register rt, uint16_t imm16) {
572 CHECK(IsR6());
573 CHECK_NE(rs, ZERO);
574 CHECK_NE(rt, ZERO);
575 CHECK_NE(rs, rt);
576 EmitI(0x18, std::min(rs, rt), std::max(rs, rt), imm16);
577}
578
579void MipsAssembler::Beqzc(Register rs, uint32_t imm21) {
580 CHECK(IsR6());
581 CHECK_NE(rs, ZERO);
582 EmitI21(0x36, rs, imm21);
583}
584
585void MipsAssembler::Bnezc(Register rs, uint32_t imm21) {
586 CHECK(IsR6());
587 CHECK_NE(rs, ZERO);
588 EmitI21(0x3E, rs, imm21);
589}
590
591void MipsAssembler::EmitBcond(BranchCondition cond, Register rs, Register rt, uint16_t imm16) {
592 switch (cond) {
593 case kCondLTZ:
594 CHECK_EQ(rt, ZERO);
595 Bltz(rs, imm16);
596 break;
597 case kCondGEZ:
598 CHECK_EQ(rt, ZERO);
599 Bgez(rs, imm16);
600 break;
601 case kCondLEZ:
602 CHECK_EQ(rt, ZERO);
603 Blez(rs, imm16);
604 break;
605 case kCondGTZ:
606 CHECK_EQ(rt, ZERO);
607 Bgtz(rs, imm16);
608 break;
609 case kCondEQ:
610 Beq(rs, rt, imm16);
611 break;
612 case kCondNE:
613 Bne(rs, rt, imm16);
614 break;
615 case kCondEQZ:
616 CHECK_EQ(rt, ZERO);
617 Beqz(rs, imm16);
618 break;
619 case kCondNEZ:
620 CHECK_EQ(rt, ZERO);
621 Bnez(rs, imm16);
622 break;
623 case kCondLT:
624 case kCondGE:
625 case kCondLE:
626 case kCondGT:
627 case kCondLTU:
628 case kCondGEU:
629 case kUncond:
630 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
631 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
632 LOG(FATAL) << "Unexpected branch condition " << cond;
633 UNREACHABLE();
634 }
635}
636
637void MipsAssembler::EmitBcondc(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21) {
638 switch (cond) {
639 case kCondLT:
640 Bltc(rs, rt, imm16_21);
641 break;
642 case kCondGE:
643 Bgec(rs, rt, imm16_21);
644 break;
645 case kCondLE:
646 Bgec(rt, rs, imm16_21);
647 break;
648 case kCondGT:
649 Bltc(rt, rs, imm16_21);
650 break;
651 case kCondLTZ:
652 CHECK_EQ(rt, ZERO);
653 Bltzc(rs, imm16_21);
654 break;
655 case kCondGEZ:
656 CHECK_EQ(rt, ZERO);
657 Bgezc(rs, imm16_21);
658 break;
659 case kCondLEZ:
660 CHECK_EQ(rt, ZERO);
661 Blezc(rs, imm16_21);
662 break;
663 case kCondGTZ:
664 CHECK_EQ(rt, ZERO);
665 Bgtzc(rs, imm16_21);
666 break;
667 case kCondEQ:
668 Beqc(rs, rt, imm16_21);
669 break;
670 case kCondNE:
671 Bnec(rs, rt, imm16_21);
672 break;
673 case kCondEQZ:
674 CHECK_EQ(rt, ZERO);
675 Beqzc(rs, imm16_21);
676 break;
677 case kCondNEZ:
678 CHECK_EQ(rt, ZERO);
679 Bnezc(rs, imm16_21);
680 break;
681 case kCondLTU:
682 Bltuc(rs, rt, imm16_21);
683 break;
684 case kCondGEU:
685 Bgeuc(rs, rt, imm16_21);
686 break;
687 case kUncond:
688 LOG(FATAL) << "Unexpected branch condition " << cond;
689 UNREACHABLE();
690 }
jeffhao7fbee072012-08-24 17:56:54 -0700691}
692
693void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) {
694 EmitFR(0x11, 0x10, ft, fs, fd, 0x0);
695}
696
697void MipsAssembler::SubS(FRegister fd, FRegister fs, FRegister ft) {
698 EmitFR(0x11, 0x10, ft, fs, fd, 0x1);
699}
700
701void MipsAssembler::MulS(FRegister fd, FRegister fs, FRegister ft) {
702 EmitFR(0x11, 0x10, ft, fs, fd, 0x2);
703}
704
705void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) {
706 EmitFR(0x11, 0x10, ft, fs, fd, 0x3);
707}
708
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200709void MipsAssembler::AddD(FRegister fd, FRegister fs, FRegister ft) {
710 EmitFR(0x11, 0x11, ft, fs, fd, 0x0);
jeffhao7fbee072012-08-24 17:56:54 -0700711}
712
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200713void MipsAssembler::SubD(FRegister fd, FRegister fs, FRegister ft) {
714 EmitFR(0x11, 0x11, ft, fs, fd, 0x1);
jeffhao7fbee072012-08-24 17:56:54 -0700715}
716
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200717void MipsAssembler::MulD(FRegister fd, FRegister fs, FRegister ft) {
718 EmitFR(0x11, 0x11, ft, fs, fd, 0x2);
jeffhao7fbee072012-08-24 17:56:54 -0700719}
720
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200721void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) {
722 EmitFR(0x11, 0x11, ft, fs, fd, 0x3);
jeffhao7fbee072012-08-24 17:56:54 -0700723}
724
725void MipsAssembler::MovS(FRegister fd, FRegister fs) {
726 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6);
727}
728
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200729void MipsAssembler::MovD(FRegister fd, FRegister fs) {
730 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x6);
731}
732
733void MipsAssembler::NegS(FRegister fd, FRegister fs) {
734 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x7);
735}
736
737void MipsAssembler::NegD(FRegister fd, FRegister fs) {
738 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7);
739}
740
741void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) {
742 EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x20);
743}
744
745void MipsAssembler::Cvtdw(FRegister fd, FRegister fs) {
746 EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x21);
747}
748
749void MipsAssembler::Cvtsd(FRegister fd, FRegister fs) {
750 EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x20);
751}
752
753void MipsAssembler::Cvtds(FRegister fd, FRegister fs) {
754 EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x21);
jeffhao7fbee072012-08-24 17:56:54 -0700755}
756
757void MipsAssembler::Mfc1(Register rt, FRegister fs) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200758 EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
jeffhao7fbee072012-08-24 17:56:54 -0700759}
760
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200761void MipsAssembler::Mtc1(Register rt, FRegister fs) {
762 EmitFR(0x11, 0x04, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
763}
764
765void MipsAssembler::Mfhc1(Register rt, FRegister fs) {
766 EmitFR(0x11, 0x03, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
767}
768
769void MipsAssembler::Mthc1(Register rt, FRegister fs) {
770 EmitFR(0x11, 0x07, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0);
jeffhao7fbee072012-08-24 17:56:54 -0700771}
772
773void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200774 EmitI(0x31, rs, static_cast<Register>(ft), imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700775}
776
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200777void MipsAssembler::Ldc1(FRegister ft, Register rs, uint16_t imm16) {
778 EmitI(0x35, rs, static_cast<Register>(ft), imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700779}
780
781void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200782 EmitI(0x39, rs, static_cast<Register>(ft), imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700783}
784
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200785void MipsAssembler::Sdc1(FRegister ft, Register rs, uint16_t imm16) {
786 EmitI(0x3d, rs, static_cast<Register>(ft), imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700787}
788
789void MipsAssembler::Break() {
790 EmitR(0, static_cast<Register>(0), static_cast<Register>(0),
791 static_cast<Register>(0), 0, 0xD);
792}
793
jeffhao07030602012-09-26 14:33:14 -0700794void MipsAssembler::Nop() {
795 EmitR(0x0, static_cast<Register>(0), static_cast<Register>(0), static_cast<Register>(0), 0, 0x0);
796}
797
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200798void MipsAssembler::Move(Register rd, Register rs) {
799 Or(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700800}
801
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200802void MipsAssembler::Clear(Register rd) {
803 Move(rd, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700804}
805
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200806void MipsAssembler::Not(Register rd, Register rs) {
807 Nor(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700808}
809
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200810void MipsAssembler::Push(Register rs) {
811 IncreaseFrameSize(kMipsWordSize);
812 Sw(rs, SP, 0);
jeffhao7fbee072012-08-24 17:56:54 -0700813}
814
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200815void MipsAssembler::Pop(Register rd) {
816 Lw(rd, SP, 0);
817 DecreaseFrameSize(kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -0700818}
819
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200820void MipsAssembler::PopAndReturn(Register rd, Register rt) {
821 Lw(rd, SP, 0);
822 Jr(rt);
823 DecreaseFrameSize(kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -0700824}
825
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200826void MipsAssembler::LoadConst32(Register rd, int32_t value) {
827 if (IsUint<16>(value)) {
828 // Use OR with (unsigned) immediate to encode 16b unsigned int.
829 Ori(rd, ZERO, value);
830 } else if (IsInt<16>(value)) {
831 // Use ADD with (signed) immediate to encode 16b signed int.
832 Addiu(rd, ZERO, value);
jeffhao7fbee072012-08-24 17:56:54 -0700833 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200834 Lui(rd, High16Bits(value));
835 if (value & 0xFFFF)
836 Ori(rd, rd, Low16Bits(value));
837 }
838}
839
840void MipsAssembler::LoadConst64(Register reg_hi, Register reg_lo, int64_t value) {
841 LoadConst32(reg_lo, Low32Bits(value));
842 LoadConst32(reg_hi, High32Bits(value));
843}
844
845void MipsAssembler::StoreConst32ToOffset(int32_t value,
846 Register base,
847 int32_t offset,
848 Register temp) {
849 if (!IsInt<16>(offset)) {
850 CHECK_NE(temp, AT); // Must not use AT as temp, as not to overwrite the loaded value.
851 LoadConst32(AT, offset);
852 Addu(AT, AT, base);
853 base = AT;
854 offset = 0;
855 }
856 LoadConst32(temp, value);
857 Sw(temp, base, offset);
858}
859
860void MipsAssembler::StoreConst64ToOffset(int64_t value,
861 Register base,
862 int32_t offset,
863 Register temp) {
864 // IsInt<16> must be passed a signed value.
865 if (!IsInt<16>(offset) || !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize))) {
866 CHECK_NE(temp, AT); // Must not use AT as temp, as not to overwrite the loaded value.
867 LoadConst32(AT, offset);
868 Addu(AT, AT, base);
869 base = AT;
870 offset = 0;
871 }
872 LoadConst32(temp, Low32Bits(value));
873 Sw(temp, base, offset);
874 LoadConst32(temp, High32Bits(value));
875 Sw(temp, base, offset + kMipsWordSize);
876}
877
878void MipsAssembler::LoadSConst32(FRegister r, int32_t value, Register temp) {
879 LoadConst32(temp, value);
880 Mtc1(temp, r);
881}
882
883void MipsAssembler::LoadDConst64(FRegister rd, int64_t value, Register temp) {
884 LoadConst32(temp, Low32Bits(value));
885 Mtc1(temp, rd);
886 LoadConst32(temp, High32Bits(value));
887 Mthc1(temp, rd);
888}
889
890void MipsAssembler::Addiu32(Register rt, Register rs, int32_t value, Register temp) {
891 if (IsInt<16>(value)) {
892 Addiu(rt, rs, value);
893 } else {
894 LoadConst32(temp, value);
895 Addu(rt, rs, temp);
896 }
897}
898
899void MipsAssembler::Branch::InitShortOrLong(MipsAssembler::Branch::OffsetBits offset_size,
900 MipsAssembler::Branch::Type short_type,
901 MipsAssembler::Branch::Type long_type) {
902 type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type;
903}
904
905void MipsAssembler::Branch::InitializeType(bool is_call, bool is_r6) {
906 OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_);
907 if (is_r6) {
908 // R6
909 if (is_call) {
910 InitShortOrLong(offset_size, kR6Call, kR6LongCall);
911 } else if (condition_ == kUncond) {
912 InitShortOrLong(offset_size, kR6UncondBranch, kR6LongUncondBranch);
913 } else {
914 if (condition_ == kCondEQZ || condition_ == kCondNEZ) {
915 // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions.
916 type_ = (offset_size <= kOffset23) ? kR6CondBranch : kR6LongCondBranch;
917 } else {
918 InitShortOrLong(offset_size, kR6CondBranch, kR6LongCondBranch);
919 }
920 }
921 } else {
922 // R2
923 if (is_call) {
924 InitShortOrLong(offset_size, kCall, kLongCall);
925 } else if (condition_ == kUncond) {
926 InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch);
927 } else {
928 InitShortOrLong(offset_size, kCondBranch, kLongCondBranch);
929 }
930 }
931 old_type_ = type_;
932}
933
934bool MipsAssembler::Branch::IsNop(BranchCondition condition, Register lhs, Register rhs) {
935 switch (condition) {
936 case kCondLT:
937 case kCondGT:
938 case kCondNE:
939 case kCondLTU:
940 return lhs == rhs;
941 default:
942 return false;
943 }
944}
945
946bool MipsAssembler::Branch::IsUncond(BranchCondition condition, Register lhs, Register rhs) {
947 switch (condition) {
948 case kUncond:
949 return true;
950 case kCondGE:
951 case kCondLE:
952 case kCondEQ:
953 case kCondGEU:
954 return lhs == rhs;
955 default:
956 return false;
957 }
958}
959
960MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target)
961 : old_location_(location),
962 location_(location),
963 target_(target),
964 lhs_reg_(0),
965 rhs_reg_(0),
966 condition_(kUncond) {
967 InitializeType(false, is_r6);
968}
969
970MipsAssembler::Branch::Branch(bool is_r6,
971 uint32_t location,
972 uint32_t target,
973 MipsAssembler::BranchCondition condition,
974 Register lhs_reg,
975 Register rhs_reg)
976 : old_location_(location),
977 location_(location),
978 target_(target),
979 lhs_reg_(lhs_reg),
980 rhs_reg_(rhs_reg),
981 condition_(condition) {
982 CHECK_NE(condition, kUncond);
983 switch (condition) {
984 case kCondLT:
985 case kCondGE:
986 case kCondLE:
987 case kCondGT:
988 case kCondLTU:
989 case kCondGEU:
990 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
991 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
992 // We leave this up to the caller.
993 CHECK(is_r6);
994 FALLTHROUGH_INTENDED;
995 case kCondEQ:
996 case kCondNE:
997 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
998 // To compare with 0, use dedicated kCond*Z conditions.
999 CHECK_NE(lhs_reg, ZERO);
1000 CHECK_NE(rhs_reg, ZERO);
1001 break;
1002 case kCondLTZ:
1003 case kCondGEZ:
1004 case kCondLEZ:
1005 case kCondGTZ:
1006 case kCondEQZ:
1007 case kCondNEZ:
1008 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1009 CHECK_NE(lhs_reg, ZERO);
1010 CHECK_EQ(rhs_reg, ZERO);
1011 break;
1012 case kUncond:
1013 UNREACHABLE();
1014 }
1015 CHECK(!IsNop(condition, lhs_reg, rhs_reg));
1016 if (IsUncond(condition, lhs_reg, rhs_reg)) {
1017 // Branch condition is always true, make the branch unconditional.
1018 condition_ = kUncond;
1019 }
1020 InitializeType(false, is_r6);
1021}
1022
1023MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target, Register indirect_reg)
1024 : old_location_(location),
1025 location_(location),
1026 target_(target),
1027 lhs_reg_(indirect_reg),
1028 rhs_reg_(0),
1029 condition_(kUncond) {
1030 CHECK_NE(indirect_reg, ZERO);
1031 CHECK_NE(indirect_reg, AT);
1032 InitializeType(true, is_r6);
1033}
1034
1035MipsAssembler::BranchCondition MipsAssembler::Branch::OppositeCondition(
1036 MipsAssembler::BranchCondition cond) {
1037 switch (cond) {
1038 case kCondLT:
1039 return kCondGE;
1040 case kCondGE:
1041 return kCondLT;
1042 case kCondLE:
1043 return kCondGT;
1044 case kCondGT:
1045 return kCondLE;
1046 case kCondLTZ:
1047 return kCondGEZ;
1048 case kCondGEZ:
1049 return kCondLTZ;
1050 case kCondLEZ:
1051 return kCondGTZ;
1052 case kCondGTZ:
1053 return kCondLEZ;
1054 case kCondEQ:
1055 return kCondNE;
1056 case kCondNE:
1057 return kCondEQ;
1058 case kCondEQZ:
1059 return kCondNEZ;
1060 case kCondNEZ:
1061 return kCondEQZ;
1062 case kCondLTU:
1063 return kCondGEU;
1064 case kCondGEU:
1065 return kCondLTU;
1066 case kUncond:
1067 LOG(FATAL) << "Unexpected branch condition " << cond;
1068 }
1069 UNREACHABLE();
1070}
1071
1072MipsAssembler::Branch::Type MipsAssembler::Branch::GetType() const {
1073 return type_;
1074}
1075
1076MipsAssembler::BranchCondition MipsAssembler::Branch::GetCondition() const {
1077 return condition_;
1078}
1079
1080Register MipsAssembler::Branch::GetLeftRegister() const {
1081 return static_cast<Register>(lhs_reg_);
1082}
1083
1084Register MipsAssembler::Branch::GetRightRegister() const {
1085 return static_cast<Register>(rhs_reg_);
1086}
1087
1088uint32_t MipsAssembler::Branch::GetTarget() const {
1089 return target_;
1090}
1091
1092uint32_t MipsAssembler::Branch::GetLocation() const {
1093 return location_;
1094}
1095
1096uint32_t MipsAssembler::Branch::GetOldLocation() const {
1097 return old_location_;
1098}
1099
1100uint32_t MipsAssembler::Branch::GetLength() const {
1101 return branch_info_[type_].length;
1102}
1103
1104uint32_t MipsAssembler::Branch::GetOldLength() const {
1105 return branch_info_[old_type_].length;
1106}
1107
1108uint32_t MipsAssembler::Branch::GetSize() const {
1109 return GetLength() * sizeof(uint32_t);
1110}
1111
1112uint32_t MipsAssembler::Branch::GetOldSize() const {
1113 return GetOldLength() * sizeof(uint32_t);
1114}
1115
1116uint32_t MipsAssembler::Branch::GetEndLocation() const {
1117 return GetLocation() + GetSize();
1118}
1119
1120uint32_t MipsAssembler::Branch::GetOldEndLocation() const {
1121 return GetOldLocation() + GetOldSize();
1122}
1123
1124bool MipsAssembler::Branch::IsLong() const {
1125 switch (type_) {
1126 // R2 short branches.
1127 case kUncondBranch:
1128 case kCondBranch:
1129 case kCall:
1130 // R6 short branches.
1131 case kR6UncondBranch:
1132 case kR6CondBranch:
1133 case kR6Call:
1134 return false;
1135 // R2 long branches.
1136 case kLongUncondBranch:
1137 case kLongCondBranch:
1138 case kLongCall:
1139 // R6 long branches.
1140 case kR6LongUncondBranch:
1141 case kR6LongCondBranch:
1142 case kR6LongCall:
1143 return true;
1144 }
1145 UNREACHABLE();
1146}
1147
1148bool MipsAssembler::Branch::IsResolved() const {
1149 return target_ != kUnresolved;
1150}
1151
1152MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSize() const {
1153 OffsetBits offset_size =
1154 (type_ == kR6CondBranch && (condition_ == kCondEQZ || condition_ == kCondNEZ))
1155 ? kOffset23
1156 : branch_info_[type_].offset_size;
1157 return offset_size;
1158}
1159
1160MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSizeNeeded(uint32_t location,
1161 uint32_t target) {
1162 // For unresolved targets assume the shortest encoding
1163 // (later it will be made longer if needed).
1164 if (target == kUnresolved)
1165 return kOffset16;
1166 int64_t distance = static_cast<int64_t>(target) - location;
1167 // To simplify calculations in composite branches consisting of multiple instructions
1168 // bump up the distance by a value larger than the max byte size of a composite branch.
1169 distance += (distance >= 0) ? kMaxBranchSize : -kMaxBranchSize;
1170 if (IsInt<kOffset16>(distance))
1171 return kOffset16;
1172 else if (IsInt<kOffset18>(distance))
1173 return kOffset18;
1174 else if (IsInt<kOffset21>(distance))
1175 return kOffset21;
1176 else if (IsInt<kOffset23>(distance))
1177 return kOffset23;
1178 else if (IsInt<kOffset28>(distance))
1179 return kOffset28;
1180 return kOffset32;
1181}
1182
1183void MipsAssembler::Branch::Resolve(uint32_t target) {
1184 target_ = target;
1185}
1186
1187void MipsAssembler::Branch::Relocate(uint32_t expand_location, uint32_t delta) {
1188 if (location_ > expand_location) {
1189 location_ += delta;
1190 }
1191 if (!IsResolved()) {
1192 return; // Don't know the target yet.
1193 }
1194 if (target_ > expand_location) {
1195 target_ += delta;
1196 }
1197}
1198
1199void MipsAssembler::Branch::PromoteToLong() {
1200 switch (type_) {
1201 // R2 short branches.
1202 case kUncondBranch:
1203 type_ = kLongUncondBranch;
1204 break;
1205 case kCondBranch:
1206 type_ = kLongCondBranch;
1207 break;
1208 case kCall:
1209 type_ = kLongCall;
1210 break;
1211 // R6 short branches.
1212 case kR6UncondBranch:
1213 type_ = kR6LongUncondBranch;
1214 break;
1215 case kR6CondBranch:
1216 type_ = kR6LongCondBranch;
1217 break;
1218 case kR6Call:
1219 type_ = kR6LongCall;
1220 break;
1221 default:
1222 // Note: 'type_' is already long.
1223 break;
1224 }
1225 CHECK(IsLong());
1226}
1227
1228uint32_t MipsAssembler::Branch::PromoteIfNeeded(uint32_t max_short_distance) {
1229 // If the branch is still unresolved or already long, nothing to do.
1230 if (IsLong() || !IsResolved()) {
1231 return 0;
1232 }
1233 // Promote the short branch to long if the offset size is too small
1234 // to hold the distance between location_ and target_.
1235 if (GetOffsetSizeNeeded(location_, target_) > GetOffsetSize()) {
1236 PromoteToLong();
1237 uint32_t old_size = GetOldSize();
1238 uint32_t new_size = GetSize();
1239 CHECK_GT(new_size, old_size);
1240 return new_size - old_size;
1241 }
1242 // The following logic is for debugging/testing purposes.
1243 // Promote some short branches to long when it's not really required.
1244 if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max())) {
1245 int64_t distance = static_cast<int64_t>(target_) - location_;
1246 distance = (distance >= 0) ? distance : -distance;
1247 if (distance >= max_short_distance) {
1248 PromoteToLong();
1249 uint32_t old_size = GetOldSize();
1250 uint32_t new_size = GetSize();
1251 CHECK_GT(new_size, old_size);
1252 return new_size - old_size;
1253 }
1254 }
1255 return 0;
1256}
1257
1258uint32_t MipsAssembler::Branch::GetOffsetLocation() const {
1259 return location_ + branch_info_[type_].instr_offset * sizeof(uint32_t);
1260}
1261
1262uint32_t MipsAssembler::Branch::GetOffset() const {
1263 CHECK(IsResolved());
1264 uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize());
1265 // Calculate the byte distance between instructions and also account for
1266 // different PC-relative origins.
1267 uint32_t offset = target_ - GetOffsetLocation() - branch_info_[type_].pc_org * sizeof(uint32_t);
1268 // Prepare the offset for encoding into the instruction(s).
1269 offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift;
1270 return offset;
1271}
1272
1273MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) {
1274 CHECK_LT(branch_id, branches_.size());
1275 return &branches_[branch_id];
1276}
1277
1278const MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) const {
1279 CHECK_LT(branch_id, branches_.size());
1280 return &branches_[branch_id];
1281}
1282
1283void MipsAssembler::Bind(MipsLabel* label) {
1284 CHECK(!label->IsBound());
1285 uint32_t bound_pc = buffer_.Size();
1286
1287 // Walk the list of branches referring to and preceding this label.
1288 // Store the previously unknown target addresses in them.
1289 while (label->IsLinked()) {
1290 uint32_t branch_id = label->Position();
1291 Branch* branch = GetBranch(branch_id);
1292 branch->Resolve(bound_pc);
1293
1294 uint32_t branch_location = branch->GetLocation();
1295 // Extract the location of the previous branch in the list (walking the list backwards;
1296 // the previous branch ID was stored in the space reserved for this branch).
1297 uint32_t prev = buffer_.Load<uint32_t>(branch_location);
1298
1299 // On to the previous branch in the list...
1300 label->position_ = prev;
1301 }
1302
1303 // Now make the label object contain its own location (relative to the end of the preceding
1304 // branch, if any; it will be used by the branches referring to and following this label).
1305 label->prev_branch_id_plus_one_ = branches_.size();
1306 if (label->prev_branch_id_plus_one_) {
1307 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
1308 const Branch* branch = GetBranch(branch_id);
1309 bound_pc -= branch->GetEndLocation();
1310 }
1311 label->BindTo(bound_pc);
1312}
1313
1314uint32_t MipsAssembler::GetLabelLocation(MipsLabel* label) const {
1315 CHECK(label->IsBound());
1316 uint32_t target = label->Position();
1317 if (label->prev_branch_id_plus_one_) {
1318 // Get label location based on the branch preceding it.
1319 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
1320 const Branch* branch = GetBranch(branch_id);
1321 target += branch->GetEndLocation();
1322 }
1323 return target;
1324}
1325
1326uint32_t MipsAssembler::GetAdjustedPosition(uint32_t old_position) {
1327 // We can reconstruct the adjustment by going through all the branches from the beginning
1328 // up to the old_position. Since we expect AdjustedPosition() to be called in a loop
1329 // with increasing old_position, we can use the data from last AdjustedPosition() to
1330 // continue where we left off and the whole loop should be O(m+n) where m is the number
1331 // of positions to adjust and n is the number of branches.
1332 if (old_position < last_old_position_) {
1333 last_position_adjustment_ = 0;
1334 last_old_position_ = 0;
1335 last_branch_id_ = 0;
1336 }
1337 while (last_branch_id_ != branches_.size()) {
1338 const Branch* branch = GetBranch(last_branch_id_);
1339 if (branch->GetLocation() >= old_position + last_position_adjustment_) {
1340 break;
1341 }
1342 last_position_adjustment_ += branch->GetSize() - branch->GetOldSize();
1343 ++last_branch_id_;
1344 }
1345 last_old_position_ = old_position;
1346 return old_position + last_position_adjustment_;
1347}
1348
1349void MipsAssembler::FinalizeLabeledBranch(MipsLabel* label) {
1350 uint32_t length = branches_.back().GetLength();
1351 if (!label->IsBound()) {
1352 // Branch forward (to a following label), distance is unknown.
1353 // The first branch forward will contain 0, serving as the terminator of
1354 // the list of forward-reaching branches.
1355 Emit(label->position_);
1356 length--;
1357 // Now make the label object point to this branch
1358 // (this forms a linked list of branches preceding this label).
1359 uint32_t branch_id = branches_.size() - 1;
1360 label->LinkTo(branch_id);
1361 }
1362 // Reserve space for the branch.
1363 while (length--) {
1364 Nop();
1365 }
1366}
1367
1368void MipsAssembler::Buncond(MipsLabel* label) {
1369 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
1370 branches_.emplace_back(IsR6(), buffer_.Size(), target);
1371 FinalizeLabeledBranch(label);
1372}
1373
1374void MipsAssembler::Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs) {
1375 // If lhs = rhs, this can be a NOP.
1376 if (Branch::IsNop(condition, lhs, rhs)) {
1377 return;
1378 }
1379 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
1380 branches_.emplace_back(IsR6(), buffer_.Size(), target, condition, lhs, rhs);
1381 FinalizeLabeledBranch(label);
1382}
1383
1384void MipsAssembler::Call(MipsLabel* label, Register indirect_reg) {
1385 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
1386 branches_.emplace_back(IsR6(), buffer_.Size(), target, indirect_reg);
1387 FinalizeLabeledBranch(label);
1388}
1389
1390void MipsAssembler::PromoteBranches() {
1391 // Promote short branches to long as necessary.
1392 bool changed;
1393 do {
1394 changed = false;
1395 for (auto& branch : branches_) {
1396 CHECK(branch.IsResolved());
1397 uint32_t delta = branch.PromoteIfNeeded();
1398 // If this branch has been promoted and needs to expand in size,
1399 // relocate all branches by the expansion size.
1400 if (delta) {
1401 changed = true;
1402 uint32_t expand_location = branch.GetLocation();
1403 for (auto& branch2 : branches_) {
1404 branch2.Relocate(expand_location, delta);
1405 }
1406 }
1407 }
1408 } while (changed);
1409
1410 // Account for branch expansion by resizing the code buffer
1411 // and moving the code in it to its final location.
1412 size_t branch_count = branches_.size();
1413 if (branch_count > 0) {
1414 // Resize.
1415 Branch& last_branch = branches_[branch_count - 1];
1416 uint32_t size_delta = last_branch.GetEndLocation() - last_branch.GetOldEndLocation();
1417 uint32_t old_size = buffer_.Size();
1418 buffer_.Resize(old_size + size_delta);
1419 // Move the code residing between branch placeholders.
1420 uint32_t end = old_size;
1421 for (size_t i = branch_count; i > 0; ) {
1422 Branch& branch = branches_[--i];
1423 uint32_t size = end - branch.GetOldEndLocation();
1424 buffer_.Move(branch.GetEndLocation(), branch.GetOldEndLocation(), size);
1425 end = branch.GetOldLocation();
1426 }
1427 }
1428}
1429
1430// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
1431const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = {
1432 // R2 short branches.
1433 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kUncondBranch
1434 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCondBranch
1435 { 5, 2, 0, MipsAssembler::Branch::kOffset16, 0 }, // kCall
1436 // R2 long branches.
1437 { 9, 3, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongUncondBranch
1438 { 10, 4, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCondBranch
1439 { 6, 1, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCall
1440 // R6 short branches.
1441 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6UncondBranch
1442 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kR6CondBranch
1443 // Exception: kOffset23 for beqzc/bnezc.
1444 { 2, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Call
1445 // R6 long branches.
1446 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongUncondBranch
1447 { 3, 1, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCondBranch
1448 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCall
1449};
1450
1451// Note: make sure branch_info_[] and mitBranch() are kept synchronized.
1452void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
1453 CHECK_EQ(overwriting_, true);
1454 overwrite_location_ = branch->GetLocation();
1455 uint32_t offset = branch->GetOffset();
1456 BranchCondition condition = branch->GetCondition();
1457 Register lhs = branch->GetLeftRegister();
1458 Register rhs = branch->GetRightRegister();
1459 switch (branch->GetType()) {
1460 // R2 short branches.
1461 case Branch::kUncondBranch:
1462 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
1463 B(offset);
1464 Nop(); // TODO: improve by filling the delay slot.
1465 break;
1466 case Branch::kCondBranch:
1467 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
1468 EmitBcond(condition, lhs, rhs, offset);
1469 Nop(); // TODO: improve by filling the delay slot.
1470 break;
1471 case Branch::kCall:
1472 Nal();
1473 Nop(); // TODO: is this NOP really needed here?
1474 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
1475 Addiu(lhs, RA, offset);
1476 Jalr(lhs);
1477 Nop();
1478 break;
1479
1480 // R2 long branches.
1481 case Branch::kLongUncondBranch:
1482 // To get the value of the PC register we need to use the NAL instruction.
1483 // NAL clobbers the RA register. However, RA must be preserved if the
1484 // method is compiled without the entry/exit sequences that would take care
1485 // of preserving RA (typically, leaf methods don't preserve RA explicitly).
1486 // So, we need to preserve RA in some temporary storage ourselves. The AT
1487 // register can't be used for this because we need it to load a constant
1488 // which will be added to the value that NAL stores in RA. And we can't
1489 // use T9 for this in the context of the JNI compiler, which uses it
1490 // as a scratch register (see InterproceduralScratchRegister()).
1491 // If we were to add a 32-bit constant to RA using two ADDIU instructions,
1492 // we'd also need to use the ROTR instruction, which requires no less than
1493 // MIPSR2.
1494 // Perhaps, we could use T8 or one of R2's multiplier/divider registers
1495 // (LO or HI) or even a floating-point register, but that doesn't seem
1496 // like a nice solution. We may want this to work on both R6 and pre-R6.
1497 // For now simply use the stack for RA. This should be OK since for the
1498 // vast majority of code a short PC-relative branch is sufficient.
1499 // TODO: can this be improved?
1500 Push(RA);
1501 Nal();
1502 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
1503 Lui(AT, High16Bits(offset));
1504 Ori(AT, AT, Low16Bits(offset));
1505 Addu(AT, AT, RA);
1506 Lw(RA, SP, 0);
1507 Jr(AT);
1508 DecreaseFrameSize(kMipsWordSize);
1509 break;
1510 case Branch::kLongCondBranch:
1511 // The comment on case 'Branch::kLongUncondBranch' applies here as well.
1512 // Note: the opposite condition branch encodes 8 as the distance, which is equal to the
1513 // number of instructions skipped:
1514 // (PUSH(IncreaseFrameSize(ADDIU) + SW) + NAL + LUI + ORI + ADDU + LW + JR).
1515 EmitBcond(Branch::OppositeCondition(condition), lhs, rhs, 8);
1516 Push(RA);
1517 Nal();
1518 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
1519 Lui(AT, High16Bits(offset));
1520 Ori(AT, AT, Low16Bits(offset));
1521 Addu(AT, AT, RA);
1522 Lw(RA, SP, 0);
1523 Jr(AT);
1524 DecreaseFrameSize(kMipsWordSize);
1525 break;
1526 case Branch::kLongCall:
1527 Nal();
1528 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
1529 Lui(AT, High16Bits(offset));
1530 Ori(AT, AT, Low16Bits(offset));
1531 Addu(lhs, AT, RA);
1532 Jalr(lhs);
1533 Nop();
1534 break;
1535
1536 // R6 short branches.
1537 case Branch::kR6UncondBranch:
1538 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
1539 Bc(offset);
1540 break;
1541 case Branch::kR6CondBranch:
1542 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
1543 EmitBcondc(condition, lhs, rhs, offset);
1544 Nop(); // TODO: improve by filling the forbidden slot.
1545 break;
1546 case Branch::kR6Call:
1547 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
1548 Addiupc(lhs, offset);
1549 Jialc(lhs, 0);
1550 break;
1551
1552 // R6 long branches.
1553 case Branch::kR6LongUncondBranch:
1554 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
1555 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
1556 Auipc(AT, High16Bits(offset));
1557 Jic(AT, Low16Bits(offset));
1558 break;
1559 case Branch::kR6LongCondBranch:
1560 EmitBcondc(Branch::OppositeCondition(condition), lhs, rhs, 2);
1561 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
1562 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
1563 Auipc(AT, High16Bits(offset));
1564 Jic(AT, Low16Bits(offset));
1565 break;
1566 case Branch::kR6LongCall:
1567 offset += (offset & 0x8000) << 1; // Account for sign extension in addiu.
1568 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
1569 Auipc(lhs, High16Bits(offset));
1570 Addiu(lhs, lhs, Low16Bits(offset));
1571 Jialc(lhs, 0);
1572 break;
1573 }
1574 CHECK_EQ(overwrite_location_, branch->GetEndLocation());
1575 CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize));
1576}
1577
1578void MipsAssembler::B(MipsLabel* label) {
1579 Buncond(label);
1580}
1581
1582void MipsAssembler::Jalr(MipsLabel* label, Register indirect_reg) {
1583 Call(label, indirect_reg);
1584}
1585
1586void MipsAssembler::Beq(Register rs, Register rt, MipsLabel* label) {
1587 Bcond(label, kCondEQ, rs, rt);
1588}
1589
1590void MipsAssembler::Bne(Register rs, Register rt, MipsLabel* label) {
1591 Bcond(label, kCondNE, rs, rt);
1592}
1593
1594void MipsAssembler::Beqz(Register rt, MipsLabel* label) {
1595 Bcond(label, kCondEQZ, rt);
1596}
1597
1598void MipsAssembler::Bnez(Register rt, MipsLabel* label) {
1599 Bcond(label, kCondNEZ, rt);
1600}
1601
1602void MipsAssembler::Bltz(Register rt, MipsLabel* label) {
1603 Bcond(label, kCondLTZ, rt);
1604}
1605
1606void MipsAssembler::Bgez(Register rt, MipsLabel* label) {
1607 Bcond(label, kCondGEZ, rt);
1608}
1609
1610void MipsAssembler::Blez(Register rt, MipsLabel* label) {
1611 Bcond(label, kCondLEZ, rt);
1612}
1613
1614void MipsAssembler::Bgtz(Register rt, MipsLabel* label) {
1615 Bcond(label, kCondGTZ, rt);
1616}
1617
1618void MipsAssembler::Blt(Register rs, Register rt, MipsLabel* label) {
1619 if (IsR6()) {
1620 Bcond(label, kCondLT, rs, rt);
1621 } else if (!Branch::IsNop(kCondLT, rs, rt)) {
1622 // Synthesize the instruction (not available on R2).
1623 Slt(AT, rs, rt);
1624 Bnez(AT, label);
1625 }
1626}
1627
1628void MipsAssembler::Bge(Register rs, Register rt, MipsLabel* label) {
1629 if (IsR6()) {
1630 Bcond(label, kCondGE, rs, rt);
1631 } else if (Branch::IsUncond(kCondGE, rs, rt)) {
1632 B(label);
1633 } else {
1634 // Synthesize the instruction (not available on R2).
1635 Slt(AT, rs, rt);
1636 Beqz(AT, label);
1637 }
1638}
1639
1640void MipsAssembler::Bltu(Register rs, Register rt, MipsLabel* label) {
1641 if (IsR6()) {
1642 Bcond(label, kCondLTU, rs, rt);
1643 } else if (!Branch::IsNop(kCondLTU, rs, rt)) {
1644 // Synthesize the instruction (not available on R2).
1645 Sltu(AT, rs, rt);
1646 Bnez(AT, label);
1647 }
1648}
1649
1650void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) {
1651 if (IsR6()) {
1652 Bcond(label, kCondGEU, rs, rt);
1653 } else if (Branch::IsUncond(kCondGEU, rs, rt)) {
1654 B(label);
1655 } else {
1656 // Synthesize the instruction (not available on R2).
1657 Sltu(AT, rs, rt);
1658 Beqz(AT, label);
jeffhao7fbee072012-08-24 17:56:54 -07001659 }
1660}
1661
1662void MipsAssembler::LoadFromOffset(LoadOperandType type, Register reg, Register base,
1663 int32_t offset) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001664 // IsInt<16> must be passed a signed value.
1665 if (!IsInt<16>(offset) ||
1666 (type == kLoadDoubleword && !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
1667 LoadConst32(AT, offset);
1668 Addu(AT, AT, base);
1669 base = AT;
1670 offset = 0;
1671 }
1672
jeffhao7fbee072012-08-24 17:56:54 -07001673 switch (type) {
1674 case kLoadSignedByte:
1675 Lb(reg, base, offset);
1676 break;
1677 case kLoadUnsignedByte:
1678 Lbu(reg, base, offset);
1679 break;
1680 case kLoadSignedHalfword:
1681 Lh(reg, base, offset);
1682 break;
1683 case kLoadUnsignedHalfword:
1684 Lhu(reg, base, offset);
1685 break;
1686 case kLoadWord:
1687 Lw(reg, base, offset);
1688 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001689 case kLoadDoubleword:
1690 if (reg == base) {
1691 // This will clobber the base when loading the lower register. Since we have to load the
1692 // higher register as well, this will fail. Solution: reverse the order.
1693 Lw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
1694 Lw(reg, base, offset);
1695 } else {
1696 Lw(reg, base, offset);
1697 Lw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
1698 }
jeffhao7fbee072012-08-24 17:56:54 -07001699 break;
1700 default:
1701 LOG(FATAL) << "UNREACHABLE";
1702 }
1703}
1704
1705void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001706 if (!IsInt<16>(offset)) {
1707 LoadConst32(AT, offset);
1708 Addu(AT, AT, base);
1709 base = AT;
1710 offset = 0;
1711 }
1712
jeffhao7fbee072012-08-24 17:56:54 -07001713 Lwc1(reg, base, offset);
1714}
1715
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001716void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset) {
1717 // IsInt<16> must be passed a signed value.
1718 if (!IsInt<16>(offset) ||
1719 (!IsAligned<kMipsDoublewordSize>(offset) &&
1720 !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
1721 LoadConst32(AT, offset);
1722 Addu(AT, AT, base);
1723 base = AT;
1724 offset = 0;
1725 }
1726
1727 if (offset & 0x7) {
1728 if (Is32BitFPU()) {
1729 Lwc1(reg, base, offset);
1730 Lwc1(static_cast<FRegister>(reg + 1), base, offset + kMipsWordSize);
1731 } else {
1732 // 64-bit FPU.
1733 Lwc1(reg, base, offset);
1734 Lw(T8, base, offset + kMipsWordSize);
1735 Mthc1(T8, reg);
1736 }
1737 } else {
1738 Ldc1(reg, base, offset);
1739 }
1740}
1741
1742void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
1743 size_t size) {
1744 MipsManagedRegister dst = m_dst.AsMips();
1745 if (dst.IsNoRegister()) {
1746 CHECK_EQ(0u, size) << dst;
1747 } else if (dst.IsCoreRegister()) {
1748 CHECK_EQ(kMipsWordSize, size) << dst;
1749 LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
1750 } else if (dst.IsRegisterPair()) {
1751 CHECK_EQ(kMipsDoublewordSize, size) << dst;
1752 LoadFromOffset(kLoadDoubleword, dst.AsRegisterPairLow(), src_register, src_offset);
1753 } else if (dst.IsFRegister()) {
1754 if (size == kMipsWordSize) {
1755 LoadSFromOffset(dst.AsFRegister(), src_register, src_offset);
1756 } else {
1757 CHECK_EQ(kMipsDoublewordSize, size) << dst;
1758 LoadDFromOffset(dst.AsFRegister(), src_register, src_offset);
1759 }
1760 }
jeffhao7fbee072012-08-24 17:56:54 -07001761}
1762
1763void MipsAssembler::StoreToOffset(StoreOperandType type, Register reg, Register base,
1764 int32_t offset) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001765 // IsInt<16> must be passed a signed value.
1766 if (!IsInt<16>(offset) ||
1767 (type == kStoreDoubleword && !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
1768 LoadConst32(AT, offset);
1769 Addu(AT, AT, base);
1770 base = AT;
1771 offset = 0;
1772 }
1773
jeffhao7fbee072012-08-24 17:56:54 -07001774 switch (type) {
1775 case kStoreByte:
1776 Sb(reg, base, offset);
1777 break;
1778 case kStoreHalfword:
1779 Sh(reg, base, offset);
1780 break;
1781 case kStoreWord:
1782 Sw(reg, base, offset);
1783 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001784 case kStoreDoubleword:
1785 CHECK_NE(reg, base);
1786 CHECK_NE(static_cast<Register>(reg + 1), base);
1787 Sw(reg, base, offset);
1788 Sw(static_cast<Register>(reg + 1), base, offset + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07001789 break;
1790 default:
1791 LOG(FATAL) << "UNREACHABLE";
1792 }
1793}
1794
Goran Jakovljevicff734982015-08-24 12:58:55 +00001795void MipsAssembler::StoreSToOffset(FRegister reg, Register base, int32_t offset) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001796 if (!IsInt<16>(offset)) {
1797 LoadConst32(AT, offset);
1798 Addu(AT, AT, base);
1799 base = AT;
1800 offset = 0;
1801 }
1802
jeffhao7fbee072012-08-24 17:56:54 -07001803 Swc1(reg, base, offset);
1804}
1805
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001806void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset) {
1807 // IsInt<16> must be passed a signed value.
1808 if (!IsInt<16>(offset) ||
1809 (!IsAligned<kMipsDoublewordSize>(offset) &&
1810 !IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
1811 LoadConst32(AT, offset);
1812 Addu(AT, AT, base);
1813 base = AT;
1814 offset = 0;
1815 }
1816
1817 if (offset & 0x7) {
1818 if (Is32BitFPU()) {
1819 Swc1(reg, base, offset);
1820 Swc1(static_cast<FRegister>(reg + 1), base, offset + kMipsWordSize);
1821 } else {
1822 // 64-bit FPU.
1823 Mfhc1(T8, reg);
1824 Swc1(reg, base, offset);
1825 Sw(T8, base, offset + kMipsWordSize);
1826 }
1827 } else {
1828 Sdc1(reg, base, offset);
1829 }
jeffhao7fbee072012-08-24 17:56:54 -07001830}
1831
David Srbeckydd973932015-04-07 20:29:48 +01001832static dwarf::Reg DWARFReg(Register reg) {
1833 return dwarf::Reg::MipsCore(static_cast<int>(reg));
1834}
1835
Ian Rogers790a6b72014-04-01 10:36:00 -07001836constexpr size_t kFramePointerSize = 4;
1837
jeffhao7fbee072012-08-24 17:56:54 -07001838void MipsAssembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
1839 const std::vector<ManagedRegister>& callee_save_regs,
Dmitry Petrochenkofca82202014-03-21 11:21:37 +07001840 const ManagedRegisterEntrySpills& entry_spills) {
jeffhao7fbee072012-08-24 17:56:54 -07001841 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01001842 DCHECK(!overwriting_);
jeffhao7fbee072012-08-24 17:56:54 -07001843
1844 // Increase frame to required size.
1845 IncreaseFrameSize(frame_size);
1846
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001847 // Push callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07001848 int stack_offset = frame_size - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07001849 StoreToOffset(kStoreWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01001850 cfi_.RelOffset(DWARFReg(RA), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07001851 for (int i = callee_save_regs.size() - 1; i >= 0; --i) {
Ian Rogers790a6b72014-04-01 10:36:00 -07001852 stack_offset -= kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07001853 Register reg = callee_save_regs.at(i).AsMips().AsCoreRegister();
1854 StoreToOffset(kStoreWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01001855 cfi_.RelOffset(DWARFReg(reg), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07001856 }
1857
1858 // Write out Method*.
1859 StoreToOffset(kStoreWord, method_reg.AsMips().AsCoreRegister(), SP, 0);
1860
1861 // Write out entry spills.
Goran Jakovljevicff734982015-08-24 12:58:55 +00001862 int32_t offset = frame_size + kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07001863 for (size_t i = 0; i < entry_spills.size(); ++i) {
Goran Jakovljevicff734982015-08-24 12:58:55 +00001864 MipsManagedRegister reg = entry_spills.at(i).AsMips();
1865 if (reg.IsNoRegister()) {
1866 ManagedRegisterSpill spill = entry_spills.at(i);
1867 offset += spill.getSize();
1868 } else if (reg.IsCoreRegister()) {
1869 StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001870 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00001871 } else if (reg.IsFRegister()) {
1872 StoreSToOffset(reg.AsFRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001873 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00001874 } else if (reg.IsDRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001875 StoreDToOffset(reg.AsOverlappingDRegisterLow(), SP, offset);
1876 offset += kMipsDoublewordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00001877 }
jeffhao7fbee072012-08-24 17:56:54 -07001878 }
1879}
1880
1881void MipsAssembler::RemoveFrame(size_t frame_size,
1882 const std::vector<ManagedRegister>& callee_save_regs) {
1883 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01001884 DCHECK(!overwriting_);
David Srbeckydd973932015-04-07 20:29:48 +01001885 cfi_.RememberState();
jeffhao7fbee072012-08-24 17:56:54 -07001886
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001887 // Pop callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07001888 int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07001889 for (size_t i = 0; i < callee_save_regs.size(); ++i) {
1890 Register reg = callee_save_regs.at(i).AsMips().AsCoreRegister();
1891 LoadFromOffset(kLoadWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01001892 cfi_.Restore(DWARFReg(reg));
Ian Rogers790a6b72014-04-01 10:36:00 -07001893 stack_offset += kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07001894 }
1895 LoadFromOffset(kLoadWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01001896 cfi_.Restore(DWARFReg(RA));
jeffhao7fbee072012-08-24 17:56:54 -07001897
1898 // Decrease frame to required size.
1899 DecreaseFrameSize(frame_size);
jeffhao07030602012-09-26 14:33:14 -07001900
1901 // Then jump to the return address.
1902 Jr(RA);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001903 Nop();
David Srbeckydd973932015-04-07 20:29:48 +01001904
1905 // The CFI should be restored for any code that follows the exit block.
1906 cfi_.RestoreState();
1907 cfi_.DefCFAOffset(frame_size);
jeffhao7fbee072012-08-24 17:56:54 -07001908}
1909
1910void MipsAssembler::IncreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001911 CHECK_ALIGNED(adjust, kFramePointerSize);
1912 Addiu32(SP, SP, -adjust);
David Srbeckydd973932015-04-07 20:29:48 +01001913 cfi_.AdjustCFAOffset(adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01001914 if (overwriting_) {
1915 cfi_.OverrideDelayedPC(overwrite_location_);
1916 }
jeffhao7fbee072012-08-24 17:56:54 -07001917}
1918
1919void MipsAssembler::DecreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001920 CHECK_ALIGNED(adjust, kFramePointerSize);
1921 Addiu32(SP, SP, adjust);
David Srbeckydd973932015-04-07 20:29:48 +01001922 cfi_.AdjustCFAOffset(-adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01001923 if (overwriting_) {
1924 cfi_.OverrideDelayedPC(overwrite_location_);
1925 }
jeffhao7fbee072012-08-24 17:56:54 -07001926}
1927
1928void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
1929 MipsManagedRegister src = msrc.AsMips();
1930 if (src.IsNoRegister()) {
1931 CHECK_EQ(0u, size);
1932 } else if (src.IsCoreRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001933 CHECK_EQ(kMipsWordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07001934 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
1935 } else if (src.IsRegisterPair()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001936 CHECK_EQ(kMipsDoublewordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07001937 StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
1938 StoreToOffset(kStoreWord, src.AsRegisterPairHigh(),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001939 SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07001940 } else if (src.IsFRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001941 if (size == kMipsWordSize) {
1942 StoreSToOffset(src.AsFRegister(), SP, dest.Int32Value());
1943 } else {
1944 CHECK_EQ(kMipsDoublewordSize, size);
1945 StoreDToOffset(src.AsFRegister(), SP, dest.Int32Value());
1946 }
jeffhao7fbee072012-08-24 17:56:54 -07001947 }
1948}
1949
1950void MipsAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
1951 MipsManagedRegister src = msrc.AsMips();
1952 CHECK(src.IsCoreRegister());
1953 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
1954}
1955
1956void MipsAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
1957 MipsManagedRegister src = msrc.AsMips();
1958 CHECK(src.IsCoreRegister());
1959 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
1960}
1961
1962void MipsAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
1963 ManagedRegister mscratch) {
1964 MipsManagedRegister scratch = mscratch.AsMips();
1965 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001966 LoadConst32(scratch.AsCoreRegister(), imm);
jeffhao7fbee072012-08-24 17:56:54 -07001967 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
1968}
1969
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001970void MipsAssembler::StoreImmediateToThread32(ThreadOffset<kMipsWordSize> dest, uint32_t imm,
jeffhao7fbee072012-08-24 17:56:54 -07001971 ManagedRegister mscratch) {
1972 MipsManagedRegister scratch = mscratch.AsMips();
1973 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001974 // Is this function even referenced anywhere else in the code?
1975 LoadConst32(scratch.AsCoreRegister(), imm);
1976 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), S1, dest.Int32Value());
1977}
1978
1979void MipsAssembler::StoreStackOffsetToThread32(ThreadOffset<kMipsWordSize> thr_offs,
1980 FrameOffset fr_offs,
1981 ManagedRegister mscratch) {
1982 MipsManagedRegister scratch = mscratch.AsMips();
1983 CHECK(scratch.IsCoreRegister()) << scratch;
1984 Addiu32(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07001985 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
1986 S1, thr_offs.Int32Value());
1987}
1988
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001989void MipsAssembler::StoreStackPointerToThread32(ThreadOffset<kMipsWordSize> thr_offs) {
jeffhao7fbee072012-08-24 17:56:54 -07001990 StoreToOffset(kStoreWord, SP, S1, thr_offs.Int32Value());
1991}
1992
1993void MipsAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
1994 FrameOffset in_off, ManagedRegister mscratch) {
1995 MipsManagedRegister src = msrc.AsMips();
1996 MipsManagedRegister scratch = mscratch.AsMips();
1997 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
1998 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001999 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002000}
2001
2002void MipsAssembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) {
2003 return EmitLoad(mdest, SP, src.Int32Value(), size);
2004}
2005
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002006void MipsAssembler::LoadFromThread32(ManagedRegister mdest,
2007 ThreadOffset<kMipsWordSize> src, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07002008 return EmitLoad(mdest, S1, src.Int32Value(), size);
2009}
2010
2011void MipsAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) {
2012 MipsManagedRegister dest = mdest.AsMips();
2013 CHECK(dest.IsCoreRegister());
2014 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value());
2015}
2016
Mathieu Chartiere401d142015-04-22 13:56:20 -07002017void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs,
Roland Levillain4d027112015-07-01 15:41:14 +01002018 bool unpoison_reference) {
jeffhao7fbee072012-08-24 17:56:54 -07002019 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002020 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07002021 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
2022 base.AsMips().AsCoreRegister(), offs.Int32Value());
Roland Levillain4d027112015-07-01 15:41:14 +01002023 if (kPoisonHeapReferences && unpoison_reference) {
Hiroshi Yamauchie63a7452014-02-27 14:44:36 -08002024 Subu(dest.AsCoreRegister(), ZERO, dest.AsCoreRegister());
2025 }
jeffhao7fbee072012-08-24 17:56:54 -07002026}
2027
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002028void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) {
jeffhao7fbee072012-08-24 17:56:54 -07002029 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002030 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07002031 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
2032 base.AsMips().AsCoreRegister(), offs.Int32Value());
2033}
2034
Ian Rogersdd7624d2014-03-14 17:43:00 -07002035void MipsAssembler::LoadRawPtrFromThread32(ManagedRegister mdest,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002036 ThreadOffset<kMipsWordSize> offs) {
jeffhao7fbee072012-08-24 17:56:54 -07002037 MipsManagedRegister dest = mdest.AsMips();
2038 CHECK(dest.IsCoreRegister());
2039 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), S1, offs.Int32Value());
2040}
2041
2042void MipsAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
2043 UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips";
2044}
2045
2046void MipsAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
2047 UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips";
2048}
2049
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002050void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07002051 MipsManagedRegister dest = mdest.AsMips();
2052 MipsManagedRegister src = msrc.AsMips();
2053 if (!dest.Equals(src)) {
2054 if (dest.IsCoreRegister()) {
2055 CHECK(src.IsCoreRegister()) << src;
2056 Move(dest.AsCoreRegister(), src.AsCoreRegister());
2057 } else if (dest.IsFRegister()) {
2058 CHECK(src.IsFRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002059 if (size == kMipsWordSize) {
2060 MovS(dest.AsFRegister(), src.AsFRegister());
2061 } else {
2062 CHECK_EQ(kMipsDoublewordSize, size);
2063 MovD(dest.AsFRegister(), src.AsFRegister());
2064 }
jeffhao7fbee072012-08-24 17:56:54 -07002065 } else if (dest.IsDRegister()) {
2066 CHECK(src.IsDRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002067 MovD(dest.AsOverlappingDRegisterLow(), src.AsOverlappingDRegisterLow());
jeffhao7fbee072012-08-24 17:56:54 -07002068 } else {
2069 CHECK(dest.IsRegisterPair()) << dest;
2070 CHECK(src.IsRegisterPair()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002071 // Ensure that the first move doesn't clobber the input of the second.
jeffhao7fbee072012-08-24 17:56:54 -07002072 if (src.AsRegisterPairHigh() != dest.AsRegisterPairLow()) {
2073 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
2074 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
2075 } else {
2076 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
2077 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
2078 }
2079 }
2080 }
2081}
2082
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002083void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07002084 MipsManagedRegister scratch = mscratch.AsMips();
2085 CHECK(scratch.IsCoreRegister()) << scratch;
2086 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
2087 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
2088}
2089
Ian Rogersdd7624d2014-03-14 17:43:00 -07002090void MipsAssembler::CopyRawPtrFromThread32(FrameOffset fr_offs,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002091 ThreadOffset<kMipsWordSize> thr_offs,
2092 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07002093 MipsManagedRegister scratch = mscratch.AsMips();
2094 CHECK(scratch.IsCoreRegister()) << scratch;
2095 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
2096 S1, thr_offs.Int32Value());
2097 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
2098 SP, fr_offs.Int32Value());
2099}
2100
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002101void MipsAssembler::CopyRawPtrToThread32(ThreadOffset<kMipsWordSize> thr_offs,
2102 FrameOffset fr_offs,
2103 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07002104 MipsManagedRegister scratch = mscratch.AsMips();
2105 CHECK(scratch.IsCoreRegister()) << scratch;
2106 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
2107 SP, fr_offs.Int32Value());
2108 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
2109 S1, thr_offs.Int32Value());
2110}
2111
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002112void MipsAssembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07002113 MipsManagedRegister scratch = mscratch.AsMips();
2114 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002115 CHECK(size == kMipsWordSize || size == kMipsDoublewordSize) << size;
2116 if (size == kMipsWordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07002117 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
2118 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002119 } else if (size == kMipsDoublewordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07002120 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
2121 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002122 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + kMipsWordSize);
2123 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002124 }
2125}
2126
2127void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
2128 ManagedRegister mscratch, size_t size) {
2129 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002130 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002131 LoadFromOffset(kLoadWord, scratch, src_base.AsMips().AsCoreRegister(), src_offset.Int32Value());
2132 StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
2133}
2134
2135void MipsAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
2136 ManagedRegister mscratch, size_t size) {
2137 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002138 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002139 LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
2140 StoreToOffset(kStoreWord, scratch, dest_base.AsMips().AsCoreRegister(), dest_offset.Int32Value());
2141}
2142
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002143void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
2144 FrameOffset src_base ATTRIBUTE_UNUSED,
2145 Offset src_offset ATTRIBUTE_UNUSED,
2146 ManagedRegister mscratch ATTRIBUTE_UNUSED,
2147 size_t size ATTRIBUTE_UNUSED) {
2148 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002149}
2150
2151void MipsAssembler::Copy(ManagedRegister dest, Offset dest_offset,
2152 ManagedRegister src, Offset src_offset,
2153 ManagedRegister mscratch, size_t size) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002154 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07002155 Register scratch = mscratch.AsMips().AsCoreRegister();
2156 LoadFromOffset(kLoadWord, scratch, src.AsMips().AsCoreRegister(), src_offset.Int32Value());
2157 StoreToOffset(kStoreWord, scratch, dest.AsMips().AsCoreRegister(), dest_offset.Int32Value());
2158}
2159
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002160void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
2161 Offset dest_offset ATTRIBUTE_UNUSED,
2162 FrameOffset src ATTRIBUTE_UNUSED,
2163 Offset src_offset ATTRIBUTE_UNUSED,
2164 ManagedRegister mscratch ATTRIBUTE_UNUSED,
2165 size_t size ATTRIBUTE_UNUSED) {
2166 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002167}
2168
2169void MipsAssembler::MemoryBarrier(ManagedRegister) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002170 // TODO: sync?
2171 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002172}
2173
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002174void MipsAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002175 FrameOffset handle_scope_offset,
2176 ManagedRegister min_reg,
2177 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07002178 MipsManagedRegister out_reg = mout_reg.AsMips();
2179 MipsManagedRegister in_reg = min_reg.AsMips();
2180 CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
2181 CHECK(out_reg.IsCoreRegister()) << out_reg;
2182 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002183 MipsLabel null_arg;
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002184 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
2185 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002186 // E.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset).
jeffhao7fbee072012-08-24 17:56:54 -07002187 if (in_reg.IsNoRegister()) {
2188 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002189 SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07002190 in_reg = out_reg;
2191 }
2192 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002193 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07002194 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002195 Beqz(in_reg.AsCoreRegister(), &null_arg);
2196 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
2197 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07002198 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002199 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07002200 }
2201}
2202
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002203void MipsAssembler::CreateHandleScopeEntry(FrameOffset out_off,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002204 FrameOffset handle_scope_offset,
2205 ManagedRegister mscratch,
2206 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07002207 MipsManagedRegister scratch = mscratch.AsMips();
2208 CHECK(scratch.IsCoreRegister()) << scratch;
2209 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002210 MipsLabel null_arg;
2211 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002212 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
2213 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002214 // E.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset).
2215 Beqz(scratch.AsCoreRegister(), &null_arg);
2216 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
2217 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07002218 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002219 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07002220 }
2221 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
2222}
2223
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07002224// Given a handle scope entry, load the associated reference.
2225void MipsAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002226 ManagedRegister min_reg) {
jeffhao7fbee072012-08-24 17:56:54 -07002227 MipsManagedRegister out_reg = mout_reg.AsMips();
2228 MipsManagedRegister in_reg = min_reg.AsMips();
2229 CHECK(out_reg.IsCoreRegister()) << out_reg;
2230 CHECK(in_reg.IsCoreRegister()) << in_reg;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002231 MipsLabel null_arg;
jeffhao7fbee072012-08-24 17:56:54 -07002232 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002233 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07002234 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002235 Beqz(in_reg.AsCoreRegister(), &null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07002236 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
2237 in_reg.AsCoreRegister(), 0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002238 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07002239}
2240
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002241void MipsAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,
2242 bool could_be_null ATTRIBUTE_UNUSED) {
2243 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07002244}
2245
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002246void MipsAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED,
2247 bool could_be_null ATTRIBUTE_UNUSED) {
2248 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07002249}
2250
2251void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) {
2252 MipsManagedRegister base = mbase.AsMips();
2253 MipsManagedRegister scratch = mscratch.AsMips();
2254 CHECK(base.IsCoreRegister()) << base;
2255 CHECK(scratch.IsCoreRegister()) << scratch;
2256 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
2257 base.AsCoreRegister(), offset.Int32Value());
2258 Jalr(scratch.AsCoreRegister());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002259 Nop();
2260 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07002261}
2262
2263void MipsAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
2264 MipsManagedRegister scratch = mscratch.AsMips();
2265 CHECK(scratch.IsCoreRegister()) << scratch;
2266 // Call *(*(SP + base) + offset)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002267 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, base.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07002268 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
2269 scratch.AsCoreRegister(), offset.Int32Value());
2270 Jalr(scratch.AsCoreRegister());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002271 Nop();
2272 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07002273}
2274
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002275void MipsAssembler::CallFromThread32(ThreadOffset<kMipsWordSize> offset ATTRIBUTE_UNUSED,
2276 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
Ian Rogers468532e2013-08-05 10:56:33 -07002277 UNIMPLEMENTED(FATAL) << "no mips implementation";
jeffhao7fbee072012-08-24 17:56:54 -07002278}
2279
2280void MipsAssembler::GetCurrentThread(ManagedRegister tr) {
2281 Move(tr.AsMips().AsCoreRegister(), S1);
2282}
2283
2284void MipsAssembler::GetCurrentThread(FrameOffset offset,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002285 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
jeffhao7fbee072012-08-24 17:56:54 -07002286 StoreToOffset(kStoreWord, S1, SP, offset.Int32Value());
2287}
2288
jeffhao7fbee072012-08-24 17:56:54 -07002289void MipsAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
2290 MipsManagedRegister scratch = mscratch.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002291 exception_blocks_.emplace_back(scratch, stack_adjust);
jeffhao7fbee072012-08-24 17:56:54 -07002292 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002293 S1, Thread::ExceptionOffset<kMipsWordSize>().Int32Value());
2294 // TODO: on MIPS32R6 prefer Bnezc(scratch.AsCoreRegister(), slow.Entry());
2295 // as the NAL instruction (occurring in long R2 branches) may become deprecated.
2296 // For now use common for R2 and R6 instructions as this code must execute on both.
2297 Bnez(scratch.AsCoreRegister(), exception_blocks_.back().Entry());
jeffhao7fbee072012-08-24 17:56:54 -07002298}
2299
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002300void MipsAssembler::EmitExceptionPoll(MipsExceptionSlowPath* exception) {
2301 Bind(exception->Entry());
2302 if (exception->stack_adjust_ != 0) { // Fix up the frame.
2303 DecreaseFrameSize(exception->stack_adjust_);
jeffhao7fbee072012-08-24 17:56:54 -07002304 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002305 // Pass exception object as argument.
2306 // Don't care about preserving A0 as this call won't return.
2307 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
2308 Move(A0, exception->scratch_.AsCoreRegister());
2309 // Set up call to Thread::Current()->pDeliverException.
2310 LoadFromOffset(kLoadWord, T9, S1,
2311 QUICK_ENTRYPOINT_OFFSET(kMipsWordSize, pDeliverException).Int32Value());
2312 Jr(T9);
2313 Nop();
2314
2315 // Call never returns.
2316 Break();
jeffhao7fbee072012-08-24 17:56:54 -07002317}
2318
2319} // namespace mips
2320} // namespace art