blob: a99d02d4d0d9ddbb045bfac3ebfeaaf7938cd84d [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 Larsencd0295d2017-03-31 15:26:54 -0700638// TODO: This instruction is available in both R6 and MSA and it should be used when available.
Chris Larsen692235e2016-11-21 16:04:53 -0800639void MipsAssembler::Lsa(Register rd, Register rs, Register rt, int saPlusOne) {
640 CHECK(IsR6());
641 CHECK(1 <= saPlusOne && saPlusOne <= 4) << saPlusOne;
642 int sa = saPlusOne - 1;
643 DsFsmInstrRrr(EmitR(0x0, rs, rt, rd, sa, 0x05), rd, rs, rt);
644}
645
Chris Larsencd0295d2017-03-31 15:26:54 -0700646void MipsAssembler::ShiftAndAdd(Register dst,
647 Register src_idx,
648 Register src_base,
649 int shamt,
650 Register tmp) {
651 CHECK(0 <= shamt && shamt <= 4) << shamt;
652 CHECK_NE(src_base, tmp);
653 if (shamt == TIMES_1) {
654 // Catch the special case where the shift amount is zero (0).
655 Addu(dst, src_base, src_idx);
656 } else if (IsR6()) {
657 Lsa(dst, src_idx, src_base, shamt);
658 } else {
659 Sll(tmp, src_idx, shamt);
660 Addu(dst, src_base, tmp);
661 }
662}
663
jeffhao7fbee072012-08-24 17:56:54 -0700664void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700665 DsFsmInstrRrr(EmitI(0x20, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700666}
667
668void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700669 DsFsmInstrRrr(EmitI(0x21, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700670}
671
672void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700673 DsFsmInstrRrr(EmitI(0x23, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700674}
675
Chris Larsen3acee732015-11-18 13:31:08 -0800676void MipsAssembler::Lwl(Register rt, Register rs, uint16_t imm16) {
677 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700678 DsFsmInstrRrr(EmitI(0x22, rs, rt, imm16), rt, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800679}
680
681void MipsAssembler::Lwr(Register rt, Register rs, uint16_t imm16) {
682 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700683 DsFsmInstrRrr(EmitI(0x26, rs, rt, imm16), rt, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800684}
685
jeffhao7fbee072012-08-24 17:56:54 -0700686void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700687 DsFsmInstrRrr(EmitI(0x24, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700688}
689
690void MipsAssembler::Lhu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700691 DsFsmInstrRrr(EmitI(0x25, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700692}
693
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700694void MipsAssembler::Lwpc(Register rs, uint32_t imm19) {
695 CHECK(IsR6());
696 CHECK(IsUint<19>(imm19)) << imm19;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700697 DsFsmInstrNop(EmitI21(0x3B, rs, (0x01 << 19) | imm19));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700698}
699
jeffhao7fbee072012-08-24 17:56:54 -0700700void MipsAssembler::Lui(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700701 DsFsmInstrRrr(EmitI(0xf, static_cast<Register>(0), rt, imm16), rt, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700702}
703
Alexey Frunzecad3a4c2016-06-07 23:40:37 -0700704void MipsAssembler::Aui(Register rt, Register rs, uint16_t imm16) {
705 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700706 DsFsmInstrRrr(EmitI(0xf, rs, rt, imm16), rt, rt, rs);
Alexey Frunzecad3a4c2016-06-07 23:40:37 -0700707}
708
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200709void MipsAssembler::Sync(uint32_t stype) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700710 DsFsmInstrNop(EmitR(0, ZERO, ZERO, ZERO, stype & 0x1f, 0xf));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200711}
712
jeffhao7fbee072012-08-24 17:56:54 -0700713void MipsAssembler::Mfhi(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200714 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700715 DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x10), rd, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700716}
717
718void MipsAssembler::Mflo(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200719 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700720 DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x12), rd, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700721}
722
723void MipsAssembler::Sb(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700724 DsFsmInstrRrr(EmitI(0x28, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700725}
726
727void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700728 DsFsmInstrRrr(EmitI(0x29, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700729}
730
731void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700732 DsFsmInstrRrr(EmitI(0x2b, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700733}
734
Chris Larsen3acee732015-11-18 13:31:08 -0800735void MipsAssembler::Swl(Register rt, Register rs, uint16_t imm16) {
736 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700737 DsFsmInstrRrr(EmitI(0x2a, rs, rt, imm16), ZERO, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800738}
739
740void MipsAssembler::Swr(Register rt, Register rs, uint16_t imm16) {
741 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700742 DsFsmInstrRrr(EmitI(0x2e, rs, rt, imm16), ZERO, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800743}
744
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700745void MipsAssembler::LlR2(Register rt, Register base, int16_t imm16) {
746 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700747 DsFsmInstrRrr(EmitI(0x30, base, rt, imm16), rt, base, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700748}
749
750void MipsAssembler::ScR2(Register rt, Register base, int16_t imm16) {
751 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700752 DsFsmInstrRrr(EmitI(0x38, base, rt, imm16), rt, rt, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700753}
754
755void MipsAssembler::LlR6(Register rt, Register base, int16_t imm9) {
756 CHECK(IsR6());
757 CHECK(IsInt<9>(imm9));
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700758 DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36), rt, base, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700759}
760
761void MipsAssembler::ScR6(Register rt, Register base, int16_t imm9) {
762 CHECK(IsR6());
763 CHECK(IsInt<9>(imm9));
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700764 DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26), rt, rt, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700765}
766
jeffhao7fbee072012-08-24 17:56:54 -0700767void MipsAssembler::Slt(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700768 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2a), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700769}
770
771void MipsAssembler::Sltu(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700772 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2b), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700773}
774
775void MipsAssembler::Slti(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700776 DsFsmInstrRrr(EmitI(0xa, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700777}
778
779void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700780 DsFsmInstrRrr(EmitI(0xb, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700781}
782
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200783void MipsAssembler::B(uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700784 DsFsmInstrNop(EmitI(0x4, static_cast<Register>(0), static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200785}
786
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700787void MipsAssembler::Bal(uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700788 DsFsmInstrNop(EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x11), imm16));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700789}
790
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200791void MipsAssembler::Beq(Register rs, Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700792 DsFsmInstrNop(EmitI(0x4, rs, rt, imm16));
jeffhao7fbee072012-08-24 17:56:54 -0700793}
794
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200795void MipsAssembler::Bne(Register rs, Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700796 DsFsmInstrNop(EmitI(0x5, rs, rt, imm16));
jeffhao7fbee072012-08-24 17:56:54 -0700797}
798
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200799void MipsAssembler::Beqz(Register rt, uint16_t imm16) {
800 Beq(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700801}
802
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200803void MipsAssembler::Bnez(Register rt, uint16_t imm16) {
804 Bne(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700805}
806
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200807void MipsAssembler::Bltz(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700808 DsFsmInstrNop(EmitI(0x1, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200809}
810
811void MipsAssembler::Bgez(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700812 DsFsmInstrNop(EmitI(0x1, rt, static_cast<Register>(0x1), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200813}
814
815void MipsAssembler::Blez(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700816 DsFsmInstrNop(EmitI(0x6, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200817}
818
819void MipsAssembler::Bgtz(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700820 DsFsmInstrNop(EmitI(0x7, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200821}
822
Chris Larsenb74353a2015-11-20 09:07:09 -0800823void MipsAssembler::Bc1f(uint16_t imm16) {
824 Bc1f(0, imm16);
825}
826
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800827void MipsAssembler::Bc1f(int cc, uint16_t imm16) {
828 CHECK(!IsR6());
829 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700830 DsFsmInstrNop(EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>(cc << 2), imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800831}
832
Chris Larsenb74353a2015-11-20 09:07:09 -0800833void MipsAssembler::Bc1t(uint16_t imm16) {
834 Bc1t(0, imm16);
835}
836
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800837void MipsAssembler::Bc1t(int cc, uint16_t imm16) {
838 CHECK(!IsR6());
839 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700840 DsFsmInstrNop(EmitI(0x11,
841 static_cast<Register>(0x8),
842 static_cast<Register>((cc << 2) | 1),
843 imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800844}
845
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200846void MipsAssembler::J(uint32_t addr26) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700847 DsFsmInstrNop(EmitI26(0x2, addr26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200848}
849
850void MipsAssembler::Jal(uint32_t addr26) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700851 DsFsmInstrNop(EmitI26(0x3, addr26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200852}
853
854void MipsAssembler::Jalr(Register rd, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700855 uint32_t last_instruction = delay_slot_.instruction_;
856 bool exchange = (last_instruction != 0 &&
857 (delay_slot_.gpr_outs_mask_ & (1u << rs)) == 0 &&
858 ((delay_slot_.gpr_ins_mask_ | delay_slot_.gpr_outs_mask_) & (1u << rd)) == 0);
859 if (exchange) {
860 // The last instruction cannot be used in a different delay slot,
861 // do not commit the label before it (if any).
862 DsFsmDropLabel();
863 }
864 DsFsmInstrNop(EmitR(0, rs, static_cast<Register>(0), rd, 0, 0x09));
865 if (exchange) {
866 // Exchange the last two instructions in the assembler buffer.
867 size_t size = buffer_.Size();
868 CHECK_GE(size, 2 * sizeof(uint32_t));
869 size_t pos1 = size - 2 * sizeof(uint32_t);
870 size_t pos2 = size - sizeof(uint32_t);
871 uint32_t instr1 = buffer_.Load<uint32_t>(pos1);
872 uint32_t instr2 = buffer_.Load<uint32_t>(pos2);
873 CHECK_EQ(instr1, last_instruction);
874 buffer_.Store<uint32_t>(pos1, instr2);
875 buffer_.Store<uint32_t>(pos2, instr1);
876 } else if (reordering_) {
877 Nop();
878 }
jeffhao7fbee072012-08-24 17:56:54 -0700879}
880
881void MipsAssembler::Jalr(Register rs) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200882 Jalr(RA, rs);
883}
884
885void MipsAssembler::Jr(Register rs) {
886 Jalr(ZERO, rs);
887}
888
889void MipsAssembler::Nal() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700890 DsFsmInstrNop(EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x10), 0));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200891}
892
893void MipsAssembler::Auipc(Register rs, uint16_t imm16) {
894 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700895 DsFsmInstrNop(EmitI(0x3B, rs, static_cast<Register>(0x1E), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200896}
897
898void MipsAssembler::Addiupc(Register rs, uint32_t imm19) {
899 CHECK(IsR6());
900 CHECK(IsUint<19>(imm19)) << imm19;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700901 DsFsmInstrNop(EmitI21(0x3B, rs, imm19));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200902}
903
904void MipsAssembler::Bc(uint32_t imm26) {
905 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700906 DsFsmInstrNop(EmitI26(0x32, imm26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200907}
908
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700909void MipsAssembler::Balc(uint32_t imm26) {
910 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700911 DsFsmInstrNop(EmitI26(0x3A, imm26));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700912}
913
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200914void MipsAssembler::Jic(Register rt, uint16_t imm16) {
915 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700916 DsFsmInstrNop(EmitI(0x36, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200917}
918
919void MipsAssembler::Jialc(Register rt, uint16_t imm16) {
920 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700921 DsFsmInstrNop(EmitI(0x3E, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200922}
923
924void MipsAssembler::Bltc(Register rs, Register rt, uint16_t imm16) {
925 CHECK(IsR6());
926 CHECK_NE(rs, ZERO);
927 CHECK_NE(rt, ZERO);
928 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700929 DsFsmInstrNop(EmitI(0x17, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200930}
931
932void MipsAssembler::Bltzc(Register rt, uint16_t imm16) {
933 CHECK(IsR6());
934 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700935 DsFsmInstrNop(EmitI(0x17, rt, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200936}
937
938void MipsAssembler::Bgtzc(Register rt, uint16_t imm16) {
939 CHECK(IsR6());
940 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700941 DsFsmInstrNop(EmitI(0x17, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200942}
943
944void MipsAssembler::Bgec(Register rs, Register rt, uint16_t imm16) {
945 CHECK(IsR6());
946 CHECK_NE(rs, ZERO);
947 CHECK_NE(rt, ZERO);
948 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700949 DsFsmInstrNop(EmitI(0x16, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200950}
951
952void MipsAssembler::Bgezc(Register rt, uint16_t imm16) {
953 CHECK(IsR6());
954 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700955 DsFsmInstrNop(EmitI(0x16, rt, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200956}
957
958void MipsAssembler::Blezc(Register rt, uint16_t imm16) {
959 CHECK(IsR6());
960 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700961 DsFsmInstrNop(EmitI(0x16, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200962}
963
964void MipsAssembler::Bltuc(Register rs, Register rt, uint16_t imm16) {
965 CHECK(IsR6());
966 CHECK_NE(rs, ZERO);
967 CHECK_NE(rt, ZERO);
968 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700969 DsFsmInstrNop(EmitI(0x7, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200970}
971
972void MipsAssembler::Bgeuc(Register rs, Register rt, uint16_t imm16) {
973 CHECK(IsR6());
974 CHECK_NE(rs, ZERO);
975 CHECK_NE(rt, ZERO);
976 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700977 DsFsmInstrNop(EmitI(0x6, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200978}
979
980void MipsAssembler::Beqc(Register rs, Register rt, uint16_t imm16) {
981 CHECK(IsR6());
982 CHECK_NE(rs, ZERO);
983 CHECK_NE(rt, ZERO);
984 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700985 DsFsmInstrNop(EmitI(0x8, std::min(rs, rt), std::max(rs, rt), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200986}
987
988void MipsAssembler::Bnec(Register rs, Register rt, uint16_t imm16) {
989 CHECK(IsR6());
990 CHECK_NE(rs, ZERO);
991 CHECK_NE(rt, ZERO);
992 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700993 DsFsmInstrNop(EmitI(0x18, std::min(rs, rt), std::max(rs, rt), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200994}
995
996void MipsAssembler::Beqzc(Register rs, uint32_t imm21) {
997 CHECK(IsR6());
998 CHECK_NE(rs, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700999 DsFsmInstrNop(EmitI21(0x36, rs, imm21));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001000}
1001
1002void MipsAssembler::Bnezc(Register rs, uint32_t imm21) {
1003 CHECK(IsR6());
1004 CHECK_NE(rs, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001005 DsFsmInstrNop(EmitI21(0x3E, rs, imm21));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001006}
1007
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001008void MipsAssembler::Bc1eqz(FRegister ft, uint16_t imm16) {
1009 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001010 DsFsmInstrNop(EmitFI(0x11, 0x9, ft, imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001011}
1012
1013void MipsAssembler::Bc1nez(FRegister ft, uint16_t imm16) {
1014 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001015 DsFsmInstrNop(EmitFI(0x11, 0xD, ft, imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001016}
1017
1018void MipsAssembler::EmitBcondR2(BranchCondition cond, Register rs, Register rt, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001019 switch (cond) {
1020 case kCondLTZ:
1021 CHECK_EQ(rt, ZERO);
1022 Bltz(rs, imm16);
1023 break;
1024 case kCondGEZ:
1025 CHECK_EQ(rt, ZERO);
1026 Bgez(rs, imm16);
1027 break;
1028 case kCondLEZ:
1029 CHECK_EQ(rt, ZERO);
1030 Blez(rs, imm16);
1031 break;
1032 case kCondGTZ:
1033 CHECK_EQ(rt, ZERO);
1034 Bgtz(rs, imm16);
1035 break;
1036 case kCondEQ:
1037 Beq(rs, rt, imm16);
1038 break;
1039 case kCondNE:
1040 Bne(rs, rt, imm16);
1041 break;
1042 case kCondEQZ:
1043 CHECK_EQ(rt, ZERO);
1044 Beqz(rs, imm16);
1045 break;
1046 case kCondNEZ:
1047 CHECK_EQ(rt, ZERO);
1048 Bnez(rs, imm16);
1049 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001050 case kCondF:
1051 CHECK_EQ(rt, ZERO);
1052 Bc1f(static_cast<int>(rs), imm16);
1053 break;
1054 case kCondT:
1055 CHECK_EQ(rt, ZERO);
1056 Bc1t(static_cast<int>(rs), imm16);
1057 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001058 case kCondLT:
1059 case kCondGE:
1060 case kCondLE:
1061 case kCondGT:
1062 case kCondLTU:
1063 case kCondGEU:
1064 case kUncond:
1065 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
1066 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
1067 LOG(FATAL) << "Unexpected branch condition " << cond;
1068 UNREACHABLE();
1069 }
1070}
1071
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001072void MipsAssembler::EmitBcondR6(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001073 switch (cond) {
1074 case kCondLT:
1075 Bltc(rs, rt, imm16_21);
1076 break;
1077 case kCondGE:
1078 Bgec(rs, rt, imm16_21);
1079 break;
1080 case kCondLE:
1081 Bgec(rt, rs, imm16_21);
1082 break;
1083 case kCondGT:
1084 Bltc(rt, rs, imm16_21);
1085 break;
1086 case kCondLTZ:
1087 CHECK_EQ(rt, ZERO);
1088 Bltzc(rs, imm16_21);
1089 break;
1090 case kCondGEZ:
1091 CHECK_EQ(rt, ZERO);
1092 Bgezc(rs, imm16_21);
1093 break;
1094 case kCondLEZ:
1095 CHECK_EQ(rt, ZERO);
1096 Blezc(rs, imm16_21);
1097 break;
1098 case kCondGTZ:
1099 CHECK_EQ(rt, ZERO);
1100 Bgtzc(rs, imm16_21);
1101 break;
1102 case kCondEQ:
1103 Beqc(rs, rt, imm16_21);
1104 break;
1105 case kCondNE:
1106 Bnec(rs, rt, imm16_21);
1107 break;
1108 case kCondEQZ:
1109 CHECK_EQ(rt, ZERO);
1110 Beqzc(rs, imm16_21);
1111 break;
1112 case kCondNEZ:
1113 CHECK_EQ(rt, ZERO);
1114 Bnezc(rs, imm16_21);
1115 break;
1116 case kCondLTU:
1117 Bltuc(rs, rt, imm16_21);
1118 break;
1119 case kCondGEU:
1120 Bgeuc(rs, rt, imm16_21);
1121 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001122 case kCondF:
1123 CHECK_EQ(rt, ZERO);
1124 Bc1eqz(static_cast<FRegister>(rs), imm16_21);
1125 break;
1126 case kCondT:
1127 CHECK_EQ(rt, ZERO);
1128 Bc1nez(static_cast<FRegister>(rs), imm16_21);
1129 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001130 case kUncond:
1131 LOG(FATAL) << "Unexpected branch condition " << cond;
1132 UNREACHABLE();
1133 }
jeffhao7fbee072012-08-24 17:56:54 -07001134}
1135
1136void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001137 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x0), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001138}
1139
1140void MipsAssembler::SubS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001141 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001142}
1143
1144void MipsAssembler::MulS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001145 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x2), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001146}
1147
1148void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001149 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x3), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001150}
1151
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001152void MipsAssembler::AddD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001153 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x0), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001154}
1155
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001156void MipsAssembler::SubD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001157 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001158}
1159
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001160void MipsAssembler::MulD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001161 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x2), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001162}
1163
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001164void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001165 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x3), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001166}
1167
Chris Larsenb74353a2015-11-20 09:07:09 -08001168void MipsAssembler::SqrtS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001169 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x4), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001170}
1171
1172void MipsAssembler::SqrtD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001173 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x4), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001174}
1175
1176void MipsAssembler::AbsS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001177 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x5), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001178}
1179
1180void MipsAssembler::AbsD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001181 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x5), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001182}
1183
jeffhao7fbee072012-08-24 17:56:54 -07001184void MipsAssembler::MovS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001185 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6), fd, fs, fs);
jeffhao7fbee072012-08-24 17:56:54 -07001186}
1187
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001188void MipsAssembler::MovD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001189 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x6), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001190}
1191
1192void MipsAssembler::NegS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001193 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x7), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001194}
1195
1196void MipsAssembler::NegD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001197 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001198}
1199
Chris Larsenb74353a2015-11-20 09:07:09 -08001200void MipsAssembler::CunS(FRegister fs, FRegister ft) {
1201 CunS(0, fs, ft);
1202}
1203
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001204void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) {
1205 CHECK(!IsR6());
1206 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001207 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x31), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001208}
1209
Chris Larsenb74353a2015-11-20 09:07:09 -08001210void MipsAssembler::CeqS(FRegister fs, FRegister ft) {
1211 CeqS(0, fs, ft);
1212}
1213
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001214void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) {
1215 CHECK(!IsR6());
1216 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001217 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x32), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001218}
1219
Chris Larsenb74353a2015-11-20 09:07:09 -08001220void MipsAssembler::CueqS(FRegister fs, FRegister ft) {
1221 CueqS(0, fs, ft);
1222}
1223
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001224void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) {
1225 CHECK(!IsR6());
1226 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001227 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x33), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001228}
1229
Chris Larsenb74353a2015-11-20 09:07:09 -08001230void MipsAssembler::ColtS(FRegister fs, FRegister ft) {
1231 ColtS(0, fs, ft);
1232}
1233
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001234void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) {
1235 CHECK(!IsR6());
1236 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001237 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x34), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001238}
1239
Chris Larsenb74353a2015-11-20 09:07:09 -08001240void MipsAssembler::CultS(FRegister fs, FRegister ft) {
1241 CultS(0, fs, ft);
1242}
1243
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001244void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) {
1245 CHECK(!IsR6());
1246 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001247 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x35), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001248}
1249
Chris Larsenb74353a2015-11-20 09:07:09 -08001250void MipsAssembler::ColeS(FRegister fs, FRegister ft) {
1251 ColeS(0, fs, ft);
1252}
1253
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001254void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) {
1255 CHECK(!IsR6());
1256 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001257 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x36), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001258}
1259
Chris Larsenb74353a2015-11-20 09:07:09 -08001260void MipsAssembler::CuleS(FRegister fs, FRegister ft) {
1261 CuleS(0, fs, ft);
1262}
1263
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001264void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) {
1265 CHECK(!IsR6());
1266 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001267 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x37), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001268}
1269
Chris Larsenb74353a2015-11-20 09:07:09 -08001270void MipsAssembler::CunD(FRegister fs, FRegister ft) {
1271 CunD(0, fs, ft);
1272}
1273
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001274void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) {
1275 CHECK(!IsR6());
1276 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001277 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x31), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001278}
1279
Chris Larsenb74353a2015-11-20 09:07:09 -08001280void MipsAssembler::CeqD(FRegister fs, FRegister ft) {
1281 CeqD(0, fs, ft);
1282}
1283
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001284void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) {
1285 CHECK(!IsR6());
1286 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001287 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x32), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001288}
1289
Chris Larsenb74353a2015-11-20 09:07:09 -08001290void MipsAssembler::CueqD(FRegister fs, FRegister ft) {
1291 CueqD(0, fs, ft);
1292}
1293
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001294void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) {
1295 CHECK(!IsR6());
1296 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001297 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x33), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001298}
1299
Chris Larsenb74353a2015-11-20 09:07:09 -08001300void MipsAssembler::ColtD(FRegister fs, FRegister ft) {
1301 ColtD(0, fs, ft);
1302}
1303
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001304void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) {
1305 CHECK(!IsR6());
1306 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001307 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x34), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001308}
1309
Chris Larsenb74353a2015-11-20 09:07:09 -08001310void MipsAssembler::CultD(FRegister fs, FRegister ft) {
1311 CultD(0, fs, ft);
1312}
1313
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001314void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) {
1315 CHECK(!IsR6());
1316 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001317 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x35), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001318}
1319
Chris Larsenb74353a2015-11-20 09:07:09 -08001320void MipsAssembler::ColeD(FRegister fs, FRegister ft) {
1321 ColeD(0, fs, ft);
1322}
1323
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001324void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) {
1325 CHECK(!IsR6());
1326 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001327 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x36), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001328}
1329
Chris Larsenb74353a2015-11-20 09:07:09 -08001330void MipsAssembler::CuleD(FRegister fs, FRegister ft) {
1331 CuleD(0, fs, ft);
1332}
1333
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001334void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) {
1335 CHECK(!IsR6());
1336 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001337 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x37), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001338}
1339
1340void MipsAssembler::CmpUnS(FRegister fd, FRegister fs, FRegister ft) {
1341 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001342 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x01), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001343}
1344
1345void MipsAssembler::CmpEqS(FRegister fd, FRegister fs, FRegister ft) {
1346 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001347 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x02), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001348}
1349
1350void MipsAssembler::CmpUeqS(FRegister fd, FRegister fs, FRegister ft) {
1351 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001352 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x03), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001353}
1354
1355void MipsAssembler::CmpLtS(FRegister fd, FRegister fs, FRegister ft) {
1356 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001357 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x04), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001358}
1359
1360void MipsAssembler::CmpUltS(FRegister fd, FRegister fs, FRegister ft) {
1361 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001362 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x05), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001363}
1364
1365void MipsAssembler::CmpLeS(FRegister fd, FRegister fs, FRegister ft) {
1366 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001367 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x06), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001368}
1369
1370void MipsAssembler::CmpUleS(FRegister fd, FRegister fs, FRegister ft) {
1371 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001372 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x07), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001373}
1374
1375void MipsAssembler::CmpOrS(FRegister fd, FRegister fs, FRegister ft) {
1376 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001377 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x11), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001378}
1379
1380void MipsAssembler::CmpUneS(FRegister fd, FRegister fs, FRegister ft) {
1381 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001382 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x12), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001383}
1384
1385void MipsAssembler::CmpNeS(FRegister fd, FRegister fs, FRegister ft) {
1386 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001387 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x13), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001388}
1389
1390void MipsAssembler::CmpUnD(FRegister fd, FRegister fs, FRegister ft) {
1391 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001392 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x01), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001393}
1394
1395void MipsAssembler::CmpEqD(FRegister fd, FRegister fs, FRegister ft) {
1396 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001397 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x02), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001398}
1399
1400void MipsAssembler::CmpUeqD(FRegister fd, FRegister fs, FRegister ft) {
1401 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001402 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x03), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001403}
1404
1405void MipsAssembler::CmpLtD(FRegister fd, FRegister fs, FRegister ft) {
1406 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001407 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x04), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001408}
1409
1410void MipsAssembler::CmpUltD(FRegister fd, FRegister fs, FRegister ft) {
1411 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001412 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x05), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001413}
1414
1415void MipsAssembler::CmpLeD(FRegister fd, FRegister fs, FRegister ft) {
1416 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001417 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x06), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001418}
1419
1420void MipsAssembler::CmpUleD(FRegister fd, FRegister fs, FRegister ft) {
1421 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001422 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x07), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001423}
1424
1425void MipsAssembler::CmpOrD(FRegister fd, FRegister fs, FRegister ft) {
1426 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001427 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x11), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001428}
1429
1430void MipsAssembler::CmpUneD(FRegister fd, FRegister fs, FRegister ft) {
1431 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001432 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x12), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001433}
1434
1435void MipsAssembler::CmpNeD(FRegister fd, FRegister fs, FRegister ft) {
1436 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001437 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x13), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001438}
1439
1440void MipsAssembler::Movf(Register rd, Register rs, int cc) {
1441 CHECK(!IsR6());
1442 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001443 DsFsmInstrRrrc(EmitR(0, rs, static_cast<Register>(cc << 2), rd, 0, 0x01), rd, rs, cc);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001444}
1445
1446void MipsAssembler::Movt(Register rd, Register rs, int cc) {
1447 CHECK(!IsR6());
1448 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001449 DsFsmInstrRrrc(EmitR(0, rs, static_cast<Register>((cc << 2) | 1), rd, 0, 0x01), rd, rs, cc);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001450}
1451
Chris Larsenb74353a2015-11-20 09:07:09 -08001452void MipsAssembler::MovfS(FRegister fd, FRegister fs, int cc) {
1453 CHECK(!IsR6());
1454 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001455 DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast<FRegister>(cc << 2), fs, fd, 0x11), fd, fs, cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001456}
1457
1458void MipsAssembler::MovfD(FRegister fd, FRegister fs, int cc) {
1459 CHECK(!IsR6());
1460 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001461 DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast<FRegister>(cc << 2), fs, fd, 0x11), fd, fs, cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001462}
1463
1464void MipsAssembler::MovtS(FRegister fd, FRegister fs, int cc) {
1465 CHECK(!IsR6());
1466 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001467 DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11),
1468 fd,
1469 fs,
1470 cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001471}
1472
1473void MipsAssembler::MovtD(FRegister fd, FRegister fs, int cc) {
1474 CHECK(!IsR6());
1475 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001476 DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11),
1477 fd,
1478 fs,
1479 cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001480}
1481
Alexey Frunze674b9ee2016-09-20 14:54:15 -07001482void MipsAssembler::MovzS(FRegister fd, FRegister fs, Register rt) {
1483 CHECK(!IsR6());
1484 DsFsmInstrFffr(EmitFR(0x11, 0x10, static_cast<FRegister>(rt), fs, fd, 0x12), fd, fs, rt);
1485}
1486
1487void MipsAssembler::MovzD(FRegister fd, FRegister fs, Register rt) {
1488 CHECK(!IsR6());
1489 DsFsmInstrFffr(EmitFR(0x11, 0x11, static_cast<FRegister>(rt), fs, fd, 0x12), fd, fs, rt);
1490}
1491
1492void MipsAssembler::MovnS(FRegister fd, FRegister fs, Register rt) {
1493 CHECK(!IsR6());
1494 DsFsmInstrFffr(EmitFR(0x11, 0x10, static_cast<FRegister>(rt), fs, fd, 0x13), fd, fs, rt);
1495}
1496
1497void MipsAssembler::MovnD(FRegister fd, FRegister fs, Register rt) {
1498 CHECK(!IsR6());
1499 DsFsmInstrFffr(EmitFR(0x11, 0x11, static_cast<FRegister>(rt), fs, fd, 0x13), fd, fs, rt);
1500}
1501
Chris Larsenb74353a2015-11-20 09:07:09 -08001502void MipsAssembler::SelS(FRegister fd, FRegister fs, FRegister ft) {
1503 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001504 DsFsmInstrFfff(EmitFR(0x11, 0x10, ft, fs, fd, 0x10), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001505}
1506
1507void MipsAssembler::SelD(FRegister fd, FRegister fs, FRegister ft) {
1508 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001509 DsFsmInstrFfff(EmitFR(0x11, 0x11, ft, fs, fd, 0x10), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001510}
1511
Alexey Frunze674b9ee2016-09-20 14:54:15 -07001512void MipsAssembler::SeleqzS(FRegister fd, FRegister fs, FRegister ft) {
1513 CHECK(IsR6());
1514 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x14), fd, fs, ft);
1515}
1516
1517void MipsAssembler::SeleqzD(FRegister fd, FRegister fs, FRegister ft) {
1518 CHECK(IsR6());
1519 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x14), fd, fs, ft);
1520}
1521
1522void MipsAssembler::SelnezS(FRegister fd, FRegister fs, FRegister ft) {
1523 CHECK(IsR6());
1524 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x17), fd, fs, ft);
1525}
1526
1527void MipsAssembler::SelnezD(FRegister fd, FRegister fs, FRegister ft) {
1528 CHECK(IsR6());
1529 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x17), fd, fs, ft);
1530}
1531
Chris Larsenb74353a2015-11-20 09:07:09 -08001532void MipsAssembler::ClassS(FRegister fd, FRegister fs) {
1533 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001534 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x1b), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001535}
1536
1537void MipsAssembler::ClassD(FRegister fd, FRegister fs) {
1538 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001539 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x1b), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001540}
1541
1542void MipsAssembler::MinS(FRegister fd, FRegister fs, FRegister ft) {
1543 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001544 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1c), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001545}
1546
1547void MipsAssembler::MinD(FRegister fd, FRegister fs, FRegister ft) {
1548 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001549 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1c), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001550}
1551
1552void MipsAssembler::MaxS(FRegister fd, FRegister fs, FRegister ft) {
1553 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001554 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1e), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001555}
1556
1557void MipsAssembler::MaxD(FRegister fd, FRegister fs, FRegister ft) {
1558 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001559 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1e), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001560}
1561
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001562void MipsAssembler::TruncLS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001563 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x09), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001564}
1565
1566void MipsAssembler::TruncLD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001567 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x09), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001568}
1569
1570void MipsAssembler::TruncWS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001571 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x0D), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001572}
1573
1574void MipsAssembler::TruncWD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001575 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x0D), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001576}
1577
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001578void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001579 DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001580}
1581
1582void MipsAssembler::Cvtdw(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001583 DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001584}
1585
1586void MipsAssembler::Cvtsd(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001587 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001588}
1589
1590void MipsAssembler::Cvtds(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001591 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
jeffhao7fbee072012-08-24 17:56:54 -07001592}
1593
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001594void MipsAssembler::Cvtsl(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001595 DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001596}
1597
1598void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001599 DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001600}
1601
Chris Larsenb74353a2015-11-20 09:07:09 -08001602void MipsAssembler::FloorWS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001603 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0xf), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001604}
1605
1606void MipsAssembler::FloorWD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001607 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0xf), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001608}
1609
jeffhao7fbee072012-08-24 17:56:54 -07001610void MipsAssembler::Mfc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001611 DsFsmInstrRf(EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1612 rt,
1613 fs);
jeffhao7fbee072012-08-24 17:56:54 -07001614}
1615
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001616void MipsAssembler::Mtc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001617 DsFsmInstrFr(EmitFR(0x11, 0x04, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1618 fs,
1619 rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001620}
1621
1622void MipsAssembler::Mfhc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001623 DsFsmInstrRf(EmitFR(0x11, 0x03, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1624 rt,
1625 fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001626}
1627
1628void MipsAssembler::Mthc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001629 DsFsmInstrFr(EmitFR(0x11, 0x07, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1630 fs,
1631 rt);
jeffhao7fbee072012-08-24 17:56:54 -07001632}
1633
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001634void MipsAssembler::MoveFromFpuHigh(Register rt, FRegister fs) {
1635 if (Is32BitFPU()) {
1636 CHECK_EQ(fs % 2, 0) << fs;
1637 Mfc1(rt, static_cast<FRegister>(fs + 1));
1638 } else {
1639 Mfhc1(rt, fs);
1640 }
1641}
1642
1643void MipsAssembler::MoveToFpuHigh(Register rt, FRegister fs) {
1644 if (Is32BitFPU()) {
1645 CHECK_EQ(fs % 2, 0) << fs;
1646 Mtc1(rt, static_cast<FRegister>(fs + 1));
1647 } else {
1648 Mthc1(rt, fs);
1649 }
1650}
1651
jeffhao7fbee072012-08-24 17:56:54 -07001652void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001653 DsFsmInstrFr(EmitI(0x31, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001654}
1655
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001656void MipsAssembler::Ldc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001657 DsFsmInstrFr(EmitI(0x35, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001658}
1659
1660void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001661 DsFsmInstrFR(EmitI(0x39, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001662}
1663
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001664void MipsAssembler::Sdc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001665 DsFsmInstrFR(EmitI(0x3d, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001666}
1667
1668void MipsAssembler::Break() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001669 DsFsmInstrNop(EmitR(0, ZERO, ZERO, ZERO, 0, 0xD));
jeffhao7fbee072012-08-24 17:56:54 -07001670}
1671
jeffhao07030602012-09-26 14:33:14 -07001672void MipsAssembler::Nop() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001673 DsFsmInstrNop(EmitR(0x0, ZERO, ZERO, ZERO, 0, 0x0));
1674}
1675
1676void MipsAssembler::NopIfNoReordering() {
1677 if (!reordering_) {
1678 Nop();
1679 }
jeffhao07030602012-09-26 14:33:14 -07001680}
1681
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001682void MipsAssembler::Move(Register rd, Register rs) {
1683 Or(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001684}
1685
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001686void MipsAssembler::Clear(Register rd) {
1687 Move(rd, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001688}
1689
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001690void MipsAssembler::Not(Register rd, Register rs) {
1691 Nor(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001692}
1693
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001694void MipsAssembler::Push(Register rs) {
1695 IncreaseFrameSize(kMipsWordSize);
1696 Sw(rs, SP, 0);
jeffhao7fbee072012-08-24 17:56:54 -07001697}
1698
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001699void MipsAssembler::Pop(Register rd) {
1700 Lw(rd, SP, 0);
1701 DecreaseFrameSize(kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07001702}
1703
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001704void MipsAssembler::PopAndReturn(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001705 bool reordering = SetReorder(false);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001706 Lw(rd, SP, 0);
1707 Jr(rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001708 DecreaseFrameSize(kMipsWordSize); // Single instruction in delay slot.
1709 SetReorder(reordering);
jeffhao7fbee072012-08-24 17:56:54 -07001710}
1711
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001712void MipsAssembler::LoadConst32(Register rd, int32_t value) {
1713 if (IsUint<16>(value)) {
1714 // Use OR with (unsigned) immediate to encode 16b unsigned int.
1715 Ori(rd, ZERO, value);
1716 } else if (IsInt<16>(value)) {
1717 // Use ADD with (signed) immediate to encode 16b signed int.
1718 Addiu(rd, ZERO, value);
jeffhao7fbee072012-08-24 17:56:54 -07001719 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001720 Lui(rd, High16Bits(value));
1721 if (value & 0xFFFF)
1722 Ori(rd, rd, Low16Bits(value));
1723 }
1724}
1725
1726void MipsAssembler::LoadConst64(Register reg_hi, Register reg_lo, int64_t value) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001727 uint32_t low = Low32Bits(value);
1728 uint32_t high = High32Bits(value);
1729 LoadConst32(reg_lo, low);
1730 if (high != low) {
1731 LoadConst32(reg_hi, high);
1732 } else {
1733 Move(reg_hi, reg_lo);
1734 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001735}
1736
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001737void MipsAssembler::LoadSConst32(FRegister r, int32_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001738 if (value == 0) {
1739 temp = ZERO;
1740 } else {
1741 LoadConst32(temp, value);
1742 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001743 Mtc1(temp, r);
1744}
1745
1746void MipsAssembler::LoadDConst64(FRegister rd, int64_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001747 uint32_t low = Low32Bits(value);
1748 uint32_t high = High32Bits(value);
1749 if (low == 0) {
1750 Mtc1(ZERO, rd);
1751 } else {
1752 LoadConst32(temp, low);
1753 Mtc1(temp, rd);
1754 }
1755 if (high == 0) {
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001756 MoveToFpuHigh(ZERO, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001757 } else {
1758 LoadConst32(temp, high);
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001759 MoveToFpuHigh(temp, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001760 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001761}
1762
1763void MipsAssembler::Addiu32(Register rt, Register rs, int32_t value, Register temp) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001764 CHECK_NE(rs, temp); // Must not overwrite the register `rs` while loading `value`.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001765 if (IsInt<16>(value)) {
1766 Addiu(rt, rs, value);
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001767 } else if (IsR6()) {
1768 int16_t high = High16Bits(value);
1769 int16_t low = Low16Bits(value);
1770 high += (low < 0) ? 1 : 0; // Account for sign extension in addiu.
1771 if (low != 0) {
1772 Aui(temp, rs, high);
1773 Addiu(rt, temp, low);
1774 } else {
1775 Aui(rt, rs, high);
1776 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001777 } else {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001778 // Do not load the whole 32-bit `value` if it can be represented as
1779 // a sum of two 16-bit signed values. This can save an instruction.
1780 constexpr int32_t kMinValueForSimpleAdjustment = std::numeric_limits<int16_t>::min() * 2;
1781 constexpr int32_t kMaxValueForSimpleAdjustment = std::numeric_limits<int16_t>::max() * 2;
1782 if (0 <= value && value <= kMaxValueForSimpleAdjustment) {
1783 Addiu(temp, rs, kMaxValueForSimpleAdjustment / 2);
1784 Addiu(rt, temp, value - kMaxValueForSimpleAdjustment / 2);
1785 } else if (kMinValueForSimpleAdjustment <= value && value < 0) {
1786 Addiu(temp, rs, kMinValueForSimpleAdjustment / 2);
1787 Addiu(rt, temp, value - kMinValueForSimpleAdjustment / 2);
1788 } else {
1789 // Now that all shorter options have been exhausted, load the full 32-bit value.
1790 LoadConst32(temp, value);
1791 Addu(rt, rs, temp);
1792 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001793 }
1794}
1795
1796void MipsAssembler::Branch::InitShortOrLong(MipsAssembler::Branch::OffsetBits offset_size,
1797 MipsAssembler::Branch::Type short_type,
1798 MipsAssembler::Branch::Type long_type) {
1799 type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type;
1800}
1801
Alexey Frunze96b66822016-09-10 02:32:44 -07001802void MipsAssembler::Branch::InitializeType(Type initial_type, bool is_r6) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001803 OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_);
1804 if (is_r6) {
1805 // R6
Alexey Frunze96b66822016-09-10 02:32:44 -07001806 switch (initial_type) {
1807 case kLabel:
1808 CHECK(!IsResolved());
1809 type_ = kR6Label;
1810 break;
1811 case kLiteral:
1812 CHECK(!IsResolved());
1813 type_ = kR6Literal;
1814 break;
1815 case kCall:
1816 InitShortOrLong(offset_size, kR6Call, kR6LongCall);
1817 break;
1818 case kCondBranch:
1819 switch (condition_) {
1820 case kUncond:
1821 InitShortOrLong(offset_size, kR6UncondBranch, kR6LongUncondBranch);
1822 break;
1823 case kCondEQZ:
1824 case kCondNEZ:
1825 // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions.
1826 type_ = (offset_size <= kOffset23) ? kR6CondBranch : kR6LongCondBranch;
1827 break;
1828 default:
1829 InitShortOrLong(offset_size, kR6CondBranch, kR6LongCondBranch);
1830 break;
1831 }
1832 break;
1833 default:
1834 LOG(FATAL) << "Unexpected branch type " << initial_type;
1835 UNREACHABLE();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001836 }
1837 } else {
1838 // R2
Alexey Frunze96b66822016-09-10 02:32:44 -07001839 switch (initial_type) {
1840 case kLabel:
1841 CHECK(!IsResolved());
1842 type_ = kLabel;
1843 break;
1844 case kLiteral:
1845 CHECK(!IsResolved());
1846 type_ = kLiteral;
1847 break;
1848 case kCall:
1849 InitShortOrLong(offset_size, kCall, kLongCall);
1850 break;
1851 case kCondBranch:
1852 switch (condition_) {
1853 case kUncond:
1854 InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch);
1855 break;
1856 default:
1857 InitShortOrLong(offset_size, kCondBranch, kLongCondBranch);
1858 break;
1859 }
1860 break;
1861 default:
1862 LOG(FATAL) << "Unexpected branch type " << initial_type;
1863 UNREACHABLE();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001864 }
1865 }
1866 old_type_ = type_;
1867}
1868
1869bool MipsAssembler::Branch::IsNop(BranchCondition condition, Register lhs, Register rhs) {
1870 switch (condition) {
1871 case kCondLT:
1872 case kCondGT:
1873 case kCondNE:
1874 case kCondLTU:
1875 return lhs == rhs;
1876 default:
1877 return false;
1878 }
1879}
1880
1881bool MipsAssembler::Branch::IsUncond(BranchCondition condition, Register lhs, Register rhs) {
1882 switch (condition) {
1883 case kUncond:
1884 return true;
1885 case kCondGE:
1886 case kCondLE:
1887 case kCondEQ:
1888 case kCondGEU:
1889 return lhs == rhs;
1890 default:
1891 return false;
1892 }
1893}
1894
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001895MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target, bool is_call)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001896 : old_location_(location),
1897 location_(location),
1898 target_(target),
1899 lhs_reg_(0),
1900 rhs_reg_(0),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001901 condition_(kUncond),
1902 delayed_instruction_(kUnfilledDelaySlot) {
Alexey Frunze96b66822016-09-10 02:32:44 -07001903 InitializeType((is_call ? kCall : kCondBranch), is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001904}
1905
1906MipsAssembler::Branch::Branch(bool is_r6,
1907 uint32_t location,
1908 uint32_t target,
1909 MipsAssembler::BranchCondition condition,
1910 Register lhs_reg,
1911 Register rhs_reg)
1912 : old_location_(location),
1913 location_(location),
1914 target_(target),
1915 lhs_reg_(lhs_reg),
1916 rhs_reg_(rhs_reg),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001917 condition_(condition),
1918 delayed_instruction_(kUnfilledDelaySlot) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001919 CHECK_NE(condition, kUncond);
1920 switch (condition) {
1921 case kCondLT:
1922 case kCondGE:
1923 case kCondLE:
1924 case kCondGT:
1925 case kCondLTU:
1926 case kCondGEU:
1927 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
1928 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
1929 // We leave this up to the caller.
1930 CHECK(is_r6);
1931 FALLTHROUGH_INTENDED;
1932 case kCondEQ:
1933 case kCondNE:
1934 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1935 // To compare with 0, use dedicated kCond*Z conditions.
1936 CHECK_NE(lhs_reg, ZERO);
1937 CHECK_NE(rhs_reg, ZERO);
1938 break;
1939 case kCondLTZ:
1940 case kCondGEZ:
1941 case kCondLEZ:
1942 case kCondGTZ:
1943 case kCondEQZ:
1944 case kCondNEZ:
1945 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1946 CHECK_NE(lhs_reg, ZERO);
1947 CHECK_EQ(rhs_reg, ZERO);
1948 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001949 case kCondF:
1950 case kCondT:
1951 CHECK_EQ(rhs_reg, ZERO);
1952 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001953 case kUncond:
1954 UNREACHABLE();
1955 }
1956 CHECK(!IsNop(condition, lhs_reg, rhs_reg));
1957 if (IsUncond(condition, lhs_reg, rhs_reg)) {
1958 // Branch condition is always true, make the branch unconditional.
1959 condition_ = kUncond;
1960 }
Alexey Frunze96b66822016-09-10 02:32:44 -07001961 InitializeType(kCondBranch, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001962}
1963
Alexey Frunze96b66822016-09-10 02:32:44 -07001964MipsAssembler::Branch::Branch(bool is_r6,
1965 uint32_t location,
1966 Register dest_reg,
1967 Register base_reg,
1968 Type label_or_literal_type)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001969 : old_location_(location),
1970 location_(location),
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001971 target_(kUnresolved),
1972 lhs_reg_(dest_reg),
1973 rhs_reg_(base_reg),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001974 condition_(kUncond),
1975 delayed_instruction_(kUnfilledDelaySlot) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001976 CHECK_NE(dest_reg, ZERO);
1977 if (is_r6) {
1978 CHECK_EQ(base_reg, ZERO);
1979 } else {
1980 CHECK_NE(base_reg, ZERO);
1981 }
Alexey Frunze96b66822016-09-10 02:32:44 -07001982 InitializeType(label_or_literal_type, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001983}
1984
1985MipsAssembler::BranchCondition MipsAssembler::Branch::OppositeCondition(
1986 MipsAssembler::BranchCondition cond) {
1987 switch (cond) {
1988 case kCondLT:
1989 return kCondGE;
1990 case kCondGE:
1991 return kCondLT;
1992 case kCondLE:
1993 return kCondGT;
1994 case kCondGT:
1995 return kCondLE;
1996 case kCondLTZ:
1997 return kCondGEZ;
1998 case kCondGEZ:
1999 return kCondLTZ;
2000 case kCondLEZ:
2001 return kCondGTZ;
2002 case kCondGTZ:
2003 return kCondLEZ;
2004 case kCondEQ:
2005 return kCondNE;
2006 case kCondNE:
2007 return kCondEQ;
2008 case kCondEQZ:
2009 return kCondNEZ;
2010 case kCondNEZ:
2011 return kCondEQZ;
2012 case kCondLTU:
2013 return kCondGEU;
2014 case kCondGEU:
2015 return kCondLTU;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002016 case kCondF:
2017 return kCondT;
2018 case kCondT:
2019 return kCondF;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002020 case kUncond:
2021 LOG(FATAL) << "Unexpected branch condition " << cond;
2022 }
2023 UNREACHABLE();
2024}
2025
2026MipsAssembler::Branch::Type MipsAssembler::Branch::GetType() const {
2027 return type_;
2028}
2029
2030MipsAssembler::BranchCondition MipsAssembler::Branch::GetCondition() const {
2031 return condition_;
2032}
2033
2034Register MipsAssembler::Branch::GetLeftRegister() const {
2035 return static_cast<Register>(lhs_reg_);
2036}
2037
2038Register MipsAssembler::Branch::GetRightRegister() const {
2039 return static_cast<Register>(rhs_reg_);
2040}
2041
2042uint32_t MipsAssembler::Branch::GetTarget() const {
2043 return target_;
2044}
2045
2046uint32_t MipsAssembler::Branch::GetLocation() const {
2047 return location_;
2048}
2049
2050uint32_t MipsAssembler::Branch::GetOldLocation() const {
2051 return old_location_;
2052}
2053
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002054uint32_t MipsAssembler::Branch::GetPrecedingInstructionLength(Type type) const {
2055 // Short branches with delay slots always consist of two instructions, the branch
2056 // and the delay slot, irrespective of whether the delay slot is filled with a
2057 // useful instruction or not.
2058 // Long composite branches may have a length longer by one instruction than
2059 // specified in branch_info_[].length. This happens when an instruction is taken
2060 // to fill the short branch delay slot, but the branch eventually becomes long
2061 // and formally has no delay slot to fill. This instruction is placed at the
2062 // beginning of the long composite branch and this needs to be accounted for in
2063 // the branch length and the location of the offset encoded in the branch.
2064 switch (type) {
2065 case kLongUncondBranch:
2066 case kLongCondBranch:
2067 case kLongCall:
2068 case kR6LongCondBranch:
2069 return (delayed_instruction_ != kUnfilledDelaySlot &&
2070 delayed_instruction_ != kUnfillableDelaySlot) ? 1 : 0;
2071 default:
2072 return 0;
2073 }
2074}
2075
2076uint32_t MipsAssembler::Branch::GetPrecedingInstructionSize(Type type) const {
2077 return GetPrecedingInstructionLength(type) * sizeof(uint32_t);
2078}
2079
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002080uint32_t MipsAssembler::Branch::GetLength() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002081 return GetPrecedingInstructionLength(type_) + branch_info_[type_].length;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002082}
2083
2084uint32_t MipsAssembler::Branch::GetOldLength() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002085 return GetPrecedingInstructionLength(old_type_) + branch_info_[old_type_].length;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002086}
2087
2088uint32_t MipsAssembler::Branch::GetSize() const {
2089 return GetLength() * sizeof(uint32_t);
2090}
2091
2092uint32_t MipsAssembler::Branch::GetOldSize() const {
2093 return GetOldLength() * sizeof(uint32_t);
2094}
2095
2096uint32_t MipsAssembler::Branch::GetEndLocation() const {
2097 return GetLocation() + GetSize();
2098}
2099
2100uint32_t MipsAssembler::Branch::GetOldEndLocation() const {
2101 return GetOldLocation() + GetOldSize();
2102}
2103
2104bool MipsAssembler::Branch::IsLong() const {
2105 switch (type_) {
2106 // R2 short branches.
2107 case kUncondBranch:
2108 case kCondBranch:
2109 case kCall:
Alexey Frunze96b66822016-09-10 02:32:44 -07002110 // R2 near label.
2111 case kLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002112 // R2 near literal.
2113 case kLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002114 // R6 short branches.
2115 case kR6UncondBranch:
2116 case kR6CondBranch:
2117 case kR6Call:
Alexey Frunze96b66822016-09-10 02:32:44 -07002118 // R6 near label.
2119 case kR6Label:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002120 // R6 near literal.
2121 case kR6Literal:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002122 return false;
2123 // R2 long branches.
2124 case kLongUncondBranch:
2125 case kLongCondBranch:
2126 case kLongCall:
Alexey Frunze96b66822016-09-10 02:32:44 -07002127 // R2 far label.
2128 case kFarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002129 // R2 far literal.
2130 case kFarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002131 // R6 long branches.
2132 case kR6LongUncondBranch:
2133 case kR6LongCondBranch:
2134 case kR6LongCall:
Alexey Frunze96b66822016-09-10 02:32:44 -07002135 // R6 far label.
2136 case kR6FarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002137 // R6 far literal.
2138 case kR6FarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002139 return true;
2140 }
2141 UNREACHABLE();
2142}
2143
2144bool MipsAssembler::Branch::IsResolved() const {
2145 return target_ != kUnresolved;
2146}
2147
2148MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSize() const {
2149 OffsetBits offset_size =
2150 (type_ == kR6CondBranch && (condition_ == kCondEQZ || condition_ == kCondNEZ))
2151 ? kOffset23
2152 : branch_info_[type_].offset_size;
2153 return offset_size;
2154}
2155
2156MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSizeNeeded(uint32_t location,
2157 uint32_t target) {
2158 // For unresolved targets assume the shortest encoding
2159 // (later it will be made longer if needed).
2160 if (target == kUnresolved)
2161 return kOffset16;
2162 int64_t distance = static_cast<int64_t>(target) - location;
2163 // To simplify calculations in composite branches consisting of multiple instructions
2164 // bump up the distance by a value larger than the max byte size of a composite branch.
2165 distance += (distance >= 0) ? kMaxBranchSize : -kMaxBranchSize;
2166 if (IsInt<kOffset16>(distance))
2167 return kOffset16;
2168 else if (IsInt<kOffset18>(distance))
2169 return kOffset18;
2170 else if (IsInt<kOffset21>(distance))
2171 return kOffset21;
2172 else if (IsInt<kOffset23>(distance))
2173 return kOffset23;
2174 else if (IsInt<kOffset28>(distance))
2175 return kOffset28;
2176 return kOffset32;
2177}
2178
2179void MipsAssembler::Branch::Resolve(uint32_t target) {
2180 target_ = target;
2181}
2182
2183void MipsAssembler::Branch::Relocate(uint32_t expand_location, uint32_t delta) {
2184 if (location_ > expand_location) {
2185 location_ += delta;
2186 }
2187 if (!IsResolved()) {
2188 return; // Don't know the target yet.
2189 }
2190 if (target_ > expand_location) {
2191 target_ += delta;
2192 }
2193}
2194
2195void MipsAssembler::Branch::PromoteToLong() {
2196 switch (type_) {
2197 // R2 short branches.
2198 case kUncondBranch:
2199 type_ = kLongUncondBranch;
2200 break;
2201 case kCondBranch:
2202 type_ = kLongCondBranch;
2203 break;
2204 case kCall:
2205 type_ = kLongCall;
2206 break;
Alexey Frunze96b66822016-09-10 02:32:44 -07002207 // R2 near label.
2208 case kLabel:
2209 type_ = kFarLabel;
2210 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002211 // R2 near literal.
2212 case kLiteral:
2213 type_ = kFarLiteral;
2214 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002215 // R6 short branches.
2216 case kR6UncondBranch:
2217 type_ = kR6LongUncondBranch;
2218 break;
2219 case kR6CondBranch:
2220 type_ = kR6LongCondBranch;
2221 break;
2222 case kR6Call:
2223 type_ = kR6LongCall;
2224 break;
Alexey Frunze96b66822016-09-10 02:32:44 -07002225 // R6 near label.
2226 case kR6Label:
2227 type_ = kR6FarLabel;
2228 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002229 // R6 near literal.
2230 case kR6Literal:
2231 type_ = kR6FarLiteral;
2232 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002233 default:
2234 // Note: 'type_' is already long.
2235 break;
2236 }
2237 CHECK(IsLong());
2238}
2239
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002240uint32_t MipsAssembler::GetBranchLocationOrPcRelBase(const MipsAssembler::Branch* branch) const {
2241 switch (branch->GetType()) {
Alexey Frunze96b66822016-09-10 02:32:44 -07002242 case Branch::kLabel:
2243 case Branch::kFarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002244 case Branch::kLiteral:
2245 case Branch::kFarLiteral:
2246 return GetLabelLocation(&pc_rel_base_label_);
2247 default:
2248 return branch->GetLocation();
2249 }
2250}
2251
2252uint32_t MipsAssembler::Branch::PromoteIfNeeded(uint32_t location, uint32_t max_short_distance) {
Alexey Frunze96b66822016-09-10 02:32:44 -07002253 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 labels/literals or
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002254 // `this->GetLocation()` for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002255 // If the branch is still unresolved or already long, nothing to do.
2256 if (IsLong() || !IsResolved()) {
2257 return 0;
2258 }
2259 // Promote the short branch to long if the offset size is too small
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002260 // to hold the distance between location and target_.
2261 if (GetOffsetSizeNeeded(location, target_) > GetOffsetSize()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002262 PromoteToLong();
2263 uint32_t old_size = GetOldSize();
2264 uint32_t new_size = GetSize();
2265 CHECK_GT(new_size, old_size);
2266 return new_size - old_size;
2267 }
2268 // The following logic is for debugging/testing purposes.
2269 // Promote some short branches to long when it's not really required.
2270 if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max())) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002271 int64_t distance = static_cast<int64_t>(target_) - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002272 distance = (distance >= 0) ? distance : -distance;
2273 if (distance >= max_short_distance) {
2274 PromoteToLong();
2275 uint32_t old_size = GetOldSize();
2276 uint32_t new_size = GetSize();
2277 CHECK_GT(new_size, old_size);
2278 return new_size - old_size;
2279 }
2280 }
2281 return 0;
2282}
2283
2284uint32_t MipsAssembler::Branch::GetOffsetLocation() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002285 return location_ + GetPrecedingInstructionSize(type_) +
2286 branch_info_[type_].instr_offset * sizeof(uint32_t);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002287}
2288
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002289uint32_t MipsAssembler::GetBranchOrPcRelBaseForEncoding(const MipsAssembler::Branch* branch) const {
2290 switch (branch->GetType()) {
Alexey Frunze96b66822016-09-10 02:32:44 -07002291 case Branch::kLabel:
2292 case Branch::kFarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002293 case Branch::kLiteral:
2294 case Branch::kFarLiteral:
2295 return GetLabelLocation(&pc_rel_base_label_);
2296 default:
2297 return branch->GetOffsetLocation() +
2298 Branch::branch_info_[branch->GetType()].pc_org * sizeof(uint32_t);
2299 }
2300}
2301
2302uint32_t MipsAssembler::Branch::GetOffset(uint32_t location) const {
Alexey Frunze96b66822016-09-10 02:32:44 -07002303 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 labels/literals or
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002304 // `this->GetOffsetLocation() + branch_info_[this->GetType()].pc_org * sizeof(uint32_t)`
2305 // for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002306 CHECK(IsResolved());
2307 uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize());
2308 // Calculate the byte distance between instructions and also account for
2309 // different PC-relative origins.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002310 uint32_t offset = target_ - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002311 // Prepare the offset for encoding into the instruction(s).
2312 offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift;
2313 return offset;
2314}
2315
2316MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) {
2317 CHECK_LT(branch_id, branches_.size());
2318 return &branches_[branch_id];
2319}
2320
2321const MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) const {
2322 CHECK_LT(branch_id, branches_.size());
2323 return &branches_[branch_id];
2324}
2325
2326void MipsAssembler::Bind(MipsLabel* label) {
2327 CHECK(!label->IsBound());
2328 uint32_t bound_pc = buffer_.Size();
2329
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002330 // Make the delay slot FSM aware of the new label.
2331 DsFsmLabel();
2332
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002333 // Walk the list of branches referring to and preceding this label.
2334 // Store the previously unknown target addresses in them.
2335 while (label->IsLinked()) {
2336 uint32_t branch_id = label->Position();
2337 Branch* branch = GetBranch(branch_id);
2338 branch->Resolve(bound_pc);
2339
2340 uint32_t branch_location = branch->GetLocation();
2341 // Extract the location of the previous branch in the list (walking the list backwards;
2342 // the previous branch ID was stored in the space reserved for this branch).
2343 uint32_t prev = buffer_.Load<uint32_t>(branch_location);
2344
2345 // On to the previous branch in the list...
2346 label->position_ = prev;
2347 }
2348
2349 // Now make the label object contain its own location (relative to the end of the preceding
2350 // branch, if any; it will be used by the branches referring to and following this label).
2351 label->prev_branch_id_plus_one_ = branches_.size();
2352 if (label->prev_branch_id_plus_one_) {
2353 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
2354 const Branch* branch = GetBranch(branch_id);
2355 bound_pc -= branch->GetEndLocation();
2356 }
2357 label->BindTo(bound_pc);
2358}
2359
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002360uint32_t MipsAssembler::GetLabelLocation(const MipsLabel* label) const {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002361 CHECK(label->IsBound());
2362 uint32_t target = label->Position();
2363 if (label->prev_branch_id_plus_one_) {
2364 // Get label location based on the branch preceding it.
2365 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
2366 const Branch* branch = GetBranch(branch_id);
2367 target += branch->GetEndLocation();
2368 }
2369 return target;
2370}
2371
2372uint32_t MipsAssembler::GetAdjustedPosition(uint32_t old_position) {
2373 // We can reconstruct the adjustment by going through all the branches from the beginning
2374 // up to the old_position. Since we expect AdjustedPosition() to be called in a loop
2375 // with increasing old_position, we can use the data from last AdjustedPosition() to
2376 // continue where we left off and the whole loop should be O(m+n) where m is the number
2377 // of positions to adjust and n is the number of branches.
2378 if (old_position < last_old_position_) {
2379 last_position_adjustment_ = 0;
2380 last_old_position_ = 0;
2381 last_branch_id_ = 0;
2382 }
2383 while (last_branch_id_ != branches_.size()) {
2384 const Branch* branch = GetBranch(last_branch_id_);
2385 if (branch->GetLocation() >= old_position + last_position_adjustment_) {
2386 break;
2387 }
2388 last_position_adjustment_ += branch->GetSize() - branch->GetOldSize();
2389 ++last_branch_id_;
2390 }
2391 last_old_position_ = old_position;
2392 return old_position + last_position_adjustment_;
2393}
2394
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002395void MipsAssembler::BindPcRelBaseLabel() {
2396 Bind(&pc_rel_base_label_);
2397}
2398
Alexey Frunze06a46c42016-07-19 15:00:40 -07002399uint32_t MipsAssembler::GetPcRelBaseLabelLocation() const {
2400 return GetLabelLocation(&pc_rel_base_label_);
2401}
2402
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002403void MipsAssembler::FinalizeLabeledBranch(MipsLabel* label) {
2404 uint32_t length = branches_.back().GetLength();
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002405 // Commit the last branch target label (if any).
2406 DsFsmCommitLabel();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002407 if (!label->IsBound()) {
2408 // Branch forward (to a following label), distance is unknown.
2409 // The first branch forward will contain 0, serving as the terminator of
2410 // the list of forward-reaching branches.
2411 Emit(label->position_);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002412 // Nothing for the delay slot (yet).
2413 DsFsmInstrNop(0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002414 length--;
2415 // Now make the label object point to this branch
2416 // (this forms a linked list of branches preceding this label).
2417 uint32_t branch_id = branches_.size() - 1;
2418 label->LinkTo(branch_id);
2419 }
2420 // Reserve space for the branch.
2421 while (length--) {
2422 Nop();
2423 }
2424}
2425
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002426bool MipsAssembler::Branch::CanHaveDelayedInstruction(const DelaySlot& delay_slot) const {
2427 if (delay_slot.instruction_ == 0) {
2428 // NOP or no instruction for the delay slot.
2429 return false;
2430 }
2431 switch (type_) {
2432 // R2 unconditional branches.
2433 case kUncondBranch:
2434 case kLongUncondBranch:
2435 // There are no register interdependencies.
2436 return true;
2437
2438 // R2 calls.
2439 case kCall:
2440 case kLongCall:
2441 // Instructions depending on or modifying RA should not be moved into delay slots
2442 // of branches modifying RA.
2443 return ((delay_slot.gpr_ins_mask_ | delay_slot.gpr_outs_mask_) & (1u << RA)) == 0;
2444
2445 // R2 conditional branches.
2446 case kCondBranch:
2447 case kLongCondBranch:
2448 switch (condition_) {
2449 // Branches with one GPR source.
2450 case kCondLTZ:
2451 case kCondGEZ:
2452 case kCondLEZ:
2453 case kCondGTZ:
2454 case kCondEQZ:
2455 case kCondNEZ:
2456 return (delay_slot.gpr_outs_mask_ & (1u << lhs_reg_)) == 0;
2457
2458 // Branches with two GPR sources.
2459 case kCondEQ:
2460 case kCondNE:
2461 return (delay_slot.gpr_outs_mask_ & ((1u << lhs_reg_) | (1u << rhs_reg_))) == 0;
2462
2463 // Branches with one FPU condition code source.
2464 case kCondF:
2465 case kCondT:
2466 return (delay_slot.cc_outs_mask_ & (1u << lhs_reg_)) == 0;
2467
2468 default:
2469 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
2470 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
2471 LOG(FATAL) << "Unexpected branch condition " << condition_;
2472 UNREACHABLE();
2473 }
2474
2475 // R6 unconditional branches.
2476 case kR6UncondBranch:
2477 case kR6LongUncondBranch:
2478 // R6 calls.
2479 case kR6Call:
2480 case kR6LongCall:
2481 // There are no delay slots.
2482 return false;
2483
2484 // R6 conditional branches.
2485 case kR6CondBranch:
2486 case kR6LongCondBranch:
2487 switch (condition_) {
2488 // Branches with one FPU register source.
2489 case kCondF:
2490 case kCondT:
2491 return (delay_slot.fpr_outs_mask_ & (1u << lhs_reg_)) == 0;
2492 // Others have a forbidden slot instead of a delay slot.
2493 default:
2494 return false;
2495 }
2496
2497 // Literals.
2498 default:
2499 LOG(FATAL) << "Unexpected branch type " << type_;
2500 UNREACHABLE();
2501 }
2502}
2503
2504uint32_t MipsAssembler::Branch::GetDelayedInstruction() const {
2505 return delayed_instruction_;
2506}
2507
2508void MipsAssembler::Branch::SetDelayedInstruction(uint32_t instruction) {
2509 CHECK_NE(instruction, kUnfilledDelaySlot);
2510 CHECK_EQ(delayed_instruction_, kUnfilledDelaySlot);
2511 delayed_instruction_ = instruction;
2512}
2513
2514void MipsAssembler::Branch::DecrementLocations() {
2515 // We first create a branch object, which gets its type and locations initialized,
2516 // and then we check if the branch can actually have the preceding instruction moved
2517 // into its delay slot. If it can, the branch locations need to be decremented.
2518 //
2519 // We could make the check before creating the branch object and avoid the location
2520 // adjustment, but the check is cleaner when performed on an initialized branch
2521 // object.
2522 //
2523 // If the branch is backwards (to a previously bound label), reducing the locations
2524 // cannot cause a short branch to exceed its offset range because the offset reduces.
2525 // And this is not at all a problem for a long branch backwards.
2526 //
2527 // If the branch is forward (not linked to any label yet), reducing the locations
2528 // is harmless. The branch will be promoted to long if needed when the target is known.
2529 CHECK_EQ(location_, old_location_);
2530 CHECK_GE(old_location_, sizeof(uint32_t));
2531 old_location_ -= sizeof(uint32_t);
2532 location_ = old_location_;
2533}
2534
2535void MipsAssembler::MoveInstructionToDelaySlot(Branch& branch) {
2536 if (branch.CanHaveDelayedInstruction(delay_slot_)) {
2537 // The last instruction cannot be used in a different delay slot,
2538 // do not commit the label before it (if any).
2539 DsFsmDropLabel();
2540 // Remove the last emitted instruction.
2541 size_t size = buffer_.Size();
2542 CHECK_GE(size, sizeof(uint32_t));
2543 size -= sizeof(uint32_t);
2544 CHECK_EQ(buffer_.Load<uint32_t>(size), delay_slot_.instruction_);
2545 buffer_.Resize(size);
2546 // Attach it to the branch and adjust the branch locations.
2547 branch.DecrementLocations();
2548 branch.SetDelayedInstruction(delay_slot_.instruction_);
2549 } else if (!reordering_ && branch.GetType() == Branch::kUncondBranch) {
2550 // If reordefing is disabled, prevent absorption of the target instruction.
2551 branch.SetDelayedInstruction(Branch::kUnfillableDelaySlot);
2552 }
2553}
2554
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002555void MipsAssembler::Buncond(MipsLabel* label) {
2556 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002557 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ false);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002558 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002559 FinalizeLabeledBranch(label);
2560}
2561
2562void MipsAssembler::Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs) {
2563 // If lhs = rhs, this can be a NOP.
2564 if (Branch::IsNop(condition, lhs, rhs)) {
2565 return;
2566 }
2567 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
2568 branches_.emplace_back(IsR6(), buffer_.Size(), target, condition, lhs, rhs);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002569 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002570 FinalizeLabeledBranch(label);
2571}
2572
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002573void MipsAssembler::Call(MipsLabel* label) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002574 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002575 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ true);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002576 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002577 FinalizeLabeledBranch(label);
2578}
2579
Alexey Frunze96b66822016-09-10 02:32:44 -07002580void MipsAssembler::LoadLabelAddress(Register dest_reg, Register base_reg, MipsLabel* label) {
2581 // Label address loads are treated as pseudo branches since they require very similar handling.
2582 DCHECK(!label->IsBound());
2583 branches_.emplace_back(IsR6(), buffer_.Size(), dest_reg, base_reg, Branch::kLabel);
2584 FinalizeLabeledBranch(label);
2585}
2586
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002587Literal* MipsAssembler::NewLiteral(size_t size, const uint8_t* data) {
2588 DCHECK(size == 4u || size == 8u) << size;
2589 literals_.emplace_back(size, data);
2590 return &literals_.back();
2591}
2592
2593void MipsAssembler::LoadLiteral(Register dest_reg, Register base_reg, Literal* literal) {
2594 // Literal loads are treated as pseudo branches since they require very similar handling.
2595 DCHECK_EQ(literal->GetSize(), 4u);
2596 MipsLabel* label = literal->GetLabel();
2597 DCHECK(!label->IsBound());
Alexey Frunze96b66822016-09-10 02:32:44 -07002598 branches_.emplace_back(IsR6(), buffer_.Size(), dest_reg, base_reg, Branch::kLiteral);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002599 FinalizeLabeledBranch(label);
2600}
2601
Alexey Frunze96b66822016-09-10 02:32:44 -07002602JumpTable* MipsAssembler::CreateJumpTable(std::vector<MipsLabel*>&& labels) {
2603 jump_tables_.emplace_back(std::move(labels));
2604 JumpTable* table = &jump_tables_.back();
2605 DCHECK(!table->GetLabel()->IsBound());
2606 return table;
2607}
2608
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002609void MipsAssembler::EmitLiterals() {
2610 if (!literals_.empty()) {
2611 // We don't support byte and half-word literals.
2612 // TODO: proper alignment for 64-bit literals when they're implemented.
2613 for (Literal& literal : literals_) {
2614 MipsLabel* label = literal.GetLabel();
2615 Bind(label);
2616 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
2617 DCHECK(literal.GetSize() == 4u || literal.GetSize() == 8u);
2618 for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
2619 buffer_.Emit<uint8_t>(literal.GetData()[i]);
2620 }
2621 }
2622 }
2623}
2624
Alexey Frunze96b66822016-09-10 02:32:44 -07002625void MipsAssembler::ReserveJumpTableSpace() {
2626 if (!jump_tables_.empty()) {
2627 for (JumpTable& table : jump_tables_) {
2628 MipsLabel* label = table.GetLabel();
2629 Bind(label);
2630
2631 // Bulk ensure capacity, as this may be large.
2632 size_t orig_size = buffer_.Size();
2633 size_t required_capacity = orig_size + table.GetSize();
2634 if (required_capacity > buffer_.Capacity()) {
2635 buffer_.ExtendCapacity(required_capacity);
2636 }
2637#ifndef NDEBUG
2638 buffer_.has_ensured_capacity_ = true;
2639#endif
2640
2641 // Fill the space with dummy data as the data is not final
2642 // until the branches have been promoted. And we shouldn't
2643 // be moving uninitialized data during branch promotion.
2644 for (size_t cnt = table.GetData().size(), i = 0; i < cnt; i++) {
2645 buffer_.Emit<uint32_t>(0x1abe1234u);
2646 }
2647
2648#ifndef NDEBUG
2649 buffer_.has_ensured_capacity_ = false;
2650#endif
2651 }
2652 }
2653}
2654
2655void MipsAssembler::EmitJumpTables() {
2656 if (!jump_tables_.empty()) {
2657 CHECK(!overwriting_);
2658 // Switch from appending instructions at the end of the buffer to overwriting
2659 // existing instructions (here, jump tables) in the buffer.
2660 overwriting_ = true;
2661
2662 for (JumpTable& table : jump_tables_) {
2663 MipsLabel* table_label = table.GetLabel();
2664 uint32_t start = GetLabelLocation(table_label);
2665 overwrite_location_ = start;
2666
2667 for (MipsLabel* target : table.GetData()) {
2668 CHECK_EQ(buffer_.Load<uint32_t>(overwrite_location_), 0x1abe1234u);
2669 // The table will contain target addresses relative to the table start.
2670 uint32_t offset = GetLabelLocation(target) - start;
2671 Emit(offset);
2672 }
2673 }
2674
2675 overwriting_ = false;
2676 }
2677}
2678
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002679void MipsAssembler::PromoteBranches() {
2680 // Promote short branches to long as necessary.
2681 bool changed;
2682 do {
2683 changed = false;
2684 for (auto& branch : branches_) {
2685 CHECK(branch.IsResolved());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002686 uint32_t base = GetBranchLocationOrPcRelBase(&branch);
2687 uint32_t delta = branch.PromoteIfNeeded(base);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002688 // If this branch has been promoted and needs to expand in size,
2689 // relocate all branches by the expansion size.
2690 if (delta) {
2691 changed = true;
2692 uint32_t expand_location = branch.GetLocation();
2693 for (auto& branch2 : branches_) {
2694 branch2.Relocate(expand_location, delta);
2695 }
2696 }
2697 }
2698 } while (changed);
2699
2700 // Account for branch expansion by resizing the code buffer
2701 // and moving the code in it to its final location.
2702 size_t branch_count = branches_.size();
2703 if (branch_count > 0) {
2704 // Resize.
2705 Branch& last_branch = branches_[branch_count - 1];
2706 uint32_t size_delta = last_branch.GetEndLocation() - last_branch.GetOldEndLocation();
2707 uint32_t old_size = buffer_.Size();
2708 buffer_.Resize(old_size + size_delta);
2709 // Move the code residing between branch placeholders.
2710 uint32_t end = old_size;
2711 for (size_t i = branch_count; i > 0; ) {
2712 Branch& branch = branches_[--i];
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002713 CHECK_GE(end, branch.GetOldEndLocation());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002714 uint32_t size = end - branch.GetOldEndLocation();
2715 buffer_.Move(branch.GetEndLocation(), branch.GetOldEndLocation(), size);
2716 end = branch.GetOldLocation();
2717 }
2718 }
2719}
2720
2721// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
2722const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = {
2723 // R2 short branches.
2724 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kUncondBranch
2725 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002726 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCall
Alexey Frunze96b66822016-09-10 02:32:44 -07002727 // R2 near label.
2728 { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLabel
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002729 // R2 near literal.
2730 { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002731 // R2 long branches.
2732 { 9, 3, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongUncondBranch
2733 { 10, 4, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCondBranch
2734 { 6, 1, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCall
Alexey Frunze96b66822016-09-10 02:32:44 -07002735 // R2 far label.
2736 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLabel
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002737 // R2 far literal.
2738 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002739 // R6 short branches.
2740 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6UncondBranch
2741 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kR6CondBranch
2742 // Exception: kOffset23 for beqzc/bnezc.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002743 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6Call
Alexey Frunze96b66822016-09-10 02:32:44 -07002744 // R6 near label.
2745 { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Label
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002746 // R6 near literal.
2747 { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Literal
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002748 // R6 long branches.
2749 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongUncondBranch
2750 { 3, 1, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002751 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCall
Alexey Frunze96b66822016-09-10 02:32:44 -07002752 // R6 far label.
2753 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLabel
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002754 // R6 far literal.
2755 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002756};
2757
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002758// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002759void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
2760 CHECK_EQ(overwriting_, true);
2761 overwrite_location_ = branch->GetLocation();
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002762 uint32_t offset = branch->GetOffset(GetBranchOrPcRelBaseForEncoding(branch));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002763 BranchCondition condition = branch->GetCondition();
2764 Register lhs = branch->GetLeftRegister();
2765 Register rhs = branch->GetRightRegister();
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002766 uint32_t delayed_instruction = branch->GetDelayedInstruction();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002767 switch (branch->GetType()) {
2768 // R2 short branches.
2769 case Branch::kUncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002770 if (delayed_instruction == Branch::kUnfillableDelaySlot) {
2771 // The branch was created when reordering was disabled, do not absorb the target
2772 // instruction.
2773 delayed_instruction = 0; // NOP.
2774 } else if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2775 // Try to absorb the target instruction into the delay slot.
2776 delayed_instruction = 0; // NOP.
2777 // Incrementing the signed 16-bit offset past the target instruction must not
2778 // cause overflow into the negative subrange, check for the max offset.
2779 if (offset != 0x7FFF) {
2780 uint32_t target = branch->GetTarget();
2781 if (std::binary_search(ds_fsm_target_pcs_.begin(), ds_fsm_target_pcs_.end(), target)) {
2782 delayed_instruction = buffer_.Load<uint32_t>(target);
2783 offset++;
2784 }
2785 }
2786 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002787 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2788 B(offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002789 Emit(delayed_instruction);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002790 break;
2791 case Branch::kCondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002792 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2793 if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2794 delayed_instruction = 0; // NOP.
2795 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002796 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002797 EmitBcondR2(condition, lhs, rhs, offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002798 Emit(delayed_instruction);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002799 break;
2800 case Branch::kCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002801 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2802 if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2803 delayed_instruction = 0; // NOP.
2804 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002805 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002806 Bal(offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002807 Emit(delayed_instruction);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002808 break;
2809
Alexey Frunze96b66822016-09-10 02:32:44 -07002810 // R2 near label.
2811 case Branch::kLabel:
2812 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2813 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2814 Addiu(lhs, rhs, offset);
2815 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002816 // R2 near literal.
2817 case Branch::kLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002818 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002819 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2820 Lw(lhs, rhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002821 break;
2822
2823 // R2 long branches.
2824 case Branch::kLongUncondBranch:
2825 // To get the value of the PC register we need to use the NAL instruction.
2826 // NAL clobbers the RA register. However, RA must be preserved if the
2827 // method is compiled without the entry/exit sequences that would take care
2828 // of preserving RA (typically, leaf methods don't preserve RA explicitly).
2829 // So, we need to preserve RA in some temporary storage ourselves. The AT
2830 // register can't be used for this because we need it to load a constant
2831 // which will be added to the value that NAL stores in RA. And we can't
2832 // use T9 for this in the context of the JNI compiler, which uses it
2833 // as a scratch register (see InterproceduralScratchRegister()).
2834 // If we were to add a 32-bit constant to RA using two ADDIU instructions,
2835 // we'd also need to use the ROTR instruction, which requires no less than
2836 // MIPSR2.
2837 // Perhaps, we could use T8 or one of R2's multiplier/divider registers
2838 // (LO or HI) or even a floating-point register, but that doesn't seem
2839 // like a nice solution. We may want this to work on both R6 and pre-R6.
2840 // For now simply use the stack for RA. This should be OK since for the
2841 // vast majority of code a short PC-relative branch is sufficient.
2842 // TODO: can this be improved?
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002843 // TODO: consider generation of a shorter sequence when we know that RA
2844 // is explicitly preserved by the method entry/exit code.
2845 if (delayed_instruction != Branch::kUnfilledDelaySlot &&
2846 delayed_instruction != Branch::kUnfillableDelaySlot) {
2847 Emit(delayed_instruction);
2848 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002849 Push(RA);
2850 Nal();
2851 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2852 Lui(AT, High16Bits(offset));
2853 Ori(AT, AT, Low16Bits(offset));
2854 Addu(AT, AT, RA);
2855 Lw(RA, SP, 0);
2856 Jr(AT);
2857 DecreaseFrameSize(kMipsWordSize);
2858 break;
2859 case Branch::kLongCondBranch:
2860 // The comment on case 'Branch::kLongUncondBranch' applies here as well.
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 // Note: the opposite condition branch encodes 8 as the distance, which is equal to the
2866 // number of instructions skipped:
2867 // (PUSH(IncreaseFrameSize(ADDIU) + SW) + NAL + LUI + ORI + ADDU + LW + JR).
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002868 EmitBcondR2(Branch::OppositeCondition(condition), lhs, rhs, 8);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002869 Push(RA);
2870 Nal();
2871 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2872 Lui(AT, High16Bits(offset));
2873 Ori(AT, AT, Low16Bits(offset));
2874 Addu(AT, AT, RA);
2875 Lw(RA, SP, 0);
2876 Jr(AT);
2877 DecreaseFrameSize(kMipsWordSize);
2878 break;
2879 case Branch::kLongCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002880 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2881 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2882 Emit(delayed_instruction);
2883 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002884 Nal();
2885 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2886 Lui(AT, High16Bits(offset));
2887 Ori(AT, AT, Low16Bits(offset));
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002888 Addu(AT, AT, RA);
2889 Jalr(AT);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002890 Nop();
2891 break;
2892
Alexey Frunze96b66822016-09-10 02:32:44 -07002893 // R2 far label.
2894 case Branch::kFarLabel:
2895 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2896 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2897 Lui(AT, High16Bits(offset));
2898 Ori(AT, AT, Low16Bits(offset));
2899 Addu(lhs, AT, rhs);
2900 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002901 // R2 far literal.
2902 case Branch::kFarLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002903 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002904 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2905 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2906 Lui(AT, High16Bits(offset));
2907 Addu(AT, AT, rhs);
2908 Lw(lhs, AT, Low16Bits(offset));
2909 break;
2910
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002911 // R6 short branches.
2912 case Branch::kR6UncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002913 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002914 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2915 Bc(offset);
2916 break;
2917 case Branch::kR6CondBranch:
2918 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002919 EmitBcondR6(condition, lhs, rhs, offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002920 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2921 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2922 Emit(delayed_instruction);
2923 } else {
2924 // TODO: improve by filling the forbidden slot (IFF this is
2925 // a forbidden and not a delay slot).
2926 Nop();
2927 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002928 break;
2929 case Branch::kR6Call:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002930 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002931 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002932 Balc(offset);
2933 break;
2934
Alexey Frunze96b66822016-09-10 02:32:44 -07002935 // R6 near label.
2936 case Branch::kR6Label:
2937 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2938 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2939 Addiupc(lhs, offset);
2940 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002941 // R6 near literal.
2942 case Branch::kR6Literal:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002943 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002944 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2945 Lwpc(lhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002946 break;
2947
2948 // R6 long branches.
2949 case Branch::kR6LongUncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002950 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002951 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2952 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2953 Auipc(AT, High16Bits(offset));
2954 Jic(AT, Low16Bits(offset));
2955 break;
2956 case Branch::kR6LongCondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002957 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2958 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2959 Emit(delayed_instruction);
2960 }
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002961 EmitBcondR6(Branch::OppositeCondition(condition), lhs, rhs, 2);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002962 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2963 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2964 Auipc(AT, High16Bits(offset));
2965 Jic(AT, Low16Bits(offset));
2966 break;
2967 case Branch::kR6LongCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002968 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002969 offset += (offset & 0x8000) << 1; // Account for sign extension in jialc.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002970 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002971 Auipc(AT, High16Bits(offset));
2972 Jialc(AT, Low16Bits(offset));
2973 break;
2974
Alexey Frunze96b66822016-09-10 02:32:44 -07002975 // R6 far label.
2976 case Branch::kR6FarLabel:
2977 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2978 offset += (offset & 0x8000) << 1; // Account for sign extension in addiu.
2979 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2980 Auipc(AT, High16Bits(offset));
2981 Addiu(lhs, AT, Low16Bits(offset));
2982 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002983 // R6 far literal.
2984 case Branch::kR6FarLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002985 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002986 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2987 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2988 Auipc(AT, High16Bits(offset));
2989 Lw(lhs, AT, Low16Bits(offset));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002990 break;
2991 }
2992 CHECK_EQ(overwrite_location_, branch->GetEndLocation());
2993 CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize));
2994}
2995
2996void MipsAssembler::B(MipsLabel* label) {
2997 Buncond(label);
2998}
2999
Alexey Frunzee3fb2452016-05-10 16:08:05 -07003000void MipsAssembler::Bal(MipsLabel* label) {
3001 Call(label);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003002}
3003
3004void MipsAssembler::Beq(Register rs, Register rt, MipsLabel* label) {
3005 Bcond(label, kCondEQ, rs, rt);
3006}
3007
3008void MipsAssembler::Bne(Register rs, Register rt, MipsLabel* label) {
3009 Bcond(label, kCondNE, rs, rt);
3010}
3011
3012void MipsAssembler::Beqz(Register rt, MipsLabel* label) {
3013 Bcond(label, kCondEQZ, rt);
3014}
3015
3016void MipsAssembler::Bnez(Register rt, MipsLabel* label) {
3017 Bcond(label, kCondNEZ, rt);
3018}
3019
3020void MipsAssembler::Bltz(Register rt, MipsLabel* label) {
3021 Bcond(label, kCondLTZ, rt);
3022}
3023
3024void MipsAssembler::Bgez(Register rt, MipsLabel* label) {
3025 Bcond(label, kCondGEZ, rt);
3026}
3027
3028void MipsAssembler::Blez(Register rt, MipsLabel* label) {
3029 Bcond(label, kCondLEZ, rt);
3030}
3031
3032void MipsAssembler::Bgtz(Register rt, MipsLabel* label) {
3033 Bcond(label, kCondGTZ, rt);
3034}
3035
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003036bool MipsAssembler::CanExchangeWithSlt(Register rs, Register rt) const {
3037 // If the instruction modifies AT, `rs` or `rt`, it can't be exchanged with the slt[u]
3038 // instruction because either slt[u] depends on `rs` or `rt` or the following
3039 // conditional branch depends on AT set by slt[u].
3040 // Likewise, if the instruction depends on AT, it can't be exchanged with slt[u]
3041 // because slt[u] changes AT.
3042 return (delay_slot_.instruction_ != 0 &&
3043 (delay_slot_.gpr_outs_mask_ & ((1u << AT) | (1u << rs) | (1u << rt))) == 0 &&
3044 (delay_slot_.gpr_ins_mask_ & (1u << AT)) == 0);
3045}
3046
3047void MipsAssembler::ExchangeWithSlt(const DelaySlot& forwarded_slot) {
3048 // Exchange the last two instructions in the assembler buffer.
3049 size_t size = buffer_.Size();
3050 CHECK_GE(size, 2 * sizeof(uint32_t));
3051 size_t pos1 = size - 2 * sizeof(uint32_t);
3052 size_t pos2 = size - sizeof(uint32_t);
3053 uint32_t instr1 = buffer_.Load<uint32_t>(pos1);
3054 uint32_t instr2 = buffer_.Load<uint32_t>(pos2);
3055 CHECK_EQ(instr1, forwarded_slot.instruction_);
3056 CHECK_EQ(instr2, delay_slot_.instruction_);
3057 buffer_.Store<uint32_t>(pos1, instr2);
3058 buffer_.Store<uint32_t>(pos2, instr1);
3059 // Set the current delay slot information to that of the last instruction
3060 // in the buffer.
3061 delay_slot_ = forwarded_slot;
3062}
3063
3064void MipsAssembler::GenerateSltForCondBranch(bool unsigned_slt, Register rs, Register rt) {
3065 // If possible, exchange the slt[u] instruction with the preceding instruction,
3066 // so it can fill the delay slot.
3067 DelaySlot forwarded_slot = delay_slot_;
3068 bool exchange = CanExchangeWithSlt(rs, rt);
3069 if (exchange) {
3070 // The last instruction cannot be used in a different delay slot,
3071 // do not commit the label before it (if any).
3072 DsFsmDropLabel();
3073 }
3074 if (unsigned_slt) {
3075 Sltu(AT, rs, rt);
3076 } else {
3077 Slt(AT, rs, rt);
3078 }
3079 if (exchange) {
3080 ExchangeWithSlt(forwarded_slot);
3081 }
3082}
3083
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003084void MipsAssembler::Blt(Register rs, Register rt, MipsLabel* label) {
3085 if (IsR6()) {
3086 Bcond(label, kCondLT, rs, rt);
3087 } else if (!Branch::IsNop(kCondLT, rs, rt)) {
3088 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003089 GenerateSltForCondBranch(/* unsigned_slt */ false, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003090 Bnez(AT, label);
3091 }
3092}
3093
3094void MipsAssembler::Bge(Register rs, Register rt, MipsLabel* label) {
3095 if (IsR6()) {
3096 Bcond(label, kCondGE, rs, rt);
3097 } else if (Branch::IsUncond(kCondGE, rs, rt)) {
3098 B(label);
3099 } else {
3100 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003101 GenerateSltForCondBranch(/* unsigned_slt */ false, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003102 Beqz(AT, label);
3103 }
3104}
3105
3106void MipsAssembler::Bltu(Register rs, Register rt, MipsLabel* label) {
3107 if (IsR6()) {
3108 Bcond(label, kCondLTU, rs, rt);
3109 } else if (!Branch::IsNop(kCondLTU, rs, rt)) {
3110 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003111 GenerateSltForCondBranch(/* unsigned_slt */ true, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003112 Bnez(AT, label);
3113 }
3114}
3115
3116void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) {
3117 if (IsR6()) {
3118 Bcond(label, kCondGEU, rs, rt);
3119 } else if (Branch::IsUncond(kCondGEU, rs, rt)) {
3120 B(label);
3121 } else {
3122 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003123 GenerateSltForCondBranch(/* unsigned_slt */ true, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003124 Beqz(AT, label);
jeffhao7fbee072012-08-24 17:56:54 -07003125 }
3126}
3127
Chris Larsenb74353a2015-11-20 09:07:09 -08003128void MipsAssembler::Bc1f(MipsLabel* label) {
3129 Bc1f(0, label);
3130}
3131
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003132void MipsAssembler::Bc1f(int cc, MipsLabel* label) {
3133 CHECK(IsUint<3>(cc)) << cc;
3134 Bcond(label, kCondF, static_cast<Register>(cc), ZERO);
3135}
3136
Chris Larsenb74353a2015-11-20 09:07:09 -08003137void MipsAssembler::Bc1t(MipsLabel* label) {
3138 Bc1t(0, label);
3139}
3140
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003141void MipsAssembler::Bc1t(int cc, MipsLabel* label) {
3142 CHECK(IsUint<3>(cc)) << cc;
3143 Bcond(label, kCondT, static_cast<Register>(cc), ZERO);
3144}
3145
3146void MipsAssembler::Bc1eqz(FRegister ft, MipsLabel* label) {
3147 Bcond(label, kCondF, static_cast<Register>(ft), ZERO);
3148}
3149
3150void MipsAssembler::Bc1nez(FRegister ft, MipsLabel* label) {
3151 Bcond(label, kCondT, static_cast<Register>(ft), ZERO);
3152}
3153
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003154void MipsAssembler::AdjustBaseAndOffset(Register& base,
3155 int32_t& offset,
3156 bool is_doubleword,
3157 bool is_float) {
3158 // This method is used to adjust the base register and offset pair
3159 // for a load/store when the offset doesn't fit into int16_t.
3160 // It is assumed that `base + offset` is sufficiently aligned for memory
3161 // operands that are machine word in size or smaller. For doubleword-sized
3162 // operands it's assumed that `base` is a multiple of 8, while `offset`
3163 // may be a multiple of 4 (e.g. 4-byte-aligned long and double arguments
3164 // and spilled variables on the stack accessed relative to the stack
3165 // pointer register).
3166 // We preserve the "alignment" of `offset` by adjusting it by a multiple of 8.
3167 CHECK_NE(base, AT); // Must not overwrite the register `base` while loading `offset`.
3168
3169 bool doubleword_aligned = IsAligned<kMipsDoublewordSize>(offset);
3170 bool two_accesses = is_doubleword && (!is_float || !doubleword_aligned);
3171
3172 // IsInt<16> must be passed a signed value, hence the static cast below.
3173 if (IsInt<16>(offset) &&
3174 (!two_accesses || IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
3175 // Nothing to do: `offset` (and, if needed, `offset + 4`) fits into int16_t.
3176 return;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003177 }
3178
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003179 // Remember the "(mis)alignment" of `offset`, it will be checked at the end.
3180 uint32_t misalignment = offset & (kMipsDoublewordSize - 1);
3181
3182 // Do not load the whole 32-bit `offset` if it can be represented as
3183 // a sum of two 16-bit signed offsets. This can save an instruction or two.
3184 // To simplify matters, only do this for a symmetric range of offsets from
3185 // about -64KB to about +64KB, allowing further addition of 4 when accessing
3186 // 64-bit variables with two 32-bit accesses.
3187 constexpr int32_t kMinOffsetForSimpleAdjustment = 0x7ff8; // Max int16_t that's a multiple of 8.
3188 constexpr int32_t kMaxOffsetForSimpleAdjustment = 2 * kMinOffsetForSimpleAdjustment;
3189 if (0 <= offset && offset <= kMaxOffsetForSimpleAdjustment) {
3190 Addiu(AT, base, kMinOffsetForSimpleAdjustment);
3191 offset -= kMinOffsetForSimpleAdjustment;
3192 } else if (-kMaxOffsetForSimpleAdjustment <= offset && offset < 0) {
3193 Addiu(AT, base, -kMinOffsetForSimpleAdjustment);
3194 offset += kMinOffsetForSimpleAdjustment;
3195 } else if (IsR6()) {
3196 // On R6 take advantage of the aui instruction, e.g.:
3197 // aui AT, base, offset_high
3198 // lw reg_lo, offset_low(AT)
3199 // lw reg_hi, (offset_low+4)(AT)
3200 // or when offset_low+4 overflows int16_t:
3201 // aui AT, base, offset_high
3202 // addiu AT, AT, 8
3203 // lw reg_lo, (offset_low-8)(AT)
3204 // lw reg_hi, (offset_low-4)(AT)
3205 int16_t offset_high = High16Bits(offset);
3206 int16_t offset_low = Low16Bits(offset);
3207 offset_high += (offset_low < 0) ? 1 : 0; // Account for offset sign extension in load/store.
3208 Aui(AT, base, offset_high);
3209 if (two_accesses && !IsInt<16>(static_cast<int32_t>(offset_low + kMipsWordSize))) {
3210 // Avoid overflow in the 16-bit offset of the load/store instruction when adding 4.
3211 Addiu(AT, AT, kMipsDoublewordSize);
3212 offset_low -= kMipsDoublewordSize;
3213 }
3214 offset = offset_low;
3215 } else {
3216 // Do not load the whole 32-bit `offset` if it can be represented as
3217 // a sum of three 16-bit signed offsets. This can save an instruction.
3218 // To simplify matters, only do this for a symmetric range of offsets from
3219 // about -96KB to about +96KB, allowing further addition of 4 when accessing
3220 // 64-bit variables with two 32-bit accesses.
3221 constexpr int32_t kMinOffsetForMediumAdjustment = 2 * kMinOffsetForSimpleAdjustment;
3222 constexpr int32_t kMaxOffsetForMediumAdjustment = 3 * kMinOffsetForSimpleAdjustment;
3223 if (0 <= offset && offset <= kMaxOffsetForMediumAdjustment) {
3224 Addiu(AT, base, kMinOffsetForMediumAdjustment / 2);
3225 Addiu(AT, AT, kMinOffsetForMediumAdjustment / 2);
3226 offset -= kMinOffsetForMediumAdjustment;
3227 } else if (-kMaxOffsetForMediumAdjustment <= offset && offset < 0) {
3228 Addiu(AT, base, -kMinOffsetForMediumAdjustment / 2);
3229 Addiu(AT, AT, -kMinOffsetForMediumAdjustment / 2);
3230 offset += kMinOffsetForMediumAdjustment;
3231 } else {
3232 // Now that all shorter options have been exhausted, load the full 32-bit offset.
3233 int32_t loaded_offset = RoundDown(offset, kMipsDoublewordSize);
3234 LoadConst32(AT, loaded_offset);
3235 Addu(AT, AT, base);
3236 offset -= loaded_offset;
3237 }
3238 }
3239 base = AT;
3240
3241 CHECK(IsInt<16>(offset));
3242 if (two_accesses) {
3243 CHECK(IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)));
3244 }
3245 CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
3246}
3247
Alexey Frunze2923db72016-08-20 01:55:47 -07003248void MipsAssembler::LoadFromOffset(LoadOperandType type,
3249 Register reg,
3250 Register base,
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003251 int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003252 LoadFromOffset<>(type, reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003253}
3254
3255void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003256 LoadSFromOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003257}
3258
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003259void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003260 LoadDFromOffset<>(reg, base, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003261}
3262
3263void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
3264 size_t size) {
3265 MipsManagedRegister dst = m_dst.AsMips();
3266 if (dst.IsNoRegister()) {
3267 CHECK_EQ(0u, size) << dst;
3268 } else if (dst.IsCoreRegister()) {
3269 CHECK_EQ(kMipsWordSize, size) << dst;
3270 LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
3271 } else if (dst.IsRegisterPair()) {
3272 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3273 LoadFromOffset(kLoadDoubleword, dst.AsRegisterPairLow(), src_register, src_offset);
3274 } else if (dst.IsFRegister()) {
3275 if (size == kMipsWordSize) {
3276 LoadSFromOffset(dst.AsFRegister(), src_register, src_offset);
3277 } else {
3278 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3279 LoadDFromOffset(dst.AsFRegister(), src_register, src_offset);
3280 }
Alexey Frunze1b8464d2016-11-12 17:22:05 -08003281 } else if (dst.IsDRegister()) {
3282 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3283 LoadDFromOffset(dst.AsOverlappingDRegisterLow(), src_register, src_offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003284 }
jeffhao7fbee072012-08-24 17:56:54 -07003285}
3286
Alexey Frunze2923db72016-08-20 01:55:47 -07003287void MipsAssembler::StoreToOffset(StoreOperandType type,
3288 Register reg,
3289 Register base,
jeffhao7fbee072012-08-24 17:56:54 -07003290 int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003291 StoreToOffset<>(type, reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003292}
3293
Goran Jakovljevicff734982015-08-24 12:58:55 +00003294void MipsAssembler::StoreSToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003295 StoreSToOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003296}
3297
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003298void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003299 StoreDToOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003300}
3301
David Srbeckydd973932015-04-07 20:29:48 +01003302static dwarf::Reg DWARFReg(Register reg) {
3303 return dwarf::Reg::MipsCore(static_cast<int>(reg));
3304}
3305
Ian Rogers790a6b72014-04-01 10:36:00 -07003306constexpr size_t kFramePointerSize = 4;
3307
Vladimir Marko32248382016-05-19 10:37:24 +01003308void MipsAssembler::BuildFrame(size_t frame_size,
3309 ManagedRegister method_reg,
3310 ArrayRef<const ManagedRegister> callee_save_regs,
Dmitry Petrochenkofca82202014-03-21 11:21:37 +07003311 const ManagedRegisterEntrySpills& entry_spills) {
jeffhao7fbee072012-08-24 17:56:54 -07003312 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003313 DCHECK(!overwriting_);
jeffhao7fbee072012-08-24 17:56:54 -07003314
3315 // Increase frame to required size.
3316 IncreaseFrameSize(frame_size);
3317
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003318 // Push callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07003319 int stack_offset = frame_size - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003320 StoreToOffset(kStoreWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003321 cfi_.RelOffset(DWARFReg(RA), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07003322 for (int i = callee_save_regs.size() - 1; i >= 0; --i) {
Ian Rogers790a6b72014-04-01 10:36:00 -07003323 stack_offset -= kFramePointerSize;
Vladimir Marko32248382016-05-19 10:37:24 +01003324 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07003325 StoreToOffset(kStoreWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003326 cfi_.RelOffset(DWARFReg(reg), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07003327 }
3328
3329 // Write out Method*.
3330 StoreToOffset(kStoreWord, method_reg.AsMips().AsCoreRegister(), SP, 0);
3331
3332 // Write out entry spills.
Goran Jakovljevicff734982015-08-24 12:58:55 +00003333 int32_t offset = frame_size + kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003334 for (size_t i = 0; i < entry_spills.size(); ++i) {
Goran Jakovljevicff734982015-08-24 12:58:55 +00003335 MipsManagedRegister reg = entry_spills.at(i).AsMips();
3336 if (reg.IsNoRegister()) {
3337 ManagedRegisterSpill spill = entry_spills.at(i);
3338 offset += spill.getSize();
3339 } else if (reg.IsCoreRegister()) {
3340 StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003341 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003342 } else if (reg.IsFRegister()) {
3343 StoreSToOffset(reg.AsFRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003344 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003345 } else if (reg.IsDRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003346 StoreDToOffset(reg.AsOverlappingDRegisterLow(), SP, offset);
3347 offset += kMipsDoublewordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003348 }
jeffhao7fbee072012-08-24 17:56:54 -07003349 }
3350}
3351
3352void MipsAssembler::RemoveFrame(size_t frame_size,
Vladimir Marko32248382016-05-19 10:37:24 +01003353 ArrayRef<const ManagedRegister> callee_save_regs) {
jeffhao7fbee072012-08-24 17:56:54 -07003354 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003355 DCHECK(!overwriting_);
David Srbeckydd973932015-04-07 20:29:48 +01003356 cfi_.RememberState();
jeffhao7fbee072012-08-24 17:56:54 -07003357
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003358 // Pop callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07003359 int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003360 for (size_t i = 0; i < callee_save_regs.size(); ++i) {
Vladimir Marko32248382016-05-19 10:37:24 +01003361 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07003362 LoadFromOffset(kLoadWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003363 cfi_.Restore(DWARFReg(reg));
Ian Rogers790a6b72014-04-01 10:36:00 -07003364 stack_offset += kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003365 }
3366 LoadFromOffset(kLoadWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003367 cfi_.Restore(DWARFReg(RA));
jeffhao7fbee072012-08-24 17:56:54 -07003368
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003369 // Adjust the stack pointer in the delay slot if doing so doesn't break CFI.
3370 bool exchange = IsInt<16>(static_cast<int32_t>(frame_size));
3371 bool reordering = SetReorder(false);
3372 if (exchange) {
3373 // Jump to the return address.
3374 Jr(RA);
3375 // Decrease frame to required size.
3376 DecreaseFrameSize(frame_size); // Single instruction in delay slot.
3377 } else {
3378 // Decrease frame to required size.
3379 DecreaseFrameSize(frame_size);
3380 // Jump to the return address.
3381 Jr(RA);
3382 Nop(); // In delay slot.
3383 }
3384 SetReorder(reordering);
David Srbeckydd973932015-04-07 20:29:48 +01003385
3386 // The CFI should be restored for any code that follows the exit block.
3387 cfi_.RestoreState();
3388 cfi_.DefCFAOffset(frame_size);
jeffhao7fbee072012-08-24 17:56:54 -07003389}
3390
3391void MipsAssembler::IncreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003392 CHECK_ALIGNED(adjust, kFramePointerSize);
3393 Addiu32(SP, SP, -adjust);
David Srbeckydd973932015-04-07 20:29:48 +01003394 cfi_.AdjustCFAOffset(adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003395 if (overwriting_) {
3396 cfi_.OverrideDelayedPC(overwrite_location_);
3397 }
jeffhao7fbee072012-08-24 17:56:54 -07003398}
3399
3400void MipsAssembler::DecreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003401 CHECK_ALIGNED(adjust, kFramePointerSize);
3402 Addiu32(SP, SP, adjust);
David Srbeckydd973932015-04-07 20:29:48 +01003403 cfi_.AdjustCFAOffset(-adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003404 if (overwriting_) {
3405 cfi_.OverrideDelayedPC(overwrite_location_);
3406 }
jeffhao7fbee072012-08-24 17:56:54 -07003407}
3408
3409void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
3410 MipsManagedRegister src = msrc.AsMips();
3411 if (src.IsNoRegister()) {
3412 CHECK_EQ(0u, size);
3413 } else if (src.IsCoreRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003414 CHECK_EQ(kMipsWordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07003415 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3416 } else if (src.IsRegisterPair()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003417 CHECK_EQ(kMipsDoublewordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07003418 StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
3419 StoreToOffset(kStoreWord, src.AsRegisterPairHigh(),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003420 SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003421 } else if (src.IsFRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003422 if (size == kMipsWordSize) {
3423 StoreSToOffset(src.AsFRegister(), SP, dest.Int32Value());
3424 } else {
3425 CHECK_EQ(kMipsDoublewordSize, size);
3426 StoreDToOffset(src.AsFRegister(), SP, dest.Int32Value());
3427 }
Alexey Frunze1b8464d2016-11-12 17:22:05 -08003428 } else if (src.IsDRegister()) {
3429 CHECK_EQ(kMipsDoublewordSize, size);
3430 StoreDToOffset(src.AsOverlappingDRegisterLow(), SP, dest.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003431 }
3432}
3433
3434void MipsAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
3435 MipsManagedRegister src = msrc.AsMips();
3436 CHECK(src.IsCoreRegister());
3437 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3438}
3439
3440void MipsAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
3441 MipsManagedRegister src = msrc.AsMips();
3442 CHECK(src.IsCoreRegister());
3443 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3444}
3445
3446void MipsAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
3447 ManagedRegister mscratch) {
3448 MipsManagedRegister scratch = mscratch.AsMips();
3449 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003450 LoadConst32(scratch.AsCoreRegister(), imm);
jeffhao7fbee072012-08-24 17:56:54 -07003451 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
3452}
3453
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003454void MipsAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs,
3455 FrameOffset fr_offs,
jeffhao7fbee072012-08-24 17:56:54 -07003456 ManagedRegister mscratch) {
3457 MipsManagedRegister scratch = mscratch.AsMips();
3458 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003459 Addiu32(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003460 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3461 S1, thr_offs.Int32Value());
3462}
3463
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003464void MipsAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003465 StoreToOffset(kStoreWord, SP, S1, thr_offs.Int32Value());
3466}
3467
3468void MipsAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
3469 FrameOffset in_off, ManagedRegister mscratch) {
3470 MipsManagedRegister src = msrc.AsMips();
3471 MipsManagedRegister scratch = mscratch.AsMips();
3472 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3473 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003474 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003475}
3476
3477void MipsAssembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) {
3478 return EmitLoad(mdest, SP, src.Int32Value(), size);
3479}
3480
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003481void MipsAssembler::LoadFromThread(ManagedRegister mdest, ThreadOffset32 src, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003482 return EmitLoad(mdest, S1, src.Int32Value(), size);
3483}
3484
3485void MipsAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) {
3486 MipsManagedRegister dest = mdest.AsMips();
3487 CHECK(dest.IsCoreRegister());
3488 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value());
3489}
3490
Mathieu Chartiere401d142015-04-22 13:56:20 -07003491void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs,
Roland Levillain4d027112015-07-01 15:41:14 +01003492 bool unpoison_reference) {
jeffhao7fbee072012-08-24 17:56:54 -07003493 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003494 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07003495 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
3496 base.AsMips().AsCoreRegister(), offs.Int32Value());
Alexey Frunzec061de12017-02-14 13:27:23 -08003497 if (unpoison_reference) {
3498 MaybeUnpoisonHeapReference(dest.AsCoreRegister());
Hiroshi Yamauchie63a7452014-02-27 14:44:36 -08003499 }
jeffhao7fbee072012-08-24 17:56:54 -07003500}
3501
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003502void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003503 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003504 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07003505 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
3506 base.AsMips().AsCoreRegister(), offs.Int32Value());
3507}
3508
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003509void MipsAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset32 offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003510 MipsManagedRegister dest = mdest.AsMips();
3511 CHECK(dest.IsCoreRegister());
3512 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), S1, offs.Int32Value());
3513}
3514
3515void MipsAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
3516 UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips";
3517}
3518
3519void MipsAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
3520 UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips";
3521}
3522
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003523void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003524 MipsManagedRegister dest = mdest.AsMips();
3525 MipsManagedRegister src = msrc.AsMips();
3526 if (!dest.Equals(src)) {
3527 if (dest.IsCoreRegister()) {
3528 CHECK(src.IsCoreRegister()) << src;
3529 Move(dest.AsCoreRegister(), src.AsCoreRegister());
3530 } else if (dest.IsFRegister()) {
3531 CHECK(src.IsFRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003532 if (size == kMipsWordSize) {
3533 MovS(dest.AsFRegister(), src.AsFRegister());
3534 } else {
3535 CHECK_EQ(kMipsDoublewordSize, size);
3536 MovD(dest.AsFRegister(), src.AsFRegister());
3537 }
jeffhao7fbee072012-08-24 17:56:54 -07003538 } else if (dest.IsDRegister()) {
3539 CHECK(src.IsDRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003540 MovD(dest.AsOverlappingDRegisterLow(), src.AsOverlappingDRegisterLow());
jeffhao7fbee072012-08-24 17:56:54 -07003541 } else {
3542 CHECK(dest.IsRegisterPair()) << dest;
3543 CHECK(src.IsRegisterPair()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003544 // Ensure that the first move doesn't clobber the input of the second.
jeffhao7fbee072012-08-24 17:56:54 -07003545 if (src.AsRegisterPairHigh() != dest.AsRegisterPairLow()) {
3546 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
3547 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
3548 } else {
3549 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
3550 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
3551 }
3552 }
3553 }
3554}
3555
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003556void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003557 MipsManagedRegister scratch = mscratch.AsMips();
3558 CHECK(scratch.IsCoreRegister()) << scratch;
3559 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3560 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
3561}
3562
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003563void MipsAssembler::CopyRawPtrFromThread(FrameOffset fr_offs,
3564 ThreadOffset32 thr_offs,
3565 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003566 MipsManagedRegister scratch = mscratch.AsMips();
3567 CHECK(scratch.IsCoreRegister()) << scratch;
3568 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3569 S1, thr_offs.Int32Value());
3570 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3571 SP, fr_offs.Int32Value());
3572}
3573
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003574void MipsAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs,
3575 FrameOffset fr_offs,
3576 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003577 MipsManagedRegister scratch = mscratch.AsMips();
3578 CHECK(scratch.IsCoreRegister()) << scratch;
3579 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3580 SP, fr_offs.Int32Value());
3581 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3582 S1, thr_offs.Int32Value());
3583}
3584
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003585void MipsAssembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003586 MipsManagedRegister scratch = mscratch.AsMips();
3587 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003588 CHECK(size == kMipsWordSize || size == kMipsDoublewordSize) << size;
3589 if (size == kMipsWordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07003590 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3591 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003592 } else if (size == kMipsDoublewordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07003593 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3594 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003595 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + kMipsWordSize);
3596 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003597 }
3598}
3599
3600void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
3601 ManagedRegister mscratch, size_t size) {
3602 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003603 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003604 LoadFromOffset(kLoadWord, scratch, src_base.AsMips().AsCoreRegister(), src_offset.Int32Value());
3605 StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
3606}
3607
3608void MipsAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
3609 ManagedRegister mscratch, size_t size) {
3610 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003611 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003612 LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
3613 StoreToOffset(kStoreWord, scratch, dest_base.AsMips().AsCoreRegister(), dest_offset.Int32Value());
3614}
3615
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003616void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
3617 FrameOffset src_base ATTRIBUTE_UNUSED,
3618 Offset src_offset ATTRIBUTE_UNUSED,
3619 ManagedRegister mscratch ATTRIBUTE_UNUSED,
3620 size_t size ATTRIBUTE_UNUSED) {
3621 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003622}
3623
3624void MipsAssembler::Copy(ManagedRegister dest, Offset dest_offset,
3625 ManagedRegister src, Offset src_offset,
3626 ManagedRegister mscratch, size_t size) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003627 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003628 Register scratch = mscratch.AsMips().AsCoreRegister();
3629 LoadFromOffset(kLoadWord, scratch, src.AsMips().AsCoreRegister(), src_offset.Int32Value());
3630 StoreToOffset(kStoreWord, scratch, dest.AsMips().AsCoreRegister(), dest_offset.Int32Value());
3631}
3632
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003633void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
3634 Offset dest_offset ATTRIBUTE_UNUSED,
3635 FrameOffset src ATTRIBUTE_UNUSED,
3636 Offset src_offset ATTRIBUTE_UNUSED,
3637 ManagedRegister mscratch ATTRIBUTE_UNUSED,
3638 size_t size ATTRIBUTE_UNUSED) {
3639 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003640}
3641
3642void MipsAssembler::MemoryBarrier(ManagedRegister) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003643 // TODO: sync?
3644 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003645}
3646
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003647void MipsAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003648 FrameOffset handle_scope_offset,
3649 ManagedRegister min_reg,
3650 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003651 MipsManagedRegister out_reg = mout_reg.AsMips();
3652 MipsManagedRegister in_reg = min_reg.AsMips();
3653 CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
3654 CHECK(out_reg.IsCoreRegister()) << out_reg;
3655 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003656 MipsLabel null_arg;
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003657 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3658 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003659 // E.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset).
jeffhao7fbee072012-08-24 17:56:54 -07003660 if (in_reg.IsNoRegister()) {
3661 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003662 SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003663 in_reg = out_reg;
3664 }
3665 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003666 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003667 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003668 Beqz(in_reg.AsCoreRegister(), &null_arg);
3669 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3670 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003671 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003672 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003673 }
3674}
3675
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003676void MipsAssembler::CreateHandleScopeEntry(FrameOffset out_off,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003677 FrameOffset handle_scope_offset,
3678 ManagedRegister mscratch,
3679 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003680 MipsManagedRegister scratch = mscratch.AsMips();
3681 CHECK(scratch.IsCoreRegister()) << scratch;
3682 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003683 MipsLabel null_arg;
3684 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003685 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3686 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003687 // E.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset).
3688 Beqz(scratch.AsCoreRegister(), &null_arg);
3689 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3690 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003691 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003692 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003693 }
3694 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
3695}
3696
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003697// Given a handle scope entry, load the associated reference.
3698void MipsAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003699 ManagedRegister min_reg) {
jeffhao7fbee072012-08-24 17:56:54 -07003700 MipsManagedRegister out_reg = mout_reg.AsMips();
3701 MipsManagedRegister in_reg = min_reg.AsMips();
3702 CHECK(out_reg.IsCoreRegister()) << out_reg;
3703 CHECK(in_reg.IsCoreRegister()) << in_reg;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003704 MipsLabel null_arg;
jeffhao7fbee072012-08-24 17:56:54 -07003705 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003706 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003707 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003708 Beqz(in_reg.AsCoreRegister(), &null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003709 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
3710 in_reg.AsCoreRegister(), 0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003711 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003712}
3713
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003714void MipsAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,
3715 bool could_be_null ATTRIBUTE_UNUSED) {
3716 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003717}
3718
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003719void MipsAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED,
3720 bool could_be_null ATTRIBUTE_UNUSED) {
3721 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003722}
3723
3724void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) {
3725 MipsManagedRegister base = mbase.AsMips();
3726 MipsManagedRegister scratch = mscratch.AsMips();
3727 CHECK(base.IsCoreRegister()) << base;
3728 CHECK(scratch.IsCoreRegister()) << scratch;
3729 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3730 base.AsCoreRegister(), offset.Int32Value());
3731 Jalr(scratch.AsCoreRegister());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003732 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003733 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003734}
3735
3736void MipsAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
3737 MipsManagedRegister scratch = mscratch.AsMips();
3738 CHECK(scratch.IsCoreRegister()) << scratch;
3739 // Call *(*(SP + base) + offset)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003740 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, base.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003741 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3742 scratch.AsCoreRegister(), offset.Int32Value());
3743 Jalr(scratch.AsCoreRegister());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003744 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003745 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003746}
3747
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003748void MipsAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UNUSED,
3749 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
Ian Rogers468532e2013-08-05 10:56:33 -07003750 UNIMPLEMENTED(FATAL) << "no mips implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003751}
3752
3753void MipsAssembler::GetCurrentThread(ManagedRegister tr) {
3754 Move(tr.AsMips().AsCoreRegister(), S1);
3755}
3756
3757void MipsAssembler::GetCurrentThread(FrameOffset offset,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003758 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
jeffhao7fbee072012-08-24 17:56:54 -07003759 StoreToOffset(kStoreWord, S1, SP, offset.Int32Value());
3760}
3761
jeffhao7fbee072012-08-24 17:56:54 -07003762void MipsAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
3763 MipsManagedRegister scratch = mscratch.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003764 exception_blocks_.emplace_back(scratch, stack_adjust);
jeffhao7fbee072012-08-24 17:56:54 -07003765 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
Andreas Gampe542451c2016-07-26 09:02:02 -07003766 S1, Thread::ExceptionOffset<kMipsPointerSize>().Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003767 Bnez(scratch.AsCoreRegister(), exception_blocks_.back().Entry());
jeffhao7fbee072012-08-24 17:56:54 -07003768}
3769
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003770void MipsAssembler::EmitExceptionPoll(MipsExceptionSlowPath* exception) {
3771 Bind(exception->Entry());
3772 if (exception->stack_adjust_ != 0) { // Fix up the frame.
3773 DecreaseFrameSize(exception->stack_adjust_);
jeffhao7fbee072012-08-24 17:56:54 -07003774 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003775 // Pass exception object as argument.
3776 // Don't care about preserving A0 as this call won't return.
3777 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
3778 Move(A0, exception->scratch_.AsCoreRegister());
3779 // Set up call to Thread::Current()->pDeliverException.
3780 LoadFromOffset(kLoadWord, T9, S1,
Andreas Gampe542451c2016-07-26 09:02:02 -07003781 QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, pDeliverException).Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003782 Jr(T9);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003783 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003784
3785 // Call never returns.
3786 Break();
jeffhao7fbee072012-08-24 17:56:54 -07003787}
3788
3789} // namespace mips
3790} // namespace art