Skip to content

Commit 2979e19

Browse files
committed
[experimental-solidity] Generate code for types of different stack sizes
1 parent 5fe3173 commit 2979e19

File tree

9 files changed

+311
-37
lines changed

9 files changed

+311
-37
lines changed

Diff for: libsolidity/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ set(sources
207207
experimental/ast/TypeSystemHelper.h
208208
experimental/codegen/Common.h
209209
experimental/codegen/Common.cpp
210+
experimental/codegen/IRVariable.cpp
211+
experimental/codegen/IRVariable.h
210212
experimental/codegen/IRGenerationContext.h
211213
experimental/codegen/IRGenerator.cpp
212214
experimental/codegen/IRGenerator.h

Diff for: libsolidity/ast/AST.h

+3
Original file line numberDiff line numberDiff line change
@@ -2162,6 +2162,9 @@ class BinaryOperation: public Expression
21622162
Expression const& rightExpression() const { return *m_right; }
21632163
Token getOperator() const { return m_operator; }
21642164

2165+
/// @returns the given arguments in the order they were written.
2166+
std::vector<ASTPointer<Expression const>> arguments() const { return {m_left, m_right}; }
2167+
21652168
FunctionType const* userDefinedFunctionType() const;
21662169

21672170
BinaryOperationAnnotation& annotation() const override;

Diff for: libsolidity/experimental/analysis/TypeInference.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,7 @@ bool TypeInference::visit(TypeDefinition const& _typeDefinition)
822822

823823
members->second.emplace("abs", TypeMember{helper.functionType(*underlyingType, definedType)});
824824
members->second.emplace("rep", TypeMember{helper.functionType(definedType, *underlyingType)});
825+
annotation().underlyingTypes[constructor] = *underlyingType;
825826
}
826827

827828
if (helper.isPrimitiveType(definedType, PrimitiveType::Pair))

Diff for: libsolidity/experimental/analysis/TypeInference.h

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class TypeInference: public ASTConstVisitor
5050
std::map<TypeClass, std::map<std::string, Type>> typeClassFunctions;
5151
std::map<Token, std::tuple<TypeClass, std::string>> operators;
5252
std::map<TypeConstructor, std::map<std::string, TypeMember>> members;
53+
std::map<TypeConstructor, Type> underlyingTypes;
5354
};
5455
bool visit(Block const&) override { return true; }
5556
bool visit(VariableDeclarationStatement const&) override { return true; }

Diff for: libsolidity/experimental/codegen/IRGeneratorForStatements.cpp

+119-36
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <libsolidity/experimental/codegen/Common.h>
3434

3535
#include <range/v3/view/drop_last.hpp>
36+
#include <range/v3/view/zip.hpp>
3637

3738
using namespace solidity;
3839
using namespace solidity::util;
@@ -101,7 +102,7 @@ struct CopyTranslate: public yul::ASTCopier
101102
auto type = m_context.analysis.annotation<TypeInference>(*varDecl).type;
102103
solAssert(type);
103104
solAssert(m_context.env->typeEquals(*type, m_context.analysis.typeSystem().type(PrimitiveType::Word, {})));
104-
std::string value = IRNames::localVariable(*varDecl);
105+
std::string value = IRVariable{*varDecl, *type, IRGeneratorForStatements::stackSize(m_context, *type)}.name();
105106
return yul::Identifier{_identifier.debugData, yul::YulString{value}};
106107
}
107108

@@ -112,14 +113,75 @@ struct CopyTranslate: public yul::ASTCopier
112113

113114
}
114115

