// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_DEOPTIMIZER_DEOPTIMIZER_H_ #define V8_DEOPTIMIZER_DEOPTIMIZER_H_ #include #include "src/builtins/builtins.h" #include "src/codegen/source-position.h" #include "src/deoptimizer/deoptimize-reason.h" #include "src/deoptimizer/frame-description.h" #include "src/deoptimizer/translated-state.h" #include "src/diagnostics/code-tracer.h" #include "src/objects/js-function.h" #if V8_ENABLE_WEBASSEMBLY #include "src/wasm/value-type.h" #endif // V8_ENABLE_WEBASSEMBLY namespace v8 { namespace internal { enum class BuiltinContinuationMode; class DeoptimizedFrameInfo; class Isolate; class Deoptimizer : public Malloced { public: struct DeoptInfo { DeoptInfo(SourcePosition position, DeoptimizeReason deopt_reason, uint32_t node_id, int deopt_id) : position(position), deopt_reason(deopt_reason), node_id(node_id), deopt_id(deopt_id) {} const SourcePosition position; const DeoptimizeReason deopt_reason; const uint32_t node_id; const int deopt_id; }; static DeoptInfo GetDeoptInfo(Code code, Address from); static int ComputeSourcePositionFromBytecodeArray( Isolate* isolate, SharedFunctionInfo shared, BytecodeOffset bytecode_offset); static const char* MessageFor(DeoptimizeKind kind); Handle function() const; Handle compiled_code() const; DeoptimizeKind deopt_kind() const { return deopt_kind_; } static Deoptimizer* New(Address raw_function, DeoptimizeKind kind, Address from, int fp_to_sp_delta, Isolate* isolate); static Deoptimizer* Grab(Isolate* isolate); // The returned object with information on the optimized frame needs to be // freed before another one can be generated. static DeoptimizedFrameInfo* DebuggerInspectableFrame(JavaScriptFrame* frame, int jsframe_index, Isolate* isolate); // Deoptimize the function now. Its current optimized code will never be run // again and any activations of the optimized code will get deoptimized when // execution returns. If {code} is specified then the given code is targeted // instead of the function code (e.g. OSR code not installed on function). static void DeoptimizeFunction(JSFunction function, Code code = Code()); // Deoptimize all code in the given isolate. V8_EXPORT_PRIVATE static void DeoptimizeAll(Isolate* isolate); // Deoptimizes all optimized code that has been previously marked // (via code->set_marked_for_deoptimization) and unlinks all functions that // refer to that code. static void DeoptimizeMarkedCode(Isolate* isolate); // Check the given address against a list of allowed addresses, to prevent a // potential attacker from using the frame creation process in the // deoptimizer, in particular the signing process, to gain control over the // program. // When building mksnapshot, always return false. static bool IsValidReturnAddress(Address address); ~Deoptimizer(); void MaterializeHeapObjects(); static void ComputeOutputFrames(Deoptimizer* deoptimizer); V8_EXPORT_PRIVATE static Builtin GetDeoptimizationEntry(DeoptimizeKind kind); // Returns true if {addr} is a deoptimization entry and stores its type in // {type_out}. Returns false if {addr} is not a deoptimization entry. static bool IsDeoptimizationEntry(Isolate* isolate, Address addr, DeoptimizeKind* type_out); // Code generation support. static int input_offset() { return offsetof(Deoptimizer, input_); } static int output_count_offset() { return offsetof(Deoptimizer, output_count_); } static int output_offset() { return offsetof(Deoptimizer, output_); } static int caller_frame_top_offset() { return offsetof(Deoptimizer, caller_frame_top_); } V8_EXPORT_PRIVATE static int GetDeoptimizedCodeCount(Isolate* isolate); Isolate* isolate() const { return isolate_; } static constexpr int kMaxNumberOfEntries = 16384; // This marker is passed to Deoptimizer::New as {deopt_exit_index} on // platforms that have fixed deopt sizes. The actual deoptimization id is then // calculated from the return address. static constexpr unsigned kFixedExitSizeMarker = kMaxUInt32; // Size of deoptimization exit sequence. V8_EXPORT_PRIVATE static const int kEagerDeoptExitSize; V8_EXPORT_PRIVATE static const int kLazyDeoptExitSize; // Tracing. static void TraceMarkForDeoptimization(Code code, const char* reason); static void TraceEvictFromOptimizedCodeCache(SharedFunctionInfo sfi, const char* reason); private: void QueueValueForMaterialization(Address output_address, Object obj, const TranslatedFrame::iterator& iterator); Deoptimizer(Isolate* isolate, JSFunction function, DeoptimizeKind kind, Address from, int fp_to_sp_delta); Code FindOptimizedCode(); void DeleteFrameDescriptions(); void DoComputeOutputFrames(); void DoComputeUnoptimizedFrame(TranslatedFrame* translated_frame, int frame_index, bool goto_catch_handler); void DoComputeArgumentsAdaptorFrame(TranslatedFrame* translated_frame, int frame_index); void DoComputeConstructStubFrame(TranslatedFrame* translated_frame, int frame_index); static Builtin TrampolineForBuiltinContinuation(BuiltinContinuationMode mode, bool must_handle_result); #if V8_ENABLE_WEBASSEMBLY TranslatedValue TranslatedValueForWasmReturnKind( base::Optional wasm_call_return_kind); #endif // V8_ENABLE_WEBASSEMBLY void DoComputeBuiltinContinuation(TranslatedFrame* translated_frame, int frame_index, BuiltinContinuationMode mode); unsigned ComputeInputFrameAboveFpFixedSize() const; unsigned ComputeInputFrameSize() const; static unsigned ComputeIncomingArgumentSize(SharedFunctionInfo shared); static void MarkAllCodeForContext(NativeContext native_context); static void DeoptimizeMarkedCodeForContext(NativeContext native_context); // Searches the list of known deoptimizing code for a Code object // containing the given address (which is supposedly faster than // searching all code objects). Code FindDeoptimizingCode(Address addr); // Tracing. bool tracing_enabled() const { return trace_scope_ != nullptr; } bool verbose_tracing_enabled() const { return FLAG_trace_deopt_verbose && tracing_enabled(); } CodeTracer::Scope* trace_scope() const { return trace_scope_; } CodeTracer::Scope* verbose_trace_scope() const { return FLAG_trace_deopt_verbose ? trace_scope() : nullptr; } void TraceDeoptBegin(int optimization_id, BytecodeOffset bytecode_offset); void TraceDeoptEnd(double deopt_duration); #ifdef DEBUG static void TraceFoundActivation(Isolate* isolate, JSFunction function); #endif static void TraceDeoptAll(Isolate* isolate); static void TraceDeoptMarked(Isolate* isolate); Isolate* isolate_; JSFunction function_; Code compiled_code_; unsigned deopt_exit_index_; DeoptimizeKind deopt_kind_; Address from_; int fp_to_sp_delta_; bool deoptimizing_throw_; int catch_handler_data_; int catch_handler_pc_offset_; // Input frame description. FrameDescription* input_; // Number of output frames. int output_count_; // Array of output frame descriptions. FrameDescription** output_; // Caller frame details computed from input frame. intptr_t caller_frame_top_; intptr_t caller_fp_; intptr_t caller_pc_; intptr_t caller_constant_pool_; // The argument count of the bottom most frame. int actual_argument_count_; // Key for lookup of previously materialized objects. intptr_t stack_fp_; TranslatedState translated_state_; struct ValueToMaterialize { Address output_slot_address_; TranslatedFrame::iterator value_; }; std::vector values_to_materialize_; #ifdef DEBUG DisallowGarbageCollection* disallow_garbage_collection_; #endif // DEBUG // Note: This is intentionally not a unique_ptr s.t. the Deoptimizer // satisfies is_standard_layout, needed for offsetof(). CodeTracer::Scope* const trace_scope_; friend class DeoptimizedFrameInfo; friend class FrameDescription; friend class FrameWriter; }; } // namespace internal } // namespace v8 #endif // V8_DEOPTIMIZER_DEOPTIMIZER_H_