Milán Major

netscape-revival

1 branch
Code

sun-java/jit/win32/jcompile.c

// $Header: /m/src/ns/sun-java/jit/win32/Attic/jcompile.c,v 1.13.6.2 1996/09/30 02:45:40 dhopwood Exp $
/*
 * Copyright (c) 1996 Borland International. All Rights Reserved.
 *
 * AppAccelerator(tm) for x86
 *
 * JCompile.c, R. Crelier, 9/24/96
 *
*/
#include "opcodes.h"
#include "oobj.h"
#include "interpreter.h"
#include "exceptions.h"

#include "jinterf.h"
#include "jcodegen.h"
#include "jcompile.h"
#include "jcompsup.h"

#define ALIGN UCALIGN

#ifdef	DEBUG
#include "opcodes.length"
#define check(assertion)    sysAssert(assertion)
static long bc_count = 0;
static long nc_count = 0;
#else
#define check(assertion)    (assertion ? (void)0 : ByteCodeError() )
#endif

/* Spill fpu results for better compatibility to the VM (slower) */
#define STRICT_FLOATING

/* Intel to bytecode size factor, used to allocate the compiled code array */
#define INIT_CODE_SIZE_FACTOR 6
static long codeSizeFactor = INIT_CODE_SIZE_FACTOR;

/* Unit of frequency for variable access */
#define FREQ_UNIT_LEV 10
#define FREQ_UNIT (1 << FREQ_UNIT_LEV)

/* Minimum remaining space required for compiling one opcode.
 * This is to avoid too frequent checking.
 * (opc_tableswitch and opc_lookupswitch are a special case)
 */
#define PC_RED_ZONE 256
static char pcInRedZone[] = "try again";

#define FRAME_WITH_XHANDLER_SIZE 36

#define OPSTACK_HEIGHT_EXCEPTION    0xcafebad0
#define BAD_BYTECODE_EXCEPTION	    0xcafebad1
#define INTERNAL_EXCEPTION	    0xcafebad2

#define IsQuadAccessibleOpnd(_n)					       \
    ((_n)->mr == MR_FST && (_n)->size == 8                                     \
     || (IsMem((_n)->mr) && (_n)->mr == ((_n)+1)->mr                           \
         && ((_n)+1)->offset - (_n)->offset == 4))

#define swap(_x)    (((_x) << 24) |					       \
                    (((_x) & 0x0000ff00) << 8) |                               \
                    (((_x) & 0x00ff0000) >> 8) |                               \
                    (((unsigned long)((_x) & 0xff000000)) >> 24))

#define pc2signedshort(pc)  ((((signed char *)(pc))[1] << 8) | pc[2])
#define pc2signedlong(pc)   ((((signed char *)(pc))[1] << 24) | (pc[2]	<< 16) \
                            | (pc[3] << 8) | (pc[4]))

#define NOT_A_VAR (-1)
#define VAR_HANDLE (0x80000000)
#define UNDEF_RH ((RangeHdr *)(-1))

#define OpndOffset(_ce, _n) ((_ce)->codeInfo->baseOff + 4*((_n) - (_ce)->ctxt->base))

typedef char* Label;

typedef enum RangeFlags
{
    RF_START	    = 0x01, // force creation of a range
    RF_JSR	    = 0x02, // target of jsr
    RF_XHANDLER     = 0x04, // exception handler entry
    RF_SET_XCTXT    = 0x08, // reachable from a different exception context
    RF_VISITED	    = 0x10, // range was visited by WalkControlFlow
    RF_XHVISITED    = 0x20, // range was visited by WalkXHControlFlow
    RF_DATABLOCK    = 0x40, // range contains data not opcodes (ie. jump tables)

}   RangeFlags;


typedef struct RangeHdr
{
    struct RangeHdr *nextRh;	// link list of all ranges in the code
    struct RangeHdr *targRh;	// target range reachable from this range
    struct RangeHdr *contRh;	// continuation range reachable from this range
    char	    *pc;	// intel pc of block entry
    char	    *xpc;	// intel pc of block entry from a block
				// with a different xctxt
    unsigned char   rangeFlags; // flags
    char	    joinCnt;	// 0: normal flow or single target,
				// -1: dead code, >0: join point
    long	    freq;	// execution frequency estimate
    int 	    topOnEntry; // absolute operand stack height on entry
    int 	    topOnExit;	// relative operand stack height on exit
    long	    bcpc;	// rel byte code pc for this block
    long	    xIndex;	// index of this handler in the exception table
    long	    xctxt;	// exception context
    Context	    *ctxt;	// opStack, regs, vars
    Label	    link;	// links all the forward branches to pc
    Label	    xlink;	// links all the forward branches to xpc

}   RangeHdr;


typedef union RangePtr
{
    struct
    {
	unsigned char	rangeFlags;
	char		joinCnt;
	unsigned char	tryEnterCnt, tryExitCnt;
    }	mark;

    RangeHdr *hdr;

}   RangePtr;


typedef struct Case
{
    long	key;
    RangeHdr	*targRh;
}   Case;


static void
OpStackHeightError(void)
{
    sysAssert(0);
    RaiseException(OPSTACK_HEIGHT_EXCEPTION, EXCEPTION_NONCONTINUABLE, 0, NULL);
}


static void
ByteCodeError(void)
{
    sysAssert(0);
    RaiseException(BAD_BYTECODE_EXCEPTION, EXCEPTION_NONCONTINUABLE, 0, NULL);
}


static void
InternalError(void)
{
    sysAssert(0);
    RaiseException(INTERNAL_EXCEPTION, EXCEPTION_NONCONTINUABLE, 0, NULL);
}


static	void
FlushVarCache(Context *cx)
{
    ModReg  mr;
    int     *var;

    for (mr = MR_ESP, var = &cx->cachedVar[MR_EAX];
	    --mr >= MR_EAX; var++)
	*var = NOT_A_VAR;
}


static bool_t
AllocContext(CompEnv *ce, Context **ctxt, int top)
{
    struct methodblock *mb = ce->mb;
    Context *cx;
    Item    *opnd;

    check((unsigned)top <= mb->maxstack);

    *ctxt = cx = (*p_malloc)(sizeof(Context) +
		    (mb->maxstack - 1 + 2)*sizeof(Item));
    // +2 to avoid block overrun in Body in case of a bytecode error
    // that may be detected too late (after an opcode has pushed 2 opnds).

    if (cx)
    {
	if  (mb->nlocals)
	{
	    cx->varCachingReg = (*p_calloc)(mb->nlocals, sizeof(ModReg));
	    cx->varhCachingReg = (*p_calloc)(mb->nlocals, sizeof(ModReg));
        }
	else
	{
	    cx->varCachingReg = NULL;
	    cx->varhCachingReg = NULL;
        }
	FlushVarCache(cx);
	cx->espLevel = 0;
	cx->regRefCnt[MR_EAX] = 0;
	cx->regRefCnt[MR_EDX] = 0;
	cx->regRefCnt[MR_ECX] = 0;
	cx->roundRobin = MR_ECX;
	cx->top = cx->base + top;
	for (opnd = cx->base; --top >= 0; opnd++)
	{
	    opnd->mr = MR_BASE + MR_ESP;
	    opnd->offset = OpndOffset(ce, opnd);
	    opnd->size = 4;
	    opnd->var = NOT_A_VAR;
	}
    }

    if (!cx || (mb->nlocals > 0 &&
	    (!cx->varCachingReg || !cx->varhCachingReg)))
    {
	ce->err = "Not enough memory";
	return FALSE;
    }

    return TRUE;
}


static bool_t
CopyContext(CompEnv *ce, RangeHdr *rh, int *clone)
{
    struct methodblock *mb;
    Context *dst_cx, *src_cx;
    long    cx_size, cache_size;

    if (*clone == 0)
    {
	rh->ctxt = ce->ctxt;
	*clone = 1;
    }
    else
    {
	mb = ce->mb;
	src_cx = ce->ctxt;
	cx_size = sizeof(Context) + (mb->maxstack - 1)*sizeof(Item);
	rh->ctxt = dst_cx = (*p_malloc)(cx_size);
	if  (dst_cx)
	{
	    memcpy(dst_cx, src_cx, cx_size);
	    cache_size = sizeof(ModReg)*mb->nlocals;
	    if (cache_size)
	    {
		dst_cx->varCachingReg = (*p_malloc)(cache_size);
		memcpy(dst_cx->varCachingReg, src_cx->varCachingReg, cache_size);
		dst_cx->varhCachingReg = (*p_malloc)(cache_size);
		memcpy(dst_cx->varhCachingReg, src_cx->varhCachingReg, cache_size);
	    }
	    dst_cx->top = dst_cx->base + (src_cx->top - src_cx->base);
	}
	if  (!dst_cx ||	(mb->nlocals > 0 &&
		(!dst_cx->varCachingReg || !dst_cx->varhCachingReg)))
	{
	    ce->err = "Not enough memory";
	    return FALSE;
	}
    }
    return TRUE;
}


static bool_t
InitializeCompEnv(CompEnv *ce, struct methodblock *mb, ExecEnv *ee)
{
    long blockSize;

    memset(ce, 0, sizeof(*ce));
    ce->mb = mb;
    ce->ee = ee;

    if  (mb->nlocals < mb->args_size)
	RaiseException(BAD_BYTECODE_EXCEPTION, EXCEPTION_NONCONTINUABLE, 0, NULL);

    ce->rp = (*p_calloc)(mb->code_length + 1, sizeof(RangePtr));
	/* +1: sentinel for marking end of exception ranges and epilogue */

    ce->xctxt = -1;
    blockSize = mb->code_length * codeSizeFactor + 64 + PC_RED_ZONE;
    ce->compiledCode = (*p_malloc)(blockSize);
    ce->codeInfo = (*p_malloc)(sizeof(CodeInfo));
    ce->pc = ce->compiledCode;
    ce->pcRedZone = ce->compiledCode + blockSize - PC_RED_ZONE;
    ce->epilogueRh = (*p_calloc)(1, sizeof(RangeHdr));

    if (mb->nlocals > 0)
    {
	ce->varOff = (*p_calloc)(mb->nlocals, sizeof(long));
	// calloc: init to 0 for MarkBranchTargAndVars
        ce->varFreq = (*p_calloc)(mb->nlocals, sizeof(long));
	// calloc: init to 0 for CountVarFreq
    }
    if (    !ce->rp
	||  !ce->compiledCode
	||  !ce->codeInfo
	||  !ce->epilogueRh
	||  ((!ce->varOff || !ce->varFreq) && mb->nlocals > 0)
	)
    {
	ce->err = "Not enough memory";
	return FALSE;
    }
    // ce->ctxt allocated in Prologue
    ce->epilogueRh->bcpc = mb->code_length;
    ce->epilogueRh->xctxt = -1; // epilogue does not execute in a catch frame
    // ce->rp[mb->code_length].hdr = ce->epilogueRh;
    // not yet: union! and not needed anyway
    return TRUE;
}


static void
FreeContext(Context *ctxt)
{
    if (ctxt->varCachingReg)
	(*p_free)(ctxt->varCachingReg);
    if (ctxt->varhCachingReg)
	(*p_free)(ctxt->varhCachingReg);

    (*p_free)(ctxt);
}


static void
FinalizeCompEnv(CompEnv *ce)
{
    RangeHdr *rh, *nextRh;

    // free all temporary allocated storage
    if (ce->rp)
    {
	rh = ce->rp[0].hdr;
	while (rh)
	{
	    nextRh = rh->nextRh;
	    if (rh->ctxt)
	    {
		FreeContext(rh->ctxt);
	    }
	    (*p_free)(rh);
	    rh = nextRh;
	}
	(*p_free)(ce->rp);
    }
    if (ce->epilogueRh)
	(*p_free)(ce->epilogueRh);
    if (ce->varOff)
	(*p_free)(ce->varOff);
    if (ce->varFreq)
	(*p_free)(ce->varFreq);
    if (ce->cases)
	(*p_free)(ce->cases);
    if (ce->ctxt)
	FreeContext(ce->ctxt);
}


/* Returns the register variable being assigned to at hintpc if any.
 * Returns the register of the low part only if the variable is long.
 * We ignore the wide form of store (not enough regs for 256 variables).
 */
static RegSet
GetHint(CompEnv *ce, unsigned char *hintpc)
{
    int     var;
    long    off;

    switch  (*hintpc)
    {
    case opc_istore:
    case opc_astore:
    case opc_lstore:
	var = hintpc[1]; break;

    case opc_istore_0:
    case opc_astore_0:
    case opc_lstore_0:
	var = 0; break;

    case opc_istore_1:
    case opc_astore_1:
    case opc_lstore_1:
	var = 1; break;

    case opc_istore_2:
    case opc_astore_2:
    case opc_lstore_2:
	var = 2; break;

    case opc_istore_3:
    case opc_astore_3:
    case opc_lstore_3:
	var = 3; break;

    default:
	return RS_CALLER_SAVED;
    }

    off = ce->varOff[var];
    if (off < 0)
	return regSetOfMr[-off];

    return RS_CALLER_SAVED;
}


/* Returns the register variable being assigned to at hintpc if any.
 * Returns the register of the high part only.
 * We ignore the wide form of store (not enough regs for 256 variables).
 */
static RegSet
GetHintHigh(CompEnv *ce, unsigned char *hintpc)
{
    int     var;
    long    off;

    switch  (*hintpc)
    {
    case opc_lstore:
	var = hintpc[1] + 1; break;

    case opc_lstore_0:
	var = 1; break;

    case opc_lstore_1:
	var = 2; break;

    case opc_lstore_2:
	var = 3; break;

    case opc_lstore_3:
	var = 4; break;

    default:
	return RS_CALLER_SAVED;
    }

    off = ce->varOff[var];
    if (off < 0)
	return regSetOfMr[-off];

    return RS_CALLER_SAVED;
}


static void
FreeRegs(CompEnv *ce, ModReg mr)
{
    int reg;

    if (mr < MR_ABS)
    {
    	reg = BaseRegOf(mr);
	if (reg <= MR_ECX)
	{
	    ce->ctxt->regRefCnt[reg]--;
	    sysAssert(ce->ctxt->regRefCnt[reg] >= 0);
	}
	if (IsBasedIndexed(mr))
	{
    	    reg = ((mr - MR_BINX) >> 3) & 7;
	    if (reg <= MR_ECX)
	    {
		ce->ctxt->regRefCnt[reg]--;
		sysAssert(ce->ctxt->regRefCnt[reg] >= 0);
	    }
	}
    }
}


static void
IncRefCnt(CompEnv *ce, ModReg mr)
{
    int reg;

    if (mr < MR_ABS)
    {
    	reg = BaseRegOf(mr);
	if (reg <= MR_ECX)
	    ce->ctxt->regRefCnt[reg]++;
	if (IsBasedIndexed(mr))
	{
    	    reg = ((mr - MR_BINX) >> 3) & 7;
	    if (reg <= MR_ECX)
		ce->ctxt->regRefCnt[reg]++;
	}
    }
}


/* Return the set of registers that would be free after a call to FreeRegs(ce, mr),
 * but do not do it.
 */
static RegSet
FreeRegSet(CompEnv *ce, ModReg mr)
{
    int reg;
    int *rr = ce->ctxt->regRefCnt;
    int regRefCnt[MR_ECX+1];
    RegSet rs = RS_EMPTY;

    regRefCnt[MR_EAX] = rr[MR_EAX];
    regRefCnt[MR_EDX] = rr[MR_EDX];
    regRefCnt[MR_ECX] = rr[MR_ECX];
    if (mr < MR_ABS)
    {
    	reg = BaseRegOf(mr);
	if (reg <= MR_ECX)
	{
	    regRefCnt[reg]--;
	    sysAssert(regRefCnt[reg] >= 0);
	}
	if (IsBasedIndexed(mr))
	{
    	    reg = ((mr - MR_BINX) >> 3) & 7;
	    if (reg <= MR_ECX)
	    {
		regRefCnt[reg]--;
		sysAssert(regRefCnt[reg] >= 0);
	    }
	}
    }
    if (regRefCnt[MR_EAX] == 0)
    	rs = RS_EAX;
    if (regRefCnt[MR_EDX] == 0)
    	rs |= RS_EDX;
    if (regRefCnt[MR_ECX] == 0)
    	rs |= RS_ECX;
    return rs;
}


static void
LoadOpnd(CompEnv *ce, Item *n, RegSet targ);


/* Spill the operand n to the operand stack in order to free the registers
 * it is using.
 * n must point into the current operand stack, n->var is therefore valid and
 * NOT_A_VAR, because the item of a local cannot use a caller-saved register,
 * so this function should not be called to spill it.
 * Be careful to preserve both condition codes (FPU and integer)
 */
static void
SpillOpnd(CompEnv *ce, Item *n)
{
    Item    mem;
    ModReg  mr;
    int	    *rr;

    sysAssert(n >= ce->ctxt->base && n < ce->ctxt->top
	      && n->var == NOT_A_VAR && n->mr != MR_FST);

    mem.mr = MR_BASE + MR_ESP;
    mem.offset = OpndOffset(ce, n);
    mem.size = 4;
    FreeRegs(ce, n->mr);
    if (IsIntReg(n->mr))
        GenStore(ce, &mem, n->mr);
    else
    {
	for (mr = MR_EAX, rr = &ce->ctxt->regRefCnt[MR_EAX]; mr <= MR_ECX; mr++, rr++)
	    if (*rr == 0)
		break;

	if (mr > MR_ECX)
	{
	    // all regs are in use
	    // we must push the opnd and pop it to its position on the opnd stack
	    if (mem.offset == 0 && ce->ctxt->espLevel == 0)
		// special case: spare a pop
		GenAddImmRL(ce, MR_ESP, 4);

	    if (IsIntReg(n->mr))
		GenByte(ce, PUSHR_L + regMap[n->mr]);
	    else
	    {
		GenByte(ce, 0xFF);
		GenEA(ce, PUSH_L, n);
	    }
	    if (mem.offset != 0 || ce->ctxt->espLevel != 0)
	    {
		GenByte(ce, 0x8F);
		GenEA(ce, POP_L, &mem); // esp += 4; [esp+off] = [esp-4];
	    }
	}
	else
	{
            ce->ctxt->cachedVar[mr] = NOT_A_VAR;	// invalidate cache
	    GenLoad(ce, mr, n);
	    GenStore(ce, &mem, mr);
	}
    }
    n->mr = MR_BASE + MR_ESP;
    n->offset = mem.offset;
}


/* This function allocates a caller-saved register only.
 * If targ is a callee-saved register, we return.
 * The returned register must be an element of the targ set parameter.
 * If all regs in targ are in use, the operand on the opstack using
 * a reg of the targ set is spilled.
 */
static	ModReg
GetReg(CompEnv *ce, RegSet targ)
{
    Context *cx = ce->ctxt;
    Item    *opnd;
    int     i;
    ModReg  rRb;
    RegSet  rs;

    if ((targ & RS_CALLEE_SAVED) != RS_EMPTY)
    {
	sysAssert(bitCnt[targ] == 1);
	return lowestBit[targ] + MR_EAX;
    }

    rRb = cx->roundRobin;
    while (1)
    {
	if (rRb == MR_ECX && cx->regRefCnt[MR_EAX] == 0 && (targ & RS_EAX) != RS_EMPTY)
	{
	    cx->regRefCnt[MR_EAX] = 1;
	    cx->cachedVar[MR_EAX] = NOT_A_VAR;	// invalidate this caching register
	    // caching regs can be used once only, this simplifies the cache
	    // invalidating mechanism which is centralized here, in inlined
	    // GetReg in LoadOpnd, and in SpillOpnd.
	    // LookupVarCache may decide to revalidate the register.
            cx->roundRobin = MR_EAX;
	    return MR_EAX;
	}
	if (rRb != MR_EDX && cx->regRefCnt[MR_EDX] == 0 && (targ & RS_EDX) != RS_EMPTY)
	{
	    cx->regRefCnt[MR_EDX] = 1;
	    cx->cachedVar[MR_EDX] = NOT_A_VAR;
            cx->roundRobin = MR_EDX;
	    return MR_EDX;
	}
	if (cx->regRefCnt[MR_ECX] == 0 && (targ & RS_ECX) != RS_EMPTY)
	{
	    cx->regRefCnt[MR_ECX] = 1;
	    cx->cachedVar[MR_ECX] = NOT_A_VAR;
            cx->roundRobin = MR_ECX;
	    return MR_ECX;
	}
	if (cx->regRefCnt[MR_EAX] == 0 && (targ & RS_EAX) != RS_EMPTY)
	{
	    cx->regRefCnt[MR_EAX] = 1;
	    cx->cachedVar[MR_EAX] = NOT_A_VAR;
            cx->roundRobin = MR_EAX;
	    return MR_EAX;
	}
	if (cx->regRefCnt[MR_EDX] == 0 && (targ & RS_EDX) != RS_EMPTY)
	{
	    cx->regRefCnt[MR_EDX] = 1;
	    cx->cachedVar[MR_EDX] = NOT_A_VAR;
            cx->roundRobin = MR_EDX;
	    return MR_EDX;
	}
	// All regs of targ are in use
	// Spill an opnd that uses a reg of targ.
	// Other opnd may use the same reg, repeat spilling if necessary.
	for (opnd = cx->base, i = cx->top - opnd; --i >= 0; opnd++)
	{
    	    rs = regSetOfMr[opnd->mr];
	    if (rs & targ)
		break;
	}
	if (i < 0)
	    InternalError(); // a register was not released

	if (bitCnt[rs] > 1 && FreeRegSet(ce, opnd->mr))
	// opnd is using several registers and can be loaded without spill.
	// Loading it will free at least one register. This may avoid a spill.
    	    LoadOpnd(ce, opnd, RS_CALLER_SAVED);
	else
	    SpillOpnd(ce, opnd);
    }
}


static	void
CacheVar(CompEnv *ce, int var, ModReg mr)
{
    if (IsCallerSaved(mr))
    {
    	sysAssert(var >= 0 && var < ce->mb->nlocals);
	ce->ctxt->varCachingReg[var] = mr;
	ce->ctxt->cachedVar[mr] = var;
    }
}


static	void
CacheVarH(CompEnv *ce, int var, ModReg mr)
{
    if (IsCallerSaved(mr))
    {
    	sysAssert(var >= 0 && var < ce->mb->nlocals);
	ce->ctxt->varhCachingReg[var] = mr;
	ce->ctxt->cachedVar[mr] = var + VAR_HANDLE;
    }
}


/* Check whether n is a variable currently cached by a register.
 * If it is the case and if the register is free, allocate it and change n->mr
 * to this register. There is no need to free n, because n is a local.
 */
static	int
LookupVarCache(CompEnv *ce, Item *n, int var, RegSet targ)
{
    ModReg  mr;
    Context *cx = ce->ctxt;
    Item    dup;

    sysAssert(IsIntReg(n->mr) && n->mr == -ce->varOff[var]
	      || n->mr == MR_BASE + MR_ESP && n->offset == ce->varOff[var]);
    mr = cx->varCachingReg[var];
    if  (cx->cachedVar[mr] == var)
    {
	sysAssert(IsCallerSaved(mr) && var >= 0 && var < ce->mb->nlocals);
	if ((targ & RS_CALLEE_SAVED) == RS_EMPTY)
	{
    	    // mr may be modified
	    if (cx->regRefCnt[mr] == 0)
	    {
                // GetReg(ce, targOfMr[mr]);
		cx->regRefCnt[mr]++;
		cx->cachedVar[mr] = NOT_A_VAR;
            }
            else
	    {
    		dup.mr = mr;
		mr = GetReg(ce, targ);
		// GetReg may have trashed the caching register by spilling the only operand
		// using it. Check that the cache is still valid. If not, give up.
		if (cx->cachedVar[dup.mr] != var)
		{
    		    FreeRegs(ce, mr);
    		    return 0;
		}
		GenLoad(ce, mr, &dup);
            }
        }
	else
            // GetReg(ce, targOfMr[mr]); with no invalidate
	    cx->regRefCnt[mr]++;	
	n->mr = mr;
	return 1;
    }
    return 0;
}


/* Check whether n is a variable whose handle is currently cached by a reg.
 * If it is the case and if the register is free, allocate it and change n->mr
 * to this register. There is no need to free n, because n is a local.
 */
static	int
LookupVarHCache(CompEnv *ce, Item *n, int var)
{
    ModReg  mr;
    Context *cx = ce->ctxt;

    sysAssert(IsIntReg(n->mr) && n->mr == -ce->varOff[var]
	      || n->mr == MR_BASE + MR_ESP && n->offset == ce->varOff[var]);
    mr = cx->varhCachingReg[var];
    if  (cx->cachedVar[mr] == (signed)(var | VAR_HANDLE))
    {
	sysAssert(IsCallerSaved(mr) && var >= 0 && var < ce->mb->nlocals);
	// GetReg(ce, targOfMr[mr]); with no invalidate: a handle is read-only
	cx->regRefCnt[mr]++;	
	n->mr = mr;
	return 1;
    }
    return 0;
}


static void
Link(CompEnv *ce, Label *link)
{
    char *next;

    next = *link;
    *link = ce->pc;
    *((char **)ce->pc)++ = next;
}


static void
FixLink(CompEnv *ce, Label link)
{
    char *next;
    long dist;

    // eliminate the last JMP rel32 if rel32 is 0
    if (link)
    {
	dist = ce->pc - link - 4;
	if  (dist == 0 && *((unsigned char *)link - 1) == JMPN)
	{
	    ce->pc -= 5;
	    link = *(char **)link;
	}
    }
    while (link)
    {
	next = *(char **)link;
	*(char **)link = (char *)(ce->pc - link - 4);
	link = next;
    }
}


static void
ShortLink(CompEnv *ce, Label *link)
{
    char *next;
    long dist;

    next = *link;
    if (next)
	dist = next - ce->pc;
    else
	dist = 0;

    *link = ce->pc;
    sysAssert(ByteRange(dist));
    *((char *)ce->pc)++ = (char)dist;
}


static void
FixShortLink(CompEnv *ce, Label link)
{
    long dist, rel8;

    while (link)
    {
	dist = *(signed char *)link;
	rel8 = ce->pc - link - 1;
	sysAssert(ByteRange(rel8));
	*link = (char)rel8;
	link = dist ? link + dist : 0;
    }
}


/* Store operand n into opstack memory if not yet flushed. Be careful to
 * preserve the integer condition code since flushing may happen between a
 * compare and a branch! (The fpu condition code may be modified).
 */
static void
FlushOpnd(CompEnv *ce, Item *n)
{
    ModReg  mr;
    long    off;

    sysAssert(n >= ce->ctxt->base && n < ce->ctxt->top);
    off = OpndOffset(ce, n);
    if (n->mr == MR_BASE + MR_ESP && n->offset == off)
	return;

    mr = n->mr;
    if (mr == MR_FST)
    {
	if  (n->size == 0)
	    return; // the high part of a double is flushed with its low part
	n->mr = MR_BASE + MR_ESP;
	n->offset = off;
	GenFStore(ce, n, 0);
	if  (n->size == 8)
	{
	    (n + 1)->mr = MR_BASE + MR_ESP;
	    (n + 1)->offset = off + 4;
	    (n + 1)->size = 4;
	}
	n->size = 4;	// now in memory, both parts have size 4
#ifdef	DEBUG
	// we must flush top-dowm, because of operands on the FPU stack
	{
	    Item *opnd, *top = ce->ctxt->top;
	    for (opnd = n + 1; opnd < top; opnd++)
		sysAssert(opnd->mr != MR_FST);
	}
#endif
    }
    else
    {
	if  (mr == MR_IMM)
	{
	    mr = GetReg(ce, RS_CALLER_SAVED);
	    GenByte(ce, LOADI_L + regMap[mr]);	// XOR by GenLoad modifies cc!
	    GenLong(ce, n->offset);
	}
	else if (!IsIntReg(mr))
	{
    	    LoadOpnd(ce, n, RS_CALLER_SAVED);
	    mr = n->mr;
	}
	n->mr = MR_BASE + MR_ESP;
	n->offset = off;
	n->var = NOT_A_VAR; // item is loaded on the operand stack now
	GenStore(ce, n, mr);
	FreeRegs(ce, mr);
    }
}


/* Flush all operands below n on the opstack to opstack memory. This function is
 * used to merge different contexts at a join point.
 */
static void
FlushOpStack(CompEnv *ce, Item *n)
{
    Item *base = ce->ctxt->base;

    while (--n >= base)   // top-down because of MR_FST operands!
	FlushOpnd(ce, n);
}


static void
PinRVarAliases(CompEnv *ce, RegSet var, Item *n);


static void
MakeFOpndAccessible(CompEnv *ce, Item *n, int size)
{
    sysAssert(n >= ce->ctxt->base && n < ce->ctxt->top);

    if (size == 8)
    {
	if  (!IsQuadAccessibleOpnd(n))
	{
	    // may happen for opc_l2d (n, n+1 in regs)
	    FlushOpnd(ce, n);
	    FlushOpnd(ce, n + 1);
	    sysAssert(IsQuadAccessibleOpnd(n));
	}
    }
    else if (IsIntReg(n->mr) || n->mr == MR_IMM)
	FlushOpnd(ce, n);

}


/* Load an operand from the opstack into a register of targ. Modify its item.
 * Allocate a cache register if cache hit.
 * Make a copy of n if n is a register variable not in targ or
 * if n is a caller-saved referenced more than once
 */
