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 
VmapTable(const uint8_t * table)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 
Size()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.
IsInContext(size_t vreg,VRegKind kind,uint32_t * vmap_offset)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.
ComputeRegister(uint32_t spill_mask,uint32_t vmap_offset,VRegKind kind)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