2dae577c67
This reverts commit63179aa49d. See commit1ab083ae48for the original description of the change. The original change was reverted because it did not take into account that in Dart it is legal to make some parameters optional when you override a method. Such parameters should not be passed in registers - which means for each possible interface target we need to compute the minimum number of direct parameters accepted by all overrides. I have also added two comprehensive tests for different combinations of direct, optional and named parameters. TEST=vm/dart/calling_conventions_test.dart,vm/dart/calling_conventions2_test.dart Cq-Include-Trybots: luci.dart.try:vm-aot-linux-debug-simarm_x64-try,vm-aot-linux-debug-simriscv64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-linux-product-x64-try,vm-aot-linux-release-arm64-try,vm-aot-linux-release-simarm_x64-try,vm-aot-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try Change-Id: Ib76e2512d3e4077a40265d315483ebe3b0f5551c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/357862 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Slava Egorov <vegorov@google.com>
188 lines
5.3 KiB
C++
188 lines
5.3 KiB
C++
// 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/compiler/backend/dart_calling_conventions.h"
|
|
|
|
#include <array>
|
|
|
|
#include "vm/compiler/runtime_api.h"
|
|
#include "vm/object.h"
|
|
|
|
namespace dart {
|
|
|
|
namespace compiler {
|
|
|
|
namespace {
|
|
template <typename R, size_t N>
|
|
class SimpleAllocator {
|
|
public:
|
|
explicit SimpleAllocator(const R (®s)[N]) : regs_(regs) {
|
|
// C++ does not allow zero length arrays - so we use an array with a single
|
|
// kNoRegister element as a replacement.
|
|
if (N == 1 && regs_[0] == -1) {
|
|
next_ = N;
|
|
}
|
|
}
|
|
|
|
R Allocate() { return next_ < N ? regs_[next_++] : static_cast<R>(-1); }
|
|
|
|
std::pair<R, R> AllocatePair() {
|
|
if ((next_ + 2) <= N) {
|
|
const auto lo = Allocate();
|
|
const auto hi = Allocate();
|
|
return {lo, hi};
|
|
}
|
|
return {static_cast<R>(-1), static_cast<R>(-1)};
|
|
}
|
|
|
|
private:
|
|
const R (®s_)[N];
|
|
size_t next_ = 0;
|
|
};
|
|
|
|
template <typename R, size_t N>
|
|
SimpleAllocator(const R (&)[N]) -> SimpleAllocator<R, N>;
|
|
|
|
} // namespace
|
|
|
|
intptr_t ComputeCallingConvention(
|
|
Zone* zone,
|
|
const Function& target,
|
|
intptr_t argc,
|
|
std::function<Representation(intptr_t)> argument_rep,
|
|
bool should_assign_stack_locations,
|
|
ParameterInfoArray* parameter_info) {
|
|
SimpleAllocator cpu_allocator(DartCallingConvention::kCpuRegistersForArgs);
|
|
SimpleAllocator fpu_allocator(DartCallingConvention::kFpuRegistersForArgs);
|
|
|
|
if (parameter_info != nullptr) {
|
|
parameter_info->TruncateTo(0);
|
|
parameter_info->EnsureLength(argc, {});
|
|
}
|
|
|
|
const intptr_t max_arguments_in_registers =
|
|
!target.IsNull() ? target.MaxNumberOfParametersInRegisters(zone) : 0;
|
|
|
|
// First allocate all register parameters and compute the size of
|
|
// parameters on the stack.
|
|
intptr_t stack_parameters_size_in_words = 0;
|
|
for (intptr_t i = 0; i < argc; ++i) {
|
|
auto rep = argument_rep(i);
|
|
|
|
Location location;
|
|
if (i < max_arguments_in_registers) {
|
|
switch (rep) {
|
|
case kUnboxedInt64:
|
|
#if defined(TARGET_ARCH_IS_32_BIT)
|
|
if (auto [lo_reg, hi_reg] = cpu_allocator.AllocatePair();
|
|
hi_reg != kNoRegister) {
|
|
location = Location::Pair(Location::RegisterLocation(lo_reg),
|
|
Location::RegisterLocation(hi_reg));
|
|
#else
|
|
if (auto reg = cpu_allocator.Allocate(); reg != kNoRegister) {
|
|
location = Location::RegisterLocation(reg);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case kUnboxedDouble:
|
|
if (auto reg = fpu_allocator.Allocate(); reg != kNoFpuRegister) {
|
|
location = Location::FpuRegisterLocation(reg);
|
|
}
|
|
break;
|
|
|
|
case kTagged:
|
|
if (auto reg = cpu_allocator.Allocate(); reg != kNoRegister) {
|
|
location = Location::RegisterLocation(reg);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (location.IsInvalid()) {
|
|
intptr_t size_in_words;
|
|
switch (rep) {
|
|
case kUnboxedInt64:
|
|
size_in_words = compiler::target::kIntSpillFactor;
|
|
break;
|
|
|
|
case kUnboxedDouble:
|
|
size_in_words = compiler::target::kDoubleSpillFactor;
|
|
break;
|
|
|
|
case kTagged:
|
|
size_in_words = 1;
|
|
break;
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
stack_parameters_size_in_words += size_in_words;
|
|
}
|
|
|
|
if (parameter_info != nullptr) {
|
|
(*parameter_info)[i] = {location, rep};
|
|
}
|
|
}
|
|
|
|
if (parameter_info == nullptr || !should_assign_stack_locations) {
|
|
return stack_parameters_size_in_words;
|
|
}
|
|
|
|
// Now that we allocated all register parameters, allocate all other
|
|
// parameters to the stack.
|
|
const intptr_t offset_to_last_parameter_slot_from_fp =
|
|
(compiler::target::frame_layout.param_end_from_fp + 1);
|
|
intptr_t offset_in_words_from_fp = offset_to_last_parameter_slot_from_fp;
|
|
for (intptr_t i = argc - 1; i >= 0; --i) {
|
|
auto& [location, representation] = (*parameter_info)[i];
|
|
if (!location.IsInvalid()) {
|
|
continue; // Already allocated to a register.
|
|
}
|
|
|
|
switch (representation) {
|
|
case kUnboxedInt64:
|
|
if (compiler::target::kIntSpillFactor == 1) {
|
|
location = Location::StackSlot(offset_in_words_from_fp, FPREG);
|
|
} else {
|
|
ASSERT(compiler::target::kIntSpillFactor == 2);
|
|
location = Location::Pair(
|
|
Location::StackSlot(offset_in_words_from_fp, FPREG),
|
|
Location::StackSlot(offset_in_words_from_fp + 1, FPREG));
|
|
}
|
|
offset_in_words_from_fp += compiler::target::kIntSpillFactor;
|
|
break;
|
|
|
|
case kUnboxedDouble:
|
|
location = Location::DoubleStackSlot(offset_in_words_from_fp, FPREG);
|
|
offset_in_words_from_fp += compiler::target::kDoubleSpillFactor;
|
|
break;
|
|
|
|
case kTagged:
|
|
location = Location::StackSlot(offset_in_words_from_fp, FPREG);
|
|
offset_in_words_from_fp += 1;
|
|
break;
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
RELEASE_ASSERT(
|
|
(offset_in_words_from_fp - offset_to_last_parameter_slot_from_fp) ==
|
|
stack_parameters_size_in_words);
|
|
|
|
return stack_parameters_size_in_words;
|
|
}
|
|
|
|
} // namespace compiler
|
|
|
|
} // namespace dart
|