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_MEMORY_REGION_H_ 18 #define ART_RUNTIME_MEMORY_REGION_H_ 19 20 #include <stdint.h> 21 #include <type_traits> 22 23 #include "arch/instruction_set.h" 24 #include "base/bit_utils.h" 25 #include "base/casts.h" 26 #include "base/logging.h" 27 #include "base/macros.h" 28 #include "base/value_object.h" 29 #include "globals.h" 30 31 namespace art { 32 33 // Memory regions are useful for accessing memory with bounds check in 34 // debug mode. They can be safely passed by value and do not assume ownership 35 // of the region. 36 class MemoryRegion FINAL : public ValueObject { 37 public: 38 struct ContentEquals { operatorContentEquals39 constexpr bool operator()(const MemoryRegion& lhs, const MemoryRegion& rhs) const { 40 return lhs.size() == rhs.size() && memcmp(lhs.begin(), rhs.begin(), lhs.size()) == 0; 41 } 42 }; 43 MemoryRegion()44 MemoryRegion() : pointer_(nullptr), size_(0) {} MemoryRegion(void * pointer_in,uintptr_t size_in)45 MemoryRegion(void* pointer_in, uintptr_t size_in) : pointer_(pointer_in), size_(size_in) {} 46 pointer()47 void* pointer() const { return pointer_; } size()48 size_t size() const { return size_; } size_in_bits()49 size_t size_in_bits() const { return size_ * kBitsPerByte; } 50 pointer_offset()51 static size_t pointer_offset() { 52 return OFFSETOF_MEMBER(MemoryRegion, pointer_); 53 } 54 begin()55 uint8_t* begin() const { return reinterpret_cast<uint8_t*>(pointer_); } end()56 uint8_t* end() const { return begin() + size_; } 57 58 // Load value of type `T` at `offset`. The memory address corresponding 59 // to `offset` should be word-aligned (on ARM, this is a requirement). 60 template<typename T> Load(uintptr_t offset)61 ALWAYS_INLINE T Load(uintptr_t offset) const { 62 T* address = ComputeInternalPointer<T>(offset); 63 DCHECK(IsWordAligned(address)); 64 return *address; 65 } 66 67 // Store `value` (of type `T`) at `offset`. The memory address 68 // corresponding to `offset` should be word-aligned (on ARM, this is 69 // a requirement). 70 template<typename T> Store(uintptr_t offset,T value)71 ALWAYS_INLINE void Store(uintptr_t offset, T value) const { 72 T* address = ComputeInternalPointer<T>(offset); 73 DCHECK(IsWordAligned(address)); 74 *address = value; 75 } 76 77 // Load value of type `T` at `offset`. The memory address corresponding 78 // to `offset` does not need to be word-aligned. 79 template<typename T> LoadUnaligned(uintptr_t offset)80 ALWAYS_INLINE T LoadUnaligned(uintptr_t offset) const { 81 // Equivalent unsigned integer type corresponding to T. 82 typedef typename std::make_unsigned<T>::type U; 83 U equivalent_unsigned_integer_value = 0; 84 // Read the value byte by byte in a little-endian fashion. 85 for (size_t i = 0; i < sizeof(U); ++i) { 86 equivalent_unsigned_integer_value += 87 *ComputeInternalPointer<uint8_t>(offset + i) << (i * kBitsPerByte); 88 } 89 return bit_cast<T, U>(equivalent_unsigned_integer_value); 90 } 91 92 // Store `value` (of type `T`) at `offset`. The memory address 93 // corresponding to `offset` does not need to be word-aligned. 94 template<typename T> StoreUnaligned(uintptr_t offset,T value)95 ALWAYS_INLINE void StoreUnaligned(uintptr_t offset, T value) const { 96 // Equivalent unsigned integer type corresponding to T. 97 typedef typename std::make_unsigned<T>::type U; 98 U equivalent_unsigned_integer_value = bit_cast<U, T>(value); 99 // Write the value byte by byte in a little-endian fashion. 100 for (size_t i = 0; i < sizeof(U); ++i) { 101 *ComputeInternalPointer<uint8_t>(offset + i) = 102 (equivalent_unsigned_integer_value >> (i * kBitsPerByte)) & 0xFF; 103 } 104 } 105 106 template<typename T> PointerTo(uintptr_t offset)107 ALWAYS_INLINE T* PointerTo(uintptr_t offset) const { 108 return ComputeInternalPointer<T>(offset); 109 } 110 111 // Load a single bit in the region. The bit at offset 0 is the least 112 // significant bit in the first byte. LoadBit(uintptr_t bit_offset)113 ALWAYS_INLINE bool LoadBit(uintptr_t bit_offset) const { 114 uint8_t bit_mask; 115 uint8_t byte = *ComputeBitPointer(bit_offset, &bit_mask); 116 return byte & bit_mask; 117 } 118 StoreBit(uintptr_t bit_offset,bool value)119 ALWAYS_INLINE void StoreBit(uintptr_t bit_offset, bool value) const { 120 uint8_t bit_mask; 121 uint8_t* byte = ComputeBitPointer(bit_offset, &bit_mask); 122 if (value) { 123 *byte |= bit_mask; 124 } else { 125 *byte &= ~bit_mask; 126 } 127 } 128 129 // Load `length` bits from the region starting at bit offset `bit_offset`. 130 // The bit at the smallest offset is the least significant bit in the 131 // loaded value. `length` must not be larger than the number of bits 132 // contained in the return value (32). LoadBits(uintptr_t bit_offset,size_t length)133 ALWAYS_INLINE uint32_t LoadBits(uintptr_t bit_offset, size_t length) const { 134 DCHECK_LE(length, BitSizeOf<uint32_t>()); 135 DCHECK_LE(bit_offset + length, size_in_bits()); 136 if (UNLIKELY(length == 0)) { 137 // Do not touch any memory if the range is empty. 138 return 0; 139 } 140 const uint8_t* address = begin() + bit_offset / kBitsPerByte; 141 const uint32_t shift = bit_offset & (kBitsPerByte - 1); 142 // Load the value (reading only the strictly needed bytes). 143 const uint32_t load_bit_count = shift + length; 144 uint32_t value = address[0] >> shift; 145 if (load_bit_count > 8) { 146 value |= static_cast<uint32_t>(address[1]) << (8 - shift); 147 if (load_bit_count > 16) { 148 value |= static_cast<uint32_t>(address[2]) << (16 - shift); 149 if (load_bit_count > 24) { 150 value |= static_cast<uint32_t>(address[3]) << (24 - shift); 151 if (load_bit_count > 32) { 152 value |= static_cast<uint32_t>(address[4]) << (32 - shift); 153 } 154 } 155 } 156 } 157 // Clear unwanted most significant bits. 158 uint32_t clear_bit_count = BitSizeOf(value) - length; 159 value = (value << clear_bit_count) >> clear_bit_count; 160 for (size_t i = 0; i < length; ++i) { 161 DCHECK_EQ((value >> i) & 1, LoadBit(bit_offset + i)); 162 } 163 return value; 164 } 165 166 // Store `value` on `length` bits in the region starting at bit offset 167 // `bit_offset`. The bit at the smallest offset is the least significant 168 // bit of the stored `value`. `value` must not be larger than `length` 169 // bits. 170 void StoreBits(uintptr_t bit_offset, uint32_t value, size_t length); 171 172 void CopyFrom(size_t offset, const MemoryRegion& from) const; 173 174 template<class Vector> CopyFromVector(size_t offset,Vector & vector)175 void CopyFromVector(size_t offset, Vector& vector) const { 176 if (!vector.empty()) { 177 CopyFrom(offset, MemoryRegion(vector.data(), vector.size())); 178 } 179 } 180 181 // Compute a sub memory region based on an existing one. Subregion(uintptr_t offset,uintptr_t size_in)182 ALWAYS_INLINE MemoryRegion Subregion(uintptr_t offset, uintptr_t size_in) const { 183 CHECK_GE(this->size(), size_in); 184 CHECK_LE(offset, this->size() - size_in); 185 return MemoryRegion(reinterpret_cast<void*>(begin() + offset), size_in); 186 } 187 188 // Compute an extended memory region based on an existing one. Extend(const MemoryRegion & region,uintptr_t extra)189 ALWAYS_INLINE void Extend(const MemoryRegion& region, uintptr_t extra) { 190 pointer_ = region.pointer(); 191 size_ = (region.size() + extra); 192 } 193 194 private: 195 template<typename T> ComputeInternalPointer(size_t offset)196 ALWAYS_INLINE T* ComputeInternalPointer(size_t offset) const { 197 CHECK_GE(size(), sizeof(T)); 198 CHECK_LE(offset, size() - sizeof(T)); 199 return reinterpret_cast<T*>(begin() + offset); 200 } 201 202 // Locate the bit with the given offset. Returns a pointer to the byte 203 // containing the bit, and sets bit_mask to the bit within that byte. ComputeBitPointer(uintptr_t bit_offset,uint8_t * bit_mask)204 ALWAYS_INLINE uint8_t* ComputeBitPointer(uintptr_t bit_offset, uint8_t* bit_mask) const { 205 uintptr_t bit_remainder = (bit_offset & (kBitsPerByte - 1)); 206 *bit_mask = (1U << bit_remainder); 207 uintptr_t byte_offset = (bit_offset >> kBitsPerByteLog2); 208 return ComputeInternalPointer<uint8_t>(byte_offset); 209 } 210 211 // Is `address` aligned on a machine word? IsWordAligned(const T * address)212 template<typename T> static constexpr bool IsWordAligned(const T* address) { 213 // Word alignment in bytes. 214 size_t kWordAlignment = static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)); 215 return IsAlignedParam(address, kWordAlignment); 216 } 217 218 void* pointer_; 219 size_t size_; 220 }; 221 222 } // namespace art 223 224 #endif // ART_RUNTIME_MEMORY_REGION_H_ 225