forked from xcore/tool_axe
-
Notifications
You must be signed in to change notification settings - Fork 1
/
JITOptimize.cpp
256 lines (241 loc) · 7.79 KB
/
JITOptimize.cpp
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
// Copyright (c) 2012, Richard Osborne, All rights reserved
// This software is freely distributable under a derivative of the
// University of Illinois/NCSA Open Source License posted in
// LICENSE.txt and at <http://github.xcore.com/>
#include "JITOptimize.h"
#include "Register.h"
#include "InstructionProperties.h"
#include <algorithm>
#include <iostream>
#include <cassert>
#include <cstring>
// baseReg + offsetImm + scale * offsetReg
class MemoryAccess {
Register::Reg baseReg;
uint32_t offsetImm;
unsigned scale;
Register::Reg offsetReg;
unsigned size;
bool isStore;
public:
MemoryAccess() {}
MemoryAccess(Register::Reg base, unsigned si, bool st) :
baseReg(base),
offsetImm(0),
scale(0),
size(si),
isStore(st)
{
}
MemoryAccess &addRegisterOffset(unsigned s, Register::Reg r) {
assert(scale == 0);
scale = scale;
offsetReg = r;
// Canonicalise.
if (scale == 1 && offsetReg < baseReg)
std::swap(offsetReg, baseReg);
return *this;
}
MemoryAccess &addImmOffset(uint32_t offset) {
offsetImm += offset;
return *this;
}
Register::Reg getBaseReg() const { return baseReg; }
unsigned getScale() const { return scale; }
Register::Reg getOffsetReg() const { return baseReg; }
uint32_t getOffsetImm() const { return offsetImm; }
unsigned getSize() const { return size; }
bool getIsStore() const { return isStore; }
};
class MemoryCheckCandidate {
unsigned earliestIndex;
unsigned instructionIndex;
MemoryCheck *check;
public:
MemoryCheckCandidate(unsigned e, unsigned i, MemoryCheck *c) :
earliestIndex(e),
instructionIndex(i),
check(c) {}
unsigned getEarliestIndex() const { return earliestIndex; }
unsigned getInstructionIndex() const { return instructionIndex; }
MemoryCheck *getMemoryCheck() { return check; }
};
static bool
getInstructionMemoryAccess(InstructionOpcode opc, Operands ops,
MemoryAccess &access)
{
bool isStore = false;
switch (opc) {
default: assert(0 && "Unexpected opcode");
case LDWCP_ru6:
case LDWCP_lru6:
access = MemoryAccess(Register::CP, 4, false).addImmOffset(ops.ops[1]);
return true;
case LDWCPL_u10:
case LDWCPL_lu10:
access = MemoryAccess(Register::CP, 4, false).addImmOffset(ops.ops[0]);
return true;
case STWDP_ru6:
case STWDP_lru6:
isStore = true;
// Fallthrough.
case LDWDP_ru6:
case LDWDP_lru6:
access = MemoryAccess(Register::DP, 4, isStore).addImmOffset(ops.ops[1]);
return true;
case STWSP_ru6:
case STWSP_lru6:
isStore = true;
// Fallthrough.
case LDWSP_ru6:
case LDWSP_lru6:
access = MemoryAccess(Register::SP, 4, isStore).addImmOffset(ops.ops[1]);
return true;
case ST8_l3r:
isStore = true;
// Fallthrough.
case LD8U_3r:
access = MemoryAccess(Register::Reg(ops.ops[1]), 1, isStore)
.addRegisterOffset(1, Register::Reg(ops.ops[2]));
return true;
case ST16_l3r:
isStore = true;
// Fallthrough.
case LD16S_3r:
access = MemoryAccess(Register::Reg(ops.ops[1]), 2, isStore)
.addRegisterOffset(2, Register::Reg(ops.ops[2]));
return true;
case STW_l3r:
isStore = true;
// Fallthrough.
case LDW_3r:
access = MemoryAccess(Register::Reg(ops.ops[1]), 4, isStore)
.addRegisterOffset(4, Register::Reg(ops.ops[2]));
return true;
case STW_2rus:
isStore = true;
// Fallthrough.
case LDW_2rus:
access = MemoryAccess(Register::Reg(ops.ops[1]), 4, isStore)
.addImmOffset(ops.ops[2]);
return true;
case ENTSP_u6:
case ENTSP_lu6:
if (ops.ops[0] == 0)
return false;
access = MemoryAccess(Register::SP, 4, true);
return true;
case RETSP_u6:
case RETSP_lu6:
if (ops.ops[0] == 0)
return false;
access = MemoryAccess(Register::SP, 4, false).addImmOffset(ops.ops[0]);
return true;
}
}
static bool isDef(OperandProperties::OpType type)
{
return type == OperandProperties::out || type == OperandProperties::inout;
}
static Register::Reg
getRegister(const InstructionProperties &properties, const Operands &ops,
unsigned i)
{
if (i >= properties.getNumExplicitOperands())
return properties.getImplicitOperand(i - properties.getNumExplicitOperands());
if (properties.getNumExplicitOperands() > 3)
return static_cast<Register::Reg>(ops.lops[i]);
return static_cast<Register::Reg>(ops.ops[i]);
}
static unsigned
getFlagsForCheck(const MemoryAccess &access)
{
unsigned flags = 0;
if (access.getSize() > 1)
flags |= MemoryCheck::CheckAlignment;
flags |= MemoryCheck::CheckAddress;
if (access.getIsStore())
flags |= MemoryCheck::CheckInvalidation;
return flags;
}
struct MemoryCheckState {
// Index of the first instruction where the value in the specified register
// is available.
unsigned regDefs[Register::NUM_REGISTERS];
unsigned char minAlignment[Register::NUM_REGISTERS];
MemoryCheckState() {
std::memset(regDefs, 0, sizeof(regDefs));
std::memset(minAlignment, 1, sizeof(minAlignment));
}
void update(InstructionOpcode opc, const Operands &ops,
unsigned nextOffset);
void setMinAlignment(Register::Reg reg, unsigned char value) {
minAlignment[reg] = value;
}
unsigned char getMinAlignment(Register::Reg reg) const {
return minAlignment[reg];
}
};
void MemoryCheckState::
update(InstructionOpcode opc, const Operands &ops, unsigned nextOffset)
{
const InstructionProperties &properties = instructionProperties[opc];
for (unsigned i = 0, e = properties.getNumOperands(); i != e; ++i) {
if (!isDef(properties.getOperandType(i)))
continue;
Register::Reg reg = getRegister(properties, ops, i);
regDefs[reg] = nextOffset;
minAlignment[reg] = 1;
}
}
void placeMemoryChecks(std::vector<InstructionOpcode> &opcode,
std::vector<Operands> &operands,
std::queue<std::pair<uint32_t,MemoryCheck*> > &checks)
{
MemoryCheckState state;
std::vector<MemoryCheckCandidate> candidates;
// Gather expressions for memory accesses.
for (unsigned i = 0, e = opcode.size(); i != e; ++i) {
InstructionOpcode opc = opcode[i];
const Operands &ops = operands[i];
InstructionProperties &properties = instructionProperties[opc];
MemoryAccess access;
if (properties.memCheckHoistingOptEnabled() &&
getInstructionMemoryAccess(opc, ops, access)) {
assert(access.getOffsetImm() % access.getSize() == 0);
// Compute the first offset where all registers are available.
unsigned first = state.regDefs[access.getBaseReg()];
if (access.getScale())
first = std::min(first, state.regDefs[access.getOffsetReg()]);
unsigned flags = getFlagsForCheck(access);
bool updateBaseRegAlign = false;
if (flags & MemoryCheck::CheckAlignment) {
assert(access.getScale() == 0 ||
(access.getScale() % access.getSize()) == 0);
assert((access.getOffsetImm() % access.getSize()) == 0);
unsigned size = access.getSize();
if ((state.getMinAlignment(access.getBaseReg()) % size) == 0) {
flags &= ~MemoryCheck::CheckAlignment;
} else {
updateBaseRegAlign = true;
}
}
MemoryCheck *check =
new MemoryCheck(access.getSize(), access.getBaseReg(),
access.getScale(), access.getOffsetReg(),
access.getOffsetImm(), flags);
candidates.push_back(MemoryCheckCandidate(first, i, check));
if (updateBaseRegAlign) {
state.setMinAlignment(access.getBaseReg(), access.getSize());
}
}
// Update regDefs.
state.update(opc, ops, i + 1);
}
if (candidates.empty())
return;
for (unsigned index = 0, size = candidates.size(); index < size; index++) {
checks.push(std::make_pair(candidates[index].getInstructionIndex(),
candidates[index].getMemoryCheck()));
}
}