blob: 6ec7cc85252d92e007b1a09b1b2558db40df785b [file] [log] [blame]
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +01001/*
2 * Copyright (C) 2014 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_STACK_MAP_H_
18#define ART_RUNTIME_STACK_MAP_H_
19
20#include "base/bit_vector.h"
21#include "memory_region.h"
Nicolas Geoffray376b2bb2014-12-09 14:26:32 +000022#include "utils.h"
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +010023
24namespace art {
25
Roland Levillaina2d8ec62015-03-12 15:25:29 +000026// Size of a frame slot, in bytes. This constant is a signed value,
27// to please the compiler in arithmetic operations involving int32_t
28// (signed) values.
29static ssize_t constexpr kFrameSlotSize = 4;
30
Roland Levillain9ac0e4d2015-03-12 18:33:05 +000031// Word alignment required on ARM, in bytes.
32static constexpr size_t kWordAlignment = 4;
33
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +000034// Size of Dex virtual registers.
35static size_t constexpr kVRegSize = 4;
36
Nicolas Geoffray004c2302015-03-20 10:06:38 +000037class CodeInfo;
38
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +010039/**
40 * Classes in the following file are wrapper on stack map information backed
41 * by a MemoryRegion. As such they read and write to the region, they don't have
42 * their own fields.
43 */
44
45/**
46 * Inline information for a specific PC. The information is of the form:
47 * [inlining_depth, [method_dex reference]+]
48 */
49class InlineInfo {
50 public:
51 explicit InlineInfo(MemoryRegion region) : region_(region) {}
52
53 uint8_t GetDepth() const {
Roland Levillainede7bf82015-03-13 12:23:04 +000054 return region_.LoadUnaligned<uint8_t>(kDepthOffset);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +010055 }
56
57 void SetDepth(uint8_t depth) {
Roland Levillainede7bf82015-03-13 12:23:04 +000058 region_.StoreUnaligned<uint8_t>(kDepthOffset, depth);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +010059 }
60
61 uint32_t GetMethodReferenceIndexAtDepth(uint8_t depth) const {
Roland Levillainede7bf82015-03-13 12:23:04 +000062 return region_.LoadUnaligned<uint32_t>(kFixedSize + depth * SingleEntrySize());
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +010063 }
64
65 void SetMethodReferenceIndexAtDepth(uint8_t depth, uint32_t index) {
Roland Levillainede7bf82015-03-13 12:23:04 +000066 region_.StoreUnaligned<uint32_t>(kFixedSize + depth * SingleEntrySize(), index);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +010067 }
68
69 static size_t SingleEntrySize() {
70 return sizeof(uint32_t);
71 }
72
73 private:
Roland Levillaina2d8ec62015-03-12 15:25:29 +000074 // TODO: Instead of plain types such as "uint8_t", introduce
75 // typedefs (and document the memory layout of InlineInfo).
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +010076 static constexpr int kDepthOffset = 0;
77 static constexpr int kFixedSize = kDepthOffset + sizeof(uint8_t);
78
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +010079 MemoryRegion region_;
80
Nicolas Geoffray39468442014-09-02 15:17:15 +010081 friend class CodeInfo;
82 friend class StackMap;
83 friend class StackMapStream;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +010084};
85
Roland Levillaina2d8ec62015-03-12 15:25:29 +000086// Dex register location container used by DexRegisterMap and StackMapStream.
87class DexRegisterLocation {
88 public:
89 /*
90 * The location kind used to populate the Dex register information in a
91 * StackMapStream can either be:
92 * - kNone: the register has no location yet, meaning it has not been set;
93 * - kConstant: value holds the constant;
94 * - kStack: value holds the stack offset;
95 * - kRegister: value holds the physical register number;
96 * - kFpuRegister: value holds the physical register number.
97 *
98 * In addition, DexRegisterMap also uses these values:
99 * - kInStackLargeOffset: value holds a "large" stack offset (greater than
100 * 128 bytes);
101 * - kConstantLargeValue: value holds a "large" constant (lower than or
102 * equal to -16, or greater than 16).
103 */
104 enum class Kind : uint8_t {
105 // Short location kinds, for entries fitting on one byte (3 bits
106 // for the kind, 5 bits for the value) in a DexRegisterMap.
107 kNone = 0, // 0b000
108 kInStack = 1, // 0b001
109 kInRegister = 2, // 0b010
110 kInFpuRegister = 3, // 0b011
111 kConstant = 4, // 0b100
112
113 // Large location kinds, requiring a 5-byte encoding (1 byte for the
114 // kind, 4 bytes for the value).
115
116 // Stack location at a large offset, meaning that the offset value
117 // divided by the stack frame slot size (4 bytes) cannot fit on a
118 // 5-bit unsigned integer (i.e., this offset value is greater than
119 // or equal to 2^5 * 4 = 128 bytes).
120 kInStackLargeOffset = 5, // 0b101
121
122 // Large constant, that cannot fit on a 5-bit signed integer (i.e.,
123 // lower than -2^(5-1) = -16, or greater than or equal to
124 // 2^(5-1) - 1 = 15).
125 kConstantLargeValue = 6, // 0b110
126
127 kLastLocationKind = kConstantLargeValue
128 };
129
130 static_assert(
131 sizeof(Kind) == 1u,
132 "art::DexRegisterLocation::Kind has a size different from one byte.");
133
134 static const char* PrettyDescriptor(Kind kind) {
135 switch (kind) {
136 case Kind::kNone:
137 return "none";
138 case Kind::kInStack:
139 return "in stack";
140 case Kind::kInRegister:
141 return "in register";
142 case Kind::kInFpuRegister:
143 return "in fpu register";
144 case Kind::kConstant:
145 return "as constant";
146 case Kind::kInStackLargeOffset:
147 return "in stack (large offset)";
148 case Kind::kConstantLargeValue:
149 return "as constant (large value)";
150 default:
151 UNREACHABLE();
152 }
153 }
154
155 static bool IsShortLocationKind(Kind kind) {
156 switch (kind) {
157 case Kind::kNone:
158 case Kind::kInStack:
159 case Kind::kInRegister:
160 case Kind::kInFpuRegister:
161 case Kind::kConstant:
162 return true;
163
164 case Kind::kInStackLargeOffset:
165 case Kind::kConstantLargeValue:
166 return false;
167
168 default:
169 UNREACHABLE();
170 }
171 }
172
173 // Convert `kind` to a "surface" kind, i.e. one that doesn't include
174 // any value with a "large" qualifier.
175 // TODO: Introduce another enum type for the surface kind?
176 static Kind ConvertToSurfaceKind(Kind kind) {
177 switch (kind) {
178 case Kind::kNone:
179 case Kind::kInStack:
180 case Kind::kInRegister:
181 case Kind::kInFpuRegister:
182 case Kind::kConstant:
183 return kind;
184
185 case Kind::kInStackLargeOffset:
186 return Kind::kInStack;
187
188 case Kind::kConstantLargeValue:
189 return Kind::kConstant;
190
191 default:
192 UNREACHABLE();
193 }
194 }
195
196 DexRegisterLocation(Kind kind, int32_t value)
197 : kind_(kind), value_(value) {}
198
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000199 static DexRegisterLocation None() {
200 return DexRegisterLocation(Kind::kNone, 0);
201 }
202
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000203 // Get the "surface" kind of the location, i.e., the one that doesn't
204 // include any value with a "large" qualifier.
205 Kind GetKind() const {
206 return ConvertToSurfaceKind(kind_);
207 }
208
209 // Get the value of the location.
210 int32_t GetValue() const { return value_; }
211
212 // Get the actual kind of the location.
213 Kind GetInternalKind() const { return kind_; }
214
Calin Juravle6ae70962015-03-18 16:31:28 +0000215 bool operator==(DexRegisterLocation other) const {
216 return kind_ == other.kind_ && value_ == other.value_;
217 }
218
219 bool operator!=(DexRegisterLocation other) const {
220 return !(*this == other);
221 }
222
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000223 private:
224 Kind kind_;
225 int32_t value_;
226};
227
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100228/**
229 * Information on dex register values for a specific PC. The information is
230 * of the form:
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000231 * [live_bit_mask, DexRegisterLocation+].
232 * DexRegisterLocations are either 1- or 5-byte wide (see art::DexRegisterLocation::Kind).
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100233 */
234class DexRegisterMap {
235 public:
236 explicit DexRegisterMap(MemoryRegion region) : region_(region) {}
237
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000238 // Short (compressed) location, fitting on one byte.
239 typedef uint8_t ShortLocation;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100240
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000241 static size_t LiveBitMaskSize(uint16_t number_of_dex_registers) {
242 return RoundUp(number_of_dex_registers, kBitsPerByte) / kBitsPerByte;
243 }
244
245 void SetLiveBitMask(size_t offset,
246 uint16_t number_of_dex_registers,
247 const BitVector& live_dex_registers_mask) {
248 for (uint16_t i = 0; i < number_of_dex_registers; i++) {
249 region_.StoreBit(offset + i, live_dex_registers_mask.IsBitSet(i));
250 }
251 }
252
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000253 void SetRegisterInfo(size_t offset, const DexRegisterLocation& dex_register_location) {
254 DexRegisterLocation::Kind kind = ComputeCompressedKind(dex_register_location);
255 int32_t value = dex_register_location.GetValue();
256 if (DexRegisterLocation::IsShortLocationKind(kind)) {
257 // Short location. Compress the kind and the value as a single byte.
258 if (kind == DexRegisterLocation::Kind::kInStack) {
259 // Instead of storing stack offsets expressed in bytes for
260 // short stack locations, store slot offsets. A stack offset
261 // is a multiple of 4 (kFrameSlotSize). This means that by
262 // dividing it by 4, we can fit values from the [0, 128)
263 // interval in a short stack location, and not just values
264 // from the [0, 32) interval.
265 DCHECK_EQ(value % kFrameSlotSize, 0);
266 value /= kFrameSlotSize;
267 }
268 DCHECK(IsUint<kValueBits>(value)) << value;
269 region_.StoreUnaligned<ShortLocation>(offset, MakeShortLocation(kind, value));
270 } else {
271 // Large location. Write the location on one byte and the value
272 // on 4 bytes.
273 DCHECK(!IsUint<kValueBits>(value)) << value;
274 if (kind == DexRegisterLocation::Kind::kInStackLargeOffset) {
275 // Also divide large stack offsets by 4 for the sake of consistency.
276 DCHECK_EQ(value % kFrameSlotSize, 0);
277 value /= kFrameSlotSize;
278 }
279 // Data can be unaligned as the written Dex register locations can
280 // either be 1-byte or 5-byte wide. Use
281 // art::MemoryRegion::StoreUnaligned instead of
282 // art::MemoryRegion::Store to prevent unligned word accesses on ARM.
283 region_.StoreUnaligned<DexRegisterLocation::Kind>(offset, kind);
284 region_.StoreUnaligned<int32_t>(offset + sizeof(DexRegisterLocation::Kind), value);
Roland Levillain442b46a2015-02-18 16:54:21 +0000285 }
286 }
287
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000288 bool IsDexRegisterLive(uint16_t dex_register_index) const {
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000289 size_t offset = kFixedSize;
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000290 return region_.LoadBit(offset + dex_register_index);
291 }
292
293 static constexpr size_t kNoDexRegisterLocationOffset = -1;
294
295 static size_t GetDexRegisterMapLocationsOffset(uint16_t number_of_dex_registers) {
296 return kLiveBitMaskOffset + LiveBitMaskSize(number_of_dex_registers);
297 }
298
299 // Find the offset of the Dex register location number `dex_register_index`.
300 size_t FindLocationOffset(uint16_t dex_register_index, uint16_t number_of_dex_registers) const {
301 if (!IsDexRegisterLive(dex_register_index)) return kNoDexRegisterLocationOffset;
302 size_t offset = GetDexRegisterMapLocationsOffset(number_of_dex_registers);
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000303 // Skip the first `dex_register_index - 1` entries.
304 for (uint16_t i = 0; i < dex_register_index; ++i) {
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000305 if (IsDexRegisterLive(i)) {
306 // Read the first next byte and inspect its first 3 bits to decide
307 // whether it is a short or a large location.
308 DexRegisterLocation::Kind kind = ExtractKindAtOffset(offset);
309 if (DexRegisterLocation::IsShortLocationKind(kind)) {
310 // Short location. Skip the current byte.
311 offset += SingleShortEntrySize();
312 } else {
313 // Large location. Skip the 5 next bytes.
314 offset += SingleLargeEntrySize();
315 }
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000316 }
317 }
318 return offset;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100319 }
320
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000321 // Get the surface kind.
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000322 DexRegisterLocation::Kind GetLocationKind(uint16_t dex_register_index,
323 uint16_t number_of_dex_registers) const {
324 return IsDexRegisterLive(dex_register_index)
325 ? DexRegisterLocation::ConvertToSurfaceKind(
326 GetLocationInternalKind(dex_register_index, number_of_dex_registers))
327 : DexRegisterLocation::Kind::kNone;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100328 }
329
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000330 // Get the internal kind.
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000331 DexRegisterLocation::Kind GetLocationInternalKind(uint16_t dex_register_index,
332 uint16_t number_of_dex_registers) const {
333 return IsDexRegisterLive(dex_register_index)
334 ? ExtractKindAtOffset(FindLocationOffset(dex_register_index, number_of_dex_registers))
335 : DexRegisterLocation::Kind::kNone;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100336 }
337
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000338 // TODO: Rename as GetDexRegisterLocation?
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000339 DexRegisterLocation GetLocationKindAndValue(uint16_t dex_register_index,
340 uint16_t number_of_dex_registers) const {
341 if (!IsDexRegisterLive(dex_register_index)) {
342 return DexRegisterLocation::None();
343 }
344 size_t offset = FindLocationOffset(dex_register_index, number_of_dex_registers);
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000345 // Read the first byte and inspect its first 3 bits to get the location.
346 ShortLocation first_byte = region_.LoadUnaligned<ShortLocation>(offset);
347 DexRegisterLocation::Kind kind = ExtractKindFromShortLocation(first_byte);
348 if (DexRegisterLocation::IsShortLocationKind(kind)) {
349 // Short location. Extract the value from the remaining 5 bits.
350 int32_t value = ExtractValueFromShortLocation(first_byte);
351 if (kind == DexRegisterLocation::Kind::kInStack) {
352 // Convert the stack slot (short) offset to a byte offset value.
353 value *= kFrameSlotSize;
354 }
355 return DexRegisterLocation(kind, value);
356 } else {
357 // Large location. Read the four next bytes to get the value.
358 int32_t value = region_.LoadUnaligned<int32_t>(offset + sizeof(DexRegisterLocation::Kind));
359 if (kind == DexRegisterLocation::Kind::kInStackLargeOffset) {
360 // Convert the stack slot (large) offset to a byte offset value.
361 value *= kFrameSlotSize;
362 }
363 return DexRegisterLocation(kind, value);
364 }
Sebastien Hertz7cde48c2015-01-20 16:06:43 +0100365 }
366
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000367 int32_t GetStackOffsetInBytes(uint16_t dex_register_index,
368 uint16_t number_of_dex_registers) const {
369 DexRegisterLocation location =
370 GetLocationKindAndValue(dex_register_index, number_of_dex_registers);
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000371 DCHECK(location.GetKind() == DexRegisterLocation::Kind::kInStack);
372 // GetLocationKindAndValue returns the offset in bytes.
373 return location.GetValue();
Sebastien Hertz7cde48c2015-01-20 16:06:43 +0100374 }
375
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000376 int32_t GetConstant(uint16_t dex_register_index, uint16_t number_of_dex_registers) const {
377 DexRegisterLocation location =
378 GetLocationKindAndValue(dex_register_index, number_of_dex_registers);
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000379 DCHECK(location.GetKind() == DexRegisterLocation::Kind::kConstant);
380 return location.GetValue();
Sebastien Hertz7cde48c2015-01-20 16:06:43 +0100381 }
382
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000383 int32_t GetMachineRegister(uint16_t dex_register_index, uint16_t number_of_dex_registers) const {
384 DexRegisterLocation location =
385 GetLocationKindAndValue(dex_register_index, number_of_dex_registers);
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000386 DCHECK(location.GetInternalKind() == DexRegisterLocation::Kind::kInRegister
387 || location.GetInternalKind() == DexRegisterLocation::Kind::kInFpuRegister)
388 << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind());
389 return location.GetValue();
390 }
391
392 // Compute the compressed kind of `location`.
393 static DexRegisterLocation::Kind ComputeCompressedKind(const DexRegisterLocation& location) {
394 switch (location.GetInternalKind()) {
395 case DexRegisterLocation::Kind::kNone:
396 DCHECK_EQ(location.GetValue(), 0);
397 return DexRegisterLocation::Kind::kNone;
398
399 case DexRegisterLocation::Kind::kInRegister:
400 DCHECK_GE(location.GetValue(), 0);
401 DCHECK_LT(location.GetValue(), 1 << DexRegisterMap::kValueBits);
402 return DexRegisterLocation::Kind::kInRegister;
403
404 case DexRegisterLocation::Kind::kInFpuRegister:
405 DCHECK_GE(location.GetValue(), 0);
406 DCHECK_LT(location.GetValue(), 1 << DexRegisterMap::kValueBits);
407 return DexRegisterLocation::Kind::kInFpuRegister;
408
409 case DexRegisterLocation::Kind::kInStack:
410 DCHECK_EQ(location.GetValue() % kFrameSlotSize, 0);
411 return IsUint<DexRegisterMap::kValueBits>(location.GetValue() / kFrameSlotSize)
412 ? DexRegisterLocation::Kind::kInStack
413 : DexRegisterLocation::Kind::kInStackLargeOffset;
414
415 case DexRegisterLocation::Kind::kConstant:
416 return IsUint<DexRegisterMap::kValueBits>(location.GetValue())
417 ? DexRegisterLocation::Kind::kConstant
418 : DexRegisterLocation::Kind::kConstantLargeValue;
419
420 default:
421 LOG(FATAL) << "Unexpected location kind"
422 << DexRegisterLocation::PrettyDescriptor(location.GetInternalKind());
423 UNREACHABLE();
424 }
425 }
426
427 // Can `location` be turned into a short location?
428 static bool CanBeEncodedAsShortLocation(const DexRegisterLocation& location) {
429 switch (location.GetInternalKind()) {
430 case DexRegisterLocation::Kind::kNone:
431 case DexRegisterLocation::Kind::kInRegister:
432 case DexRegisterLocation::Kind::kInFpuRegister:
433 return true;
434
435 case DexRegisterLocation::Kind::kInStack:
436 DCHECK_EQ(location.GetValue() % kFrameSlotSize, 0);
437 return IsUint<kValueBits>(location.GetValue() / kFrameSlotSize);
438
439 case DexRegisterLocation::Kind::kConstant:
440 return IsUint<kValueBits>(location.GetValue());
441
442 default:
443 UNREACHABLE();
444 }
445 }
446
447 static size_t EntrySize(const DexRegisterLocation& location) {
448 return CanBeEncodedAsShortLocation(location)
449 ? DexRegisterMap::SingleShortEntrySize()
450 : DexRegisterMap::SingleLargeEntrySize();
451 }
452
453 static size_t SingleShortEntrySize() {
454 return sizeof(ShortLocation);
455 }
456
457 static size_t SingleLargeEntrySize() {
458 return sizeof(DexRegisterLocation::Kind) + sizeof(int32_t);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100459 }
460
Roland Levillain12baf472015-03-05 12:41:42 +0000461 size_t Size() const {
462 return region_.size();
463 }
464
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000465 static constexpr int kLiveBitMaskOffset = 0;
466 static constexpr int kFixedSize = kLiveBitMaskOffset;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100467
Roland Levillain12baf472015-03-05 12:41:42 +0000468 private:
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000469 // Width of the kind "field" in a short location, in bits.
470 static constexpr size_t kKindBits = 3;
471 // Width of the value "field" in a short location, in bits.
472 static constexpr size_t kValueBits = 5;
473
474 static constexpr uint8_t kKindMask = (1 << kKindBits) - 1;
475 static constexpr int32_t kValueMask = (1 << kValueBits) - 1;
476 static constexpr size_t kKindOffset = 0;
477 static constexpr size_t kValueOffset = kKindBits;
478
479 static ShortLocation MakeShortLocation(DexRegisterLocation::Kind kind, int32_t value) {
480 DCHECK(IsUint<kKindBits>(static_cast<uint8_t>(kind))) << static_cast<uint8_t>(kind);
481 DCHECK(IsUint<kValueBits>(value)) << value;
482 return (static_cast<uint8_t>(kind) & kKindMask) << kKindOffset
483 | (value & kValueMask) << kValueOffset;
484 }
485
486 static DexRegisterLocation::Kind ExtractKindFromShortLocation(ShortLocation location) {
487 uint8_t kind = (location >> kKindOffset) & kKindMask;
488 DCHECK_LE(kind, static_cast<uint8_t>(DexRegisterLocation::Kind::kLastLocationKind));
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000489 // We do not encode kNone locations in the stack map.
490 DCHECK_NE(kind, static_cast<uint8_t>(DexRegisterLocation::Kind::kNone));
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000491 return static_cast<DexRegisterLocation::Kind>(kind);
492 }
493
494 static int32_t ExtractValueFromShortLocation(ShortLocation location) {
495 return (location >> kValueOffset) & kValueMask;
496 }
497
498 // Extract a location kind from the byte at position `offset`.
499 DexRegisterLocation::Kind ExtractKindAtOffset(size_t offset) const {
500 ShortLocation first_byte = region_.LoadUnaligned<ShortLocation>(offset);
501 return ExtractKindFromShortLocation(first_byte);
502 }
503
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100504 MemoryRegion region_;
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000505
506 friend class CodeInfo;
507 friend class StackMapStream;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100508};
509
510/**
511 * A Stack Map holds compilation information for a specific PC necessary for:
512 * - Mapping it to a dex PC,
513 * - Knowing which stack entries are objects,
514 * - Knowing which registers hold objects,
515 * - Knowing the inlining information,
516 * - Knowing the values of dex registers.
517 *
518 * The information is of the form:
Nicolas Geoffray39468442014-09-02 15:17:15 +0100519 * [dex_pc, native_pc_offset, dex_register_map_offset, inlining_info_offset, register_mask, stack_mask].
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100520 *
521 * Note that register_mask is fixed size, but stack_mask is variable size, depending on the
522 * stack size of a method.
523 */
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100524class StackMap {
525 public:
526 explicit StackMap(MemoryRegion region) : region_(region) {}
527
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000528 uint32_t GetDexPc(const CodeInfo& info) const;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100529
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000530 void SetDexPc(const CodeInfo& info, uint32_t dex_pc);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100531
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000532 uint32_t GetNativePcOffset(const CodeInfo& info) const;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100533
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000534 void SetNativePcOffset(const CodeInfo& info, uint32_t native_pc_offset);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100535
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000536 uint32_t GetDexRegisterMapOffset(const CodeInfo& info) const;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100537
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000538 void SetDexRegisterMapOffset(const CodeInfo& info, uint32_t offset);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100539
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000540 uint32_t GetInlineDescriptorOffset(const CodeInfo& info) const;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100541
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000542 void SetInlineDescriptorOffset(const CodeInfo& info, uint32_t offset);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100543
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000544 uint32_t GetRegisterMask(const CodeInfo& info) const;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100545
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000546 void SetRegisterMask(const CodeInfo& info, uint32_t mask);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100547
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000548 MemoryRegion GetStackMask(const CodeInfo& info) const;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100549
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000550 void SetStackMask(const CodeInfo& info, const BitVector& sp_map) {
551 MemoryRegion region = GetStackMask(info);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100552 for (size_t i = 0; i < region.size_in_bits(); i++) {
553 region.StoreBit(i, sp_map.IsBitSet(i));
554 }
555 }
556
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000557 bool HasDexRegisterMap(const CodeInfo& info) const {
558 return GetDexRegisterMapOffset(info) != kNoDexRegisterMap;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100559 }
560
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000561 bool HasInlineInfo(const CodeInfo& info) const {
562 return GetInlineDescriptorOffset(info) != kNoInlineInfo;
Roland Levillain442b46a2015-02-18 16:54:21 +0000563 }
564
565 bool Equals(const StackMap& other) const {
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100566 return region_.pointer() == other.region_.pointer()
567 && region_.size() == other.region_.size();
568 }
569
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000570 static size_t ComputeStackMapSize(size_t stack_mask_size,
571 bool has_inline_info,
572 bool is_small_inline_info,
573 bool is_small_dex_map,
574 bool is_small_dex_pc,
575 bool is_small_native_pc);
576
577 static size_t ComputeStackMapSize(size_t stack_mask_size,
578 size_t inline_info_size,
579 size_t dex_register_map_size,
580 size_t dex_pc_max,
581 size_t native_pc_max);
582
583 // TODO: Revisit this abstraction if we allow 3 bytes encoding.
584 typedef uint8_t kSmallEncoding;
585 typedef uint32_t kLargeEncoding;
586 static constexpr size_t kBytesForSmallEncoding = sizeof(kSmallEncoding);
587 static constexpr size_t kBitsForSmallEncoding = kBitsPerByte * kBytesForSmallEncoding;
588 static constexpr size_t kBytesForLargeEncoding = sizeof(kLargeEncoding);
589 static constexpr size_t kBitsForLargeEncoding = kBitsPerByte * kBytesForLargeEncoding;
Nicolas Geoffray376b2bb2014-12-09 14:26:32 +0000590
Roland Levillain442b46a2015-02-18 16:54:21 +0000591 // Special (invalid) offset for the DexRegisterMapOffset field meaning
592 // that there is no Dex register map for this stack map.
593 static constexpr uint32_t kNoDexRegisterMap = -1;
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000594 static constexpr uint32_t kNoDexRegisterMapSmallEncoding =
595 std::numeric_limits<kSmallEncoding>::max();
Roland Levillain442b46a2015-02-18 16:54:21 +0000596
597 // Special (invalid) offset for the InlineDescriptorOffset field meaning
598 // that there is no inline info for this stack map.
599 static constexpr uint32_t kNoInlineInfo = -1;
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000600 static constexpr uint32_t kNoInlineInfoSmallEncoding =
601 std::numeric_limits<kSmallEncoding>::max();
602
603 // Returns the number of bytes needed for an entry in the StackMap.
604 static size_t NumberOfBytesForEntry(bool small_encoding) {
605 return small_encoding ? kBytesForSmallEncoding : kBytesForLargeEncoding;
606 }
Roland Levillain442b46a2015-02-18 16:54:21 +0000607
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100608 private:
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000609 // TODO: Instead of plain types such as "uint32_t", introduce
610 // typedefs (and document the memory layout of StackMap).
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000611 static constexpr int kRegisterMaskOffset = 0;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100612 static constexpr int kFixedSize = kRegisterMaskOffset + sizeof(uint32_t);
613 static constexpr int kStackMaskOffset = kFixedSize;
614
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100615 MemoryRegion region_;
616
Nicolas Geoffray39468442014-09-02 15:17:15 +0100617 friend class CodeInfo;
618 friend class StackMapStream;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100619};
620
621
622/**
623 * Wrapper around all compiler information collected for a method.
624 * The information is of the form:
Nicolas Geoffray39468442014-09-02 15:17:15 +0100625 * [overall_size, number_of_stack_maps, stack_mask_size, StackMap+, DexRegisterInfo+, InlineInfo*].
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100626 */
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100627class CodeInfo {
628 public:
629 explicit CodeInfo(MemoryRegion region) : region_(region) {}
630
Nicolas Geoffray39468442014-09-02 15:17:15 +0100631 explicit CodeInfo(const void* data) {
632 uint32_t size = reinterpret_cast<const uint32_t*>(data)[0];
633 region_ = MemoryRegion(const_cast<void*>(data), size);
634 }
635
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000636 void SetEncoding(size_t inline_info_size,
637 size_t dex_register_map_size,
638 size_t dex_pc_max,
639 size_t native_pc_max) {
640 if (inline_info_size != 0) {
641 region_.StoreBit(kHasInlineInfoBitOffset, 1);
642 region_.StoreBit(kHasSmallInlineInfoBitOffset, IsUint<StackMap::kBitsForSmallEncoding>(
643 // + 1 to also encode kNoInlineInfo: if an inline info offset
644 // is at 0xFF, we want to overflow to a larger encoding, because it will
645 // conflict with kNoInlineInfo.
646 // The offset is relative to the dex register map. TODO: Change this.
647 inline_info_size + dex_register_map_size + 1));
648 } else {
649 region_.StoreBit(kHasInlineInfoBitOffset, 0);
650 region_.StoreBit(kHasSmallInlineInfoBitOffset, 0);
651 }
652 region_.StoreBit(kHasSmallDexRegisterMapBitOffset,
653 // + 1 to also encode kNoDexRegisterMap: if a dex register map offset
654 // is at 0xFF, we want to overflow to a larger encoding, because it will
655 // conflict with kNoDexRegisterMap.
656 IsUint<StackMap::kBitsForSmallEncoding>(dex_register_map_size + 1));
657 region_.StoreBit(kHasSmallDexPcBitOffset, IsUint<StackMap::kBitsForSmallEncoding>(dex_pc_max));
658 region_.StoreBit(kHasSmallNativePcBitOffset,
659 IsUint<StackMap::kBitsForSmallEncoding>(native_pc_max));
660 }
661
662 bool HasInlineInfo() const {
663 return region_.LoadBit(kHasInlineInfoBitOffset);
664 }
665
666 bool HasSmallInlineInfo() const {
667 return region_.LoadBit(kHasSmallInlineInfoBitOffset);
668 }
669
670 bool HasSmallDexRegisterMap() const {
671 return region_.LoadBit(kHasSmallDexRegisterMapBitOffset);
672 }
673
674 bool HasSmallNativePc() const {
675 return region_.LoadBit(kHasSmallNativePcBitOffset);
676 }
677
678 bool HasSmallDexPc() const {
679 return region_.LoadBit(kHasSmallDexPcBitOffset);
680 }
681
682 size_t ComputeStackMapRegisterMaskOffset() const {
683 return StackMap::kRegisterMaskOffset;
684 }
685
686 size_t ComputeStackMapStackMaskOffset() const {
687 return StackMap::kStackMaskOffset;
688 }
689
690 size_t ComputeStackMapDexPcOffset() const {
691 return ComputeStackMapStackMaskOffset() + GetStackMaskSize();
692 }
693
694 size_t ComputeStackMapNativePcOffset() const {
695 return ComputeStackMapDexPcOffset()
696 + (HasSmallDexPc() ? sizeof(uint8_t) : sizeof(uint32_t));
697 }
698
699 size_t ComputeStackMapDexRegisterMapOffset() const {
700 return ComputeStackMapNativePcOffset()
701 + (HasSmallNativePc() ? sizeof(uint8_t) : sizeof(uint32_t));
702 }
703
704 size_t ComputeStackMapInlineInfoOffset() const {
705 CHECK(HasInlineInfo());
706 return ComputeStackMapDexRegisterMapOffset()
707 + (HasSmallDexRegisterMap() ? sizeof(uint8_t) : sizeof(uint32_t));
708 }
709
Nicolas Geoffray39468442014-09-02 15:17:15 +0100710 StackMap GetStackMapAt(size_t i) const {
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100711 size_t size = StackMapSize();
Nicolas Geoffray39468442014-09-02 15:17:15 +0100712 return StackMap(GetStackMaps().Subregion(i * size, size));
713 }
714
715 uint32_t GetOverallSize() const {
Nicolas Geoffrayaec8f932015-03-18 10:42:22 +0000716 return region_.LoadUnaligned<uint32_t>(kOverallSizeOffset);
Nicolas Geoffray39468442014-09-02 15:17:15 +0100717 }
718
719 void SetOverallSize(uint32_t size) {
Nicolas Geoffrayaec8f932015-03-18 10:42:22 +0000720 region_.StoreUnaligned<uint32_t>(kOverallSizeOffset, size);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100721 }
722
723 uint32_t GetStackMaskSize() const {
Nicolas Geoffrayaec8f932015-03-18 10:42:22 +0000724 return region_.LoadUnaligned<uint32_t>(kStackMaskSizeOffset);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100725 }
726
727 void SetStackMaskSize(uint32_t size) {
Nicolas Geoffrayaec8f932015-03-18 10:42:22 +0000728 region_.StoreUnaligned<uint32_t>(kStackMaskSizeOffset, size);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100729 }
730
731 size_t GetNumberOfStackMaps() const {
Nicolas Geoffrayaec8f932015-03-18 10:42:22 +0000732 return region_.LoadUnaligned<uint32_t>(kNumberOfStackMapsOffset);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100733 }
734
735 void SetNumberOfStackMaps(uint32_t number_of_stack_maps) {
Nicolas Geoffrayaec8f932015-03-18 10:42:22 +0000736 region_.StoreUnaligned<uint32_t>(kNumberOfStackMapsOffset, number_of_stack_maps);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100737 }
738
Roland Levillain29ba1b02015-03-13 11:45:07 +0000739 // Get the size of one stack map of this CodeInfo object, in bytes.
740 // All stack maps of a CodeInfo have the same size.
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100741 size_t StackMapSize() const {
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000742 return StackMap::ComputeStackMapSize(GetStackMaskSize(),
743 HasInlineInfo(),
744 HasSmallInlineInfo(),
745 HasSmallDexRegisterMap(),
746 HasSmallDexPc(),
747 HasSmallNativePc());
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100748 }
749
Roland Levillain29ba1b02015-03-13 11:45:07 +0000750 // Get the size all the stack maps of this CodeInfo object, in bytes.
751 size_t StackMapsSize() const {
752 return StackMapSize() * GetNumberOfStackMaps();
753 }
754
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000755 size_t GetDexRegisterMapsOffset() const {
756 return CodeInfo::kFixedSize + StackMapsSize();
757 }
758
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000759 uint32_t GetStackMapsOffset() const {
760 return kFixedSize;
761 }
762
Roland Levillain442b46a2015-02-18 16:54:21 +0000763 DexRegisterMap GetDexRegisterMapOf(StackMap stack_map, uint32_t number_of_dex_registers) const {
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000764 DCHECK(stack_map.HasDexRegisterMap(*this));
765 uint32_t offset = stack_map.GetDexRegisterMapOffset(*this) + GetDexRegisterMapsOffset();
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000766 size_t size = ComputeDexRegisterMapSize(offset, number_of_dex_registers);
767 return DexRegisterMap(region_.Subregion(offset, size));
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100768 }
769
Roland Levillain442b46a2015-02-18 16:54:21 +0000770 InlineInfo GetInlineInfoOf(StackMap stack_map) const {
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000771 DCHECK(stack_map.HasInlineInfo(*this));
772 uint32_t offset = stack_map.GetInlineDescriptorOffset(*this) + GetDexRegisterMapsOffset();
773 uint8_t depth = region_.LoadUnaligned<uint8_t>(offset);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100774 return InlineInfo(region_.Subregion(offset,
775 InlineInfo::kFixedSize + depth * InlineInfo::SingleEntrySize()));
776 }
777
Roland Levillain442b46a2015-02-18 16:54:21 +0000778 StackMap GetStackMapForDexPc(uint32_t dex_pc) const {
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100779 for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
Nicolas Geoffray39468442014-09-02 15:17:15 +0100780 StackMap stack_map = GetStackMapAt(i);
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000781 if (stack_map.GetDexPc(*this) == dex_pc) {
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100782 return stack_map;
783 }
784 }
785 LOG(FATAL) << "Unreachable";
Ian Rogers2c4257b2014-10-24 14:20:06 -0700786 UNREACHABLE();
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100787 }
788
Roland Levillain442b46a2015-02-18 16:54:21 +0000789 StackMap GetStackMapForNativePcOffset(uint32_t native_pc_offset) const {
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100790 // TODO: stack maps are sorted by native pc, we can do a binary search.
791 for (size_t i = 0, e = GetNumberOfStackMaps(); i < e; ++i) {
Nicolas Geoffray39468442014-09-02 15:17:15 +0100792 StackMap stack_map = GetStackMapAt(i);
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000793 if (stack_map.GetNativePcOffset(*this) == native_pc_offset) {
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100794 return stack_map;
795 }
796 }
797 LOG(FATAL) << "Unreachable";
Ian Rogers2c4257b2014-10-24 14:20:06 -0700798 UNREACHABLE();
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100799 }
800
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000801 void Dump(std::ostream& os, uint16_t number_of_dex_registers) const;
802 void DumpStackMapHeader(std::ostream& os, size_t stack_map_num) const;
803
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100804 private:
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000805 // TODO: Instead of plain types such as "uint32_t", introduce
806 // typedefs (and document the memory layout of CodeInfo).
Nicolas Geoffray39468442014-09-02 15:17:15 +0100807 static constexpr int kOverallSizeOffset = 0;
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000808 static constexpr int kEncodingInfoOffset = kOverallSizeOffset + sizeof(uint32_t);
809 static constexpr int kNumberOfStackMapsOffset = kEncodingInfoOffset + sizeof(uint8_t);
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100810 static constexpr int kStackMaskSizeOffset = kNumberOfStackMapsOffset + sizeof(uint32_t);
811 static constexpr int kFixedSize = kStackMaskSizeOffset + sizeof(uint32_t);
812
Nicolas Geoffray004c2302015-03-20 10:06:38 +0000813 static constexpr int kHasInlineInfoBitOffset = (kEncodingInfoOffset * kBitsPerByte);
814 static constexpr int kHasSmallInlineInfoBitOffset = kHasInlineInfoBitOffset + 1;
815 static constexpr int kHasSmallDexRegisterMapBitOffset = kHasSmallInlineInfoBitOffset + 1;
816 static constexpr int kHasSmallDexPcBitOffset = kHasSmallDexRegisterMapBitOffset + 1;
817 static constexpr int kHasSmallNativePcBitOffset = kHasSmallDexPcBitOffset + 1;
818
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100819 MemoryRegion GetStackMaps() const {
820 return region_.size() == 0
821 ? MemoryRegion()
Roland Levillain29ba1b02015-03-13 11:45:07 +0000822 : region_.Subregion(kFixedSize, StackMapsSize());
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100823 }
824
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000825 // Compute the size of a Dex register map starting at offset `origin` in
826 // `region_` and containing `number_of_dex_registers` locations.
827 size_t ComputeDexRegisterMapSize(uint32_t origin, uint32_t number_of_dex_registers) const {
828 // TODO: Ideally, we would like to use art::DexRegisterMap::Size or
829 // art::DexRegisterMap::FindLocationOffset, but the DexRegisterMap is not
830 // yet built. Try to factor common code.
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000831 size_t offset =
832 origin + DexRegisterMap::GetDexRegisterMapLocationsOffset(number_of_dex_registers);
833
834 // Create a temporary DexRegisterMap to be able to call DexRegisterMap.IsDexRegisterLive.
835 DexRegisterMap only_live_mask(MemoryRegion(region_.Subregion(origin, offset - origin)));
836
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000837 // Skip the first `number_of_dex_registers - 1` entries.
838 for (uint16_t i = 0; i < number_of_dex_registers; ++i) {
Nicolas Geoffrayfead4e42015-03-13 14:39:40 +0000839 if (only_live_mask.IsDexRegisterLive(i)) {
840 // Read the first next byte and inspect its first 3 bits to decide
841 // whether it is a short or a large location.
842 DexRegisterMap::ShortLocation first_byte =
843 region_.LoadUnaligned<DexRegisterMap::ShortLocation>(offset);
844 DexRegisterLocation::Kind kind =
845 DexRegisterMap::ExtractKindFromShortLocation(first_byte);
846 if (DexRegisterLocation::IsShortLocationKind(kind)) {
847 // Short location. Skip the current byte.
848 offset += DexRegisterMap::SingleShortEntrySize();
849 } else {
850 // Large location. Skip the 5 next bytes.
851 offset += DexRegisterMap::SingleLargeEntrySize();
852 }
Roland Levillaina2d8ec62015-03-12 15:25:29 +0000853 }
854 }
855 size_t size = offset - origin;
856 return size;
857 }
858
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100859 MemoryRegion region_;
Nicolas Geoffray39468442014-09-02 15:17:15 +0100860 friend class StackMapStream;
Nicolas Geoffray99ea58c2014-07-02 15:08:17 +0100861};
862
863} // namespace art
864
865#endif // ART_RUNTIME_STACK_MAP_H_