1 /*
2  * Copyright (C) 2012 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 /* This file contains codegen for the Mips ISA */
18 
19 #include "codegen_mips.h"
20 
21 #include "art_method.h"
22 #include "base/logging.h"
23 #include "dex/mir_graph.h"
24 #include "dex/quick/dex_file_to_method_inliner_map.h"
25 #include "dex/quick/mir_to_lir-inl.h"
26 #include "driver/compiler_driver.h"
27 #include "driver/compiler_options.h"
28 #include "entrypoints/quick/quick_entrypoints.h"
29 #include "gc/accounting/card_table.h"
30 #include "mips_lir.h"
31 #include "mirror/object_array-inl.h"
32 
33 namespace art {
34 
GenSpecialCase(BasicBlock * bb,MIR * mir,const InlineMethod & special)35 bool MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special) {
36   // TODO
37   UNUSED(bb, mir, special);
38   return false;
39 }
40 
41 /*
42  * The lack of pc-relative loads on Mips presents somewhat of a challenge
43  * for our PIC switch table strategy.  To materialize the current location
44  * we'll do a dummy JAL and reference our tables using rRA as the
45  * base register.  Note that rRA will be used both as the base to
46  * locate the switch table data and as the reference base for the switch
47  * target offsets stored in the table.  We'll use a special pseudo-instruction
48  * to represent the jal and trigger the construction of the
49  * switch table offsets (which will happen after final assembly and all
50  * labels are fixed).
51  *
52  * The test loop will look something like:
53  *
54  *   ori   r_end, rZERO, #table_size  ; size in bytes
55  *   jal   BaseLabel         ; stores "return address" (BaseLabel) in rRA
56  *   nop                     ; opportunistically fill
57  * BaseLabel:
58  *   addiu r_base, rRA, <table> - <BaseLabel>    ; table relative to BaseLabel
59      addu  r_end, r_end, r_base                   ; end of table
60  *   lw    r_val, [rSP, v_reg_off]                ; Test Value
61  * loop:
62  *   beq   r_base, r_end, done
63  *   lw    r_key, 0(r_base)
64  *   addu  r_base, 8
65  *   bne   r_val, r_key, loop
66  *   lw    r_disp, -4(r_base)
67  *   addu  rRA, r_disp
68  *   jalr  rZERO, rRA
69  * done:
70  *
71  */
GenLargeSparseSwitch(MIR * mir,DexOffset table_offset,RegLocation rl_src)72 void MipsMir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
73   const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
74   // Add the table to the list - we'll process it later.
75   SwitchTable* tab_rec =
76       static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
77   tab_rec->switch_mir = mir;
78   tab_rec->table = table;
79   tab_rec->vaddr = current_dalvik_offset_;
80   int elements = table[1];
81   switch_tables_.push_back(tab_rec);
82 
83   // The table is composed of 8-byte key/disp pairs.
84   int byte_size = elements * 8;
85 
86   int size_hi = byte_size >> 16;
87   int size_lo = byte_size & 0xffff;
88 
89   RegStorage r_end = AllocPtrSizeTemp();
90   if (size_hi) {
91     NewLIR2(kMipsLui, r_end.GetReg(), size_hi);
92   }
93   // Must prevent code motion for the curr pc pair.
94   GenBarrier();  // Scheduling barrier
95   NewLIR0(kMipsCurrPC);  // Really a jal to .+8.
96   // Now, fill the branch delay slot.
97   if (size_hi) {
98     NewLIR3(kMipsOri, r_end.GetReg(), r_end.GetReg(), size_lo);
99   } else {
100     NewLIR3(kMipsOri, r_end.GetReg(), rZERO, size_lo);
101   }
102   GenBarrier();  // Scheduling barrier.
103 
104   // Construct BaseLabel and set up table base register.
105   LIR* base_label = NewLIR0(kPseudoTargetLabel);
106   // Remember base label so offsets can be computed later.
107   tab_rec->anchor = base_label;
108   RegStorage r_base = AllocPtrSizeTemp();
109   NewLIR4(kMipsDelta, r_base.GetReg(), 0, WrapPointer(base_label), WrapPointer(tab_rec));
110   OpRegRegReg(kOpAdd, r_end, r_end, r_base);
111 
112   // Grab switch test value.
113   rl_src = LoadValue(rl_src, kCoreReg);
114 
115   // Test loop.
116   RegStorage r_key = AllocTemp();
117   LIR* loop_label = NewLIR0(kPseudoTargetLabel);
118   LIR* exit_branch = OpCmpBranch(kCondEq, r_base, r_end, nullptr);
119   Load32Disp(r_base, 0, r_key);
120   OpRegImm(kOpAdd, r_base, 8);
121   OpCmpBranch(kCondNe, rl_src.reg, r_key, loop_label);
122   RegStorage r_disp = AllocTemp();
123   Load32Disp(r_base, -4, r_disp);
124   const RegStorage rs_ra = TargetPtrReg(kLr);
125   OpRegRegReg(kOpAdd, rs_ra, rs_ra, r_disp);
126   OpReg(kOpBx, rs_ra);
127   // Loop exit.
128   LIR* exit_label = NewLIR0(kPseudoTargetLabel);
129   exit_branch->target = exit_label;
130 }
131 
132 /*
133  * Code pattern will look something like:
134  *
135  *   lw    r_val
136  *   jal   BaseLabel         ; stores "return address" (BaseLabel) in rRA
137  *   nop                     ; opportunistically fill
138  *   [subiu r_val, bias]      ; Remove bias if low_val != 0
139  *   bound check -> done
140  *   lw    r_disp, [rRA, r_val]
141  *   addu  rRA, r_disp
142  *   jalr  rZERO, rRA
143  * done:
144  */
GenLargePackedSwitch(MIR * mir,DexOffset table_offset,RegLocation rl_src)145 void MipsMir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
146   const uint16_t* table = mir_graph_->GetTable(mir, table_offset);
147   // Add the table to the list - we'll process it later.
148   SwitchTable* tab_rec =
149       static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
150   tab_rec->switch_mir = mir;
151   tab_rec->table = table;
152   tab_rec->vaddr = current_dalvik_offset_;
153   int size = table[1];
154   switch_tables_.push_back(tab_rec);
155 
156   // Get the switch value.
157   rl_src = LoadValue(rl_src, kCoreReg);
158 
159   // Prepare the bias.  If too big, handle 1st stage here.
160   int low_key = s4FromSwitchData(&table[2]);
161   bool large_bias = false;
162   RegStorage r_key;
163   if (low_key == 0) {
164     r_key = rl_src.reg;
165   } else if ((low_key & 0xffff) != low_key) {
166     r_key = AllocTemp();
167     LoadConstant(r_key, low_key);
168     large_bias = true;
169   } else {
170     r_key = AllocTemp();
171   }
172 
173   // Must prevent code motion for the curr pc pair.
174   GenBarrier();
175   NewLIR0(kMipsCurrPC);  // Really a jal to .+8.
176   // Now, fill the branch delay slot with bias strip.
177   if (low_key == 0) {
178     NewLIR0(kMipsNop);
179   } else {
180     if (large_bias) {
181       OpRegRegReg(kOpSub, r_key, rl_src.reg, r_key);
182     } else {
183       OpRegRegImm(kOpSub, r_key, rl_src.reg, low_key);
184     }
185   }
186   GenBarrier();  // Scheduling barrier.
187 
188   // Construct BaseLabel and set up table base register.
189   LIR* base_label = NewLIR0(kPseudoTargetLabel);
190   // Remember base label so offsets can be computed later.
191   tab_rec->anchor = base_label;
192 
193   // Bounds check - if < 0 or >= size continue following switch.
194   LIR* branch_over = OpCmpImmBranch(kCondHi, r_key, size-1, nullptr);
195 
196   // Materialize the table base pointer.
197   RegStorage r_base = AllocPtrSizeTemp();
198   NewLIR4(kMipsDelta, r_base.GetReg(), 0, WrapPointer(base_label), WrapPointer(tab_rec));
199 
200   // Load the displacement from the switch table.
201   RegStorage r_disp = AllocTemp();
202   LoadBaseIndexed(r_base, r_key, r_disp, 2, k32);
203 
204   // Add to rRA and go.
205   const RegStorage rs_ra = TargetPtrReg(kLr);
206   OpRegRegReg(kOpAdd, rs_ra, rs_ra, r_disp);
207   OpReg(kOpBx, rs_ra);
208 
209   // Branch_over target here.
210   LIR* target = NewLIR0(kPseudoTargetLabel);
211   branch_over->target = target;
212 }
213 
GenMoveException(RegLocation rl_dest)214 void MipsMir2Lir::GenMoveException(RegLocation rl_dest) {
215   int ex_offset = cu_->target64 ? Thread::ExceptionOffset<8>().Int32Value() :
216       Thread::ExceptionOffset<4>().Int32Value();
217   RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
218   RegStorage reset_reg = AllocTempRef();
219   LoadRefDisp(TargetPtrReg(kSelf), ex_offset, rl_result.reg, kNotVolatile);
220   LoadConstant(reset_reg, 0);
221   StoreRefDisp(TargetPtrReg(kSelf), ex_offset, reset_reg, kNotVolatile);
222   FreeTemp(reset_reg);
223   StoreValue(rl_dest, rl_result);
224 }
225 
UnconditionallyMarkGCCard(RegStorage tgt_addr_reg)226 void MipsMir2Lir::UnconditionallyMarkGCCard(RegStorage tgt_addr_reg) {
227   RegStorage reg_card_base = AllocPtrSizeTemp();
228   RegStorage reg_card_no = AllocPtrSizeTemp();
229   if (cu_->target64) {
230     // NOTE: native pointer.
231     LoadWordDisp(TargetPtrReg(kSelf), Thread::CardTableOffset<8>().Int32Value(), reg_card_base);
232     OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
233     StoreBaseIndexed(reg_card_base, reg_card_no, As32BitReg(reg_card_base), 0, kUnsignedByte);
234   } else {
235     // NOTE: native pointer.
236     LoadWordDisp(TargetPtrReg(kSelf), Thread::CardTableOffset<4>().Int32Value(), reg_card_base);
237     OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
238     StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte);
239   }
240   FreeTemp(reg_card_base);
241   FreeTemp(reg_card_no);
242 }
243 
DwarfCoreReg(int num)244 static dwarf::Reg DwarfCoreReg(int num) {
245   return dwarf::Reg::MipsCore(num);
246 }
247 
GenEntrySequence(RegLocation * ArgLocs,RegLocation rl_method)248 void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
249   DCHECK_EQ(cfi_.GetCurrentCFAOffset(), 0);
250   int spill_count = num_core_spills_ + num_fp_spills_;
251   /*
252    * On entry, A0, A1, A2 & A3 are live. On Mips64, A4, A5, A6 & A7 are also live.
253    * Let the register allocation mechanism know so it doesn't try to use any of them when
254    * expanding the frame or flushing.
255    */
256   const RegStorage arg0 = TargetReg(kArg0);
257   const RegStorage arg1 = TargetReg(kArg1);
258   const RegStorage arg2 = TargetReg(kArg2);
259   const RegStorage arg3 = TargetReg(kArg3);
260   const RegStorage arg4 = TargetReg(kArg4);
261   const RegStorage arg5 = TargetReg(kArg5);
262   const RegStorage arg6 = TargetReg(kArg6);
263   const RegStorage arg7 = TargetReg(kArg7);
264 
265   LockTemp(arg0);
266   LockTemp(arg1);
267   LockTemp(arg2);
268   LockTemp(arg3);
269   if (cu_->target64) {
270     LockTemp(arg4);
271     LockTemp(arg5);
272     LockTemp(arg6);
273     LockTemp(arg7);
274   }
275 
276   bool skip_overflow_check;
277   InstructionSet target = (cu_->target64) ? kMips64 : kMips;
278   int ptr_size = cu_->target64 ? 8 : 4;
279 
280   /*
281    * We can safely skip the stack overflow check if we're
282    * a leaf *and* our frame size < fudge factor.
283    */
284 
285   skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, target);
286   RegStorage check_reg = AllocPtrSizeTemp();
287   RegStorage new_sp = AllocPtrSizeTemp();
288   const RegStorage rs_sp = TargetPtrReg(kSp);
289   const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(target);
290   const bool large_frame = static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes;
291   bool generate_explicit_stack_overflow_check = large_frame ||
292     !cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks();
293 
294   if (!skip_overflow_check) {
295     if (generate_explicit_stack_overflow_check) {
296       // Load stack limit.
297       if (cu_->target64) {
298         LoadWordDisp(TargetPtrReg(kSelf), Thread::StackEndOffset<8>().Int32Value(), check_reg);
299       } else {
300         Load32Disp(TargetPtrReg(kSelf), Thread::StackEndOffset<4>().Int32Value(), check_reg);
301       }
302     } else {
303       // Implicit stack overflow check.
304       // Generate a load from [sp, #-overflowsize].  If this is in the stack
305       // redzone we will get a segmentation fault.
306       Load32Disp(rs_sp, -kStackOverflowReservedUsableBytes, rs_rZERO);
307       MarkPossibleStackOverflowException();
308     }
309   }
310   // Spill core callee saves.
311   SpillCoreRegs();
312   // NOTE: promotion of FP regs currently unsupported, thus no FP spill.
313   DCHECK_EQ(num_fp_spills_, 0);
314   const int frame_sub = frame_size_ - spill_count * ptr_size;
315   if (!skip_overflow_check && generate_explicit_stack_overflow_check) {
316     class StackOverflowSlowPath : public LIRSlowPath {
317      public:
318       StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace)
319           : LIRSlowPath(m2l, branch), sp_displace_(sp_displace) {
320       }
321       void Compile() OVERRIDE {
322         m2l_->ResetRegPool();
323         m2l_->ResetDefTracking();
324         GenerateTargetLabel(kPseudoThrowTarget);
325         // RA is offset 0 since we push in reverse order.
326         m2l_->LoadWordDisp(m2l_->TargetPtrReg(kSp), 0, m2l_->TargetPtrReg(kLr));
327         m2l_->OpRegImm(kOpAdd, m2l_->TargetPtrReg(kSp), sp_displace_);
328         m2l_->cfi().AdjustCFAOffset(-sp_displace_);
329         m2l_->ClobberCallerSave();
330         RegStorage r_tgt = m2l_->CallHelperSetup(kQuickThrowStackOverflow);  // Doesn't clobber LR.
331         m2l_->CallHelper(r_tgt, kQuickThrowStackOverflow, false /* MarkSafepointPC */,
332                          false /* UseLink */);
333         m2l_->cfi().AdjustCFAOffset(sp_displace_);
334       }
335 
336      private:
337       const size_t sp_displace_;
338     };
339     OpRegRegImm(kOpSub, new_sp, rs_sp, frame_sub);
340     LIR* branch = OpCmpBranch(kCondUlt, new_sp, check_reg, nullptr);
341     AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, spill_count * ptr_size));
342     // TODO: avoid copy for small frame sizes.
343     OpRegCopy(rs_sp, new_sp);  // Establish stack.
344     cfi_.AdjustCFAOffset(frame_sub);
345   } else {
346     // Here if skip_overflow_check or doing implicit stack overflow check.
347     // Just make room on the stack for the frame now.
348     OpRegImm(kOpSub, rs_sp, frame_sub);
349     cfi_.AdjustCFAOffset(frame_sub);
350   }
351 
352   FlushIns(ArgLocs, rl_method);
353 
354   FreeTemp(arg0);
355   FreeTemp(arg1);
356   FreeTemp(arg2);
357   FreeTemp(arg3);
358   if (cu_->target64) {
359     FreeTemp(arg4);
360     FreeTemp(arg5);
361     FreeTemp(arg6);
362     FreeTemp(arg7);
363   }
364 }
365 
GenExitSequence()366 void MipsMir2Lir::GenExitSequence() {
367   cfi_.RememberState();
368   /*
369    * In the exit path, rMIPS_RET0/rMIPS_RET1 are live - make sure they aren't
370    * allocated by the register utilities as temps.
371    */
372   LockTemp(TargetPtrReg(kRet0));
373   LockTemp(TargetPtrReg(kRet1));
374 
375   UnSpillCoreRegs();
376   OpReg(kOpBx, TargetPtrReg(kLr));
377   // The CFI should be restored for any code that follows the exit block.
378   cfi_.RestoreState();
379   cfi_.DefCFAOffset(frame_size_);
380 }
381 
GenSpecialExitSequence()382 void MipsMir2Lir::GenSpecialExitSequence() {
383   OpReg(kOpBx, TargetPtrReg(kLr));
384 }
385 
GenSpecialEntryForSuspend()386 void MipsMir2Lir::GenSpecialEntryForSuspend() {
387   // Keep 16-byte stack alignment - push A0, i.e. ArtMethod*, 2 filler words and RA for mips32,
388   // but A0 and RA for mips64.
389   core_spill_mask_ = (1u << TargetPtrReg(kLr).GetRegNum());
390   num_core_spills_ = 1u;
391   fp_spill_mask_ = 0u;
392   num_fp_spills_ = 0u;
393   frame_size_ = 16u;
394   core_vmap_table_.clear();
395   fp_vmap_table_.clear();
396   const RegStorage rs_sp = TargetPtrReg(kSp);
397   OpRegImm(kOpSub, rs_sp, frame_size_);
398   cfi_.AdjustCFAOffset(frame_size_);
399   StoreWordDisp(rs_sp, frame_size_ - (cu_->target64 ? 8 : 4), TargetPtrReg(kLr));
400   cfi_.RelOffset(DwarfCoreReg(rRA), frame_size_ - (cu_->target64 ? 8 : 4));
401   StoreWordDisp(rs_sp, 0, TargetPtrReg(kArg0));
402   // Do not generate CFI for scratch register A0.
403 }
404 
GenSpecialExitForSuspend()405 void MipsMir2Lir::GenSpecialExitForSuspend() {
406   // Pop the frame. Don't pop ArtMethod*, it's no longer needed.
407   const RegStorage rs_sp = TargetPtrReg(kSp);
408   LoadWordDisp(rs_sp, frame_size_ - (cu_->target64 ? 8 : 4), TargetPtrReg(kLr));
409   cfi_.Restore(DwarfCoreReg(rRA));
410   OpRegImm(kOpAdd, rs_sp, frame_size_);
411   cfi_.AdjustCFAOffset(-frame_size_);
412 }
413 
414 /*
415  * Bit of a hack here - in the absence of a real scheduling pass,
416  * emit the next instruction in static & direct invoke sequences.
417  */
NextSDCallInsn(CompilationUnit * cu,CallInfo * info,int state,const MethodReference & target_method,uint32_t,uintptr_t direct_code,uintptr_t direct_method,InvokeType type)418 static int NextSDCallInsn(CompilationUnit* cu, CallInfo* info, int state,
419                           const MethodReference& target_method, uint32_t, uintptr_t direct_code,
420                           uintptr_t direct_method, InvokeType type) {
421   Mir2Lir* cg = static_cast<Mir2Lir*>(cu->cg.get());
422   if (info->string_init_offset != 0) {
423     RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
424     switch (state) {
425     case 0: {  // Grab target method* from thread pointer
426       cg->LoadWordDisp(cg->TargetPtrReg(kSelf), info->string_init_offset, arg0_ref);
427       break;
428     }
429     case 1:  // Grab the code from the method*
430       if (direct_code == 0) {
431         int32_t offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
432             InstructionSetPointerSize(cu->instruction_set)).Int32Value();
433         cg->LoadWordDisp(arg0_ref, offset, cg->TargetPtrReg(kInvokeTgt));
434       }
435       break;
436     default:
437       return -1;
438     }
439   } else if (direct_code != 0 && direct_method != 0) {
440     switch (state) {
441       case 0:  // Get the current Method* [sets kArg0]
442         if (direct_code != static_cast<uintptr_t>(-1)) {
443           if (cu->target64) {
444             cg->LoadConstantWide(cg->TargetPtrReg(kInvokeTgt), direct_code);
445           } else {
446             cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
447           }
448         } else {
449           cg->LoadCodeAddress(target_method, type, kInvokeTgt);
450         }
451         if (direct_method != static_cast<uintptr_t>(-1)) {
452           if (cu->target64) {
453             cg->LoadConstantWide(cg->TargetReg(kArg0, kRef), direct_method);
454           } else {
455             cg->LoadConstant(cg->TargetReg(kArg0, kRef), direct_method);
456           }
457         } else {
458           cg->LoadMethodAddress(target_method, type, kArg0);
459         }
460         break;
461       default:
462         return -1;
463     }
464   } else {
465     RegStorage arg0_ref = cg->TargetReg(kArg0, kRef);
466     switch (state) {
467       case 0:  // Get the current Method* [sets kArg0]
468         // TUNING: we can save a reg copy if Method* has been promoted.
469         cg->LoadCurrMethodDirect(arg0_ref);
470         break;
471       case 1:  // Get method->dex_cache_resolved_methods_
472         cg->LoadRefDisp(arg0_ref,
473                         ArtMethod::DexCacheResolvedMethodsOffset().Int32Value(),
474                         arg0_ref,
475                         kNotVolatile);
476         // Set up direct code if known.
477         if (direct_code != 0) {
478           if (direct_code != static_cast<uintptr_t>(-1)) {
479             if (cu->target64) {
480               cg->LoadConstantWide(cg->TargetPtrReg(kInvokeTgt), direct_code);
481             } else {
482               cg->LoadConstant(cg->TargetPtrReg(kInvokeTgt), direct_code);
483             }
484           } else {
485             CHECK_LT(target_method.dex_method_index, target_method.dex_file->NumMethodIds());
486             cg->LoadCodeAddress(target_method, type, kInvokeTgt);
487           }
488         }
489         break;
490       case 2: {
491         // Grab target method*
492         CHECK_EQ(cu->dex_file, target_method.dex_file);
493         const size_t pointer_size = GetInstructionSetPointerSize(cu->instruction_set);
494         cg->LoadWordDisp(arg0_ref,
495                          mirror::Array::DataOffset(pointer_size).Uint32Value() +
496                          target_method.dex_method_index * pointer_size, arg0_ref);
497         break;
498       }
499       case 3:  // Grab the code from the method*
500         if (direct_code == 0) {
501           int32_t offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
502               InstructionSetPointerSize(cu->instruction_set)).Int32Value();
503           // Get the compiled code address [use *alt_from or kArg0, set kInvokeTgt]
504           cg->LoadWordDisp(arg0_ref, offset, cg->TargetPtrReg(kInvokeTgt));
505         }
506         break;
507       default:
508         return -1;
509     }
510   }
511   return state + 1;
512 }
513 
GetNextSDCallInsn()514 NextCallInsn MipsMir2Lir::GetNextSDCallInsn() {
515   return NextSDCallInsn;
516 }
517 
GenCallInsn(const MirMethodLoweringInfo & method_info ATTRIBUTE_UNUSED)518 LIR* MipsMir2Lir::GenCallInsn(const MirMethodLoweringInfo& method_info ATTRIBUTE_UNUSED) {
519   return OpReg(kOpBlx, TargetPtrReg(kInvokeTgt));
520 }
521 
522 }  // namespace art
523