|
Lines 29-34
Source/JavaScriptCore/b3/B3ReduceStrength.cpp_sec1
|
| 29 |
#if ENABLE(B3_JIT) |
29 |
#if ENABLE(B3_JIT) |
| 30 |
|
30 |
|
| 31 |
#include "B3BasicBlockInlines.h" |
31 |
#include "B3BasicBlockInlines.h" |
|
|
32 |
#include "B3BlockInsertionSet.h" |
| 32 |
#include "B3ControlValue.h" |
33 |
#include "B3ControlValue.h" |
| 33 |
#include "B3Dominators.h" |
34 |
#include "B3Dominators.h" |
| 34 |
#include "B3IndexSet.h" |
35 |
#include "B3IndexSet.h" |
|
Lines 89-94
public:
Source/JavaScriptCore/b3/B3ReduceStrength.cpp_sec2
|
| 89 |
ReduceStrength(Procedure& proc) |
90 |
ReduceStrength(Procedure& proc) |
| 90 |
: m_proc(proc) |
91 |
: m_proc(proc) |
| 91 |
, m_insertionSet(proc) |
92 |
, m_insertionSet(proc) |
|
|
93 |
, m_blockInsertionSet(proc) |
| 92 |
{ |
94 |
{ |
| 93 |
} |
95 |
} |
| 94 |
|
96 |
|
|
Lines 117-122
public:
Source/JavaScriptCore/b3/B3ReduceStrength.cpp_sec3
|
| 117 |
m_block = block; |
119 |
m_block = block; |
| 118 |
|
120 |
|
| 119 |
for (m_index = 0; m_index < block->size(); ++m_index) { |
121 |
for (m_index = 0; m_index < block->size(); ++m_index) { |
|
|
122 |
if (verbose) { |
| 123 |
dataLog( |
| 124 |
"Looking at ", *block, " #", m_index, ": ", |
| 125 |
deepDump(m_proc, block->at(m_index)), "\n"); |
| 126 |
} |
| 120 |
m_value = m_block->at(m_index); |
127 |
m_value = m_block->at(m_index); |
| 121 |
m_value->performSubstitution(); |
128 |
m_value->performSubstitution(); |
| 122 |
|
129 |
|
|
Lines 126-131
public:
Source/JavaScriptCore/b3/B3ReduceStrength.cpp_sec4
|
| 126 |
m_insertionSet.execute(m_block); |
133 |
m_insertionSet.execute(m_block); |
| 127 |
} |
134 |
} |
| 128 |
|
135 |
|
|
|
136 |
if (m_blockInsertionSet.execute()) { |
| 137 |
m_proc.resetReachability(); |
| 138 |
m_proc.invalidateCFG(); |
| 139 |
m_dominators = &m_proc.dominators(); // Recompute if necessary. |
| 140 |
m_changedCFG = true; |
| 141 |
} |
| 142 |
|
| 129 |
simplifyCFG(); |
143 |
simplifyCFG(); |
| 130 |
|
144 |
|
| 131 |
if (m_changedCFG) { |
145 |
if (m_changedCFG) { |
|
Lines 1191-1196
private:
Source/JavaScriptCore/b3/B3ReduceStrength.cpp_sec5
|
| 1191 |
&& checkValue->child(0)->child(1)->isInt(0)) { |
1205 |
&& checkValue->child(0)->child(1)->isInt(0)) { |
| 1192 |
checkValue->child(0) = checkValue->child(0)->child(0); |
1206 |
checkValue->child(0) = checkValue->child(0)->child(0); |
| 1193 |
m_changed = true; |
1207 |
m_changed = true; |
|
|
1208 |
} |
| 1209 |
|
| 1210 |
// If we are checking some bounded-size SSA expression that leads to a Select that |
| 1211 |
// has a constant as one of its results, then turn the Select into a Branch and split |
| 1212 |
// the code between the Check and the Branch. For example, this: |
| 1213 |
// |
| 1214 |
// @a = Select(@p, @x, 42) |
| 1215 |
// @b = Add(@a, 35) |
| 1216 |
// Check(@b) |
| 1217 |
// |
| 1218 |
// becomes this: |
| 1219 |
// |
| 1220 |
// Branch(@p, #truecase, #falsecase) |
| 1221 |
// |
| 1222 |
// BB#truecase: |
| 1223 |
// @b_truecase = Add(@x, 35) |
| 1224 |
// Check(@b_truecase) |
| 1225 |
// Upsilon(@x, ^a) |
| 1226 |
// Upsilon(@b_truecase, ^b) |
| 1227 |
// Jump(#continuation) |
| 1228 |
// |
| 1229 |
// BB#falsecase: |
| 1230 |
// @b_falsecase = Add(42, 35) |
| 1231 |
// Check(@b_falsecase) |
| 1232 |
// Upsilon(42, ^a) |
| 1233 |
// Upsilon(@b_falsecase, ^b) |
| 1234 |
// Jump(#continuation) |
| 1235 |
// |
| 1236 |
// BB#continuation: |
| 1237 |
// @a = Phi() |
| 1238 |
// @b = Phi() |
| 1239 |
// |
| 1240 |
// The goal of this optimization is to kill a lot of code in one of those basic |
| 1241 |
// blocks. This is pretty much guaranteed since one of those blocks will replace all |
| 1242 |
// uses of the Select with a constant, and that constant will be transitively used |
| 1243 |
// from the check. |
| 1244 |
static const unsigned selectSpecializationBound = 3; |
| 1245 |
Value* select = findRecentNodeMatching( |
| 1246 |
m_value->child(0), selectSpecializationBound, |
| 1247 |
[&] (Value* value) -> bool { |
| 1248 |
return value->opcode() == Select |
| 1249 |
&& (value->child(1)->isConstant() || value->child(2)->isConstant()); |
| 1250 |
}); |
| 1251 |
if (select) { |
| 1252 |
specializeSelect(select); |
| 1194 |
break; |
1253 |
break; |
| 1195 |
} |
1254 |
} |
| 1196 |
break; |
1255 |
break; |
|
Lines 1262-1267
private:
Source/JavaScriptCore/b3/B3ReduceStrength.cpp_sec6
|
| 1262 |
} |
1321 |
} |
| 1263 |
} |
1322 |
} |
| 1264 |
|
1323 |
|
|
|
1324 |
// Find a node that: |
| 1325 |
// - functor(node) returns true. |
| 1326 |
// - it's reachable from the given node via children. |
| 1327 |
// - it's in the last "bound" slots in the current basic block. |
| 1328 |
// This algorithm is optimized under the assumption that the bound is small. |
| 1329 |
template<typename Functor> |
| 1330 |
Value* findRecentNodeMatching(Value* start, unsigned bound, const Functor& functor) |
| 1331 |
{ |
| 1332 |
unsigned startIndex = bound < m_index ? m_index - bound : 0; |
| 1333 |
Value* result = nullptr; |
| 1334 |
start->walk( |
| 1335 |
[&] (Value* value) -> Value::WalkStatus { |
| 1336 |
bool found = false; |
| 1337 |
for (unsigned i = startIndex; i <= m_index; ++i) { |
| 1338 |
if (m_block->at(i) == value) |
| 1339 |
found = true; |
| 1340 |
} |
| 1341 |
if (!found) |
| 1342 |
return Value::IgnoreChildren; |
| 1343 |
|
| 1344 |
if (functor(value)) { |
| 1345 |
result = value; |
| 1346 |
return Value::Stop; |
| 1347 |
} |
| 1348 |
|
| 1349 |
return Value::Continue; |
| 1350 |
}); |
| 1351 |
return result; |
| 1352 |
} |
| 1353 |
|
| 1354 |
// This specializes a sequence of code up to a Select. This doesn't work when we're at a |
| 1355 |
// terminal. It would be cool to fix that eventually. The main problem is that instead of |
| 1356 |
// splitting the block, we should just insert the then/else blocks. We'll have to create |
| 1357 |
// double the Phis and double the Upsilons. It'll probably be the sort of optimization that |
| 1358 |
// we want to do only after we've done loop optimizations, since this will *definitely* |
| 1359 |
// obscure things. In fact, even this simpler form of select specialization will possibly |
| 1360 |
// obscure other optimizations. It would be great to have two modes of strength reduction, |
| 1361 |
// one that does obscuring optimizations and runs late, and another that does not do |
| 1362 |
// obscuring optimizations and runs early. |
| 1363 |
// FIXME: Make select specialization handle branches. |
| 1364 |
// FIXME: Have a form of strength reduction that does no obscuring optimizations and runs |
| 1365 |
// early. |
| 1366 |
void specializeSelect(Value* source) |
| 1367 |
{ |
| 1368 |
if (verbose) |
| 1369 |
dataLog("Specializing select: ", deepDump(m_proc, source), "\n"); |
| 1370 |
|
| 1371 |
// This mutates startIndex to account for the fact that m_block got the front of it |
| 1372 |
// chopped off. |
| 1373 |
BasicBlock* predecessor = |
| 1374 |
m_blockInsertionSet.splitForward(m_block, m_index, &m_insertionSet); |
| 1375 |
|
| 1376 |
// Splitting will commit the insertion set, which changes the exact position of the |
| 1377 |
// source. That's why we do the search after splitting. |
| 1378 |
unsigned startIndex = UINT_MAX; |
| 1379 |
for (unsigned i = predecessor->size(); i--;) { |
| 1380 |
if (predecessor->at(i) == source) { |
| 1381 |
startIndex = i; |
| 1382 |
break; |
| 1383 |
} |
| 1384 |
} |
| 1385 |
|
| 1386 |
RELEASE_ASSERT(startIndex != UINT_MAX); |
| 1387 |
|
| 1388 |
// By BasicBlock convention, caseIndex == 0 => then, caseIndex == 1 => else. |
| 1389 |
static const unsigned numCases = 2; |
| 1390 |
BasicBlock* cases[numCases]; |
| 1391 |
for (unsigned i = 0; i < numCases; ++i) |
| 1392 |
cases[i] = m_blockInsertionSet.insertBefore(m_block); |
| 1393 |
|
| 1394 |
HashMap<Value*, Value*> mappings[2]; |
| 1395 |
|
| 1396 |
// Save things we want to know about the source. |
| 1397 |
Value* predicate = source->child(0); |
| 1398 |
|
| 1399 |
for (unsigned i = 0; i < numCases; ++i) |
| 1400 |
mappings[i].add(source, source->child(1 + i)); |
| 1401 |
|
| 1402 |
auto cloneValue = [&] (Value* value) { |
| 1403 |
ASSERT(value != source); |
| 1404 |
|
| 1405 |
for (unsigned i = 0; i < numCases; ++i) { |
| 1406 |
Value* clone = m_proc.clone(value); |
| 1407 |
for (Value*& child : clone->children()) { |
| 1408 |
if (Value* newChild = mappings[i].get(child)) |
| 1409 |
child = newChild; |
| 1410 |
} |
| 1411 |
if (value->type() != Void) |
| 1412 |
mappings[i].add(value, clone); |
| 1413 |
|
| 1414 |
cases[i]->append(clone); |
| 1415 |
if (value->type() != Void) |
| 1416 |
cases[i]->appendNew<UpsilonValue>(m_proc, value->origin(), clone, value); |
| 1417 |
} |
| 1418 |
|
| 1419 |
value->replaceWithPhi(); |
| 1420 |
}; |
| 1421 |
|
| 1422 |
// The jump that the splitter inserted is of no use to us. |
| 1423 |
m_proc.deleteValue(predecessor->values().takeLast()); |
| 1424 |
|
| 1425 |
// Hance the source, it's special. |
| 1426 |
for (unsigned i = 0; i < numCases; ++i) { |
| 1427 |
cases[i]->appendNew<UpsilonValue>( |
| 1428 |
m_proc, source->origin(), source->child(1 + i), source); |
| 1429 |
} |
| 1430 |
source->replaceWithPhi(); |
| 1431 |
m_insertionSet.insertValue(m_index, source); |
| 1432 |
|
| 1433 |
// Now handle all values between the source and the check. |
| 1434 |
for (unsigned i = startIndex + 1; i < predecessor->size(); ++i) { |
| 1435 |
Value* value = predecessor->at(i); |
| 1436 |
value->owner = nullptr; |
| 1437 |
|
| 1438 |
cloneValue(value); |
| 1439 |
|
| 1440 |
if (value->type() != Void) |
| 1441 |
m_insertionSet.insertValue(m_index, value); |
| 1442 |
else |
| 1443 |
m_proc.deleteValue(value); |
| 1444 |
} |
| 1445 |
|
| 1446 |
// Finally, deal with the check. |
| 1447 |
cloneValue(m_value); |
| 1448 |
|
| 1449 |
// Remove the values from the predecessor. |
| 1450 |
predecessor->values().resize(startIndex); |
| 1451 |
|
| 1452 |
predecessor->appendNew<ControlValue>( |
| 1453 |
m_proc, Branch, source->origin(), predicate, |
| 1454 |
FrequentedBlock(cases[0]), FrequentedBlock(cases[1])); |
| 1455 |
|
| 1456 |
for (unsigned i = 0; i < numCases; ++i) { |
| 1457 |
cases[i]->appendNew<ControlValue>( |
| 1458 |
m_proc, Jump, m_value->origin(), FrequentedBlock(m_block)); |
| 1459 |
} |
| 1460 |
|
| 1461 |
m_changed = true; |
| 1462 |
|
| 1463 |
predecessor->updatePredecessorsAfter(); |
| 1464 |
} |
| 1465 |
|
| 1265 |
// Turn this: Add(constant, value) |
1466 |
// Turn this: Add(constant, value) |
| 1266 |
// Into this: Add(value, constant) |
1467 |
// Into this: Add(value, constant) |
| 1267 |
// |
1468 |
// |
|
Lines 1609-1614
private:
Source/JavaScriptCore/b3/B3ReduceStrength.cpp_sec7
|
| 1609 |
|
1810 |
|
| 1610 |
Procedure& m_proc; |
1811 |
Procedure& m_proc; |
| 1611 |
InsertionSet m_insertionSet; |
1812 |
InsertionSet m_insertionSet; |
|
|
1813 |
BlockInsertionSet m_blockInsertionSet; |
| 1612 |
BasicBlock* m_block { nullptr }; |
1814 |
BasicBlock* m_block { nullptr }; |
| 1613 |
unsigned m_index { 0 }; |
1815 |
unsigned m_index { 0 }; |
| 1614 |
Value* m_value { nullptr }; |
1816 |
Value* m_value { nullptr }; |