|
|
@ -31,10 +31,37 @@ using namespace dev; |
|
|
|
using namespace dev::eth; |
|
|
|
|
|
|
|
vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems() |
|
|
|
{ |
|
|
|
map<int, EquivalenceClassId> currentStackContents; |
|
|
|
map<int, EquivalenceClassId> targetStackContents; |
|
|
|
int minHeight = m_stackHeight + 1; |
|
|
|
if (!m_stackElements.empty()) |
|
|
|
minHeight = min(minHeight, m_stackElements.begin()->first.first); |
|
|
|
for (int height = minHeight; height <= max(0, m_stackHeight); ++height) |
|
|
|
{ |
|
|
|
// make sure it is created
|
|
|
|
EquivalenceClassId c = getStackElement(height); |
|
|
|
if (height <= 0) |
|
|
|
currentStackContents[height] = getClass(AssemblyItem(dupInstruction(1 - height))); |
|
|
|
if (height <= m_stackHeight) |
|
|
|
targetStackContents[height] = c; |
|
|
|
} |
|
|
|
|
|
|
|
// Debug info:
|
|
|
|
//stream(cout, currentStackContents, targetStackContents);
|
|
|
|
|
|
|
|
return CSECodeGenerator().generateCode(currentStackContents, targetStackContents, m_equivalenceClasses); |
|
|
|
} |
|
|
|
|
|
|
|
ostream& CommonSubexpressionEliminator::stream( |
|
|
|
ostream& _out, |
|
|
|
map<int, EquivalenceClassId> _currentStack, |
|
|
|
map<int, EquivalenceClassId> _targetStack |
|
|
|
) const |
|
|
|
{ |
|
|
|
auto streamEquivalenceClass = [this](ostream& _out, EquivalenceClassId _id) |
|
|
|
{ |
|
|
|
auto const& eqClass = m_equivalenceClasses[_id]; |
|
|
|
auto const& eqClass = m_equivalenceClasses.at(_id); |
|
|
|
_out << " " << _id << ": " << *eqClass.first; |
|
|
|
_out << "("; |
|
|
|
for (EquivalenceClassId arg: eqClass.second) |
|
|
@ -42,59 +69,36 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems() |
|
|
|
_out << ")" << endl; |
|
|
|
}; |
|
|
|
|
|
|
|
cout << dec; |
|
|
|
cout << "Optimizer results:" << endl; |
|
|
|
cout << "Final stack height: " << m_stackHeight << endl; |
|
|
|
cout << "Stack elements: " << endl; |
|
|
|
_out << "Optimizer analysis:" << endl; |
|
|
|
_out << "Final stack height: " << dec << m_stackHeight << endl; |
|
|
|
_out << "Stack elements: " << endl; |
|
|
|
for (auto const& it: m_stackElements) |
|
|
|
{ |
|
|
|
cout |
|
|
|
<< " " << dec << it.first.first << "(" << it.first.second << ") = "; |
|
|
|
streamEquivalenceClass(cout, it.second); |
|
|
|
_out << " " << dec << it.first.first << "(" << it.first.second << ") = "; |
|
|
|
streamEquivalenceClass(_out, it.second); |
|
|
|
} |
|
|
|
cout << "Equivalence classes: " << endl; |
|
|
|
_out << "Equivalence classes: " << endl; |
|
|
|
for (EquivalenceClassId eqClass = 0; eqClass < m_equivalenceClasses.size(); ++eqClass) |
|
|
|
streamEquivalenceClass(cout, eqClass); |
|
|
|
cout << "----------------------------" << endl; |
|
|
|
streamEquivalenceClass(_out, eqClass); |
|
|
|
|
|
|
|
map<int, EquivalenceClassId> currentStackContents; |
|
|
|
map<int, EquivalenceClassId> targetStackContents; |
|
|
|
int minStackHeight = m_stackHeight; |
|
|
|
if (m_stackElements.size() > 0) |
|
|
|
minStackHeight = min(minStackHeight, m_stackElements.begin()->first.first); |
|
|
|
for (int stackHeight = minStackHeight; stackHeight <= m_stackHeight; ++stackHeight) |
|
|
|
_out << "Current stack: " << endl; |
|
|
|
for (auto const& it: _currentStack) |
|
|
|
{ |
|
|
|
if (stackHeight <= 0) |
|
|
|
currentStackContents[stackHeight] = getClass(AssemblyItem(dupInstruction(1 - stackHeight))); |
|
|
|
targetStackContents[stackHeight] = getStackElement(stackHeight); |
|
|
|
_out << " " << dec << it.first << ": "; |
|
|
|
streamEquivalenceClass(_out, it.second); |
|
|
|
} |
|
|
|
|
|
|
|
return CSECodeGenerator().generateCode(currentStackContents, targetStackContents, m_equivalenceClasses); |
|
|
|
} |
|
|
|
|
|
|
|
bool CommonSubexpressionEliminator::breaksBasicBlock(AssemblyItem const& _item) |
|
|
|
{ |
|
|
|
switch (_item.type()) |
|
|
|
_out << "Target stack: " << endl; |
|
|
|
for (auto const& it: _targetStack) |
|
|
|
{ |
|
|
|
case UndefinedItem: |
|
|
|
case Tag: |
|
|
|
return true; |
|
|
|
case Push: |
|
|
|
case PushString: |
|
|
|
case PushTag: |
|
|
|
case PushSub: |
|
|
|
case PushSubSize: |
|
|
|
case PushProgramSize: |
|
|
|
case PushData: |
|
|
|
return false; |
|
|
|
case Operation: |
|
|
|
return instructionInfo(_item.instruction()).sideEffects; |
|
|
|
_out << " " << dec << it.first << ": "; |
|
|
|
streamEquivalenceClass(_out, it.second); |
|
|
|
} |
|
|
|
|
|
|
|
return _out; |
|
|
|
} |
|
|
|
|
|
|
|
void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) |
|
|
|
{ |
|
|
|
cout << _item << endl; |
|
|
|
if (_item.type() != Operation) |
|
|
|
{ |
|
|
|
if (_item.deposit() != 1) |
|
|
@ -105,12 +109,12 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) |
|
|
|
{ |
|
|
|
Instruction instruction = _item.instruction(); |
|
|
|
InstructionInfo info = instructionInfo(instruction); |
|
|
|
if (Instruction::DUP1 <= instruction && instruction <= Instruction::DUP16) |
|
|
|
if (SemanticInformation::isDupInstruction(_item)) |
|
|
|
setStackElement( |
|
|
|
m_stackHeight + 1, |
|
|
|
getStackElement(m_stackHeight - int(instruction) + int(Instruction::DUP1)) |
|
|
|
); |
|
|
|
else if (Instruction::SWAP1 <= instruction && instruction <= Instruction::SWAP16) |
|
|
|
else if (SemanticInformation::isSwapInstruction(_item)) |
|
|
|
swapStackElements( |
|
|
|
m_stackHeight, |
|
|
|
m_stackHeight - 1 - int(instruction) + int(Instruction::SWAP1) |
|
|
@ -168,61 +172,75 @@ EquivalenceClassId CommonSubexpressionEliminator::getClass( |
|
|
|
EquivalenceClassIds const& _arguments |
|
|
|
) |
|
|
|
{ |
|
|
|
// do a clever search, i.e.
|
|
|
|
// TODO: do a clever search, i.e.
|
|
|
|
// - check for the presence of constants in the argument classes and do arithmetic
|
|
|
|
// - check whether the two items are equal for a SUB instruction
|
|
|
|
// - check whether 0 or 1 is in one of the classes for a MUL
|
|
|
|
// - for commutative opcodes, sort the arguments before searching
|
|
|
|
|
|
|
|
EquivalenceClassIds args = _arguments; |
|
|
|
if (SemanticInformation::isCommutativeOperation(_item)) |
|
|
|
sort(args.begin(), args.end()); |
|
|
|
|
|
|
|
//@todo use a better data structure for search here
|
|
|
|
for (EquivalenceClassId c = 0; c < m_equivalenceClasses.size(); ++c) |
|
|
|
{ |
|
|
|
AssemblyItem const& classItem = *m_equivalenceClasses[c].first; |
|
|
|
AssemblyItem const& classItem = *m_equivalenceClasses.at(c).first; |
|
|
|
if (classItem != _item) |
|
|
|
continue; |
|
|
|
if (_arguments.size() != m_equivalenceClasses[c].second.size()) |
|
|
|
BOOST_THROW_EXCEPTION( |
|
|
|
OptimizerException() << |
|
|
|
errinfo_comment("Equal assembly items with different number of arguments.") |
|
|
|
|
|
|
|
assertThrow( |
|
|
|
args.size() == m_equivalenceClasses.at(c).second.size(), |
|
|
|
OptimizerException, |
|
|
|
"Equal assembly items with different number of arguments." |
|
|
|
); |
|
|
|
if (equal(_arguments.begin(), _arguments.end(), m_equivalenceClasses[c].second.begin())) |
|
|
|
if (equal(args.begin(), args.end(), m_equivalenceClasses.at(c).second.begin())) |
|
|
|
return c; |
|
|
|
} |
|
|
|
if (_item.type() == Operation && _arguments.size() == 2 && all_of( |
|
|
|
_arguments.begin(), |
|
|
|
_arguments.end(), |
|
|
|
[this](EquivalenceClassId eqc) { return m_equivalenceClasses[eqc].first->match(Push); })) |
|
|
|
// constant folding
|
|
|
|
if (_item.type() == Operation && args.size() == 2 && all_of( |
|
|
|
args.begin(), |
|
|
|
args.end(), |
|
|
|
[this](EquivalenceClassId eqc) { return m_equivalenceClasses.at(eqc).first->match(Push); })) |
|
|
|
{ |
|
|
|
auto signextend = [](u256 a, u256 b) -> u256 |
|
|
|
{ |
|
|
|
if (a >= 31) |
|
|
|
return b; |
|
|
|
unsigned testBit = unsigned(a) * 8 + 7; |
|
|
|
u256 mask = (u256(1) << testBit) - 1; |
|
|
|
return boost::multiprecision::bit_test(b, testBit) ? b | ~mask : b & mask; |
|
|
|
}; |
|
|
|
map<Instruction, function<u256(u256, u256)>> const arithmetics = |
|
|
|
{ |
|
|
|
//@todo these are not correct (e.g. for div by zero)
|
|
|
|
{ Instruction::SUB, [](u256 a, u256 b)->u256{return a - b;} }, |
|
|
|
{ Instruction::DIV, [](u256 a, u256 b)->u256{return a / b;} }, |
|
|
|
{ Instruction::SDIV, [](u256 a, u256 b)->u256{return s2u(u2s(a) / u2s(b));} }, |
|
|
|
{ Instruction::MOD, [](u256 a, u256 b)->u256{return a % b;} }, |
|
|
|
{ Instruction::SMOD, [](u256 a, u256 b)->u256{return s2u(u2s(a) % u2s(b));} }, |
|
|
|
{ Instruction::EXP, [](u256 a, u256 b)->u256{return (u256)boost::multiprecision::powm((bigint)a, (bigint)b, bigint(1) << 256);} }, |
|
|
|
//{ Instruction::SIGNEXTEND, signextend },
|
|
|
|
{ Instruction::LT, [](u256 a, u256 b)->u256{return a < b ? 1 : 0;} }, |
|
|
|
{ Instruction::GT, [](u256 a, u256 b)->u256{return a > b ? 1 : 0;} }, |
|
|
|
{ Instruction::SLT, [](u256 a, u256 b)->u256{return u2s(a) < u2s(b) ? 1 : 0;} }, |
|
|
|
{ Instruction::SGT, [](u256 a, u256 b)->u256{return u2s(a) > u2s(b) ? 1 : 0;} }, |
|
|
|
{ Instruction::EQ, [](u256 a, u256 b)->u256{return a == b ? 1 : 0;} }, |
|
|
|
{ Instruction::ADD, [](u256 a, u256 b)->u256{return a + b;} }, |
|
|
|
{ Instruction::MUL, [](u256 a, u256 b)->u256{return a * b;} }, |
|
|
|
{ Instruction::AND, [](u256 a, u256 b)->u256{return a & b;} }, |
|
|
|
{ Instruction::OR, [](u256 a, u256 b)->u256{return a | b;} }, |
|
|
|
{ Instruction::XOR, [](u256 a, u256 b)->u256{return a ^ b;} }, |
|
|
|
{ Instruction::SUB, [](u256 a, u256 b) -> u256 {return a - b; } }, |
|
|
|
{ Instruction::DIV, [](u256 a, u256 b) -> u256 {return b == 0 ? 0 : a / b; } }, |
|
|
|
{ Instruction::SDIV, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) / u2s(b)); } }, |
|
|
|
{ Instruction::MOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : a % b; } }, |
|
|
|
{ Instruction::SMOD, [](u256 a, u256 b) -> u256 { return b == 0 ? 0 : s2u(u2s(a) % u2s(b)); } }, |
|
|
|
{ Instruction::EXP, [](u256 a, u256 b) -> u256 { return (u256)boost::multiprecision::powm(bigint(a), bigint(b), bigint(1) << 256); } }, |
|
|
|
{ Instruction::SIGNEXTEND, signextend }, |
|
|
|
{ Instruction::LT, [](u256 a, u256 b) -> u256 { return a < b ? 1 : 0; } }, |
|
|
|
{ Instruction::GT, [](u256 a, u256 b) -> u256 { return a > b ? 1 : 0; } }, |
|
|
|
{ Instruction::SLT, [](u256 a, u256 b) -> u256 { return u2s(a) < u2s(b) ? 1 : 0; } }, |
|
|
|
{ Instruction::SGT, [](u256 a, u256 b) -> u256 { return u2s(a) > u2s(b) ? 1 : 0; } }, |
|
|
|
{ Instruction::EQ, [](u256 a, u256 b) -> u256 { return a == b ? 1 : 0; } }, |
|
|
|
{ Instruction::ADD, [](u256 a, u256 b) -> u256 { return a + b; } }, |
|
|
|
{ Instruction::MUL, [](u256 a, u256 b) -> u256 { return a * b; } }, |
|
|
|
{ Instruction::AND, [](u256 a, u256 b) -> u256 { return a & b; } }, |
|
|
|
{ Instruction::OR, [](u256 a, u256 b) -> u256 { return a | b; } }, |
|
|
|
{ Instruction::XOR, [](u256 a, u256 b) -> u256 { return a ^ b; } }, |
|
|
|
}; |
|
|
|
if (arithmetics.count(_item.instruction())) |
|
|
|
{ |
|
|
|
u256 result = arithmetics.at(_item.instruction())( |
|
|
|
m_equivalenceClasses[_arguments[0]].first->data(), |
|
|
|
m_equivalenceClasses[_arguments[1]].first->data() |
|
|
|
m_equivalenceClasses.at(args[0]).first->data(), |
|
|
|
m_equivalenceClasses.at(args[1]).first->data() |
|
|
|
); |
|
|
|
m_spareAssemblyItem.push_back(make_shared<AssemblyItem>(result)); |
|
|
|
return getClass(*m_spareAssemblyItem.back()); |
|
|
|
} |
|
|
|
} |
|
|
|
m_equivalenceClasses.push_back(make_pair(&_item, _arguments)); |
|
|
|
m_equivalenceClasses.push_back(make_pair(&_item, args)); |
|
|
|
return m_equivalenceClasses.size() - 1; |
|
|
|
} |
|
|
|
|
|
|
@ -238,6 +256,64 @@ unsigned CommonSubexpressionEliminator::getNextStackElementSequence(int _stackHe |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
bool SemanticInformation::breaksBasicBlock(AssemblyItem const& _item) |
|
|
|
{ |
|
|
|
switch (_item.type()) |
|
|
|
{ |
|
|
|
case UndefinedItem: |
|
|
|
case Tag: |
|
|
|
return true; |
|
|
|
case Push: |
|
|
|
case PushString: |
|
|
|
case PushTag: |
|
|
|
case PushSub: |
|
|
|
case PushSubSize: |
|
|
|
case PushProgramSize: |
|
|
|
case PushData: |
|
|
|
return false; |
|
|
|
case Operation: |
|
|
|
{ |
|
|
|
if (isSwapInstruction(_item) || isDupInstruction(_item)) |
|
|
|
return false; |
|
|
|
InstructionInfo info = instructionInfo(_item.instruction()); |
|
|
|
// the second requirement will be lifted once it is implemented
|
|
|
|
return info.sideEffects || info.args > 2; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item) |
|
|
|
{ |
|
|
|
if (_item.type() != Operation) |
|
|
|
return false; |
|
|
|
switch (_item.instruction()) |
|
|
|
{ |
|
|
|
case Instruction::ADD: |
|
|
|
case Instruction::MUL: |
|
|
|
case Instruction::EQ: |
|
|
|
case Instruction::AND: |
|
|
|
case Instruction::OR: |
|
|
|
case Instruction::XOR: |
|
|
|
return true; |
|
|
|
default: |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool SemanticInformation::isDupInstruction(AssemblyItem const& _item) |
|
|
|
{ |
|
|
|
if (_item.type() != Operation) |
|
|
|
return false; |
|
|
|
return Instruction::DUP1 <= _item.instruction() && _item.instruction() <= Instruction::DUP16; |
|
|
|
} |
|
|
|
|
|
|
|
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) |
|
|
|
{ |
|
|
|
if (_item.type() != Operation) |
|
|
|
return false; |
|
|
|
return Instruction::SWAP1 <= _item.instruction() && _item.instruction() <= Instruction::SWAP16; |
|
|
|
} |
|
|
|
|
|
|
|
AssemblyItems CSECodeGenerator::generateCode( |
|
|
|
map<int, EquivalenceClassId> const& _currentStack, |
|
|
|
map<int, EquivalenceClassId> const& _targetStackContents, |
|
|
@ -249,24 +325,48 @@ AssemblyItems CSECodeGenerator::generateCode( |
|
|
|
m_stack = _currentStack; |
|
|
|
m_equivalenceClasses = _equivalenceClasses; |
|
|
|
for (auto const& item: m_stack) |
|
|
|
if (!m_classPositions.count(item.second)) |
|
|
|
m_classPositions[item.second] = item.first; |
|
|
|
|
|
|
|
// @todo: provide information about the positions of copies of class elements
|
|
|
|
|
|
|
|
// generate the dependency graph
|
|
|
|
for (auto const& stackContent: _targetStackContents) |
|
|
|
for (auto const& targetItem: _targetStackContents) |
|
|
|
{ |
|
|
|
m_finalClasses.insert(stackContent.second); |
|
|
|
addDependencies(stackContent.second); |
|
|
|
m_finalClasses.insert(targetItem.second); |
|
|
|
addDependencies(targetItem.second); |
|
|
|
} |
|
|
|
|
|
|
|
for (auto const& cid: m_finalClasses) |
|
|
|
generateClassElement(cid); |
|
|
|
// generate the actual elements
|
|
|
|
for (auto const& targetItem: _targetStackContents) |
|
|
|
{ |
|
|
|
removeStackTopIfPossible(); |
|
|
|
int position = generateClassElement(targetItem.second); |
|
|
|
if (position == targetItem.first) |
|
|
|
continue; |
|
|
|
if (position < targetItem.first) |
|
|
|
// it is already at its target, we need another copy
|
|
|
|
appendDup(position); |
|
|
|
else |
|
|
|
appendSwap(position); |
|
|
|
appendSwap(targetItem.first); |
|
|
|
} |
|
|
|
|
|
|
|
// @TODO shuffle and copy the elements
|
|
|
|
// remove surplus elements
|
|
|
|
while (removeStackTopIfPossible()) |
|
|
|
{ |
|
|
|
// no-op
|
|
|
|
} |
|
|
|
|
|
|
|
cout << "--------------- generated code: ---------------" << endl; |
|
|
|
for (auto const& it: m_generatedItems) |
|
|
|
cout << it << endl; |
|
|
|
cout << "-----------------------------" << endl; |
|
|
|
// check validity
|
|
|
|
int finalHeight = 0; |
|
|
|
if (!_targetStackContents.empty()) |
|
|
|
finalHeight = (--_targetStackContents.end())->first; |
|
|
|
else if (!_currentStack.empty()) |
|
|
|
finalHeight = _currentStack.begin()->first - 1; |
|
|
|
else |
|
|
|
finalHeight = 0; |
|
|
|
assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height."); |
|
|
|
|
|
|
|
return m_generatedItems; |
|
|
|
} |
|
|
@ -275,7 +375,7 @@ void CSECodeGenerator::addDependencies(EquivalenceClassId _c) |
|
|
|
{ |
|
|
|
if (m_neededBy.count(_c)) |
|
|
|
return; |
|
|
|
for (EquivalenceClassId argument: m_equivalenceClasses[_c].second) |
|
|
|
for (EquivalenceClassId argument: m_equivalenceClasses.at(_c).second) |
|
|
|
{ |
|
|
|
addDependencies(argument); |
|
|
|
m_neededBy.insert(make_pair(argument, _c)); |
|
|
@ -285,13 +385,15 @@ void CSECodeGenerator::addDependencies(EquivalenceClassId _c) |
|
|
|
int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) |
|
|
|
{ |
|
|
|
if (m_classPositions.count(_c)) |
|
|
|
return m_classPositions[_c]; |
|
|
|
{ |
|
|
|
assertThrow( |
|
|
|
m_classPositions[_c] != c_invalidPosition, |
|
|
|
OptimizerException, |
|
|
|
"Element already removed but still needed." |
|
|
|
); |
|
|
|
EquivalenceClassIds const& arguments = m_equivalenceClasses[_c].second; |
|
|
|
return m_classPositions[_c]; |
|
|
|
} |
|
|
|
EquivalenceClassIds const& arguments = m_equivalenceClasses.at(_c).second; |
|
|
|
for (EquivalenceClassId arg: boost::adaptors::reverse(arguments)) |
|
|
|
generateClassElement(arg); |
|
|
|
|
|
|
@ -312,10 +414,10 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) |
|
|
|
else if (canBeRemoved(arguments[0], _c)) |
|
|
|
{ |
|
|
|
appendSwap(m_stackHeight - 1); |
|
|
|
appendSwap(generateClassElement(arguments[1])); |
|
|
|
appendSwap(generateClassElement(arguments[0])); |
|
|
|
} |
|
|
|
else |
|
|
|
appendDup(generateClassElement(arguments[1])); |
|
|
|
appendDup(generateClassElement(arguments[0])); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
@ -343,10 +445,20 @@ int CSECodeGenerator::generateClassElement(EquivalenceClassId _c) |
|
|
|
OptimizerException, |
|
|
|
"Opcodes with more than two arguments not implemented yet." |
|
|
|
); |
|
|
|
for (size_t i = 0; i < arguments.size(); ++i) |
|
|
|
assertThrow(m_stack[m_stackHeight - i] == arguments[i], OptimizerException, "Expected arguments not present." ); |
|
|
|
|
|
|
|
AssemblyItem const& item = *m_equivalenceClasses.at(_c).first; |
|
|
|
while (SemanticInformation::isCommutativeOperation(item) && |
|
|
|
!m_generatedItems.empty() && |
|
|
|
m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) |
|
|
|
appendSwap(m_stackHeight - 1); |
|
|
|
for (auto arg: arguments) |
|
|
|
if (canBeRemoved(arg, _c)) |
|
|
|
m_classPositions[arguments[1]] = c_invalidPosition; |
|
|
|
appendItem(*m_equivalenceClasses[_c].first); |
|
|
|
m_classPositions[arg] = c_invalidPosition; |
|
|
|
for (size_t i = 0; i < arguments.size(); ++i) |
|
|
|
m_stack.erase(m_stackHeight - i); |
|
|
|
appendItem(*m_equivalenceClasses.at(_c).first); |
|
|
|
m_stack[m_stackHeight] = _c; |
|
|
|
return m_classPositions[_c] = m_stackHeight; |
|
|
|
} |
|
|
@ -365,9 +477,22 @@ bool CSECodeGenerator::canBeRemoved(EquivalenceClassId _element, EquivalenceClas |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool CSECodeGenerator::removeStackTopIfPossible() |
|
|
|
{ |
|
|
|
if (m_stack.empty()) |
|
|
|
return false; |
|
|
|
assertThrow(m_stack.count(m_stackHeight), OptimizerException, ""); |
|
|
|
EquivalenceClassId top = m_stack[m_stackHeight]; |
|
|
|
if (!canBeRemoved(top)) |
|
|
|
return false; |
|
|
|
m_generatedItems.push_back(AssemblyItem(Instruction::POP)); |
|
|
|
m_stack.erase(m_stackHeight); |
|
|
|
m_stackHeight--; |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
void CSECodeGenerator::appendDup(int _fromPosition) |
|
|
|
{ |
|
|
|
m_generatedItems.push_back(AssemblyItem(swapInstruction(1 + m_stackHeight - _fromPosition))); |
|
|
|
int nr = 1 + m_stackHeight - _fromPosition; |
|
|
|
assertThrow(1 <= nr && nr <= 16, OptimizerException, "Stack too deep."); |
|
|
|
m_generatedItems.push_back(AssemblyItem(dupInstruction(nr))); |
|
|
@ -388,6 +513,13 @@ void CSECodeGenerator::appendSwap(int _fromPosition) |
|
|
|
if (m_classPositions[m_stack[_fromPosition]] == _fromPosition) |
|
|
|
m_classPositions[m_stack[_fromPosition]] = m_stackHeight; |
|
|
|
swap(m_stack[m_stackHeight], m_stack[_fromPosition]); |
|
|
|
if (m_generatedItems.size() >= 2 && |
|
|
|
SemanticInformation::isSwapInstruction(m_generatedItems.back()) && |
|
|
|
*(m_generatedItems.end() - 2) == m_generatedItems.back()) |
|
|
|
{ |
|
|
|
m_generatedItems.pop_back(); |
|
|
|
m_generatedItems.pop_back(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void CSECodeGenerator::appendItem(AssemblyItem const& _item) |
|
|
|