static void
LoadOpnd(CompEnv *ce, Item *n, RegSet targ)
{
    ModReg mr, rRb;
    Item *opnd;
    int i;
    Context *cx = ce->ctxt;
    // locals for inline code below
    int reg, var;
    int *rr = cx->regRefCnt;
    int regRefCnt[MR_ECX+1];
    RegSet rs, t;

    sysAssert(n >= cx->base && n < cx->top);

    mr = n->mr;
    sysAssert((var = n->var) == NOT_A_VAR
	      || IsIntReg(mr) && (mr == -ce->varOff[var] || mr == cx->varCachingReg[var])
	      || mr == MR_BASE + MR_ESP && n->offset == ce->varOff[var]);

    if (IsCallerSaved(mr) && rr[mr] > 1 && (targ & RS_CALLEE_SAVED) == RS_EMPTY)
    	// we need a copy of multi-referenced n, because a caller-saved
	// reg was requested, i.e. a reg that will be modified.
	// if targ is RS_ALL, we can safely use the same reg.
	targ &= ~regSetOfMr[mr];

    if (MrFitsTarget(mr, targ))
    {
    	// n is already in a reg
        if ((targ & RS_CALLER_SAVED) == RS_EMPTY)
	{
	    sysAssert(bitCnt[targ] == 1);
	    // we want to load n into its own register var (e.g. LAddOp)
	    // we need to make a copy of this reg var if it is already loaded
	    // on the operand stack below n before using it
	    PinRVarAliases(ce, targ, n);
	}
	else if ((targ & RS_CALLEE_SAVED) == RS_EMPTY)
            cx->cachedVar[mr] = NOT_A_VAR;	// n is not read-only, invalidate cache

	n->var = NOT_A_VAR; // item is loaded on the operand stack now
	return;
    }

    var = n->var;
    if (var != NOT_A_VAR)
	LookupVarCache(ce, n, var, targ);
    mr = n->mr;
    if (!MrFitsTarget(mr, targ))
    {
	if ((targ & RS_CALLER_SAVED) == RS_EMPTY)
	{
	    sysAssert(bitCnt[targ] == 1);   // we want to load n into a register var
	    mr = lowestBit[targ] + MR_EAX;
	    // we need to make a copy of this reg var if it is already loaded
	    // on the operand stack below n before overwriting it with n
	    PinRVarAliases(ce, targ, n);
	    FreeRegs(ce, n->mr);
	}
	else
	{
	    t = targ & RS_CALLER_SAVED;
	    rRb = cx->roundRobin;
	    // make sure not to free regs too early;
	    // the following situation could occur:
	    // we want to load [eax+ecx] into ecx, an other opnd also uses [ecx]
	    // we cannot free eax and ecx, and get ecx, because eax would be
	    // trashed for spilling [ecx]
	    while (1)
	    {

/* inline and optimize the following 3 calls for efficiency:
		if (FreeRegSet(ce, mr) & t)
 		{
 		    FreeRegs(ce, mr);
 		    mr = GetReg(ce, t); // no spill
 		    break;
 		}
*/

/* start inline */
		regRefCnt[MR_EAX] = rr[MR_EAX];
		regRefCnt[MR_EDX] = rr[MR_EDX];
		regRefCnt[MR_ECX] = rr[MR_ECX];
		if (mr < MR_ABS)
		{
		    reg = BaseRegOf(mr);
		    if (reg <= MR_ECX)
		    {
			regRefCnt[reg]--;
			sysAssert(regRefCnt[reg] >= 0);
		    }
		    if (IsBasedIndexed(mr))
		    {
			reg = ((mr - MR_BINX) >> 3) & 7;
			if (reg <= MR_ECX)
			{
			    regRefCnt[reg]--;
			    sysAssert(regRefCnt[reg] >= 0);
			}
		    }
		}
		if (rRb == MR_ECX && regRefCnt[MR_EAX] == 0 && (t & RS_EAX) != RS_EMPTY)
		{
    		    cx->regRefCnt[MR_EAX] = 1;
                    cx->regRefCnt[MR_EDX] = regRefCnt[MR_EDX];
                    cx->regRefCnt[MR_ECX] = regRefCnt[MR_ECX];
		    cx->cachedVar[MR_EAX] = NOT_A_VAR;
		    mr = MR_EAX;
		    break;
		}
		if (rRb != MR_EDX && regRefCnt[MR_EDX] == 0 && (t & RS_EDX) != RS_EMPTY)
		{
    		    cx->regRefCnt[MR_EAX] = regRefCnt[MR_EAX];
                    cx->regRefCnt[MR_EDX] = 1;
                    cx->regRefCnt[MR_ECX] = regRefCnt[MR_ECX];
		    cx->cachedVar[MR_EDX] = NOT_A_VAR;
		    mr = MR_EDX;
		    break;
		}
		if (regRefCnt[MR_ECX] == 0 && (t & RS_ECX) != RS_EMPTY)
                {
    		    cx->regRefCnt[MR_EAX] = regRefCnt[MR_EAX];
                    cx->regRefCnt[MR_EDX] = regRefCnt[MR_EDX];
                    cx->regRefCnt[MR_ECX] = 1;
		    cx->cachedVar[MR_ECX] = NOT_A_VAR;
		    mr = MR_ECX;
		    break;
		}
		if (regRefCnt[MR_EAX] == 0 && (t & RS_EAX) != RS_EMPTY)
		{
    		    cx->regRefCnt[MR_EAX] = 1;
                    cx->regRefCnt[MR_EDX] = regRefCnt[MR_EDX];
                    cx->regRefCnt[MR_ECX] = regRefCnt[MR_ECX];
		    cx->cachedVar[MR_EAX] = NOT_A_VAR;
		    mr = MR_EAX;
		    break;
		}
		if (regRefCnt[MR_EDX] == 0 && (t & RS_EDX) != RS_EMPTY)
		{
    		    cx->regRefCnt[MR_EAX] = regRefCnt[MR_EAX];
                    cx->regRefCnt[MR_EDX] = 1;
                    cx->regRefCnt[MR_ECX] = regRefCnt[MR_ECX];
		    cx->cachedVar[MR_EDX] = NOT_A_VAR;
		    mr = MR_EDX;
		    break;
		}
/* end inline */
		for (opnd = cx->base, i = cx->top - opnd; --i >= 0; opnd++)
		{
    		    rs = regSetOfMr[opnd->mr];
		    if (rs & t)
			break;
		}

		if (i < 0)
		    InternalError(); // a register was not released

		if (bitCnt[rs] > 1 && FreeRegSet(ce, opnd->mr))
		// opnd is using several registers and can be loaded without spill.
		// Loading it will free at least one register. This may avoid a spill.
		    LoadOpnd(ce, opnd, RS_CALLER_SAVED);
		else
		    SpillOpnd(ce, opnd);
		mr = n->mr; // in case n was spilled
            }
	    cx->roundRobin = mr;
	}
	GenLoad(ce, mr, n);
	n->mr = mr;
	// if mr is holding a read-only variable, validate the cache
	if (n->var != NOT_A_VAR && targ == RS_ALL)
	    CacheVar(ce, n->var, mr);
    }
    sysAssert((targ & RS_CALLEE_SAVED) != RS_EMPTY || cx->cachedVar[mr] == NOT_A_VAR);
    n->var = NOT_A_VAR; // item is loaded on the operand stack now
}


/* Load a floating point operand from the opstack on top of the floating point
 * stack. Modify its item.
 * A loaded double opnd has its size changed from <4, 4> to <8, 0>.
 * See FlushOpnd.
 */
static void
LoadFOpnd(CompEnv *ce, Item *n, int size)
{
    sysAssert(n >= ce->ctxt->base && n < ce->ctxt->top);

    if (n->mr != MR_FST)
    {
	MakeFOpndAccessible(ce, n, size);
	FreeRegs(ce, n->mr);
	if  (size == 8)
	    FreeRegs(ce, (n + 1)->mr);
	n->size = size;
	GenFLoad(ce, n);
	n->mr = MR_FST;
	if  (size == 8)
	{
	    (n + 1)->mr = MR_FST;
	    (n + 1)->size = 0;
	}
    }
    else
	sysAssert(n->size == size);
}


static	void
Push(CompEnv *ce, Item *n)
{
    if (IsIntReg(n->mr))
	GenByte(ce, PUSHR_L + regMap[n->mr]);
    else if (n->mr == MR_IMM)
    {
	if  (ByteRange(n->offset))
	    Gen2Bytes(ce, PUSHI_B, n->offset);
	else
	    GenByteLong(ce, PUSHI_L, n->offset);
    }
    else
    {
	GenByte(ce, 0xFF);
	GenEA(ce, PUSH_L, n);
    }
    ce->ctxt->espLevel -= 4;
    FreeRegs(ce, n->mr);
}


/* Push an operand from the opstack on top of the procedure stack for parameter
 * passing. Modify its item, which should not be used any more.
 */
static void
PushOpnd(CompEnv *ce, Item *n)
{
    int var;

    sysAssert(n >= ce->ctxt->base && n < ce->ctxt->top);

    if (!IsIntReg(n->mr) && (var = n->var) != NOT_A_VAR)
	LookupVarCache(ce, n, var, RS_ALL);
    Push(ce, n);
    // avoid spilling released operand
    n->mr = MR_IMM;
}


/* Push a floating point operand from the opstack on top of the procedure stack
 * for parameter passing. Convert between float and double as indicated by
  * opndSize and paramSize. Modify its item, which should not be used any more.
 */
static void
PushFOpnd(CompEnv *ce, Item *n, int opndSize, int paramSize)
{
    sysAssert(n >= ce->ctxt->base && n < ce->ctxt->top);

    if (opndSize != paramSize && n->mr != MR_FST)
	LoadFOpnd(ce, n, opndSize);

    if (n->mr == MR_FST)
    {
	GenAddImmRL(ce, MR_ESP, -paramSize);
	ce->ctxt->espLevel -= paramSize;
	GenIndirEA(ce, paramSize == 4 ? 0xD9 : 0xDD, 0x18, MR_ESP, 0);
	n->mr = MR_IMM; // avoid spilling released operand
	if  (opndSize == 8)
	    (n + 1)->mr = MR_IMM;
    }
    else
    {
	if  (paramSize == 8)
	    PushOpnd(ce, n + 1);
	PushOpnd(ce, n);
    }
}


/* Make sure all operands below n on the opstack do not use caller-saved regs
 * or the floating point stack, and that they don't reference non-local memory.
 * This function is called before a function call.
 */
static void
PinTempMemAliases(CompEnv *ce, Item *n)
{
    Item *base = ce->ctxt->base;

    while (--n >= base)   // in this order because of MR_FST operands!
    {
	sysAssert(n->mr != MR_CC);
	if  ((regSetOfMr[n->mr] & RS_CALLER_SAVED) != RS_EMPTY || n->mr == MR_FST
	     || IsMem(n->mr) && n->mr != MR_BASE + MR_ESP)
	    FlushOpnd(ce, n);
    }
}


/* The register variable var is to be assigned. If register var is used by
 * operands loaded on the stack below n, we need to make a copy of these
 * operands before the assignment to var.
 */
static void
PinRVarAliases(CompEnv *ce, RegSet var, Item *n)
{
    Item    *base = ce->ctxt->base;

    while (--n >= base)
	if  (regSetOfMr[n->mr] & var)
	    LoadOpnd(ce, n, RS_CALLER_SAVED);	// make a copy
}


/* The variable var in memory is to be assigned. If var is aliased by operands
 * on the stack below n, we need to make a copy of these operands before
 * the assignment to var.
 */
static void
PinMVarAliases(CompEnv *ce, int var, Item *n)
{
    Item    *base = ce->ctxt->base;
    long    off = ce->varOff[var];

    sysAssert(off >= 0);
    while (--n >= base)
	if  (n->mr == MR_BASE + MR_ESP && n->offset == off)
	    FlushOpnd(ce, n);
	    // LoadOpnd does not work here:
	    //	1) type unknown (integer, float, or double)
	    //	2) FST would be out of order
}


/* Some assignment is to be done in non-local memory. Make sure that no operand
 * below n on the operand stack references non-local memory. Flush if necessary.
 */
static void
PinMemAliases(CompEnv *ce, Item *n)
{
    Item *base = ce->ctxt->base;

    while (--n >= base)
	if  (IsMem(n->mr) && n->mr != MR_BASE + MR_ESP)
	    FlushOpnd(ce, n);
}


/* Prepare operand stack for dup and swap opcodes. Do not allow more than one
 * operand on FST above and included the from parameter, flushing top-down
 */
static void
FlushMultipleFST(CompEnv *ce, Item *from)
{
    Item *topFST, *n;

    topFST = 0;
    for (n = ce->ctxt->top; --n >= from; )
    {
	if  (n->mr == MR_FST)
	{
	    if (topFST)
	    {
		FlushOpnd(ce, topFST);
		if  (topFST->size == 0)     // high part
		{
		    sysAssert(n + 1 == topFST && n->size == 8);
		    FlushOpnd(ce, n);
		    topFST = 0;
		    continue;
		}
	    }
	    topFST = n;
	}
    }
}


/* Move 1 (or 2) stack operand from src (and src + 1) to dst (and dst + 1).
 * Operands may overlap.
 */
static void
MoveOpnd(CompEnv *ce, Item *dst, Item *src, int size, RegSet hint)
{
    RegSet  targ;
    int     inc;
    Item    *s, *d, *end;

    if (size == 4 || dst < src)
    {	// move low part first
	inc = 1;
	s = src;
	d = dst;
	end = src + size/4;
    }
    else
    {	// move up 2 operands, move high part first
	inc = -1;
	s = src + 1;
	d = dst + 1;
	end = src - 1;
    }
    for ( ; s != end; s += inc, d += inc)
    {
	if  (s == src)
	// set targ to hint, hint is only for lower part
	    targ = hint;
	else
	    targ = RS_CALLER_SAVED;

	*d = *s;
	if  (s->mr == MR_BASE + MR_ESP
	     &&	s->offset == OpndOffset(ce, s))
	    // s is flushed, we have to really move it
	    LoadOpnd(ce, d, targ);
	    // d can stay in a reg even if it is a floating point,
	    // it will be flushed before being used
    }
}


/* Copy 1 or 2 stack operands from src to dst. If src is on FST, a copy is
 * spilled into memory. The topmost operand of either src or dst becomes then
 * FST, the other one is in memory. Operands may not overlap.
 */
static void
CopyOpnd(CompEnv *ce, Item *dst, Item *src, int size, RegSet hint)
{
    Item *mem, *end;

    end = src + size/4;
    for ( ; src != end; src++, dst++, hint = RS_CALLER_SAVED)
    {
	// reset hint to default, was only for lower part
	dst->size = src->size;
	dst->var = src->var;
	if  (src->mr == MR_FST)
	{
	    if (src < dst)
	    {
		mem = src;
		dst->mr = MR_FST;
	    }
	    else
		mem = dst;

	    mem->mr = MR_BASE + MR_ESP;
	    mem->offset = OpndOffset(ce, mem);
	    // flush the operand to bottom-most position of src and dst
	    // without popping it from FST
	    if (src->size > 0)
	    {
		check(src->size <= size);
		GenFStore(ce, mem, 1);	// no pop
	    }
	    else
		check(src == end - 1 && size == 8);

	    mem->size = 4;  // was 0, 4, or 8
	}
	else if (src->mr == MR_IMM)
	{
	    dst->mr = MR_IMM;
	    dst->offset = src->offset;
	}
	else
	{
	    sysAssert(src->mr < MR_IMM);
    	    if (!IsIntReg(src->mr) && size == 4)
	    {
    		// we load the operand in a reg to spare a second load
		// worse for single float in memory, should not be frequent
		LoadOpnd(ce, src, RS_CALLER_SAVED);
                dst->var = NOT_A_VAR;
            }
	    if (src->mr == MR_BASE + MR_ESP
		    && src->offset == OpndOffset(ce, src))
	    {
		// we can make a copy in a reg even if src is floating point,
		// it will be flushed before being used
		dst->mr = GetReg(ce, hint);
		GenLoad(ce, dst->mr, src);
		sysAssert(dst->var == NOT_A_VAR);
	    }
	    else
	    {
    		// src is not physically on the opstack, so we may
		// duplicate the item
		*dst = *src;
                IncRefCnt(ce, dst->mr);
	    }
	}
    }
}


/* Swap x and y on the operand stack.
 * x is just below y, which is top of stack; size is 4.
 */
static void
SwapOpnd(CompEnv *ce, Item *x, Item *y, RegSet hint)
{
    Item    h;

    FlushMultipleFST(ce, x);
    if (x->mr == MR_FST)
    {
	check(x->size > 0);
	if  (x->size == 8)
	    FlushOpnd(ce, x);	// swap high and low part of a double (?)
    }
    check(y->mr != MR_FST || y->size == 4);

    if (y->mr == MR_BASE + MR_ESP
	&&  y->offset == OpndOffset(ce, y)) // y is flushed
	LoadOpnd(ce, y, RS_CALLER_SAVED);   // this can flush x

    if (x->mr == MR_BASE + MR_ESP
	&&  x->offset == OpndOffset(ce, x)) // x is flushed
	LoadOpnd(ce, x, hint);	// this cannot flush y (== top)

    // now that the following 2 conditions hold, we can swap the items:
    //	- at most one of the operands is on FST and of size 4
    //	- none of the operands is flushed
    h = *y;
    *y = *x;
    *x = h;

    // an operand that was in memory or on FST can stay in a register,
    // because it will be flushed before being used as a float
}


static void
SymBinOp(CompEnv *ce, Item *x, Item *y, int op, int opimm, RegSet hint)
{
    // may be called from LSymBinOp with hint != GetHint(hintpc)
    Item    *l, *r;

    if (x->mr == MR_IMM && y->mr == MR_IMM)
    {
	switch	(opimm)
	{
	case ADDI:
	    x->offset += y->offset; break;
	case ANDI:
	    x->offset &= y->offset; break;
	case ORI:
	    x->offset |= y->offset; break;
	case XORI:
	    x->offset ^= y->offset; break;
	}
    }
    else
    {
	if  (x->mr == MR_IMM || MrFitsTarget(y->mr, hint))
	{
	    l = y;
	    r = x;
	}
	else
	{
	    l = x;
	    r = y;
	}
	if  ((regSetOfMr[r->mr] & hint & RS_CALLEE_SAVED) != RS_EMPTY)
	    // l cannot be loaded into callee-saved hint, because r is using it
	    hint = RS_CALLER_SAVED;

	LoadOpnd(ce, l, hint);
	FreeRegs(ce, r->mr);	// this cannot free caller-saved regs of l
	if  (r->mr == MR_IMM)
	    if (opimm == ADDI)
		GenAddImmRL(ce, l->mr, r->offset);
	    else
		GenOpImmR(ce, opimm, l->mr, r->offset, 4);
	else
	    GenOpSizRegEA(ce, op, l->mr, r);

	x->mr = l->mr;
    }
    x->var = NOT_A_VAR;
}


static void
SubOp(CompEnv *ce, Item *x, Item *y, unsigned char *hintpc)
{
    RegSet  hint = GetHint(ce, hintpc);

    if (x->mr == MR_IMM && y->mr == MR_IMM)
	x->offset -= y->offset;
    else
    {
	if  ((regSetOfMr[y->mr] & hint & RS_CALLEE_SAVED) != RS_EMPTY)
	    // x cannot be loaded into callee-saved hint, because y is using it
	    hint = RS_CALLER_SAVED;

	LoadOpnd(ce, x, hint);
	FreeRegs(ce, y->mr);	// this cannot free caller-saved regs of x
	if  (y->mr == MR_IMM)
	    GenOpImmR(ce, SUBI, x->mr, y->offset, 4);
	else
	    GenOpSizRegEA(ce, SUBR, x->mr, y);
    }
    x->var = NOT_A_VAR;
}


static void
MulOp(CompEnv *ce, Item *x, Item *y, unsigned char *hintpc)
{
    RegSet  hint = GetHint(ce, hintpc);
    Item    *l, *r;
    ModReg  mr;

    if (x->mr == MR_IMM && y->mr == MR_IMM)
	x->offset *= y->offset;
    else
    {
	if  (x->mr == MR_IMM || MrFitsTarget(y->mr, hint))
	{
	    l = y;
	    r = x;
	}
	else
	{
	    l = x;
	    r = y;
	}
	if  (r->mr == MR_IMM)
	{
	    if (r->offset > 0 && IsPower(r->offset))
	    {
		LoadOpnd(ce, l, hint);
		mr = l->mr;
		ShiftLeft(ce, mr, PowerOf(r->offset));
	    }
	    else
	    {
		// LoadOpnd not necessary with IMUL r32,r/m32,imm
		if  ((hint & RS_CALLEE_SAVED) != RS_EMPTY)
		{
		    PinRVarAliases(ce, hint, x);
		    FreeRegs(ce, l->mr);
		    mr = GetReg(ce, hint);	// no spill
                }
		else if (FreeRegSet(ce, l->mr) & hint)
		{
		    FreeRegs(ce, l->mr);
		    mr = GetReg(ce, hint);	// no spill
		}
		else
                {
    		    // the spill could destroy the freed l->mr
		    mr = GetReg(ce, hint);	// spill
                    FreeRegs(ce, l->mr);
                }
		if  (ByteRange(r->offset))
		{
		    GenOpSizRegEA(ce, IMULIB-1, mr, l);
		    GenByte(ce, r->offset);
		}
		else
		{
		    GenOpSizRegEA(ce, IMULI-1, mr, l);
		    GenLong(ce, r->offset);
		}
	    }
	}
	else
	{
	    if ((regSetOfMr[r->mr] & hint & RS_CALLEE_SAVED) != RS_EMPTY)
		// l cannot be loaded into callee-saved hint, r is using it
		hint = RS_CALLER_SAVED;

	    LoadOpnd(ce, l, hint);
	    mr = l->mr;
	    FreeRegs(ce, r->mr);    // this cannot free caller-saved regs of l
	    Gen2Bytes(ce, 0x0F, IMUL);
	    GenEA(ce, reg3Map[mr], r);
	}
	x->mr = mr;
    }
    x->var = NOT_A_VAR;
}


static void
DivRemOp(CompEnv *ce, Item *x, Item *y, unsigned char *hintpc, int div)
{
    RegSet  hint = GetHint(ce, hintpc);
    int     power;
    Label   skipLab;

    if (y->mr == MR_IMM && IsPower(y->offset) && y->offset != 0)
    {
	LoadOpnd(ce, x, hint);
	power = PowerOf(y->offset);
	if  (div)
	{
	    if (power == 1)
	    {
		ShiftRight(ce, x->mr, power, 4, 1);
		skipLab = 0;
		GenByte(ce, 0x70 + CC_NS);
		ShortLink(ce, &skipLab);
		GenOpImmR(ce, ADCI, x->mr, 0, 4);
		FixShortLink(ce, skipLab);  // keep same context
	    }
	    else
	    {
		GenOpSizRegEA(ce, TESTM, x->mr, x);
		skipLab = 0;
		GenByte(ce, 0x70 + CC_NS);
		ShortLink(ce, &skipLab);
		GenAddImmRL(ce, x->mr, y->offset-1);
		FixShortLink(ce, skipLab);  // keep same context
		ShiftRight(ce, x->mr, power, 4, power != 4*8-1);
	    }
	}
	else
	{
	    /*	Now, things get tricky:
		If the sign bit is off, we want to clear
		the bits to left, if it's on, we want to
		fill up with one bits. So we do this:

		and	n,%100..11
		jns	@@skip
		dec	n
		or	n,%011..00
		inc	n
	      @@skip:
	    */
	    unsigned long mask;

	    mask = (1UL << power) - 1;
	    mask |= 1UL << (4*8-1);
	    GenOpImm(ce, ANDI, x, mask);
	    skipLab = 0;
	    GenByte(ce, 0x70 + CC_NS);
	    ShortLink(ce, &skipLab);
	    GenByte(ce, DECR_L + regMap[x->mr]);
	    GenOpImm(ce, ORI, x, ~((1UL << power) - 1));
	    GenByte(ce, INCR_L + regMap[x->mr]);
	    FixShortLink(ce, skipLab);	// keep same context
	}
    }
    else
    {
	LoadOpnd(ce, x, RS_EAX);
	if  (y->mr == MR_IMM || (regSetOfMr[y->mr] & RS_EDX) != RS_EMPTY)
	    LoadOpnd(ce, y, RS_CALLER_SAVED - RS_EDX);

	GetReg(ce, RS_EDX);	// spill edx if necessary
	GenByte(ce, CDQ);
	GenOpSizEA(ce, 0xF6, IDIV, y);
	FreeRegs(ce, y->mr);
	if  (div)
	    FreeRegs(ce, MR_EDX);
	else
	{
	    FreeRegs(ce, MR_EAX);
	    x->mr = MR_EDX;
	}
    }
    x->var = NOT_A_VAR;
}


/* Increment variable var by val.
 * Watch for aliases of var on operand stack.
 */
static void
IncOp(CompEnv *ce, int var, long val)
{
    Item    n;
    ModReg  mr;
    long    off;

    off = ce->varOff[var];
    if (off < 0)
    {
	mr = -off;
	PinRVarAliases(ce, regSetOfMr[mr], ce->ctxt->top);
	GenAddImmRL(ce, mr, val);
    }
    else
    {
	n.mr = MR_BASE + MR_ESP;
	n.offset = off;
	n.size = 4;
	if  (val == -1 || val == +1)
	{
	    PinMVarAliases(ce, var, ce->ctxt->top);
	    GenOpSizEA(ce, 0xFE, val > 0 ? INC2 : DEC2, &n);
	    // memory var may be cached by a caller-saved reg,
	    // invalidate the caching reg:
	    mr = ce->ctxt->varCachingReg[var];
	    ce->ctxt->cachedVar[mr] = NOT_A_VAR;
	}
	else
	{
	    mr = GetReg(ce, RS_CALLER_SAVED);
	    GenLoad(ce, mr, &n);
	    PinMVarAliases(ce, var, ce->ctxt->top);
	    GenAddImmRL(ce, mr, val);
	    GenStore(ce, &n, mr);
	    FreeRegs(ce, mr);
	    CacheVar(ce, var, mr);
	}
    }
}


/* Add long <y, y + 1> to long <x, x + 1>
 */
static void
LAddOp(CompEnv *ce, Item *x, Item *y, unsigned char *hintpc)
{
    Item    *l, *r;
    RegSet  hint;
    ModReg  mr;
    int     carry, CFvalid;

    carry = 0;
    CFvalid = 0;
    // add low parts
    if (x->mr == MR_IMM && y->mr == MR_IMM)
    {
	carry = (unsigned long)x->offset > (unsigned long)(-1 - y->offset);
	x->offset += y->offset;
    }
    else
    {
	hint = GetHint(ce, hintpc);
	if  (x->mr == MR_IMM || MrFitsTarget(y->mr, hint))
	{
	    l = y;
	    r = x;
	}
	else
	{
	    l = x;
	    r = y;
	}
	if  (((regSetOfMr[r->mr] |
		regSetOfMr[(x + 1)->mr] | regSetOfMr[(y + 1)->mr])
	    & hint & RS_CALLEE_SAVED) != RS_EMPTY)
	    // l cannot be loaded into the callee-saved hint, because r or the
	    // higher operand is using it
	    hint = RS_CALLER_SAVED;

	LoadOpnd(ce, l, hint);
	FreeRegs(ce, r->mr);	// this cannot free caller-saved regs of l
	if  (r->mr == MR_IMM)
	    GenOpImmR(ce, ADDI, l->mr, r->offset, 4);
	else
	    GenOpSizRegEA(ce, ADDR, l->mr, r);

	CFvalid = 1;
	x->mr = l->mr;
    }
    x->var = NOT_A_VAR;
    y->mr = MR_IMM; // avoid spilling released operand

    // add high parts
    x++;
    y++;
    hint = GetHintHigh(ce, hintpc);
    if (x->mr == MR_IMM && y->mr == MR_IMM)
    {
	if  (CFvalid)
	{
	    mr = GetReg(ce, hint);
	    GenByte(ce, LOADI_L + regMap[mr]);	// XOR by GenLoad modifies cc!
	    GenLong(ce, 0);
	    GenOpImmR(ce, ADCI, mr, y->offset + x->offset, 4);
	    x->mr = mr;
	}
	else
	    x->offset += y->offset + carry;
    }
    else
    {
	if  (x->mr == MR_IMM || MrFitsTarget(y->mr, hint))
	{
	    l = y;
	    r = x;
	}
	else
	{
	    l = x;
	    r = y;
	}
	if  ((regSetOfMr[r->mr] & hint & RS_CALLEE_SAVED) != RS_EMPTY)
	    // l cannot be loaded into callee-saved hint, because r is using it
	    hint = RS_CALLER_SAVED;

	// If x - 1 is using the hint, it will be moved automatically
	sysAssert(l->mr != MR_IMM); // otherwise, CF could be modified
	LoadOpnd(ce, l, hint);
	FreeRegs(ce, r->mr);	// this cannot free caller-saved regs of l
	if  (r->mr == MR_IMM)
	    GenOpImmR(ce, CFvalid ? ADCI : ADDI, l->mr, r->offset + carry, 4);
	else
	{
	    GenOpSizRegEA(ce, CFvalid ? ADCR : ADDR, l->mr, r);
	    if (carry)
		GenByte(ce, INCR_L + regMap[l->mr]);
	}
	x->mr = l->mr;
    }
    x->var = NOT_A_VAR;
}


/* Subtract long <y, y + 1> from long <x, x + 1>
 */
