33
33
#include < libsolidity/experimental/codegen/Common.h>
34
34
35
35
#include < range/v3/view/drop_last.hpp>
36
+ #include < range/v3/view/zip.hpp>
36
37
37
38
using namespace solidity ;
38
39
using namespace solidity ::util;
@@ -47,9 +48,70 @@ std::string IRGeneratorForStatements::generate(ASTNode const& _node)
47
48
}
48
49
49
50
50
- namespace
51
+ namespace solidity ::frontend::experimental
51
52
{
52
53
54
+ static std::size_t stackSize (IRGenerationContext const & _context, Type _type)
55
+ {
56
+ TypeSystemHelpers helper{_context.analysis .typeSystem ()};
57
+ _type = _context.env ->resolve (_type);
58
+ solAssert (std::holds_alternative<TypeConstant>(_type), " No monomorphized type." );
59
+
60
+ // type -> # stack slots
61
+ // unit, itself -> 0
62
+ // void, literals(integer), typeFunction -> error (maybe generate a revert)
63
+ // word, bool, function -> 1
64
+ // pair -> sum(stackSize(args))
65
+ // user-defined -> stackSize(underlying type)
66
+ TypeConstant typeConstant = std::get<TypeConstant>(_type);
67
+ if (
68
+ helper.isPrimitiveType (_type, PrimitiveType::Unit) ||
69
+ helper.isPrimitiveType (_type, PrimitiveType::Itself)
70
+ )
71
+ return 0 ;
72
+ else if (
73
+ helper.isPrimitiveType (_type, PrimitiveType::Bool) ||
74
+ helper.isPrimitiveType (_type, PrimitiveType::Word)
75
+ )
76
+ {
77
+ solAssert (typeConstant.arguments .empty (), " Primitive type Bool or Word should have no arguments." );
78
+ return 1 ;
79
+ }
80
+ else if (helper.isFunctionType (_type))
81
+ return 1 ;
82
+ else if (
83
+ helper.isPrimitiveType (_type, PrimitiveType::Integer) ||
84
+ helper.isPrimitiveType (_type, PrimitiveType::Void) ||
85
+ helper.isPrimitiveType (_type, PrimitiveType::TypeFunction)
86
+ )
87
+ solAssert (false , " Attempted to query the stack size of a type without stack representation." );
88
+ else if (helper.isPrimitiveType (_type, PrimitiveType::Pair))
89
+ {
90
+ solAssert (typeConstant.arguments .size () == 2 );
91
+ return stackSize (_context, typeConstant.arguments .front ()) + stackSize (_context, typeConstant.arguments .back ());
92
+ }
93
+ else
94
+ {
95
+ Type underlyingType = _context.env ->resolve (
96
+ _context.analysis .annotation <TypeInference>().underlyingTypes .at (typeConstant.constructor ));
97
+ if (helper.isTypeConstant (underlyingType))
98
+ return stackSize (_context, underlyingType);
99
+
100
+ TypeEnvironment env = _context.env ->clone ();
101
+ Type genericFunctionType = helper.typeFunctionType (
102
+ helper.tupleType (typeConstant.arguments ),
103
+ env.typeSystem ().freshTypeVariable ({}));
104
+ solAssert (env.unify (genericFunctionType, underlyingType).empty ());
105
+
106
+ Type resolvedType = env.resolveRecursive (genericFunctionType);
107
+ auto [argumentType, resultType] = helper.destTypeFunctionType (resolvedType);
108
+ return stackSize (_context, resultType);
109
+ }
110
+
111
+ // TODO: sum types
112
+ return 0 ;
113
+ }
114
+
53
115
struct CopyTranslate : public yul ::ASTCopier
54
116
{
55
117
CopyTranslate (
@@ -101,7 +163,7 @@ struct CopyTranslate: public yul::ASTCopier
101
163
auto type = m_context.analysis .annotation <TypeInference>(*varDecl).type ;
102
164
solAssert (type);
103
165
solAssert (m_context.env ->typeEquals (*type, m_context.analysis .typeSystem ().type (PrimitiveType::Word, {})));
104
- std::string value = IRNames::localVariable ( *varDecl);
166
+ std::string value = IRVariable{ *varDecl, *type, stackSize (m_context, *type)}. name ( );
105
167
return yul::Identifier{_identifier.debugData , yul::YulString{value}};
106
168
}
107
169
@@ -110,16 +172,14 @@ struct CopyTranslate: public yul::ASTCopier
110
172
std::map<yul::Identifier const *, InlineAssemblyAnnotation::ExternalIdentifierInfo> m_references;
111
173
};
112
174
113
- }
114
-
115
175
bool IRGeneratorForStatements::visit (TupleExpression const & _tupleExpression)
116
176
{
117
177
std::vector<std::string> components;
118
178
for (auto const & component: _tupleExpression.components ())
119
179
{
120
180
solUnimplementedAssert (component);
121
181
component->accept (*this );
122
- components.emplace_back (IRNames::localVariable (*component));
182
+ components.emplace_back (var (*component). commaSeparatedList ( ));
123
183
}
124
184
125
185
solUnimplementedAssert (false , " No support for tuples." );
@@ -144,10 +204,11 @@ bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _variab
144
204
VariableDeclaration const * variableDeclaration = _variableDeclarationStatement.declarations ().front ().get ();
145
205
solAssert (variableDeclaration);
146
206
// TODO: check the type of the variable; register local variable; initialize
147
- m_code << " let " << IRNames::localVariable (*variableDeclaration);
148
207
if (_variableDeclarationStatement.initialValue ())
149
- m_code << " := " << IRNames::localVariable (*_variableDeclarationStatement.initialValue ());
150
- m_code << " \n " ;
208
+ define (var (*variableDeclaration), var (*_variableDeclarationStatement.initialValue ()));
209
+ else
210
+ declare (var (*variableDeclaration));
211
+
151
212
return false ;
152
213
}
153
214
@@ -158,10 +219,8 @@ bool IRGeneratorForStatements::visit(ExpressionStatement const&)
158
219
159
220
bool IRGeneratorForStatements::visit (Identifier const & _identifier)
160
221
{
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
- }
222
+ if (auto const * variable = dynamic_cast <VariableDeclaration const *>(_identifier.annotation ().referencedDeclaration ))
223
+ define (var (_identifier), var (*variable));
165
224
else if (auto const * function = dynamic_cast <FunctionDefinition const *>(_identifier.annotation ().referencedDeclaration ))
166
225
solAssert (m_expressionDeclaration.emplace (&_identifier, function).second );
167
226
else if (auto const * typeClass = dynamic_cast <TypeClassDefinition const *>(_identifier.annotation ().referencedDeclaration ))
@@ -179,7 +238,8 @@ void IRGeneratorForStatements::endVisit(Return const& _return)
179
238
{
180
239
solAssert (_return.annotation ().function , " Invalid return." );
181
240
solAssert (_return.annotation ().function ->experimentalReturnExpression (), " Invalid return." );
182
- m_code << IRNames::localVariable (*_return.annotation ().function ->experimentalReturnExpression ()) << " := " << IRNames::localVariable (*value) << " \n " ;
241
+ auto returnExpression = _return.annotation ().function ->experimentalReturnExpression ();
242
+ assign (var (*returnExpression), var (*value));
183
243
}
184
244
185
245
m_code << " leave\n " ;
@@ -201,13 +261,44 @@ void IRGeneratorForStatements::endVisit(BinaryOperation const& _binaryOperation)
201
261
Type functionType = helper.functionType (helper.tupleType ({leftType, rightType}), resultType);
202
262
auto [typeClass, memberName] = m_context.analysis .annotation <TypeInference>().operators .at (_binaryOperation.getOperator ());
203
263
auto const & functionDefinition = resolveTypeClassFunction (typeClass, memberName, functionType);
204
- // TODO: deduplicate with FunctionCall
264
+ std::string result = var (_binaryOperation).commaSeparatedList ();
265
+ if (!result.empty ())
266
+ m_code << " let " << result << " := " ;
267
+ m_code << buildFunctionCall (functionDefinition, functionType, _binaryOperation.arguments ());
268
+ }
269
+
270
+ std::string IRGeneratorForStatements::buildFunctionCall (FunctionDefinition const & _functionDefinition, Type _functionType, std::vector<ASTPointer<Expression const >> const & _arguments)
271
+ {
272
+ // Ensure type is resolved
205
273
// 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 " ;
274
+ Type resolvedFunctionType = m_context.env ->resolveRecursive (_functionType);
275
+ m_context.enqueueFunctionDefinition (&_functionDefinition, resolvedFunctionType);
276
+
277
+ std::ostringstream output;
278
+ output << IRNames::function (*m_context.env , _functionDefinition, resolvedFunctionType) << " (" ;
279
+ if (_arguments.size () == 1 )
280
+ output << var (*_arguments.back ()).commaSeparatedList ();
281
+ else if (_arguments.size () > 1 )
282
+ {
283
+ for (auto arg: _arguments | ranges::views::drop_last (1 ))
284
+ output << var (*arg).commaSeparatedList ();
285
+ output << var (*_arguments.back ()).commaSeparatedListPrefixed ();
286
+ }
287
+ output << " )\n " ;
288
+ return output.str ();
289
+ }
290
+
291
+ void IRGeneratorForStatements::assign (IRVariable const & _lhs, IRVariable const & _rhs, bool _declare)
292
+ {
293
+ solAssert (stackSize (m_context, _lhs.type ()) == stackSize (m_context, _rhs.type ()));
294
+ for (auto && [lhsSlot, rhsSlot]: ranges::zip_view (_lhs.stackSlots (), _rhs.stackSlots ()))
295
+ m_code << (_declare ? " let " : " " ) << lhsSlot << " := " << rhsSlot << " \n " ;
296
+ }
297
+
298
+ void IRGeneratorForStatements::declare (IRVariable const & _var)
299
+ {
300
+ if (_var.stackSize () > 0 )
301
+ m_code << " let " << _var.commaSeparatedList () << " \n " ;
211
302
}
212
303
213
304
namespace
@@ -308,32 +399,23 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
308
399
case Builtins::FromBool:
309
400
case Builtins::Identity:
310
401
solAssert (_functionCall.arguments ().size () == 1 );
311
- m_code << " let " << IRNames::localVariable ( _functionCall) << " := " << IRNames::localVariable (*_functionCall.arguments ().front ()) << " \n " ;
402
+ define ( var ( _functionCall), var (*_functionCall.arguments ().front ())) ;
312
403
return ;
313
404
case Builtins::ToBool:
314
405
solAssert (_functionCall.arguments ().size () == 1 );
315
- m_code << " let " << IRNames::localVariable (_functionCall) << " := iszero(iszero(" << IRNames::localVariable (*_functionCall.arguments ().front ()) << " ))\n " ;
406
+ m_code << " let " << var (_functionCall). name () << " := iszero(iszero(" << var (*_functionCall.arguments ().front ()). name ( ) << " ))\n " ;
316
407
return ;
317
408
}
318
409
solAssert (false );
319
410
}
320
411
FunctionDefinition const * functionDefinition = dynamic_cast <FunctionDefinition const *>(std::get<Declaration const *>(declaration));
321
412
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);
325
413
// TODO: account for return stack size
326
414
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 " ;
415
+ std::string result = var (_functionCall).commaSeparatedList ();
416
+ if (!result.empty ())
417
+ m_code << " let " << result << " := " ;
418
+ m_code << buildFunctionCall (*functionDefinition, functionType, _functionCall.arguments ());
337
419
}
338
420
339
421
bool IRGeneratorForStatements::visit (FunctionCall const &)
@@ -356,7 +438,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
356
438
_ifStatement.condition ().accept (*this );
357
439
if (_ifStatement.falseStatement ())
358
440
{
359
- m_code << " switch " << IRNames::localVariable (_ifStatement.condition ()) << " {\n " ;
441
+ m_code << " switch " << var (_ifStatement.condition ()). name ( ) << " {\n " ;
360
442
m_code << " case 0 {\n " ;
361
443
_ifStatement.falseStatement ()->accept (*this );
362
444
m_code << " }\n " ;
@@ -366,7 +448,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
366
448
}
367
449
else
368
450
{
369
- m_code << " if " << IRNames::localVariable (_ifStatement.condition ()) << " {\n " ;
451
+ m_code << " if " << var (_ifStatement.condition ()). name ( ) << " {\n " ;
370
452
_ifStatement.trueStatement ().accept (*this );
371
453
m_code << " }\n " ;
372
454
}
@@ -380,9 +462,8 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment)
380
462
solAssert (lhs, " Can only assign to identifiers." );
381
463
auto const * lhsVar = dynamic_cast <VariableDeclaration const *>(lhs->annotation ().referencedDeclaration );
382
464
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 " ;
465
+ assign (var (*lhsVar), var (_assignment.rightHandSide ()));
466
+ define (var (_assignment), var (*lhsVar));
386
467
return false ;
387
468
}
388
469
@@ -391,3 +472,5 @@ bool IRGeneratorForStatements::visitNode(ASTNode const&)
391
472
{
392
473
solAssert (false , " Unsupported AST node during statement code generation." );
393
474
}
475
+
476
+ }
0 commit comments