From 1a8e9f6ac42b9f782ac8428f14769652e919f495 Mon Sep 17 00:00:00 2001 From: r0qs Date: Thu, 28 Sep 2023 18:50:07 +0200 Subject: [PATCH] Add IRVariable --- libsolidity/CMakeLists.txt | 2 + .../codegen/IRGeneratorForStatements.cpp | 40 ++++---- .../experimental/codegen/IRVariable.cpp | 91 +++++++++++++++++++ libsolidity/experimental/codegen/IRVariable.h | 90 ++++++++++++++++++ 4 files changed, 204 insertions(+), 19 deletions(-) create mode 100644 libsolidity/experimental/codegen/IRVariable.cpp create mode 100644 libsolidity/experimental/codegen/IRVariable.h diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 6ad451ed1..49421979f 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -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 diff --git a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp index 589fe6787..e8ecf2a62 100644 --- a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp @@ -101,7 +101,7 @@ private: auto type = m_context.analysis.annotation(*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(_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(_identifier.annotation().referencedDeclaration)) solAssert(m_expressionDeclaration.emplace(&_identifier, function).second); else if (auto const* typeClass = dynamic_cast(_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(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; } diff --git a/libsolidity/experimental/codegen/IRVariable.cpp b/libsolidity/experimental/codegen/IRVariable.cpp new file mode 100644 index 000000000..dd00640a2 --- /dev/null +++ b/libsolidity/experimental/codegen/IRVariable.cpp @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include +#include + +#include + +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 IRVariable::stackSlots() const +{ + // TODO + return std::vector{}; +} + +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; +} diff --git a/libsolidity/experimental/codegen/IRVariable.h b/libsolidity/experimental/codegen/IRVariable.h new file mode 100644 index 000000000..18c275100 --- /dev/null +++ b/libsolidity/experimental/codegen/IRVariable.h @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include +#include + +#include +#include +#include + +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 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; +}; + + +}