static void
LSubOp(CompEnv *ce, Item *x, Item *y, unsigned char *hintpc)
{
    RegSet  hint;
    ModReg  mr;
    int     carry, CFvalid;

    carry = 0;
    CFvalid = 0;
    // subtract low parts
    if (x->mr == MR_IMM && y->mr == MR_IMM)
    {
	carry = (unsigned long)x->offset < (unsigned long)y->offset;
	x->offset -= y->offset;
    }
    else
    {
	hint = GetHint(ce, hintpc);
	if  (((regSetOfMr[y->mr] |
		regSetOfMr[(x + 1)->mr] | regSetOfMr[(y + 1)->mr])
	    & hint & RS_CALLEE_SAVED) != RS_EMPTY)
	    // x cannot be loaded into the callee-saved hint, because y or the
	    // higher operand is using it
	    hint = RS_CALLER_SAVED;

	LoadOpnd(ce, x, hint);
	FreeRegs(ce, y->mr);	// this cannot free caller-saved regs of x
	if  (y->mr == MR_IMM)
	    GenOpImmR(ce, SUBI, x->mr, y->offset, 4);
	else
	    GenOpSizRegEA(ce, SUBR, x->mr, y);

	CFvalid = 1;
    }
    x->var = NOT_A_VAR;
    y->mr = MR_IMM; // avoid spilling released operand

    // subtract high parts
    x++;
    y++;
    hint = GetHintHigh(ce, hintpc);
    if (x->mr == MR_IMM && y->mr == MR_IMM)
    {
	if  (CFvalid)
	{
	    mr = GetReg(ce, hint);
	    GenByte(ce, LOADI_L + regMap[mr]);	// XOR by GenLoad modifies cc!
	    GenLong(ce, 0);
	    GenOpImmR(ce, SBBI, mr, y->offset - x->offset, 4);
	    x->mr = mr;
	}
	else
	    x->offset -= y->offset + carry;
    }
    else
    {
	if  ((regSetOfMr[y->mr] & hint & RS_CALLEE_SAVED) != RS_EMPTY)
	    // x cannot be loaded into callee-saved hint, because y is using it
	    hint = RS_CALLER_SAVED;

	// If x - 1 is using the hint, it will be moved automatically
	if  (CFvalid && x->mr == MR_IMM)
	{
	    mr = GetReg(ce, hint);
	    GenByte(ce, LOADI_L + regMap[mr]);
	    GenLong(ce, x->offset);
	    x->mr = mr;
	    // otherwise, CF could be modified by LoadOpnd
	}
	else
	    LoadOpnd(ce, x, hint);

	FreeRegs(ce, y->mr);	// this cannot free caller-saved regs of x
	if  (y->mr == MR_IMM)
	    GenOpImmR(ce, CFvalid ? SBBI : SUBI, x->mr, y->offset + carry, 4);
	else
	{
	    GenOpSizRegEA(ce, CFvalid ? SBBR : SUBR, x->mr, y);
	    if (carry)
		GenByte(ce, DECR_L + regMap[x->mr]);
	}
    }
    x->var = NOT_A_VAR;
}


static void
LNegOp(CompEnv *ce, Item *x, unsigned char *hintpc)
{
    RegSet  hint;
    int     carry, CFvalid;
    ModReg  mr;

    carry = 0;
    CFvalid = 0;
    // negate low part
    if (x->mr == MR_IMM)
    {
	carry = x->offset != 0;
	x->offset = -x->offset;
    }
    else
    {
	hint = GetHint(ce, hintpc);
	if  ((regSetOfMr[(x + 1)->mr] & hint & RS_CALLEE_SAVED) != RS_EMPTY)
	    // x cannot be loaded into the callee-saved hint, because the
	    // higher operand is using it
	    hint = RS_CALLER_SAVED;

	LoadOpnd(ce, x, hint);
	GenOpSizEA(ce, 0xF6, NEG, x);
	CFvalid = 1;
    }
    x->var = NOT_A_VAR;

    // negate high part
    x++;
    hint = GetHintHigh(ce, hintpc);
    if (CFvalid)
    {
	if  ((regSetOfMr[x->mr] & hint & RS_CALLEE_SAVED) != RS_EMPTY)
	    // zero cannot be loaded into the callee-saved hint, because x is
	    // using it
	    hint = RS_CALLER_SAVED;

	// If x - 1 is using the hint, it will be moved automatically
	mr = GetReg(ce, hint);
	GenByte(ce, LOADI_L + regMap[mr]);  // XOR modifies cc!
	GenLong(ce, 0);
	if  (x->mr == MR_IMM)
	    GenOpImmR(ce, SBBI, mr, x->offset, 4);
	else
	{
	    GenOpSizRegEA(ce, SBBR, mr, x);
	    FreeRegs(ce, x->mr);
	}
	x->mr = mr;
    }
    else
    {
	if  (x->mr == MR_IMM)
	    x->offset = -(x->offset + carry);
	else
	{
	    LoadOpnd(ce, x, hint);
	    GenOpSizEA(ce, 0xF6, NEG, x);
	    if (carry)
		GenByte(ce, DECR_L + regMap[x->mr]);
	}
    }
    x->var = NOT_A_VAR;
}


static void
LSymBinOp(CompEnv *ce, Item *x, Item *y, int op, int opimm,
    unsigned char *hintpc)
{
    RegSet hint;

    hint = GetHint(ce, hintpc);
    if ((  (regSetOfMr[(x + 1)->mr] | regSetOfMr[(y + 1)->mr])
	 &  hint & RS_CALLEE_SAVED) != RS_EMPTY)
	// hint is useless, because the higher operand uses the hint register
	hint = RS_CALLER_SAVED;

    SymBinOp(ce, x, y, op, opimm, hint);
    y->mr = MR_IMM; // avoid spilling released operand
    hint = GetHintHigh(ce, hintpc);
    SymBinOp(ce, x + 1, y + 1, op, opimm, hint);
}


static void
ShiftOp(CompEnv *ce, Item *x, Item *y, int left, int signedFlag,
    unsigned char *hintpc)
{
    RegSet  hint = GetHint(ce, hintpc);
    long    cnt;
    char    byte2;

    if (y->mr == MR_IMM)
    {
	cnt = y->offset & 0x1F;
	LoadOpnd(ce, x, hint);
	if  (left)
	    ShiftLeft(ce, x->mr, cnt);
	else
	    ShiftRight(ce, x->mr, cnt, 4, signedFlag);
    }
    else
    {
	if  ((regSetOfMr[x->mr] & RS_ECX) == RS_EMPTY)
	{
	    LoadOpnd(ce, y, RS_ECX);
	    LoadOpnd(ce, x, hint);
	}
	else
	{
	    hint &= ~RS_ECX;
	    if ((regSetOfMr[y->mr] & hint & RS_CALLEE_SAVED) != RS_EMPTY)
		hint = RS_CALLER_SAVED - RS_ECX;

	    LoadOpnd(ce, x, hint);
	    LoadOpnd(ce, y, RS_ECX);
	}
	byte2 = sibModRmTab[x->mr];
	if  (left)
	    byte2 += SHL;
	else if (signedFlag)
	    byte2 += SAR;
	else
	    byte2 += SHR;
	Gen2Bytes(ce, 0xD3, byte2);
	FreeRegs(ce, MR_ECX);
    }
    x->var = NOT_A_VAR;
}


static void
RealOp(CompEnv *ce, Item *x, Item *y, int op, int opr, int notSym, int size)
{
    int swap;

    if (x->mr == MR_FST)
    {
	sysAssert(x->size == size);
	MakeFOpndAccessible(ce, y, size);
	y->size = size;
	GenFOpEA(ce, op, opr, y);
	FreeRegs(ce, y->mr);
	x->var = NOT_A_VAR;
	if  (size == 8)
        {
            FreeRegs(ce, (y + 1)->mr);
	    (x + 1)->var = NOT_A_VAR;
	}
    }
    else
    {
	LoadFOpnd(ce, y, size);
	MakeFOpndAccessible(ce, x, size);
	x->size = size;
	swap = notSym*(FSUBR - FSUB);
	GenFOpEA(ce, op + swap, opr - swap, x);
	FreeRegs(ce, x->mr);
	x->mr = MR_FST;
	x->var = NOT_A_VAR;
	if (size == 8)
	{
            FreeRegs(ce, (x + 1)->mr);
	    (x + 1)->mr = MR_FST;
	    (x + 1)->size = 0;
	    (x + 1)->var = NOT_A_VAR;
	}
    }
}


static void
CallCompSupport(CompEnv *ce, char *funcAddr, Item *firstPar, long parSize)
{
    PinTempMemAliases(ce, firstPar);
    GenByteLong(ce, CALLN, funcAddr - ce->pc - 5);
    ce->ctxt->espLevel += parSize;
    FlushVarCache(ce->ctxt);
    sysAssert(ce->ctxt->regRefCnt[MR_EAX] == 0
	      && ce->ctxt->regRefCnt[MR_EDX] == 0
	      && ce->ctxt->regRefCnt[MR_ECX] == 0);
}


static void
PushImm(CompEnv *ce, long val)
{
    if (ByteRange(val))
	Gen2Bytes(ce, PUSHI_B, val);
    else
	GenByteLong(ce, PUSHI_L, val);
    ce->ctxt->espLevel -= 4;
}


/* Store a 32-bit dword from the operand stack to a variable
 */
static void
StoreOpnd(CompEnv *ce, int var, Item *n)
{
    long    off;
    Item    v;
    ModReg  mr;
    RegSet  targ;

    off = ce->varOff[var];
    if (off < 0)   // var is a callee-saved reg
    {
	targ = regSetOfMr[-off];
	// PinRVarAliases(ce, targ, n); called in LoadOpnd
	LoadOpnd(ce, n, targ);
    }
    else if (n->mr != MR_BASE + MR_ESP || n->offset != off)
    {
	v.mr = MR_BASE + MR_ESP;
	v.offset = off;
	v.size = 4;
	LoadOpnd(ce, n, RS_ALL);
	mr = n->mr;
	PinMVarAliases(ce, var, n);
	GenStore(ce, &v, mr);
	FreeRegs(ce, mr);
	CacheVar(ce, var, mr);
    }
}


/* Store a float (or a double) from the operand stack to one (or two) variables
 */
static void
StoreFOpnd(CompEnv *ce, int var, Item *n, int size)
{
    long    off;
    Item    v;

    // calling MakeFOpndAccessible is not optimal here: we recode it inline
    // using StoreOpnd instead of FlushOpnd and we are done
    if (size == 8 && (IsIntReg(n->mr) || IsIntReg((n + 1)->mr)))
    {
	sysAssert(n->size == 4 && (n + 1)->size == 4);
	StoreOpnd(ce, var, n);
	StoreOpnd(ce, var + 1, n + 1);
    }
    else if (size == 4 && IsIntReg(n->mr))
    {
	sysAssert(n->size == 4);
	StoreOpnd(ce, var, n);
    }
    else
    {
	off = ce->varOff[var];
	sysAssert(off >= 0);
	if  (n->mr != MR_BASE + MR_ESP || n->offset != off)
	{
	    // we use the fpu
	    PinMVarAliases(ce, var, n);
	    if (size == 8)
	    {
		PinMVarAliases(ce, var + 1, n);
		if  (!IsQuadAccessibleOpnd(n))
		{
		    // when ??
		    FlushOpnd(ce, n);
		    FlushOpnd(ce, n + 1);
		    sysAssert(IsQuadAccessibleOpnd(n));
		}
	    }
	    v.mr = MR_BASE + MR_ESP;
	    v.offset = off;
	    v.size = size;
	    if (n->mr != MR_FST)
	    {
		n->size = size; // may change from 4 to 8
		FreeRegs(ce, n->mr);
		if  (size == 8)
		    FreeRegs(ce, (n + 1)->mr);
		GenFLoad(ce, n);
	    }
	    GenFStore(ce, &v, 0);
	}
    }
}


static void
Int2BW(CompEnv *ce, Item *n, int size, int op, unsigned char *hintpc)
{
    RegSet  hint = GetHint(ce, hintpc);
    ModReg  mr;

    mr = n->mr;
    if (mr == MR_IMM)
    	if (size == 1)
	    n->offset = (signed char)n->offset;		// opc_int2byte
	else if (op == 0xB7)
            n->offset = (unsigned short)n->offset;	// opc_int2char
	else
            n->offset = (signed short)n->offset;	// opc_int2short
    else
    {
	if (!IsByteAccessible(mr))
	{
	    LoadOpnd(ce, n, RS_BYTE & RS_CALLER_SAVED);
	    mr = n->mr;
	}
	else    // memory or byte register
	{
	    if  ((hint & RS_CALLEE_SAVED) != RS_EMPTY)
	    {
		PinRVarAliases(ce, hint, n);
		FreeRegs(ce, mr);
		mr = GetReg(ce, hint);	// no spill
	    }
	    else if (FreeRegSet(ce, mr) & hint)
	    {
		FreeRegs(ce, mr);
		mr = GetReg(ce, hint);	// no spill
	    }
	    else
	    {
		// the spill could destroy the freed n->mr
		mr = GetReg(ce, hint);	// spill
		FreeRegs(ce, n->mr);
	    }
        }
	n->size = size;
	Gen2Bytes(ce, 0x0F, op);
	GenEA(ce, reg3Map[mr], n);	// mov(s|z)x erx, byte|word
	n->mr = mr;
    }
    n->size = 4;
    n->var = NOT_A_VAR;
}


static void
Jcc(CompEnv *ce, CondCode cc, RangeHdr *rh, Item *top)
{
    long    dist;
    char    *targetpc;
    Label   *targetlink;


    if (rh->pc)
    {
	// backward jump
	sysAssert(rh->pc < ce->pc);
	FlushOpStack(ce, top);

	if  (ce->xctxt != rh->xctxt)
	{
	    sysAssert(rh->rangeFlags & RF_SET_XCTXT);
	    targetpc = rh->xpc;
	}
	else
	    targetpc = rh->pc;

	dist = targetpc - ce->pc;
	if  (cc == CC_JSR)
	    GenByteLong(ce, CALLN, dist - 5);
	else if (ByteRange(dist - 2))
	{
	    if (cc == CC_JMP)
		GenByte(ce, 0xEB);
	    else
		GenByte(ce, 0x70 + cc);

	    GenByte(ce, dist - 2);
	}
	else
	{
	    if (cc == CC_JMP)
	    {
		GenByte(ce, 0xE9);
		GenLong(ce, dist - 5);
	    }
	    else
	    {
		Gen2Bytes(ce, 0x0F, 0x80 + cc);
		GenLong(ce, dist - 6);
	    }
	}
    }
    else
    {
	// forward jump
	if  (rh->joinCnt > 0)
	    FlushOpStack(ce, top);

	if  (ce->xctxt != rh->xctxt)
	{
	    sysAssert(rh->rangeFlags & RF_SET_XCTXT);
	    targetlink = &rh->xlink;
	}
	else
	    targetlink = &rh->link;

	if  (cc == CC_JSR)
	    GenByte(ce, CALLN);
	else if (cc == CC_JMP)
	    GenByte(ce, 0xE9);
	else
	    Gen2Bytes(ce, 0x0F, 0x80 + cc);

	Link(ce, targetlink);
    }
}


/* special version of Jcc for switch tables:
 * always 5 bytes total, no call to FlushOpStack (done in Body)
 */
static void
SwitchTableJmp(CompEnv *ce, RangeHdr *rh)
{
    char    *targetpc;
    Label   *targetlink;


    if (rh->pc)
    {
	// backward jump
	sysAssert(rh->pc < ce->pc);

	if  (ce->xctxt != rh->xctxt)
	{
	    sysAssert(rh->rangeFlags & RF_SET_XCTXT);
	    targetpc = rh->xpc;
	}
	else
	    targetpc = rh->pc;

	GenByte(ce, 0xE9);
	GenLong(ce, targetpc - ce->pc - 4);
    }
    else
    {
	// forward jump
	if  (ce->xctxt != rh->xctxt)
	{
	    sysAssert(rh->rangeFlags & RF_SET_XCTXT);
	    targetlink = &rh->xlink;
	}
	else
	    targetlink = &rh->link;

	GenByte(ce, 0xE9);
	Link(ce, targetlink);
    }
}


static CondCode
GetCondition(unsigned char *pc)
{
    switch  (*pc)
    {
    case opc_ifeq:
    case opc_ifnull:
	return CC_E;

    case opc_ifne:
    case opc_ifnonnull:
	return CC_NE;

    case opc_iflt:
	return CC_L;

    case opc_ifle:
	return CC_LE;

    case opc_ifgt:
	return CC_G;

    case opc_ifge:
	return CC_GE;

    }
    return CC_NONE;
}


/* Compare x with y, both float or double. y is on top of x on the operand stack.
 * Both x and y may be on FST or in memory, but y cannot be below x on FST.
 */
static void
FCompare(CompEnv *ce, Item *x, Item *y, int size, int NaNres,
    unsigned char *nextpc)
{
    CondCode	cc;
    RangeHdr	*rh;
    Label	loadm1, loadp1, done;

    cc = GetCondition(nextpc);
#ifdef	STRICT_FLOATING
    if (cc == CC_NONE || cc == CC_E || cc == CC_NE)
    {
    	// we must use 64-bit precision to conform to the spec,	i.e. flush the
	// operands to memory, extended precision would not satisfy
	// 1.0e+308d + Double.MAX_VALUE == Double.POSITIVE_INFINITY
	if (y->mr == MR_FST)
	    FlushOpnd(ce, y);
        if (x->mr == MR_FST)
	    FlushOpnd(ce, x);
    }
#endif
    LoadFOpnd(ce, y, size);	// make sure y is on FST(0)
    MakeFOpndAccessible(ce, x, size);
    x->size = size;
    GenFOpEA(ce, FCOMP, FCOMP, x);
    FreeRegs(ce, x->mr);
    if (size == 8)
	FreeRegs(ce, (x + 1)->mr);
    GetReg(ce, RS_EAX);     // must preserve the fpu condition code!
    Gen2Bytes(ce, 0xDF, FNSTSTWAX);
    GenByte(ce, SAHF);
    // the condition code holds the result of y compared to x (unsigned)
    // y <  x:	-   -	CF
    // y >  x:	-   -	-
    // y == x:	ZF  -	-
    // NaN:	ZF  PF	CF
    if (cc != CC_NONE)
    {
	// the next opcode compiles to a Jcc instruction
	FreeRegs(ce, MR_EAX);
	rh = ce->rp[nextpc + pc2signedshort(nextpc) - ce->mb->code].hdr;

	if  (rh->joinCnt > 0)
	    FlushOpStack(ce, x);    // before jump! var cache flushed later

	cc = xsgnCC[swapCC[cc]];    // swapped operands and unsigned test
	x->size = cc;
	x->mr = MR_CC;
	x->offset = 0;	// continuation link

	// if NaN:
	//	JB, JBE, JE, JAE are taken
	//	JNE, JA are not taken
	// if NaNres < 0
	//	JB, JBE, JA do what we want
	//	JE, JAE, JNE need a correction:
	//	    JNE 	-> JPE target
	//	    JE, JAE	-> JPE continue
	// if NaNres > 0
	//	JAE do what we want
	//	JB, JBE, JE, JNE, JA need a correction:
	//	    JNE, JA	    -> JPE target
	//	    JB, JBE, JE     -> JPE continue

	if  (cc == CC_NE
	     || NaNres > 0 && cc == CC_A)
	{
	    Jcc(ce, CC_PE, rh, x);	// jpe target
	}
	else if (cc == CC_E
		 || NaNres < 0 && cc == CC_AE
		 || NaNres > 0 && (cc == CC_B || cc == CC_BE))
	{
	    GenByte(ce, 0x70 + CC_PE);	    // jpe continue
	    ShortLink(ce, &((Label)x->offset));
	}
    }
    else
    {
	// We need to generate -1, 0, or +1.
	loadm1 = 0;
	loadp1 = 0;
	done = 0;
	if  (NaNres < 0)
	{
	    // Emit JB first so that NaN yields -1
	    GenByte(ce, 0x70 + CC_B);
	    ShortLink(ce, &loadm1);
	    GenByte(ce, 0x70 + CC_A);
	    ShortLink(ce, &loadp1);
	}
	else
	{
	    // Emit JA first so that NaN yields +1
	    GenByte(ce, 0x70 + CC_A);
	    ShortLink(ce, &loadp1);
	    GenByte(ce, 0x70 + CC_B);
	    ShortLink(ce, &loadm1);
	}
	GenOpRegReg(ce, XORR_L, MR_EAX, MR_EAX);
	GenByte(ce, 0xEB);  // jmp done
	ShortLink(ce, &done);
	FixShortLink(ce, loadm1);   // keep same context
	GenOpImmR(ce, ORI, MR_EAX, -1, 4);
	GenByte(ce, 0xEB);  // jmp done
	ShortLink(ce, &done);
	FixShortLink(ce, loadp1);   // keep same context
	GenByte(ce, LOADI_L + regMap[MR_EAX]);
	GenLong(ce, 1);
	FixShortLink(ce, done);     // keep same context
	x->mr = MR_EAX;
	x->size = 4;
    }
    x->var = NOT_A_VAR;
}


/* Compare x with y, both long. y is on top of x on the operand stack.
 */
static void
LCompare(CompEnv *ce, Item *x, Item *y, unsigned char *nextpc)
{
    CondCode	cc;
    RangeHdr	*rh;
    Label	cont, loadm1, loadp1, done;
    ModReg	mr;

    LoadOpnd(ce, x + 1, RS_ALL);
    GenCmpRegEA(ce, (x + 1)->mr, y + 1);    // cmp high(x), high(y)
    FreeRegs(ce, (x + 1)->mr);
    (x + 1)->mr = MR_IMM;   // avoid spilling released operand
    FreeRegs(ce, (y + 1)->mr);
    (y + 1)->mr = MR_IMM;   // avoid spilling released operand

    // Do not try to load x before the compare above (register pressure)
    // Emit spill code before jumping and do not destroy condition code:
    mr = x->mr;
    if (mr == MR_IMM)
    {
	mr = GetReg(ce, RS_CALLER_SAVED);
	GenByte(ce, LOADI_L + regMap[mr]);  // XOR by GenLoad modifies cc!
	GenLong(ce, x->offset);
	x->mr = mr;
    }
    else if (!IsIntReg(mr))
    	LoadOpnd(ce, x, RS_CALLER_SAVED);

    x->var = NOT_A_VAR; // item is loaded on the operand stack now
    cc = GetCondition(nextpc);
    if (cc != CC_NONE)
    {
	// the next opcode compiles to a Jcc instruction
	rh = ce->rp[nextpc + pc2signedshort(nextpc) - ce->mb->code].hdr;
	cont = 0;

	if  (rh->joinCnt > 0)
	    FlushOpStack(ce, x);    // before jump!

	if  (cc == CC_E)
	{
	    GenByte(ce, 0x70 + CC_NE);
	    ShortLink(ce, &cont);
	}
	else if (cc == CC_NE)
	    Jcc(ce, CC_NE, rh, x);
	else
	{
	    Jcc(ce, highCC[cc], rh, x);
	    GenByte(ce, 0x70 + revhCC[cc]);
	    ShortLink(ce, &cont);
	}
	// no need to flush var cache because read only and no new caching regs
	sysAssert(IsIntReg(x->mr));
	GenCmpRegEA(ce, x->mr, y);  // cmp low(x), low(y)
	FreeRegs(ce, x->mr);
	FreeRegs(ce, y->mr);
	x->mr = MR_CC;
	x->size = xsgnCC[cc];	// unsigned compare
	x->offset = (long)cont;
    }
    else
    {
	// We need to generate -1, 0, or +1.
	loadm1 = 0;
	loadp1 = 0;
	done = 0;
	mr = GetReg(ce, RS_CALLER_SAVED);   // must preserve the condition code
	// GetReg must be called now to avoid spill code between jumps, which
	// would modify the context
	GenByte(ce, 0x70 + CC_L);
	ShortLink(ce, &loadm1);
	GenByte(ce, 0x70 + CC_G);
	ShortLink(ce, &loadp1);
	// no need to flush var cache because read only and no new caching regs
	sysAssert(IsIntReg(x->mr));
	GenCmpRegEA(ce, x->mr, y);  // cmp low(x), low(y)
	FreeRegs(ce, x->mr);
	FreeRegs(ce, y->mr);
	GenByte(ce, 0x70 + CC_B);
	ShortLink(ce, &loadm1);
	GenByte(ce, 0x70 + CC_A);
	ShortLink(ce, &loadp1);
	GenOpRegReg(ce, XORR_L, mr, mr);
	GenByte(ce, 0xEB);  // jmp done
	ShortLink(ce, &done);
	FixShortLink(ce, loadm1);   // keep same context
	GenOpImmR(ce, ORI, mr, -1, 4);
	GenByte(ce, 0xEB);  // jmp done
	ShortLink(ce, &done);
	FixShortLink(ce, loadp1);   // keep same context
	GenByte(ce, LOADI_L + regMap[mr]);
	GenLong(ce, 1);
	FixShortLink(ce, done);     // keep same context
	x->mr = mr;
	x->size = 4;
    }
    x->var = NOT_A_VAR;
}


static void
Ifcc(CompEnv *ce, Item *n, CondCode cc, unsigned char *pc)
{
    RangeHdr *rh = ce->rp[pc + pc2signedshort(pc) - ce->mb->code].hdr;

    if (n->mr == MR_CC)
    {
	// n->offset is the continuation link, n->size is the corrected cc
	// stack is flushed already
	Jcc(ce, n->size, rh, n);
	FixShortLink(ce, (Label)n->offset); // continue here, keep same context
    }
    else if (n->mr == MR_IMM)
    {
	// bytecode is not optimal, bytecode compiler should do constant folding
	if ((cc == CC_E) == (n->offset == 0))
	    Jcc(ce, CC_JMP, rh, n);
    }
    else
    {
	GenOpImm(ce, CMPI, n, 0);
	FreeRegs(ce, n->mr);
	Jcc(ce, cc, rh, n);
    }
}


static void
IfCmpcc(CompEnv *ce, Item *x, Item *y, CondCode cc, unsigned char *pc)
{
    RangeHdr	*rh = ce->rp[pc + pc2signedshort(pc) - ce->mb->code].hdr;
    Item	*l, *r;

    if (x->mr == MR_IMM || IsIntReg(y->mr))
    {
	l = y;
	r = x;
	cc = swapCC[cc];
    }
    else
    {
	l = x;
	r = y;
    }
    LoadOpnd(ce, l, RS_ALL);
    if (r->mr == MR_IMM)
	GenOpImmR(ce, CMPI, l->mr, r->offset, 4);
    else
    {
	GenOpSizRegEA(ce, CMPR, l->mr, r);
	FreeRegs(ce, r->mr);
    }
    FreeRegs(ce, l->mr);
    Jcc(ce, cc, rh, x);
}


static void
SortCases(Case *start, Case *end)	// sort [start..end[
{
    Case	*a, *b;
    long	key;
    RangeHdr	*rh;

    a = start;
    while (++a < end) // sort keys in increasing order (insertion sort)
    {
	key = a->key;
	rh = a->targRh;
	b = a;
	while	(--b >= start && b->key > key)
	    b[1] = b[0];
	b[1].key = key;
	b[1].targRh = rh;
    }
}


static void
TableJmp(CompEnv *ce, ModReg mr,
		Case *firstCase, Case *lastCase, RangeHdr *defRh)
{
    long    lo, hi;
    Item    table, *top;

    top = ce->ctxt->base;   // stack is flushed already
    lo = firstCase->key;
    hi = lastCase->key;
    if (lo > 0 && lo <= 2)
	lo = 0;
    GenAddImmRL(ce, mr, -lo);
    hi -= lo;
    GenOpImmR(ce, CMPI, mr, hi, 4);
    Jcc(ce, CC_A, defRh, top);
    // lea mr, table[4*mr + mr]
    table.mr = MR_BINX + (mr << 3) + mr;
    table.size = 4;
    table.scale = 2;
    table.offset = (long)ce->pc + 9;
    GenByte(ce, LEA_L);
    GenEA(ce, reg3Map[mr], &table);
    // jmp mr
    Gen2Bytes(ce, 0xFF, JMPI + sibModRmTab[mr]);
    hi = lo;
    for (;;)
    {
	while	(hi < firstCase->key)
	{
	    SwitchTableJmp(ce, defRh);
	    hi++;
	}
	SwitchTableJmp(ce, firstCase->targRh);
	hi++;
	if  (firstCase != lastCase)
	    firstCase++;
	else
	    break;
    }
}


static void
SeqJmp(CompEnv *ce, ModReg mr, long siz,
	    Case *firstCase, Case *lastCase, RangeHdr *defRh)
{
    long    key, prev, sub;
    Item    *top;
    int     ccSet;

    top = ce->ctxt->base;   // stack is flushed already
    prev = 0;
    ccSet = 0;
    for (;;)
    {
	key = firstCase->key;
	sub = key - prev;
	if  (sub == 0)
	{
	    if (ccSet)
		Jcc(ce, CC_E, firstCase->targRh, top);
	    else
	    {
		GenSubImmRSetCC(ce, mr, 1, siz);
		Jcc(ce, CC_B, firstCase->targRh, top);
		prev = key + 1;
		ccSet = 1;
	    }
	}
	else
	{
	    if (sub == 1)
	    {
		if  (siz == 4)
		    GenByte(ce, DECR_L + regMap[mr]);
		else
		    Gen2Bytes(ce, 0xFE + GenSiz(ce, siz), DEC2 + sibModRmTab[mr]);
	    }
	    else
		GenSubImmRSetCC(ce, mr, sub, siz);

	    Jcc(ce, CC_E, firstCase->targRh, top);
	    prev = key;
	    ccSet = 0;
	}
	if  (firstCase != lastCase)
	    firstCase++;
	else
	    break;
    }
    Jcc(ce, CC_JMP, defRh, top);
}


static void
Switch(CompEnv *ce, ModReg mr,
	    Case *firstCase, Case *lastCase, RangeHdr *defRh);

static void
BinJmp(CompEnv *ce, ModReg mr,
	    Case *firstCase, Case *lastCase, RangeHdr *defRh)
{
    Case    *middle;
    Label   upperLab;
    Item    *top;

    top = ce->ctxt->base;   // stack is flushed already
    middle = firstCase + ((lastCase - firstCase) / 2);
    GenOpImmR(ce, CMPI, mr, middle->key, 4);
    upperLab = 0;
    Gen2Bytes(ce, 0x0F, 0x80 + CC_G);
    Link(ce, &upperLab);
    Jcc(ce, CC_E, middle->targRh, top);
    Switch(ce, mr, firstCase, middle - 1, defRh);
    FixLink(ce, upperLab);
    Switch(ce, mr, middle + 1, lastCase, defRh);
}