116+
std::size_t IRGeneratorForStatements::stackSize(IRGenerationContext const& _context, Type _type)
117+
{
118+
TypeSystemHelpers helper{_context.analysis.typeSystem()};
119+
_type = _context.env->resolve(_type);
120+
solAssert(std::holds_alternative<TypeConstant>(_type), "No monomorphized type.");
121+
122+
// type -> # stack slots
123+
// unit, itself -> 0
124+
// void, literals(integer), typeFunction -> error (maybe generate a revert)
125+
// word, bool, function -> 1
126+
// pair -> sum(stackSize(args))
127+
// user-defined -> stackSize(underlying type)
128+
TypeConstant typeConstant = std::get<TypeConstant>(_type);
129+
if (
130+
helper.isPrimitiveType(_type, PrimitiveType::Unit) ||
131+
helper.isPrimitiveType(_type, PrimitiveType::Itself)
132+
)
133+
return 0;
134+
else if (
135+
helper.isPrimitiveType(_type, PrimitiveType::Bool) ||
136+
helper.isPrimitiveType(_type, PrimitiveType::Word)
137+
)
138+
{
139+
solAssert(typeConstant.arguments.empty(), "Primitive type Bool or Word should have no arguments.");
140+
return 1;
141+
}
142+
else if (helper.isFunctionType(_type))
143+
return 1;
144+
else if (
145+
helper.isPrimitiveType(_type, PrimitiveType::Integer) ||
146+
helper.isPrimitiveType(_type, PrimitiveType::Void) ||
147+
helper.isPrimitiveType(_type, PrimitiveType::TypeFunction)
148+
)
149+
solAssert(false, "Attempted to query the stack size of a type without stack representation.");
150+
else if (helper.isPrimitiveType(_type, PrimitiveType::Pair))
151+
{
152+
solAssert(typeConstant.arguments.size() == 2);
153+
return stackSize(_context, typeConstant.arguments.front()) + stackSize(_context, typeConstant.arguments.back());
154+
}
155+
else
156+
{
157+
Type underlyingType = _context.env->resolve(
158+
_context.analysis.annotation<TypeInference>().underlyingTypes.at(typeConstant.constructor));
159+
if (helper.isTypeConstant(underlyingType))
160+
return stackSize(_context, underlyingType);
161+
162+
TypeEnvironment env = _context.env->clone();
163+
Type genericFunctionType = helper.typeFunctionType(
164+
helper.tupleType(typeConstant.arguments),
165+
env.typeSystem().freshTypeVariable({}));
166+
solAssert(env.unify(genericFunctionType, underlyingType).empty());
167+
168+
Type resolvedType = env.resolveRecursive(genericFunctionType);
169+
auto [argumentType, resultType] = helper.destTypeFunctionType(resolvedType);
170+
return stackSize(_context, resultType);
171+
}
172+
173+
//TODO: sum types
174+
return 0;
175+
}
176+
115177
bool IRGeneratorForStatements::visit(TupleExpression const& _tupleExpression)
116178
{
117179
std::vector<std::string> components;
118180
for (auto const& component: _tupleExpression.components())
119181
{
120182
solUnimplementedAssert(component);
121183
component->accept(*this);
122-
components.emplace_back(IRNames::localVariable(*component));
184+
components.emplace_back(var(*component).commaSeparatedList());
123185
}
124186

125187
solUnimplementedAssert(false, "No support for tuples.");
@@ -144,10 +206,11 @@ bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _variab
144206
VariableDeclaration const* variableDeclaration = _variableDeclarationStatement.declarations().front().get();
145207
solAssert(variableDeclaration);
146208
// TODO: check the type of the variable; register local variable; initialize
147-
m_code << "let " << IRNames::localVariable(*variableDeclaration);
148209
if (_variableDeclarationStatement.initialValue())
149-
m_code << " := " << IRNames::localVariable(*_variableDeclarationStatement.initialValue());
150-
m_code << "\n";
210+
define(var(*variableDeclaration), var(*_variableDeclarationStatement.initialValue()));
211+
else
212+
declare(var(*variableDeclaration));
213+
151214
return false;
152215
}
153216

@@ -158,10 +221,8 @@ bool IRGeneratorForStatements::visit(ExpressionStatement const&)
158221

159222
bool IRGeneratorForStatements::visit(Identifier const& _identifier)
160223
{
161-
if (auto const* var = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
162-
{
163-
m_code << "let " << IRNames::localVariable(_identifier) << " := " << IRNames::localVariable(*var) << "\n";
164-
}
224+
if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
225+
define(var(_identifier), var(*variable));
165226
else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_identifier.annotation().referencedDeclaration))
166227
solAssert(m_expressionDeclaration.emplace(&_identifier, function).second);
167228
else if (auto const* typeClass = dynamic_cast<TypeClassDefinition const*>(_identifier.annotation().referencedDeclaration))
@@ -179,7 +240,8 @@ void IRGeneratorForStatements::endVisit(Return const& _return)
179240
{
180241
solAssert(_return.annotation().function, "Invalid return.");
181242
solAssert(_return.annotation().function->experimentalReturnExpression(), "Invalid return.");
182-
m_code << IRNames::localVariable(*_return.annotation().function->experimentalReturnExpression()) << " := " << IRNames::localVariable(*value) << "\n";
243+
auto returnExpression = _return.annotation().function->experimentalReturnExpression();
244+
assign(var(*returnExpression), var(*value));
183245
}
184246

