#ifndef V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_
#define V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_
#include <cstddef>
#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(Handle<BytecodeArray> bytecode);
#endif
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);
}
int locals_count() const {
DCHECK_GE(local_register_count_, 0);
return local_register_count_;
}
int fixed_register_count() const { return locals_count(); }
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;
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);
BytecodeArrayBuilder& SetPrototypeProperties(size_t index_obj, size_t slot);
BytecodeArrayBuilder& LoadGlobal(const AstRawString* name, int feedback_slot,
TypeofMode typeof_mode);
BytecodeArrayBuilder& StoreGlobal(const AstRawString* name,
int feedback_slot);
enum ContextSlotMutability { kImmutableSlot, kMutableSlot };
BytecodeArrayBuilder& LoadContextSlot(Register context, Variable* variable,
int depth,
ContextSlotMutability immutable);
BytecodeArrayBuilder& StoreContextSlot(Register context, Variable* variable,
int depth);
BytecodeArrayBuilder& LoadModuleVariable(int cell_index, int depth);
BytecodeArrayBuilder& StoreModuleVariable(int cell_index, int depth);
BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg);
BytecodeArrayBuilder& StoreAccumulatorInRegister(Register reg);
BytecodeArrayBuilder& MoveRegister(Register from, Register to);
BytecodeArrayBuilder& LoadNamedProperty(Register object,
const AstRawString* name,
int feedback_slot);
BytecodeArrayBuilder& LoadNamedPropertyFromSuper(Register object,
const AstRawString* name,
int feedback_slot);
BytecodeArrayBuilder& LoadKeyedProperty(Register object, int feedback_slot);
BytecodeArrayBuilder& LoadEnumeratedKeyedProperty(Register object,
Register enum_index,
Register cache_type,
int feedback_slot);
BytecodeArrayBuilder& LoadIteratorProperty(Register object,
int feedback_slot);
BytecodeArrayBuilder& GetIterator(Register object, int load_feedback_slot,
int call_feedback_slot);
BytecodeArrayBuilder& LoadAsyncIteratorProperty(Register object,
int feedback_slot);
BytecodeArrayBuilder& DefineKeyedOwnPropertyInLiteral(
Register object, Register name,
DefineKeyedOwnPropertyInLiteralFlags flags, int feedback_slot);
BytecodeArrayBuilder& SetNamedProperty(Register object,
const AstRawString* name,
int feedback_slot,
LanguageMode language_mode);
BytecodeArrayBuilder& SetNamedProperty(Register object,
size_t constant_pool_entry,
int feedback_slot,
LanguageMode language_mode);
BytecodeArrayBuilder& DefineNamedOwnProperty(Register object,
const AstRawString* name,
int feedback_slot);
BytecodeArrayBuilder& SetKeyedProperty(Register object, Register key,
int feedback_slot,
LanguageMode language_mode);
BytecodeArrayBuilder& DefineKeyedOwnProperty(
Register object, Register key, DefineKeyedOwnPropertyFlags flags,
int feedback_slot);
BytecodeArrayBuilder& StoreInArrayLiteral(Register array, Register index,
int feedback_slot);
BytecodeArrayBuilder& StoreClassFieldsInitializer(Register constructor,
int feedback_slot);
BytecodeArrayBuilder& LoadClassFieldsInitializer(Register constructor,
int feedback_slot);
BytecodeArrayBuilder& LoadLookupSlot(const AstRawString* name,
TypeofMode typeof_mode);
BytecodeArrayBuilder& LoadLookupContextSlot(const AstRawString* name,
TypeofMode typeof_mode,
ContextMode context_mode,
int slot_index, int depth);
BytecodeArrayBuilder& LoadLookupGlobalSlot(const AstRawString* name,
TypeofMode typeof_mode,
int feedback_slot, int depth);
BytecodeArrayBuilder& StoreLookupSlot(
const AstRawString* name, LanguageMode language_mode,
LookupHoistingMode lookup_hoisting_mode);
BytecodeArrayBuilder& CreateClosure(size_t shared_function_info_entry,
int slot, int flags);
BytecodeArrayBuilder& CreateBlockContext(const Scope* scope);
BytecodeArrayBuilder& CreateCatchContext(Register exception,
const Scope* scope);
BytecodeArrayBuilder& CreateFunctionContext(const Scope* scope, int slots);
BytecodeArrayBuilder& CreateEvalContext(const Scope* scope, int slots);
BytecodeArrayBuilder& CreateWithContext(Register object, const Scope* scope);
BytecodeArrayBuilder& CreateArguments(CreateArgumentsType type);
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);
BytecodeArrayBuilder& GetTemplateObject(
size_t template_object_description_entry, int feedback_slot);
BytecodeArrayBuilder& PushContext(Register context);
BytecodeArrayBuilder& PopContext(Register context);
BytecodeArrayBuilder& CallProperty(Register callable, RegisterList args,
int feedback_slot);
BytecodeArrayBuilder& CallUndefinedReceiver(Register callable,
RegisterList args,
int feedback_slot);
BytecodeArrayBuilder& CallAnyReceiver(Register callable, RegisterList args,
int feedback_slot);
BytecodeArrayBuilder& TailCall(Register callable, RegisterList args,
int feedback_slot);
BytecodeArrayBuilder& CallWithSpread(Register callable, RegisterList args,
int feedback_slot);
BytecodeArrayBuilder& Construct(Register constructor, RegisterList args,
int feedback_slot);
BytecodeArrayBuilder& ConstructWithSpread(Register constructor,
RegisterList args,
int feedback_slot);
BytecodeArrayBuilder& ConstructForwardAllArgs(Register constructor,
int feedback_slot);
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
RegisterList args);
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id,
Register arg);
BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id);
BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id,
RegisterList args,
RegisterList return_pair);
BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id,
Register arg,
RegisterList return_pair);
BytecodeArrayBuilder& CallJSRuntime(int context_index, RegisterList args);
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg,
int feedback_slot);
BytecodeArrayBuilder& Add_StringConstant_Internalize(
Token::Value binop, Register reg, int feedback_slot,
AddStringConstantAndInternalizeVariant as_variant);
BytecodeArrayBuilder& BinaryOperationSmiLiteral(Token::Value binop,
Tagged<Smi> literal,
int feedback_slot);
BytecodeArrayBuilder& UnaryOperation(Token::Value op, int feedback_slot);
enum class ToBooleanMode {
kConvertToBoolean,
kAlreadyBoolean,
};
BytecodeArrayBuilder& LogicalNot(ToBooleanMode mode);
BytecodeArrayBuilder& TypeOf(int feedback_slot);
BytecodeArrayBuilder& GetSuperConstructor(Register out);
BytecodeArrayBuilder& FindNonDefaultConstructorOrConstruct(
Register this_function, Register new_target, RegisterList output);
BytecodeArrayBuilder& Delete(Register object, LanguageMode language_mode);
enum NilValue { kNullValue, kUndefinedValue };
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);
BytecodeArrayBuilder& ToObject(Register out);
BytecodeArrayBuilder& ToName();
BytecodeArrayBuilder& ToString();
BytecodeArrayBuilder& ToBoolean(ToBooleanMode mode);
BytecodeArrayBuilder& ToNumber(int feedback_slot);
BytecodeArrayBuilder& ToNumeric(int feedback_slot);
BytecodeArrayBuilder& MarkHandler(int handler_id,
HandlerTable::CatchPrediction will_catch);
BytecodeArrayBuilder& MarkTryBegin(int handler_id, Register context);
BytecodeArrayBuilder& MarkTryEnd(int handler_id);
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);
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);
BytecodeArrayBuilder& Debugger();
BytecodeArrayBuilder& IncBlockCounter(int slot);
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);
BytecodeArrayBuilder& ForOfNext(Register object, Register next,
RegisterList value_done, int call_slot);
BytecodeArrayBuilder& SuspendGenerator(Register generator,
RegisterList registers,
int suspend_id);
BytecodeArrayBuilder& SwitchOnGeneratorState(Register generator,
BytecodeJumpTable* jump_table);
BytecodeArrayBuilder& ResumeGenerator(Register generator,
RegisterList registers);
int NewHandlerEntry() { return handler_table_builder()->NewHandlerEntry(); }
BytecodeJumpTable* AllocateJumpTable(int size, int case_value_base);
void TrimJumpTable(BytecodeJumpTable* jump_table, int size);
BytecodeRegisterOptimizer* GetRegisterOptimizer() {
return register_optimizer_;
}
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
size_t AllocateDeferredConstantPoolEntry();
void SetDeferredConstantPoolEntry(size_t entry, Handle<Object> object);
void InitializeReturnPosition(FunctionLiteral* literal);
void SetStatementPosition(Statement* stmt, bool is_breakable = true) {
SetStatementPosition(stmt->position(), is_breakable);
}
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, bool is_breakable = true) {
if (position == kNoSourcePosition) return;
latest_source_info_.MakeStatementPosition(position, is_breakable);
}
void SetExpressionPosition(Expression* expr) {
SetExpressionPosition(expr->position());
}
void SetExpressionPosition(int position) {
if (position == kNoSourcePosition) return;
if (!latest_source_info_.is_statement()) {
latest_source_info_.MakeExpressionPosition(position);
}
}
void SetExpressionAsStatementPosition(Expression* expr,
bool is_breakable = true) {
SetStatementPosition(expr->position(), is_breakable);
}
bool RemainderOfBlockIsDead() const {
return bytecode_array_writer_.RemainderOfBlockIsDead();
}
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);
void OutputLdarRaw(Register reg);
void OutputStarRaw(Register reg);
void OutputMovRaw(Register src, Register dest);
void EmitFunctionStartSourcePosition(int position);
size_t current_bytecode_size() const {
return bytecode_array_writer_.current_bytecode_size();
}
BytecodeRegisterAllocator* register_allocator() {
return ®ister_allocator_;
}
const BytecodeRegisterAllocator* register_allocator() const {
return ®ister_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_;
}
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;
void SetDeferredSourceInfo(BytecodeSourceInfo source_info);
void AttachOrEmitDeferredSourceInfo(BytecodeNode* node);
void Write(BytecodeNode* node);
void WriteJump(BytecodeNode* node, BytecodeLabel* label);
void WriteJumpLoop(BytecodeNode* node, BytecodeLoopHeader* loop_header);
void WriteSwitch(BytecodeNode* node, BytecodeJumpTable* label);
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);
}
}
}
#endif