e4196ce8c6
Second sub-CL of https://dart-review.googlesource.com/c/sdk/+/100644 The first was here https://dart-review.googlesource.com/c/sdk/+/103487 Now that offsets_extractor is checked in, and we have a big header full of hard coded constants, the next step is to make sure everything is using those constants. This is essentially everything in the original CL, except the new simarm_x64 architecture. Bug: https://github.com/dart-lang/sdk/issues/36839 Change-Id: I236e4f30aa1df6d92209891c983b792d1835b608 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104286 Commit-Queue: Liam Appelbe <liama@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
488 lines
18 KiB
C++
488 lines
18 KiB
C++
// Copyright (c) 2017, 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/compiler/frontend/prologue_builder.h"
|
|
|
|
#include "vm/compiler/backend/il.h"
|
|
#include "vm/compiler/backend/il_printer.h"
|
|
#include "vm/compiler/frontend/base_flow_graph_builder.h"
|
|
#include "vm/compiler/jit/compiler.h"
|
|
#include "vm/kernel_loader.h"
|
|
#include "vm/longjump.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/report.h"
|
|
#include "vm/resolver.h"
|
|
#include "vm/stack_frame.h"
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
namespace dart {
|
|
namespace kernel {
|
|
|
|
#define Z (zone_)
|
|
|
|
// Returns static type of the parameter if it can be trusted (was type checked
|
|
// by caller) and dynamic otherwise.
|
|
static CompileType ParameterType(LocalVariable* param) {
|
|
return param->was_type_checked_by_caller()
|
|
? CompileType::FromAbstractType(param->type())
|
|
: CompileType::Dynamic();
|
|
}
|
|
|
|
bool PrologueBuilder::PrologueSkippableOnUncheckedEntry(
|
|
const Function& function) {
|
|
return !function.HasOptionalParameters() &&
|
|
!function.IsNonImplicitClosureFunction() && !function.IsGeneric();
|
|
}
|
|
|
|
bool PrologueBuilder::HasEmptyPrologue(const Function& function) {
|
|
return !function.HasOptionalParameters() && !function.IsGeneric() &&
|
|
!function.CanReceiveDynamicInvocation();
|
|
}
|
|
|
|
BlockEntryInstr* PrologueBuilder::BuildPrologue(BlockEntryInstr* entry,
|
|
PrologueInfo* prologue_info) {
|
|
// We always have to build the graph, but we only link it sometimes.
|
|
const bool link = !is_inlining_ && !compiling_for_osr_;
|
|
|
|
const intptr_t previous_block_id = last_used_block_id_;
|
|
|
|
const bool load_optional_arguments = function_.HasOptionalParameters();
|
|
const bool expect_type_args = function_.IsGeneric();
|
|
const bool check_arguments = function_.CanReceiveDynamicInvocation();
|
|
|
|
Fragment prologue = Fragment(entry);
|
|
JoinEntryInstr* nsm = NULL;
|
|
if (load_optional_arguments || check_arguments || expect_type_args) {
|
|
nsm = BuildThrowNoSuchMethod();
|
|
}
|
|
if (check_arguments) {
|
|
Fragment f = BuildTypeArgumentsLengthCheck(nsm, expect_type_args);
|
|
if (link) prologue += f;
|
|
}
|
|
if (load_optional_arguments) {
|
|
Fragment f = BuildOptionalParameterHandling(
|
|
nsm, parsed_function_->expression_temp_var());
|
|
if (link) prologue += f;
|
|
} else if (check_arguments) {
|
|
Fragment f = BuildFixedParameterLengthChecks(nsm);
|
|
if (link) prologue += f;
|
|
}
|
|
if (function_.IsClosureFunction()) {
|
|
Fragment f = BuildClosureContextHandling();
|
|
if (!compiling_for_osr_) prologue += f;
|
|
}
|
|
if (expect_type_args) {
|
|
Fragment f = BuildTypeArgumentsHandling(nsm);
|
|
if (link) prologue += f;
|
|
}
|
|
|
|
const bool is_empty_prologue = prologue.entry == prologue.current;
|
|
|
|
// Always do this to preserve deoptid numbering.
|
|
JoinEntryInstr* normal_code = BuildJoinEntry();
|
|
Fragment jump_to_normal_code = Goto(normal_code);
|
|
|
|
if (is_empty_prologue) {
|
|
*prologue_info = PrologueInfo(-1, -1);
|
|
return entry;
|
|
} else {
|
|
prologue += jump_to_normal_code;
|
|
*prologue_info =
|
|
PrologueInfo(previous_block_id, normal_code->block_id() - 1);
|
|
return normal_code;
|
|
}
|
|
}
|
|
|
|
Fragment PrologueBuilder::BuildTypeArgumentsLengthCheck(JoinEntryInstr* nsm,
|
|
bool expect_type_args) {
|
|
Fragment check_type_args;
|
|
JoinEntryInstr* done = BuildJoinEntry();
|
|
|
|
// Type args are always optional, so length can always be zero.
|
|
// If expect_type_args, a non-zero length must match the declaration length.
|
|
TargetEntryInstr *then, *fail;
|
|
check_type_args += LoadArgDescriptor();
|
|
check_type_args += LoadNativeField(Slot::ArgumentsDescriptor_type_args_len());
|
|
if (expect_type_args) {
|
|
JoinEntryInstr* join2 = BuildJoinEntry();
|
|
|
|
LocalVariable* len = MakeTemporary();
|
|
|
|
TargetEntryInstr* otherwise;
|
|
check_type_args += LoadLocal(len);
|
|
check_type_args += IntConstant(0);
|
|
check_type_args += BranchIfEqual(&then, &otherwise);
|
|
|
|
TargetEntryInstr* then2;
|
|
Fragment check_len(otherwise);
|
|
check_len += LoadLocal(len);
|
|
check_len += IntConstant(function_.NumTypeParameters());
|
|
check_len += BranchIfEqual(&then2, &fail);
|
|
|
|
Fragment(then) + Goto(join2);
|
|
Fragment(then2) + Goto(join2);
|
|
|
|
Fragment(join2) + Drop() + Goto(done);
|
|
Fragment(fail) + Goto(nsm);
|
|
} else {
|
|
check_type_args += IntConstant(0);
|
|
check_type_args += BranchIfEqual(&then, &fail);
|
|
Fragment(then) + Goto(done);
|
|
Fragment(fail) + Goto(nsm);
|
|
}
|
|
|
|
return Fragment(check_type_args.entry, done);
|
|
}
|
|
|
|
Fragment PrologueBuilder::BuildOptionalParameterHandling(
|
|
JoinEntryInstr* nsm,
|
|
LocalVariable* temp_var) {
|
|
Fragment copy_args_prologue;
|
|
const int num_fixed_params = function_.num_fixed_parameters();
|
|
const int num_opt_pos_params = function_.NumOptionalPositionalParameters();
|
|
const int num_opt_named_params = function_.NumOptionalNamedParameters();
|
|
const int num_params =
|
|
num_fixed_params + num_opt_pos_params + num_opt_named_params;
|
|
ASSERT(function_.NumParameters() == num_params);
|
|
|
|
// Check that min_num_pos_args <= num_pos_args <= max_num_pos_args,
|
|
// where num_pos_args is the number of positional arguments passed in.
|
|
const int min_num_pos_args = num_fixed_params;
|
|
const int max_num_pos_args = num_fixed_params + num_opt_pos_params;
|
|
|
|
copy_args_prologue += LoadArgDescriptor();
|
|
copy_args_prologue +=
|
|
LoadNativeField(Slot::ArgumentsDescriptor_positional_count());
|
|
LocalVariable* positional_count_var = MakeTemporary();
|
|
|
|
copy_args_prologue += LoadArgDescriptor();
|
|
copy_args_prologue += LoadNativeField(Slot::ArgumentsDescriptor_count());
|
|
LocalVariable* count_var = MakeTemporary();
|
|
|
|
// Ensure the caller provided at least [min_num_pos_args] arguments.
|
|
copy_args_prologue += IntConstant(min_num_pos_args);
|
|
copy_args_prologue += LoadLocal(positional_count_var);
|
|
copy_args_prologue += SmiRelationalOp(Token::kLTE);
|
|
TargetEntryInstr *success1, *fail1;
|
|
copy_args_prologue += BranchIfTrue(&success1, &fail1);
|
|
copy_args_prologue = Fragment(copy_args_prologue.entry, success1);
|
|
|
|
// Ensure the caller provided at most [max_num_pos_args] arguments.
|
|
copy_args_prologue += LoadLocal(positional_count_var);
|
|
copy_args_prologue += IntConstant(max_num_pos_args);
|
|
copy_args_prologue += SmiRelationalOp(Token::kLTE);
|
|
TargetEntryInstr *success2, *fail2;
|
|
copy_args_prologue += BranchIfTrue(&success2, &fail2);
|
|
copy_args_prologue = Fragment(copy_args_prologue.entry, success2);
|
|
|
|
// Link up the argument check failing code.
|
|
Fragment(fail1) + Goto(nsm);
|
|
Fragment(fail2) + Goto(nsm);
|
|
|
|
copy_args_prologue += LoadLocal(count_var);
|
|
copy_args_prologue += IntConstant(min_num_pos_args);
|
|
copy_args_prologue += SmiBinaryOp(Token::kSUB, /* truncate= */ true);
|
|
LocalVariable* optional_count_var = MakeTemporary();
|
|
|
|
// Copy mandatory parameters down.
|
|
intptr_t param = 0;
|
|
for (; param < num_fixed_params; ++param) {
|
|
copy_args_prologue += LoadLocal(optional_count_var);
|
|
copy_args_prologue += LoadFpRelativeSlot(
|
|
compiler::target::kWordSize *
|
|
(compiler::target::frame_layout.param_end_from_fp +
|
|
num_fixed_params - param),
|
|
ParameterType(ParameterVariable(param)));
|
|
copy_args_prologue +=
|
|
StoreLocalRaw(TokenPosition::kNoSource, ParameterVariable(param));
|
|
copy_args_prologue += Drop();
|
|
}
|
|
|
|
// Copy optional parameters down.
|
|
if (num_opt_pos_params > 0) {
|
|
JoinEntryInstr* next_missing = NULL;
|
|
for (intptr_t opt_param = 1; param < num_params; ++param, ++opt_param) {
|
|
TargetEntryInstr *supplied, *missing;
|
|
copy_args_prologue += IntConstant(opt_param);
|
|
copy_args_prologue += LoadLocal(optional_count_var);
|
|
copy_args_prologue += SmiRelationalOp(Token::kLTE);
|
|
copy_args_prologue += BranchIfTrue(&supplied, &missing);
|
|
|
|
Fragment good(supplied);
|
|
good += LoadLocal(optional_count_var);
|
|
good += LoadFpRelativeSlot(
|
|
compiler::target::kWordSize *
|
|
(compiler::target::frame_layout.param_end_from_fp +
|
|
num_fixed_params - param),
|
|
ParameterType(ParameterVariable(param)));
|
|
good += StoreLocalRaw(TokenPosition::kNoSource, ParameterVariable(param));
|
|
good += Drop();
|
|
|
|
Fragment not_good(missing);
|
|
if (next_missing != NULL) {
|
|
not_good += Goto(next_missing);
|
|
not_good.current = next_missing;
|
|
}
|
|
next_missing = BuildJoinEntry();
|
|
not_good += Constant(DefaultParameterValueAt(opt_param - 1));
|
|
not_good +=
|
|
StoreLocalRaw(TokenPosition::kNoSource, ParameterVariable(param));
|
|
not_good += Drop();
|
|
not_good += Goto(next_missing);
|
|
|
|
copy_args_prologue.current = good.current;
|
|
}
|
|
copy_args_prologue += Goto(next_missing /* join good/not_good flows */);
|
|
copy_args_prologue.current = next_missing;
|
|
|
|
// If there are more arguments from the caller we haven't processed, go
|
|
// NSM.
|
|
TargetEntryInstr *done, *unknown_named_arg_passed;
|
|
copy_args_prologue += LoadLocal(positional_count_var);
|
|
copy_args_prologue += LoadLocal(count_var);
|
|
copy_args_prologue += BranchIfEqual(&done, &unknown_named_arg_passed);
|
|
copy_args_prologue.current = done;
|
|
{
|
|
Fragment f(unknown_named_arg_passed);
|
|
f += Goto(nsm);
|
|
}
|
|
} else {
|
|
ASSERT(num_opt_named_params > 0);
|
|
|
|
const intptr_t first_name_offset =
|
|
compiler::target::ArgumentsDescriptor::first_named_entry_offset() -
|
|
compiler::target::Array::data_offset();
|
|
|
|
// Start by alphabetically sorting the names of the optional parameters.
|
|
int* opt_param_position = Z->Alloc<int>(num_opt_named_params);
|
|
SortOptionalNamedParametersInto(opt_param_position, num_fixed_params,
|
|
num_params);
|
|
|
|
ASSERT(temp_var != nullptr);
|
|
LocalVariable* optional_count_vars_processed = temp_var;
|
|
copy_args_prologue += IntConstant(0);
|
|
copy_args_prologue +=
|
|
StoreLocalRaw(TokenPosition::kNoSource, optional_count_vars_processed);
|
|
copy_args_prologue += Drop();
|
|
|
|
for (intptr_t i = 0; param < num_params; ++param, ++i) {
|
|
JoinEntryInstr* join = BuildJoinEntry();
|
|
|
|
copy_args_prologue += IntConstant(
|
|
compiler::target::ArgumentsDescriptor::named_entry_size() /
|
|
compiler::target::kWordSize);
|
|
copy_args_prologue += LoadLocal(optional_count_vars_processed);
|
|
copy_args_prologue += SmiBinaryOp(Token::kMUL, /* truncate= */ true);
|
|
LocalVariable* tuple_diff = MakeTemporary();
|
|
|
|
// name = arg_desc[names_offset + arg_desc_name_index + nameOffset]
|
|
copy_args_prologue += LoadArgDescriptor();
|
|
copy_args_prologue +=
|
|
IntConstant((first_name_offset +
|
|
compiler::target::ArgumentsDescriptor::name_offset()) /
|
|
compiler::target::kWordSize);
|
|
copy_args_prologue += LoadLocal(tuple_diff);
|
|
copy_args_prologue += SmiBinaryOp(Token::kADD, /* truncate= */ true);
|
|
copy_args_prologue +=
|
|
LoadIndexed(/* index_scale = */ compiler::target::kWordSize);
|
|
|
|
// first name in sorted list of all names
|
|
const String& param_name = String::ZoneHandle(
|
|
Z, function_.ParameterNameAt(opt_param_position[i]));
|
|
ASSERT(param_name.IsSymbol());
|
|
copy_args_prologue += Constant(param_name);
|
|
|
|
// Compare the two names: Note that the ArgumentDescriptor array always
|
|
// terminates with a "null" name (i.e. kNullCid), which will prevent us
|
|
// from running out-of-bounds.
|
|
TargetEntryInstr *supplied, *missing;
|
|
copy_args_prologue += BranchIfStrictEqual(&supplied, &missing);
|
|
|
|
// Let's load position from arg descriptor (to see which parameter is the
|
|
// name) and move kEntrySize forward in ArgDescriptopr names array.
|
|
Fragment good(supplied);
|
|
|
|
{
|
|
// fp[target::frame_layout.param_end_from_fp + (count_var - pos)]
|
|
good += LoadLocal(count_var);
|
|
{
|
|
// pos = arg_desc[names_offset + arg_desc_name_index + positionOffset]
|
|
good += LoadArgDescriptor();
|
|
good += IntConstant(
|
|
(first_name_offset +
|
|
compiler::target::ArgumentsDescriptor::position_offset()) /
|
|
compiler::target::kWordSize);
|
|
good += LoadLocal(tuple_diff);
|
|
good += SmiBinaryOp(Token::kADD, /* truncate= */ true);
|
|
good += LoadIndexed(/* index_scale = */ compiler::target::kWordSize);
|
|
}
|
|
good += SmiBinaryOp(Token::kSUB, /* truncate= */ true);
|
|
good += LoadFpRelativeSlot(
|
|
compiler::target::kWordSize *
|
|
compiler::target::frame_layout.param_end_from_fp,
|
|
ParameterType(ParameterVariable(opt_param_position[i])));
|
|
|
|
// Copy down.
|
|
good += StoreLocalRaw(TokenPosition::kNoSource,
|
|
ParameterVariable(opt_param_position[i]));
|
|
good += Drop();
|
|
|
|
// Increase processed optional variable count.
|
|
good += LoadLocal(optional_count_vars_processed);
|
|
good += IntConstant(1);
|
|
good += SmiBinaryOp(Token::kADD, /* truncate= */ true);
|
|
good += StoreLocalRaw(TokenPosition::kNoSource,
|
|
optional_count_vars_processed);
|
|
good += Drop();
|
|
|
|
good += Goto(join);
|
|
}
|
|
|
|
// We had no match, let's just load the default constant.
|
|
Fragment not_good(missing);
|
|
{
|
|
not_good += Constant(
|
|
DefaultParameterValueAt(opt_param_position[i] - num_fixed_params));
|
|
|
|
// Copy down with default value.
|
|
not_good += StoreLocalRaw(TokenPosition::kNoSource,
|
|
ParameterVariable(opt_param_position[i]));
|
|
not_good += Drop();
|
|
not_good += Goto(join);
|
|
}
|
|
|
|
copy_args_prologue.current = join;
|
|
copy_args_prologue += Drop(); // tuple_diff
|
|
}
|
|
|
|
// If there are more arguments from the caller we haven't processed, go
|
|
// NSM.
|
|
TargetEntryInstr *done, *unknown_named_arg_passed;
|
|
copy_args_prologue += LoadLocal(optional_count_var);
|
|
copy_args_prologue += LoadLocal(optional_count_vars_processed);
|
|
copy_args_prologue += BranchIfEqual(&done, &unknown_named_arg_passed);
|
|
copy_args_prologue.current = done;
|
|
|
|
{
|
|
Fragment f(unknown_named_arg_passed);
|
|
f += Goto(nsm);
|
|
}
|
|
}
|
|
|
|
copy_args_prologue += Drop(); // optional_count_var
|
|
copy_args_prologue += Drop(); // count_var
|
|
copy_args_prologue += Drop(); // positional_count_var
|
|
|
|
return copy_args_prologue;
|
|
}
|
|
|
|
Fragment PrologueBuilder::BuildFixedParameterLengthChecks(JoinEntryInstr* nsm) {
|
|
Fragment check_args;
|
|
JoinEntryInstr* done = BuildJoinEntry();
|
|
|
|
check_args += LoadArgDescriptor();
|
|
check_args += LoadNativeField(Slot::ArgumentsDescriptor_count());
|
|
LocalVariable* count = MakeTemporary();
|
|
|
|
TargetEntryInstr *then, *fail;
|
|
check_args += LoadLocal(count);
|
|
check_args += IntConstant(function_.num_fixed_parameters());
|
|
check_args += BranchIfEqual(&then, &fail);
|
|
|
|
TargetEntryInstr *then2, *fail2;
|
|
Fragment check_len(then);
|
|
check_len += LoadArgDescriptor();
|
|
check_len += LoadNativeField(Slot::ArgumentsDescriptor_positional_count());
|
|
check_len += BranchIfEqual(&then2, &fail2);
|
|
|
|
Fragment(fail) + Goto(nsm);
|
|
Fragment(fail2) + Goto(nsm);
|
|
Fragment(then2) + Goto(done);
|
|
|
|
return Fragment(check_args.entry, done);
|
|
}
|
|
|
|
Fragment PrologueBuilder::BuildClosureContextHandling() {
|
|
LocalVariable* closure_parameter = parsed_function_->ParameterVariable(0);
|
|
LocalVariable* context = parsed_function_->current_context_var();
|
|
|
|
// Load closure.context & store it into the context variable.
|
|
// (both load/store happen on the copyied-down places).
|
|
Fragment populate_context;
|
|
populate_context += LoadLocal(closure_parameter);
|
|
populate_context += LoadNativeField(Slot::Closure_context());
|
|
populate_context += StoreLocal(TokenPosition::kNoSource, context);
|
|
populate_context += Drop();
|
|
return populate_context;
|
|
}
|
|
|
|
Fragment PrologueBuilder::BuildTypeArgumentsHandling(JoinEntryInstr* nsm) {
|
|
LocalVariable* type_args_var = parsed_function_->RawTypeArgumentsVariable();
|
|
|
|
Fragment handling;
|
|
|
|
Fragment store_type_args;
|
|
store_type_args += LoadArgDescriptor();
|
|
store_type_args += LoadNativeField(Slot::ArgumentsDescriptor_count());
|
|
store_type_args += LoadFpRelativeSlot(
|
|
compiler::target::kWordSize *
|
|
(1 + compiler::target::frame_layout.param_end_from_fp),
|
|
CompileType::CreateNullable(/*is_nullable=*/true, kTypeArgumentsCid));
|
|
store_type_args += StoreLocal(TokenPosition::kNoSource, type_args_var);
|
|
store_type_args += Drop();
|
|
|
|
Fragment store_null;
|
|
store_null += NullConstant();
|
|
store_null += StoreLocal(TokenPosition::kNoSource, type_args_var);
|
|
store_null += Drop();
|
|
|
|
handling += TestTypeArgsLen(store_null, store_type_args, 0);
|
|
|
|
if (parsed_function_->function().IsClosureFunction()) {
|
|
LocalVariable* closure = parsed_function_->ParameterVariable(0);
|
|
|
|
// Currently, delayed type arguments can only be introduced through type
|
|
// inference in the FE. So if they are present, we can assume they are
|
|
// correct in number and bound.
|
|
Fragment use_delayed_type_args;
|
|
use_delayed_type_args += LoadLocal(closure);
|
|
use_delayed_type_args +=
|
|
LoadNativeField(Slot::Closure_delayed_type_arguments());
|
|
use_delayed_type_args +=
|
|
StoreLocal(TokenPosition::kNoSource, type_args_var);
|
|
use_delayed_type_args += Drop();
|
|
|
|
handling += TestDelayedTypeArgs(
|
|
closure,
|
|
/*present=*/TestTypeArgsLen(use_delayed_type_args, Goto(nsm), 0),
|
|
/*absent=*/Fragment());
|
|
}
|
|
|
|
return handling;
|
|
}
|
|
|
|
void PrologueBuilder::SortOptionalNamedParametersInto(int* opt_param_position,
|
|
int num_fixed_params,
|
|
int num_params) {
|
|
String& name = String::Handle(Z);
|
|
String& name_i = String::Handle(Z);
|
|
for (int pos = num_fixed_params; pos < num_params; pos++) {
|
|
name = function_.ParameterNameAt(pos);
|
|
int i = pos - num_fixed_params;
|
|
while (--i >= 0) {
|
|
name_i = function_.ParameterNameAt(opt_param_position[i]);
|
|
const intptr_t result = name.CompareTo(name_i);
|
|
ASSERT(result != 0);
|
|
if (result > 0) break;
|
|
opt_param_position[i + 1] = opt_param_position[i];
|
|
}
|
|
opt_param_position[i + 1] = pos;
|
|
}
|
|
}
|
|
|
|
} // namespace kernel
|
|
} // namespace dart
|
|
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|