Home | History | Annotate | Download | only in runtime
      1 /*
      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 #ifndef ART_RUNTIME_VMAP_TABLE_H_
     18 #define ART_RUNTIME_VMAP_TABLE_H_
     19 
     20 #include "base/logging.h"
     21 #include "leb128.h"
     22 #include "stack.h"
     23 
     24 namespace art {
     25 
     26 class VmapTable {
     27  public:
     28   // For efficient encoding of special values, entries are adjusted by 2.
     29   static constexpr uint16_t kEntryAdjustment = 2u;
     30   static constexpr uint16_t kAdjustedFpMarker = static_cast<uint16_t>(0xffffu + kEntryAdjustment);
     31 
     32   explicit VmapTable(const uint8_t* table) : table_(table) {
     33   }
     34 
     35   // Look up nth entry, not called from performance critical code.
     36   uint16_t operator[](size_t n) const {
     37     const uint8_t* table = table_;
     38     size_t size = DecodeUnsignedLeb128(&table);
     39     CHECK_LT(n, size);
     40     uint16_t adjusted_entry = DecodeUnsignedLeb128(&table);
     41     for (size_t i = 0; i < n; ++i) {
     42       adjusted_entry = DecodeUnsignedLeb128(&table);
     43     }
     44     return adjusted_entry - kEntryAdjustment;
     45   }
     46 
     47   size_t Size() const {
     48     const uint8_t* table = table_;
     49     return DecodeUnsignedLeb128(&table);
     50   }
     51 
     52   // Is the dex register 'vreg' in the context or on the stack? Should not be called when the
     53   // 'kind' is unknown or constant.
     54   bool IsInContext(size_t vreg, VRegKind kind, uint32_t* vmap_offset) const {
     55     DCHECK(kind == kReferenceVReg || kind == kIntVReg || kind == kFloatVReg ||
     56            kind == kLongLoVReg || kind == kLongHiVReg || kind == kDoubleLoVReg ||
     57            kind == kDoubleHiVReg || kind == kImpreciseConstant);
     58     *vmap_offset = 0xEBAD0FF5;
     59     // TODO: take advantage of the registers being ordered
     60     // TODO: we treat kImpreciseConstant as an integer below, need to ensure that such values
     61     //       are never promoted to floating point registers.
     62     bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
     63     bool in_floats = false;
     64     const uint8_t* table = table_;
     65     uint16_t adjusted_vreg = vreg + kEntryAdjustment;
     66     size_t end = DecodeUnsignedLeb128(&table);
     67     bool high_reg = (kind == kLongHiVReg) || (kind == kDoubleHiVReg);
     68     bool target64 = (kRuntimeISA == kArm64) || (kRuntimeISA == kX86_64);
     69     if (target64 && high_reg) {
     70       // Wide promoted registers are associated with the sreg of the low portion.
     71       adjusted_vreg--;
     72     }
     73     for (size_t i = 0; i < end; ++i) {
     74       // Stop if we find what we are are looking for.
     75       uint16_t adjusted_entry = DecodeUnsignedLeb128(&table);
     76       if ((adjusted_entry == adjusted_vreg) && (in_floats == is_float)) {
     77         *vmap_offset = i;
     78         return true;
     79       }
     80       // 0xffff is the marker for LR (return PC on x86), following it are spilled float registers.
     81       if (adjusted_entry == kAdjustedFpMarker) {
     82         in_floats = true;
     83       }
     84     }
     85     return false;
     86   }
     87 
     88   // Compute the register number that corresponds to the entry in the vmap (vmap_offset, computed
     89   // by IsInContext above). If the kind is floating point then the result will be a floating point
     90   // register number, otherwise it will be an integer register number.
     91   uint32_t ComputeRegister(uint32_t spill_mask, uint32_t vmap_offset, VRegKind kind) const {
     92     // Compute the register we need to load from the context.
     93     DCHECK(kind == kReferenceVReg || kind == kIntVReg || kind == kFloatVReg ||
     94            kind == kLongLoVReg || kind == kLongHiVReg || kind == kDoubleLoVReg ||
     95            kind == kDoubleHiVReg || kind == kImpreciseConstant);
     96     // TODO: we treat kImpreciseConstant as an integer below, need to ensure that such values
     97     //       are never promoted to floating point registers.
     98     bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
     99     uint32_t matches = 0;
    100     if (UNLIKELY(is_float)) {
    101       const uint8_t* table = table_;
    102       DecodeUnsignedLeb128(&table);  // Skip size.
    103       while (DecodeUnsignedLeb128(&table) != kAdjustedFpMarker) {
    104         matches++;
    105       }
    106       matches++;
    107     }
    108     CHECK_LT(vmap_offset - matches, static_cast<uint32_t>(POPCOUNT(spill_mask)));
    109     uint32_t spill_shifts = 0;
    110     while (matches != (vmap_offset + 1)) {
    111       DCHECK_NE(spill_mask, 0u);
    112       matches += spill_mask & 1;  // Add 1 if the low bit is set
    113       spill_mask >>= 1;
    114       spill_shifts++;
    115     }
    116     spill_shifts--;  // wind back one as we want the last match
    117     return spill_shifts;
    118   }
    119 
    120  private:
    121   const uint8_t* const table_;
    122 };
    123 
    124 }  // namespace art
    125 
    126 #endif  // ART_RUNTIME_VMAP_TABLE_H_
    127