source: src/CodeGen/CodeGenerator.cpp@ 1fb0a883

Last change on this file since 1fb0a883 was f070ea8, checked in by Andrew Beach <ajbeach@…>, 8 months ago

Improved code generation for intermediate steps. Now generates using @= on the approprate initializers.

  • Property mode set to 100644
File size: 35.9 KB
RevLine 
[8941b6b]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//
[83fd57d]7// CodeGenerator.cpp --
[8941b6b]8//
9// Author : Andrew Beach
10// Created On : Tue Oct 17 15:54:00 2023
[d96f7c4]11// Last Modified By : Peter A. Buhr
12// Last Modified On : Fri Jan 17 14:19:22 2025
13// Update Count : 1
[8941b6b]14//
15
[83fd57d]16#include "CodeGenerator.hpp"
[8941b6b]17
18#include "AST/Print.hpp"
[c92bdcc]19#include "OperatorTable.hpp" // for OperatorInfo, operatorLookup
20#include "CodeGen/GenType.hpp" // for genType
[8941b6b]21#include "Common/ToString.hpp" // for toString
[c92bdcc]22#include "Common/UniqueName.hpp" // for UniqueName
[8941b6b]23
24namespace CodeGen {
25
[0bd3faf]26int CodeGenerator::tabsize = 4;
[8941b6b]27
28// The kinds of statements that should be followed by whitespace.
29static bool wantSpacing( ast::Stmt const * stmt ) {
30 return dynamic_cast<ast::IfStmt const *>( stmt )
31 || dynamic_cast<ast::CompoundStmt const *>( stmt )
32 || dynamic_cast<ast::WhileDoStmt const *>( stmt )
33 || dynamic_cast<ast::ForStmt const *>( stmt )
34 || dynamic_cast<ast::SwitchStmt const *>( stmt );
35}
36
[0bd3faf]37void CodeGenerator::extension( ast::Expr const * expr ) {
[8941b6b]38 if ( expr->extension ) output << "__extension__ ";
39}
40
[0bd3faf]41void CodeGenerator::extension( ast::Decl const * decl ) {
[8941b6b]42 if ( decl->extension ) output << "__extension__ ";
43}
44
[0bd3faf]45void CodeGenerator::asmName( ast::DeclWithType const * decl ) {
[8941b6b]46 if ( auto asmName = decl->asmName.as<ast::ConstantExpr>() ) {
47 output << " asm ( " << asmName->rep << " )";
48 }
49}
50
[0bd3faf]51CodeGenerator::LabelPrinter & CodeGenerator::LabelPrinter::operator()(
[8941b6b]52 std::vector<ast::Label> const & l ) {
53 labels = &l;
54 return *this;
55}
56
[0bd3faf]57std::ostream & CodeGenerator::LabelPrinter::operator()( std::ostream & output ) const {
[8941b6b]58 const std::vector<ast::Label> & labels = *this->labels;
59 for ( const ast::Label & label : labels ) {
60 output << label.name + ": ";
61 this->cg.genAttributes( label.attributes );
62 }
63 return output;
64}
65
66// Using updateLocation at the beginning of a node and endl within a node
67// should become the method of formating.
[0bd3faf]68void CodeGenerator::updateLocation( CodeLocation const & to ) {
[8941b6b]69 // Skip if linemarks shouldn't appear or if location is unset.
70 if ( !options.lineMarks || to.isUnset() ) return;
71
72 if ( currentLocation.followedBy( to, 0 ) ) {
73 return;
74 } else if ( currentLocation.followedBy( to, 1 ) ) {
75 output << "\n" << indent;
76 currentLocation.first_line += 1;
77 } else if ( currentLocation.followedBy( to, 2 ) ) {
78 output << "\n\n" << indent;
79 currentLocation.first_line += 2;
80 } else {
[661e7b0]81 output << "\n# " << to.first_line << " \"" << to.filename.c_str()
[8941b6b]82 << "\"\n" << indent;
83 currentLocation = to;
84 }
85 output << std::flush;
86}
87
[0bd3faf]88void CodeGenerator::updateLocation( ast::ParseNode const * to ) {
[8941b6b]89 updateLocation( to->location );
90}
91
[0bd3faf]92std::ostream & CodeGenerator::LineEnder::operator()( std::ostream & os ) const {
[8941b6b]93 os << "\n" << std::flush;
94 cg.currentLocation.first_line++;
95 return os;
96}
97
[0bd3faf]98CodeGenerator::CodeGenerator( std::ostream & os, const Options & options ) :
99 indent( 0, CodeGenerator::tabsize ), output( os ),
[8941b6b]100 options( options ), printLabels( *this ), endl( *this )
101{}
102
[0bd3faf]103std::string CodeGenerator::mangleName( ast::DeclWithType const * decl ) {
[c7ebbec]104 if ( !options.pretty && decl->linkage.is_mangled && decl->mangleName != "" ) {
[8941b6b]105 return decl->scopedMangleName();
106 } else {
107 return decl->name;
108 }
109}
110
[0bd3faf]111void CodeGenerator::genAttributes(
[8941b6b]112 const std::vector<ast::ptr<ast::Attribute>> & attributes ) {
113 if ( attributes.empty() ) return;
114 output << "__attribute__ ((";
115 for ( auto attr = attributes.begin() ;; ) {
116 output << (*attr)->name;
117 if ( !(*attr)->params.empty() ) {
118 output << "(";
119 genCommaList( (*attr)->params );
120 output << ")";
121 }
122 if ( ++attr == attributes.end() ) break;
123 output << ",";
124 }
125 output << ")) ";
126}
127
[0bd3faf]128void CodeGenerator::previsit( ast::Node const * ) {
[8941b6b]129 // All traversal is manual.
130 // TODO: Which means the ast::Pass is just providing a default no visit?
131 visit_children = false;
132}
133
[0bd3faf]134void CodeGenerator::previsit( ast::ParseNode const * node ) {
[8941b6b]135 previsit( (ast::Node const *)node );
136 updateLocation( node );
137}
138
[0bd3faf]139void CodeGenerator::postvisit( ast::Node const * node ) {
[8941b6b]140 std::stringstream ss;
141 ast::print( ss, node );
142 assertf( false, "Unhandled node reached in CodeGenerator: %s", ss.str().c_str() );
143}
144
[0bd3faf]145void CodeGenerator::previsit( ast::Expr const * expr ) {
[8941b6b]146 previsit( (ast::ParseNode const *)expr );
147 GuardAction( [this, expr](){
148 if ( options.printExprTypes && expr->result ) {
149 output << " /* " << genType( expr->result, "", options ) << " */ ";
150 }
151 } );
152}
153
[0bd3faf]154void CodeGenerator::postvisit( ast::FunctionDecl const * decl ) {
[8941b6b]155 // Deleted decls should never be used, so don't print them in C.
156 if ( decl->isDeleted && options.genC ) return;
157 extension( decl );
158 genAttributes( decl->attributes );
159
160 handleStorageClass( decl );
161 ast::print( output, decl->funcSpec );
162
163 Options subOptions = options;
164 subOptions.anonymousUnused = decl->stmts;
165
166 std::ostringstream acc;
[0bd3faf]167 ast::Pass<CodeGenerator> subCG( acc, subOptions );
[8941b6b]168 // Add the forall clause.
[3c4003b9]169 if ( !decl->type_params.empty() ) {
170 assertf( !options.genC, "FunctionDecl forall should not reach code generation." );
[8941b6b]171 acc << "forall(";
172 subCG.core.genCommaList( decl->type_params );
173 acc << ")" << std::endl;
174 }
[a1fbad0]175 // The forall clause should be printed early as part of the preamble.
176 output << acc.str();
177 acc.str("");
[8941b6b]178
179 acc << mangleName( decl );
180
181 if ( 0 == decl->params.size() ) {
[db19e1d]182 if ( !decl->type->isVarArgs ) {
183 acc << "(void)";
184 } else if ( options.genC ) {
[8941b6b]185 acc << "()";
186 } else {
[db19e1d]187 acc << "(...)";
[8941b6b]188 }
189 } else {
190 acc << "(";
191 subCG.core.genCommaList( decl->params );
192 if ( decl->type->isVarArgs ) {
193 acc << ", ...";
194 }
195 acc << ")";
196 }
197
198 if ( 1 == decl->returns.size() ) {
199 ast::ptr<ast::Type> const & type = decl->returns[0]->get_type();
200 output << genTypeNoAttr( type, acc.str(), subOptions );
[0139351]201 } else if ( 0 == decl->returns.size() ) {
[8941b6b]202 output << "void " + acc.str();
[0139351]203 } else {
204 assertf( !options.genC, "Multi-return should not reach code generation." );
205 ast::ptr<ast::Type> type = new ast::TupleType( copy( decl->type->returns ) );
206 output << genTypeNoAttr( type, acc.str(), subOptions );
[8941b6b]207 }
208
209 asmName( decl );
210
211 if ( decl->stmts ) {
212 decl->stmts->accept( *visitor );
213 }
214 if ( decl->isDeleted ) {
215 output << " = void";
216 }
217}
218
[0bd3faf]219ast::ObjectDecl const * CodeGenerator::postvisit(
[8941b6b]220 ast::ObjectDecl const * decl ) {
221 // Deleted decls should never be used, so don't print them in C.
222 if ( decl->isDeleted && options.genC ) return decl;
223
224 // GCC allows an empty declarator (no name) for bit-fields and C
225 // states: 6.7.2.1 Structure and union specifiers, point 4, page 113:
226 // If the (bit field) value is zero, the declaration shall have no
227 // declarator. For anything else, the anonymous name refers to the
228 // anonymous object for plan9 inheritance.
229 if ( decl->name.empty() && options.genC && !decl->bitfieldWidth ) {
230 // TODO: Should this be changed in a pervious pass?
231 auto mutDecl = ast::mutate( decl );
232 // Only generate an anonymous name when generating C code,
233 // otherwise it clutters the output too much.
234 static UniqueName name = { "__anonymous_object" };
235 mutDecl->name = name.newName();
236 // Stops unused parameter warnings.
237 if ( options.anonymousUnused ) {
238 mutDecl->attributes.push_back( new ast::Attribute( "unused" ) );
239 }
240 decl = mutDecl;
241 }
242
243 extension( decl );
244 genAttributes( decl->attributes );
245
246 handleStorageClass( decl );
247 output << genType( decl->type, mangleName( decl ),
248 Options( options.pretty, options.genC, false, false ) );
249
250 asmName( decl );
251
252 if ( decl->init ) {
[f070ea8]253 bool isGenericInit = options.genC || decl->init->maybeConstructed;
254 output << ( (isGenericInit) ? " = " : " @= " );
[8941b6b]255 decl->init->accept( *visitor );
256 }
257 if ( decl->isDeleted ) {
258 output << " = void";
259 }
260
261 if ( decl->bitfieldWidth ) {
262 output << ":";
263 decl->bitfieldWidth->accept( *visitor );
264 }
265 return decl;
266}
267
[0bd3faf]268void CodeGenerator::handleStorageClass( ast::DeclWithType const * decl ) {
[8941b6b]269 if ( decl->storage.any() ) {
270 ast::print( output, decl->storage );
271 }
272}
273
[0bd3faf]274void CodeGenerator::handleAggregate(
[8941b6b]275 ast::AggregateDecl const * decl, std::string const & kind ) {
276 if ( !decl->params.empty() && !options.genC ) {
277 output << "forall(";
278 genCommaList( decl->params );
279 output << ")\n" << indent << std::flush;
280 }
281
282 output << kind;
283 genAttributes( decl->attributes );
284 output << decl->name;
285
286 if ( decl->body ) {
287 auto & members = decl->members;
288 output << " {" << endl;
289
290 ++indent;
291 for ( auto & member : members ) {
292 output << indent;
293 member->accept( *visitor );
294 output << ";" << endl;
295 }
296 --indent;
297
298 output << indent << "}";
299 }
300}
301
[0bd3faf]302void CodeGenerator::postvisit( ast::StructDecl const * decl ) {
[8941b6b]303 extension( decl );
304 handleAggregate( decl, "struct " );
305}
306
[0bd3faf]307void CodeGenerator::postvisit( ast::UnionDecl const * decl ) {
[8941b6b]308 extension( decl );
309 handleAggregate( decl, "union " );
310}
311
312template<typename pass_type>
313inline void genEnumInitializer( ast::Pass<pass_type> * visitor,
314 ast::Type const * baseType, std::ostream & output,
315 ast::Init const * init, long long * curVal, Options options ) {
316 auto baseTypeAsBasic = dynamic_cast<ast::BasicType const *>( baseType );
317 // Value is provided.
318 if ( init ) {
319 output << " = (" << genType( baseType, "", options ) << ")";
320 init->accept( *visitor );
321 // If it is an integral type and initilizer offered,
322 // need to update the curVal.
323 if ( baseTypeAsBasic && baseTypeAsBasic->isInteger() ) {
324 ast::Expr const * expr = ((ast::SingleInit const *)(init))->value;
325 // Unwrap introduced cast.
326 while ( auto temp = dynamic_cast<ast::CastExpr const *>( expr ) ) {
327 expr = temp->arg;
328 }
329 *curVal = ((ast::ConstantExpr const *)(expr))->intValue() + 1;
330 }
331 // Generate next value from previous value.
332 } else if ( baseTypeAsBasic && baseTypeAsBasic->isInteger() ) {
333 output << " = (" << genType( baseType, "", options ) << ")";
334 output << (*curVal)++;
335 }
336}
337
[0bd3faf]338void CodeGenerator::postvisit( ast::EnumDecl const * decl ) {
[8941b6b]339 extension( decl );
340 auto members = decl->members;
[c75b30a]341 // if ( decl->base && !members.empty() ) {
342 // long long curVal = 0;
343 // for ( auto member : members ) {
344 // auto obj = member.strict_as<ast::ObjectDecl>();
345 // output << "static ";
346 // output << genType( decl->base, mangleName( obj ), options );
347 // genEnumInitializer( visitor, decl->base, output, obj->init, &curVal, options );
348 // output << ";" << endl;
349 // }
350 // } else {
[8941b6b]351 output << "enum ";
352 genAttributes( decl->attributes );
353
354 output << decl->name;
355
356 if ( !members.empty() ) {
357 output << " {" << endl;
358
359 ++indent;
360 for ( auto member : members ) {
361 auto obj = member.strict_as<ast::ObjectDecl>();
362 output << indent << mangleName( obj );
[c75b30a]363 if ( !decl->base && obj->init ) {
[8941b6b]364 output << " = ";
365 obj->init->accept( *visitor );
366 }
367 output << "," << endl;
368 }
369 --indent;
370
371 output << indent << "}";
372 }
[c75b30a]373 // }
[8941b6b]374}
375
[0bd3faf]376void CodeGenerator::postvisit( ast::TraitDecl const * decl ) {
[8941b6b]377 assertf( !options.genC, "TraitDecls should not reach code generation." );
378 extension( decl );
379 handleAggregate( decl, "trait " );
380}
381
[0bd3faf]382void CodeGenerator::postvisit( ast::TypedefDecl const * decl ) {
[8941b6b]383 assertf( !options.genC, "Typedefs should not reach code generation." );
384 output << "typedef " << genType( decl->base, decl->name, options ) << endl;
385}
386
[0bd3faf]387void CodeGenerator::postvisit( ast::TypeDecl const * decl ) {
[8941b6b]388 assertf( !options.genC, "TypeDecls should not reach code generation." );
389 output << decl->genTypeString() << " " << decl->name;
390 if ( decl->sized ) {
391 output << " | sized(" << decl->name << ")";
392 }
393 if ( !decl->assertions.empty() ) {
394 output << " | { ";
395 for ( ast::DeclWithType const * assert : decl->assertions ) {
396 assert->accept( *visitor );
397 output << "; ";
398 }
399 output << " }";
400 }
401}
402
[0bd3faf]403void CodeGenerator::postvisit( ast::StaticAssertDecl const * decl ) {
[8941b6b]404 output << "_Static_assert(";
405 decl->cond->accept( *visitor );
406 output << ", ";
407 decl->msg->accept( *visitor );
408 output << ")";
409}
410
[0bd3faf]411void CodeGenerator::postvisit( ast::Designation const * designation ) {
[8941b6b]412 auto designators = designation->designators;
413 if ( 0 == designators.size() ) return;
414 for ( ast::ptr<ast::Expr> const & des : designators ) {
415 // If the expression is a NameExpr or VariableExpr, then it is a field.
416 if ( des.as<ast::NameExpr>() || des.as<ast::VariableExpr>() ) {
417 output << ".";
418 des->accept( *visitor );
419 // Otherwise, it is a ConstantExpr or CastExpr, then it is an index.
420 } else {
421 output << "[";
422 des->accept( *visitor );
423 output << "]";
424 }
425 }
426 output << " = ";
427}
428
[0bd3faf]429void CodeGenerator::postvisit( ast::SingleInit const * init ) {
[8941b6b]430 init->value->accept( *visitor );
431}
432
[0bd3faf]433void CodeGenerator::postvisit( ast::ListInit const * init ) {
[8941b6b]434 auto initBegin = init->initializers.begin();
435 auto initEnd = init->initializers.end();
436 auto desigBegin = init->designations.begin();
437 auto desigEnd = init->designations.end();
438
439 output << "{ ";
440 if ( initBegin != initEnd ) while (true) {
441 (*desigBegin)->accept( *visitor );
442 (*initBegin)->accept( *visitor );
443 ++initBegin, ++desigBegin;
444 if ( initBegin == initEnd ) break;
445 output << ", ";
446 }
447 output << " }";
448 assertf( initBegin == initEnd && desigBegin == desigEnd,
449 "Initializers and designators not the same length. %s", toCString( init ) );
450}
451
[0bd3faf]452void CodeGenerator::postvisit( ast::ConstructorInit const * init ) {
[8941b6b]453 assertf( !options.genC, "ConstructorInit nodes should not reach code generation." );
454 // This isn't actual code, but labels the constructor/destructor pairs.
455 output << "<ctorinit>{" << endl << ++indent << "ctor: ";
456 if ( init->ctor ) init->ctor->accept( *visitor );
457 output << ", " << endl << indent << "dtor: ";
458 if ( init->dtor ) init->dtor->accept( *visitor );
459 output << endl << --indent << "}";
460}
461
[0bd3faf]462void CodeGenerator::postvisit( ast::ApplicationExpr const * expr ) {
[8941b6b]463 extension( expr );
464 if ( auto var = expr->func.as<ast::VariableExpr>() ) {
465 const OperatorInfo * opInfo;
466 if ( var->var->linkage == ast::Linkage::Intrinsic &&
467 ( opInfo = operatorLookup( var->var->name ) ) ) {
468 auto arg = expr->args.begin();
469 switch ( opInfo->type ) {
470 case OT_INDEX:
471 assert( 2 == expr->args.size() );
472 (*arg++)->accept( *visitor );
473 output << "[";
474 (*arg)->accept( *visitor );
475 output << "]";
476 break;
477
478 // There are no intrinsic definitions of the function call operator.
479 case OT_CALL:
480 assert( false );
481 break;
482
483 case OT_CTOR:
484 case OT_DTOR:
485 // No-op constructor, but run the internal expression.
486 if ( 1 == expr->args.size() ) {
487 output << "(";
488 (*arg++)->accept( *visitor );
489 output << ") /* " << opInfo->inputName << " */";
490 // These are all implemented as some form of assignment.
491 } else if ( 2 == expr->args.size() ) {
492 output << "(";
493 (*arg++)->accept( *visitor );
494 output << opInfo->symbol;
495 (*arg)->accept( *visitor );
496 output << ") /* " << opInfo->inputName << " */";
497 // No constructors with 0 or more than 2 parameters.
498 } else {
499 assert( false );
500 }
501 break;
502
503 case OT_PREFIX:
504 case OT_PREFIXASSIGN:
505 assert( 1 == expr->args.size() );
506 output << "(" << opInfo->symbol;
507 (*arg)->accept( *visitor );
508 output << ")";
509 break;
510
511 case OT_POSTFIX:
512 case OT_POSTFIXASSIGN:
513 assert( 1 == expr->args.size() );
514 (*arg)->accept( *visitor );
515 output << opInfo->symbol;
516 break;
517
518 case OT_INFIX:
519 case OT_INFIXASSIGN:
520 assert( 2 == expr->args.size() );
521 output << "(";
522 (*arg++)->accept( *visitor );
523 output << opInfo->symbol;
524 (*arg)->accept( *visitor );
525 output << ")";
526 break;
527
528 // There are no intrinsic definitions of 0/1 or label address
529 // as function.
530 case OT_CONSTANT:
531 case OT_LABELADDRESS:
532 assert( false );
533 }
534 // TODO: This is a work-around to make it a constant until a proper
535 // constexpr solution is created.
536 } else if ( var->var->linkage == ast::Linkage::BuiltinCFA &&
537 var->var->name == "intptr" ) {
538 output << "((void*)";
539 auto arg = expr->args.begin();
540 (*arg++)->accept( *visitor );
541 output << ")";
542 } else {
543 var->accept( *visitor );
544 output << "(";
545 genCommaList( expr->args );
546 output << ")";
547 }
548 } else {
549 expr->func->accept( *visitor );
550 output << "(";
551 genCommaList( expr->args );
552 output << ")";
553 }
554}
555
[0bd3faf]556void CodeGenerator::postvisit( ast::UntypedExpr const * expr ) {
[8941b6b]557 extension( expr );
558 if ( auto name = expr->func.as<ast::NameExpr>() ) {
559 if ( const OperatorInfo * opInfo = operatorLookup( name->name ) ) {
560 auto arg = expr->args.begin();
561 switch ( opInfo->type ) {
562 case OT_INDEX:
563 assert( 2 == expr->args.size() );
564 (*arg++)->accept( *visitor );
565 output << "[";
566 (*arg)->accept( *visitor );
567 output << "]";
568 break;
569
570 case OT_CALL:
571 assert( false );
572
573 case OT_CTOR:
574 case OT_DTOR:
575 // No-op constructor, but run the internal expression.
576 if ( 1 == expr->args.size() ) {
577 output << "(";
578 (*arg++)->accept( *visitor );
579 output << ")";
580 // These are all implemented as some form of assignment.
581 } else if ( 2 == expr->args.size() ) {
582 output << "(";
583 (*arg++)->accept( *visitor );
584 output << opInfo->symbol;
585 (*arg)->accept( *visitor );
586 output << ") /* " << opInfo->inputName << " */";
587 // No constructors with 0 or more than 2 parameters.
588 } else {
589 assertf( !options.genC, "UntypedExpr constructor/destructor with 0 or more than 2 parameters." );
590 output << "(";
591 (*arg++)->accept( *visitor );
592 output << opInfo->symbol << "{ ";
593 genCommaList( arg, expr->args.end() );
594 output << "}) /* " << opInfo->inputName << " */";
595 }
596 break;
597
598 case OT_PREFIX:
599 case OT_PREFIXASSIGN:
600 case OT_LABELADDRESS:
601 assert( 1 == expr->args.size() );
602 output << "(" << opInfo->symbol;
603 (*arg)->accept( *visitor );
604 output << ")";
605 break;
606
607 case OT_POSTFIX:
608 case OT_POSTFIXASSIGN:
609 assert( 1 == expr->args.size() );
610 (*arg)->accept( *visitor );
611 output << opInfo->symbol;
612 break;
613
614 case OT_INFIX:
615 case OT_INFIXASSIGN:
616 assert( 2 == expr->args.size() );
617 output << "(";
618 (*arg++)->accept( *visitor );
619 output << opInfo->symbol;
620 (*arg)->accept( *visitor );
621 output << ")";
622 break;
623
624 // There are no intrinsic definitions of 0/1 or label address
625 // as function.
626 case OT_CONSTANT:
627 assert( false );
628 }
629 // builtin routines
630 } else {
631 name->accept( *visitor );
632 output << "(";
633 genCommaList( expr->args );
634 output << ")";
635 }
636 } else {
637 expr->func->accept( *visitor );
638 output << "(";
639 genCommaList( expr->args );
640 output << ")";
641 }
642}
643
[0bd3faf]644void CodeGenerator::postvisit( ast::RangeExpr const * expr ) {
[8941b6b]645 expr->low->accept( *visitor );
646 output << " ... ";
647 expr->high->accept( *visitor );
648}
649
[0bd3faf]650void CodeGenerator::postvisit( ast::NameExpr const * expr ) {
[8941b6b]651 extension( expr );
652 if ( const OperatorInfo * opInfo = operatorLookup( expr->name ) ) {
653 if ( OT_CONSTANT == opInfo->type ) {
654 output << opInfo->symbol;
655 } else {
656 output << opInfo->outputName;
657 }
658 } else {
659 output << expr->name;
660 }
661}
662
[0bd3faf]663void CodeGenerator::postvisit( ast::DimensionExpr const * expr ) {
[8941b6b]664 extension( expr );
665 output << "/*non-type*/" << expr->name;
666}
667
[0bd3faf]668void CodeGenerator::postvisit( ast::AddressExpr const * expr ) {
[8941b6b]669 extension( expr );
670 output << "(&";
671 expr->arg->accept( *visitor );
672 output << ")";
673}
674
[0bd3faf]675void CodeGenerator::postvisit( ast::LabelAddressExpr const * expr ) {
[8941b6b]676 extension( expr );
677 output << "(&&" << expr->arg << ")";
678}
679
[0bd3faf]680void CodeGenerator::postvisit( ast::CastExpr const * expr ) {
[8941b6b]681 extension( expr );
682 output << "(";
[ecf3812]683 switch ( expr->kind ) {
684 case ast::CCast:
685 if ( expr->result->isVoid() ) {
686 output << "(void)";
687 } else {
688 output << "(";
689 output << genType( expr->result, "", options );
690 output << ")";
691 }
692 break;
693 case ast::CoerceCast:
694 assertf( ast::CoerceCast != expr->kind, "Coercion cast is not implemented." );
695 // And likely shouldn't reach code generation when it is implemented.
696 break;
697 case ast::ReturnCast:
698 // This should be invisible in the resulting C code.
699 // Can we insert a check here?
700 //assert( ResolvExpr::typesCompatable(???) );
701 if ( options.genC ) break;
702 output << "(return ";
[8941b6b]703 output << genType( expr->result, "", options );
704 output << ")";
[ecf3812]705 break;
[8941b6b]706 }
707 expr->arg->accept( *visitor );
708 output << ")";
709}
710
[0bd3faf]711void CodeGenerator::postvisit( ast::KeywordCastExpr const * expr ) {
[8941b6b]712 assertf( !options.genC, "KeywordCastExpr should not reach code generation." );
713 extension( expr );
714 output << "((" << expr->targetString() << " &)";
715 expr->arg->accept( *visitor );
716 output << ")";
717}
718
[0bd3faf]719void CodeGenerator::postvisit( ast::VirtualCastExpr const * expr ) {
[8941b6b]720 assertf( !options.genC, "VirtualCastExpr should not reach code generation." );
721 extension( expr );
722 // TODO: Is this busted?
723 output << "(virtual ";
724 expr->arg->accept( *visitor );
725 output << ")";
726}
727
[0bd3faf]728void CodeGenerator::postvisit( ast::UntypedMemberExpr const * expr ) {
[8941b6b]729 assertf( !options.genC, "UntypedMemberExpr should not reach code generation." );
730 extension( expr );
731 expr->aggregate->accept( *visitor );
732 output << ".";
733 expr->member->accept( *visitor );
734}
735
[0bd3faf]736void CodeGenerator::postvisit( ast::MemberExpr const * expr ) {
[8941b6b]737 extension( expr );
738 expr->aggregate->accept( *visitor );
739 output << "." << mangleName( expr->member );
740}
741
[0bd3faf]742void CodeGenerator::postvisit( ast::VariableExpr const * expr ) {
[8941b6b]743 extension( expr );
744 const OperatorInfo * opInfo;
[e0330d2c]745 if ( expr->var->linkage == ast::Linkage::Intrinsic
[8941b6b]746 && ( opInfo = operatorLookup( expr->var->name ) )
747 && opInfo->type == OT_CONSTANT ) {
748 output << opInfo->symbol;
749 } else {
750 output << mangleName( expr->var );
751 }
752}
753
[0bd3faf]754void CodeGenerator::postvisit( ast::ConstantExpr const * expr ) {
[8941b6b]755 extension( expr );
756 output << expr->rep;
757}
758
[0bd3faf]759void CodeGenerator::postvisit( ast::SizeofExpr const * expr ) {
[8941b6b]760 extension( expr );
761 output << "sizeof(";
[b6f2e7ab]762 if ( auto type = expr->type.as<ast::TypeofType>() ) {
763 type->expr->accept( *visitor );
[8941b6b]764 } else {
[b6f2e7ab]765 output << genType( expr->type, "", options );
[8941b6b]766 }
767 output << ")";
768}
769
[0bd3faf]770void CodeGenerator::postvisit( ast::AlignofExpr const * expr ) {
[8941b6b]771 // Using the GCC extension to avoid changing the std to C11.
772 extension( expr );
773 output << "__alignof__(";
[b6f2e7ab]774 if ( auto type = expr->type.as<ast::TypeofType>() ) {
775 type->expr->accept( *visitor );
[8941b6b]776 } else {
[b6f2e7ab]777 output << genType( expr->type, "", options );
[8941b6b]778 }
779 output << ")";
780}
781
[0bd3faf]782void CodeGenerator::postvisit( ast::UntypedOffsetofExpr const * expr ) {
[8941b6b]783 assertf( !options.genC, "UntypedOffsetofExpr should not reach code generation." );
784 output << "offsetof(";
785 output << genType( expr->type, "", options );
786 output << ", " << expr->member;
787 output << ")";
788}
789
[0bd3faf]790void CodeGenerator::postvisit( ast::OffsetofExpr const * expr ) {
[8941b6b]791 // Use GCC builtin
792 output << "__builtin_offsetof(";
793 output << genType( expr->type, "", options );
794 output << ", " << mangleName( expr->member );
795 output << ")";
796}
797
[0bd3faf]798void CodeGenerator::postvisit( ast::OffsetPackExpr const * expr ) {
[8941b6b]799 assertf( !options.genC, "OffsetPackExpr should not reach code generation." );
800 output << "__CFA_offsetpack(" << genType( expr->type, "", options ) << ")";
801}
802
[0bd3faf]803void CodeGenerator::postvisit( ast::LogicalExpr const * expr ) {
[8941b6b]804 extension( expr );
805 output << "(";
806 expr->arg1->accept( *visitor );
807 output << ( ( expr->isAnd ) ? " && " : " || " );
808 expr->arg2->accept( *visitor );
809 output << ")";
810}
811
[0bd3faf]812void CodeGenerator::postvisit( ast::ConditionalExpr const * expr ) {
[8941b6b]813 extension( expr );
814 output << "(";
815 expr->arg1->accept( *visitor );
816 output << " ? ";
817 expr->arg2->accept( *visitor );
818 output << " : ";
819 expr->arg3->accept( *visitor );
820 output << ")";
821}
822
[0bd3faf]823void CodeGenerator::postvisit( ast::CommaExpr const * expr ) {
[8941b6b]824 extension( expr );
825 output << "(";
826 if ( options.genC ) {
827 // arg1 of a comma expression is never used, so it can be safely cast
828 // to void to reduce gcc warnings.
829 ast::ptr<ast::Expr> arg1 = new ast::CastExpr( expr->location, expr->arg1 );
830 arg1->accept( *visitor );
831 } else {
832 expr->arg1->accept( *visitor );
833 }
834 output << " , ";
835 expr->arg2->accept( *visitor );
836 output << ")";
837}
838
[0bd3faf]839void CodeGenerator::postvisit( ast::TupleAssignExpr const * expr ) {
[8941b6b]840 assertf( !options.genC, "TupleAssignExpr should not reach code generation." );
841 expr->stmtExpr->accept( *visitor );
842}
843
[0bd3faf]844void CodeGenerator::postvisit( ast::UntypedTupleExpr const * expr ) {
[8941b6b]845 assertf( !options.genC, "UntypedTupleExpr should not reach code generation." );
846 extension( expr );
847 output << "[";
848 genCommaList( expr->exprs );
849 output << "]";
850}
851
[0bd3faf]852void CodeGenerator::postvisit( ast::TupleExpr const * expr ) {
[8941b6b]853 assertf( !options.genC, "TupleExpr should not reach code generation." );
854 extension( expr );
855 output << "[";
856 genCommaList( expr->exprs );
857 output << "]";
858}
859
[0bd3faf]860void CodeGenerator::postvisit( ast::TupleIndexExpr const * expr ) {
[8941b6b]861 assertf( !options.genC, "TupleIndexExpr should not reach code generation." );
862 extension( expr );
863 expr->tuple->accept( *visitor );
864 output << "." << expr->index;
865}
866
[0bd3faf]867void CodeGenerator::postvisit( ast::TypeExpr const * expr ) {
[8941b6b]868 // TODO: Should there be an assertion there?
869 if ( !options.genC ) {
870 output << genType( expr->type, "", options );
871 }
872}
873
[0bd3faf]874void CodeGenerator::postvisit( ast::AsmExpr const * expr ) {
[8941b6b]875 if ( !expr->inout.empty() ) {
876 output << "[ " << expr->inout << " ] ";
877 }
878 expr->constraint->accept( *visitor );
879 output << " ( ";
880 expr->operand->accept( *visitor );
881 output << " )";
882}
883
[0bd3faf]884void CodeGenerator::postvisit( ast::CompoundLiteralExpr const * expr ) {
[8941b6b]885 //assert( expr->result && dynamic_cast<ast::ListInit const *>( expr->init ) );
886 assert( expr->result && expr->init.as<ast::ListInit>() );
887 output << "(" << genType( expr->result, "", options ) << ")";
888 expr->init->accept( *visitor );
889}
890
[0bd3faf]891void CodeGenerator::postvisit( ast::UniqueExpr const * expr ) {
[8941b6b]892 assertf( !options.genC, "UniqueExpr should not reach code generation." );
893 output << "unq<" << expr->id << ">{ ";
894 expr->expr->accept( *visitor );
895 output << " }";
896}
897
[0bd3faf]898void CodeGenerator::postvisit( ast::StmtExpr const * expr ) {
[8941b6b]899 auto stmts = expr->stmts->kids;
900 output << "({" << endl;
901 ++indent;
902 unsigned int numStmts = stmts.size();
903 unsigned int i = 0;
904 for ( ast::ptr<ast::Stmt> const & stmt : stmts ) {
905 output << indent << printLabels( stmt->labels );
906 if ( i + 1 == numStmts ) {
907 // Last statement in a statement expression needs to be handled
908 // specially - cannot cast to void, otherwise the expression
909 // statement has no value.
910 if ( ast::ExprStmt const * exprStmt = stmt.as<ast::ExprStmt>() ) {
911 exprStmt->expr->accept( *visitor );
912 output << ";" << endl;
913 ++i;
914 break;
915 }
916 }
917 stmt->accept( *visitor );
918 output << endl;
919 if ( wantSpacing( stmt ) ) output << endl;
920 ++i;
921 }
922 --indent;
923 output << indent << "})";
924}
925
[0bd3faf]926void CodeGenerator::postvisit( ast::ConstructorExpr const * expr ) {
[8941b6b]927 assertf( !options.genC, "ConstructorExpr should not reach code generation." );
928 expr->callExpr->accept( *visitor );
929}
930
[0bd3faf]931void CodeGenerator::postvisit( ast::DeletedExpr const * expr ) {
[8941b6b]932 assertf( !options.genC, "DeletedExpr should not reach code generation." );
933 expr->expr->accept( *visitor );
934}
935
[0bd3faf]936void CodeGenerator::postvisit( ast::DefaultArgExpr const * expr ) {
[8941b6b]937 assertf( !options.genC, "DefaultArgExpr should not reach code generation." );
938 expr->expr->accept( *visitor );
939}
940
[0bd3faf]941void CodeGenerator::postvisit( ast::GenericExpr const * expr ) {
[8941b6b]942 assertf( !options.genC, "GenericExpr should not reach code generation." );
943 output << "_Generic(";
944 expr->control->accept( *visitor );
945 output << ", ";
946 unsigned int numAssocs = expr->associations.size();
947 unsigned int i = 0;
948 for ( const ast::GenericExpr::Association & assoc : expr->associations ) {
949 if ( nullptr == assoc.type ) {
950 output << "default: ";
951 } else {
952 output << genType( assoc.type, "", options ) << ": ";
953 }
954 assoc.expr->accept( *visitor );
955 ++i;
956 if ( i != numAssocs ) output << ", ";
957 }
958 output << ")";
959}
960
[0bd3faf]961void CodeGenerator::postvisit( ast::CompoundStmt const * stmt ) {
[8941b6b]962 output << "{" << endl;
963
964 ++indent;
965 for ( auto kid : stmt->kids ) {
966 output << indent << printLabels( kid->labels );
967 kid->accept( *visitor );
968 output << endl;
969 if ( wantSpacing( kid ) ) output << endl;
970 }
971 --indent;
972
973 output << indent << "}";
974}
975
[0bd3faf]976void CodeGenerator::postvisit( ast::ExprStmt const * stmt ) {
[8941b6b]977 assert( stmt );
978 // Cast the top-level expression to void to reduce gcc warnings.
979 if ( options.genC ) {
980 ast::ptr<ast::Expr> expr = new ast::CastExpr( stmt->location, stmt->expr );
981 expr->accept( *visitor );
982 } else {
983 stmt->expr->accept( *visitor );
984 }
985 output << ";";
986}
987
[0bd3faf]988void CodeGenerator::postvisit( ast::AsmStmt const * stmt ) {
[8941b6b]989 output << "asm ";
990 if ( stmt->isVolatile ) output << "volatile ";
991 if ( !stmt->gotoLabels.empty() ) output << "goto ";
992 output << "( ";
993 if ( stmt->instruction ) stmt->instruction->accept( *visitor );
994 output << " : ";
995 genCommaList( stmt->output );
996 output << " : ";
997 genCommaList( stmt->input );
998 output << " : ";
999 genCommaList( stmt->clobber );
1000 if ( !stmt->gotoLabels.empty() ) {
1001 output << " : ";
1002 auto it = stmt->gotoLabels.begin();
1003 while (true) {
1004 output << *it++;
1005 if ( stmt->gotoLabels.end() == it ) break;
1006 output << ", ";
1007 }
1008 }
1009 output << " );";
1010}
1011
[0bd3faf]1012void CodeGenerator::postvisit( ast::AsmDecl const * decl ) {
[8941b6b]1013 output << "asm ";
1014 ast::AsmStmt const * stmt = decl->stmt;
1015 output << "( ";
1016 if ( stmt->instruction ) stmt->instruction->accept( *visitor );
1017 output << " )";
1018}
1019
[0bd3faf]1020void CodeGenerator::postvisit( ast::DirectiveDecl const * decl ) {
[8941b6b]1021 // endl prevents spaces before the directive.
1022 output << endl << decl->stmt->directive;
1023}
1024
[0bd3faf]1025void CodeGenerator::postvisit( ast::DirectiveStmt const * stmt ) {
[8941b6b]1026 // endl prevents spaces before the directive.
1027 output << endl << stmt->directive;
1028}
1029
[0bd3faf]1030void CodeGenerator::postvisit( ast::IfStmt const * stmt ) {
[8941b6b]1031 output << "if ( ";
1032 stmt->cond->accept( *visitor );
1033 output << " ) ";
1034
1035 stmt->then->accept( *visitor );
1036
1037 if ( nullptr != stmt->else_ ) {
1038 output << " else ";
1039 stmt->else_->accept( *visitor );
1040 }
1041}
1042
[0bd3faf]1043void CodeGenerator::postvisit( ast::SwitchStmt const * stmt ) {
[8941b6b]1044 output << "switch ( ";
1045 stmt->cond->accept( *visitor );
1046 output << " ) ";
1047
[69ab896]1048 output << "{" << endl;
[8941b6b]1049 ++indent;
1050 for ( auto node : stmt->cases ) {
1051 node->accept( *visitor );
1052 }
1053 --indent;
1054 output << indent << "}";
1055}
1056
[0bd3faf]1057void CodeGenerator::postvisit( ast::CaseClause const * clause ) {
[8941b6b]1058 updateLocation( clause );
1059 output << indent;
1060 if ( clause->isDefault() ) {
1061 output << "default";
1062 } else {
1063 output << "case ";
1064 clause->cond->accept( *visitor );
1065 }
1066 output << ":" << endl;
1067
1068 ++indent;
1069 for ( auto stmt : clause->stmts ) {
1070 output << indent << printLabels( stmt->labels ) ;
1071 stmt->accept( *visitor );
1072 output << endl;
1073 }
1074 --indent;
1075}
1076
[0bd3faf]1077void CodeGenerator::postvisit( ast::BranchStmt const * stmt ) {
[8941b6b]1078 switch ( stmt->kind ) {
1079 case ast::BranchStmt::Goto:
1080 if ( !stmt->target.empty() ) {
1081 output << "goto " << stmt->target;
1082 } else if ( nullptr != stmt->computedTarget ) {
1083 output << "goto *";
1084 stmt->computedTarget->accept( *visitor );
1085 }
1086 break;
1087 case ast::BranchStmt::Break:
1088 output << "break";
1089 break;
1090 case ast::BranchStmt::Continue:
1091 output << "continue";
1092 break;
1093 case ast::BranchStmt::FallThrough:
1094 case ast::BranchStmt::FallThroughDefault:
[d96f7c4]1095 assertf( !options.genC, "fallthrough should not reach code generation." );
1096 output << "fallthrough";
[8941b6b]1097 break;
1098 default:
1099 assertf( false, "Bad BranchStmt value." );
1100 }
[d96f7c4]1101 // Print branch target for labelled break/continue/fallthrough in debug mode.
[8941b6b]1102 if ( !options.genC && stmt->kind != ast::BranchStmt::Goto ) {
1103 if ( !stmt->target.empty() ) {
1104 output << " " << stmt->target;
1105 } else if ( stmt->kind == ast::BranchStmt::FallThrough ) {
1106 output << " default";
1107 }
1108 }
1109 output << ";";
1110}
1111
[0bd3faf]1112void CodeGenerator::postvisit( ast::ReturnStmt const * stmt ) {
[8941b6b]1113 output << "return ";
1114 if ( stmt->expr ) stmt->expr->accept( *visitor );
1115 output << ";";
1116}
1117
[0bd3faf]1118void CodeGenerator::postvisit( ast::ThrowStmt const * stmt ) {
[8941b6b]1119 assertf( !options.genC, "ThrowStmt should not reach code generation." );
1120
1121 output << ((stmt->kind == ast::Terminate) ? "throw" : "throwResume");
1122 if ( stmt->expr ) {
1123 output << " ";
1124 stmt->expr->accept( *visitor );
1125 }
1126 if ( stmt->target ) {
1127 output << " _At ";
1128 stmt->target->accept( *visitor );
1129 }
1130 output << ";";
1131}
1132
[550446f]1133void CodeGenerator::postvisit( ast::TryStmt const * stmt ) {
1134 assertf( !options.genC, "TryStmt should not reach code generation." );
1135
1136 output << "try ";
1137 stmt->body->accept( *visitor );
1138 for ( ast::ptr<ast::CatchClause> const & handler : stmt->handlers ) {
1139 handler->accept( *visitor );
1140 }
1141 if ( stmt->finally ) stmt->finally->accept( *visitor );
1142}
1143
[0bd3faf]1144void CodeGenerator::postvisit( ast::CatchClause const * stmt ) {
[8941b6b]1145 assertf( !options.genC, "CatchClause should not reach code generation." );
1146
[550446f]1147 output << ((stmt->kind == ast::Terminate) ? " catch " : " catchResume ");
[8941b6b]1148 output << "( ";
1149 stmt->decl->accept( *visitor );
1150 if ( stmt->cond ) {
1151 output << " ; ";
1152 stmt->cond->accept( *visitor );
1153 }
1154 output << " ) ";
1155 stmt->body->accept( *visitor );
1156}
1157
[550446f]1158void CodeGenerator::postvisit( ast::FinallyClause const * clause ) {
1159 assertf( !options.genC, "FinallyClause should not reach code generation." );
1160
1161 output << " finally ";
1162 clause->body->accept( *visitor );
1163}
1164
[0bd3faf]1165void CodeGenerator::postvisit( ast::WaitForStmt const * stmt ) {
[8941b6b]1166 assertf( !options.genC, "WaitforStmt should not reach code generation." );
1167
1168 bool first = true;
1169 for ( ast::ptr<ast::WaitForClause> const & clause : stmt->clauses ) {
1170 if (first) { output << "or "; first = false; }
1171 if ( clause->when_cond ) {
1172 output << "when(";
[61e362f]1173 clause->when_cond->accept( *visitor );
[8941b6b]1174 output << ") ";
1175 }
1176 output << "waitfor(";
1177 clause->target->accept( *visitor );
1178 for ( ast::ptr<ast::Expr> const & expr : clause->target_args ) {
1179 output << ",";
1180 expr->accept( *visitor );
1181 }
1182 output << ") ";
1183 clause->stmt->accept( *visitor );
1184 }
1185
1186 if ( stmt->timeout_stmt ) {
1187 output << "or ";
1188 if ( stmt->timeout_cond ) {
1189 output << "when(";
1190 stmt->timeout_cond->accept( *visitor );
1191 output << ") ";
1192 }
1193 output << "timeout(";
1194 stmt->timeout_time->accept( *visitor );
1195 output << ") ";
1196 stmt->timeout_stmt->accept( *visitor );
1197 }
1198
1199 if ( stmt->else_stmt ) {
1200 output << "or ";
1201 if ( stmt->else_cond ) {
1202 output << "when(";
1203 stmt->else_cond->accept( *visitor );
1204 output << ")";
1205 }
1206 output << "else ";
1207 stmt->else_stmt->accept( *visitor );
1208 }
1209}
1210
[0bd3faf]1211void CodeGenerator::postvisit( ast::WithStmt const * stmt ) {
[8941b6b]1212 assertf( !options.genC, "WithStmt should not reach code generation." );
1213
1214 output << "with ( ";
1215 genCommaList( stmt->exprs );
1216 output << " ) ";
1217 stmt->stmt->accept( *visitor );
1218}
1219
[0bd3faf]1220void CodeGenerator::postvisit( ast::WhileDoStmt const * stmt ) {
[8941b6b]1221 if ( stmt->isDoWhile ) {
1222 output << "do";
1223 } else {
1224 output << "while (";
1225 stmt->cond->accept( *visitor );
1226 output << ")";
1227 }
1228 output << " ";
1229
[0bd3faf]1230 output << CodeGenerator::printLabels( stmt->body->labels );
[8941b6b]1231 stmt->body->accept( *visitor );
1232
1233 if ( stmt->isDoWhile ) {
1234 output << " while (";
1235 stmt->cond->accept( *visitor );
[88bc876]1236 output << ( ( nullptr == stmt->else_ ) ? ");" : ")" );
1237 }
1238 if ( stmt->else_ ) {
1239 output << " else ";
1240 stmt->else_->accept( *visitor );
[8941b6b]1241 }
1242}
1243
[0bd3faf]1244void CodeGenerator::postvisit( ast::ForStmt const * stmt ) {
[8941b6b]1245 // Initializer is always hoised so don't generate it.
1246 // TODO: Do an assertion check?
1247 output << "for (;";
1248
1249 if ( nullptr != stmt->cond ) {
1250 stmt->cond->accept( *visitor );
1251 }
1252 output << ";";
1253
1254 if ( nullptr != stmt->inc ) {
1255 // cast the top-level expression to void to reduce gcc warnings.
1256 ast::Expr * expr = new ast::CastExpr( stmt->inc );
1257 expr->accept( *visitor );
1258 }
1259 output << ") ";
1260
1261 if ( nullptr != stmt->body ) {
1262 output << printLabels( stmt->body->labels );
1263 stmt->body->accept( *visitor );
1264 }
[88bc876]1265
1266 if ( nullptr != stmt->else_ ) {
1267 assertf( !options.genC, "Loop else should not reach code generation." );
1268 output << " else ";
1269 stmt->else_->accept( *visitor );
1270 }
[8941b6b]1271}
1272
[0bd3faf]1273void CodeGenerator::postvisit( ast::NullStmt const * ) {
[8941b6b]1274 output << "/* null statement */ ;";
1275}
1276
[0bd3faf]1277void CodeGenerator::postvisit( ast::DeclStmt const * stmt ) {
[8941b6b]1278 stmt->decl->accept( *visitor );
1279
1280 if ( doSemicolon( stmt->decl ) ) output << ";";
1281}
1282
[0bd3faf]1283void CodeGenerator::postvisit( ast::ImplicitCtorDtorStmt const * stmt ) {
[8941b6b]1284 assertf( !options.genC, "ImplicitCtorCtorStmt should not reach code generation." );
1285 stmt->callStmt->accept( *visitor );
1286}
1287
[0bd3faf]1288void CodeGenerator::postvisit( ast::MutexStmt const * stmt ) {
[8941b6b]1289 assertf( !options.genC, "MutexStmt should not reach code generation." );
1290 // TODO: But this isn't what a mutex statement looks like.
1291 stmt->stmt->accept( *visitor );
1292}
1293
[6e7ed0aa]1294std::string genName( ast::DeclWithType const * decl ) {
1295 if ( const OperatorInfo * opInfo = operatorLookup( decl->name ) ) {
1296 return opInfo->outputName;
1297 } else {
1298 return decl->name;
1299 }
1300}
[8941b6b]1301
1302} // namespace CodeGen
Note: See TracBrowser for help on using the repository browser.