[vm/bytecode] Reuse flow graph building for recognized methods
Change-Id: Ia5aaad97d18d53fc0313713f00ff3403a41ac9c9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107680 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
0abff7b2bb
commit
5f2bcd9d6c
@@ -252,6 +252,110 @@ void BytecodeFlowGraphBuilder::AllocateFixedParameters() {
|
||||
parsed_function()->SetRawParameters(parameters);
|
||||
}
|
||||
|
||||
const KBCInstr*
|
||||
BytecodeFlowGraphBuilder::AllocateParametersAndLocalsForEntryOptional() {
|
||||
ASSERT(KernelBytecode::IsEntryOptionalOpcode(bytecode_instr_));
|
||||
|
||||
const intptr_t num_fixed_params = DecodeOperandA().value();
|
||||
const intptr_t num_opt_pos_params = DecodeOperandB().value();
|
||||
const intptr_t num_opt_named_params = DecodeOperandC().value();
|
||||
|
||||
ASSERT(num_fixed_params == function().num_fixed_parameters());
|
||||
ASSERT(num_opt_pos_params == function().NumOptionalPositionalParameters());
|
||||
ASSERT(num_opt_named_params == function().NumOptionalNamedParameters());
|
||||
|
||||
ASSERT((num_opt_pos_params == 0) || (num_opt_named_params == 0));
|
||||
const intptr_t num_load_const = num_opt_pos_params + 2 * num_opt_named_params;
|
||||
|
||||
const KBCInstr* instr = KernelBytecode::Next(bytecode_instr_);
|
||||
const KBCInstr* frame_instr = instr;
|
||||
for (intptr_t i = 0; i < num_load_const; ++i) {
|
||||
frame_instr = KernelBytecode::Next(frame_instr);
|
||||
}
|
||||
ASSERT(KernelBytecode::IsFrameOpcode(frame_instr));
|
||||
const intptr_t num_extra_locals = KernelBytecode::DecodeD(frame_instr);
|
||||
const intptr_t num_params =
|
||||
num_fixed_params + num_opt_pos_params + num_opt_named_params;
|
||||
const intptr_t total_locals = num_params + num_extra_locals;
|
||||
|
||||
AllocateLocalVariables(Operand(total_locals), num_params);
|
||||
|
||||
ZoneGrowableArray<const Instance*>* default_values =
|
||||
new (Z) ZoneGrowableArray<const Instance*>(
|
||||
Z, num_opt_pos_params + num_opt_named_params);
|
||||
ZoneGrowableArray<LocalVariable*>* raw_parameters =
|
||||
new (Z) ZoneGrowableArray<LocalVariable*>(Z, num_params);
|
||||
|
||||
intptr_t param = 0;
|
||||
for (; param < num_fixed_params; ++param) {
|
||||
LocalVariable* param_var = AllocateParameter(param, VariableIndex(-param));
|
||||
raw_parameters->Add(param_var);
|
||||
}
|
||||
|
||||
for (intptr_t i = 0; i < num_opt_pos_params; ++i, ++param) {
|
||||
const KBCInstr* load_value_instr = instr;
|
||||
instr = KernelBytecode::Next(instr);
|
||||
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value_instr));
|
||||
ASSERT(KernelBytecode::DecodeA(load_value_instr) == param);
|
||||
const Object& default_value =
|
||||
ConstantAt(Operand(KernelBytecode::DecodeE(load_value_instr))).value();
|
||||
|
||||
LocalVariable* param_var = AllocateParameter(param, VariableIndex(-param));
|
||||
raw_parameters->Add(param_var);
|
||||
default_values->Add(
|
||||
&Instance::ZoneHandle(Z, Instance::RawCast(default_value.raw())));
|
||||
}
|
||||
|
||||
if (num_opt_named_params > 0) {
|
||||
default_values->EnsureLength(num_opt_named_params, nullptr);
|
||||
raw_parameters->EnsureLength(num_params, nullptr);
|
||||
|
||||
ASSERT(scratch_var_ != nullptr);
|
||||
|
||||
for (intptr_t i = 0; i < num_opt_named_params; ++i, ++param) {
|
||||
const KBCInstr* load_name_instr = instr;
|
||||
const KBCInstr* load_value_instr = KernelBytecode::Next(load_name_instr);
|
||||
instr = KernelBytecode::Next(load_value_instr);
|
||||
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_name_instr));
|
||||
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value_instr));
|
||||
const String& param_name = String::Cast(
|
||||
ConstantAt(Operand(KernelBytecode::DecodeE(load_name_instr)))
|
||||
.value());
|
||||
ASSERT(param_name.IsSymbol());
|
||||
const Object& default_value =
|
||||
ConstantAt(Operand(KernelBytecode::DecodeE(load_value_instr)))
|
||||
.value();
|
||||
|
||||
intptr_t param_index = num_fixed_params;
|
||||
for (; param_index < num_params; ++param_index) {
|
||||
if (function().ParameterNameAt(param_index) == param_name.raw()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT(param_index < num_params);
|
||||
|
||||
ASSERT(default_values->At(param_index - num_fixed_params) == nullptr);
|
||||
(*default_values)[param_index - num_fixed_params] =
|
||||
&Instance::ZoneHandle(Z, Instance::RawCast(default_value.raw()));
|
||||
|
||||
const intptr_t local_index = KernelBytecode::DecodeA(load_name_instr);
|
||||
ASSERT(local_index == KernelBytecode::DecodeA(load_value_instr));
|
||||
|
||||
LocalVariable* param_var =
|
||||
AllocateParameter(param_index, VariableIndex(-param));
|
||||
ASSERT(raw_parameters->At(param_index) == nullptr);
|
||||
(*raw_parameters)[param_index] = param_var;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(instr == frame_instr);
|
||||
|
||||
parsed_function()->set_default_parameter_values(default_values);
|
||||
parsed_function()->SetRawParameters(raw_parameters);
|
||||
|
||||
return KernelBytecode::Next(frame_instr);
|
||||
}
|
||||
|
||||
LocalVariable* BytecodeFlowGraphBuilder::LocalVariableAt(intptr_t local_index) {
|
||||
ASSERT(!is_generating_interpreter());
|
||||
if (local_index < 0) {
|
||||
@@ -469,104 +573,14 @@ void BytecodeFlowGraphBuilder::BuildEntryOptional() {
|
||||
UNIMPLEMENTED(); // TODO(alexmarkov): interpreter
|
||||
}
|
||||
|
||||
const intptr_t num_fixed_params = DecodeOperandA().value();
|
||||
const intptr_t num_opt_pos_params = DecodeOperandB().value();
|
||||
const intptr_t num_opt_named_params = DecodeOperandC().value();
|
||||
ASSERT(num_fixed_params == function().num_fixed_parameters());
|
||||
ASSERT(num_opt_pos_params == function().NumOptionalPositionalParameters());
|
||||
ASSERT(num_opt_named_params == function().NumOptionalNamedParameters());
|
||||
const KBCInstr* next_instr = AllocateParametersAndLocalsForEntryOptional();
|
||||
|
||||
ASSERT((num_opt_pos_params == 0) || (num_opt_named_params == 0));
|
||||
const intptr_t num_load_const = num_opt_pos_params + 2 * num_opt_named_params;
|
||||
|
||||
const KBCInstr* instr = KernelBytecode::Next(bytecode_instr_);
|
||||
const KBCInstr* frame_instr = instr;
|
||||
for (intptr_t i = 0; i < num_load_const; ++i) {
|
||||
frame_instr = KernelBytecode::Next(frame_instr);
|
||||
}
|
||||
ASSERT(KernelBytecode::IsFrameOpcode(frame_instr));
|
||||
const intptr_t num_extra_locals = KernelBytecode::DecodeD(frame_instr);
|
||||
const intptr_t num_params =
|
||||
num_fixed_params + num_opt_pos_params + num_opt_named_params;
|
||||
const intptr_t total_locals = num_params + num_extra_locals;
|
||||
|
||||
AllocateLocalVariables(Operand(total_locals), num_params);
|
||||
|
||||
ZoneGrowableArray<const Instance*>* default_values =
|
||||
new (Z) ZoneGrowableArray<const Instance*>(
|
||||
Z, num_opt_pos_params + num_opt_named_params);
|
||||
ZoneGrowableArray<LocalVariable*>* raw_parameters =
|
||||
new (Z) ZoneGrowableArray<LocalVariable*>(Z, num_params);
|
||||
LocalVariable* temp_var = nullptr;
|
||||
|
||||
intptr_t param = 0;
|
||||
for (; param < num_fixed_params; ++param) {
|
||||
LocalVariable* param_var = AllocateParameter(param, VariableIndex(-param));
|
||||
raw_parameters->Add(param_var);
|
||||
}
|
||||
|
||||
for (intptr_t i = 0; i < num_opt_pos_params; ++i, ++param) {
|
||||
const KBCInstr* load_value_instr = instr;
|
||||
instr = KernelBytecode::Next(instr);
|
||||
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value_instr));
|
||||
ASSERT(KernelBytecode::DecodeA(load_value_instr) == param);
|
||||
const Object& default_value =
|
||||
ConstantAt(Operand(KernelBytecode::DecodeE(load_value_instr))).value();
|
||||
|
||||
LocalVariable* param_var = AllocateParameter(param, VariableIndex(-param));
|
||||
raw_parameters->Add(param_var);
|
||||
default_values->Add(
|
||||
&Instance::ZoneHandle(Z, Instance::RawCast(default_value.raw())));
|
||||
}
|
||||
|
||||
if (num_opt_named_params > 0) {
|
||||
default_values->EnsureLength(num_opt_named_params, nullptr);
|
||||
raw_parameters->EnsureLength(num_params, nullptr);
|
||||
|
||||
if (function().HasOptionalNamedParameters()) {
|
||||
ASSERT(scratch_var_ != nullptr);
|
||||
temp_var = scratch_var_;
|
||||
|
||||
for (intptr_t i = 0; i < num_opt_named_params; ++i, ++param) {
|
||||
const KBCInstr* load_name_instr = instr;
|
||||
const KBCInstr* load_value_instr = KernelBytecode::Next(load_name_instr);
|
||||
instr = KernelBytecode::Next(load_value_instr);
|
||||
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_name_instr));
|
||||
ASSERT(KernelBytecode::IsLoadConstantOpcode(load_value_instr));
|
||||
const String& param_name = String::Cast(
|
||||
ConstantAt(Operand(KernelBytecode::DecodeE(load_name_instr)))
|
||||
.value());
|
||||
ASSERT(param_name.IsSymbol());
|
||||
const Object& default_value =
|
||||
ConstantAt(Operand(KernelBytecode::DecodeE(load_value_instr)))
|
||||
.value();
|
||||
|
||||
intptr_t param_index = num_fixed_params;
|
||||
for (; param_index < num_params; ++param_index) {
|
||||
if (function().ParameterNameAt(param_index) == param_name.raw()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT(param_index < num_params);
|
||||
|
||||
ASSERT(default_values->At(param_index - num_fixed_params) == nullptr);
|
||||
(*default_values)[param_index - num_fixed_params] =
|
||||
&Instance::ZoneHandle(Z, Instance::RawCast(default_value.raw()));
|
||||
|
||||
const intptr_t local_index = KernelBytecode::DecodeA(load_name_instr);
|
||||
ASSERT(local_index == KernelBytecode::DecodeA(load_value_instr));
|
||||
|
||||
LocalVariable* param_var =
|
||||
AllocateParameter(param_index, VariableIndex(-param));
|
||||
ASSERT(raw_parameters->At(param_index) == nullptr);
|
||||
(*raw_parameters)[param_index] = param_var;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(instr == frame_instr);
|
||||
|
||||
parsed_function()->set_default_parameter_values(default_values);
|
||||
parsed_function()->SetRawParameters(raw_parameters);
|
||||
|
||||
Fragment copy_args_prologue;
|
||||
|
||||
// Code generated for EntryOptional is considered a prologue code.
|
||||
@@ -599,7 +613,7 @@ void BytecodeFlowGraphBuilder::BuildEntryOptional() {
|
||||
PrologueInfo(prologue_entry->block_id(), prologue_exit->block_id() - 1);
|
||||
|
||||
// Skip LoadConstant and Frame instructions.
|
||||
next_pc_ = pc_ + (KernelBytecode::Next(instr) - bytecode_instr_);
|
||||
next_pc_ = pc_ + (next_instr - bytecode_instr_);
|
||||
|
||||
ASSERT(IsStackEmpty());
|
||||
}
|
||||
@@ -892,194 +906,17 @@ void BytecodeFlowGraphBuilder::BuildNativeCall() {
|
||||
}
|
||||
|
||||
ASSERT(function().is_native());
|
||||
B->InlineBailout("BytecodeFlowGraphBuilder::BuildNativeCall");
|
||||
|
||||
// TODO(alexmarkov): find a way to avoid code duplication with
|
||||
// FlowGraphBuilder::NativeFunctionBody.
|
||||
const MethodRecognizer::Kind kind =
|
||||
MethodRecognizer::RecognizeKind(function());
|
||||
switch (kind) {
|
||||
case MethodRecognizer::kObjectEquals:
|
||||
ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
|
||||
code_ += B->StrictCompare(Token::kEQ_STRICT);
|
||||
break;
|
||||
case MethodRecognizer::kStringBaseLength:
|
||||
case MethodRecognizer::kStringBaseIsEmpty:
|
||||
ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
|
||||
code_ += B->LoadNativeField(Slot::String_length());
|
||||
if (kind == MethodRecognizer::kStringBaseIsEmpty) {
|
||||
code_ += B->IntConstant(0);
|
||||
code_ += B->StrictCompare(Token::kEQ_STRICT);
|
||||
}
|
||||
break;
|
||||
case MethodRecognizer::kGrowableArrayLength:
|
||||
ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
|
||||
code_ += B->LoadNativeField(Slot::GrowableObjectArray_length());
|
||||
break;
|
||||
case MethodRecognizer::kObjectArrayLength:
|
||||
case MethodRecognizer::kImmutableArrayLength:
|
||||
ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
|
||||
code_ += B->LoadNativeField(Slot::Array_length());
|
||||
break;
|
||||
case MethodRecognizer::kTypedListLength:
|
||||
case MethodRecognizer::kTypedListViewLength:
|
||||
case MethodRecognizer::kByteDataViewLength:
|
||||
ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
|
||||
code_ += B->LoadNativeField(Slot::TypedDataBase_length());
|
||||
break;
|
||||
case MethodRecognizer::kClassIDgetID:
|
||||
ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
|
||||
code_ += B->LoadClassId();
|
||||
break;
|
||||
case MethodRecognizer::kGrowableArrayCapacity:
|
||||
ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
|
||||
code_ += B->LoadNativeField(Slot::GrowableObjectArray_data());
|
||||
code_ += B->LoadNativeField(Slot::Array_length());
|
||||
break;
|
||||
case MethodRecognizer::kListFactory: {
|
||||
ASSERT((function().NumParameters() == 2) && !function().IsGeneric() &&
|
||||
function().HasOptionalParameters());
|
||||
ASSERT(scratch_var_ != nullptr);
|
||||
// Generate code that performs:
|
||||
//
|
||||
// factory List<E>([int length]) {
|
||||
// return (:arg_desc.positional_count == 2) ? new _List<E>(length)
|
||||
// : new _GrowableList<E>(0);
|
||||
// }
|
||||
const auto& core_lib = Library::Handle(Z, Library::CoreLibrary());
|
||||
|
||||
TargetEntryInstr *allocate_non_growable, *allocate_growable;
|
||||
|
||||
code_ += B->Drop(); // Drop 'length'.
|
||||
code_ += B->Drop(); // Drop 'type arguments'.
|
||||
code_ += B->LoadArgDescriptor();
|
||||
code_ += B->LoadNativeField(Slot::ArgumentsDescriptor_positional_count());
|
||||
code_ += B->IntConstant(2);
|
||||
code_ +=
|
||||
B->BranchIfStrictEqual(&allocate_non_growable, &allocate_growable);
|
||||
|
||||
JoinEntryInstr* join = B->BuildJoinEntry();
|
||||
|
||||
{
|
||||
const auto& cls = Class::Handle(
|
||||
Z, core_lib.LookupClass(
|
||||
Library::PrivateCoreLibName(Symbols::_List())));
|
||||
ASSERT(!cls.IsNull());
|
||||
const auto& func = Function::ZoneHandle(
|
||||
Z, cls.LookupFactoryAllowPrivate(Symbols::_ListFactory()));
|
||||
ASSERT(!func.IsNull());
|
||||
|
||||
code_ = Fragment(allocate_non_growable);
|
||||
code_ += B->LoadLocal(LocalVariableAt(0));
|
||||
code_ += B->LoadLocal(LocalVariableAt(1));
|
||||
auto* call = new (Z) StaticCallInstr(
|
||||
TokenPosition::kNoSource, func, 0, Array::null_array(),
|
||||
GetArguments(2), *ic_data_array_, B->GetNextDeoptId(),
|
||||
ICData::kStatic);
|
||||
code_ <<= call;
|
||||
B->Push(call);
|
||||
code_ += B->StoreLocal(TokenPosition::kNoSource, scratch_var_);
|
||||
code_ += B->Drop();
|
||||
code_ += B->Goto(join);
|
||||
}
|
||||
|
||||
{
|
||||
const auto& cls = Class::Handle(
|
||||
Z, core_lib.LookupClass(
|
||||
Library::PrivateCoreLibName(Symbols::_GrowableList())));
|
||||
ASSERT(!cls.IsNull());
|
||||
const auto& func = Function::ZoneHandle(
|
||||
Z, cls.LookupFactoryAllowPrivate(Symbols::_GrowableListFactory()));
|
||||
ASSERT(!func.IsNull());
|
||||
|
||||
code_ = Fragment(allocate_growable);
|
||||
code_ += B->LoadLocal(LocalVariableAt(0));
|
||||
code_ += B->IntConstant(0);
|
||||
auto* call = new (Z) StaticCallInstr(
|
||||
TokenPosition::kNoSource, func, 0, Array::null_array(),
|
||||
GetArguments(2), *ic_data_array_, B->GetNextDeoptId(),
|
||||
ICData::kStatic);
|
||||
code_ <<= call;
|
||||
B->Push(call);
|
||||
code_ += B->StoreLocal(TokenPosition::kNoSource, scratch_var_);
|
||||
code_ += B->Drop();
|
||||
code_ += B->Goto(join);
|
||||
}
|
||||
|
||||
code_ = Fragment(join);
|
||||
code_ += B->LoadLocal(scratch_var_);
|
||||
break;
|
||||
}
|
||||
case MethodRecognizer::kObjectArrayAllocate:
|
||||
ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
|
||||
code_ += B->CreateArray();
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_getIndex:
|
||||
ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
|
||||
code_ += B->LoadNativeField(Slot::LinkedHashMap_index());
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_setIndex:
|
||||
ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
|
||||
code_ += B->StoreInstanceField(TokenPosition::kNoSource,
|
||||
Slot::LinkedHashMap_index());
|
||||
code_ += B->NullConstant();
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_getData:
|
||||
ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
|
||||
code_ += B->LoadNativeField(Slot::LinkedHashMap_data());
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_setData:
|
||||
ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
|
||||
code_ += B->StoreInstanceField(TokenPosition::kNoSource,
|
||||
Slot::LinkedHashMap_data());
|
||||
code_ += B->NullConstant();
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_getHashMask:
|
||||
ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
|
||||
code_ += B->LoadNativeField(Slot::LinkedHashMap_hash_mask());
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_setHashMask:
|
||||
ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
|
||||
code_ += B->StoreInstanceField(TokenPosition::kNoSource,
|
||||
Slot::LinkedHashMap_hash_mask(),
|
||||
kNoStoreBarrier);
|
||||
code_ += B->NullConstant();
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_getUsedData:
|
||||
ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
|
||||
code_ += B->LoadNativeField(Slot::LinkedHashMap_used_data());
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_setUsedData:
|
||||
ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
|
||||
code_ += B->StoreInstanceField(TokenPosition::kNoSource,
|
||||
Slot::LinkedHashMap_used_data(),
|
||||
kNoStoreBarrier);
|
||||
code_ += B->NullConstant();
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_getDeletedKeys:
|
||||
ASSERT((function().NumParameters() == 1) && !function().IsGeneric());
|
||||
code_ += B->LoadNativeField(Slot::LinkedHashMap_deleted_keys());
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_setDeletedKeys:
|
||||
ASSERT((function().NumParameters() == 2) && !function().IsGeneric());
|
||||
code_ += B->StoreInstanceField(TokenPosition::kNoSource,
|
||||
Slot::LinkedHashMap_deleted_keys(),
|
||||
kNoStoreBarrier);
|
||||
code_ += B->NullConstant();
|
||||
break;
|
||||
default: {
|
||||
B->InlineBailout("BytecodeFlowGraphBuilder::BuildNativeCall");
|
||||
const auto& name = String::ZoneHandle(Z, function().native_name());
|
||||
const intptr_t num_args =
|
||||
function().NumParameters() + (function().IsGeneric() ? 1 : 0);
|
||||
ArgumentArray arguments = GetArguments(num_args);
|
||||
auto* call =
|
||||
new (Z) NativeCallInstr(&name, &function(), FLAG_link_natives_lazily,
|
||||
function().end_token_pos(), arguments);
|
||||
code_ <<= call;
|
||||
B->Push(call);
|
||||
break;
|
||||
}
|
||||
}
|
||||
const auto& name = String::ZoneHandle(Z, function().native_name());
|
||||
const intptr_t num_args =
|
||||
function().NumParameters() + (function().IsGeneric() ? 1 : 0);
|
||||
ArgumentArray arguments = GetArguments(num_args);
|
||||
auto* call =
|
||||
new (Z) NativeCallInstr(&name, &function(), FLAG_link_natives_lazily,
|
||||
function().end_token_pos(), arguments);
|
||||
code_ <<= call;
|
||||
B->Push(call);
|
||||
}
|
||||
|
||||
void BytecodeFlowGraphBuilder::BuildAllocate() {
|
||||
@@ -1894,6 +1731,25 @@ void BytecodeFlowGraphBuilder::CollectControlFlow(
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeFlowGraphBuilder::CreateParameterVariables() {
|
||||
const Bytecode& bytecode = Bytecode::Handle(Z, function().bytecode());
|
||||
object_pool_ = bytecode.object_pool();
|
||||
bytecode_instr_ = reinterpret_cast<const KBCInstr*>(bytecode.PayloadStart());
|
||||
|
||||
if (KernelBytecode::IsEntryOptionalOpcode(bytecode_instr_)) {
|
||||
scratch_var_ = parsed_function_->EnsureExpressionTemp();
|
||||
AllocateParametersAndLocalsForEntryOptional();
|
||||
} else if (KernelBytecode::IsEntryOpcode(bytecode_instr_)) {
|
||||
AllocateLocalVariables(DecodeOperandD());
|
||||
AllocateFixedParameters();
|
||||
} else if (KernelBytecode::IsEntryFixedOpcode(bytecode_instr_)) {
|
||||
AllocateLocalVariables(DecodeOperandE());
|
||||
AllocateFixedParameters();
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
FlowGraph* BytecodeFlowGraphBuilder::BuildGraph() {
|
||||
const Bytecode& bytecode = Bytecode::Handle(Z, function().bytecode());
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ class BytecodeFlowGraphBuilder {
|
||||
|
||||
FlowGraph* BuildGraph();
|
||||
|
||||
// Create parameter variables without building a flow graph.
|
||||
void CreateParameterVariables();
|
||||
|
||||
protected:
|
||||
// Returns `true` if building a flow graph for a bytecode interpreter, or
|
||||
// `false` if compiling a function from bytecode.
|
||||
@@ -122,6 +125,12 @@ class BytecodeFlowGraphBuilder {
|
||||
LocalVariable* AllocateParameter(intptr_t param_index,
|
||||
VariableIndex var_index);
|
||||
void AllocateFixedParameters();
|
||||
|
||||
// Allocates parameters and local variables in case of EntryOptional.
|
||||
// Returns pointer to the instruction after EntryOptional/LoadConstant/Frame
|
||||
// bytecodes.
|
||||
const KBCInstr* AllocateParametersAndLocalsForEntryOptional();
|
||||
|
||||
LocalVariable* LocalVariableAt(intptr_t local_index);
|
||||
void StoreLocal(Operand local_index);
|
||||
void LoadLocal(Operand local_index);
|
||||
|
||||
@@ -774,12 +774,6 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(
|
||||
bool is_constructor) {
|
||||
const Function& dart_function = parsed_function()->function();
|
||||
|
||||
// The prologue builder needs the default parameter values.
|
||||
SetupDefaultParameterValues();
|
||||
// TypeArgumentsHandling / BuildDefaultTypeHandling needs
|
||||
// default function type arguments.
|
||||
ReadDefaultFunctionTypeArguments(dart_function);
|
||||
|
||||
intptr_t type_parameters_offset = 0;
|
||||
LocalVariable* first_parameter = nullptr;
|
||||
TokenPosition token_position = TokenPosition::kNoSource;
|
||||
@@ -961,9 +955,16 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraph() {
|
||||
}
|
||||
|
||||
ASSERT(function.HasBytecode());
|
||||
|
||||
BytecodeFlowGraphBuilder bytecode_compiler(
|
||||
flow_graph_builder_, parsed_function(),
|
||||
&(flow_graph_builder_->ic_data_array_));
|
||||
|
||||
if (B->IsRecognizedMethodForFlowGraph(function)) {
|
||||
bytecode_compiler.CreateParameterVariables();
|
||||
return B->BuildGraphOfRecognizedMethod(function);
|
||||
}
|
||||
|
||||
return bytecode_compiler.BuildGraph();
|
||||
}
|
||||
|
||||
@@ -983,13 +984,12 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraph() {
|
||||
case RawFunction::kRegularFunction:
|
||||
case RawFunction::kGetterFunction:
|
||||
case RawFunction::kSetterFunction:
|
||||
case RawFunction::kClosureFunction: {
|
||||
ReadUntilFunctionNode();
|
||||
return BuildGraphOfFunction(false);
|
||||
}
|
||||
case RawFunction::kClosureFunction:
|
||||
case RawFunction::kConstructor: {
|
||||
ReadUntilFunctionNode();
|
||||
return BuildGraphOfFunction(!function.IsFactory());
|
||||
if (B->IsRecognizedMethodForFlowGraph(function)) {
|
||||
return B->BuildGraphOfRecognizedMethod(function);
|
||||
}
|
||||
return BuildGraphOfFunction(function.IsGenerativeConstructor());
|
||||
}
|
||||
case RawFunction::kImplicitGetter:
|
||||
case RawFunction::kImplicitStaticGetter:
|
||||
@@ -1053,6 +1053,11 @@ void StreamingFlowGraphBuilder::ParseKernelASTFunction() {
|
||||
case RawFunction::kSetterFunction:
|
||||
case RawFunction::kClosureFunction:
|
||||
case RawFunction::kConstructor:
|
||||
case RawFunction::kImplicitClosureFunction:
|
||||
ReadUntilFunctionNode();
|
||||
SetupDefaultParameterValues();
|
||||
ReadDefaultFunctionTypeArguments(function);
|
||||
break;
|
||||
case RawFunction::kImplicitGetter:
|
||||
case RawFunction::kImplicitStaticGetter:
|
||||
case RawFunction::kImplicitSetter:
|
||||
@@ -1062,11 +1067,6 @@ void StreamingFlowGraphBuilder::ParseKernelASTFunction() {
|
||||
case RawFunction::kInvokeFieldDispatcher:
|
||||
case RawFunction::kFfiTrampoline:
|
||||
break;
|
||||
case RawFunction::kImplicitClosureFunction:
|
||||
ReadUntilFunctionNode();
|
||||
SetupDefaultParameterValues();
|
||||
ReadDefaultFunctionTypeArguments(function);
|
||||
break;
|
||||
case RawFunction::kDynamicInvocationForwarder:
|
||||
if (PeekTag() != kField) {
|
||||
ReadUntilFunctionNode();
|
||||
|
||||
@@ -741,13 +741,99 @@ FlowGraph* FlowGraphBuilder::BuildGraph() {
|
||||
Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
|
||||
LocalVariable* first_parameter) {
|
||||
ASSERT(function.is_native());
|
||||
// We explicitly build the graph for native functions in the same way that the
|
||||
// from-source backend does. We should find a way to have a single component
|
||||
// to build these graphs so that this code is not duplicated.
|
||||
ASSERT(!IsRecognizedMethodForFlowGraph(function));
|
||||
|
||||
Fragment body;
|
||||
String& name = String::ZoneHandle(Z, function.native_name());
|
||||
if (function.IsGeneric()) {
|
||||
body += LoadLocal(parsed_function_->RawTypeArgumentsVariable());
|
||||
body += PushArgument();
|
||||
}
|
||||
for (intptr_t i = 0; i < function.NumParameters(); ++i) {
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(i));
|
||||
body += PushArgument();
|
||||
}
|
||||
body += NativeCall(&name, &function);
|
||||
// We typecheck results of native calls for type safety.
|
||||
body +=
|
||||
Return(TokenPosition::kNoSource, /* omit_result_type_check = */ false);
|
||||
return body;
|
||||
}
|
||||
|
||||
bool FlowGraphBuilder::IsRecognizedMethodForFlowGraph(
|
||||
const Function& function) {
|
||||
const MethodRecognizer::Kind kind = MethodRecognizer::RecognizeKind(function);
|
||||
|
||||
switch (kind) {
|
||||
// On simdbc we fall back to natives.
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
case MethodRecognizer::kTypedData_ByteDataView_factory:
|
||||
case MethodRecognizer::kTypedData_Int8ArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Uint8ArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Uint8ClampedArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Int16ArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Uint16ArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Int32ArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Uint32ArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Int64ArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Uint64ArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Float32ArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Float64ArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Float32x4ArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Int32x4ArrayView_factory:
|
||||
case MethodRecognizer::kTypedData_Float64x2ArrayView_factory:
|
||||
#endif // !defined(TARGET_ARCH_DBC)
|
||||
case MethodRecognizer::kObjectEquals:
|
||||
case MethodRecognizer::kStringBaseLength:
|
||||
case MethodRecognizer::kStringBaseIsEmpty:
|
||||
case MethodRecognizer::kGrowableArrayLength:
|
||||
case MethodRecognizer::kObjectArrayLength:
|
||||
case MethodRecognizer::kImmutableArrayLength:
|
||||
case MethodRecognizer::kTypedListLength:
|
||||
case MethodRecognizer::kTypedListViewLength:
|
||||
case MethodRecognizer::kByteDataViewLength:
|
||||
case MethodRecognizer::kByteDataViewOffsetInBytes:
|
||||
case MethodRecognizer::kTypedDataViewOffsetInBytes:
|
||||
case MethodRecognizer::kByteDataViewTypedData:
|
||||
case MethodRecognizer::kTypedDataViewTypedData:
|
||||
case MethodRecognizer::kClassIDgetID:
|
||||
case MethodRecognizer::kGrowableArrayCapacity:
|
||||
case MethodRecognizer::kListFactory:
|
||||
case MethodRecognizer::kObjectArrayAllocate:
|
||||
case MethodRecognizer::kLinkedHashMap_getIndex:
|
||||
case MethodRecognizer::kLinkedHashMap_setIndex:
|
||||
case MethodRecognizer::kLinkedHashMap_getData:
|
||||
case MethodRecognizer::kLinkedHashMap_setData:
|
||||
case MethodRecognizer::kLinkedHashMap_getHashMask:
|
||||
case MethodRecognizer::kLinkedHashMap_setHashMask:
|
||||
case MethodRecognizer::kLinkedHashMap_getUsedData:
|
||||
case MethodRecognizer::kLinkedHashMap_setUsedData:
|
||||
case MethodRecognizer::kLinkedHashMap_getDeletedKeys:
|
||||
case MethodRecognizer::kLinkedHashMap_setDeletedKeys:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod(
|
||||
const Function& function) {
|
||||
ASSERT(IsRecognizedMethodForFlowGraph(function));
|
||||
|
||||
graph_entry_ =
|
||||
new (Z) GraphEntryInstr(*parsed_function_, Compiler::kNoOSRDeoptId);
|
||||
|
||||
auto normal_entry = BuildFunctionEntry(graph_entry_);
|
||||
graph_entry_->set_normal_entry(normal_entry);
|
||||
|
||||
PrologueInfo prologue_info(-1, -1);
|
||||
BlockEntryInstr* instruction_cursor =
|
||||
BuildPrologue(normal_entry, &prologue_info);
|
||||
|
||||
Fragment body(instruction_cursor);
|
||||
body += CheckStackOverflowInPrologue(function.token_pos());
|
||||
|
||||
const MethodRecognizer::Kind kind = MethodRecognizer::RecognizeKind(function);
|
||||
bool omit_result_type_check = true;
|
||||
switch (kind) {
|
||||
// On simdbc we fall back to natives.
|
||||
#if !defined(TARGET_ARCH_DBC)
|
||||
@@ -812,13 +898,13 @@ Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
|
||||
break;
|
||||
#endif // !defined(TARGET_ARCH_DBC)
|
||||
case MethodRecognizer::kObjectEquals:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(first_parameter);
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(1));
|
||||
body += StrictCompare(Token::kEQ_STRICT);
|
||||
break;
|
||||
case MethodRecognizer::kStringBaseLength:
|
||||
case MethodRecognizer::kStringBaseIsEmpty:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(Slot::String_length());
|
||||
if (kind == MethodRecognizer::kStringBaseIsEmpty) {
|
||||
body += IntConstant(0);
|
||||
@@ -826,36 +912,36 @@ Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
|
||||
}
|
||||
break;
|
||||
case MethodRecognizer::kGrowableArrayLength:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(Slot::GrowableObjectArray_length());
|
||||
break;
|
||||
case MethodRecognizer::kObjectArrayLength:
|
||||
case MethodRecognizer::kImmutableArrayLength:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(Slot::Array_length());
|
||||
break;
|
||||
case MethodRecognizer::kTypedListLength:
|
||||
case MethodRecognizer::kTypedListViewLength:
|
||||
case MethodRecognizer::kByteDataViewLength:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(Slot::TypedDataBase_length());
|
||||
break;
|
||||
case MethodRecognizer::kByteDataViewOffsetInBytes:
|
||||
case MethodRecognizer::kTypedDataViewOffsetInBytes:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(Slot::TypedDataView_offset_in_bytes());
|
||||
break;
|
||||
case MethodRecognizer::kByteDataViewTypedData:
|
||||
case MethodRecognizer::kTypedDataViewTypedData:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(Slot::TypedDataView_data());
|
||||
break;
|
||||
case MethodRecognizer::kClassIDgetID:
|
||||
body += LoadLocal(first_parameter);
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadClassId();
|
||||
break;
|
||||
case MethodRecognizer::kGrowableArrayCapacity:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(Slot::GrowableObjectArray_data());
|
||||
body += LoadNativeField(Slot::Array_length());
|
||||
break;
|
||||
@@ -885,9 +971,9 @@ Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
|
||||
ASSERT(!func.IsNull());
|
||||
|
||||
Fragment allocate(allocate_non_growable);
|
||||
allocate += LoadLocal(scopes_->type_arguments_variable);
|
||||
allocate += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
allocate += PushArgument();
|
||||
allocate += LoadLocal(first_parameter);
|
||||
allocate += LoadLocal(parsed_function_->RawParameterVariable(1));
|
||||
allocate += PushArgument();
|
||||
allocate +=
|
||||
StaticCall(TokenPosition::kNoSource, func, 2, ICData::kStatic);
|
||||
@@ -907,7 +993,7 @@ Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
|
||||
ASSERT(!func.IsNull());
|
||||
|
||||
Fragment allocate(allocate_growable);
|
||||
allocate += LoadLocal(scopes_->type_arguments_variable);
|
||||
allocate += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
allocate += PushArgument();
|
||||
allocate += IntConstant(0);
|
||||
allocate += PushArgument();
|
||||
@@ -924,85 +1010,78 @@ Fragment FlowGraphBuilder::NativeFunctionBody(const Function& function,
|
||||
break;
|
||||
}
|
||||
case MethodRecognizer::kObjectArrayAllocate:
|
||||
body += LoadLocal(scopes_->type_arguments_variable);
|
||||
body += LoadLocal(first_parameter);
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(1));
|
||||
body += CreateArray();
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_getIndex:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(Slot::LinkedHashMap_index());
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_setIndex:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(first_parameter);
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(1));
|
||||
body += StoreInstanceField(TokenPosition::kNoSource,
|
||||
Slot::LinkedHashMap_index());
|
||||
body += NullConstant();
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_getData:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(Slot::LinkedHashMap_data());
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_setData:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(first_parameter);
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(1));
|
||||
body += StoreInstanceField(TokenPosition::kNoSource,
|
||||
Slot::LinkedHashMap_data());
|
||||
body += NullConstant();
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_getHashMask:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(Slot::LinkedHashMap_hash_mask());
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_setHashMask:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(first_parameter);
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(1));
|
||||
body +=
|
||||
StoreInstanceField(TokenPosition::kNoSource,
|
||||
Slot::LinkedHashMap_hash_mask(), kNoStoreBarrier);
|
||||
body += NullConstant();
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_getUsedData:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(Slot::LinkedHashMap_used_data());
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_setUsedData:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(first_parameter);
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(1));
|
||||
body +=
|
||||
StoreInstanceField(TokenPosition::kNoSource,
|
||||
Slot::LinkedHashMap_used_data(), kNoStoreBarrier);
|
||||
body += NullConstant();
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_getDeletedKeys:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadNativeField(Slot::LinkedHashMap_deleted_keys());
|
||||
break;
|
||||
case MethodRecognizer::kLinkedHashMap_setDeletedKeys:
|
||||
body += LoadLocal(parsed_function_->receiver_var());
|
||||
body += LoadLocal(first_parameter);
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(0));
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(1));
|
||||
body += StoreInstanceField(TokenPosition::kNoSource,
|
||||
Slot::LinkedHashMap_deleted_keys(),
|
||||
kNoStoreBarrier);
|
||||
body += NullConstant();
|
||||
break;
|
||||
default: {
|
||||
String& name = String::ZoneHandle(Z, function.native_name());
|
||||
if (function.IsGeneric()) {
|
||||
body += LoadLocal(parsed_function_->RawTypeArgumentsVariable());
|
||||
body += PushArgument();
|
||||
}
|
||||
for (intptr_t i = 0; i < function.NumParameters(); ++i) {
|
||||
body += LoadLocal(parsed_function_->RawParameterVariable(i));
|
||||
body += PushArgument();
|
||||
}
|
||||
body += NativeCall(&name, &function);
|
||||
// We typecheck results of native calls for type safety.
|
||||
omit_result_type_check = false;
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return body + Return(TokenPosition::kNoSource, omit_result_type_check);
|
||||
|
||||
body += Return(TokenPosition::kNoSource, /* omit_result_type_check = */ true);
|
||||
|
||||
return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
|
||||
prologue_info);
|
||||
}
|
||||
|
||||
Fragment FlowGraphBuilder::BuildTypedDataViewFactoryConstructor(
|
||||
|
||||
@@ -107,6 +107,9 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
|
||||
Fragment NativeFunctionBody(const Function& function,
|
||||
LocalVariable* first_parameter);
|
||||
|
||||
bool IsRecognizedMethodForFlowGraph(const Function& function);
|
||||
FlowGraph* BuildGraphOfRecognizedMethod(const Function& function);
|
||||
|
||||
Fragment BuildTypedDataViewFactoryConstructor(const Function& function,
|
||||
classid_t cid);
|
||||
|
||||
|
||||
@@ -901,6 +901,26 @@ class KernelBytecode {
|
||||
return DecodeOpcode(instr) == KernelBytecode::kCheckStack;
|
||||
}
|
||||
|
||||
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 IsEntryFixedOpcode(const KBCInstr* instr) {
|
||||
switch (DecodeOpcode(instr)) {
|
||||
case KernelBytecode::kEntryFixed:
|
||||
case KernelBytecode::kEntryFixed_Wide:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DART_FORCE_INLINE static bool IsEntryOptionalOpcode(const KBCInstr* instr) {
|
||||
return DecodeOpcode(instr) == KernelBytecode::kEntryOptional;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user