Add IRVariable

This commit is contained in:
r0qs 2023-09-28 18:50:07 +02:00
parent 0819e31de8
commit 1a8e9f6ac4
No known key found for this signature in database
GPG Key ID: 61503DBA6667276C
4 changed files with 204 additions and 19 deletions

View File

@ -204,6 +204,8 @@ set(sources
experimental/ast/TypeSystemHelper.h
experimental/codegen/Common.h
experimental/codegen/Common.cpp
experimental/codegen/IRVariable.cpp
experimental/codegen/IRVariable.h
experimental/codegen/IRGenerationContext.h
experimental/codegen/IRGenerator.cpp
experimental/codegen/IRGenerator.h

View File

@ -101,7 +101,7 @@ private:
auto type = m_context.analysis.annotation<TypeInference>(*varDecl).type;
solAssert(type);
solAssert(m_context.env->typeEquals(*type, m_context.analysis.typeSystem().type(PrimitiveType::Word, {})));
std::string value = IRNames::localVariable(*varDecl);
std::string value = IRVariable{*varDecl, *type}.name();
return yul::Identifier{_identifier.debugData, yul::YulString{value}};
}
@ -119,7 +119,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tupleExpression)
{
solUnimplementedAssert(component);
component->accept(*this);
components.emplace_back(IRNames::localVariable(*component));
components.emplace_back(IRVariable{*component, type(*component)}.name());
}
solUnimplementedAssert(false, "No support for tuples.");
@ -144,9 +144,12 @@ bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _variab
VariableDeclaration const* variableDeclaration = _variableDeclarationStatement.declarations().front().get();
solAssert(variableDeclaration);
// TODO: check the type of the variable; register local variable; initialize
m_code << "let " << IRNames::localVariable(*variableDeclaration);
m_code << "let " << IRVariable{*variableDeclaration, type(*variableDeclaration)}.name();
if (_variableDeclarationStatement.initialValue())
m_code << " := " << IRNames::localVariable(*_variableDeclarationStatement.initialValue());
{
auto value = _variableDeclarationStatement.initialValue();
m_code << " := " << IRVariable{*value, type(*value)}.name();
}
m_code << "\n";
return false;
}
@ -159,9 +162,7 @@ bool IRGeneratorForStatements::visit(ExpressionStatement const&)
bool IRGeneratorForStatements::visit(Identifier const& _identifier)
{
if (auto const* var = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
{
m_code << "let " << IRNames::localVariable(_identifier) << " := " << IRNames::localVariable(*var) << "\n";
}
m_code << "let " << IRVariable{_identifier, type(_identifier)}.name() << " := " << IRVariable{*var, type(*var)}.name() << "\n";
else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_identifier.annotation().referencedDeclaration))
solAssert(m_expressionDeclaration.emplace(&_identifier, function).second);
else if (auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(_identifier.annotation().referencedDeclaration))
@ -179,7 +180,8 @@ void IRGeneratorForStatements::endVisit(Return const& _return)
{
solAssert(_return.annotation().function, "Invalid return.");
solAssert(_return.annotation().function->experimentalReturnExpression(), "Invalid return.");
m_code << IRNames::localVariable(*_return.annotation().function->experimentalReturnExpression()) << " := " << IRNames::localVariable(*value) << "\n";
auto returnExpression = _return.annotation().function->experimentalReturnExpression();
m_code << IRVariable{*returnExpression, type(*returnExpression)}.name() << " := " << IRVariable{*value, type(*value)}.name() << "\n";
}
m_code << "leave\n";
@ -206,8 +208,8 @@ void IRGeneratorForStatements::endVisit(BinaryOperation const& _binaryOperation)
functionType = m_context.env->resolveRecursive(functionType);
m_context.enqueueFunctionDefinition(&functionDefinition, functionType);
// TODO: account for return stack size
m_code << "let " << IRNames::localVariable(_binaryOperation) << " := " << IRNames::function(*m_context.env, functionDefinition, functionType) << "("
<< IRNames::localVariable(_binaryOperation.leftExpression()) << ", " << IRNames::localVariable(_binaryOperation.rightExpression()) << ")\n";
m_code << "let " << IRVariable{_binaryOperation, type(_binaryOperation)}.name() << " := " << IRNames::function(*m_context.env, functionDefinition, functionType) << "("
<< IRVariable{_binaryOperation.leftExpression(), type(_binaryOperation.leftExpression())}.name() << ", " << IRVariable{_binaryOperation.rightExpression(), type(_binaryOperation.rightExpression())}.name() << ")\n";
}
namespace
@ -307,11 +309,11 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
case Builtins::FromBool:
case Builtins::Identity:
solAssert(_functionCall.arguments().size() == 1);
m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::localVariable(*_functionCall.arguments().front()) << "\n";
m_code << "let " << IRVariable{_functionCall, type(_functionCall)}.name() << " := " << IRVariable{*_functionCall.arguments().front(), type(*_functionCall.arguments().front())}.name() << "\n";
return;
case Builtins::ToBool:
solAssert(_functionCall.arguments().size() == 1);
m_code << "let " << IRNames::localVariable(_functionCall) << " := iszero(iszero(" << IRNames::localVariable(*_functionCall.arguments().front()) << "))\n";
m_code << "let " << IRVariable{_functionCall, type(_functionCall)}.name() << " := iszero(iszero(" << IRVariable{*_functionCall.arguments().front(), type(*_functionCall.arguments().front())}.name() << "))\n";
return;
}
solAssert(false);
@ -322,13 +324,13 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
functionType = m_context.env->resolveRecursive(functionType);
m_context.enqueueFunctionDefinition(functionDefinition, functionType);
// TODO: account for return stack size
m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::function(*m_context.env, *functionDefinition, functionType) << "(";
m_code << "let " << IRVariable{_functionCall, type(_functionCall)}.name() << " := " << IRNames::function(*m_context.env, *functionDefinition, functionType) << "(";
auto const& arguments = _functionCall.arguments();
if (arguments.size() > 1)
for (auto arg: arguments | ranges::views::drop_last(1))
m_code << IRNames::localVariable(*arg) << ", ";
m_code << IRVariable{*arg, type(*arg)}.name() << ", ";
if (!arguments.empty())
m_code << IRNames::localVariable(*arguments.back());
m_code << IRVariable{*arguments.back(), type(*arguments.back())}.name();
m_code << ")\n";
}
@ -352,7 +354,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
_ifStatement.condition().accept(*this);
if (_ifStatement.falseStatement())
{
m_code << "switch " << IRNames::localVariable(_ifStatement.condition()) << " {\n";
m_code << "switch " << IRVariable{_ifStatement.condition(), type(_ifStatement.condition())}.name() << " {\n";
m_code << "case 0 {\n";
_ifStatement.falseStatement()->accept(*this);
m_code << "}\n";
@ -362,7 +364,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
}
else
{
m_code << "if " << IRNames::localVariable(_ifStatement.condition()) << " {\n";
m_code << "if " << IRVariable{_ifStatement.condition(), type(_ifStatement.condition())}.name() << " {\n";
_ifStatement.trueStatement().accept(*this);
m_code << "}\n";
}
@ -376,9 +378,9 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
solAssert(lhs, "Can only assign to identifiers.");
auto const* lhsVar = dynamic_cast<VariableDeclaration const*>(lhs->annotation().referencedDeclaration);
solAssert(lhsVar, "Can only assign to identifiers referring to variables.");
m_code << IRNames::localVariable(*lhsVar) << " := " << IRNames::localVariable(_assignment.rightHandSide()) << "\n";
m_code << IRVariable{*lhsVar, type(*lhsVar)}.name() << " := " << IRVariable{_assignment.rightHandSide(), type(_assignment.rightHandSide())}.name() << "\n";
m_code << "let " << IRNames::localVariable(_assignment) << " := " << IRNames::localVariable(*lhsVar) << "\n";
m_code << "let " << IRVariable{_assignment, type(_assignment)}.name() << " := " << IRVariable{*lhsVar, type(*lhsVar)}.name() << "\n";
return false;
}

