mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Move some util functions to low-level functions.
This commit is contained in:
parent
d0e8d340a5
commit
b60623521f
@ -502,9 +502,17 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayUtils::clearArray(ArrayType const& _type) const
|
void ArrayUtils::clearArray(ArrayType const& _typeIn) const
|
||||||
{
|
{
|
||||||
unsigned stackHeightStart = m_context.stackHeight();
|
TypePointer type = _typeIn.shared_from_this();
|
||||||
|
m_context.callLowLevelFunction(
|
||||||
|
"$clearArray_" + _typeIn.identifier(),
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
[type](CompilerContext& _context)
|
||||||
|
{
|
||||||
|
ArrayType const& _type = dynamic_cast<ArrayType const&>(*type);
|
||||||
|
unsigned stackHeightStart = _context.stackHeight();
|
||||||
solAssert(_type.location() == DataLocation::Storage, "");
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
if (_type.baseType()->storageBytes() < 32)
|
if (_type.baseType()->storageBytes() < 32)
|
||||||
{
|
{
|
||||||
@ -514,20 +522,20 @@ void ArrayUtils::clearArray(ArrayType const& _type) const
|
|||||||
if (_type.baseType()->isValueType())
|
if (_type.baseType()->isValueType())
|
||||||
solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type.");
|
solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type.");
|
||||||
|
|
||||||
m_context << Instruction::POP; // remove byte offset
|
_context << Instruction::POP; // remove byte offset
|
||||||
if (_type.isDynamicallySized())
|
if (_type.isDynamicallySized())
|
||||||
clearDynamicArray(_type);
|
ArrayUtils(_context).clearDynamicArray(_type);
|
||||||
else if (_type.length() == 0 || _type.baseType()->category() == Type::Category::Mapping)
|
else if (_type.length() == 0 || _type.baseType()->category() == Type::Category::Mapping)
|
||||||
m_context << Instruction::POP;
|
_context << Instruction::POP;
|
||||||
else if (_type.baseType()->isValueType() && _type.storageSize() <= 5)
|
else if (_type.baseType()->isValueType() && _type.storageSize() <= 5)
|
||||||
{
|
{
|
||||||
// unroll loop for small arrays @todo choose a good value
|
// unroll loop for small arrays @todo choose a good value
|
||||||
// Note that we loop over storage slots here, not elements.
|
// Note that we loop over storage slots here, not elements.
|
||||||
for (unsigned i = 1; i < _type.storageSize(); ++i)
|
for (unsigned i = 1; i < _type.storageSize(); ++i)
|
||||||
m_context
|
_context
|
||||||
<< u256(0) << Instruction::DUP2 << Instruction::SSTORE
|
<< u256(0) << Instruction::DUP2 << Instruction::SSTORE
|
||||||
<< u256(1) << Instruction::ADD;
|
<< u256(1) << Instruction::ADD;
|
||||||
m_context << u256(0) << Instruction::SWAP1 << Instruction::SSTORE;
|
_context << u256(0) << Instruction::SWAP1 << Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
else if (!_type.baseType()->isValueType() && _type.length() <= 4)
|
else if (!_type.baseType()->isValueType() && _type.length() <= 4)
|
||||||
{
|
{
|
||||||
@ -535,27 +543,29 @@ void ArrayUtils::clearArray(ArrayType const& _type) const
|
|||||||
solAssert(_type.baseType()->storageBytes() >= 32, "Invalid storage size.");
|
solAssert(_type.baseType()->storageBytes() >= 32, "Invalid storage size.");
|
||||||
for (unsigned i = 1; i < _type.length(); ++i)
|
for (unsigned i = 1; i < _type.length(); ++i)
|
||||||
{
|
{
|
||||||
m_context << u256(0);
|
_context << u256(0);
|
||||||
StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), false);
|
StorageItem(_context, *_type.baseType()).setToZero(SourceLocation(), false);
|
||||||
m_context
|
_context
|
||||||
<< Instruction::POP
|
<< Instruction::POP
|
||||||
<< u256(_type.baseType()->storageSize()) << Instruction::ADD;
|
<< u256(_type.baseType()->storageSize()) << Instruction::ADD;
|
||||||
}
|
}
|
||||||
m_context << u256(0);
|
_context << u256(0);
|
||||||
StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), true);
|
StorageItem(_context, *_type.baseType()).setToZero(SourceLocation(), true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << Instruction::DUP1 << _type.length();
|
_context << Instruction::DUP1 << _type.length();
|
||||||
convertLengthToSize(_type);
|
ArrayUtils(_context).convertLengthToSize(_type);
|
||||||
m_context << Instruction::ADD << Instruction::SWAP1;
|
_context << Instruction::ADD << Instruction::SWAP1;
|
||||||
if (_type.baseType()->storageBytes() < 32)
|
if (_type.baseType()->storageBytes() < 32)
|
||||||
clearStorageLoop(IntegerType(256));
|
ArrayUtils(_context).clearStorageLoop(IntegerType(256));
|
||||||
else
|
else
|
||||||
clearStorageLoop(*_type.baseType());
|
ArrayUtils(_context).clearStorageLoop(*_type.baseType());
|
||||||
m_context << Instruction::POP;
|
_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
solAssert(m_context.stackHeight() == stackHeightStart - 2, "");
|
solAssert(_context.stackHeight() == stackHeightStart - 2, "");
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
||||||
@ -597,144 +607,154 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
|||||||
m_context << Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayUtils::resizeDynamicArray(ArrayType const& _type) const
|
void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const
|
||||||
{
|
{
|
||||||
|
TypePointer type = _typeIn.shared_from_this();
|
||||||
|
m_context.callLowLevelFunction(
|
||||||
|
"$resizeDynamicArray_" + _typeIn.identifier(),
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
[type](CompilerContext& _context)
|
||||||
|
{
|
||||||
|
ArrayType const& _type = dynamic_cast<ArrayType const&>(*type);
|
||||||
solAssert(_type.location() == DataLocation::Storage, "");
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32)
|
if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32)
|
||||||
solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
|
solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
|
||||||
|
|
||||||
unsigned stackHeightStart = m_context.stackHeight();
|
unsigned stackHeightStart = _context.stackHeight();
|
||||||
eth::AssemblyItem resizeEnd = m_context.newTag();
|
eth::AssemblyItem resizeEnd = _context.newTag();
|
||||||
|
|
||||||
// stack: ref new_length
|
// stack: ref new_length
|
||||||
// fetch old length
|
// fetch old length
|
||||||
retrieveLength(_type, 1);
|
ArrayUtils(_context).retrieveLength(_type, 1);
|
||||||
// stack: ref new_length old_length
|
// stack: ref new_length old_length
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "2");
|
solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "2");
|
||||||
|
|
||||||
// Special case for short byte arrays, they are stored together with their length
|
// Special case for short byte arrays, they are stored together with their length
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArray())
|
||||||
{
|
{
|
||||||
eth::AssemblyItem regularPath = m_context.newTag();
|
eth::AssemblyItem regularPath = _context.newTag();
|
||||||
// We start by a large case-distinction about the old and new length of the byte array.
|
// We start by a large case-distinction about the old and new length of the byte array.
|
||||||
|
|
||||||
m_context << Instruction::DUP3 << Instruction::SLOAD;
|
_context << Instruction::DUP3 << Instruction::SLOAD;
|
||||||
// stack: ref new_length current_length ref_value
|
// stack: ref new_length current_length ref_value
|
||||||
|
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
||||||
m_context << Instruction::DUP2 << u256(31) << Instruction::LT;
|
_context << Instruction::DUP2 << u256(31) << Instruction::LT;
|
||||||
eth::AssemblyItem currentIsLong = m_context.appendConditionalJump();
|
eth::AssemblyItem currentIsLong = _context.appendConditionalJump();
|
||||||
m_context << Instruction::DUP3 << u256(31) << Instruction::LT;
|
_context << Instruction::DUP3 << u256(31) << Instruction::LT;
|
||||||
eth::AssemblyItem newIsLong = m_context.appendConditionalJump();
|
eth::AssemblyItem newIsLong = _context.appendConditionalJump();
|
||||||
|
|
||||||
// Here: short -> short
|
// Here: short -> short
|
||||||
|
|
||||||
// Compute 1 << (256 - 8 * new_size)
|
// Compute 1 << (256 - 8 * new_size)
|
||||||
eth::AssemblyItem shortToShort = m_context.newTag();
|
eth::AssemblyItem shortToShort = _context.newTag();
|
||||||
m_context << shortToShort;
|
_context << shortToShort;
|
||||||
m_context << Instruction::DUP3 << u256(8) << Instruction::MUL;
|
_context << Instruction::DUP3 << u256(8) << Instruction::MUL;
|
||||||
m_context << u256(0x100) << Instruction::SUB;
|
_context << u256(0x100) << Instruction::SUB;
|
||||||
m_context << u256(2) << Instruction::EXP;
|
_context << u256(2) << Instruction::EXP;
|
||||||
// Divide and multiply by that value, clearing bits.
|
// Divide and multiply by that value, clearing bits.
|
||||||
m_context << Instruction::DUP1 << Instruction::SWAP2;
|
_context << Instruction::DUP1 << Instruction::SWAP2;
|
||||||
m_context << Instruction::DIV << Instruction::MUL;
|
_context << Instruction::DIV << Instruction::MUL;
|
||||||
// Insert 2*length.
|
// Insert 2*length.
|
||||||
m_context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD;
|
_context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD;
|
||||||
m_context << Instruction::OR;
|
_context << Instruction::OR;
|
||||||
// Store.
|
// Store.
|
||||||
m_context << Instruction::DUP4 << Instruction::SSTORE;
|
_context << Instruction::DUP4 << Instruction::SSTORE;
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "3");
|
solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3");
|
||||||
m_context.appendJumpTo(resizeEnd);
|
_context.appendJumpTo(resizeEnd);
|
||||||
|
|
||||||
m_context.adjustStackOffset(1); // we have to do that because of the jumps
|
_context.adjustStackOffset(1); // we have to do that because of the jumps
|
||||||
// Here: short -> long
|
// Here: short -> long
|
||||||
|
|
||||||
m_context << newIsLong;
|
_context << newIsLong;
|
||||||
// stack: ref new_length current_length ref_value
|
// stack: ref new_length current_length ref_value
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
||||||
// Zero out lower-order byte.
|
// Zero out lower-order byte.
|
||||||
m_context << u256(0xff) << Instruction::NOT << Instruction::AND;
|
_context << u256(0xff) << Instruction::NOT << Instruction::AND;
|
||||||
// Store at data location.
|
// Store at data location.
|
||||||
m_context << Instruction::DUP4;
|
_context << Instruction::DUP4;
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(_context).computeHashStatic();
|
||||||
m_context << Instruction::SSTORE;
|
_context << Instruction::SSTORE;
|
||||||
// stack: ref new_length current_length
|
// stack: ref new_length current_length
|
||||||
// Store new length: Compule 2*length + 1 and store it.
|
// Store new length: Compule 2*length + 1 and store it.
|
||||||
m_context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD;
|
_context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD;
|
||||||
m_context << u256(1) << Instruction::ADD;
|
_context << u256(1) << Instruction::ADD;
|
||||||
// stack: ref new_length current_length 2*new_length+1
|
// stack: ref new_length current_length 2*new_length+1
|
||||||
m_context << Instruction::DUP4 << Instruction::SSTORE;
|
_context << Instruction::DUP4 << Instruction::SSTORE;
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 3 - 2, "3");
|
solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3");
|
||||||
m_context.appendJumpTo(resizeEnd);
|
_context.appendJumpTo(resizeEnd);
|
||||||
|
|
||||||
m_context.adjustStackOffset(1); // we have to do that because of the jumps
|
_context.adjustStackOffset(1); // we have to do that because of the jumps
|
||||||
|
|
||||||
m_context << currentIsLong;
|
_context << currentIsLong;
|
||||||
m_context << Instruction::DUP3 << u256(31) << Instruction::LT;
|
_context << Instruction::DUP3 << u256(31) << Instruction::LT;
|
||||||
m_context.appendConditionalJumpTo(regularPath);
|
_context.appendConditionalJumpTo(regularPath);
|
||||||
|
|
||||||
// Here: long -> short
|
// Here: long -> short
|
||||||
// Read the first word of the data and store it on the stack. Clear the data location and
|
// Read the first word of the data and store it on the stack. Clear the data location and
|
||||||
// then jump to the short -> short case.
|
// then jump to the short -> short case.
|
||||||
|
|
||||||
// stack: ref new_length current_length ref_value
|
// stack: ref new_length current_length ref_value
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
||||||
m_context << Instruction::POP << Instruction::DUP3;
|
_context << Instruction::POP << Instruction::DUP3;
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(_context).computeHashStatic();
|
||||||
m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1;
|
_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1;
|
||||||
// stack: ref new_length current_length first_word data_location
|
// stack: ref new_length current_length first_word data_location
|
||||||
m_context << Instruction::DUP3;
|
_context << Instruction::DUP3;
|
||||||
convertLengthToSize(_type);
|
ArrayUtils(_context).convertLengthToSize(_type);
|
||||||
m_context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1;
|
_context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1;
|
||||||
// stack: ref new_length current_length first_word data_location_end data_location
|
// stack: ref new_length current_length first_word data_location_end data_location
|
||||||
clearStorageLoop(IntegerType(256));
|
ArrayUtils(_context).clearStorageLoop(IntegerType(256));
|
||||||
m_context << Instruction::POP;
|
_context << Instruction::POP;
|
||||||
// stack: ref new_length current_length first_word
|
// stack: ref new_length current_length first_word
|
||||||
solAssert(m_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3");
|
||||||
m_context.appendJumpTo(shortToShort);
|
_context.appendJumpTo(shortToShort);
|
||||||
|
|
||||||
m_context << regularPath;
|
_context << regularPath;
|
||||||
// stack: ref new_length current_length ref_value
|
// stack: ref new_length current_length ref_value
|
||||||
m_context << Instruction::POP;
|
_context << Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change of length for a regular array (i.e. length at location, data at sha3(location)).
|
// Change of length for a regular array (i.e. length at location, data at sha3(location)).
|
||||||
// stack: ref new_length old_length
|
// stack: ref new_length old_length
|
||||||
// store new length
|
// store new length
|
||||||
m_context << Instruction::DUP2;
|
_context << Instruction::DUP2;
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArray())
|
||||||
// For a "long" byte array, store length as 2*length+1
|
// For a "long" byte array, store length as 2*length+1
|
||||||
m_context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD;
|
_context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD;
|
||||||
m_context<< Instruction::DUP4 << Instruction::SSTORE;
|
_context<< Instruction::DUP4 << Instruction::SSTORE;
|
||||||
// skip if size is not reduced
|
// skip if size is not reduced
|
||||||
m_context << Instruction::DUP2 << Instruction::DUP2
|
_context << Instruction::DUP2 << Instruction::DUP2
|
||||||
<< Instruction::ISZERO << Instruction::GT;
|
<< Instruction::ISZERO << Instruction::GT;
|
||||||
m_context.appendConditionalJumpTo(resizeEnd);
|
_context.appendConditionalJumpTo(resizeEnd);
|
||||||
|
|
||||||
// size reduced, clear the end of the array
|
// size reduced, clear the end of the array
|
||||||
// stack: ref new_length old_length
|
// stack: ref new_length old_length
|
||||||
convertLengthToSize(_type);
|
ArrayUtils(_context).convertLengthToSize(_type);
|
||||||
m_context << Instruction::DUP2;
|
_context << Instruction::DUP2;
|
||||||
convertLengthToSize(_type);
|
ArrayUtils(_context).convertLengthToSize(_type);
|
||||||
// stack: ref new_length old_size new_size
|
// stack: ref new_length old_size new_size
|
||||||
// compute data positions
|
// compute data positions
|
||||||
m_context << Instruction::DUP4;
|
_context << Instruction::DUP4;
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(_context).computeHashStatic();
|
||||||
// stack: ref new_length old_size new_size data_pos
|
// stack: ref new_length old_size new_size data_pos
|
||||||
m_context << Instruction::SWAP2 << Instruction::DUP3 << Instruction::ADD;
|
_context << Instruction::SWAP2 << Instruction::DUP3 << Instruction::ADD;
|
||||||
// stack: ref new_length data_pos new_size delete_end
|
// stack: ref new_length data_pos new_size delete_end
|
||||||
m_context << Instruction::SWAP2 << Instruction::ADD;
|
_context << Instruction::SWAP2 << Instruction::ADD;
|
||||||
// stack: ref new_length delete_end delete_start
|
// stack: ref new_length delete_end delete_start
|
||||||
if (_type.isByteArray() || _type.baseType()->storageBytes() < 32)
|
if (_type.isByteArray() || _type.baseType()->storageBytes() < 32)
|
||||||
clearStorageLoop(IntegerType(256));
|
ArrayUtils(_context).clearStorageLoop(IntegerType(256));
|
||||||
else
|
else
|
||||||
clearStorageLoop(*_type.baseType());
|
ArrayUtils(_context).clearStorageLoop(*_type.baseType());
|
||||||
|
|
||||||
m_context << resizeEnd;
|
_context << resizeEnd;
|
||||||
// cleanup
|
// cleanup
|
||||||
m_context << Instruction::POP << Instruction::POP << Instruction::POP;
|
_context << Instruction::POP << Instruction::POP << Instruction::POP;
|
||||||
solAssert(m_context.stackHeight() == stackHeightStart - 2, "");
|
solAssert(_context.stackHeight() == stackHeightStart - 2, "");
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayUtils::clearStorageLoop(Type const& _type) const
|
void ArrayUtils::clearStorageLoop(Type const& _type) const
|
||||||
|
@ -21,15 +21,18 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/codegen/CompilerContext.h>
|
#include <libsolidity/codegen/CompilerContext.h>
|
||||||
#include <utility>
|
#include <libsolidity/codegen/CompilerUtils.h>
|
||||||
#include <numeric>
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libsolidity/codegen/Compiler.h>
|
#include <libsolidity/codegen/Compiler.h>
|
||||||
#include <libsolidity/interface/Version.h>
|
#include <libsolidity/interface/Version.h>
|
||||||
#include <libsolidity/inlineasm/AsmData.h>
|
#include <libsolidity/inlineasm/AsmData.h>
|
||||||
#include <libsolidity/inlineasm/AsmStack.h>
|
#include <libsolidity/inlineasm/AsmStack.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
@ -57,6 +60,51 @@ void CompilerContext::startFunction(Declaration const& _function)
|
|||||||
*this << functionEntryLabel(_function);
|
*this << functionEntryLabel(_function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompilerContext::callLowLevelFunction(
|
||||||
|
string const& _name,
|
||||||
|
unsigned _inArgs,
|
||||||
|
unsigned _outArgs,
|
||||||
|
function<void(CompilerContext&)> const& _generator
|
||||||
|
)
|
||||||
|
{
|
||||||
|
eth::AssemblyItem retTag = pushNewTag();
|
||||||
|
CompilerUtils(*this).moveIntoStack(_inArgs);
|
||||||
|
|
||||||
|
auto it = m_lowLevelFunctions.find(_name);
|
||||||
|
if (it == m_lowLevelFunctions.end())
|
||||||
|
{
|
||||||
|
eth::AssemblyItem tag = newTag().pushTag();
|
||||||
|
m_lowLevelFunctions.insert(make_pair(_name, tag));
|
||||||
|
m_lowLevelFunctionGenerationQueue.push(make_tuple(_name, _inArgs, _outArgs, _generator));
|
||||||
|
*this << tag;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*this << it->second;
|
||||||
|
appendJump(eth::AssemblyItem::JumpType::IntoFunction);
|
||||||
|
adjustStackOffset(_outArgs - 1 - _inArgs);
|
||||||
|
*this << retTag.tag();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerContext::appendMissingLowLevelFunctions()
|
||||||
|
{
|
||||||
|
while (!m_lowLevelFunctionGenerationQueue.empty())
|
||||||
|
{
|
||||||
|
string name;
|
||||||
|
unsigned inArgs;
|
||||||
|
unsigned outArgs;
|
||||||
|
function<void(CompilerContext&)> generator;
|
||||||
|
tie(name, inArgs, outArgs, generator) = m_lowLevelFunctionGenerationQueue.front();
|
||||||
|
m_lowLevelFunctionGenerationQueue.pop();
|
||||||
|
|
||||||
|
setStackOffset(inArgs + 1);
|
||||||
|
*this << m_lowLevelFunctions.at(name).tag();
|
||||||
|
generator(*this);
|
||||||
|
CompilerUtils(*this).moveToStackTop(outArgs);
|
||||||
|
appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
|
||||||
|
solAssert(stackHeight() == outArgs, "Invalid stack height in low-level function " + name + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerContext::addVariable(VariableDeclaration const& _declaration,
|
void CompilerContext::addVariable(VariableDeclaration const& _declaration,
|
||||||
unsigned _offsetToCurrent)
|
unsigned _offsetToCurrent)
|
||||||
{
|
{
|
||||||
@ -125,21 +173,6 @@ Declaration const* CompilerContext::nextFunctionToCompile() const
|
|||||||
return m_functionCompilationQueue.nextFunctionToCompile();
|
return m_functionCompilationQueue.nextFunctionToCompile();
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::AssemblyItem const* CompilerContext::lowLevelFunctionEntryPoint(string const& _name) const
|
|
||||||
{
|
|
||||||
auto it = m_lowLevelFunctions.find(_name);
|
|
||||||
if (it == m_lowLevelFunctions.end())
|
|
||||||
return nullptr;
|
|
||||||
else
|
|
||||||
return *it;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompilerContext::addLowLevelFunction(string const& _name, eth::AssemblyItem const& _label)
|
|
||||||
{
|
|
||||||
solAssert(lowLevelFunctionEntryPoint(_name) != nullptr, "Low level function with that name already exists.");
|
|
||||||
m_lowLevelFunctions[_name] = _label.pushTag();
|
|
||||||
}
|
|
||||||
|
|
||||||
ModifierDefinition const& CompilerContext::functionModifier(string const& _name) const
|
ModifierDefinition const& CompilerContext::functionModifier(string const& _name) const
|
||||||
{
|
{
|
||||||
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
|
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
|
||||||
|
@ -22,16 +22,20 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
|
#include <libsolidity/ast/Types.h>
|
||||||
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
|
|
||||||
|
#include <libevmasm/Instruction.h>
|
||||||
|
#include <libevmasm/Assembly.h>
|
||||||
|
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <libevmasm/Instruction.h>
|
#include <functional>
|
||||||
#include <libevmasm/Assembly.h>
|
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
|
||||||
#include <libsolidity/ast/Types.h>
|
|
||||||
#include <libsolidity/ast/ASTAnnotations.h>
|
|
||||||
#include <libdevcore/Common.h>
|
|
||||||
|
|
||||||
namespace dev {
|
namespace dev {
|
||||||
namespace solidity {
|
namespace solidity {
|
||||||
@ -90,11 +94,18 @@ public:
|
|||||||
/// as "having code".
|
/// as "having code".
|
||||||
void startFunction(Declaration const& _function);
|
void startFunction(Declaration const& _function);
|
||||||
|
|
||||||
/// Returns the label of the low level function with the given name or nullptr if it
|
/// Appends a call to the named low-level function and inserts the generator into the
|
||||||
/// does not exist. The lifetime of the returned pointer is short.
|
/// list of low-level-functions to be generated, unless it already exists.
|
||||||
eth::AssemblyItem const* lowLevelFunctionEntryPoint(std::string const& _name) const;
|
/// Note that the generator should not assume that objects are still alive when it is called,
|
||||||
/// Inserts a low level function entry point into the list of low level functions.
|
/// unless they are guaranteed to be alive for the whole run of the compiler (AST nodes, for example).
|
||||||
void addLowLevelFunction(std::string const& _name, eth::AssemblyItem const& _label);
|
void callLowLevelFunction(
|
||||||
|
std::string const& _name,
|
||||||
|
unsigned _inArgs,
|
||||||
|
unsigned _outArgs,
|
||||||
|
std::function<void(CompilerContext&)> const& _generator
|
||||||
|
);
|
||||||
|
/// Generates the code for missing low-level functions, i.e. calls the generators passed above.
|
||||||
|
void appendMissingLowLevelFunctions();
|
||||||
|
|
||||||
ModifierDefinition const& functionModifier(std::string const& _name) const;
|
ModifierDefinition const& functionModifier(std::string const& _name) const;
|
||||||
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
|
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
|
||||||
@ -256,6 +267,8 @@ private:
|
|||||||
size_t m_runtimeSub = -1;
|
size_t m_runtimeSub = -1;
|
||||||
/// An index of low-level function labels by name.
|
/// An index of low-level function labels by name.
|
||||||
std::map<std::string, eth::AssemblyItem> m_lowLevelFunctions;
|
std::map<std::string, eth::AssemblyItem> m_lowLevelFunctions;
|
||||||
|
/// The queue of low-level functions to generate.
|
||||||
|
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -820,37 +820,46 @@ void CompilerUtils::pushZeroValue(Type const& _type)
|
|||||||
}
|
}
|
||||||
solAssert(referenceType->location() == DataLocation::Memory, "");
|
solAssert(referenceType->location() == DataLocation::Memory, "");
|
||||||
|
|
||||||
m_context << u256(max(32u, _type.calldataEncodedSize()));
|
TypePointer type = _type.shared_from_this();
|
||||||
allocateMemory();
|
m_context.callLowLevelFunction(
|
||||||
m_context << Instruction::DUP1;
|
"$pushZeroValue_" + referenceType->identifier(),
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
[type](CompilerContext& _context) {
|
||||||
|
CompilerUtils utils(_context);
|
||||||
|
_context << u256(max(32u, type->calldataEncodedSize()));
|
||||||
|
utils.allocateMemory();
|
||||||
|
_context << Instruction::DUP1;
|
||||||
|
|
||||||
if (auto structType = dynamic_cast<StructType const*>(&_type))
|
if (auto structType = dynamic_cast<StructType const*>(type.get()))
|
||||||
for (auto const& member: structType->members(nullptr))
|
for (auto const& member: structType->members(nullptr))
|
||||||
{
|
{
|
||||||
pushZeroValue(*member.type);
|
utils.pushZeroValue(*member.type);
|
||||||
storeInMemoryDynamic(*member.type);
|
utils.storeInMemoryDynamic(*member.type);
|
||||||
}
|
}
|
||||||
else if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
|
else if (auto arrayType = dynamic_cast<ArrayType const*>(type.get()))
|
||||||
{
|
{
|
||||||
if (arrayType->isDynamicallySized())
|
if (arrayType->isDynamicallySized())
|
||||||
{
|
{
|
||||||
// zero length
|
// zero length
|
||||||
m_context << u256(0);
|
_context << u256(0);
|
||||||
storeInMemoryDynamic(IntegerType(256));
|
utils.storeInMemoryDynamic(IntegerType(256));
|
||||||
}
|
}
|
||||||
else if (arrayType->length() > 0)
|
else if (arrayType->length() > 0)
|
||||||
{
|
{
|
||||||
m_context << arrayType->length() << Instruction::SWAP1;
|
_context << arrayType->length() << Instruction::SWAP1;
|
||||||
// stack: items_to_do memory_pos
|
// stack: items_to_do memory_pos
|
||||||
zeroInitialiseMemoryArray(*arrayType);
|
utils.zeroInitialiseMemoryArray(*arrayType);
|
||||||
// stack: updated_memory_pos
|
// stack: updated_memory_pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
solAssert(false, "Requested initialisation for unknown type: " + _type.toString());
|
solAssert(false, "Requested initialisation for unknown type: " + type->toString());
|
||||||
|
|
||||||
// remove the updated memory pointer
|
// remove the updated memory pointer
|
||||||
m_context << Instruction::POP;
|
_context << Instruction::POP;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
||||||
|
@ -827,6 +827,7 @@ void ContractCompiler::appendMissingFunctions()
|
|||||||
function->accept(*this);
|
function->accept(*this);
|
||||||
solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?");
|
solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?");
|
||||||
}
|
}
|
||||||
|
m_context.appendMissingLowLevelFunctions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContractCompiler::appendModifierOrFunctionCode()
|
void ContractCompiler::appendModifierOrFunctionCode()
|
||||||
|
Loading…
Reference in New Issue
Block a user