static void
Switch(CompEnv *ce, ModReg mr,
	    Case *firstCase, Case *lastCase, RangeHdr *defRh)
{
    long    range;
    long    labCnt;

    range = lastCase->key - firstCase->key + 1;
    labCnt = lastCase - firstCase + 1;
    if (labCnt <= 4)
	SeqJmp(ce, mr, 4, firstCase, lastCase, defRh);
    else if (range > 4*labCnt)
	BinJmp(ce, mr, firstCase, lastCase, defRh);
    else
	TableJmp(ce, mr, firstCase, lastCase, defRh);
}


static void
PutField(CompEnv *ce, Item *obj, long offset, Item *val, int size)
{
    Item mem;	// do not use opnd obj as a temp
#ifndef	HANDLE_IN_OBJECT
    int var;
#endif

    sysAssert(obj >= ce->ctxt->base && obj < ce->ctxt->top);
    PinMemAliases(ce, obj);
#ifdef	HANDLE_IN_OBJECT
    LoadOpnd(ce, obj, RS_ALL);
    mem.offset = offset + 8;
#else
    var = obj->var;
    if (var == NOT_A_VAR || !LookupVarHCache(ce, obj, var))
    {
	LoadOpnd(ce, obj, RS_ALL);
	obj->mr += MR_BASE;
	obj->offset = offsetof(JHandle, obj);
	LoadOpnd(ce, obj, RS_ALL);
	if (var != NOT_A_VAR)
	    CacheVarH(ce, var, obj->mr);
    }
    mem.offset = offset;
#endif
    mem.mr = obj->mr + MR_BASE;
    mem.size = 4;
    obj->mr = MR_IMM;	// avoid spilling released operand
    if (val->mr != MR_FST)
    {
	LoadOpnd(ce, val, RS_ALL);
	GenStore(ce, &mem, val->mr);
	FreeRegs(ce, val->mr);
        val->mr = MR_IMM;	// avoid spilling released operand
	if  (size == 8)
	{
	    mem.offset += 4;
	    LoadOpnd(ce, val + 1, RS_ALL);
	    GenStore(ce, &mem, (val + 1)->mr);
	    FreeRegs(ce, (val + 1)->mr);
	}
    }
    else
    {
	sysAssert(val->size == size);
	mem.size = size;
	GenFStore(ce, &mem, 0);
    }
    FreeRegs(ce, mem.mr);
}


static void
GetField(CompEnv *ce, Item *top, long offset, int size, unsigned char *hintpc)
{
    RegSet hint;
#ifndef	HANDLE_IN_OBJECT
    int	   var;
#endif

#ifdef	HANDLE_IN_OBJECT
    if (FreeRegSet(ce, top->mr))
	// use a caller-saved register for better register
	// cache hit rate in following accesses
	hint = RS_ALL;
    else
	// use the hint to avoid a spill
	hint = GetHint(ce, hintpc);

    LoadOpnd(ce, top, hint);
    top->offset = offset + 8;
#else
    var = top->var;
    if (var == NOT_A_VAR  || !LookupVarHCache(ce, top, var))
    {
    	if (FreeRegSet(ce, top->mr))
    	    // use a caller-saved register for better register
	    // cache hit rate in following accesses
            hint = RS_ALL;
        else
    	    // use the hint to avoid a spill
	    hint = GetHint(ce, hintpc);

	LoadOpnd(ce, top, hint);
	top->mr += MR_BASE;
	top->offset = offsetof(JHandle, obj);
	LoadOpnd(ce, top, hint);
	if (var != NOT_A_VAR)
	    CacheVarH(ce, var, top->mr);
    }
    top->offset = offset;
#endif
    top->mr += MR_BASE;
    top->var = NOT_A_VAR;
    if (size == 8)
    {
	(top + 1)->mr = top->mr;
	(top + 1)->offset = offset + 4;
	(top + 1)->size = 4;
	(top + 1)->var = NOT_A_VAR;
        IncRefCnt(ce, top->mr);
	// If the base is a callee-saved register and if the higher part of the
	// qword field is to be assigned to this base (which we don't know,
	// because the hint is for the lower part), the code will not be
	// efficient because PinRVarAliases will flush the lower part before
	// assigning the higher part. This should not happen very often, since
	// a local should not be used as an object and as the higher part of
	// a long at the same time.
	// On the other hand, if the lower part is to be assigned to the base
	// (which can happen more often, because of the hint asking to use
	// this callee-saved as the base, thereby using one less register), we
	// don't have aliasing problems, since the higher part is assigned
	// first to another register or memory.
	// Note that the higher part MUST be assigned first by StoreOpnd for
	// PinRVarAliases to work correctly! (top is assumed to be n)
    }
}


static void
PutStatic(CompEnv *ce, long adr, Item *val, int size)
{
    Item mem;

    sysAssert(val >= ce->ctxt->base && val < ce->ctxt->top);
    PinMemAliases(ce, val);
    mem.mr = MR_ABS;
    mem.offset = adr;
    if (val->mr != MR_FST)
    {
	mem.size = 4;
	LoadOpnd(ce, val, RS_ALL);
	GenStore(ce, &mem, val->mr);
	FreeRegs(ce, val->mr);
	if  (size == 8)
	{
	    mem.offset += 4;
	    LoadOpnd(ce, val + 1, RS_ALL);
	    GenStore(ce, &mem, (val + 1)->mr);
	    FreeRegs(ce, (val + 1)->mr);
	}
    }
    else
    {
	sysAssert(val->size == size);
	mem.size = size;
	GenFStore(ce, &mem, 0);
    }
}


static void
GetStatic(long offset, Item *top, int size)
{
    top->mr = MR_ABS;
    top->offset = offset;
    top->size = 4;
    top->var = NOT_A_VAR;
    if (size == 8)
    {
	(top + 1)->mr = MR_ABS;
	(top + 1)->offset = offset + 4;
	(top + 1)->size = 4;
	(top + 1)->var = NOT_A_VAR;
    }
}


static int
ResultSize(char *sig)
{
    char    *p;

    for (p = sig + 1; *p != SIGNATURE_ENDFUNC; p++);
    if (p[1] == SIGNATURE_LONG || p[1] == SIGNATURE_DOUBLE)
	return 2;
    else if (p[1] == SIGNATURE_VOID)
	return 0;
    else
	return 1;
}


/* Pop the method arguments from the operand stack and push them onto the
 * procedure stack according to the signature sig. Since it is not possible
 * to parse the signature backwards (SIGNATURE_CLASS may be part of a class
 * name), we recursively traverse the signature and pop the arguments in
 * reverse order when coming back from the recursion.
 * Return the new operand stack top and sets *resSig to the result signature.
 * "this" is not included in the signature and therefore not pushed.
 */
static Item *
PopArgs(CompEnv *ce, Item *top, char *sig, char *resSig)
{
    if (*sig == SIGNATURE_ENDFUNC)
	*resSig = sig[1];
    else
    {
	switch	(*sig)
	{
	case SIGNATURE_BOOLEAN:
	case SIGNATURE_BYTE:
	case SIGNATURE_CHAR:
	case SIGNATURE_SHORT:
	case SIGNATURE_INT:
	    top = PopArgs(ce, top, sig + 1, resSig);
	    PushOpnd(ce, --top);
	    break;

	case SIGNATURE_FLOAT:
	    top = PopArgs(ce, top, sig + 1, resSig);
	    PushFOpnd(ce, --top, 4, 4);
	    break;

	case SIGNATURE_CLASS:
	    while (*sig != SIGNATURE_ENDCLASS) sig++;
	    top = PopArgs(ce, top, sig + 1, resSig);
	    PushOpnd(ce, --top);
	    break;

	case SIGNATURE_ARRAY:
	    while (*sig == SIGNATURE_ARRAY) sig++;
	    if (*sig == SIGNATURE_CLASS)
		while	(*sig != SIGNATURE_ENDCLASS) sig++;
	    top = PopArgs(ce, top, sig + 1, resSig);
	    PushOpnd(ce, --top);
	    break;

	case SIGNATURE_LONG:
	    top = PopArgs(ce, top, sig + 1, resSig);
	    PushOpnd(ce, --top);    // high
	    PushOpnd(ce, --top);    // low
	    break;

	case SIGNATURE_DOUBLE:
	    top = PopArgs(ce, top, sig + 1, resSig);
	    top -= 2;
	    PushFOpnd(ce, top, 8, 8);
	    break;

	default:
	    check(0);
	}
    }
    return top;
}


/* Push the method result to the operand stack according to the signature sig.
 * Return the new operand stack top.
 */
static Item *
PushResult(CompEnv *ce, Item *top, char sig)
{
    switch  (sig)
    {
    default:	// 4-byte integer word
	top->mr = GetReg(ce, RS_EAX);
	top->size = 4;
	top->var = NOT_A_VAR;
	top++;
	break;

    case SIGNATURE_LONG:
	(top + 0)->mr = GetReg(ce, RS_EAX);
	(top + 0)->size = 4;
	(top + 0)->var = NOT_A_VAR;
	(top + 1)->mr = GetReg(ce, RS_EDX);
	(top + 1)->size = 4;
	(top + 1)->var = NOT_A_VAR;
	top += 2;
	break;

    case SIGNATURE_FLOAT:
	top->mr = MR_FST;
	top->size = 4;
	top->var = NOT_A_VAR;
	top++;
	break;

    case SIGNATURE_DOUBLE:
	(top + 0)->mr = MR_FST;
	(top + 0)->size = 8;
	(top + 0)->var = NOT_A_VAR;
	(top + 1)->mr = MR_FST;
	(top + 1)->size = 0;
	(top + 1)->var = NOT_A_VAR;
	top += 2;
	break;

    case SIGNATURE_VOID:
	break;
    }
    return top;
}


static int
InvokeMethod(CompEnv *ce, struct methodblock *mb_type, Item *top,
    int isVirtual, int isStatic)
{
    Item    *new_top, mtab, meth, code;
    ModReg  thisR;
    char    *sig = fieldsig(&mb_type->fb);
    char    resSig;

    new_top = PopArgs(ce, top, sig + 1, &resSig);
    if (!isStatic)
    {
	--new_top;
	if  (isVirtual)
	{
	    // If the called method is virtual, "this" is pushed via a reg,
	    // because "this" is also used to access the method table.
	    // Since edx must hold mb, it cannot hold "this".
	    // If we need to load "this" or move it from edx, we use eax
	    if (!IsIntReg(new_top->mr) || new_top->mr == MR_EDX)
		LoadOpnd(ce, new_top, RS_EAX);
	    thisR = new_top->mr;
	    PushOpnd(ce, new_top);	    // new_top->mr changed to MR_IMM
	    GetReg(ce, regSetOfMr[thisR]);  // thisR not available in PinTempMemAliases
	}
	else
	    PushOpnd(ce, new_top);
    }
    sysAssert(top - new_top == mb_type->args_size);
    PinTempMemAliases(ce, new_top);
    if (isVirtual)
    {
	FreeRegs(ce, thisR);
	sysAssert(ce->ctxt->regRefCnt[MR_EAX] == 0
		  && ce->ctxt->regRefCnt[MR_EDX] == 0
		  && ce->ctxt->regRefCnt[MR_ECX] == 0);
	mtab.mr = MR_BASE + thisR;
	mtab.offset = offsetof(Hjava_lang_Object, methods);
	mtab.size = 4;
	GenLoad(ce, MR_EDX, &mtab);
	if  (fieldclass(&mb_type->fb) == classJavaLangObject)
	    GenByteLong(ce, CALLN,
		(char *)CompSupport_invokevirtualobject - ce->pc - 5);
	meth.mr = MR_BASE + MR_EDX;
	meth.offset = offsetof(struct methodtable, methods)
	    + 4*mb_type->fb.u.offset;
	meth.size = 4;
	GenLoad(ce, MR_EDX, &meth);
    }
    else    // nonvirtual or static
    {
	sysAssert(ce->ctxt->regRefCnt[MR_EAX] == 0
		  && ce->ctxt->regRefCnt[MR_EDX] == 0
		  && ce->ctxt->regRefCnt[MR_ECX] == 0);
	GenByte(ce, LOADI_L + regMap[MR_EDX]);
	GenLong(ce, (long)mb_type);
    }
    GenByte(ce, LOADI_L + regMap[MR_ECX]);
    GenLong(ce, (long)ce->mb);	// caller mb
    if (isVirtual)
    {
	code.mr = MR_BASE + MR_EDX;
	code.offset = offsetof(struct methodblock, CompiledCode);
    }
    else    // nonvirtual or static
    {
	code.mr = MR_ABS;
	code.offset = (long)mb_type + offsetof(struct methodblock, CompiledCode);
    }
    code.size = 4;
    GenByte(ce, 0xFF);
    GenEA(ce, CALLI, &code);
    ce->ctxt->espLevel += 4*(top - new_top);
    sysAssert(ce->ctxt->espLevel == 0);		// otherwise we cannot walk the stack
    FlushVarCache(ce->ctxt);
    new_top = PushResult(ce, new_top, resSig);
    return new_top - top;
}


static int
InvokeInterface(CompEnv *ce, unsigned ID, Item *top)
{
    Item    *new_top;
    char    *sig, resSig;

    sig = (*p_IDToType)(ID);
    new_top = PopArgs(ce, top, sig + 1, &resSig);
    PushOpnd(ce, --new_top);
    PinTempMemAliases(ce, new_top);	// must be done before pushing the hint for patching to work
    GenByteLong(ce, PUSHI_L, (long)ce->mb);	// caller mb
    GenByteLong(ce, PUSHI_L, ID);		// ID
    GenByteLong(ce, PUSHI_L, top - new_top);	// nargs
    GenByteLong(ce, PUSHI_L, 0);		// mslot hint, at return address - 9
    CallCompSupport(ce, (char *)CompSupport_invokeinterface, new_top, 4*(top - new_top));
    new_top = PushResult(ce, new_top, resSig);
    return new_top - top;
}


/* Set elem to a[x<<elem->scale], elem->scale must be defined on entry,
 * generate bounds checking
 */
static void
ArrayIndex(CompEnv *ce, Item *a, Item *x, Item *elem)
{
    ModReg  lenR, arrR;
    Item    arrh;
    Label   skipLab;

    if (!IsIntReg(a->mr) && a->var != NOT_A_VAR)
    	LookupVarCache(ce, a, a->var, RS_ALL);  // look for a cache hit before GetReg

    lenR = GetReg(ce, RS_CALLER_SAVED);
    LoadOpnd(ce, a, RS_ALL);
    arrh.mr = MR_BASE + a->mr;
    a->mr = MR_IMM;	// protect register of arrh from being spilled
    arrh.offset = offsetof(HArrayOfObject, methods);
    arrh.size = 4;
    GenLoad(ce, lenR, &arrh);
    ShiftRight(ce, lenR, METHOD_FLAG_BITS, 4, 0);
    if (x->mr != MR_IMM && FreeRegSet(ce, x->mr))
	LoadOpnd(ce, x, RS_ALL); // no spill, otherwise loading x later is fine
    GenCmpRegEA(ce, lenR, x);
    GenByte(ce, 0x70 + CC_A);
    skipLab = 0;
    ShortLink(ce, &skipLab);
    // push the bad index, call	support routine, and don't return
    IncRefCnt(ce, x->mr);
    Push(ce, x);
    GenByteLong(ce, CALLN, (char *)CompSupport_throwArrayIndexOutOfBounds - ce->pc - 5);
    ce->ctxt->espLevel += 4;
    FixShortLink(ce, skipLab);
#ifdef	HANDLE_IN_OBJECT
    FreeRegs(ce, lenR);
    arrR = arrh.mr - MR_BASE;
#else
    FreeRegs(ce, arrh.mr);
    arrR = lenR;
    arrh.offset = offsetof(HArrayOfObject, obj);
    GenLoad(ce, arrR, &arrh);
#endif
    if (x->mr != MR_IMM)
    {
	LoadOpnd(ce, x, RS_ALL);
	elem->mr = MR_BINX + (x->mr << 3) + arrR;
	x->mr = MR_IMM; // protect register of x from being spilled
	elem->offset = offsetof(ArrayOfObject, body);
    }
    else
    {
	elem->mr = MR_BASE + arrR;
	elem->offset = offsetof(ArrayOfObject, body) + (x->offset << elem->scale);
    }
#ifdef	HANDLE_IN_OBJECT
    elem->offset += 8;
#endif
}


static void
ArrayLoad(CompEnv *ce, Item *a, Item *x, int scale, int size, int signedFlag,
    unsigned char *hintpc)
{
    RegSet  hint = GetHint(ce, hintpc);
    ModReg  elemR;
    Item    elem;
    int     op;

    elem.scale = scale;
    ArrayIndex(ce, a, x, &elem);
    if (size == 8)
    {
	sysAssert(scale == 3);
	(a + 1)->mr = elem.mr;
        (a + 1)->offset = elem.offset + 4;
        (a + 1)->scale = scale;
	(a + 1)->size = 4;
	(a + 1)->var = NOT_A_VAR;
	IncRefCnt(ce, elem.mr);
	sysAssert(IsCallerSaved(BaseRegOf(elem.mr)));
    }
    if (scale <= 1)	    // we need to load and extend the element
    {
	if  ((hint & RS_CALLEE_SAVED) != RS_EMPTY)
	{
	    PinRVarAliases(ce, hint, a);
	    FreeRegs(ce, elem.mr);
	    elemR = GetReg(ce, hint);	// no spill
	}
	else if (FreeRegSet(ce, elem.mr) & hint)
	{
	    FreeRegs(ce, elem.mr);
	    elemR = GetReg(ce, hint);	// no spill
	}
	else
	{
	    // the spill could destroy the freed elem.mr
	    // a, x, and elem cannot be spilled:
	    // a and x are MR_IMM, elem is not visible (not on opnd stack)
	    sysAssert(a->mr == MR_IMM && x->mr == MR_IMM);
	    elemR = GetReg(ce, hint);	// spill
	    // always at least one caller-saved reg to be spilled
      	    FreeRegs(ce, elem.mr);
	}
       	if  (scale == 0)
	{
	    elem.size = 1;
	    op = 0xBE;	// movsx erx, byte
	}
	else if (signedFlag)
	{
	    elem.size = 2;
	    op = 0xBF;	// movsx erx, word
	}
	else
	{
	    elem.size = 2;
	    op = 0xB7;	// movzx erx, word
	}
	Gen2Bytes(ce, 0x0F, op);
	GenEA(ce, reg3Map[elemR], &elem);   // mov(s|z)x erx, byte|word
	(a + 0)->mr = elemR;
    }
    else
    {
	(a + 0)->mr = elem.mr;
	(a + 0)->offset = elem.offset;
	(a + 0)->scale = scale;
    }
    (a + 0)->size = 4;
    (a + 0)->var = NOT_A_VAR;
}


static void
ArrayStore(CompEnv *ce, Item *a, Item *x, Item *val, int scale, int size)
{
    ModReg  elemR;
    Item    elem;

    PinMemAliases(ce, a);
    elem.scale = scale;
    ArrayIndex(ce, a, x, &elem);
    if (val->mr == MR_FST)
    {
	check(val->size == size);
	elem.size = size;
	GenFStore(ce, &elem, 0);
    }
    else
    {
	if  (size == 8)
	{
	    sysAssert(scale == 3);
	    elemR = (val + 1)->mr;
	    if (!IsIntReg(elemR))
	    {
    		LoadOpnd(ce, val + 1, RS_CALLER_SAVED);
		elemR = (val + 1)->mr;
	    }
	    elem.size = 4;
	    elem.offset += 4;	// high
	    GenStore(ce, &elem, elemR);
	    FreeRegs(ce, elemR);
	    elem.offset -= 4;	// low again
	}
	elemR = val->mr;
	if  (scale <= 1)	// we need to truncate the element
	{
	    if (!IsByteReg(elemR))
	    {
    		LoadOpnd(ce, val, RS_BYTE & RS_CALLER_SAVED);
		elemR = val->mr;
	    }
	    elem.size = 1 << scale;
	}
	else
	{
	    if (!IsIntReg(elemR))
	    {
    		LoadOpnd(ce, val, RS_CALLER_SAVED);
		elemR = val->mr;
	    }
	    elem.size = 4;
	}
	GenStore(ce, &elem, elemR);
	FreeRegs(ce, elemR);
    }
    FreeRegs(ce, elem.mr);
}


#define SET_RANGE_FLAG(t, k) rp[t].mark.rangeFlags |= k;
#define INC_JOIN_CNT(t) if (rp[t].mark.joinCnt < 127) rp[t].mark.joinCnt++
#define DEC_JOIN_CNT(t) rp[t].mark.joinCnt-- // no guard, called once at most


static bool_t
MarkExceptionRanges(CompEnv *ce)
{
    struct CatchFrame *cf = ce->mb->exception_table;
    signed long cnt = ce->mb->exception_table_length;
    RangePtr *rp = ce->rp;
    unsigned char n, m;

    for (; --cnt >= 0; cf += 1)
    {
	n = rp[cf->start_pc].mark.tryEnterCnt + 1;
	m = rp[cf->end_pc].mark.tryExitCnt + 1;
	rp[cf->start_pc].mark.tryEnterCnt = n;
	rp[cf->end_pc].mark.tryExitCnt = m;
	SET_RANGE_FLAG(cf->handler_pc, RF_XHANDLER | RF_SET_XCTXT);
	// INC_JOIN_CNT(cf->handler_pc); no, to avoid stack flushing
	if  (n == 0 || m == 0)
	{
	    ce->err = "Too many nested try statements";
	    return FALSE;
	}
    }
    return TRUE;
}


/* Find and mark branch targets.
 * Mark variables that cannot be allocated in registers, i.e. float or double.
 */
static bool_t
MarkBranchTargAndVars(CompEnv *ce)
{
    unsigned char   *initial_pc = ce->mb->code;
    unsigned char   *max_pc = initial_pc + ce->mb->code_length;
    unsigned char   *pc;
    RangePtr	    *rp = ce->rp;
    long	    relpc, dist;
    long	    target;
    int 	    var;

    SET_RANGE_FLAG(0, RF_START);
    pc = initial_pc;
    while (pc < max_pc)
    {
	switch (*pc)
	{
	case opc_dload_0:
	case opc_dstore_0:
	    ce->varOff[1] = 1;
	    // fall thru
	case opc_fload_0:
	case opc_fstore_0:
	    ce->varOff[0] = 1;
	    pc++;
	    continue;

	case opc_dload_1:
	case opc_dstore_1:
	    ce->varOff[2] = 1;
	    // fall thru
	case opc_fload_1:
	case opc_fstore_1:
	    ce->varOff[1] = 1;
	    pc++;
	    continue;

	case opc_dload_2:
	case opc_dstore_2:
	    ce->varOff[3] = 1;
	    // fall thru
	case opc_fload_2:
	case opc_fstore_2:
	    ce->varOff[2] = 1;
	    pc++;
	    continue;

	case opc_dload_3:
	case opc_dstore_3:
	    ce->varOff[4] = 1;
	    // fall thru
	case opc_fload_3:
	case opc_fstore_3:
	    ce->varOff[3] = 1;
	    pc++;
	    continue;

	case opc_dload:
	case opc_dstore:
	    ce->varOff[pc[1]+1] = 1;
	    // fall thru
	case opc_fload:
	case opc_fstore:
	    ce->varOff[pc[1]] = 1;
	    // fall thru
	case opc_lload:
	case opc_lstore:
	case opc_aload:
	case opc_astore:
	case opc_iload:
	case opc_istore:
	case opc_newarray:
	case opc_bipush:
	case opc_ldc:
	case opc_ldc_quick:
	    pc += 2;
	    continue;

	case opc_iinc:
	case opc_anewarray:
	case opc_anewarray_quick:
	case opc_sipush:
	case opc_ldc_w:
	case opc_ldc_w_quick:
	case opc_ldc2_w:
	case opc_ldc2_w_quick:
	case opc_instanceof:
	case opc_instanceof_quick:
	case opc_checkcast:
	case opc_checkcast_quick:
	case opc_new:
	case opc_new_quick:
	case opc_putstatic:
	case opc_putstatic_quick:
	case opc_putstatic2_quick:
	case opc_getstatic:
	case opc_getstatic_quick:
	case opc_getstatic2_quick:
	case opc_putfield:
	case opc_putfield_quick:
	case opc_putfield_quick_w:
	case opc_putfield2_quick:
	case opc_getfield:
	case opc_getfield_quick:
	case opc_getfield_quick_w:
	case opc_getfield2_quick:
	case opc_invokevirtual:
	case opc_invokevirtual_quick:
	case opc_invokevirtual_quick_w:
	case opc_invokenonvirtual:
	case opc_invokenonvirtual_quick:
        case opc_invokesuper_quick:
	case opc_invokestatic:
	case opc_invokestatic_quick:
	case opc_invokevirtualobject_quick:
	    pc += 3;
	    continue;

	case opc_invokeinterface:
	case opc_invokeinterface_quick:
	    pc += 5;
	    continue;

	case opc_multianewarray:
	case opc_multianewarray_quick:
	    pc += 4;
	    continue;

	case opc_jsr:
	    relpc = pc - initial_pc;
	    target = relpc + pc2signedshort(pc);
	    SET_RANGE_FLAG(relpc + 3, RF_START);
	    SET_RANGE_FLAG(target, RF_JSR);
	    INC_JOIN_CNT(target);
	    pc += 3;
	    continue;

	case opc_jsr_w:
	    relpc = pc - initial_pc;
	    target = relpc + pc2signedlong(pc);
	    SET_RANGE_FLAG(relpc + 5, RF_START);
	    SET_RANGE_FLAG(target, RF_JSR);
	    INC_JOIN_CNT(target);
	    pc += 5;
	    continue;

	case opc_ret:
	    relpc = pc - initial_pc;
	    DEC_JOIN_CNT(relpc + 2);
	    pc += 2;
	    continue;

	case opc_ifeq:
	case opc_ifge:
	case opc_ifgt:
	case opc_ifle:
	case opc_iflt:
	case opc_ifne:
	case opc_if_icmpeq:
	case opc_if_icmpne:
	case opc_if_icmpge:
	case opc_if_icmpgt:
	case opc_if_icmple:
	case opc_if_icmplt:
	case opc_if_acmpeq:
	case opc_if_acmpne:
	case opc_ifnull:
	case opc_ifnonnull:
	    dist = pc2signedshort(pc);
	    relpc = pc - initial_pc;
	    target = relpc + dist;
	    SET_RANGE_FLAG(target, RF_START);
	    INC_JOIN_CNT(target);
	    SET_RANGE_FLAG(relpc + 3, RF_START);
	    pc += 3;
	    continue;

	case opc_goto:
	    dist = pc2signedshort(pc);
	    relpc = pc - initial_pc;
	    target = relpc + dist;
	    SET_RANGE_FLAG(target, RF_START);
	    INC_JOIN_CNT(target);
	    DEC_JOIN_CNT(relpc + 3);
	    SET_RANGE_FLAG(relpc + 3, RF_START);
	    pc += 3;
	    continue;

	case opc_goto_w:
	    dist = pc2signedlong(pc);
	    relpc = pc - initial_pc;
	    target = relpc + dist;
	    SET_RANGE_FLAG(target, RF_START);
	    INC_JOIN_CNT(target);
	    DEC_JOIN_CNT(relpc + 5);
	    SET_RANGE_FLAG(relpc + 5, RF_START);
	    pc += 5;
	    continue;

	case opc_return:
	case opc_areturn:
	case opc_ireturn:
	case opc_lreturn:
	case opc_freturn:
	case opc_dreturn:
	    // ce->epilogueRh->joinCnt++;	return code optimized now
	    relpc = pc - initial_pc;
	    DEC_JOIN_CNT(relpc + 1);
	    pc++;
	    continue;

	case opc_wide:
	    var = GET_INDEX(pc + 2);

	    switch(pc[1])
	    {
	    case opc_dload:
	    case opc_dstore:
		ce->varOff[var+1] = 1;
		// fall thru
	    case opc_fload:
	    case opc_fstore:
		ce->varOff[var] = 1;
		// fall thru
	    case opc_aload:
	    case opc_astore:
	    case opc_iload:
	    case opc_istore:
	    case opc_lload:
	    case opc_lstore:
		pc += 4;
		continue;

	    case opc_iinc:
		pc += 6;
		continue;

	    case opc_ret:
		relpc = pc - initial_pc;
		DEC_JOIN_CNT(relpc + 4);
		pc += 4;
		continue;

	    default:
		sysAssert(0);
		ce->err = "Undefined opcode";
		return FALSE;
	    }

	case opc_tableswitch:
	{
	    long *ltbl = (long *) ALIGN((int)pc + 1);
	    long def = swap(ltbl[0]);
	    long low = swap(ltbl[1]);
	    long high = swap(ltbl[2]);
	    long i;

	    relpc = pc - initial_pc;
	    target = relpc + def;
	    SET_RANGE_FLAG(target, RF_START);
	    INC_JOIN_CNT(target);
	    for (i = high - low + 1; --i >= 0; )
	    {
		dist = swap(ltbl[i + 3]);
		target = relpc + dist;
		// special range header needed for computing stack height:
		SET_RANGE_FLAG(relpc + i + 1, RF_DATABLOCK);
		SET_RANGE_FLAG(target, RF_START);
		INC_JOIN_CNT(target);
	    }

	    pc = (unsigned char *)(ltbl + 3 + high - low + 1);
	    DEC_JOIN_CNT(pc - initial_pc);
	}
	continue;

	case opc_lookupswitch:
	{
	    long *ltbl = (long *) ALIGN((int)pc + 1);
	    long def = swap(ltbl[0]);
	    long npairs = swap(ltbl[1]);
	    long i;

	    relpc = pc - initial_pc;
	    target = relpc + def;
	    SET_RANGE_FLAG(target, RF_START);
	    INC_JOIN_CNT(target);
	    for (i = npairs; --i >= 0; )
	    {
		dist = swap(ltbl[i * 2 + 3]);
		target = relpc + dist;
		// special range header needed for computing stack height:
		SET_RANGE_FLAG(relpc + i + 1, RF_DATABLOCK);
		SET_RANGE_FLAG(target, RF_START);
		INC_JOIN_CNT(target);
	    }

	    pc = (unsigned char *)(ltbl + 2 + npairs * 2);
	    DEC_JOIN_CNT(pc - initial_pc);
	}
	continue;

	case opc_athrow:
	    relpc = pc - initial_pc;
	    DEC_JOIN_CNT(relpc + 1);
	    pc++;
	    continue;

	case opc_breakpoint:
	// a method containing breakpoints should be interpreted
	// or should we compile a "throw BreakpointException"?
	    ce->err = "Breakpoint found in code";
	    return FALSE;

	//case opc_software:
        //case opc_hardware:
	case opc_invokeignored_quick:
	    ce->err = "opcode not implemented";
	    return FALSE;

	default:
	    sysAssert(opcode_length[*pc] == 1);
	    pc++;
	}
    }
    return TRUE;
}


