mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
LValues and state variables of value type.
This commit is contained in:
parent
dcca6f6318
commit
aa4d4afcdc
@ -73,6 +73,8 @@ 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
|
||||||
formal/EncodingContext.cpp
|
formal/EncodingContext.cpp
|
||||||
formal/EncodingContext.h
|
formal/EncodingContext.h
|
||||||
formal/SMTChecker.cpp
|
formal/SMTChecker.cpp
|
||||||
|
@ -237,6 +237,30 @@ string YulUtilFunctions::shiftRightFunction(size_t _numBits)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
|
||||||
|
{
|
||||||
|
solAssert(_numBytes <= 32, "");
|
||||||
|
solAssert(_shiftBytes <= 32, "");
|
||||||
|
size_t numBits = _numBytes * 8;
|
||||||
|
size_t shiftBits = _shiftBytes * 8;
|
||||||
|
string functionName = "update_byte_slice_" + to_string(_numBytes) + "_shift_" + to_string(_shiftBytes);
|
||||||
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
return
|
||||||
|
Whiskers(R"(
|
||||||
|
function <functionName>(value, toInsert) -> result {
|
||||||
|
let mask := <mask>
|
||||||
|
toInsert := <shl>(toInsert)
|
||||||
|
value := and(value, not(mask))
|
||||||
|
result := or(value, and(toInsert, mask))
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("mask", formatNumber(((bigint(1) << numBits) - 1) << shiftBits))
|
||||||
|
("shl", shiftLeftFunction(shiftBits))
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::roundUpFunction()
|
string YulUtilFunctions::roundUpFunction()
|
||||||
{
|
{
|
||||||
string functionName = "round_up_to_mul_of_32";
|
string functionName = "round_up_to_mul_of_32";
|
||||||
@ -541,6 +565,26 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::prepareStoreFunction(Type const& _type)
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(_type.category() != Type::Category::Function, "");
|
||||||
|
|
||||||
|
string functionName = "prepare_store_" + _type.identifier();
|
||||||
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
Whiskers templ(R"(
|
||||||
|
function <functionName>(value) -> ret {
|
||||||
|
ret := <actualPrepare>
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
templ("functionName", functionName);
|
||||||
|
if (_type.category() == Type::Category::FixedBytes)
|
||||||
|
templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)");
|
||||||
|
else
|
||||||
|
templ("actualPrepare", "value");
|
||||||
|
return templ.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::allocationFunction()
|
string YulUtilFunctions::allocationFunction()
|
||||||
{
|
{
|
||||||
string functionName = "allocateMemory";
|
string functionName = "allocateMemory";
|
||||||
|
@ -69,6 +69,11 @@ public:
|
|||||||
std::string shiftLeftFunction(size_t _numBits);
|
std::string shiftLeftFunction(size_t _numBits);
|
||||||
std::string shiftRightFunction(size_t _numBits);
|
std::string shiftRightFunction(size_t _numBits);
|
||||||
|
|
||||||
|
/// @returns the name of a function f(value, toInsert) -> newValue which replaces the
|
||||||
|
/// _numBytes bytes starting at byte position _shiftBytes (counted from the least significant
|
||||||
|
/// byte) by the _numBytes least significant bytes of `toInsert`.
|
||||||
|
std::string updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes);
|
||||||
|
|
||||||
/// @returns the name of a function that rounds its input to the next multiple
|
/// @returns the name of a function that rounds its input to the next multiple
|
||||||
/// of 32 or the input if it is a multiple of 32.
|
/// of 32 or the input if it is a multiple of 32.
|
||||||
std::string roundUpFunction();
|
std::string roundUpFunction();
|
||||||
@ -110,6 +115,12 @@ public:
|
|||||||
/// single variable.
|
/// single variable.
|
||||||
std::string cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes);
|
std::string cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes);
|
||||||
|
|
||||||
|
/// @returns the name of a function that prepares a value of the given type
|
||||||
|
/// for being stored in storage. This usually includes cleanup and right-alignment
|
||||||
|
/// to fit the number of bytes in storage.
|
||||||
|
/// The resulting value might still have dirty higher order bits.
|
||||||
|
std::string prepareStoreFunction(Type const& _type);
|
||||||
|
|
||||||
/// @returns the name of a function that allocates memory.
|
/// @returns the name of a function that allocates memory.
|
||||||
/// Modifies the "free memory pointer"
|
/// Modifies the "free memory pointer"
|
||||||
/// Arguments: size
|
/// Arguments: size
|
||||||
|
@ -39,7 +39,7 @@ string IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl
|
|||||||
return m_localVariables[&_varDecl] = "vloc_" + _varDecl.name() + "_" + to_string(_varDecl.id());
|
return m_localVariables[&_varDecl] = "vloc_" + _varDecl.name() + "_" + to_string(_varDecl.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerationContext::variableName(VariableDeclaration const& _varDecl)
|
string IRGenerationContext::localVariableName(VariableDeclaration const& _varDecl)
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(
|
||||||
m_localVariables.count(&_varDecl),
|
m_localVariables.count(&_varDecl),
|
||||||
@ -48,6 +48,15 @@ string IRGenerationContext::variableName(VariableDeclaration const& _varDecl)
|
|||||||
return m_localVariables[&_varDecl];
|
return m_localVariables[&_varDecl];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IRGenerationContext::addStateVariable(
|
||||||
|
VariableDeclaration const& _declaration,
|
||||||
|
u256 _storageOffset,
|
||||||
|
unsigned _byteOffset
|
||||||
|
)
|
||||||
|
{
|
||||||
|
m_stateVariables[&_declaration] = make_pair(move(_storageOffset), _byteOffset);
|
||||||
|
}
|
||||||
|
|
||||||
string IRGenerationContext::functionName(FunctionDefinition const& _function)
|
string IRGenerationContext::functionName(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
// @TODO previously, we had to distinguish creation context and runtime context,
|
// @TODO previously, we had to distinguish creation context and runtime context,
|
||||||
@ -128,3 +137,8 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
|
|||||||
return templ.render();
|
return templ.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
YulUtilFunctions IRGenerationContext::utils()
|
||||||
|
{
|
||||||
|
return YulUtilFunctions(m_evmVersion, m_functions);
|
||||||
|
}
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -39,6 +41,7 @@ class ContractDefinition;
|
|||||||
class VariableDeclaration;
|
class VariableDeclaration;
|
||||||
class FunctionDefinition;
|
class FunctionDefinition;
|
||||||
class Expression;
|
class Expression;
|
||||||
|
class YulUtilFunctions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that contains contextual information during IR generation.
|
* Class that contains contextual information during IR generation.
|
||||||
@ -62,7 +65,16 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
std::string addLocalVariable(VariableDeclaration const& _varDecl);
|
std::string addLocalVariable(VariableDeclaration const& _varDecl);
|
||||||
std::string variableName(VariableDeclaration const& _varDecl);
|
bool isLocalVariable(VariableDeclaration const& _varDecl) const { return m_localVariables.count(&_varDecl); }
|
||||||
|
std::string localVariableName(VariableDeclaration const& _varDecl);
|
||||||
|
|
||||||
|
void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset);
|
||||||
|
bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); }
|
||||||
|
std::pair<u256, unsigned> storageLocationOfVariable(VariableDeclaration const& _varDecl) const
|
||||||
|
{
|
||||||
|
return m_stateVariables.at(&_varDecl);
|
||||||
|
}
|
||||||
|
|
||||||
std::string functionName(FunctionDefinition const& _function);
|
std::string functionName(FunctionDefinition const& _function);
|
||||||
FunctionDefinition const& virtualFunction(FunctionDefinition const& _functionDeclaration);
|
FunctionDefinition const& virtualFunction(FunctionDefinition const& _functionDeclaration);
|
||||||
std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration);
|
std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration);
|
||||||
@ -74,11 +86,16 @@ public:
|
|||||||
|
|
||||||
std::string internalDispatch(size_t _in, size_t _out);
|
std::string internalDispatch(size_t _in, size_t _out);
|
||||||
|
|
||||||
|
/// @returns a new copy of the utility function generator (but using the same function set).
|
||||||
|
YulUtilFunctions utils();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
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*, std::string> m_localVariables;
|
||||||
|
/// Storage offsets of state variables
|
||||||
|
std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
||||||
std::shared_ptr<MultiUseYulFunctionCollector> m_functions;
|
std::shared_ptr<MultiUseYulFunctionCollector> m_functions;
|
||||||
size_t m_varCounter = 0;
|
size_t m_varCounter = 0;
|
||||||
};
|
};
|
||||||
|
@ -89,15 +89,14 @@ string IRGenerator::generate(ContractDefinition const& _contract)
|
|||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
|
|
||||||
resetContext();
|
resetContext(_contract);
|
||||||
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
|
|
||||||
t("CreationObject", creationObjectName(_contract));
|
t("CreationObject", creationObjectName(_contract));
|
||||||
t("memoryInit", memoryInit());
|
t("memoryInit", memoryInit());
|
||||||
t("constructor", _contract.constructor() ? constructorCode(*_contract.constructor()) : "");
|
t("constructor", _contract.constructor() ? constructorCode(*_contract.constructor()) : "");
|
||||||
t("deploy", deployCode(_contract));
|
t("deploy", deployCode(_contract));
|
||||||
t("functions", m_context.functionCollector()->requestedFunctions());
|
t("functions", m_context.functionCollector()->requestedFunctions());
|
||||||
|
|
||||||
resetContext();
|
resetContext(_contract);
|
||||||
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
|
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
|
||||||
t("RuntimeObject", runtimeObjectName(_contract));
|
t("RuntimeObject", runtimeObjectName(_contract));
|
||||||
t("dispatch", dispatchRoutine(_contract));
|
t("dispatch", dispatchRoutine(_contract));
|
||||||
@ -249,7 +248,7 @@ string IRGenerator::memoryInit()
|
|||||||
.render();
|
.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRGenerator::resetContext()
|
void IRGenerator::resetContext(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(
|
||||||
m_context.functionCollector()->requestedFunctions().empty(),
|
m_context.functionCollector()->requestedFunctions().empty(),
|
||||||
@ -257,4 +256,8 @@ void IRGenerator::resetContext()
|
|||||||
);
|
);
|
||||||
m_context = IRGenerationContext(m_evmVersion, m_optimiserSettings);
|
m_context = IRGenerationContext(m_evmVersion, m_optimiserSettings);
|
||||||
m_utils = YulUtilFunctions(m_evmVersion, m_context.functionCollector());
|
m_utils = YulUtilFunctions(m_evmVersion, m_context.functionCollector());
|
||||||
|
|
||||||
|
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
|
||||||
|
for (auto const& var: ContractType(_contract).stateVariables())
|
||||||
|
m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var));
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ private:
|
|||||||
|
|
||||||
std::string memoryInit();
|
std::string memoryInit();
|
||||||
|
|
||||||
void resetContext();
|
void resetContext(ContractDefinition const& _contract);
|
||||||
|
|
||||||
langutil::EVMVersion const m_evmVersion;
|
langutil::EVMVersion const m_evmVersion;
|
||||||
OptimiserSettings const m_optimiserSettings;
|
OptimiserSettings const m_optimiserSettings;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <libsolidity/codegen/ir/IRGeneratorForStatements.h>
|
#include <libsolidity/codegen/ir/IRGeneratorForStatements.h>
|
||||||
|
|
||||||
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
||||||
|
#include <libsolidity/codegen/ir/IRLValue.h>
|
||||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||||
#include <libsolidity/ast/TypeProvider.h>
|
#include <libsolidity/ast/TypeProvider.h>
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ struct CopyTranslate: public yul::ASTCopier
|
|||||||
|
|
||||||
return yul::Identifier{
|
return yul::Identifier{
|
||||||
_identifier.location,
|
_identifier.location,
|
||||||
yul::YulString{m_context.variableName(*varDecl)}
|
yul::YulString{m_context.localVariableName(*varDecl)}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +80,12 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
string IRGeneratorForStatements::code() const
|
||||||
|
{
|
||||||
|
solAssert(!m_currentLValue, "LValue not reset!");
|
||||||
|
return m_code.str();
|
||||||
|
}
|
||||||
|
|
||||||
bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _varDeclStatement)
|
bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _varDeclStatement)
|
||||||
{
|
{
|
||||||
for (auto const& decl: _varDeclStatement.declarations())
|
for (auto const& decl: _varDeclStatement.declarations())
|
||||||
@ -94,7 +101,7 @@ bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _varDec
|
|||||||
VariableDeclaration const& varDecl = *_varDeclStatement.declarations().front();
|
VariableDeclaration const& varDecl = *_varDeclStatement.declarations().front();
|
||||||
m_code <<
|
m_code <<
|
||||||
"let " <<
|
"let " <<
|
||||||
m_context.variableName(varDecl) <<
|
m_context.localVariableName(varDecl) <<
|
||||||
" := " <<
|
" := " <<
|
||||||
expressionAsType(*expression, *varDecl.type()) <<
|
expressionAsType(*expression, *varDecl.type()) <<
|
||||||
"\n";
|
"\n";
|
||||||
@ -102,7 +109,7 @@ bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _varDec
|
|||||||
else
|
else
|
||||||
for (auto const& decl: _varDeclStatement.declarations())
|
for (auto const& decl: _varDeclStatement.declarations())
|
||||||
if (decl)
|
if (decl)
|
||||||
m_code << "let " << m_context.variableName(*decl) << "\n";
|
m_code << "let " << m_context.localVariableName(*decl) << "\n";
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -112,17 +119,18 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
|||||||
solUnimplementedAssert(_assignment.assignmentOperator() == Token::Assign, "");
|
solUnimplementedAssert(_assignment.assignmentOperator() == Token::Assign, "");
|
||||||
|
|
||||||
_assignment.rightHandSide().accept(*this);
|
_assignment.rightHandSide().accept(*this);
|
||||||
|
Type const* intermediateType = _assignment.rightHandSide().annotation().type->closestTemporaryType(
|
||||||
|
_assignment.leftHandSide().annotation().type
|
||||||
|
);
|
||||||
|
string intermediateValue = m_context.newYulVariable();
|
||||||
|
m_code << "let " << intermediateValue << " := " << expressionAsType(_assignment.rightHandSide(), *intermediateType) << "\n";
|
||||||
|
|
||||||
// TODO proper lvalue handling
|
_assignment.leftHandSide().accept(*this);
|
||||||
auto const& lvalue = dynamic_cast<Identifier const&>(_assignment.leftHandSide());
|
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
||||||
string varName = m_context.variableName(dynamic_cast<VariableDeclaration const&>(*lvalue.annotation().referencedDeclaration));
|
m_currentLValue->storeValue(intermediateValue, *intermediateType);
|
||||||
|
m_currentLValue.reset();
|
||||||
|
|
||||||
m_code <<
|
defineExpression(_assignment) << intermediateValue << "\n";
|
||||||
varName <<
|
|
||||||
" := " <<
|
|
||||||
expressionAsType(_assignment.rightHandSide(), *lvalue.annotation().type) <<
|
|
||||||
"\n";
|
|
||||||
defineExpression(_assignment) << varName << "\n";
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -179,7 +187,7 @@ bool IRGeneratorForStatements::visit(Return const& _return)
|
|||||||
// TODO support tuples
|
// TODO support tuples
|
||||||
solUnimplementedAssert(types.size() == 1, "Multi-returns not implemented.");
|
solUnimplementedAssert(types.size() == 1, "Multi-returns not implemented.");
|
||||||
m_code <<
|
m_code <<
|
||||||
m_context.variableName(*returnParameters.front()) <<
|
m_context.localVariableName(*returnParameters.front()) <<
|
||||||
" := " <<
|
" := " <<
|
||||||
expressionAsType(*value, *types.front()) <<
|
expressionAsType(*value, *types.front()) <<
|
||||||
"\n";
|
"\n";
|
||||||
@ -329,14 +337,26 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
|
|||||||
bool IRGeneratorForStatements::visit(Identifier const& _identifier)
|
bool IRGeneratorForStatements::visit(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
Declaration const* declaration = _identifier.annotation().referencedDeclaration;
|
Declaration const* declaration = _identifier.annotation().referencedDeclaration;
|
||||||
string value;
|
|
||||||
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
||||||
value = to_string(m_context.virtualFunction(*functionDef).id());
|
defineExpression(_identifier) << to_string(m_context.virtualFunction(*functionDef).id()) << "\n";
|
||||||
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||||
value = m_context.variableName(*varDecl);
|
{
|
||||||
|
// TODO for the constant case, we have to be careful:
|
||||||
|
// If the value is visited twice, `defineExpression` is called twice on
|
||||||
|
// the same expression.
|
||||||
|
solUnimplementedAssert(!varDecl->isConstant(), "");
|
||||||
|
unique_ptr<IRLValue> lvalue;
|
||||||
|
if (m_context.isLocalVariable(*varDecl))
|
||||||
|
lvalue = make_unique<IRLocalVariable>(m_code, m_context, *varDecl);
|
||||||
|
else if (m_context.isStateVariable(*varDecl))
|
||||||
|
lvalue = make_unique<IRStorageItem>(m_code, m_context, *varDecl);
|
||||||
|
else
|
||||||
|
solAssert(false, "Invalid variable kind.");
|
||||||
|
|
||||||
|
setLValue(_identifier, move(lvalue));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
solUnimplemented("");
|
solUnimplemented("");
|
||||||
defineExpression(_identifier) << value << "\n";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,3 +395,14 @@ ostream& IRGeneratorForStatements::defineExpression(Expression const& _expressio
|
|||||||
{
|
{
|
||||||
return m_code << "let " << m_context.variable(_expression) << " := ";
|
return m_code << "let " << m_context.variable(_expression) << " := ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IRGeneratorForStatements::setLValue(Expression const& _expression, unique_ptr<IRLValue> _lvalue)
|
||||||
|
{
|
||||||
|
solAssert(!m_currentLValue, "");
|
||||||
|
|
||||||
|
if (_expression.annotation().lValueRequested)
|
||||||
|
// Do not define the expression, so it cannot be used as value.
|
||||||
|
m_currentLValue = std::move(_lvalue);
|
||||||
|
else
|
||||||
|
defineExpression(_expression) << _lvalue->retrieveValue() << "\n";
|
||||||
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libsolidity/ast/ASTVisitor.h>
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
|
#include <libsolidity/codegen/ir/IRLValue.h>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -42,7 +43,7 @@ public:
|
|||||||
m_utils(_utils)
|
m_utils(_utils)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
std::string code() const { return m_code.str(); }
|
std::string code() const;
|
||||||
|
|
||||||
bool visit(VariableDeclarationStatement const& _variableDeclaration) override;
|
bool visit(VariableDeclarationStatement const& _variableDeclaration) override;
|
||||||
bool visit(Assignment const& _assignment) override;
|
bool visit(Assignment const& _assignment) override;
|
||||||
@ -62,9 +63,12 @@ private:
|
|||||||
std::string expressionAsType(Expression const& _expression, Type const& _to);
|
std::string expressionAsType(Expression const& _expression, Type const& _to);
|
||||||
std::ostream& defineExpression(Expression const& _expression);
|
std::ostream& defineExpression(Expression const& _expression);
|
||||||
|
|
||||||
|
void setLValue(Expression const& _expression, std::unique_ptr<IRLValue> _lvalue);
|
||||||
|
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
103
libsolidity/codegen/ir/IRLValue.cpp
Normal file
103
libsolidity/codegen/ir/IRLValue.cpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
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/ast/AST.h>
|
||||||
|
|
||||||
|
#include <libdevcore/Whiskers.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
IRLocalVariable::IRLocalVariable(
|
||||||
|
ostream& _code,
|
||||||
|
IRGenerationContext& _context,
|
||||||
|
VariableDeclaration const& _varDecl
|
||||||
|
):
|
||||||
|
IRLValue(_code, _context, _varDecl.annotation().type),
|
||||||
|
m_variableName(_context.localVariableName(_varDecl))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRLocalVariable::storeValue(string const& _value, Type const& _type) const
|
||||||
|
{
|
||||||
|
solAssert(_type == *m_type, "Storing different types - not necessarily a problem.");
|
||||||
|
m_code << m_variableName << " := " << _value << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
IRStorageItem::IRStorageItem(
|
||||||
|
ostream& _code,
|
||||||
|
IRGenerationContext& _context,
|
||||||
|
VariableDeclaration const& _varDecl
|
||||||
|
):
|
||||||
|
IRLValue(_code, _context, _varDecl.annotation().type)
|
||||||
|
{
|
||||||
|
u256 slot;
|
||||||
|
unsigned offset;
|
||||||
|
std::tie(slot, offset) = _context.storageLocationOfVariable(_varDecl);
|
||||||
|
m_slot = toCompactHexWithPrefix(slot);
|
||||||
|
m_offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRStorageItem::retrieveValue() const
|
||||||
|
{
|
||||||
|
if (!m_type->isValueType())
|
||||||
|
return m_slot;
|
||||||
|
solUnimplementedAssert(m_type->category() != Type::Category::Function, "");
|
||||||
|
return m_context.utils().readFromStorage(*m_type, m_offset, false) + "(" + m_slot + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRStorageItem::storeValue(string const& _value, Type const& _sourceType) const
|
||||||
|
{
|
||||||
|
if (m_type->isValueType())
|
||||||
|
{
|
||||||
|
solAssert(m_type->storageBytes() <= 32, "Invalid storage bytes size.");
|
||||||
|
solAssert(m_type->storageBytes() > 0, "Invalid storage bytes size.");
|
||||||
|
solAssert(m_type->storageBytes() + m_offset <= 32, "");
|
||||||
|
|
||||||
|
solAssert(_sourceType == *m_type, "Different type, but might not be an error.");
|
||||||
|
|
||||||
|
m_code <<
|
||||||
|
Whiskers("sstore(<slot>, <update>(sload(<slot>), <prepare>(<value>)))\n")
|
||||||
|
("slot", m_slot)
|
||||||
|
("update", m_context.utils().updateByteSliceFunction(m_type->storageBytes(), m_offset))
|
||||||
|
("prepare", m_context.utils().prepareStoreFunction(*m_type))
|
||||||
|
("value", _value)
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
_sourceType.category() == m_type->category(),
|
||||||
|
"Wrong type conversation for assignment."
|
||||||
|
);
|
||||||
|
if (m_type->category() == Type::Category::Array)
|
||||||
|
solUnimplementedAssert(false, "");
|
||||||
|
else if (m_type->category() == Type::Category::Struct)
|
||||||
|
solUnimplementedAssert(false, "");
|
||||||
|
else
|
||||||
|
solAssert(false, "Invalid non-value type for assignment.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
95
libsolidity/codegen/ir/IRLValue.h
Normal file
95
libsolidity/codegen/ir/IRLValue.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
|
||||||
|
class VariableDeclaration;
|
||||||
|
class IRGenerationContext;
|
||||||
|
class Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class used to retrieve, delete and store data in LValues.
|
||||||
|
*/
|
||||||
|
class IRLValue
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
IRLValue(std::ostream& _code, IRGenerationContext& _context, Type const* _type = nullptr):
|
||||||
|
m_code(_code),
|
||||||
|
m_context(_context),
|
||||||
|
m_type(_type)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~IRLValue() = default;
|
||||||
|
/// @returns an expression to retrieve the value of the lvalue. This might also emit
|
||||||
|
/// code to m_code.
|
||||||
|
virtual std::string retrieveValue() const = 0;
|
||||||
|
/// Appends code to m_code to store the value of @a _value (should be an identifier)
|
||||||
|
/// of type @a _type in the lvalue. Might perform type conversion.
|
||||||
|
virtual void storeValue(std::string const& _value, Type const& _type) const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::ostream& m_code;
|
||||||
|
IRGenerationContext& m_context;
|
||||||
|
Type const* m_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IRLocalVariable: public IRLValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IRLocalVariable(
|
||||||
|
std::ostream& _code,
|
||||||
|
IRGenerationContext& _context,
|
||||||
|
VariableDeclaration const& _varDecl
|
||||||
|
);
|
||||||
|
std::string retrieveValue() const override { return m_variableName; }
|
||||||
|
void storeValue(std::string const& _value, Type const& _type) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_variableName;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IRStorageItem: public IRLValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IRStorageItem(
|
||||||
|
std::ostream& _code,
|
||||||
|
IRGenerationContext& _context,
|
||||||
|
VariableDeclaration const& _varDecl
|
||||||
|
);
|
||||||
|
std::string retrieveValue() const override;
|
||||||
|
void storeValue(std::string const& _value, Type const& _type) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_slot;
|
||||||
|
unsigned m_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
contract C {
|
||||||
|
uint16 x;
|
||||||
|
byte y;
|
||||||
|
uint16 z;
|
||||||
|
function f(uint8 a) public returns (uint _x) {
|
||||||
|
x = a;
|
||||||
|
y = byte(uint8(x) + 1);
|
||||||
|
z = uint8(y) + 1;
|
||||||
|
x = z + 1;
|
||||||
|
_x = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// f(uint8): 6 -> 9
|
@ -0,0 +1,17 @@
|
|||||||
|
contract C {
|
||||||
|
uint x;
|
||||||
|
uint y;
|
||||||
|
function setX(uint a) public returns (uint _x) {
|
||||||
|
x = a;
|
||||||
|
_x = x;
|
||||||
|
}
|
||||||
|
function setY(uint a) public returns (uint _y) {
|
||||||
|
y = a;
|
||||||
|
_y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// setX(uint256): 6 -> 6
|
||||||
|
// setY(uint256): 2 -> 2
|
Loading…
Reference in New Issue
Block a user