mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8270 from ethereum/sol2yul-ExternalCall
Finishes external call implementation.
This commit is contained in:
commit
3e4493dc35
@ -622,13 +622,13 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
solAssert(indexedArgs.size() <= 4, "Too many indexed arguments.");
|
||||
Whiskers templ(R"({
|
||||
let <pos> := mload(<freeMemoryPointer>)
|
||||
let <pos> := <freeMemory>
|
||||
let <end> := <encode>(<pos> <nonIndexedArgs>)
|
||||
<log>(<pos>, sub(<end>, <pos>) <indexedArgs>)
|
||||
})");
|
||||
templ("pos", m_context.newYulVariable());
|
||||
templ("end", m_context.newYulVariable());
|
||||
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
|
||||
templ("freeMemory", freeMemory());
|
||||
templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes));
|
||||
templ("nonIndexedArgs", nonIndexedArgs);
|
||||
templ("log", "log" + to_string(indexedArgs.size()));
|
||||
@ -1173,7 +1173,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
|
||||
|
||||
bool haveReturndatacopy = m_context.evmVersion().supportsReturndata();
|
||||
unsigned retSize = 0;
|
||||
unsigned estimatedReturnSize = 0;
|
||||
bool dynamicReturnSize = false;
|
||||
TypePointers returnTypes;
|
||||
if (!returnSuccessConditionAndReturndata)
|
||||
@ -1188,13 +1188,13 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
{
|
||||
solAssert(haveReturndatacopy, "");
|
||||
dynamicReturnSize = true;
|
||||
retSize = 0;
|
||||
estimatedReturnSize = 0;
|
||||
break;
|
||||
}
|
||||
else if (retType->decodingType())
|
||||
retSize += retType->decodingType()->calldataEncodedSize();
|
||||
estimatedReturnSize += retType->decodingType()->calldataEncodedSize();
|
||||
else
|
||||
retSize += retType->calldataEncodedSize();
|
||||
estimatedReturnSize += retType->calldataEncodedSize();
|
||||
}
|
||||
|
||||
TypePointers argumentTypes;
|
||||
@ -1204,7 +1204,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
argumentTypes.emplace_back(&type(*arg));
|
||||
argumentStrings.emplace_back(IRVariable(*arg).commaSeparatedList());
|
||||
}
|
||||
string argumentString = joinHumanReadable(argumentStrings);
|
||||
string argumentString = ", " + joinHumanReadable(argumentStrings);
|
||||
|
||||
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
|
||||
|
||||
@ -1214,8 +1214,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
// (which we would have to subtract from the gas left)
|
||||
// We could also just use MLOAD; POP right before the gas calculation, but the optimizer
|
||||
// would remove that, so we use MSTORE here.
|
||||
if (!funType.gasSet() && retSize > 0)
|
||||
m_code << "mstore(add(" << fetchFreeMem() << ", " << to_string(retSize) << "), 0)\n";
|
||||
if (!funType.gasSet() && estimatedReturnSize > 0)
|
||||
m_code << "mstore(add(" << freeMemory() << ", " << to_string(estimatedReturnSize) << "), 0)\n";
|
||||
}
|
||||
|
||||
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
|
||||
@ -1226,26 +1226,29 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
if iszero(extcodesize(<address>)) { revert(0, 0) }
|
||||
</checkExistence>
|
||||
|
||||
let <pos> := <freeMem>
|
||||
let <pos> := <freeMemory>
|
||||
|
||||
mstore(<pos>, <shl28>(<funId>))
|
||||
let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
|
||||
|
||||
let <result> := <call>(<gas>, <address>, <value>, <pos>, sub(<end>, <pos>), <pos>, <retSize>)
|
||||
if iszero(<result>) { <forwardingRevert> }
|
||||
let <result> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>)
|
||||
if iszero(<result>) { <forwardingRevert>() }
|
||||
|
||||
<?dynamicReturnSize>
|
||||
returndatacopy(<pos>, 0, returndatasize())
|
||||
</dynamicReturnSize>
|
||||
<allocate>
|
||||
mstore(<freeMem>, add(<pos>, and(add(<retSize>, 0x1f), not(0x1f))))
|
||||
<?returns> let <retvars> := </returns> <abiDecode>(<pos>, <retSize>)
|
||||
|
||||
mstore(<freeMemoryPointer>, add(<pos>, and(add(<returnSize>, 0x1f), not(0x1f))))
|
||||
<?returns> let <retVars> := </returns> <abiDecode>(<pos>, add(<pos>, <returnSize>))
|
||||
)");
|
||||
templ("pos", m_context.newYulVariable());
|
||||
templ("end", m_context.newYulVariable());
|
||||
templ("result", m_context.newYulVariable());
|
||||
templ("freeMem", fetchFreeMem());
|
||||
templ("freeMemory", freeMemory());
|
||||
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
|
||||
templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4)));
|
||||
templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name());
|
||||
templ("address", IRVariable(_functionCall.expression()).part("address").name());
|
||||
|
||||
// If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place.
|
||||
// Move arguments to memory, will not update the free memory pointer (but will update the memory
|
||||
@ -1257,23 +1260,19 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
encodeInPlace = false;
|
||||
bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
|
||||
solUnimplementedAssert(!encodeInPlace, "");
|
||||
solUnimplementedAssert(!funType.padArguments(), "");
|
||||
solUnimplementedAssert(funType.padArguments(), "");
|
||||
templ("encodeArgs", abi.tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall));
|
||||
templ("argumentString", argumentString);
|
||||
|
||||
// Output data will replace input data, unless we have ECRecover (then, output
|
||||
// area will be 32 bytes just before input area).
|
||||
templ("retSize", to_string(retSize));
|
||||
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
|
||||
|
||||
if (isDelegateCall)
|
||||
solAssert(!funType.valueSet(), "Value set for delegatecall");
|
||||
else if (useStaticCall)
|
||||
solAssert(!funType.valueSet(), "Value set for staticcall");
|
||||
else if (funType.valueSet())
|
||||
templ("value", IRVariable(_functionCall.expression()).part("value").name());
|
||||
else
|
||||
templ("value", "0");
|
||||
solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall");
|
||||
solAssert(!useStaticCall || !funType.valueSet(), "Value set for staticcall");
|
||||
|
||||
templ("hasValue", !isDelegateCall && !useStaticCall);
|
||||
templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
||||
|
||||
// Check that the target contract exists (has code) for non-low-level calls.
|
||||
bool checkExistence = (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall);
|
||||
@ -1315,13 +1314,18 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
if (haveReturndatacopy)
|
||||
templ("returnSize", "returndatasize()");
|
||||
else
|
||||
templ("returnSize", to_string(retSize));
|
||||
templ("returnSize", to_string(estimatedReturnSize));
|
||||
|
||||
templ("reservedReturnSize", dynamicReturnSize ? "0" : to_string(estimatedReturnSize));
|
||||
|
||||
templ("abiDecode", abi.tupleDecoder(returnTypes, true));
|
||||
templ("returns", !returnTypes.empty());
|
||||
templ("retVars", IRVariable(_functionCall).commaSeparatedList());
|
||||
|
||||
m_code << templ.render();
|
||||
}
|
||||
|
||||
string IRGeneratorForStatements::fetchFreeMem() const
|
||||
string IRGeneratorForStatements::freeMemory()
|
||||
{
|
||||
return "mload(" + to_string(CompilerUtils::freeMemoryPointer) + ")";
|
||||
}
|
||||
|
@ -75,7 +75,9 @@ private:
|
||||
std::vector<ASTPointer<Expression const>> const& _arguments
|
||||
);
|
||||
|
||||
std::string fetchFreeMem() const;
|
||||
/// @returns code that evaluates to the first unused memory slot (which does not have to
|
||||
/// be empty).
|
||||
static std::string freeMemory();
|
||||
|
||||
/// Generates the required conversion code and @returns an IRVariable referring to the value of @a _variable
|
||||
/// converted to type @a _to.
|
||||
|
17
test/libsolidity/semanticTests/array/memory.sol
Normal file
17
test/libsolidity/semanticTests/array/memory.sol
Normal file
@ -0,0 +1,17 @@
|
||||
pragma solidity >= 0.6.0;
|
||||
|
||||
contract C {
|
||||
function h(uint[4] memory n) public pure returns (uint) {
|
||||
return n[0] + n[1] + n[2] + n[3];
|
||||
}
|
||||
|
||||
function i(uint[4] memory n) public view returns (uint) {
|
||||
return this.h(n) * 2;
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// h(uint256[4]): 1, 2, 3, 4 -> 10
|
||||
// i(uint256[4]): 1, 2, 3, 4 -> 20
|
@ -0,0 +1,17 @@
|
||||
pragma solidity >= 0.6.0;
|
||||
|
||||
contract C {
|
||||
function g(uint n) external pure returns (uint) {
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
function f(uint n) public view returns (uint) {
|
||||
return this.g(2 * n);
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// g(uint256): 4 -> 5
|
||||
// f(uint256): 2 -> 5
|
@ -0,0 +1,24 @@
|
||||
pragma solidity >= 0.6.0;
|
||||
|
||||
contract C {
|
||||
function d(uint n) external pure returns (uint[] memory) {
|
||||
uint[] memory data = new uint[](n);
|
||||
for (uint i = 0; i < data.length; ++i)
|
||||
data[i] = i;
|
||||
return data;
|
||||
}
|
||||
|
||||
function dt(uint n) public view returns (uint) {
|
||||
uint[] memory data = this.d(n);
|
||||
uint sum = 0;
|
||||
for (uint i = 0; i < data.length; ++i)
|
||||
sum += data[i];
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// EVMVersion: >=byzantium
|
||||
// ----
|
||||
// dt(uint256): 4 -> 6
|
Loading…
Reference in New Issue
Block a user