700 lines
21 KiB
C++
700 lines
21 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.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
//
|
|
// x86Instruction.cpp
|
|
//
|
|
// Simon Holmes a Court
|
|
// Peter DeSantis
|
|
|
|
#include "prtypes.h"
|
|
#include "x86Instruction.h"
|
|
#include "InstructionEmitter.h"
|
|
#include "x86Formatter.h"
|
|
|
|
class x86Emitter;
|
|
extern char* x86GPRText[];
|
|
|
|
UT_DEFINE_LOG_MODULE(x86Spill);
|
|
|
|
|
|
//================================================================================
|
|
// Debugging Structures
|
|
#ifdef DEBUG_LOG
|
|
|
|
char* conditionalSuffixes[] =
|
|
{
|
|
"o", // ccJO
|
|
"no", // ccJNO
|
|
"b", // ccJB
|
|
"nb", // ccJNB
|
|
"e", // ccJE
|
|
"ne", // ccJNE
|
|
"be", // ccJBE
|
|
"nbe", // ccJNBE
|
|
"s", // ccJS
|
|
"ns", // ccJNS
|
|
"p", // ccJP
|
|
"np", // ccJNP
|
|
"l", // ccJL
|
|
"nl", // ccJNL
|
|
"le", // ccJLE
|
|
"nle", // ccJNLE
|
|
};
|
|
|
|
#endif // DEBUG_LOG
|
|
|
|
//================================================================================
|
|
// x86ArgListInstruction Methods
|
|
|
|
// For now this is a member of x86ArgListInstruction, but it really should be available for all
|
|
// instructions on the x86 platform
|
|
|
|
// Method: x86StandardUseDefine
|
|
// Caller: Emitter
|
|
// Purpose: Due to x86 behaviour of modifying source register, we must make a copy of the source before
|
|
// the operation. Unnecessary copies can be removed by the register allocator.
|
|
void x86ArgListInstruction::
|
|
x86StandardUseDefine(x86Emitter& inEmitter)
|
|
{
|
|
InstructionUse* instructionUseBegin = getInstructionUseBegin();
|
|
InstructionDefine* instructionDefineBegin = getInstructionDefineBegin();
|
|
Uint8 curIndex;
|
|
|
|
// Copy the virtual register which will be overwritten
|
|
VirtualRegister& vrToBeOverwritten = inEmitter.emit_CopyOfInput(*this, *mSrcPrimitive, 0);
|
|
|
|
inEmitter.useProducer(*mSrcPrimitive, *this, 0);
|
|
|
|
// Set the rest of the uses.
|
|
InstructionUse* curUse;
|
|
for (curUse = instructionUseBegin + 1, curIndex = 1; curUse < getInstructionUseEnd(); curUse++, curIndex++)
|
|
addStandardUse(inEmitter, curIndex);
|
|
|
|
// Now redefine the copied register.
|
|
inEmitter.redefineTemporary(*this, vrToBeOverwritten, 0);
|
|
|
|
assert(instructionDefineBegin + 1 >= getInstructionDefineEnd());
|
|
//// Set the rest of the defines.
|
|
//InstructionDefine* curDefine;
|
|
//for (curDefine = instructionDefineBegin + 1, curIndex = 1; curDefine < getInstructionDefineEnd(); curDefine++, curIndex++)
|
|
// inEmitter.defineProducer(nthOutputProducer(*mSrcPrimitive, curIndex), *this, curIndex);
|
|
}
|
|
|
|
// Method: switchUseToSpill
|
|
// Caller: Register Allocator
|
|
// Purpose: Folds the spill into the instruction if possible.
|
|
// Returns: Returns true if possible, false otherwise
|
|
bool x86ArgListInstruction::
|
|
switchUseToSpill(Uint8 inWhichUse, VirtualRegister& inVR)
|
|
{
|
|
#ifdef DEBUG_LOG
|
|
UT_LOG(x86Spill, PR_LOG_DEBUG, (" spill use %d of (%p)'", inWhichUse, this));
|
|
printOpcode(UT_LOG_MODULE(x86Spill));
|
|
#endif
|
|
DEBUG_ONLY(checkIntegrity();)
|
|
|
|
if (!opcodeAcceptsSpill())
|
|
goto SpillFail;
|
|
|
|
// Ask the argument list if it can switch the current argument
|
|
if (iArgumentList->alSwitchArgumentTypeToSpill(inWhichUse, *this)) // has side effect!
|
|
{
|
|
// Tell the Opcode that the argumentlist has been switched to a spill
|
|
switchOpcodeToSpill();
|
|
|
|
// Replace the old virtual register with a new one which contains the colored stack slot.
|
|
InstructionUse& use = getInstructionUseBegin()[inWhichUse];
|
|
InstructionDefine& define = getInstructionDefineBegin()[0];
|
|
assert(use.isVirtualRegister());
|
|
|
|
if(inWhichUse == 0 && define.isVirtualRegister() && use.getVirtualRegister().getRegisterIndex() == define.getVirtualRegister().getRegisterIndex())
|
|
// this virtual register is also redefined by this instruction so we must reinitialize the define also
|
|
define.getVirtualRegisterPtr().initialize(inVR);
|
|
use.getVirtualRegisterPtr().initialize(inVR);
|
|
DEBUG_ONLY(checkIntegrity();)
|
|
|
|
goto SpillSuccess;
|
|
}
|
|
|
|
// by default we fail
|
|
SpillFail:
|
|
UT_LOG(x86Spill, PR_LOG_DEBUG, ("': false\n"));
|
|
DEBUG_ONLY(checkIntegrity();)
|
|
return false;
|
|
|
|
SpillSuccess:
|
|
UT_LOG(x86Spill, PR_LOG_DEBUG, ("': true\n"));
|
|
DEBUG_ONLY(checkIntegrity();)
|
|
return true;
|
|
}
|
|
|
|
// Method: switchDefineToSpill
|
|
// Caller: Register Allocator
|
|
// Purpose: Folds the spill into the instruction if possible.
|
|
// Returns: Returns true if possible, false otherwise
|
|
bool x86ArgListInstruction::
|
|
switchDefineToSpill(Uint8 inWhichDefine, VirtualRegister& inVR)
|
|
{
|
|
#ifdef DEBUG_LOG
|
|
UT_LOG(x86Spill, PR_LOG_DEBUG, (" spill def %d of (%p)'", inWhichDefine, this));
|
|
printOpcode(UT_LOG_MODULE(x86Spill));
|
|
#endif
|
|
DEBUG_ONLY(checkIntegrity();)
|
|
|
|
assert(inWhichDefine == 0); // can only switch the first define
|
|
|
|
InstructionUse& use = getInstructionUseBegin()[0];
|
|
InstructionUse* useEnd = getInstructionUseEnd();
|
|
InstructionDefine& define = getInstructionDefineBegin()[inWhichDefine];
|
|
|
|
assert(define.isVirtualRegister()); // cannot call this routine on anything other than a virtual register
|
|
|
|
// some instructions cannot spill
|
|
if (!opcodeAcceptsSpill())
|
|
goto SpillFail;
|
|
|
|
// If this register is being redefined then it should correspond to the first use
|
|
// Make sure that there is a first use
|
|
if(&use < useEnd)
|
|
{
|
|
// If it is indeed the same virtual register
|
|
if(use.isVirtualRegister() && use.getVirtualRegister().getRegisterIndex() == define.getVirtualRegister().getRegisterIndex())
|
|
{
|
|
// define == first use, try to switch the first argument to spill type
|
|
if(opcodeAcceptsSpill() && iArgumentList->alSwitchArgumentTypeToSpill(0, *this))
|
|
{
|
|
switchOpcodeToSpill(); // Tell the Opcode that the argumentlist has been switched to a spill
|
|
// Replace the old virtual register with a new one which contains the colored stack slot
|
|
// The define is also the same as the use so we need to reinitialize both the use and the define VR.
|
|
use.getVirtualRegisterPtr().initialize(inVR);
|
|
define.getVirtualRegisterPtr().initialize(inVR);
|
|
goto SpillSuccess;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// There are no other VRs in the uses, define is the second argument
|
|
if(opcodeAcceptsSpill() && iArgumentList->alSwitchArgumentTypeToSpill(1, *this))
|
|
{
|
|
switchOpcodeToSpill(); // Tell the Opcode that the argumentlist has been switched to a spill
|
|
// Replace the old virtual register with a new one which contains the colored stack slot
|
|
define.getVirtualRegisterPtr().initialize(inVR);
|
|
goto SpillSuccess;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// There are no VRs in the uses so we need to try to switch the first argument
|
|
if(opcodeAcceptsSpill() && iArgumentList->alSwitchArgumentTypeToSpill(0, *this))
|
|
{
|
|
switchOpcodeToSpill(); // Tell the Opcode that the argumentlist has been switched to a spill
|
|
// Replace the old virtual register with a new one which contains the colored stack slot
|
|
define.getVirtualRegisterPtr().initialize(inVR);
|
|
goto SpillSuccess;
|
|
}
|
|
}
|
|
|
|
// by default we fail
|
|
SpillFail:
|
|
UT_LOG(x86Spill, PR_LOG_DEBUG, ("': false\n"));
|
|
DEBUG_ONLY(checkIntegrity();)
|
|
return false;
|
|
|
|
SpillSuccess:
|
|
UT_LOG(x86Spill, PR_LOG_DEBUG, ("': true\n"));
|
|
DEBUG_ONLY(checkIntegrity();)
|
|
return true;
|
|
}
|
|
|
|
//================================================================================
|
|
// x86Instruction
|
|
|
|
void x86Instruction::
|
|
formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& inFormatter)
|
|
{
|
|
assert(iOpcode != NULL && iArgumentList != NULL);
|
|
// Format the opcode to memory
|
|
iOpcode->opFormatToMemory(inStart, *iArgumentList, *this);
|
|
// Find the location of the argumnet list and format it to memory
|
|
Uint8* argLocation = (Uint8*)inStart + iOpcode->opSize();
|
|
iArgumentList->alFormatToMemory((void*)argLocation , inOffset, *this, inFormatter);
|
|
// If the opcode has an opcode extension then or it into the proper place. ( the reg field of the modr/m byte.)
|
|
if(iOpcode->opHasRegFieldExtension( *iArgumentList, *this ))
|
|
{
|
|
Uint8 temp = iOpcode->opGetRegFieldExtension();
|
|
*argLocation = *argLocation | temp;
|
|
}
|
|
}
|
|
|
|
//================================================================================
|
|
// InsnSwitch
|
|
|
|
InsnSwitch::
|
|
InsnSwitch(DataNode* inPrimitive, Pool& inPool) :
|
|
InsnUseXDefineYFromPool(inPrimitive, inPool, 1, 0 )
|
|
{
|
|
mControlNode = inPrimitive->getContainer();
|
|
assert(mControlNode);
|
|
mNumCases = mControlNode->nSuccessors();
|
|
}
|
|
|
|
void InsnSwitch::
|
|
formatToMemory(void* inStartAddress, Uint32 /*inOffset*/, MdFormatter& inFormatter)
|
|
{
|
|
Uint8* start = (Uint8*)inStartAddress;
|
|
|
|
// calculate position of jump table
|
|
mTableAddress = start + 7;
|
|
|
|
// get the register
|
|
Uint8 reg = useToRegisterNumber(*getInstructionUseBegin());
|
|
assert (reg != ESP); // ESP is an invalid index
|
|
|
|
// calculate the SIB
|
|
Uint8 SIB = 0x80 | ( reg << 3) | 0x05;
|
|
|
|
// write out instruction
|
|
*start++ = 0xff; // opcode for jump
|
|
*start++ = 0x24; // mod/ext/rm for jmp disp32[reg * 4]
|
|
*start++ = SIB;
|
|
|
|
// write address of jump table
|
|
writeLittleWordUnaligned(start, (int)mTableAddress);
|
|
|
|
// write out table
|
|
Uint8* methodBegin = inFormatter.getMethodBegin();
|
|
ControlEdge* edgesEnd = mControlNode->getSuccessorsEnd();
|
|
for(ControlEdge* edge = mControlNode->getSwitchSuccessors(); edge != edgesEnd; edge++)
|
|
{
|
|
Uint8* destAddress = methodBegin + edge->getTarget().getNativeOffset();
|
|
start += 4;
|
|
writeLittleWordUnaligned(start, (Uint32)destAddress);
|
|
}
|
|
}
|
|
|
|
size_t InsnSwitch::
|
|
getFormattedSize(MdFormatter& /*inFormatter*/)
|
|
{
|
|
// reserve 7 bytes for the indexed jump, plus 4 bytes per entry
|
|
return 7 + (4 * mNumCases);
|
|
}
|
|
|
|
#ifdef DEBUG_LOG
|
|
void InsnSwitch::
|
|
printPretty(LogModuleObject &f)
|
|
{
|
|
Uint8 reg = useToRegisterNumber(*getInstructionUseBegin());
|
|
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("jmp 0x%x[4 * %s]", Uint32(mTableAddress), x86GPRText[reg]));
|
|
|
|
// print table
|
|
ControlEdge* edgesEnd = mControlNode->getSuccessorsEnd();
|
|
for(ControlEdge* edge = mControlNode->getSwitchSuccessors(); edge != edgesEnd; edge++)
|
|
{
|
|
Uint32 destAddress = edge->getTarget().getNativeOffset();
|
|
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("\n Method Start + 0x%x [<A HREF=\"#N%d\">N%d</A>]",
|
|
destAddress, edge->getTarget().dfsNum, edge->getTarget().dfsNum));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//================================================================================
|
|
// InsnCondBranch
|
|
|
|
void InsnCondBranch::
|
|
formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& /*inFormatter*/)
|
|
{
|
|
uint8* start = (uint8*) inStart;
|
|
|
|
*start++ = 0x0f;
|
|
*start++ = 0x80 | condType;
|
|
|
|
// find destination
|
|
ControlNode& cnoTarget = cnoSource.getTrueSuccessor().getTarget();
|
|
|
|
// 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.
|
|
const int opcodeSize = 2;
|
|
Int32 jumpOffset = cnoTarget.getNativeOffset() - inOffset - opcodeSize - 4;
|
|
writeLittleWordUnaligned(start, jumpOffset);
|
|
}
|
|
|
|
#ifdef DEBUG_LOG
|
|
|
|
void InsnCondBranch::
|
|
printPretty(LogModuleObject &f)
|
|
{
|
|
ControlNode& cnoTarget = cnoSource.getTrueSuccessor().getTarget();
|
|
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("j%-7s start+%0d [<A HREF=\"#N%d\">N%d</A>]",
|
|
conditionalSuffixes[condType], cnoTarget.getNativeOffset(), cnoTarget.dfsNum, cnoTarget.dfsNum));
|
|
}
|
|
|
|
#endif
|
|
|
|
//================================================================================
|
|
// Set
|
|
|
|
// Can only be EAX, ECX, EDX or EBX
|
|
void InsnSet::
|
|
formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/ )
|
|
{
|
|
uint8* start = (uint8*) inStart;
|
|
|
|
// format the opcode to memory
|
|
*start++ = 0x0f;
|
|
*start++ = 0x90 | condType;
|
|
|
|
// find the register
|
|
InstructionDefine* defineBegin = getInstructionDefineBegin();
|
|
|
|
#ifdef DEBUG
|
|
InstructionUse* useBegin = getInstructionUseBegin();
|
|
InstructionUse* useEnd = getInstructionUseEnd();
|
|
assert(useBegin < useEnd); // condition code always used
|
|
|
|
InstructionDefine* defineEnd = getInstructionDefineEnd();
|
|
assert((defineBegin < defineEnd) && defineBegin->isVirtualRegister());
|
|
#endif
|
|
|
|
Uint8 reg = defineToRegisterNumber(*defineBegin);
|
|
assert(/* (reg >= 0) && */ (reg <= 3)); // these are the only legal registers
|
|
|
|
// format the register
|
|
Uint8 modRM = 0xc0 | reg;
|
|
*start = modRM;
|
|
}
|
|
|
|
#ifdef DEBUG_LOG
|
|
|
|
void InsnSet::
|
|
printPretty(LogModuleObject &f)
|
|
{
|
|
InstructionDefine* defineBegin = getInstructionDefineBegin();
|
|
Uint8 reg = defineToRegisterNumber(*defineBegin);
|
|
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("set%-5s %s", conditionalSuffixes[condType], x86GPRText[reg]));
|
|
}
|
|
|
|
#endif
|
|
|
|
//================================================================================
|
|
// InsnCondSysCallBranch
|
|
#ifdef DEBUG_LOG
|
|
void InsnSysCallCondBranch::
|
|
printPretty(LogModuleObject &f)
|
|
{
|
|
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("j%-7s (over next)\n call %p", conditionalSuffixes[condType], functionAddress));
|
|
}
|
|
#endif
|
|
|
|
//================================================================================
|
|
// InsnCondSysCallBranch
|
|
struct x86NoArgsInfo
|
|
{
|
|
uint8 opcode; // generally the opcode, but can have additional info
|
|
// eventually will be in DEBUG_LOG build only
|
|
char* text; // string for fake disassembly
|
|
};
|
|
|
|
x86NoArgsInfo noArgsInfo[] =
|
|
{
|
|
{ 0x99, "cdq" }, //opCdq 99
|
|
{ 0xcc, "int 3" }, //opBreak cc
|
|
{ 0x9e, "sahf" }, //opSahf 9e
|
|
};
|
|
|
|
void InsnNoArgs::
|
|
formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/)
|
|
{
|
|
*((Uint8*)inStart) = (Uint8) noArgsInfo[code].opcode;
|
|
}
|
|
|
|
#ifdef DEBUG_LOG
|
|
void InsnNoArgs::
|
|
printPretty(LogModuleObject &f)
|
|
{
|
|
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%-8s", noArgsInfo[code].text));
|
|
}
|
|
#endif
|
|
|
|
//================================================================================
|
|
// InsnDoubleOp Methods
|
|
struct x86DoubleOpInfo
|
|
{
|
|
uint8 opcode; // generally the opcode, but can have additional info
|
|
bool hasPrefix; // does the opcode needs to be prefixed with 0x0f
|
|
|
|
// eventually will be in DEBUG_LOG build only
|
|
char* text; // string for fake disassembly
|
|
};
|
|
|
|
x86DoubleOpInfo doubleOpInfo[] =
|
|
{
|
|
{ 0xaf, true, "imul " }, //opIMul 0f af
|
|
{ 0xbe, true, "movsxB "}, //opMovSxB 0f be
|
|
{ 0xbf, true, "movsxH "}, //opMovSxH 0f bf
|
|
{ 0xb6, true, "movzxB "}, //opMovZxB 0f b6
|
|
{ 0xb7, true, "movzxH "} //opMovZxH 0f b7
|
|
};
|
|
|
|
InsnDoubleOp::
|
|
InsnDoubleOp( DataNode* inPrimitive, Pool& inPool, x86DoubleOpCode inCodeType,
|
|
x86ArgumentType inArgType1, x86ArgumentType inArgType2,
|
|
Uint8 uses, Uint8 defines ) :
|
|
x86ArgListInstruction (inPrimitive, inPool, uses, defines ),
|
|
codeType(inCodeType)
|
|
{
|
|
iArgumentList = new x86DoubleArgumentList(inArgType1, inArgType2);
|
|
DEBUG_ONLY(debugType = kInsnDoubleOp);
|
|
}
|
|
|
|
InsnDoubleOp::
|
|
InsnDoubleOp( DataNode* inPrimitive, Pool& inPool, x86DoubleOpCode inCodeType,
|
|
Uint32 inDisplacement,
|
|
x86ArgumentType inArgType1, x86ArgumentType inArgType2,
|
|
Uint8 uses, Uint8 defines) :
|
|
x86ArgListInstruction (inPrimitive, inPool, uses, defines ),
|
|
codeType(inCodeType)
|
|
{
|
|
iArgumentList = new x86DoubleArgumentList(inArgType1, inArgType2, inDisplacement);
|
|
DEBUG_ONLY(debugType = kInsnDoubleOp);
|
|
}
|
|
|
|
void InsnDoubleOp::
|
|
formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& inFormatter)
|
|
{
|
|
uint8* start = (uint8*) inStart;
|
|
assert(iArgumentList);
|
|
|
|
// Format the opcode to memory
|
|
if(doubleOpInfo[codeType].hasPrefix)
|
|
*start++ = kPrefix_For_2_Byte;
|
|
*start = doubleOpInfo[codeType].opcode;
|
|
|
|
// Find the location of the argumnet list and format it to memory
|
|
Uint8* argLocation = (Uint8*)inStart + opcodeSize();
|
|
iArgumentList->alFormatToMemory((void*)argLocation , inOffset, *this, inFormatter);
|
|
}
|
|
|
|
Uint8 InsnDoubleOp::
|
|
opcodeSize()
|
|
{
|
|
return (doubleOpInfo[codeType].hasPrefix) ? 2 : 1;
|
|
}
|
|
|
|
#ifdef DEBUG_LOG
|
|
void InsnDoubleOp::
|
|
printOpcode(LogModuleObject &f)
|
|
{
|
|
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%-8s ", doubleOpInfo[codeType].text));
|
|
}
|
|
#endif
|
|
|
|
//================================================================================
|
|
// InsnDoubleOpDir Methods
|
|
|
|
// used for debugging only (for now)
|
|
enum raType
|
|
{
|
|
kArith,
|
|
kLoad,
|
|
kStore,
|
|
kCopy
|
|
};
|
|
|
|
struct x86RAOpcodeInfo
|
|
{
|
|
uint8 opcode; // generally the opcode, but can have additional info
|
|
bool needs16BitPrefix; // does the opcode need the 'force to 16 bit' opcode?
|
|
raType type; // is it a move
|
|
|
|
// eventually will be in DEBUG_LOG build only
|
|
char* text; // string for fake disassembly
|
|
};
|
|
|
|
// note: store dest, source
|
|
// note: copy
|
|
x86RAOpcodeInfo raInfo[] =
|
|
{
|
|
{ 0x01, false, kArith, "add " }, //raAdd
|
|
{ 0x11, false, kArith, "adc " }, //raAdc
|
|
{ 0x29, false, kArith, "sub " }, //raSub
|
|
{ 0x19, false, kArith, "sbb " }, //raSbb
|
|
{ 0x21, false, kArith, "and " }, //raAnd
|
|
{ 0x09, false, kArith, "or " }, //raOr
|
|
{ 0x31, false, kArith, "xor " }, //raXor
|
|
{ 0x39, false, kArith, "cmp " }, //raCmp
|
|
|
|
{ 0x89, false, kLoad, "mov(ld) " }, //raLoadI
|
|
{ 0x89, false, kCopy, "mov(cp) " }, //raCopyI
|
|
{ 0x89, false, kStore, "mov(st) " }, //raStoreI
|
|
{ 0x89, false, kStore, "mov(sv) " }, //raSaveReg
|
|
|
|
{ 0x88, false, kStore, "movB "}, //raStoreB
|
|
{ 0x89, true, kStore, "movH "}, //raStoreH
|
|
};
|
|
|
|
InsnDoubleOpDir::
|
|
InsnDoubleOpDir(DataNode* inPrimitive, Pool& inPool, x86DoubleOpDirCode inCodeType,
|
|
x86ArgumentType inArgType1, x86ArgumentType inArgType2,
|
|
Uint8 uses, Uint8 defines ) :
|
|
x86ArgListInstruction (inPrimitive, inPool, uses, defines ),
|
|
codeType(inCodeType)
|
|
{
|
|
iArgumentList = new x86DoubleArgumentList(inArgType1, inArgType2);
|
|
DEBUG_ONLY(debugType = kInsnDoubleOpDir);
|
|
|
|
// temp for asserts
|
|
getDirectionBit();
|
|
}
|
|
|
|
InsnDoubleOpDir::
|
|
InsnDoubleOpDir(DataNode* inPrimitive, Pool& inPool, x86DoubleOpDirCode inCodeType,
|
|
Uint32 inDisplacement, x86ArgumentType inArgType1, x86ArgumentType inArgType2,
|
|
Uint8 uses, Uint8 defines) :
|
|
x86ArgListInstruction (inPrimitive, inPool, uses, defines ),
|
|
codeType(inCodeType)
|
|
{
|
|
iArgumentList = new x86DoubleArgumentList(inArgType1, inArgType2, inDisplacement);
|
|
DEBUG_ONLY(debugType = kInsnDoubleOpDir);
|
|
|
|
// temp for asserts
|
|
getDirectionBit();
|
|
}
|
|
|
|
// copy flag should only be set if insn is a copy and the use and define are registerDirect -- no other flags can be set
|
|
InstructionFlags InsnDoubleOpDir::
|
|
getFlags() const
|
|
{
|
|
return (codeType == raCopyI && iArgumentList->alIsRegisterDirect()) ? ifCopy : ifNone;
|
|
}
|
|
|
|
bool InsnDoubleOpDir::
|
|
getDirectionBit()
|
|
{
|
|
x86DoubleArgumentList* arglist = (x86DoubleArgumentList*)iArgumentList;
|
|
bool arg1isDirect = arglist->akIsArg1Direct();
|
|
bool arg2isDirect = arglist->akIsArg2Direct();
|
|
assert(arg1isDirect || arg2isDirect); // at least one has to be register direct
|
|
|
|
bool dirBit;
|
|
|
|
switch(codeType)
|
|
{
|
|
case raSaveReg:
|
|
// Save Instructions
|
|
// saves use (first arg) to stack slot (define)
|
|
// mov r1 -> M => mov r1 -> M dir = false
|
|
assert(arg1isDirect);
|
|
assert(!arg2isDirect);
|
|
dirBit = false;
|
|
break;
|
|
|
|
case raCopyI:
|
|
// Copy Instructions
|
|
/* OLD
|
|
// copies use (first arg) to define (second arg)
|
|
// mov r1 -> r2 => mov r1 -> r2 dir = false
|
|
// mov r1 -> M => mov r1 -> M dir = false
|
|
// mov M -> r2 => mov r2 <- M dir = true
|
|
// ie direction bit is clear iff argument 1 is registerDirect
|
|
dirBit = !arg1isDirect;
|
|
break;
|
|
*/
|
|
|
|
// copies use (first arg) to define (second arg)
|
|
// mov r1 -> r2 => mov r2 <- r1 dir = true
|
|
// mov r1 -> M => mov r1 -> M dir = false
|
|
// mov M -> r2 => mov r2 <- M dir = true
|
|
// ie direction bit is clear iff argument 1 is registerDirect
|
|
dirBit = arg2isDirect;
|
|
break;
|
|
|
|
case raStoreB:
|
|
case raStoreH:
|
|
case raStoreI:
|
|
// Store Instruction
|
|
// stores second use into first use memory
|
|
// mov M <- r2 => mov r2 -> M dir = false
|
|
dirBit = false;
|
|
assert(!arg1isDirect);
|
|
assert(arg2isDirect);
|
|
break;
|
|
|
|
case raLoadI:
|
|
// Load Instruction
|
|
// loads from memory address (use, first arg) into register (define, second arg)
|
|
// mov M -> r1 => mov r1 <- M dir = true
|
|
dirBit = true;
|
|
assert(!arg1isDirect);
|
|
assert(arg2isDirect);
|
|
break;
|
|
|
|
default:
|
|
// Arithmetic Instructions
|
|
// add r1, r2 => add r1, r2 dir = true
|
|
// add r1, M => add r1, M dir = true
|
|
// add M, r2 => add r2, M dir = false
|
|
// ie direction bit is set iff argument 1 is registerDirect
|
|
assert(raInfo[codeType].type == kArith);
|
|
dirBit = arg1isDirect;
|
|
}
|
|
|
|
return dirBit;
|
|
}
|
|
|
|
void InsnDoubleOpDir::
|
|
formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& inFormatter)
|
|
{
|
|
uint8* start = (uint8*) inStart;
|
|
assert(iArgumentList);
|
|
|
|
// Format the opcode to memory
|
|
if(raInfo[codeType].needs16BitPrefix)
|
|
*start++ = kPrefix_For_16_Bits;
|
|
|
|
// calculate opcode
|
|
Uint8 direction = getDirectionBit() ? 2 : 0;
|
|
Uint8 opcode = raInfo[codeType].opcode | direction;
|
|
*start = opcode;
|
|
|
|
// Find the location of the argumnet list and format it to memory
|
|
Uint8* argLocation = (Uint8*)inStart + opcodeSize();
|
|
iArgumentList->alFormatToMemory((void*)argLocation , inOffset, *this, inFormatter);
|
|
}
|
|
|
|
Uint8 InsnDoubleOpDir::
|
|
opcodeSize()
|
|
{
|
|
return (raInfo[codeType].needs16BitPrefix) ? 2 : 1;
|
|
}
|
|
|
|
#ifdef DEBUG_LOG
|
|
void InsnDoubleOpDir::
|
|
printOpcode(LogModuleObject &f)
|
|
{
|
|
UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%-8s ", raInfo[codeType].text));
|
|
}
|
|
#endif
|
|
|
|
//================================================================================
|