static bool_t
AllocRangeHeaders(CompEnv *ce)
{
    long	    pc, exceptLev, tryEnterCnt, tryExitCnt;
    const long	    code_length = ce->mb->code_length;
    long	    xctxt;
    RangePtr	    *rp = ce->rp;
    RangeHdr	    *rh, *lastrh;

    lastrh = 0;
    xctxt = -1;
    exceptLev = 0;
    for (pc = 0; pc < code_length; pc++)
    {
	if  (rp[pc].hdr) // (rangeFlags || joinCnt || tryEnterCnt || tryExitCnt)
	{
	    tryEnterCnt = rp[pc].mark.tryEnterCnt;
	    tryExitCnt = rp[pc].mark.tryExitCnt;
	    if (tryEnterCnt != 0 || tryExitCnt != 0)
	    {
		exceptLev += tryEnterCnt - tryExitCnt;
		if  (exceptLev == 0)
		    xctxt = -1;
		else
		    xctxt = pc;
	    }

	    rh = (*p_calloc)(1, sizeof(RangeHdr));
	    if (rh == 0)
	    {
		ce->err = "Not enough memory";
		return FALSE;
	    }

	    if (lastrh)
		lastrh->nextRh = rh;

	    lastrh = rh;
	    rh->bcpc = pc;
	    rh->rangeFlags = rp[pc].mark.rangeFlags;
	    rh->joinCnt = rp[pc].mark.joinCnt;
	    rh->xctxt = xctxt;
	    rp[pc].hdr = rh;	// do this last: union!
	}
    }
    return TRUE;
}


/* This recursive function walks the control flow graph in order to determine
 * the absolute opstack height on entry for each range.
 * The relative opstack height on exit is already computed.
 * Each range is visited once.
 * The execution frequency of each range is estimated.
 */
static void
WalkControlFlow(RangeHdr *rh, long freqLev)
{
    RangeHdr *targRh, *contRh;
    long topOnExit;
    long f;

    while (1)
    {
	rh->rangeFlags |= RF_VISITED;
	topOnExit = rh->topOnExit;
        check(topOnExit >= 0);
	freqLev += rh->joinCnt;	// dead code (joinCnt == -1) not visited
	if (freqLev > 30)
    	    freqLev = 30;
	else if (freqLev < 0)
    	    freqLev = 0;
	rh->freq = 1 << freqLev;
	targRh = rh->targRh;
        contRh = rh->contRh;
	if  (targRh)
	{
	    if ((targRh->rangeFlags & RF_VISITED) == 0)
	    {
		targRh->topOnEntry = topOnExit;
		targRh->topOnExit += topOnExit;
		if (targRh->bcpc <= rh->bcpc)	// backwards jump
		    f = freqLev + 1;
		else if (contRh)
    		    f = freqLev - 1;	// e.g. if statement, divide freq by 2
		else
    		    f = freqLev;	// e.g. fwd jump to a loop condition
		WalkControlFlow(targRh, f);
	    }
	    else if (targRh->topOnEntry != topOnExit)
		OpStackHeightError();
	}
	rh = contRh;
	if  (rh)
	{
	    if ((rh->rangeFlags & RF_VISITED) == 0)
	    {
		rh->topOnEntry = topOnExit;
		rh->topOnExit += topOnExit;
		if (targRh)
    		    freqLev--;
		continue;
	    }
	    else if (rh->topOnEntry != topOnExit)
		OpStackHeightError();
	}
	break;
    }
}


/* For each range, this function computes the relative operand stack height
 * on exit, assuming that the height on entry is 0. It also determines whether
 * a range has to set its exception context on entry.
 * The constant pool has to be resolved to determine the size of some operands.
 * Set ce->codeInfo->baseOff to 0, 4, or 8. This allocates a place holder for the
 * result of invoked methods in order not to destroy the operand stack in case where
 * the result is larger than the arguments. We check that on a range basis,
 * which is a little bit too conservative, but easier.
 * Calls to CompSupport functions are not dangerous.
 */
static bool_t
ComputeOpstackHeight(CompEnv *ce)
{
#define SIZE_AND_STACK(size, stack) pc += size; top += stack; continue
#define SIZE_AND_STACK_BUT(size, stack) pc += size; top += stack
#define NEXT_RANGE()							       \
    top = 0;                                                                   \
    rh = nextRh;                                                               \
    nextRh = rh->nextRh;                                                       \
    if  (nextRh == NULL)                                                       \
        nextRhPc = max_pc;                                                     \
    else                                                                       \
        nextRhPc = initial_pc + nextRh->bcpc

#define SIZE_STACK_TARG_CONT(size, stack, targ, cont)			       \
    pc += size;                                                                \
    top += stack;                                                              \
    rh->targRh = targ;                                                         \
    rh->contRh = cont;                                                         \
    rh->topOnExit = top;                                                       \
    if  (targ && rh->xctxt != targ->xctxt)                                     \
        targ->rangeFlags |= RF_SET_XCTXT;                                      \
    if  (cont && rh->xctxt != cont->xctxt)                                     \
        cont->rangeFlags |= RF_SET_XCTXT;                                      \
    continue

#define SIZE_STACK_TARG_NOCONT(size, stack, targ)			       \
    pc += size;                                                                \
    top += stack;                                                              \
    rh->targRh = targ;                                                         \
    rh->contRh = 0;                                                            \
    rh->topOnExit = top;                                                       \
    if  (targ && rh->xctxt != targ->xctxt)                                     \
        targ->rangeFlags |= RF_SET_XCTXT;                                      \
    continue

#define SIZE_STACK_NOTARG_NOCONT(size, stack)				       \
    pc += size;                                                                \
    top += stack;                                                              \
    rh->targRh = 0;                                                            \
    rh->contRh = 0;                                                            \
    rh->topOnExit = top;                                                       \
    continue

#define SIZE_STACK_TARG_CONT_BUT(size, stack, targ, cont)		       \
    pc += size;                                                                \
    top += stack;                                                              \
    rh->targRh = targ;                                                         \
    rh->contRh = cont;                                                         \
    rh->topOnExit = top;                                                       \
    if  (targ && rh->xctxt != targ->xctxt)                                     \
        targ->rangeFlags |= RF_SET_XCTXT;                                      \
    if  (cont && rh->xctxt != cont->xctxt)                                     \
        cont->rangeFlags |= RF_SET_XCTXT

    struct methodblock *mb = ce->mb;
    struct execenv *ee = ce->ee;
    ClassClass *cb = fieldclass(&mb->fb);
    ClassClass *array_cb, *new_cb;
    union cp_item_type *cpool = cbConstantPool(cb);
    unsigned char *type_table = cpool[CONSTANT_POOL_TYPE_TABLE_INDEX].p;
    struct fieldblock *fb;
    struct methodblock *mb_type;
    char	    isig, *sig;
    unsigned char   *initial_pc = mb->code;
    unsigned char   *max_pc = initial_pc + mb->code_length;
    unsigned char   *pc, *nextRhPc;
    RangeHdr	    *rh, *nextRh, *targRh;
    unsigned	    index, ID;
    unsigned char   opcode;
    int 	    top, push, maxPush = 0;

    pc = initial_pc;
    rh = NULL;
    nextRh = ce->rp[0].hdr;
    nextRhPc = initial_pc;

    while (pc < max_pc)
    {
	if  (pc >= nextRhPc)
	{
	    // a new range starts here
	    sysAssert(pc == nextRhPc);
	    if (rh && rh->contRh == UNDEF_RH)
	    {
		// normal control flow from rh to nextRh
		rh->contRh = nextRh;
		rh->topOnExit = top;
		if  (rh->xctxt != nextRh->xctxt)
		    nextRh->rangeFlags |= RF_SET_XCTXT;
	    }
	    NEXT_RANGE();
	    sysAssert((rh->rangeFlags & RF_DATABLOCK) == 0);
	    rh->contRh = UNDEF_RH;
	    if (rh->rangeFlags & RF_JSR)
		top++;
	}
	opcode = *pc;
	switch (opcode)
	{
	case opc_nop:
	    SIZE_AND_STACK(1, 0);

	case opc_aload:
	case opc_iload:
	case opc_fload:
	    SIZE_AND_STACK(2, 1);

	case opc_lload:
	case opc_dload:
	    SIZE_AND_STACK(2, 2);

	case opc_iload_0:
	case opc_aload_0:
	case opc_fload_0:
	case opc_iload_1:
	case opc_aload_1:
	case opc_fload_1:
	case opc_iload_2:
	case opc_aload_2:
	case opc_fload_2:
	case opc_iload_3:
	case opc_aload_3:
	case opc_fload_3:
	    SIZE_AND_STACK(1, 1);

	case opc_lload_0:
	case opc_dload_0:
	case opc_lload_1:
	case opc_dload_1:
	case opc_lload_2:
	case opc_dload_2:
	case opc_lload_3:
	case opc_dload_3:
	    SIZE_AND_STACK(1, 2);

	case opc_istore:
	case opc_astore:
	case opc_fstore:
	    SIZE_AND_STACK(2, -1);

	case opc_lstore:
	case opc_dstore:
	    SIZE_AND_STACK(2, -2);

	case opc_istore_0:
	case opc_astore_0:
	case opc_fstore_0:
	case opc_istore_1:
	case opc_astore_1:
	case opc_fstore_1:
	case opc_istore_2:
	case opc_astore_2:
	case opc_fstore_2:
	case opc_istore_3:
	case opc_astore_3:
	case opc_fstore_3:
	    SIZE_AND_STACK(1, -1);

	case opc_lstore_0:
	case opc_dstore_0:
	case opc_lstore_1:
	case opc_dstore_1:
	case opc_lstore_2:
	case opc_dstore_2:
	case opc_lstore_3:
	case opc_dstore_3:
	    SIZE_AND_STACK(1, -2);

	case opc_return:
	case opc_areturn:
	case opc_ireturn:
	case opc_lreturn:
	case opc_freturn:
	case opc_dreturn:
	    targRh = ce->epilogueRh;
	    SIZE_STACK_TARG_NOCONT(1, 0, targRh);

	case opc_i2f:
	case opc_f2i:
	case opc_l2d:
	case opc_d2l:
	case opc_int2byte:
	case opc_int2char:
	case opc_int2short:
	    SIZE_AND_STACK(1, 0);

	case opc_i2l:
	case opc_i2d:
	case opc_f2l:
	case opc_f2d:
	    SIZE_AND_STACK(1, 1);

	case opc_l2i:
	case opc_l2f:
	case opc_d2i:
	case opc_d2f:
	    SIZE_AND_STACK(1, -1);

	case opc_fcmpl:
	case opc_fcmpg:
	    SIZE_AND_STACK(1, -1);

	case opc_lcmp:
	case opc_dcmpl:
	case opc_dcmpg:
	    SIZE_AND_STACK(1, -3);

	case opc_bipush:
	    SIZE_AND_STACK(2, 1);

	case opc_sipush:
	    SIZE_AND_STACK(3, 1);

	case opc_ldc:
	    if (!(*p_ResolveClassConstantFromClass)(cb, pc[1], ee,
		     (1 << CONSTANT_Integer) |
		     (1 << CONSTANT_Float) |
		     (1 << CONSTANT_String)))
	    {
		ce->err = "Unable to resolve class constant";
		return FALSE;
	    }
	    // break thru

	case opc_ldc_quick:
	    SIZE_AND_STACK(2, 1);

	case opc_ldc_w:
	    if (!(*p_ResolveClassConstantFromClass)(cb, GET_INDEX(pc + 1), ee,
		     (1 << CONSTANT_Integer) |
		     (1 << CONSTANT_Float) |
		     (1 << CONSTANT_String)))
	    {
		ce->err = "Unable to resolve class constant";
		return FALSE;
	    }
	    // break thru

	case opc_ldc_w_quick:
	    SIZE_AND_STACK(3, 1);

	case opc_ldc2_w:
	    if (!(*p_ResolveClassConstantFromClass)(cb, GET_INDEX(pc + 1), ee,
		 (1 << CONSTANT_Double) | (1 << CONSTANT_Long)))
	    {
		ce->err = "Unable to resolve class constant";
		return FALSE;
	    }
	    // break thru

	case opc_ldc2_w_quick:
	    SIZE_AND_STACK(3, 2);

	case opc_aconst_null:
	case opc_iconst_m1:
	case opc_iconst_0:
	case opc_iconst_1:
	case opc_iconst_2:
	case opc_iconst_3:
	case opc_iconst_4:
	case opc_iconst_5:
	case opc_fconst_0:
	case opc_fconst_1:
	case opc_fconst_2:
	    SIZE_AND_STACK(1, 1);

	case opc_dconst_0:
	case opc_dconst_1:
	case opc_lconst_0:
	case opc_lconst_1:
	    SIZE_AND_STACK(1, 2);

	case opc_iadd:
	case opc_isub:
	case opc_iand:
	case opc_ior:
	case opc_ixor:
	case opc_imul:
	case opc_idiv:
	case opc_irem:
	    SIZE_AND_STACK(1, -1);

	case opc_ladd:
	case opc_lsub:
	case opc_land:
	case opc_lor:
	case opc_lxor:
	case opc_lmul:
	case opc_ldiv:
	case opc_lrem:
	    SIZE_AND_STACK(1, -2);

	case opc_ishl:
	case opc_ishr:
	case opc_iushr:
	case opc_lshl:
	case opc_lshr:
	case opc_lushr:
	    SIZE_AND_STACK(1, -1);

	case opc_ineg:
	case opc_lneg:
	case opc_fneg:
	case opc_dneg:
	    SIZE_AND_STACK(1, 0);

	case opc_iinc:
	    SIZE_AND_STACK(3, 0);

	case opc_fadd:
	case opc_fsub:
	case opc_fmul:
	case opc_fdiv:
	case opc_frem:
	    SIZE_AND_STACK(1, -1);

	case opc_dadd:
	case opc_dsub:
	case opc_dmul:
	case opc_ddiv:
	case opc_drem:
	    SIZE_AND_STACK(1, -2);

	case opc_jsr:
	    targRh = ce->rp[pc + pc2signedshort(pc) - initial_pc].hdr;
	    SIZE_STACK_TARG_CONT_BUT(3, 0, targRh, nextRh);
	    if (rh->xctxt != targRh->xctxt)
		nextRh->rangeFlags |= RF_SET_XCTXT; // restore xctxt after return
	    continue;
	    // 0: ret addr is not pushed onto the opstack

	case opc_jsr_w:
	    targRh = ce->rp[pc + pc2signedlong(pc) - initial_pc].hdr;
	    SIZE_STACK_TARG_CONT_BUT(5, 0, targRh, nextRh);
	    if (rh->xctxt != targRh->xctxt)
		nextRh->rangeFlags |= RF_SET_XCTXT; // restore xctxt after return
	    continue;
	    // 0: ret addr is not pushed onto the opstack

	case opc_goto:
	    targRh = ce->rp[pc + pc2signedshort(pc) - initial_pc].hdr;
	    SIZE_STACK_TARG_NOCONT(3, 0, targRh);

	case opc_goto_w:
	    targRh = ce->rp[pc + pc2signedlong(pc) - initial_pc].hdr;
	    SIZE_STACK_TARG_NOCONT(5, 0, targRh);

	case opc_ret:
	    SIZE_STACK_NOTARG_NOCONT(2, 0);

	case opc_if_icmpeq:
	case opc_if_icmpne:
	case opc_if_icmplt:
	case opc_if_icmpgt:
	case opc_if_icmple:
	case opc_if_icmpge:
	case opc_if_acmpeq:
	case opc_if_acmpne:
	    targRh = ce->rp[pc + pc2signedshort(pc) - initial_pc].hdr;
	    SIZE_STACK_TARG_CONT(3, -2, targRh, nextRh);

	case opc_ifeq:
	case opc_ifne:
	case opc_iflt:
	case opc_ifgt:
	case opc_ifle:
	case opc_ifge:
	case opc_ifnull:
	case opc_ifnonnull:
	    targRh = ce->rp[pc + pc2signedshort(pc) - initial_pc].hdr;
	    SIZE_STACK_TARG_CONT(3, -1, targRh, nextRh);

	case opc_pop:
	    SIZE_AND_STACK(1, -1);

	case opc_pop2:
	    SIZE_AND_STACK(1, -2);

	case opc_dup:
	case opc_dup_x1:
	case opc_dup_x2:
	    SIZE_AND_STACK(1, 1);

	case opc_dup2:
	case opc_dup2_x1:
	case opc_dup2_x2:
	    SIZE_AND_STACK(1, 2);

	case opc_swap:
	    SIZE_AND_STACK(1, 0);

	case opc_arraylength:
	    SIZE_AND_STACK(1, 0);

	case opc_tableswitch:
	{
	    long *ltbl = (long *) ALIGN((int)pc + 1);
	    long def = swap(ltbl[0]);
	    long low = swap(ltbl[1]);
	    long high = swap(ltbl[2]);
	    long npairs = high - low + 1;
	    long i, relpc, dist;

	    relpc = pc - initial_pc;
	    targRh = ce->rp[relpc + def].hdr;
	    SIZE_STACK_TARG_CONT_BUT(0, -1, targRh, nextRh);
	    for (i = 0; i < npairs; i++)
	    {
		NEXT_RANGE();
		dist = swap(ltbl[i + 3]);
		targRh = ce->rp[relpc + dist].hdr;
		SIZE_STACK_TARG_CONT_BUT(0, 0, targRh, nextRh);
	    }
	    pc = (unsigned char *)(ltbl + 3 + npairs);
	    rh->contRh = 0;
	}
	continue;

	case opc_lookupswitch:
	{
	    long *ltbl = (long *) ALIGN((int)pc + 1);
	    long def = swap(ltbl[0]);
	    long npairs = swap(ltbl[1]);
	    long i, relpc, dist;

	    relpc = pc - initial_pc;
	    targRh = ce->rp[relpc + def].hdr;
	    SIZE_STACK_TARG_CONT_BUT(0, -1, targRh, nextRh);
	    for (i = 0; i < npairs; i++)
	    {
		NEXT_RANGE();
		dist = swap(ltbl[i * 2 + 3]);
		targRh = ce->rp[relpc + dist].hdr;
		SIZE_STACK_TARG_CONT_BUT(0, 0, targRh, nextRh);
	    }
	    pc = (unsigned char *)(ltbl + 2 + npairs * 2);
	    rh->contRh = 0;
	}
	continue;

	case opc_athrow:
	    SIZE_STACK_NOTARG_NOCONT(1, -1);

	case opc_getfield:
	case opc_putfield:
	case opc_getstatic:
	case opc_putstatic:
	    index = GET_INDEX(pc + 1);
	    if (!(*p_ResolveClassConstantFromClass)(cb, index, ee,
		    1 << CONSTANT_Fieldref))
	    {
                exceptionClear(ee);
		ce->err = "Unable to resolve field ref";
		return FALSE;
	    }
	    fb = cpool[index].p;
	    if (    ((opcode == opc_getstatic || opcode == opc_putstatic)
		     == ((fb->access & ACC_STATIC) == 0))
		||
		    ((opcode == opc_putstatic || opcode == opc_putfield)
		     && (fb->access & ACC_FINAL)
                     && (!mb || (fieldclass(fb) != fieldclass(&mb->fb)))))
	    {
		ce->err = "Incompatible class change";
		return FALSE;
		// will be reported by the interpreter
	    }
	    isig = fieldsig(fb)[0];
	    if (isig == SIGNATURE_LONG || isig == SIGNATURE_DOUBLE)
	    {
		switch	(opcode)
		{
		case opc_getfield:
		    push = 1; break;
		case opc_putfield:
		    push = -3; break;
		case opc_getstatic:
		    push = 2; break;
		case opc_putstatic:
		    push = -2; break;
		}
	    }
	    else
	    {
		switch	(opcode)
		{
		case opc_getfield:
		    push = 0; break;
		case opc_putfield:
		    push = -2; break;
		case opc_getstatic:
		    push = 1; break;
		case opc_putstatic:
		    push = -1; break;
		}
	    }
	    SIZE_AND_STACK(3, push);

	case opc_putfield_quick:
	    SIZE_AND_STACK(3, -2);

	case opc_getfield_quick:
	    SIZE_AND_STACK(3, 0);

	case opc_putfield2_quick:
	    SIZE_AND_STACK(3, -3);

	case opc_getfield2_quick:
	    SIZE_AND_STACK(3, 1);

	case opc_putstatic_quick:
	    SIZE_AND_STACK(3, -1);

	case opc_getstatic_quick:
	    SIZE_AND_STACK(3, 1);

	case opc_putstatic2_quick:
	    SIZE_AND_STACK(3, -2);

	case opc_getstatic2_quick:
	    SIZE_AND_STACK(3, 2);

	case opc_putfield_quick_w:
	    fb = cpool[GET_INDEX(pc + 1)].p;
	    isig = fieldsig(fb)[0];
	    if (isig == SIGNATURE_LONG || isig == SIGNATURE_DOUBLE)
	    {
		SIZE_AND_STACK(3, -3);
	    }
	    else
	    {
		SIZE_AND_STACK(3, -2);
	    }

	case opc_getfield_quick_w:
	    fb = cpool[GET_INDEX(pc + 1)].p;
	    isig = fieldsig(fb)[0];
	    if (isig == SIGNATURE_LONG || isig == SIGNATURE_DOUBLE)
	    {
		SIZE_AND_STACK(3, 1);
	    }
	    else
	    {
		SIZE_AND_STACK(3, 0);
	    }

	case opc_invokevirtual:
	case opc_invokenonvirtual:
	case opc_invokestatic:
	    index = GET_INDEX(pc + 1);
	    if (!(*p_ResolveClassConstantFromClass)(cb, index, ee,
		    1 << CONSTANT_Methodref))
	    {
                exceptionClear(ee);
		ce->err = "Unable to resolve method ref";
		return FALSE;
	    }
	    mb_type = cpool[index].p;
	    if (    (opcode == opc_invokestatic)
		==  ((mb_type->fb.access & ACC_STATIC) == 0))
	    {
		ce->err = "Incompatible class change";
		return FALSE;
		// will be reported by the interpreter
	    }
	    push = ResultSize(fieldsig(&mb_type->fb)) - mb_type->args_size;
	    if (push > maxPush)
		maxPush = push;
	    SIZE_AND_STACK(3, push);

	case opc_invokeinterface:
	    index = GET_INDEX(pc + 1);
	    if (!(*p_ResolveClassConstantFromClass)(cb, index, ee,
		    1 << CONSTANT_InterfaceMethodref))
	    {
		ce->err = "Unable to resolve interface ref";
		return FALSE;
	    }
	    ID = cpool[cpool[index].i & 0xFFFF].i;
	    sig = (*p_IDToType)(ID);
	    push = ResultSize(sig) - pc[3];
	    if (push > maxPush)
		maxPush = push;
	    SIZE_AND_STACK(5, push);

	case opc_invokenonvirtual_quick:
	case opc_invokevirtual_quick_w:
	case opc_invokestatic_quick:
	    mb_type = cpool[GET_INDEX(pc + 1)].p;
	    push = ResultSize(fieldsig(&mb_type->fb)) - mb_type->args_size;
	    if (push > maxPush)
		maxPush = push;
	    SIZE_AND_STACK(3, push);

	case opc_invokesuper_quick:
	    mb_type = cbMethodTable(unhand(cbSuperclass(cb)))
		->methods[GET_INDEX(pc + 1)];
	    push = ResultSize(fieldsig(&mb_type->fb)) - mb_type->args_size;
	    if (push > maxPush)
		maxPush = push;
	    SIZE_AND_STACK(3, push);

	case opc_invokeinterface_quick:
	    ID = cpool[GET_INDEX(pc + 1)].i;
	    sig = (*p_IDToType)(ID);
	    push = ResultSize(sig) - pc[3];
	    if (push > maxPush)
		maxPush = push;
	    SIZE_AND_STACK(5, push);

	case opc_invokevirtualobject_quick:
	    ce->err = "opc_invokevirtualobject_quick cannot be compiled";
	    return FALSE;
	    // push = ResultSize(sig which is unknown) - pc[2];
	    // if   (push > maxPush)
	    //	maxPush = push;
	    // SIZE_AND_STACK(3, push);

	case opc_invokevirtual_quick:
	    ce->err = "opc_invokevirtual_quick cannot be compiled";
	    return FALSE;
	    // push = ResultSize(sig which is unknown) - pc[2];
	    // if   (push > maxPush)
	    //	maxPush = push;
	    // SIZE_AND_STACK(3, push);

	case opc_instanceof:
	case opc_checkcast:
	    if (!(*p_ResolveClassConstantFromClass)(cb, GET_INDEX(pc + 1), ee,
		     1 << CONSTANT_Class))
	    {
                exceptionClear(ee);
		ce->err = "Unable to resolve class constant";
		return FALSE;
	    }
	    // break thru

	case opc_instanceof_quick:
	case opc_checkcast_quick:
	    SIZE_AND_STACK(3, 0);

	case opc_new:
	    index = GET_INDEX(pc + 1);
	    if (!(*p_ResolveClassConstantFromClass)(cb, index, ee,
		     1 << CONSTANT_Class))
	    {
                exceptionClear(ee);
		ce->err = "Unable to resolve class constant";
		return FALSE;
	    }
	    new_cb = cpool[GET_INDEX(pc + 1)].p;
	    if (cbAccess(new_cb) & (ACC_INTERFACE | ACC_ABSTRACT))
	    {
		ce->err = "Instantiation error";
		return FALSE;
		// will be reported by the interpreter
	    }
	    if (!(*p_VerifyClassAccess)(cb, new_cb, FALSE))
	    {
		ce->err = "Illegal access error";
		return FALSE;
		// will be reported by the interpreter
	    }
	    // break thru

	case opc_new_quick:
	    SIZE_AND_STACK(3, 1);

	case opc_anewarray:
	    if (!(*p_ResolveClassConstantFromClass)(cb, GET_INDEX(pc + 1), ee,
		     1 << CONSTANT_Class))
	    {
                exceptionClear(ee);
		ce->err = "Unable to resolve class constant";
		return FALSE;
	    }
	    // break thru

	case opc_anewarray_quick:
	    SIZE_AND_STACK(3, 0);

	case opc_multianewarray:
	    if (!(*p_ResolveClassConstantFromClass)(cb, GET_INDEX(pc + 1), ee,
		     1 << CONSTANT_Class))
	    {
                exceptionClear(ee);
		ce->err = "Unable to resolve class constant";
		return FALSE;
	    }
	    // break thru

	case opc_multianewarray_quick:
	    array_cb = cpool[GET_INDEX(pc + 1)].p;
	    (*p_ResolveClassConstantFromClass)(array_cb,
		CONSTANT_POOL_ARRAY_CLASS_INDEX, ee, 1 << CONSTANT_Class);
	    push = 1 - pc[3];
	    SIZE_AND_STACK(4, push);

	case opc_newarray:
	    SIZE_AND_STACK(2, 0);

	case opc_iaload:
	case opc_faload:
	case opc_aaload:
	case opc_baload:
	case opc_caload:
	case opc_saload:
	    SIZE_AND_STACK(1, -1);

	case opc_laload:
	case opc_daload:
	    SIZE_AND_STACK(1, 0);

	case opc_iastore:
	case opc_fastore:
	case opc_aastore:
	case opc_bastore:
	case opc_castore:
	case opc_sastore:
	    SIZE_AND_STACK(1, -3);

	case opc_lastore:
	case opc_dastore:
	    SIZE_AND_STACK(1, -4);

	case opc_monitorenter:
	case opc_monitorexit:
	    SIZE_AND_STACK(1, -1);

	case opc_breakpoint:
	    check(0);	// should be found in first pass

	case opc_wide:
	    switch(pc[1])
	    {
	    case opc_aload:
	    case opc_iload:
	    case opc_fload:
		SIZE_AND_STACK(4, 1);

	    case opc_lload:
	    case opc_dload:
		SIZE_AND_STACK(4, 2);

	    case opc_istore:
	    case opc_astore:
		SIZE_AND_STACK(4, -1);

	    case opc_fstore:
		SIZE_AND_STACK(4, -1);

	    case opc_lstore:
		SIZE_AND_STACK(4, -2);

	    case opc_dstore:
		SIZE_AND_STACK(4, -2);

	    case opc_iinc:
		SIZE_AND_STACK(6, 0);

	    case opc_ret:
		SIZE_STACK_NOTARG_NOCONT(4, 0);

	    default:
		sysAssert(0);
		ce->err = "Undefined opcode";
		return FALSE;
	    }

	default:
	    sysAssert(0);
	    ce->err = "Undefined opcode";
	    return FALSE;
	}
    }
    sysAssert(maxPush >= 0 && maxPush <= 2);
    ce->codeInfo->baseOff = 4*maxPush;
    // ce->epilogueRh->joinCnt--;	return code optimized now
    rh = ce->rp[0].hdr;
    rh->topOnEntry = 0;
    WalkControlFlow(rh, FREQ_UNIT_LEV);
    return TRUE;
}