185247
m_code << "leave\n";
@@ -201,13 +263,44 @@ void IRGeneratorForStatements::endVisit(BinaryOperation const& _binaryOperation)
201263
Type functionType = helper.functionType(helper.tupleType({leftType, rightType}), resultType);
202264
auto [typeClass, memberName] = m_context.analysis.annotation<TypeInference>().operators.at(_binaryOperation.getOperator());
203265
auto const& functionDefinition = resolveTypeClassFunction(typeClass, memberName, functionType);
204-
// TODO: deduplicate with FunctionCall
266+
std::string result = var(_binaryOperation).commaSeparatedList();
267+
if (!result.empty())
268+
m_code << "let " << result << " := ";
269+
m_code << buildFunctionCall(functionDefinition, functionType, _binaryOperation.arguments());
270+
}
271+
272+
std::string IRGeneratorForStatements::buildFunctionCall(FunctionDefinition const& _functionDefinition, Type _functionType, std::vector<ASTPointer<Expression const>> const& _arguments)
273+
{
274+
// Ensure type is resolved
205275
// TODO: get around resolveRecursive by passing the environment further down?
206-
functionType = m_context.env->resolveRecursive(functionType);
207-
m_context.enqueueFunctionDefinition(&functionDefinition, functionType);
208-
// TODO: account for return stack size
209-
m_code << "let " << IRNames::localVariable(_binaryOperation) << " := " << IRNames::function(*m_context.env, functionDefinition, functionType) << "("
210-
<< IRNames::localVariable(_binaryOperation.leftExpression()) << ", " << IRNames::localVariable(_binaryOperation.rightExpression()) << ")\n";
276+
Type resolvedFunctionType = m_context.env->resolveRecursive(_functionType);
277+
m_context.enqueueFunctionDefinition(&_functionDefinition, resolvedFunctionType);
278+
279+
std::ostringstream output;
280+
output << IRNames::function(*m_context.env, _functionDefinition, resolvedFunctionType) << "(";
281+
if (_arguments.size() == 1)
282+
output << var(*_arguments.back()).commaSeparatedList();
283+
else if (_arguments.size() > 1)
284+
{
285+
for (auto arg: _arguments | ranges::views::drop_last(1))
286+
output << var(*arg).commaSeparatedList();
287+
output << var(*_arguments.back()).commaSeparatedListPrefixed();
288+
}
289+
output << ")\n";
290+
return output.str();
291+
}
292+
293+
void IRGeneratorForStatements::assign(IRVariable const& _lhs, IRVariable const& _rhs, bool _declare)
294+
{
295+
solAssert(stackSize(m_context, _lhs.type()) == stackSize(m_context, _rhs.type()));
296+
for (auto&& [lhsSlot, rhsSlot]: ranges::zip_view(_lhs.stackSlots(), _rhs.stackSlots()))
297+
m_code << (_declare ? "let " : "") << lhsSlot << " := " << rhsSlot << "\n";
298+
}
299+
300+
void IRGeneratorForStatements::declare(IRVariable const& _var)
301+
{
302+
if (_var.stackSize() > 0)
303+
m_code << "let " << _var.commaSeparatedList() << "\n";
211304
}
212305

213306
namespace
@@ -308,32 +401,23 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
308401
case Builtins::FromBool:
309402
case Builtins::Identity:
310403
solAssert(_functionCall.arguments().size() == 1);
311-
m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::localVariable(*_functionCall.arguments().front()) << "\n";
404+
define(var(_functionCall), var(*_functionCall.arguments().front()));
312405
return;
313406
case Builtins::ToBool:
314407
solAssert(_functionCall.arguments().size() == 1);
315-
m_code << "let " << IRNames::localVariable(_functionCall) << " := iszero(iszero(" << IRNames::localVariable(*_functionCall.arguments().front()) << "))\n";
408+
m_code << "let " << var(_functionCall).name() << " := iszero(iszero(" << var(*_functionCall.arguments().front()).name() << "))\n";
316409
return;
317410
}
318411
solAssert(false);
319412
}
320413
FunctionDefinition const* functionDefinition = dynamic_cast<FunctionDefinition const*>(std::get<Declaration const*>(declaration));
321414
solAssert(functionDefinition);
322-
// TODO: get around resolveRecursive by passing the environment further down?
323-
functionType = m_context.env->resolveRecursive(functionType);
324-
m_context.enqueueFunctionDefinition(functionDefinition, functionType);
325415
// TODO: account for return stack size
326416
solAssert(!functionDefinition->returnParameterList());
327-
if (functionDefinition->experimentalReturnExpression())
328-
m_code << "let " << IRNames::localVariable(_functionCall) << " := ";
329-
m_code << IRNames::function(*m_context.env, *functionDefinition, functionType) << "(";
330-
auto const& arguments = _functionCall.arguments();
331-
if (arguments.size() > 1)
332-
for (auto arg: arguments | ranges::views::drop_last(1))
333-
m_code << IRNames::localVariable(*arg) << ", ";
334-
if (!arguments.empty())
335-
m_code << IRNames::localVariable(*arguments.back());
336-
m_code << ")\n";
417+
std::string result = var(_functionCall).commaSeparatedList();
418+
if (!result.empty())
419+
m_code << "let " << result << " := ";
420+
m_code << buildFunctionCall(*functionDefinition, functionType, _functionCall.arguments());
337421
}
338422

