Commit f6b4e6ce authored by Victor Gomes's avatar Victor Gomes Committed by V8 LUCI CQ

[maglev] Preparation to support exception handlers

This does not enable exception handlers yet, we still bail out in
MaglevCompiler::Compile if we have an exception handler table in
the bytecode array.

This CL:
- Generates code for exception handler blocks (which previously were
set as dead code)
- Creates a machinery for nodes to set the property CanThrow
- Reads the exception handler table from the bytecode array and
identifies if we're emitting nodes inside a try-block and for which
handler we should jump in case of an exception
- Generates an exception handler table for Maglev code


Change-Id: Ifc9d4cb7440d3222f4fda48a86e4e482340b3b15
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3854061
Commit-Queue: Victor Gomes <victorgomes@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82726}
parent 64ca6cc4
...@@ -2235,8 +2235,7 @@ void OptimizedFrame::Summarize(std::vector<FrameSummary>* frames) const { ...@@ -2235,8 +2235,7 @@ void OptimizedFrame::Summarize(std::vector<FrameSummary>* frames) const {
} }
} }
// TODO(leszeks): Move to OptimizedFrame when/if maglev supports exceptions. int OptimizedFrame::LookupExceptionHandlerInTable(
int TurbofanFrame::LookupExceptionHandlerInTable(
int* data, HandlerTable::CatchPrediction* prediction) { int* data, HandlerTable::CatchPrediction* prediction) {
// We cannot perform exception prediction on optimized code. Instead, we need // We cannot perform exception prediction on optimized code. Instead, we need
// to use FrameSummary to find the corresponding code offset in unoptimized // to use FrameSummary to find the corresponding code offset in unoptimized
......
...@@ -826,6 +826,10 @@ class OptimizedFrame : public JavaScriptFrame { ...@@ -826,6 +826,10 @@ class OptimizedFrame : public JavaScriptFrame {
static int StackSlotOffsetRelativeToFp(int slot_index); static int StackSlotOffsetRelativeToFp(int slot_index);
// Lookup exception handler for current {pc}, returns -1 if none found.
int LookupExceptionHandlerInTable(
int* data, HandlerTable::CatchPrediction* prediction) override;
protected: protected:
inline explicit OptimizedFrame(StackFrameIteratorBase* iterator); inline explicit OptimizedFrame(StackFrameIteratorBase* iterator);
}; };
...@@ -946,10 +950,6 @@ class TurbofanFrame : public OptimizedFrame { ...@@ -946,10 +950,6 @@ class TurbofanFrame : public OptimizedFrame {
public: public:
Type type() const override { return TURBOFAN; } Type type() const override { return TURBOFAN; }
// Lookup exception handler for current {pc}, returns -1 if none found.
int LookupExceptionHandlerInTable(
int* data, HandlerTable::CatchPrediction* prediction) override;
int ComputeParametersCount() const override; int ComputeParametersCount() const override;
bool HasTaggedOutgoingParams(CodeLookupResult& code_lookup) const; bool HasTaggedOutgoingParams(CodeLookupResult& code_lookup) const;
......
...@@ -2114,11 +2114,12 @@ Object Isolate::UnwindAndFindHandler() { ...@@ -2114,11 +2114,12 @@ Object Isolate::UnwindAndFindHandler() {
} }
#endif // V8_ENABLE_WEBASSEMBLY #endif // V8_ENABLE_WEBASSEMBLY
case StackFrame::MAGLEV:
case StackFrame::TURBOFAN: { case StackFrame::TURBOFAN: {
// For optimized frames we perform a lookup in the handler table. // For optimized frames we perform a lookup in the handler table.
if (!catchable_by_js) break; if (!catchable_by_js) break;
TurbofanFrame* js_frame = static_cast<TurbofanFrame*>(frame); OptimizedFrame* opt_frame = static_cast<OptimizedFrame*>(frame);
int offset = js_frame->LookupExceptionHandlerInTable(nullptr, nullptr); int offset = opt_frame->LookupExceptionHandlerInTable(nullptr, nullptr);
if (offset < 0) break; if (offset < 0) break;
// The code might be an optimized code or a turbofanned builtin. // The code might be an optimized code or a turbofanned builtin.
CodeT code = frame->LookupCodeT().ToCodeT(); CodeT code = frame->LookupCodeT().ToCodeT();
......
...@@ -100,6 +100,15 @@ class BasicBlock { ...@@ -100,6 +100,15 @@ class BasicBlock {
} }
bool has_state() const { return !is_empty_block() && state_ != nullptr; } bool has_state() const { return !is_empty_block() && state_ != nullptr; }
#ifdef DEBUG
void set_is_exception_handler_block(bool value) {
is_exception_handler_block_ = value;
}
bool is_exception_handler_block() const {
return is_exception_handler_block_;
}
#endif // DEBUG
private: private:
bool is_empty_block_ = false; bool is_empty_block_ = false;
Node::List nodes_; Node::List nodes_;
...@@ -110,6 +119,10 @@ class BasicBlock { ...@@ -110,6 +119,10 @@ class BasicBlock {
}; };
BasicBlock* empty_block_predecessor_; BasicBlock* empty_block_predecessor_;
Label label_; Label label_;
#ifdef DEBUG
bool is_exception_handler_block_ = false;
#endif // DEBUG
}; };
} // namespace maglev } // namespace maglev
......
...@@ -57,6 +57,14 @@ class MaglevCodeGenState { ...@@ -57,6 +57,14 @@ class MaglevCodeGenState {
} }
inline void DefineLazyDeoptPoint(LazyDeoptInfo* info); inline void DefineLazyDeoptPoint(LazyDeoptInfo* info);
void PushHandlerInfo(ExceptionHandlerInfo* handler) {
handlers_.push_back(handler);
}
const std::vector<ExceptionHandlerInfo*>& handlers() const {
return handlers_;
}
inline void DefineExceptionHandlerPoint(ExceptionHandlerInfo* info);
compiler::NativeContextRef native_context() const { compiler::NativeContextRef native_context() const {
return broker()->target_native_context(); return broker()->target_native_context();
} }
...@@ -111,6 +119,8 @@ class MaglevCodeGenState { ...@@ -111,6 +119,8 @@ class MaglevCodeGenState {
std::vector<DeferredCodeInfo*> deferred_code_; std::vector<DeferredCodeInfo*> deferred_code_;
std::vector<EagerDeoptInfo*> eager_deopts_; std::vector<EagerDeoptInfo*> eager_deopts_;
std::vector<LazyDeoptInfo*> lazy_deopts_; std::vector<LazyDeoptInfo*> lazy_deopts_;
std::vector<ExceptionHandlerInfo*> handlers_;
int untagged_slots_ = 0; int untagged_slots_ = 0;
int tagged_slots_ = 0; int tagged_slots_ = 0;
}; };
...@@ -156,6 +166,13 @@ inline void MaglevCodeGenState::DefineLazyDeoptPoint(LazyDeoptInfo* info) { ...@@ -156,6 +166,13 @@ inline void MaglevCodeGenState::DefineLazyDeoptPoint(LazyDeoptInfo* info) {
safepoint_table_builder()->DefineSafepoint(masm()); safepoint_table_builder()->DefineSafepoint(masm());
} }
inline void MaglevCodeGenState::DefineExceptionHandlerPoint(
ExceptionHandlerInfo* info) {
if (!info->HasExceptionHandler()) return;
info->pc_offset = masm()->pc_offset_for_safepoint();
PushHandlerInfo(info);
}
} // namespace maglev } // namespace maglev
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -813,13 +813,20 @@ class MaglevCodeGeneratorImpl final { ...@@ -813,13 +813,20 @@ class MaglevCodeGeneratorImpl final {
masm()->Align(Code::kMetadataAlignment); masm()->Align(Code::kMetadataAlignment);
safepoint_table_builder()->Emit(masm()); safepoint_table_builder()->Emit(masm());
// Exception handler table.
handler_table_offset_ = HandlerTable::EmitReturnTableStart(masm());
for (ExceptionHandlerInfo* info : code_gen_state_.handlers()) {
HandlerTable::EmitReturnEntry(
masm(), info->pc_offset,
info->catch_block.block_ptr()->label()->pos());
}
} }
MaybeHandle<Code> BuildCodeObject() { MaybeHandle<Code> BuildCodeObject() {
CodeDesc desc; CodeDesc desc;
static constexpr int kNoHandlerTableOffset = 0;
masm()->GetCode(isolate(), &desc, safepoint_table_builder(), masm()->GetCode(isolate(), &desc, safepoint_table_builder(),
kNoHandlerTableOffset); handler_table_offset_);
return Factory::CodeBuilder{isolate(), desc, CodeKind::MAGLEV} return Factory::CodeBuilder{isolate(), desc, CodeKind::MAGLEV}
.set_stack_slots(stack_slot_count_with_fixed_frame()) .set_stack_slots(stack_slot_count_with_fixed_frame())
.set_deoptimization_data(GenerateDeoptimizationData()) .set_deoptimization_data(GenerateDeoptimizationData())
...@@ -932,6 +939,7 @@ class MaglevCodeGeneratorImpl final { ...@@ -932,6 +939,7 @@ class MaglevCodeGeneratorImpl final {
Graph* const graph_; Graph* const graph_;
int deopt_exit_start_offset_ = -1; int deopt_exit_start_offset_ = -1;
int handler_table_offset_ = 0;
}; };
// static // static
......
...@@ -50,7 +50,8 @@ MaglevGraphBuilder::MaglevGraphBuilder(LocalIsolate* local_isolate, ...@@ -50,7 +50,8 @@ MaglevGraphBuilder::MaglevGraphBuilder(LocalIsolate* local_isolate,
// exit when needed. // exit when needed.
merge_states_(zone()->NewArray<MergePointInterpreterFrameState*>( merge_states_(zone()->NewArray<MergePointInterpreterFrameState*>(
bytecode().length() + 1)), bytecode().length() + 1)),
current_interpreter_frame_(*compilation_unit_) { current_interpreter_frame_(*compilation_unit_),
catch_block_stack_(zone()) {
memset(merge_states_, 0, memset(merge_states_, 0,
(bytecode().length() + 1) * sizeof(InterpreterFrameState*)); (bytecode().length() + 1) * sizeof(InterpreterFrameState*));
// Default construct basic block refs. // Default construct basic block refs.
......
...@@ -130,6 +130,17 @@ class MaglevGraphBuilder { ...@@ -130,6 +130,17 @@ class MaglevGraphBuilder {
return merge_states_[offset] != nullptr; return merge_states_[offset] != nullptr;
} }
// Return true if current offset is the beginning of a catch block, that
// is, it is the offset handler in the exception handler table in the
// bytecode array.
bool IsHandlerOffset(int offset) const {
HandlerTable table(*bytecode().object());
for (int i = 0; i < table.NumberOfRangeEntries(); i++) {
if (offset == table.GetRangeHandler(i)) return true;
}
return false;
}
// Called when a block is killed by an unconditional eager deopt. // Called when a block is killed by an unconditional eager deopt.
void EmitUnconditionalDeopt(DeoptimizeReason reason) { void EmitUnconditionalDeopt(DeoptimizeReason reason) {
// Create a block rather than calling finish, since we don't yet know the // Create a block rather than calling finish, since we don't yet know the
...@@ -192,6 +203,19 @@ class MaglevGraphBuilder { ...@@ -192,6 +203,19 @@ class MaglevGraphBuilder {
ProcessMergePoint(offset); ProcessMergePoint(offset);
StartNewBlock(offset); StartNewBlock(offset);
// If we have no predecessor, then we can be the start of an exception
// handler block.
} else if (predecessors_[offset] == 0 && IsHandlerOffset(offset)) {
// If we have no reference to this block, then the exception handler is
// dead.
if (!jump_targets_[offset].has_ref()) {
MarkBytecodeDead();
return;
}
StartNewBlock(offset);
#ifdef DEBUG
current_block_->set_is_exception_handler_block(true);
#endif // DEBUG
} else if (V8_UNLIKELY(current_block_ == nullptr)) { } else if (V8_UNLIKELY(current_block_ == nullptr)) {
// If we don't have a current block, the bytecode must be dead (because of // If we don't have a current block, the bytecode must be dead (because of
// some earlier deopt). Mark this bytecode dead too and return. // some earlier deopt). Mark this bytecode dead too and return.
...@@ -208,6 +232,34 @@ class MaglevGraphBuilder { ...@@ -208,6 +232,34 @@ class MaglevGraphBuilder {
MarkBytecodeDead(); MarkBytecodeDead();
return; return;
} }
// Handle exceptions if we have a table.
if (bytecode().handler_table_size() > 0) {
if (catch_block_stack_.size() > 0) {
// Pop all entries where offset >= end.
while (catch_block_stack_.size() > 0) {
HandlerTableEntry& entry = catch_block_stack_.top();
if (offset < entry.end) break;
catch_block_stack_.pop();
}
}
// Push new entries from interpreter handler table where offset >= start
// && offset < end.
HandlerTable table(*bytecode().object());
while (next_handler_table_index_ < table.NumberOfRangeEntries()) {
int start = table.GetRangeStart(next_handler_table_index_);
if (offset < start) break;
int end = table.GetRangeEnd(next_handler_table_index_);
if (offset >= end) {
next_handler_table_index_++;
continue;
}
int handler = table.GetRangeHandler(next_handler_table_index_);
catch_block_stack_.push({end, handler});
next_handler_table_index_++;
}
}
DCHECK_NOT_NULL(current_block_); DCHECK_NOT_NULL(current_block_);
if (FLAG_trace_maglev_graph_building) { if (FLAG_trace_maglev_graph_building) {
std::cout << std::setw(4) << iterator_.current_offset() << " : "; std::cout << std::setw(4) << iterator_.current_offset() << " : ";
...@@ -268,7 +320,7 @@ class MaglevGraphBuilder { ...@@ -268,7 +320,7 @@ class MaglevGraphBuilder {
} }
template <typename NodeT, typename... Args> template <typename NodeT, typename... Args>
NodeT* CreateNewNode(Args&&... args) { NodeT* CreateNewNodeHelper(Args&&... args) {
if constexpr (NodeT::kProperties.can_eager_deopt()) { if constexpr (NodeT::kProperties.can_eager_deopt()) {
return NodeBase::New<NodeT>(zone(), *compilation_unit_, return NodeBase::New<NodeT>(zone(), *compilation_unit_,
GetLatestCheckpointedState(), GetLatestCheckpointedState(),
...@@ -282,6 +334,22 @@ class MaglevGraphBuilder { ...@@ -282,6 +334,22 @@ class MaglevGraphBuilder {
} }
} }
template <typename NodeT, typename... Args>
NodeT* CreateNewNode(Args&&... args) {
NodeT* node = CreateNewNodeHelper<NodeT>(std::forward<Args>(args)...);
if constexpr (NodeT::kProperties.can_throw()) {
if (catch_block_stack_.size() > 0) {
// Inside a try-block.
new (node->exception_handler_info()) ExceptionHandlerInfo(
&jump_targets_[catch_block_stack_.top().handler]);
} else {
// Patch no exception handler marker.
new (node->exception_handler_info()) ExceptionHandlerInfo();
}
}
return node;
}
template <Builtin kBuiltin> template <Builtin kBuiltin>
CallBuiltin* BuildCallBuiltin(std::initializer_list<ValueNode*> inputs) { CallBuiltin* BuildCallBuiltin(std::initializer_list<ValueNode*> inputs) {
using Descriptor = typename CallInterfaceDescriptorFor<kBuiltin>::type; using Descriptor = typename CallInterfaceDescriptorFor<kBuiltin>::type;
...@@ -960,6 +1028,13 @@ class MaglevGraphBuilder { ...@@ -960,6 +1028,13 @@ class MaglevGraphBuilder {
InterpreterFrameState current_interpreter_frame_; InterpreterFrameState current_interpreter_frame_;
struct HandlerTableEntry {
int end;
int handler;
};
ZoneStack<HandlerTableEntry> catch_block_stack_;
int next_handler_table_index_ = 0;
#ifdef DEBUG #ifdef DEBUG
std::unordered_set<Node*> new_nodes_; std::unordered_set<Node*> new_nodes_;
#endif #endif
......
...@@ -354,7 +354,13 @@ void MaglevPrintingVisitor::PreProcessBasicBlock( ...@@ -354,7 +354,13 @@ void MaglevPrintingVisitor::PreProcessBasicBlock(
} }
int block_id = graph_labeller->BlockId(block); int block_id = graph_labeller->BlockId(block);
os_ << "Block b" << block_id << "\n"; os_ << "Block b" << block_id;
#ifdef DEBUG
if (block->is_exception_handler_block()) {
os_ << " (exception handler)";
}
#endif // DEBUG
os_ << "\n";
MaglevPrintingVisitorOstream::cast(os_for_additional_info_)->set_padding(1); MaglevPrintingVisitorOstream::cast(os_for_additional_info_)->set_padding(1);
} }
......
...@@ -2951,6 +2951,7 @@ void CallRuntime::GenerateCode(MaglevCodeGenState* code_gen_state, ...@@ -2951,6 +2951,7 @@ void CallRuntime::GenerateCode(MaglevCodeGenState* code_gen_state,
__ CallRuntime(function_id(), num_args()); __ CallRuntime(function_id(), num_args());
// TODO(victorgomes): Not sure if this is needed for all runtime calls. // TODO(victorgomes): Not sure if this is needed for all runtime calls.
code_gen_state->DefineLazyDeoptPoint(lazy_deopt_info()); code_gen_state->DefineLazyDeoptPoint(lazy_deopt_info());
code_gen_state->DefineExceptionHandlerPoint(exception_handler_info());
} }
void CallRuntime::PrintParams(std::ostream& os, void CallRuntime::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const { MaglevGraphLabeller* graph_labeller) const {
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment