// // 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. // // Autogen.cc -- // // Author : Rob Schluntz // Created On : Thu Mar 03 15:45:56 2016 // Last Modified By : Peter A. Buhr // Last Modified On : Thu Feb 2 18:04:40 2017 // Update Count : 11 // #include #include #include "SynTree/Visitor.h" #include "SynTree/Type.h" #include "SynTree/Statement.h" #include "SynTree/TypeSubstitution.h" #include "Common/utility.h" #include "AddVisit.h" #include "MakeLibCfa.h" #include "Autogen.h" #include "GenPoly/ScopedSet.h" #include "Common/ScopedMap.h" #include "SymTab/Mangler.h" #include "GenPoly/DeclMutator.h" namespace SymTab { Type * SizeType = 0; typedef ScopedMap< std::string, bool > TypeMap; /// Data used to generate functions generically. Specifically, the name of the generated function, a function which generates the routine protoype, and a map which contains data to determine whether a function should be generated. struct FuncData { typedef FunctionType * (*TypeGen)( Type * ); FuncData( const std::string & fname, const TypeGen & genType, TypeMap & map ) : fname( fname ), genType( genType ), map( map ) {} std::string fname; TypeGen genType; TypeMap & map; }; class AutogenerateRoutines final : public Visitor { template< typename Visitor > friend void acceptAndAdd( std::list< Declaration * > &translationUnit, Visitor &visitor ); template< typename Visitor > friend void addVisitStatementList( std::list< Statement* > &stmts, Visitor &visitor ); public: std::list< Declaration * > &get_declsToAdd() { return declsToAdd; } typedef Visitor Parent; using Parent::visit; AutogenerateRoutines(); virtual void visit( EnumDecl *enumDecl ); virtual void visit( StructDecl *structDecl ); virtual void visit( UnionDecl *structDecl ); virtual void visit( TypeDecl *typeDecl ); virtual void visit( TraitDecl *ctxDecl ); virtual void visit( FunctionDecl *functionDecl ); virtual void visit( FunctionType *ftype ); virtual void visit( PointerType *ftype ); virtual void visit( CompoundStmt *compoundStmt ); virtual void visit( SwitchStmt *switchStmt ); private: template< typename StmtClass > void visitStatement( StmtClass *stmt ); std::list< Declaration * > declsToAdd, declsToAddAfter; std::set< std::string > structsDone; unsigned int functionNesting = 0; // current level of nested functions /// Note: the following maps could be ScopedSets, but it should be easier to work /// deleted functions in if they are maps, since the value false can be inserted /// at the current scope without affecting outer scopes or requiring copies. TypeMap copyable, assignable, constructable, destructable; std::vector< FuncData > data; }; /// generates routines for tuple types. /// Doesn't really need to be a mutator, but it's easier to reuse DeclMutator than it is to use AddVisit /// or anything we currently have that supports adding new declarations for visitors class AutogenTupleRoutines : public GenPoly::DeclMutator { public: typedef GenPoly::DeclMutator Parent; using Parent::mutate; virtual DeclarationWithType * mutate( FunctionDecl *functionDecl ); virtual Type * mutate( TupleType *tupleType ); virtual CompoundStmt * mutate( CompoundStmt *compoundStmt ); private: unsigned int functionNesting = 0; // current level of nested functions GenPoly::ScopedSet< std::string > seenTuples; }; void autogenerateRoutines( std::list< Declaration * > &translationUnit ) { AutogenerateRoutines generator; acceptAndAdd( translationUnit, generator ); // needs to be done separately because AutogenerateRoutines skips types that appear as function arguments, etc. // AutogenTupleRoutines tupleGenerator; // tupleGenerator.mutateDeclarationList( translationUnit ); } bool isUnnamedBitfield( ObjectDecl * obj ) { return obj != NULL && obj->get_name() == "" && obj->get_bitfieldWidth() != NULL; } /// inserts a forward declaration for functionDecl into declsToAdd void addForwardDecl( FunctionDecl * functionDecl, std::list< Declaration * > & declsToAdd ) { FunctionDecl * decl = functionDecl->clone(); delete decl->get_statements(); decl->set_statements( NULL ); declsToAdd.push_back( decl ); decl->fixUniqueId(); } /// given type T, generate type of default ctor/dtor, i.e. function type void (*) (T *) FunctionType * genDefaultType( Type * paramType ) { FunctionType *ftype = new FunctionType( Type::Qualifiers(), false ); ObjectDecl *dstParam = new ObjectDecl( "_dst", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, nullptr, new PointerType( Type::Qualifiers(), paramType->clone() ), nullptr ); ftype->get_parameters().push_back( dstParam ); return ftype; } /// given type T, generate type of copy ctor, i.e. function type void (*) (T *, T) FunctionType * genCopyType( Type * paramType ) { FunctionType *ftype = genDefaultType( paramType ); ObjectDecl *srcParam = new ObjectDecl( "_src", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, nullptr, paramType->clone(), nullptr ); ftype->get_parameters().push_back( srcParam ); return ftype; } /// given type T, generate type of assignment, i.e. function type T (*) (T *, T) FunctionType * genAssignType( Type * paramType ) { FunctionType *ftype = genCopyType( paramType ); ObjectDecl *returnVal = new ObjectDecl( "_ret", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, nullptr, paramType->clone(), nullptr ); ftype->get_returnVals().push_back( returnVal ); return ftype; } /// true if the aggregate's layout is dynamic template< typename AggrDecl > bool hasDynamicLayout( AggrDecl * aggregateDecl ) { for ( TypeDecl * param : aggregateDecl->get_parameters() ) { if ( param->get_kind() == TypeDecl::Any ) return true; } return false; } /// generate a function decl from a name and type. Nesting depth determines whether /// the declaration is static or not; optional paramter determines if declaration is intrinsic FunctionDecl * genFunc( const std::string & fname, FunctionType * ftype, unsigned int functionNesting, bool isIntrinsic = false ) { // Routines at global scope marked "static" to prevent multiple definitions in separate translation units // because each unit generates copies of the default routines for each aggregate. DeclarationNode::StorageClass sc = functionNesting > 0 ? DeclarationNode::NoStorageClass : DeclarationNode::Static; LinkageSpec::Spec spec = isIntrinsic ? LinkageSpec::Intrinsic : LinkageSpec::AutoGen; FunctionDecl * decl = new FunctionDecl( fname, sc, spec, ftype, new CompoundStmt( noLabels ), true, false ); decl->fixUniqueId(); return decl; } /// inserts base type of first argument into map if pred(funcDecl) is true void insert( FunctionDecl *funcDecl, TypeMap & map, FunctionDecl * (*pred)(Declaration *) ) { // insert type into constructable, etc. map if appropriate if ( pred( funcDecl ) ) { FunctionType * ftype = funcDecl->get_functionType(); assert( ! ftype->get_parameters().empty() ); Type * t = safe_dynamic_cast< PointerType * >( ftype->get_parameters().front()->get_type() )->get_base(); map.insert( Mangler::mangleType( t ), true ); } } /// using map and t, determines if is constructable, etc. bool lookup( const TypeMap & map, Type * t ) { if ( dynamic_cast< PointerType * >( t ) ) { // will need more complicated checking if we want this to work with pointer types, since currently return true; } else if ( ArrayType * at = dynamic_cast< ArrayType * >( t ) ) { // an array's constructor, etc. is generated on the fly based on the base type's constructor, etc. return lookup( map, at->get_base() ); } TypeMap::const_iterator it = map.find( Mangler::mangleType( t ) ); if ( it != map.end() ) return it->second; // something that does not appear in the map is by default not constructable, etc. return false; } /// using map and aggr, examines each member to determine if constructor, etc. should be generated template bool shouldGenerate( const TypeMap & map, AggrDecl * aggr ) { for ( Declaration * dcl : aggr->get_members() ) { if ( DeclarationWithType * dwt = dynamic_cast< DeclarationWithType * >( dcl ) ) { if ( ! lookup( map, dwt->get_type() ) ) return false; } } return true; } /// data structure for abstracting the generation of special functions template< typename OutputIterator > struct FuncGenerator { StructDecl *aggregateDecl; StructInstType *refType; unsigned int functionNesting; const std::list< TypeDecl* > & typeParams; OutputIterator out; FuncGenerator( StructDecl *aggregateDecl, StructInstType *refType, unsigned int functionNesting, const std::list< TypeDecl* > & typeParams, OutputIterator out ) : aggregateDecl( aggregateDecl ), refType( refType ), functionNesting( functionNesting ), typeParams( typeParams ), out( out ) {} /// generates a function (?{}, ?=?, ^?{}) based on the data argument and members. If function is generated, inserts the type into the map. void gen( const FuncData & data ) { if ( ! shouldGenerate( data.map, aggregateDecl ) ) return; FunctionType * ftype = data.genType( refType ); cloneAll( typeParams, ftype->get_forall() ); *out++ = genFunc( data.fname, ftype, functionNesting ); data.map.insert( Mangler::mangleType( refType ), true ); } }; template< typename OutputIterator > FuncGenerator makeFuncGenerator( StructDecl *aggregateDecl, StructInstType *refType, unsigned int functionNesting, const std::list< TypeDecl* > & typeParams, OutputIterator out ) { return FuncGenerator( aggregateDecl, refType, functionNesting, typeParams, out ); } /// generates a single enumeration assignment expression ApplicationExpr * genEnumAssign( FunctionType * ftype, FunctionDecl * assignDecl ) { // enum copy construct and assignment is just C-style assignment. // this looks like a bad recursive call, but code gen will turn it into // a C-style assignment. // This happens before function pointer type conversion, so need to do it manually here // NOTE: ftype is not necessarily the functionType belonging to assignDecl - ftype is the // type of the function that this expression is being generated for (so that the correct // parameters) are using in the variable exprs assert( ftype->get_parameters().size() == 2 ); ObjectDecl * dstParam = safe_dynamic_cast< ObjectDecl * >( ftype->get_parameters().front() ); ObjectDecl * srcParam = safe_dynamic_cast< ObjectDecl * >( ftype->get_parameters().back() ); VariableExpr * assignVarExpr = new VariableExpr( assignDecl ); Type * assignVarExprType = assignVarExpr->get_result(); assignVarExprType = new PointerType( Type::Qualifiers(), assignVarExprType ); assignVarExpr->set_result( assignVarExprType ); ApplicationExpr * assignExpr = new ApplicationExpr( assignVarExpr ); assignExpr->get_args().push_back( new VariableExpr( dstParam ) ); assignExpr->get_args().push_back( new VariableExpr( srcParam ) ); return assignExpr; } // E ?=?(E volatile*, int), // ?=?(E _Atomic volatile*, int); void makeEnumFunctions( EnumDecl *enumDecl, EnumInstType *refType, unsigned int functionNesting, std::list< Declaration * > &declsToAdd ) { // T ?=?(E *, E); FunctionType *assignType = genAssignType( refType ); // void ?{}(E *); void ^?{}(E *); FunctionType * ctorType = genDefaultType( refType->clone() ); FunctionType * dtorType = genDefaultType( refType->clone() ); // void ?{}(E *, E); FunctionType *copyCtorType = genCopyType( refType->clone() ); // xxx - should we also generate void ?{}(E *, int) and E ?{}(E *, E)? // right now these cases work, but that might change. // xxx - Temporary: make these functions intrinsic so they codegen as C assignment. // Really they're something of a cross between instrinsic and autogen, so should // probably make a new linkage type FunctionDecl *assignDecl = genFunc( "?=?", assignType, functionNesting, true ); FunctionDecl *ctorDecl = genFunc( "?{}", ctorType, functionNesting, true ); FunctionDecl *copyCtorDecl = genFunc( "?{}", copyCtorType, functionNesting, true ); FunctionDecl *dtorDecl = genFunc( "^?{}", dtorType, functionNesting, true ); // body is either return stmt or expr stmt assignDecl->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, genEnumAssign( assignType, assignDecl ) ) ); copyCtorDecl->get_statements()->get_kids().push_back( new ExprStmt( noLabels, genEnumAssign( copyCtorType, assignDecl ) ) ); declsToAdd.push_back( ctorDecl ); declsToAdd.push_back( copyCtorDecl ); declsToAdd.push_back( dtorDecl ); declsToAdd.push_back( assignDecl ); // assignment should come last since it uses copy constructor in return } /// generates a single struct member operation (constructor call, destructor call, assignment call) void makeStructMemberOp( ObjectDecl * dstParam, Expression * src, DeclarationWithType * field, FunctionDecl * func, bool isDynamicLayout, bool forward = true ) { ObjectDecl * returnVal = NULL; if ( ! func->get_functionType()->get_returnVals().empty() ) { returnVal = dynamic_cast( func->get_functionType()->get_returnVals().front() ); } InitTweak::InitExpander srcParam( src ); // assign to destination (and return value if generic) UntypedExpr *derefExpr = UntypedExpr::createDeref( new VariableExpr( dstParam ) ); Expression *dstselect = new MemberExpr( field, derefExpr ); genImplicitCall( srcParam, dstselect, func->get_name(), back_inserter( func->get_statements()->get_kids() ), field, forward ); if ( isDynamicLayout && returnVal ) { // xxx - there used to be a dereference on returnVal, but this seems to have been wrong? Expression *retselect = new MemberExpr( field, new VariableExpr( returnVal ) ); genImplicitCall( srcParam, retselect, func->get_name(), back_inserter( func->get_statements()->get_kids() ), field, forward ); } // if } /// generates the body of a struct function by iterating the struct members (via parameters) - generates default ctor, copy ctor, assignment, and dtor bodies, but NOT field ctor bodies template void makeStructFunctionBody( Iterator member, Iterator end, FunctionDecl * func, bool isDynamicLayout, bool forward = true ) { for ( ; member != end; ++member ) { if ( DeclarationWithType *field = dynamic_cast< DeclarationWithType * >( *member ) ) { // otherwise some form of type declaration, e.g. Aggregate // query the type qualifiers of this field and skip assigning it if it is marked const. // If it is an array type, we need to strip off the array layers to find its qualifiers. Type * type = field->get_type(); while ( ArrayType * at = dynamic_cast< ArrayType * >( type ) ) { type = at->get_base(); } if ( type->get_qualifiers().isConst && func->get_name() == "?=?" ) { // don't assign const members, but do construct/destruct continue; } if ( field->get_name() == "" ) { // don't assign to anonymous members // xxx - this is a temporary fix. Anonymous members tie into // our inheritance model. I think the correct way to handle this is to // cast the structure to the type of the member and let the resolver // figure out whether it's valid and have a pass afterwards that fixes // the assignment to use pointer arithmetic with the offset of the // member, much like how generic type members are handled. continue; } assert( ! func->get_functionType()->get_parameters().empty() ); ObjectDecl * dstParam = dynamic_cast( func->get_functionType()->get_parameters().front() ); ObjectDecl * srcParam = NULL; if ( func->get_functionType()->get_parameters().size() == 2 ) { srcParam = dynamic_cast( func->get_functionType()->get_parameters().back() ); } // srcParam may be NULL, in which case we have default ctor/dtor assert( dstParam ); Expression *srcselect = srcParam ? new MemberExpr( field, new VariableExpr( srcParam ) ) : NULL; makeStructMemberOp( dstParam, srcselect, field, func, isDynamicLayout, forward ); } // if } // for } // makeStructFunctionBody /// generate the body of a constructor which takes parameters that match fields, e.g. /// void ?{}(A *, int) and void?{}(A *, int, int) for a struct A which has two int fields. template void makeStructFieldCtorBody( Iterator member, Iterator end, FunctionDecl * func, bool isDynamicLayout ) { FunctionType * ftype = func->get_functionType(); std::list & params = ftype->get_parameters(); assert( params.size() >= 2 ); // should not call this function for default ctor, etc. // skip 'this' parameter ObjectDecl * dstParam = dynamic_cast( params.front() ); assert( dstParam ); std::list::iterator parameter = params.begin()+1; for ( ; member != end; ++member ) { if ( DeclarationWithType * field = dynamic_cast( *member ) ) { if ( isUnnamedBitfield( dynamic_cast< ObjectDecl * > ( field ) ) ) { // don't make a function whose parameter is an unnamed bitfield continue; } else if ( field->get_name() == "" ) { // don't assign to anonymous members // xxx - this is a temporary fix. Anonymous members tie into // our inheritance model. I think the correct way to handle this is to // cast the structure to the type of the member and let the resolver // figure out whether it's valid and have a pass afterwards that fixes // the assignment to use pointer arithmetic with the offset of the // member, much like how generic type members are handled. continue; } else if ( parameter != params.end() ) { // matching parameter, initialize field with copy ctor Expression *srcselect = new VariableExpr(*parameter); makeStructMemberOp( dstParam, srcselect, field, func, isDynamicLayout ); ++parameter; } else { // no matching parameter, initialize field with default ctor makeStructMemberOp( dstParam, NULL, field, func, isDynamicLayout ); } } } } /// generates struct constructors, destructor, and assignment functions void makeStructFunctions( StructDecl *aggregateDecl, StructInstType *refType, unsigned int functionNesting, std::list< Declaration * > & declsToAdd, const std::vector< FuncData > & data ) { // Make function polymorphic in same parameters as generic struct, if applicable const std::list< TypeDecl* > & typeParams = aggregateDecl->get_parameters(); // List of type variables to be placed on the generated functions bool isDynamicLayout = hasDynamicLayout( aggregateDecl ); // NOTE this flag is an incredibly ugly kludge; we should fix the assignment signature instead (ditto for union) // generate each of the functions based on the supplied FuncData objects std::list< FunctionDecl * > newFuncs; auto generator = makeFuncGenerator( aggregateDecl, refType, functionNesting, typeParams, back_inserter( newFuncs ) ); for ( const FuncData & d : data ) { generator.gen( d ); } // field ctors are only generated if default constructor and copy constructor are both generated unsigned numCtors = std::count_if( newFuncs.begin(), newFuncs.end(), [](FunctionDecl * dcl) { return InitTweak::isConstructor( dcl->get_name() ); } ); if ( functionNesting == 0 ) { // forward declare if top-level struct, so that // type is complete as soon as its body ends // Note: this is necessary if we want structs which contain // generic (otype) structs as members. for ( FunctionDecl * dcl : newFuncs ) { addForwardDecl( dcl, declsToAdd ); } } for ( FunctionDecl * dcl : newFuncs ) { // generate appropriate calls to member ctor, assignment // destructor needs to do everything in reverse, so pass "forward" based on whether the function is a destructor if ( ! InitTweak::isDestructor( dcl->get_name() ) ) { makeStructFunctionBody( aggregateDecl->get_members().begin(), aggregateDecl->get_members().end(), dcl, isDynamicLayout ); } else { makeStructFunctionBody( aggregateDecl->get_members().rbegin(), aggregateDecl->get_members().rend(), dcl, isDynamicLayout, false ); } if ( InitTweak::isAssignment( dcl->get_name() ) ) { // assignment needs to return a value FunctionType * assignType = dcl->get_functionType(); assert( assignType->get_parameters().size() == 2 ); ObjectDecl * srcParam = safe_dynamic_cast< ObjectDecl * >( assignType->get_parameters().back() ); dcl->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, new VariableExpr( srcParam ) ) ); } declsToAdd.push_back( dcl ); } // create constructors which take each member type as a parameter. // for example, for struct A { int x, y; }; generate // void ?{}(A *, int) and void ?{}(A *, int, int) // Field constructors are only generated if default and copy constructor // are generated, since they need access to both if ( numCtors == 2 ) { FunctionType * memCtorType = genDefaultType( refType ); cloneAll( typeParams, memCtorType->get_forall() ); for ( std::list::iterator i = aggregateDecl->get_members().begin(); i != aggregateDecl->get_members().end(); ++i ) { DeclarationWithType * member = dynamic_cast( *i ); assert( member ); if ( isUnnamedBitfield( dynamic_cast< ObjectDecl * > ( member ) ) ) { // don't make a function whose parameter is an unnamed bitfield continue; } else if ( member->get_name() == "" ) { // don't assign to anonymous members // xxx - this is a temporary fix. Anonymous members tie into // our inheritance model. I think the correct way to handle this is to // cast the structure to the type of the member and let the resolver // figure out whether it's valid and have a pass afterwards that fixes // the assignment to use pointer arithmetic with the offset of the // member, much like how generic type members are handled. continue; } memCtorType->get_parameters().push_back( new ObjectDecl( member->get_name(), DeclarationNode::NoStorageClass, LinkageSpec::Cforall, 0, member->get_type()->clone(), 0 ) ); FunctionDecl * ctor = genFunc( "?{}", memCtorType->clone(), functionNesting ); makeStructFieldCtorBody( aggregateDecl->get_members().begin(), aggregateDecl->get_members().end(), ctor, isDynamicLayout ); declsToAdd.push_back( ctor ); } delete memCtorType; } } /// generate a single union assignment expression (using memcpy) template< typename OutputIterator > void makeUnionFieldsAssignment( ObjectDecl * srcParam, ObjectDecl * dstParam, OutputIterator out ) { UntypedExpr *copy = new UntypedExpr( new NameExpr( "__builtin_memcpy" ) ); copy->get_args().push_back( new VariableExpr( dstParam ) ); copy->get_args().push_back( new AddressExpr( new VariableExpr( srcParam ) ) ); copy->get_args().push_back( new SizeofExpr( srcParam->get_type()->clone() ) ); *out++ = new ExprStmt( noLabels, copy ); } /// generates the body of a union assignment/copy constructor/field constructor void makeUnionAssignBody( FunctionDecl * funcDecl, bool isDynamicLayout ) { FunctionType * ftype = funcDecl->get_functionType(); assert( ftype->get_parameters().size() == 2 ); ObjectDecl * dstParam = safe_dynamic_cast< ObjectDecl * >( ftype->get_parameters().front() ); ObjectDecl * srcParam = safe_dynamic_cast< ObjectDecl * >( ftype->get_parameters().back() ); ObjectDecl * returnVal = nullptr; if ( ! ftype->get_returnVals().empty() ) { returnVal = safe_dynamic_cast< ObjectDecl * >( ftype->get_returnVals().front() ); } makeUnionFieldsAssignment( srcParam, dstParam, back_inserter( funcDecl->get_statements()->get_kids() ) ); if ( returnVal ) { if ( isDynamicLayout ) makeUnionFieldsAssignment( srcParam, returnVal, back_inserter( funcDecl->get_statements()->get_kids() ) ); else funcDecl->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, new VariableExpr( srcParam ) ) ); } } /// generates union constructors, destructors, and assignment operator void makeUnionFunctions( UnionDecl *aggregateDecl, UnionInstType *refType, unsigned int functionNesting, std::list< Declaration * > & declsToAdd ) { // Make function polymorphic in same parameters as generic union, if applicable const std::list< TypeDecl* > & typeParams = aggregateDecl->get_parameters(); // List of type variables to be placed on the generated functions bool isDynamicLayout = hasDynamicLayout( aggregateDecl ); // NOTE this flag is an incredibly ugly kludge; we should fix the assignment signature instead (ditto for struct) // default ctor/dtor need only first parameter // void ?{}(T *); void ^?{}(T *); FunctionType *ctorType = genDefaultType( refType ); FunctionType *dtorType = genDefaultType( refType ); // copy ctor needs both parameters // void ?{}(T *, T); FunctionType *copyCtorType = genCopyType( refType ); // assignment needs both and return value // T ?=?(T *, T); FunctionType *assignType = genAssignType( refType ); cloneAll( typeParams, ctorType->get_forall() ); cloneAll( typeParams, dtorType->get_forall() ); cloneAll( typeParams, copyCtorType->get_forall() ); cloneAll( typeParams, assignType->get_forall() ); // Routines at global scope marked "static" to prevent multiple definitions is separate translation units // because each unit generates copies of the default routines for each aggregate. FunctionDecl *assignDecl = genFunc( "?=?", assignType, functionNesting ); FunctionDecl *ctorDecl = genFunc( "?{}", ctorType, functionNesting ); FunctionDecl *copyCtorDecl = genFunc( "?{}", copyCtorType, functionNesting ); FunctionDecl *dtorDecl = genFunc( "^?{}", dtorType, functionNesting ); makeUnionAssignBody( assignDecl, isDynamicLayout ); // body of assignment and copy ctor is the same makeUnionAssignBody( copyCtorDecl, isDynamicLayout ); // create a constructor which takes the first member type as a parameter. // for example, for Union A { int x; double y; }; generate // void ?{}(A *, int) // This is to mimic C's behaviour which initializes the first member of the union. std::list memCtors; for ( Declaration * member : aggregateDecl->get_members() ) { if ( DeclarationWithType * field = dynamic_cast< DeclarationWithType * >( member ) ) { ObjectDecl * srcParam = new ObjectDecl( "src", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, 0, field->get_type()->clone(), 0 ); FunctionType * memCtorType = ctorType->clone(); memCtorType->get_parameters().push_back( srcParam ); FunctionDecl * ctor = genFunc( "?{}", memCtorType, functionNesting ); makeUnionAssignBody( ctor, isDynamicLayout ); memCtors.push_back( ctor ); // only generate a ctor for the first field break; } } declsToAdd.push_back( ctorDecl ); declsToAdd.push_back( copyCtorDecl ); declsToAdd.push_back( dtorDecl ); declsToAdd.push_back( assignDecl ); // assignment should come last since it uses copy constructor in return declsToAdd.splice( declsToAdd.end(), memCtors ); } AutogenerateRoutines::AutogenerateRoutines() { // the order here determines the order that these functions are generated. // assignment should come last since it uses copy constructor in return. data.push_back( FuncData( "?{}", genDefaultType, constructable ) ); data.push_back( FuncData( "?{}", genCopyType, copyable ) ); data.push_back( FuncData( "^?{}", genDefaultType, destructable ) ); data.push_back( FuncData( "?=?", genAssignType, assignable ) ); } void AutogenerateRoutines::visit( EnumDecl *enumDecl ) { if ( ! enumDecl->get_members().empty() ) { EnumInstType *enumInst = new EnumInstType( Type::Qualifiers(), enumDecl->get_name() ); // enumInst->set_baseEnum( enumDecl ); makeEnumFunctions( enumDecl, enumInst, functionNesting, declsToAddAfter ); } } void AutogenerateRoutines::visit( StructDecl *structDecl ) { if ( structDecl->has_body() && structsDone.find( structDecl->get_name() ) == structsDone.end() ) { StructInstType structInst( Type::Qualifiers(), structDecl->get_name() ); for ( TypeDecl * typeDecl : structDecl->get_parameters() ) { // need to visit assertions so that they are added to the appropriate maps acceptAll( typeDecl->get_assertions(), *this ); structInst.get_parameters().push_back( new TypeExpr( new TypeInstType( Type::Qualifiers(), typeDecl->get_name(), typeDecl ) ) ); } structInst.set_baseStruct( structDecl ); makeStructFunctions( structDecl, &structInst, functionNesting, declsToAddAfter, data ); structsDone.insert( structDecl->get_name() ); } // if } void AutogenerateRoutines::visit( UnionDecl *unionDecl ) { if ( ! unionDecl->get_members().empty() ) { UnionInstType unionInst( Type::Qualifiers(), unionDecl->get_name() ); unionInst.set_baseUnion( unionDecl ); for ( TypeDecl * typeDecl : unionDecl->get_parameters() ) { unionInst.get_parameters().push_back( new TypeExpr( new TypeInstType( Type::Qualifiers(), typeDecl->get_name(), typeDecl ) ) ); } makeUnionFunctions( unionDecl, &unionInst, functionNesting, declsToAddAfter ); } // if } void AutogenerateRoutines::visit( TypeDecl *typeDecl ) { TypeInstType *typeInst = new TypeInstType( Type::Qualifiers(), typeDecl->get_name(), false ); typeInst->set_baseType( typeDecl ); ObjectDecl *src = new ObjectDecl( "_src", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, nullptr, typeInst->clone(), nullptr ); ObjectDecl *dst = new ObjectDecl( "_dst", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, nullptr, new PointerType( Type::Qualifiers(), typeInst->clone() ), nullptr ); std::list< Statement * > stmts; if ( typeDecl->get_base() ) { // xxx - generate ctor/dtors for typedecls, e.g. // otype T = int *; UntypedExpr *assign = new UntypedExpr( new NameExpr( "?=?" ) ); assign->get_args().push_back( new CastExpr( new VariableExpr( dst ), new PointerType( Type::Qualifiers(), typeDecl->get_base()->clone() ) ) ); assign->get_args().push_back( new CastExpr( new VariableExpr( src ), typeDecl->get_base()->clone() ) ); stmts.push_back( new ReturnStmt( std::list< Label >(), assign ) ); } // if FunctionType *type = new FunctionType( Type::Qualifiers(), false ); type->get_returnVals().push_back( new ObjectDecl( "", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, 0, typeInst, 0 ) ); type->get_parameters().push_back( dst ); type->get_parameters().push_back( src ); FunctionDecl *func = genFunc( "?=?", type, functionNesting ); func->get_statements()->get_kids() = stmts; declsToAddAfter.push_back( func ); } void addDecls( std::list< Declaration * > &declsToAdd, std::list< Statement * > &statements, std::list< Statement * >::iterator i ) { for ( std::list< Declaration * >::iterator decl = declsToAdd.begin(); decl != declsToAdd.end(); ++decl ) { statements.insert( i, new DeclStmt( noLabels, *decl ) ); } // for declsToAdd.clear(); } void AutogenerateRoutines::visit( FunctionType *) { // ensure that we don't add assignment ops for types defined as part of the function } void AutogenerateRoutines::visit( PointerType *) { // ensure that we don't add assignment ops for types defined as part of the pointer } void AutogenerateRoutines::visit( TraitDecl *) { // ensure that we don't add assignment ops for types defined as part of the trait } template< typename StmtClass > inline void AutogenerateRoutines::visitStatement( StmtClass *stmt ) { std::set< std::string > oldStructs = structsDone; addVisit( stmt, *this ); structsDone = oldStructs; } void AutogenerateRoutines::visit( FunctionDecl *functionDecl ) { // record the existence of this function as appropriate insert( functionDecl, constructable, InitTweak::isDefaultConstructor ); insert( functionDecl, assignable, InitTweak::isAssignment ); insert( functionDecl, copyable, InitTweak::isCopyConstructor ); insert( functionDecl, destructable, InitTweak::isDestructor ); maybeAccept( functionDecl->get_functionType(), *this ); acceptAll( functionDecl->get_oldDecls(), *this ); functionNesting += 1; maybeAccept( functionDecl->get_statements(), *this ); functionNesting -= 1; } void AutogenerateRoutines::visit( CompoundStmt *compoundStmt ) { constructable.beginScope(); assignable.beginScope(); copyable.beginScope(); destructable.beginScope(); visitStatement( compoundStmt ); constructable.endScope(); assignable.endScope(); copyable.endScope(); destructable.endScope(); } void AutogenerateRoutines::visit( SwitchStmt *switchStmt ) { visitStatement( switchStmt ); } void makeTupleFunctionBody( FunctionDecl * function ) { FunctionType * ftype = function->get_functionType(); assertf( ftype->get_parameters().size() == 1 || ftype->get_parameters().size() == 2, "too many parameters in generated tuple function" ); UntypedExpr * untyped = new UntypedExpr( new NameExpr( function->get_name() ) ); /// xxx - &* is used to make this easier for later passes to handle untyped->get_args().push_back( new AddressExpr( UntypedExpr::createDeref( new VariableExpr( ftype->get_parameters().front() ) ) ) ); if ( ftype->get_parameters().size() == 2 ) { untyped->get_args().push_back( new VariableExpr( ftype->get_parameters().back() ) ); } function->get_statements()->get_kids().push_back( new ExprStmt( noLabels, untyped ) ); function->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, UntypedExpr::createDeref( new VariableExpr( ftype->get_parameters().front() ) ) ) ); } Type * AutogenTupleRoutines::mutate( TupleType * tupleType ) { tupleType = safe_dynamic_cast< TupleType * >( Parent::mutate( tupleType ) ); std::string mangleName = SymTab::Mangler::mangleType( tupleType ); if ( seenTuples.find( mangleName ) != seenTuples.end() ) return tupleType; seenTuples.insert( mangleName ); // T ?=?(T *, T); FunctionType *assignType = genAssignType( tupleType ); // void ?{}(T *); void ^?{}(T *); FunctionType *ctorType = genDefaultType( tupleType ); FunctionType *dtorType = genDefaultType( tupleType ); // void ?{}(T *, T); FunctionType *copyCtorType = genCopyType( tupleType ); std::set< TypeDecl* > done; std::list< TypeDecl * > typeParams; for ( Type * t : *tupleType ) { if ( TypeInstType * ty = dynamic_cast< TypeInstType * >( t ) ) { if ( ! done.count( ty->get_baseType() ) ) { TypeDecl * newDecl = new TypeDecl( ty->get_baseType()->get_name(), DeclarationNode::NoStorageClass, nullptr, TypeDecl::Any ); TypeInstType * inst = new TypeInstType( Type::Qualifiers(), newDecl->get_name(), newDecl ); newDecl->get_assertions().push_back( new FunctionDecl( "?=?", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, genAssignType( inst ), nullptr, true, false ) ); newDecl->get_assertions().push_back( new FunctionDecl( "?{}", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, genDefaultType( inst ), nullptr, true, false ) ); newDecl->get_assertions().push_back( new FunctionDecl( "?{}", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, genCopyType( inst ), nullptr, true, false ) ); newDecl->get_assertions().push_back( new FunctionDecl( "^?{}", DeclarationNode::NoStorageClass, LinkageSpec::Cforall, genDefaultType( inst ), nullptr, true, false ) ); typeParams.push_back( newDecl ); done.insert( ty->get_baseType() ); } } } cloneAll( typeParams, ctorType->get_forall() ); cloneAll( typeParams, dtorType->get_forall() ); cloneAll( typeParams, copyCtorType->get_forall() ); cloneAll( typeParams, assignType->get_forall() ); FunctionDecl *assignDecl = genFunc( "?=?", assignType, functionNesting ); FunctionDecl *ctorDecl = genFunc( "?{}", ctorType, functionNesting ); FunctionDecl *copyCtorDecl = genFunc( "?{}", copyCtorType, functionNesting ); FunctionDecl *dtorDecl = genFunc( "^?{}", dtorType, functionNesting ); makeTupleFunctionBody( assignDecl ); makeTupleFunctionBody( ctorDecl ); makeTupleFunctionBody( copyCtorDecl ); makeTupleFunctionBody( dtorDecl ); addDeclaration( ctorDecl ); addDeclaration( copyCtorDecl ); addDeclaration( dtorDecl ); addDeclaration( assignDecl ); // assignment should come last since it uses copy constructor in return return tupleType; } DeclarationWithType * AutogenTupleRoutines::mutate( FunctionDecl *functionDecl ) { functionDecl->set_functionType( maybeMutate( functionDecl->get_functionType(), *this ) ); mutateAll( functionDecl->get_oldDecls(), *this ); functionNesting += 1; functionDecl->set_statements( maybeMutate( functionDecl->get_statements(), *this ) ); functionNesting -= 1; return functionDecl; } CompoundStmt * AutogenTupleRoutines::mutate( CompoundStmt *compoundStmt ) { seenTuples.beginScope(); compoundStmt = safe_dynamic_cast< CompoundStmt * >( Parent::mutate( compoundStmt ) ); seenTuples.endScope(); return compoundStmt; } } // SymTab // Local Variables: // // tab-width: 4 // // mode: c++ // // compile-command: "make install" // // End: //