2016-07-01 14:01:06 +01:00
|
|
|
/* This file is part of the dynarmic project.
|
|
|
|
* Copyright (c) 2016 MerryMage
|
|
|
|
* This software may be used and distributed according to the terms of the GNU
|
|
|
|
* General Public License version 2 or any later version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2016-08-17 15:53:36 +01:00
|
|
|
#include <array>
|
2016-07-01 14:01:06 +01:00
|
|
|
#include <map>
|
2016-08-17 15:53:36 +01:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <boost/optional.hpp>
|
2016-08-24 20:07:08 +01:00
|
|
|
#include <xbyak.h>
|
2016-07-01 14:01:06 +01:00
|
|
|
|
2016-08-07 18:08:48 +01:00
|
|
|
#include "backend_x64/block_of_code.h"
|
2016-08-24 20:07:08 +01:00
|
|
|
#include "backend_x64/hostloc.h"
|
2016-07-01 14:01:06 +01:00
|
|
|
#include "backend_x64/jitstate.h"
|
|
|
|
#include "common/common_types.h"
|
2016-08-17 15:53:36 +01:00
|
|
|
#include "frontend/ir/microinstruction.h"
|
|
|
|
#include "frontend/ir/value.h"
|
2016-07-01 14:01:06 +01:00
|
|
|
|
|
|
|
namespace Dynarmic {
|
|
|
|
namespace BackendX64 {
|
|
|
|
|
2016-08-24 20:07:08 +01:00
|
|
|
struct OpArg {
|
|
|
|
OpArg() : type(OPERAND), inner_operand() {}
|
|
|
|
OpArg(const Xbyak::Address& address) : type(ADDRESS), inner_address(address) {}
|
2016-08-26 15:23:38 +01:00
|
|
|
OpArg(const Xbyak::Reg& reg) : type(REG), inner_reg(reg) {}
|
2016-08-24 20:07:08 +01:00
|
|
|
|
|
|
|
Xbyak::Operand& operator*() {
|
|
|
|
switch (type) {
|
|
|
|
case ADDRESS:
|
|
|
|
return inner_address;
|
|
|
|
case OPERAND:
|
|
|
|
return inner_operand;
|
2016-08-26 15:23:38 +01:00
|
|
|
case REG:
|
|
|
|
return inner_reg;
|
2016-08-24 20:07:08 +01:00
|
|
|
}
|
|
|
|
ASSERT_MSG(false, "Unreachable");
|
|
|
|
}
|
|
|
|
|
|
|
|
void setBit(int bits) {
|
|
|
|
switch (type) {
|
|
|
|
case ADDRESS:
|
|
|
|
inner_address.setBit(bits);
|
|
|
|
return;
|
|
|
|
case OPERAND:
|
|
|
|
inner_operand.setBit(bits);
|
|
|
|
return;
|
2016-08-26 15:23:38 +01:00
|
|
|
case REG:
|
|
|
|
switch (bits) {
|
|
|
|
case 8:
|
|
|
|
inner_reg = inner_reg.cvt8();
|
|
|
|
return;
|
|
|
|
case 16:
|
|
|
|
inner_reg = inner_reg.cvt16();
|
|
|
|
return;
|
|
|
|
case 32:
|
|
|
|
inner_reg = inner_reg.cvt32();
|
|
|
|
return;
|
|
|
|
case 64:
|
|
|
|
inner_reg = inner_reg.cvt64();
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
ASSERT_MSG(false, "Invalid bits");
|
|
|
|
return;
|
|
|
|
}
|
2016-08-24 20:07:08 +01:00
|
|
|
}
|
|
|
|
ASSERT_MSG(false, "Unreachable");
|
|
|
|
}
|
2016-07-01 14:01:06 +01:00
|
|
|
|
2016-08-24 20:07:08 +01:00
|
|
|
private:
|
|
|
|
enum {
|
|
|
|
OPERAND,
|
|
|
|
ADDRESS,
|
2016-08-26 15:23:38 +01:00
|
|
|
REG,
|
2016-08-24 20:07:08 +01:00
|
|
|
} type;
|
|
|
|
|
|
|
|
union {
|
|
|
|
Xbyak::Operand inner_operand;
|
|
|
|
Xbyak::Address inner_address;
|
2016-08-26 15:23:38 +01:00
|
|
|
Xbyak::Reg inner_reg;
|
2016-08-24 20:07:08 +01:00
|
|
|
};
|
2016-07-01 14:01:06 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
class RegAlloc final {
|
|
|
|
public:
|
2016-08-07 18:08:48 +01:00
|
|
|
RegAlloc(BlockOfCode* code) : code(code) {}
|
2016-07-01 14:01:06 +01:00
|
|
|
|
|
|
|
/// Late-def
|
2016-08-24 20:07:08 +01:00
|
|
|
Xbyak::Reg64 DefGpr(IR::Inst* def_inst, HostLocList desired_locations = any_gpr) {
|
|
|
|
return HostLocToReg64(DefHostLocReg(def_inst, desired_locations));
|
|
|
|
}
|
|
|
|
Xbyak::Xmm DefXmm(IR::Inst* def_inst, HostLocList desired_locations = any_xmm) {
|
|
|
|
return HostLocToXmm(DefHostLocReg(def_inst, desired_locations));
|
|
|
|
}
|
2016-08-05 14:10:39 +01:00
|
|
|
void RegisterAddDef(IR::Inst* def_inst, const IR::Value& use_inst);
|
2016-07-01 14:01:06 +01:00
|
|
|
/// Early-use, Late-def
|
2016-08-24 20:07:08 +01:00
|
|
|
Xbyak::Reg64 UseDefGpr(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations = any_gpr) {
|
|
|
|
return HostLocToReg64(UseDefHostLocReg(use_value, def_inst, desired_locations));
|
|
|
|
}
|
|
|
|
Xbyak::Xmm UseDefXmm(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations = any_xmm) {
|
|
|
|
return HostLocToXmm(UseDefHostLocReg(use_value, def_inst, desired_locations));
|
|
|
|
}
|
|
|
|
std::tuple<OpArg, Xbyak::Reg64> UseDefOpArgGpr(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations = any_gpr) {
|
|
|
|
OpArg op;
|
|
|
|
HostLoc host_loc;
|
|
|
|
std::tie(op, host_loc) = UseDefOpArgHostLocReg(use_value, def_inst, desired_locations);
|
|
|
|
return std::make_tuple(op, HostLocToReg64(host_loc));
|
|
|
|
}
|
|
|
|
std::tuple<OpArg, Xbyak::Xmm> UseDefOpArgXmm(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations = any_gpr) {
|
|
|
|
OpArg op;
|
|
|
|
HostLoc host_loc;
|
|
|
|
std::tie(op, host_loc) = UseDefOpArgHostLocReg(use_value, def_inst, desired_locations);
|
|
|
|
return std::make_tuple(op, HostLocToXmm(host_loc));
|
|
|
|
}
|
2016-07-01 14:01:06 +01:00
|
|
|
/// Early-use
|
2016-08-24 20:07:08 +01:00
|
|
|
Xbyak::Reg64 UseGpr(IR::Value use_value, HostLocList desired_locations = any_gpr) {
|
|
|
|
return HostLocToReg64(UseHostLocReg(use_value, desired_locations));
|
|
|
|
}
|
|
|
|
Xbyak::Xmm UseXmm(IR::Value use_value, HostLocList desired_locations = any_xmm) {
|
|
|
|
return HostLocToXmm(UseHostLocReg(use_value, desired_locations));
|
|
|
|
}
|
|
|
|
OpArg UseOpArg(IR::Value use_value, HostLocList desired_locations);
|
2016-07-18 15:11:16 +01:00
|
|
|
/// Early-use, Destroyed
|
2016-08-24 20:07:08 +01:00
|
|
|
Xbyak::Reg64 UseScratchGpr(IR::Value use_value, HostLocList desired_locations = any_gpr) {
|
|
|
|
return HostLocToReg64(UseScratchHostLocReg(use_value, desired_locations));
|
|
|
|
}
|
|
|
|
Xbyak::Xmm UseScratchXmm(IR::Value use_value, HostLocList desired_locations = any_xmm) {
|
|
|
|
return HostLocToXmm(UseScratchHostLocReg(use_value, desired_locations));
|
|
|
|
}
|
2016-07-01 14:01:06 +01:00
|
|
|
/// Early-def, Late-use, single-use
|
2016-08-24 20:07:08 +01:00
|
|
|
Xbyak::Reg64 ScratchGpr(HostLocList desired_locations = any_gpr) {
|
|
|
|
return HostLocToReg64(ScratchHostLocReg(desired_locations));
|
|
|
|
}
|
|
|
|
Xbyak::Xmm ScratchXmm(HostLocList desired_locations = any_xmm) {
|
|
|
|
return HostLocToXmm(ScratchHostLocReg(desired_locations));
|
|
|
|
}
|
2016-07-01 14:01:06 +01:00
|
|
|
|
2016-07-11 15:28:10 +01:00
|
|
|
/// Late-def for result register, Early-use for all arguments, Each value is placed into registers according to host ABI.
|
2016-07-22 23:55:00 +01:00
|
|
|
void HostCall(IR::Inst* result_def = nullptr, IR::Value arg0_use = {}, IR::Value arg1_use = {}, IR::Value arg2_use = {}, IR::Value arg3_use = {});
|
2016-07-11 15:28:10 +01:00
|
|
|
|
2016-07-01 14:01:06 +01:00
|
|
|
// TODO: Values in host flags
|
|
|
|
|
2016-07-22 23:55:00 +01:00
|
|
|
void DecrementRemainingUses(IR::Inst* value);
|
2016-07-11 22:43:53 +01:00
|
|
|
|
2016-07-01 14:01:06 +01:00
|
|
|
void EndOfAllocScope();
|
|
|
|
|
2016-07-11 22:43:53 +01:00
|
|
|
void AssertNoMoreUses();
|
|
|
|
|
2016-07-04 14:37:50 +01:00
|
|
|
void Reset();
|
|
|
|
|
2016-07-01 14:01:06 +01:00
|
|
|
private:
|
2016-08-02 13:46:12 +01:00
|
|
|
HostLoc SelectARegister(HostLocList desired_locations) const;
|
2016-08-19 03:13:38 +01:00
|
|
|
boost::optional<HostLoc> ValueLocation(const IR::Inst* value) const;
|
2016-07-01 14:01:06 +01:00
|
|
|
bool IsRegisterOccupied(HostLoc loc) const;
|
|
|
|
bool IsRegisterAllocated(HostLoc loc) const;
|
2016-08-05 14:10:39 +01:00
|
|
|
bool IsLastUse(IR::Inst* inst) const;
|
|
|
|
|
2016-08-24 20:07:08 +01:00
|
|
|
HostLoc DefHostLocReg(IR::Inst* def_inst, HostLocList desired_locations);
|
|
|
|
HostLoc UseDefHostLocReg(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations);
|
|
|
|
HostLoc UseDefHostLocReg(IR::Inst* use_inst, IR::Inst* def_inst, HostLocList desired_locations);
|
|
|
|
std::tuple<OpArg, HostLoc> UseDefOpArgHostLocReg(IR::Value use_value, IR::Inst* def_inst, HostLocList desired_locations);
|
|
|
|
HostLoc UseHostLocReg(IR::Value use_value, HostLocList desired_locations);
|
|
|
|
HostLoc UseHostLocReg(IR::Inst* use_inst, HostLocList desired_locations);
|
2016-08-05 14:10:39 +01:00
|
|
|
std::tuple<HostLoc, bool> UseHostLoc(IR::Inst* use_inst, HostLocList desired_locations);
|
2016-08-24 20:07:08 +01:00
|
|
|
HostLoc UseScratchHostLocReg(IR::Value use_value, HostLocList desired_locations);
|
|
|
|
HostLoc UseScratchHostLocReg(IR::Inst* use_inst, HostLocList desired_locations);
|
|
|
|
HostLoc ScratchHostLocReg(HostLocList desired_locations);
|
2016-07-01 14:01:06 +01:00
|
|
|
|
2016-08-02 13:46:12 +01:00
|
|
|
void EmitMove(HostLoc to, HostLoc from);
|
|
|
|
void EmitExchange(HostLoc a, HostLoc b);
|
2016-08-24 20:07:08 +01:00
|
|
|
HostLoc LoadImmediateIntoHostLocReg(IR::Value imm, HostLoc reg);
|
2016-08-05 14:10:39 +01:00
|
|
|
|
2016-07-01 14:01:06 +01:00
|
|
|
void SpillRegister(HostLoc loc);
|
|
|
|
HostLoc FindFreeSpill() const;
|
|
|
|
|
2016-08-07 18:08:48 +01:00
|
|
|
BlockOfCode* code = nullptr;
|
2016-07-01 14:01:06 +01:00
|
|
|
|
2016-08-02 13:46:12 +01:00
|
|
|
struct HostLocInfo {
|
2016-08-05 14:10:39 +01:00
|
|
|
std::vector<IR::Inst*> values; // early value
|
|
|
|
IR::Inst* def = nullptr; // late value
|
|
|
|
bool is_being_used = false;
|
|
|
|
|
|
|
|
bool IsIdle() const {
|
|
|
|
return !is_being_used;
|
|
|
|
}
|
|
|
|
bool IsScratch() const {
|
|
|
|
return is_being_used && !def && values.empty();
|
|
|
|
}
|
|
|
|
bool IsUse() const {
|
|
|
|
return is_being_used && !def && !values.empty();
|
|
|
|
}
|
|
|
|
bool IsDef() const {
|
|
|
|
return is_being_used && def && values.empty();
|
|
|
|
}
|
|
|
|
bool IsUseDef() const {
|
|
|
|
return is_being_used && def && !values.empty();
|
2016-08-02 13:46:12 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
std::array<HostLocInfo, HostLocCount> hostloc_info;
|
|
|
|
HostLocInfo& LocInfo(HostLoc loc) {
|
|
|
|
return hostloc_info[static_cast<size_t>(loc)];
|
|
|
|
}
|
2016-08-16 19:52:46 +01:00
|
|
|
const HostLocInfo& LocInfo(HostLoc loc) const {
|
2016-08-02 13:46:12 +01:00
|
|
|
return hostloc_info[static_cast<size_t>(loc)];
|
|
|
|
}
|
2016-07-01 14:01:06 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace BackendX64
|
|
|
|
} // namespace Dynarmic
|