Skip to content

Commit dbc6b0f

Browse files
kddnewtonmeta-codesync[bot]
authored andcommitted
Rely on a generic arch.h file instead of x86-64
Summary: Previously many different files were relying on x86-64 internals. Instead, rely on an arch.h file that imports the platform-specific internals necessary. Each file is responsible for implementing the same interface. Reviewed By: alexmalyshev Differential Revision: D86322928 fbshipit-source-id: 8e6ba2b7a0325284d3fab8c6a7583ec4425c72e0
1 parent cd33607 commit dbc6b0f

25 files changed

Lines changed: 508 additions & 139 deletions

cinderx/Jit/codegen/arch.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) Meta Platforms, Inc. and affiliates.
2+
3+
#include "cinderx/Jit/codegen/arch.h"
4+
5+
namespace jit::codegen {
6+
7+
std::ostream& operator<<(std::ostream& out, const PhyLocation& loc) {
8+
return out << loc.toString();
9+
}
10+
11+
} // namespace jit::codegen

cinderx/Jit/codegen/arch.h

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22

33
#pragma once
44

5+
#include "cinderx/Jit/codegen/arch/detection.h"
6+
#include "fmt/ostream.h"
7+
58
#include <asmjit/asmjit.h>
69

7-
#if defined(__x86_64__)
8-
#define CINDER_X86_64
10+
#include <iosfwd>
11+
12+
#if defined(CINDER_X86_64)
13+
14+
#include "cinderx/Jit/codegen/arch/x86_64.h"
915

1016
#include <asmjit/x86/x86builder.h>
1117
#include <asmjit/x86/x86emitter.h>
@@ -26,19 +32,28 @@ using EmitterExplicitT = asmjit::x86::EmitterExplicitT<T>;
2632
// If you change this register you'll also need to change the deopt
2733
// trampoline code that saves all registers.
2834
constexpr auto reg_scratch_deopt = asmjit::x86::r15;
29-
constexpr auto reg_general_return_64 = asmjit::x86::rax;
35+
constexpr auto reg_general_return_loc = RAX;
36+
constexpr auto reg_general_auxilary_return_loc = RDX;
37+
constexpr auto reg_double_return_loc = XMM0;
38+
constexpr auto reg_double_auxilary_return_loc = XMM1;
39+
constexpr auto reg_frame_pointer_loc = RBP;
40+
constexpr auto reg_stack_pointer_loc = RSP;
3041

3142
} // namespace jit::codegen::arch
3243

3344
#else
34-
// This macro is a marker for places that need platform-specific code.
35-
#define CINDER_UNSUPPORTED
45+
46+
#include "cinderx/Jit/codegen/arch/unknown.h"
3647

3748
namespace jit::codegen::arch {
3849

3950
class Builder : public asmjit::BaseBuilder {
4051
public:
41-
explicit Builder(asmjit::CodeHolder* code = nullptr) noexcept;
52+
explicit Builder(asmjit::CodeHolder* code = nullptr) noexcept {
53+
if (code) {
54+
code->attach(this);
55+
}
56+
}
4257
};
4358

4459
using Emitter = asmjit::BaseEmitter;
@@ -51,8 +66,37 @@ template <typename T>
5166
struct EmitterExplicitT {};
5267

5368
constexpr auto reg_scratch_deopt = asmjit::BaseReg();
54-
constexpr auto reg_general_return_64 = asmjit::BaseReg();
69+
constexpr auto reg_general_return_loc = R0;
70+
constexpr auto reg_general_auxilary_return_loc = R1;
71+
constexpr auto reg_double_return_loc = D0;
72+
constexpr auto reg_double_auxilary_return_loc = D1;
73+
constexpr auto reg_frame_pointer_loc = R3;
74+
constexpr auto reg_stack_pointer_loc = SP;
5575

5676
} // namespace jit::codegen::arch
5777

