Merge remote-tracking branch 'origin/develop' into HEAD

This commit is contained in:
chriseth
2020-11-09 14:28:05 +01:00
238 changed files with 2662 additions and 762 deletions
+2 -2
View File
@@ -802,7 +802,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
items[i]["inRange"] = "1";
else
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);
return templ.render();
@@ -888,7 +888,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))";
previousSlotOffset = storageSlotOffset;
}
members.back()["retrieveValue"] = m_utils.extractFromStorageValue(*memberTypeFrom, intraSlotOffset, false) + "(slotValue)";
members.back()["retrieveValue"] = m_utils.extractFromStorageValue(*memberTypeFrom, intraSlotOffset) + "(slotValue)";
}
else
{
+1 -20
View File
@@ -702,26 +702,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
{
int const depositBefore = _assembly.stackHeight();
solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
{
solAssert(!ref->second.isOffset && !ref->second.isSlot, "");
functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract());
auto functionEntryLabel = m_context.functionEntryLabel(*functionDef).pushTag();
solAssert(functionEntryLabel.data() <= std::numeric_limits<size_t>::max(), "");
_assembly.appendLabelReference(static_cast<size_t>(functionEntryLabel.data()));
// If there is a runtime context, we have to merge both labels into the same
// stack slot in case we store it in storage.
if (CompilerContext* rtc = m_context.runtimeContext())
{
_assembly.appendConstant(u256(1) << 32);
_assembly.appendInstruction(Instruction::MUL);
auto runtimeEntryLabel = rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub());
solAssert(runtimeEntryLabel.data() <= std::numeric_limits<size_t>::max(), "");
_assembly.appendLabelReference(static_cast<size_t>(runtimeEntryLabel.data()));
_assembly.appendInstruction(Instruction::OR);
}
}
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
{
solAssert(!variable->immutable(), "");
if (variable->isConstant())
+93 -97
View File
@@ -2153,58 +2153,47 @@ string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool
string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes)
{
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(), "");
if (_type.category() == Type::Category::Function)
solUnimplementedAssert(!_splitFunctionTypes, "");
string functionName =
"read_from_storage_" +
string(_splitFunctionTypes ? "split_" : "") +
"offset_" +
to_string(_offset) +
string(_splitFunctionTypes ? "split_" : "") + (
_offset.has_value() ?
"offset_" + to_string(*_offset) :
"dynamic"
) +
"_" +
_type.identifier();
return m_functionCollector.createFunction(functionName, [&] {
solAssert(_type.sizeOnStack() == 1, "");
return Whiskers(R"(
function <functionName>(slot) -> value {
value := <extract>(sload(slot))
Whiskers templ(R"(
function <functionName>(slot<?dynamic>, offset</dynamic>) -> <?split>addr, selector<!split>value</split> {
<?split>let</split> value := <extract>(sload(slot)<?dynamic>, offset</dynamic>)
<?split>
addr, selector := <splitFunction>(value)
</split>
}
)")
("functionName", functionName)
("extract", extractFromStorageValue(_type, _offset, false))
.render();
)");
templ("functionName", functionName);
templ("dynamic", !_offset.has_value());
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)
{
solUnimplementedAssert(_type.category() == Type::Category::Struct, "");
@@ -2437,9 +2426,7 @@ string YulUtilFunctions::updateStorageValueFunction(
string YulUtilFunctions::writeToMemoryFunction(Type const& _type)
{
string const functionName =
string("write_to_memory_") +
_type.identifier();
string const functionName = "write_to_memory_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&] {
solAssert(!dynamic_cast<StringLiteralType const*>(&_type), "");
@@ -2493,14 +2480,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 =
"extract_from_storage_value_dynamic" +
string(_splitFunctionTypes ? "split_" : "") +
_type.identifier();
return m_functionCollector.createFunction(functionName, [&] {
return Whiskers(R"(
@@ -2510,21 +2493,14 @@ string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type, bool
)")
("functionName", functionName)
("shr", shiftRightFunctionDynamic())
("cleanupStorage", cleanupFromStorageFunction(_type, _splitFunctionTypes))
("cleanupStorage", cleanupFromStorageFunction(_type))
.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_" +
string(_splitFunctionTypes ? "split_" : "") +
"offset_" +
to_string(_offset) +
_type.identifier();
string functionName = "extract_from_storage_value_offset_" + to_string(_offset) + _type.identifier();
return m_functionCollector.createFunction(functionName, [&] {
return Whiskers(R"(
function <functionName>(slot_value) -> value {
@@ -2533,18 +2509,16 @@ string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offs
)")
("functionName", functionName)
("shr", shiftRightFunction(_offset * 8))
("cleanupStorage", cleanupFromStorageFunction(_type, _splitFunctionTypes))
("cleanupStorage", cleanupFromStorageFunction(_type))
.render();
});
}
string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes)
string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type)
{
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, [&] {
Whiskers templ(R"(
function <functionName>(value) -> cleaned {
@@ -2581,22 +2555,37 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
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();
return m_functionCollector.createFunction(functionName, [&]() {
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)");
solAssert(_type.isValueType(), "");
auto const* funType = dynamic_cast<FunctionType const*>(&_type);
if (funType && funType->kind() == FunctionType::Kind::External)
{
Whiskers templ(R"(
function <functionName>(addr, selector) -> ret {
ret := <prepareBytes>(<combine>(addr, selector))
}
)");
templ("functionName", functionName);
templ("prepareBytes", prepareStoreFunction(*TypeProvider::fixedBytes(24)));
templ("combine", combineExternalFunctionIdFunction());
return templ.render();
}
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();
}
});
}
@@ -3683,37 +3672,44 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
}
solAssert(_type.isValueType(), "");
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
if (funType->kind() == FunctionType::Kind::External)
return Whiskers(R"(
function <functionName>(memPtr) -> addr, selector {
let combined := <load>(memPtr)
addr, selector := <splitFunction>(combined)
}
)")
("functionName", functionName)
("load", _fromCalldata ? "calldataload" : "mload")
("splitFunction", splitExternalFunctionIdFunction())
.render();
return Whiskers(R"(
function <functionName>(ptr) -> value {
Whiskers templ(R"(
function <functionName>(ptr) -> <returnVariables> {
<?fromCalldata>
value := calldataload(ptr)
let value := calldataload(ptr)
<validate>(value)
<!fromCalldata>
value := <cleanup>(mload(ptr))
let value := <cleanup>(mload(ptr))
</fromCalldata>
<returnVariables> :=
<?externalFunction>
<splitFunction>(value)
<!externalFunction>
value
</externalFunction>
}
)")
("functionName", functionName)
("fromCalldata", _fromCalldata)
("validate", validatorFunction(_type, true))
)");
templ("functionName", functionName);
templ("fromCalldata", _fromCalldata);
if (_fromCalldata)
templ("validate", validatorFunction(_type, true));
auto const* funType = dynamic_cast<FunctionType const*>(&_type);
if (funType && funType->kind() == FunctionType::Kind::External)
{
templ("externalFunction", true);
templ("splitFunction", splitExternalFunctionIdFunction());
templ("returnVariables", "addr, selector");
}
else
{
templ("externalFunction", false);
templ("returnVariables", "returnValue");
}
// Byte array elements generally need cleanup.
// Other types are cleaned as well to account for dirty memory e.g. due to inline assembly.
("cleanup", cleanupFunction(_type))
.render();
templ("cleanup", cleanupFunction(_type));
return templ.render();
});
}
+8 -9
View File
@@ -283,10 +283,10 @@ public:
/// @returns a function that extracts a value type from storage slot that has been
/// retrieved already.
/// 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.
std::string extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes);
std::string extractFromStorageValueDynamic(Type const& _type, bool _splitFunctionTypes);
///
/// For external function types, input and output is in "compressed"/"unsplit" form.
std::string extractFromStorageValue(Type const& _type, size_t _offset);
std::string extractFromStorageValueDynamic(Type const& _type);
/// 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
@@ -309,9 +309,8 @@ public:
/// higher order bytes or left-aligns (in case of bytesNN).
/// The storage cleanup expects the value to be right-aligned with potentially
/// dirty higher order bytes.
/// @param _splitFunctionTypes if false, returns the address and function signature in a
/// single variable.
std::string cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes);
/// For external functions, input and output is in "compressed"/"unsplit" form.
std::string cleanupFromStorageFunction(Type const& _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
@@ -459,8 +458,8 @@ private:
/// 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.
std::string readFromStorageValueType(Type const& _type, size_t _offset, bool _splitFunctionTypes);
std::string readFromStorageValueTypeDynamic(Type const& _type, bool _splitFunctionTypes);
/// @param _offset if provided, read from static offset, otherwise offset is a parameter of the Yul function.
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).
std::string readFromStorageReferenceType(Type const& _type);
@@ -2720,7 +2720,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
define(result) << _storage.slot << "\n";
else if (std::holds_alternative<string>(_storage.offset))
define(result) <<
m_utils.readFromStorageDynamic(_lvalue.type, false) <<
m_utils.readFromStorageDynamic(_lvalue.type, true) <<
"(" <<
_storage.slot <<
", " <<
@@ -2728,7 +2728,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
")\n";
else
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 <<
")\n";