#include "GlobalAnalyzer.h" GlobalAnalyzer::GlobalAnalyzer(Errors* errors, Linker* linker) : mErrors(errors), mLinker(linker), mCalledFunctions(nullptr), mCallingFunctions(nullptr), mVariableFunctions(nullptr), mFunctions(nullptr), mGlobalVariables(nullptr), mTopoFunctions(nullptr), mCompilerOptions(COPT_DEFAULT) { } GlobalAnalyzer::~GlobalAnalyzer(void) { } void GlobalAnalyzer::DumpCallGraph(void) { printf("------------------------------\n"); for (int i = 0; i < mFunctions.Size(); i++) { GrowingArray decs(nullptr); GrowingArray calls(0); Declaration* from = mFunctions[i]; for (int j = 0; j < from->mCalled.Size(); j++) { Declaration* to = from->mCalled[j]; int k = decs.IndexOf(to); if (k == -1) { decs.Push(to); calls.Push(1); } else calls[k]++; } if (decs.Size() > 0) { for (int j = 0; j < decs.Size(); j++) { if (decs[j]->mType == DT_CONST_FUNCTION) printf("CALL %s[%d, %08llx] -> %d -> %s[%d, %08llx]\n", from->mQualIdent->mString, from->mComplexity, from->mFlags, calls[j], decs[j]->mQualIdent->mString, decs[j]->mComplexity, decs[j]->mFlags); else printf("CALL %s[%d, %08llx] -> %d\n", from->mQualIdent->mString, from->mComplexity, from->mFlags, calls[j]); } } else { printf("LEAF %d -> %s[%d, %08llx]\n", from->mCallers.Size(), from->mQualIdent->mString, from->mComplexity, from->mFlags ); } } for (int i = 0; i < mGlobalVariables.Size(); i++) { Declaration* var = mGlobalVariables[i]; printf("VAR %s[%d, %08llx, %d]\n", var->mQualIdent->mString, var->mSize, var->mFlags, var->mUseCount); } } static int VarUseCountScale(Declaration* type) { if (type->mType == DT_TYPE_BOOL || type->mType == DT_TYPE_INTEGER || type->mType == DT_TYPE_FLOAT || type->mType == DT_TYPE_ENUM) return 0x100 / type->mSize; else if (type->mType == DT_TYPE_POINTER) return 0x800; else if (type->mType == DT_TYPE_ARRAY) { if (type->mSize > 0) return VarUseCountScale(type->mBase) / type->mSize; else return 0; } else if (type->mSize == DT_TYPE_STRUCT) { int size = 0; Declaration* e = type->mParams; while (e) { int t = VarUseCountScale(e->mBase); if (t == 0) return 0; size += t; e = e->mNext; } return size / (type->mSize * type->mSize); } else return 0; } void GlobalAnalyzer::AutoZeroPage(LinkerSection* lszp, int zpsize) { if (mCompilerOptions & COPT_OPTIMIZE_AUTO_ZEROPAGE) { GrowingArray vars(nullptr); for (int i = 0; i < mGlobalVariables.Size(); i++) { Declaration* var = mGlobalVariables[i]; if (var->mFlags & DTF_ANALYZED) { if (var->mFlags & DTF_ZEROPAGE) zpsize -= var->mSize; else if (var->mValue) ; else { var->mUseCount *= VarUseCountScale(var->mBase); if (var->mUseCount) { int j = 0; while (j < vars.Size() && vars[j]->mUseCount > var->mUseCount) j++; vars.Insert(j, var); } } } } int i = 0; while (i < vars.Size() && zpsize > 0) { if (vars[i]->mSize <= zpsize && !vars[i]->mLinkerObject) { vars[i]->mSection = lszp; vars[i]->mFlags |= DTF_ZEROPAGE; zpsize -= vars[i]->mSize; } i++; } } } void GlobalAnalyzer::TopoSort(Declaration* procDec) { if (!(procDec->mFlags & DTF_FUNC_ANALYZING)) { procDec->mFlags |= DTF_FUNC_ANALYZING; if (!mTopoFunctions.Contains(procDec)) { for (int i = 0; i < procDec->mCalled.Size(); i++) TopoSort(procDec->mCalled[i]); mTopoFunctions.Push(procDec); } procDec->mFlags &= ~DTF_FUNC_ANALYZING; } } int GlobalAnalyzer::CallerInvokes(Declaration* called) { int n = 0; for (int i = 0; i < called->mCallers.Size(); i++) { Declaration* f = called->mCallers[i]; n += CallerInvokes(f, called); } return n; } int GlobalAnalyzer::CallerInvokes(Declaration* caller, Declaration* called) { int n = 1; if (caller->mType == DT_CONST_FUNCTION && (caller->mFlags & (DTF_INLINE | DTF_FORCE_INLINE | DTF_REQUEST_INLINE)) && !(caller->mFlags & DTF_PREVENT_INLINE) && !(caller->mFlags & DTF_FUNC_RECURSIVE) && !(caller->mFlags & DTF_FUNC_VARIABLE) && !(caller->mFlags & DTF_EXPORT)) n = CallerInvokes(caller); return n > 1 ? n : 1; } void GlobalAnalyzer::AutoInline(void) { for (int i = 0; i < mFunctions.Size(); i++) TopoSort(mFunctions[i]); bool changed = false; do { changed = false; // Reverse order, to check inline from bottom to top of call graph for (int i = 0; i< mTopoFunctions.Size(); i++) { Declaration* f = mTopoFunctions[i]; if (f->mType == DT_CONST_FUNCTION && !(f->mFlags & DTF_INLINE) && !(f->mFlags & DTF_EXPORT) && !(f->mFlags & DTF_PREVENT_INLINE) && !(f->mBase->mFlags & DTF_VARIADIC) && !(f->mFlags & DTF_FUNC_VARIABLE) && !((f->mFlags & DTF_FUNC_ASSEMBLER) && !(f->mFlags & DTF_REQUEST_INLINE)) && !(f->mFlags & DTF_INTRINSIC) && !(f->mFlags & DTF_FUNC_RECURSIVE) && !(f->mFlags & DTF_FUNC_NO_RETURN)) { int nparams = 0; Declaration* dec = f->mBase->mParams; while (dec) { nparams += dec->mSize; dec = dec->mNext; } int invokes = CallerInvokes(f); int cost = (f->mComplexity - 20 * nparams - 10); // printf("CHECK INLINING %s (%d) %d * (%d - 1)\n", f->mIdent->mString, f->mComplexity, cost, invokes); bool doinline = false; if ((f->mCompilerOptions & COPT_OPTIMIZE_INLINE) && (f->mFlags & DTF_REQUEST_INLINE) || (f->mFlags & DTF_FORCE_INLINE)) doinline = true; if (f->mLocalSize < 100) { if ((f->mCompilerOptions & COPT_OPTIMIZE_AUTO_INLINE) && ((cost - 20) * (invokes - 1) <= 20)) { if (f->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) { if (invokes == 1 && f->mSection == f->mCallers[0]->mSection || cost < 0) doinline = true; } else if (invokes == 1 && f->mComplexity > 100) { // printf("CHECK INLINING2 %s <- %s %d\n", f->mIdent->mString, f->mCallers[0]->mIdent->mString, f->mCallers[0]->mCalled.Size()); if (cost < 0 || f->mCallers[0]->mComplexity + cost < 1000 || f->mCallers[0]->mCalled.Size() == 1) doinline = true; } else doinline = true; } if ((f->mCompilerOptions & COPT_OPTIMIZE_AUTO_INLINE_ALL) && (cost * (invokes - 1) <= 10000)) doinline = true; } if (doinline) { // printf("INLINING %s %d * (%d - 1)\n", f->mIdent->mString, cost, invokes); f->mFlags |= DTF_INLINE; for (int j = 0; j < f->mCallers.Size(); j++) { Declaration* cf = f->mCallers[j]; int sk = 0, dk = 0; while (sk < cf->mCalled.Size()) { if (cf->mCalled[sk] == f && ((cf->mFlags & DTF_NATIVE) || !(f->mFlags & DTF_NATIVE))) { cf->mComplexity += cost; for (int m = 0; m < f->mCalled.Size(); m++) { cf->mCalled.Push(f->mCalled[m]); f->mCalled[m]->mCallers.Push(cf); } } else cf->mCalled[dk++] = cf->mCalled[sk]; sk++; } cf->mCalled.SetSize(dk); } changed = true; } } } } while (changed); for (int i = 0; i < mFunctions.Size(); i++) { CheckFastcall(mFunctions[i], true); } for (int i = 0; i < mFunctions.Size(); i++) { Declaration* dec = mFunctions[i]; Declaration* pdec = dec->mBase->mParams; while (pdec) { if (pdec->mFlags & DTF_FPARAM_CONST) { pdec->mFlags |= DTF_FPARAM_UNUSED; pdec->mVarIndex = dec->mNumVars++; Expression* aexp = new Expression(pdec->mLocation, EX_INITIALIZATION); Expression* pexp = new Expression(pdec->mLocation, EX_VARIABLE); Expression* lexp = new Expression(dec->mLocation, EX_SEQUENCE); pexp->mDecType = pdec->mBase; pexp->mDecValue = pdec; aexp->mDecType = pdec->mBase; aexp->mToken = TK_ASSIGN; aexp->mLeft = pexp; aexp->mRight = pdec->mValue; lexp->mLeft = aexp; lexp->mRight = dec->mValue; dec->mValue = lexp; } pdec = pdec->mNext; } } } bool GlobalAnalyzer::MarkCycle(Declaration* rootDec, Declaration* procDec) { if (rootDec == procDec) return true; if (!(procDec->mFlags & DTF_FUNC_ANALYZING)) { procDec->mFlags |= DTF_FUNC_ANALYZING; bool cycle = false; for (int i = 0; i < procDec->mCalled.Size(); i++) { if (MarkCycle(rootDec, procDec->mCalled[i])) cycle = true; } if (cycle) procDec->mFlags |= DTF_FUNC_RECURSIVE; procDec->mFlags &= ~DTF_FUNC_ANALYZING; return cycle; } return false; } void GlobalAnalyzer::MarkRecursions(void) { for (int i = 0; i < mFunctions.Size(); i++) { Declaration* cf = mFunctions[i]; for (int j = 0; j < cf->mCalled.Size(); j++) { if (MarkCycle(cf, cf->mCalled[j])) cf->mFlags |= DTF_FUNC_RECURSIVE; } } } void GlobalAnalyzer::CheckFastcall(Declaration* procDec, bool head) { if (!(procDec->mBase->mFlags & DTF_FASTCALL) && (procDec->mType == DT_CONST_FUNCTION) && !(procDec->mFlags & DTF_FUNC_ANALYZING)) { // printf("CheckFastcall1 %s %08llx %08llx\n", procDec->mQualIdent->mString, procDec->mFlags, procDec->mBase->mFlags); procDec->mFlags |= DTF_FUNC_ANALYZING; int nbase = 0; for (int i = 0; i < procDec->mCalled.Size(); i++) { Declaration* cf = procDec->mCalled[i]; if (cf->mType == DT_TYPE_FUNCTION) { for (int i = 0; i < mVariableFunctions.Size(); i++) { Declaration* vf = mVariableFunctions[i]; if (vf->mBase->IsSame(cf)) { CheckFastcall(vf, false); int n = vf->mBase->mFastCallSize; if (n > nbase) nbase = n; } } // procDec->mFlags |= DTF_DYNSTACK; } else CheckFastcall(cf, false); if (cf->mFlags & DTF_DYNSTACK) procDec->mFlags |= DTF_DYNSTACK; // if (!(cf->mBase->mFlags & DTF_FASTCALL)) // procDec->mBase->mFlags |= DTF_STACKCALL; cf = cf->mBase; int n = cf->mFastCallSize; if (n > nbase) nbase = n; } // printf("CheckFastcall2 %s %08llx %08llx\n", procDec->mQualIdent->mString, procDec->mFlags, procDec->mBase->mFlags); if (procDec->mValue && procDec->mValue->mType == EX_DISPATCH) { Declaration* maxf = nullptr, * reff = nullptr; bool stackCall = false; for (int i = 0; i < procDec->mCalled.Size(); i++) { Declaration* cf = procDec->mCalled[i]; if (cf->mBase->mFlags & DTF_STACKCALL) { stackCall = true; reff = cf; } if (!maxf) maxf = cf; else if (cf->mBase->mFastCallBase > maxf->mBase->mFastCallBase) maxf = cf; } if (!reff) reff = maxf; for (int i = 0; i < procDec->mCalled.Size(); i++) { Declaration* cf = procDec->mCalled[i]; if (stackCall) { cf->mBase->mFlags &= ~DTF_FASTCALL; cf->mBase->mFlags |= DTF_STACKCALL; } if (cf != reff) { Declaration* fp = cf->mBase->mParams, * mp = reff->mBase->mParams; while (fp) { fp->mVarIndex = mp->mVarIndex; fp = fp->mNext; mp = mp->mNext; } assert(!mp); cf->mBase->mFastCallSize = cf->mFastCallSize = maxf->mBase->mFastCallSize; } if (cf != maxf) cf->mBase->mFastCallBase = cf->mFastCallBase = maxf->mBase->mFastCallBase; } procDec->mFastCallBase = procDec->mBase->mFastCallBase; procDec->mFastCallSize = procDec->mBase->mFastCallSize; procDec->mFlags &= ~DTF_FUNC_ANALYZING; return; } procDec->mFastCallBase = nbase; procDec->mFastCallSize = nbase; procDec->mBase->mFastCallBase = nbase; procDec->mBase->mFastCallSize = nbase; procDec->mFlags &= ~DTF_FUNC_ANALYZING; // printf("CheckFastcall3 %s %08llx %08llx\n", procDec->mQualIdent->mString, procDec->mFlags, procDec->mBase->mFlags); if (procDec->mBase->mFlags & DTF_STACKCALL) { procDec->mBase->mFlags |= DTF_STACKCALL; } else if (procDec->mFlags & DTF_FUNC_RECURSIVE) { // if (head) procDec->mBase->mFlags |= DTF_STACKCALL; } else if (!(procDec->mBase->mFlags & DTF_VARIADIC) && !(procDec->mFlags & DTF_FUNC_VARIABLE) && !(procDec->mFlags & DTF_DYNSTACK)) { int nparams = nbase, npalign = 0; int numfpzero = BC_REG_FPARAMS_END - BC_REG_FPARAMS; int fplimit = numfpzero; if ((procDec->mFlags & DTF_NATIVE) || (mCompilerOptions & COPT_NATIVE)) { if (!(procDec->mFlags & DTF_FUNC_INTRCALLED)) fplimit += 256; } if (procDec->mBase->mBase->mType == DT_TYPE_STRUCT) { if (nbase < numfpzero && nbase + 2 > numfpzero) nbase = numfpzero; nparams += 2; } int cnparams = nparams; Declaration* dec = procDec->mBase->mParams; while (dec) { if (!(dec->mFlags & DTF_FPARAM_UNUSED)) { // Check for parameter crossing boundary if (cnparams < numfpzero && cnparams + dec->mBase->mSize > numfpzero) { npalign = numfpzero - nparams; cnparams += npalign; } cnparams += dec->mBase->mSize; } dec = dec->mNext; } if (cnparams <= fplimit) { npalign = 0; dec = procDec->mBase->mParams; while (dec) { if (dec->mFlags & DTF_FPARAM_UNUSED) { } else if (dec->mForwardParam && (dec->mForwardCall->mBase->mFlags & DTF_FASTCALL) && !(dec->mForwardCall->mFlags & DTF_INLINE)) { dec->mVarIndex = dec->mForwardParam->mVarIndex; } else { dec->mForwardParam = nullptr; // Check for parameter crossing boundary if (nparams < numfpzero && nparams + dec->mBase->mSize > numfpzero) { npalign = numfpzero - nparams; nparams += npalign; } dec->mVarIndex = nparams; nparams += dec->mBase->mSize; } dec = dec->mNext; } procDec->mFastCallBase = nbase; procDec->mFastCallSize = nparams; procDec->mBase->mFastCallBase = nbase; procDec->mBase->mFastCallSize = nparams; procDec->mBase->mFlags |= DTF_FASTCALL; // printf("FASTCALL %s\n", procDec->mQualIdent->mString); } else { // printf("STACKCALL %s, %d %d\n", procDec->mQualIdent->mString, cnparams, fplimit); procDec->mBase->mFlags |= DTF_STACKCALL; } } else { // printf("STACKCALL %s, F%d\n", procDec->mQualIdent->mString, !!(procDec->mFlags& DTF_FUNC_VARIABLE)); procDec->mBase->mFlags |= DTF_STACKCALL; } } } void GlobalAnalyzer::CheckInterrupt(void) { bool changed = false; do { changed = false; for (int i = 0; i < mFunctions.Size(); i++) { Declaration* f = mFunctions[i]; if (f->mFlags & DTF_FUNC_INTRCALLED) { for (int j = 0; j < f->mCalled.Size(); j++) { Declaration* cf = f->mCalled[j]; if (!(cf->mFlags & DTF_FUNC_INTRCALLED)) { cf->mFlags |= DTF_FUNC_INTRCALLED; changed = true; } } } } } while (changed); } bool GlobalAnalyzer::IsStackParam(const Declaration* pdec) const { if (pdec->mType == DT_TYPE_STRUCT) { if (pdec->mSize > 4) return true; if (pdec->mCopyConstructor) { if (!((mCompilerOptions & COPT_OPTIMIZE_INLINE) && (pdec->mCopyConstructor->mFlags & DTF_REQUEST_INLINE))) return true; } if (pdec->mDestructor) { if (!((mCompilerOptions & COPT_OPTIMIZE_INLINE) && (pdec->mDestructor->mFlags & DTF_REQUEST_INLINE))) return true; } } return false; } void GlobalAnalyzer::UndoParamReference(Expression* exp, Declaration * param) { if (exp) { if (exp->mType == EX_VARIABLE) { if (exp->mDecValue == param) exp->mDecType = param->mBase; } UndoParamReference(exp->mLeft, param); UndoParamReference(exp->mRight, param); } } void GlobalAnalyzer::AnalyzeProcedure(Expression* cexp, Expression* exp, Declaration* dec) { dec->mUseCount++; if (dec->mFlags & DTF_FUNC_ANALYZING) { dec->mFlags |= DTF_FUNC_RECURSIVE; dec->mFlags &= ~DTF_FUNC_CONSTEXPR; } if (!(dec->mFlags & DTF_ANALYZED)) { dec->mFlags |= DTF_FUNC_ANALYZING; if (dec->mFlags & DTF_DEPRECATED) { mErrors->Error(dec->mLocation, EWARN_DEFAULT_COPY_DEPRECATED, "Using deprecated function", dec->mQualIdent->mString); if (cexp) mErrors->Error(cexp->mLocation, EINFO_CALLED_FROM, "Called from here"); } mFunctions.Push(dec); Declaration* pdec = dec->mBase->mParams; while (pdec) { if (IsStackParam(pdec->mBase)) dec->mBase->mFlags |= DTF_STACKCALL; pdec = pdec->mNext; } dec->mFlags |= DTF_ANALYZED; dec->mFlags |= DTF_FUNC_INTRSAVE; if (dec->mBase->mBase && dec->mBase->mBase->mType != DT_TYPE_VOID) dec->mFlags |= DTF_FUNC_NO_RETURN; if (dec->mFlags & DTF_INTERRUPT) dec->mFlags |= DTF_FUNC_INTRCALLED; if ((dec->mFlags & DTF_INTRINSIC) && !dec->mValue) dec->mFlags |= DTF_FUNC_CONSTEXPR; else if (dec->mFlags & DTF_DEFINED) { if (mCompilerOptions & COPT_OPTIMIZE_CONST_EXPRESSIONS) dec->mFlags |= DTF_FUNC_CONSTEXPR; dec->mFlags |= DTF_FUNC_PURE; Analyze(exp, dec, 0); Declaration* pdec = dec->mBase->mParams; int vi = 0; while (pdec) { pdec->mVarIndex += vi; if (pdec->mBase->mType == DT_TYPE_REFERENCE && pdec->mBase->mBase->IsSimpleType() && !(pdec->mFlags & DTF_VAR_ADDRESS) && (pdec->mBase->mBase->mFlags & DTF_CONST)) { pdec->mBase = pdec->mBase->mBase; pdec->mSize = pdec->mBase->mSize; vi += pdec->mSize - 2; UndoParamReference(exp, pdec); } pdec = pdec->mNext; } } else { mErrors->Error(dec->mLocation, EERR_UNDEFINED_OBJECT, "Calling undefined function", dec->FullIdent()); if (cexp) mErrors->Error(cexp->mLocation, EINFO_CALLED_FROM, "Called from here"); } dec->mFlags &= ~DTF_FUNC_ANALYZING; } } void GlobalAnalyzer::AnalyzeAssembler(Expression* exp, Declaration* procDec) { while (exp) { if (procDec) procDec->mComplexity += 2; if (exp->mLeft && exp->mLeft->mDecValue) { Declaration* adec = exp->mLeft->mDecValue; if (adec->mType == DT_LABEL_REF) { } else if (adec->mType == DT_VARIABLE_REF) { if (adec->mBase->mFlags & DTF_GLOBAL) { AnalyzeGlobalVariable(adec->mBase); adec->mBase->mFlags |= DTF_VAR_ALIASING; } } else if (adec->mType == DT_LABEL) { } else if (adec->mType == DT_VARIABLE) { if (adec->mFlags & DTF_GLOBAL) { AnalyzeGlobalVariable(adec); adec->mFlags |= DTF_VAR_ALIASING; } } else if (adec->mType == DT_FUNCTION_REF) { AnalyzeProcedure(exp, adec->mBase->mValue, adec->mBase); RegisterProc(adec->mBase); } else if (adec->mType == DT_CONST_FUNCTION) { AnalyzeProcedure(exp, adec->mValue, adec); RegisterCall(procDec, adec); } } exp = exp->mRight; } } void GlobalAnalyzer::AnalyzeGlobalVariable(Declaration* dec) { while (dec->mType == DT_VARIABLE_REF) dec = dec->mBase; dec->mUseCount++; if (!(dec->mFlags & DTF_ANALYZED)) { dec->mFlags |= DTF_ANALYZED; mGlobalVariables.Push(dec); if (dec->mValue) { Analyze(dec->mValue, dec, 0); } } } void GlobalAnalyzer::AnalyzeInit(Declaration* mdec) { while (mdec) { if (mdec->mValue) RegisterProc(Analyze(mdec->mValue, mdec, 0)); else if (mdec->mParams) AnalyzeInit(mdec->mParams); mdec = mdec->mNext; } } Declaration * GlobalAnalyzer::Analyze(Expression* exp, Declaration* procDec, uint32 flags) { Declaration* ldec, * rdec; exp->mFlags = flags; switch (exp->mType) { case EX_ERROR: case EX_VOID: break; case EX_CONSTANT: if (mCompilerOptions & COPT_DEBUGINFO) exp->mDecValue->mReferences.Push(exp); if (exp->mDecValue->mType == DT_CONST_FUNCTION) AnalyzeProcedure(exp, exp->mDecValue->mValue, exp->mDecValue); else if (exp->mDecValue->mType == DT_CONST_STRUCT) { AnalyzeInit(exp->mDecValue->mParams); } else if (exp->mDecValue->mType == DT_CONST_POINTER) { ldec = Analyze(exp->mDecValue->mValue, procDec, ANAFL_LHS | ANAFL_ALIAS); if (ldec->mType == DT_VARIABLE) ldec->mFlags |= DTF_VAR_ALIASING; RegisterProc(ldec); } else if (exp->mDecValue->mType == DT_CONST_ADDRESS) { procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; } else if (exp->mDecValue->mType == DT_CONST_ASSEMBLER) { AnalyzeAssembler(exp->mDecValue->mValue, procDec); } return exp->mDecValue; case EX_VARIABLE: if (exp->mDecType->IsSimpleType()) procDec->mComplexity += 5 * exp->mDecType->mSize; else procDec->mComplexity += 10; if (mCompilerOptions & COPT_DEBUGINFO) exp->mDecValue->mReferences.Push(exp); if (flags & ANAFL_ALIAS) { Declaration* dec = exp->mDecValue; while (dec->mType == DT_VARIABLE_REF) dec = dec->mBase; dec->mFlags |= DTF_VAR_ALIASING; } if ((exp->mDecValue->mFlags & DTF_STATIC) || (exp->mDecValue->mFlags & DTF_GLOBAL)) { Declaration* type = exp->mDecValue->mBase; while (type->mType == DT_TYPE_ARRAY) type = type->mBase; if (!(type->mFlags & DTF_CONST)) procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; if (flags & ANAFL_LHS) procDec->mFlags &= ~DTF_FUNC_PURE; AnalyzeGlobalVariable(exp->mDecValue); } else { if (flags & ANAFL_LHS) exp->mDecValue->mFlags |= DTF_VAR_ADDRESS; if (!(exp->mDecValue->mFlags & DTF_ANALYZED)) { procDec->mLocalSize += exp->mDecValue->mSize; exp->mDecValue->mFlags |= DTF_ANALYZED; } } return exp->mDecValue; case EX_INITIALIZATION: case EX_ASSIGNMENT: procDec->mComplexity += 5 * exp->mLeft->mDecType->mSize; ldec = Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS); rdec = Analyze(exp->mRight, procDec, ANAFL_RHS); if (exp->mLeft->mType == EX_VARIABLE && exp->mRight->mType == EX_CALL && exp->mLeft->mDecType->mType == DT_TYPE_STRUCT) exp->mLeft->mDecValue->mFlags |= DTF_VAR_ALIASING; RegisterProc(rdec); return ldec; case EX_BINARY: procDec->mComplexity += 10 * exp->mDecType->mSize; ldec = Analyze(exp->mLeft, procDec, flags & ~ANAFL_ALIAS); rdec = Analyze(exp->mRight, procDec, flags & ~ANAFL_ALIAS); return ldec; case EX_RELATIONAL: procDec->mComplexity += 10 * exp->mLeft->mDecType->mSize; ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); rdec = Analyze(exp->mRight, procDec, ANAFL_RHS); return TheBoolTypeDeclaration; case EX_PREINCDEC: procDec->mComplexity += 10 * exp->mLeft->mDecType->mSize; return Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS); case EX_PREFIX: if (exp->mToken == TK_BINARY_AND) { ldec = Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_ALIAS); if (ldec->mType == DT_VARIABLE) ldec->mFlags |= DTF_VAR_ALIASING; } else if (exp->mToken == TK_MUL) { ldec = Analyze(exp->mLeft, procDec, 0); procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; if (flags & ANAFL_LHS) procDec->mFlags &= ~DTF_FUNC_PURE; return exp->mDecType; } else if (exp->mToken == TK_BANKOF) { return TheUnsignedCharTypeDeclaration; } else if (exp->mToken == TK_SIZEOF) { return TheUnsignedIntTypeDeclaration; } else { procDec->mComplexity += 10 * exp->mLeft->mDecType->mSize; return Analyze(exp->mLeft, procDec, 0); } break; case EX_POSTFIX: procDec->mComplexity += 10 * exp->mLeft->mDecType->mSize; break; case EX_POSTINCDEC: procDec->mComplexity += 10 * exp->mLeft->mDecType->mSize; return Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS); case EX_INDEX: procDec->mComplexity += 10 * exp->mRight->mDecType->mSize; ldec = Analyze(exp->mLeft, procDec, flags & ~ANAFL_ALIAS); if (ldec->mType == DT_VARIABLE || ldec->mType == DT_ARGUMENT) { ldec = ldec->mBase; if (ldec->mType == DT_TYPE_POINTER) { if (flags & ANAFL_LHS) procDec->mFlags &= ~DTF_FUNC_PURE; procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; } } rdec = Analyze(exp->mRight, procDec, 0); if (ldec->mBase) return ldec->mBase; break; case EX_QUALIFY: Analyze(exp->mLeft, procDec, flags); return exp->mDecValue->mBase; case EX_DISPATCH: procDec->mFlags |= DTF_PREVENT_INLINE; Analyze(exp->mLeft, procDec, flags & ~ANAFL_ALIAS); // RegisterCall(procDec, exp->mLeft->mDecType); break; case EX_VCALL: exp->mType = EX_CALL; exp->mLeft->mDecValue = exp->mLeft->mDecValue->mVTable; // intentional fall through case EX_CALL: case EX_INLINE: procDec->mComplexity += 10; ldec = Analyze(exp->mLeft, procDec, 0); if ((ldec->mFlags & DTF_INTRINSIC) && !ldec->mValue) { } else { if (exp->mType == EX_INLINE) { for (int i = 0; i < ldec->mCalled.Size(); i++) RegisterCall(procDec, ldec->mCalled[i]); } else RegisterCall(procDec, ldec); if (!(GetProcFlags(ldec) & (DTF_FUNC_INTRSAVE | DTF_INTERRUPT))) { procDec->mFlags &= ~DTF_FUNC_INTRSAVE; if (procDec->mFlags & DTF_INTERRUPT) mErrors->Error(exp->mLocation, EWARN_NOT_INTERRUPT_SAFE, "Calling non interrupt safe function", ldec->mQualIdent); } if (!(GetProcFlags(ldec) & DTF_FUNC_PURE)) procDec->mFlags &= ~DTF_FUNC_PURE; } if (exp->mRight) { // Check for struct to struct forwarding Expression* rex = exp->mRight; Declaration* pdec = ldec->mBase->mParams; while (rex) { Expression* pex = rex->mType == EX_LIST ? rex->mLeft : rex; procDec->mComplexity += 5 * pex->mDecType->mSize; if (pdec && !(ldec->mBase->mFlags & DTF_VARIADIC) && !(ldec->mFlags & (DTF_INTRINSIC | DTF_FUNC_ASSEMBLER))) { #if 1 if (mCompilerOptions & COPT_OPTIMIZE_CONST_PARAMS) { if (!(pdec->mFlags & DTF_FPARAM_NOCONST)) { if (pex->mType == EX_CONSTANT) { if (pdec->mFlags & DTF_FPARAM_CONST) { if (!pex->mDecValue->IsSameValue(pdec->mValue->mDecValue)) { pdec->mFlags |= DTF_FPARAM_NOCONST; pdec->mFlags &= ~DTF_FPARAM_CONST; } } else { pdec->mValue = pex; pdec->mFlags |= DTF_FPARAM_CONST; } } else { pdec->mFlags |= DTF_FPARAM_NOCONST; pdec->mFlags &= ~DTF_FPARAM_CONST; } } } else { pdec->mFlags |= DTF_FPARAM_NOCONST; pdec->mFlags &= ~DTF_FPARAM_CONST; } #endif } if (pdec && pdec->mBase->mType == DT_TYPE_STRUCT && pdec->mBase->mCopyConstructor) { if (pdec->mBase->mMoveConstructor) { AnalyzeProcedure(exp, pdec->mBase->mMoveConstructor->mValue, pdec->mBase->mMoveConstructor); RegisterCall(procDec, pdec->mBase->mMoveConstructor); } AnalyzeProcedure(exp, pdec->mBase->mCopyConstructor->mValue, pdec->mBase->mCopyConstructor); RegisterCall(procDec, pdec->mBase->mCopyConstructor); } if (pex->mType == EX_CALL && IsStackParam(pex->mDecType) && !(pdec && (pdec->mBase->mType == DT_TYPE_REFERENCE || pdec->mBase->mType == DT_TYPE_RVALUEREF))) ldec->mBase->mFlags |= DTF_STACKCALL; RegisterProc(Analyze(pex, procDec, (pdec && pdec->mBase->IsReference()) ? ANAFL_LHS : 0)); if (pdec) pdec = pdec->mNext; if (rex->mType == EX_LIST) rex = rex->mRight; else rex = nullptr; } } break; case EX_LIST: case EX_COMMA: RegisterProc(Analyze(exp->mLeft, procDec, 0)); return Analyze(exp->mRight, procDec, 0); case EX_RETURN: if (exp->mLeft) { RegisterProc(Analyze(exp->mLeft, procDec, procDec->mBase->mBase->IsReference() ? ANAFL_LHS : 0)); if (procDec->mBase->mBase && procDec->mBase->mBase->mType == DT_TYPE_STRUCT && procDec->mBase->mBase->mCopyConstructor) { if (procDec->mBase->mBase->mMoveConstructor) { AnalyzeProcedure(exp, procDec->mBase->mBase->mMoveConstructor->mValue, procDec->mBase->mBase->mMoveConstructor); RegisterCall(procDec, procDec->mBase->mBase->mMoveConstructor); } AnalyzeProcedure(exp, procDec->mBase->mBase->mCopyConstructor->mValue, procDec->mBase->mBase->mCopyConstructor); RegisterCall(procDec, procDec->mBase->mBase->mCopyConstructor); } procDec->mFlags &= ~DTF_FUNC_NO_RETURN; } break; case EX_SEQUENCE: do { if (exp->mType == EX_SEQUENCE) { if (exp->mLeft) ldec = Analyze(exp->mLeft, procDec, 0); exp = exp->mRight; } else return Analyze(exp, procDec, 0); } while (exp); break; case EX_SCOPE: Analyze(exp->mLeft, procDec, 0); break; case EX_CONSTRUCT: if (exp->mLeft->mLeft) Analyze(exp->mLeft->mLeft, procDec, 0); if (exp->mLeft->mRight) Analyze(exp->mLeft->mRight, procDec, 0); if (exp->mRight) return Analyze(exp->mRight, procDec, 0); break; case EX_CLEANUP: Analyze(exp->mRight, procDec, 0); return Analyze(exp->mLeft, procDec, flags & ~ANAFL_ALIAS); case EX_WHILE: procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; procDec->mComplexity += 20; ldec = Analyze(exp->mLeft, procDec, 0); rdec = Analyze(exp->mRight, procDec, 0); break; case EX_IF: procDec->mComplexity += 20; ldec = Analyze(exp->mLeft, procDec, 0); rdec = Analyze(exp->mRight->mLeft, procDec, 0); if (exp->mRight->mRight) rdec = Analyze(exp->mRight->mRight, procDec, 0); break; case EX_ELSE: break; case EX_FOR: procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; procDec->mComplexity += 30; if (exp->mLeft->mRight) ldec = Analyze(exp->mLeft->mRight, procDec, 0); if (exp->mLeft->mLeft->mLeft) ldec = Analyze(exp->mLeft->mLeft->mLeft, procDec, 0); rdec = Analyze(exp->mRight, procDec, 0); if (exp->mLeft->mLeft->mRight) ldec = Analyze(exp->mLeft->mLeft->mRight, procDec, 0); break; case EX_FORBODY: ldec = Analyze(exp->mLeft, procDec, 0); if (exp->mRight) Analyze(exp->mRight, procDec, 0); break; case EX_DO: procDec->mComplexity += 20; ldec = Analyze(exp->mLeft, procDec, 0); rdec = Analyze(exp->mRight, procDec, 0); break; case EX_BREAK: case EX_CONTINUE: case EX_ASSUME: break; case EX_TYPE: break; case EX_TYPECAST: return Analyze(exp->mLeft, procDec, 0); break; case EX_LOGICAL_AND: ldec = Analyze(exp->mLeft, procDec, 0); rdec = Analyze(exp->mRight, procDec, 0); break; case EX_LOGICAL_OR: ldec = Analyze(exp->mLeft, procDec, 0); rdec = Analyze(exp->mRight, procDec, 0); break; case EX_LOGICAL_NOT: ldec = Analyze(exp->mLeft, procDec, 0); break; case EX_ASSEMBLER: procDec->mFlags |= DTF_FUNC_ASSEMBLER; procDec->mFlags &= ~DTF_FUNC_CONSTEXPR; procDec->mFlags &= ~DTF_FUNC_PURE; AnalyzeAssembler(exp, procDec); break; case EX_UNDEFINED: break; case EX_SWITCH: ldec = Analyze(exp->mLeft, procDec, 0); exp = exp->mRight; while (exp) { procDec->mComplexity += 10; if (exp->mLeft->mRight) rdec = Analyze(exp->mLeft->mRight, procDec, 0); exp = exp->mRight; } break; case EX_CASE: break; case EX_DEFAULT: break; case EX_CONDITIONAL: procDec->mComplexity += exp->mDecType->mSize * 10; ldec = Analyze(exp->mLeft, procDec, 0); RegisterProc(Analyze(exp->mRight->mLeft, procDec, flags)); RegisterProc(Analyze(exp->mRight->mRight, procDec, flags)); break; } return TheVoidTypeDeclaration; } uint64 GlobalAnalyzer::GetProcFlags(Declaration* to) const { if (to->mType == DT_CONST_FUNCTION) return to->mFlags; else if (to->mType == DT_TYPE_FUNCTION) return to->mFlags; else if (to->mType == DT_TYPE_POINTER && to->mBase->mType == DT_TYPE_FUNCTION) return GetProcFlags(to->mBase); else if (to->mType == DT_VARIABLE || to->mType == DT_ARGUMENT) return GetProcFlags(to->mBase); else return 0; } void GlobalAnalyzer::RegisterCall(Declaration* from, Declaration* to) { if (from) { if (to->mType == DT_VARIABLE || to->mType == DT_ARGUMENT) to = to->mBase; if (to->mType == DT_CONST_FUNCTION) { if (to->mFlags & DTF_DYNSTACK) from->mFlags |= DTF_DYNSTACK; if (!(to->mFlags & DTF_FUNC_CONSTEXPR)) from->mFlags &= ~DTF_FUNC_CONSTEXPR; if (to->mCallers.Size() == 0) mCalledFunctions.Push(to); to->mCallers.Push(from); if (from->mCalled.Size() == 0) mCallingFunctions.Push(from); from->mCalled.Push(to); } else if (to->mType == DT_TYPE_FUNCTION) { from->mFlags &= ~DTF_FUNC_CONSTEXPR; if (from->mCalled.Size() == 0) mCallingFunctions.Push(from); from->mCalled.Push(to); } else if (to->mType == DT_TYPE_POINTER && to->mBase->mType == DT_TYPE_FUNCTION) { from->mFlags &= ~DTF_FUNC_CONSTEXPR; if (from->mCalled.Size() == 0) mCallingFunctions.Push(from); from->mCalled.Push(to->mBase); } } } void GlobalAnalyzer::RegisterProc(Declaration* to) { if (to->mType == DT_CONST_FUNCTION) { #if 1 if (to->mBase->mFlags & DTF_VIRTUAL) { } else #endif if (!(to->mFlags & DTF_FUNC_VARIABLE)) { to->mFlags |= DTF_FUNC_VARIABLE; mVariableFunctions.Push(to); Declaration* pdec = to->mParams; while (pdec) { pdec->mFlags |= DTF_FPARAM_NOCONST; pdec->mFlags &= ~DTF_FPARAM_CONST; pdec = pdec->mNext; } } } }