339423
bool IRGeneratorForStatements::visit(FunctionCall const&)
@@ -356,7 +440,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
356440
_ifStatement.condition().accept(*this);
357441
if (_ifStatement.falseStatement())
358442
{
359-
m_code << "switch " << IRNames::localVariable(_ifStatement.condition()) << " {\n";
443+
m_code << "switch " << var(_ifStatement.condition()).name() << " {\n";
360444
m_code << "case 0 {\n";
361445
_ifStatement.falseStatement()->accept(*this);
362446
m_code << "}\n";
@@ -366,7 +450,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
366450
}
367451
else
368452
{
369-
m_code << "if " << IRNames::localVariable(_ifStatement.condition()) << " {\n";
453+
m_code << "if " << var(_ifStatement.condition()).name() << " {\n";
370454
_ifStatement.trueStatement().accept(*this);
371455
m_code << "}\n";
372456
}
@@ -380,9 +464,8 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
380464
solAssert(lhs, "Can only assign to identifiers.");
381465
auto const* lhsVar = dynamic_cast<VariableDeclaration const*>(lhs->annotation().referencedDeclaration);
382466
solAssert(lhsVar, "Can only assign to identifiers referring to variables.");
383-
m_code << IRNames::localVariable(*lhsVar) << " := " << IRNames::localVariable(_assignment.rightHandSide()) << "\n";
384-
385-
m_code << "let " << IRNames::localVariable(_assignment) << " := " << IRNames::localVariable(*lhsVar) << "\n";
467+
assign(var(*lhsVar), var(_assignment.rightHandSide()));
468+
define(var(_assignment), var(*lhsVar));
386469
return false;
387470
}
388471

Diff for: libsolidity/experimental/codegen/IRGeneratorForStatements.h

+20
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#pragma once
2020

2121
#include <libsolidity/experimental/codegen/IRGenerationContext.h>
22+
#include <libsolidity/experimental/codegen/IRVariable.h>
23+
2224
#include <libsolidity/ast/ASTVisitor.h>
2325

2426
#include <functional>
@@ -34,6 +36,7 @@ class IRGeneratorForStatements: public ASTConstVisitor
3436
IRGeneratorForStatements(IRGenerationContext& _context): m_context(_context) {}
3537

3638
std::string generate(ASTNode const& _node);
39+
static std::size_t stackSize(IRGenerationContext const& _context, Type _type);
3740
private:
3841
bool visit(ExpressionStatement const& _expressionStatement) override;
3942
bool visit(Block const& _block) override;
@@ -54,6 +57,14 @@ class IRGeneratorForStatements: public ASTConstVisitor
5457
void endVisit(Return const& _return) override;
5558
/// Default visit will reject all AST nodes that are not explicitly supported.
5659
bool visitNode(ASTNode const& _node) override;
60+
61+
/// Defines @a _var using the value of @a _value. It declares and assign the variable.
62+
void define(IRVariable const& _var, IRVariable const& _value) { assign(_var, _value, true); }
63+
/// Assigns @a _var to the value of @a _value. It does not declare the variable.
64+
void assign(IRVariable const& _var, IRVariable const& _value, bool _declare = false);
65+
/// Declares variable @a _var.
66+
void declare(IRVariable const& _var);
67+
5768
IRGenerationContext& m_context;
5869
std::stringstream m_code;
5970
enum class Builtins
@@ -63,6 +74,15 @@ class IRGeneratorForStatements: public ASTConstVisitor
6374
ToBool
6475
};
6576
std::map<Expression const*, std::variant<Declaration const*, Builtins>> m_expressionDeclaration;
77+
78+
std::string buildFunctionCall(FunctionDefinition const& _functionDefinition, Type _functionType, std::vector<ASTPointer<Expression const>> const& _arguments);
79+
80+
template<typename IRVariableType>
81+
IRVariable var(IRVariableType const& _var) const
82+
{
83+
return IRVariable(_var, type(_var), stackSize(m_context, type(_var)));
84+
}
85+
6686
Type type(ASTNode const& _node) const;
6787

6888
FunctionDefinition const& resolveTypeClassFunction(TypeClass _class, std::string _name, Type _type);

0 commit comments

Comments
 (0)