Use IRVariable's in IR code generation and implement tuples.

This commit is contained in:
Daniel Kirchner
2020-02-12 12:36:14 +01:00
parent 6abe0a50b1
commit 3c9f18b749
20 changed files with 855 additions and 649 deletions
+7 -25
View File
@@ -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);
+4 -8
View File
@@ -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;
+2 -2
View File
@@ -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;
};
}
-228
View File
@@ -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);
}
+35 -99
View File
@@ -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
View 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
View 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;
};
}