Merge pull request #10182 from ethereum/storeExternalFunctions

Store external functions
This commit is contained in:
Đorđe Mijović 2020-11-03 15:09:42 +01:00 committed by GitHub
commit 2f3b0bf9ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 112 additions and 86 deletions

View File

@ -802,7 +802,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
items[i]["inRange"] = "1"; items[i]["inRange"] = "1";
else else
items[i]["inRange"] = "0"; items[i]["inRange"] = "0";
items[i]["extractFromSlot"] = m_utils.extractFromStorageValue(*_from.baseType(), i * storageBytes, false); items[i]["extractFromSlot"] = m_utils.extractFromStorageValue(*_from.baseType(), i * storageBytes);
} }
templ("items", items); templ("items", items);
return templ.render(); return templ.render();
@ -888,7 +888,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))"; members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))";
previousSlotOffset = storageSlotOffset; previousSlotOffset = storageSlotOffset;
} }
members.back()["retrieveValue"] = m_utils.extractFromStorageValue(*memberTypeFrom, intraSlotOffset, false) + "(slotValue)"; members.back()["retrieveValue"] = m_utils.extractFromStorageValue(*memberTypeFrom, intraSlotOffset) + "(slotValue)";
} }
else else
{ {

View File

@ -2059,58 +2059,47 @@ string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool
string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes) string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes)
{ {
solAssert(_type.isValueType(), ""); solAssert(_type.isValueType(), "");
return readFromStorageValueTypeDynamic(_type, _splitFunctionTypes); return readFromStorageValueType(_type, {}, _splitFunctionTypes);
} }
string YulUtilFunctions::readFromStorageValueType(Type const& _type, size_t _offset, bool _splitFunctionTypes) string YulUtilFunctions::readFromStorageValueType(Type const& _type, optional<size_t> _offset, bool _splitFunctionTypes)
{ {
solAssert(_type.isValueType(), ""); solAssert(_type.isValueType(), "");
if (_type.category() == Type::Category::Function)
solUnimplementedAssert(!_splitFunctionTypes, "");
string functionName = string functionName =
"read_from_storage_" + "read_from_storage_" +
string(_splitFunctionTypes ? "split_" : "") + string(_splitFunctionTypes ? "split_" : "") + (
"offset_" + _offset.has_value() ?
to_string(_offset) + "offset_" + to_string(*_offset) :
"dynamic"
) +
"_" + "_" +
_type.identifier(); _type.identifier();
return m_functionCollector.createFunction(functionName, [&] { return m_functionCollector.createFunction(functionName, [&] {
solAssert(_type.sizeOnStack() == 1, ""); Whiskers templ(R"(
return Whiskers(R"( function <functionName>(slot<?dynamic>, offset</dynamic>) -> <?split>addr, selector<!split>value</split> {
function <functionName>(slot) -> value { <?split>let</split> value := <extract>(sload(slot)<?dynamic>, offset</dynamic>)
value := <extract>(sload(slot)) <?split>
addr, selector := <splitFunction>(value)
</split>
} }
)") )");
("functionName", functionName) templ("functionName", functionName);
("extract", extractFromStorageValue(_type, _offset, false)) templ("dynamic", !_offset.has_value());
.render(); if (_offset.has_value())
templ("extract", extractFromStorageValue(_type, *_offset));
else
templ("extract", extractFromStorageValueDynamic(_type));
auto const* funType = dynamic_cast<FunctionType const*>(&_type);
bool split = _splitFunctionTypes && funType && funType->kind() == FunctionType::Kind::External;
templ("split", split);
if (split)
templ("splitFunction", splitExternalFunctionIdFunction());
return templ.render();
}); });
} }
string YulUtilFunctions::readFromStorageValueTypeDynamic(Type const& _type, bool _splitFunctionTypes)
{
solAssert(_type.isValueType(), "");
if (_type.category() == Type::Category::Function)
solUnimplementedAssert(!_splitFunctionTypes, "");
string functionName =
"read_from_storage_value_type_dynamic" +
string(_splitFunctionTypes ? "split_" : "") +
"_" +
_type.identifier();
return m_functionCollector.createFunction(functionName, [&] {
solAssert(_type.sizeOnStack() == 1, "");
return Whiskers(R"(
function <functionName>(slot, offset) -> value {
value := <extract>(sload(slot), offset)
}
)")
("functionName", functionName)
("extract", extractFromStorageValueDynamic(_type, _splitFunctionTypes))
.render();
});
}
string YulUtilFunctions::readFromStorageReferenceType(Type const& _type) string YulUtilFunctions::readFromStorageReferenceType(Type const& _type)
{ {
solUnimplementedAssert(_type.category() == Type::Category::Struct, ""); solUnimplementedAssert(_type.category() == Type::Category::Struct, "");
@ -2343,9 +2332,7 @@ string YulUtilFunctions::updateStorageValueFunction(
string YulUtilFunctions::writeToMemoryFunction(Type const& _type) string YulUtilFunctions::writeToMemoryFunction(Type const& _type)
{ {
string const functionName = string const functionName = "write_to_memory_" + _type.identifier();
string("write_to_memory_") +
_type.identifier();
return m_functionCollector.createFunction(functionName, [&] { return m_functionCollector.createFunction(functionName, [&] {
solAssert(!dynamic_cast<StringLiteralType const*>(&_type), ""); solAssert(!dynamic_cast<StringLiteralType const*>(&_type), "");
@ -2399,14 +2386,10 @@ string YulUtilFunctions::writeToMemoryFunction(Type const& _type)
}); });
} }
string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type, bool _splitFunctionTypes) string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type)
{ {
if (_type.category() == Type::Category::Function)
solUnimplementedAssert(!_splitFunctionTypes, "");
string functionName = string functionName =
"extract_from_storage_value_dynamic" + "extract_from_storage_value_dynamic" +
string(_splitFunctionTypes ? "split_" : "") +
_type.identifier(); _type.identifier();
return m_functionCollector.createFunction(functionName, [&] { return m_functionCollector.createFunction(functionName, [&] {
return Whiskers(R"( return Whiskers(R"(
@ -2416,21 +2399,14 @@ string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type, bool
)") )")
("functionName", functionName) ("functionName", functionName)
("shr", shiftRightFunctionDynamic()) ("shr", shiftRightFunctionDynamic())
("cleanupStorage", cleanupFromStorageFunction(_type, _splitFunctionTypes)) ("cleanupStorage", cleanupFromStorageFunction(_type))
.render(); .render();
}); });
} }
string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes) string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offset)
{ {
solUnimplementedAssert(!_splitFunctionTypes, ""); string functionName = "extract_from_storage_value_offset_" + to_string(_offset) + _type.identifier();
string functionName =
"extract_from_storage_value_" +
string(_splitFunctionTypes ? "split_" : "") +
"offset_" +
to_string(_offset) +
_type.identifier();
return m_functionCollector.createFunction(functionName, [&] { return m_functionCollector.createFunction(functionName, [&] {
return Whiskers(R"( return Whiskers(R"(
function <functionName>(slot_value) -> value { function <functionName>(slot_value) -> value {
@ -2439,18 +2415,16 @@ string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offs
)") )")
("functionName", functionName) ("functionName", functionName)
("shr", shiftRightFunction(_offset * 8)) ("shr", shiftRightFunction(_offset * 8))
("cleanupStorage", cleanupFromStorageFunction(_type, _splitFunctionTypes)) ("cleanupStorage", cleanupFromStorageFunction(_type))
.render(); .render();
}); });
} }
string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes) string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type)
{ {
solAssert(_type.isValueType(), ""); solAssert(_type.isValueType(), "");
if (_type.category() == Type::Category::Function)
solUnimplementedAssert(!_splitFunctionTypes, "");
string functionName = string("cleanup_from_storage_") + (_splitFunctionTypes ? "split_" : "") + _type.identifier(); string functionName = string("cleanup_from_storage_") + _type.identifier();
return m_functionCollector.createFunction(functionName, [&] { return m_functionCollector.createFunction(functionName, [&] {
Whiskers templ(R"( Whiskers templ(R"(
function <functionName>(value) -> cleaned { function <functionName>(value) -> cleaned {
@ -2487,22 +2461,37 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
string YulUtilFunctions::prepareStoreFunction(Type const& _type) string YulUtilFunctions::prepareStoreFunction(Type const& _type)
{ {
if (_type.category() == Type::Category::Function)
solUnimplementedAssert(dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::Internal, "");
string functionName = "prepare_store_" + _type.identifier(); string functionName = "prepare_store_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() { return m_functionCollector.createFunction(functionName, [&]() {
Whiskers templ(R"( solAssert(_type.isValueType(), "");
function <functionName>(value) -> ret { auto const* funType = dynamic_cast<FunctionType const*>(&_type);
ret := <actualPrepare> if (funType && funType->kind() == FunctionType::Kind::External)
} {
)"); Whiskers templ(R"(
templ("functionName", functionName); function <functionName>(addr, selector) -> ret {
if (_type.category() == Type::Category::FixedBytes) ret := <prepareBytes>(<combine>(addr, selector))
templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)"); }
)");
templ("functionName", functionName);
templ("prepareBytes", prepareStoreFunction(*TypeProvider::fixedBytes(24)));
templ("combine", combineExternalFunctionIdFunction());
return templ.render();
}
else else
templ("actualPrepare", "value"); {
return templ.render(); solAssert(_type.sizeOnStack() == 1, "");
Whiskers templ(R"(
function <functionName>(value) -> ret {
ret := <actualPrepare>
}
)");
templ("functionName", functionName);
if (_type.category() == Type::Category::FixedBytes)
templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)");
else
templ("actualPrepare", "value");
return templ.render();
}
}); });
} }

View File

@ -266,10 +266,10 @@ public:
/// @returns a function that extracts a value type from storage slot that has been /// @returns a function that extracts a value type from storage slot that has been
/// retrieved already. /// retrieved already.
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation. /// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
/// @param _splitFunctionTypes if false, returns the address and function signature in a ///
/// single variable. /// For external function types, input and output is in "compressed"/"unsplit" form.
std::string extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes); std::string extractFromStorageValue(Type const& _type, size_t _offset);
std::string extractFromStorageValueDynamic(Type const& _type, bool _splitFunctionTypes); std::string extractFromStorageValueDynamic(Type const& _type);
/// Returns the name of a function will write the given value to /// Returns the name of a function will write the given value to
/// the specified slot and offset. If offset is not given, it is expected as /// the specified slot and offset. If offset is not given, it is expected as
@ -292,9 +292,8 @@ public:
/// higher order bytes or left-aligns (in case of bytesNN). /// higher order bytes or left-aligns (in case of bytesNN).
/// The storage cleanup expects the value to be right-aligned with potentially /// The storage cleanup expects the value to be right-aligned with potentially
/// dirty higher order bytes. /// dirty higher order bytes.
/// @param _splitFunctionTypes if false, returns the address and function signature in a /// For external functions, input and output is in "compressed"/"unsplit" form.
/// single variable. std::string cleanupFromStorageFunction(Type const& _type);
std::string cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes);
/// @returns the name of a function that prepares a value of the given type /// @returns the name of a function that prepares a value of the given type
/// for being stored in storage. This usually includes cleanup and right-alignment /// for being stored in storage. This usually includes cleanup and right-alignment
@ -440,8 +439,8 @@ private:
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation. /// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
/// @param _splitFunctionTypes if false, returns the address and function signature in a /// @param _splitFunctionTypes if false, returns the address and function signature in a
/// single variable. /// single variable.
std::string readFromStorageValueType(Type const& _type, size_t _offset, bool _splitFunctionTypes); /// @param _offset if provided, read from static offset, otherwise offset is a parameter of the Yul function.
std::string readFromStorageValueTypeDynamic(Type const& _type, bool _splitFunctionTypes); std::string readFromStorageValueType(Type const& _type, std::optional<size_t> _offset, bool _splitFunctionTypes);
/// @returns a function that reads a reference type from storage to memory (performing a deep copy). /// @returns a function that reads a reference type from storage to memory (performing a deep copy).
std::string readFromStorageReferenceType(Type const& _type); std::string readFromStorageReferenceType(Type const& _type);

View File

@ -2733,7 +2733,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
define(result) << _storage.slot << "\n"; define(result) << _storage.slot << "\n";
else if (std::holds_alternative<string>(_storage.offset)) else if (std::holds_alternative<string>(_storage.offset))
define(result) << define(result) <<
m_utils.readFromStorageDynamic(_lvalue.type, false) << m_utils.readFromStorageDynamic(_lvalue.type, true) <<
"(" << "(" <<
_storage.slot << _storage.slot <<
", " << ", " <<
@ -2741,7 +2741,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
")\n"; ")\n";
else else
define(result) << define(result) <<
m_utils.readFromStorage(_lvalue.type, std::get<unsigned>(_storage.offset), false) << m_utils.readFromStorage(_lvalue.type, std::get<unsigned>(_storage.offset), true) <<
"(" << "(" <<
_storage.slot << _storage.slot <<
")\n"; ")\n";

View File

@ -24,5 +24,7 @@ contract C {
} }
} }
// ====
// compileViaYul: also
// ---- // ----
// t() -> 9 // t() -> 9

View File

@ -0,0 +1,34 @@
struct S {
uint16 a;
function() external returns (uint) x;
uint16 b;
}
contract Flow {
S[2] t;
function X() public pure returns (uint) {
return 1;
}
function Y() public pure returns (uint) {
return 2;
}
constructor() {
t[0].a = 0xff07;
t[0].b = 0xff07;
t[1].x = this.Y;
t[1].a = 0xff07;
t[1].b = 0xff07;
t[0].x = this.X;
}
function f() public returns (uint, uint) {
return (t[0].x(), t[1].x());
}
}
// ====
// compileViaYul: also
// ----
// f() -> 1, 2

View File

@ -39,6 +39,8 @@ contract C {
} }
} }
// ====
// compileViaYul: also
// ---- // ----
// set() -> // set() ->
// t1() -> 7 // t1() -> 7