/*++ Copyright (c) 1996 Microsoft Corporation Module Name: compile.c Abstract: This module contains code to put the fragments into the translation cache. Author: Dave Hastings (daveh) creation-date 27-Jun-1995 Revision History: Dave Hastings (daveh) 16-Jan-1996 Move operand handling into fragment library Notes: We don't yet have any code to handle processor errata --*/ #include #include #include #include #define _WX86CPUAPI_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ASSERTNAME; #if _ALPHA_ #define MAX_RISC_COUNT 32768 #else #define MAX_RISC_COUNT 16384 #endif DWORD TranslationCacheFlags; // indicates what kind of code is in the TC #ifdef CODEGEN_PROFILE DWORD EPSequence; #endif // // This is guaranteed only to be accessed by a single thread at a time. // INSTRUCTION InstructionStream[MAX_INSTR_COUNT]; ULONG NumberOfInstructions; PENTRYPOINT CreateEntryPoints( PENTRYPOINT ContainingEntrypoint, PBYTE EntryPointMemory ) /*++ Routine Description: This function takes the InstructionStream and creates entrypoints from the information computed by LocateEntrypoints(). Entrypoints are then added into the Red/Black tree. Arguments: ContainingEntrypoint -- entrypoint which describes this range of intel code already EntryPointMemory -- pre-allocated Entrypoint memory Return Value: The Entry Point corresponding to the first instruction --*/ { ULONG i, j, intelDest; PEPNODE EP; PENTRYPOINT EntryPoint; PENTRYPOINT PrevEntryPoint; #ifdef CODEGEN_PROFILE ULONG CreateTime; CreateTime = GetCurrentTime(); EPSequence++; #endif // // Performance is O(n) always. // i=0; PrevEntryPoint = InstructionStream[0].EntryPoint; while (iep; EntryPointMemory+=sizeof(EPNODE); } // // Find the next entrypoint and the RISC address of the next // instruction which begins an entrypoint. Each instruction // in that range contains a pointer to the containing Entrypoint. // for (j=i+1; jSequenceNumber = EPSequence; EntryPoint->CreationTime = CreateTime; #endif EntryPoint->intelStart = (PVOID)InstructionStream[i].IntelAddress; if (j < NumberOfInstructions) { EntryPoint->intelEnd = (PVOID)(InstructionStream[j].IntelAddress-1); } else { ULONG Prev; for (Prev=j-1; InstructionStream[Prev].Size == 0; Prev--) ; EntryPoint->intelEnd = (PVOID)(InstructionStream[Prev].IntelAddress + InstructionStream[Prev].Size - 1); } InstructionStream[i].EntryPoint = EntryPoint; if (ContainingEntrypoint) { // // Link this sub-entrypoint into the containing entrypoint // EntryPoint->SubEP = ContainingEntrypoint->SubEP; ContainingEntrypoint->SubEP = EntryPoint; } else { INT RetVal; // // Insert it into the EP tree // EntryPoint->SubEP = NULL; RetVal = insertEntryPoint(EP); CPUASSERT(RetVal==1); } // // Advance to the next instruction which contains an // Entrypoint. // i=j; } if (ContainingEntrypoint) { // Indicate that the Entrypoints are present EntrypointTimestamp++; } return InstructionStream[0].EntryPoint; } PENTRYPOINT Compile( PENTRYPOINT ContainingEntrypoint, PVOID Eip ) /*++ Routine Description: This function puts together code fragments to execute the Intel code stream at Eip. It gets a stream of pre-decoded instructions from the code analysis module. Arguments: ContaingingEntrypoint -- If NULL, there is no entrypoint which already describes the Intel address to be compiled. Otherwise, this entrypoint describes the Intel address. The caller ensures that the Entrypoint->intelStart != Eip. Eip -- Supplies the location to compile from Return Value: pointer to the entrypoint for the compiled code --*/ { ULONG NativeSize, InstructionSize, IntelSize, OperationSize; PCHAR CodeLocation, CurrentCodeLocation; ULONG i; PENTRYPOINT Entrypoint; INT RetVal; PVOID StopEip; DWORD cEntryPoints; PBYTE EntryPointMemory; DWORD EPSize; #if defined(_ALPHA_) ULONG ECUSize, ECUOffset; #endif #if DBG DWORD OldEPTimestamp; #endif DECLARE_CPU; if (ContainingEntrypoint) { // // See if the entrypoint exactly describes the x86 address // if (ContainingEntrypoint->intelStart == Eip) { return ContainingEntrypoint; } // // No need to compile past the end of the current entrypoint // StopEip = ContainingEntrypoint->intelEnd; // // Assert that the ContainingEntrypoint is actually an EPNODE. // CPUASSERTMSG( ((PEPNODE)ContainingEntrypoint)->intelColor == RED || ((PEPNODE)ContainingEntrypoint)->intelColor == BLACK, "ContainingEntrypoint is not an EPNODE!"); } else { // // Find out if there is a compiled block following this one // Entrypoint = GetNextEPFromIntelAddr(Eip); if (Entrypoint == NULL) { StopEip = (PVOID)0xffffffff; } else { StopEip = Entrypoint->intelStart; } } // // Get the stream of instructions to compile. // If the Trap Flag is set, then compile only one instruction // if (cpu->flag_tf) { NumberOfInstructions = 1; } else { NumberOfInstructions = CpuInstructionLookahead; } cEntryPoints = GetInstructionStream(InstructionStream, &NumberOfInstructions, Eip, StopEip ); // // Pre-allocate enough space from the Translation Cache to store // the compiled code. // CodeLocation = AllocateTranslationCache(MAX_RISC_COUNT); // // Allocate memory for all of the Entrypoints. This must be done // after the Translation Cache allocation, in case that allocation // caused a cache flush. // if (ContainingEntrypoint) { EPSize = cEntryPoints * sizeof(ENTRYPOINT); } else { EPSize = cEntryPoints * sizeof(EPNODE); } EntryPointMemory = (PBYTE)EPAlloc(EPSize); if (!EntryPointMemory) { // // Either failed to commit extra pages of memory to grow Entrypoint // memory, or there are so many entrypoints that the the reserved // size has been exceeded. Flush the Translation Cache, which will // free up memory, then try the allocation again. // FlushTranslationCache(0, 0xffffffff); EntryPointMemory = (PBYTE)EPAlloc(EPSize); if (!EntryPointMemory) { // // We've tried our hardest, but there simply isn't any // memory available. Time to give up. // RtlRaiseStatus(STATUS_NO_MEMORY); } // // Now that the cache has been flushed, CodeLocation is invalid. // re-allocate from the Translation Cache. We know that // the cache was just flushed, so it is impossible for the cache // to flush again, which would invalidate EntryPointMemory. // #if DBG OldEPTimestamp = EntrypointTimestamp; #endif CodeLocation = AllocateTranslationCache(MAX_RISC_COUNT); CPUASSERTMSG(EntrypointTimestamp == OldEPTimestamp, "Unexpected Translation Cache flush!"); } // // Fill in the IntelStart, IntelEnd, and update // InstructionStream[]->EntryPoint // CreateEntryPoints(ContainingEntrypoint, EntryPointMemory); // // Generate RISC code from the x86 code // NativeSize = PlaceInstructions(CodeLocation, cEntryPoints); // // Give back the unused part of the Translation Cache // FreeUnusedTranslationCache(CodeLocation + NativeSize); // // Flush the information to the instruction cache // NtFlushInstructionCache(NtCurrentProcess(), CodeLocation, NativeSize); // // Update the flags indicating what kind of code is in the TC // TranslationCacheFlags |= CompilerFlags; return (PENTRYPOINT)EntryPointMemory; }