[vm/compiler] Support materializing unboxed variables when entering catch.
Previously we tried to rely on the assumption that all variables would be boxed - so the machinery for setting correct catch-entry state only supported tagged values and constants. However this both leads to worse code and is not entirely correct assumption. This also: - renames various confusingly named classes: we move away from talking about "catch entry state" to "catch entry moves" - because we only record a subset of moves that needs to be performed and that does not describe the whole state; - refactors a bunch of associated code to be more readable and maintainable; - adds documentation about catch implementation in optimized code to runtime/docs/compiler; Fixes https://github.com/flutter/flutter/issues/21685. Change-Id: I03ae361a1bb7710acbd9f661ae014e663a163c59 Reviewed-on: https://dart-review.googlesource.com/74860 Commit-Queue: Vyacheslav Egorov <vegorov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
eec96f9076
commit
229d793602
@@ -0,0 +1,92 @@
|
||||
# Exceptions Implementation
|
||||
|
||||
This page describes how exceptions throwing and catching is implemented in the
|
||||
VM.
|
||||
|
||||
## Intermediate Language
|
||||
|
||||
Dart VM's IL **does not** explicitly represent exceptional control flow in its
|
||||
flow graph, there are **no** explicit exceptional edges connecting potentially
|
||||
throwing instructions (e.g. calls) with corresponding catch blocks. Instead this
|
||||
connection is defined at the block level: all exceptions that occur in any block
|
||||
with the given `try_index` will be caught by `CatchBlockEntry` with the equal
|
||||
`catch_try_index`.
|
||||
|
||||

|
||||
|
||||
For optimized code this means that data flow associated with exceptional control
|
||||
flow is also represented implicitly: due to the absence of explicit exceptional
|
||||
edges the data flow can't be represented using explicit phi-functions. Instead
|
||||
in optimized code each `CatchBlockEntry` is treated almost as if it was an
|
||||
independent entry into the function: for each variable `v` `CatchBlockEntry`
|
||||
will contain a `Parameter(...)` instruction restoring variable state at catch
|
||||
entry from a fixed location on the stack. When an exception is thrown runtime
|
||||
system takes care of populating these stack slots with right values - current
|
||||
state of corresponding local variables. It's easy to see a parallel between
|
||||
these `Parameter(...)` instructions and `Phi(...)` instructions that would be
|
||||
used if exception control flow would be explicit.
|
||||
|
||||

