source: src/InitTweak/InitTweak.cc@ 8655363

Last change on this file since 8655363 was 58c5821, checked in by Andrew Beach <ajbeach@…>, 21 months ago

Remove extra compound statement and clean-up in InitTweak.

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