/* Estimate the frequency of use of each variable.
 */
static bool_t
EstimateVarFreq(CompEnv *ce)
{
#define ACCESS_VAR(v)							       \
    freq = varFreq[v] + rFreq;						       \
    if (freq < 0)							       \
	freq = 0x40000000;						       \
    varFreq[v] = freq						

    RangeHdr *rh, *nextRh;
    unsigned char *pc, *nextRhPc;
    unsigned char *initial_pc;
    struct methodblock *mb = ce->mb;
    int var;
    long *varFreq, rFreq, freq;

    initial_pc = mb->code;
    pc = initial_pc;
    rh = ce->rp[0].hdr;
    varFreq = ce->varFreq;
    while (rh)
    {
	// ignore any ranges that do not contain code...
	if (rh->rangeFlags & RF_DATABLOCK)
	{
	    rh = rh->nextRh;
	    continue;
	}
	nextRh = rh->nextRh;
	if (nextRh == NULL)
	    nextRhPc = initial_pc + mb->code_length;
	else
	    nextRhPc = initial_pc + nextRh->bcpc;

	rFreq = rh->freq;
	sysAssert(rFreq > 0);
	while (pc < nextRhPc)
	{
	    switch (*pc)
	    {
	    case opc_lload_0:
	    case opc_dload_0:
	    case opc_lstore_0:
	    case opc_dstore_0:
		ACCESS_VAR(1);
		// fall thru
	    case opc_iload_0:
	    case opc_aload_0:
	    case opc_fload_0:
	    case opc_istore_0:
	    case opc_astore_0:
	    case opc_fstore_0:
		ACCESS_VAR(0);
		pc++;
		break;

	    case opc_lload_1:
	    case opc_dload_1:
	    case opc_lstore_1:
	    case opc_dstore_1:
		ACCESS_VAR(2);
		// fall thru
	    case opc_iload_1:
	    case opc_aload_1:
	    case opc_fload_1:
	    case opc_istore_1:
	    case opc_astore_1:
	    case opc_fstore_1:
		ACCESS_VAR(1);
		pc++;
		continue;

	    case opc_lload_2:
	    case opc_dload_2:
	    case opc_lstore_2:
	    case opc_dstore_2:
		ACCESS_VAR(3);
		// fall thru
	    case opc_iload_2:
	    case opc_aload_2:
	    case opc_fload_2:
	    case opc_istore_2:
	    case opc_astore_2:
	    case opc_fstore_2:
		ACCESS_VAR(2);
		pc++;
		continue;

	    case opc_lload_3:
	    case opc_dload_3:
	    case opc_lstore_3:
	    case opc_dstore_3:
		ACCESS_VAR(4);
		// fall thru
	    case opc_iload_3:
	    case opc_aload_3:
	    case opc_fload_3:
	    case opc_istore_3:
	    case opc_astore_3:
	    case opc_fstore_3:
		ACCESS_VAR(3);
		pc++;
		continue;

	    case opc_lload:
	    case opc_dload:
	    case opc_lstore:
	    case opc_dstore:
		ACCESS_VAR(pc[1]+1);
		// fall thru
	    case opc_iload:
	    case opc_aload:
	    case opc_fload:
	    case opc_istore:
	    case opc_astore:
	    case opc_fstore:
		ACCESS_VAR(pc[1]);
		pc += 2;
		continue;

	    case opc_newarray:
	    case opc_bipush:
	    case opc_ldc:
	    case opc_ldc_quick:
	    case opc_ret:
		pc += 2;
		continue;

	    case opc_iinc:
	    case opc_anewarray:
	    case opc_anewarray_quick:
	    case opc_sipush:
	    case opc_ldc_w:
	    case opc_ldc_w_quick:
	    case opc_ldc2_w:
	    case opc_ldc2_w_quick:
	    case opc_instanceof:
	    case opc_instanceof_quick:
	    case opc_checkcast:
	    case opc_checkcast_quick:
	    case opc_new:
	    case opc_new_quick:
	    case opc_putstatic:
	    case opc_putstatic_quick:
	    case opc_putstatic2_quick:
	    case opc_getstatic:
	    case opc_getstatic_quick:
	    case opc_getstatic2_quick:
	    case opc_putfield:
	    case opc_putfield_quick:
	    case opc_putfield_quick_w:
	    case opc_putfield2_quick:
	    case opc_getfield:
	    case opc_getfield_quick:
	    case opc_getfield_quick_w:
	    case opc_getfield2_quick:
	    case opc_invokevirtual:
	    case opc_invokevirtual_quick:
	    case opc_invokevirtual_quick_w:
	    case opc_invokenonvirtual:
	    case opc_invokenonvirtual_quick:
	    case opc_invokesuper_quick:
	    case opc_invokestatic:
	    case opc_invokestatic_quick:
	    case opc_invokevirtualobject_quick:
	    case opc_jsr:
	    case opc_ifeq:
	    case opc_ifge:
	    case opc_ifgt:
	    case opc_ifle:
	    case opc_iflt:
	    case opc_ifne:
	    case opc_if_icmpeq:
	    case opc_if_icmpne:
	    case opc_if_icmpge:
	    case opc_if_icmpgt:
	    case opc_if_icmple:
	    case opc_if_icmplt:
	    case opc_if_acmpeq:
	    case opc_if_acmpne:
	    case opc_ifnull:
	    case opc_ifnonnull:
	    case opc_goto:
		pc += 3;
		continue;

	    case opc_invokeinterface:
	    case opc_invokeinterface_quick:
	    case opc_jsr_w:
	    case opc_goto_w:
		pc += 5;
		continue;

	    case opc_multianewarray:
	    case opc_multianewarray_quick:
		pc += 4;
		continue;

	    case opc_wide:
		var = GET_INDEX(pc + 2);

		switch(pc[1])
		{
		case opc_lload:
		case opc_dload:
		case opc_lstore:
		case opc_dstore:
		    ACCESS_VAR(var+1);
		    // fall thru
		case opc_iload:
		case opc_aload:
		case opc_fload:
		case opc_istore:
		case opc_astore:
		case opc_fstore:
		    ACCESS_VAR(var);
		    pc += 4;
		    continue;

		case opc_ret:
		    pc += 4;
		    continue;

		case opc_iinc:
		    pc += 6;
		    continue;

		default:
		    sysAssert(0);
		    ce->err = "Undefined opcode";
		    return FALSE;
		}

	    case opc_tableswitch:
	    {
		long *ltbl = (long *) ALIGN((int)pc + 1);
		long low = swap(ltbl[1]);
		long high = swap(ltbl[2]);

		pc = (unsigned char *)(ltbl + 3 + high - low + 1);
	    }
	    continue;

	    case opc_lookupswitch:
	    {
		long *ltbl = (long *) ALIGN((int)pc + 1);
		long npairs = swap(ltbl[1]);

		pc = (unsigned char *)(ltbl + 2 + npairs * 2);
	    }
	    continue;

	    default:
		sysAssert(opcode_length[*pc] == 1);
		pc++;
	    }
	}
	rh = nextRh;
    }
    return TRUE;
}


/* Mark variables that cannot be allocated in registers, because they are used
 * in exception handlers.
 */
static bool_t
MarkXHVars(CompEnv *ce, unsigned char *pc, unsigned char *max_pc)
{
    int var;

    while (pc < max_pc)
    {
	switch (*pc)
	{
	case opc_lload_0:
	case opc_dload_0:
	case opc_lstore_0:
	case opc_dstore_0:
	    ce->varOff[1] = 1;
	    // fall thru
	case opc_iload_0:
	case opc_aload_0:
	case opc_fload_0:
	case opc_istore_0:
	case opc_astore_0:
	case opc_fstore_0:
	    ce->varOff[0] = 1;
	    pc++;
	    continue;

	case opc_lload_1:
	case opc_dload_1:
	case opc_lstore_1:
	case opc_dstore_1:
	    ce->varOff[2] = 1;
	    // fall thru
	case opc_iload_1:
	case opc_aload_1:
	case opc_fload_1:
	case opc_istore_1:
	case opc_astore_1:
	case opc_fstore_1:
	    ce->varOff[1] = 1;
	    pc++;
	    continue;

	case opc_lload_2:
	case opc_dload_2:
	case opc_lstore_2:
	case opc_dstore_2:
	    ce->varOff[3] = 1;
	    // fall thru
	case opc_iload_2:
	case opc_aload_2:
	case opc_fload_2:
	case opc_istore_2:
	case opc_astore_2:
	case opc_fstore_2:
	    ce->varOff[2] = 1;
	    pc++;
	    continue;

	case opc_lload_3:
	case opc_dload_3:
	case opc_lstore_3:
	case opc_dstore_3:
	    ce->varOff[4] = 1;
	    // fall thru
	case opc_iload_3:
	case opc_aload_3:
	case opc_fload_3:
	case opc_istore_3:
	case opc_astore_3:
	case opc_fstore_3:
	    ce->varOff[3] = 1;
	    pc++;
	    continue;

	case opc_lload:
	case opc_dload:
	case opc_lstore:
	case opc_dstore:
	    ce->varOff[pc[1]+1] = 1;
	    // fall thru
	case opc_iload:
	case opc_aload:
	case opc_fload:
	case opc_istore:
	case opc_astore:
	case opc_fstore:
	    ce->varOff[pc[1]] = 1;
	    pc += 2;
	    continue;

	case opc_newarray:
	case opc_bipush:
	case opc_ldc:
	case opc_ldc_quick:
	case opc_ret:
	    pc += 2;
	    continue;

	case opc_iinc:
	case opc_anewarray:
	case opc_anewarray_quick:
	case opc_sipush:
	case opc_ldc_w:
	case opc_ldc_w_quick:
	case opc_ldc2_w:
	case opc_ldc2_w_quick:
	case opc_instanceof:
	case opc_instanceof_quick:
	case opc_checkcast:
	case opc_checkcast_quick:
	case opc_new:
	case opc_new_quick:
	case opc_putstatic:
	case opc_putstatic_quick:
	case opc_putstatic2_quick:
	case opc_getstatic:
	case opc_getstatic_quick:
	case opc_getstatic2_quick:
	case opc_putfield:
	case opc_putfield_quick:
	case opc_putfield_quick_w:
	case opc_putfield2_quick:
	case opc_getfield:
	case opc_getfield_quick:
	case opc_getfield_quick_w:
	case opc_getfield2_quick:
	case opc_invokevirtual:
	case opc_invokevirtual_quick:
	case opc_invokevirtual_quick_w:
	case opc_invokenonvirtual:
	case opc_invokenonvirtual_quick:
	case opc_invokesuper_quick:
	case opc_invokestatic:
	case opc_invokestatic_quick:
	case opc_invokevirtualobject_quick:
	case opc_jsr:
	case opc_ifeq:
	case opc_ifge:
	case opc_ifgt:
	case opc_ifle:
	case opc_iflt:
	case opc_ifne:
	case opc_if_icmpeq:
	case opc_if_icmpne:
	case opc_if_icmpge:
	case opc_if_icmpgt:
	case opc_if_icmple:
	case opc_if_icmplt:
	case opc_if_acmpeq:
	case opc_if_acmpne:
	case opc_ifnull:
	case opc_ifnonnull:
	case opc_goto:
	    pc += 3;
	    continue;

	case opc_invokeinterface:
	case opc_invokeinterface_quick:
	case opc_jsr_w:
	case opc_goto_w:
	    pc += 5;
	    continue;

	case opc_multianewarray:
	case opc_multianewarray_quick:
	    pc += 4;
	    continue;

	case opc_wide:
	    var = GET_INDEX(pc + 2);

	    switch(pc[1])
	    {
	    case opc_lload:
	    case opc_dload:
	    case opc_lstore:
	    case opc_dstore:
		ce->varOff[var+1] = 1;
		// fall thru
	    case opc_iload:
	    case opc_aload:
	    case opc_fload:
	    case opc_istore:
	    case opc_astore:
	    case opc_fstore:
		ce->varOff[var] = 1;
		pc += 4;
		continue;

	    case opc_ret:
		pc += 4;
		continue;

	    case opc_iinc:
		pc += 6;
		continue;

	    default:
		sysAssert(0);
		ce->err = "Undefined opcode";
		return FALSE;
	    }

	case opc_tableswitch:
	{
	    long *ltbl = (long *) ALIGN((int)pc + 1);
	    long low = swap(ltbl[1]);
	    long high = swap(ltbl[2]);

	    pc = (unsigned char *)(ltbl + 3 + high - low + 1);
	}
	continue;

	case opc_lookupswitch:
	{
	    long *ltbl = (long *) ALIGN((int)pc + 1);
	    long npairs = swap(ltbl[1]);

	    pc = (unsigned char *)(ltbl + 2 + npairs * 2);
	}
	continue;

	default:
	    sysAssert(opcode_length[*pc] == 1);
	    pc++;
	}
    }
    return TRUE;
}


/* This recursive function walks the control flow graph of exception handlers
 * in order to mark used variables. Each reachable range is visited once.
 */
static void
WalkXHControlFlow(CompEnv *ce, RangeHdr *rh)
{
    RangeHdr	    *targRh, *nextRh;
    unsigned char   *nextRhPc, *initial_pc = ce->mb->code;

    while (1)
    {
	rh->rangeFlags |= RF_XHVISITED;
	nextRh = rh->nextRh;
	if  (nextRh == NULL)
	    nextRhPc = initial_pc + ce->mb->code_length;
	else
	    nextRhPc = initial_pc + nextRh->bcpc;

	// ignore any ranges that do not contain code...
	if ((rh->rangeFlags & RF_DATABLOCK) == 0)
	    MarkXHVars(ce, initial_pc + rh->bcpc, nextRhPc);

	targRh = rh->targRh;
	if  (targRh)
	{
	    if ((targRh->rangeFlags & RF_XHVISITED) == 0)
		WalkXHControlFlow(ce, targRh);
	}
	rh = rh->contRh;
	if  (rh)
	{
	    if ((rh->rangeFlags & RF_XHVISITED) == 0)
		continue;
	}
	break;
    }
}


/* Write the index number of each catch frame in the entry range of the handler.
 * This will be used in Body to store the compiled address of the handler in the
 * exception table.
 */
static bool_t
IndexExceptionHandlers(CompEnv *ce)
{
    struct CatchFrame *cf = ce->mb->exception_table;
    long cnt = ce->mb->exception_table_length;
    long i;
    RangeHdr *rh;

    for (i = 0; i < cnt; i++, cf += 1)
    {
	rh = ce->rp[cf->handler_pc].hdr;
	rh->xIndex = i;
	rh->topOnEntry = 1;
	rh->topOnExit++;
	WalkControlFlow(rh, FREQ_UNIT_LEV);
	WalkXHControlFlow(ce, rh);
    }
    return TRUE;
}


/* Variables that cannot be allocated in a register because they are accessed
 * as float, or double (long OK), or because they are used in an exception
 * handler are marked by a non-zero offset and their varFreq is reset to 0.
 * This function allocates mostly used variables in registers.
 * Theory:
 * We assume that a memory access costs 3 register accesses.
 * The cost of allocating a parameter in a register is equivalent to 3 memory
 * accesses: save the callee-saved reg, load the parameter (passed on the
 * stack), restore the reg. Therefore, a parameter must be used more than 4.5
 * times to be allocated in a register: 3 + n/3 < n => n > 4.5
 * The cost of allocating a local variable in a reg is equivalent to 2 memory
 * accesses (save + restore).
 * Therefore, a local variable must be used more than 3 times to be
 * allocated in a register: 2 + n/3 = n => n > 3
 * Well, theory does not work on a Pentium (better on an 486).
 * Experience shows better results on Pentium with the following code:
 */
static bool_t
AllocVariables(CompEnv *ce)
{
    struct methodblock *mb = ce->mb;
    int     nargs = mb->args_size;
    int     nlocals = mb->nlocals;
    int     var, maxVar, nRegVCand;
    ModReg  reg;
    long    *voffp, off;
    long    frameSize;
    long    *vfreqp, maxFreq;

    /* Count the number of qualifying locals and reset varFreq of locals
     * marked with varOff == 1 and of locals with a too low freq
     */
    nRegVCand = nlocals;
    /* Visit parameters first */
    for (var = nargs, voffp = &ce->varOff[0], vfreqp = &ce->varFreq[0];
	 --var >= 0; voffp++, vfreqp++)
    {
    	if (*voffp || *vfreqp <= FREQ_UNIT)
	{
	    *vfreqp = 0;
	    nRegVCand--;
	}
	else
	    sysAssert(*vfreqp > 0);
    }
    /* Visit local variables next */
    for (var = nlocals - nargs; --var >= 0; voffp++, vfreqp++)
    {
    	if (*voffp || *vfreqp <= 0)
	{
	    *vfreqp = 0;
	    nRegVCand--;
	}
	else
	    sysAssert(*vfreqp > 0);
    }
    if (nRegVCand <= MR_ESP - MR_EBX)
    {
    	/* enough regs for everybody */
	reg = MR_EBX;
	for (var = nlocals, voffp = &ce->varOff[0], vfreqp = &ce->varFreq[0];
	     --var >= 0; voffp++, vfreqp++)
	    if (*vfreqp)
		*voffp = -reg++;
    }
    else
    {
	/* allocate mostly used variables in registers */
	for (reg = MR_EBX; reg < MR_ESP && nRegVCand > 0; reg++)
	{
	    maxFreq = 0;
	    maxVar = -1;
	    for (var = 0, vfreqp = &ce->varFreq[0]; var < nlocals; var++, vfreqp++)
	    {
		if (*vfreqp > maxFreq)
		{
		    maxFreq = *vfreqp;
		    maxVar = var;
		}
	    }
	    sysAssert(maxVar >= 0 && maxVar < nlocals);
	    ce->varFreq[maxVar] = 0;
	    ce->varOff[maxVar] = -reg;
	    nRegVCand--;
	}
    }
    /* reg is live */

    /* allocate remaining arguments in memory */
    off = 0;
    for (var = nargs, voffp = &ce->varOff[0]; --var >= 0; voffp++)
    {
	if (*voffp >= 0)	// 0 cannot indicate a reg (MR_EAX not used here)
	    *voffp = off;	// allocate in memory
	off += 4;
    }

    /* allocate remaining locals relative to esp */
    off = ce->codeInfo->baseOff + 4*mb->maxstack;
    for (var = nlocals - nargs, voffp = &ce->varOff[nargs]; --var >= 0; voffp++)
    {
	if  (*voffp >= 0)	// 0 cannot indicate a reg (MR_EAX not used here)
	{
	    *voffp = off;	// allocate in memory
	    off += 4;
	}
    }

    if (mb->exception_table_length > 0 || (mb->fb.access & ACC_SYNCHRONIZED))
	frameSize = FRAME_WITH_XHANDLER_SIZE;
    else
	frameSize = 4*(reg - MR_EBX) + 4;   // callee-saved and return address

#ifdef IN_NETSCAPE_NAVIGATOR
    off += 4;
#endif
    ce->codeInfo->localSize = off;
    ce->codeInfo->frameSize = frameSize;

    /* adjust offset of arguments relative to esp */
    off += frameSize;
    for (var = nargs, voffp = &ce->varOff[0]; --var >= 0; voffp++)
	if  (*voffp >= 0)
	    *voffp += off;	// allocated in memory

    return TRUE;
}


static bool_t
Prologue(CompEnv *ce)
{
    struct methodblock *mb = ce->mb;
    ModReg  reg;
    int     nSaved, var;
    int     nargs = mb->args_size;
    long    *voffp, off, xctxt;
    ModReg  mr;
    Item    arg;

    AllocContext(ce, &ce->ctxt, 0);
#ifdef	DEBUG
    GenByteLong(ce, TESTI_AL + 1, (long)mb); // to see mb value in disassembly
#endif
    if (mb->exception_table_length > 0 || (mb->fb.access & ACC_SYNCHRONIZED))
    {
/* method prologue (with exception handling, or synchronized):
    push    ebx
    push    esi
    push    edi
    xor     eax, eax
    push    ebp
    push    offset HandleException
    push    dword ptr fs:[eax]
    mov     dword ptr fs:[eax], esp
    push    xctxt		// xctxt of first opcode
    push    edx 		// mb parameter

    sub     esp, localSize
or
    push    0			// for annotation in Netscape Navigator
    sub     esp, localSize-4

    esp has to point to top of operand stack which may be trashed
    by native call results (this is safe)
*/
	ce->codeInfo->xframe2esp = -ce->codeInfo->localSize - 8;
	GenByte(ce, PUSHR_L + RC_EBX);
	GenByte(ce, PUSHR_L + RC_ESI);
	GenByte(ce, PUSHR_L + RC_EDI);
	GenOpRegReg(ce, XORR_L, MR_EAX, MR_EAX);
	GenByte(ce, PUSHR_L + RC_EBP);
	GenByteLong(ce, PUSHI_L, (long)HandleException);
	GenByte(ce, FSSEG); GenIndirEA(ce, 0xFF, PUSH_L, MR_EAX, 0);
	GenByte(ce, FSSEG); GenIndirEA(ce, STORE_L, reg3Map[MR_ESP], MR_EAX, 0);
	xctxt = ce->rp[0].hdr->xctxt;
	if (xctxt == 0)
	    GenByte(ce, PUSHR_L + RC_EAX);
        else
	    GenByteLong(ce, PUSHI_L, xctxt);
	GenByte(ce, PUSHR_L + RC_EDX);

#ifdef IN_NETSCAPE_NAVIGATOR
	GenByte(ce, PUSHR_L + RC_EAX);
#endif
    }
    else
    {
/* method prologue (without exception handling, and not synchronized):
    push    callee-saved    // as used
    push    edx 	    // if mb used (never if jit compilation, see below)

    sub     esp, localSize
or
    push    0			// for annotation in Netscape Navigator
    sub     esp, localSize-4
*/
    /* Actually, mb is never saved since its value is known at compile time and
     * can be inserted in the code wherever needed as an immediate value.
     * This only works with a just-in-time compiler.
     */
	ce->codeInfo->xframe2esp = 0;	// no xframe
	for (nSaved = (ce->codeInfo->frameSize - 4) >> 2, reg = MR_EBX; --nSaved >= 0; )
	    GenByte(ce, PUSHR_L + regMap[reg++]);

#ifdef IN_NETSCAPE_NAVIGATOR
	Gen2Bytes(ce, PUSHI_B, 0);
#endif
    }
    off = ce->codeInfo->localSize;
#ifdef IN_NETSCAPE_NAVIGATOR
    off -= 4;
#endif
    GenAddImmRL(ce, MR_ESP, -off);

    /* load parameters into their permanent registers */
    arg.mr = MR_BASE + MR_ESP;
    arg.offset = ce->codeInfo->localSize + ce->codeInfo->frameSize;
    arg.size = 4;
    for (var = nargs, voffp = &ce->varOff[0]; --var >= 0; voffp++)
    {
	mr = -*voffp;
	if  (mr > 0)	    // allocated in register
	{
	    GenByte(ce, LOAD_L);
	    GenEA(ce, reg3Map[mr], &arg);
	}
	arg.offset += 4;
    }

    if (mb->fb.access & ACC_SYNCHRONIZED)
    {
	if (mb->fb.access & ACC_STATIC)
	    // CompSupport_monitorenter(cbHandle(fieldclass(&mb->fb)));
	    PushImm(ce, (long)(cbHandle(fieldclass(&mb->fb))));
	else
	{
	    // CompSupport_monitorenter(this);
	    off = ce->varOff[0];
	    if (off < 0)
		arg.mr = -off;
	    else
	    // no var reg if used in exception handler
	    {
		arg.mr = MR_BASE + MR_ESP;
		arg.offset = off;
	    }
	    arg.size = 4;
	    Push(ce, &arg);
	}
	CallCompSupport(ce, (char *)CompSupport_monitorenter,
	    ce->ctxt->top, 4);
    }
    ce->rp[0].hdr->ctxt = ce->ctxt; // for Body
    ce->ctxt = 0;
    return TRUE;
}


static bool_t
Epilogue(CompEnv *ce)
{
    struct methodblock *mb = ce->mb;
    ModReg	reg;
    int 	nSaved;
    Item	*top, arg;
    long	off;
    RangeHdr	*rh = ce->epilogueRh;
    Label	a, b, h;

    if (!(rh->rangeFlags & RF_VISITED))
	return TRUE;	    // method is an endless loop, no epilogue necessary

    ce->ctxt = rh->ctxt;
    ce->xctxt = -1; // not used anyway
    sysAssert(rh->xctxt == -1);
    rh->ctxt = 0;
    FlushVarCache(ce->ctxt);
    top = ce->ctxt->top;
    a = rh->link;
    b = rh->xlink;  // keep the exception context of opc_return
    // fix shortest dist (i.e. higher link) first, because of jump optimization
    if (b > a) { h = a; a = b; b = h; }
    FixLink(ce, a);
    FixLink(ce, b);

    if (mb->fb.access & ACC_SYNCHRONIZED)
    {
    	PushImm(ce, ce->codeInfo->localSize + 3*4);	// obj, obj2xctxt, mb
	if (mb->fb.access & ACC_STATIC)
	    // CompSupport_monitorexit(cbHandle(fieldclass(&mb->fb)), obj2xctxt);
	    PushImm(ce, (long)(cbHandle(fieldclass(&mb->fb))));
	else
	{
	    // CompSupport_monitorexit(this, obj2xctxt);
	    if (mb->exception_table_length > 0)
    		// this register can be trashed
    		off = ce->codeInfo->localSize + ce->codeInfo->frameSize;
	    else
		off = ce->varOff[0];

	    if (off < 0)
		arg.mr = -off;
	    else
	    // no var reg if used in exception handler
	    {
		arg.mr = MR_BASE + MR_ESP;
		arg.offset = off;
	    }
	    arg.size = 4;
	    Push(ce, &arg);
	}
	CallCompSupport(ce, (char *)CompSupport_monitorexit, top, 8);

	switch  (mb->CompiledCodeFlags & 7)
	{
	case 0: // int
	    LoadOpnd(ce, top - 1, RS_EAX);
	    break;

	case 1: // long
	    sysAssert((regSetOfMr[(top - 1)->mr] & RS_EAX) == RS_EMPTY);
	    LoadOpnd(ce, top - 2, RS_EAX);
	    LoadOpnd(ce, top - 1, RS_EDX);
	    break;

	case 2: // float
	    LoadFOpnd(ce, top - 1, 4);
	    break;

	case 3: // double
	    LoadFOpnd(ce, top - 2, 8);
	    break;
	}
    }
    // else result is already loaded

    if (ce->codeInfo->frameSize == FRAME_WITH_XHANDLER_SIZE)
    {
/* method epilogue (with exception handling, or synchronized):
    add     esp, -xframe2esp
    pop     dword ptr fs:[0]
    pop     ecx
    pop     ebp
    pop     edi
    pop     esi
    pop     ebx
    ret     4*nargs
*/
	GenAddImmRL(ce, MR_ESP, ce->codeInfo->localSize + 8);
	GenByte(ce, FSSEG); Gen2BytesLong(ce, 0x8F, POP_L + 0x05, 0);
	GenByte(ce, POPR_L + RC_ECX);
	GenByte(ce, POPR_L + RC_EBP);
	GenByte(ce, POPR_L + RC_EDI);
	GenByte(ce, POPR_L + RC_ESI);
	GenByte(ce, POPR_L + RC_EBX);
    }
    else
    {
/*
method epilogue (without exception handling, and not synchronized):
    add     esp, localSize  // (+4 if mb used) if non-zero
    pop     callee-saved    // as used
    ret     4*nargs
*/
	GenAddImmRL(ce, MR_ESP, ce->codeInfo->localSize);

	for (nSaved = (ce->codeInfo->frameSize - 4) >> 2, reg = MR_EBX + nSaved;
		--nSaved >= 0; )
	    GenByte(ce, POPR_L + regMap[--reg]);
    }
    if (mb->args_size)
    {
	GenByte(ce, RETP); GenWord(ce, 4*mb->args_size);
    }
    else
	GenByte(ce, RET);

    return TRUE;
}


