Implement calldata arrays for Yul IR

This commit is contained in:
Daniel Kirchner
2020-02-12 14:00:29 +01:00
parent b580a7a35d
commit df0873d138
7 changed files with 178 additions and 7 deletions
+53 -1
View File
@@ -908,6 +908,58 @@ string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type)
});
}
string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type)
{
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
string functionName = "calldata_array_index_access_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(base_ref<?dynamicallySized>, length</dynamicallySized>, index) -> addr<?dynamicallySizedBase>, len</dynamicallySizedBase> {
if iszero(lt(index, <?dynamicallySized>length<!dynamicallySized><arrayLen></dynamicallySized>)) { invalid() }
addr := add(base_ref, mul(index, <stride>))
<?dynamicallyEncodedBase>
addr<?dynamicallySizedBase>, len</dynamicallySizedBase> := <accessCalldataTail>(base_ref, addr)
</dynamicallyEncodedBase>
}
)")
("functionName", functionName)
("stride", to_string(_type.calldataStride()))
("dynamicallySized", _type.isDynamicallySized())
("dynamicallyEncodedBase", _type.baseType()->isDynamicallyEncoded())
("dynamicallySizedBase", _type.baseType()->isDynamicallySized())
("arrayLen", toCompactHexWithPrefix(_type.length()))
("accessCalldataTail", _type.baseType()->isDynamicallyEncoded() ? accessCalldataTailFunction(*_type.baseType()): "")
.render();
});
}
string YulUtilFunctions::accessCalldataTailFunction(Type const& _type)
{
solAssert(_type.isDynamicallyEncoded(), "");
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
string functionName = "access_calldata_tail_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(base_ref, ptr_to_tail) -> addr<?dynamicallySized>, length</dynamicallySized> {
let rel_offset_of_tail := calldataload(ptr_to_tail)
if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { revert(0, 0) }
addr := add(base_ref, rel_offset_of_tail)
<?dynamicallySized>
length := calldataload(addr)
if gt(length, 0xffffffffffffffff) { revert(0, 0) }
if sgt(base_ref, sub(calldatasize(), mul(length, <calldataStride>))) { revert(0, 0) }
addr := add(addr, 32)
</dynamicallySized>
}
)")
("functionName", functionName)
("dynamicallySized", _type.isDynamicallySized())
("neededLength", toCompactHexWithPrefix(_type.calldataEncodedTailSize()))
("calldataStride", toCompactHexWithPrefix(_type.isDynamicallySized() ? dynamic_cast<ArrayType const&>(_type).calldataStride() : 0))
.render();
});
}
string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
{
solAssert(!_type.isByteArray(), "");
@@ -1932,7 +1984,7 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
function <functionName>(memPtr) -> value {
value := <load>(memPtr)
<?needsValidation>
value := <validate>(value)
<validate>(value)
</needsValidation>
}
)")
+6 -1
View File
@@ -172,9 +172,14 @@ public:
/// @returns the name of a function that returns the calldata address for the
/// given array base ref and index.
/// signature: (baseRef, index) -> address
/// signature: (baseRef, index) -> offset[, length]
std::string calldataArrayIndexAccessFunction(ArrayType const& _type);
/// @returns the name of a function that follows a calldata tail while performing
/// bounds checks.
/// signature: (baseRef, tailPointer) -> offset[, length]
std::string accessCalldataTailFunction(Type const& _type);
/// @returns the name of a function that advances an array data pointer to the next element.
/// Only works for memory arrays, calldata arrays and storage arrays that every item occupies one or multiple full slots.
std::string nextArrayElementFunction(ArrayType const& _type);
@@ -815,7 +815,12 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
else if (member == "gasprice")
define(_memberAccess) << "gasprice()\n";
else if (member == "data")
solUnimplementedAssert(false, "");
{
IRVariable var(_memberAccess);
declare(var);
define(var.part("offset")) << "0\n";
define(var.part("length")) << "calldatasize()\n";
}
else if (member == "sig")
define(_memberAccess) <<
"and(calldataload(0), " <<
@@ -862,8 +867,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
switch (type.location())
{
case DataLocation::CallData:
solUnimplementedAssert(false, "");
//m_context << Instruction::SWAP1 << Instruction::POP;
define(_memberAccess, IRVariable(_memberAccess.expression()).part("length"));
break;
case DataLocation::Storage:
{
@@ -997,8 +1001,29 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
}
case DataLocation::CallData:
{
solUnimplemented("calldata not yet implemented!");
IRVariable var(m_context.newYulVariable(), *arrayType.baseType());
define(var) <<
m_utils.calldataArrayIndexAccessFunction(arrayType) <<
"(" <<
IRVariable(_indexAccess.baseExpression()).commaSeparatedList() <<
", " <<
expressionAsType(*_indexAccess.indexExpression(), *TypeProvider::uint256()) <<
")\n";
if (arrayType.isByteArray())
define(_indexAccess) <<
m_utils.cleanupFunction(*arrayType.baseType()) <<
"(calldataload(" <<
var.name() <<
"))\n";
else if (arrayType.baseType()->isValueType())
define(_indexAccess) <<
m_utils.readFromCalldata(*arrayType.baseType()) <<
"(" <<
var.commaSeparatedList() <<
")\n";
else
define(_indexAccess, var);
break;
}
}
}
@@ -1534,7 +1559,10 @@ void IRGeneratorForStatements::setLValue(Expression const& _expression, IRLValue
solAssert(!m_currentLValue, "");
if (_expression.annotation().lValueRequested)
{
m_currentLValue.emplace(std::move(_lvalue));
solAssert(!_lvalue.type.dataStoredIn(DataLocation::CallData), "");
}
else
// Only define the expression, if it will not be written to.
define(_expression, readFromLValue(_lvalue));