source: src/GenPoly/SpecializeNew.cpp @ c36814a

ADTast-experimentalpthread-emulation
Last change on this file since c36814a was c36814a, checked in by Andrew Beach <ajbeach@…>, 20 months ago

Adding 'final' and removing a redundent namespace in the post resolve new ast code.

  • Property mode set to 100644
File size: 16.0 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// SpecializeNew.cpp -- Generate thunks to specialize polymorphic functions.
8//
9// Author           : Andrew Beach
10// Created On       : Tue Jun  7 13:37:00 2022
11// Last Modified By : Andrew Beach
12// Last Modified On : Tue Jun  7 13:37:00 2022
13// Update Count     : 0
14//
15
16#include "Specialize.h"
17
18#include "AST/Pass.hpp"
19#include "AST/TypeEnvironment.hpp"       // for OpenVarSet, AssertionSet
20#include "Common/UniqueName.h"           // for UniqueName
21#include "GenPoly/GenPoly.h"             // for getFunctionType
22#include "InitTweak/InitTweak.h"         // for isIntrinsicCallExpr
23#include "ResolvExpr/FindOpenVars.h"     // for findOpenVars
24#include "ResolvExpr/TypeEnvironment.h"  // for FirstOpen, FirstClosed
25
26#include "AST/Print.hpp"
27
28namespace GenPoly {
29
30namespace {
31
32struct SpecializeCore final :
33                public ast::WithConstTypeSubstitution,
34                public ast::WithDeclsToAdd<>,
35                public ast::WithVisitorRef<SpecializeCore> {
36        std::string paramPrefix = "_p";
37
38        ast::ApplicationExpr * handleExplicitParams(
39                const ast::ApplicationExpr * expr );
40        const ast::Expr * createThunkFunction(
41                const CodeLocation & location,
42                const ast::FunctionType * funType,
43                const ast::Expr * actual,
44                const ast::InferredParams * inferParams );
45        const ast::Expr * doSpecialization(
46                const CodeLocation & location,
47                const ast::Type * formalType,
48                const ast::Expr * actual,
49                const ast::InferredParams * inferParams );
50
51        const ast::Expr * postvisit( const ast::ApplicationExpr * expr );
52        const ast::Expr * postvisit( const ast::CastExpr * expr );
53};
54
55const ast::InferredParams * getInferredParams( const ast::Expr * expr ) {
56        const ast::Expr::InferUnion & inferred = expr->inferred;
57        if ( inferred.hasParams() ) {
58                return &inferred.inferParams();
59        } else {
60                return nullptr;
61        }
62}
63
64// Check if both types have the same structure. The leaf (non-tuple) types
65// don't have to match but the tuples must match.
66bool isTupleStructureMatching( const ast::Type * t0, const ast::Type * t1 ) {
67        const ast::TupleType * tt0 = dynamic_cast<const ast::TupleType *>( t0 );
68        const ast::TupleType * tt1 = dynamic_cast<const ast::TupleType *>( t1 );
69        if ( tt0 && tt1 ) {
70                if ( tt0->size() != tt1->size() ) {
71                        return false;
72                }
73                for ( auto types : group_iterate( tt0->types, tt1->types ) ) {
74                        if ( !isTupleStructureMatching(
75                                        std::get<0>( types ), std::get<1>( types ) ) ) {
76                                return false;
77                        }
78                }
79                return true;
80        }
81        return (!tt0 && !tt1);
82}
83
84// The number of elements in a type if it is a flattened tuple.
85size_t flatTupleSize( const ast::Type * type ) {
86        if ( auto tuple = dynamic_cast<const ast::TupleType *>( type ) ) {
87                size_t sum = 0;
88                for ( auto t : *tuple ) {
89                        sum += flatTupleSize( t );
90                }
91                return sum;
92        } else {
93                return 1;
94        }
95}
96
97// Find the total number of components in a parameter list.
98size_t functionParameterSize( const ast::FunctionType * type ) {
99        size_t sum = 0;
100        for ( auto param : type->params ) {
101                sum += flatTupleSize( param );
102        }
103        return sum;
104}
105
106bool needsPolySpecialization(
107                const ast::Type * formalType,
108                const ast::Type * actualType,
109                const ast::TypeSubstitution * subs ) {
110        if ( !subs ) {
111                return false;
112        }
113
114        using namespace ResolvExpr;
115        ast::OpenVarSet openVars, closedVars;
116        ast::AssertionSet need, have;
117        findOpenVars( formalType, openVars, closedVars, need, have, FirstClosed );
118        findOpenVars( actualType, openVars, closedVars, need, have, FirstOpen );
119        for ( const ast::OpenVarSet::value_type & openVar : openVars ) {
120                const ast::Type * boundType = subs->lookup( openVar.first );
121                // If the variable is not bound, move onto the next variable.
122                if ( !boundType ) continue;
123
124                // Is the variable cound to another type variable?
125                if ( auto inst = dynamic_cast<const ast::TypeInstType *>( boundType ) ) {
126                        if ( closedVars.find( *inst ) == closedVars.end() ) {
127                                return true;
128                        }
129                // Otherwise, the variable is bound to a concrete type.
130                } else {
131                        return true;
132                }
133        }
134        // None of the type variables are bound.
135        return false;
136}
137
138bool needsTupleSpecialization(
139                const ast::Type * formalType, const ast::Type * actualType ) {
140        // Needs tuple specialization if the structure of the formal type and
141        // actual type do not match.
142
143        // This is the case if the formal type has ttype polymorphism, or if the structure  of tuple types
144        // between the function do not match exactly.
145        if ( const ast::FunctionType * ftype = getFunctionType( formalType ) ) {
146                // A pack in the parameter or return type requires specialization.
147                if ( ftype->isTtype() ) {
148                        return true;
149                }
150                // Conversion of 0 to a function type does not require specialization.
151                if ( dynamic_cast<const ast::ZeroType *>( actualType ) ) {
152                        return false;
153                }
154                const ast::FunctionType * atype =
155                        getFunctionType( actualType->stripReferences() );
156                assertf( atype,
157                        "formal type is a function type, but actual type is not: %s",
158                        toString( actualType ).c_str() );
159                // Can't tuple specialize if parameter sizes deeply-differ.
160                if ( functionParameterSize( ftype ) != functionParameterSize( atype ) ) {
161                        return false;
162                }
163                // If tuple parameter size matches but actual parameter sizes differ
164                // then there needs to be specialization.
165                if ( ftype->params.size() != atype->params.size() ) {
166                        return true;
167                }
168                // Total parameter size can be the same, while individual parameters
169                // can have different structure.
170                for ( auto pairs : group_iterate( ftype->params, atype->params ) ) {
171                        if ( !isTupleStructureMatching(
172                                        std::get<0>( pairs ), std::get<1>( pairs ) ) ) {
173                                return true;
174                        }
175                }
176        }
177        return false;
178}
179
180bool needsSpecialization(
181                const ast::Type * formalType, const ast::Type * actualType,
182                const ast::TypeSubstitution * subs ) {
183        return needsPolySpecialization( formalType, actualType, subs )
184                || needsTupleSpecialization( formalType, actualType );
185}
186
187ast::ApplicationExpr * SpecializeCore::handleExplicitParams(
188                const ast::ApplicationExpr * expr ) {
189        assert( expr->func->result );
190        const ast::FunctionType * func = getFunctionType( expr->func->result );
191        assert( func );
192
193        ast::ApplicationExpr * mut = ast::mutate( expr );
194
195        std::vector<ast::ptr<ast::Type>>::const_iterator formal;
196        std::vector<ast::ptr<ast::Expr>>::iterator actual;
197        for ( formal = func->params.begin(), actual = mut->args.begin() ;
198                        formal != func->params.end() && actual != mut->args.end() ;
199                        ++formal, ++actual ) {
200                *actual = doSpecialization( (*actual)->location,
201                        *formal, *actual, getInferredParams( expr ) );
202        }
203        return mut;
204}
205
206// Explode assuming simple cases: either type is pure tuple (but not tuple
207// expr) or type is non-tuple.
208template<typename OutputIterator>
209void explodeSimple( const CodeLocation & location,
210                const ast::Expr * expr, OutputIterator out ) {
211        // Recurse on tuple types using index expressions on each component.
212        if ( auto tuple = expr->result.as<ast::TupleType>() ) {
213                ast::ptr<ast::Expr> cleanup = expr;
214                for ( unsigned int i = 0 ; i < tuple->size() ; ++i ) {
215                        explodeSimple( location,
216                                new ast::TupleIndexExpr( location, expr, i ), out );
217                }
218        // For a non-tuple type, output a clone of the expression.
219        } else {
220                *out++ = expr;
221        }
222}
223
224// Restructures arguments to match the structure of the formal parameters
225// of the actual function. Returns the next structured argument.
226template<typename Iterator>
227const ast::Expr * structureArg(
228                const CodeLocation& location, const ast::ptr<ast::Type> & type,
229                Iterator & begin, const Iterator & end ) {
230        if ( auto tuple = type.as<ast::TupleType>() ) {
231                std::vector<ast::ptr<ast::Expr>> exprs;
232                for ( const ast::ptr<ast::Type> & t : *tuple ) {
233                        exprs.push_back( structureArg( location, t, begin, end ) );
234                }
235                return new ast::TupleExpr( location, std::move( exprs ) );
236        } else {
237                assertf( begin != end, "reached the end of the arguments while structuring" );
238                return *begin++;
239        }
240}
241
242struct TypeInstFixer final : public ast::WithShortCircuiting {
243        std::map<const ast::TypeDecl *, std::pair<int, int>> typeMap;
244
245        void previsit(const ast::TypeDecl *) { visit_children = false; }
246        const ast::TypeInstType * postvisit(const ast::TypeInstType * typeInst) {
247                if (typeMap.count(typeInst->base)) {
248                        ast::TypeInstType * newInst = mutate(typeInst);
249                        auto const & pair = typeMap[typeInst->base];
250                        newInst->expr_id = pair.first;
251                        newInst->formal_usage = pair.second;
252                        return newInst;
253                }
254                return typeInst;
255        }
256};
257
258const ast::Expr * SpecializeCore::createThunkFunction(
259                const CodeLocation & location,
260                const ast::FunctionType * funType,
261                const ast::Expr * actual,
262                const ast::InferredParams * inferParams ) {
263        // One set of unique names per program.
264        static UniqueName thunkNamer("_thunk");
265
266        const ast::FunctionType * newType = ast::deepCopy( funType );
267        if ( typeSubs ) {
268                // Must replace only occurrences of type variables
269                // that occure free in the thunk's type.
270                auto result = typeSubs->applyFree( newType );
271                newType = result.node.release();
272        }
273
274        using DWTVector = std::vector<ast::ptr<ast::DeclWithType>>;
275        using DeclVector = std::vector<ast::ptr<ast::TypeDecl>>;
276
277        UniqueName paramNamer( paramPrefix );
278
279        // Create new thunk with same signature as formal type.
280        ast::Pass<TypeInstFixer> fixer;
281        for (const auto & kv : newType->forall) {
282                if (fixer.core.typeMap.count(kv->base)) {
283                        std::cerr << location << ' ' << kv->base->name
284                                << ' ' << kv->expr_id << '_' << kv->formal_usage
285                                << ',' << fixer.core.typeMap[kv->base].first
286                                << '_' << fixer.core.typeMap[kv->base].second << std::endl;
287                        assertf(false, "multiple formals in specialize");
288                }
289                else {
290                        fixer.core.typeMap[kv->base] = std::make_pair(kv->expr_id, kv->formal_usage);
291                }
292        }
293
294        ast::CompoundStmt * thunkBody = new ast::CompoundStmt( location );
295        ast::FunctionDecl * thunkFunc = new ast::FunctionDecl(
296                location,
297                thunkNamer.newName(),
298                map_range<DeclVector>( newType->forall, []( const ast::TypeInstType * inst ) {
299                        return ast::deepCopy( inst->base );
300                } ),
301                map_range<DWTVector>( newType->assertions, []( const ast::VariableExpr * expr ) {
302                        return ast::deepCopy( expr->var );
303                } ),
304                map_range<DWTVector>( newType->params, [&location, &paramNamer]( const ast::Type * type ) {
305                        return new ast::ObjectDecl( location, paramNamer.newName(), ast::deepCopy( type ) );
306                } ),
307                map_range<DWTVector>( newType->returns, [&location, &paramNamer]( const ast::Type * type ) {
308                        return new ast::ObjectDecl( location, paramNamer.newName(), ast::deepCopy( type ) );
309                } ),
310                thunkBody,
311                ast::Storage::Classes(),
312                ast::Linkage::C
313                );
314
315        thunkFunc->fixUniqueId();
316
317        // Thunks may be generated and not used, avoid them.
318        thunkFunc->attributes.push_back( new ast::Attribute( "unused" ) );
319
320        // Global thunks must be static to avoid collitions.
321        // Nested thunks must not be unique and hence, not static.
322        thunkFunc->storage.is_static = !isInFunction();
323
324        // Weave thunk parameters into call to actual function,
325        // naming thunk parameters as we go.
326        ast::ApplicationExpr * app = new ast::ApplicationExpr( location, actual );
327
328        const ast::FunctionType * actualType = ast::deepCopy( getFunctionType( actual->result ) );
329        if ( typeSubs ) {
330                // Need to apply the environment to the actual function's type,
331                // since it may itself be polymorphic.
332                auto result = typeSubs->apply( actualType );
333                actualType = result.node.release();
334        }
335
336        ast::ptr<ast::FunctionType> actualTypeManager = actualType;
337
338        std::vector<ast::ptr<ast::Expr>> args;
339        for ( ast::ptr<ast::DeclWithType> & param : thunkFunc->params ) {
340                // Name each thunk parameter and explode it.
341                // These are then threaded back into the actual function call.
342                ast::DeclWithType * mutParam = ast::mutate( param.get() );
343                explodeSimple( location, new ast::VariableExpr( location, mutParam ),
344                        std::back_inserter( args ) );
345        }
346
347        // Walk parameters to the actual function alongside the exploded thunk
348        // parameters and restructure the arguments to match the actual parameters.
349        std::vector<ast::ptr<ast::Expr>>::iterator
350                argBegin = args.begin(), argEnd = args.end();
351        for ( const auto & actualArg : actualType->params ) {
352                app->args.push_back(
353                        structureArg( location, actualArg.get(), argBegin, argEnd ) );
354        }
355        assertf( argBegin == argEnd, "Did not structure all arguments." );
356
357        app->accept(fixer); // this should modify in place
358
359        app->env = ast::TypeSubstitution::newFromExpr( app, typeSubs );
360        if ( inferParams ) {
361                app->inferred.inferParams() = *inferParams;
362        }
363
364        // Handle any specializations that may still be present.
365        {
366                std::string oldParamPrefix = paramPrefix;
367                paramPrefix += "p";
368                std::list<ast::ptr<ast::Decl>> oldDecls;
369                oldDecls.splice( oldDecls.end(), declsToAddBefore );
370
371                app->accept( *visitor );
372                // Write recursive specializations into the thunk body.
373                for ( const ast::ptr<ast::Decl> & decl : declsToAddBefore ) {
374                        thunkBody->push_back( new ast::DeclStmt( decl->location, decl ) );
375                }
376
377                declsToAddBefore = std::move( oldDecls );
378                paramPrefix = std::move( oldParamPrefix );
379        }
380
381        // Add return (or valueless expression) to the thunk.
382        ast::Stmt * appStmt;
383        if ( funType->returns.empty() ) {
384                appStmt = new ast::ExprStmt( app->location, app );
385        } else {
386                appStmt = new ast::ReturnStmt( app->location, app );
387        }
388        thunkBody->push_back( appStmt );
389
390        // Add the thunk definition:
391        declsToAddBefore.push_back( thunkFunc );
392
393        // Return address of thunk function as replacement expression.
394        return new ast::AddressExpr( location,
395                new ast::VariableExpr( location, thunkFunc ) );
396}
397
398const ast::Expr * SpecializeCore::doSpecialization(
399                const CodeLocation & location,
400                const ast::Type * formalType,
401                const ast::Expr * actual,
402                const ast::InferredParams * inferParams ) {
403        assertf( actual->result, "attempting to specialize an untyped expression" );
404        if ( needsSpecialization( formalType, actual->result, typeSubs ) ) {
405                if ( const ast::FunctionType * type = getFunctionType( formalType ) ) {
406                        if ( const ast::ApplicationExpr * expr =
407                                        dynamic_cast<const ast::ApplicationExpr *>( actual ) ) {
408                                return createThunkFunction( location, type, expr->func, inferParams );
409                        } else if ( auto expr =
410                                        dynamic_cast<const ast::VariableExpr *>( actual ) ) {
411                                return createThunkFunction( location, type, expr, inferParams );
412                        } else {
413                                // (I don't even know what that comment means.)
414                                // This likely won't work, as anything that could build an ApplicationExpr probably hit one of the previous two branches
415                                return createThunkFunction( location, type, actual, inferParams );
416                        }
417                } else {
418                        return actual;
419                }
420        } else {
421                return actual;
422        }
423}
424
425const ast::Expr * SpecializeCore::postvisit(
426                const ast::ApplicationExpr * expr ) {
427        if ( InitTweak::isIntrinsicCallExpr( expr ) ) {
428                return expr;
429        }
430
431        // Create thunks for the inferred parameters.
432        // This is not needed for intrinsic calls, because they aren't
433        // actually passed to the function. It needs to handle explicit params
434        // before inferred params so that explicit params do not recieve a
435        // changed set of inferParams (and change them again).
436        // Alternatively, if order starts to matter then copy expr's inferParams
437        // and pass them to handleExplicitParams.
438        ast::ApplicationExpr * mut = handleExplicitParams( expr );
439        if ( !mut->inferred.hasParams() ) {
440                return mut;
441        }
442        ast::InferredParams & inferParams = mut->inferred.inferParams();
443        for ( ast::InferredParams::value_type & inferParam : inferParams ) {
444                inferParam.second.expr = doSpecialization(
445                        inferParam.second.expr->location,
446                        inferParam.second.formalType,
447                        inferParam.second.expr,
448                        getInferredParams( inferParam.second.expr )
449                );
450        }
451        return mut;
452}
453
454const ast::Expr * SpecializeCore::postvisit( const ast::CastExpr * expr ) {
455        if ( expr->result->isVoid() ) {
456                // No specialization if there is no return value.
457                return expr;
458        }
459        const ast::Expr * specialized = doSpecialization(
460                expr->location, expr->result, expr->arg, getInferredParams( expr ) );
461        if ( specialized != expr->arg ) {
462                // Assume that the specialization incorporates the cast.
463                return specialized;
464        } else {
465                return expr;
466        }
467}
468
469} // namespace
470
471void convertSpecializations( ast::TranslationUnit & translationUnit ) {
472        ast::Pass<SpecializeCore>::run( translationUnit );
473}
474
475} // namespace GenPoly
476
477// Local Variables: //
478// tab-width: 4 //
479// mode: c++ //
480// compile-command: "make install" //
481// End: //
Note: See TracBrowser for help on using the repository browser.