source: src/Tuples/TupleAssignment.cc @ 4883712

Last change on this file since 4883712 was e580aa5, checked in by Andrew Beach <ajbeach@…>, 7 months 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
Line 
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//
7// TupleAssignment.cc --
8//
9// Author           : Rodolfo G. Esteves
10// Created On       : Mon May 18 07:44:20 2015
11// Last Modified By : Andrew Beach
12// Last Modified On : Wed Mar 16 14:06:00 2022
13// Update Count     : 10
14//
15
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
22#include <vector>
23
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"
29#include "CodeGen/OperatorTable.h"
30#include "Common/UniqueName.h"             // for UniqueName
31#include "Common/utility.h"                // for splice, zipWith
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
37#include "ResolvExpr/typeops.h"            // for combos
38
39#if 0
40#define PRINT(x) x
41#else
42#define PRINT(x)
43#endif
44
45namespace Tuples {
46
47namespace {
48
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 );
64        }
65}
66
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                }
85
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;
96
97                virtual std::vector< ast::ptr< ast::Expr > > match() = 0;
98
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;
105
106                        EnvRemover( ast::TypeEnvironment & e ) : tenv( e ) {}
107
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;
114                                }
115                                return stmt;
116                        }
117                };
118
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 );
134                        }
135
136                        PRINT( std::cerr << "new object: " << ret << std::endl; )
137                        return ret;
138                }
139
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 ) );
157                        }
158                }
159        };
160
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 ) );
186
187                        return out;
188                }
189        };
190
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 );
229                        }
230
231                        splice( tmpDecls, ltmp );
232                        splice( tmpDecls, rtmp );
233
234                        return out;
235                }
236        };
237
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                        }
259
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;
264
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                                }
271
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 ) );
281                                        }
282                                }
283
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 );
295                                                        matcher.reset(
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 ) );
302                                                }
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();
317                                        }
318                                }
319                        }
320                }
321        }
322
323        void match() {
324                assert( matcher );
325
326                std::vector< ast::ptr< ast::Expr > > newAssigns = matcher->match();
327
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                }
334
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;
351                        }
352
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                }
358
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 );
365                }
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
375} // anonymous namespace
376
377void handleTupleAssignment(
378        ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
379        std::vector< ResolvExpr::CandidateFinder > & args
380) {
381        TupleAssignSpotter spotter( finder );
382        spotter.spot( assign, args );
383}
384
385} // namespace Tuples
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.