mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 08:44:27 +00:00
6fe15f6df9
This works around bugs in UndefinedBehaviorSanitizer and Clang. Bug: b/28638298 Change-Id: I6be595f9664516019d28017d24559583a1ae3a21 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144354 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
621 lines
21 KiB
C++
621 lines
21 KiB
C++
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_DEOPT_INSTRUCTIONS_H_
|
|
#define RUNTIME_VM_DEOPT_INSTRUCTIONS_H_
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#include "vm/allocation.h"
|
|
#include "vm/code_descriptors.h"
|
|
#include "vm/compiler/backend/flow_graph_compiler.h"
|
|
#include "vm/compiler/backend/locations.h"
|
|
#include "vm/deferred_objects.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/object.h"
|
|
#include "vm/runtime_entry.h"
|
|
#include "vm/stack_frame.h"
|
|
#include "vm/thread.h"
|
|
|
|
namespace dart {
|
|
|
|
class Location;
|
|
class Value;
|
|
class MaterializeObjectInstr;
|
|
class StackFrame;
|
|
class TimelineEvent;
|
|
|
|
// Holds all data relevant for execution of deoptimization instructions.
|
|
// Structure is allocated in C-heap.
|
|
class DeoptContext {
|
|
public:
|
|
enum DestFrameOptions {
|
|
kDestIsOriginalFrame, // Replace the original frame with deopt frame.
|
|
kDestIsAllocated // Write deopt frame to a buffer.
|
|
};
|
|
|
|
// If 'deoptimizing_code' is false, only frame is being deoptimized.
|
|
DeoptContext(const StackFrame* frame,
|
|
const Code& code,
|
|
DestFrameOptions dest_options,
|
|
fpu_register_t* fpu_registers,
|
|
intptr_t* cpu_registers,
|
|
bool is_lazy_deopt,
|
|
bool deoptimizing_code);
|
|
virtual ~DeoptContext();
|
|
|
|
// Returns the offset of the dest fp from the dest sp. Used in
|
|
// runtime code to adjust the stack size before deoptimization.
|
|
intptr_t DestStackAdjustment() const;
|
|
|
|
intptr_t* GetSourceFrameAddressAt(intptr_t index) const {
|
|
ASSERT(source_frame_ != NULL);
|
|
ASSERT((0 <= index) && (index < source_frame_size_));
|
|
// Convert FP relative index to SP relative one.
|
|
index = source_frame_size_ - 1 - index;
|
|
return &source_frame_[index];
|
|
}
|
|
|
|
// Returns index in stack slot notation where -1 is the first argument
|
|
intptr_t GetStackSlot(intptr_t index) const {
|
|
ASSERT((0 <= index) && (index < source_frame_size_));
|
|
index -= num_args_;
|
|
return index < 0 ? index : index - kDartFrameFixedSize;
|
|
}
|
|
|
|
intptr_t GetSourceFp() const;
|
|
intptr_t GetSourcePp() const;
|
|
intptr_t GetSourcePc() const;
|
|
|
|
intptr_t GetCallerFp() const;
|
|
void SetCallerFp(intptr_t callers_fp);
|
|
|
|
ObjectPtr ObjectAt(intptr_t index) const {
|
|
const ObjectPool& object_pool = ObjectPool::Handle(object_pool_);
|
|
return object_pool.ObjectAt(index);
|
|
}
|
|
|
|
intptr_t RegisterValue(Register reg) const {
|
|
ASSERT(reg >= 0);
|
|
ASSERT(reg < kNumberOfCpuRegisters);
|
|
ASSERT(cpu_registers_ != NULL);
|
|
return cpu_registers_[reg];
|
|
}
|
|
|
|
double FpuRegisterValue(FpuRegister reg) const {
|
|
ASSERT(FlowGraphCompiler::SupportsUnboxedDoubles());
|
|
ASSERT(fpu_registers_ != NULL);
|
|
ASSERT(reg >= 0);
|
|
ASSERT(reg < kNumberOfFpuRegisters);
|
|
return *reinterpret_cast<double*>(&fpu_registers_[reg]);
|
|
}
|
|
|
|
simd128_value_t FpuRegisterValueAsSimd128(FpuRegister reg) const {
|
|
ASSERT(FlowGraphCompiler::SupportsUnboxedSimd128());
|
|
ASSERT(fpu_registers_ != NULL);
|
|
ASSERT(reg >= 0);
|
|
ASSERT(reg < kNumberOfFpuRegisters);
|
|
const float* address = reinterpret_cast<float*>(&fpu_registers_[reg]);
|
|
return simd128_value_t().readFrom(address);
|
|
}
|
|
|
|
// Return base pointer for the given frame (either source or destination).
|
|
// Base pointer points to the slot with the lowest address in the frame
|
|
// including incoming arguments and artificial deoptimization frame
|
|
// on top of it.
|
|
// Note: artificial frame created by the deoptimization stub is considered
|
|
// part of the frame because it contains saved caller PC and FP that
|
|
// deoptimization will fill in.
|
|
intptr_t* FrameBase(const StackFrame* frame) {
|
|
// SP of the deoptimization frame is the lowest slot because
|
|
// stack is growing downwards.
|
|
return reinterpret_cast<intptr_t*>(frame->sp() -
|
|
(kDartFrameFixedSize * kWordSize));
|
|
}
|
|
|
|
void set_dest_frame(const StackFrame* frame) {
|
|
ASSERT(frame != NULL && dest_frame_ == NULL);
|
|
dest_frame_ = FrameBase(frame);
|
|
}
|
|
|
|
Thread* thread() const { return thread_; }
|
|
Zone* zone() const { return thread_->zone(); }
|
|
|
|
intptr_t source_frame_size() const { return source_frame_size_; }
|
|
intptr_t dest_frame_size() const { return dest_frame_size_; }
|
|
|
|
CodePtr code() const { return code_; }
|
|
|
|
bool is_lazy_deopt() const { return is_lazy_deopt_; }
|
|
|
|
bool deoptimizing_code() const { return deoptimizing_code_; }
|
|
|
|
ICData::DeoptReasonId deopt_reason() const { return deopt_reason_; }
|
|
bool HasDeoptFlag(ICData::DeoptFlags flag) {
|
|
return (deopt_flags_ & flag) != 0;
|
|
}
|
|
|
|
TypedDataPtr deopt_info() const { return deopt_info_; }
|
|
|
|
// Fills the destination frame but defers materialization of
|
|
// objects.
|
|
void FillDestFrame();
|
|
|
|
// Convert deoptimization instructions to a list of moves that need
|
|
// to be executed when entering catch entry block from this deoptimization
|
|
// point.
|
|
const CatchEntryMoves* ToCatchEntryMoves(intptr_t num_vars);
|
|
|
|
// Materializes all deferred objects. Returns the total number of
|
|
// artificial arguments used during deoptimization.
|
|
intptr_t MaterializeDeferredObjects();
|
|
|
|
ArrayPtr DestFrameAsArray();
|
|
|
|
void VisitObjectPointers(ObjectPointerVisitor* visitor);
|
|
|
|
void DeferMaterializedObjectRef(intptr_t idx, intptr_t* slot) {
|
|
deferred_slots_ = new DeferredObjectRef(
|
|
idx, reinterpret_cast<ObjectPtr*>(slot), deferred_slots_);
|
|
}
|
|
|
|
void DeferMaterialization(double value, DoublePtr* slot) {
|
|
deferred_slots_ = new DeferredDouble(
|
|
value, reinterpret_cast<ObjectPtr*>(slot), deferred_slots_);
|
|
}
|
|
|
|
void DeferMintMaterialization(int64_t value, MintPtr* slot) {
|
|
deferred_slots_ = new DeferredMint(
|
|
value, reinterpret_cast<ObjectPtr*>(slot), deferred_slots_);
|
|
}
|
|
|
|
void DeferMaterialization(simd128_value_t value, Float32x4Ptr* slot) {
|
|
deferred_slots_ = new DeferredFloat32x4(
|
|
value, reinterpret_cast<ObjectPtr*>(slot), deferred_slots_);
|
|
}
|
|
|
|
void DeferMaterialization(simd128_value_t value, Float64x2Ptr* slot) {
|
|
deferred_slots_ = new DeferredFloat64x2(
|
|
value, reinterpret_cast<ObjectPtr*>(slot), deferred_slots_);
|
|
}
|
|
|
|
void DeferMaterialization(simd128_value_t value, Int32x4Ptr* slot) {
|
|
deferred_slots_ = new DeferredInt32x4(
|
|
value, reinterpret_cast<ObjectPtr*>(slot), deferred_slots_);
|
|
}
|
|
|
|
void DeferRetAddrMaterialization(intptr_t index,
|
|
intptr_t deopt_id,
|
|
intptr_t* slot) {
|
|
deferred_slots_ = new DeferredRetAddr(
|
|
index, deopt_id, reinterpret_cast<ObjectPtr*>(slot), deferred_slots_);
|
|
}
|
|
|
|
void DeferPcMarkerMaterialization(intptr_t index, intptr_t* slot) {
|
|
deferred_slots_ = new DeferredPcMarker(
|
|
index, reinterpret_cast<ObjectPtr*>(slot), deferred_slots_);
|
|
}
|
|
|
|
void DeferPpMaterialization(intptr_t index, ObjectPtr* slot) {
|
|
deferred_slots_ = new DeferredPp(index, slot, deferred_slots_);
|
|
}
|
|
|
|
DeferredObject* GetDeferredObject(intptr_t idx) const {
|
|
return deferred_objects_[idx];
|
|
}
|
|
|
|
intptr_t num_args() const { return num_args_; }
|
|
|
|
private:
|
|
intptr_t* GetDestFrameAddressAt(intptr_t index) const {
|
|
ASSERT(dest_frame_ != NULL);
|
|
ASSERT((0 <= index) && (index < dest_frame_size_));
|
|
return &dest_frame_[index];
|
|
}
|
|
|
|
void PrepareForDeferredMaterialization(intptr_t count) {
|
|
if (count > 0) {
|
|
deferred_objects_ = new DeferredObject*[count];
|
|
deferred_objects_count_ = count;
|
|
}
|
|
}
|
|
|
|
// Sets the materialized value for some deferred object.
|
|
//
|
|
// Claims ownership of the memory for 'object'.
|
|
void SetDeferredObjectAt(intptr_t idx, DeferredObject* object) {
|
|
deferred_objects_[idx] = object;
|
|
}
|
|
|
|
intptr_t DeferredObjectsCount() const { return deferred_objects_count_; }
|
|
|
|
CodePtr code_;
|
|
ObjectPoolPtr object_pool_;
|
|
TypedDataPtr deopt_info_;
|
|
bool dest_frame_is_allocated_;
|
|
intptr_t* dest_frame_;
|
|
intptr_t dest_frame_size_;
|
|
bool source_frame_is_allocated_;
|
|
intptr_t* source_frame_;
|
|
intptr_t source_frame_size_;
|
|
intptr_t* cpu_registers_;
|
|
fpu_register_t* fpu_registers_;
|
|
intptr_t num_args_;
|
|
ICData::DeoptReasonId deopt_reason_;
|
|
uint32_t deopt_flags_;
|
|
intptr_t caller_fp_;
|
|
Thread* thread_;
|
|
int64_t deopt_start_micros_;
|
|
|
|
DeferredSlot* deferred_slots_;
|
|
|
|
intptr_t deferred_objects_count_;
|
|
DeferredObject** deferred_objects_;
|
|
|
|
const bool is_lazy_deopt_;
|
|
const bool deoptimizing_code_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(DeoptContext);
|
|
};
|
|
|
|
// Represents one deopt instruction, e.g, setup return address, store object,
|
|
// store register, etc. The target is defined by instruction's position in
|
|
// the deopt-info array.
|
|
class DeoptInstr : public ZoneAllocated {
|
|
public:
|
|
enum Kind {
|
|
kRetAddress,
|
|
kConstant,
|
|
kWord,
|
|
kDouble,
|
|
kFloat32x4,
|
|
kFloat64x2,
|
|
kInt32x4,
|
|
// Mints are split into low and high words on 32-bit architectures. Each
|
|
// word can be in a register or stack slot. Note Mint pairs are only
|
|
// used on 32-bit architectures.
|
|
kMintPair,
|
|
// Mints are held in one word on 64-bit architectures.
|
|
kMint,
|
|
kInt32,
|
|
kUint32,
|
|
kPcMarker,
|
|
kPp,
|
|
kCallerFp,
|
|
kCallerPp,
|
|
kCallerPc,
|
|
kMaterializedObjectRef,
|
|
kMaterializeObject
|
|
};
|
|
|
|
static DeoptInstr* Create(intptr_t kind_as_int, intptr_t source_index);
|
|
|
|
DeoptInstr() {}
|
|
virtual ~DeoptInstr() {}
|
|
|
|
virtual const char* ToCString() const {
|
|
const char* args = ArgumentsToCString();
|
|
if (args != NULL) {
|
|
return Thread::Current()->zone()->PrintToString(
|
|
"%s(%s)", KindToCString(kind()), args);
|
|
} else {
|
|
return KindToCString(kind());
|
|
}
|
|
}
|
|
|
|
virtual void Execute(DeoptContext* deopt_context, intptr_t* dest_addr) = 0;
|
|
|
|
virtual CatchEntryMove ToCatchEntryMove(DeoptContext* deopt_context,
|
|
intptr_t dest_slot) {
|
|
UNREACHABLE();
|
|
return CatchEntryMove();
|
|
}
|
|
|
|
virtual DeoptInstr::Kind kind() const = 0;
|
|
|
|
bool Equals(const DeoptInstr& other) const {
|
|
return (kind() == other.kind()) && (source_index() == other.source_index());
|
|
}
|
|
|
|
// Get the code and return address which is encoded in this
|
|
// kRetAfterAddress deopt instruction.
|
|
static uword GetRetAddress(DeoptInstr* instr,
|
|
const ObjectPool& object_pool,
|
|
Code* code);
|
|
|
|
// Return number of initialized fields in the object that will be
|
|
// materialized by kMaterializeObject instruction.
|
|
static intptr_t GetFieldCount(DeoptInstr* instr) {
|
|
ASSERT(instr->kind() == DeoptInstr::kMaterializeObject);
|
|
return instr->source_index();
|
|
}
|
|
|
|
protected:
|
|
friend class DeoptInfoBuilder;
|
|
|
|
virtual intptr_t source_index() const = 0;
|
|
|
|
virtual const char* ArgumentsToCString() const { return NULL; }
|
|
|
|
private:
|
|
static const char* KindToCString(Kind kind);
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(DeoptInstr);
|
|
};
|
|
|
|
// Helper class that allows to read a value of the given register from
|
|
// the DeoptContext as the specified type.
|
|
// It calls different method depending on which kind of register (cpu/fpu) and
|
|
// destination types are specified.
|
|
template <typename RegisterType, typename DestinationType>
|
|
struct RegisterReader;
|
|
|
|
template <typename T>
|
|
struct RegisterReader<Register, T> {
|
|
static intptr_t Read(DeoptContext* context, Register reg) {
|
|
return context->RegisterValue(reg);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct RegisterReader<FpuRegister, double> {
|
|
static double Read(DeoptContext* context, FpuRegister reg) {
|
|
return context->FpuRegisterValue(reg);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct RegisterReader<FpuRegister, simd128_value_t> {
|
|
static simd128_value_t Read(DeoptContext* context, FpuRegister reg) {
|
|
return context->FpuRegisterValueAsSimd128(reg);
|
|
}
|
|
};
|
|
|
|
// Class that encapsulates reading and writing of values that were either in
|
|
// the registers in the optimized code or were spilled from those registers
|
|
// to the stack.
|
|
template <typename RegisterType>
|
|
class RegisterSource {
|
|
public:
|
|
enum Kind {
|
|
// Spilled register source represented as its spill slot.
|
|
kStackSlot = 0,
|
|
// Register source represented as its register index.
|
|
kRegister = 1
|
|
};
|
|
|
|
explicit RegisterSource(intptr_t source_index)
|
|
: source_index_(source_index) {}
|
|
|
|
RegisterSource(Kind kind, intptr_t index)
|
|
: source_index_(KindField::encode(kind) |
|
|
IndexFieldLayout::encode(index)) {}
|
|
|
|
template <typename T>
|
|
T Value(DeoptContext* context) const {
|
|
if (is_register()) {
|
|
return static_cast<T>(
|
|
RegisterReader<RegisterType, T>::Read(context, reg()));
|
|
} else {
|
|
return *reinterpret_cast<T*>(
|
|
context->GetSourceFrameAddressAt(raw_index()));
|
|
}
|
|
}
|
|
|
|
intptr_t StackSlot(DeoptContext* context) const {
|
|
ASSERT(!is_register());
|
|
return context->GetStackSlot(raw_index());
|
|
}
|
|
|
|
intptr_t source_index() const { return source_index_; }
|
|
|
|
const char* ToCString() const {
|
|
if (is_register()) {
|
|
return Name(reg());
|
|
} else {
|
|
return Thread::Current()->zone()->PrintToString("s%" Pd "", raw_index());
|
|
}
|
|
}
|
|
|
|
private:
|
|
class KindField : public BitField<intptr_t, intptr_t, 0, 1> {};
|
|
class IndexFieldLayout
|
|
: public BitField<intptr_t, intptr_t, 1, kBitsPerWord - 1> {};
|
|
|
|
bool is_register() const {
|
|
return KindField::decode(source_index_) == kRegister;
|
|
}
|
|
intptr_t raw_index() const { return IndexFieldLayout::decode(source_index_); }
|
|
|
|
RegisterType reg() const { return static_cast<RegisterType>(raw_index()); }
|
|
|
|
static const char* Name(Register reg) {
|
|
return RegisterNames::RegisterName(reg);
|
|
}
|
|
|
|
static const char* Name(FpuRegister fpu_reg) {
|
|
return RegisterNames::FpuRegisterName(fpu_reg);
|
|
}
|
|
|
|
const intptr_t source_index_;
|
|
};
|
|
|
|
typedef RegisterSource<Register> CpuRegisterSource;
|
|
typedef RegisterSource<FpuRegister> FpuRegisterSource;
|
|
|
|
// Builds a deoptimization info table, one DeoptInfo at a time. Call AddXXX
|
|
// methods in the order of their target, starting wih deoptimized code
|
|
// continuation pc and ending with the first argument of the deoptimized
|
|
// code. Call CreateDeoptInfo to write the accumulated instructions into
|
|
// the heap and reset the builder's internal state for the next DeoptInfo.
|
|
class DeoptInfoBuilder : public ValueObject {
|
|
public:
|
|
DeoptInfoBuilder(Zone* zone,
|
|
const intptr_t num_args,
|
|
compiler::Assembler* assembler);
|
|
|
|
// Return address before instruction.
|
|
void AddReturnAddress(const Function& function,
|
|
intptr_t deopt_id,
|
|
intptr_t dest_index);
|
|
|
|
// Copy from optimized frame to unoptimized.
|
|
void AddCopy(Value* value, const Location& source_loc, intptr_t dest_index);
|
|
void AddPcMarker(const Function& function, intptr_t dest_index);
|
|
void AddPp(const Function& function, intptr_t dest_index);
|
|
void AddCallerFp(intptr_t dest_index);
|
|
void AddCallerPp(intptr_t dest_index);
|
|
void AddCallerPc(intptr_t dest_index);
|
|
|
|
// Add object to be materialized. Emit kMaterializeObject instruction.
|
|
void AddMaterialization(MaterializeObjectInstr* mat);
|
|
|
|
// For every materialized object emit instructions describing data required
|
|
// for materialization: class of the instance to allocate and field-value
|
|
// pairs for initialization.
|
|
// Emitted instructions are expected to follow fixed size section of frame
|
|
// emitted first. This way they become a part of the bottom-most deoptimized
|
|
// frame and are discoverable by GC.
|
|
// At deoptimization they will be removed by the stub at the very end:
|
|
// after they were used to materialize objects.
|
|
// Returns the index of the next stack slot. Used for verification.
|
|
intptr_t EmitMaterializationArguments(intptr_t dest_index);
|
|
|
|
TypedDataPtr CreateDeoptInfo(const Array& deopt_table);
|
|
|
|
// Mark the actual start of the frame description after all materialization
|
|
// instructions were emitted. Used for verification purposes.
|
|
void MarkFrameStart() {
|
|
ASSERT(frame_start_ == -1);
|
|
frame_start_ = instructions_.length();
|
|
}
|
|
|
|
private:
|
|
friend class CompilerDeoptInfo; // For current_info_number_.
|
|
|
|
class TrieNode;
|
|
|
|
CpuRegisterSource ToCpuRegisterSource(const Location& loc);
|
|
FpuRegisterSource ToFpuRegisterSource(
|
|
const Location& loc,
|
|
Location::Kind expected_stack_slot_kind);
|
|
|
|
intptr_t FindOrAddObjectInTable(const Object& obj) const;
|
|
intptr_t FindMaterialization(MaterializeObjectInstr* mat) const;
|
|
intptr_t CalculateStackIndex(const Location& source_loc) const;
|
|
|
|
intptr_t FrameSize() const {
|
|
ASSERT(frame_start_ != -1);
|
|
const intptr_t frame_size = instructions_.length() - frame_start_;
|
|
ASSERT(frame_size >= 0);
|
|
return frame_size;
|
|
}
|
|
|
|
void AddConstant(const Object& obj, intptr_t dest_index);
|
|
|
|
Zone* zone() const { return zone_; }
|
|
|
|
Zone* zone_;
|
|
|
|
GrowableArray<DeoptInstr*> instructions_;
|
|
const intptr_t num_args_;
|
|
compiler::Assembler* assembler_;
|
|
|
|
// Used to compress entries by sharing suffixes.
|
|
TrieNode* trie_root_;
|
|
intptr_t current_info_number_;
|
|
|
|
intptr_t frame_start_;
|
|
GrowableArray<MaterializeObjectInstr*> materializations_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(DeoptInfoBuilder);
|
|
};
|
|
|
|
// Utilities for managing the deopt table and its entries. The table is
|
|
// stored in an Array in the heap. It consists of triples of (PC offset,
|
|
// info, reason). Elements of each entry are stored consecutively in the
|
|
// array.
|
|
// TODO(vegorov): consider compressing the whole table into a single TypedData
|
|
// object.
|
|
class DeoptTable : public AllStatic {
|
|
public:
|
|
// Return the array size in elements for a given number of table entries.
|
|
static intptr_t SizeFor(intptr_t length);
|
|
|
|
// Set the entry at the given index into the table (not an array index).
|
|
static void SetEntry(const Array& table,
|
|
intptr_t index,
|
|
const Smi& offset,
|
|
const TypedData& info,
|
|
const Smi& reason_and_flags);
|
|
|
|
// Return the length of the table in entries.
|
|
static intptr_t GetLength(const Array& table);
|
|
|
|
// Set the output parameters (offset, info, reason) to the entry values at
|
|
// the index into the table (not an array index).
|
|
static void GetEntry(const Array& table,
|
|
intptr_t index,
|
|
Smi* offset,
|
|
TypedData* info,
|
|
Smi* reason_and_flags);
|
|
|
|
static SmiPtr EncodeReasonAndFlags(ICData::DeoptReasonId reason,
|
|
uint32_t flags) {
|
|
return Smi::New(ReasonField::encode(reason) | FlagsField::encode(flags));
|
|
}
|
|
|
|
class ReasonField : public BitField<intptr_t, ICData::DeoptReasonId, 0, 8> {};
|
|
class FlagsField : public BitField<intptr_t, uint32_t, 8, 8> {};
|
|
|
|
private:
|
|
static const intptr_t kEntrySize = 3;
|
|
};
|
|
|
|
// Holds deopt information at one deoptimization point. The information consists
|
|
// of two parts:
|
|
// - first a prefix consisting of kMaterializeObject instructions describing
|
|
// objects which had their allocation removed as part of AllocationSinking
|
|
// pass and have to be materialized;
|
|
// - followed by a list of DeoptInstr objects, specifying transformation
|
|
// information for each slot in unoptimized frame(s).
|
|
// Arguments for object materialization (class of instance to be allocated and
|
|
// field-value pairs) are added as artificial slots to the expression stack
|
|
// of the bottom-most frame. They are removed from the stack at the very end
|
|
// of deoptimization by the deoptimization stub.
|
|
class DeoptInfo : public AllStatic {
|
|
public:
|
|
// Size of the frame part of the translation not counting kMaterializeObject
|
|
// instructions in the prefix.
|
|
static intptr_t FrameSize(const TypedData& packed);
|
|
|
|
// Returns the number of kMaterializeObject instructions in the prefix.
|
|
static intptr_t NumMaterializations(const GrowableArray<DeoptInstr*>&);
|
|
|
|
// Unpack the entire translation into an array of deoptimization
|
|
// instructions. This copies any shared suffixes into the array.
|
|
static void Unpack(const Array& table,
|
|
const TypedData& packed,
|
|
GrowableArray<DeoptInstr*>* instructions);
|
|
|
|
// Size of the frame part of the translation not counting kMaterializeObject
|
|
// instructions in the prefix.
|
|
static const char* ToCString(const Array& table, const TypedData& packed);
|
|
|
|
// Returns true iff decompression yields the same instructions as the
|
|
// original.
|
|
static bool VerifyDecompression(const GrowableArray<DeoptInstr*>& original,
|
|
const Array& deopt_table,
|
|
const TypedData& packed);
|
|
|
|
private:
|
|
static void UnpackInto(const Array& table,
|
|
const TypedData& packed,
|
|
GrowableArray<DeoptInstr*>* instructions,
|
|
intptr_t length);
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
#endif // RUNTIME_VM_DEOPT_INSTRUCTIONS_H_
|