source: src/InitTweak/InitTweak.cpp@ ef05cf0

Last change on this file since ef05cf0 was ef05cf0, checked in by Andrew Beach <ajbeach@…>, 5 months ago

Moved over some clean-up I did in various attempted fixes. InitDepthChecker has a very rare error case that has now been removed.

  • Property mode set to 100644
File size: 16.2 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// InitTweak.cpp --
8//
9// Author : Rob Schluntz
10// Created On : Fri May 13 11:26:36 2016
11// Last Modified By : Andrew Beach
12// Last Modified On : Wed Sep 22 9:50:00 2022
13// Update Count : 21
14//
15
16#include "InitTweak.hpp"
17
18#include <algorithm> // for find, all_of
19#include <cassert> // for assertf, assert, strict_dynamic_...
20#include <iostream> // for ostream, cerr, endl
21#include <iterator> // for back_insert_iterator, back_inser...
22#include <memory> // for __shared_ptr
23#include <vector>
24
25#include "AST/Expr.hpp"
26#include "AST/Init.hpp"
27#include "AST/Inspect.hpp"
28#include "AST/Node.hpp"
29#include "AST/Pass.hpp"
30#include "AST/Stmt.hpp"
31#include "AST/Type.hpp"
32#include "CodeGen/OperatorTable.hpp" // for isConstructor, isDestructor, isC...
33#include "Common/SemanticError.hpp" // for SemanticError
34#include "Common/ToString.hpp" // for toCString
35#include "Common/UniqueName.hpp" // for UniqueName
36#include "GenPoly/GenPoly.hpp" // for getFunctionType
37#include "ResolvExpr/Unify.hpp" // for typesCompatibleIgnoreQualifiers
38#include "Tuples/Tuples.hpp" // for Tuples::isTtype
39
40namespace InitTweak {
41
42// Forward declared, because it is used as a parent type.
43class InitExpander::ExpanderImpl {
44public:
45 virtual ~ExpanderImpl() = default;
46 virtual std::vector< ast::ptr< ast::Expr > > next( IndexList & indices ) = 0;
47 virtual ast::ptr< ast::Stmt > buildListInit(
48 ast::UntypedExpr * callExpr, IndexList & indices ) = 0;
49};
50
51namespace {
52 struct HasDesignations : public ast::WithShortCircuiting {
53 bool result = false;
54
55 void previsit( const ast::Node * ) {
56 // Short circuit if we already know there are designations.
57 if ( result ) visit_children = false;
58 }
59
60 void previsit( const ast::Designation * des ) {
61 // Short circuit if we already know there are designations.
62 if ( result ) visit_children = false;
63 else if ( !des->designators.empty() ) {
64 result = true;
65 visit_children = false;
66 }
67 }
68 };
69
70 struct InitDepthChecker : public ast::WithShortCircuiting {
71 bool result = true;
72 const ast::Type * type;
73 int curDepth = 0, maxDepth = 0;
74 InitDepthChecker( const ast::Type * type ) : type( type ) {
75 const ast::Type * t = type;
76 while ( auto at = dynamic_cast< const ast::ArrayType * >( t ) ) {
77 maxDepth++;
78 t = at->base;
79 }
80 maxDepth++;
81 }
82 void previsit( ast::ListInit const * ) {
83 curDepth++;
84 if ( curDepth > maxDepth ) result = false;
85 }
86 void postvisit( ast::ListInit const * ) {
87 curDepth--;
88 }
89 void previsit( ast::SingleInit const * ) {
90 // We don't want to visit the value field.
91 visit_children = false;
92 }
93 };
94
95 struct InitFlattener : public ast::WithShortCircuiting {
96 std::vector< ast::ptr< ast::Expr > > argList;
97
98 void previsit( const ast::SingleInit * singleInit ) {
99 visit_children = false;
100 argList.emplace_back( singleInit->value );
101 }
102 };
103
104 template< typename Out >
105 void buildCallExpr(
106 ast::UntypedExpr * callExpr, const ast::Expr * index, const ast::Expr * dimension,
107 const ast::Init * init, Out & out
108 ) {
109 const CodeLocation & loc = init->location;
110
111 auto cond = new ast::UntypedExpr{
112 loc, new ast::NameExpr{ loc, "?<?" }, { index, dimension } };
113
114 std::vector< ast::ptr< ast::Expr > > args = makeInitList( init );
115 splice( callExpr->args, args );
116
117 out.emplace_back( new ast::IfStmt{ loc, cond, new ast::ExprStmt{ loc, callExpr } } );
118
119 out.emplace_back( new ast::ExprStmt{
120 loc, new ast::UntypedExpr{ loc, new ast::NameExpr{ loc, "++?" }, { index } } } );
121 }
122
123 template< typename Out >
124 void build(
125 ast::UntypedExpr * callExpr, const InitExpander::IndexList & indices,
126 const ast::Init * init, Out & out
127 ) {
128 if ( indices.empty() ) return;
129
130 unsigned idx = 0;
131
132 const ast::Expr * index = indices[idx++];
133 assert( idx != indices.size() );
134 const ast::Expr * dimension = indices[idx++];
135
136 if ( idx == indices.size() ) {
137 if ( auto listInit = dynamic_cast< const ast::ListInit * >( init ) ) {
138 for ( const ast::Init * init : *listInit ) {
139 buildCallExpr( shallowCopy(callExpr), index, dimension, init, out );
140 }
141 } else {
142 buildCallExpr( shallowCopy(callExpr), index, dimension, init, out );
143 }
144 } else {
145 const CodeLocation & loc = init->location;
146
147 unsigned long cond = 0;
148 auto listInit = dynamic_cast< const ast::ListInit * >( init );
149 if ( ! listInit ) { SemanticError( loc, "unbalanced list initializers" ); }
150
151 static UniqueName targetLabel( "L__autogen__" );
152 ast::Label switchLabel{
153 loc, targetLabel.newName(), { new ast::Attribute{ "unused" } } };
154
155 std::vector< ast::ptr< ast::CaseClause > > branches;
156 for ( const ast::Init * init : *listInit ) {
157 auto condition = ast::ConstantExpr::from_ulong( loc, cond );
158 ++cond;
159
160 std::vector< ast::ptr< ast::Stmt > > stmts;
161 build( callExpr, indices, init, stmts );
162 stmts.emplace_back(
163 new ast::BranchStmt{ loc, ast::BranchStmt::Break, switchLabel } );
164 branches.emplace_back( new ast::CaseClause{ loc, condition, std::move( stmts ) } );
165 }
166 out.emplace_back( new ast::SwitchStmt{ loc, index, std::move( branches ) } );
167 out.emplace_back( new ast::NullStmt{ loc, { switchLabel } } );
168 }
169 }
170
171 class InitImpl final : public InitExpander::ExpanderImpl {
172 ast::ptr< ast::Init > init;
173 public:
174 InitImpl( const ast::Init * i ) : init( i ) {}
175
176 std::vector< ast::ptr< ast::Expr > > next( InitExpander::IndexList & ) override {
177 return makeInitList( init );
178 }
179
180 ast::ptr< ast::Stmt > buildListInit(
181 ast::UntypedExpr * callExpr, InitExpander::IndexList & indices
182 ) override {
183 // If array came with an initializer list, initialize each element. We may have more
184 // initializers than elements of the array; need to check at each index that we have
185 // not exceeded size. We may have fewer initializers than elements in the array; need
186 // to default-construct remaining elements. To accomplish this, generate switch
187 // statement consuming all of expander's elements
188
189 if ( ! init ) return {};
190
191 std::list< ast::ptr< ast::Stmt > > stmts;
192 build( callExpr, indices, init, stmts );
193 if ( stmts.empty() ) {
194 return {};
195 } else if ( 1 == stmts.size() ) {
196 return std::move( stmts.front() );
197 } else {
198 auto block = new ast::CompoundStmt{ init->location, std::move( stmts ) };
199 init = nullptr; // consumed in creating the list init
200 return block;
201 }
202 }
203 };
204
205 class ExprImpl final : public InitExpander::ExpanderImpl {
206 ast::ptr< ast::Expr > arg;
207 public:
208 ExprImpl( const ast::Expr * a ) : arg( a ) {}
209
210 std::vector< ast::ptr< ast::Expr > > next(
211 InitExpander::IndexList & indices
212 ) override {
213 if ( !arg ) return {};
214
215 const CodeLocation & loc = arg->location;
216 const ast::Expr * expr = arg;
217 for ( auto it = indices.rbegin(); it != indices.rend(); ++it ) {
218 // Go through indices and layer on subscript exprs ?[?].
219 ++it;
220 expr = new ast::UntypedExpr{
221 loc, new ast::NameExpr{ loc, "?[?]" }, { expr, *it } };
222 }
223 return { expr };
224 }
225
226 ast::ptr< ast::Stmt > buildListInit(
227 ast::UntypedExpr *, InitExpander::IndexList &
228 ) override {
229 return {};
230 }
231 };
232
233 struct CallFinder final {
234 std::vector< const ast::Expr * > matches;
235 const std::vector< std::string > names;
236
237 CallFinder( std::vector< std::string > && ns ) : matches(), names( std::move(ns) ) {}
238
239 void handleCallExpr( const ast::Expr * expr ) {
240 std::string fname = getFunctionName( expr );
241 if ( std::find( names.begin(), names.end(), fname ) != names.end() ) {
242 matches.emplace_back( expr );
243 }
244 }
245
246 void postvisit( const ast::ApplicationExpr * expr ) { handleCallExpr( expr ); }
247 void postvisit( const ast::UntypedExpr * expr ) { handleCallExpr( expr ); }
248 };
249
250 template <typename Predicate>
251 bool allofCtorDtor( const ast::Stmt * stmt, const Predicate & pred ) {
252 std::vector< const ast::Expr * > callExprs = collectCtorDtorCalls( stmt );
253 return std::all_of( callExprs.begin(), callExprs.end(), pred );
254 }
255
256 struct ConstExprChecker : public ast::WithShortCircuiting {
257 // Most expressions are not const-expr.
258 void previsit( const ast::Expr * ) { result = false; visit_children = false; }
259
260 void previsit( const ast::AddressExpr *addressExpr ) {
261 visit_children = false;
262 const ast::Expr * arg = addressExpr->arg;
263
264 // Address of a variable or member expression is const-expr.
265 if ( !dynamic_cast< const ast::NameExpr * >( arg )
266 && !dynamic_cast< const ast::VariableExpr * >( arg )
267 && !dynamic_cast< const ast::MemberExpr * >( arg )
268 && !dynamic_cast< const ast::UntypedMemberExpr * >( arg ) ) result = false;
269 }
270
271 // These expressions may be const expr, depending on their children.
272 void previsit( const ast::SizeofExpr * ) {}
273 void previsit( const ast::AlignofExpr * ) {}
274 void previsit( const ast::UntypedOffsetofExpr * ) {}
275 void previsit( const ast::OffsetofExpr * ) {}
276 void previsit( const ast::OffsetPackExpr * ) {}
277 void previsit( const ast::CommaExpr * ) {}
278 void previsit( const ast::LogicalExpr * ) {}
279 void previsit( const ast::ConditionalExpr * ) {}
280 void previsit( const ast::CastExpr * ) {}
281 void previsit( const ast::ConstantExpr * ) {}
282
283 void previsit( const ast::VariableExpr * varExpr ) {
284 visit_children = false;
285
286 if ( auto inst = varExpr->result.as<ast::EnumInstType>() ) {
287 long long int value;
288 if ( inst->base->valueOf( varExpr->var, value ) ) {
289 // enumerators are const expr
290 return;
291 }
292 }
293 result = false;
294 }
295
296 bool result = true;
297 };
298} // namespace
299
300bool isAssignment( const ast::FunctionDecl * decl ) {
301 return CodeGen::isAssignment( decl->name ) && isCopyFunction( decl );
302}
303
304bool isDestructor( const ast::FunctionDecl * decl ) {
305 return CodeGen::isDestructor( decl->name );
306}
307
308bool isDefaultConstructor( const ast::FunctionDecl * decl ) {
309 return CodeGen::isConstructor( decl->name ) && 1 == decl->params.size();
310}
311
312bool isCopyConstructor( const ast::FunctionDecl * decl ) {
313 return CodeGen::isConstructor( decl->name ) && 2 == decl->params.size();
314}
315
316bool isCopyFunction( const ast::FunctionDecl * decl ) {
317 const ast::FunctionType * ftype = decl->type;
318 if ( ftype->params.size() != 2 ) return false;
319
320 const ast::Type * t1 = ast::getPointerBase( ftype->params.front() );
321 if ( ! t1 ) return false;
322 const ast::Type * t2 = ftype->params.back();
323
324 return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2 );
325}
326
327const ast::Type * getTypeofThis( const ast::FunctionType * ftype ) {
328 assertf( ftype, "getTypeofThis: nullptr ftype" );
329 const std::vector<ast::ptr<ast::Type>> & params = ftype->params;
330 assertf( !params.empty(), "getTypeofThis: ftype with 0 parameters: %s",
331 toCString( ftype ) );
332 const ast::ReferenceType * refType =
333 params.front().strict_as<ast::ReferenceType>();
334 return refType->base;
335}
336
337const ast::ObjectDecl * getParamThis(const ast::FunctionDecl * func) {
338 assertf( func, "getParamThis: nullptr ftype" );
339 auto & params = func->params;
340 assertf( !params.empty(), "getParamThis: ftype with 0 parameters: %s", toCString( func ));
341 return params.front().strict_as<ast::ObjectDecl>();
342}
343
344// looks like some other such codegen uses UntypedExpr and does not create fake function. should revisit afterwards
345// following passes may accidentally resolve this expression if returned as untyped...
346ast::Expr * createBitwiseAssignment(const ast::Expr * dst, const ast::Expr * src) {
347 static ast::ptr<ast::FunctionDecl> assign = nullptr;
348 if (!assign) {
349 auto td = new ast::TypeDecl(CodeLocation(), "T", {}, nullptr, ast::TypeDecl::Dtype, true);
350 assign = new ast::FunctionDecl(CodeLocation(), "?=?", {td}, {},
351 { new ast::ObjectDecl(CodeLocation(), "_dst", new ast::ReferenceType(new ast::TypeInstType("T", td))),
352 new ast::ObjectDecl(CodeLocation(), "_src", new ast::TypeInstType("T", td))},
353 { new ast::ObjectDecl(CodeLocation(), "_ret", new ast::TypeInstType("T", td))}, nullptr, {}, ast::Linkage::Intrinsic);
354 }
355 if (dst->result.as<ast::ReferenceType>()) {
356 for (int depth = dst->result->referenceDepth(); depth > 0; depth--) {
357 dst = new ast::AddressExpr(dst);
358 }
359 } else {
360 dst = new ast::CastExpr(dst, new ast::ReferenceType(dst->result, {}));
361 }
362 if (src->result.as<ast::ReferenceType>()) {
363 for (int depth = src->result->referenceDepth(); depth > 0; depth--) {
364 src = new ast::AddressExpr(src);
365 }
366 }
367 auto var = ast::VariableExpr::functionPointer(dst->location, assign);
368 auto app = new ast::ApplicationExpr(dst->location, var, {dst, src});
369 // Skip the resolver, just set the result to the correct type.
370 app->result = ast::deepCopy( src->result );
371 return app;
372}
373
374std::vector< ast::ptr< ast::Expr > > makeInitList( const ast::Init * init ) {
375 ast::Pass< InitFlattener > flattener;
376 maybe_accept( init, flattener );
377 return std::move( flattener.core.argList );
378}
379
380bool tryConstruct( const ast::DeclWithType * dwt ) {
381 auto objDecl = dynamic_cast< const ast::ObjectDecl * >( dwt );
382 if ( !objDecl ) return false;
383 return (objDecl->init == nullptr ||
384 ( objDecl->init != nullptr && objDecl->init->maybeConstructed ))
385 && !objDecl->storage.is_extern
386 && isConstructable( objDecl->type );
387}
388
389bool isConstructable( const ast::Type * type ) {
390 return !dynamic_cast< const ast::VarArgsType * >( type ) && !dynamic_cast< const ast::ReferenceType * >( type )
391 && !dynamic_cast< const ast::FunctionType * >( type ) && !Tuples::isTtype( type );
392}
393
394bool isDesignated( const ast::Init * init ) {
395 return ( init ) ? ast::Pass<HasDesignations>::read( init ) : false;
396}
397
398bool checkInitDepth( const ast::ObjectDecl * objDecl ) {
399 return ( objDecl->init ) ? ast::Pass<InitDepthChecker>::read(
400 objDecl->init.get(), objDecl->type.get() ) : true;
401}
402
403bool isIntrinsicSingleArgCallStmt( const ast::Stmt * stmt ) {
404 return allofCtorDtor( stmt, []( const ast::Expr * callExpr ){
405 if ( const ast::ApplicationExpr * appExpr = isIntrinsicCallExpr( callExpr ) ) {
406 const ast::FunctionType * funcType =
407 GenPoly::getFunctionType( appExpr->func->result );
408 assert( funcType );
409 return funcType->params.size() == 1;
410 }
411 return false;
412 });
413}
414
415std::vector< const ast::Expr * > collectCtorDtorCalls( const ast::Stmt * stmt ) {
416 ast::Pass< CallFinder > finder{ std::vector< std::string >{ "?{}", "^?{}" } };
417 maybe_accept( stmt, finder );
418 return std::move( finder.core.matches );
419}
420
421bool isConstExpr( const ast::Expr * expr ) {
422 return ( expr ) ? ast::Pass<ConstExprChecker>::read( expr ) : true;
423}
424
425bool isConstExpr( const ast::Init * init ) {
426 // for all intents and purposes, no initializer means const expr
427 return ( init ) ? ast::Pass<ConstExprChecker>::read( init ) : true;
428}
429
430#if defined( __x86_64 ) || defined( __i386 ) // assembler comment to prevent assembler warning message
431 #define ASM_COMMENT "#"
432#else // defined( __ARM_ARCH )
433 #define ASM_COMMENT "//"
434#endif
435static const char * const data_section = ".data" ASM_COMMENT;
436static const char * const tlsd_section = ".tdata" ASM_COMMENT;
437
438void addDataSectionAttribute( ast::ObjectDecl * objDecl ) {
439 const bool is_tls = objDecl->storage.is_threadlocal_any();
440 const char * section = is_tls ? tlsd_section : data_section;
441 objDecl->attributes.push_back(new ast::Attribute("section", {
442 ast::ConstantExpr::from_string(objDecl->location, section)
443 }));
444}
445
446InitExpander::InitExpander( const ast::Init * init )
447: expander( new InitImpl{ init } ), crnt(), indices() {}
448
449InitExpander::InitExpander( const ast::Expr * expr )
450: expander( new ExprImpl{ expr } ), crnt(), indices() {}
451
452std::vector< ast::ptr< ast::Expr > > InitExpander::operator* () { return crnt; }
453
454InitExpander & InitExpander::operator++ () {
455 crnt = expander->next( indices );
456 return *this;
457}
458
459/// builds statement which has the same semantics as a C-style list initializer (for array
460/// initializers) using callExpr as the base expression to perform initialization
461ast::ptr< ast::Stmt > InitExpander::buildListInit( ast::UntypedExpr * callExpr ) {
462 return expander->buildListInit( callExpr, indices );
463}
464
465void InitExpander::addArrayIndex( const ast::Expr * index, const ast::Expr * dimension ) {
466 indices.emplace_back( index );
467 indices.emplace_back( dimension );
468}
469
470void InitExpander::clearArrayIndices() { indices.clear(); }
471
472bool InitExpander::addReference() {
473 for ( ast::ptr< ast::Expr > & expr : crnt ) {
474 expr = new ast::AddressExpr{ expr };
475 }
476 return !crnt.empty();
477}
478
479} // namespace InitTweak
Note: See TracBrowser for help on using the repository browser.