blob: 3dcad6a6b95e16284909c7f231b9f3f85bae20c0 [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
jeffhao7fbee072012-08-24 17:56:54 -0700638void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700639 DsFsmInstrRrr(EmitI(0x20, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700640}
641
642void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700643 DsFsmInstrRrr(EmitI(0x21, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700644}
645
646void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700647 DsFsmInstrRrr(EmitI(0x23, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700648}
649
Chris Larsen3acee732015-11-18 13:31:08 -0800650void MipsAssembler::Lwl(Register rt, Register rs, uint16_t imm16) {
651 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700652 DsFsmInstrRrr(EmitI(0x22, rs, rt, imm16), rt, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800653}
654
655void MipsAssembler::Lwr(Register rt, Register rs, uint16_t imm16) {
656 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700657 DsFsmInstrRrr(EmitI(0x26, rs, rt, imm16), rt, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800658}
659
jeffhao7fbee072012-08-24 17:56:54 -0700660void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700661 DsFsmInstrRrr(EmitI(0x24, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700662}
663
664void MipsAssembler::Lhu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700665 DsFsmInstrRrr(EmitI(0x25, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700666}
667
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700668void MipsAssembler::Lwpc(Register rs, uint32_t imm19) {
669 CHECK(IsR6());
670 CHECK(IsUint<19>(imm19)) << imm19;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700671 DsFsmInstrNop(EmitI21(0x3B, rs, (0x01 << 19) | imm19));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700672}
673
jeffhao7fbee072012-08-24 17:56:54 -0700674void MipsAssembler::Lui(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700675 DsFsmInstrRrr(EmitI(0xf, static_cast<Register>(0), rt, imm16), rt, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700676}
677
Alexey Frunzecad3a4c2016-06-07 23:40:37 -0700678void MipsAssembler::Aui(Register rt, Register rs, uint16_t imm16) {
679 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700680 DsFsmInstrRrr(EmitI(0xf, rs, rt, imm16), rt, rt, rs);
Alexey Frunzecad3a4c2016-06-07 23:40:37 -0700681}
682
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200683void MipsAssembler::Sync(uint32_t stype) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700684 DsFsmInstrNop(EmitR(0, ZERO, ZERO, ZERO, stype & 0x1f, 0xf));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200685}
686
jeffhao7fbee072012-08-24 17:56:54 -0700687void MipsAssembler::Mfhi(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200688 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700689 DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x10), rd, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700690}
691
692void MipsAssembler::Mflo(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200693 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700694 DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x12), rd, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700695}
696
697void MipsAssembler::Sb(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700698 DsFsmInstrRrr(EmitI(0x28, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700699}
700
701void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700702 DsFsmInstrRrr(EmitI(0x29, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700703}
704
705void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700706 DsFsmInstrRrr(EmitI(0x2b, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700707}
708
Chris Larsen3acee732015-11-18 13:31:08 -0800709void MipsAssembler::Swl(Register rt, Register rs, uint16_t imm16) {
710 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700711 DsFsmInstrRrr(EmitI(0x2a, rs, rt, imm16), ZERO, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800712}
713
714void MipsAssembler::Swr(Register rt, Register rs, uint16_t imm16) {
715 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700716 DsFsmInstrRrr(EmitI(0x2e, rs, rt, imm16), ZERO, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800717}
718
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700719void MipsAssembler::LlR2(Register rt, Register base, int16_t imm16) {
720 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700721 DsFsmInstrRrr(EmitI(0x30, base, rt, imm16), rt, base, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700722}
723
724void MipsAssembler::ScR2(Register rt, Register base, int16_t imm16) {
725 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700726 DsFsmInstrRrr(EmitI(0x38, base, rt, imm16), rt, rt, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700727}
728
729void MipsAssembler::LlR6(Register rt, Register base, int16_t imm9) {
730 CHECK(IsR6());
731 CHECK(IsInt<9>(imm9));
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700732 DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36), rt, base, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700733}
734
735void MipsAssembler::ScR6(Register rt, Register base, int16_t imm9) {
736 CHECK(IsR6());
737 CHECK(IsInt<9>(imm9));
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700738 DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26), rt, rt, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700739}
740
jeffhao7fbee072012-08-24 17:56:54 -0700741void MipsAssembler::Slt(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700742 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2a), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700743}
744
745void MipsAssembler::Sltu(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700746 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2b), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700747}
748
749void MipsAssembler::Slti(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700750 DsFsmInstrRrr(EmitI(0xa, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700751}
752
753void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700754 DsFsmInstrRrr(EmitI(0xb, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700755}
756
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200757void MipsAssembler::B(uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700758 DsFsmInstrNop(EmitI(0x4, static_cast<Register>(0), static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200759}
760
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700761void MipsAssembler::Bal(uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700762 DsFsmInstrNop(EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x11), imm16));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700763}
764
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200765void MipsAssembler::Beq(Register rs, Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700766 DsFsmInstrNop(EmitI(0x4, rs, rt, imm16));
jeffhao7fbee072012-08-24 17:56:54 -0700767}
768
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200769void MipsAssembler::Bne(Register rs, Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700770 DsFsmInstrNop(EmitI(0x5, rs, rt, imm16));
jeffhao7fbee072012-08-24 17:56:54 -0700771}
772
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200773void MipsAssembler::Beqz(Register rt, uint16_t imm16) {
774 Beq(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700775}
776
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200777void MipsAssembler::Bnez(Register rt, uint16_t imm16) {
778 Bne(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700779}
780
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200781void MipsAssembler::Bltz(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700782 DsFsmInstrNop(EmitI(0x1, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200783}
784
785void MipsAssembler::Bgez(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700786 DsFsmInstrNop(EmitI(0x1, rt, static_cast<Register>(0x1), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200787}
788
789void MipsAssembler::Blez(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700790 DsFsmInstrNop(EmitI(0x6, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200791}
792
793void MipsAssembler::Bgtz(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700794 DsFsmInstrNop(EmitI(0x7, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200795}
796
Chris Larsenb74353a2015-11-20 09:07:09 -0800797void MipsAssembler::Bc1f(uint16_t imm16) {
798 Bc1f(0, imm16);
799}
800
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800801void MipsAssembler::Bc1f(int cc, uint16_t imm16) {
802 CHECK(!IsR6());
803 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700804 DsFsmInstrNop(EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>(cc << 2), imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800805}
806
Chris Larsenb74353a2015-11-20 09:07:09 -0800807void MipsAssembler::Bc1t(uint16_t imm16) {
808 Bc1t(0, imm16);
809}
810
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800811void MipsAssembler::Bc1t(int cc, uint16_t imm16) {
812 CHECK(!IsR6());
813 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700814 DsFsmInstrNop(EmitI(0x11,
815 static_cast<Register>(0x8),
816 static_cast<Register>((cc << 2) | 1),
817 imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800818}
819
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200820void MipsAssembler::J(uint32_t addr26) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700821 DsFsmInstrNop(EmitI26(0x2, addr26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200822}
823
824void MipsAssembler::Jal(uint32_t addr26) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700825 DsFsmInstrNop(EmitI26(0x3, addr26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200826}
827
828void MipsAssembler::Jalr(Register rd, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700829 uint32_t last_instruction = delay_slot_.instruction_;
830 bool exchange = (last_instruction != 0 &&
831 (delay_slot_.gpr_outs_mask_ & (1u << rs)) == 0 &&
832 ((delay_slot_.gpr_ins_mask_ | delay_slot_.gpr_outs_mask_) & (1u << rd)) == 0);
833 if (exchange) {
834 // The last instruction cannot be used in a different delay slot,
835 // do not commit the label before it (if any).
836 DsFsmDropLabel();
837 }
838 DsFsmInstrNop(EmitR(0, rs, static_cast<Register>(0), rd, 0, 0x09));
839 if (exchange) {
840 // Exchange the last two instructions in the assembler buffer.
841 size_t size = buffer_.Size();
842 CHECK_GE(size, 2 * sizeof(uint32_t));
843 size_t pos1 = size - 2 * sizeof(uint32_t);
844 size_t pos2 = size - sizeof(uint32_t);
845 uint32_t instr1 = buffer_.Load<uint32_t>(pos1);
846 uint32_t instr2 = buffer_.Load<uint32_t>(pos2);
847 CHECK_EQ(instr1, last_instruction);
848 buffer_.Store<uint32_t>(pos1, instr2);
849 buffer_.Store<uint32_t>(pos2, instr1);
850 } else if (reordering_) {
851 Nop();
852 }
jeffhao7fbee072012-08-24 17:56:54 -0700853}
854
855void MipsAssembler::Jalr(Register rs) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200856 Jalr(RA, rs);
857}
858
859void MipsAssembler::Jr(Register rs) {
860 Jalr(ZERO, rs);
861}
862
863void MipsAssembler::Nal() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700864 DsFsmInstrNop(EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x10), 0));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200865}
866
867void MipsAssembler::Auipc(Register rs, uint16_t imm16) {
868 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700869 DsFsmInstrNop(EmitI(0x3B, rs, static_cast<Register>(0x1E), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200870}
871
872void MipsAssembler::Addiupc(Register rs, uint32_t imm19) {
873 CHECK(IsR6());
874 CHECK(IsUint<19>(imm19)) << imm19;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700875 DsFsmInstrNop(EmitI21(0x3B, rs, imm19));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200876}
877
878void MipsAssembler::Bc(uint32_t imm26) {
879 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700880 DsFsmInstrNop(EmitI26(0x32, imm26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200881}
882
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700883void MipsAssembler::Balc(uint32_t imm26) {
884 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700885 DsFsmInstrNop(EmitI26(0x3A, imm26));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700886}
887
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200888void MipsAssembler::Jic(Register rt, uint16_t imm16) {
889 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700890 DsFsmInstrNop(EmitI(0x36, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200891}
892
893void MipsAssembler::Jialc(Register rt, uint16_t imm16) {
894 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700895 DsFsmInstrNop(EmitI(0x3E, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200896}
897
898void MipsAssembler::Bltc(Register rs, Register rt, uint16_t imm16) {
899 CHECK(IsR6());
900 CHECK_NE(rs, ZERO);
901 CHECK_NE(rt, ZERO);
902 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700903 DsFsmInstrNop(EmitI(0x17, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200904}
905
906void MipsAssembler::Bltzc(Register rt, uint16_t imm16) {
907 CHECK(IsR6());
908 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700909 DsFsmInstrNop(EmitI(0x17, rt, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200910}
911
912void MipsAssembler::Bgtzc(Register rt, uint16_t imm16) {
913 CHECK(IsR6());
914 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700915 DsFsmInstrNop(EmitI(0x17, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200916}
917
918void MipsAssembler::Bgec(Register rs, Register rt, uint16_t imm16) {
919 CHECK(IsR6());
920 CHECK_NE(rs, ZERO);
921 CHECK_NE(rt, ZERO);
922 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700923 DsFsmInstrNop(EmitI(0x16, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200924}
925
926void MipsAssembler::Bgezc(Register rt, uint16_t imm16) {
927 CHECK(IsR6());
928 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700929 DsFsmInstrNop(EmitI(0x16, rt, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200930}
931
932void MipsAssembler::Blezc(Register rt, uint16_t imm16) {
933 CHECK(IsR6());
934 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700935 DsFsmInstrNop(EmitI(0x16, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200936}
937
938void MipsAssembler::Bltuc(Register rs, Register rt, uint16_t imm16) {
939 CHECK(IsR6());
940 CHECK_NE(rs, ZERO);
941 CHECK_NE(rt, ZERO);
942 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700943 DsFsmInstrNop(EmitI(0x7, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200944}
945
946void MipsAssembler::Bgeuc(Register rs, Register rt, uint16_t imm16) {
947 CHECK(IsR6());
948 CHECK_NE(rs, ZERO);
949 CHECK_NE(rt, ZERO);
950 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700951 DsFsmInstrNop(EmitI(0x6, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200952}
953
954void MipsAssembler::Beqc(Register rs, Register rt, uint16_t imm16) {
955 CHECK(IsR6());
956 CHECK_NE(rs, ZERO);
957 CHECK_NE(rt, ZERO);
958 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700959 DsFsmInstrNop(EmitI(0x8, std::min(rs, rt), std::max(rs, rt), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200960}
961
962void MipsAssembler::Bnec(Register rs, Register rt, uint16_t imm16) {
963 CHECK(IsR6());
964 CHECK_NE(rs, ZERO);
965 CHECK_NE(rt, ZERO);
966 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700967 DsFsmInstrNop(EmitI(0x18, std::min(rs, rt), std::max(rs, rt), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200968}
969
970void MipsAssembler::Beqzc(Register rs, uint32_t imm21) {
971 CHECK(IsR6());
972 CHECK_NE(rs, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700973 DsFsmInstrNop(EmitI21(0x36, rs, imm21));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200974}
975
976void MipsAssembler::Bnezc(Register rs, uint32_t imm21) {
977 CHECK(IsR6());
978 CHECK_NE(rs, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700979 DsFsmInstrNop(EmitI21(0x3E, rs, imm21));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200980}
981
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800982void MipsAssembler::Bc1eqz(FRegister ft, uint16_t imm16) {
983 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700984 DsFsmInstrNop(EmitFI(0x11, 0x9, ft, imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800985}
986
987void MipsAssembler::Bc1nez(FRegister ft, uint16_t imm16) {
988 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700989 DsFsmInstrNop(EmitFI(0x11, 0xD, ft, imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800990}
991
992void MipsAssembler::EmitBcondR2(BranchCondition cond, Register rs, Register rt, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200993 switch (cond) {
994 case kCondLTZ:
995 CHECK_EQ(rt, ZERO);
996 Bltz(rs, imm16);
997 break;
998 case kCondGEZ:
999 CHECK_EQ(rt, ZERO);
1000 Bgez(rs, imm16);
1001 break;
1002 case kCondLEZ:
1003 CHECK_EQ(rt, ZERO);
1004 Blez(rs, imm16);
1005 break;
1006 case kCondGTZ:
1007 CHECK_EQ(rt, ZERO);
1008 Bgtz(rs, imm16);
1009 break;
1010 case kCondEQ:
1011 Beq(rs, rt, imm16);
1012 break;
1013 case kCondNE:
1014 Bne(rs, rt, imm16);
1015 break;
1016 case kCondEQZ:
1017 CHECK_EQ(rt, ZERO);
1018 Beqz(rs, imm16);
1019 break;
1020 case kCondNEZ:
1021 CHECK_EQ(rt, ZERO);
1022 Bnez(rs, imm16);
1023 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001024 case kCondF:
1025 CHECK_EQ(rt, ZERO);
1026 Bc1f(static_cast<int>(rs), imm16);
1027 break;
1028 case kCondT:
1029 CHECK_EQ(rt, ZERO);
1030 Bc1t(static_cast<int>(rs), imm16);
1031 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001032 case kCondLT:
1033 case kCondGE:
1034 case kCondLE:
1035 case kCondGT:
1036 case kCondLTU:
1037 case kCondGEU:
1038 case kUncond:
1039 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
1040 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
1041 LOG(FATAL) << "Unexpected branch condition " << cond;
1042 UNREACHABLE();
1043 }
1044}
1045
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001046void MipsAssembler::EmitBcondR6(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001047 switch (cond) {
1048 case kCondLT:
1049 Bltc(rs, rt, imm16_21);
1050 break;
1051 case kCondGE:
1052 Bgec(rs, rt, imm16_21);
1053 break;
1054 case kCondLE:
1055 Bgec(rt, rs, imm16_21);
1056 break;
1057 case kCondGT:
1058 Bltc(rt, rs, imm16_21);
1059 break;
1060 case kCondLTZ:
1061 CHECK_EQ(rt, ZERO);
1062 Bltzc(rs, imm16_21);
1063 break;
1064 case kCondGEZ:
1065 CHECK_EQ(rt, ZERO);
1066 Bgezc(rs, imm16_21);
1067 break;
1068 case kCondLEZ:
1069 CHECK_EQ(rt, ZERO);
1070 Blezc(rs, imm16_21);
1071 break;
1072 case kCondGTZ:
1073 CHECK_EQ(rt, ZERO);
1074 Bgtzc(rs, imm16_21);
1075 break;
1076 case kCondEQ:
1077 Beqc(rs, rt, imm16_21);
1078 break;
1079 case kCondNE:
1080 Bnec(rs, rt, imm16_21);
1081 break;
1082 case kCondEQZ:
1083 CHECK_EQ(rt, ZERO);
1084 Beqzc(rs, imm16_21);
1085 break;
1086 case kCondNEZ:
1087 CHECK_EQ(rt, ZERO);
1088 Bnezc(rs, imm16_21);
1089 break;
1090 case kCondLTU:
1091 Bltuc(rs, rt, imm16_21);
1092 break;
1093 case kCondGEU:
1094 Bgeuc(rs, rt, imm16_21);
1095 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001096 case kCondF:
1097 CHECK_EQ(rt, ZERO);
1098 Bc1eqz(static_cast<FRegister>(rs), imm16_21);
1099 break;
1100 case kCondT:
1101 CHECK_EQ(rt, ZERO);
1102 Bc1nez(static_cast<FRegister>(rs), imm16_21);
1103 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001104 case kUncond:
1105 LOG(FATAL) << "Unexpected branch condition " << cond;
1106 UNREACHABLE();
1107 }
jeffhao7fbee072012-08-24 17:56:54 -07001108}
1109
1110void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001111 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x0), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001112}
1113
1114void MipsAssembler::SubS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001115 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001116}
1117
1118void MipsAssembler::MulS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001119 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x2), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001120}
1121
1122void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001123 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x3), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001124}
1125
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001126void MipsAssembler::AddD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001127 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x0), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001128}
1129
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001130void MipsAssembler::SubD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001131 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001132}
1133
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001134void MipsAssembler::MulD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001135 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x2), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001136}
1137
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001138void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001139 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x3), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001140}
1141
Chris Larsenb74353a2015-11-20 09:07:09 -08001142void MipsAssembler::SqrtS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001143 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x4), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001144}
1145
1146void MipsAssembler::SqrtD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001147 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x4), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001148}
1149
1150void MipsAssembler::AbsS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001151 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x5), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001152}
1153
1154void MipsAssembler::AbsD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001155 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x5), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001156}
1157
jeffhao7fbee072012-08-24 17:56:54 -07001158void MipsAssembler::MovS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001159 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6), fd, fs, fs);
jeffhao7fbee072012-08-24 17:56:54 -07001160}
1161
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001162void MipsAssembler::MovD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001163 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x6), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001164}
1165
1166void MipsAssembler::NegS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001167 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x7), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001168}
1169
1170void MipsAssembler::NegD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001171 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001172}
1173
Chris Larsenb74353a2015-11-20 09:07:09 -08001174void MipsAssembler::CunS(FRegister fs, FRegister ft) {
1175 CunS(0, fs, ft);
1176}
1177
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001178void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) {
1179 CHECK(!IsR6());
1180 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001181 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x31), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001182}
1183
Chris Larsenb74353a2015-11-20 09:07:09 -08001184void MipsAssembler::CeqS(FRegister fs, FRegister ft) {
1185 CeqS(0, fs, ft);
1186}
1187
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001188void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) {
1189 CHECK(!IsR6());
1190 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001191 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x32), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001192}
1193
Chris Larsenb74353a2015-11-20 09:07:09 -08001194void MipsAssembler::CueqS(FRegister fs, FRegister ft) {
1195 CueqS(0, fs, ft);
1196}
1197
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001198void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) {
1199 CHECK(!IsR6());
1200 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001201 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x33), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001202}
1203
Chris Larsenb74353a2015-11-20 09:07:09 -08001204void MipsAssembler::ColtS(FRegister fs, FRegister ft) {
1205 ColtS(0, fs, ft);
1206}
1207
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001208void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) {
1209 CHECK(!IsR6());
1210 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001211 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x34), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001212}
1213
Chris Larsenb74353a2015-11-20 09:07:09 -08001214void MipsAssembler::CultS(FRegister fs, FRegister ft) {
1215 CultS(0, fs, ft);
1216}
1217
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001218void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) {
1219 CHECK(!IsR6());
1220 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001221 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x35), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001222}
1223
Chris Larsenb74353a2015-11-20 09:07:09 -08001224void MipsAssembler::ColeS(FRegister fs, FRegister ft) {
1225 ColeS(0, fs, ft);
1226}
1227
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001228void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) {
1229 CHECK(!IsR6());
1230 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001231 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x36), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001232}
1233
Chris Larsenb74353a2015-11-20 09:07:09 -08001234void MipsAssembler::CuleS(FRegister fs, FRegister ft) {
1235 CuleS(0, fs, ft);
1236}
1237
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001238void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) {
1239 CHECK(!IsR6());
1240 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001241 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x37), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001242}
1243
Chris Larsenb74353a2015-11-20 09:07:09 -08001244void MipsAssembler::CunD(FRegister fs, FRegister ft) {
1245 CunD(0, fs, ft);
1246}
1247
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001248void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) {
1249 CHECK(!IsR6());
1250 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001251 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x31), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001252}
1253
Chris Larsenb74353a2015-11-20 09:07:09 -08001254void MipsAssembler::CeqD(FRegister fs, FRegister ft) {
1255 CeqD(0, fs, ft);
1256}
1257
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001258void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) {
1259 CHECK(!IsR6());
1260 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001261 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x32), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001262}
1263
Chris Larsenb74353a2015-11-20 09:07:09 -08001264void MipsAssembler::CueqD(FRegister fs, FRegister ft) {
1265 CueqD(0, fs, ft);
1266}
1267
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001268void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) {
1269 CHECK(!IsR6());
1270 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001271 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x33), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001272}
1273
Chris Larsenb74353a2015-11-20 09:07:09 -08001274void MipsAssembler::ColtD(FRegister fs, FRegister ft) {
1275 ColtD(0, fs, ft);
1276}
1277
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001278void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) {
1279 CHECK(!IsR6());
1280 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001281 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x34), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001282}
1283
Chris Larsenb74353a2015-11-20 09:07:09 -08001284void MipsAssembler::CultD(FRegister fs, FRegister ft) {
1285 CultD(0, fs, ft);
1286}
1287
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001288void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) {
1289 CHECK(!IsR6());
1290 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001291 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x35), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001292}
1293
Chris Larsenb74353a2015-11-20 09:07:09 -08001294void MipsAssembler::ColeD(FRegister fs, FRegister ft) {
1295 ColeD(0, fs, ft);
1296}
1297
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001298void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) {
1299 CHECK(!IsR6());
1300 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001301 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x36), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001302}
1303
Chris Larsenb74353a2015-11-20 09:07:09 -08001304void MipsAssembler::CuleD(FRegister fs, FRegister ft) {
1305 CuleD(0, fs, ft);
1306}
1307
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001308void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) {
1309 CHECK(!IsR6());
1310 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001311 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x37), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001312}
1313
1314void MipsAssembler::CmpUnS(FRegister fd, FRegister fs, FRegister ft) {
1315 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001316 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x01), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001317}
1318
1319void MipsAssembler::CmpEqS(FRegister fd, FRegister fs, FRegister ft) {
1320 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001321 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x02), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001322}
1323
1324void MipsAssembler::CmpUeqS(FRegister fd, FRegister fs, FRegister ft) {
1325 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001326 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x03), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001327}
1328
1329void MipsAssembler::CmpLtS(FRegister fd, FRegister fs, FRegister ft) {
1330 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001331 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x04), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001332}
1333
1334void MipsAssembler::CmpUltS(FRegister fd, FRegister fs, FRegister ft) {
1335 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001336 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x05), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001337}
1338
1339void MipsAssembler::CmpLeS(FRegister fd, FRegister fs, FRegister ft) {
1340 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001341 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x06), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001342}
1343
1344void MipsAssembler::CmpUleS(FRegister fd, FRegister fs, FRegister ft) {
1345 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001346 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x07), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001347}
1348
1349void MipsAssembler::CmpOrS(FRegister fd, FRegister fs, FRegister ft) {
1350 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001351 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x11), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001352}
1353
1354void MipsAssembler::CmpUneS(FRegister fd, FRegister fs, FRegister ft) {
1355 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001356 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x12), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001357}
1358
1359void MipsAssembler::CmpNeS(FRegister fd, FRegister fs, FRegister ft) {
1360 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001361 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x13), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001362}
1363
1364void MipsAssembler::CmpUnD(FRegister fd, FRegister fs, FRegister ft) {
1365 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001366 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x01), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001367}
1368
1369void MipsAssembler::CmpEqD(FRegister fd, FRegister fs, FRegister ft) {
1370 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001371 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x02), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001372}
1373
1374void MipsAssembler::CmpUeqD(FRegister fd, FRegister fs, FRegister ft) {
1375 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001376 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x03), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001377}
1378
1379void MipsAssembler::CmpLtD(FRegister fd, FRegister fs, FRegister ft) {
1380 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001381 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x04), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001382}
1383
1384void MipsAssembler::CmpUltD(FRegister fd, FRegister fs, FRegister ft) {
1385 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001386 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x05), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001387}
1388
1389void MipsAssembler::CmpLeD(FRegister fd, FRegister fs, FRegister ft) {
1390 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001391 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x06), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001392}
1393
1394void MipsAssembler::CmpUleD(FRegister fd, FRegister fs, FRegister ft) {
1395 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001396 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x07), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001397}
1398
1399void MipsAssembler::CmpOrD(FRegister fd, FRegister fs, FRegister ft) {
1400 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001401 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x11), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001402}
1403
1404void MipsAssembler::CmpUneD(FRegister fd, FRegister fs, FRegister ft) {
1405 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001406 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x12), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001407}
1408
1409void MipsAssembler::CmpNeD(FRegister fd, FRegister fs, FRegister ft) {
1410 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001411 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x13), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001412}
1413
1414void MipsAssembler::Movf(Register rd, Register rs, int cc) {
1415 CHECK(!IsR6());
1416 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001417 DsFsmInstrRrrc(EmitR(0, rs, static_cast<Register>(cc << 2), rd, 0, 0x01), rd, rs, cc);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001418}
1419
1420void MipsAssembler::Movt(Register rd, Register rs, int cc) {
1421 CHECK(!IsR6());
1422 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001423 DsFsmInstrRrrc(EmitR(0, rs, static_cast<Register>((cc << 2) | 1), rd, 0, 0x01), rd, rs, cc);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001424}
1425
Chris Larsenb74353a2015-11-20 09:07:09 -08001426void MipsAssembler::MovfS(FRegister fd, FRegister fs, int cc) {
1427 CHECK(!IsR6());
1428 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001429 DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast<FRegister>(cc << 2), fs, fd, 0x11), fd, fs, cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001430}
1431
1432void MipsAssembler::MovfD(FRegister fd, FRegister fs, int cc) {
1433 CHECK(!IsR6());
1434 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001435 DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast<FRegister>(cc << 2), fs, fd, 0x11), fd, fs, cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001436}
1437
1438void MipsAssembler::MovtS(FRegister fd, FRegister fs, int cc) {
1439 CHECK(!IsR6());
1440 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001441 DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11),
1442 fd,
1443 fs,
1444 cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001445}
1446
1447void MipsAssembler::MovtD(FRegister fd, FRegister fs, int cc) {
1448 CHECK(!IsR6());
1449 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001450 DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11),
1451 fd,
1452 fs,
1453 cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001454}
1455
Alexey Frunze674b9ee2016-09-20 14:54:15 -07001456void MipsAssembler::MovzS(FRegister fd, FRegister fs, Register rt) {
1457 CHECK(!IsR6());
1458 DsFsmInstrFffr(EmitFR(0x11, 0x10, static_cast<FRegister>(rt), fs, fd, 0x12), fd, fs, rt);
1459}
1460
1461void MipsAssembler::MovzD(FRegister fd, FRegister fs, Register rt) {
1462 CHECK(!IsR6());
1463 DsFsmInstrFffr(EmitFR(0x11, 0x11, static_cast<FRegister>(rt), fs, fd, 0x12), fd, fs, rt);
1464}
1465
1466void MipsAssembler::MovnS(FRegister fd, FRegister fs, Register rt) {
1467 CHECK(!IsR6());
1468 DsFsmInstrFffr(EmitFR(0x11, 0x10, static_cast<FRegister>(rt), fs, fd, 0x13), fd, fs, rt);
1469}
1470
1471void MipsAssembler::MovnD(FRegister fd, FRegister fs, Register rt) {
1472 CHECK(!IsR6());
1473 DsFsmInstrFffr(EmitFR(0x11, 0x11, static_cast<FRegister>(rt), fs, fd, 0x13), fd, fs, rt);
1474}
1475
Chris Larsenb74353a2015-11-20 09:07:09 -08001476void MipsAssembler::SelS(FRegister fd, FRegister fs, FRegister ft) {
1477 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001478 DsFsmInstrFfff(EmitFR(0x11, 0x10, ft, fs, fd, 0x10), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001479}
1480
1481void MipsAssembler::SelD(FRegister fd, FRegister fs, FRegister ft) {
1482 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001483 DsFsmInstrFfff(EmitFR(0x11, 0x11, ft, fs, fd, 0x10), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001484}
1485
Alexey Frunze674b9ee2016-09-20 14:54:15 -07001486void MipsAssembler::SeleqzS(FRegister fd, FRegister fs, FRegister ft) {
1487 CHECK(IsR6());
1488 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x14), fd, fs, ft);
1489}
1490
1491void MipsAssembler::SeleqzD(FRegister fd, FRegister fs, FRegister ft) {
1492 CHECK(IsR6());
1493 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x14), fd, fs, ft);
1494}
1495
1496void MipsAssembler::SelnezS(FRegister fd, FRegister fs, FRegister ft) {
1497 CHECK(IsR6());
1498 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x17), fd, fs, ft);
1499}
1500
1501void MipsAssembler::SelnezD(FRegister fd, FRegister fs, FRegister ft) {
1502 CHECK(IsR6());
1503 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x17), fd, fs, ft);
1504}
1505
Chris Larsenb74353a2015-11-20 09:07:09 -08001506void MipsAssembler::ClassS(FRegister fd, FRegister fs) {
1507 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001508 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x1b), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001509}
1510
1511void MipsAssembler::ClassD(FRegister fd, FRegister fs) {
1512 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001513 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x1b), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001514}
1515
1516void MipsAssembler::MinS(FRegister fd, FRegister fs, FRegister ft) {
1517 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001518 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1c), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001519}
1520
1521void MipsAssembler::MinD(FRegister fd, FRegister fs, FRegister ft) {
1522 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001523 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1c), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001524}
1525
1526void MipsAssembler::MaxS(FRegister fd, FRegister fs, FRegister ft) {
1527 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001528 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1e), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001529}
1530
1531void MipsAssembler::MaxD(FRegister fd, FRegister fs, FRegister ft) {
1532 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001533 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1e), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001534}
1535
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001536void MipsAssembler::TruncLS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001537 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x09), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001538}
1539
1540void MipsAssembler::TruncLD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001541 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x09), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001542}
1543
1544void MipsAssembler::TruncWS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001545 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x0D), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001546}
1547
1548void MipsAssembler::TruncWD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001549 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x0D), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001550}
1551
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001552void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001553 DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001554}
1555
1556void MipsAssembler::Cvtdw(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001557 DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001558}
1559
1560void MipsAssembler::Cvtsd(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001561 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001562}
1563
1564void MipsAssembler::Cvtds(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001565 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
jeffhao7fbee072012-08-24 17:56:54 -07001566}
1567
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001568void MipsAssembler::Cvtsl(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001569 DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001570}
1571
1572void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001573 DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001574}
1575
Chris Larsenb74353a2015-11-20 09:07:09 -08001576void MipsAssembler::FloorWS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001577 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0xf), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001578}
1579
1580void MipsAssembler::FloorWD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001581 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0xf), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001582}
1583
jeffhao7fbee072012-08-24 17:56:54 -07001584void MipsAssembler::Mfc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001585 DsFsmInstrRf(EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1586 rt,
1587 fs);
jeffhao7fbee072012-08-24 17:56:54 -07001588}
1589
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001590void MipsAssembler::Mtc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001591 DsFsmInstrFr(EmitFR(0x11, 0x04, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1592 fs,
1593 rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001594}
1595
1596void MipsAssembler::Mfhc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001597 DsFsmInstrRf(EmitFR(0x11, 0x03, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1598 rt,
1599 fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001600}
1601
1602void MipsAssembler::Mthc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001603 DsFsmInstrFr(EmitFR(0x11, 0x07, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1604 fs,
1605 rt);
jeffhao7fbee072012-08-24 17:56:54 -07001606}
1607
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001608void MipsAssembler::MoveFromFpuHigh(Register rt, FRegister fs) {
1609 if (Is32BitFPU()) {
1610 CHECK_EQ(fs % 2, 0) << fs;
1611 Mfc1(rt, static_cast<FRegister>(fs + 1));
1612 } else {
1613 Mfhc1(rt, fs);
1614 }
1615}
1616
1617void MipsAssembler::MoveToFpuHigh(Register rt, FRegister fs) {
1618 if (Is32BitFPU()) {
1619 CHECK_EQ(fs % 2, 0) << fs;
1620 Mtc1(rt, static_cast<FRegister>(fs + 1));
1621 } else {
1622 Mthc1(rt, fs);
1623 }
1624}
1625
jeffhao7fbee072012-08-24 17:56:54 -07001626void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001627 DsFsmInstrFr(EmitI(0x31, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001628}
1629
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001630void MipsAssembler::Ldc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001631 DsFsmInstrFr(EmitI(0x35, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001632}
1633
1634void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001635 DsFsmInstrFR(EmitI(0x39, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001636}
1637
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001638void MipsAssembler::Sdc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001639 DsFsmInstrFR(EmitI(0x3d, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001640}
1641
1642void MipsAssembler::Break() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001643 DsFsmInstrNop(EmitR(0, ZERO, ZERO, ZERO, 0, 0xD));
jeffhao7fbee072012-08-24 17:56:54 -07001644}
1645
jeffhao07030602012-09-26 14:33:14 -07001646void MipsAssembler::Nop() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001647 DsFsmInstrNop(EmitR(0x0, ZERO, ZERO, ZERO, 0, 0x0));
1648}
1649
1650void MipsAssembler::NopIfNoReordering() {
1651 if (!reordering_) {
1652 Nop();
1653 }
jeffhao07030602012-09-26 14:33:14 -07001654}
1655
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001656void MipsAssembler::Move(Register rd, Register rs) {
1657 Or(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001658}
1659
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001660void MipsAssembler::Clear(Register rd) {
1661 Move(rd, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001662}
1663
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001664void MipsAssembler::Not(Register rd, Register rs) {
1665 Nor(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001666}
1667
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001668void MipsAssembler::Push(Register rs) {
1669 IncreaseFrameSize(kMipsWordSize);
1670 Sw(rs, SP, 0);
jeffhao7fbee072012-08-24 17:56:54 -07001671}
1672
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001673void MipsAssembler::Pop(Register rd) {
1674 Lw(rd, SP, 0);
1675 DecreaseFrameSize(kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07001676}
1677
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001678void MipsAssembler::PopAndReturn(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001679 bool reordering = SetReorder(false);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001680 Lw(rd, SP, 0);
1681 Jr(rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001682 DecreaseFrameSize(kMipsWordSize); // Single instruction in delay slot.
1683 SetReorder(reordering);
jeffhao7fbee072012-08-24 17:56:54 -07001684}
1685
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001686void MipsAssembler::LoadConst32(Register rd, int32_t value) {
1687 if (IsUint<16>(value)) {
1688 // Use OR with (unsigned) immediate to encode 16b unsigned int.
1689 Ori(rd, ZERO, value);
1690 } else if (IsInt<16>(value)) {
1691 // Use ADD with (signed) immediate to encode 16b signed int.
1692 Addiu(rd, ZERO, value);
jeffhao7fbee072012-08-24 17:56:54 -07001693 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001694 Lui(rd, High16Bits(value));
1695 if (value & 0xFFFF)
1696 Ori(rd, rd, Low16Bits(value));
1697 }
1698}
1699
1700void MipsAssembler::LoadConst64(Register reg_hi, Register reg_lo, int64_t value) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001701 uint32_t low = Low32Bits(value);
1702 uint32_t high = High32Bits(value);
1703 LoadConst32(reg_lo, low);
1704 if (high != low) {
1705 LoadConst32(reg_hi, high);
1706 } else {
1707 Move(reg_hi, reg_lo);
1708 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001709}
1710
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001711void MipsAssembler::LoadSConst32(FRegister r, int32_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001712 if (value == 0) {
1713 temp = ZERO;
1714 } else {
1715 LoadConst32(temp, value);
1716 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001717 Mtc1(temp, r);
1718}
1719
1720void MipsAssembler::LoadDConst64(FRegister rd, int64_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001721 uint32_t low = Low32Bits(value);
1722 uint32_t high = High32Bits(value);
1723 if (low == 0) {
1724 Mtc1(ZERO, rd);
1725 } else {
1726 LoadConst32(temp, low);
1727 Mtc1(temp, rd);
1728 }
1729 if (high == 0) {
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001730 MoveToFpuHigh(ZERO, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001731 } else {
1732 LoadConst32(temp, high);
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001733 MoveToFpuHigh(temp, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001734 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001735}
1736
1737void MipsAssembler::Addiu32(Register rt, Register rs, int32_t value, Register temp) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001738 CHECK_NE(rs, temp); // Must not overwrite the register `rs` while loading `value`.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001739 if (IsInt<16>(value)) {
1740 Addiu(rt, rs, value);
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001741 } else if (IsR6()) {
1742 int16_t high = High16Bits(value);
1743 int16_t low = Low16Bits(value);
1744 high += (low < 0) ? 1 : 0; // Account for sign extension in addiu.
1745 if (low != 0) {
1746 Aui(temp, rs, high);
1747 Addiu(rt, temp, low);
1748 } else {
1749 Aui(rt, rs, high);
1750 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001751 } else {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001752 // Do not load the whole 32-bit `value` if it can be represented as
1753 // a sum of two 16-bit signed values. This can save an instruction.
1754 constexpr int32_t kMinValueForSimpleAdjustment = std::numeric_limits<int16_t>::min() * 2;
1755 constexpr int32_t kMaxValueForSimpleAdjustment = std::numeric_limits<int16_t>::max() * 2;
1756 if (0 <= value && value <= kMaxValueForSimpleAdjustment) {
1757 Addiu(temp, rs, kMaxValueForSimpleAdjustment / 2);
1758 Addiu(rt, temp, value - kMaxValueForSimpleAdjustment / 2);
1759 } else if (kMinValueForSimpleAdjustment <= value && value < 0) {
1760 Addiu(temp, rs, kMinValueForSimpleAdjustment / 2);
1761 Addiu(rt, temp, value - kMinValueForSimpleAdjustment / 2);
1762 } else {
1763 // Now that all shorter options have been exhausted, load the full 32-bit value.
1764 LoadConst32(temp, value);
1765 Addu(rt, rs, temp);
1766 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001767 }
1768}
1769
1770void MipsAssembler::Branch::InitShortOrLong(MipsAssembler::Branch::OffsetBits offset_size,
1771 MipsAssembler::Branch::Type short_type,
1772 MipsAssembler::Branch::Type long_type) {
1773 type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type;
1774}
1775
Alexey Frunze96b66822016-09-10 02:32:44 -07001776void MipsAssembler::Branch::InitializeType(Type initial_type, bool is_r6) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001777 OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_);
1778 if (is_r6) {
1779 // R6
Alexey Frunze96b66822016-09-10 02:32:44 -07001780 switch (initial_type) {
1781 case kLabel:
1782 CHECK(!IsResolved());
1783 type_ = kR6Label;
1784 break;
1785 case kLiteral:
1786 CHECK(!IsResolved());
1787 type_ = kR6Literal;
1788 break;
1789 case kCall:
1790 InitShortOrLong(offset_size, kR6Call, kR6LongCall);
1791 break;
1792 case kCondBranch:
1793 switch (condition_) {
1794 case kUncond:
1795 InitShortOrLong(offset_size, kR6UncondBranch, kR6LongUncondBranch);
1796 break;
1797 case kCondEQZ:
1798 case kCondNEZ:
1799 // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions.
1800 type_ = (offset_size <= kOffset23) ? kR6CondBranch : kR6LongCondBranch;
1801 break;
1802 default:
1803 InitShortOrLong(offset_size, kR6CondBranch, kR6LongCondBranch);
1804 break;
1805 }
1806 break;
1807 default:
1808 LOG(FATAL) << "Unexpected branch type " << initial_type;
1809 UNREACHABLE();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001810 }
1811 } else {
1812 // R2
Alexey Frunze96b66822016-09-10 02:32:44 -07001813 switch (initial_type) {
1814 case kLabel:
1815 CHECK(!IsResolved());
1816 type_ = kLabel;
1817 break;
1818 case kLiteral:
1819 CHECK(!IsResolved());
1820 type_ = kLiteral;
1821 break;
1822 case kCall:
1823 InitShortOrLong(offset_size, kCall, kLongCall);
1824 break;
1825 case kCondBranch:
1826 switch (condition_) {
1827 case kUncond:
1828 InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch);
1829 break;
1830 default:
1831 InitShortOrLong(offset_size, kCondBranch, kLongCondBranch);
1832 break;
1833 }
1834 break;
1835 default:
1836 LOG(FATAL) << "Unexpected branch type " << initial_type;
1837 UNREACHABLE();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001838 }
1839 }
1840 old_type_ = type_;
1841}
1842
1843bool MipsAssembler::Branch::IsNop(BranchCondition condition, Register lhs, Register rhs) {
1844 switch (condition) {
1845 case kCondLT:
1846 case kCondGT:
1847 case kCondNE:
1848 case kCondLTU:
1849 return lhs == rhs;
1850 default:
1851 return false;
1852 }
1853}
1854
1855bool MipsAssembler::Branch::IsUncond(BranchCondition condition, Register lhs, Register rhs) {
1856 switch (condition) {
1857 case kUncond:
1858 return true;
1859 case kCondGE:
1860 case kCondLE:
1861 case kCondEQ:
1862 case kCondGEU:
1863 return lhs == rhs;
1864 default:
1865 return false;
1866 }
1867}
1868
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001869MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target, bool is_call)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001870 : old_location_(location),
1871 location_(location),
1872 target_(target),
1873 lhs_reg_(0),
1874 rhs_reg_(0),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001875 condition_(kUncond),
1876 delayed_instruction_(kUnfilledDelaySlot) {
Alexey Frunze96b66822016-09-10 02:32:44 -07001877 InitializeType((is_call ? kCall : kCondBranch), is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001878}
1879
1880MipsAssembler::Branch::Branch(bool is_r6,
1881 uint32_t location,
1882 uint32_t target,
1883 MipsAssembler::BranchCondition condition,
1884 Register lhs_reg,
1885 Register rhs_reg)
1886 : old_location_(location),
1887 location_(location),
1888 target_(target),
1889 lhs_reg_(lhs_reg),
1890 rhs_reg_(rhs_reg),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001891 condition_(condition),
1892 delayed_instruction_(kUnfilledDelaySlot) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001893 CHECK_NE(condition, kUncond);
1894 switch (condition) {
1895 case kCondLT:
1896 case kCondGE:
1897 case kCondLE:
1898 case kCondGT:
1899 case kCondLTU:
1900 case kCondGEU:
1901 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
1902 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
1903 // We leave this up to the caller.
1904 CHECK(is_r6);
1905 FALLTHROUGH_INTENDED;
1906 case kCondEQ:
1907 case kCondNE:
1908 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1909 // To compare with 0, use dedicated kCond*Z conditions.
1910 CHECK_NE(lhs_reg, ZERO);
1911 CHECK_NE(rhs_reg, ZERO);
1912 break;
1913 case kCondLTZ:
1914 case kCondGEZ:
1915 case kCondLEZ:
1916 case kCondGTZ:
1917 case kCondEQZ:
1918 case kCondNEZ:
1919 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1920 CHECK_NE(lhs_reg, ZERO);
1921 CHECK_EQ(rhs_reg, ZERO);
1922 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001923 case kCondF:
1924 case kCondT:
1925 CHECK_EQ(rhs_reg, ZERO);
1926 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001927 case kUncond:
1928 UNREACHABLE();
1929 }
1930 CHECK(!IsNop(condition, lhs_reg, rhs_reg));
1931 if (IsUncond(condition, lhs_reg, rhs_reg)) {
1932 // Branch condition is always true, make the branch unconditional.
1933 condition_ = kUncond;
1934 }
Alexey Frunze96b66822016-09-10 02:32:44 -07001935 InitializeType(kCondBranch, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001936}
1937
Alexey Frunze96b66822016-09-10 02:32:44 -07001938MipsAssembler::Branch::Branch(bool is_r6,
1939 uint32_t location,
1940 Register dest_reg,
1941 Register base_reg,
1942 Type label_or_literal_type)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001943 : old_location_(location),
1944 location_(location),
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001945 target_(kUnresolved),
1946 lhs_reg_(dest_reg),
1947 rhs_reg_(base_reg),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001948 condition_(kUncond),
1949 delayed_instruction_(kUnfilledDelaySlot) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001950 CHECK_NE(dest_reg, ZERO);
1951 if (is_r6) {
1952 CHECK_EQ(base_reg, ZERO);
1953 } else {
1954 CHECK_NE(base_reg, ZERO);
1955 }
Alexey Frunze96b66822016-09-10 02:32:44 -07001956 InitializeType(label_or_literal_type, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001957}
1958
1959MipsAssembler::BranchCondition MipsAssembler::Branch::OppositeCondition(
1960 MipsAssembler::BranchCondition cond) {
1961 switch (cond) {
1962 case kCondLT:
1963 return kCondGE;
1964 case kCondGE:
1965 return kCondLT;
1966 case kCondLE:
1967 return kCondGT;
1968 case kCondGT:
1969 return kCondLE;
1970 case kCondLTZ:
1971 return kCondGEZ;
1972 case kCondGEZ:
1973 return kCondLTZ;
1974 case kCondLEZ:
1975 return kCondGTZ;
1976 case kCondGTZ:
1977 return kCondLEZ;
1978 case kCondEQ:
1979 return kCondNE;
1980 case kCondNE:
1981 return kCondEQ;
1982 case kCondEQZ:
1983 return kCondNEZ;
1984 case kCondNEZ:
1985 return kCondEQZ;
1986 case kCondLTU:
1987 return kCondGEU;
1988 case kCondGEU:
1989 return kCondLTU;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001990 case kCondF:
1991 return kCondT;
1992 case kCondT:
1993 return kCondF;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001994 case kUncond:
1995 LOG(FATAL) << "Unexpected branch condition " << cond;
1996 }
1997 UNREACHABLE();
1998}
1999
2000MipsAssembler::Branch::Type MipsAssembler::Branch::GetType() const {
2001 return type_;
2002}
2003
2004MipsAssembler::BranchCondition MipsAssembler::Branch::GetCondition() const {
2005 return condition_;
2006}
2007
2008Register MipsAssembler::Branch::GetLeftRegister() const {
2009 return static_cast<Register>(lhs_reg_);
2010}
2011
2012Register MipsAssembler::Branch::GetRightRegister() const {
2013 return static_cast<Register>(rhs_reg_);
2014}
2015
2016uint32_t MipsAssembler::Branch::GetTarget() const {
2017 return target_;
2018}
2019
2020uint32_t MipsAssembler::Branch::GetLocation() const {
2021 return location_;
2022}
2023
2024uint32_t MipsAssembler::Branch::GetOldLocation() const {
2025 return old_location_;
2026}
2027
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002028uint32_t MipsAssembler::Branch::GetPrecedingInstructionLength(Type type) const {
2029 // Short branches with delay slots always consist of two instructions, the branch
2030 // and the delay slot, irrespective of whether the delay slot is filled with a
2031 // useful instruction or not.
2032 // Long composite branches may have a length longer by one instruction than
2033 // specified in branch_info_[].length. This happens when an instruction is taken
2034 // to fill the short branch delay slot, but the branch eventually becomes long
2035 // and formally has no delay slot to fill. This instruction is placed at the
2036 // beginning of the long composite branch and this needs to be accounted for in
2037 // the branch length and the location of the offset encoded in the branch.
2038 switch (type) {
2039 case kLongUncondBranch:
2040 case kLongCondBranch:
2041 case kLongCall:
2042 case kR6LongCondBranch:
2043 return (delayed_instruction_ != kUnfilledDelaySlot &&
2044 delayed_instruction_ != kUnfillableDelaySlot) ? 1 : 0;
2045 default:
2046 return 0;
2047 }
2048}
2049
2050uint32_t MipsAssembler::Branch::GetPrecedingInstructionSize(Type type) const {
2051 return GetPrecedingInstructionLength(type) * sizeof(uint32_t);
2052}
2053
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002054uint32_t MipsAssembler::Branch::GetLength() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002055 return GetPrecedingInstructionLength(type_) + branch_info_[type_].length;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002056}
2057
2058uint32_t MipsAssembler::Branch::GetOldLength() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002059 return GetPrecedingInstructionLength(old_type_) + branch_info_[old_type_].length;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002060}
2061
2062uint32_t MipsAssembler::Branch::GetSize() const {
2063 return GetLength() * sizeof(uint32_t);
2064}
2065
2066uint32_t MipsAssembler::Branch::GetOldSize() const {
2067 return GetOldLength() * sizeof(uint32_t);
2068}
2069
2070uint32_t MipsAssembler::Branch::GetEndLocation() const {
2071 return GetLocation() + GetSize();
2072}
2073
2074uint32_t MipsAssembler::Branch::GetOldEndLocation() const {
2075 return GetOldLocation() + GetOldSize();
2076}
2077
2078bool MipsAssembler::Branch::IsLong() const {
2079 switch (type_) {
2080 // R2 short branches.
2081 case kUncondBranch:
2082 case kCondBranch:
2083 case kCall:
Alexey Frunze96b66822016-09-10 02:32:44 -07002084 // R2 near label.
2085 case kLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002086 // R2 near literal.
2087 case kLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002088 // R6 short branches.
2089 case kR6UncondBranch:
2090 case kR6CondBranch:
2091 case kR6Call:
Alexey Frunze96b66822016-09-10 02:32:44 -07002092 // R6 near label.
2093 case kR6Label:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002094 // R6 near literal.
2095 case kR6Literal:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002096 return false;
2097 // R2 long branches.
2098 case kLongUncondBranch:
2099 case kLongCondBranch:
2100 case kLongCall:
Alexey Frunze96b66822016-09-10 02:32:44 -07002101 // R2 far label.
2102 case kFarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002103 // R2 far literal.
2104 case kFarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002105 // R6 long branches.
2106 case kR6LongUncondBranch:
2107 case kR6LongCondBranch:
2108 case kR6LongCall:
Alexey Frunze96b66822016-09-10 02:32:44 -07002109 // R6 far label.
2110 case kR6FarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002111 // R6 far literal.
2112 case kR6FarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002113 return true;
2114 }
2115 UNREACHABLE();
2116}
2117
2118bool MipsAssembler::Branch::IsResolved() const {
2119 return target_ != kUnresolved;
2120}
2121
2122MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSize() const {
2123 OffsetBits offset_size =
2124 (type_ == kR6CondBranch && (condition_ == kCondEQZ || condition_ == kCondNEZ))
2125 ? kOffset23
2126 : branch_info_[type_].offset_size;
2127 return offset_size;
2128}
2129
2130MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSizeNeeded(uint32_t location,
2131 uint32_t target) {
2132 // For unresolved targets assume the shortest encoding
2133 // (later it will be made longer if needed).
2134 if (target == kUnresolved)
2135 return kOffset16;
2136 int64_t distance = static_cast<int64_t>(target) - location;
2137 // To simplify calculations in composite branches consisting of multiple instructions
2138 // bump up the distance by a value larger than the max byte size of a composite branch.
2139 distance += (distance >= 0) ? kMaxBranchSize : -kMaxBranchSize;
2140 if (IsInt<kOffset16>(distance))
2141 return kOffset16;
2142 else if (IsInt<kOffset18>(distance))
2143 return kOffset18;
2144 else if (IsInt<kOffset21>(distance))
2145 return kOffset21;
2146 else if (IsInt<kOffset23>(distance))
2147 return kOffset23;
2148 else if (IsInt<kOffset28>(distance))
2149 return kOffset28;
2150 return kOffset32;
2151}
2152
2153void MipsAssembler::Branch::Resolve(uint32_t target) {
2154 target_ = target;
2155}
2156
2157void MipsAssembler::Branch::Relocate(uint32_t expand_location, uint32_t delta) {
2158 if (location_ > expand_location) {
2159 location_ += delta;
2160 }
2161 if (!IsResolved()) {
2162 return; // Don't know the target yet.
2163 }
2164 if (target_ > expand_location) {
2165 target_ += delta;
2166 }
2167}
2168
2169void MipsAssembler::Branch::PromoteToLong() {
2170 switch (type_) {
2171 // R2 short branches.
2172 case kUncondBranch:
2173 type_ = kLongUncondBranch;
2174 break;
2175 case kCondBranch:
2176 type_ = kLongCondBranch;
2177 break;
2178 case kCall:
2179 type_ = kLongCall;
2180 break;
Alexey Frunze96b66822016-09-10 02:32:44 -07002181 // R2 near label.
2182 case kLabel:
2183 type_ = kFarLabel;
2184 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002185 // R2 near literal.
2186 case kLiteral:
2187 type_ = kFarLiteral;
2188 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002189 // R6 short branches.
2190 case kR6UncondBranch:
2191 type_ = kR6LongUncondBranch;
2192 break;
2193 case kR6CondBranch:
2194 type_ = kR6LongCondBranch;
2195 break;
2196 case kR6Call:
2197 type_ = kR6LongCall;
2198 break;
Alexey Frunze96b66822016-09-10 02:32:44 -07002199 // R6 near label.
2200 case kR6Label:
2201 type_ = kR6FarLabel;
2202 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002203 // R6 near literal.
2204 case kR6Literal:
2205 type_ = kR6FarLiteral;
2206 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002207 default:
2208 // Note: 'type_' is already long.
2209 break;
2210 }
2211 CHECK(IsLong());
2212}
2213
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002214uint32_t MipsAssembler::GetBranchLocationOrPcRelBase(const MipsAssembler::Branch* branch) const {
2215 switch (branch->GetType()) {
Alexey Frunze96b66822016-09-10 02:32:44 -07002216 case Branch::kLabel:
2217 case Branch::kFarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002218 case Branch::kLiteral:
2219 case Branch::kFarLiteral:
2220 return GetLabelLocation(&pc_rel_base_label_);
2221 default:
2222 return branch->GetLocation();
2223 }
2224}
2225
2226uint32_t MipsAssembler::Branch::PromoteIfNeeded(uint32_t location, uint32_t max_short_distance) {
Alexey Frunze96b66822016-09-10 02:32:44 -07002227 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 labels/literals or
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002228 // `this->GetLocation()` for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002229 // If the branch is still unresolved or already long, nothing to do.
2230 if (IsLong() || !IsResolved()) {
2231 return 0;
2232 }
2233 // Promote the short branch to long if the offset size is too small
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002234 // to hold the distance between location and target_.
2235 if (GetOffsetSizeNeeded(location, target_) > GetOffsetSize()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002236 PromoteToLong();
2237 uint32_t old_size = GetOldSize();
2238 uint32_t new_size = GetSize();
2239 CHECK_GT(new_size, old_size);
2240 return new_size - old_size;
2241 }
2242 // The following logic is for debugging/testing purposes.
2243 // Promote some short branches to long when it's not really required.
2244 if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max())) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002245 int64_t distance = static_cast<int64_t>(target_) - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002246 distance = (distance >= 0) ? distance : -distance;
2247 if (distance >= max_short_distance) {
2248 PromoteToLong();
2249 uint32_t old_size = GetOldSize();
2250 uint32_t new_size = GetSize();
2251 CHECK_GT(new_size, old_size);
2252 return new_size - old_size;
2253 }
2254 }
2255 return 0;
2256}
2257
2258uint32_t MipsAssembler::Branch::GetOffsetLocation() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002259 return location_ + GetPrecedingInstructionSize(type_) +
2260 branch_info_[type_].instr_offset * sizeof(uint32_t);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002261}
2262
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002263uint32_t MipsAssembler::GetBranchOrPcRelBaseForEncoding(const MipsAssembler::Branch* branch) const {
2264 switch (branch->GetType()) {
Alexey Frunze96b66822016-09-10 02:32:44 -07002265 case Branch::kLabel:
2266 case Branch::kFarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002267 case Branch::kLiteral:
2268 case Branch::kFarLiteral:
2269 return GetLabelLocation(&pc_rel_base_label_);
2270 default:
2271 return branch->GetOffsetLocation() +
2272 Branch::branch_info_[branch->GetType()].pc_org * sizeof(uint32_t);
2273 }
2274}
2275
2276uint32_t MipsAssembler::Branch::GetOffset(uint32_t location) const {
Alexey Frunze96b66822016-09-10 02:32:44 -07002277 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 labels/literals or
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002278 // `this->GetOffsetLocation() + branch_info_[this->GetType()].pc_org * sizeof(uint32_t)`
2279 // for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002280 CHECK(IsResolved());
2281 uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize());
2282 // Calculate the byte distance between instructions and also account for
2283 // different PC-relative origins.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002284 uint32_t offset = target_ - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002285 // Prepare the offset for encoding into the instruction(s).
2286 offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift;
2287 return offset;
2288}
2289
2290MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) {
2291 CHECK_LT(branch_id, branches_.size());
2292 return &branches_[branch_id];
2293}
2294
2295const MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) const {
2296 CHECK_LT(branch_id, branches_.size());
2297 return &branches_[branch_id];
2298}
2299
2300void MipsAssembler::Bind(MipsLabel* label) {
2301 CHECK(!label->IsBound());
2302 uint32_t bound_pc = buffer_.Size();
2303
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002304 // Make the delay slot FSM aware of the new label.
2305 DsFsmLabel();
2306
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002307 // Walk the list of branches referring to and preceding this label.
2308 // Store the previously unknown target addresses in them.
2309 while (label->IsLinked()) {
2310 uint32_t branch_id = label->Position();
2311 Branch* branch = GetBranch(branch_id);
2312 branch->Resolve(bound_pc);
2313
2314 uint32_t branch_location = branch->GetLocation();
2315 // Extract the location of the previous branch in the list (walking the list backwards;
2316 // the previous branch ID was stored in the space reserved for this branch).
2317 uint32_t prev = buffer_.Load<uint32_t>(branch_location);
2318
2319 // On to the previous branch in the list...
2320 label->position_ = prev;
2321 }
2322
2323 // Now make the label object contain its own location (relative to the end of the preceding
2324 // branch, if any; it will be used by the branches referring to and following this label).
2325 label->prev_branch_id_plus_one_ = branches_.size();
2326 if (label->prev_branch_id_plus_one_) {
2327 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
2328 const Branch* branch = GetBranch(branch_id);
2329 bound_pc -= branch->GetEndLocation();
2330 }
2331 label->BindTo(bound_pc);
2332}
2333
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002334uint32_t MipsAssembler::GetLabelLocation(const MipsLabel* label) const {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002335 CHECK(label->IsBound());
2336 uint32_t target = label->Position();
2337 if (label->prev_branch_id_plus_one_) {
2338 // Get label location based on the branch preceding it.
2339 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
2340 const Branch* branch = GetBranch(branch_id);
2341 target += branch->GetEndLocation();
2342 }
2343 return target;
2344}
2345
2346uint32_t MipsAssembler::GetAdjustedPosition(uint32_t old_position) {
2347 // We can reconstruct the adjustment by going through all the branches from the beginning
2348 // up to the old_position. Since we expect AdjustedPosition() to be called in a loop
2349 // with increasing old_position, we can use the data from last AdjustedPosition() to
2350 // continue where we left off and the whole loop should be O(m+n) where m is the number
2351 // of positions to adjust and n is the number of branches.
2352 if (old_position < last_old_position_) {
2353 last_position_adjustment_ = 0;
2354 last_old_position_ = 0;
2355 last_branch_id_ = 0;
2356 }
2357 while (last_branch_id_ != branches_.size()) {
2358 const Branch* branch = GetBranch(last_branch_id_);
2359 if (branch->GetLocation() >= old_position + last_position_adjustment_) {
2360 break;
2361 }
2362 last_position_adjustment_ += branch->GetSize() - branch->GetOldSize();
2363 ++last_branch_id_;
2364 }
2365 last_old_position_ = old_position;
2366 return old_position + last_position_adjustment_;
2367}
2368
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002369void MipsAssembler::BindPcRelBaseLabel() {
2370 Bind(&pc_rel_base_label_);
2371}
2372
Alexey Frunze06a46c42016-07-19 15:00:40 -07002373uint32_t MipsAssembler::GetPcRelBaseLabelLocation() const {
2374 return GetLabelLocation(&pc_rel_base_label_);
2375}
2376
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002377void MipsAssembler::FinalizeLabeledBranch(MipsLabel* label) {
2378 uint32_t length = branches_.back().GetLength();
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002379 // Commit the last branch target label (if any).
2380 DsFsmCommitLabel();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002381 if (!label->IsBound()) {
2382 // Branch forward (to a following label), distance is unknown.
2383 // The first branch forward will contain 0, serving as the terminator of
2384 // the list of forward-reaching branches.
2385 Emit(label->position_);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002386 // Nothing for the delay slot (yet).
2387 DsFsmInstrNop(0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002388 length--;
2389 // Now make the label object point to this branch
2390 // (this forms a linked list of branches preceding this label).
2391 uint32_t branch_id = branches_.size() - 1;
2392 label->LinkTo(branch_id);
2393 }
2394 // Reserve space for the branch.
2395 while (length--) {
2396 Nop();
2397 }
2398}
2399
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002400bool MipsAssembler::Branch::CanHaveDelayedInstruction(const DelaySlot& delay_slot) const {
2401 if (delay_slot.instruction_ == 0) {
2402 // NOP or no instruction for the delay slot.
2403 return false;
2404 }
2405 switch (type_) {
2406 // R2 unconditional branches.
2407 case kUncondBranch:
2408 case kLongUncondBranch:
2409 // There are no register interdependencies.
2410 return true;
2411
2412 // R2 calls.
2413 case kCall:
2414 case kLongCall:
2415 // Instructions depending on or modifying RA should not be moved into delay slots
2416 // of branches modifying RA.
2417 return ((delay_slot.gpr_ins_mask_ | delay_slot.gpr_outs_mask_) & (1u << RA)) == 0;
2418
2419 // R2 conditional branches.
2420 case kCondBranch:
2421 case kLongCondBranch:
2422 switch (condition_) {
2423 // Branches with one GPR source.
2424 case kCondLTZ:
2425 case kCondGEZ:
2426 case kCondLEZ:
2427 case kCondGTZ:
2428 case kCondEQZ:
2429 case kCondNEZ:
2430 return (delay_slot.gpr_outs_mask_ & (1u << lhs_reg_)) == 0;
2431
2432 // Branches with two GPR sources.
2433 case kCondEQ:
2434 case kCondNE:
2435 return (delay_slot.gpr_outs_mask_ & ((1u << lhs_reg_) | (1u << rhs_reg_))) == 0;
2436
2437 // Branches with one FPU condition code source.
2438 case kCondF:
2439 case kCondT:
2440 return (delay_slot.cc_outs_mask_ & (1u << lhs_reg_)) == 0;
2441
2442 default:
2443 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
2444 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
2445 LOG(FATAL) << "Unexpected branch condition " << condition_;
2446 UNREACHABLE();
2447 }
2448
2449 // R6 unconditional branches.
2450 case kR6UncondBranch:
2451 case kR6LongUncondBranch:
2452 // R6 calls.
2453 case kR6Call:
2454 case kR6LongCall:
2455 // There are no delay slots.
2456 return false;
2457
2458 // R6 conditional branches.
2459 case kR6CondBranch:
2460 case kR6LongCondBranch:
2461 switch (condition_) {
2462 // Branches with one FPU register source.
2463 case kCondF:
2464 case kCondT:
2465 return (delay_slot.fpr_outs_mask_ & (1u << lhs_reg_)) == 0;
2466 // Others have a forbidden slot instead of a delay slot.
2467 default:
2468 return false;
2469 }
2470
2471 // Literals.
2472 default:
2473 LOG(FATAL) << "Unexpected branch type " << type_;
2474 UNREACHABLE();
2475 }
2476}
2477
2478uint32_t MipsAssembler::Branch::GetDelayedInstruction() const {
2479 return delayed_instruction_;
2480}
2481
2482void MipsAssembler::Branch::SetDelayedInstruction(uint32_t instruction) {
2483 CHECK_NE(instruction, kUnfilledDelaySlot);
2484 CHECK_EQ(delayed_instruction_, kUnfilledDelaySlot);
2485 delayed_instruction_ = instruction;
2486}
2487
2488void MipsAssembler::Branch::DecrementLocations() {
2489 // We first create a branch object, which gets its type and locations initialized,
2490 // and then we check if the branch can actually have the preceding instruction moved
2491 // into its delay slot. If it can, the branch locations need to be decremented.
2492 //
2493 // We could make the check before creating the branch object and avoid the location
2494 // adjustment, but the check is cleaner when performed on an initialized branch
2495 // object.
2496 //
2497 // If the branch is backwards (to a previously bound label), reducing the locations
2498 // cannot cause a short branch to exceed its offset range because the offset reduces.
2499 // And this is not at all a problem for a long branch backwards.
2500 //
2501 // If the branch is forward (not linked to any label yet), reducing the locations
2502 // is harmless. The branch will be promoted to long if needed when the target is known.
2503 CHECK_EQ(location_, old_location_);
2504 CHECK_GE(old_location_, sizeof(uint32_t));
2505 old_location_ -= sizeof(uint32_t);
2506 location_ = old_location_;
2507}
2508
2509void MipsAssembler::MoveInstructionToDelaySlot(Branch& branch) {
2510 if (branch.CanHaveDelayedInstruction(delay_slot_)) {
2511 // The last instruction cannot be used in a different delay slot,
2512 // do not commit the label before it (if any).
2513 DsFsmDropLabel();
2514 // Remove the last emitted instruction.
2515 size_t size = buffer_.Size();
2516 CHECK_GE(size, sizeof(uint32_t));
2517 size -= sizeof(uint32_t);
2518 CHECK_EQ(buffer_.Load<uint32_t>(size), delay_slot_.instruction_);
2519 buffer_.Resize(size);
2520 // Attach it to the branch and adjust the branch locations.
2521 branch.DecrementLocations();
2522 branch.SetDelayedInstruction(delay_slot_.instruction_);
2523 } else if (!reordering_ && branch.GetType() == Branch::kUncondBranch) {
2524 // If reordefing is disabled, prevent absorption of the target instruction.
2525 branch.SetDelayedInstruction(Branch::kUnfillableDelaySlot);
2526 }
2527}
2528
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002529void MipsAssembler::Buncond(MipsLabel* label) {
2530 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002531 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ false);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002532 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002533 FinalizeLabeledBranch(label);
2534}
2535
2536void MipsAssembler::Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs) {
2537 // If lhs = rhs, this can be a NOP.
2538 if (Branch::IsNop(condition, lhs, rhs)) {
2539 return;
2540 }
2541 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
2542 branches_.emplace_back(IsR6(), buffer_.Size(), target, condition, lhs, rhs);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002543 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002544 FinalizeLabeledBranch(label);
2545}
2546
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002547void MipsAssembler::Call(MipsLabel* label) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002548 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002549 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ true);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002550 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002551 FinalizeLabeledBranch(label);
2552}
2553
Alexey Frunze96b66822016-09-10 02:32:44 -07002554void MipsAssembler::LoadLabelAddress(Register dest_reg, Register base_reg, MipsLabel* label) {
2555 // Label address loads are treated as pseudo branches since they require very similar handling.
2556 DCHECK(!label->IsBound());
2557 branches_.emplace_back(IsR6(), buffer_.Size(), dest_reg, base_reg, Branch::kLabel);
2558 FinalizeLabeledBranch(label);
2559}
2560
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002561Literal* MipsAssembler::NewLiteral(size_t size, const uint8_t* data) {
2562 DCHECK(size == 4u || size == 8u) << size;
2563 literals_.emplace_back(size, data);
2564 return &literals_.back();
2565}
2566
2567void MipsAssembler::LoadLiteral(Register dest_reg, Register base_reg, Literal* literal) {
2568 // Literal loads are treated as pseudo branches since they require very similar handling.
2569 DCHECK_EQ(literal->GetSize(), 4u);
2570 MipsLabel* label = literal->GetLabel();
2571 DCHECK(!label->IsBound());
Alexey Frunze96b66822016-09-10 02:32:44 -07002572 branches_.emplace_back(IsR6(), buffer_.Size(), dest_reg, base_reg, Branch::kLiteral);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002573 FinalizeLabeledBranch(label);
2574}
2575
Alexey Frunze96b66822016-09-10 02:32:44 -07002576JumpTable* MipsAssembler::CreateJumpTable(std::vector<MipsLabel*>&& labels) {
2577 jump_tables_.emplace_back(std::move(labels));
2578 JumpTable* table = &jump_tables_.back();
2579 DCHECK(!table->GetLabel()->IsBound());
2580 return table;
2581}
2582
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002583void MipsAssembler::EmitLiterals() {
2584 if (!literals_.empty()) {
2585 // We don't support byte and half-word literals.
2586 // TODO: proper alignment for 64-bit literals when they're implemented.
2587 for (Literal& literal : literals_) {
2588 MipsLabel* label = literal.GetLabel();
2589 Bind(label);
2590 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
2591 DCHECK(literal.GetSize() == 4u || literal.GetSize() == 8u);
2592 for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
2593 buffer_.Emit<uint8_t>(literal.GetData()[i]);
2594 }
2595 }
2596 }
2597}
2598
Alexey Frunze96b66822016-09-10 02:32:44 -07002599void MipsAssembler::ReserveJumpTableSpace() {
2600 if (!jump_tables_.empty()) {
2601 for (JumpTable& table : jump_tables_) {
2602 MipsLabel* label = table.GetLabel();
2603 Bind(label);
2604
2605 // Bulk ensure capacity, as this may be large.
2606 size_t orig_size = buffer_.Size();
2607 size_t required_capacity = orig_size + table.GetSize();
2608 if (required_capacity > buffer_.Capacity()) {
2609 buffer_.ExtendCapacity(required_capacity);
2610 }
2611#ifndef NDEBUG
2612 buffer_.has_ensured_capacity_ = true;
2613#endif
2614
2615 // Fill the space with dummy data as the data is not final
2616 // until the branches have been promoted. And we shouldn't
2617 // be moving uninitialized data during branch promotion.
2618 for (size_t cnt = table.GetData().size(), i = 0; i < cnt; i++) {
2619 buffer_.Emit<uint32_t>(0x1abe1234u);
2620 }
2621
2622#ifndef NDEBUG
2623 buffer_.has_ensured_capacity_ = false;
2624#endif
2625 }
2626 }
2627}
2628
2629void MipsAssembler::EmitJumpTables() {
2630 if (!jump_tables_.empty()) {
2631 CHECK(!overwriting_);
2632 // Switch from appending instructions at the end of the buffer to overwriting
2633 // existing instructions (here, jump tables) in the buffer.
2634 overwriting_ = true;
2635
2636 for (JumpTable& table : jump_tables_) {
2637 MipsLabel* table_label = table.GetLabel();
2638 uint32_t start = GetLabelLocation(table_label);
2639 overwrite_location_ = start;
2640
2641 for (MipsLabel* target : table.GetData()) {
2642 CHECK_EQ(buffer_.Load<uint32_t>(overwrite_location_), 0x1abe1234u);
2643 // The table will contain target addresses relative to the table start.
2644 uint32_t offset = GetLabelLocation(target) - start;
2645 Emit(offset);
2646 }
2647 }
2648
2649 overwriting_ = false;
2650 }
2651}
2652
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002653void MipsAssembler::PromoteBranches() {
2654 // Promote short branches to long as necessary.
2655 bool changed;
2656 do {
2657 changed = false;
2658 for (auto& branch : branches_) {
2659 CHECK(branch.IsResolved());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002660 uint32_t base = GetBranchLocationOrPcRelBase(&branch);
2661 uint32_t delta = branch.PromoteIfNeeded(base);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002662 // If this branch has been promoted and needs to expand in size,
2663 // relocate all branches by the expansion size.
2664 if (delta) {
2665 changed = true;
2666 uint32_t expand_location = branch.GetLocation();
2667 for (auto& branch2 : branches_) {
2668 branch2.Relocate(expand_location, delta);
2669 }
2670 }
2671 }
2672 } while (changed);
2673
2674 // Account for branch expansion by resizing the code buffer
2675 // and moving the code in it to its final location.
2676 size_t branch_count = branches_.size();
2677 if (branch_count > 0) {
2678 // Resize.
2679 Branch& last_branch = branches_[branch_count - 1];
2680 uint32_t size_delta = last_branch.GetEndLocation() - last_branch.GetOldEndLocation();
2681 uint32_t old_size = buffer_.Size();
2682 buffer_.Resize(old_size + size_delta);
2683 // Move the code residing between branch placeholders.
2684 uint32_t end = old_size;
2685 for (size_t i = branch_count; i > 0; ) {
2686 Branch& branch = branches_[--i];
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002687 CHECK_GE(end, branch.GetOldEndLocation());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002688 uint32_t size = end - branch.GetOldEndLocation();
2689 buffer_.Move(branch.GetEndLocation(), branch.GetOldEndLocation(), size);
2690 end = branch.GetOldLocation();
2691 }
2692 }
2693}
2694
2695// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
2696const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = {
2697 // R2 short branches.
2698 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kUncondBranch
2699 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002700 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCall
Alexey Frunze96b66822016-09-10 02:32:44 -07002701 // R2 near label.
2702 { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLabel
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002703 // R2 near literal.
2704 { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002705 // R2 long branches.
2706 { 9, 3, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongUncondBranch
2707 { 10, 4, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCondBranch
2708 { 6, 1, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCall
Alexey Frunze96b66822016-09-10 02:32:44 -07002709 // R2 far label.
2710 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLabel
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002711 // R2 far literal.
2712 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002713 // R6 short branches.
2714 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6UncondBranch
2715 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kR6CondBranch
2716 // Exception: kOffset23 for beqzc/bnezc.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002717 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6Call
Alexey Frunze96b66822016-09-10 02:32:44 -07002718 // R6 near label.
2719 { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Label
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002720 // R6 near literal.
2721 { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Literal
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002722 // R6 long branches.
2723 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongUncondBranch
2724 { 3, 1, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002725 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCall
Alexey Frunze96b66822016-09-10 02:32:44 -07002726 // R6 far label.
2727 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLabel
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002728 // R6 far literal.
2729 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002730};
2731
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002732// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002733void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
2734 CHECK_EQ(overwriting_, true);
2735 overwrite_location_ = branch->GetLocation();
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002736 uint32_t offset = branch->GetOffset(GetBranchOrPcRelBaseForEncoding(branch));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002737 BranchCondition condition = branch->GetCondition();
2738 Register lhs = branch->GetLeftRegister();
2739 Register rhs = branch->GetRightRegister();
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002740 uint32_t delayed_instruction = branch->GetDelayedInstruction();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002741 switch (branch->GetType()) {
2742 // R2 short branches.
2743 case Branch::kUncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002744 if (delayed_instruction == Branch::kUnfillableDelaySlot) {
2745 // The branch was created when reordering was disabled, do not absorb the target
2746 // instruction.
2747 delayed_instruction = 0; // NOP.
2748 } else if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2749 // Try to absorb the target instruction into the delay slot.
2750 delayed_instruction = 0; // NOP.
2751 // Incrementing the signed 16-bit offset past the target instruction must not
2752 // cause overflow into the negative subrange, check for the max offset.
2753 if (offset != 0x7FFF) {
2754 uint32_t target = branch->GetTarget();
2755 if (std::binary_search(ds_fsm_target_pcs_.begin(), ds_fsm_target_pcs_.end(), target)) {
2756 delayed_instruction = buffer_.Load<uint32_t>(target);
2757 offset++;
2758 }
2759 }
2760 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002761 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2762 B(offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002763 Emit(delayed_instruction);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002764 break;
2765 case Branch::kCondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002766 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2767 if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2768 delayed_instruction = 0; // NOP.
2769 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002770 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002771 EmitBcondR2(condition, lhs, rhs, offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002772 Emit(delayed_instruction);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002773 break;
2774 case Branch::kCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002775 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2776 if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2777 delayed_instruction = 0; // NOP.
2778 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002779 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002780 Bal(offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002781 Emit(delayed_instruction);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002782 break;
2783
Alexey Frunze96b66822016-09-10 02:32:44 -07002784 // R2 near label.
2785 case Branch::kLabel:
2786 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2787 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2788 Addiu(lhs, rhs, offset);
2789 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002790 // R2 near literal.
2791 case Branch::kLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002792 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002793 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2794 Lw(lhs, rhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002795 break;
2796
2797 // R2 long branches.
2798 case Branch::kLongUncondBranch:
2799 // To get the value of the PC register we need to use the NAL instruction.
2800 // NAL clobbers the RA register. However, RA must be preserved if the
2801 // method is compiled without the entry/exit sequences that would take care
2802 // of preserving RA (typically, leaf methods don't preserve RA explicitly).
2803 // So, we need to preserve RA in some temporary storage ourselves. The AT
2804 // register can't be used for this because we need it to load a constant
2805 // which will be added to the value that NAL stores in RA. And we can't
2806 // use T9 for this in the context of the JNI compiler, which uses it
2807 // as a scratch register (see InterproceduralScratchRegister()).
2808 // If we were to add a 32-bit constant to RA using two ADDIU instructions,
2809 // we'd also need to use the ROTR instruction, which requires no less than
2810 // MIPSR2.
2811 // Perhaps, we could use T8 or one of R2's multiplier/divider registers
2812 // (LO or HI) or even a floating-point register, but that doesn't seem
2813 // like a nice solution. We may want this to work on both R6 and pre-R6.
2814 // For now simply use the stack for RA. This should be OK since for the
2815 // vast majority of code a short PC-relative branch is sufficient.
2816 // TODO: can this be improved?
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002817 // TODO: consider generation of a shorter sequence when we know that RA
2818 // is explicitly preserved by the method entry/exit code.
2819 if (delayed_instruction != Branch::kUnfilledDelaySlot &&
2820 delayed_instruction != Branch::kUnfillableDelaySlot) {
2821 Emit(delayed_instruction);
2822 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002823 Push(RA);
2824 Nal();
2825 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2826 Lui(AT, High16Bits(offset));
2827 Ori(AT, AT, Low16Bits(offset));
2828 Addu(AT, AT, RA);
2829 Lw(RA, SP, 0);
2830 Jr(AT);
2831 DecreaseFrameSize(kMipsWordSize);
2832 break;
2833 case Branch::kLongCondBranch:
2834 // The comment on case 'Branch::kLongUncondBranch' applies here as well.
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002835 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2836 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2837 Emit(delayed_instruction);
2838 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002839 // Note: the opposite condition branch encodes 8 as the distance, which is equal to the
2840 // number of instructions skipped:
2841 // (PUSH(IncreaseFrameSize(ADDIU) + SW) + NAL + LUI + ORI + ADDU + LW + JR).
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002842 EmitBcondR2(Branch::OppositeCondition(condition), lhs, rhs, 8);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002843 Push(RA);
2844 Nal();
2845 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2846 Lui(AT, High16Bits(offset));
2847 Ori(AT, AT, Low16Bits(offset));
2848 Addu(AT, AT, RA);
2849 Lw(RA, SP, 0);
2850 Jr(AT);
2851 DecreaseFrameSize(kMipsWordSize);
2852 break;
2853 case Branch::kLongCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002854 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2855 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2856 Emit(delayed_instruction);
2857 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002858 Nal();
2859 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2860 Lui(AT, High16Bits(offset));
2861 Ori(AT, AT, Low16Bits(offset));
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002862 Addu(AT, AT, RA);
2863 Jalr(AT);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002864 Nop();
2865 break;
2866
Alexey Frunze96b66822016-09-10 02:32:44 -07002867 // R2 far label.
2868 case Branch::kFarLabel:
2869 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2870 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2871 Lui(AT, High16Bits(offset));
2872 Ori(AT, AT, Low16Bits(offset));
2873 Addu(lhs, AT, rhs);
2874 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002875 // R2 far literal.
2876 case Branch::kFarLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002877 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002878 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2879 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2880 Lui(AT, High16Bits(offset));
2881 Addu(AT, AT, rhs);
2882 Lw(lhs, AT, Low16Bits(offset));
2883 break;
2884
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002885 // R6 short branches.
2886 case Branch::kR6UncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002887 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002888 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2889 Bc(offset);
2890 break;
2891 case Branch::kR6CondBranch:
2892 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002893 EmitBcondR6(condition, lhs, rhs, offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002894 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2895 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2896 Emit(delayed_instruction);
2897 } else {
2898 // TODO: improve by filling the forbidden slot (IFF this is
2899 // a forbidden and not a delay slot).
2900 Nop();
2901 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002902 break;
2903 case Branch::kR6Call:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002904 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002905 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002906 Balc(offset);
2907 break;
2908
Alexey Frunze96b66822016-09-10 02:32:44 -07002909 // R6 near label.
2910 case Branch::kR6Label:
2911 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2912 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2913 Addiupc(lhs, offset);
2914 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002915 // R6 near literal.
2916 case Branch::kR6Literal:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002917 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002918 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2919 Lwpc(lhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002920 break;
2921
2922 // R6 long branches.
2923 case Branch::kR6LongUncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002924 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002925 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2926 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2927 Auipc(AT, High16Bits(offset));
2928 Jic(AT, Low16Bits(offset));
2929 break;
2930 case Branch::kR6LongCondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002931 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2932 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2933 Emit(delayed_instruction);
2934 }
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002935 EmitBcondR6(Branch::OppositeCondition(condition), lhs, rhs, 2);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002936 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2937 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2938 Auipc(AT, High16Bits(offset));
2939 Jic(AT, Low16Bits(offset));
2940 break;
2941 case Branch::kR6LongCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002942 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002943 offset += (offset & 0x8000) << 1; // Account for sign extension in jialc.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002944 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002945 Auipc(AT, High16Bits(offset));
2946 Jialc(AT, Low16Bits(offset));
2947 break;
2948
Alexey Frunze96b66822016-09-10 02:32:44 -07002949 // R6 far label.
2950 case Branch::kR6FarLabel:
2951 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2952 offset += (offset & 0x8000) << 1; // Account for sign extension in addiu.
2953 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2954 Auipc(AT, High16Bits(offset));
2955 Addiu(lhs, AT, Low16Bits(offset));
2956 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002957 // R6 far literal.
2958 case Branch::kR6FarLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002959 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002960 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2961 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2962 Auipc(AT, High16Bits(offset));
2963 Lw(lhs, AT, Low16Bits(offset));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002964 break;
2965 }
2966 CHECK_EQ(overwrite_location_, branch->GetEndLocation());
2967 CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize));
2968}
2969
2970void MipsAssembler::B(MipsLabel* label) {
2971 Buncond(label);
2972}
2973
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002974void MipsAssembler::Bal(MipsLabel* label) {
2975 Call(label);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002976}
2977
2978void MipsAssembler::Beq(Register rs, Register rt, MipsLabel* label) {
2979 Bcond(label, kCondEQ, rs, rt);
2980}
2981
2982void MipsAssembler::Bne(Register rs, Register rt, MipsLabel* label) {
2983 Bcond(label, kCondNE, rs, rt);
2984}
2985
2986void MipsAssembler::Beqz(Register rt, MipsLabel* label) {
2987 Bcond(label, kCondEQZ, rt);
2988}
2989
2990void MipsAssembler::Bnez(Register rt, MipsLabel* label) {
2991 Bcond(label, kCondNEZ, rt);
2992}
2993
2994void MipsAssembler::Bltz(Register rt, MipsLabel* label) {
2995 Bcond(label, kCondLTZ, rt);
2996}
2997
2998void MipsAssembler::Bgez(Register rt, MipsLabel* label) {
2999 Bcond(label, kCondGEZ, rt);
3000}
3001
3002void MipsAssembler::Blez(Register rt, MipsLabel* label) {
3003 Bcond(label, kCondLEZ, rt);
3004}
3005
3006void MipsAssembler::Bgtz(Register rt, MipsLabel* label) {
3007 Bcond(label, kCondGTZ, rt);
3008}
3009
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003010bool MipsAssembler::CanExchangeWithSlt(Register rs, Register rt) const {
3011 // If the instruction modifies AT, `rs` or `rt`, it can't be exchanged with the slt[u]
3012 // instruction because either slt[u] depends on `rs` or `rt` or the following
3013 // conditional branch depends on AT set by slt[u].
3014 // Likewise, if the instruction depends on AT, it can't be exchanged with slt[u]
3015 // because slt[u] changes AT.
3016 return (delay_slot_.instruction_ != 0 &&
3017 (delay_slot_.gpr_outs_mask_ & ((1u << AT) | (1u << rs) | (1u << rt))) == 0 &&
3018 (delay_slot_.gpr_ins_mask_ & (1u << AT)) == 0);
3019}
3020
3021void MipsAssembler::ExchangeWithSlt(const DelaySlot& forwarded_slot) {
3022 // Exchange the last two instructions in the assembler buffer.
3023 size_t size = buffer_.Size();
3024 CHECK_GE(size, 2 * sizeof(uint32_t));
3025 size_t pos1 = size - 2 * sizeof(uint32_t);
3026 size_t pos2 = size - sizeof(uint32_t);
3027 uint32_t instr1 = buffer_.Load<uint32_t>(pos1);
3028 uint32_t instr2 = buffer_.Load<uint32_t>(pos2);
3029 CHECK_EQ(instr1, forwarded_slot.instruction_);
3030 CHECK_EQ(instr2, delay_slot_.instruction_);
3031 buffer_.Store<uint32_t>(pos1, instr2);
3032 buffer_.Store<uint32_t>(pos2, instr1);
3033 // Set the current delay slot information to that of the last instruction
3034 // in the buffer.
3035 delay_slot_ = forwarded_slot;
3036}
3037
3038void MipsAssembler::GenerateSltForCondBranch(bool unsigned_slt, Register rs, Register rt) {
3039 // If possible, exchange the slt[u] instruction with the preceding instruction,
3040 // so it can fill the delay slot.
3041 DelaySlot forwarded_slot = delay_slot_;
3042 bool exchange = CanExchangeWithSlt(rs, rt);
3043 if (exchange) {
3044 // The last instruction cannot be used in a different delay slot,
3045 // do not commit the label before it (if any).
3046 DsFsmDropLabel();
3047 }
3048 if (unsigned_slt) {
3049 Sltu(AT, rs, rt);
3050 } else {
3051 Slt(AT, rs, rt);
3052 }
3053 if (exchange) {
3054 ExchangeWithSlt(forwarded_slot);
3055 }
3056}
3057
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003058void MipsAssembler::Blt(Register rs, Register rt, MipsLabel* label) {
3059 if (IsR6()) {
3060 Bcond(label, kCondLT, rs, rt);
3061 } else if (!Branch::IsNop(kCondLT, rs, rt)) {
3062 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003063 GenerateSltForCondBranch(/* unsigned_slt */ false, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003064 Bnez(AT, label);
3065 }
3066}
3067
3068void MipsAssembler::Bge(Register rs, Register rt, MipsLabel* label) {
3069 if (IsR6()) {
3070 Bcond(label, kCondGE, rs, rt);
3071 } else if (Branch::IsUncond(kCondGE, rs, rt)) {
3072 B(label);
3073 } else {
3074 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003075 GenerateSltForCondBranch(/* unsigned_slt */ false, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003076 Beqz(AT, label);
3077 }
3078}
3079
3080void MipsAssembler::Bltu(Register rs, Register rt, MipsLabel* label) {
3081 if (IsR6()) {
3082 Bcond(label, kCondLTU, rs, rt);
3083 } else if (!Branch::IsNop(kCondLTU, rs, rt)) {
3084 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003085 GenerateSltForCondBranch(/* unsigned_slt */ true, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003086 Bnez(AT, label);
3087 }
3088}
3089
3090void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) {
3091 if (IsR6()) {
3092 Bcond(label, kCondGEU, rs, rt);
3093 } else if (Branch::IsUncond(kCondGEU, rs, rt)) {
3094 B(label);
3095 } else {
3096 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003097 GenerateSltForCondBranch(/* unsigned_slt */ true, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003098 Beqz(AT, label);
jeffhao7fbee072012-08-24 17:56:54 -07003099 }
3100}
3101
Chris Larsenb74353a2015-11-20 09:07:09 -08003102void MipsAssembler::Bc1f(MipsLabel* label) {
3103 Bc1f(0, label);
3104}
3105
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003106void MipsAssembler::Bc1f(int cc, MipsLabel* label) {
3107 CHECK(IsUint<3>(cc)) << cc;
3108 Bcond(label, kCondF, static_cast<Register>(cc), ZERO);
3109}
3110
Chris Larsenb74353a2015-11-20 09:07:09 -08003111void MipsAssembler::Bc1t(MipsLabel* label) {
3112 Bc1t(0, label);
3113}
3114
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003115void MipsAssembler::Bc1t(int cc, MipsLabel* label) {
3116 CHECK(IsUint<3>(cc)) << cc;
3117 Bcond(label, kCondT, static_cast<Register>(cc), ZERO);
3118}
3119
3120void MipsAssembler::Bc1eqz(FRegister ft, MipsLabel* label) {
3121 Bcond(label, kCondF, static_cast<Register>(ft), ZERO);
3122}
3123
3124void MipsAssembler::Bc1nez(FRegister ft, MipsLabel* label) {
3125 Bcond(label, kCondT, static_cast<Register>(ft), ZERO);
3126}
3127
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003128void MipsAssembler::AdjustBaseAndOffset(Register& base,
3129 int32_t& offset,
3130 bool is_doubleword,
3131 bool is_float) {
3132 // This method is used to adjust the base register and offset pair
3133 // for a load/store when the offset doesn't fit into int16_t.
3134 // It is assumed that `base + offset` is sufficiently aligned for memory
3135 // operands that are machine word in size or smaller. For doubleword-sized
3136 // operands it's assumed that `base` is a multiple of 8, while `offset`
3137 // may be a multiple of 4 (e.g. 4-byte-aligned long and double arguments
3138 // and spilled variables on the stack accessed relative to the stack
3139 // pointer register).
3140 // We preserve the "alignment" of `offset` by adjusting it by a multiple of 8.
3141 CHECK_NE(base, AT); // Must not overwrite the register `base` while loading `offset`.
3142
3143 bool doubleword_aligned = IsAligned<kMipsDoublewordSize>(offset);
3144 bool two_accesses = is_doubleword && (!is_float || !doubleword_aligned);
3145
3146 // IsInt<16> must be passed a signed value, hence the static cast below.
3147 if (IsInt<16>(offset) &&
3148 (!two_accesses || IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
3149 // Nothing to do: `offset` (and, if needed, `offset + 4`) fits into int16_t.
3150 return;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003151 }
3152
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003153 // Remember the "(mis)alignment" of `offset`, it will be checked at the end.
3154 uint32_t misalignment = offset & (kMipsDoublewordSize - 1);
3155
3156 // Do not load the whole 32-bit `offset` if it can be represented as
3157 // a sum of two 16-bit signed offsets. This can save an instruction or two.
3158 // To simplify matters, only do this for a symmetric range of offsets from
3159 // about -64KB to about +64KB, allowing further addition of 4 when accessing
3160 // 64-bit variables with two 32-bit accesses.
3161 constexpr int32_t kMinOffsetForSimpleAdjustment = 0x7ff8; // Max int16_t that's a multiple of 8.
3162 constexpr int32_t kMaxOffsetForSimpleAdjustment = 2 * kMinOffsetForSimpleAdjustment;
3163 if (0 <= offset && offset <= kMaxOffsetForSimpleAdjustment) {
3164 Addiu(AT, base, kMinOffsetForSimpleAdjustment);
3165 offset -= kMinOffsetForSimpleAdjustment;
3166 } else if (-kMaxOffsetForSimpleAdjustment <= offset && offset < 0) {
3167 Addiu(AT, base, -kMinOffsetForSimpleAdjustment);
3168 offset += kMinOffsetForSimpleAdjustment;
3169 } else if (IsR6()) {
3170 // On R6 take advantage of the aui instruction, e.g.:
3171 // aui AT, base, offset_high
3172 // lw reg_lo, offset_low(AT)
3173 // lw reg_hi, (offset_low+4)(AT)
3174 // or when offset_low+4 overflows int16_t:
3175 // aui AT, base, offset_high
3176 // addiu AT, AT, 8
3177 // lw reg_lo, (offset_low-8)(AT)
3178 // lw reg_hi, (offset_low-4)(AT)
3179 int16_t offset_high = High16Bits(offset);
3180 int16_t offset_low = Low16Bits(offset);
3181 offset_high += (offset_low < 0) ? 1 : 0; // Account for offset sign extension in load/store.
3182 Aui(AT, base, offset_high);
3183 if (two_accesses && !IsInt<16>(static_cast<int32_t>(offset_low + kMipsWordSize))) {
3184 // Avoid overflow in the 16-bit offset of the load/store instruction when adding 4.
3185 Addiu(AT, AT, kMipsDoublewordSize);
3186 offset_low -= kMipsDoublewordSize;
3187 }
3188 offset = offset_low;
3189 } else {
3190 // Do not load the whole 32-bit `offset` if it can be represented as
3191 // a sum of three 16-bit signed offsets. This can save an instruction.
3192 // To simplify matters, only do this for a symmetric range of offsets from
3193 // about -96KB to about +96KB, allowing further addition of 4 when accessing
3194 // 64-bit variables with two 32-bit accesses.
3195 constexpr int32_t kMinOffsetForMediumAdjustment = 2 * kMinOffsetForSimpleAdjustment;
3196 constexpr int32_t kMaxOffsetForMediumAdjustment = 3 * kMinOffsetForSimpleAdjustment;
3197 if (0 <= offset && offset <= kMaxOffsetForMediumAdjustment) {
3198 Addiu(AT, base, kMinOffsetForMediumAdjustment / 2);
3199 Addiu(AT, AT, kMinOffsetForMediumAdjustment / 2);
3200 offset -= kMinOffsetForMediumAdjustment;
3201 } else if (-kMaxOffsetForMediumAdjustment <= offset && offset < 0) {
3202 Addiu(AT, base, -kMinOffsetForMediumAdjustment / 2);
3203 Addiu(AT, AT, -kMinOffsetForMediumAdjustment / 2);
3204 offset += kMinOffsetForMediumAdjustment;
3205 } else {
3206 // Now that all shorter options have been exhausted, load the full 32-bit offset.
3207 int32_t loaded_offset = RoundDown(offset, kMipsDoublewordSize);
3208 LoadConst32(AT, loaded_offset);
3209 Addu(AT, AT, base);
3210 offset -= loaded_offset;
3211 }
3212 }
3213 base = AT;
3214
3215 CHECK(IsInt<16>(offset));
3216 if (two_accesses) {
3217 CHECK(IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)));
3218 }
3219 CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
3220}
3221
Alexey Frunze2923db72016-08-20 01:55:47 -07003222void MipsAssembler::LoadFromOffset(LoadOperandType type,
3223 Register reg,
3224 Register base,
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003225 int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003226 LoadFromOffset<>(type, reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003227}
3228
3229void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003230 LoadSFromOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003231}
3232
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003233void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003234 LoadDFromOffset<>(reg, base, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003235}
3236
3237void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
3238 size_t size) {
3239 MipsManagedRegister dst = m_dst.AsMips();
3240 if (dst.IsNoRegister()) {
3241 CHECK_EQ(0u, size) << dst;
3242 } else if (dst.IsCoreRegister()) {
3243 CHECK_EQ(kMipsWordSize, size) << dst;
3244 LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
3245 } else if (dst.IsRegisterPair()) {
3246 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3247 LoadFromOffset(kLoadDoubleword, dst.AsRegisterPairLow(), src_register, src_offset);
3248 } else if (dst.IsFRegister()) {
3249 if (size == kMipsWordSize) {
3250 LoadSFromOffset(dst.AsFRegister(), src_register, src_offset);
3251 } else {
3252 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3253 LoadDFromOffset(dst.AsFRegister(), src_register, src_offset);
3254 }
Alexey Frunze1b8464d2016-11-12 17:22:05 -08003255 } else if (dst.IsDRegister()) {
3256 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3257 LoadDFromOffset(dst.AsOverlappingDRegisterLow(), src_register, src_offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003258 }
jeffhao7fbee072012-08-24 17:56:54 -07003259}
3260
Alexey Frunze2923db72016-08-20 01:55:47 -07003261void MipsAssembler::StoreToOffset(StoreOperandType type,
3262 Register reg,
3263 Register base,
jeffhao7fbee072012-08-24 17:56:54 -07003264 int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003265 StoreToOffset<>(type, reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003266}
3267
Goran Jakovljevicff734982015-08-24 12:58:55 +00003268void MipsAssembler::StoreSToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003269 StoreSToOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003270}
3271
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003272void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003273 StoreDToOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003274}
3275
David Srbeckydd973932015-04-07 20:29:48 +01003276static dwarf::Reg DWARFReg(Register reg) {
3277 return dwarf::Reg::MipsCore(static_cast<int>(reg));
3278}
3279
Ian Rogers790a6b72014-04-01 10:36:00 -07003280constexpr size_t kFramePointerSize = 4;
3281
Vladimir Marko32248382016-05-19 10:37:24 +01003282void MipsAssembler::BuildFrame(size_t frame_size,
3283 ManagedRegister method_reg,
3284 ArrayRef<const ManagedRegister> callee_save_regs,
Dmitry Petrochenkofca82202014-03-21 11:21:37 +07003285 const ManagedRegisterEntrySpills& entry_spills) {
jeffhao7fbee072012-08-24 17:56:54 -07003286 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003287 DCHECK(!overwriting_);
jeffhao7fbee072012-08-24 17:56:54 -07003288
3289 // Increase frame to required size.
3290 IncreaseFrameSize(frame_size);
3291
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003292 // Push callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07003293 int stack_offset = frame_size - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003294 StoreToOffset(kStoreWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003295 cfi_.RelOffset(DWARFReg(RA), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07003296 for (int i = callee_save_regs.size() - 1; i >= 0; --i) {
Ian Rogers790a6b72014-04-01 10:36:00 -07003297 stack_offset -= kFramePointerSize;
Vladimir Marko32248382016-05-19 10:37:24 +01003298 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07003299 StoreToOffset(kStoreWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003300 cfi_.RelOffset(DWARFReg(reg), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07003301 }
3302
3303 // Write out Method*.
3304 StoreToOffset(kStoreWord, method_reg.AsMips().AsCoreRegister(), SP, 0);
3305
3306 // Write out entry spills.
Goran Jakovljevicff734982015-08-24 12:58:55 +00003307 int32_t offset = frame_size + kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003308 for (size_t i = 0; i < entry_spills.size(); ++i) {
Goran Jakovljevicff734982015-08-24 12:58:55 +00003309 MipsManagedRegister reg = entry_spills.at(i).AsMips();
3310 if (reg.IsNoRegister()) {
3311 ManagedRegisterSpill spill = entry_spills.at(i);
3312 offset += spill.getSize();
3313 } else if (reg.IsCoreRegister()) {
3314 StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003315 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003316 } else if (reg.IsFRegister()) {
3317 StoreSToOffset(reg.AsFRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003318 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003319 } else if (reg.IsDRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003320 StoreDToOffset(reg.AsOverlappingDRegisterLow(), SP, offset);
3321 offset += kMipsDoublewordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003322 }
jeffhao7fbee072012-08-24 17:56:54 -07003323 }
3324}
3325
3326void MipsAssembler::RemoveFrame(size_t frame_size,
Vladimir Marko32248382016-05-19 10:37:24 +01003327 ArrayRef<const ManagedRegister> callee_save_regs) {
jeffhao7fbee072012-08-24 17:56:54 -07003328 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003329 DCHECK(!overwriting_);
David Srbeckydd973932015-04-07 20:29:48 +01003330 cfi_.RememberState();
jeffhao7fbee072012-08-24 17:56:54 -07003331
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003332 // Pop callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07003333 int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003334 for (size_t i = 0; i < callee_save_regs.size(); ++i) {
Vladimir Marko32248382016-05-19 10:37:24 +01003335 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07003336 LoadFromOffset(kLoadWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003337 cfi_.Restore(DWARFReg(reg));
Ian Rogers790a6b72014-04-01 10:36:00 -07003338 stack_offset += kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003339 }
3340 LoadFromOffset(kLoadWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003341 cfi_.Restore(DWARFReg(RA));
jeffhao7fbee072012-08-24 17:56:54 -07003342
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003343 // Adjust the stack pointer in the delay slot if doing so doesn't break CFI.
3344 bool exchange = IsInt<16>(static_cast<int32_t>(frame_size));
3345 bool reordering = SetReorder(false);
3346 if (exchange) {
3347 // Jump to the return address.
3348 Jr(RA);
3349 // Decrease frame to required size.
3350 DecreaseFrameSize(frame_size); // Single instruction in delay slot.
3351 } else {
3352 // Decrease frame to required size.
3353 DecreaseFrameSize(frame_size);
3354 // Jump to the return address.
3355 Jr(RA);
3356 Nop(); // In delay slot.
3357 }
3358 SetReorder(reordering);
David Srbeckydd973932015-04-07 20:29:48 +01003359
3360 // The CFI should be restored for any code that follows the exit block.
3361 cfi_.RestoreState();
3362 cfi_.DefCFAOffset(frame_size);
jeffhao7fbee072012-08-24 17:56:54 -07003363}
3364
3365void MipsAssembler::IncreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003366 CHECK_ALIGNED(adjust, kFramePointerSize);
3367 Addiu32(SP, SP, -adjust);
David Srbeckydd973932015-04-07 20:29:48 +01003368 cfi_.AdjustCFAOffset(adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003369 if (overwriting_) {
3370 cfi_.OverrideDelayedPC(overwrite_location_);
3371 }
jeffhao7fbee072012-08-24 17:56:54 -07003372}
3373
3374void MipsAssembler::DecreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003375 CHECK_ALIGNED(adjust, kFramePointerSize);
3376 Addiu32(SP, SP, adjust);
David Srbeckydd973932015-04-07 20:29:48 +01003377 cfi_.AdjustCFAOffset(-adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003378 if (overwriting_) {
3379 cfi_.OverrideDelayedPC(overwrite_location_);
3380 }
jeffhao7fbee072012-08-24 17:56:54 -07003381}
3382
3383void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
3384 MipsManagedRegister src = msrc.AsMips();
3385 if (src.IsNoRegister()) {
3386 CHECK_EQ(0u, size);
3387 } else if (src.IsCoreRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003388 CHECK_EQ(kMipsWordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07003389 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3390 } else if (src.IsRegisterPair()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003391 CHECK_EQ(kMipsDoublewordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07003392 StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
3393 StoreToOffset(kStoreWord, src.AsRegisterPairHigh(),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003394 SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003395 } else if (src.IsFRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003396 if (size == kMipsWordSize) {
3397 StoreSToOffset(src.AsFRegister(), SP, dest.Int32Value());
3398 } else {
3399 CHECK_EQ(kMipsDoublewordSize, size);
3400 StoreDToOffset(src.AsFRegister(), SP, dest.Int32Value());
3401 }
Alexey Frunze1b8464d2016-11-12 17:22:05 -08003402 } else if (src.IsDRegister()) {
3403 CHECK_EQ(kMipsDoublewordSize, size);
3404 StoreDToOffset(src.AsOverlappingDRegisterLow(), SP, dest.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003405 }
3406}
3407
3408void MipsAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
3409 MipsManagedRegister src = msrc.AsMips();
3410 CHECK(src.IsCoreRegister());
3411 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3412}
3413
3414void MipsAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
3415 MipsManagedRegister src = msrc.AsMips();
3416 CHECK(src.IsCoreRegister());
3417 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3418}
3419
3420void MipsAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
3421 ManagedRegister mscratch) {
3422 MipsManagedRegister scratch = mscratch.AsMips();
3423 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003424 LoadConst32(scratch.AsCoreRegister(), imm);
jeffhao7fbee072012-08-24 17:56:54 -07003425 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
3426}
3427
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003428void MipsAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs,
3429 FrameOffset fr_offs,
jeffhao7fbee072012-08-24 17:56:54 -07003430 ManagedRegister mscratch) {
3431 MipsManagedRegister scratch = mscratch.AsMips();
3432 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003433 Addiu32(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003434 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3435 S1, thr_offs.Int32Value());
3436}
3437
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003438void MipsAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003439 StoreToOffset(kStoreWord, SP, S1, thr_offs.Int32Value());
3440}
3441
3442void MipsAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
3443 FrameOffset in_off, ManagedRegister mscratch) {
3444 MipsManagedRegister src = msrc.AsMips();
3445 MipsManagedRegister scratch = mscratch.AsMips();
3446 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3447 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003448 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003449}
3450
3451void MipsAssembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) {
3452 return EmitLoad(mdest, SP, src.Int32Value(), size);
3453}
3454
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003455void MipsAssembler::LoadFromThread(ManagedRegister mdest, ThreadOffset32 src, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003456 return EmitLoad(mdest, S1, src.Int32Value(), size);
3457}
3458
3459void MipsAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) {
3460 MipsManagedRegister dest = mdest.AsMips();
3461 CHECK(dest.IsCoreRegister());
3462 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value());
3463}
3464
Mathieu Chartiere401d142015-04-22 13:56:20 -07003465void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs,
Roland Levillain4d027112015-07-01 15:41:14 +01003466 bool unpoison_reference) {
jeffhao7fbee072012-08-24 17:56:54 -07003467 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003468 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07003469 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
3470 base.AsMips().AsCoreRegister(), offs.Int32Value());
Roland Levillain4d027112015-07-01 15:41:14 +01003471 if (kPoisonHeapReferences && unpoison_reference) {
Hiroshi Yamauchie63a7452014-02-27 14:44:36 -08003472 Subu(dest.AsCoreRegister(), ZERO, dest.AsCoreRegister());
3473 }
jeffhao7fbee072012-08-24 17:56:54 -07003474}
3475
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003476void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003477 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003478 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07003479 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
3480 base.AsMips().AsCoreRegister(), offs.Int32Value());
3481}
3482
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003483void MipsAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset32 offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003484 MipsManagedRegister dest = mdest.AsMips();
3485 CHECK(dest.IsCoreRegister());
3486 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), S1, offs.Int32Value());
3487}
3488
3489void MipsAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
3490 UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips";
3491}
3492
3493void MipsAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
3494 UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips";
3495}
3496
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003497void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003498 MipsManagedRegister dest = mdest.AsMips();
3499 MipsManagedRegister src = msrc.AsMips();
3500 if (!dest.Equals(src)) {
3501 if (dest.IsCoreRegister()) {
3502 CHECK(src.IsCoreRegister()) << src;
3503 Move(dest.AsCoreRegister(), src.AsCoreRegister());
3504 } else if (dest.IsFRegister()) {
3505 CHECK(src.IsFRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003506 if (size == kMipsWordSize) {
3507 MovS(dest.AsFRegister(), src.AsFRegister());
3508 } else {
3509 CHECK_EQ(kMipsDoublewordSize, size);
3510 MovD(dest.AsFRegister(), src.AsFRegister());
3511 }
jeffhao7fbee072012-08-24 17:56:54 -07003512 } else if (dest.IsDRegister()) {
3513 CHECK(src.IsDRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003514 MovD(dest.AsOverlappingDRegisterLow(), src.AsOverlappingDRegisterLow());
jeffhao7fbee072012-08-24 17:56:54 -07003515 } else {
3516 CHECK(dest.IsRegisterPair()) << dest;
3517 CHECK(src.IsRegisterPair()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003518 // Ensure that the first move doesn't clobber the input of the second.
jeffhao7fbee072012-08-24 17:56:54 -07003519 if (src.AsRegisterPairHigh() != dest.AsRegisterPairLow()) {
3520 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
3521 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
3522 } else {
3523 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
3524 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
3525 }
3526 }
3527 }
3528}
3529
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003530void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003531 MipsManagedRegister scratch = mscratch.AsMips();
3532 CHECK(scratch.IsCoreRegister()) << scratch;
3533 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3534 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
3535}
3536
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003537void MipsAssembler::CopyRawPtrFromThread(FrameOffset fr_offs,
3538 ThreadOffset32 thr_offs,
3539 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003540 MipsManagedRegister scratch = mscratch.AsMips();
3541 CHECK(scratch.IsCoreRegister()) << scratch;
3542 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3543 S1, thr_offs.Int32Value());
3544 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3545 SP, fr_offs.Int32Value());
3546}
3547
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003548void MipsAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs,
3549 FrameOffset fr_offs,
3550 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003551 MipsManagedRegister scratch = mscratch.AsMips();
3552 CHECK(scratch.IsCoreRegister()) << scratch;
3553 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3554 SP, fr_offs.Int32Value());
3555 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3556 S1, thr_offs.Int32Value());
3557}
3558
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003559void MipsAssembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003560 MipsManagedRegister scratch = mscratch.AsMips();
3561 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003562 CHECK(size == kMipsWordSize || size == kMipsDoublewordSize) << size;
3563 if (size == kMipsWordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07003564 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3565 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003566 } else if (size == kMipsDoublewordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07003567 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3568 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003569 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + kMipsWordSize);
3570 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003571 }
3572}
3573
3574void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
3575 ManagedRegister mscratch, size_t size) {
3576 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003577 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003578 LoadFromOffset(kLoadWord, scratch, src_base.AsMips().AsCoreRegister(), src_offset.Int32Value());
3579 StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
3580}
3581
3582void MipsAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
3583 ManagedRegister mscratch, size_t size) {
3584 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003585 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003586 LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
3587 StoreToOffset(kStoreWord, scratch, dest_base.AsMips().AsCoreRegister(), dest_offset.Int32Value());
3588}
3589
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003590void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
3591 FrameOffset src_base ATTRIBUTE_UNUSED,
3592 Offset src_offset ATTRIBUTE_UNUSED,
3593 ManagedRegister mscratch ATTRIBUTE_UNUSED,
3594 size_t size ATTRIBUTE_UNUSED) {
3595 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003596}
3597
3598void MipsAssembler::Copy(ManagedRegister dest, Offset dest_offset,
3599 ManagedRegister src, Offset src_offset,
3600 ManagedRegister mscratch, size_t size) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003601 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003602 Register scratch = mscratch.AsMips().AsCoreRegister();
3603 LoadFromOffset(kLoadWord, scratch, src.AsMips().AsCoreRegister(), src_offset.Int32Value());
3604 StoreToOffset(kStoreWord, scratch, dest.AsMips().AsCoreRegister(), dest_offset.Int32Value());
3605}
3606
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003607void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
3608 Offset dest_offset ATTRIBUTE_UNUSED,
3609 FrameOffset src ATTRIBUTE_UNUSED,
3610 Offset src_offset ATTRIBUTE_UNUSED,
3611 ManagedRegister mscratch ATTRIBUTE_UNUSED,
3612 size_t size ATTRIBUTE_UNUSED) {
3613 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003614}
3615
3616void MipsAssembler::MemoryBarrier(ManagedRegister) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003617 // TODO: sync?
3618 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003619}
3620
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003621void MipsAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003622 FrameOffset handle_scope_offset,
3623 ManagedRegister min_reg,
3624 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003625 MipsManagedRegister out_reg = mout_reg.AsMips();
3626 MipsManagedRegister in_reg = min_reg.AsMips();
3627 CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
3628 CHECK(out_reg.IsCoreRegister()) << out_reg;
3629 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003630 MipsLabel null_arg;
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003631 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3632 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003633 // E.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset).
jeffhao7fbee072012-08-24 17:56:54 -07003634 if (in_reg.IsNoRegister()) {
3635 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003636 SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003637 in_reg = out_reg;
3638 }
3639 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003640 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003641 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003642 Beqz(in_reg.AsCoreRegister(), &null_arg);
3643 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3644 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003645 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003646 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003647 }
3648}
3649
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003650void MipsAssembler::CreateHandleScopeEntry(FrameOffset out_off,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003651 FrameOffset handle_scope_offset,
3652 ManagedRegister mscratch,
3653 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003654 MipsManagedRegister scratch = mscratch.AsMips();
3655 CHECK(scratch.IsCoreRegister()) << scratch;
3656 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003657 MipsLabel null_arg;
3658 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003659 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3660 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003661 // E.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset).
3662 Beqz(scratch.AsCoreRegister(), &null_arg);
3663 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3664 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003665 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003666 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003667 }
3668 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
3669}
3670
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003671// Given a handle scope entry, load the associated reference.
3672void MipsAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003673 ManagedRegister min_reg) {
jeffhao7fbee072012-08-24 17:56:54 -07003674 MipsManagedRegister out_reg = mout_reg.AsMips();
3675 MipsManagedRegister in_reg = min_reg.AsMips();
3676 CHECK(out_reg.IsCoreRegister()) << out_reg;
3677 CHECK(in_reg.IsCoreRegister()) << in_reg;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003678 MipsLabel null_arg;
jeffhao7fbee072012-08-24 17:56:54 -07003679 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003680 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003681 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003682 Beqz(in_reg.AsCoreRegister(), &null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003683 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
3684 in_reg.AsCoreRegister(), 0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003685 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003686}
3687
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003688void MipsAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,
3689 bool could_be_null ATTRIBUTE_UNUSED) {
3690 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003691}
3692
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003693void MipsAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED,
3694 bool could_be_null ATTRIBUTE_UNUSED) {
3695 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003696}
3697
3698void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) {
3699 MipsManagedRegister base = mbase.AsMips();
3700 MipsManagedRegister scratch = mscratch.AsMips();
3701 CHECK(base.IsCoreRegister()) << base;
3702 CHECK(scratch.IsCoreRegister()) << scratch;
3703 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3704 base.AsCoreRegister(), offset.Int32Value());
3705 Jalr(scratch.AsCoreRegister());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003706 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003707 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003708}
3709
3710void MipsAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
3711 MipsManagedRegister scratch = mscratch.AsMips();
3712 CHECK(scratch.IsCoreRegister()) << scratch;
3713 // Call *(*(SP + base) + offset)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003714 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, base.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003715 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3716 scratch.AsCoreRegister(), offset.Int32Value());
3717 Jalr(scratch.AsCoreRegister());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003718 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003719 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003720}
3721
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003722void MipsAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UNUSED,
3723 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
Ian Rogers468532e2013-08-05 10:56:33 -07003724 UNIMPLEMENTED(FATAL) << "no mips implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003725}
3726
3727void MipsAssembler::GetCurrentThread(ManagedRegister tr) {
3728 Move(tr.AsMips().AsCoreRegister(), S1);
3729}
3730
3731void MipsAssembler::GetCurrentThread(FrameOffset offset,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003732 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
jeffhao7fbee072012-08-24 17:56:54 -07003733 StoreToOffset(kStoreWord, S1, SP, offset.Int32Value());
3734}
3735
jeffhao7fbee072012-08-24 17:56:54 -07003736void MipsAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
3737 MipsManagedRegister scratch = mscratch.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003738 exception_blocks_.emplace_back(scratch, stack_adjust);
jeffhao7fbee072012-08-24 17:56:54 -07003739 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
Andreas Gampe542451c2016-07-26 09:02:02 -07003740 S1, Thread::ExceptionOffset<kMipsPointerSize>().Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003741 Bnez(scratch.AsCoreRegister(), exception_blocks_.back().Entry());
jeffhao7fbee072012-08-24 17:56:54 -07003742}
3743
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003744void MipsAssembler::EmitExceptionPoll(MipsExceptionSlowPath* exception) {
3745 Bind(exception->Entry());
3746 if (exception->stack_adjust_ != 0) { // Fix up the frame.
3747 DecreaseFrameSize(exception->stack_adjust_);
jeffhao7fbee072012-08-24 17:56:54 -07003748 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003749 // Pass exception object as argument.
3750 // Don't care about preserving A0 as this call won't return.
3751 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
3752 Move(A0, exception->scratch_.AsCoreRegister());
3753 // Set up call to Thread::Current()->pDeliverException.
3754 LoadFromOffset(kLoadWord, T9, S1,
Andreas Gampe542451c2016-07-26 09:02:02 -07003755 QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, pDeliverException).Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003756 Jr(T9);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003757 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003758
3759 // Call never returns.
3760 Break();
jeffhao7fbee072012-08-24 17:56:54 -07003761}
3762
3763} // namespace mips
3764} // namespace art