// // Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo // // The contents of this file are covered under the licence agreement in the // file "LICENCE" distributed with Cforall. // // CodeGenerator.cpp -- // // Author : Andrew Beach // Created On : Tue Oct 17 15:54:00 2023 // Last Modified By : Andrew Beach // Last Modified On : Wed Oct 25 18:28:00 2023 // Update Count : 0 // #include "CodeGenerator.hpp" #include "AST/Print.hpp" #include "OperatorTable.hpp" // for OperatorInfo, operatorLookup #include "CodeGen/GenType.hpp" // for genType #include "Common/ToString.hpp" // for toString #include "Common/UniqueName.hpp" // for UniqueName namespace CodeGen { int CodeGenerator::tabsize = 4; // The kinds of statements that should be followed by whitespace. static bool wantSpacing( ast::Stmt const * stmt ) { return dynamic_cast( stmt ) || dynamic_cast( stmt ) || dynamic_cast( stmt ) || dynamic_cast( stmt ) || dynamic_cast( stmt ); } void CodeGenerator::extension( ast::Expr const * expr ) { if ( expr->extension ) output << "__extension__ "; } void CodeGenerator::extension( ast::Decl const * decl ) { if ( decl->extension ) output << "__extension__ "; } void CodeGenerator::asmName( ast::DeclWithType const * decl ) { if ( auto asmName = decl->asmName.as() ) { output << " asm ( " << asmName->rep << " )"; } } CodeGenerator::LabelPrinter & CodeGenerator::LabelPrinter::operator()( std::vector const & l ) { labels = &l; return *this; } std::ostream & CodeGenerator::LabelPrinter::operator()( std::ostream & output ) const { const std::vector & labels = *this->labels; for ( const ast::Label & label : labels ) { output << label.name + ": "; this->cg.genAttributes( label.attributes ); } return output; } // Using updateLocation at the beginning of a node and endl within a node // should become the method of formating. void CodeGenerator::updateLocation( CodeLocation const & to ) { // Skip if linemarks shouldn't appear or if location is unset. if ( !options.lineMarks || to.isUnset() ) return; if ( currentLocation.followedBy( to, 0 ) ) { return; } else if ( currentLocation.followedBy( to, 1 ) ) { output << "\n" << indent; currentLocation.first_line += 1; } else if ( currentLocation.followedBy( to, 2 ) ) { output << "\n\n" << indent; currentLocation.first_line += 2; } else { output << "\n# " << to.first_line << " \"" << to.filename << "\"\n" << indent; currentLocation = to; } output << std::flush; } void CodeGenerator::updateLocation( ast::ParseNode const * to ) { updateLocation( to->location ); } std::ostream & CodeGenerator::LineEnder::operator()( std::ostream & os ) const { os << "\n" << std::flush; cg.currentLocation.first_line++; return os; } CodeGenerator::CodeGenerator( std::ostream & os, const Options & options ) : indent( 0, CodeGenerator::tabsize ), output( os ), options( options ), printLabels( *this ), endl( *this ) {} std::string CodeGenerator::mangleName( ast::DeclWithType const * decl ) { if ( !options.pretty && decl->linkage.is_mangled && decl->mangleName != "" ) { return decl->scopedMangleName(); } else { return decl->name; } } void CodeGenerator::genAttributes( const std::vector> & attributes ) { if ( attributes.empty() ) return; output << "__attribute__ (("; for ( auto attr = attributes.begin() ;; ) { output << (*attr)->name; if ( !(*attr)->params.empty() ) { output << "("; genCommaList( (*attr)->params ); output << ")"; } if ( ++attr == attributes.end() ) break; output << ","; } output << ")) "; } void CodeGenerator::previsit( ast::Node const * ) { // All traversal is manual. // TODO: Which means the ast::Pass is just providing a default no visit? visit_children = false; } void CodeGenerator::previsit( ast::ParseNode const * node ) { previsit( (ast::Node const *)node ); updateLocation( node ); } void CodeGenerator::postvisit( ast::Node const * node ) { std::stringstream ss; ast::print( ss, node ); assertf( false, "Unhandled node reached in CodeGenerator: %s", ss.str().c_str() ); } void CodeGenerator::previsit( ast::Expr const * expr ) { previsit( (ast::ParseNode const *)expr ); GuardAction( [this, expr](){ if ( options.printExprTypes && expr->result ) { output << " /* " << genType( expr->result, "", options ) << " */ "; } } ); } void CodeGenerator::postvisit( ast::FunctionDecl const * decl ) { // Deleted decls should never be used, so don't print them in C. if ( decl->isDeleted && options.genC ) return; extension( decl ); genAttributes( decl->attributes ); handleStorageClass( decl ); ast::print( output, decl->funcSpec ); Options subOptions = options; subOptions.anonymousUnused = decl->stmts; std::ostringstream acc; ast::Pass subCG( acc, subOptions ); // Add the forall clause. if ( !decl->type_params.empty() ) { assertf( !options.genC, "FunctionDecl forall should not reach code generation." ); acc << "forall("; subCG.core.genCommaList( decl->type_params ); acc << ")" << std::endl; } // The forall clause should be printed early as part of the preamble. output << acc.str(); acc.str(""); acc << mangleName( decl ); if ( 0 == decl->params.size() ) { if ( decl->type->isVarArgs ) { acc << "()"; } else { acc << "(void)"; } } else { acc << "("; subCG.core.genCommaList( decl->params ); if ( decl->type->isVarArgs ) { acc << ", ..."; } acc << ")"; } if ( 1 == decl->returns.size() ) { ast::ptr const & type = decl->returns[0]->get_type(); output << genTypeNoAttr( type, acc.str(), subOptions ); } else if ( 0 == decl->returns.size() ) { output << "void " + acc.str(); } else { assertf( !options.genC, "Multi-return should not reach code generation." ); ast::ptr type = new ast::TupleType( copy( decl->type->returns ) ); output << genTypeNoAttr( type, acc.str(), subOptions ); } asmName( decl ); if ( decl->stmts ) { decl->stmts->accept( *visitor ); } if ( decl->isDeleted ) { output << " = void"; } } ast::ObjectDecl const * CodeGenerator::postvisit( ast::ObjectDecl const * decl ) { // Deleted decls should never be used, so don't print them in C. if ( decl->isDeleted && options.genC ) return decl; // GCC allows an empty declarator (no name) for bit-fields and C // states: 6.7.2.1 Structure and union specifiers, point 4, page 113: // If the (bit field) value is zero, the declaration shall have no // declarator. For anything else, the anonymous name refers to the // anonymous object for plan9 inheritance. if ( decl->name.empty() && options.genC && !decl->bitfieldWidth ) { // TODO: Should this be changed in a pervious pass? auto mutDecl = ast::mutate( decl ); // Only generate an anonymous name when generating C code, // otherwise it clutters the output too much. static UniqueName name = { "__anonymous_object" }; mutDecl->name = name.newName(); // Stops unused parameter warnings. if ( options.anonymousUnused ) { mutDecl->attributes.push_back( new ast::Attribute( "unused" ) ); } decl = mutDecl; } extension( decl ); genAttributes( decl->attributes ); handleStorageClass( decl ); output << genType( decl->type, mangleName( decl ), Options( options.pretty, options.genC, false, false ) ); asmName( decl ); if ( decl->init ) { output << " = "; decl->init->accept( *visitor ); } if ( decl->isDeleted ) { output << " = void"; } if ( decl->bitfieldWidth ) { output << ":"; decl->bitfieldWidth->accept( *visitor ); } return decl; } void CodeGenerator::handleStorageClass( ast::DeclWithType const * decl ) { if ( decl->storage.any() ) { ast::print( output, decl->storage ); } } void CodeGenerator::handleAggregate( ast::AggregateDecl const * decl, std::string const & kind ) { if ( !decl->params.empty() && !options.genC ) { output << "forall("; genCommaList( decl->params ); output << ")\n" << indent << std::flush; } output << kind; genAttributes( decl->attributes ); output << decl->name; if ( decl->body ) { auto & members = decl->members; output << " {" << endl; ++indent; for ( auto & member : members ) { output << indent; member->accept( *visitor ); output << ";" << endl; } --indent; output << indent << "}"; } } void CodeGenerator::postvisit( ast::StructDecl const * decl ) { extension( decl ); handleAggregate( decl, "struct " ); } void CodeGenerator::postvisit( ast::UnionDecl const * decl ) { extension( decl ); handleAggregate( decl, "union " ); } template inline void genEnumInitializer( ast::Pass * visitor, ast::Type const * baseType, std::ostream & output, ast::Init const * init, long long * curVal, Options options ) { auto baseTypeAsBasic = dynamic_cast( baseType ); // Value is provided. if ( init ) { output << " = (" << genType( baseType, "", options ) << ")"; init->accept( *visitor ); // If it is an integral type and initilizer offered, // need to update the curVal. if ( baseTypeAsBasic && baseTypeAsBasic->isInteger() ) { ast::Expr const * expr = ((ast::SingleInit const *)(init))->value; // Unwrap introduced cast. while ( auto temp = dynamic_cast( expr ) ) { expr = temp->arg; } *curVal = ((ast::ConstantExpr const *)(expr))->intValue() + 1; } // Generate next value from previous value. } else if ( baseTypeAsBasic && baseTypeAsBasic->isInteger() ) { output << " = (" << genType( baseType, "", options ) << ")"; output << (*curVal)++; } } void CodeGenerator::postvisit( ast::EnumDecl const * decl ) { extension( decl ); auto members = decl->members; // if ( decl->base && !members.empty() ) { // long long curVal = 0; // for ( auto member : members ) { // auto obj = member.strict_as(); // output << "static "; // output << genType( decl->base, mangleName( obj ), options ); // genEnumInitializer( visitor, decl->base, output, obj->init, &curVal, options ); // output << ";" << endl; // } // } else { output << "enum "; genAttributes( decl->attributes ); output << decl->name; if ( !members.empty() ) { output << " {" << endl; ++indent; for ( auto member : members ) { auto obj = member.strict_as(); output << indent << mangleName( obj ); if ( !decl->base && obj->init ) { output << " = "; obj->init->accept( *visitor ); } output << "," << endl; } --indent; output << indent << "}"; } // } } void CodeGenerator::postvisit( ast::TraitDecl const * decl ) { assertf( !options.genC, "TraitDecls should not reach code generation." ); extension( decl ); handleAggregate( decl, "trait " ); } void CodeGenerator::postvisit( ast::TypedefDecl const * decl ) { assertf( !options.genC, "Typedefs should not reach code generation." ); output << "typedef " << genType( decl->base, decl->name, options ) << endl; } void CodeGenerator::postvisit( ast::TypeDecl const * decl ) { assertf( !options.genC, "TypeDecls should not reach code generation." ); output << decl->genTypeString() << " " << decl->name; if ( decl->sized ) { output << " | sized(" << decl->name << ")"; } if ( !decl->assertions.empty() ) { output << " | { "; for ( ast::DeclWithType const * assert : decl->assertions ) { assert->accept( *visitor ); output << "; "; } output << " }"; } } void CodeGenerator::postvisit( ast::StaticAssertDecl const * decl ) { output << "_Static_assert("; decl->cond->accept( *visitor ); output << ", "; decl->msg->accept( *visitor ); output << ")"; } void CodeGenerator::postvisit( ast::Designation const * designation ) { auto designators = designation->designators; if ( 0 == designators.size() ) return; for ( ast::ptr const & des : designators ) { // If the expression is a NameExpr or VariableExpr, then it is a field. if ( des.as() || des.as() ) { output << "."; des->accept( *visitor ); // Otherwise, it is a ConstantExpr or CastExpr, then it is an index. } else { output << "["; des->accept( *visitor ); output << "]"; } } output << " = "; } void CodeGenerator::postvisit( ast::SingleInit const * init ) { init->value->accept( *visitor ); } void CodeGenerator::postvisit( ast::ListInit const * init ) { auto initBegin = init->initializers.begin(); auto initEnd = init->initializers.end(); auto desigBegin = init->designations.begin(); auto desigEnd = init->designations.end(); output << "{ "; if ( initBegin != initEnd ) while (true) { (*desigBegin)->accept( *visitor ); (*initBegin)->accept( *visitor ); ++initBegin, ++desigBegin; if ( initBegin == initEnd ) break; output << ", "; } output << " }"; assertf( initBegin == initEnd && desigBegin == desigEnd, "Initializers and designators not the same length. %s", toCString( init ) ); } void CodeGenerator::postvisit( ast::ConstructorInit const * init ) { assertf( !options.genC, "ConstructorInit nodes should not reach code generation." ); // This isn't actual code, but labels the constructor/destructor pairs. output << "{" << endl << ++indent << "ctor: "; if ( init->ctor ) init->ctor->accept( *visitor ); output << ", " << endl << indent << "dtor: "; if ( init->dtor ) init->dtor->accept( *visitor ); output << endl << --indent << "}"; } void CodeGenerator::postvisit( ast::ApplicationExpr const * expr ) { extension( expr ); if ( auto var = expr->func.as() ) { const OperatorInfo * opInfo; if ( var->var->linkage == ast::Linkage::Intrinsic && ( opInfo = operatorLookup( var->var->name ) ) ) { auto arg = expr->args.begin(); switch ( opInfo->type ) { case OT_INDEX: assert( 2 == expr->args.size() ); (*arg++)->accept( *visitor ); output << "["; (*arg)->accept( *visitor ); output << "]"; break; // There are no intrinsic definitions of the function call operator. case OT_CALL: assert( false ); break; case OT_CTOR: case OT_DTOR: // No-op constructor, but run the internal expression. if ( 1 == expr->args.size() ) { output << "("; (*arg++)->accept( *visitor ); output << ") /* " << opInfo->inputName << " */"; // These are all implemented as some form of assignment. } else if ( 2 == expr->args.size() ) { output << "("; (*arg++)->accept( *visitor ); output << opInfo->symbol; (*arg)->accept( *visitor ); output << ") /* " << opInfo->inputName << " */"; // No constructors with 0 or more than 2 parameters. } else { assert( false ); } break; case OT_PREFIX: case OT_PREFIXASSIGN: assert( 1 == expr->args.size() ); output << "(" << opInfo->symbol; (*arg)->accept( *visitor ); output << ")"; break; case OT_POSTFIX: case OT_POSTFIXASSIGN: assert( 1 == expr->args.size() ); (*arg)->accept( *visitor ); output << opInfo->symbol; break; case OT_INFIX: case OT_INFIXASSIGN: assert( 2 == expr->args.size() ); output << "("; (*arg++)->accept( *visitor ); output << opInfo->symbol; (*arg)->accept( *visitor ); output << ")"; break; // There are no intrinsic definitions of 0/1 or label address // as function. case OT_CONSTANT: case OT_LABELADDRESS: assert( false ); } // TODO: This is a work-around to make it a constant until a proper // constexpr solution is created. } else if ( var->var->linkage == ast::Linkage::BuiltinCFA && var->var->name == "intptr" ) { output << "((void*)"; auto arg = expr->args.begin(); (*arg++)->accept( *visitor ); output << ")"; } else { var->accept( *visitor ); output << "("; genCommaList( expr->args ); output << ")"; } } else { expr->func->accept( *visitor ); output << "("; genCommaList( expr->args ); output << ")"; } } void CodeGenerator::postvisit( ast::UntypedExpr const * expr ) { extension( expr ); if ( auto name = expr->func.as() ) { if ( const OperatorInfo * opInfo = operatorLookup( name->name ) ) { auto arg = expr->args.begin(); switch ( opInfo->type ) { case OT_INDEX: assert( 2 == expr->args.size() ); (*arg++)->accept( *visitor ); output << "["; (*arg)->accept( *visitor ); output << "]"; break; case OT_CALL: assert( false ); case OT_CTOR: case OT_DTOR: // No-op constructor, but run the internal expression. if ( 1 == expr->args.size() ) { output << "("; (*arg++)->accept( *visitor ); output << ")"; // These are all implemented as some form of assignment. } else if ( 2 == expr->args.size() ) { output << "("; (*arg++)->accept( *visitor ); output << opInfo->symbol; (*arg)->accept( *visitor ); output << ") /* " << opInfo->inputName << " */"; // No constructors with 0 or more than 2 parameters. } else { assertf( !options.genC, "UntypedExpr constructor/destructor with 0 or more than 2 parameters." ); output << "("; (*arg++)->accept( *visitor ); output << opInfo->symbol << "{ "; genCommaList( arg, expr->args.end() ); output << "}) /* " << opInfo->inputName << " */"; } break; case OT_PREFIX: case OT_PREFIXASSIGN: case OT_LABELADDRESS: assert( 1 == expr->args.size() ); output << "(" << opInfo->symbol; (*arg)->accept( *visitor ); output << ")"; break; case OT_POSTFIX: case OT_POSTFIXASSIGN: assert( 1 == expr->args.size() ); (*arg)->accept( *visitor ); output << opInfo->symbol; break; case OT_INFIX: case OT_INFIXASSIGN: assert( 2 == expr->args.size() ); output << "("; (*arg++)->accept( *visitor ); output << opInfo->symbol; (*arg)->accept( *visitor ); output << ")"; break; // There are no intrinsic definitions of 0/1 or label address // as function. case OT_CONSTANT: assert( false ); } // builtin routines } else { name->accept( *visitor ); output << "("; genCommaList( expr->args ); output << ")"; } } else { expr->func->accept( *visitor ); output << "("; genCommaList( expr->args ); output << ")"; } } void CodeGenerator::postvisit( ast::RangeExpr const * expr ) { expr->low->accept( *visitor ); output << " ... "; expr->high->accept( *visitor ); } void CodeGenerator::postvisit( ast::NameExpr const * expr ) { extension( expr ); if ( const OperatorInfo * opInfo = operatorLookup( expr->name ) ) { if ( OT_CONSTANT == opInfo->type ) { output << opInfo->symbol; } else { output << opInfo->outputName; } } else { output << expr->name; } } void CodeGenerator::postvisit( ast::DimensionExpr const * expr ) { extension( expr ); output << "/*non-type*/" << expr->name; } void CodeGenerator::postvisit( ast::AddressExpr const * expr ) { extension( expr ); output << "(&"; expr->arg->accept( *visitor ); output << ")"; } void CodeGenerator::postvisit( ast::LabelAddressExpr const * expr ) { extension( expr ); output << "(&&" << expr->arg << ")"; } void CodeGenerator::postvisit( ast::CastExpr const * expr ) { extension( expr ); output << "("; if ( expr->result->isVoid() ) { output << "(void)"; } else { output << "("; output << genType( expr->result, "", options ); output << ")"; } expr->arg->accept( *visitor ); output << ")"; } void CodeGenerator::postvisit( ast::KeywordCastExpr const * expr ) { assertf( !options.genC, "KeywordCastExpr should not reach code generation." ); extension( expr ); output << "((" << expr->targetString() << " &)"; expr->arg->accept( *visitor ); output << ")"; } void CodeGenerator::postvisit( ast::VirtualCastExpr const * expr ) { assertf( !options.genC, "VirtualCastExpr should not reach code generation." ); extension( expr ); // TODO: Is this busted? output << "(virtual "; expr->arg->accept( *visitor ); output << ")"; } void CodeGenerator::postvisit( ast::UntypedMemberExpr const * expr ) { assertf( !options.genC, "UntypedMemberExpr should not reach code generation." ); extension( expr ); expr->aggregate->accept( *visitor ); output << "."; expr->member->accept( *visitor ); } void CodeGenerator::postvisit( ast::MemberExpr const * expr ) { extension( expr ); expr->aggregate->accept( *visitor ); output << "." << mangleName( expr->member ); } void CodeGenerator::postvisit( ast::VariableExpr const * expr ) { extension( expr ); const OperatorInfo * opInfo; if ( dynamic_cast( expr->var->get_type() ) ) { output << "0"; } else if ( expr->var->linkage == ast::Linkage::Intrinsic && ( opInfo = operatorLookup( expr->var->name ) ) && opInfo->type == OT_CONSTANT ) { output << opInfo->symbol; } else { output << mangleName( expr->var ); } } void CodeGenerator::postvisit( ast::ConstantExpr const * expr ) { extension( expr ); output << expr->rep; } void CodeGenerator::postvisit( ast::SizeofExpr const * expr ) { extension( expr ); output << "sizeof("; if ( expr->type ) { output << genType( expr->type, "", options ); } else { expr->expr->accept( *visitor ); } output << ")"; } void CodeGenerator::postvisit( ast::AlignofExpr const * expr ) { // Using the GCC extension to avoid changing the std to C11. extension( expr ); output << "__alignof__("; if ( expr->type ) { output << genType( expr->type, "", options ); } else { expr->expr->accept( *visitor ); } output << ")"; } void CodeGenerator::postvisit( ast::UntypedOffsetofExpr const * expr ) { assertf( !options.genC, "UntypedOffsetofExpr should not reach code generation." ); output << "offsetof("; output << genType( expr->type, "", options ); output << ", " << expr->member; output << ")"; } void CodeGenerator::postvisit( ast::OffsetofExpr const * expr ) { // Use GCC builtin output << "__builtin_offsetof("; output << genType( expr->type, "", options ); output << ", " << mangleName( expr->member ); output << ")"; } void CodeGenerator::postvisit( ast::OffsetPackExpr const * expr ) { assertf( !options.genC, "OffsetPackExpr should not reach code generation." ); output << "__CFA_offsetpack(" << genType( expr->type, "", options ) << ")"; } void CodeGenerator::postvisit( ast::LogicalExpr const * expr ) { extension( expr ); output << "("; expr->arg1->accept( *visitor ); output << ( ( expr->isAnd ) ? " && " : " || " ); expr->arg2->accept( *visitor ); output << ")"; } void CodeGenerator::postvisit( ast::ConditionalExpr const * expr ) { extension( expr ); output << "("; expr->arg1->accept( *visitor ); output << " ? "; expr->arg2->accept( *visitor ); output << " : "; expr->arg3->accept( *visitor ); output << ")"; } void CodeGenerator::postvisit( ast::CommaExpr const * expr ) { extension( expr ); output << "("; if ( options.genC ) { // arg1 of a comma expression is never used, so it can be safely cast // to void to reduce gcc warnings. ast::ptr arg1 = new ast::CastExpr( expr->location, expr->arg1 ); arg1->accept( *visitor ); } else { expr->arg1->accept( *visitor ); } output << " , "; expr->arg2->accept( *visitor ); output << ")"; } void CodeGenerator::postvisit( ast::TupleAssignExpr const * expr ) { assertf( !options.genC, "TupleAssignExpr should not reach code generation." ); expr->stmtExpr->accept( *visitor ); } void CodeGenerator::postvisit( ast::UntypedTupleExpr const * expr ) { assertf( !options.genC, "UntypedTupleExpr should not reach code generation." ); extension( expr ); output << "["; genCommaList( expr->exprs ); output << "]"; } void CodeGenerator::postvisit( ast::TupleExpr const * expr ) { assertf( !options.genC, "TupleExpr should not reach code generation." ); extension( expr ); output << "["; genCommaList( expr->exprs ); output << "]"; } void CodeGenerator::postvisit( ast::TupleIndexExpr const * expr ) { assertf( !options.genC, "TupleIndexExpr should not reach code generation." ); extension( expr ); expr->tuple->accept( *visitor ); output << "." << expr->index; } void CodeGenerator::postvisit( ast::TypeExpr const * expr ) { // TODO: Should there be an assertion there? if ( !options.genC ) { output << genType( expr->type, "", options ); } } void CodeGenerator::postvisit( ast::AsmExpr const * expr ) { if ( !expr->inout.empty() ) { output << "[ " << expr->inout << " ] "; } expr->constraint->accept( *visitor ); output << " ( "; expr->operand->accept( *visitor ); output << " )"; } void CodeGenerator::postvisit( ast::CompoundLiteralExpr const * expr ) { //assert( expr->result && dynamic_cast( expr->init ) ); assert( expr->result && expr->init.as() ); output << "(" << genType( expr->result, "", options ) << ")"; expr->init->accept( *visitor ); } void CodeGenerator::postvisit( ast::UniqueExpr const * expr ) { assertf( !options.genC, "UniqueExpr should not reach code generation." ); output << "unq<" << expr->id << ">{ "; expr->expr->accept( *visitor ); output << " }"; } void CodeGenerator::postvisit( ast::StmtExpr const * expr ) { auto stmts = expr->stmts->kids; output << "({" << endl; ++indent; unsigned int numStmts = stmts.size(); unsigned int i = 0; for ( ast::ptr const & stmt : stmts ) { output << indent << printLabels( stmt->labels ); if ( i + 1 == numStmts ) { // Last statement in a statement expression needs to be handled // specially - cannot cast to void, otherwise the expression // statement has no value. if ( ast::ExprStmt const * exprStmt = stmt.as() ) { exprStmt->expr->accept( *visitor ); output << ";" << endl; ++i; break; } } stmt->accept( *visitor ); output << endl; if ( wantSpacing( stmt ) ) output << endl; ++i; } --indent; output << indent << "})"; } void CodeGenerator::postvisit( ast::ConstructorExpr const * expr ) { assertf( !options.genC, "ConstructorExpr should not reach code generation." ); expr->callExpr->accept( *visitor ); } void CodeGenerator::postvisit( ast::DeletedExpr const * expr ) { assertf( !options.genC, "DeletedExpr should not reach code generation." ); expr->expr->accept( *visitor ); } void CodeGenerator::postvisit( ast::DefaultArgExpr const * expr ) { assertf( !options.genC, "DefaultArgExpr should not reach code generation." ); expr->expr->accept( *visitor ); } void CodeGenerator::postvisit( ast::GenericExpr const * expr ) { assertf( !options.genC, "GenericExpr should not reach code generation." ); output << "_Generic("; expr->control->accept( *visitor ); output << ", "; unsigned int numAssocs = expr->associations.size(); unsigned int i = 0; for ( const ast::GenericExpr::Association & assoc : expr->associations ) { if ( nullptr == assoc.type ) { output << "default: "; } else { output << genType( assoc.type, "", options ) << ": "; } assoc.expr->accept( *visitor ); ++i; if ( i != numAssocs ) output << ", "; } output << ")"; } void CodeGenerator::postvisit( ast::CompoundStmt const * stmt ) { output << "{" << endl; ++indent; for ( auto kid : stmt->kids ) { output << indent << printLabels( kid->labels ); kid->accept( *visitor ); output << endl; if ( wantSpacing( kid ) ) output << endl; } --indent; output << indent << "}"; } void CodeGenerator::postvisit( ast::ExprStmt const * stmt ) { assert( stmt ); // Cast the top-level expression to void to reduce gcc warnings. if ( options.genC ) { ast::ptr expr = new ast::CastExpr( stmt->location, stmt->expr ); expr->accept( *visitor ); } else { stmt->expr->accept( *visitor ); } output << ";"; } void CodeGenerator::postvisit( ast::AsmStmt const * stmt ) { output << "asm "; if ( stmt->isVolatile ) output << "volatile "; if ( !stmt->gotoLabels.empty() ) output << "goto "; output << "( "; if ( stmt->instruction ) stmt->instruction->accept( *visitor ); output << " : "; genCommaList( stmt->output ); output << " : "; genCommaList( stmt->input ); output << " : "; genCommaList( stmt->clobber ); if ( !stmt->gotoLabels.empty() ) { output << " : "; auto it = stmt->gotoLabels.begin(); while (true) { output << *it++; if ( stmt->gotoLabels.end() == it ) break; output << ", "; } } output << " );"; } void CodeGenerator::postvisit( ast::AsmDecl const * decl ) { output << "asm "; ast::AsmStmt const * stmt = decl->stmt; output << "( "; if ( stmt->instruction ) stmt->instruction->accept( *visitor ); output << " )"; } void CodeGenerator::postvisit( ast::DirectiveDecl const * decl ) { // endl prevents spaces before the directive. output << endl << decl->stmt->directive; } void CodeGenerator::postvisit( ast::DirectiveStmt const * stmt ) { // endl prevents spaces before the directive. output << endl << stmt->directive; } void CodeGenerator::postvisit( ast::IfStmt const * stmt ) { output << "if ( "; stmt->cond->accept( *visitor ); output << " ) "; stmt->then->accept( *visitor ); if ( nullptr != stmt->else_ ) { output << " else "; stmt->else_->accept( *visitor ); } } void CodeGenerator::postvisit( ast::SwitchStmt const * stmt ) { output << "switch ( "; stmt->cond->accept( *visitor ); output << " ) "; output << "{" << endl; ++indent; for ( auto node : stmt->cases ) { node->accept( *visitor ); } --indent; output << indent << "}"; } void CodeGenerator::postvisit( ast::CaseClause const * clause ) { updateLocation( clause ); output << indent; if ( clause->isDefault() ) { output << "default"; } else { output << "case "; clause->cond->accept( *visitor ); } output << ":" << endl; ++indent; for ( auto stmt : clause->stmts ) { output << indent << printLabels( stmt->labels ) ; stmt->accept( *visitor ); output << endl; } --indent; } void CodeGenerator::postvisit( ast::BranchStmt const * stmt ) { switch ( stmt->kind ) { case ast::BranchStmt::Goto: if ( !stmt->target.empty() ) { output << "goto " << stmt->target; } else if ( nullptr != stmt->computedTarget ) { output << "goto *"; stmt->computedTarget->accept( *visitor ); } break; case ast::BranchStmt::Break: output << "break"; break; case ast::BranchStmt::Continue: output << "continue"; break; case ast::BranchStmt::FallThrough: case ast::BranchStmt::FallThroughDefault: assertf( !options.genC, "fallthru should not reach code generation." ); output << "fallthru"; break; default: assertf( false, "Bad BranchStmt value." ); } // Print branch target for labelled break/continue/fallthru in debug mode. if ( !options.genC && stmt->kind != ast::BranchStmt::Goto ) { if ( !stmt->target.empty() ) { output << " " << stmt->target; } else if ( stmt->kind == ast::BranchStmt::FallThrough ) { output << " default"; } } output << ";"; } void CodeGenerator::postvisit( ast::ReturnStmt const * stmt ) { output << "return "; if ( stmt->expr ) stmt->expr->accept( *visitor ); output << ";"; } void CodeGenerator::postvisit( ast::ThrowStmt const * stmt ) { assertf( !options.genC, "ThrowStmt should not reach code generation." ); output << ((stmt->kind == ast::Terminate) ? "throw" : "throwResume"); if ( stmt->expr ) { output << " "; stmt->expr->accept( *visitor ); } if ( stmt->target ) { output << " _At "; stmt->target->accept( *visitor ); } output << ";"; } void CodeGenerator::postvisit( ast::CatchClause const * stmt ) { assertf( !options.genC, "CatchClause should not reach code generation." ); output << ((stmt->kind == ast::Terminate) ? "catch" : "catchResume"); output << "( "; stmt->decl->accept( *visitor ); if ( stmt->cond ) { output << " ; "; stmt->cond->accept( *visitor ); } output << " ) "; stmt->body->accept( *visitor ); } void CodeGenerator::postvisit( ast::WaitForStmt const * stmt ) { assertf( !options.genC, "WaitforStmt should not reach code generation." ); bool first = true; for ( ast::ptr const & clause : stmt->clauses ) { if (first) { output << "or "; first = false; } if ( clause->when_cond ) { output << "when("; clause->when_cond->accept( *visitor ); output << ") "; } output << "waitfor("; clause->target->accept( *visitor ); for ( ast::ptr const & expr : clause->target_args ) { output << ","; expr->accept( *visitor ); } output << ") "; clause->stmt->accept( *visitor ); } if ( stmt->timeout_stmt ) { output << "or "; if ( stmt->timeout_cond ) { output << "when("; stmt->timeout_cond->accept( *visitor ); output << ") "; } output << "timeout("; stmt->timeout_time->accept( *visitor ); output << ") "; stmt->timeout_stmt->accept( *visitor ); } if ( stmt->else_stmt ) { output << "or "; if ( stmt->else_cond ) { output << "when("; stmt->else_cond->accept( *visitor ); output << ")"; } output << "else "; stmt->else_stmt->accept( *visitor ); } } void CodeGenerator::postvisit( ast::WithStmt const * stmt ) { assertf( !options.genC, "WithStmt should not reach code generation." ); output << "with ( "; genCommaList( stmt->exprs ); output << " ) "; stmt->stmt->accept( *visitor ); } void CodeGenerator::postvisit( ast::WhileDoStmt const * stmt ) { if ( stmt->isDoWhile ) { output << "do"; } else { output << "while ("; stmt->cond->accept( *visitor ); output << ")"; } output << " "; output << CodeGenerator::printLabels( stmt->body->labels ); stmt->body->accept( *visitor ); if ( stmt->isDoWhile ) { output << " while ("; stmt->cond->accept( *visitor ); output << ( ( nullptr == stmt->else_ ) ? ");" : ")" ); } if ( stmt->else_ ) { output << " else "; stmt->else_->accept( *visitor ); } } void CodeGenerator::postvisit( ast::ForStmt const * stmt ) { // Initializer is always hoised so don't generate it. // TODO: Do an assertion check? output << "for (;"; if ( nullptr != stmt->cond ) { stmt->cond->accept( *visitor ); } output << ";"; if ( nullptr != stmt->inc ) { // cast the top-level expression to void to reduce gcc warnings. ast::Expr * expr = new ast::CastExpr( stmt->inc ); expr->accept( *visitor ); } output << ") "; if ( nullptr != stmt->body ) { output << printLabels( stmt->body->labels ); stmt->body->accept( *visitor ); } if ( nullptr != stmt->else_ ) { assertf( !options.genC, "Loop else should not reach code generation." ); output << " else "; stmt->else_->accept( *visitor ); } } void CodeGenerator::postvisit( ast::NullStmt const * ) { output << "/* null statement */ ;"; } void CodeGenerator::postvisit( ast::DeclStmt const * stmt ) { stmt->decl->accept( *visitor ); if ( doSemicolon( stmt->decl ) ) output << ";"; } void CodeGenerator::postvisit( ast::ImplicitCtorDtorStmt const * stmt ) { assertf( !options.genC, "ImplicitCtorCtorStmt should not reach code generation." ); stmt->callStmt->accept( *visitor ); } void CodeGenerator::postvisit( ast::MutexStmt const * stmt ) { assertf( !options.genC, "MutexStmt should not reach code generation." ); // TODO: But this isn't what a mutex statement looks like. stmt->stmt->accept( *visitor ); } std::string genName( ast::DeclWithType const * decl ) { if ( const OperatorInfo * opInfo = operatorLookup( decl->name ) ) { return opInfo->outputName; } else { return decl->name; } } } // namespace CodeGen