diff --git a/runtime/docs/compiler/exceptions.md b/runtime/docs/compiler/exceptions.md new file mode 100644 index 00000000000..222e5f8be64 --- /dev/null +++ b/runtime/docs/compiler/exceptions.md @@ -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`. + +![Catch Block Entry](images/catch-block-entry-0.png) + +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. + +![Catch Block Entry](images/catch-block-entry-1.png) + +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(*(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`. diff --git a/runtime/docs/compiler/images/catch-block-entry-0.png b/runtime/docs/compiler/images/catch-block-entry-0.png new file mode 100644 index 00000000000..b6013de5ed7 Binary files /dev/null and b/runtime/docs/compiler/images/catch-block-entry-0.png differ diff --git a/runtime/docs/compiler/images/catch-block-entry-1.png b/runtime/docs/compiler/images/catch-block-entry-1.png new file mode 100644 index 00000000000..c41adb439b7 Binary files /dev/null and b/runtime/docs/compiler/images/catch-block-entry-1.png differ diff --git a/runtime/tests/vm/dart/catch_entry_state_test.dart b/runtime/tests/vm/dart/catch_entry_state_test.dart new file mode 100644 index 00000000000..ed33f178432 --- /dev/null +++ b/runtime/tests/vm/dart/catch_entry_state_test.dart @@ -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); +} diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status index 163282516ee..df4f8e3bc94 100644 --- a/runtime/tests/vm/vm.status +++ b/runtime/tests/vm/vm.status @@ -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 diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc index f799a8d4fb7..84bc363ad50 100644 --- a/runtime/vm/clustered_snapshot.cc +++ b/runtime/vm/clustered_snapshot.cc @@ -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(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(d->ReadRef()); #else code->ptr()->catch_entry_.variables_ = diff --git a/runtime/vm/code_descriptors.cc b/runtime/vm/code_descriptors.cc index da771141cd5..84d98825e35 100644 --- a/runtime/vm/code_descriptors.cc +++ b/runtime/vm/code_descriptors.cc @@ -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(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 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); diff --git a/runtime/vm/code_descriptors.h b/runtime/vm/code_descriptors.h index 6c0daab9c3e..dfec824eaee 100644 --- a/runtime/vm/code_descriptors.h +++ b/runtime/vm/code_descriptors.h @@ -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 moves_; + GrowableArray 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 diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc index 3448fea416c..d2fdd609bb4 100644 --- a/runtime/vm/compiler/aot/precompiler.cc +++ b/runtime/vm/compiler/aot/precompiler.cc @@ -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); diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc index 3c9e52413c9..161889fbb0d 100644 --- a/runtime/vm/compiler/backend/flow_graph.cc +++ b/runtime/vm/compiler/backend/flow_graph.cc @@ -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) { diff --git a/runtime/vm/compiler/backend/flow_graph.h b/runtime/vm/compiler/backend/flow_graph.h index a3b4ef00c9e..ba3e046ab8c 100644 --- a/runtime/vm/compiler/backend/flow_graph.h +++ b/runtime/vm/compiler/backend/flow_graph.h @@ -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, diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc index ad396ce8e01..0dcc98fb8ed 100644 --- a/runtime/vm/compiler/backend/flow_graph_compiler.cc +++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc @@ -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* 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(); diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.h b/runtime/vm/compiler/backend/flow_graph_compiler.h index 7baed3cf190..1df8a26849d 100644 --- a/runtime/vm/compiler/backend/flow_graph_compiler.h +++ b/runtime/vm/compiler/backend/flow_graph_compiler.h @@ -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 block_info_; GrowableArray deopt_infos_; GrowableArray slow_path_code_; diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc index a6ff28b02a7..f53c4387aaf 100644 --- a/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc +++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm.cc @@ -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()); } diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc index 2dc1a303da9..998842ad1db 100644 --- a/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc +++ b/runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc @@ -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()); } diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc index 93dde40e221..bfc712ad8da 100644 --- a/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc +++ b/runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc @@ -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()); } diff --git a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc index 5c1329a133c..ae17719e4e7 100644 --- a/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc +++ b/runtime/vm/compiler/backend/flow_graph_compiler_x64.cc @@ -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); } diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc index fb9dcdbd27a..3e94025b9d3 100644 --- a/runtime/vm/compiler/backend/il.cc +++ b/runtime/vm/compiler/backend/il.cc @@ -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(); } diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc index 6474df0ae9f..99b85adf3df 100644 --- a/runtime/vm/compiler/backend/il_arm.cc +++ b/runtime/vm/compiler/backend/il_arm.cc @@ -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(), diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc index ed0f4002aef..61b4766153d 100644 --- a/runtime/vm/compiler/backend/il_arm64.cc +++ b/runtime/vm/compiler/backend/il_arm64.cc @@ -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(), diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc index a5e521b0d33..f0cfb7b04ae 100644 --- a/runtime/vm/compiler/backend/il_x64.cc +++ b/runtime/vm/compiler/backend/il_x64.cc @@ -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(), diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc index 14670ceb588..e84b28db347 100644 --- a/runtime/vm/compiler/jit/compiler.cc +++ b/runtime/vm/compiler/jit/compiler.cc @@ -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); diff --git a/runtime/vm/deopt_instructions.cc b/runtime/vm/deopt_instructions.cc index ec6fa743e84..ec35ebc8565 100644 --- a/runtime/vm/deopt_instructions.cc +++ b/runtime/vm/deopt_instructions.cc @@ -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 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(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(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(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 {}; @@ -611,7 +618,7 @@ class DeoptMintPairInstr : public DeoptIntegerInstrBase { DISALLOW_COPY_AND_ASSIGN(DeoptMintPairInstr); }; -template +template class DeoptIntInstr : public DeoptIntegerInstrBase { public: explicit DeoptIntInstr(intptr_t source_index) @@ -629,17 +636,35 @@ class DeoptIntInstr : public DeoptIntegerInstrBase { return static_cast(source_.Value(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 DeoptUint32Instr; -typedef DeoptIntInstr DeoptInt32Instr; -typedef DeoptIntInstr DeoptMintInstr; +typedef DeoptIntInstr + DeoptUint32Instr; +typedef DeoptIntInstr + DeoptInt32Instr; +typedef DeoptIntInstr + DeoptMintInstr; -template +template class DeoptFpuInstr : public DeoptInstr { public: explicit DeoptFpuInstr(intptr_t source_index) : source_(source_index) {} @@ -658,22 +683,39 @@ class DeoptFpuInstr : public DeoptInstr { reinterpret_cast(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 DeoptDoubleInstr; +typedef DeoptFpuInstr + DeoptDoubleInstr; // Simd128 types. -typedef DeoptFpuInstr +typedef DeoptFpuInstr DeoptFloat32x4Instr; -typedef DeoptFpuInstr - DeoptFloat32x4Instr; -typedef DeoptFpuInstr +typedef DeoptFpuInstr DeoptFloat64x2Instr; -typedef DeoptFpuInstr +typedef DeoptFpuInstr DeoptInt32x4Instr; // Deoptimization instruction creating a PC marker for the code of diff --git a/runtime/vm/deopt_instructions.h b/runtime/vm/deopt_instructions.h index 38d95fdce33..e2096b61d31 100644 --- a/runtime/vm/deopt_instructions.h +++ b/runtime/vm/deopt_instructions.h @@ -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; diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc index 54379301913..52487822de3 100644 --- a/runtime/vm/exceptions.cc +++ b/runtime/vm/exceptions.cc @@ -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(fp + stack_slot * kWordSize); -#else - const intptr_t frame_slot = - runtime_frame_layout.FrameSlotForVariableIndex(-stack_slot); - return reinterpret_cast(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(fp, move.src_slot())); + break; + + case CatchEntryMove::SourceKind::kFloat32x4Slot: + value = Float32x4::New(*SlotAt(fp, move.src_slot())); + break; + + case CatchEntryMove::SourceKind::kFloat64x2Slot: + value = Float64x2::New(*SlotAt(fp, move.src_slot())); + break; + + case CatchEntryMove::SourceKind::kInt32x4Slot: + value = Int32x4::New(*SlotAt(fp, move.src_slot())); + break; + + case CatchEntryMove::SourceKind::kInt64PairSlot: + value = Integer::New( + Utils::LowHighTo64Bits(*SlotAt(fp, move.src_lo_slot()), + *SlotAt(fp, move.src_hi_slot()))); + break; + + case CatchEntryMove::SourceKind::kInt64Slot: + value = Integer::New(*SlotAt(fp, move.src_slot())); + break; + + case CatchEntryMove::SourceKind::kInt32Slot: + value = Integer::New(*SlotAt(fp, move.src_slot())); + break; + + case CatchEntryMove::SourceKind::kUint32Slot: + value = Integer::New(*SlotAt(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(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 + static T* SlotAt(uword fp, int stack_slot) { +#if defined(TARGET_ARCH_DBC) + return reinterpret_cast(fp + stack_slot * kWordSize); +#else + const intptr_t frame_slot = + runtime_frame_layout.FrameSlotForVariableIndex(-stack_slot); + return reinterpret_cast(fp + frame_slot * kWordSize); +#endif + } + + static RawObject** TaggedSlotAt(uword fp, int stack_slot) { + return SlotAt(fp, stack_slot); + } + typedef ReadStream::Raw 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; + 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; + 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); diff --git a/runtime/vm/exceptions.h b/runtime/vm/exceptions.h index d9b8c14cd31..b681b0ff6f9 100644 --- a/runtime/vm/exceptions.h +++ b/runtime/vm/exceptions.h @@ -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(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; + + static constexpr intptr_t kHalfSourceBits = kBitsPerWord / 2; + using LoSourceSlot = BitField; + using HiSourceSlot = + BitField; + + 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( + malloc(sizeof(CatchEntryMoves) + sizeof(CatchEntryMove) * num_moves)); + result->count_ = num_moves; + return result; + } + + static void Free(const CatchEntryMoves* moves) { + free(const_cast(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(this + 1); + } + + const CatchEntryMove* Moves() const { + return reinterpret_cast(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_; }; diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc index de9911f323a..2b319e28d7e 100644 --- a/runtime/vm/heap/heap.cc +++ b/runtime/vm/heap/heap.cc @@ -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(); } } diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc index af54df85721..aa4ae114fea 100644 --- a/runtime/vm/isolate.cc +++ b/runtime/vm/isolate.cc @@ -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); diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h index b40651f0249..07e61972c0e 100644 --- a/runtime/vm/isolate.h +++ b/runtime/vm/isolate.h @@ -130,7 +130,7 @@ class NoReloadScope : public StackResource { // Fixed cache for exception handler lookup. typedef FixedCache HandlerInfoCache; // Fixed cache for catch entry state lookup. -typedef FixedCache CatchEntryStateCache; +typedef FixedCache 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_; diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index c8149f6f327..434de7b1e81 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -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 diff --git a/runtime/vm/object.h b/runtime/vm/object.h index 6613544b3d0..11ea3987b6b 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -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_; } diff --git a/runtime/vm/program_visitor.cc b/runtime/vm/program_visitor.cc index a6400e89088..2fe88417e0a 100644 --- a/runtime/vm/program_visitor.cc +++ b/runtime/vm/program_visitor.cc @@ -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(); diff --git a/runtime/vm/program_visitor.h b/runtime/vm/program_visitor.h index 4125f71512c..f2b255aa61e 100644 --- a/runtime/vm/program_visitor.h +++ b/runtime/vm/program_visitor.h @@ -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(); diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h index 57950fa716f..f8dacf3af19 100644 --- a/runtime/vm/raw_object.h +++ b/runtime/vm/raw_object.h @@ -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_;