Files
sousa-gecko/ef/Compiler/CodeGenerator/md/x86/x86ArgumentList.cpp
T
fur@netscape.com 5501ba1bd0 A bunch of grunge work to reduce OS-dependent code in ElectricalFire
sources.

Here's a detailed list of the changes:

Renamed files and classes that were inappropriately named, i.e. the
names started with 'x86Win32', but the code was for generic x86, not
specific to Win32.

Segregated the 64-bit arithmetic code into an OS-independent
x86Arith64.{cpp,h}
1999-02-26 22:45:11 +00:00

1145 lines
37 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
//
// x86ArgumentList.cpp
//
// Peter DeSantis
// Simon Holmes a Court
#include "x86ArgumentList.h"
#include "x86Opcode.h"
#include "x86Instruction.h"
#include "MemoryAccess.h"
#include "x86Formatter.h"
UT_DEFINE_LOG_MODULE(x86ArgList);
// Maps register alloc color to x86 machine register
// Order matters.
x86GPR colorTox86GPR[] =
{
EAX,
EBX,
ECX,
EDX,
ESI,
EDI,
/*ESP,
EBP*/
};
// Maps x86 machine register to register alloc color
// Order matters.
Uint8 x86GPRToColor[] =
{
0, // EAX
2, // ECX
3, // EDX
1, // EBX
6, // ESP
7, // EBP
4, // ESI
5, // EDI
};
// Text labels listed by processor register numbers
// Order matters.
char* x86GPRText[] =
{
"eax",
"ecx",
"edx",
"ebx",
"esp",
"ebp",
"esi",
"edi"
};
/*--------------------------------------------------------------------------------
x86ArgumentType
struct x86ArgumentTypeInfo
{
Uint8 atiSize; // number of bytes needed to format
Uint8 atiNumUses; // number of uses consumed
SIBScale atiScale; // scale factor
TypeClass atTypeClass; // can the type be passed to the constructor?
char* atiPrintInfo; // debug formatting string // FIX shouldn't be in release build
}
atRegisterIndirect, atBaseIndexed, atScaledIndexedBy2, atScaledIndexedBy4, atScaledIndexedBy8, atStackOffset can
only be specified without displacements. The constructor promotes these types based on the size of the
displacement. The constructors depends on the ordering of these.
*/
enum SIBScale
{
kScaleBy1 = 0x00,
kScaleBy2 = 0x40,
kScaleBy4 = 0x80,
kScaleBy8 = 0xc0,
kScaleIllegal = 0xff // not a legal SIB value -- used only for debugging
};
enum TypeClass
{
kCannotHaveDisp,// can be passed in as a parameter
kMustHaveDisp, // as per kPrimaryArg, but _must_ have an extra arg (displacement or immediate)
kMayHaveDisp, // as per kPrimaryArg, but _may_ have an extra arg (displacement or immediate)
kDerived // cannot be passed in with a parameter
};
// Each x86ArgumentType has a x86ArgumentTypeInfo defined for it in atInfo.
struct x86ArgumentTypeInfo
{
Uint8 atiSize; // number of bytes needed to format
Uint8 atiNumUses; // number of uses consumed
SIBScale atiScale; // scale factor
TypeClass atTypeClass; // see TypeClass
char* atiPrintInfo; // debug formatting string // FIX shouldn't be in release build
};
x86ArgumentTypeInfo atInfo[] =
{
{1, 1, kScaleIllegal, kCannotHaveDisp,"%s"}, // atRegDirect
{5, 1, kScaleIllegal, kCannotHaveDisp,"%d[%s]"}, // atRegAllocStackSlot
{5, 1, kScaleIllegal, kCannotHaveDisp,"%d[%s]"}, // atRegAllocStackSlotHi32
{1, 1, kScaleIllegal, kMayHaveDisp, "[%s]"}, // atRegisterIndirect
{2, 1, kScaleIllegal, kDerived, "%d[%s]"}, // atRegisterRelative1ByteDisplacement
{5, 1, kScaleIllegal, kDerived, "%d[%s]"}, // atRegisterRelative4ByteDisplacement
{2, 2, kScaleBy1, kMayHaveDisp, "[%s+%s]"}, // atBaseIndexed
{3, 2, kScaleBy1, kDerived, "%d[%s+%s]"}, // atBaseRelativeIndexed1ByteDisplacement
{6, 2, kScaleBy1, kDerived, "%d[%s+%s]"}, // atBaseRelativeIndexed4ByteDisplacement
{2, 2, kScaleBy2, kMayHaveDisp, "[%s+2*%s]"}, // atScaledIndexedBy2
{3, 2, kScaleBy2, kDerived, "%d[%s+2*%s]"}, // atScaledIndexed1ByteDisplacementBy2
{6, 2, kScaleBy2, kDerived, "%d[%s+2*%s]"}, // atBaseRelativeIndexed4ByteDisplacementBy2
{2, 2, kScaleBy4, kMayHaveDisp, "[%s+4*%s]"}, // atScaledIndexedBy4
{3, 2, kScaleBy4, kDerived, "%d[%s+4*%s]"}, // atScaledIndexed1ByteDisplacementBy4
{6, 2, kScaleBy4, kDerived, "%d[%s+4*%s]"}, // atScaledIndexed4ByteDisplacementBy4
{2, 2, kScaleBy8, kMayHaveDisp, "[%s+8*%s]"}, // atScaledIndexedBy8
{3, 2, kScaleBy8, kDerived, "%d[%s+8*%s]"}, // atScaledIndexed1ByteDisplacementBy8
{6, 2, kScaleBy8, kDerived, "%d[%s+8*%s]"}, // atScaledIndexed4ByteDisplacementBy8
{0, 0, kScaleIllegal, kMayHaveDisp, "NOT USED"}, // atStackOffset
{2, 0, kScaleIllegal, kDerived, "%d[EBP]"}, // atStackOffset1ByteDisplacement
{5, 0, kScaleIllegal, kDerived, "%d[EBP]"}, // atStackOffset4ByteDisplacement
{6, 0, kScaleIllegal, kMustHaveDisp, "[0x%08x]"}, // atAbsoluteAddress
{0, 0, kScaleIllegal, kCannotHaveDisp,"%d"} // atImmediateOnly
};
//--------------------------------------------------------------------------------
// SingleArgumentList
// Method: alGetRegisterOperand
// Purpose: extract the register or registers that are the components of the current operand
// Used by: X86Opcode_Reg_Condensable when it is trying to append register operands into the opcode
// Input: instruction
// Output: returns the register number (if SingleArgumentList and ImmediateArgumentList)
// or NOT_A_REG (otherwise)
Uint8 x86SingleArgumentList::
alGetRegisterOperand(x86ArgListInstruction& inInsn)
{
if(argType1 == atRegDirect)
{
InstructionUse* useBegin = &(inInsn.getInstructionUseBegin()[0]);
InstructionUse* useEnd = inInsn.getInstructionUseEnd();
if((useBegin < useEnd) && useBegin->isVirtualRegister())
return useToRegisterNumber(*useBegin);
InstructionDefine* defineBegin = inInsn.getInstructionDefineBegin();
InstructionDefine* defineEnd = inInsn.getInstructionDefineEnd();
assert((defineBegin < defineEnd) && (defineBegin)->isVirtualRegister()) ;
return defineToRegisterNumber(*defineBegin);
}
// Must be memory
return NOT_A_REG;
}
// Method: extractOperand
// Purpose: extract the register or registers that are the components of the current operand
// Input: instruction
// location of the register encodings
// Output: one or two registers (expressed as x86 register numbers)
// reg = either InstructionUses[inInstructionUseStartIndex] or the first InstructionDefine
// reg2 = is filled iff the use or define after reg is a valid register
inline void extractOperand(x86ArgListInstruction& inInsn, Uint8 inInstructionUseStartIndex, Uint8& reg, Uint8& reg2)
{
InstructionUse* useBegin = &(inInsn.getInstructionUseBegin()[inInstructionUseStartIndex]);
InstructionUse* useEnd = inInsn.getInstructionUseEnd();
if((useBegin < useEnd) && useBegin->isVirtualRegister())
{
reg = useToRegisterNumber(*useBegin);
if((useBegin + 1 < useEnd) && (useBegin +1)->isVirtualRegister())
reg2 = useToRegisterNumber(*(useBegin+1));
else
reg2 = NOT_A_REG;
}
else
{ // This means that the virtual registers must be the first of the defines
InstructionDefine* defineBegin = inInsn.getInstructionDefineBegin();
InstructionDefine* defineEnd = inInsn.getInstructionDefineEnd();
assert((defineBegin < defineEnd) && (defineBegin)->isVirtualRegister()) ;
reg = defineToRegisterNumber(*defineBegin);
if ((defineBegin + 1 < defineEnd) && (defineBegin + 1)->isVirtualRegister())
reg2 = defineToRegisterNumber(*(defineBegin+1));
else
reg2 = NOT_A_REG;
}
}
#ifdef DEBUG_LOG
// Method: alPrintPretty
// Purpose: basic print pretty utility function for all argument lists which have one or more reg/mem operands
// Input: LogModuleObject
// x86ArgListInstruction to be printed
// type of argument
// location of the register encodings
// Output: prints to LogModuleObject
void x86SingleArgumentList::
alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn, x86ArgumentType inType, Uint8 inInstructionUseStartIndex )
{
switch (inType)
{
case atImmediateOnly:
return;
case atAbsoluteAddress:
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (atInfo[inType].atiPrintInfo, arg4ByteDisplacement));
return;
case atRegAllocStackSlot:
{
InstructionUse* useBegin = &(inInsn.getInstructionUseBegin()[inInstructionUseStartIndex]);
InstructionUse* useEnd = inInsn.getInstructionUseEnd();
if((useBegin < useEnd) && useBegin->isVirtualRegister()) // Stack slot is the use
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("slot%d", useBegin->getVirtualRegister().getColor()));
else // Stack slot is the define
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("slot%d", inInsn.getInstructionDefineBegin()->getVirtualRegister().getColor() ));
}
return;
case atRegAllocStackSlotHi32:
{
InstructionUse* useBegin = &(inInsn.getInstructionUseBegin()[inInstructionUseStartIndex]);
InstructionUse* useEnd = inInsn.getInstructionUseEnd();
if((useBegin < useEnd) && useBegin->isVirtualRegister()) // Stack slot is the use
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("Hi32(slot%d)", useBegin->getVirtualRegister().getColor()));
else // Stack slot is the define
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("Hi32(slot%d)", inInsn.getInstructionDefineBegin()->getVirtualRegister().getColor() ));
}
return;
case atStackOffset1ByteDisplacement:
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (atInfo[inType].atiPrintInfo, arg1ByteDisplacement));
return;
case atStackOffset4ByteDisplacement:
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (atInfo[inType].atiPrintInfo, arg4ByteDisplacement));
return;
default:
0; // do nothing -- fall through (is there a better way to make gcc happy?)
}
// registers are needed for remaining types
Uint8 reg, reg2;
extractOperand(inInsn, inInstructionUseStartIndex, reg, reg2);
switch (inType)
{
case (atRegDirect):
case (atRegisterIndirect):
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (atInfo[inType].atiPrintInfo, x86GPRText[reg] ));
break;
case (atRegisterRelative1ByteDisplacement):
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (atInfo[inType].atiPrintInfo, arg1ByteDisplacement, x86GPRText[reg] ));
break;
case (atRegisterRelative4ByteDisplacement):
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (atInfo[inType].atiPrintInfo, arg4ByteDisplacement, x86GPRText[reg] ));
break;
case(atBaseIndexed):
case(atScaledIndexedBy2):
case(atScaledIndexedBy4):
case(atScaledIndexedBy8):
assert(reg2 != NOT_A_REG);
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (atInfo[inType].atiPrintInfo, x86GPRText[reg], x86GPRText[reg2] ));
break;
case(atBaseRelativeIndexed1ByteDisplacement):
case(atScaledIndexed1ByteDisplacementBy2):
case(atScaledIndexed1ByteDisplacementBy4):
case(atScaledIndexed1ByteDisplacementBy8):
assert(reg2 != NOT_A_REG);
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (atInfo[inType].atiPrintInfo, arg1ByteDisplacement, x86GPRText[reg], x86GPRText[reg2] ));
break;
default:
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (atInfo[inType].atiPrintInfo, arg4ByteDisplacement, x86GPRText[reg], x86GPRText[reg2] ));
break;
}
}
#endif // DEBUG_LOG
// Method: alSize
// Purpose: calculate the size of the argument list
// Input: type of argument
// instruction
// location of the register encodings
// Output: number of bytes of space needed to encode argument
Uint8 x86SingleArgumentList::
alSize(x86ArgumentType inType, x86ArgListInstruction& inInsn, Uint8 inInstructionUseStartIndex, MdFormatter& inFormatter)
{
switch (inType)
{
case atRegisterIndirect:
{
Uint8 reg, reg2;
extractOperand(inInsn, inInstructionUseStartIndex, reg, reg2);
if ((reg == EBP))
return 2;
return atInfo[inType].atiSize;
}
break;
case atRegAllocStackSlot:
case atRegAllocStackSlotHi32:
{
InstructionUseOrDefine* uord = NULL;
InstructionUse* useBegin = &inInsn.getInstructionUseBegin()[inInstructionUseStartIndex];
InstructionUse* useEnd = inInsn.getInstructionUseEnd();
if ((useBegin < useEnd) && useBegin->isVirtualRegister())
uord = useBegin;
else
uord = inInsn.getInstructionDefineBegin();
Uint32 unsizedDisplacement = inFormatter.getStackSlotOffset(uord->getVirtualRegister());
if(valueIsOneByteSigned(unsizedDisplacement))
return 2;
else
return 5;
}
break;
/*
If we want to use ESP
case (atRegisterIndirect):
if ((reg == EBP))
return 2;
case (atRegisterRelative1ByteDisplacement):
if (getRegUse() == ESP)
return 3;
case (atRegisterRelative4ByteDisplacement):
if (getRegUse() == ESP)
return 6;
*/
default:
return atInfo[inType].atiSize;
}
}
void x86SingleArgumentList::
alFormatStackSlotToMemory(void* inStart, x86ArgListInstruction& inInsn, Uint8 inInstructionUseStartIndex,
MdFormatter& inFormatter, int offset)
{
Uint8 modRM = 0;
Uint8* start = (Uint8*) inStart;
InstructionUse* useBegin = &inInsn.getInstructionUseBegin()[inInstructionUseStartIndex];
InstructionUse* useEnd = inInsn.getInstructionUseEnd();
// find the stack slot
VirtualRegister* vrStackSlot = NULL;
if ((useBegin < useEnd) && useBegin->isVirtualRegister()) // if there are uses
{
vrStackSlot = &(useBegin->getVirtualRegister());
}
// at this point vrStackSlot points to
// a. a stack slot (the correct thing to do)
// b. an non-stack slot register (bad)
// c. NULL
// In the last two conditions we need to get the stack slot from the define
if(vrStackSlot == NULL || vrStackSlot->getClass() != StackSlotRegister)
{
InstructionDefine* defineBegin = inInsn.getInstructionDefineBegin();
InstructionDefine* defineEnd = inInsn.getInstructionDefineEnd();
assert(defineBegin < defineEnd); // we've got to have one!
vrStackSlot = &(defineBegin->getVirtualRegister());
assert(vrStackSlot->getClass() == StackSlotRegister);
}
Int32 unsizedDisplacement = inFormatter.getStackSlotOffset(*vrStackSlot) + offset;
assert(vrStackSlot->getClass() == StackSlotRegister);
if(valueIsOneByteSigned(unsizedDisplacement))
{
modRM = 0x40 | EBP;
*start++ = modRM;
*(Int8*)start = (Uint8)unsizedDisplacement;
}
else
{
modRM = 0x80 | EBP;
*start++ = modRM;
*(Int32*)start = unsizedDisplacement;
}
}
void x86SingleArgumentList::
alFormatNonRegToMemory(void* inStart, x86ArgListInstruction& /*inInsn*/, x86ArgumentType inType, MdFormatter& /*inFormatter*/)
{
Uint8* start = (Uint8*) inStart;
Uint8 modRM, SIB;
switch(inType)
{
case atAbsoluteAddress:
modRM = 0x04;
SIB = 0x25;
*start++ = modRM;
*start++ = SIB;
// Absolute memory address must use a 4 byte "displacement".
writeLittleWordUnaligned((void*)start, arg4ByteDisplacement);
break;
case atStackOffset1ByteDisplacement:
modRM = 0x45;
*start++ = modRM;
*start = arg1ByteDisplacement;
break;
case atStackOffset4ByteDisplacement:
modRM = 0x85;
*start++ = modRM;
writeLittleWordUnaligned((void*)start, arg4ByteDisplacement);
break;
default:
trespass("illegal inType");
}
}
void x86SingleArgumentList::
alFormatRegToMemory(void* inStart, x86ArgListInstruction& inInsn, x86ArgumentType inType, Uint8 inInstructionUseStartIndex, MdFormatter& /*inFormatter*/)
{
Uint8 modRM;
Uint8 SIB;
Uint8 reg, reg2;
Uint8* start = (Uint8*) inStart;
SIBScale scale = atInfo[argType1].atiScale;
extractOperand(inInsn, inInstructionUseStartIndex, reg, reg2);
assert( reg != ESP && reg2 != ESP );
switch (inType)
{
case atRegDirect:
modRM = 0xc0 | reg;
*start = modRM;
break;
case atRegisterIndirect:
if (reg == EBP)
{
modRM = 0x45;
*start++ = modRM;
*start = 0;
break;
}
/* if (reg == ESP)
{
modRM = 0x04;
SIB = 0x24;
*start = modRM;
start++;
*start = SIB;
return;
}
*/
modRM = 0x00 | reg;
*start = modRM;
break;
case atBaseIndexed:
case atScaledIndexedBy2:
case atScaledIndexedBy4:
case atScaledIndexedBy8:
assert(reg2 != NOT_A_REG);
assert(scale != kScaleIllegal);
modRM = 0x04;
*start++ = modRM;
assert (reg != EBP); // EBP is an invalid index
//assert (reg2 != ESP); // Index
SIB = scale | (reg2 << 3) | reg;
*start = SIB;
break;
case atBaseRelativeIndexed1ByteDisplacement:
case atScaledIndexed1ByteDisplacementBy2:
case atScaledIndexed1ByteDisplacementBy4:
case atScaledIndexed1ByteDisplacementBy8:
assert(reg2 != NOT_A_REG);
assert(scale != kScaleIllegal);
modRM = 0x44;
*start++ = modRM;
SIB = scale | ( reg2 << 3) | reg;
*start++ = SIB;
*start = arg1ByteDisplacement;
break;
case atBaseRelativeIndexed4ByteDisplacement:
case atScaledIndexed4ByteDisplacementBy2:
case atScaledIndexed4ByteDisplacementBy4:
case atScaledIndexed4ByteDisplacementBy8:
assert(reg2 != NOT_A_REG); // must have two regs
assert(scale != kScaleIllegal);
modRM = 0x84;
assert (reg2 != ESP); // ESP is an invalid index
SIB = scale | ( reg2 << 3) | reg;
*start++ = modRM;
*start++ = SIB;
writeLittleWordUnaligned((void*)start, arg4ByteDisplacement);
break;
case atRegisterRelative1ByteDisplacement:
if (reg == ESP)
{
modRM = 0x44;
SIB = 0x24;
*start++ = modRM;
*start++ = SIB;
*start = arg1ByteDisplacement;
}
else
{
modRM = 0x40 | reg;
*start++ = modRM;
*start = arg1ByteDisplacement;
}
break;
case atRegisterRelative4ByteDisplacement:
if (reg == ESP)
{
modRM = 0x84;
SIB = 0x24;
*start++ = modRM;
*start = SIB;
} else {
modRM = 0x80 | reg;
*start++ = modRM;
writeLittleWordUnaligned((void*)start, arg4ByteDisplacement);
}
break;
default:
trespass("illegal inType");
}
}
// Format an instruction to IA32 machine code
void x86SingleArgumentList::
alFormatToMemory(void* inStart, x86ArgListInstruction& inInsn, x86ArgumentType inType, Uint8 inInstructionUseStartIndex, MdFormatter& inFormatter)
{
// FIX replace by table lookup if more efficient
switch(inType)
{
case atImmediateOnly:
return;
case atAbsoluteAddress:
case atStackOffset1ByteDisplacement:
case atStackOffset4ByteDisplacement:
alFormatNonRegToMemory(inStart, inInsn, inType, inFormatter);
return;
case atRegAllocStackSlot:
alFormatStackSlotToMemory(inStart, inInsn, inInstructionUseStartIndex, inFormatter, 0);
return;
case atRegAllocStackSlotHi32:
alFormatStackSlotToMemory(inStart, inInsn, inInstructionUseStartIndex, inFormatter, 4);
return;
case atRegDirect:
case atRegisterIndirect:
case atBaseIndexed:
case atScaledIndexedBy2:
case atScaledIndexedBy4:
case atScaledIndexedBy8:
case atBaseRelativeIndexed1ByteDisplacement:
case atScaledIndexed1ByteDisplacementBy2:
case atScaledIndexed1ByteDisplacementBy4:
case atScaledIndexed1ByteDisplacementBy8:
case atBaseRelativeIndexed4ByteDisplacement:
case atScaledIndexed4ByteDisplacementBy2:
case atScaledIndexed4ByteDisplacementBy4:
case atScaledIndexed4ByteDisplacementBy8:
case atRegisterRelative1ByteDisplacement:
case atRegisterRelative4ByteDisplacement:
alFormatRegToMemory(inStart, inInsn, inType, inInstructionUseStartIndex, inFormatter);
return;
default:
trespass("illegal inType");
}
}
//--------------------------------------------------------------------------------
// x86SingleArgumentList
x86SingleArgumentList::
x86SingleArgumentList(x86ArgumentType inType) :
argType1(inType)
{
// check that inType is valid.
assert(atInfo[inType].atTypeClass == kMayHaveDisp || atInfo[inType].atTypeClass == kCannotHaveDisp);
}
x86SingleArgumentList::
x86SingleArgumentList(x86ArgumentType inType, Uint32 inDisplacement)
{
assert(atInfo[inType].atTypeClass == kMayHaveDisp || atInfo[inType].atTypeClass == kMustHaveDisp);
if(inType == atAbsoluteAddress)
{
// Must take a 4 byte address
arg4ByteDisplacement = inDisplacement;
argType1 = inType;
return;
}
assert(atInfo[inType].atTypeClass == kMayHaveDisp);
if(valueIsOneByteSigned(inDisplacement))
{
arg1ByteDisplacement = (Uint8)inDisplacement;
argType1 = (x86ArgumentType)(inType + 1);
} else {
arg4ByteDisplacement = inDisplacement;
argType1 = (x86ArgumentType)(inType + 2);
}
}
bool x86SingleArgumentList::
alSwitchArgumentTypeToSpill(Uint8 /*inWhichUse*/, x86ArgListInstruction& /* inInsn */ )
{
// assert(argType1 != atRegAllocStackSlot); // wouldn't we be lying in our result if this were true?
bool couldSpill;
if(argType1 == atRegDirect)
{
argType1 = atRegAllocStackSlot;
couldSpill = true;
}
else
couldSpill = false;
return couldSpill;
}
void x86SingleArgumentList::
alFormatToMemory(void* inStart, Uint32 /*inOffset*/, x86ArgListInstruction& inInsn, MdFormatter& inFormatter)
{
// If the argument is a register direct and the opcode can be appended with the reg
// then the argument list is empty, so do nothing.
if( (argType1 != atRegDirect) || (!inInsn.regOperandCanBeCondensed()) )
alFormatToMemory(inStart, inInsn, argType1, 0, inFormatter);
}
Uint8 x86SingleArgumentList::
alSize(x86ArgListInstruction& inInsn, MdFormatter& inFormatter)
{
// If the argument is a register direct and the opcode can be appended with the reg
// then the argument list is empty, so do nothing.
if( (argType1 != atRegDirect) || (!inInsn.regOperandCanBeCondensed()) )
return alSize(argType1, inInsn, 0, inFormatter);
else
return 0;
}
//--------------------------------------------------------------------------------
// x86DoubleArgumentList
x86DoubleArgumentList::
x86DoubleArgumentList(x86ArgumentType inType1, x86ArgumentType inType2) :
x86SingleArgumentList(inType1),
argType2(inType2)
{
// check that at most one of the operands is 'memory'
// ie check that at least on of the operands is register
assert(inType1 == atRegDirect || inType2 == atRegDirect);
// check that inType is valid.
assert(atInfo[inType1].atTypeClass == kMayHaveDisp || atInfo[inType1].atTypeClass == kCannotHaveDisp);
assert(atInfo[inType2].atTypeClass == kMayHaveDisp || atInfo[inType2].atTypeClass == kCannotHaveDisp);
}
x86DoubleArgumentList::
x86DoubleArgumentList(x86ArgumentType inType1, x86ArgumentType inType2, Uint32 inDisplacement)
{
// Make sure only one of the operands is memory type. And set the type based on the displacement.
switch(inType1)
{
case (atRegDirect):
argType1 = inType1;
switch(inType2)
{
/* case (atMemoryDirect):
arg4ByteDisplacement = inDisplacement;
argType2 = inType2;
break;
*/
case (atRegisterIndirect):
case (atBaseIndexed):
case (atScaledIndexedBy2):
case (atScaledIndexedBy4):
case (atScaledIndexedBy8):
if(valueIsOneByteSigned(inDisplacement))
{
arg1ByteDisplacement = (Uint8)inDisplacement;
argType2 = x86ArgumentType(inType2 + 1);
} else {
arg4ByteDisplacement = inDisplacement;
argType2 = x86ArgumentType(inType2 + 2);
}
break;
default:
assert(false);
}
break;
/* case (atMemoryDirect):
arg4ByteDisplacement = inDisplacement;
argType1 = inType1;
assert(inType2 == atRegDirect);
argType2 = atRegDirect;
break;
*/
// case kMayHaveDisp:
case (atRegisterIndirect):
case (atBaseIndexed):
case (atScaledIndexedBy2):
case (atScaledIndexedBy4):
case (atScaledIndexedBy8):
case (atStackOffset):
if(valueIsOneByteSigned(inDisplacement))
{
arg1ByteDisplacement = (Uint8)inDisplacement;
argType1 = x86ArgumentType(inType1 + 1);
} else {
arg4ByteDisplacement = inDisplacement;
argType1 = x86ArgumentType(inType1 + 2);
}
assert(inType2 == atRegDirect);
argType2 = atRegDirect;
break;
default:
assert(false);
}
}
#ifdef DEBUG
void x86DoubleArgumentList::
alCheckIntegrity(x86ArgListInstruction& inInsn)
{
VRClass reg1;
VRClass reg2;
if(argType1 == atRegDirect || argType1 == atRegAllocStackSlot || argType1 == atRegAllocStackSlotHi32)
reg1 = alGetArg1VirtualRegister(inInsn)->getClass();
if(argType2 == atRegDirect || argType2 == atRegAllocStackSlot || argType2 == atRegAllocStackSlotHi32)
reg2 = alGetArg2VirtualRegister(inInsn)->getClass();
// ie if use1 is an IntegerRegister, arg1 must be atRegDirect
// etc.
if(argType1 == atRegDirect)
if(reg1 != IntegerRegister)
goto failedTest;
if(argType1 == atRegAllocStackSlot || argType1 == atRegAllocStackSlotHi32)
if(reg1 != StackSlotRegister)
goto failedTest;
if(argType2 == atRegDirect)
if(reg2 != IntegerRegister)
goto failedTest;
if(argType2 == atRegAllocStackSlot || argType2 == atRegAllocStackSlotHi32)
if(reg2 != StackSlotRegister)
goto failedTest;
return;
failedTest:
fprintf(stderr, "\n\n** Failed Test\n");
alPrintArgs(inInsn);
trespass("argument list failed integrity check");
}
void x86DoubleArgumentList::
alPrintArgs(x86ArgListInstruction& inInsn)
{
UT_LOG(x86ArgList, PR_LOG_DEBUG, ("\n Instruction %p\n", &inInsn));
UT_LOG(x86ArgList, PR_LOG_DEBUG, (" argType1: "));
switch(argType1)
{
case atRegDirect: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("atRegDirect ")); break;
case atRegAllocStackSlot: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("atRegAllocStackSlot ")); break;
case atRegAllocStackSlotHi32: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("atRegAllocStackSlotHi32 ")); break;
default: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("other ")); break;
}
UT_LOG(x86ArgList, PR_LOG_DEBUG, ("use1(vr %p): ", alGetArg1VirtualRegister(inInsn)));
switch(alGetArg1VirtualRegister(inInsn)->getClass())
{
case IntegerRegister: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("IntegerRegister\n")); break;
case StackSlotRegister: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("StackSlotRegister\n")); break;
default: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("other\n")); break;
}
UT_LOG(x86ArgList, PR_LOG_DEBUG, (" argType2: "));
switch(argType2)
{
case atRegDirect: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("atRegDirect ")); break;
case atRegAllocStackSlot: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("atRegAllocStackSlot ")); break;
case atRegAllocStackSlotHi32: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("atRegAllocStackSlotHi32 ")); break;
default: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("other ")); break;
}
UT_LOG(x86ArgList, PR_LOG_DEBUG, ("use2(vr %p): ", alGetArg2VirtualRegister(inInsn)));
switch(alGetArg2VirtualRegister(inInsn)->getClass())
{
case IntegerRegister: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("IntegerRegister\n\n")); break;
case StackSlotRegister: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("StackSlotRegister\n\n")); break;
default: UT_LOG(x86ArgList, PR_LOG_DEBUG, ("other\n\n")); break;
}
}
#endif
bool x86DoubleArgumentList::
alSwitchArgumentTypeToSpill(Uint8 inWhichUse, x86ArgListInstruction& inInsn)
{
#ifdef DEBUG
alCheckIntegrity(inInsn); // ensure that everthing makes sense here
#endif
// ensure that we are not trying to spill a stack slot register
if(inWhichUse == 0)
assert(argType1 != atRegAllocStackSlot && argType1 != atRegAllocStackSlotHi32);
else
assert(argType2 != atRegAllocStackSlot && argType2 != atRegAllocStackSlotHi32);
// can only spill if both arguments are currently atRegDirect
if((argType1 != atRegDirect) || (argType2 != atRegDirect))
return false;
// try to spill
assert(inWhichUse <= 1);
if(inWhichUse == 0) // first use
{
// to spill we'll need to reverse the operands
// return failure if operands couldn't be reversed
if(!inInsn.canReverseOperands())
return false;
// ok, we can reverse, so do it
argType1 = atRegAllocStackSlot;
}
else // second use
{
// FIX FIX FIX Quick Hack
if(!inInsn.canReverseOperands())
return false;
argType2 = atRegAllocStackSlot;
}
return true;
}
// temporary helper method until x86DoubleArgumentList can be cleaned up
bool x86DoubleArgumentList::
alHasTwoArgs(x86ArgListInstruction& inInsn)
{
InstructionUse* useBegin = inInsn.getInstructionUseBegin();
InstructionUse* supplementaryUseBegin = useBegin + atInfo[argType1].atiNumUses; // skip over first arg's registers
InstructionUse* useEnd = inInsn.getInstructionUseEnd();
return (supplementaryUseBegin < useEnd) && supplementaryUseBegin->isVirtualRegister();
}
void x86DoubleArgumentList::
alFormatToMemory(void* inStart, Uint32 /*inOffset*/, x86ArgListInstruction& inInsn, MdFormatter& inFormatter)
{
VirtualRegister* directReg;
// Four cases:
//
// Use1 == Def Use2 is memory
// a b a [b]
// +----------+ <- +----------+ <-
// | add + --> add a, b | add + --> add a, [b]
// +----------+ +----------+
// a a
//
//
// Use1 != Def Use1 is memory
// a [a] b
// +----------+ +----------+ ->
// | movsx + --> movsx b, a | add + --> add b, [a]
// +----------+ +----------+
// b [a]
//
//
//
// 1. if there is a memory, format it first
// 2. if both register and only one use, format the define first
// Format Mem/Reg Operand // mem/reg == first use if...
if( (argType1 != atRegDirect) || // ...arg1 is memory, or
(argType1 == atRegDirect && argType2 == atRegDirect && !alHasTwoArgs(inInsn))) // ...both reg, and there is only one use
{
x86SingleArgumentList::alFormatToMemory(inStart, inInsn, argType1, 0, inFormatter);
directReg = alGetArg2VirtualRegister(inInsn);
assert(directReg->getClass() == IntegerRegister); // reg must be Integer register
}
else
{
//otherwise mem/reg == second use
x86SingleArgumentList::alFormatToMemory(inStart, inInsn, argType2, 1, inFormatter);
directReg = alGetArg1VirtualRegister(inInsn);
assert(directReg->getClass() == IntegerRegister); // reg must be Integer register
}
// Format Reg Operand
*(Uint8*)inStart = *(Uint8*)inStart | (getRegisterNumber(directReg) << 3);
}
#ifdef DEBUG_LOG
void x86DoubleArgumentList::
alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn)
{
InstructionUse* useBegin = inInsn.getInstructionUseBegin();
InstructionUse* useEnd = inInsn.getInstructionUseEnd();
Uint8 totalUses = atInfo[argType1].atiNumUses + atInfo[argType2].atiNumUses;
// Check to see if there are enough uses to encode all of the registers. If this is the
// case then the operands should be printed in there normal order. Otherwise the second operand is defined
// only in the defines (ie a mov instruction) and should be printed first.
if(useEnd >= useBegin + totalUses)
{
x86SingleArgumentList::alPrintPretty(f, inInsn, argType1, 0);
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (", "));
x86SingleArgumentList::alPrintPretty(f, inInsn, argType2, atInfo[argType1].atiNumUses);
} else {
x86SingleArgumentList::alPrintPretty(f, inInsn, argType2, atInfo[argType1].atiNumUses);
UT_OBJECTLOG(f, PR_LOG_ALWAYS, (", "));
x86SingleArgumentList::alPrintPretty(f, inInsn, argType1, 0);
}
}
#endif // DEBUG_LOG
VirtualRegister* x86DoubleArgumentList::
alGetArg1VirtualRegister(x86ArgListInstruction& inInsn)
{
InstructionUse* use = inInsn.getInstructionUseBegin();
return &use->getVirtualRegister();
}
// Find the virtual register corresponding to the second argument
VirtualRegister* x86DoubleArgumentList::
alGetArg2VirtualRegister(x86ArgListInstruction& inInsn)
{
VirtualRegister* vreg;
InstructionUse* useBegin = inInsn.getInstructionUseBegin();
InstructionUse* useEnd = inInsn.getInstructionUseEnd();
InstructionUse* supplementaryUseBegin = useBegin + atInfo[argType1].atiNumUses; // skip over first arg's registers
if( (supplementaryUseBegin < useEnd) && supplementaryUseBegin->isVirtualRegister() )
{
vreg = &(supplementaryUseBegin->getVirtualRegister()); // supplementary uses
}
else
{
InstructionDefine* defineBegin = inInsn.getInstructionDefineBegin();
vreg = &(defineBegin->getVirtualRegister()); // first define
}
return vreg;
}
//--------------------------------------------------------------------------------
// x86ControlNodeOffsetArgumentList
#ifdef DEBUG_LOG
void x86ControlNodeOffsetArgumentList::
alPrintPretty(LogModuleObject &f, x86ArgListInstruction& /*inInsn*/)
{
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("0x%x [<A HREF=\"#N%d\">N%d</A>]", cnoTarget.getNativeOffset(), cnoTarget.dfsNum, cnoTarget.dfsNum));
}
#endif // DEBUG_LOG
void x86ControlNodeOffsetArgumentList::
alFormatToMemory(void* inStart, Uint32 inOffset, x86ArgListInstruction& inInsn, MdFormatter& /*inFormatter*/)
{
// To compute the relative branch we subtract our current address from out target address. Then we subtract the size
// of our instruction because our current IP is actually there. This is the sum of the size of the opcode and the size of the
// argumentlist. NOTE: If we use 1 byte jumps in the future then this needs to be fixed from a constant 4.
writeLittleWordUnaligned((void*)inStart, cnoTarget.getNativeOffset() - inOffset - inInsn.opcodeSize() - 4);
}
//--------------------------------------------------------------------------------
// x86CondensableImmediateArgumentList
// must take 4 byte immediate
void x86CondensableImmediateArgumentList::
alFormatToMemory(void* inStart, Uint32 /*inOffset*/, x86ArgListInstruction& inInsn, MdFormatter& inFormatter)
{
Uint8* immLocation;
// find location of immediate.
if(argType1 != atRegDirect)
{
// argument list takes up some space
immLocation = (Uint8*)inStart + x86SingleArgumentList::alSize(argType1, inInsn, 0, inFormatter);
x86SingleArgumentList::alFormatToMemory(inStart, inInsn, argType1, 0, inFormatter);
} else
// argument list has been captured in the opcode
immLocation = (Uint8*)inStart;
// write out the immediate
writeLittleWordUnaligned((void*)immLocation, imm4Bytes);
}
Uint8 x86CondensableImmediateArgumentList::
alSize(x86ArgListInstruction& inInsn, MdFormatter& inFormatter)
{
return x86SingleArgumentList::alSize(inInsn, inFormatter) + 4;
}
//--------------------------------------------------------------------------------
// x86ImmediateArgumentList
void x86ImmediateArgumentList::
alFormatToMemory(void* inStart, Uint32 /*inOffset*/, x86ArgListInstruction& inInsn, MdFormatter& inFormatter)
{
// Find the location of the immediate.
Uint8* immLocation = (Uint8*)inStart;
if (argType1 != atImmediateOnly)
{
immLocation += x86SingleArgumentList::alSize(argType1, inInsn, 0, inFormatter);
x86SingleArgumentList::alFormatToMemory(inStart, inInsn, argType1, 0, inFormatter);
}
// write out the immediate
if(iSize == is1ByteImmediate && inInsn.opcodeCanAccept1ByteImmediate())
*immLocation = imm1Byte;
else
{
assert(inInsn.opcodeCanAccept4ByteImmediate());
writeLittleWordUnaligned((void*)immLocation, imm4Bytes);
}
}
Uint8 x86ImmediateArgumentList::
alSize(x86ArgListInstruction& inInsn, MdFormatter& inFormatter)
{
Uint8 size = 0;
// Handle special case of immediate push, which has only one argument
if (argType1 != atImmediateOnly)
size += x86SingleArgumentList::alSize(inInsn, inFormatter);
if (iSize == is1ByteImmediate && inInsn.opcodeCanAccept1ByteImmediate())
size += 1;
else
{
assert(inInsn.opcodeCanAccept4ByteImmediate());
size += 4;
}
return size;
}
//--------------------------------------------------------------------------------
// x86SpecialRegMemArgumentList
void x86SpecialRegMemArgumentList::
alFormatToMemory(void* inStart, Uint32 inOffset, x86ArgListInstruction& inInsn, MdFormatter& inFormatter)
{
if(argType1 == atRegDirect)
{
// We should output ourselves as though we were a double argument list
// with both arguments being the same register.
InstructionUse* useBegin = inInsn.getInstructionUseBegin();
InstructionUse* useEnd = inInsn.getInstructionUseEnd();
Uint8 reg;
if( (useBegin < useEnd) && (useBegin->isVirtualRegister()) )
reg = useToRegisterNumber(*useBegin);
else {
InstructionDefine* defineBegin = inInsn.getInstructionDefineBegin();
InstructionDefine* defineEnd = inInsn.getInstructionDefineEnd();
assert( (defineBegin < defineEnd) && (defineBegin->isVirtualRegister()) );
reg = defineToRegisterNumber(*defineBegin);
}
*(Uint8*)inStart = ( 0xc0 | (reg) | (reg << 3) );
}
else
{
// Otherwise we format ourselves as though we were an immediate argument list
x86ImmediateArgumentList::alFormatToMemory(inStart, inOffset, inInsn, inFormatter);
}
}
#ifdef DEBUG_LOG
void x86SpecialRegMemArgumentList::
alPrintPretty(LogModuleObject &f, x86ArgListInstruction& inInsn)
{
if(argType1 == atRegDirect)
{
// We should print ourselves as though we were a double argument list
// with both arguments being the same register.
InstructionUse* useBegin = inInsn.getInstructionUseBegin();
InstructionUse* useEnd = inInsn.getInstructionUseEnd();
Uint8 reg;
if( (useBegin < useEnd) && (useBegin->isVirtualRegister()) )
reg = useToRegisterNumber(*useBegin);
else {
InstructionDefine* defineBegin = inInsn.getInstructionDefineBegin();
InstructionDefine* defineEnd = inInsn.getInstructionDefineEnd();
assert( (defineBegin < defineEnd) && (defineBegin->isVirtualRegister()) );
reg = defineToRegisterNumber(*defineBegin);
}
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%s, %s", x86GPRText[reg], x86GPRText[reg] ));
} else
// Otherwise we print ourselves as though we were an immediate argument list
x86ImmediateArgumentList::alPrintPretty(f, inInsn);
}
#endif // DEBUG_LOG