Jit/codegen/x86_64.h (224 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#pragma once
#include "Jit/log.h"
#include "Jit/util.h"
#include <iosfwd>
#include <set>
#include <string>
#include <unordered_map>
namespace jit {
namespace codegen {
// A physical location (register or stack slot). If this represents a stack
// slot (is_memory() is true) then `loc` is relative to RBP.
struct PhyLocation {
PhyLocation() : loc(REG_INVALID) {}
constexpr PhyLocation(int l) : loc(l) {}
constexpr operator int() const {
return loc;
}
bool is_memory() const {
return loc < 0;
}
bool is_register() const {
return loc >= 0;
}
bool is_gp_register() const {
return is_register() && loc < XMM_REG_BASE;
}
bool is_fp_register() const {
return is_register() && loc >= XMM_REG_BASE;
}
int loc;
#define FOREACH_GP(X) \
X(RAX) \
X(RCX) \
X(RDX) \
X(RBX) \
X(RSP) \
X(RBP) \
X(RSI) \
X(RDI) \
X(R8) \
X(R9) \
X(R10) \
X(R11) \
X(R12) \
X(R13) \
X(R14) \
X(R15)
#define FOREACH_XMM(X) \
X(XMM0) \
X(XMM1) \
X(XMM2) \
X(XMM3) \
X(XMM4) \
X(XMM5) \
X(XMM6) \
X(XMM7) \
X(XMM8) \
X(XMM9) \
X(XMM10) \
X(XMM11) \
X(XMM12) \
X(XMM13) \
X(XMM14) \
X(XMM15)
enum Reg : int {
REG_INVALID = -1,
#define DECLARE_REG(v, ...) v,
FOREACH_GP(DECLARE_REG) FOREACH_XMM(DECLARE_REG)
#undef DECLARE_REG
};
static const char* regName(Reg reg) {
JIT_CHECK(reg >= 0, "reg must be nonnegative");
switch (reg) {
#define DECLARE_REG(v, ...) \
case v: \
return #v;
FOREACH_GP(DECLARE_REG)
FOREACH_XMM(DECLARE_REG)
#undef DECLARE_REG
case REG_INVALID:
JIT_CHECK(false, "invalid register");
}
JIT_CHECK(false, "unknown register %d", reg);
}
std::string toString() const {
if (is_memory()) {
return fmt::format("[RBP{}]", loc);
}
return regName(static_cast<Reg>(loc));
}
#define COUNT_REGS(...) +1
static constexpr int NUM_GP_REGS = FOREACH_GP(COUNT_REGS);
static constexpr int XMM_REG_BASE = XMM0;
static constexpr int NUM_XMM_REGS = FOREACH_XMM(COUNT_REGS);
static constexpr int NUM_REGS = NUM_GP_REGS + NUM_XMM_REGS;
#undef COUNT_REGS
bool operator==(const PhyLocation& rhs) const {
return loc == rhs.loc;
}
bool operator==(int rhs) const {
return loc == rhs;
}
bool operator!=(const PhyLocation& rhs) const {
return loc != rhs.loc;
}
bool operator!=(int rhs) const {
return loc != rhs;
}
// Parses the register name in string and returns the corresponding
// physical register.
// Returns REG_INVALID if name is not a valid register name.
static PhyLocation parse(const std::string& name);
};
class PhyRegisterSet {
public:
constexpr PhyRegisterSet() : rs_(0) {}
constexpr PhyRegisterSet(PhyLocation r) : rs_(0) {
rs_ |= (1 << r);
}
constexpr PhyRegisterSet operator|(PhyLocation reg) const {
PhyRegisterSet set;
set.rs_ = rs_ | (1 << reg);
return set;
}
constexpr PhyRegisterSet operator|(const PhyRegisterSet& rs) const {
PhyRegisterSet res;
res.rs_ = rs_ | rs.rs_;
return res;
}
PhyRegisterSet& operator|=(const PhyRegisterSet& rs) {
rs_ |= rs.rs_;
return *this;
}
constexpr PhyRegisterSet operator-(PhyLocation rs) const {
return operator-(PhyRegisterSet(rs));
}
constexpr PhyRegisterSet operator-(PhyRegisterSet rs) const {
PhyRegisterSet set;
set.rs_ = rs_ & ~(rs.rs_);
return set;
}
constexpr PhyRegisterSet operator&(PhyRegisterSet rs) const {
PhyRegisterSet set;
set.rs_ = rs_ & rs.rs_;
return set;
}
constexpr bool operator==(const PhyRegisterSet& rs) const {
return rs_ == rs.rs_;
}
constexpr bool Empty() const {
return rs_ == 0;
}
int count() const {
return popcount(rs_);
}
PhyLocation GetFirst() const {
return __builtin_ctz(rs_);
}
constexpr void RemoveFirst() {
rs_ &= (rs_ - 1);
}
void Set(PhyLocation reg) {
rs_ |= (1 << reg);
}
void Reset(PhyLocation reg) {
rs_ &= ~(1 << reg);
}
void ResetAll() {
rs_ = 0;
}
bool Has(PhyLocation reg) const {
return rs_ & (1 << reg);
}
constexpr int GetMask() const {
return rs_;
}
private:
unsigned rs_;
};
std::ostream& operator<<(std::ostream& out, const PhyLocation& loc);
#define ADD_REG(v, ...) | PhyLocation::v
static constexpr PhyRegisterSet ALL_GP_REGISTERS =
PhyRegisterSet() FOREACH_GP(ADD_REG);
static constexpr PhyRegisterSet ALL_XMM_REGISTERS =
PhyRegisterSet() FOREACH_XMM(ADD_REG);
static constexpr PhyRegisterSet ALL_REGISTERS =
ALL_GP_REGISTERS | ALL_XMM_REGISTERS;
#undef ADD_REG
static constexpr PhyRegisterSet STACK_REGISTERS =
PhyRegisterSet(PhyLocation::RSP) | PhyLocation::RBP;
static constexpr PhyRegisterSet INIT_REGISTERS =
ALL_REGISTERS - STACK_REGISTERS;
static constexpr PhyRegisterSet CALLER_SAVE_REGS =
PhyRegisterSet(PhyLocation::RAX) | PhyLocation::RCX | PhyLocation::RDX |
PhyLocation::RSI | PhyLocation::RDI | PhyLocation::R8 | PhyLocation::R9 |
PhyLocation::R10 | PhyLocation::R11 | ALL_XMM_REGISTERS;
static constexpr PhyRegisterSet CALLEE_SAVE_REGS =
INIT_REGISTERS - CALLER_SAVE_REGS;
static constexpr PhyLocation GP_ARGUMENT_REGS[] = {
PhyLocation::RDI,
PhyLocation::RSI,
PhyLocation::RDX,
PhyLocation::RCX,
PhyLocation::R8,
PhyLocation::R9};
static constexpr PhyLocation FP_ARGUMENT_REGS[] = {
PhyLocation::XMM0,
PhyLocation::XMM1,
PhyLocation::XMM2,
PhyLocation::XMM3,
PhyLocation::XMM4,
PhyLocation::XMM5,
PhyLocation::XMM6,
PhyLocation::XMM7};
static constexpr size_t FP_ARGUMENT_REG_COUNT = ARRAYSIZE(FP_ARGUMENT_REGS);
// For coroutines, we want to unify on the "awaited" flag being in the 3rd
// argument register. This avoids having to spill it to memory during a JIT
// function prologue and then immediately pulling it back out. It's the 3rd
// argument because this is where this value will necessarily be for
// vector-called functions (i.e. nargsf).
static constexpr size_t CORO_NARGSF_ARG_IDX = 2;
inline size_t numGpRegsForArgs(const PyCodeObject* code) {
return std::size(GP_ARGUMENT_REGS) - (code->co_flags & CO_COROUTINE ? 1 : 0);
}
} // namespace codegen
} // namespace jit
namespace std {
template <>
struct hash<jit::codegen::PhyLocation> {
std::size_t operator()(jit::codegen::PhyLocation const& s) const noexcept {
return s.loc;
}
};
}; // namespace std