|
|
/*++
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 <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#define _WX86CPUAPI_
#include <wx86.h>
#include <wx86nt.h>
#include <wx86cpu.h>
#include <cpuassrt.h>
#include <config.h>
#include <instr.h>
#include <threadst.h>
#include <frag.h>
#include <analysis.h>
#include <entrypt.h>
#include <compilep.h>
#include <compiler.h>
#include <tc.h>
#include <mrsw.h>
#include <stdio.h>
#include <stdlib.h>
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 (i<NumberOfInstructions) {
//
// This loop skips from entrypoint to entrypoint.
//
CPUASSERT(i == 0 || InstructionStream[i-1].EntryPoint != PrevEntryPoint);
//
// Get an entrypoint node from the EntryPointMemory allocated by
// our caller.
//
if (ContainingEntrypoint) { EntryPoint = (PENTRYPOINT)EntryPointMemory; EntryPointMemory+=sizeof(ENTRYPOINT); } else { EP = (PEPNODE)EntryPointMemory; EntryPoint = &EP->ep; 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; j<NumberOfInstructions; ++j) { if (InstructionStream[j].EntryPoint != PrevEntryPoint) { PrevEntryPoint = InstructionStream[j].EntryPoint; break; } InstructionStream[j].EntryPoint = EntryPoint; }
//
// Fill in the Entrypoint structure
//
#ifdef CODEGEN_PROFILE
EntryPoint->SequenceNumber = 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; }
|