call_mips.cc revision 834b394ee759ed31c5371d8093d7cd8cd90014a8
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#include "dex/quick/mir_to_lir-inl.h"
21#include "entrypoints/quick/quick_entrypoints.h"
22#include "mips_lir.h"
23
24namespace art {
25
26void MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
27                                 SpecialCaseHandler special_case) {
28    // TODO
29}
30
31/*
32 * The lack of pc-relative loads on Mips presents somewhat of a challenge
33 * for our PIC switch table strategy.  To materialize the current location
34 * we'll do a dummy JAL and reference our tables using r_RA as the
35 * base register.  Note that r_RA will be used both as the base to
36 * locate the switch table data and as the reference base for the switch
37 * target offsets stored in the table.  We'll use a special pseudo-instruction
38 * to represent the jal and trigger the construction of the
39 * switch table offsets (which will happen after final assembly and all
40 * labels are fixed).
41 *
42 * The test loop will look something like:
43 *
44 *   ori   rEnd, r_ZERO, #table_size  ; size in bytes
45 *   jal   BaseLabel         ; stores "return address" (BaseLabel) in r_RA
46 *   nop                     ; opportunistically fill
47 * BaseLabel:
48 *   addiu rBase, r_RA, <table> - <BaseLabel>  ; table relative to BaseLabel
49     addu  rEnd, rEnd, rBase                   ; end of table
50 *   lw    r_val, [rSP, v_reg_off]                ; Test Value
51 * loop:
52 *   beq   rBase, rEnd, done
53 *   lw    r_key, 0(rBase)
54 *   addu  rBase, 8
55 *   bne   r_val, r_key, loop
56 *   lw    r_disp, -4(rBase)
57 *   addu  r_RA, r_disp
58 *   jr    r_RA
59 * done:
60 *
61 */
62void MipsMir2Lir::GenSparseSwitch(MIR* mir, uint32_t table_offset,
63                                  RegLocation rl_src) {
64  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
65  if (cu_->verbose) {
66    DumpSparseSwitchTable(table);
67  }
68  // Add the table to the list - we'll process it later
69  SwitchTable *tab_rec =
70      static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true,
71                                               ArenaAllocator::kAllocData));
72  tab_rec->table = table;
73  tab_rec->vaddr = current_dalvik_offset_;
74  int elements = table[1];
75  tab_rec->targets =
76      static_cast<LIR**>(arena_->NewMem(elements * sizeof(LIR*), true, ArenaAllocator::kAllocLIR));
77  switch_tables_.Insert(tab_rec);
78
79  // The table is composed of 8-byte key/disp pairs
80  int byte_size = elements * 8;
81
82  int size_hi = byte_size >> 16;
83  int size_lo = byte_size & 0xffff;
84
85  int rEnd = AllocTemp();
86  if (size_hi) {
87    NewLIR2(kMipsLui, rEnd, size_hi);
88  }
89  // Must prevent code motion for the curr pc pair
90  GenBarrier();  // Scheduling barrier
91  NewLIR0(kMipsCurrPC);  // Really a jal to .+8
92  // Now, fill the branch delay slot
93  if (size_hi) {
94    NewLIR3(kMipsOri, rEnd, rEnd, size_lo);
95  } else {
96    NewLIR3(kMipsOri, rEnd, r_ZERO, size_lo);
97  }
98  GenBarrier();  // Scheduling barrier
99
100  // Construct BaseLabel and set up table base register
101  LIR* base_label = NewLIR0(kPseudoTargetLabel);
102  // Remember base label so offsets can be computed later
103  tab_rec->anchor = base_label;
104  int rBase = AllocTemp();
105  NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(base_label),
106          reinterpret_cast<uintptr_t>(tab_rec));
107  OpRegRegReg(kOpAdd, rEnd, rEnd, rBase);
108
109  // Grab switch test value
110  rl_src = LoadValue(rl_src, kCoreReg);
111
112  // Test loop
113  int r_key = AllocTemp();
114  LIR* loop_label = NewLIR0(kPseudoTargetLabel);
115  LIR* exit_branch = OpCmpBranch(kCondEq, rBase, rEnd, NULL);
116  LoadWordDisp(rBase, 0, r_key);
117  OpRegImm(kOpAdd, rBase, 8);
118  OpCmpBranch(kCondNe, rl_src.low_reg, r_key, loop_label);
119  int r_disp = AllocTemp();
120  LoadWordDisp(rBase, -4, r_disp);
121  OpRegRegReg(kOpAdd, r_RA, r_RA, r_disp);
122  OpReg(kOpBx, r_RA);
123
124  // Loop exit
125  LIR* exit_label = NewLIR0(kPseudoTargetLabel);
126  exit_branch->target = exit_label;
127}
128
129/*
130 * Code pattern will look something like:
131 *
132 *   lw    r_val
133 *   jal   BaseLabel         ; stores "return address" (BaseLabel) in r_RA
134 *   nop                     ; opportunistically fill
135 *   [subiu r_val, bias]      ; Remove bias if low_val != 0
136 *   bound check -> done
137 *   lw    r_disp, [r_RA, r_val]
138 *   addu  r_RA, r_disp
139 *   jr    r_RA
140 * done:
141 */
142void MipsMir2Lir::GenPackedSwitch(MIR* mir, uint32_t table_offset,
143                                  RegLocation rl_src) {
144  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
145  if (cu_->verbose) {
146    DumpPackedSwitchTable(table);
147  }
148  // Add the table to the list - we'll process it later
149  SwitchTable *tab_rec =
150      static_cast<SwitchTable*>(arena_->NewMem(sizeof(SwitchTable), true,
151                                               ArenaAllocator::kAllocData));
152  tab_rec->table = table;
153  tab_rec->vaddr = current_dalvik_offset_;
154  int size = table[1];
155  tab_rec->targets = static_cast<LIR**>(arena_->NewMem(size * sizeof(LIR*), true,
156                                                       ArenaAllocator::kAllocLIR));
157  switch_tables_.Insert(tab_rec);
158
159  // Get the switch value
160  rl_src = LoadValue(rl_src, kCoreReg);
161
162  // Prepare the bias.  If too big, handle 1st stage here
163  int low_key = s4FromSwitchData(&table[2]);
164  bool large_bias = false;
165  int r_key;
166  if (low_key == 0) {
167    r_key = rl_src.low_reg;
168  } else if ((low_key & 0xffff) != low_key) {
169    r_key = AllocTemp();
170    LoadConstant(r_key, low_key);
171    large_bias = true;
172  } else {
173    r_key = AllocTemp();
174  }
175
176  // Must prevent code motion for the curr pc pair
177  GenBarrier();
178  NewLIR0(kMipsCurrPC);  // Really a jal to .+8
179  // Now, fill the branch delay slot with bias strip
180  if (low_key == 0) {
181    NewLIR0(kMipsNop);
182  } else {
183    if (large_bias) {
184      OpRegRegReg(kOpSub, r_key, rl_src.low_reg, r_key);
185    } else {
186      OpRegRegImm(kOpSub, r_key, rl_src.low_reg, low_key);
187    }
188  }
189  GenBarrier();  // Scheduling barrier
190
191  // Construct BaseLabel and set up table base register
192  LIR* base_label = NewLIR0(kPseudoTargetLabel);
193  // Remember base label so offsets can be computed later
194  tab_rec->anchor = base_label;
195
196  // Bounds check - if < 0 or >= size continue following switch
197  LIR* branch_over = OpCmpImmBranch(kCondHi, r_key, size-1, NULL);
198
199  // Materialize the table base pointer
200  int rBase = AllocTemp();
201  NewLIR4(kMipsDelta, rBase, 0, reinterpret_cast<uintptr_t>(base_label),
202          reinterpret_cast<uintptr_t>(tab_rec));
203
204  // Load the displacement from the switch table
205  int r_disp = AllocTemp();
206  LoadBaseIndexed(rBase, r_key, r_disp, 2, kWord);
207
208  // Add to r_AP and go
209  OpRegRegReg(kOpAdd, r_RA, r_RA, r_disp);
210  OpReg(kOpBx, r_RA);
211
212  /* branch_over target here */
213  LIR* target = NewLIR0(kPseudoTargetLabel);
214  branch_over->target = target;
215}
216
217/*
218 * Array data table format:
219 *  ushort ident = 0x0300   magic value
220 *  ushort width            width of each element in the table
221 *  uint   size             number of elements in the table
222 *  ubyte  data[size*width] table of data values (may contain a single-byte
223 *                          padding at the end)
224 *
225 * Total size is 4+(width * size + 1)/2 16-bit code units.
226 */
227void MipsMir2Lir::GenFillArrayData(uint32_t table_offset, RegLocation rl_src) {
228  const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
229  // Add the table to the list - we'll process it later
230  FillArrayData *tab_rec =
231      reinterpret_cast<FillArrayData*>(arena_->NewMem(sizeof(FillArrayData), true,
232                                                      ArenaAllocator::kAllocData));
233  tab_rec->table = table;
234  tab_rec->vaddr = current_dalvik_offset_;
235  uint16_t width = tab_rec->table[1];
236  uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
237  tab_rec->size = (size * width) + 8;
238
239  fill_array_data_.Insert(tab_rec);
240
241  // Making a call - use explicit registers
242  FlushAllRegs();   /* Everything to home location */
243  LockCallTemps();
244  LoadValueDirectFixed(rl_src, rMIPS_ARG0);
245
246  // Must prevent code motion for the curr pc pair
247  GenBarrier();
248  NewLIR0(kMipsCurrPC);  // Really a jal to .+8
249  // Now, fill the branch delay slot with the helper load
250  int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pHandleFillArrayDataFromCode));
251  GenBarrier();  // Scheduling barrier
252
253  // Construct BaseLabel and set up table base register
254  LIR* base_label = NewLIR0(kPseudoTargetLabel);
255
256  // Materialize a pointer to the fill data image
257  NewLIR4(kMipsDelta, rMIPS_ARG1, 0, reinterpret_cast<uintptr_t>(base_label),
258          reinterpret_cast<uintptr_t>(tab_rec));
259
260  // And go...
261  ClobberCalleeSave();
262  LIR* call_inst = OpReg(kOpBlx, r_tgt);  // ( array*, fill_data* )
263  MarkSafepointPC(call_inst);
264}
265
266/*
267 * TODO: implement fast path to short-circuit thin-lock case
268 */
269void MipsMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
270  FlushAllRegs();
271  LoadValueDirectFixed(rl_src, rMIPS_ARG0);  // Get obj
272  LockCallTemps();  // Prepare for explicit register usage
273  GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags);
274  // Go expensive route - artLockObjectFromCode(self, obj);
275  int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pLockObjectFromCode));
276  ClobberCalleeSave();
277  LIR* call_inst = OpReg(kOpBlx, r_tgt);
278  MarkSafepointPC(call_inst);
279}
280
281/*
282 * TODO: implement fast path to short-circuit thin-lock case
283 */
284void MipsMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
285  FlushAllRegs();
286  LoadValueDirectFixed(rl_src, rMIPS_ARG0);  // Get obj
287  LockCallTemps();  // Prepare for explicit register usage
288  GenNullCheck(rl_src.s_reg_low, rMIPS_ARG0, opt_flags);
289  // Go expensive route - UnlockObjectFromCode(obj);
290  int r_tgt = LoadHelper(QUICK_ENTRYPOINT_OFFSET(pUnlockObjectFromCode));
291  ClobberCalleeSave();
292  LIR* call_inst = OpReg(kOpBlx, r_tgt);
293  MarkSafepointPC(call_inst);
294}
295
296void MipsMir2Lir::GenMoveException(RegLocation rl_dest) {
297  int ex_offset = Thread::ExceptionOffset().Int32Value();
298  RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
299  int reset_reg = AllocTemp();
300  LoadWordDisp(rMIPS_SELF, ex_offset, rl_result.low_reg);
301  LoadConstant(reset_reg, 0);
302  StoreWordDisp(rMIPS_SELF, ex_offset, reset_reg);
303  FreeTemp(reset_reg);
304  StoreValue(rl_dest, rl_result);
305}
306
307/*
308 * Mark garbage collection card. Skip if the value we're storing is null.
309 */
310void MipsMir2Lir::MarkGCCard(int val_reg, int tgt_addr_reg) {
311  int reg_card_base = AllocTemp();
312  int reg_card_no = AllocTemp();
313  LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL);
314  LoadWordDisp(rMIPS_SELF, Thread::CardTableOffset().Int32Value(), reg_card_base);
315  OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
316  StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0,
317                   kUnsignedByte);
318  LIR* target = NewLIR0(kPseudoTargetLabel);
319  branch_over->target = target;
320  FreeTemp(reg_card_base);
321  FreeTemp(reg_card_no);
322}
323void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
324  int spill_count = num_core_spills_ + num_fp_spills_;
325  /*
326   * On entry, rMIPS_ARG0, rMIPS_ARG1, rMIPS_ARG2 & rMIPS_ARG3 are live.  Let the register
327   * allocation mechanism know so it doesn't try to use any of them when
328   * expanding the frame or flushing.  This leaves the utility
329   * code with a single temp: r12.  This should be enough.
330   */
331  LockTemp(rMIPS_ARG0);
332  LockTemp(rMIPS_ARG1);
333  LockTemp(rMIPS_ARG2);
334  LockTemp(rMIPS_ARG3);
335
336  /*
337   * We can safely skip the stack overflow check if we're
338   * a leaf *and* our frame size < fudge factor.
339   */
340  bool skip_overflow_check = (mir_graph_->MethodIsLeaf() &&
341      (static_cast<size_t>(frame_size_) < Thread::kStackOverflowReservedBytes));
342  NewLIR0(kPseudoMethodEntry);
343  int check_reg = AllocTemp();
344  int new_sp = AllocTemp();
345  if (!skip_overflow_check) {
346    /* Load stack limit */
347    LoadWordDisp(rMIPS_SELF, Thread::StackEndOffset().Int32Value(), check_reg);
348  }
349  /* Spill core callee saves */
350  SpillCoreRegs();
351  /* NOTE: promotion of FP regs currently unsupported, thus no FP spill */
352  DCHECK_EQ(num_fp_spills_, 0);
353  if (!skip_overflow_check) {
354    OpRegRegImm(kOpSub, new_sp, rMIPS_SP, frame_size_ - (spill_count * 4));
355    GenRegRegCheck(kCondCc, new_sp, check_reg, kThrowStackOverflow);
356    OpRegCopy(rMIPS_SP, new_sp);     // Establish stack
357  } else {
358    OpRegImm(kOpSub, rMIPS_SP, frame_size_ - (spill_count * 4));
359  }
360
361  FlushIns(ArgLocs, rl_method);
362
363  FreeTemp(rMIPS_ARG0);
364  FreeTemp(rMIPS_ARG1);
365  FreeTemp(rMIPS_ARG2);
366  FreeTemp(rMIPS_ARG3);
367}
368
369void MipsMir2Lir::GenExitSequence() {
370  /*
371   * In the exit path, rMIPS_RET0/rMIPS_RET1 are live - make sure they aren't
372   * allocated by the register utilities as temps.
373   */
374  LockTemp(rMIPS_RET0);
375  LockTemp(rMIPS_RET1);
376
377  NewLIR0(kPseudoMethodExit);
378  UnSpillCoreRegs();
379  OpReg(kOpBx, r_RA);
380}
381
382}  // namespace art
383