5878
#endif
79+
80+
namespace jit::codegen {
81+
82+
std::ostream& operator<<(std::ostream& out, const PhyLocation& loc);
83+
84+
} // namespace jit::codegen
85+
86+
inline auto format_as(jit::codegen::RegId reg) {
87+
return fmt::underlying(reg);
88+
}
89+
90+
namespace std {
91+
92+
template <>
93+
struct hash<jit::codegen::PhyLocation> {
94+
std::size_t operator()(jit::codegen::PhyLocation const& s) const noexcept {
95+
return s.loc;
96+
}
97+
};
98+
99+
} // namespace std
100+
101+
template <>
102+
struct fmt::formatter<jit::codegen::PhyLocation> : fmt::ostream_formatter {};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) Meta Platforms, Inc. and affiliates.
2+
3+
#pragma once
4+
5+
#if defined(__x86_64__)
6+
7+
#define CINDER_X86_64
8+
9+
#else
10+
11+
#define CINDER_UNKNOWN
12+
13+
// This macro is a marker for places that need platform-specific code.
14+
#define CINDER_UNSUPPORTED
15+
16+
#endif
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Meta Platforms, Inc. and affiliates.
2+
3+
#include "cinderx/Jit/codegen/arch/unknown.h"
4+
5+
#include "cinderx/Jit/codegen/arch/detection.h"
6+
7+
#ifdef CINDER_UNKNOWN
8+
9+
namespace jit::codegen {
10+
11+
PhyLocation PhyLocation::parse(std::string_view name) {
12+
#define FIND_GP_REG(V) \
13+
if (name == #V) { \
14+
return PhyLocation{RegId::V, 64}; \
15+
}
16+
17+
#define FIND_VECD_REG(V) \
18+
if (name == #V) { \
19+
return PhyLocation{RegId::V, 64}; \
20+
}
21+
22+
FOREACH_GP(FIND_GP_REG)
23+
FOREACH_VECD(FIND_VECD_REG)
24+
if (name == "SP") {
25+
return PhyLocation{SP, 64};
26+
}
27+
#undef FIND_GP_REG
28+
#undef FIND_VECD_REG
29+
JIT_ABORT("Unrecognized register {}", name);
30+
}
31+
32+
std::string PhyLocation::toString() const {
33+
if (is_memory()) {
34+
return fmt::format("[FP({})]", loc);
35+
}
36+
return std::string{name(static_cast<RegId>(loc))};
37+
}
38+
39+
} // namespace jit::codegen
40+
41+
#endif

