-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathAMD64Types.h
281 lines (227 loc) · 8.47 KB
/
AMD64Types.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
#ifndef AMD64_TYPES_H
#define AMD64_TYPES_H
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinTypes.h"
#include "fadec-enc.h"
#include "mlir/IR/OpDefinition.h"
#include <llvm/ADT/StringMap.h>
using FeMnem = uint64_t;
namespace amd64{
namespace conditional{
enum predicate{
Z = 0, // E
NZ, // NE
L,
GE,
LE,
G,
C, // B
NC, // AE
BE,
A,
NONE = 0xFF
};
inline predicate invert(predicate pred){
return static_cast<predicate>(pred ^ 0x1);
}
}
namespace SizeChange {
enum Kind {
None,
SExt,
ZExt,
Trunc,
};
}
// TODO template this and only have one of the two
// chmpxchg16b is the worst for this
struct OperandRegisterConstraint{
#define NO_CONSTRAINT -1
int8_t which = NO_CONSTRAINT;
FeReg reg;
/// TODO I hope this doesn't make it less efficient, because its not POD anymore
bool constrainsReg() const{
return which != NO_CONSTRAINT;
}
};
struct ResultRegisterConstraint{
int8_t which = NO_CONSTRAINT;
FeReg reg;
bool constrainsReg() const{
return which != NO_CONSTRAINT;
}
};
// TODO think if 'which' can be eliminated somehow, this is quite ugly
// a light wrapper around a pair
template<typename T>
struct Constraints{
T first, second;
constexpr Constraints(T first, T second): first(first), second(second){}
constexpr Constraints(std::pair<T, T> pair): first(pair.first), second(pair.second){}
operator std::pair<T, T>(){
return std::make_pair(first, second);
}
// TODO also a bit ugly
// TODO I think this is also not right, because the operand index i, with which it is called, doesn't always equate to the ith *register* operand (memory op in the middle/...)
T operator [](int i){
if(i == 0)
return first.which == 0 ? first : T();
else if(i == 1)
return first.which == 1 ? first : (second.which == 1 ? second : T());
else
llvm_unreachable("invalid index");
}
};
using OperandRegisterConstraints = Constraints<OperandRegisterConstraint>;
using ResultRegisterConstraints = Constraints<ResultRegisterConstraint>;
// TODO maybe actually don't put any of this into the namespace?
struct GlobalSymbolInfo{
llvm::SmallVector<uint8_t, 8> bytes;
unsigned alignment;
// TODO linkage/visibility
intptr_t addrInDataSection; // TODO probably rename, this can also be outside the data section
};
using GlobalsInfo = llvm::StringMap<GlobalSymbolInfo>;
// TODO put the rest of the stuff into the namespace as well
}
// this is heavily based on mlir/test/lib/Dialect/Test/TestOps.td/cpp/h
/// to support saving register info on multi result instructions (up to 2 results for now)
struct ResultRegisters{
// TODO these take up 32 bits each, even tho the information fits within 16 bits by simply shifting, and into 8 bits from an information theory perspective. But this makes accessing the registers much easier, so optimize this later.
FeReg reg1;
FeReg reg2;
ResultRegisters(): reg1(FE_NOREG), reg2(FE_NOREG){}
ResultRegisters(FeReg reg1, FeReg reg2): reg1(reg1), reg2(reg2){
// assert this, because the combine methods would otherwise fail
assert(static_cast<uint16_t>(reg1) == reg1 && static_cast<uint16_t>(reg2) == reg2 && "registers contain too much information to be saved in this class");
}
/// if this class is implicitly cast to a FeReg, return the first register, as this is the most common case
inline bool reg1Empty() const{
return reg1 == FE_NOREG;
}
inline bool reg2Empty() const{
return reg2 == FE_NOREG;
}
inline uint32_t combined() const{
return reg1 | (reg2 << 16);
}
inline void setFromCombined(uint32_t combined){
reg1 = static_cast<FeReg>(combined & 0x0000FFFF);
reg2 = static_cast<FeReg>(combined & 0xFFFF0000);
}
// these 3 are to use this as an mlir property:
inline mlir::Attribute asAttribute(mlir::MLIRContext* ctx) const{
mlir::Builder builder(ctx);
return builder.getI32IntegerAttr(combined());
}
inline static mlir::LogicalResult setFromAttr(ResultRegisters& prop, mlir::Attribute attr, mlir::InFlightDiagnostic* diag){
mlir::IntegerAttr intAttr = attr.dyn_cast<mlir::IntegerAttr>();
if(!intAttr){
if(diag)
*diag << "expected integer attribute for ResultRegisters";
return mlir::failure();
}
prop.setFromCombined(intAttr.getInt());
return mlir::success();
}
inline llvm::hash_code hash() const {
return llvm::hash_value({reg1, reg2});
}
private:
/// if the internal representation of FeReg changes, this needs to be updated
inline static uint16_t transform(FeReg reg){
return reg;
}
inline static FeReg transform(uint16_t reg){
return static_cast<FeReg>(reg);
}
};
/// overarching property class for all instructions
struct InstructionInfo{
ResultRegisters regs;
// TODO maybe optimize this later, but for now this is fine
int64_t imm; // only has a useful value if the instruction has the Special<HasImm> trait
/// returns if true if not all of the registers were constrained, i.e. if there is still something to do on this instruction for the register allocator
inline bool setRegsFromConstraints(const amd64::ResultRegisterConstraints& constraints){
auto allConstrained = true;
for(auto constraint : {constraints.first, constraints.second}){
allConstrained &= constraint.constrainsReg();
if(constraint.constrainsReg()){
if(constraint.which == 0){
assert((regs.reg1Empty() || regs.reg1 == constraint.reg) && "register constraint mismatch");
regs.reg1 = constraint.reg;
}else if(constraint.which == 1){
assert((regs.reg2Empty() || regs.reg2 == constraint.reg) && "register constraint mismatch");
regs.reg2 = constraint.reg;
}else{
assert(false && "invalid result number");
}
}
}
return !allConstrained;
}
// these 3 are to use this as an mlir property:
inline mlir::Attribute asAttribute(mlir::MLIRContext* ctx) const{
mlir::Builder builder(ctx);
return builder.getI64ArrayAttr({regs.combined(), imm}); // this is somewhat inefficient, but there is no better way to do this as far as I know, and it shouldn't happend during normal compilation
}
inline static mlir::LogicalResult setFromAttr(InstructionInfo& prop, mlir::Attribute attr, mlir::InFlightDiagnostic* diag){
auto arrayAttr = attr.cast<mlir::ArrayAttr>();
if(!arrayAttr){
if(diag)
*diag << "expected array attribute for InstructionInfo";
return mlir::failure();
}
auto intAttrs = arrayAttr.getValue();
auto immAttr = intAttrs[1].dyn_cast<mlir::IntegerAttr>();
if(!immAttr){
if(diag)
*diag << "expected integer attribute for InstructionInfo immediate";
return mlir::failure();
}
prop.imm = immAttr.getInt();
return ResultRegisters::setFromAttr(prop.regs, *intAttrs.begin(), diag);
}
inline llvm::hash_code hash() const {
return llvm::hash_combine(regs.hash(), imm);
}
inline bool operator==(const InstructionInfo& other) const{
return regs.combined() == other.regs.combined() && imm == other.imm;
}
};
// === traits ===
namespace amd64{
enum class Special{
IDIV, // sign-extending of the upper part
DIV, // zero-extending of the upper part
HasImm
};
}
// the way to define these seems so hacky...
namespace mlir::OpTrait{
// TODO does this need to be parametrized? Is 0 = 0 enough?
// probably not. Anything but 0 is not supported atm anyway
/// lots of x86 instructions have the first operand as the destination -> this trait signals that
template<unsigned N>
class Operand0IsDestN{
public:
template<typename ConcreteType>
class Impl:public mlir::OpTrait::TraitBase<ConcreteType, Impl> {
};
};
template<amd64::Special specialKind>
class SpecialCase{
public:
template<typename ConcreteType>
class Impl:public mlir::OpTrait::TraitBase<ConcreteType, Impl> {
};
};
} // namespace mlir::OpTrait
namespace mlir::TypeTrait{
} // namespace mlir::TypeTrait
// my own interfaces
#include "AMD64/AMD64OpInterfaces.h.inc"
#include "AMD64/AMD64TypeInterfaces.h.inc"
#define GET_TYPEDEF_CLASSES
#include "AMD64/AMD64OpsTypes.h.inc"
#endif // AMD64_TYPES_H