From 9d8c6991e8374e4d2b4a2adf4c01ddf7ecf04816 Mon Sep 17 00:00:00 2001 From: drmortalwombat <90205530+drmortalwombat@users.noreply.github.com> Date: Sat, 23 Sep 2023 14:56:04 +0200 Subject: [PATCH] Add global optimizer for parameter/return optimizations --- README.md | 6 +- include/opp/iostream.h | 6 +- oscar64/Compiler.cpp | 29 +- oscar64/Compiler.h | 2 + oscar64/CompilerTypes.h | 7 +- oscar64/Constexpr.cpp | 47 +- oscar64/Constexpr.h | 1 + oscar64/Declaration.cpp | 91 +++- oscar64/Declaration.h | 8 +- oscar64/GlobalAnalyzer.cpp | 3 + oscar64/GlobalOptimizer.cpp | 925 ++++++++++++++++++++++++++++++++ oscar64/GlobalOptimizer.h | 43 ++ oscar64/InterCode.cpp | 66 ++- oscar64/InterCode.h | 1 + oscar64/InterCodeGenerator.cpp | 33 +- oscar64/Parser.cpp | 24 +- oscar64/oscar64.cpp | 7 + oscar64/oscar64.vcxproj | 2 + oscar64/oscar64.vcxproj.filters | 6 + 19 files changed, 1258 insertions(+), 49 deletions(-) create mode 100644 oscar64/GlobalOptimizer.cpp create mode 100644 oscar64/GlobalOptimizer.h diff --git a/README.md b/README.md index 2b0ef43..d0d1876 100644 --- a/README.md +++ b/README.md @@ -559,7 +559,11 @@ This sample fills a single screen column with a given color, by generating 25 as This sample initially assigns the value 0 to the pre processor macro ry and increments it each time the loop body is replicated. The loop generates 25 copies of the body, each with a different value for ry. -A simpler loop with only a single line template expansion is provided with the #for(iterator, count) preprocessor command: +A simpler loop with only a single line template expansion is provided with the for preprocessor command: + + #for(, ) + +This sample generates an array with pointers to screen rows: char * const ScreenRows2[] = { #for(i,SCREEN_HEIGHT) Screen + SCREEN_WIDTH * i, diff --git a/include/opp/iostream.h b/include/opp/iostream.h index 5548bef..5d43bc6 100644 --- a/include/opp/iostream.h +++ b/include/opp/iostream.h @@ -9,7 +9,7 @@ class ios { public: - ios(void); + constexpr ios(void); virtual ~ios(void); char fill() const; @@ -85,7 +85,7 @@ typedef ostream & (* manip)(ostream &); class ostream : public ios { public: - ostream(void); + constexpr ostream(void); ostream & put(char c); ostream & write(const char * s, int n); @@ -150,7 +150,7 @@ protected: class costream : public ostream { public: - costream(void); + constexpr costream(void); protected: void bput(char ch); diff --git a/oscar64/Compiler.cpp b/oscar64/Compiler.cpp index baae398..2a76edf 100644 --- a/oscar64/Compiler.cpp +++ b/oscar64/Compiler.cpp @@ -33,6 +33,7 @@ Compiler::Compiler(void) mNativeCodeGenerator = new NativeCodeGenerator(mErrors, mLinker, mCompilationUnits->mSectionCode); mInterCodeModule = new InterCodeModule(mErrors, mLinker); mGlobalAnalyzer = new GlobalAnalyzer(mErrors, mLinker); + mGlobalOptimizer = new GlobalOptimizer(mErrors, mLinker); mCartridgeID = 0x0000; } @@ -835,8 +836,6 @@ bool Compiler::GenerateCode(void) dcrtstart->mSection = sectionStartup; - mGlobalAnalyzer->mCompilerOptions = mCompilerOptions; - if (mCompilerOptions & COPT_CPLUSPLUS) { if (mCompilerOptions & COPT_VERBOSE) @@ -845,6 +844,32 @@ bool Compiler::GenerateCode(void) BuildVTables(); } + if (mCompilerOptions & COPT_OPTIMIZE_GLOBAL) + { + mGlobalOptimizer->mCompilerOptions = mCompilerOptions; + + if (mCompilerOptions & COPT_VERBOSE) + printf("Global optimizer\n"); + + do { + mGlobalOptimizer->Reset(); + + mGlobalOptimizer->AnalyzeAssembler(dcrtstart->mValue, nullptr); + + for (int i = 0; i < mCompilationUnits->mReferenced.Size(); i++) + { + Declaration* dec = mCompilationUnits->mReferenced[i]; + if (dec->mType == DT_CONST_FUNCTION) + mGlobalOptimizer->AnalyzeProcedure(dec->mValue, dec); + else + mGlobalOptimizer->AnalyzeGlobalVariable(dec); + } + } while (mGlobalOptimizer->Optimize()); + + } + + mGlobalAnalyzer->mCompilerOptions = mCompilerOptions; + if (mCompilerOptions & COPT_VERBOSE) printf("Global analyzer\n"); diff --git a/oscar64/Compiler.h b/oscar64/Compiler.h index 3b9f00d..474dcce 100644 --- a/oscar64/Compiler.h +++ b/oscar64/Compiler.h @@ -7,6 +7,7 @@ #include "NativeCodeGenerator.h" #include "InterCodeGenerator.h" #include "GlobalAnalyzer.h" +#include "GlobalOptimizer.h" #include "Linker.h" #include "CompilerTypes.h" @@ -25,6 +26,7 @@ public: InterCodeGenerator* mInterCodeGenerator; InterCodeModule* mInterCodeModule; GlobalAnalyzer* mGlobalAnalyzer; + GlobalOptimizer* mGlobalOptimizer; GrowingArray mByteCodeFunctions; ExpandingArray mNativeProcedures; diff --git a/oscar64/CompilerTypes.h b/oscar64/CompilerTypes.h index e6c6016..79b8fc5 100644 --- a/oscar64/CompilerTypes.h +++ b/oscar64/CompilerTypes.h @@ -13,6 +13,7 @@ static const uint64 COPT_OPTIMIZE_CONST_EXPRESSIONS = 1ULL << 7; static const uint64 COPT_OPTIMIZE_AUTO_ZEROPAGE = 1ULL << 8; static const uint64 COPT_OPTIMIZE_CONST_PARAMS = 1ULL << 9; static const uint64 COPT_OPTIMIZE_MERGE_CALLS = 1ULL << 10; +static const uint64 COPT_OPTIMIZE_GLOBAL = 1ULL << 11; static const uint64 COPT_OPTIMIZE_CODE_SIZE = 1ULL << 16; static const uint64 COPT_NATIVE = 1ULL << 17; @@ -44,11 +45,11 @@ static const uint64 COPT_DEFAULT = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | static const uint64 COPT_OPTIMIZE_DEFAULT = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_CONST_EXPRESSIONS; -static const uint64 COPT_OPTIMIZE_SIZE = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_CONST_EXPRESSIONS | COPT_OPTIMIZE_CODE_SIZE | COPT_OPTIMIZE_CONST_PARAMS | COPT_OPTIMIZE_MERGE_CALLS; +static const uint64 COPT_OPTIMIZE_SIZE = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_CONST_EXPRESSIONS | COPT_OPTIMIZE_CODE_SIZE | COPT_OPTIMIZE_CONST_PARAMS | COPT_OPTIMIZE_MERGE_CALLS | COPT_OPTIMIZE_GLOBAL; -static const uint64 COPT_OPTIMIZE_SPEED = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_AUTO_UNROLL | COPT_OPTIMIZE_CONST_EXPRESSIONS | COPT_OPTIMIZE_ASSEMBLER | COPT_OPTIMIZE_CONST_PARAMS | COPT_OPTIMIZE_MERGE_CALLS; +static const uint64 COPT_OPTIMIZE_SPEED = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_AUTO_UNROLL | COPT_OPTIMIZE_CONST_EXPRESSIONS | COPT_OPTIMIZE_ASSEMBLER | COPT_OPTIMIZE_CONST_PARAMS | COPT_OPTIMIZE_MERGE_CALLS | COPT_OPTIMIZE_GLOBAL; -static const uint64 COPT_OPTIMIZE_ALL = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_AUTO_INLINE_ALL | COPT_OPTIMIZE_AUTO_UNROLL | COPT_OPTIMIZE_CONST_EXPRESSIONS | COPT_OPTIMIZE_ASSEMBLER | COPT_OPTIMIZE_AUTO_ZEROPAGE | COPT_OPTIMIZE_CONST_PARAMS | COPT_OPTIMIZE_MERGE_CALLS; +static const uint64 COPT_OPTIMIZE_ALL = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_AUTO_INLINE_ALL | COPT_OPTIMIZE_AUTO_UNROLL | COPT_OPTIMIZE_CONST_EXPRESSIONS | COPT_OPTIMIZE_ASSEMBLER | COPT_OPTIMIZE_AUTO_ZEROPAGE | COPT_OPTIMIZE_CONST_PARAMS | COPT_OPTIMIZE_MERGE_CALLS | COPT_OPTIMIZE_GLOBAL; enum TargetMachine { diff --git a/oscar64/Constexpr.cpp b/oscar64/Constexpr.cpp index 97efbfa..6f52b05 100644 --- a/oscar64/Constexpr.cpp +++ b/oscar64/Constexpr.cpp @@ -434,6 +434,7 @@ Declaration* ConstexprInterpreter::Value::GetConst(int offset, Declaration* type { case DT_TYPE_INTEGER: case DT_TYPE_BOOL: + case DT_TYPE_ENUM: dec = new Declaration(mLocation, DT_CONST_INTEGER); dec->mBase = type; dec->mFlags = type->mFlags & DTF_SIGNED; @@ -454,20 +455,25 @@ Declaration* ConstexprInterpreter::Value::GetConst(int offset, Declaration* type dec->mSection = dataSection; dec->mOffset = offset; - Declaration* ldec = nullptr; while (type) { for (Declaration* mdec = type->mParams; mdec; mdec = mdec->mNext) { Declaration* cdec = GetConst(offset + mdec->mOffset, mdec->mBase, dataSection); cdec->mOffset = mdec->mOffset; + cdec->mNext = dec->mParams; + dec->mParams = cdec; + } - if (ldec) - ldec->mNext = cdec; - else - dec->mParams = cdec; - - ldec = cdec; + if (type->mVTable) + { + Declaration * cdec = new Declaration(mLocation, DT_CONST_INTEGER); + cdec->mBase = TheConstCharTypeDeclaration; + cdec->mSize = 1; + cdec->mInteger = type->mVTable->mDefaultConstructor->mInteger; + cdec->mOffset = offset + type->mVTable->mOffset; + cdec->mNext = dec->mParams; + dec->mParams = cdec; } if (type->mBase) @@ -587,6 +593,29 @@ void ConstexprInterpreter::DeleteValue(Value* v) mErrors->Error(v->mLocation, EERR_DOUBLE_FREE, "Freeing not allocated memory"); } +Expression* ConstexprInterpreter::EvalConstructor(Expression* exp) +{ + mProcType = exp->mLeft->mDecType; + + Declaration* cdec = exp->mLeft->mDecType->mParams; + + int pos = 0; + mResult = Value(exp->mLocation, cdec->mBase->mBase); + mParams[pos] = Value(exp->mLocation, cdec->mBase); + mParams[pos].PutPtr(Value(&mResult)); + pos = 2; + + mHeap = new ExpandingArray(); + + Execute(exp->mLeft->mDecValue->mValue); + + if (mHeap->Size() > 0) + mErrors->Error(exp->mLocation, EERR_UNBALANCED_HEAP_USE, "Unbalanced heap use in constexpr"); + delete mHeap; + + return mResult.ToExpression(mDataSection); +} + Expression* ConstexprInterpreter::EvalCall(Expression* exp) { mProcType = exp->mLeft->mDecType; @@ -1118,6 +1147,7 @@ ConstexprInterpreter::Value ConstexprInterpreter::Eval(Expression* exp) } case EX_LIST: + case EX_COMMA: Eval(exp->mLeft); return Eval(exp->mRight); @@ -1191,6 +1221,7 @@ ConstexprInterpreter::Value ConstexprInterpreter::Eval(Expression* exp) Value v = Eval(exp->mLeft); if (v.mBaseValue) return Value(exp->mLocation, v.mBaseValue, exp->mDecType, v.mOffset + exp->mDecValue->mOffset); + break; } case EX_INDEX: @@ -1208,6 +1239,7 @@ ConstexprInterpreter::Value ConstexprInterpreter::Eval(Expression* exp) Value p = v.GetPtr(); return Value(exp->mLocation, p.mBaseValue, exp->mDecType, p.mOffset + v.mDecType->mBase->mSize * int(vi.GetInt())); } + break; } case EX_RESULT: @@ -1256,6 +1288,7 @@ ConstexprInterpreter::Flow ConstexprInterpreter::Execute(Expression* exp) case EX_PREFIX: case EX_TYPECAST: case EX_CALL: + case EX_COMMA: case EX_LIST: case EX_CONDITIONAL: case EX_LOGICAL_AND: diff --git a/oscar64/Constexpr.h b/oscar64/Constexpr.h index 8a0df57..7986427 100644 --- a/oscar64/Constexpr.h +++ b/oscar64/Constexpr.h @@ -9,6 +9,7 @@ public: ~ConstexprInterpreter(void); Expression* EvalCall(Expression* exp); + Expression* EvalConstructor(Expression* exp); protected: struct Value; diff --git a/oscar64/Declaration.cpp b/oscar64/Declaration.cpp index 3d5facf..2772d84 100644 --- a/oscar64/Declaration.cpp +++ b/oscar64/Declaration.cpp @@ -222,6 +222,9 @@ void Expression::Dump(int ident) const case EX_LIST: printf("LIST"); break; + case EX_COMMA: + printf("COMMA"); + break; case EX_RETURN: printf("RETURN"); break; @@ -523,6 +526,10 @@ Expression* Expression::ConstantFold(Errors * errors, LinkerSection * dataSectio ex->mDecType = mDecType; return ex; } + else if (mType == EX_PREFIX && mToken == TK_BINARY_AND && mLeft->mType == EX_PREFIX && mLeft->mToken == TK_MUL) + { + return mLeft->mLeft; + } #endif else if (mType == EX_TYPECAST && mLeft->mType == EX_CONSTANT) { @@ -888,7 +895,7 @@ Expression* Expression::ConstantFold(Errors * errors, LinkerSection * dataSectio return ex; } } - else if (mType == EX_CALL && mLeft->mType == EX_CONSTANT && (mLeft->mDecValue->mFlags & DTF_CONSTEXPR)) + else if (mType == EX_CALL && mLeft->mType == EX_CONSTANT && (mLeft->mDecValue->mFlags & DTF_CONSTEXPR) && dataSection) { ConstexprInterpreter cinter(mLocation, errors, dataSection); return cinter.EvalCall(this); @@ -900,7 +907,7 @@ Expression* Expression::ConstantFold(Errors * errors, LinkerSection * dataSectio Declaration::Declaration(const Location& loc, DecType type) : mLocation(loc), mEndLocation(loc), mType(type), mScope(nullptr), mData(nullptr), mIdent(nullptr), mQualIdent(nullptr), mMangleIdent(nullptr), mSize(0), mOffset(0), mFlags(0), mComplexity(0), mLocalSize(0), mNumVars(0), - mBase(nullptr), mParams(nullptr), mParamPack(nullptr), mValue(nullptr), mNext(nullptr), mPrev(nullptr), + mBase(nullptr), mParams(nullptr), mParamPack(nullptr), mValue(nullptr), mReturn(nullptr), mNext(nullptr), mPrev(nullptr), mConst(nullptr), mMutable(nullptr), mDefaultConstructor(nullptr), mDestructor(nullptr), mCopyConstructor(nullptr), mCopyAssignment(nullptr), mMoveConstructor(nullptr), mMoveAssignment(nullptr), mVectorConstructor(nullptr), mVectorDestructor(nullptr), mVectorCopyConstructor(nullptr), mVectorCopyAssignment(nullptr), @@ -908,7 +915,7 @@ Declaration::Declaration(const Location& loc, DecType type) mVarIndex(-1), mLinkerObject(nullptr), mCallers(nullptr), mCalled(nullptr), mAlignment(1), mFriends(nullptr), mInteger(0), mNumber(0), mMinValue(-0x80000000LL), mMaxValue(0x7fffffffLL), mFastCallBase(0), mFastCallSize(0), mStride(0), mStripe(1), mCompilerOptions(0), mUseCount(0), mTokens(nullptr), mParser(nullptr), - mShift(0), mBits(0) + mShift(0), mBits(0), mOptFlags(0) {} Declaration::~Declaration(void) @@ -960,6 +967,71 @@ Declaration* Declaration::BuildArrayPointer(void) return this; } +Declaration* Declaration::ConstCast(Declaration* ntype) +{ + if (ntype == mBase) + return this; + else if (ntype->mType == DT_TYPE_POINTER) + { + if (mBase->mType == DT_TYPE_POINTER) + { + if (mBase->mBase->IsSame(ntype->mBase)) + return this; + + Declaration* pdec = this->Clone(); + pdec->mBase = ntype; + return pdec; + } + else if (mType == DT_TYPE_INTEGER) + { + Declaration* pdec = this->Clone(); + pdec->mType = DT_CONST_ADDRESS; + pdec->mBase = ntype; + pdec->mSize = 2; + return pdec; + } + else + return this; + } + else if (ntype->mType == DT_TYPE_INTEGER || ntype->mType == DT_TYPE_BOOL || ntype->mType == DT_TYPE_ENUM) + { + if (mType == DT_TYPE_FLOAT) + { + Declaration* pdec = this->Clone(); + pdec->mInteger = int64(mNumber); + pdec->mBase = ntype; + pdec->mSize = ntype->mSize; + return pdec; + } + else + { + Declaration* pdec = this->Clone(); + pdec->mBase = ntype; + pdec->mSize = ntype->mSize; + return pdec; + } + } + else if (ntype->mType == DT_TYPE_FLOAT) + { + if (mType == DT_TYPE_FLOAT) + { + Declaration* pdec = this->Clone(); + pdec->mBase = ntype; + return pdec; + } + else + { + Declaration* pdec = this->Clone(); + pdec->mNumber = float(mInteger); + pdec->mBase = ntype; + pdec->mSize = ntype->mSize; + return pdec; + } + } + else + return this; +} + Declaration* Declaration::BuildPointer(const Location& loc) { Declaration* pdec = new Declaration(loc, DT_TYPE_POINTER); @@ -2263,6 +2335,7 @@ Declaration* TheBoolTypeDeclaration, * TheFloatTypeDeclaration, * TheConstVoidPo Declaration* TheVoidFunctionTypeDeclaration, * TheConstVoidValueDeclaration; Declaration* TheCharPointerTypeDeclaration, * TheConstCharPointerTypeDeclaration; Expression* TheVoidExpression; +Declaration* TheNullptrConstDeclaration, * TheZeroIntegerConstDeclaration, * TheZeroFloatConstDeclaration; void InitDeclarations(void) { @@ -2345,4 +2418,16 @@ void InitDeclarations(void) TheVoidExpression = new Expression(noloc, EX_CONSTANT); TheVoidExpression->mDecType = TheConstVoidTypeDeclaration; TheVoidExpression->mDecValue = TheConstVoidValueDeclaration; + + + TheNullptrConstDeclaration = new Declaration(noloc, DT_CONST_ADDRESS); + TheNullptrConstDeclaration->mBase = TheVoidPointerTypeDeclaration; + TheNullptrConstDeclaration->mSize = 2; + TheZeroIntegerConstDeclaration = new Declaration(noloc, DT_CONST_INTEGER); + TheZeroIntegerConstDeclaration->mBase = TheSignedIntTypeDeclaration; + TheZeroIntegerConstDeclaration->mSize = 2; + TheZeroFloatConstDeclaration = new Declaration(noloc, DT_CONST_FLOAT); + TheZeroFloatConstDeclaration->mBase = TheFloatTypeDeclaration; + TheZeroFloatConstDeclaration->mSize = 4; + } diff --git a/oscar64/Declaration.h b/oscar64/Declaration.h index 17bb110..1c8ab25 100644 --- a/oscar64/Declaration.h +++ b/oscar64/Declaration.h @@ -116,6 +116,7 @@ static const uint64 DTF_VAR_ADDRESS = (1ULL << 46); static const uint64 DTF_FUNC_THIS = (1ULL << 47); static const uint64 DTF_VAR_ALIASING = (1ULL << 48); +static const uint64 DTF_FPARAM_UNUSED = (1ULL << 49); class Declaration; @@ -196,6 +197,7 @@ enum ExpressionType EX_VCALL, EX_DISPATCH, EX_LIST, + EX_COMMA, EX_RETURN, EX_SEQUENCE, EX_WHILE, @@ -267,13 +269,13 @@ public: Declaration * mVectorConstructor, * mVectorDestructor, * mVectorCopyConstructor, * mVectorCopyAssignment; Declaration * mVTable, * mClass, * mTemplate; - Expression* mValue; + Expression* mValue, * mReturn; DeclarationScope* mScope; int mOffset, mSize, mVarIndex, mNumVars, mComplexity, mLocalSize, mAlignment, mFastCallBase, mFastCallSize, mStride, mStripe; uint8 mShift, mBits; int64 mInteger, mMinValue, mMaxValue; double mNumber; - uint64 mFlags, mCompilerOptions; + uint64 mFlags, mCompilerOptions, mOptFlags; const Ident * mIdent, * mQualIdent, * mMangleIdent; LinkerSection * mSection; const uint8 * mData; @@ -322,6 +324,7 @@ public: Declaration* NonRefBase(void); Declaration* BuildArrayPointer(void); Declaration* DeduceAuto(Declaration* dec); + Declaration* ConstCast(Declaration* ntype); bool IsAuto(void) const; DecType ValueType(void) const; @@ -343,5 +346,6 @@ extern Declaration* TheVoidTypeDeclaration, * TheConstVoidTypeDeclaration, * The extern Declaration* TheBoolTypeDeclaration, * TheFloatTypeDeclaration, * TheVoidPointerTypeDeclaration, * TheConstVoidPointerTypeDeclaration, * TheSignedLongTypeDeclaration, * TheUnsignedLongTypeDeclaration; extern Declaration* TheVoidFunctionTypeDeclaration, * TheConstVoidValueDeclaration; extern Declaration* TheCharPointerTypeDeclaration, * TheConstCharPointerTypeDeclaration; +extern Declaration* TheNullptrConstDeclaration, * TheZeroIntegerConstDeclaration, * TheZeroFloatConstDeclaration; extern Expression* TheVoidExpression; diff --git a/oscar64/GlobalAnalyzer.cpp b/oscar64/GlobalAnalyzer.cpp index 1d50024..bc2b778 100644 --- a/oscar64/GlobalAnalyzer.cpp +++ b/oscar64/GlobalAnalyzer.cpp @@ -219,6 +219,8 @@ void GlobalAnalyzer::AutoInline(void) { if (pdec->mFlags & DTF_FPARAM_CONST) { + pdec->mFlags |= DTF_FPARAM_UNUSED; + pdec->mVarIndex = dec->mNumVars++; Expression* aexp = new Expression(pdec->mLocation, EX_INITIALIZATION); @@ -897,6 +899,7 @@ Declaration * GlobalAnalyzer::Analyze(Expression* exp, Declaration* procDec, boo } break; case EX_LIST: + case EX_COMMA: RegisterProc(Analyze(exp->mLeft, procDec, false)); return Analyze(exp->mRight, procDec, false); case EX_RETURN: diff --git a/oscar64/GlobalOptimizer.cpp b/oscar64/GlobalOptimizer.cpp new file mode 100644 index 0000000..a3b20a5 --- /dev/null +++ b/oscar64/GlobalOptimizer.cpp @@ -0,0 +1,925 @@ +#include "GlobalOptimizer.h" +#include "Constexpr.h" + + +#define DUMP_OPTS 0 + + +static const uint64 OPTF_ANALYZED = (1ULL << 0); +static const uint64 OPTF_ANALYZING = (1ULL << 1); +static const uint64 OPTF_RECURSIVE = (1ULL << 2); +static const uint64 OPTF_FUNC_VARIABLE = (1ULL << 3); +static const uint64 OPTF_CALLED = (1ULL << 4); +static const uint64 OPTF_CALLING = (1ULL << 5); +static const uint64 OPTF_VAR_MODIFIED = (1ULL << 6); +static const uint64 OPTF_VAR_ADDRESS = (1ULL << 7); +static const uint64 OPTF_VAR_USED = (1ULL << 8); +static const uint64 OPTF_VAR_CONST = (1ULL << 9); +static const uint64 OPTF_VAR_NOCONST = (1ULL << 10); +static const uint64 OPTF_SINGLE_RETURN = (1ULL << 11); +static const uint64 OPTF_MULTI_RETURN = (1ULL << 12); + + +GlobalOptimizer::GlobalOptimizer(Errors* errors, Linker* linker) + : mErrors(errors), mLinker(linker) +{ + +} + +GlobalOptimizer::~GlobalOptimizer(void) +{ + +} + +void GlobalOptimizer::Reset(void) +{ + for (int i = 0; i < mFunctions.Size(); i++) + { + Declaration* func = mFunctions[i]; + Declaration* ftype = func->mBase; + func->mOptFlags = 0; + ftype->mOptFlags = 0; + func->mReturn = nullptr; + + Declaration* pdec = ftype->mParams; + while (pdec) + { + pdec->mOptFlags = 0; + pdec = pdec->mNext; + } + } + + for (int i = 0; i < mGlobalVariables.Size(); i++) + { + mGlobalVariables[i]->mOptFlags = 0; + } + + mFunctions.SetSize(0); + mGlobalVariables.SetSize(0); + mCalledFunctions.SetSize(0); + mCalledFunctions.SetSize(0); +} + +void GlobalOptimizer::PropagateParamCommas(Expression*& fexp, Expression*& exp) +{ + PropagateCommas(exp); + if (exp->mType == EX_COMMA) + { + Expression* cexp = exp; + exp = cexp->mRight; + cexp->mRight = fexp; + fexp = cexp; + fexp->mDecType = cexp->mRight->mDecType; + } +} + +void GlobalOptimizer::PropagateCommas(Expression*& exp) +{ + if (exp->mType == EX_PREFIX && exp->mLeft->mType == EX_COMMA) + { + Expression* cexp = exp->mLeft; + exp->mLeft = cexp->mRight; + cexp->mDecType = exp->mDecType; + cexp->mRight = exp->ConstantFold(mErrors, nullptr); + exp = cexp; + } + else if (exp->mType == EX_CALL) + { + Expression* pexp = exp; + Expression* rexp = pexp->mRight; + if (rexp) + { + while (rexp && rexp->mType == EX_LIST) + { + PropagateParamCommas(exp, rexp->mLeft); + if (rexp->mLeft-> mType == EX_COMMA) + pexp = rexp; + rexp = rexp->mRight; + } + if (rexp) + PropagateParamCommas(exp, pexp->mRight); + } + } + else + { + if (exp->mLeft) + PropagateCommas(exp->mLeft); + if (exp->mRight) + PropagateCommas(exp->mRight); + } + +} + +bool GlobalOptimizer::CheckConstReturns(Expression*& exp) +{ + bool changed = false; + + if (exp->mType == EX_CALL && exp->mDecType && exp->mDecType->mType != DT_TYPE_VOID) + { + if (exp->mLeft->mType == EX_CONSTANT) + { + Declaration* pcall = exp->mLeft->mDecValue; + if ((pcall->mOptFlags & OPTF_SINGLE_RETURN) && pcall->mReturn->mLeft) + { + Expression * rexp = pcall->mReturn->mLeft; + if (rexp->mType == EX_CONSTANT) + { +#if DUMP_OPTS + printf("Extract const return\n"); +#endif + + Expression* lexp = new Expression(exp->mLocation, EX_COMMA); + lexp->mLeft = exp; + lexp->mRight = new Expression(pcall->mReturn->mLocation, EX_CONSTANT); + lexp->mRight->mDecValue = pcall->mReturn->mLeft->mDecValue; + lexp->mRight->mDecType = exp->mDecType; + lexp->mDecType = exp->mDecType; + exp->mDecType = TheVoidTypeDeclaration; + exp = lexp; + return true; + } + else if (rexp->mType == EX_PREFIX && rexp->mToken == TK_MUL && rexp->mLeft->mType == EX_VARIABLE && rexp->mLeft->mDecValue->mType == DT_ARGUMENT) + { + Declaration* pdec = rexp->mLeft->mDecValue; + if (pdec->mVarIndex == 0 && !(pdec->mOptFlags & OPTF_VAR_ADDRESS)) + { + Expression* pex = exp->mRight; + if (pex->mType == EX_LIST) + pex = pex->mLeft; + if (pex->mType == EX_CONSTANT) + { +#if DUMP_OPTS + printf("Forward this pointer\n"); +#endif + Expression* lexp = new Expression(exp->mLocation, EX_COMMA); + lexp->mLeft = exp; + lexp->mRight = new Expression(pcall->mReturn->mLocation, EX_PREFIX); + lexp->mRight->mToken = TK_MUL; + lexp->mRight->mLeft = pex; + lexp->mRight->mDecType = pcall->mBase->mBase; + exp->mDecType = TheVoidTypeDeclaration; + exp = lexp; + return true; + } + } + } + } + } + } + + if (exp->mLeft && CheckConstReturns(exp->mLeft)) + changed = true; + if (exp->mRight && CheckConstReturns(exp->mRight)) + changed = true; + + return changed; +} + +bool GlobalOptimizer::CheckUnusedLocals(Expression*& exp) +{ + bool changed = false; + + if (exp->mType == EX_INITIALIZATION) + { + Expression* vexp = exp->mLeft; + if (vexp->mType == EX_VARIABLE) + { + Declaration* vdec = vexp->mDecValue; + if (vdec->mType == DT_VARIABLE && !(vdec->mFlags & (DTF_GLOBAL | DTF_STATIC)) && !(vdec->mOptFlags & OPTF_VAR_USED)) + { + exp = exp->mRight; + return true; + } + } + } + + if (exp->mLeft && CheckUnusedLocals(exp->mLeft)) + changed = true; + if (exp->mRight && CheckUnusedLocals(exp->mRight)) + changed = true; + + return changed; +} + +void GlobalOptimizer::RemoveValueReturn(Expression* exp) +{ + if (exp->mType == EX_RETURN && exp->mLeft) + { + if (exp->mLeft->HasSideEffects()) + { + exp->mType = EX_SEQUENCE; + exp->mRight = new Expression(exp->mLocation, EX_RETURN); + } + else + exp->mLeft = nullptr; + } + + if (exp->mLeft) + RemoveValueReturn(exp->mLeft); + if (exp->mRight) + RemoveValueReturn(exp->mRight); +} + +void GlobalOptimizer::UndoParamReference(Expression* exp, Declaration* param) +{ + if (exp) + { + if (exp->mType == EX_VARIABLE && exp->mDecValue == param) + { + exp->mDecType = param->mBase; + } + + UndoParamReference(exp->mLeft, param); + UndoParamReference(exp->mRight, param); + } +} + +bool GlobalOptimizer::ReplaceParamConst(Expression* exp, Declaration* param) +{ + bool changed = false; + if (exp) + { + if (exp->mType == EX_VARIABLE && exp->mDecValue == param) + { + exp->mType = EX_CONSTANT; + exp->mDecType = param->mBase; + exp->mDecValue = param->mValue->mDecValue->ConstCast(param->mBase); + changed = true; + } + + if (ReplaceParamConst(exp->mLeft, param)) + changed = true; + if (ReplaceParamConst(exp->mRight, param)) + changed = true; + } + return changed; +} + +bool GlobalOptimizer::Optimize(void) +{ + bool changed = false; + +#if DUMP_OPTS + printf("OPT---\n"); +#endif + for (int i = 0; i < mFunctions.Size(); i++) + { + Declaration* func = mFunctions[i]; + Declaration* ftype = func->mBase; + + if (func->mValue && func->mValue->mType != EX_DISPATCH) + { +#if DUMP_OPTS + printf("%s %08llx\n", mFunctions[i]->mQualIdent->mString, mFunctions[i]->mOptFlags); +#endif + if (CheckUnusedLocals(func->mValue)) + changed = true; + + if (CheckConstReturns(func->mValue)) + changed = true; + + if (!(func->mOptFlags & OPTF_FUNC_VARIABLE) && !(func->mBase->mFlags & DTF_VIRTUAL)) + { + if (!(func->mOptFlags & OPTF_VAR_USED) && func->mBase->mBase && (func->mBase->mBase->IsSimpleType() || func->mBase->mBase->IsReference())) + { +#if DUMP_OPTS + printf("Remove return value\n"); +#endif + RemoveValueReturn(func->mValue); + func->mBase->mBase = TheVoidTypeDeclaration; + changed = true; + } + else if (!(func->mOptFlags & OPTF_VAR_ADDRESS) && func->mBase->mBase && func->mBase->mBase->IsReference() && func->mBase->mBase->mBase->IsSimpleType()) + { +#if DUMP_OPTS + printf("Demote reference return\n"); +#endif + func->mBase->mBase = func->mBase->mBase->mBase; + changed = true; + } + + + Declaration* pdec = ftype->mParams; + int vi = 0; + while (pdec) + { + pdec->mVarIndex += vi; + if (!(pdec->mOptFlags & OPTF_VAR_USED) && !(pdec->mFlags & DTF_FPARAM_UNUSED)) + { + if (!pdec->mBase->IsReference() || !(pdec->mOptFlags & OPTF_VAR_ADDRESS)) + { +#if DUMP_OPTS + printf("Unused parameter %s\n", pdec->mIdent ? pdec->mIdent->mString : "_"); +#endif + vi -= pdec->mSize; + + pdec->mFlags |= DTF_FPARAM_UNUSED; + changed = true; + } + } + else if (!(pdec->mOptFlags & OPTF_VAR_ADDRESS) && pdec->mBase->IsReference() && pdec->mBase->mBase->IsSimpleType()) + { +#if DUMP_OPTS + printf("Reference parameter %s to value\n", pdec->mIdent ? pdec->mIdent->mString : "_"); +#endif + vi += pdec->mSize - 2; + + pdec->mBase = pdec->mBase->mBase; + pdec->mSize = pdec->mBase->mSize; + + UndoParamReference(func->mValue, pdec); + + changed = true; + } + else if ((pdec->mOptFlags & OPTF_VAR_CONST) && !(pdec->mOptFlags & OPTF_VAR_ADDRESS)) + { + if (ReplaceParamConst(func->mValue, pdec)) + { +#if DUMP_OPTS + printf("Const parameter %s\n", pdec->mIdent ? pdec->mIdent->mString : "_"); +#endif + changed = true; + } + } + + pdec->mOptFlags = 0; + pdec = pdec->mNext; + } + } + + PropagateCommas(func->mValue); + } + else if (func->mValue && func->mValue->mType == EX_DISPATCH) + { + if (!(func->mOptFlags & OPTF_FUNC_VARIABLE)) + { + Declaration* pdec = ftype->mParams; + while (pdec) + { + if ((pdec->mOptFlags & OPTF_VAR_CONST) && !(pdec->mOptFlags & OPTF_VAR_ADDRESS)) + { + if (ReplaceParamConst(func->mValue, pdec)) + { +#if DUMP_OPTS + printf("Const parameter %s\n", pdec->mIdent ? pdec->mIdent->mString : "_"); +#endif + changed = true; + } + } + + pdec->mOptFlags = 0; + pdec = pdec->mNext; + } + } + } + } + + return changed; +} + +void GlobalOptimizer::AnalyzeProcedure(Expression* exp, Declaration* procDec) +{ + if (procDec->mOptFlags & OPTF_ANALYZING) + { + procDec->mOptFlags |= OPTF_RECURSIVE; + } + else if (!(procDec->mOptFlags & OPTF_ANALYZED)) + { + procDec->mOptFlags |= OPTF_ANALYZING | OPTF_ANALYZED; + + mFunctions.Push(procDec); + + if ((procDec->mFlags & DTF_INTRINSIC) && !procDec->mValue) + ; + else if (procDec->mFlags & DTF_DEFINED) + { + Analyze(exp, procDec, false); + } + else + mErrors->Error(procDec->mLocation, EERR_UNDEFINED_OBJECT, "Calling undefined function", procDec->mQualIdent); + + procDec->mOptFlags &= ~OPTF_ANALYZING; + } +} + +void GlobalOptimizer::AnalyzeAssembler(Expression* exp, Declaration* procDec) +{ + while (exp) + { + 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); + else + adec->mBase->mOptFlags |= OPTF_VAR_USED | OPTF_VAR_ADDRESS; + } + else if (adec->mType == DT_LABEL) + { + + } + else if (adec->mType == DT_VARIABLE) + { + if (adec->mFlags & DTF_GLOBAL) + AnalyzeGlobalVariable(adec); + else + adec->mOptFlags |= OPTF_VAR_USED | OPTF_VAR_ADDRESS; + } + else if (adec->mType == DT_ARGUMENT) + { + adec->mOptFlags |= OPTF_VAR_USED; + } + else if (adec->mType == DT_FUNCTION_REF) + { + adec->mBase->mOptFlags |= OPTF_VAR_USED; + AnalyzeProcedure(adec->mBase->mValue, adec->mBase); + RegisterProc(adec->mBase); + } + else if (adec->mType == DT_CONST_FUNCTION) + { + adec->mOptFlags |= OPTF_VAR_USED; + AnalyzeProcedure(adec->mValue, adec); + RegisterCall(procDec, adec); + } + } + + exp = exp->mRight; + } + +} + +void GlobalOptimizer::AnalyzeGlobalVariable(Declaration* dec) +{ + while (dec->mType == DT_VARIABLE_REF) + dec = dec->mBase; + + if (!(dec->mOptFlags & OPTF_ANALYZED)) + { + dec->mOptFlags |= OPTF_ANALYZED; + + if (dec->mValue && dec->mValue->mType == EX_CONSTRUCT) + { + if (dec->mValue->mLeft->mLeft->mType == EX_CALL && (dec->mValue->mLeft->mLeft->mLeft->mDecValue->mFlags & DTF_CONSTEXPR)) + { + ConstexprInterpreter cinter(dec->mLocation, mErrors, dec->mSection); + dec->mValue = cinter.EvalConstructor(dec->mValue->mLeft->mLeft); + } + } + + mGlobalVariables.Push(dec); + + if (dec->mValue) + { + Analyze(dec->mValue, dec, false); + } + } +} + +void GlobalOptimizer::AnalyzeInit(Declaration* mdec) +{ + while (mdec) + { + if (mdec->mValue) + RegisterProc(Analyze(mdec->mValue, mdec, false)); + else if (mdec->mParams) + AnalyzeInit(mdec->mParams); + mdec = mdec->mNext; + } +} + + +void GlobalOptimizer::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->mOptFlags & OPTF_CALLED)) + { + to->mOptFlags |= OPTF_CALLED; + mCalledFunctions.Push(to); + } + + if (!(from->mOptFlags & OPTF_CALLING)) + { + from->mOptFlags |= OPTF_CALLING; + mCallingFunctions.Push(from); + } + } + else if (to->mType == DT_TYPE_FUNCTION) + { + if (!(from->mOptFlags & OPTF_CALLING)) + { + from->mOptFlags |= OPTF_CALLING; + mCallingFunctions.Push(from); + } + } + else if (to->mType == DT_TYPE_POINTER && to->mBase->mType == DT_TYPE_FUNCTION) + { + if (!(from->mOptFlags & OPTF_CALLING)) + { + from->mOptFlags |= OPTF_CALLING; + mCallingFunctions.Push(from); + } + } + } +} + +void GlobalOptimizer::RegisterProc(Declaration* to) +{ + if (to->mType == DT_CONST_FUNCTION) + { + if (to->mBase->mFlags & DTF_VIRTUAL) + { + + } + else if (!(to->mOptFlags & OPTF_FUNC_VARIABLE)) + { + to->mOptFlags |= OPTF_FUNC_VARIABLE; + mVariableFunctions.Push(to); + } + } +} + +static const uint32 ANAFL_LHS = (1U << 0); +static const uint32 ANAFL_RHS = (1U << 1); + +Declaration* GlobalOptimizer::Analyze(Expression* exp, Declaration* procDec, uint32 flags) +{ + Declaration* ldec, * rdec; + + switch (exp->mType) + { + case EX_ERROR: + case EX_VOID: + break; + case EX_CONSTANT: + if (exp->mDecValue->mType == DT_CONST_FUNCTION) + { + AnalyzeProcedure(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_RHS); + RegisterProc(ldec); + } + else if (exp->mDecValue->mType == DT_CONST_ADDRESS) + { + } + else if (exp->mDecValue->mType == DT_CONST_ASSEMBLER) + { + AnalyzeAssembler(exp->mDecValue->mValue, procDec); + } + + return exp->mDecValue; + case EX_VARIABLE: + 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; + + AnalyzeGlobalVariable(exp->mDecValue); + } + else + { + if (flags & ANAFL_RHS) + exp->mDecValue->mOptFlags |= OPTF_VAR_USED; + if (flags & ANAFL_LHS) + exp->mDecValue->mOptFlags |= OPTF_VAR_ADDRESS; + } + return exp->mDecValue; + case EX_INITIALIZATION: + case EX_ASSIGNMENT: + ldec = Analyze(exp->mLeft, procDec, ANAFL_LHS | flags); + rdec = Analyze(exp->mRight, procDec, ANAFL_RHS); + RegisterProc(rdec); + return ldec; + + case EX_BINARY: + ldec = Analyze(exp->mLeft, procDec, flags); + rdec = Analyze(exp->mRight, procDec, flags); + return ldec; + + case EX_RELATIONAL: + ldec = Analyze(exp->mLeft, procDec, flags); + rdec = Analyze(exp->mRight, procDec, flags); + return TheBoolTypeDeclaration; + + case EX_PREINCDEC: + 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_RHS); + else if (exp->mToken == TK_MUL) + { + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + return exp->mDecType; + } + else + return Analyze(exp->mLeft, procDec, flags); + break; + case EX_POSTFIX: + break; + case EX_POSTINCDEC: + return Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS); + case EX_INDEX: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + if (ldec->mType == DT_VARIABLE || ldec->mType == DT_ARGUMENT) + ldec = ldec->mBase; + rdec = Analyze(exp->mRight, procDec, ANAFL_RHS); + if (ldec->mBase) + return ldec->mBase; + break; + case EX_QUALIFY: + Analyze(exp->mLeft, procDec, flags); + return exp->mDecValue->mBase; + case EX_DISPATCH: + Analyze(exp->mLeft, procDec, flags); + break; + case EX_VCALL: + exp->mType = EX_CALL; + exp->mLeft->mDecValue = exp->mLeft->mDecValue->mVTable; + // intentional fall through + case EX_CALL: + case EX_INLINE: + if (exp->mLeft->mType == EX_CONSTANT) + { + if (flags & ANAFL_RHS) + exp->mLeft->mDecValue->mOptFlags |= OPTF_VAR_USED; + if (flags & ANAFL_LHS) + exp->mLeft->mDecValue->mOptFlags |= OPTF_VAR_ADDRESS; + } + + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + if ((ldec->mFlags & DTF_INTRINSIC) && !ldec->mValue) + { + + } + else + { + RegisterCall(procDec, ldec); + } + + if (exp->mRight) + { + // Check for struct to struct forwarding + + Expression* rex = exp->mRight; + if (rex) + { + Expression* prex = exp; + while (rex && rex->mType == EX_LIST) + { + rex->mLeft = rex->mLeft->ConstantFold(mErrors, nullptr); + prex = rex; + rex = rex->mRight; + } + if (rex) + prex->mRight = rex->ConstantFold(mErrors, nullptr); + rex = exp->mRight; + } + + Declaration* pdec = ldec->mBase->mParams; + while (rex) + { + Expression* pex = rex->mType == EX_LIST ? rex->mLeft : rex; + + if (pdec) + { + if ((pdec->mFlags & DTF_FPARAM_UNUSED) && !pex->HasSideEffects() && pex->mType != EX_CONSTANT) + { + if (pdec->mBase->IsSimpleType()) + { + pex->mType = EX_CONSTANT; + pex->mLeft = nullptr; + pex->mRight = nullptr; + switch (pex->mDecType->mType) + { + case DT_TYPE_INTEGER: + case DT_TYPE_ENUM: + case DT_TYPE_BOOL: + pex->mDecValue = TheZeroIntegerConstDeclaration; + break; + case DT_TYPE_FLOAT: + pex->mDecValue = TheZeroFloatConstDeclaration; + break; + case DT_TYPE_POINTER: + pex->mDecValue = TheNullptrConstDeclaration; + break; + } + } + } + if (!(pdec->mFlags & DTF_FPARAM_UNUSED) && !(pdec->mOptFlags & OPTF_VAR_NOCONST)) + { + if (pex->mType == EX_CONSTANT && pdec->mBase->IsSimpleType()) + { + if (pdec->mOptFlags & OPTF_VAR_CONST) + { + if (!pex->mDecValue->IsSameValue(pdec->mValue->mDecValue)) + { + pdec->mOptFlags |= OPTF_VAR_NOCONST; + pdec->mOptFlags &= ~OPTF_VAR_CONST; + } + } + else + { + pdec->mValue = pex; + pdec->mOptFlags |= OPTF_VAR_CONST; + } + } + else + { + pdec->mOptFlags |= OPTF_VAR_NOCONST; + pdec->mOptFlags &= ~OPTF_VAR_CONST; + } + } + + if (pdec->mBase->mType == DT_TYPE_STRUCT && pdec->mBase->mCopyConstructor) + { + if (pdec->mBase->mMoveConstructor) + { + AnalyzeProcedure(pdec->mBase->mMoveConstructor->mValue, pdec->mBase->mMoveConstructor); + RegisterCall(procDec, pdec->mBase->mMoveConstructor); + } + AnalyzeProcedure(pdec->mBase->mCopyConstructor->mValue, pdec->mBase->mCopyConstructor); + RegisterCall(procDec, pdec->mBase->mCopyConstructor); + } + } + + if (pdec && (pdec->mFlags & DTF_FPARAM_UNUSED)) + RegisterProc(Analyze(pex, procDec, 0)); + else if (pdec && pdec->mBase->IsReference()) + RegisterProc(Analyze(pex, procDec, ANAFL_LHS | ANAFL_RHS)); + else + RegisterProc(Analyze(pex, procDec, ANAFL_RHS)); + + 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)); + Declaration* dec = Analyze(exp->mRight, procDec, flags); + RegisterProc(dec); + return dec; + } + case EX_RETURN: + if (exp->mLeft) + { + exp->mLeft = exp->mLeft->ConstantFold(mErrors, nullptr); + + if (procDec->mOptFlags & OPTF_SINGLE_RETURN) + { + procDec->mOptFlags &= ~OPTF_SINGLE_RETURN; + procDec->mOptFlags |= OPTF_MULTI_RETURN; + } + else if (!(procDec->mOptFlags & OPTF_MULTI_RETURN)) + { + procDec->mOptFlags |= OPTF_SINGLE_RETURN; + procDec->mReturn = exp; + } + + if (procDec->mBase->mBase->IsReference()) + RegisterProc(Analyze(exp->mLeft, procDec, ANAFL_LHS | ANAFL_RHS)); + else + RegisterProc(Analyze(exp->mLeft, procDec, ANAFL_RHS)); + + if (procDec->mBase->mBase && procDec->mBase->mBase->mType == DT_TYPE_STRUCT && procDec->mBase->mBase->mCopyConstructor) + { + if (procDec->mBase->mBase->mMoveConstructor) + { + AnalyzeProcedure(procDec->mBase->mBase->mMoveConstructor->mValue, procDec->mBase->mBase->mMoveConstructor); + RegisterCall(procDec, procDec->mBase->mBase->mMoveConstructor); + } + AnalyzeProcedure(procDec->mBase->mBase->mCopyConstructor->mValue, procDec->mBase->mBase->mCopyConstructor); + RegisterCall(procDec, procDec->mBase->mBase->mCopyConstructor); + } + } + 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, ANAFL_RHS); + break; + + case EX_CLEANUP: + Analyze(exp->mRight, procDec, 0); + return Analyze(exp->mLeft, procDec, flags); + + case EX_WHILE: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + rdec = Analyze(exp->mRight, procDec, 0); + break; + case EX_IF: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + 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: + if (exp->mLeft->mRight) + ldec = Analyze(exp->mLeft->mRight, procDec, 0); + if (exp->mLeft->mLeft->mLeft) + ldec = Analyze(exp->mLeft->mLeft->mLeft, procDec, ANAFL_RHS); + rdec = Analyze(exp->mRight, procDec, 0); + if (exp->mLeft->mLeft->mRight) + ldec = Analyze(exp->mLeft->mLeft->mRight, procDec, 0); + break; + case EX_DO: + ldec = Analyze(exp->mLeft, procDec, 0); + rdec = Analyze(exp->mRight, procDec, ANAFL_RHS); + break; + case EX_BREAK: + case EX_CONTINUE: + case EX_ASSUME: + break; + case EX_TYPE: + break; + case EX_TYPECAST: + return Analyze(exp->mLeft, procDec, ANAFL_RHS); + break; + case EX_LOGICAL_AND: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + rdec = Analyze(exp->mRight, procDec, ANAFL_RHS); + break; + case EX_LOGICAL_OR: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + rdec = Analyze(exp->mRight, procDec, ANAFL_RHS); + break; + case EX_LOGICAL_NOT: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + break; + case EX_ASSEMBLER: + AnalyzeAssembler(exp, procDec); + break; + case EX_UNDEFINED: + break; + case EX_SWITCH: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + exp = exp->mRight; + while (exp) + { + 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: + ldec = Analyze(exp->mLeft, procDec, ANAFL_RHS); + RegisterProc(Analyze(exp->mRight->mLeft, procDec, flags)); + RegisterProc(Analyze(exp->mRight->mRight, procDec, flags)); + break; + } + + return TheVoidTypeDeclaration; +} diff --git a/oscar64/GlobalOptimizer.h b/oscar64/GlobalOptimizer.h new file mode 100644 index 0000000..c03a1bc --- /dev/null +++ b/oscar64/GlobalOptimizer.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Declaration.h" +#include "Linker.h" +#include "CompilerTypes.h" + +class GlobalOptimizer +{ +public: + GlobalOptimizer(Errors* errors, Linker* linker); + ~GlobalOptimizer(void); + + void Reset(void); + bool Optimize(void); + + void AnalyzeProcedure(Expression* exp, Declaration* dec); + void AnalyzeAssembler(Expression* exp, Declaration* dec); + void AnalyzeGlobalVariable(Declaration* dec); + + uint64 mCompilerOptions; + +protected: + Errors* mErrors; + Linker* mLinker; + + ExpandingArray mCalledFunctions, mCallingFunctions, mVariableFunctions, mFunctions; + ExpandingArray mGlobalVariables; + + void AnalyzeInit(Declaration* mdec); + Declaration* Analyze(Expression* exp, Declaration* procDec, uint32 flags); + + void RegisterCall(Declaration* from, Declaration* to); + void RegisterProc(Declaration* to); + + void RemoveValueReturn(Expression* exp); + bool CheckConstReturns(Expression*& exp); + bool CheckUnusedLocals(Expression*& exp); + void UndoParamReference(Expression* exp, Declaration* param); + bool ReplaceParamConst(Expression* exp, Declaration* param); + void PropagateCommas(Expression*& exp); + void PropagateParamCommas(Expression *& fexp, Expression*& exp); + +}; diff --git a/oscar64/InterCode.cpp b/oscar64/InterCode.cpp index 1599ca1..59a1115 100644 --- a/oscar64/InterCode.cpp +++ b/oscar64/InterCode.cpp @@ -4379,7 +4379,12 @@ void InterOperand::Disassemble(FILE* file, InterCodeProcedure* proc) else if (mMemory == IM_GLOBAL) { if (mVarIndex < 0) - vname = ""; + { + if (mLinkerObject && mLinkerObject->mIdent) + vname = mLinkerObject->mIdent->mString; + else + vname = ""; + } else if (!proc->mModule->mGlobalVars[mVarIndex]) vname = "null"; else if (!proc->mModule->mGlobalVars[mVarIndex]->mIdent) @@ -4563,7 +4568,12 @@ void InterInstruction::Disassemble(FILE* file, InterCodeProcedure* proc) else if (mConst.mMemory == IM_GLOBAL) { if (mConst.mVarIndex < 0) - vname = ""; + { + if (mConst.mLinkerObject && mConst.mLinkerObject->mIdent) + vname = mConst.mLinkerObject->mIdent->mString; + else + vname = ""; + } else if (!proc->mModule->mGlobalVars[mConst.mVarIndex]) vname = "null"; else if (!proc->mModule->mGlobalVars[mConst.mVarIndex]->mIdent) @@ -6415,7 +6425,10 @@ bool InterCodeBasicBlock::BuildGlobalIntegerRangeSets(bool initial, const Growin else { for (int i = 0; i < mLocalValueRange.Size(); i++) - mLocalValueRange[i].Merge(range[i], mLoopHead, initial); + { + if (this != from || IsTempModified(i)) + mLocalValueRange[i].Merge(range[i], mLoopHead, initial); + } for (int i = 0; i < mLocalParamValueRange.Size(); i++) mLocalParamValueRange[i].Merge(prange[i], mLoopHead, initial); } @@ -12262,6 +12275,45 @@ static InterInstruction * FindSourceInstruction(InterCodeBasicBlock* block, int } } +bool InterCodeBasicBlock::MoveLoopHeadCheckToTail(void) +{ + bool modified = false; + if (!mVisited) + { + mVisited = true; + + if (mLoopHead && mEntryBlocks.Size() == 2 && mInstructions.Size() == 2) + { + if (mInstructions[0]->mCode == IC_RELATIONAL_OPERATOR && mInstructions[1]->mCode == IC_BRANCH && + mInstructions[1]->mSrc[0].mTemp == mInstructions[0]->mDst.mTemp) + { + if (mFalseJump != this && mTrueJump->mTrueJump == this && !mTrueJump->mFalseJump) + { + mTrueJump->mInstructions.SetSize(mTrueJump->mInstructions.Size() - 1); + mTrueJump->mInstructions.Push(mInstructions[0]->Clone()); + mTrueJump->mInstructions.Push(mInstructions[1]->Clone()); + mTrueJump->mTrueJump = mTrueJump; + mTrueJump->mFalseJump = mFalseJump; + mTrueJump->mEntryBlocks.Push(mTrueJump); + mTrueJump->mNumEntries++; + mFalseJump->mEntryBlocks.Push(mTrueJump); + mFalseJump->mNumEntries++; + mNumEntries--; + mEntryBlocks.Remove(mEntryBlocks.IndexOf(mTrueJump)); + modified = true; + } + } + } + + if (mTrueJump && mTrueJump->MoveLoopHeadCheckToTail()) + modified = true; + if (mFalseJump && mFalseJump->MoveLoopHeadCheckToTail()) + modified = true; + } + + return modified; +} + bool InterCodeBasicBlock::SingleTailLoopOptimization(const NumberSet& aliasedParams, const GrowingVariableArray& staticVars) { bool modified = false; @@ -17606,6 +17658,12 @@ void InterCodeProcedure::Close(void) #endif +#if 1 + ResetVisited(); + mEntryBlock->MoveLoopHeadCheckToTail(); + +#endif + #if 1 BuildLoopPrefix(); DisassembleDebug("added dominators"); @@ -18110,7 +18168,7 @@ void InterCodeProcedure::Close(void) #if 1 case IC_CONSTANT: - if (ins->mConst.mType == IT_POINTER && (ins->mConst.mMemory == IM_FPARAM || ins->mConst.mMemory == IM_PARAM || ins->mConst.mMemory == IM_LOCAL)) + if (ins->mDst.mType == IT_POINTER && (ins->mConst.mMemory == IM_FPARAM || ins->mConst.mMemory == IM_PARAM || ins->mConst.mMemory == IM_LOCAL)) nother++; else nconst++; diff --git a/oscar64/InterCode.h b/oscar64/InterCode.h index 9fcf1eb..8e8378a 100644 --- a/oscar64/InterCode.h +++ b/oscar64/InterCode.h @@ -543,6 +543,7 @@ public: void PeepholeOptimization(const GrowingVariableArray& staticVars); bool PeepholeReplaceOptimization(const GrowingVariableArray& staticVars); + bool MoveLoopHeadCheckToTail(void); void SingleBlockLoopOptimisation(const NumberSet& aliasedParams, const GrowingVariableArray& staticVars); void SingleBlockLoopUnrolling(void); bool SingleBlockLoopPointerSplit(int& spareTemps); diff --git a/oscar64/InterCodeGenerator.cpp b/oscar64/InterCodeGenerator.cpp index 39ca8ee..c20bdbe 100644 --- a/oscar64/InterCodeGenerator.cpp +++ b/oscar64/InterCodeGenerator.cpp @@ -1,4 +1,5 @@ #include "InterCodeGenerator.h" +#include "Constexpr.h" InterCodeGenerator::InterCodeGenerator(Errors* errors, Linker* linker) : mErrors(errors), mLinker(linker), mCompilerOptions(COPT_DEFAULT) @@ -1200,7 +1201,7 @@ InterCodeGenerator::ExValue InterCodeGenerator::TranslateInline(Declaration* pro if (pdec) { - if (!(pdec->mFlags & DTF_FPARAM_CONST)) + if (!(pdec->mFlags & DTF_FPARAM_UNUSED)) nmapper.mParams[pdec->mVarIndex] = nindex; vdec->mVarIndex = nindex; @@ -1317,7 +1318,7 @@ InterCodeGenerator::ExValue InterCodeGenerator::TranslateInline(Declaration* pro else wins->mSrc[1].mOperandSize = 2; - if (!pdec || !(pdec->mFlags & DTF_FPARAM_CONST)) + if (!pdec || !(pdec->mFlags & DTF_FPARAM_UNUSED)) block->Append(wins); } @@ -1444,7 +1445,7 @@ void InterCodeGenerator::CopyStruct(InterCodeProcedure* proc, Expression* exp, I ains->mConst.mMemory = IM_LOCAL; ains->mConst.mVarIndex = nindex; - if (!(pdec->mFlags & DTF_FPARAM_CONST)) + if (!(pdec->mFlags & DTF_FPARAM_UNUSED)) nmapper.mParams[pdec->mVarIndex] = nindex; vdec->mVarIndex = nindex; @@ -1463,7 +1464,7 @@ void InterCodeGenerator::CopyStruct(InterCodeProcedure* proc, Expression* exp, I wins->mSrc[1].mType = IT_POINTER; wins->mSrc[1].mTemp = ains->mDst.mTemp; wins->mSrc[1].mOperandSize = 2; - if (!(pdec->mFlags & DTF_FPARAM_CONST)) + if (!(pdec->mFlags & DTF_FPARAM_UNUSED)) block->Append(wins); pdec = pdec->mNext; @@ -1479,7 +1480,7 @@ void InterCodeGenerator::CopyStruct(InterCodeProcedure* proc, Expression* exp, I ains->mConst.mMemory = IM_LOCAL; ains->mConst.mVarIndex = nindex; - if (!(pdec->mFlags & DTF_FPARAM_CONST)) + if (!(pdec->mFlags & DTF_FPARAM_UNUSED)) nmapper.mParams[pdec->mVarIndex] = nindex; vdec->mVarIndex = nindex; @@ -1498,7 +1499,7 @@ void InterCodeGenerator::CopyStruct(InterCodeProcedure* proc, Expression* exp, I wins->mSrc[1].mType = IT_POINTER; wins->mSrc[1].mTemp = ains->mDst.mTemp; wins->mSrc[1].mOperandSize = 2; - if (!(pdec->mFlags & DTF_FPARAM_CONST)) + if (!(pdec->mFlags & DTF_FPARAM_UNUSED)) block->Append(wins); TranslateExpression(ftype, proc, block, fexp, destack, BranchTarget(), BranchTarget(), &nmapper); @@ -1620,6 +1621,7 @@ InterCodeGenerator::ExValue InterCodeGenerator::TranslateExpression(Declaration* case EX_SEQUENCE: case EX_LIST: + case EX_COMMA: if (exp->mLeft) vr = TranslateExpression(procType, proc, block, exp->mLeft, destack, breakBlock, continueBlock, inlineMapper); exp = exp->mRight; @@ -1948,7 +1950,7 @@ InterCodeGenerator::ExValue InterCodeGenerator::TranslateExpression(Declaration* int ref = 1; if (dec->mType == DT_ARGUMENT) { - if (dec->mFlags & DTF_FPARAM_CONST) + if (dec->mFlags & DTF_FPARAM_UNUSED) { ins->mConst.mMemory = IM_LOCAL; if (inlineMapper) @@ -3543,7 +3545,7 @@ InterCodeGenerator::ExValue InterCodeGenerator::TranslateExpression(Declaration* if (pdec) { - if (!pdec->mBase->CanAssign(vr.mType)) + if (!(pdec->mFlags & DTF_FPARAM_UNUSED) && !pdec->mBase->CanAssign(vr.mType)) { pdec->mBase->CanAssign(vr.mType); mErrors->Error(texp->mLocation, EERR_INCOMPATIBLE_TYPES, "Cannot assign incompatible types"); @@ -3577,7 +3579,7 @@ InterCodeGenerator::ExValue InterCodeGenerator::TranslateExpression(Declaration* else wins->mSrc[1].mOperandSize = 2; - if (!pdec || !(pdec->mFlags & DTF_FPARAM_CONST)) + if (!pdec || !(pdec->mFlags & DTF_FPARAM_UNUSED)) { if (ftype->mFlags & DTF_FASTCALL) defins.Push(wins); @@ -3702,7 +3704,7 @@ InterCodeGenerator::ExValue InterCodeGenerator::TranslateExpression(Declaration* if (vdec->mType == DT_ARGUMENT) { vins->mConst.mVarIndex = vdec->mVarIndex; - if (vdec->mFlags & DTF_FPARAM_CONST) + if (vdec->mFlags & DTF_FPARAM_UNUSED) { vins->mConst.mMemory = IM_LOCAL; if (inlineMapper) @@ -5016,10 +5018,15 @@ void InterCodeGenerator::TranslateLogic(Declaration* procType, InterCodeProcedur default: { ExValue vr = TranslateExpression(procType, proc, block, exp, destack, BranchTarget(), BranchTarget(), inlineMapper); - vr = Dereference(proc, exp, block, vr); - if (!vr.mType->IsSimpleType()) - mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Not a valid condition value"); + if (vr.mType->mType == DT_TYPE_ARRAY) + vr = Dereference(proc, exp, block, vr, 1); + else + { + vr = Dereference(proc, exp, block, vr); + if (!vr.mType->IsSimpleType()) + mErrors->Error(exp->mLocation, EERR_INCOMPATIBLE_TYPES, "Not a valid condition value"); + } InterInstruction* ins = new InterInstruction(exp->mLocation, IC_BRANCH); ins->mSrc[0].mType = InterTypeOf(vr.mType); diff --git a/oscar64/Parser.cpp b/oscar64/Parser.cpp index 313b047..44d06d6 100644 --- a/oscar64/Parser.cpp +++ b/oscar64/Parser.cpp @@ -2,6 +2,7 @@ #include #include "Assembler.h" #include "MachineTypes.h" +#include "Constexpr.h" Parser::Parser(Errors* errors, Scanner* scanner, CompilationUnits* compilationUnits) : mErrors(errors), mScanner(scanner), mCompilationUnits(compilationUnits) @@ -3455,13 +3456,10 @@ Expression* Parser::AddFunctionCallRefReturned(Expression* exp) // A simple constant is passed by const ref if (pex->mDecValue->mType == DT_CONST_INTEGER || pex->mDecValue->mType == DT_CONST_FLOAT || pex->mDecValue->mType == DT_CONST_POINTER || pex->mDecValue->mType == DT_CONST_ADDRESS) { - int nindex = mLocalIndex++; + if (pdec->mType == DT_TYPE_REFERENCE && !(pdec->mBase->mFlags & DTF_CONST)) + mErrors->Error(pex->mLocation, EERR_INCOMPATIBLE_TYPES, "Can't pass constant as non constante reference"); - Declaration* vdec = new Declaration(exp->mLocation, DT_VARIABLE); - - vdec->mVarIndex = nindex; - vdec->mBase = pdec->mBase->mBase; - vdec->mSize = pdec->mBase->mBase->mSize; + Declaration* vdec = AllocTempVar(pdec->mBase->mBase); Expression* vexp = new Expression(pex->mLocation, EX_VARIABLE); vexp->mDecType = pdec->mBase->mBase; @@ -3771,7 +3769,7 @@ Declaration* Parser::ParseDeclaration(Declaration * pdec, bool variable, bool ex cdec->mBase = ctdec; cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); - ctdec->mFlags |= storageFlags & DTF_VIRTUAL; + ctdec->mFlags |= storageFlags & (DTF_REQUEST_INLINE | DTF_CONSTEXPR | DTF_VIRTUAL); cdec->mSection = mCodeSection; cdec->mBase->mFlags |= typeFlags; @@ -3821,7 +3819,7 @@ Declaration* Parser::ParseDeclaration(Declaration * pdec, bool variable, bool ex cdec->mBase = ctdec; cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); - ctdec->mFlags |= storageFlags & DTF_VIRTUAL; + ctdec->mFlags |= storageFlags & (DTF_REQUEST_INLINE | DTF_CONSTEXPR | DTF_VIRTUAL); cdec->mSection = mCodeSection; cdec->mBase->mFlags |= typeFlags; @@ -3866,6 +3864,7 @@ Declaration* Parser::ParseDeclaration(Declaration * pdec, bool variable, bool ex cdec->mBase = ctdec; cdec->mFlags |= cdec->mBase->mFlags & (DTF_CONST | DTF_VOLATILE); + cdec->mFlags |= storageFlags & (DTF_INLINE | DTF_CONSTEXPR); cdec->mSection = mCodeSection; cdec->mBase->mFlags |= typeFlags; @@ -3964,7 +3963,7 @@ Declaration* Parser::ParseDeclaration(Declaration * pdec, bool variable, bool ex if (cdec) { - cdec->mFlags |= storageFlags & DTF_REQUEST_INLINE; + cdec->mFlags |= storageFlags & (DTF_REQUEST_INLINE | DTF_CONSTEXPR); // Initializer list if (mScanner->mToken == TK_COLON) @@ -4042,7 +4041,7 @@ Declaration* Parser::ParseDeclaration(Declaration * pdec, bool variable, bool ex if (bdec->mDestructor) { - bdec->mDestructor->mFlags |= storageFlags & DTF_REQUEST_INLINE; + bdec->mDestructor->mFlags |= storageFlags & (DTF_REQUEST_INLINE | DTF_CONSTEXPR); Declaration* bthis = new Declaration(mScanner->mLocation, DT_TYPE_POINTER); bthis->mFlags |= DTF_CONST | DTF_DEFINED; @@ -4411,7 +4410,7 @@ Declaration* Parser::ParseDeclaration(Declaration * pdec, bool variable, bool ex { ParseVariableInit(ndec); } - else if ((mCompilerOptions & COPT_CPLUSPLUS) && ndec->mType == DT_VARIABLE && !pthis) + else if ((mCompilerOptions & COPT_CPLUSPLUS) && ndec->mType == DT_VARIABLE && !pthis && !(storageFlags & DTF_EXTERN)) { // Find default constructor @@ -4445,6 +4444,9 @@ Declaration* Parser::ParseDeclaration(Declaration * pdec, bool variable, bool ex fexp->mLeft = cexp; fexp->mRight = texp; + if (bdec->mDefaultConstructor->mFlags & DTF_CONSTEXPR) + ndec->mSection = mDataSection; + Expression* dexp = nullptr; if (ndec->mBase->mDestructor) { diff --git a/oscar64/oscar64.cpp b/oscar64/oscar64.cpp index cb0e4db..4e6688d 100644 --- a/oscar64/oscar64.cpp +++ b/oscar64/oscar64.cpp @@ -205,6 +205,8 @@ int main2(int argc, const char** argv) compiler->mCompilerOptions |= COPT_OPTIMIZE_AUTO_ZEROPAGE; else if (arg[2] == 'p') compiler->mCompilerOptions |= COPT_OPTIMIZE_CONST_PARAMS; + else if (arg[2] == 'g') + compiler->mCompilerOptions |= COPT_OPTIMIZE_GLOBAL; } else if (arg[1] == 'e') { @@ -276,6 +278,11 @@ int main2(int argc, const char** argv) compiler->AddDefine(Ident::Unique("OSCAR_NATIVE_ALL"), "1"); } + + // REMOVE ME + // compiler->mCompilerOptions |= COPT_OPTIMIZE_GLOBAL; + // REMOVE ME + char basicStart[10]; strcpy_s(basicStart, "0x0801"); diff --git a/oscar64/oscar64.vcxproj b/oscar64/oscar64.vcxproj index a87fe01..56e143c 100644 --- a/oscar64/oscar64.vcxproj +++ b/oscar64/oscar64.vcxproj @@ -165,6 +165,7 @@ + @@ -192,6 +193,7 @@ + diff --git a/oscar64/oscar64.vcxproj.filters b/oscar64/oscar64.vcxproj.filters index cbde344..98e8470 100644 --- a/oscar64/oscar64.vcxproj.filters +++ b/oscar64/oscar64.vcxproj.filters @@ -81,6 +81,9 @@ Source Files + + Source Files + @@ -158,6 +161,9 @@ Header Files + + Header Files +