51d1c8923a
This is a reland of commit 88496ba1c3
Fixes on top of the original change:
* Closure layout is changed to avoid gap between fixed fields and
variable-length elements on compressed pointers architecture.
This gap was causing crashes in the GC when scanning closure
objects.
* pkg/vm_snapshot_analysis/test/instruction_sizes_test is fixed
on arm64 by decreasing threshold for detecting size changes.
Original change's description:
> [vm,dart2bytecode,modular_aot] Variable-length closure objects
>
> Extend closure objects with variable number of elements to capture.
> This is needed to support capturing multiple independent contexts
> after capturing is computed in the front-end.
>
> The following fixed Closure fields are moved into variable-length
> elements:
> - delayed type arguments;
> - instantiator type arguments;
> - function type arguments;
> - context.
>
> Number of elements and presence/indices of various type arguments
> are encoded into the new length_and_flags field in the Closure.
>
> Most closure objects don't need any of the type arguments so this
> change will reduce average Closure object size.
TEST=ci
Issue: https://github.com/dart-lang/sdk/issues/61572
Issue: https://github.com/dart-lang/sdk/issues/61635
Change-Id: I8685e632e2d0832766ecdc470f3cf9a6b880de48
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/494243
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Tess Strickland <sstrickl@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
623 lines
19 KiB
C++
623 lines
19 KiB
C++
// Copyright (c) 2013, 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/locations.h"
|
|
#include <limits>
|
|
|
|
#include "vm/class_id.h"
|
|
#include "vm/compiler/assembler/assembler.h"
|
|
#include "vm/compiler/backend/il_printer.h"
|
|
#include "vm/log.h"
|
|
#include "vm/stack_frame.h"
|
|
|
|
namespace dart {
|
|
|
|
compiler::OperandSize RepresentationUtils::OperandSize(Representation rep) {
|
|
switch (rep) {
|
|
case kTagged:
|
|
return compiler::kObjectBytes;
|
|
case kUntagged:
|
|
// Untagged addresses are either loaded from and stored to word size
|
|
// native fields or generated from already-extended tagged addresses when
|
|
// compressed pointers are enabled.
|
|
return compiler::kWordBytes;
|
|
|
|
case kUnboxedInt8:
|
|
return compiler::kByte;
|
|
case kUnboxedUint8:
|
|
return compiler::kUnsignedByte;
|
|
case kUnboxedInt16:
|
|
return compiler::kTwoBytes;
|
|
case kUnboxedUint16:
|
|
return compiler::kUnsignedTwoBytes;
|
|
case kUnboxedInt32:
|
|
return compiler::kFourBytes;
|
|
case kUnboxedUint32:
|
|
return compiler::kUnsignedFourBytes;
|
|
case kUnboxedInt64:
|
|
return compiler::kEightBytes;
|
|
|
|
case kUnboxedFloat:
|
|
return compiler::kFourBytes;
|
|
case kUnboxedDouble:
|
|
return compiler::kEightBytes;
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
return compiler::kObjectBytes;
|
|
}
|
|
}
|
|
|
|
#define REP_MIN_VALUE_CLAUSE(name, ___, ____, type) \
|
|
case k##name: \
|
|
return static_cast<int64_t>(std::numeric_limits<type>::min());
|
|
int64_t RepresentationUtils::MinValue(Representation rep) {
|
|
switch (rep) {
|
|
FOR_EACH_INTEGER_REPRESENTATION_KIND(REP_MIN_VALUE_CLAUSE)
|
|
default:
|
|
UNREACHABLE();
|
|
return kMinInt64;
|
|
}
|
|
}
|
|
#undef REP_MIN_VALUE_CLAUSE
|
|
|
|
#define REP_MAX_VALUE_CLAUSE(name, ___, ____, type) \
|
|
case k##name: \
|
|
return static_cast<int64_t>(std::numeric_limits<type>::max());
|
|
int64_t RepresentationUtils::MaxValue(Representation rep) {
|
|
switch (rep) {
|
|
FOR_EACH_INTEGER_REPRESENTATION_KIND(REP_MAX_VALUE_CLAUSE)
|
|
default:
|
|
UNREACHABLE();
|
|
return kMaxInt64;
|
|
}
|
|
}
|
|
#undef REP_MAX_VALUE_CLAUSE
|
|
|
|
bool RepresentationUtils::IsRepresentable(Representation rep, int64_t value) {
|
|
ASSERT(IsUnboxedInteger(rep));
|
|
const intptr_t bit_size = ValueSize(rep) * kBitsPerByte;
|
|
return IsUnsignedInteger(rep) ? Utils::IsUint(bit_size, value)
|
|
: Utils::IsInt(bit_size, value);
|
|
}
|
|
|
|
Representation RepresentationUtils::RepresentationOfArrayElement(
|
|
classid_t cid) {
|
|
if (IsTypedDataBaseClassId(cid)) {
|
|
// Normalize typed data cids to the internal cid for the switch statement.
|
|
cid = cid - ((cid - kFirstTypedDataCid) % kNumTypedDataCidRemainders) +
|
|
kTypedDataCidRemainderInternal;
|
|
}
|
|
switch (cid) {
|
|
#define ARRAY_CASE(Name) case k##Name##Cid:
|
|
CLASS_LIST_ARRAYS(ARRAY_CASE)
|
|
#undef ARRAY_CASE
|
|
case kClosureCid:
|
|
case kRecordCid:
|
|
case kTypeArgumentsCid:
|
|
return kTagged;
|
|
case kTypedDataInt8ArrayCid:
|
|
return kUnboxedInt8;
|
|
case kOneByteStringCid:
|
|
case kTypedDataUint8ArrayCid:
|
|
case kTypedDataUint8ClampedArrayCid:
|
|
case kExternalTypedDataUint8ArrayCid:
|
|
case kExternalTypedDataUint8ClampedArrayCid:
|
|
return kUnboxedUint8;
|
|
case kTypedDataInt16ArrayCid:
|
|
return kUnboxedInt16;
|
|
case kTwoByteStringCid:
|
|
case kTypedDataUint16ArrayCid:
|
|
return kUnboxedUint16;
|
|
case kTypedDataInt32ArrayCid:
|
|
return kUnboxedInt32;
|
|
case kTypedDataUint32ArrayCid:
|
|
return kUnboxedUint32;
|
|
case kTypedDataInt64ArrayCid:
|
|
case kTypedDataUint64ArrayCid:
|
|
return kUnboxedInt64;
|
|
case kTypedDataFloat32ArrayCid:
|
|
return kUnboxedFloat;
|
|
case kTypedDataFloat64ArrayCid:
|
|
return kUnboxedDouble;
|
|
case kTypedDataInt32x4ArrayCid:
|
|
return kUnboxedInt32x4;
|
|
case kTypedDataFloat32x4ArrayCid:
|
|
return kUnboxedFloat32x4;
|
|
case kTypedDataFloat64x2ArrayCid:
|
|
return kUnboxedFloat64x2;
|
|
default:
|
|
FATAL("Unexpected array cid %u", cid);
|
|
return kTagged;
|
|
}
|
|
}
|
|
|
|
const char* RepresentationUtils::ToCString(Representation repr) {
|
|
switch (repr) {
|
|
#define REPR_CASE(Name, PrintName, __, ___) \
|
|
case k##Name: \
|
|
return #PrintName;
|
|
FOR_EACH_REPRESENTATION_KIND(REPR_CASE)
|
|
#undef KIND_CASE
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
intptr_t RegisterSet::RegisterCount(intptr_t registers) {
|
|
// Brian Kernighan's algorithm for counting the bits set.
|
|
intptr_t count = 0;
|
|
while (registers != 0) {
|
|
++count;
|
|
// Clear the least significant bit set.
|
|
registers &= (static_cast<uintptr_t>(registers) - 1);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void RegisterSet::DebugPrint() {
|
|
for (intptr_t i = 0; i < kNumberOfCpuRegisters; i++) {
|
|
Register r = static_cast<Register>(i);
|
|
if (ContainsRegister(r)) {
|
|
THR_Print("%s %s\n", RegisterNames::RegisterName(r),
|
|
IsTagged(r) ? "tagged" : "untagged");
|
|
}
|
|
}
|
|
|
|
for (intptr_t i = 0; i < kNumberOfFpuRegisters; i++) {
|
|
FpuRegister r = static_cast<FpuRegister>(i);
|
|
if (ContainsFpuRegister(r)) {
|
|
THR_Print("%s\n", RegisterNames::FpuRegisterName(r));
|
|
}
|
|
}
|
|
}
|
|
|
|
LocationSummary::LocationSummary(Zone* zone,
|
|
intptr_t input_count,
|
|
intptr_t temp_count,
|
|
LocationSummary::ContainsCall contains_call)
|
|
: num_inputs_(input_count),
|
|
num_temps_(temp_count),
|
|
output_location_(), // out(0)->IsInvalid() unless later set.
|
|
stack_bitmap_(nullptr),
|
|
contains_call_(contains_call),
|
|
live_registers_() {
|
|
#if defined(DEBUG)
|
|
writable_inputs_ = 0;
|
|
#endif
|
|
input_locations_ = zone->Alloc<Location>(num_inputs_);
|
|
temp_locations_ = zone->Alloc<Location>(num_temps_);
|
|
}
|
|
|
|
LocationSummary* LocationSummary::Make(
|
|
Zone* zone,
|
|
intptr_t input_count,
|
|
Location out,
|
|
LocationSummary::ContainsCall contains_call) {
|
|
LocationSummary* summary =
|
|
new (zone) LocationSummary(zone, input_count, 0, contains_call);
|
|
for (intptr_t i = 0; i < input_count; i++) {
|
|
summary->set_in(i, Location::RequiresRegister());
|
|
}
|
|
summary->set_out(0, out);
|
|
return summary;
|
|
}
|
|
|
|
static bool ValidOutputForAlwaysCalls(const Location& loc) {
|
|
return loc.IsMachineRegister() || loc.IsInvalid() || loc.IsPairLocation();
|
|
}
|
|
|
|
void LocationSummary::set_in(intptr_t index, Location loc) {
|
|
ASSERT(index >= 0);
|
|
ASSERT(index < num_inputs_);
|
|
#if defined(DEBUG)
|
|
// See FlowGraphAllocator::ProcessOneInstruction for explanation of these
|
|
// restrictions.
|
|
if (always_calls()) {
|
|
if (loc.IsUnallocated()) {
|
|
ASSERT(loc.policy() == Location::kAny ||
|
|
loc.policy() == Location::kRequiresStack);
|
|
} else if (loc.IsPairLocation()) {
|
|
ASSERT(!loc.AsPairLocation()->At(0).IsUnallocated() ||
|
|
loc.AsPairLocation()->At(0).policy() == Location::kAny ||
|
|
loc.AsPairLocation()->At(0).policy() == Location::kRequiresStack);
|
|
ASSERT(!loc.AsPairLocation()->At(1).IsUnallocated() ||
|
|
loc.AsPairLocation()->At(1).policy() == Location::kAny ||
|
|
loc.AsPairLocation()->At(1).policy() == Location::kRequiresStack);
|
|
}
|
|
if (index == 0 && out(0).IsUnallocated() &&
|
|
out(0).policy() == Location::kSameAsFirstInput) {
|
|
ASSERT(ValidOutputForAlwaysCalls(loc));
|
|
}
|
|
}
|
|
#endif
|
|
input_locations_[index] = loc;
|
|
}
|
|
|
|
void LocationSummary::set_out(intptr_t index, Location loc) {
|
|
ASSERT(index == 0);
|
|
ASSERT(!always_calls() || ValidOutputForAlwaysCalls(loc) ||
|
|
(loc.IsUnallocated() && loc.policy() == Location::kSameAsFirstInput &&
|
|
num_inputs_ > 0 && ValidOutputForAlwaysCalls(in(0))));
|
|
output_location_ = loc;
|
|
}
|
|
|
|
Location Location::ToSpRelative(intptr_t fp_to_sp_delta) const {
|
|
if (IsPairLocation()) {
|
|
auto pair = AsPairLocation();
|
|
return Pair(pair->At(0).ToSpRelative(fp_to_sp_delta),
|
|
pair->At(1).ToSpRelative(fp_to_sp_delta));
|
|
}
|
|
|
|
if (HasStackIndex()) {
|
|
ASSERT(base_reg() == FPREG);
|
|
uword payload = StackSlotBaseField::encode(SPREG) |
|
|
StackIndexField::encode(stack_index() - fp_to_sp_delta);
|
|
return Location(kind(), payload);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
Location Location::ToEntrySpRelative() const {
|
|
const auto fp_to_entry_sp_delta =
|
|
(compiler::target::frame_layout.param_end_from_fp + 1) -
|
|
compiler::target::frame_layout.last_param_from_entry_sp;
|
|
return ToSpRelative(fp_to_entry_sp_delta);
|
|
}
|
|
|
|
Location Location::ToCallerSpRelative() const {
|
|
const auto fp_to_caller_sp_delta =
|
|
(compiler::target::frame_layout.param_end_from_fp + 1);
|
|
return ToSpRelative(fp_to_caller_sp_delta);
|
|
}
|
|
|
|
Location Location::Pair(Location first, Location second) {
|
|
PairLocation* pair_location = new PairLocation();
|
|
ASSERT((reinterpret_cast<intptr_t>(pair_location) & kLocationTagMask) == 0);
|
|
pair_location->SetAt(0, first);
|
|
pair_location->SetAt(1, second);
|
|
Location loc(reinterpret_cast<uword>(pair_location) | kPairLocationTag);
|
|
return loc;
|
|
}
|
|
|
|
PairLocation* Location::AsPairLocation() const {
|
|
ASSERT(IsPairLocation());
|
|
return reinterpret_cast<PairLocation*>(value_ & ~kLocationTagMask);
|
|
}
|
|
|
|
Location Location::Component(intptr_t i) const {
|
|
return AsPairLocation()->At(i);
|
|
}
|
|
|
|
Location LocationRegisterOrConstant(Value* value) {
|
|
ConstantInstr* constant = value->definition()->AsConstant();
|
|
return ((constant != nullptr) &&
|
|
compiler::Assembler::IsSafe(constant->value()))
|
|
? Location::Constant(constant)
|
|
: Location::RequiresRegister();
|
|
}
|
|
|
|
Location LocationRegisterOrSmiConstant(Value* value,
|
|
intptr_t min_value,
|
|
intptr_t max_value) {
|
|
ConstantInstr* constant = value->definition()->AsConstant();
|
|
if (constant == nullptr) {
|
|
return Location::RequiresRegister();
|
|
}
|
|
if (!compiler::Assembler::IsSafeSmi(constant->value())) {
|
|
return Location::RequiresRegister();
|
|
}
|
|
const intptr_t smi_value = value->BoundSmiConstant();
|
|
if (smi_value < min_value || smi_value > max_value) {
|
|
return Location::RequiresRegister();
|
|
}
|
|
return Location::Constant(constant);
|
|
}
|
|
|
|
Location LocationWritableRegisterOrConstant(Value* value) {
|
|
ConstantInstr* constant = value->definition()->AsConstant();
|
|
return ((constant != nullptr) &&
|
|
compiler::Assembler::IsSafe(constant->value()))
|
|
? Location::Constant(constant)
|
|
: Location::WritableRegister();
|
|
}
|
|
|
|
Location LocationWritableRegisterOrSmiConstant(Value* value,
|
|
intptr_t min_value,
|
|
intptr_t max_value) {
|
|
ConstantInstr* constant = value->definition()->AsConstant();
|
|
if (constant == nullptr) {
|
|
return Location::WritableRegister();
|
|
}
|
|
if (!compiler::Assembler::IsSafeSmi(constant->value())) {
|
|
return Location::WritableRegister();
|
|
}
|
|
const intptr_t smi_value = value->BoundSmiConstant();
|
|
if (smi_value < min_value || smi_value > max_value) {
|
|
return Location::WritableRegister();
|
|
}
|
|
return Location::Constant(constant);
|
|
}
|
|
|
|
Location LocationFixedRegisterOrConstant(Value* value, Register reg) {
|
|
ASSERT(((1 << reg) & kDartAvailableCpuRegs) != 0);
|
|
ConstantInstr* constant = value->definition()->AsConstant();
|
|
return ((constant != nullptr) &&
|
|
compiler::Assembler::IsSafe(constant->value()))
|
|
? Location::Constant(constant)
|
|
: Location::RegisterLocation(reg);
|
|
}
|
|
|
|
Location LocationFixedRegisterOrSmiConstant(Value* value, Register reg) {
|
|
ASSERT(((1 << reg) & kDartAvailableCpuRegs) != 0);
|
|
ConstantInstr* constant = value->definition()->AsConstant();
|
|
return ((constant != nullptr) &&
|
|
compiler::Assembler::IsSafeSmi(constant->value()))
|
|
? Location::Constant(constant)
|
|
: Location::RegisterLocation(reg);
|
|
}
|
|
|
|
Location LocationAnyOrConstant(Value* value) {
|
|
ConstantInstr* constant = value->definition()->AsConstant();
|
|
return ((constant != nullptr) &&
|
|
compiler::Assembler::IsSafe(constant->value()))
|
|
? Location::Constant(constant)
|
|
: Location::Any();
|
|
}
|
|
|
|
compiler::Address LocationToStackSlotAddress(Location loc) {
|
|
return compiler::Address(loc.base_reg(), loc.ToStackSlotOffset());
|
|
}
|
|
|
|
intptr_t Location::ToStackSlotOffset() const {
|
|
return stack_index() * compiler::target::kWordSize;
|
|
}
|
|
|
|
const Object& Location::constant() const {
|
|
return constant_instruction()->value();
|
|
}
|
|
|
|
const char* Location::Name() const {
|
|
switch (kind()) {
|
|
case kInvalid:
|
|
return "?";
|
|
case kRegister:
|
|
return RegisterNames::RegisterName(reg());
|
|
case kFpuRegister:
|
|
return RegisterNames::FpuRegisterName(fpu_reg());
|
|
case kStackSlot:
|
|
return "S";
|
|
case kDoubleStackSlot:
|
|
return "DS";
|
|
case kQuadStackSlot:
|
|
return "QS";
|
|
case kUnallocated:
|
|
switch (policy()) {
|
|
case kAny:
|
|
return "A";
|
|
case kPrefersRegister:
|
|
return "P";
|
|
case kRequiresRegister:
|
|
return "R";
|
|
case kRequiresFpuRegister:
|
|
return "DR";
|
|
case kWritableRegister:
|
|
return "WR";
|
|
case kSameAsFirstInput:
|
|
return "0";
|
|
case kSameAsFirstOrSecondInput:
|
|
return "0|1";
|
|
case kMayBeSameAsFirstInput:
|
|
return "0?";
|
|
case kRequiresStack:
|
|
return "RS";
|
|
}
|
|
UNREACHABLE();
|
|
default:
|
|
if (IsConstant()) {
|
|
return "C";
|
|
} else {
|
|
ASSERT(IsPairLocation());
|
|
return "2P";
|
|
}
|
|
}
|
|
return "?";
|
|
}
|
|
|
|
void Location::PrintTo(BaseTextBuffer* f) const {
|
|
if (!FLAG_support_il_printer) {
|
|
return;
|
|
}
|
|
if (kind() == kStackSlot || kind() == kDoubleStackSlot ||
|
|
kind() == kQuadStackSlot) {
|
|
const char* suffix = "";
|
|
if (kind() == kDoubleStackSlot) {
|
|
suffix = " f64";
|
|
} else if (kind() == kQuadStackSlot) {
|
|
suffix = " f128";
|
|
}
|
|
f->Printf("%s[%" Pd "]%s", base_reg() == FPREG ? "fp" : "sp", stack_index(),
|
|
suffix);
|
|
} else if (IsPairLocation()) {
|
|
f->AddString("(");
|
|
AsPairLocation()->At(0).PrintTo(f);
|
|
f->AddString(", ");
|
|
AsPairLocation()->At(1).PrintTo(f);
|
|
f->AddString(")");
|
|
} else {
|
|
f->Printf("%s", Name());
|
|
}
|
|
}
|
|
|
|
const char* Location::ToCString() const {
|
|
char buffer[1024];
|
|
BufferFormatter bf(buffer, 1024);
|
|
PrintTo(&bf);
|
|
return Thread::Current()->zone()->MakeCopyOfString(buffer);
|
|
}
|
|
|
|
void Location::Print() const {
|
|
if (kind() == kStackSlot || kind() == kDoubleStackSlot ||
|
|
kind() == kQuadStackSlot) {
|
|
const char* suffix = "";
|
|
if (kind() == kDoubleStackSlot) {
|
|
suffix = " f64";
|
|
} else if (kind() == kQuadStackSlot) {
|
|
suffix = " f128";
|
|
}
|
|
THR_Print("%s[%" Pd "] %s", base_reg() == FPREG ? "fp" : "sp",
|
|
stack_index(), suffix);
|
|
} else {
|
|
THR_Print("%s", Name());
|
|
}
|
|
}
|
|
|
|
Location Location::Copy() const {
|
|
if (IsPairLocation()) {
|
|
PairLocation* pair = AsPairLocation();
|
|
ASSERT(!pair->At(0).IsPairLocation());
|
|
ASSERT(!pair->At(1).IsPairLocation());
|
|
return Location::Pair(pair->At(0).Copy(), pair->At(1).Copy());
|
|
} else {
|
|
// Copy by value.
|
|
return *this;
|
|
}
|
|
}
|
|
|
|
Location LocationArgumentsDescriptorLocation() {
|
|
return Location::RegisterLocation(ARGS_DESC_REG);
|
|
}
|
|
|
|
Location LocationExceptionLocation() {
|
|
return Location::RegisterLocation(kExceptionObjectReg);
|
|
}
|
|
|
|
Location LocationStackTraceLocation() {
|
|
return Location::RegisterLocation(kStackTraceObjectReg);
|
|
}
|
|
|
|
Location LocationRemapForSlowPath(Location loc,
|
|
Definition* def,
|
|
intptr_t* cpu_reg_slots,
|
|
intptr_t* fpu_reg_slots) {
|
|
if (loc.IsRegister()) {
|
|
intptr_t index = cpu_reg_slots[loc.reg()];
|
|
ASSERT(index >= 0);
|
|
return Location::StackSlot(
|
|
compiler::target::frame_layout.FrameSlotForVariableIndex(-index),
|
|
FPREG);
|
|
} else if (loc.IsFpuRegister()) {
|
|
intptr_t index = fpu_reg_slots[loc.fpu_reg()];
|
|
ASSERT(index >= 0);
|
|
switch (def->representation()) {
|
|
case kUnboxedDouble: // SlowPathEnvironmentFor sees _one_ register
|
|
case kUnboxedFloat: // both for doubles and floats.
|
|
return Location::DoubleStackSlot(
|
|
compiler::target::frame_layout.FrameSlotForVariableIndex(-index),
|
|
FPREG);
|
|
|
|
case kUnboxedFloat32x4:
|
|
case kUnboxedInt32x4:
|
|
case kUnboxedFloat64x2:
|
|
return Location::QuadStackSlot(
|
|
compiler::target::frame_layout.FrameSlotForVariableIndex(-index),
|
|
FPREG);
|
|
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
} else if (loc.IsPairLocation()) {
|
|
ASSERT(def->representation() == kUnboxedInt64);
|
|
PairLocation* value_pair = loc.AsPairLocation();
|
|
intptr_t index_lo;
|
|
intptr_t index_hi;
|
|
|
|
if (value_pair->At(0).IsRegister()) {
|
|
index_lo = compiler::target::frame_layout.FrameSlotForVariableIndex(
|
|
-cpu_reg_slots[value_pair->At(0).reg()]);
|
|
} else {
|
|
ASSERT(value_pair->At(0).IsStackSlot());
|
|
index_lo = value_pair->At(0).stack_index();
|
|
}
|
|
|
|
if (value_pair->At(1).IsRegister()) {
|
|
index_hi = compiler::target::frame_layout.FrameSlotForVariableIndex(
|
|
-cpu_reg_slots[value_pair->At(1).reg()]);
|
|
} else {
|
|
ASSERT(value_pair->At(1).IsStackSlot());
|
|
index_hi = value_pair->At(1).stack_index();
|
|
}
|
|
|
|
return Location::Pair(Location::StackSlot(index_lo, FPREG),
|
|
Location::StackSlot(index_hi, FPREG));
|
|
} else if (loc.IsInvalid() && def->IsMaterializeObject()) {
|
|
def->AsMaterializeObject()->RemapRegisters(cpu_reg_slots, fpu_reg_slots);
|
|
return loc;
|
|
}
|
|
|
|
return loc;
|
|
}
|
|
|
|
void LocationSummary::PrintTo(BaseTextBuffer* f) const {
|
|
if (!FLAG_support_il_printer) {
|
|
return;
|
|
}
|
|
if (input_count() > 0) {
|
|
f->AddString(" (");
|
|
for (intptr_t i = 0; i < input_count(); i++) {
|
|
if (i != 0) f->AddString(", ");
|
|
in(i).PrintTo(f);
|
|
}
|
|
f->AddString(")");
|
|
}
|
|
|
|
if (temp_count() > 0) {
|
|
f->AddString(" [");
|
|
for (intptr_t i = 0; i < temp_count(); i++) {
|
|
if (i != 0) f->AddString(", ");
|
|
temp(i).PrintTo(f);
|
|
}
|
|
f->AddString("]");
|
|
}
|
|
|
|
if (!out(0).IsInvalid()) {
|
|
f->AddString(" => ");
|
|
out(0).PrintTo(f);
|
|
}
|
|
|
|
if (always_calls()) f->AddString(" C");
|
|
}
|
|
|
|
#if defined(DEBUG)
|
|
void LocationSummary::DiscoverWritableInputs() {
|
|
if (!HasCallOnSlowPath()) {
|
|
return;
|
|
}
|
|
|
|
for (intptr_t i = 0; i < input_count(); i++) {
|
|
if (in(i).IsUnallocated() &&
|
|
(in(i).policy() == Location::kWritableRegister)) {
|
|
writable_inputs_ |= 1 << i;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LocationSummary::CheckWritableInputs() {
|
|
ASSERT(HasCallOnSlowPath());
|
|
for (intptr_t i = 0; i < input_count(); i++) {
|
|
if ((writable_inputs_ & (1 << i)) != 0) {
|
|
// Writable registers have to be manually preserved because
|
|
// with the right representation because register allocator does not know
|
|
// how they are used within the instruction template.
|
|
ASSERT(in(i).IsMachineRegister());
|
|
ASSERT(live_registers()->Contains(in(i)));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
} // namespace dart
|