// // 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. // // ResolvProtoDump.cc -- Translates CFA resolver instances into resolv-proto instances // // Author : Aaron Moss // Created On : Tue Sep 11 09:04:00 2018 // Last Modified By : Aaron Moss // Last Modified On : Tue Sep 11 09:04:00 2018 // Update Count : 1 // #include #include #include #include #include #include #include #include #include #include #include #include "Common/PassVisitor.h" #include "CodeGen/OperatorTable.h" #include "SynTree/Declaration.h" #include "SynTree/Expression.h" #include "SynTree/Statement.h" #include "SynTree/Type.h" namespace CodeTools { /// Visitor for dumping resolver prototype output class ProtoDump : public WithShortCircuiting, public WithVisitorRef { std::set decls; ///< Declarations in this scope std::vector exprs; ///< Expressions in this scope std::vector subs; ///< Sub-scopes const ProtoDump* parent; ///< Outer lexical scope public: /// Default constructor for root ProtoDump ProtoDump() : decls(), exprs(), subs(), parent(nullptr) {} /// Child constructor ProtoDump(const ProtoDump* p) : decls(), exprs(), subs(), parent(p) {} private: /// checks if this declaration is contained in the scope or one of its parents bool hasDecl( const std::string& s ) const { if ( decls.count( s ) ) return true; if ( parent ) return parent->hasDecl( s ); return false; } /// adds a new declaration to this scope, providing it does not already exist void addDecl( const std::string& s ) { if ( ! hasDecl( s ) ) decls.insert( s ); } /// adds a new expression to this scope void addExpr( const std::string& s ) { if ( ! s.empty() ) { exprs.emplace_back( s ); } } /// adds a new subscope to this scope, returning a reference void addSub( PassVisitor&& sub ) { subs.emplace_back( std::move(sub.pass) ); } /// Whether lists should be separated, terminated, or preceded by their separator enum septype { separated, terminated, preceded }; /// builds space-separated list of types template static void build( V& visitor, const std::list< Type* >& tys, std::stringstream& ss, septype mode = separated ) { if ( tys.empty() ) return; if ( mode == preceded ) { ss << ' '; } auto it = tys.begin(); (*it)->accept( visitor ); while ( ++it != tys.end() ) { ss << ' '; (*it)->accept( visitor ); } if ( mode == terminated ) { ss << ' '; } } /// builds list of types wrapped as tuple type template static void buildAsTuple( V& visitor, const std::list< Type* >& tys, std::stringstream& ss ) { switch ( tys.size() ) { case 0: ss << "#void"; break; case 1: tys.front()->accept( visitor ); break; default: ss << "#$" << tys.size() << '<'; build( visitor, tys, ss ); ss << '>'; break; } } /// gets types from DWT list static std::list< Type* > from_decls( const std::list< DeclarationWithType* >& decls ) { std::list< Type* > tys; for ( auto decl : decls ) { tys.emplace_back( decl->get_type() ); } return tys; } /// gets types from TypeExpr list static std::list< Type* > from_exprs( const std::list< Expression* >& exprs ) { std::list< Type* > tys; for ( auto expr : exprs ) { if ( TypeExpr* tyExpr = dynamic_cast(expr) ) { tys.emplace_back( tyExpr->type ); } } return tys; } /// builds prefixes for rp_name static std::string new_prefix( const std::string& old, const char* added ) { if ( old.empty() ) return std::string{"$"} + added; return old + added; } /// shortens operator names static void op_name( const std::string& name, std::stringstream& ss ) { if ( name.compare( 0, 10, "_operator_" ) == 0 ) { ss << name.substr(10); } else if ( name.compare( "_constructor" ) == 0 || name.compare( "_destructor" ) == 0 ) { ss << name.substr(1); } else if ( name.compare( 0, 11, "__operator_" ) == 0 ) { ss << name.substr(11); } else { ss << name; } } /// replaces operators with resolv-proto names static void rp_name( const std::string& name, std::stringstream& ss, std::string&& pre = "" ) { // safety check for anonymous names if ( name.empty() ) { ss << new_prefix(pre, "anon"); return; } // replace operator names CodeGen::OperatorInfo info; if ( CodeGen::operatorLookup( name, info ) ) { ss << new_prefix(pre, ""); op_name( info.outputName, ss ); return; } // replace retval names if ( name.compare( 0, 8, "_retval_" ) == 0 ) { ss << new_prefix(pre, "rtn_"); op_name( name.substr(8), ss ); return; } // default to just name ss << pre << name; } /// ensures type inst names are uppercase static void ti_name( const std::string& name, std::stringstream& ss ) { ss << (char)std::toupper( static_cast(name[0]) ) << (name.c_str() + 1); } /// Visitor for printing types struct TypePrinter : public WithShortCircuiting, WithVisitorRef, WithGuards { std::stringstream& ss; ///< Output to print to unsigned depth; ///< Depth of nesting from root type TypePrinter( std::stringstream& ss ) : ss(ss), depth(0) {} // basic type represented as integer type // TODO maybe hard-code conversion graph and make named type void previsit( BasicType* bt ) { ss << (int)bt->get_kind(); } // pointers represented as generic type // TODO except pointer to function void previsit( PointerType* ) { ss << "#$ptr<"; ++depth; } void postvisit( PointerType* ) { --depth; ss << '>'; } // arrays represented as generic type void previsit( ArrayType* at ) { ss << "#$arr<"; ++depth; at->base->accept( *visitor ); --depth; ss << '>'; visit_children = false; } // ignore top-level reference types, they're mostly transparent to resolution void previsit( ReferenceType* ) { if ( depth > 0 ) { ss << "#$ref<"; } ++depth; } void postvisit( ReferenceType* ) { --depth; if ( depth > 0 ) { ss << '>'; } } // encode function type as a 2-param generic type // TODO handle forall functions void previsit( FunctionType* ft ) { ss << "#$fn<"; ++depth; buildAsTuple( *visitor, from_decls( ft->returnVals ), ss ); ss << ' '; buildAsTuple( *visitor, from_decls( ft->parameters ), ss ); --depth; ss << '>'; visit_children = false; } private: // prints aggregate type name as NamedType with optional paramters void handleAggregate( ReferenceToType* at ) { ss << '#' << at->name; if ( ! at->parameters.empty() ) { ss << '<'; ++depth; build( *visitor, from_exprs( at->parameters ), ss ); --depth; ss << '>'; } visit_children = false; } public: // handle aggregate types using NamedType void previsit( StructInstType* st ) { handleAggregate( st ); } void previsit( UnionInstType* ut ) { handleAggregate( ut ); } // replace enums with int void previsit( EnumInstType* ) { ss << (int)BasicType::SignedInt; } // make sure first letter of TypeInstType is capitalized void previsit( TypeInstType* vt ) { ti_name( vt->name, ss ); } // flattens empty and singleton tuples void previsit( TupleType* tt ) { ++depth; buildAsTuple( *visitor, tt->types, ss ); --depth; visit_children = false; } // TODO support VarArgsType // replace 0 and 1 with int // TODO support 0 and 1 with their proper type names and conversions void previsit( ZeroType* ) { ss << (int)BasicType::SignedInt; } void previsit( OneType* ) { ss << (int)BasicType::SignedInt; } // only print void type if not at top level void previsit( VoidType* ) { if ( depth > 0 ) { ss << "#void"; } } }; /// builds description of function void build( const std::string& name, FunctionType* fnTy, std::stringstream& ss ) { PassVisitor printTy{ ss }; build( printTy, from_decls( fnTy->returnVals ), ss, terminated ); rp_name( name, ss ); build( printTy, from_decls( fnTy->parameters ), ss, preceded ); // TODO handle assertions } /// builds description of a variable (falls back to function if function type) void build( const std::string& name, Type* ty, std::stringstream& ss ) { // ignore top-level references Type *norefs = ty->stripReferences(); // fall back to function declaration if function type if ( PointerType* pTy = dynamic_cast< PointerType* >(norefs) ) { if ( FunctionType* fnTy = dynamic_cast< FunctionType* >(pTy->base) ) { build( name, fnTy, ss ); return; } } else if ( FunctionType* fnTy = dynamic_cast< FunctionType* >(norefs) ) { build( name, fnTy, ss ); return; } // print variable declaration as zero-arg function PassVisitor printTy{ ss }; norefs->accept( printTy ); ss << ' '; rp_name( name, ss ); } /// builds description of a field access void build( const std::string& name, AggregateDecl* agg, Type* ty, std::stringstream& ss ) { // ignore top-level references Type *norefs = ty->stripReferences(); // print access as new field name PassVisitor printTy{ ss }; norefs->accept( printTy ); ss << ' '; rp_name( name, ss, "$field_" ); ss << " #" << agg->name; // handle type parameters if ( ! agg->parameters.empty() ) { ss << '<'; auto it = agg->parameters.begin(); while (true) { ti_name( (*it)->name, ss ); if ( ++it == agg->parameters.end() ) break; ss << ' '; } ss << '>'; } } /// Visitor for printing expressions struct ExprPrinter : WithShortCircuiting, WithVisitorRef { // TODO change interface to generate multiple expression candidates std::stringstream& ss; ///< Output to print to ExprPrinter( std::stringstream& ss ) : ss(ss) {} /// Names handled as nullary function calls void previsit( NameExpr* expr ) { rp_name( expr->name, ss ); ss << "()"; } /// Calls handled as calls void previsit( UntypedExpr* expr ) { // TODO handle name extraction more generally NameExpr* name = dynamic_cast(expr->function); // fall back on just resolving call to function name // TODO incorporate function type into resolv-proto if ( ! name ) { expr->function->accept( *visitor ); visit_children = false; return; } rp_name( name->name, ss ); if ( expr->args.empty() ) { ss << "()"; } else { ss << "( "; auto it = expr->args.begin(); while (true) { (*it)->accept( *visitor ); if ( ++it == expr->args.end() ) break; ss << ' '; } ss << " )"; } visit_children = false; } /// Address-of handled as operator void previsit( AddressExpr* expr ) { // TODO global function to implement this ss << "$addr( "; expr->arg->accept( *visitor ); ss << " )"; visit_children = false; } /// Casts replaced with result type /// TODO put cast target functions in, and add second expression for target void previsit( CastExpr* cast ) { PassVisitor tyPrinter{ ss }; cast->result->accept( tyPrinter ); visit_children = false; } /// Member access handled as function from aggregate to member void previsit( UntypedMemberExpr* expr ) { // TODO handle name extraction more generally NameExpr* name = dynamic_cast(expr->member); // fall back on just resolving call to member name // TODO incorporate function type into resolv-proto if ( ! name ) { expr->member->accept( *visitor ); visit_children = false; return; } rp_name( name->name, ss, "$field_" ); ss << "( "; expr->aggregate->accept( *visitor ); ss << " )"; visit_children = false; } /// Constant expression replaced by its type void previsit( ConstantExpr* expr ) { PassVisitor tyPrinter{ ss }; expr->constant.get_type()->accept( tyPrinter ); visit_children = false; } /// sizeof( ... ), alignof( ... ), offsetof( ... ) replaced by unsigned long constant /// TODO extra expression to resolve argument void previsit( SizeofExpr* ) { ss << (int)BasicType::LongUnsignedInt; visit_children = false; } void previsit( AlignofExpr* ) { ss << (int)BasicType::LongUnsignedInt; visit_children = false; } void previsit( UntypedOffsetofExpr* ) { ss << (int)BasicType::LongUnsignedInt; visit_children = false; } /// Logical expressions represented as operators void previsit( LogicalExpr* expr ) { // TODO global functions for these ss << '$' << ( expr->get_isAnd() ? "and" : "or" ) << "( "; expr->arg1->accept( *visitor ); ss << ' '; expr->arg2->accept( *visitor ); ss << " )"; visit_children = false; } /// Conditional expression represented as operator void previsit( ConditionalExpr* expr ) { // TODO global function for this ss << "$if( "; expr->arg1->accept( *visitor ); ss << ' '; expr->arg2->accept( *visitor ); ss << ' '; expr->arg3->accept( *visitor ); ss << " )"; visit_children = false; } /// Comma expression represented as operator void previsit( CommaExpr* expr ) { // TODO global function for this ss << "$seq( "; expr->arg1->accept( *visitor ); ss << ' '; expr->arg2->accept( *visitor ); ss << " )"; visit_children = false; } // TODO handle ignored ImplicitCopyCtorExpr and below }; /// Adds all named declarations in a list to the local scope void addAll( const std::list& decls ) { for ( auto decl : decls ) { // skip anonymous decls if ( decl->name.empty() ) continue; // handle objects if ( ObjectDecl* obj = dynamic_cast< ObjectDecl* >( decl ) ) { previsit( obj ); } } } /// encode field access as function void addAggregateFields( AggregateDecl* agg ) { // make field names functions for ( Declaration* member : agg->members ) { if ( ObjectDecl* obj = dynamic_cast< ObjectDecl* >(member) ) { std::stringstream ss; build( obj->name, agg, obj->type, ss ); addDecl( ss.str() ); } } visit_children = false; } public: void previsit( ObjectDecl *obj ) { // add variable as declaration std::stringstream ss; build( obj->name, obj->type, ss ); addDecl( ss.str() ); } void previsit( FunctionDecl *decl ) { // add function as declaration std::stringstream ss; build( decl->name, decl->type, ss ); addDecl( ss.str() ); // add body if available if ( decl->statements ) { PassVisitor body{ this }; // add named parameters and returns to local scope body.pass.addAll( decl->type->returnVals ); body.pass.addAll( decl->type->parameters ); // TODO add assertions to local scope // TODO add set of "closed" types to body so that it can print them as NamedType // add contents of function to new scope decl->statements->accept( body ); // store sub-scope addSub( std::move(body) ); } visit_children = false; } void previsit( StructDecl* sd ) { addAggregateFields(sd); } void previsit( UnionDecl* ud ) { addAggregateFields(ud); } void previsit( EnumDecl* ed ) { std::unique_ptr eType = std::make_unique( Type::Qualifiers{}, BasicType::SignedInt ); // add field names directly to enclosing scope for ( Declaration* member : ed->members ) { if ( ObjectDecl* obj = dynamic_cast< ObjectDecl* >(member) ) { previsit(obj); } } visit_children = false; } void previsit( Expression* expr ) { std::stringstream ss; PassVisitor exPrinter{ss}; expr->accept( exPrinter ); addExpr( ss.str() ); visit_children = false; } public: /// Prints this ProtoDump instance void print(unsigned indent = 0) const { std::string tab( indent, '\t' ); // print decls for ( const std::string& d : decls ) { std::cout << tab << d << std::endl; } // print divider std::cout << tab << "%%" << std::endl; // print top-level expressions for ( const std::string& e : exprs ) { std::cout << tab << e << std::endl; } // print child scopes ++indent; for ( const PassVisitor& s : subs ) { std::cout << tab << '{' << std::endl; s.pass.print( indent ); std::cout << tab << '}' << std::endl; } } }; void dumpAsResolvProto( std::list< Declaration * > &translationUnit ) { PassVisitor dump; acceptAll( translationUnit, dump ); dump.pass.print(); } } // namespace CodeTools // Local Variables: // // tab-width: 4 // // mode: c++ // // compile-command: "make install" // // End: //