/* -*- 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.h // // Peter DeSantis // Simon Holmes a Court #ifndef X86_WIN32_INSTRUCTION #define X86_WIN32_INSTRUCTION #include "CatchAssert.h" #include #include "prtypes.h" #include "Instruction.h" #include "InstructionEmitter.h" #include "VirtualRegister.h" #include "Value.h" #include "x86Opcode.h" #include "x86ArgumentList.h" #include "MemoryAccess.h" #include "LogModule.h" #include "NativeCodeCache.h" #ifdef DEBUG enum instructionObjType { kx86Instruction, kSetInstruction, kInsnDoubleOpDir, kInsnDoubleOp, kStandardArith, kStandardArithDisp, kNotKnown }; #endif //================================================================================ /* x86Instruction(being eliminated) InsnSet InsnDoubleOp InsnSwitch InsnDoubleOpDir InsnSysCallCondBranch | InsnCondBranch | InsnNoArgs x86ArglistInstruction Call \ / \ / \ / InsnUseXDefineYFromPool | Instruction (can't spill) */ //================================================================================ // x86ArgListInstruction class x86ArgListInstruction : public InsnUseXDefineYFromPool { public: x86ArgListInstruction(DataNode* inPrimitive, Pool& inPool, Uint8 inX, Uint8 inY) : InsnUseXDefineYFromPool (inPrimitive, inPool, inX, inY ) { DEBUG_ONLY(debugType = kNotKnown); } // utility void x86StandardUseDefine(x86Emitter& inEmitter); // virtual methods that must be written virtual Uint8 opcodeSize() = 0; // defaults to (result of opcodeSize() + iArgumentList->alSize(*this)) virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/); virtual void formatToMemory(void * /*inStart*/, Uint32 /*inCurOffset*/, MdFormatter& /*inFormatter*/) = 0; // flags InstructionFlags getFlags() const { return ifNone; } // spilling virtual bool switchUseToSpill(Uint8 inWhichUse, VirtualRegister&inVR); virtual bool switchDefineToSpill(Uint8 inWhichDefine, VirtualRegister& inVR); // Control Spilling // eg switch cannot spill (at least for now) // eg special mem-reg type does funky things when spilling virtual bool opcodeAcceptsSpill() { return true; } // most instructions can spill virtual void switchOpcodeToSpill() { } // most opcodes don't change on spilling // simple arithmetic and move instructions have a direction bit virtual bool canReverseOperands() { return false; } // most instructions can't reverse their operands // immediates virtual bool opcodeCanAccept1ByteImmediate() { return false; } virtual bool opcodeCanAccept4ByteImmediate() { return false; } virtual bool opcodeCanAcceptImmediate() { return (opcodeCanAccept1ByteImmediate() || opcodeCanAccept4ByteImmediate()); } // condensable virtual bool regOperandCanBeCondensed() { return false; } protected: x86ArgumentList* iArgumentList; public: #ifdef DEBUG_LOG // Debugging Methods virtual void printPretty(LogModuleObject &f); virtual void printOpcode(LogModuleObject &f) = 0; #endif // DEBUG_LOG #ifdef DEBUG instructionObjType debugType; // used so we know what type of object we are in the debugger void checkIntegrity() { iArgumentList->alCheckIntegrity(*this); } void printArgs() { iArgumentList->alPrintArgs(*this); } #endif // DEBUG }; inline size_t x86ArgListInstruction:: getFormattedSize(MdFormatter& inFormatter) { assert(iArgumentList != NULL); size_t size = opcodeSize() + iArgumentList->alSize(*this, inFormatter); return size; } #ifdef DEBUG_LOG inline void x86ArgListInstruction:: printPretty(LogModuleObject &f) { printOpcode(f); assert(iArgumentList != NULL); iArgumentList->alPrintPretty(f, *this ); } #endif // DEBUG_LOG //================================================================================ // x86Instruction // This mega-class will eventually be phased out as it is split up into smaller classes class x86Instruction : public x86ArgListInstruction { public: //-------------------------------------------------------------------------------- // ImmediateArithType Instructions x86Instruction (DataNode* inPrimitive, Pool& inPool, ControlNode& inControlNode) : x86ArgListInstruction (inPrimitive, inPool, 0, 0 ) , flags(ifNone) { iOpcode = new x86Opcode_ImmArith( iaJmp ); iArgumentList = new x86ControlNodeOffsetArgumentList( inControlNode ); } x86Instruction (DataNode* inPrimitive, Pool& inPool, x86ImmediateArithType inOpInfo, Uint32 inConstant, x86ArgumentType inArgType1, Uint8 inX, Uint8 inY) : x86ArgListInstruction (inPrimitive, inPool, inX, inY ) , flags(ifNone) { iOpcode = new x86Opcode_ImmArith( inOpInfo ); iArgumentList = new x86ImmediateArgumentList( inArgType1, inConstant ); } x86Instruction (DataNode* inPrimitive, Pool& inPool, x86ImmediateArithType inOpInfo, Uint32 inConstant, x86ArgumentType inArgType1, Uint32 inDisplacement, Uint8 inX, Uint8 inY) : x86ArgListInstruction (inPrimitive, inPool, inX, inY ) , flags(ifNone) { iOpcode = new x86Opcode_ImmArith( inOpInfo ); iArgumentList = new x86ImmediateArgumentList( inArgType1, inDisplacement, inConstant ); } //-------------------------------------------------------------------------------- // ExtendedType Instructions x86Instruction (DataNode* inPrimitive, Pool& inPool, x86ExtendedType inOpInfo, Uint32 inConstant, x86ArgumentType inArgType1, Uint8 inX, Uint8 inY) : x86ArgListInstruction (inPrimitive, inPool, inX, inY ) , flags(ifNone) { iOpcode = new x86Opcode_Reg( inOpInfo ); iArgumentList = new x86ImmediateArgumentList( inArgType1, inConstant ); } x86Instruction (DataNode* inPrimitive, Pool& inPool, x86ExtendedType inOpInfo, Uint32 inConstant, x86ArgumentType inArgType1, Uint32 inDisplacement, Uint8 inX, Uint8 inY) : x86ArgListInstruction (inPrimitive, inPool, inX, inY ) , flags(ifNone) { iOpcode = new x86Opcode_Reg( inOpInfo ); iArgumentList = new x86ImmediateArgumentList( inArgType1, inDisplacement, inConstant ); } x86Instruction (DataNode* inPrimitive, Pool& inPool, x86ExtendedType inOpInfo, x86ArgumentType inArgType1, Uint8 inX, Uint8 inY ) : x86ArgListInstruction (inPrimitive, inPool, inX, inY ) , flags(ifNone) { iOpcode = new x86Opcode_Reg( inOpInfo ); iArgumentList = new x86SingleArgumentList( inArgType1 ); } x86Instruction (DataNode* inPrimitive, Pool& inPool, x86ExtendedType inOpInfo, x86ArgumentType inArgType1, Uint32 inDisplacement, Uint8 inX, Uint8 inY) : x86ArgListInstruction (inPrimitive, inPool, inX, inY ) , flags(ifNone) { iOpcode = new x86Opcode_Reg( inOpInfo ); iArgumentList = new x86SingleArgumentList( inArgType1, inDisplacement ); } //-------------------------------------------------------------------------------- // CondensableExtendedType Instructions x86Instruction (DataNode* inPrimitive, Pool& inPool, x86CondensableExtendedType inOpInfo, Uint32 inConstant, x86ArgumentType inArgType1, Uint8 inX, Uint8 inY) : x86ArgListInstruction (inPrimitive, inPool, inX, inY ) , flags(ifNone) { iOpcode = new x86Opcode_Condensable_Reg( inOpInfo ); iArgumentList = new x86CondensableImmediateArgumentList( inArgType1, inConstant ); } x86Instruction (DataNode* inPrimitive, Pool& inPool, x86CondensableExtendedType inOpInfo, x86ArgumentType inArgType1, Uint8 inX, Uint8 inY) : x86ArgListInstruction (inPrimitive, inPool, inX, inY ) , flags(ifNone) { iOpcode = new x86Opcode_Condensable_Reg( inOpInfo ); iArgumentList = new x86SingleArgumentList( inArgType1 ); } //-------------------------------------------------------------------------------- // SpecialRegMemType Instruction x86Instruction (DataNode* inPrimitive, Pool& inPool, x86SpecialRegMemType inType, Uint32 inImmediate, Uint8 inX, Uint8 inY ) : x86ArgListInstruction (inPrimitive, inPool, inX, inY ) , flags(ifNone) { iOpcode = new x86Opcode_SpecialRegMem( inType ); iArgumentList = new x86SpecialRegMemArgumentList( inImmediate ); } size_t getFormattedSize(MdFormatter& /*inFormatter*/); virtual void formatToMemory(void * /*inStart*/, Uint32 /*inCurOffset*/, MdFormatter& /*inFormatter*/); // access flags InstructionFlags getFlags() const { return flags; } void setFlags(InstructionFlags f) { flags = f; } // Allows ArgumentList access to opcode without passing the extra reference to the opcode. Uint8 opcodeSize(){ return iOpcode->opSize(); } bool opcodeCanAccept1ByteImmediate() { return iOpcode->opCanAccept1ByteImmediate(); } bool opcodeCanAccept4ByteImmediate() { return iOpcode->opCanAccept4ByteImmediate(); } bool regOperandCanBeCondensed() { return iOpcode->opRegOperandCanBeCondensed(); } virtual bool opcodeAcceptsSpill() { return true; } virtual void switchOpcodeToSpill() { iOpcode->opSwitchToRegisterIndirect(); } protected: x86Opcode* iOpcode; InstructionFlags flags; // Used to mark copy instructions so the register allocator can remove them. public: #ifdef DEBUG_LOG void printOpcode(LogModuleObject &f); #endif // DEBUG_LOG }; //-------------------------------------------------------------------------------- // FIX these two methods should be removed (third method replaces them) inline Uint8 useToRegisterNumber(InstructionUse& inUse) { return colorTox86GPR[inUse.getVirtualRegister().getColor()]; } inline Uint8 defineToRegisterNumber(InstructionDefine& inDefine) { Uint8 color = inDefine.getVirtualRegister().getColor(); return colorTox86GPR[color]; } inline Uint8 getRegisterNumber(VirtualRegister* vreg) { Uint8 color = vreg->getColor(); assert(color < 6); return colorTox86GPR[color]; } //-------------------------------------------------------------------------------- inline size_t x86Instruction:: getFormattedSize(MdFormatter& inFormatter) { assert(iOpcode != NULL && iArgumentList != NULL); return (iOpcode->opSize() + iArgumentList->alSize(*this, inFormatter)); } #ifdef DEBUG_LOG inline void x86Instruction:: printOpcode(LogModuleObject &f) { iOpcode->opPrintPretty(f); } #endif DEBUG_LOG //================================================================================ // InsnNoArgs class InsnNoArgs : public InsnUseXDefineYFromPool { public: InsnNoArgs(DataNode* inPrimitive, Pool& inPool, x86NoArgsCode inCode, Uint8 inUses, Uint8 inDefines) : InsnUseXDefineYFromPool(inPrimitive, inPool, inUses, inDefines), code(inCode) {} virtual void formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/); virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/) { return 1; } InstructionFlags getFlags() const { return ifNone; } virtual bool opcodeAcceptsSpill() { return false; } virtual void switchOpcodeToSpill() { assert(false); } protected: x86NoArgsCode code; #ifdef DEBUG_LOG public: virtual void printPretty(LogModuleObject &f); #endif }; //================================================================================ // InsnSwitch class InsnSwitch : public InsnUseXDefineYFromPool { public: InsnSwitch(DataNode* inPrimitive, Pool& inPool); virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& /*inFormatter*/); virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/); InstructionFlags getFlags() const { return ifNone; } // can't spill virtual bool opcodeAcceptsSpill() { return false; } virtual void switchOpcodeToSpill() { assert(false); } protected: Uint32 mNumCases; ControlNode* mControlNode; Uint8* mTableAddress; #ifdef DEBUG_LOG public: virtual void printPretty(LogModuleObject &f); #endif }; //================================================================================ // InsnCondBranch // FIX For now all branches take 4 bytes immediates -- eventually we want this to take // the minimum possible class InsnCondBranch : public InsnUseXDefineYFromPool { public: InsnCondBranch(DataNode* inPrimitive, Pool& inPool, x86ConditionCode condType, ControlNode& inControlNode) : InsnUseXDefineYFromPool(inPrimitive, inPool, 1, 0), condType(condType), cnoSource(inControlNode) {}; virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& /*inFormatter*/); virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/) { return 6; } InstructionFlags getFlags() const { return ifNone; } virtual bool opcodeAcceptsSpill() { return false; } // spilling makes no sense here virtual void switchOpcodeToSpill() { assert(false); } protected: x86ConditionCode condType; // x86 condition codes ControlNode& cnoSource; // the source of the branch #ifdef DEBUG_LOG public: virtual void printPretty(LogModuleObject &f); #endif }; //================================================================================ // InsnSysCallCondBranch // emit // jcc OK // call inFunc // OK: // class InsnSysCallCondBranch : public InsnUseXDefineYFromPool { public: InsnSysCallCondBranch(DataNode* inPrimitive, Pool& inPool, x86ConditionCode inCondType, void (*inFunc)()) : InsnUseXDefineYFromPool(inPrimitive, inPool, 1, 0), functionAddress(inFunc), condType(inCondType) {}; virtual void formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/) { Uint8* start = (Uint8*) inStart; // emit jump *start++ = 0x0f; *start++ = 0x80 + condType; writeLittleWordUnaligned((void*)start, 5); start += 4; // emit call Uint8* callStart = start; *start++ = 0xe8; int32 branchOffset = (Uint32)functionAddress - (Uint32) callStart - 5; writeLittleWordUnaligned((void*)start, branchOffset); } virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/) { return 6 + 5; } InstructionFlags getFlags() const { return ifNone; } virtual bool opcodeAcceptsSpill() { return false; } // spilling makes no sense here virtual void switchOpcodeToSpill() { assert(false); } protected: void* functionAddress; x86ConditionCode condType; #ifdef DEBUG_LOG public: virtual void printPretty(LogModuleObject &f); #endif }; //================================================================================ // Set on condition flags // cannot spill // uses a condition class InsnSet : public InsnUseXDefineYFromPool { public: InsnSet(DataNode* inPrimitive, Pool& inPool, x86ConditionCode condType, int inX = 2, int inY = 1) : InsnUseXDefineYFromPool(inPrimitive, inPool, inX, inY ), condType(condType) {} virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& /* inFormatter */ ); virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/) { return 3; } // 0xff 0x90 reg virtual Uint8 opcodeSize() { return 2; } protected: x86ConditionCode condType; // x86 condition codes #ifdef DEBUG_LOG public: virtual void printPretty(LogModuleObject &f); #endif }; //================================================================================ /* InsnDoubleOp 0110 1001:11 reg1 reg2: immdata register1 with immediate to register2 0110 1001:mod reg r/m: immdata register with immediate to register 0000 1111:1010 1111: 11 reg1 reg2 register1 with register2 0000 1111:1010 1111: mod reg r/m register with memory */ class InsnDoubleOp : public x86ArgListInstruction { public: InsnDoubleOp( DataNode* inPrimitive, Pool& inPool, x86DoubleOpCode inCodeType, x86ArgumentType inArgType1 = atRegDirect, x86ArgumentType inArgType2 = atRegDirect, Uint8 uses = 2, Uint8 defines = 1 ); InsnDoubleOp( DataNode* inPrimitive, Pool& inPool, x86DoubleOpCode inCodeType, Uint32 inDisplacement, x86ArgumentType inArgType1 = atRegDirect, x86ArgumentType inArgType2 = atRegDirect, Uint8 uses = 2, Uint8 defines = 1 ); virtual bool canReverseOperands() { return false; } virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& inFormatter); virtual Uint8 opcodeSize(); protected: x86DoubleOpCode codeType; public: #ifdef DEBUG_LOG virtual void printOpcode(LogModuleObject &f); #endif }; //================================================================================ // InsnDoubleOpDir // use the default spilling behaviour for x86ArgListInstruction class InsnDoubleOpDir : public x86ArgListInstruction { public: InsnDoubleOpDir(DataNode* inPrimitive, Pool& inPool, x86DoubleOpDirCode inCodeType, x86ArgumentType inArgType1 = atRegDirect, x86ArgumentType inArgType2 = atRegDirect, Uint8 uses = 2, Uint8 defines = 1 ); InsnDoubleOpDir(DataNode* inPrimitive, Pool& inPool, x86DoubleOpDirCode inCodeType, Uint32 inDisplacement, x86ArgumentType inArgType1 = atRegDirect, x86ArgumentType inArgType2 = atRegDirect, Uint8 uses = 2, Uint8 defines = 1 ); // the main feature of these instructions is their ability to reverse operands virtual bool canReverseOperands() { return true; } virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& inFormatter); virtual Uint8 opcodeSize(); InstructionFlags getFlags() const; // ifCopy if we are an unspilt move, ifNone otherwise protected: bool getDirectionBit(); x86DoubleOpDirCode codeType; public: #ifdef DEBUG_LOG virtual void printOpcode(LogModuleObject &f); #endif }; //================================================================================ // Utililty // Returns a new copy instruction inline InsnDoubleOpDir& newCopyInstruction(DataNode& inDataNode, Pool& inPool, Uint8 uses = 1, Uint8 defines = 1) { return *new(inPool) InsnDoubleOpDir(&inDataNode, inPool, raCopyI, atRegDirect, atRegDirect, uses, defines); } //================================================================================ // Calls template class Call : public InsnUseXDefineYFromPool { public: static inline bool hasReturnValue(DataNode& inDataNode); static inline Uint8 numberOfArguments(DataNode& inDataNode); Call( DataNode* inDataNode, Pool& inPool, Uint8 inRegisterArguments, bool inHasReturnValue, x86Emitter& inEmitter, void (*inFunc)() = NULL, DataNode* inUseDataNode = NULL ); public: virtual void formatToMemory(void* inStart, Uint32 inOffset, MdFormatter& /*inFormatter*/); virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/) { return (5); } virtual InstructionFlags getFlags() const { return (ifCall); } protected: Uint32 mCalleeAddress; #ifdef DEBUG_LOG public: virtual void printPretty(LogModuleObject &f) { CacheEntry* ce = NativeCodeCache::getCache().lookupByRange((Uint8*)mCalleeAddress); if(ce) { Method* method = ce->descriptor.method; assert(method); const char* name = method->getName(); const char* tag = method->getHTMLName(); UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" call %s", tag, name)); } else UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" call %p", (Uint32 *)mCalleeAddress)); } #endif }; template class CallS : public Call { public: inline CallS( DataNode* inDataNode, Pool& inPool, Uint8 inRegisterArguments, bool inHasReturnValue, x86Emitter& inEmitter, void (*inFunc)(), DataNode* inUseDataNode = NULL ) : Call(inDataNode, inPool, inRegisterArguments, inHasReturnValue, inEmitter, inFunc, inUseDataNode) { } }; typedef CallS CallS_V; typedef CallS CallS_; typedef CallS CallS_C; typedef Call Call_; // Dynamically dispatched call class CallD_ : public Call { public: inline CallD_(DataNode* inDataNode, Pool& inPool, Uint8 inRegisterArguments, bool inHasReturnValue, x86Emitter& inEmitter) : Call(inDataNode, inPool, inRegisterArguments, inHasReturnValue, inEmitter) { } inline virtual void formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& inEmitter); virtual size_t getFormattedSize(MdFormatter& /*inFormatter*/) { return (2); } #ifdef DEBUG_LOG virtual void printPretty(LogModuleObject &f) { UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" call ???")); } #endif }; template bool Call:: hasReturnValue(DataNode& inDataNode) { bool hasReturnValue = (inDataNode.getOutgoingEdgesBegin() + tHasOutgoingStore < inDataNode.getOutgoingEdgesEnd()); return (hasReturnValue); } template Uint8 Call:: numberOfArguments(DataNode& inDataNode) { DataConsumer* firstArg; DataConsumer* lastArg; assert(!(tHasFunctionAddress && !tHasIncomingStore)); // no such primitive firstArg = inDataNode.getInputsBegin() + tHasFunctionAddress + tHasIncomingStore; lastArg = inDataNode.getInputsEnd(); return (lastArg - firstArg); } template void Call:: formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/) { Uint8* start = (Uint8*)inStart; int32 branchOffset = mCalleeAddress - (Uint32) inStart - 5; *start++ = 0xe8; writeLittleWordUnaligned((void*)start, branchOffset); } inline void CallD_:: formatToMemory(void* inStart, Uint32 /*inOffset*/, MdFormatter& /*inFormatter*/) { Uint8 *curPC = (Uint8 *) inStart; *curPC++ = 0xff; *curPC++ = 0xd0 | useToRegisterNumber(getInstructionUseBegin()[0]); } #endif