blob: b972c70eb94699311b8695906bcf25d75cbd241b [file] [log] [blame]
jeffhao7fbee072012-08-24 17:56:54 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "assembler_mips.h"
18
Vladimir Marko80afd022015-05-19 18:08:00 +010019#include "base/bit_utils.h"
Elliott Hughes1aa246d2012-12-13 09:29:36 -080020#include "base/casts.h"
Ian Rogers166db042013-07-26 12:05:57 -070021#include "entrypoints/quick/quick_entrypoints.h"
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +020022#include "entrypoints/quick/quick_entrypoints_enum.h"
jeffhao7fbee072012-08-24 17:56:54 -070023#include "memory_region.h"
jeffhao7fbee072012-08-24 17:56:54 -070024#include "thread.h"
25
26namespace art {
27namespace mips {
jeffhao7fbee072012-08-24 17:56:54 -070028
Andreas Gampe542451c2016-07-26 09:02:02 -070029static_assert(static_cast<size_t>(kMipsPointerSize) == kMipsWordSize,
30 "Unexpected Mips pointer size.");
31static_assert(kMipsPointerSize == PointerSize::k32, "Unexpected Mips pointer size.");
32
33
jeffhao7fbee072012-08-24 17:56:54 -070034std::ostream& operator<<(std::ostream& os, const DRegister& rhs) {
35 if (rhs >= D0 && rhs < kNumberOfDRegisters) {
36 os << "d" << static_cast<int>(rhs);
37 } else {
38 os << "DRegister[" << static_cast<int>(rhs) << "]";
39 }
40 return os;
41}
42
Alexey Frunze57eb0f52016-07-29 22:04:46 -070043MipsAssembler::DelaySlot::DelaySlot()
44 : instruction_(0),
45 gpr_outs_mask_(0),
46 gpr_ins_mask_(0),
47 fpr_outs_mask_(0),
48 fpr_ins_mask_(0),
49 cc_outs_mask_(0),
50 cc_ins_mask_(0) {}
51
52void MipsAssembler::DsFsmInstr(uint32_t instruction,
53 uint32_t gpr_outs_mask,
54 uint32_t gpr_ins_mask,
55 uint32_t fpr_outs_mask,
56 uint32_t fpr_ins_mask,
57 uint32_t cc_outs_mask,
58 uint32_t cc_ins_mask) {
59 if (!reordering_) {
60 CHECK_EQ(ds_fsm_state_, kExpectingLabel);
61 CHECK_EQ(delay_slot_.instruction_, 0u);
62 return;
63 }
64 switch (ds_fsm_state_) {
65 case kExpectingLabel:
66 break;
67 case kExpectingInstruction:
68 CHECK_EQ(ds_fsm_target_pc_ + sizeof(uint32_t), buffer_.Size());
69 // If the last instruction is not suitable for delay slots, drop
70 // the PC of the label preceding it so that no unconditional branch
71 // uses this instruction to fill its delay slot.
72 if (instruction == 0) {
73 DsFsmDropLabel(); // Sets ds_fsm_state_ = kExpectingLabel.
74 } else {
75 // Otherwise wait for another instruction or label before we can
76 // commit the label PC. The label PC will be dropped if instead
77 // of another instruction or label there's a call from the code
78 // generator to CodePosition() to record the buffer size.
79 // Instructions after which the buffer size is recorded cannot
80 // be moved into delay slots or anywhere else because they may
81 // trigger signals and the signal handlers expect these signals
82 // to be coming from the instructions immediately preceding the
83 // recorded buffer locations.
84 ds_fsm_state_ = kExpectingCommit;
85 }
86 break;
87 case kExpectingCommit:
88 CHECK_EQ(ds_fsm_target_pc_ + 2 * sizeof(uint32_t), buffer_.Size());
89 DsFsmCommitLabel(); // Sets ds_fsm_state_ = kExpectingLabel.
90 break;
91 }
92 delay_slot_.instruction_ = instruction;
93 delay_slot_.gpr_outs_mask_ = gpr_outs_mask & ~1u; // Ignore register ZERO.
94 delay_slot_.gpr_ins_mask_ = gpr_ins_mask & ~1u; // Ignore register ZERO.
95 delay_slot_.fpr_outs_mask_ = fpr_outs_mask;
96 delay_slot_.fpr_ins_mask_ = fpr_ins_mask;
97 delay_slot_.cc_outs_mask_ = cc_outs_mask;
98 delay_slot_.cc_ins_mask_ = cc_ins_mask;
99}
100
101void MipsAssembler::DsFsmLabel() {
102 if (!reordering_) {
103 CHECK_EQ(ds_fsm_state_, kExpectingLabel);
104 CHECK_EQ(delay_slot_.instruction_, 0u);
105 return;
106 }
107 switch (ds_fsm_state_) {
108 case kExpectingLabel:
109 ds_fsm_target_pc_ = buffer_.Size();
110 ds_fsm_state_ = kExpectingInstruction;
111 break;
112 case kExpectingInstruction:
113 // Allow consecutive labels.
114 CHECK_EQ(ds_fsm_target_pc_, buffer_.Size());
115 break;
116 case kExpectingCommit:
117 CHECK_EQ(ds_fsm_target_pc_ + sizeof(uint32_t), buffer_.Size());
118 DsFsmCommitLabel();
119 ds_fsm_target_pc_ = buffer_.Size();
120 ds_fsm_state_ = kExpectingInstruction;
121 break;
122 }
123 // We cannot move instructions into delay slots across labels.
124 delay_slot_.instruction_ = 0;
125}
126
127void MipsAssembler::DsFsmCommitLabel() {
128 if (ds_fsm_state_ == kExpectingCommit) {
129 ds_fsm_target_pcs_.emplace_back(ds_fsm_target_pc_);
130 }
131 ds_fsm_state_ = kExpectingLabel;
132}
133
134void MipsAssembler::DsFsmDropLabel() {
135 ds_fsm_state_ = kExpectingLabel;
136}
137
138bool MipsAssembler::SetReorder(bool enable) {
139 bool last_state = reordering_;
140 if (last_state != enable) {
141 DsFsmCommitLabel();
142 DsFsmInstrNop(0);
143 }
144 reordering_ = enable;
145 return last_state;
146}
147
148size_t MipsAssembler::CodePosition() {
149 // The last instruction cannot be used in a delay slot, do not commit
150 // the label before it (if any) and clear the delay slot.
151 DsFsmDropLabel();
152 DsFsmInstrNop(0);
153 size_t size = buffer_.Size();
154 // In theory we can get the following sequence:
155 // label1:
156 // instr
157 // label2: # label1 gets committed when label2 is seen
158 // CodePosition() call
159 // and we need to uncommit label1.
160 if (ds_fsm_target_pcs_.size() != 0 && ds_fsm_target_pcs_.back() + sizeof(uint32_t) == size) {
161 ds_fsm_target_pcs_.pop_back();
162 }
163 return size;
164}
165
166void MipsAssembler::DsFsmInstrNop(uint32_t instruction ATTRIBUTE_UNUSED) {
167 DsFsmInstr(0, 0, 0, 0, 0, 0, 0);
168}
169
170void MipsAssembler::DsFsmInstrRrr(uint32_t instruction, Register out, Register in1, Register in2) {
171 DsFsmInstr(instruction, (1u << out), (1u << in1) | (1u << in2), 0, 0, 0, 0);
172}
173
174void MipsAssembler::DsFsmInstrRrrr(uint32_t instruction,
175 Register in1_out,
176 Register in2,
177 Register in3) {
178 DsFsmInstr(instruction, (1u << in1_out), (1u << in1_out) | (1u << in2) | (1u << in3), 0, 0, 0, 0);
179}
180
181void MipsAssembler::DsFsmInstrFff(uint32_t instruction,
182 FRegister out,
183 FRegister in1,
184 FRegister in2) {
185 DsFsmInstr(instruction, 0, 0, (1u << out), (1u << in1) | (1u << in2), 0, 0);
186}
187
188void MipsAssembler::DsFsmInstrFfff(uint32_t instruction,
189 FRegister in1_out,
190 FRegister in2,
191 FRegister in3) {
192 DsFsmInstr(instruction, 0, 0, (1u << in1_out), (1u << in1_out) | (1u << in2) | (1u << in3), 0, 0);
193}
194
195void MipsAssembler::DsFsmInstrRf(uint32_t instruction, Register out, FRegister in) {
196 DsFsmInstr(instruction, (1u << out), 0, 0, (1u << in), 0, 0);
197}
198
199void MipsAssembler::DsFsmInstrFr(uint32_t instruction, FRegister out, Register in) {
200 DsFsmInstr(instruction, 0, (1u << in), (1u << out), 0, 0, 0);
201}
202
203void MipsAssembler::DsFsmInstrFR(uint32_t instruction, FRegister in1, Register in2) {
204 DsFsmInstr(instruction, 0, (1u << in2), 0, (1u << in1), 0, 0);
205}
206
207void MipsAssembler::DsFsmInstrCff(uint32_t instruction, int cc_out, FRegister in1, FRegister in2) {
208 DsFsmInstr(instruction, 0, 0, 0, (1u << in1) | (1u << in2), (1 << cc_out), 0);
209}
210
211void MipsAssembler::DsFsmInstrRrrc(uint32_t instruction,
212 Register in1_out,
213 Register in2,
214 int cc_in) {
215 DsFsmInstr(instruction, (1u << in1_out), (1u << in1_out) | (1u << in2), 0, 0, 0, (1 << cc_in));
216}
217
218void MipsAssembler::DsFsmInstrFffc(uint32_t instruction,
219 FRegister in1_out,
220 FRegister in2,
221 int cc_in) {
222 DsFsmInstr(instruction, 0, 0, (1u << in1_out), (1u << in1_out) | (1u << in2), 0, (1 << cc_in));
223}
224
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200225void MipsAssembler::FinalizeCode() {
226 for (auto& exception_block : exception_blocks_) {
227 EmitExceptionPoll(&exception_block);
228 }
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700229 // Commit the last branch target label (if any) and disable instruction reordering.
230 DsFsmCommitLabel();
231 SetReorder(false);
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700232 EmitLiterals();
Alexey Frunze96b66822016-09-10 02:32:44 -0700233 ReserveJumpTableSpace();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200234 PromoteBranches();
235}
236
237void MipsAssembler::FinalizeInstructions(const MemoryRegion& region) {
Vladimir Marko10ef6942015-10-22 15:25:54 +0100238 size_t number_of_delayed_adjust_pcs = cfi().NumberOfDelayedAdvancePCs();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200239 EmitBranches();
Alexey Frunze96b66822016-09-10 02:32:44 -0700240 EmitJumpTables();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200241 Assembler::FinalizeInstructions(region);
Vladimir Marko10ef6942015-10-22 15:25:54 +0100242 PatchCFI(number_of_delayed_adjust_pcs);
243}
244
245void MipsAssembler::PatchCFI(size_t number_of_delayed_adjust_pcs) {
246 if (cfi().NumberOfDelayedAdvancePCs() == 0u) {
247 DCHECK_EQ(number_of_delayed_adjust_pcs, 0u);
248 return;
249 }
250
251 typedef DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC DelayedAdvancePC;
252 const auto data = cfi().ReleaseStreamAndPrepareForDelayedAdvancePC();
253 const std::vector<uint8_t>& old_stream = data.first;
254 const std::vector<DelayedAdvancePC>& advances = data.second;
255
256 // PCs recorded before EmitBranches() need to be adjusted.
257 // PCs recorded during EmitBranches() are already adjusted.
258 // Both ranges are separately sorted but they may overlap.
259 if (kIsDebugBuild) {
260 auto cmp = [](const DelayedAdvancePC& lhs, const DelayedAdvancePC& rhs) {
261 return lhs.pc < rhs.pc;
262 };
263 CHECK(std::is_sorted(advances.begin(), advances.begin() + number_of_delayed_adjust_pcs, cmp));
264 CHECK(std::is_sorted(advances.begin() + number_of_delayed_adjust_pcs, advances.end(), cmp));
265 }
266
267 // Append initial CFI data if any.
268 size_t size = advances.size();
269 DCHECK_NE(size, 0u);
270 cfi().AppendRawData(old_stream, 0u, advances[0].stream_pos);
271 // Emit PC adjustments interleaved with the old CFI stream.
272 size_t adjust_pos = 0u;
273 size_t late_emit_pos = number_of_delayed_adjust_pcs;
274 while (adjust_pos != number_of_delayed_adjust_pcs || late_emit_pos != size) {
275 size_t adjusted_pc = (adjust_pos != number_of_delayed_adjust_pcs)
276 ? GetAdjustedPosition(advances[adjust_pos].pc)
277 : static_cast<size_t>(-1);
278 size_t late_emit_pc = (late_emit_pos != size)
279 ? advances[late_emit_pos].pc
280 : static_cast<size_t>(-1);
281 size_t advance_pc = std::min(adjusted_pc, late_emit_pc);
282 DCHECK_NE(advance_pc, static_cast<size_t>(-1));
283 size_t entry = (adjusted_pc <= late_emit_pc) ? adjust_pos : late_emit_pos;
284 if (adjusted_pc <= late_emit_pc) {
285 ++adjust_pos;
286 } else {
287 ++late_emit_pos;
288 }
289 cfi().AdvancePC(advance_pc);
290 size_t end_pos = (entry + 1u == size) ? old_stream.size() : advances[entry + 1u].stream_pos;
291 cfi().AppendRawData(old_stream, advances[entry].stream_pos, end_pos);
292 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200293}
294
295void MipsAssembler::EmitBranches() {
296 CHECK(!overwriting_);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700297 CHECK(!reordering_);
298 // Now that everything has its final position in the buffer (the branches have
299 // been promoted), adjust the target label PCs.
300 for (size_t cnt = ds_fsm_target_pcs_.size(), i = 0; i < cnt; i++) {
301 ds_fsm_target_pcs_[i] = GetAdjustedPosition(ds_fsm_target_pcs_[i]);
302 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200303 // Switch from appending instructions at the end of the buffer to overwriting
304 // existing instructions (branch placeholders) in the buffer.
305 overwriting_ = true;
306 for (auto& branch : branches_) {
307 EmitBranch(&branch);
308 }
309 overwriting_ = false;
310}
311
312void MipsAssembler::Emit(uint32_t value) {
313 if (overwriting_) {
314 // Branches to labels are emitted into their placeholders here.
315 buffer_.Store<uint32_t>(overwrite_location_, value);
316 overwrite_location_ += sizeof(uint32_t);
317 } else {
318 // Other instructions are simply appended at the end here.
319 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
320 buffer_.Emit<uint32_t>(value);
321 }
jeffhao7fbee072012-08-24 17:56:54 -0700322}
323
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700324uint32_t MipsAssembler::EmitR(int opcode,
325 Register rs,
326 Register rt,
327 Register rd,
328 int shamt,
329 int funct) {
jeffhao7fbee072012-08-24 17:56:54 -0700330 CHECK_NE(rs, kNoRegister);
331 CHECK_NE(rt, kNoRegister);
332 CHECK_NE(rd, kNoRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200333 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
334 static_cast<uint32_t>(rs) << kRsShift |
335 static_cast<uint32_t>(rt) << kRtShift |
336 static_cast<uint32_t>(rd) << kRdShift |
337 shamt << kShamtShift |
338 funct;
jeffhao7fbee072012-08-24 17:56:54 -0700339 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700340 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700341}
342
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700343uint32_t MipsAssembler::EmitI(int opcode, Register rs, Register rt, uint16_t imm) {
jeffhao7fbee072012-08-24 17:56:54 -0700344 CHECK_NE(rs, kNoRegister);
345 CHECK_NE(rt, kNoRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200346 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
347 static_cast<uint32_t>(rs) << kRsShift |
348 static_cast<uint32_t>(rt) << kRtShift |
349 imm;
jeffhao7fbee072012-08-24 17:56:54 -0700350 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700351 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700352}
353
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700354uint32_t MipsAssembler::EmitI21(int opcode, Register rs, uint32_t imm21) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200355 CHECK_NE(rs, kNoRegister);
356 CHECK(IsUint<21>(imm21)) << imm21;
357 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
358 static_cast<uint32_t>(rs) << kRsShift |
359 imm21;
jeffhao7fbee072012-08-24 17:56:54 -0700360 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700361 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700362}
363
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700364uint32_t MipsAssembler::EmitI26(int opcode, uint32_t imm26) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200365 CHECK(IsUint<26>(imm26)) << imm26;
366 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift | imm26;
367 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700368 return encoding;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200369}
370
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700371uint32_t MipsAssembler::EmitFR(int opcode,
372 int fmt,
373 FRegister ft,
374 FRegister fs,
375 FRegister fd,
376 int funct) {
jeffhao7fbee072012-08-24 17:56:54 -0700377 CHECK_NE(ft, kNoFRegister);
378 CHECK_NE(fs, kNoFRegister);
379 CHECK_NE(fd, kNoFRegister);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200380 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
381 fmt << kFmtShift |
382 static_cast<uint32_t>(ft) << kFtShift |
383 static_cast<uint32_t>(fs) << kFsShift |
384 static_cast<uint32_t>(fd) << kFdShift |
385 funct;
jeffhao7fbee072012-08-24 17:56:54 -0700386 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700387 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700388}
389
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700390uint32_t MipsAssembler::EmitFI(int opcode, int fmt, FRegister ft, uint16_t imm) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200391 CHECK_NE(ft, kNoFRegister);
392 uint32_t encoding = static_cast<uint32_t>(opcode) << kOpcodeShift |
393 fmt << kFmtShift |
394 static_cast<uint32_t>(ft) << kFtShift |
395 imm;
jeffhao7fbee072012-08-24 17:56:54 -0700396 Emit(encoding);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700397 return encoding;
jeffhao7fbee072012-08-24 17:56:54 -0700398}
399
jeffhao7fbee072012-08-24 17:56:54 -0700400void MipsAssembler::Addu(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700401 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x21), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700402}
403
jeffhao7fbee072012-08-24 17:56:54 -0700404void MipsAssembler::Addiu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700405 DsFsmInstrRrr(EmitI(0x9, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700406}
407
jeffhao7fbee072012-08-24 17:56:54 -0700408void MipsAssembler::Subu(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700409 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x23), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700410}
411
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200412void MipsAssembler::MultR2(Register rs, Register rt) {
413 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700414 DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x18), ZERO, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700415}
416
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200417void MipsAssembler::MultuR2(Register rs, Register rt) {
418 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700419 DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x19), ZERO, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700420}
421
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200422void MipsAssembler::DivR2(Register rs, Register rt) {
423 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700424 DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1a), ZERO, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700425}
426
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200427void MipsAssembler::DivuR2(Register rs, Register rt) {
428 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700429 DsFsmInstrRrr(EmitR(0, rs, rt, static_cast<Register>(0), 0, 0x1b), ZERO, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700430}
431
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200432void MipsAssembler::MulR2(Register rd, Register rs, Register rt) {
433 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700434 DsFsmInstrRrr(EmitR(0x1c, rs, rt, rd, 0, 2), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200435}
436
437void MipsAssembler::DivR2(Register rd, Register rs, Register rt) {
438 CHECK(!IsR6());
439 DivR2(rs, rt);
440 Mflo(rd);
441}
442
443void MipsAssembler::ModR2(Register rd, Register rs, Register rt) {
444 CHECK(!IsR6());
445 DivR2(rs, rt);
446 Mfhi(rd);
447}
448
449void MipsAssembler::DivuR2(Register rd, Register rs, Register rt) {
450 CHECK(!IsR6());
451 DivuR2(rs, rt);
452 Mflo(rd);
453}
454
455void MipsAssembler::ModuR2(Register rd, Register rs, Register rt) {
456 CHECK(!IsR6());
457 DivuR2(rs, rt);
458 Mfhi(rd);
459}
460
461void MipsAssembler::MulR6(Register rd, Register rs, Register rt) {
462 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700463 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x18), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200464}
465
Alexey Frunze7e99e052015-11-24 19:28:01 -0800466void MipsAssembler::MuhR6(Register rd, Register rs, Register rt) {
467 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700468 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x18), rd, rs, rt);
Alexey Frunze7e99e052015-11-24 19:28:01 -0800469}
470
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200471void MipsAssembler::MuhuR6(Register rd, Register rs, Register rt) {
472 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700473 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x19), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200474}
475
476void MipsAssembler::DivR6(Register rd, Register rs, Register rt) {
477 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700478 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x1a), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200479}
480
481void MipsAssembler::ModR6(Register rd, Register rs, Register rt) {
482 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700483 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x1a), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200484}
485
486void MipsAssembler::DivuR6(Register rd, Register rs, Register rt) {
487 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700488 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 2, 0x1b), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200489}
490
491void MipsAssembler::ModuR6(Register rd, Register rs, Register rt) {
492 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700493 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 3, 0x1b), rd, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200494}
495
jeffhao7fbee072012-08-24 17:56:54 -0700496void MipsAssembler::And(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700497 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x24), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700498}
499
500void MipsAssembler::Andi(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700501 DsFsmInstrRrr(EmitI(0xc, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700502}
503
504void MipsAssembler::Or(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700505 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x25), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700506}
507
508void MipsAssembler::Ori(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700509 DsFsmInstrRrr(EmitI(0xd, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700510}
511
512void MipsAssembler::Xor(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700513 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x26), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700514}
515
516void MipsAssembler::Xori(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700517 DsFsmInstrRrr(EmitI(0xe, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700518}
519
520void MipsAssembler::Nor(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700521 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x27), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700522}
523
Chris Larsene3845472015-11-18 12:27:15 -0800524void MipsAssembler::Movz(Register rd, Register rs, Register rt) {
525 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700526 DsFsmInstrRrrr(EmitR(0, rs, rt, rd, 0, 0x0A), rd, rs, rt);
Chris Larsene3845472015-11-18 12:27:15 -0800527}
528
529void MipsAssembler::Movn(Register rd, Register rs, Register rt) {
530 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700531 DsFsmInstrRrrr(EmitR(0, rs, rt, rd, 0, 0x0B), rd, rs, rt);
Chris Larsene3845472015-11-18 12:27:15 -0800532}
533
534void MipsAssembler::Seleqz(Register rd, Register rs, Register rt) {
535 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700536 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x35), rd, rs, rt);
Chris Larsene3845472015-11-18 12:27:15 -0800537}
538
539void MipsAssembler::Selnez(Register rd, Register rs, Register rt) {
540 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700541 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x37), rd, rs, rt);
Chris Larsene3845472015-11-18 12:27:15 -0800542}
543
544void MipsAssembler::ClzR6(Register rd, Register rs) {
545 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700546 DsFsmInstrRrr(EmitR(0, rs, static_cast<Register>(0), rd, 0x01, 0x10), rd, rs, rs);
Chris Larsene3845472015-11-18 12:27:15 -0800547}
548
549void MipsAssembler::ClzR2(Register rd, Register rs) {
550 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700551 DsFsmInstrRrr(EmitR(0x1C, rs, rd, rd, 0, 0x20), rd, rs, rs);
Chris Larsene3845472015-11-18 12:27:15 -0800552}
553
554void MipsAssembler::CloR6(Register rd, Register rs) {
555 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700556 DsFsmInstrRrr(EmitR(0, rs, static_cast<Register>(0), rd, 0x01, 0x11), rd, rs, rs);
Chris Larsene3845472015-11-18 12:27:15 -0800557}
558
559void MipsAssembler::CloR2(Register rd, Register rs) {
560 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700561 DsFsmInstrRrr(EmitR(0x1C, rs, rd, rd, 0, 0x21), rd, rs, rs);
Chris Larsene3845472015-11-18 12:27:15 -0800562}
563
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200564void MipsAssembler::Seb(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700565 DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x10, 0x20), rd, rt, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700566}
567
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200568void MipsAssembler::Seh(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700569 DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x18, 0x20), rd, rt, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700570}
571
Chris Larsen3f8bf652015-10-28 10:08:56 -0700572void MipsAssembler::Wsbh(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700573 DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 2, 0x20), rd, rt, rt);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700574}
575
Chris Larsen70014c82015-11-18 12:26:08 -0800576void MipsAssembler::Bitswap(Register rd, Register rt) {
577 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700578 DsFsmInstrRrr(EmitR(0x1f, static_cast<Register>(0), rt, rd, 0x0, 0x20), rd, rt, rt);
Chris Larsen70014c82015-11-18 12:26:08 -0800579}
580
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200581void MipsAssembler::Sll(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700582 CHECK(IsUint<5>(shamt)) << shamt;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700583 DsFsmInstrRrr(EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x00), rd, rt, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700584}
585
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200586void MipsAssembler::Srl(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700587 CHECK(IsUint<5>(shamt)) << shamt;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700588 DsFsmInstrRrr(EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x02), rd, rt, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200589}
590
Chris Larsen3f8bf652015-10-28 10:08:56 -0700591void MipsAssembler::Rotr(Register rd, Register rt, int shamt) {
592 CHECK(IsUint<5>(shamt)) << shamt;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700593 DsFsmInstrRrr(EmitR(0, static_cast<Register>(1), rt, rd, shamt, 0x02), rd, rt, rt);
Chris Larsen3f8bf652015-10-28 10:08:56 -0700594}
595
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200596void MipsAssembler::Sra(Register rd, Register rt, int shamt) {
Chris Larsen3f8bf652015-10-28 10:08:56 -0700597 CHECK(IsUint<5>(shamt)) << shamt;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700598 DsFsmInstrRrr(EmitR(0, static_cast<Register>(0), rt, rd, shamt, 0x03), rd, rt, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200599}
600
601void MipsAssembler::Sllv(Register rd, Register rt, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700602 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x04), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700603}
604
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200605void MipsAssembler::Srlv(Register rd, Register rt, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700606 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x06), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700607}
608
Chris Larsene16ce5a2015-11-18 12:30:20 -0800609void MipsAssembler::Rotrv(Register rd, Register rt, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700610 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 1, 0x06), rd, rs, rt);
Chris Larsene16ce5a2015-11-18 12:30:20 -0800611}
612
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200613void MipsAssembler::Srav(Register rd, Register rt, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700614 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x07), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700615}
616
Alexey Frunze5c7aed32015-11-25 19:41:54 -0800617void MipsAssembler::Ext(Register rd, Register rt, int pos, int size) {
618 CHECK(IsUint<5>(pos)) << pos;
619 CHECK(0 < size && size <= 32) << size;
620 CHECK(0 < pos + size && pos + size <= 32) << pos << " + " << size;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700621 DsFsmInstrRrr(EmitR(0x1f, rt, rd, static_cast<Register>(size - 1), pos, 0x00), rd, rt, rt);
Alexey Frunze5c7aed32015-11-25 19:41:54 -0800622}
623
624void MipsAssembler::Ins(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>(pos + size - 1), pos, 0x04), rd, rd, rt);
Alexey Frunze5c7aed32015-11-25 19:41:54 -0800629}
630
jeffhao7fbee072012-08-24 17:56:54 -0700631void MipsAssembler::Lb(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700632 DsFsmInstrRrr(EmitI(0x20, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700633}
634
635void MipsAssembler::Lh(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700636 DsFsmInstrRrr(EmitI(0x21, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700637}
638
639void MipsAssembler::Lw(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700640 DsFsmInstrRrr(EmitI(0x23, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700641}
642
Chris Larsen3acee732015-11-18 13:31:08 -0800643void MipsAssembler::Lwl(Register rt, Register rs, uint16_t imm16) {
644 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700645 DsFsmInstrRrr(EmitI(0x22, rs, rt, imm16), rt, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800646}
647
648void MipsAssembler::Lwr(Register rt, Register rs, uint16_t imm16) {
649 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700650 DsFsmInstrRrr(EmitI(0x26, rs, rt, imm16), rt, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800651}
652
jeffhao7fbee072012-08-24 17:56:54 -0700653void MipsAssembler::Lbu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700654 DsFsmInstrRrr(EmitI(0x24, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700655}
656
657void MipsAssembler::Lhu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700658 DsFsmInstrRrr(EmitI(0x25, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700659}
660
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700661void MipsAssembler::Lwpc(Register rs, uint32_t imm19) {
662 CHECK(IsR6());
663 CHECK(IsUint<19>(imm19)) << imm19;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700664 DsFsmInstrNop(EmitI21(0x3B, rs, (0x01 << 19) | imm19));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700665}
666
jeffhao7fbee072012-08-24 17:56:54 -0700667void MipsAssembler::Lui(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700668 DsFsmInstrRrr(EmitI(0xf, static_cast<Register>(0), rt, imm16), rt, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700669}
670
Alexey Frunzecad3a4c2016-06-07 23:40:37 -0700671void MipsAssembler::Aui(Register rt, Register rs, uint16_t imm16) {
672 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700673 DsFsmInstrRrr(EmitI(0xf, rs, rt, imm16), rt, rt, rs);
Alexey Frunzecad3a4c2016-06-07 23:40:37 -0700674}
675
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200676void MipsAssembler::Sync(uint32_t stype) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700677 DsFsmInstrNop(EmitR(0, ZERO, ZERO, ZERO, stype & 0x1f, 0xf));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200678}
679
jeffhao7fbee072012-08-24 17:56:54 -0700680void MipsAssembler::Mfhi(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200681 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700682 DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x10), rd, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700683}
684
685void MipsAssembler::Mflo(Register rd) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200686 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700687 DsFsmInstrRrr(EmitR(0, ZERO, ZERO, rd, 0, 0x12), rd, ZERO, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -0700688}
689
690void MipsAssembler::Sb(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700691 DsFsmInstrRrr(EmitI(0x28, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700692}
693
694void MipsAssembler::Sh(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700695 DsFsmInstrRrr(EmitI(0x29, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700696}
697
698void MipsAssembler::Sw(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700699 DsFsmInstrRrr(EmitI(0x2b, rs, rt, imm16), ZERO, rt, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700700}
701
Chris Larsen3acee732015-11-18 13:31:08 -0800702void MipsAssembler::Swl(Register rt, Register rs, uint16_t imm16) {
703 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700704 DsFsmInstrRrr(EmitI(0x2a, rs, rt, imm16), ZERO, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800705}
706
707void MipsAssembler::Swr(Register rt, Register rs, uint16_t imm16) {
708 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700709 DsFsmInstrRrr(EmitI(0x2e, rs, rt, imm16), ZERO, rt, rs);
Chris Larsen3acee732015-11-18 13:31:08 -0800710}
711
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700712void MipsAssembler::LlR2(Register rt, Register base, int16_t imm16) {
713 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700714 DsFsmInstrRrr(EmitI(0x30, base, rt, imm16), rt, base, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700715}
716
717void MipsAssembler::ScR2(Register rt, Register base, int16_t imm16) {
718 CHECK(!IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700719 DsFsmInstrRrr(EmitI(0x38, base, rt, imm16), rt, rt, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700720}
721
722void MipsAssembler::LlR6(Register rt, Register base, int16_t imm9) {
723 CHECK(IsR6());
724 CHECK(IsInt<9>(imm9));
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700725 DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x36), rt, base, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700726}
727
728void MipsAssembler::ScR6(Register rt, Register base, int16_t imm9) {
729 CHECK(IsR6());
730 CHECK(IsInt<9>(imm9));
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700731 DsFsmInstrRrr(EmitI(0x1f, base, rt, ((imm9 & 0x1ff) << 7) | 0x26), rt, rt, base);
Alexey Frunze51aff3a2016-03-17 17:21:45 -0700732}
733
jeffhao7fbee072012-08-24 17:56:54 -0700734void MipsAssembler::Slt(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700735 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2a), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700736}
737
738void MipsAssembler::Sltu(Register rd, Register rs, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700739 DsFsmInstrRrr(EmitR(0, rs, rt, rd, 0, 0x2b), rd, rs, rt);
jeffhao7fbee072012-08-24 17:56:54 -0700740}
741
742void MipsAssembler::Slti(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700743 DsFsmInstrRrr(EmitI(0xa, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700744}
745
746void MipsAssembler::Sltiu(Register rt, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700747 DsFsmInstrRrr(EmitI(0xb, rs, rt, imm16), rt, rs, rs);
jeffhao7fbee072012-08-24 17:56:54 -0700748}
749
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200750void MipsAssembler::B(uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700751 DsFsmInstrNop(EmitI(0x4, static_cast<Register>(0), static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200752}
753
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700754void MipsAssembler::Bal(uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700755 DsFsmInstrNop(EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x11), imm16));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700756}
757
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200758void MipsAssembler::Beq(Register rs, Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700759 DsFsmInstrNop(EmitI(0x4, rs, rt, imm16));
jeffhao7fbee072012-08-24 17:56:54 -0700760}
761
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200762void MipsAssembler::Bne(Register rs, Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700763 DsFsmInstrNop(EmitI(0x5, rs, rt, imm16));
jeffhao7fbee072012-08-24 17:56:54 -0700764}
765
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200766void MipsAssembler::Beqz(Register rt, uint16_t imm16) {
767 Beq(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700768}
769
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200770void MipsAssembler::Bnez(Register rt, uint16_t imm16) {
771 Bne(ZERO, rt, imm16);
jeffhao7fbee072012-08-24 17:56:54 -0700772}
773
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200774void MipsAssembler::Bltz(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700775 DsFsmInstrNop(EmitI(0x1, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200776}
777
778void MipsAssembler::Bgez(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700779 DsFsmInstrNop(EmitI(0x1, rt, static_cast<Register>(0x1), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200780}
781
782void MipsAssembler::Blez(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700783 DsFsmInstrNop(EmitI(0x6, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200784}
785
786void MipsAssembler::Bgtz(Register rt, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700787 DsFsmInstrNop(EmitI(0x7, rt, static_cast<Register>(0), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200788}
789
Chris Larsenb74353a2015-11-20 09:07:09 -0800790void MipsAssembler::Bc1f(uint16_t imm16) {
791 Bc1f(0, imm16);
792}
793
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800794void MipsAssembler::Bc1f(int cc, uint16_t imm16) {
795 CHECK(!IsR6());
796 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700797 DsFsmInstrNop(EmitI(0x11, static_cast<Register>(0x8), static_cast<Register>(cc << 2), imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800798}
799
Chris Larsenb74353a2015-11-20 09:07:09 -0800800void MipsAssembler::Bc1t(uint16_t imm16) {
801 Bc1t(0, imm16);
802}
803
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800804void MipsAssembler::Bc1t(int cc, uint16_t imm16) {
805 CHECK(!IsR6());
806 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700807 DsFsmInstrNop(EmitI(0x11,
808 static_cast<Register>(0x8),
809 static_cast<Register>((cc << 2) | 1),
810 imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800811}
812
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200813void MipsAssembler::J(uint32_t addr26) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700814 DsFsmInstrNop(EmitI26(0x2, addr26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200815}
816
817void MipsAssembler::Jal(uint32_t addr26) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700818 DsFsmInstrNop(EmitI26(0x3, addr26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200819}
820
821void MipsAssembler::Jalr(Register rd, Register rs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700822 uint32_t last_instruction = delay_slot_.instruction_;
823 bool exchange = (last_instruction != 0 &&
824 (delay_slot_.gpr_outs_mask_ & (1u << rs)) == 0 &&
825 ((delay_slot_.gpr_ins_mask_ | delay_slot_.gpr_outs_mask_) & (1u << rd)) == 0);
826 if (exchange) {
827 // The last instruction cannot be used in a different delay slot,
828 // do not commit the label before it (if any).
829 DsFsmDropLabel();
830 }
831 DsFsmInstrNop(EmitR(0, rs, static_cast<Register>(0), rd, 0, 0x09));
832 if (exchange) {
833 // Exchange the last two instructions in the assembler buffer.
834 size_t size = buffer_.Size();
835 CHECK_GE(size, 2 * sizeof(uint32_t));
836 size_t pos1 = size - 2 * sizeof(uint32_t);
837 size_t pos2 = size - sizeof(uint32_t);
838 uint32_t instr1 = buffer_.Load<uint32_t>(pos1);
839 uint32_t instr2 = buffer_.Load<uint32_t>(pos2);
840 CHECK_EQ(instr1, last_instruction);
841 buffer_.Store<uint32_t>(pos1, instr2);
842 buffer_.Store<uint32_t>(pos2, instr1);
843 } else if (reordering_) {
844 Nop();
845 }
jeffhao7fbee072012-08-24 17:56:54 -0700846}
847
848void MipsAssembler::Jalr(Register rs) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200849 Jalr(RA, rs);
850}
851
852void MipsAssembler::Jr(Register rs) {
853 Jalr(ZERO, rs);
854}
855
856void MipsAssembler::Nal() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700857 DsFsmInstrNop(EmitI(0x1, static_cast<Register>(0), static_cast<Register>(0x10), 0));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200858}
859
860void MipsAssembler::Auipc(Register rs, uint16_t imm16) {
861 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700862 DsFsmInstrNop(EmitI(0x3B, rs, static_cast<Register>(0x1E), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200863}
864
865void MipsAssembler::Addiupc(Register rs, uint32_t imm19) {
866 CHECK(IsR6());
867 CHECK(IsUint<19>(imm19)) << imm19;
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700868 DsFsmInstrNop(EmitI21(0x3B, rs, imm19));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200869}
870
871void MipsAssembler::Bc(uint32_t imm26) {
872 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700873 DsFsmInstrNop(EmitI26(0x32, imm26));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200874}
875
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700876void MipsAssembler::Balc(uint32_t imm26) {
877 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700878 DsFsmInstrNop(EmitI26(0x3A, imm26));
Alexey Frunzee3fb2452016-05-10 16:08:05 -0700879}
880
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200881void MipsAssembler::Jic(Register rt, uint16_t imm16) {
882 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700883 DsFsmInstrNop(EmitI(0x36, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200884}
885
886void MipsAssembler::Jialc(Register rt, uint16_t imm16) {
887 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700888 DsFsmInstrNop(EmitI(0x3E, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200889}
890
891void MipsAssembler::Bltc(Register rs, Register rt, uint16_t imm16) {
892 CHECK(IsR6());
893 CHECK_NE(rs, ZERO);
894 CHECK_NE(rt, ZERO);
895 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700896 DsFsmInstrNop(EmitI(0x17, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200897}
898
899void MipsAssembler::Bltzc(Register rt, uint16_t imm16) {
900 CHECK(IsR6());
901 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700902 DsFsmInstrNop(EmitI(0x17, rt, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200903}
904
905void MipsAssembler::Bgtzc(Register rt, uint16_t imm16) {
906 CHECK(IsR6());
907 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700908 DsFsmInstrNop(EmitI(0x17, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200909}
910
911void MipsAssembler::Bgec(Register rs, Register rt, uint16_t imm16) {
912 CHECK(IsR6());
913 CHECK_NE(rs, ZERO);
914 CHECK_NE(rt, ZERO);
915 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700916 DsFsmInstrNop(EmitI(0x16, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200917}
918
919void MipsAssembler::Bgezc(Register rt, uint16_t imm16) {
920 CHECK(IsR6());
921 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700922 DsFsmInstrNop(EmitI(0x16, rt, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200923}
924
925void MipsAssembler::Blezc(Register rt, uint16_t imm16) {
926 CHECK(IsR6());
927 CHECK_NE(rt, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700928 DsFsmInstrNop(EmitI(0x16, static_cast<Register>(0), rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200929}
930
931void MipsAssembler::Bltuc(Register rs, Register rt, uint16_t imm16) {
932 CHECK(IsR6());
933 CHECK_NE(rs, ZERO);
934 CHECK_NE(rt, ZERO);
935 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700936 DsFsmInstrNop(EmitI(0x7, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200937}
938
939void MipsAssembler::Bgeuc(Register rs, Register rt, uint16_t imm16) {
940 CHECK(IsR6());
941 CHECK_NE(rs, ZERO);
942 CHECK_NE(rt, ZERO);
943 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700944 DsFsmInstrNop(EmitI(0x6, rs, rt, imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200945}
946
947void MipsAssembler::Beqc(Register rs, Register rt, uint16_t imm16) {
948 CHECK(IsR6());
949 CHECK_NE(rs, ZERO);
950 CHECK_NE(rt, ZERO);
951 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700952 DsFsmInstrNop(EmitI(0x8, std::min(rs, rt), std::max(rs, rt), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200953}
954
955void MipsAssembler::Bnec(Register rs, Register rt, uint16_t imm16) {
956 CHECK(IsR6());
957 CHECK_NE(rs, ZERO);
958 CHECK_NE(rt, ZERO);
959 CHECK_NE(rs, rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700960 DsFsmInstrNop(EmitI(0x18, std::min(rs, rt), std::max(rs, rt), imm16));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200961}
962
963void MipsAssembler::Beqzc(Register rs, uint32_t imm21) {
964 CHECK(IsR6());
965 CHECK_NE(rs, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700966 DsFsmInstrNop(EmitI21(0x36, rs, imm21));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200967}
968
969void MipsAssembler::Bnezc(Register rs, uint32_t imm21) {
970 CHECK(IsR6());
971 CHECK_NE(rs, ZERO);
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700972 DsFsmInstrNop(EmitI21(0x3E, rs, imm21));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200973}
974
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800975void MipsAssembler::Bc1eqz(FRegister ft, uint16_t imm16) {
976 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700977 DsFsmInstrNop(EmitFI(0x11, 0x9, ft, imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800978}
979
980void MipsAssembler::Bc1nez(FRegister ft, uint16_t imm16) {
981 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -0700982 DsFsmInstrNop(EmitFI(0x11, 0xD, ft, imm16));
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -0800983}
984
985void MipsAssembler::EmitBcondR2(BranchCondition cond, Register rs, Register rt, uint16_t imm16) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +0200986 switch (cond) {
987 case kCondLTZ:
988 CHECK_EQ(rt, ZERO);
989 Bltz(rs, imm16);
990 break;
991 case kCondGEZ:
992 CHECK_EQ(rt, ZERO);
993 Bgez(rs, imm16);
994 break;
995 case kCondLEZ:
996 CHECK_EQ(rt, ZERO);
997 Blez(rs, imm16);
998 break;
999 case kCondGTZ:
1000 CHECK_EQ(rt, ZERO);
1001 Bgtz(rs, imm16);
1002 break;
1003 case kCondEQ:
1004 Beq(rs, rt, imm16);
1005 break;
1006 case kCondNE:
1007 Bne(rs, rt, imm16);
1008 break;
1009 case kCondEQZ:
1010 CHECK_EQ(rt, ZERO);
1011 Beqz(rs, imm16);
1012 break;
1013 case kCondNEZ:
1014 CHECK_EQ(rt, ZERO);
1015 Bnez(rs, imm16);
1016 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001017 case kCondF:
1018 CHECK_EQ(rt, ZERO);
1019 Bc1f(static_cast<int>(rs), imm16);
1020 break;
1021 case kCondT:
1022 CHECK_EQ(rt, ZERO);
1023 Bc1t(static_cast<int>(rs), imm16);
1024 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001025 case kCondLT:
1026 case kCondGE:
1027 case kCondLE:
1028 case kCondGT:
1029 case kCondLTU:
1030 case kCondGEU:
1031 case kUncond:
1032 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
1033 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
1034 LOG(FATAL) << "Unexpected branch condition " << cond;
1035 UNREACHABLE();
1036 }
1037}
1038
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001039void MipsAssembler::EmitBcondR6(BranchCondition cond, Register rs, Register rt, uint32_t imm16_21) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001040 switch (cond) {
1041 case kCondLT:
1042 Bltc(rs, rt, imm16_21);
1043 break;
1044 case kCondGE:
1045 Bgec(rs, rt, imm16_21);
1046 break;
1047 case kCondLE:
1048 Bgec(rt, rs, imm16_21);
1049 break;
1050 case kCondGT:
1051 Bltc(rt, rs, imm16_21);
1052 break;
1053 case kCondLTZ:
1054 CHECK_EQ(rt, ZERO);
1055 Bltzc(rs, imm16_21);
1056 break;
1057 case kCondGEZ:
1058 CHECK_EQ(rt, ZERO);
1059 Bgezc(rs, imm16_21);
1060 break;
1061 case kCondLEZ:
1062 CHECK_EQ(rt, ZERO);
1063 Blezc(rs, imm16_21);
1064 break;
1065 case kCondGTZ:
1066 CHECK_EQ(rt, ZERO);
1067 Bgtzc(rs, imm16_21);
1068 break;
1069 case kCondEQ:
1070 Beqc(rs, rt, imm16_21);
1071 break;
1072 case kCondNE:
1073 Bnec(rs, rt, imm16_21);
1074 break;
1075 case kCondEQZ:
1076 CHECK_EQ(rt, ZERO);
1077 Beqzc(rs, imm16_21);
1078 break;
1079 case kCondNEZ:
1080 CHECK_EQ(rt, ZERO);
1081 Bnezc(rs, imm16_21);
1082 break;
1083 case kCondLTU:
1084 Bltuc(rs, rt, imm16_21);
1085 break;
1086 case kCondGEU:
1087 Bgeuc(rs, rt, imm16_21);
1088 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001089 case kCondF:
1090 CHECK_EQ(rt, ZERO);
1091 Bc1eqz(static_cast<FRegister>(rs), imm16_21);
1092 break;
1093 case kCondT:
1094 CHECK_EQ(rt, ZERO);
1095 Bc1nez(static_cast<FRegister>(rs), imm16_21);
1096 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001097 case kUncond:
1098 LOG(FATAL) << "Unexpected branch condition " << cond;
1099 UNREACHABLE();
1100 }
jeffhao7fbee072012-08-24 17:56:54 -07001101}
1102
1103void MipsAssembler::AddS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001104 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x0), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001105}
1106
1107void MipsAssembler::SubS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001108 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001109}
1110
1111void MipsAssembler::MulS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001112 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x2), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001113}
1114
1115void MipsAssembler::DivS(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001116 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x3), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001117}
1118
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001119void MipsAssembler::AddD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001120 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x0), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001121}
1122
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001123void MipsAssembler::SubD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001124 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001125}
1126
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001127void MipsAssembler::MulD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001128 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x2), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001129}
1130
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001131void MipsAssembler::DivD(FRegister fd, FRegister fs, FRegister ft) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001132 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x3), fd, fs, ft);
jeffhao7fbee072012-08-24 17:56:54 -07001133}
1134
Chris Larsenb74353a2015-11-20 09:07:09 -08001135void MipsAssembler::SqrtS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001136 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x4), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001137}
1138
1139void MipsAssembler::SqrtD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001140 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x4), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001141}
1142
1143void MipsAssembler::AbsS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001144 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x5), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001145}
1146
1147void MipsAssembler::AbsD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001148 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x5), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001149}
1150
jeffhao7fbee072012-08-24 17:56:54 -07001151void MipsAssembler::MovS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001152 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x6), fd, fs, fs);
jeffhao7fbee072012-08-24 17:56:54 -07001153}
1154
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001155void MipsAssembler::MovD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001156 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x6), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001157}
1158
1159void MipsAssembler::NegS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001160 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x7), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001161}
1162
1163void MipsAssembler::NegD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001164 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x7), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001165}
1166
Chris Larsenb74353a2015-11-20 09:07:09 -08001167void MipsAssembler::CunS(FRegister fs, FRegister ft) {
1168 CunS(0, fs, ft);
1169}
1170
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001171void MipsAssembler::CunS(int cc, FRegister fs, FRegister ft) {
1172 CHECK(!IsR6());
1173 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001174 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x31), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001175}
1176
Chris Larsenb74353a2015-11-20 09:07:09 -08001177void MipsAssembler::CeqS(FRegister fs, FRegister ft) {
1178 CeqS(0, fs, ft);
1179}
1180
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001181void MipsAssembler::CeqS(int cc, FRegister fs, FRegister ft) {
1182 CHECK(!IsR6());
1183 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001184 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x32), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001185}
1186
Chris Larsenb74353a2015-11-20 09:07:09 -08001187void MipsAssembler::CueqS(FRegister fs, FRegister ft) {
1188 CueqS(0, fs, ft);
1189}
1190
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001191void MipsAssembler::CueqS(int cc, FRegister fs, FRegister ft) {
1192 CHECK(!IsR6());
1193 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001194 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x33), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001195}
1196
Chris Larsenb74353a2015-11-20 09:07:09 -08001197void MipsAssembler::ColtS(FRegister fs, FRegister ft) {
1198 ColtS(0, fs, ft);
1199}
1200
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001201void MipsAssembler::ColtS(int cc, FRegister fs, FRegister ft) {
1202 CHECK(!IsR6());
1203 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001204 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x34), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001205}
1206
Chris Larsenb74353a2015-11-20 09:07:09 -08001207void MipsAssembler::CultS(FRegister fs, FRegister ft) {
1208 CultS(0, fs, ft);
1209}
1210
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001211void MipsAssembler::CultS(int cc, FRegister fs, FRegister ft) {
1212 CHECK(!IsR6());
1213 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001214 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x35), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001215}
1216
Chris Larsenb74353a2015-11-20 09:07:09 -08001217void MipsAssembler::ColeS(FRegister fs, FRegister ft) {
1218 ColeS(0, fs, ft);
1219}
1220
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001221void MipsAssembler::ColeS(int cc, FRegister fs, FRegister ft) {
1222 CHECK(!IsR6());
1223 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001224 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x36), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001225}
1226
Chris Larsenb74353a2015-11-20 09:07:09 -08001227void MipsAssembler::CuleS(FRegister fs, FRegister ft) {
1228 CuleS(0, fs, ft);
1229}
1230
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001231void MipsAssembler::CuleS(int cc, FRegister fs, FRegister ft) {
1232 CHECK(!IsR6());
1233 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001234 DsFsmInstrCff(EmitFR(0x11, 0x10, ft, fs, static_cast<FRegister>(cc << 2), 0x37), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001235}
1236
Chris Larsenb74353a2015-11-20 09:07:09 -08001237void MipsAssembler::CunD(FRegister fs, FRegister ft) {
1238 CunD(0, fs, ft);
1239}
1240
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001241void MipsAssembler::CunD(int cc, FRegister fs, FRegister ft) {
1242 CHECK(!IsR6());
1243 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001244 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x31), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001245}
1246
Chris Larsenb74353a2015-11-20 09:07:09 -08001247void MipsAssembler::CeqD(FRegister fs, FRegister ft) {
1248 CeqD(0, fs, ft);
1249}
1250
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001251void MipsAssembler::CeqD(int cc, FRegister fs, FRegister ft) {
1252 CHECK(!IsR6());
1253 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001254 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x32), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001255}
1256
Chris Larsenb74353a2015-11-20 09:07:09 -08001257void MipsAssembler::CueqD(FRegister fs, FRegister ft) {
1258 CueqD(0, fs, ft);
1259}
1260
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001261void MipsAssembler::CueqD(int cc, FRegister fs, FRegister ft) {
1262 CHECK(!IsR6());
1263 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001264 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x33), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001265}
1266
Chris Larsenb74353a2015-11-20 09:07:09 -08001267void MipsAssembler::ColtD(FRegister fs, FRegister ft) {
1268 ColtD(0, fs, ft);
1269}
1270
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001271void MipsAssembler::ColtD(int cc, FRegister fs, FRegister ft) {
1272 CHECK(!IsR6());
1273 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001274 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x34), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001275}
1276
Chris Larsenb74353a2015-11-20 09:07:09 -08001277void MipsAssembler::CultD(FRegister fs, FRegister ft) {
1278 CultD(0, fs, ft);
1279}
1280
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001281void MipsAssembler::CultD(int cc, FRegister fs, FRegister ft) {
1282 CHECK(!IsR6());
1283 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001284 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x35), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001285}
1286
Chris Larsenb74353a2015-11-20 09:07:09 -08001287void MipsAssembler::ColeD(FRegister fs, FRegister ft) {
1288 ColeD(0, fs, ft);
1289}
1290
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001291void MipsAssembler::ColeD(int cc, FRegister fs, FRegister ft) {
1292 CHECK(!IsR6());
1293 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001294 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x36), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001295}
1296
Chris Larsenb74353a2015-11-20 09:07:09 -08001297void MipsAssembler::CuleD(FRegister fs, FRegister ft) {
1298 CuleD(0, fs, ft);
1299}
1300
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001301void MipsAssembler::CuleD(int cc, FRegister fs, FRegister ft) {
1302 CHECK(!IsR6());
1303 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001304 DsFsmInstrCff(EmitFR(0x11, 0x11, ft, fs, static_cast<FRegister>(cc << 2), 0x37), cc, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001305}
1306
1307void MipsAssembler::CmpUnS(FRegister fd, FRegister fs, FRegister ft) {
1308 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001309 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x01), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001310}
1311
1312void MipsAssembler::CmpEqS(FRegister fd, FRegister fs, FRegister ft) {
1313 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001314 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x02), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001315}
1316
1317void MipsAssembler::CmpUeqS(FRegister fd, FRegister fs, FRegister ft) {
1318 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001319 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x03), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001320}
1321
1322void MipsAssembler::CmpLtS(FRegister fd, FRegister fs, FRegister ft) {
1323 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001324 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x04), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001325}
1326
1327void MipsAssembler::CmpUltS(FRegister fd, FRegister fs, FRegister ft) {
1328 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001329 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x05), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001330}
1331
1332void MipsAssembler::CmpLeS(FRegister fd, FRegister fs, FRegister ft) {
1333 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001334 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x06), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001335}
1336
1337void MipsAssembler::CmpUleS(FRegister fd, FRegister fs, FRegister ft) {
1338 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001339 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x07), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001340}
1341
1342void MipsAssembler::CmpOrS(FRegister fd, FRegister fs, FRegister ft) {
1343 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001344 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x11), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001345}
1346
1347void MipsAssembler::CmpUneS(FRegister fd, FRegister fs, FRegister ft) {
1348 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001349 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x12), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001350}
1351
1352void MipsAssembler::CmpNeS(FRegister fd, FRegister fs, FRegister ft) {
1353 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001354 DsFsmInstrFff(EmitFR(0x11, 0x14, ft, fs, fd, 0x13), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001355}
1356
1357void MipsAssembler::CmpUnD(FRegister fd, FRegister fs, FRegister ft) {
1358 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001359 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x01), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001360}
1361
1362void MipsAssembler::CmpEqD(FRegister fd, FRegister fs, FRegister ft) {
1363 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001364 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x02), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001365}
1366
1367void MipsAssembler::CmpUeqD(FRegister fd, FRegister fs, FRegister ft) {
1368 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001369 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x03), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001370}
1371
1372void MipsAssembler::CmpLtD(FRegister fd, FRegister fs, FRegister ft) {
1373 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001374 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x04), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001375}
1376
1377void MipsAssembler::CmpUltD(FRegister fd, FRegister fs, FRegister ft) {
1378 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001379 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x05), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001380}
1381
1382void MipsAssembler::CmpLeD(FRegister fd, FRegister fs, FRegister ft) {
1383 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001384 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x06), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001385}
1386
1387void MipsAssembler::CmpUleD(FRegister fd, FRegister fs, FRegister ft) {
1388 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001389 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x07), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001390}
1391
1392void MipsAssembler::CmpOrD(FRegister fd, FRegister fs, FRegister ft) {
1393 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001394 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x11), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001395}
1396
1397void MipsAssembler::CmpUneD(FRegister fd, FRegister fs, FRegister ft) {
1398 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001399 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x12), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001400}
1401
1402void MipsAssembler::CmpNeD(FRegister fd, FRegister fs, FRegister ft) {
1403 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001404 DsFsmInstrFff(EmitFR(0x11, 0x15, ft, fs, fd, 0x13), fd, fs, ft);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001405}
1406
1407void MipsAssembler::Movf(Register rd, Register rs, int cc) {
1408 CHECK(!IsR6());
1409 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001410 DsFsmInstrRrrc(EmitR(0, rs, static_cast<Register>(cc << 2), rd, 0, 0x01), rd, rs, cc);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001411}
1412
1413void MipsAssembler::Movt(Register rd, Register rs, int cc) {
1414 CHECK(!IsR6());
1415 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001416 DsFsmInstrRrrc(EmitR(0, rs, static_cast<Register>((cc << 2) | 1), rd, 0, 0x01), rd, rs, cc);
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001417}
1418
Chris Larsenb74353a2015-11-20 09:07:09 -08001419void MipsAssembler::MovfS(FRegister fd, FRegister fs, int cc) {
1420 CHECK(!IsR6());
1421 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001422 DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast<FRegister>(cc << 2), fs, fd, 0x11), fd, fs, cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001423}
1424
1425void MipsAssembler::MovfD(FRegister fd, FRegister fs, int cc) {
1426 CHECK(!IsR6());
1427 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001428 DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast<FRegister>(cc << 2), fs, fd, 0x11), fd, fs, cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001429}
1430
1431void MipsAssembler::MovtS(FRegister fd, FRegister fs, int cc) {
1432 CHECK(!IsR6());
1433 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001434 DsFsmInstrFffc(EmitFR(0x11, 0x10, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11),
1435 fd,
1436 fs,
1437 cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001438}
1439
1440void MipsAssembler::MovtD(FRegister fd, FRegister fs, int cc) {
1441 CHECK(!IsR6());
1442 CHECK(IsUint<3>(cc)) << cc;
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001443 DsFsmInstrFffc(EmitFR(0x11, 0x11, static_cast<FRegister>((cc << 2) | 1), fs, fd, 0x11),
1444 fd,
1445 fs,
1446 cc);
Chris Larsenb74353a2015-11-20 09:07:09 -08001447}
1448
1449void MipsAssembler::SelS(FRegister fd, FRegister fs, FRegister ft) {
1450 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001451 DsFsmInstrFfff(EmitFR(0x11, 0x10, ft, fs, fd, 0x10), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001452}
1453
1454void MipsAssembler::SelD(FRegister fd, FRegister fs, FRegister ft) {
1455 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001456 DsFsmInstrFfff(EmitFR(0x11, 0x11, ft, fs, fd, 0x10), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001457}
1458
1459void MipsAssembler::ClassS(FRegister fd, FRegister fs) {
1460 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001461 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x1b), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001462}
1463
1464void MipsAssembler::ClassD(FRegister fd, FRegister fs) {
1465 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001466 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x1b), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001467}
1468
1469void MipsAssembler::MinS(FRegister fd, FRegister fs, FRegister ft) {
1470 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001471 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1c), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001472}
1473
1474void MipsAssembler::MinD(FRegister fd, FRegister fs, FRegister ft) {
1475 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001476 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1c), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001477}
1478
1479void MipsAssembler::MaxS(FRegister fd, FRegister fs, FRegister ft) {
1480 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001481 DsFsmInstrFff(EmitFR(0x11, 0x10, ft, fs, fd, 0x1e), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001482}
1483
1484void MipsAssembler::MaxD(FRegister fd, FRegister fs, FRegister ft) {
1485 CHECK(IsR6());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001486 DsFsmInstrFff(EmitFR(0x11, 0x11, ft, fs, fd, 0x1e), fd, fs, ft);
Chris Larsenb74353a2015-11-20 09:07:09 -08001487}
1488
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001489void MipsAssembler::TruncLS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001490 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x09), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001491}
1492
1493void MipsAssembler::TruncLD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001494 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x09), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001495}
1496
1497void MipsAssembler::TruncWS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001498 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x0D), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001499}
1500
1501void MipsAssembler::TruncWD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001502 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x0D), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001503}
1504
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001505void MipsAssembler::Cvtsw(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001506 DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001507}
1508
1509void MipsAssembler::Cvtdw(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001510 DsFsmInstrFff(EmitFR(0x11, 0x14, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001511}
1512
1513void MipsAssembler::Cvtsd(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001514 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001515}
1516
1517void MipsAssembler::Cvtds(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001518 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
jeffhao7fbee072012-08-24 17:56:54 -07001519}
1520
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001521void MipsAssembler::Cvtsl(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001522 DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x20), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001523}
1524
1525void MipsAssembler::Cvtdl(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001526 DsFsmInstrFff(EmitFR(0x11, 0x15, static_cast<FRegister>(0), fs, fd, 0x21), fd, fs, fs);
Alexey Frunzebaf60b72015-12-22 15:15:03 -08001527}
1528
Chris Larsenb74353a2015-11-20 09:07:09 -08001529void MipsAssembler::FloorWS(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001530 DsFsmInstrFff(EmitFR(0x11, 0x10, static_cast<FRegister>(0), fs, fd, 0xf), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001531}
1532
1533void MipsAssembler::FloorWD(FRegister fd, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001534 DsFsmInstrFff(EmitFR(0x11, 0x11, static_cast<FRegister>(0), fs, fd, 0xf), fd, fs, fs);
Chris Larsenb74353a2015-11-20 09:07:09 -08001535}
1536
jeffhao7fbee072012-08-24 17:56:54 -07001537void MipsAssembler::Mfc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001538 DsFsmInstrRf(EmitFR(0x11, 0x00, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1539 rt,
1540 fs);
jeffhao7fbee072012-08-24 17:56:54 -07001541}
1542
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001543void MipsAssembler::Mtc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001544 DsFsmInstrFr(EmitFR(0x11, 0x04, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1545 fs,
1546 rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001547}
1548
1549void MipsAssembler::Mfhc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001550 DsFsmInstrRf(EmitFR(0x11, 0x03, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1551 rt,
1552 fs);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001553}
1554
1555void MipsAssembler::Mthc1(Register rt, FRegister fs) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001556 DsFsmInstrFr(EmitFR(0x11, 0x07, static_cast<FRegister>(rt), fs, static_cast<FRegister>(0), 0x0),
1557 fs,
1558 rt);
jeffhao7fbee072012-08-24 17:56:54 -07001559}
1560
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001561void MipsAssembler::MoveFromFpuHigh(Register rt, FRegister fs) {
1562 if (Is32BitFPU()) {
1563 CHECK_EQ(fs % 2, 0) << fs;
1564 Mfc1(rt, static_cast<FRegister>(fs + 1));
1565 } else {
1566 Mfhc1(rt, fs);
1567 }
1568}
1569
1570void MipsAssembler::MoveToFpuHigh(Register rt, FRegister fs) {
1571 if (Is32BitFPU()) {
1572 CHECK_EQ(fs % 2, 0) << fs;
1573 Mtc1(rt, static_cast<FRegister>(fs + 1));
1574 } else {
1575 Mthc1(rt, fs);
1576 }
1577}
1578
jeffhao7fbee072012-08-24 17:56:54 -07001579void MipsAssembler::Lwc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001580 DsFsmInstrFr(EmitI(0x31, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001581}
1582
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001583void MipsAssembler::Ldc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001584 DsFsmInstrFr(EmitI(0x35, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001585}
1586
1587void MipsAssembler::Swc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001588 DsFsmInstrFR(EmitI(0x39, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001589}
1590
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001591void MipsAssembler::Sdc1(FRegister ft, Register rs, uint16_t imm16) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001592 DsFsmInstrFR(EmitI(0x3d, rs, static_cast<Register>(ft), imm16), ft, rs);
jeffhao7fbee072012-08-24 17:56:54 -07001593}
1594
1595void MipsAssembler::Break() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001596 DsFsmInstrNop(EmitR(0, ZERO, ZERO, ZERO, 0, 0xD));
jeffhao7fbee072012-08-24 17:56:54 -07001597}
1598
jeffhao07030602012-09-26 14:33:14 -07001599void MipsAssembler::Nop() {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001600 DsFsmInstrNop(EmitR(0x0, ZERO, ZERO, ZERO, 0, 0x0));
1601}
1602
1603void MipsAssembler::NopIfNoReordering() {
1604 if (!reordering_) {
1605 Nop();
1606 }
jeffhao07030602012-09-26 14:33:14 -07001607}
1608
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001609void MipsAssembler::Move(Register rd, Register rs) {
1610 Or(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001611}
1612
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001613void MipsAssembler::Clear(Register rd) {
1614 Move(rd, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001615}
1616
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001617void MipsAssembler::Not(Register rd, Register rs) {
1618 Nor(rd, rs, ZERO);
jeffhao7fbee072012-08-24 17:56:54 -07001619}
1620
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001621void MipsAssembler::Push(Register rs) {
1622 IncreaseFrameSize(kMipsWordSize);
1623 Sw(rs, SP, 0);
jeffhao7fbee072012-08-24 17:56:54 -07001624}
1625
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001626void MipsAssembler::Pop(Register rd) {
1627 Lw(rd, SP, 0);
1628 DecreaseFrameSize(kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07001629}
1630
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001631void MipsAssembler::PopAndReturn(Register rd, Register rt) {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001632 bool reordering = SetReorder(false);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001633 Lw(rd, SP, 0);
1634 Jr(rt);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001635 DecreaseFrameSize(kMipsWordSize); // Single instruction in delay slot.
1636 SetReorder(reordering);
jeffhao7fbee072012-08-24 17:56:54 -07001637}
1638
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001639void MipsAssembler::LoadConst32(Register rd, int32_t value) {
1640 if (IsUint<16>(value)) {
1641 // Use OR with (unsigned) immediate to encode 16b unsigned int.
1642 Ori(rd, ZERO, value);
1643 } else if (IsInt<16>(value)) {
1644 // Use ADD with (signed) immediate to encode 16b signed int.
1645 Addiu(rd, ZERO, value);
jeffhao7fbee072012-08-24 17:56:54 -07001646 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001647 Lui(rd, High16Bits(value));
1648 if (value & 0xFFFF)
1649 Ori(rd, rd, Low16Bits(value));
1650 }
1651}
1652
1653void MipsAssembler::LoadConst64(Register reg_hi, Register reg_lo, int64_t value) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001654 uint32_t low = Low32Bits(value);
1655 uint32_t high = High32Bits(value);
1656 LoadConst32(reg_lo, low);
1657 if (high != low) {
1658 LoadConst32(reg_hi, high);
1659 } else {
1660 Move(reg_hi, reg_lo);
1661 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001662}
1663
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001664void MipsAssembler::LoadSConst32(FRegister r, int32_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001665 if (value == 0) {
1666 temp = ZERO;
1667 } else {
1668 LoadConst32(temp, value);
1669 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001670 Mtc1(temp, r);
1671}
1672
1673void MipsAssembler::LoadDConst64(FRegister rd, int64_t value, Register temp) {
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001674 uint32_t low = Low32Bits(value);
1675 uint32_t high = High32Bits(value);
1676 if (low == 0) {
1677 Mtc1(ZERO, rd);
1678 } else {
1679 LoadConst32(temp, low);
1680 Mtc1(temp, rd);
1681 }
1682 if (high == 0) {
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001683 MoveToFpuHigh(ZERO, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001684 } else {
1685 LoadConst32(temp, high);
Alexey Frunzebb9863a2016-01-11 15:51:16 -08001686 MoveToFpuHigh(temp, rd);
Alexey Frunze5c7aed32015-11-25 19:41:54 -08001687 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001688}
1689
1690void MipsAssembler::Addiu32(Register rt, Register rs, int32_t value, Register temp) {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001691 CHECK_NE(rs, temp); // Must not overwrite the register `rs` while loading `value`.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001692 if (IsInt<16>(value)) {
1693 Addiu(rt, rs, value);
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001694 } else if (IsR6()) {
1695 int16_t high = High16Bits(value);
1696 int16_t low = Low16Bits(value);
1697 high += (low < 0) ? 1 : 0; // Account for sign extension in addiu.
1698 if (low != 0) {
1699 Aui(temp, rs, high);
1700 Addiu(rt, temp, low);
1701 } else {
1702 Aui(rt, rs, high);
1703 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001704 } else {
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07001705 // Do not load the whole 32-bit `value` if it can be represented as
1706 // a sum of two 16-bit signed values. This can save an instruction.
1707 constexpr int32_t kMinValueForSimpleAdjustment = std::numeric_limits<int16_t>::min() * 2;
1708 constexpr int32_t kMaxValueForSimpleAdjustment = std::numeric_limits<int16_t>::max() * 2;
1709 if (0 <= value && value <= kMaxValueForSimpleAdjustment) {
1710 Addiu(temp, rs, kMaxValueForSimpleAdjustment / 2);
1711 Addiu(rt, temp, value - kMaxValueForSimpleAdjustment / 2);
1712 } else if (kMinValueForSimpleAdjustment <= value && value < 0) {
1713 Addiu(temp, rs, kMinValueForSimpleAdjustment / 2);
1714 Addiu(rt, temp, value - kMinValueForSimpleAdjustment / 2);
1715 } else {
1716 // Now that all shorter options have been exhausted, load the full 32-bit value.
1717 LoadConst32(temp, value);
1718 Addu(rt, rs, temp);
1719 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001720 }
1721}
1722
1723void MipsAssembler::Branch::InitShortOrLong(MipsAssembler::Branch::OffsetBits offset_size,
1724 MipsAssembler::Branch::Type short_type,
1725 MipsAssembler::Branch::Type long_type) {
1726 type_ = (offset_size <= branch_info_[short_type].offset_size) ? short_type : long_type;
1727}
1728
Alexey Frunze96b66822016-09-10 02:32:44 -07001729void MipsAssembler::Branch::InitializeType(Type initial_type, bool is_r6) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001730 OffsetBits offset_size = GetOffsetSizeNeeded(location_, target_);
1731 if (is_r6) {
1732 // R6
Alexey Frunze96b66822016-09-10 02:32:44 -07001733 switch (initial_type) {
1734 case kLabel:
1735 CHECK(!IsResolved());
1736 type_ = kR6Label;
1737 break;
1738 case kLiteral:
1739 CHECK(!IsResolved());
1740 type_ = kR6Literal;
1741 break;
1742 case kCall:
1743 InitShortOrLong(offset_size, kR6Call, kR6LongCall);
1744 break;
1745 case kCondBranch:
1746 switch (condition_) {
1747 case kUncond:
1748 InitShortOrLong(offset_size, kR6UncondBranch, kR6LongUncondBranch);
1749 break;
1750 case kCondEQZ:
1751 case kCondNEZ:
1752 // Special case for beqzc/bnezc with longer offset than in other b<cond>c instructions.
1753 type_ = (offset_size <= kOffset23) ? kR6CondBranch : kR6LongCondBranch;
1754 break;
1755 default:
1756 InitShortOrLong(offset_size, kR6CondBranch, kR6LongCondBranch);
1757 break;
1758 }
1759 break;
1760 default:
1761 LOG(FATAL) << "Unexpected branch type " << initial_type;
1762 UNREACHABLE();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001763 }
1764 } else {
1765 // R2
Alexey Frunze96b66822016-09-10 02:32:44 -07001766 switch (initial_type) {
1767 case kLabel:
1768 CHECK(!IsResolved());
1769 type_ = kLabel;
1770 break;
1771 case kLiteral:
1772 CHECK(!IsResolved());
1773 type_ = kLiteral;
1774 break;
1775 case kCall:
1776 InitShortOrLong(offset_size, kCall, kLongCall);
1777 break;
1778 case kCondBranch:
1779 switch (condition_) {
1780 case kUncond:
1781 InitShortOrLong(offset_size, kUncondBranch, kLongUncondBranch);
1782 break;
1783 default:
1784 InitShortOrLong(offset_size, kCondBranch, kLongCondBranch);
1785 break;
1786 }
1787 break;
1788 default:
1789 LOG(FATAL) << "Unexpected branch type " << initial_type;
1790 UNREACHABLE();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001791 }
1792 }
1793 old_type_ = type_;
1794}
1795
1796bool MipsAssembler::Branch::IsNop(BranchCondition condition, Register lhs, Register rhs) {
1797 switch (condition) {
1798 case kCondLT:
1799 case kCondGT:
1800 case kCondNE:
1801 case kCondLTU:
1802 return lhs == rhs;
1803 default:
1804 return false;
1805 }
1806}
1807
1808bool MipsAssembler::Branch::IsUncond(BranchCondition condition, Register lhs, Register rhs) {
1809 switch (condition) {
1810 case kUncond:
1811 return true;
1812 case kCondGE:
1813 case kCondLE:
1814 case kCondEQ:
1815 case kCondGEU:
1816 return lhs == rhs;
1817 default:
1818 return false;
1819 }
1820}
1821
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001822MipsAssembler::Branch::Branch(bool is_r6, uint32_t location, uint32_t target, bool is_call)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001823 : old_location_(location),
1824 location_(location),
1825 target_(target),
1826 lhs_reg_(0),
1827 rhs_reg_(0),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001828 condition_(kUncond),
1829 delayed_instruction_(kUnfilledDelaySlot) {
Alexey Frunze96b66822016-09-10 02:32:44 -07001830 InitializeType((is_call ? kCall : kCondBranch), is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001831}
1832
1833MipsAssembler::Branch::Branch(bool is_r6,
1834 uint32_t location,
1835 uint32_t target,
1836 MipsAssembler::BranchCondition condition,
1837 Register lhs_reg,
1838 Register rhs_reg)
1839 : old_location_(location),
1840 location_(location),
1841 target_(target),
1842 lhs_reg_(lhs_reg),
1843 rhs_reg_(rhs_reg),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001844 condition_(condition),
1845 delayed_instruction_(kUnfilledDelaySlot) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001846 CHECK_NE(condition, kUncond);
1847 switch (condition) {
1848 case kCondLT:
1849 case kCondGE:
1850 case kCondLE:
1851 case kCondGT:
1852 case kCondLTU:
1853 case kCondGEU:
1854 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
1855 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
1856 // We leave this up to the caller.
1857 CHECK(is_r6);
1858 FALLTHROUGH_INTENDED;
1859 case kCondEQ:
1860 case kCondNE:
1861 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1862 // To compare with 0, use dedicated kCond*Z conditions.
1863 CHECK_NE(lhs_reg, ZERO);
1864 CHECK_NE(rhs_reg, ZERO);
1865 break;
1866 case kCondLTZ:
1867 case kCondGEZ:
1868 case kCondLEZ:
1869 case kCondGTZ:
1870 case kCondEQZ:
1871 case kCondNEZ:
1872 // Require registers other than 0 not only for R6, but also for R2 to catch errors.
1873 CHECK_NE(lhs_reg, ZERO);
1874 CHECK_EQ(rhs_reg, ZERO);
1875 break;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001876 case kCondF:
1877 case kCondT:
1878 CHECK_EQ(rhs_reg, ZERO);
1879 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001880 case kUncond:
1881 UNREACHABLE();
1882 }
1883 CHECK(!IsNop(condition, lhs_reg, rhs_reg));
1884 if (IsUncond(condition, lhs_reg, rhs_reg)) {
1885 // Branch condition is always true, make the branch unconditional.
1886 condition_ = kUncond;
1887 }
Alexey Frunze96b66822016-09-10 02:32:44 -07001888 InitializeType(kCondBranch, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001889}
1890
Alexey Frunze96b66822016-09-10 02:32:44 -07001891MipsAssembler::Branch::Branch(bool is_r6,
1892 uint32_t location,
1893 Register dest_reg,
1894 Register base_reg,
1895 Type label_or_literal_type)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001896 : old_location_(location),
1897 location_(location),
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001898 target_(kUnresolved),
1899 lhs_reg_(dest_reg),
1900 rhs_reg_(base_reg),
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001901 condition_(kUncond),
1902 delayed_instruction_(kUnfilledDelaySlot) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07001903 CHECK_NE(dest_reg, ZERO);
1904 if (is_r6) {
1905 CHECK_EQ(base_reg, ZERO);
1906 } else {
1907 CHECK_NE(base_reg, ZERO);
1908 }
Alexey Frunze96b66822016-09-10 02:32:44 -07001909 InitializeType(label_or_literal_type, is_r6);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001910}
1911
1912MipsAssembler::BranchCondition MipsAssembler::Branch::OppositeCondition(
1913 MipsAssembler::BranchCondition cond) {
1914 switch (cond) {
1915 case kCondLT:
1916 return kCondGE;
1917 case kCondGE:
1918 return kCondLT;
1919 case kCondLE:
1920 return kCondGT;
1921 case kCondGT:
1922 return kCondLE;
1923 case kCondLTZ:
1924 return kCondGEZ;
1925 case kCondGEZ:
1926 return kCondLTZ;
1927 case kCondLEZ:
1928 return kCondGTZ;
1929 case kCondGTZ:
1930 return kCondLEZ;
1931 case kCondEQ:
1932 return kCondNE;
1933 case kCondNE:
1934 return kCondEQ;
1935 case kCondEQZ:
1936 return kCondNEZ;
1937 case kCondNEZ:
1938 return kCondEQZ;
1939 case kCondLTU:
1940 return kCondGEU;
1941 case kCondGEU:
1942 return kCondLTU;
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08001943 case kCondF:
1944 return kCondT;
1945 case kCondT:
1946 return kCondF;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02001947 case kUncond:
1948 LOG(FATAL) << "Unexpected branch condition " << cond;
1949 }
1950 UNREACHABLE();
1951}
1952
1953MipsAssembler::Branch::Type MipsAssembler::Branch::GetType() const {
1954 return type_;
1955}
1956
1957MipsAssembler::BranchCondition MipsAssembler::Branch::GetCondition() const {
1958 return condition_;
1959}
1960
1961Register MipsAssembler::Branch::GetLeftRegister() const {
1962 return static_cast<Register>(lhs_reg_);
1963}
1964
1965Register MipsAssembler::Branch::GetRightRegister() const {
1966 return static_cast<Register>(rhs_reg_);
1967}
1968
1969uint32_t MipsAssembler::Branch::GetTarget() const {
1970 return target_;
1971}
1972
1973uint32_t MipsAssembler::Branch::GetLocation() const {
1974 return location_;
1975}
1976
1977uint32_t MipsAssembler::Branch::GetOldLocation() const {
1978 return old_location_;
1979}
1980
Alexey Frunze57eb0f52016-07-29 22:04:46 -07001981uint32_t MipsAssembler::Branch::GetPrecedingInstructionLength(Type type) const {
1982 // Short branches with delay slots always consist of two instructions, the branch
1983 // and the delay slot, irrespective of whether the delay slot is filled with a
1984 // useful instruction or not.
1985 // Long composite branches may have a length longer by one instruction than
1986 // specified in branch_info_[].length. This happens when an instruction is taken
1987 // to fill the short branch delay slot, but the branch eventually becomes long
1988 // and formally has no delay slot to fill. This instruction is placed at the
1989 // beginning of the long composite branch and this needs to be accounted for in
1990 // the branch length and the location of the offset encoded in the branch.
1991 switch (type) {
1992 case kLongUncondBranch:
1993 case kLongCondBranch:
1994 case kLongCall:
1995 case kR6LongCondBranch:
1996 return (delayed_instruction_ != kUnfilledDelaySlot &&
1997 delayed_instruction_ != kUnfillableDelaySlot) ? 1 : 0;
1998 default:
1999 return 0;
2000 }
2001}
2002
2003uint32_t MipsAssembler::Branch::GetPrecedingInstructionSize(Type type) const {
2004 return GetPrecedingInstructionLength(type) * sizeof(uint32_t);
2005}
2006
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002007uint32_t MipsAssembler::Branch::GetLength() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002008 return GetPrecedingInstructionLength(type_) + branch_info_[type_].length;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002009}
2010
2011uint32_t MipsAssembler::Branch::GetOldLength() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002012 return GetPrecedingInstructionLength(old_type_) + branch_info_[old_type_].length;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002013}
2014
2015uint32_t MipsAssembler::Branch::GetSize() const {
2016 return GetLength() * sizeof(uint32_t);
2017}
2018
2019uint32_t MipsAssembler::Branch::GetOldSize() const {
2020 return GetOldLength() * sizeof(uint32_t);
2021}
2022
2023uint32_t MipsAssembler::Branch::GetEndLocation() const {
2024 return GetLocation() + GetSize();
2025}
2026
2027uint32_t MipsAssembler::Branch::GetOldEndLocation() const {
2028 return GetOldLocation() + GetOldSize();
2029}
2030
2031bool MipsAssembler::Branch::IsLong() const {
2032 switch (type_) {
2033 // R2 short branches.
2034 case kUncondBranch:
2035 case kCondBranch:
2036 case kCall:
Alexey Frunze96b66822016-09-10 02:32:44 -07002037 // R2 near label.
2038 case kLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002039 // R2 near literal.
2040 case kLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002041 // R6 short branches.
2042 case kR6UncondBranch:
2043 case kR6CondBranch:
2044 case kR6Call:
Alexey Frunze96b66822016-09-10 02:32:44 -07002045 // R6 near label.
2046 case kR6Label:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002047 // R6 near literal.
2048 case kR6Literal:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002049 return false;
2050 // R2 long branches.
2051 case kLongUncondBranch:
2052 case kLongCondBranch:
2053 case kLongCall:
Alexey Frunze96b66822016-09-10 02:32:44 -07002054 // R2 far label.
2055 case kFarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002056 // R2 far literal.
2057 case kFarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002058 // R6 long branches.
2059 case kR6LongUncondBranch:
2060 case kR6LongCondBranch:
2061 case kR6LongCall:
Alexey Frunze96b66822016-09-10 02:32:44 -07002062 // R6 far label.
2063 case kR6FarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002064 // R6 far literal.
2065 case kR6FarLiteral:
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002066 return true;
2067 }
2068 UNREACHABLE();
2069}
2070
2071bool MipsAssembler::Branch::IsResolved() const {
2072 return target_ != kUnresolved;
2073}
2074
2075MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSize() const {
2076 OffsetBits offset_size =
2077 (type_ == kR6CondBranch && (condition_ == kCondEQZ || condition_ == kCondNEZ))
2078 ? kOffset23
2079 : branch_info_[type_].offset_size;
2080 return offset_size;
2081}
2082
2083MipsAssembler::Branch::OffsetBits MipsAssembler::Branch::GetOffsetSizeNeeded(uint32_t location,
2084 uint32_t target) {
2085 // For unresolved targets assume the shortest encoding
2086 // (later it will be made longer if needed).
2087 if (target == kUnresolved)
2088 return kOffset16;
2089 int64_t distance = static_cast<int64_t>(target) - location;
2090 // To simplify calculations in composite branches consisting of multiple instructions
2091 // bump up the distance by a value larger than the max byte size of a composite branch.
2092 distance += (distance >= 0) ? kMaxBranchSize : -kMaxBranchSize;
2093 if (IsInt<kOffset16>(distance))
2094 return kOffset16;
2095 else if (IsInt<kOffset18>(distance))
2096 return kOffset18;
2097 else if (IsInt<kOffset21>(distance))
2098 return kOffset21;
2099 else if (IsInt<kOffset23>(distance))
2100 return kOffset23;
2101 else if (IsInt<kOffset28>(distance))
2102 return kOffset28;
2103 return kOffset32;
2104}
2105
2106void MipsAssembler::Branch::Resolve(uint32_t target) {
2107 target_ = target;
2108}
2109
2110void MipsAssembler::Branch::Relocate(uint32_t expand_location, uint32_t delta) {
2111 if (location_ > expand_location) {
2112 location_ += delta;
2113 }
2114 if (!IsResolved()) {
2115 return; // Don't know the target yet.
2116 }
2117 if (target_ > expand_location) {
2118 target_ += delta;
2119 }
2120}
2121
2122void MipsAssembler::Branch::PromoteToLong() {
2123 switch (type_) {
2124 // R2 short branches.
2125 case kUncondBranch:
2126 type_ = kLongUncondBranch;
2127 break;
2128 case kCondBranch:
2129 type_ = kLongCondBranch;
2130 break;
2131 case kCall:
2132 type_ = kLongCall;
2133 break;
Alexey Frunze96b66822016-09-10 02:32:44 -07002134 // R2 near label.
2135 case kLabel:
2136 type_ = kFarLabel;
2137 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002138 // R2 near literal.
2139 case kLiteral:
2140 type_ = kFarLiteral;
2141 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002142 // R6 short branches.
2143 case kR6UncondBranch:
2144 type_ = kR6LongUncondBranch;
2145 break;
2146 case kR6CondBranch:
2147 type_ = kR6LongCondBranch;
2148 break;
2149 case kR6Call:
2150 type_ = kR6LongCall;
2151 break;
Alexey Frunze96b66822016-09-10 02:32:44 -07002152 // R6 near label.
2153 case kR6Label:
2154 type_ = kR6FarLabel;
2155 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002156 // R6 near literal.
2157 case kR6Literal:
2158 type_ = kR6FarLiteral;
2159 break;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002160 default:
2161 // Note: 'type_' is already long.
2162 break;
2163 }
2164 CHECK(IsLong());
2165}
2166
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002167uint32_t MipsAssembler::GetBranchLocationOrPcRelBase(const MipsAssembler::Branch* branch) const {
2168 switch (branch->GetType()) {
Alexey Frunze96b66822016-09-10 02:32:44 -07002169 case Branch::kLabel:
2170 case Branch::kFarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002171 case Branch::kLiteral:
2172 case Branch::kFarLiteral:
2173 return GetLabelLocation(&pc_rel_base_label_);
2174 default:
2175 return branch->GetLocation();
2176 }
2177}
2178
2179uint32_t MipsAssembler::Branch::PromoteIfNeeded(uint32_t location, uint32_t max_short_distance) {
Alexey Frunze96b66822016-09-10 02:32:44 -07002180 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 labels/literals or
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002181 // `this->GetLocation()` for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002182 // If the branch is still unresolved or already long, nothing to do.
2183 if (IsLong() || !IsResolved()) {
2184 return 0;
2185 }
2186 // Promote the short branch to long if the offset size is too small
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002187 // to hold the distance between location and target_.
2188 if (GetOffsetSizeNeeded(location, target_) > GetOffsetSize()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002189 PromoteToLong();
2190 uint32_t old_size = GetOldSize();
2191 uint32_t new_size = GetSize();
2192 CHECK_GT(new_size, old_size);
2193 return new_size - old_size;
2194 }
2195 // The following logic is for debugging/testing purposes.
2196 // Promote some short branches to long when it's not really required.
2197 if (UNLIKELY(max_short_distance != std::numeric_limits<uint32_t>::max())) {
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002198 int64_t distance = static_cast<int64_t>(target_) - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002199 distance = (distance >= 0) ? distance : -distance;
2200 if (distance >= max_short_distance) {
2201 PromoteToLong();
2202 uint32_t old_size = GetOldSize();
2203 uint32_t new_size = GetSize();
2204 CHECK_GT(new_size, old_size);
2205 return new_size - old_size;
2206 }
2207 }
2208 return 0;
2209}
2210
2211uint32_t MipsAssembler::Branch::GetOffsetLocation() const {
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002212 return location_ + GetPrecedingInstructionSize(type_) +
2213 branch_info_[type_].instr_offset * sizeof(uint32_t);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002214}
2215
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002216uint32_t MipsAssembler::GetBranchOrPcRelBaseForEncoding(const MipsAssembler::Branch* branch) const {
2217 switch (branch->GetType()) {
Alexey Frunze96b66822016-09-10 02:32:44 -07002218 case Branch::kLabel:
2219 case Branch::kFarLabel:
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002220 case Branch::kLiteral:
2221 case Branch::kFarLiteral:
2222 return GetLabelLocation(&pc_rel_base_label_);
2223 default:
2224 return branch->GetOffsetLocation() +
2225 Branch::branch_info_[branch->GetType()].pc_org * sizeof(uint32_t);
2226 }
2227}
2228
2229uint32_t MipsAssembler::Branch::GetOffset(uint32_t location) const {
Alexey Frunze96b66822016-09-10 02:32:44 -07002230 // `location` is either `GetLabelLocation(&pc_rel_base_label_)` for R2 labels/literals or
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002231 // `this->GetOffsetLocation() + branch_info_[this->GetType()].pc_org * sizeof(uint32_t)`
2232 // for everything else.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002233 CHECK(IsResolved());
2234 uint32_t ofs_mask = 0xFFFFFFFF >> (32 - GetOffsetSize());
2235 // Calculate the byte distance between instructions and also account for
2236 // different PC-relative origins.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002237 uint32_t offset = target_ - location;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002238 // Prepare the offset for encoding into the instruction(s).
2239 offset = (offset & ofs_mask) >> branch_info_[type_].offset_shift;
2240 return offset;
2241}
2242
2243MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) {
2244 CHECK_LT(branch_id, branches_.size());
2245 return &branches_[branch_id];
2246}
2247
2248const MipsAssembler::Branch* MipsAssembler::GetBranch(uint32_t branch_id) const {
2249 CHECK_LT(branch_id, branches_.size());
2250 return &branches_[branch_id];
2251}
2252
2253void MipsAssembler::Bind(MipsLabel* label) {
2254 CHECK(!label->IsBound());
2255 uint32_t bound_pc = buffer_.Size();
2256
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002257 // Make the delay slot FSM aware of the new label.
2258 DsFsmLabel();
2259
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002260 // Walk the list of branches referring to and preceding this label.
2261 // Store the previously unknown target addresses in them.
2262 while (label->IsLinked()) {
2263 uint32_t branch_id = label->Position();
2264 Branch* branch = GetBranch(branch_id);
2265 branch->Resolve(bound_pc);
2266
2267 uint32_t branch_location = branch->GetLocation();
2268 // Extract the location of the previous branch in the list (walking the list backwards;
2269 // the previous branch ID was stored in the space reserved for this branch).
2270 uint32_t prev = buffer_.Load<uint32_t>(branch_location);
2271
2272 // On to the previous branch in the list...
2273 label->position_ = prev;
2274 }
2275
2276 // Now make the label object contain its own location (relative to the end of the preceding
2277 // branch, if any; it will be used by the branches referring to and following this label).
2278 label->prev_branch_id_plus_one_ = branches_.size();
2279 if (label->prev_branch_id_plus_one_) {
2280 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
2281 const Branch* branch = GetBranch(branch_id);
2282 bound_pc -= branch->GetEndLocation();
2283 }
2284 label->BindTo(bound_pc);
2285}
2286
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002287uint32_t MipsAssembler::GetLabelLocation(const MipsLabel* label) const {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002288 CHECK(label->IsBound());
2289 uint32_t target = label->Position();
2290 if (label->prev_branch_id_plus_one_) {
2291 // Get label location based on the branch preceding it.
2292 uint32_t branch_id = label->prev_branch_id_plus_one_ - 1;
2293 const Branch* branch = GetBranch(branch_id);
2294 target += branch->GetEndLocation();
2295 }
2296 return target;
2297}
2298
2299uint32_t MipsAssembler::GetAdjustedPosition(uint32_t old_position) {
2300 // We can reconstruct the adjustment by going through all the branches from the beginning
2301 // up to the old_position. Since we expect AdjustedPosition() to be called in a loop
2302 // with increasing old_position, we can use the data from last AdjustedPosition() to
2303 // continue where we left off and the whole loop should be O(m+n) where m is the number
2304 // of positions to adjust and n is the number of branches.
2305 if (old_position < last_old_position_) {
2306 last_position_adjustment_ = 0;
2307 last_old_position_ = 0;
2308 last_branch_id_ = 0;
2309 }
2310 while (last_branch_id_ != branches_.size()) {
2311 const Branch* branch = GetBranch(last_branch_id_);
2312 if (branch->GetLocation() >= old_position + last_position_adjustment_) {
2313 break;
2314 }
2315 last_position_adjustment_ += branch->GetSize() - branch->GetOldSize();
2316 ++last_branch_id_;
2317 }
2318 last_old_position_ = old_position;
2319 return old_position + last_position_adjustment_;
2320}
2321
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002322void MipsAssembler::BindPcRelBaseLabel() {
2323 Bind(&pc_rel_base_label_);
2324}
2325
Alexey Frunze06a46c42016-07-19 15:00:40 -07002326uint32_t MipsAssembler::GetPcRelBaseLabelLocation() const {
2327 return GetLabelLocation(&pc_rel_base_label_);
2328}
2329
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002330void MipsAssembler::FinalizeLabeledBranch(MipsLabel* label) {
2331 uint32_t length = branches_.back().GetLength();
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002332 // Commit the last branch target label (if any).
2333 DsFsmCommitLabel();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002334 if (!label->IsBound()) {
2335 // Branch forward (to a following label), distance is unknown.
2336 // The first branch forward will contain 0, serving as the terminator of
2337 // the list of forward-reaching branches.
2338 Emit(label->position_);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002339 // Nothing for the delay slot (yet).
2340 DsFsmInstrNop(0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002341 length--;
2342 // Now make the label object point to this branch
2343 // (this forms a linked list of branches preceding this label).
2344 uint32_t branch_id = branches_.size() - 1;
2345 label->LinkTo(branch_id);
2346 }
2347 // Reserve space for the branch.
2348 while (length--) {
2349 Nop();
2350 }
2351}
2352
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002353bool MipsAssembler::Branch::CanHaveDelayedInstruction(const DelaySlot& delay_slot) const {
2354 if (delay_slot.instruction_ == 0) {
2355 // NOP or no instruction for the delay slot.
2356 return false;
2357 }
2358 switch (type_) {
2359 // R2 unconditional branches.
2360 case kUncondBranch:
2361 case kLongUncondBranch:
2362 // There are no register interdependencies.
2363 return true;
2364
2365 // R2 calls.
2366 case kCall:
2367 case kLongCall:
2368 // Instructions depending on or modifying RA should not be moved into delay slots
2369 // of branches modifying RA.
2370 return ((delay_slot.gpr_ins_mask_ | delay_slot.gpr_outs_mask_) & (1u << RA)) == 0;
2371
2372 // R2 conditional branches.
2373 case kCondBranch:
2374 case kLongCondBranch:
2375 switch (condition_) {
2376 // Branches with one GPR source.
2377 case kCondLTZ:
2378 case kCondGEZ:
2379 case kCondLEZ:
2380 case kCondGTZ:
2381 case kCondEQZ:
2382 case kCondNEZ:
2383 return (delay_slot.gpr_outs_mask_ & (1u << lhs_reg_)) == 0;
2384
2385 // Branches with two GPR sources.
2386 case kCondEQ:
2387 case kCondNE:
2388 return (delay_slot.gpr_outs_mask_ & ((1u << lhs_reg_) | (1u << rhs_reg_))) == 0;
2389
2390 // Branches with one FPU condition code source.
2391 case kCondF:
2392 case kCondT:
2393 return (delay_slot.cc_outs_mask_ & (1u << lhs_reg_)) == 0;
2394
2395 default:
2396 // We don't support synthetic R2 branches (preceded with slt[u]) at this level
2397 // (R2 doesn't have branches to compare 2 registers using <, <=, >=, >).
2398 LOG(FATAL) << "Unexpected branch condition " << condition_;
2399 UNREACHABLE();
2400 }
2401
2402 // R6 unconditional branches.
2403 case kR6UncondBranch:
2404 case kR6LongUncondBranch:
2405 // R6 calls.
2406 case kR6Call:
2407 case kR6LongCall:
2408 // There are no delay slots.
2409 return false;
2410
2411 // R6 conditional branches.
2412 case kR6CondBranch:
2413 case kR6LongCondBranch:
2414 switch (condition_) {
2415 // Branches with one FPU register source.
2416 case kCondF:
2417 case kCondT:
2418 return (delay_slot.fpr_outs_mask_ & (1u << lhs_reg_)) == 0;
2419 // Others have a forbidden slot instead of a delay slot.
2420 default:
2421 return false;
2422 }
2423
2424 // Literals.
2425 default:
2426 LOG(FATAL) << "Unexpected branch type " << type_;
2427 UNREACHABLE();
2428 }
2429}
2430
2431uint32_t MipsAssembler::Branch::GetDelayedInstruction() const {
2432 return delayed_instruction_;
2433}
2434
2435void MipsAssembler::Branch::SetDelayedInstruction(uint32_t instruction) {
2436 CHECK_NE(instruction, kUnfilledDelaySlot);
2437 CHECK_EQ(delayed_instruction_, kUnfilledDelaySlot);
2438 delayed_instruction_ = instruction;
2439}
2440
2441void MipsAssembler::Branch::DecrementLocations() {
2442 // We first create a branch object, which gets its type and locations initialized,
2443 // and then we check if the branch can actually have the preceding instruction moved
2444 // into its delay slot. If it can, the branch locations need to be decremented.
2445 //
2446 // We could make the check before creating the branch object and avoid the location
2447 // adjustment, but the check is cleaner when performed on an initialized branch
2448 // object.
2449 //
2450 // If the branch is backwards (to a previously bound label), reducing the locations
2451 // cannot cause a short branch to exceed its offset range because the offset reduces.
2452 // And this is not at all a problem for a long branch backwards.
2453 //
2454 // If the branch is forward (not linked to any label yet), reducing the locations
2455 // is harmless. The branch will be promoted to long if needed when the target is known.
2456 CHECK_EQ(location_, old_location_);
2457 CHECK_GE(old_location_, sizeof(uint32_t));
2458 old_location_ -= sizeof(uint32_t);
2459 location_ = old_location_;
2460}
2461
2462void MipsAssembler::MoveInstructionToDelaySlot(Branch& branch) {
2463 if (branch.CanHaveDelayedInstruction(delay_slot_)) {
2464 // The last instruction cannot be used in a different delay slot,
2465 // do not commit the label before it (if any).
2466 DsFsmDropLabel();
2467 // Remove the last emitted instruction.
2468 size_t size = buffer_.Size();
2469 CHECK_GE(size, sizeof(uint32_t));
2470 size -= sizeof(uint32_t);
2471 CHECK_EQ(buffer_.Load<uint32_t>(size), delay_slot_.instruction_);
2472 buffer_.Resize(size);
2473 // Attach it to the branch and adjust the branch locations.
2474 branch.DecrementLocations();
2475 branch.SetDelayedInstruction(delay_slot_.instruction_);
2476 } else if (!reordering_ && branch.GetType() == Branch::kUncondBranch) {
2477 // If reordefing is disabled, prevent absorption of the target instruction.
2478 branch.SetDelayedInstruction(Branch::kUnfillableDelaySlot);
2479 }
2480}
2481
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002482void MipsAssembler::Buncond(MipsLabel* label) {
2483 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002484 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ false);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002485 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002486 FinalizeLabeledBranch(label);
2487}
2488
2489void MipsAssembler::Bcond(MipsLabel* label, BranchCondition condition, Register lhs, Register rhs) {
2490 // If lhs = rhs, this can be a NOP.
2491 if (Branch::IsNop(condition, lhs, rhs)) {
2492 return;
2493 }
2494 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
2495 branches_.emplace_back(IsR6(), buffer_.Size(), target, condition, lhs, rhs);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002496 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002497 FinalizeLabeledBranch(label);
2498}
2499
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002500void MipsAssembler::Call(MipsLabel* label) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002501 uint32_t target = label->IsBound() ? GetLabelLocation(label) : Branch::kUnresolved;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002502 branches_.emplace_back(IsR6(), buffer_.Size(), target, /* is_call */ true);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002503 MoveInstructionToDelaySlot(branches_.back());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002504 FinalizeLabeledBranch(label);
2505}
2506
Alexey Frunze96b66822016-09-10 02:32:44 -07002507void MipsAssembler::LoadLabelAddress(Register dest_reg, Register base_reg, MipsLabel* label) {
2508 // Label address loads are treated as pseudo branches since they require very similar handling.
2509 DCHECK(!label->IsBound());
2510 branches_.emplace_back(IsR6(), buffer_.Size(), dest_reg, base_reg, Branch::kLabel);
2511 FinalizeLabeledBranch(label);
2512}
2513
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002514Literal* MipsAssembler::NewLiteral(size_t size, const uint8_t* data) {
2515 DCHECK(size == 4u || size == 8u) << size;
2516 literals_.emplace_back(size, data);
2517 return &literals_.back();
2518}
2519
2520void MipsAssembler::LoadLiteral(Register dest_reg, Register base_reg, Literal* literal) {
2521 // Literal loads are treated as pseudo branches since they require very similar handling.
2522 DCHECK_EQ(literal->GetSize(), 4u);
2523 MipsLabel* label = literal->GetLabel();
2524 DCHECK(!label->IsBound());
Alexey Frunze96b66822016-09-10 02:32:44 -07002525 branches_.emplace_back(IsR6(), buffer_.Size(), dest_reg, base_reg, Branch::kLiteral);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002526 FinalizeLabeledBranch(label);
2527}
2528
Alexey Frunze96b66822016-09-10 02:32:44 -07002529JumpTable* MipsAssembler::CreateJumpTable(std::vector<MipsLabel*>&& labels) {
2530 jump_tables_.emplace_back(std::move(labels));
2531 JumpTable* table = &jump_tables_.back();
2532 DCHECK(!table->GetLabel()->IsBound());
2533 return table;
2534}
2535
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002536void MipsAssembler::EmitLiterals() {
2537 if (!literals_.empty()) {
2538 // We don't support byte and half-word literals.
2539 // TODO: proper alignment for 64-bit literals when they're implemented.
2540 for (Literal& literal : literals_) {
2541 MipsLabel* label = literal.GetLabel();
2542 Bind(label);
2543 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
2544 DCHECK(literal.GetSize() == 4u || literal.GetSize() == 8u);
2545 for (size_t i = 0, size = literal.GetSize(); i != size; ++i) {
2546 buffer_.Emit<uint8_t>(literal.GetData()[i]);
2547 }
2548 }
2549 }
2550}
2551
Alexey Frunze96b66822016-09-10 02:32:44 -07002552void MipsAssembler::ReserveJumpTableSpace() {
2553 if (!jump_tables_.empty()) {
2554 for (JumpTable& table : jump_tables_) {
2555 MipsLabel* label = table.GetLabel();
2556 Bind(label);
2557
2558 // Bulk ensure capacity, as this may be large.
2559 size_t orig_size = buffer_.Size();
2560 size_t required_capacity = orig_size + table.GetSize();
2561 if (required_capacity > buffer_.Capacity()) {
2562 buffer_.ExtendCapacity(required_capacity);
2563 }
2564#ifndef NDEBUG
2565 buffer_.has_ensured_capacity_ = true;
2566#endif
2567
2568 // Fill the space with dummy data as the data is not final
2569 // until the branches have been promoted. And we shouldn't
2570 // be moving uninitialized data during branch promotion.
2571 for (size_t cnt = table.GetData().size(), i = 0; i < cnt; i++) {
2572 buffer_.Emit<uint32_t>(0x1abe1234u);
2573 }
2574
2575#ifndef NDEBUG
2576 buffer_.has_ensured_capacity_ = false;
2577#endif
2578 }
2579 }
2580}
2581
2582void MipsAssembler::EmitJumpTables() {
2583 if (!jump_tables_.empty()) {
2584 CHECK(!overwriting_);
2585 // Switch from appending instructions at the end of the buffer to overwriting
2586 // existing instructions (here, jump tables) in the buffer.
2587 overwriting_ = true;
2588
2589 for (JumpTable& table : jump_tables_) {
2590 MipsLabel* table_label = table.GetLabel();
2591 uint32_t start = GetLabelLocation(table_label);
2592 overwrite_location_ = start;
2593
2594 for (MipsLabel* target : table.GetData()) {
2595 CHECK_EQ(buffer_.Load<uint32_t>(overwrite_location_), 0x1abe1234u);
2596 // The table will contain target addresses relative to the table start.
2597 uint32_t offset = GetLabelLocation(target) - start;
2598 Emit(offset);
2599 }
2600 }
2601
2602 overwriting_ = false;
2603 }
2604}
2605
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002606void MipsAssembler::PromoteBranches() {
2607 // Promote short branches to long as necessary.
2608 bool changed;
2609 do {
2610 changed = false;
2611 for (auto& branch : branches_) {
2612 CHECK(branch.IsResolved());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002613 uint32_t base = GetBranchLocationOrPcRelBase(&branch);
2614 uint32_t delta = branch.PromoteIfNeeded(base);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002615 // If this branch has been promoted and needs to expand in size,
2616 // relocate all branches by the expansion size.
2617 if (delta) {
2618 changed = true;
2619 uint32_t expand_location = branch.GetLocation();
2620 for (auto& branch2 : branches_) {
2621 branch2.Relocate(expand_location, delta);
2622 }
2623 }
2624 }
2625 } while (changed);
2626
2627 // Account for branch expansion by resizing the code buffer
2628 // and moving the code in it to its final location.
2629 size_t branch_count = branches_.size();
2630 if (branch_count > 0) {
2631 // Resize.
2632 Branch& last_branch = branches_[branch_count - 1];
2633 uint32_t size_delta = last_branch.GetEndLocation() - last_branch.GetOldEndLocation();
2634 uint32_t old_size = buffer_.Size();
2635 buffer_.Resize(old_size + size_delta);
2636 // Move the code residing between branch placeholders.
2637 uint32_t end = old_size;
2638 for (size_t i = branch_count; i > 0; ) {
2639 Branch& branch = branches_[--i];
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002640 CHECK_GE(end, branch.GetOldEndLocation());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002641 uint32_t size = end - branch.GetOldEndLocation();
2642 buffer_.Move(branch.GetEndLocation(), branch.GetOldEndLocation(), size);
2643 end = branch.GetOldLocation();
2644 }
2645 }
2646}
2647
2648// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
2649const MipsAssembler::Branch::BranchInfo MipsAssembler::Branch::branch_info_[] = {
2650 // R2 short branches.
2651 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kUncondBranch
2652 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002653 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kCall
Alexey Frunze96b66822016-09-10 02:32:44 -07002654 // R2 near label.
2655 { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLabel
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002656 // R2 near literal.
2657 { 1, 0, 0, MipsAssembler::Branch::kOffset16, 0 }, // kLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002658 // R2 long branches.
2659 { 9, 3, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongUncondBranch
2660 { 10, 4, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCondBranch
2661 { 6, 1, 1, MipsAssembler::Branch::kOffset32, 0 }, // kLongCall
Alexey Frunze96b66822016-09-10 02:32:44 -07002662 // R2 far label.
2663 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLabel
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002664 // R2 far literal.
2665 { 3, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kFarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002666 // R6 short branches.
2667 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6UncondBranch
2668 { 2, 0, 1, MipsAssembler::Branch::kOffset18, 2 }, // kR6CondBranch
2669 // Exception: kOffset23 for beqzc/bnezc.
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002670 { 1, 0, 1, MipsAssembler::Branch::kOffset28, 2 }, // kR6Call
Alexey Frunze96b66822016-09-10 02:32:44 -07002671 // R6 near label.
2672 { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Label
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002673 // R6 near literal.
2674 { 1, 0, 0, MipsAssembler::Branch::kOffset21, 2 }, // kR6Literal
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002675 // R6 long branches.
2676 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongUncondBranch
2677 { 3, 1, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCondBranch
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002678 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6LongCall
Alexey Frunze96b66822016-09-10 02:32:44 -07002679 // R6 far label.
2680 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLabel
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002681 // R6 far literal.
2682 { 2, 0, 0, MipsAssembler::Branch::kOffset32, 0 }, // kR6FarLiteral
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002683};
2684
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002685// Note: make sure branch_info_[] and EmitBranch() are kept synchronized.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002686void MipsAssembler::EmitBranch(MipsAssembler::Branch* branch) {
2687 CHECK_EQ(overwriting_, true);
2688 overwrite_location_ = branch->GetLocation();
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002689 uint32_t offset = branch->GetOffset(GetBranchOrPcRelBaseForEncoding(branch));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002690 BranchCondition condition = branch->GetCondition();
2691 Register lhs = branch->GetLeftRegister();
2692 Register rhs = branch->GetRightRegister();
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002693 uint32_t delayed_instruction = branch->GetDelayedInstruction();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002694 switch (branch->GetType()) {
2695 // R2 short branches.
2696 case Branch::kUncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002697 if (delayed_instruction == Branch::kUnfillableDelaySlot) {
2698 // The branch was created when reordering was disabled, do not absorb the target
2699 // instruction.
2700 delayed_instruction = 0; // NOP.
2701 } else if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2702 // Try to absorb the target instruction into the delay slot.
2703 delayed_instruction = 0; // NOP.
2704 // Incrementing the signed 16-bit offset past the target instruction must not
2705 // cause overflow into the negative subrange, check for the max offset.
2706 if (offset != 0x7FFF) {
2707 uint32_t target = branch->GetTarget();
2708 if (std::binary_search(ds_fsm_target_pcs_.begin(), ds_fsm_target_pcs_.end(), target)) {
2709 delayed_instruction = buffer_.Load<uint32_t>(target);
2710 offset++;
2711 }
2712 }
2713 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002714 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2715 B(offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002716 Emit(delayed_instruction);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002717 break;
2718 case Branch::kCondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002719 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2720 if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2721 delayed_instruction = 0; // NOP.
2722 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002723 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002724 EmitBcondR2(condition, lhs, rhs, offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002725 Emit(delayed_instruction);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002726 break;
2727 case Branch::kCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002728 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2729 if (delayed_instruction == Branch::kUnfilledDelaySlot) {
2730 delayed_instruction = 0; // NOP.
2731 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002732 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002733 Bal(offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002734 Emit(delayed_instruction);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002735 break;
2736
Alexey Frunze96b66822016-09-10 02:32:44 -07002737 // R2 near label.
2738 case Branch::kLabel:
2739 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2740 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2741 Addiu(lhs, rhs, offset);
2742 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002743 // R2 near literal.
2744 case Branch::kLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002745 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002746 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2747 Lw(lhs, rhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002748 break;
2749
2750 // R2 long branches.
2751 case Branch::kLongUncondBranch:
2752 // To get the value of the PC register we need to use the NAL instruction.
2753 // NAL clobbers the RA register. However, RA must be preserved if the
2754 // method is compiled without the entry/exit sequences that would take care
2755 // of preserving RA (typically, leaf methods don't preserve RA explicitly).
2756 // So, we need to preserve RA in some temporary storage ourselves. The AT
2757 // register can't be used for this because we need it to load a constant
2758 // which will be added to the value that NAL stores in RA. And we can't
2759 // use T9 for this in the context of the JNI compiler, which uses it
2760 // as a scratch register (see InterproceduralScratchRegister()).
2761 // If we were to add a 32-bit constant to RA using two ADDIU instructions,
2762 // we'd also need to use the ROTR instruction, which requires no less than
2763 // MIPSR2.
2764 // Perhaps, we could use T8 or one of R2's multiplier/divider registers
2765 // (LO or HI) or even a floating-point register, but that doesn't seem
2766 // like a nice solution. We may want this to work on both R6 and pre-R6.
2767 // For now simply use the stack for RA. This should be OK since for the
2768 // vast majority of code a short PC-relative branch is sufficient.
2769 // TODO: can this be improved?
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002770 // TODO: consider generation of a shorter sequence when we know that RA
2771 // is explicitly preserved by the method entry/exit code.
2772 if (delayed_instruction != Branch::kUnfilledDelaySlot &&
2773 delayed_instruction != Branch::kUnfillableDelaySlot) {
2774 Emit(delayed_instruction);
2775 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002776 Push(RA);
2777 Nal();
2778 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2779 Lui(AT, High16Bits(offset));
2780 Ori(AT, AT, Low16Bits(offset));
2781 Addu(AT, AT, RA);
2782 Lw(RA, SP, 0);
2783 Jr(AT);
2784 DecreaseFrameSize(kMipsWordSize);
2785 break;
2786 case Branch::kLongCondBranch:
2787 // The comment on case 'Branch::kLongUncondBranch' applies here as well.
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002788 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2789 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2790 Emit(delayed_instruction);
2791 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002792 // Note: the opposite condition branch encodes 8 as the distance, which is equal to the
2793 // number of instructions skipped:
2794 // (PUSH(IncreaseFrameSize(ADDIU) + SW) + NAL + LUI + ORI + ADDU + LW + JR).
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002795 EmitBcondR2(Branch::OppositeCondition(condition), lhs, rhs, 8);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002796 Push(RA);
2797 Nal();
2798 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2799 Lui(AT, High16Bits(offset));
2800 Ori(AT, AT, Low16Bits(offset));
2801 Addu(AT, AT, RA);
2802 Lw(RA, SP, 0);
2803 Jr(AT);
2804 DecreaseFrameSize(kMipsWordSize);
2805 break;
2806 case Branch::kLongCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002807 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2808 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2809 Emit(delayed_instruction);
2810 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002811 Nal();
2812 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2813 Lui(AT, High16Bits(offset));
2814 Ori(AT, AT, Low16Bits(offset));
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002815 Addu(AT, AT, RA);
2816 Jalr(AT);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002817 Nop();
2818 break;
2819
Alexey Frunze96b66822016-09-10 02:32:44 -07002820 // R2 far label.
2821 case Branch::kFarLabel:
2822 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2823 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2824 Lui(AT, High16Bits(offset));
2825 Ori(AT, AT, Low16Bits(offset));
2826 Addu(lhs, AT, rhs);
2827 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002828 // R2 far literal.
2829 case Branch::kFarLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002830 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002831 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2832 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2833 Lui(AT, High16Bits(offset));
2834 Addu(AT, AT, rhs);
2835 Lw(lhs, AT, Low16Bits(offset));
2836 break;
2837
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002838 // R6 short branches.
2839 case Branch::kR6UncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002840 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002841 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2842 Bc(offset);
2843 break;
2844 case Branch::kR6CondBranch:
2845 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002846 EmitBcondR6(condition, lhs, rhs, offset);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002847 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2848 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2849 Emit(delayed_instruction);
2850 } else {
2851 // TODO: improve by filling the forbidden slot (IFF this is
2852 // a forbidden and not a delay slot).
2853 Nop();
2854 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002855 break;
2856 case Branch::kR6Call:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002857 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002858 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002859 Balc(offset);
2860 break;
2861
Alexey Frunze96b66822016-09-10 02:32:44 -07002862 // R6 near label.
2863 case Branch::kR6Label:
2864 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2865 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2866 Addiupc(lhs, offset);
2867 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002868 // R6 near literal.
2869 case Branch::kR6Literal:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002870 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002871 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2872 Lwpc(lhs, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002873 break;
2874
2875 // R6 long branches.
2876 case Branch::kR6LongUncondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002877 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002878 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2879 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2880 Auipc(AT, High16Bits(offset));
2881 Jic(AT, Low16Bits(offset));
2882 break;
2883 case Branch::kR6LongCondBranch:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002884 DCHECK_NE(delayed_instruction, Branch::kUnfillableDelaySlot);
2885 if (delayed_instruction != Branch::kUnfilledDelaySlot) {
2886 Emit(delayed_instruction);
2887 }
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08002888 EmitBcondR6(Branch::OppositeCondition(condition), lhs, rhs, 2);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002889 offset += (offset & 0x8000) << 1; // Account for sign extension in jic.
2890 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2891 Auipc(AT, High16Bits(offset));
2892 Jic(AT, Low16Bits(offset));
2893 break;
2894 case Branch::kR6LongCall:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002895 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002896 offset += (offset & 0x8000) << 1; // Account for sign extension in jialc.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002897 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002898 Auipc(AT, High16Bits(offset));
2899 Jialc(AT, Low16Bits(offset));
2900 break;
2901
Alexey Frunze96b66822016-09-10 02:32:44 -07002902 // R6 far label.
2903 case Branch::kR6FarLabel:
2904 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
2905 offset += (offset & 0x8000) << 1; // Account for sign extension in addiu.
2906 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2907 Auipc(AT, High16Bits(offset));
2908 Addiu(lhs, AT, Low16Bits(offset));
2909 break;
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002910 // R6 far literal.
2911 case Branch::kR6FarLiteral:
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002912 DCHECK_EQ(delayed_instruction, Branch::kUnfilledDelaySlot);
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002913 offset += (offset & 0x8000) << 1; // Account for sign extension in lw.
2914 CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
2915 Auipc(AT, High16Bits(offset));
2916 Lw(lhs, AT, Low16Bits(offset));
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002917 break;
2918 }
2919 CHECK_EQ(overwrite_location_, branch->GetEndLocation());
2920 CHECK_LT(branch->GetSize(), static_cast<uint32_t>(Branch::kMaxBranchSize));
2921}
2922
2923void MipsAssembler::B(MipsLabel* label) {
2924 Buncond(label);
2925}
2926
Alexey Frunzee3fb2452016-05-10 16:08:05 -07002927void MipsAssembler::Bal(MipsLabel* label) {
2928 Call(label);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02002929}
2930
2931void MipsAssembler::Beq(Register rs, Register rt, MipsLabel* label) {
2932 Bcond(label, kCondEQ, rs, rt);
2933}
2934
2935void MipsAssembler::Bne(Register rs, Register rt, MipsLabel* label) {
2936 Bcond(label, kCondNE, rs, rt);
2937}
2938
2939void MipsAssembler::Beqz(Register rt, MipsLabel* label) {
2940 Bcond(label, kCondEQZ, rt);
2941}
2942
2943void MipsAssembler::Bnez(Register rt, MipsLabel* label) {
2944 Bcond(label, kCondNEZ, rt);
2945}
2946
2947void MipsAssembler::Bltz(Register rt, MipsLabel* label) {
2948 Bcond(label, kCondLTZ, rt);
2949}
2950
2951void MipsAssembler::Bgez(Register rt, MipsLabel* label) {
2952 Bcond(label, kCondGEZ, rt);
2953}
2954
2955void MipsAssembler::Blez(Register rt, MipsLabel* label) {
2956 Bcond(label, kCondLEZ, rt);
2957}
2958
2959void MipsAssembler::Bgtz(Register rt, MipsLabel* label) {
2960 Bcond(label, kCondGTZ, rt);
2961}
2962
Alexey Frunze57eb0f52016-07-29 22:04:46 -07002963bool MipsAssembler::CanExchangeWithSlt(Register rs, Register rt) const {
2964 // If the instruction modifies AT, `rs` or `rt`, it can't be exchanged with the slt[u]
2965 // instruction because either slt[u] depends on `rs` or `rt` or the following
2966 // conditional branch depends on AT set by slt[u].
2967 // Likewise, if the instruction depends on AT, it can't be exchanged with slt[u]
2968 // because slt[u] changes AT.
2969 return (delay_slot_.instruction_ != 0 &&
2970 (delay_slot_.gpr_outs_mask_ & ((1u << AT) | (1u << rs) | (1u << rt))) == 0 &&
2971 (delay_slot_.gpr_ins_mask_ & (1u << AT)) == 0);
2972}
2973
2974void MipsAssembler::ExchangeWithSlt(const DelaySlot& forwarded_slot) {
2975 // Exchange the last two instructions in the assembler buffer.
2976 size_t size = buffer_.Size();
2977 CHECK_GE(size, 2 * sizeof(uint32_t));
2978 size_t pos1 = size - 2 * sizeof(uint32_t);
2979 size_t pos2 = size - sizeof(uint32_t);
2980 uint32_t instr1 = buffer_.Load<uint32_t>(pos1);
2981 uint32_t instr2 = buffer_.Load<uint32_t>(pos2);
2982 CHECK_EQ(instr1, forwarded_slot.instruction_);
2983 CHECK_EQ(instr2, delay_slot_.instruction_);
2984 buffer_.Store<uint32_t>(pos1, instr2);
2985 buffer_.Store<uint32_t>(pos2, instr1);
2986 // Set the current delay slot information to that of the last instruction
2987 // in the buffer.
2988 delay_slot_ = forwarded_slot;
2989}
2990
2991void MipsAssembler::GenerateSltForCondBranch(bool unsigned_slt, Register rs, Register rt) {
2992 // If possible, exchange the slt[u] instruction with the preceding instruction,
2993 // so it can fill the delay slot.
2994 DelaySlot forwarded_slot = delay_slot_;
2995 bool exchange = CanExchangeWithSlt(rs, rt);
2996 if (exchange) {
2997 // The last instruction cannot be used in a different delay slot,
2998 // do not commit the label before it (if any).
2999 DsFsmDropLabel();
3000 }
3001 if (unsigned_slt) {
3002 Sltu(AT, rs, rt);
3003 } else {
3004 Slt(AT, rs, rt);
3005 }
3006 if (exchange) {
3007 ExchangeWithSlt(forwarded_slot);
3008 }
3009}
3010
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003011void MipsAssembler::Blt(Register rs, Register rt, MipsLabel* label) {
3012 if (IsR6()) {
3013 Bcond(label, kCondLT, rs, rt);
3014 } else if (!Branch::IsNop(kCondLT, rs, rt)) {
3015 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003016 GenerateSltForCondBranch(/* unsigned_slt */ false, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003017 Bnez(AT, label);
3018 }
3019}
3020
3021void MipsAssembler::Bge(Register rs, Register rt, MipsLabel* label) {
3022 if (IsR6()) {
3023 Bcond(label, kCondGE, rs, rt);
3024 } else if (Branch::IsUncond(kCondGE, rs, rt)) {
3025 B(label);
3026 } else {
3027 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003028 GenerateSltForCondBranch(/* unsigned_slt */ false, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003029 Beqz(AT, label);
3030 }
3031}
3032
3033void MipsAssembler::Bltu(Register rs, Register rt, MipsLabel* label) {
3034 if (IsR6()) {
3035 Bcond(label, kCondLTU, rs, rt);
3036 } else if (!Branch::IsNop(kCondLTU, rs, rt)) {
3037 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003038 GenerateSltForCondBranch(/* unsigned_slt */ true, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003039 Bnez(AT, label);
3040 }
3041}
3042
3043void MipsAssembler::Bgeu(Register rs, Register rt, MipsLabel* label) {
3044 if (IsR6()) {
3045 Bcond(label, kCondGEU, rs, rt);
3046 } else if (Branch::IsUncond(kCondGEU, rs, rt)) {
3047 B(label);
3048 } else {
3049 // Synthesize the instruction (not available on R2).
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003050 GenerateSltForCondBranch(/* unsigned_slt */ true, rs, rt);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003051 Beqz(AT, label);
jeffhao7fbee072012-08-24 17:56:54 -07003052 }
3053}
3054
Chris Larsenb74353a2015-11-20 09:07:09 -08003055void MipsAssembler::Bc1f(MipsLabel* label) {
3056 Bc1f(0, label);
3057}
3058
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003059void MipsAssembler::Bc1f(int cc, MipsLabel* label) {
3060 CHECK(IsUint<3>(cc)) << cc;
3061 Bcond(label, kCondF, static_cast<Register>(cc), ZERO);
3062}
3063
Chris Larsenb74353a2015-11-20 09:07:09 -08003064void MipsAssembler::Bc1t(MipsLabel* label) {
3065 Bc1t(0, label);
3066}
3067
Alexey Frunzecd7b0ee2015-12-03 16:46:38 -08003068void MipsAssembler::Bc1t(int cc, MipsLabel* label) {
3069 CHECK(IsUint<3>(cc)) << cc;
3070 Bcond(label, kCondT, static_cast<Register>(cc), ZERO);
3071}
3072
3073void MipsAssembler::Bc1eqz(FRegister ft, MipsLabel* label) {
3074 Bcond(label, kCondF, static_cast<Register>(ft), ZERO);
3075}
3076
3077void MipsAssembler::Bc1nez(FRegister ft, MipsLabel* label) {
3078 Bcond(label, kCondT, static_cast<Register>(ft), ZERO);
3079}
3080
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003081void MipsAssembler::AdjustBaseAndOffset(Register& base,
3082 int32_t& offset,
3083 bool is_doubleword,
3084 bool is_float) {
3085 // This method is used to adjust the base register and offset pair
3086 // for a load/store when the offset doesn't fit into int16_t.
3087 // It is assumed that `base + offset` is sufficiently aligned for memory
3088 // operands that are machine word in size or smaller. For doubleword-sized
3089 // operands it's assumed that `base` is a multiple of 8, while `offset`
3090 // may be a multiple of 4 (e.g. 4-byte-aligned long and double arguments
3091 // and spilled variables on the stack accessed relative to the stack
3092 // pointer register).
3093 // We preserve the "alignment" of `offset` by adjusting it by a multiple of 8.
3094 CHECK_NE(base, AT); // Must not overwrite the register `base` while loading `offset`.
3095
3096 bool doubleword_aligned = IsAligned<kMipsDoublewordSize>(offset);
3097 bool two_accesses = is_doubleword && (!is_float || !doubleword_aligned);
3098
3099 // IsInt<16> must be passed a signed value, hence the static cast below.
3100 if (IsInt<16>(offset) &&
3101 (!two_accesses || IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)))) {
3102 // Nothing to do: `offset` (and, if needed, `offset + 4`) fits into int16_t.
3103 return;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003104 }
3105
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003106 // Remember the "(mis)alignment" of `offset`, it will be checked at the end.
3107 uint32_t misalignment = offset & (kMipsDoublewordSize - 1);
3108
3109 // Do not load the whole 32-bit `offset` if it can be represented as
3110 // a sum of two 16-bit signed offsets. This can save an instruction or two.
3111 // To simplify matters, only do this for a symmetric range of offsets from
3112 // about -64KB to about +64KB, allowing further addition of 4 when accessing
3113 // 64-bit variables with two 32-bit accesses.
3114 constexpr int32_t kMinOffsetForSimpleAdjustment = 0x7ff8; // Max int16_t that's a multiple of 8.
3115 constexpr int32_t kMaxOffsetForSimpleAdjustment = 2 * kMinOffsetForSimpleAdjustment;
3116 if (0 <= offset && offset <= kMaxOffsetForSimpleAdjustment) {
3117 Addiu(AT, base, kMinOffsetForSimpleAdjustment);
3118 offset -= kMinOffsetForSimpleAdjustment;
3119 } else if (-kMaxOffsetForSimpleAdjustment <= offset && offset < 0) {
3120 Addiu(AT, base, -kMinOffsetForSimpleAdjustment);
3121 offset += kMinOffsetForSimpleAdjustment;
3122 } else if (IsR6()) {
3123 // On R6 take advantage of the aui instruction, e.g.:
3124 // aui AT, base, offset_high
3125 // lw reg_lo, offset_low(AT)
3126 // lw reg_hi, (offset_low+4)(AT)
3127 // or when offset_low+4 overflows int16_t:
3128 // aui AT, base, offset_high
3129 // addiu AT, AT, 8
3130 // lw reg_lo, (offset_low-8)(AT)
3131 // lw reg_hi, (offset_low-4)(AT)
3132 int16_t offset_high = High16Bits(offset);
3133 int16_t offset_low = Low16Bits(offset);
3134 offset_high += (offset_low < 0) ? 1 : 0; // Account for offset sign extension in load/store.
3135 Aui(AT, base, offset_high);
3136 if (two_accesses && !IsInt<16>(static_cast<int32_t>(offset_low + kMipsWordSize))) {
3137 // Avoid overflow in the 16-bit offset of the load/store instruction when adding 4.
3138 Addiu(AT, AT, kMipsDoublewordSize);
3139 offset_low -= kMipsDoublewordSize;
3140 }
3141 offset = offset_low;
3142 } else {
3143 // Do not load the whole 32-bit `offset` if it can be represented as
3144 // a sum of three 16-bit signed offsets. This can save an instruction.
3145 // To simplify matters, only do this for a symmetric range of offsets from
3146 // about -96KB to about +96KB, allowing further addition of 4 when accessing
3147 // 64-bit variables with two 32-bit accesses.
3148 constexpr int32_t kMinOffsetForMediumAdjustment = 2 * kMinOffsetForSimpleAdjustment;
3149 constexpr int32_t kMaxOffsetForMediumAdjustment = 3 * kMinOffsetForSimpleAdjustment;
3150 if (0 <= offset && offset <= kMaxOffsetForMediumAdjustment) {
3151 Addiu(AT, base, kMinOffsetForMediumAdjustment / 2);
3152 Addiu(AT, AT, kMinOffsetForMediumAdjustment / 2);
3153 offset -= kMinOffsetForMediumAdjustment;
3154 } else if (-kMaxOffsetForMediumAdjustment <= offset && offset < 0) {
3155 Addiu(AT, base, -kMinOffsetForMediumAdjustment / 2);
3156 Addiu(AT, AT, -kMinOffsetForMediumAdjustment / 2);
3157 offset += kMinOffsetForMediumAdjustment;
3158 } else {
3159 // Now that all shorter options have been exhausted, load the full 32-bit offset.
3160 int32_t loaded_offset = RoundDown(offset, kMipsDoublewordSize);
3161 LoadConst32(AT, loaded_offset);
3162 Addu(AT, AT, base);
3163 offset -= loaded_offset;
3164 }
3165 }
3166 base = AT;
3167
3168 CHECK(IsInt<16>(offset));
3169 if (two_accesses) {
3170 CHECK(IsInt<16>(static_cast<int32_t>(offset + kMipsWordSize)));
3171 }
3172 CHECK_EQ(misalignment, offset & (kMipsDoublewordSize - 1));
3173}
3174
Alexey Frunze2923db72016-08-20 01:55:47 -07003175void MipsAssembler::LoadFromOffset(LoadOperandType type,
3176 Register reg,
3177 Register base,
Alexey Frunzecad3a4c2016-06-07 23:40:37 -07003178 int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003179 LoadFromOffset<>(type, reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003180}
3181
3182void MipsAssembler::LoadSFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003183 LoadSFromOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003184}
3185
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003186void MipsAssembler::LoadDFromOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003187 LoadDFromOffset<>(reg, base, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003188}
3189
3190void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset,
3191 size_t size) {
3192 MipsManagedRegister dst = m_dst.AsMips();
3193 if (dst.IsNoRegister()) {
3194 CHECK_EQ(0u, size) << dst;
3195 } else if (dst.IsCoreRegister()) {
3196 CHECK_EQ(kMipsWordSize, size) << dst;
3197 LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
3198 } else if (dst.IsRegisterPair()) {
3199 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3200 LoadFromOffset(kLoadDoubleword, dst.AsRegisterPairLow(), src_register, src_offset);
3201 } else if (dst.IsFRegister()) {
3202 if (size == kMipsWordSize) {
3203 LoadSFromOffset(dst.AsFRegister(), src_register, src_offset);
3204 } else {
3205 CHECK_EQ(kMipsDoublewordSize, size) << dst;
3206 LoadDFromOffset(dst.AsFRegister(), src_register, src_offset);
3207 }
3208 }
jeffhao7fbee072012-08-24 17:56:54 -07003209}
3210
Alexey Frunze2923db72016-08-20 01:55:47 -07003211void MipsAssembler::StoreToOffset(StoreOperandType type,
3212 Register reg,
3213 Register base,
jeffhao7fbee072012-08-24 17:56:54 -07003214 int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003215 StoreToOffset<>(type, reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003216}
3217
Goran Jakovljevicff734982015-08-24 12:58:55 +00003218void MipsAssembler::StoreSToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003219 StoreSToOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003220}
3221
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003222void MipsAssembler::StoreDToOffset(FRegister reg, Register base, int32_t offset) {
Alexey Frunze2923db72016-08-20 01:55:47 -07003223 StoreDToOffset<>(reg, base, offset);
jeffhao7fbee072012-08-24 17:56:54 -07003224}
3225
David Srbeckydd973932015-04-07 20:29:48 +01003226static dwarf::Reg DWARFReg(Register reg) {
3227 return dwarf::Reg::MipsCore(static_cast<int>(reg));
3228}
3229
Ian Rogers790a6b72014-04-01 10:36:00 -07003230constexpr size_t kFramePointerSize = 4;
3231
Vladimir Marko32248382016-05-19 10:37:24 +01003232void MipsAssembler::BuildFrame(size_t frame_size,
3233 ManagedRegister method_reg,
3234 ArrayRef<const ManagedRegister> callee_save_regs,
Dmitry Petrochenkofca82202014-03-21 11:21:37 +07003235 const ManagedRegisterEntrySpills& entry_spills) {
jeffhao7fbee072012-08-24 17:56:54 -07003236 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003237 DCHECK(!overwriting_);
jeffhao7fbee072012-08-24 17:56:54 -07003238
3239 // Increase frame to required size.
3240 IncreaseFrameSize(frame_size);
3241
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003242 // Push callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07003243 int stack_offset = frame_size - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003244 StoreToOffset(kStoreWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003245 cfi_.RelOffset(DWARFReg(RA), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07003246 for (int i = callee_save_regs.size() - 1; i >= 0; --i) {
Ian Rogers790a6b72014-04-01 10:36:00 -07003247 stack_offset -= kFramePointerSize;
Vladimir Marko32248382016-05-19 10:37:24 +01003248 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07003249 StoreToOffset(kStoreWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003250 cfi_.RelOffset(DWARFReg(reg), stack_offset);
jeffhao7fbee072012-08-24 17:56:54 -07003251 }
3252
3253 // Write out Method*.
3254 StoreToOffset(kStoreWord, method_reg.AsMips().AsCoreRegister(), SP, 0);
3255
3256 // Write out entry spills.
Goran Jakovljevicff734982015-08-24 12:58:55 +00003257 int32_t offset = frame_size + kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003258 for (size_t i = 0; i < entry_spills.size(); ++i) {
Goran Jakovljevicff734982015-08-24 12:58:55 +00003259 MipsManagedRegister reg = entry_spills.at(i).AsMips();
3260 if (reg.IsNoRegister()) {
3261 ManagedRegisterSpill spill = entry_spills.at(i);
3262 offset += spill.getSize();
3263 } else if (reg.IsCoreRegister()) {
3264 StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003265 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003266 } else if (reg.IsFRegister()) {
3267 StoreSToOffset(reg.AsFRegister(), SP, offset);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003268 offset += kMipsWordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003269 } else if (reg.IsDRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003270 StoreDToOffset(reg.AsOverlappingDRegisterLow(), SP, offset);
3271 offset += kMipsDoublewordSize;
Goran Jakovljevicff734982015-08-24 12:58:55 +00003272 }
jeffhao7fbee072012-08-24 17:56:54 -07003273 }
3274}
3275
3276void MipsAssembler::RemoveFrame(size_t frame_size,
Vladimir Marko32248382016-05-19 10:37:24 +01003277 ArrayRef<const ManagedRegister> callee_save_regs) {
jeffhao7fbee072012-08-24 17:56:54 -07003278 CHECK_ALIGNED(frame_size, kStackAlignment);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003279 DCHECK(!overwriting_);
David Srbeckydd973932015-04-07 20:29:48 +01003280 cfi_.RememberState();
jeffhao7fbee072012-08-24 17:56:54 -07003281
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003282 // Pop callee saves and return address.
Ian Rogers790a6b72014-04-01 10:36:00 -07003283 int stack_offset = frame_size - (callee_save_regs.size() * kFramePointerSize) - kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003284 for (size_t i = 0; i < callee_save_regs.size(); ++i) {
Vladimir Marko32248382016-05-19 10:37:24 +01003285 Register reg = callee_save_regs[i].AsMips().AsCoreRegister();
jeffhao7fbee072012-08-24 17:56:54 -07003286 LoadFromOffset(kLoadWord, reg, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003287 cfi_.Restore(DWARFReg(reg));
Ian Rogers790a6b72014-04-01 10:36:00 -07003288 stack_offset += kFramePointerSize;
jeffhao7fbee072012-08-24 17:56:54 -07003289 }
3290 LoadFromOffset(kLoadWord, RA, SP, stack_offset);
David Srbeckydd973932015-04-07 20:29:48 +01003291 cfi_.Restore(DWARFReg(RA));
jeffhao7fbee072012-08-24 17:56:54 -07003292
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003293 // Adjust the stack pointer in the delay slot if doing so doesn't break CFI.
3294 bool exchange = IsInt<16>(static_cast<int32_t>(frame_size));
3295 bool reordering = SetReorder(false);
3296 if (exchange) {
3297 // Jump to the return address.
3298 Jr(RA);
3299 // Decrease frame to required size.
3300 DecreaseFrameSize(frame_size); // Single instruction in delay slot.
3301 } else {
3302 // Decrease frame to required size.
3303 DecreaseFrameSize(frame_size);
3304 // Jump to the return address.
3305 Jr(RA);
3306 Nop(); // In delay slot.
3307 }
3308 SetReorder(reordering);
David Srbeckydd973932015-04-07 20:29:48 +01003309
3310 // The CFI should be restored for any code that follows the exit block.
3311 cfi_.RestoreState();
3312 cfi_.DefCFAOffset(frame_size);
jeffhao7fbee072012-08-24 17:56:54 -07003313}
3314
3315void MipsAssembler::IncreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003316 CHECK_ALIGNED(adjust, kFramePointerSize);
3317 Addiu32(SP, SP, -adjust);
David Srbeckydd973932015-04-07 20:29:48 +01003318 cfi_.AdjustCFAOffset(adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003319 if (overwriting_) {
3320 cfi_.OverrideDelayedPC(overwrite_location_);
3321 }
jeffhao7fbee072012-08-24 17:56:54 -07003322}
3323
3324void MipsAssembler::DecreaseFrameSize(size_t adjust) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003325 CHECK_ALIGNED(adjust, kFramePointerSize);
3326 Addiu32(SP, SP, adjust);
David Srbeckydd973932015-04-07 20:29:48 +01003327 cfi_.AdjustCFAOffset(-adjust);
Vladimir Marko10ef6942015-10-22 15:25:54 +01003328 if (overwriting_) {
3329 cfi_.OverrideDelayedPC(overwrite_location_);
3330 }
jeffhao7fbee072012-08-24 17:56:54 -07003331}
3332
3333void MipsAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
3334 MipsManagedRegister src = msrc.AsMips();
3335 if (src.IsNoRegister()) {
3336 CHECK_EQ(0u, size);
3337 } else if (src.IsCoreRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003338 CHECK_EQ(kMipsWordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07003339 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3340 } else if (src.IsRegisterPair()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003341 CHECK_EQ(kMipsDoublewordSize, size);
jeffhao7fbee072012-08-24 17:56:54 -07003342 StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
3343 StoreToOffset(kStoreWord, src.AsRegisterPairHigh(),
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003344 SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003345 } else if (src.IsFRegister()) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003346 if (size == kMipsWordSize) {
3347 StoreSToOffset(src.AsFRegister(), SP, dest.Int32Value());
3348 } else {
3349 CHECK_EQ(kMipsDoublewordSize, size);
3350 StoreDToOffset(src.AsFRegister(), SP, dest.Int32Value());
3351 }
jeffhao7fbee072012-08-24 17:56:54 -07003352 }
3353}
3354
3355void MipsAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
3356 MipsManagedRegister src = msrc.AsMips();
3357 CHECK(src.IsCoreRegister());
3358 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3359}
3360
3361void MipsAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
3362 MipsManagedRegister src = msrc.AsMips();
3363 CHECK(src.IsCoreRegister());
3364 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3365}
3366
3367void MipsAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
3368 ManagedRegister mscratch) {
3369 MipsManagedRegister scratch = mscratch.AsMips();
3370 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003371 LoadConst32(scratch.AsCoreRegister(), imm);
jeffhao7fbee072012-08-24 17:56:54 -07003372 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
3373}
3374
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003375void MipsAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs,
3376 FrameOffset fr_offs,
jeffhao7fbee072012-08-24 17:56:54 -07003377 ManagedRegister mscratch) {
3378 MipsManagedRegister scratch = mscratch.AsMips();
3379 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003380 Addiu32(scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003381 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3382 S1, thr_offs.Int32Value());
3383}
3384
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003385void MipsAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003386 StoreToOffset(kStoreWord, SP, S1, thr_offs.Int32Value());
3387}
3388
3389void MipsAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
3390 FrameOffset in_off, ManagedRegister mscratch) {
3391 MipsManagedRegister src = msrc.AsMips();
3392 MipsManagedRegister scratch = mscratch.AsMips();
3393 StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
3394 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003395 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003396}
3397
3398void MipsAssembler::Load(ManagedRegister mdest, FrameOffset src, size_t size) {
3399 return EmitLoad(mdest, SP, src.Int32Value(), size);
3400}
3401
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003402void MipsAssembler::LoadFromThread(ManagedRegister mdest, ThreadOffset32 src, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003403 return EmitLoad(mdest, S1, src.Int32Value(), size);
3404}
3405
3406void MipsAssembler::LoadRef(ManagedRegister mdest, FrameOffset src) {
3407 MipsManagedRegister dest = mdest.AsMips();
3408 CHECK(dest.IsCoreRegister());
3409 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), SP, src.Int32Value());
3410}
3411
Mathieu Chartiere401d142015-04-22 13:56:20 -07003412void MipsAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base, MemberOffset offs,
Roland Levillain4d027112015-07-01 15:41:14 +01003413 bool unpoison_reference) {
jeffhao7fbee072012-08-24 17:56:54 -07003414 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003415 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07003416 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
3417 base.AsMips().AsCoreRegister(), offs.Int32Value());
Roland Levillain4d027112015-07-01 15:41:14 +01003418 if (kPoisonHeapReferences && unpoison_reference) {
Hiroshi Yamauchie63a7452014-02-27 14:44:36 -08003419 Subu(dest.AsCoreRegister(), ZERO, dest.AsCoreRegister());
3420 }
jeffhao7fbee072012-08-24 17:56:54 -07003421}
3422
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003423void MipsAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base, Offset offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003424 MipsManagedRegister dest = mdest.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003425 CHECK(dest.IsCoreRegister() && base.AsMips().IsCoreRegister());
jeffhao7fbee072012-08-24 17:56:54 -07003426 LoadFromOffset(kLoadWord, dest.AsCoreRegister(),
3427 base.AsMips().AsCoreRegister(), offs.Int32Value());
3428}
3429
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003430void MipsAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset32 offs) {
jeffhao7fbee072012-08-24 17:56:54 -07003431 MipsManagedRegister dest = mdest.AsMips();
3432 CHECK(dest.IsCoreRegister());
3433 LoadFromOffset(kLoadWord, dest.AsCoreRegister(), S1, offs.Int32Value());
3434}
3435
3436void MipsAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
3437 UNIMPLEMENTED(FATAL) << "no sign extension necessary for mips";
3438}
3439
3440void MipsAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
3441 UNIMPLEMENTED(FATAL) << "no zero extension necessary for mips";
3442}
3443
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003444void MipsAssembler::Move(ManagedRegister mdest, ManagedRegister msrc, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003445 MipsManagedRegister dest = mdest.AsMips();
3446 MipsManagedRegister src = msrc.AsMips();
3447 if (!dest.Equals(src)) {
3448 if (dest.IsCoreRegister()) {
3449 CHECK(src.IsCoreRegister()) << src;
3450 Move(dest.AsCoreRegister(), src.AsCoreRegister());
3451 } else if (dest.IsFRegister()) {
3452 CHECK(src.IsFRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003453 if (size == kMipsWordSize) {
3454 MovS(dest.AsFRegister(), src.AsFRegister());
3455 } else {
3456 CHECK_EQ(kMipsDoublewordSize, size);
3457 MovD(dest.AsFRegister(), src.AsFRegister());
3458 }
jeffhao7fbee072012-08-24 17:56:54 -07003459 } else if (dest.IsDRegister()) {
3460 CHECK(src.IsDRegister()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003461 MovD(dest.AsOverlappingDRegisterLow(), src.AsOverlappingDRegisterLow());
jeffhao7fbee072012-08-24 17:56:54 -07003462 } else {
3463 CHECK(dest.IsRegisterPair()) << dest;
3464 CHECK(src.IsRegisterPair()) << src;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003465 // Ensure that the first move doesn't clobber the input of the second.
jeffhao7fbee072012-08-24 17:56:54 -07003466 if (src.AsRegisterPairHigh() != dest.AsRegisterPairLow()) {
3467 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
3468 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
3469 } else {
3470 Move(dest.AsRegisterPairHigh(), src.AsRegisterPairHigh());
3471 Move(dest.AsRegisterPairLow(), src.AsRegisterPairLow());
3472 }
3473 }
3474 }
3475}
3476
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003477void MipsAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003478 MipsManagedRegister scratch = mscratch.AsMips();
3479 CHECK(scratch.IsCoreRegister()) << scratch;
3480 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3481 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
3482}
3483
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003484void MipsAssembler::CopyRawPtrFromThread(FrameOffset fr_offs,
3485 ThreadOffset32 thr_offs,
3486 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003487 MipsManagedRegister scratch = mscratch.AsMips();
3488 CHECK(scratch.IsCoreRegister()) << scratch;
3489 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3490 S1, thr_offs.Int32Value());
3491 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3492 SP, fr_offs.Int32Value());
3493}
3494
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003495void MipsAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs,
3496 FrameOffset fr_offs,
3497 ManagedRegister mscratch) {
jeffhao7fbee072012-08-24 17:56:54 -07003498 MipsManagedRegister scratch = mscratch.AsMips();
3499 CHECK(scratch.IsCoreRegister()) << scratch;
3500 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3501 SP, fr_offs.Int32Value());
3502 StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
3503 S1, thr_offs.Int32Value());
3504}
3505
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003506void MipsAssembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) {
jeffhao7fbee072012-08-24 17:56:54 -07003507 MipsManagedRegister scratch = mscratch.AsMips();
3508 CHECK(scratch.IsCoreRegister()) << scratch;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003509 CHECK(size == kMipsWordSize || size == kMipsDoublewordSize) << size;
3510 if (size == kMipsWordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07003511 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3512 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003513 } else if (size == kMipsDoublewordSize) {
jeffhao7fbee072012-08-24 17:56:54 -07003514 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
3515 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003516 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + kMipsWordSize);
3517 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003518 }
3519}
3520
3521void MipsAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
3522 ManagedRegister mscratch, size_t size) {
3523 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003524 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003525 LoadFromOffset(kLoadWord, scratch, src_base.AsMips().AsCoreRegister(), src_offset.Int32Value());
3526 StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
3527}
3528
3529void MipsAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
3530 ManagedRegister mscratch, size_t size) {
3531 Register scratch = mscratch.AsMips().AsCoreRegister();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003532 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003533 LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
3534 StoreToOffset(kStoreWord, scratch, dest_base.AsMips().AsCoreRegister(), dest_offset.Int32Value());
3535}
3536
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003537void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
3538 FrameOffset src_base ATTRIBUTE_UNUSED,
3539 Offset src_offset ATTRIBUTE_UNUSED,
3540 ManagedRegister mscratch ATTRIBUTE_UNUSED,
3541 size_t size ATTRIBUTE_UNUSED) {
3542 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003543}
3544
3545void MipsAssembler::Copy(ManagedRegister dest, Offset dest_offset,
3546 ManagedRegister src, Offset src_offset,
3547 ManagedRegister mscratch, size_t size) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003548 CHECK_EQ(size, kMipsWordSize);
jeffhao7fbee072012-08-24 17:56:54 -07003549 Register scratch = mscratch.AsMips().AsCoreRegister();
3550 LoadFromOffset(kLoadWord, scratch, src.AsMips().AsCoreRegister(), src_offset.Int32Value());
3551 StoreToOffset(kStoreWord, scratch, dest.AsMips().AsCoreRegister(), dest_offset.Int32Value());
3552}
3553
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003554void MipsAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
3555 Offset dest_offset ATTRIBUTE_UNUSED,
3556 FrameOffset src ATTRIBUTE_UNUSED,
3557 Offset src_offset ATTRIBUTE_UNUSED,
3558 ManagedRegister mscratch ATTRIBUTE_UNUSED,
3559 size_t size ATTRIBUTE_UNUSED) {
3560 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003561}
3562
3563void MipsAssembler::MemoryBarrier(ManagedRegister) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003564 // TODO: sync?
3565 UNIMPLEMENTED(FATAL) << "no MIPS implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003566}
3567
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003568void MipsAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003569 FrameOffset handle_scope_offset,
3570 ManagedRegister min_reg,
3571 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003572 MipsManagedRegister out_reg = mout_reg.AsMips();
3573 MipsManagedRegister in_reg = min_reg.AsMips();
3574 CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
3575 CHECK(out_reg.IsCoreRegister()) << out_reg;
3576 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003577 MipsLabel null_arg;
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003578 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3579 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003580 // E.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset).
jeffhao7fbee072012-08-24 17:56:54 -07003581 if (in_reg.IsNoRegister()) {
3582 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003583 SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003584 in_reg = out_reg;
3585 }
3586 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003587 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003588 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003589 Beqz(in_reg.AsCoreRegister(), &null_arg);
3590 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3591 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003592 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003593 Addiu32(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003594 }
3595}
3596
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003597void MipsAssembler::CreateHandleScopeEntry(FrameOffset out_off,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003598 FrameOffset handle_scope_offset,
3599 ManagedRegister mscratch,
3600 bool null_allowed) {
jeffhao7fbee072012-08-24 17:56:54 -07003601 MipsManagedRegister scratch = mscratch.AsMips();
3602 CHECK(scratch.IsCoreRegister()) << scratch;
3603 if (null_allowed) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003604 MipsLabel null_arg;
3605 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003606 // Null values get a handle scope entry value of 0. Otherwise, the handle scope entry is
3607 // the address in the handle scope holding the reference.
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003608 // E.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset).
3609 Beqz(scratch.AsCoreRegister(), &null_arg);
3610 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
3611 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003612 } else {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003613 Addiu32(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003614 }
3615 StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
3616}
3617
Mathieu Chartiereb8167a2014-05-07 15:43:14 -07003618// Given a handle scope entry, load the associated reference.
3619void MipsAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003620 ManagedRegister min_reg) {
jeffhao7fbee072012-08-24 17:56:54 -07003621 MipsManagedRegister out_reg = mout_reg.AsMips();
3622 MipsManagedRegister in_reg = min_reg.AsMips();
3623 CHECK(out_reg.IsCoreRegister()) << out_reg;
3624 CHECK(in_reg.IsCoreRegister()) << in_reg;
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003625 MipsLabel null_arg;
jeffhao7fbee072012-08-24 17:56:54 -07003626 if (!out_reg.Equals(in_reg)) {
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003627 LoadConst32(out_reg.AsCoreRegister(), 0);
jeffhao7fbee072012-08-24 17:56:54 -07003628 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003629 Beqz(in_reg.AsCoreRegister(), &null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003630 LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
3631 in_reg.AsCoreRegister(), 0);
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003632 Bind(&null_arg);
jeffhao7fbee072012-08-24 17:56:54 -07003633}
3634
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003635void MipsAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,
3636 bool could_be_null ATTRIBUTE_UNUSED) {
3637 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003638}
3639
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003640void MipsAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED,
3641 bool could_be_null ATTRIBUTE_UNUSED) {
3642 // TODO: not validating references.
jeffhao7fbee072012-08-24 17:56:54 -07003643}
3644
3645void MipsAssembler::Call(ManagedRegister mbase, Offset offset, ManagedRegister mscratch) {
3646 MipsManagedRegister base = mbase.AsMips();
3647 MipsManagedRegister scratch = mscratch.AsMips();
3648 CHECK(base.IsCoreRegister()) << base;
3649 CHECK(scratch.IsCoreRegister()) << scratch;
3650 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3651 base.AsCoreRegister(), offset.Int32Value());
3652 Jalr(scratch.AsCoreRegister());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003653 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003654 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003655}
3656
3657void MipsAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
3658 MipsManagedRegister scratch = mscratch.AsMips();
3659 CHECK(scratch.IsCoreRegister()) << scratch;
3660 // Call *(*(SP + base) + offset)
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003661 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, base.Int32Value());
jeffhao7fbee072012-08-24 17:56:54 -07003662 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
3663 scratch.AsCoreRegister(), offset.Int32Value());
3664 Jalr(scratch.AsCoreRegister());
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003665 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003666 // TODO: place reference map on call.
jeffhao7fbee072012-08-24 17:56:54 -07003667}
3668
Andreas Gampe3b165bc2016-08-01 22:07:04 -07003669void MipsAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UNUSED,
3670 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
Ian Rogers468532e2013-08-05 10:56:33 -07003671 UNIMPLEMENTED(FATAL) << "no mips implementation";
jeffhao7fbee072012-08-24 17:56:54 -07003672}
3673
3674void MipsAssembler::GetCurrentThread(ManagedRegister tr) {
3675 Move(tr.AsMips().AsCoreRegister(), S1);
3676}
3677
3678void MipsAssembler::GetCurrentThread(FrameOffset offset,
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003679 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
jeffhao7fbee072012-08-24 17:56:54 -07003680 StoreToOffset(kStoreWord, S1, SP, offset.Int32Value());
3681}
3682
jeffhao7fbee072012-08-24 17:56:54 -07003683void MipsAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
3684 MipsManagedRegister scratch = mscratch.AsMips();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003685 exception_blocks_.emplace_back(scratch, stack_adjust);
jeffhao7fbee072012-08-24 17:56:54 -07003686 LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
Andreas Gampe542451c2016-07-26 09:02:02 -07003687 S1, Thread::ExceptionOffset<kMipsPointerSize>().Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003688 Bnez(scratch.AsCoreRegister(), exception_blocks_.back().Entry());
jeffhao7fbee072012-08-24 17:56:54 -07003689}
3690
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003691void MipsAssembler::EmitExceptionPoll(MipsExceptionSlowPath* exception) {
3692 Bind(exception->Entry());
3693 if (exception->stack_adjust_ != 0) { // Fix up the frame.
3694 DecreaseFrameSize(exception->stack_adjust_);
jeffhao7fbee072012-08-24 17:56:54 -07003695 }
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003696 // Pass exception object as argument.
3697 // Don't care about preserving A0 as this call won't return.
3698 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
3699 Move(A0, exception->scratch_.AsCoreRegister());
3700 // Set up call to Thread::Current()->pDeliverException.
3701 LoadFromOffset(kLoadWord, T9, S1,
Andreas Gampe542451c2016-07-26 09:02:02 -07003702 QUICK_ENTRYPOINT_OFFSET(kMipsPointerSize, pDeliverException).Int32Value());
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003703 Jr(T9);
Alexey Frunze57eb0f52016-07-29 22:04:46 -07003704 NopIfNoReordering();
Goran Jakovljevic8c434dc2015-08-26 14:39:44 +02003705
3706 // Call never returns.
3707 Break();
jeffhao7fbee072012-08-24 17:56:54 -07003708}
3709
3710} // namespace mips
3711} // namespace art