cinderx/Jit/codegen/arch/unknown.h

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
// Copyright (c) Meta Platforms, Inc. and affiliates.
2+
3+
#pragma once
4+
5+
#include "cinderx/Common/log.h"
6+
#include "cinderx/Common/util.h"
7+
8+
#include <fmt/format.h>
9+
10+
#include <array>
11+
#include <string>
12+
#include <string_view>
13+
14+
namespace jit::codegen {
15+
16+
#define FOREACH_GP(X) \
17+
X(R0) \
18+
X(R1) \
19+
X(R2) \
20+
X(R3)
21+
22+
#define FOREACH_VECD(X) \
23+
X(D0) \
24+
X(D1) \
25+
X(D2) \
26+
X(D3)
27+
28+
enum class RegId : uint32_t {
29+
#define DEFINE_REG(V) V,
30+
FOREACH_GP(DEFINE_REG) FOREACH_VECD(DEFINE_REG)
31+
#undef DEFINE_REG
32+
SP,
33+
};
34+
35+
constexpr uint32_t raw(RegId id) {
36+
return static_cast<uint32_t>(id);
37+
}
38+
39+
#define COUNT_REGS(...) +1
40+
constexpr int NUM_GP_REGS = FOREACH_GP(COUNT_REGS);
41+
constexpr int VECD_REG_BASE = raw(RegId::D0);
42+
constexpr int NUM_VECD_REGS = FOREACH_VECD(COUNT_REGS);
43+
constexpr int NUM_REGS = NUM_GP_REGS + NUM_VECD_REGS;
44+
#undef COUNT_REGS
45+
46+
constexpr std::string_view name(RegId id) {
47+
switch (id) {
48+
#define STRING_REG(V) \
49+
case RegId::V: \
50+
return #V;
51+
52+
FOREACH_GP(STRING_REG)
53+
FOREACH_VECD(STRING_REG)
54+
case RegId::SP:
55+
return "SP";
56+
#undef STRING_REG
57+
default:
58+
JIT_ABORT("Unrecognized register ID {}", raw(id));
59+
}
60+
}
61+
62+
// A physical location (register or stack slot). If this represents a stack
63+
// slot (is_memory() is true) then `loc` is relative to R3.
64+
struct PhyLocation {
65+
static constexpr int REG_INVALID = -1;
66+
67+
#define DEFINE_REG(V) static constexpr int V = raw(RegId::V);
68+
FOREACH_GP(DEFINE_REG)
69+
FOREACH_VECD(DEFINE_REG)
70+
static constexpr int SP = raw(RegId::SP);
71+
#undef DEFINE_REG
72+
73+
// Parse a register name and return the corresponding physical register.
74+
// Return REG_INVALID if the name is not a valid register name. Does not
75+
// support parsing stack slots.
76+
static PhyLocation parse(std::string_view name);
77+
78+
int32_t loc{REG_INVALID};
79+
uint32_t bitSize{64};
80+
81+
PhyLocation() = default;
82+
83+
/* implicit */ constexpr PhyLocation(RegId reg, size_t size = 64)
84+
: PhyLocation{static_cast<int>(reg), size} {}
85+
86+
/* implicit */ constexpr PhyLocation(RegId reg, int size)
87+
: PhyLocation{static_cast<int>(reg), static_cast<size_t>(size)} {}
88+
89+
/* implicit */ constexpr PhyLocation(int loc, size_t size = 64)
90+
: loc{loc}, bitSize{static_cast<uint32_t>(size)} {}
91+
92+
/* implicit */ constexpr PhyLocation(int loc, int size)
93+
: PhyLocation{loc, static_cast<size_t>(size)} {}
94+
95+
bool is_memory() const {
96+
return loc < 0;
97+
}
98+
99+
bool is_register() const {
100+
return loc >= 0;
101+
}
102+
103+
bool is_gp_register() const {
104+
return is_register() && loc < VECD_REG_BASE;
105+
}
106+
107+
bool is_fp_register() const {
108+
return is_register() && loc >= VECD_REG_BASE;
109+
}
110+
111+
std::string toString() const;
112+
113+
bool operator==(const PhyLocation& rhs) const {
114+
return loc == rhs.loc;
115+
}
116+
117+
bool operator!=(const PhyLocation& rhs) const {
118+
return loc != rhs.loc;
119+
}
120+
};
121+
122+
// Define global definitions like `R0` and `D0`.
123+
#define DEFINE_PHY_REG(V) constexpr PhyLocation V{RegId::V, 64};
124+
125+
FOREACH_GP(DEFINE_PHY_REG)
126+
FOREACH_VECD(DEFINE_PHY_REG)
127+
constexpr PhyLocation SP{RegId::SP, 64};
128+
129+
#undef DEFINE_PHY_GP_REG
130+
#undef DEFINE_PHY_VECD_REG
131+
132+
class PhyRegisterSet {
133+
public:
134+
constexpr PhyRegisterSet() : rs_(0) {}
135+
explicit constexpr PhyRegisterSet(PhyLocation r) : rs_(0) {
136+
rs_ |= (1 << r.loc);
137+
}
138+
139+
constexpr PhyRegisterSet operator|(PhyLocation reg) const {
140+
PhyRegisterSet set;
141+
set.rs_ = rs_ | (1 << reg.loc);
142+
return set;
143+
}
144+
145+
constexpr PhyRegisterSet operator|(const PhyRegisterSet& rs) const {
146+
PhyRegisterSet res;
147+
res.rs_ = rs_ | rs.rs_;
148+
return res;
149+
}
150+
151+
PhyRegisterSet& operator|=(const PhyRegisterSet& rs) {
152+
rs_ |= rs.rs_;
153+
return *this;
154+
}
155+
156+
constexpr PhyRegisterSet operator-(PhyLocation rs) const {
157+
return operator-(PhyRegisterSet(rs));
158+
}
159+
160+
constexpr PhyRegisterSet operator-(PhyRegisterSet rs) const {
161+
PhyRegisterSet set;
162+
set.rs_ = rs_ & ~(rs.rs_);
163+
return set;
164+
}
165+
166+
constexpr PhyRegisterSet operator&(PhyRegisterSet rs) const {
167+
PhyRegisterSet set;
168+
set.rs_ = rs_ & rs.rs_;
169+
return set;
170+
}
171+
172+
constexpr bool operator==(const PhyRegisterSet& rs) const {
173+
return rs_ == rs.rs_;
174+
}
175+
176+
constexpr bool Empty() const {
177+
return rs_ == 0ULL;
178+
}
179+
180+
int count() const {
181+
return popcount(rs_);
182+
}
183+
184+
PhyLocation GetFirst() const {
185+
return __builtin_ctz(rs_);
186+
}
187+
188+
constexpr void RemoveFirst() {
189+
rs_ &= (rs_ - 1);
190+
}
191+
192+
void Set(PhyLocation reg) {
193+
rs_ |= (1 << reg.loc);
194+
}
195+
void Reset(PhyLocation reg) {
196+
rs_ &= ~(1 << reg.loc);
197+
}
198+
void ResetAll() {
199+
rs_ = 0;
200+
}
201+
202+
bool Has(PhyLocation reg) const {
203+
return rs_ & (1 << reg.loc);
204+
}
205+
206+
private:
207+
uint32_t rs_;
208+
};
209+
210+
#define ADD_REG(V) | PhyLocation::V
211+
constexpr PhyRegisterSet ALL_GP_REGISTERS =
212+
PhyRegisterSet() FOREACH_GP(ADD_REG);
213+
constexpr PhyRegisterSet ALL_VECD_REGISTERS =
214+
PhyRegisterSet() FOREACH_VECD(ADD_REG);
215+
constexpr PhyRegisterSet ALL_REGISTERS = ALL_GP_REGISTERS | ALL_VECD_REGISTERS;
216+
#undef ADD_REG
217+
218+
constexpr PhyRegisterSet STACK_REGISTERS = PhyRegisterSet();
219+
constexpr PhyRegisterSet INIT_REGISTERS = ALL_REGISTERS - STACK_REGISTERS;
220+
constexpr PhyRegisterSet CALLEE_SAVE_REGS = PhyRegisterSet();
221+
constexpr PhyRegisterSet CALLER_SAVE_REGS = INIT_REGISTERS - CALLEE_SAVE_REGS;
222+
223+
constexpr auto ARGUMENT_REGS = std::to_array({R0});
224+
constexpr auto RETURN_REGS = std::to_array({R0});
225+
constexpr auto FP_ARGUMENT_REGS = std::to_array({D0});
226+
227+
// This is where the function prologue will initially store this data at entry
228+
// to the function body. The register allocator may move things around from
229+
// there.
230+
constexpr PhyLocation INITIAL_EXTRA_ARGS_REG = R1;
231+
constexpr PhyLocation INITIAL_TSTATE_REG = R2;
232+
constexpr PhyLocation INITIAL_INTERPRETER_FRAME_REG = R3;
233+
// This is often provided by the first argument in the vector call protocol.
234+
constexpr PhyLocation INITIAL_FUNC_REG = ARGUMENT_REGS[0];
235+
236+
} // namespace jit::codegen

0 commit comments

Comments
 (0)