blob: 2e2231b07de71ca3c6c59b81c768b1913bb90323 [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
Alexey Frunze674b9ee2016-09-20 14:54:15 -0700195void MipsAssembler::DsFsmInstrFffr(uint32_t instruction,
196 FRegister in1_out,
197 FRegister in2,
198 Register in3) {
199 DsFsmInstr(instruction, 0, (1u << in3), (1u << in1_out), (1u << in1_out) | (1u << in2), 0, 0);
200}
201
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700202void MipsAssembler::DsFsmInstrRf(uint32_t instruction, Register out, FRegister in) {
203 DsFsmInstr(instruction, (1u << out), 0, 0, (1u << in), 0, 0);
204}
205
206void MipsAssembler::DsFsmInstrFr(uint32_t instruction, FRegister out, Register in) {
207 DsFsmInstr(instruction, 0, (1u << in), (1u << out), 0, 0, 0);
208}
209
210void MipsAssembler::DsFsmInstrFR(uint32_t instruction, FRegister in1, Register in2) {
211 DsFsmInstr(instruction, 0, (1u << in2), 0, (1u << in1), 0, 0);
212}
213
214void MipsAssembler::DsFsmInstrCff(uint32_t instruction, int cc_out, FRegister in1, FRegister in2) {
215 DsFsmInstr(instruction, 0, 0, 0, (1u << in1) | (1u << in2), (1 << cc_out), 0);
216}
217
218void MipsAssembler::DsFsmInstrRrrc(uint32_t instruction,
219 Register in1_out,
220 Register in2,
221 int cc_in) {
222 DsFsmInstr(instruction, (1u << in1_out), (1u << in1_out) | (1u << in2), 0, 0, 0, (1 << cc_in));
223}
224
225void MipsAssembler::DsFsmInstrFffc(uint32_t instruction,
226 FRegister in1_out,
227 FRegister in2,
228 int cc_in) {
229 DsFsmInstr(instruction, 0, 0, (1u << in1_out), (1u << in1_out) | (1u << in2), 0, (1 << cc_in));
230}
231
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200232void MipsAssembler::FinalizeCode() {
233 for (auto& exception_block : exception_blocks_) {
234 EmitExceptionPoll(&exception_block);
235 }
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700236 // Commit the last branch target label (if any) and disable instruction reordering.
237 DsFsmCommitLabel();
238 SetReorder(false);
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700239 EmitLiterals();
Alexey Frunze96b66822016-09-10 02:32:44 -0700240 ReserveJumpTableSpace();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200241 PromoteBranches();
242}
243
244void MipsAssembler::FinalizeInstructions(const MemoryRegion& region) {
Vladimir Marko10ef6942015-10-22 15:25:54 +0100245 size_t number_of_delayed_adjust_pcs = cfi().NumberOfDelayedAdvancePCs();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200246 EmitBranches();
Alexey Frunze96b66822016-09-10 02:32:44 -0700247 EmitJumpTables();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200248 Assembler::FinalizeInstructions(region);
Vladimir Marko10ef6942015-10-22 15:25:54 +0100249 PatchCFI(number_of_delayed_adjust_pcs);
250}
251
252void MipsAssembler::PatchCFI(size_t number_of_delayed_adjust_pcs) {
253 if (cfi().NumberOfDelayedAdvancePCs() == 0u) {
254 DCHECK_EQ(number_of_delayed_adjust_pcs, 0u);
255 return;
256 }
257
258 typedef DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC DelayedAdvancePC;
259 const auto data = cfi().ReleaseStreamAndPrepareForDelayedAdvancePC();
260 const std::vector<uint8_t>& old_stream = data.first;
261 const std::vector<DelayedAdvancePC>& advances = data.second;
262
263 // PCs recorded before EmitBranches() need to be adjusted.
264 // PCs recorded during EmitBranches() are already adjusted.
265 // Both ranges are separately sorted but they may overlap.
266 if (kIsDebugBuild) {
267 auto cmp = [](const DelayedAdvancePC& lhs, const DelayedAdvancePC& rhs) {
268 return lhs.pc < rhs.pc;
269 };
270 CHECK(std::is_sorted(advances.begin(), advances.begin() + number_of_delayed_adjust_pcs, cmp));
271 CHECK(std::is_sorted(advances.begin() + number_of_delayed_adjust_pcs, advances.end(), cmp));
272 }
273
274 // Append initial CFI data if any.
275 size_t size = advances.size();
276 DCHECK_NE(size, 0u);
277 cfi().AppendRawData(old_stream, 0u, advances[0].stream_pos);
278 // Emit PC adjustments interleaved with the old CFI stream.
279 size_t adjust_pos = 0u;
280 size_t late_emit_pos = number_of_delayed_adjust_pcs;
281 while (adjust_pos != number_of_delayed_adjust_pcs || late_emit_pos != size) {
282 size_t adjusted_pc = (adjust_pos != number_of_delayed_adjust_pcs)
283 ? GetAdjustedPosition(advances[adjust_pos].pc)
284 : static_cast<size_t>(-1);
285 size_t late_emit_pc = (late_emit_pos != size)
286 ? advances[late_emit_pos].pc
287 : static_cast<size_t>(-1);
288 size_t advance_pc = std::min(adjusted_pc, late_emit_pc);
289 DCHECK_NE(advance_pc, static_cast<size_t>(-1));
290 size_t entry = (adjusted_pc <= late_emit_pc) ? adjust_pos : late_emit_pos;
291 if (adjusted_pc <= late_emit_pc) {
292 ++adjust_pos;
293 } else {
294 ++late_emit_pos;
295 }
296 cfi().AdvancePC(advance_pc);
297 size_t end_pos = (entry + 1u == size) ? old_stream.size() : advances[entry + 1u].stream_pos;
298 cfi().AppendRawData(old_stream, advances[entry].stream_pos, end_pos);
299 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200300}
301
302void MipsAssembler::EmitBranches() {
303 CHECK(!overwriting_);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700304 CHECK(!reordering_);
305 // Now that everything has its final position in the buffer (the branches have
306 // been promoted), adjust the target label PCs.
307 for (size_t cnt = ds_fsm_target_pcs_.size(), i = 0; i < cnt; i++) {
308 ds_fsm_target_pcs_[i] = GetAdjustedPosition(ds_fsm_target_pcs_[i]);
309 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200310 // Switch from appending instructions at the end of the buffer to overwriting
311 // existing instructions (branch placeholders) in the buffer.
312 overwriting_ = true;
313 for (auto& branch : branches_) {
314 EmitBranch(&branch);
315 }
316 overwriting_ = false;
317}
318
319void MipsAssembler::Emit(uint32_t value) {
320 if (overwriting_) {
321 // Branches to labels are emitted into their placeholders here.
322 buffer_.Store<uint32_t>(overwrite_location_, value);
323 overwrite_location_ += sizeof(uint32_t);
324 } else {
325 // Other instructions are simply appended at the end here.
326 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
327 buffer_.Emit<uint32_t>(value);
328 }
jeffhao7fbee072012-08-24 17:56:54 -0700329}
330
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700331uint32_t MipsAssembler::EmitR(int opcode,
332 Register rs,
333 Register rt,
334 Register rd,
335 int shamt,
336 int funct) {
jeffhao7fbee072012-08-24 17:56:54 -0700337 CHECK_NE(rs, kNoRegister);
338 CHECK_NE(rt, kNoRegister);
339 CHECK_NE(rd, kNoRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200340 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
341 static_cast<uint32_t>(rs) << kRsShift |
342 static_cast<uint32_t>(rt) << kRtShift |
343 static_cast<uint32_t>(rd) << kRdShift |
344 shamt << kShamtShift |
345 funct;
jeffhao7fbee072012-08-24 17:56:54 -0700346 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700347 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700348}
349
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700350uint32_t MipsAssembler::EmitI(int opcode, Register rs, Register rt, uint16_t imm) {
jeffhao7fbee072012-08-24 17:56:54 -0700351 CHECK_NE(rs, kNoRegister);
352 CHECK_NE(rt, kNoRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200353 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
354 static_cast<uint32_t>(rs) << kRsShift |
355 static_cast<uint32_t>(rt) << kRtShift |
356 imm;
jeffhao7fbee072012-08-24 17:56:54 -0700357 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700358 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700359}
360
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700361uint32_t MipsAssembler::EmitI21(int opcode, Register rs, uint32_t imm21) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200362 CHECK_NE(rs, kNoRegister);
363 CHECK(IsUint<21>(imm21)) << imm21;
364 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
365 static_cast<uint32_t>(rs) << kRsShift |
366 imm21;
jeffhao7fbee072012-08-24 17:56:54 -0700367 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700368 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700369}
370
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700371uint32_t MipsAssembler::EmitI26(int opcode, uint32_t imm26) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200372 CHECK(IsUint<26>(imm26)) << imm26;
373 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | imm26;
374 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700375 return encoding;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200376}
377
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700378uint32_t MipsAssembler::EmitFR(int opcode,
379 int fmt,
380 FRegister ft,
381 FRegister fs,
382 FRegister fd,
383 int funct) {
jeffhao7fbee072012-08-24 17:56:54 -0700384 CHECK_NE(ft, kNoFRegister);
385 CHECK_NE(fs, kNoFRegister);
386 CHECK_NE(fd, kNoFRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200387 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
388 fmt << kFmtShift |
389 static_cast<uint32_t>(ft) << kFtShift |
390 static_cast<uint32_t>(fs) << kFsShift |
391 static_cast<uint32_t>(fd) << kFdShift |
392 funct;
jeffhao7fbee072012-08-24 17:56:54 -0700393 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700394 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700395}
396
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700397uint32_t MipsAssembler::EmitFI(int opcode, int fmt, FRegister ft, uint16_t imm) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200398 CHECK_NE(ft, kNoFRegister);
399 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
400 fmt << kFmtShift |
401 static_cast<uint32_t>(ft) << kFtShift |
402 imm;
jeffhao7fbee072012-08-24 17:56:54 -0700403 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700404 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700405}
406
jeffhao7fbee072012-08-24 17:56:54 -0700407void MipsAssembler::Addu(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700408 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x21), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700409}
410
jeffhao7fbee072012-08-24 17:56:54 -0700411void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700412 DsFsmInstrRrr(EmitI(0x9, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700413}
414
jeffhao7fbee072012-08-24 17:56:54 -0700415void MipsAssembler::Subu(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700416 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x23), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700417}
418
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200419void MipsAssembler::MultR2(Register rs, Register rt) {
420 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700421 DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x18), ZERO, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700422}
423
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200424void MipsAssembler::MultuR2(Register rs, Register rt) {
425 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700426 DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x19), ZERO, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700427}
428
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200429void MipsAssembler::DivR2(Register rs, Register rt) {
430 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700431 DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1a), ZERO, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700432}
433
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200434void MipsAssembler::DivuR2(Register rs, Register rt) {
435 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700436 DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1b), ZERO, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700437}
438
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200439void MipsAssembler::MulR2(Register rd, Register rs, Register rt) {
440 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700441 DsFsmInstrRrr(EmitR(0x1c, rs, rt, rd, 0, 2), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200442}
443
444void MipsAssembler::DivR2(Register rd, Register rs, Register rt) {
445 CHECK(!IsR6());
446 DivR2(rs, rt);
447 Mflo(rd);
448}
449
450void MipsAssembler::ModR2(Register rd, Register rs, Register rt) {
451 CHECK(!IsR6());
452 DivR2(rs, rt);
453 Mfhi(rd);
454}
455
456void MipsAssembler::DivuR2(Register rd, Register rs, Register rt) {
457 CHECK(!IsR6());
458 DivuR2(rs, rt);
459 Mflo(rd);
460}
461
462void MipsAssembler::ModuR2(Register rd, Register rs, Register rt) {
463 CHECK(!IsR6());
464 DivuR2(rs, rt);
465 Mfhi(rd);
466}
467
468void MipsAssembler::MulR6(Register rd, Register rs, Register rt) {
469 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700470 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x18), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200471}
472
Alexey Frunze7e99e052015-11-24 19:28:01 -0800473void MipsAssembler::MuhR6(Register rd, Register rs, Register rt) {
474 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700475 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x18), rd, rs, rt);
Alexey Frunze7e99e052015-11-24 19:28:01 -0800476}
477
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200478void MipsAssembler::MuhuR6(Register rd, Register rs, Register rt) {
479 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700480 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x19), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200481}
482
483void MipsAssembler::DivR6(Register rd, Register rs, Register rt) {
484 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700485 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x1a), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200486}
487
488void MipsAssembler::ModR6(Register rd, Register rs, Register rt) {
489 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700490 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x1a), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200491}
492
493void MipsAssembler::DivuR6(Register rd, Register rs, Register rt) {
494 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700495 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x1b), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200496}
497
498void MipsAssembler::ModuR6(Register rd, Register rs, Register rt) {
499 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700500 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x1b), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200501}
502
jeffhao7fbee072012-08-24 17:56:54 -0700503void MipsAssembler::And(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700504 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x24), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700505}
506
507void MipsAssembler::Andi(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700508 DsFsmInstrRrr(EmitI(0xc, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700509}
510
511void MipsAssembler::Or(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700512 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x25), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700513}
514
515void MipsAssembler::Ori(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700516 DsFsmInstrRrr(EmitI(0xd, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700517}
518
519void MipsAssembler::Xor(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700520 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x26), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700521}
522
523void MipsAssembler::Xori(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700524 DsFsmInstrRrr(EmitI(0xe, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700525}
526
527void MipsAssembler::Nor(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700528 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x27), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700529}
530
Chris Larsene3845472015-11-18 12:27:15 -0800531void MipsAssembler::Movz(Register rd, Register rs, Register rt) {
532 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700533 DsFsmInstrRrrr(EmitR(0, rs, rt, rd, 0, 0x0A), rd, rs, rt);
Chris Larsene3845472015-11-18 12:27:15 -0800534}
535
536void MipsAssembler::Movn(Register rd, Register rs, Register rt) {
537 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700538 DsFsmInstrRrrr(EmitR(0, rs, rt, rd, 0, 0x0B), rd, rs, rt);
Chris Larsene3845472015-11-18 12:27:15 -0800539}
540
541void MipsAssembler::Seleqz(Register rd, Register rs, Register rt) {
542 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700543 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x35), rd, rs, rt);
Chris Larsene3845472015-11-18 12:27:15 -0800544}
545
546void MipsAssembler::Selnez(Register rd, Register rs, Register rt) {
547 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700548 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x37), rd, rs, rt);
Chris Larsene3845472015-11-18 12:27:15 -0800549}
550
551void MipsAssembler::ClzR6(Register rd, Register rs) {
552 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700553 DsFsmInstrRrr(EmitR(0, rs, static_cast<Register>(0), rd, 0x01, 0x10), rd, rs, rs);
Chris Larsene3845472015-11-18 12:27:15 -0800554}
555
556void MipsAssembler::ClzR2(Register rd, Register rs) {
557 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700558 DsFsmInstrRrr(EmitR(0x1C, rs, rd, rd, 0, 0x20), rd, rs, rs);
Chris Larsene3845472015-11-18 12:27:15 -0800559}
560
561void MipsAssembler::CloR6(Register rd, Register rs) {
562 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700563 DsFsmInstrRrr(EmitR(0, rs, static_cast<Register>(0), rd, 0x01, 0x11), rd, rs, rs);
Chris Larsene3845472015-11-18 12:27:15 -0800564}
565
566void MipsAssembler::CloR2(Register rd, Register rs) {
567 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700568 DsFsmInstrRrr(EmitR(0x1C, rs, rd, rd, 0, 0x21), rd, rs, rs);
Chris Larsene3845472015-11-18 12:27:15 -0800569}
570
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200571void MipsAssembler::Seb(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700572 DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x10, 0x20), rd, rt, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700573}
574
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200575void MipsAssembler::Seh(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700576 DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x18, 0x20), rd, rt, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700577}
578
Chris Larsen3f8bf652015-10-28 10:08:56 -0700579void MipsAssembler::Wsbh(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700580 DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 2, 0x20), rd, rt, rt);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700581}
582
Chris Larsen70014c82015-11-18 12:26:08 -0800583void MipsAssembler::Bitswap(Register rd, Register rt) {
584 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700585 DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x0, 0x20), rd, rt, rt);
Chris Larsen70014c82015-11-18 12:26:08 -0800586}
587
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200588void MipsAssembler::Sll(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700589 CHECK(IsUint<5>(shamt)) << shamt;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700590 DsFsmInstrRrr(EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x00), rd, rt, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700591}
592
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200593void MipsAssembler::Srl(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700594 CHECK(IsUint<5>(shamt)) << shamt;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700595 DsFsmInstrRrr(EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x02), rd, rt, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200596}
597
Chris Larsen3f8bf652015-10-28 10:08:56 -0700598void MipsAssembler::Rotr(Register rd, Register rt, int shamt) {
599 CHECK(IsUint<5>(shamt)) << shamt;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700600 DsFsmInstrRrr(EmitR(0, static_cast<Register>(1), rt, rd, shamt, 0x02), rd, rt, rt);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700601}
602
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200603void MipsAssembler::Sra(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700604 CHECK(IsUint<5>(shamt)) << shamt;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700605 DsFsmInstrRrr(EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x03), rd, rt, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200606}
607
608void MipsAssembler::Sllv(Register rd, Register rt, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700609 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x04), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700610}
611
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200612void MipsAssembler::Srlv(Register rd, Register rt, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700613 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x06), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700614}
615
Chris Larsene16ce5a2015-11-18 12:30:20 -0800616void MipsAssembler::Rotrv(Register rd, Register rt, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700617 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 1, 0x06), rd, rs, rt);
Chris Larsene16ce5a2015-11-18 12:30:20 -0800618}
619
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200620void MipsAssembler::Srav(Register rd, Register rt, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700621 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x07), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700622}
623
Alexey Frunze5c7aed32015-11-25 19:41:54 -0800624void MipsAssembler::Ext(Register rd, Register rt, int pos, int size) {
625 CHECK(IsUint<5>(pos)) << pos;
626 CHECK(0 < size && size <= 32) << size;
627 CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700628 DsFsmInstrRrr(EmitR(0x1f, rt, rd, static_cast<Register>(size - 1), pos, 0x00), rd, rt, rt);
Alexey Frunze5c7aed32015-11-25 19:41:54 -0800629}
630
631void MipsAssembler::Ins(Register rd, Register rt, int pos, int size) {
632 CHECK(IsUint<5>(pos)) << pos;
633 CHECK(0 < size && size <= 32) << size;
634 CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700635 DsFsmInstrRrr(EmitR(0x1f, rt, rd, static_cast<Register>(pos + size - 1), pos, 0x04), rd, rd, rt);
Alexey Frunze5c7aed32015-11-25 19:41:54 -0800636}
637
Chris Larsen692235e2016-11-21 16:04:53 -0800638void MipsAssembler::Lsa(Register rd, Register rs, Register rt, int saPlusOne) {
639 CHECK(IsR6());
640 CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne;
641 int sa = saPlusOne - 1;
642 DsFsmInstrRrr(EmitR(0x0, rs, rt, rd, sa, 0x05), rd, rs, rt);
643}
644
jeffhao7fbee072012-08-24 17:56:54 -0700645void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700646 DsFsmInstrRrr(EmitI(0x20, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700647}
648
649void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700650 DsFsmInstrRrr(EmitI(0x21, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700651}
652
653void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700654 DsFsmInstrRrr(EmitI(0x23, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700655}
656
Chris Larsen3acee732015-11-18 13:31:08 -0800657void MipsAssembler::Lwl(Register rt, Register rs, uint16_t imm16) {
658 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700659 DsFsmInstrRrr(EmitI(0x22, rs, rt, imm16), rt, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800660}
661
662void MipsAssembler::Lwr(Register rt, Register rs, uint16_t imm16) {
663 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700664 DsFsmInstrRrr(EmitI(0x26, rs, rt, imm16), rt, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800665}
666
jeffhao7fbee072012-08-24 17:56:54 -0700667void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700668 DsFsmInstrRrr(EmitI(0x24, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700669}
670
671void MipsAssembler::Lhu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700672 DsFsmInstrRrr(EmitI(0x25, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700673}
674
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700675void MipsAssembler::Lwpc(Register rs, uint32_t imm19) {
676 CHECK(IsR6());
677 CHECK(IsUint<19>(imm19)) << imm19;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700678 DsFsmInstrNop(EmitI21(0x3B, rs, (0x01 << 19) | imm19));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700679}
680
jeffhao7fbee072012-08-24 17:56:54 -0700681void MipsAssembler::Lui(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700682 DsFsmInstrRrr(EmitI(0xf, static_cast<Register>(0), rt, imm16), rt, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700683}
684
Alexey Frunzecad3a4c2016-06-07 23:40:37 -0700685void MipsAssembler::Aui(Register rt, Register rs, uint16_t imm16) {
686 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700687 DsFsmInstrRrr(EmitI(0xf, rs, rt, imm16), rt, rt, rs);
Alexey Frunzecad3a4c2016-06-07 23:40:37 -0700688}
689
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200690void MipsAssembler::Sync(uint32_t stype) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700691 DsFsmInstrNop(EmitR(0, ZERO, ZERO, ZERO, stype & 0x1f, 0xf));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200692}
693
jeffhao7fbee072012-08-24 17:56:54 -0700694void MipsAssembler::Mfhi(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200695 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700696 DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x10), rd, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700697}
698
699void MipsAssembler::Mflo(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200700 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700701 DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x12), rd, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700702}
703
704void MipsAssembler::Sb(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700705 DsFsmInstrRrr(EmitI(0x28, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700706}
707
708void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700709 DsFsmInstrRrr(EmitI(0x29, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700710}
711
712void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700713 DsFsmInstrRrr(EmitI(0x2b, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700714}
715
Chris Larsen3acee732015-11-18 13:31:08 -0800716void MipsAssembler::Swl(Register rt, Register rs, uint16_t imm16) {
717 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700718 DsFsmInstrRrr(EmitI(0x2a, rs, rt, imm16), ZERO, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800719}
720
721void MipsAssembler::Swr(Register rt, Register rs, uint16_t imm16) {
722 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700723 DsFsmInstrRrr(EmitI(0x2e, rs, rt, imm16), ZERO, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800724}
725
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700726void MipsAssembler::LlR2(Register rt, Register base, int16_t imm16) {
727 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700728 DsFsmInstrRrr(EmitI(0x30, base, rt, imm16), rt, base, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700729}
730
731void MipsAssembler::ScR2(Register rt, Register base, int16_t imm16) {
732 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700733 DsFsmInstrRrr(EmitI(0x38, base, rt, imm16), rt, rt, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700734}
735
736void MipsAssembler::LlR6(Register rt, Register base, int16_t imm9) {
737 CHECK(IsR6());
738 CHECK(IsInt<9>(imm9));
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700739 DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36), rt, base, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700740}
741
742void MipsAssembler::ScR6(Register rt, Register base, int16_t imm9) {
743 CHECK(IsR6());
744 CHECK(IsInt<9>(imm9));
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700745 DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26), rt, rt, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700746}
747
jeffhao7fbee072012-08-24 17:56:54 -0700748void MipsAssembler::Slt(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700749 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2a), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700750}
751
752void MipsAssembler::Sltu(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700753 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2b), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700754}
755
756void MipsAssembler::Slti(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700757 DsFsmInstrRrr(EmitI(0xa, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700758}
759
760void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700761 DsFsmInstrRrr(EmitI(0xb, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700762}
763
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200764void MipsAssembler::B(uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700765 DsFsmInstrNop(EmitI(0x4, static_cast<Register>(0), static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200766}
767
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700768void MipsAssembler::Bal(uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700769 DsFsmInstrNop(EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x11), imm16));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700770}
771
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200772void MipsAssembler::Beq(Register rs, Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700773 DsFsmInstrNop(EmitI(0x4, rs, rt, imm16));
jeffhao7fbee072012-08-24 17:56:54 -0700774}
775
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200776void MipsAssembler::Bne(Register rs, Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700777 DsFsmInstrNop(EmitI(0x5, rs, rt, imm16));
jeffhao7fbee072012-08-24 17:56:54 -0700778}
779
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200780void MipsAssembler::Beqz(Register rt, uint16_t imm16) {
781 Beq(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700782}
783
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200784void MipsAssembler::Bnez(Register rt, uint16_t imm16) {
785 Bne(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700786}
787
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200788void MipsAssembler::Bltz(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700789 DsFsmInstrNop(EmitI(0x1, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200790}
791
792void MipsAssembler::Bgez(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700793 DsFsmInstrNop(EmitI(0x1, rt, static_cast<Register>(0x1), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200794}
795
796void MipsAssembler::Blez(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700797 DsFsmInstrNop(EmitI(0x6, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200798}
799
800void MipsAssembler::Bgtz(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700801 DsFsmInstrNop(EmitI(0x7, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200802}
803
Chris Larsenb74353a2015-11-20 09:07:09 -0800804void MipsAssembler::Bc1f(uint16_t imm16) {
805 Bc1f(0, imm16);
806}
807
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800808void MipsAssembler::Bc1f(int cc, uint16_t imm16) {
809 CHECK(!IsR6());
810 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700811 DsFsmInstrNop(EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>(cc << 2), imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800812}
813
Chris Larsenb74353a2015-11-20 09:07:09 -0800814void MipsAssembler::Bc1t(uint16_t imm16) {
815 Bc1t(0, imm16);
816}
817
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800818void MipsAssembler::Bc1t(int cc, uint16_t imm16) {
819 CHECK(!IsR6());
820 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700821 DsFsmInstrNop(EmitI(0x11,
822 static_cast<Register>(0x8),
823 static_cast<Register>((cc << 2) | 1),
824 imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800825}
826
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200827void MipsAssembler::J(uint32_t addr26) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700828 DsFsmInstrNop(EmitI26(0x2, addr26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200829}
830
831void MipsAssembler::Jal(uint32_t addr26) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700832 DsFsmInstrNop(EmitI26(0x3, addr26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200833}
834
835void MipsAssembler::Jalr(Register rd, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700836 uint32_t last_instruction = delay_slot_.instruction_;
837 bool exchange = (last_instruction != 0 &&
838 (delay_slot_.gpr_outs_mask_ & (1u << rs)) == 0 &&
839 ((delay_slot_.gpr_ins_mask_ | delay_slot_.gpr_outs_mask_) & (1u << rd)) == 0);
840 if (exchange) {
841 // The last instruction cannot be used in a different delay slot,
842 // do not commit the label before it (if any).
843 DsFsmDropLabel();
844 }
845 DsFsmInstrNop(EmitR(0, rs, static_cast<Register>(0), rd, 0, 0x09));
846 if (exchange) {
847 // Exchange the last two instructions in the assembler buffer.
848 size_t size = buffer_.Size();
849 CHECK_GE(size, 2 * sizeof(uint32_t));
850 size_t pos1 = size - 2 * sizeof(uint32_t);
851 size_t pos2 = size - sizeof(uint32_t);
852 uint32_t instr1 = buffer_.Load<uint32_t>(pos1);
853 uint32_t instr2 = buffer_.Load<uint32_t>(pos2);
854 CHECK_EQ(instr1, last_instruction);
855 buffer_.Store<uint32_t>(pos1, instr2);
856 buffer_.Store<uint32_t>(pos2, instr1);
857 } else if (reordering_) {
858 Nop();
859 }
jeffhao7fbee072012-08-24 17:56:54 -0700860}
861
862void MipsAssembler::Jalr(Register rs) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200863 Jalr(RA, rs);
864}
865
866void MipsAssembler::Jr(Register rs) {
867 Jalr(ZERO, rs);
868}
869
870void MipsAssembler::Nal() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700871 DsFsmInstrNop(EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x10), 0));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200872}
873
874void MipsAssembler::Auipc(Register rs, uint16_t imm16) {
875 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700876 DsFsmInstrNop(EmitI(0x3B, rs, static_cast<Register>(0x1E), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200877}
878
879void MipsAssembler::Addiupc(Register rs, uint32_t imm19) {
880 CHECK(IsR6());
881 CHECK(IsUint<19>(imm19)) << imm19;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700882 DsFsmInstrNop(EmitI21(0x3B, rs, imm19));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200883}
884
885void MipsAssembler::Bc(uint32_t imm26) {
886 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700887 DsFsmInstrNop(EmitI26(0x32, imm26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200888}
889
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700890void MipsAssembler::Balc(uint32_t imm26) {
891 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700892 DsFsmInstrNop(EmitI26(0x3A, imm26));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700893}
894
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200895void MipsAssembler::Jic(Register rt, uint16_t imm16) {
896 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700897 DsFsmInstrNop(EmitI(0x36, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200898}
899
900void MipsAssembler::Jialc(Register rt, uint16_t imm16) {
901 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700902 DsFsmInstrNop(EmitI(0x3E, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200903}
904
905void MipsAssembler::Bltc(Register rs, Register rt, uint16_t imm16) {
906 CHECK(IsR6());
907 CHECK_NE(rs, ZERO);
908 CHECK_NE(rt, ZERO);
909 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700910 DsFsmInstrNop(EmitI(0x17, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200911}
912
913void MipsAssembler::Bltzc(Register rt, uint16_t imm16) {
914 CHECK(IsR6());
915 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700916 DsFsmInstrNop(EmitI(0x17, rt, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200917}
918
919void MipsAssembler::Bgtzc(Register rt, uint16_t imm16) {
920 CHECK(IsR6());
921 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700922 DsFsmInstrNop(EmitI(0x17, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200923}
924
925void MipsAssembler::Bgec(Register rs, Register rt, uint16_t imm16) {
926 CHECK(IsR6());
927 CHECK_NE(rs, ZERO);
928 CHECK_NE(rt, ZERO);
929 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700930 DsFsmInstrNop(EmitI(0x16, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200931}
932
933void MipsAssembler::Bgezc(Register rt, uint16_t imm16) {
934 CHECK(IsR6());
935 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700936 DsFsmInstrNop(EmitI(0x16, rt, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200937}
938
939void MipsAssembler::Blezc(Register rt, uint16_t imm16) {
940 CHECK(IsR6());
941 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700942 DsFsmInstrNop(EmitI(0x16, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200943}
944
945void MipsAssembler::Bltuc(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(0x7, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200951}
952
953void MipsAssembler::Bgeuc(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(0x6, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200959}
960
961void MipsAssembler::Beqc(Register rs, Register rt, uint16_t imm16) {
962 CHECK(IsR6());
963 CHECK_NE(rs, ZERO);
964 CHECK_NE(rt, ZERO);
965 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700966 DsFsmInstrNop(EmitI(0x8, std::min(rs, rt), std::max(rs, rt), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200967}
968
969void MipsAssembler::Bnec(Register rs, Register rt, uint16_t imm16) {
970 CHECK(IsR6());
971 CHECK_NE(rs, ZERO);
972 CHECK_NE(rt, ZERO);
973 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700974 DsFsmInstrNop(EmitI(0x18, std::min(rs, rt), std::max(rs, rt), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200975}
976
977void MipsAssembler::Beqzc(Register rs, uint32_t imm21) {
978 CHECK(IsR6());
979 CHECK_NE(rs, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700980 DsFsmInstrNop(EmitI21(0x36, rs, imm21));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200981}
982
983void MipsAssembler::Bnezc(Register rs, uint32_t imm21) {
984 CHECK(IsR6());
985 CHECK_NE(rs, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700986 DsFsmInstrNop(EmitI21(0x3E, rs, imm21));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200987}
988
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800989void MipsAssembler::Bc1eqz(FRegister ft, uint16_t imm16) {
990 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700991 DsFsmInstrNop(EmitFI(0x11, 0x9, ft, imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800992}
993
994void MipsAssembler::Bc1nez(FRegister ft, uint16_t imm16) {
995 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700996 DsFsmInstrNop(EmitFI(0x11, 0xD, ft, imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800997}
998
999void MipsAssembler::EmitBcondR2(BranchCondition cond, Register rs, Register rt, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001000 switch (cond) {
1001 case kCondLTZ:
1002 CHECK_EQ(rt, ZERO);
1003 Bltz(rs, imm16);
1004 break;
1005 case kCondGEZ:
1006 CHECK_EQ(rt, ZERO);
1007 Bgez(rs, imm16);
1008 break;
1009 case kCondLEZ:
1010 CHECK_EQ(rt, ZERO);
1011 Blez(rs, imm16);
1012 break;
1013 case kCondGTZ:
1014 CHECK_EQ(rt, ZERO);
1015 Bgtz(rs, imm16);
1016 break;
1017 case kCondEQ:
1018 Beq(rs, rt, imm16);
1019 break;
1020 case kCondNE:
1021 Bne(rs, rt, imm16);
1022 break;
1023 case kCondEQZ:
1024 CHECK_EQ(rt, ZERO);
1025 Beqz(rs, imm16);
1026 break;
1027 case kCondNEZ:
1028 CHECK_EQ(rt, ZERO);
1029 Bnez(rs, imm16);
1030 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001031 case kCondF:
1032 CHECK_EQ(rt, ZERO);
1033 Bc1f(static_cast<int>(rs), imm16);
1034 break;
1035 case kCondT:
1036 CHECK_EQ(rt, ZERO);
1037 Bc1t(static_cast<int>(rs), imm16);
1038 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001039 case kCondLT:
1040 case kCondGE:
1041 case kCondLE:
1042 case kCondGT:
1043 case kCondLTU:
1044 case kCondGEU:
1045 case kUncond:
1046 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
1047 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
1048 LOG(FATAL) << "Unexpected branch condition " << cond;
1049 UNREACHABLE();
1050 }
1051}
1052
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001053void MipsAssembler::EmitBcondR6(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001054 switch (cond) {
1055 case kCondLT:
1056 Bltc(rs, rt, imm16_21);
1057 break;
1058 case kCondGE:
1059 Bgec(rs, rt, imm16_21);
1060 break;
1061 case kCondLE:
1062 Bgec(rt, rs, imm16_21);
1063 break;
1064 case kCondGT:
1065 Bltc(rt, rs, imm16_21);
1066 break;
1067 case kCondLTZ:
1068 CHECK_EQ(rt, ZERO);
1069 Bltzc(rs, imm16_21);
1070 break;
1071 case kCondGEZ:
1072 CHECK_EQ(rt, ZERO);
1073 Bgezc(rs, imm16_21);
1074 break;
1075 case kCondLEZ:
1076 CHECK_EQ(rt, ZERO);
1077 Blezc(rs, imm16_21);
1078 break;
1079 case kCondGTZ:
1080 CHECK_EQ(rt, ZERO);
1081 Bgtzc(rs, imm16_21);
1082 break;
1083 case kCondEQ:
1084 Beqc(rs, rt, imm16_21);
1085 break;
1086 case kCondNE:
1087 Bnec(rs, rt, imm16_21);
1088 break;
1089 case kCondEQZ:
1090 CHECK_EQ(rt, ZERO);
1091 Beqzc(rs, imm16_21);
1092 break;
1093 case kCondNEZ:
1094 CHECK_EQ(rt, ZERO);
1095 Bnezc(rs, imm16_21);
1096 break;
1097 case kCondLTU:
1098 Bltuc(rs, rt, imm16_21);
1099 break;
1100 case kCondGEU:
1101 Bgeuc(rs, rt, imm16_21);
1102 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001103 case kCondF:
1104 CHECK_EQ(rt, ZERO);
1105 Bc1eqz(static_cast<FRegister>(rs), imm16_21);
1106 break;
1107 case kCondT:
1108 CHECK_EQ(rt, ZERO);
1109 Bc1nez(static_cast<FRegister>(rs), imm16_21);
1110 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001111 case kUncond:
1112 LOG(FATAL) << "Unexpected branch condition " << cond;
1113 UNREACHABLE();
1114 }
jeffhao7fbee072012-08-24 17:56:54 -07001115}
1116
1117void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001118 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x0), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001119}
1120
1121void MipsAssembler::SubS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001122 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001123}
1124
1125void MipsAssembler::MulS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001126 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x2), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001127}
1128
1129void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001130 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x3), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001131}
1132
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001133void MipsAssembler::AddD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001134 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x0), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001135}
1136
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001137void MipsAssembler::SubD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001138 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001139}
1140
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001141void MipsAssembler::MulD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001142 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x2), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001143}
1144
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001145void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001146 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x3), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001147}
1148
Chris Larsenb74353a2015-11-20 09:07:09 -08001149void MipsAssembler::SqrtS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001150 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x4), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001151}
1152
1153void MipsAssembler::SqrtD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001154 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x4), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001155}
1156
1157void MipsAssembler::AbsS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001158 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x5), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001159}
1160
1161void MipsAssembler::AbsD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001162 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x5), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001163}
1164
jeffhao7fbee072012-08-24 17:56:54 -07001165void MipsAssembler::MovS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001166 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6), fd, fs, fs);
jeffhao7fbee072012-08-24 17:56:54 -07001167}
1168
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001169void MipsAssembler::MovD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001170 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x6), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001171}
1172
1173void MipsAssembler::NegS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001174 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x7), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001175}
1176
1177void MipsAssembler::NegD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001178 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001179}
1180
Chris Larsenb74353a2015-11-20 09:07:09 -08001181void MipsAssembler::CunS(FRegister fs, FRegister ft) {
1182 CunS(0, fs, ft);
1183}
1184
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001185void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) {
1186 CHECK(!IsR6());
1187 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001188 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x31), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001189}
1190
Chris Larsenb74353a2015-11-20 09:07:09 -08001191void MipsAssembler::CeqS(FRegister fs, FRegister ft) {
1192 CeqS(0, fs, ft);
1193}
1194
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001195void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) {
1196 CHECK(!IsR6());
1197 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001198 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x32), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001199}
1200
Chris Larsenb74353a2015-11-20 09:07:09 -08001201void MipsAssembler::CueqS(FRegister fs, FRegister ft) {
1202 CueqS(0, fs, ft);
1203}
1204
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001205void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) {
1206 CHECK(!IsR6());
1207 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001208 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x33), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001209}
1210
Chris Larsenb74353a2015-11-20 09:07:09 -08001211void MipsAssembler::ColtS(FRegister fs, FRegister ft) {
1212 ColtS(0, fs, ft);
1213}
1214
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001215void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) {
1216 CHECK(!IsR6());
1217 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001218 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x34), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001219}
1220
Chris Larsenb74353a2015-11-20 09:07:09 -08001221void MipsAssembler::CultS(FRegister fs, FRegister ft) {
1222 CultS(0, fs, ft);
1223}
1224
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001225void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) {
1226 CHECK(!IsR6());
1227 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001228 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x35), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001229}
1230
Chris Larsenb74353a2015-11-20 09:07:09 -08001231void MipsAssembler::ColeS(FRegister fs, FRegister ft) {
1232 ColeS(0, fs, ft);
1233}
1234
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001235void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) {
1236 CHECK(!IsR6());
1237 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001238 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x36), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001239}
1240
Chris Larsenb74353a2015-11-20 09:07:09 -08001241void MipsAssembler::CuleS(FRegister fs, FRegister ft) {
1242 CuleS(0, fs, ft);
1243}
1244
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001245void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) {
1246 CHECK(!IsR6());
1247 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001248 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x37), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001249}
1250
Chris Larsenb74353a2015-11-20 09:07:09 -08001251void MipsAssembler::CunD(FRegister fs, FRegister ft) {
1252 CunD(0, fs, ft);
1253}
1254
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001255void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) {
1256 CHECK(!IsR6());
1257 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001258 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x31), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001259}
1260
Chris Larsenb74353a2015-11-20 09:07:09 -08001261void MipsAssembler::CeqD(FRegister fs, FRegister ft) {
1262 CeqD(0, fs, ft);
1263}
1264
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001265void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) {
1266 CHECK(!IsR6());
1267 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001268 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x32), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001269}
1270
Chris Larsenb74353a2015-11-20 09:07:09 -08001271void MipsAssembler::CueqD(FRegister fs, FRegister ft) {
1272 CueqD(0, fs, ft);
1273}
1274
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001275void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) {
1276 CHECK(!IsR6());
1277 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001278 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x33), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001279}
1280
Chris Larsenb74353a2015-11-20 09:07:09 -08001281void MipsAssembler::ColtD(FRegister fs, FRegister ft) {
1282 ColtD(0, fs, ft);
1283}
1284
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001285void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) {
1286 CHECK(!IsR6());
1287 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001288 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x34), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001289}
1290
Chris Larsenb74353a2015-11-20 09:07:09 -08001291void MipsAssembler::CultD(FRegister fs, FRegister ft) {
1292 CultD(0, fs, ft);
1293}
1294
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001295void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) {
1296 CHECK(!IsR6());
1297 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001298 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x35), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001299}
1300
Chris Larsenb74353a2015-11-20 09:07:09 -08001301void MipsAssembler::ColeD(FRegister fs, FRegister ft) {
1302 ColeD(0, fs, ft);
1303}
1304
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001305void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) {
1306 CHECK(!IsR6());
1307 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001308 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x36), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001309}
1310
Chris Larsenb74353a2015-11-20 09:07:09 -08001311void MipsAssembler::CuleD(FRegister fs, FRegister ft) {
1312 CuleD(0, fs, ft);
1313}
1314
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001315void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) {
1316 CHECK(!IsR6());
1317 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001318 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x37), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001319}
1320
1321void MipsAssembler::CmpUnS(FRegister fd, FRegister fs, FRegister ft) {
1322 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001323 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x01), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001324}
1325
1326void MipsAssembler::CmpEqS(FRegister fd, FRegister fs, FRegister ft) {
1327 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001328 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x02), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001329}
1330
1331void MipsAssembler::CmpUeqS(FRegister fd, FRegister fs, FRegister ft) {
1332 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001333 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x03), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001334}
1335
1336void MipsAssembler::CmpLtS(FRegister fd, FRegister fs, FRegister ft) {
1337 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001338 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x04), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001339}
1340
1341void MipsAssembler::CmpUltS(FRegister fd, FRegister fs, FRegister ft) {
1342 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001343 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x05), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001344}
1345
1346void MipsAssembler::CmpLeS(FRegister fd, FRegister fs, FRegister ft) {
1347 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001348 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x06), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001349}
1350
1351void MipsAssembler::CmpUleS(FRegister fd, FRegister fs, FRegister ft) {
1352 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001353 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x07), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001354}
1355
1356void MipsAssembler::CmpOrS(FRegister fd, FRegister fs, FRegister ft) {
1357 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001358 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x11), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001359}
1360
1361void MipsAssembler::CmpUneS(FRegister fd, FRegister fs, FRegister ft) {
1362 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001363 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x12), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001364}
1365
1366void MipsAssembler::CmpNeS(FRegister fd, FRegister fs, FRegister ft) {
1367 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001368 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x13), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001369}
1370
1371void MipsAssembler::CmpUnD(FRegister fd, FRegister fs, FRegister ft) {
1372 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001373 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x01), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001374}
1375
1376void MipsAssembler::CmpEqD(FRegister fd, FRegister fs, FRegister ft) {
1377 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001378 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x02), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001379}
1380
1381void MipsAssembler::CmpUeqD(FRegister fd, FRegister fs, FRegister ft) {
1382 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001383 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x03), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001384}
1385
1386void MipsAssembler::CmpLtD(FRegister fd, FRegister fs, FRegister ft) {
1387 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001388 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x04), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001389}
1390
1391void MipsAssembler::CmpUltD(FRegister fd, FRegister fs, FRegister ft) {
1392 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001393 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x05), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001394}
1395
1396void MipsAssembler::CmpLeD(FRegister fd, FRegister fs, FRegister ft) {
1397 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001398 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x06), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001399}
1400
1401void MipsAssembler::CmpUleD(FRegister fd, FRegister fs, FRegister ft) {
1402 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001403 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x07), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001404}
1405
1406void MipsAssembler::CmpOrD(FRegister fd, FRegister fs, FRegister ft) {
1407 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001408 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x11), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001409}
1410
1411void MipsAssembler::CmpUneD(FRegister fd, FRegister fs, FRegister ft) {
1412 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001413 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x12), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001414}
1415
1416void MipsAssembler::CmpNeD(FRegister fd, FRegister fs, FRegister ft) {
1417 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001418 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x13), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001419}
1420
1421void MipsAssembler::Movf(Register rd, Register rs, int cc) {
1422 CHECK(!IsR6());
1423 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001424 DsFsmInstrRrrc(EmitR(0, rs, static_cast<Register>(cc << 2), rd, 0, 0x01), rd, rs, cc);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001425}
1426
1427void MipsAssembler::Movt(Register rd, Register rs, int cc) {
1428 CHECK(!IsR6());
1429 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001430 DsFsmInstrRrrc(EmitR(0, rs, static_cast<Register>((cc << 2) | 1), rd, 0, 0x01), rd, rs, cc);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001431}
1432
Chris Larsenb74353a2015-11-20 09:07:09 -08001433void MipsAssembler::MovfS(FRegister fd, FRegister fs, int cc) {
1434 CHECK(!IsR6());
1435 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001436 DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast<FRegister>(cc << 2), fs, fd, 0x11), fd, fs, cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001437}
1438
1439void MipsAssembler::MovfD(FRegister fd, FRegister fs, int cc) {
1440 CHECK(!IsR6());
1441 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001442 DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast<FRegister>(cc << 2), fs, fd, 0x11), fd, fs, cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001443}
1444
1445void MipsAssembler::MovtS(FRegister fd, FRegister fs, int cc) {
1446 CHECK(!IsR6());
1447 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001448 DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11),
1449 fd,
1450 fs,
1451 cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001452}
1453
1454void MipsAssembler::MovtD(FRegister fd, FRegister fs, int cc) {
1455 CHECK(!IsR6());
1456 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001457 DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11),
1458 fd,
1459 fs,
1460 cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001461}
1462
Alexey Frunze674b9ee2016-09-20 14:54:15 -07001463void MipsAssembler::MovzS(FRegister fd, FRegister fs, Register rt) {
1464 CHECK(!IsR6());
1465 DsFsmInstrFffr(EmitFR(0x11, 0x10, static_cast<FRegister>(rt), fs, fd, 0x12), fd, fs, rt);
1466}
1467
1468void MipsAssembler::MovzD(FRegister fd, FRegister fs, Register rt) {
1469 CHECK(!IsR6());
1470 DsFsmInstrFffr(EmitFR(0x11, 0x11, static_cast<FRegister>(rt), fs, fd, 0x12), fd, fs, rt);
1471}
1472
1473void MipsAssembler::MovnS(FRegister fd, FRegister fs, Register rt) {
1474 CHECK(!IsR6());
1475 DsFsmInstrFffr(EmitFR(0x11, 0x10, static_cast<FRegister>(rt), fs, fd, 0x13), fd, fs, rt);
1476}
1477
1478void MipsAssembler::MovnD(FRegister fd, FRegister fs, Register rt) {
1479 CHECK(!IsR6());
1480 DsFsmInstrFffr(EmitFR(0x11, 0x11, static_cast<FRegister>(rt), fs, fd, 0x13), fd, fs, rt);
1481}
1482
Chris Larsenb74353a2015-11-20 09:07:09 -08001483void MipsAssembler::SelS(FRegister fd, FRegister fs, FRegister ft) {
1484 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001485 DsFsmInstrFfff(EmitFR(0x11, 0x10, ft, fs, fd, 0x10), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001486}
1487
1488void MipsAssembler::SelD(FRegister fd, FRegister fs, FRegister ft) {
1489 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001490 DsFsmInstrFfff(EmitFR(0x11, 0x11, ft, fs, fd, 0x10), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001491}
1492
Alexey Frunze674b9ee2016-09-20 14:54:15 -07001493void MipsAssembler::SeleqzS(FRegister fd, FRegister fs, FRegister ft) {
1494 CHECK(IsR6());
1495 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x14), fd, fs, ft);
1496}
1497
1498void MipsAssembler::SeleqzD(FRegister fd, FRegister fs, FRegister ft) {
1499 CHECK(IsR6());
1500 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x14), fd, fs, ft);
1501}
1502
1503void MipsAssembler::SelnezS(FRegister fd, FRegister fs, FRegister ft) {
1504 CHECK(IsR6());
1505 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x17), fd, fs, ft);
1506}
1507
1508void MipsAssembler::SelnezD(FRegister fd, FRegister fs, FRegister ft) {
1509 CHECK(IsR6());
1510 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x17), fd, fs, ft);
1511}
1512
Chris Larsenb74353a2015-11-20 09:07:09 -08001513void MipsAssembler::ClassS(FRegister fd, FRegister fs) {
1514 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001515 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x1b), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001516}
1517
1518void MipsAssembler::ClassD(FRegister fd, FRegister fs) {
1519 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001520 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x1b), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001521}
1522
1523void MipsAssembler::MinS(FRegister fd, FRegister fs, FRegister ft) {
1524 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001525 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1c), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001526}
1527
1528void MipsAssembler::MinD(FRegister fd, FRegister fs, FRegister ft) {
1529 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001530 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1c), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001531}
1532
1533void MipsAssembler::MaxS(FRegister fd, FRegister fs, FRegister ft) {
1534 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001535 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1e), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001536}
1537
1538void MipsAssembler::MaxD(FRegister fd, FRegister fs, FRegister ft) {
1539 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001540 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1e), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001541}
1542
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001543void MipsAssembler::TruncLS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001544 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x09), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001545}
1546
1547void MipsAssembler::TruncLD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001548 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x09), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001549}
1550
1551void MipsAssembler::TruncWS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001552 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x0D), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001553}
1554
1555void MipsAssembler::TruncWD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001556 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x0D), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001557}
1558
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001559void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001560 DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001561}
1562
1563void MipsAssembler::Cvtdw(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001564 DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001565}
1566
1567void MipsAssembler::Cvtsd(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001568 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001569}
1570
1571void MipsAssembler::Cvtds(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001572 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
jeffhao7fbee072012-08-24 17:56:54 -07001573}
1574
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001575void MipsAssembler::Cvtsl(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001576 DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001577}
1578
1579void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001580 DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001581}
1582
Chris Larsenb74353a2015-11-20 09:07:09 -08001583void MipsAssembler::FloorWS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001584 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0xf), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001585}
1586
1587void MipsAssembler::FloorWD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001588 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0xf), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001589}
1590
jeffhao7fbee072012-08-24 17:56:54 -07001591void MipsAssembler::Mfc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001592 DsFsmInstrRf(EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1593 rt,
1594 fs);
jeffhao7fbee072012-08-24 17:56:54 -07001595}
1596
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001597void MipsAssembler::Mtc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001598 DsFsmInstrFr(EmitFR(0x11, 0x04, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1599 fs,
1600 rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001601}
1602
1603void MipsAssembler::Mfhc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001604 DsFsmInstrRf(EmitFR(0x11, 0x03, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1605 rt,
1606 fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001607}
1608
1609void MipsAssembler::Mthc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001610 DsFsmInstrFr(EmitFR(0x11, 0x07, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1611 fs,
1612 rt);
jeffhao7fbee072012-08-24 17:56:54 -07001613}
1614
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001615void MipsAssembler::MoveFromFpuHigh(Register rt, FRegister fs) {
1616 if (Is32BitFPU()) {
1617 CHECK_EQ(fs % 2, 0) << fs;
1618 Mfc1(rt, static_cast<FRegister>(fs + 1));
1619 } else {
1620 Mfhc1(rt, fs);
1621 }
1622}
1623
1624void MipsAssembler::MoveToFpuHigh(Register rt, FRegister fs) {
1625 if (Is32BitFPU()) {
1626 CHECK_EQ(fs % 2, 0) << fs;
1627 Mtc1(rt, static_cast<FRegister>(fs + 1));
1628 } else {
1629 Mthc1(rt, fs);
1630 }
1631}
1632
jeffhao7fbee072012-08-24 17:56:54 -07001633void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001634 DsFsmInstrFr(EmitI(0x31, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001635}
1636
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001637void MipsAssembler::Ldc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001638 DsFsmInstrFr(EmitI(0x35, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001639}
1640
1641void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001642 DsFsmInstrFR(EmitI(0x39, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001643}
1644
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001645void MipsAssembler::Sdc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001646 DsFsmInstrFR(EmitI(0x3d, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001647}
1648
1649void MipsAssembler::Break() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001650 DsFsmInstrNop(EmitR(0, ZERO, ZERO, ZERO, 0, 0xD));
jeffhao7fbee072012-08-24 17:56:54 -07001651}
1652
jeffhao07030602012-09-26 14:33:14 -07001653void MipsAssembler::Nop() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001654 DsFsmInstrNop(EmitR(0x0, ZERO, ZERO, ZERO, 0, 0x0));
1655}
1656
1657void MipsAssembler::NopIfNoReordering() {
1658 if (!reordering_) {
1659 Nop();
1660 }
jeffhao07030602012-09-26 14:33:14 -07001661}
1662
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001663void MipsAssembler::Move(Register rd, Register rs) {
1664 Or(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001665}
1666
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001667void MipsAssembler::Clear(Register rd) {
1668 Move(rd, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001669}
1670
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001671void MipsAssembler::Not(Register rd, Register rs) {
1672 Nor(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001673}
1674
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001675void MipsAssembler::Push(Register rs) {
1676 IncreaseFrameSize(kMipsWordSize);
1677 Sw(rs, SP, 0);
jeffhao7fbee072012-08-24 17:56:54 -07001678}
1679
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001680void MipsAssembler::Pop(Register rd) {
1681 Lw(rd, SP, 0);
1682 DecreaseFrameSize(kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07001683}
1684
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001685void MipsAssembler::PopAndReturn(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001686 bool reordering = SetReorder(false);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001687 Lw(rd, SP, 0);
1688 Jr(rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001689 DecreaseFrameSize(kMipsWordSize); // Single instruction in delay slot.
1690 SetReorder(reordering);
jeffhao7fbee072012-08-24 17:56:54 -07001691}
1692
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001693void MipsAssembler::LoadConst32(Register rd, int32_t value) {
1694 if (IsUint<16>(value)) {
1695 // Use OR with (unsigned) immediate to encode 16b unsigned int.
1696 Ori(rd, ZERO, value);
1697 } else if (IsInt<16>(value)) {
1698 // Use ADD with (signed) immediate to encode 16b signed int.
1699 Addiu(rd, ZERO, value);
jeffhao7fbee072012-08-24 17:56:54 -07001700 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001701 Lui(rd, High16Bits(value));
1702 if (value & 0xFFFF)
1703 Ori(rd, rd, Low16Bits(value));
1704 }
1705}
1706
1707void MipsAssembler::LoadConst64(Register reg_hi, Register reg_lo, int64_t value) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001708 uint32_t low = Low32Bits(value);
1709 uint32_t high = High32Bits(value);
1710 LoadConst32(reg_lo, low);
1711 if (high != low) {
1712 LoadConst32(reg_hi, high);
1713 } else {
1714 Move(reg_hi, reg_lo);
1715 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001716}
1717
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001718void MipsAssembler::LoadSConst32(FRegister r, int32_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001719 if (value == 0) {
1720 temp = ZERO;
1721 } else {
1722 LoadConst32(temp, value);
1723 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001724 Mtc1(temp, r);
1725}
1726
1727void MipsAssembler::LoadDConst64(FRegister rd, int64_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001728 uint32_t low = Low32Bits(value);
1729 uint32_t high = High32Bits(value);
1730 if (low == 0) {
1731 Mtc1(ZERO, rd);
1732 } else {
1733 LoadConst32(temp, low);
1734 Mtc1(temp, rd);
1735 }
1736 if (high == 0) {
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001737 MoveToFpuHigh(ZERO, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001738 } else {
1739 LoadConst32(temp, high);
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001740 MoveToFpuHigh(temp, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001741 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001742}
1743
1744void MipsAssembler::Addiu32(Register rt, Register rs, int32_t value, Register temp) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001745 CHECK_NE(rs, temp); // Must not overwrite the register `rs` while loading `value`.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001746 if (IsInt<16>(value)) {
1747 Addiu(rt, rs, value);
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001748 } else if (IsR6()) {
1749 int16_t high = High16Bits(value);
1750 int16_t low = Low16Bits(value);
1751 high += (low < 0) ? 1 : 0; // Account for sign extension in addiu.
1752 if (low != 0) {
1753 Aui(temp, rs, high);
1754 Addiu(rt, temp, low);
1755 } else {
1756 Aui(rt, rs, high);
1757 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001758 } else {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001759 // Do not load the whole 32-bit `value` if it can be represented as
1760 // a sum of two 16-bit signed values. This can save an instruction.
1761 constexpr int32_t kMinValueForSimpleAdjustment = std::numeric_limits<int16_t>::min() * 2;
1762 constexpr int32_t kMaxValueForSimpleAdjustment = std::numeric_limits<int16_t>::max() * 2;
1763 if (0 <= value && value <= kMaxValueForSimpleAdjustment) {
1764 Addiu(temp, rs, kMaxValueForSimpleAdjustment / 2);
1765 Addiu(rt, temp, value - kMaxValueForSimpleAdjustment / 2);
1766 } else if (kMinValueForSimpleAdjustment <= value && value < 0) {
1767 Addiu(temp, rs, kMinValueForSimpleAdjustment / 2);
1768 Addiu(rt, temp, value - kMinValueForSimpleAdjustment / 2);
1769 } else {
1770 // Now that all shorter options have been exhausted, load the full 32-bit value.
1771 LoadConst32(temp, value);
1772 Addu(rt, rs, temp);
1773 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001774 }
1775}
1776
1777void MipsAssembler::Branch::InitShortOrLong(MipsAssembler::Branch::OffsetBits offset_size,
1778 MipsAssembler::Branch::Type short_type,
1779 MipsAssembler::Branch::Type long_type) {
1780 type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type;
1781}
1782
Alexey Frunze96b66822016-09-10 02:32:44 -07001783void MipsAssembler::Branch::InitializeType(Type initial_type, bool is_r6) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001784 OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_);
1785 if (is_r6) {
1786 // R6
Alexey Frunze96b66822016-09-10 02:32:44 -07001787 switch (initial_type) {
1788 case kLabel:
1789 CHECK(!IsResolved());
1790 type_ = kR6Label;
1791 break;
1792 case kLiteral:
1793 CHECK(!IsResolved());
1794 type_ = kR6Literal;
1795 break;
1796 case kCall:
1797 InitShortOrLong(offset_size, kR6Call, kR6LongCall);
1798 break;
1799 case kCondBranch:
1800 switch (condition_) {
1801 case kUncond:
1802 InitShortOrLong(offset_size, kR6UncondBranch, kR6LongUncondBranch);
1803 break;
1804 case kCondEQZ:
1805 case kCondNEZ:
1806 // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions.
1807 type_ = (offset_size <= kOffset23) ? kR6CondBranch : kR6LongCondBranch;
1808 break;
1809 default:
1810 InitShortOrLong(offset_size, kR6CondBranch, kR6LongCondBranch);
1811 break;
1812 }
1813 break;
1814 default:
1815 LOG(FATAL) << "Unexpected branch type " << initial_type;
1816 UNREACHABLE();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001817 }
1818 } else {
1819 // R2
Alexey Frunze96b66822016-09-10 02:32:44 -07001820 switch (initial_type) {
1821 case kLabel:
1822 CHECK(!IsResolved());
1823 type_ = kLabel;
1824 break;
1825 case kLiteral:
1826 CHECK(!IsResolved());
1827 type_ = kLiteral;
1828 break;
1829 case kCall:
1830 InitShortOrLong(offset_size, kCall, kLongCall);
1831 break;
1832 case kCondBranch:
1833 switch (condition_) {
1834 case kUncond:
1835 InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch);
1836 break;
1837 default:
1838 InitShortOrLong(offset_size, kCondBranch, kLongCondBranch);
1839 break;
1840 }
1841 break;
1842 default:
1843 LOG(FATAL) << "Unexpected branch type " << initial_type;
1844 UNREACHABLE();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001845 }
1846 }
1847 old_type_ = type_;
1848}
1849
1850bool MipsAssembler::Branch::IsNop(BranchCondition condition, Register lhs, Register rhs) {
1851 switch (condition) {
1852 case kCondLT:
1853 case kCondGT:
1854 case kCondNE:
1855 case kCondLTU:
1856 return lhs == rhs;
1857 default:
1858 return false;
1859 }
1860}
1861
1862bool MipsAssembler::Branch::IsUncond(BranchCondition condition, Register lhs, Register rhs) {
1863 switch (condition) {
1864 case kUncond:
1865 return true;
1866 case kCondGE:
1867 case kCondLE:
1868 case kCondEQ:
1869 case kCondGEU:
1870 return lhs == rhs;
1871 default:
1872 return false;
1873 }
1874}
1875
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001876MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target, bool is_call)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001877 : old_location_(location),
1878 location_(location),
1879 target_(target),
1880 lhs_reg_(0),
1881 rhs_reg_(0),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001882 condition_(kUncond),
1883 delayed_instruction_(kUnfilledDelaySlot) {
Alexey Frunze96b66822016-09-10 02:32:44 -07001884 InitializeType((is_call ? kCall : kCondBranch), is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001885}
1886
1887MipsAssembler::Branch::Branch(bool is_r6,
1888 uint32_t location,
1889 uint32_t target,
1890 MipsAssembler::BranchCondition condition,
1891 Register lhs_reg,
1892 Register rhs_reg)
1893 : old_location_(location),
1894 location_(location),
1895 target_(target),
1896 lhs_reg_(lhs_reg),
1897 rhs_reg_(rhs_reg),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001898 condition_(condition),
1899 delayed_instruction_(kUnfilledDelaySlot) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001900 CHECK_NE(condition, kUncond);
1901 switch (condition) {
1902 case kCondLT:
1903 case kCondGE:
1904 case kCondLE:
1905 case kCondGT:
1906 case kCondLTU:
1907 case kCondGEU:
1908 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
1909 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
1910 // We leave this up to the caller.
1911 CHECK(is_r6);
1912 FALLTHROUGH_INTENDED;
1913 case kCondEQ:
1914 case kCondNE:
1915 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1916 // To compare with 0, use dedicated kCond*Z conditions.
1917 CHECK_NE(lhs_reg, ZERO);
1918 CHECK_NE(rhs_reg, ZERO);
1919 break;
1920 case kCondLTZ:
1921 case kCondGEZ:
1922 case kCondLEZ:
1923 case kCondGTZ:
1924 case kCondEQZ:
1925 case kCondNEZ:
1926 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1927 CHECK_NE(lhs_reg, ZERO);
1928 CHECK_EQ(rhs_reg, ZERO);
1929 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001930 case kCondF:
1931 case kCondT:
1932 CHECK_EQ(rhs_reg, ZERO);
1933 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001934 case kUncond:
1935 UNREACHABLE();
1936 }
1937 CHECK(!IsNop(condition, lhs_reg, rhs_reg));
1938 if (IsUncond(condition, lhs_reg, rhs_reg)) {
1939 // Branch condition is always true, make the branch unconditional.
1940 condition_ = kUncond;
1941 }
Alexey Frunze96b66822016-09-10 02:32:44 -07001942 InitializeType(kCondBranch, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001943}
1944
Alexey Frunze96b66822016-09-10 02:32:44 -07001945MipsAssembler::Branch::Branch(bool is_r6,
1946 uint32_t location,
1947 Register dest_reg,
1948 Register base_reg,
1949 Type label_or_literal_type)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001950 : old_location_(location),
1951 location_(location),
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001952 target_(kUnresolved),
1953 lhs_reg_(dest_reg),
1954 rhs_reg_(base_reg),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001955 condition_(kUncond),
1956 delayed_instruction_(kUnfilledDelaySlot) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001957 CHECK_NE(dest_reg, ZERO);
1958 if (is_r6) {
1959 CHECK_EQ(base_reg, ZERO);
1960 } else {
1961 CHECK_NE(base_reg, ZERO);
1962 }
Alexey Frunze96b66822016-09-10 02:32:44 -07001963 InitializeType(label_or_literal_type, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001964}
1965
1966MipsAssembler::BranchCondition MipsAssembler::Branch::OppositeCondition(
1967 MipsAssembler::BranchCondition cond) {
1968 switch (cond) {
1969 case kCondLT:
1970 return kCondGE;
1971 case kCondGE:
1972 return kCondLT;
1973 case kCondLE:
1974 return kCondGT;
1975 case kCondGT:
1976 return kCondLE;
1977 case kCondLTZ:
1978 return kCondGEZ;
1979 case kCondGEZ:
1980 return kCondLTZ;
1981 case kCondLEZ:
1982 return kCondGTZ;
1983 case kCondGTZ:
1984 return kCondLEZ;
1985 case kCondEQ:
1986 return kCondNE;
1987 case kCondNE:
1988 return kCondEQ;
1989 case kCondEQZ:
1990 return kCondNEZ;
1991 case kCondNEZ:
1992 return kCondEQZ;
1993 case kCondLTU:
1994 return kCondGEU;
1995 case kCondGEU:
1996 return kCondLTU;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001997 case kCondF:
1998 return kCondT;
1999 case kCondT:
2000 return kCondF;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002001 case kUncond:
2002 LOG(FATAL) << "Unexpected branch condition " << cond;
2003 }
2004 UNREACHABLE();
2005}
2006
2007MipsAssembler::Branch::Type MipsAssembler::Branch::GetType() const {
2008 return type_;
2009}
2010
2011MipsAssembler::BranchCondition MipsAssembler::Branch::GetCondition() const {
2012 return condition_;
2013}
2014
2015Register MipsAssembler::Branch::GetLeftRegister() const {
2016 return static_cast<Register>(lhs_reg_);
2017}
2018
2019Register MipsAssembler::Branch::GetRightRegister() const {
2020 return static_cast<Register>(rhs_reg_);
2021}
2022
2023uint32_t MipsAssembler::Branch::GetTarget() const {
2024 return target_;
2025}
2026
2027uint32_t MipsAssembler::Branch::GetLocation() const {
2028 return location_;
2029}
2030
2031uint32_t MipsAssembler::Branch::GetOldLocation() const {
2032 return old_location_;
2033}
2034
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002035uint32_t MipsAssembler::Branch::GetPrecedingInstructionLength(Type type) const {
2036 // Short branches with delay slots always consist of two instructions, the branch
2037 // and the delay slot, irrespective of whether the delay slot is filled with a
2038 // useful instruction or not.
2039 // Long composite branches may have a length longer by one instruction than
2040 // specified in branch_info_[].length. This happens when an instruction is taken
2041 // to fill the short branch delay slot, but the branch eventually becomes long
2042 // and formally has no delay slot to fill. This instruction is placed at the
2043 // beginning of the long composite branch and this needs to be accounted for in
2044 // the branch length and the location of the offset encoded in the branch.
2045 switch (type) {
2046 case kLongUncondBranch:
2047 case kLongCondBranch:
2048 case kLongCall:
2049 case kR6LongCondBranch:
2050 return (delayed_instruction_ != kUnfilledDelaySlot &&
2051 delayed_instruction_ != kUnfillableDelaySlot) ? 1 : 0;
2052 default:
2053 return 0;
2054 }
2055}
2056
2057uint32_t MipsAssembler::Branch::GetPrecedingInstructionSize(Type type) const {
2058 return GetPrecedingInstructionLength(type) * sizeof(uint32_t);
2059}
2060
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002061uint32_t MipsAssembler::Branch::GetLength() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002062 return GetPrecedingInstructionLength(type_) + branch_info_[type_].length;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002063}
2064
2065uint32_t MipsAssembler::Branch::GetOldLength() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002066 return GetPrecedingInstructionLength(old_type_) + branch_info_[old_type_].length;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002067}
2068
2069uint32_t MipsAssembler::Branch::GetSize() const {
2070 return GetLength() * sizeof(uint32_t);
2071}
2072
2073uint32_t MipsAssembler::Branch::GetOldSize() const {
2074 return GetOldLength() * sizeof(uint32_t);
2075}
2076
2077uint32_t MipsAssembler::Branch::GetEndLocation() const {
2078 return GetLocation() + GetSize();
2079}
2080
2081uint32_t MipsAssembler::Branch::GetOldEndLocation() const {
2082 return GetOldLocation() + GetOldSize();
2083}
2084
2085bool MipsAssembler::Branch::IsLong() const {
2086 switch (type_) {
2087 // R2 short branches.
2088 case kUncondBranch:
2089 case kCondBranch:
2090 case kCall:
Alexey Frunze96b66822016-09-10 02:32:44 -07002091 // R2 near label.
2092 case kLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002093 // R2 near literal.
2094 case kLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002095 // R6 short branches.
2096 case kR6UncondBranch:
2097 case kR6CondBranch:
2098 case kR6Call:
Alexey Frunze96b66822016-09-10 02:32:44 -07002099 // R6 near label.
2100 case kR6Label:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002101 // R6 near literal.
2102 case kR6Literal:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002103 return false;
2104 // R2 long branches.
2105 case kLongUncondBranch:
2106 case kLongCondBranch:
2107 case kLongCall:
Alexey Frunze96b66822016-09-10 02:32:44 -07002108 // R2 far label.
2109 case kFarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002110 // R2 far literal.
2111 case kFarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002112 // R6 long branches.
2113 case kR6LongUncondBranch:
2114 case kR6LongCondBranch:
2115 case kR6LongCall:
Alexey Frunze96b66822016-09-10 02:32:44 -07002116 // R6 far label.
2117 case kR6FarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002118 // R6 far literal.
2119 case kR6FarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002120 return true;
2121 }
2122 UNREACHABLE();
2123}
2124
2125bool MipsAssembler::Branch::IsResolved() const {
2126 return target_ != kUnresolved;
2127}
2128
2129MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSize() const {
2130 OffsetBits offset_size =
2131 (type_ == kR6CondBranch && (condition_ == kCondEQZ || condition_ == kCondNEZ))
2132 ? kOffset23
2133 : branch_info_[type_].offset_size;
2134 return offset_size;
2135}
2136
2137MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSizeNeeded(uint32_t location,
2138 uint32_t target) {
2139 // For unresolved targets assume the shortest encoding
2140 // (later it will be made longer if needed).
2141 if (target == kUnresolved)
2142 return kOffset16;
2143 int64_t distance = static_cast<int64_t>(target) - location;
2144 // To simplify calculations in composite branches consisting of multiple instructions
2145 // bump up the distance by a value larger than the max byte size of a composite branch.
2146 distance += (distance >= 0) ? kMaxBranchSize : -kMaxBranchSize;
2147 if (IsInt<kOffset16>(distance))
2148 return kOffset16;
2149 else if (IsInt<kOffset18>(distance))
2150 return kOffset18;
2151 else if (IsInt<kOffset21>(distance))
2152 return kOffset21;
2153 else if (IsInt<kOffset23>(distance))
2154 return kOffset23;
2155 else if (IsInt<kOffset28>(distance))
2156 return kOffset28;
2157 return kOffset32;
2158}
2159
2160void MipsAssembler::Branch::Resolve(uint32_t target) {
2161 target_ = target;
2162}
2163
2164void MipsAssembler::Branch::Relocate(uint32_t expand_location, uint32_t delta) {
2165 if (location_ > expand_location) {
2166 location_ += delta;
2167 }
2168 if (!IsResolved()) {
2169 return; // Don't know the target yet.
2170 }
2171 if (target_ > expand_location) {
2172 target_ += delta;
2173 }
2174}
2175
2176void MipsAssembler::Branch::PromoteToLong() {
2177 switch (type_) {
2178 // R2 short branches.
2179 case kUncondBranch:
2180 type_ = kLongUncondBranch;
2181 break;
2182 case kCondBranch:
2183 type_ = kLongCondBranch;
2184 break;
2185 case kCall:
2186 type_ = kLongCall;
2187 break;
Alexey Frunze96b66822016-09-10 02:32:44 -07002188 // R2 near label.
2189 case kLabel:
2190 type_ = kFarLabel;
2191 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002192 // R2 near literal.
2193 case kLiteral:
2194 type_ = kFarLiteral;
2195 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002196 // R6 short branches.
2197 case kR6UncondBranch:
2198 type_ = kR6LongUncondBranch;
2199 break;
2200 case kR6CondBranch:
2201 type_ = kR6LongCondBranch;
2202 break;
2203 case kR6Call:
2204 type_ = kR6LongCall;
2205 break;
Alexey Frunze96b66822016-09-10 02:32:44 -07002206 // R6 near label.
2207 case kR6Label:
2208 type_ = kR6FarLabel;
2209 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002210 // R6 near literal.
2211 case kR6Literal:
2212 type_ = kR6FarLiteral;
2213 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002214 default:
2215 // Note: 'type_' is already long.
2216 break;
2217 }
2218 CHECK(IsLong());
2219}
2220
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002221uint32_t MipsAssembler::GetBranchLocationOrPcRelBase(const MipsAssembler::Branch* branch) const {
2222 switch (branch->GetType()) {
Alexey Frunze96b66822016-09-10 02:32:44 -07002223 case Branch::kLabel:
2224 case Branch::kFarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002225 case Branch::kLiteral:
2226 case Branch::kFarLiteral:
2227 return GetLabelLocation(&pc_rel_base_label_);
2228 default:
2229 return branch->GetLocation();
2230 }
2231}
2232
2233uint32_t MipsAssembler::Branch::PromoteIfNeeded(uint32_t location, uint32_t max_short_distance) {
Alexey Frunze96b66822016-09-10 02:32:44 -07002234 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 labels/literals or
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002235 // `this->GetLocation()` for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002236 // If the branch is still unresolved or already long, nothing to do.
2237 if (IsLong() || !IsResolved()) {
2238 return 0;
2239 }
2240 // Promote the short branch to long if the offset size is too small
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002241 // to hold the distance between location and target_.
2242 if (GetOffsetSizeNeeded(location, target_) > GetOffsetSize()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002243 PromoteToLong();
2244 uint32_t old_size = GetOldSize();
2245 uint32_t new_size = GetSize();
2246 CHECK_GT(new_size, old_size);
2247 return new_size - old_size;
2248 }
2249 // The following logic is for debugging/testing purposes.
2250 // Promote some short branches to long when it's not really required.
2251 if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max())) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002252 int64_t distance = static_cast<int64_t>(target_) - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002253 distance = (distance >= 0) ? distance : -distance;
2254 if (distance >= max_short_distance) {
2255 PromoteToLong();
2256 uint32_t old_size = GetOldSize();
2257 uint32_t new_size = GetSize();
2258 CHECK_GT(new_size, old_size);
2259 return new_size - old_size;
2260 }
2261 }
2262 return 0;
2263}
2264
2265uint32_t MipsAssembler::Branch::GetOffsetLocation() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002266 return location_ + GetPrecedingInstructionSize(type_) +
2267 branch_info_[type_].instr_offset * sizeof(uint32_t);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002268}
2269
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002270uint32_t MipsAssembler::GetBranchOrPcRelBaseForEncoding(const MipsAssembler::Branch* branch) const {
2271 switch (branch->GetType()) {
Alexey Frunze96b66822016-09-10 02:32:44 -07002272 case Branch::kLabel:
2273 case Branch::kFarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002274 case Branch::kLiteral:
2275 case Branch::kFarLiteral:
2276 return GetLabelLocation(&pc_rel_base_label_);
2277 default:
2278 return branch->GetOffsetLocation() +
2279 Branch::branch_info_[branch->GetType()].pc_org * sizeof(uint32_t);
2280 }
2281}
2282
2283uint32_t MipsAssembler::Branch::GetOffset(uint32_t location) const {
Alexey Frunze96b66822016-09-10 02:32:44 -07002284 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 labels/literals or
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002285 // `this->GetOffsetLocation() + branch_info_[this->GetType()].pc_org * sizeof(uint32_t)`
2286 // for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002287 CHECK(IsResolved());
2288 uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize());
2289 // Calculate the byte distance between instructions and also account for
2290 // different PC-relative origins.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002291 uint32_t offset = target_ - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002292 // Prepare the offset for encoding into the instruction(s).
2293 offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift;
2294 return offset;
2295}
2296
2297MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) {
2298 CHECK_LT(branch_id, branches_.size());
2299 return &branches_[branch_id];
2300}
2301
2302const MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) const {
2303 CHECK_LT(branch_id, branches_.size());
2304 return &branches_[branch_id];
2305}
2306
2307void MipsAssembler::Bind(MipsLabel* label) {
2308 CHECK(!label->IsBound());
2309 uint32_t bound_pc = buffer_.Size();
2310
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002311 // Make the delay slot FSM aware of the new label.
2312 DsFsmLabel();
2313
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002314 // Walk the list of branches referring to and preceding this label.
2315 // Store the previously unknown target addresses in them.
2316 while (label->IsLinked()) {
2317 uint32_t branch_id = label->Position();
2318 Branch* branch = GetBranch(branch_id);
2319 branch->Resolve(bound_pc);
2320
2321 uint32_t branch_location = branch->GetLocation();
2322 // Extract the location of the previous branch in the list (walking the list backwards;
2323 // the previous branch ID was stored in the space reserved for this branch).
2324 uint32_t prev = buffer_.Load<uint32_t>(branch_location);
2325
2326 // On to the previous branch in the list...
2327 label->position_ = prev;
2328 }
2329
2330 // Now make the label object contain its own location (relative to the end of the preceding
2331 // branch, if any; it will be used by the branches referring to and following this label).
2332 label->prev_branch_id_plus_one_ = branches_.size();
2333 if (label->prev_branch_id_plus_one_) {
2334 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
2335 const Branch* branch = GetBranch(branch_id);
2336 bound_pc -= branch->GetEndLocation();
2337 }
2338 label->BindTo(bound_pc);
2339}
2340
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002341uint32_t MipsAssembler::GetLabelLocation(const MipsLabel* label) const {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002342 CHECK(label->IsBound());
2343 uint32_t target = label->Position();
2344 if (label->prev_branch_id_plus_one_) {
2345 // Get label location based on the branch preceding it.
2346 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
2347 const Branch* branch = GetBranch(branch_id);
2348 target += branch->GetEndLocation();
2349 }
2350 return target;
2351}
2352
2353uint32_t MipsAssembler::GetAdjustedPosition(uint32_t old_position) {
2354 // We can reconstruct the adjustment by going through all the branches from the beginning
2355 // up to the old_position. Since we expect AdjustedPosition() to be called in a loop
2356 // with increasing old_position, we can use the data from last AdjustedPosition() to
2357 // continue where we left off and the whole loop should be O(m+n) where m is the number
2358 // of positions to adjust and n is the number of branches.
2359 if (old_position < last_old_position_) {
2360 last_position_adjustment_ = 0;
2361 last_old_position_ = 0;
2362 last_branch_id_ = 0;
2363 }
2364 while (last_branch_id_ != branches_.size()) {
2365 const Branch* branch = GetBranch(last_branch_id_);
2366 if (branch->GetLocation() >= old_position + last_position_adjustment_) {
2367 break;
2368 }
2369 last_position_adjustment_ += branch->GetSize() - branch->GetOldSize();
2370 ++last_branch_id_;
2371 }
2372 last_old_position_ = old_position;
2373 return old_position + last_position_adjustment_;
2374}
2375
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002376void MipsAssembler::BindPcRelBaseLabel() {
2377 Bind(&pc_rel_base_label_);
2378}
2379
Alexey Frunze06a46c42016-07-19 15:00:40 -07002380uint32_t MipsAssembler::GetPcRelBaseLabelLocation() const {
2381 return GetLabelLocation(&pc_rel_base_label_);
2382}
2383
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002384void MipsAssembler::FinalizeLabeledBranch(MipsLabel* label) {
2385 uint32_t length = branches_.back().GetLength();
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002386 // Commit the last branch target label (if any).
2387 DsFsmCommitLabel();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002388 if (!label->IsBound()) {
2389 // Branch forward (to a following label), distance is unknown.
2390 // The first branch forward will contain 0, serving as the terminator of
2391 // the list of forward-reaching branches.
2392 Emit(label->position_);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002393 // Nothing for the delay slot (yet).
2394 DsFsmInstrNop(0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002395 length--;
2396 // Now make the label object point to this branch
2397 // (this forms a linked list of branches preceding this label).
2398 uint32_t branch_id = branches_.size() - 1;
2399 label->LinkTo(branch_id);
2400 }
2401 // Reserve space for the branch.
2402 while (length--) {
2403 Nop();
2404 }
2405}
2406
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002407bool MipsAssembler::Branch::CanHaveDelayedInstruction(const DelaySlot& delay_slot) const {
2408 if (delay_slot.instruction_ == 0) {
2409 // NOP or no instruction for the delay slot.
2410 return false;
2411 }
2412 switch (type_) {
2413 // R2 unconditional branches.
2414 case kUncondBranch:
2415 case kLongUncondBranch:
2416 // There are no register interdependencies.
2417 return true;
2418
2419 // R2 calls.
2420 case kCall:
2421 case kLongCall:
2422 // Instructions depending on or modifying RA should not be moved into delay slots
2423 // of branches modifying RA.
2424 return ((delay_slot.gpr_ins_mask_ | delay_slot.gpr_outs_mask_) & (1u << RA)) == 0;
2425
2426 // R2 conditional branches.
2427 case kCondBranch:
2428 case kLongCondBranch:
2429 switch (condition_) {
2430 // Branches with one GPR source.
2431 case kCondLTZ:
2432 case kCondGEZ:
2433 case kCondLEZ:
2434 case kCondGTZ:
2435 case kCondEQZ:
2436 case kCondNEZ:
2437 return (delay_slot.gpr_outs_mask_ & (1u << lhs_reg_)) == 0;
2438
2439 // Branches with two GPR sources.
2440 case kCondEQ:
2441 case kCondNE:
2442 return (delay_slot.gpr_outs_mask_ & ((1u << lhs_reg_) | (1u << rhs_reg_))) == 0;
2443
2444 // Branches with one FPU condition code source.
2445 case kCondF:
2446 case kCondT:
2447 return (delay_slot.cc_outs_mask_ & (1u << lhs_reg_)) == 0;
2448
2449 default:
2450 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
2451 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
2452 LOG(FATAL) << "Unexpected branch condition " << condition_;
2453 UNREACHABLE();
2454 }
2455
2456 // R6 unconditional branches.
2457 case kR6UncondBranch:
2458 case kR6LongUncondBranch:
2459 // R6 calls.
2460 case kR6Call:
2461 case kR6LongCall:
2462 // There are no delay slots.
2463 return false;
2464
2465 // R6 conditional branches.
2466 case kR6CondBranch:
2467 case kR6LongCondBranch:
2468 switch (condition_) {
2469 // Branches with one FPU register source.
2470 case kCondF:
2471 case kCondT:
2472 return (delay_slot.fpr_outs_mask_ & (1u << lhs_reg_)) == 0;
2473 // Others have a forbidden slot instead of a delay slot.
2474 default:
2475 return false;
2476 }
2477
2478 // Literals.
2479 default:
2480 LOG(FATAL) << "Unexpected branch type " << type_;
2481 UNREACHABLE();
2482 }
2483}
2484
2485uint32_t MipsAssembler::Branch::GetDelayedInstruction() const {
2486 return delayed_instruction_;
2487}
2488
2489void MipsAssembler::Branch::SetDelayedInstruction(uint32_t instruction) {
2490 CHECK_NE(instruction, kUnfilledDelaySlot);
2491 CHECK_EQ(delayed_instruction_, kUnfilledDelaySlot);
2492 delayed_instruction_ = instruction;
2493}
2494
2495void MipsAssembler::Branch::DecrementLocations() {
2496 // We first create a branch object, which gets its type and locations initialized,
2497 // and then we check if the branch can actually have the preceding instruction moved
2498 // into its delay slot. If it can, the branch locations need to be decremented.
2499 //
2500 // We could make the check before creating the branch object and avoid the location
2501 // adjustment, but the check is cleaner when performed on an initialized branch
2502 // object.
2503 //
2504 // If the branch is backwards (to a previously bound label), reducing the locations
2505 // cannot cause a short branch to exceed its offset range because the offset reduces.
2506 // And this is not at all a problem for a long branch backwards.
2507 //
2508 // If the branch is forward (not linked to any label yet), reducing the locations
2509 // is harmless. The branch will be promoted to long if needed when the target is known.
2510 CHECK_EQ(location_, old_location_);
2511 CHECK_GE(old_location_, sizeof(uint32_t));
2512 old_location_ -= sizeof(uint32_t);
2513 location_ = old_location_;
2514}
2515
2516void MipsAssembler::MoveInstructionToDelaySlot(Branch& branch) {
2517 if (branch.CanHaveDelayedInstruction(delay_slot_)) {
2518 // The last instruction cannot be used in a different delay slot,
2519 // do not commit the label before it (if any).
2520 DsFsmDropLabel();
2521 // Remove the last emitted instruction.
2522 size_t size = buffer_.Size();
2523 CHECK_GE(size, sizeof(uint32_t));
2524 size -= sizeof(uint32_t);
2525 CHECK_EQ(buffer_.Load<uint32_t>(size), delay_slot_.instruction_);
2526 buffer_.Resize(size);
2527 // Attach it to the branch and adjust the branch locations.
2528 branch.DecrementLocations();
2529 branch.SetDelayedInstruction(delay_slot_.instruction_);
2530 } else if (!reordering_ && branch.GetType() == Branch::kUncondBranch) {
2531 // If reordefing is disabled, prevent absorption of the target instruction.
2532 branch.SetDelayedInstruction(Branch::kUnfillableDelaySlot);
2533 }
2534}
2535
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002536void MipsAssembler::Buncond(MipsLabel* label) {
2537 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002538 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ false);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002539 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002540 FinalizeLabeledBranch(label);
2541}
2542
2543void MipsAssembler::Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs) {
2544 // If lhs = rhs, this can be a NOP.
2545 if (Branch::IsNop(condition, lhs, rhs)) {
2546 return;
2547 }
2548 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
2549 branches_.emplace_back(IsR6(), buffer_.Size(), target, condition, lhs, rhs);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002550 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002551 FinalizeLabeledBranch(label);
2552}
2553
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002554void MipsAssembler::Call(MipsLabel* label) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002555 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002556 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ true);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002557 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002558 FinalizeLabeledBranch(label);
2559}
2560
Alexey Frunze96b66822016-09-10 02:32:44 -07002561void MipsAssembler::LoadLabelAddress(Register dest_reg, Register base_reg, MipsLabel* label) {
2562 // Label address loads are treated as pseudo branches since they require very similar handling.
2563 DCHECK(!label->IsBound());
2564 branches_.emplace_back(IsR6(), buffer_.Size(), dest_reg, base_reg, Branch::kLabel);
2565 FinalizeLabeledBranch(label);
2566}
2567
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002568Literal* MipsAssembler::NewLiteral(size_t size, const uint8_t* data) {
2569 DCHECK(size == 4u || size == 8u) << size;
2570 literals_.emplace_back(size, data);
2571 return &literals_.back();
2572}
2573
2574void MipsAssembler::LoadLiteral(Register dest_reg, Register base_reg, Literal* literal) {
2575 // Literal loads are treated as pseudo branches since they require very similar handling.
2576 DCHECK_EQ(literal->GetSize(), 4u);
2577 MipsLabel* label = literal->GetLabel();
2578 DCHECK(!label->IsBound());
Alexey Frunze96b66822016-09-10 02:32:44 -07002579 branches_.emplace_back(IsR6(), buffer_.Size(), dest_reg, base_reg, Branch::kLiteral);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002580 FinalizeLabeledBranch(label);
2581}
2582
Alexey Frunze96b66822016-09-10 02:32:44 -07002583JumpTable* MipsAssembler::CreateJumpTable(std::vector<MipsLabel*>&& labels) {
2584 jump_tables_.emplace_back(std::move(labels));
2585 JumpTable* table = &jump_tables_.back();
2586 DCHECK(!table->GetLabel()->IsBound());
2587 return table;
2588}
2589
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002590void MipsAssembler::EmitLiterals() {
2591 if (!literals_.empty()) {
2592 // We don't support byte and half-word literals.
2593 // TODO: proper alignment for 64-bit literals when they're implemented.
2594 for (Literal& literal : literals_) {
2595 MipsLabel* label = literal.GetLabel();
2596 Bind(label);
2597 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
2598 DCHECK(literal.GetSize() == 4u || literal.GetSize() == 8u);
2599 for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
2600 buffer_.Emit<uint8_t>(literal.GetData()[i]);
2601 }
2602 }
2603 }
2604}
2605
Alexey Frunze96b66822016-09-10 02:32:44 -07002606void MipsAssembler::ReserveJumpTableSpace() {
2607 if (!jump_tables_.empty()) {
2608 for (JumpTable& table : jump_tables_) {
2609 MipsLabel* label = table.GetLabel();
2610 Bind(label);
2611
2612 // Bulk ensure capacity, as this may be large.
2613 size_t orig_size = buffer_.Size();
2614 size_t required_capacity = orig_size + table.GetSize();
2615 if (required_capacity > buffer_.Capacity()) {
2616 buffer_.ExtendCapacity(required_capacity);
2617 }
2618#ifndef NDEBUG
2619 buffer_.has_ensured_capacity_ = true;
2620#endif
2621
2622 // Fill the space with dummy data as the data is not final
2623 // until the branches have been promoted. And we shouldn't
2624 // be moving uninitialized data during branch promotion.
2625 for (size_t cnt = table.GetData().size(), i = 0; i < cnt; i++) {
2626 buffer_.Emit<uint32_t>(0x1abe1234u);
2627 }
2628
2629#ifndef NDEBUG
2630 buffer_.has_ensured_capacity_ = false;
2631#endif
2632 }
2633 }
2634}
2635
2636void MipsAssembler::EmitJumpTables() {
2637 if (!jump_tables_.empty()) {
2638 CHECK(!overwriting_);
2639 // Switch from appending instructions at the end of the buffer to overwriting
2640 // existing instructions (here, jump tables) in the buffer.
2641 overwriting_ = true;
2642
2643 for (JumpTable& table : jump_tables_) {
2644 MipsLabel* table_label = table.GetLabel();
2645 uint32_t start = GetLabelLocation(table_label);
2646 overwrite_location_ = start;
2647
2648 for (MipsLabel* target : table.GetData()) {
2649 CHECK_EQ(buffer_.Load<uint32_t>(overwrite_location_), 0x1abe1234u);
2650 // The table will contain target addresses relative to the table start.
2651 uint32_t offset = GetLabelLocation(target) - start;
2652 Emit(offset);
2653 }
2654 }
2655
2656 overwriting_ = false;
2657 }
2658}
2659
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002660void MipsAssembler::PromoteBranches() {
2661 // Promote short branches to long as necessary.
2662 bool changed;
2663 do {
2664 changed = false;
2665 for (auto& branch : branches_) {
2666 CHECK(branch.IsResolved());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002667 uint32_t base = GetBranchLocationOrPcRelBase(&branch);
2668 uint32_t delta = branch.PromoteIfNeeded(base);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002669 // If this branch has been promoted and needs to expand in size,
2670 // relocate all branches by the expansion size.
2671 if (delta) {
2672 changed = true;
2673 uint32_t expand_location = branch.GetLocation();
2674 for (auto& branch2 : branches_) {
2675 branch2.Relocate(expand_location, delta);
2676 }
2677 }
2678 }
2679 } while (changed);
2680
2681 // Account for branch expansion by resizing the code buffer
2682 // and moving the code in it to its final location.
2683 size_t branch_count = branches_.size();
2684 if (branch_count > 0) {
2685 // Resize.
2686 Branch& last_branch = branches_[branch_count - 1];
2687 uint32_t size_delta = last_branch.GetEndLocation() - last_branch.GetOldEndLocation();
2688 uint32_t old_size = buffer_.Size();
2689 buffer_.Resize(old_size + size_delta);
2690 // Move the code residing between branch placeholders.
2691 uint32_t end = old_size;
2692 for (size_t i = branch_count; i > 0; ) {
2693 Branch& branch = branches_[--i];
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002694 CHECK_GE(end, branch.GetOldEndLocation());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002695 uint32_t size = end - branch.GetOldEndLocation();
2696 buffer_.Move(branch.GetEndLocation(), branch.GetOldEndLocation(), size);
2697 end = branch.GetOldLocation();
2698 }
2699 }
2700}
2701
2702// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
2703const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = {
2704 // R2 short branches.
2705 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kUncondBranch
2706 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002707 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCall
Alexey Frunze96b66822016-09-10 02:32:44 -07002708 // R2 near label.
2709 { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLabel
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002710 // R2 near literal.
2711 { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002712 // R2 long branches.
2713 { 9, 3, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongUncondBranch
2714 { 10, 4, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCondBranch
2715 { 6, 1, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCall
Alexey Frunze96b66822016-09-10 02:32:44 -07002716 // R2 far label.
2717 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLabel
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002718 // R2 far literal.
2719 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002720 // R6 short branches.
2721 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6UncondBranch
2722 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kR6CondBranch
2723 // Exception: kOffset23 for beqzc/bnezc.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002724 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6Call
Alexey Frunze96b66822016-09-10 02:32:44 -07002725 // R6 near label.
2726 { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Label
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002727 // R6 near literal.
2728 { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Literal
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002729 // R6 long branches.
2730 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongUncondBranch
2731 { 3, 1, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002732 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCall
Alexey Frunze96b66822016-09-10 02:32:44 -07002733 // R6 far label.
2734 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLabel
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002735 // R6 far literal.
2736 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002737};
2738
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002739// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002740void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
2741 CHECK_EQ(overwriting_, true);
2742 overwrite_location_ = branch->GetLocation();
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002743 uint32_t offset = branch->GetOffset(GetBranchOrPcRelBaseForEncoding(branch));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002744 BranchCondition condition = branch->GetCondition();
2745 Register lhs = branch->GetLeftRegister();
2746 Register rhs = branch->GetRightRegister();
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002747 uint32_t delayed_instruction = branch->GetDelayedInstruction();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002748 switch (branch->GetType()) {
2749 // R2 short branches.
2750 case Branch::kUncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002751 if (delayed_instruction == Branch::kUnfillableDelaySlot) {
2752 // The branch was created when reordering was disabled, do not absorb the target
2753 // instruction.
2754 delayed_instruction = 0; // NOP.
2755 } else if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2756 // Try to absorb the target instruction into the delay slot.
2757 delayed_instruction = 0; // NOP.
2758 // Incrementing the signed 16-bit offset past the target instruction must not
2759 // cause overflow into the negative subrange, check for the max offset.
2760 if (offset != 0x7FFF) {
2761 uint32_t target = branch->GetTarget();
2762 if (std::binary_search(ds_fsm_target_pcs_.begin(), ds_fsm_target_pcs_.end(), target)) {
2763 delayed_instruction = buffer_.Load<uint32_t>(target);
2764 offset++;
2765 }
2766 }
2767 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002768 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2769 B(offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002770 Emit(delayed_instruction);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002771 break;
2772 case Branch::kCondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002773 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2774 if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2775 delayed_instruction = 0; // NOP.
2776 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002777 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002778 EmitBcondR2(condition, lhs, rhs, offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002779 Emit(delayed_instruction);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002780 break;
2781 case Branch::kCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002782 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2783 if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2784 delayed_instruction = 0; // NOP.
2785 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002786 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002787 Bal(offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002788 Emit(delayed_instruction);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002789 break;
2790
Alexey Frunze96b66822016-09-10 02:32:44 -07002791 // R2 near label.
2792 case Branch::kLabel:
2793 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2794 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2795 Addiu(lhs, rhs, offset);
2796 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002797 // R2 near literal.
2798 case Branch::kLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002799 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002800 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2801 Lw(lhs, rhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002802 break;
2803
2804 // R2 long branches.
2805 case Branch::kLongUncondBranch:
2806 // To get the value of the PC register we need to use the NAL instruction.
2807 // NAL clobbers the RA register. However, RA must be preserved if the
2808 // method is compiled without the entry/exit sequences that would take care
2809 // of preserving RA (typically, leaf methods don't preserve RA explicitly).
2810 // So, we need to preserve RA in some temporary storage ourselves. The AT
2811 // register can't be used for this because we need it to load a constant
2812 // which will be added to the value that NAL stores in RA. And we can't
2813 // use T9 for this in the context of the JNI compiler, which uses it
2814 // as a scratch register (see InterproceduralScratchRegister()).
2815 // If we were to add a 32-bit constant to RA using two ADDIU instructions,
2816 // we'd also need to use the ROTR instruction, which requires no less than
2817 // MIPSR2.
2818 // Perhaps, we could use T8 or one of R2's multiplier/divider registers
2819 // (LO or HI) or even a floating-point register, but that doesn't seem
2820 // like a nice solution. We may want this to work on both R6 and pre-R6.
2821 // For now simply use the stack for RA. This should be OK since for the
2822 // vast majority of code a short PC-relative branch is sufficient.
2823 // TODO: can this be improved?
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002824 // TODO: consider generation of a shorter sequence when we know that RA
2825 // is explicitly preserved by the method entry/exit code.
2826 if (delayed_instruction != Branch::kUnfilledDelaySlot &&
2827 delayed_instruction != Branch::kUnfillableDelaySlot) {
2828 Emit(delayed_instruction);
2829 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002830 Push(RA);
2831 Nal();
2832 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2833 Lui(AT, High16Bits(offset));
2834 Ori(AT, AT, Low16Bits(offset));
2835 Addu(AT, AT, RA);
2836 Lw(RA, SP, 0);
2837 Jr(AT);
2838 DecreaseFrameSize(kMipsWordSize);
2839 break;
2840 case Branch::kLongCondBranch:
2841 // The comment on case 'Branch::kLongUncondBranch' applies here as well.
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002842 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2843 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2844 Emit(delayed_instruction);
2845 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002846 // Note: the opposite condition branch encodes 8 as the distance, which is equal to the
2847 // number of instructions skipped:
2848 // (PUSH(IncreaseFrameSize(ADDIU) + SW) + NAL + LUI + ORI + ADDU + LW + JR).
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002849 EmitBcondR2(Branch::OppositeCondition(condition), lhs, rhs, 8);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002850 Push(RA);
2851 Nal();
2852 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2853 Lui(AT, High16Bits(offset));
2854 Ori(AT, AT, Low16Bits(offset));
2855 Addu(AT, AT, RA);
2856 Lw(RA, SP, 0);
2857 Jr(AT);
2858 DecreaseFrameSize(kMipsWordSize);
2859 break;
2860 case Branch::kLongCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002861 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2862 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2863 Emit(delayed_instruction);
2864 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002865 Nal();
2866 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2867 Lui(AT, High16Bits(offset));
2868 Ori(AT, AT, Low16Bits(offset));
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002869 Addu(AT, AT, RA);
2870 Jalr(AT);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002871 Nop();
2872 break;
2873
Alexey Frunze96b66822016-09-10 02:32:44 -07002874 // R2 far label.
2875 case Branch::kFarLabel:
2876 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2877 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2878 Lui(AT, High16Bits(offset));
2879 Ori(AT, AT, Low16Bits(offset));
2880 Addu(lhs, AT, rhs);
2881 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002882 // R2 far literal.
2883 case Branch::kFarLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002884 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002885 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2886 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2887 Lui(AT, High16Bits(offset));
2888 Addu(AT, AT, rhs);
2889 Lw(lhs, AT, Low16Bits(offset));
2890 break;
2891
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002892 // R6 short branches.
2893 case Branch::kR6UncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002894 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002895 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2896 Bc(offset);
2897 break;
2898 case Branch::kR6CondBranch:
2899 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002900 EmitBcondR6(condition, lhs, rhs, offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002901 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2902 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2903 Emit(delayed_instruction);
2904 } else {
2905 // TODO: improve by filling the forbidden slot (IFF this is
2906 // a forbidden and not a delay slot).
2907 Nop();
2908 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002909 break;
2910 case Branch::kR6Call:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002911 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002912 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002913 Balc(offset);
2914 break;
2915
Alexey Frunze96b66822016-09-10 02:32:44 -07002916 // R6 near label.
2917 case Branch::kR6Label:
2918 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2919 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2920 Addiupc(lhs, offset);
2921 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002922 // R6 near literal.
2923 case Branch::kR6Literal:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002924 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002925 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2926 Lwpc(lhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002927 break;
2928
2929 // R6 long branches.
2930 case Branch::kR6LongUncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002931 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002932 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2933 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2934 Auipc(AT, High16Bits(offset));
2935 Jic(AT, Low16Bits(offset));
2936 break;
2937 case Branch::kR6LongCondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002938 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2939 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2940 Emit(delayed_instruction);
2941 }
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002942 EmitBcondR6(Branch::OppositeCondition(condition), lhs, rhs, 2);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002943 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2944 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2945 Auipc(AT, High16Bits(offset));
2946 Jic(AT, Low16Bits(offset));
2947 break;
2948 case Branch::kR6LongCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002949 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002950 offset += (offset & 0x8000) << 1; // Account for sign extension in jialc.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002951 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002952 Auipc(AT, High16Bits(offset));
2953 Jialc(AT, Low16Bits(offset));
2954 break;
2955
Alexey Frunze96b66822016-09-10 02:32:44 -07002956 // R6 far label.
2957 case Branch::kR6FarLabel:
2958 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2959 offset += (offset & 0x8000) << 1; // Account for sign extension in addiu.
2960 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2961 Auipc(AT, High16Bits(offset));
2962 Addiu(lhs, AT, Low16Bits(offset));
2963 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002964 // R6 far literal.
2965 case Branch::kR6FarLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002966 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002967 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2968 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2969 Auipc(AT, High16Bits(offset));
2970 Lw(lhs, AT, Low16Bits(offset));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002971 break;
2972 }
2973 CHECK_EQ(overwrite_location_, branch->GetEndLocation());
2974 CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize));
2975}
2976
2977void MipsAssembler::B(MipsLabel* label) {
2978 Buncond(label);
2979}
2980
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002981void MipsAssembler::Bal(MipsLabel* label) {
2982 Call(label);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002983}
2984
2985void MipsAssembler::Beq(Register rs, Register rt, MipsLabel* label) {
2986 Bcond(label, kCondEQ, rs, rt);
2987}
2988
2989void MipsAssembler::Bne(Register rs, Register rt, MipsLabel* label) {
2990 Bcond(label, kCondNE, rs, rt);
2991}
2992
2993void MipsAssembler::Beqz(Register rt, MipsLabel* label) {
2994 Bcond(label, kCondEQZ, rt);
2995}
2996
2997void MipsAssembler::Bnez(Register rt, MipsLabel* label) {
2998 Bcond(label, kCondNEZ, rt);
2999}
3000
3001void MipsAssembler::Bltz(Register rt, MipsLabel* label) {
3002 Bcond(label, kCondLTZ, rt);
3003}
3004
3005void MipsAssembler::Bgez(Register rt, MipsLabel* label) {
3006 Bcond(label, kCondGEZ, rt);
3007}
3008
3009void MipsAssembler::Blez(Register rt, MipsLabel* label) {
3010 Bcond(label, kCondLEZ, rt);
3011}
3012
3013void MipsAssembler::Bgtz(Register rt, MipsLabel* label) {
3014 Bcond(label, kCondGTZ, rt);
3015}
3016
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003017bool MipsAssembler::CanExchangeWithSlt(Register rs, Register rt) const {
3018 // If the instruction modifies AT, `rs` or `rt`, it can't be exchanged with the slt[u]
3019 // instruction because either slt[u] depends on `rs` or `rt` or the following
3020 // conditional branch depends on AT set by slt[u].
3021 // Likewise, if the instruction depends on AT, it can't be exchanged with slt[u]
3022 // because slt[u] changes AT.
3023 return (delay_slot_.instruction_ != 0 &&
3024 (delay_slot_.gpr_outs_mask_ & ((1u << AT) | (1u << rs) | (1u << rt))) == 0 &&
3025 (delay_slot_.gpr_ins_mask_ & (1u << AT)) == 0);
3026}
3027
3028void MipsAssembler::ExchangeWithSlt(const DelaySlot& forwarded_slot) {
3029 // Exchange the last two instructions in the assembler buffer.
3030 size_t size = buffer_.Size();
3031 CHECK_GE(size, 2 * sizeof(uint32_t));
3032 size_t pos1 = size - 2 * sizeof(uint32_t);
3033 size_t pos2 = size - sizeof(uint32_t);
3034 uint32_t instr1 = buffer_.Load<uint32_t>(pos1);
3035 uint32_t instr2 = buffer_.Load<uint32_t>(pos2);
3036 CHECK_EQ(instr1, forwarded_slot.instruction_);
3037 CHECK_EQ(instr2, delay_slot_.instruction_);
3038 buffer_.Store<uint32_t>(pos1, instr2);
3039 buffer_.Store<uint32_t>(pos2, instr1);
3040 // Set the current delay slot information to that of the last instruction
3041 // in the buffer.
3042 delay_slot_ = forwarded_slot;
3043}
3044
3045void MipsAssembler::GenerateSltForCondBranch(bool unsigned_slt, Register rs, Register rt) {
3046 // If possible, exchange the slt[u] instruction with the preceding instruction,
3047 // so it can fill the delay slot.
3048 DelaySlot forwarded_slot = delay_slot_;
3049 bool exchange = CanExchangeWithSlt(rs, rt);
3050 if (exchange) {
3051 // The last instruction cannot be used in a different delay slot,
3052 // do not commit the label before it (if any).
3053 DsFsmDropLabel();
3054 }
3055 if (unsigned_slt) {
3056 Sltu(AT, rs, rt);
3057 } else {
3058 Slt(AT, rs, rt);
3059 }
3060 if (exchange) {
3061 ExchangeWithSlt(forwarded_slot);
3062 }
3063}
3064
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003065void MipsAssembler::Blt(Register rs, Register rt, MipsLabel* label) {
3066 if (IsR6()) {
3067 Bcond(label, kCondLT, rs, rt);
3068 } else if (!Branch::IsNop(kCondLT, rs, rt)) {
3069 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003070 GenerateSltForCondBranch(/* unsigned_slt */ false, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003071 Bnez(AT, label);
3072 }
3073}
3074
3075void MipsAssembler::Bge(Register rs, Register rt, MipsLabel* label) {
3076 if (IsR6()) {
3077 Bcond(label, kCondGE, rs, rt);
3078 } else if (Branch::IsUncond(kCondGE, rs, rt)) {
3079 B(label);
3080 } else {
3081 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003082 GenerateSltForCondBranch(/* unsigned_slt */ false, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003083 Beqz(AT, label);
3084 }
3085}
3086
3087void MipsAssembler::Bltu(Register rs, Register rt, MipsLabel* label) {
3088 if (IsR6()) {
3089 Bcond(label, kCondLTU, rs, rt);
3090 } else if (!Branch::IsNop(kCondLTU, rs, rt)) {
3091 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003092 GenerateSltForCondBranch(/* unsigned_slt */ true, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003093 Bnez(AT, label);
3094 }
3095}
3096
3097void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) {
3098 if (IsR6()) {
3099 Bcond(label, kCondGEU, rs, rt);
3100 } else if (Branch::IsUncond(kCondGEU, rs, rt)) {
3101 B(label);
3102 } else {
3103 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003104 GenerateSltForCondBranch(/* unsigned_slt */ true, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003105 Beqz(AT, label);
jeffhao7fbee072012-08-24 17:56:54 -07003106 }
3107}
3108
Chris Larsenb74353a2015-11-20 09:07:09 -08003109void MipsAssembler::Bc1f(MipsLabel* label) {
3110 Bc1f(0, label);
3111}
3112
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003113void MipsAssembler::Bc1f(int cc, MipsLabel* label) {
3114 CHECK(IsUint<3>(cc)) << cc;
3115 Bcond(label, kCondF, static_cast<Register>(cc), ZERO);
3116}
3117
Chris Larsenb74353a2015-11-20 09:07:09 -08003118void MipsAssembler::Bc1t(MipsLabel* label) {
3119 Bc1t(0, label);
3120}
3121
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003122void MipsAssembler::Bc1t(int cc, MipsLabel* label) {
3123 CHECK(IsUint<3>(cc)) << cc;
3124 Bcond(label, kCondT, static_cast<Register>(cc), ZERO);
3125}
3126
3127void MipsAssembler::Bc1eqz(FRegister ft, MipsLabel* label) {
3128 Bcond(label, kCondF, static_cast<Register>(ft), ZERO);
3129}
3130
3131void MipsAssembler::Bc1nez(FRegister ft, MipsLabel* label) {
3132 Bcond(label, kCondT, static_cast<Register>(ft), ZERO);
3133}
3134
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003135void MipsAssembler::AdjustBaseAndOffset(Register& base,
3136 int32_t& offset,
3137 bool is_doubleword,
3138 bool is_float) {
3139 // This method is used to adjust the base register and offset pair
3140 // for a load/store when the offset doesn't fit into int16_t.
3141 // It is assumed that `base + offset` is sufficiently aligned for memory
3142 // operands that are machine word in size or smaller. For doubleword-sized
3143 // operands it's assumed that `base` is a multiple of 8, while `offset`
3144 // may be a multiple of 4 (e.g. 4-byte-aligned long and double arguments
3145 // and spilled variables on the stack accessed relative to the stack
3146 // pointer register).
3147 // We preserve the "alignment" of `offset` by adjusting it by a multiple of 8.
3148 CHECK_NE(base, AT); // Must not overwrite the register `base` while loading `offset`.
3149
3150 bool doubleword_aligned = IsAligned<kMipsDoublewordSize>(offset);
3151 bool two_accesses = is_doubleword && (!is_float || !doubleword_aligned);
3152
3153 // IsInt<16> must be passed a signed value, hence the static cast below.
3154 if (IsInt<16>(offset) &&
3155 (!two_accesses || IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
3156 // Nothing to do: `offset` (and, if needed, `offset + 4`) fits into int16_t.
3157 return;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003158 }
3159
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003160 // Remember the "(mis)alignment" of `offset`, it will be checked at the end.
3161 uint32_t misalignment = offset & (kMipsDoublewordSize - 1);
3162
3163 // Do not load the whole 32-bit `offset` if it can be represented as
3164 // a sum of two 16-bit signed offsets. This can save an instruction or two.
3165 // To simplify matters, only do this for a symmetric range of offsets from
3166 // about -64KB to about +64KB, allowing further addition of 4 when accessing
3167 // 64-bit variables with two 32-bit accesses.
3168 constexpr int32_t kMinOffsetForSimpleAdjustment = 0x7ff8; // Max int16_t that's a multiple of 8.
3169 constexpr int32_t kMaxOffsetForSimpleAdjustment = 2 * kMinOffsetForSimpleAdjustment;
3170 if (0 <= offset && offset <= kMaxOffsetForSimpleAdjustment) {
3171 Addiu(AT, base, kMinOffsetForSimpleAdjustment);
3172 offset -= kMinOffsetForSimpleAdjustment;
3173 } else if (-kMaxOffsetForSimpleAdjustment <= offset && offset < 0) {
3174 Addiu(AT, base, -kMinOffsetForSimpleAdjustment);
3175 offset += kMinOffsetForSimpleAdjustment;
3176 } else if (IsR6()) {
3177 // On R6 take advantage of the aui instruction, e.g.:
3178 // aui AT, base, offset_high
3179 // lw reg_lo, offset_low(AT)
3180 // lw reg_hi, (offset_low+4)(AT)
3181 // or when offset_low+4 overflows int16_t:
3182 // aui AT, base, offset_high
3183 // addiu AT, AT, 8
3184 // lw reg_lo, (offset_low-8)(AT)
3185 // lw reg_hi, (offset_low-4)(AT)
3186 int16_t offset_high = High16Bits(offset);
3187 int16_t offset_low = Low16Bits(offset);
3188 offset_high += (offset_low < 0) ? 1 : 0; // Account for offset sign extension in load/store.
3189 Aui(AT, base, offset_high);
3190 if (two_accesses && !IsInt<16>(static_cast<int32_t>(offset_low + kMipsWordSize))) {
3191 // Avoid overflow in the 16-bit offset of the load/store instruction when adding 4.
3192 Addiu(AT, AT, kMipsDoublewordSize);
3193 offset_low -= kMipsDoublewordSize;
3194 }
3195 offset = offset_low;
3196 } else {
3197 // Do not load the whole 32-bit `offset` if it can be represented as
3198 // a sum of three 16-bit signed offsets. This can save an instruction.
3199 // To simplify matters, only do this for a symmetric range of offsets from
3200 // about -96KB to about +96KB, allowing further addition of 4 when accessing
3201 // 64-bit variables with two 32-bit accesses.
3202 constexpr int32_t kMinOffsetForMediumAdjustment = 2 * kMinOffsetForSimpleAdjustment;
3203 constexpr int32_t kMaxOffsetForMediumAdjustment = 3 * kMinOffsetForSimpleAdjustment;
3204 if (0 <= offset && offset <= kMaxOffsetForMediumAdjustment) {
3205 Addiu(AT, base, kMinOffsetForMediumAdjustment / 2);
3206 Addiu(AT, AT, kMinOffsetForMediumAdjustment / 2);
3207 offset -= kMinOffsetForMediumAdjustment;
3208 } else if (-kMaxOffsetForMediumAdjustment <= offset && offset < 0) {
3209 Addiu(AT, base, -kMinOffsetForMediumAdjustment / 2);
3210 Addiu(AT, AT, -kMinOffsetForMediumAdjustment / 2);
3211 offset += kMinOffsetForMediumAdjustment;
3212 } else {
3213 // Now that all shorter options have been exhausted, load the full 32-bit offset.
3214 int32_t loaded_offset = RoundDown(offset, kMipsDoublewordSize);
3215 LoadConst32(AT, loaded_offset);
3216 Addu(AT, AT, base);
3217 offset -= loaded_offset;
3218 }
3219 }
3220 base = AT;
3221
3222 CHECK(IsInt<16>(offset));
3223 if (two_accesses) {
3224 CHECK(IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)));
3225 }
3226 CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
3227}
3228
Alexey Frunze2923db72016-08-20 01:55:47 -07003229void MipsAssembler::LoadFromOffset(LoadOperandType type,
3230 Register reg,
3231 Register base,
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003232 int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003233 LoadFromOffset<>(type, reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003234}
3235
3236void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003237 LoadSFromOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003238}
3239
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003240void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003241 LoadDFromOffset<>(reg, base, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003242}
3243
3244void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
3245 size_t size) {
3246 MipsManagedRegister dst = m_dst.AsMips();
3247 if (dst.IsNoRegister()) {
3248 CHECK_EQ(0u, size) << dst;
3249 } else if (dst.IsCoreRegister()) {
3250 CHECK_EQ(kMipsWordSize, size) << dst;
3251 LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
3252 } else if (dst.IsRegisterPair()) {
3253 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3254 LoadFromOffset(kLoadDoubleword, dst.AsRegisterPairLow(), src_register, src_offset);
3255 } else if (dst.IsFRegister()) {
3256 if (size == kMipsWordSize) {
3257 LoadSFromOffset(dst.AsFRegister(), src_register, src_offset);
3258 } else {
3259 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3260 LoadDFromOffset(dst.AsFRegister(), src_register, src_offset);
3261 }
Alexey Frunze1b8464d2016-11-12 17:22:05 -08003262 } else if (dst.IsDRegister()) {
3263 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3264 LoadDFromOffset(dst.AsOverlappingDRegisterLow(), src_register, src_offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003265 }
jeffhao7fbee072012-08-24 17:56:54 -07003266}
3267
Alexey Frunze2923db72016-08-20 01:55:47 -07003268void MipsAssembler::StoreToOffset(StoreOperandType type,
3269 Register reg,
3270 Register base,
jeffhao7fbee072012-08-24 17:56:54 -07003271 int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003272 StoreToOffset<>(type, reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003273}
3274
Goran Jakovljevicff734982015-08-24 12:58:55 +00003275void MipsAssembler::StoreSToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003276 StoreSToOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003277}
3278
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003279void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003280 StoreDToOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003281}
3282
David Srbeckydd973932015-04-07 20:29:48 +01003283static dwarf::Reg DWARFReg(Register reg) {
3284 return dwarf::Reg::MipsCore(static_cast<int>(reg));
3285}
3286
Ian Rogers790a6b72014-04-01 10:36:00 -07003287constexpr size_t kFramePointerSize = 4;
3288
Vladimir Marko32248382016-05-19 10:37:24 +01003289void MipsAssembler::BuildFrame(size_t frame_size,
3290 ManagedRegister method_reg,
3291 ArrayRef<const ManagedRegister> callee_save_regs,
Dmitry Petrochenkofca82202014-03-21 11:21:37 +07003292 const ManagedRegisterEntrySpills& entry_spills) {
jeffhao7fbee072012-08-24 17:56:54 -07003293 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003294 DCHECK(!overwriting_);
jeffhao7fbee072012-08-24 17:56:54 -07003295
3296 // Increase frame to required size.
3297 IncreaseFrameSize(frame_size);
3298
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003299 // Push callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07003300 int stack_offset = frame_size - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003301 StoreToOffset(kStoreWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003302 cfi_.RelOffset(DWARFReg(RA), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07003303 for (int i = callee_save_regs.size() - 1; i >= 0; --i) {
Ian Rogers790a6b72014-04-01 10:36:00 -07003304 stack_offset -= kFramePointerSize;
Vladimir Marko32248382016-05-19 10:37:24 +01003305 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07003306 StoreToOffset(kStoreWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003307 cfi_.RelOffset(DWARFReg(reg), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07003308 }
3309
3310 // Write out Method*.
3311 StoreToOffset(kStoreWord, method_reg.AsMips().AsCoreRegister(), SP, 0);
3312
3313 // Write out entry spills.
Goran Jakovljevicff734982015-08-24 12:58:55 +00003314 int32_t offset = frame_size + kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003315 for (size_t i = 0; i < entry_spills.size(); ++i) {
Goran Jakovljevicff734982015-08-24 12:58:55 +00003316 MipsManagedRegister reg = entry_spills.at(i).AsMips();
3317 if (reg.IsNoRegister()) {
3318 ManagedRegisterSpill spill = entry_spills.at(i);
3319 offset += spill.getSize();
3320 } else if (reg.IsCoreRegister()) {
3321 StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003322 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003323 } else if (reg.IsFRegister()) {
3324 StoreSToOffset(reg.AsFRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003325 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003326 } else if (reg.IsDRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003327 StoreDToOffset(reg.AsOverlappingDRegisterLow(), SP, offset);
3328 offset += kMipsDoublewordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003329 }
jeffhao7fbee072012-08-24 17:56:54 -07003330 }
3331}
3332
3333void MipsAssembler::RemoveFrame(size_t frame_size,
Vladimir Marko32248382016-05-19 10:37:24 +01003334 ArrayRef<const ManagedRegister> callee_save_regs) {
jeffhao7fbee072012-08-24 17:56:54 -07003335 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003336 DCHECK(!overwriting_);
David Srbeckydd973932015-04-07 20:29:48 +01003337 cfi_.RememberState();
jeffhao7fbee072012-08-24 17:56:54 -07003338
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003339 // Pop callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07003340 int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003341 for (size_t i = 0; i < callee_save_regs.size(); ++i) {
Vladimir Marko32248382016-05-19 10:37:24 +01003342 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07003343 LoadFromOffset(kLoadWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003344 cfi_.Restore(DWARFReg(reg));
Ian Rogers790a6b72014-04-01 10:36:00 -07003345 stack_offset += kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003346 }
3347 LoadFromOffset(kLoadWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003348 cfi_.Restore(DWARFReg(RA));
jeffhao7fbee072012-08-24 17:56:54 -07003349
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003350 // Adjust the stack pointer in the delay slot if doing so doesn't break CFI.
3351 bool exchange = IsInt<16>(static_cast<int32_t>(frame_size));
3352 bool reordering = SetReorder(false);
3353 if (exchange) {
3354 // Jump to the return address.
3355 Jr(RA);
3356 // Decrease frame to required size.
3357 DecreaseFrameSize(frame_size); // Single instruction in delay slot.
3358 } else {
3359 // Decrease frame to required size.
3360 DecreaseFrameSize(frame_size);
3361 // Jump to the return address.
3362 Jr(RA);
3363 Nop(); // In delay slot.
3364 }
3365 SetReorder(reordering);
David Srbeckydd973932015-04-07 20:29:48 +01003366
3367 // The CFI should be restored for any code that follows the exit block.
3368 cfi_.RestoreState();
3369 cfi_.DefCFAOffset(frame_size);
jeffhao7fbee072012-08-24 17:56:54 -07003370}
3371
3372void MipsAssembler::IncreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003373 CHECK_ALIGNED(adjust, kFramePointerSize);
3374 Addiu32(SP, SP, -adjust);
David Srbeckydd973932015-04-07 20:29:48 +01003375 cfi_.AdjustCFAOffset(adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003376 if (overwriting_) {
3377 cfi_.OverrideDelayedPC(overwrite_location_);
3378 }
jeffhao7fbee072012-08-24 17:56:54 -07003379}
3380
3381void MipsAssembler::DecreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003382 CHECK_ALIGNED(adjust, kFramePointerSize);
3383 Addiu32(SP, SP, adjust);
David Srbeckydd973932015-04-07 20:29:48 +01003384 cfi_.AdjustCFAOffset(-adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003385 if (overwriting_) {
3386 cfi_.OverrideDelayedPC(overwrite_location_);
3387 }
jeffhao7fbee072012-08-24 17:56:54 -07003388}
3389
3390void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
3391 MipsManagedRegister src = msrc.AsMips();
3392 if (src.IsNoRegister()) {
3393 CHECK_EQ(0u, size);
3394 } else if (src.IsCoreRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003395 CHECK_EQ(kMipsWordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07003396 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3397 } else if (src.IsRegisterPair()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003398 CHECK_EQ(kMipsDoublewordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07003399 StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
3400 StoreToOffset(kStoreWord, src.AsRegisterPairHigh(),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003401 SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003402 } else if (src.IsFRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003403 if (size == kMipsWordSize) {
3404 StoreSToOffset(src.AsFRegister(), SP, dest.Int32Value());
3405 } else {
3406 CHECK_EQ(kMipsDoublewordSize, size);
3407 StoreDToOffset(src.AsFRegister(), SP, dest.Int32Value());
3408 }
Alexey Frunze1b8464d2016-11-12 17:22:05 -08003409 } else if (src.IsDRegister()) {
3410 CHECK_EQ(kMipsDoublewordSize, size);
3411 StoreDToOffset(src.AsOverlappingDRegisterLow(), SP, dest.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003412 }
3413}
3414
3415void MipsAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
3416 MipsManagedRegister src = msrc.AsMips();
3417 CHECK(src.IsCoreRegister());
3418 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3419}
3420
3421void MipsAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
3422 MipsManagedRegister src = msrc.AsMips();
3423 CHECK(src.IsCoreRegister());
3424 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3425}
3426
3427void MipsAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
3428 ManagedRegister mscratch) {
3429 MipsManagedRegister scratch = mscratch.AsMips();
3430 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003431 LoadConst32(scratch.AsCoreRegister(), imm);
jeffhao7fbee072012-08-24 17:56:54 -07003432 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
3433}
3434
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003435void MipsAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs,
3436 FrameOffset fr_offs,
jeffhao7fbee072012-08-24 17:56:54 -07003437 ManagedRegister mscratch) {
3438 MipsManagedRegister scratch = mscratch.AsMips();
3439 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003440 Addiu32(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003441 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3442 S1, thr_offs.Int32Value());
3443}
3444
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003445void MipsAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003446 StoreToOffset(kStoreWord, SP, S1, thr_offs.Int32Value());
3447}
3448
3449void MipsAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
3450 FrameOffset in_off, ManagedRegister mscratch) {
3451 MipsManagedRegister src = msrc.AsMips();
3452 MipsManagedRegister scratch = mscratch.AsMips();
3453 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3454 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003455 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003456}
3457
3458void MipsAssembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) {
3459 return EmitLoad(mdest, SP, src.Int32Value(), size);
3460}
3461
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003462void MipsAssembler::LoadFromThread(ManagedRegister mdest, ThreadOffset32 src, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003463 return EmitLoad(mdest, S1, src.Int32Value(), size);
3464}
3465
3466void MipsAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) {
3467 MipsManagedRegister dest = mdest.AsMips();
3468 CHECK(dest.IsCoreRegister());
3469 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value());
3470}
3471
Mathieu Chartiere401d142015-04-22 13:56:20 -07003472void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs,
Roland Levillain4d027112015-07-01 15:41:14 +01003473 bool unpoison_reference) {
jeffhao7fbee072012-08-24 17:56:54 -07003474 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003475 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07003476 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
3477 base.AsMips().AsCoreRegister(), offs.Int32Value());
Alexey Frunzec061de12017-02-14 13:27:23 -08003478 if (unpoison_reference) {
3479 MaybeUnpoisonHeapReference(dest.AsCoreRegister());
Hiroshi Yamauchie63a7452014-02-27 14:44:36 -08003480 }
jeffhao7fbee072012-08-24 17:56:54 -07003481}
3482
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003483void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003484 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003485 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07003486 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
3487 base.AsMips().AsCoreRegister(), offs.Int32Value());
3488}
3489
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003490void MipsAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset32 offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003491 MipsManagedRegister dest = mdest.AsMips();
3492 CHECK(dest.IsCoreRegister());
3493 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), S1, offs.Int32Value());
3494}
3495
3496void MipsAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
3497 UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips";
3498}
3499
3500void MipsAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
3501 UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips";
3502}
3503
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003504void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003505 MipsManagedRegister dest = mdest.AsMips();
3506 MipsManagedRegister src = msrc.AsMips();
3507 if (!dest.Equals(src)) {
3508 if (dest.IsCoreRegister()) {
3509 CHECK(src.IsCoreRegister()) << src;
3510 Move(dest.AsCoreRegister(), src.AsCoreRegister());
3511 } else if (dest.IsFRegister()) {
3512 CHECK(src.IsFRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003513 if (size == kMipsWordSize) {
3514 MovS(dest.AsFRegister(), src.AsFRegister());
3515 } else {
3516 CHECK_EQ(kMipsDoublewordSize, size);
3517 MovD(dest.AsFRegister(), src.AsFRegister());
3518 }
jeffhao7fbee072012-08-24 17:56:54 -07003519 } else if (dest.IsDRegister()) {
3520 CHECK(src.IsDRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003521 MovD(dest.AsOverlappingDRegisterLow(), src.AsOverlappingDRegisterLow());
jeffhao7fbee072012-08-24 17:56:54 -07003522 } else {
3523 CHECK(dest.IsRegisterPair()) << dest;
3524 CHECK(src.IsRegisterPair()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003525 // Ensure that the first move doesn't clobber the input of the second.
jeffhao7fbee072012-08-24 17:56:54 -07003526 if (src.AsRegisterPairHigh() != dest.AsRegisterPairLow()) {
3527 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
3528 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
3529 } else {
3530 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
3531 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
3532 }
3533 }
3534 }
3535}
3536
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003537void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003538 MipsManagedRegister scratch = mscratch.AsMips();
3539 CHECK(scratch.IsCoreRegister()) << scratch;
3540 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3541 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
3542}
3543
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003544void MipsAssembler::CopyRawPtrFromThread(FrameOffset fr_offs,
3545 ThreadOffset32 thr_offs,
3546 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003547 MipsManagedRegister scratch = mscratch.AsMips();
3548 CHECK(scratch.IsCoreRegister()) << scratch;
3549 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3550 S1, thr_offs.Int32Value());
3551 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3552 SP, fr_offs.Int32Value());
3553}
3554
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003555void MipsAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs,
3556 FrameOffset fr_offs,
3557 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003558 MipsManagedRegister scratch = mscratch.AsMips();
3559 CHECK(scratch.IsCoreRegister()) << scratch;
3560 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3561 SP, fr_offs.Int32Value());
3562 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3563 S1, thr_offs.Int32Value());
3564}
3565
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003566void MipsAssembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003567 MipsManagedRegister scratch = mscratch.AsMips();
3568 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003569 CHECK(size == kMipsWordSize || size == kMipsDoublewordSize) << size;
3570 if (size == kMipsWordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07003571 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3572 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003573 } else if (size == kMipsDoublewordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07003574 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3575 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003576 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + kMipsWordSize);
3577 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003578 }
3579}
3580
3581void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
3582 ManagedRegister mscratch, size_t size) {
3583 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003584 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003585 LoadFromOffset(kLoadWord, scratch, src_base.AsMips().AsCoreRegister(), src_offset.Int32Value());
3586 StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
3587}
3588
3589void MipsAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
3590 ManagedRegister mscratch, size_t size) {
3591 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003592 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003593 LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
3594 StoreToOffset(kStoreWord, scratch, dest_base.AsMips().AsCoreRegister(), dest_offset.Int32Value());
3595}
3596
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003597void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
3598 FrameOffset src_base ATTRIBUTE_UNUSED,
3599 Offset src_offset ATTRIBUTE_UNUSED,
3600 ManagedRegister mscratch ATTRIBUTE_UNUSED,
3601 size_t size ATTRIBUTE_UNUSED) {
3602 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003603}
3604
3605void MipsAssembler::Copy(ManagedRegister dest, Offset dest_offset,
3606 ManagedRegister src, Offset src_offset,
3607 ManagedRegister mscratch, size_t size) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003608 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003609 Register scratch = mscratch.AsMips().AsCoreRegister();
3610 LoadFromOffset(kLoadWord, scratch, src.AsMips().AsCoreRegister(), src_offset.Int32Value());
3611 StoreToOffset(kStoreWord, scratch, dest.AsMips().AsCoreRegister(), dest_offset.Int32Value());
3612}
3613
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003614void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
3615 Offset dest_offset ATTRIBUTE_UNUSED,
3616 FrameOffset src ATTRIBUTE_UNUSED,
3617 Offset src_offset ATTRIBUTE_UNUSED,
3618 ManagedRegister mscratch ATTRIBUTE_UNUSED,
3619 size_t size ATTRIBUTE_UNUSED) {
3620 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003621}
3622
3623void MipsAssembler::MemoryBarrier(ManagedRegister) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003624 // TODO: sync?
3625 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003626}
3627
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003628void MipsAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003629 FrameOffset handle_scope_offset,
3630 ManagedRegister min_reg,
3631 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003632 MipsManagedRegister out_reg = mout_reg.AsMips();
3633 MipsManagedRegister in_reg = min_reg.AsMips();
3634 CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
3635 CHECK(out_reg.IsCoreRegister()) << out_reg;
3636 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003637 MipsLabel null_arg;
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003638 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3639 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003640 // E.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset).
jeffhao7fbee072012-08-24 17:56:54 -07003641 if (in_reg.IsNoRegister()) {
3642 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003643 SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003644 in_reg = out_reg;
3645 }
3646 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003647 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003648 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003649 Beqz(in_reg.AsCoreRegister(), &null_arg);
3650 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3651 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003652 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003653 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003654 }
3655}
3656
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003657void MipsAssembler::CreateHandleScopeEntry(FrameOffset out_off,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003658 FrameOffset handle_scope_offset,
3659 ManagedRegister mscratch,
3660 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003661 MipsManagedRegister scratch = mscratch.AsMips();
3662 CHECK(scratch.IsCoreRegister()) << scratch;
3663 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003664 MipsLabel null_arg;
3665 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003666 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3667 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003668 // E.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset).
3669 Beqz(scratch.AsCoreRegister(), &null_arg);
3670 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3671 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003672 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003673 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003674 }
3675 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
3676}
3677
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003678// Given a handle scope entry, load the associated reference.
3679void MipsAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003680 ManagedRegister min_reg) {
jeffhao7fbee072012-08-24 17:56:54 -07003681 MipsManagedRegister out_reg = mout_reg.AsMips();
3682 MipsManagedRegister in_reg = min_reg.AsMips();
3683 CHECK(out_reg.IsCoreRegister()) << out_reg;
3684 CHECK(in_reg.IsCoreRegister()) << in_reg;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003685 MipsLabel null_arg;
jeffhao7fbee072012-08-24 17:56:54 -07003686 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003687 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003688 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003689 Beqz(in_reg.AsCoreRegister(), &null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003690 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
3691 in_reg.AsCoreRegister(), 0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003692 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003693}
3694
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003695void MipsAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,
3696 bool could_be_null ATTRIBUTE_UNUSED) {
3697 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003698}
3699
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003700void MipsAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED,
3701 bool could_be_null ATTRIBUTE_UNUSED) {
3702 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003703}
3704
3705void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) {
3706 MipsManagedRegister base = mbase.AsMips();
3707 MipsManagedRegister scratch = mscratch.AsMips();
3708 CHECK(base.IsCoreRegister()) << base;
3709 CHECK(scratch.IsCoreRegister()) << scratch;
3710 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3711 base.AsCoreRegister(), offset.Int32Value());
3712 Jalr(scratch.AsCoreRegister());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003713 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003714 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003715}
3716
3717void MipsAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
3718 MipsManagedRegister scratch = mscratch.AsMips();
3719 CHECK(scratch.IsCoreRegister()) << scratch;
3720 // Call *(*(SP + base) + offset)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003721 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, base.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003722 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3723 scratch.AsCoreRegister(), offset.Int32Value());
3724 Jalr(scratch.AsCoreRegister());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003725 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003726 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003727}
3728
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003729void MipsAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UNUSED,
3730 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
Ian Rogers468532e2013-08-05 10:56:33 -07003731 UNIMPLEMENTED(FATAL) << "no mips implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003732}
3733
3734void MipsAssembler::GetCurrentThread(ManagedRegister tr) {
3735 Move(tr.AsMips().AsCoreRegister(), S1);
3736}
3737
3738void MipsAssembler::GetCurrentThread(FrameOffset offset,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003739 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
jeffhao7fbee072012-08-24 17:56:54 -07003740 StoreToOffset(kStoreWord, S1, SP, offset.Int32Value());
3741}
3742
jeffhao7fbee072012-08-24 17:56:54 -07003743void MipsAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
3744 MipsManagedRegister scratch = mscratch.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003745 exception_blocks_.emplace_back(scratch, stack_adjust);
jeffhao7fbee072012-08-24 17:56:54 -07003746 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
Andreas Gampe542451c2016-07-26 09:02:02 -07003747 S1, Thread::ExceptionOffset<kMipsPointerSize>().Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003748 Bnez(scratch.AsCoreRegister(), exception_blocks_.back().Entry());
jeffhao7fbee072012-08-24 17:56:54 -07003749}
3750
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003751void MipsAssembler::EmitExceptionPoll(MipsExceptionSlowPath* exception) {
3752 Bind(exception->Entry());
3753 if (exception->stack_adjust_ != 0) { // Fix up the frame.
3754 DecreaseFrameSize(exception->stack_adjust_);
jeffhao7fbee072012-08-24 17:56:54 -07003755 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003756 // Pass exception object as argument.
3757 // Don't care about preserving A0 as this call won't return.
3758 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
3759 Move(A0, exception->scratch_.AsCoreRegister());
3760 // Set up call to Thread::Current()->pDeliverException.
3761 LoadFromOffset(kLoadWord, T9, S1,
Andreas Gampe542451c2016-07-26 09:02:02 -07003762 QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, pDeliverException).Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003763 Jr(T9);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003764 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003765
3766 // Call never returns.
3767 Break();
jeffhao7fbee072012-08-24 17:56:54 -07003768}
3769
3770} // namespace mips
3771} // namespace art