Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

17
deps/v8/src/interpreter/DEPS vendored Normal file
View File

@ -0,0 +1,17 @@
# Copyright 2024 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.
specific_include_rules = {
"interpreter-generator.cc": [
"+src/compiler/linkage.h",
"+src/compiler/pipeline.h",
"+src/compiler/turboshaft/builtin-compiler.h",
],
"interpreter-generator-tsa.h": [
"+src/compiler/turboshaft/builtin-compiler.h",
],
".*-tsa.cc": [
"+src/compiler",
],
}

14
deps/v8/src/interpreter/DIR_METADATA vendored Normal file
View File

@ -0,0 +1,14 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Blink>JavaScript>Interpreter"
}
buganizer_public: {
component_id: 1456960
}

5
deps/v8/src/interpreter/OWNERS vendored Normal file
View File

@ -0,0 +1,5 @@
cbruni@chromium.org
ishell@chromium.org
jgruber@chromium.org
leszeks@chromium.org
syg@chromium.org

View File

@ -0,0 +1,97 @@
// Copyright 2017 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_INTERPRETER_BLOCK_COVERAGE_BUILDER_H_
#define V8_INTERPRETER_BLOCK_COVERAGE_BUILDER_H_
#include "src/ast/ast-source-ranges.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
namespace interpreter {
// Used to generate IncBlockCounter bytecodes and the {source range, slot}
// mapping for block coverage.
class BlockCoverageBuilder final : public ZoneObject {
public:
BlockCoverageBuilder(Zone* zone, BytecodeArrayBuilder* builder,
SourceRangeMap* source_range_map)
: slots_(0, zone),
builder_(builder),
source_range_map_(source_range_map) {
DCHECK_NOT_NULL(builder);
DCHECK_NOT_NULL(source_range_map);
}
static constexpr int kNoCoverageArraySlot = -1;
int AllocateBlockCoverageSlot(ZoneObject* node, SourceRangeKind kind) {
AstNodeSourceRanges* ranges = source_range_map_->Find(node);
if (ranges == nullptr) return kNoCoverageArraySlot;
SourceRange range = ranges->GetRange(kind);
if (range.IsEmpty()) return kNoCoverageArraySlot;
const int slot = static_cast<int>(slots_.size());
slots_.emplace_back(range);
return slot;
}
int AllocateNaryBlockCoverageSlot(NaryOperation* node, size_t index) {
NaryOperationSourceRanges* ranges =
static_cast<NaryOperationSourceRanges*>(source_range_map_->Find(node));
if (ranges == nullptr) return kNoCoverageArraySlot;
SourceRange range = ranges->GetRangeAtIndex(index);
if (range.IsEmpty()) return kNoCoverageArraySlot;
const int slot = static_cast<int>(slots_.size());
slots_.emplace_back(range);
return slot;
}
int AllocateConditionalChainBlockCoverageSlot(ConditionalChain* node,
SourceRangeKind kind,
size_t index) {
ConditionalChainSourceRanges* ranges =
static_cast<ConditionalChainSourceRanges*>(
source_range_map_->Find(node));
if (ranges == nullptr) return kNoCoverageArraySlot;
SourceRange range = ranges->GetRangeAtIndex(kind, index);
if (range.IsEmpty()) return kNoCoverageArraySlot;
const int slot = static_cast<int>(slots_.size());
slots_.emplace_back(range);
return slot;
}
void IncrementBlockCounter(int coverage_array_slot) {
if (coverage_array_slot == kNoCoverageArraySlot) return;
builder_->IncBlockCounter(coverage_array_slot);
}
void IncrementBlockCounter(ZoneObject* node, SourceRangeKind kind) {
int slot = AllocateBlockCoverageSlot(node, kind);
IncrementBlockCounter(slot);
}
const ZoneVector<SourceRange>& slots() const { return slots_; }
private:
// Contains source range information for allocated block coverage counter
// slots. Slot i covers range slots_[i].
ZoneVector<SourceRange> slots_;
BytecodeArrayBuilder* builder_;
SourceRangeMap* source_range_map_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BLOCK_COVERAGE_BUILDER_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,687 @@
// Copyright 2015 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_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_
#define V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_
#include <optional>
#include "src/ast/ast.h"
#include "src/base/export-template.h"
#include "src/common/globals.h"
#include "src/interpreter/bytecode-array-writer.h"
#include "src/interpreter/bytecode-flags-and-tokens.h"
#include "src/interpreter/bytecode-register-allocator.h"
#include "src/interpreter/bytecode-register.h"
#include "src/interpreter/bytecode-source-info.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/constant-array-builder.h"
#include "src/interpreter/handler-table-builder.h"
namespace v8 {
namespace internal {
class BytecodeArray;
class FeedbackVectorSpec;
class Isolate;
namespace interpreter {
class BytecodeLabel;
class BytecodeLoopHeader;
class BytecodeNode;
class BytecodeRegisterOptimizer;
class BytecodeJumpTable;
class Register;
class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
public:
BytecodeArrayBuilder(
Zone* zone, int parameter_count, int locals_count,
FeedbackVectorSpec* feedback_vector_spec = nullptr,
SourcePositionTableBuilder::RecordingMode source_position_mode =
SourcePositionTableBuilder::RECORD_SOURCE_POSITIONS);
BytecodeArrayBuilder(const BytecodeArrayBuilder&) = delete;
BytecodeArrayBuilder& operator=(const BytecodeArrayBuilder&) = delete;
template <typename IsolateT>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
Handle<BytecodeArray> ToBytecodeArray(IsolateT* isolate);
template <typename IsolateT>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
DirectHandle<TrustedByteArray> ToSourcePositionTable(IsolateT* isolate);
#ifdef DEBUG
int CheckBytecodeMatches(Tagged<BytecodeArray> bytecode);
#endif
// Get the number of parameters expected by function.
uint16_t parameter_count() const { return parameter_count_; }
uint16_t max_arguments() const { return max_arguments_; }
void UpdateMaxArguments(uint16_t max_arguments) {
max_arguments_ = std::max(max_arguments_, max_arguments);
}
// Get the number of locals required for bytecode array.
int locals_count() const {
DCHECK_GE(local_register_count_, 0);
return local_register_count_;
}
// Returns the number of fixed (non-temporary) registers.
int fixed_register_count() const { return locals_count(); }
// Returns the number of fixed and temporary registers.
int total_register_count() const {
DCHECK_LE(fixed_register_count(),
register_allocator()->maximum_register_count());
return register_allocator()->maximum_register_count();
}
Register Local(int index) const;
Register Parameter(int parameter_index) const;
Register Receiver() const;
// Constant loads to accumulator.
BytecodeArrayBuilder& LoadConstantPoolEntry(size_t entry);
BytecodeArrayBuilder& LoadLiteral(Tagged<Smi> value);
BytecodeArrayBuilder& LoadLiteral(double value);
BytecodeArrayBuilder& LoadLiteral(const AstRawString* raw_string);
BytecodeArrayBuilder& LoadLiteral(const AstConsString* cons_string);
BytecodeArrayBuilder& LoadLiteral(const Scope* scope);
BytecodeArrayBuilder& LoadLiteral(AstBigInt bigint);
BytecodeArrayBuilder& LoadUndefined();
BytecodeArrayBuilder& LoadNull();
BytecodeArrayBuilder& LoadTheHole();
BytecodeArrayBuilder& LoadTrue();
BytecodeArrayBuilder& LoadFalse();
BytecodeArrayBuilder& LoadBoolean(bool value);
// Global loads to the accumulator and stores from the accumulator.
BytecodeArrayBuilder& LoadGlobal(const AstRawString* name, int feedback_slot,
TypeofMode typeof_mode);
BytecodeArrayBuilder& StoreGlobal(const AstRawString* name,
int feedback_slot);
// Load the object at |variable| at |depth| in the context chain starting
// with |context| into the accumulator.
enum ContextSlotMutability { kImmutableSlot, kMutableSlot };
BytecodeArrayBuilder& LoadContextSlot(Register context, Variable* variable,
int depth,
ContextSlotMutability immutable);
// Stores the object in the accumulator into |variable| at |depth| in the
// context chain starting with |context|.
BytecodeArrayBuilder& StoreContextSlot(Register context, Variable* variable,
int depth);
// Load from a module variable into the accumulator. |depth| is the depth of
// the current context relative to the module context.
BytecodeArrayBuilder& LoadModuleVariable(int cell_index, int depth);
// Store from the accumulator into a module variable. |depth| is the depth of
// the current context relative to the module context.
BytecodeArrayBuilder& StoreModuleVariable(int cell_index, int depth);
// Register-accumulator transfers.
BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg);
BytecodeArrayBuilder& StoreAccumulatorInRegister(Register reg);
// Register-register transfer.
BytecodeArrayBuilder& MoveRegister(Register from, Register to);
// Named load property.
BytecodeArrayBuilder& LoadNamedProperty(Register object,
const AstRawString* name,
int feedback_slot);
BytecodeArrayBuilder& LoadNamedPropertyFromSuper(Register object,
const AstRawString* name,
int feedback_slot);
// Keyed load property. The key should be in the accumulator.
BytecodeArrayBuilder& LoadKeyedProperty(Register object, int feedback_slot);
BytecodeArrayBuilder& LoadEnumeratedKeyedProperty(Register object,
Register enum_index,
Register cache_type,
int feedback_slot);
// Named load property of the @@iterator symbol.
BytecodeArrayBuilder& LoadIteratorProperty(Register object,
int feedback_slot);
// Load and call property of the @@iterator symbol
BytecodeArrayBuilder& GetIterator(Register object, int load_feedback_slot,
int call_feedback_slot);
// Named load property of the @@asyncIterator symbol.
BytecodeArrayBuilder& LoadAsyncIteratorProperty(Register object,
int feedback_slot);
// Store properties. Flag for NeedsSetFunctionName() should
// be in the accumulator.
BytecodeArrayBuilder& DefineKeyedOwnPropertyInLiteral(
Register object, Register name,
DefineKeyedOwnPropertyInLiteralFlags flags, int feedback_slot);
// Set a property named by a property name, trigger the setters and
// set traps if necessary. The value to be set should be in the
// accumulator.
BytecodeArrayBuilder& SetNamedProperty(Register object,
const AstRawString* name,
int feedback_slot,
LanguageMode language_mode);
// Set a property named by a constant from the constant pool,
// trigger the setters and set traps if necessary. The value to be
// set should be in the accumulator.
BytecodeArrayBuilder& SetNamedProperty(Register object,
size_t constant_pool_entry,
int feedback_slot,
LanguageMode language_mode);
// Define an own property named by a constant from the constant pool,
// trigger the defineProperty traps if necessary. The value to be
// defined should be in the accumulator.
BytecodeArrayBuilder& DefineNamedOwnProperty(Register object,
const AstRawString* name,
int feedback_slot);
// Set a property keyed by a value in a register, trigger the setters and
// set traps if necessary. The value to be set should be in the
// accumulator.
BytecodeArrayBuilder& SetKeyedProperty(Register object, Register key,
int feedback_slot,
LanguageMode language_mode);
// Define an own property keyed by a value in a register, trigger the
// defineProperty traps if necessary. The value to be defined should be
// in the accumulator.
BytecodeArrayBuilder& DefineKeyedOwnProperty(
Register object, Register key, DefineKeyedOwnPropertyFlags flags,
int feedback_slot);
// Store an own element in an array literal. The value to be stored should be
// in the accumulator.
BytecodeArrayBuilder& StoreInArrayLiteral(Register array, Register index,
int feedback_slot);
// Store the class fields property. The initializer to be stored should
// be in the accumulator.
BytecodeArrayBuilder& StoreClassFieldsInitializer(Register constructor,
int feedback_slot);
// Load class fields property.
BytecodeArrayBuilder& LoadClassFieldsInitializer(Register constructor,
int feedback_slot);
// Lookup the variable with |name|.
BytecodeArrayBuilder& LoadLookupSlot(const AstRawString* name,
TypeofMode typeof_mode);
// Lookup the variable with |name|, which is known to be at |slot_index| at
// |depth| in the context chain if not shadowed by a context extension
// somewhere in that context chain.
BytecodeArrayBuilder& LoadLookupContextSlot(const AstRawString* name,
TypeofMode typeof_mode,
ContextKind context_kind,
int slot_index, int depth);
// Lookup the variable with |name|, which has its feedback in |feedback_slot|
// and is known to be global if not shadowed by a context extension somewhere
// up to |depth| in that context chain.
BytecodeArrayBuilder& LoadLookupGlobalSlot(const AstRawString* name,
TypeofMode typeof_mode,
int feedback_slot, int depth);
// Store value in the accumulator into the variable with |name|.
BytecodeArrayBuilder& StoreLookupSlot(
const AstRawString* name, LanguageMode language_mode,
LookupHoistingMode lookup_hoisting_mode);
// Create a new closure for a SharedFunctionInfo which will be inserted at
// constant pool index |shared_function_info_entry|.
BytecodeArrayBuilder& CreateClosure(size_t shared_function_info_entry,
int slot, int flags);
// Create a new local context for a |scope|.
BytecodeArrayBuilder& CreateBlockContext(const Scope* scope);
// Create a new context for a catch block with |exception| and |scope|.
BytecodeArrayBuilder& CreateCatchContext(Register exception,
const Scope* scope);
// Create a new context with the given |scope| and size |slots|.
BytecodeArrayBuilder& CreateFunctionContext(const Scope* scope, int slots);
// Create a new eval context with the given |scope| and size |slots|.
BytecodeArrayBuilder& CreateEvalContext(const Scope* scope, int slots);
// Creates a new context with the given |scope| for a with-statement
// with the |object| in a register.
BytecodeArrayBuilder& CreateWithContext(Register object, const Scope* scope);
// Create a new arguments object in the accumulator.
BytecodeArrayBuilder& CreateArguments(CreateArgumentsType type);
// Literals creation. Constant elements should be in the accumulator.
BytecodeArrayBuilder& CreateRegExpLiteral(const AstRawString* pattern,
int literal_index, int flags);
BytecodeArrayBuilder& CreateArrayLiteral(size_t constant_elements_entry,
int literal_index, int flags);
BytecodeArrayBuilder& CreateEmptyArrayLiteral(int literal_index);
BytecodeArrayBuilder& CreateArrayFromIterable();
BytecodeArrayBuilder& CreateObjectLiteral(size_t constant_properties_entry,
int literal_index, int flags);
BytecodeArrayBuilder& CreateEmptyObjectLiteral();
BytecodeArrayBuilder& CloneObject(Register source, int flags,
int feedback_slot);
// Gets or creates the template for a TemplateObjectDescription which will
// be inserted at constant pool index |template_object_description_entry|.
BytecodeArrayBuilder& GetTemplateObject(
size_t template_object_description_entry, int feedback_slot);
// Push the context in accumulator as the new context, and store in register
// |context|.
BytecodeArrayBuilder& PushContext(Register context);
// Pop the current context and replace with |context|.
BytecodeArrayBuilder& PopContext(Register context);
// Call a JS function which is known to be a property of a JS object. The
// JSFunction or Callable to be called should be in |callable|. The arguments
// should be in |args|, with the receiver in |args[0]|. Type feedback is
// recorded in the |feedback_slot| in the type feedback vector.
BytecodeArrayBuilder& CallProperty(Register callable, RegisterList args,
int feedback_slot);
// Call a JS function with an known undefined receiver. The JSFunction or
// Callable to be called should be in |callable|. The arguments should be in
// |args|, with no receiver as it is implicitly set to undefined. Type
// feedback is recorded in the |feedback_slot| in the type feedback vector.
BytecodeArrayBuilder& CallUndefinedReceiver(Register callable,
RegisterList args,
int feedback_slot);
// Call a JS function with an any receiver, possibly (but not necessarily)
// undefined. The JSFunction or Callable to be called should be in |callable|.
// The arguments should be in |args|, with the receiver in |args[0]|. Type
// feedback is recorded in the |feedback_slot| in the type feedback vector.
BytecodeArrayBuilder& CallAnyReceiver(Register callable, RegisterList args,
int feedback_slot);
// Tail call into a JS function. The JSFunction or Callable to be called
// should be in |callable|. The arguments should be in |args|, with the
// receiver in |args[0]|. Type feedback is recorded in the |feedback_slot| in
// the type feedback vector.
BytecodeArrayBuilder& TailCall(Register callable, RegisterList args,
int feedback_slot);
// Call a JS function. The JSFunction or Callable to be called should be in
// |callable|, the receiver in |args[0]| and the arguments in |args[1]|
// onwards. The final argument must be a spread.
BytecodeArrayBuilder& CallWithSpread(Register callable, RegisterList args,
int feedback_slot);
// Call the Construct operator. The accumulator holds the |new_target|.
// The |constructor| is in a register and arguments are in |args|.
BytecodeArrayBuilder& Construct(Register constructor, RegisterList args,
int feedback_slot);
// Call the Construct operator for use with a spread. The accumulator holds
// the |new_target|. The |constructor| is in a register and arguments are in
// |args|. The final argument must be a spread.
BytecodeArrayBuilder& ConstructWithSpread(Register constructor,
RegisterList args,
int feedback_slot);
// Call the Construct operator, forwarding all arguments passed to the current
// interpreted frame, including the receiver. The accumulator holds the
// |new_target|. The |constructor| is in a register.
BytecodeArrayBuilder& ConstructForwardAllArgs(Register constructor,
int feedback_slot);
// Call the runtime function with |function_id| and arguments |args|.
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
RegisterList args);
// Call the runtime function with |function_id| with single argument |arg|.
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
Register arg);
// Call the runtime function with |function_id| with no arguments.
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id);
// Call the runtime function with |function_id| and arguments |args|, that
// returns a pair of values. The return values will be returned in
// |return_pair|.
BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id,
RegisterList args,
RegisterList return_pair);
// Call the runtime function with |function_id| with single argument |arg|
// that returns a pair of values. The return values will be returned in
// |return_pair|.
BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id,
Register arg,
RegisterList return_pair);
// Call the JS runtime function with |context_index| and arguments |args|,
// with no receiver as it is implicitly set to undefined.
BytecodeArrayBuilder& CallJSRuntime(int context_index, RegisterList args);
// Operators (register holds the lhs value, accumulator holds the rhs value).
// Type feedback will be recorded in the |feedback_slot|
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg,
int feedback_slot);
// Same as above, but lhs in the accumulator and rhs in |literal|.
BytecodeArrayBuilder& BinaryOperationSmiLiteral(Token::Value binop,
Tagged<Smi> literal,
int feedback_slot);
// Unary and Count Operators (value stored in accumulator).
// Type feedback will be recorded in the |feedback_slot|
BytecodeArrayBuilder& UnaryOperation(Token::Value op, int feedback_slot);
enum class ToBooleanMode {
kConvertToBoolean, // Perform ToBoolean conversion on accumulator.
kAlreadyBoolean, // Accumulator is already a Boolean.
};
// Unary Operators.
BytecodeArrayBuilder& LogicalNot(ToBooleanMode mode);
BytecodeArrayBuilder& TypeOf(int feedback_slot);
// Expects a heap object in the accumulator. Returns its super constructor in
// the register |out| if it passes the IsConstructor test. Otherwise, it
// throws a TypeError exception.
BytecodeArrayBuilder& GetSuperConstructor(Register out);
BytecodeArrayBuilder& FindNonDefaultConstructorOrConstruct(
Register this_function, Register new_target, RegisterList output);
// Deletes property from an object. This expects that accumulator contains
// the key to be deleted and the register contains a reference to the object.
BytecodeArrayBuilder& Delete(Register object, LanguageMode language_mode);
// JavaScript defines two kinds of 'nil'.
enum NilValue { kNullValue, kUndefinedValue };
// Tests.
BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg,
int feedback_slot);
BytecodeArrayBuilder& CompareReference(Register reg);
BytecodeArrayBuilder& CompareUndetectable();
BytecodeArrayBuilder& CompareUndefined();
BytecodeArrayBuilder& CompareNull();
BytecodeArrayBuilder& CompareNil(Token::Value op, NilValue nil);
BytecodeArrayBuilder& CompareTypeOf(
TestTypeOfFlags::LiteralFlag literal_flag);
// Converts accumulator and stores result in register |out|.
BytecodeArrayBuilder& ToObject(Register out);
// Converts accumulator and stores result back in accumulator.
BytecodeArrayBuilder& ToName();
BytecodeArrayBuilder& ToString();
BytecodeArrayBuilder& ToBoolean(ToBooleanMode mode);
BytecodeArrayBuilder& ToNumber(int feedback_slot);
BytecodeArrayBuilder& ToNumeric(int feedback_slot);
// Exception handling.
BytecodeArrayBuilder& MarkHandler(int handler_id,
HandlerTable::CatchPrediction will_catch);
BytecodeArrayBuilder& MarkTryBegin(int handler_id, Register context);
BytecodeArrayBuilder& MarkTryEnd(int handler_id);
// Flow Control.
BytecodeArrayBuilder& Bind(BytecodeLabel* label);
BytecodeArrayBuilder& Bind(BytecodeLoopHeader* label);
BytecodeArrayBuilder& Bind(BytecodeJumpTable* jump_table, int case_value);
BytecodeArrayBuilder& Jump(BytecodeLabel* label);
BytecodeArrayBuilder& JumpLoop(BytecodeLoopHeader* loop_header,
int loop_depth, int position,
int feedback_slot);
BytecodeArrayBuilder& JumpIfTrue(ToBooleanMode mode, BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfFalse(ToBooleanMode mode, BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfJSReceiver(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfNull(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfNotNull(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfUndefined(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfUndefinedOrNull(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfNotUndefined(BytecodeLabel* label);
BytecodeArrayBuilder& JumpIfNil(BytecodeLabel* label, Token::Value op,
NilValue nil);
BytecodeArrayBuilder& JumpIfNotNil(BytecodeLabel* label, Token::Value op,
NilValue nil);
BytecodeArrayBuilder& JumpIfForInDone(BytecodeLabel* label, Register index,
Register cache_length);
BytecodeArrayBuilder& SwitchOnSmiNoFeedback(BytecodeJumpTable* jump_table);
// Sets the pending message to the value in the accumulator, and returns the
// previous pending message in the accumulator.
BytecodeArrayBuilder& SetPendingMessage();
BytecodeArrayBuilder& Throw();
BytecodeArrayBuilder& ReThrow();
BytecodeArrayBuilder& Abort(AbortReason reason);
BytecodeArrayBuilder& Return();
BytecodeArrayBuilder& ThrowReferenceErrorIfHole(const AstRawString* name);
BytecodeArrayBuilder& ThrowSuperNotCalledIfHole();
BytecodeArrayBuilder& ThrowSuperAlreadyCalledIfNotHole();
BytecodeArrayBuilder& ThrowIfNotSuperConstructor(Register constructor);
// Debugger.
BytecodeArrayBuilder& Debugger();
// Increment the block counter at the given slot (block code coverage).
BytecodeArrayBuilder& IncBlockCounter(int slot);
// Complex flow control.
BytecodeArrayBuilder& ForInEnumerate(Register receiver);
BytecodeArrayBuilder& ForInPrepare(RegisterList cache_info_triple,
int feedback_slot);
BytecodeArrayBuilder& ForInNext(Register receiver, Register index,
RegisterList cache_type_array_pair,
int feedback_slot);
BytecodeArrayBuilder& ForInStep(Register index);
// Generators.
BytecodeArrayBuilder& SuspendGenerator(Register generator,
RegisterList registers,
int suspend_id);
BytecodeArrayBuilder& SwitchOnGeneratorState(Register generator,
BytecodeJumpTable* jump_table);
BytecodeArrayBuilder& ResumeGenerator(Register generator,
RegisterList registers);
// Creates a new handler table entry and returns a {hander_id} identifying the
// entry, so that it can be referenced by above exception handling support.
int NewHandlerEntry() { return handler_table_builder()->NewHandlerEntry(); }
// Allocates a new jump table of given |size| and |case_value_base| in the
// constant pool.
BytecodeJumpTable* AllocateJumpTable(int size, int case_value_base);
BytecodeRegisterOptimizer* GetRegisterOptimizer() {
return register_optimizer_;
}
// Gets a constant pool entry.
size_t GetConstantPoolEntry(const AstRawString* raw_string);
size_t GetConstantPoolEntry(const AstConsString* cons_string);
size_t GetConstantPoolEntry(AstBigInt bigint);
size_t GetConstantPoolEntry(const Scope* scope);
size_t GetConstantPoolEntry(double number);
#define ENTRY_GETTER(NAME, ...) size_t NAME##ConstantPoolEntry();
SINGLETON_CONSTANT_ENTRY_TYPES(ENTRY_GETTER)
#undef ENTRY_GETTER
// Allocates a slot in the constant pool which can later be set.
size_t AllocateDeferredConstantPoolEntry();
// Sets the deferred value into an allocated constant pool entry.
void SetDeferredConstantPoolEntry(size_t entry, Handle<Object> object);
void InitializeReturnPosition(FunctionLiteral* literal);
void SetStatementPosition(Statement* stmt) {
SetStatementPosition(stmt->position());
}
std::optional<BytecodeSourceInfo> MaybePopSourcePosition(int scope_start) {
if (!latest_source_info_.is_valid() ||
latest_source_info_.source_position() < scope_start) {
return std::nullopt;
}
BytecodeSourceInfo source_info = latest_source_info_;
latest_source_info_.set_invalid();
return source_info;
}
void PushSourcePosition(BytecodeSourceInfo source_info) {
DCHECK(!latest_source_info_.is_valid());
latest_source_info_ = source_info;
}
void SetStatementPosition(int position) {
if (position == kNoSourcePosition) return;
latest_source_info_.MakeStatementPosition(position);
}
void SetExpressionPosition(Expression* expr) {
SetExpressionPosition(expr->position());
}
void SetExpressionPosition(int position) {
if (position == kNoSourcePosition) return;
if (!latest_source_info_.is_statement()) {
// Ensure the current expression position is overwritten with the
// latest value.
latest_source_info_.MakeExpressionPosition(position);
}
}
void SetExpressionAsStatementPosition(Expression* expr) {
SetStatementPosition(expr->position());
}
bool RemainderOfBlockIsDead() const {
return bytecode_array_writer_.RemainderOfBlockIsDead();
}
// Returns the raw operand value for the given register or register list.
uint32_t GetInputRegisterOperand(Register reg);
uint32_t GetOutputRegisterOperand(Register reg);
uint32_t GetInputOutputRegisterOperand(Register reg);
uint32_t GetInputRegisterListOperand(RegisterList reg_list);
uint32_t GetOutputRegisterListOperand(RegisterList reg_list);
// Outputs raw register transfer bytecodes without going through the register
// optimizer.
void OutputLdarRaw(Register reg);
void OutputStarRaw(Register reg);
void OutputMovRaw(Register src, Register dest);
void EmitFunctionStartSourcePosition(int position);
// Accessors
BytecodeRegisterAllocator* register_allocator() {
return &register_allocator_;
}
const BytecodeRegisterAllocator* register_allocator() const {
return &register_allocator_;
}
Zone* zone() const { return zone_; }
private:
friend class BytecodeRegisterAllocator;
template <Bytecode bytecode, ImplicitRegisterUse implicit_register_use,
OperandType... operand_types>
friend class BytecodeNodeBuilder;
const FeedbackVectorSpec* feedback_vector_spec() const {
return feedback_vector_spec_;
}
// Returns the current source position for the given |bytecode|.
V8_INLINE BytecodeSourceInfo CurrentSourcePosition(Bytecode bytecode);
#define DECLARE_BYTECODE_OUTPUT(Name, ...) \
template <typename... Operands> \
V8_INLINE BytecodeNode Create##Name##Node(Operands... operands); \
template <typename... Operands> \
V8_INLINE void Output##Name(Operands... operands); \
template <typename... Operands> \
V8_INLINE void Output##Name(BytecodeLabel* label, Operands... operands);
BYTECODE_LIST(DECLARE_BYTECODE_OUTPUT, DECLARE_BYTECODE_OUTPUT)
#undef DECLARE_OPERAND_TYPE_INFO
V8_INLINE void OutputJumpLoop(BytecodeLoopHeader* loop_header, int loop_depth,
int feedback_slot);
V8_INLINE void OutputSwitchOnSmiNoFeedback(BytecodeJumpTable* jump_table);
bool RegisterIsValid(Register reg) const;
bool RegisterListIsValid(RegisterList reg_list) const;
// Sets a deferred source info which should be emitted before any future
// source info (either attached to a following bytecode or as a nop).
void SetDeferredSourceInfo(BytecodeSourceInfo source_info);
// Either attach deferred source info to node, or emit it as a nop bytecode
// if node already have valid source info.
void AttachOrEmitDeferredSourceInfo(BytecodeNode* node);
// Write bytecode to bytecode array.
void Write(BytecodeNode* node);
void WriteJump(BytecodeNode* node, BytecodeLabel* label);
void WriteJumpLoop(BytecodeNode* node, BytecodeLoopHeader* loop_header);
void WriteSwitch(BytecodeNode* node, BytecodeJumpTable* label);
// Not implemented as the illegal bytecode is used inside internally
// to indicate a bytecode field is not valid or an error has occurred
// during bytecode generation.
BytecodeArrayBuilder& Illegal();
template <Bytecode bytecode, ImplicitRegisterUse implicit_register_use>
void PrepareToOutputBytecode();
BytecodeArrayWriter* bytecode_array_writer() {
return &bytecode_array_writer_;
}
ConstantArrayBuilder* constant_array_builder() {
return &constant_array_builder_;
}
const ConstantArrayBuilder* constant_array_builder() const {
return &constant_array_builder_;
}
HandlerTableBuilder* handler_table_builder() {
return &handler_table_builder_;
}
Zone* zone_;
FeedbackVectorSpec* feedback_vector_spec_;
bool bytecode_generated_;
ConstantArrayBuilder constant_array_builder_;
HandlerTableBuilder handler_table_builder_;
uint16_t parameter_count_;
uint16_t max_arguments_;
int local_register_count_;
BytecodeRegisterAllocator register_allocator_;
BytecodeArrayWriter bytecode_array_writer_;
BytecodeRegisterOptimizer* register_optimizer_;
BytecodeSourceInfo latest_source_info_;
BytecodeSourceInfo deferred_source_info_;
};
V8_EXPORT_PRIVATE std::ostream& operator<<(
std::ostream& os, const BytecodeArrayBuilder::ToBooleanMode& mode);
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_

View File

@ -0,0 +1,442 @@
// Copyright 2015 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.
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-decoder.h"
#include "src/interpreter/interpreter-intrinsics.h"
#include "src/objects/feedback-vector.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
namespace interpreter {
BytecodeArrayIterator::BytecodeArrayIterator(
Handle<BytecodeArray> bytecode_array, int initial_offset)
: bytecode_array_(bytecode_array),
start_(reinterpret_cast<uint8_t*>(
bytecode_array_->GetFirstBytecodeAddress())),
end_(start_ + bytecode_array_->length()),
cursor_(start_),
operand_scale_(OperandScale::kSingle),
prefix_size_(0),
local_heap_(LocalHeap::Current()
? LocalHeap::Current()
: Isolate::Current()->main_thread_local_heap()) {
local_heap_->AddGCEpilogueCallback(UpdatePointersCallback, this);
UpdateOperandScale();
if (initial_offset != 0) {
AdvanceTo(initial_offset);
}
}
BytecodeArrayIterator::BytecodeArrayIterator(
Handle<BytecodeArray> bytecode_array, int initial_offset,
DisallowGarbageCollection& no_gc)
: bytecode_array_(bytecode_array),
start_(reinterpret_cast<uint8_t*>(
bytecode_array_->GetFirstBytecodeAddress())),
end_(start_ + bytecode_array_->length()),
cursor_(start_),
operand_scale_(OperandScale::kSingle),
prefix_size_(0),
local_heap_(nullptr) {
// Don't add a GC callback, since we're in a no_gc scope.
UpdateOperandScale();
if (initial_offset != 0) {
AdvanceTo(initial_offset);
}
}
BytecodeArrayIterator::~BytecodeArrayIterator() {
if (local_heap_) {
local_heap_->RemoveGCEpilogueCallback(UpdatePointersCallback, this);
}
}
void BytecodeArrayIterator::AdvanceTo(int offset) {
DCHECK_GE(offset, current_offset());
while (current_offset() != offset && cursor_ < end_) {
Advance();
}
// Make sure we're always at a valid offset.
CHECK_EQ(current_offset(), offset);
}
void BytecodeArrayIterator::SetOffset(int offset) {
DCHECK_GE(offset, 0);
if (offset < current_offset()) {
Reset();
}
// Advance to the given offset instead of just setting cursor_.
// This way, we can guarantee that the offset is always valid.
AdvanceTo(offset);
}
void BytecodeArrayIterator::Reset() {
cursor_ = start_;
UpdateOperandScale();
}
// protected
void BytecodeArrayIterator::SetOffsetUnchecked(int offset) {
DCHECK_GE(offset, 0);
cursor_ = start_ + offset;
UpdateOperandScale();
}
// static
bool BytecodeArrayIterator::IsValidOffset(Handle<BytecodeArray> bytecode_array,
int offset) {
for (BytecodeArrayIterator it(bytecode_array); !it.done(); it.Advance()) {
if (it.current_offset() == offset) return true;
if (it.current_offset() > offset) break;
}
return false;
}
// static
bool BytecodeArrayIterator::IsValidOSREntryOffset(
Handle<BytecodeArray> bytecode_array, int offset) {
BytecodeArrayIterator it(bytecode_array, offset);
return it.CurrentBytecodeIsValidOSREntry();
}
bool BytecodeArrayIterator::CurrentBytecodeIsValidOSREntry() const {
return current_bytecode() == interpreter::Bytecode::kJumpLoop;
}
void BytecodeArrayIterator::ApplyDebugBreak() {
// Get the raw bytecode from the bytecode array. This may give us a
// scaling prefix, which we can patch with the matching debug-break
// variant.
uint8_t* cursor = cursor_ - prefix_size_;
interpreter::Bytecode bytecode = interpreter::Bytecodes::FromByte(*cursor);
if (interpreter::Bytecodes::IsDebugBreak(bytecode)) return;
interpreter::Bytecode debugbreak =
interpreter::Bytecodes::GetDebugBreak(bytecode);
*cursor = interpreter::Bytecodes::ToByte(debugbreak);
}
uint32_t BytecodeArrayIterator::GetUnsignedOperand(
int operand_index, OperandType operand_type) const {
DCHECK_GE(operand_index, 0);
DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
DCHECK_EQ(operand_type,
Bytecodes::GetOperandType(current_bytecode(), operand_index));
DCHECK(Bytecodes::IsUnsignedOperandType(operand_type));
Address operand_start =
reinterpret_cast<Address>(cursor_) +
Bytecodes::GetOperandOffset(current_bytecode(), operand_index,
current_operand_scale());
return BytecodeDecoder::DecodeUnsignedOperand(operand_start, operand_type,
current_operand_scale());
}
int32_t BytecodeArrayIterator::GetSignedOperand(
int operand_index, OperandType operand_type) const {
DCHECK_GE(operand_index, 0);
DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
DCHECK_EQ(operand_type,
Bytecodes::GetOperandType(current_bytecode(), operand_index));
DCHECK(!Bytecodes::IsUnsignedOperandType(operand_type));
Address operand_start =
reinterpret_cast<Address>(cursor_) +
Bytecodes::GetOperandOffset(current_bytecode(), operand_index,
current_operand_scale());
return BytecodeDecoder::DecodeSignedOperand(operand_start, operand_type,
current_operand_scale());
}
uint32_t BytecodeArrayIterator::GetFlag8Operand(int operand_index) const {
DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
OperandType::kFlag8);
return GetUnsignedOperand(operand_index, OperandType::kFlag8);
}
uint32_t BytecodeArrayIterator::GetFlag16Operand(int operand_index) const {
DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
OperandType::kFlag16);
return GetUnsignedOperand(operand_index, OperandType::kFlag16);
}
uint32_t BytecodeArrayIterator::GetUnsignedImmediateOperand(
int operand_index) const {
DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
OperandType::kUImm);
return GetUnsignedOperand(operand_index, OperandType::kUImm);
}
int32_t BytecodeArrayIterator::GetImmediateOperand(int operand_index) const {
DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
OperandType::kImm);
return GetSignedOperand(operand_index, OperandType::kImm);
}
uint32_t BytecodeArrayIterator::GetRegisterCountOperand(
int operand_index) const {
DCHECK_EQ(Bytecodes::GetOperandType(current_bytecode(), operand_index),
OperandType::kRegCount);
return GetUnsignedOperand(operand_index, OperandType::kRegCount);
}
uint32_t BytecodeArrayIterator::GetIndexOperand(int operand_index) const {
OperandType operand_type =
Bytecodes::GetOperandType(current_bytecode(), operand_index);
DCHECK_EQ(operand_type, OperandType::kIdx);
return GetUnsignedOperand(operand_index, operand_type);
}
FeedbackSlot BytecodeArrayIterator::GetSlotOperand(int operand_index) const {
int index = GetIndexOperand(operand_index);
return FeedbackVector::ToSlot(index);
}
Register BytecodeArrayIterator::GetParameter(int parameter_index) const {
DCHECK_GE(parameter_index, 0);
// The parameter indices are shifted by 1 (receiver is the
// first entry).
return Register::FromParameterIndex(parameter_index + 1);
}
Register BytecodeArrayIterator::GetRegisterOperand(int operand_index) const {
OperandType operand_type =
Bytecodes::GetOperandType(current_bytecode(), operand_index);
Address operand_start =
reinterpret_cast<Address>(cursor_) +
Bytecodes::GetOperandOffset(current_bytecode(), operand_index,
current_operand_scale());
return BytecodeDecoder::DecodeRegisterOperand(operand_start, operand_type,
current_operand_scale());
}
Register BytecodeArrayIterator::GetStarTargetRegister() const {
Bytecode bytecode = current_bytecode();
DCHECK(Bytecodes::IsAnyStar(bytecode));
if (Bytecodes::IsShortStar(bytecode)) {
return Register::FromShortStar(bytecode);
} else {
DCHECK_EQ(bytecode, Bytecode::kStar);
DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 1);
DCHECK_EQ(Bytecodes::GetOperandTypes(bytecode)[0], OperandType::kRegOut);
return GetRegisterOperand(0);
}
}
std::pair<Register, Register> BytecodeArrayIterator::GetRegisterPairOperand(
int operand_index) const {
Register first = GetRegisterOperand(operand_index);
Register second(first.index() + 1);
return std::make_pair(first, second);
}
RegisterList BytecodeArrayIterator::GetRegisterListOperand(
int operand_index) const {
Register first = GetRegisterOperand(operand_index);
uint32_t count = GetRegisterCountOperand(operand_index + 1);
return RegisterList(first.index(), count);
}
int BytecodeArrayIterator::GetRegisterOperandRange(int operand_index) const {
DCHECK_LE(operand_index, Bytecodes::NumberOfOperands(current_bytecode()));
const OperandType* operand_types =
Bytecodes::GetOperandTypes(current_bytecode());
OperandType operand_type = operand_types[operand_index];
DCHECK(Bytecodes::IsRegisterOperandType(operand_type));
if (operand_type == OperandType::kRegList ||
operand_type == OperandType::kRegOutList) {
return GetRegisterCountOperand(operand_index + 1);
} else {
return Bytecodes::GetNumberOfRegistersRepresentedBy(operand_type);
}
}
Runtime::FunctionId BytecodeArrayIterator::GetRuntimeIdOperand(
int operand_index) const {
OperandType operand_type =
Bytecodes::GetOperandType(current_bytecode(), operand_index);
DCHECK_EQ(operand_type, OperandType::kRuntimeId);
uint32_t raw_id = GetUnsignedOperand(operand_index, operand_type);
return static_cast<Runtime::FunctionId>(raw_id);
}
uint32_t BytecodeArrayIterator::GetNativeContextIndexOperand(
int operand_index) const {
OperandType operand_type =
Bytecodes::GetOperandType(current_bytecode(), operand_index);
DCHECK_EQ(operand_type, OperandType::kNativeContextIndex);
return GetUnsignedOperand(operand_index, operand_type);
}
Runtime::FunctionId BytecodeArrayIterator::GetIntrinsicIdOperand(
int operand_index) const {
OperandType operand_type =
Bytecodes::GetOperandType(current_bytecode(), operand_index);
DCHECK_EQ(operand_type, OperandType::kIntrinsicId);
uint32_t raw_id = GetUnsignedOperand(operand_index, operand_type);
return IntrinsicsHelper::ToRuntimeId(
static_cast<IntrinsicsHelper::IntrinsicId>(raw_id));
}
template <typename IsolateT>
Handle<Object> BytecodeArrayIterator::GetConstantAtIndex(
int index, IsolateT* isolate) const {
return handle(bytecode_array()->constant_pool()->get(index), isolate);
}
bool BytecodeArrayIterator::IsConstantAtIndexSmi(int index) const {
return IsSmi(bytecode_array()->constant_pool()->get(index));
}
Tagged<Smi> BytecodeArrayIterator::GetConstantAtIndexAsSmi(int index) const {
return Cast<Smi>(bytecode_array()->constant_pool()->get(index));
}
template <typename IsolateT>
Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
int operand_index, IsolateT* isolate) const {
return GetConstantAtIndex(GetIndexOperand(operand_index), isolate);
}
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
int operand_index, Isolate* isolate) const;
template Handle<Object> BytecodeArrayIterator::GetConstantForIndexOperand(
int operand_index, LocalIsolate* isolate) const;
int BytecodeArrayIterator::GetRelativeJumpTargetOffset() const {
Bytecode bytecode = current_bytecode();
if (interpreter::Bytecodes::IsJumpImmediate(bytecode)) {
int relative_offset = GetUnsignedImmediateOperand(0);
if (bytecode == Bytecode::kJumpLoop) {
relative_offset = -relative_offset;
}
return relative_offset;
} else if (interpreter::Bytecodes::IsJumpConstant(bytecode)) {
Tagged<Smi> smi = GetConstantAtIndexAsSmi(GetIndexOperand(0));
return smi.value();
} else {
UNREACHABLE();
}
}
int BytecodeArrayIterator::GetJumpTargetOffset() const {
return GetAbsoluteOffset(GetRelativeJumpTargetOffset());
}
JumpTableTargetOffsets BytecodeArrayIterator::GetJumpTableTargetOffsets()
const {
uint32_t table_start, table_size;
int32_t case_value_base;
if (current_bytecode() == Bytecode::kSwitchOnGeneratorState) {
table_start = GetIndexOperand(1);
table_size = GetUnsignedImmediateOperand(2);
case_value_base = 0;
} else {
DCHECK_EQ(current_bytecode(), Bytecode::kSwitchOnSmiNoFeedback);
table_start = GetIndexOperand(0);
table_size = GetUnsignedImmediateOperand(1);
case_value_base = GetImmediateOperand(2);
}
return JumpTableTargetOffsets(this, table_start, table_size, case_value_base);
}
int BytecodeArrayIterator::GetAbsoluteOffset(int relative_offset) const {
return current_offset() + relative_offset + prefix_size_;
}
std::ostream& BytecodeArrayIterator::PrintTo(std::ostream& os) const {
return BytecodeDecoder::Decode(os, cursor_ - prefix_size_);
}
void BytecodeArrayIterator::UpdatePointers() {
DisallowGarbageCollection no_gc;
uint8_t* start =
reinterpret_cast<uint8_t*>(bytecode_array_->GetFirstBytecodeAddress());
if (start != start_) {
start_ = start;
uint8_t* end = start + bytecode_array_->length();
size_t distance_to_end = end_ - cursor_;
cursor_ = end - distance_to_end;
end_ = end;
}
}
JumpTableTargetOffsets::JumpTableTargetOffsets(
const BytecodeArrayIterator* iterator, int table_start, int table_size,
int case_value_base)
: iterator_(iterator),
table_start_(table_start),
table_size_(table_size),
case_value_base_(case_value_base) {}
JumpTableTargetOffsets::iterator JumpTableTargetOffsets::begin() const {
return iterator(case_value_base_, table_start_, table_start_ + table_size_,
iterator_);
}
JumpTableTargetOffsets::iterator JumpTableTargetOffsets::end() const {
return iterator(case_value_base_ + table_size_, table_start_ + table_size_,
table_start_ + table_size_, iterator_);
}
int JumpTableTargetOffsets::size() const {
int ret = 0;
// TODO(leszeks): Is there a more efficient way of doing this than iterating?
for (JumpTableTargetOffset entry : *this) {
USE(entry);
ret++;
}
return ret;
}
JumpTableTargetOffsets::iterator::iterator(
int case_value, int table_offset, int table_end,
const BytecodeArrayIterator* iterator)
: iterator_(iterator),
current_(Smi::zero()),
index_(case_value),
table_offset_(table_offset),
table_end_(table_end) {
UpdateAndAdvanceToValid();
}
JumpTableTargetOffset JumpTableTargetOffsets::iterator::operator*() {
DCHECK_LT(table_offset_, table_end_);
return {index_, iterator_->GetAbsoluteOffset(Smi::ToInt(current_))};
}
JumpTableTargetOffsets::iterator&
JumpTableTargetOffsets::iterator::operator++() {
DCHECK_LT(table_offset_, table_end_);
++table_offset_;
++index_;
UpdateAndAdvanceToValid();
return *this;
}
bool JumpTableTargetOffsets::iterator::operator!=(
const JumpTableTargetOffsets::iterator& other) {
DCHECK_EQ(iterator_, other.iterator_);
DCHECK_EQ(table_end_, other.table_end_);
DCHECK_EQ(index_ - other.index_, table_offset_ - other.table_offset_);
return index_ != other.index_;
}
void JumpTableTargetOffsets::iterator::UpdateAndAdvanceToValid() {
while (table_offset_ < table_end_ &&
!iterator_->IsConstantAtIndexSmi(table_offset_)) {
++table_offset_;
++index_;
}
// Make sure we haven't reached the end of the table with a hole in current.
if (table_offset_ < table_end_) {
DCHECK(iterator_->IsConstantAtIndexSmi(table_offset_));
current_ = iterator_->GetConstantAtIndexAsSmi(table_offset_);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,226 @@
// Copyright 2016 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_INTERPRETER_BYTECODE_ARRAY_ITERATOR_H_
#define V8_INTERPRETER_BYTECODE_ARRAY_ITERATOR_H_
#include <memory>
#include "include/v8-callbacks.h"
#include "src/common/globals.h"
#include "src/handles/handles.h"
#include "src/interpreter/bytecode-register.h"
#include "src/interpreter/bytecodes.h"
#include "src/objects/objects.h"
#include "src/objects/smi.h"
#include "src/runtime/runtime.h"
namespace v8 {
namespace internal {
class BytecodeArray;
namespace interpreter {
class BytecodeArrayIterator;
struct V8_EXPORT_PRIVATE JumpTableTargetOffset {
int case_value;
int target_offset;
};
class V8_EXPORT_PRIVATE JumpTableTargetOffsets final {
public:
// Minimal iterator implementation for use in ranged-for.
class V8_EXPORT_PRIVATE iterator final {
public:
iterator(int case_value, int table_offset, int table_end,
const BytecodeArrayIterator* iterator);
JumpTableTargetOffset operator*();
iterator& operator++();
bool operator!=(const iterator& other);
private:
void UpdateAndAdvanceToValid();
const BytecodeArrayIterator* iterator_;
Tagged<Smi> current_;
int index_;
int table_offset_;
int table_end_;
};
JumpTableTargetOffsets(const BytecodeArrayIterator* iterator, int table_start,
int table_size, int case_value_base);
iterator begin() const;
iterator end() const;
int size() const;
private:
const BytecodeArrayIterator* iterator_;
int table_start_;
int table_size_;
int case_value_base_;
};
class V8_EXPORT_PRIVATE BytecodeArrayIterator {
public:
explicit BytecodeArrayIterator(Handle<BytecodeArray> bytecode_array,
int initial_offset = 0);
BytecodeArrayIterator(Handle<BytecodeArray> bytecode_array,
int initial_offset, DisallowGarbageCollection& no_gc);
~BytecodeArrayIterator();
BytecodeArrayIterator(const BytecodeArrayIterator&) = delete;
BytecodeArrayIterator& operator=(const BytecodeArrayIterator&) = delete;
inline void Advance() {
cursor_ += current_bytecode_size_without_prefix();
UpdateOperandScale();
}
// Prefer AdvanceTo over SetOffset if the new offset is greater than the
// current offset as it is more efficient.
void AdvanceTo(int offset);
void SetOffset(int offset);
void Reset();
// Whether the given offset is reachable in this bytecode array.
static bool IsValidOffset(Handle<BytecodeArray> bytecode_array, int offset);
static bool IsValidOSREntryOffset(Handle<BytecodeArray> bytecode_array,
int offset);
bool CurrentBytecodeIsValidOSREntry() const;
void ApplyDebugBreak();
inline Bytecode current_bytecode() const {
DCHECK(!done());
uint8_t current_byte = *cursor_;
Bytecode current_bytecode = Bytecodes::FromByte(current_byte);
DCHECK(!Bytecodes::IsPrefixScalingBytecode(current_bytecode));
return current_bytecode;
}
int current_bytecode_size() const {
return prefix_size_ + current_bytecode_size_without_prefix();
}
int current_bytecode_size_without_prefix() const {
return Bytecodes::Size(current_bytecode(), current_operand_scale());
}
int current_offset() const {
return static_cast<int>(cursor_ - start_ - prefix_size_);
}
uint8_t* current_address() const { return cursor_ - prefix_size_; }
int next_offset() const { return current_offset() + current_bytecode_size(); }
Bytecode next_bytecode() const {
uint8_t* next_cursor = cursor_ + current_bytecode_size_without_prefix();
if (next_cursor == end_) return Bytecode::kIllegal;
Bytecode next_bytecode = Bytecodes::FromByte(*next_cursor);
if (Bytecodes::IsPrefixScalingBytecode(next_bytecode)) {
next_bytecode = Bytecodes::FromByte(*(next_cursor + 1));
}
return next_bytecode;
}
OperandScale current_operand_scale() const { return operand_scale_; }
DirectHandle<BytecodeArray> bytecode_array() const { return bytecode_array_; }
uint32_t GetFlag8Operand(int operand_index) const;
uint32_t GetFlag16Operand(int operand_index) const;
uint32_t GetUnsignedImmediateOperand(int operand_index) const;
int32_t GetImmediateOperand(int operand_index) const;
uint32_t GetIndexOperand(int operand_index) const;
FeedbackSlot GetSlotOperand(int operand_index) const;
Register GetParameter(int parameter_index) const;
uint32_t GetRegisterCountOperand(int operand_index) const;
Register GetRegisterOperand(int operand_index) const;
Register GetStarTargetRegister() const;
std::pair<Register, Register> GetRegisterPairOperand(int operand_index) const;
RegisterList GetRegisterListOperand(int operand_index) const;
int GetRegisterOperandRange(int operand_index) const;
Runtime::FunctionId GetRuntimeIdOperand(int operand_index) const;
Runtime::FunctionId GetIntrinsicIdOperand(int operand_index) const;
uint32_t GetNativeContextIndexOperand(int operand_index) const;
template <typename IsolateT>
Handle<Object> GetConstantAtIndex(int offset, IsolateT* isolate) const;
bool IsConstantAtIndexSmi(int offset) const;
Tagged<Smi> GetConstantAtIndexAsSmi(int offset) const;
template <typename IsolateT>
Handle<Object> GetConstantForIndexOperand(int operand_index,
IsolateT* isolate) const;
// Returns the relative offset of the branch target at the current bytecode.
// It is an error to call this method if the bytecode is not for a jump or
// conditional jump. Returns a negative offset for backward jumps.
int GetRelativeJumpTargetOffset() const;
// Returns the absolute offset of the branch target at the current bytecode.
// It is an error to call this method if the bytecode is not for a jump or
// conditional jump.
int GetJumpTargetOffset() const;
// Returns an iterator over the absolute offsets of the targets of the current
// switch bytecode's jump table. It is an error to call this method if the
// bytecode is not a switch.
JumpTableTargetOffsets GetJumpTableTargetOffsets() const;
// Returns the absolute offset of the bytecode at the given relative offset
// from the current bytecode.
int GetAbsoluteOffset(int relative_offset) const;
std::ostream& PrintTo(std::ostream& os) const;
static void UpdatePointersCallback(void* iterator) {
reinterpret_cast<BytecodeArrayIterator*>(iterator)->UpdatePointers();
}
void UpdatePointers();
inline bool done() const { return cursor_ >= end_; }
bool operator==(const BytecodeArrayIterator& other) const {
return cursor_ == other.cursor_;
}
bool operator!=(const BytecodeArrayIterator& other) const {
return cursor_ != other.cursor_;
}
protected:
void SetOffsetUnchecked(int offset);
private:
uint32_t GetUnsignedOperand(int operand_index,
OperandType operand_type) const;
int32_t GetSignedOperand(int operand_index, OperandType operand_type) const;
inline void UpdateOperandScale() {
if (done()) return;
uint8_t current_byte = *cursor_;
Bytecode current_bytecode = Bytecodes::FromByte(current_byte);
if (Bytecodes::IsPrefixScalingBytecode(current_bytecode)) {
operand_scale_ =
Bytecodes::PrefixBytecodeToOperandScale(current_bytecode);
++cursor_;
prefix_size_ = 1;
} else {
operand_scale_ = OperandScale::kSingle;
prefix_size_ = 0;
}
}
Handle<BytecodeArray> bytecode_array_;
uint8_t* start_;
uint8_t* end_;
// The cursor always points to the active bytecode. If there's a prefix, the
// prefix is at (cursor - 1).
uint8_t* cursor_;
OperandScale operand_scale_;
int prefix_size_;
LocalHeap* const local_heap_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_ARRAY_ITERATOR_H_

View File

@ -0,0 +1,43 @@
// Copyright 2015 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.
#include "src/interpreter/bytecode-array-random-iterator.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
namespace interpreter {
BytecodeArrayRandomIterator::BytecodeArrayRandomIterator(
Handle<BytecodeArray> bytecode_array, Zone* zone)
: BytecodeArrayIterator(bytecode_array, 0), offsets_(zone) {
offsets_.reserve(bytecode_array->length() / 2);
Initialize();
}
void BytecodeArrayRandomIterator::Initialize() {
// Run forwards through the bytecode array to determine the offset of each
// bytecode.
while (!done()) {
offsets_.push_back(current_offset());
Advance();
}
GoToStart();
}
bool BytecodeArrayRandomIterator::IsValid() const {
return current_index_ >= 0 &&
static_cast<size_t>(current_index_) < offsets_.size();
}
void BytecodeArrayRandomIterator::UpdateOffsetFromIndex() {
if (IsValid()) {
SetOffsetUnchecked(offsets_[current_index_]);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,81 @@
// Copyright 2016 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_INTERPRETER_BYTECODE_ARRAY_RANDOM_ITERATOR_H_
#define V8_INTERPRETER_BYTECODE_ARRAY_RANDOM_ITERATOR_H_
#include <memory>
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/zone/zone-containers.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace interpreter {
class V8_EXPORT_PRIVATE BytecodeArrayRandomIterator final
: public BytecodeArrayIterator {
public:
BytecodeArrayRandomIterator(Handle<BytecodeArray> bytecode_array, Zone* zone);
BytecodeArrayRandomIterator(const BytecodeArrayRandomIterator&) = delete;
BytecodeArrayRandomIterator& operator=(const BytecodeArrayRandomIterator&) =
delete;
BytecodeArrayRandomIterator& operator++() {
++current_index_;
UpdateOffsetFromIndex();
return *this;
}
BytecodeArrayRandomIterator& operator--() {
--current_index_;
UpdateOffsetFromIndex();
return *this;
}
BytecodeArrayRandomIterator& operator+=(int offset) {
current_index_ += offset;
UpdateOffsetFromIndex();
return *this;
}
BytecodeArrayRandomIterator& operator-=(int offset) {
current_index_ -= offset;
UpdateOffsetFromIndex();
return *this;
}
int current_index() const { return current_index_; }
int size() const { return static_cast<int>(offsets_.size()); }
void GoToIndex(int index) {
current_index_ = index;
UpdateOffsetFromIndex();
}
void GoToStart() {
current_index_ = 0;
UpdateOffsetFromIndex();
}
void GoToEnd() {
current_index_ = size() - 1;
UpdateOffsetFromIndex();
}
bool IsValid() const;
private:
ZoneVector<int> offsets_;
int current_index_;
void Initialize();
void UpdateOffsetFromIndex();
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_ARRAY_RANDOM_ITERATOR_H_

View File

@ -0,0 +1,550 @@
// Copyright 2015 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.
#include "src/interpreter/bytecode-array-writer.h"
#include "src/api/api-inl.h"
#include "src/heap/local-factory-inl.h"
#include "src/interpreter/bytecode-jump-table.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-node.h"
#include "src/interpreter/bytecode-source-info.h"
#include "src/interpreter/constant-array-builder.h"
#include "src/interpreter/handler-table-builder.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
namespace interpreter {
STATIC_CONST_MEMBER_DEFINITION const size_t
BytecodeArrayWriter::kMaxSizeOfPackedBytecode;
BytecodeArrayWriter::BytecodeArrayWriter(
Zone* zone, ConstantArrayBuilder* constant_array_builder,
SourcePositionTableBuilder::RecordingMode source_position_mode)
: bytecodes_(zone),
unbound_jumps_(0),
source_position_table_builder_(zone, source_position_mode),
constant_array_builder_(constant_array_builder),
last_bytecode_(Bytecode::kIllegal),
last_bytecode_offset_(0),
last_bytecode_had_source_info_(false),
elide_noneffectful_bytecodes_(
v8_flags.ignition_elide_noneffectful_bytecodes),
exit_seen_in_block_(false) {
bytecodes_.reserve(512); // Derived via experimentation.
}
template <typename IsolateT>
Handle<BytecodeArray> BytecodeArrayWriter::ToBytecodeArray(
IsolateT* isolate, int register_count, uint16_t parameter_count,
uint16_t max_arguments, DirectHandle<TrustedByteArray> handler_table) {
DCHECK_EQ(0, unbound_jumps_);
int bytecode_size = static_cast<int>(bytecodes()->size());
int frame_size = register_count * kSystemPointerSize;
DirectHandle<TrustedFixedArray> constant_pool =
constant_array_builder()->ToFixedArray(isolate);
Handle<BytecodeArray> bytecode_array = isolate->factory()->NewBytecodeArray(
bytecode_size, &bytecodes()->front(), frame_size, parameter_count,
max_arguments, constant_pool, handler_table);
return bytecode_array;
}
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
Handle<BytecodeArray> BytecodeArrayWriter::ToBytecodeArray(
Isolate* isolate, int register_count, uint16_t parameter_count,
uint16_t max_arguments, DirectHandle<TrustedByteArray> handler_table);
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
Handle<BytecodeArray> BytecodeArrayWriter::ToBytecodeArray(
LocalIsolate* isolate, int register_count, uint16_t parameter_count,
uint16_t max_arguments, DirectHandle<TrustedByteArray> handler_table);
template <typename IsolateT>
DirectHandle<TrustedByteArray> BytecodeArrayWriter::ToSourcePositionTable(
IsolateT* isolate) {
DCHECK(!source_position_table_builder_.Lazy());
DirectHandle<TrustedByteArray> source_position_table =
source_position_table_builder_.Omit()
? isolate->factory()->empty_trusted_byte_array()
: source_position_table_builder_.ToSourcePositionTable(isolate);
return source_position_table;
}
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
DirectHandle<TrustedByteArray> BytecodeArrayWriter::ToSourcePositionTable(
Isolate* isolate);
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
DirectHandle<TrustedByteArray> BytecodeArrayWriter::ToSourcePositionTable(
LocalIsolate* isolate);
#ifdef DEBUG
int BytecodeArrayWriter::CheckBytecodeMatches(Tagged<BytecodeArray> bytecode) {
int mismatches = false;
int bytecode_size = static_cast<int>(bytecodes()->size());
const uint8_t* bytecode_ptr = &bytecodes()->front();
if (bytecode_size != bytecode->length()) mismatches = true;
// If there's a mismatch only in the length of the bytecode (very unlikely)
// then the first mismatch will be the first extra bytecode.
int first_mismatch = std::min(bytecode_size, bytecode->length());
for (int i = 0; i < first_mismatch; ++i) {
if (bytecode_ptr[i] != bytecode->get(i)) {
mismatches = true;
first_mismatch = i;
break;
}
}
if (mismatches) {
return first_mismatch;
}
return -1;
}
#endif
void BytecodeArrayWriter::Write(BytecodeNode* node) {
DCHECK(!Bytecodes::IsJump(node->bytecode()));
if (exit_seen_in_block_) return; // Don't emit dead code.
UpdateExitSeenInBlock(node->bytecode());
MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid());
UpdateSourcePositionTable(node);
EmitBytecode(node);
}
void BytecodeArrayWriter::WriteJump(BytecodeNode* node, BytecodeLabel* label) {
DCHECK(Bytecodes::IsForwardJump(node->bytecode()));
if (exit_seen_in_block_) return; // Don't emit dead code.
UpdateExitSeenInBlock(node->bytecode());
MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid());
UpdateSourcePositionTable(node);
EmitJump(node, label);
}
void BytecodeArrayWriter::WriteJumpLoop(BytecodeNode* node,
BytecodeLoopHeader* loop_header) {
DCHECK_EQ(node->bytecode(), Bytecode::kJumpLoop);
if (exit_seen_in_block_) return; // Don't emit dead code.
UpdateExitSeenInBlock(node->bytecode());
MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid());
UpdateSourcePositionTable(node);
EmitJumpLoop(node, loop_header);
}
void BytecodeArrayWriter::WriteSwitch(BytecodeNode* node,
BytecodeJumpTable* jump_table) {
DCHECK(Bytecodes::IsSwitch(node->bytecode()));
if (exit_seen_in_block_) return; // Don't emit dead code.
UpdateExitSeenInBlock(node->bytecode());
MaybeElideLastBytecode(node->bytecode(), node->source_info().is_valid());
UpdateSourcePositionTable(node);
EmitSwitch(node, jump_table);
}
void BytecodeArrayWriter::BindLabel(BytecodeLabel* label) {
DCHECK(label->has_referrer_jump());
size_t current_offset = bytecodes()->size();
// Update the jump instruction's location.
PatchJump(current_offset, label->jump_offset());
label->bind();
StartBasicBlock();
}
void BytecodeArrayWriter::BindLoopHeader(BytecodeLoopHeader* loop_header) {
size_t current_offset = bytecodes()->size();
loop_header->bind_to(current_offset);
// Don't start a basic block when the entire loop is dead.
if (exit_seen_in_block_) return;
StartBasicBlock();
}
void BytecodeArrayWriter::BindJumpTableEntry(BytecodeJumpTable* jump_table,
int case_value) {
DCHECK(!jump_table->is_bound(case_value));
size_t current_offset = bytecodes()->size();
size_t relative_jump = current_offset - jump_table->switch_bytecode_offset();
constant_array_builder()->SetJumpTableSmi(
jump_table->ConstantPoolEntryFor(case_value),
Smi::FromInt(static_cast<int>(relative_jump)));
jump_table->mark_bound(case_value);
StartBasicBlock();
}
void BytecodeArrayWriter::BindHandlerTarget(
HandlerTableBuilder* handler_table_builder, int handler_id) {
size_t current_offset = bytecodes()->size();
StartBasicBlock();
handler_table_builder->SetHandlerTarget(handler_id, current_offset);
}
void BytecodeArrayWriter::BindTryRegionStart(
HandlerTableBuilder* handler_table_builder, int handler_id) {
size_t current_offset = bytecodes()->size();
// Try blocks don't have to be in a separate basic block, but we do have to
// invalidate the bytecode to avoid eliding it and changing the offset.
InvalidateLastBytecode();
handler_table_builder->SetTryRegionStart(handler_id, current_offset);
}
void BytecodeArrayWriter::BindTryRegionEnd(
HandlerTableBuilder* handler_table_builder, int handler_id) {
// Try blocks don't have to be in a separate basic block, but we do have to
// invalidate the bytecode to avoid eliding it and changing the offset.
InvalidateLastBytecode();
size_t current_offset = bytecodes()->size();
handler_table_builder->SetTryRegionEnd(handler_id, current_offset);
}
void BytecodeArrayWriter::SetFunctionEntrySourcePosition(int position) {
bool is_statement = false;
source_position_table_builder_.AddPosition(
kFunctionEntryBytecodeOffset, SourcePosition(position), is_statement);
}
void BytecodeArrayWriter::StartBasicBlock() {
InvalidateLastBytecode();
exit_seen_in_block_ = false;
}
void BytecodeArrayWriter::UpdateSourcePositionTable(
const BytecodeNode* const node) {
int bytecode_offset = static_cast<int>(bytecodes()->size());
const BytecodeSourceInfo& source_info = node->source_info();
if (source_info.is_valid()) {
source_position_table_builder()->AddPosition(
bytecode_offset, SourcePosition(source_info.source_position()),
source_info.is_statement());
}
}
void BytecodeArrayWriter::UpdateExitSeenInBlock(Bytecode bytecode) {
switch (bytecode) {
case Bytecode::kReturn:
case Bytecode::kThrow:
case Bytecode::kReThrow:
case Bytecode::kAbort:
case Bytecode::kJump:
case Bytecode::kJumpLoop:
case Bytecode::kJumpConstant:
case Bytecode::kSuspendGenerator:
exit_seen_in_block_ = true;
break;
default:
break;
}
}
void BytecodeArrayWriter::MaybeElideLastBytecode(Bytecode next_bytecode,
bool has_source_info) {
if (!elide_noneffectful_bytecodes_) return;
// If the last bytecode loaded the accumulator without any external effect,
// and the next bytecode clobbers this load without reading the accumulator,
// then the previous bytecode can be elided as it has no effect.
if (Bytecodes::IsAccumulatorLoadWithoutEffects(last_bytecode_) &&
Bytecodes::GetImplicitRegisterUse(next_bytecode) ==
ImplicitRegisterUse::kWriteAccumulator &&
(!last_bytecode_had_source_info_ || !has_source_info)) {
DCHECK_GT(bytecodes()->size(), last_bytecode_offset_);
bytecodes()->resize(last_bytecode_offset_);
// If the last bytecode had source info we will transfer the source info
// to this bytecode.
has_source_info |= last_bytecode_had_source_info_;
}
last_bytecode_ = next_bytecode;
last_bytecode_had_source_info_ = has_source_info;
last_bytecode_offset_ = bytecodes()->size();
}
void BytecodeArrayWriter::InvalidateLastBytecode() {
last_bytecode_ = Bytecode::kIllegal;
}
void BytecodeArrayWriter::EmitBytecode(const BytecodeNode* const node) {
DCHECK_NE(node->bytecode(), Bytecode::kIllegal);
Bytecode bytecode = node->bytecode();
OperandScale operand_scale = node->operand_scale();
if (operand_scale != OperandScale::kSingle) {
Bytecode prefix = Bytecodes::OperandScaleToPrefixBytecode(operand_scale);
bytecodes()->push_back(Bytecodes::ToByte(prefix));
}
bytecodes()->push_back(Bytecodes::ToByte(bytecode));
const uint32_t* const operands = node->operands();
const int operand_count = node->operand_count();
const OperandSize* operand_sizes =
Bytecodes::GetOperandSizes(bytecode, operand_scale);
for (int i = 0; i < operand_count; ++i) {
switch (operand_sizes[i]) {
case OperandSize::kNone:
UNREACHABLE();
case OperandSize::kByte:
bytecodes()->push_back(static_cast<uint8_t>(operands[i]));
break;
case OperandSize::kShort: {
uint16_t operand = static_cast<uint16_t>(operands[i]);
const uint8_t* raw_operand = reinterpret_cast<const uint8_t*>(&operand);
bytecodes()->push_back(raw_operand[0]);
bytecodes()->push_back(raw_operand[1]);
break;
}
case OperandSize::kQuad: {
const uint8_t* raw_operand =
reinterpret_cast<const uint8_t*>(&operands[i]);
bytecodes()->push_back(raw_operand[0]);
bytecodes()->push_back(raw_operand[1]);
bytecodes()->push_back(raw_operand[2]);
bytecodes()->push_back(raw_operand[3]);
break;
}
}
}
}
// static
Bytecode GetJumpWithConstantOperand(Bytecode jump_bytecode) {
switch (jump_bytecode) {
case Bytecode::kJump:
return Bytecode::kJumpConstant;
case Bytecode::kJumpIfTrue:
return Bytecode::kJumpIfTrueConstant;
case Bytecode::kJumpIfFalse:
return Bytecode::kJumpIfFalseConstant;
case Bytecode::kJumpIfToBooleanTrue:
return Bytecode::kJumpIfToBooleanTrueConstant;
case Bytecode::kJumpIfToBooleanFalse:
return Bytecode::kJumpIfToBooleanFalseConstant;
case Bytecode::kJumpIfNull:
return Bytecode::kJumpIfNullConstant;
case Bytecode::kJumpIfNotNull:
return Bytecode::kJumpIfNotNullConstant;
case Bytecode::kJumpIfUndefined:
return Bytecode::kJumpIfUndefinedConstant;
case Bytecode::kJumpIfNotUndefined:
return Bytecode::kJumpIfNotUndefinedConstant;
case Bytecode::kJumpIfUndefinedOrNull:
return Bytecode::kJumpIfUndefinedOrNullConstant;
case Bytecode::kJumpIfJSReceiver:
return Bytecode::kJumpIfJSReceiverConstant;
case Bytecode::kJumpIfForInDone:
return Bytecode::kJumpIfForInDoneConstant;
default:
UNREACHABLE();
}
}
void BytecodeArrayWriter::PatchJumpWith8BitOperand(size_t jump_location,
int delta) {
Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location));
DCHECK(Bytecodes::IsForwardJump(jump_bytecode));
DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
DCHECK_EQ(Bytecodes::GetOperandType(jump_bytecode, 0), OperandType::kUImm);
DCHECK_GT(delta, 0);
size_t operand_location = jump_location + 1;
DCHECK_EQ(bytecodes()->at(operand_location), k8BitJumpPlaceholder);
if (Bytecodes::ScaleForUnsignedOperand(delta) == OperandScale::kSingle) {
// The jump fits within the range of an UImm8 operand, so cancel
// the reservation and jump directly.
constant_array_builder()->DiscardReservedEntry(OperandSize::kByte);
bytecodes()->at(operand_location) = static_cast<uint8_t>(delta);
} else {
// The jump does not fit within the range of an UImm8 operand, so
// commit reservation putting the offset into the constant pool,
// and update the jump instruction and operand.
size_t entry = constant_array_builder()->CommitReservedEntry(
OperandSize::kByte, Smi::FromInt(delta));
DCHECK_EQ(Bytecodes::SizeForUnsignedOperand(static_cast<uint32_t>(entry)),
OperandSize::kByte);
jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
bytecodes()->at(jump_location) = Bytecodes::ToByte(jump_bytecode);
bytecodes()->at(operand_location) = static_cast<uint8_t>(entry);
}
}
void BytecodeArrayWriter::PatchJumpWith16BitOperand(size_t jump_location,
int delta) {
Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location));
DCHECK(Bytecodes::IsForwardJump(jump_bytecode));
DCHECK(Bytecodes::IsJumpImmediate(jump_bytecode));
DCHECK_EQ(Bytecodes::GetOperandType(jump_bytecode, 0), OperandType::kUImm);
DCHECK_GT(delta, 0);
size_t operand_location = jump_location + 1;
uint8_t operand_bytes[2];
if (Bytecodes::ScaleForUnsignedOperand(delta) <= OperandScale::kDouble) {
// The jump fits within the range of an Imm16 operand, so cancel
// the reservation and jump directly.
constant_array_builder()->DiscardReservedEntry(OperandSize::kShort);
base::WriteUnalignedValue<uint16_t>(
reinterpret_cast<Address>(operand_bytes), static_cast<uint16_t>(delta));
} else {
// The jump does not fit within the range of an Imm16 operand, so
// commit reservation putting the offset into the constant pool,
// and update the jump instruction and operand.
size_t entry = constant_array_builder()->CommitReservedEntry(
OperandSize::kShort, Smi::FromInt(delta));
jump_bytecode = GetJumpWithConstantOperand(jump_bytecode);
bytecodes()->at(jump_location) = Bytecodes::ToByte(jump_bytecode);
base::WriteUnalignedValue<uint16_t>(
reinterpret_cast<Address>(operand_bytes), static_cast<uint16_t>(entry));
}
DCHECK(bytecodes()->at(operand_location) == k8BitJumpPlaceholder &&
bytecodes()->at(operand_location + 1) == k8BitJumpPlaceholder);
bytecodes()->at(operand_location++) = operand_bytes[0];
bytecodes()->at(operand_location) = operand_bytes[1];
}
void BytecodeArrayWriter::PatchJumpWith32BitOperand(size_t jump_location,
int delta) {
DCHECK(Bytecodes::IsJumpImmediate(
Bytecodes::FromByte(bytecodes()->at(jump_location))));
constant_array_builder()->DiscardReservedEntry(OperandSize::kQuad);
uint8_t operand_bytes[4];
base::WriteUnalignedValue<uint32_t>(reinterpret_cast<Address>(operand_bytes),
static_cast<uint32_t>(delta));
size_t operand_location = jump_location + 1;
DCHECK(bytecodes()->at(operand_location) == k8BitJumpPlaceholder &&
bytecodes()->at(operand_location + 1) == k8BitJumpPlaceholder &&
bytecodes()->at(operand_location + 2) == k8BitJumpPlaceholder &&
bytecodes()->at(operand_location + 3) == k8BitJumpPlaceholder);
bytecodes()->at(operand_location++) = operand_bytes[0];
bytecodes()->at(operand_location++) = operand_bytes[1];
bytecodes()->at(operand_location++) = operand_bytes[2];
bytecodes()->at(operand_location) = operand_bytes[3];
}
void BytecodeArrayWriter::PatchJump(size_t jump_target, size_t jump_location) {
Bytecode jump_bytecode = Bytecodes::FromByte(bytecodes()->at(jump_location));
int delta = static_cast<int>(jump_target - jump_location);
int prefix_offset = 0;
OperandScale operand_scale = OperandScale::kSingle;
if (Bytecodes::IsPrefixScalingBytecode(jump_bytecode)) {
// If a prefix scaling bytecode is emitted the target offset is one
// less than the case of no prefix scaling bytecode.
delta -= 1;
prefix_offset = 1;
operand_scale = Bytecodes::PrefixBytecodeToOperandScale(jump_bytecode);
jump_bytecode =
Bytecodes::FromByte(bytecodes()->at(jump_location + prefix_offset));
}
DCHECK(Bytecodes::IsJump(jump_bytecode));
switch (operand_scale) {
case OperandScale::kSingle:
PatchJumpWith8BitOperand(jump_location, delta);
break;
case OperandScale::kDouble:
PatchJumpWith16BitOperand(jump_location + prefix_offset, delta);
break;
case OperandScale::kQuadruple:
PatchJumpWith32BitOperand(jump_location + prefix_offset, delta);
break;
default:
UNREACHABLE();
}
unbound_jumps_--;
}
void BytecodeArrayWriter::EmitJumpLoop(BytecodeNode* node,
BytecodeLoopHeader* loop_header) {
DCHECK_EQ(node->bytecode(), Bytecode::kJumpLoop);
DCHECK_EQ(0u, node->operand(0));
size_t current_offset = bytecodes()->size();
CHECK_GE(current_offset, loop_header->offset());
CHECK_LE(current_offset, static_cast<size_t>(kMaxUInt32));
// Update the actual jump offset now that we know the bytecode offset of both
// the target loop header and this JumpLoop bytecode.
//
// The label has been bound already so this is a backwards jump.
uint32_t delta =
static_cast<uint32_t>(current_offset - loop_header->offset());
// This JumpLoop bytecode itself may have a kWide or kExtraWide prefix; if
// so, bump the delta to account for it.
const bool emits_prefix_bytecode =
Bytecodes::OperandScaleRequiresPrefixBytecode(node->operand_scale()) ||
Bytecodes::OperandScaleRequiresPrefixBytecode(
Bytecodes::ScaleForUnsignedOperand(delta));
if (emits_prefix_bytecode) {
static constexpr int kPrefixBytecodeSize = 1;
delta += kPrefixBytecodeSize;
DCHECK_EQ(Bytecodes::Size(Bytecode::kWide, OperandScale::kSingle),
kPrefixBytecodeSize);
DCHECK_EQ(Bytecodes::Size(Bytecode::kExtraWide, OperandScale::kSingle),
kPrefixBytecodeSize);
}
node->update_operand0(delta);
DCHECK_EQ(
Bytecodes::OperandScaleRequiresPrefixBytecode(node->operand_scale()),
emits_prefix_bytecode);
EmitBytecode(node);
}
void BytecodeArrayWriter::EmitJump(BytecodeNode* node, BytecodeLabel* label) {
DCHECK(Bytecodes::IsForwardJump(node->bytecode()));
DCHECK_EQ(0u, node->operand(0));
size_t current_offset = bytecodes()->size();
// The label has not yet been bound so this is a forward reference
// that will be patched when the label is bound. We create a
// reservation in the constant pool so the jump can be patched
// when the label is bound. The reservation means the maximum size
// of the operand for the constant is known and the jump can
// be emitted into the bytecode stream with space for the operand.
unbound_jumps_++;
label->set_referrer(current_offset);
OperandSize reserved_operand_size =
constant_array_builder()->CreateReservedEntry(
static_cast<OperandSize>(node->operand_scale()));
DCHECK_NE(Bytecode::kJumpLoop, node->bytecode());
switch (reserved_operand_size) {
case OperandSize::kNone:
UNREACHABLE();
case OperandSize::kByte:
node->update_operand0(k8BitJumpPlaceholder);
break;
case OperandSize::kShort:
node->update_operand0(k16BitJumpPlaceholder);
break;
case OperandSize::kQuad:
node->update_operand0(k32BitJumpPlaceholder);
break;
}
EmitBytecode(node);
}
void BytecodeArrayWriter::EmitSwitch(BytecodeNode* node,
BytecodeJumpTable* jump_table) {
DCHECK(Bytecodes::IsSwitch(node->bytecode()));
size_t current_offset = bytecodes()->size();
if (node->operand_scale() > OperandScale::kSingle) {
// Adjust for scaling byte prefix.
current_offset += 1;
}
jump_table->set_switch_bytecode_offset(current_offset);
EmitBytecode(node);
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,137 @@
// Copyright 2015 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_INTERPRETER_BYTECODE_ARRAY_WRITER_H_
#define V8_INTERPRETER_BYTECODE_ARRAY_WRITER_H_
#include "src/codegen/source-position-table.h"
#include "src/common/globals.h"
#include "src/interpreter/bytecodes.h"
namespace v8 {
namespace internal {
class BytecodeArray;
class TrustedByteArray;
class SourcePositionTableBuilder;
namespace interpreter {
class BytecodeLabel;
class BytecodeLoopHeader;
class BytecodeNode;
class BytecodeJumpTable;
class ConstantArrayBuilder;
class HandlerTableBuilder;
namespace bytecode_array_writer_unittest {
class BytecodeArrayWriterUnittest;
} // namespace bytecode_array_writer_unittest
// Class for emitting bytecode as the final stage of the bytecode
// generation pipeline.
class V8_EXPORT_PRIVATE BytecodeArrayWriter final {
public:
BytecodeArrayWriter(
Zone* zone, ConstantArrayBuilder* constant_array_builder,
SourcePositionTableBuilder::RecordingMode source_position_mode);
BytecodeArrayWriter(const BytecodeArrayWriter&) = delete;
BytecodeArrayWriter& operator=(const BytecodeArrayWriter&) = delete;
void Write(BytecodeNode* node);
void WriteJump(BytecodeNode* node, BytecodeLabel* label);
void WriteJumpLoop(BytecodeNode* node, BytecodeLoopHeader* loop_header);
void WriteSwitch(BytecodeNode* node, BytecodeJumpTable* jump_table);
void BindLabel(BytecodeLabel* label);
void BindLoopHeader(BytecodeLoopHeader* loop_header);
void BindJumpTableEntry(BytecodeJumpTable* jump_table, int case_value);
void BindHandlerTarget(HandlerTableBuilder* handler_table_builder,
int handler_id);
void BindTryRegionStart(HandlerTableBuilder* handler_table_builder,
int handler_id);
void BindTryRegionEnd(HandlerTableBuilder* handler_table_builder,
int handler_id);
void SetFunctionEntrySourcePosition(int position);
template <typename IsolateT>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
Handle<BytecodeArray> ToBytecodeArray(
IsolateT* isolate, int register_count, uint16_t parameter_count,
uint16_t max_arguments, DirectHandle<TrustedByteArray> handler_table);
template <typename IsolateT>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
DirectHandle<TrustedByteArray> ToSourcePositionTable(IsolateT* isolate);
#ifdef DEBUG
// Returns -1 if they match or the offset of the first mismatching byte.
int CheckBytecodeMatches(Tagged<BytecodeArray> bytecode);
#endif
bool RemainderOfBlockIsDead() const { return exit_seen_in_block_; }
private:
// Maximum sized packed bytecode is comprised of a prefix bytecode,
// plus the actual bytecode, plus the maximum number of operands times
// the maximum operand size.
static const size_t kMaxSizeOfPackedBytecode =
2 * sizeof(Bytecode) +
Bytecodes::kMaxOperands * static_cast<size_t>(OperandSize::kLast);
// Constants that act as placeholders for jump operands to be
// patched. These have operand sizes that match the sizes of
// reserved constant pool entries.
const uint32_t k8BitJumpPlaceholder = 0x7f;
const uint32_t k16BitJumpPlaceholder =
k8BitJumpPlaceholder | (k8BitJumpPlaceholder << 8);
const uint32_t k32BitJumpPlaceholder =
k16BitJumpPlaceholder | (k16BitJumpPlaceholder << 16);
void PatchJump(size_t jump_target, size_t jump_location);
void PatchJumpWith8BitOperand(size_t jump_location, int delta);
void PatchJumpWith16BitOperand(size_t jump_location, int delta);
void PatchJumpWith32BitOperand(size_t jump_location, int delta);
void EmitBytecode(const BytecodeNode* const node);
void EmitJump(BytecodeNode* node, BytecodeLabel* label);
void EmitJumpLoop(BytecodeNode* node, BytecodeLoopHeader* loop_header);
void EmitSwitch(BytecodeNode* node, BytecodeJumpTable* jump_table);
void UpdateSourcePositionTable(const BytecodeNode* const node);
void UpdateExitSeenInBlock(Bytecode bytecode);
void MaybeElideLastBytecode(Bytecode next_bytecode, bool has_source_info);
void InvalidateLastBytecode();
void StartBasicBlock();
ZoneVector<uint8_t>* bytecodes() { return &bytecodes_; }
SourcePositionTableBuilder* source_position_table_builder() {
return &source_position_table_builder_;
}
ConstantArrayBuilder* constant_array_builder() {
return constant_array_builder_;
}
ZoneVector<uint8_t> bytecodes_;
int unbound_jumps_;
SourcePositionTableBuilder source_position_table_builder_;
ConstantArrayBuilder* constant_array_builder_;
Bytecode last_bytecode_;
size_t last_bytecode_offset_;
bool last_bytecode_had_source_info_;
bool elide_noneffectful_bytecodes_;
bool exit_seen_in_block_;
friend class bytecode_array_writer_unittest::BytecodeArrayWriterUnittest;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_ARRAY_WRITER_H_

View File

@ -0,0 +1,226 @@
// Copyright 2015 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.
#include "src/interpreter/bytecode-decoder.h"
#include <iomanip>
#include "src/interpreter/interpreter-intrinsics.h"
#include "src/objects/contexts.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
namespace interpreter {
// static
Register BytecodeDecoder::DecodeRegisterOperand(Address operand_start,
OperandType operand_type,
OperandScale operand_scale) {
DCHECK(Bytecodes::IsRegisterOperandType(operand_type));
int32_t operand =
DecodeSignedOperand(operand_start, operand_type, operand_scale);
return Register::FromOperand(operand);
}
// static
RegisterList BytecodeDecoder::DecodeRegisterListOperand(
Address operand_start, uint32_t count, OperandType operand_type,
OperandScale operand_scale) {
Register first_reg =
DecodeRegisterOperand(operand_start, operand_type, operand_scale);
return RegisterList(first_reg.index(), static_cast<int>(count));
}
// static
int32_t BytecodeDecoder::DecodeSignedOperand(Address operand_start,
OperandType operand_type,
OperandScale operand_scale) {
DCHECK(!Bytecodes::IsUnsignedOperandType(operand_type));
switch (Bytecodes::SizeOfOperand(operand_type, operand_scale)) {
case OperandSize::kByte:
return *reinterpret_cast<const int8_t*>(operand_start);
case OperandSize::kShort:
return static_cast<int16_t>(
base::ReadUnalignedValue<uint16_t>(operand_start));
case OperandSize::kQuad:
return static_cast<int32_t>(
base::ReadUnalignedValue<uint32_t>(operand_start));
case OperandSize::kNone:
UNREACHABLE();
}
return 0;
}
// static
uint32_t BytecodeDecoder::DecodeUnsignedOperand(Address operand_start,
OperandType operand_type,
OperandScale operand_scale) {
DCHECK(Bytecodes::IsUnsignedOperandType(operand_type));
switch (Bytecodes::SizeOfOperand(operand_type, operand_scale)) {
case OperandSize::kByte:
return *reinterpret_cast<const uint8_t*>(operand_start);
case OperandSize::kShort:
return base::ReadUnalignedValue<uint16_t>(operand_start);
case OperandSize::kQuad:
return base::ReadUnalignedValue<uint32_t>(operand_start);
case OperandSize::kNone:
UNREACHABLE();
}
return 0;
}
namespace {
const char* NameForRuntimeId(Runtime::FunctionId idx) {
return Runtime::FunctionForId(idx)->name;
}
const char* NameForNativeContextIndex(uint32_t idx) {
switch (idx) {
#define CASE(index_name, type, name) \
case Context::index_name: \
return #name;
NATIVE_CONTEXT_FIELDS(CASE)
#undef CASE
default:
UNREACHABLE();
}
}
} // anonymous namespace
// static
std::ostream& BytecodeDecoder::Decode(std::ostream& os,
const uint8_t* bytecode_start,
bool with_hex) {
Bytecode bytecode = Bytecodes::FromByte(bytecode_start[0]);
int prefix_offset = 0;
OperandScale operand_scale = OperandScale::kSingle;
if (Bytecodes::IsPrefixScalingBytecode(bytecode)) {
prefix_offset = 1;
operand_scale = Bytecodes::PrefixBytecodeToOperandScale(bytecode);
bytecode = Bytecodes::FromByte(bytecode_start[1]);
}
// Prepare to print bytecode and operands as hex digits.
if (with_hex) {
std::ios saved_format(nullptr);
saved_format.copyfmt(saved_format);
os.fill('0');
os.flags(std::ios::hex);
int bytecode_size = Bytecodes::Size(bytecode, operand_scale);
for (int i = 0; i < prefix_offset + bytecode_size; i++) {
os << std::setw(2) << static_cast<uint32_t>(bytecode_start[i]) << ' ';
}
os.copyfmt(saved_format);
const int kBytecodeColumnSize = 6;
for (int i = prefix_offset + bytecode_size; i < kBytecodeColumnSize; i++) {
os << " ";
}
}
os << Bytecodes::ToString(bytecode, operand_scale);
// Operands for the debug break are from the original instruction.
if (Bytecodes::IsDebugBreak(bytecode)) return os;
int number_of_operands = Bytecodes::NumberOfOperands(bytecode);
if (number_of_operands > 0) os << " ";
for (int i = 0; i < number_of_operands; i++) {
OperandType op_type = Bytecodes::GetOperandType(bytecode, i);
int operand_offset =
Bytecodes::GetOperandOffset(bytecode, i, operand_scale);
Address operand_start = reinterpret_cast<Address>(
&bytecode_start[prefix_offset + operand_offset]);
switch (op_type) {
case interpreter::OperandType::kIdx:
case interpreter::OperandType::kUImm:
os << "["
<< DecodeUnsignedOperand(operand_start, op_type, operand_scale)
<< "]";
break;
case interpreter::OperandType::kIntrinsicId: {
auto id = static_cast<IntrinsicsHelper::IntrinsicId>(
DecodeUnsignedOperand(operand_start, op_type, operand_scale));
os << "[" << NameForRuntimeId(IntrinsicsHelper::ToRuntimeId(id)) << "]";
break;
}
case interpreter::OperandType::kNativeContextIndex: {
auto id = DecodeUnsignedOperand(operand_start, op_type, operand_scale);
os << "[" << NameForNativeContextIndex(id) << "]";
break;
}
case interpreter::OperandType::kRuntimeId:
os << "["
<< NameForRuntimeId(static_cast<Runtime::FunctionId>(
DecodeUnsignedOperand(operand_start, op_type, operand_scale)))
<< "]";
break;
case interpreter::OperandType::kImm:
os << "[" << DecodeSignedOperand(operand_start, op_type, operand_scale)
<< "]";
break;
case interpreter::OperandType::kFlag8:
case interpreter::OperandType::kFlag16:
os << "#"
<< DecodeUnsignedOperand(operand_start, op_type, operand_scale);
break;
case interpreter::OperandType::kReg:
case interpreter::OperandType::kRegOut:
case interpreter::OperandType::kRegInOut: {
Register reg =
DecodeRegisterOperand(operand_start, op_type, operand_scale);
os << reg.ToString();
break;
}
case interpreter::OperandType::kRegOutTriple: {
RegisterList reg_list =
DecodeRegisterListOperand(operand_start, 3, op_type, operand_scale);
os << reg_list.first_register().ToString() << "-"
<< reg_list.last_register().ToString();
break;
}
case interpreter::OperandType::kRegOutPair:
case interpreter::OperandType::kRegPair: {
RegisterList reg_list =
DecodeRegisterListOperand(operand_start, 2, op_type, operand_scale);
os << reg_list.first_register().ToString() << "-"
<< reg_list.last_register().ToString();
break;
}
case interpreter::OperandType::kRegOutList:
case interpreter::OperandType::kRegList: {
DCHECK_LT(i, number_of_operands - 1);
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, i + 1),
OperandType::kRegCount);
int reg_count_offset =
Bytecodes::GetOperandOffset(bytecode, i + 1, operand_scale);
Address reg_count_operand = reinterpret_cast<Address>(
&bytecode_start[prefix_offset + reg_count_offset]);
uint32_t count = DecodeUnsignedOperand(
reg_count_operand, OperandType::kRegCount, operand_scale);
RegisterList reg_list = DecodeRegisterListOperand(
operand_start, count, op_type, operand_scale);
os << reg_list.first_register().ToString() << "-"
<< reg_list.last_register().ToString();
i++; // Skip kRegCount.
break;
}
case interpreter::OperandType::kNone:
case interpreter::OperandType::kRegCount: // Dealt with in kRegList.
UNREACHABLE();
}
if (i != number_of_operands - 1) {
os << ", ";
}
}
return os;
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,48 @@
// Copyright 2015 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_INTERPRETER_BYTECODE_DECODER_H_
#define V8_INTERPRETER_BYTECODE_DECODER_H_
#include <iosfwd>
#include "src/interpreter/bytecode-register.h"
namespace v8 {
namespace internal {
namespace interpreter {
class V8_EXPORT_PRIVATE BytecodeDecoder final {
public:
// Decodes a register operand in a byte array.
static Register DecodeRegisterOperand(Address operand_start,
OperandType operand_type,
OperandScale operand_scale);
// Decodes a register list operand in a byte array.
static RegisterList DecodeRegisterListOperand(Address operand_start,
uint32_t count,
OperandType operand_type,
OperandScale operand_scale);
// Decodes a signed operand in a byte array.
static int32_t DecodeSignedOperand(Address operand_start,
OperandType operand_type,
OperandScale operand_scale);
// Decodes an unsigned operand in a byte array.
static uint32_t DecodeUnsignedOperand(Address operand_start,
OperandType operand_type,
OperandScale operand_scale);
// Decode a single bytecode and operands to |os|.
static std::ostream& Decode(std::ostream& os, const uint8_t* bytecode_start,
bool with_hex = true);
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_DECODER_H_

View File

@ -0,0 +1,111 @@
// Copyright 2016 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.
#include "src/interpreter/bytecode-flags-and-tokens.h"
#include "src/ast/ast-value-factory.h"
#include "src/ast/ast.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
namespace interpreter {
// static
uint8_t CreateArrayLiteralFlags::Encode(bool use_fast_shallow_clone,
int runtime_flags) {
uint8_t result = FlagsBits::encode(runtime_flags);
result |= FastCloneSupportedBit::encode(use_fast_shallow_clone);
return result;
}
// static
uint8_t CreateObjectLiteralFlags::Encode(int runtime_flags,
bool fast_clone_supported) {
uint8_t result = FlagsBits::encode(runtime_flags);
result |= FastCloneSupportedBit::encode(fast_clone_supported);
return result;
}
// static
uint8_t CreateClosureFlags::Encode(bool pretenure, bool is_function_scope,
bool might_always_turbofan) {
uint8_t result = PretenuredBit::encode(pretenure);
if (!might_always_turbofan && !pretenure && is_function_scope) {
result |= FastNewClosureBit::encode(true);
}
return result;
}
// static
TestTypeOfFlags::LiteralFlag TestTypeOfFlags::GetFlagForLiteral(
const AstStringConstants* ast_constants, Literal* literal) {
const AstRawString* raw_literal = literal->AsRawString();
if (raw_literal == ast_constants->number_string()) {
return LiteralFlag::kNumber;
} else if (raw_literal == ast_constants->string_string()) {
return LiteralFlag::kString;
} else if (raw_literal == ast_constants->symbol_string()) {
return LiteralFlag::kSymbol;
} else if (raw_literal == ast_constants->boolean_string()) {
return LiteralFlag::kBoolean;
} else if (raw_literal == ast_constants->bigint_string()) {
return LiteralFlag::kBigInt;
} else if (raw_literal == ast_constants->undefined_string()) {
return LiteralFlag::kUndefined;
} else if (raw_literal == ast_constants->function_string()) {
return LiteralFlag::kFunction;
} else if (raw_literal == ast_constants->object_string()) {
return LiteralFlag::kObject;
} else {
return LiteralFlag::kOther;
}
}
// static
uint8_t TestTypeOfFlags::Encode(LiteralFlag literal_flag) {
return static_cast<uint8_t>(literal_flag);
}
// static
TestTypeOfFlags::LiteralFlag TestTypeOfFlags::Decode(uint8_t raw_flag) {
DCHECK_LE(raw_flag, static_cast<uint8_t>(LiteralFlag::kOther));
return static_cast<LiteralFlag>(raw_flag);
}
// static
const char* TestTypeOfFlags::ToString(LiteralFlag literal_flag) {
switch (literal_flag) {
#define CASE(Name, name) \
case LiteralFlag::k##Name: \
return #name;
TYPEOF_LITERAL_LIST(CASE)
#undef CASE
default:
return "<invalid>";
}
}
// static
uint8_t StoreLookupSlotFlags::Encode(LanguageMode language_mode,
LookupHoistingMode lookup_hoisting_mode) {
DCHECK_IMPLIES(lookup_hoisting_mode == LookupHoistingMode::kLegacySloppy,
language_mode == LanguageMode::kSloppy);
return LanguageModeBit::encode(language_mode) |
LookupHoistingModeBit::encode(static_cast<bool>(lookup_hoisting_mode));
}
// static
LanguageMode StoreLookupSlotFlags::GetLanguageMode(uint8_t flags) {
return LanguageModeBit::decode(flags);
}
// static
bool StoreLookupSlotFlags::IsLookupHoistingMode(uint8_t flags) {
return LookupHoistingModeBit::decode(flags);
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,116 @@
// Copyright 2016 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_INTERPRETER_BYTECODE_FLAGS_AND_TOKENS_H_
#define V8_INTERPRETER_BYTECODE_FLAGS_AND_TOKENS_H_
#include "src/base/bit-field.h"
#include "src/common/globals.h"
namespace v8 {
namespace internal {
// Forward declarations.
class Literal;
class AstStringConstants;
namespace interpreter {
class CreateArrayLiteralFlags {
public:
using FlagsBits = base::BitField8<int, 0, 5>;
using FastCloneSupportedBit = FlagsBits::Next<bool, 1>;
static uint8_t Encode(bool use_fast_shallow_clone, int runtime_flags);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CreateArrayLiteralFlags);
};
class CreateObjectLiteralFlags {
public:
using FlagsBits = base::BitField8<int, 0, 5>;
using FastCloneSupportedBit = FlagsBits::Next<bool, 1>;
static uint8_t Encode(int runtime_flags, bool fast_clone_supported);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CreateObjectLiteralFlags);
};
class CreateClosureFlags {
public:
using PretenuredBit = base::BitField8<bool, 0, 1>;
using FastNewClosureBit = PretenuredBit::Next<bool, 1>;
static uint8_t Encode(bool pretenure, bool is_function_scope,
bool might_always_turbofan);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CreateClosureFlags);
};
#define TYPEOF_LITERAL_LIST(V) \
V(Number, number) \
V(String, string) \
V(Symbol, symbol) \
V(Boolean, boolean) \
V(BigInt, bigint) \
V(Undefined, undefined) \
V(Function, function) \
V(Object, object) \
V(Other, other)
class TestTypeOfFlags {
public:
enum class LiteralFlag : uint8_t {
#define DECLARE_LITERAL_FLAG(name, _) k##name,
TYPEOF_LITERAL_LIST(DECLARE_LITERAL_FLAG)
#undef DECLARE_LITERAL_FLAG
};
static LiteralFlag GetFlagForLiteral(const AstStringConstants* ast_constants,
Literal* literal);
static uint8_t Encode(LiteralFlag literal_flag);
static LiteralFlag Decode(uint8_t raw_flag);
static const char* ToString(LiteralFlag literal_flag);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(TestTypeOfFlags);
};
class StoreLookupSlotFlags {
public:
using LanguageModeBit = base::BitField8<LanguageMode, 0, 1>;
using LookupHoistingModeBit = LanguageModeBit::Next<bool, 1>;
static_assert(LanguageModeSize <= LanguageModeBit::kNumValues);
static uint8_t Encode(LanguageMode language_mode,
LookupHoistingMode lookup_hoisting_mode);
static LanguageMode GetLanguageMode(uint8_t flags);
static bool IsLookupHoistingMode(uint8_t flags);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(StoreLookupSlotFlags);
};
enum class TryFinallyContinuationToken: int {
// Fixed value tokens for paths we know we need.
// Fallthrough is set to -1 to make it the fallthrough case of the jump table,
// where the remaining cases start at 0.
kFallthroughToken = -1,
// TODO(leszeks): Rethrow being 0 makes it use up a valuable LdaZero, which
// means that other commands (such as break or return) have to use LdaSmi.
// This can very slightly bloat bytecode, so perhaps token values should all
// be shifted down by 1.
kRethrowToken = 0
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_FLAGS_AND_TOKENS_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,657 @@
// Copyright 2015 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_INTERPRETER_BYTECODE_GENERATOR_H_
#define V8_INTERPRETER_BYTECODE_GENERATOR_H_
#include "src/ast/ast.h"
#include "src/execution/isolate.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-register.h"
#include "src/objects/feedback-vector.h"
#include "src/objects/function-kind.h"
namespace v8 {
namespace internal {
class AstNodeSourceRanges;
class AstStringConstants;
class BytecodeArray;
class UnoptimizedCompilationInfo;
enum class SourceRangeKind;
namespace interpreter {
class TopLevelDeclarationsBuilder;
class LoopBuilder;
class BlockCoverageBuilder;
class BytecodeJumpTable;
class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
public:
enum TypeHint : uint8_t {
kBoolean = 1 << 0,
kInternalizedString = 1 << 1,
kString = kInternalizedString | (1 << 2),
kAny = kBoolean | kString,
kUnknown = 0xFFu
};
explicit BytecodeGenerator(
LocalIsolate* local_isolate, Zone* zone, UnoptimizedCompilationInfo* info,
const AstStringConstants* ast_string_constants,
std::vector<FunctionLiteral*>* eager_inner_literals,
Handle<Script> script);
void GenerateBytecode(uintptr_t stack_limit);
template <typename IsolateT>
Handle<BytecodeArray> FinalizeBytecode(IsolateT* isolate,
Handle<Script> script);
template <typename IsolateT>
DirectHandle<TrustedByteArray> FinalizeSourcePositionTable(IsolateT* isolate);
// Check if hint2 is same or the subtype of hint1.
static bool IsSameOrSubTypeHint(TypeHint hint1, TypeHint hint2) {
return hint1 == (hint1 | hint2);
}
static bool IsStringTypeHint(TypeHint hint) {
return IsSameOrSubTypeHint(TypeHint::kString, hint);
}
#ifdef DEBUG
int CheckBytecodeMatches(Tagged<BytecodeArray> bytecode);
#endif
#define DECLARE_VISIT(type) void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
// Visiting function for declarations list and statements are overridden.
void VisitModuleDeclarations(Declaration::List* declarations);
void VisitGlobalDeclarations(Declaration::List* declarations);
void VisitDeclarations(Declaration::List* declarations);
void VisitStatements(const ZonePtrList<Statement>* statments, int start = 0);
private:
class AccumulatorPreservingScope;
class ContextScope;
class ControlScope;
class ControlScopeForBreakable;
class ControlScopeForDerivedConstructor;
class ControlScopeForIteration;
class ControlScopeForTopLevel;
class ControlScopeForTryCatch;
class ControlScopeForTryFinally;
class CurrentScope;
class EffectResultScope;
class ExpressionResultScope;
class FeedbackSlotCache;
class HoleCheckElisionScope;
class HoleCheckElisionMergeScope;
class IteratorRecord;
class MultipleEntryBlockContextScope;
class LoopScope;
class ForInScope;
class NaryCodeCoverageSlots;
class OptionalChainNullLabelScope;
class RegisterAllocationScope;
class TestResultScope;
class TopLevelDeclarationsBuilder;
class DisposablesStackScope;
class ValueResultScope;
using ToBooleanMode = BytecodeArrayBuilder::ToBooleanMode;
enum class TestFallthrough { kThen, kElse, kNone };
enum class AccumulatorPreservingMode { kNone, kPreserve };
// An assignment has to evaluate its LHS before its RHS, but has to assign to
// the LHS after both evaluations are done. This class stores the data
// computed in the LHS evaluation that has to live across the RHS evaluation,
// and is used in the actual LHS assignment.
class AssignmentLhsData {
public:
static AssignmentLhsData NonProperty(Expression* expr);
static AssignmentLhsData NamedProperty(Expression* object_expr,
Register object,
const AstRawString* name);
static AssignmentLhsData KeyedProperty(Register object, Register key);
static AssignmentLhsData PrivateMethodOrAccessor(AssignType type,
Property* property,
Register object,
Register key);
static AssignmentLhsData PrivateDebugEvaluate(AssignType type,
Property* property,
Register object);
static AssignmentLhsData NamedSuperProperty(
RegisterList super_property_args);
static AssignmentLhsData KeyedSuperProperty(
RegisterList super_property_args);
AssignType assign_type() const { return assign_type_; }
bool is_private_assign_type() const {
return assign_type_ == PRIVATE_METHOD ||
assign_type_ == PRIVATE_GETTER_ONLY ||
assign_type_ == PRIVATE_SETTER_ONLY ||
assign_type_ == PRIVATE_GETTER_AND_SETTER ||
assign_type_ == PRIVATE_DEBUG_DYNAMIC;
}
Expression* expr() const {
DCHECK(assign_type_ == NON_PROPERTY || is_private_assign_type());
return expr_;
}
Expression* object_expr() const {
DCHECK_EQ(assign_type_, NAMED_PROPERTY);
return object_expr_;
}
Register object() const {
DCHECK(assign_type_ == NAMED_PROPERTY || assign_type_ == KEYED_PROPERTY ||
is_private_assign_type());
return object_;
}
Register key() const {
DCHECK((assign_type_ == KEYED_PROPERTY || is_private_assign_type()) &&
assign_type_ != PRIVATE_DEBUG_DYNAMIC);
return key_;
}
const AstRawString* name() const {
DCHECK(assign_type_ == NAMED_PROPERTY);
return name_;
}
RegisterList super_property_args() const {
DCHECK(assign_type_ == NAMED_SUPER_PROPERTY ||
assign_type_ == KEYED_SUPER_PROPERTY);
return super_property_args_;
}
private:
AssignmentLhsData(AssignType assign_type, Expression* expr,
RegisterList super_property_args, Register object,
Register key, Expression* object_expr,
const AstRawString* name)
: assign_type_(assign_type),
expr_(expr),
super_property_args_(super_property_args),
object_(object),
key_(key),
object_expr_(object_expr),
name_(name) {}
AssignType assign_type_;
// Different assignment types use different fields:
//
// NON_PROPERTY: expr
// NAMED_PROPERTY: object_expr, object, name
// KEYED_PROPERTY, PRIVATE_METHOD: object, key
// NAMED_SUPER_PROPERTY: super_property_args
// KEYED_SUPER_PROPERT: super_property_args
Expression* expr_;
RegisterList super_property_args_;
Register object_;
Register key_;
Expression* object_expr_;
const AstRawString* name_;
};
void GenerateBytecodeBody();
void GenerateBaseConstructorBody();
void GenerateDerivedConstructorBody();
void GenerateAsyncFunctionBody();
void GenerateAsyncGeneratorFunctionBody();
void GenerateBodyPrologue();
void GenerateBodyStatements(int start = 0);
void GenerateBodyStatementsWithoutImplicitFinalReturn(int start = 0);
template <typename IsolateT>
void AllocateDeferredConstants(IsolateT* isolate, Handle<Script> script);
DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
// Dispatched from VisitBinaryOperation.
void VisitArithmeticExpression(BinaryOperation* binop);
void VisitCommaExpression(BinaryOperation* binop);
void VisitLogicalOrExpression(BinaryOperation* binop);
void VisitLogicalAndExpression(BinaryOperation* binop);
void VisitNullishExpression(BinaryOperation* binop);
// Dispatched from VisitNaryOperation.
void VisitNaryArithmeticExpression(NaryOperation* expr);
void VisitNaryCommaExpression(NaryOperation* expr);
void VisitNaryLogicalOrExpression(NaryOperation* expr);
void VisitNaryLogicalAndExpression(NaryOperation* expr);
void VisitNaryNullishExpression(NaryOperation* expr);
// Dispatched from VisitUnaryOperation.
void VisitVoid(UnaryOperation* expr);
void VisitTypeOf(UnaryOperation* expr);
void VisitNot(UnaryOperation* expr);
void VisitDelete(UnaryOperation* expr);
// Visits a typeof expression for the value on which to perform the typeof.
void VisitForTypeOfValue(Expression* expr);
// Used by flow control routines to evaluate loop condition.
void VisitCondition(Expression* expr);
// Visit the arguments expressions in |args| and store them in |args_regs|,
// growing |args_regs| for each argument visited.
void VisitArguments(const ZonePtrList<Expression>* args,
RegisterList* arg_regs);
// Visit a keyed super property load. The optional
// |opt_receiver_out| register will have the receiver stored to it
// if it's a valid register. The loaded value is placed in the
// accumulator.
void VisitKeyedSuperPropertyLoad(Property* property,
Register opt_receiver_out);
// Visit a named super property load. The optional
// |opt_receiver_out| register will have the receiver stored to it
// if it's a valid register. The loaded value is placed in the
// accumulator.
void VisitNamedSuperPropertyLoad(Property* property,
Register opt_receiver_out);
void VisitPropertyLoad(Register obj, Property* expr);
void VisitPropertyLoadForRegister(Register obj, Property* expr,
Register destination);
AssignmentLhsData PrepareAssignmentLhs(
Expression* lhs, AccumulatorPreservingMode accumulator_preserving_mode =
AccumulatorPreservingMode::kNone);
void BuildAssignment(const AssignmentLhsData& data, Token::Value op,
LookupHoistingMode lookup_hoisting_mode);
void BuildThisVariableLoad();
void BuildDeclareCall(Runtime::FunctionId id);
Expression* GetDestructuringDefaultValue(Expression** target);
void BuildDestructuringArrayAssignment(
ArrayLiteral* pattern, Token::Value op,
LookupHoistingMode lookup_hoisting_mode);
void BuildDestructuringObjectAssignment(
ObjectLiteral* pattern, Token::Value op,
LookupHoistingMode lookup_hoisting_mode);
void BuildLoadNamedProperty(const Expression* object_expr, Register object,
const AstRawString* name);
void BuildSetNamedProperty(const Expression* object_expr, Register object,
const AstRawString* name);
void BuildStoreGlobal(Variable* variable);
void BuildLoadKeyedProperty(Register object, FeedbackSlot slot);
bool IsVariableInRegister(Variable* var, Register reg);
void SetVariableInRegister(Variable* var, Register reg);
Variable* GetPotentialVariableInAccumulator();
void BuildVariableLoad(Variable* variable, HoleCheckMode hole_check_mode,
TypeofMode typeof_mode = TypeofMode::kNotInside);
void BuildVariableLoadForAccumulatorValue(
Variable* variable, HoleCheckMode hole_check_mode,
TypeofMode typeof_mode = TypeofMode::kNotInside);
void BuildVariableAssignment(
Variable* variable, Token::Value op, HoleCheckMode hole_check_mode,
LookupHoistingMode lookup_hoisting_mode = LookupHoistingMode::kNormal);
void BuildLiteralCompareNil(Token::Value compare_op,
BytecodeArrayBuilder::NilValue nil);
void BuildLiteralStrictCompareBoolean(Literal* literal);
void BuildReturn(int source_position);
void BuildAsyncReturn(int source_position);
void BuildAsyncGeneratorReturn();
void BuildReThrow();
void RememberHoleCheckInCurrentBlock(Variable* variable);
bool VariableNeedsHoleCheckInCurrentBlock(Variable* variable,
HoleCheckMode hole_check_mode);
bool VariableNeedsHoleCheckInCurrentBlockForAssignment(
Variable* variable, Token::Value op, HoleCheckMode hole_check_mode);
void BuildHoleCheckForVariableAssignment(Variable* variable, Token::Value op);
void BuildThrowIfHole(Variable* variable);
void BuildNewLocalActivationContext();
void BuildLocalActivationContextInitialization();
void BuildNewLocalBlockContext(Scope* scope);
void BuildNewLocalCatchContext(Scope* scope);
void BuildNewLocalWithContext(Scope* scope);
void BuildGeneratorPrologue();
void BuildSuspendPoint(int position);
void BuildAwait(int position = kNoSourcePosition);
void BuildAwait(Expression* await_expr);
void BuildFinalizeIteration(IteratorRecord iterator, Register done,
Register iteration_continuation_token);
void BuildGetIterator(IteratorType hint);
// Create an IteratorRecord with pre-allocated registers holding the next
// method and iterator object.
IteratorRecord BuildGetIteratorRecord(Register iterator_next,
Register iterator_object,
IteratorType hint);
// Create an IteratorRecord allocating new registers to hold the next method
// and iterator object.
IteratorRecord BuildGetIteratorRecord(IteratorType hint);
void BuildIteratorNext(const IteratorRecord& iterator, Register next_result);
void BuildIteratorClose(const IteratorRecord& iterator,
Expression* expr = nullptr);
void BuildCallIteratorMethod(Register iterator, const AstRawString* method,
RegisterList receiver_and_args,
BytecodeLabel* if_called,
BytecodeLabels* if_notcalled);
void BuildFillArrayWithIterator(IteratorRecord iterator, Register array,
Register index, Register value,
FeedbackSlot next_value_slot,
FeedbackSlot next_done_slot,
FeedbackSlot index_slot,
FeedbackSlot element_slot);
// Create Array literals. |expr| can be nullptr, but if provided,
// a boilerplate will be used to create an initial array for elements
// before the first spread.
void BuildCreateArrayLiteral(const ZonePtrList<Expression>* elements,
ArrayLiteral* expr);
void BuildCreateObjectLiteral(Register literal, uint8_t flags, size_t entry);
void AllocateTopLevelRegisters();
void VisitArgumentsObject(Variable* variable);
void VisitRestArgumentsArray(Variable* rest);
void VisitCallSuper(Call* call);
void BuildInstanceInitializationAfterSuperCall(Register this_function,
Register instance);
void BuildInvalidPropertyAccess(MessageTemplate tmpl, Property* property);
void BuildPrivateBrandCheck(Property* property, Register object);
void BuildPrivateMethodIn(Variable* private_name,
Expression* object_expression);
void BuildPrivateGetterAccess(Register obj, Register access_pair);
void BuildPrivateSetterAccess(Register obj, Register access_pair,
Register value);
void BuildPrivateDebugDynamicGet(Property* property, Register obj);
void BuildPrivateDebugDynamicSet(Property* property, Register obj,
Register value);
void BuildPrivateMethods(ClassLiteral* expr, bool is_static,
Register home_object);
void BuildClassProperty(ClassLiteral::Property* property);
void BuildClassLiteral(ClassLiteral* expr, Register name);
void VisitClassLiteral(ClassLiteral* expr, Register name);
void VisitNewTargetVariable(Variable* variable);
void VisitThisFunctionVariable(Variable* variable);
void BuildPrivateBrandInitialization(Register receiver, Variable* brand);
void BuildInstanceMemberInitialization(Register constructor,
Register instance);
void BuildGeneratorObjectVariableInitialization();
void VisitBlockDeclarationsAndStatements(Block* stmt);
void VisitBlockMaybeDispose(Block* stmt);
void VisitLiteralAccessor(LiteralProperty* property, Register value_out);
void VisitForInAssignment(Expression* expr);
void VisitModuleNamespaceImports();
// Visit a logical OR/AND within a test context, rewiring the jumps based
// on the expression values.
void VisitLogicalTest(Token::Value token, Expression* left, Expression* right,
int right_coverage_slot);
void VisitNaryLogicalTest(Token::Value token, NaryOperation* expr,
const NaryCodeCoverageSlots* coverage_slots);
// Visit a (non-RHS) test for a logical op, which falls through if the test
// fails or jumps to the appropriate labels if it succeeds.
void VisitLogicalTestSubExpression(Token::Value token, Expression* expr,
BytecodeLabels* then_labels,
BytecodeLabels* else_labels,
int coverage_slot);
// Helpers for binary and nary logical op value expressions.
bool VisitLogicalOrSubExpression(Expression* expr, BytecodeLabels* end_labels,
int coverage_slot);
bool VisitLogicalAndSubExpression(Expression* expr,
BytecodeLabels* end_labels,
int coverage_slot);
// Helper for binary and nary nullish op value expressions.
bool VisitNullishSubExpression(Expression* expr, BytecodeLabels* end_labels,
int coverage_slot);
// Visit the body of a loop iteration.
void VisitIterationBody(IterationStatement* stmt, LoopBuilder* loop_builder);
// Visit a statement and switch scopes, the context is in the accumulator.
void VisitInScope(Statement* stmt, Scope* scope);
void BuildPushUndefinedIntoRegisterList(RegisterList* reg_list);
void BuildLoadPropertyKey(LiteralProperty* property, Register out_reg);
int AllocateBlockCoverageSlotIfEnabled(AstNode* node, SourceRangeKind kind);
int AllocateNaryBlockCoverageSlotIfEnabled(NaryOperation* node, size_t index);
int AllocateConditionalChainBlockCoverageSlotIfEnabled(ConditionalChain* node,
SourceRangeKind kind,
size_t index);
void BuildIncrementBlockCoverageCounterIfEnabled(AstNode* node,
SourceRangeKind kind);
void BuildIncrementBlockCoverageCounterIfEnabled(int coverage_array_slot);
void BuildTest(ToBooleanMode mode, BytecodeLabels* then_labels,
BytecodeLabels* else_labels, TestFallthrough fallthrough);
template <typename TryBodyFunc, typename CatchBodyFunc>
void BuildTryCatch(TryBodyFunc try_body_func, CatchBodyFunc catch_body_func,
HandlerTable::CatchPrediction catch_prediction,
TryCatchStatement* stmt_for_coverage = nullptr);
template <typename TryBodyFunc, typename FinallyBodyFunc>
void BuildTryFinally(TryBodyFunc try_body_func,
FinallyBodyFunc finally_body_func,
HandlerTable::CatchPrediction catch_prediction,
TryFinallyStatement* stmt_for_coverage = nullptr);
template <typename WrappedFunc>
void BuildDisposeScope(WrappedFunc wrapped_func, bool has_await_using);
template <typename ExpressionFunc>
void BuildOptionalChain(ExpressionFunc expression_func);
void BuildGetAndCheckSuperConstructor(Register this_function,
Register new_target,
Register constructor,
BytecodeLabel* super_ctor_call_done);
void BuildSuperCallOptimization(Register this_function, Register new_target,
Register constructor_then_instance,
BytecodeLabel* super_ctor_call_done);
// Visitors for obtaining expression result in the accumulator, in a
// register, or just getting the effect. Some visitors return a TypeHint which
// specifies the type of the result of the visited expression.
TypeHint VisitForAccumulatorValue(Expression* expr);
void VisitForAccumulatorValueOrTheHole(Expression* expr);
V8_WARN_UNUSED_RESULT Register VisitForRegisterValue(Expression* expr);
V8_INLINE void VisitForRegisterValue(Expression* expr, Register destination);
void VisitAndPushIntoRegisterList(Expression* expr, RegisterList* reg_list);
void VisitForEffect(Expression* expr);
void VisitForTest(Expression* expr, BytecodeLabels* then_labels,
BytecodeLabels* else_labels, TestFallthrough fallthrough);
void VisitForNullishTest(Expression* expr, BytecodeLabels* then_labels,
BytecodeLabels* test_next_labels,
BytecodeLabels* else_labels);
// Convenience visitors that put a HoleCheckElisionScope on stack.
template <typename T>
void VisitInHoleCheckElisionScope(T* node);
void VisitIterationBodyInHoleCheckElisionScope(IterationStatement* stmt,
LoopBuilder* loop_builder);
TypeHint VisitInHoleCheckElisionScopeForAccumulatorValue(Expression* expr);
void VisitInSameTestExecutionScope(Expression* expr);
Register GetRegisterForLocalVariable(Variable* variable);
bool IsLocalVariableWithInternalizedStringHint(Expression* expr);
TypeHint GetTypeHintForLocalVariable(Variable* variable);
// Returns the runtime function id for a store to super for the function's
// language mode.
inline Runtime::FunctionId StoreToSuperRuntimeId();
inline Runtime::FunctionId StoreKeyedToSuperRuntimeId();
// Returns a cached slot, or create and cache a new slot if one doesn't
// already exists.
FeedbackSlot GetCachedLoadGlobalICSlot(TypeofMode typeof_mode,
Variable* variable);
FeedbackSlot GetCachedStoreGlobalICSlot(LanguageMode language_mode,
Variable* variable);
FeedbackSlot GetCachedLoadICSlot(const Expression* expr,
const AstRawString* name);
FeedbackSlot GetCachedLoadSuperICSlot(const AstRawString* name);
FeedbackSlot GetCachedStoreICSlot(const Expression* expr,
const AstRawString* name);
FeedbackSlot GetDummyCompareICSlot();
int GetCachedCreateClosureSlot(FunctionLiteral* literal);
void AddToEagerLiteralsIfEager(FunctionLiteral* literal);
static constexpr ToBooleanMode ToBooleanModeFromTypeHint(TypeHint type_hint) {
return type_hint == TypeHint::kBoolean ? ToBooleanMode::kAlreadyBoolean
: ToBooleanMode::kConvertToBoolean;
}
inline Register incoming_new_target() const;
inline Register generator_object() const;
inline BytecodeArrayBuilder* builder() { return &builder_; }
inline Zone* zone() const { return zone_; }
inline DeclarationScope* closure_scope() const { return closure_scope_; }
inline UnoptimizedCompilationInfo* info() const { return info_; }
inline const AstStringConstants* ast_string_constants() const {
return ast_string_constants_;
}
inline Scope* current_scope() const { return current_scope_; }
inline void set_current_scope(Scope* scope) { current_scope_ = scope; }
inline ControlScope* execution_control() const { return execution_control_; }
inline void set_execution_control(ControlScope* scope) {
execution_control_ = scope;
}
inline ContextScope* execution_context() const { return execution_context_; }
inline void set_execution_context(ContextScope* context) {
execution_context_ = context;
}
inline void set_execution_result(ExpressionResultScope* execution_result) {
execution_result_ = execution_result;
}
ExpressionResultScope* execution_result() const { return execution_result_; }
BytecodeRegisterAllocator* register_allocator() {
return builder()->register_allocator();
}
TopLevelDeclarationsBuilder* top_level_builder() {
DCHECK_NOT_NULL(top_level_builder_);
return top_level_builder_;
}
inline LanguageMode language_mode() const;
inline FunctionKind function_kind() const;
inline FeedbackVectorSpec* feedback_spec();
inline int feedback_index(FeedbackSlot slot) const;
inline FeedbackSlotCache* feedback_slot_cache() {
return feedback_slot_cache_;
}
inline HandlerTable::CatchPrediction catch_prediction() const {
return catch_prediction_;
}
inline void set_catch_prediction(HandlerTable::CatchPrediction value) {
catch_prediction_ = value;
}
LoopScope* current_loop_scope() const { return current_loop_scope_; }
void set_current_loop_scope(LoopScope* loop_scope) {
current_loop_scope_ = loop_scope;
}
inline ForInScope* current_for_in_scope() const {
return current_for_in_scope_;
}
inline void set_current_for_in_scope(ForInScope* for_in_scope) {
current_for_in_scope_ = for_in_scope;
}
Register current_disposables_stack() const {
SBXCHECK(current_disposables_stack_.is_valid());
return current_disposables_stack_;
}
void set_current_disposables_stack(Register disposables_stack) {
current_disposables_stack_ = disposables_stack;
}
LocalIsolate* local_isolate_;
Zone* zone_;
BytecodeArrayBuilder builder_;
UnoptimizedCompilationInfo* info_;
const AstStringConstants* ast_string_constants_;
DeclarationScope* closure_scope_;
Scope* current_scope_;
// External vector of literals to be eagerly compiled.
std::vector<FunctionLiteral*>* eager_inner_literals_;
Handle<Script> script_;
FeedbackSlotCache* feedback_slot_cache_;
TopLevelDeclarationsBuilder* top_level_builder_;
BlockCoverageBuilder* block_coverage_builder_;
ZoneVector<std::pair<FunctionLiteral*, size_t>> function_literals_;
ZoneVector<std::pair<NativeFunctionLiteral*, size_t>>
native_function_literals_;
ZoneVector<std::pair<ObjectLiteralBoilerplateBuilder*, size_t>>
object_literals_;
ZoneVector<std::pair<ArrayLiteralBoilerplateBuilder*, size_t>>
array_literals_;
ZoneVector<std::pair<ClassLiteral*, size_t>> class_literals_;
ZoneVector<std::pair<GetTemplateObject*, size_t>> template_objects_;
ZoneVector<Variable*> vars_in_hole_check_bitmap_;
ZoneVector<std::pair<Call*, Scope*>> eval_calls_;
ControlScope* execution_control_;
ContextScope* execution_context_;
ExpressionResultScope* execution_result_;
Register incoming_new_target_or_generator_;
Register current_disposables_stack_;
BytecodeLabels* optional_chaining_null_labels_;
// Dummy feedback slot for compare operations, where we don't care about
// feedback
SharedFeedbackSlot dummy_feedback_slot_;
BytecodeJumpTable* generator_jump_table_;
int suspend_count_;
// TODO(solanes): assess if we can move loop_depth_ into LoopScope.
int loop_depth_;
// Variables for which hole checks have been emitted in the current basic
// block. Managed by HoleCheckElisionScope and HoleCheckElisionMergeScope.
Variable::HoleCheckBitmap hole_check_bitmap_;
LoopScope* current_loop_scope_;
ForInScope* current_for_in_scope_;
HandlerTable::CatchPrediction catch_prediction_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_GENERATOR_H_

View File

@ -0,0 +1,88 @@
// Copyright 2017 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_INTERPRETER_BYTECODE_JUMP_TABLE_H_
#define V8_INTERPRETER_BYTECODE_JUMP_TABLE_H_
#include "src/utils/bit-vector.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace interpreter {
class ConstantArrayBuilder;
// A jump table for a set of targets in a bytecode array. When an entry in the
// table is bound, it represents a known position in the bytecode array. If no
// entries match, the switch falls through.
class V8_EXPORT_PRIVATE BytecodeJumpTable final : public ZoneObject {
public:
// Constructs a new BytecodeJumpTable starting at |constant_pool_index|, with
// the given |size|, where the case values of the table start at
// |case_value_base|.
BytecodeJumpTable(size_t constant_pool_index, int size, int case_value_base,
Zone* zone)
:
#ifdef DEBUG
bound_(size, zone),
#endif
constant_pool_index_(constant_pool_index),
switch_bytecode_offset_(kInvalidOffset),
size_(size),
case_value_base_(case_value_base) {
}
size_t constant_pool_index() const { return constant_pool_index_; }
size_t switch_bytecode_offset() const { return switch_bytecode_offset_; }
int case_value_base() const { return case_value_base_; }
int size() const { return size_; }
#ifdef DEBUG
bool is_bound(int case_value) const {
DCHECK_GE(case_value, case_value_base_);
DCHECK_LT(case_value, case_value_base_ + size());
return bound_.Contains(case_value - case_value_base_);
}
#endif
size_t ConstantPoolEntryFor(int case_value) {
DCHECK_GE(case_value, case_value_base_);
return constant_pool_index_ + case_value - case_value_base_;
}
private:
static const size_t kInvalidIndex = static_cast<size_t>(-1);
static const size_t kInvalidOffset = static_cast<size_t>(-1);
void mark_bound(int case_value) {
#ifdef DEBUG
DCHECK_GE(case_value, case_value_base_);
DCHECK_LT(case_value, case_value_base_ + size());
bound_.Add(case_value - case_value_base_);
#endif
}
void set_switch_bytecode_offset(size_t offset) {
DCHECK_EQ(switch_bytecode_offset_, kInvalidOffset);
switch_bytecode_offset_ = offset;
}
#ifdef DEBUG
// This bit vector is only used for DCHECKS, so only store the field in debug
// builds.
BitVector bound_;
#endif
size_t constant_pool_index_;
size_t switch_bytecode_offset_;
int size_;
int case_value_base_;
friend class BytecodeArrayWriter;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_JUMP_TABLE_H_

View File

@ -0,0 +1,30 @@
// Copyright 2016 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.
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
namespace interpreter {
BytecodeLabel* BytecodeLabels::New() {
DCHECK(!is_bound());
labels_.emplace_back(BytecodeLabel());
return &labels_.back();
}
void BytecodeLabels::Bind(BytecodeArrayBuilder* builder) {
DCHECK(!is_bound_);
is_bound_ = true;
for (auto& label : labels_) {
builder->Bind(&label);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

114
deps/v8/src/interpreter/bytecode-label.h vendored Normal file
View File

@ -0,0 +1,114 @@
// Copyright 2015 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_INTERPRETER_BYTECODE_LABEL_H_
#define V8_INTERPRETER_BYTECODE_LABEL_H_
#include <algorithm>
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
namespace interpreter {
class BytecodeArrayBuilder;
// A label representing a loop header in a bytecode array. It is bound before
// the jump is seen, so its position is always known by the time the jump is
// reached.
class V8_EXPORT_PRIVATE BytecodeLoopHeader final {
public:
BytecodeLoopHeader() : offset_(kInvalidOffset) {}
size_t offset() const {
DCHECK_NE(offset_, kInvalidOffset);
return offset_;
}
private:
static const size_t kInvalidOffset = static_cast<size_t>(-1);
void bind_to(size_t offset) {
DCHECK_NE(offset, kInvalidOffset);
DCHECK_EQ(offset_, kInvalidOffset);
offset_ = offset;
}
// The bytecode offset of the loop header.
size_t offset_;
friend class BytecodeArrayWriter;
};
// A label representing a forward branch target in a bytecode array. When a
// label is bound, it represents a known position in the bytecode array. A label
// can only have at most one referrer jump.
class V8_EXPORT_PRIVATE BytecodeLabel final {
public:
BytecodeLabel() : bound_(false), jump_offset_(kInvalidOffset) {}
bool is_bound() const { return bound_; }
size_t jump_offset() const {
DCHECK_NE(jump_offset_, kInvalidOffset);
return jump_offset_;
}
bool has_referrer_jump() const { return jump_offset_ != kInvalidOffset; }
private:
static const size_t kInvalidOffset = static_cast<size_t>(-1);
void bind() {
DCHECK(!bound_);
bound_ = true;
}
void set_referrer(size_t offset) {
DCHECK(!bound_);
DCHECK_NE(offset, kInvalidOffset);
DCHECK_EQ(jump_offset_, kInvalidOffset);
jump_offset_ = offset;
}
// Set when the label is bound (i.e. the start of the target basic block).
bool bound_;
// Set when the jump referrer is set (i.e. the location of the jump).
size_t jump_offset_;
friend class BytecodeArrayWriter;
};
// Class representing a branch target of multiple jumps.
class V8_EXPORT_PRIVATE BytecodeLabels {
public:
explicit BytecodeLabels(Zone* zone) : labels_(zone), is_bound_(false) {}
BytecodeLabels(const BytecodeLabels&) = delete;
BytecodeLabels& operator=(const BytecodeLabels&) = delete;
BytecodeLabel* New();
void Bind(BytecodeArrayBuilder* builder);
bool is_bound() const {
DCHECK_IMPLIES(
is_bound_,
std::all_of(labels_.begin(), labels_.end(), [](const BytecodeLabel& l) {
return !l.has_referrer_jump() || l.is_bound();
}));
return is_bound_;
}
bool empty() const { return labels_.empty(); }
private:
ZoneLinkedList<BytecodeLabel> labels_;
bool is_bound_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_LABEL_H_

View File

@ -0,0 +1,56 @@
// Copyright 2017 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.
#include "src/interpreter/bytecode-node.h"
#include <iomanip>
namespace v8 {
namespace internal {
namespace interpreter {
void BytecodeNode::Print(std::ostream& os) const {
#ifdef DEBUG
std::ios saved_state(nullptr);
saved_state.copyfmt(os);
os << Bytecodes::ToString(bytecode_);
for (int i = 0; i < operand_count(); ++i) {
os << ' ' << std::setw(8) << std::setfill('0') << std::hex << operands_[i];
}
os.copyfmt(saved_state);
if (source_info_.is_valid()) {
os << ' ' << source_info_;
}
os << '\n';
#else
os << static_cast<const void*>(this);
#endif // DEBUG
}
bool BytecodeNode::operator==(const BytecodeNode& other) const {
if (this == &other) {
return true;
} else if (this->bytecode() != other.bytecode() ||
this->source_info() != other.source_info()) {
return false;
} else {
for (int i = 0; i < this->operand_count(); ++i) {
if (this->operand(i) != other.operand(i)) {
return false;
}
}
}
return true;
}
std::ostream& operator<<(std::ostream& os, const BytecodeNode& node) {
node.Print(os);
return os;
}
} // namespace interpreter
} // namespace internal
} // namespace v8

277
deps/v8/src/interpreter/bytecode-node.h vendored Normal file
View File

@ -0,0 +1,277 @@
// Copyright 2017 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_INTERPRETER_BYTECODE_NODE_H_
#define V8_INTERPRETER_BYTECODE_NODE_H_
#include <algorithm>
#include "src/interpreter/bytecode-source-info.h"
#include "src/interpreter/bytecodes.h"
namespace v8 {
namespace internal {
namespace interpreter {
// A container for a generated bytecode, it's operands, and source information.
class V8_EXPORT_PRIVATE BytecodeNode final {
public:
V8_INLINE BytecodeNode(Bytecode bytecode,
BytecodeSourceInfo source_info = BytecodeSourceInfo())
: bytecode_(bytecode),
operand_count_(0),
operand_scale_(OperandScale::kSingle),
source_info_(source_info) {
DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count());
}
V8_INLINE BytecodeNode(Bytecode bytecode, uint32_t operand0,
BytecodeSourceInfo source_info = BytecodeSourceInfo())
: bytecode_(bytecode),
operand_count_(1),
operand_scale_(OperandScale::kSingle),
source_info_(source_info) {
DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count());
SetOperand(0, operand0);
}
V8_INLINE BytecodeNode(Bytecode bytecode, uint32_t operand0,
uint32_t operand1,
BytecodeSourceInfo source_info = BytecodeSourceInfo())
: bytecode_(bytecode),
operand_count_(2),
operand_scale_(OperandScale::kSingle),
source_info_(source_info) {
DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count());
SetOperand(0, operand0);
SetOperand(1, operand1);
}
V8_INLINE BytecodeNode(Bytecode bytecode, uint32_t operand0,
uint32_t operand1, uint32_t operand2,
BytecodeSourceInfo source_info = BytecodeSourceInfo())
: bytecode_(bytecode),
operand_count_(3),
operand_scale_(OperandScale::kSingle),
source_info_(source_info) {
DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count());
SetOperand(0, operand0);
SetOperand(1, operand1);
SetOperand(2, operand2);
}
V8_INLINE BytecodeNode(Bytecode bytecode, uint32_t operand0,
uint32_t operand1, uint32_t operand2,
uint32_t operand3,
BytecodeSourceInfo source_info = BytecodeSourceInfo())
: bytecode_(bytecode),
operand_count_(4),
operand_scale_(OperandScale::kSingle),
source_info_(source_info) {
DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count());
SetOperand(0, operand0);
SetOperand(1, operand1);
SetOperand(2, operand2);
SetOperand(3, operand3);
}
V8_INLINE BytecodeNode(Bytecode bytecode, uint32_t operand0,
uint32_t operand1, uint32_t operand2,
uint32_t operand3, uint32_t operand4,
BytecodeSourceInfo source_info = BytecodeSourceInfo())
: bytecode_(bytecode),
operand_count_(5),
operand_scale_(OperandScale::kSingle),
source_info_(source_info) {
DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count());
SetOperand(0, operand0);
SetOperand(1, operand1);
SetOperand(2, operand2);
SetOperand(3, operand3);
SetOperand(4, operand4);
}
#define DEFINE_BYTECODE_NODE_CREATOR(Name, ...) \
template <typename... Operands> \
V8_INLINE static BytecodeNode Name(BytecodeSourceInfo source_info, \
Operands... operands) { \
return Create<Bytecode::k##Name, __VA_ARGS__>(source_info, operands...); \
}
BYTECODE_LIST(DEFINE_BYTECODE_NODE_CREATOR, DEFINE_BYTECODE_NODE_CREATOR)
#undef DEFINE_BYTECODE_NODE_CREATOR
// Print to stream |os|.
void Print(std::ostream& os) const;
Bytecode bytecode() const { return bytecode_; }
uint32_t operand(int i) const {
DCHECK_LT(i, operand_count());
return operands_[i];
}
const uint32_t* operands() const { return operands_; }
void update_operand0(uint32_t operand0) { SetOperand(0, operand0); }
int operand_count() const { return operand_count_; }
OperandScale operand_scale() const { return operand_scale_; }
const BytecodeSourceInfo& source_info() const { return source_info_; }
void set_source_info(BytecodeSourceInfo source_info) {
source_info_ = source_info;
}
bool operator==(const BytecodeNode& other) const;
bool operator!=(const BytecodeNode& other) const { return !(*this == other); }
private:
template <Bytecode bytecode, ImplicitRegisterUse implicit_register_use,
OperandType... operand_types>
friend class BytecodeNodeBuilder;
V8_INLINE BytecodeNode(Bytecode bytecode, int operand_count,
OperandScale operand_scale,
BytecodeSourceInfo source_info, uint32_t operand0 = 0,
uint32_t operand1 = 0, uint32_t operand2 = 0,
uint32_t operand3 = 0, uint32_t operand4 = 0)
: bytecode_(bytecode),
operand_count_(operand_count),
operand_scale_(operand_scale),
source_info_(source_info) {
DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count);
operands_[0] = operand0;
operands_[1] = operand1;
operands_[2] = operand2;
operands_[3] = operand3;
operands_[4] = operand4;
}
template <Bytecode bytecode, ImplicitRegisterUse accum_use>
V8_INLINE static BytecodeNode Create(BytecodeSourceInfo source_info) {
return BytecodeNode(bytecode, 0, OperandScale::kSingle, source_info);
}
template <Bytecode bytecode, ImplicitRegisterUse accum_use,
OperandType operand0_type>
V8_INLINE static BytecodeNode Create(BytecodeSourceInfo source_info,
uint32_t operand0) {
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type);
OperandScale scale = OperandScale::kSingle;
scale = std::max(scale, ScaleForOperand<operand0_type>(operand0));
return BytecodeNode(bytecode, 1, scale, source_info, operand0);
}
template <Bytecode bytecode, ImplicitRegisterUse accum_use,
OperandType operand0_type, OperandType operand1_type>
V8_INLINE static BytecodeNode Create(BytecodeSourceInfo source_info,
uint32_t operand0, uint32_t operand1) {
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type);
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 1), operand1_type);
OperandScale scale = OperandScale::kSingle;
scale = std::max(scale, ScaleForOperand<operand0_type>(operand0));
scale = std::max(scale, ScaleForOperand<operand1_type>(operand1));
return BytecodeNode(bytecode, 2, scale, source_info, operand0, operand1);
}
template <Bytecode bytecode, ImplicitRegisterUse accum_use,
OperandType operand0_type, OperandType operand1_type,
OperandType operand2_type>
V8_INLINE static BytecodeNode Create(BytecodeSourceInfo source_info,
uint32_t operand0, uint32_t operand1,
uint32_t operand2) {
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type);
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 1), operand1_type);
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 2), operand2_type);
OperandScale scale = OperandScale::kSingle;
scale = std::max(scale, ScaleForOperand<operand0_type>(operand0));
scale = std::max(scale, ScaleForOperand<operand1_type>(operand1));
scale = std::max(scale, ScaleForOperand<operand2_type>(operand2));
return BytecodeNode(bytecode, 3, scale, source_info, operand0, operand1,
operand2);
}
template <Bytecode bytecode, ImplicitRegisterUse accum_use,
OperandType operand0_type, OperandType operand1_type,
OperandType operand2_type, OperandType operand3_type>
V8_INLINE static BytecodeNode Create(BytecodeSourceInfo source_info,
uint32_t operand0, uint32_t operand1,
uint32_t operand2, uint32_t operand3) {
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type);
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 1), operand1_type);
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 2), operand2_type);
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 3), operand3_type);
OperandScale scale = OperandScale::kSingle;
scale = std::max(scale, ScaleForOperand<operand0_type>(operand0));
scale = std::max(scale, ScaleForOperand<operand1_type>(operand1));
scale = std::max(scale, ScaleForOperand<operand2_type>(operand2));
scale = std::max(scale, ScaleForOperand<operand3_type>(operand3));
return BytecodeNode(bytecode, 4, scale, source_info, operand0, operand1,
operand2, operand3);
}
template <Bytecode bytecode, ImplicitRegisterUse accum_use,
OperandType operand0_type, OperandType operand1_type,
OperandType operand2_type, OperandType operand3_type,
OperandType operand4_type>
V8_INLINE static BytecodeNode Create(BytecodeSourceInfo source_info,
uint32_t operand0, uint32_t operand1,
uint32_t operand2, uint32_t operand3,
uint32_t operand4) {
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type);
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 1), operand1_type);
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 2), operand2_type);
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 3), operand3_type);
DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 4), operand4_type);
OperandScale scale = OperandScale::kSingle;
scale = std::max(scale, ScaleForOperand<operand0_type>(operand0));
scale = std::max(scale, ScaleForOperand<operand1_type>(operand1));
scale = std::max(scale, ScaleForOperand<operand2_type>(operand2));
scale = std::max(scale, ScaleForOperand<operand3_type>(operand3));
scale = std::max(scale, ScaleForOperand<operand4_type>(operand4));
return BytecodeNode(bytecode, 5, scale, source_info, operand0, operand1,
operand2, operand3, operand4);
}
template <OperandType operand_type>
V8_INLINE static OperandScale ScaleForOperand(uint32_t operand) {
if (BytecodeOperands::IsScalableUnsignedByte(operand_type)) {
return Bytecodes::ScaleForUnsignedOperand(operand);
} else if (BytecodeOperands::IsScalableSignedByte(operand_type)) {
return Bytecodes::ScaleForSignedOperand(operand);
} else {
return OperandScale::kSingle;
}
}
V8_INLINE void UpdateScaleForOperand(int operand_index, uint32_t operand) {
if (Bytecodes::OperandIsScalableSignedByte(bytecode(), operand_index)) {
operand_scale_ =
std::max(operand_scale_, Bytecodes::ScaleForSignedOperand(operand));
} else if (Bytecodes::OperandIsScalableUnsignedByte(bytecode(),
operand_index)) {
operand_scale_ =
std::max(operand_scale_, Bytecodes::ScaleForUnsignedOperand(operand));
}
}
V8_INLINE void SetOperand(int operand_index, uint32_t operand) {
operands_[operand_index] = operand;
UpdateScaleForOperand(operand_index, operand);
}
Bytecode bytecode_;
uint32_t operands_[Bytecodes::kMaxOperands];
int operand_count_;
OperandScale operand_scale_;
BytecodeSourceInfo source_info_;
};
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
const BytecodeNode& node);
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_NODE_H_

View File

@ -0,0 +1,94 @@
// Copyright 2016 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.
#include "src/interpreter/bytecode-operands.h"
#include <iomanip>
namespace v8 {
namespace internal {
namespace interpreter {
namespace {
const char* ImplicitRegisterUseToString(
ImplicitRegisterUse implicit_register_use) {
switch (implicit_register_use) {
case ImplicitRegisterUse::kNone:
return "None";
case ImplicitRegisterUse::kReadAccumulator:
return "ReadAccumulator";
case ImplicitRegisterUse::kWriteAccumulator:
return "WriteAccumulator";
case ImplicitRegisterUse::kClobberAccumulator:
return "ClobberAccumulator";
case ImplicitRegisterUse::kWriteShortStar:
return "WriteShortStar";
case ImplicitRegisterUse::kReadAndClobberAccumulator:
return "ReadAndClobberAccumulator";
case ImplicitRegisterUse::kReadWriteAccumulator:
return "ReadWriteAccumulator";
case ImplicitRegisterUse::kReadAccumulatorWriteShortStar:
return "ReadAccumulatorWriteShortStar";
}
UNREACHABLE();
}
const char* OperandTypeToString(OperandType operand_type) {
switch (operand_type) {
#define CASE(Name, _) \
case OperandType::k##Name: \
return #Name;
OPERAND_TYPE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
}
const char* OperandScaleToString(OperandScale operand_scale) {
switch (operand_scale) {
#define CASE(Name, _) \
case OperandScale::k##Name: \
return #Name;
OPERAND_SCALE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
}
const char* OperandSizeToString(OperandSize operand_size) {
switch (operand_size) {
case OperandSize::kNone:
return "None";
case OperandSize::kByte:
return "Byte";
case OperandSize::kShort:
return "Short";
case OperandSize::kQuad:
return "Quad";
}
UNREACHABLE();
}
} // namespace
std::ostream& operator<<(std::ostream& os, const ImplicitRegisterUse& use) {
return os << ImplicitRegisterUseToString(use);
}
std::ostream& operator<<(std::ostream& os, const OperandSize& operand_size) {
return os << OperandSizeToString(operand_size);
}
std::ostream& operator<<(std::ostream& os, const OperandScale& operand_scale) {
return os << OperandScaleToString(operand_scale);
}
std::ostream& operator<<(std::ostream& os, const OperandType& operand_type) {
return os << OperandTypeToString(operand_type);
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,236 @@
// Copyright 2016 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_INTERPRETER_BYTECODE_OPERANDS_H_
#define V8_INTERPRETER_BYTECODE_OPERANDS_H_
#include "src/base/bounds.h"
#include "src/common/globals.h"
namespace v8 {
namespace internal {
namespace interpreter {
#define INVALID_OPERAND_TYPE_LIST(V) V(None, OperandTypeInfo::kNone)
#define REGISTER_INPUT_OPERAND_TYPE_LIST(V) \
V(Reg, OperandTypeInfo::kScalableSignedByte) \
V(RegList, OperandTypeInfo::kScalableSignedByte) \
V(RegPair, OperandTypeInfo::kScalableSignedByte)
#define REGISTER_OUTPUT_OPERAND_TYPE_LIST(V) \
V(RegOut, OperandTypeInfo::kScalableSignedByte) \
V(RegOutList, OperandTypeInfo::kScalableSignedByte) \
V(RegOutPair, OperandTypeInfo::kScalableSignedByte) \
V(RegOutTriple, OperandTypeInfo::kScalableSignedByte)
#define SIGNED_SCALABLE_SCALAR_OPERAND_TYPE_LIST(V) \
V(Imm, OperandTypeInfo::kScalableSignedByte)
#define UNSIGNED_SCALABLE_SCALAR_OPERAND_TYPE_LIST(V) \
V(Idx, OperandTypeInfo::kScalableUnsignedByte) \
V(UImm, OperandTypeInfo::kScalableUnsignedByte) \
V(RegCount, OperandTypeInfo::kScalableUnsignedByte)
#define UNSIGNED_FIXED_SCALAR_OPERAND_TYPE_LIST(V) \
V(Flag8, OperandTypeInfo::kFixedUnsignedByte) \
V(Flag16, OperandTypeInfo::kFixedUnsignedShort) \
V(IntrinsicId, OperandTypeInfo::kFixedUnsignedByte) \
V(RuntimeId, OperandTypeInfo::kFixedUnsignedShort) \
V(NativeContextIndex, OperandTypeInfo::kFixedUnsignedByte)
// Carefully ordered for operand type range checks below.
#define NON_REGISTER_OPERAND_TYPE_LIST(V) \
INVALID_OPERAND_TYPE_LIST(V) \
UNSIGNED_FIXED_SCALAR_OPERAND_TYPE_LIST(V) \
UNSIGNED_SCALABLE_SCALAR_OPERAND_TYPE_LIST(V) \
SIGNED_SCALABLE_SCALAR_OPERAND_TYPE_LIST(V)
// Carefully ordered for operand type range checks below.
#define REGISTER_OPERAND_TYPE_LIST(V) \
REGISTER_INPUT_OPERAND_TYPE_LIST(V) \
REGISTER_OUTPUT_OPERAND_TYPE_LIST(V) \
V(RegInOut, OperandTypeInfo::kScalableSignedByte)
// The list of operand types used by bytecodes.
// Carefully ordered for operand type range checks below.
#define OPERAND_TYPE_LIST(V) \
NON_REGISTER_OPERAND_TYPE_LIST(V) \
REGISTER_OPERAND_TYPE_LIST(V)
// Enumeration of scaling factors applicable to scalable operands. Code
// relies on being able to cast values to integer scaling values.
#define OPERAND_SCALE_LIST(V) \
V(Single, 1) \
V(Double, 2) \
V(Quadruple, 4)
enum class OperandScale : uint8_t {
#define DECLARE_OPERAND_SCALE(Name, Scale) k##Name = Scale,
OPERAND_SCALE_LIST(DECLARE_OPERAND_SCALE)
#undef DECLARE_OPERAND_SCALE
kLast = kQuadruple
};
// Enumeration of the size classes of operand types used by
// bytecodes. Code relies on being able to cast values to integer
// types to get the size in bytes.
enum class OperandSize : uint8_t {
kNone = 0,
kByte = 1,
kShort = 2,
kQuad = 4,
kLast = kQuad
};
// Primitive operand info used that summarize properties of operands.
// Columns are Name, IsScalable, IsUnsigned, UnscaledSize.
#define OPERAND_TYPE_INFO_LIST(V) \
V(None, false, false, OperandSize::kNone) \
V(ScalableSignedByte, true, false, OperandSize::kByte) \
V(ScalableUnsignedByte, true, true, OperandSize::kByte) \
V(FixedUnsignedByte, false, true, OperandSize::kByte) \
V(FixedUnsignedShort, false, true, OperandSize::kShort)
enum class OperandTypeInfo : uint8_t {
#define DECLARE_OPERAND_TYPE_INFO(Name, ...) k##Name,
OPERAND_TYPE_INFO_LIST(DECLARE_OPERAND_TYPE_INFO)
#undef DECLARE_OPERAND_TYPE_INFO
};
// Enumeration of operand types used by bytecodes.
enum class OperandType : uint8_t {
#define DECLARE_OPERAND_TYPE(Name, _) k##Name,
OPERAND_TYPE_LIST(DECLARE_OPERAND_TYPE)
#undef DECLARE_OPERAND_TYPE
#define COUNT_OPERAND_TYPES(x, _) +1
// The COUNT_OPERAND macro will turn this into kLast = -1 +1 +1... which will
// evaluate to the same value as the last operand.
kLast = -1 OPERAND_TYPE_LIST(COUNT_OPERAND_TYPES)
#undef COUNT_OPERAND_TYPES
};
enum class ImplicitRegisterUse : uint8_t {
kNone = 0,
kReadAccumulator = 1 << 0,
kWriteAccumulator = 1 << 1,
kClobberAccumulator = 1 << 2,
kWriteShortStar = 1 << 3,
kReadWriteAccumulator = kReadAccumulator | kWriteAccumulator,
kReadAndClobberAccumulator = kReadAccumulator | kClobberAccumulator,
kReadAccumulatorWriteShortStar = kReadAccumulator | kWriteShortStar
};
constexpr inline ImplicitRegisterUse operator&(ImplicitRegisterUse lhs,
ImplicitRegisterUse rhs) {
return static_cast<ImplicitRegisterUse>(static_cast<int>(lhs) &
static_cast<int>(rhs));
}
constexpr inline ImplicitRegisterUse operator|(ImplicitRegisterUse lhs,
ImplicitRegisterUse rhs) {
return static_cast<ImplicitRegisterUse>(static_cast<int>(lhs) |
static_cast<int>(rhs));
}
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
const ImplicitRegisterUse& use);
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
const OperandScale& operand_scale);
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
const OperandSize& operand_size);
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
const OperandType& operand_type);
class BytecodeOperands : public AllStatic {
public:
// The total number of bytecode operand types used.
static const int kOperandTypeCount = static_cast<int>(OperandType::kLast) + 1;
// The total number of bytecode operand scales used.
#define OPERAND_SCALE_COUNT(...) +1
static const int kOperandScaleCount =
0 OPERAND_SCALE_LIST(OPERAND_SCALE_COUNT);
#undef OPERAND_SCALE_COUNT
static constexpr int OperandScaleAsIndex(OperandScale operand_scale) {
#ifdef DEBUG
int result = static_cast<int>(operand_scale) >> 1;
switch (operand_scale) {
case OperandScale::kSingle:
DCHECK_EQ(0, result);
break;
case OperandScale::kDouble:
DCHECK_EQ(1, result);
break;
case OperandScale::kQuadruple:
DCHECK_EQ(2, result);
break;
default:
UNREACHABLE();
}
#endif
return static_cast<int>(operand_scale) >> 1;
}
// Returns true if |implicit_register_use| reads the
// accumulator.
static constexpr bool ReadsAccumulator(
ImplicitRegisterUse implicit_register_use) {
return (implicit_register_use & ImplicitRegisterUse::kReadAccumulator) ==
ImplicitRegisterUse::kReadAccumulator;
}
// Returns true if |implicit_register_use| writes the
// accumulator.
static constexpr bool WritesAccumulator(
ImplicitRegisterUse implicit_register_use) {
return (implicit_register_use & ImplicitRegisterUse::kWriteAccumulator) ==
ImplicitRegisterUse::kWriteAccumulator;
}
// Returns true if |implicit_register_use| clobbers the
// accumulator.
static constexpr bool ClobbersAccumulator(
ImplicitRegisterUse implicit_register_use) {
return (implicit_register_use & ImplicitRegisterUse::kClobberAccumulator) ==
ImplicitRegisterUse::kClobberAccumulator;
}
// Returns true if |implicit_register_use| writes or clobbers the
// accumulator.
static constexpr bool WritesOrClobbersAccumulator(
ImplicitRegisterUse implicit_register_use) {
return (implicit_register_use &
(ImplicitRegisterUse::kWriteAccumulator |
ImplicitRegisterUse::kClobberAccumulator)) !=
ImplicitRegisterUse::kNone;
}
// Returns true if |implicit_register_use| writes to a
// register not specified by an operand.
static constexpr bool WritesImplicitRegister(
ImplicitRegisterUse implicit_register_use) {
return (implicit_register_use & ImplicitRegisterUse::kWriteShortStar) ==
ImplicitRegisterUse::kWriteShortStar;
}
// Returns true if |operand_type| is a scalable signed byte.
static constexpr bool IsScalableSignedByte(OperandType operand_type) {
return base::IsInRange(operand_type, OperandType::kImm,
OperandType::kRegInOut);
}
// Returns true if |operand_type| is a scalable unsigned byte.
static constexpr bool IsScalableUnsignedByte(OperandType operand_type) {
return base::IsInRange(operand_type, OperandType::kIdx,
OperandType::kRegCount);
}
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_OPERANDS_H_

View File

@ -0,0 +1,122 @@
// Copyright 2015 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_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_
#define V8_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_
#include "src/interpreter/bytecode-register.h"
namespace v8 {
namespace internal {
namespace interpreter {
// A class that allows the allocation of contiguous temporary registers.
class BytecodeRegisterAllocator final {
public:
// Enables observation of register allocation and free events.
class Observer {
public:
virtual ~Observer() = default;
virtual void RegisterAllocateEvent(Register reg) = 0;
virtual void RegisterListAllocateEvent(RegisterList reg_list) = 0;
virtual void RegisterListFreeEvent(RegisterList reg_list) = 0;
virtual void RegisterFreeEvent(Register reg_list) = 0;
};
explicit BytecodeRegisterAllocator(int start_index)
: next_register_index_(start_index),
max_register_count_(start_index),
observer_(nullptr) {}
~BytecodeRegisterAllocator() = default;
BytecodeRegisterAllocator(const BytecodeRegisterAllocator&) = delete;
BytecodeRegisterAllocator& operator=(const BytecodeRegisterAllocator&) =
delete;
// Returns a new register.
Register NewRegister() {
Register reg(next_register_index_++);
max_register_count_ = std::max(next_register_index_, max_register_count_);
if (observer_) {
observer_->RegisterAllocateEvent(reg);
}
return reg;
}
// Returns a consecutive list of |count| new registers.
RegisterList NewRegisterList(int count) {
RegisterList reg_list(next_register_index_, count);
next_register_index_ += count;
max_register_count_ = std::max(next_register_index_, max_register_count_);
if (observer_) {
observer_->RegisterListAllocateEvent(reg_list);
}
return reg_list;
}
// Returns a growable register list.
RegisterList NewGrowableRegisterList() {
RegisterList reg_list(next_register_index_, 0);
return reg_list;
}
// Appends a new register to |reg_list| increasing it's count by one and
// returning the register added.
//
// Note: no other new registers must be currently allocated since the register
// list was originally allocated.
Register GrowRegisterList(RegisterList* reg_list) {
Register reg(NewRegister());
reg_list->IncrementRegisterCount();
// If the following CHECK fails then a register was allocated (and not
// freed) between the creation of the RegisterList and this call to add a
// Register.
CHECK_EQ(reg.index(), reg_list->last_register().index());
return reg;
}
// Release all registers above |register_index|.
void ReleaseRegisters(int register_index) {
int count = next_register_index_ - register_index;
next_register_index_ = register_index;
if (observer_) {
observer_->RegisterListFreeEvent(RegisterList(register_index, count));
}
}
// Release last allocated register
void ReleaseRegister(Register reg) {
DCHECK_EQ(next_register_index_ - 1, reg.index());
if (observer_) {
observer_->RegisterFreeEvent(reg);
}
next_register_index_--;
}
// Returns true if the register |reg| is a live register.
bool RegisterIsLive(Register reg) const {
return reg.index() < next_register_index_;
}
// Returns a register list for all currently live registers.
RegisterList AllLiveRegisters() const {
return RegisterList(0, next_register_index());
}
void set_observer(Observer* observer) { observer_ = observer; }
int next_register_index() const { return next_register_index_; }
int maximum_register_count() const { return max_register_count_; }
private:
int next_register_index_;
int max_register_count_;
Observer* observer_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_

View File

@ -0,0 +1,592 @@
// Copyright 2016 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.
#include "src/interpreter/bytecode-register-optimizer.h"
#include "src/interpreter/bytecode-generator.h"
namespace v8 {
namespace internal {
namespace interpreter {
const uint32_t BytecodeRegisterOptimizer::kInvalidEquivalenceId = kMaxUInt32;
using TypeHint = BytecodeRegisterOptimizer::TypeHint;
// kDefinitelyHasVariable means that the variable is definitely in the register.
// kMightHaveVariable means that the variable might be in the register.
enum class VariableHintMode { kDefinitelyHasVariable, kMightHaveVariable };
struct VariableHint {
Variable* variable;
VariableHintMode mode;
};
enum class MaterializedInfo { kNotMaterialized, kMaterialized };
enum class ResetVariableHint { kDontReset, kReset };
// A class for tracking the state of a register. This class tracks
// which equivalence set a register is a member of and also whether a
// register is materialized in the bytecode stream.
class BytecodeRegisterOptimizer::RegisterInfo final : public ZoneObject {
public:
RegisterInfo(Register reg, uint32_t equivalence_id, bool materialized,
bool allocated)
: register_(reg),
equivalence_id_(equivalence_id),
materialized_(materialized),
allocated_(allocated),
needs_flush_(false),
type_hint_(TypeHint::kAny),
variable_hint_({nullptr, VariableHintMode::kDefinitelyHasVariable}),
next_(this),
prev_(this) {}
RegisterInfo(const RegisterInfo&) = delete;
RegisterInfo& operator=(const RegisterInfo&) = delete;
void AddToEquivalenceSetOf(RegisterInfo* info);
void MoveToNewEquivalenceSet(
uint32_t equivalence_id, MaterializedInfo materialized,
ResetVariableHint reset = ResetVariableHint::kReset);
bool IsOnlyMemberOfEquivalenceSet() const;
bool IsInSameEquivalenceSet(RegisterInfo* info) const;
// Get a member of the register's equivalence set that is allocated.
// Returns itself if allocated, and nullptr if there is no unallocated
// equivalent register.
RegisterInfo* GetAllocatedEquivalent();
// Get a member of this register's equivalence set that is
// materialized. The materialized equivalent will be this register
// if it is materialized. Returns nullptr if no materialized
// equivalent exists.
RegisterInfo* GetMaterializedEquivalent();
// Get a member of this register's equivalence set that is
// materialized and not register |reg|. The materialized equivalent
// will be this register if it is materialized. Returns nullptr if
// no materialized equivalent exists.
RegisterInfo* GetMaterializedEquivalentOtherThan(Register reg);
// Get a member of this register's equivalence set that is intended
// to be materialized in place of this register (which is currently
// materialized). The best candidate is deemed to be the register
// with the lowest index as this permits temporary registers to be
// removed from the bytecode stream. Returns nullptr if no candidate
// exists.
RegisterInfo* GetEquivalentToMaterialize();
// Marks all temporary registers of the equivalence set as unmaterialized.
void MarkTemporariesAsUnmaterialized(Register temporary_base);
// Get an equivalent register. Returns this if none exists.
RegisterInfo* GetEquivalent();
Register register_value() const { return register_; }
bool materialized() const { return materialized_; }
void set_materialized(bool materialized) { materialized_ = materialized; }
bool allocated() const { return allocated_; }
void set_allocated(bool allocated) { allocated_ = allocated; }
void set_equivalence_id(uint32_t equivalence_id) {
equivalence_id_ = equivalence_id;
}
uint32_t equivalence_id() const { return equivalence_id_; }
// Indicates if a register should be processed when calling Flush().
bool needs_flush() const { return needs_flush_; }
void set_needs_flush(bool needs_flush) { needs_flush_ = needs_flush; }
TypeHint type_hint() const { return type_hint_; }
void set_type_hint(TypeHint hint) { type_hint_ = hint; }
VariableHint variable_hint() const { return variable_hint_; }
void set_variable_hint(VariableHint hint) { variable_hint_ = hint; }
void flush_variable_hint(bool reset_variable_hint) {
if (reset_variable_hint) {
variable_hint_ = {nullptr, VariableHintMode::kDefinitelyHasVariable};
} else if (variable_hint_.variable != nullptr) {
variable_hint_.mode = VariableHintMode::kMightHaveVariable;
}
}
RegisterInfo* next() const { return next_; }
private:
Register register_;
uint32_t equivalence_id_;
bool materialized_;
bool allocated_;
bool needs_flush_;
TypeHint type_hint_;
VariableHint variable_hint_;
// Equivalence set pointers.
RegisterInfo* next_;
RegisterInfo* prev_;
};
void BytecodeRegisterOptimizer::RegisterInfo::AddToEquivalenceSetOf(
RegisterInfo* info) {
DCHECK_NE(kInvalidEquivalenceId, info->equivalence_id());
// Fix old list
next_->prev_ = prev_;
prev_->next_ = next_;
// Add to new list.
next_ = info->next_;
prev_ = info;
prev_->next_ = this;
next_->prev_ = this;
set_equivalence_id(info->equivalence_id());
set_materialized(false);
set_variable_hint(info->variable_hint());
type_hint_ = info->type_hint();
}
void BytecodeRegisterOptimizer::RegisterInfo::MoveToNewEquivalenceSet(
uint32_t equivalence_id, MaterializedInfo materialized,
ResetVariableHint reset) {
next_->prev_ = prev_;
prev_->next_ = next_;
next_ = prev_ = this;
equivalence_id_ = equivalence_id;
materialized_ = materialized == MaterializedInfo::kMaterialized;
flush_variable_hint(reset == ResetVariableHint::kReset);
type_hint_ = TypeHint::kAny;
}
bool BytecodeRegisterOptimizer::RegisterInfo::IsOnlyMemberOfEquivalenceSet()
const {
return this->next_ == this;
}
void BytecodeRegisterOptimizer::SetVariableInRegister(Variable* var,
Register reg) {
RegisterInfo* info = GetRegisterInfo(reg);
RegisterInfo* it = info;
do {
PushToRegistersNeedingFlush(it);
it->set_variable_hint({var, VariableHintMode::kDefinitelyHasVariable});
it = it->next();
} while (it != info);
}
Variable* BytecodeRegisterOptimizer::GetPotentialVariableInRegister(
Register reg) {
RegisterInfo* info = GetRegisterInfo(reg);
return info->variable_hint().variable;
}
bool BytecodeRegisterOptimizer::IsVariableInRegister(Variable* var,
Register reg) {
DCHECK_NOT_NULL(var);
RegisterInfo* info = GetRegisterInfo(reg);
VariableHint hint = info->variable_hint();
return hint.mode == VariableHintMode::kDefinitelyHasVariable &&
hint.variable == var;
}
TypeHint BytecodeRegisterOptimizer::GetTypeHint(Register reg) {
RegisterInfo* info = GetRegisterInfo(reg);
return info->type_hint();
}
void BytecodeRegisterOptimizer::SetTypeHintForAccumulator(TypeHint hint) {
DCHECK(BytecodeGenerator::IsSameOrSubTypeHint(accumulator_info_->type_hint(),
hint));
if (accumulator_info_->type_hint() != hint) {
accumulator_info_->set_type_hint(hint);
}
}
void BytecodeRegisterOptimizer::ResetTypeHintForAccumulator() {
accumulator_info_->set_type_hint(TypeHint::kAny);
}
bool BytecodeRegisterOptimizer::IsAccumulatorReset() {
return accumulator_info_->type_hint() == TypeHint::kAny;
}
bool BytecodeRegisterOptimizer::RegisterInfo::IsInSameEquivalenceSet(
RegisterInfo* info) const {
return equivalence_id() == info->equivalence_id();
}
BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::RegisterInfo::GetAllocatedEquivalent() {
RegisterInfo* visitor = this;
do {
if (visitor->allocated()) {
return visitor;
}
visitor = visitor->next_;
} while (visitor != this);
return nullptr;
}
BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::RegisterInfo::GetMaterializedEquivalent() {
RegisterInfo* visitor = this;
do {
if (visitor->materialized()) {
return visitor;
}
visitor = visitor->next_;
} while (visitor != this);
return nullptr;
}
BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::RegisterInfo::GetMaterializedEquivalentOtherThan(
Register reg) {
RegisterInfo* visitor = this;
do {
if (visitor->materialized() && visitor->register_value() != reg) {
return visitor;
}
visitor = visitor->next_;
} while (visitor != this);
return nullptr;
}
BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::RegisterInfo::GetEquivalentToMaterialize() {
DCHECK(this->materialized());
RegisterInfo* visitor = this->next_;
RegisterInfo* best_info = nullptr;
while (visitor != this) {
if (visitor->materialized()) {
return nullptr;
}
if (visitor->allocated() &&
(best_info == nullptr ||
visitor->register_value() < best_info->register_value())) {
best_info = visitor;
}
visitor = visitor->next_;
}
return best_info;
}
void BytecodeRegisterOptimizer::RegisterInfo::MarkTemporariesAsUnmaterialized(
Register temporary_base) {
DCHECK(this->register_value() < temporary_base);
DCHECK(this->materialized());
RegisterInfo* visitor = this->next_;
while (visitor != this) {
if (visitor->register_value() >= temporary_base) {
visitor->set_materialized(false);
}
visitor = visitor->next_;
}
}
BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::RegisterInfo::GetEquivalent() {
return next_;
}
BytecodeRegisterOptimizer::BytecodeRegisterOptimizer(
Zone* zone, BytecodeRegisterAllocator* register_allocator,
int fixed_registers_count, int parameter_count,
BytecodeWriter* bytecode_writer)
: accumulator_(Register::virtual_accumulator()),
temporary_base_(fixed_registers_count),
max_register_index_(fixed_registers_count - 1),
register_info_table_(zone),
registers_needing_flushed_(zone),
equivalence_id_(0),
bytecode_writer_(bytecode_writer),
flush_required_(false),
zone_(zone) {
register_allocator->set_observer(this);
// Calculate offset so register index values can be mapped into
// a vector of register metadata.
// There is at least one parameter, which is the JS receiver.
DCHECK_NE(parameter_count, 0);
int first_slot_index = parameter_count - 1;
register_info_table_offset_ =
-Register::FromParameterIndex(first_slot_index).index();
// Initialize register map for parameters, locals, and the
// accumulator.
register_info_table_.resize(register_info_table_offset_ +
static_cast<size_t>(temporary_base_.index()));
for (size_t i = 0; i < register_info_table_.size(); ++i) {
register_info_table_[i] = zone->New<RegisterInfo>(
RegisterFromRegisterInfoTableIndex(i), NextEquivalenceId(), true, true);
DCHECK_EQ(register_info_table_[i]->register_value().index(),
RegisterFromRegisterInfoTableIndex(i).index());
}
accumulator_info_ = GetRegisterInfo(accumulator_);
DCHECK(accumulator_info_->register_value() == accumulator_);
}
void BytecodeRegisterOptimizer::PushToRegistersNeedingFlush(RegisterInfo* reg) {
// Flushing is required in two cases:
// 1) Two or more registers in the same equivalence set.
// 2) Binding a variable to a register.
flush_required_ = true;
if (!reg->needs_flush()) {
reg->set_needs_flush(true);
registers_needing_flushed_.push_back(reg);
}
}
bool BytecodeRegisterOptimizer::EnsureAllRegistersAreFlushed() const {
for (RegisterInfo* reg_info : register_info_table_) {
if (reg_info->needs_flush()) {
return false;
} else if (!reg_info->IsOnlyMemberOfEquivalenceSet()) {
return false;
} else if (reg_info->allocated() && !reg_info->materialized()) {
return false;
}
}
return true;
}
void BytecodeRegisterOptimizer::Flush() {
if (!flush_required_) {
return;
}
// Materialize all live registers and break equivalences.
for (RegisterInfo* reg_info : registers_needing_flushed_) {
if (!reg_info->needs_flush()) continue;
reg_info->set_needs_flush(false);
reg_info->flush_variable_hint(false);
reg_info->set_type_hint(TypeHint::kAny);
RegisterInfo* materialized = reg_info->materialized()
? reg_info
: reg_info->GetMaterializedEquivalent();
if (materialized != nullptr) {
// Walk equivalents of materialized registers, materializing
// each equivalent register as necessary and placing in their
// own equivalence set.
RegisterInfo* equivalent;
while ((equivalent = materialized->GetEquivalent()) != materialized) {
if (equivalent->allocated() && !equivalent->materialized()) {
OutputRegisterTransfer(materialized, equivalent);
}
equivalent->MoveToNewEquivalenceSet(NextEquivalenceId(),
MaterializedInfo::kMaterialized,
ResetVariableHint::kDontReset);
equivalent->set_needs_flush(false);
}
} else {
// Equivalence class containing only unallocated registers.
DCHECK_NULL(reg_info->GetAllocatedEquivalent());
reg_info->MoveToNewEquivalenceSet(NextEquivalenceId(),
MaterializedInfo::kNotMaterialized,
ResetVariableHint::kDontReset);
}
}
registers_needing_flushed_.clear();
DCHECK(EnsureAllRegistersAreFlushed());
flush_required_ = false;
}
void BytecodeRegisterOptimizer::OutputRegisterTransfer(
RegisterInfo* input_info, RegisterInfo* output_info) {
Register input = input_info->register_value();
Register output = output_info->register_value();
DCHECK_NE(input.index(), output.index());
if (input == accumulator_) {
bytecode_writer_->EmitStar(output);
} else if (output == accumulator_) {
bytecode_writer_->EmitLdar(input);
} else {
bytecode_writer_->EmitMov(input, output);
}
if (output != accumulator_) {
max_register_index_ = std::max(max_register_index_, output.index());
}
output_info->set_materialized(true);
}
void BytecodeRegisterOptimizer::CreateMaterializedEquivalent(
RegisterInfo* info) {
DCHECK(info->materialized());
RegisterInfo* unmaterialized = info->GetEquivalentToMaterialize();
if (unmaterialized) {
OutputRegisterTransfer(info, unmaterialized);
}
}
BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::GetMaterializedEquivalentNotAccumulator(
RegisterInfo* info) {
if (info->materialized()) {
return info;
}
RegisterInfo* result = info->GetMaterializedEquivalentOtherThan(accumulator_);
if (result == nullptr) {
Materialize(info);
result = info;
}
DCHECK(result->register_value() != accumulator_);
return result;
}
void BytecodeRegisterOptimizer::Materialize(RegisterInfo* info) {
if (!info->materialized()) {
RegisterInfo* materialized = info->GetMaterializedEquivalent();
DCHECK_NOT_NULL(materialized);
OutputRegisterTransfer(materialized, info);
}
}
void BytecodeRegisterOptimizer::AddToEquivalenceSet(
RegisterInfo* set_member, RegisterInfo* non_set_member) {
// Equivalence class is now of size >= 2, so we make sure it will be flushed.
PushToRegistersNeedingFlush(non_set_member);
non_set_member->AddToEquivalenceSetOf(set_member);
}
void BytecodeRegisterOptimizer::RegisterTransfer(RegisterInfo* input_info,
RegisterInfo* output_info) {
bool output_is_observable =
RegisterIsObservable(output_info->register_value());
bool in_same_equivalence_set =
output_info->IsInSameEquivalenceSet(input_info);
if (in_same_equivalence_set &&
(!output_is_observable || output_info->materialized())) {
return; // Nothing more to do.
}
// Materialize an alternate in the equivalence set that
// |output_info| is leaving.
if (output_info->materialized()) {
CreateMaterializedEquivalent(output_info);
}
// Add |output_info| to new equivalence set.
if (!in_same_equivalence_set) {
AddToEquivalenceSet(input_info, output_info);
}
if (output_is_observable) {
// Force store to be emitted when register is observable.
output_info->set_materialized(false);
RegisterInfo* materialized_info = input_info->GetMaterializedEquivalent();
OutputRegisterTransfer(materialized_info, output_info);
}
bool input_is_observable = RegisterIsObservable(input_info->register_value());
if (input_is_observable) {
// If input is observable by the debugger, mark all other temporaries
// registers as unmaterialized so that this register is used in preference.
input_info->MarkTemporariesAsUnmaterialized(temporary_base_);
}
}
void BytecodeRegisterOptimizer::PrepareOutputRegister(Register reg) {
RegisterInfo* reg_info = GetRegisterInfo(reg);
if (reg_info->materialized()) {
CreateMaterializedEquivalent(reg_info);
}
reg_info->MoveToNewEquivalenceSet(NextEquivalenceId(),
MaterializedInfo::kMaterialized);
max_register_index_ =
std::max(max_register_index_, reg_info->register_value().index());
}
void BytecodeRegisterOptimizer::PrepareOutputRegisterList(
RegisterList reg_list) {
int start_index = reg_list.first_register().index();
for (int i = 0; i < reg_list.register_count(); ++i) {
Register current(start_index + i);
PrepareOutputRegister(current);
}
}
Register BytecodeRegisterOptimizer::GetInputRegister(Register reg) {
RegisterInfo* reg_info = GetRegisterInfo(reg);
if (reg_info->materialized()) {
return reg;
} else {
RegisterInfo* equivalent_info =
GetMaterializedEquivalentNotAccumulator(reg_info);
return equivalent_info->register_value();
}
}
RegisterList BytecodeRegisterOptimizer::GetInputRegisterList(
RegisterList reg_list) {
if (reg_list.register_count() == 1) {
// If there is only a single register, treat it as a normal input register.
Register reg(GetInputRegister(reg_list.first_register()));
return RegisterList(reg);
} else {
int start_index = reg_list.first_register().index();
for (int i = 0; i < reg_list.register_count(); ++i) {
Register current(start_index + i);
RegisterInfo* input_info = GetRegisterInfo(current);
Materialize(input_info);
}
return reg_list;
}
}
void BytecodeRegisterOptimizer::GrowRegisterMap(Register reg) {
DCHECK(RegisterIsTemporary(reg));
size_t index = GetRegisterInfoTableIndex(reg);
if (index >= register_info_table_.size()) {
size_t new_size = index + 1;
size_t old_size = register_info_table_.size();
register_info_table_.resize(new_size);
for (size_t i = old_size; i < new_size; ++i) {
register_info_table_[i] =
zone()->New<RegisterInfo>(RegisterFromRegisterInfoTableIndex(i),
NextEquivalenceId(), true, false);
}
}
}
void BytecodeRegisterOptimizer::AllocateRegister(RegisterInfo* info) {
info->set_allocated(true);
if (!info->materialized()) {
info->MoveToNewEquivalenceSet(NextEquivalenceId(),
MaterializedInfo::kMaterialized);
}
}
void BytecodeRegisterOptimizer::RegisterAllocateEvent(Register reg) {
AllocateRegister(GetOrCreateRegisterInfo(reg));
}
void BytecodeRegisterOptimizer::RegisterListAllocateEvent(
RegisterList reg_list) {
if (reg_list.register_count() != 0) {
int first_index = reg_list.first_register().index();
GrowRegisterMap(Register(first_index + reg_list.register_count() - 1));
for (int i = 0; i < reg_list.register_count(); i++) {
AllocateRegister(GetRegisterInfo(Register(first_index + i)));
}
}
}
void BytecodeRegisterOptimizer::RegisterListFreeEvent(RegisterList reg_list) {
int first_index = reg_list.first_register().index();
for (int i = 0; i < reg_list.register_count(); i++) {
GetRegisterInfo(Register(first_index + i))->set_allocated(false);
}
}
void BytecodeRegisterOptimizer::RegisterFreeEvent(Register reg) {
GetRegisterInfo(reg)->set_allocated(false);
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,239 @@
// Copyright 2016 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_INTERPRETER_BYTECODE_REGISTER_OPTIMIZER_H_
#define V8_INTERPRETER_BYTECODE_REGISTER_OPTIMIZER_H_
#include "src/ast/variables.h"
#include "src/base/compiler-specific.h"
#include "src/common/globals.h"
#include "src/interpreter/bytecode-generator.h"
#include "src/interpreter/bytecode-register-allocator.h"
#include "src/zone/zone-containers.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace interpreter {
// An optimization stage for eliminating unnecessary transfers between
// registers. The bytecode generator uses temporary registers
// liberally for correctness and convenience and this stage removes
// transfers that are not required and preserves correctness.
class V8_EXPORT_PRIVATE BytecodeRegisterOptimizer final
: public NON_EXPORTED_BASE(BytecodeRegisterAllocator::Observer),
public NON_EXPORTED_BASE(ZoneObject) {
public:
using TypeHint = BytecodeGenerator::TypeHint;
class BytecodeWriter {
public:
BytecodeWriter() = default;
virtual ~BytecodeWriter() = default;
BytecodeWriter(const BytecodeWriter&) = delete;
BytecodeWriter& operator=(const BytecodeWriter&) = delete;
// Called to emit a register transfer bytecode.
virtual void EmitLdar(Register input) = 0;
virtual void EmitStar(Register output) = 0;
virtual void EmitMov(Register input, Register output) = 0;
};
BytecodeRegisterOptimizer(Zone* zone,
BytecodeRegisterAllocator* register_allocator,
int fixed_registers_count, int parameter_count,
BytecodeWriter* bytecode_writer);
~BytecodeRegisterOptimizer() override = default;
BytecodeRegisterOptimizer(const BytecodeRegisterOptimizer&) = delete;
BytecodeRegisterOptimizer& operator=(const BytecodeRegisterOptimizer&) =
delete;
// Perform explicit register transfer operations.
void DoLdar(Register input) {
// TODO(rmcilroy): Avoid treating accumulator loads as clobbering the
// accumulator until the value is actually materialized in the accumulator.
RegisterInfo* input_info = GetRegisterInfo(input);
RegisterTransfer(input_info, accumulator_info_);
}
void DoStar(Register output) {
RegisterInfo* output_info = GetRegisterInfo(output);
RegisterTransfer(accumulator_info_, output_info);
}
void DoMov(Register input, Register output) {
RegisterInfo* input_info = GetRegisterInfo(input);
RegisterInfo* output_info = GetRegisterInfo(output);
RegisterTransfer(input_info, output_info);
}
// Materialize all live registers and flush equivalence sets.
void Flush();
bool EnsureAllRegistersAreFlushed() const;
// Prepares for |bytecode|.
template <Bytecode bytecode, ImplicitRegisterUse implicit_register_use>
V8_INLINE void PrepareForBytecode() {
if (Bytecodes::IsJump(bytecode) || Bytecodes::IsSwitch(bytecode) ||
bytecode == Bytecode::kDebugger ||
bytecode == Bytecode::kSuspendGenerator ||
bytecode == Bytecode::kResumeGenerator) {
// All state must be flushed before emitting
// - a jump bytecode (as the register equivalents at the jump target
// aren't known)
// - a switch bytecode (as the register equivalents at the switch targets
// aren't known)
// - a call to the debugger (as it can manipulate locals and parameters),
// - a generator suspend (as this involves saving all registers).
// - a generator register restore.
Flush();
}
// Materialize the accumulator if it is read by the bytecode. The
// accumulator is special and no other register can be materialized
// in it's place.
if (BytecodeOperands::ReadsAccumulator(implicit_register_use)) {
Materialize(accumulator_info_);
}
// Materialize an equivalent to the accumulator if it will be
// clobbered when the bytecode is dispatched.
if (BytecodeOperands::WritesOrClobbersAccumulator(implicit_register_use)) {
PrepareOutputRegister(accumulator_);
DCHECK_EQ(GetTypeHint(accumulator_), TypeHint::kAny);
}
}
// Prepares |reg| for being used as an output operand.
void PrepareOutputRegister(Register reg);
// Prepares registers in |reg_list| for being used as an output operand.
void PrepareOutputRegisterList(RegisterList reg_list);
// Returns an equivalent register to |reg| to be used as an input operand.
Register GetInputRegister(Register reg);
// Returns an equivalent register list to |reg_list| to be used as an input
// operand.
RegisterList GetInputRegisterList(RegisterList reg_list);
// Maintain the map between Variable and Register.
void SetVariableInRegister(Variable* var, Register reg);
// Get the variable that might be in the reg. This is a variable value that
// is preserved across flushes.
Variable* GetPotentialVariableInRegister(Register reg);
// Get the variable that might be in the accumulator. This is a variable value
// that is preserved across flushes.
Variable* GetPotentialVariableInAccumulator() {
return GetPotentialVariableInRegister(accumulator_);
}
// Return true if the var is in the reg.
bool IsVariableInRegister(Variable* var, Register reg);
TypeHint GetTypeHint(Register reg);
void SetTypeHintForAccumulator(TypeHint hint);
void ResetTypeHintForAccumulator();
bool IsAccumulatorReset();
int maxiumum_register_index() const { return max_register_index_; }
private:
static const uint32_t kInvalidEquivalenceId;
class RegisterInfo;
// BytecodeRegisterAllocator::Observer interface.
void RegisterAllocateEvent(Register reg) override;
void RegisterListAllocateEvent(RegisterList reg_list) override;
void RegisterListFreeEvent(RegisterList reg) override;
void RegisterFreeEvent(Register reg) override;
// Update internal state for register transfer from |input| to |output|
void RegisterTransfer(RegisterInfo* input, RegisterInfo* output);
// Emit a register transfer bytecode from |input| to |output|.
void OutputRegisterTransfer(RegisterInfo* input, RegisterInfo* output);
void CreateMaterializedEquivalent(RegisterInfo* info);
RegisterInfo* GetMaterializedEquivalentNotAccumulator(RegisterInfo* info);
void Materialize(RegisterInfo* info);
void AddToEquivalenceSet(RegisterInfo* set_member,
RegisterInfo* non_set_member);
void PushToRegistersNeedingFlush(RegisterInfo* reg);
// Methods for finding and creating metadata for each register.
RegisterInfo* GetRegisterInfo(Register reg) {
size_t index = GetRegisterInfoTableIndex(reg);
DCHECK_LT(index, register_info_table_.size());
return register_info_table_[index];
}
RegisterInfo* GetOrCreateRegisterInfo(Register reg) {
size_t index = GetRegisterInfoTableIndex(reg);
return index < register_info_table_.size() ? register_info_table_[index]
: NewRegisterInfo(reg);
}
RegisterInfo* NewRegisterInfo(Register reg) {
size_t index = GetRegisterInfoTableIndex(reg);
DCHECK_GE(index, register_info_table_.size());
GrowRegisterMap(reg);
return register_info_table_[index];
}
void GrowRegisterMap(Register reg);
bool RegisterIsTemporary(Register reg) const {
return reg >= temporary_base_;
}
bool RegisterIsObservable(Register reg) const {
return reg != accumulator_ && !RegisterIsTemporary(reg);
}
static Register OperandToRegister(uint32_t operand) {
return Register::FromOperand(static_cast<int32_t>(operand));
}
size_t GetRegisterInfoTableIndex(Register reg) const {
return static_cast<size_t>(reg.index() + register_info_table_offset_);
}
Register RegisterFromRegisterInfoTableIndex(size_t index) const {
return Register(static_cast<int>(index) - register_info_table_offset_);
}
uint32_t NextEquivalenceId() {
equivalence_id_++;
CHECK_NE(equivalence_id_, kInvalidEquivalenceId);
return equivalence_id_;
}
void AllocateRegister(RegisterInfo* info);
Zone* zone() { return zone_; }
const Register accumulator_;
RegisterInfo* accumulator_info_;
const Register temporary_base_;
int max_register_index_;
// Direct mapping to register info.
ZoneVector<RegisterInfo*> register_info_table_;
int register_info_table_offset_;
ZoneDeque<RegisterInfo*> registers_needing_flushed_;
// Counter for equivalence sets identifiers.
uint32_t equivalence_id_;
BytecodeWriter* bytecode_writer_;
bool flush_required_;
Zone* zone_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_REGISTER_OPTIMIZER_H_

View File

@ -0,0 +1,36 @@
// Copyright 2015 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.
#include "src/interpreter/bytecode-register.h"
namespace v8 {
namespace internal {
namespace interpreter {
std::string Register::ToString() const {
if (is_current_context()) {
return std::string("<context>");
} else if (is_function_closure()) {
return std::string("<closure>");
} else if (*this == virtual_accumulator()) {
return std::string("<accumulator>");
} else if (is_parameter()) {
int parameter_index = ToParameterIndex();
if (parameter_index == 0) {
return std::string("<this>");
} else {
std::ostringstream s;
s << "a" << parameter_index - 1;
return s.str();
}
} else {
std::ostringstream s;
s << "r" << index();
return s.str();
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,278 @@
// Copyright 2015 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_INTERPRETER_BYTECODE_REGISTER_H_
#define V8_INTERPRETER_BYTECODE_REGISTER_H_
#include <optional>
#include "src/base/macros.h"
#include "src/base/platform/platform.h"
#include "src/common/globals.h"
#include "src/execution/frame-constants.h"
#include "src/interpreter/bytecodes.h"
namespace v8 {
namespace internal {
namespace interpreter {
constexpr int OffsetFromFPToRegisterIndex(int offset) {
return (InterpreterFrameConstants::kRegisterFileFromFp - offset) /
kSystemPointerSize;
}
// An interpreter Register which is located in the function's Register file
// in its stack-frame. Register hold parameters, this, and expression values.
class V8_EXPORT_PRIVATE Register final {
public:
constexpr explicit Register(int index = kInvalidIndex) : index_(index) {}
constexpr int index() const { return index_; }
constexpr bool is_parameter() const { return index() < 0; }
constexpr bool is_valid() const { return index_ != kInvalidIndex; }
static constexpr Register FromParameterIndex(int index);
constexpr int ToParameterIndex() const;
static constexpr Register receiver() { return FromParameterIndex(0); }
constexpr bool is_receiver() const { return ToParameterIndex() == 0; }
// Returns an invalid register.
static constexpr Register invalid_value() { return Register(); }
// Returns the register for the function's closure object.
static constexpr Register function_closure();
constexpr bool is_function_closure() const;
// Returns the register which holds the current context object.
static constexpr Register current_context();
constexpr bool is_current_context() const;
// Returns the register for the bytecode array.
static constexpr Register bytecode_array();
constexpr bool is_bytecode_array() const;
// Returns the register for the saved bytecode offset.
static constexpr Register bytecode_offset();
constexpr bool is_bytecode_offset() const;
// Returns the register for the cached feedback vector.
static constexpr Register feedback_vector();
constexpr bool is_feedback_vector() const;
// Returns the register for the argument count.
static constexpr Register argument_count();
// Returns a register that can be used to represent the accumulator
// within code in the interpreter, but should never be emitted in
// bytecode.
static constexpr Register virtual_accumulator();
constexpr OperandSize SizeOfOperand() const;
constexpr int32_t ToOperand() const {
return kRegisterFileStartOffset - index_;
}
static constexpr Register FromOperand(int32_t operand) {
return Register(kRegisterFileStartOffset - operand);
}
static constexpr Register FromShortStar(Bytecode bytecode) {
DCHECK(Bytecodes::IsShortStar(bytecode));
return Register(static_cast<int>(Bytecode::kStar0) -
static_cast<int>(bytecode));
}
constexpr std::optional<Bytecode> TryToShortStar() const {
if (index() >= 0 && index() < Bytecodes::kShortStarCount) {
Bytecode bytecode =
static_cast<Bytecode>(static_cast<int>(Bytecode::kStar0) - index());
DCHECK_GE(bytecode, Bytecode::kFirstShortStar);
DCHECK_LE(bytecode, Bytecode::kLastShortStar);
return bytecode;
}
return {};
}
std::string ToString() const;
constexpr bool operator==(const Register& other) const {
return index() == other.index();
}
constexpr bool operator!=(const Register& other) const {
return index() != other.index();
}
constexpr bool operator<(const Register& other) const {
return index() < other.index();
}
constexpr bool operator<=(const Register& other) const {
return index() <= other.index();
}
constexpr bool operator>(const Register& other) const {
return index() > other.index();
}
constexpr bool operator>=(const Register& other) const {
return index() >= other.index();
}
private:
DISALLOW_NEW_AND_DELETE()
static constexpr int kInvalidIndex = kMaxInt;
static constexpr int kRegisterFileStartOffset =
OffsetFromFPToRegisterIndex(0);
static constexpr int kFirstParamRegisterIndex =
OffsetFromFPToRegisterIndex(InterpreterFrameConstants::kFirstParamFromFp);
static constexpr int kFunctionClosureRegisterIndex =
OffsetFromFPToRegisterIndex(StandardFrameConstants::kFunctionOffset);
static constexpr int kCurrentContextRegisterIndex =
OffsetFromFPToRegisterIndex(StandardFrameConstants::kContextOffset);
static constexpr int kBytecodeArrayRegisterIndex =
OffsetFromFPToRegisterIndex(
InterpreterFrameConstants::kBytecodeArrayFromFp);
static constexpr int kBytecodeOffsetRegisterIndex =
OffsetFromFPToRegisterIndex(
InterpreterFrameConstants::kBytecodeOffsetFromFp);
static constexpr int kFeedbackVectorRegisterIndex =
OffsetFromFPToRegisterIndex(
InterpreterFrameConstants::kFeedbackVectorFromFp);
static constexpr int kCallerPCOffsetRegisterIndex =
OffsetFromFPToRegisterIndex(InterpreterFrameConstants::kCallerPCOffset);
static constexpr int kArgumentCountRegisterIndex =
OffsetFromFPToRegisterIndex(InterpreterFrameConstants::kArgCOffset);
int index_;
};
class RegisterList {
public:
RegisterList()
: first_reg_index_(Register::invalid_value().index()),
register_count_(0) {}
explicit RegisterList(Register r) : RegisterList(r.index(), 1) {}
// Returns a new RegisterList which is a truncated version of this list, with
// |count| registers.
const RegisterList Truncate(int new_count) {
DCHECK_GE(new_count, 0);
DCHECK_LT(new_count, register_count_);
return RegisterList(first_reg_index_, new_count);
}
const RegisterList PopLeft() const {
DCHECK_GE(register_count_, 0);
return RegisterList(first_reg_index_ + 1, register_count_ - 1);
}
const Register operator[](size_t i) const {
DCHECK_LT(static_cast<int>(i), register_count_);
return Register(first_reg_index_ + static_cast<int>(i));
}
const Register first_register() const {
return (register_count() == 0) ? Register(0) : (*this)[0];
}
const Register last_register() const {
return (register_count() == 0) ? Register(0) : (*this)[register_count_ - 1];
}
int register_count() const { return register_count_; }
private:
friend class BytecodeRegisterAllocator;
friend class BytecodeDecoder;
friend class InterpreterTester;
friend class BytecodeUtils;
friend class BytecodeArrayIterator;
friend class CallArguments;
RegisterList(int first_reg_index, int register_count)
: first_reg_index_(first_reg_index), register_count_(register_count) {}
// Increases the size of the register list by one.
void IncrementRegisterCount() { register_count_++; }
int first_reg_index_;
int register_count_;
};
constexpr Register Register::FromParameterIndex(int index) {
DCHECK_GE(index, 0);
int register_index = kFirstParamRegisterIndex - index;
DCHECK_LT(register_index, 0);
return Register(register_index);
}
constexpr int Register::ToParameterIndex() const {
DCHECK(is_parameter());
return kFirstParamRegisterIndex - index();
}
constexpr Register Register::function_closure() {
return Register(kFunctionClosureRegisterIndex);
}
constexpr bool Register::is_function_closure() const {
return index() == kFunctionClosureRegisterIndex;
}
constexpr Register Register::current_context() {
return Register(kCurrentContextRegisterIndex);
}
constexpr bool Register::is_current_context() const {
return index() == kCurrentContextRegisterIndex;
}
constexpr Register Register::bytecode_array() {
return Register(kBytecodeArrayRegisterIndex);
}
constexpr bool Register::is_bytecode_array() const {
return index() == kBytecodeArrayRegisterIndex;
}
constexpr Register Register::bytecode_offset() {
return Register(kBytecodeOffsetRegisterIndex);
}
constexpr bool Register::is_bytecode_offset() const {
return index() == kBytecodeOffsetRegisterIndex;
}
constexpr Register Register::feedback_vector() {
return Register(kFeedbackVectorRegisterIndex);
}
constexpr bool Register::is_feedback_vector() const {
return index() == kFeedbackVectorRegisterIndex;
}
// static
constexpr Register Register::virtual_accumulator() {
return Register(kCallerPCOffsetRegisterIndex);
}
// static
constexpr Register Register::argument_count() {
return Register(kArgumentCountRegisterIndex);
}
constexpr OperandSize Register::SizeOfOperand() const {
int32_t operand = ToOperand();
if (operand >= kMinInt8 && operand <= kMaxInt8) {
return OperandSize::kByte;
} else if (operand >= kMinInt16 && operand <= kMaxInt16) {
return OperandSize::kShort;
} else {
return OperandSize::kQuad;
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_REGISTER_H_

View File

@ -0,0 +1,23 @@
// Copyright 2017 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.
#include "src/interpreter/bytecode-source-info.h"
#include <iomanip>
namespace v8 {
namespace internal {
namespace interpreter {
std::ostream& operator<<(std::ostream& os, const BytecodeSourceInfo& info) {
if (info.is_valid()) {
char description = info.is_statement() ? 'S' : 'E';
os << info.source_position() << ' ' << description << '>';
}
return os;
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,98 @@
// Copyright 2017 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_INTERPRETER_BYTECODE_SOURCE_INFO_H_
#define V8_INTERPRETER_BYTECODE_SOURCE_INFO_H_
#include "src/common/globals.h"
namespace v8 {
namespace internal {
namespace interpreter {
// Source code position information.
class BytecodeSourceInfo final {
public:
static const int kUninitializedPosition = -1;
BytecodeSourceInfo()
: position_type_(PositionType::kNone),
source_position_(kUninitializedPosition) {}
BytecodeSourceInfo(int source_position, bool is_statement)
: position_type_(is_statement ? PositionType::kStatement
: PositionType::kExpression),
source_position_(source_position) {
DCHECK_GE(source_position, 0);
}
// Makes instance into a statement position.
void MakeStatementPosition(int source_position) {
// Statement positions can be replaced by other statement
// positions. For example , "for (x = 0; x < 3; ++x) 7;" has a
// statement position associated with 7 but no bytecode associated
// with it. Then Next is emitted after the body and has
// statement position and overrides the existing one.
position_type_ = PositionType::kStatement;
source_position_ = source_position;
}
// Makes instance into an expression position. Instance should not
// be a statement position otherwise it could be lost and impair the
// debugging experience.
void MakeExpressionPosition(int source_position) {
DCHECK(!is_statement());
position_type_ = PositionType::kExpression;
source_position_ = source_position;
}
// Forces an instance into an expression position.
void ForceExpressionPosition(int source_position) {
position_type_ = PositionType::kExpression;
source_position_ = source_position;
}
int source_position() const {
DCHECK(is_valid());
return source_position_;
}
bool is_statement() const {
return position_type_ == PositionType::kStatement;
}
bool is_expression() const {
return position_type_ == PositionType::kExpression;
}
bool is_valid() const { return position_type_ != PositionType::kNone; }
void set_invalid() {
position_type_ = PositionType::kNone;
source_position_ = kUninitializedPosition;
}
bool operator==(const BytecodeSourceInfo& other) const {
return position_type_ == other.position_type_ &&
source_position_ == other.source_position_;
}
bool operator!=(const BytecodeSourceInfo& other) const {
return position_type_ != other.position_type_ ||
source_position_ != other.source_position_;
}
private:
enum class PositionType : uint8_t { kNone, kExpression, kStatement };
PositionType position_type_;
int source_position_;
};
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
const BytecodeSourceInfo& info);
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_SOURCE_INFO_H_

View File

@ -0,0 +1,117 @@
// Copyright 2015 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_INTERPRETER_BYTECODE_TRAITS_H_
#define V8_INTERPRETER_BYTECODE_TRAITS_H_
#include "src/interpreter/bytecode-operands.h"
namespace v8 {
namespace internal {
namespace interpreter {
template <OperandTypeInfo>
struct OperandTypeInfoTraits;
#define DECLARE_OPERAND_TYPE_INFO(Name, Scalable, Unsigned, BaseSize) \
template <> \
struct OperandTypeInfoTraits<OperandTypeInfo::k##Name> { \
static constexpr bool kIsScalable = Scalable; \
static constexpr bool kIsUnsigned = Unsigned; \
static constexpr OperandSize kUnscaledSize = BaseSize; \
};
OPERAND_TYPE_INFO_LIST(DECLARE_OPERAND_TYPE_INFO)
#undef DECLARE_OPERAND_TYPE_INFO
template <OperandType>
struct OperandTraits;
#define DECLARE_OPERAND_TYPE_TRAITS(Name, InfoType) \
template <> \
struct OperandTraits<OperandType::k##Name> { \
using TypeInfoTraits = OperandTypeInfoTraits<InfoType>; \
static constexpr OperandTypeInfo kOperandTypeInfo = InfoType; \
};
OPERAND_TYPE_LIST(DECLARE_OPERAND_TYPE_TRAITS)
#undef DECLARE_OPERAND_TYPE_TRAITS
template <OperandType operand_type, OperandScale operand_scale>
struct OperandScaler {
static constexpr int kSize =
static_cast<int>(
OperandTraits<operand_type>::TypeInfoTraits::kUnscaledSize) *
(OperandTraits<operand_type>::TypeInfoTraits::kIsScalable
? static_cast<int>(operand_scale)
: 1);
static constexpr OperandSize kOperandSize = static_cast<OperandSize>(kSize);
};
template <ImplicitRegisterUse implicit_register_use, OperandType... operands>
struct BytecodeTraits {
static constexpr OperandType kOperandTypes[] = {operands...};
static constexpr OperandTypeInfo kOperandTypeInfos[] = {
OperandTraits<operands>::kOperandTypeInfo...};
static constexpr OperandSize kSingleScaleOperandSizes[] = {
OperandScaler<operands, OperandScale::kSingle>::kOperandSize...};
static constexpr OperandSize kDoubleScaleOperandSizes[] = {
OperandScaler<operands, OperandScale::kDouble>::kOperandSize...};
static constexpr OperandSize kQuadrupleScaleOperandSizes[] = {
OperandScaler<operands, OperandScale::kQuadruple>::kOperandSize...};
template <OperandScale scale>
static constexpr auto CalculateOperandOffsets() {
std::array<int, sizeof...(operands) + 1> result{};
int offset = 1;
int i = 0;
(((result[i++] = offset),
(offset += OperandScaler<operands, scale>::kSize)),
...);
return result;
}
static constexpr auto kSingleScaleOperandOffsets =
CalculateOperandOffsets<OperandScale::kSingle>();
static constexpr auto kDoubleScaleOperandOffsets =
CalculateOperandOffsets<OperandScale::kDouble>();
static constexpr auto kQuadrupleScaleOperandOffsets =
CalculateOperandOffsets<OperandScale::kQuadruple>();
static constexpr int kSingleScaleSize =
(1 + ... + OperandScaler<operands, OperandScale::kSingle>::kSize);
static constexpr int kDoubleScaleSize =
(1 + ... + OperandScaler<operands, OperandScale::kDouble>::kSize);
static constexpr int kQuadrupleScaleSize =
(1 + ... + OperandScaler<operands, OperandScale::kQuadruple>::kSize);
static constexpr ImplicitRegisterUse kImplicitRegisterUse =
implicit_register_use;
static constexpr int kOperandCount = sizeof...(operands);
};
template <ImplicitRegisterUse implicit_register_use>
struct BytecodeTraits<implicit_register_use> {
static constexpr OperandType* kOperandTypes = nullptr;
static constexpr OperandTypeInfo* kOperandTypeInfos = nullptr;
static constexpr OperandSize* kSingleScaleOperandSizes = nullptr;
static constexpr OperandSize* kDoubleScaleOperandSizes = nullptr;
static constexpr OperandSize* kQuadrupleScaleOperandSizes = nullptr;
static constexpr auto kSingleScaleOperandOffsets = std::array<int, 0>{};
static constexpr auto kDoubleScaleOperandOffsets = std::array<int, 0>{};
static constexpr auto kQuadrupleScaleOperandOffsets = std::array<int, 0>{};
static constexpr int kSingleScaleSize = 1;
static constexpr int kDoubleScaleSize = 1;
static constexpr int kQuadrupleScaleSize = 1;
static constexpr ImplicitRegisterUse kImplicitRegisterUse =
implicit_register_use;
static constexpr int kOperandCount = 0;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_BYTECODE_TRAITS_H_

346
deps/v8/src/interpreter/bytecodes.cc vendored Normal file
View File

@ -0,0 +1,346 @@
// Copyright 2015 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.
#include "src/interpreter/bytecodes.h"
#include <iomanip>
#include "src/interpreter/bytecode-traits.h"
namespace v8 {
namespace internal {
namespace interpreter {
// clang-format off
const OperandType* const Bytecodes::kOperandTypes[] = {
#define ENTRY(Name, ...) BytecodeTraits<__VA_ARGS__>::kOperandTypes,
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
};
const OperandTypeInfo* const Bytecodes::kOperandTypeInfos[] = {
#define ENTRY(Name, ...) BytecodeTraits<__VA_ARGS__>::kOperandTypeInfos,
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
};
const int Bytecodes::kOperandCount[] = {
#define ENTRY(Name, ...) BytecodeTraits<__VA_ARGS__>::kOperandCount,
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
};
const ImplicitRegisterUse Bytecodes::kImplicitRegisterUse[] = {
#define ENTRY(Name, ...) BytecodeTraits<__VA_ARGS__>::kImplicitRegisterUse,
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
};
const uint8_t Bytecodes::kBytecodeSizes[3][kBytecodeCount] = {
{
#define ENTRY(Name, ...) BytecodeTraits<__VA_ARGS__>::kSingleScaleSize,
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
}, {
#define ENTRY(Name, ...) BytecodeTraits<__VA_ARGS__>::kDoubleScaleSize,
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
}, {
#define ENTRY(Name, ...) BytecodeTraits<__VA_ARGS__>::kQuadrupleScaleSize,
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
}
};
const OperandSize* const Bytecodes::kOperandSizes[3][kBytecodeCount] = {
{
#define ENTRY(Name, ...) \
BytecodeTraits<__VA_ARGS__>::kSingleScaleOperandSizes,
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
}, {
#define ENTRY(Name, ...) \
BytecodeTraits<__VA_ARGS__>::kDoubleScaleOperandSizes,
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
}, {
#define ENTRY(Name, ...) \
BytecodeTraits<__VA_ARGS__>::kQuadrupleScaleOperandSizes,
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
}
};
const int* const Bytecodes::kOperandOffsets[3][kBytecodeCount] = {
{
#define ENTRY(Name, ...) \
BytecodeTraits<__VA_ARGS__>::kSingleScaleOperandOffsets.data(),
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
}, {
#define ENTRY(Name, ...) \
BytecodeTraits<__VA_ARGS__>::kDoubleScaleOperandOffsets.data(),
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
}, {
#define ENTRY(Name, ...) \
BytecodeTraits<__VA_ARGS__>::kQuadrupleScaleOperandOffsets.data(),
BYTECODE_LIST(ENTRY, ENTRY)
#undef ENTRY
}
};
const OperandSize
Bytecodes::kOperandKindSizes[3][BytecodeOperands::kOperandTypeCount] = {
{
#define ENTRY(Name, ...) \
OperandScaler<OperandType::k##Name, OperandScale::kSingle>::kOperandSize,
OPERAND_TYPE_LIST(ENTRY)
#undef ENTRY
}, {
#define ENTRY(Name, ...) \
OperandScaler<OperandType::k##Name, OperandScale::kDouble>::kOperandSize,
OPERAND_TYPE_LIST(ENTRY)
#undef ENTRY
}, {
#define ENTRY(Name, ...) \
OperandScaler<OperandType::k##Name, OperandScale::kQuadruple>::kOperandSize,
OPERAND_TYPE_LIST(ENTRY)
#undef ENTRY
}
};
// clang-format on
// Make sure kFirstShortStar and kLastShortStar are set correctly.
#define ASSERT_SHORT_STAR_RANGE(Name, ...) \
static_assert(Bytecode::k##Name >= Bytecode::kFirstShortStar && \
Bytecode::k##Name <= Bytecode::kLastShortStar);
SHORT_STAR_BYTECODE_LIST(ASSERT_SHORT_STAR_RANGE)
#undef ASSERT_SHORT_STAR_RANGE
// static
const char* Bytecodes::ToString(Bytecode bytecode) {
switch (bytecode) {
#define CASE(Name, ...) \
case Bytecode::k##Name: \
return #Name;
BYTECODE_LIST(CASE, CASE)
#undef CASE
}
UNREACHABLE();
}
// static
std::string Bytecodes::ToString(Bytecode bytecode, OperandScale operand_scale,
const char* separator) {
std::string value(ToString(bytecode));
if (operand_scale > OperandScale::kSingle) {
Bytecode prefix_bytecode = OperandScaleToPrefixBytecode(operand_scale);
std::string suffix = ToString(prefix_bytecode);
return value.append(separator).append(suffix);
} else {
return value;
}
}
// static
Bytecode Bytecodes::GetDebugBreak(Bytecode bytecode) {
DCHECK(!IsDebugBreak(bytecode));
if (bytecode == Bytecode::kWide) {
return Bytecode::kDebugBreakWide;
}
if (bytecode == Bytecode::kExtraWide) {
return Bytecode::kDebugBreakExtraWide;
}
int bytecode_size = Size(bytecode, OperandScale::kSingle);
#define RETURN_IF_DEBUG_BREAK_SIZE_MATCHES(Name) \
if (bytecode_size == Size(Bytecode::k##Name, OperandScale::kSingle)) { \
return Bytecode::k##Name; \
}
DEBUG_BREAK_PLAIN_BYTECODE_LIST(RETURN_IF_DEBUG_BREAK_SIZE_MATCHES)
#undef RETURN_IF_DEBUG_BREAK_SIZE_MATCHES
UNREACHABLE();
}
// static
bool Bytecodes::IsDebugBreak(Bytecode bytecode) {
switch (bytecode) {
#define CASE(Name, ...) case Bytecode::k##Name:
DEBUG_BREAK_BYTECODE_LIST(CASE);
#undef CASE
return true;
default:
break;
}
return false;
}
// static
bool Bytecodes::IsRegisterOperandType(OperandType operand_type) {
switch (operand_type) {
#define CASE(Name, _) \
case OperandType::k##Name: \
return true;
REGISTER_OPERAND_TYPE_LIST(CASE)
#undef CASE
#define CASE(Name, _) \
case OperandType::k##Name: \
break;
NON_REGISTER_OPERAND_TYPE_LIST(CASE)
#undef CASE
}
return false;
}
// static
bool Bytecodes::IsRegisterListOperandType(OperandType operand_type) {
switch (operand_type) {
case OperandType::kRegList:
case OperandType::kRegOutList:
return true;
default:
return false;
}
}
bool Bytecodes::MakesCallAlongCriticalPath(Bytecode bytecode) {
if (IsCallOrConstruct(bytecode) || IsCallRuntime(bytecode)) return true;
switch (bytecode) {
case Bytecode::kCreateWithContext:
case Bytecode::kCreateBlockContext:
case Bytecode::kCreateCatchContext:
case Bytecode::kCreateRegExpLiteral:
case Bytecode::kGetIterator:
return true;
default:
return false;
}
}
// static
bool Bytecodes::IsRegisterInputOperandType(OperandType operand_type) {
switch (operand_type) {
#define CASE(Name, _) \
case OperandType::k##Name: \
return true;
REGISTER_INPUT_OPERAND_TYPE_LIST(CASE)
CASE(RegInOut, _)
#undef CASE
#define CASE(Name, _) \
case OperandType::k##Name: \
break;
NON_REGISTER_OPERAND_TYPE_LIST(CASE)
REGISTER_OUTPUT_OPERAND_TYPE_LIST(CASE)
#undef CASE
}
return false;
}
// static
bool Bytecodes::IsRegisterOutputOperandType(OperandType operand_type) {
switch (operand_type) {
#define CASE(Name, _) \
case OperandType::k##Name: \
return true;
REGISTER_OUTPUT_OPERAND_TYPE_LIST(CASE)
CASE(RegInOut, _)
#undef CASE
#define CASE(Name, _) \
case OperandType::k##Name: \
break;
NON_REGISTER_OPERAND_TYPE_LIST(CASE)
REGISTER_INPUT_OPERAND_TYPE_LIST(CASE)
#undef CASE
}
return false;
}
// static
bool Bytecodes::IsStarLookahead(Bytecode bytecode, OperandScale operand_scale) {
if (operand_scale == OperandScale::kSingle) {
switch (bytecode) {
// Short-star lookahead is required for correctness on kDebugBreak0. The
// handler for all short-star codes re-reads the opcode from the bytecode
// array and would not work correctly if it instead read kDebugBreak0.
case Bytecode::kDebugBreak0:
case Bytecode::kLdaZero:
case Bytecode::kLdaSmi:
case Bytecode::kLdaNull:
case Bytecode::kLdaTheHole:
case Bytecode::kLdaConstant:
case Bytecode::kLdaUndefined:
case Bytecode::kLdaGlobal:
case Bytecode::kGetNamedProperty:
case Bytecode::kGetKeyedProperty:
case Bytecode::kLdaContextSlot:
case Bytecode::kLdaImmutableContextSlot:
case Bytecode::kLdaCurrentContextSlot:
case Bytecode::kLdaImmutableCurrentContextSlot:
case Bytecode::kAdd:
case Bytecode::kSub:
case Bytecode::kMul:
case Bytecode::kAddSmi:
case Bytecode::kSubSmi:
case Bytecode::kInc:
case Bytecode::kDec:
case Bytecode::kTypeOf:
case Bytecode::kCallAnyReceiver:
case Bytecode::kCallProperty:
case Bytecode::kCallProperty0:
case Bytecode::kCallProperty1:
case Bytecode::kCallProperty2:
case Bytecode::kCallUndefinedReceiver:
case Bytecode::kCallUndefinedReceiver0:
case Bytecode::kCallUndefinedReceiver1:
case Bytecode::kCallUndefinedReceiver2:
case Bytecode::kConstruct:
case Bytecode::kConstructWithSpread:
case Bytecode::kCreateObjectLiteral:
case Bytecode::kCreateArrayLiteral:
case Bytecode::kThrowReferenceErrorIfHole:
case Bytecode::kGetTemplateObject:
return true;
default:
return false;
}
}
return false;
}
// static
bool Bytecodes::IsBytecodeWithScalableOperands(Bytecode bytecode) {
for (int i = 0; i < NumberOfOperands(bytecode); i++) {
if (OperandIsScalable(bytecode, i)) return true;
}
return false;
}
// static
bool Bytecodes::IsUnsignedOperandType(OperandType operand_type) {
switch (operand_type) {
#define CASE(Name, _) \
case OperandType::k##Name: \
return OperandTraits<OperandType::k##Name>::TypeInfoTraits::kIsUnsigned;
OPERAND_TYPE_LIST(CASE)
#undef CASE
}
UNREACHABLE();
}
// static
bool Bytecodes::BytecodeHasHandler(Bytecode bytecode,
OperandScale operand_scale) {
return (operand_scale == OperandScale::kSingle &&
(!IsShortStar(bytecode) || bytecode == Bytecode::kStar0)) ||
Bytecodes::IsBytecodeWithScalableOperands(bytecode);
}
std::ostream& operator<<(std::ostream& os, const Bytecode& bytecode) {
return os << Bytecodes::ToString(bytecode);
}
} // namespace interpreter
} // namespace internal
} // namespace v8

1132
deps/v8/src/interpreter/bytecodes.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,438 @@
// Copyright 2015 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.
#include "src/interpreter/constant-array-builder.h"
#include <cmath>
#include <functional>
#include <set>
#include "src/ast/ast-value-factory.h"
#include "src/ast/scopes.h"
#include "src/base/hashing.h"
#include "src/execution/isolate.h"
#include "src/handles/handles.h"
#include "src/heap/local-factory-inl.h"
#include "src/interpreter/bytecode-operands.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
namespace interpreter {
ConstantArrayBuilder::ConstantArraySlice::ConstantArraySlice(
Zone* zone, size_t start_index, size_t capacity, OperandSize operand_size)
: start_index_(start_index),
capacity_(capacity),
reserved_(0),
operand_size_(operand_size),
constants_(zone) {}
void ConstantArrayBuilder::ConstantArraySlice::Reserve() {
DCHECK_GT(available(), 0u);
reserved_++;
DCHECK_LE(reserved_, capacity() - constants_.size());
}
void ConstantArrayBuilder::ConstantArraySlice::Unreserve() {
DCHECK_GT(reserved_, 0u);
reserved_--;
}
size_t ConstantArrayBuilder::ConstantArraySlice::Allocate(
ConstantArrayBuilder::Entry entry, size_t count) {
DCHECK_GE(available(), count);
size_t index = constants_.size();
DCHECK_LT(index, capacity());
for (size_t i = 0; i < count; ++i) {
constants_.push_back(entry);
}
return index + start_index();
}
ConstantArrayBuilder::Entry& ConstantArrayBuilder::ConstantArraySlice::At(
size_t index) {
DCHECK_GE(index, start_index());
DCHECK_LT(index, start_index() + size());
return constants_[index - start_index()];
}
const ConstantArrayBuilder::Entry& ConstantArrayBuilder::ConstantArraySlice::At(
size_t index) const {
DCHECK_GE(index, start_index());
DCHECK_LT(index, start_index() + size());
return constants_[index - start_index()];
}
#if DEBUG
template <typename IsolateT>
void ConstantArrayBuilder::ConstantArraySlice::CheckAllElementsAreUnique(
IsolateT* isolate) const {
std::set<Tagged<Smi>> smis;
std::set<double> heap_numbers;
std::set<const AstRawString*> strings;
std::set<const AstConsString*> cons_strings;
std::set<const char*> bigints;
std::set<const Scope*> scopes;
std::set<Tagged<Object>, Object::Comparer> deferred_objects;
for (const Entry& entry : constants_) {
bool duplicate = false;
switch (entry.tag_) {
case Entry::Tag::kSmi:
duplicate = !smis.insert(entry.smi_).second;
break;
case Entry::Tag::kHeapNumber:
duplicate = !heap_numbers.insert(entry.heap_number_).second;
break;
case Entry::Tag::kRawString:
duplicate = !strings.insert(entry.raw_string_).second;
break;
case Entry::Tag::kConsString:
duplicate = !cons_strings.insert(entry.cons_string_).second;
break;
case Entry::Tag::kBigInt:
duplicate = !bigints.insert(entry.bigint_.c_str()).second;
break;
case Entry::Tag::kScope:
duplicate = !scopes.insert(entry.scope_).second;
break;
case Entry::Tag::kHandle:
duplicate = !deferred_objects.insert(*entry.handle_).second;
break;
case Entry::Tag::kDeferred:
UNREACHABLE(); // Should be kHandle at this point.
case Entry::Tag::kJumpTableSmi:
case Entry::Tag::kUninitializedJumpTableSmi:
// TODO(leszeks): Ignore jump tables because they have to be contiguous,
// so they can contain duplicates.
break;
#define CASE_TAG(NAME, ...) case Entry::Tag::k##NAME:
SINGLETON_CONSTANT_ENTRY_TYPES(CASE_TAG)
#undef CASE_TAG
// Singletons are non-duplicated by definition.
break;
}
if (duplicate) {
std::ostringstream os;
os << "Duplicate constant found: " << Brief(*entry.ToHandle(isolate))
<< std::endl;
// Print all the entries in the slice to help debug duplicates.
size_t i = start_index();
for (const Entry& prev_entry : constants_) {
os << i++ << ": " << Brief(*prev_entry.ToHandle(isolate)) << std::endl;
}
FATAL("%s", os.str().c_str());
}
}
}
#endif
STATIC_CONST_MEMBER_DEFINITION const size_t ConstantArrayBuilder::k8BitCapacity;
STATIC_CONST_MEMBER_DEFINITION const size_t
ConstantArrayBuilder::k16BitCapacity;
STATIC_CONST_MEMBER_DEFINITION const size_t
ConstantArrayBuilder::k32BitCapacity;
ConstantArrayBuilder::ConstantArrayBuilder(Zone* zone)
: constants_map_(16, base::KeyEqualityMatcher<intptr_t>(),
ZoneAllocationPolicy(zone)),
smi_map_(zone),
smi_pairs_(zone),
heap_number_map_(zone) {
idx_slice_[0] =
zone->New<ConstantArraySlice>(zone, 0, k8BitCapacity, OperandSize::kByte);
idx_slice_[1] = zone->New<ConstantArraySlice>(
zone, k8BitCapacity, k16BitCapacity, OperandSize::kShort);
idx_slice_[2] = zone->New<ConstantArraySlice>(
zone, k8BitCapacity + k16BitCapacity, k32BitCapacity, OperandSize::kQuad);
}
size_t ConstantArrayBuilder::size() const {
size_t i = arraysize(idx_slice_);
while (i > 0) {
ConstantArraySlice* slice = idx_slice_[--i];
if (slice->size() > 0) {
return slice->start_index() + slice->size();
}
}
return idx_slice_[0]->size();
}
ConstantArrayBuilder::ConstantArraySlice* ConstantArrayBuilder::IndexToSlice(
size_t index) const {
for (ConstantArraySlice* slice : idx_slice_) {
if (index <= slice->max_index()) {
return slice;
}
}
UNREACHABLE();
}
template <typename IsolateT>
MaybeHandle<Object> ConstantArrayBuilder::At(size_t index,
IsolateT* isolate) const {
const ConstantArraySlice* slice = IndexToSlice(index);
DCHECK_LT(index, slice->capacity());
if (index < slice->start_index() + slice->size()) {
const Entry& entry = slice->At(index);
if (!entry.IsDeferred()) return entry.ToHandle(isolate);
}
return MaybeHandle<Object>();
}
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
MaybeHandle<Object> ConstantArrayBuilder::At(size_t index,
Isolate* isolate) const;
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
MaybeHandle<Object> ConstantArrayBuilder::At(size_t index,
LocalIsolate* isolate) const;
template <typename IsolateT>
Handle<TrustedFixedArray> ConstantArrayBuilder::ToFixedArray(
IsolateT* isolate) {
Handle<TrustedFixedArray> fixed_array =
isolate->factory()->NewTrustedFixedArray(static_cast<int>(size()));
MemsetTagged(fixed_array->RawFieldOfFirstElement(),
*isolate->factory()->the_hole_value(), size());
int array_index = 0;
for (const ConstantArraySlice* slice : idx_slice_) {
DCHECK_EQ(slice->reserved(), 0);
DCHECK(array_index == 0 ||
base::bits::IsPowerOfTwo(static_cast<uint32_t>(array_index)));
#if DEBUG
// Different slices might contain the same element due to reservations, but
// all elements within a slice should be unique.
slice->CheckAllElementsAreUnique(isolate);
#endif
// Copy objects from slice into array.
for (size_t i = 0; i < slice->size(); ++i) {
DirectHandle<Object> value =
slice->At(slice->start_index() + i).ToHandle(isolate);
fixed_array->set(array_index++, *value);
}
// Leave holes where reservations led to unused slots.
size_t padding = slice->capacity() - slice->size();
if (static_cast<size_t>(fixed_array->length() - array_index) <= padding) {
break;
}
array_index += padding;
}
DCHECK_GE(array_index, fixed_array->length());
return fixed_array;
}
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
Handle<TrustedFixedArray> ConstantArrayBuilder::ToFixedArray(
Isolate* isolate);
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
Handle<TrustedFixedArray> ConstantArrayBuilder::ToFixedArray(
LocalIsolate* isolate);
size_t ConstantArrayBuilder::Insert(Tagged<Smi> smi) {
auto entry = smi_map_.find(smi);
if (entry == smi_map_.end()) {
return AllocateReservedEntry(smi);
}
return entry->second;
}
size_t ConstantArrayBuilder::Insert(double number) {
if (std::isnan(number)) return InsertNaN();
auto entry = heap_number_map_.find(number);
if (entry == heap_number_map_.end()) {
index_t index = static_cast<index_t>(AllocateIndex(Entry(number)));
heap_number_map_[number] = index;
return index;
}
return entry->second;
}
size_t ConstantArrayBuilder::Insert(const AstRawString* raw_string) {
return constants_map_
.LookupOrInsert(reinterpret_cast<intptr_t>(raw_string),
raw_string->Hash(),
[&]() { return AllocateIndex(Entry(raw_string)); })
->value;
}
size_t ConstantArrayBuilder::Insert(const AstConsString* cons_string) {
const AstRawString* last = cons_string->last();
uint32_t hash = last == nullptr ? 0 : last->Hash();
return constants_map_
.LookupOrInsert(reinterpret_cast<intptr_t>(cons_string), hash,
[&]() { return AllocateIndex(Entry(cons_string)); })
->value;
}
size_t ConstantArrayBuilder::Insert(AstBigInt bigint) {
return constants_map_
.LookupOrInsert(reinterpret_cast<intptr_t>(bigint.c_str()),
static_cast<uint32_t>(base::hash_value(bigint.c_str())),
[&]() { return AllocateIndex(Entry(bigint)); })
->value;
}
size_t ConstantArrayBuilder::Insert(const Scope* scope) {
return constants_map_
.LookupOrInsert(reinterpret_cast<intptr_t>(scope),
static_cast<uint32_t>(base::hash_value(scope)),
[&]() { return AllocateIndex(Entry(scope)); })
->value;
}
#define INSERT_ENTRY(NAME, LOWER_NAME) \
size_t ConstantArrayBuilder::Insert##NAME() { \
if (LOWER_NAME##_ < 0) { \
LOWER_NAME##_ = AllocateIndex(Entry::NAME()); \
} \
return LOWER_NAME##_; \
}
SINGLETON_CONSTANT_ENTRY_TYPES(INSERT_ENTRY)
#undef INSERT_ENTRY
ConstantArrayBuilder::index_t ConstantArrayBuilder::AllocateIndex(
ConstantArrayBuilder::Entry entry) {
return AllocateIndexArray(entry, 1);
}
ConstantArrayBuilder::index_t ConstantArrayBuilder::AllocateIndexArray(
ConstantArrayBuilder::Entry entry, size_t count) {
for (size_t i = 0; i < arraysize(idx_slice_); ++i) {
if (idx_slice_[i]->available() >= count) {
return static_cast<index_t>(idx_slice_[i]->Allocate(entry, count));
}
}
UNREACHABLE();
}
ConstantArrayBuilder::ConstantArraySlice*
ConstantArrayBuilder::OperandSizeToSlice(OperandSize operand_size) const {
ConstantArraySlice* slice = nullptr;
switch (operand_size) {
case OperandSize::kNone:
UNREACHABLE();
case OperandSize::kByte:
slice = idx_slice_[0];
break;
case OperandSize::kShort:
slice = idx_slice_[1];
break;
case OperandSize::kQuad:
slice = idx_slice_[2];
break;
}
DCHECK(slice->operand_size() == operand_size);
return slice;
}
size_t ConstantArrayBuilder::InsertDeferred() {
return AllocateIndex(Entry::Deferred());
}
size_t ConstantArrayBuilder::InsertJumpTable(size_t size) {
return AllocateIndexArray(Entry::UninitializedJumpTableSmi(), size);
}
void ConstantArrayBuilder::SetDeferredAt(size_t index, Handle<Object> object) {
ConstantArraySlice* slice = IndexToSlice(index);
return slice->At(index).SetDeferred(object);
}
void ConstantArrayBuilder::SetJumpTableSmi(size_t index, Tagged<Smi> smi) {
ConstantArraySlice* slice = IndexToSlice(index);
// Allow others to reuse these Smis, but insert using emplace to avoid
// overwriting existing values in the Smi map (which may have a smaller
// operand size).
smi_map_.emplace(smi, static_cast<index_t>(index));
return slice->At(index).SetJumpTableSmi(smi);
}
OperandSize ConstantArrayBuilder::CreateReservedEntry(
OperandSize minimum_operand_size) {
for (size_t i = 0; i < arraysize(idx_slice_); ++i) {
if (idx_slice_[i]->available() > 0 &&
idx_slice_[i]->operand_size() >= minimum_operand_size) {
idx_slice_[i]->Reserve();
return idx_slice_[i]->operand_size();
}
}
UNREACHABLE();
}
ConstantArrayBuilder::index_t ConstantArrayBuilder::AllocateReservedEntry(
Tagged<Smi> value) {
index_t index = static_cast<index_t>(AllocateIndex(Entry(value)));
smi_map_[value] = index;
return index;
}
size_t ConstantArrayBuilder::CommitReservedEntry(OperandSize operand_size,
Tagged<Smi> value) {
DiscardReservedEntry(operand_size);
size_t index;
auto entry = smi_map_.find(value);
if (entry == smi_map_.end()) {
index = AllocateReservedEntry(value);
} else {
ConstantArraySlice* slice = OperandSizeToSlice(operand_size);
index = entry->second;
if (index > slice->max_index()) {
// The object is already in the constant array, but may have an
// index too big for the reserved operand_size. So, duplicate
// entry with the smaller operand size.
index = AllocateReservedEntry(value);
}
DCHECK_LE(index, slice->max_index());
}
return index;
}
void ConstantArrayBuilder::DiscardReservedEntry(OperandSize operand_size) {
OperandSizeToSlice(operand_size)->Unreserve();
}
template <typename IsolateT>
Handle<Object> ConstantArrayBuilder::Entry::ToHandle(IsolateT* isolate) const {
switch (tag_) {
case Tag::kDeferred:
// We shouldn't have any deferred entries by now.
UNREACHABLE();
case Tag::kHandle:
return handle_;
case Tag::kSmi:
case Tag::kJumpTableSmi:
return handle(smi_, isolate);
case Tag::kUninitializedJumpTableSmi:
// TODO(leszeks): There's probably a better value we could use here.
return isolate->factory()->the_hole_value();
case Tag::kRawString:
return raw_string_->string();
case Tag::kConsString:
return cons_string_->AllocateFlat(isolate);
case Tag::kHeapNumber:
return isolate->factory()->template NewNumber<AllocationType::kOld>(
heap_number_);
case Tag::kBigInt:
// This should never fail: the parser will never create a BigInt
// literal that cannot be allocated.
return BigIntLiteral(isolate, bigint_.c_str()).ToHandleChecked();
case Tag::kScope:
return scope_->scope_info();
#define ENTRY_LOOKUP(Name, name) \
case Tag::k##Name: \
return isolate->factory()->name();
SINGLETON_CONSTANT_ENTRY_TYPES(ENTRY_LOOKUP);
#undef ENTRY_LOOKUP
}
UNREACHABLE();
}
template Handle<Object> ConstantArrayBuilder::Entry::ToHandle(
Isolate* isolate) const;
template Handle<Object> ConstantArrayBuilder::Entry::ToHandle(
LocalIsolate* isolate) const;
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,256 @@
// Copyright 2015 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_INTERPRETER_CONSTANT_ARRAY_BUILDER_H_
#define V8_INTERPRETER_CONSTANT_ARRAY_BUILDER_H_
#include "src/ast/ast-value-factory.h"
#include "src/common/globals.h"
#include "src/handles/handles.h"
#include "src/interpreter/bytecode-operands.h"
#include "src/objects/smi.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
class Isolate;
class AstRawString;
class AstValue;
namespace interpreter {
// Constant array entries that represent singletons.
#define SINGLETON_CONSTANT_ENTRY_TYPES(V) \
V(AsyncIteratorSymbol, async_iterator_symbol) \
V(ClassFieldsSymbol, class_fields_symbol) \
V(EmptyObjectBoilerplateDescription, empty_object_boilerplate_description) \
V(EmptyArrayBoilerplateDescription, empty_array_boilerplate_description) \
V(EmptyFixedArray, empty_fixed_array) \
V(IteratorSymbol, iterator_symbol) \
V(InterpreterTrampolineSymbol, interpreter_trampoline_symbol) \
V(NaN, nan_value)
// A helper class for constructing constant arrays for the
// interpreter. Each instance of this class is intended to be used to
// generate exactly one FixedArray of constants via the ToFixedArray
// method.
class V8_EXPORT_PRIVATE ConstantArrayBuilder final {
public:
// Capacity of the 8-bit operand slice.
static const size_t k8BitCapacity = 1u << kBitsPerByte;
// Capacity of the 16-bit operand slice.
static const size_t k16BitCapacity = (1u << 2 * kBitsPerByte) - k8BitCapacity;
// Capacity of the 32-bit operand slice.
static const size_t k32BitCapacity =
kMaxUInt32 - k16BitCapacity - k8BitCapacity + 1;
explicit ConstantArrayBuilder(Zone* zone);
// Generate a fixed array of constant handles based on inserted objects.
template <typename IsolateT>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
Handle<TrustedFixedArray> ToFixedArray(IsolateT* isolate);
// Returns the object, as a handle in |isolate|, that is in the constant pool
// array at index |index|. Returns null if there is no handle at this index.
// Only expected to be used in tests.
template <typename IsolateT>
EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE)
MaybeHandle<Object> At(size_t index, IsolateT* isolate) const;
// Returns the number of elements in the array.
size_t size() const;
// Insert an object into the constants array if it is not already present.
// Returns the array index associated with the object.
size_t Insert(Tagged<Smi> smi);
size_t Insert(double number);
size_t Insert(const AstRawString* raw_string);
size_t Insert(const AstConsString* cons_string);
size_t Insert(AstBigInt bigint);
size_t Insert(const Scope* scope);
#define INSERT_ENTRY(NAME, ...) size_t Insert##NAME();
SINGLETON_CONSTANT_ENTRY_TYPES(INSERT_ENTRY)
#undef INSERT_ENTRY
// Inserts an empty entry and returns the array index associated with the
// reservation. The entry's handle value can be inserted by calling
// SetDeferredAt().
size_t InsertDeferred();
// Inserts |size| consecutive empty entries and returns the array index
// associated with the first reservation. Each entry's Smi value can be
// inserted by calling SetJumpTableSmi().
size_t InsertJumpTable(size_t size);
// Sets the deferred value at |index| to |object|.
void SetDeferredAt(size_t index, Handle<Object> object);
// Sets the jump table entry at |index| to |smi|. Note that |index| is the
// constant pool index, not the switch case value.
void SetJumpTableSmi(size_t index, Tagged<Smi> smi);
// Creates a reserved entry in the constant pool and returns
// the size of the operand that'll be required to hold the entry
// when committed.
OperandSize CreateReservedEntry(
OperandSize minimum_operand_size = OperandSize::kNone);
// Commit reserved entry and returns the constant pool index for the
// SMI value.
size_t CommitReservedEntry(OperandSize operand_size, Tagged<Smi> value);
// Discards constant pool reservation.
void DiscardReservedEntry(OperandSize operand_size);
private:
using index_t = uint32_t;
struct ConstantArraySlice;
class Entry {
private:
enum class Tag : uint8_t;
public:
explicit Entry(Tagged<Smi> smi) : smi_(smi), tag_(Tag::kSmi) {}
explicit Entry(double heap_number)
: heap_number_(heap_number), tag_(Tag::kHeapNumber) {}
explicit Entry(const AstRawString* raw_string)
: raw_string_(raw_string), tag_(Tag::kRawString) {}
explicit Entry(const AstConsString* cons_string)
: cons_string_(cons_string), tag_(Tag::kConsString) {}
explicit Entry(AstBigInt bigint) : bigint_(bigint), tag_(Tag::kBigInt) {}
explicit Entry(const Scope* scope) : scope_(scope), tag_(Tag::kScope) {}
#define CONSTRUCT_ENTRY(NAME, LOWER_NAME) \
static Entry NAME() { return Entry(Tag::k##NAME); }
SINGLETON_CONSTANT_ENTRY_TYPES(CONSTRUCT_ENTRY)
#undef CONSTRUCT_ENTRY
static Entry Deferred() { return Entry(Tag::kDeferred); }
static Entry UninitializedJumpTableSmi() {
return Entry(Tag::kUninitializedJumpTableSmi);
}
bool IsDeferred() const { return tag_ == Tag::kDeferred; }
bool IsJumpTableEntry() const {
return tag_ == Tag::kUninitializedJumpTableSmi ||
tag_ == Tag::kJumpTableSmi;
}
void SetDeferred(Handle<Object> handle) {
DCHECK_EQ(tag_, Tag::kDeferred);
tag_ = Tag::kHandle;
handle_ = handle;
}
void SetJumpTableSmi(Tagged<Smi> smi) {
DCHECK_EQ(tag_, Tag::kUninitializedJumpTableSmi);
tag_ = Tag::kJumpTableSmi;
smi_ = smi;
}
template <typename IsolateT>
Handle<Object> ToHandle(IsolateT* isolate) const;
private:
explicit Entry(Tag tag) : tag_(tag) {}
union {
IndirectHandle<Object> handle_;
Tagged<Smi> smi_;
double heap_number_;
const AstRawString* raw_string_;
const AstConsString* cons_string_;
AstBigInt bigint_;
const Scope* scope_;
};
enum class Tag : uint8_t {
kDeferred,
kHandle,
kSmi,
kRawString,
kConsString,
kHeapNumber,
kBigInt,
kScope,
kUninitializedJumpTableSmi,
kJumpTableSmi,
#define ENTRY_TAG(NAME, ...) k##NAME,
SINGLETON_CONSTANT_ENTRY_TYPES(ENTRY_TAG)
#undef ENTRY_TAG
} tag_;
#if DEBUG
// Required by CheckAllElementsAreUnique().
friend struct ConstantArraySlice;
#endif
};
index_t AllocateIndex(Entry constant_entry);
index_t AllocateIndexArray(Entry constant_entry, size_t size);
index_t AllocateReservedEntry(Tagged<Smi> value);
struct ConstantArraySlice final : public ZoneObject {
ConstantArraySlice(Zone* zone, size_t start_index, size_t capacity,
OperandSize operand_size);
ConstantArraySlice(const ConstantArraySlice&) = delete;
ConstantArraySlice& operator=(const ConstantArraySlice&) = delete;
void Reserve();
void Unreserve();
size_t Allocate(Entry entry, size_t count = 1);
Entry& At(size_t index);
const Entry& At(size_t index) const;
#if DEBUG
template <typename IsolateT>
void CheckAllElementsAreUnique(IsolateT* isolate) const;
#endif
inline size_t available() const { return capacity() - reserved() - size(); }
inline size_t reserved() const { return reserved_; }
inline size_t capacity() const { return capacity_; }
inline size_t size() const { return constants_.size(); }
inline size_t start_index() const { return start_index_; }
inline size_t max_index() const { return start_index_ + capacity() - 1; }
inline OperandSize operand_size() const { return operand_size_; }
private:
const size_t start_index_;
const size_t capacity_;
size_t reserved_;
OperandSize operand_size_;
ZoneVector<Entry> constants_;
};
ConstantArraySlice* IndexToSlice(size_t index) const;
ConstantArraySlice* OperandSizeToSlice(OperandSize operand_size) const;
ConstantArraySlice* idx_slice_[3];
base::TemplateHashMapImpl<intptr_t, index_t,
base::KeyEqualityMatcher<intptr_t>,
ZoneAllocationPolicy>
constants_map_;
ZoneMap<Tagged<Smi>, index_t> smi_map_;
ZoneVector<std::pair<Tagged<Smi>, index_t>> smi_pairs_;
ZoneMap<double, index_t> heap_number_map_;
#define SINGLETON_ENTRY_FIELD(NAME, LOWER_NAME) int LOWER_NAME##_ = -1;
SINGLETON_CONSTANT_ENTRY_TYPES(SINGLETON_ENTRY_FIELD)
#undef SINGLETON_ENTRY_FIELD
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_CONSTANT_ARRAY_BUILDER_H_

View File

@ -0,0 +1,287 @@
// Copyright 2015 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.
#include "src/interpreter/control-flow-builders.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
namespace interpreter {
BreakableControlFlowBuilder::~BreakableControlFlowBuilder() {
BindBreakTarget();
DCHECK(break_labels_.empty() || break_labels_.is_bound());
if (block_coverage_builder_ != nullptr) {
block_coverage_builder_->IncrementBlockCounter(
node_, SourceRangeKind::kContinuation);
}
}
void BreakableControlFlowBuilder::BindBreakTarget() {
break_labels_.Bind(builder());
}
void BreakableControlFlowBuilder::EmitJump(BytecodeLabels* sites) {
builder()->Jump(sites->New());
}
void BreakableControlFlowBuilder::EmitJumpIfTrue(
BytecodeArrayBuilder::ToBooleanMode mode, BytecodeLabels* sites) {
builder()->JumpIfTrue(mode, sites->New());
}
void BreakableControlFlowBuilder::EmitJumpIfFalse(
BytecodeArrayBuilder::ToBooleanMode mode, BytecodeLabels* sites) {
builder()->JumpIfFalse(mode, sites->New());
}
void BreakableControlFlowBuilder::EmitJumpIfUndefined(BytecodeLabels* sites) {
builder()->JumpIfUndefined(sites->New());
}
void BreakableControlFlowBuilder::EmitJumpIfForInDone(BytecodeLabels* sites,
Register index,
Register cache_length) {
builder()->JumpIfForInDone(sites->New(), index, cache_length);
}
LoopBuilder::~LoopBuilder() {
DCHECK(continue_labels_.empty() || continue_labels_.is_bound());
DCHECK(end_labels_.empty() || end_labels_.is_bound());
}
void LoopBuilder::LoopHeader() {
// Jumps from before the loop header into the loop violate ordering
// requirements of bytecode basic blocks. The only entry into a loop
// must be the loop header. Surely breaks is okay? Not if nested
// and misplaced between the headers.
DCHECK(break_labels_.empty() && continue_labels_.empty() &&
end_labels_.empty());
builder()->Bind(&loop_header_);
}
void LoopBuilder::LoopBody() {
if (block_coverage_builder_ != nullptr) {
block_coverage_builder_->IncrementBlockCounter(block_coverage_body_slot_);
}
}
void LoopBuilder::JumpToHeader(int loop_depth, LoopBuilder* const parent_loop) {
BindLoopEnd();
if (parent_loop &&
loop_header_.offset() == parent_loop->loop_header_.offset()) {
// TurboFan can't cope with multiple loops that have the same loop header
// bytecode offset. If we have an inner loop with the same header offset
// than its parent loop, we do not create a JumpLoop bytecode. Instead, we
// Jump to our parent's JumpToHeader which in turn can be a JumpLoop or, iff
// they are a nested inner loop too, a Jump to its parent's JumpToHeader.
parent_loop->JumpToLoopEnd();
} else {
// Pass the proper loop depth to the backwards branch for triggering OSR.
// For purposes of OSR, the loop depth is capped at `kMaxOsrUrgency - 1`.
// Once that urgency is reached, all loops become OSR candidates.
//
// The loop must have closed form, i.e. all loop elements are within the
// loop, the loop header precedes the body and next elements in the loop.
int slot_index = feedback_vector_spec_->AddJumpLoopSlot().ToInt();
builder()->JumpLoop(
&loop_header_, std::min(loop_depth, FeedbackVector::kMaxOsrUrgency - 1),
source_position_, slot_index);
}
}
void LoopBuilder::BindContinueTarget() { continue_labels_.Bind(builder()); }
void LoopBuilder::BindLoopEnd() { end_labels_.Bind(builder()); }
SwitchBuilder::~SwitchBuilder() {
#ifdef DEBUG
for (auto site : case_sites_) {
DCHECK(!site.has_referrer_jump() || site.is_bound());
}
#endif
}
void SwitchBuilder::BindCaseTargetForJumpTable(int case_value,
CaseClause* clause) {
builder()->Bind(jump_table_, case_value);
BuildBlockCoverage(clause);
}
void SwitchBuilder::BindCaseTargetForCompareJump(int index,
CaseClause* clause) {
builder()->Bind(&case_sites_.at(index));
BuildBlockCoverage(clause);
}
void SwitchBuilder::JumpToCaseIfTrue(BytecodeArrayBuilder::ToBooleanMode mode,
int index) {
builder()->JumpIfTrue(mode, &case_sites_.at(index));
}
// Precondition: tag is in the accumulator
void SwitchBuilder::EmitJumpTableIfExists(
int min_case, int max_case, std::map<int, CaseClause*>& covered_cases) {
builder()->SwitchOnSmiNoFeedback(jump_table_);
fall_through_.Bind(builder());
for (int j = min_case; j <= max_case; ++j) {
if (covered_cases.find(j) == covered_cases.end()) {
this->BindCaseTargetForJumpTable(j, nullptr);
}
}
}
void SwitchBuilder::BindDefault(CaseClause* clause) {
default_.Bind(builder());
BuildBlockCoverage(clause);
}
void SwitchBuilder::JumpToDefault() { this->EmitJump(&default_); }
void SwitchBuilder::JumpToFallThroughIfFalse() {
this->EmitJumpIfFalse(BytecodeArrayBuilder::ToBooleanMode::kAlreadyBoolean,
&fall_through_);
}
TryCatchBuilder::~TryCatchBuilder() {
if (block_coverage_builder_ != nullptr) {
block_coverage_builder_->IncrementBlockCounter(
statement_, SourceRangeKind::kContinuation);
}
}
void TryCatchBuilder::BeginTry(Register context) {
builder()->MarkTryBegin(handler_id_, context);
}
void TryCatchBuilder::EndTry() {
builder()->MarkTryEnd(handler_id_);
builder()->Jump(&exit_);
builder()->MarkHandler(handler_id_, catch_prediction_);
if (block_coverage_builder_ != nullptr) {
block_coverage_builder_->IncrementBlockCounter(statement_,
SourceRangeKind::kCatch);
}
}
void TryCatchBuilder::EndCatch() { builder()->Bind(&exit_); }
TryFinallyBuilder::~TryFinallyBuilder() {
if (block_coverage_builder_ != nullptr) {
block_coverage_builder_->IncrementBlockCounter(
statement_, SourceRangeKind::kContinuation);
}
}
void TryFinallyBuilder::BeginTry(Register context) {
builder()->MarkTryBegin(handler_id_, context);
}
void TryFinallyBuilder::LeaveTry() {
builder()->Jump(finalization_sites_.New());
}
void TryFinallyBuilder::EndTry() {
builder()->MarkTryEnd(handler_id_);
}
void TryFinallyBuilder::BeginHandler() {
builder()->Bind(&handler_);
builder()->MarkHandler(handler_id_, catch_prediction_);
}
void TryFinallyBuilder::BeginFinally() {
finalization_sites_.Bind(builder());
if (block_coverage_builder_ != nullptr) {
block_coverage_builder_->IncrementBlockCounter(statement_,
SourceRangeKind::kFinally);
}
}
void TryFinallyBuilder::EndFinally() {
// Nothing to be done here.
}
ConditionalChainControlFlowBuilder::~ConditionalChainControlFlowBuilder() {
end_labels_.Bind(builder());
#ifdef DEBUG
DCHECK(end_labels_.empty() || end_labels_.is_bound());
for (auto* label : then_labels_list_) {
DCHECK(label->empty() || label->is_bound());
}
for (auto* label : else_labels_list_) {
DCHECK(label->empty() || label->is_bound());
}
#endif
}
void ConditionalChainControlFlowBuilder::JumpToEnd() {
builder()->Jump(end_labels_.New());
}
void ConditionalChainControlFlowBuilder::ThenAt(size_t index) {
DCHECK_LT(index, then_labels_list_.length());
then_labels_at(index)->Bind(builder());
if (block_coverage_builder_) {
block_coverage_builder_->IncrementBlockCounter(
block_coverage_then_slot_at(index));
}
}
void ConditionalChainControlFlowBuilder::ElseAt(size_t index) {
DCHECK_LT(index, else_labels_list_.length());
else_labels_at(index)->Bind(builder());
if (block_coverage_builder_) {
block_coverage_builder_->IncrementBlockCounter(
block_coverage_else_slot_at(index));
}
}
ConditionalControlFlowBuilder::~ConditionalControlFlowBuilder() {
if (!else_labels_.is_bound()) else_labels_.Bind(builder());
end_labels_.Bind(builder());
DCHECK(end_labels_.empty() || end_labels_.is_bound());
DCHECK(then_labels_.empty() || then_labels_.is_bound());
DCHECK(else_labels_.empty() || else_labels_.is_bound());
// IfStatement requires a continuation counter, Conditional does not (as it
// can only contain expressions).
if (block_coverage_builder_ != nullptr && node_->IsIfStatement()) {
block_coverage_builder_->IncrementBlockCounter(
node_, SourceRangeKind::kContinuation);
}
}
void ConditionalControlFlowBuilder::JumpToEnd() {
DCHECK(end_labels_.empty()); // May only be called once.
builder()->Jump(end_labels_.New());
}
void ConditionalControlFlowBuilder::Then() {
then_labels()->Bind(builder());
if (block_coverage_builder_ != nullptr) {
block_coverage_builder_->IncrementBlockCounter(block_coverage_then_slot_);
}
}
void ConditionalControlFlowBuilder::Else() {
else_labels()->Bind(builder());
if (block_coverage_builder_ != nullptr) {
block_coverage_builder_->IncrementBlockCounter(block_coverage_else_slot_);
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,386 @@
// Copyright 2015 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_INTERPRETER_CONTROL_FLOW_BUILDERS_H_
#define V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_
#include <map>
#include "src/ast/ast-source-ranges.h"
#include "src/interpreter/block-coverage-builder.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/bytecode-generator.h"
#include "src/interpreter/bytecode-jump-table.h"
#include "src/interpreter/bytecode-label.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
namespace interpreter {
class V8_EXPORT_PRIVATE ControlFlowBuilder {
public:
explicit ControlFlowBuilder(BytecodeArrayBuilder* builder)
: builder_(builder) {}
ControlFlowBuilder(const ControlFlowBuilder&) = delete;
ControlFlowBuilder& operator=(const ControlFlowBuilder&) = delete;
virtual ~ControlFlowBuilder() = default;
protected:
BytecodeArrayBuilder* builder() const { return builder_; }
private:
BytecodeArrayBuilder* builder_;
};
class V8_EXPORT_PRIVATE BreakableControlFlowBuilder
: public ControlFlowBuilder {
public:
BreakableControlFlowBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder,
AstNode* node)
: ControlFlowBuilder(builder),
break_labels_(builder->zone()),
node_(node),
block_coverage_builder_(block_coverage_builder) {}
~BreakableControlFlowBuilder() override;
// This method is called when visiting break statements in the AST.
// Inserts a jump to an unbound label that is patched when the corresponding
// BindBreakTarget is called.
void Break() { EmitJump(&break_labels_); }
void BreakIfTrue(BytecodeArrayBuilder::ToBooleanMode mode) {
EmitJumpIfTrue(mode, &break_labels_);
}
void BreakIfForInDone(Register index, Register cache_length) {
EmitJumpIfForInDone(&break_labels_, index, cache_length);
}
BytecodeLabels* break_labels() { return &break_labels_; }
protected:
void EmitJump(BytecodeLabels* labels);
void EmitJumpIfTrue(BytecodeArrayBuilder::ToBooleanMode mode,
BytecodeLabels* labels);
void EmitJumpIfFalse(BytecodeArrayBuilder::ToBooleanMode mode,
BytecodeLabels* labels);
void EmitJumpIfUndefined(BytecodeLabels* labels);
void EmitJumpIfForInDone(BytecodeLabels* labels, Register index,
Register cache_length);
// Called from the destructor to update sites that emit jumps for break.
void BindBreakTarget();
// Unbound labels that identify jumps for break statements in the code.
BytecodeLabels break_labels_;
// A continuation counter (for block coverage) is needed e.g. when
// encountering a break statement.
AstNode* node_;
BlockCoverageBuilder* block_coverage_builder_;
};
// Class to track control flow for block statements (which can break in JS).
class V8_EXPORT_PRIVATE BlockBuilder final
: public BreakableControlFlowBuilder {
public:
BlockBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder,
BreakableStatement* statement)
: BreakableControlFlowBuilder(builder, block_coverage_builder,
statement) {}
};
// A class to help with co-ordinating break and continue statements with
// their loop.
class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
public:
LoopBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder, AstNode* node,
FeedbackVectorSpec* feedback_vector_spec)
: BreakableControlFlowBuilder(builder, block_coverage_builder, node),
continue_labels_(builder->zone()),
end_labels_(builder->zone()),
feedback_vector_spec_(feedback_vector_spec) {
if (block_coverage_builder_ != nullptr) {
block_coverage_body_slot_ =
block_coverage_builder_->AllocateBlockCoverageSlot(
node, SourceRangeKind::kBody);
}
source_position_ = node ? node->position() : kNoSourcePosition;
}
~LoopBuilder() override;
void LoopHeader();
void LoopBody();
void JumpToHeader(int loop_depth, LoopBuilder* const parent_loop);
void BindContinueTarget();
// This method is called when visiting continue statements in the AST.
// Inserts a jump to an unbound label that is patched when BindContinueTarget
// is called.
void Continue() { EmitJump(&continue_labels_); }
void ContinueIfUndefined() { EmitJumpIfUndefined(&continue_labels_); }
private:
// Emit a Jump to our parent_loop_'s end label which could be a JumpLoop or,
// iff they are a nested inner loop with the same loop header bytecode offset
// as their parent's, a Jump to its parent's end label.
void JumpToLoopEnd() { EmitJump(&end_labels_); }
void BindLoopEnd();
BytecodeLoopHeader loop_header_;
// Unbound labels that identify jumps for continue statements in the code and
// jumps from checking the loop condition to the header for do-while loops.
BytecodeLabels continue_labels_;
// Unbound labels that identify jumps for nested inner loops which share the
// same header offset as this loop. Said inner loops will Jump to our end
// label, which could be a JumpLoop or, iff we are a nested inner loop too, a
// Jump to our parent's end label.
BytecodeLabels end_labels_;
int block_coverage_body_slot_;
int source_position_;
FeedbackVectorSpec* const feedback_vector_spec_;
};
// A class to help with co-ordinating break statements with their switch.
class V8_EXPORT_PRIVATE SwitchBuilder final
: public BreakableControlFlowBuilder {
public:
SwitchBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder,
SwitchStatement* statement, int number_of_cases,
BytecodeJumpTable* jump_table)
: BreakableControlFlowBuilder(builder, block_coverage_builder, statement),
case_sites_(builder->zone()),
default_(builder->zone()),
fall_through_(builder->zone()),
jump_table_(jump_table) {
case_sites_.resize(number_of_cases);
}
~SwitchBuilder() override;
void BindCaseTargetForJumpTable(int case_value, CaseClause* clause);
void BindCaseTargetForCompareJump(int index, CaseClause* clause);
// This method is called when visiting case comparison operation for |index|.
// Inserts a JumpIfTrue with ToBooleanMode |mode| to a unbound label that is
// patched when the corresponding SetCaseTarget is called.
void JumpToCaseIfTrue(BytecodeArrayBuilder::ToBooleanMode mode, int index);
void EmitJumpTableIfExists(int min_case, int max_case,
std::map<int, CaseClause*>& covered_cases);
void BindDefault(CaseClause* clause);
void JumpToDefault();
void JumpToFallThroughIfFalse();
private:
// Unbound labels that identify jumps for case statements in the code.
ZoneVector<BytecodeLabel> case_sites_;
BytecodeLabels default_;
BytecodeLabels fall_through_;
BytecodeJumpTable* jump_table_;
void BuildBlockCoverage(CaseClause* clause) {
if (block_coverage_builder_ && clause != nullptr) {
block_coverage_builder_->IncrementBlockCounter(clause,
SourceRangeKind::kBody);
}
}
};
// A class to help with co-ordinating control flow in try-catch statements.
class V8_EXPORT_PRIVATE TryCatchBuilder final : public ControlFlowBuilder {
public:
TryCatchBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder,
TryCatchStatement* statement,
HandlerTable::CatchPrediction catch_prediction)
: ControlFlowBuilder(builder),
handler_id_(builder->NewHandlerEntry()),
catch_prediction_(catch_prediction),
block_coverage_builder_(block_coverage_builder),
statement_(statement) {}
~TryCatchBuilder() override;
void BeginTry(Register context);
void EndTry();
void EndCatch();
private:
int handler_id_;
HandlerTable::CatchPrediction catch_prediction_;
BytecodeLabel exit_;
BlockCoverageBuilder* block_coverage_builder_;
TryCatchStatement* statement_;
};
// A class to help with co-ordinating control flow in try-finally statements.
class V8_EXPORT_PRIVATE TryFinallyBuilder final : public ControlFlowBuilder {
public:
TryFinallyBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder,
TryFinallyStatement* statement,
HandlerTable::CatchPrediction catch_prediction)
: ControlFlowBuilder(builder),
handler_id_(builder->NewHandlerEntry()),
catch_prediction_(catch_prediction),
finalization_sites_(builder->zone()),
block_coverage_builder_(block_coverage_builder),
statement_(statement) {}
~TryFinallyBuilder() override;
void BeginTry(Register context);
void LeaveTry();
void EndTry();
void BeginHandler();
void BeginFinally();
void EndFinally();
private:
int handler_id_;
HandlerTable::CatchPrediction catch_prediction_;
BytecodeLabel handler_;
// Unbound labels that identify jumps to the finally block in the code.
BytecodeLabels finalization_sites_;
BlockCoverageBuilder* block_coverage_builder_;
TryFinallyStatement* statement_;
};
class V8_EXPORT_PRIVATE ConditionalChainControlFlowBuilder final
: public ControlFlowBuilder {
public:
ConditionalChainControlFlowBuilder(
BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder, AstNode* node,
size_t then_count)
: ControlFlowBuilder(builder),
end_labels_(builder->zone()),
then_count_(then_count),
then_labels_list_(static_cast<int>(then_count_), builder->zone()),
else_labels_list_(static_cast<int>(then_count_), builder->zone()),
block_coverage_then_slots_(then_count_, builder->zone()),
block_coverage_else_slots_(then_count_, builder->zone()),
block_coverage_builder_(block_coverage_builder) {
DCHECK(node->IsConditionalChain());
Zone* zone = builder->zone();
for (size_t i = 0; i < then_count_; ++i) {
then_labels_list_.Add(zone->New<BytecodeLabels>(zone), zone);
else_labels_list_.Add(zone->New<BytecodeLabels>(zone), zone);
}
if (block_coverage_builder != nullptr) {
ConditionalChain* conditional_chain = node->AsConditionalChain();
block_coverage_then_slots_.resize(then_count_);
block_coverage_else_slots_.resize(then_count_);
for (size_t i = 0; i < then_count_; ++i) {
block_coverage_then_slots_[i] =
block_coverage_builder->AllocateConditionalChainBlockCoverageSlot(
conditional_chain, SourceRangeKind::kThen, i);
block_coverage_else_slots_[i] =
block_coverage_builder->AllocateConditionalChainBlockCoverageSlot(
conditional_chain, SourceRangeKind::kElse, i);
}
}
}
~ConditionalChainControlFlowBuilder() override;
BytecodeLabels* then_labels_at(size_t index) {
DCHECK_LT(index, then_count_);
return then_labels_list_[static_cast<int>(index)];
}
BytecodeLabels* else_labels_at(size_t index) {
DCHECK_LT(index, then_count_);
return else_labels_list_[static_cast<int>(index)];
}
int block_coverage_then_slot_at(size_t index) const {
DCHECK_LT(index, then_count_);
return block_coverage_then_slots_[index];
}
int block_coverage_else_slot_at(size_t index) const {
DCHECK_LT(index, then_count_);
return block_coverage_else_slots_[index];
}
void ThenAt(size_t index);
void ElseAt(size_t index);
void JumpToEnd();
private:
BytecodeLabels end_labels_;
size_t then_count_;
ZonePtrList<BytecodeLabels> then_labels_list_;
ZonePtrList<BytecodeLabels> else_labels_list_;
ZoneVector<int> block_coverage_then_slots_;
ZoneVector<int> block_coverage_else_slots_;
BlockCoverageBuilder* block_coverage_builder_;
};
class V8_EXPORT_PRIVATE ConditionalControlFlowBuilder final
: public ControlFlowBuilder {
public:
ConditionalControlFlowBuilder(BytecodeArrayBuilder* builder,
BlockCoverageBuilder* block_coverage_builder,
AstNode* node)
: ControlFlowBuilder(builder),
end_labels_(builder->zone()),
then_labels_(builder->zone()),
else_labels_(builder->zone()),
node_(node),
block_coverage_builder_(block_coverage_builder) {
DCHECK(node->IsIfStatement() || node->IsConditional());
if (block_coverage_builder != nullptr) {
block_coverage_then_slot_ =
block_coverage_builder->AllocateBlockCoverageSlot(
node, SourceRangeKind::kThen);
block_coverage_else_slot_ =
block_coverage_builder->AllocateBlockCoverageSlot(
node, SourceRangeKind::kElse);
}
}
~ConditionalControlFlowBuilder() override;
BytecodeLabels* then_labels() { return &then_labels_; }
BytecodeLabels* else_labels() { return &else_labels_; }
void Then();
void Else();
void JumpToEnd();
private:
BytecodeLabels end_labels_;
BytecodeLabels then_labels_;
BytecodeLabels else_labels_;
AstNode* node_;
int block_coverage_then_slot_;
int block_coverage_else_slot_;
BlockCoverageBuilder* block_coverage_builder_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_

View File

@ -0,0 +1,79 @@
// Copyright 2016 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.
#include "src/interpreter/handler-table-builder.h"
#include "src/execution/isolate.h"
#include "src/heap/factory.h"
#include "src/interpreter/bytecode-register.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
namespace interpreter {
HandlerTableBuilder::HandlerTableBuilder(Zone* zone) : entries_(zone) {}
template <typename IsolateT>
DirectHandle<TrustedByteArray> HandlerTableBuilder::ToHandlerTable(
IsolateT* isolate) {
int handler_table_size = static_cast<int>(entries_.size());
DirectHandle<TrustedByteArray> table_byte_array =
isolate->factory()->NewTrustedByteArray(
HandlerTable::LengthForRange(handler_table_size));
HandlerTable table(*table_byte_array);
for (int i = 0; i < handler_table_size; ++i) {
Entry& entry = entries_[i];
HandlerTable::CatchPrediction pred = entry.catch_prediction_;
table.SetRangeStart(i, static_cast<int>(entry.offset_start));
table.SetRangeEnd(i, static_cast<int>(entry.offset_end));
table.SetRangeHandler(i, static_cast<int>(entry.offset_target), pred);
table.SetRangeData(i, entry.context.index());
}
return table_byte_array;
}
template DirectHandle<TrustedByteArray> HandlerTableBuilder::ToHandlerTable(
Isolate* isolate);
template DirectHandle<TrustedByteArray> HandlerTableBuilder::ToHandlerTable(
LocalIsolate* isolate);
int HandlerTableBuilder::NewHandlerEntry() {
int handler_id = static_cast<int>(entries_.size());
Entry entry = {0, 0, 0, Register::invalid_value(), HandlerTable::UNCAUGHT};
entries_.push_back(entry);
return handler_id;
}
void HandlerTableBuilder::SetTryRegionStart(int handler_id, size_t offset) {
DCHECK(Smi::IsValid(offset)); // Encoding of handler table requires this.
entries_[handler_id].offset_start = offset;
}
void HandlerTableBuilder::SetTryRegionEnd(int handler_id, size_t offset) {
DCHECK(Smi::IsValid(offset)); // Encoding of handler table requires this.
entries_[handler_id].offset_end = offset;
}
void HandlerTableBuilder::SetHandlerTarget(int handler_id, size_t offset) {
DCHECK(Smi::IsValid(offset)); // Encoding of handler table requires this.
entries_[handler_id].offset_target = offset;
}
void HandlerTableBuilder::SetPrediction(
int handler_id, HandlerTable::CatchPrediction prediction) {
entries_[handler_id].catch_prediction_ = prediction;
}
void HandlerTableBuilder::SetContextRegister(int handler_id, Register reg) {
entries_[handler_id].context = reg;
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,62 @@
// Copyright 2016 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_INTERPRETER_HANDLER_TABLE_BUILDER_H_
#define V8_INTERPRETER_HANDLER_TABLE_BUILDER_H_
#include "src/codegen/handler-table.h"
#include "src/interpreter/bytecode-register.h"
#include "src/objects/fixed-array.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
class HandlerTable;
namespace interpreter {
// A helper class for constructing exception handler tables for the interpreter.
class V8_EXPORT_PRIVATE HandlerTableBuilder final {
public:
explicit HandlerTableBuilder(Zone* zone);
HandlerTableBuilder(const HandlerTableBuilder&) = delete;
HandlerTableBuilder& operator=(const HandlerTableBuilder&) = delete;
// Builds the actual handler table by copying the current values into a heap
// object. Any further mutations to the builder won't be reflected.
template <typename IsolateT>
DirectHandle<TrustedByteArray> ToHandlerTable(IsolateT* isolate);
// Creates a new handler table entry and returns a {hander_id} identifying the
// entry, so that it can be referenced by below setter functions.
int NewHandlerEntry();
// Setter functions that modify certain values within the handler table entry
// being referenced by the given {handler_id}. All values will be encoded by
// the resulting {HandlerTable} class when copied into the heap.
void SetTryRegionStart(int handler_id, size_t offset);
void SetTryRegionEnd(int handler_id, size_t offset);
void SetHandlerTarget(int handler_id, size_t offset);
void SetPrediction(int handler_id, HandlerTable::CatchPrediction prediction);
void SetContextRegister(int handler_id, Register reg);
private:
struct Entry {
size_t offset_start; // Bytecode offset starting try-region.
size_t offset_end; // Bytecode offset ending try-region.
size_t offset_target; // Bytecode offset of handler target.
Register context; // Register holding context for handler.
// Optimistic prediction for handler.
HandlerTable::CatchPrediction catch_prediction_;
};
ZoneVector<Entry> entries_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_HANDLER_TABLE_BUILDER_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,476 @@
// Copyright 2015 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_INTERPRETER_INTERPRETER_ASSEMBLER_H_
#define V8_INTERPRETER_INTERPRETER_ASSEMBLER_H_
#include "src/codegen/code-stub-assembler.h"
#include "src/common/globals.h"
#include "src/interpreter/bytecode-register.h"
#include "src/interpreter/bytecodes.h"
#include "src/objects/bytecode-array.h"
#include "src/runtime/runtime.h"
namespace v8 {
namespace internal {
namespace interpreter {
class V8_EXPORT_PRIVATE InterpreterAssembler : public CodeStubAssembler {
public:
InterpreterAssembler(compiler::CodeAssemblerState* state, Bytecode bytecode,
OperandScale operand_scale);
~InterpreterAssembler();
InterpreterAssembler(const InterpreterAssembler&) = delete;
InterpreterAssembler& operator=(const InterpreterAssembler&) = delete;
// Returns the 32-bit unsigned count immediate for bytecode operand
// |operand_index| in the current bytecode.
TNode<Uint32T> BytecodeOperandCount(int operand_index);
// Returns the 32-bit unsigned flag for bytecode operand |operand_index|
// in the current bytecode.
TNode<Uint32T> BytecodeOperandFlag8(int operand_index);
// Returns the 32-bit unsigned 2-byte flag for bytecode operand
// |operand_index| in the current bytecode.
TNode<Uint32T> BytecodeOperandFlag16(int operand_index);
// Returns the 32-bit zero-extended index immediate for bytecode operand
// |operand_index| in the current bytecode.
TNode<Uint32T> BytecodeOperandIdxInt32(int operand_index);
// Returns the word zero-extended index immediate for bytecode operand
// |operand_index| in the current bytecode.
TNode<UintPtrT> BytecodeOperandIdx(int operand_index);
// Returns the smi index immediate for bytecode operand |operand_index|
// in the current bytecode.
TNode<Smi> BytecodeOperandIdxSmi(int operand_index);
// Returns the TaggedIndex immediate for bytecode operand |operand_index|
// in the current bytecode.
TNode<TaggedIndex> BytecodeOperandIdxTaggedIndex(int operand_index);
// Returns the 32-bit unsigned immediate for bytecode operand |operand_index|
// in the current bytecode.
TNode<Uint32T> BytecodeOperandUImm(int operand_index);
// Returns the word-size unsigned immediate for bytecode operand
// |operand_index| in the current bytecode.
TNode<UintPtrT> BytecodeOperandUImmWord(int operand_index);
// Returns the unsigned smi immediate for bytecode operand |operand_index| in
// the current bytecode.
TNode<Smi> BytecodeOperandUImmSmi(int operand_index);
// Returns the 32-bit signed immediate for bytecode operand |operand_index|
// in the current bytecode.
TNode<Int32T> BytecodeOperandImm(int operand_index);
// Returns the word-size signed immediate for bytecode operand |operand_index|
// in the current bytecode.
TNode<IntPtrT> BytecodeOperandImmIntPtr(int operand_index);
// Returns the smi immediate for bytecode operand |operand_index| in the
// current bytecode.
TNode<Smi> BytecodeOperandImmSmi(int operand_index);
// Returns the 32-bit unsigned runtime id immediate for bytecode operand
// |operand_index| in the current bytecode.
TNode<Uint32T> BytecodeOperandRuntimeId(int operand_index);
// Returns the word zero-extended native context index immediate for bytecode
// operand |operand_index| in the current bytecode.
TNode<UintPtrT> BytecodeOperandNativeContextIndex(int operand_index);
// Returns the 32-bit unsigned intrinsic id immediate for bytecode operand
// |operand_index| in the current bytecode.
TNode<Uint32T> BytecodeOperandIntrinsicId(int operand_index);
// Accumulator.
TNode<Object> GetAccumulator();
void SetAccumulator(TNode<Object> value);
void ClobberAccumulator(TNode<Object> clobber_value);
// Context.
TNode<Context> GetContext();
void SetContext(TNode<Context> value);
// Context at |depth| in the context chain starting at |context|.
TNode<Context> GetContextAtDepth(TNode<Context> context,
TNode<Uint32T> depth);
// A RegListNodePair provides an abstraction over lists of registers.
class RegListNodePair {
public:
RegListNodePair(TNode<IntPtrT> base_reg_location, TNode<Word32T> reg_count)
: base_reg_location_(base_reg_location), reg_count_(reg_count) {}
TNode<Word32T> reg_count() const { return reg_count_; }
TNode<IntPtrT> base_reg_location() const { return base_reg_location_; }
private:
TNode<IntPtrT> base_reg_location_;
TNode<Word32T> reg_count_;
};
// Backup/restore register file to/from a fixed array of the correct length.
// There is an asymmetry between suspend/export and resume/import.
// - Suspend copies arguments and registers to the generator.
// - Resume copies only the registers from the generator, the arguments
// are copied by the ResumeGenerator trampoline.
TNode<FixedArray> ExportParametersAndRegisterFile(
TNode<FixedArray> array, const RegListNodePair& registers);
TNode<FixedArray> ImportRegisterFile(TNode<FixedArray> array,
const RegListNodePair& registers);
// Loads from and stores to the interpreter register file.
TNode<Object> LoadRegister(Register reg);
TNode<IntPtrT> LoadAndUntagRegister(Register reg);
TNode<Object> LoadRegisterAtOperandIndex(int operand_index);
std::pair<TNode<Object>, TNode<Object>> LoadRegisterPairAtOperandIndex(
int operand_index);
void StoreRegister(TNode<Object> value, Register reg);
void StoreRegisterAtOperandIndex(TNode<Object> value, int operand_index);
void StoreRegisterPairAtOperandIndex(TNode<Object> value1,
TNode<Object> value2, int operand_index);
void StoreRegisterTripleAtOperandIndex(TNode<Object> value1,
TNode<Object> value2,
TNode<Object> value3,
int operand_index);
RegListNodePair GetRegisterListAtOperandIndex(int operand_index);
TNode<Object> LoadRegisterFromRegisterList(const RegListNodePair& reg_list,
int index);
TNode<IntPtrT> RegisterLocationInRegisterList(const RegListNodePair& reg_list,
int index);
// Load constant at the index specified in operand |operand_index| from the
// constant pool.
TNode<Object> LoadConstantPoolEntryAtOperandIndex(int operand_index);
// Load and untag constant at the index specified in operand |operand_index|
// from the constant pool.
TNode<IntPtrT> LoadAndUntagConstantPoolEntryAtOperandIndex(int operand_index);
// Load constant at |index| in the constant pool.
TNode<Object> LoadConstantPoolEntry(TNode<WordT> index);
// Load and untag constant at |index| in the constant pool.
TNode<IntPtrT> LoadAndUntagConstantPoolEntry(TNode<WordT> index);
TNode<JSFunction> LoadFunctionClosure();
// Load the FeedbackVector for the current function. The returned node could
// be undefined.
TNode<Union<FeedbackVector, Undefined>> LoadFeedbackVector();
auto LoadFeedbackVectorOrUndefinedIfJitless() {
#ifndef V8_JITLESS
return LoadFeedbackVector();
#else
return UndefinedConstant();
#endif // V8_JITLESS
}
static constexpr UpdateFeedbackMode DefaultUpdateFeedbackMode() {
#ifndef V8_JITLESS
return UpdateFeedbackMode::kOptionalFeedback;
#else
return UpdateFeedbackMode::kNoFeedback;
#endif // !V8_JITLESS
}
// Call JSFunction or Callable |function| with |args| arguments, possibly
// including the receiver depending on |receiver_mode|. After the call returns
// directly dispatches to the next bytecode.
void CallJSAndDispatch(TNode<JSAny> function, TNode<Context> context,
const RegListNodePair& args,
ConvertReceiverMode receiver_mode);
// Call JSFunction or Callable |function| with |arg_count| arguments (not
// including receiver) passed as |args|, possibly including the receiver
// depending on |receiver_mode|. After the call returns directly dispatches to
// the next bytecode.
template <class... TArgs>
void CallJSAndDispatch(TNode<JSAny> function, TNode<Context> context,
TNode<Word32T> arg_count,
ConvertReceiverMode receiver_mode, TArgs... args);
// Call JSFunction or Callable |function| with |args|
// arguments (not including receiver), and the final argument being spread.
// After the call returns directly dispatches to the next bytecode.
void CallJSWithSpreadAndDispatch(TNode<JSAny> function,
TNode<Context> context,
const RegListNodePair& args,
TNode<UintPtrT> slot_id);
// Call constructor |target| with |args| arguments (not including receiver).
// The |new_target| is the same as the |target| for the new keyword, but
// differs for the super keyword.
TNode<Object> Construct(
TNode<JSAny> target, TNode<Context> context, TNode<JSAny> new_target,
const RegListNodePair& args, TNode<UintPtrT> slot_id,
TNode<Union<FeedbackVector, Undefined>> maybe_feedback_vector);
// Call constructor |target| with |args| arguments (not including
// receiver). The last argument is always a spread. The |new_target| is the
// same as the |target| for the new keyword, but differs for the super
// keyword.
TNode<Object> ConstructWithSpread(TNode<JSAny> target, TNode<Context> context,
TNode<JSAny> new_target,
const RegListNodePair& args,
TNode<UintPtrT> slot_id);
// Call constructor |target|, forwarding all arguments in the current JS
// frame.
TNode<Object> ConstructForwardAllArgs(TNode<JSAny> target,
TNode<Context> context,
TNode<JSAny> new_target,
TNode<TaggedIndex> slot_id);
// Call runtime function with |args| arguments.
template <class T = Object>
TNode<T> CallRuntimeN(TNode<Uint32T> function_id, TNode<Context> context,
const RegListNodePair& args, int return_count);
// Jump forward relative to the current bytecode by the |jump_offset|.
void Jump(TNode<IntPtrT> jump_offset);
// Jump backward relative to the current bytecode by the |jump_offset|.
void JumpBackward(TNode<IntPtrT> jump_offset);
// Jump forward relative to the current bytecode by |jump_offset| if the
// word values |lhs| and |rhs| are equal.
void JumpIfTaggedEqual(TNode<Object> lhs, TNode<Object> rhs,
TNode<IntPtrT> jump_offset);
// Jump forward relative to the current bytecode by offest specified in
// operand |operand_index| if the word values |lhs| and |rhs| are equal.
void JumpIfTaggedEqual(TNode<Object> lhs, TNode<Object> rhs,
int operand_index);
// Jump forward relative to the current bytecode by offest specified from the
// constant pool if the word values |lhs| and |rhs| are equal.
// The constant's index is specified in operand |operand_index|.
void JumpIfTaggedEqualConstant(TNode<Object> lhs, TNode<Object> rhs,
int operand_index);
// Jump forward relative to the current bytecode by |jump_offset| if the
// word values |lhs| and |rhs| are not equal.
void JumpIfTaggedNotEqual(TNode<Object> lhs, TNode<Object> rhs,
TNode<IntPtrT> jump_offset);
// Jump forward relative to the current bytecode by offest specified in
// operand |operand_index| if the word values |lhs| and |rhs| are not equal.
void JumpIfTaggedNotEqual(TNode<Object> lhs, TNode<Object> rhs,
int operand_index);
// Jump forward relative to the current bytecode by offest specified from the
// constant pool if the word values |lhs| and |rhs| are not equal.
// The constant's index is specified in operand |operand_index|.
void JumpIfTaggedNotEqualConstant(TNode<Object> lhs, TNode<Object> rhs,
int operand_index);
// Updates the profiler interrupt budget for a return.
void UpdateInterruptBudgetOnReturn();
// Adjusts the interrupt budget by the provided weight. Returns the new
// budget.
TNode<Int32T> UpdateInterruptBudget(TNode<Int32T> weight);
// Decrements the bytecode array's interrupt budget by a 32-bit unsigned
// |weight| and calls Runtime::kInterrupt if counter reaches zero.
enum StackCheckBehavior {
kEnableStackCheck,
kDisableStackCheck,
};
void DecreaseInterruptBudget(TNode<Int32T> weight,
StackCheckBehavior stack_check_behavior);
TNode<Int8T> LoadOsrState(TNode<FeedbackVector> feedback_vector);
// Dispatch to the bytecode.
void Dispatch();
// Dispatch bytecode as wide operand variant.
void DispatchWide(OperandScale operand_scale);
// Dispatch to |target_bytecode| at |new_bytecode_offset|.
// |target_bytecode| should be equivalent to loading from the offset.
void DispatchToBytecode(TNode<WordT> target_bytecode,
TNode<IntPtrT> new_bytecode_offset);
// Dispatches to |target_bytecode| at BytecodeOffset(). Includes short-star
// lookahead if the current bytecode_ is likely followed by a short-star
// instruction.
void DispatchToBytecodeWithOptionalStarLookahead(
TNode<WordT> target_bytecode);
// Abort with the given abort reason.
void Abort(AbortReason abort_reason);
void AbortIfWordNotEqual(TNode<WordT> lhs, TNode<WordT> rhs,
AbortReason abort_reason);
// Abort if |register_count| is invalid for given register file array.
void AbortIfRegisterCountInvalid(TNode<FixedArray> parameters_and_registers,
TNode<IntPtrT> parameter_count,
TNode<UintPtrT> register_count);
// Attempts to OSR.
enum OnStackReplacementParams {
kBaselineCodeIsCached,
kDefault,
};
void OnStackReplacement(TNode<Context> context,
TNode<FeedbackVector> feedback_vector,
TNode<IntPtrT> relative_jump,
TNode<Int32T> loop_depth,
TNode<IntPtrT> feedback_slot, TNode<Int8T> osr_state,
OnStackReplacementParams params);
// The BytecodeOffset() is the offset from the ByteCodeArray pointer; to
// translate into runtime `BytecodeOffset` (defined in utils.h as the offset
// from the start of the bytecode section), this constant has to be applied.
static constexpr int kFirstBytecodeOffset =
BytecodeArray::kHeaderSize - kHeapObjectTag;
// Returns the offset from the BytecodeArrayPointer of the current bytecode.
TNode<IntPtrT> BytecodeOffset();
protected:
Bytecode bytecode() const { return bytecode_; }
static bool TargetSupportsUnalignedAccess();
void ToNumberOrNumeric(Object::Conversion mode);
void StoreRegisterForShortStar(TNode<Object> value, TNode<WordT> opcode);
// Load the bytecode at |bytecode_offset|.
TNode<WordT> LoadBytecode(TNode<IntPtrT> bytecode_offset);
// Load the parameter count of the current function from its BytecodeArray.
TNode<IntPtrT> LoadParameterCountWithoutReceiver();
private:
// Returns a pointer to the current function's BytecodeArray object.
TNode<BytecodeArray> BytecodeArrayTaggedPointer();
// Returns a pointer to first entry in the interpreter dispatch table.
TNode<ExternalReference> DispatchTablePointer();
// Returns the accumulator value without checking whether bytecode
// uses it. This is intended to be used only in dispatch and in
// tracing as these need to bypass accumulator use validity checks.
TNode<Object> GetAccumulatorUnchecked();
// Returns the frame pointer for the interpreted frame of the function being
// interpreted.
TNode<RawPtrT> GetInterpretedFramePointer();
// Operations on registers.
TNode<IntPtrT> RegisterLocation(Register reg);
TNode<IntPtrT> RegisterLocation(TNode<IntPtrT> reg_index);
TNode<IntPtrT> NextRegister(TNode<IntPtrT> reg_index);
TNode<Object> LoadRegister(TNode<IntPtrT> reg_index);
void StoreRegister(TNode<Object> value, TNode<IntPtrT> reg_index);
// Saves and restores interpreter bytecode offset to the interpreter stack
// frame when performing a call.
void CallPrologue();
void CallEpilogue();
// Increment the dispatch counter for the (current, next) bytecode pair.
void TraceBytecodeDispatch(TNode<WordT> target_bytecode);
// Traces the current bytecode by calling |function_id|.
void TraceBytecode(Runtime::FunctionId function_id);
// Returns the offset of register |index| relative to RegisterFilePointer().
TNode<IntPtrT> RegisterFrameOffset(TNode<IntPtrT> index);
// Returns the offset of an operand relative to the current bytecode offset.
TNode<IntPtrT> OperandOffset(int operand_index);
// Returns a value built from an sequence of bytes in the bytecode
// array starting at |relative_offset| from the current bytecode.
// The |result_type| determines the size and signedness. of the
// value read. This method should only be used on architectures that
// do not support unaligned memory accesses.
TNode<Word32T> BytecodeOperandReadUnaligned(int relative_offset,
MachineType result_type);
// Returns zero- or sign-extended to word32 value of the operand.
TNode<Uint8T> BytecodeOperandUnsignedByte(int operand_index);
TNode<Int8T> BytecodeOperandSignedByte(int operand_index);
TNode<Uint16T> BytecodeOperandUnsignedShort(int operand_index);
TNode<Int16T> BytecodeOperandSignedShort(int operand_index);
TNode<Uint32T> BytecodeOperandUnsignedQuad(int operand_index);
TNode<Int32T> BytecodeOperandSignedQuad(int operand_index);
// Returns zero- or sign-extended to word32 value of the operand of
// given size.
TNode<Int32T> BytecodeSignedOperand(int operand_index,
OperandSize operand_size);
TNode<Uint32T> BytecodeUnsignedOperand(int operand_index,
OperandSize operand_size);
// Returns the word-size sign-extended register index for bytecode operand
// |operand_index| in the current bytecode.
TNode<IntPtrT> BytecodeOperandReg(int operand_index);
// Returns the word zero-extended index immediate for bytecode operand
// |operand_index| in the current bytecode for use when loading a constant
// pool element.
TNode<UintPtrT> BytecodeOperandConstantPoolIdx(int operand_index);
// Jump to a specific bytecode offset.
void JumpToOffset(TNode<IntPtrT> new_bytecode_offset);
// Jump forward relative to the current bytecode by |jump_offset| if the
// |condition| is true. Helper function for JumpIfTaggedEqual and
// JumpIfTaggedNotEqual.
void JumpConditional(TNode<BoolT> condition, TNode<IntPtrT> jump_offset);
// Jump forward relative to the current bytecode by offest specified in
// operand |operand_index| if the |condition| is true. Helper function for
// JumpIfTaggedEqual and JumpIfTaggedNotEqual.
void JumpConditionalByImmediateOperand(TNode<BoolT> condition,
int operand_index);
// Jump forward relative to the current bytecode by offest specified from the
// constant pool if the |condition| is true. The constant's index is specified
// in operand |operand_index|. Helper function for JumpIfTaggedEqualConstant
// and JumpIfTaggedNotEqualConstant.
void JumpConditionalByConstantOperand(TNode<BoolT> condition,
int operand_index);
// Save the bytecode offset to the interpreter frame.
void SaveBytecodeOffset();
// Reload the bytecode offset from the interpreter frame.
TNode<IntPtrT> ReloadBytecodeOffset();
// Updates and returns BytecodeOffset() advanced by the current bytecode's
// size. Traces the exit of the current bytecode.
TNode<IntPtrT> Advance();
// Updates and returns BytecodeOffset() advanced by delta bytecodes.
// Traces the exit of the current bytecode.
TNode<IntPtrT> Advance(int delta);
TNode<IntPtrT> Advance(TNode<IntPtrT> delta);
// Look ahead for short Star and inline it in a branch, including subsequent
// dispatch. Anything after this point can assume that the following
// instruction was not a short Star.
void StarDispatchLookahead(TNode<WordT> target_bytecode);
// Build code for short Star at the current BytecodeOffset() and Advance() to
// the next dispatch offset.
void InlineShortStar(TNode<WordT> target_bytecode);
// Dispatch to the bytecode handler with code entry point |handler_entry|.
void DispatchToBytecodeHandlerEntry(TNode<RawPtrT> handler_entry,
TNode<IntPtrT> bytecode_offset);
int CurrentBytecodeSize() const;
OperandScale operand_scale() const { return operand_scale_; }
Bytecode bytecode_;
OperandScale operand_scale_;
CodeStubAssembler::TVariable<RawPtrT> interpreted_frame_pointer_;
CodeStubAssembler::TVariable<BytecodeArray> bytecode_array_;
CodeStubAssembler::TVariable<IntPtrT> bytecode_offset_;
CodeStubAssembler::TVariable<ExternalReference> dispatch_table_;
CodeStubAssembler::TVariable<Object> accumulator_;
ImplicitRegisterUse implicit_register_use_;
bool made_call_;
bool reloaded_frame_ptr_;
bool bytecode_array_valid_;
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_INTERPRETER_ASSEMBLER_H_

View File

@ -0,0 +1,351 @@
// Copyright 2024 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.
#include "src/interpreter/interpreter-generator-tsa.h"
#include "src/builtins/number-builtins-reducer-inl.h"
#include "src/codegen/turboshaft-builtins-assembler-inl.h"
#include "src/compiler/linkage.h"
namespace v8::internal::interpreter {
#include "src/compiler/turboshaft/define-assembler-macros.inc"
using namespace compiler::turboshaft; // NOLINT(build/namespaces)
#define IGNITION_HANDLER_TS(Name, BaseAssembler) \
class Name##AssemblerTS : public BaseAssembler { \
public: \
using Base = BaseAssembler; \
Name##AssemblerTS(compiler::turboshaft::PipelineData* data, \
Isolate* isolate, compiler::turboshaft::Graph& graph, \
Zone* phase_zone) \
: Base(data, graph, phase_zone) {} \
Name##AssemblerTS(const Name##AssemblerTS&) = delete; \
Name##AssemblerTS& operator=(const Name##AssemblerTS&) = delete; \
void Generate##Name##Impl(); \
}; \
void Name##AssemblerTS_Generate( \
compiler::turboshaft::PipelineData* data, Isolate* isolate, \
compiler::turboshaft::Graph& graph, Zone* zone) { \
Name##AssemblerTS assembler(data, isolate, graph, zone); \
assembler.EmitBytecodeHandlerProlog(); \
compiler::turboshaft::Block* catch_block = assembler.NewBlock(); \
Name##AssemblerTS::CatchScope catch_scope(assembler, catch_block); \
assembler.Generate##Name##Impl(); \
assembler.EmitEpilog(catch_block); \
} \
void Name##AssemblerTS::Generate##Name##Impl()
template <typename Next>
class BytecodeHandlerReducer : public Next {
public:
BUILTIN_REDUCER(BytecodeHandler)
~BytecodeHandlerReducer() {
// If the following check fails the handler does not use the
// accumulator in the way described in the bytecode definitions in
// bytecodes.h.
DCHECK_EQ(data_.implicit_register_use,
Bytecodes::GetImplicitRegisterUse(data_.bytecode));
}
void InitializeParameters(V<Object> accumulator,
V<BytecodeArray> bytecode_array,
V<WordPtr> bytecode_offset,
V<WordPtr> dispatch_table) {
accumulator_ = accumulator;
bytecode_array_ = bytecode_array;
bytecode_offset_ = bytecode_offset;
dispatch_table_ = dispatch_table;
}
V<Object> GetAccumulator() {
DCHECK(Bytecodes::ReadsAccumulator(data_.bytecode));
TrackRegisterUse(ImplicitRegisterUse::kReadAccumulator);
return accumulator_;
}
void SetAccumulator(V<Object> value) {
DCHECK(Bytecodes::WritesAccumulator(data_.bytecode));
TrackRegisterUse(ImplicitRegisterUse::kWriteAccumulator);
accumulator_ = value;
}
V<Context> GetContext() {
return V<Context>::Cast(LoadRegister(Register::current_context()));
}
void Dispatch() {
__ CodeComment("========= Dispatch");
DCHECK_IMPLIES(Bytecodes::MakesCallAlongCriticalPath(data_.bytecode),
data_.made_call);
V<WordPtr> target_offset = Advance(CurrentBytecodeSize());
V<WordPtr> target_bytecode = LoadBytecode(target_offset);
DispatchToBytecodeWithOptionalStarLookahead(target_bytecode);
}
void DispatchToBytecodeWithOptionalStarLookahead(V<WordPtr> target_bytecode) {
if (Bytecodes::IsStarLookahead(data_.bytecode, operand_scale())) {
StarDispatchLookahead(target_bytecode);
}
DispatchToBytecode(target_bytecode, BytecodeOffset());
}
void DispatchToBytecode(V<WordPtr> target_bytecode,
V<WordPtr> new_bytecode_offset) {
#ifdef V8_IGNITION_DISPATCH_COUNTING
TraceBytecodeDispatch(target_bytecode);
#endif
static_assert(kSystemPointerSizeLog2 ==
MemoryRepresentation::UintPtr().SizeInBytesLog2());
V<WordPtr> target_code_entry =
__ LoadOffHeap(DispatchTablePointer(), target_bytecode, 0,
MemoryRepresentation::UintPtr());
DispatchToBytecodeHandlerEntry(target_code_entry, new_bytecode_offset);
}
void DispatchToBytecodeHandlerEntry(V<WordPtr> handler_entry,
V<WordPtr> bytecode_offset) {
TailCallBytecodeDispatch(
InterpreterDispatchDescriptor{}, handler_entry, accumulator_.Get(),
bytecode_offset, BytecodeArrayTaggedPointer(), DispatchTablePointer());
}
void StarDispatchLookahead(V<WordPtr> target_bytecode) { UNIMPLEMENTED(); }
template <typename... Args>
void TailCallBytecodeDispatch(const CallInterfaceDescriptor& descriptor,
V<WordPtr> target, Args... args) {
DCHECK_EQ(descriptor.GetParameterCount(), sizeof...(Args));
auto call_descriptor = compiler::Linkage::GetBytecodeDispatchCallDescriptor(
graph_zone_, descriptor, descriptor.GetStackParameterCount());
auto ts_call_descriptor =
TSCallDescriptor::Create(call_descriptor, compiler::CanThrow::kNo,
compiler::LazyDeoptOnThrow::kNo, graph_zone_);
std::initializer_list<const OpIndex> arguments{args...};
__ TailCall(target, base::VectorOf(arguments), ts_call_descriptor);
}
V<WordPtr> Advance(ConstOrV<WordPtr> delta) {
V<WordPtr> next_offset = __ WordPtrAdd(BytecodeOffset(), delta);
bytecode_offset_ = next_offset;
return next_offset;
}
V<Object> LoadRegister(Register reg) {
const int offset = reg.ToOperand() * kSystemPointerSize;
return __ LoadOffHeap(GetInterpretedFramePointer(), offset,
MemoryRepresentation::AnyTagged());
}
V<WordPtr> GetInterpretedFramePointer() {
if (!interpreted_frame_pointer_.Get().valid()) {
interpreted_frame_pointer_ = __ ParentFramePointer();
} else if (Bytecodes::MakesCallAlongCriticalPath(data_.bytecode) &&
data_.made_call && data_.reloaded_frame_ptr) {
interpreted_frame_pointer_ = __ ParentFramePointer();
data_.reloaded_frame_ptr = true;
}
return interpreted_frame_pointer_;
}
V<WordPtr> BytecodeOffset() {
if (Bytecodes::MakesCallAlongCriticalPath(data_.bytecode) &&
data_.made_call && (bytecode_offset_ == bytecode_offset_parameter_)) {
bytecode_offset_ = ReloadBytecodeOffset();
}
return bytecode_offset_;
}
V<WordPtr> ReloadBytecodeOffset() {
V<WordPtr> offset = LoadAndUntagRegister(Register::bytecode_offset());
if (operand_scale() == OperandScale::kSingle) {
return offset;
}
// Add one to the offset such that it points to the actual bytecode rather
// than the Wide / ExtraWide prefix bytecode.
return __ WordPtrAdd(offset, 1);
}
V<Word32> LoadFromBytecodeArrayAt(MemoryRepresentation loaded_rep,
V<WordPtr> bytecode_offset,
int additional_offset = 0) {
return __ Load(BytecodeArrayTaggedPointer(), bytecode_offset,
LoadOp::Kind::TaggedBase(), loaded_rep,
additional_offset + kHeapObjectTag);
}
V<WordPtr> LoadBytecode(V<WordPtr> bytecode_offset) {
V<Word32> bytecode = __ Load(BytecodeArrayTaggedPointer(), bytecode_offset,
LoadOp::Kind::TaggedBase(),
MemoryRepresentation::Uint8(), kHeapObjectTag);
return __ ChangeUint32ToUintPtr(bytecode);
}
V<WordPtr> LoadAndUntagRegister(Register reg) {
V<WordPtr> base = GetInterpretedFramePointer();
int index = reg.ToOperand() * kSystemPointerSize;
if (SmiValuesAre32Bits()) {
#if V8_TARGET_LITTLE_ENDIAN
index += 4;
#endif
return __ ChangeInt32ToIntPtr(
__ LoadOffHeap(base, index, MemoryRepresentation::Int32()));
} else {
return __ ChangeInt32ToIntPtr(__ UntagSmi(
__ LoadOffHeap(base, index, MemoryRepresentation::TaggedSigned())));
}
}
// TODO(nicohartmann): Consider providing a V<ExternalReference>.
V<WordPtr> DispatchTablePointer() {
if (Bytecodes::MakesCallAlongCriticalPath(data_.bytecode) &&
data_.made_call && (dispatch_table_ == dispatch_table_parameter_)) {
dispatch_table_ = __ ExternalConstant(
ExternalReference::interpreter_dispatch_table_address(isolate_));
}
return dispatch_table_;
}
V<BytecodeArray> BytecodeArrayTaggedPointer() {
// Force a re-load of the bytecode array after every call in case the
// debugger has been activated.
if (!data_.bytecode_array_valid) {
bytecode_array_ = LoadRegister(Register::bytecode_array());
data_.bytecode_array_valid = true;
}
return V<BytecodeArray>::Cast(bytecode_array_);
}
V<Word32> BytecodeOperandIdxInt32(int operand_index) {
DCHECK_EQ(OperandType::kIdx,
Bytecodes::GetOperandType(data_.bytecode, operand_index));
OperandSize operand_size = Bytecodes::GetOperandSize(
data_.bytecode, operand_index, operand_scale());
return BytecodeUnsignedOperand(operand_index, operand_size);
}
V<Word32> BytecodeUnsignedOperand(int operand_index,
OperandSize operand_size) {
return BytecodeOperand(operand_index, operand_size);
}
V<Word32> BytecodeOperand(int operand_index, OperandSize operand_size) {
DCHECK_LT(operand_index, Bytecodes::NumberOfOperands(bytecode()));
DCHECK_EQ(operand_size, Bytecodes::GetOperandSize(bytecode(), operand_index,
operand_scale()));
MemoryRepresentation loaded_rep;
switch (operand_size) {
case OperandSize::kByte:
loaded_rep = MemoryRepresentation::Uint8();
break;
case OperandSize::kShort:
loaded_rep = MemoryRepresentation::Uint16();
break;
case OperandSize::kQuad:
loaded_rep = MemoryRepresentation::Uint32();
break;
case OperandSize::kNone:
UNREACHABLE();
}
return LoadFromBytecodeArrayAt(loaded_rep, BytecodeOffset(),
OperandOffset(operand_index));
}
int OperandOffset(int operand_index) const {
return Bytecodes::GetOperandOffset(bytecode(), operand_index,
operand_scale());
}
private:
Bytecode bytecode() const { return data_.bytecode; }
OperandScale operand_scale() const { return data_.operand_scale; }
int CurrentBytecodeSize() const {
return Bytecodes::Size(data_.bytecode, data_.operand_scale);
}
void TrackRegisterUse(ImplicitRegisterUse use) {
data_.implicit_register_use = data_.implicit_register_use | use;
}
Isolate* isolate_ = __ data() -> isolate();
ZoneWithName<compiler::kGraphZoneName>& graph_zone_ =
__ data() -> graph_zone();
BytecodeHandlerData& data_ = *__ data() -> bytecode_handler_data();
// TODO(nicohartmann): Replace with Var<T>s.
OpIndex bytecode_offset_parameter_;
OpIndex dispatch_table_parameter_;
template <typename T>
using Var = compiler::turboshaft::Var<T, assembler_t>;
Var<Object> accumulator_{this};
Var<WordPtr> interpreted_frame_pointer_{this};
Var<WordPtr> bytecode_offset_{this};
Var<Object> bytecode_array_{this};
Var<WordPtr> dispatch_table_{this};
};
template <template <typename> typename Reducer>
class TurboshaftBytecodeHandlerAssembler
: public compiler::turboshaft::TSAssembler<
Reducer, BytecodeHandlerReducer, BuiltinsReducer,
FeedbackCollectorReducer,
compiler::turboshaft::MachineLoweringReducer,
compiler::turboshaft::VariableReducer> {
public:
using Base = compiler::turboshaft::TSAssembler<
Reducer, BytecodeHandlerReducer, BuiltinsReducer,
FeedbackCollectorReducer, compiler::turboshaft::MachineLoweringReducer,
compiler::turboshaft::VariableReducer>;
TurboshaftBytecodeHandlerAssembler(compiler::turboshaft::PipelineData* data,
compiler::turboshaft::Graph& graph,
Zone* phase_zone)
: Base(data, graph, graph, phase_zone) {}
using Base::Asm;
void EmitBytecodeHandlerProlog() {
// Bind an entry block.
__ Bind(__ NewBlock());
// Initialize parameters.
V<Object> acc = __ template Parameter<Object>(
InterpreterDispatchDescriptor::kAccumulator);
V<WordPtr> bytecode_offset = __ template Parameter<WordPtr>(
InterpreterDispatchDescriptor::kBytecodeOffset);
V<BytecodeArray> bytecode_array = __ template Parameter<BytecodeArray>(
InterpreterDispatchDescriptor::kBytecodeArray);
V<WordPtr> dispatch_table = __ template Parameter<WordPtr>(
InterpreterDispatchDescriptor::kDispatchTable);
__ InitializeParameters(acc, bytecode_array, bytecode_offset,
dispatch_table);
}
};
using NumberBuiltinsBytecodeHandlerAssembler =
TurboshaftBytecodeHandlerAssembler<NumberBuiltinsReducer>;
IGNITION_HANDLER_TS(BitwiseNot, NumberBuiltinsBytecodeHandlerAssembler) {
V<Object> value = GetAccumulator();
V<Context> context = GetContext();
constexpr int kSlotIndex = 0;
SetFeedbackSlot(
__ ChangeUint32ToUintPtr(__ BytecodeOperandIdxInt32(kSlotIndex)));
LoadFeedbackVectorOrUndefinedIfJitless();
V<Object> result = BitwiseNot(context, value);
SetAccumulator(result);
UpdateFeedback();
Dispatch();
}
#include "src/compiler/turboshaft/undef-assembler-macros.inc"
} // namespace v8::internal::interpreter

View File

@ -0,0 +1,16 @@
// Copyright 2024 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_INTERPRETER_INTERPRETER_GENERATOR_TSA_H_
#define V8_INTERPRETER_INTERPRETER_GENERATOR_TSA_H_
#include "src/compiler/turboshaft/builtin-compiler.h"
namespace v8::internal::interpreter {
using BytecodeHandlerData = compiler::turboshaft::BytecodeHandlerData;
} // namespace v8::internal::interpreter
#endif // V8_INTERPRETER_INTERPRETER_GENERATOR_TSA_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
// Copyright 2017 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_INTERPRETER_INTERPRETER_GENERATOR_H_
#define V8_INTERPRETER_INTERPRETER_GENERATOR_H_
#include "src/interpreter/bytecode-operands.h"
#include "src/interpreter/bytecodes.h"
namespace v8 {
namespace internal {
namespace compiler {
class CodeAssemblerState;
}
struct AssemblerOptions;
enum class Builtin;
namespace interpreter {
extern void GenerateBytecodeHandler(compiler::CodeAssemblerState* state,
Bytecode bytecode,
OperandScale operand_scale);
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_INTERPRETER_GENERATOR_H_

View File

@ -0,0 +1,273 @@
// Copyright 2017 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.
#include "src/interpreter/interpreter-intrinsics-generator.h"
#include "src/builtins/builtins.h"
#include "src/heap/factory-inl.h"
#include "src/interpreter/interpreter-assembler.h"
#include "src/interpreter/interpreter-intrinsics.h"
#include "src/objects/js-generator.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
namespace interpreter {
#include "src/codegen/define-code-stub-assembler-macros.inc"
class IntrinsicsGenerator {
public:
explicit IntrinsicsGenerator(InterpreterAssembler* assembler)
: isolate_(assembler->isolate()),
zone_(assembler->zone()),
assembler_(assembler) {}
IntrinsicsGenerator(const IntrinsicsGenerator&) = delete;
IntrinsicsGenerator& operator=(const IntrinsicsGenerator&) = delete;
TNode<Object> InvokeIntrinsic(
TNode<Uint32T> function_id, TNode<Context> context,
const InterpreterAssembler::RegListNodePair& args);
private:
TNode<Object> IntrinsicAsBuiltinCall(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
Builtin name, int arg_count);
void AbortIfArgCountMismatch(int expected, TNode<Word32T> actual);
#define DECLARE_INTRINSIC_HELPER(name, lower_case, count) \
TNode<Object> name(const InterpreterAssembler::RegListNodePair& args, \
TNode<Context> context, int arg_count);
INTRINSICS_LIST(DECLARE_INTRINSIC_HELPER)
#undef DECLARE_INTRINSIC_HELPER
Isolate* isolate() { return isolate_; }
Zone* zone() { return zone_; }
Factory* factory() { return isolate()->factory(); }
Isolate* isolate_;
Zone* zone_;
InterpreterAssembler* assembler_;
};
TNode<Object> GenerateInvokeIntrinsic(
InterpreterAssembler* assembler, TNode<Uint32T> function_id,
TNode<Context> context, const InterpreterAssembler::RegListNodePair& args) {
IntrinsicsGenerator generator(assembler);
return generator.InvokeIntrinsic(function_id, context, args);
}
#define __ assembler_->
TNode<Object> IntrinsicsGenerator::InvokeIntrinsic(
TNode<Uint32T> function_id, TNode<Context> context,
const InterpreterAssembler::RegListNodePair& args) {
InterpreterAssembler::Label abort(assembler_), end(assembler_);
InterpreterAssembler::TVariable<Object> result(assembler_);
#define MAKE_LABEL(name, lower_case, count) \
InterpreterAssembler::Label lower_case(assembler_);
INTRINSICS_LIST(MAKE_LABEL)
#undef MAKE_LABEL
#define LABEL_POINTER(name, lower_case, count) &lower_case,
InterpreterAssembler::Label* labels[] = {INTRINSICS_LIST(LABEL_POINTER)};
#undef LABEL_POINTER
#define CASE(name, lower_case, count) \
static_cast<int32_t>(IntrinsicsHelper::IntrinsicId::k##name),
int32_t cases[] = {INTRINSICS_LIST(CASE)};
#undef CASE
__ Switch(function_id, &abort, cases, labels, arraysize(cases));
#define HANDLE_CASE(name, lower_case, expected_arg_count) \
__ BIND(&lower_case); \
{ \
if (v8_flags.debug_code && expected_arg_count >= 0) { \
AbortIfArgCountMismatch(expected_arg_count, args.reg_count()); \
} \
TNode<Object> value = name(args, context, expected_arg_count); \
if (value) { \
result = value; \
__ Goto(&end); \
} \
}
INTRINSICS_LIST(HANDLE_CASE)
#undef HANDLE_CASE
__ BIND(&abort);
{
__ Abort(AbortReason::kUnexpectedFunctionIDForInvokeIntrinsic);
result = __ UndefinedConstant();
__ Goto(&end);
}
__ BIND(&end);
return result.value();
}
TNode<Object> IntrinsicsGenerator::IntrinsicAsBuiltinCall(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
Builtin builtin, int arg_count) {
switch (arg_count) {
case 1:
return __ CallBuiltin(builtin, context,
__ LoadRegisterFromRegisterList(args, 0));
case 2:
return __ CallBuiltin(builtin, context,
__ LoadRegisterFromRegisterList(args, 0),
__ LoadRegisterFromRegisterList(args, 1));
case 3:
return __ CallBuiltin(builtin, context,
__ LoadRegisterFromRegisterList(args, 0),
__ LoadRegisterFromRegisterList(args, 1),
__ LoadRegisterFromRegisterList(args, 2));
default:
UNREACHABLE();
}
}
TNode<Object> IntrinsicsGenerator::CopyDataProperties(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtin::kCopyDataProperties,
arg_count);
}
TNode<Object>
IntrinsicsGenerator::CopyDataPropertiesWithExcludedPropertiesOnStack(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
TNode<IntPtrT> offset = __ TimesSystemPointerSize(__ IntPtrConstant(1));
auto base = __ Signed(__ IntPtrSub(args.base_reg_location(), offset));
TNode<IntPtrT> excluded_property_count = __ IntPtrSub(
__ ChangeInt32ToIntPtr(args.reg_count()), __ IntPtrConstant(1));
return __ CallBuiltin(
Builtin::kCopyDataPropertiesWithExcludedPropertiesOnStack, context,
__ LoadRegisterFromRegisterList(args, 0), excluded_property_count, base);
}
TNode<Object> IntrinsicsGenerator::CreateIterResultObject(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtin::kCreateIterResultObject,
arg_count);
}
TNode<Object> IntrinsicsGenerator::CreateAsyncFromSyncIterator(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
TNode<JSAny> sync_iterator =
__ CAST(__ LoadRegisterFromRegisterList(args, 0));
return __ CreateAsyncFromSyncIterator(context, sync_iterator);
}
TNode<Object> IntrinsicsGenerator::CreateJSGeneratorObject(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtin::kCreateGeneratorObject,
arg_count);
}
TNode<Object> IntrinsicsGenerator::GeneratorGetResumeMode(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
TNode<JSGeneratorObject> generator =
__ CAST(__ LoadRegisterFromRegisterList(args, 0));
const TNode<Object> value =
__ LoadObjectField(generator, JSGeneratorObject::kResumeModeOffset);
return value;
}
TNode<Object> IntrinsicsGenerator::GeneratorClose(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
TNode<JSGeneratorObject> generator =
__ CAST(__ LoadRegisterFromRegisterList(args, 0));
__ StoreObjectFieldNoWriteBarrier(
generator, JSGeneratorObject::kContinuationOffset,
__ SmiConstant(JSGeneratorObject::kGeneratorClosed));
return __ UndefinedConstant();
}
TNode<Object> IntrinsicsGenerator::GetImportMetaObject(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return __ GetImportMetaObject(context);
}
TNode<Object> IntrinsicsGenerator::AsyncFunctionAwait(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtin::kAsyncFunctionAwait,
arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncFunctionEnter(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtin::kAsyncFunctionEnter,
arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncFunctionReject(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtin::kAsyncFunctionReject,
arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncFunctionResolve(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtin::kAsyncFunctionResolve,
arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncGeneratorAwait(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtin::kAsyncGeneratorAwait,
arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncGeneratorReject(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtin::kAsyncGeneratorReject,
arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncGeneratorResolve(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(args, context, Builtin::kAsyncGeneratorResolve,
arg_count);
}
TNode<Object> IntrinsicsGenerator::AsyncGeneratorYieldWithAwait(
const InterpreterAssembler::RegListNodePair& args, TNode<Context> context,
int arg_count) {
return IntrinsicAsBuiltinCall(
args, context, Builtin::kAsyncGeneratorYieldWithAwait, arg_count);
}
void IntrinsicsGenerator::AbortIfArgCountMismatch(int expected,
TNode<Word32T> actual) {
InterpreterAssembler::Label match(assembler_);
TNode<BoolT> comparison = __ Word32Equal(actual, __ Int32Constant(expected));
__ GotoIf(comparison, &match);
__ Abort(AbortReason::kWrongArgumentCountForInvokeIntrinsic);
__ Goto(&match);
__ BIND(&match);
}
#undef __
#include "src/codegen/undef-code-stub-assembler-macros.inc"
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,27 @@
// Copyright 2017 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_INTERPRETER_INTERPRETER_INTRINSICS_GENERATOR_H_
#define V8_INTERPRETER_INTERPRETER_INTRINSICS_GENERATOR_H_
#include "src/interpreter/interpreter-assembler.h"
namespace v8 {
namespace internal {
namespace compiler {
class Node;
} // namespace compiler
namespace interpreter {
extern TNode<Object> GenerateInvokeIntrinsic(
InterpreterAssembler* assembler, TNode<Uint32T> function_id,
TNode<Context> context, const InterpreterAssembler::RegListNodePair& args);
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_INTERPRETER_INTRINSICS_GENERATOR_H_

View File

@ -0,0 +1,55 @@
// Copyright 2015 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.
#include "src/interpreter/interpreter-intrinsics.h"
#include "src/base/logging.h"
namespace v8 {
namespace internal {
namespace interpreter {
// static
bool IntrinsicsHelper::IsSupported(Runtime::FunctionId function_id) {
switch (function_id) {
#define SUPPORTED(name, lower_case, count) case Runtime::kInline##name:
INTRINSICS_LIST(SUPPORTED)
return true;
#undef SUPPORTED
default:
return false;
}
}
// static
IntrinsicsHelper::IntrinsicId IntrinsicsHelper::FromRuntimeId(
Runtime::FunctionId function_id) {
switch (function_id) {
#define TO_RUNTIME_ID(name, lower_case, count) \
case Runtime::kInline##name: \
return IntrinsicId::k##name;
INTRINSICS_LIST(TO_RUNTIME_ID)
#undef TO_RUNTIME_ID
default:
UNREACHABLE();
}
}
// static
Runtime::FunctionId IntrinsicsHelper::ToRuntimeId(
IntrinsicsHelper::IntrinsicId intrinsic_id) {
switch (intrinsic_id) {
#define TO_INTRINSIC_ID(name, lower_case, count) \
case IntrinsicId::k##name: \
return Runtime::kInline##name;
INTRINSICS_LIST(TO_INTRINSIC_ID)
#undef TO_INTRINSIC_ID
default:
UNREACHABLE();
}
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,57 @@
// Copyright 2015 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_INTERPRETER_INTERPRETER_INTRINSICS_H_
#define V8_INTERPRETER_INTERPRETER_INTRINSICS_H_
#include "src/runtime/runtime.h"
namespace v8 {
namespace internal {
namespace interpreter {
// List of supported intrisics, with upper case name, lower case name and
// expected number of arguments (-1 denoting argument count is variable).
#define INTRINSICS_LIST(V) \
V(AsyncFunctionAwait, async_function_await_caught, 2) \
V(AsyncFunctionEnter, async_function_enter, 2) \
V(AsyncFunctionReject, async_function_reject, 2) \
V(AsyncFunctionResolve, async_function_resolve, 2) \
V(AsyncGeneratorAwait, async_generator_await_caught, 2) \
V(AsyncGeneratorReject, async_generator_reject, 2) \
V(AsyncGeneratorResolve, async_generator_resolve, 3) \
V(AsyncGeneratorYieldWithAwait, async_generator_yield_with_await, 2) \
V(CreateJSGeneratorObject, create_js_generator_object, 2) \
V(GeneratorGetResumeMode, generator_get_resume_mode, 1) \
V(GeneratorClose, generator_close, 1) \
V(GetImportMetaObject, get_import_meta_object, 0) \
V(CopyDataProperties, copy_data_properties, 2) \
V(CopyDataPropertiesWithExcludedPropertiesOnStack, \
copy_data_properties_with_excluded_properties_on_stack, -1) \
V(CreateIterResultObject, create_iter_result_object, 2) \
V(CreateAsyncFromSyncIterator, create_async_from_sync_iterator, 1)
class IntrinsicsHelper {
public:
enum class IntrinsicId {
#define DECLARE_INTRINSIC_ID(name, lower_case, count) k##name,
INTRINSICS_LIST(DECLARE_INTRINSIC_ID)
#undef DECLARE_INTRINSIC_ID
kIdCount
};
static_assert(static_cast<uint32_t>(IntrinsicId::kIdCount) <= kMaxUInt8);
V8_EXPORT_PRIVATE static bool IsSupported(Runtime::FunctionId function_id);
static IntrinsicId FromRuntimeId(Runtime::FunctionId function_id);
static Runtime::FunctionId ToRuntimeId(IntrinsicId intrinsic_id);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(IntrinsicsHelper);
};
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_INTERPRETER_INTRINSICS_H_

431
deps/v8/src/interpreter/interpreter.cc vendored Normal file
View File

@ -0,0 +1,431 @@
// Copyright 2015 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.
#include "src/interpreter/interpreter.h"
#include <fstream>
#include <memory>
#include "builtins-generated/bytecodes-builtins-list.h"
#include "src/ast/prettyprinter.h"
#include "src/ast/scopes.h"
#include "src/codegen/compiler.h"
#include "src/codegen/unoptimized-compilation-info.h"
#include "src/common/globals.h"
#include "src/execution/local-isolate.h"
#include "src/heap/parked-scope.h"
#include "src/init/setup-isolate.h"
#include "src/interpreter/bytecode-generator.h"
#include "src/interpreter/bytecodes.h"
#include "src/logging/runtime-call-stats-scope.h"
#include "src/objects/objects-inl.h"
#include "src/objects/shared-function-info.h"
#include "src/parsing/parse-info.h"
#include "src/utils/ostreams.h"
namespace v8 {
namespace internal {
namespace interpreter {
class InterpreterCompilationJob final : public UnoptimizedCompilationJob {
public:
InterpreterCompilationJob(ParseInfo* parse_info, FunctionLiteral* literal,
Handle<Script> script,
AccountingAllocator* allocator,
std::vector<FunctionLiteral*>* eager_inner_literals,
LocalIsolate* local_isolate);
InterpreterCompilationJob(const InterpreterCompilationJob&) = delete;
InterpreterCompilationJob& operator=(const InterpreterCompilationJob&) =
delete;
protected:
Status ExecuteJobImpl() final;
Status FinalizeJobImpl(DirectHandle<SharedFunctionInfo> shared_info,
Isolate* isolate) final;
Status FinalizeJobImpl(DirectHandle<SharedFunctionInfo> shared_info,
LocalIsolate* isolate) final;
private:
BytecodeGenerator* generator() { return &generator_; }
template <typename IsolateT>
void CheckAndPrintBytecodeMismatch(IsolateT* isolate, Handle<Script> script,
DirectHandle<BytecodeArray> bytecode);
template <typename IsolateT>
Status DoFinalizeJobImpl(DirectHandle<SharedFunctionInfo> shared_info,
IsolateT* isolate);
Zone zone_;
UnoptimizedCompilationInfo compilation_info_;
LocalIsolate* local_isolate_;
BytecodeGenerator generator_;
};
Interpreter::Interpreter(Isolate* isolate)
: isolate_(isolate),
interpreter_entry_trampoline_instruction_start_(kNullAddress) {
memset(dispatch_table_, 0, sizeof(dispatch_table_));
if (V8_IGNITION_DISPATCH_COUNTING_BOOL) {
InitDispatchCounters();
}
}
void Interpreter::InitDispatchCounters() {
static const int kBytecodeCount = static_cast<int>(Bytecode::kLast) + 1;
bytecode_dispatch_counters_table_.reset(
new uintptr_t[kBytecodeCount * kBytecodeCount]);
memset(bytecode_dispatch_counters_table_.get(), 0,
sizeof(uintptr_t) * kBytecodeCount * kBytecodeCount);
}
namespace {
Builtin BuiltinIndexFromBytecode(Bytecode bytecode,
OperandScale operand_scale) {
int index = static_cast<int>(bytecode);
if (operand_scale == OperandScale::kSingle) {
if (Bytecodes::IsShortStar(bytecode)) {
index = static_cast<int>(Bytecode::kFirstShortStar);
} else if (bytecode > Bytecode::kLastShortStar) {
// Adjust the index due to repeated handlers.
index -= Bytecodes::kShortStarCount - 1;
}
} else {
// The table contains uint8_t offsets starting at 0 with
// kIllegalBytecodeHandlerEncoding for illegal bytecode/scale combinations.
uint8_t offset = kWideBytecodeToBuiltinsMapping[index];
if (offset == kIllegalBytecodeHandlerEncoding) {
return Builtin::kIllegalHandler;
} else {
index = kNumberOfBytecodeHandlers + offset;
if (operand_scale == OperandScale::kQuadruple) {
index += kNumberOfWideBytecodeHandlers;
}
}
}
return Builtins::FromInt(static_cast<int>(Builtin::kFirstBytecodeHandler) +
index);
}
} // namespace
Tagged<Code> Interpreter::GetBytecodeHandler(Bytecode bytecode,
OperandScale operand_scale) {
Builtin builtin = BuiltinIndexFromBytecode(bytecode, operand_scale);
return isolate_->builtins()->code(builtin);
}
void Interpreter::SetBytecodeHandler(Bytecode bytecode,
OperandScale operand_scale,
Tagged<Code> handler) {
DCHECK(!handler->has_instruction_stream());
DCHECK(handler->kind() == CodeKind::BYTECODE_HANDLER);
size_t index = GetDispatchTableIndex(bytecode, operand_scale);
dispatch_table_[index] = handler->instruction_start();
}
// static
size_t Interpreter::GetDispatchTableIndex(Bytecode bytecode,
OperandScale operand_scale) {
static const size_t kEntriesPerOperandScale = 1u << kBitsPerByte;
size_t index = static_cast<size_t>(bytecode);
return index + BytecodeOperands::OperandScaleAsIndex(operand_scale) *
kEntriesPerOperandScale;
}
namespace {
void MaybePrintAst(ParseInfo* parse_info,
UnoptimizedCompilationInfo* compilation_info) {
if (!v8_flags.print_ast) return;
StdoutStream os;
std::unique_ptr<char[]> name = compilation_info->literal()->GetDebugName();
os << "[generating bytecode for function: " << name.get() << "]" << std::endl;
#ifdef DEBUG
os << "--- AST ---" << std::endl
<< AstPrinter(parse_info->stack_limit())
.PrintProgram(compilation_info->literal())
<< std::endl;
#endif // DEBUG
}
bool ShouldPrintBytecode(DirectHandle<SharedFunctionInfo> shared) {
if (!v8_flags.print_bytecode) return false;
// Checks whether function passed the filter.
if (shared->is_toplevel()) {
base::Vector<const char> filter =
base::CStrVector(v8_flags.print_bytecode_filter);
return filter.empty() || (filter.length() == 1 && filter[0] == '*');
} else {
return shared->PassesFilter(v8_flags.print_bytecode_filter);
}
}
} // namespace
InterpreterCompilationJob::InterpreterCompilationJob(
ParseInfo* parse_info, FunctionLiteral* literal, Handle<Script> script,
AccountingAllocator* allocator,
std::vector<FunctionLiteral*>* eager_inner_literals,
LocalIsolate* local_isolate)
: UnoptimizedCompilationJob(parse_info->stack_limit(), parse_info,
&compilation_info_),
zone_(allocator, ZONE_NAME),
compilation_info_(&zone_, parse_info, literal),
local_isolate_(local_isolate),
generator_(local_isolate, &zone_, &compilation_info_,
parse_info->ast_string_constants(), eager_inner_literals,
script) {}
InterpreterCompilationJob::Status InterpreterCompilationJob::ExecuteJobImpl() {
RCS_SCOPE(parse_info()->runtime_call_stats(),
RuntimeCallCounterId::kCompileIgnition,
RuntimeCallStats::kThreadSpecific);
// TODO(lpy): add support for background compilation RCS trace.
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.CompileIgnition");
// Print AST if flag is enabled. Note, if compiling on a background thread
// then ASTs from different functions may be intersperse when printed.
{
DisallowGarbageCollection no_heap_access;
MaybePrintAst(parse_info(), compilation_info());
}
local_isolate_->ParkIfOnBackgroundAndExecute(
[this]() { generator()->GenerateBytecode(stack_limit()); });
if (generator()->HasStackOverflow()) {
return FAILED;
}
return SUCCEEDED;
}
#ifdef DEBUG
template <typename IsolateT>
void InterpreterCompilationJob::CheckAndPrintBytecodeMismatch(
IsolateT* isolate, Handle<Script> script,
DirectHandle<BytecodeArray> bytecode) {
int first_mismatch = generator()->CheckBytecodeMatches(*bytecode);
if (first_mismatch >= 0) {
parse_info()->ast_value_factory()->Internalize(isolate);
DeclarationScope::AllocateScopeInfos(parse_info(), script, isolate);
DirectHandle<BytecodeArray> new_bytecode =
generator()->FinalizeBytecode(isolate, script);
std::cerr << "Bytecode mismatch";
#ifdef OBJECT_PRINT
std::cerr << " found for function: ";
MaybeDirectHandle<String> maybe_name =
parse_info()->literal()->GetName(isolate);
DirectHandle<String> name;
if (maybe_name.ToHandle(&name) && name->length() != 0) {
name->PrintUC16(std::cerr);
} else {
std::cerr << "anonymous";
}
Tagged<Object> script_name = script->GetNameOrSourceURL();
if (IsString(script_name)) {
std::cerr << " ";
Cast<String>(script_name)->PrintUC16(std::cerr);
std::cerr << ":" << parse_info()->literal()->start_position();
}
#endif
std::cerr << "\nOriginal bytecode:\n";
bytecode->Disassemble(std::cerr);
std::cerr << "\nNew bytecode:\n";
new_bytecode->Disassemble(std::cerr);
FATAL("Bytecode mismatch at offset %d\n", first_mismatch);
}
}
#endif
InterpreterCompilationJob::Status InterpreterCompilationJob::FinalizeJobImpl(
DirectHandle<SharedFunctionInfo> shared_info, Isolate* isolate) {
RCS_SCOPE(parse_info()->runtime_call_stats(),
RuntimeCallCounterId::kCompileIgnitionFinalization);
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.CompileIgnitionFinalization");
return DoFinalizeJobImpl(shared_info, isolate);
}
InterpreterCompilationJob::Status InterpreterCompilationJob::FinalizeJobImpl(
DirectHandle<SharedFunctionInfo> shared_info, LocalIsolate* isolate) {
RCS_SCOPE(isolate, RuntimeCallCounterId::kCompileIgnitionFinalization,
RuntimeCallStats::kThreadSpecific);
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"),
"V8.CompileIgnitionFinalization");
return DoFinalizeJobImpl(shared_info, isolate);
}
template <typename IsolateT>
InterpreterCompilationJob::Status InterpreterCompilationJob::DoFinalizeJobImpl(
DirectHandle<SharedFunctionInfo> shared_info, IsolateT* isolate) {
Handle<BytecodeArray> bytecodes = compilation_info_.bytecode_array();
if (bytecodes.is_null()) {
bytecodes = generator()->FinalizeBytecode(
isolate, handle(Cast<Script>(shared_info->script()), isolate));
if (generator()->HasStackOverflow()) {
return FAILED;
}
compilation_info()->SetBytecodeArray(bytecodes);
}
if (compilation_info()->SourcePositionRecordingMode() ==
SourcePositionTableBuilder::RecordingMode::RECORD_SOURCE_POSITIONS) {
DirectHandle<TrustedByteArray> source_position_table =
generator()->FinalizeSourcePositionTable(isolate);
bytecodes->set_source_position_table(*source_position_table, kReleaseStore);
}
if (ShouldPrintBytecode(shared_info)) {
StdoutStream os;
std::unique_ptr<char[]> name =
compilation_info()->literal()->GetDebugName();
os << "[generated bytecode for function: " << name.get() << " ("
<< shared_info << ")]" << std::endl;
os << "Bytecode length: " << bytecodes->length() << std::endl;
bytecodes->Disassemble(os);
os << std::flush;
}
#ifdef DEBUG
if (parse_info()->literal()->shared_function_info().is_null()) {
parse_info()->literal()->set_shared_function_info(
indirect_handle(shared_info, isolate));
}
CheckAndPrintBytecodeMismatch(
isolate, handle(Cast<Script>(shared_info->script()), isolate), bytecodes);
#endif
return SUCCEEDED;
}
std::unique_ptr<UnoptimizedCompilationJob> Interpreter::NewCompilationJob(
ParseInfo* parse_info, FunctionLiteral* literal, Handle<Script> script,
AccountingAllocator* allocator,
std::vector<FunctionLiteral*>* eager_inner_literals,
LocalIsolate* local_isolate) {
return std::make_unique<InterpreterCompilationJob>(
parse_info, literal, script, allocator, eager_inner_literals,
local_isolate);
}
std::unique_ptr<UnoptimizedCompilationJob>
Interpreter::NewSourcePositionCollectionJob(
ParseInfo* parse_info, FunctionLiteral* literal,
Handle<BytecodeArray> existing_bytecode, AccountingAllocator* allocator,
LocalIsolate* local_isolate) {
auto job = std::make_unique<InterpreterCompilationJob>(
parse_info, literal, Handle<Script>(), allocator, nullptr, local_isolate);
job->compilation_info()->SetBytecodeArray(existing_bytecode);
return job;
}
void Interpreter::ForEachBytecode(
const std::function<void(Bytecode, OperandScale)>& f) {
constexpr OperandScale kOperandScales[] = {
#define VALUE(Name, _) OperandScale::k##Name,
OPERAND_SCALE_LIST(VALUE)
#undef VALUE
};
for (OperandScale operand_scale : kOperandScales) {
for (int i = 0; i < Bytecodes::kBytecodeCount; i++) {
f(Bytecodes::FromByte(i), operand_scale);
}
}
}
void Interpreter::Initialize() {
Builtins* builtins = isolate_->builtins();
// Set the interpreter entry trampoline entry point now that builtins are
// initialized.
DirectHandle<Code> code = BUILTIN_CODE(isolate_, InterpreterEntryTrampoline);
DCHECK(builtins->is_initialized());
DCHECK(!code->has_instruction_stream());
interpreter_entry_trampoline_instruction_start_ = code->instruction_start();
// Initialize the dispatch table.
ForEachBytecode([=, this](Bytecode bytecode, OperandScale operand_scale) {
Builtin builtin = BuiltinIndexFromBytecode(bytecode, operand_scale);
Tagged<Code> handler = builtins->code(builtin);
if (Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) {
#ifdef DEBUG
std::string builtin_name(Builtins::name(builtin));
std::string expected_name =
(Bytecodes::IsShortStar(bytecode)
? "ShortStar"
: Bytecodes::ToString(bytecode, operand_scale, "")) +
"Handler";
DCHECK_EQ(expected_name, builtin_name);
#endif
}
SetBytecodeHandler(bytecode, operand_scale, handler);
});
DCHECK(IsDispatchTableInitialized());
}
bool Interpreter::IsDispatchTableInitialized() const {
return dispatch_table_[0] != kNullAddress;
}
uintptr_t Interpreter::GetDispatchCounter(Bytecode from, Bytecode to) const {
int from_index = Bytecodes::ToByte(from);
int to_index = Bytecodes::ToByte(to);
CHECK_WITH_MSG(bytecode_dispatch_counters_table_ != nullptr,
"Dispatch counters require building with "
"v8_enable_ignition_dispatch_counting");
return bytecode_dispatch_counters_table_[from_index * kNumberOfBytecodes +
to_index];
}
DirectHandle<JSObject> Interpreter::GetDispatchCountersObject() {
DirectHandle<JSObject> counters_map =
isolate_->factory()->NewJSObjectWithNullProto();
// Output is a JSON-encoded object of objects.
//
// The keys on the top level object are source bytecodes,
// and corresponding value are objects. Keys on these last are the
// destinations of the dispatch and the value associated is a counter for
// the correspondent source-destination dispatch chain.
//
// Only non-zero counters are written to file, but an entry in the top-level
// object is always present, even if the value is empty because all counters
// for that source are zero.
for (int from_index = 0; from_index < kNumberOfBytecodes; ++from_index) {
Bytecode from_bytecode = Bytecodes::FromByte(from_index);
DirectHandle<JSObject> counters_row =
isolate_->factory()->NewJSObjectWithNullProto();
for (int to_index = 0; to_index < kNumberOfBytecodes; ++to_index) {
Bytecode to_bytecode = Bytecodes::FromByte(to_index);
uintptr_t counter = GetDispatchCounter(from_bytecode, to_bytecode);
if (counter > 0) {
DirectHandle<Object> value =
isolate_->factory()->NewNumberFromSize(counter);
JSObject::AddProperty(isolate_, counters_row,
Bytecodes::ToString(to_bytecode), value, NONE);
}
}
JSObject::AddProperty(isolate_, counters_map,
Bytecodes::ToString(from_bytecode), counters_row,
NONE);
}
return counters_map;
}
} // namespace interpreter
} // namespace internal
} // namespace v8

126
deps/v8/src/interpreter/interpreter.h vendored Normal file
View File

@ -0,0 +1,126 @@
// Copyright 2015 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_INTERPRETER_INTERPRETER_H_
#define V8_INTERPRETER_INTERPRETER_H_
#include <memory>
// Clients of this interface shouldn't depend on lots of interpreter internals.
// Do not include anything from src/interpreter other than
// src/interpreter/bytecodes.h here!
#include "src/base/macros.h"
#include "src/builtins/builtins.h"
#include "src/interpreter/bytecodes.h"
namespace v8 {
namespace internal {
class AccountingAllocator;
class BytecodeArray;
class Callable;
class UnoptimizedCompilationJob;
class FunctionLiteral;
class IgnitionStatisticsTester;
class Isolate;
class LocalIsolate;
class ParseInfo;
class RootVisitor;
class SetupIsolateDelegate;
template <typename>
class ZoneVector;
namespace interpreter {
class InterpreterAssembler;
class Interpreter {
public:
explicit Interpreter(Isolate* isolate);
virtual ~Interpreter() = default;
Interpreter(const Interpreter&) = delete;
Interpreter& operator=(const Interpreter&) = delete;
// Creates a compilation job which will generate bytecode for |literal|.
// Additionally, if |eager_inner_literals| is not null, adds any eagerly
// compilable inner FunctionLiterals to this list.
static std::unique_ptr<UnoptimizedCompilationJob> NewCompilationJob(
ParseInfo* parse_info, FunctionLiteral* literal, Handle<Script> script,
AccountingAllocator* allocator,
std::vector<FunctionLiteral*>* eager_inner_literals,
LocalIsolate* local_isolate);
// Creates a compilation job which will generate source positions for
// |literal| and when finalized, store the result into |existing_bytecode|.
static std::unique_ptr<UnoptimizedCompilationJob>
NewSourcePositionCollectionJob(ParseInfo* parse_info,
FunctionLiteral* literal,
Handle<BytecodeArray> existing_bytecode,
AccountingAllocator* allocator,
LocalIsolate* local_isolate);
// If the bytecode handler for |bytecode| and |operand_scale| has not yet
// been loaded, deserialize it. Then return the handler.
V8_EXPORT_PRIVATE Tagged<Code> GetBytecodeHandler(Bytecode bytecode,
OperandScale operand_scale);
// Set the bytecode handler for |bytecode| and |operand_scale|.
void SetBytecodeHandler(Bytecode bytecode, OperandScale operand_scale,
Tagged<Code> handler);
V8_EXPORT_PRIVATE DirectHandle<JSObject> GetDispatchCountersObject();
void ForEachBytecode(const std::function<void(Bytecode, OperandScale)>& f);
void Initialize();
bool IsDispatchTableInitialized() const;
Address dispatch_table_address() {
return reinterpret_cast<Address>(&dispatch_table_[0]);
}
Address bytecode_dispatch_counters_table() {
return reinterpret_cast<Address>(bytecode_dispatch_counters_table_.get());
}
Address address_of_interpreter_entry_trampoline_instruction_start() const {
return reinterpret_cast<Address>(
&interpreter_entry_trampoline_instruction_start_);
}
private:
friend class SetupInterpreter;
friend class v8::internal::SetupIsolateDelegate;
friend class v8::internal::IgnitionStatisticsTester;
V8_EXPORT_PRIVATE void InitDispatchCounters();
V8_EXPORT_PRIVATE uintptr_t GetDispatchCounter(Bytecode from,
Bytecode to) const;
// Get dispatch table index of bytecode.
static size_t GetDispatchTableIndex(Bytecode bytecode,
OperandScale operand_scale);
static const int kNumberOfWideVariants = BytecodeOperands::kOperandScaleCount;
static const int kDispatchTableSize = kNumberOfWideVariants * (kMaxUInt8 + 1);
static const int kNumberOfBytecodes = static_cast<int>(Bytecode::kLast) + 1;
Isolate* isolate_;
Address dispatch_table_[kDispatchTableSize];
std::unique_ptr<uintptr_t[]> bytecode_dispatch_counters_table_;
Address interpreter_entry_trampoline_instruction_start_;
};
#ifdef V8_IGNITION_DISPATCH_COUNTING
#define V8_IGNITION_DISPATCH_COUNTING_BOOL true
#else
#define V8_IGNITION_DISPATCH_COUNTING_BOOL false
#endif
} // namespace interpreter
} // namespace internal
} // namespace v8
#endif // V8_INTERPRETER_INTERPRETER_H_