source: src/Tuples/TupleAssignment.cc@ 7f2bfb7

Last change on this file since 7f2bfb7 was e580aa5, checked in by Andrew Beach <ajbeach@…>, 2 years ago

Round of clean-up in Tuples directory. (Skipping TupleExpansion, which will be fused together later.)

  • Property mode set to 100644
File size: 13.6 KB
RevLine 
[51587aa]1//
2// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
[5af62f1]7// TupleAssignment.cc --
[51587aa]8//
[843054c2]9// Author : Rodolfo G. Esteves
[51587aa]10// Created On : Mon May 18 07:44:20 2015
[39d8950]11// Last Modified By : Andrew Beach
12// Last Modified On : Wed Mar 16 14:06:00 2022
13// Update Count : 10
[51587aa]14//
15
[03321e4]16#include <algorithm> // for transform
17#include <cassert> // for assert
18#include <iterator> // for back_insert_iterator, back...
19#include <list> // for _List_const_iterator, _Lis...
20#include <memory> // for unique_ptr, allocator_trai...
21#include <string> // for string
[78315272]22#include <vector>
[51b73452]23
[234b1cb]24#include "AST/Decl.hpp"
25#include "AST/Init.hpp"
26#include "AST/Pass.hpp"
27#include "AST/Stmt.hpp"
28#include "AST/TypeEnvironment.hpp"
[8135d4c]29#include "CodeGen/OperatorTable.h"
[03321e4]30#include "Common/UniqueName.h" // for UniqueName
[234b1cb]31#include "Common/utility.h" // for splice, zipWith
[03321e4]32#include "Explode.h" // for explode
33#include "InitTweak/GenInit.h" // for genCtorInit
34#include "InitTweak/InitTweak.h" // for getPointerBase, isAssignment
35#include "ResolvExpr/Cost.h" // for Cost
36#include "ResolvExpr/Resolver.h" // for resolveCtorInit
[c43c171]37#include "ResolvExpr/typeops.h" // for combos
[51b73452]38
[4b6ef70]39#if 0
40#define PRINT(x) x
41#else
42#define PRINT(x)
43#endif
44
[51b73452]45namespace Tuples {
[432ce7a]46
[234b1cb]47namespace {
[7870799]48
[e580aa5]49/// Checks if `expr` is of tuple type.
50bool isTuple( const ast::Expr * expr ) {
51 if ( !expr ) return false;
52 assert( expr->result );
53 return dynamic_cast< const ast::TupleType * >( expr->result->stripReferences() );
54}
55
56/// Checks if `expr` is of tuple type or a cast to one.
57bool refToTuple( const ast::Expr * expr ) {
58 assert( expr->result );
59 // Check for function returning tuple of reference types.
60 if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) {
61 return refToTuple( castExpr->arg );
62 } else {
63 return isTuple( expr );
[432ce7a]64 }
[e580aa5]65}
[234b1cb]66
[e580aa5]67/// Dispatcher for tuple (multiple and mass) assignment operations.
68class TupleAssignSpotter final {
69 /// Actually finds tuple assignment operations, by subclass.
70 struct Matcher {
71 ResolvExpr::CandidateList lhs, rhs;
72 TupleAssignSpotter & spotter;
73 CodeLocation location;
74 ResolvExpr::Cost baseCost;
75 std::vector< ast::ptr< ast::ObjectDecl > > tmpDecls;
76 ast::TypeEnvironment env;
77 ast::OpenVarSet open;
78 ast::AssertionSet need;
79
80 void combineState( const ResolvExpr::Candidate & cand ) {
81 env.simpleCombine( cand.env );
82 ast::mergeOpenVars( open, cand.open );
83 need.insert( cand.need.begin(), cand.need.end() );
84 }
[234b1cb]85
[e580aa5]86 Matcher(
87 TupleAssignSpotter & s, const CodeLocation & loc,
88 const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
89 : lhs( l ), rhs( r ), spotter( s ), location( loc ),
90 baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ), tmpDecls(),
91 env(), open(), need() {
92 for ( auto & cand : lhs ) combineState( *cand );
93 for ( auto & cand : rhs ) combineState( *cand );
94 }
95 virtual ~Matcher() = default;
[234b1cb]96
[e580aa5]97 virtual std::vector< ast::ptr< ast::Expr > > match() = 0;
[234b1cb]98
[e580aa5]99 /// Removes environments from subexpressions within statement expressions, which could
100 /// throw off later passes like those in Box which rely on PolyMutator, and adds the
101 /// bindings to the env.
102 struct EnvRemover {
103 /// Environment to hoist ExprStmt environments to.
104 ast::TypeEnvironment & tenv;
[234b1cb]105
[e580aa5]106 EnvRemover( ast::TypeEnvironment & e ) : tenv( e ) {}
[234b1cb]107
[e580aa5]108 const ast::ExprStmt * previsit( const ast::ExprStmt * stmt ) {
109 if ( stmt->expr->env ) {
110 tenv.add( *stmt->expr->env );
111 ast::ExprStmt * mut = mutate( stmt );
112 mut->expr.get_and_mutate()->env = nullptr;
113 return mut;
[234b1cb]114 }
[e580aa5]115 return stmt;
[234b1cb]116 }
[e580aa5]117 };
[234b1cb]118
[e580aa5]119 ast::ObjectDecl * newObject( UniqueName & namer, const ast::Expr * expr ) {
120 assert( expr->result && !expr->result->isVoid() );
121
122 ast::ObjectDecl * ret = new ast::ObjectDecl(
123 location, namer.newName(), expr->result, new ast::SingleInit( location, expr ),
124 ast::Storage::Classes{}, ast::Linkage::Cforall );
125
126 // If expression type is a reference, just need an initializer, otherwise construct.
127 if ( ! expr->result.as< ast::ReferenceType >() ) {
128 // Resolve ctor/dtor for the new object.
129 ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit(
130 InitTweak::genCtorInit( location, ret ), spotter.crntFinder.context );
131 // Remove environments from subexpressions of stmtExpr.
132 ast::Pass< EnvRemover > rm( env );
133 ret->init = ctorInit->accept( rm );
[234b1cb]134 }
135
[e580aa5]136 PRINT( std::cerr << "new object: " << ret << std::endl; )
137 return ret;
138 }
[234b1cb]139
[e580aa5]140 ast::UntypedExpr * createFunc(
141 const std::string & fname, const ast::ObjectDecl * left,
142 const ast::ObjectDecl * right
143 ) {
144 assert( left );
145 std::vector< ast::ptr< ast::Expr > > args;
146 args.emplace_back( new ast::VariableExpr( location, left ) );
147 if ( right ) { args.emplace_back( new ast::VariableExpr( location, right ) ); }
148
149 if ( left->type->referenceDepth() > 1 && CodeGen::isConstructor( fname ) ) {
150 args.front() = new ast::AddressExpr( location, args.front() );
151 if ( right ) { args.back() = new ast::AddressExpr( location, args.back() ); }
152 return new ast::UntypedExpr(
153 location, new ast::NameExpr( location, "?=?" ), std::move( args ) );
154 } else {
155 return new ast::UntypedExpr(
156 location, new ast::NameExpr( location, fname ), std::move( args ) );
[234b1cb]157 }
[e580aa5]158 }
159 };
[234b1cb]160
[e580aa5]161 /// Finds mass-assignment operations.
162 struct MassAssignMatcher final : public Matcher {
163 MassAssignMatcher(
164 TupleAssignSpotter & s, const CodeLocation & loc,
165 const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
166 : Matcher( s, loc, l, r ) {}
167
168 std::vector< ast::ptr< ast::Expr > > match() override {
169 static UniqueName lhsNamer( "__massassign_L" );
170 static UniqueName rhsNamer( "__massassign_R" );
171 // Empty tuple case falls into this matcher.
172 assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 );
173
174 ast::ptr< ast::ObjectDecl > rtmp =
175 1 == rhs.size() ? newObject( rhsNamer, rhs.front()->expr ) : nullptr;
176
177 std::vector< ast::ptr< ast::Expr > > out;
178 for ( ResolvExpr::CandidateRef & lhsCand : lhs ) {
179 // Create a temporary object for each value in
180 // the LHS and create a call involving the RHS.
181 ast::ptr< ast::ObjectDecl > ltmp = newObject( lhsNamer, lhsCand->expr );
182 out.emplace_back( createFunc( spotter.fname, ltmp, rtmp ) );
183 tmpDecls.emplace_back( std::move( ltmp ) );
184 }
185 if ( rtmp ) tmpDecls.emplace_back( std::move( rtmp ) );
[7870799]186
[e580aa5]187 return out;
188 }
189 };
[7870799]190
[e580aa5]191 /// Finds multiple-assignment operations.
192 struct MultipleAssignMatcher final : public Matcher {
193 MultipleAssignMatcher(
194 TupleAssignSpotter & s, const CodeLocation & loc,
195 const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
196 : Matcher( s, loc, l, r ) {}
197
198 std::vector< ast::ptr< ast::Expr > > match() override {
199 static UniqueName lhsNamer( "__multassign_L" );
200 static UniqueName rhsNamer( "__multassign_R" );
201
202 if ( lhs.size() != rhs.size() ) return {};
203
204 // Produce a new temporary object for each value in
205 // the LHS and RHS and pairwise create the calls.
206 std::vector< ast::ptr< ast::ObjectDecl > > ltmp, rtmp;
207
208 std::vector< ast::ptr< ast::Expr > > out;
209 for ( unsigned i = 0; i < lhs.size(); ++i ) {
210 ResolvExpr::CandidateRef & lhsCand = lhs[i];
211 ResolvExpr::CandidateRef & rhsCand = rhs[i];
212
213 // Convert RHS to LHS type minus one reference --
214 // important for case where LHS is && and RHS is lvalue.
215 auto lhsType = lhsCand->expr->result.strict_as< ast::ReferenceType >();
216 rhsCand->expr = new ast::CastExpr( rhsCand->expr, lhsType->base );
217 ast::ptr< ast::ObjectDecl > lobj = newObject( lhsNamer, lhsCand->expr );
218 ast::ptr< ast::ObjectDecl > robj = newObject( rhsNamer, rhsCand->expr );
219 out.emplace_back( createFunc( spotter.fname, lobj, robj ) );
220 ltmp.emplace_back( std::move( lobj ) );
221 rtmp.emplace_back( std::move( robj ) );
222
223 // Resolve the cast expression so that rhsCand return type is bound
224 // by the cast type as needed, and transfer the resulting environment.
225 ResolvExpr::CandidateFinder finder( spotter.crntFinder.context, env );
226 finder.find( rhsCand->expr, ResolvExpr::ResolvMode::withAdjustment() );
227 assert( 1 == finder.candidates.size() );
228 env = std::move( finder.candidates.front()->env );
[234b1cb]229 }
230
[e580aa5]231 splice( tmpDecls, ltmp );
232 splice( tmpDecls, rtmp );
[7870799]233
[e580aa5]234 return out;
235 }
236 };
[234b1cb]237
[e580aa5]238 ResolvExpr::CandidateFinder & crntFinder;
239 std::string fname;
240 std::unique_ptr< Matcher > matcher;
241
242public:
243 TupleAssignSpotter( ResolvExpr::CandidateFinder & f )
244 : crntFinder( f ), fname(), matcher() {}
245
246 // Find left- and right-hand-sides for mass or multiple assignment.
247 void spot(
248 const ast::UntypedExpr * expr, std::vector< ResolvExpr::CandidateFinder > & args
249 ) {
250 if ( auto op = expr->func.as< ast::NameExpr >() ) {
251 // Skip non-assignment functions.
252 if ( !CodeGen::isCtorDtorAssign( op->name ) ) return;
253 fname = op->name;
254
255 // Handled by CandidateFinder if applicable (both odd cases).
256 if ( args.empty() || ( 1 == args.size() && CodeGen::isAssignment( fname ) ) ) {
257 return;
258 }
[234b1cb]259
[e580aa5]260 // Look over all possible left-hand-side.
261 for ( ResolvExpr::CandidateRef & lhsCand : args[0] ) {
262 // Skip non-tuple LHS.
263 if ( !refToTuple( lhsCand->expr ) ) continue;
[234b1cb]264
[e580aa5]265 // Explode is aware of casts - ensure every LHS
266 // is sent into explode with a reference cast.
267 if ( !lhsCand->expr.as< ast::CastExpr >() ) {
268 lhsCand->expr = new ast::CastExpr(
269 lhsCand->expr, new ast::ReferenceType( lhsCand->expr->result ) );
270 }
[234b1cb]271
[e580aa5]272 // Explode the LHS so that each field of a tuple-valued expr is assigned.
273 ResolvExpr::CandidateList lhs;
274 explode( *lhsCand, crntFinder.context.symtab, back_inserter(lhs), true );
275 for ( ResolvExpr::CandidateRef & cand : lhs ) {
276 // Each LHS value must be a reference - some come in
277 // with a cast, if not just cast to reference here.
278 if ( !cand->expr->result.as< ast::ReferenceType >() ) {
279 cand->expr = new ast::CastExpr(
280 cand->expr, new ast::ReferenceType( cand->expr->result ) );
[234b1cb]281 }
[e580aa5]282 }
[234b1cb]283
[e580aa5]284 if ( 1 == args.size() ) {
285 // Mass default-initialization/destruction.
286 ResolvExpr::CandidateList rhs{};
287 matcher.reset( new MassAssignMatcher( *this, expr->location, lhs, rhs ) );
288 match();
289 } else if ( 2 == args.size() ) {
290 for ( const ResolvExpr::CandidateRef & rhsCand : args[1] ) {
291 ResolvExpr::CandidateList rhs;
292 if ( isTuple( rhsCand->expr ) ) {
293 // Multiple assignment:
294 explode( *rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true );
[7870799]295 matcher.reset(
[e580aa5]296 new MultipleAssignMatcher( *this, expr->location, lhs, rhs ) );
297 } else {
298 // Mass assignment:
299 rhs.emplace_back( rhsCand );
300 matcher.reset(
301 new MassAssignMatcher( *this, expr->location, lhs, rhs ) );
[234b1cb]302 }
[e580aa5]303 match();
304 }
305 } else {
306 // Expand all possible RHS possibilities.
307 std::vector< ResolvExpr::CandidateList > rhsCands;
308 combos(
309 std::next( args.begin(), 1 ), args.end(), back_inserter( rhsCands ) );
310 for ( const ResolvExpr::CandidateList & rhsCand : rhsCands ) {
311 // Multiple assignment:
312 ResolvExpr::CandidateList rhs;
313 explode( rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true );
314 matcher.reset(
315 new MultipleAssignMatcher( *this, expr->location, lhs, rhs ) );
316 match();
[234b1cb]317 }
318 }
319 }
320 }
[e580aa5]321 }
[234b1cb]322
[e580aa5]323 void match() {
324 assert( matcher );
[234b1cb]325
[e580aa5]326 std::vector< ast::ptr< ast::Expr > > newAssigns = matcher->match();
[234b1cb]327
[e580aa5]328 if ( !( matcher->lhs.empty() && matcher->rhs.empty() ) ) {
329 // If both LHS and RHS are empty than this is the empty tuple
330 // case, wherein it's okay for newAssigns to be empty. Otherwise,
331 // return early so that no new candidates are generated.
332 if ( newAssigns.empty() ) return;
333 }
[234b1cb]334
[e580aa5]335 ResolvExpr::CandidateList crnt;
336 // Now resolve new assignments.
337 for ( const ast::Expr * expr : newAssigns ) {
338 PRINT(
339 std::cerr << "== resolving tuple assign ==" << std::endl;
340 std::cerr << expr << std::endl;
341 )
342
343 ResolvExpr::CandidateFinder finder( crntFinder.context, matcher->env );
344 finder.allowVoid = true;
345
346 try {
347 finder.find( expr, ResolvExpr::ResolvMode::withAdjustment() );
348 } catch (...) {
349 // No match is not failure, just that this tuple assignment is invalid.
350 return;
[234b1cb]351 }
352
[e580aa5]353 ResolvExpr::CandidateList & cands = finder.candidates;
354 assert( 1 == cands.size() );
355 assert( cands.front()->expr );
356 crnt.emplace_back( std::move( cands.front() ) );
357 }
[234b1cb]358
[e580aa5]359 // extract expressions from the assignment candidates to produce a list of assignments
360 // that together form a sigle candidate
361 std::vector< ast::ptr< ast::Expr > > solved;
362 for ( ResolvExpr::CandidateRef & cand : crnt ) {
363 solved.emplace_back( cand->expr );
364 matcher->combineState( *cand );
[234b1cb]365 }
[e580aa5]366
367 crntFinder.candidates.emplace_back( std::make_shared< ResolvExpr::Candidate >(
368 new ast::TupleAssignExpr(
369 matcher->location, std::move( solved ), std::move( matcher->tmpDecls ) ),
370 std::move( matcher->env ), std::move( matcher->open ), std::move( matcher->need ),
371 ResolvExpr::sumCost( crnt ) + matcher->baseCost ) );
372 }
373};
374
[234b1cb]375} // anonymous namespace
376
[7870799]377void handleTupleAssignment(
378 ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
[234b1cb]379 std::vector< ResolvExpr::CandidateFinder > & args
380) {
[e580aa5]381 TupleAssignSpotter spotter( finder );
[234b1cb]382 spotter.spot( assign, args );
383}
384
[51b73452]385} // namespace Tuples
[51587aa]386
387// Local Variables: //
388// tab-width: 4 //
389// mode: c++ //
390// compile-command: "make install" //
391// End: //
Note: See TracBrowser for help on using the repository browser.