blob: 4b580b620fb9a33caf2177d583ea030be69092e0 [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
Alexey Frunze57eb0f52016-07-29 22:04:46 -070043MipsAssembler::DelaySlot::DelaySlot()
44 : instruction_(0),
45 gpr_outs_mask_(0),
46 gpr_ins_mask_(0),
47 fpr_outs_mask_(0),
48 fpr_ins_mask_(0),
49 cc_outs_mask_(0),
50 cc_ins_mask_(0) {}
51
52void MipsAssembler::DsFsmInstr(uint32_t instruction,
53 uint32_t gpr_outs_mask,
54 uint32_t gpr_ins_mask,
55 uint32_t fpr_outs_mask,
56 uint32_t fpr_ins_mask,
57 uint32_t cc_outs_mask,
58 uint32_t cc_ins_mask) {
59 if (!reordering_) {
60 CHECK_EQ(ds_fsm_state_, kExpectingLabel);
61 CHECK_EQ(delay_slot_.instruction_, 0u);
62 return;
63 }
64 switch (ds_fsm_state_) {
65 case kExpectingLabel:
66 break;
67 case kExpectingInstruction:
68 CHECK_EQ(ds_fsm_target_pc_ + sizeof(uint32_t), buffer_.Size());
69 // If the last instruction is not suitable for delay slots, drop
70 // the PC of the label preceding it so that no unconditional branch
71 // uses this instruction to fill its delay slot.
72 if (instruction == 0) {
73 DsFsmDropLabel(); // Sets ds_fsm_state_ = kExpectingLabel.
74 } else {
75 // Otherwise wait for another instruction or label before we can
76 // commit the label PC. The label PC will be dropped if instead
77 // of another instruction or label there's a call from the code
78 // generator to CodePosition() to record the buffer size.
79 // Instructions after which the buffer size is recorded cannot
80 // be moved into delay slots or anywhere else because they may
81 // trigger signals and the signal handlers expect these signals
82 // to be coming from the instructions immediately preceding the
83 // recorded buffer locations.
84 ds_fsm_state_ = kExpectingCommit;
85 }
86 break;
87 case kExpectingCommit:
88 CHECK_EQ(ds_fsm_target_pc_ + 2 * sizeof(uint32_t), buffer_.Size());
89 DsFsmCommitLabel(); // Sets ds_fsm_state_ = kExpectingLabel.
90 break;
91 }
92 delay_slot_.instruction_ = instruction;
93 delay_slot_.gpr_outs_mask_ = gpr_outs_mask & ~1u; // Ignore register ZERO.
94 delay_slot_.gpr_ins_mask_ = gpr_ins_mask & ~1u; // Ignore register ZERO.
95 delay_slot_.fpr_outs_mask_ = fpr_outs_mask;
96 delay_slot_.fpr_ins_mask_ = fpr_ins_mask;
97 delay_slot_.cc_outs_mask_ = cc_outs_mask;
98 delay_slot_.cc_ins_mask_ = cc_ins_mask;
99}
100
101void MipsAssembler::DsFsmLabel() {
102 if (!reordering_) {
103 CHECK_EQ(ds_fsm_state_, kExpectingLabel);
104 CHECK_EQ(delay_slot_.instruction_, 0u);
105 return;
106 }
107 switch (ds_fsm_state_) {
108 case kExpectingLabel:
109 ds_fsm_target_pc_ = buffer_.Size();
110 ds_fsm_state_ = kExpectingInstruction;
111 break;
112 case kExpectingInstruction:
113 // Allow consecutive labels.
114 CHECK_EQ(ds_fsm_target_pc_, buffer_.Size());
115 break;
116 case kExpectingCommit:
117 CHECK_EQ(ds_fsm_target_pc_ + sizeof(uint32_t), buffer_.Size());
118 DsFsmCommitLabel();
119 ds_fsm_target_pc_ = buffer_.Size();
120 ds_fsm_state_ = kExpectingInstruction;
121 break;
122 }
123 // We cannot move instructions into delay slots across labels.
124 delay_slot_.instruction_ = 0;
125}
126
127void MipsAssembler::DsFsmCommitLabel() {
128 if (ds_fsm_state_ == kExpectingCommit) {
129 ds_fsm_target_pcs_.emplace_back(ds_fsm_target_pc_);
130 }
131 ds_fsm_state_ = kExpectingLabel;
132}
133
134void MipsAssembler::DsFsmDropLabel() {
135 ds_fsm_state_ = kExpectingLabel;
136}
137
138bool MipsAssembler::SetReorder(bool enable) {
139 bool last_state = reordering_;
140 if (last_state != enable) {
141 DsFsmCommitLabel();
142 DsFsmInstrNop(0);
143 }
144 reordering_ = enable;
145 return last_state;
146}
147
148size_t MipsAssembler::CodePosition() {
149 // The last instruction cannot be used in a delay slot, do not commit
150 // the label before it (if any) and clear the delay slot.
151 DsFsmDropLabel();
152 DsFsmInstrNop(0);
153 size_t size = buffer_.Size();
154 // In theory we can get the following sequence:
155 // label1:
156 // instr
157 // label2: # label1 gets committed when label2 is seen
158 // CodePosition() call
159 // and we need to uncommit label1.
160 if (ds_fsm_target_pcs_.size() != 0 && ds_fsm_target_pcs_.back() + sizeof(uint32_t) == size) {
161 ds_fsm_target_pcs_.pop_back();
162 }
163 return size;
164}
165
166void MipsAssembler::DsFsmInstrNop(uint32_t instruction ATTRIBUTE_UNUSED) {
167 DsFsmInstr(0, 0, 0, 0, 0, 0, 0);
168}
169
170void MipsAssembler::DsFsmInstrRrr(uint32_t instruction, Register out, Register in1, Register in2) {
171 DsFsmInstr(instruction, (1u << out), (1u << in1) | (1u << in2), 0, 0, 0, 0);
172}
173
174void MipsAssembler::DsFsmInstrRrrr(uint32_t instruction,
175 Register in1_out,
176 Register in2,
177 Register in3) {
178 DsFsmInstr(instruction, (1u << in1_out), (1u << in1_out) | (1u << in2) | (1u << in3), 0, 0, 0, 0);
179}
180
181void MipsAssembler::DsFsmInstrFff(uint32_t instruction,
182 FRegister out,
183 FRegister in1,
184 FRegister in2) {
185 DsFsmInstr(instruction, 0, 0, (1u << out), (1u << in1) | (1u << in2), 0, 0);
186}
187
188void MipsAssembler::DsFsmInstrFfff(uint32_t instruction,
189 FRegister in1_out,
190 FRegister in2,
191 FRegister in3) {
192 DsFsmInstr(instruction, 0, 0, (1u << in1_out), (1u << in1_out) | (1u << in2) | (1u << in3), 0, 0);
193}
194
195void MipsAssembler::DsFsmInstrRf(uint32_t instruction, Register out, FRegister in) {
196 DsFsmInstr(instruction, (1u << out), 0, 0, (1u << in), 0, 0);
197}
198
199void MipsAssembler::DsFsmInstrFr(uint32_t instruction, FRegister out, Register in) {
200 DsFsmInstr(instruction, 0, (1u << in), (1u << out), 0, 0, 0);
201}
202
203void MipsAssembler::DsFsmInstrFR(uint32_t instruction, FRegister in1, Register in2) {
204 DsFsmInstr(instruction, 0, (1u << in2), 0, (1u << in1), 0, 0);
205}
206
207void MipsAssembler::DsFsmInstrCff(uint32_t instruction, int cc_out, FRegister in1, FRegister in2) {
208 DsFsmInstr(instruction, 0, 0, 0, (1u << in1) | (1u << in2), (1 << cc_out), 0);
209}
210
211void MipsAssembler::DsFsmInstrRrrc(uint32_t instruction,
212 Register in1_out,
213 Register in2,
214 int cc_in) {
215 DsFsmInstr(instruction, (1u << in1_out), (1u << in1_out) | (1u << in2), 0, 0, 0, (1 << cc_in));
216}
217
218void MipsAssembler::DsFsmInstrFffc(uint32_t instruction,
219 FRegister in1_out,
220 FRegister in2,
221 int cc_in) {
222 DsFsmInstr(instruction, 0, 0, (1u << in1_out), (1u << in1_out) | (1u << in2), 0, (1 << cc_in));
223}
224
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200225void MipsAssembler::FinalizeCode() {
226 for (auto& exception_block : exception_blocks_) {
227 EmitExceptionPoll(&exception_block);
228 }
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700229 // Commit the last branch target label (if any) and disable instruction reordering.
230 DsFsmCommitLabel();
231 SetReorder(false);
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700232 EmitLiterals();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200233 PromoteBranches();
234}
235
236void MipsAssembler::FinalizeInstructions(const MemoryRegion& region) {
Vladimir Marko10ef6942015-10-22 15:25:54 +0100237 size_t number_of_delayed_adjust_pcs = cfi().NumberOfDelayedAdvancePCs();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200238 EmitBranches();
239 Assembler::FinalizeInstructions(region);
Vladimir Marko10ef6942015-10-22 15:25:54 +0100240 PatchCFI(number_of_delayed_adjust_pcs);
241}
242
243void MipsAssembler::PatchCFI(size_t number_of_delayed_adjust_pcs) {
244 if (cfi().NumberOfDelayedAdvancePCs() == 0u) {
245 DCHECK_EQ(number_of_delayed_adjust_pcs, 0u);
246 return;
247 }
248
249 typedef DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC DelayedAdvancePC;
250 const auto data = cfi().ReleaseStreamAndPrepareForDelayedAdvancePC();
251 const std::vector<uint8_t>& old_stream = data.first;
252 const std::vector<DelayedAdvancePC>& advances = data.second;
253
254 // PCs recorded before EmitBranches() need to be adjusted.
255 // PCs recorded during EmitBranches() are already adjusted.
256 // Both ranges are separately sorted but they may overlap.
257 if (kIsDebugBuild) {
258 auto cmp = [](const DelayedAdvancePC& lhs, const DelayedAdvancePC& rhs) {
259 return lhs.pc < rhs.pc;
260 };
261 CHECK(std::is_sorted(advances.begin(), advances.begin() + number_of_delayed_adjust_pcs, cmp));
262 CHECK(std::is_sorted(advances.begin() + number_of_delayed_adjust_pcs, advances.end(), cmp));
263 }
264
265 // Append initial CFI data if any.
266 size_t size = advances.size();
267 DCHECK_NE(size, 0u);
268 cfi().AppendRawData(old_stream, 0u, advances[0].stream_pos);
269 // Emit PC adjustments interleaved with the old CFI stream.
270 size_t adjust_pos = 0u;
271 size_t late_emit_pos = number_of_delayed_adjust_pcs;
272 while (adjust_pos != number_of_delayed_adjust_pcs || late_emit_pos != size) {
273 size_t adjusted_pc = (adjust_pos != number_of_delayed_adjust_pcs)
274 ? GetAdjustedPosition(advances[adjust_pos].pc)
275 : static_cast<size_t>(-1);
276 size_t late_emit_pc = (late_emit_pos != size)
277 ? advances[late_emit_pos].pc
278 : static_cast<size_t>(-1);
279 size_t advance_pc = std::min(adjusted_pc, late_emit_pc);
280 DCHECK_NE(advance_pc, static_cast<size_t>(-1));
281 size_t entry = (adjusted_pc <= late_emit_pc) ? adjust_pos : late_emit_pos;
282 if (adjusted_pc <= late_emit_pc) {
283 ++adjust_pos;
284 } else {
285 ++late_emit_pos;
286 }
287 cfi().AdvancePC(advance_pc);
288 size_t end_pos = (entry + 1u == size) ? old_stream.size() : advances[entry + 1u].stream_pos;
289 cfi().AppendRawData(old_stream, advances[entry].stream_pos, end_pos);
290 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200291}
292
293void MipsAssembler::EmitBranches() {
294 CHECK(!overwriting_);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700295 CHECK(!reordering_);
296 // Now that everything has its final position in the buffer (the branches have
297 // been promoted), adjust the target label PCs.
298 for (size_t cnt = ds_fsm_target_pcs_.size(), i = 0; i < cnt; i++) {
299 ds_fsm_target_pcs_[i] = GetAdjustedPosition(ds_fsm_target_pcs_[i]);
300 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200301 // Switch from appending instructions at the end of the buffer to overwriting
302 // existing instructions (branch placeholders) in the buffer.
303 overwriting_ = true;
304 for (auto& branch : branches_) {
305 EmitBranch(&branch);
306 }
307 overwriting_ = false;
308}
309
310void MipsAssembler::Emit(uint32_t value) {
311 if (overwriting_) {
312 // Branches to labels are emitted into their placeholders here.
313 buffer_.Store<uint32_t>(overwrite_location_, value);
314 overwrite_location_ += sizeof(uint32_t);
315 } else {
316 // Other instructions are simply appended at the end here.
317 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
318 buffer_.Emit<uint32_t>(value);
319 }
jeffhao7fbee072012-08-24 17:56:54 -0700320}
321
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700322uint32_t MipsAssembler::EmitR(int opcode,
323 Register rs,
324 Register rt,
325 Register rd,
326 int shamt,
327 int funct) {
jeffhao7fbee072012-08-24 17:56:54 -0700328 CHECK_NE(rs, kNoRegister);
329 CHECK_NE(rt, kNoRegister);
330 CHECK_NE(rd, kNoRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200331 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
332 static_cast<uint32_t>(rs) << kRsShift |
333 static_cast<uint32_t>(rt) << kRtShift |
334 static_cast<uint32_t>(rd) << kRdShift |
335 shamt << kShamtShift |
336 funct;
jeffhao7fbee072012-08-24 17:56:54 -0700337 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700338 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700339}
340
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700341uint32_t MipsAssembler::EmitI(int opcode, Register rs, Register rt, uint16_t imm) {
jeffhao7fbee072012-08-24 17:56:54 -0700342 CHECK_NE(rs, kNoRegister);
343 CHECK_NE(rt, kNoRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200344 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
345 static_cast<uint32_t>(rs) << kRsShift |
346 static_cast<uint32_t>(rt) << kRtShift |
347 imm;
jeffhao7fbee072012-08-24 17:56:54 -0700348 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700349 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700350}
351
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700352uint32_t MipsAssembler::EmitI21(int opcode, Register rs, uint32_t imm21) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200353 CHECK_NE(rs, kNoRegister);
354 CHECK(IsUint<21>(imm21)) << imm21;
355 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
356 static_cast<uint32_t>(rs) << kRsShift |
357 imm21;
jeffhao7fbee072012-08-24 17:56:54 -0700358 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700359 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700360}
361
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700362uint32_t MipsAssembler::EmitI26(int opcode, uint32_t imm26) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200363 CHECK(IsUint<26>(imm26)) << imm26;
364 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | imm26;
365 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700366 return encoding;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200367}
368
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700369uint32_t MipsAssembler::EmitFR(int opcode,
370 int fmt,
371 FRegister ft,
372 FRegister fs,
373 FRegister fd,
374 int funct) {
jeffhao7fbee072012-08-24 17:56:54 -0700375 CHECK_NE(ft, kNoFRegister);
376 CHECK_NE(fs, kNoFRegister);
377 CHECK_NE(fd, kNoFRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200378 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
379 fmt << kFmtShift |
380 static_cast<uint32_t>(ft) << kFtShift |
381 static_cast<uint32_t>(fs) << kFsShift |
382 static_cast<uint32_t>(fd) << kFdShift |
383 funct;
jeffhao7fbee072012-08-24 17:56:54 -0700384 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700385 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700386}
387
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700388uint32_t MipsAssembler::EmitFI(int opcode, int fmt, FRegister ft, uint16_t imm) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200389 CHECK_NE(ft, kNoFRegister);
390 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
391 fmt << kFmtShift |
392 static_cast<uint32_t>(ft) << kFtShift |
393 imm;
jeffhao7fbee072012-08-24 17:56:54 -0700394 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700395 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700396}
397
jeffhao7fbee072012-08-24 17:56:54 -0700398void MipsAssembler::Addu(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700399 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x21), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700400}
401
jeffhao7fbee072012-08-24 17:56:54 -0700402void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700403 DsFsmInstrRrr(EmitI(0x9, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700404}
405
jeffhao7fbee072012-08-24 17:56:54 -0700406void MipsAssembler::Subu(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700407 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x23), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700408}
409
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200410void MipsAssembler::MultR2(Register rs, Register rt) {
411 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700412 DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x18), ZERO, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700413}
414
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200415void MipsAssembler::MultuR2(Register rs, Register rt) {
416 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700417 DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x19), ZERO, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700418}
419
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200420void MipsAssembler::DivR2(Register rs, Register rt) {
421 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700422 DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1a), ZERO, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700423}
424
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200425void MipsAssembler::DivuR2(Register rs, Register rt) {
426 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700427 DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1b), ZERO, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700428}
429
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200430void MipsAssembler::MulR2(Register rd, Register rs, Register rt) {
431 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700432 DsFsmInstrRrr(EmitR(0x1c, rs, rt, rd, 0, 2), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200433}
434
435void MipsAssembler::DivR2(Register rd, Register rs, Register rt) {
436 CHECK(!IsR6());
437 DivR2(rs, rt);
438 Mflo(rd);
439}
440
441void MipsAssembler::ModR2(Register rd, Register rs, Register rt) {
442 CHECK(!IsR6());
443 DivR2(rs, rt);
444 Mfhi(rd);
445}
446
447void MipsAssembler::DivuR2(Register rd, Register rs, Register rt) {
448 CHECK(!IsR6());
449 DivuR2(rs, rt);
450 Mflo(rd);
451}
452
453void MipsAssembler::ModuR2(Register rd, Register rs, Register rt) {
454 CHECK(!IsR6());
455 DivuR2(rs, rt);
456 Mfhi(rd);
457}
458
459void MipsAssembler::MulR6(Register rd, Register rs, Register rt) {
460 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700461 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x18), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200462}
463
Alexey Frunze7e99e052015-11-24 19:28:01 -0800464void MipsAssembler::MuhR6(Register rd, Register rs, Register rt) {
465 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700466 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x18), rd, rs, rt);
Alexey Frunze7e99e052015-11-24 19:28:01 -0800467}
468
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200469void MipsAssembler::MuhuR6(Register rd, Register rs, Register rt) {
470 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700471 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x19), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200472}
473
474void MipsAssembler::DivR6(Register rd, Register rs, Register rt) {
475 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700476 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x1a), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200477}
478
479void MipsAssembler::ModR6(Register rd, Register rs, Register rt) {
480 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700481 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x1a), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200482}
483
484void MipsAssembler::DivuR6(Register rd, Register rs, Register rt) {
485 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700486 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x1b), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200487}
488
489void MipsAssembler::ModuR6(Register rd, Register rs, Register rt) {
490 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700491 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x1b), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200492}
493
jeffhao7fbee072012-08-24 17:56:54 -0700494void MipsAssembler::And(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700495 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x24), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700496}
497
498void MipsAssembler::Andi(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700499 DsFsmInstrRrr(EmitI(0xc, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700500}
501
502void MipsAssembler::Or(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700503 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x25), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700504}
505
506void MipsAssembler::Ori(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700507 DsFsmInstrRrr(EmitI(0xd, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700508}
509
510void MipsAssembler::Xor(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700511 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x26), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700512}
513
514void MipsAssembler::Xori(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700515 DsFsmInstrRrr(EmitI(0xe, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700516}
517
518void MipsAssembler::Nor(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700519 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x27), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700520}
521
Chris Larsene3845472015-11-18 12:27:15 -0800522void MipsAssembler::Movz(Register rd, Register rs, Register rt) {
523 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700524 DsFsmInstrRrrr(EmitR(0, rs, rt, rd, 0, 0x0A), rd, rs, rt);
Chris Larsene3845472015-11-18 12:27:15 -0800525}
526
527void MipsAssembler::Movn(Register rd, Register rs, Register rt) {
528 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700529 DsFsmInstrRrrr(EmitR(0, rs, rt, rd, 0, 0x0B), rd, rs, rt);
Chris Larsene3845472015-11-18 12:27:15 -0800530}
531
532void MipsAssembler::Seleqz(Register rd, Register rs, Register rt) {
533 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700534 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x35), rd, rs, rt);
Chris Larsene3845472015-11-18 12:27:15 -0800535}
536
537void MipsAssembler::Selnez(Register rd, Register rs, Register rt) {
538 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700539 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x37), rd, rs, rt);
Chris Larsene3845472015-11-18 12:27:15 -0800540}
541
542void MipsAssembler::ClzR6(Register rd, Register rs) {
543 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700544 DsFsmInstrRrr(EmitR(0, rs, static_cast<Register>(0), rd, 0x01, 0x10), rd, rs, rs);
Chris Larsene3845472015-11-18 12:27:15 -0800545}
546
547void MipsAssembler::ClzR2(Register rd, Register rs) {
548 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700549 DsFsmInstrRrr(EmitR(0x1C, rs, rd, rd, 0, 0x20), rd, rs, rs);
Chris Larsene3845472015-11-18 12:27:15 -0800550}
551
552void MipsAssembler::CloR6(Register rd, Register rs) {
553 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700554 DsFsmInstrRrr(EmitR(0, rs, static_cast<Register>(0), rd, 0x01, 0x11), rd, rs, rs);
Chris Larsene3845472015-11-18 12:27:15 -0800555}
556
557void MipsAssembler::CloR2(Register rd, Register rs) {
558 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700559 DsFsmInstrRrr(EmitR(0x1C, rs, rd, rd, 0, 0x21), rd, rs, rs);
Chris Larsene3845472015-11-18 12:27:15 -0800560}
561
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200562void MipsAssembler::Seb(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700563 DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x10, 0x20), rd, rt, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700564}
565
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200566void MipsAssembler::Seh(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700567 DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x18, 0x20), rd, rt, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700568}
569
Chris Larsen3f8bf652015-10-28 10:08:56 -0700570void MipsAssembler::Wsbh(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700571 DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 2, 0x20), rd, rt, rt);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700572}
573
Chris Larsen70014c82015-11-18 12:26:08 -0800574void MipsAssembler::Bitswap(Register rd, Register rt) {
575 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700576 DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x0, 0x20), rd, rt, rt);
Chris Larsen70014c82015-11-18 12:26:08 -0800577}
578
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200579void MipsAssembler::Sll(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700580 CHECK(IsUint<5>(shamt)) << shamt;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700581 DsFsmInstrRrr(EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x00), rd, rt, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700582}
583
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200584void MipsAssembler::Srl(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700585 CHECK(IsUint<5>(shamt)) << shamt;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700586 DsFsmInstrRrr(EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x02), rd, rt, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200587}
588
Chris Larsen3f8bf652015-10-28 10:08:56 -0700589void MipsAssembler::Rotr(Register rd, Register rt, int shamt) {
590 CHECK(IsUint<5>(shamt)) << shamt;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700591 DsFsmInstrRrr(EmitR(0, static_cast<Register>(1), rt, rd, shamt, 0x02), rd, rt, rt);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700592}
593
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200594void MipsAssembler::Sra(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700595 CHECK(IsUint<5>(shamt)) << shamt;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700596 DsFsmInstrRrr(EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x03), rd, rt, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200597}
598
599void MipsAssembler::Sllv(Register rd, Register rt, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700600 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x04), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700601}
602
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200603void MipsAssembler::Srlv(Register rd, Register rt, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700604 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x06), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700605}
606
Chris Larsene16ce5a2015-11-18 12:30:20 -0800607void MipsAssembler::Rotrv(Register rd, Register rt, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700608 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 1, 0x06), rd, rs, rt);
Chris Larsene16ce5a2015-11-18 12:30:20 -0800609}
610
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200611void MipsAssembler::Srav(Register rd, Register rt, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700612 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x07), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700613}
614
Alexey Frunze5c7aed32015-11-25 19:41:54 -0800615void MipsAssembler::Ext(Register rd, Register rt, int pos, int size) {
616 CHECK(IsUint<5>(pos)) << pos;
617 CHECK(0 < size && size <= 32) << size;
618 CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700619 DsFsmInstrRrr(EmitR(0x1f, rt, rd, static_cast<Register>(size - 1), pos, 0x00), rd, rt, rt);
Alexey Frunze5c7aed32015-11-25 19:41:54 -0800620}
621
622void MipsAssembler::Ins(Register rd, Register rt, int pos, int size) {
623 CHECK(IsUint<5>(pos)) << pos;
624 CHECK(0 < size && size <= 32) << size;
625 CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700626 DsFsmInstrRrr(EmitR(0x1f, rt, rd, static_cast<Register>(pos + size - 1), pos, 0x04), rd, rd, rt);
Alexey Frunze5c7aed32015-11-25 19:41:54 -0800627}
628
jeffhao7fbee072012-08-24 17:56:54 -0700629void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700630 DsFsmInstrRrr(EmitI(0x20, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700631}
632
633void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700634 DsFsmInstrRrr(EmitI(0x21, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700635}
636
637void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700638 DsFsmInstrRrr(EmitI(0x23, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700639}
640
Chris Larsen3acee732015-11-18 13:31:08 -0800641void MipsAssembler::Lwl(Register rt, Register rs, uint16_t imm16) {
642 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700643 DsFsmInstrRrr(EmitI(0x22, rs, rt, imm16), rt, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800644}
645
646void MipsAssembler::Lwr(Register rt, Register rs, uint16_t imm16) {
647 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700648 DsFsmInstrRrr(EmitI(0x26, rs, rt, imm16), rt, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800649}
650
jeffhao7fbee072012-08-24 17:56:54 -0700651void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700652 DsFsmInstrRrr(EmitI(0x24, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700653}
654
655void MipsAssembler::Lhu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700656 DsFsmInstrRrr(EmitI(0x25, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700657}
658
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700659void MipsAssembler::Lwpc(Register rs, uint32_t imm19) {
660 CHECK(IsR6());
661 CHECK(IsUint<19>(imm19)) << imm19;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700662 DsFsmInstrNop(EmitI21(0x3B, rs, (0x01 << 19) | imm19));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700663}
664
jeffhao7fbee072012-08-24 17:56:54 -0700665void MipsAssembler::Lui(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700666 DsFsmInstrRrr(EmitI(0xf, static_cast<Register>(0), rt, imm16), rt, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700667}
668
Alexey Frunzecad3a4c2016-06-07 23:40:37 -0700669void MipsAssembler::Aui(Register rt, Register rs, uint16_t imm16) {
670 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700671 DsFsmInstrRrr(EmitI(0xf, rs, rt, imm16), rt, rt, rs);
Alexey Frunzecad3a4c2016-06-07 23:40:37 -0700672}
673
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200674void MipsAssembler::Sync(uint32_t stype) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700675 DsFsmInstrNop(EmitR(0, ZERO, ZERO, ZERO, stype & 0x1f, 0xf));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200676}
677
jeffhao7fbee072012-08-24 17:56:54 -0700678void MipsAssembler::Mfhi(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200679 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700680 DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x10), rd, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700681}
682
683void MipsAssembler::Mflo(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200684 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700685 DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x12), rd, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700686}
687
688void MipsAssembler::Sb(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700689 DsFsmInstrRrr(EmitI(0x28, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700690}
691
692void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700693 DsFsmInstrRrr(EmitI(0x29, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700694}
695
696void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700697 DsFsmInstrRrr(EmitI(0x2b, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700698}
699
Chris Larsen3acee732015-11-18 13:31:08 -0800700void MipsAssembler::Swl(Register rt, Register rs, uint16_t imm16) {
701 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700702 DsFsmInstrRrr(EmitI(0x2a, rs, rt, imm16), ZERO, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800703}
704
705void MipsAssembler::Swr(Register rt, Register rs, uint16_t imm16) {
706 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700707 DsFsmInstrRrr(EmitI(0x2e, rs, rt, imm16), ZERO, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800708}
709
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700710void MipsAssembler::LlR2(Register rt, Register base, int16_t imm16) {
711 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700712 DsFsmInstrRrr(EmitI(0x30, base, rt, imm16), rt, base, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700713}
714
715void MipsAssembler::ScR2(Register rt, Register base, int16_t imm16) {
716 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700717 DsFsmInstrRrr(EmitI(0x38, base, rt, imm16), rt, rt, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700718}
719
720void MipsAssembler::LlR6(Register rt, Register base, int16_t imm9) {
721 CHECK(IsR6());
722 CHECK(IsInt<9>(imm9));
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700723 DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36), rt, base, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700724}
725
726void MipsAssembler::ScR6(Register rt, Register base, int16_t imm9) {
727 CHECK(IsR6());
728 CHECK(IsInt<9>(imm9));
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700729 DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26), rt, rt, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700730}
731
jeffhao7fbee072012-08-24 17:56:54 -0700732void MipsAssembler::Slt(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700733 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2a), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700734}
735
736void MipsAssembler::Sltu(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700737 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2b), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700738}
739
740void MipsAssembler::Slti(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700741 DsFsmInstrRrr(EmitI(0xa, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700742}
743
744void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700745 DsFsmInstrRrr(EmitI(0xb, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700746}
747
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200748void MipsAssembler::B(uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700749 DsFsmInstrNop(EmitI(0x4, static_cast<Register>(0), static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200750}
751
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700752void MipsAssembler::Bal(uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700753 DsFsmInstrNop(EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x11), imm16));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700754}
755
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200756void MipsAssembler::Beq(Register rs, Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700757 DsFsmInstrNop(EmitI(0x4, rs, rt, imm16));
jeffhao7fbee072012-08-24 17:56:54 -0700758}
759
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200760void MipsAssembler::Bne(Register rs, Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700761 DsFsmInstrNop(EmitI(0x5, rs, rt, imm16));
jeffhao7fbee072012-08-24 17:56:54 -0700762}
763
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200764void MipsAssembler::Beqz(Register rt, uint16_t imm16) {
765 Beq(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700766}
767
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200768void MipsAssembler::Bnez(Register rt, uint16_t imm16) {
769 Bne(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700770}
771
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200772void MipsAssembler::Bltz(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700773 DsFsmInstrNop(EmitI(0x1, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200774}
775
776void MipsAssembler::Bgez(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700777 DsFsmInstrNop(EmitI(0x1, rt, static_cast<Register>(0x1), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200778}
779
780void MipsAssembler::Blez(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700781 DsFsmInstrNop(EmitI(0x6, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200782}
783
784void MipsAssembler::Bgtz(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700785 DsFsmInstrNop(EmitI(0x7, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200786}
787
Chris Larsenb74353a2015-11-20 09:07:09 -0800788void MipsAssembler::Bc1f(uint16_t imm16) {
789 Bc1f(0, imm16);
790}
791
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800792void MipsAssembler::Bc1f(int cc, uint16_t imm16) {
793 CHECK(!IsR6());
794 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700795 DsFsmInstrNop(EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>(cc << 2), imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800796}
797
Chris Larsenb74353a2015-11-20 09:07:09 -0800798void MipsAssembler::Bc1t(uint16_t imm16) {
799 Bc1t(0, imm16);
800}
801
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800802void MipsAssembler::Bc1t(int cc, uint16_t imm16) {
803 CHECK(!IsR6());
804 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700805 DsFsmInstrNop(EmitI(0x11,
806 static_cast<Register>(0x8),
807 static_cast<Register>((cc << 2) | 1),
808 imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800809}
810
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200811void MipsAssembler::J(uint32_t addr26) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700812 DsFsmInstrNop(EmitI26(0x2, addr26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200813}
814
815void MipsAssembler::Jal(uint32_t addr26) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700816 DsFsmInstrNop(EmitI26(0x3, addr26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200817}
818
819void MipsAssembler::Jalr(Register rd, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700820 uint32_t last_instruction = delay_slot_.instruction_;
821 bool exchange = (last_instruction != 0 &&
822 (delay_slot_.gpr_outs_mask_ & (1u << rs)) == 0 &&
823 ((delay_slot_.gpr_ins_mask_ | delay_slot_.gpr_outs_mask_) & (1u << rd)) == 0);
824 if (exchange) {
825 // The last instruction cannot be used in a different delay slot,
826 // do not commit the label before it (if any).
827 DsFsmDropLabel();
828 }
829 DsFsmInstrNop(EmitR(0, rs, static_cast<Register>(0), rd, 0, 0x09));
830 if (exchange) {
831 // Exchange the last two instructions in the assembler buffer.
832 size_t size = buffer_.Size();
833 CHECK_GE(size, 2 * sizeof(uint32_t));
834 size_t pos1 = size - 2 * sizeof(uint32_t);
835 size_t pos2 = size - sizeof(uint32_t);
836 uint32_t instr1 = buffer_.Load<uint32_t>(pos1);
837 uint32_t instr2 = buffer_.Load<uint32_t>(pos2);
838 CHECK_EQ(instr1, last_instruction);
839 buffer_.Store<uint32_t>(pos1, instr2);
840 buffer_.Store<uint32_t>(pos2, instr1);
841 } else if (reordering_) {
842 Nop();
843 }
jeffhao7fbee072012-08-24 17:56:54 -0700844}
845
846void MipsAssembler::Jalr(Register rs) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200847 Jalr(RA, rs);
848}
849
850void MipsAssembler::Jr(Register rs) {
851 Jalr(ZERO, rs);
852}
853
854void MipsAssembler::Nal() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700855 DsFsmInstrNop(EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x10), 0));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200856}
857
858void MipsAssembler::Auipc(Register rs, uint16_t imm16) {
859 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700860 DsFsmInstrNop(EmitI(0x3B, rs, static_cast<Register>(0x1E), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200861}
862
863void MipsAssembler::Addiupc(Register rs, uint32_t imm19) {
864 CHECK(IsR6());
865 CHECK(IsUint<19>(imm19)) << imm19;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700866 DsFsmInstrNop(EmitI21(0x3B, rs, imm19));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200867}
868
869void MipsAssembler::Bc(uint32_t imm26) {
870 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700871 DsFsmInstrNop(EmitI26(0x32, imm26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200872}
873
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700874void MipsAssembler::Balc(uint32_t imm26) {
875 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700876 DsFsmInstrNop(EmitI26(0x3A, imm26));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700877}
878
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200879void MipsAssembler::Jic(Register rt, uint16_t imm16) {
880 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700881 DsFsmInstrNop(EmitI(0x36, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200882}
883
884void MipsAssembler::Jialc(Register rt, uint16_t imm16) {
885 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700886 DsFsmInstrNop(EmitI(0x3E, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200887}
888
889void MipsAssembler::Bltc(Register rs, Register rt, uint16_t imm16) {
890 CHECK(IsR6());
891 CHECK_NE(rs, ZERO);
892 CHECK_NE(rt, ZERO);
893 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700894 DsFsmInstrNop(EmitI(0x17, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200895}
896
897void MipsAssembler::Bltzc(Register rt, uint16_t imm16) {
898 CHECK(IsR6());
899 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700900 DsFsmInstrNop(EmitI(0x17, rt, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200901}
902
903void MipsAssembler::Bgtzc(Register rt, uint16_t imm16) {
904 CHECK(IsR6());
905 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700906 DsFsmInstrNop(EmitI(0x17, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200907}
908
909void MipsAssembler::Bgec(Register rs, Register rt, uint16_t imm16) {
910 CHECK(IsR6());
911 CHECK_NE(rs, ZERO);
912 CHECK_NE(rt, ZERO);
913 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700914 DsFsmInstrNop(EmitI(0x16, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200915}
916
917void MipsAssembler::Bgezc(Register rt, uint16_t imm16) {
918 CHECK(IsR6());
919 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700920 DsFsmInstrNop(EmitI(0x16, rt, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200921}
922
923void MipsAssembler::Blezc(Register rt, uint16_t imm16) {
924 CHECK(IsR6());
925 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700926 DsFsmInstrNop(EmitI(0x16, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200927}
928
929void MipsAssembler::Bltuc(Register rs, Register rt, uint16_t imm16) {
930 CHECK(IsR6());
931 CHECK_NE(rs, ZERO);
932 CHECK_NE(rt, ZERO);
933 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700934 DsFsmInstrNop(EmitI(0x7, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200935}
936
937void MipsAssembler::Bgeuc(Register rs, Register rt, uint16_t imm16) {
938 CHECK(IsR6());
939 CHECK_NE(rs, ZERO);
940 CHECK_NE(rt, ZERO);
941 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700942 DsFsmInstrNop(EmitI(0x6, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200943}
944
945void MipsAssembler::Beqc(Register rs, Register rt, uint16_t imm16) {
946 CHECK(IsR6());
947 CHECK_NE(rs, ZERO);
948 CHECK_NE(rt, ZERO);
949 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700950 DsFsmInstrNop(EmitI(0x8, std::min(rs, rt), std::max(rs, rt), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200951}
952
953void MipsAssembler::Bnec(Register rs, Register rt, uint16_t imm16) {
954 CHECK(IsR6());
955 CHECK_NE(rs, ZERO);
956 CHECK_NE(rt, ZERO);
957 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700958 DsFsmInstrNop(EmitI(0x18, std::min(rs, rt), std::max(rs, rt), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200959}
960
961void MipsAssembler::Beqzc(Register rs, uint32_t imm21) {
962 CHECK(IsR6());
963 CHECK_NE(rs, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700964 DsFsmInstrNop(EmitI21(0x36, rs, imm21));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200965}
966
967void MipsAssembler::Bnezc(Register rs, uint32_t imm21) {
968 CHECK(IsR6());
969 CHECK_NE(rs, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700970 DsFsmInstrNop(EmitI21(0x3E, rs, imm21));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200971}
972
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800973void MipsAssembler::Bc1eqz(FRegister ft, uint16_t imm16) {
974 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700975 DsFsmInstrNop(EmitFI(0x11, 0x9, ft, imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800976}
977
978void MipsAssembler::Bc1nez(FRegister ft, uint16_t imm16) {
979 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700980 DsFsmInstrNop(EmitFI(0x11, 0xD, ft, imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800981}
982
983void MipsAssembler::EmitBcondR2(BranchCondition cond, Register rs, Register rt, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200984 switch (cond) {
985 case kCondLTZ:
986 CHECK_EQ(rt, ZERO);
987 Bltz(rs, imm16);
988 break;
989 case kCondGEZ:
990 CHECK_EQ(rt, ZERO);
991 Bgez(rs, imm16);
992 break;
993 case kCondLEZ:
994 CHECK_EQ(rt, ZERO);
995 Blez(rs, imm16);
996 break;
997 case kCondGTZ:
998 CHECK_EQ(rt, ZERO);
999 Bgtz(rs, imm16);
1000 break;
1001 case kCondEQ:
1002 Beq(rs, rt, imm16);
1003 break;
1004 case kCondNE:
1005 Bne(rs, rt, imm16);
1006 break;
1007 case kCondEQZ:
1008 CHECK_EQ(rt, ZERO);
1009 Beqz(rs, imm16);
1010 break;
1011 case kCondNEZ:
1012 CHECK_EQ(rt, ZERO);
1013 Bnez(rs, imm16);
1014 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001015 case kCondF:
1016 CHECK_EQ(rt, ZERO);
1017 Bc1f(static_cast<int>(rs), imm16);
1018 break;
1019 case kCondT:
1020 CHECK_EQ(rt, ZERO);
1021 Bc1t(static_cast<int>(rs), imm16);
1022 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001023 case kCondLT:
1024 case kCondGE:
1025 case kCondLE:
1026 case kCondGT:
1027 case kCondLTU:
1028 case kCondGEU:
1029 case kUncond:
1030 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
1031 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
1032 LOG(FATAL) << "Unexpected branch condition " << cond;
1033 UNREACHABLE();
1034 }
1035}
1036
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001037void MipsAssembler::EmitBcondR6(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001038 switch (cond) {
1039 case kCondLT:
1040 Bltc(rs, rt, imm16_21);
1041 break;
1042 case kCondGE:
1043 Bgec(rs, rt, imm16_21);
1044 break;
1045 case kCondLE:
1046 Bgec(rt, rs, imm16_21);
1047 break;
1048 case kCondGT:
1049 Bltc(rt, rs, imm16_21);
1050 break;
1051 case kCondLTZ:
1052 CHECK_EQ(rt, ZERO);
1053 Bltzc(rs, imm16_21);
1054 break;
1055 case kCondGEZ:
1056 CHECK_EQ(rt, ZERO);
1057 Bgezc(rs, imm16_21);
1058 break;
1059 case kCondLEZ:
1060 CHECK_EQ(rt, ZERO);
1061 Blezc(rs, imm16_21);
1062 break;
1063 case kCondGTZ:
1064 CHECK_EQ(rt, ZERO);
1065 Bgtzc(rs, imm16_21);
1066 break;
1067 case kCondEQ:
1068 Beqc(rs, rt, imm16_21);
1069 break;
1070 case kCondNE:
1071 Bnec(rs, rt, imm16_21);
1072 break;
1073 case kCondEQZ:
1074 CHECK_EQ(rt, ZERO);
1075 Beqzc(rs, imm16_21);
1076 break;
1077 case kCondNEZ:
1078 CHECK_EQ(rt, ZERO);
1079 Bnezc(rs, imm16_21);
1080 break;
1081 case kCondLTU:
1082 Bltuc(rs, rt, imm16_21);
1083 break;
1084 case kCondGEU:
1085 Bgeuc(rs, rt, imm16_21);
1086 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001087 case kCondF:
1088 CHECK_EQ(rt, ZERO);
1089 Bc1eqz(static_cast<FRegister>(rs), imm16_21);
1090 break;
1091 case kCondT:
1092 CHECK_EQ(rt, ZERO);
1093 Bc1nez(static_cast<FRegister>(rs), imm16_21);
1094 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001095 case kUncond:
1096 LOG(FATAL) << "Unexpected branch condition " << cond;
1097 UNREACHABLE();
1098 }
jeffhao7fbee072012-08-24 17:56:54 -07001099}
1100
1101void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001102 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x0), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001103}
1104
1105void MipsAssembler::SubS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001106 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001107}
1108
1109void MipsAssembler::MulS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001110 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x2), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001111}
1112
1113void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001114 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x3), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001115}
1116
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001117void MipsAssembler::AddD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001118 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x0), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001119}
1120
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001121void MipsAssembler::SubD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001122 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001123}
1124
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001125void MipsAssembler::MulD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001126 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x2), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001127}
1128
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001129void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001130 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x3), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001131}
1132
Chris Larsenb74353a2015-11-20 09:07:09 -08001133void MipsAssembler::SqrtS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001134 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x4), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001135}
1136
1137void MipsAssembler::SqrtD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001138 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x4), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001139}
1140
1141void MipsAssembler::AbsS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001142 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x5), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001143}
1144
1145void MipsAssembler::AbsD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001146 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x5), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001147}
1148
jeffhao7fbee072012-08-24 17:56:54 -07001149void MipsAssembler::MovS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001150 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6), fd, fs, fs);
jeffhao7fbee072012-08-24 17:56:54 -07001151}
1152
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001153void MipsAssembler::MovD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001154 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x6), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001155}
1156
1157void MipsAssembler::NegS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001158 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x7), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001159}
1160
1161void MipsAssembler::NegD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001162 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001163}
1164
Chris Larsenb74353a2015-11-20 09:07:09 -08001165void MipsAssembler::CunS(FRegister fs, FRegister ft) {
1166 CunS(0, fs, ft);
1167}
1168
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001169void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) {
1170 CHECK(!IsR6());
1171 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001172 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x31), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001173}
1174
Chris Larsenb74353a2015-11-20 09:07:09 -08001175void MipsAssembler::CeqS(FRegister fs, FRegister ft) {
1176 CeqS(0, fs, ft);
1177}
1178
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001179void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) {
1180 CHECK(!IsR6());
1181 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001182 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x32), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001183}
1184
Chris Larsenb74353a2015-11-20 09:07:09 -08001185void MipsAssembler::CueqS(FRegister fs, FRegister ft) {
1186 CueqS(0, fs, ft);
1187}
1188
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001189void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) {
1190 CHECK(!IsR6());
1191 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001192 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x33), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001193}
1194
Chris Larsenb74353a2015-11-20 09:07:09 -08001195void MipsAssembler::ColtS(FRegister fs, FRegister ft) {
1196 ColtS(0, fs, ft);
1197}
1198
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001199void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) {
1200 CHECK(!IsR6());
1201 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001202 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x34), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001203}
1204
Chris Larsenb74353a2015-11-20 09:07:09 -08001205void MipsAssembler::CultS(FRegister fs, FRegister ft) {
1206 CultS(0, fs, ft);
1207}
1208
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001209void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) {
1210 CHECK(!IsR6());
1211 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001212 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x35), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001213}
1214
Chris Larsenb74353a2015-11-20 09:07:09 -08001215void MipsAssembler::ColeS(FRegister fs, FRegister ft) {
1216 ColeS(0, fs, ft);
1217}
1218
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001219void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) {
1220 CHECK(!IsR6());
1221 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001222 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x36), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001223}
1224
Chris Larsenb74353a2015-11-20 09:07:09 -08001225void MipsAssembler::CuleS(FRegister fs, FRegister ft) {
1226 CuleS(0, fs, ft);
1227}
1228
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001229void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) {
1230 CHECK(!IsR6());
1231 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001232 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x37), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001233}
1234
Chris Larsenb74353a2015-11-20 09:07:09 -08001235void MipsAssembler::CunD(FRegister fs, FRegister ft) {
1236 CunD(0, fs, ft);
1237}
1238
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001239void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) {
1240 CHECK(!IsR6());
1241 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001242 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x31), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001243}
1244
Chris Larsenb74353a2015-11-20 09:07:09 -08001245void MipsAssembler::CeqD(FRegister fs, FRegister ft) {
1246 CeqD(0, fs, ft);
1247}
1248
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001249void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) {
1250 CHECK(!IsR6());
1251 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001252 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x32), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001253}
1254
Chris Larsenb74353a2015-11-20 09:07:09 -08001255void MipsAssembler::CueqD(FRegister fs, FRegister ft) {
1256 CueqD(0, fs, ft);
1257}
1258
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001259void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) {
1260 CHECK(!IsR6());
1261 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001262 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x33), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001263}
1264
Chris Larsenb74353a2015-11-20 09:07:09 -08001265void MipsAssembler::ColtD(FRegister fs, FRegister ft) {
1266 ColtD(0, fs, ft);
1267}
1268
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001269void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) {
1270 CHECK(!IsR6());
1271 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001272 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x34), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001273}
1274
Chris Larsenb74353a2015-11-20 09:07:09 -08001275void MipsAssembler::CultD(FRegister fs, FRegister ft) {
1276 CultD(0, fs, ft);
1277}
1278
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001279void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) {
1280 CHECK(!IsR6());
1281 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001282 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x35), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001283}
1284
Chris Larsenb74353a2015-11-20 09:07:09 -08001285void MipsAssembler::ColeD(FRegister fs, FRegister ft) {
1286 ColeD(0, fs, ft);
1287}
1288
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001289void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) {
1290 CHECK(!IsR6());
1291 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001292 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x36), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001293}
1294
Chris Larsenb74353a2015-11-20 09:07:09 -08001295void MipsAssembler::CuleD(FRegister fs, FRegister ft) {
1296 CuleD(0, fs, ft);
1297}
1298
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001299void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) {
1300 CHECK(!IsR6());
1301 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001302 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x37), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001303}
1304
1305void MipsAssembler::CmpUnS(FRegister fd, FRegister fs, FRegister ft) {
1306 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001307 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x01), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001308}
1309
1310void MipsAssembler::CmpEqS(FRegister fd, FRegister fs, FRegister ft) {
1311 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001312 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x02), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001313}
1314
1315void MipsAssembler::CmpUeqS(FRegister fd, FRegister fs, FRegister ft) {
1316 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001317 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x03), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001318}
1319
1320void MipsAssembler::CmpLtS(FRegister fd, FRegister fs, FRegister ft) {
1321 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001322 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x04), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001323}
1324
1325void MipsAssembler::CmpUltS(FRegister fd, FRegister fs, FRegister ft) {
1326 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001327 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x05), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001328}
1329
1330void MipsAssembler::CmpLeS(FRegister fd, FRegister fs, FRegister ft) {
1331 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001332 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x06), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001333}
1334
1335void MipsAssembler::CmpUleS(FRegister fd, FRegister fs, FRegister ft) {
1336 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001337 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x07), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001338}
1339
1340void MipsAssembler::CmpOrS(FRegister fd, FRegister fs, FRegister ft) {
1341 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001342 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x11), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001343}
1344
1345void MipsAssembler::CmpUneS(FRegister fd, FRegister fs, FRegister ft) {
1346 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001347 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x12), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001348}
1349
1350void MipsAssembler::CmpNeS(FRegister fd, FRegister fs, FRegister ft) {
1351 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001352 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x13), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001353}
1354
1355void MipsAssembler::CmpUnD(FRegister fd, FRegister fs, FRegister ft) {
1356 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001357 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x01), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001358}
1359
1360void MipsAssembler::CmpEqD(FRegister fd, FRegister fs, FRegister ft) {
1361 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001362 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x02), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001363}
1364
1365void MipsAssembler::CmpUeqD(FRegister fd, FRegister fs, FRegister ft) {
1366 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001367 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x03), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001368}
1369
1370void MipsAssembler::CmpLtD(FRegister fd, FRegister fs, FRegister ft) {
1371 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001372 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x04), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001373}
1374
1375void MipsAssembler::CmpUltD(FRegister fd, FRegister fs, FRegister ft) {
1376 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001377 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x05), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001378}
1379
1380void MipsAssembler::CmpLeD(FRegister fd, FRegister fs, FRegister ft) {
1381 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001382 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x06), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001383}
1384
1385void MipsAssembler::CmpUleD(FRegister fd, FRegister fs, FRegister ft) {
1386 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001387 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x07), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001388}
1389
1390void MipsAssembler::CmpOrD(FRegister fd, FRegister fs, FRegister ft) {
1391 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001392 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x11), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001393}
1394
1395void MipsAssembler::CmpUneD(FRegister fd, FRegister fs, FRegister ft) {
1396 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001397 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x12), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001398}
1399
1400void MipsAssembler::CmpNeD(FRegister fd, FRegister fs, FRegister ft) {
1401 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001402 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x13), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001403}
1404
1405void MipsAssembler::Movf(Register rd, Register rs, int cc) {
1406 CHECK(!IsR6());
1407 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001408 DsFsmInstrRrrc(EmitR(0, rs, static_cast<Register>(cc << 2), rd, 0, 0x01), rd, rs, cc);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001409}
1410
1411void MipsAssembler::Movt(Register rd, Register rs, int cc) {
1412 CHECK(!IsR6());
1413 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001414 DsFsmInstrRrrc(EmitR(0, rs, static_cast<Register>((cc << 2) | 1), rd, 0, 0x01), rd, rs, cc);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001415}
1416
Chris Larsenb74353a2015-11-20 09:07:09 -08001417void MipsAssembler::MovfS(FRegister fd, FRegister fs, int cc) {
1418 CHECK(!IsR6());
1419 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001420 DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast<FRegister>(cc << 2), fs, fd, 0x11), fd, fs, cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001421}
1422
1423void MipsAssembler::MovfD(FRegister fd, FRegister fs, int cc) {
1424 CHECK(!IsR6());
1425 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001426 DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast<FRegister>(cc << 2), fs, fd, 0x11), fd, fs, cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001427}
1428
1429void MipsAssembler::MovtS(FRegister fd, FRegister fs, int cc) {
1430 CHECK(!IsR6());
1431 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001432 DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11),
1433 fd,
1434 fs,
1435 cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001436}
1437
1438void MipsAssembler::MovtD(FRegister fd, FRegister fs, int cc) {
1439 CHECK(!IsR6());
1440 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001441 DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11),
1442 fd,
1443 fs,
1444 cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001445}
1446
1447void MipsAssembler::SelS(FRegister fd, FRegister fs, FRegister ft) {
1448 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001449 DsFsmInstrFfff(EmitFR(0x11, 0x10, ft, fs, fd, 0x10), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001450}
1451
1452void MipsAssembler::SelD(FRegister fd, FRegister fs, FRegister ft) {
1453 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001454 DsFsmInstrFfff(EmitFR(0x11, 0x11, ft, fs, fd, 0x10), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001455}
1456
1457void MipsAssembler::ClassS(FRegister fd, FRegister fs) {
1458 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001459 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x1b), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001460}
1461
1462void MipsAssembler::ClassD(FRegister fd, FRegister fs) {
1463 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001464 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x1b), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001465}
1466
1467void MipsAssembler::MinS(FRegister fd, FRegister fs, FRegister ft) {
1468 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001469 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1c), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001470}
1471
1472void MipsAssembler::MinD(FRegister fd, FRegister fs, FRegister ft) {
1473 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001474 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1c), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001475}
1476
1477void MipsAssembler::MaxS(FRegister fd, FRegister fs, FRegister ft) {
1478 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001479 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1e), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001480}
1481
1482void MipsAssembler::MaxD(FRegister fd, FRegister fs, FRegister ft) {
1483 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001484 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1e), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001485}
1486
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001487void MipsAssembler::TruncLS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001488 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x09), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001489}
1490
1491void MipsAssembler::TruncLD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001492 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x09), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001493}
1494
1495void MipsAssembler::TruncWS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001496 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x0D), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001497}
1498
1499void MipsAssembler::TruncWD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001500 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x0D), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001501}
1502
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001503void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001504 DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001505}
1506
1507void MipsAssembler::Cvtdw(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001508 DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001509}
1510
1511void MipsAssembler::Cvtsd(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001512 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001513}
1514
1515void MipsAssembler::Cvtds(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001516 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
jeffhao7fbee072012-08-24 17:56:54 -07001517}
1518
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001519void MipsAssembler::Cvtsl(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001520 DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001521}
1522
1523void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001524 DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001525}
1526
Chris Larsenb74353a2015-11-20 09:07:09 -08001527void MipsAssembler::FloorWS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001528 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0xf), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001529}
1530
1531void MipsAssembler::FloorWD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001532 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0xf), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001533}
1534
jeffhao7fbee072012-08-24 17:56:54 -07001535void MipsAssembler::Mfc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001536 DsFsmInstrRf(EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1537 rt,
1538 fs);
jeffhao7fbee072012-08-24 17:56:54 -07001539}
1540
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001541void MipsAssembler::Mtc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001542 DsFsmInstrFr(EmitFR(0x11, 0x04, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1543 fs,
1544 rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001545}
1546
1547void MipsAssembler::Mfhc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001548 DsFsmInstrRf(EmitFR(0x11, 0x03, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1549 rt,
1550 fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001551}
1552
1553void MipsAssembler::Mthc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001554 DsFsmInstrFr(EmitFR(0x11, 0x07, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1555 fs,
1556 rt);
jeffhao7fbee072012-08-24 17:56:54 -07001557}
1558
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001559void MipsAssembler::MoveFromFpuHigh(Register rt, FRegister fs) {
1560 if (Is32BitFPU()) {
1561 CHECK_EQ(fs % 2, 0) << fs;
1562 Mfc1(rt, static_cast<FRegister>(fs + 1));
1563 } else {
1564 Mfhc1(rt, fs);
1565 }
1566}
1567
1568void MipsAssembler::MoveToFpuHigh(Register rt, FRegister fs) {
1569 if (Is32BitFPU()) {
1570 CHECK_EQ(fs % 2, 0) << fs;
1571 Mtc1(rt, static_cast<FRegister>(fs + 1));
1572 } else {
1573 Mthc1(rt, fs);
1574 }
1575}
1576
jeffhao7fbee072012-08-24 17:56:54 -07001577void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001578 DsFsmInstrFr(EmitI(0x31, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001579}
1580
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001581void MipsAssembler::Ldc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001582 DsFsmInstrFr(EmitI(0x35, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001583}
1584
1585void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001586 DsFsmInstrFR(EmitI(0x39, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001587}
1588
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001589void MipsAssembler::Sdc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001590 DsFsmInstrFR(EmitI(0x3d, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001591}
1592
1593void MipsAssembler::Break() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001594 DsFsmInstrNop(EmitR(0, ZERO, ZERO, ZERO, 0, 0xD));
jeffhao7fbee072012-08-24 17:56:54 -07001595}
1596
jeffhao07030602012-09-26 14:33:14 -07001597void MipsAssembler::Nop() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001598 DsFsmInstrNop(EmitR(0x0, ZERO, ZERO, ZERO, 0, 0x0));
1599}
1600
1601void MipsAssembler::NopIfNoReordering() {
1602 if (!reordering_) {
1603 Nop();
1604 }
jeffhao07030602012-09-26 14:33:14 -07001605}
1606
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001607void MipsAssembler::Move(Register rd, Register rs) {
1608 Or(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001609}
1610
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001611void MipsAssembler::Clear(Register rd) {
1612 Move(rd, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001613}
1614
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001615void MipsAssembler::Not(Register rd, Register rs) {
1616 Nor(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001617}
1618
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001619void MipsAssembler::Push(Register rs) {
1620 IncreaseFrameSize(kMipsWordSize);
1621 Sw(rs, SP, 0);
jeffhao7fbee072012-08-24 17:56:54 -07001622}
1623
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001624void MipsAssembler::Pop(Register rd) {
1625 Lw(rd, SP, 0);
1626 DecreaseFrameSize(kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07001627}
1628
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001629void MipsAssembler::PopAndReturn(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001630 bool reordering = SetReorder(false);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001631 Lw(rd, SP, 0);
1632 Jr(rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001633 DecreaseFrameSize(kMipsWordSize); // Single instruction in delay slot.
1634 SetReorder(reordering);
jeffhao7fbee072012-08-24 17:56:54 -07001635}
1636
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001637void MipsAssembler::LoadConst32(Register rd, int32_t value) {
1638 if (IsUint<16>(value)) {
1639 // Use OR with (unsigned) immediate to encode 16b unsigned int.
1640 Ori(rd, ZERO, value);
1641 } else if (IsInt<16>(value)) {
1642 // Use ADD with (signed) immediate to encode 16b signed int.
1643 Addiu(rd, ZERO, value);
jeffhao7fbee072012-08-24 17:56:54 -07001644 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001645 Lui(rd, High16Bits(value));
1646 if (value & 0xFFFF)
1647 Ori(rd, rd, Low16Bits(value));
1648 }
1649}
1650
1651void MipsAssembler::LoadConst64(Register reg_hi, Register reg_lo, int64_t value) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001652 uint32_t low = Low32Bits(value);
1653 uint32_t high = High32Bits(value);
1654 LoadConst32(reg_lo, low);
1655 if (high != low) {
1656 LoadConst32(reg_hi, high);
1657 } else {
1658 Move(reg_hi, reg_lo);
1659 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001660}
1661
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001662void MipsAssembler::LoadSConst32(FRegister r, int32_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001663 if (value == 0) {
1664 temp = ZERO;
1665 } else {
1666 LoadConst32(temp, value);
1667 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001668 Mtc1(temp, r);
1669}
1670
1671void MipsAssembler::LoadDConst64(FRegister rd, int64_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001672 uint32_t low = Low32Bits(value);
1673 uint32_t high = High32Bits(value);
1674 if (low == 0) {
1675 Mtc1(ZERO, rd);
1676 } else {
1677 LoadConst32(temp, low);
1678 Mtc1(temp, rd);
1679 }
1680 if (high == 0) {
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001681 MoveToFpuHigh(ZERO, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001682 } else {
1683 LoadConst32(temp, high);
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001684 MoveToFpuHigh(temp, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001685 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001686}
1687
1688void MipsAssembler::Addiu32(Register rt, Register rs, int32_t value, Register temp) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001689 CHECK_NE(rs, temp); // Must not overwrite the register `rs` while loading `value`.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001690 if (IsInt<16>(value)) {
1691 Addiu(rt, rs, value);
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001692 } else if (IsR6()) {
1693 int16_t high = High16Bits(value);
1694 int16_t low = Low16Bits(value);
1695 high += (low < 0) ? 1 : 0; // Account for sign extension in addiu.
1696 if (low != 0) {
1697 Aui(temp, rs, high);
1698 Addiu(rt, temp, low);
1699 } else {
1700 Aui(rt, rs, high);
1701 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001702 } else {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001703 // Do not load the whole 32-bit `value` if it can be represented as
1704 // a sum of two 16-bit signed values. This can save an instruction.
1705 constexpr int32_t kMinValueForSimpleAdjustment = std::numeric_limits<int16_t>::min() * 2;
1706 constexpr int32_t kMaxValueForSimpleAdjustment = std::numeric_limits<int16_t>::max() * 2;
1707 if (0 <= value && value <= kMaxValueForSimpleAdjustment) {
1708 Addiu(temp, rs, kMaxValueForSimpleAdjustment / 2);
1709 Addiu(rt, temp, value - kMaxValueForSimpleAdjustment / 2);
1710 } else if (kMinValueForSimpleAdjustment <= value && value < 0) {
1711 Addiu(temp, rs, kMinValueForSimpleAdjustment / 2);
1712 Addiu(rt, temp, value - kMinValueForSimpleAdjustment / 2);
1713 } else {
1714 // Now that all shorter options have been exhausted, load the full 32-bit value.
1715 LoadConst32(temp, value);
1716 Addu(rt, rs, temp);
1717 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001718 }
1719}
1720
1721void MipsAssembler::Branch::InitShortOrLong(MipsAssembler::Branch::OffsetBits offset_size,
1722 MipsAssembler::Branch::Type short_type,
1723 MipsAssembler::Branch::Type long_type) {
1724 type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type;
1725}
1726
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001727void MipsAssembler::Branch::InitializeType(bool is_call, bool is_literal, bool is_r6) {
1728 CHECK_EQ(is_call && is_literal, false);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001729 OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_);
1730 if (is_r6) {
1731 // R6
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001732 if (is_literal) {
1733 CHECK(!IsResolved());
1734 type_ = kR6Literal;
1735 } else if (is_call) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001736 InitShortOrLong(offset_size, kR6Call, kR6LongCall);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001737 } else {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001738 switch (condition_) {
1739 case kUncond:
1740 InitShortOrLong(offset_size, kR6UncondBranch, kR6LongUncondBranch);
1741 break;
1742 case kCondEQZ:
1743 case kCondNEZ:
1744 // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions.
1745 type_ = (offset_size <= kOffset23) ? kR6CondBranch : kR6LongCondBranch;
1746 break;
1747 default:
1748 InitShortOrLong(offset_size, kR6CondBranch, kR6LongCondBranch);
1749 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001750 }
1751 }
1752 } else {
1753 // R2
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001754 if (is_literal) {
1755 CHECK(!IsResolved());
1756 type_ = kLiteral;
1757 } else if (is_call) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001758 InitShortOrLong(offset_size, kCall, kLongCall);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001759 } else {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001760 switch (condition_) {
1761 case kUncond:
1762 InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch);
1763 break;
1764 default:
1765 InitShortOrLong(offset_size, kCondBranch, kLongCondBranch);
1766 break;
1767 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001768 }
1769 }
1770 old_type_ = type_;
1771}
1772
1773bool MipsAssembler::Branch::IsNop(BranchCondition condition, Register lhs, Register rhs) {
1774 switch (condition) {
1775 case kCondLT:
1776 case kCondGT:
1777 case kCondNE:
1778 case kCondLTU:
1779 return lhs == rhs;
1780 default:
1781 return false;
1782 }
1783}
1784
1785bool MipsAssembler::Branch::IsUncond(BranchCondition condition, Register lhs, Register rhs) {
1786 switch (condition) {
1787 case kUncond:
1788 return true;
1789 case kCondGE:
1790 case kCondLE:
1791 case kCondEQ:
1792 case kCondGEU:
1793 return lhs == rhs;
1794 default:
1795 return false;
1796 }
1797}
1798
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001799MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target, bool is_call)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001800 : old_location_(location),
1801 location_(location),
1802 target_(target),
1803 lhs_reg_(0),
1804 rhs_reg_(0),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001805 condition_(kUncond),
1806 delayed_instruction_(kUnfilledDelaySlot) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001807 InitializeType(is_call, /* is_literal */ false, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001808}
1809
1810MipsAssembler::Branch::Branch(bool is_r6,
1811 uint32_t location,
1812 uint32_t target,
1813 MipsAssembler::BranchCondition condition,
1814 Register lhs_reg,
1815 Register rhs_reg)
1816 : old_location_(location),
1817 location_(location),
1818 target_(target),
1819 lhs_reg_(lhs_reg),
1820 rhs_reg_(rhs_reg),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001821 condition_(condition),
1822 delayed_instruction_(kUnfilledDelaySlot) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001823 CHECK_NE(condition, kUncond);
1824 switch (condition) {
1825 case kCondLT:
1826 case kCondGE:
1827 case kCondLE:
1828 case kCondGT:
1829 case kCondLTU:
1830 case kCondGEU:
1831 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
1832 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
1833 // We leave this up to the caller.
1834 CHECK(is_r6);
1835 FALLTHROUGH_INTENDED;
1836 case kCondEQ:
1837 case kCondNE:
1838 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1839 // To compare with 0, use dedicated kCond*Z conditions.
1840 CHECK_NE(lhs_reg, ZERO);
1841 CHECK_NE(rhs_reg, ZERO);
1842 break;
1843 case kCondLTZ:
1844 case kCondGEZ:
1845 case kCondLEZ:
1846 case kCondGTZ:
1847 case kCondEQZ:
1848 case kCondNEZ:
1849 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1850 CHECK_NE(lhs_reg, ZERO);
1851 CHECK_EQ(rhs_reg, ZERO);
1852 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001853 case kCondF:
1854 case kCondT:
1855 CHECK_EQ(rhs_reg, ZERO);
1856 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001857 case kUncond:
1858 UNREACHABLE();
1859 }
1860 CHECK(!IsNop(condition, lhs_reg, rhs_reg));
1861 if (IsUncond(condition, lhs_reg, rhs_reg)) {
1862 // Branch condition is always true, make the branch unconditional.
1863 condition_ = kUncond;
1864 }
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001865 InitializeType(/* is_call */ false, /* is_literal */ false, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001866}
1867
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001868MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, Register dest_reg, Register base_reg)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001869 : old_location_(location),
1870 location_(location),
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001871 target_(kUnresolved),
1872 lhs_reg_(dest_reg),
1873 rhs_reg_(base_reg),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001874 condition_(kUncond),
1875 delayed_instruction_(kUnfilledDelaySlot) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001876 CHECK_NE(dest_reg, ZERO);
1877 if (is_r6) {
1878 CHECK_EQ(base_reg, ZERO);
1879 } else {
1880 CHECK_NE(base_reg, ZERO);
1881 }
1882 InitializeType(/* is_call */ false, /* is_literal */ true, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001883}
1884
1885MipsAssembler::BranchCondition MipsAssembler::Branch::OppositeCondition(
1886 MipsAssembler::BranchCondition cond) {
1887 switch (cond) {
1888 case kCondLT:
1889 return kCondGE;
1890 case kCondGE:
1891 return kCondLT;
1892 case kCondLE:
1893 return kCondGT;
1894 case kCondGT:
1895 return kCondLE;
1896 case kCondLTZ:
1897 return kCondGEZ;
1898 case kCondGEZ:
1899 return kCondLTZ;
1900 case kCondLEZ:
1901 return kCondGTZ;
1902 case kCondGTZ:
1903 return kCondLEZ;
1904 case kCondEQ:
1905 return kCondNE;
1906 case kCondNE:
1907 return kCondEQ;
1908 case kCondEQZ:
1909 return kCondNEZ;
1910 case kCondNEZ:
1911 return kCondEQZ;
1912 case kCondLTU:
1913 return kCondGEU;
1914 case kCondGEU:
1915 return kCondLTU;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001916 case kCondF:
1917 return kCondT;
1918 case kCondT:
1919 return kCondF;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001920 case kUncond:
1921 LOG(FATAL) << "Unexpected branch condition " << cond;
1922 }
1923 UNREACHABLE();
1924}
1925
1926MipsAssembler::Branch::Type MipsAssembler::Branch::GetType() const {
1927 return type_;
1928}
1929
1930MipsAssembler::BranchCondition MipsAssembler::Branch::GetCondition() const {
1931 return condition_;
1932}
1933
1934Register MipsAssembler::Branch::GetLeftRegister() const {
1935 return static_cast<Register>(lhs_reg_);
1936}
1937
1938Register MipsAssembler::Branch::GetRightRegister() const {
1939 return static_cast<Register>(rhs_reg_);
1940}
1941
1942uint32_t MipsAssembler::Branch::GetTarget() const {
1943 return target_;
1944}
1945
1946uint32_t MipsAssembler::Branch::GetLocation() const {
1947 return location_;
1948}
1949
1950uint32_t MipsAssembler::Branch::GetOldLocation() const {
1951 return old_location_;
1952}
1953
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001954uint32_t MipsAssembler::Branch::GetPrecedingInstructionLength(Type type) const {
1955 // Short branches with delay slots always consist of two instructions, the branch
1956 // and the delay slot, irrespective of whether the delay slot is filled with a
1957 // useful instruction or not.
1958 // Long composite branches may have a length longer by one instruction than
1959 // specified in branch_info_[].length. This happens when an instruction is taken
1960 // to fill the short branch delay slot, but the branch eventually becomes long
1961 // and formally has no delay slot to fill. This instruction is placed at the
1962 // beginning of the long composite branch and this needs to be accounted for in
1963 // the branch length and the location of the offset encoded in the branch.
1964 switch (type) {
1965 case kLongUncondBranch:
1966 case kLongCondBranch:
1967 case kLongCall:
1968 case kR6LongCondBranch:
1969 return (delayed_instruction_ != kUnfilledDelaySlot &&
1970 delayed_instruction_ != kUnfillableDelaySlot) ? 1 : 0;
1971 default:
1972 return 0;
1973 }
1974}
1975
1976uint32_t MipsAssembler::Branch::GetPrecedingInstructionSize(Type type) const {
1977 return GetPrecedingInstructionLength(type) * sizeof(uint32_t);
1978}
1979
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001980uint32_t MipsAssembler::Branch::GetLength() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001981 return GetPrecedingInstructionLength(type_) + branch_info_[type_].length;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001982}
1983
1984uint32_t MipsAssembler::Branch::GetOldLength() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001985 return GetPrecedingInstructionLength(old_type_) + branch_info_[old_type_].length;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001986}
1987
1988uint32_t MipsAssembler::Branch::GetSize() const {
1989 return GetLength() * sizeof(uint32_t);
1990}
1991
1992uint32_t MipsAssembler::Branch::GetOldSize() const {
1993 return GetOldLength() * sizeof(uint32_t);
1994}
1995
1996uint32_t MipsAssembler::Branch::GetEndLocation() const {
1997 return GetLocation() + GetSize();
1998}
1999
2000uint32_t MipsAssembler::Branch::GetOldEndLocation() const {
2001 return GetOldLocation() + GetOldSize();
2002}
2003
2004bool MipsAssembler::Branch::IsLong() const {
2005 switch (type_) {
2006 // R2 short branches.
2007 case kUncondBranch:
2008 case kCondBranch:
2009 case kCall:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002010 // R2 near literal.
2011 case kLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002012 // R6 short branches.
2013 case kR6UncondBranch:
2014 case kR6CondBranch:
2015 case kR6Call:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002016 // R6 near literal.
2017 case kR6Literal:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002018 return false;
2019 // R2 long branches.
2020 case kLongUncondBranch:
2021 case kLongCondBranch:
2022 case kLongCall:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002023 // R2 far literal.
2024 case kFarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002025 // R6 long branches.
2026 case kR6LongUncondBranch:
2027 case kR6LongCondBranch:
2028 case kR6LongCall:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002029 // R6 far literal.
2030 case kR6FarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002031 return true;
2032 }
2033 UNREACHABLE();
2034}
2035
2036bool MipsAssembler::Branch::IsResolved() const {
2037 return target_ != kUnresolved;
2038}
2039
2040MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSize() const {
2041 OffsetBits offset_size =
2042 (type_ == kR6CondBranch && (condition_ == kCondEQZ || condition_ == kCondNEZ))
2043 ? kOffset23
2044 : branch_info_[type_].offset_size;
2045 return offset_size;
2046}
2047
2048MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSizeNeeded(uint32_t location,
2049 uint32_t target) {
2050 // For unresolved targets assume the shortest encoding
2051 // (later it will be made longer if needed).
2052 if (target == kUnresolved)
2053 return kOffset16;
2054 int64_t distance = static_cast<int64_t>(target) - location;
2055 // To simplify calculations in composite branches consisting of multiple instructions
2056 // bump up the distance by a value larger than the max byte size of a composite branch.
2057 distance += (distance >= 0) ? kMaxBranchSize : -kMaxBranchSize;
2058 if (IsInt<kOffset16>(distance))
2059 return kOffset16;
2060 else if (IsInt<kOffset18>(distance))
2061 return kOffset18;
2062 else if (IsInt<kOffset21>(distance))
2063 return kOffset21;
2064 else if (IsInt<kOffset23>(distance))
2065 return kOffset23;
2066 else if (IsInt<kOffset28>(distance))
2067 return kOffset28;
2068 return kOffset32;
2069}
2070
2071void MipsAssembler::Branch::Resolve(uint32_t target) {
2072 target_ = target;
2073}
2074
2075void MipsAssembler::Branch::Relocate(uint32_t expand_location, uint32_t delta) {
2076 if (location_ > expand_location) {
2077 location_ += delta;
2078 }
2079 if (!IsResolved()) {
2080 return; // Don't know the target yet.
2081 }
2082 if (target_ > expand_location) {
2083 target_ += delta;
2084 }
2085}
2086
2087void MipsAssembler::Branch::PromoteToLong() {
2088 switch (type_) {
2089 // R2 short branches.
2090 case kUncondBranch:
2091 type_ = kLongUncondBranch;
2092 break;
2093 case kCondBranch:
2094 type_ = kLongCondBranch;
2095 break;
2096 case kCall:
2097 type_ = kLongCall;
2098 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002099 // R2 near literal.
2100 case kLiteral:
2101 type_ = kFarLiteral;
2102 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002103 // R6 short branches.
2104 case kR6UncondBranch:
2105 type_ = kR6LongUncondBranch;
2106 break;
2107 case kR6CondBranch:
2108 type_ = kR6LongCondBranch;
2109 break;
2110 case kR6Call:
2111 type_ = kR6LongCall;
2112 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002113 // R6 near literal.
2114 case kR6Literal:
2115 type_ = kR6FarLiteral;
2116 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002117 default:
2118 // Note: 'type_' is already long.
2119 break;
2120 }
2121 CHECK(IsLong());
2122}
2123
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002124uint32_t MipsAssembler::GetBranchLocationOrPcRelBase(const MipsAssembler::Branch* branch) const {
2125 switch (branch->GetType()) {
2126 case Branch::kLiteral:
2127 case Branch::kFarLiteral:
2128 return GetLabelLocation(&pc_rel_base_label_);
2129 default:
2130 return branch->GetLocation();
2131 }
2132}
2133
2134uint32_t MipsAssembler::Branch::PromoteIfNeeded(uint32_t location, uint32_t max_short_distance) {
2135 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 literals or
2136 // `this->GetLocation()` for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002137 // If the branch is still unresolved or already long, nothing to do.
2138 if (IsLong() || !IsResolved()) {
2139 return 0;
2140 }
2141 // Promote the short branch to long if the offset size is too small
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002142 // to hold the distance between location and target_.
2143 if (GetOffsetSizeNeeded(location, target_) > GetOffsetSize()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002144 PromoteToLong();
2145 uint32_t old_size = GetOldSize();
2146 uint32_t new_size = GetSize();
2147 CHECK_GT(new_size, old_size);
2148 return new_size - old_size;
2149 }
2150 // The following logic is for debugging/testing purposes.
2151 // Promote some short branches to long when it's not really required.
2152 if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max())) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002153 int64_t distance = static_cast<int64_t>(target_) - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002154 distance = (distance >= 0) ? distance : -distance;
2155 if (distance >= max_short_distance) {
2156 PromoteToLong();
2157 uint32_t old_size = GetOldSize();
2158 uint32_t new_size = GetSize();
2159 CHECK_GT(new_size, old_size);
2160 return new_size - old_size;
2161 }
2162 }
2163 return 0;
2164}
2165
2166uint32_t MipsAssembler::Branch::GetOffsetLocation() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002167 return location_ + GetPrecedingInstructionSize(type_) +
2168 branch_info_[type_].instr_offset * sizeof(uint32_t);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002169}
2170
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002171uint32_t MipsAssembler::GetBranchOrPcRelBaseForEncoding(const MipsAssembler::Branch* branch) const {
2172 switch (branch->GetType()) {
2173 case Branch::kLiteral:
2174 case Branch::kFarLiteral:
2175 return GetLabelLocation(&pc_rel_base_label_);
2176 default:
2177 return branch->GetOffsetLocation() +
2178 Branch::branch_info_[branch->GetType()].pc_org * sizeof(uint32_t);
2179 }
2180}
2181
2182uint32_t MipsAssembler::Branch::GetOffset(uint32_t location) const {
2183 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 literals or
2184 // `this->GetOffsetLocation() + branch_info_[this->GetType()].pc_org * sizeof(uint32_t)`
2185 // for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002186 CHECK(IsResolved());
2187 uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize());
2188 // Calculate the byte distance between instructions and also account for
2189 // different PC-relative origins.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002190 uint32_t offset = target_ - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002191 // Prepare the offset for encoding into the instruction(s).
2192 offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift;
2193 return offset;
2194}
2195
2196MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) {
2197 CHECK_LT(branch_id, branches_.size());
2198 return &branches_[branch_id];
2199}
2200
2201const MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) const {
2202 CHECK_LT(branch_id, branches_.size());
2203 return &branches_[branch_id];
2204}
2205
2206void MipsAssembler::Bind(MipsLabel* label) {
2207 CHECK(!label->IsBound());
2208 uint32_t bound_pc = buffer_.Size();
2209
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002210 // Make the delay slot FSM aware of the new label.
2211 DsFsmLabel();
2212
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002213 // Walk the list of branches referring to and preceding this label.
2214 // Store the previously unknown target addresses in them.
2215 while (label->IsLinked()) {
2216 uint32_t branch_id = label->Position();
2217 Branch* branch = GetBranch(branch_id);
2218 branch->Resolve(bound_pc);
2219
2220 uint32_t branch_location = branch->GetLocation();
2221 // Extract the location of the previous branch in the list (walking the list backwards;
2222 // the previous branch ID was stored in the space reserved for this branch).
2223 uint32_t prev = buffer_.Load<uint32_t>(branch_location);
2224
2225 // On to the previous branch in the list...
2226 label->position_ = prev;
2227 }
2228
2229 // Now make the label object contain its own location (relative to the end of the preceding
2230 // branch, if any; it will be used by the branches referring to and following this label).
2231 label->prev_branch_id_plus_one_ = branches_.size();
2232 if (label->prev_branch_id_plus_one_) {
2233 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
2234 const Branch* branch = GetBranch(branch_id);
2235 bound_pc -= branch->GetEndLocation();
2236 }
2237 label->BindTo(bound_pc);
2238}
2239
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002240uint32_t MipsAssembler::GetLabelLocation(const MipsLabel* label) const {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002241 CHECK(label->IsBound());
2242 uint32_t target = label->Position();
2243 if (label->prev_branch_id_plus_one_) {
2244 // Get label location based on the branch preceding it.
2245 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
2246 const Branch* branch = GetBranch(branch_id);
2247 target += branch->GetEndLocation();
2248 }
2249 return target;
2250}
2251
2252uint32_t MipsAssembler::GetAdjustedPosition(uint32_t old_position) {
2253 // We can reconstruct the adjustment by going through all the branches from the beginning
2254 // up to the old_position. Since we expect AdjustedPosition() to be called in a loop
2255 // with increasing old_position, we can use the data from last AdjustedPosition() to
2256 // continue where we left off and the whole loop should be O(m+n) where m is the number
2257 // of positions to adjust and n is the number of branches.
2258 if (old_position < last_old_position_) {
2259 last_position_adjustment_ = 0;
2260 last_old_position_ = 0;
2261 last_branch_id_ = 0;
2262 }
2263 while (last_branch_id_ != branches_.size()) {
2264 const Branch* branch = GetBranch(last_branch_id_);
2265 if (branch->GetLocation() >= old_position + last_position_adjustment_) {
2266 break;
2267 }
2268 last_position_adjustment_ += branch->GetSize() - branch->GetOldSize();
2269 ++last_branch_id_;
2270 }
2271 last_old_position_ = old_position;
2272 return old_position + last_position_adjustment_;
2273}
2274
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002275void MipsAssembler::BindPcRelBaseLabel() {
2276 Bind(&pc_rel_base_label_);
2277}
2278
Alexey Frunze06a46c42016-07-19 15:00:40 -07002279uint32_t MipsAssembler::GetPcRelBaseLabelLocation() const {
2280 return GetLabelLocation(&pc_rel_base_label_);
2281}
2282
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002283void MipsAssembler::FinalizeLabeledBranch(MipsLabel* label) {
2284 uint32_t length = branches_.back().GetLength();
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002285 // Commit the last branch target label (if any).
2286 DsFsmCommitLabel();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002287 if (!label->IsBound()) {
2288 // Branch forward (to a following label), distance is unknown.
2289 // The first branch forward will contain 0, serving as the terminator of
2290 // the list of forward-reaching branches.
2291 Emit(label->position_);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002292 // Nothing for the delay slot (yet).
2293 DsFsmInstrNop(0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002294 length--;
2295 // Now make the label object point to this branch
2296 // (this forms a linked list of branches preceding this label).
2297 uint32_t branch_id = branches_.size() - 1;
2298 label->LinkTo(branch_id);
2299 }
2300 // Reserve space for the branch.
2301 while (length--) {
2302 Nop();
2303 }
2304}
2305
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002306bool MipsAssembler::Branch::CanHaveDelayedInstruction(const DelaySlot& delay_slot) const {
2307 if (delay_slot.instruction_ == 0) {
2308 // NOP or no instruction for the delay slot.
2309 return false;
2310 }
2311 switch (type_) {
2312 // R2 unconditional branches.
2313 case kUncondBranch:
2314 case kLongUncondBranch:
2315 // There are no register interdependencies.
2316 return true;
2317
2318 // R2 calls.
2319 case kCall:
2320 case kLongCall:
2321 // Instructions depending on or modifying RA should not be moved into delay slots
2322 // of branches modifying RA.
2323 return ((delay_slot.gpr_ins_mask_ | delay_slot.gpr_outs_mask_) & (1u << RA)) == 0;
2324
2325 // R2 conditional branches.
2326 case kCondBranch:
2327 case kLongCondBranch:
2328 switch (condition_) {
2329 // Branches with one GPR source.
2330 case kCondLTZ:
2331 case kCondGEZ:
2332 case kCondLEZ:
2333 case kCondGTZ:
2334 case kCondEQZ:
2335 case kCondNEZ:
2336 return (delay_slot.gpr_outs_mask_ & (1u << lhs_reg_)) == 0;
2337
2338 // Branches with two GPR sources.
2339 case kCondEQ:
2340 case kCondNE:
2341 return (delay_slot.gpr_outs_mask_ & ((1u << lhs_reg_) | (1u << rhs_reg_))) == 0;
2342
2343 // Branches with one FPU condition code source.
2344 case kCondF:
2345 case kCondT:
2346 return (delay_slot.cc_outs_mask_ & (1u << lhs_reg_)) == 0;
2347
2348 default:
2349 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
2350 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
2351 LOG(FATAL) << "Unexpected branch condition " << condition_;
2352 UNREACHABLE();
2353 }
2354
2355 // R6 unconditional branches.
2356 case kR6UncondBranch:
2357 case kR6LongUncondBranch:
2358 // R6 calls.
2359 case kR6Call:
2360 case kR6LongCall:
2361 // There are no delay slots.
2362 return false;
2363
2364 // R6 conditional branches.
2365 case kR6CondBranch:
2366 case kR6LongCondBranch:
2367 switch (condition_) {
2368 // Branches with one FPU register source.
2369 case kCondF:
2370 case kCondT:
2371 return (delay_slot.fpr_outs_mask_ & (1u << lhs_reg_)) == 0;
2372 // Others have a forbidden slot instead of a delay slot.
2373 default:
2374 return false;
2375 }
2376
2377 // Literals.
2378 default:
2379 LOG(FATAL) << "Unexpected branch type " << type_;
2380 UNREACHABLE();
2381 }
2382}
2383
2384uint32_t MipsAssembler::Branch::GetDelayedInstruction() const {
2385 return delayed_instruction_;
2386}
2387
2388void MipsAssembler::Branch::SetDelayedInstruction(uint32_t instruction) {
2389 CHECK_NE(instruction, kUnfilledDelaySlot);
2390 CHECK_EQ(delayed_instruction_, kUnfilledDelaySlot);
2391 delayed_instruction_ = instruction;
2392}
2393
2394void MipsAssembler::Branch::DecrementLocations() {
2395 // We first create a branch object, which gets its type and locations initialized,
2396 // and then we check if the branch can actually have the preceding instruction moved
2397 // into its delay slot. If it can, the branch locations need to be decremented.
2398 //
2399 // We could make the check before creating the branch object and avoid the location
2400 // adjustment, but the check is cleaner when performed on an initialized branch
2401 // object.
2402 //
2403 // If the branch is backwards (to a previously bound label), reducing the locations
2404 // cannot cause a short branch to exceed its offset range because the offset reduces.
2405 // And this is not at all a problem for a long branch backwards.
2406 //
2407 // If the branch is forward (not linked to any label yet), reducing the locations
2408 // is harmless. The branch will be promoted to long if needed when the target is known.
2409 CHECK_EQ(location_, old_location_);
2410 CHECK_GE(old_location_, sizeof(uint32_t));
2411 old_location_ -= sizeof(uint32_t);
2412 location_ = old_location_;
2413}
2414
2415void MipsAssembler::MoveInstructionToDelaySlot(Branch& branch) {
2416 if (branch.CanHaveDelayedInstruction(delay_slot_)) {
2417 // The last instruction cannot be used in a different delay slot,
2418 // do not commit the label before it (if any).
2419 DsFsmDropLabel();
2420 // Remove the last emitted instruction.
2421 size_t size = buffer_.Size();
2422 CHECK_GE(size, sizeof(uint32_t));
2423 size -= sizeof(uint32_t);
2424 CHECK_EQ(buffer_.Load<uint32_t>(size), delay_slot_.instruction_);
2425 buffer_.Resize(size);
2426 // Attach it to the branch and adjust the branch locations.
2427 branch.DecrementLocations();
2428 branch.SetDelayedInstruction(delay_slot_.instruction_);
2429 } else if (!reordering_ && branch.GetType() == Branch::kUncondBranch) {
2430 // If reordefing is disabled, prevent absorption of the target instruction.
2431 branch.SetDelayedInstruction(Branch::kUnfillableDelaySlot);
2432 }
2433}
2434
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002435void MipsAssembler::Buncond(MipsLabel* label) {
2436 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002437 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ false);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002438 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002439 FinalizeLabeledBranch(label);
2440}
2441
2442void MipsAssembler::Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs) {
2443 // If lhs = rhs, this can be a NOP.
2444 if (Branch::IsNop(condition, lhs, rhs)) {
2445 return;
2446 }
2447 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
2448 branches_.emplace_back(IsR6(), buffer_.Size(), target, condition, lhs, rhs);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002449 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002450 FinalizeLabeledBranch(label);
2451}
2452
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002453void MipsAssembler::Call(MipsLabel* label) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002454 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002455 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ true);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002456 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002457 FinalizeLabeledBranch(label);
2458}
2459
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002460Literal* MipsAssembler::NewLiteral(size_t size, const uint8_t* data) {
2461 DCHECK(size == 4u || size == 8u) << size;
2462 literals_.emplace_back(size, data);
2463 return &literals_.back();
2464}
2465
2466void MipsAssembler::LoadLiteral(Register dest_reg, Register base_reg, Literal* literal) {
2467 // Literal loads are treated as pseudo branches since they require very similar handling.
2468 DCHECK_EQ(literal->GetSize(), 4u);
2469 MipsLabel* label = literal->GetLabel();
2470 DCHECK(!label->IsBound());
2471 branches_.emplace_back(IsR6(),
2472 buffer_.Size(),
2473 dest_reg,
2474 base_reg);
2475 FinalizeLabeledBranch(label);
2476}
2477
2478void MipsAssembler::EmitLiterals() {
2479 if (!literals_.empty()) {
2480 // We don't support byte and half-word literals.
2481 // TODO: proper alignment for 64-bit literals when they're implemented.
2482 for (Literal& literal : literals_) {
2483 MipsLabel* label = literal.GetLabel();
2484 Bind(label);
2485 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
2486 DCHECK(literal.GetSize() == 4u || literal.GetSize() == 8u);
2487 for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
2488 buffer_.Emit<uint8_t>(literal.GetData()[i]);
2489 }
2490 }
2491 }
2492}
2493
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002494void MipsAssembler::PromoteBranches() {
2495 // Promote short branches to long as necessary.
2496 bool changed;
2497 do {
2498 changed = false;
2499 for (auto& branch : branches_) {
2500 CHECK(branch.IsResolved());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002501 uint32_t base = GetBranchLocationOrPcRelBase(&branch);
2502 uint32_t delta = branch.PromoteIfNeeded(base);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002503 // If this branch has been promoted and needs to expand in size,
2504 // relocate all branches by the expansion size.
2505 if (delta) {
2506 changed = true;
2507 uint32_t expand_location = branch.GetLocation();
2508 for (auto& branch2 : branches_) {
2509 branch2.Relocate(expand_location, delta);
2510 }
2511 }
2512 }
2513 } while (changed);
2514
2515 // Account for branch expansion by resizing the code buffer
2516 // and moving the code in it to its final location.
2517 size_t branch_count = branches_.size();
2518 if (branch_count > 0) {
2519 // Resize.
2520 Branch& last_branch = branches_[branch_count - 1];
2521 uint32_t size_delta = last_branch.GetEndLocation() - last_branch.GetOldEndLocation();
2522 uint32_t old_size = buffer_.Size();
2523 buffer_.Resize(old_size + size_delta);
2524 // Move the code residing between branch placeholders.
2525 uint32_t end = old_size;
2526 for (size_t i = branch_count; i > 0; ) {
2527 Branch& branch = branches_[--i];
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002528 CHECK_GE(end, branch.GetOldEndLocation());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002529 uint32_t size = end - branch.GetOldEndLocation();
2530 buffer_.Move(branch.GetEndLocation(), branch.GetOldEndLocation(), size);
2531 end = branch.GetOldLocation();
2532 }
2533 }
2534}
2535
2536// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
2537const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = {
2538 // R2 short branches.
2539 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kUncondBranch
2540 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002541 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCall
2542 // R2 near literal.
2543 { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002544 // R2 long branches.
2545 { 9, 3, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongUncondBranch
2546 { 10, 4, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCondBranch
2547 { 6, 1, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCall
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002548 // R2 far literal.
2549 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002550 // R6 short branches.
2551 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6UncondBranch
2552 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kR6CondBranch
2553 // Exception: kOffset23 for beqzc/bnezc.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002554 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6Call
2555 // R6 near literal.
2556 { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Literal
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002557 // R6 long branches.
2558 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongUncondBranch
2559 { 3, 1, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002560 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCall
2561 // R6 far literal.
2562 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002563};
2564
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002565// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002566void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
2567 CHECK_EQ(overwriting_, true);
2568 overwrite_location_ = branch->GetLocation();
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002569 uint32_t offset = branch->GetOffset(GetBranchOrPcRelBaseForEncoding(branch));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002570 BranchCondition condition = branch->GetCondition();
2571 Register lhs = branch->GetLeftRegister();
2572 Register rhs = branch->GetRightRegister();
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002573 uint32_t delayed_instruction = branch->GetDelayedInstruction();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002574 switch (branch->GetType()) {
2575 // R2 short branches.
2576 case Branch::kUncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002577 if (delayed_instruction == Branch::kUnfillableDelaySlot) {
2578 // The branch was created when reordering was disabled, do not absorb the target
2579 // instruction.
2580 delayed_instruction = 0; // NOP.
2581 } else if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2582 // Try to absorb the target instruction into the delay slot.
2583 delayed_instruction = 0; // NOP.
2584 // Incrementing the signed 16-bit offset past the target instruction must not
2585 // cause overflow into the negative subrange, check for the max offset.
2586 if (offset != 0x7FFF) {
2587 uint32_t target = branch->GetTarget();
2588 if (std::binary_search(ds_fsm_target_pcs_.begin(), ds_fsm_target_pcs_.end(), target)) {
2589 delayed_instruction = buffer_.Load<uint32_t>(target);
2590 offset++;
2591 }
2592 }
2593 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002594 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2595 B(offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002596 Emit(delayed_instruction);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002597 break;
2598 case Branch::kCondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002599 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2600 if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2601 delayed_instruction = 0; // NOP.
2602 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002603 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002604 EmitBcondR2(condition, lhs, rhs, offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002605 Emit(delayed_instruction);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002606 break;
2607 case Branch::kCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002608 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2609 if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2610 delayed_instruction = 0; // NOP.
2611 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002612 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002613 Bal(offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002614 Emit(delayed_instruction);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002615 break;
2616
2617 // R2 near literal.
2618 case Branch::kLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002619 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002620 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2621 Lw(lhs, rhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002622 break;
2623
2624 // R2 long branches.
2625 case Branch::kLongUncondBranch:
2626 // To get the value of the PC register we need to use the NAL instruction.
2627 // NAL clobbers the RA register. However, RA must be preserved if the
2628 // method is compiled without the entry/exit sequences that would take care
2629 // of preserving RA (typically, leaf methods don't preserve RA explicitly).
2630 // So, we need to preserve RA in some temporary storage ourselves. The AT
2631 // register can't be used for this because we need it to load a constant
2632 // which will be added to the value that NAL stores in RA. And we can't
2633 // use T9 for this in the context of the JNI compiler, which uses it
2634 // as a scratch register (see InterproceduralScratchRegister()).
2635 // If we were to add a 32-bit constant to RA using two ADDIU instructions,
2636 // we'd also need to use the ROTR instruction, which requires no less than
2637 // MIPSR2.
2638 // Perhaps, we could use T8 or one of R2's multiplier/divider registers
2639 // (LO or HI) or even a floating-point register, but that doesn't seem
2640 // like a nice solution. We may want this to work on both R6 and pre-R6.
2641 // For now simply use the stack for RA. This should be OK since for the
2642 // vast majority of code a short PC-relative branch is sufficient.
2643 // TODO: can this be improved?
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002644 // TODO: consider generation of a shorter sequence when we know that RA
2645 // is explicitly preserved by the method entry/exit code.
2646 if (delayed_instruction != Branch::kUnfilledDelaySlot &&
2647 delayed_instruction != Branch::kUnfillableDelaySlot) {
2648 Emit(delayed_instruction);
2649 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002650 Push(RA);
2651 Nal();
2652 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2653 Lui(AT, High16Bits(offset));
2654 Ori(AT, AT, Low16Bits(offset));
2655 Addu(AT, AT, RA);
2656 Lw(RA, SP, 0);
2657 Jr(AT);
2658 DecreaseFrameSize(kMipsWordSize);
2659 break;
2660 case Branch::kLongCondBranch:
2661 // The comment on case 'Branch::kLongUncondBranch' applies here as well.
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002662 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2663 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2664 Emit(delayed_instruction);
2665 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002666 // Note: the opposite condition branch encodes 8 as the distance, which is equal to the
2667 // number of instructions skipped:
2668 // (PUSH(IncreaseFrameSize(ADDIU) + SW) + NAL + LUI + ORI + ADDU + LW + JR).
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002669 EmitBcondR2(Branch::OppositeCondition(condition), lhs, rhs, 8);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002670 Push(RA);
2671 Nal();
2672 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2673 Lui(AT, High16Bits(offset));
2674 Ori(AT, AT, Low16Bits(offset));
2675 Addu(AT, AT, RA);
2676 Lw(RA, SP, 0);
2677 Jr(AT);
2678 DecreaseFrameSize(kMipsWordSize);
2679 break;
2680 case Branch::kLongCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002681 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2682 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2683 Emit(delayed_instruction);
2684 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002685 Nal();
2686 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2687 Lui(AT, High16Bits(offset));
2688 Ori(AT, AT, Low16Bits(offset));
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002689 Addu(AT, AT, RA);
2690 Jalr(AT);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002691 Nop();
2692 break;
2693
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002694 // R2 far literal.
2695 case Branch::kFarLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002696 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002697 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2698 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2699 Lui(AT, High16Bits(offset));
2700 Addu(AT, AT, rhs);
2701 Lw(lhs, AT, Low16Bits(offset));
2702 break;
2703
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002704 // R6 short branches.
2705 case Branch::kR6UncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002706 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002707 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2708 Bc(offset);
2709 break;
2710 case Branch::kR6CondBranch:
2711 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002712 EmitBcondR6(condition, lhs, rhs, offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002713 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2714 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2715 Emit(delayed_instruction);
2716 } else {
2717 // TODO: improve by filling the forbidden slot (IFF this is
2718 // a forbidden and not a delay slot).
2719 Nop();
2720 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002721 break;
2722 case Branch::kR6Call:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002723 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002724 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002725 Balc(offset);
2726 break;
2727
2728 // R6 near literal.
2729 case Branch::kR6Literal:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002730 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002731 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2732 Lwpc(lhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002733 break;
2734
2735 // R6 long branches.
2736 case Branch::kR6LongUncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002737 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002738 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2739 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2740 Auipc(AT, High16Bits(offset));
2741 Jic(AT, Low16Bits(offset));
2742 break;
2743 case Branch::kR6LongCondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002744 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2745 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2746 Emit(delayed_instruction);
2747 }
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002748 EmitBcondR6(Branch::OppositeCondition(condition), lhs, rhs, 2);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002749 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2750 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2751 Auipc(AT, High16Bits(offset));
2752 Jic(AT, Low16Bits(offset));
2753 break;
2754 case Branch::kR6LongCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002755 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002756 offset += (offset & 0x8000) << 1; // Account for sign extension in jialc.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002757 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002758 Auipc(AT, High16Bits(offset));
2759 Jialc(AT, Low16Bits(offset));
2760 break;
2761
2762 // R6 far literal.
2763 case Branch::kR6FarLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002764 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002765 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2766 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2767 Auipc(AT, High16Bits(offset));
2768 Lw(lhs, AT, Low16Bits(offset));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002769 break;
2770 }
2771 CHECK_EQ(overwrite_location_, branch->GetEndLocation());
2772 CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize));
2773}
2774
2775void MipsAssembler::B(MipsLabel* label) {
2776 Buncond(label);
2777}
2778
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002779void MipsAssembler::Bal(MipsLabel* label) {
2780 Call(label);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002781}
2782
2783void MipsAssembler::Beq(Register rs, Register rt, MipsLabel* label) {
2784 Bcond(label, kCondEQ, rs, rt);
2785}
2786
2787void MipsAssembler::Bne(Register rs, Register rt, MipsLabel* label) {
2788 Bcond(label, kCondNE, rs, rt);
2789}
2790
2791void MipsAssembler::Beqz(Register rt, MipsLabel* label) {
2792 Bcond(label, kCondEQZ, rt);
2793}
2794
2795void MipsAssembler::Bnez(Register rt, MipsLabel* label) {
2796 Bcond(label, kCondNEZ, rt);
2797}
2798
2799void MipsAssembler::Bltz(Register rt, MipsLabel* label) {
2800 Bcond(label, kCondLTZ, rt);
2801}
2802
2803void MipsAssembler::Bgez(Register rt, MipsLabel* label) {
2804 Bcond(label, kCondGEZ, rt);
2805}
2806
2807void MipsAssembler::Blez(Register rt, MipsLabel* label) {
2808 Bcond(label, kCondLEZ, rt);
2809}
2810
2811void MipsAssembler::Bgtz(Register rt, MipsLabel* label) {
2812 Bcond(label, kCondGTZ, rt);
2813}
2814
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002815bool MipsAssembler::CanExchangeWithSlt(Register rs, Register rt) const {
2816 // If the instruction modifies AT, `rs` or `rt`, it can't be exchanged with the slt[u]
2817 // instruction because either slt[u] depends on `rs` or `rt` or the following
2818 // conditional branch depends on AT set by slt[u].
2819 // Likewise, if the instruction depends on AT, it can't be exchanged with slt[u]
2820 // because slt[u] changes AT.
2821 return (delay_slot_.instruction_ != 0 &&
2822 (delay_slot_.gpr_outs_mask_ & ((1u << AT) | (1u << rs) | (1u << rt))) == 0 &&
2823 (delay_slot_.gpr_ins_mask_ & (1u << AT)) == 0);
2824}
2825
2826void MipsAssembler::ExchangeWithSlt(const DelaySlot& forwarded_slot) {
2827 // Exchange the last two instructions in the assembler buffer.
2828 size_t size = buffer_.Size();
2829 CHECK_GE(size, 2 * sizeof(uint32_t));
2830 size_t pos1 = size - 2 * sizeof(uint32_t);
2831 size_t pos2 = size - sizeof(uint32_t);
2832 uint32_t instr1 = buffer_.Load<uint32_t>(pos1);
2833 uint32_t instr2 = buffer_.Load<uint32_t>(pos2);
2834 CHECK_EQ(instr1, forwarded_slot.instruction_);
2835 CHECK_EQ(instr2, delay_slot_.instruction_);
2836 buffer_.Store<uint32_t>(pos1, instr2);
2837 buffer_.Store<uint32_t>(pos2, instr1);
2838 // Set the current delay slot information to that of the last instruction
2839 // in the buffer.
2840 delay_slot_ = forwarded_slot;
2841}
2842
2843void MipsAssembler::GenerateSltForCondBranch(bool unsigned_slt, Register rs, Register rt) {
2844 // If possible, exchange the slt[u] instruction with the preceding instruction,
2845 // so it can fill the delay slot.
2846 DelaySlot forwarded_slot = delay_slot_;
2847 bool exchange = CanExchangeWithSlt(rs, rt);
2848 if (exchange) {
2849 // The last instruction cannot be used in a different delay slot,
2850 // do not commit the label before it (if any).
2851 DsFsmDropLabel();
2852 }
2853 if (unsigned_slt) {
2854 Sltu(AT, rs, rt);
2855 } else {
2856 Slt(AT, rs, rt);
2857 }
2858 if (exchange) {
2859 ExchangeWithSlt(forwarded_slot);
2860 }
2861}
2862
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002863void MipsAssembler::Blt(Register rs, Register rt, MipsLabel* label) {
2864 if (IsR6()) {
2865 Bcond(label, kCondLT, rs, rt);
2866 } else if (!Branch::IsNop(kCondLT, rs, rt)) {
2867 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002868 GenerateSltForCondBranch(/* unsigned_slt */ false, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002869 Bnez(AT, label);
2870 }
2871}
2872
2873void MipsAssembler::Bge(Register rs, Register rt, MipsLabel* label) {
2874 if (IsR6()) {
2875 Bcond(label, kCondGE, rs, rt);
2876 } else if (Branch::IsUncond(kCondGE, rs, rt)) {
2877 B(label);
2878 } else {
2879 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002880 GenerateSltForCondBranch(/* unsigned_slt */ false, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002881 Beqz(AT, label);
2882 }
2883}
2884
2885void MipsAssembler::Bltu(Register rs, Register rt, MipsLabel* label) {
2886 if (IsR6()) {
2887 Bcond(label, kCondLTU, rs, rt);
2888 } else if (!Branch::IsNop(kCondLTU, rs, rt)) {
2889 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002890 GenerateSltForCondBranch(/* unsigned_slt */ true, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002891 Bnez(AT, label);
2892 }
2893}
2894
2895void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) {
2896 if (IsR6()) {
2897 Bcond(label, kCondGEU, rs, rt);
2898 } else if (Branch::IsUncond(kCondGEU, rs, rt)) {
2899 B(label);
2900 } else {
2901 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002902 GenerateSltForCondBranch(/* unsigned_slt */ true, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002903 Beqz(AT, label);
jeffhao7fbee072012-08-24 17:56:54 -07002904 }
2905}
2906
Chris Larsenb74353a2015-11-20 09:07:09 -08002907void MipsAssembler::Bc1f(MipsLabel* label) {
2908 Bc1f(0, label);
2909}
2910
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002911void MipsAssembler::Bc1f(int cc, MipsLabel* label) {
2912 CHECK(IsUint<3>(cc)) << cc;
2913 Bcond(label, kCondF, static_cast<Register>(cc), ZERO);
2914}
2915
Chris Larsenb74353a2015-11-20 09:07:09 -08002916void MipsAssembler::Bc1t(MipsLabel* label) {
2917 Bc1t(0, label);
2918}
2919
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002920void MipsAssembler::Bc1t(int cc, MipsLabel* label) {
2921 CHECK(IsUint<3>(cc)) << cc;
2922 Bcond(label, kCondT, static_cast<Register>(cc), ZERO);
2923}
2924
2925void MipsAssembler::Bc1eqz(FRegister ft, MipsLabel* label) {
2926 Bcond(label, kCondF, static_cast<Register>(ft), ZERO);
2927}
2928
2929void MipsAssembler::Bc1nez(FRegister ft, MipsLabel* label) {
2930 Bcond(label, kCondT, static_cast<Register>(ft), ZERO);
2931}
2932
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002933void MipsAssembler::AdjustBaseAndOffset(Register& base,
2934 int32_t& offset,
2935 bool is_doubleword,
2936 bool is_float) {
2937 // This method is used to adjust the base register and offset pair
2938 // for a load/store when the offset doesn't fit into int16_t.
2939 // It is assumed that `base + offset` is sufficiently aligned for memory
2940 // operands that are machine word in size or smaller. For doubleword-sized
2941 // operands it's assumed that `base` is a multiple of 8, while `offset`
2942 // may be a multiple of 4 (e.g. 4-byte-aligned long and double arguments
2943 // and spilled variables on the stack accessed relative to the stack
2944 // pointer register).
2945 // We preserve the "alignment" of `offset` by adjusting it by a multiple of 8.
2946 CHECK_NE(base, AT); // Must not overwrite the register `base` while loading `offset`.
2947
2948 bool doubleword_aligned = IsAligned<kMipsDoublewordSize>(offset);
2949 bool two_accesses = is_doubleword && (!is_float || !doubleword_aligned);
2950
2951 // IsInt<16> must be passed a signed value, hence the static cast below.
2952 if (IsInt<16>(offset) &&
2953 (!two_accesses || IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
2954 // Nothing to do: `offset` (and, if needed, `offset + 4`) fits into int16_t.
2955 return;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002956 }
2957
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07002958 // Remember the "(mis)alignment" of `offset`, it will be checked at the end.
2959 uint32_t misalignment = offset & (kMipsDoublewordSize - 1);
2960
2961 // Do not load the whole 32-bit `offset` if it can be represented as
2962 // a sum of two 16-bit signed offsets. This can save an instruction or two.
2963 // To simplify matters, only do this for a symmetric range of offsets from
2964 // about -64KB to about +64KB, allowing further addition of 4 when accessing
2965 // 64-bit variables with two 32-bit accesses.
2966 constexpr int32_t kMinOffsetForSimpleAdjustment = 0x7ff8; // Max int16_t that's a multiple of 8.
2967 constexpr int32_t kMaxOffsetForSimpleAdjustment = 2 * kMinOffsetForSimpleAdjustment;
2968 if (0 <= offset && offset <= kMaxOffsetForSimpleAdjustment) {
2969 Addiu(AT, base, kMinOffsetForSimpleAdjustment);
2970 offset -= kMinOffsetForSimpleAdjustment;
2971 } else if (-kMaxOffsetForSimpleAdjustment <= offset && offset < 0) {
2972 Addiu(AT, base, -kMinOffsetForSimpleAdjustment);
2973 offset += kMinOffsetForSimpleAdjustment;
2974 } else if (IsR6()) {
2975 // On R6 take advantage of the aui instruction, e.g.:
2976 // aui AT, base, offset_high
2977 // lw reg_lo, offset_low(AT)
2978 // lw reg_hi, (offset_low+4)(AT)
2979 // or when offset_low+4 overflows int16_t:
2980 // aui AT, base, offset_high
2981 // addiu AT, AT, 8
2982 // lw reg_lo, (offset_low-8)(AT)
2983 // lw reg_hi, (offset_low-4)(AT)
2984 int16_t offset_high = High16Bits(offset);
2985 int16_t offset_low = Low16Bits(offset);
2986 offset_high += (offset_low < 0) ? 1 : 0; // Account for offset sign extension in load/store.
2987 Aui(AT, base, offset_high);
2988 if (two_accesses && !IsInt<16>(static_cast<int32_t>(offset_low + kMipsWordSize))) {
2989 // Avoid overflow in the 16-bit offset of the load/store instruction when adding 4.
2990 Addiu(AT, AT, kMipsDoublewordSize);
2991 offset_low -= kMipsDoublewordSize;
2992 }
2993 offset = offset_low;
2994 } else {
2995 // Do not load the whole 32-bit `offset` if it can be represented as
2996 // a sum of three 16-bit signed offsets. This can save an instruction.
2997 // To simplify matters, only do this for a symmetric range of offsets from
2998 // about -96KB to about +96KB, allowing further addition of 4 when accessing
2999 // 64-bit variables with two 32-bit accesses.
3000 constexpr int32_t kMinOffsetForMediumAdjustment = 2 * kMinOffsetForSimpleAdjustment;
3001 constexpr int32_t kMaxOffsetForMediumAdjustment = 3 * kMinOffsetForSimpleAdjustment;
3002 if (0 <= offset && offset <= kMaxOffsetForMediumAdjustment) {
3003 Addiu(AT, base, kMinOffsetForMediumAdjustment / 2);
3004 Addiu(AT, AT, kMinOffsetForMediumAdjustment / 2);
3005 offset -= kMinOffsetForMediumAdjustment;
3006 } else if (-kMaxOffsetForMediumAdjustment <= offset && offset < 0) {
3007 Addiu(AT, base, -kMinOffsetForMediumAdjustment / 2);
3008 Addiu(AT, AT, -kMinOffsetForMediumAdjustment / 2);
3009 offset += kMinOffsetForMediumAdjustment;
3010 } else {
3011 // Now that all shorter options have been exhausted, load the full 32-bit offset.
3012 int32_t loaded_offset = RoundDown(offset, kMipsDoublewordSize);
3013 LoadConst32(AT, loaded_offset);
3014 Addu(AT, AT, base);
3015 offset -= loaded_offset;
3016 }
3017 }
3018 base = AT;
3019
3020 CHECK(IsInt<16>(offset));
3021 if (two_accesses) {
3022 CHECK(IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)));
3023 }
3024 CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
3025}
3026
Alexey Frunze2923db72016-08-20 01:55:47 -07003027void MipsAssembler::LoadFromOffset(LoadOperandType type,
3028 Register reg,
3029 Register base,
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003030 int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003031 LoadFromOffset<>(type, reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003032}
3033
3034void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003035 LoadSFromOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003036}
3037
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003038void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003039 LoadDFromOffset<>(reg, base, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003040}
3041
3042void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
3043 size_t size) {
3044 MipsManagedRegister dst = m_dst.AsMips();
3045 if (dst.IsNoRegister()) {
3046 CHECK_EQ(0u, size) << dst;
3047 } else if (dst.IsCoreRegister()) {
3048 CHECK_EQ(kMipsWordSize, size) << dst;
3049 LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
3050 } else if (dst.IsRegisterPair()) {
3051 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3052 LoadFromOffset(kLoadDoubleword, dst.AsRegisterPairLow(), src_register, src_offset);
3053 } else if (dst.IsFRegister()) {
3054 if (size == kMipsWordSize) {
3055 LoadSFromOffset(dst.AsFRegister(), src_register, src_offset);
3056 } else {
3057 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3058 LoadDFromOffset(dst.AsFRegister(), src_register, src_offset);
3059 }
3060 }
jeffhao7fbee072012-08-24 17:56:54 -07003061}
3062
Alexey Frunze2923db72016-08-20 01:55:47 -07003063void MipsAssembler::StoreToOffset(StoreOperandType type,
3064 Register reg,
3065 Register base,
jeffhao7fbee072012-08-24 17:56:54 -07003066 int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003067 StoreToOffset<>(type, reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003068}
3069
Goran Jakovljevicff734982015-08-24 12:58:55 +00003070void MipsAssembler::StoreSToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003071 StoreSToOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003072}
3073
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003074void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003075 StoreDToOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003076}
3077
David Srbeckydd973932015-04-07 20:29:48 +01003078static dwarf::Reg DWARFReg(Register reg) {
3079 return dwarf::Reg::MipsCore(static_cast<int>(reg));
3080}
3081
Ian Rogers790a6b72014-04-01 10:36:00 -07003082constexpr size_t kFramePointerSize = 4;
3083
Vladimir Marko32248382016-05-19 10:37:24 +01003084void MipsAssembler::BuildFrame(size_t frame_size,
3085 ManagedRegister method_reg,
3086 ArrayRef<const ManagedRegister> callee_save_regs,
Dmitry Petrochenkofca82202014-03-21 11:21:37 +07003087 const ManagedRegisterEntrySpills& entry_spills) {
jeffhao7fbee072012-08-24 17:56:54 -07003088 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003089 DCHECK(!overwriting_);
jeffhao7fbee072012-08-24 17:56:54 -07003090
3091 // Increase frame to required size.
3092 IncreaseFrameSize(frame_size);
3093
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003094 // Push callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07003095 int stack_offset = frame_size - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003096 StoreToOffset(kStoreWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003097 cfi_.RelOffset(DWARFReg(RA), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07003098 for (int i = callee_save_regs.size() - 1; i >= 0; --i) {
Ian Rogers790a6b72014-04-01 10:36:00 -07003099 stack_offset -= kFramePointerSize;
Vladimir Marko32248382016-05-19 10:37:24 +01003100 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07003101 StoreToOffset(kStoreWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003102 cfi_.RelOffset(DWARFReg(reg), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07003103 }
3104
3105 // Write out Method*.
3106 StoreToOffset(kStoreWord, method_reg.AsMips().AsCoreRegister(), SP, 0);
3107
3108 // Write out entry spills.
Goran Jakovljevicff734982015-08-24 12:58:55 +00003109 int32_t offset = frame_size + kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003110 for (size_t i = 0; i < entry_spills.size(); ++i) {
Goran Jakovljevicff734982015-08-24 12:58:55 +00003111 MipsManagedRegister reg = entry_spills.at(i).AsMips();
3112 if (reg.IsNoRegister()) {
3113 ManagedRegisterSpill spill = entry_spills.at(i);
3114 offset += spill.getSize();
3115 } else if (reg.IsCoreRegister()) {
3116 StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003117 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003118 } else if (reg.IsFRegister()) {
3119 StoreSToOffset(reg.AsFRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003120 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003121 } else if (reg.IsDRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003122 StoreDToOffset(reg.AsOverlappingDRegisterLow(), SP, offset);
3123 offset += kMipsDoublewordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003124 }
jeffhao7fbee072012-08-24 17:56:54 -07003125 }
3126}
3127
3128void MipsAssembler::RemoveFrame(size_t frame_size,
Vladimir Marko32248382016-05-19 10:37:24 +01003129 ArrayRef<const ManagedRegister> callee_save_regs) {
jeffhao7fbee072012-08-24 17:56:54 -07003130 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003131 DCHECK(!overwriting_);
David Srbeckydd973932015-04-07 20:29:48 +01003132 cfi_.RememberState();
jeffhao7fbee072012-08-24 17:56:54 -07003133
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003134 // Pop callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07003135 int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003136 for (size_t i = 0; i < callee_save_regs.size(); ++i) {
Vladimir Marko32248382016-05-19 10:37:24 +01003137 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07003138 LoadFromOffset(kLoadWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003139 cfi_.Restore(DWARFReg(reg));
Ian Rogers790a6b72014-04-01 10:36:00 -07003140 stack_offset += kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003141 }
3142 LoadFromOffset(kLoadWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003143 cfi_.Restore(DWARFReg(RA));
jeffhao7fbee072012-08-24 17:56:54 -07003144
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003145 // Adjust the stack pointer in the delay slot if doing so doesn't break CFI.
3146 bool exchange = IsInt<16>(static_cast<int32_t>(frame_size));
3147 bool reordering = SetReorder(false);
3148 if (exchange) {
3149 // Jump to the return address.
3150 Jr(RA);
3151 // Decrease frame to required size.
3152 DecreaseFrameSize(frame_size); // Single instruction in delay slot.
3153 } else {
3154 // Decrease frame to required size.
3155 DecreaseFrameSize(frame_size);
3156 // Jump to the return address.
3157 Jr(RA);
3158 Nop(); // In delay slot.
3159 }
3160 SetReorder(reordering);
David Srbeckydd973932015-04-07 20:29:48 +01003161
3162 // The CFI should be restored for any code that follows the exit block.
3163 cfi_.RestoreState();
3164 cfi_.DefCFAOffset(frame_size);
jeffhao7fbee072012-08-24 17:56:54 -07003165}
3166
3167void MipsAssembler::IncreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003168 CHECK_ALIGNED(adjust, kFramePointerSize);
3169 Addiu32(SP, SP, -adjust);
David Srbeckydd973932015-04-07 20:29:48 +01003170 cfi_.AdjustCFAOffset(adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003171 if (overwriting_) {
3172 cfi_.OverrideDelayedPC(overwrite_location_);
3173 }
jeffhao7fbee072012-08-24 17:56:54 -07003174}
3175
3176void MipsAssembler::DecreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003177 CHECK_ALIGNED(adjust, kFramePointerSize);
3178 Addiu32(SP, SP, adjust);
David Srbeckydd973932015-04-07 20:29:48 +01003179 cfi_.AdjustCFAOffset(-adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003180 if (overwriting_) {
3181 cfi_.OverrideDelayedPC(overwrite_location_);
3182 }
jeffhao7fbee072012-08-24 17:56:54 -07003183}
3184
3185void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
3186 MipsManagedRegister src = msrc.AsMips();
3187 if (src.IsNoRegister()) {
3188 CHECK_EQ(0u, size);
3189 } else if (src.IsCoreRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003190 CHECK_EQ(kMipsWordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07003191 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3192 } else if (src.IsRegisterPair()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003193 CHECK_EQ(kMipsDoublewordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07003194 StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
3195 StoreToOffset(kStoreWord, src.AsRegisterPairHigh(),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003196 SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003197 } else if (src.IsFRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003198 if (size == kMipsWordSize) {
3199 StoreSToOffset(src.AsFRegister(), SP, dest.Int32Value());
3200 } else {
3201 CHECK_EQ(kMipsDoublewordSize, size);
3202 StoreDToOffset(src.AsFRegister(), SP, dest.Int32Value());
3203 }
jeffhao7fbee072012-08-24 17:56:54 -07003204 }
3205}
3206
3207void MipsAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
3208 MipsManagedRegister src = msrc.AsMips();
3209 CHECK(src.IsCoreRegister());
3210 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3211}
3212
3213void MipsAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
3214 MipsManagedRegister src = msrc.AsMips();
3215 CHECK(src.IsCoreRegister());
3216 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3217}
3218
3219void MipsAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
3220 ManagedRegister mscratch) {
3221 MipsManagedRegister scratch = mscratch.AsMips();
3222 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003223 LoadConst32(scratch.AsCoreRegister(), imm);
jeffhao7fbee072012-08-24 17:56:54 -07003224 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
3225}
3226
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003227void MipsAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs,
3228 FrameOffset fr_offs,
jeffhao7fbee072012-08-24 17:56:54 -07003229 ManagedRegister mscratch) {
3230 MipsManagedRegister scratch = mscratch.AsMips();
3231 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003232 Addiu32(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003233 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3234 S1, thr_offs.Int32Value());
3235}
3236
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003237void MipsAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003238 StoreToOffset(kStoreWord, SP, S1, thr_offs.Int32Value());
3239}
3240
3241void MipsAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
3242 FrameOffset in_off, ManagedRegister mscratch) {
3243 MipsManagedRegister src = msrc.AsMips();
3244 MipsManagedRegister scratch = mscratch.AsMips();
3245 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3246 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003247 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003248}
3249
3250void MipsAssembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) {
3251 return EmitLoad(mdest, SP, src.Int32Value(), size);
3252}
3253
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003254void MipsAssembler::LoadFromThread(ManagedRegister mdest, ThreadOffset32 src, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003255 return EmitLoad(mdest, S1, src.Int32Value(), size);
3256}
3257
3258void MipsAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) {
3259 MipsManagedRegister dest = mdest.AsMips();
3260 CHECK(dest.IsCoreRegister());
3261 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value());
3262}
3263
Mathieu Chartiere401d142015-04-22 13:56:20 -07003264void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs,
Roland Levillain4d027112015-07-01 15:41:14 +01003265 bool unpoison_reference) {
jeffhao7fbee072012-08-24 17:56:54 -07003266 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003267 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07003268 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
3269 base.AsMips().AsCoreRegister(), offs.Int32Value());
Roland Levillain4d027112015-07-01 15:41:14 +01003270 if (kPoisonHeapReferences && unpoison_reference) {
Hiroshi Yamauchie63a7452014-02-27 14:44:36 -08003271 Subu(dest.AsCoreRegister(), ZERO, dest.AsCoreRegister());
3272 }
jeffhao7fbee072012-08-24 17:56:54 -07003273}
3274
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003275void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003276 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003277 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07003278 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
3279 base.AsMips().AsCoreRegister(), offs.Int32Value());
3280}
3281
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003282void MipsAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset32 offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003283 MipsManagedRegister dest = mdest.AsMips();
3284 CHECK(dest.IsCoreRegister());
3285 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), S1, offs.Int32Value());
3286}
3287
3288void MipsAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
3289 UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips";
3290}
3291
3292void MipsAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
3293 UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips";
3294}
3295
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003296void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003297 MipsManagedRegister dest = mdest.AsMips();
3298 MipsManagedRegister src = msrc.AsMips();
3299 if (!dest.Equals(src)) {
3300 if (dest.IsCoreRegister()) {
3301 CHECK(src.IsCoreRegister()) << src;
3302 Move(dest.AsCoreRegister(), src.AsCoreRegister());
3303 } else if (dest.IsFRegister()) {
3304 CHECK(src.IsFRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003305 if (size == kMipsWordSize) {
3306 MovS(dest.AsFRegister(), src.AsFRegister());
3307 } else {
3308 CHECK_EQ(kMipsDoublewordSize, size);
3309 MovD(dest.AsFRegister(), src.AsFRegister());
3310 }
jeffhao7fbee072012-08-24 17:56:54 -07003311 } else if (dest.IsDRegister()) {
3312 CHECK(src.IsDRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003313 MovD(dest.AsOverlappingDRegisterLow(), src.AsOverlappingDRegisterLow());
jeffhao7fbee072012-08-24 17:56:54 -07003314 } else {
3315 CHECK(dest.IsRegisterPair()) << dest;
3316 CHECK(src.IsRegisterPair()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003317 // Ensure that the first move doesn't clobber the input of the second.
jeffhao7fbee072012-08-24 17:56:54 -07003318 if (src.AsRegisterPairHigh() != dest.AsRegisterPairLow()) {
3319 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
3320 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
3321 } else {
3322 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
3323 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
3324 }
3325 }
3326 }
3327}
3328
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003329void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003330 MipsManagedRegister scratch = mscratch.AsMips();
3331 CHECK(scratch.IsCoreRegister()) << scratch;
3332 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3333 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
3334}
3335
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003336void MipsAssembler::CopyRawPtrFromThread(FrameOffset fr_offs,
3337 ThreadOffset32 thr_offs,
3338 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003339 MipsManagedRegister scratch = mscratch.AsMips();
3340 CHECK(scratch.IsCoreRegister()) << scratch;
3341 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3342 S1, thr_offs.Int32Value());
3343 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3344 SP, fr_offs.Int32Value());
3345}
3346
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003347void MipsAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs,
3348 FrameOffset fr_offs,
3349 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003350 MipsManagedRegister scratch = mscratch.AsMips();
3351 CHECK(scratch.IsCoreRegister()) << scratch;
3352 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3353 SP, fr_offs.Int32Value());
3354 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3355 S1, thr_offs.Int32Value());
3356}
3357
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003358void MipsAssembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003359 MipsManagedRegister scratch = mscratch.AsMips();
3360 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003361 CHECK(size == kMipsWordSize || size == kMipsDoublewordSize) << size;
3362 if (size == kMipsWordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07003363 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3364 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003365 } else if (size == kMipsDoublewordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07003366 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3367 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003368 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + kMipsWordSize);
3369 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003370 }
3371}
3372
3373void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
3374 ManagedRegister mscratch, size_t size) {
3375 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003376 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003377 LoadFromOffset(kLoadWord, scratch, src_base.AsMips().AsCoreRegister(), src_offset.Int32Value());
3378 StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
3379}
3380
3381void MipsAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
3382 ManagedRegister mscratch, size_t size) {
3383 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003384 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003385 LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
3386 StoreToOffset(kStoreWord, scratch, dest_base.AsMips().AsCoreRegister(), dest_offset.Int32Value());
3387}
3388
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003389void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
3390 FrameOffset src_base ATTRIBUTE_UNUSED,
3391 Offset src_offset ATTRIBUTE_UNUSED,
3392 ManagedRegister mscratch ATTRIBUTE_UNUSED,
3393 size_t size ATTRIBUTE_UNUSED) {
3394 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003395}
3396
3397void MipsAssembler::Copy(ManagedRegister dest, Offset dest_offset,
3398 ManagedRegister src, Offset src_offset,
3399 ManagedRegister mscratch, size_t size) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003400 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003401 Register scratch = mscratch.AsMips().AsCoreRegister();
3402 LoadFromOffset(kLoadWord, scratch, src.AsMips().AsCoreRegister(), src_offset.Int32Value());
3403 StoreToOffset(kStoreWord, scratch, dest.AsMips().AsCoreRegister(), dest_offset.Int32Value());
3404}
3405
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003406void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
3407 Offset dest_offset ATTRIBUTE_UNUSED,
3408 FrameOffset src ATTRIBUTE_UNUSED,
3409 Offset src_offset ATTRIBUTE_UNUSED,
3410 ManagedRegister mscratch ATTRIBUTE_UNUSED,
3411 size_t size ATTRIBUTE_UNUSED) {
3412 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003413}
3414
3415void MipsAssembler::MemoryBarrier(ManagedRegister) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003416 // TODO: sync?
3417 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003418}
3419
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003420void MipsAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003421 FrameOffset handle_scope_offset,
3422 ManagedRegister min_reg,
3423 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003424 MipsManagedRegister out_reg = mout_reg.AsMips();
3425 MipsManagedRegister in_reg = min_reg.AsMips();
3426 CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
3427 CHECK(out_reg.IsCoreRegister()) << out_reg;
3428 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003429 MipsLabel null_arg;
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003430 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3431 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003432 // E.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset).
jeffhao7fbee072012-08-24 17:56:54 -07003433 if (in_reg.IsNoRegister()) {
3434 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003435 SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003436 in_reg = out_reg;
3437 }
3438 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003439 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003440 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003441 Beqz(in_reg.AsCoreRegister(), &null_arg);
3442 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3443 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003444 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003445 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003446 }
3447}
3448
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003449void MipsAssembler::CreateHandleScopeEntry(FrameOffset out_off,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003450 FrameOffset handle_scope_offset,
3451 ManagedRegister mscratch,
3452 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003453 MipsManagedRegister scratch = mscratch.AsMips();
3454 CHECK(scratch.IsCoreRegister()) << scratch;
3455 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003456 MipsLabel null_arg;
3457 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003458 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3459 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003460 // E.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset).
3461 Beqz(scratch.AsCoreRegister(), &null_arg);
3462 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3463 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003464 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003465 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003466 }
3467 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
3468}
3469
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003470// Given a handle scope entry, load the associated reference.
3471void MipsAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003472 ManagedRegister min_reg) {
jeffhao7fbee072012-08-24 17:56:54 -07003473 MipsManagedRegister out_reg = mout_reg.AsMips();
3474 MipsManagedRegister in_reg = min_reg.AsMips();
3475 CHECK(out_reg.IsCoreRegister()) << out_reg;
3476 CHECK(in_reg.IsCoreRegister()) << in_reg;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003477 MipsLabel null_arg;
jeffhao7fbee072012-08-24 17:56:54 -07003478 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003479 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003480 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003481 Beqz(in_reg.AsCoreRegister(), &null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003482 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
3483 in_reg.AsCoreRegister(), 0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003484 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003485}
3486
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003487void MipsAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,
3488 bool could_be_null ATTRIBUTE_UNUSED) {
3489 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003490}
3491
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003492void MipsAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED,
3493 bool could_be_null ATTRIBUTE_UNUSED) {
3494 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003495}
3496
3497void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) {
3498 MipsManagedRegister base = mbase.AsMips();
3499 MipsManagedRegister scratch = mscratch.AsMips();
3500 CHECK(base.IsCoreRegister()) << base;
3501 CHECK(scratch.IsCoreRegister()) << scratch;
3502 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3503 base.AsCoreRegister(), offset.Int32Value());
3504 Jalr(scratch.AsCoreRegister());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003505 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003506 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003507}
3508
3509void MipsAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
3510 MipsManagedRegister scratch = mscratch.AsMips();
3511 CHECK(scratch.IsCoreRegister()) << scratch;
3512 // Call *(*(SP + base) + offset)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003513 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, base.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003514 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3515 scratch.AsCoreRegister(), offset.Int32Value());
3516 Jalr(scratch.AsCoreRegister());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003517 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003518 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003519}
3520
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003521void MipsAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UNUSED,
3522 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
Ian Rogers468532e2013-08-05 10:56:33 -07003523 UNIMPLEMENTED(FATAL) << "no mips implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003524}
3525
3526void MipsAssembler::GetCurrentThread(ManagedRegister tr) {
3527 Move(tr.AsMips().AsCoreRegister(), S1);
3528}
3529
3530void MipsAssembler::GetCurrentThread(FrameOffset offset,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003531 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
jeffhao7fbee072012-08-24 17:56:54 -07003532 StoreToOffset(kStoreWord, S1, SP, offset.Int32Value());
3533}
3534
jeffhao7fbee072012-08-24 17:56:54 -07003535void MipsAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
3536 MipsManagedRegister scratch = mscratch.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003537 exception_blocks_.emplace_back(scratch, stack_adjust);
jeffhao7fbee072012-08-24 17:56:54 -07003538 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
Andreas Gampe542451c2016-07-26 09:02:02 -07003539 S1, Thread::ExceptionOffset<kMipsPointerSize>().Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003540 Bnez(scratch.AsCoreRegister(), exception_blocks_.back().Entry());
jeffhao7fbee072012-08-24 17:56:54 -07003541}
3542
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003543void MipsAssembler::EmitExceptionPoll(MipsExceptionSlowPath* exception) {
3544 Bind(exception->Entry());
3545 if (exception->stack_adjust_ != 0) { // Fix up the frame.
3546 DecreaseFrameSize(exception->stack_adjust_);
jeffhao7fbee072012-08-24 17:56:54 -07003547 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003548 // Pass exception object as argument.
3549 // Don't care about preserving A0 as this call won't return.
3550 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
3551 Move(A0, exception->scratch_.AsCoreRegister());
3552 // Set up call to Thread::Current()->pDeliverException.
3553 LoadFromOffset(kLoadWord, T9, S1,
Andreas Gampe542451c2016-07-26 09:02:02 -07003554 QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, pDeliverException).Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003555 Jr(T9);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003556 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003557
3558 // Call never returns.
3559 Break();
jeffhao7fbee072012-08-24 17:56:54 -07003560}
3561
3562} // namespace mips
3563} // namespace art