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/IRGenerationContext.cpp
|
||||
codegen/ir/IRGenerationContext.h
|
||||
codegen/ir/IRLValue.cpp
|
||||
codegen/ir/IRLValue.h
|
||||
codegen/ir/IRVariable.cpp
|
||||
codegen/ir/IRVariable.h
|
||||
formal/BMC.cpp
|
||||
formal/BMC.h
|
||||
formal/CHC.cpp
|
||||
|
@ -151,7 +151,7 @@ util::Result<TypePointers> transformParametersToExternal(TypePointers const& _pa
|
||||
void Type::clearCache() const
|
||||
{
|
||||
m_members.clear();
|
||||
m_stackSlots.reset();
|
||||
m_stackItems.reset();
|
||||
m_stackSize.reset();
|
||||
}
|
||||
|
||||
@ -1703,14 +1703,22 @@ u256 ArrayType::storageSize() const
|
||||
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())
|
||||
return {std::make_tuple("offset", TypeProvider::uint256()), std::make_tuple("length", TypeProvider::uint256())};
|
||||
else
|
||||
// storage slot or memory offset
|
||||
// byte offset inside storage value is omitted
|
||||
return {std::make_tuple(string(), nullptr)};
|
||||
switch (m_location)
|
||||
{
|
||||
case DataLocation::CallData:
|
||||
if (isDynamicallySized())
|
||||
return {std::make_tuple("offset", TypeProvider::uint256()), std::make_tuple("length", TypeProvider::uint256())};
|
||||
else
|
||||
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
|
||||
return {std::make_tuple("slot", TypeProvider::uint256())};
|
||||
}
|
||||
solAssert(false, "");
|
||||
}
|
||||
|
||||
string ArrayType::toString(bool _short) const
|
||||
@ -1892,7 +1900,7 @@ string ArraySliceType::toString(bool _short) const
|
||||
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()}};
|
||||
}
|
||||
@ -1995,12 +2003,12 @@ vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVar
|
||||
return variablesAndOffsets;
|
||||
}
|
||||
|
||||
vector<tuple<string, TypePointer>> ContractType::makeStackSlots() const
|
||||
vector<tuple<string, TypePointer>> ContractType::makeStackItems() const
|
||||
{
|
||||
if (m_super)
|
||||
return {};
|
||||
else
|
||||
return {make_tuple("address", isPayable() ? TypeProvider::payableAddress() :TypeProvider::address())};
|
||||
return {make_tuple("address", isPayable() ? TypeProvider::payableAddress() : TypeProvider::address())};
|
||||
}
|
||||
|
||||
void StructType::clearCache() const
|
||||
@ -2436,7 +2444,7 @@ u256 TupleType::storageSize() const
|
||||
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;
|
||||
unsigned i = 1;
|
||||
@ -2902,7 +2910,7 @@ unsigned FunctionType::storageBytes() const
|
||||
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;
|
||||
Kind kind = m_kind;
|
||||
@ -2927,12 +2935,12 @@ vector<tuple<string, TypePointer>> FunctionType::makeStackSlots() const
|
||||
slots = {make_tuple("address", TypeProvider::address())};
|
||||
break;
|
||||
case Kind::Internal:
|
||||
slots = {make_tuple(string(), nullptr)};
|
||||
slots = {make_tuple("functionIdentifier", TypeProvider::uint256())};
|
||||
break;
|
||||
case Kind::ArrayPush:
|
||||
case Kind::ArrayPop:
|
||||
case Kind::ByteArrayPush:
|
||||
slots = {make_tuple(string(), nullptr)};
|
||||
slots = {make_tuple("slot", TypeProvider::uint256())};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -2945,7 +2953,7 @@ vector<tuple<string, TypePointer>> FunctionType::makeStackSlots() const
|
||||
if (m_saltSet)
|
||||
slots.emplace_back("salt", TypeProvider::uint256());
|
||||
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);
|
||||
return slots;
|
||||
}
|
||||
@ -3441,7 +3449,7 @@ u256 TypeType::storageSize() const
|
||||
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 (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,
|
||||
/// i.e. it behaves differently in lvalue context and in value context.
|
||||
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)
|
||||
m_stackSlots = makeStackSlots();
|
||||
return *m_stackSlots;
|
||||
if (!m_stackItems)
|
||||
m_stackItems = makeStackItems();
|
||||
return *m_stackItems;
|
||||
}
|
||||
/// Total number of stack slots occupied by this type. This is the sum of ``sizeOnStack`` of all ``stackItems()``.
|
||||
unsigned sizeOnStack() const
|
||||
{
|
||||
if (!m_stackSize)
|
||||
{
|
||||
size_t sizeOnStack = 0;
|
||||
for (auto const& slot: stackSlots())
|
||||
for (auto const& slot: stackItems())
|
||||
if (std::get<1>(slot))
|
||||
sizeOnStack += std::get<1>(slot)->sizeOnStack();
|
||||
else
|
||||
@ -355,7 +362,9 @@ protected:
|
||||
{
|
||||
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)};
|
||||
}
|
||||
@ -363,7 +372,7 @@ protected:
|
||||
|
||||
/// List of member types (parameterised by scape), will be lazy-initialized.
|
||||
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;
|
||||
};
|
||||
|
||||
@ -597,7 +606,7 @@ public:
|
||||
std::string const& value() const { return m_value; }
|
||||
|
||||
protected:
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override { return {}; }
|
||||
private:
|
||||
std::string m_value;
|
||||
};
|
||||
@ -783,7 +792,7 @@ public:
|
||||
void clearCache() const override;
|
||||
|
||||
protected:
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override;
|
||||
private:
|
||||
/// String is interpreted as a subtype of Bytes.
|
||||
enum class ArrayKind { Ordinary, Bytes, String };
|
||||
@ -824,7 +833,7 @@ public:
|
||||
std::unique_ptr<ReferenceType> copyForLocation(DataLocation, bool) const override { solAssert(false, ""); }
|
||||
|
||||
protected:
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override;
|
||||
private:
|
||||
ArrayType const& m_arrayType;
|
||||
};
|
||||
@ -885,7 +894,7 @@ public:
|
||||
/// offsets in storage.
|
||||
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const;
|
||||
protected:
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override;
|
||||
private:
|
||||
ContractDefinition const& m_contract;
|
||||
/// 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; }
|
||||
|
||||
protected:
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override;
|
||||
private:
|
||||
std::vector<TypePointer> const m_components;
|
||||
};
|
||||
@ -1282,7 +1291,7 @@ public:
|
||||
FunctionTypePointer asCallableFunction(bool _inLibrary, bool _bound = false) const;
|
||||
|
||||
protected:
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override;
|
||||
private:
|
||||
static TypePointers parseElementaryTypeVector(strings const& _types);
|
||||
|
||||
@ -1358,7 +1367,7 @@ public:
|
||||
|
||||
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||
protected:
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override;
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override;
|
||||
private:
|
||||
TypePointer m_actualType;
|
||||
};
|
||||
@ -1384,7 +1393,7 @@ public:
|
||||
std::string toString(bool _short) const override;
|
||||
|
||||
protected:
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override { return {}; }
|
||||
private:
|
||||
TypePointers m_parameterTypes;
|
||||
};
|
||||
@ -1412,7 +1421,7 @@ public:
|
||||
std::string toString(bool _short) const override;
|
||||
|
||||
protected:
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override { return {}; }
|
||||
private:
|
||||
SourceUnit const& m_sourceUnit;
|
||||
};
|
||||
@ -1456,7 +1465,7 @@ public:
|
||||
TypePointer typeArgument() const;
|
||||
|
||||
protected:
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackSlots() const override { return {}; }
|
||||
std::vector<std::tuple<std::string, TypePointer>> makeStackItems() const override { return {}; }
|
||||
private:
|
||||
Kind m_kind;
|
||||
/// Contract type used for contract metadata magic.
|
||||
|
@ -1791,6 +1791,44 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const
|
||||
"_to_" +
|
||||
_to.identifier();
|
||||
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(
|
||||
_from.category() == Type::Category::StringLiteral,
|
||||
"Type conversion " + _from.toString() + " -> " + _to.toString() + " not yet implemented."
|
||||
|
@ -31,23 +31,22 @@ using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::frontend;
|
||||
|
||||
string IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl)
|
||||
IRVariable const& IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
solUnimplementedAssert(
|
||||
_varDecl.annotation().type->sizeOnStack() == 1,
|
||||
"Multi-slot types not yet implemented."
|
||||
auto const& [it, didInsert] = m_localVariables.emplace(
|
||||
std::make_pair(&_varDecl, IRVariable{_varDecl})
|
||||
);
|
||||
|
||||
return m_localVariables[&_varDecl] = "vloc_" + _varDecl.name() + "_" + to_string(_varDecl.id());
|
||||
solAssert(didInsert, "Local variable added multiple times.");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
string IRGenerationContext::localVariableName(VariableDeclaration const& _varDecl)
|
||||
IRVariable const& IRGenerationContext::localVariable(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
solAssert(
|
||||
m_localVariables.count(&_varDecl),
|
||||
"Unknown variable: " + _varDecl.name()
|
||||
);
|
||||
return m_localVariables[&_varDecl];
|
||||
return m_localVariables.at(&_varDecl);
|
||||
}
|
||||
|
||||
void IRGenerationContext::addStateVariable(
|
||||
@ -98,23 +97,6 @@ string IRGenerationContext::newYulVariable()
|
||||
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 funName = "dispatch_internal_in_" + to_string(_in) + "_out_" + to_string(_out);
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/codegen/ir/IRVariable.h>
|
||||
#include <libsolidity/interface/OptimiserSettings.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); }
|
||||
std::string localVariableName(VariableDeclaration const& _varDecl);
|
||||
IRVariable const& localVariable(VariableDeclaration const& _varDecl);
|
||||
|
||||
void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset);
|
||||
bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); }
|
||||
@ -85,11 +86,6 @@ public:
|
||||
std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration);
|
||||
|
||||
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);
|
||||
|
||||
@ -109,7 +105,7 @@ private:
|
||||
RevertStrings m_revertStrings;
|
||||
OptimiserSettings m_optimiserSettings;
|
||||
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
|
||||
std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
||||
std::shared_ptr<MultiUseYulFunctionCollector> m_functions;
|
||||
|
@ -139,11 +139,11 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
||||
t("functionName", functionName);
|
||||
string params;
|
||||
for (auto const& varDecl: _function.parameters())
|
||||
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl);
|
||||
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
|
||||
t("params", params);
|
||||
string retParams;
|
||||
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("body", generate(_function.body()));
|
||||
return t.render();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,7 @@
|
||||
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <libsolidity/codegen/ir/IRLValue.h>
|
||||
#include <libsolidity/codegen/ir/IRVariable.h>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
@ -75,12 +76,25 @@ private:
|
||||
|
||||
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,
|
||||
/// converted to type @a _to if it does not yet have that type.
|
||||
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.
|
||||
std::ostream& defineExpressionPart(Expression const& _expression, std::string const& _part);
|
||||
|
||||
/// @returns an output stream that can be used to define @a _var using a function call or
|
||||
/// 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 appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr);
|
||||
@ -93,7 +107,14 @@ private:
|
||||
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(
|
||||
Statement const& _body,
|
||||
Expression const* _conditionExpression,
|
||||
@ -107,7 +128,7 @@ private:
|
||||
std::ostringstream m_code;
|
||||
IRGenerationContext& m_context;
|
||||
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/>.
|
||||
*/
|
||||
/**
|
||||
* Generator for code that handles LValues.
|
||||
* Classes that store locations of lvalues.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||
|
||||
#include <libsolutil/Common.h>
|
||||
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <boost/variant.hpp>
|
||||
#include <libsolidity/codegen/ir/IRVariable.h>
|
||||
#include <variant>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
class VariableDeclaration;
|
||||
class IRGenerationContext;
|
||||
class Type;
|
||||
class ArrayType;
|
||||
|
||||
/**
|
||||
* Abstract class used to retrieve, delete and store data in LValues.
|
||||
*/
|
||||
class IRLValue
|
||||
struct IRLValue
|
||||
{
|
||||
protected:
|
||||
explicit IRLValue(YulUtilFunctions _utils, Type const* _type = nullptr):
|
||||
m_utils(std::move(_utils)),
|
||||
m_type(_type)
|
||||
{}
|
||||
|
||||
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;
|
||||
Type const& type;
|
||||
struct Stack
|
||||
{
|
||||
IRVariable variable;
|
||||
};
|
||||
struct Storage
|
||||
{
|
||||
std::string const slot;
|
||||
/// unsigned: Used when the offset is known at compile time, uses optimized
|
||||
/// functions
|
||||
/// string: Used when the offset is determined at run time
|
||||
std::variant<std::string, unsigned> const offset;
|
||||
std::string offsetString() const
|
||||
{
|
||||
if (std::holds_alternative<unsigned>(offset))
|
||||
return std::to_string(std::get<unsigned>(offset));
|
||||
else
|
||||
return std::get<std::string>(offset);
|
||||
}
|
||||
};
|
||||
struct Memory
|
||||
{
|
||||
std::string const address;
|
||||
bool byteArrayElement = false;
|
||||
};
|
||||
struct Tuple
|
||||
{
|
||||
std::vector<std::optional<IRLValue>> components;
|
||||
};
|
||||
std::variant<Stack, Storage, Memory, Tuple> kind;
|
||||
};
|
||||
|
||||
class IRLocalVariable: public IRLValue
|
||||
{
|
||||
public:
|
||||
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
|
||||
/// functions
|
||||
/// string: Used when the offset is determined at run time
|
||||
boost::variant<std::string, unsigned> const m_offset;
|
||||
};
|
||||
|
||||
class IRMemoryItem: public IRLValue
|
||||
{
|
||||
public:
|
||||
IRMemoryItem(
|
||||
YulUtilFunctions _utils,
|
||||
std::string _address,
|
||||
bool _byteArrayElement,
|
||||
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:
|
||||
std::string const m_address;
|
||||
bool m_byteArrayElement;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
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 {
|
||||
vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr()
|
||||
function fun_f_9() -> vloc__4_mpos {
|
||||
vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr()
|
||||
leave
|
||||
|
||||
}
|
||||
@ -130,8 +130,8 @@ object \"C_10\" {
|
||||
}
|
||||
}
|
||||
|
||||
function fun_f_9() -> vloc__4 {
|
||||
vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr()
|
||||
function fun_f_9() -> vloc__4_mpos {
|
||||
vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr()
|
||||
leave
|
||||
|
||||
}
|
||||
|
@ -38,8 +38,8 @@ object \"C_10\" {
|
||||
|
||||
}
|
||||
|
||||
function fun_f_9() -> vloc__4 {
|
||||
vloc__4 := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr()
|
||||
function fun_f_9() -> vloc__4_mpos {
|
||||
vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr()
|
||||
leave
|
||||
|
||||
}
|
||||
@ -138,8 +138,8 @@ object \"C_10\" {
|
||||
}
|
||||
}
|
||||
|
||||
function fun_f_9() -> vloc__4 {
|
||||
vloc__4 := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr()
|
||||
function fun_f_9() -> vloc__4_mpos {
|
||||
vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr()
|
||||
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