mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Use IRVariable's in IR code generation and implement tuples.
This commit is contained in:
parent
6abe0a50b1
commit
3c9f18b749
@ -79,8 +79,9 @@ set(sources
|
|||||||
codegen/ir/IRGeneratorForStatements.h
|
codegen/ir/IRGeneratorForStatements.h
|
||||||
codegen/ir/IRGenerationContext.cpp
|
codegen/ir/IRGenerationContext.cpp
|
||||||
codegen/ir/IRGenerationContext.h
|
codegen/ir/IRGenerationContext.h
|
||||||
codegen/ir/IRLValue.cpp
|
|
||||||
codegen/ir/IRLValue.h
|
codegen/ir/IRLValue.h
|
||||||
|
codegen/ir/IRVariable.cpp
|
||||||
|
codegen/ir/IRVariable.h
|
||||||
formal/BMC.cpp
|
formal/BMC.cpp
|
||||||
formal/BMC.h
|
formal/BMC.h
|
||||||
formal/CHC.cpp
|
formal/CHC.cpp
|
||||||
|
@ -151,7 +151,7 @@ util::Result<TypePointers> transformParametersToExternal(TypePointers const& _pa
|
|||||||
void Type::clearCache() const
|
void Type::clearCache() const
|
||||||
{
|
{
|
||||||
m_members.clear();
|
m_members.clear();
|
||||||
m_stackSlots.reset();
|
m_stackItems.reset();
|
||||||
m_stackSize.reset();
|
m_stackSize.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1703,14 +1703,22 @@ u256 ArrayType::storageSize() const
|
|||||||
return max<u256>(1, u256(size));
|
return max<u256>(1, u256(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<tuple<string, TypePointer>> ArrayType::makeStackSlots() const
|
vector<tuple<string, TypePointer>> ArrayType::makeStackItems() const
|
||||||
{
|
{
|
||||||
if (m_location == DataLocation::CallData && isDynamicallySized())
|
switch (m_location)
|
||||||
|
{
|
||||||
|
case DataLocation::CallData:
|
||||||
|
if (isDynamicallySized())
|
||||||
return {std::make_tuple("offset", TypeProvider::uint256()), std::make_tuple("length", TypeProvider::uint256())};
|
return {std::make_tuple("offset", TypeProvider::uint256()), std::make_tuple("length", TypeProvider::uint256())};
|
||||||
else
|
else
|
||||||
// storage slot or memory offset
|
return {std::make_tuple("offset", TypeProvider::uint256())};
|
||||||
|
case DataLocation::Memory:
|
||||||
|
return {std::make_tuple("mpos", TypeProvider::uint256())};
|
||||||
|
case DataLocation::Storage:
|
||||||
// byte offset inside storage value is omitted
|
// byte offset inside storage value is omitted
|
||||||
return {std::make_tuple(string(), nullptr)};
|
return {std::make_tuple("slot", TypeProvider::uint256())};
|
||||||
|
}
|
||||||
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
string ArrayType::toString(bool _short) const
|
string ArrayType::toString(bool _short) const
|
||||||
@ -1892,7 +1900,7 @@ string ArraySliceType::toString(bool _short) const
|
|||||||
return m_arrayType.toString(_short) + " slice";
|
return m_arrayType.toString(_short) + " slice";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::tuple<std::string, TypePointer>> ArraySliceType::makeStackSlots() const
|
std::vector<std::tuple<std::string, TypePointer>> ArraySliceType::makeStackItems() const
|
||||||
{
|
{
|
||||||
return {{"offset", TypeProvider::uint256()}, {"length", TypeProvider::uint256()}};
|
return {{"offset", TypeProvider::uint256()}, {"length", TypeProvider::uint256()}};
|
||||||
}
|
}
|
||||||
@ -1995,7 +2003,7 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar
|
|||||||
return variablesAndOffsets;
|
return variablesAndOffsets;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<tuple<string, TypePointer>> ContractType::makeStackSlots() const
|
vector<tuple<string, TypePointer>> ContractType::makeStackItems() const
|
||||||
{
|
{
|
||||||
if (m_super)
|
if (m_super)
|
||||||
return {};
|
return {};
|
||||||
@ -2436,7 +2444,7 @@ u256 TupleType::storageSize() const
|
|||||||
solAssert(false, "Storage size of non-storable tuple type requested.");
|
solAssert(false, "Storage size of non-storable tuple type requested.");
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<tuple<string, TypePointer>> TupleType::makeStackSlots() const
|
vector<tuple<string, TypePointer>> TupleType::makeStackItems() const
|
||||||
{
|
{
|
||||||
vector<tuple<string, TypePointer>> slots;
|
vector<tuple<string, TypePointer>> slots;
|
||||||
unsigned i = 1;
|
unsigned i = 1;
|
||||||
@ -2902,7 +2910,7 @@ unsigned FunctionType::storageBytes() const
|
|||||||
solAssert(false, "Storage size of non-storable function type requested.");
|
solAssert(false, "Storage size of non-storable function type requested.");
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<tuple<string, TypePointer>> FunctionType::makeStackSlots() const
|
vector<tuple<string, TypePointer>> FunctionType::makeStackItems() const
|
||||||
{
|
{
|
||||||
vector<tuple<string, TypePointer>> slots;
|
vector<tuple<string, TypePointer>> slots;
|
||||||
Kind kind = m_kind;
|
Kind kind = m_kind;
|
||||||
@ -2927,12 +2935,12 @@ vector<tuple<string, TypePointer>> FunctionType::makeStackSlots() const
|
|||||||
slots = {make_tuple("address", TypeProvider::address())};
|
slots = {make_tuple("address", TypeProvider::address())};
|
||||||
break;
|
break;
|
||||||
case Kind::Internal:
|
case Kind::Internal:
|
||||||
slots = {make_tuple(string(), nullptr)};
|
slots = {make_tuple("functionIdentifier", TypeProvider::uint256())};
|
||||||
break;
|
break;
|
||||||
case Kind::ArrayPush:
|
case Kind::ArrayPush:
|
||||||
case Kind::ArrayPop:
|
case Kind::ArrayPop:
|
||||||
case Kind::ByteArrayPush:
|
case Kind::ByteArrayPush:
|
||||||
slots = {make_tuple(string(), nullptr)};
|
slots = {make_tuple("slot", TypeProvider::uint256())};
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -2945,7 +2953,7 @@ vector<tuple<string, TypePointer>> FunctionType::makeStackSlots() const
|
|||||||
if (m_saltSet)
|
if (m_saltSet)
|
||||||
slots.emplace_back("salt", TypeProvider::uint256());
|
slots.emplace_back("salt", TypeProvider::uint256());
|
||||||
if (bound())
|
if (bound())
|
||||||
for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackSlots())
|
for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackItems())
|
||||||
slots.emplace_back("self_" + boundName, boundType);
|
slots.emplace_back("self_" + boundName, boundType);
|
||||||
return slots;
|
return slots;
|
||||||
}
|
}
|
||||||
@ -3441,7 +3449,7 @@ u256 TypeType::storageSize() const
|
|||||||
solAssert(false, "Storage size of non-storable type type requested.");
|
solAssert(false, "Storage size of non-storable type type requested.");
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<tuple<string, TypePointer>> TypeType::makeStackSlots() const
|
vector<tuple<string, TypePointer>> TypeType::makeStackItems() const
|
||||||
{
|
{
|
||||||
if (auto contractType = dynamic_cast<ContractType const*>(m_actualType))
|
if (auto contractType = dynamic_cast<ContractType const*>(m_actualType))
|
||||||
if (contractType->contractDefinition().isLibrary())
|
if (contractType->contractDefinition().isLibrary())
|
||||||
|
@ -259,18 +259,25 @@ public:
|
|||||||
/// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
|
/// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
|
||||||
/// i.e. it behaves differently in lvalue context and in value context.
|
/// i.e. it behaves differently in lvalue context and in value context.
|
||||||
virtual bool isValueType() const { return false; }
|
virtual bool isValueType() const { return false; }
|
||||||
std::vector<std::tuple<std::string, TypePointer>> const& stackSlots() const
|
/// @returns a list of named and typed stack items that determine the layout of this type on the stack.
|
||||||
|
/// A stack item either has an empty name and type ``nullptr`` referring to a single stack slot, or
|
||||||
|
/// has a non-empty name and a valid type referring to the stack layout of that type.
|
||||||
|
/// The complete layout of a type on the stack can be obtained from its stack items recursively as follows:
|
||||||
|
/// - Each unnamed stack item is untyped (its type is ``nullptr``) and contributes exactly one stack slot.
|
||||||
|
/// - Each named stack item is typed and contributes the stack slots given by the stack items of its type.
|
||||||
|
std::vector<std::tuple<std::string, TypePointer>> const& stackItems() const
|
||||||
{
|
{
|
||||||
if (!m_stackSlots)
|
if (!m_stackItems)
|
||||||
m_stackSlots = makeStackSlots();
|
m_stackItems = makeStackItems();
|
||||||
return *m_stackSlots;
|
return *m_stackItems;
|
||||||
}
|
}
|
||||||
|
/// Total number of stack slots occupied by this type. This is the sum of ``sizeOnStack`` of all ``stackItems()``.
|
||||||
unsigned sizeOnStack() const
|
unsigned sizeOnStack() const
|
||||||
{
|
{
|
||||||
if (!m_stackSize)
|
if (!m_stackSize)
|
||||||
{
|
{
|
||||||
size_t sizeOnStack = 0;
|
size_t sizeOnStack = 0;
|
||||||
for (auto const& slot: stackSlots())
|
for (auto const& slot: stackItems())
|
||||||
if (std::get<1>(slot))
|
if (std::get<1>(slot))
|
||||||
sizeOnStack += std::get<1>(slot)->sizeOnStack();
|
sizeOnStack += std::get<1>(slot)->sizeOnStack();
|
||||||
else
|
else
|
||||||
@ -355,7 +362,9 @@ protected:
|
|||||||
{
|
{
|
||||||
return MemberList::MemberMap();
|
return MemberList::MemberMap();
|
||||||
}
|
}
|
||||||
virtual std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const
|
/// Generates the stack items to be returned by ``stackItems()``. Defaults
|
||||||
|
/// to exactly one unnamed and untyped stack item referring to a single stack slot.
|
||||||
|
virtual std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const
|
||||||
{
|
{
|
||||||
return {std::make_tuple(std::string(), nullptr)};
|
return {std::make_tuple(std::string(), nullptr)};
|
||||||
}
|
}
|
||||||
@ -363,7 +372,7 @@ protected:
|
|||||||
|
|
||||||
/// List of member types (parameterised by scape), will be lazy-initialized.
|
/// List of member types (parameterised by scape), will be lazy-initialized.
|
||||||
mutable std::map<ContractDefinition const*, std::unique_ptr<MemberList>> m_members;
|
mutable std::map<ContractDefinition const*, std::unique_ptr<MemberList>> m_members;
|
||||||
mutable std::optional<std::vector<std::tuple<std::string, TypePointer>>> m_stackSlots;
|
mutable std::optional<std::vector<std::tuple<std::string, TypePointer>>> m_stackItems;
|
||||||
mutable std::optional<size_t> m_stackSize;
|
mutable std::optional<size_t> m_stackSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -597,7 +606,7 @@ public:
|
|||||||
std::string const& value() const { return m_value; }
|
std::string const& value() const { return m_value; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
|
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override { return {}; }
|
||||||
private:
|
private:
|
||||||
std::string m_value;
|
std::string m_value;
|
||||||
};
|
};
|
||||||
@ -783,7 +792,7 @@ public:
|
|||||||
void clearCache() const override;
|
void clearCache() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
|
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override;
|
||||||
private:
|
private:
|
||||||
/// String is interpreted as a subtype of Bytes.
|
/// String is interpreted as a subtype of Bytes.
|
||||||
enum class ArrayKind { Ordinary, Bytes, String };
|
enum class ArrayKind { Ordinary, Bytes, String };
|
||||||
@ -824,7 +833,7 @@ public:
|
|||||||
std::unique_ptr<ReferenceType> copyForLocation(DataLocation, bool) const override { solAssert(false, ""); }
|
std::unique_ptr<ReferenceType> copyForLocation(DataLocation, bool) const override { solAssert(false, ""); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
|
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override;
|
||||||
private:
|
private:
|
||||||
ArrayType const& m_arrayType;
|
ArrayType const& m_arrayType;
|
||||||
};
|
};
|
||||||
@ -885,7 +894,7 @@ public:
|
|||||||
/// offsets in storage.
|
/// offsets in storage.
|
||||||
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const;
|
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const;
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
|
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override;
|
||||||
private:
|
private:
|
||||||
ContractDefinition const& m_contract;
|
ContractDefinition const& m_contract;
|
||||||
/// If true, this is a special "super" type of m_contract containing only members that m_contract inherited
|
/// If true, this is a special "super" type of m_contract containing only members that m_contract inherited
|
||||||
@ -1026,7 +1035,7 @@ public:
|
|||||||
std::vector<TypePointer> const& components() const { return m_components; }
|
std::vector<TypePointer> const& components() const { return m_components; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
|
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override;
|
||||||
private:
|
private:
|
||||||
std::vector<TypePointer> const m_components;
|
std::vector<TypePointer> const m_components;
|
||||||
};
|
};
|
||||||
@ -1282,7 +1291,7 @@ public:
|
|||||||
FunctionTypePointer asCallableFunction(bool _inLibrary, bool _bound = false) const;
|
FunctionTypePointer asCallableFunction(bool _inLibrary, bool _bound = false) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
|
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override;
|
||||||
private:
|
private:
|
||||||
static TypePointers parseElementaryTypeVector(strings const& _types);
|
static TypePointers parseElementaryTypeVector(strings const& _types);
|
||||||
|
|
||||||
@ -1358,7 +1367,7 @@ public:
|
|||||||
|
|
||||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
|
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override;
|
||||||
private:
|
private:
|
||||||
TypePointer m_actualType;
|
TypePointer m_actualType;
|
||||||
};
|
};
|
||||||
@ -1384,7 +1393,7 @@ public:
|
|||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
|
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override { return {}; }
|
||||||
private:
|
private:
|
||||||
TypePointers m_parameterTypes;
|
TypePointers m_parameterTypes;
|
||||||
};
|
};
|
||||||
@ -1412,7 +1421,7 @@ public:
|
|||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
|
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override { return {}; }
|
||||||
private:
|
private:
|
||||||
SourceUnit const& m_sourceUnit;
|
SourceUnit const& m_sourceUnit;
|
||||||
};
|
};
|
||||||
@ -1456,7 +1465,7 @@ public:
|
|||||||
TypePointer typeArgument() const;
|
TypePointer typeArgument() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
|
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override { return {}; }
|
||||||
private:
|
private:
|
||||||
Kind m_kind;
|
Kind m_kind;
|
||||||
/// Contract type used for contract metadata magic.
|
/// Contract type used for contract metadata magic.
|
||||||
|
@ -1791,6 +1791,44 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const
|
|||||||
"_to_" +
|
"_to_" +
|
||||||
_to.identifier();
|
_to.identifier();
|
||||||
return m_functionCollector->createFunction(functionName, [&]() {
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
if (
|
||||||
|
auto fromTuple = dynamic_cast<TupleType const*>(&_from), toTuple = dynamic_cast<TupleType const*>(&_to);
|
||||||
|
fromTuple && toTuple && fromTuple->components().size() == toTuple->components().size()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
size_t sourceStackSize = 0;
|
||||||
|
size_t destStackSize = 0;
|
||||||
|
std::string conversions;
|
||||||
|
for (size_t i = 0; i < fromTuple->components().size(); ++i)
|
||||||
|
{
|
||||||
|
auto fromComponent = fromTuple->components()[i];
|
||||||
|
auto toComponent = toTuple->components()[i];
|
||||||
|
solAssert(fromComponent, "");
|
||||||
|
if (toComponent)
|
||||||
|
{
|
||||||
|
conversions +=
|
||||||
|
suffixedVariableNameList("converted", destStackSize, destStackSize + toComponent->sizeOnStack()) +
|
||||||
|
" := " +
|
||||||
|
conversionFunction(*fromComponent, *toComponent) +
|
||||||
|
"(" +
|
||||||
|
suffixedVariableNameList("value", sourceStackSize, sourceStackSize + fromComponent->sizeOnStack()) +
|
||||||
|
")\n";
|
||||||
|
destStackSize += toComponent->sizeOnStack();
|
||||||
|
}
|
||||||
|
sourceStackSize += fromComponent->sizeOnStack();
|
||||||
|
}
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(<values>) -> <converted> {
|
||||||
|
<conversions>
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("values", suffixedVariableNameList("value", 0, sourceStackSize))
|
||||||
|
("converted", suffixedVariableNameList("converted", 0, destStackSize))
|
||||||
|
("conversions", conversions)
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
|
||||||
solUnimplementedAssert(
|
solUnimplementedAssert(
|
||||||
_from.category() == Type::Category::StringLiteral,
|
_from.category() == Type::Category::StringLiteral,
|
||||||
"Type conversion " + _from.toString() + " -> " + _to.toString() + " not yet implemented."
|
"Type conversion " + _from.toString() + " -> " + _to.toString() + " not yet implemented."
|
||||||
|
@ -31,23 +31,22 @@ using namespace solidity;
|
|||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
using namespace solidity::frontend;
|
using namespace solidity::frontend;
|
||||||
|
|
||||||
string IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl)
|
IRVariable const& IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl)
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(
|
auto const& [it, didInsert] = m_localVariables.emplace(
|
||||||
_varDecl.annotation().type->sizeOnStack() == 1,
|
std::make_pair(&_varDecl, IRVariable{_varDecl})
|
||||||
"Multi-slot types not yet implemented."
|
|
||||||
);
|
);
|
||||||
|
solAssert(didInsert, "Local variable added multiple times.");
|
||||||
return m_localVariables[&_varDecl] = "vloc_" + _varDecl.name() + "_" + to_string(_varDecl.id());
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerationContext::localVariableName(VariableDeclaration const& _varDecl)
|
IRVariable const& IRGenerationContext::localVariable(VariableDeclaration const& _varDecl)
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(
|
||||||
m_localVariables.count(&_varDecl),
|
m_localVariables.count(&_varDecl),
|
||||||
"Unknown variable: " + _varDecl.name()
|
"Unknown variable: " + _varDecl.name()
|
||||||
);
|
);
|
||||||
return m_localVariables[&_varDecl];
|
return m_localVariables.at(&_varDecl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRGenerationContext::addStateVariable(
|
void IRGenerationContext::addStateVariable(
|
||||||
@ -98,23 +97,6 @@ string IRGenerationContext::newYulVariable()
|
|||||||
return "_" + to_string(++m_varCounter);
|
return "_" + to_string(++m_varCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerationContext::variable(Expression const& _expression)
|
|
||||||
{
|
|
||||||
unsigned size = _expression.annotation().type->sizeOnStack();
|
|
||||||
string var = "expr_" + to_string(_expression.id());
|
|
||||||
if (size == 1)
|
|
||||||
return var;
|
|
||||||
else
|
|
||||||
return suffixedVariableNameList(move(var) + "_", 1, 1 + size);
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRGenerationContext::variablePart(Expression const& _expression, string const& _part)
|
|
||||||
{
|
|
||||||
size_t numVars = _expression.annotation().type->sizeOnStack();
|
|
||||||
solAssert(numVars > 1, "");
|
|
||||||
return "expr_" + to_string(_expression.id()) + "_" + _part;
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
|
string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
|
||||||
{
|
{
|
||||||
string funName = "dispatch_internal_in_" + to_string(_in) + "_out_" + to_string(_out);
|
string funName = "dispatch_internal_in_" + to_string(_in) + "_out_" + to_string(_out);
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/codegen/ir/IRVariable.h>
|
||||||
#include <libsolidity/interface/OptimiserSettings.h>
|
#include <libsolidity/interface/OptimiserSettings.h>
|
||||||
#include <libsolidity/interface/DebugSettings.h>
|
#include <libsolidity/interface/DebugSettings.h>
|
||||||
|
|
||||||
@ -68,9 +69,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string addLocalVariable(VariableDeclaration const& _varDecl);
|
IRVariable const& addLocalVariable(VariableDeclaration const& _varDecl);
|
||||||
bool isLocalVariable(VariableDeclaration const& _varDecl) const { return m_localVariables.count(&_varDecl); }
|
bool isLocalVariable(VariableDeclaration const& _varDecl) const { return m_localVariables.count(&_varDecl); }
|
||||||
std::string localVariableName(VariableDeclaration const& _varDecl);
|
IRVariable const& localVariable(VariableDeclaration const& _varDecl);
|
||||||
|
|
||||||
void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset);
|
void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset);
|
||||||
bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); }
|
bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); }
|
||||||
@ -85,11 +86,6 @@ public:
|
|||||||
std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration);
|
std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration);
|
||||||
|
|
||||||
std::string newYulVariable();
|
std::string newYulVariable();
|
||||||
/// @returns the variable (or comma-separated list of variables) that contain
|
|
||||||
/// the value of the given expression.
|
|
||||||
std::string variable(Expression const& _expression);
|
|
||||||
/// @returns the sub-variable of a multi-variable expression.
|
|
||||||
std::string variablePart(Expression const& _expression, std::string const& _part);
|
|
||||||
|
|
||||||
std::string internalDispatch(size_t _in, size_t _out);
|
std::string internalDispatch(size_t _in, size_t _out);
|
||||||
|
|
||||||
@ -109,7 +105,7 @@ private:
|
|||||||
RevertStrings m_revertStrings;
|
RevertStrings m_revertStrings;
|
||||||
OptimiserSettings m_optimiserSettings;
|
OptimiserSettings m_optimiserSettings;
|
||||||
std::vector<ContractDefinition const*> m_inheritanceHierarchy;
|
std::vector<ContractDefinition const*> m_inheritanceHierarchy;
|
||||||
std::map<VariableDeclaration const*, std::string> m_localVariables;
|
std::map<VariableDeclaration const*, IRVariable> m_localVariables;
|
||||||
/// Storage offsets of state variables
|
/// Storage offsets of state variables
|
||||||
std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
||||||
std::shared_ptr<MultiUseYulFunctionCollector> m_functions;
|
std::shared_ptr<MultiUseYulFunctionCollector> m_functions;
|
||||||
|
@ -139,11 +139,11 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
|||||||
t("functionName", functionName);
|
t("functionName", functionName);
|
||||||
string params;
|
string params;
|
||||||
for (auto const& varDecl: _function.parameters())
|
for (auto const& varDecl: _function.parameters())
|
||||||
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl);
|
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
|
||||||
t("params", params);
|
t("params", params);
|
||||||
string retParams;
|
string retParams;
|
||||||
for (auto const& varDecl: _function.returnParameters())
|
for (auto const& varDecl: _function.returnParameters())
|
||||||
retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl);
|
retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
|
||||||
t("returns", retParams.empty() ? "" : " -> " + retParams);
|
t("returns", retParams.empty() ? "" : " -> " + retParams);
|
||||||
t("body", generate(_function.body()));
|
t("body", generate(_function.body()));
|
||||||
return t.render();
|
return t.render();
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <libsolidity/ast/ASTVisitor.h>
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
#include <libsolidity/codegen/ir/IRLValue.h>
|
#include <libsolidity/codegen/ir/IRLValue.h>
|
||||||
|
#include <libsolidity/codegen/ir/IRVariable.h>
|
||||||
|
|
||||||
namespace solidity::frontend
|
namespace solidity::frontend
|
||||||
{
|
{
|
||||||
@ -75,12 +76,25 @@ private:
|
|||||||
|
|
||||||
std::string fetchFreeMem() const;
|
std::string fetchFreeMem() const;
|
||||||
|
|
||||||
|
/// Generates the required conversion code and @returns an IRVariable referring to the value of @a _variable
|
||||||
|
/// converted to type @a _to.
|
||||||
|
IRVariable convert(IRVariable const& _variable, Type const& _to);
|
||||||
|
|
||||||
/// @returns a Yul expression representing the current value of @a _expression,
|
/// @returns a Yul expression representing the current value of @a _expression,
|
||||||
/// converted to type @a _to if it does not yet have that type.
|
/// converted to type @a _to if it does not yet have that type.
|
||||||
std::string expressionAsType(Expression const& _expression, Type const& _to);
|
std::string expressionAsType(Expression const& _expression, Type const& _to);
|
||||||
std::ostream& defineExpression(Expression const& _expression);
|
|
||||||
/// Defines only one of many variables corresponding to an expression.
|
/// @returns an output stream that can be used to define @a _var using a function call or
|
||||||
std::ostream& defineExpressionPart(Expression const& _expression, std::string const& _part);
|
/// single stack slot expression.
|
||||||
|
std::ostream& define(IRVariable const& _var);
|
||||||
|
/// Defines @a _var using the value of @a _value while performing type conversions, if required.
|
||||||
|
void define(IRVariable const& _var, IRVariable const& _value) { declareAssign(_var, _value, true); }
|
||||||
|
/// Assigns @a _var to the value of @a _value while performing type conversions, if required.
|
||||||
|
void assign(IRVariable const& _var, IRVariable const& _value) { declareAssign(_var, _value, false); }
|
||||||
|
/// Declares variable @a _var.
|
||||||
|
void declare(IRVariable const& _var);
|
||||||
|
|
||||||
|
void declareAssign(IRVariable const& _var, IRVariable const& _value, bool _define);
|
||||||
|
|
||||||
void appendAndOrOperatorCode(BinaryOperation const& _binOp);
|
void appendAndOrOperatorCode(BinaryOperation const& _binOp);
|
||||||
void appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr);
|
void appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr);
|
||||||
@ -93,7 +107,14 @@ private:
|
|||||||
std::string const& _right
|
std::string const& _right
|
||||||
);
|
);
|
||||||
|
|
||||||
void setLValue(Expression const& _expression, std::unique_ptr<IRLValue> _lvalue);
|
/// Assigns the value of @a _value to the lvalue @a _lvalue.
|
||||||
|
void writeToLValue(IRLValue const& _lvalue, IRVariable const& _value);
|
||||||
|
/// @returns a fresh IR variable containing the value of the lvalue @a _lvalue.
|
||||||
|
IRVariable readFromLValue(IRLValue const& _lvalue);
|
||||||
|
|
||||||
|
/// Stores the given @a _lvalue in m_currentLValue, if it will be written to (lValueRequested). Otherwise
|
||||||
|
/// defines the expression @a _expression by reading the value from @a _lvalue.
|
||||||
|
void setLValue(Expression const& _expression, IRLValue _lvalue);
|
||||||
void generateLoop(
|
void generateLoop(
|
||||||
Statement const& _body,
|
Statement const& _body,
|
||||||
Expression const* _conditionExpression,
|
Expression const* _conditionExpression,
|
||||||
@ -107,7 +128,7 @@ private:
|
|||||||
std::ostringstream m_code;
|
std::ostringstream m_code;
|
||||||
IRGenerationContext& m_context;
|
IRGenerationContext& m_context;
|
||||||
YulUtilFunctions& m_utils;
|
YulUtilFunctions& m_utils;
|
||||||
std::unique_ptr<IRLValue> m_currentLValue;
|
std::optional<IRLValue> m_currentLValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,228 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of solidity.
|
|
||||||
|
|
||||||
solidity is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
solidity is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Generator for code that handles LValues.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <libsolidity/codegen/ir/IRLValue.h>
|
|
||||||
|
|
||||||
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
|
||||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
|
||||||
#include <libsolidity/codegen/CompilerUtils.h>
|
|
||||||
#include <libsolidity/ast/AST.h>
|
|
||||||
|
|
||||||
#include <libsolutil/Whiskers.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace solidity;
|
|
||||||
using namespace solidity::frontend;
|
|
||||||
|
|
||||||
IRLocalVariable::IRLocalVariable(
|
|
||||||
IRGenerationContext& _context,
|
|
||||||
VariableDeclaration const& _varDecl
|
|
||||||
):
|
|
||||||
IRLValue(_context.utils(), _varDecl.annotation().type),
|
|
||||||
m_variableName(_context.localVariableName(_varDecl))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRLocalVariable::storeValue(string const& _value, Type const& _type) const
|
|
||||||
{
|
|
||||||
solAssert(_type == *m_type, "Storing different types - not necessarily a problem.");
|
|
||||||
return m_variableName + " := " + _value + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRLocalVariable::setToZero() const
|
|
||||||
{
|
|
||||||
return storeValue(m_utils.zeroValueFunction(*m_type) + "()", *m_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
IRStorageItem::IRStorageItem(
|
|
||||||
IRGenerationContext& _context,
|
|
||||||
VariableDeclaration const& _varDecl
|
|
||||||
):
|
|
||||||
IRStorageItem(
|
|
||||||
_context.utils(),
|
|
||||||
*_varDecl.annotation().type,
|
|
||||||
_context.storageLocationOfVariable(_varDecl)
|
|
||||||
)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
IRStorageItem::IRStorageItem(
|
|
||||||
YulUtilFunctions _utils,
|
|
||||||
Type const& _type,
|
|
||||||
std::pair<u256, unsigned> slot_offset
|
|
||||||
):
|
|
||||||
IRLValue(std::move(_utils), &_type),
|
|
||||||
m_slot(util::toCompactHexWithPrefix(slot_offset.first)),
|
|
||||||
m_offset(slot_offset.second)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IRStorageItem::IRStorageItem(
|
|
||||||
YulUtilFunctions _utils,
|
|
||||||
string _slot,
|
|
||||||
boost::variant<string, unsigned> _offset,
|
|
||||||
Type const& _type
|
|
||||||
):
|
|
||||||
IRLValue(std::move(_utils), &_type),
|
|
||||||
m_slot(std::move(_slot)),
|
|
||||||
m_offset(std::move(_offset))
|
|
||||||
{
|
|
||||||
solAssert(!m_offset.empty(), "");
|
|
||||||
solAssert(!m_slot.empty(), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRStorageItem::retrieveValue() const
|
|
||||||
{
|
|
||||||
if (!m_type->isValueType())
|
|
||||||
return m_slot;
|
|
||||||
solUnimplementedAssert(m_type->category() != Type::Category::Function, "");
|
|
||||||
if (m_offset.type() == typeid(string))
|
|
||||||
return
|
|
||||||
m_utils.readFromStorageDynamic(*m_type, false) +
|
|
||||||
"(" +
|
|
||||||
m_slot +
|
|
||||||
", " +
|
|
||||||
boost::get<string>(m_offset) +
|
|
||||||
")";
|
|
||||||
else if (m_offset.type() == typeid(unsigned))
|
|
||||||
return
|
|
||||||
m_utils.readFromStorage(*m_type, boost::get<unsigned>(m_offset), false) +
|
|
||||||
"(" +
|
|
||||||
m_slot +
|
|
||||||
")";
|
|
||||||
|
|
||||||
solAssert(false, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRStorageItem::storeValue(string const& _value, Type const& _sourceType) const
|
|
||||||
{
|
|
||||||
if (m_type->isValueType())
|
|
||||||
solAssert(_sourceType == *m_type, "Different type, but might not be an error.");
|
|
||||||
|
|
||||||
std::optional<unsigned> offset;
|
|
||||||
|
|
||||||
if (m_offset.type() == typeid(unsigned))
|
|
||||||
offset = get<unsigned>(m_offset);
|
|
||||||
|
|
||||||
return
|
|
||||||
m_utils.updateStorageValueFunction(*m_type, offset) +
|
|
||||||
"(" +
|
|
||||||
m_slot +
|
|
||||||
(m_offset.type() == typeid(string) ?
|
|
||||||
(", " + get<string>(m_offset)) :
|
|
||||||
""
|
|
||||||
) +
|
|
||||||
", " +
|
|
||||||
_value +
|
|
||||||
")\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRStorageItem::setToZero() const
|
|
||||||
{
|
|
||||||
return
|
|
||||||
m_utils.storageSetToZeroFunction(*m_type) +
|
|
||||||
"(" +
|
|
||||||
m_slot +
|
|
||||||
", " +
|
|
||||||
(
|
|
||||||
m_offset.type() == typeid(unsigned) ?
|
|
||||||
to_string(get<unsigned>(m_offset)) :
|
|
||||||
get<string>(m_offset)
|
|
||||||
) +
|
|
||||||
")\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
IRMemoryItem::IRMemoryItem(
|
|
||||||
YulUtilFunctions _utils,
|
|
||||||
std::string _address,
|
|
||||||
bool _byteArrayElement,
|
|
||||||
Type const& _type
|
|
||||||
):
|
|
||||||
IRLValue(std::move(_utils), &_type),
|
|
||||||
m_address(move(_address)),
|
|
||||||
m_byteArrayElement(_byteArrayElement)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
string IRMemoryItem::retrieveValue() const
|
|
||||||
{
|
|
||||||
if (m_byteArrayElement)
|
|
||||||
return m_utils.cleanupFunction(*m_type) +
|
|
||||||
"(mload(" +
|
|
||||||
m_address +
|
|
||||||
"))";
|
|
||||||
|
|
||||||
if (m_type->isValueType())
|
|
||||||
return m_utils.readFromMemory(*m_type) +
|
|
||||||
"(" +
|
|
||||||
m_address +
|
|
||||||
")";
|
|
||||||
else
|
|
||||||
return "mload(" + m_address + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRMemoryItem::storeValue(string const& _value, Type const& _type) const
|
|
||||||
{
|
|
||||||
if (!m_type->isValueType())
|
|
||||||
{
|
|
||||||
solUnimplementedAssert(_type == *m_type, "Conversion not implemented for assignment to memory.");
|
|
||||||
|
|
||||||
solAssert(m_type->sizeOnStack() == 1, "");
|
|
||||||
solAssert(dynamic_cast<ReferenceType const*>(m_type), "");
|
|
||||||
|
|
||||||
return "mstore(" + m_address + ", " + _value + ")\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
solAssert(_type.isValueType(), "");
|
|
||||||
|
|
||||||
string prepared = _value;
|
|
||||||
|
|
||||||
// Exists to see if this case ever happens
|
|
||||||
solAssert(_type == *m_type, "");
|
|
||||||
|
|
||||||
if (_type != *m_type)
|
|
||||||
prepared =
|
|
||||||
m_utils.conversionFunction(_type, *m_type) +
|
|
||||||
"(" +
|
|
||||||
_value +
|
|
||||||
")";
|
|
||||||
else
|
|
||||||
prepared =
|
|
||||||
m_utils.cleanupFunction(*m_type) +
|
|
||||||
"(" +
|
|
||||||
_value +
|
|
||||||
")";
|
|
||||||
|
|
||||||
if (m_byteArrayElement)
|
|
||||||
{
|
|
||||||
solAssert(*m_type == *TypeProvider::byte(), "");
|
|
||||||
return "mstore8(" + m_address + ", byte(0, " + prepared + "))\n";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return m_utils.writeToMemoryFunction(*m_type) +
|
|
||||||
"(" +
|
|
||||||
m_address +
|
|
||||||
", " +
|
|
||||||
prepared +
|
|
||||||
")\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
string IRMemoryItem::setToZero() const
|
|
||||||
{
|
|
||||||
return storeValue(m_utils.zeroValueFunction(*m_type) + "()", *m_type);
|
|
||||||
}
|
|
@ -15,115 +15,51 @@
|
|||||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Generator for code that handles LValues.
|
* Classes that store locations of lvalues.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
#include <libsolidity/codegen/ir/IRVariable.h>
|
||||||
|
#include <variant>
|
||||||
#include <libsolutil/Common.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <ostream>
|
|
||||||
#include <boost/variant.hpp>
|
|
||||||
|
|
||||||
namespace solidity::frontend
|
namespace solidity::frontend
|
||||||
{
|
{
|
||||||
|
|
||||||
class VariableDeclaration;
|
|
||||||
class IRGenerationContext;
|
|
||||||
class Type;
|
class Type;
|
||||||
class ArrayType;
|
|
||||||
|
|
||||||
/**
|
struct IRLValue
|
||||||
* Abstract class used to retrieve, delete and store data in LValues.
|
|
||||||
*/
|
|
||||||
class IRLValue
|
|
||||||
{
|
{
|
||||||
protected:
|
Type const& type;
|
||||||
explicit IRLValue(YulUtilFunctions _utils, Type const* _type = nullptr):
|
struct Stack
|
||||||
m_utils(std::move(_utils)),
|
{
|
||||||
m_type(_type)
|
IRVariable variable;
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~IRLValue() = default;
|
|
||||||
/// @returns an expression to retrieve the value of the lvalue.
|
|
||||||
virtual std::string retrieveValue() const = 0;
|
|
||||||
/// Returns code that stores the value of @a _value (should be an identifier)
|
|
||||||
/// of type @a _type in the lvalue. Might perform type conversion.
|
|
||||||
virtual std::string storeValue(std::string const& _value, Type const& _type) const = 0;
|
|
||||||
|
|
||||||
/// Returns code that will reset the stored value to zero
|
|
||||||
virtual std::string setToZero() const = 0;
|
|
||||||
protected:
|
|
||||||
YulUtilFunctions mutable m_utils;
|
|
||||||
Type const* m_type;
|
|
||||||
};
|
};
|
||||||
|
struct Storage
|
||||||
class IRLocalVariable: public IRLValue
|
|
||||||
{
|
{
|
||||||
public:
|
std::string const slot;
|
||||||
IRLocalVariable(
|
|
||||||
IRGenerationContext& _context,
|
|
||||||
VariableDeclaration const& _varDecl
|
|
||||||
);
|
|
||||||
std::string retrieveValue() const override { return m_variableName; }
|
|
||||||
std::string storeValue(std::string const& _value, Type const& _type) const override;
|
|
||||||
|
|
||||||
std::string setToZero() const override;
|
|
||||||
private:
|
|
||||||
std::string m_variableName;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IRStorageItem: public IRLValue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
IRStorageItem(
|
|
||||||
IRGenerationContext& _context,
|
|
||||||
VariableDeclaration const& _varDecl
|
|
||||||
);
|
|
||||||
IRStorageItem(
|
|
||||||
YulUtilFunctions _utils,
|
|
||||||
std::string _slot,
|
|
||||||
boost::variant<std::string, unsigned> _offset,
|
|
||||||
Type const& _type
|
|
||||||
);
|
|
||||||
std::string retrieveValue() const override;
|
|
||||||
std::string storeValue(std::string const& _value, Type const& _type) const override;
|
|
||||||
|
|
||||||
std::string setToZero() const override;
|
|
||||||
private:
|
|
||||||
IRStorageItem(
|
|
||||||
YulUtilFunctions _utils,
|
|
||||||
Type const& _type,
|
|
||||||
std::pair<u256, unsigned> slot_offset
|
|
||||||
);
|
|
||||||
|
|
||||||
std::string const m_slot;
|
|
||||||
/// unsigned: Used when the offset is known at compile time, uses optimized
|
/// unsigned: Used when the offset is known at compile time, uses optimized
|
||||||
/// functions
|
/// functions
|
||||||
/// string: Used when the offset is determined at run time
|
/// string: Used when the offset is determined at run time
|
||||||
boost::variant<std::string, unsigned> const m_offset;
|
std::variant<std::string, unsigned> const offset;
|
||||||
};
|
std::string offsetString() const
|
||||||
|
|
||||||
class IRMemoryItem: public IRLValue
|
|
||||||
{
|
{
|
||||||
public:
|
if (std::holds_alternative<unsigned>(offset))
|
||||||
IRMemoryItem(
|
return std::to_string(std::get<unsigned>(offset));
|
||||||
YulUtilFunctions _utils,
|
else
|
||||||
std::string _address,
|
return std::get<std::string>(offset);
|
||||||
bool _byteArrayElement,
|
}
|
||||||
Type const& _type
|
};
|
||||||
);
|
struct Memory
|
||||||
std::string retrieveValue() const override;
|
{
|
||||||
std::string storeValue(std::string const& _value, Type const& _type) const override;
|
std::string const address;
|
||||||
|
bool byteArrayElement = false;
|
||||||
std::string setToZero() const override;
|
};
|
||||||
private:
|
struct Tuple
|
||||||
std::string const m_address;
|
{
|
||||||
bool m_byteArrayElement;
|
std::vector<std::optional<IRLValue>> components;
|
||||||
|
};
|
||||||
|
std::variant<Stack, Storage, Memory, Tuple> kind;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
111
libsolidity/codegen/ir/IRVariable.cpp
Normal file
111
libsolidity/codegen/ir/IRVariable.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <libsolidity/codegen/ir/IRVariable.h>
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
|
#include <boost/range/adaptor/transformed.hpp>
|
||||||
|
#include <libsolutil/StringUtils.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::frontend;
|
||||||
|
using namespace solidity::util;
|
||||||
|
|
||||||
|
IRVariable::IRVariable(std::string _baseName, Type const& _type):
|
||||||
|
m_baseName(std::move(_baseName)), m_type(_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IRVariable::IRVariable(VariableDeclaration const& _declaration):
|
||||||
|
IRVariable(
|
||||||
|
"vloc_" + _declaration.name() + '_' + std::to_string(_declaration.id()),
|
||||||
|
*_declaration.annotation().type
|
||||||
|
)
|
||||||
|
{
|
||||||
|
solAssert(!_declaration.isStateVariable(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
IRVariable::IRVariable(Expression const& _expression):
|
||||||
|
IRVariable(
|
||||||
|
"expr_" + to_string(_expression.id()),
|
||||||
|
*_expression.annotation().type
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
IRVariable IRVariable::part(string const& _name) const
|
||||||
|
{
|
||||||
|
for (auto const& [itemName, itemType]: m_type.stackItems())
|
||||||
|
if (itemName == _name)
|
||||||
|
{
|
||||||
|
solAssert(itemName.empty() || itemType, "");
|
||||||
|
return IRVariable{suffixedName(itemName), itemType ? *itemType : m_type};
|
||||||
|
}
|
||||||
|
solAssert(false, "Invalid stack item name.");
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string> IRVariable::stackSlots() const
|
||||||
|
{
|
||||||
|
vector<string> result;
|
||||||
|
for (auto const& [itemName, itemType]: m_type.stackItems())
|
||||||
|
if (itemType)
|
||||||
|
{
|
||||||
|
solAssert(!itemName.empty(), "");
|
||||||
|
solAssert(m_type != *itemType, "");
|
||||||
|
result += IRVariable{suffixedName(itemName), *itemType}.stackSlots();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(itemName.empty(), "");
|
||||||
|
result.emplace_back(m_baseName);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRVariable::commaSeparatedList() const
|
||||||
|
{
|
||||||
|
return joinHumanReadable(stackSlots());
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRVariable::commaSeparatedListPrefixed() const
|
||||||
|
{
|
||||||
|
return joinHumanReadablePrefixed(stackSlots());
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRVariable::name() const
|
||||||
|
{
|
||||||
|
solAssert(m_type.sizeOnStack() == 1, "");
|
||||||
|
auto const& [itemName, type] = m_type.stackItems().front();
|
||||||
|
solAssert(!type, "");
|
||||||
|
return suffixedName(itemName);
|
||||||
|
}
|
||||||
|
|
||||||
|
IRVariable IRVariable::tupleComponent(size_t _i) const
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
m_type.category() == Type::Category::Tuple,
|
||||||
|
"Requested tuple component of non-tuple IR variable."
|
||||||
|
);
|
||||||
|
return part("component_" + std::to_string(_i + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRVariable::suffixedName(string const& _suffix) const
|
||||||
|
{
|
||||||
|
if (_suffix.empty())
|
||||||
|
return m_baseName;
|
||||||
|
else
|
||||||
|
return m_baseName + '_' + _suffix;
|
||||||
|
}
|
85
libsolidity/codegen/ir/IRVariable.h
Normal file
85
libsolidity/codegen/ir/IRVariable.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace solidity::frontend
|
||||||
|
{
|
||||||
|
|
||||||
|
class VariableDeclaration;
|
||||||
|
class Type;
|
||||||
|
class Expression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An IRVariable refers to a set of yul variables that correspond to the stack layout of a Solidity variable or expression
|
||||||
|
* of a specific S
|
||||||
|
* olidity type. If the Solidity type occupies a single stack slot, the IRVariable refers to a single yul variable.
|
||||||
|
* Otherwise the set of yul variables it refers to is (recursively) determined by @see ``Type::stackItems()``.
|
||||||
|
* For example, an IRVariable referring to a dynamically sized calldata array will consist of two parts named
|
||||||
|
* ``offset`` and ``length``, whereas an IRVariable referring to a statically sized calldata type, a storage reference
|
||||||
|
* type or a memory reference type will contain a single unnamed part containing an offset. An IRVariable referring to
|
||||||
|
* a value type will contain a single unnamed part containing the value, an IRVariable referring to a tuple will
|
||||||
|
* have the typed tuple components as parts.
|
||||||
|
*/
|
||||||
|
class IRVariable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// IR variable with explicit base name @a _baseName and type @a _type.
|
||||||
|
IRVariable(std::string _baseName, Type const& _type);
|
||||||
|
/// IR variable referring to the declaration @a _decl.
|
||||||
|
explicit IRVariable(VariableDeclaration const& _decl);
|
||||||
|
/// IR variable referring to the expression @a _expr.
|
||||||
|
/// Intentionally not defined as explicit to allow defining IRVariables for expressions directly via implicit conversions.
|
||||||
|
IRVariable(Expression const& _expression);
|
||||||
|
|
||||||
|
/// @returns the name of the variable, if it occupies a single stack slot (otherwise throws).
|
||||||
|
std::string name() const;
|
||||||
|
|
||||||
|
/// @returns a comma-separated list of the stack slots of the variable.
|
||||||
|
std::string commaSeparatedList() const;
|
||||||
|
|
||||||
|
/// @returns a comma-separated list of the stack slots of the variable that is
|
||||||
|
/// prefixed with a comma, unless it is empty.
|
||||||
|
std::string commaSeparatedListPrefixed() const;
|
||||||
|
|
||||||
|
/// @returns an IRVariable referring to the tuple component @a _i of a tuple variable.
|
||||||
|
IRVariable tupleComponent(std::size_t _i) const;
|
||||||
|
|
||||||
|
/// @returns the type of the variable.
|
||||||
|
Type const& type() const { return m_type; }
|
||||||
|
|
||||||
|
/// @returns an IRVariable referring to the stack component @a _slot of the variable.
|
||||||
|
/// @a _slot must be among the stack slots in ``m_type.stackItems()``.
|
||||||
|
/// The returned IRVariable is itself typed with the type of the stack slot as defined
|
||||||
|
/// in ``m_type.stackItems()`` and may again occupy multiple stack slots.
|
||||||
|
IRVariable part(std::string const& _slot) const;
|
||||||
|
private:
|
||||||
|
/// @returns a vector containing the names of the stack slots of the variable.
|
||||||
|
std::vector<std::string> stackSlots() const;
|
||||||
|
|
||||||
|
/// @returns a name consisting of the base name appended with an underscore and @æ _suffix,
|
||||||
|
/// unless @a _suffix is empty, in which case the base name itself is returned.
|
||||||
|
std::string suffixedName(std::string const& _suffix) const;
|
||||||
|
std::string m_baseName;
|
||||||
|
Type const& m_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -34,8 +34,8 @@ object \"C_10\" {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fun_f_9() -> vloc__4 {
|
function fun_f_9() -> vloc__4_mpos {
|
||||||
vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr()
|
vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr()
|
||||||
leave
|
leave
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -130,8 +130,8 @@ object \"C_10\" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fun_f_9() -> vloc__4 {
|
function fun_f_9() -> vloc__4_mpos {
|
||||||
vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr()
|
vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr()
|
||||||
leave
|
leave
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,8 +38,8 @@ object \"C_10\" {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fun_f_9() -> vloc__4 {
|
function fun_f_9() -> vloc__4_mpos {
|
||||||
vloc__4 := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr()
|
vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr()
|
||||||
leave
|
leave
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -138,8 +138,8 @@ object \"C_10\" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fun_f_9() -> vloc__4 {
|
function fun_f_9() -> vloc__4_mpos {
|
||||||
vloc__4 := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr()
|
vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr()
|
||||||
leave
|
leave
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure returns (uint, uint, uint) {
|
||||||
|
return (1, 2, 3);
|
||||||
|
}
|
||||||
|
function g() public pure returns (uint a, uint b, uint c) {
|
||||||
|
(c, b, a) = f();
|
||||||
|
}
|
||||||
|
function h() public pure returns (uint a) {
|
||||||
|
(,,a) = f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// g() -> 3, 2, 1
|
||||||
|
// h() -> 3
|
@ -0,0 +1,18 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure returns (uint, uint, uint) {
|
||||||
|
return (1, 2, 3);
|
||||||
|
}
|
||||||
|
function g() public pure returns (uint x, uint y, uint z) {
|
||||||
|
(uint c, uint b, uint a) = f();
|
||||||
|
(x, y, z) = (a, b, c);
|
||||||
|
}
|
||||||
|
function h() public pure returns (uint) {
|
||||||
|
(,,uint a) = f();
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// g() -> 3, 2, 1
|
||||||
|
// h() -> 3
|
@ -0,0 +1,32 @@
|
|||||||
|
contract C {
|
||||||
|
uint public x = 17;
|
||||||
|
function f(uint a1, uint a2) public returns (uint r1, uint r2) {
|
||||||
|
(uint b1, uint b2) = (a1, a2);
|
||||||
|
(r1, x, r2) = (b1, b2, b2);
|
||||||
|
}
|
||||||
|
function g() public returns (uint a, uint b, uint c) {
|
||||||
|
uint256[3] memory m;
|
||||||
|
(m[0], m[1], m[2]) = (1, x, 3);
|
||||||
|
return (m[2], m[1], m[0]);
|
||||||
|
}
|
||||||
|
function h() public returns (uint a, uint b, uint c) {
|
||||||
|
uint256[3] memory m;
|
||||||
|
(m[0], m[1], , m[2], m[0]) = (1, x, 3, 4, 42);
|
||||||
|
return (m[2], m[1], m[0]);
|
||||||
|
}
|
||||||
|
function i() public returns (uint a, uint b, uint c, uint d) {
|
||||||
|
(a) = 42;
|
||||||
|
(((((b))))) = 23;
|
||||||
|
c = (((17)));
|
||||||
|
(((d))) = (13);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// x() -> 17
|
||||||
|
// f(uint256,uint256): 23, 42 -> 23, 42
|
||||||
|
// x() -> 42
|
||||||
|
// g() -> 3, 42, 1
|
||||||
|
// h() -> 4, 42, 1
|
||||||
|
// i() -> 42, 23, 17, 13
|
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure returns (uint) {
|
||||||
|
uint x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0
|
@ -0,0 +1,14 @@
|
|||||||
|
contract C {
|
||||||
|
uint256 x;
|
||||||
|
uint256 y;
|
||||||
|
function set(uint256 v) public returns (uint256) { x = v; return v; }
|
||||||
|
function f() public returns (uint256, uint256) {
|
||||||
|
(y, y, y) = (set(1), set(2), set(3));
|
||||||
|
assert(y == 1 && x == 3);
|
||||||
|
return (x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 3, 1
|
Loading…
Reference in New Issue
Block a user