View File

@ -0,0 +1,91 @@
/*
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/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <libsolidity/experimental/analysis/TypeInference.h>
#include <libsolidity/experimental/codegen/Common.h>
#include <libsolidity/experimental/codegen/IRVariable.h>
#include <libsolutil/StringUtils.h>
using namespace solidity;
using namespace solidity::util;
using namespace solidity::frontend::experimental;
IRVariable::IRVariable(std::string _baseName, Type _type):
m_baseName(std::move(_baseName)), m_type(_type)
{
}
IRVariable::IRVariable(VariableDeclaration const& _declaration, Type _type):
IRVariable(IRNames::localVariable(_declaration), _type)
{
solAssert(!_declaration.isStateVariable(), "");
}
IRVariable::IRVariable(Expression const& _expression, Type _type):
IRVariable(IRNames::localVariable(_expression), _type)
{
}
IRVariable IRVariable::part(std::string const&) const
{
// TODO
return IRVariable{"", Type{}};
}
bool IRVariable::hasPart(std::string const&) const
{
// TODO
return false;
}
std::vector<std::string> IRVariable::stackSlots() const
{
// TODO
return std::vector<std::string>{};
}
std::string IRVariable::commaSeparatedList() const
{
return joinHumanReadable(stackSlots());
}
std::string IRVariable::commaSeparatedListPrefixed() const
{
return joinHumanReadablePrefixed(stackSlots());
}
std::string IRVariable::name() const
{
// TODO
return m_baseName;
}
IRVariable IRVariable::tupleComponent(size_t) const
{
// TODO
return IRVariable{"", Type{}};
}
std::string IRVariable::suffixedName(std::string const& _suffix) const
{
if (_suffix.empty())
return m_baseName;
else
return m_baseName + '_' + _suffix;
}

View File

@ -0,0 +1,90 @@
/*
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/>.
*/
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include <libsolidity/ast/AST.h>
#include <libsolidity/experimental/codegen/IRGenerationContext.h>
#include <libsolidity/experimental/ast/Type.h>
#include <optional>
#include <string>
#include <vector>
namespace solidity::frontend::experimental
{
/**
* An IRVariable refers to a set of yul variables that correspond to the stack layout of a Solidity variable or expression
* of a specific Solidity 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 _type);
/// IR variable referring to the declaration @a _decl.
explicit IRVariable(VariableDeclaration const& _decl, Type _type);
/// 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, Type _type);
/// @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 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;
/// @returns true if variable contains @a _name component
/// @a _name name of the component that is being checked
bool hasPart(std::string const& _name) const;
/// @returns a vector containing the names of the stack slots of the variable.
std::vector<std::string> stackSlots() const;
private:
/// @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 m_type;
};
}