diff --git a/oscar64/Assembler.cpp b/oscar64/Assembler.cpp index 2b9fc21..25fc9aa 100644 --- a/oscar64/Assembler.cpp +++ b/oscar64/Assembler.cpp @@ -293,7 +293,7 @@ const char* AsmInstructionNames[NUM_ASM_INS_TYPES] = { "INV", "BYT" }; -int AsmInsModeSize[NUM_ASM_INS_MODES] = { +int AsmInsModeSize[NUM_ASM_INS_MODES_X] = { 1, 2, 2, @@ -306,6 +306,8 @@ int AsmInsModeSize[NUM_ASM_INS_MODES] = { 2, 2, 2, + 0, + 2 }; void InitAssembler(void) diff --git a/oscar64/Assembler.h b/oscar64/Assembler.h index 05c3a80..73fcc8e 100644 --- a/oscar64/Assembler.h +++ b/oscar64/Assembler.h @@ -31,7 +31,9 @@ enum AsmInsMode NUM_ASM_INS_MODES, - ASMIM_IMMEDIATE_ADDRESS + ASMIM_IMMEDIATE_ADDRESS, + + NUM_ASM_INS_MODES_X, }; struct AsmInsData @@ -45,6 +47,8 @@ extern AsmInsData DecInsData[256]; extern short AsmInsOpcodes[NUM_ASM_INS_TYPES][NUM_ASM_INS_MODES]; +extern int AsmInsModeSize[NUM_ASM_INS_MODES_X]; + extern const char* AsmInstructionNames[NUM_ASM_INS_TYPES]; AsmInsType FindAsmInstruction(const char * ins); diff --git a/oscar64/Compiler.cpp b/oscar64/Compiler.cpp index 040f200..1e76451 100644 --- a/oscar64/Compiler.cpp +++ b/oscar64/Compiler.cpp @@ -466,7 +466,7 @@ void Compiler::CompileProcedure(InterCodeProcedure* proc) printf("Generate native code <%s>\n", proc->mIdent->mString); ncproc->Compile(proc); - mNativeProcedures.Push(ncproc); + mNativeCodeGenerator->mProcedures.Push(ncproc); } else { @@ -1036,13 +1036,15 @@ bool Compiler::GenerateCode(void) mCompilationUnits->mSectionStack->mSections.Push(proc->mLinkerObject->mStackSection); } + mNativeCodeGenerator->OutlineFunctions(); + mNativeCodeGenerator->BuildFunctionProxies(); - for (int i = 0; i < mNativeProcedures.Size(); i++) + for (int i = 0; i < mNativeCodeGenerator->mProcedures.Size(); i++) { if (mCompilerOptions & COPT_VERBOSE2) - printf("Assemble native code <%s>\n", mNativeProcedures[i]->mInterProc->mIdent->mString); - mNativeProcedures[i]->Assemble(); + printf("Assemble native code <%s>\n", mNativeCodeGenerator->mProcedures[i]->mInterProc->mIdent->mString); + mNativeCodeGenerator->mProcedures[i]->Assemble(); } LinkerObject* byteCodeObject = nullptr; diff --git a/oscar64/Compiler.h b/oscar64/Compiler.h index 7286e25..2c13f79 100644 --- a/oscar64/Compiler.h +++ b/oscar64/Compiler.h @@ -29,7 +29,6 @@ public: GlobalOptimizer* mGlobalOptimizer; GrowingArray mByteCodeFunctions; - ExpandingArray mNativeProcedures; TargetMachine mTargetMachine; uint64 mCompilerOptions; diff --git a/oscar64/CompilerTypes.h b/oscar64/CompilerTypes.h index 64187b5..8ea2d42 100644 --- a/oscar64/CompilerTypes.h +++ b/oscar64/CompilerTypes.h @@ -14,6 +14,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_OUTLINE = 1ULL << 12; static const uint64 COPT_OPTIMIZE_CODE_SIZE = 1ULL << 16; static const uint64 COPT_NATIVE = 1ULL << 17; @@ -50,7 +51,7 @@ 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_AUTO_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_SIZE = COPT_OPTIMIZE_BASIC | COPT_OPTIMIZE_INLINE | COPT_OPTIMIZE_AUTO_INLINE | COPT_OPTIMIZE_CONST_EXPRESSIONS | COPT_OPTIMIZE_CODE_SIZE | COPT_OPTIMIZE_CONST_PARAMS | COPT_OPTIMIZE_MERGE_CALLS | COPT_OPTIMIZE_GLOBAL;// | COPT_OPTIMIZE_OUTLINE; 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; diff --git a/oscar64/InterCode.cpp b/oscar64/InterCode.cpp index 42dc317..8ed0596 100644 --- a/oscar64/InterCode.cpp +++ b/oscar64/InterCode.cpp @@ -9533,7 +9533,12 @@ void InterCodeBasicBlock::UpdateLocalIntegerRangeSets(const GrowingVariableArray else mTrueValueRange[s1].LimitMinWeak(0); - if (mFalseValueRange[s1].mMinState == IntegerValueRange::S_BOUND && mFalseValueRange[s1].mMinValue >= 0) + if (mInstructions[sz - 2]->mSrc[1].mType == IT_INT8) + { + mFalseValueRange[s1].LimitMin(mInstructions[sz - 2]->mSrc[0].mIntConst); + mFalseValueRange[s1].LimitMax(255); + } + else if (mFalseValueRange[s1].mMinState == IntegerValueRange::S_BOUND && mFalseValueRange[s1].mMinValue >= 0) { mFalseValueRange[s1].LimitMin(mInstructions[sz - 2]->mSrc[0].mIntConst); } @@ -19507,7 +19512,7 @@ void InterCodeBasicBlock::CheckFinalLocal(void) { if (ins->mSrc[j].mTemp >= 0 && !provided[ins->mSrc[j].mTemp]) { - printf("Use of potentially undefined temp %d\n", ins->mSrc[j].mTemp); +// printf("Use of potentially undefined temp %d\n", ins->mSrc[j].mTemp); } } @@ -23211,7 +23216,7 @@ void InterCodeProcedure::Close(void) { GrowingTypeArray tstack(IT_NONE); - CheckFunc = !strcmp(mIdent->mString, "enemies_iterate"); + CheckFunc = !strcmp(mIdent->mString, "main"); CheckCase = false; mEntryBlock = mBlocks[0]; diff --git a/oscar64/NativeCodeGenerator.cpp b/oscar64/NativeCodeGenerator.cpp index 56e5bc8..33506ac 100644 --- a/oscar64/NativeCodeGenerator.cpp +++ b/oscar64/NativeCodeGenerator.cpp @@ -1,5 +1,6 @@ #include "NativeCodeGenerator.h" #include "CompilerTypes.h" +#include "NativeCodeOutliner.h" #define JUMP_TO_BRANCH 1 #define CHECK_NULLPTR 0 @@ -539,6 +540,67 @@ NativeCodeInstruction::NativeCodeInstruction(const InterInstruction* ins, AsmIns } } +const char* NativeCodeInstruction::AddrName(char* buffer) const +{ + if (mLinkerObject) + { + if (mLinkerObject->mIdent) + sprintf_s(buffer, 160, "%s + %d", mLinkerObject->mIdent->mString, mAddress); + else + sprintf_s(buffer, 160, "_lobj%d + %d", mLinkerObject->mID, mAddress); + } + else if (mAddress < 256) + sprintf_s(buffer, 160, "%02x", mAddress); + else + sprintf_s(buffer, 160, "%04x", mAddress); + + return buffer; +} + +void NativeCodeInstruction::Disassemble(FILE* file) const +{ + char buffer[160]; + + switch (mMode) + { + case ASMIM_IMPLIED: + fprintf(file, "%s", AsmInstructionNames[mType]); + break; + case ASMIM_IMMEDIATE: + fprintf(file, "%s #$%02x", AsmInstructionNames[mType], mAddress); + break; + case ASMIM_ZERO_PAGE: + fprintf(file, "%s %s", AsmInstructionNames[mType], AddrName(buffer)); + break; + case ASMIM_ZERO_PAGE_X: + fprintf(file, "%s %s, x", AsmInstructionNames[mType], AddrName(buffer)); + break; + case ASMIM_ZERO_PAGE_Y: + fprintf(file, "%s %s, y", AsmInstructionNames[mType], AddrName(buffer)); + break; + case ASMIM_ABSOLUTE: + fprintf(file, "%s %s", AsmInstructionNames[mType], AddrName(buffer)); + break; + case ASMIM_ABSOLUTE_X: + fprintf(file, "%s %s, x", AsmInstructionNames[mType], AddrName(buffer)); + break; + case ASMIM_ABSOLUTE_Y: + fprintf(file, "%s %s, y", AsmInstructionNames[mType], AddrName(buffer)); + break; + case ASMIM_INDIRECT: + fprintf(file, "%s (%s)", AsmInstructionNames[mType], AddrName(buffer)); + break; + case ASMIM_INDIRECT_X: + fprintf(file, "%s (%s, x)", AsmInstructionNames[mType], AddrName(buffer)); + break; + case ASMIM_INDIRECT_Y: + fprintf(file, "%s (%s), y", AsmInstructionNames[mType], AddrName(buffer)); + break; + case ASMIM_RELATIVE: + fprintf(file, "%s %d", AsmInstructionNames[mType], mAddress); + break; + } +} bool NativeCodeInstruction::IsUsedResultInstructions(NumberSet& requiredTemps) { @@ -4951,6 +5013,28 @@ void NativeCodeInstruction::FilterRegUsage(NumberSet& requiredTemps, NumberSet& } } +uint32 NativeCodeInstruction::CodeHash(void) const +{ + uint32 hash = mType + 0x20 * mMode + 0x100 * mAddress; + if (mLinkerObject) + hash += mLinkerObject->mID * 0x1000; + return hash; +} + +bool NativeCodeInstruction::CodeSame(const NativeCodeInstruction& ins) +{ + if (mType != ins.mType || mMode != ins.mMode) + return false; + if (mMode != ASMIM_IMPLIED && (mAddress != ins.mAddress || mLinkerObject != ins.mLinkerObject)) + return false; + if (mMode == ASMIM_IMMEDIATE_ADDRESS && (mFlags & (NCIF_LOWER | NCIF_UPPER)) != (ins.mFlags & (NCIF_LOWER | NCIF_UPPER))) + return false; + if ((mFlags & NCIF_USE_ZP_32_X) && mParam != ins.mParam) + return false; + + return true; +} + void NativeCodeInstruction::CopyMode(const NativeCodeInstruction& ins) { mMode = ins.mMode; @@ -5019,7 +5103,7 @@ void NativeCodeInstruction::Assemble(NativeCodeBasicBlock* block) } else { - if (mType == ASMIT_JSR && (mFlags & NCIF_USE_ZP_32_X)) + if ((mType == ASMIT_JSR || mType == ASMIT_JMP) && (mFlags & NCIF_USE_ZP_32_X)) { block->PutOpcode(AsmInsOpcodes[ASMIT_LDX][ASMIM_IMMEDIATE]); block->PutByte(mParam); @@ -7818,9 +7902,9 @@ NativeCodeBasicBlock * NativeCodeBasicBlock::CopyValue(InterCodeProcedure* proc, if (sstride > 1 || dstride > 1) msize = 32; - else if (nproc->mInterProc->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) + else if (nproc->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) msize = 8; - else if (nproc->mInterProc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) + else if (nproc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) msize = 2; #if 1 if (ins->mSrc[0].mTemp < 0 && ins->mSrc[1].mTemp < 0) @@ -8314,9 +8398,9 @@ NativeCodeBasicBlock* NativeCodeBasicBlock::FillValue(InterCodeProcedure* proc, if (dstride > 1) msize = 32; - else if (nproc->mInterProc->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) + else if (nproc->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) msize = 8; - else if (nproc->mInterProc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) + else if (nproc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) msize = 2; #if 1 if (ins->mSrc[1].mTemp < 0) @@ -10604,8 +10688,8 @@ NativeCodeBasicBlock* NativeCodeBasicBlock::BinaryOperator(InterCodeProcedure* p int lcost = 8 + 2 * (nbytes - 1); int ucost = shift * (1 + 2 * nbytes); - if ((nproc->mInterProc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) && lcost < ucost || - !(nproc->mInterProc->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) && 2 * lcost < ucost) + if ((nproc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE) && lcost < ucost || + !(nproc->mCompilerOptions & COPT_OPTIMIZE_AUTO_UNROLL) && 2 * lcost < ucost) { mIns.Push(NativeCodeInstruction(ins, ASMIT_LDX, ASMIM_IMMEDIATE, shift)); this->Close(ins, lblock, nullptr, ASMIT_JMP); @@ -14015,6 +14099,35 @@ void NativeCodeBasicBlock::CallFunction(InterCodeProcedure* proc, NativeCodeProc } } + +void NativeCodeBasicBlock::Disassemble(FILE* file) +{ + if (!mVisited) + { + mVisited = true; + + fprintf(file, "L%d:\n", mIndex); + for (int i = 0; i < mIns.Size(); i++) + { + fprintf(file, "%03d ", i); + mIns[i].Disassemble(file); + fprintf(file, "\n"); + } + fprintf(file, "%03d %s", mIns.Size(), AsmInstructionNames[mBranch]); + if (mTrueJump) + { + fprintf(file, " L%d", mTrueJump->mIndex); + if (mFalseJump) + fprintf(file, ", L%d", mFalseJump->mIndex); + } + fprintf(file, "\n"); + + if (mTrueJump) mTrueJump->Disassemble(file); + if (mFalseJump) mFalseJump->Disassemble(file); + } +} + + NativeCodeInstruction NativeCodeBasicBlock::DecodeNative(const InterInstruction* ins, LinkerObject* lobj, int& offset) const { uint8 op = lobj->mData[offset++]; @@ -41554,7 +41667,7 @@ static bool CheckBlockCopySequence(const ExpandingArray& bool NativeCodeBasicBlock::BlockSizeCopyReduction(NativeCodeProcedure* proc, int& si, int& di) { - if ((proc->mInterProc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE)) + if ((proc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE)) { if (si + 1 < mIns.Size() && mIns[si + 0].mType == ASMIT_LDA && (mIns[si + 0].mMode == ASMIM_ZERO_PAGE || mIns[si + 0].mMode == ASMIM_ABSOLUTE) && @@ -51378,6 +51491,32 @@ bool NativeCodeBasicBlock::PeepHoleOptimizer(int pass) return false; } +void NativeCodeBasicBlock::AddToSuffixTree(NativeCodeMapper& mapper, SuffixTree * tree) +{ + if (!mVisited) + { + mVisited = true; + + if (!mSuffixString) + mSuffixString = new int[mIns.Size() + 100]; + + bool rel = false; + for (int i = 0; i < mIns.Size(); i++) + { + if (mIns[i].mMode == ASMIM_RELATIVE) + rel = true; + mSuffixString[i] = mapper.MapInstruction(mIns[i], mProc->mLinkerObject->mSection); + } + mSuffixString[mIns.Size()] = mapper.MapBasicBlock(this); + + if (!rel) + tree->AddString(mSuffixString); + + if (mTrueJump) mTrueJump->AddToSuffixTree(mapper, tree); + if (mFalseJump) mFalseJump->AddToSuffixTree(mapper, tree); + } +} + void NativeCodeBasicBlock::CheckVisited(void) { #if _DEBUG @@ -52063,6 +52202,7 @@ NativeCodeBasicBlock::NativeCodeBasicBlock(NativeCodeProcedure* proc) mDominator = nullptr; mLoopHeadBlock = nullptr; mLoopTailBlock = nullptr; + mSuffixString = nullptr; mEntryRegA = false; mEntryRegX = false; mEntryRegY = false; @@ -52089,6 +52229,22 @@ NativeCodeProcedure::~NativeCodeProcedure(void) } + +void NativeCodeProcedure::Disassemble(FILE* file) +{ + fprintf(file, "--------------------------------------------------------------------\n"); + fprintf(file, "%s: %s:%d\n", mIdent->mString, mLocation.mFileName, mLocation.mLine); + + ResetVisited(); + mEntryBlock->Disassemble(file); +} + +void NativeCodeProcedure::AddToSuffixTree(NativeCodeMapper& mapper, SuffixTree* tree) +{ + ResetVisited(); + mEntryBlock->AddToSuffixTree(mapper, tree); +} + void NativeCodeProcedure::CompressTemporaries(bool singles) { if (mInterProc->mTempSize > 0) @@ -52286,9 +52442,14 @@ void NativeCodeProcedure::LoadTempsFromStack(int tempSave) void NativeCodeProcedure::Compile(InterCodeProcedure* proc) { mInterProc = proc; + mLinkerObject = proc->mLinkerObject; + mIdent = proc->mIdent; + mLocation = proc->mLocation; + mCompilerOptions = proc->mCompilerOptions; + mInterProc->mLinkerObject->mNativeProc = this; - CheckFunc = !strcmp(mInterProc->mIdent->mString, "rirq_sort"); + CheckFunc = !strcmp(mIdent->mString, "rirq_sort"); int nblocks = proc->mBlocks.Size(); tblocks = new NativeCodeBasicBlock * [nblocks]; @@ -52417,14 +52578,14 @@ void NativeCodeProcedure::Compile(InterCodeProcedure* proc) if (mInterProc->mInterrupt) { if (!mNoFrame || mStackExpand > 0 || commonFrameSize > 0) - mGenerator->mErrors->Error(mInterProc->mLocation, ERRR_INTERRUPT_TO_COMPLEX, "Function to complex for interrupt"); + mGenerator->mErrors->Error(mLocation, ERRR_INTERRUPT_TO_COMPLEX, "Function to complex for interrupt"); ZeroPageSet zpLocal, zpGlobal; ResetVisited(); if (mEntryBlock->CollectZeroPageSet(zpLocal, zpGlobal, true)) zpLocal |= zpGlobal; else - mGenerator->mErrors->Error(mInterProc->mLocation, ERRR_INTERRUPT_TO_COMPLEX, "No recursive functions in interrupt"); + mGenerator->mErrors->Error(mLocation, ERRR_INTERRUPT_TO_COMPLEX, "No recursive functions in interrupt"); if (proc->mHardwareInterrupt) { @@ -52830,7 +52991,7 @@ void NativeCodeProcedure::Compile(InterCodeProcedure* proc) } } - if (mInterProc->mCompilerOptions & COPT_OPTIMIZE_MERGE_CALLS) + if (mCompilerOptions & COPT_OPTIMIZE_MERGE_CALLS) { ResetVisited(); mEntryBlock->RegisterFunctionCalls(); @@ -52839,9 +53000,9 @@ void NativeCodeProcedure::Compile(InterCodeProcedure* proc) void NativeCodeProcedure::Assemble(void) { - CheckFunc = !strcmp(mInterProc->mIdent->mString, "fighter_ai"); + CheckFunc = !strcmp(mIdent->mString, "fighter_ai"); - if (mInterProc->mCompilerOptions & COPT_OPTIMIZE_MERGE_CALLS) + if (mCompilerOptions & COPT_OPTIMIZE_MERGE_CALLS) { ResetVisited(); mEntryBlock->MergeFunctionCalls(); @@ -52891,7 +53052,7 @@ void NativeCodeProcedure::Assemble(void) for (int i = 0; i < placement.Size(); i++) placement[i]->mAsmFromJump = -1; - uint8* data = mInterProc->mLinkerObject->AddSpace(total); + uint8* data = mLinkerObject->AddSpace(total); for (int i = 0; i < placement.Size(); i++) { @@ -52905,7 +53066,7 @@ void NativeCodeProcedure::Assemble(void) range.mIdent = Ident::Unique(buffer); range.mOffset = placement[i]->mOffset; range.mSize = placement[i]->mSize; - mInterProc->mLinkerObject->mRanges.Push(range); + mLinkerObject->mRanges.Push(range); placement[i]->CopyCode(this, data); } @@ -52913,10 +53074,10 @@ void NativeCodeProcedure::Assemble(void) for (int i = 0; i < mRelocations.Size(); i++) { LinkerReference& rl(mRelocations[i]); - rl.mObject = mInterProc->mLinkerObject; + rl.mObject = mLinkerObject; if (!rl.mRefObject) - rl.mRefObject = mInterProc->mLinkerObject; - mInterProc->mLinkerObject->AddReference(rl); + rl.mRefObject = mLinkerObject; + mLinkerObject->AddReference(rl); } if (mGenerator->mCompilerOptions & (COPT_DEBUGINFO | COPT_PROFILEINFO)) @@ -52937,7 +53098,7 @@ void NativeCodeProcedure::Assemble(void) } mCodeLocations.SetSize(j + 1); - mInterProc->mLinkerObject->AddLocations(mCodeLocations); + mLinkerObject->AddLocations(mCodeLocations); } } } @@ -53947,7 +54108,7 @@ void NativeCodeProcedure::Optimize(void) #endif #if 1 - if (step == 10 && (mInterProc->mCompilerOptions & COPT_OPTIMIZE_BASIC)) + if (step == 10 && (mCompilerOptions & COPT_OPTIMIZE_BASIC)) { ResetVisited(); mEntryBlock->MarkLocalUsedLinkerObjects(); @@ -54051,7 +54212,7 @@ void NativeCodeProcedure::Optimize(void) if (step == 16) { - if (mInterProc->mCompilerOptions & COPT_OPTIMIZE_INLINE) + if (mCompilerOptions & COPT_OPTIMIZE_INLINE) { ResetVisited(); if (mEntryBlock->SimpleInlineCalls()) @@ -54061,7 +54222,7 @@ void NativeCodeProcedure::Optimize(void) if (step == 17) { - if (!(mInterProc->mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE)) + if (!(mCompilerOptions & COPT_OPTIMIZE_CODE_SIZE)) { ResetVisited(); if (mEntryBlock->Expand16BitLoopBranch()) @@ -54100,7 +54261,7 @@ void NativeCodeProcedure::Optimize(void) if (cnt > 200) { changed = false; - mGenerator->mErrors->Error(mInterProc->mLocation, EWARN_OPTIMIZER_LOCKED, "Optimizer locked in infinite loop", mInterProc->mIdent); + mGenerator->mErrors->Error(mLocation, EWARN_OPTIMIZER_LOCKED, "Optimizer locked in infinite loop", mIdent); } #if 1 @@ -55316,6 +55477,144 @@ bool NativeCodeGenerator::MergeFunctionCall(NativeCodeBasicBlock* block, int at) return false; } +void NativeCodeGenerator::OutlineFunctions(void) +{ + NativeCodeMapper mapper; + + bool progress; + + int k = 0; + + int numOutlines = 0; + do { + progress = false; + + SuffixTree* tree = new SuffixTree(nullptr, 0, nullptr); + + for (int i = 0; i < mProcedures.Size(); i++) + { + if (mProcedures[i]->mCompilerOptions & COPT_OPTIMIZE_OUTLINE) + mProcedures[i]->AddToSuffixTree(mapper, tree); + } + tree->AddParents(nullptr); + +#if 0 + FILE* f; + + if (!fopen_s(&f, "r:\\suffix.txt", "w")) + { + tree->Print(f, mapper, 0); + fclose(f); + } +#endif + + SuffixTree* ltree = nullptr; + int lsize = 6; + + tree->LongestMatch(mapper, 0, 0, lsize, ltree); + if (lsize > 6) + { + SuffixTree* leaf = ltree; + while (leaf->mFirst) + leaf = leaf->mFirst; + NativeCodeBasicBlock* block = mapper.mBlocks[-(1 + leaf->mSeg[leaf->mSize - 1])]; + + NativeCodeProcedure* nproc = new NativeCodeProcedure(this); + + NativeCodeBasicBlock* nblock = nproc->AllocateBlock(); + +// printf("Suffix %s,%d:%s\n", block->mProc->mIdent->mString, block->mIndex, block->mProc->mLinkerObject->mSection->mIdent->mString); + + nproc->mLocation = block->mIns[0].mIns ? block->mIns[0].mIns->mLocation : block->mProc->mLocation; + nproc->mCompilerOptions = block->mProc->mCompilerOptions; + nproc->mIdent = Ident::Unique("$outline", numOutlines); + nproc->mLinkerObject = mLinker->AddObject(nproc->mLocation, nproc->mIdent, block->mProc->mLinkerObject->mSection, LOT_NATIVE_CODE); + nproc->mEntryBlock = nblock; + + bool dojmp = false; + + ltree->ParentCollect(mapper, nblock); + if (nblock->mIns[nblock->mIns.Size() - 1].mType == ASMIT_JSR) + nblock->mIns[nblock->mIns.Size() - 1].mType = ASMIT_JMP; + else if (nblock->mIns[nblock->mIns.Size() - 1].mType == ASMIT_RTS || nblock->mIns[nblock->mIns.Size() - 1].mType == ASMIT_JMP) + dojmp = true; + else + nblock->mIns.Push(NativeCodeInstruction(nblock->mIns[nblock->mIns.Size() - 1].mIns, ASMIT_RTS)); + + ExpandingArray segs; + ltree->ReplaceCalls(mapper, segs); + + segs.Sort([](const SuffixSegment& l, const SuffixSegment& r)->bool { + return l.mBlock == r.mBlock ? l.mStart > r.mStart : ptrdiff_t(l.mBlock) < ptrdiff_t(r.mBlock); + }); + + // Check for complete loop block replacement + int k = 0; + while (k < segs.Size() && segs[k].mStart == 0 && segs[k].mEnd == segs[k].mBlock->mIns.Size() && segs[k].mBlock->mTrueJump == segs[k].mBlock && segs[k].mBlock->mBranch == segs[0].mBlock->mBranch) + k++; + + if (k == segs.Size()) + { + NativeCodeBasicBlock* eblock = nproc->AllocateBlock(); + + nblock->mTrueJump = nblock; + nblock->mFalseJump = eblock; + nblock->mBranch = segs[0].mBlock->mBranch; + + for (int i = 0; i < segs.Size(); i++) + { + SuffixSegment& s(segs[i]); + segs[i].mBlock->mTrueJump = segs[i].mBlock->mFalseJump; + segs[i].mBlock->mFalseJump = nullptr; + segs[i].mBlock->mBranch = ASMIT_JMP; + segs[i].mBlock->mNumEntries = 1; + } + + eblock->mIns.Push(NativeCodeInstruction(nblock->mIns[nblock->mIns.Size() - 1].mIns, ASMIT_RTS)); + if (nblock->mIns[nblock->mIns.Size() - 1].mType == ASMIT_JMP) + nblock->mIns[nblock->mIns.Size() - 1].mType = ASMIT_JSR; + else + nblock->mIns.Remove(nblock->mIns.Size() - 1); + } + + NativeCodeBasicBlock* pblock = nullptr; + int pstart; + for (int i = 0; i < segs.Size(); i++) + { + SuffixSegment& s(segs[i]); +// printf("Seg %s,%d\n", segs[i].mBlock->mProc->mIdent->mString, segs[i].mBlock->mIndex); + if (s.mBlock != pblock || s.mEnd <= pstart) + { + s.mBlock->mIns.Remove(s.mStart + 1, s.mEnd - s.mStart - 1); + s.mBlock->mIns[s.mStart] = NativeCodeInstruction(s.mBlock->mIns[s.mStart].mIns, dojmp ? ASMIT_JMP : ASMIT_JSR, ASMIM_ABSOLUTE, 0, nproc->mLinkerObject); + pblock = s.mBlock; + pstart = s.mStart; + } + } + + mProcedures.Push(nproc); + + numOutlines++; + progress = true; + } +#if 0 + if (!fopen_s(&f, "r:\\lsuffix.txt", "w")) + { + ltree->ParentPrint(f, mapper); + fclose(f); + } +#endif + delete tree; + mapper.Reset(); + +#if 0 + k++; + if (k == 2) + break; +#endif + } while (progress); +} + void NativeCodeGenerator::BuildFunctionProxies(void) { FunctionCall* cp = mFunctionCalls; diff --git a/oscar64/NativeCodeGenerator.h b/oscar64/NativeCodeGenerator.h index a8d8de9..5f09ea9 100644 --- a/oscar64/NativeCodeGenerator.h +++ b/oscar64/NativeCodeGenerator.h @@ -9,6 +9,9 @@ class NativeCodeBasicBlock; class NativeCodeGenerator; class NativeCodeInstruction; +class NativeCodeMapper; +class SuffixTree; + enum NativeRegisterDataMode { NRDM_UNKNOWN, @@ -154,6 +157,9 @@ public: LinkerObject * mLinkerObject; const InterInstruction * mIns; + void Disassemble(FILE* file) const; + void DisassembleAddress(FILE* file) const; + void CopyMode(const NativeCodeInstruction& ins); void Assemble(NativeCodeBasicBlock* block); @@ -216,6 +222,12 @@ public: void BuildCollisionTable(NumberSet& liveTemps, NumberSet* collisionSets); uint32 NeedsLive(void) const; + + uint32 CodeHash(void) const; + bool CodeSame(const NativeCodeInstruction& ins); + +protected: + const char* AddrName(char* buffer) const; }; struct NativeCodeLoadStorePair @@ -264,6 +276,8 @@ public: NativeCodeInstruction mALSIns, mXLSIns, mYLSIns; + void Disassemble(FILE* file); + NativeCodeInstruction DecodeNative(const InterInstruction* ins, LinkerObject * lobj, int& offset) const; int PutBranch(NativeCodeProcedure* proc, NativeCodeBasicBlock* target, AsmInsType code, int from, int to); @@ -817,6 +831,9 @@ public: void CheckBlocks(bool sequence = false); void CheckAsmCode(void); void CheckVisited(void); + + int* mSuffixString; + void AddToSuffixTree(NativeCodeMapper& mapper, SuffixTree * tree); }; class NativeCodeProcedure @@ -831,6 +848,10 @@ class NativeCodeProcedure NativeCodeGenerator* mGenerator; InterCodeProcedure* mInterProc; + LinkerObject* mLinkerObject; + const Ident* mIdent; + Location mLocation; + uint64 mCompilerOptions; int mProgStart, mProgSize, mIndex, mFrameOffset, mStackExpand; int mFastCallBase; @@ -842,10 +863,14 @@ class NativeCodeProcedure ExpandingArray mCodeLocations; + void Disassemble(FILE* file); + void Compile(InterCodeProcedure* proc); void Optimize(void); void Assemble(void); + void AddToSuffixTree(NativeCodeMapper& mapper, SuffixTree* tree); + NativeCodeBasicBlock* CompileBlock(InterCodeProcedure* iproc, InterCodeBasicBlock* block); NativeCodeBasicBlock* AllocateBlock(void); @@ -911,6 +936,8 @@ public: Linker* mLinker; LinkerSection* mRuntimeSection; + ExpandingArray mProcedures; + ExpandingArray mRuntime; ExpandingArray mMulTables; ExpandingArray mFloatTables; @@ -929,6 +956,8 @@ public: FunctionCall* mFunctionCalls; + void OutlineFunctions(void); + void RegisterFunctionCall(NativeCodeBasicBlock* block, int at); void BuildFunctionProxies(void); bool MergeFunctionCall(NativeCodeBasicBlock* block, int at); diff --git a/oscar64/NativeCodeOutliner.cpp b/oscar64/NativeCodeOutliner.cpp new file mode 100644 index 0000000..0e8de71 --- /dev/null +++ b/oscar64/NativeCodeOutliner.cpp @@ -0,0 +1,319 @@ +#include "NativeCodeOutliner.h" + + +NativeCodeMapper::NativeCodeMapper(void) +{ + for (int i = 0; i < HashSize; i++) + mHash[i] = nullptr; +} + +NativeCodeMapper::~NativeCodeMapper(void) +{ + for (int i = 0; i < HashSize; i++) + { + InsNode* n = mHash[i]; + while (n) + { + InsNode* m = n; + n = n->mNext; + delete m; + } + mHash[i] = nullptr; + } + +} + +void NativeCodeMapper::Reset(void) +{ + mBlocks.SetSize(0); +} + +int NativeCodeMapper::MapBasicBlock(NativeCodeBasicBlock* block) +{ + mBlocks.Push(block); + return -mBlocks.Size(); +} + +int NativeCodeMapper::MapInstruction(const NativeCodeInstruction& ins, LinkerSection* ls) +{ + int hash = ins.CodeHash() % HashSize; + InsNode* n = mHash[hash]; + while (n) + { + if (mIns[n->mIndex].CodeSame(ins) && n->mSection == ls) + return n->mIndex; + n = n->mNext; + } + n = new InsNode(); + n->mIndex = mIns.Size(); + n->mSection = ls; + mIns.Push(ins); + n->mNext = mHash[hash]; + mHash[hash] = n; + return n->mIndex; +} + +SuffixTree::SuffixTree(const int* str, int s, SuffixTree* n) +{ + mSeg = str; + mSize = s; + mNext = n; + mParent = nullptr; + mFirst = nullptr; +} + +void SuffixTree::AddParents(SuffixTree* parent) +{ + mParent = parent; + SuffixTree* n = mFirst; + while (n) + { + n->AddParents(this); + n = n->mNext; + } +} + +void SuffixTree::AddSuffix(const int* str, int s) +{ + SuffixTree* c = mFirst; + while (c && c->mSeg[0] != str[0]) + c = c->mNext; + + if (c) + { + int k = 1; + while (k < c->mSize && str[k] == c->mSeg[k]) + k++; + if (k == c->mSize) + c->AddSuffix(str + k, s - k); + else + { + SuffixTree* t = c->mFirst; + c->mFirst = new SuffixTree(c->mSeg + k, c->mSize - k, nullptr); + c->mFirst->mFirst = t; + c->mFirst = new SuffixTree(str + k, s - k, c->mFirst); + c->mSize = k; + } + } + else + mFirst = new SuffixTree(str, s, mFirst); +} + +void SuffixTree::AddString(const int* str) +{ + int s = 0; + while(str[s] >= 0) + s++; + s++; + + int i = 0; + while (str[i] >= 0) + { + AddSuffix(str + i, s - i); + i++; + } + AddSuffix(str + i, 1); +} + +void SuffixTree::CollectSuffix(NativeCodeMapper& map, int offset, ExpandingArray& segs) +{ + offset += mSize; + if (mFirst) + { + SuffixTree* t = mFirst; + while (t) + { + t->CollectSuffix(map, offset, segs); + t = t->mNext; + } + } + else + { + NativeCodeBasicBlock* block = map.mBlocks[-(mSeg[mSize - 1] + 1)]; + + SuffixSegment seg; + seg.mBlock = block; + seg.mStart = offset; + seg.mEnd = block->mIns.Size() + 1; + segs.Push(seg); + } +} + +int SuffixTree::LongestMatch(NativeCodeMapper& map, int size, int isize, int& msize, SuffixTree*& mtree) +{ + if (mFirst) + { + isize += mSize; + + for (int i = 0; i < mSize; i++) + { + if (mSeg[i] >= 0) + size += AsmInsModeSize[map.mIns[mSeg[i]].mMode]; + } + + assert(size < 10000); + + int cnt = 0; + SuffixTree* t = mFirst; + while (t) + { + cnt += t->LongestMatch(map, size, isize, msize, mtree); + t = t->mNext; + } + + if (size >= 6 && (size - 3) * (cnt - 1) > msize) + { + // Second run to cross check for overlaps + ExpandingArray segs; + SuffixTree* t = mFirst; + while (t) + { + t->CollectSuffix(map, 0, segs); + t = t->mNext; + } + segs.Sort([](const SuffixSegment& l, const SuffixSegment& r)->bool { + return l.mBlock == r.mBlock ? l.mStart < r.mStart : ptrdiff_t(l.mBlock) < ptrdiff_t(r.mBlock); + }); + + for (int i = 0; i + 1 < segs.Size(); i++) + { + if (segs[i].mBlock == segs[i + 1].mBlock && segs[i].mStart + isize > segs[i + 1].mStart) + cnt--; + } + + if (cnt > 1 && (size - 3) * (cnt - 1) > msize) + { + msize = (size - 3) * (cnt - 1); + mtree = this; + } + } + + return cnt; + } + else + return 1; +} + +void SuffixTree::Print(FILE * file, NativeCodeMapper& map, int depth) +{ + for (int i = 0; i < depth; i++) + fprintf(file, "."); + + for (int i = 0; i < mSize; i++) + { + fprintf(file, "["); + + if (mSeg[i] >= 0) + map.mIns[mSeg[i]].Disassemble(file); + else + { + NativeCodeBasicBlock* block = map.mBlocks[- (mSeg[i] + 1)]; + fprintf(file, "%s,%d", block->mProc->mInterProc->mIdent->mString, block->mIndex); + } + fprintf(file, "]"); + } + fprintf(file, "\n"); + + SuffixTree* n = mFirst; + while (n) + { + n->Print(file, map, depth + 1); + n = n->mNext; + } + +} + +void SuffixTree::ParentPrint(FILE* file, NativeCodeMapper& map) +{ + if (mParent) + mParent->ParentPrint(file, map); + for (int i = 0; i < mSize; i++) + { + if (mSeg[i] >= 0) + map.mIns[mSeg[i]].Disassemble(file); + else + { + NativeCodeBasicBlock* block = map.mBlocks[-(mSeg[i] + 1)]; + fprintf(file, "%s,%d", block->mProc->mInterProc->mIdent->mString, block->mIndex); + } + fprintf(file, "\n"); + } +} + +int SuffixTree::ParentCodeSize(NativeCodeMapper& map) const +{ + int size = 0; + for (int i = 0; i < mSize; i++) + { + if (mSeg[i] >= 0) + size += AsmInsModeSize[map.mIns[mSeg[i]].mMode]; + } + + if (mParent) + size += mParent->ParentCodeSize(map); + + return size; +} + +void SuffixTree::ParentCollect(NativeCodeMapper& map, NativeCodeBasicBlock* block) +{ + if (mParent) + mParent->ParentCollect(map, block); + for (int i = 0; i < mSize; i++) + block->mIns.Push(map.mIns[mSeg[i]]); +} + +void SuffixTree::ReplaceCalls(NativeCodeMapper& map, ExpandingArray& segs) +{ + SuffixTree* n = mFirst; + while (n) + { + n->ChildReplaceCalls(map, this, 0, segs); + n = n->mNext; + } +} + +void SuffixTree::ChildReplaceCalls(NativeCodeMapper& map, SuffixTree* tree, int offset, ExpandingArray& segs) +{ + for (int i = 0; i < mSize; i++) + { + if (mSeg[i] >= 0) + offset ++; + } + + if (mFirst) + { + SuffixTree* n = mFirst; + while (n) + { + n->ChildReplaceCalls(map, tree, offset, segs); + n = n->mNext; + } + } + else + { + NativeCodeBasicBlock* block = map.mBlocks[-(mSeg[mSize - 1] + 1)]; + tree->ParentReplaceCalls(map, block, offset, 0, segs); + } +} + +void SuffixTree::ParentReplaceCalls(NativeCodeMapper& map, NativeCodeBasicBlock* block, int offset, int size, ExpandingArray& segs) +{ + for (int i = 0; i < mSize; i++) + { + if (mSeg[i] >= 0) + size ++; + } + if (mParent) + mParent->ParentReplaceCalls(map, block, offset, size, segs); + else + { + int at = block->mIns.Size() - offset - size; + + SuffixSegment seg; + seg.mBlock = block; + seg.mStart = at; + seg.mEnd = at + size; + segs.Push(seg); + } +} diff --git a/oscar64/NativeCodeOutliner.h b/oscar64/NativeCodeOutliner.h new file mode 100644 index 0000000..000040f --- /dev/null +++ b/oscar64/NativeCodeOutliner.h @@ -0,0 +1,62 @@ +#pragma once + +#include "NativeCodeGenerator.h" +#include "Array.h" + +class NativeCodeMapper +{ +public: + NativeCodeMapper(void); + ~NativeCodeMapper(void); + + void Reset(void); + + int MapInstruction(const NativeCodeInstruction& ins, LinkerSection * ls); + int MapBasicBlock(NativeCodeBasicBlock* block); + + ExpandingArray mIns; + ExpandingArray mBlocks; +protected: + static const int HashSize = 256; + + struct InsNode + { + int mIndex; + LinkerSection * mSection; + InsNode* mNext; + }; + + InsNode * mHash[HashSize]; +}; + +struct SuffixSegment +{ + NativeCodeBasicBlock * mBlock; + int mStart, mEnd; +}; + +class SuffixTree +{ +public: + const int * mSeg; + int mSize; + + SuffixTree* mNext, * mParent, * mFirst; + + SuffixTree(const int* str, int s, SuffixTree* n); + void AddParents(SuffixTree* parent); + + void AddSuffix(const int* str, int s); + void AddString(const int* str); + + void Print(FILE* file, NativeCodeMapper & map, int depth); + void ParentPrint(FILE* file, NativeCodeMapper& map); + int LongestMatch(NativeCodeMapper& map, int size, int isize, int & msize, SuffixTree *& mtree); + void CollectSuffix(NativeCodeMapper& map, int offset, ExpandingArray& segs); + int ParentCodeSize(NativeCodeMapper& map) const; + void ParentCollect(NativeCodeMapper& map, NativeCodeBasicBlock * block); + void ReplaceCalls(NativeCodeMapper& map, ExpandingArray & segs); + void ChildReplaceCalls(NativeCodeMapper& map, SuffixTree * tree, int offset, ExpandingArray& segs); + void ParentReplaceCalls(NativeCodeMapper& map, NativeCodeBasicBlock* block, int offset, int size, ExpandingArray& segs); + +}; \ No newline at end of file diff --git a/oscar64/Parser.cpp b/oscar64/Parser.cpp index 6a719cd..9ddac71 100644 --- a/oscar64/Parser.cpp +++ b/oscar64/Parser.cpp @@ -13478,6 +13478,10 @@ void Parser::ParsePragma(void) mCompilerOptions |= COPT_OPTIMIZE_CONST_PARAMS; else if (ConsumeIdentIf("noconstparams")) mCompilerOptions &= ~COPT_OPTIMIZE_CONST_PARAMS; + else if (ConsumeIdentIf("outline")) + mCompilerOptions |= COPT_OPTIMIZE_OUTLINE; + else if (ConsumeIdentIf("nooutline")) + mCompilerOptions &= ~COPT_OPTIMIZE_OUTLINE; else mErrors->Error(mScanner->mLocation, EERR_INVALID_IDENTIFIER, "Invalid option"); diff --git a/oscar64/oscar64.cpp b/oscar64/oscar64.cpp index 6d95712..e8849fd 100644 --- a/oscar64/oscar64.cpp +++ b/oscar64/oscar64.cpp @@ -261,6 +261,8 @@ int main2(int argc, const char** argv) compiler->mCompilerOptions |= COPT_OPTIMIZE_GLOBAL; else if (arg[2] == 'm' && !arg[3]) compiler->mCompilerOptions |= COPT_OPTIMIZE_MERGE_CALLS; + else if (arg[2] == 'o' && !arg[3]) + compiler->mCompilerOptions |= COPT_OPTIMIZE_OUTLINE; else compiler->mErrors->Error(loc, EERR_COMMAND_LINE, "Invalid command line argument", arg); } diff --git a/oscar64/oscar64.vcxproj b/oscar64/oscar64.vcxproj index ba48400..ff681b9 100644 --- a/oscar64/oscar64.vcxproj +++ b/oscar64/oscar64.vcxproj @@ -189,6 +189,7 @@ + @@ -218,6 +219,7 @@ + diff --git a/oscar64/oscar64.vcxproj.filters b/oscar64/oscar64.vcxproj.filters index a95c2e9..371cbf1 100644 --- a/oscar64/oscar64.vcxproj.filters +++ b/oscar64/oscar64.vcxproj.filters @@ -87,6 +87,9 @@ Source Files + + Source Files + @@ -170,6 +173,9 @@ Header Files + + Header Files +