Home | History | Annotate | Download | only in mips
      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 "gc/accounting/card_table.h"
     23 #include "mips_lir.h"
     24 
     25 namespace art {
     26 
     27 bool MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
     28                                  const InlineMethod& special) {
     29   // TODO
     30   return false;
     31 }
     32 
     33 /*
     34  * The lack of pc-relative loads on Mips presents somewhat of a challenge
     35  * for our PIC switch table strategy.  To materialize the current location
     36  * we'll do a dummy JAL and reference our tables using rRA as the
     37  * base register.  Note that rRA will be used both as the base to
     38  * locate the switch table data and as the reference base for the switch
     39  * target offsets stored in the table.  We'll use a special pseudo-instruction
     40  * to represent the jal and trigger the construction of the
     41  * switch table offsets (which will happen after final assembly and all
     42  * labels are fixed).
     43  *
     44  * The test loop will look something like:
     45  *
     46  *   ori   r_end, rZERO, #table_size  ; size in bytes
     47  *   jal   BaseLabel         ; stores "return address" (BaseLabel) in rRA
     48  *   nop                     ; opportunistically fill
     49  * BaseLabel:
     50  *   addiu r_base, rRA, <table> - <BaseLabel>    ; table relative to BaseLabel
     51      addu  r_end, r_end, r_base                   ; end of table
     52  *   lw    r_val, [rSP, v_reg_off]                ; Test Value
     53  * loop:
     54  *   beq   r_base, r_end, done
     55  *   lw    r_key, 0(r_base)
     56  *   addu  r_base, 8
     57  *   bne   r_val, r_key, loop
     58  *   lw    r_disp, -4(r_base)
     59  *   addu  rRA, r_disp
     60  *   jr    rRA
     61  * done:
     62  *
     63  */
     64 void MipsMir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
     65   const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
     66   if (cu_->verbose) {
     67     DumpSparseSwitchTable(table);
     68   }
     69   // Add the table to the list - we'll process it later
     70   SwitchTable* tab_rec =
     71       static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
     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_->Alloc(elements * sizeof(LIR*), kArenaAllocLIR));
     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   RegStorage r_end = AllocTemp();
     86   if (size_hi) {
     87     NewLIR2(kMipsLui, r_end.GetReg(), 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, r_end.GetReg(), r_end.GetReg(), size_lo);
     95   } else {
     96     NewLIR3(kMipsOri, r_end.GetReg(), rZERO, 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   RegStorage r_base = AllocTemp();
    105   NewLIR4(kMipsDelta, r_base.GetReg(), 0, WrapPointer(base_label), WrapPointer(tab_rec));
    106   OpRegRegReg(kOpAdd, r_end, r_end, r_base);
    107 
    108   // Grab switch test value
    109   rl_src = LoadValue(rl_src, kCoreReg);
    110 
    111   // Test loop
    112   RegStorage r_key = AllocTemp();
    113   LIR* loop_label = NewLIR0(kPseudoTargetLabel);
    114   LIR* exit_branch = OpCmpBranch(kCondEq, r_base, r_end, NULL);
    115   Load32Disp(r_base, 0, r_key);
    116   OpRegImm(kOpAdd, r_base, 8);
    117   OpCmpBranch(kCondNe, rl_src.reg, r_key, loop_label);
    118   RegStorage r_disp = AllocTemp();
    119   Load32Disp(r_base, -4, r_disp);
    120   OpRegRegReg(kOpAdd, rs_rRA, rs_rRA, r_disp);
    121   OpReg(kOpBx, rs_rRA);
    122 
    123   // Loop exit
    124   LIR* exit_label = NewLIR0(kPseudoTargetLabel);
    125   exit_branch->target = exit_label;
    126 }
    127 
    128 /*
    129  * Code pattern will look something like:
    130  *
    131  *   lw    r_val
    132  *   jal   BaseLabel         ; stores "return address" (BaseLabel) in rRA
    133  *   nop                     ; opportunistically fill
    134  *   [subiu r_val, bias]      ; Remove bias if low_val != 0
    135  *   bound check -> done
    136  *   lw    r_disp, [rRA, r_val]
    137  *   addu  rRA, r_disp
    138  *   jr    rRA
    139  * done:
    140  */
    141 void MipsMir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) {
    142   const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
    143   if (cu_->verbose) {
    144     DumpPackedSwitchTable(table);
    145   }
    146   // Add the table to the list - we'll process it later
    147   SwitchTable* tab_rec =
    148       static_cast<SwitchTable*>(arena_->Alloc(sizeof(SwitchTable), kArenaAllocData));
    149   tab_rec->table = table;
    150   tab_rec->vaddr = current_dalvik_offset_;
    151   int size = table[1];
    152   tab_rec->targets = static_cast<LIR**>(arena_->Alloc(size * sizeof(LIR*),
    153                                                       kArenaAllocLIR));
    154   switch_tables_.Insert(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, NULL);
    195 
    196   // Materialize the table base pointer
    197   RegStorage r_base = AllocTemp();
    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 rAP and go
    205   OpRegRegReg(kOpAdd, rs_rRA, rs_rRA, r_disp);
    206   OpReg(kOpBx, rs_rRA);
    207 
    208   /* branch_over target here */
    209   LIR* target = NewLIR0(kPseudoTargetLabel);
    210   branch_over->target = target;
    211 }
    212 
    213 /*
    214  * Array data table format:
    215  *  ushort ident = 0x0300   magic value
    216  *  ushort width            width of each element in the table
    217  *  uint   size             number of elements in the table
    218  *  ubyte  data[size*width] table of data values (may contain a single-byte
    219  *                          padding at the end)
    220  *
    221  * Total size is 4+(width * size + 1)/2 16-bit code units.
    222  */
    223 void MipsMir2Lir::GenFillArrayData(DexOffset table_offset, RegLocation rl_src) {
    224   const uint16_t* table = cu_->insns + current_dalvik_offset_ + table_offset;
    225   // Add the table to the list - we'll process it later
    226   FillArrayData* tab_rec =
    227       reinterpret_cast<FillArrayData*>(arena_->Alloc(sizeof(FillArrayData),
    228                                                      kArenaAllocData));
    229   tab_rec->table = table;
    230   tab_rec->vaddr = current_dalvik_offset_;
    231   uint16_t width = tab_rec->table[1];
    232   uint32_t size = tab_rec->table[2] | ((static_cast<uint32_t>(tab_rec->table[3])) << 16);
    233   tab_rec->size = (size * width) + 8;
    234 
    235   fill_array_data_.Insert(tab_rec);
    236 
    237   // Making a call - use explicit registers
    238   FlushAllRegs();   /* Everything to home location */
    239   LockCallTemps();
    240   LoadValueDirectFixed(rl_src, rs_rMIPS_ARG0);
    241 
    242   // Must prevent code motion for the curr pc pair
    243   GenBarrier();
    244   NewLIR0(kMipsCurrPC);  // Really a jal to .+8
    245   // Now, fill the branch delay slot with the helper load
    246   RegStorage r_tgt = LoadHelper(kQuickHandleFillArrayData);
    247   GenBarrier();  // Scheduling barrier
    248 
    249   // Construct BaseLabel and set up table base register
    250   LIR* base_label = NewLIR0(kPseudoTargetLabel);
    251 
    252   // Materialize a pointer to the fill data image
    253   NewLIR4(kMipsDelta, rMIPS_ARG1, 0, WrapPointer(base_label), WrapPointer(tab_rec));
    254 
    255   // And go...
    256   ClobberCallerSave();
    257   LIR* call_inst = OpReg(kOpBlx, r_tgt);  // ( array*, fill_data* )
    258   MarkSafepointPC(call_inst);
    259 }
    260 
    261 void MipsMir2Lir::GenMoveException(RegLocation rl_dest) {
    262   int ex_offset = Thread::ExceptionOffset<4>().Int32Value();
    263   RegLocation rl_result = EvalLoc(rl_dest, kRefReg, true);
    264   RegStorage reset_reg = AllocTempRef();
    265   LoadRefDisp(rs_rMIPS_SELF, ex_offset, rl_result.reg, kNotVolatile);
    266   LoadConstant(reset_reg, 0);
    267   StoreRefDisp(rs_rMIPS_SELF, ex_offset, reset_reg, kNotVolatile);
    268   FreeTemp(reset_reg);
    269   StoreValue(rl_dest, rl_result);
    270 }
    271 
    272 /*
    273  * Mark garbage collection card. Skip if the value we're storing is null.
    274  */
    275 void MipsMir2Lir::MarkGCCard(RegStorage val_reg, RegStorage tgt_addr_reg) {
    276   RegStorage reg_card_base = AllocTemp();
    277   RegStorage reg_card_no = AllocTemp();
    278   LIR* branch_over = OpCmpImmBranch(kCondEq, val_reg, 0, NULL);
    279   // NOTE: native pointer.
    280   LoadWordDisp(rs_rMIPS_SELF, Thread::CardTableOffset<4>().Int32Value(), reg_card_base);
    281   OpRegRegImm(kOpLsr, reg_card_no, tgt_addr_reg, gc::accounting::CardTable::kCardShift);
    282   StoreBaseIndexed(reg_card_base, reg_card_no, reg_card_base, 0, kUnsignedByte);
    283   LIR* target = NewLIR0(kPseudoTargetLabel);
    284   branch_over->target = target;
    285   FreeTemp(reg_card_base);
    286   FreeTemp(reg_card_no);
    287 }
    288 
    289 void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) {
    290   int spill_count = num_core_spills_ + num_fp_spills_;
    291   /*
    292    * On entry, rMIPS_ARG0, rMIPS_ARG1, rMIPS_ARG2 & rMIPS_ARG3 are live.  Let the register
    293    * allocation mechanism know so it doesn't try to use any of them when
    294    * expanding the frame or flushing.  This leaves the utility
    295    * code with a single temp: r12.  This should be enough.
    296    */
    297   LockTemp(rs_rMIPS_ARG0);
    298   LockTemp(rs_rMIPS_ARG1);
    299   LockTemp(rs_rMIPS_ARG2);
    300   LockTemp(rs_rMIPS_ARG3);
    301 
    302   /*
    303    * We can safely skip the stack overflow check if we're
    304    * a leaf *and* our frame size < fudge factor.
    305    */
    306   bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kMips);
    307   NewLIR0(kPseudoMethodEntry);
    308   RegStorage check_reg = AllocTemp();
    309   RegStorage new_sp = AllocTemp();
    310   if (!skip_overflow_check) {
    311     /* Load stack limit */
    312     Load32Disp(rs_rMIPS_SELF, Thread::StackEndOffset<4>().Int32Value(), check_reg);
    313   }
    314   /* Spill core callee saves */
    315   SpillCoreRegs();
    316   /* NOTE: promotion of FP regs currently unsupported, thus no FP spill */
    317   DCHECK_EQ(num_fp_spills_, 0);
    318   const int frame_sub = frame_size_ - spill_count * 4;
    319   if (!skip_overflow_check) {
    320     class StackOverflowSlowPath : public LIRSlowPath {
    321      public:
    322       StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace)
    323           : LIRSlowPath(m2l, m2l->GetCurrentDexPc(), branch, nullptr), sp_displace_(sp_displace) {
    324       }
    325       void Compile() OVERRIDE {
    326         m2l_->ResetRegPool();
    327         m2l_->ResetDefTracking();
    328         GenerateTargetLabel(kPseudoThrowTarget);
    329         // LR is offset 0 since we push in reverse order.
    330         m2l_->Load32Disp(rs_rMIPS_SP, 0, rs_rRA);
    331         m2l_->OpRegImm(kOpAdd, rs_rMIPS_SP, sp_displace_);
    332         m2l_->ClobberCallerSave();
    333         RegStorage r_tgt = m2l_->CallHelperSetup(kQuickThrowStackOverflow);  // Doesn't clobber LR.
    334         m2l_->CallHelper(r_tgt, kQuickThrowStackOverflow, false /* MarkSafepointPC */,
    335                          false /* UseLink */);
    336       }
    337 
    338      private:
    339       const size_t sp_displace_;
    340     };
    341     OpRegRegImm(kOpSub, new_sp, rs_rMIPS_SP, frame_sub);
    342     LIR* branch = OpCmpBranch(kCondUlt, new_sp, check_reg, nullptr);
    343     AddSlowPath(new(arena_)StackOverflowSlowPath(this, branch, spill_count * 4));
    344     // TODO: avoid copy for small frame sizes.
    345     OpRegCopy(rs_rMIPS_SP, new_sp);     // Establish stack
    346   } else {
    347     OpRegImm(kOpSub, rs_rMIPS_SP, frame_sub);
    348   }
    349 
    350   FlushIns(ArgLocs, rl_method);
    351 
    352   FreeTemp(rs_rMIPS_ARG0);
    353   FreeTemp(rs_rMIPS_ARG1);
    354   FreeTemp(rs_rMIPS_ARG2);
    355   FreeTemp(rs_rMIPS_ARG3);
    356 }
    357 
    358 void MipsMir2Lir::GenExitSequence() {
    359   /*
    360    * In the exit path, rMIPS_RET0/rMIPS_RET1 are live - make sure they aren't
    361    * allocated by the register utilities as temps.
    362    */
    363   LockTemp(rs_rMIPS_RET0);
    364   LockTemp(rs_rMIPS_RET1);
    365 
    366   NewLIR0(kPseudoMethodExit);
    367   UnSpillCoreRegs();
    368   OpReg(kOpBx, rs_rRA);
    369 }
    370 
    371 void MipsMir2Lir::GenSpecialExitSequence() {
    372   OpReg(kOpBx, rs_rRA);
    373 }
    374 
    375 }  // namespace art
    376