/* * COLLAPSE.C - Collapse Declarations and Types * * (c)Copyright 1993-2014, Matthew Dillon, All Rights Reserved. See the * COPYRIGHT file at the base of the distribution. * * This module is essentially optional but if we do not collapse duplicates * the generated output will be massive to say the least. * * We can reach the entire tree by resolving the top-level import * statement's DeclBlock. */ #include "defs.h" #include #if 0 static void CollapseClasses(Stmt *st); #endif static void collapseStmt(Stmt *st); static void collapseDecl(Declaration *d, int dynamic); static void collapseExp(Exp *exp); static void collapseSemGroup(SemGroup *sg); static void collapseType(Type **typep, Type *adjtype); void CollapseProject(Stmt *st) { Type *type; int i; dassert_stmt(st, st->st_Op == ST_Import); /* * All of our base types should collapse to themselves. Easiest * to just call it so the proper flags get set, otherwise they * might not get generated. */ for (i = 0; BaseTypeAry[i]; ++i) { type = BaseTypeAry[i]; collapseType(&type, NULL); dassert(type == BaseTypeAry[i]); } /* * General project */ /*CollapseClasses(st);*/ collapseStmt(st); } #if 0 /* * CollapseClasses() */ static void CollapseClasses(Stmt *st) { /* * Collapse interlock. */ if (st == NULL) return; if (st->st_Flags & (STF_COLLAPSING | STF_COLLAPSED)) return; if (st->st_Op == ST_Class && st->st_ClassStmt.es_Super) { collapseType(&st->st_ClassStmt.es_Super, NULL); collapseDecl(st->st_ClassStmt.es_Decl, 0); } /* * If this is an ST_Import we must recurse through it. The only * statements under an Import should be Modules. Well, really just * one module. And under that module we only care about ST_Import * and ST_Class statements. * * If this is a shared import the statement list will be empty (later * it may be used for import refinement, I dunno). This is what we * want since we should only resolve a shared import once. */ if (st->st_Op == ST_Import) { Stmt *scan; st->st_Flags |= STF_COLLAPSING; RUNE_FOREACH(scan, &st->st_List, st_Node) { Stmt *scan2; dassert_stmt(scan, scan->st_Op == ST_Module); RUNE_FOREACH(scan2, &scan->st_List, st_Node) { if (scan2->st_Op == ST_Import || scan2->st_Op == ST_Class) { CollapseClasses(scan2); } } } #if 0 if (st->st_ImportStmt.es_DLL) { void (*func)(void) = dlsym(st->st_ImportStmt.es_DLL, "resolveClasses"); if (func) func(); } #endif st->st_Flags &= ~STF_COLLAPSING; st->st_Flags |= STF_COLLAPSED; } } #endif /* * collapseStmt() - Collapse all types, declarations, and semantic refs * * Collapses all types, declarations, and identifiers. Additionally * this function collapses intermediate types for expressions. */ static void collapseStmt(Stmt *st) { Stmt *scan; if (st == NULL) return; if (st->st_Flags & (STF_COLLAPSING | STF_COLLAPSED)) return; st->st_Flags |= STF_COLLAPSING; if (st->st_Flags & STF_SEMANTIC) { collapseSemGroup(st->st_MyGroup); #if 0 SemGroup *sg = st->st_MyGroup; Type *type; RUNE_FOREACH(type, &sg->sg_ClassList, ty_Node) { if (type->ty_Op == TY_UNRESOLVED) { if (resolveSuperClass(type) == NULL) { errorDottedId( type->ty_UnresType.et_DottedId, "Unable to resolve class"); dassert_stmt(st, 0); } } } #endif } /* * Collapse statements. */ switch(st->st_Op) { case ST_Import: /* * This will just flag the import declaration as resolved * so the code generator dives it for generation. */ if (st->st_ImportStmt.es_Decl) collapseDecl(st->st_ImportStmt.es_Decl, 0); /* fall through */ case ST_Module: /* * Recursively resolve contents */ RUNE_FOREACH(scan, &st->st_List, st_Node) { collapseStmt(scan); } break; case ST_Class: collapseType(&st->st_ClassStmt.es_Super, NULL); collapseDecl(st->st_ClassStmt.es_Decl, 0); break; case ST_Typedef: collapseDecl(st->st_TypedefStmt.es_Decl, 0); break; case ST_Decl: /* * Resolve declarations, skipping any whos context was * moved to a class (e.g. a declaration at the top level * of a file like Fd.setfd(...) also exists in the Fd class). */ { Declaration *d = st->st_DeclStmt.es_Decl; int i; for (i = 0; i < st->st_DeclStmt.es_DeclCount; ++i) { if (st->st_MyGroup != d->d_MyGroup) /*printf("SKIPA %s\n", d->d_Id)*/; else collapseDecl(d, 0); d = RUNE_NEXT(d, d_Node); } } break; case ST_Block: RUNE_FOREACH(scan, &st->st_List, st_Node) { collapseStmt(scan); } break; case ST_Nop: break; case ST_Loop: collapseStmt(st->st_LoopStmt.es_Init); collapseExp(st->st_LoopStmt.es_BCond); collapseExp(st->st_LoopStmt.es_ACond); collapseExp(st->st_LoopStmt.es_AExp); collapseStmt(st->st_LoopStmt.es_Body); break; case ST_BreakCont: break; case ST_Bad: break; case ST_IfElse: /* * NOTE: BoolType global implies an rvalue. */ collapseExp(st->st_IfStmt.es_Exp); collapseStmt(st->st_IfStmt.es_TrueStmt); collapseStmt(st->st_IfStmt.es_FalseStmt); break; case ST_Return: collapseType(&st->st_RetStmt.es_ProcRetType, NULL); collapseExp(st->st_RetStmt.es_Exp); break; case ST_Result: collapseType(&st->st_ResStmt.es_ProcRetType, NULL); collapseExp(st->st_ResStmt.es_Exp); break; case ST_Switch: /* * NOTE: Switch type must be an rvalue. * * NOTE: It is possible to switch on a type. See ST_Case * below for more detail. */ collapseExp(st->st_SwStmt.es_Exp); RUNE_FOREACH(scan, &st->st_List, st_Node) { collapseStmt(scan); } break; case ST_Case: /* * Handle a case/default. Note that when switching on a type, * each case expression must return a type. * * NOTE: Case type must be an rvalue. We use the switch type * to cast, so it will be. */ { dassert_stmt(st, st->st_Parent->st_Op == ST_Switch); /* * Elements of the case/default */ collapseExp(st->st_CaseStmt.es_Exp); RUNE_FOREACH(scan, &st->st_List, st_Node) { collapseStmt(scan); } } break; case ST_Exp: /* * NOTE: VoidType global implies an rvalue. * * NOTE: If resolveExp() doesn't cast to void for * us, we will do it here. */ collapseExp(st->st_ExpStmt.es_Exp); break; case ST_Proc: RUNE_FOREACH(scan, &st->st_List, st_Node) { collapseStmt(scan); } break; case ST_ThreadSched: break; default: dassert_stmt(st, 0); break; } st->st_Flags &= ~STF_COLLAPSING; st->st_Flags |= STF_COLLAPSED; } /* * collapseDecl() - collapse a declaration */ static void collapseDecl(Declaration *d, int dynamic) { SemGroup *sg = NULL; if (d == NULL) return; if (d->d_Flags & (DF_COLLAPSING | DF_COLLAPSED)) return; if ((d->d_Flags & DF_RESOLVED) == 0) return; d->d_Flags |= DF_COLLAPSING; if (d->d_Flags & DF_ADDRUSED) d->d_InfoIndex = d->d_MyGroup->sg_InfoCount++; switch(d->d_Op) { case DOP_CLASS: collapseType(&d->d_ClassDecl.ed_Super, NULL); sg = d->d_ClassDecl.ed_SemGroup; collapseSemGroup(sg); d->d_MyGroup->sg_InfoCount += sg->sg_InfoCount; break; case DOP_ALIAS: /* * Alias access is a barrier and always returns an rvalue. */ collapseType(&d->d_AliasDecl.ed_Type, NULL); collapseExp(d->d_AliasDecl.ed_AssExp); break; case DOP_TYPEDEF: collapseType(&d->d_TypedefDecl.ed_Type, NULL); break; case DOP_IMPORT: /* * This only occurs when resolving an import's semantic group. * Since we are scanning statements in that context we do not * have to recurse here, collapseStmt() will do it for us. */ break; case DOP_PROC: /* * XXX global procedure, later on, make the argument a * type instead of storage? */ collapseType(&d->d_ProcDecl.ed_Type, NULL); collapseStmt(d->d_ProcDecl.ed_ProcBody); /* XXX */ collapseStmt(d->d_ProcDecl.ed_OrigBody); /* XXX */ break; case DOP_ARGS_STORAGE: case DOP_STACK_STORAGE: case DOP_GLOBAL_STORAGE: case DOP_GROUP_STORAGE: collapseType(&d->d_StorDecl.ed_Type, NULL); collapseExp(d->d_StorDecl.ed_AssExp); break; default: dassert_decl(d, 0); } d->d_Flags &= ~DF_COLLAPSING; d->d_Flags |= DF_COLLAPSED | DF_LAYOUT; if (dynamic) { Declaration *scan; for (scan = d->d_SubBase; scan; scan = scan->d_SubNext) collapseDecl(scan, 1); } } /* * collapseExp() */ static void collapseExp(Exp *exp) { Exp *scan; if (exp == NULL) return; if (exp->ex_Flags & (EXF_COLLAPSING | EXF_COLLAPSED)) return; exp->ex_Flags |= EXF_COLLAPSING; collapseType(&exp->ex_Type, NULL); collapseDecl(exp->ex_Decl, 0); switch(exp->ex_Token) { case TOK_ASS: case TOK_ANDAND: case TOK_OROR: collapseExp(exp->ex_Lhs); collapseExp(exp->ex_Rhs); break; case TOK_DECL: break; case TOK_DOT: case TOK_STRIND: collapseExp(exp->ex_Lhs); /* * Resolver might have turned RHS into a SEMGRP_ID, only * collapse if it hasn't. XXX */ if (exp->ex_Rhs->ex_Token != TOK_SEMGRP_ID) collapseExp(exp->ex_Rhs); break; case TOK_STRUCT_ID: break; case TOK_OPER: collapseExp(exp->ex_Lhs); collapseExp(exp->ex_Rhs); break; case TOK_PTRIND: collapseExp(exp->ex_Lhs); break; case TOK_ADDR: collapseExp(exp->ex_Lhs); break; case TOK_OBRACKET: collapseExp(exp->ex_Lhs); collapseExp(exp->ex_Rhs); break; case TOK_OPAREN: dassert_exp(exp, 0); /* XXX */ break; case TOK_DSTRING: case TOK_SSTRING: case TOK_BSTRING: case TOK_INTEGER: case TOK_FLOAT: case TOK_VOIDEXP: case TOK_SELF: case TOK_DOLLAR: break; case TOK_ID: case TOK_CLASSID: break; case TOK_NOT: collapseExp(exp->ex_Lhs); break; case TOK_TYPE: break; case TOK_CAST: collapseExp(exp->ex_Lhs); /* RHS was converted to type */ break; case TOK_INLINE_CALL: collapseExp(exp->ex_Lhs); collapseExp(exp->ex_Rhs); collapseStmt(exp->ex_AuxStmt); if (exp->ex_Lhs->ex_Token == TOK_STRIND && exp->ex_Lhs->ex_Lhs->ex_Type && exp->ex_Lhs->ex_Lhs->ex_Type->ty_Op == TY_REFTO) { collapseDecl(exp->ex_Lhs->ex_Decl, 1); } break; case TOK_CALL: collapseExp(exp->ex_Lhs); collapseExp(exp->ex_Rhs); if (exp->ex_Lhs->ex_Token == TOK_STRIND && exp->ex_Lhs->ex_Lhs->ex_Type && exp->ex_Lhs->ex_Lhs->ex_Type->ty_Op == TY_REFTO) { collapseDecl(exp->ex_Lhs->ex_Decl, 1); } break; case TOK_COMPOUND: case TOK_BRACKETED: for (scan = exp->ex_Lhs; scan; scan = scan->ex_Next) collapseExp(scan); break; case TOK_TYPEOF: /* fall through */ case TOK_SIZEOF: case TOK_ARYSIZE: collapseExp(exp->ex_Lhs); break; case TOK_ERR_EXP_REMOVED: break; default: fprintf(stderr, "BADEXP %p %08x\n", exp, exp->ex_Token); /* dassert_exp(exp, 0); */ break; } exp->ex_Flags &= ~EXF_COLLAPSING; exp->ex_Flags |= EXF_COLLAPSED; } /* * Collapse a SemGroup. The SemGroup may be used even if not resolved * if individual functions were picked out of it without instantiating * it. */ static void collapseSemGroup(SemGroup *sg) { Declaration *d; if (sg == NULL) return; if (sg->sg_Flags & (SGF_COLLAPSING | SGF_COLLAPSED)) return; if ((sg->sg_Flags & (SGF_RESOLVED | SGF_ALIGNRESOLVED)) == 0) return; sg->sg_Flags |= SGF_COLLAPSING; RUNE_FOREACH(d, &sg->sg_DeclList, d_Node) { if (d->d_Flags & DF_RESOLVED) collapseDecl(d, 0); } sg->sg_Flags &= ~SGF_COLLAPSING; sg->sg_Flags |= SGF_COLLAPSED | SGF_LAYOUT; } /* * Match basic type fields. Note that the TF_RESOLVE* flags must also match. * (that is, we do not match a resolved against an unresolved type). */ static int matchbasicfields(Type *type, Type *scan) { if (type->ty_Op == scan->ty_Op && (type->ty_Flags & ~(TF_COLLAPSED | TF_LAYOUT)) == (scan->ty_Flags & ~(TF_COLLAPSED | TF_LAYOUT)) && type->ty_AssExp == scan->ty_AssExp && type->ty_Bytes == scan->ty_Bytes && type->ty_AlignMask == scan->ty_AlignMask && type->ty_SQFlags == scan->ty_SQFlags && type->ty_Visibility == scan->ty_Visibility && type->ty_TmpBytes == scan->ty_TmpBytes && type->ty_TmpAlignMask == scan->ty_TmpAlignMask) { return 1; } else { return 0; } } /* * Resolve a type and attempt to remove duplicates that were present as * semantic glue. */ static void collapseType(Type **typep, Type *adjtype) { SemGroup *sg; Type *type; Type *scan; Type *otype; type = *typep; if (type == NULL) return; if ((type->ty_Flags & TF_RESOLVED) == 0) return; if (type->ty_Flags & TF_COLLAPSED) { if (type->ty_Flags & TF_LAYOUT) return; /* re-execute (XXX cache in type->ty_CollapsedType) */ } type->ty_Flags |= TF_COLLAPSED; /* * Otherwise try to collapse */ otype = type; #if 0 printf("; CHECKTYPE l=%p %s\n", type->ty_SQList, TypeToStr(type, NULL)); #endif switch(type->ty_Op) { case TY_CLASS: dassert(type->ty_SQList == &type->ty_ClassType.et_SemGroup->sg_ClassList); collapseType(&type->ty_ClassType.et_Super, NULL); collapseSemGroup(type->ty_ClassType.et_SemGroup); if (type->ty_ClassType.et_Super) { dassert((type->ty_ClassType.et_Super->ty_Flags & TF_LAYOUT) != 0); } if (type->ty_Flags & TF_ISINTERNAL) { scan = type; break; } sg = type->ty_ClassType.et_SemGroup; dassert(type->ty_SQList == &sg->sg_ClassList); RUNE_FOREACH(scan, &sg->sg_ClassList, ty_Node) { #if 0 printf("; TEST %s\t", TypeToStr(scan, NULL)); #endif if (matchbasicfields(type, scan) && type->ty_ClassType.et_SemGroup == scan->ty_ClassType.et_SemGroup && type->ty_ClassType.et_Super == scan->ty_ClassType.et_Super) { type = scan; break; } else { #if 0 if (type->ty_Op != scan->ty_Op) printf(" op"); if ((type->ty_Flags & ~TF_COLLAPSED) != (scan->ty_Flags & ~TF_COLLAPSED)) printf(" flg"); if (type->ty_AssExp != scan->ty_AssExp) printf(" ass"); if (type->ty_Bytes != scan->ty_Bytes) printf(" bytes"); if (type->ty_AlignMask != scan->ty_AlignMask) printf(" align"); if (type->ty_SQFlags != scan->ty_SQFlags) printf(" sqflg"); if (type->ty_Visibility != scan->ty_Visibility) printf(" vis[%d,%d]", type->ty_Visibility, scan->ty_Visibility); if (type->ty_TmpBytes != scan->ty_TmpBytes) printf(" tmpbytes"); if (type->ty_TmpAlignMask != scan->ty_TmpAlignMask) printf(" tmpalign"); if (type->ty_ClassType.et_SemGroup != scan->ty_ClassType.et_SemGroup) printf(" sg"); if (type->ty_ClassType.et_Super != scan->ty_ClassType.et_Super) printf(" super"); #endif } #if 0 printf("\n"); #endif } break; case TY_CPTRTO: case TY_PTRTO: case TY_REFTO: dassert(type->ty_SQList == &type->ty_PtrType.et_Type->ty_QList); collapseType(&type->ty_PtrType.et_Type, type); if (type->ty_Flags & TF_ISINTERNAL) { scan = type; break; } RUNE_FOREACH(scan, &type->ty_PtrType.et_Type->ty_QList, ty_Node) { #if 0 printf("; TEST %s\n", TypeToStr(scan, NULL)); #endif if (matchbasicfields(type, scan) && type->ty_PtrType.et_Type == scan->ty_PtrType.et_Type) { type = scan; break; } } break; case TY_ARYOF: #if 0 dassert(type->ty_SQList == &type->ty_AryType.et_Type->ty_QList); #endif collapseType(&type->ty_AryType.et_Type, type); if (type->ty_Flags & TF_ISINTERNAL) { scan = type; break; } RUNE_FOREACH(scan, &type->ty_AryType.et_Type->ty_QList, ty_Node) { #if 0 printf("; TEST %s\n", TypeToStr(scan, NULL)); #endif if (matchbasicfields(type, scan) && type->ty_AryType.et_SemGroup == scan->ty_AryType.et_SemGroup && type->ty_AryType.et_Type == scan->ty_AryType.et_Type && type->ty_AryType.et_Count == scan->ty_AryType.et_Count) { type = scan; break; } } break; case TY_COMPOUND: collapseSemGroup(type->ty_CompType.et_SemGroup); dassert(type->ty_SQList == &CompoundTypeList); if (type->ty_Flags & TF_ISINTERNAL) { scan = type; break; } RUNE_FOREACH(scan, &CompoundTypeList, ty_Node) { #if 0 printf("; TEST %s\n", TypeToStr(scan, NULL)); #endif if (matchbasicfields(type, scan) && type->ty_CompType.et_SemGroup == scan->ty_CompType.et_SemGroup) { type = scan; break; } } break; case TY_ARGS: collapseSemGroup(type->ty_CompType.et_SemGroup); dassert(type->ty_SQList == &ArgsTypeList); if (type->ty_Flags & TF_ISINTERNAL) { scan = type; break; } RUNE_FOREACH(scan, &ArgsTypeList, ty_Node) { #if 0 printf("; TEST %s\n", TypeToStr(scan, NULL)); #endif if (matchbasicfields(type, scan) && type->ty_CompType.et_SemGroup == scan->ty_CompType.et_SemGroup) { type = scan; break; } } break; case TY_VAR: collapseSemGroup(type->ty_VarType.et_SemGroup); scan = type; /* XXX */ break; case TY_PROC: /* * NOTE: Storage not tracked. */ #if 0 dassert(type->ty_SQList == &type->ty_ProcType.et_RetType->ty_QList); #endif collapseType(&type->ty_ProcType.et_ArgsType, NULL); collapseType(&type->ty_ProcType.et_RetType, type); if (type->ty_Flags & TF_ISINTERNAL) { scan = type; break; } RUNE_FOREACH(scan, &type->ty_ProcType.et_RetType->ty_QList, ty_Node) { #if 0 printf("; TEST %s\n", TypeToStr(scan, NULL)); #endif if (matchbasicfields(type, scan) && type->ty_ProcType.et_ArgsType == scan->ty_ProcType.et_ArgsType && type->ty_ProcType.et_RetType == scan->ty_ProcType.et_RetType && type->ty_ProcType.et_ArgCount == scan->ty_ProcType.et_ArgCount) { type = scan; break; } } break; case TY_STORAGE: dassert(type->ty_SQList == &StorageTypeList); if (type->ty_Flags & TF_ISINTERNAL) { scan = type; break; } RUNE_FOREACH(scan, &StorageTypeList, ty_Node) { #if 0 printf("; TEST %s\n", TypeToStr(scan, NULL)); #endif if (matchbasicfields(type, scan) && type->ty_StorType.et_Bytes == scan->ty_StorType.et_Bytes) { type = scan; break; } } break; case TY_UNRESOLVED: scan = type; /* XXX */ break; case TY_DYNAMIC: dassert(type->ty_SQList == &DynamicTypeList); if (type->ty_Flags & TF_ISINTERNAL) { scan = type; break; } RUNE_FOREACH(scan, &DynamicTypeList, ty_Node) { #if 0 printf("; TEST %s\n", TypeToStr(scan, NULL)); #endif if (matchbasicfields(type, scan)) { type = scan; break; } } break; case TY_IMPORT: scan = type; /* XXX */ break; default: scan = NULL; dpanic("Unknown type %d (type=%p)", type->ty_Op, type); break; } dassert(scan != NULL); /* we should find at least ourselves */ if (type != otype) { collapseType(&type, NULL); if (adjtype) { RUNE_REMOVE(adjtype->ty_SQList, adjtype, ty_Node); adjtype->ty_SQList = &type->ty_QList; RUNE_INSERT_TAIL(adjtype->ty_SQList, adjtype, ty_Node); } #if 0 printf("; COLLAPSE %s <- %s\n", TypeToStr(type, NULL), TypeToStr(otype, NULL)); if (type->ty_Op == TY_CLASS && type->ty_ClassType.et_Super && type->ty_ClassType.et_Super != otype->ty_ClassType.et_Super) printf("; SUPERCLASS %s <- %s\n", TypeToStr(type->ty_ClassType.et_Super, NULL), TypeToStr(otype->ty_ClassType.et_Super, NULL)); #endif *typep = type; } else { #if 0 printf("; MATCHEDSAME\n"); #endif } type->ty_Flags |= TF_LAYOUT; }