|
||||
|
||||
How does runtime system populate stack slots corresponding to these
|
||||
`Parameter(...)` instructions? During compilation necessary information is
|
||||
available in _deoptimization environment_ attached to the instruction. This
|
||||
environment encodes the state of local variables in terms of SSA values i.e. if
|
||||
we need to reconstruct unoptimized frame which SSA value should be stored into
|
||||
the given local variable (see [Optimized
|
||||
IL](compiler-pipeline-overview.md#optimized-il) for
|
||||
an overview). However the way we use these information for exception handling is
|
||||
slightly different in JIT and AOT modes.
|
||||
|
||||
### AOT mode
|
||||
|
||||
AOT mode does not support deoptimization and thus AOT compiler does not
|
||||
associate any deoptimization metadata with generated code. Instead
|
||||
deoptimization environments associated with instructions that can throw are
|
||||
converted into `CatchEntryMoves` metadata during code generation and resulting
|
||||
metadata is stored `RawCode::catch_entry_moves_maps_` in a compressed form.
|
||||
|
||||
`CatchEntryMoves` is essentially a sequence of moves which runtime needs to
|
||||
perform to create the state that catch entry expects. There are three types of
|
||||
moves:
|
||||
|
||||
* `*(FP + Dst) <- ObjectPool[PoolIndex]` - a move of a constant from an object
|
||||
pool;
|
||||
* `*(FP + Dst) <- *(FP + Src)` - a move of a tagged value;
|
||||
* `*(FP + Dst) <- Box<Rep>(*(FP + Src))` - a boxing operation for an untagged
|
||||
value;
|
||||
|
||||
When an exception is caught runtime decompresses the metadata associated with the
|
||||
call site which has thrown an exception and uses it to prepare the state of the
|
||||
stack for the catch block entry. See
|
||||
`ExceptionHandlerFinder::{ReadCompressedCatchEntryMoves, ExecuteCatchEntryMoves}`.
|
||||
|
||||
NOTE: See [this
|
||||
design/motivation](https://docs.google.com/a/google.com/document/d/1_vX8VkvHVA1Om7jjONiWLA325k_JmSZuvVClet-x-xM/edit?usp=sharing)
|
||||
document for `CatchEntryMoves` metadata
|
||||
|
||||
### JIT mode
|
||||
|
||||
JIT mode heavily relies on deoptimization and all call instructions have (lazy)
|
||||
deoptimization environments associated with them. These environments are
|
||||
converted to [deoptimization
|
||||
instructions](deoptimization.md#in-optimized-code)
|
||||
during code generation and stored on the `Code` object.
|
||||
|
||||
When an exception is caught the runtime system converts deoptimization
|
||||
environment associated with the call site that threw an exception into
|
||||
`CatchEntryMoves` and then uses it to prepare the state of the stack for the
|
||||
catch block entry. See `ExceptionHandlerFinder::{GetCatchEntryMovesFromDeopt, ExecuteCatchEntryMoves}`.
|
||||
|
||||
Constructing `CatchEntryMoves` dynamically from deoptimization instructions
|
||||
allows to avoid unnecessary duplication of the metadata and save memory: as
|
||||
deoptimization environments contain all information necessary for constructing
|
||||
correct stack state.
|
||||
|
||||
IMPORTANT: There is a subtle difference between DBC and other architectures with
|
||||
respect to catch block entry state. On normal architectures `Parameter(i)` at
|
||||
catch entry would be associated with the same stack space that would be used to
|
||||
store variable with index `i`. On DBC however at catch entry `Parameter(i)`
|
||||
would be allocated to a separate scratch space at the very top of the register
|
||||
space. See `FlowGraphAllocator::ProcessInitialDefinition` and
|
||||
`FlowGraphCompiler::CatchEntryRegForVariable`.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2018, 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.
|
||||
// VMOptions=--no-background-compilation --enable-inlining-annotations --optimization-counter-threshold=100
|
||||
|
||||
// Verify that runtime correctly materializes unboxed variables on the catch
|
||||
// entry in optimized code.
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
const NeverInline = "NeverInline";
|
||||
|
||||
@NeverInline
|
||||
void testThrow(bool shouldThrow) {
|
||||
var dbl = 0.0;
|
||||
var i32 = 0;
|
||||
var i64 = 0;
|
||||
var f32x4 = new Float32x4.zero();
|
||||
var f64x2 = new Float64x2.zero();
|
||||
var i32x4 = new Int32x4(0, 0, 0, 0);
|
||||
try {
|
||||
for (var i = 0; i < 100; i++) {
|
||||
dbl += i;
|
||||
i32 = i | 0x70000000;
|
||||
i64 = i | 0x80000000;
|
||||
final d = i.toDouble();
|
||||
f32x4 += new Float32x4(d, -d, d, -d);
|
||||
f64x2 += new Float64x2(d, -d);
|
||||
i32x4 += new Int32x4(-i, i, -i, i);
|
||||
if (shouldThrow && i == 50) {
|
||||
throw "";
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (shouldThrow) {
|
||||
Expect.equals(1275.0, dbl);
|
||||
Expect.equals(0x70000000 | 50, i32);
|
||||
Expect.equals(0x80000000 | 50, i64);
|
||||
Expect.listEquals([1275.0, -1275.0, 1275.0, -1275.0],
|
||||
[f32x4.x, f32x4.y, f32x4.z, f32x4.w]);
|
||||
Expect.listEquals([1275.0, -1275.0], [f64x2.x, f64x2.y]);
|
||||
Expect.listEquals(
|
||||
[-1275, 1275, -1275, 1275], [i32x4.x, i32x4.y, i32x4.z, i32x4.w]);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
for (var i = 0; i < 100; i++) testThrow(false);
|
||||
testThrow(true);
|
||||
}
|
||||
@@ -60,6 +60,9 @@ cc/DartAPI_New: Fail # Issue #33041
|
||||
dart/redirection_type_shuffling_test/00: RuntimeError, Pass
|
||||
dart/redirection_type_shuffling_test/none: RuntimeError
|
||||
|
||||
[ $runtime != vm && $runtime != dart_precompiled ]
|
||||
dart/catch_entry_state: SkipByDesign
|
||||
|
||||
[ $compiler != dartk && $compiler != dartkb ]
|
||||
cc/IsolateReload_KernelIncrementalCompile: SkipByDesign
|
||||
cc/IsolateReload_KernelIncrementalCompileAppAndLib: SkipByDesign
|
||||
|
||||
@@ -1666,7 +1666,7 @@ class CodeSerializationCluster : public SerializationCluster {
|
||||
s->Push(code->ptr()->exception_handlers_);
|
||||
s->Push(code->ptr()->pc_descriptors_);
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
|
||||
s->Push(code->ptr()->catch_entry_.catch_entry_state_maps_);
|
||||
s->Push(code->ptr()->catch_entry_.catch_entry_moves_maps_);
|
||||
#else
|
||||
s->Push(code->ptr()->catch_entry_.variables_);
|
||||
#endif
|
||||
@@ -1727,7 +1727,7 @@ class CodeSerializationCluster : public SerializationCluster {
|
||||
s->WriteRef(code->ptr()->exception_handlers_);
|
||||
s->WriteRef(code->ptr()->pc_descriptors_);
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
|
||||
s->WriteRef(code->ptr()->catch_entry_.catch_entry_state_maps_);
|
||||
s->WriteRef(code->ptr()->catch_entry_.catch_entry_moves_maps_);
|
||||
#else
|
||||
s->WriteRef(code->ptr()->catch_entry_.variables_);
|
||||
#endif
|
||||
@@ -1810,7 +1810,7 @@ class CodeDeserializationCluster : public DeserializationCluster {
|
||||
code->ptr()->pc_descriptors_ =
|
||||
reinterpret_cast<RawPcDescriptors*>(d->ReadRef());
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
|
||||
code->ptr()->catch_entry_.catch_entry_state_maps_ =
|
||||
code->ptr()->catch_entry_.catch_entry_moves_maps_ =
|
||||
reinterpret_cast<RawTypedData*>(d->ReadRef());
|
||||
#else
|
||||
code->ptr()->catch_entry_.variables_ =
|
||||
|
||||
@@ -112,18 +112,19 @@ RawExceptionHandlers* ExceptionHandlerList::FinalizeExceptionHandlers(
|
||||
return handlers.raw();
|
||||
}
|
||||
|
||||
static uint8_t* zone_allocator(uint8_t* ptr,
|
||||
intptr_t old_size,
|
||||
intptr_t new_size) {
|
||||
static uint8_t* ZoneAllocator(uint8_t* ptr,
|
||||
intptr_t old_size,
|
||||
intptr_t new_size) {
|
||||
Zone* zone = Thread::Current()->zone();
|
||||
return zone->Realloc<uint8_t>(ptr, old_size, new_size);
|
||||
}
|
||||
|
||||
class CatchEntryStateMapBuilder::TrieNode : public ZoneAllocated {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
class CatchEntryMovesMapBuilder::TrieNode : public ZoneAllocated {
|
||||
public:
|
||||
TrieNode() : pair_(), entry_state_offset_(-1) {}
|
||||
TrieNode(CatchEntryStatePair pair, intptr_t index)
|
||||
: pair_(pair), entry_state_offset_(index) {}
|
||||
TrieNode() : move_(), entry_state_offset_(-1) {}
|
||||
TrieNode(CatchEntryMove move, intptr_t index)
|
||||
: move_(move), entry_state_offset_(index) {}
|
||||
|
||||
intptr_t Offset() { return entry_state_offset_; }
|
||||
|
||||
@@ -132,42 +133,36 @@ class CatchEntryStateMapBuilder::TrieNode : public ZoneAllocated {
|
||||
return node;
|
||||
}
|
||||
|
||||
TrieNode* Follow(CatchEntryStatePair next) {
|
||||
TrieNode* Follow(CatchEntryMove next) {
|
||||
for (intptr_t i = 0; i < children_.length(); i++) {
|
||||
if (children_[i]->pair_ == next) return children_[i];
|
||||
if (children_[i]->move_ == next) return children_[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
CatchEntryStatePair pair_;
|
||||
CatchEntryMove move_;
|
||||
const intptr_t entry_state_offset_;
|
||||
GrowableArray<TrieNode*> children_;
|
||||
};
|
||||
|
||||
CatchEntryStateMapBuilder::CatchEntryStateMapBuilder()
|
||||
CatchEntryMovesMapBuilder::CatchEntryMovesMapBuilder()
|
||||
: zone_(Thread::Current()->zone()),
|
||||
root_(new TrieNode()),
|
||||
current_pc_offset_(0),
|
||||
buffer_(NULL),
|
||||
stream_(&buffer_, zone_allocator, 64) {}
|
||||
stream_(&buffer_, ZoneAllocator, 64) {}
|
||||
|
||||
void CatchEntryStateMapBuilder::AppendMove(intptr_t src_slot,
|
||||
intptr_t dest_slot) {
|
||||
moves_.Add(CatchEntryStatePair::FromMove(src_slot, dest_slot));
|
||||
void CatchEntryMovesMapBuilder::Append(const CatchEntryMove& move) {
|
||||
moves_.Add(move);
|
||||
}
|
||||
|
||||
void CatchEntryStateMapBuilder::AppendConstant(intptr_t pool_id,
|
||||
intptr_t dest_slot) {
|
||||
moves_.Add(CatchEntryStatePair::FromConstant(pool_id, dest_slot));
|
||||
}
|
||||
|
||||
void CatchEntryStateMapBuilder::NewMapping(intptr_t pc_offset) {
|
||||
void CatchEntryMovesMapBuilder::NewMapping(intptr_t pc_offset) {
|
||||
moves_.Clear();
|
||||
current_pc_offset_ = pc_offset;
|
||||
}
|
||||
|
||||
void CatchEntryStateMapBuilder::EndMapping() {
|
||||
void CatchEntryMovesMapBuilder::EndMapping() {
|
||||
intptr_t suffix_length = 0;
|
||||
TrieNode* suffix = root_;
|
||||
// Find the largest common suffix, get the last node of the path.
|
||||
@@ -189,8 +184,7 @@ void CatchEntryStateMapBuilder::EndMapping() {
|
||||
// Write the unshared part, adding it to the trie.
|
||||
TrieNode* node = suffix;
|
||||
for (intptr_t i = length - 1; i >= 0; i--) {
|
||||
Writer::Write(&stream_, moves_[i].src);
|
||||
Writer::Write(&stream_, moves_[i].dest);
|
||||
moves_[i].WriteTo(&stream_);
|
||||
|
||||
TrieNode* child = new (zone_) TrieNode(moves_[i], current_offset);
|
||||
node->Insert(child);
|
||||
@@ -198,7 +192,7 @@ void CatchEntryStateMapBuilder::EndMapping() {
|
||||
}
|
||||
}
|
||||
|
||||
RawTypedData* CatchEntryStateMapBuilder::FinalizeCatchEntryStateMap() {
|
||||
RawTypedData* CatchEntryMovesMapBuilder::FinalizeCatchEntryMovesMap() {
|
||||
TypedData& td = TypedData::Handle(TypedData::New(
|
||||
kTypedDataInt8ArrayCid, stream_.bytes_written(), Heap::kOld));
|
||||
NoSafepointScope no_safepoint;
|
||||
@@ -209,6 +203,7 @@ RawTypedData* CatchEntryStateMapBuilder::FinalizeCatchEntryStateMap() {
|
||||
}
|
||||
return td.raw();
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
const TokenPosition CodeSourceMapBuilder::kInitialPosition =
|
||||
TokenPosition(TokenPosition::kDartCodeProloguePos);
|
||||
@@ -230,7 +225,7 @@ CodeSourceMapBuilder::CodeSourceMapBuilder(
|
||||
inlined_functions_(
|
||||
GrowableObjectArray::Handle(GrowableObjectArray::New(Heap::kOld))),
|
||||
buffer_(NULL),
|
||||
stream_(&buffer_, zone_allocator, 64),
|
||||
stream_(&buffer_, ZoneAllocator, 64),
|
||||
stack_traces_only_(stack_traces_only) {
|
||||
buffered_inline_id_stack_.Add(0);
|
||||
buffered_token_pos_stack_.Add(kInitialPosition);
|
||||
|
||||
@@ -144,43 +144,16 @@ class ExceptionHandlerList : public ZoneAllocated {
|
||||
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerList);
|
||||
};
|
||||
|
||||
// An encoded move from stack/constant to stack performed
|
||||
struct CatchEntryStatePair {
|
||||
enum { kCatchEntryStateIsMove = 1, kCatchEntryStateDestShift = 1 };
|
||||
|
||||
intptr_t src, dest;
|
||||
|
||||
static CatchEntryStatePair FromConstant(intptr_t pool_id,
|
||||
intptr_t dest_slot) {
|
||||
CatchEntryStatePair pair;
|
||||
pair.src = pool_id;
|
||||
pair.dest = (dest_slot << kCatchEntryStateDestShift);
|
||||
return pair;
|
||||
}
|
||||
|
||||
static CatchEntryStatePair FromMove(intptr_t src_slot, intptr_t dest_slot) {
|
||||
CatchEntryStatePair pair;
|
||||
pair.src = src_slot;
|
||||
pair.dest =
|
||||
(dest_slot << kCatchEntryStateDestShift) | kCatchEntryStateIsMove;
|
||||
return pair;
|
||||
}
|
||||
|
||||
bool operator==(const CatchEntryStatePair& rhs) {
|
||||
return src == rhs.src && dest == rhs.dest;
|
||||
}
|
||||
};
|
||||
|
||||
// Used to construct CatchEntryState metadata for AoT mode of compilation.
|
||||
class CatchEntryStateMapBuilder : public ZoneAllocated {
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
// Used to construct CatchEntryMoves for the AOT mode of compilation.
|
||||
class CatchEntryMovesMapBuilder : public ZoneAllocated {
|
||||
public:
|
||||
CatchEntryStateMapBuilder();
|
||||
CatchEntryMovesMapBuilder();
|
||||
|
||||
void NewMapping(intptr_t pc_offset);
|
||||
void AppendMove(intptr_t src_slot, intptr_t dest_slot);
|
||||
void AppendConstant(intptr_t pool_id, intptr_t dest_slot);
|
||||
void Append(const CatchEntryMove& move);
|
||||
void EndMapping();
|
||||
RawTypedData* FinalizeCatchEntryStateMap();
|
||||
RawTypedData* FinalizeCatchEntryMovesMap();
|
||||
|
||||
private:
|
||||
class TrieNode;
|
||||
@@ -188,12 +161,13 @@ class CatchEntryStateMapBuilder : public ZoneAllocated {
|
||||
Zone* zone_;
|
||||
TrieNode* root_;
|
||||
intptr_t current_pc_offset_;
|
||||
GrowableArray<CatchEntryStatePair> moves_;
|
||||
GrowableArray<CatchEntryMove> moves_;
|
||||
uint8_t* buffer_;
|
||||
WriteStream stream_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CatchEntryStateMapBuilder);
|
||||
DISALLOW_COPY_AND_ASSIGN(CatchEntryMovesMapBuilder);
|
||||
};
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
// A CodeSourceMap maps from pc offsets to a stack of inlined functions and
|
||||
// their positions. This is encoded as a little bytecode that pushes and pops
|
||||
|
||||
@@ -2709,7 +2709,7 @@ void PrecompileParsedFunctionHelper::FinalizeCompilation(
|
||||
graph_compiler->FinalizeStackMaps(code);
|
||||
graph_compiler->FinalizeVarDescriptors(code);
|
||||
graph_compiler->FinalizeExceptionHandlers(code);
|
||||
graph_compiler->FinalizeCatchEntryStateMap(code);
|
||||
graph_compiler->FinalizeCatchEntryMovesMap(code);
|
||||
graph_compiler->FinalizeStaticCallTargetsTable(code);
|
||||
graph_compiler->FinalizeCodeSourceMap(code);
|
||||
|
||||
|
||||
@@ -1702,32 +1702,12 @@ void FlowGraph::InsertConversion(Representation from,
|
||||
}
|
||||
}
|
||||
|
||||
void FlowGraph::ConvertEnvironmentUse(Value* use, Representation from_rep) {
|
||||
const Representation to_rep = kTagged;
|
||||
if (from_rep == to_rep) {
|
||||
return;
|
||||
}
|
||||
InsertConversion(from_rep, to_rep, use, /*is_environment_use=*/true);
|
||||
}
|
||||
|
||||
void FlowGraph::InsertConversionsFor(Definition* def) {
|
||||
const Representation from_rep = def->representation();
|
||||
|
||||
for (Value::Iterator it(def->input_use_list()); !it.Done(); it.Advance()) {
|
||||
ConvertUse(it.Current(), from_rep);
|
||||
}
|
||||
|
||||
if (!graph_entry()->catch_entries().is_empty()) {
|
||||
for (Value::Iterator it(def->env_use_list()); !it.Done(); it.Advance()) {
|
||||
Value* use = it.Current();
|
||||
if (use->instruction()->MayThrow() &&
|
||||
use->instruction()->GetBlock()->InsideTryBlock()) {
|
||||
// Environment uses at calls inside try-blocks must be converted to
|
||||
// tagged representation.
|
||||
ConvertEnvironmentUse(it.Current(), from_rep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void UnboxPhi(PhiInstr* phi) {
|
||||
|
||||
@@ -438,7 +438,6 @@ class FlowGraph : public ZoneAllocated {
|
||||
|
||||
void InsertConversionsFor(Definition* def);
|
||||
void ConvertUse(Value* use, Representation from);
|
||||
void ConvertEnvironmentUse(Value* use, Representation from);
|
||||
void InsertConversion(Representation from,
|
||||
Representation to,
|
||||
Value* use,
|
||||
|
||||
@@ -119,7 +119,7 @@ FlowGraphCompiler::FlowGraphCompiler(
|
||||
pc_descriptors_list_(NULL),
|
||||
stackmap_table_builder_(NULL),
|
||||
code_source_map_builder_(NULL),
|
||||
catch_entry_state_maps_builder_(NULL),
|
||||
catch_entry_moves_maps_builder_(NULL),
|
||||
block_info_(block_order_.length()),
|
||||
deopt_infos_(),
|
||||
static_calls_target_table_(),
|
||||
@@ -183,7 +183,9 @@ bool FlowGraphCompiler::IsPotentialUnboxedField(const Field& field) {
|
||||
void FlowGraphCompiler::InitCompiler() {
|
||||
pc_descriptors_list_ = new (zone()) DescriptorList(64);
|
||||
exception_handlers_list_ = new (zone()) ExceptionHandlerList();
|
||||
catch_entry_state_maps_builder_ = new (zone()) CatchEntryStateMapBuilder();
|
||||
#if defined(DART_PRECOMPILER)
|
||||
catch_entry_moves_maps_builder_ = new (zone()) CatchEntryMovesMapBuilder();
|
||||
#endif
|
||||
block_info_.Clear();
|
||||
// Initialize block info and search optimized (non-OSR) code for calls
|
||||
// indicating a non-leaf routine and calls without IC data indicating
|
||||
@@ -340,80 +342,122 @@ intptr_t FlowGraphCompiler::UncheckedEntryOffset() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FlowGraphCompiler::EmitCatchEntryState(Environment* env,
|
||||
intptr_t try_index) {
|
||||
#if defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if defined(DART_PRECOMPILER)
|
||||
static intptr_t LocationToStackIndex(const Location& src) {
|
||||
ASSERT(src.HasStackIndex());
|
||||
return -compiler_frame_layout.VariableIndexForFrameSlot(src.stack_index());
|
||||
}
|
||||
|
||||
static CatchEntryMove CatchEntryMoveFor(Assembler* assembler,
|
||||
Representation src_rep,
|
||||
const Location& src,
|
||||
intptr_t dst_index) {
|
||||
if (src.IsConstant()) {
|
||||
// Skip dead locations.
|
||||
if (src.constant().raw() == Symbols::OptimizedOut().raw()) {
|
||||
return CatchEntryMove();
|
||||
}
|
||||
const intptr_t pool_index =
|
||||
assembler->object_pool_wrapper().FindObject(src.constant());
|
||||
return CatchEntryMove::FromSlot(CatchEntryMove::SourceKind::kConstant,
|
||||
pool_index, dst_index);
|
||||
}
|
||||
|
||||
if (src.IsPairLocation()) {
|
||||
const auto lo_loc = src.AsPairLocation()->At(0);
|
||||
const auto hi_loc = src.AsPairLocation()->At(1);
|
||||
ASSERT(lo_loc.IsStackSlot() && hi_loc.IsStackSlot());
|
||||
return CatchEntryMove::FromSlot(
|
||||
CatchEntryMove::SourceKind::kInt64PairSlot,
|
||||
CatchEntryMove::EncodePairSource(LocationToStackIndex(lo_loc),
|
||||
LocationToStackIndex(hi_loc)),
|
||||
dst_index);
|
||||
}
|
||||
|
||||
CatchEntryMove::SourceKind src_kind;
|
||||
switch (src_rep) {
|
||||
case kTagged:
|
||||
src_kind = CatchEntryMove::SourceKind::kTaggedSlot;
|
||||
break;
|
||||
case kUnboxedInt64:
|
||||
src_kind = CatchEntryMove::SourceKind::kInt64Slot;
|
||||
break;
|
||||
case kUnboxedInt32:
|
||||
src_kind = CatchEntryMove::SourceKind::kInt32Slot;
|
||||
break;
|
||||
case kUnboxedUint32:
|
||||
src_kind = CatchEntryMove::SourceKind::kUint32Slot;
|
||||
break;
|
||||
case kUnboxedDouble:
|
||||
src_kind = CatchEntryMove::SourceKind::kDoubleSlot;
|
||||
break;
|
||||
case kUnboxedFloat32x4:
|
||||
src_kind = CatchEntryMove::SourceKind::kFloat32x4Slot;
|
||||
break;
|
||||
case kUnboxedFloat64x2:
|
||||
src_kind = CatchEntryMove::SourceKind::kFloat64x2Slot;
|
||||
break;
|
||||
case kUnboxedInt32x4:
|
||||
src_kind = CatchEntryMove::SourceKind::kInt32x4Slot;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
return CatchEntryMove::FromSlot(src_kind, LocationToStackIndex(src),
|
||||
dst_index);
|
||||
}
|
||||
#endif
|
||||
|
||||
void FlowGraphCompiler::RecordCatchEntryMoves(Environment* env,
|
||||
intptr_t try_index) {
|
||||
#if defined(DART_PRECOMPILER)
|
||||
env = env ? env : pending_deoptimization_env_;
|
||||
try_index = try_index != CatchClauseNode::kInvalidTryIndex
|
||||
? try_index
|
||||
: CurrentTryIndex();
|
||||
if (is_optimizing() && env != NULL &&
|
||||
if (is_optimizing() && env != nullptr &&
|
||||
(try_index != CatchClauseNode::kInvalidTryIndex)) {
|
||||
env = env->Outermost();
|
||||
CatchBlockEntryInstr* catch_block =
|
||||
flow_graph().graph_entry()->GetCatchEntry(try_index);
|
||||
const GrowableArray<Definition*>* idefs =
|
||||
catch_block->initial_definitions();
|
||||
catch_entry_state_maps_builder_->NewMapping(assembler()->CodeSize());
|
||||
// Parameters first.
|
||||
intptr_t i = 0;
|
||||
catch_entry_moves_maps_builder_->NewMapping(assembler()->CodeSize());
|
||||
|
||||
const intptr_t num_direct_parameters = flow_graph().num_direct_parameters();
|
||||
for (; i < num_direct_parameters; ++i) {
|
||||
const intptr_t ex_idx =
|
||||
catch_block->raw_exception_var() != nullptr
|
||||
? flow_graph().EnvIndex(catch_block->raw_exception_var())
|
||||
: -1;
|
||||
const intptr_t st_idx =
|
||||
catch_block->raw_stacktrace_var() != nullptr
|
||||
? flow_graph().EnvIndex(catch_block->raw_stacktrace_var())
|
||||
: -1;
|
||||
for (intptr_t i = 0; i < flow_graph().variable_count(); ++i) {
|
||||
// Don't sync captured parameters. They are not in the environment.
|
||||
if (flow_graph().captured_parameters()->Contains(i)) continue;
|
||||
if ((*idefs)[i]->IsConstant()) continue; // Common constants.
|
||||
// Don't sync exception or stack trace variables.
|
||||
if (i == ex_idx || i == st_idx) continue;
|
||||
// Don't sync values that have been replaced with constants.
|
||||
if ((*idefs)[i]->IsConstant()) continue;
|
||||
|
||||
Location src = env->LocationAt(i);
|
||||
// Can only occur if AllocationSinking is enabled - and it is disabled
|
||||
// in functions with try.
|
||||
ASSERT(!src.IsInvalid());
|
||||
const Representation src_rep =
|
||||
env->ValueAt(i)->definition()->representation();
|
||||
intptr_t dest_index = i - num_direct_parameters;
|
||||
if (!src.IsStackSlot()) {
|
||||
ASSERT(src.IsConstant());
|
||||
// Skip dead locations.
|
||||
if (src.constant().raw() == Symbols::OptimizedOut().raw()) {
|
||||
continue;
|
||||
}
|
||||
intptr_t id =
|
||||
assembler()->object_pool_wrapper().FindObject(src.constant());
|
||||
catch_entry_state_maps_builder_->AppendConstant(id, dest_index);
|
||||
continue;
|
||||
}
|
||||
const intptr_t src_index =
|
||||
-compiler_frame_layout.VariableIndexForFrameSlot(src.stack_index());
|
||||
if (src_index != dest_index) {
|
||||
catch_entry_state_maps_builder_->AppendMove(src_index, dest_index);
|
||||
const auto move =
|
||||
CatchEntryMoveFor(assembler(), src_rep, src, dest_index);
|
||||
if (!move.IsRedundant()) {
|
||||
catch_entry_moves_maps_builder_->Append(move);
|
||||
}
|
||||
}
|
||||
|
||||
// Process locals. Skip exception_var and stacktrace_var.
|
||||
intptr_t local_base = num_direct_parameters;
|
||||
intptr_t ex_idx = local_base - catch_block->exception_var().index().value();
|
||||
intptr_t st_idx =
|
||||
local_base - catch_block->stacktrace_var().index().value();
|
||||
for (; i < flow_graph().variable_count(); ++i) {
|
||||
// Don't sync captured parameters. They are not in the environment.
|
||||
if (flow_graph().captured_parameters()->Contains(i)) continue;
|
||||
if (i == ex_idx || i == st_idx) continue;
|
||||
if ((*idefs)[i]->IsConstant()) continue; // Common constants.
|
||||
Location src = env->LocationAt(i);
|
||||
if (src.IsInvalid()) continue;
|
||||
intptr_t dest_index = i - num_direct_parameters;
|
||||
if (!src.IsStackSlot()) {
|
||||
ASSERT(src.IsConstant());
|
||||
// Skip dead locations.
|
||||
if (src.constant().raw() == Symbols::OptimizedOut().raw()) {
|
||||
continue;
|
||||
}
|
||||
intptr_t id =
|
||||
assembler()->object_pool_wrapper().FindObject(src.constant());
|
||||
catch_entry_state_maps_builder_->AppendConstant(id, dest_index);
|
||||
continue;
|
||||
}
|
||||
const intptr_t src_index =
|
||||
-compiler_frame_layout.VariableIndexForFrameSlot(src.stack_index());
|
||||
if (src_index != dest_index) {
|
||||
catch_entry_state_maps_builder_->AppendMove(src_index, dest_index);
|
||||
}
|
||||
}
|
||||
catch_entry_state_maps_builder_->EndMapping();
|
||||
catch_entry_moves_maps_builder_->EndMapping();
|
||||
}
|
||||
#endif // defined(DART_PRECOMPILER) || defined(DART_PRECOMPILED_RUNTIME)
|
||||
}
|
||||
@@ -424,7 +468,7 @@ void FlowGraphCompiler::EmitCallsiteMetadata(TokenPosition token_pos,
|
||||
LocationSummary* locs) {
|
||||
AddCurrentDescriptor(kind, deopt_id, token_pos);
|
||||
RecordSafepoint(locs);
|
||||
EmitCatchEntryState();
|
||||
RecordCatchEntryMoves();
|
||||
if (deopt_id != DeoptId::kNone) {
|
||||
// Marks either the continuation point in unoptimized code or the
|
||||
// deoptimization point in optimized code, after call.
|
||||
@@ -1019,11 +1063,11 @@ void FlowGraphCompiler::FinalizeVarDescriptors(const Code& code) {
|
||||
code.set_var_descriptors(var_descs);
|
||||
}
|
||||
|
||||
void FlowGraphCompiler::FinalizeCatchEntryStateMap(const Code& code) {
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
|
||||
void FlowGraphCompiler::FinalizeCatchEntryMovesMap(const Code& code) {
|
||||
#if defined(DART_PRECOMPILER)
|
||||
TypedData& maps = TypedData::Handle(
|
||||
catch_entry_state_maps_builder_->FinalizeCatchEntryStateMap());
|
||||
code.set_catch_entry_state_maps(maps);
|
||||
catch_entry_moves_maps_builder_->FinalizeCatchEntryMovesMap());
|
||||
code.set_catch_entry_moves_maps(maps);
|
||||
#else
|
||||
code.set_variables(Smi::Handle(Smi::New(flow_graph().variable_count())));
|
||||
#endif
|
||||
@@ -2223,7 +2267,7 @@ void ThrowErrorSlowPathCode::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||
(compiler->CurrentTryIndex() != CatchClauseNode::kInvalidTryIndex)) {
|
||||
Environment* env =
|
||||
compiler->SlowPathEnvironmentFor(instruction(), num_args_);
|
||||
compiler->EmitCatchEntryState(env, try_index_);
|
||||
compiler->RecordCatchEntryMoves(env, try_index_);
|
||||
}
|
||||
if (!use_shared_stub) {
|
||||
__ Breakpoint();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
namespace dart {
|
||||
|
||||
// Forward declarations.
|
||||
class CatchEntryMovesMapBuilder;
|
||||
class Code;
|
||||
class DeoptInfoBuilder;
|
||||
class FlowGraph;
|
||||
@@ -592,7 +593,7 @@ class FlowGraphCompiler : public ValueObject {
|
||||
|
||||
void EmitEdgeCounter(intptr_t edge_id);
|
||||
#endif // !defined(TARGET_ARCH_DBC)
|
||||
void EmitCatchEntryState(
|
||||
void RecordCatchEntryMoves(
|
||||
Environment* env = NULL,
|
||||
intptr_t try_index = CatchClauseNode::kInvalidTryIndex);
|
||||
|
||||
@@ -665,7 +666,7 @@ class FlowGraphCompiler : public ValueObject {
|
||||
RawArray* CreateDeoptInfo(Assembler* assembler);
|
||||
void FinalizeStackMaps(const Code& code);
|
||||
void FinalizeVarDescriptors(const Code& code);
|
||||
void FinalizeCatchEntryStateMap(const Code& code);
|
||||
void FinalizeCatchEntryMovesMap(const Code& code);
|
||||
void FinalizeStaticCallTargetsTable(const Code& code);
|
||||
void FinalizeCodeSourceMap(const Code& code);
|
||||
|
||||
@@ -953,7 +954,7 @@ class FlowGraphCompiler : public ValueObject {
|
||||
DescriptorList* pc_descriptors_list_;
|
||||
StackMapTableBuilder* stackmap_table_builder_;
|
||||
CodeSourceMapBuilder* code_source_map_builder_;
|
||||
CatchEntryStateMapBuilder* catch_entry_state_maps_builder_;
|
||||
CatchEntryMovesMapBuilder* catch_entry_moves_maps_builder_;
|
||||
GrowableArray<BlockInfo*> block_info_;
|
||||
GrowableArray<CompilerDeoptInfo*> deopt_infos_;
|
||||
GrowableArray<SlowPathCode*> slow_path_code_;
|
||||
|
||||
@@ -1070,7 +1070,7 @@ void FlowGraphCompiler::EmitMegamorphicInstanceCall(
|
||||
// arguments are removed.
|
||||
AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after, token_pos);
|
||||
}
|
||||
EmitCatchEntryState(pending_deoptimization_env_, try_index);
|
||||
RecordCatchEntryMoves(pending_deoptimization_env_, try_index);
|
||||
__ Drop(args_desc.CountWithTypeArgs());
|
||||
}
|
||||
|
||||
|
||||
@@ -1039,7 +1039,7 @@ void FlowGraphCompiler::EmitMegamorphicInstanceCall(
|
||||
// arguments are removed.
|
||||
AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after, token_pos);
|
||||
}
|
||||
EmitCatchEntryState(pending_deoptimization_env_, try_index);
|
||||
RecordCatchEntryMoves(pending_deoptimization_env_, try_index);
|
||||
__ Drop(args_desc.CountWithTypeArgs());
|
||||
}
|
||||
|
||||
|
||||
@@ -979,7 +979,7 @@ void FlowGraphCompiler::EmitMegamorphicInstanceCall(
|
||||
// arguments are removed.
|
||||
AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after, token_pos);
|
||||
}
|
||||
EmitCatchEntryState(pending_deoptimization_env_, try_index);
|
||||
RecordCatchEntryMoves(pending_deoptimization_env_, try_index);
|
||||
__ Drop(args_desc.CountWithTypeArgs());
|
||||
}
|
||||
|
||||
|
||||
@@ -1062,7 +1062,7 @@ void FlowGraphCompiler::EmitMegamorphicInstanceCall(
|
||||
// arguments are removed.
|
||||
AddCurrentDescriptor(RawPcDescriptors::kDeopt, deopt_id_after, token_pos);
|
||||
}
|
||||
EmitCatchEntryState(pending_deoptimization_env_, try_index);
|
||||
RecordCatchEntryMoves(pending_deoptimization_env_, try_index);
|
||||
__ Drop(args_desc.CountWithTypeArgs(), RCX);
|
||||
}
|
||||
|
||||
|
||||
@@ -2824,19 +2824,8 @@ Instruction* DebugStepCheckInstr::Canonicalize(FlowGraph* flow_graph) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool HasTryBlockUse(Value* use_list) {
|
||||
for (Value::Iterator it(use_list); !it.Done(); it.Advance()) {
|
||||
Value* use = it.Current();
|
||||
if (use->instruction()->MayThrow() &&
|
||||
use->instruction()->GetBlock()->InsideTryBlock()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Definition* BoxInstr::Canonicalize(FlowGraph* flow_graph) {
|
||||
if ((input_use_list() == NULL) && !HasTryBlockUse(env_use_list())) {
|
||||
if (input_use_list() == nullptr) {
|
||||
// Environments can accommodate any representation. No need to box.
|
||||
return value()->definition();
|
||||
}
|
||||
@@ -2859,7 +2848,7 @@ bool BoxIntegerInstr::ValueFitsSmi() const {
|
||||
}
|
||||
|
||||
Definition* BoxIntegerInstr::Canonicalize(FlowGraph* flow_graph) {
|
||||
if ((input_use_list() == NULL) && !HasTryBlockUse(env_use_list())) {
|
||||
if (input_use_list() == nullptr) {
|
||||
// Environments can accommodate any representation. No need to box.
|
||||
return value()->definition();
|
||||
}
|
||||
|
||||
@@ -3068,7 +3068,7 @@ class CheckStackOverflowSlowPath
|
||||
__ ldr(LR, Address(THR, entry_point_offset));
|
||||
__ blx(LR);
|
||||
compiler->RecordSafepoint(instruction()->locs(), kNumSlowPathArgs);
|
||||
compiler->EmitCatchEntryState();
|
||||
compiler->RecordCatchEntryMoves();
|
||||
compiler->AddDescriptor(
|
||||
RawPcDescriptors::kOther, compiler->assembler()->CodeSize(),
|
||||
instruction()->deopt_id(), instruction()->token_pos(),
|
||||
|
||||
@@ -2752,7 +2752,7 @@ class CheckStackOverflowSlowPath
|
||||
__ ldr(LR, Address(THR, entry_point_offset));
|
||||
__ blr(LR);
|
||||
compiler->RecordSafepoint(instruction()->locs(), kNumSlowPathArgs);
|
||||
compiler->EmitCatchEntryState();
|
||||
compiler->RecordCatchEntryMoves();
|
||||
compiler->AddDescriptor(
|
||||
RawPcDescriptors::kOther, compiler->assembler()->CodeSize(),
|
||||
instruction()->deopt_id(), instruction()->token_pos(),
|
||||
|
||||
@@ -2752,7 +2752,7 @@ class CheckStackOverflowSlowPath
|
||||
stack_overflow_shared_without_fpu_regs_entry_point_offset();
|
||||
__ call(Address(THR, entry_point_offset));
|
||||
compiler->RecordSafepoint(instruction()->locs(), kNumSlowPathArgs);
|
||||
compiler->EmitCatchEntryState();
|
||||
compiler->RecordCatchEntryMoves();
|
||||
compiler->AddDescriptor(
|
||||
RawPcDescriptors::kOther, compiler->assembler()->CodeSize(),
|
||||
instruction()->deopt_id(), instruction()->token_pos(),
|
||||
|
||||
@@ -630,7 +630,7 @@ RawCode* CompileParsedFunctionHelper::FinalizeCompilation(
|
||||
graph_compiler->FinalizeStackMaps(code);
|
||||
graph_compiler->FinalizeVarDescriptors(code);
|
||||
graph_compiler->FinalizeExceptionHandlers(code);
|
||||
graph_compiler->FinalizeCatchEntryStateMap(code);
|
||||
graph_compiler->FinalizeCatchEntryMovesMap(code);
|
||||
graph_compiler->FinalizeStaticCallTargetsTable(code);
|
||||
graph_compiler->FinalizeCodeSourceMap(code);
|
||||
|
||||
|
||||
@@ -335,7 +335,7 @@ void DeoptContext::FillDestFrame() {
|
||||
}
|
||||
}
|
||||
|
||||
intptr_t* DeoptContext::CatchEntryState(intptr_t num_vars) {
|
||||
const CatchEntryMoves* DeoptContext::ToCatchEntryMoves(intptr_t num_vars) {
|
||||
const Code& code = Code::Handle(code_);
|
||||
const TypedData& deopt_info = TypedData::Handle(deopt_info_);
|
||||
GrowableArray<DeoptInstr*> deopt_instructions;
|
||||
@@ -343,8 +343,7 @@ intptr_t* DeoptContext::CatchEntryState(intptr_t num_vars) {
|
||||
ASSERT(!deopt_table.IsNull());
|
||||
DeoptInfo::Unpack(deopt_table, deopt_info, &deopt_instructions);
|
||||
|
||||
intptr_t* state = new intptr_t[2 * num_vars + 1];
|
||||
state[0] = num_vars;
|
||||
CatchEntryMoves* moves = CatchEntryMoves::Allocate(num_vars);
|
||||
|
||||
Function& function = Function::Handle(zone(), code.function());
|
||||
intptr_t params =
|
||||
@@ -363,12 +362,10 @@ intptr_t* DeoptContext::CatchEntryState(intptr_t num_vars) {
|
||||
DeoptInstr* instr = deopt_instructions[len - 1 - slot];
|
||||
intptr_t dest_index = i - params;
|
||||
#endif
|
||||
CatchEntryStatePair p = instr->ToCatchEntryStatePair(this, dest_index);
|
||||
state[1 + 2 * i] = p.src;
|
||||
state[2 + 2 * i] = p.dest;
|
||||
moves->At(i) = instr->ToCatchEntryMove(this, dest_index);
|
||||
}
|
||||
|
||||
return state;
|
||||
return moves;
|
||||
}
|
||||
|
||||
static void FillDeferredSlots(DeoptContext* deopt_context,
|
||||
@@ -508,9 +505,9 @@ class DeoptConstantInstr : public DeoptInstr {
|
||||
*reinterpret_cast<RawObject**>(dest_addr) = obj.raw();
|
||||
}
|
||||
|
||||
CatchEntryStatePair ToCatchEntryStatePair(DeoptContext* deopt_context,
|
||||
intptr_t dest_slot) {
|
||||
return CatchEntryStatePair::FromConstant(object_table_index_, dest_slot);
|
||||
CatchEntryMove ToCatchEntryMove(DeoptContext* deopt_context,
|
||||
intptr_t dest_slot) {
|
||||
return CatchEntryMove::FromConstant(object_table_index_, dest_slot);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -540,10 +537,11 @@ class DeoptWordInstr : public DeoptInstr {
|
||||
*dest_addr = source_.Value<intptr_t>(deopt_context);
|
||||
}
|
||||
|
||||
CatchEntryStatePair ToCatchEntryStatePair(DeoptContext* deopt_context,
|
||||
intptr_t dest_slot) {
|
||||
return CatchEntryStatePair::FromMove(source_.StackSlot(deopt_context),
|
||||
dest_slot);
|
||||
CatchEntryMove ToCatchEntryMove(DeoptContext* deopt_context,
|
||||
intptr_t dest_slot) {
|
||||
return CatchEntryMove::FromSlot(CatchEntryMove::SourceKind::kTaggedSlot,
|
||||
source_.StackSlot(deopt_context),
|
||||
dest_slot);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -599,6 +597,15 @@ class DeoptMintPairInstr : public DeoptIntegerInstrBase {
|
||||
hi_.Value<int32_t>(deopt_context));
|
||||
}
|
||||
|
||||
CatchEntryMove ToCatchEntryMove(DeoptContext* deopt_context,
|
||||
intptr_t dest_slot) {
|
||||
return CatchEntryMove::FromSlot(
|
||||
CatchEntryMove::SourceKind::kInt64PairSlot,
|
||||
CatchEntryMove::EncodePairSource(lo_.StackSlot(deopt_context),
|
||||
hi_.StackSlot(deopt_context)),
|
||||
dest_slot);
|
||||
}
|
||||
|
||||
private:
|
||||
static const intptr_t kFieldWidth = kBitsPerWord / 2;
|
||||
class LoRegister : public BitField<intptr_t, intptr_t, 0, kFieldWidth> {};
|
||||
@@ -611,7 +618,7 @@ class DeoptMintPairInstr : public DeoptIntegerInstrBase {
|
||||
DISALLOW_COPY_AND_ASSIGN(DeoptMintPairInstr);
|
||||
};
|
||||
|
||||
template <DeoptInstr::Kind K, typename T>
|
||||
template <DeoptInstr::Kind K, CatchEntryMove::SourceKind slot_kind, typename T>
|
||||
class DeoptIntInstr : public DeoptIntegerInstrBase {
|
||||
public:
|
||||
explicit DeoptIntInstr(intptr_t source_index)
|
||||
@@ -629,17 +636,35 @@ class DeoptIntInstr : public DeoptIntegerInstrBase {
|
||||
return static_cast<int64_t>(source_.Value<T>(deopt_context));
|
||||
}
|
||||
|
||||
CatchEntryMove ToCatchEntryMove(DeoptContext* deopt_context,
|
||||
intptr_t dest_slot) {
|
||||
return CatchEntryMove::FromSlot(slot_kind, source_.StackSlot(deopt_context),
|
||||
dest_slot);
|
||||
}
|
||||
|
||||
private:
|
||||
const CpuRegisterSource source_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DeoptIntInstr);
|
||||
};
|
||||
|
||||
typedef DeoptIntInstr<DeoptInstr::kUint32, uint32_t> DeoptUint32Instr;
|
||||
typedef DeoptIntInstr<DeoptInstr::kInt32, int32_t> DeoptInt32Instr;
|
||||
typedef DeoptIntInstr<DeoptInstr::kMint, int64_t> DeoptMintInstr;
|
||||
typedef DeoptIntInstr<DeoptInstr::kUint32,
|
||||
CatchEntryMove::SourceKind::kUint32Slot,
|
||||
uint32_t>
|
||||
DeoptUint32Instr;
|
||||
typedef DeoptIntInstr<DeoptInstr::kInt32,
|
||||
CatchEntryMove::SourceKind::kInt32Slot,
|
||||
int32_t>
|
||||
DeoptInt32Instr;
|
||||
typedef DeoptIntInstr<DeoptInstr::kMint,
|
||||
CatchEntryMove::SourceKind::kInt64Slot,
|
||||
int64_t>
|
||||
DeoptMintInstr;
|
||||
|
||||
template <DeoptInstr::Kind K, typename Type, typename RawObjectType>
|
||||
template <DeoptInstr::Kind K,
|
||||
CatchEntryMove::SourceKind slot_kind,
|
||||
typename Type,
|
||||
typename RawObjectType>
|
||||
class DeoptFpuInstr : public DeoptInstr {
|
||||
public:
|
||||
explicit DeoptFpuInstr(intptr_t source_index) : source_(source_index) {}
|
||||
@@ -658,22 +683,39 @@ class DeoptFpuInstr : public DeoptInstr {
|
||||
reinterpret_cast<RawObjectType**>(dest_addr));
|
||||
}
|
||||
|
||||
CatchEntryMove ToCatchEntryMove(DeoptContext* deopt_context,
|
||||
intptr_t dest_slot) {
|
||||
return CatchEntryMove::FromSlot(slot_kind, source_.StackSlot(deopt_context),
|
||||
dest_slot);
|
||||
}
|
||||
|
||||
private:
|
||||
const FpuRegisterSource source_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DeoptFpuInstr);
|
||||
};
|
||||
|
||||
typedef DeoptFpuInstr<DeoptInstr::kDouble, double, RawDouble> DeoptDoubleInstr;
|
||||
typedef DeoptFpuInstr<DeoptInstr::kDouble,
|
||||
CatchEntryMove::SourceKind::kDoubleSlot,
|
||||
double,
|
||||
RawDouble>
|
||||
DeoptDoubleInstr;
|
||||
|
||||
// Simd128 types.
|
||||
typedef DeoptFpuInstr<DeoptInstr::kFloat32x4, simd128_value_t, RawFloat32x4>
|
||||
typedef DeoptFpuInstr<DeoptInstr::kFloat32x4,
|
||||
CatchEntryMove::SourceKind::kFloat32x4Slot,
|
||||
simd128_value_t,
|
||||
RawFloat32x4>
|
||||
DeoptFloat32x4Instr;
|
||||
typedef DeoptFpuInstr<DeoptInstr::kFloat32x4, simd128_value_t, RawFloat32x4>
|
||||
DeoptFloat32x4Instr;
|
||||
typedef DeoptFpuInstr<DeoptInstr::kFloat64x2, simd128_value_t, RawFloat64x2>
|
||||
typedef DeoptFpuInstr<DeoptInstr::kFloat64x2,
|
||||
CatchEntryMove::SourceKind::kFloat64x2Slot,
|
||||
simd128_value_t,
|
||||
RawFloat64x2>
|
||||
DeoptFloat64x2Instr;
|
||||
typedef DeoptFpuInstr<DeoptInstr::kInt32x4, simd128_value_t, RawInt32x4>
|
||||
typedef DeoptFpuInstr<DeoptInstr::kInt32x4,
|
||||
CatchEntryMove::SourceKind::kInt32x4Slot,
|
||||
simd128_value_t,
|
||||
RawInt32x4>
|
||||
DeoptInt32x4Instr;
|
||||
|
||||
// Deoptimization instruction creating a PC marker for the code of
|
||||
|
||||
@@ -166,8 +166,10 @@ class DeoptContext {
|
||||
// objects.
|
||||
void FillDestFrame();
|
||||
|
||||
// Allocate and prepare exceptions metadata for TrySync
|
||||
intptr_t* CatchEntryState(intptr_t num_vars);
|
||||
// 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.
|
||||
@@ -334,11 +336,10 @@ class DeoptInstr : public ZoneAllocated {
|
||||
|
||||
virtual void Execute(DeoptContext* deopt_context, intptr_t* dest_addr) = 0;
|
||||
|
||||
// Convert DeoptInstr to TrySync metadata entry.
|
||||
virtual CatchEntryStatePair ToCatchEntryStatePair(DeoptContext* deopt_context,
|
||||
intptr_t dest_slot) {
|
||||
virtual CatchEntryMove ToCatchEntryMove(DeoptContext* deopt_context,
|
||||
intptr_t dest_slot) {
|
||||
UNREACHABLE();
|
||||
return CatchEntryStatePair();
|
||||
return CatchEntryMove();
|
||||
}
|
||||
|
||||
virtual DeoptInstr::Kind kind() const = 0;
|
||||
|
||||
+144
-76
@@ -141,20 +141,10 @@ static void BuildStackTrace(StackTraceBuilder* builder) {
|
||||
}
|
||||
}
|
||||
|
||||
static RawObject** VariableAt(uword fp, int stack_slot) {
|
||||
#if defined(TARGET_ARCH_DBC)
|
||||
return reinterpret_cast<RawObject**>(fp + stack_slot * kWordSize);
|
||||
#else
|
||||
const intptr_t frame_slot =
|
||||
runtime_frame_layout.FrameSlotForVariableIndex(-stack_slot);
|
||||
return reinterpret_cast<RawObject**>(fp + frame_slot * kWordSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
class ExceptionHandlerFinder : public StackResource {
|
||||
public:
|
||||
explicit ExceptionHandlerFinder(Thread* thread)
|
||||
: StackResource(thread), thread_(thread), cache_(NULL), metadata_(NULL) {}
|
||||
: StackResource(thread), thread_(thread) {}
|
||||
|
||||
// Iterate through the stack frames and try to find a frame with an
|
||||
// exception handler. Once found, set the pc, sp and fp so that execution
|
||||
@@ -172,7 +162,7 @@ class ExceptionHandlerFinder : public StackResource {
|
||||
uword temp_handler_pc = kUwordMax;
|
||||
bool is_optimized = false;
|
||||
code_ = NULL;
|
||||
cache_ = thread_->isolate()->catch_entry_state_cache();
|
||||
catch_entry_moves_cache_ = thread_->isolate()->catch_entry_moves_cache();
|
||||
|
||||
while (!frame->IsEntryFrame()) {
|
||||
if (frame->IsDartFrame()) {
|
||||
@@ -187,13 +177,20 @@ class ExceptionHandlerFinder : public StackResource {
|
||||
if (is_optimized) {
|
||||
pc_ = frame->pc();
|
||||
code_ = &Code::Handle(frame->LookupDartCode());
|
||||
CatchEntryState* state = cache_->Lookup(pc_);
|
||||
if (state != NULL) cached_ = *state;
|
||||
CatchEntryMovesRefPtr* cached_catch_entry_moves =
|
||||
catch_entry_moves_cache_->Lookup(pc_);
|
||||
if (cached_catch_entry_moves != NULL) {
|
||||
cached_catch_entry_moves_ = *cached_catch_entry_moves;
|
||||
}
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_PRECOMPILER)
|
||||
intptr_t num_vars = Smi::Value(code_->variables());
|
||||
if (cached_.Empty()) GetMetaDataFromDeopt(num_vars, frame);
|
||||
if (cached_catch_entry_moves_.IsEmpty()) {
|
||||
GetCatchEntryMovesFromDeopt(num_vars, frame);
|
||||
}
|
||||
#else
|
||||
if (cached_.Empty()) ReadCompressedMetaData();
|
||||
if (cached_catch_entry_moves_.IsEmpty()) {
|
||||
ReadCompressedCatchEntryMoves();
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_PRECOMPILER)
|
||||
}
|
||||
}
|
||||
@@ -216,80 +213,121 @@ class ExceptionHandlerFinder : public StackResource {
|
||||
return handler_pc_set_;
|
||||
}
|
||||
|
||||
void TrySync() {
|
||||
if (code_ == NULL || !code_->is_optimized()) {
|
||||
// When entering catch block in the optimized code we need to execute
|
||||
// catch entry moves that would morph the state of the frame into
|
||||
// what catch entry expects.
|
||||
void PrepareFrameForCatchEntry() {
|
||||
if (code_ == nullptr || !code_->is_optimized()) {
|
||||
return;
|
||||
}
|
||||
if (!cached_.Empty()) {
|
||||
// Cache hit.
|
||||
TrySyncCached(&cached_);
|
||||
|
||||
if (cached_catch_entry_moves_.IsEmpty()) {
|
||||
catch_entry_moves_cache_->Insert(
|
||||
pc_, CatchEntryMovesRefPtr(catch_entry_moves_));
|
||||
} else {
|
||||
// New cache entry.
|
||||
CatchEntryState m(metadata_);
|
||||
TrySyncCached(&m);
|
||||
cache_->Insert(pc_, m);
|
||||
catch_entry_moves_ = &cached_catch_entry_moves_.moves();
|
||||
}
|
||||
|
||||
ExecuteCatchEntryMoves(*catch_entry_moves_);
|
||||
}
|
||||
|
||||
void TrySyncCached(CatchEntryState* md) {
|
||||
void ExecuteCatchEntryMoves(const CatchEntryMoves& moves) {
|
||||
uword fp = handler_fp;
|
||||
ObjectPool* pool = NULL;
|
||||
intptr_t pairs = md->Pairs();
|
||||
for (int j = 0; j < pairs; j++) {
|
||||
intptr_t src = md->Src(j);
|
||||
intptr_t dest = md->Dest(j);
|
||||
if (md->isMove(j)) {
|
||||
*VariableAt(fp, dest) = *VariableAt(fp, src);
|
||||
} else {
|
||||
if (pool == NULL) {
|
||||
pool = &ObjectPool::Handle(code_->object_pool());
|
||||
}
|
||||
RawObject* obj = pool->ObjectAt(src);
|
||||
*VariableAt(fp, dest) = obj;
|
||||
ObjectPool* pool = nullptr;
|
||||
for (int j = 0; j < moves.count(); j++) {
|
||||
const CatchEntryMove& move = moves.At(j);
|
||||
|
||||
RawObject* value;
|
||||
switch (move.source_kind()) {
|
||||
case CatchEntryMove::SourceKind::kConstant:
|
||||
if (pool == nullptr) {
|
||||
pool = &ObjectPool::Handle(code_->object_pool());
|
||||
}
|
||||
value = pool->ObjectAt(move.src_slot());
|
||||
break;
|
||||
|
||||
case CatchEntryMove::SourceKind::kTaggedSlot:
|
||||
value = *TaggedSlotAt(fp, move.src_slot());
|
||||
break;
|
||||
|
||||
case CatchEntryMove::SourceKind::kDoubleSlot:
|
||||
value = Double::New(*SlotAt<double>(fp, move.src_slot()));
|
||||
break;
|
||||
|
||||
case CatchEntryMove::SourceKind::kFloat32x4Slot:
|
||||
value = Float32x4::New(*SlotAt<simd128_value_t>(fp, move.src_slot()));
|
||||
break;
|
||||
|
||||
case CatchEntryMove::SourceKind::kFloat64x2Slot:
|
||||
value = Float64x2::New(*SlotAt<simd128_value_t>(fp, move.src_slot()));
|
||||
break;
|
||||
|
||||
case CatchEntryMove::SourceKind::kInt32x4Slot:
|
||||
value = Int32x4::New(*SlotAt<simd128_value_t>(fp, move.src_slot()));
|
||||
break;
|
||||
|
||||
case CatchEntryMove::SourceKind::kInt64PairSlot:
|
||||
value = Integer::New(
|
||||
Utils::LowHighTo64Bits(*SlotAt<uint32_t>(fp, move.src_lo_slot()),
|
||||
*SlotAt<int32_t>(fp, move.src_hi_slot())));
|
||||
break;
|
||||
|
||||
case CatchEntryMove::SourceKind::kInt64Slot:
|
||||
value = Integer::New(*SlotAt<int64_t>(fp, move.src_slot()));
|
||||
break;
|
||||
|
||||
case CatchEntryMove::SourceKind::kInt32Slot:
|
||||
value = Integer::New(*SlotAt<int32_t>(fp, move.src_slot()));
|
||||
break;
|
||||
|
||||
case CatchEntryMove::SourceKind::kUint32Slot:
|
||||
value = Integer::New(*SlotAt<uint32_t>(fp, move.src_slot()));
|
||||
break;
|
||||
}
|
||||
|
||||
*TaggedSlotAt(fp, move.dest_slot()) = value;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
|
||||
void ReadCompressedMetaData() {
|
||||
void ReadCompressedCatchEntryMoves() {
|
||||
intptr_t pc_offset = pc_ - code_->PayloadStart();
|
||||
const TypedData& td = TypedData::Handle(code_->catch_entry_state_maps());
|
||||
const TypedData& td = TypedData::Handle(code_->catch_entry_moves_maps());
|
||||
NoSafepointScope no_safepoint;
|
||||
ReadStream stream(static_cast<uint8_t*>(td.DataAddr(0)), td.Length());
|
||||
|
||||
bool found_metadata = false;
|
||||
intptr_t prefix_length, suffix_length, suffix_offset;
|
||||
while (stream.PendingBytes() > 0) {
|
||||
intptr_t target_pc_offset = Reader::Read(&stream);
|
||||
intptr_t variables = Reader::Read(&stream);
|
||||
intptr_t suffix_length = Reader::Read(&stream);
|
||||
intptr_t suffix_offset = Reader::Read(&stream);
|
||||
prefix_length = Reader::Read(&stream);
|
||||
suffix_length = Reader::Read(&stream);
|
||||
suffix_offset = Reader::Read(&stream);
|
||||
if (pc_offset == target_pc_offset) {
|
||||
metadata_ = new intptr_t[2 * (variables + suffix_length) + 1];
|
||||
metadata_[0] = variables + suffix_length;
|
||||
for (int j = 0; j < variables; j++) {
|
||||
intptr_t src = Reader::Read(&stream);
|
||||
intptr_t dest = Reader::Read(&stream);
|
||||
metadata_[1 + 2 * j] = src;
|
||||
metadata_[2 + 2 * j] = dest;
|
||||
}
|
||||
ReadCompressedSuffix(&stream, suffix_offset, suffix_length, metadata_,
|
||||
2 * variables + 1);
|
||||
found_metadata = true;
|
||||
break;
|
||||
} else {
|
||||
for (intptr_t j = 0; j < 2 * variables; j++) {
|
||||
Reader::Read(&stream);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip the moves.
|
||||
for (intptr_t j = 0; j < prefix_length; j++) {
|
||||
CatchEntryMove::ReadFrom(&stream);
|
||||
}
|
||||
}
|
||||
ASSERT(found_metadata);
|
||||
ASSERT((stream.PendingBytes() > 0) || (prefix_length == 0));
|
||||
|
||||
CatchEntryMoves* moves =
|
||||
CatchEntryMoves::Allocate(prefix_length + suffix_length);
|
||||
for (int j = 0; j < prefix_length; j++) {
|
||||
moves->At(j) = CatchEntryMove::ReadFrom(&stream);
|
||||
}
|
||||
ReadCompressedCatchEntryMovesSuffix(&stream, suffix_offset, suffix_length,
|
||||
moves, prefix_length);
|
||||
catch_entry_moves_ = moves;
|
||||
}
|
||||
|
||||
void ReadCompressedSuffix(ReadStream* stream,
|
||||
intptr_t offset,
|
||||
intptr_t length,
|
||||
intptr_t* target,
|
||||
intptr_t target_offset) {
|
||||
void ReadCompressedCatchEntryMovesSuffix(ReadStream* stream,
|
||||
intptr_t offset,
|
||||
intptr_t length,
|
||||
CatchEntryMoves* moves,
|
||||
intptr_t moves_offset) {
|
||||
stream->SetPosition(offset);
|
||||
Reader::Read(stream); // skip pc_offset
|
||||
Reader::Read(stream); // skip variables
|
||||
@@ -297,24 +335,23 @@ class ExceptionHandlerFinder : public StackResource {
|
||||
intptr_t suffix_offset = Reader::Read(stream);
|
||||
intptr_t to_read = length - suffix_length;
|
||||
for (int j = 0; j < to_read; j++) {
|
||||
target[target_offset + 2 * j] = Reader::Read(stream);
|
||||
target[target_offset + 2 * j + 1] = Reader::Read(stream);
|
||||
moves->At(moves_offset + j) = CatchEntryMove::ReadFrom(stream);
|
||||
}
|
||||
if (suffix_length > 0) {
|
||||
ReadCompressedSuffix(stream, suffix_offset, suffix_length, target,
|
||||
target_offset + to_read * 2);
|
||||
ReadCompressedCatchEntryMovesSuffix(stream, suffix_offset, suffix_length,
|
||||
moves, moves_offset + to_read);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
void GetMetaDataFromDeopt(intptr_t num_vars, StackFrame* frame) {
|
||||
void GetCatchEntryMovesFromDeopt(intptr_t num_vars, StackFrame* frame) {
|
||||
Isolate* isolate = thread_->isolate();
|
||||
DeoptContext* deopt_context =
|
||||
new DeoptContext(frame, *code_, DeoptContext::kDestIsAllocated, NULL,
|
||||
NULL, true, false /* deoptimizing_code */);
|
||||
isolate->set_deopt_context(deopt_context);
|
||||
|
||||
metadata_ = deopt_context->CatchEntryState(num_vars);
|
||||
catch_entry_moves_ = deopt_context->ToCatchEntryMoves(num_vars);
|
||||
|
||||
isolate->set_deopt_context(NULL);
|
||||
delete deopt_context;
|
||||
@@ -327,16 +364,47 @@ class ExceptionHandlerFinder : public StackResource {
|
||||
uword handler_fp;
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
static T* SlotAt(uword fp, int stack_slot) {
|
||||
#if defined(TARGET_ARCH_DBC)
|
||||
return reinterpret_cast<T*>(fp + stack_slot * kWordSize);
|
||||
#else
|
||||
const intptr_t frame_slot =
|
||||
runtime_frame_layout.FrameSlotForVariableIndex(-stack_slot);
|
||||
return reinterpret_cast<T*>(fp + frame_slot * kWordSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
static RawObject** TaggedSlotAt(uword fp, int stack_slot) {
|
||||
return SlotAt<RawObject*>(fp, stack_slot);
|
||||
}
|
||||
|
||||
typedef ReadStream::Raw<sizeof(intptr_t), intptr_t> Reader;
|
||||
Thread* thread_;
|
||||
CatchEntryStateCache* cache_;
|
||||
Code* code_;
|
||||
bool handler_pc_set_;
|
||||
intptr_t* metadata_; // MetaData generated from deopt.
|
||||
CatchEntryState cached_; // Value of per PC MetaData cache.
|
||||
intptr_t pc_; // Current pc in the handler frame.
|
||||
|
||||
const CatchEntryMoves* catch_entry_moves_ = nullptr;
|
||||
CatchEntryMovesCache* catch_entry_moves_cache_ = nullptr;
|
||||
CatchEntryMovesRefPtr cached_catch_entry_moves_;
|
||||
};
|
||||
|
||||
CatchEntryMove CatchEntryMove::ReadFrom(ReadStream* stream) {
|
||||
using Reader = ReadStream::Raw<sizeof(intptr_t), intptr_t>;
|
||||
const intptr_t src = Reader::Read(stream);
|
||||
const intptr_t dest_and_kind = Reader::Read(stream);
|
||||
return CatchEntryMove(src, dest_and_kind);
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
void CatchEntryMove::WriteTo(WriteStream* stream) {
|
||||
using Writer = WriteStream::Raw<sizeof(intptr_t), intptr_t>;
|
||||
Writer::Write(stream, src_);
|
||||
Writer::Write(stream, dest_and_kind_);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void FindErrorHandler(uword* handler_pc,
|
||||
uword* handler_sp,
|
||||
uword* handler_fp) {
|
||||
@@ -619,7 +687,7 @@ static void ThrowExceptionHelper(Thread* thread,
|
||||
THR_Print("%s\n", stacktrace.ToCString());
|
||||
}
|
||||
if (handler_exists) {
|
||||
finder.TrySync();
|
||||
finder.PrepareFrameForCatchEntry();
|
||||
// Found a dart handler for the exception, jump to it.
|
||||
JumpToExceptionHandler(thread, handler_pc, handler_sp, handler_fp,
|
||||
exception, stacktrace);
|
||||
|
||||
+164
-29
@@ -6,6 +6,7 @@
|
||||
#define RUNTIME_VM_EXCEPTIONS_H_
|
||||
|
||||
#include "vm/allocation.h"
|
||||
#include "vm/bitfield.h"
|
||||
#include "vm/token_position.h"
|
||||
|
||||
namespace dart {
|
||||
@@ -22,6 +23,8 @@ class RawInstance;
|
||||
class RawObject;
|
||||
class RawScript;
|
||||
class RawStackTrace;
|
||||
class ReadStream;
|
||||
class WriteStream;
|
||||
class String;
|
||||
class Thread;
|
||||
|
||||
@@ -106,61 +109,193 @@ struct ExceptionHandlerInfo {
|
||||
int8_t is_generated; // True if this is a generated handler.
|
||||
};
|
||||
|
||||
class CatchEntryState {
|
||||
//
|
||||
// Support for try/catch in the optimized code.
|
||||
//
|
||||
// Optimizing compiler does not model exceptional control flow explicitly,
|
||||
// instead we rely on the runtime system to create correct state at the
|
||||
// entry into the catch block by reshuffling values in the frame into
|
||||
// positions where they are expected to be at the beginning of the catch block.
|
||||
//
|
||||
// See runtime/docs/compiler/exceptions.md for more details.
|
||||
//
|
||||
|
||||
// A single move from a stack slot or an object pool into another stack slot.
|
||||
// Destination slot is expecting only tagged values, however source
|
||||
// slot can contain an unboxed value (e.g. an unboxed double) - in this case
|
||||
// we will box the value before executing the move.
|
||||
class CatchEntryMove {
|
||||
public:
|
||||
enum { kCatchEntryStateIsMove = 1, kCatchEntryStateDestShift = 1 };
|
||||
CatchEntryMove()
|
||||
: src_(0),
|
||||
dest_and_kind_(static_cast<intptr_t>(SourceKind::kTaggedSlot)) {
|
||||
ASSERT(IsRedundant());
|
||||
}
|
||||
|
||||
CatchEntryState() : data_(NULL), ref_count_(NULL) {}
|
||||
explicit CatchEntryState(intptr_t* data)
|
||||
: data_(data), ref_count_(new intptr_t(1)) {}
|
||||
enum class SourceKind {
|
||||
kConstant,
|
||||
kTaggedSlot,
|
||||
kDoubleSlot,
|
||||
kFloat32x4Slot,
|
||||
kFloat64x2Slot,
|
||||
kInt32x4Slot,
|
||||
kInt64PairSlot,
|
||||
kInt64Slot,
|
||||
kInt32Slot,
|
||||
kUint32Slot,
|
||||
};
|
||||
|
||||
CatchEntryState(const CatchEntryState& state) { Copy(state); }
|
||||
SourceKind source_kind() const {
|
||||
return SourceKindField::decode(dest_and_kind_);
|
||||
}
|
||||
|
||||
~CatchEntryState() { Destroy(); }
|
||||
intptr_t src_slot() const {
|
||||
ASSERT(source_kind() != SourceKind::kInt64PairSlot);
|
||||
return src_;
|
||||
}
|
||||
|
||||
CatchEntryState& operator=(const CatchEntryState& state) {
|
||||
intptr_t src_lo_slot() const {
|
||||
ASSERT(source_kind() == SourceKind::kInt64PairSlot);
|
||||
return LoSourceSlot::decode(src_);
|
||||
}
|
||||
|
||||
intptr_t src_hi_slot() const {
|
||||
ASSERT(source_kind() == SourceKind::kInt64PairSlot);
|
||||
return HiSourceSlot::decode(src_);
|
||||
}
|
||||
|
||||
intptr_t dest_slot() const {
|
||||
return dest_and_kind_ >> SourceKindField::bitsize();
|
||||
}
|
||||
|
||||
static CatchEntryMove FromConstant(intptr_t pool_id, intptr_t dest_slot) {
|
||||
return FromSlot(SourceKind::kConstant, pool_id, dest_slot);
|
||||
}
|
||||
|
||||
static CatchEntryMove FromSlot(SourceKind kind,
|
||||
intptr_t src_slot,
|
||||
intptr_t dest_slot) {
|
||||
return CatchEntryMove(src_slot,
|
||||
SourceKindField::encode(kind) |
|
||||
(dest_slot << SourceKindField::bitsize()));
|
||||
}
|
||||
|
||||
static intptr_t EncodePairSource(intptr_t src_lo_slot, intptr_t src_hi_slot) {
|
||||
return LoSourceSlot::encode(src_lo_slot) |
|
||||
HiSourceSlot::encode(src_hi_slot);
|
||||
}
|
||||
|
||||
bool IsRedundant() const {
|
||||
return (source_kind() == SourceKind::kTaggedSlot) &&
|
||||
(dest_slot() == src_slot());
|
||||
}
|
||||
|
||||
bool operator==(const CatchEntryMove& rhs) {
|
||||
return src_ == rhs.src_ && dest_and_kind_ == rhs.dest_and_kind_;
|
||||
}
|
||||
|
||||
static CatchEntryMove ReadFrom(ReadStream* stream);
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
void WriteTo(WriteStream* stream);
|
||||
#endif
|
||||
|
||||
private:
|
||||
CatchEntryMove(intptr_t src, intptr_t dest_and_kind)
|
||||
: src_(src), dest_and_kind_(dest_and_kind) {}
|
||||
|
||||
// Note: BitField helper does not work with signed values of size that does
|
||||
// not match the destination size - thus we don't use BitField for declaring
|
||||
// DestinationField and instead encode and decode it manually.
|
||||
using SourceKindField = BitField<intptr_t, SourceKind, 0, 4>;
|
||||
|
||||
static constexpr intptr_t kHalfSourceBits = kBitsPerWord / 2;
|
||||
using LoSourceSlot = BitField<intptr_t, intptr_t, 0, kHalfSourceBits>;
|
||||
using HiSourceSlot =
|
||||
BitField<intptr_t, intptr_t, kHalfSourceBits, kHalfSourceBits>;
|
||||
|
||||
intptr_t src_;
|
||||
intptr_t dest_and_kind_;
|
||||
};
|
||||
|
||||
// A sequence of moves that needs to be executed to create a state expected
|
||||
// at the catch entry.
|
||||
// Note: this is a deserialized representation that is used by the runtime
|
||||
// system as a temporary representation and for caching. That is why this
|
||||
// object is allocated in the malloced heap and not in the Dart heap.
|
||||
class CatchEntryMoves {
|
||||
public:
|
||||
static CatchEntryMoves* Allocate(intptr_t num_moves) {
|
||||
auto result = reinterpret_cast<CatchEntryMoves*>(
|
||||
malloc(sizeof(CatchEntryMoves) + sizeof(CatchEntryMove) * num_moves));
|
||||
result->count_ = num_moves;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void Free(const CatchEntryMoves* moves) {
|
||||
free(const_cast<CatchEntryMoves*>(moves));
|
||||
}
|
||||
|
||||
intptr_t count() const { return count_; }
|
||||
CatchEntryMove& At(intptr_t i) { return Moves()[i]; }
|
||||
const CatchEntryMove& At(intptr_t i) const { return Moves()[i]; }
|
||||
|
||||
private:
|
||||
CatchEntryMove* Moves() {
|
||||
return reinterpret_cast<CatchEntryMove*>(this + 1);
|
||||
}
|
||||
|
||||
const CatchEntryMove* Moves() const {
|
||||
return reinterpret_cast<const CatchEntryMove*>(this + 1);
|
||||
}
|
||||
|
||||
intptr_t count_;
|
||||
// Followed by CatchEntryMove[count_]
|
||||
};
|
||||
|
||||
// A simple reference counting wrapper for CatchEntryMoves.
|
||||
//
|
||||
// TODO(vegorov) switch this to intrusive reference counting.
|
||||
class CatchEntryMovesRefPtr {
|
||||
public:
|
||||
CatchEntryMovesRefPtr() : moves_(nullptr), ref_count_(nullptr) {}
|
||||
explicit CatchEntryMovesRefPtr(const CatchEntryMoves* moves)
|
||||
: moves_(moves), ref_count_(new intptr_t(1)) {}
|
||||
|
||||
CatchEntryMovesRefPtr(const CatchEntryMovesRefPtr& state) { Copy(state); }
|
||||
|
||||
~CatchEntryMovesRefPtr() { Destroy(); }
|
||||
|
||||
CatchEntryMovesRefPtr& operator=(const CatchEntryMovesRefPtr& state) {
|
||||
Destroy();
|
||||
Copy(state);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Empty() { return ref_count_ == NULL; }
|
||||
bool IsEmpty() { return ref_count_ == nullptr; }
|
||||
|
||||
intptr_t Pairs() { return data_[0]; }
|
||||
|
||||
intptr_t Src(intptr_t i) { return data_[1 + 2 * i]; }
|
||||
|
||||
intptr_t Dest(intptr_t i) {
|
||||
return data_[2 + 2 * i] >> kCatchEntryStateDestShift;
|
||||
}
|
||||
|
||||
bool isMove(intptr_t i) { return data_[2 + 2 * i] & kCatchEntryStateIsMove; }
|
||||
const CatchEntryMoves& moves() { return *moves_; }
|
||||
|
||||
private:
|
||||
void Destroy() {
|
||||
if (ref_count_ != NULL) {
|
||||
if (ref_count_ != nullptr) {
|
||||
(*ref_count_)--;
|
||||
if (*ref_count_ == 0) {
|
||||
delete ref_count_;
|
||||
delete[] data_;
|
||||
CatchEntryMoves::Free(moves_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Copy(const CatchEntryState& state) {
|
||||
data_ = state.data_;
|
||||
void Copy(const CatchEntryMovesRefPtr& state) {
|
||||
moves_ = state.moves_;
|
||||
ref_count_ = state.ref_count_;
|
||||
if (ref_count_ != NULL) {
|
||||
if (ref_count_ != nullptr) {
|
||||
(*ref_count_)++;
|
||||
}
|
||||
}
|
||||
|
||||
// data_ has the following format:
|
||||
// 0 - number of pairs in this state
|
||||
// 1-2 - 1st encoded src,dest pair
|
||||
// 3-4 - 2nd pair
|
||||
// ....
|
||||
intptr_t* data_;
|
||||
const CatchEntryMoves* moves_;
|
||||
intptr_t* ref_count_;
|
||||
};
|
||||
|
||||
|
||||
@@ -431,7 +431,7 @@ void Heap::CollectOldSpaceGarbage(Thread* thread,
|
||||
NOT_IN_PRODUCT(PrintStatsToTimeline(&tds, reason));
|
||||
// Some Code objects may have been collected so invalidate handler cache.
|
||||
thread->isolate()->handler_info_cache()->Clear();
|
||||
thread->isolate()->catch_entry_state_cache()->Clear();
|
||||
thread->isolate()->catch_entry_moves_cache()->Clear();
|
||||
EndOldSpaceGC();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -941,7 +941,7 @@ Isolate::Isolate(const Dart_IsolateFlags& api_flags)
|
||||
spawn_count_monitor_(new Monitor()),
|
||||
spawn_count_(0),
|
||||
handler_info_cache_(),
|
||||
catch_entry_state_cache_(),
|
||||
catch_entry_moves_cache_(),
|
||||
embedder_entry_points_(NULL),
|
||||
obfuscation_map_(NULL) {
|
||||
FlagsCopyFrom(api_flags);
|
||||
|
||||
@@ -130,7 +130,7 @@ class NoReloadScope : public StackResource {
|
||||
// Fixed cache for exception handler lookup.
|
||||
typedef FixedCache<intptr_t, ExceptionHandlerInfo, 16> HandlerInfoCache;
|
||||
// Fixed cache for catch entry state lookup.
|
||||
typedef FixedCache<intptr_t, CatchEntryState, 16> CatchEntryStateCache;
|
||||
typedef FixedCache<intptr_t, CatchEntryMovesRefPtr, 16> CatchEntryMovesCache;
|
||||
|
||||
// List of Isolate flags with corresponding members of Dart_IsolateFlags and
|
||||
// corresponding global command line flags.
|
||||
@@ -784,8 +784,8 @@ class Isolate : public BaseIsolate {
|
||||
|
||||
HandlerInfoCache* handler_info_cache() { return &handler_info_cache_; }
|
||||
|
||||
CatchEntryStateCache* catch_entry_state_cache() {
|
||||
return &catch_entry_state_cache_;
|
||||
CatchEntryMovesCache* catch_entry_moves_cache() {
|
||||
return &catch_entry_moves_cache_;
|
||||
}
|
||||
|
||||
void MaybeIncreaseReloadEveryNStackOverflowChecks();
|
||||
@@ -1017,7 +1017,7 @@ class Isolate : public BaseIsolate {
|
||||
intptr_t spawn_count_;
|
||||
|
||||
HandlerInfoCache handler_info_cache_;
|
||||
CatchEntryStateCache catch_entry_state_cache_;
|
||||
CatchEntryMovesCache catch_entry_moves_cache_;
|
||||
|
||||
Dart_QualifiedFunctionName* embedder_entry_points_;
|
||||
const char** obfuscation_map_;
|
||||
|
||||
@@ -15398,8 +15398,8 @@ void Code::set_variables(const Smi& smi) const {
|
||||
StorePointer(&raw_ptr()->catch_entry_.variables_, smi.raw());
|
||||
}
|
||||
#else
|
||||
void Code::set_catch_entry_state_maps(const TypedData& maps) const {
|
||||
StorePointer(&raw_ptr()->catch_entry_.catch_entry_state_maps_, maps.raw());
|
||||
void Code::set_catch_entry_moves_maps(const TypedData& maps) const {
|
||||
StorePointer(&raw_ptr()->catch_entry_.catch_entry_moves_maps_, maps.raw());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+3
-3
@@ -5045,10 +5045,10 @@ class Code : public Object {
|
||||
RawSmi* variables() const { return raw_ptr()->catch_entry_.variables_; }
|
||||
void set_variables(const Smi& smi) const;
|
||||
#else
|
||||
RawTypedData* catch_entry_state_maps() const {
|
||||
return raw_ptr()->catch_entry_.catch_entry_state_maps_;
|
||||
RawTypedData* catch_entry_moves_maps() const {
|
||||
return raw_ptr()->catch_entry_.catch_entry_moves_maps_;
|
||||
}
|
||||
void set_catch_entry_state_maps(const TypedData& maps) const;
|
||||
void set_catch_entry_moves_maps(const TypedData& maps) const;
|
||||
#endif
|
||||
|
||||
RawArray* stackmaps() const { return raw_ptr()->stackmaps_; }
|
||||
|
||||
@@ -380,51 +380,51 @@ void ProgramVisitor::DedupDeoptEntries() {
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#if defined(DART_PRECOMPILER)
|
||||
void ProgramVisitor::DedupCatchEntryStateMaps() {
|
||||
void ProgramVisitor::DedupCatchEntryMovesMaps() {
|
||||
if (!FLAG_precompiled_mode) {
|
||||
return;
|
||||
}
|
||||
class DedupCatchEntryStateMapsVisitor : public FunctionVisitor {
|
||||
class DedupCatchEntryMovesMapsVisitor : public FunctionVisitor {
|
||||
public:
|
||||
explicit DedupCatchEntryStateMapsVisitor(Zone* zone)
|
||||
explicit DedupCatchEntryMovesMapsVisitor(Zone* zone)
|
||||
: zone_(zone),
|
||||
canonical_catch_entry_state_maps_(),
|
||||
canonical_catch_entry_moves_maps_(),
|
||||
code_(Code::Handle(zone)),
|
||||
catch_entry_state_maps_(TypedData::Handle(zone)) {}
|
||||
catch_entry_moves_maps_(TypedData::Handle(zone)) {}
|
||||
|
||||
void Visit(const Function& function) {
|
||||
if (!function.HasCode()) {
|
||||
return;
|
||||
}
|
||||
code_ = function.CurrentCode();
|
||||
catch_entry_state_maps_ = code_.catch_entry_state_maps();
|
||||
catch_entry_state_maps_ =
|
||||
DedupCatchEntryStateMaps(catch_entry_state_maps_);
|
||||
code_.set_catch_entry_state_maps(catch_entry_state_maps_);
|
||||
catch_entry_moves_maps_ = code_.catch_entry_moves_maps();
|
||||
catch_entry_moves_maps_ =
|
||||
DedupCatchEntryMovesMaps(catch_entry_moves_maps_);
|
||||
code_.set_catch_entry_moves_maps(catch_entry_moves_maps_);
|
||||
}
|
||||
|
||||
RawTypedData* DedupCatchEntryStateMaps(
|
||||
const TypedData& catch_entry_state_maps) {
|
||||
const TypedData* canonical_catch_entry_state_maps =
|
||||
canonical_catch_entry_state_maps_.LookupValue(
|
||||
&catch_entry_state_maps);
|
||||
if (canonical_catch_entry_state_maps == NULL) {
|
||||
canonical_catch_entry_state_maps_.Insert(
|
||||
&TypedData::ZoneHandle(zone_, catch_entry_state_maps.raw()));
|
||||
return catch_entry_state_maps.raw();
|
||||
RawTypedData* DedupCatchEntryMovesMaps(
|
||||
const TypedData& catch_entry_moves_maps) {
|
||||
const TypedData* canonical_catch_entry_moves_maps =
|
||||
canonical_catch_entry_moves_maps_.LookupValue(
|
||||
&catch_entry_moves_maps);
|
||||
if (canonical_catch_entry_moves_maps == NULL) {
|
||||
canonical_catch_entry_moves_maps_.Insert(
|
||||
&TypedData::ZoneHandle(zone_, catch_entry_moves_maps.raw()));
|
||||
return catch_entry_moves_maps.raw();
|
||||
} else {
|
||||
return canonical_catch_entry_state_maps->raw();
|
||||
return canonical_catch_entry_moves_maps->raw();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Zone* zone_;
|
||||
TypedDataSet canonical_catch_entry_state_maps_;
|
||||
TypedDataSet canonical_catch_entry_moves_maps_;
|
||||
Code& code_;
|
||||
TypedData& catch_entry_state_maps_;
|
||||
TypedData& catch_entry_moves_maps_;
|
||||
};
|
||||
|
||||
DedupCatchEntryStateMapsVisitor visitor(Thread::Current()->zone());
|
||||
DedupCatchEntryMovesMapsVisitor visitor(Thread::Current()->zone());
|
||||
ProgramVisitor::VisitFunctions(&visitor);
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILER)
|
||||
@@ -724,7 +724,7 @@ void ProgramVisitor::Dedup() {
|
||||
DedupPcDescriptors();
|
||||
NOT_IN_PRECOMPILED(DedupDeoptEntries());
|
||||
#if defined(DART_PRECOMPILER)
|
||||
DedupCatchEntryStateMaps();
|
||||
DedupCatchEntryMovesMaps();
|
||||
#endif
|
||||
DedupCodeSourceMaps();
|
||||
DedupLists();
|
||||
|
||||
@@ -35,7 +35,7 @@ class ProgramVisitor : public AllStatic {
|
||||
static void DedupPcDescriptors();
|
||||
NOT_IN_PRECOMPILED(static void DedupDeoptEntries());
|
||||
#if defined(DART_PRECOMPILER)
|
||||
static void DedupCatchEntryStateMaps();
|
||||
static void DedupCatchEntryMovesMaps();
|
||||
#endif
|
||||
static void DedupCodeSourceMaps();
|
||||
static void DedupLists();
|
||||
|
||||
@@ -1391,7 +1391,7 @@ class RawCode : public RawObject {
|
||||
RawExceptionHandlers* exception_handlers_;
|
||||
RawPcDescriptors* pc_descriptors_;
|
||||
union {
|
||||
RawTypedData* catch_entry_state_maps_;
|
||||
RawTypedData* catch_entry_moves_maps_;
|
||||
RawSmi* variables_;
|
||||
} catch_entry_;
|
||||
RawArray* stackmaps_;
|
||||
|
||||
Reference in New Issue
Block a user