static bool_t
Body(CompEnv *ce)
{
#define SIZE_AND_STACK(size, stack) pc += size; top += stack; continue
#define SIZE_AND_STACK_BUT(size, stack) pc += size; top += stack
    struct methodblock *mb = ce->mb;
    struct execenv *ee = ce->ee;
    ClassClass *cb = fieldclass(&mb->fb);
    union cp_item_type *cpool = cbConstantPool(cb);
    unsigned char *type_table = cpool[CONSTANT_POOL_TYPE_TABLE_INDEX].p;
    struct CatchFrame *cf = mb->exception_table;
    struct fieldblock *fb;
    struct methodblock *mb_type;
    char	    isig;
    unsigned char   *initial_pc = mb->code;
    unsigned char   *max_pc = initial_pc + mb->code_length;
    unsigned char   *pc, *nextRhPc;
    RangeHdr	    *rh, *nextRh, *targRh, *contRh;
    int 	    var, clone, push, dimensions;
    unsigned	    index, type;
    long	    off;
    unsigned char   opcode;
    Item	    *top, arg;

    top = NULL;
    rh = NULL;
    nextRh = ce->rp[0].hdr;
    nextRhPc = initial_pc;

    pc = initial_pc;
    while (pc <= max_pc)
    // instead of pc < max_pc: finalize the last range before the epilogue
    {
	if  (ce->pc >= ce->pcRedZone)
	{
	    ce->err = pcInRedZone;
	    return FALSE;
	}

	if  (pc >= nextRhPc)
	{
	    // a new range starts here
	    sysAssert(pc == nextRhPc);
	    if (rh)
	    {
		// finalize the current range
		ce->ctxt->top = top;
		targRh = rh->targRh;
		contRh = rh->contRh;
		clone = 0;
		if  (targRh != 0)
		{
		    // no need to flush the opstack, it was done before jumping
		    // to the target (if targRh->joinCnt > 0)
		    if (targRh->pc == 0 && targRh->ctxt == 0)
		    {
			// forward target, context not set yet
			if  (!CopyContext(ce, targRh, &clone))
			    return FALSE;

			sysAssert(targRh->joinCnt == 0
                                  || (targRh->ctxt->regRefCnt[MR_EAX] == 0
				      && targRh->ctxt->regRefCnt[MR_EDX] == 0
				      && targRh->ctxt->regRefCnt[MR_ECX] == 0));
		    }
		}
		if  (contRh != 0)
		{
		    if (contRh->joinCnt > 0)
			FlushOpStack(ce, top);

		    if (contRh->ctxt == 0)
		    {
			// (always forward) continuation context not set yet
			if  (!CopyContext(ce, contRh, &clone))
			    return FALSE;
		    }
		}
		/* does not really speed up loops
		else
		{
    		    while ((long)ce->pc & 15)
    			GenByte(ce, 0x90);	// align loops on a 16-byte boundaries
		}
		*/
		if  (clone == 0)
		    FreeContext(ce->ctxt);
		ce->ctxt = 0;
	    }
	    rh = nextRh;
	    if (rh == 0)
		// we are done
		return TRUE;

	    nextRh = rh->nextRh;
	    if (nextRh == NULL)
		nextRhPc = max_pc;
	    else
		nextRhPc = initial_pc + nextRh->bcpc;

	    sysAssert((rh->rangeFlags & RF_DATABLOCK) == 0);
	    sysAssert(rh->rangeFlags & RF_VISITED);
	    ce->ctxt = rh->ctxt;
	    rh->ctxt = 0;
	    if (ce->ctxt == 0)
	    {
		// this range is the target of one or several backward jumps or
		// the range of an exception handler:
		// the context is still unknown, except for the opstack height
		// which was computed in the first pass. We assume all operands
		// are flushed (by Jcc if backwards jump).
		if  (!AllocContext(ce, &ce->ctxt, rh->topOnEntry))
		    return FALSE;
	    }
	    else
	    {
		// this range is the target of at least one forward jump or
		// is a continuation range
		if  (rh->joinCnt > 0)
		{
		    // opstack is flushed at jump point or in previous range
		    FlushVarCache(ce->ctxt);
		    sysAssert(ce->ctxt->regRefCnt[MR_EAX] == 0
			      && ce->ctxt->regRefCnt[MR_EDX] == 0
			      && ce->ctxt->regRefCnt[MR_ECX] == 0);
		}
	    }

	    ce->xctxt = rh->xctxt;
	    top = ce->ctxt->top;
	    sysAssert(top == ce->ctxt->base + rh->topOnEntry);

	    if (rh->rangeFlags & RF_XHANDLER)
	    {
		(top - 1)->mr = MR_EAX;
		(top - 1)->size = 4;
		(top - 1)->var = NOT_A_VAR;
                ce->ctxt->regRefCnt[MR_EAX] = 1;
                ce->ctxt->regRefCnt[MR_EDX] = 0;
                ce->ctxt->regRefCnt[MR_ECX] = 0;
		(cf + rh->xIndex)->compiled_CatchFrame =
		    (void *)(ce->pc - ce->compiledCode);
		// compiled_CatchFrame is actually the relative
		// address of the compiled handler
	    }
	    if (rh->rangeFlags & RF_SET_XCTXT)
	    {
		FixLink(ce, rh->xlink);
		rh->xpc = ce->pc;
		if (*pc == opc_return
		    // only opcode in this range
		    || (*pc == opc_pop && pc[1] == opc_return))
		    // only 2 opcodes in this xhandler
    		    ;	// optimization: not necessary to set xctxt
		else
		{
		    if (rh->rangeFlags & RF_JSR)
			off = 4; // for the return address, espLevel not corrected
		    else
			off = 0;

		    GenIndirEA(ce, MOVI_L, 0, MR_ESP,
			ce->codeInfo->localSize + 4 - ce->ctxt->espLevel + off);
		    GenLong(ce, rh->xctxt);
		}
	    }
	    FixLink(ce, rh->link);
	    rh->pc = ce->pc;
	    if (rh->rangeFlags & RF_JSR)
	    {
    		// continue and break statements are allowed in finally,
		// so we have to restore espLevel by popping the ret addr
		sysAssert(ce->ctxt->espLevel == 0);
		switch  (*pc)
		{
		case opc_astore:
		    var = pc[1];
		    pc += 2;
		    break;
	
		case opc_astore_0:
		    var = 0;
		    pc++;
		    break;
	
		case opc_astore_1:
		    var = 1;
		    pc++;
		    break;
	
		case opc_astore_2:
		    var = 2;
		    pc++;
		    break;
	
		case opc_astore_3:
		    var = 3;
		    pc++;
		    break;
	
                case opc_wide:
    		    if (pc[1] == opc_astore)
		    {
			var = GET_INDEX(pc + 2);
			pc += 4;
			break;
                    }
		    // fall thru
		default:
    		    var = -1;
		}
		if (var != -1)
		{
		    off = ce->varOff[var];
		    if (off < 0)
                        GenByte(ce, POPR_L + regMap[-off]);
                    else
		    {
			arg.size = 4;
			arg.mr = MR_BASE + MR_ESP;
			arg.offset = off;
			GenByte(ce, 0x8F);
			GenEA(ce, POP_L, &arg); // esp += 4; [esp+off] = [esp-4];
                    }
		    if  (pc >= nextRhPc)
			// next opcode belongs to the next range
			continue;
		}
		else
		{
    		    top++; // top was not incremented for the return address at opc_jsr
		    (top - 1)->size = 4;
		    (top - 1)->mr = MR_BASE + MR_ESP;
		    (top - 1)->offset = OpndOffset(ce, top - 1);
		    GenByte(ce, 0x8F);
		    GenEA(ce, POP_L, top - 1); // esp += 4; [esp+off] = [esp-4];
                }
	    }
	}

	check((unsigned)(top - ce->ctxt->base) <= mb->maxstack);

	ce->ctxt->top = top;
	opcode = *pc;
	switch (opcode)
	{
	case opc_nop:
	    SIZE_AND_STACK(1, 0);

#define DO_LOAD_DWORD(top, num) 					       \
            off = ce->varOff[num];                                             \
            if  (off < 0)                                                      \
                (top)->mr = -off;                                              \
            else                                                               \
            {                                                                  \
                (top)->mr = MR_BASE + MR_ESP;                                  \
                (top)->offset = off;                                           \
            }                                                                  \
            (top)->size = 4;                                                   \
            (top)->var = num;

#define DO_LOAD_QWORD(top, num) 					       \
            DO_LOAD_DWORD(top + 0, num + 0)                                    \
            DO_LOAD_DWORD(top + 1, num + 1)

	case opc_aload:
	case opc_iload:
	case opc_fload:
	    var = pc[1];
	    DO_LOAD_DWORD(top, var)
	    SIZE_AND_STACK(2, 1);

	case opc_lload:
	case opc_dload:
	    var = pc[1];
	    DO_LOAD_QWORD(top, var)
	    SIZE_AND_STACK(2, 2);

#define OPC_DO_LOAD_DWORD_n(num)					       \
        case opc_iload_##num:                                                  \
        case opc_aload_##num:                                                  \
        case opc_fload_##num:                                                  \
            DO_LOAD_DWORD(top, num)                                            \
            SIZE_AND_STACK(1, 1);

#define OPC_DO_LOAD_QWORD_n(num)					       \
        case opc_lload_##num:                                                  \
        case opc_dload_##num:                                                  \
            DO_LOAD_QWORD(top, num)                                            \
            SIZE_AND_STACK(1, 2);

	OPC_DO_LOAD_DWORD_n(0)
	OPC_DO_LOAD_DWORD_n(1)
	OPC_DO_LOAD_DWORD_n(2)
	OPC_DO_LOAD_DWORD_n(3)

	OPC_DO_LOAD_QWORD_n(0)
	OPC_DO_LOAD_QWORD_n(1)
	OPC_DO_LOAD_QWORD_n(2)
	OPC_DO_LOAD_QWORD_n(3)

	case opc_istore:
	case opc_astore:
	    StoreOpnd(ce, pc[1], top - 1);
	    SIZE_AND_STACK(2, -1);

	case opc_fstore:
	    StoreFOpnd(ce, pc[1], top - 1, 4);
	    SIZE_AND_STACK(2, -1);

	case opc_lstore:
	    StoreOpnd(ce, pc[1] + 1, top - 1);	// high first for anti-aliasing
	    StoreOpnd(ce, pc[1] + 0, top - 2);
	    SIZE_AND_STACK(2, -2);

	case opc_dstore:
	    StoreFOpnd(ce, pc[1], top - 2, 8);
	    SIZE_AND_STACK(2, -2);

#define OPC_DO_STORE_DWORD_n(num)					       \
        case opc_istore_##num:                                                 \
        case opc_astore_##num:                                                 \
            StoreOpnd(ce, num, top - 1);                                       \
            SIZE_AND_STACK(1, -1);

#define OPC_DO_STORE_LONG_n(num)					       \
        case opc_lstore_##num:                                                 \
            StoreOpnd(ce, num + 1, top - 1);                                   \
            StoreOpnd(ce, num + 0, top - 2);                                   \
            SIZE_AND_STACK(1, -2);

#define OPC_DO_STORE_FLOAT_n(num)					       \
        case opc_fstore_##num:                                                 \
            StoreFOpnd(ce, num, top - 1, 4);                                   \
            SIZE_AND_STACK(1, -1);

#define OPC_DO_STORE_DOUBLE_n(num)					       \
        case opc_dstore_##num:                                                 \
            StoreFOpnd(ce, num, top - 2, 8);                                   \
            SIZE_AND_STACK(1, -2);

	OPC_DO_STORE_DWORD_n(0)
	OPC_DO_STORE_DWORD_n(1)
	OPC_DO_STORE_DWORD_n(2)
	OPC_DO_STORE_DWORD_n(3)

	OPC_DO_STORE_LONG_n(0)
	OPC_DO_STORE_LONG_n(1)
	OPC_DO_STORE_LONG_n(2)
	OPC_DO_STORE_LONG_n(3)

	OPC_DO_STORE_FLOAT_n(0)
	OPC_DO_STORE_FLOAT_n(1)
	OPC_DO_STORE_FLOAT_n(2)
	OPC_DO_STORE_FLOAT_n(3)

	OPC_DO_STORE_DOUBLE_n(0)
	OPC_DO_STORE_DOUBLE_n(1)
	OPC_DO_STORE_DOUBLE_n(2)
	OPC_DO_STORE_DOUBLE_n(3)

	case opc_return:
	case opc_areturn:
	case opc_ireturn:
	case opc_lreturn:
	case opc_freturn:
	case opc_dreturn:
	    /* The following code avoids to flush the return value,
	     * epilogueRh->jointCnt is always 0 now
	     */
	    if (mb->fb.access & ACC_SYNCHRONIZED)
                FlushOpStack(ce, top);
	    else
	    {
		switch  (mb->CompiledCodeFlags & 7)
		{
		case 0: // int
		    LoadOpnd(ce, top - 1, RS_EAX);
		    break;
	
		case 1: // long
		    if ((regSetOfMr[(top - 1)->mr] & RS_EAX) == RS_EMPTY)
		    {
			LoadOpnd(ce, top - 2, RS_EAX);
			LoadOpnd(ce, top - 1, RS_EDX);
		    }
		    else if ((regSetOfMr[(top - 2)->mr] & RS_EDX) == RS_EMPTY)
		    {
			LoadOpnd(ce, top - 1, RS_EDX);
			LoadOpnd(ce, top - 2, RS_EAX);
		    }
		    else
		    {
			FlushOpnd(ce, top - 2);
			FlushOpnd(ce, top - 1);
			LoadOpnd(ce, top - 2, RS_EAX);
			LoadOpnd(ce, top - 1, RS_EDX);
		    }
		    break;
	
		case 2: // float
		    LoadFOpnd(ce, top - 1, 4);
		    break;
	
		case 3: // double
		    LoadFOpnd(ce, top - 2, 8);
		    break;
		}
	    }
	    GenByte(ce, 0xE9);
	    Link(ce, &ce->epilogueRh->link);
	    SIZE_AND_STACK(1, 0);

	case opc_i2f:
	    MakeFOpndAccessible(ce, top - 1, 4);
	    GenByte(ce, 0xDB);
	    GenEA(ce, FILD, top - 1);
	    FreeRegs(ce, (top - 1)->mr);
	    (top - 1)->mr = MR_FST;
	    (top - 1)->size = 4;
	    (top - 1)->var = NOT_A_VAR;
#ifdef	STRICT_FLOATING
	    FlushOpnd(ce, top - 1);
#endif
	    SIZE_AND_STACK(1, 0);

	case opc_i2l:
	    LoadOpnd(ce, top - 1, RS_EAX);
	    (top - 0)->size = 4;
	    (top - 0)->mr = GetReg(ce, RS_EDX);
	    (top - 0)->var = NOT_A_VAR;
	    GenByte(ce, CDQ);
	    SIZE_AND_STACK(1, 1);

	case opc_i2d:
	    MakeFOpndAccessible(ce, top - 1, 4);
	    GenByte(ce, 0xDB);
	    GenEA(ce, FILD, top - 1);
	    FreeRegs(ce, (top - 1)->mr);
	    (top - 1)->mr = MR_FST;
	    (top - 0)->mr = MR_FST;
	    (top - 1)->size = 8;
	    (top - 0)->size = 0;
	    (top - 1)->var = NOT_A_VAR;
	    (top - 0)->var = NOT_A_VAR;
#ifdef	STRICT_FLOATING
	    ce->ctxt->top = top + 1;	// for sysAssert in FlushOpnd
	    FlushOpnd(ce, top - 1);
	    FlushOpnd(ce, top - 0);
#endif
	    SIZE_AND_STACK(1, 1);

	case opc_f2i:
	    PushFOpnd(ce, top - 1, 4, 4);
	    CallCompSupport(ce, (char *)CompSupport_f2i, top - 1, 4);
	    (top - 1)->mr = GetReg(ce, RS_EAX);
	    (top - 1)->size = 4;
	    (top - 1)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, 0);

	case opc_f2l:
	    PushFOpnd(ce, top - 1, 4, 4);
	    CallCompSupport(ce, (char *)CompSupport_f2l, top - 1, 4);
	    (top - 1)->mr = GetReg(ce, RS_EAX);
	    (top - 0)->mr = GetReg(ce, RS_EDX);
	    (top - 1)->size = 4;
	    (top - 0)->size = 4;
	    (top - 1)->var = NOT_A_VAR;
	    (top - 0)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, 1);

	case opc_f2d:
	    LoadFOpnd(ce, top - 1, 4);
	    (top - 1)->size = 8;
	    (top - 1)->var = NOT_A_VAR;
	    (top - 0)->mr = MR_FST;
	    (top - 0)->size = 0;
	    (top - 0)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, 1);

	case opc_l2i:
	    FreeRegs(ce, (top - 1)->mr);
	    (top - 2)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, -1);

	case opc_l2f:
	    MakeFOpndAccessible(ce, top - 2, 8);
	    GenByte(ce, 0xDF);
	    GenEA(ce, FILD_Q, top - 2);
	    FreeRegs(ce, (top - 2)->mr);
	    FreeRegs(ce, (top - 1)->mr);
	    (top - 2)->mr = MR_FST;
	    (top - 2)->size = 4;
	    (top - 2)->var = NOT_A_VAR;
#ifdef	STRICT_FLOATING
	    FlushOpnd(ce, top - 2);
#endif
	    SIZE_AND_STACK(1, -1);

	case opc_l2d:
	    MakeFOpndAccessible(ce, top - 2, 8);
	    GenByte(ce, 0xDF);
	    GenEA(ce, FILD_Q, top - 2);
	    FreeRegs(ce, (top - 2)->mr);
	    FreeRegs(ce, (top - 1)->mr);
	    (top - 2)->mr = MR_FST;
	    (top - 2)->size = 8;
	    (top - 2)->var = NOT_A_VAR;
	    (top - 1)->mr = MR_FST;
	    (top - 1)->size = 0;
	    (top - 1)->var = NOT_A_VAR;
#ifdef	STRICT_FLOATING
	    FlushOpnd(ce, top - 2);
	    FlushOpnd(ce, top - 1);
#endif
	    SIZE_AND_STACK(1, 0);

	case opc_d2i:
	    PushFOpnd(ce, top - 2, 8, 8);
	    CallCompSupport(ce, (char *)CompSupport_d2i, top - 2, 8);
	    (top - 2)->mr = GetReg(ce, RS_EAX);
	    (top - 2)->size = 4;
	    (top - 2)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, -1);

	case opc_d2f:
	    LoadFOpnd(ce, top - 2, 8);
	    (top - 2)->size = 4;
	    (top - 2)->var = NOT_A_VAR;
#ifdef	STRICT_FLOATING
	    (top - 1)->mr = MR_IMM;	// for debug checking in FlushOpnd
	    FlushOpnd(ce, top - 2);
#endif
	    SIZE_AND_STACK(1, -1);

	case opc_d2l:
	    PushFOpnd(ce, top - 2, 8, 8);
	    CallCompSupport(ce, (char *)CompSupport_d2l, top - 2, 8);
	    (top - 2)->mr = GetReg(ce, RS_EAX);
	    (top - 1)->mr = GetReg(ce, RS_EDX);
	    (top - 2)->size = 4;
	    (top - 1)->size = 4;
	    (top - 2)->var = NOT_A_VAR;
	    (top - 1)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, 0);

	case opc_int2byte:
	    Int2BW(ce, top - 1, 1, 0xBE, pc + 1);   // movsx erx, byte
	    SIZE_AND_STACK(1, 0);

	case opc_int2char:
	    Int2BW(ce, top - 1, 2, 0xB7, pc + 1);   // movzx erx, word
	    SIZE_AND_STACK(1, 0);

	case opc_int2short:
	    Int2BW(ce, top - 1, 2, 0xBF, pc + 1);   // movsx erx, word
	    SIZE_AND_STACK(1, 0);

	case opc_fcmpl:
	case opc_fcmpg:
	    FCompare(ce, top - 2, top - 1, 4, opcode == opc_fcmpl ? -1 : 1,
		pc + 1);
	    SIZE_AND_STACK(1, -1);

	case opc_lcmp:
	    LCompare(ce, top - 4, top - 2, pc + 1);
	    SIZE_AND_STACK(1, -3);

	case opc_dcmpl:
	case opc_dcmpg:
	    FCompare(ce, top - 4, top - 2, 8, opcode == opc_dcmpl ? -1 : 1,
		pc + 1);
	    SIZE_AND_STACK(1, -3);

	case opc_bipush:
	    top->mr = MR_IMM;
	    top->offset = (signed char)(pc[1]);
	    top->size = 4;
	    top->var = NOT_A_VAR;
	    SIZE_AND_STACK(2, 1);

	case opc_sipush:
	    top->mr = MR_IMM;
	    top->offset = pc2signedshort(pc);
	    top->size = 4;
	    top->var = NOT_A_VAR;
	    SIZE_AND_STACK(3, 1);

	case opc_ldc:
	case opc_ldc_quick:
	    index = pc[1];
	    type = CONSTANT_POOL_TYPE_TABLE_GET_TYPE(type_table, index);
	    if (type == CONSTANT_Float)
	    {
		top->mr = MR_ABS;
		top->offset = (long)(&cpool[index].p);
	    }
	    else
	    {
		top->mr = MR_IMM;
		top->offset = (long)cpool[index].p;
	    }
	    top->size = 4;
	    top->var = NOT_A_VAR;
	    SIZE_AND_STACK(2, 1);

	case opc_ldc_w:
	case opc_ldc_w_quick:
	    index = GET_INDEX(pc + 1);
	    type = CONSTANT_POOL_TYPE_TABLE_GET_TYPE(type_table, index);
	    if (type == CONSTANT_Float)
	    {
		top->mr = MR_ABS;
		top->offset = (long)(&cpool[index].p);
	    }
	    else
	    {
		top->mr = MR_IMM;
		top->offset = (long)cpool[index].p;
	    }
	    top->size = 4;
	    top->var = NOT_A_VAR;
	    SIZE_AND_STACK(3, 1);

	case opc_ldc2_w:
	case opc_ldc2_w_quick:
	    index = GET_INDEX(pc + 1);
	    type = CONSTANT_POOL_TYPE_TABLE_GET_TYPE(type_table, index);
	    if (type == CONSTANT_Double)
	    {
		(top + 0)->mr = MR_ABS;
		(top + 0)->offset = (long)(&cpool[index].p);
		(top + 1)->mr = MR_ABS;
		(top + 1)->offset = (long)(&cpool[index + 1].p);
	    }
	    else
	    {
		(top + 0)->mr = MR_IMM;
		(top + 0)->offset = (long)cpool[index].p;
		(top + 1)->mr = MR_IMM;
		(top + 1)->offset = (long)cpool[index + 1].p;
	    }
	    (top + 0)->size = 4;
	    (top + 0)->var = NOT_A_VAR;
	    (top + 1)->size = 4;
	    (top + 1)->var = NOT_A_VAR;
	    SIZE_AND_STACK(3, 2);

#define CONSTANT_OP(opcode, value)					       \
        case opcode:                                                           \
            top->mr = MR_IMM;                                                  \
            top->offset = value;                                               \
            top->size = 4;                                                     \
            top->var = NOT_A_VAR;                                              \
            SIZE_AND_STACK(1, 1);

	CONSTANT_OP(opc_aconst_null,	0)
	CONSTANT_OP(opc_iconst_m1,     -1)
	CONSTANT_OP(opc_iconst_0,	0)
	CONSTANT_OP(opc_iconst_1,	1)
	CONSTANT_OP(opc_iconst_2,	2)
	CONSTANT_OP(opc_iconst_3,	3)
	CONSTANT_OP(opc_iconst_4,	4)
	CONSTANT_OP(opc_iconst_5,	5)

#define FCONSTANT_OP(opcode, name)					       \
        case opcode:                                                           \
            top->mr = MR_ABS;                                                  \
            top->offset = (long)(&name);                                       \
            top->size = 4;                                                     \
            top->var = NOT_A_VAR;                                              \
            SIZE_AND_STACK(1, 1);

	FCONSTANT_OP(opc_fconst_0, CompSupport_fconst_0)
	FCONSTANT_OP(opc_fconst_1, CompSupport_fconst_1)
	FCONSTANT_OP(opc_fconst_2, CompSupport_fconst_2)

#define DCONSTANT_OP(opcode, name)					       \
        case opcode:                                                           \
            (top + 0)->mr = MR_ABS;                                            \
            (top + 0)->offset = (long)(&name);                                 \
            (top + 0)->size = 4;                                               \
            (top + 0)->var = NOT_A_VAR;                                        \
            (top + 1)->mr = MR_ABS;                                            \
            (top + 1)->offset = (long)(&name) + 4;                             \
            (top + 1)->size = 4;                                               \
            (top + 1)->var = NOT_A_VAR;                                        \
            SIZE_AND_STACK(1, 2);

	DCONSTANT_OP(opc_dconst_0, CompSupport_dconst_0)
	DCONSTANT_OP(opc_dconst_1, CompSupport_dconst_1)

#define LCONSTANT_OP(opcode, value)					       \
        case opcode:                                                           \
            (top + 0)->mr = MR_IMM;                                            \
            (top + 0)->offset = value; /* little endian! */                    \
            (top + 0)->size = 4;                                               \
            (top + 0)->var = NOT_A_VAR;                                        \
            (top + 1)->mr = MR_IMM;                                            \
            (top + 1)->offset = 0;                                             \
            (top + 1)->size = 4;                                               \
            (top + 1)->var = NOT_A_VAR;                                        \
            SIZE_AND_STACK(1, 2);

	LCONSTANT_OP(opc_lconst_0, 0)
	LCONSTANT_OP(opc_lconst_1, 1)

	case opc_iadd:
	    SymBinOp(ce, top - 2, top - 1, ADDR, ADDI, GetHint(ce, pc + 1));
	    SIZE_AND_STACK(1, -1);

	case opc_iand:
	    SymBinOp(ce, top - 2, top - 1, ANDR, ANDI, GetHint(ce, pc + 1));
	    SIZE_AND_STACK(1, -1);

	case opc_ior:
	    SymBinOp(ce, top - 2, top - 1, ORR, ORI, GetHint(ce, pc + 1));
	    SIZE_AND_STACK(1, -1);

	case opc_ixor:
	    SymBinOp(ce, top - 2, top - 1, XORR, XORI, GetHint(ce, pc + 1));
	    SIZE_AND_STACK(1, -1);

	case opc_isub:
	    SubOp(ce, top - 2, top - 1, pc + 1);
	    SIZE_AND_STACK(1, -1);

	case opc_imul:
	    MulOp(ce, top - 2, top - 1, pc + 1);
	    SIZE_AND_STACK(1, -1);

	case opc_idiv:
	    DivRemOp(ce, top - 2, top - 1, pc + 1, 1);
	    SIZE_AND_STACK(1, -1);

	case opc_irem:
	    DivRemOp(ce, top - 2, top - 1, pc + 1, 0);
	    SIZE_AND_STACK(1, -1);

	case opc_ladd:
	    LAddOp(ce, top - 4, top - 2, pc + 1);
	    SIZE_AND_STACK(1, -2);

	case opc_lsub:
	    LSubOp(ce, top - 4, top - 2, pc + 1);
	    SIZE_AND_STACK(1, -2);

	case opc_lmul:
	    PushOpnd(ce, top - 1);
	    PushOpnd(ce, top - 2);
	    PushOpnd(ce, top - 3);
	    PushOpnd(ce, top - 4);
	    CallCompSupport(ce, (char *)CompSupport_lmul, top - 4, 16);
	    (top - 4)->mr = GetReg(ce, RS_EAX);
	    (top - 3)->mr = GetReg(ce, RS_EDX);
	    (top - 4)->var = NOT_A_VAR;
	    (top - 3)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, -2);

	case opc_ldiv:
	    PushOpnd(ce, top - 1);
	    PushOpnd(ce, top - 2);
	    PushOpnd(ce, top - 3);
	    PushOpnd(ce, top - 4);
	    CallCompSupport(ce, (char *)CompSupport_ldiv, top - 4, 16);
	    (top - 4)->mr = GetReg(ce, RS_EAX);
	    (top - 3)->mr = GetReg(ce, RS_EDX);
	    (top - 4)->var = NOT_A_VAR;
	    (top - 3)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, -2);

	case opc_lrem:
	    PushOpnd(ce, top - 1);
	    PushOpnd(ce, top - 2);
	    PushOpnd(ce, top - 3);
	    PushOpnd(ce, top - 4);
	    CallCompSupport(ce, (char *)CompSupport_lrem, top - 4, 16);
	    (top - 4)->mr = GetReg(ce, RS_EAX);
	    (top - 3)->mr = GetReg(ce, RS_EDX);
	    (top - 4)->var = NOT_A_VAR;
	    (top - 3)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, -2);

	case opc_land:
	    LSymBinOp(ce, top - 4, top - 2, ANDR, ANDI, pc + 1);
	    SIZE_AND_STACK(1, -2);

	case opc_lor:
	    LSymBinOp(ce, top - 4, top - 2, ORR, ORI, pc + 1);
	    SIZE_AND_STACK(1, -2);

	case opc_lxor:
	    LSymBinOp(ce, top - 4, top - 2, XORR, XORI, pc + 1);
	    SIZE_AND_STACK(1, -2);

	case opc_ishl:
	    ShiftOp(ce, top - 2, top - 1, 1, 1, pc + 1);
	    SIZE_AND_STACK(1, -1);

	case opc_ishr:
	    ShiftOp(ce, top - 2, top - 1, 0, 1, pc + 1);
	    SIZE_AND_STACK(1, -1);

	case opc_iushr:
	    ShiftOp(ce, top - 2, top - 1, 0, 0, pc + 1);
	    SIZE_AND_STACK(1, -1);

	case opc_lshl:
	    PushOpnd(ce, top - 1);
	    PushOpnd(ce, top - 2);
	    PushOpnd(ce, top - 3);
	    CallCompSupport(ce, (char *)CompSupport_lshl, top - 3, 12);
	    (top - 3)->mr = GetReg(ce, RS_EAX);
	    (top - 2)->mr = GetReg(ce, RS_EDX);
	    (top - 3)->var = NOT_A_VAR;
	    (top - 2)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, -1);

	case opc_lshr:
	    PushOpnd(ce, top - 1);
	    PushOpnd(ce, top - 2);
	    PushOpnd(ce, top - 3);
	    CallCompSupport(ce, (char *)CompSupport_lshr, top - 3, 12);
	    (top - 3)->mr = GetReg(ce, RS_EAX);
	    (top - 2)->mr = GetReg(ce, RS_EDX);
	    (top - 3)->var = NOT_A_VAR;
	    (top - 2)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, -1);

	case opc_lushr:
	    PushOpnd(ce, top - 1);
	    PushOpnd(ce, top - 2);
	    PushOpnd(ce, top - 3);
	    CallCompSupport(ce, (char *)CompSupport_lushr, top - 3, 12);
	    (top - 3)->mr = GetReg(ce, RS_EAX);
	    (top - 2)->mr = GetReg(ce, RS_EDX);
	    (top - 3)->var = NOT_A_VAR;
	    (top - 2)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, -1);

	case opc_ineg:
	    LoadOpnd(ce, top - 1, GetHint(ce, pc + 1));
	    GenOpSizEA(ce, 0xF6, NEG, top - 1);
	    SIZE_AND_STACK(1, 0);

	case opc_lneg:
	    LNegOp(ce, top - 2, pc + 1);
	    SIZE_AND_STACK(1, 0);

	case opc_iinc:
	    IncOp(ce, pc[1], ((signed char *) pc)[2]);
	    SIZE_AND_STACK(3, 0);

