383 lines
9.8 KiB
C
383 lines
9.8 KiB
C
|
/*++
|
||
|
|
||
|
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;
|
||
|
}
|