[vm] Initial implementation of dynamic modules in the VM/AOT

TEST=Manually tested dynamic modules

Change-Id: Icb2616e414167bd1fbd10f01dea64c57dbdeeac7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/380281
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov
2024-08-15 14:09:52 +00:00
committed by Commit Queue
parent a09fbf8468
commit 8fbca8ba67
73 changed files with 14701 additions and 3515 deletions
+4
View File
@@ -172,6 +172,10 @@ config("dart_config") {
defines += [ "SUPPORT_PERFETTO" ]
}
if (dart_dynamic_modules) {
defines += [ "DART_DYNAMIC_MODULES" ]
}
if (is_fuchsia) {
lib_dirs = [ "${fuchsia_arch_root}/lib" ]
+18 -14
View File
@@ -31,21 +31,25 @@ static ScriptPtr FindScript(DartFrameIterator* iterator) {
ASSERT(!assert_error_class.IsNull());
bool hit_assertion_error = false;
for (; stack_frame != nullptr; stack_frame = iterator->NextFrame()) {
code = stack_frame->LookupDartCode();
if (code.is_optimized()) {
InlinedFunctionsIterator inlined_iterator(code, stack_frame->pc());
while (!inlined_iterator.Done()) {
func = inlined_iterator.function();
if (hit_assertion_error) {
return func.script();
}
ASSERT(!hit_assertion_error);
hit_assertion_error = (func.Owner() == assert_error_class.ptr());
inlined_iterator.Advance();
}
continue;
if (stack_frame->is_interpreted()) {
func = stack_frame->LookupDartFunction();
} else {
func = code.function();
code = stack_frame->LookupDartCode();
if (code.is_optimized()) {
InlinedFunctionsIterator inlined_iterator(code, stack_frame->pc());
while (!inlined_iterator.Done()) {
func = inlined_iterator.function();
if (hit_assertion_error) {
return func.script();
}
ASSERT(!hit_assertion_error);
hit_assertion_error = (func.Owner() == assert_error_class.ptr());
inlined_iterator.Advance();
}
continue;
} else {
func = code.function();
}
}
ASSERT(!func.IsNull());
if (hit_assertion_error) {
+56
View File
@@ -5,6 +5,7 @@
#include "vm/bootstrap_natives.h"
#include "lib/invocation_mirror.h"
#include "vm/bytecode_reader.h"
#include "vm/code_patcher.h"
#include "vm/dart_entry.h"
#include "vm/exceptions.h"
@@ -560,6 +561,61 @@ DEFINE_NATIVE_ENTRY(Internal_boundsCheckForPartialInstantiation, 0, 2) {
return Object::null();
}
DEFINE_NATIVE_ENTRY(Internal_loadDynamicModule, 0, 1) {
#if defined(DART_DYNAMIC_MODULES)
GET_NON_NULL_NATIVE_ARGUMENT(TypedData, module_bytes,
arguments->NativeArgAt(0));
const intptr_t length = module_bytes.LengthInBytes();
uint8_t* data = reinterpret_cast<uint8_t*>(::malloc(length));
if (data == nullptr) {
const auto& exception = Instance::Handle(
zone, thread->isolate_group()->object_store()->out_of_memory());
Exceptions::Throw(thread, exception);
}
{
NoSafepointScope no_safepoint;
// The memory does not overlap.
memcpy(data, module_bytes.DataAddr(0), length); // NOLINT
}
const ExternalTypedData& typed_data = ExternalTypedData::Handle(
zone,
ExternalTypedData::New(kExternalTypedDataUint8ArrayCid, data, length));
auto& function = Function::Handle();
{
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
bytecode::BytecodeLoader loader(thread, typed_data);
function = loader.LoadBytecode();
}
if (function.IsNull()) {
return Object::null();
}
ASSERT(function.is_static());
ASSERT(function.is_declared_in_bytecode());
auto& result = Object::Handle(zone);
if (function.NumParameters() == 0) {
result = DartEntry::InvokeFunction(function, Object::empty_array());
} else {
ASSERT(function.NumParameters() == 1);
// <String>[]
const auto& arg0 = Array::Handle(
zone, Array::New(0, Type::Handle(zone, Type::StringType())));
const auto& args = Array::Handle(zone, Array::New(1));
args.SetAt(0, arg0);
result = DartEntry::InvokeFunction(function, args);
}
if (result.IsError()) {
Exceptions::PropagateError(Error::Cast(result));
}
return result.ptr();
#else
Exceptions::ThrowUnsupportedError(
"Loading of dynamic modules is not supported.");
return Object::null();
#endif // defined(DART_DYNAMIC_MODULES)
}
DEFINE_NATIVE_ENTRY(InvocationMirror_unpackTypeArguments, 0, 2) {
const TypeArguments& type_arguments =
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0));
+20 -5
View File
@@ -46,7 +46,11 @@ static StackTracePtr CurrentStackTrace(Thread* thread,
// Collect the frames.
StackTraceUtils::CollectFrames(thread, skip_frames,
[&](const StackTraceUtils::Frame& frame) {
code_array.Add(frame.code);
if (!frame.bytecode.IsNull()) {
code_array.Add(frame.bytecode);
} else {
code_array.Add(frame.code);
}
pc_offset_array.Add(frame.pc_offset);
});
@@ -72,6 +76,7 @@ static void AppendFrames(const GrowableObjectArray& code_list,
StackFrame* frame = frames.NextFrame();
ASSERT(frame != nullptr); // We expect to find a dart invocation frame.
Code& code = Code::Handle(zone);
Bytecode& bytecode = Bytecode::Handle(zone);
for (; frame != nullptr; frame = frames.NextFrame()) {
if (!frame->IsDartFrame()) {
continue;
@@ -81,10 +86,20 @@ static void AppendFrames(const GrowableObjectArray& code_list,
continue;
}
code = frame->LookupDartCode();
const intptr_t pc_offset = frame->pc() - code.PayloadStart();
code_list.Add(code);
pc_offset_list->Add(pc_offset);
if (frame->is_interpreted()) {
bytecode = frame->LookupDartBytecode();
if (bytecode.function() == Function::null()) {
continue;
}
const intptr_t pc_offset = frame->pc() - bytecode.PayloadStart();
code_list.Add(bytecode);
pc_offset_list->Add(pc_offset);
} else {
code = frame->LookupDartCode();
const intptr_t pc_offset = frame->pc() - code.PayloadStart();
code_list.Add(code);
pc_offset_list->Add(pc_offset);
}
}
}
+3
View File
@@ -74,6 +74,9 @@ declare_args() {
# being built on platforms which have a problem linking in the Perfetto
# library.
dart_support_perfetto = true
# Whether to support dynamic loading and interpretation of Dart bytecode.
dart_dynamic_modules = false
}
declare_args() {
+3 -3
View File
@@ -1702,7 +1702,7 @@ class FunctionSerializationCluster : public SerializationCluster {
} else if (kind == Snapshot::kFullJIT) {
NOT_IN_PRECOMPILED(s->Push(func->untag()->unoptimized_code()));
s->Push(func->untag()->code());
s->Push(func->untag()->ic_data_array());
s->Push(func->untag()->ic_data_array_or_bytecode());
}
if (kind != Snapshot::kFullAOT) {
NOT_IN_PRECOMPILED(s->Push(func->untag()->positional_parameter_names()));
@@ -1737,7 +1737,7 @@ class FunctionSerializationCluster : public SerializationCluster {
} else if (s->kind() == Snapshot::kFullJIT) {
NOT_IN_PRECOMPILED(WriteCompressedField(func, unoptimized_code));
WriteCompressedField(func, code);
WriteCompressedField(func, ic_data_array);
WriteCompressedField(func, ic_data_array_or_bytecode);
}
if (kind != Snapshot::kFullAOT) {
@@ -1917,7 +1917,7 @@ class FunctionDeserializationCluster : public DeserializationCluster {
if (kind == Snapshot::kFullJIT) {
func->untag()->unoptimized_code_ = static_cast<CodePtr>(d.ReadRef());
func->untag()->code_ = static_cast<CodePtr>(d.ReadRef());
func->untag()->ic_data_array_ = static_cast<ArrayPtr>(d.ReadRef());
func->untag()->ic_data_array_or_bytecode_ = d.ReadRef();
}
#endif
+1
View File
@@ -268,6 +268,7 @@ namespace dart {
V(Internal_extractTypeArguments, 2) \
V(Internal_prependTypeArguments, 4) \
V(Internal_boundsCheckForPartialInstantiation, 2) \
V(Internal_loadDynamicModule, 1) \
V(Internal_allocateOneByteString, 1) \
V(Internal_allocateTwoByteString, 1) \
V(Internal_writeIntoOneByteString, 3) \
File diff suppressed because it is too large Load Diff
+521
View File
@@ -0,0 +1,521 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_BYTECODE_READER_H_
#define RUNTIME_VM_BYTECODE_READER_H_
#include "vm/globals.h"
#if defined(DART_DYNAMIC_MODULES)
#include "vm/bit_vector.h"
#include "vm/constants_kbc.h"
#include "vm/hash_table.h"
#include "vm/object.h"
namespace dart {
namespace bytecode {
class BytecodeComponentData;
class BytecodeLoader {
public:
BytecodeLoader(Thread* thread, const TypedDataBase& binary);
~BytecodeLoader();
FunctionPtr LoadBytecode();
TypedDataBasePtr binary() const { return binary_.ptr(); }
ArrayPtr bytecode_component_array() const {
return bytecode_component_array_.ptr();
}
void SetOffset(const Object& obj, intptr_t offset);
intptr_t GetOffset(const Object& obj);
private:
Thread* thread_;
const TypedDataBase& binary_;
Array& bytecode_component_array_;
Array& bytecode_offsets_map_;
DISALLOW_COPY_AND_ASSIGN(BytecodeLoader);
};
class Reader : public ValueObject {
public:
explicit Reader(const TypedDataBase& typed_data) : typed_data_(&typed_data) {
Init();
}
uint32_t ReadUInt32At(intptr_t offset) const {
ASSERT((size_ >= 4) && (offset >= 0) && (offset <= size_ - 4));
uint32_t value =
LoadUnaligned(reinterpret_cast<const uint32_t*>(raw_buffer_ + offset));
return Utils::BigEndianToHost32(value);
}
uint32_t ReadUInt32() {
uint32_t value = ReadUInt32At(offset_);
offset_ += 4;
return value;
}
uint32_t ReadUInt() {
ASSERT((size_ >= 1) && (offset_ >= 0) && (offset_ <= size_ - 1));
const uint8_t* buffer = raw_buffer_;
uword byte0 = buffer[offset_];
if ((byte0 & 0x80) == 0) {
// 0...
offset_++;
return byte0;
} else if ((byte0 & 0xc0) == 0x80) {
// 10...
ASSERT((size_ >= 2) && (offset_ >= 0) && (offset_ <= size_ - 2));
uint32_t value =
((byte0 & ~static_cast<uword>(0x80)) << 8) | (buffer[offset_ + 1]);
offset_ += 2;
return value;
} else {
// 11...
ASSERT((size_ >= 4) && (offset_ >= 0) && (offset_ <= size_ - 4));
uint32_t value = ((byte0 & ~static_cast<uword>(0xc0)) << 24) |
(buffer[offset_ + 1] << 16) |
(buffer[offset_ + 2] << 8) | (buffer[offset_ + 3] << 0);
offset_ += 4;
return value;
}
}
intptr_t ReadSLEB128() {
ReadStream stream(raw_buffer_, size_, offset_);
const intptr_t result = stream.ReadSLEB128();
offset_ = stream.Position();
return result;
}
int64_t ReadSLEB128AsInt64() {
ReadStream stream(raw_buffer_, size_, offset_);
const int64_t result = stream.ReadSLEB128<int64_t>();
offset_ = stream.Position();
return result;
}
/**
* Read and return a TokenPosition from this reader.
*/
TokenPosition ReadPosition() {
// Position is saved as unsigned,
// but actually ranges from -1 and up (thus the -1)
intptr_t value = ReadUInt() - 1;
TokenPosition result = TokenPosition::Deserialize(value);
return result;
}
intptr_t ReadListLength() { return ReadUInt(); }
uint8_t ReadByte() { return raw_buffer_[offset_++]; }
uint8_t PeekByte() { return raw_buffer_[offset_]; }
void ReadBytes(uint8_t* buffer, uint8_t size) {
for (int i = 0; i < size; i++) {
buffer[i] = ReadByte();
}
}
const TypedDataBase* typed_data() { return typed_data_; }
intptr_t offset() const { return offset_; }
void set_offset(intptr_t offset) {
ASSERT(offset <= size_);
offset_ = offset;
}
intptr_t size() const { return size_; }
TypedDataViewPtr ViewFromTo(intptr_t start, intptr_t end) {
return typed_data_->ViewFromTo(start, end, Heap::kOld);
}
const uint8_t* BufferAt(intptr_t offset) {
ASSERT((offset >= 0) && (offset < size_));
return &raw_buffer_[offset];
}
private:
friend class AlternativeReadingScope;
void Init() {
ASSERT(typed_data_->IsExternalOrExternalView());
raw_buffer_ = reinterpret_cast<uint8_t*>(typed_data_->DataAddr(0));
size_ = typed_data_->LengthInBytes();
offset_ = 0;
}
// A external typed data or a view on an external typed data.
const TypedDataBase* typed_data_ = nullptr;
// The raw data size/length of [typed_data_].
const uint8_t* raw_buffer_ = nullptr;
intptr_t size_ = 0;
intptr_t offset_ = 0;
};
// A helper class that saves the current reader position, goes to another reader
// position, and upon destruction, resets to the original reader position.
class AlternativeReadingScope {
public:
AlternativeReadingScope(Reader* reader, intptr_t new_position)
: reader_(reader), saved_offset_(reader_->offset_) {
reader_->offset_ = new_position;
}
~AlternativeReadingScope() { reader_->offset_ = saved_offset_; }
private:
Reader* const reader_;
const intptr_t saved_offset_;
DISALLOW_COPY_AND_ASSIGN(AlternativeReadingScope);
};
// Helper class for reading bytecode.
class BytecodeReaderHelper : public ValueObject {
public:
explicit BytecodeReaderHelper(Thread* thread,
const TypedDataBase& typed_data);
explicit BytecodeReaderHelper(Thread* thread,
BytecodeComponentData* bytecode_component);
Reader& reader() { return reader_; }
void ReadCode(const Function& function, intptr_t code_offset);
void ReadMembers(const Class& cls, bool discard_fields);
void ReadFieldDeclarations(const Class& cls, bool discard_fields);
void ReadFunctionDeclarations(const Class& cls);
void ReadClassDeclaration(const Class& cls);
void ReadLibraryDeclaration(const Library& library,
bool lookup_classes,
const GrowableObjectArray& pending_classes);
void ReadLibraryDeclarations(intptr_t num_libraries);
LibraryPtr ReadMain();
ArrayPtr ReadBytecodeComponent();
void ResetObjects();
// Fills in [is_covariant] and [is_generic_covariant_impl] vectors
// according to covariance attributes of [function] parameters.
//
// [function] should be declared in bytecode.
// [is_covariant] and [is_generic_covariant_impl] should contain bitvectors
// of function.NumParameters() length.
void ReadParameterCovariance(const Function& function,
intptr_t code_offset,
BitVector* is_covariant,
BitVector* is_generic_covariant_impl);
// Read bytecode PackedObject.
ObjectPtr ReadObject();
private:
// These constants should match corresponding constants in class ObjectHandle
// (pkg/dart2bytecode/lib/object_table.dart).
static const int kReferenceBit = 1 << 0;
static const int kIndexShift = 1;
static const int kKindShift = 1;
static const int kKindMask = 0x0f;
static const int kFlagBit0 = 1 << 5;
static const int kFlagBit1 = 1 << 6;
static const int kFlagBit2 = 1 << 7;
static const int kFlagBit3 = 1 << 8;
static const int kFlagBit4 = 1 << 9;
static const int kFlagBit5 = 1 << 10;
static const int kTagMask = (kFlagBit0 | kFlagBit1 | kFlagBit2 | kFlagBit3);
static const int kFlagIsNullable = kFlagBit4;
static const int kFlagsMask = (kTagMask | kFlagBit4 | kFlagBit5);
// Code flags, must be in sync with Code constants in
// pkg/dart2bytecode/lib/declarations.dart.
struct Code {
static const int kHasExceptionsTableFlag = 1 << 0;
static const int kHasSourcePositionsFlag = 1 << 1;
static const int kHasNullableFieldsFlag = 1 << 2;
static const int kHasClosuresFlag = 1 << 3;
static const int kHasParameterFlagsFlag = 1 << 4;
static const int kHasForwardingStubTargetFlag = 1 << 5;
static const int kHasDefaultFunctionTypeArgsFlag = 1 << 6;
static const int kHasLocalVariablesFlag = 1 << 7;
};
// Closure code flags, must be in sync with ClosureCode constants in
// pkg/dart2bytecode/lib/declarations.dart.
struct ClosureCode {
static const int kHasExceptionsTableFlag = 1 << 0;
static const int kHasSourcePositionsFlag = 1 << 1;
static const int kHasLocalVariablesFlag = 1 << 2;
};
// Parameter flags, must be in sync with ParameterDeclaration constants in
// pkg/dart2bytecode/lib/declarations.dart.
struct Parameter {
static const int kIsCovariantFlag = 1 << 0;
static const int kIsGenericCovariantImplFlag = 1 << 1;
static const int kIsFinalFlag = 1 << 2;
static const int kIsRequiredFlag = 1 << 3;
};
class FunctionTypeScope : public ValueObject {
public:
explicit FunctionTypeScope(BytecodeReaderHelper* bytecode_reader,
const FunctionType& type)
: bytecode_reader_(bytecode_reader) {
bytecode_reader_->enclosing_function_types_.Add(&type);
}
~FunctionTypeScope() {
bytecode_reader_->enclosing_function_types_.RemoveLast();
}
private:
BytecodeReaderHelper* const bytecode_reader_;
};
class FunctionScope : public ValueObject {
public:
FunctionScope(BytecodeReaderHelper* bytecode_reader,
const Function& function,
const String& name,
const Class& cls)
: bytecode_reader_(bytecode_reader) {
ASSERT(bytecode_reader_->scoped_function_.IsNull());
ASSERT(bytecode_reader_->scoped_function_name_.IsNull());
ASSERT(bytecode_reader_->scoped_function_class_.IsNull());
ASSERT(name.IsSymbol());
bytecode_reader_->scoped_function_ = function.ptr();
bytecode_reader_->scoped_function_name_ = name.ptr();
bytecode_reader_->scoped_function_class_ = cls.ptr();
}
~FunctionScope() {
bytecode_reader_->scoped_function_ = Function::null();
bytecode_reader_->scoped_function_name_ = String::null();
bytecode_reader_->scoped_function_class_ = Class::null();
}
private:
BytecodeReaderHelper* bytecode_reader_;
};
void ReadClosureDeclaration(const Function& function, intptr_t closureIndex);
FunctionTypePtr ReadFunctionSignature(const FunctionType& signature,
bool has_optional_positional_params,
bool has_optional_named_params,
bool has_type_params,
bool has_positional_param_names,
bool has_parameter_flags);
void ReadTypeParametersDeclaration(
const Class& parameterized_class,
const FunctionType& parameterized_signature);
// Read portion of constant pool corresponding to one function/closure.
// Start with [start_index], and stop when reaching EndClosureFunctionScope.
// Return index of the last read constant pool entry.
intptr_t ReadConstantPool(const Function& function,
const ObjectPool& pool,
intptr_t start_index);
BytecodePtr ReadBytecode(const ObjectPool& pool);
void ReadExceptionsTable(const Function& function,
const Bytecode& bytecode,
bool has_exceptions_table);
void ReadSourcePositions(const Bytecode& bytecode, bool has_source_positions);
void ReadLocalVariables(const Bytecode& bytecode, bool has_local_variables);
StringPtr ConstructorName(const Class& cls, const String& name);
ObjectPtr ReadObjectContents(uint32_t header);
ObjectPtr ReadConstObject(intptr_t tag);
ObjectPtr ReadType(intptr_t tag, Nullability nullability);
StringPtr ReadString(bool is_canonical = true);
TypeArgumentsPtr ReadTypeArguments();
void SetupFieldAccessorFunction(const Class& klass,
const Function& function,
const AbstractType& field_type);
PatchClassPtr GetPatchClass(const Class& cls, const Script& script);
InstancePtr Canonicalize(const Instance& instance);
// Similar to cls.EnsureClassDeclaration, but may be more efficient if
// class is from the current kernel binary.
void LoadReferencedClass(const Class& cls);
Reader reader_;
Thread* const thread_;
Zone* const zone_;
BytecodeComponentData* bytecode_component_;
Array* closures_ = nullptr;
PatchClass* patch_class_ = nullptr;
Array* functions_ = nullptr;
intptr_t function_index_ = 0;
GrowableArray<const FunctionType*> enclosing_function_types_;
Function& scoped_function_;
String& scoped_function_name_;
Class& scoped_function_class_;
DISALLOW_COPY_AND_ASSIGN(BytecodeReaderHelper);
};
class BytecodeComponentData : ValueObject {
public:
enum {
kTypedData,
kVersion,
kStringsHeaderOffset,
kStringsContentsOffset,
kObjectOffsetsOffset,
kNumObjects,
kObjectsContentsOffset,
kMainOffset,
kNumLibraries,
kLibraryIndexOffset,
kLibrariesOffset,
kNumClasses,
kClassesOffset,
kMembersOffset,
kNumCodes,
kCodesOffset,
kSourcePositionsOffset,
kSourceFilesOffset,
kLineStartsOffset,
kLocalVariablesOffset,
kAnnotationsOffset,
kNumFields
};
explicit BytecodeComponentData(const Array& data) : data_(data) {}
TypedDataBasePtr GetTypedData() const;
intptr_t GetVersion() const;
intptr_t GetStringsHeaderOffset() const;
intptr_t GetStringsContentsOffset() const;
intptr_t GetObjectOffsetsOffset() const;
intptr_t GetNumObjects() const;
intptr_t GetObjectsContentsOffset() const;
intptr_t GetMainOffset() const;
intptr_t GetNumLibraries() const;
intptr_t GetLibraryIndexOffset() const;
intptr_t GetLibrariesOffset() const;
intptr_t GetNumClasses() const;
intptr_t GetClassesOffset() const;
intptr_t GetMembersOffset() const;
intptr_t GetNumCodes() const;
intptr_t GetCodesOffset() const;
intptr_t GetSourcePositionsOffset() const;
intptr_t GetSourceFilesOffset() const;
intptr_t GetLineStartsOffset() const;
intptr_t GetLocalVariablesOffset() const;
intptr_t GetAnnotationsOffset() const;
void SetObject(intptr_t index, const Object& obj) const;
ObjectPtr GetObject(intptr_t index) const;
bool IsNull() const { return data_.IsNull(); }
static ArrayPtr New(Zone* zone,
const TypedDataBase& typed_data,
intptr_t version,
intptr_t num_objects,
intptr_t strings_header_offset,
intptr_t strings_contents_offset,
intptr_t object_offsets_offset,
intptr_t objects_contents_offset,
intptr_t main_offset,
intptr_t num_libraries,
intptr_t library_index_offset,
intptr_t libraries_offset,
intptr_t num_classes,
intptr_t classes_offset,
intptr_t members_offset,
intptr_t num_codes,
intptr_t codes_offset,
intptr_t source_positions_offset,
intptr_t source_files_offset,
intptr_t line_starts_offset,
intptr_t local_variables_offset,
intptr_t annotations_offset,
Heap::Space space);
private:
const Array& data_;
};
class BytecodeReader : public AllStatic {
public:
// Read declaration of the given class.
static void LoadClassDeclaration(const Class& cls);
// Read members of the given class.
static void FinishClassLoading(const Class& cls);
static void ReadParameterCovariance(const Function& function,
BitVector* is_covariant,
BitVector* is_generic_covariant_impl);
};
class BytecodeSourcePositionsIterator : ValueObject {
public:
// These constants should match corresponding constants in class
// SourcePositions (pkg/dart2bytecode/lib/source_positions.dart).
static const intptr_t kSyntheticCodeMarker = -1;
static const intptr_t kYieldPointMarker = -2;
BytecodeSourcePositionsIterator(Zone* zone, const Bytecode& bytecode)
: reader_(TypedDataBase::Handle(zone, bytecode.binary())) {
ASSERT(bytecode.HasSourcePositions());
reader_.set_offset(bytecode.source_positions_binary_offset());
pairs_remaining_ = reader_.ReadUInt();
}
bool MoveNext() {
if (pairs_remaining_ == 0) {
return false;
}
ASSERT(pairs_remaining_ > 0);
--pairs_remaining_;
cur_bci_ += reader_.ReadUInt();
cur_token_pos_ += reader_.ReadSLEB128();
is_yield_point_ = false;
if (cur_token_pos_ == kYieldPointMarker) {
const bool result = MoveNext();
is_yield_point_ = true;
return result;
}
return true;
}
uword PcOffset() const { return cur_bci_; }
TokenPosition TokenPos() const {
return (cur_token_pos_ == kSyntheticCodeMarker)
? TokenPosition::kNoSource
: TokenPosition::Deserialize(cur_token_pos_);
}
bool IsYieldPoint() const { return is_yield_point_; }
private:
Reader reader_;
intptr_t pairs_remaining_ = 0;
intptr_t cur_bci_ = 0;
intptr_t cur_token_pos_ = 0;
bool is_yield_point_ = false;
};
} // namespace bytecode
} // namespace dart
#endif // defined(DART_DYNAMIC_MODULES)
#endif // RUNTIME_VM_BYTECODE_READER_H_
+43 -11
View File
@@ -7,6 +7,7 @@
#include "vm/class_finalizer.h"
#include "vm/bytecode_reader.h"
#include "vm/canonical_tables.h"
#include "vm/closure_functions_cache.h"
#include "vm/compiler/jit/compiler.h"
@@ -436,7 +437,7 @@ AbstractTypePtr ClassFinalizer::FinalizeType(const AbstractType& type,
}
}
#if !defined(DART_PRECOMPILED_RUNTIME)
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
#if defined(TARGET_ARCH_X64)
static bool IsPotentialExactGeneric(const AbstractType& type) {
@@ -521,7 +522,7 @@ void ClassFinalizer::FinalizeMemberTypes(const Class& cls) {
}
}
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
Thread* thread = Thread::Current();
@@ -531,9 +532,7 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
return;
}
#if defined(DART_PRECOMPILED_RUNTIME)
UNREACHABLE();
#else
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
Zone* zone = thread->zone();
SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
if (cls.is_type_finalized()) {
@@ -585,6 +584,8 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
cls.set_is_type_finalized();
cls.set_is_isolate_unsendable_due_to_pragma(has_isolate_unsendable_pragma);
cls.set_is_future_subtype(is_future_subtype);
#if !defined(DART_PRECOMPILED_RUNTIME)
if (is_future_subtype && !cls.is_abstract()) {
MarkClassCanBeFuture(zone, cls);
}
@@ -593,7 +594,11 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
}
ClassHiearchyUpdater(zone).Register(cls);
#endif // defined(DART_PRECOMPILED_RUNTIME)
#endif // !defined(DART_PRECOMPILED_RUNTIME)
#else
UNREACHABLE();
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
}
#if !defined(DART_PRECOMPILED_RUNTIME)
@@ -709,7 +714,7 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
return;
}
#if defined(DART_PRECOMPILED_RUNTIME)
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
UNREACHABLE();
#else
Thread* thread = Thread::Current();
@@ -729,9 +734,22 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
#endif // defined(SUPPORT_TIMELINE)
// If loading from a kernel, make sure that the class is fully loaded.
ASSERT(cls.IsTopLevel() || (cls.kernel_offset() > 0));
ASSERT(cls.IsTopLevel() || cls.is_declared_in_bytecode() ||
(cls.kernel_offset() > 0));
if (!cls.is_loaded()) {
kernel::KernelLoader::FinishLoading(cls);
if (cls.is_declared_in_bytecode()) {
#if defined(DART_DYNAMIC_MODULES)
bytecode::BytecodeReader::FinishClassLoading(cls);
#else
UNREACHABLE();
#endif
} else {
#if defined(DART_PRECOMPILED_RUNTIME)
UNREACHABLE();
#else
kernel::KernelLoader::FinishLoading(cls);
#endif
}
if (cls.is_finalized()) {
return;
}
@@ -747,9 +765,11 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
}
// Mark as loaded and finalized.
cls.Finalize();
#if !defined(DART_PRECOMPILED_RUNTIME)
if (FLAG_print_classes) {
PrintClassInformation(cls);
}
#endif
FinalizeMemberTypes(cls);
// The rest of finalization for non-top-level class has to be done with
@@ -758,7 +778,15 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
if (cls.IsTopLevel()) {
cls.set_is_allocate_finalized();
}
#if defined(DART_PRECOMPILED_RUNTIME)
// Allocate-finalization is a no-op in AOT, so
// mark finalized classed as allocate-finalized eagerly.
if (!cls.is_allocate_finalized()) {
cls.set_is_allocate_finalized();
}
#endif // defined(DART_PRECOMPILED_RUNTIME)
#endif // defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
}
#if !defined(DART_PRECOMPILED_RUNTIME)
@@ -814,15 +842,16 @@ ErrorPtr ClassFinalizer::AllocateFinalizeClass(const Class& cls) {
return Error::null();
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
ErrorPtr ClassFinalizer::LoadClassMembers(const Class& cls) {
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
ASSERT(!cls.is_finalized());
LongJumpScope jump;
if (setjmp(*jump.Set()) == 0) {
#if !defined(DART_PRECOMPILED_RUNTIME)
cls.EnsureDeclarationLoaded();
#endif
ASSERT(cls.is_type_finalized());
ClassFinalizer::FinalizeClass(cls);
return Error::null();
@@ -830,6 +859,9 @@ ErrorPtr ClassFinalizer::LoadClassMembers(const Class& cls) {
return Thread::Current()->StealStickyError();
}
}
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
#if !defined(DART_PRECOMPILED_RUNTIME)
void ClassFinalizer::PrintClassInformation(const Class& cls) {
Thread* thread = Thread::Current();
+7 -1
View File
@@ -68,13 +68,17 @@ class ClassFinalizer : public AllStatic {
#if !defined(DART_PRECOMPILED_RUNTIME)
// Makes class instantiatable and usable by generated code.
static ErrorPtr AllocateFinalizeClass(const Class& cls);
#endif // !defined(DART_PRECOMPILED_RUNTIME)
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
// Completes loading of the class, this populates the function
// and fields of the class.
//
// Returns Error::null() if there is no loading error.
static ErrorPtr LoadClassMembers(const Class& cls);
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
#if !defined(DART_PRECOMPILED_RUNTIME)
// Verify that the classes have been properly prefinalized. This is
// needed during bootstrapping where the classes have been preloaded.
static void VerifyBootstrapClasses();
@@ -91,8 +95,10 @@ class ClassFinalizer : public AllStatic {
const TypeParameters& type_params,
FinalizationKind finalization);
#if !defined(DART_PRECOMPILED_RUNTIME)
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
static void FinalizeMemberTypes(const Class& cls);
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
#if !defined(DART_PRECOMPILED_RUNTIME)
static void PrintClassInformation(const Class& cls);
#endif // !defined(DART_PRECOMPILED_RUNTIME)
+1
View File
@@ -38,6 +38,7 @@ static constexpr intptr_t kClassIdTagMax = (1 << 20) - 1;
V(WeakSerializationReference) \
V(WeakArray) \
V(Code) \
V(Bytecode) \
V(Instructions) \
V(InstructionsSection) \
V(InstructionsTable) \
+17 -2
View File
@@ -1948,8 +1948,15 @@ void Precompiler::TraceForRetainedFunctions() {
function ^= functions.At(j);
function.DropUncompiledImplicitClosureFunction();
const bool retained =
possibly_retained_functions_.ContainsKey(function);
bool retained = possibly_retained_functions_.ContainsKey(function);
#if defined(DART_DYNAMIC_MODULES)
// Retain abstract functions annotated with entry point
// pragmas as they can be used as targets of interface calls.
if (function.is_abstract() &&
functions_with_entry_point_pragmas_.ContainsKey(function)) {
retained = true;
}
#endif // defined(DART_DYNAMIC_MODULES)
if (retained) {
AddTypesOf(function);
}
@@ -2575,6 +2582,14 @@ void Precompiler::DropTransitiveUserDefinedConstants() {
if (cls.constants() == Array::null()) {
continue;
}
#if defined(DART_DYNAMIC_MODULES)
// Retain constant tables of exported classes to allow constant
// canonicalization at runtime.
if (HasApiUse(cls)) {
continue;
}
#endif // defined(DART_DYNAMIC_MODULES)
typedef UnorderedHashSet<CanonicalInstanceTraits> CanonicalInstancesSet;
CanonicalInstancesSet constants_set(cls.constants());
@@ -2017,7 +2017,7 @@ void AsmIntrinsifier::Timeline_isDartStreamEnabled(Assembler* assembler,
#else
Label true_label;
// Load TimelineStream*.
__ lx(A0, Address(THR, target::Thread::dart_stream_offset()));
__ LoadFromOffset(A0, THR, target::Thread::dart_stream_offset());
// Load uintptr_t from TimelineStream*.
__ lx(A0, Address(A0, target::TimelineStream::enabled_offset()));
__ bnez(A0, &true_label, Assembler::kNearJump);
@@ -0,0 +1,414 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/globals.h"
#if defined(DART_DYNAMIC_MODULES)
#include "vm/compiler/assembler/disassembler_kbc.h"
#include "platform/assert.h"
#include "vm/bytecode_reader.h"
#include "vm/constants_kbc.h"
namespace dart {
static const char* kOpcodeNames[] = {
#define BYTECODE_NAME(name, encoding, kind, op1, op2, op3) #name,
KERNEL_BYTECODES_LIST(BYTECODE_NAME)
#undef BYTECODE_NAME
};
static const size_t kOpcodeCount =
sizeof(kOpcodeNames) / sizeof(kOpcodeNames[0]);
static_assert(kOpcodeCount <= 256, "Opcode should fit into a byte");
typedef void (*BytecodeFormatter)(char* buffer,
intptr_t size,
KernelBytecode::Opcode opcode,
const KBCInstr* instr);
typedef void (*Fmt)(char** buf,
intptr_t* size,
const KBCInstr* instr,
int32_t value);
template <typename ValueType>
void FormatOperand(char** buf,
intptr_t* size,
const char* fmt,
ValueType value) {
intptr_t written = Utils::SNPrint(*buf, *size, fmt, value);
if (written < *size) {
*buf += written;
*size += written;
} else {
*size = -1;
}
}
static void Fmt___(char** buf,
intptr_t* size,
const KBCInstr* instr,
int32_t value) {}
static void Fmttgt(char** buf,
intptr_t* size,
const KBCInstr* instr,
int32_t value) {
if (FLAG_disassemble_relative) {
FormatOperand(buf, size, "-> %" Pd, value);
} else {
FormatOperand(buf, size, "-> %" Px, instr + value);
}
}
static void Fmtlit(char** buf,
intptr_t* size,
const KBCInstr* instr,
int32_t value) {
FormatOperand(buf, size, "k%d", value);
}
static void Fmtreg(char** buf,
intptr_t* size,
const KBCInstr* instr,
int32_t value) {
FormatOperand(buf, size, "r%d", value);
}
static void Fmtxeg(char** buf,
intptr_t* size,
const KBCInstr* instr,
int32_t value) {
if (value < 0) {
FormatOperand(buf, size, "FP[%d]", value);
} else {
Fmtreg(buf, size, instr, value);
}
}
static void Fmtnum(char** buf,
intptr_t* size,
const KBCInstr* instr,
int32_t value) {
FormatOperand(buf, size, "#%d", value);
}
static void Apply(char** buf,
intptr_t* size,
const KBCInstr* instr,
Fmt fmt,
int32_t value,
const char* suffix) {
if (*size <= 0) {
return;
}
fmt(buf, size, instr, value);
if (*size > 0) {
FormatOperand(buf, size, "%s", suffix);
}
}
static void Format0(char* buf,
intptr_t size,
KernelBytecode::Opcode opcode,
const KBCInstr* instr,
Fmt op1,
Fmt op2,
Fmt op3) {}
static void FormatA(char* buf,
intptr_t size,
KernelBytecode::Opcode opcode,
const KBCInstr* instr,
Fmt op1,
Fmt op2,
Fmt op3) {
const int32_t a = KernelBytecode::DecodeA(instr);
Apply(&buf, &size, instr, op1, a, "");
}
static void FormatD(char* buf,
intptr_t size,
KernelBytecode::Opcode opcode,
const KBCInstr* instr,
Fmt op1,
Fmt op2,
Fmt op3) {
const int32_t bc = KernelBytecode::DecodeD(instr);
Apply(&buf, &size, instr, op1, bc, "");
}
static void FormatX(char* buf,
intptr_t size,
KernelBytecode::Opcode opcode,
const KBCInstr* instr,
Fmt op1,
Fmt op2,
Fmt op3) {
const int32_t bc = KernelBytecode::DecodeX(instr);
Apply(&buf, &size, instr, op1, bc, "");
}
static void FormatT(char* buf,
intptr_t size,
KernelBytecode::Opcode opcode,
const KBCInstr* instr,
Fmt op1,
Fmt op2,
Fmt op3) {
const int32_t x = KernelBytecode::DecodeT(instr);
Apply(&buf, &size, instr, op1, x, "");
}
static void FormatA_E(char* buf,
intptr_t size,
KernelBytecode::Opcode opcode,
const KBCInstr* instr,
Fmt op1,
Fmt op2,
Fmt op3) {
const int32_t a = KernelBytecode::DecodeA(instr);
const int32_t e = KernelBytecode::DecodeE(instr);
Apply(&buf, &size, instr, op1, a, ", ");
Apply(&buf, &size, instr, op2, e, "");
}
static void FormatA_Y(char* buf,
intptr_t size,
KernelBytecode::Opcode opcode,
const KBCInstr* instr,
Fmt op1,
Fmt op2,
Fmt op3) {
const int32_t a = KernelBytecode::DecodeA(instr);
const int32_t y = KernelBytecode::DecodeY(instr);
Apply(&buf, &size, instr, op1, a, ", ");
Apply(&buf, &size, instr, op2, y, "");
}
static void FormatD_F(char* buf,
intptr_t size,
KernelBytecode::Opcode opcode,
const KBCInstr* instr,
Fmt op1,
Fmt op2,
Fmt op3) {
const int32_t d = KernelBytecode::DecodeD(instr);
const int32_t f = KernelBytecode::DecodeF(instr);
Apply(&buf, &size, instr, op1, d, ", ");
Apply(&buf, &size, instr, op2, f, "");
}
static void FormatA_B_C(char* buf,
intptr_t size,
KernelBytecode::Opcode opcode,
const KBCInstr* instr,
Fmt op1,
Fmt op2,
Fmt op3) {
const int32_t a = KernelBytecode::DecodeA(instr);
const int32_t b = KernelBytecode::DecodeB(instr);
const int32_t c = KernelBytecode::DecodeC(instr);
Apply(&buf, &size, instr, op1, a, ", ");
Apply(&buf, &size, instr, op2, b, ", ");
Apply(&buf, &size, instr, op3, c, "");
}
#define BYTECODE_FORMATTER(name, encoding, kind, op1, op2, op3) \
static void Format##name(char* buf, intptr_t size, \
KernelBytecode::Opcode opcode, \
const KBCInstr* instr) { \
Format##encoding(buf, size, opcode, instr, Fmt##op1, Fmt##op2, Fmt##op3); \
}
KERNEL_BYTECODES_LIST(BYTECODE_FORMATTER)
#undef BYTECODE_FORMATTER
static const BytecodeFormatter kFormatters[] = {
#define BYTECODE_FORMATTER(name, encoding, kind, op1, op2, op3) &Format##name,
KERNEL_BYTECODES_LIST(BYTECODE_FORMATTER)
#undef BYTECODE_FORMATTER
};
static intptr_t GetConstantPoolIndex(const KBCInstr* instr) {
switch (KernelBytecode::DecodeOpcode(instr)) {
case KernelBytecode::kLoadConstant:
case KernelBytecode::kLoadConstant_Wide:
case KernelBytecode::kInstantiateTypeArgumentsTOS:
case KernelBytecode::kInstantiateTypeArgumentsTOS_Wide:
case KernelBytecode::kAssertAssignable:
case KernelBytecode::kAssertAssignable_Wide:
return KernelBytecode::DecodeE(instr);
case KernelBytecode::kPushConstant:
case KernelBytecode::kPushConstant_Wide:
case KernelBytecode::kInitLateField:
case KernelBytecode::kInitLateField_Wide:
case KernelBytecode::kStoreStaticTOS:
case KernelBytecode::kStoreStaticTOS_Wide:
case KernelBytecode::kLoadStatic:
case KernelBytecode::kLoadStatic_Wide:
case KernelBytecode::kAllocate:
case KernelBytecode::kAllocate_Wide:
case KernelBytecode::kAllocateClosure:
case KernelBytecode::kAllocateClosure_Wide:
case KernelBytecode::kInstantiateType:
case KernelBytecode::kInstantiateType_Wide:
case KernelBytecode::kDirectCall:
case KernelBytecode::kDirectCall_Wide:
case KernelBytecode::kUncheckedDirectCall:
case KernelBytecode::kUncheckedDirectCall_Wide:
case KernelBytecode::kInterfaceCall:
case KernelBytecode::kInterfaceCall_Wide:
case KernelBytecode::kInstantiatedInterfaceCall:
case KernelBytecode::kInstantiatedInterfaceCall_Wide:
case KernelBytecode::kUncheckedClosureCall:
case KernelBytecode::kUncheckedClosureCall_Wide:
case KernelBytecode::kUncheckedInterfaceCall:
case KernelBytecode::kUncheckedInterfaceCall_Wide:
case KernelBytecode::kDynamicCall:
case KernelBytecode::kDynamicCall_Wide:
return KernelBytecode::DecodeD(instr);
default:
return -1;
}
}
static bool GetLoadedObjectAt(uword pc,
const ObjectPool& object_pool,
Object* obj) {
const KBCInstr* instr = reinterpret_cast<const KBCInstr*>(pc);
const intptr_t index = GetConstantPoolIndex(instr);
if (index >= 0) {
if (object_pool.TypeAt(index) == ObjectPool::EntryType::kTaggedObject) {
*obj = object_pool.ObjectAt(index);
return true;
}
}
return false;
}
void KernelBytecodeDisassembler::DecodeInstruction(char* hex_buffer,
intptr_t hex_size,
char* human_buffer,
intptr_t human_size,
int* out_instr_size,
const Bytecode& bytecode,
Object** object,
uword pc) {
const KBCInstr* instr = reinterpret_cast<const KBCInstr*>(pc);
const KernelBytecode::Opcode opcode = KernelBytecode::DecodeOpcode(instr);
const intptr_t instr_size = KernelBytecode::kInstructionSize[opcode];
size_t name_size =
Utils::SNPrint(human_buffer, human_size, "%-10s\t", kOpcodeNames[opcode]);
human_buffer += name_size;
human_size -= name_size;
kFormatters[opcode](human_buffer, human_size, opcode, instr);
const intptr_t kCharactersPerByte = 3;
if (hex_size > instr_size * kCharactersPerByte) {
for (intptr_t i = 0; i < instr_size; ++i) {
Utils::SNPrint(hex_buffer + (i * kCharactersPerByte),
hex_size - (i * kCharactersPerByte), " %02x", instr[i]);
}
}
if (out_instr_size != nullptr) {
*out_instr_size = instr_size;
}
*object = NULL;
if (!bytecode.IsNull()) {
*object = &Object::Handle();
const ObjectPool& pool = ObjectPool::Handle(bytecode.object_pool());
if (!GetLoadedObjectAt(pc, pool, *object)) {
*object = NULL;
}
}
}
void KernelBytecodeDisassembler::Disassemble(uword start,
uword end,
DisassemblyFormatter* formatter,
const Bytecode& bytecode) {
#if !defined(PRODUCT)
ASSERT(formatter != NULL);
char hex_buffer[kHexadecimalBufferSize]; // Instruction in hexadecimal form.
char human_buffer[kUserReadableBufferSize]; // Human-readable instruction.
uword pc = start;
GrowableArray<const Function*> inlined_functions;
GrowableArray<TokenPosition> token_positions;
while (pc < end) {
int instruction_length;
Object* object;
DecodeInstruction(hex_buffer, sizeof(hex_buffer), human_buffer,
sizeof(human_buffer), &instruction_length, bytecode,
&object, pc);
formatter->ConsumeInstruction(hex_buffer, sizeof(hex_buffer), human_buffer,
sizeof(human_buffer), object,
FLAG_disassemble_relative ? pc - start : pc);
pc += instruction_length;
}
#else
UNREACHABLE();
#endif
}
void KernelBytecodeDisassembler::Disassemble(const Function& function) {
#if !defined(PRODUCT)
ASSERT(function.HasBytecode());
const char* function_fullname = function.ToFullyQualifiedCString();
Zone* zone = Thread::Current()->zone();
const Bytecode& bytecode = Bytecode::Handle(zone, function.GetBytecode());
THR_Print("Bytecode for function '%s' {\n", function_fullname);
const uword start = bytecode.PayloadStart();
const uword base = FLAG_disassemble_relative ? 0 : start;
DisassembleToStdout stdout_formatter;
LogBlock lb;
Disassemble(start, start + bytecode.Size(), &stdout_formatter, bytecode);
THR_Print("}\n");
const ObjectPool& object_pool =
ObjectPool::Handle(zone, bytecode.object_pool());
object_pool.DebugPrint();
THR_Print("PC Descriptors for function '%s' {\n", function_fullname);
const PcDescriptors& descriptors =
PcDescriptors::Handle(zone, bytecode.pc_descriptors());
THR_Print("%s}\n", descriptors.ToCString());
if (bytecode.HasSourcePositions()) {
THR_Print("Source positions for function '%s' {\n", function_fullname);
// 4 bits per hex digit + 2 for "0x".
const int addr_width = (kBitsPerWord / 4) + 2;
// "*" in a printf format specifier tells it to read the field width from
// the printf argument list.
THR_Print("%-*s\tpos\tline\tcolumn\tyield\n", addr_width, "pc");
const Script& script = Script::Handle(zone, function.script());
bytecode::BytecodeSourcePositionsIterator iter(zone, bytecode);
while (iter.MoveNext()) {
TokenPosition pos = iter.TokenPos();
intptr_t line = -1, column = -1;
script.GetTokenLocation(pos, &line, &column);
THR_Print("%#-*" Px "\t%s\t%" Pd "\t%" Pd "\t%s\n", addr_width,
base + iter.PcOffset(), pos.ToCString(), line, column,
iter.IsYieldPoint() ? "yield" : "");
}
THR_Print("}\n");
}
THR_Print("Exception Handlers for function '%s' {\n", function_fullname);
const ExceptionHandlers& handlers =
ExceptionHandlers::Handle(zone, bytecode.exception_handlers());
THR_Print("%s}\n", handlers.ToCString());
#else
UNREACHABLE();
#endif
}
} // namespace dart
#endif // defined(DART_DYNAMIC_MODULES)
@@ -0,0 +1,89 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_COMPILER_ASSEMBLER_DISASSEMBLER_KBC_H_
#define RUNTIME_VM_COMPILER_ASSEMBLER_DISASSEMBLER_KBC_H_
#include "vm/globals.h"
#if defined(DART_DYNAMIC_MODULES)
#include "vm/compiler/assembler/disassembler.h"
namespace dart {
// Disassemble instructions.
class KernelBytecodeDisassembler : public AllStatic {
public:
// Disassemble instructions between start and end.
// (The assumption is that start is at a valid instruction).
// Return true if all instructions were successfully decoded, false otherwise.
static void Disassemble(uword start,
uword end,
DisassemblyFormatter* formatter,
const Bytecode& bytecode);
static void Disassemble(uword start,
uword end,
DisassemblyFormatter* formatter) {
Disassemble(start, end, formatter, Bytecode::Handle());
}
static void Disassemble(uword start, uword end, const Bytecode& bytecode) {
#if !defined(PRODUCT)
DisassembleToStdout stdout_formatter;
LogBlock lb;
Disassemble(start, end, &stdout_formatter, bytecode);
#else
UNREACHABLE();
#endif
}
static void Disassemble(uword start, uword end) {
#if !defined(PRODUCT)
DisassembleToStdout stdout_formatter;
LogBlock lb;
Disassemble(start, end, &stdout_formatter);
#else
UNREACHABLE();
#endif
}
static void Disassemble(uword start,
uword end,
char* buffer,
uintptr_t buffer_size) {
#if !defined(PRODUCT)
DisassembleToMemory memory_formatter(buffer, buffer_size);
LogBlock lb;
Disassemble(start, end, &memory_formatter);
#else
UNREACHABLE();
#endif
}
// Decodes one instruction.
// Writes a hexadecimal representation into the hex_buffer and a
// human-readable representation into the human_buffer.
// Writes the length of the decoded instruction in bytes in out_instr_len.
static void DecodeInstruction(char* hex_buffer,
intptr_t hex_size,
char* human_buffer,
intptr_t human_size,
int* out_instr_len,
const Bytecode& bytecode,
Object** object,
uword pc);
static void Disassemble(const Function& function);
private:
static const int kHexadecimalBufferSize = 32;
static const int kUserReadableBufferSize = 256;
};
} // namespace dart
#endif // defined(DART_DYNAMIC_MODULES)
#endif // RUNTIME_VM_COMPILER_ASSEMBLER_DISASSEMBLER_KBC_H_
+5
View File
@@ -809,6 +809,11 @@ void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// R0: Closure with a cached entry point.
__ ldr(R2, compiler::FieldAddress(
R0, compiler::target::Closure::entry_point_offset()));
#if defined(DART_DYNAMIC_MODULES)
ASSERT(FUNCTION_REG != R2);
__ ldr(FUNCTION_REG, compiler::FieldAddress(
R0, compiler::target::Closure::function_offset()));
#endif
} else {
ASSERT(locs()->in(0).reg() == FUNCTION_REG);
// FUNCTION_REG: Function.
+5
View File
@@ -651,6 +651,11 @@ void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// R0: Closure with a cached entry point.
__ LoadFieldFromOffset(R2, R0,
compiler::target::Closure::entry_point_offset());
#if defined(DART_DYNAMIC_MODULES)
ASSERT(FUNCTION_REG != R2);
__ LoadCompressedFieldFromOffset(
FUNCTION_REG, R0, compiler::target::Closure::function_offset());
#endif
} else {
ASSERT(locs()->in(0).reg() == FUNCTION_REG);
// FUNCTION_REG: Function.
+5
View File
@@ -700,6 +700,11 @@ void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// T0: Closure with a cached entry point.
__ LoadFieldFromOffset(A1, T0,
compiler::target::Closure::entry_point_offset());
#if defined(DART_DYNAMIC_MODULES)
ASSERT(FUNCTION_REG != A1);
__ LoadCompressedFieldFromOffset(
FUNCTION_REG, T0, compiler::target::Closure::function_offset());
#endif
} else {
ASSERT(locs()->in(0).reg() == FUNCTION_REG);
// FUNCTION_REG: Function.
+6
View File
@@ -6615,6 +6615,12 @@ void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
// RAX: Closure with cached entry point.
__ movq(RCX, compiler::FieldAddress(
RAX, compiler::target::Closure::entry_point_offset()));
#if defined(DART_DYNAMIC_MODULES)
ASSERT(FUNCTION_REG != RCX);
__ LoadCompressed(FUNCTION_REG,
compiler::FieldAddress(
RAX, compiler::target::Closure::function_offset()));
#endif
} else {
ASSERT(locs()->in(0).reg() == FUNCTION_REG);
// FUNCTION_REG: Function.
+2
View File
@@ -205,6 +205,8 @@ disassembler_sources = [
"assembler/disassembler.h",
"assembler/disassembler_arm.cc",
"assembler/disassembler_arm64.cc",
"assembler/disassembler_kbc.cc",
"assembler/disassembler_kbc.h",
"assembler/disassembler_riscv.cc",
"assembler/disassembler_x86.cc",
]
@@ -798,6 +798,7 @@ LocalVariable* FlowGraphBuilder::LookupVariable(intptr_t kernel_offset) {
FlowGraph* FlowGraphBuilder::BuildGraph() {
const Function& function = parsed_function_->function();
ASSERT(!function.is_declared_in_bytecode());
#ifdef DEBUG
// Check that all functions that are explicitly marked as recognized with the
@@ -1390,7 +1390,6 @@ class KernelReaderHelper {
friend class VariableDeclarationHelper;
friend class ObfuscationProhibitionsMetadataHelper;
friend class LoadingUnitsMetadataHelper;
friend bool NeedsDynamicInvocationForwarder(const Function& function);
friend ArrayPtr CollectConstConstructorCoverageFrom(
const Script& interesting_script);
+8
View File
@@ -877,6 +877,12 @@ class KernelProgramInfo : public AllStatic {
FINAL_CLASS();
};
class Bytecode : public AllStatic {
public:
static word InstanceSize();
FINAL_CLASS();
};
class PcDescriptors : public AllStatic {
public:
static word HeaderSize();
@@ -1215,6 +1221,8 @@ class Thread : public AllStatic {
static word slow_type_test_stub_offset();
static word call_to_runtime_stub_offset();
static word invoke_dart_code_stub_offset();
static word interpret_call_entry_point_offset();
static word invoke_dart_code_from_bytecode_stub_offset();
static word late_initialization_error_shared_without_fpu_regs_stub_offset();
static word late_initialization_error_shared_with_fpu_regs_stub_offset();
static word null_error_shared_without_fpu_regs_stub_offset();
File diff suppressed because it is too large Load Diff
@@ -281,6 +281,8 @@
FIELD(Thread, float_not_address_offset) \
FIELD(Thread, float_zerow_address_offset) \
FIELD(Thread, global_object_pool_offset) \
FIELD(Thread, interpret_call_entry_point_offset) \
FIELD(Thread, invoke_dart_code_from_bytecode_stub_offset) \
FIELD(Thread, invoke_dart_code_stub_offset) \
FIELD(Thread, exit_through_ffi_offset) \
FIELD(Thread, isolate_offset) \
@@ -410,6 +412,7 @@
SIZEOF(ApiError, InstanceSize, UntaggedApiError) \
SIZEOF(Array, header_size, UntaggedArray) \
SIZEOF(Bool, InstanceSize, UntaggedBool) \
SIZEOF(Bytecode, InstanceSize, UntaggedBytecode) \
SIZEOF(Capability, InstanceSize, UntaggedCapability) \
SIZEOF(Class, InstanceSize, UntaggedClass) \
SIZEOF(Closure, InstanceSize, UntaggedClosure) \
@@ -150,8 +150,14 @@ void StubCodeCompiler::GenerateInitLateInstanceFieldStub(bool is_final) {
if (!FLAG_precompiled_mode) {
__ LoadCompressedFieldFromOffset(CODE_REG, FUNCTION_REG,
target::Function::code_offset());
#if defined(DART_DYNAMIC_MODULES)
// InterpretCall stub needs arguments descriptor for all function calls.
__ LoadObject(ARGS_DESC_REG, ArgumentsDescriptorBoxed(/*type_args_len=*/0,
/*num_arguments=*/1));
#else
// Load a GC-safe value for the arguments descriptor (unused but tagged).
__ LoadImmediate(ARGS_DESC_REG, 0);
#endif // defined(DART_DYNAMIC_MODULES)
}
__ Call(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
__ Drop(1); // Drop argument.
+225 -3
View File
@@ -1227,7 +1227,7 @@ void StubCodeCompiler::GenerateAllocateMintSharedWithoutFPURegsStub() {
// Called when invoking Dart code from C++ (VM code).
// Input parameters:
// LR : points to return address.
// R0 : target code or entry point (in bare instructions mode).
// R0 : target code or entry point (in AOT mode).
// R1 : arguments descriptor array.
// R2 : arguments array.
// R3 : current thread.
@@ -1356,6 +1356,143 @@ void StubCodeCompiler::GenerateInvokeDartCodeStub() {
__ Ret();
}
// Called when invoking compiled Dart code from interpreted Dart code.
// Input parameters:
// LR : points to return address.
// R0 : target code or entry point (in AOT mode).
// R1 : arguments descriptor array.
// R2 : address of first argument.
// R3 : current thread.
void StubCodeCompiler::GenerateInvokeDartCodeFromBytecodeStub() {
#if defined(DART_DYNAMIC_MODULES)
SPILLS_LR_TO_FRAME(__ EnterFrame((1 << FP) | (1 << LR), 0));
// Push code object to PC marker slot.
__ ldr(IP,
Address(R3,
target::Thread::invoke_dart_code_from_bytecode_stub_offset()));
__ Push(IP);
__ PushNativeCalleeSavedRegisters();
// Set up THR, which caches the current thread in Dart code.
if (THR != R3) {
__ mov(THR, Operand(R3));
}
#if defined(USING_SHADOW_CALL_STACK)
#error Unimplemented
#endif
// Save the current VMTag on the stack.
__ LoadFromOffset(R9, THR, target::Thread::vm_tag_offset());
__ Push(R9);
// Save top resource and top exit frame info. Use R4-6 as temporary registers.
// StackFrameIterator reads the top exit frame info saved in this frame.
__ LoadFromOffset(R4, THR, target::Thread::top_resource_offset());
__ Push(R4);
__ LoadImmediate(R8, 0);
__ StoreToOffset(R8, THR, target::Thread::top_resource_offset());
__ LoadFromOffset(R8, THR, target::Thread::exit_through_ffi_offset());
__ Push(R8);
__ LoadImmediate(R8, 0);
__ StoreToOffset(R8, THR, target::Thread::exit_through_ffi_offset());
__ LoadFromOffset(R9, THR, target::Thread::top_exit_frame_info_offset());
__ StoreToOffset(R8, THR, target::Thread::top_exit_frame_info_offset());
// target::frame_layout.exit_link_slot_from_entry_fp must be kept in sync
// with the code below.
#if defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)
ASSERT(target::frame_layout.exit_link_slot_from_entry_fp == -27);
#else
ASSERT(target::frame_layout.exit_link_slot_from_entry_fp == -28);
#endif
__ Push(R9);
__ EmitEntryFrameVerification(R9);
// Mark that the thread is executing Dart code. Do this after initializing the
// exit link for the profiler.
__ LoadImmediate(R9, VMTag::kDartTagId);
__ StoreToOffset(R9, THR, target::Thread::vm_tag_offset());
// Load arguments descriptor array into R4, which is passed to Dart code.
__ mov(R4, Operand(R1));
// Load number of arguments into R9 and adjust count for type arguments.
__ ldr(R3,
FieldAddress(R4, target::ArgumentsDescriptor::type_args_len_offset()));
__ ldr(R9, FieldAddress(R4, target::ArgumentsDescriptor::count_offset()));
__ cmp(R3, Operand(0));
__ AddImmediate(R9, R9, target::ToRawSmi(1),
NE); // Include the type arguments.
__ SmiUntag(R9);
// R2 points to first argument.
// Set up arguments for the Dart call.
Label push_arguments;
Label done_push_arguments;
__ CompareImmediate(R9, 0); // check if there are arguments.
__ b(&done_push_arguments, EQ);
__ LoadImmediate(R1, 0);
__ Bind(&push_arguments);
__ ldr(R3, Address(R2));
__ Push(R3);
__ AddImmediate(R2, target::kWordSize);
__ AddImmediate(R1, 1);
__ cmp(R1, Operand(R9));
__ b(&push_arguments, LT);
__ Bind(&done_push_arguments);
// Call the Dart code entrypoint.
if (FLAG_precompiled_mode) {
__ SetupGlobalPoolAndDispatchTable();
__ LoadImmediate(CODE_REG, 0); // GC safe value into CODE_REG.
} else {
__ LoadImmediate(PP, 0); // GC safe value into PP.
__ mov(CODE_REG, Operand(R0));
__ ldr(R0, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
}
__ blx(R0); // R4 is the arguments descriptor array.
// Get rid of arguments pushed on the stack.
__ AddImmediate(
SP, FP,
target::frame_layout.exit_link_slot_from_entry_fp * target::kWordSize);
// Restore the saved top exit frame info and top resource back into the
// Isolate structure. Uses R9 as a temporary register for this.
__ Pop(R9);
__ StoreToOffset(R9, THR, target::Thread::top_exit_frame_info_offset());
__ Pop(R9);
__ StoreToOffset(R9, THR, target::Thread::exit_through_ffi_offset());
__ Pop(R9);
__ StoreToOffset(R9, THR, target::Thread::top_resource_offset());
// Restore the current VMTag from the stack.
__ Pop(R4);
__ StoreToOffset(R4, THR, target::Thread::vm_tag_offset());
#if defined(USING_SHADOW_CALL_STACK)
#error Unimplemented
#endif
__ PopNativeCalleeSavedRegisters();
__ set_constant_pool_allowed(false);
// Restore the frame pointer and return.
RESTORES_LR_FROM_FRAME(__ LeaveFrame((1 << FP) | (1 << LR)));
__ Ret();
#else
__ Stop("Not using Dart dynamic modules");
#endif // defined(DART_DYNAMIC_MODULES)
}
// Helper to generate space allocation of context stub.
// This does not initialise the fields of the context.
// Input:
@@ -2656,6 +2793,90 @@ void StubCodeCompiler::GenerateLazyCompileStub() {
__ Branch(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
}
// Stub for interpreting a function call.
// R4: Arguments descriptor.
// R0: Function.
void StubCodeCompiler::GenerateInterpretCallStub() {
#if defined(DART_DYNAMIC_MODULES)
__ EnterStubFrame();
#if defined(DEBUG)
{
Label ok;
// Check that we are always entering from Dart code.
__ LoadFromOffset(kWord, R8, THR, target::Thread::vm_tag_offset());
__ CompareImmediate(R8, VMTag::kDartTagId);
__ b(&ok, EQ);
__ Stop("Not coming from Dart code.");
__ Bind(&ok);
}
#endif
// Adjust arguments count for type arguments vector.
__ LoadFieldFromOffset(kWord, R2, R4,
target::ArgumentsDescriptor::count_offset());
__ SmiUntag(R2);
__ LoadFieldFromOffset(kWord, R1, R4,
target::ArgumentsDescriptor::type_args_len_offset());
__ cmp(R1, Operand(0));
__ AddImmediate(R2, R2, 1, NE); // Include the type arguments.
// Compute argv.
__ mov(R3, Operand(R2, LSL, 2));
__ add(R3, FP, Operand(R3));
__ AddImmediate(R3,
target::frame_layout.param_end_from_fp * target::kWordSize);
// Indicate decreasing memory addresses of arguments with negative argc.
__ rsb(R2, R2, Operand(0));
// Align frame before entering C++ world. Fifth argument passed on the stack.
__ ReserveAlignedFrameSpace(1 * target::kWordSize);
// Pass arguments in registers.
// R0: Function.
__ mov(R1, Operand(R4)); // Arguments descriptor.
// R2: Negative argc.
// R3: Argv.
__ str(THR, Address(SP, 0)); // Fifth argument: Thread.
// Save exit frame information to enable stack walking as we are about
// to transition to Dart VM C++ code.
__ StoreToOffset(kWord, FP, THR,
target::Thread::top_exit_frame_info_offset());
// Mark that the thread exited generated code through a runtime call.
__ LoadImmediate(R5, target::Thread::exit_through_runtime_call());
__ StoreToOffset(kWord, R5, THR, target::Thread::exit_through_ffi_offset());
// Mark that the thread is executing VM code.
__ LoadFromOffset(kWord, R5, THR,
target::Thread::interpret_call_entry_point_offset());
__ StoreToOffset(kWord, R5, THR, target::Thread::vm_tag_offset());
__ blx(R5);
// Mark that the thread is executing Dart code.
__ LoadImmediate(R2, VMTag::kDartTagId);
__ StoreToOffset(kWord, R2, THR, target::Thread::vm_tag_offset());
// Mark that the thread has not exited generated Dart code.
__ LoadImmediate(R2, 0);
__ StoreToOffset(kWord, R2, THR, target::Thread::exit_through_ffi_offset());
// Reset exit frame information in Isolate's mutator thread structure.
__ StoreToOffset(kWord, R2, THR,
target::Thread::top_exit_frame_info_offset());
__ LeaveStubFrame();
__ Ret();
#else
__ Stop("Not using Dart dynamic modules");
#endif // defined(DART_DYNAMIC_MODULES)
}
// R9: Contains an ICData.
void StubCodeCompiler::GenerateICCallBreakpointStub() {
#if defined(PRODUCT)
@@ -3165,8 +3386,9 @@ void StubCodeCompiler::GenerateICCallThroughCodeStub() {
if (FLAG_precompiled_mode) {
const intptr_t entry_offset =
target::ICData::EntryPointIndexFor(1) * target::kWordSize;
__ LoadCompressed(R0, Address(R8, entry_offset));
__ Branch(FieldAddress(R0, target::Function::entry_point_offset()));
__ LoadCompressed(FUNCTION_REG, Address(R8, entry_offset));
__ Branch(
FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
} else {
const intptr_t code_offset =
target::ICData::CodeIndexFor(1) * target::kWordSize;
+248 -3
View File
@@ -1512,7 +1512,7 @@ void StubCodeCompiler::GenerateAllocateMintSharedWithoutFPURegsStub() {
// Called when invoking Dart code from C++ (VM code).
// Input parameters:
// LR : points to return address.
// R0 : target code or entry point (in bare instructions mode).
// R0 : target code or entry point (in AOT mode).
// R1 : arguments descriptor array.
// R2 : arguments array.
// R3 : current thread.
@@ -1653,6 +1653,155 @@ void StubCodeCompiler::GenerateInvokeDartCodeStub() {
__ ret();
}
// Called when invoking compiled Dart code from interpreted Dart code.
// Input parameters:
// LR : points to return address.
// R0 : target code or entry point (in AOT mode).
// R1 : arguments descriptor array.
// R2 : address of first argument.
// R3 : current thread.
void StubCodeCompiler::GenerateInvokeDartCodeFromBytecodeStub() {
#if defined(DART_DYNAMIC_MODULES)
__ Comment("InvokeDartCodeFromBytecodeStub");
// Copy the C stack pointer (CSP/R31) into the stack pointer we'll actually
// use to access the stack (SP/R15) and set the C stack pointer to near the
// stack limit, loaded from the Thread held in R3, to prevent signal handlers
// from over-writing Dart frames.
__ mov(SP, CSP);
__ SetupCSPFromThread(R3);
__ EnterFrame(0);
// Push code object to PC marker slot.
__ ldr(TMP,
Address(R3,
target::Thread::invoke_dart_code_from_bytecode_stub_offset()));
__ Push(TMP);
#if defined(DART_TARGET_OS_FUCHSIA)
__ str(R18, Address(R3, target::Thread::saved_shadow_call_stack_offset()));
#elif defined(USING_SHADOW_CALL_STACK)
#error Unimplemented
#endif
__ PushNativeCalleeSavedRegisters();
// Set up THR, which caches the current thread in Dart code.
if (THR != R3) {
__ mov(THR, R3);
}
// Refresh pinned registers (write barrier mask, null, dispatch table, etc).
__ RestorePinnedRegisters();
// Save the current VMTag on the stack.
__ LoadFromOffset(R4, THR, target::Thread::vm_tag_offset());
__ Push(R4);
// Save top resource and top exit frame info. Use R6 as a temporary register.
// StackFrameIterator reads the top exit frame info saved in this frame.
__ LoadFromOffset(R6, THR, target::Thread::top_resource_offset());
__ StoreToOffset(ZR, THR, target::Thread::top_resource_offset());
__ Push(R6);
__ LoadFromOffset(R6, THR, target::Thread::exit_through_ffi_offset());
__ Push(R6);
__ StoreToOffset(ZR, THR, target::Thread::exit_through_ffi_offset());
__ LoadFromOffset(R6, THR, target::Thread::top_exit_frame_info_offset());
__ StoreToOffset(ZR, THR, target::Thread::top_exit_frame_info_offset());
// target::frame_layout.exit_link_slot_from_entry_fp must be kept in sync
// with the code below.
#if defined(DART_TARGET_OS_FUCHSIA)
ASSERT(target::frame_layout.exit_link_slot_from_entry_fp == -24);
#else
ASSERT(target::frame_layout.exit_link_slot_from_entry_fp == -23);
#endif
__ Push(R6);
// In debug mode, verify that we've pushed the top exit frame info at the
// correct offset from FP.
__ EmitEntryFrameVerification();
// Mark that the thread is executing Dart code. Do this after initializing the
// exit link for the profiler.
__ LoadImmediate(R6, VMTag::kDartTagId);
__ StoreToOffset(R6, THR, target::Thread::vm_tag_offset());
// Load arguments descriptor array into R4, which is passed to Dart code.
__ mov(R4, R1);
// Load number of arguments into R5 and adjust count for type arguments.
__ LoadCompressedSmiFieldFromOffset(
R5, R4, target::ArgumentsDescriptor::count_offset());
__ LoadCompressedSmiFieldFromOffset(
R3, R4, target::ArgumentsDescriptor::type_args_len_offset());
__ SmiUntag(R5);
// Include the type arguments.
__ cmp(R3, Operand(0), kObjectBytes);
__ csinc(R5, R5, R5, EQ); // R5 <- (R3 == 0) ? R5 : R5 + 1
// R2 points to first argument.
// Set up arguments for the Dart call.
Label push_arguments;
Label done_push_arguments;
__ cmp(R5, Operand(0));
__ b(&done_push_arguments, EQ); // check if there are arguments.
__ LoadImmediate(R1, 0);
__ Bind(&push_arguments);
__ ldr(R3, Address(R2));
__ Push(R3);
__ add(R1, R1, Operand(1));
__ add(R2, R2, Operand(target::kWordSize));
__ cmp(R1, Operand(R5));
__ b(&push_arguments, LT);
__ Bind(&done_push_arguments);
if (FLAG_precompiled_mode) {
__ SetupGlobalPoolAndDispatchTable();
__ mov(CODE_REG, ZR); // GC-safe value into CODE_REG.
} else {
// We now load the pool pointer(PP) with a GC safe value as we are about to
// invoke dart code. We don't need a real object pool here.
// Smi zero does not work because ARM64 assumes PP to be untagged.
__ LoadObject(PP, NullObject());
__ mov(CODE_REG, R0);
__ ldr(R0, FieldAddress(CODE_REG, target::Code::entry_point_offset()));
}
// Call the Dart code entrypoint.
__ blr(R0); // R4 is the arguments descriptor array.
__ Comment("InvokeDartCodeFromBytecodeStub return");
// Get rid of arguments pushed on the stack.
__ AddImmediate(
SP, FP,
target::frame_layout.exit_link_slot_from_entry_fp * target::kWordSize);
// Restore the saved top exit frame info and top resource back into the
// Isolate structure. Uses R6 as a temporary register for this.
__ Pop(R6);
__ StoreToOffset(R6, THR, target::Thread::top_exit_frame_info_offset());
__ Pop(R6);
__ StoreToOffset(R6, THR, target::Thread::exit_through_ffi_offset());
__ Pop(R6);
__ StoreToOffset(R6, THR, target::Thread::top_resource_offset());
// Restore the current VMTag from the stack.
__ Pop(R4);
__ StoreToOffset(R4, THR, target::Thread::vm_tag_offset());
__ PopNativeCalleeSavedRegisters();
// Restore the frame pointer and C stack pointer and return.
__ LeaveFrame();
__ RestoreCSP();
__ ret();
#else
__ Stop("Not using Dart dynamic modules");
#endif // defined(DART_DYNAMIC_MODULES)
}
// Helper to generate space allocation of context stub.
// This does not initialise the fields of the context.
// Input:
@@ -3015,6 +3164,101 @@ void StubCodeCompiler::GenerateLazyCompileStub() {
__ br(R2);
}
// Stub for interpreting a function call.
// R4: Arguments descriptor.
// R0: Function.
void StubCodeCompiler::GenerateInterpretCallStub() {
#if defined(DART_DYNAMIC_MODULES)
__ SetPrologueOffset();
__ EnterStubFrame();
#if defined(DEBUG)
{
Label ok;
// Check that we are always entering from Dart code.
__ LoadFromOffset(R8, THR, target::Thread::vm_tag_offset());
__ CompareImmediate(R8, VMTag::kDartTagId);
__ b(&ok, EQ);
__ Stop("Not coming from Dart code.");
__ Bind(&ok);
}
#endif
// Adjust arguments count for type arguments vector.
__ LoadCompressedSmiFieldFromOffset(
R2, R4, target::ArgumentsDescriptor::count_offset());
__ SmiUntag(R2);
__ LoadCompressedSmiFieldFromOffset(
R1, R4, target::ArgumentsDescriptor::type_args_len_offset());
__ cmp(R1, Operand(0));
__ csinc(R2, R2, R2, EQ); // R2 <- (R1 == 0) ? R2 : R2 + 1.
// Compute argv.
__ add(R3, ZR, Operand(R2, LSL, 3));
__ add(R3, FP, Operand(R3));
__ AddImmediate(R3,
target::frame_layout.param_end_from_fp * target::kWordSize);
// Indicate decreasing memory addresses of arguments with negative argc.
__ neg(R2, R2);
// Align frame before entering C++ world. No shadow stack space required.
__ ReserveAlignedFrameSpace(0 * target::kWordSize);
// Pass arguments in registers.
// R0: Function.
__ mov(R1, R4); // Arguments descriptor.
// R2: Negative argc.
// R3: Argv.
__ mov(R4, THR); // Thread.
// Save exit frame information to enable stack walking as we are about
// to transition to Dart VM C++ code.
__ StoreToOffset(FP, THR, target::Thread::top_exit_frame_info_offset());
// Mark that the thread exited generated code through a runtime call.
__ LoadImmediate(R5, target::Thread::exit_through_runtime_call());
__ StoreToOffset(R5, THR, target::Thread::exit_through_ffi_offset());
// Mark that the thread is executing VM code.
__ LoadFromOffset(R5, THR,
target::Thread::interpret_call_entry_point_offset());
__ StoreToOffset(R5, THR, target::Thread::vm_tag_offset());
// We are entering runtime code, so the C stack pointer must be restored from
// the stack limit to the top of the stack. We cache the stack limit address
// in a callee-saved register.
__ mov(R25, CSP);
__ mov(CSP, SP);
__ blr(R5);
// Restore SP and CSP.
__ mov(SP, CSP);
__ mov(CSP, R25);
// Refresh pinned registers values (inc. write barrier mask and null object).
__ RestorePinnedRegisters();
// Mark that the thread is executing Dart code.
__ LoadImmediate(R2, VMTag::kDartTagId);
__ StoreToOffset(R2, THR, target::Thread::vm_tag_offset());
// Mark that the thread has not exited generated Dart code.
__ StoreToOffset(ZR, THR, target::Thread::exit_through_ffi_offset());
// Reset exit frame information in Isolate's mutator thread structure.
__ StoreToOffset(ZR, THR, target::Thread::top_exit_frame_info_offset());
__ LeaveStubFrame();
__ ret();
#else
__ Stop("Not using Dart dynamic modules");
#endif // defined(DART_DYNAMIC_MODULES)
}
// R5: Contains an ICData.
void StubCodeCompiler::GenerateICCallBreakpointStub() {
#if defined(PRODUCT)
@@ -3482,8 +3726,9 @@ void StubCodeCompiler::GenerateICCallThroughCodeStub() {
if (FLAG_precompiled_mode) {
const intptr_t entry_offset =
target::ICData::EntryPointIndexFor(1) * target::kCompressedWordSize;
__ LoadCompressed(R1, Address(R8, entry_offset));
__ ldr(R1, FieldAddress(R1, target::Function::entry_point_offset()));
__ LoadCompressed(FUNCTION_REG, Address(R8, entry_offset));
__ ldr(R1,
FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
} else {
const intptr_t code_offset =
target::ICData::CodeIndexFor(1) * target::kCompressedWordSize;
@@ -1161,6 +1161,137 @@ void StubCodeCompiler::GenerateInvokeDartCodeStub() {
__ ret();
}
// Called when invoking compiled Dart code from interpreted Dart code.
// Input parameters:
// ESP : points to return address.
// ESP + 4 : code object of the dart function to call.
// ESP + 8 : arguments descriptor array.
// ESP + 12: address of first argument.
// ESP + 16 : current thread.
void StubCodeCompiler::GenerateInvokeDartCodeFromBytecodeStub() {
#if defined(DART_DYNAMIC_MODULES)
const intptr_t kTargetCodeOffset = 2 * target::kWordSize;
const intptr_t kArgumentsDescOffset = 3 * target::kWordSize;
const intptr_t kArgumentsOffset = 4 * target::kWordSize;
const intptr_t kThreadOffset = 5 * target::kWordSize;
__ EnterFrame(0);
// Push code object to PC marker slot.
__ movl(EAX, Address(EBP, kThreadOffset));
__ pushl(Address(EAX, target::Thread::invoke_dart_code_stub_offset()));
// Save C++ ABI callee-saved registers.
__ pushl(EBX);
__ pushl(ESI);
__ pushl(EDI);
// Set up THR, which caches the current thread in Dart code.
__ movl(THR, EAX);
#if defined(USING_SHADOW_CALL_STACK)
#error Unimplemented
#endif
// Save the current VMTag on the stack.
__ movl(ECX, Assembler::VMTagAddress());
__ pushl(ECX);
// Save top resource and top exit frame info. Use EDX as a temporary register.
// StackFrameIterator reads the top exit frame info saved in this frame.
__ movl(EDX, Address(THR, target::Thread::top_resource_offset()));
__ pushl(EDX);
__ movl(Address(THR, target::Thread::top_resource_offset()), Immediate(0));
__ movl(EAX, Address(THR, target::Thread::exit_through_ffi_offset()));
__ pushl(EAX);
__ movl(Address(THR, target::Thread::exit_through_ffi_offset()),
Immediate(0));
// The constant target::frame_layout.exit_link_slot_from_entry_fp must be
// kept in sync with the code below.
ASSERT(target::frame_layout.exit_link_slot_from_entry_fp == -8);
__ movl(EDX, Address(THR, target::Thread::top_exit_frame_info_offset()));
__ pushl(EDX);
__ movl(Address(THR, target::Thread::top_exit_frame_info_offset()),
Immediate(0));
// In debug mode, verify that we've pushed the top exit frame info at the
// correct offset from FP.
__ EmitEntryFrameVerification();
// Mark that the thread is executing Dart code. Do this after initializing the
// exit link for the profiler.
__ movl(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
// Load arguments descriptor array into EDX.
__ movl(EDX, Address(EBP, kArgumentsDescOffset));
// Load number of arguments into EBX and adjust count for type arguments.
__ movl(EBX, FieldAddress(EDX, target::ArgumentsDescriptor::count_offset()));
__ cmpl(
FieldAddress(EDX, target::ArgumentsDescriptor::type_args_len_offset()),
Immediate(0));
Label args_count_ok;
__ j(EQUAL, &args_count_ok, Assembler::kNearJump);
__ addl(EBX, Immediate(target::ToRawSmi(1))); // Include the type arguments.
__ Bind(&args_count_ok);
// Save number of arguments as Smi on stack, replacing ArgumentsDesc.
__ movl(Address(EBP, kArgumentsDescOffset), EBX);
__ SmiUntag(EBX);
// Set up arguments for the dart call.
Label push_arguments;
Label done_push_arguments;
__ testl(EBX, EBX); // check if there are arguments.
__ j(ZERO, &done_push_arguments, Assembler::kNearJump);
__ movl(EAX, Immediate(0));
// Compute address of 'arguments array' data area into EDI.
__ movl(EDI, Address(EBP, kArgumentsOffset));
__ Bind(&push_arguments);
__ movl(ECX, Address(EDI, EAX, TIMES_4, 0));
__ pushl(ECX);
__ incl(EAX);
__ cmpl(EAX, EBX);
__ j(LESS, &push_arguments, Assembler::kNearJump);
__ Bind(&done_push_arguments);
// Call the dart code entrypoint.
__ movl(EAX, Address(EBP, kTargetCodeOffset));
__ call(FieldAddress(EAX, target::Code::entry_point_offset()));
// Read the saved number of passed arguments as Smi.
__ movl(EDX, Address(EBP, kArgumentsDescOffset));
// Get rid of arguments pushed on the stack.
__ leal(ESP, Address(ESP, EDX, TIMES_2, 0)); // EDX is a Smi.
// Restore the saved top exit frame info and top resource back into the
// Isolate structure.
__ popl(Address(THR, target::Thread::top_exit_frame_info_offset()));
__ popl(Address(THR, target::Thread::exit_through_ffi_offset()));
__ popl(Address(THR, target::Thread::top_resource_offset()));
// Restore the current VMTag from the stack.
__ popl(Assembler::VMTagAddress());
#if defined(USING_SHADOW_CALL_STACK)
#error Unimplemented
#endif
// Restore C++ ABI callee-saved registers.
__ popl(EDI);
__ popl(ESI);
__ popl(EBX);
// Restore the frame pointer.
__ LeaveFrame();
__ ret();
#else
__ Stop("Not using Dart dynamic modules");
#endif // defined(DART_DYNAMIC_MODULES)
}
// Helper to generate space allocation of context stub.
// This does not initialise the fields of the context.
// Input:
@@ -2365,6 +2496,86 @@ void StubCodeCompiler::GenerateLazyCompileStub() {
__ jmp(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
}
// Stub for interpreting a function call.
// EDX: Arguments descriptor.
// EAX: Function.
void StubCodeCompiler::GenerateInterpretCallStub() {
#if defined(DART_DYNAMIC_MODULES)
__ EnterStubFrame();
#if defined(DEBUG)
{
Label ok;
// Check that we are always entering from Dart code.
__ cmpl(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
__ j(EQUAL, &ok, Assembler::kNearJump);
__ Stop("Not coming from Dart code.");
__ Bind(&ok);
}
#endif
// Adjust arguments count for type arguments vector.
__ movl(ECX, FieldAddress(EDX, target::ArgumentsDescriptor::count_offset()));
__ SmiUntag(ECX);
__ cmpl(
FieldAddress(EDX, target::ArgumentsDescriptor::type_args_len_offset()),
Immediate(0));
Label args_count_ok;
__ j(EQUAL, &args_count_ok, Assembler::kNearJump);
__ incl(ECX);
__ Bind(&args_count_ok);
// Compute argv.
__ leal(EBX,
Address(EBP, ECX, TIMES_4,
target::frame_layout.param_end_from_fp * target::kWordSize));
// Indicate decreasing memory addresses of arguments with negative argc.
__ negl(ECX);
__ pushl(THR); // Arg 4: Thread.
__ pushl(EBX); // Arg 3: Argv.
__ pushl(ECX); // Arg 2: Negative argc.
__ pushl(EDX); // Arg 1: Arguments descriptor
__ pushl(EAX); // Arg 0: Function
// Save exit frame information to enable stack walking as we are about
// to transition to Dart VM C++ code.
__ movl(Address(THR, target::Thread::top_exit_frame_info_offset()), EBP);
// Mark that the thread exited generated code through a runtime call.
__ movl(Address(THR, target::Thread::exit_through_ffi_offset()),
Immediate(target::Thread::exit_through_runtime_call()));
// Mark that the thread is executing VM code.
__ movl(EAX,
Address(THR, target::Thread::interpret_call_entry_point_offset()));
__ movl(Assembler::VMTagAddress(), EAX);
__ call(EAX);
__ Drop(5);
// Mark that the thread is executing Dart code.
__ movl(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
// Mark that the thread has not exited generated Dart code.
__ movl(Address(THR, target::Thread::exit_through_ffi_offset()),
Immediate(0));
// Reset exit frame information in Isolate's mutator thread structure.
__ movl(Address(THR, target::Thread::top_exit_frame_info_offset()),
Immediate(0));
__ LeaveFrame();
__ ret();
#else
__ Stop("Not using Dart dynamic modules");
#endif // defined(DART_DYNAMIC_MODULES)
}
// ECX: Contains an ICData.
void StubCodeCompiler::GenerateICCallBreakpointStub() {
#if defined(PRODUCT)
@@ -1341,7 +1341,7 @@ void StubCodeCompiler::GenerateAllocateMintSharedWithoutFPURegsStub() {
// Called when invoking Dart code from C++ (VM code).
// Input parameters:
// RA : points to return address.
// A0 : target code or entry point (in bare instructions mode).
// A0 : target code or entry point (in AOT mode).
// A1 : arguments descriptor array.
// A2 : arguments array.
// A3 : current thread.
@@ -1471,6 +1471,17 @@ void StubCodeCompiler::GenerateInvokeDartCodeStub() {
__ ret();
}
// Called when invoking compiled Dart code from interpreted Dart code.
// Input parameters:
// RSP : points to return address.
// RDI : target code or entry point (in AOT mode).
// RSI : arguments descriptor array.
// RDX : address of first argument.
// RCX : current thread.
void StubCodeCompiler::GenerateInvokeDartCodeFromBytecodeStub() {
__ Stop("Not implemented on RISC-V.");
}
// Helper to generate space allocation of context stub.
// This does not initialise the fields of the context.
// Input:
@@ -2765,6 +2776,13 @@ void StubCodeCompiler::GenerateLazyCompileStub() {
__ jr(TMP);
}
// Stub for interpreting a function call.
// ARGS_DESC_REG: Arguments descriptor.
// FUNCTION_REG: Function.
void StubCodeCompiler::GenerateInterpretCallStub() {
__ Stop("Not implemented on RISC-V.");
}
// A0: Receiver
// S5: ICData
void StubCodeCompiler::GenerateICCallBreakpointStub() {
@@ -3243,8 +3261,9 @@ void StubCodeCompiler::GenerateICCallThroughCodeStub() {
if (FLAG_precompiled_mode) {
const intptr_t entry_offset =
target::ICData::EntryPointIndexFor(1) * target::kCompressedWordSize;
__ LoadCompressed(A1, Address(T1, entry_offset));
__ lx(A1, FieldAddress(A1, target::Function::entry_point_offset()));
__ LoadCompressed(FUNCTION_REG, Address(T1, entry_offset));
__ lx(A1,
FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
} else {
const intptr_t code_offset =
target::ICData::CodeIndexFor(1) * target::kCompressedWordSize;
+249 -3
View File
@@ -1431,7 +1431,7 @@ static const RegisterSet kCalleeSavedRegisterSet(
// Called when invoking Dart code from C++ (VM code).
// Input parameters:
// RSP : points to return address.
// RDI : target code or entry point (in bare instructions mode).
// RDI : target code or entry point (in AOT mode).
// RSI : arguments descriptor array.
// RDX : arguments array.
// RCX : current thread.
@@ -1581,6 +1581,161 @@ void StubCodeCompiler::GenerateInvokeDartCodeStub() {
__ ret();
}
// Called when invoking compiled Dart code from interpreted Dart code.
// Input parameters:
// RSP : points to return address.
// RDI : target code or entry point (in AOT mode).
// RSI : arguments descriptor array.
// RDX : address of first argument.
// RCX : current thread.
void StubCodeCompiler::GenerateInvokeDartCodeFromBytecodeStub() {
#if defined(DART_DYNAMIC_MODULES)
__ EnterFrame(0);
const Register kTargetReg = CallingConventions::kArg1Reg;
const Register kArgDescReg = CallingConventions::kArg2Reg;
const Register kArg0Reg = CallingConventions::kArg3Reg;
const Register kThreadReg = CallingConventions::kArg4Reg;
// Push code object to PC marker slot.
__ pushq(
Address(kThreadReg,
target::Thread::invoke_dart_code_from_bytecode_stub_offset()));
// At this point, the stack looks like:
// | stub code object
// | saved RBP | <-- RBP
// | saved PC (return to interpreter's InvokeCompiled) |
const intptr_t kInitialOffset = 2;
// Save arguments descriptor array, later replaced by Smi argument count.
const intptr_t kArgumentsDescOffset = -(kInitialOffset)*target::kWordSize;
__ pushq(kArgDescReg);
// Save C++ ABI callee-saved registers.
__ PushRegisters(kCalleeSavedRegisterSet);
// If any additional (or fewer) values are pushed, the offsets in
// target::frame_layout.exit_link_slot_from_entry_fp will need to be changed.
// Set up THR, which caches the current thread in Dart code.
if (THR != kThreadReg) {
__ movq(THR, kThreadReg);
}
#if defined(USING_SHADOW_CALL_STACK)
#error Unimplemented
#endif
// Save the current VMTag on the stack.
__ movq(RAX, Assembler::VMTagAddress());
__ pushq(RAX);
// Save top resource and top exit frame info. Use RAX as a temporary register.
// StackFrameIterator reads the top exit frame info saved in this frame.
__ movq(RAX, Address(THR, target::Thread::top_resource_offset()));
__ pushq(RAX);
__ movq(Address(THR, target::Thread::top_resource_offset()), Immediate(0));
__ movq(RAX, Address(THR, target::Thread::exit_through_ffi_offset()));
__ pushq(RAX);
__ movq(Address(THR, target::Thread::exit_through_ffi_offset()),
Immediate(0));
__ movq(RAX, Address(THR, target::Thread::top_exit_frame_info_offset()));
__ pushq(RAX);
// The constant target::frame_layout.exit_link_slot_from_entry_fp must be kept
// in sync with the code above.
__ EmitEntryFrameVerification();
__ movq(Address(THR, target::Thread::top_exit_frame_info_offset()),
Immediate(0));
// Mark that the thread is executing Dart code. Do this after initializing the
// exit link for the profiler.
__ movq(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
// Load arguments descriptor array into R10, which is passed to Dart code.
__ movq(R10, kArgDescReg);
// Push arguments. At this point we only need to preserve kTargetReg.
ASSERT(kTargetReg != RDX);
// Load number of arguments into RBX and adjust count for type arguments.
__ OBJ(mov)(RBX,
FieldAddress(R10, target::ArgumentsDescriptor::count_offset()));
__ OBJ(cmp)(
FieldAddress(R10, target::ArgumentsDescriptor::type_args_len_offset()),
Immediate(0));
Label args_count_ok;
__ j(EQUAL, &args_count_ok, Assembler::kNearJump);
__ addq(RBX, Immediate(target::ToRawSmi(1))); // Include the type arguments.
__ Bind(&args_count_ok);
// Save number of arguments as Smi on stack, replacing saved ArgumentsDesc.
__ movq(Address(RBP, kArgumentsDescOffset), RBX);
__ SmiUntag(RBX);
// Compute address of first argument into RDX.
__ MoveRegister(RDX, kArg0Reg);
// Set up arguments for the Dart call.
Label push_arguments;
Label done_push_arguments;
__ j(ZERO, &done_push_arguments, Assembler::kNearJump);
__ LoadImmediate(RAX, Immediate(0));
__ Bind(&push_arguments);
__ pushq(Address(RDX, RAX, TIMES_8, 0));
__ incq(RAX);
__ cmpq(RAX, RBX);
__ j(LESS, &push_arguments, Assembler::kNearJump);
__ Bind(&done_push_arguments);
// Call the Dart code entrypoint.
if (FLAG_precompiled_mode) {
__ movq(PP, Address(THR, target::Thread::global_object_pool_offset()));
__ xorq(CODE_REG, CODE_REG); // GC-safe value into CODE_REG.
} else {
__ xorq(PP, PP); // GC-safe value into PP.
__ movq(CODE_REG, kTargetReg);
__ movq(kTargetReg,
FieldAddress(CODE_REG, target::Code::entry_point_offset()));
}
__ call(kTargetReg); // R10 is the arguments descriptor array.
// Read the saved number of passed arguments as Smi.
__ movq(RDX, Address(RBP, kArgumentsDescOffset));
// Get rid of arguments pushed on the stack.
__ leaq(RSP, Address(RSP, RDX, TIMES_4, 0)); // RDX is a Smi.
// Restore the saved top exit frame info and top resource back into the
// Isolate structure.
__ popq(Address(THR, target::Thread::top_exit_frame_info_offset()));
__ popq(Address(THR, target::Thread::exit_through_ffi_offset()));
__ popq(Address(THR, target::Thread::top_resource_offset()));
// Restore the current VMTag from the stack.
__ popq(Assembler::VMTagAddress());
#if defined(USING_SHADOW_CALL_STACK)
#error Unimplemented
#endif
// Restore C++ ABI callee-saved registers.
__ PopRegisters(kCalleeSavedRegisterSet);
__ set_constant_pool_allowed(false);
// Restore the frame pointer.
__ LeaveFrame();
__ ret();
#else
__ Stop("Not using Dart dynamic modules");
#endif // defined(DART_DYNAMIC_MODULES)
}
// Helper to generate space allocation of context stub.
// This does not initialize the fields of the context.
// Input:
@@ -2898,6 +3053,97 @@ void StubCodeCompiler::GenerateLazyCompileStub() {
__ jmp(RCX);
}
// Stub for interpreting a function call.
// ARGS_DESC_REG: Arguments descriptor.
// FUNCTION_REG: Function.
void StubCodeCompiler::GenerateInterpretCallStub() {
#if defined(DART_DYNAMIC_MODULES)
__ EnterStubFrame();
#if defined(DEBUG)
{
Label ok;
// Check that we are always entering from Dart code.
__ movq(R8, Immediate(VMTag::kDartTagId));
__ cmpq(R8, Assembler::VMTagAddress());
__ j(EQUAL, &ok, Assembler::kNearJump);
__ Stop("Not coming from Dart code.");
__ Bind(&ok);
}
#endif
// Adjust arguments count for type arguments vector.
__ OBJ(mov)(R11, FieldAddress(ARGS_DESC_REG,
target::ArgumentsDescriptor::count_offset()));
__ SmiUntag(R11);
__ OBJ(cmp)(FieldAddress(ARGS_DESC_REG,
target::ArgumentsDescriptor::type_args_len_offset()),
Immediate(0));
Label args_count_ok;
__ j(EQUAL, &args_count_ok, Assembler::kNearJump);
__ incq(R11);
__ Bind(&args_count_ok);
// Compute argv.
__ leaq(R12,
Address(RBP, R11, TIMES_8,
target::frame_layout.param_end_from_fp * target::kWordSize));
// Indicate decreasing memory addresses of arguments with negative argc.
__ negq(R11);
// Reserve shadow space for args and align frame before entering C++ world.
__ subq(RSP, Immediate(5 * target::kWordSize));
if (OS::ActivationFrameAlignment() > 1) {
__ andq(RSP, Immediate(~(OS::ActivationFrameAlignment() - 1)));
}
__ movq(CallingConventions::kArg1Reg, FUNCTION_REG); // Function.
__ movq(CallingConventions::kArg2Reg,
ARGS_DESC_REG); // Arguments descriptor.
__ movq(CallingConventions::kArg3Reg, R11); // Negative argc.
__ movq(CallingConventions::kArg4Reg, R12); // Argv.
#if defined(TARGET_OS_WINDOWS)
__ movq(Address(RSP, 0 * target::kWordSize), THR); // Thread.
#else
__ movq(CallingConventions::kArg5Reg, THR); // Thread.
#endif
// Save exit frame information to enable stack walking as we are about
// to transition to Dart VM C++ code.
__ movq(Address(THR, target::Thread::top_exit_frame_info_offset()), RBP);
// Mark that the thread exited generated code through a runtime call.
__ movq(Address(THR, target::Thread::exit_through_ffi_offset()),
Immediate(target::Thread::exit_through_runtime_call()));
// Mark that the thread is executing VM code.
__ movq(RAX,
Address(THR, target::Thread::interpret_call_entry_point_offset()));
__ movq(Assembler::VMTagAddress(), RAX);
__ call(RAX);
// Mark that the thread is executing Dart code.
__ movq(Assembler::VMTagAddress(), Immediate(VMTag::kDartTagId));
// Mark that the thread has not exited generated Dart code.
__ movq(Address(THR, target::Thread::exit_through_ffi_offset()),
Immediate(0));
// Reset exit frame information in Isolate's mutator thread structure.
__ movq(Address(THR, target::Thread::top_exit_frame_info_offset()),
Immediate(0));
__ LeaveStubFrame();
__ ret();
#else
__ Stop("Not using Dart dynamic modules");
#endif // defined(DART_DYNAMIC_MODULES)
}
// RBX: Contains an ICData.
// TOS(0): return address (Dart code).
void StubCodeCompiler::GenerateICCallBreakpointStub() {
@@ -3399,8 +3645,8 @@ void StubCodeCompiler::GenerateICCallThroughCodeStub() {
if (FLAG_precompiled_mode) {
const intptr_t entry_offset =
target::ICData::EntryPointIndexFor(1) * target::kCompressedWordSize;
__ LoadCompressed(RCX, Address(R13, entry_offset));
__ jmp(FieldAddress(RCX, target::Function::entry_point_offset()));
__ LoadCompressed(FUNCTION_REG, Address(R13, entry_offset));
__ jmp(FieldAddress(FUNCTION_REG, target::Function::entry_point_offset()));
} else {
const intptr_t code_offset =
target::ICData::CodeIndexFor(1) * target::kCompressedWordSize;
+64
View File
@@ -0,0 +1,64 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/constants_kbc.h"
namespace dart {
static const intptr_t kInstructionSize0 = 1;
static const intptr_t kInstructionSizeA = 2;
static const intptr_t kInstructionSizeD = 2;
static const intptr_t kInstructionSizeWideD = 5;
static const intptr_t kInstructionSizeX = 2;
static const intptr_t kInstructionSizeWideX = 5;
static const intptr_t kInstructionSizeT = 2;
static const intptr_t kInstructionSizeWideT = 4;
static const intptr_t kInstructionSizeA_E = 3;
static const intptr_t kInstructionSizeWideA_E = 6;
static const intptr_t kInstructionSizeA_Y = 3;
static const intptr_t kInstructionSizeWideA_Y = 6;
static const intptr_t kInstructionSizeD_F = 3;
static const intptr_t kInstructionSizeWideD_F = 6;
static const intptr_t kInstructionSizeA_B_C = 4;
const intptr_t KernelBytecode::kInstructionSize[] = {
#define SIZE_ORDN(encoding) kInstructionSize##encoding
#define SIZE_WIDE(encoding) kInstructionSizeWide##encoding
#define SIZE_RESV(encoding) SIZE_ORDN(encoding)
#define SIZE(name, encoding, kind, op1, op2, op3) SIZE_##kind(encoding),
KERNEL_BYTECODES_LIST(SIZE)
#undef SIZE_ORDN
#undef SIZE_WIDE
#undef SIZE_RESV
#undef SIZE
};
#define DECLARE_INSTRUCTIONS(name, fmt, kind, fmta, fmtb, fmtc) \
static const KBCInstr k##name##Instructions[] = { \
KernelBytecode::k##name, \
KernelBytecode::kReturnTOS, \
};
INTERNAL_KERNEL_BYTECODES_LIST(DECLARE_INSTRUCTIONS)
#undef DECLARE_INSTRUCTIONS
void KernelBytecode::GetVMInternalBytecodeInstructions(
Opcode opcode,
const KBCInstr** instructions,
intptr_t* instructions_size) {
switch (opcode) {
#define CASE(name, fmt, kind, fmta, fmtb, fmtc) \
case k##name: \
*instructions = k##name##Instructions; \
*instructions_size = sizeof(k##name##Instructions); \
return;
INTERNAL_KERNEL_BYTECODES_LIST(CASE)
#undef CASE
default:
UNREACHABLE();
}
}
} // namespace dart
+977
View File
@@ -0,0 +1,977 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_CONSTANTS_KBC_H_
#define RUNTIME_VM_CONSTANTS_KBC_H_
#include "platform/assert.h"
#include "platform/globals.h"
#include "platform/utils.h"
namespace dart {
// clang-format off
// List of KernelBytecode instructions.
//
// INTERPRETER STATE
//
// current frame info (see stack_frame_kbc.h for layout)
// v-----^-----v
// ~----+----~ ~----+-------+-------+-~ ~-+-------+-------+-~
// ~ | ~ ~ | FP[0] | FP[1] | ~ ~ | SP[-1]| SP[0] |
// ~----+----~ ~----+-------+-------+-~ ~-+-------+-------+-~
// ^ ^
// FP SP
//
//
// The state of execution is captured in few interpreter registers:
//
// FP - base of the current frame
// SP - top of the stack (TOS) for the current frame
// PP - object pool for the currently execution function
//
// Frame info stored below FP additionally contains pointers to the currently
// executing function and code.
//
// In the unoptimized code most of bytecodes take operands implicitly from
// stack and store results again on the stack. Constant operands are usually
// taken from the object pool by index.
//
// ENCODING
//
// Each instruction starts with opcode byte. Certain instructions have
// wide encoding variant. In such case, the least significant bit of opcode is
// not set for compact variant and set for wide variant.
//
// The following operand encodings are used:
//
// 0........8.......16.......24.......32.......40.......48
// +--------+
// | opcode | 0: no operands
// +--------+
//
// +--------+--------+
// | opcode | A | A: unsigned 8-bit operand
// +--------+--------+
//
// +--------+--------+
// | opcode | D | D: unsigned 8/32-bit operand
// +--------+--------+
//
// +--------+----------------------------------+
// | opcode | D | D (wide)
// +--------+----------------------------------+
//
// +--------+--------+
// | opcode | X | X: signed 8/32-bit operand
// +--------+--------+
//
// +--------+----------------------------------+
// | opcode | X | X (wide)
// +--------+----------------------------------+
//
// +--------+--------+
// | opcode | T | T: signed 8/24-bit operand
// +--------+--------+
//
// +--------+--------------------------+
// | opcode | T | T (wide)
// +--------+--------------------------+
//
// +--------+--------+--------+
// | opcode | A | E | A_E: unsigned 8-bit operand and
// +--------+--------+--------+ unsigned 8/32-bit operand
//
// +--------+--------+----------------------------------+
// | opcode | A | E | A_E (wide)
// +--------+--------+----------------------------------+
//
// +--------+--------+--------+
// | opcode | A | Y | A_Y: unsigned 8-bit operand and
// +--------+--------+--------+ signed 8/32-bit operand
//
// +--------+--------+----------------------------------+
// | opcode | A | Y | A_Y (wide)
// +--------+--------+----------------------------------+
//
// +--------+--------+--------+
// | opcode | D | F | D_F: unsigned 8/32-bit operand and
// +--------+--------+--------+ unsigned 8-bit operand
//
// +--------+----------------------------------+--------+
// | opcode | D | F | D_F (wide)
// +--------+----------------------------------+--------+
//
// +--------+--------+--------+--------+
// | opcode | A | B | C | A_B_C: 3 unsigned 8-bit operands
// +--------+--------+--------+--------+
//
//
// INSTRUCTIONS
//
// - Trap
//
// Unreachable instruction.
//
// - Entry rD
//
// Function prologue for the function
// rD - number of local slots to reserve;
//
// - EntryOptional A, B, C
//
// Function prologue for the function with optional or named arguments:
// A - expected number of positional arguments;
// B - number of optional arguments;
// C - number of named arguments;
//
// Only one of B and C can be not 0.
//
// If B is not 0 then EntryOptional bytecode is followed by B LoadConstant
// bytecodes specifying default values for optional arguments.
//
// If C is not 0 then EntryOptional is followed by 2 * C LoadConstant
// bytecodes.
// Bytecode at 2 * i specifies name of the i-th named argument and at
// 2 * i + 1 default value. rA part of the LoadConstant bytecode specifies
// the location of the parameter on the stack. Here named arguments are
// sorted alphabetically to enable linear matching similar to how function
// prologues are implemented on other architectures.
//
// Note: Unlike Entry bytecode EntryOptional does not setup the frame for
// local variables this is done by a separate bytecode Frame, which should
// follow EntryOptional and its LoadConstant instructions.
//
// - EntrySuspendable A, B, C
//
// Similar to EntryOptional, but also reserves a local variable slot
// for suspend state variable.
//
// - LoadConstant rA, D
//
// Used in conjunction with EntryOptional instruction to describe names and
// default values of optional parameters.
//
// - Frame D
//
// Reserve and initialize with null space for D local variables.
//
// - CheckFunctionTypeArgs A, D
//
// Check for a passed-in type argument vector of length A and
// store it at FP[D].
//
// - CheckStack A
//
// Compare SP against isolate stack limit and call StackOverflow handler if
// necessary. Should be used in prologue (A = 0), or at the beginning of
// a loop with depth A.
//
// - Allocate D
//
// Allocate object of class PP[D] with no type arguments.
//
// - AllocateT
//
// Allocate object of class SP[0] with type arguments SP[-1].
//
// - CreateArrayTOS
//
// Allocate array of length SP[0] with type arguments SP[-1].
//
// - AllocateContext A, D
//
// Allocate Context object holding D context variables.
// A is a static ID of the context. Static ID of a context may be used to
// disambiguate accesses to different context objects.
// Context objects with the same ID should have the same number of
// context variables.
//
// - CloneContext A, D
//
// Clone Context object SP[0] holding D context variables.
// A is a static ID of the context. Cloned context has the same ID.
//
// - LoadContextParent
//
// Load parent from context SP[0].
//
// - StoreContextParent
//
// Store context SP[0] into `parent` field of context SP[-1].
//
// - LoadContextVar A, D
//
// Load value from context SP[0] at index D.
// A is a static ID of the context.
//
// - StoreContextVar A, D
//
// Store value SP[0] into context SP[-1] at index D.
// A is a static ID of the context.
//
// - PushConstant D
//
// Push value at index D from constant pool onto the stack.
//
// - PushNull
//
// Push `null` onto the stack.
//
// - PushTrue
//
// Push `true` onto the stack.
//
// - PushFalse
//
// Push `false` onto the stack.
//
// - PushInt rX
//
// Push int rX onto the stack.
//
// - Drop1
//
// Drop 1 value from the stack
//
// - Push rX
//
// Push FP[rX] to the stack.
//
// - StoreLocal rX; PopLocal rX
//
// Store top of the stack into FP[rX] and pop it if needed.
//
// - LoadFieldTOS D
//
// Push value at offset (in words) PP[D] from object SP[0].
//
// - StoreFieldTOS D
//
// Store value SP[0] into object SP[-1] at offset (in words) PP[D].
//
// - StoreIndexedTOS
//
// Store SP[0] into array SP[-2] at index SP[-1]. No typechecking is done.
// SP[-2] is assumed to be a RawArray, SP[-1] to be a smi.
//
// - PushStatic D
//
// Pushes value of the static field PP[D] on to the stack.
//
// - StoreStaticTOS D
//
// Stores TOS into the static field PP[D].
//
// - Jump target
//
// Jump to the given target. Target is specified as offset from the PC of the
// jump instruction.
//
// - JumpIfNoAsserts target
//
// Jump to the given target if assertions are not enabled.
// Target is specified as offset from the PC of the jump instruction.
//
// - JumpIfNotZeroTypeArgs target
//
// Jump to the given target if number of passed function type
// arguments is not zero.
// Target is specified as offset from the PC of the jump instruction.
//
// - JumpIfEqStrict target; JumpIfNeStrict target
//
// Jump to the given target if SP[-1] is the same (JumpIfEqStrict) /
// not the same (JumpIfNeStrict) object as SP[0].
//
// - JumpIfTrue target; JumpIfFalse target
// - JumpIfNull target; JumpIfNotNull target
//
// Jump to the given target if SP[0] is true/false/null/not null.
//
// - IndirectStaticCall ArgC, D
//
// Invoke the function given by the ICData in SP[0] with arguments
// SP[-(1+ArgC)], ..., SP[-1] and argument descriptor PP[D], which
// indicates whether the first argument is a type argument vector.
//
// - DirectCall ArgC, D
//
// Invoke the function PP[D] with arguments
// SP[-(ArgC-1)], ..., SP[0] and argument descriptor PP[D+1].
//
// - InterfaceCall ArgC, D
//
// Lookup and invoke method using ICData in PP[D]
// with arguments SP[-(1+ArgC)], ..., SP[-1].
// Method has to be declared (explicitly or implicitly) in an interface
// implemented by a receiver, and passed arguments are valid for the
// interface method declaration.
// The ICData indicates whether the first argument is a type argument vector.
//
// - UncheckedInterfaceCall ArgC, D
//
// Same as InterfaceCall, but can omit type checks of generic-covariant
// parameters.
//
// - DynamicCall ArgC, D
//
// Lookup and invoke method using ICData in PP[D]
// with arguments SP[-(1+ArgC)], ..., SP[-1].
// The ICData indicates whether the first argument is a type argument vector.
//
// - ReturnTOS
//
// Return to the caller using a value from the top-of-stack as a result.
//
// Note: return instruction knows how many arguments to remove from the
// stack because it can look at the call instruction at caller's PC and
// take argument count from it.
//
// - ReturnAsync
//
// Return to the caller from async function using a value from
// the top-of-stack as a result.
//
// - ReturnAsyncStar
//
// Return to the caller from async* function using a value from
// the top-of-stack as a result.
//
// - ReturnSyncStar
//
// Return to the caller from sync* function using a value from
// the top-of-stack as a result.
//
// - AssertAssignable A, D
//
// Assert that instance SP[-4] is assignable to variable named SP[0] of
// type SP[-1] with instantiator type arguments SP[-3] and function type
// arguments SP[-2] using SubtypeTestCache PP[D].
// If A is 1, then the instance may be a Smi.
//
// Instance remains on stack. Other arguments are consumed.
//
// - AssertBoolean A
//
// Assert that TOS is a boolean (A = 1) or that TOS is not null (A = 0).
//
// - AssertSubtype
//
// Assert that one type is a subtype of another. Throws a TypeError
// otherwise. The stack has the following arguments on it:
//
// SP[-4] instantiator type args
// SP[-3] function type args
// SP[-2] sub_type
// SP[-1] super_type
// SP[-0] dst_name
//
// All 5 arguments are consumed from the stack and no results is pushed.
//
// - LoadTypeArgumentsField D
//
// Load instantiator type arguments from an instance SP[0].
// PP[D] = offset (in words) of type arguments field corresponding
// to an instance's class.
//
// - InstantiateType D
//
// Instantiate type PP[D] with instantiator type arguments SP[-1] and
// function type arguments SP[0].
//
// - InstantiateTypeArgumentsTOS A, D
//
// Instantiate type arguments PP[D] with instantiator type arguments SP[-1]
// and function type arguments SP[0]. A != 0 indicates that resulting type
// arguments are all dynamic if both instantiator and function type
// arguments are all dynamic.
//
// - Throw A
//
// Throw (Rethrow if A != 0) exception. Exception object and stack object
// are taken from TOS.
//
// - MoveSpecial A, rX
//
// Copy value from special variable to FP[rX]. Currently only
// used to pass exception object (A = 0) and stack trace object (A = 1) to
// catch handler.
//
// - SetFrame A
//
// Reinitialize SP assuming that current frame has size A.
// Used to drop temporaries from the stack in the exception handler.
//
// - BooleanNegateTOS
//
// SP[0] = !SP[0]
//
// - EqualsNull
//
// SP[0] = (SP[0] == null) ? true : false
//
// - NegateInt
//
// Equivalent to invocation of unary int operator-.
// Receiver should have static type int.
// Check SP[0] for null; SP[0] = -SP[0].
//
// - AddInt; SubInt; MulInt; TruncDivInt; ModInt; BitAndInt; BitOrInt;
// BitXorInt; ShlInt; ShrInt
//
// Equivalent to invocation of binary int operator +, -, *, ~/, %, &, |,
// ^, << or >>. Receiver and argument should have static type int.
// Check SP[-1] and SP[0] for null; push SP[-1] <op> SP[0].
//
// - CompareIntEq; CompareIntGt; CompareIntLt; CompareIntGe; CompareIntLe
//
// Equivalent to invocation of binary int operator ==, >, <, >= or <=.
// Receiver and argument should have static type int.
// Check SP[-1] and SP[0] for null; push SP[-1] <op> SP[0] ? true : false.
//
// - NegateDouble
//
// Equivalent to invocation of unary double operator-.
// Receiver should have static type double.
// Check SP[0] for null; SP[0] = -SP[0].
//
// - AddDouble; SubDouble; MulDouble; DivDouble
//
// Equivalent to invocation of binary int operator +, -, *, /.
// Receiver and argument should have static type double.
// Check SP[-1] and SP[0] for null; push SP[-1] <op> SP[0].
//
// - CompareDoubleEq; CompareDoubleGt; CompareDoubleLt; CompareDoubleGe;
// CompareDoubleLe
//
// Equivalent to invocation of binary double operator ==, >, <, >= or <=.
// Receiver and argument should have static type double.
// Check SP[-1] and SP[0] for null; push SP[-1] <op> SP[0] ? true : false.
//
// - AllocateClosure D
//
// Allocate closure object for closure function ConstantPool[D].
//
// BYTECODE LIST FORMAT
//
// KernelBytecode list below is specified using the following format:
//
// V(BytecodeName, OperandForm, BytecodeKind, Op1, Op2, Op3)
//
// - OperandForm specifies operand encoding and should be one of 0, A, D, X, T,
// A_E, A_Y, D_F or A_B_C (see ENCODING section above).
//
// - BytecodeKind is one of WIDE, RESV (reserved), ORDN (ordinary)
//
// - Op1, Op2, Op3 specify operand meaning. Possible values:
//
// ___ ignored / non-existent operand
// num immediate operand
// lit constant literal from object pool
// reg register (unsigned FP relative local)
// xeg x-register (signed FP relative local)
// tgt jump target relative to the PC of the current instruction
//
// TODO(vegorov) jump targets should be encoded relative to PC of the next
// instruction because PC is incremented immediately after fetch
// and before decoding.
//
#define PUBLIC_KERNEL_BYTECODES_LIST(V) \
V(Trap, 0, ORDN, ___, ___, ___) \
V(Unused00, 0, RESV, ___, ___, ___) \
V(Entry, D, ORDN, num, ___, ___) \
V(Entry_Wide, D, WIDE, num, ___, ___) \
V(EntryOptional, A_B_C, ORDN, num, num, num) \
V(EntrySuspendable, A_B_C, ORDN, num, num, num) \
V(LoadConstant, A_E, ORDN, reg, lit, ___) \
V(LoadConstant_Wide, A_E, WIDE, reg, lit, ___) \
V(Frame, D, ORDN, num, ___, ___) \
V(Frame_Wide, D, WIDE, num, ___, ___) \
V(CheckFunctionTypeArgs, A_E, ORDN, num, reg, ___) \
V(CheckFunctionTypeArgs_Wide, A_E, WIDE, num, reg, ___) \
V(CheckStack, A, ORDN, num, ___, ___) \
V(DebugCheck, 0, ORDN, ___, ___, ___) \
V(JumpIfUnchecked, T, ORDN, tgt, ___, ___) \
V(JumpIfUnchecked_Wide, T, WIDE, tgt, ___, ___) \
V(Allocate, D, ORDN, lit, ___, ___) \
V(Allocate_Wide, D, WIDE, lit, ___, ___) \
V(AllocateT, 0, ORDN, ___, ___, ___) \
V(CreateArrayTOS, 0, ORDN, ___, ___, ___) \
V(AllocateClosure, D, ORDN, lit, ___, ___) \
V(AllocateClosure_Wide, D, WIDE, lit, ___, ___) \
V(AllocateContext, A_E, ORDN, num, num, ___) \
V(AllocateContext_Wide, A_E, WIDE, num, num, ___) \
V(CloneContext, A_E, ORDN, num, num, ___) \
V(CloneContext_Wide, A_E, WIDE, num, num, ___) \
V(LoadContextParent, 0, ORDN, ___, ___, ___) \
V(StoreContextParent, 0, ORDN, ___, ___, ___) \
V(LoadContextVar, A_E, ORDN, num, num, ___) \
V(LoadContextVar_Wide, A_E, WIDE, num, num, ___) \
V(Unused04, 0, RESV, ___, ___, ___) \
V(Unused05, 0, RESV, ___, ___, ___) \
V(StoreContextVar, A_E, ORDN, num, num, ___) \
V(StoreContextVar_Wide, A_E, WIDE, num, num, ___) \
V(PushConstant, D, ORDN, lit, ___, ___) \
V(PushConstant_Wide, D, WIDE, lit, ___, ___) \
V(Unused06, 0, RESV, ___, ___, ___) \
V(Unused07, 0, RESV, ___, ___, ___) \
V(PushTrue, 0, ORDN, ___, ___, ___) \
V(PushFalse, 0, ORDN, ___, ___, ___) \
V(PushInt, X, ORDN, num, ___, ___) \
V(PushInt_Wide, X, WIDE, num, ___, ___) \
V(Unused08, 0, RESV, ___, ___, ___) \
V(Unused09, 0, RESV, ___, ___, ___) \
V(Unused10, 0, RESV, ___, ___, ___) \
V(Unused11, 0, RESV, ___, ___, ___) \
V(PushNull, 0, ORDN, ___, ___, ___) \
V(Drop1, 0, ORDN, ___, ___, ___) \
V(Push, X, ORDN, xeg, ___, ___) \
V(Push_Wide, X, WIDE, xeg, ___, ___) \
V(Unused12, 0, RESV, ___, ___, ___) \
V(Unused13, 0, RESV, ___, ___, ___) \
V(Unused14, 0, RESV, ___, ___, ___) \
V(Unused15, 0, RESV, ___, ___, ___) \
V(Unused16, 0, RESV, ___, ___, ___) \
V(Unused17, 0, RESV, ___, ___, ___) \
V(PopLocal, X, ORDN, xeg, ___, ___) \
V(PopLocal_Wide, X, WIDE, xeg, ___, ___) \
V(LoadStatic, D, ORDN, lit, ___, ___) \
V(LoadStatic_Wide, D, WIDE, lit, ___, ___) \
V(StoreLocal, X, ORDN, xeg, ___, ___) \
V(StoreLocal_Wide, X, WIDE, xeg, ___, ___) \
V(LoadFieldTOS, D, ORDN, lit, ___, ___) \
V(LoadFieldTOS_Wide, D, WIDE, lit, ___, ___) \
V(StoreFieldTOS, D, ORDN, lit, ___, ___) \
V(StoreFieldTOS_Wide, D, WIDE, lit, ___, ___) \
V(StoreIndexedTOS, 0, ORDN, ___, ___, ___) \
V(Unused20, 0, RESV, ___, ___, ___) \
V(JumpIfInitialized, T, ORDN, tgt, ___, ___) \
V(JumpIfInitialized_Wide, T, WIDE, tgt, ___, ___) \
V(PushUninitializedSentinel, 0, ORDN, ___, ___, ___) \
V(Unused21, 0, RESV, ___, ___, ___) \
V(InitLateField, D, ORDN, lit, ___, ___) \
V(InitLateField_Wide, D, WIDE, lit, ___, ___) \
V(StoreStaticTOS, D, ORDN, lit, ___, ___) \
V(StoreStaticTOS_Wide, D, WIDE, lit, ___, ___) \
V(Jump, T, ORDN, tgt, ___, ___) \
V(Jump_Wide, T, WIDE, tgt, ___, ___) \
V(JumpIfNoAsserts, T, ORDN, tgt, ___, ___) \
V(JumpIfNoAsserts_Wide, T, WIDE, tgt, ___, ___) \
V(JumpIfNotZeroTypeArgs, T, ORDN, tgt, ___, ___) \
V(JumpIfNotZeroTypeArgs_Wide, T, WIDE, tgt, ___, ___) \
V(JumpIfEqStrict, T, ORDN, tgt, ___, ___) \
V(JumpIfEqStrict_Wide, T, WIDE, tgt, ___, ___) \
V(JumpIfNeStrict, T, ORDN, tgt, ___, ___) \
V(JumpIfNeStrict_Wide, T, WIDE, tgt, ___, ___) \
V(JumpIfTrue, T, ORDN, tgt, ___, ___) \
V(JumpIfTrue_Wide, T, WIDE, tgt, ___, ___) \
V(JumpIfFalse, T, ORDN, tgt, ___, ___) \
V(JumpIfFalse_Wide, T, WIDE, tgt, ___, ___) \
V(JumpIfNull, T, ORDN, tgt, ___, ___) \
V(JumpIfNull_Wide, T, WIDE, tgt, ___, ___) \
V(JumpIfNotNull, T, ORDN, tgt, ___, ___) \
V(JumpIfNotNull_Wide, T, WIDE, tgt, ___, ___) \
V(DirectCall, D_F, ORDN, num, num, ___) \
V(DirectCall_Wide, D_F, WIDE, num, num, ___) \
V(UncheckedDirectCall, D_F, ORDN, num, num, ___) \
V(UncheckedDirectCall_Wide, D_F, WIDE, num, num, ___) \
V(InterfaceCall, D_F, ORDN, num, num, ___) \
V(InterfaceCall_Wide, D_F, WIDE, num, num, ___) \
V(Unused23, 0, RESV, ___, ___, ___) \
V(Unused24, 0, RESV, ___, ___, ___) \
V(InstantiatedInterfaceCall, D_F, ORDN, num, num, ___) \
V(InstantiatedInterfaceCall_Wide, D_F, WIDE, num, num, ___) \
V(UncheckedClosureCall, D_F, ORDN, num, num, ___) \
V(UncheckedClosureCall_Wide, D_F, WIDE, num, num, ___) \
V(UncheckedInterfaceCall, D_F, ORDN, num, num, ___) \
V(UncheckedInterfaceCall_Wide, D_F, WIDE, num, num, ___) \
V(DynamicCall, D_F, ORDN, num, num, ___) \
V(DynamicCall_Wide, D_F, WIDE, num, num, ___) \
V(ReturnTOS, 0, ORDN, ___, ___, ___) \
V(ReturnAsync, 0, ORDN, ___, ___, ___) \
V(ReturnAsyncStar, 0, ORDN, ___, ___, ___) \
V(ReturnSyncStar, 0, ORDN, ___, ___, ___) \
V(AssertAssignable, A_E, ORDN, num, lit, ___) \
V(AssertAssignable_Wide, A_E, WIDE, num, lit, ___) \
V(Unused30, 0, RESV, ___, ___, ___) \
V(Unused31, 0, RESV, ___, ___, ___) \
V(AssertBoolean, A, ORDN, num, ___, ___) \
V(AssertSubtype, 0, ORDN, ___, ___, ___) \
V(LoadTypeArgumentsField, D, ORDN, lit, ___, ___) \
V(LoadTypeArgumentsField_Wide, D, WIDE, lit, ___, ___) \
V(InstantiateType, D, ORDN, lit, ___, ___) \
V(InstantiateType_Wide, D, WIDE, lit, ___, ___) \
V(InstantiateTypeArgumentsTOS, A_E, ORDN, num, lit, ___) \
V(InstantiateTypeArgumentsTOS_Wide, A_E, WIDE, num, lit, ___) \
V(Unused32, 0, RESV, ___, ___, ___) \
V(Unused33, 0, RESV, ___, ___, ___) \
V(Unused34, 0, RESV, ___, ___, ___) \
V(Unused35, 0, RESV, ___, ___, ___) \
V(Throw, A, ORDN, num, ___, ___) \
V(SetFrame, A, ORDN, num, ___, num) \
V(MoveSpecial, A_Y, ORDN, num, xeg, ___) \
V(MoveSpecial_Wide, A_Y, WIDE, num, xeg, ___) \
V(BooleanNegateTOS, 0, ORDN, ___, ___, ___) \
V(EqualsNull, 0, ORDN, ___, ___, ___) \
V(NullCheck, D, ORDN, lit, ___, ___) \
V(NullCheck_Wide, D, WIDE, lit, ___, ___) \
V(NegateInt, 0, ORDN, ___, ___, ___) \
V(AddInt, 0, ORDN, ___, ___, ___) \
V(SubInt, 0, ORDN, ___, ___, ___) \
V(MulInt, 0, ORDN, ___, ___, ___) \
V(TruncDivInt, 0, ORDN, ___, ___, ___) \
V(ModInt, 0, ORDN, ___, ___, ___) \
V(BitAndInt, 0, ORDN, ___, ___, ___) \
V(BitOrInt, 0, ORDN, ___, ___, ___) \
V(BitXorInt, 0, ORDN, ___, ___, ___) \
V(ShlInt, 0, ORDN, ___, ___, ___) \
V(ShrInt, 0, ORDN, ___, ___, ___) \
V(CompareIntEq, 0, ORDN, ___, ___, ___) \
V(CompareIntGt, 0, ORDN, ___, ___, ___) \
V(CompareIntLt, 0, ORDN, ___, ___, ___) \
V(CompareIntGe, 0, ORDN, ___, ___, ___) \
V(CompareIntLe, 0, ORDN, ___, ___, ___) \
V(NegateDouble, 0, ORDN, ___, ___, ___) \
V(AddDouble, 0, ORDN, ___, ___, ___) \
V(SubDouble, 0, ORDN, ___, ___, ___) \
V(MulDouble, 0, ORDN, ___, ___, ___) \
V(DivDouble, 0, ORDN, ___, ___, ___) \
V(CompareDoubleEq, 0, ORDN, ___, ___, ___) \
V(CompareDoubleGt, 0, ORDN, ___, ___, ___) \
V(CompareDoubleLt, 0, ORDN, ___, ___, ___) \
V(CompareDoubleGe, 0, ORDN, ___, ___, ___) \
V(CompareDoubleLe, 0, ORDN, ___, ___, ___) \
// These bytecodes are only generated within the VM. Reassigning their
// opcodes is not a breaking change.
#define INTERNAL_KERNEL_BYTECODES_LIST(V) \
V(VMInternal_ImplicitGetter, 0, ORDN, ___, ___, ___) \
V(VMInternal_ImplicitSetter, 0, ORDN, ___, ___, ___) \
V(VMInternal_ImplicitStaticGetter, 0, ORDN, ___, ___, ___) \
V(VMInternal_MethodExtractor, 0, ORDN, ___, ___, ___) \
V(VMInternal_InvokeClosure, 0, ORDN, ___, ___, ___) \
V(VMInternal_InvokeField, 0, ORDN, ___, ___, ___) \
V(VMInternal_ForwardDynamicInvocation, 0, ORDN, ___, ___, ___) \
V(VMInternal_NoSuchMethodDispatcher, 0, ORDN, ___, ___, ___) \
V(VMInternal_ImplicitStaticClosure, 0, ORDN, ___, ___, ___) \
V(VMInternal_ImplicitInstanceClosure, 0, ORDN, ___, ___, ___) \
V(VMInternal_ImplicitConstructorClosure, 0, ORDN, ___, ___, ___) \
#define KERNEL_BYTECODES_LIST(V) \
PUBLIC_KERNEL_BYTECODES_LIST(V) \
INTERNAL_KERNEL_BYTECODES_LIST(V)
// clang-format on
typedef uint8_t KBCInstr;
class KernelBytecode {
public:
// Magic value of bytecode files.
static const intptr_t kMagicValue = 0x44424333; // 'DBC3'
// Bytecode format version supported by the VM
// (should match pkg/dart2bytecode/lib/dbc.dart).
static const intptr_t kBytecodeFormatVersion = 1;
enum Opcode {
#define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name,
KERNEL_BYTECODES_LIST(DECLARE_BYTECODE)
#undef DECLARE_BYTECODE
};
static const char* NameOf(Opcode op) {
const char* names[] = {
#define NAME(name, encoding, kind, op1, op2, op3) #name,
KERNEL_BYTECODES_LIST(NAME)
#undef NAME
};
return names[op];
}
static const intptr_t kInstructionSize[];
enum SpecialIndex {
kExceptionSpecialIndex,
kStackTraceSpecialIndex,
kSpecialIndexCount
};
private:
static const intptr_t kWideModifier = 1;
// Should be used only on instructions with wide variants.
DART_FORCE_INLINE static bool IsWide(const KBCInstr* instr) {
return ((DecodeOpcode(instr) & kWideModifier) != 0);
}
public:
DART_FORCE_INLINE static uint8_t DecodeA(const KBCInstr* bc) { return bc[1]; }
DART_FORCE_INLINE static uint8_t DecodeB(const KBCInstr* bc) { return bc[2]; }
DART_FORCE_INLINE static uint8_t DecodeC(const KBCInstr* bc) { return bc[3]; }
DART_FORCE_INLINE static uint32_t DecodeD(const KBCInstr* bc) {
if (IsWide(bc)) {
return static_cast<uint32_t>(bc[1]) |
(static_cast<uint32_t>(bc[2]) << 8) |
(static_cast<uint32_t>(bc[3]) << 16) |
(static_cast<uint32_t>(bc[4]) << 24);
} else {
return bc[1];
}
}
DART_FORCE_INLINE static int32_t DecodeX(const KBCInstr* bc) {
if (IsWide(bc)) {
return static_cast<int32_t>(static_cast<uint32_t>(bc[1]) |
(static_cast<uint32_t>(bc[2]) << 8) |
(static_cast<uint32_t>(bc[3]) << 16) |
(static_cast<uint32_t>(bc[4]) << 24));
} else {
return static_cast<int8_t>(bc[1]);
}
}
DART_FORCE_INLINE static int32_t DecodeT(const KBCInstr* bc) {
if (IsWide(bc)) {
return static_cast<int32_t>((static_cast<uint32_t>(bc[1]) << 8) |
(static_cast<uint32_t>(bc[2]) << 16) |
(static_cast<uint32_t>(bc[3]) << 24)) >>
8;
} else {
return static_cast<int8_t>(bc[1]);
}
}
DART_FORCE_INLINE static uint32_t DecodeE(const KBCInstr* bc) {
if (IsWide(bc)) {
return static_cast<uint32_t>(bc[2]) |
(static_cast<uint32_t>(bc[3]) << 8) |
(static_cast<uint32_t>(bc[4]) << 16) |
(static_cast<uint32_t>(bc[5]) << 24);
} else {
return bc[2];
}
}
DART_FORCE_INLINE static int32_t DecodeY(const KBCInstr* bc) {
if (IsWide(bc)) {
return static_cast<int32_t>(static_cast<uint32_t>(bc[2]) |
(static_cast<uint32_t>(bc[3]) << 8) |
(static_cast<uint32_t>(bc[4]) << 16) |
(static_cast<uint32_t>(bc[5]) << 24));
} else {
return static_cast<int8_t>(bc[2]);
}
}
DART_FORCE_INLINE static uint8_t DecodeF(const KBCInstr* bc) {
if (IsWide(bc)) {
return bc[5];
} else {
return bc[2];
}
}
DART_FORCE_INLINE static Opcode DecodeOpcode(const KBCInstr* bc) {
return static_cast<Opcode>(bc[0]);
}
DART_FORCE_INLINE static const KBCInstr* Next(const KBCInstr* bc) {
return bc + kInstructionSize[DecodeOpcode(bc)];
}
DART_FORCE_INLINE static uword Next(uword pc) {
return pc + kInstructionSize[DecodeOpcode(
reinterpret_cast<const KBCInstr*>(pc))];
}
DART_FORCE_INLINE static bool IsJumpOpcode(const KBCInstr* instr) {
switch (DecodeOpcode(instr)) {
case KernelBytecode::kJump:
case KernelBytecode::kJump_Wide:
case KernelBytecode::kJumpIfNoAsserts:
case KernelBytecode::kJumpIfNoAsserts_Wide:
case KernelBytecode::kJumpIfNotZeroTypeArgs:
case KernelBytecode::kJumpIfNotZeroTypeArgs_Wide:
case KernelBytecode::kJumpIfEqStrict:
case KernelBytecode::kJumpIfEqStrict_Wide:
case KernelBytecode::kJumpIfNeStrict:
case KernelBytecode::kJumpIfNeStrict_Wide:
case KernelBytecode::kJumpIfTrue:
case KernelBytecode::kJumpIfTrue_Wide:
case KernelBytecode::kJumpIfFalse:
case KernelBytecode::kJumpIfFalse_Wide:
case KernelBytecode::kJumpIfNull:
case KernelBytecode::kJumpIfNull_Wide:
case KernelBytecode::kJumpIfNotNull:
case KernelBytecode::kJumpIfNotNull_Wide:
case KernelBytecode::kJumpIfUnchecked:
case KernelBytecode::kJumpIfUnchecked_Wide:
case KernelBytecode::kJumpIfInitialized:
case KernelBytecode::kJumpIfInitialized_Wide:
return true;
default:
return false;
}
}
DART_FORCE_INLINE static bool IsJumpIfUncheckedOpcode(const KBCInstr* instr) {
switch (DecodeOpcode(instr)) {
case KernelBytecode::kJumpIfUnchecked:
case KernelBytecode::kJumpIfUnchecked_Wide:
return true;
default:
return false;
}
}
DART_FORCE_INLINE static bool IsLoadConstantOpcode(const KBCInstr* instr) {
switch (DecodeOpcode(instr)) {
case KernelBytecode::kLoadConstant:
case KernelBytecode::kLoadConstant_Wide:
return true;
default:
return false;
}
}
DART_FORCE_INLINE static bool IsCheckStackOpcode(const KBCInstr* instr) {
return DecodeOpcode(instr) == KernelBytecode::kCheckStack;
}
DART_FORCE_INLINE static bool IsCheckFunctionTypeArgs(const KBCInstr* instr) {
switch (DecodeOpcode(instr)) {
case KernelBytecode::kCheckFunctionTypeArgs:
case KernelBytecode::kCheckFunctionTypeArgs_Wide:
return true;
default:
return false;
}
}
DART_FORCE_INLINE static bool IsEntryOpcode(const KBCInstr* instr) {
switch (DecodeOpcode(instr)) {
case KernelBytecode::kEntry:
case KernelBytecode::kEntry_Wide:
return true;
default:
return false;
}
}
DART_FORCE_INLINE static bool IsEntryOptionalOpcode(const KBCInstr* instr) {
return DecodeOpcode(instr) == KernelBytecode::kEntryOptional;
}
DART_FORCE_INLINE static bool IsFrameOpcode(const KBCInstr* instr) {
switch (DecodeOpcode(instr)) {
case KernelBytecode::kFrame:
case KernelBytecode::kFrame_Wide:
return true;
default:
return false;
}
}
DART_FORCE_INLINE static bool IsSetFrameOpcode(const KBCInstr* instr) {
return DecodeOpcode(instr) == KernelBytecode::kSetFrame;
}
DART_FORCE_INLINE static bool IsDebugCheckOpcode(const KBCInstr* instr) {
return DecodeOpcode(instr) == KernelBytecode::kDebugCheck;
}
// The interpreter, the bytecode generator, the bytecode compiler, and this
// function must agree on this list of opcodes.
// For each instruction with listed opcode:
// - The interpreter checks for a debug break.
// - The bytecode generator emits a source position.
// - The bytecode compiler may emit a DebugStepCheck call.
DART_FORCE_INLINE static bool IsDebugCheckedOpcode(const KBCInstr* instr) {
switch (DecodeOpcode(instr)) {
case KernelBytecode::kDebugCheck:
case KernelBytecode::kDirectCall:
case KernelBytecode::kDirectCall_Wide:
case KernelBytecode::kUncheckedDirectCall:
case KernelBytecode::kUncheckedDirectCall_Wide:
case KernelBytecode::kInterfaceCall:
case KernelBytecode::kInterfaceCall_Wide:
case KernelBytecode::kInstantiatedInterfaceCall:
case KernelBytecode::kInstantiatedInterfaceCall_Wide:
case KernelBytecode::kUncheckedClosureCall:
case KernelBytecode::kUncheckedClosureCall_Wide:
case KernelBytecode::kUncheckedInterfaceCall:
case KernelBytecode::kUncheckedInterfaceCall_Wide:
case KernelBytecode::kDynamicCall:
case KernelBytecode::kDynamicCall_Wide:
case KernelBytecode::kReturnTOS:
case KernelBytecode::kReturnAsync:
case KernelBytecode::kReturnAsyncStar:
case KernelBytecode::kReturnSyncStar:
case KernelBytecode::kEqualsNull:
case KernelBytecode::kNegateInt:
case KernelBytecode::kNegateDouble:
case KernelBytecode::kAddInt:
case KernelBytecode::kSubInt:
case KernelBytecode::kMulInt:
case KernelBytecode::kTruncDivInt:
case KernelBytecode::kModInt:
case KernelBytecode::kBitAndInt:
case KernelBytecode::kBitOrInt:
case KernelBytecode::kBitXorInt:
case KernelBytecode::kShlInt:
case KernelBytecode::kShrInt:
case KernelBytecode::kCompareIntEq:
case KernelBytecode::kCompareIntGt:
case KernelBytecode::kCompareIntLt:
case KernelBytecode::kCompareIntGe:
case KernelBytecode::kCompareIntLe:
case KernelBytecode::kAddDouble:
case KernelBytecode::kSubDouble:
case KernelBytecode::kMulDouble:
case KernelBytecode::kDivDouble:
case KernelBytecode::kCompareDoubleEq:
case KernelBytecode::kCompareDoubleGt:
case KernelBytecode::kCompareDoubleLt:
case KernelBytecode::kCompareDoubleGe:
case KernelBytecode::kCompareDoubleLe:
return true;
default:
return false;
}
}
DART_FORCE_INLINE static uint8_t DecodeArgc(const KBCInstr* ret_addr) {
// All call instructions have DF encoding, with argc being the last byte
// regardless of whether the wide variant is used or not.
return ret_addr[-1];
}
// Converts bytecode PC into an offset.
// For return addresses used in PcDescriptors, PC is also augmented by 1.
// TODO(regis): Eliminate this correction.
static intptr_t BytecodePcToOffset(uint32_t pc, bool is_return_address) {
return pc + (is_return_address ? 1 : 0);
}
static uint32_t OffsetToBytecodePc(intptr_t offset, bool is_return_address) {
return offset - (is_return_address ? 1 : 0);
}
static void GetVMInternalBytecodeInstructions(Opcode opcode,
const KBCInstr** instructions,
intptr_t* instructions_size);
private:
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(KernelBytecode);
};
} // namespace dart
#endif // RUNTIME_VM_CONSTANTS_KBC_H_
+12
View File
@@ -9,6 +9,7 @@
#include "vm/debugger.h"
#include "vm/dispatch_table.h"
#include "vm/heap/safepoint.h"
#include "vm/interpreter.h"
#include "vm/object_store.h"
#include "vm/resolver.h"
#include "vm/runtime_entry.h"
@@ -120,6 +121,17 @@ ObjectPtr DartEntry::InvokeFunction(const Function& function,
ASSERT(thread->IsDartMutatorThread());
ASSERT(!function.IsNull());
#if defined(DART_DYNAMIC_MODULES)
if (function.HasBytecode()) {
// SuspendLongJumpScope suspend_long_jump_scope(thread);
TransitionToGenerated transition(thread);
return Interpreter::Current()->Call(function, arguments_descriptor,
arguments, thread);
} else {
ASSERT(!function.is_declared_in_bytecode());
}
#endif // defined(DART_DYNAMIC_MODULES)
#if !defined(DART_PRECOMPILED_RUNTIME)
if (!function.HasCode()) {
const Object& result = Object::Handle(
+2
View File
@@ -179,6 +179,8 @@ class ArgumentsDescriptor : public ValueObject {
// A cache of VM heap allocated arguments descriptors.
static ArrayPtr cached_args_descriptors_[kCachedDescriptorCount];
friend class Interpreter;
friend class InterpreterHelpers;
friend class VMSerializationRoots;
friend class VMDeserializationRoots;
DISALLOW_COPY_AND_ASSIGN(ArgumentsDescriptor);
+4 -1
View File
@@ -1684,7 +1684,7 @@ DebuggerStackTrace* DebuggerStackTrace::Collect() {
OS::PrintErr("CollectStackTrace: visiting frame:\n\t%s\n",
frame->ToCString());
}
if (frame->IsDartFrame()) {
if (frame->IsDartFrame() && !frame->is_interpreted()) {
code = frame->LookupDartCode();
stack_trace->AppendCodeFrames(frame, code);
}
@@ -1743,6 +1743,9 @@ DebuggerStackTrace* DebuggerStackTrace::CollectAsyncAwaiters() {
StackTraceUtils::CollectFrames(
thread, /*skip_frames=*/0,
[&](const StackTraceUtils::Frame& frame) {
if (frame.code.IsNull()) {
return;
}
if (frame.frame != nullptr) { // Synchronous portion of the stack.
stack_trace->AppendCodeFrames(frame.frame, frame.code);
} else {
+28 -5
View File
@@ -17,6 +17,7 @@
#include "vm/debugger.h"
#include "vm/deopt_instructions.h"
#include "vm/flags.h"
#include "vm/interpreter.h"
#include "vm/log.h"
#include "vm/longjump.h"
#include "vm/object.h"
@@ -105,14 +106,25 @@ static void BuildStackTrace(StackTraceBuilder* builder) {
StackFrame* frame = frames.NextFrame();
ASSERT(frame != nullptr); // We expect to find a dart invocation frame.
Code& code = Code::Handle();
Bytecode& bytecode = Bytecode::Handle();
for (; frame != nullptr; frame = frames.NextFrame()) {
if (!frame->IsDartFrame()) {
continue;
}
code = frame->LookupDartCode();
ASSERT(code.ContainsInstructionAt(frame->pc()));
const uword pc_offset = frame->pc() - code.PayloadStart();
builder->AddFrame(code, pc_offset);
if (frame->is_interpreted()) {
bytecode = frame->LookupDartBytecode();
ASSERT(bytecode.ContainsInstructionAt(frame->pc()));
if (bytecode.function() == Function::null()) {
continue;
}
const uword pc_offset = frame->pc() - bytecode.PayloadStart();
builder->AddFrame(bytecode, pc_offset);
} else {
code = frame->LookupDartCode();
ASSERT(code.ContainsInstructionAt(frame->pc()));
const uword pc_offset = frame->pc() - code.PayloadStart();
builder->AddFrame(code, pc_offset);
}
}
}
@@ -587,7 +599,9 @@ static void ClearLazyDeopts(Thread* thread, uword frame_pointer) {
StackFrameIterator::kNoCrossThreadIteration);
for (StackFrame* frame = frames.NextFrame(); frame != nullptr;
frame = frames.NextFrame()) {
if (frame->fp() >= frame_pointer) {
if (frame->is_interpreted()) {
continue;
} else if (frame->fp() >= frame_pointer) {
break;
}
if (frame->IsMarkedForLazyDeopt()) {
@@ -634,6 +648,15 @@ NO_SANITIZE_SAFE_STACK // This function manipulates the safestack pointer.
uword frame_pointer,
bool clear_deopt_at_target) {
ASSERT(thread->execution_state() == Thread::kThreadInVM);
#if defined(DART_DYNAMIC_MODULES)
Interpreter* interpreter = thread->interpreter();
if ((interpreter != nullptr) && interpreter->HasFrame(frame_pointer)) {
interpreter->JumpToFrame(program_counter, stack_pointer, frame_pointer,
thread);
}
#endif // defined(DART_DYNAMIC_MODULES)
const uword fp_for_clearing =
(clear_deopt_at_target ? frame_pointer + 1 : frame_pointer);
ClearLazyDeopts(thread, fp_for_clearing);
+22
View File
@@ -87,6 +87,28 @@ void _printGeneratedStackTrace(uword fp, uword sp, uword pc) {
}
}
#if defined(DART_DYNAMIC_MODULES)
// Like _printDartStackTrace, but works in the interpreter loop.
// Must be called with the current interpreter fp, sp, and pc.
// Note that sp[0] is not modified, but sp[1] will be trashed.
DART_EXPORT
void _printInterpreterStackTrace(ObjectPtr* fp,
ObjectPtr* sp,
const KBCInstr* pc) {
Thread* thread = Thread::Current();
sp[1] = Function::null();
sp[2] = Bytecode::null();
sp[3] = static_cast<ObjectPtr>(reinterpret_cast<uword>(pc));
sp[4] = static_cast<ObjectPtr>(reinterpret_cast<uword>(fp));
ObjectPtr* exit_fp = sp + 1 + kKBCDartFrameFixedSize;
thread->set_top_exit_frame_info(reinterpret_cast<uword>(exit_fp));
thread->set_execution_state(Thread::kThreadInVM);
_printDartStackTrace();
thread->set_execution_state(Thread::kThreadInGenerated);
thread->set_top_exit_frame_info(0);
}
#endif // defined(DART_DYNAMIC_MODULES)
class PrintObjectPointersVisitor : public ObjectPointerVisitor {
public:
PrintObjectPointersVisitor()
+14
View File
@@ -726,6 +726,20 @@ class MarkingWeakVisitor : public HandleVisitor {
void GCMarker::Prologue() {
isolate_group_->ReleaseStoreBuffers();
new_marking_stack_.PushAll(tlab_deferred_marking_stack_.PopAll());
#if defined(DART_DYNAMIC_MODULES)
isolate_group_->ForEachIsolate(
[&](Isolate* isolate) {
Thread* mutator_thread = isolate->mutator_thread();
if (mutator_thread != nullptr) {
Interpreter* interpreter = mutator_thread->interpreter();
if (interpreter != nullptr) {
interpreter->ClearLookupCache();
}
}
},
/*at_safepoint=*/true);
#endif // defined(DART_DYNAMIC_MODULES)
}
void GCMarker::Epilogue() {}
File diff suppressed because it is too large Load Diff
+271
View File
@@ -0,0 +1,271 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_INTERPRETER_H_
#define RUNTIME_VM_INTERPRETER_H_
#include "vm/globals.h"
#if defined(DART_DYNAMIC_MODULES)
#include "vm/compiler/method_recognizer.h"
#include "vm/constants_kbc.h"
#include "vm/tagged_pointer.h"
namespace dart {
class Array;
class Code;
class InterpreterSetjmpBuffer;
class Isolate;
class ObjectPointerVisitor;
class Thread;
class LookupCache : public ValueObject {
public:
LookupCache() {
ASSERT(Utils::IsPowerOfTwo(sizeof(Entry)));
ASSERT(Utils::IsPowerOfTwo(sizeof(kNumEntries)));
Clear();
}
void Clear();
bool Lookup(intptr_t receiver_cid,
StringPtr function_name,
ArrayPtr arguments_descriptor,
FunctionPtr* target) const;
void Insert(intptr_t receiver_cid,
StringPtr function_name,
ArrayPtr arguments_descriptor,
FunctionPtr target);
private:
struct Entry {
intptr_t receiver_cid;
StringPtr function_name;
ArrayPtr arguments_descriptor;
FunctionPtr target;
};
static const intptr_t kNumEntries = 1024;
static const intptr_t kTableMask = kNumEntries - 1;
Entry entries_[kNumEntries];
};
class Interpreter {
public:
static const uword kInterpreterStackUnderflowSize = 0x80;
// The entry frame pc marker must be non-zero (a valid exception handler pc).
static const word kEntryFramePcMarker = -1;
Interpreter();
~Interpreter();
// The currently executing Interpreter instance, which is associated to the
// current isolate
static Interpreter* Current();
// Low address (KBC stack grows up).
uword stack_base() const { return stack_base_; }
// Limit for StackOverflowError.
uword overflow_stack_limit() const { return overflow_stack_limit_; }
// High address (KBC stack grows up).
uword stack_limit() const { return stack_limit_; }
// Returns true if the interpreter's stack contains the given frame.
// TODO(regis): We should rely on a new thread vm_tag to identify an
// interpreter frame and not need this HasFrame() method.
bool HasFrame(uword frame) const {
return frame >= stack_base() && frame < stack_limit();
}
// Identify an entry frame by looking at its pc marker value.
static bool IsEntryFrameMarker(const KBCInstr* pc) {
return reinterpret_cast<word>(pc) == kEntryFramePcMarker;
}
ObjectPtr Call(const Function& function,
const Array& arguments_descriptor,
const Array& arguments,
Thread* thread);
ObjectPtr Call(FunctionPtr function,
ArrayPtr argdesc,
intptr_t argc,
ObjectPtr const* argv,
ArrayPtr args_array,
Thread* thread);
void JumpToFrame(uword pc, uword sp, uword fp, Thread* thread);
uword get_sp() const { return reinterpret_cast<uword>(fp_); } // Yes, fp_.
uword get_fp() const { return reinterpret_cast<uword>(fp_); }
uword get_pc() const { return reinterpret_cast<uword>(pc_); }
void Unexit(Thread* thread);
void VisitObjectPointers(ObjectPointerVisitor* visitor);
void ClearLookupCache() { lookup_cache_.Clear(); }
#ifndef PRODUCT
void set_is_debugging(bool value) { is_debugging_ = value; }
bool is_debugging() const { return is_debugging_; }
#endif // !PRODUCT
private:
uintptr_t* stack_;
uword stack_base_;
uword overflow_stack_limit_;
uword stack_limit_;
ObjectPtr* volatile fp_;
const KBCInstr* volatile pc_;
DEBUG_ONLY(uint64_t icount_;)
InterpreterSetjmpBuffer* last_setjmp_buffer_;
ObjectPoolPtr pp_; // Pool Pointer.
ArrayPtr argdesc_; // Arguments Descriptor: used to pass information between
// call instruction and the function entry.
ObjectPtr special_[KernelBytecode::kSpecialIndexCount];
LookupCache lookup_cache_;
void Exit(Thread* thread,
ObjectPtr* base,
ObjectPtr* exit_frame,
const KBCInstr* pc);
bool Invoke(Thread* thread,
ObjectPtr* call_base,
ObjectPtr* call_top,
const KBCInstr** pc,
ObjectPtr** FP,
ObjectPtr** SP);
bool InvokeCompiled(Thread* thread,
FunctionPtr function,
ObjectPtr* call_base,
ObjectPtr* call_top,
const KBCInstr** pc,
ObjectPtr** FP,
ObjectPtr** SP);
bool InvokeBytecode(Thread* thread,
FunctionPtr function,
ObjectPtr* call_base,
ObjectPtr* call_top,
const KBCInstr** pc,
ObjectPtr** FP,
ObjectPtr** SP);
bool InstanceCall(Thread* thread,
StringPtr target_name,
ObjectPtr* call_base,
ObjectPtr* call_top,
const KBCInstr** pc,
ObjectPtr** FP,
ObjectPtr** SP);
bool CopyParameters(Thread* thread,
const KBCInstr** pc,
ObjectPtr** FP,
ObjectPtr** SP,
const intptr_t num_fixed_params,
const intptr_t num_opt_pos_params,
const intptr_t num_opt_named_params,
const intptr_t num_reserved_locals);
bool AssertAssignable(Thread* thread,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* call_top,
ObjectPtr* args,
SubtypeTestCachePtr cache);
template <bool is_getter>
bool AssertAssignableField(Thread* thread,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP,
InstancePtr instance,
FieldPtr field,
InstancePtr value);
bool AllocateMint(Thread* thread,
int64_t value,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateDouble(Thread* thread,
double value,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateFloat32x4(Thread* thread,
simd128_value_t value,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateFloat64x2(Thread* thread,
simd128_value_t value,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateArray(Thread* thread,
TypeArgumentsPtr type_args,
ObjectPtr length,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateContext(Thread* thread,
intptr_t num_variables,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
bool AllocateClosure(Thread* thread,
const KBCInstr* pc,
ObjectPtr* FP,
ObjectPtr* SP);
#if defined(DEBUG)
// Returns true if tracing of executed instructions is enabled.
bool IsTracingExecution() const;
// Prints bytecode instruction at given pc for instruction tracing.
void TraceInstruction(const KBCInstr* pc) const;
bool IsWritingTraceFile() const;
void FlushTraceBuffer();
void WriteInstructionToTrace(const KBCInstr* pc);
void* trace_file_;
uint64_t trace_file_bytes_written_;
static const intptr_t kTraceBufferSizeInBytes = 10 * KB;
static const intptr_t kTraceBufferInstrs =
kTraceBufferSizeInBytes / sizeof(KBCInstr);
KBCInstr* trace_buffer_;
intptr_t trace_buffer_idx_;
#endif // defined(DEBUG)
// Longjmp support for exceptions.
InterpreterSetjmpBuffer* last_setjmp_buffer() { return last_setjmp_buffer_; }
void set_last_setjmp_buffer(InterpreterSetjmpBuffer* buffer) {
last_setjmp_buffer_ = buffer;
}
#ifndef PRODUCT
bool is_debugging_ = false;
#endif // !PRODUCT
friend class InterpreterSetjmpBuffer;
DISALLOW_COPY_AND_ASSIGN(Interpreter);
};
} // namespace dart
#endif // defined(DART_DYNAMIC_MODULES)
#endif // RUNTIME_VM_INTERPRETER_H_
+4 -4
View File
@@ -530,9 +530,9 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
Mutex* unlinked_call_map_mutex() { return &unlinked_call_map_mutex_; }
#endif
#if !defined(DART_PRECOMPILED_RUNTIME)
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
Mutex* initializer_functions_mutex() { return &initializer_functions_mutex_; }
#endif // !defined(DART_PRECOMPILED_RUNTIME)
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
SafepointRwLock* program_lock() { return program_lock_.get(); }
@@ -907,9 +907,9 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
Mutex unlinked_call_map_mutex_;
#endif
#if !defined(DART_PRECOMPILED_RUNTIME)
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
Mutex initializer_functions_mutex_;
#endif // !defined(DART_PRECOMPILED_RUNTIME)
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
// Protect access to boxed_field_list_.
Mutex field_list_mutex_;
-61
View File
@@ -2387,67 +2387,6 @@ FunctionPtr KernelLoader::GetClosureFunction(Thread* thread,
return function.ptr();
}
FunctionPtr CreateFieldInitializerFunction(Thread* thread,
Zone* zone,
const Field& field) {
ASSERT(field.InitializerFunction() == Function::null());
String& init_name = String::Handle(zone, field.name());
init_name = Symbols::FromConcat(thread, Symbols::InitPrefix(), init_name);
// Static field initializers are not added as members of their owning class,
// so they must be preemptively given a patch class to avoid the meaning of
// their kernel/token position changing during a reload. Compare
// Class::PatchFieldsAndFunctions().
// This might also be necessary for lazy computation of local var descriptors.
// Compare https://codereview.chromium.org//1317753004
const Script& script = Script::Handle(zone, field.Script());
const Class& field_owner = Class::Handle(zone, field.Owner());
const auto& kernel_program_info =
KernelProgramInfo::Handle(zone, field.KernelProgramInfo());
const PatchClass& initializer_owner = PatchClass::Handle(
zone, PatchClass::New(field_owner, kernel_program_info, script));
const Library& lib = Library::Handle(zone, field_owner.library());
initializer_owner.set_kernel_library_index(lib.kernel_library_index());
// Create a static initializer.
FunctionType& signature = FunctionType::Handle(zone, FunctionType::New());
const Function& initializer_fun = Function::Handle(
zone,
Function::New(signature, init_name, UntaggedFunction::kFieldInitializer,
field.is_static(), // is_static
false, // is_const
false, // is_abstract
false, // is_external
false, // is_native
initializer_owner, TokenPosition::kNoSource));
if (!field.is_static()) {
signature.set_num_fixed_parameters(1);
signature.set_parameter_types(
Array::Handle(zone, Array::New(1, Heap::kOld)));
signature.SetParameterTypeAt(
0, AbstractType::Handle(zone, field_owner.DeclarationType()));
initializer_fun.CreateNameArray();
initializer_fun.SetParameterNameAt(0, Symbols::This());
}
signature.set_result_type(AbstractType::Handle(zone, field.type()));
initializer_fun.set_is_reflectable(false);
initializer_fun.set_is_inlinable(false);
initializer_fun.set_token_pos(field.token_pos());
initializer_fun.set_end_token_pos(field.end_token_pos());
initializer_fun.set_accessor_field(field);
initializer_fun.InheritKernelOffsetFrom(field);
initializer_fun.set_is_extension_member(field.is_extension_member());
initializer_fun.set_is_extension_type_member(
field.is_extension_type_member());
signature ^= ClassFinalizer::FinalizeType(signature);
initializer_fun.SetSignature(signature);
field.SetInitializerFunction(initializer_fun);
return initializer_fun.ptr();
}
} // namespace kernel
} // namespace dart
#endif // !defined(DART_PRECOMPILED_RUNTIME)
-4
View File
@@ -420,10 +420,6 @@ class KernelLoader : public ValueObject {
DISALLOW_COPY_AND_ASSIGN(KernelLoader);
};
FunctionPtr CreateFieldInitializerFunction(Thread* thread,
Zone* zone,
const Field& field);
} // namespace kernel
} // namespace dart
+19 -2
View File
@@ -85,7 +85,8 @@ class NativeArguments {
ObjectPtr ArgAt(int index) const {
ASSERT((index >= 0) && (index < ArgCount()));
ObjectPtr* arg_ptr = &(argv_[-index]);
ObjectPtr* arg_ptr =
&(argv_[ReverseArgOrderBit::decode(argc_tag_) ? index : -index]);
// Tell MemorySanitizer the ObjectPtr was initialized (by generated code).
MSAN_UNPOISON(arg_ptr, kWordSize);
return *arg_ptr;
@@ -94,7 +95,7 @@ class NativeArguments {
void SetArgAt(int index, const Object& value) const {
ASSERT(thread_->execution_state() == Thread::kThreadInVM);
ASSERT((index >= 0) && (index < ArgCount()));
argv_[-index] = value.ptr();
argv_[ReverseArgOrderBit::decode(argc_tag_) ? index : -index] = value.ptr();
}
// Does not include hidden type arguments vector.
@@ -198,14 +199,30 @@ class NativeArguments {
kArgcSize = 24,
kFunctionBit = kArgcBit + kArgcSize,
kFunctionSize = 1,
kReverseArgOrderBit = kFunctionBit + kFunctionSize,
kReverseArgOrderSize = 1,
};
class ArgcBits : public BitField<intptr_t, int32_t, kArgcBit, kArgcSize> {};
class FunctionBits
: public BitField<intptr_t, int, kFunctionBit, kFunctionSize> {};
class ReverseArgOrderBit
: public BitField<intptr_t, bool, kReverseArgOrderBit, 1> {};
friend class Api;
friend class Interpreter;
friend class NativeEntry;
friend class Simulator;
#if defined(DART_DYNAMIC_MODULES)
NativeArguments(Thread* thread,
int argc_tag,
ObjectPtr* argv,
ObjectPtr* retval)
: thread_(thread),
argc_tag_(ReverseArgOrderBit::update(true, argc_tag)),
argv_(argv),
retval_(retval) {}
#endif // defined(DART_DYNAMIC_MODULES)
// Since this function is passed an ObjectPtr directly, we need to be
// exceedingly careful when we use it. If there are any other side
// effects in the statement that may cause GC, it could lead to
+553 -30
View File
@@ -16,6 +16,7 @@
#include "platform/unicode.h"
#include "vm/bit_vector.h"
#include "vm/bootstrap.h"
#include "vm/bytecode_reader.h"
#include "vm/canonical_tables.h"
#include "vm/class_finalizer.h"
#include "vm/class_id.h"
@@ -24,6 +25,7 @@
#include "vm/code_descriptors.h"
#include "vm/code_observers.h"
#include "vm/compiler/assembler/disassembler.h"
#include "vm/compiler/assembler/disassembler_kbc.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/compiler/runtime_api.h"
#include "vm/cpu.h"
@@ -173,6 +175,7 @@ ClassPtr Object::var_descriptors_class_ = static_cast<ClassPtr>(RAW_NULL);
ClassPtr Object::exception_handlers_class_ = static_cast<ClassPtr>(RAW_NULL);
ClassPtr Object::context_class_ = static_cast<ClassPtr>(RAW_NULL);
ClassPtr Object::context_scope_class_ = static_cast<ClassPtr>(RAW_NULL);
ClassPtr Object::bytecode_class_ = static_cast<ClassPtr>(RAW_NULL);
ClassPtr Object::sentinel_class_ = static_cast<ClassPtr>(RAW_NULL);
ClassPtr Object::singletargetcache_class_ = static_cast<ClassPtr>(RAW_NULL);
ClassPtr Object::unlinkedcall_class_ = static_cast<ClassPtr>(RAW_NULL);
@@ -551,6 +554,23 @@ static type SpecialCharacter(type value) {
return '\0';
}
#if defined(DART_DYNAMIC_MODULES)
static BytecodePtr CreateVMInternalBytecode(KernelBytecode::Opcode opcode) {
const KBCInstr* instructions = nullptr;
intptr_t instructions_size = 0;
KernelBytecode::GetVMInternalBytecodeInstructions(opcode, &instructions,
&instructions_size);
const auto& bytecode = Bytecode::Handle(
Bytecode::New(reinterpret_cast<uword>(instructions), instructions_size,
-1, TypedDataBase::Handle(), Object::empty_object_pool()));
bytecode.set_pc_descriptors(Object::empty_descriptors());
bytecode.set_exception_handlers(Object::empty_exception_handlers());
return bytecode.ptr();
}
#endif // defined(DART_DYNAMIC_MODULES)
void Object::InitNullAndBool(IsolateGroup* isolate_group) {
// Should only be run by the vm isolate.
ASSERT(isolate_group == Dart::vm_isolate_group());
@@ -912,6 +932,9 @@ void Object::Init(IsolateGroup* isolate_group) {
cls = Class::New<ContextScope, RTN::ContextScope>(isolate_group);
context_scope_class_ = cls.ptr();
cls = Class::New<Bytecode, RTN::Bytecode>(isolate_group);
bytecode_class_ = cls.ptr();
cls = Class::New<SingleTargetCache, RTN::SingleTargetCache>(isolate_group);
singletargetcache_class_ = cls.ptr();
@@ -1270,6 +1293,41 @@ void Object::Init(IsolateGroup* isolate_group) {
// synthetic_getter_parameter_names_ object needs to be created earlier as
// VM isolate snapshot reader references it before Object::FinalizeVMIsolate.
#if defined(DART_DYNAMIC_MODULES)
*implicit_getter_bytecode_ =
CreateVMInternalBytecode(KernelBytecode::kVMInternal_ImplicitGetter);
*implicit_setter_bytecode_ =
CreateVMInternalBytecode(KernelBytecode::kVMInternal_ImplicitSetter);
*implicit_static_getter_bytecode_ = CreateVMInternalBytecode(
KernelBytecode::kVMInternal_ImplicitStaticGetter);
*method_extractor_bytecode_ =
CreateVMInternalBytecode(KernelBytecode::kVMInternal_MethodExtractor);
*invoke_closure_bytecode_ =
CreateVMInternalBytecode(KernelBytecode::kVMInternal_InvokeClosure);
*invoke_field_bytecode_ =
CreateVMInternalBytecode(KernelBytecode::kVMInternal_InvokeField);
*nsm_dispatcher_bytecode_ = CreateVMInternalBytecode(
KernelBytecode::kVMInternal_NoSuchMethodDispatcher);
*dynamic_invocation_forwarder_bytecode_ = CreateVMInternalBytecode(
KernelBytecode::kVMInternal_ForwardDynamicInvocation);
*implicit_static_closure_bytecode_ = CreateVMInternalBytecode(
KernelBytecode::kVMInternal_ImplicitStaticClosure);
*implicit_instance_closure_bytecode_ = CreateVMInternalBytecode(
KernelBytecode::kVMInternal_ImplicitInstanceClosure);
*implicit_constructor_closure_bytecode_ = CreateVMInternalBytecode(
KernelBytecode::kVMInternal_ImplicitConstructorClosure);
#endif // defined(DART_DYNAMIC_MODULES)
// Some thread fields need to be reinitialized as null constants have not been
// initialized until now.
thread->ClearStickyError();
@@ -1347,6 +1405,28 @@ void Object::Init(IsolateGroup* isolate_group) {
ASSERT(synthetic_getter_parameter_types_->IsArray());
ASSERT(!synthetic_getter_parameter_names_->IsSmi());
ASSERT(synthetic_getter_parameter_names_->IsArray());
ASSERT(!implicit_getter_bytecode_->IsSmi());
ASSERT(implicit_getter_bytecode_->IsBytecode());
ASSERT(!implicit_setter_bytecode_->IsSmi());
ASSERT(implicit_setter_bytecode_->IsBytecode());
ASSERT(!implicit_static_getter_bytecode_->IsSmi());
ASSERT(implicit_static_getter_bytecode_->IsBytecode());
ASSERT(!method_extractor_bytecode_->IsSmi());
ASSERT(method_extractor_bytecode_->IsBytecode());
ASSERT(!invoke_closure_bytecode_->IsSmi());
ASSERT(invoke_closure_bytecode_->IsBytecode());
ASSERT(!invoke_field_bytecode_->IsSmi());
ASSERT(invoke_field_bytecode_->IsBytecode());
ASSERT(!nsm_dispatcher_bytecode_->IsSmi());
ASSERT(nsm_dispatcher_bytecode_->IsBytecode());
ASSERT(!dynamic_invocation_forwarder_bytecode_->IsSmi());
ASSERT(dynamic_invocation_forwarder_bytecode_->IsBytecode());
ASSERT(!implicit_static_closure_bytecode_->IsSmi());
ASSERT(implicit_static_closure_bytecode_->IsBytecode());
ASSERT(!implicit_instance_closure_bytecode_->IsSmi());
ASSERT(implicit_instance_closure_bytecode_->IsBytecode());
ASSERT(!implicit_constructor_closure_bytecode_->IsSmi());
ASSERT(implicit_constructor_closure_bytecode_->IsBytecode());
}
void Object::FinishInit(IsolateGroup* isolate_group) {
@@ -1392,6 +1472,7 @@ void Object::Cleanup() {
exception_handlers_class_ = static_cast<ClassPtr>(RAW_NULL);
context_class_ = static_cast<ClassPtr>(RAW_NULL);
context_scope_class_ = static_cast<ClassPtr>(RAW_NULL);
bytecode_class_ = static_cast<ClassPtr>(RAW_NULL);
singletargetcache_class_ = static_cast<ClassPtr>(RAW_NULL);
unlinkedcall_class_ = static_cast<ClassPtr>(RAW_NULL);
monomorphicsmiablecall_class_ = static_cast<ClassPtr>(RAW_NULL);
@@ -1509,6 +1590,7 @@ void Object::FinalizeVMIsolate(IsolateGroup* isolate_group) {
SET_CLASS_NAME(exception_handlers, ExceptionHandlers);
SET_CLASS_NAME(context, Context);
SET_CLASS_NAME(context_scope, ContextScope);
SET_CLASS_NAME(bytecode, Bytecode);
SET_CLASS_NAME(sentinel, Sentinel);
SET_CLASS_NAME(singletargetcache, SingleTargetCache);
SET_CLASS_NAME(unlinkedcall, UnlinkedCall);
@@ -3105,7 +3187,7 @@ ClassPtr Class::New(IsolateGroup* isolate_group, bool register_class) {
return result.ptr();
}
#if !defined(DART_PRECOMPILED_RUNTIME)
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
static void ReportTooManyTypeArguments(const Class& cls) {
Report::MessageF(Report::kError, Script::Handle(cls.script()),
cls.token_pos(), Report::AtLocation,
@@ -3114,10 +3196,10 @@ static void ReportTooManyTypeArguments(const Class& cls) {
String::Handle(cls.Name()).ToCString());
UNREACHABLE();
}
#endif // !defined(DART_PRECOMPILED_RUNTIME)
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
void Class::set_num_type_arguments(intptr_t value) const {
#if defined(DART_PRECOMPILED_RUNTIME)
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
UNREACHABLE();
#else
if (!Utils::IsInt(16, value)) {
@@ -3129,7 +3211,7 @@ void Class::set_num_type_arguments(intptr_t value) const {
DEBUG_ASSERT(old_value == kUnknownNumTypeArguments || old_value == value);
StoreNonPointer<int16_t, int16_t, std::memory_order_relaxed>(
&untag()->num_type_arguments_, value);
#endif // defined(DART_PRECOMPILED_RUNTIME)
#endif // defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
}
void Class::set_num_type_arguments_unsafe(intptr_t value) const {
@@ -3451,7 +3533,7 @@ void Class::set_library(const Library& value) const {
void Class::set_type_parameters(const TypeParameters& value) const {
ASSERT((num_type_arguments() == kUnknownNumTypeArguments) ||
is_prefinalized());
is_declared_in_bytecode() || is_prefinalized());
untag()->set_type_parameters(value.ptr());
}
@@ -3966,6 +4048,26 @@ FunctionPtr Class::CreateInvocationDispatcher(
signature ^= ClassFinalizer::FinalizeType(signature);
invocation.SetSignature(signature);
#if defined(DART_DYNAMIC_MODULES)
#if defined(DART_PRECOMPILED_RUNTIME)
const bool attach_bytecode = true;
#else
const bool attach_bytecode = is_declared_in_bytecode();
#endif
if (attach_bytecode) {
switch (kind) {
case UntaggedFunction::kNoSuchMethodDispatcher:
invocation.AttachBytecode(Object::nsm_dispatcher_bytecode());
break;
case UntaggedFunction::kInvokeFieldDispatcher:
invocation.AttachBytecode(Object::invoke_field_bytecode());
break;
default:
UNREACHABLE();
}
}
#endif // defined(DART_DYNAMIC_MODULES)
return invocation.ptr();
}
@@ -4015,6 +4117,17 @@ FunctionPtr Function::CreateMethodExtractor(const String& getter_name) const {
signature ^= ClassFinalizer::FinalizeType(signature);
extractor.SetSignature(signature);
#if defined(DART_DYNAMIC_MODULES)
#if defined(DART_PRECOMPILED_RUNTIME)
const bool attach_bytecode = true;
#else
const bool attach_bytecode = is_declared_in_bytecode();
#endif
if (attach_bytecode) {
extractor.AttachBytecode(Object::method_extractor_bytecode());
}
#endif // defined(DART_DYNAMIC_MODULES)
owner.AddFunction(extractor);
return extractor.ptr();
@@ -4216,7 +4329,7 @@ StringPtr Function::CreateDynamicInvocationForwarderName(const String& name) {
return Symbols::FromConcat(Thread::Current(), Symbols::DynamicPrefix(), name);
}
#if !defined(DART_PRECOMPILED_RUNTIME)
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
FunctionPtr Function::CreateDynamicInvocationForwarder(
const String& mangled_name) const {
Thread* thread = Thread::Current();
@@ -4252,6 +4365,17 @@ FunctionPtr Function::CreateDynamicInvocationForwarder(
forwarder.InheritKernelOffsetFrom(*this);
forwarder.SetForwardingTarget(*this);
#if defined(DART_DYNAMIC_MODULES)
#if defined(DART_PRECOMPILED_RUNTIME)
const bool attach_bytecode = true;
#else
const bool attach_bytecode = is_declared_in_bytecode();
#endif
if (attach_bytecode) {
forwarder.AttachBytecode(Object::dynamic_invocation_forwarder_bytecode());
}
#endif
return forwarder.ptr();
}
@@ -4271,7 +4395,12 @@ FunctionPtr Function::GetDynamicInvocationForwarder(
if (!result.IsNull()) return result.ptr();
const bool needs_dyn_forwarder =
#if defined(DART_DYNAMIC_MODULES) && defined(DART_PRECOMPILED_RUNTIME)
// TODO(alexmarkov)
false;
#else
kernel::NeedsDynamicInvocationForwarder(*this);
#endif
if (!needs_dyn_forwarder) {
return ptr();
}
@@ -4292,6 +4421,22 @@ FunctionPtr Function::GetDynamicInvocationForwarder(
return result.ptr();
}
void Function::ReadParameterCovariance(
BitVector* is_covariant,
BitVector* is_generic_covariant_impl) const {
#if defined(DART_DYNAMIC_MODULES)
if (is_declared_in_bytecode()) {
bytecode::BytecodeReader::ReadParameterCovariance(
*this, is_covariant, is_generic_covariant_impl);
return;
}
#endif
#if !defined(DART_PRECOMPILED_RUNTIME)
kernel::ReadParameterCovariance(*this, is_covariant,
is_generic_covariant_impl);
#endif
}
#endif
bool AbstractType::InstantiateAndTestSubtype(
@@ -4922,11 +5067,21 @@ ObjectPtr Instance::EvaluateCompiledExpression(
void Class::EnsureDeclarationLoaded() const {
if (!is_declaration_loaded()) {
#if defined(DART_DYNAMIC_MODULES)
// Loading of class declaration can be postponed until needed
// if class comes from bytecode.
if (is_declared_in_bytecode()) {
bytecode::BytecodeReader::LoadClassDeclaration(*this);
ASSERT(is_declaration_loaded());
ASSERT(is_type_finalized());
return;
}
#endif // defined(DART_DYNAMIC_MODULES)
#if defined(DART_PRECOMPILED_RUNTIME)
UNREACHABLE();
#else
FATAL("Unable to use class %s which is not loaded yet.", ToCString());
#endif
#endif // defined(DART_PRECOMPILED_RUNTIME)
}
}
@@ -4936,7 +5091,7 @@ ErrorPtr Class::EnsureIsFinalized(Thread* thread) const {
if (is_finalized()) {
return Error::null();
}
#if defined(DART_PRECOMPILED_RUNTIME)
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
UNREACHABLE();
return Error::null();
#else
@@ -4956,7 +5111,7 @@ ErrorPtr Class::EnsureIsFinalized(Thread* thread) const {
}
}
return error.ptr();
#endif // defined(DART_PRECOMPILED_RUNTIME)
#endif // defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
}
// Ensure that code outdated by finalized class is cleaned up, new instance of
@@ -5497,6 +5652,8 @@ const char* Class::GenerateUserVisibleName() const {
return Symbols::WeakArray().ToCString();
case kCodeCid:
return Symbols::Code().ToCString();
case kBytecodeCid:
return Symbols::Bytecode().ToCString();
case kInstructionsCid:
return Symbols::Instructions().ToCString();
case kInstructionsSectionCid:
@@ -5621,6 +5778,9 @@ uint32_t Class::Hash(ClassPtr obj) {
int32_t Class::SourceFingerprint() const {
#if !defined(DART_PRECOMPILED_RUNTIME)
if (is_declared_in_bytecode()) {
return 0;
}
return kernel::KernelSourceFingerprintHelper::CalculateClassFingerprint(
*this);
#else
@@ -5729,6 +5889,13 @@ void Class::set_is_loaded(bool value) const {
set_state_bits(IsLoadedBit::update(value, state_bits()));
}
#if defined(DART_DYNAMIC_MODULES)
void Class::set_is_declared_in_bytecode(bool value) const {
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
set_state_bits(IsDeclaredInBytecodeBit::update(value, state_bits()));
}
#endif // defined(DART_DYNAMIC_MODULES)
void Class::set_is_finalized() const {
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
ASSERT(!is_finalized());
@@ -7957,6 +8124,24 @@ bool Function::HasCode() const {
return untag()->code() != StubCode::LazyCompile().ptr();
}
#if defined(DART_DYNAMIC_MODULES)
void Function::AttachBytecode(const Bytecode& value) const {
ASSERT(IsolateGroup::Current()->program_lock()->IsCurrentThreadWriter());
ASSERT(!value.IsNull());
// Finish setting up code before activating it.
if (!value.InVMIsolateHeap()) {
value.set_function(*this);
}
ASSERT(untag()->ic_data_array_or_bytecode() == Object::null());
untag()->set_ic_data_array_or_bytecode(value.ptr());
// Set the code entry_point to InterpretCall stub.
SetInstructions(StubCode::InterpretCall());
}
#endif // defined(DART_DYNAMIC_MODULES)
bool Function::HasCode(FunctionPtr function) {
NoSafepointScope no_safepoint;
ASSERT(function->untag()->code() != Code::null());
@@ -8681,7 +8866,7 @@ StringPtr FunctionType::ParameterNameAt(intptr_t index) const {
void FunctionType::SetParameterNameAt(intptr_t index,
const String& value) const {
#if defined(DART_PRECOMPILED_RUNTIME)
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
UNREACHABLE();
#else
ASSERT(!value.IsNull() && value.IsSymbol());
@@ -8716,7 +8901,7 @@ void Function::CreateNameArray(Heap::Space space) const {
}
void FunctionType::CreateNameArrayIncludingFlags(Heap::Space space) const {
#if defined(DART_PRECOMPILED_RUNTIME)
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
UNREACHABLE();
#else
const intptr_t num_named_parameters = NumOptionalNamedParameters();
@@ -10281,7 +10466,7 @@ FunctionPtr Function::New(const FunctionType& signature,
ASSERT(!signature.IsNull());
const Function& result = Function::Handle(Function::New(space));
result.set_kind_tag(0);
result.set_packed_fields(0);
NOT_IN_PRECOMPILED(result.set_packed_fields(0));
result.set_name(name);
result.set_kind_tag(0); // Ensure determinism of uninitialized bits.
result.set_kind(kind);
@@ -10309,7 +10494,7 @@ FunctionPtr Function::New(const FunctionType& signature,
NOT_IN_PRECOMPILED(result.set_optimized_call_site_count(0));
NOT_IN_PRECOMPILED(result.set_inlining_depth(0));
NOT_IN_PRECOMPILED(result.set_kernel_offset(0));
result.set_is_optimizable(is_native ? false : true);
NOT_IN_PRECOMPILED(result.set_is_optimizable(is_native ? false : true));
result.set_is_inlinable(true);
result.reset_unboxed_parameters_and_return();
result.SetInstructionsSafe(StubCode::LazyCompile());
@@ -10414,7 +10599,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
return implicit_closure_function();
}
#if defined(DART_PRECOMPILED_RUNTIME)
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
// In AOT mode all implicit closures are pre-created.
FATAL("Cannot create implicit closure in AOT!");
return Function::null();
@@ -10434,6 +10619,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
zone, NewImplicitClosureFunction(closure_name, *this, token_pos()));
// Set closure function's context scope.
#if !defined(DART_PRECOMPILED_RUNTIME)
if (is_static() || IsConstructor()) {
closure_function.set_context_scope(Object::empty_context_scope());
} else {
@@ -10441,6 +10627,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
zone, LocalScope::CreateImplicitClosureScope(*this));
closure_function.set_context_scope(context_scope);
}
#endif
FunctionType& closure_signature =
FunctionType::Handle(zone, closure_function.signature());
@@ -10548,7 +10735,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
closure_signature.set_result_type(result_type);
// Set closure function's end token to this end token.
closure_function.set_end_token_pos(end_token_pos());
NOT_IN_PRECOMPILED(closure_function.set_end_token_pos(end_token_pos()));
// The closurized method stub just calls into the original method and should
// therefore be skipped by the debugger and in stack traces.
@@ -10571,21 +10758,22 @@ FunctionPtr Function::ImplicitClosureFunction() const {
has_opt_pos_params);
closure_signature.set_parameter_types(
Array::Handle(zone, Array::New(num_params, Heap::kOld)));
closure_function.CreateNameArray();
NOT_IN_PRECOMPILED(closure_function.CreateNameArray());
closure_signature.CreateNameArrayIncludingFlags();
AbstractType& param_type = AbstractType::Handle(zone);
String& param_name = String::Handle(zone);
// Add implicit closure object parameter.
param_type = Type::DynamicType();
closure_signature.SetParameterTypeAt(0, param_type);
closure_function.SetParameterNameAt(0, Symbols::ClosureParameter());
NOT_IN_PRECOMPILED(
closure_function.SetParameterNameAt(0, Symbols::ClosureParameter()));
for (int i = kClosure; i < num_pos_params; i++) {
param_type = ParameterTypeAt(num_implicit_params - kClosure + i);
transform_type(param_type);
closure_signature.SetParameterTypeAt(i, param_type);
param_name = ParameterNameAt(num_implicit_params - kClosure + i);
// Set the name in the function for positional parameters.
closure_function.SetParameterNameAt(i, param_name);
NOT_IN_PRECOMPILED(closure_function.SetParameterNameAt(i, param_name));
}
for (int i = num_pos_params; i < num_params; i++) {
param_type = ParameterTypeAt(num_implicit_params - kClosure + i);
@@ -10605,8 +10793,7 @@ FunctionPtr Function::ImplicitClosureFunction() const {
// Change covariant parameter types to Object?.
BitVector is_covariant(zone, NumParameters());
BitVector is_generic_covariant_impl(zone, NumParameters());
kernel::ReadParameterCovariance(*this, &is_covariant,
&is_generic_covariant_impl);
ReadParameterCovariance(&is_covariant, &is_generic_covariant_impl);
ObjectStore* object_store = IsolateGroup::Current()->object_store();
const auto& object_type =
@@ -10620,6 +10807,27 @@ FunctionPtr Function::ImplicitClosureFunction() const {
}
}
}
#if defined(DART_DYNAMIC_MODULES)
#if defined(DART_PRECOMPILED_RUNTIME)
const bool attach_bytecode = true;
#else
const bool attach_bytecode = is_declared_in_bytecode();
#endif
if (attach_bytecode) {
if (is_static()) {
closure_function.AttachBytecode(
Object::implicit_static_closure_bytecode());
} else if (IsConstructor()) {
closure_function.AttachBytecode(
Object::implicit_constructor_closure_bytecode());
} else {
closure_function.AttachBytecode(
Object::implicit_instance_closure_bytecode());
}
}
#endif
ASSERT(!closure_signature.IsFinalized());
closure_signature ^= ClassFinalizer::FinalizeType(closure_signature);
closure_function.SetSignature(closure_signature);
@@ -10874,9 +11082,17 @@ ClassPtr Function::Owner() const {
return PatchClass::Cast(obj).wrapped_class();
}
#if defined(DART_DYNAMIC_MODULES)
bool Function::is_declared_in_bytecode() const {
return Class::Handle(Owner()).is_declared_in_bytecode();
}
#endif
void Function::InheritKernelOffsetFrom(const Function& src) const {
#if defined(DART_PRECOMPILED_RUNTIME)
#if !defined(DART_DYNAMIC_MODULES)
UNREACHABLE();
#endif
#else
StoreNonPointer(&untag()->kernel_offset_, src.untag()->kernel_offset_);
#endif
@@ -10884,7 +11100,9 @@ void Function::InheritKernelOffsetFrom(const Function& src) const {
void Function::InheritKernelOffsetFrom(const Field& src) const {
#if defined(DART_PRECOMPILED_RUNTIME)
#if !defined(DART_DYNAMIC_MODULES)
UNREACHABLE();
#endif
#else
set_kernel_offset(src.kernel_offset());
#endif
@@ -10972,6 +11190,7 @@ intptr_t Function::KernelLibraryOffset() const {
}
intptr_t Function::KernelLibraryIndex() const {
ASSERT(!is_declared_in_bytecode());
if (IsNoSuchMethodDispatcher() || IsInvokeFieldDispatcher() ||
IsFfiCallbackTrampoline()) {
return -1;
@@ -11194,6 +11413,9 @@ StringPtr Function::GetSource() const {
// arguments.
int32_t Function::SourceFingerprint() const {
#if !defined(DART_PRECOMPILED_RUNTIME)
if (is_declared_in_bytecode()) {
return 0;
}
return kernel::KernelSourceFingerprintHelper::CalculateFunctionFingerprint(
*this);
#else
@@ -11292,11 +11514,22 @@ ArrayPtr Function::GetCoverageArray() const {
}
void Function::set_ic_data_array(const Array& value) const {
untag()->set_ic_data_array<std::memory_order_release>(value.ptr());
#if defined(DART_DYNAMIC_MODULES)
ASSERT(!HasBytecode());
#endif
untag()->set_ic_data_array_or_bytecode<std::memory_order_release>(
value.ptr());
}
ArrayPtr Function::ic_data_array() const {
return untag()->ic_data_array<std::memory_order_acquire>();
ObjectPtr value =
untag()->ic_data_array_or_bytecode<std::memory_order_acquire>();
#if defined(DART_DYNAMIC_MODULES)
if (value->IsBytecode()) {
return Array::null();
}
#endif
return Array::RawCast(value);
}
void Function::ClearICDataArray() const {
@@ -11350,6 +11583,8 @@ bool Function::CheckSourceFingerprint(int32_t fp, const char* kind) const {
return true; // The kernel structure has been altered, skip checking.
}
ASSERT(!is_declared_in_bytecode());
if (SourceFingerprint() != fp) {
// This output can be copied into a file, then used with sed
// to replace the old values.
@@ -11483,6 +11718,12 @@ bool Function::PrologueNeedsArgumentsDescriptor() const {
if (HasSavedArgumentsDescriptor()) {
return false;
}
#if defined(DART_DYNAMIC_MODULES)
// Entering interpreter needs arguments descriptor.
if (is_declared_in_bytecode()) {
return true;
}
#endif
// The prologue of those functions need to examine the arg descriptor for
// various purposes.
return IsGeneric() || HasOptionalParameters();
@@ -11938,6 +12179,12 @@ uint32_t Field::Hash() const {
return String::HashRawSymbol(name());
}
#if defined(DART_DYNAMIC_MODULES)
bool Field::is_declared_in_bytecode() const {
return Class::Handle(Owner()).is_declared_in_bytecode();
}
#endif
void Field::InheritKernelOffsetFrom(const Field& src) const {
#if defined(DART_PRECOMPILED_RUNTIME)
UNREACHABLE();
@@ -12110,6 +12357,9 @@ FieldPtr Field::Clone(const Field& original) const {
int32_t Field::SourceFingerprint() const {
#if !defined(DART_PRECOMPILED_RUNTIME)
if (is_declared_in_bytecode()) {
return 0;
}
return kernel::KernelSourceFingerprintHelper::CalculateFieldFingerprint(
*this);
#else
@@ -12343,7 +12593,7 @@ FunctionPtr Field::EnsureInitializerFunction() const {
Zone* zone = thread->zone();
Function& initializer = Function::Handle(zone, InitializerFunction());
if (initializer.IsNull()) {
#if defined(DART_PRECOMPILED_RUNTIME)
#if defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_DYNAMIC_MODULES)
UNREACHABLE();
#else
SafepointMutexLocker ml(
@@ -12351,17 +12601,82 @@ FunctionPtr Field::EnsureInitializerFunction() const {
// Double check after grabbing the lock.
initializer = InitializerFunction();
if (initializer.IsNull()) {
initializer = kernel::CreateFieldInitializerFunction(thread, zone, *this);
initializer = CreateFieldInitializerFunction(thread);
}
#endif
}
return initializer.ptr();
}
void Field::SetInitializerFunction(const Function& initializer) const {
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
FunctionPtr Field::CreateFieldInitializerFunction(Thread* thread) const {
Zone* zone = thread->zone();
ASSERT(InitializerFunction() == Function::null());
String& init_name = String::Handle(zone, name());
init_name = Symbols::FromConcat(thread, Symbols::InitPrefix(), init_name);
const auto& field_owner = Class::Handle(zone, Owner());
#if defined(DART_PRECOMPILED_RUNTIME)
UNREACHABLE();
const auto& initializer_owner = Class::Handle(zone, field_owner.ptr());
#else
// Static field initializers are not added as members of their owning class,
// so they must be preemptively given a patch class to avoid the meaning of
// their kernel/token position changing during a reload. Compare
// Class::PatchFieldsAndFunctions().
// This might also be necessary for lazy computation of local var descriptors.
// Compare https://codereview.chromium.org//1317753004
const auto& script = Script::Handle(zone, Script());
const auto& kernel_program_info =
KernelProgramInfo::Handle(zone, KernelProgramInfo());
const auto& initializer_owner = PatchClass::Handle(
zone, PatchClass::New(field_owner, kernel_program_info, script));
if (!is_declared_in_bytecode()) {
const Library& lib = Library::Handle(zone, field_owner.library());
initializer_owner.set_kernel_library_index(lib.kernel_library_index());
}
#endif
// Create a static initializer.
FunctionType& signature = FunctionType::Handle(zone, FunctionType::New());
const Function& initializer_fun = Function::Handle(
zone,
Function::New(signature, init_name, UntaggedFunction::kFieldInitializer,
is_static(), // is_static
false, // is_const
false, // is_abstract
false, // is_external
false, // is_native
initializer_owner, TokenPosition::kNoSource));
if (!is_static()) {
signature.set_num_fixed_parameters(1);
signature.set_parameter_types(
Array::Handle(zone, Array::New(1, Heap::kOld)));
signature.SetParameterTypeAt(
0, AbstractType::Handle(zone, field_owner.DeclarationType()));
NOT_IN_PRECOMPILED(initializer_fun.CreateNameArray());
NOT_IN_PRECOMPILED(initializer_fun.SetParameterNameAt(0, Symbols::This()));
}
signature.set_result_type(AbstractType::Handle(zone, type()));
initializer_fun.set_is_reflectable(false);
initializer_fun.set_is_inlinable(false);
NOT_IN_PRECOMPILED(initializer_fun.set_token_pos(token_pos()));
NOT_IN_PRECOMPILED(initializer_fun.set_end_token_pos(end_token_pos()));
initializer_fun.set_accessor_field(*this);
initializer_fun.InheritKernelOffsetFrom(*this);
initializer_fun.set_is_extension_member(is_extension_member());
initializer_fun.set_is_extension_type_member(is_extension_type_member());
signature ^= ClassFinalizer::FinalizeType(signature);
initializer_fun.SetSignature(signature);
SetInitializerFunction(initializer_fun);
return initializer_fun.ptr();
}
void Field::SetInitializerFunction(const Function& initializer) const {
ASSERT(IsOriginal());
ASSERT(IsolateGroup::Current()
->initializer_functions_mutex()
@@ -12371,9 +12686,10 @@ void Field::SetInitializerFunction(const Function& initializer) const {
// accessed without grabbing the lock.
untag()->set_initializer_function<std::memory_order_release>(
initializer.ptr());
#endif
}
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
bool Field::HasInitializerFunction() const {
return untag()->initializer_function() != Function::null();
}
@@ -18513,6 +18829,196 @@ void Code::DumpSourcePositions(bool relative_addresses) const {
reader.DumpSourcePositions(relative_addresses ? 0 : PayloadStart());
}
void Bytecode::Disassemble(DisassemblyFormatter* formatter) const {
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
#if defined(DART_DYNAMIC_MODULES)
if (!FLAG_support_disassembler) {
return;
}
uword start = PayloadStart();
intptr_t size = Size();
if (formatter == NULL) {
KernelBytecodeDisassembler::Disassemble(start, start + size, *this);
} else {
KernelBytecodeDisassembler::Disassemble(start, start + size, formatter,
*this);
}
#endif // defined(DART_DYNAMIC_MODULES)
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
}
BytecodePtr Bytecode::New(uword instructions,
intptr_t instructions_size,
intptr_t instructions_offset,
const TypedDataBase& binary,
const ObjectPool& object_pool) {
ASSERT(Object::bytecode_class() != Class::null());
Bytecode& result = Bytecode::Handle();
{
auto raw = Object::Allocate<Bytecode>(Heap::kOld);
NoSafepointScope no_safepoint;
result = raw;
result.set_instructions(instructions);
result.set_instructions_size(instructions_size);
result.set_object_pool(object_pool);
result.set_pc_descriptors(Object::empty_descriptors());
result.set_binary(binary);
result.set_instructions_binary_offset(instructions_offset);
result.set_code_offset(0);
result.set_source_positions_binary_offset(0);
}
return result.ptr();
}
TokenPosition Bytecode::GetTokenIndexOfPC(uword return_address) const {
#if defined(DART_DYNAMIC_MODULES)
if (!HasSourcePositions()) {
return TokenPosition::kNoSource;
}
Zone* zone = Thread::Current()->zone();
uword pc_offset = return_address - PayloadStart();
// pc_offset could equal to bytecode size if the last instruction is Throw.
ASSERT(pc_offset <= static_cast<uword>(Size()));
bytecode::BytecodeSourcePositionsIterator iter(zone, *this);
TokenPosition token_pos = TokenPosition::kNoSource;
while (iter.MoveNext()) {
if (pc_offset <= iter.PcOffset()) {
break;
}
token_pos = iter.TokenPos();
}
return token_pos;
#else
UNREACHABLE();
#endif
}
intptr_t Bytecode::GetTryIndexAtPc(uword return_address) const {
#if defined(DART_DYNAMIC_MODULES)
intptr_t try_index = -1;
const uword pc_offset = return_address - PayloadStart();
const PcDescriptors& descriptors = PcDescriptors::Handle(pc_descriptors());
PcDescriptors::Iterator iter(descriptors, UntaggedPcDescriptors::kAnyKind);
while (iter.MoveNext()) {
// PC descriptors for try blocks in bytecode are generated in pairs,
// marking start and end of a try block.
// See BytecodeReaderHelper::ReadExceptionsTable for details.
const intptr_t current_try_index = iter.TryIndex();
const uword start_pc = iter.PcOffset();
if (pc_offset < start_pc) {
break;
}
const bool has_next = iter.MoveNext();
ASSERT(has_next);
const uword end_pc = iter.PcOffset();
if (start_pc <= pc_offset && pc_offset < end_pc) {
ASSERT(try_index < current_try_index);
try_index = current_try_index;
}
}
return try_index;
#else
UNREACHABLE();
#endif
}
uword Bytecode::GetFirstDebugCheckOpcodePc() const {
#if defined(DART_DYNAMIC_MODULES)
uword pc = PayloadStart();
const uword end_pc = pc + Size();
while (pc < end_pc) {
if (KernelBytecode::IsDebugCheckOpcode(
reinterpret_cast<const KBCInstr*>(pc))) {
return pc;
}
pc = KernelBytecode::Next(pc);
}
return 0;
#else
UNREACHABLE();
#endif
}
uword Bytecode::GetDebugCheckedOpcodeReturnAddress(uword from_offset,
uword to_offset) const {
#if defined(DART_DYNAMIC_MODULES)
uword pc = PayloadStart() + from_offset;
const uword end_pc = pc + (to_offset - from_offset);
while (pc < end_pc) {
uword next_pc = KernelBytecode::Next(pc);
if (KernelBytecode::IsDebugCheckedOpcode(
reinterpret_cast<const KBCInstr*>(pc))) {
// Return the pc after the opcode, i.e. its 'return address'.
return next_pc;
}
pc = next_pc;
}
return 0;
#else
UNREACHABLE();
#endif
}
const char* Bytecode::ToCString() const {
return Thread::Current()->zone()->PrintToString("Bytecode(%s)",
QualifiedName());
}
static const char* BytecodeStubName(const Bytecode& bytecode) {
if (bytecode.ptr() == Object::implicit_getter_bytecode().ptr()) {
return "[Bytecode Stub] VMInternal_ImplicitGetter";
} else if (bytecode.ptr() == Object::implicit_setter_bytecode().ptr()) {
return "[Bytecode Stub] VMInternal_ImplicitSetter";
} else if (bytecode.ptr() ==
Object::implicit_static_getter_bytecode().ptr()) {
return "[Bytecode Stub] VMInternal_ImplicitStaticGetter";
} else if (bytecode.ptr() == Object::method_extractor_bytecode().ptr()) {
return "[Bytecode Stub] VMInternal_MethodExtractor";
} else if (bytecode.ptr() == Object::invoke_closure_bytecode().ptr()) {
return "[Bytecode Stub] VMInternal_InvokeClosure";
} else if (bytecode.ptr() == Object::invoke_field_bytecode().ptr()) {
return "[Bytecode Stub] VMInternal_InvokeField";
}
return "[unknown stub]";
}
const char* Bytecode::Name() const {
Zone* zone = Thread::Current()->zone();
const Function& fun = Function::Handle(zone, function());
if (fun.IsNull()) {
return BytecodeStubName(*this);
}
const char* function_name =
String::Handle(zone, fun.UserVisibleName()).ToCString();
return zone->PrintToString("[Bytecode] %s", function_name);
}
const char* Bytecode::QualifiedName() const {
Zone* zone = Thread::Current()->zone();
const Function& fun = Function::Handle(zone, function());
if (fun.IsNull()) {
return BytecodeStubName(*this);
}
const char* function_name =
String::Handle(zone, fun.QualifiedScrubbedName()).ToCString();
return zone->PrintToString("[Bytecode] %s", function_name);
}
const char* Bytecode::FullyQualifiedName() const {
Zone* zone = Thread::Current()->zone();
const Function& fun = Function::Handle(zone, function());
if (fun.IsNull()) {
return BytecodeStubName(*this);
}
const char* function_name = fun.ToFullyQualifiedCString();
return zone->PrintToString("[Bytecode] %s", function_name);
}
void Bytecode::set_binary(const TypedDataBase& binary) const {
ASSERT(binary.IsNull() || binary.IsExternalOrExternalView());
untag()->set_binary(binary.ptr());
}
intptr_t Context::GetLevel() const {
intptr_t level = 0;
Context& parent_ctx = Context::Handle(parent());
@@ -26389,6 +26895,26 @@ const char* StackTrace::ToCString() const {
}
const uword pc_offset = stack_trace.PcOffsetAtFrame(i);
// A visible frame ends any gap we might be in.
in_gap = false;
#if defined(DART_DYNAMIC_MODULES)
if (code_object.IsBytecode()) {
const auto& bytecode = Bytecode::Cast(code_object);
function = bytecode.function();
if (!function.IsNull() &&
(FLAG_show_invisible_frames || function.is_visible())) {
auto const pos =
bytecode.GetTokenIndexOfPC(bytecode.PayloadStart() + pc_offset);
PrintSymbolicStackFrame(zone, &buffer, function, pos, frame_index);
frame_index++;
}
continue;
}
#endif // defined(DART_DYNAMIC_MODULES)
ASSERT(code_object.IsCode());
code ^= code_object.ptr();
ASSERT(code.IsFunctionCode());
@@ -26403,9 +26929,6 @@ const char* StackTrace::ToCString() const {
const bool is_future_listener =
pc_offset == StackTraceUtils::kFutureListenerPcOffset;
// A visible frame ends any gap we might be in.
in_gap = false;
#if defined(DART_PRECOMPILED_RUNTIME)
// When printing non-symbolic frames, we normally print call
// addresses, not return addresses, by subtracting one from the PC to
+190 -2
View File
@@ -56,6 +56,7 @@ CLASS_LIST(DEFINE_FORWARD_DECLARATION)
#undef DEFINE_FORWARD_DECLARATION
class Api;
class ArgumentsDescriptor;
class BitVector;
class Closure;
class Code;
class DeoptInstr;
@@ -473,6 +474,17 @@ class Object {
V(ExceptionHandlers, empty_async_exception_handlers) \
V(Array, synthetic_getter_parameter_types) \
V(Array, synthetic_getter_parameter_names) \
V(Bytecode, implicit_getter_bytecode) \
V(Bytecode, implicit_setter_bytecode) \
V(Bytecode, implicit_static_getter_bytecode) \
V(Bytecode, method_extractor_bytecode) \
V(Bytecode, invoke_closure_bytecode) \
V(Bytecode, invoke_field_bytecode) \
V(Bytecode, nsm_dispatcher_bytecode) \
V(Bytecode, dynamic_invocation_forwarder_bytecode) \
V(Bytecode, implicit_static_closure_bytecode) \
V(Bytecode, implicit_instance_closure_bytecode) \
V(Bytecode, implicit_constructor_closure_bytecode) \
V(Sentinel, sentinel) \
V(Sentinel, unknown_constant) \
V(Sentinel, non_constant) \
@@ -542,6 +554,7 @@ class Object {
}
static ClassPtr context_class() { return context_class_; }
static ClassPtr context_scope_class() { return context_scope_class_; }
static ClassPtr bytecode_class() { return bytecode_class_; }
static ClassPtr sentinel_class() { return sentinel_class_; }
static ClassPtr api_error_class() { return api_error_class_; }
static ClassPtr language_error_class() { return language_error_class_; }
@@ -989,6 +1002,7 @@ class Object {
static ClassPtr exception_handlers_class_;
static ClassPtr context_class_;
static ClassPtr context_scope_class_;
static ClassPtr bytecode_class_;
static ClassPtr sentinel_class_;
static ClassPtr singletargetcache_class_;
static ClassPtr unlinkedcall_class_;
@@ -1012,6 +1026,7 @@ class Object {
friend void UntaggedObject::Validate(IsolateGroup* isolate_group) const;
friend class Closure;
friend class InstanceDeserializationCluster;
friend class Interpreter;
friend class ObjectGraphCopier; // For Object::InitializeObject
friend class Simd128MessageDeserializationCluster;
friend class OneByteString;
@@ -1787,6 +1802,15 @@ class Class : public Object {
bool is_loaded() const { return IsLoadedBit::decode(state_bits()); }
void set_is_loaded(bool value) const;
#if defined(DART_DYNAMIC_MODULES)
bool is_declared_in_bytecode() const {
return IsDeclaredInBytecodeBit::decode(state_bits());
}
void set_is_declared_in_bytecode(bool value) const;
#else
bool is_declared_in_bytecode() const { return false; }
#endif // defined(DART_DYNAMIC_MODULES)
uint16_t num_native_fields() const { return untag()->num_native_fields_; }
void set_num_native_fields(uint16_t value) const {
StoreNonPointer(&untag()->num_native_fields_, value);
@@ -2075,6 +2099,8 @@ class Class : public Object {
kIsDynamicallyExtendableBit,
// This class has a dynamically extendable subtype.
kHasDynamicallyExtendableSubtypesBit,
// This class was loaded from bytecode at runtime.
kIsDeclaredInBytecodeBit,
};
class ConstBit : public BitField<uint32_t, bool, kConstBit, 1> {};
class ImplementedBit : public BitField<uint32_t, bool, kImplementedBit, 1> {};
@@ -2120,6 +2146,8 @@ class Class : public Object {
bool,
kHasDynamicallyExtendableSubtypesBit,
1> {};
class IsDeclaredInBytecodeBit
: public BitField<uint32_t, bool, kIsDeclaredInBytecodeBit, 1> {};
void set_name(const String& value) const;
void set_user_name(const String& value) const;
@@ -2240,6 +2268,7 @@ class Class : public Object {
friend class Instance;
friend class Object;
friend class Type;
friend class InterpreterHelpers;
friend class Intrinsifier;
friend class ProgramWalker;
friend class Precompiler;
@@ -2920,6 +2949,7 @@ class ICData : public CallSiteData {
friend class Class;
friend class VMDeserializationRoots;
friend class ICDataTestTask;
friend class Interpreter;
friend class VMSerializationRoots;
};
@@ -3231,6 +3261,16 @@ class Function : public Object {
return OFFSET_OF(UntaggedFunction, unchecked_entry_point_);
}
#if defined(DART_DYNAMIC_MODULES)
void AttachBytecode(const Bytecode& bytecode) const;
inline BytecodePtr GetBytecode() const;
static inline BytecodePtr GetBytecode(FunctionPtr function);
inline bool HasBytecode() const;
static inline bool HasBytecode(FunctionPtr function);
#else
inline bool HasBytecode() const { return false; }
#endif
virtual uword Hash() const;
// Returns true if there is at least one debugger breakpoint
@@ -3562,6 +3602,12 @@ class Function : public Object {
#undef DEFINE_GETTERS_AND_SETTERS
#if defined(DART_DYNAMIC_MODULES)
bool is_declared_in_bytecode() const;
#else
bool is_declared_in_bytecode() const { return false; }
#endif // defined(DART_DYNAMIC_MODULES)
intptr_t kernel_offset() const {
#if defined(DART_PRECOMPILED_RUNTIME)
return 0;
@@ -4042,11 +4088,19 @@ class Function : public Object {
static StringPtr CreateDynamicInvocationForwarderName(const String& name);
#if !defined(DART_PRECOMPILED_RUNTIME)
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
FunctionPtr CreateDynamicInvocationForwarder(
const String& mangled_name) const;
FunctionPtr GetDynamicInvocationForwarder(const String& mangled_name) const;
// Fills in [is_covariant] and [is_generic_covariant_impl] vectors
// according to covariance attributes of function parameters.
//
// [is_covariant] and [is_generic_covariant_impl] should contain bitvectors
// of function.NumParameters() length.
void ReadParameterCovariance(BitVector* is_covariant,
BitVector* is_generic_covariant_impl) const;
#endif
// Slow function, use in asserts to track changes in important library
@@ -4507,6 +4561,12 @@ class Field : public Object {
}
bool is_shared() const { return SharedBit::decode(kind_bits()); }
#if defined(DART_DYNAMIC_MODULES)
bool is_declared_in_bytecode() const;
#else
bool is_declared_in_bytecode() const { return false; }
#endif // defined(DART_DYNAMIC_MODULES)
intptr_t kernel_offset() const {
#if defined(DART_PRECOMPILED_RUNTIME)
return 0;
@@ -4819,12 +4879,16 @@ class Field : public Object {
FunctionPtr InitializerFunction() const {
return untag()->initializer_function<std::memory_order_acquire>();
}
void SetInitializerFunction(const Function& initializer) const;
bool HasInitializerFunction() const;
static intptr_t initializer_function_offset() {
return OFFSET_OF(UntaggedField, initializer_function_);
}
#if !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
FunctionPtr CreateFieldInitializerFunction(Thread* thread) const;
void SetInitializerFunction(const Function& initializer) const;
#endif // !defined(DART_PRECOMPILED_RUNTIME) || defined(DART_DYNAMIC_MODULES)
// For static fields only. Constructs a closure that gets/sets the
// field value.
InstancePtr GetterClosure() const;
@@ -4858,6 +4922,7 @@ class Field : public Object {
const Object& owner,
TokenPosition token_pos,
TokenPosition end_token_pos);
friend class Interpreter; // Access to bit field.
friend class StoreFieldInstr; // Generated code access to bit field.
enum {
@@ -7418,6 +7483,109 @@ class Code : public Object {
friend void DumpStackFrame(intptr_t frame_index, uword pc, uword fp);
};
class Bytecode : public Object {
public:
uword instructions() const { return untag()->instructions_; }
uword PayloadStart() const { return instructions(); }
intptr_t Size() const { return untag()->instructions_size_; }
ObjectPoolPtr object_pool() const { return untag()->object_pool(); }
bool ContainsInstructionAt(uword addr) const {
return UntaggedBytecode::ContainsPC(ptr(), addr);
}
PcDescriptorsPtr pc_descriptors() const { return untag()->pc_descriptors(); }
void set_pc_descriptors(const PcDescriptors& descriptors) const {
ASSERT(descriptors.IsOld());
untag()->set_pc_descriptors(descriptors.ptr());
}
void Disassemble(DisassemblyFormatter* formatter = NULL) const;
ExceptionHandlersPtr exception_handlers() const {
return untag()->exception_handlers();
}
void set_exception_handlers(const ExceptionHandlers& handlers) const {
ASSERT(handlers.IsOld());
untag()->set_exception_handlers(handlers.ptr());
}
FunctionPtr function() const { return untag()->function(); }
void set_function(const Function& function) const {
ASSERT(function.IsOld());
untag()->set_function(function.ptr());
}
TypedDataBasePtr binary() const { return untag()->binary(); }
static intptr_t InstanceSize() {
return RoundedAllocationSize(sizeof(UntaggedBytecode));
}
static BytecodePtr New(uword instructions,
intptr_t instructions_size,
intptr_t instructions_offset,
const TypedDataBase& binary,
const ObjectPool& object_pool);
TokenPosition GetTokenIndexOfPC(uword return_address) const;
intptr_t GetTryIndexAtPc(uword return_address) const;
// Return the pc of the first 'DebugCheck' opcode of the bytecode.
// Return 0 if none is found.
uword GetFirstDebugCheckOpcodePc() const;
// Return the pc after the first 'debug checked' opcode in the range.
// Return 0 if none is found.
uword GetDebugCheckedOpcodeReturnAddress(uword from_offset,
uword to_offset) const;
intptr_t instructions_binary_offset() const {
return untag()->instructions_binary_offset_;
}
void set_instructions_binary_offset(intptr_t value) const {
StoreNonPointer(&untag()->instructions_binary_offset_, value);
}
intptr_t code_offset() const { return untag()->code_offset_; }
void set_code_offset(intptr_t value) const {
StoreNonPointer(&untag()->code_offset_, value);
}
intptr_t source_positions_binary_offset() const {
return untag()->source_positions_binary_offset_;
}
void set_source_positions_binary_offset(intptr_t value) const {
StoreNonPointer(&untag()->source_positions_binary_offset_, value);
}
bool HasSourcePositions() const {
return (source_positions_binary_offset() != 0);
}
const char* Name() const;
const char* QualifiedName() const;
const char* FullyQualifiedName() const;
private:
void set_instructions(uword instructions) const {
StoreNonPointer(&untag()->instructions_, instructions);
}
void set_instructions_size(intptr_t size) const {
StoreNonPointer(&untag()->instructions_size_, size);
}
void set_object_pool(const ObjectPool& object_pool) const {
untag()->set_object_pool(object_pool.ptr());
}
void set_binary(const TypedDataBase& binary) const;
FINAL_HEAP_OBJECT_IMPLEMENTATION(Bytecode, Object);
friend class BytecodeDeserializationCluster;
friend class Class;
friend class SnapshotWriter;
};
class Context : public Object {
public:
ContextPtr parent() const { return untag()->parent(); }
@@ -8455,6 +8623,7 @@ class Instance : public Object {
friend class TypedDataView;
friend class InstanceSerializationCluster;
friend class InstanceDeserializationCluster;
friend class Interpreter;
friend class ClassDeserializationCluster; // vtable
friend class InstanceMorpher;
friend class Obfuscator; // RawGetFieldAtOffset, RawSetFieldAtOffset
@@ -11044,6 +11213,7 @@ class Array : public Instance {
FINAL_HEAP_OBJECT_IMPLEMENTATION(Array, Instance);
friend class Class;
friend class ImmutableArray;
friend class Interpreter;
friend class Object;
friend class String;
friend class MessageDeserializer;
@@ -13255,6 +13425,24 @@ void Object::setPtr(ObjectPtr value, intptr_t default_cid) {
set_vtable(builtin_vtables_[cid]);
}
#if defined(DART_DYNAMIC_MODULES)
BytecodePtr Function::GetBytecode() const {
return GetBytecode(ptr());
}
BytecodePtr Function::GetBytecode(FunctionPtr function) {
return Bytecode::RawCast(function.untag()->ic_data_array_or_bytecode());
}
bool Function::HasBytecode() const {
return HasBytecode(ptr());
}
bool Function::HasBytecode(FunctionPtr function) {
return function.untag()->ic_data_array_or_bytecode()->IsBytecode();
}
#endif // defined(DART_DYNAMIC_MODULES)
intptr_t Field::HostOffset() const {
ASSERT(is_instance()); // Valid only for dart instance fields.
return (Smi::Value(untag()->host_offset_or_field_id()) * kCompressedWordSize);
+1
View File
@@ -33,6 +33,7 @@
V(CodeSourceMap) \
V(CompressedStackMaps) \
V(ContextScope) \
V(Bytecode) \
V(DynamicLibrary) \
V(Error) \
V(ExceptionHandlers) \
+48
View File
@@ -1084,6 +1084,54 @@ void Code::PrintJSONImpl(JSONStream* stream, bool ref) const {
void Code::PrintImplementationFieldsImpl(const JSONArray& jsarr_fields) const {}
void Bytecode::PrintJSONImpl(JSONStream* stream, bool ref) const {
// N.B. This is polymorphic with Code.
JSONObject jsobj(stream);
AddCommonObjectProperties(&jsobj, "Code", ref);
int64_t compile_timestamp = 0;
jsobj.AddFixedServiceId("code/%" Px64 "-%" Px "", compile_timestamp,
PayloadStart());
const char* qualified_name = QualifiedName();
const char* vm_name = Name();
AddNameProperties(&jsobj, qualified_name, vm_name);
jsobj.AddProperty("kind", "Dart");
jsobj.AddProperty("_optimized", false);
jsobj.AddProperty("_intrinsic", false);
jsobj.AddProperty("_native", false);
if (ref) {
return;
}
const Function& fun = Function::Handle(function());
jsobj.AddProperty("function", fun);
jsobj.AddPropertyF("_startAddress", "%" Px "", PayloadStart());
jsobj.AddPropertyF("_endAddress", "%" Px "", PayloadStart() + Size());
jsobj.AddProperty("_alive", true);
const ObjectPool& obj_pool = ObjectPool::Handle(object_pool());
jsobj.AddProperty("_objectPool", obj_pool);
{
JSONArray jsarr(&jsobj, "_disassembly");
DisassembleToJSONStream formatter(jsarr);
Disassemble(&formatter);
}
const PcDescriptors& descriptors = PcDescriptors::Handle(pc_descriptors());
if (!descriptors.IsNull()) {
JSONObject desc(&jsobj, "_descriptors");
descriptors.PrintToJSONObject(&desc, false);
}
{
JSONArray inlined_functions(&jsobj, "_inlinedFunctions");
}
{
JSONArray inline_intervals(&jsobj, "_inlinedIntervals");
}
}
void Bytecode::PrintImplementationFieldsImpl(
const JSONArray& jsarr_fields) const {}
void Context::PrintJSONImpl(JSONStream* stream, bool ref) const {
JSONObject jsobj(stream);
// TODO(turnidge): Should the user level type for Context be Context
+11
View File
@@ -525,6 +525,7 @@ COMPRESSED_VISITOR(TypeParameter)
COMPRESSED_VISITOR(Function)
COMPRESSED_VISITOR(Closure)
COMPRESSED_VISITOR(LibraryPrefix)
COMPRESSED_VISITOR(Bytecode)
REGULAR_VISITOR(SingleTargetCache)
REGULAR_VISITOR(UnlinkedCall)
NULL_VISITOR(MonomorphicSmiableCall)
@@ -681,6 +682,16 @@ intptr_t UntaggedCode::VisitCodePointers(CodePtr raw_obj,
#endif
}
bool UntaggedBytecode::ContainsPC(ObjectPtr raw_obj, uword pc) {
if (raw_obj->IsBytecode()) {
BytecodePtr raw_bytecode = static_cast<BytecodePtr>(raw_obj);
uword start = raw_bytecode->untag()->instructions_;
uword size = raw_bytecode->untag()->instructions_size_;
return (pc - start) <= size; // pc may point past last instruction.
}
return false;
}
intptr_t UntaggedObjectPool::VisitObjectPoolPointers(
ObjectPoolPtr raw_obj,
ObjectPointerVisitor* visitor) {
+53 -2
View File
@@ -836,6 +836,8 @@ class UntaggedObject {
friend class Instance; // StorePointer
friend class StackFrame; // GetCodeObject assertion.
friend class CodeLookupTableBuilder; // profiler
friend class Interpreter;
friend class InterpreterHelpers;
friend class ObjectLocator;
friend class WriteBarrierUpdateVisitor; // CheckHeapPointerStore
friend class OffsetsTable;
@@ -1179,6 +1181,8 @@ class UntaggedClass : public UntaggedObject {
#endif // !defined(DART_PRECOMPILED_RUNTIME)
friend class Instance;
friend class Interpreter;
friend class InterpreterHelpers;
friend class IsolateGroup;
friend class Object;
friend class UntaggedInstance;
@@ -1387,6 +1391,8 @@ class UntaggedFunction : public UntaggedObject {
private:
friend class Class;
friend class Interpreter;
friend class InterpreterHelpers;
friend class UnitDeserializationRoots;
RAW_HEAP_OBJECT_IMPLEMENTATION(Function);
@@ -1416,8 +1422,8 @@ class UntaggedFunction : public UntaggedObject {
UNREACHABLE();
return nullptr;
}
// ICData of unoptimized code.
COMPRESSED_POINTER_FIELD(ArrayPtr, ic_data_array);
// ICData of unoptimized code or Bytecode.
COMPRESSED_POINTER_FIELD(ObjectPtr, ic_data_array_or_bytecode);
// Currently active code. Accessed from generated code.
COMPRESSED_POINTER_FIELD(CodePtr, code);
#if defined(DART_PRECOMPILED_RUNTIME)
@@ -1607,6 +1613,8 @@ class UntaggedField : public UntaggedObject {
#endif // !defined(DART_PRECOMPILED_RUNTIME)
friend class CidRewriteVisitor;
friend class Interpreter;
friend class InterpreterHelpers;
friend class GuardFieldClassInstr; // For sizeof(guarded_cid_/...)
friend class LoadFieldInstr; // For sizeof(guarded_cid_/...)
friend class StoreFieldInstr; // For sizeof(guarded_cid_/...)
@@ -1964,6 +1972,36 @@ class UntaggedCode : public UntaggedObject {
friend class CallSiteResetter;
};
class UntaggedBytecode : public UntaggedObject {
RAW_HEAP_OBJECT_IMPLEMENTATION(Bytecode);
uword instructions_;
intptr_t instructions_size_;
COMPRESSED_POINTER_FIELD(ObjectPoolPtr, object_pool);
VISIT_FROM(object_pool);
COMPRESSED_POINTER_FIELD(FunctionPtr, function);
COMPRESSED_POINTER_FIELD(ArrayPtr, closures);
COMPRESSED_POINTER_FIELD(TypedDataBasePtr, binary);
COMPRESSED_POINTER_FIELD(ExceptionHandlersPtr, exception_handlers);
COMPRESSED_POINTER_FIELD(PcDescriptorsPtr, pc_descriptors);
VISIT_TO(pc_descriptors);
ObjectPtr* to_snapshot(Snapshot::Kind kind) {
return reinterpret_cast<ObjectPtr*>(&pc_descriptors_);
}
int32_t instructions_binary_offset_;
int32_t code_offset_;
int32_t source_positions_binary_offset_;
static bool ContainsPC(ObjectPtr raw_obj, uword pc);
friend class Function;
friend class Interpreter;
friend class StackFrame;
};
class UntaggedObjectPool : public UntaggedObject {
RAW_HEAP_OBJECT_IMPLEMENTATION(ObjectPool);
@@ -1988,6 +2026,7 @@ class UntaggedObjectPool : public UntaggedObject {
friend class Object;
friend class CodeSerializationCluster;
friend class Interpreter;
friend class UnitSerializationRoots;
friend class UnitDeserializationRoots;
};
@@ -2016,6 +2055,7 @@ class UntaggedInstructions : public UntaggedObject {
friend class Function;
friend class ImageReader;
friend class ImageWriter;
friend class Interpreter;
friend class AssemblyImageWriter;
friend class BlobImageWriter;
};
@@ -2434,6 +2474,7 @@ class UntaggedContext : public UntaggedObject {
COMPRESSED_VARIABLE_POINTER_FIELDS(ObjectPtr, element, data)
friend class Object;
friend class Interpreter;
friend void UpdateLengthField(intptr_t,
ObjectPtr,
ObjectPtr); // num_variables_
@@ -2613,6 +2654,8 @@ class UntaggedSubtypeTestCache : public UntaggedObject {
VISIT_TO(cache)
uint32_t num_inputs_;
uint32_t num_occupied_;
friend class Interpreter;
};
class UntaggedLoadingUnit : public UntaggedObject {
@@ -2741,6 +2784,7 @@ class UntaggedTypeArguments : public UntaggedInstance {
COMPRESSED_VARIABLE_POINTER_FIELDS(AbstractTypePtr, element, types)
friend class Object;
friend class Interpreter;
};
class UntaggedTypeParameters : public UntaggedObject {
@@ -2797,6 +2841,7 @@ class UntaggedAbstractType : public UntaggedInstance {
private:
RAW_HEAP_OBJECT_IMPLEMENTATION(AbstractType);
friend class Interpreter;
friend class ObjectStore;
friend class StubCode;
};
@@ -2961,6 +3006,7 @@ class UntaggedClosure : public UntaggedInstance {
CompressedObjectPtr* to_snapshot(Snapshot::Kind kind) { return to(); }
friend class Interpreter;
friend class UnitDeserializationRoots;
};
@@ -2985,6 +3031,7 @@ class UntaggedMint : public UntaggedInteger {
friend class Api;
friend class Class;
friend class Integer;
friend class Interpreter;
};
COMPILE_ASSERT(sizeof(UntaggedMint) == 16);
@@ -2996,6 +3043,7 @@ class UntaggedDouble : public UntaggedNumber {
friend class Api;
friend class Class;
friend class Interpreter;
};
COMPILE_ASSERT(sizeof(UntaggedDouble) == 16);
@@ -3234,6 +3282,7 @@ class UntaggedArray : public UntaggedInstance {
friend class CodeSerializationCluster;
friend class CodeDeserializationCluster;
friend class Deserializer;
friend class Interpreter;
friend class UntaggedCode;
friend class UntaggedImmutableArray;
friend class GrowableObjectArray;
@@ -3315,6 +3364,7 @@ class UntaggedFloat32x4 : public UntaggedInstance {
ALIGN8 float value_[4];
friend class Class;
friend class Interpreter;
public:
float x() const { return value_[0]; }
@@ -3350,6 +3400,7 @@ class UntaggedFloat64x2 : public UntaggedInstance {
ALIGN8 double value_[2];
friend class Class;
friend class Interpreter;
public:
double x() const { return value_[0]; }
+7 -1
View File
@@ -34,7 +34,7 @@ namespace dart {
F(Function, owner_) \
F(Function, signature_) \
F(Function, data_) \
F(Function, ic_data_array_) \
F(Function, ic_data_array_or_bytecode_) \
F(Function, code_) \
F(ClosureData, context_scope_) \
F(ClosureData, parent_function_) \
@@ -91,6 +91,12 @@ namespace dart {
F(Code, compressed_stackmaps_) \
F(Code, inlined_id_to_function_) \
F(Code, code_source_map_) \
F(Bytecode, object_pool_) \
F(Bytecode, instructions_) \
F(Bytecode, function_) \
F(Bytecode, exception_handlers_) \
F(Bytecode, pc_descriptors_) \
F(Bytecode, closures_) \
F(ExceptionHandlers, handled_types_data_) \
F(Context, parent_) \
F(SingleTargetCache, target_) \
+2
View File
@@ -83,6 +83,8 @@ REUSABLE_HANDLE_LIST(REUSABLE_SCOPE)
ReusableClassHandleScope reused_class_handle(thread);
#define REUSABLE_CODE_HANDLESCOPE(thread) \
ReusableCodeHandleScope reused_code_handle(thread);
#define REUSABLE_BYTECODE_HANDLESCOPE(thread) \
ReusableBytecodeHandleScope reused_bytecode_handle(thread);
#define REUSABLE_ERROR_HANDLESCOPE(thread) \
ReusableErrorHandleScope reused_error_handle(thread);
#define REUSABLE_EXCEPTION_HANDLERS_HANDLESCOPE(thread) \
+298 -11
View File
@@ -23,6 +23,7 @@
#include "vm/flags.h"
#include "vm/heap/verifier.h"
#include "vm/instructions.h"
#include "vm/interpreter.h"
#include "vm/kernel_isolate.h"
#include "vm/message.h"
#include "vm/message_handler.h"
@@ -237,6 +238,7 @@ static void DoThrowNullError(Isolate* isolate,
StackFrameIterator::kNoCrossThreadIteration);
const StackFrame* caller_frame = iterator.NextFrame();
ASSERT(caller_frame->IsDartFrame());
ASSERT(!caller_frame->is_interpreted());
const Code& code = Code::Handle(zone, caller_frame->LookupDartCode());
const uword pc_offset = caller_frame->pc() - code.PayloadStart();
@@ -857,6 +859,129 @@ DEFINE_RUNTIME_ENTRY(CloneSuspendState, 1) {
RuntimeAllocationEpilogue(thread);
}
// Invoke field getter before dispatch.
// Arg0: instance.
// Arg1: field name (may be demangled during call).
// Return value: field value.
DEFINE_RUNTIME_ENTRY(GetFieldForDispatch, 2) {
#if defined(DART_DYNAMIC_MODULES)
const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
String& name = String::CheckedHandle(zone, arguments.ArgAt(1));
const Class& receiver_class = Class::Handle(zone, receiver.clazz());
if (Function::IsDynamicInvocationForwarderName(name)) {
name = Function::DemangleDynamicInvocationForwarderName(name);
arguments.SetArgAt(1, name); // Reflect change in arguments.
}
const String& getter_name = String::Handle(zone, Field::GetterName(name));
const int kTypeArgsLen = 0;
const int kNumArguments = 1;
ArgumentsDescriptor args_desc(Array::Handle(
zone, ArgumentsDescriptor::NewBoxed(kTypeArgsLen, kNumArguments)));
const Function& getter = Function::Handle(
zone, Resolver::ResolveDynamicForReceiverClass(
receiver_class, getter_name, args_desc, /*allow_add=*/true));
ASSERT(!getter.IsNull()); // An InvokeFieldDispatcher function was created.
const Array& args = Array::Handle(zone, Array::New(kNumArguments));
args.SetAt(0, receiver);
const Object& result =
Object::Handle(zone, DartEntry::InvokeFunction(getter, args));
ThrowIfError(result);
arguments.SetReturn(result);
#else
UNREACHABLE();
#endif // defined(DART_DYNAMIC_MODULES)
}
// Converts arguments descriptor passed to an implicit closure
// into an arguments descriptor for the target function.
// Arg0: implicit closure arguments descriptor
// Arg1: target function
// Return value: target arguments descriptor
DEFINE_RUNTIME_ENTRY(AdjustArgumentsDesciptorForImplicitClosure, 2) {
#if defined(DART_DYNAMIC_MODULES)
const auto& descriptor = Array::CheckedHandle(zone, arguments.ArgAt(0));
const auto& target = Function::CheckedHandle(zone, arguments.ArgAt(1));
const ArgumentsDescriptor args_desc(descriptor);
intptr_t type_args_len = args_desc.TypeArgsLen();
intptr_t num_arguments = args_desc.Count();
if (target.is_static()) {
if (target.IsFactory()) {
// Factory always takes type arguments via a positional parameter.
type_args_len = 0;
} else {
// Drop closure receiver.
--num_arguments;
}
} else {
if (target.IsGenerativeConstructor()) {
// Type arguments are not passed to a generative constructor.
type_args_len = 0;
} else {
// No need to adjust arguments descriptor.
arguments.SetReturn(descriptor);
return;
}
}
const auto& optional_arguments_names =
Array::Handle(zone, args_desc.GetArgumentNames());
const auto& result = Array::Handle(
zone, ArgumentsDescriptor::NewBoxed(type_args_len, num_arguments,
optional_arguments_names));
arguments.SetReturn(result);
#else
UNREACHABLE();
#endif // defined(DART_DYNAMIC_MODULES)
}
// Check that arguments are valid for the given closure.
// Arg0: closure
// Arg1: arguments descriptor
// Return value: whether the arguments are valid
DEFINE_RUNTIME_ENTRY(ClosureArgumentsValid, 2) {
#if defined(DART_DYNAMIC_MODULES)
const auto& closure = Closure::CheckedHandle(zone, arguments.ArgAt(0));
const auto& descriptor = Array::CheckedHandle(zone, arguments.ArgAt(1));
const auto& function = Function::Handle(zone, closure.function());
const ArgumentsDescriptor args_desc(descriptor);
if (!function.AreValidArguments(args_desc, nullptr)) {
arguments.SetReturn(Bool::False());
} else if (!closure.IsGeneric() && args_desc.TypeArgsLen() > 0) {
// The arguments may be valid for the closure function itself, but if the
// closure has delayed type arguments, no type arguments should be provided.
arguments.SetReturn(Bool::False());
} else {
arguments.SetReturn(Bool::True());
}
#else
UNREACHABLE();
#endif // defined(DART_DYNAMIC_MODULES)
}
// Resolve 'call' function of receiver.
// Arg0: receiver (not a closure).
// Arg1: arguments descriptor
// Return value: 'call' function'.
DEFINE_RUNTIME_ENTRY(ResolveCallFunction, 2) {
#if defined(DART_DYNAMIC_MODULES)
const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
const Array& descriptor = Array::CheckedHandle(zone, arguments.ArgAt(1));
ArgumentsDescriptor args_desc(descriptor);
ASSERT(!receiver.IsClosure()); // Interpreter tests for closure.
Class& cls = Class::Handle(zone, receiver.clazz());
Function& call_function = Function::Handle(
zone,
Resolver::ResolveDynamicForReceiverClass(cls, Symbols::call(), args_desc,
/*allow_add=*/false));
arguments.SetReturn(call_function);
#else
UNREACHABLE();
#endif // defined(DART_DYNAMIC_MODULES)
}
// Helper routine for tracing a type check.
static void PrintTypeCheck(const char* message,
const Instance& instance,
@@ -1257,6 +1382,7 @@ DEFINE_RUNTIME_ENTRY(TypeCheck, 7) {
DartFrameIterator iterator(thread,
StackFrameIterator::kNoCrossThreadIteration);
StackFrame* caller_frame = iterator.NextFrame();
ASSERT(!caller_frame->is_interpreted());
const auto& dispatcher =
Function::Handle(zone, caller_frame->LookupDartFunction());
ASSERT(dispatcher.IsInvokeFieldDispatcher());
@@ -1388,6 +1514,7 @@ DEFINE_RUNTIME_ENTRY(TypeCheck, 7) {
DartFrameIterator iterator(thread,
StackFrameIterator::kNoCrossThreadIteration);
StackFrame* caller_frame = iterator.NextFrame();
ASSERT(!caller_frame->is_interpreted());
const Code& caller_code =
Code::Handle(zone, caller_frame->LookupDartCode());
const ObjectPool& pool =
@@ -1490,6 +1617,7 @@ DEFINE_RUNTIME_ENTRY(PatchStaticCall, 0) {
StackFrameIterator::kNoCrossThreadIteration);
StackFrame* caller_frame = iterator.NextFrame();
ASSERT(caller_frame != nullptr);
ASSERT(!caller_frame->is_interpreted());
const Code& caller_code = Code::Handle(zone, caller_frame->LookupDartCode());
ASSERT(!caller_code.IsNull());
ASSERT(caller_code.is_optimized());
@@ -1536,8 +1664,10 @@ DEFINE_RUNTIME_ENTRY(BreakpointRuntimeHandler, 0) {
StackFrame* caller_frame = iterator.NextFrame();
ASSERT(caller_frame != nullptr);
Code& orig_stub = Code::Handle(zone);
orig_stub =
isolate->group()->debugger()->GetPatchedStubAddress(caller_frame->pc());
if (!caller_frame->is_interpreted()) {
orig_stub =
isolate->group()->debugger()->GetPatchedStubAddress(caller_frame->pc());
}
const Error& error =
Error::Handle(zone, isolate->debugger()->PauseBreakpoint());
ThrowIfError(error);
@@ -1659,6 +1789,7 @@ static void TrySwitchInstanceCall(Thread* thread,
#endif
// Monomorphic/megamorphic calls are only for unoptimized code.
if (caller_frame->is_interpreted()) return;
ASSERT(!caller_code.is_optimized());
// Code is detached from its function. This will prevent us from resetting
@@ -2742,6 +2873,44 @@ DEFINE_RUNTIME_ENTRY(SwitchableCallMiss, 2) {
handler.ResolveSwitchAndReturn(old_data);
}
// Handles interpreted interface call cache miss.
// Arg0: receiver
// Arg1: target name
// Arg2: arguments descriptor
// Returns: target function (can only be null in AOT runtime)
// Modifies the instance call table in current interpreter.
DEFINE_RUNTIME_ENTRY(InterpretedInstanceCallMissHandler, 3) {
#if defined(DART_DYNAMIC_MODULES)
const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
const String& target_name = String::CheckedHandle(zone, arguments.ArgAt(1));
const Array& arg_desc = Array::CheckedHandle(zone, arguments.ArgAt(2));
ArgumentsDescriptor arguments_descriptor(arg_desc);
const Class& receiver_class = Class::Handle(zone, receiver.clazz());
Function& target_function = Function::Handle(zone);
if (receiver_class.EnsureIsFinalized(thread) == Error::null()) {
const Class& cls = Class::Handle(zone, receiver.clazz());
const bool allow_add = !FLAG_precompiled_mode;
target_function = Resolver::ResolveDynamicForReceiverClass(
cls, target_name, arguments_descriptor, allow_add);
}
// TODO(regis): In order to substitute 'simple_instance_of_function', the 2nd
// arg to the call, the type, is needed.
if (target_function.IsNull()) {
target_function =
InlineCacheMissHelper(receiver_class, arg_desc, target_name);
}
#if !defined(DART_PRECOMPILED_RUNTIME)
ASSERT(!target_function.IsNull());
#endif
arguments.SetReturn(target_function);
#else
UNREACHABLE();
#endif // defined(DART_DYNAMIC_MODULES)
}
#if defined(DART_PRECOMPILED_RUNTIME)
// Used to find the correct receiver and function to invoke or to fall back to
// invoking noSuchMethod when lazy dispatchers are disabled. Returns the
@@ -2951,6 +3120,40 @@ DEFINE_RUNTIME_ENTRY(NoSuchMethodFromPrologue, 4) {
arguments.SetReturn(result);
}
// Invoke appropriate noSuchMethod function (or in the case of no lazy
// dispatchers, walk the receiver to find the correct method to call).
// Arg0: receiver
// Arg1: function name.
// Arg2: arguments descriptor array.
// Arg3: arguments array.
DEFINE_RUNTIME_ENTRY(InvokeNoSuchMethod, 4) {
#if defined(DART_DYNAMIC_MODULES)
const Instance& receiver = Instance::CheckedHandle(zone, arguments.ArgAt(0));
const String& original_function_name =
String::CheckedHandle(zone, arguments.ArgAt(1));
const Array& orig_arguments_desc =
Array::CheckedHandle(zone, arguments.ArgAt(2));
const Array& orig_arguments = Array::CheckedHandle(zone, arguments.ArgAt(3));
auto& result = Object::Handle(zone);
#if defined(DART_PRECOMPILED_RUNTIME)
// Failing to find the method could be due to the lack of lazy invoke field
// dispatchers, so attempt a deeper search before calling noSuchMethod.
result = InvokeCallThroughGetterOrNoSuchMethod(
thread, zone, receiver, original_function_name, orig_arguments,
orig_arguments_desc);
#else
result =
DartEntry::InvokeNoSuchMethod(thread, receiver, original_function_name,
orig_arguments, orig_arguments_desc);
#endif
ThrowIfError(result);
arguments.SetReturn(result);
#else
UNREACHABLE();
#endif // defined(DART_DYNAMIC_MODULES)
}
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
// The following code is used to stress test
// - deoptimization
@@ -3000,9 +3203,13 @@ static void HandleStackOverflowTestCases(Thread* thread) {
ASSERT(frame != nullptr);
Code& code = Code::Handle();
Function& function = Function::Handle();
code = frame->LookupDartCode();
ASSERT(!code.IsNull());
function = code.function();
if (frame->is_interpreted()) {
function = frame->LookupDartFunction();
} else {
code = frame->LookupDartCode();
ASSERT(!code.IsNull());
function = code.function();
}
ASSERT(!function.IsNull());
const char* function_name = nullptr;
if ((FLAG_deoptimize_filter != nullptr) ||
@@ -3152,15 +3359,32 @@ DEFINE_RUNTIME_ENTRY(InterruptOrStackOverflow, 0) {
// persist.
uword stack_overflow_flags = thread->GetAndClearStackOverflowFlags();
bool interpreter_stack_overflow = false;
#if defined(DART_DYNAMIC_MODULES)
Interpreter* interpreter = thread->interpreter();
if (interpreter != nullptr) {
interpreter_stack_overflow =
interpreter->get_sp() >= interpreter->overflow_stack_limit();
}
#endif // defined(DART_DYNAMIC_MODULES)
// If an interrupt happens at the same time as a stack overflow, we
// process the stack overflow now and leave the interrupt for next
// time.
if (!thread->os_thread()->HasStackHeadroom() ||
if (interpreter_stack_overflow || !thread->os_thread()->HasStackHeadroom() ||
IsCalleeFrameOf(thread->saved_stack_limit(), stack_pos)) {
if (FLAG_verbose_stack_overflow) {
OS::PrintErr("Stack overflow\n");
OS::PrintErr(" Native SP = %" Px ", stack limit = %" Px "\n", stack_pos,
thread->saved_stack_limit());
#if defined(DART_DYNAMIC_MODULES)
if (thread->interpreter() != nullptr) {
OS::PrintErr(" Interpreter SP = %" Px ", stack limit = %" Px "\n",
thread->interpreter()->get_sp(),
thread->interpreter()->overflow_stack_limit());
}
#endif // defined(DART_DYNAMIC_MODULES)
OS::PrintErr("Call stack:\n");
OS::PrintErr("size | frame\n");
StackFrameIterator frames(ValidationPolicy::kDontValidateFrames, thread,
@@ -3441,6 +3665,7 @@ void DeoptimizeAt(Thread* mutator_thread,
// N.B.: Update the pending deopt table before updating the frame. The
// profiler may attempt a stack walk in between.
ASSERT(!frame->is_interpreted());
mutator_thread->pending_deopts().AddPendingDeopt(frame->fp(), deopt_pc);
frame->MarkForLazyDeopt();
@@ -3474,10 +3699,12 @@ void DeoptimizeFunctionsOnStack() {
mutator_thread, StackFrameIterator::kAllowCrossThreadIteration);
StackFrame* frame = iterator.NextFrame();
while (frame != nullptr) {
optimized_code = frame->LookupDartCode();
if (optimized_code.is_optimized() &&
!optimized_code.is_force_optimized()) {
DeoptimizeAt(mutator_thread, optimized_code, frame);
if (!frame->is_interpreted()) {
optimized_code = frame->LookupDartCode();
if (optimized_code.is_optimized() &&
!optimized_code.is_force_optimized()) {
DeoptimizeAt(mutator_thread, optimized_code, frame);
}
}
frame = iterator.NextFrame();
}
@@ -3501,7 +3728,7 @@ static void DeoptimizeLastDartFrameIfOptimized() {
DartFrameIterator iterator(mutator_thread,
StackFrameIterator::kNoCrossThreadIteration);
StackFrame* frame = iterator.NextFrame();
if (frame != nullptr) {
if (frame != nullptr && !frame->is_interpreted()) {
const auto& optimized_code = Code::Handle(frame->LookupDartCode());
if (optimized_code.is_optimized() &&
!optimized_code.is_force_optimized()) {
@@ -3962,6 +4189,66 @@ DEFINE_RAW_LEAF_RUNTIME_ENTRY(MemoryMove,
/*is_float=*/false,
static_cast<MemMoveCFunction>(memmove));
#if defined(DART_DYNAMIC_MODULES)
// Interpret a function call. Should be called only for non-jitted functions.
// argc indicates the number of arguments, including the type arguments.
// argv points to the first argument.
// If argc < 0, arguments are passed at decreasing memory addresses from argv.
extern "C" uword /*ObjectPtr*/ InterpretCall(uword /*FunctionPtr*/ function_in,
uword /*ArrayPtr*/ argdesc_in,
intptr_t argc,
ObjectPtr* argv,
Thread* thread) {
FunctionPtr function = static_cast<FunctionPtr>(function_in);
ArrayPtr argdesc = static_cast<ArrayPtr>(argdesc_in);
Interpreter* interpreter = Interpreter::Current();
#if defined(DEBUG)
uword exit_fp = thread->top_exit_frame_info();
ASSERT(exit_fp != 0);
ASSERT(thread == Thread::Current());
// Caller is InterpretCall stub called from generated code.
// We stay in "in generated code" execution state when interpreting code.
ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
ASSERT(Function::HasBytecode(function));
ASSERT(interpreter != nullptr);
#endif
// Tell MemorySanitizer 'argv' is initialized by generated code.
if (argc < 0) {
MSAN_UNPOISON(argv - argc, -argc * sizeof(ObjectPtr));
} else {
MSAN_UNPOISON(argv, argc * sizeof(ObjectPtr));
}
ObjectPtr result =
interpreter->Call(function, argdesc, argc, argv, Array::null(), thread);
DEBUG_ASSERT(thread->top_exit_frame_info() == exit_fp);
if (IsErrorClassId(result->GetClassIdMayBeSmi())) {
// Must not leak handles in the caller's zone.
HANDLESCOPE(thread);
// Protect the result in a handle before transitioning, which may trigger
// GC.
const Error& error = Error::Handle(Error::RawCast(result));
// Propagating an error may cause allocation. Check if we need to block for
// a safepoint by switching to "in VM" execution state.
TransitionGeneratedToVM transition(thread);
Exceptions::PropagateError(error);
}
return static_cast<uword>(result);
}
#endif // defined(DART_DYNAMIC_MODULES)
uword RuntimeEntry::InterpretCallEntry() {
#if defined(DART_DYNAMIC_MODULES)
uword entry = reinterpret_cast<uword>(InterpretCall);
#if defined(USING_SIMULATOR)
entry = Simulator::RedirectExternalReference(entry,
Simulator::kLeafRuntimeCall, 5);
#endif
return entry;
#else
return 0;
#endif // defined(DART_DYNAMIC_MODULES)
}
extern "C" void DFLRT_EnterSafepoint(NativeArguments __unusable_) {
CHECK_STACK_ALIGNMENT;
TRACE_RUNTIME_CALL("%s", "EnterSafepoint");
+2
View File
@@ -56,6 +56,8 @@ class RuntimeEntry : public BaseRuntimeEntry {
bool can_lazy_deopt() const { return can_lazy_deopt_; }
uword GetEntryPoint() const;
static uword InterpretCallEntry();
private:
const char* const name_;
const void* const function_;
+7 -1
View File
@@ -72,7 +72,13 @@ namespace dart {
V(ResumeFrame) \
V(SwitchableCallMiss) \
V(NotLoaded) \
V(FfiAsyncCallbackSend)
V(FfiAsyncCallbackSend) \
V(GetFieldForDispatch) \
V(AdjustArgumentsDesciptorForImplicitClosure) \
V(ClosureArgumentsValid) \
V(ResolveCallFunction) \
V(InterpretedInstanceCallMissHandler) \
V(InvokeNoSuchMethod)
// Note: Leaf runtime function have C linkage, so they cannot pass C++ struct
// values like ObjectPtr.
+161 -30
View File
@@ -148,6 +148,10 @@ bool StackFrame::IsBareInstructionsStubFrame() const {
}
bool StackFrame::IsStubFrame() const {
if (is_interpreted()) {
return false;
}
if (FLAG_precompiled_mode) {
return IsBareInstructionsStubFrame();
}
@@ -169,6 +173,15 @@ bool StackFrame::IsStubFrame() const {
const char* StackFrame::ToCString() const {
ASSERT(thread_ == Thread::Current());
Zone* zone = Thread::Current()->zone();
#if defined(DART_DYNAMIC_MODULES)
if (is_interpreted()) {
const Bytecode& bytecode = Bytecode::Handle(zone, LookupDartBytecode());
const char* name = bytecode.IsNull() ? "Cannot find bytecode object"
: bytecode.FullyQualifiedName();
return zone->PrintToString(" pc 0x%" Pp " fp 0x%" Pp " sp 0x%" Pp " %s",
pc(), fp(), sp(), name);
}
#endif // defined(DART_DYNAMIC_MODULES)
const Code& code = Code::Handle(zone, GetCodeObject());
const char* name =
code.IsNull()
@@ -180,11 +193,16 @@ const char* StackFrame::ToCString() const {
void ExitFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
ASSERT(visitor != nullptr);
// Visit pc marker and saved pool pointer.
ObjectPtr* last_fixed = reinterpret_cast<ObjectPtr*>(fp()) +
runtime_frame_layout.first_object_from_fp;
ObjectPtr* first_fixed = reinterpret_cast<ObjectPtr*>(fp()) +
runtime_frame_layout.last_fixed_object_from_fp;
// Visit pc marker and saved pool pointer, or, for interpreted frame, code
// object and function object.
ObjectPtr* last_fixed =
reinterpret_cast<ObjectPtr*>(fp()) +
(is_interpreted() ? kKBCLastFixedObjectSlotFromFp
: runtime_frame_layout.first_object_from_fp);
ObjectPtr* first_fixed =
reinterpret_cast<ObjectPtr*>(fp()) +
(is_interpreted() ? kKBCFirstObjectSlotFromFp
: runtime_frame_layout.last_fixed_object_from_fp);
if (first_fixed <= last_fixed) {
visitor->VisitPointers(first_fixed, last_fixed);
} else {
@@ -196,9 +214,12 @@ void ExitFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
void EntryFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
ASSERT(visitor != nullptr);
// Visit objects between SP and (FP - callee_save_area).
ObjectPtr* first = reinterpret_cast<ObjectPtr*>(sp());
ObjectPtr* last =
reinterpret_cast<ObjectPtr*>(fp()) + kExitLinkSlotFromEntryFp - 1;
ObjectPtr* first = is_interpreted() ? reinterpret_cast<ObjectPtr*>(fp()) +
kKBCSavedArgDescSlotFromEntryFp
: reinterpret_cast<ObjectPtr*>(sp());
ObjectPtr* last = is_interpreted() ? reinterpret_cast<ObjectPtr*>(sp())
: reinterpret_cast<ObjectPtr*>(fp()) +
kExitLinkSlotFromEntryFp - 1;
// There may not be any pointer to visit; in this case, first > last.
visitor->VisitPointers(first, last);
}
@@ -227,7 +248,9 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
global_table = global_table_payload;
} else {
ObjectPtr pc_marker = *(reinterpret_cast<ObjectPtr*>(
fp() + (runtime_frame_layout.code_from_fp * kWordSize)));
fp() + ((is_interpreted() ? kKBCPcMarkerSlotFromFp
: runtime_frame_layout.code_from_fp) *
kWordSize)));
// May forward raw code. Note we don't just visit the pc marker slot first
// because the visitor's forwarding might not be idempotent.
visitor->VisitPointer(&pc_marker);
@@ -241,7 +264,9 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
isolate_group()->object_store()->canonicalized_stack_map_entries();
}
} else {
ASSERT(pc_marker == Object::null());
ASSERT(pc_marker == Object::null() ||
(is_interpreted() && (!pc_marker->IsHeapObject() ||
(pc_marker->GetClassId() == kBytecodeCid))));
}
}
@@ -252,6 +277,9 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
maps, global_table);
const uint32_t pc_offset = pc() - code_start;
if (it.Find(pc_offset)) {
if (is_interpreted()) {
UNIMPLEMENTED();
}
ObjectPtr* first = reinterpret_cast<ObjectPtr*>(sp());
ObjectPtr* last = reinterpret_cast<ObjectPtr*>(
fp() + (runtime_frame_layout.first_local_from_fp * kWordSize));
@@ -315,14 +343,33 @@ void StackFrame::VisitObjectPointers(ObjectPointerVisitor* visitor) {
// For normal unoptimized Dart frames and Stub frames each slot
// between the first and last included are tagged objects.
ObjectPtr* first = reinterpret_cast<ObjectPtr*>(sp());
if (is_interpreted()) {
// Do not visit caller's pc or caller's fp.
ObjectPtr* first =
reinterpret_cast<ObjectPtr*>(fp()) + kKBCFirstObjectSlotFromFp;
ObjectPtr* last =
reinterpret_cast<ObjectPtr*>(fp()) + kKBCLastFixedObjectSlotFromFp;
visitor->VisitPointers(first, last);
}
ObjectPtr* first =
reinterpret_cast<ObjectPtr*>(is_interpreted() ? fp() : sp());
ObjectPtr* last = reinterpret_cast<ObjectPtr*>(
fp() + (runtime_frame_layout.first_object_from_fp * kWordSize));
is_interpreted()
? sp()
: fp() + (runtime_frame_layout.first_object_from_fp * kWordSize));
visitor->VisitPointers(first, last);
}
FunctionPtr StackFrame::LookupDartFunction() const {
if (is_interpreted()) {
ObjectPtr result = *(reinterpret_cast<FunctionPtr*>(
fp() + kKBCFunctionSlotFromFp * kWordSize));
ASSERT((result == Object::null()) ||
(result->GetClassId() == kFunctionCid));
return static_cast<FunctionPtr>(result);
}
const Code& code = Code::Handle(LookupDartCode());
if (!code.IsNull()) {
const Object& owner = Object::Handle(code.owner());
@@ -350,6 +397,8 @@ CodePtr StackFrame::LookupDartCode() const {
}
CodePtr StackFrame::GetCodeObject() const {
ASSERT(!is_interpreted());
#if defined(DART_PRECOMPILED_RUNTIME)
if (FLAG_precompiled_mode) {
if (pc() == 0) {
@@ -370,6 +419,27 @@ CodePtr StackFrame::GetCodeObject() const {
return static_cast<CodePtr>(pc_marker);
}
BytecodePtr StackFrame::LookupDartBytecode() const {
// We add a no gc scope to ensure that the code below does not trigger
// a GC as we are handling raw object references here. It is possible
// that the code is called while a GC is in progress, that is ok.
#if !defined(HOST_OS_WINDOWS) && !defined(HOST_OS_FUCHSIA)
// On Windows and Fuchsia, the profiler calls this from a separate thread
// where Thread::Current() is NULL, so we cannot create a NoSafepointScope.
NoSafepointScope no_safepoint;
#endif
return GetBytecodeObject();
}
BytecodePtr StackFrame::GetBytecodeObject() const {
ASSERT(is_interpreted());
ObjectPtr pc_marker = *(
reinterpret_cast<ObjectPtr*>(fp() + kKBCPcMarkerSlotFromFp * kWordSize));
ASSERT((pc_marker == Object::null()) ||
(pc_marker->GetClassId() == kBytecodeCid));
return static_cast<BytecodePtr>(pc_marker);
}
bool StackFrame::FindExceptionHandler(Thread* thread,
uword* handler_pc,
bool* needs_stacktrace,
@@ -377,19 +447,28 @@ bool StackFrame::FindExceptionHandler(Thread* thread,
bool* is_optimized) const {
REUSABLE_CODE_HANDLESCOPE(thread);
Code& code = reused_code_handle.Handle();
REUSABLE_BYTECODE_HANDLESCOPE(thread);
Bytecode& bytecode = reused_bytecode_handle.Handle();
REUSABLE_EXCEPTION_HANDLERS_HANDLESCOPE(thread);
ExceptionHandlers& handlers = reused_exception_handlers_handle.Handle();
REUSABLE_PC_DESCRIPTORS_HANDLESCOPE(thread);
PcDescriptors& descriptors = reused_pc_descriptors_handle.Handle();
uword start;
code = LookupDartCode();
if (code.IsNull()) {
return false; // Stub frames do not have exception handlers.
if (is_interpreted()) {
bytecode = LookupDartBytecode();
ASSERT(!bytecode.IsNull());
start = bytecode.PayloadStart();
handlers = bytecode.exception_handlers();
} else {
code = LookupDartCode();
if (code.IsNull()) {
return false; // Stub frames do not have exception handlers.
}
start = code.PayloadStart();
handlers = code.exception_handlers();
descriptors = code.pc_descriptors();
*is_optimized = code.is_optimized();
}
start = code.PayloadStart();
handlers = code.exception_handlers();
descriptors = code.pc_descriptors();
*is_optimized = code.is_optimized();
HandlerInfoCache* cache = thread->isolate()->handler_info_cache();
ExceptionHandlerInfo* info = cache->Lookup(pc());
if (info != nullptr) {
@@ -401,13 +480,18 @@ bool StackFrame::FindExceptionHandler(Thread* thread,
intptr_t try_index = -1;
if (handlers.num_entries() != 0) {
uword pc_offset = pc() - code.PayloadStart();
PcDescriptors::Iterator iter(descriptors, UntaggedPcDescriptors::kAnyKind);
while (iter.MoveNext()) {
const intptr_t current_try_index = iter.TryIndex();
if ((iter.PcOffset() == pc_offset) && (current_try_index != -1)) {
try_index = current_try_index;
break;
if (is_interpreted()) {
try_index = bytecode.GetTryIndexAtPc(pc());
} else {
uword pc_offset = pc() - code.PayloadStart();
PcDescriptors::Iterator iter(descriptors,
UntaggedPcDescriptors::kAnyKind);
while (iter.MoveNext()) {
const intptr_t current_try_index = iter.TryIndex();
if ((iter.PcOffset() == pc_offset) && (current_try_index != -1)) {
try_index = current_try_index;
break;
}
}
}
}
@@ -430,6 +514,13 @@ bool StackFrame::FindExceptionHandler(Thread* thread,
}
TokenPosition StackFrame::GetTokenPos() const {
if (is_interpreted()) {
const Bytecode& bytecode = Bytecode::Handle(LookupDartBytecode());
if (bytecode.IsNull()) {
return TokenPosition::kNoSource; // Stub frames do not have token_pos.
}
return bytecode.GetTokenIndexOfPC(pc());
}
const Code& code = Code::Handle(LookupDartCode());
if (code.IsNull()) {
return TokenPosition::kNoSource; // Stub frames do not have token_pos.
@@ -451,6 +542,9 @@ bool StackFrame::IsValid() const {
if (IsEntryFrame() || IsExitFrame() || IsStubFrame()) {
return true;
}
if (is_interpreted()) {
return (LookupDartBytecode() != Bytecode::null());
}
return (LookupDartCode() != Code::null());
}
@@ -471,16 +565,25 @@ void StackFrameIterator::SetupLastExitFrameData() {
frames_.fp_ = exit_marker;
frames_.sp_ = 0;
frames_.pc_ = 0;
#if defined(DART_DYNAMIC_MODULES)
frames_.CheckIfInterpreted(exit_marker);
#endif
frames_.Unpoison();
}
void StackFrameIterator::SetupNextExitFrameData() {
ASSERT(entry_.fp() != 0);
uword exit_address = entry_.fp() + (kExitLinkSlotFromEntryFp * kWordSize);
uword exit_address =
entry_.fp() + ((entry_.is_interpreted() ? kKBCExitLinkSlotFromEntryFp
: kExitLinkSlotFromEntryFp) *
kWordSize);
uword exit_marker = *reinterpret_cast<uword*>(exit_address);
frames_.fp_ = exit_marker;
frames_.sp_ = 0;
frames_.pc_ = 0;
#if defined(DART_DYNAMIC_MODULES)
frames_.CheckIfInterpreted(exit_marker);
#endif
frames_.Unpoison();
}
@@ -513,6 +616,9 @@ StackFrameIterator::StackFrameIterator(uword last_fp,
frames_.fp_ = last_fp;
frames_.sp_ = 0;
frames_.pc_ = 0;
#if defined(DART_DYNAMIC_MODULES)
frames_.CheckIfInterpreted(last_fp);
#endif
frames_.Unpoison();
}
@@ -533,6 +639,9 @@ StackFrameIterator::StackFrameIterator(uword fp,
frames_.fp_ = fp;
frames_.sp_ = sp;
frames_.pc_ = pc;
#if defined(DART_DYNAMIC_MODULES)
frames_.CheckIfInterpreted(fp);
#endif
frames_.Unpoison();
}
@@ -570,8 +679,10 @@ StackFrame* StackFrameIterator::NextFrame() {
// Iteration starts from an exit frame given by its fp.
current_frame_ = NextExitFrame();
} else if (*(reinterpret_cast<uword*>(
frames_.fp_ + (kSavedCallerFpSlotFromFp * kWordSize))) ==
0) {
frames_.fp_ +
((frames_.is_interpreted() ? kKBCSavedCallerFpSlotFromFp
: kSavedCallerFpSlotFromFp) *
kWordSize))) == 0) {
// Iteration starts from an entry frame given by its fp, sp, and pc.
current_frame_ = NextEntryFrame();
} else {
@@ -601,6 +712,15 @@ StackFrame* StackFrameIterator::NextFrame() {
return current_frame_;
}
#if defined(DART_DYNAMIC_MODULES)
void StackFrameIterator::FrameSetIterator::CheckIfInterpreted(
uword exit_marker) {
Interpreter* interpreter = thread_->interpreter();
is_interpreted_ =
(interpreter != nullptr) && interpreter->HasFrame(exit_marker);
}
#endif // defined(DART_DYNAMIC_MODULES)
// Tell MemorySanitizer that generated code initializes part of the stack.
void StackFrameIterator::FrameSetIterator::Unpoison() {
// When using a simulator, all writes to the stack happened from MSAN
@@ -610,7 +730,7 @@ void StackFrameIterator::FrameSetIterator::Unpoison() {
#if !defined(USING_SIMULATOR)
if (fp_ == 0) return;
// Note that Thread::os_thread_ is cleared when the thread is descheduled.
ASSERT((thread_->os_thread() == nullptr) ||
ASSERT(is_interpreted() || (thread_->os_thread() == nullptr) ||
((thread_->os_thread()->stack_limit() < fp_) &&
(thread_->os_thread()->stack_base() > fp_)));
uword lower;
@@ -633,10 +753,14 @@ StackFrame* StackFrameIterator::FrameSetIterator::NextFrame(bool validate) {
frame->sp_ = sp_;
frame->fp_ = fp_;
frame->pc_ = pc_;
#if defined(DART_DYNAMIC_MODULES)
frame->is_interpreted_ = is_interpreted();
#endif
sp_ = frame->GetCallerSp();
fp_ = frame->GetCallerFp();
pc_ = frame->GetCallerPc();
Unpoison();
ASSERT(is_interpreted() == frame->is_interpreted());
ASSERT(!validate || frame->IsValid());
return frame;
}
@@ -645,10 +769,14 @@ ExitFrame* StackFrameIterator::NextExitFrame() {
exit_.sp_ = frames_.sp_;
exit_.fp_ = frames_.fp_;
exit_.pc_ = frames_.pc_;
#if defined(DART_DYNAMIC_MODULES)
exit_.is_interpreted_ = frames_.is_interpreted();
#endif
frames_.sp_ = exit_.GetCallerSp();
frames_.fp_ = exit_.GetCallerFp();
frames_.pc_ = exit_.GetCallerPc();
frames_.Unpoison();
ASSERT(frames_.is_interpreted() == exit_.is_interpreted());
ASSERT(!validate_ || exit_.IsValid());
return &exit_;
}
@@ -658,6 +786,9 @@ EntryFrame* StackFrameIterator::NextEntryFrame() {
entry_.sp_ = frames_.sp_;
entry_.fp_ = frames_.fp_;
entry_.pc_ = frames_.pc_;
#if defined(DART_DYNAMIC_MODULES)
entry_.is_interpreted_ = frames_.is_interpreted();
#endif
SetupNextExitFrameData(); // Setup data for next exit frame in chain.
ASSERT(!validate_ || entry_.IsValid());
return &entry_;
+58 -10
View File
@@ -7,7 +7,9 @@
#include "vm/allocation.h"
#include "vm/frame_layout.h"
#include "vm/interpreter.h"
#include "vm/object.h"
#include "vm/stack_frame_kbc.h"
#include "vm/stub_code.h"
#if defined(TARGET_ARCH_IA32)
@@ -44,6 +46,7 @@ class StackFrame : public ValueObject {
// The pool pointer is not implemented on all architectures.
static int SavedCallerPpSlotFromFp() {
// Never called on an interpreter frame.
if (runtime_frame_layout.saved_caller_pp_from_fp !=
kSavedCallerFpSlotFromFp) {
return runtime_frame_layout.saved_caller_pp_from_fp;
@@ -53,30 +56,37 @@ class StackFrame : public ValueObject {
}
bool IsMarkedForLazyDeopt() const {
ASSERT(!is_interpreted());
uword raw_pc =
*reinterpret_cast<uword*>(sp() + (kSavedPcSlotFromSp * kWordSize));
return raw_pc == StubCode::DeoptimizeLazyFromReturn().EntryPoint();
}
void MarkForLazyDeopt() {
ASSERT(!is_interpreted());
set_pc(StubCode::DeoptimizeLazyFromReturn().EntryPoint());
}
void UnmarkForLazyDeopt() {
// If this frame was marked for lazy deopt, pc_ was computed to be the
// original return address using the pending deopts table in GetCallerPc.
// Write this value back into the frame.
ASSERT(!is_interpreted());
uword original_pc = pc();
ASSERT(original_pc != StubCode::DeoptimizeLazyFromReturn().EntryPoint());
set_pc(original_pc);
}
void set_pc(uword value) {
*reinterpret_cast<uword*>(sp() + (kSavedPcSlotFromSp * kWordSize)) = value;
*reinterpret_cast<uword*>(sp() + ((is_interpreted() ? kKBCSavedPcSlotFromSp
: kSavedPcSlotFromSp) *
kWordSize)) = value;
pc_ = value;
}
void set_pc_marker(CodePtr code) {
*reinterpret_cast<CodePtr*>(
fp() + (runtime_frame_layout.code_from_fp * kWordSize)) = code;
fp() + ((is_interpreted() ? kKBCPcMarkerSlotFromFp
: runtime_frame_layout.code_from_fp) *
kWordSize)) = code;
}
// Visit objects in the frame.
@@ -102,8 +112,15 @@ class StackFrame : public ValueObject {
virtual bool IsEntryFrame() const { return false; }
virtual bool IsExitFrame() const { return false; }
#if defined(DART_DYNAMIC_MODULES)
bool is_interpreted() const { return is_interpreted_; }
#else
bool is_interpreted() const { return false; }
#endif
FunctionPtr LookupDartFunction() const;
CodePtr LookupDartCode() const;
BytecodePtr LookupDartBytecode() const;
bool FindExceptionHandler(Thread* thread,
uword* handler_pc,
bool* needs_stacktrace,
@@ -114,7 +131,11 @@ class StackFrame : public ValueObject {
static void DumpCurrentTrace();
uword GetCallerSp() const { return fp() + (kCallerSpSlotFromFp * kWordSize); }
uword GetCallerSp() const {
return fp() +
((is_interpreted() ? kKBCCallerSpSlotFromFp : kCallerSpSlotFromFp) *
kWordSize);
}
protected:
explicit StackFrame(Thread* thread)
@@ -134,15 +155,20 @@ class StackFrame : public ValueObject {
private:
CodePtr GetCodeObject() const;
BytecodePtr GetBytecodeObject() const;
uword GetCallerFp() const {
return *(reinterpret_cast<uword*>(fp() +
(kSavedCallerFpSlotFromFp * kWordSize)));
return *(reinterpret_cast<uword*>(
fp() + ((is_interpreted() ? kKBCSavedCallerFpSlotFromFp
: kSavedCallerFpSlotFromFp) *
kWordSize)));
}
uword GetCallerPc() const {
uword raw_pc = *(reinterpret_cast<uword*>(
fp() + (kSavedCallerPcSlotFromFp * kWordSize)));
fp() + ((is_interpreted() ? kKBCSavedCallerPcSlotFromFp
: kSavedCallerPcSlotFromFp) *
kWordSize)));
ASSERT(raw_pc != StubCode::DeoptimizeLazyFromThrow().EntryPoint());
if (raw_pc == StubCode::DeoptimizeLazyFromReturn().EntryPoint()) {
return thread_->pending_deopts().FindPendingDeopt(GetCallerFp());
@@ -155,6 +181,10 @@ class StackFrame : public ValueObject {
uword pc_;
Thread* thread_;
#if defined(DART_DYNAMIC_MODULES)
bool is_interpreted_ = false;
#endif
// The iterators FrameSetIterator and StackFrameIterator set the private
// fields fp_ and sp_ when they return the respective frame objects.
friend class FrameSetIterator;
@@ -192,7 +222,9 @@ class ExitFrame : public StackFrame {
// dart code.
class EntryFrame : public StackFrame {
public:
bool IsValid() const { return StubCode::InInvocationStub(pc()); }
bool IsValid() const {
return StubCode::InInvocationStub(pc(), is_interpreted());
}
bool IsDartFrame(bool validate = true) const { return false; }
bool IsStubFrame() const { return false; }
bool IsEntryFrame() const { return true; }
@@ -261,9 +293,11 @@ class StackFrameIterator {
if (fp_ == 0) {
return false;
}
const uword pc =
*(reinterpret_cast<uword*>(sp_ + (kSavedPcSlotFromSp * kWordSize)));
return !StubCode::InInvocationStub(pc);
const uword pc = *(reinterpret_cast<uword*>(
sp_ +
((is_interpreted() ? kKBCSavedPcSlotFromSp : kSavedPcSlotFromSp) *
kWordSize)));
return !StubCode::InInvocationStub(pc, is_interpreted());
}
// Get next non entry/exit frame in the set (assumes a next frame exists).
@@ -272,6 +306,14 @@ class StackFrameIterator {
private:
explicit FrameSetIterator(Thread* thread)
: fp_(0), sp_(0), pc_(0), stack_frame_(thread), thread_(thread) {}
#if defined(DART_DYNAMIC_MODULES)
bool is_interpreted() const { return is_interpreted_; }
void CheckIfInterpreted(uword exit_marker);
#else
bool is_interpreted() const { return false; }
#endif
void Unpoison();
uword fp_;
@@ -280,6 +322,10 @@ class StackFrameIterator {
StackFrame stack_frame_; // Singleton frame returned by NextFrame().
Thread* thread_;
#if defined(DART_DYNAMIC_MODULES)
bool is_interpreted_ = false;
#endif
friend class StackFrameIterator;
DISALLOW_COPY_AND_ASSIGN(FrameSetIterator);
};
@@ -299,6 +345,8 @@ class StackFrameIterator {
void SetupLastExitFrameData();
void SetupNextExitFrameData();
void CheckInterpreterExitFrame(uword exit_marker);
bool validate_; // Validate each frame as we traverse the frames.
EntryFrame entry_; // Singleton entry frame returned by NextEntryFrame().
ExitFrame exit_; // Singleton exit frame returned by NextExitFrame().
+65
View File
@@ -0,0 +1,65 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_STACK_FRAME_KBC_H_
#define RUNTIME_VM_STACK_FRAME_KBC_H_
#include "platform/globals.h"
namespace dart {
/* Kernel Bytecode Frame Layout
IMPORTANT: KBC stack is growing upwards which is different from all other
architectures. This enables efficient addressing for locals via unsigned index.
| | <- TOS
Callee frame | ... |
| saved FP | (FP of current frame)
| saved PC | (PC of current frame)
| code object |
| function object |
+--------------------+
Current frame | ... T| <- SP of current frame
| ... T|
| first local T| <- FP of current frame
| caller's FP |
| caller's PC |
| code object T| (current frame's code object)
| function object T| (current frame's function object)
+--------------------+
Caller frame | last parameter | <- SP of caller frame
| ... |
T against a slot indicates it needs to be traversed during GC.
*/
static const int kKBCDartFrameFixedSize = 4; // Function, Code, PC, FP
static const int kKBCSavedPcSlotFromSp = 3;
static const int kKBCFirstObjectSlotFromFp = -4; // Used by GC.
static const int kKBCLastFixedObjectSlotFromFp = -3;
static const int kKBCSavedCallerFpSlotFromFp = -1;
static const int kKBCSavedCallerPcSlotFromFp = -2;
static const int kKBCCallerSpSlotFromFp = -kKBCDartFrameFixedSize - 1;
static const int kKBCPcMarkerSlotFromFp = -3;
static const int kKBCFunctionSlotFromFp = -4;
static const int kKBCParamEndSlotFromFp = 4;
static const int kKBCSuspendStateSlotFromFp = 0;
// Entry and exit frame layout.
static const int kKBCEntrySavedSlots = 3;
static const int kKBCExitLinkSlotFromEntryFp = 0;
static const int kKBCSavedArgDescSlotFromEntryFp = 1;
static const int kKBCSavedPpSlotFromEntryFp = 2;
// Value for stack limit that is used to cause an interrupt.
// Note that on KBC stack is growing upwards so interrupt limit is 0 unlike
// on all other architectures.
static const uword kKBCInterruptStackLimit = 0;
} // namespace dart
#endif // RUNTIME_VM_STACK_FRAME_KBC_H_
+26 -8
View File
@@ -90,6 +90,7 @@ class AsyncAwareStackUnwinder : public ValueObject {
encountered_async_catch_error_(encountered_async_catch_error),
closure_(Closure::Handle(zone_)),
code_(Code::Handle(zone_)),
bytecode_(Bytecode::Handle(zone_)),
context_(Context::Handle(zone_)),
function_(Function::Handle(zone_)),
parent_function_(Function::Handle(zone_)),
@@ -100,7 +101,9 @@ class AsyncAwareStackUnwinder : public ValueObject {
subscription_(Object::Handle(zone_)),
stream_iterator_(Object::Handle(zone_)),
async_lib_(Library::Handle(zone_, Library::AsyncLibrary())),
null_closure_(Closure::Handle(zone_)) {
null_closure_(Closure::Handle(zone_)),
null_code_(Code::Handle(zone_)),
null_bytecode_(Bytecode::Handle(zone_)) {
if (encountered_async_catch_error_ != nullptr) {
*encountered_async_catch_error_ = false;
}
@@ -259,6 +262,7 @@ class AsyncAwareStackUnwinder : public ValueObject {
Closure& closure_;
Code& code_;
Bytecode& bytecode_;
Context& context_;
Function& function_;
Function& parent_function_;
@@ -275,6 +279,8 @@ class AsyncAwareStackUnwinder : public ValueObject {
Field* fields_[kUsedFieldCount] = {};
const Closure& null_closure_;
const Code& null_code_;
const Bytecode& null_bytecode_;
DISALLOW_COPY_AND_ASSIGN(AsyncAwareStackUnwinder);
};
@@ -294,16 +300,27 @@ void AsyncAwareStackUnwinder::Unwind(
while (sync_frame_ != nullptr && awaiter_frame_.closure.IsNull()) {
const bool was_handled = HandleSynchronousFrame();
if (!was_handled) {
code_ = sync_frame_->LookupDartCode();
const uword pc_offset = sync_frame_->pc() - code_.PayloadStart();
handle_frame({sync_frame_, code_, pc_offset, null_closure_});
if (sync_frame_->is_interpreted()) {
bytecode_ = sync_frame_->LookupDartBytecode();
if (bytecode_.function() == Function::null()) {
continue;
}
const uword pc_offset = sync_frame_->pc() - bytecode_.PayloadStart();
handle_frame(
{sync_frame_, null_code_, bytecode_, pc_offset, null_closure_});
} else {
code_ = sync_frame_->LookupDartCode();
const uword pc_offset = sync_frame_->pc() - code_.PayloadStart();
handle_frame(
{sync_frame_, code_, null_bytecode_, pc_offset, null_closure_});
}
}
sync_frame_ = sync_frames_.NextFrame();
}
const StackTraceUtils::Frame gap_frame = {nullptr,
StubCode::AsynchronousGapMarker(),
/*pc_offset=*/0, null_closure_};
const StackTraceUtils::Frame gap_frame = {
nullptr, StubCode::AsynchronousGapMarker(), null_bytecode_,
/*pc_offset=*/0, null_closure_};
// Traverse awaiter frames.
bool any_async = false;
@@ -336,7 +353,8 @@ void AsyncAwareStackUnwinder::Unwind(
}
handle_frame(gap_frame);
handle_frame({nullptr, code_, pc_offset, awaiter_frame_.closure});
handle_frame(
{nullptr, code_, null_bytecode_, pc_offset, awaiter_frame_.closure});
}
if (any_async) {
+3
View File
@@ -26,6 +26,9 @@ class StackTraceUtils : public AllStatic {
// Code object corresponding to this frame.
const Code& code;
// Bytecode object corresponding to this frame.
const Bytecode& bytecode;
// Offset into the code object corresponding to this frame.
//
// Will be set to |kFutureListenerPcOffset| if this frame corresponds to
+16 -1
View File
@@ -9,6 +9,7 @@
#include "vm/compiler/assembler/disassembler.h"
#include "vm/flags.h"
#include "vm/heap/safepoint.h"
#include "vm/interpreter.h"
#include "vm/object_store.h"
#include "vm/snapshot.h"
#include "vm/virtual_memory.h"
@@ -129,8 +130,22 @@ void StubCode::Cleanup() {
}
}
bool StubCode::InInvocationStub(uword pc) {
bool StubCode::InInvocationStub(uword pc, bool is_interpreted_frame) {
ASSERT(HasBeenInitialized());
#if defined(DART_DYNAMIC_MODULES)
if (is_interpreted_frame) {
// Recognize special marker set up by interpreter in entry frame.
return Interpreter::IsEntryFrameMarker(
reinterpret_cast<const KBCInstr*>(pc));
}
{
uword entry = StubCode::InvokeDartCodeFromBytecode().EntryPoint();
uword size = StubCode::InvokeDartCodeFromBytecodeSize();
if ((pc >= entry) && (pc < (entry + size))) {
return true;
}
}
#endif // defined(DART_DYNAMIC_MODULES)
uword entry = StubCode::InvokeDartCode().EntryPoint();
uword size = StubCode::InvokeDartCodeSize();
return (pc >= entry) && (pc < (entry + size));
+1 -1
View File
@@ -47,7 +47,7 @@ class StubCode : public AllStatic {
// Check if specified pc is in the dart invocation stub used for
// transitioning into dart code.
static bool InInvocationStub(uword pc);
static bool InInvocationStub(uword pc, bool is_interpreted_frame = false);
// Check if the specified pc is in the jump to frame stub.
static bool InJumpToFrameStub(uword pc);
+2
View File
@@ -71,6 +71,7 @@ namespace dart {
V(CloneContext) \
V(CallToRuntime) \
V(LazyCompile) \
V(InterpretCall) \
V(CallBootstrapNative) \
V(CallNoScopeNative) \
V(CallAutoScopeNative) \
@@ -78,6 +79,7 @@ namespace dart {
V(CallStaticFunction) \
V(OptimizeFunction) \
V(InvokeDartCode) \
V(InvokeDartCodeFromBytecode) \
V(DebugStepCheck) \
V(SwitchableCallMiss) \
V(MonomorphicSmiableCheck) \
+1
View File
@@ -33,6 +33,7 @@ class ObjectPointerVisitor;
V(BooleanExpression, "boolean expression") \
V(BoundsCheckForPartialInstantiation, "_boundsCheckForPartialInstantiation") \
V(ByteData, "ByteData") \
V(Bytecode, "Bytecode") \
V(Capability, "Capability") \
V(CheckLoaded, "_checkLoaded") \
V(Class, "Class") \
+1
View File
@@ -377,6 +377,7 @@ DEFINE_TAGGED_POINTER(KernelProgramInfo, Object)
DEFINE_TAGGED_POINTER(WeakSerializationReference, Object)
DEFINE_TAGGED_POINTER(WeakArray, Object)
DEFINE_TAGGED_POINTER(Code, Object)
DEFINE_TAGGED_POINTER(Bytecode, Object)
DEFINE_TAGGED_POINTER(ObjectPool, Object)
DEFINE_TAGGED_POINTER(Instructions, Object)
DEFINE_TAGGED_POINTER(InstructionsSection, Object)
+1
View File
@@ -25,6 +25,7 @@ class RuntimeEntry;
V(ClassLoading) \
V(CompileParseRegExp) \
V(Dart) \
V(DartInterpreted) \
V(GCNewSpace) \
V(GCOldSpace) \
V(GCIdle) \
+20
View File
@@ -41,6 +41,10 @@ Thread::~Thread() {
ASSERT(old_marking_stack_block_ == nullptr);
ASSERT(new_marking_stack_block_ == nullptr);
ASSERT(deferred_marking_stack_block_ == nullptr);
#if defined(DART_DYNAMIC_MODULES)
delete interpreter_;
interpreter_ = nullptr;
#endif
// There should be no top api scopes at this point.
ASSERT(api_top_scope() == nullptr);
// Delete the reusable api scope if there is one.
@@ -981,6 +985,12 @@ void Thread::VisitObjectPointers(ObjectPointerVisitor* visitor,
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&active_stacktrace_));
visitor->VisitPointer(reinterpret_cast<ObjectPtr*>(&sticky_error_));
#if defined(DART_DYNAMIC_MODULES)
if (interpreter() != nullptr) {
interpreter()->VisitObjectPointers(visitor);
}
#endif
// Visit the api local scope as it has all the api local handles.
ApiLocalScope* scope = api_top_scope_;
while (scope != nullptr) {
@@ -1246,6 +1256,11 @@ bool Thread::TopErrorHandlerIsSetJump() const {
// False positives: simulator stack and native stack are unordered.
return true;
#else
#if defined(DART_DYNAMIC_MODULES)
// False positives: interpreter stack and native stack are unordered.
if ((interpreter_ != nullptr) && interpreter_->HasFrame(top_exit_frame_info_))
return true;
#endif
return reinterpret_cast<uword>(long_jump_base()) < top_exit_frame_info_;
#endif
}
@@ -1257,6 +1272,11 @@ bool Thread::TopErrorHandlerIsExitFrame() const {
// False positives: simulator stack and native stack are unordered.
return true;
#else
#if defined(DART_DYNAMIC_MODULES)
// False positives: interpreter stack and native stack are unordered.
if ((interpreter_ != nullptr) && interpreter_->HasFrame(top_exit_frame_info_))
return true;
#endif
return top_exit_frame_info_ < reinterpret_cast<uword>(long_jump_base());
#endif
}
+26
View File
@@ -39,6 +39,7 @@ class CompilerState;
class CompilerTimings;
class Class;
class Code;
class Bytecode;
class Error;
class ExceptionHandlers;
class Field;
@@ -49,6 +50,7 @@ class HandleScope;
class Heap;
class HierarchyInfo;
class Instance;
class Interpreter;
class Isolate;
class IsolateGroup;
class Library;
@@ -69,6 +71,10 @@ class TypeParameter;
class TypeUsageInfo;
class Zone;
namespace bytecode {
class BytecodeLoader;
}
namespace compiler {
namespace target {
class Thread;
@@ -80,6 +86,7 @@ class Thread;
V(Array) \
V(Class) \
V(Code) \
V(Bytecode) \
V(Error) \
V(ExceptionHandlers) \
V(Field) \
@@ -104,6 +111,8 @@ class Thread;
StubCode::FixAllocationStubTarget().ptr(), nullptr) \
V(CodePtr, invoke_dart_code_stub_, StubCode::InvokeDartCode().ptr(), \
nullptr) \
V(CodePtr, invoke_dart_code_from_bytecode_stub_, \
StubCode::InvokeDartCodeFromBytecode().ptr(), nullptr) \
V(CodePtr, call_to_runtime_stub_, StubCode::CallToRuntime().ptr(), nullptr) \
V(CodePtr, late_initialization_error_shared_without_fpu_regs_stub_, \
StubCode::LateInitializationErrorSharedWithoutFPURegs().ptr(), nullptr) \
@@ -247,6 +256,7 @@ class Thread;
NativeEntry::NoScopeNativeCallWrapperEntry(), 0) \
V(uword, auto_scope_native_wrapper_entry_point_, \
NativeEntry::AutoScopeNativeCallWrapperEntry(), 0) \
V(uword, interpret_call_entry_point_, RuntimeEntry::InterpretCallEntry(), 0) \
V(StringPtr*, predefined_symbols_address_, Symbols::PredefinedAddress(), \
nullptr) \
V(uword, double_nan_address_, reinterpret_cast<uword>(&double_nan_constant), \
@@ -1154,6 +1164,16 @@ class Thread : public ThreadState {
return SafepointLevel::kGCAndDeoptAndReload;
}
#if defined(DART_DYNAMIC_MODULES)
Interpreter* interpreter() const { return interpreter_; }
void set_interpreter(Interpreter* value) { interpreter_ = value; }
bytecode::BytecodeLoader* bytecode_loader() const { return bytecode_loader_; }
void set_bytecode_loader(bytecode::BytecodeLoader* value) {
bytecode_loader_ = value;
}
#endif
private:
template <class T>
T* AllocateReusableHandle();
@@ -1407,6 +1427,11 @@ class Thread : public ThreadState {
HeapProfileSampler heap_sampler_;
#endif
#if defined(DART_DYNAMIC_MODULES)
Interpreter* interpreter_ = nullptr;
bytecode::BytecodeLoader* bytecode_loader_ = nullptr;
#endif
explicit Thread(bool is_vm_isolate);
void StoreBufferRelease(
@@ -1472,6 +1497,7 @@ class Thread : public ThreadState {
friend class ApiZone;
friend class ActiveIsolateScope;
friend class Interpreter;
friend class InterruptChecker;
friend class Isolate;
friend class IsolateGroup;
+7
View File
@@ -23,6 +23,8 @@ vm_sources = [
"bootstrap_natives.h",
"bss_relocs.cc",
"bss_relocs.h",
"bytecode_reader.cc",
"bytecode_reader.h",
"canonical_tables.cc",
"canonical_tables.h",
"class_finalizer.cc",
@@ -53,6 +55,8 @@ vm_sources = [
"constants_base.h",
"constants_ia32.cc",
"constants_ia32.h",
"constants_kbc.cc",
"constants_kbc.h",
"constants_riscv.cc",
"constants_riscv.h",
"constants_x64.cc",
@@ -138,6 +142,8 @@ vm_sources = [
"instructions_riscv.h",
"instructions_x64.cc",
"instructions_x64.h",
"interpreter.cc",
"interpreter.h",
"intrusive_dlist.h",
"isolate.cc",
"isolate.h",
@@ -311,6 +317,7 @@ vm_sources = [
"stack_frame_arm.h",
"stack_frame_arm64.h",
"stack_frame_ia32.h",
"stack_frame_kbc.h",
"stack_frame_x64.h",
"stack_trace.cc",
"stack_trace.h",
+4 -1
View File
@@ -445,4 +445,7 @@ external String intern(String str);
@patch
Future<Object?> loadDynamicModule({Uri? uri, Uint8List? bytes}) =>
throw 'Unsupported operation';
Future.value(_loadDynamicModule(bytes!));
@pragma("vm:external-name", "Internal_loadDynamicModule")
external Object? _loadDynamicModule(Uint8List bytes);
+7
View File
@@ -257,6 +257,8 @@ def ToGnArgs(args, mode, arch, target_os, sanitizer, verify_sdk_hash,
enable_code_coverage = args.code_coverage and gn_args['is_clang']
gn_args['dart_vm_code_coverage'] = enable_code_coverage
gn_args['dart_dynamic_modules'] = args.dart_dynamic_modules
gn_args['is_asan'] = sanitizer == 'asan'
gn_args['is_lsan'] = sanitizer == 'lsan'
gn_args['is_msan'] = sanitizer == 'msan'
@@ -496,6 +498,11 @@ def AddCommonGnOptionArgs(parser):
default=False,
dest="code_coverage",
action='store_true')
parser.add_argument('--dart-dynamic-modules',
help='Enable Dart dynamic modules.',
default=False,
dest='dart_dynamic_modules',
action='store_true')
parser.add_argument('--debug-opt-level',
'-d',
help='The optimization level to use for debug builds',