#define BINARY_FLOAT_OP(name, op, opr, notSym)				       \
        case opc_f##name:                                                      \
            RealOp(ce, top - 2, top - 1, op, opr, notSym, 4);                  \
            SIZE_AND_STACK(1, -1);                                             \
        case opc_d##name:                                                      \
            RealOp(ce, top - 4, top - 2, op, opr, notSym, 8);                  \
            SIZE_AND_STACK(1, -2);

	BINARY_FLOAT_OP(add, FADD, FADD,  0)
	BINARY_FLOAT_OP(sub, FSUB, FSUBR, 1)
	BINARY_FLOAT_OP(mul, FMUL, FMUL,  0)
	BINARY_FLOAT_OP(div, FDIV, FDIVR, 1)

	case opc_frem:
	    PushFOpnd(ce, top - 1, 4, 8);   // divisor
	    PushFOpnd(ce, top - 2, 4, 8);   // dividend
	    CallCompSupport(ce, (char *)CompSupport_drem, top - 2, 16);
	    (top - 2)->mr = MR_FST;
	    (top - 2)->size = 4;
	    (top - 2)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, -1);

	case opc_drem:
	    PushFOpnd(ce, top - 2, 8, 8);   // divisor
	    PushFOpnd(ce, top - 4, 8, 8);   // dividend
	    CallCompSupport(ce, (char *)CompSupport_drem, top - 4, 16);
	    (top - 4)->mr = MR_FST;
	    (top - 4)->size = 8;
	    (top - 4)->var = NOT_A_VAR;
	    (top - 3)->mr = MR_FST;
	    (top - 3)->size = 0;
	    (top - 3)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, -2);

	case opc_fneg:
	    LoadFOpnd(ce, top - 1, 4);
	    Gen2Bytes(ce, 0xD9, FCHS);
	    (top - 1)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, 0);

	case opc_dneg:
	    LoadFOpnd(ce, top - 2, 8);
	    Gen2Bytes(ce, 0xD9, FCHS);
	    (top - 2)->var = NOT_A_VAR;
	    (top - 1)->var = NOT_A_VAR;
	    SIZE_AND_STACK(1, 0);

	case opc_jsr:
	    PinTempMemAliases(ce, top);
	    Jcc(ce, CC_JSR,
		ce->rp[pc + pc2signedshort(pc) - initial_pc].hdr, top);
	    FlushVarCache(ce->ctxt);
	    sysAssert(ce->ctxt->regRefCnt[MR_EAX] == 0
		      && ce->ctxt->regRefCnt[MR_EDX] == 0
		      && ce->ctxt->regRefCnt[MR_ECX] == 0);
	    SIZE_AND_STACK(3, 0);   // 0: ret addr is not pushed

	case opc_jsr_w:
	    PinTempMemAliases(ce, top);
	    Jcc(ce, CC_JSR,
		ce->rp[pc + pc2signedlong(pc) - initial_pc].hdr, top);
	    FlushVarCache(ce->ctxt);
	    sysAssert(ce->ctxt->regRefCnt[MR_EAX] == 0
		      && ce->ctxt->regRefCnt[MR_EDX] == 0
		      && ce->ctxt->regRefCnt[MR_ECX] == 0);
	    SIZE_AND_STACK(5, 0);   // 0: ret addr is not pushed

	case opc_goto:
	    Jcc(ce, CC_JMP,
		ce->rp[pc + pc2signedshort(pc) - initial_pc].hdr, top);
	    SIZE_AND_STACK(3, 0);

	case opc_goto_w:
	    Jcc(ce, CC_JMP,
		ce->rp[pc + pc2signedlong(pc) - initial_pc].hdr, top);
	    SIZE_AND_STACK(5, 0);

	case opc_ret:
	    sysAssert(ce->ctxt->espLevel == 0);
    	    var = pc[1];
	    off = ce->varOff[var];
	    if (off < 0)
		Gen2Bytes(ce, 0xFF, JMPI + sibModRmTab[-off]);
	    else
	    {
		arg.size = 4;
		arg.mr = MR_BASE + MR_ESP;
		arg.offset = off;
		GenByte(ce, 0xFF);
		GenEA(ce, JMPI, &arg);
            }
	    SIZE_AND_STACK(2, 0);

#define COMPARISON_OP(name, cc) 					       \
        case opc_if_icmp##name:                                                \
            IfCmpcc(ce, top - 2, top - 1, cc, pc);                             \
            SIZE_AND_STACK(3, -2);                                             \
        case opc_if##name:                                                     \
            Ifcc(ce, top - 1, cc, pc);                                         \
            SIZE_AND_STACK(3, -1);

#define COMPARISON_OP2(name, nullname, cc)				       \
        COMPARISON_OP(name, cc)                                                \
        case opc_if_acmp##name:                                                \
            IfCmpcc(ce, top - 2, top - 1, cc, pc);                             \
            SIZE_AND_STACK(3, -2);                                             \
        case opc_if##nullname:                                                 \
            Ifcc(ce, top - 1, cc, pc);                                         \
            SIZE_AND_STACK(3, -1);

	COMPARISON_OP2(eq, null, CC_E)	// also generate acmp_eq and acmp_ne
	COMPARISON_OP2(ne, nonnull, CC_NE)
	COMPARISON_OP(lt, CC_L)
	COMPARISON_OP(gt, CC_G)
	COMPARISON_OP(le, CC_LE)
	COMPARISON_OP(ge, CC_GE)

	case opc_pop:
	    if ((top - 1)->mr == MR_FST && (top - 1)->size != 0)
		Gen2Bytes(ce, 0xDD, 0xD8);  // FSTP ST(0)
	    FreeRegs(ce, (top - 1)->mr);
	    SIZE_AND_STACK(1, -1);

	case opc_pop2:
	    if ((top - 1)->mr == MR_FST && (top - 1)->size != 0)
		Gen2Bytes(ce, 0xDD, 0xD8);  // FSTP ST(0)
	    if ((top - 2)->mr == MR_FST && (top - 2)->size != 0)
		Gen2Bytes(ce, 0xDD, 0xD8);  // FSTP ST(0)
	    FreeRegs(ce, (top - 2)->mr);
	    FreeRegs(ce, (top - 1)->mr);
	    SIZE_AND_STACK(1, -2);

	case opc_dup:
	    CopyOpnd(ce, top - 0, top - 1, 4, GetHint(ce, pc + 1));
	    SIZE_AND_STACK(1, 1);

	case opc_dup2:
	    FlushMultipleFST(ce, top - 2);
	    CopyOpnd(ce, top - 0, top - 2, 8, GetHint(ce, pc + 1));
	    SIZE_AND_STACK(1, 2);

	case opc_dup_x1:
	    FlushMultipleFST(ce, top - 2);
	    ce->ctxt->top = top + 1;	// push a dummy (MR_IMM) operand
	    (top + 0)->mr = MR_IMM;	// for checking (LoadOpnd) in MoveOpnd
	    MoveOpnd(ce, top - 0, top - 1, 4, GetHint(ce, pc + 1));
	    MoveOpnd(ce, top - 1, top - 2, 4, RS_CALLER_SAVED);
	    CopyOpnd(ce, top - 2, top - 0, 4, RS_CALLER_SAVED);
	    SIZE_AND_STACK(1, 1);

	case opc_dup_x2:
	    FlushMultipleFST(ce, top - 3);
	    ce->ctxt->top = top + 1;	// push a dummy (MR_IMM) operand
	    (top + 0)->mr = MR_IMM;	// for checking (LoadOpnd) in MoveOpnd
	    MoveOpnd(ce, top - 0, top - 1, 4, GetHint(ce, pc + 1));
	    MoveOpnd(ce, top - 2, top - 3, 8, RS_CALLER_SAVED);
	    CopyOpnd(ce, top - 3, top - 0, 4, RS_CALLER_SAVED);
	    SIZE_AND_STACK(1, 1);

	case opc_dup2_x1:
	    FlushMultipleFST(ce, top - 3);
	    ce->ctxt->top = top + 2;	// push 2 dummy (MR_IMM) operands
	    (top + 0)->mr = MR_IMM;	// for checking (LoadOpnd) in MoveOpnd
	    (top + 1)->mr = MR_IMM;
	    MoveOpnd(ce, top - 0, top - 2, 8, GetHint(ce, pc + 1));
	    MoveOpnd(ce, top - 1, top - 3, 4, RS_CALLER_SAVED);
	    CopyOpnd(ce, top - 3, top - 0, 8, RS_CALLER_SAVED);
	    SIZE_AND_STACK(1, 2);

	case opc_dup2_x2:
	    FlushMultipleFST(ce, top - 4);
	    ce->ctxt->top = top + 2;	// push 2 dummy (MR_IMM) operands
	    (top + 0)->mr = MR_IMM;	// for checking (LoadOpnd) in MoveOpnd
	    (top + 1)->mr = MR_IMM;
	    MoveOpnd(ce, top - 0, top - 2, 8, GetHint(ce, pc + 1));
	    MoveOpnd(ce, top - 2, top - 4, 8, RS_CALLER_SAVED);
	    CopyOpnd(ce, top - 4, top - 0, 8, RS_CALLER_SAVED);
	    SIZE_AND_STACK(1, 2);

	case opc_swap:
	    SwapOpnd(ce, top - 2, top - 1, GetHint(ce, pc + 1));
	    SIZE_AND_STACK(1, 0);

	case opc_arraylength:
	    LoadOpnd(ce, top - 1, RS_ALL);
	    (top - 1)->mr += MR_BASE;
	    (top - 1)->offset = offsetof(HArrayOfObject, methods);
	    arg.mr = MR_IMM;
	    arg.offset = METHOD_FLAG_BITS;
	    // ShiftOp will always load top - 1
	    ShiftOp(ce, top - 1, &arg, 0, 0, pc + 1);
	    SIZE_AND_STACK(1, 0);

	case opc_tableswitch:
	{
	    long *ltbl = (long *) ALIGN((int)pc + 1);
	    long def = swap(ltbl[0]);
	    long low = swap(ltbl[1]);
	    long high = swap(ltbl[2]);
	    long npairs = high - low + 1;
	    long i;
	    RangeHdr	*defRh;
	    Case	*cases, *line;

	    defRh = rh->targRh;
	    if (npairs > 0)
	    {
		FlushOpStack(ce, top - 1);
		// actually only needed if a target is really a join point,
		// but joinCnt of target is not accurate, since several labels
		// for the same target result in a joinCnt > 0

		LoadOpnd(ce, top - 1, RS_CALLER_SAVED);
		// set correct ctxt->top and ctxt->regRefCnt for targets:
		ce->ctxt->top = top - 1;
		FreeRegs(ce, (top - 1)->mr);
		sysAssert(ce->ctxt->regRefCnt[MR_EAX] == 0
			  && ce->ctxt->regRefCnt[MR_EDX] == 0
			  && ce->ctxt->regRefCnt[MR_ECX] == 0);

		// check code size, PC_RED_ZONE not good enough
		if  (ce->pc + 5*npairs >= ce->pcRedZone)
		{
		    ce->err = pcInRedZone;
		    return FALSE;
		}

		cases = (*p_malloc)(npairs*sizeof(Case));
		if  (cases == 0)
		{
		    ce->err = "Not enough memory";
		    return FALSE;
		}
		ce->cases = cases;  // to be deallocated in case of an exception
		clone = 1;
		for (i = 0, line = cases; i < npairs; i++, line++)
		{
		    NEXT_RANGE();
		    targRh = rh->targRh;
		    if (targRh->pc == 0 && targRh->ctxt == 0)
		    {
			// forward target, context not set yet
			if  (!CopyContext(ce, targRh, &clone))
			    return FALSE;
		    }
		    line->key = low + i;
		    line->targRh = targRh;
		}
		SortCases(cases, cases + npairs);
		top = ce->ctxt->top;
		Switch(ce, top->mr, cases, cases + npairs - 1, defRh);
		(*p_free)(cases);
		ce->cases = NULL;
	    }
	    else
		Jcc(ce, CC_JMP, defRh, --top);

	    pc = (unsigned char *)(ltbl + 3 + npairs);
	}
	continue;

	case opc_lookupswitch:
	{
	    long *ltbl = (long *) ALIGN((int)pc + 1);
	    long def = swap(ltbl[0]);
	    long npairs = swap(ltbl[1]);
	    long i;
	    RangeHdr	*defRh;
	    Case	*cases, *line;

	    defRh = rh->targRh;
	    if (npairs > 0)
	    {
		FlushOpStack(ce, top - 1);
		// actually only needed if a target is really a join point,
		// but joinCnt of target is not accurate, since several labels
		// for the same target result in a joinCnt > 0

		LoadOpnd(ce, top - 1, RS_CALLER_SAVED);

		// set correct ctxt->top and ctxt->regRefCnt for targets:
		ce->ctxt->top = top - 1;
		FreeRegs(ce, (top - 1)->mr);
		sysAssert(ce->ctxt->regRefCnt[MR_EAX] == 0
			  && ce->ctxt->regRefCnt[MR_EDX] == 0
			  && ce->ctxt->regRefCnt[MR_ECX] == 0);

		// check code size, PC_RED_ZONE not good enough
		if  (ce->pc + 11*npairs >= ce->pcRedZone)
		{
		    ce->err = pcInRedZone;
		    return FALSE;
		}

		cases = (*p_malloc)(npairs*sizeof(Case));
		if  (cases == 0)
		{
		    ce->err = "Not enough memory";
		    return FALSE;
		}
		ce->cases = cases;  // to be deallocated in case of an exception
		clone = 1;
		for (i = 0, line = cases; i < npairs; i++, line++)
		{
		    NEXT_RANGE();
		    targRh = rh->targRh;
		    if (targRh->pc == 0 && targRh->ctxt == 0)
		    {
			// forward target, context not set yet
			if  (!CopyContext(ce, targRh, &clone))
			    return FALSE;
		    }
		    line->key = swap(ltbl[i * 2 + 2]);
		    line->targRh = targRh;
		}
		SortCases(cases, cases + npairs);
		top = ce->ctxt->top;
		Switch(ce, top->mr, cases, cases + npairs - 1, defRh);
		(*p_free)(cases);
		ce->cases = NULL;
	    }
	    else
		Jcc(ce, CC_JMP, defRh, --top);

	    pc = (unsigned char *)(ltbl + 2 + npairs * 2);
	}
	continue;

	case opc_athrow:
	    PushOpnd(ce, top - 1);
	    CallCompSupport(ce, (char *)CompSupport_athrow, top - 1, 4);
	    SIZE_AND_STACK(1, -1);

	case opc_getstatic:
	    fb = cpool[GET_INDEX(pc+1)].p;
	    isig = fieldsig(fb)[0];
	    if (isig == SIGNATURE_LONG || isig == SIGNATURE_DOUBLE)
	    {
		GetStatic((long)twoword_static_address(fb), top, 8);
		push = 2;
	    }
	    else
	    {
		GetStatic((long)normal_static_address(fb), top, 4);
		push = 1;
	    }
	    SIZE_AND_STACK(3, push);

	case opc_putstatic:
	    fb = cpool[GET_INDEX(pc+1)].p;
	    isig = fieldsig(fb)[0];
	    if (isig == SIGNATURE_LONG || isig == SIGNATURE_DOUBLE)
	    {
		PutStatic(ce, (long)twoword_static_address(fb), top - 2, 8);
		push = -2;
	    }
	    else
	    {
		PutStatic(ce, (long)normal_static_address(fb), top - 1, 4);
		push = -1;
	    }
	    SIZE_AND_STACK(3, push);

	case opc_putfield_quick:
	    PutField(ce, top - 2, 4*pc[1], top - 1, 4);
	    SIZE_AND_STACK(3, -2);

	case opc_getfield_quick:
	    GetField(ce, top - 1, 4*pc[1], 4, pc + 3);
	    SIZE_AND_STACK(3, 0);

	case opc_putfield2_quick:
	    PutField(ce, top - 3, 4*pc[1], top - 2, 8);
	    SIZE_AND_STACK(3, -3);

	case opc_getfield2_quick:
	    GetField(ce, top - 1, 4*pc[1], 8, pc + 3);
	    SIZE_AND_STACK(3, 1);

	case opc_putstatic_quick:
	    fb = cpool[GET_INDEX(pc+1)].p;
	    PutStatic(ce, (long)normal_static_address(fb), top - 1, 4);
	    SIZE_AND_STACK(3, -1);

	case opc_getstatic_quick:
	    fb = cpool[GET_INDEX(pc+1)].p;
	    GetStatic((long)normal_static_address(fb), top, 4);
	    SIZE_AND_STACK(3, 1);

	case opc_putstatic2_quick:
	    fb = cpool[GET_INDEX(pc+1)].p;
	    PutStatic(ce, (long)twoword_static_address(fb), top - 2, 8);
	    SIZE_AND_STACK(3, -2);

	case opc_getstatic2_quick:
	    fb = cpool[GET_INDEX(pc+1)].p;
	    GetStatic((long)twoword_static_address(fb), top, 8);
	    SIZE_AND_STACK(3, 2);

	case opc_putfield:
	case opc_putfield_quick_w:
	    fb = cpool[GET_INDEX(pc+1)].p;
	    off = fb->u.offset;
	    isig = fieldsig(fb)[0];
	    if (isig == SIGNATURE_LONG || isig == SIGNATURE_DOUBLE)
	    {
		PutField(ce, top - 3, off, top - 2, 8);
		SIZE_AND_STACK(3, -3);
	    }
	    else
	    {
		PutField(ce, top - 2, off, top - 1, 4);
		SIZE_AND_STACK(3, -2);
	    }

	case opc_getfield:
	case opc_getfield_quick_w:
	    fb = cpool[GET_INDEX(pc+1)].p;
	    isig = fieldsig(fb)[0];
	    if (isig == SIGNATURE_LONG || isig == SIGNATURE_DOUBLE)
	    {
		GetField(ce, top - 1, fb->u.offset, 8, pc + 3);
		SIZE_AND_STACK(3, 1);
	    }
	    else
	    {
		GetField(ce, top - 1, fb->u.offset, 4, pc + 3);
		SIZE_AND_STACK(3, 0);
	    }

	case opc_invokevirtual:
	case opc_invokevirtual_quick_w:
	    mb_type = cpool[GET_INDEX(pc+1)].p;
	    push = InvokeMethod(ce, mb_type, top, 1, 0);
	    SIZE_AND_STACK(3, push);

	case opc_invokenonvirtual:
	case opc_invokenonvirtual_quick:
	    mb_type = cpool[GET_INDEX(pc+1)].p;
	    push = InvokeMethod(ce, mb_type, top, 0, 0);
	    SIZE_AND_STACK(3, push);

	case opc_invokesuper_quick:
	    mb_type = cbMethodTable(unhand(cbSuperclass(cb)))
		->methods[GET_INDEX(pc + 1)];
	    push = InvokeMethod(ce, mb_type, top, 0, 0);
	    SIZE_AND_STACK(3, push);

	case opc_invokestatic:
	case opc_invokestatic_quick:
	    mb_type = cpool[GET_INDEX(pc+1)].p;
	    push = InvokeMethod(ce, mb_type, top, 0, 1);
	    SIZE_AND_STACK(3, push);

	case opc_invokeinterface:
	    index = cpool[GET_INDEX(pc+1)].i & 0xFFFF;
	    push = InvokeInterface(ce, cpool[index].i, top);
	    SIZE_AND_STACK(5, push);

	case opc_invokeinterface_quick:
	    push = InvokeInterface(ce, cpool[GET_INDEX(pc + 1)].i, top);
	    SIZE_AND_STACK(5, push);

	case opc_invokevirtualobject_quick:
	case opc_invokevirtual_quick:
	    check(0);	// must be detected in a previous pass

	case opc_instanceof:
	case opc_instanceof_quick:
	    PushOpnd(ce, top - 1);  // handle
	    arg.mr = MR_IMM;
	    arg.offset = cpool[GET_INDEX(pc + 1)].i;
	    Push(ce, &arg);	    // cb
	    CallCompSupport(ce, (char *)CompSupport_instanceof, top - 1, 8);
	    (top - 1)->mr = GetReg(ce, RS_EAX);
	    (top - 1)->size = 4;
	    (top - 1)->var = NOT_A_VAR;
	    SIZE_AND_STACK(3, 0);

	case opc_checkcast:
	case opc_checkcast_quick:
	    PushOpnd(ce, top - 1);  // handle
	    arg.mr = MR_IMM;
	    arg.offset = cpool[GET_INDEX(pc + 1)].i;
	    Push(ce, &arg);	    // cb
	    CallCompSupport(ce, (char *)CompSupport_checkcast, top - 1, 8);
	    (top - 1)->mr = GetReg(ce, RS_EAX);
	    (top - 1)->size = 4;
	    (top - 1)->var = NOT_A_VAR;
	    SIZE_AND_STACK(3, 0);

	case opc_new:
	case opc_new_quick:
	    arg.mr = MR_IMM;
	    arg.offset = cpool[GET_INDEX(pc + 1)].i;
	    Push(ce, &arg);	// cb
	    CallCompSupport(ce, (char *)CompSupport_new, top, 4);
	    top->mr = GetReg(ce, RS_EAX);
	    top->size = 4;
	    top->var = NOT_A_VAR;
	    SIZE_AND_STACK(3, 1);

	case opc_anewarray:
	case opc_anewarray_quick:
	    PushOpnd(ce, top - 1);  // size
	    arg.mr = MR_IMM;
	    arg.offset = cpool[GET_INDEX(pc + 1)].i;
	    Push(ce, &arg);	    // cb
	    CallCompSupport(ce, (char *)CompSupport_anewarray, top - 1, 8);
	    (top - 1)->mr = GetReg(ce, RS_EAX);
	    (top - 1)->size = 4;
	    (top - 1)->var = NOT_A_VAR;
	    SIZE_AND_STACK(3, 0);

	case opc_multianewarray:
	case opc_multianewarray_quick:
	    dimensions = pc[3];
	    for (push = 1; push <= dimensions; push++)
		PushOpnd(ce, top - push);
	    arg.mr = MR_IMM;
	    arg.offset = dimensions;
	    Push(ce, &arg);	    // dimensions
	    arg.mr = MR_IMM;
	    arg.offset = cpool[GET_INDEX(pc + 1)].i;
	    Push(ce, &arg);	    // cb
	    CallCompSupport(ce, (char *)CompSupport_multianewarray,
		top - dimensions, 4*dimensions + 8);
            GenAddImmRL(ce, MR_ESP, 4*dimensions + 8);	// __cdecl
	    (top - dimensions)->mr = GetReg(ce, RS_EAX);
	    (top - dimensions)->size = 4;
	    (top - dimensions)->var = NOT_A_VAR;
	    SIZE_AND_STACK(4, 1 - dimensions);

	case opc_newarray:
	    PushOpnd(ce, top - 1);  // size
	    arg.mr = MR_IMM;
	    arg.offset = pc[1];
	    Push(ce, &arg);	    // type
	    CallCompSupport(ce, (char *)CompSupport_newarray, top - 1, 8);
	    (top - 1)->mr = GetReg(ce, RS_EAX);
	    (top - 1)->size = 4;
	    (top - 1)->var = NOT_A_VAR;
	    SIZE_AND_STACK(2, 0);

	case opc_iaload:
	case opc_faload:
	case opc_aaload:
	    ArrayLoad(ce, top - 2, top - 1, 2, 4, 0, pc + 1);
	    SIZE_AND_STACK(1, -1);

	case opc_baload:
	    ArrayLoad(ce, top - 2, top - 1, 0, 4, 1, pc + 1);
	    SIZE_AND_STACK(1, -1);

	case opc_caload:
	    ArrayLoad(ce, top - 2, top - 1, 1, 4, 0, pc + 1);
	    SIZE_AND_STACK(1, -1);

	case opc_saload:
	    ArrayLoad(ce, top - 2, top - 1, 1, 4, 1, pc + 1);
	    SIZE_AND_STACK(1, -1);

	case opc_laload:
	case opc_daload:
	    ArrayLoad(ce, top - 2, top - 1, 3, 8, 0, pc + 1);
	    SIZE_AND_STACK(1, 0);

	case opc_iastore:
	case opc_fastore:
	    ArrayStore(ce, top - 3, top - 2, top - 1, 2, 4);
	    SIZE_AND_STACK(1, -3);

	case opc_bastore:
	    ArrayStore(ce, top - 3, top - 2, top - 1, 0, 4);
	    SIZE_AND_STACK(1, -3);

	case opc_castore:
	case opc_sastore:
	    ArrayStore(ce, top - 3, top - 2, top - 1, 1, 4);
	    SIZE_AND_STACK(1, -3);

	case opc_lastore:
	case opc_dastore:
	    ArrayStore(ce, top - 4, top - 3, top - 2, 3, 8);
	    SIZE_AND_STACK(1, -4);

	case opc_aastore:
	    PushOpnd(ce, top - 1);
	    PushOpnd(ce, top - 2);
	    PushOpnd(ce, top - 3);
	    CallCompSupport(ce, (char *)CompSupport_aastore, top - 3, 12);
	    SIZE_AND_STACK(1, -3);

	case opc_monitorenter:
	    PushOpnd(ce, top - 1);
	    CallCompSupport(ce, (char *)CompSupport_monitorenter, top - 1, 4);
	    SIZE_AND_STACK(1, -1);

	case opc_monitorexit:
    	    PushImm(ce, 0);
	    PushOpnd(ce, top - 1);
	    CallCompSupport(ce, (char *)CompSupport_monitorexit, top - 1, 8);
	    SIZE_AND_STACK(1, -1);

	case opc_wide:
	    var = GET_INDEX(pc + 2);
	    switch(pc[1])
	    {
	    case opc_aload:
	    case opc_iload:
	    case opc_fload:
		DO_LOAD_DWORD(top, var);
		SIZE_AND_STACK(4, 1);

	    case opc_lload:
	    case opc_dload:
		DO_LOAD_QWORD(top, var);
		SIZE_AND_STACK(4, 2);

	    case opc_istore:
	    case opc_astore:
		StoreOpnd(ce, var, top - 1);
		SIZE_AND_STACK(4, -1);

	    case opc_fstore:
		StoreFOpnd(ce, var, top - 1, 4);
		SIZE_AND_STACK(4, -1);

	    case opc_lstore:
		StoreOpnd(ce, var + 1, top - 1); // high first for anti-aliasing
		StoreOpnd(ce, var + 0, top - 2);
		SIZE_AND_STACK(4, -2);

	    case opc_dstore:
		StoreFOpnd(ce, var, top - 2, 8);
		SIZE_AND_STACK(4, -2);

	    case opc_iinc:
		IncOp(ce, var, (((signed char *)pc)[4] << 8) + pc[5]);
		SIZE_AND_STACK(6, 0);

	    case opc_ret:
		sysAssert(ce->ctxt->espLevel == 0);
		off = ce->varOff[var];
		if (off < 0)
		    Gen2Bytes(ce, 0xFF, JMPI + sibModRmTab[-off]);
		else
		{
		    arg.size = 4;
		    arg.mr = MR_BASE + MR_ESP;
		    arg.offset = off;
		    GenByte(ce, 0xFF);
		    GenEA(ce, JMPI, &arg);
		}
		SIZE_AND_STACK(4, 0);

	    default:
		sysAssert(0);
		ce->err = "Undefined opcode";
		return FALSE;
	    }

	default:
	    sysAssert(0);
	    ce->err = "Undefined opcode";
	    return FALSE;
	}
    }
    check(0);
}


bool_t
jitCompile(struct methodblock *mb, char **code, CodeInfo **info, char **err, ExecEnv *ee)
{
    CompEnv ce;
    char *cc;

    do
    {
	__try
	{
	    if (    InitializeCompEnv(&ce, mb, ee)
		&&  MarkExceptionRanges(&ce)
		&&  MarkBranchTargAndVars(&ce)
		&&  AllocRangeHeaders(&ce)
		&&  ComputeOpstackHeight(&ce)
		&&  IndexExceptionHandlers(&ce)
		&&  EstimateVarFreq(&ce)
		&&  AllocVariables(&ce)
		&&  Prologue(&ce)
		&&  Body(&ce)
		&&  Epilogue(&ce)
	       )
	    {
		// Truncate code block.
		// We have at least PC_RED_ZONE bytes to give back.
		cc = ce.compiledCode;
#ifdef	DEBUG
		nc_count += ce.pc - cc;
		bc_count += mb->code_length;
#endif
		ce.codeInfo->start_pc = cc;
                ce.codeInfo->end_pc = ce.pc;
                ce.compiledCode = (*p_realloc)(cc, ce.pc - cc);
		if  (ce.compiledCode != cc)
		    ce.err = "Truncating realloc failed";
	    }
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
	    if (GetExceptionCode() == OPSTACK_HEIGHT_EXCEPTION)
            {
		ce.err = "Stack height cannot be determined statically";
		cprintf(stderr, "JIT compiler: %s,\n        Recompile %s with a newer compiler.\n",
		    ce.err, ce.mb->fb.clazz->source_name);
	    }
	    else if (GetExceptionCode() == BAD_BYTECODE_EXCEPTION)
	    {
		ce.err = "Bytecode does not conform to the specification";
		cprintf(stderr, "JIT compiler: %s,\n        Recompile %s with a newer compiler.\n",
		    ce.err, ce.mb->fb.clazz->source_name);
	    }
	    else
		ce.err = "Internal error";
	}
	if  (ce.err)
	{
	    if (ce.compiledCode)
	    {
		(*p_free)(ce.compiledCode);
		ce.compiledCode = NULL;
	    }
	    if (ce.codeInfo)
	    {
	        (*p_free)(ce.codeInfo);
		ce.codeInfo = NULL;
	    }
	    if (ce.err == pcInRedZone)
		codeSizeFactor++;
	}
	FinalizeCompEnv(&ce);
    }
    while (ce.err == pcInRedZone);
    *err = ce.err;
    *code = ce.compiledCode;
    *info = ce.codeInfo;
    return (ce.err == 0);
}


void InitCompiler(void)
{
    InitCodeGen();
}