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);
|
CompilerContext::LocationSetter locationSetter(m_context, _indexAccess);
|
||||||
_indexAccess.baseExpression().accept(*this);
|
_indexAccess.baseExpression().accept(*this);
|
||||||
|
// stack: offset length
|
||||||
|
|
||||||
Type const& baseType = *_indexAccess.baseExpression().annotation().type;
|
Type const& baseType = *_indexAccess.baseExpression().annotation().type;
|
||||||
|
|
||||||
@ -1815,27 +1816,21 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess)
|
|||||||
acceptAndConvert(*_indexAccess.startExpression(), *TypeProvider::uint256());
|
acceptAndConvert(*_indexAccess.startExpression(), *TypeProvider::uint256());
|
||||||
else
|
else
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
|
// stack: offset length sliceStart
|
||||||
|
|
||||||
|
m_context << Instruction::SWAP1;
|
||||||
|
// stack: offset sliceStart length
|
||||||
|
|
||||||
if (_indexAccess.endExpression())
|
if (_indexAccess.endExpression())
|
||||||
acceptAndConvert(*_indexAccess.endExpression(), *TypeProvider::uint256());
|
acceptAndConvert(*_indexAccess.endExpression(), *TypeProvider::uint256());
|
||||||
else
|
else
|
||||||
m_context << Instruction::DUP2;
|
m_context << Instruction::DUP1;
|
||||||
|
// stack: offset sliceStart length sliceEnd
|
||||||
|
|
||||||
m_context.appendInlineAssembly(
|
m_context << Instruction::SWAP3;
|
||||||
Whiskers(R"({
|
// stack: sliceEnd sliceStart length offset
|
||||||
if gt(sliceStart, sliceEnd) { <revertStringStartEnd> }
|
|
||||||
if gt(sliceEnd, length) { <revertStringEndLength> }
|
|
||||||
|
|
||||||
offset := add(offset, mul(sliceStart, <stride>))
|
m_context.callYulFunction(m_context.utilFunctions().calldataArrayIndexRangeAccess(*arrayType), 4, 2);
|
||||||
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;
|
|
||||||
|
|
||||||
return false;
|
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)
|
string YulUtilFunctions::accessCalldataTailFunction(Type const& _type)
|
||||||
{
|
{
|
||||||
solAssert(_type.isDynamicallyEncoded(), "");
|
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)
|
if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1)
|
||||||
return conversionFunctionSpecial(_from, _to);
|
return conversionFunctionSpecial(_from, _to);
|
||||||
|
|
||||||
|
@ -175,6 +175,11 @@ public:
|
|||||||
/// signature: (baseRef, index) -> offset[, length]
|
/// signature: (baseRef, index) -> offset[, length]
|
||||||
std::string calldataArrayIndexAccessFunction(ArrayType const& _type);
|
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
|
/// @returns the name of a function that follows a calldata tail while performing
|
||||||
/// bounds checks.
|
/// bounds checks.
|
||||||
/// signature: (baseRef, tailPointer) -> offset[, length]
|
/// 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.");
|
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
||||||
|
|
||||||
switch (arrayType.location())
|
switch (arrayType.location())
|
||||||
@ -1086,9 +1093,50 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
|||||||
solAssert(false, "Index access only allowed for mappings or arrays.");
|
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)
|
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]);
|
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, 0, 0, 1, 42 ->
|
||||||
// f(uint256[],uint256,uint256): 0x80, 0, 1, 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