Merge pull request #7842 from ethereum/fixPushWithArg

Fix Yul IR push with and without argument
This commit is contained in:
Daniel Kirchner 2019-11-28 23:59:06 +01:00 committed by GitHub
commit 0914db8b9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 107 additions and 14 deletions

View File

@ -633,11 +633,10 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type)
string functionName = "array_push_" + _type.identifier(); string functionName = "array_push_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() { return m_functionCollector->createFunction(functionName, [&]() {
return Whiskers(R"( return Whiskers(R"(
function <functionName>(array, value) -> newLen { function <functionName>(array, value) {
let oldLen := <fetchLength>(array) let oldLen := <fetchLength>(array)
if iszero(lt(oldLen, <maxArrayLength>)) { invalid() } if iszero(lt(oldLen, <maxArrayLength>)) { invalid() }
newLen := add(oldLen, 1) sstore(array, add(oldLen, 1))
sstore(array, newLen)
let slot, offset := <indexAccess>(array, oldLen) let slot, offset := <indexAccess>(array, oldLen)
<storeValue>(slot, offset, value) <storeValue>(slot, offset, value)
@ -651,6 +650,33 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type)
}); });
} }
string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type)
{
solAssert(_type.location() == DataLocation::Storage, "");
solAssert(_type.isDynamicallySized(), "");
solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!");
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
string functionName = "array_push_zero_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(array) -> slot, offset {
let oldLen := <fetchLength>(array)
if iszero(lt(oldLen, <maxArrayLength>)) { invalid() }
sstore(array, add(oldLen, 1))
slot, offset := <indexAccess>(array, oldLen)
<storeValue>(slot, offset, <zeroValueFunction>())
})")
("functionName", functionName)
("fetchLength", arrayLengthFunction(_type))
("indexAccess", storageArrayIndexAccessFunction(_type))
("storeValue", updateStorageValueFunction(*_type.baseType()))
("maxArrayLength", (u256(1) << 64).str())
("zeroValueFunction", zeroValueFunction(*_type.baseType()))
.render();
});
}
string YulUtilFunctions::clearStorageRangeFunction(Type const& _type) string YulUtilFunctions::clearStorageRangeFunction(Type const& _type)
{ {
string functionName = "clear_storage_range_" + _type.identifier(); string functionName = "clear_storage_range_" + _type.identifier();

View File

@ -126,9 +126,13 @@ public:
std::string storageArrayPopFunction(ArrayType const& _type); std::string storageArrayPopFunction(ArrayType const& _type);
/// @returns the name of a function that pushes an element to a storage array /// @returns the name of a function that pushes an element to a storage array
/// signature: (array, value) -> newlength /// signature: (array, value)
std::string storageArrayPushFunction(ArrayType const& _type); std::string storageArrayPushFunction(ArrayType const& _type);
/// @returns the name of a function that pushes the base type's zero element to a storage array and returns storage slot and offset of the added element.
/// signature: (array) -> slot, offset
std::string storageArrayPushZeroFunction(ArrayType const& _type);
/// @returns the name of a function that will clear the storage area given /// @returns the name of a function that will clear the storage area given
/// by the start and end (exclusive) parameters (slots). /// by the start and end (exclusive) parameters (slots).
/// signature: (start, end) /// signature: (start, end)

View File

@ -659,15 +659,28 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
ArrayType const& arrayType = dynamic_cast<ArrayType const&>( ArrayType const& arrayType = dynamic_cast<ArrayType const&>(
*dynamic_cast<MemberAccess const&>(_functionCall.expression()).expression().annotation().type *dynamic_cast<MemberAccess const&>(_functionCall.expression()).expression().annotation().type
); );
defineExpression(_functionCall) << if (arguments.empty())
m_utils.storageArrayPushFunction(arrayType) << {
"(" << auto slotName = m_context.newYulVariable();
m_context.variable(_functionCall.expression()) << auto offsetName = m_context.newYulVariable();
", " << ( m_code << "let " << slotName << ", " << offsetName << " := " <<
arguments.empty() ? m_utils.storageArrayPushZeroFunction(arrayType) <<
m_utils.zeroValueFunction(*arrayType.baseType()) + "()" : "(" << m_context.variable(_functionCall.expression()) << ")\n";
expressionAsType(*arguments.front(), *arrayType.baseType()) setLValue(_functionCall, make_unique<IRStorageItem>(
) << ")\n"; m_context.utils(),
slotName,
offsetName,
*arrayType.baseType()
));
}
else
m_code <<
m_utils.storageArrayPushFunction(arrayType) <<
"(" <<
m_context.variable(_functionCall.expression()) <<
", " <<
expressionAsType(*arguments.front(), *arrayType.baseType()) <<
")\n";
break; break;
} }
default: default:

View File

@ -0,0 +1,25 @@
contract C {
uint[] storageArray;
function test(uint256 v) public {
storageArray.push() = v;
}
function getLength() public view returns (uint256) {
return storageArray.length;
}
function fetch(uint256 a) public view returns (uint256) {
return storageArray[a];
}
}
// ====
// compileViaYul: also
// ----
// getLength() -> 0
// test(uint256): 42 ->
// getLength() -> 1
// fetch(uint256): 0 -> 42
// fetch(uint256): 1 -> FAILURE
// test(uint256): 23 ->
// getLength() -> 2
// fetch(uint256): 0 -> 42
// fetch(uint256): 1 -> 23
// fetch(uint256): 2 -> FAILURE

View File

@ -0,0 +1,25 @@
contract C {
uint[] storageArray;
function test(uint256 v) public {
storageArray.push(v);
}
function getLength() public view returns (uint256) {
return storageArray.length;
}
function fetch(uint256 a) public view returns (uint256) {
return storageArray[a];
}
}
// ====
// compileViaYul: also
// ----
// getLength() -> 0
// test(uint256): 42 ->
// getLength() -> 1
// fetch(uint256): 0 -> 42
// fetch(uint256): 1 -> FAILURE
// test(uint256): 23 ->
// getLength() -> 2
// fetch(uint256): 0 -> 42
// fetch(uint256): 1 -> 23
// fetch(uint256): 2 -> FAILURE

View File

@ -12,7 +12,7 @@
// { // {
// { // {
// let y := mload(0x20) // let y := mload(0x20)
// for { } iszero(iszero(and(y, 8))) { if y { revert(0, 0) } } // for { } and(y, 8) { if y { revert(0, 0) } }
// { // {
// if y { continue } // if y { continue }
// sstore(1, 0) // sstore(1, 0)