mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8474 from mijovic/sol2YulCallDataIndexRangeAccess
[Sol->Yul] Adding slicing for call data arrays
This commit is contained in:
commit
362c2175bf
@ -1800,6 +1800,7 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _indexAccess);
|
||||
_indexAccess.baseExpression().accept(*this);
|
||||
// stack: offset length
|
||||
|
||||
Type const& baseType = *_indexAccess.baseExpression().annotation().type;
|
||||
|
||||
@ -1815,27 +1816,21 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess)
|
||||
acceptAndConvert(*_indexAccess.startExpression(), *TypeProvider::uint256());
|
||||
else
|
||||
m_context << u256(0);
|
||||
// stack: offset length sliceStart
|
||||
|
||||
m_context << Instruction::SWAP1;
|
||||
// stack: offset sliceStart length
|
||||
|
||||
if (_indexAccess.endExpression())
|
||||
acceptAndConvert(*_indexAccess.endExpression(), *TypeProvider::uint256());
|
||||
else
|
||||
m_context << Instruction::DUP2;
|
||||
m_context << Instruction::DUP1;
|
||||
// stack: offset sliceStart length sliceEnd
|
||||
|
||||
m_context.appendInlineAssembly(
|
||||
Whiskers(R"({
|
||||
if gt(sliceStart, sliceEnd) { <revertStringStartEnd> }
|
||||
if gt(sliceEnd, length) { <revertStringEndLength> }
|
||||
m_context << Instruction::SWAP3;
|
||||
// stack: sliceEnd sliceStart length offset
|
||||
|
||||
offset := add(offset, mul(sliceStart, <stride>))
|
||||
length := sub(sliceEnd, sliceStart)
|
||||
})")
|
||||
("stride", toString(arrayType->calldataStride()))
|
||||
("revertStringStartEnd", m_context.revertReasonIfDebug("Slice starts after end"))
|
||||
("revertStringEndLength", m_context.revertReasonIfDebug("Slice is greater than length"))
|
||||
.render(),
|
||||
{"offset", "length", "sliceStart", "sliceEnd"}
|
||||
);
|
||||
|
||||
m_context << Instruction::POP << Instruction::POP;
|
||||
m_context.callYulFunction(m_context.utilFunctions().calldataArrayIndexRangeAccess(*arrayType), 4, 2);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -933,6 +933,28 @@ string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::calldataArrayIndexRangeAccess(ArrayType const& _type)
|
||||
{
|
||||
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
|
||||
solAssert(_type.isDynamicallySized(), "");
|
||||
string functionName = "calldata_array_index_range_access_" + _type.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return Whiskers(R"(
|
||||
function <functionName>(offset, length, startIndex, endIndex) -> offsetOut, lengthOut {
|
||||
if gt(startIndex, endIndex) { <revertSliceStartAfterEnd> }
|
||||
if gt(endIndex, length) { <revertSliceGreaterThanLength> }
|
||||
offsetOut := add(offset, mul(startIndex, <stride>))
|
||||
lengthOut := sub(endIndex, startIndex)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("stride", to_string(_type.calldataStride()))
|
||||
("revertSliceStartAfterEnd", revertReasonIfDebug("Slice starts after end"))
|
||||
("revertSliceGreaterThanLength", revertReasonIfDebug("Slice is greater than length"))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::accessCalldataTailFunction(Type const& _type)
|
||||
{
|
||||
solAssert(_type.isDynamicallyEncoded(), "");
|
||||
@ -1365,6 +1387,37 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
||||
});
|
||||
}
|
||||
|
||||
if (_from.category() == Type::Category::ArraySlice)
|
||||
{
|
||||
solAssert(_from.isDynamicallySized(), "");
|
||||
solAssert(_from.dataStoredIn(DataLocation::CallData), "");
|
||||
solAssert(_to.category() == Type::Category::Array, "");
|
||||
|
||||
ArraySliceType const& fromType = dynamic_cast<ArraySliceType const&>(_from);
|
||||
ArrayType const& targetType = dynamic_cast<ArrayType const&>(_to);
|
||||
|
||||
solAssert(
|
||||
*fromType.arrayType().baseType() == *targetType.baseType(),
|
||||
"Converting arrays of different type is not possible"
|
||||
);
|
||||
|
||||
string const functionName =
|
||||
"convert_" +
|
||||
_from.identifier() +
|
||||
"_to_" +
|
||||
_to.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return Whiskers(R"(
|
||||
function <functionName>(offset, length) -> outOffset, outLength {
|
||||
outOffset := offset
|
||||
outLength := length
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1)
|
||||
return conversionFunctionSpecial(_from, _to);
|
||||
|
||||
|
@ -175,6 +175,11 @@ public:
|
||||
/// signature: (baseRef, index) -> offset[, length]
|
||||
std::string calldataArrayIndexAccessFunction(ArrayType const& _type);
|
||||
|
||||
/// @returns the name of a function that returns offset and length for array slice
|
||||
/// for the given array offset, length and start and end indices for slice
|
||||
/// signature: (arrayOffset, arrayLength, sliceStart, sliceEnd) -> offset, length
|
||||
std::string calldataArrayIndexRangeAccess(ArrayType const& _type);
|
||||
|
||||
/// @returns the name of a function that follows a calldata tail while performing
|
||||
/// bounds checks.
|
||||
/// signature: (baseRef, tailPointer) -> offset[, length]
|
||||
|
@ -1001,9 +1001,16 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (baseType.category() == Type::Category::Array)
|
||||
else if (baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice)
|
||||
{
|
||||
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
|
||||
ArrayType const& arrayType =
|
||||
baseType.category() == Type::Category::Array ?
|
||||
dynamic_cast<ArrayType const&>(baseType) :
|
||||
dynamic_cast<ArraySliceType const&>(baseType).arrayType();
|
||||
|
||||
if (baseType.category() == Type::Category::ArraySlice)
|
||||
solAssert(arrayType.dataStoredIn(DataLocation::CallData) && arrayType.isDynamicallySized(), "");
|
||||
|
||||
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
||||
|
||||
switch (arrayType.location())
|
||||
@ -1086,9 +1093,50 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
||||
solAssert(false, "Index access only allowed for mappings or arrays.");
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::endVisit(IndexRangeAccess const&)
|
||||
void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess)
|
||||
{
|
||||
solUnimplementedAssert(false, "Index range accesses not yet implemented.");
|
||||
Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type;
|
||||
solAssert(
|
||||
baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice,
|
||||
"Index range accesses is available only on arrays and array slices."
|
||||
);
|
||||
|
||||
ArrayType const& arrayType =
|
||||
baseType.category() == Type::Category::Array ?
|
||||
dynamic_cast<ArrayType const &>(baseType) :
|
||||
dynamic_cast<ArraySliceType const &>(baseType).arrayType();
|
||||
|
||||
switch (arrayType.location())
|
||||
{
|
||||
case DataLocation::CallData:
|
||||
{
|
||||
solAssert(baseType.isDynamicallySized(), "");
|
||||
IRVariable sliceStart{m_context.newYulVariable(), *TypeProvider::uint256()};
|
||||
if (_indexRangeAccess.startExpression())
|
||||
define(sliceStart, IRVariable{*_indexRangeAccess.startExpression()});
|
||||
else
|
||||
define(sliceStart) << u256(0) << "\n";
|
||||
|
||||
IRVariable sliceEnd{
|
||||
m_context.newYulVariable(),
|
||||
*TypeProvider::uint256()
|
||||
};
|
||||
if (_indexRangeAccess.endExpression())
|
||||
define(sliceEnd, IRVariable{*_indexRangeAccess.endExpression()});
|
||||
else
|
||||
define(sliceEnd, IRVariable{_indexRangeAccess.baseExpression()}.part("length"));
|
||||
|
||||
IRVariable range{_indexRangeAccess};
|
||||
define(range) <<
|
||||
m_utils.calldataArrayIndexRangeAccess(arrayType) << "(" <<
|
||||
IRVariable{_indexRangeAccess.baseExpression()}.commaSeparatedList() << ", " <<
|
||||
sliceStart.name() << ", " <<
|
||||
sliceEnd.name() << ")\n";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
solUnimplementedAssert(false, "Index range accesses is implemented only on calldata arrays.");
|
||||
}
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
||||
|
@ -6,6 +6,8 @@ contract C {
|
||||
return (x[start:end][index], x[start:][0:end-start][index], x[:end][start:][index]);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256[],uint256,uint256): 0x80, 0, 0, 0, 1, 42 ->
|
||||
// f(uint256[],uint256,uint256): 0x80, 0, 1, 0, 1, 42 ->
|
||||
|
@ -0,0 +1,45 @@
|
||||
pragma experimental ABIEncoderV2;
|
||||
contract C {
|
||||
function f(uint256[] calldata x, uint256 s, uint256 e) external returns (uint256) {
|
||||
return uint256[](x[s:e]).length;
|
||||
}
|
||||
function f(uint256[] calldata x, uint256 s, uint256 e, uint256 ss, uint256 ee) external returns (uint256) {
|
||||
return uint256[](x[s:e][ss:ee]).length;
|
||||
}
|
||||
function f_s_only(uint256[] calldata x, uint256 s) external returns (uint256) {
|
||||
return uint256[](x[s:]).length;
|
||||
}
|
||||
function f_e_only(uint256[] calldata x, uint256 e) external returns (uint256) {
|
||||
return uint256[](x[:e]).length;
|
||||
}
|
||||
function g(uint256[] calldata x, uint256 s, uint256 e, uint256 idx) external returns (uint256) {
|
||||
return uint256[](x[s:e])[idx];
|
||||
}
|
||||
function gg(uint256[] calldata x, uint256 s, uint256 e, uint256 idx) external returns (uint256) {
|
||||
return x[s:e][idx];
|
||||
}
|
||||
function gg_s_only(uint256[] calldata x, uint256 s, uint256 idx) external returns (uint256) {
|
||||
return x[s:][idx];
|
||||
}
|
||||
function gg_e_only(uint256[] calldata x, uint256 e, uint256 idx) external returns (uint256) {
|
||||
return x[:e][idx];
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256[],uint256,uint256): 0x60, 2, 4, 5, 1, 2, 3, 4, 5 -> 2
|
||||
// f(uint256[],uint256,uint256): 0x60, 2, 6, 5, 1, 2, 3, 4, 5 -> FAILURE
|
||||
// f(uint256[],uint256,uint256): 0x60, 3, 3, 5, 1, 2, 3, 4, 5 -> 0
|
||||
// f(uint256[],uint256,uint256): 0x60, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE
|
||||
// f(uint256[],uint256,uint256): 0x60, 0, 3, 5, 1, 2, 3, 4, 5 -> 3
|
||||
// f(uint256[],uint256,uint256,uint256,uint256): 0xA0, 1, 3, 1, 2, 5, 1, 2, 3, 4, 5 -> 1
|
||||
// f(uint256[],uint256,uint256,uint256,uint256): 0xA0, 1, 3, 1, 4, 5, 1, 2, 3, 4, 5 -> FAILURE
|
||||
// f_s_only(uint256[],uint256): 0x40, 2, 5, 1, 2, 3, 4, 5 -> 3
|
||||
// f_s_only(uint256[],uint256): 0x40, 6, 5, 1, 2, 3, 4, 5 -> FAILURE
|
||||
// f_e_only(uint256[],uint256): 0x40, 3, 5, 1, 2, 3, 4, 5 -> 3
|
||||
// f_e_only(uint256[],uint256): 0x40, 6, 5, 1, 2, 3, 4, 5 -> FAILURE
|
||||
// g(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 1, 5, 1, 2, 3, 4, 5 -> 4
|
||||
// g(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE
|
||||
// gg(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 1, 5, 1, 2, 3, 4, 5 -> 4
|
||||
// gg(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE
|
Loading…
Reference in New Issue
Block a user