blob: 9f2958909810ac567f8a35a5115f8d34b6522db7 [file] [log] [blame]
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_CODEGEN_ARM64_REGLIST_ARM64_H_
#define V8_CODEGEN_ARM64_REGLIST_ARM64_H_
#include "src/codegen/arm64/utils-arm64.h"
#include "src/codegen/register-arch.h"
#include "src/codegen/reglist-base.h"
#include "src/common/globals.h"
namespace v8 {
namespace internal {
using RegList = RegListBase<Register>;
using DoubleRegList = RegListBase<DoubleRegister>;
ASSERT_TRIVIALLY_COPYABLE(RegList);
ASSERT_TRIVIALLY_COPYABLE(DoubleRegList);
constexpr int kRegListSizeInBits = sizeof(RegList) * kBitsPerByte;
// -----------------------------------------------------------------------------
// Lists of registers.
class V8_EXPORT_PRIVATE CPURegList {
public:
template <typename... CPURegisters>
explicit CPURegList(CPURegister reg0, CPURegisters... regs)
: list_(base::fold(
[](uint64_t acc, CPURegister v) {
if (!v.is_valid()) return acc;
return acc | (uint64_t{1} << v.code());
},
0, reg0, regs...)),
size_(reg0.SizeInBits()),
type_(reg0.type()) {
DCHECK(AreSameSizeAndType(reg0, regs...));
DCHECK(is_valid());
}
CPURegList(int size, RegList list)
: list_(list.bits()), size_(size), type_(CPURegister::kRegister) {
DCHECK(is_valid());
}
CPURegList(int size, DoubleRegList list)
: list_(list.bits()), size_(size), type_(CPURegister::kVRegister) {
DCHECK(is_valid());
}
CPURegList(CPURegister::RegisterType type, int size, int first_reg,
int last_reg)
: size_(size), type_(type) {
DCHECK(
((type == CPURegister::kRegister) && (last_reg < kNumberOfRegisters)) ||
((type == CPURegister::kVRegister) &&
(last_reg < kNumberOfVRegisters)));
DCHECK(last_reg >= first_reg);
list_ = (1ULL << (last_reg + 1)) - 1;
list_ &= ~((1ULL << first_reg) - 1);
DCHECK(is_valid());
}
CPURegister::RegisterType type() const { return type_; }
uint64_t bits() const { return list_; }
inline void set_bits(uint64_t new_bits) {
list_ = new_bits;
DCHECK(is_valid());
}
// Combine another CPURegList into this one. Registers that already exist in
// this list are left unchanged. The type and size of the registers in the
// 'other' list must match those in this list.
void Combine(const CPURegList& other);
// Remove every register in the other CPURegList from this one. Registers that
// do not exist in this list are ignored. The type of the registers in the
// 'other' list must match those in this list.
void Remove(const CPURegList& other);
// Variants of Combine and Remove which take CPURegisters.
void Combine(const CPURegister& other);
void Remove(const CPURegister& other1, const CPURegister& other2 = NoCPUReg,
const CPURegister& other3 = NoCPUReg,
const CPURegister& other4 = NoCPUReg);
// Variants of Combine and Remove which take a single register by its code;
// the type and size of the register is inferred from this list.
void Combine(int code);
void Remove(int code);
// Align the list to 16 bytes.
void Align();
CPURegister PopLowestIndex();
CPURegister PopHighestIndex();
// AAPCS64 callee-saved registers.
static CPURegList GetCalleeSaved(int size = kXRegSizeInBits);
static CPURegList GetCalleeSavedV(int size = kDRegSizeInBits);
// AAPCS64 caller-saved registers. Note that this includes lr.
// TODO(all): Determine how we handle d8-d15 being callee-saved, but the top
// 64-bits being caller-saved.
static CPURegList GetCallerSaved(int size = kXRegSizeInBits);
static CPURegList GetCallerSavedV(int size = kDRegSizeInBits);
bool IsEmpty() const { return list_ == 0; }
bool IncludesAliasOf(const CPURegister& other1,
const CPURegister& other2 = NoCPUReg,
const CPURegister& other3 = NoCPUReg,
const CPURegister& other4 = NoCPUReg) const {
uint64_t list = 0;
if (!other1.IsNone() && (other1.type() == type_)) {
list |= (uint64_t{1} << other1.code());
}
if (!other2.IsNone() && (other2.type() == type_)) {
list |= (uint64_t{1} << other2.code());
}
if (!other3.IsNone() && (other3.type() == type_)) {
list |= (uint64_t{1} << other3.code());
}
if (!other4.IsNone() && (other4.type() == type_)) {
list |= (uint64_t{1} << other4.code());
}
return (list_ & list) != 0;
}
int Count() const { return CountSetBits(list_, kRegListSizeInBits); }
int RegisterSizeInBits() const { return size_; }
int RegisterSizeInBytes() const {
int size_in_bits = RegisterSizeInBits();
DCHECK_EQ(size_in_bits % kBitsPerByte, 0);
return size_in_bits / kBitsPerByte;
}
int TotalSizeInBytes() const { return RegisterSizeInBytes() * Count(); }
private:
uint64_t list_;
int size_;
CPURegister::RegisterType type_;
bool is_valid() const {
constexpr uint64_t kValidRegisters{0x8000000ffffffff};
constexpr uint64_t kValidVRegisters{0x0000000ffffffff};
switch (type_) {
case CPURegister::kRegister:
return (list_ & kValidRegisters) == list_;
case CPURegister::kVRegister:
return (list_ & kValidVRegisters) == list_;
case CPURegister::kNoRegister:
return list_ == 0;
default:
UNREACHABLE();
}
}
};
// AAPCS64 callee-saved registers.
#define kCalleeSaved CPURegList::GetCalleeSaved()
#define kCalleeSavedV CPURegList::GetCalleeSavedV()
// AAPCS64 caller-saved registers. Note that this includes lr.
#define kCallerSaved CPURegList::GetCallerSaved()
#define kCallerSavedV CPURegList::GetCallerSavedV()
} // namespace internal
} // namespace v8
#endif // V8_CODEGEN_ARM64_REGLIST_ARM64_H_