blob: 1636ebba35a3284d83c0c74448ab5fdd52f700fc [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Vyacheslav P. Shakin
*/
#ifndef _IA32_IRMANAGER_H_
#define _IA32_IRMANAGER_H_
#include "open/types.h"
#include "Stl.h"
#include "MemoryManager.h"
#include "Type.h"
#include "CodeGenIntfc.h"
#include "Ia32Inst.h"
#include "Ia32CFG.h"
#include "BitSet.h"
#include "XTimer.h"
#include "Log.h"
#include "PlatformDependant.h"
#include "CompilationContext.h"
#include "JITInstanceContext.h"
#include "PMFAction.h"
#include "Ia32CodeGeneratorFlags.h"
#include "Ia32CallingConvention.h"
#include "LoopTree.h"
#define STACK_INFO_KEY "stackInfo"
#define INLINE_INFO_KEY "inlineInfo"
#define BCMAP_INFO_KEY "bcMap"
#define GCMAP_INFO_KEY "gcMap"
namespace Jitrino
{
namespace Ia32
{
const char * newString(MemoryManager& mm, const char * str, U_32 length=EmptyUint32);
//========================================================================================
// STL aux classes (need to be moved somewhere)
//========================================================================================
struct ConstCharStringLess
{
bool operator()(const char * l, const char * r) const
{ return (l==0?(r==0?0:-1):r==0?1:strcmp(l,r))<0; }
};
//========================================================================================
// class IRManager
//========================================================================================
/**
class IRManager is used to create elements of the LIR, to change the LIR's structure,
and to access various information which is considered worth to be tracked LIR-wide
*/
class IRManager : protected ControlFlowGraphFactory
{
public:
ControlFlowGraph* getFlowGraph() const {return fg;}
typedef StlMap<const char *, void *, ConstCharStringLess> ConstCharStringToVoidPtrMap;
struct InternalHelperInfo
{
const void * pfn;
const CallingConvention * callingConvention;
InternalHelperInfo(const void * _pfn=NULL, const CallingConvention * _callingConvention=NULL)
:pfn(_pfn), callingConvention(_callingConvention){}
};
typedef StlMap<const char *, InternalHelperInfo, ConstCharStringLess> InternalHelperInfos;
//---------------------------------------------------------------------------------------
/** Creates IRManager */
IRManager(MemoryManager& memManager, TypeManager& tm, MethodDesc& md, CompilationInterface& compIface);
//-------------------------------------------------------------------------------------
/** Returns the type manager used for LIR */
TypeManager& getTypeManager()const{ return typeManager; }
/** Returns the MethodDesc for the method represented by this IR */
MethodDesc& getMethodDesc()const{ return methodDesc; }
MemoryManager& getMemoryManager() const {return memoryManager;}
/** Returns the CompilationInterface for this compilation session */
CompilationInterface& getCompilationInterface()const{ return compilationInterface; }
CompilationContext* getCompilationContext()const{ return compilationInterface.getCompilationContext();}
JITInstanceContext* getCurrentJITContext() const {return getCompilationContext()->getCurrentJITContext();}
ProfilingInterface* getProfilingInterface()const{ return getCurrentJITContext()->getProfilingInterface();}
CGFlags* getCGFlags() {return &flags;}
//-----------------------------------------------------------------------------------------------
void setInfo(const char * key, void * info)
{ infoMap[newInternalString(key)]=info; }
const void * getInfo(const char * key) const
{ ConstCharStringToVoidPtrMap::const_iterator it=infoMap.find(key); return it!=infoMap.end()?it->second:NULL; }
//-------------------------------------------------------------------------------------
struct AliasRelation
{
Opnd * outerOpnd;
U_32 offset;
AliasRelation(Opnd * oo =0, U_32 offs=0)
:outerOpnd(oo), offset(offs){}
};
//-------------------------------------------------------------------------------------
/** Creates a new unassigned operand (virtual register) of type ta*/
Opnd * newOpnd(Type * type);
/** Creates a new unassigned operand (virtual register) of type ta, with additional constraint c */
Opnd * newOpnd(Type * type, Constraint c);
/** Creates a new operand assigned with immediate value */
Opnd * newImmOpnd(Type * type, int64 immediate);
/** Creates a new immediate operand associated with
runtime info of the specified kind */
Opnd * newImmOpnd(Type * type, Opnd::RuntimeInfo::Kind kind, void * arg0=0, void * arg1=0, void * arg2=0, void * arg3=0);
ConstantAreaItem * newConstantAreaItem(float f);
ConstantAreaItem * newConstantAreaItem(double d);
ConstantAreaItem * newSwitchTableConstantAreaItem(U_32 numTargets);
ConstantAreaItem * newInternalStringConstantAreaItem(const char * str);
ConstantAreaItem * newBinaryConstantAreaItem(U_32 size, const void * pv);
Opnd * newFPConstantMemOpnd(float f, Opnd * baseOpnd=0, BasicBlock * bb=0);
Opnd * newFPConstantMemOpnd(double f, Opnd * baseOpnd=0, BasicBlock * bb=0);
Opnd * newInternalStringConstantImmOpnd(const char * str);
Opnd * newBinaryConstantImmOpnd(U_32 size, const void * pv);
/** Creates a new operand assigned to a physical register */
Opnd * newRegOpnd(Type * type, RegName reg);
/** Creates a new operand assigned to a memory location of kind k */
Opnd * newMemOpnd(Type * type, MemOpndKind k, Opnd * base, Opnd * index=0, Opnd * scale=0, Opnd * displacement=0, RegName segReg=RegName_Null);
/** Shortcut: Creates a new operand assigned to a memory location of kind Heap */
Opnd * newMemOpnd(Type * type, Opnd * base, Opnd * index=0, Opnd * scale=0, Opnd * displacement=0, RegName segReg=RegName_Null);
/** Shortcut: Creates a new operand assigned to a memory location of kind k */
Opnd * newMemOpnd(Type * type, MemOpndKind k, Opnd * base, I_32 displacement, RegName segReg=RegName_Null);
Opnd * newMemOpndAutoKind(Type * type, MemOpndKind k, Opnd * opnd0, Opnd * opnd1=0, Opnd * opnd2=0);
Opnd * newMemOpndAutoKind(Type * type, Opnd * opnd0, Opnd * opnd1=0, Opnd * opnd2=0)
{ return newMemOpndAutoKind(type, MemOpndKind_Heap, opnd0, opnd1, opnd2); }
//-------------------------------------------------------------------------------------
/** Creates a new Native Inst defined by mnemonic with up to 8 operands */
Inst * newInst(Mnemonic mnemonic, Opnd * opnd0=0, Opnd * opnd1=0, Opnd * opnd2=0);
Inst * newInst(Mnemonic mnemonic,
Opnd * opnd0, Opnd * opnd1, Opnd * opnd2, Opnd * opnd3,
Opnd * opnd4, Opnd * opnd5=0, Opnd * opnd6=0, Opnd * opnd7=0
);
/** Creates a new Extended Inst defined by mnemonic with up to 8 operands */
Inst * newInstEx(Mnemonic mnemonic, U_32 defCount, Opnd * opnd0=0, Opnd * opnd1=0, Opnd * opnd2=0);
Inst * newInstEx(Mnemonic mnemonic, U_32 defCount,
Opnd * opnd0, Opnd * opnd1, Opnd * opnd2, Opnd * opnd3,
Opnd * opnd4, Opnd * opnd5=0, Opnd * opnd6=0, Opnd * opnd7=0
);
/** Creates a new branch instruction
The source of the targetMemOpnd can be defined by its RuntimInfo
if the targetOpnd is null, implicit immediate operand is created and initialized to 0,
meaning that the branch is direct. Branch address will be updated during code emission
*/
BranchInst * newBranchInst(Mnemonic mnemonic, Node* trueTarget, Node* falseTarget, Opnd * targetOpnd=0);
SwitchInst * newSwitchInst(U_32 numTargets, Opnd * index);
JumpInst * newJumpInst(Opnd * targetOpnd=0);
/** Creates a CallInst instance. */
CallInst * newCallInst(Opnd * targetOpnd, const CallingConvention * cc,
U_32 numArgs, Opnd ** args, Opnd * retOpnd);
/** A specialization of the newCallInst to create a VM Helper CallInst. */
CallInst * newRuntimeHelperCallInst(VM_RT_SUPPORT helperId,
U_32 numArgs, Opnd ** args, Opnd * retOpnd);
/** A specialization of the newCallInst to create an internal helper CallInst. */
CallInst * newInternalRuntimeHelperCallInst(const char * internalHelperID, U_32 numArgs, Opnd ** args, Opnd * retOpnd);
void registerInternalHelperInfo(const char * internalHelperID, const InternalHelperInfo& info);
const InternalHelperInfo * getInternalHelperInfo(const char * internalHelperID)const
{ InternalHelperInfos::const_iterator it=internalHelperInfos.find(internalHelperID); return it!=internalHelperInfos.end()?&it->second:NULL; }
const char * newInternalString(const char * originalString)const
{ return newString(memoryManager, originalString); }
AliasPseudoInst * newAliasPseudoInst(Opnd * targetOpnd, Opnd * sourceOpnd, U_32 offset);
AliasPseudoInst * newAliasPseudoInst(Opnd * targetOpnd, U_32 sourceOpndCount, Opnd ** sourceOpnds);
CMPXCHG8BPseudoInst * newCMPXCHG8BPseudoInst(Opnd* mem, Opnd* edx, Opnd* eax, Opnd* ecx, Opnd* ebx);
CatchPseudoInst * newCatchPseudoInst(Opnd * exception);
Inst * newI8PseudoInst(Mnemonic mnemonic, U_32 defCount,
Opnd * opnd0, Opnd * opnd1=0, Opnd * opnd2=0, Opnd * opnd3=0
);
GCInfoPseudoInst* newGCInfoPseudoInst(const StlVector<Opnd*>& basesAndMptr);
SystemExceptionCheckPseudoInst* newSystemExceptionCheckPseudoInst(CompilationInterface::SystemExceptionId exceptionId, Opnd * opnd0, Opnd * opnd1=0, bool checksThisForInlinedMethod=false);
Inst * newCopyPseudoInst(Mnemonic mn, Opnd * opnd0, Opnd * opnd1=NULL);
/** Creates the ret instruction
using the primary calling convention associated with this method in IRManager's constructor
*/
RetInst * newRetInst(Opnd * retOpnd);
/** Creates an EntryPointPseudoInst pseudo instruction representing the entry point of a method.
*/
EntryPointPseudoInst * newEntryPointPseudoInst(const CallingConvention * cc);
Inst * newCopySequence(Mnemonic mn, Opnd * opnd0, Opnd * opnd1=NULL, U_32 regUsageMask=(U_32)~0, U_32 flagsRegUsageMask=(U_32)~0);
/** Creates an EmptyPseudoInst instruction to fill BB, which shoud not be cosidered as an empty one.
*/
Inst* newEmptyPseudoInst();
/** Creates an MethodEntryPseudoInst pseudo instruction representing the inlined method entry/end markers.
*/
MethodMarkerPseudoInst* newMethodEntryPseudoInst(MethodDesc* mDesc);
MethodMarkerPseudoInst* newMethodEndPseudoInst(MethodDesc* mDesc);
//---------------------------------------------------------------------------------------
void setHasCalls(){ hasCalls=true; }
bool getHasCalls()const{ return hasCalls; }
void setHasNonExceptionCalls(){ hasNonExceptionCalls=true; }
bool getHasNonExceptionCalls()const{ return hasNonExceptionCalls; }
//-----------------------------------------------------------------------------------------------
const CallingConvention * getCallingConvention(VM_RT_SUPPORT helperId)const;
const CallingConvention * getCallingConvention(MethodDesc * methodDesc)const;
const CallingConvention * getDefaultManagedCallingConvention() const { return &CallingConvention_Managed; }
EntryPointPseudoInst * getEntryPointInst()const { return entryPointInst; }
const CallingConvention * getCallingConvention()const { assert(NULL!=entryPointInst); return getEntryPointInst()->getCallingConventionClient().getCallingConvention(); }
Opnd * defArg(Type * type, U_32 position);
void applyCallingConventions();
U_32 getMaxInstId() { return instId; }
//-----------------------------------------------------------------------------------------------
/** Updates operands resolving their RuntimeInfo */
void resolveRuntimeInfo();
void resolveRuntimeInfo(Opnd* opnd) const;
/** AOT support: serializes inst into stream */
U_8* serialize(U_8* stream, Inst* inst);
/** AOT support: deserializes inst from stream */
U_8* deserialize(U_8* stream, Inst*& inst);
//-----------------------------------------------------------------------------------------------
/** calculate liveness information */
void calculateLivenessInfo();
void fixLivenessInfo( U_32 * map = NULL );
bool hasLivenessInfo()const
{
if (!_hasLivenessInfo)
return false;
BitSet * ls = ((CGNode*)fg->getEntryNode())->getLiveAtEntry();
return ls != NULL && ls->getSetSize()==getOpndCount();
}
/** calculate liveness information if needed*/
void updateLivenessInfo() { if (!hasLivenessInfo()) calculateLivenessInfo();}
void invalidateLivenessInfo(){ _hasLivenessInfo=false; }
void updateLoopInfo() const {fg->getLoopTree()->rebuild(false);}
void invalidateLoopInfo() {/*do nothing*/}
/** returns the live set for node entry */
BitSet * getLiveAtEntry(const Node * node)const
{ return ((const CGNode*)node)->getLiveAtEntry(); }
/** initializes ls with liveness info at node exit */
void getLiveAtExit(const Node * node, BitSet & ls)const;
/** use this function to update ls in a single bakrward pass through a node when liveness info is calculated */
void updateLiveness(const Inst * inst, BitSet & ls)const;
/** returns the reg usage information for node entry
as uint32containing bit mask for used registers */
U_32 getRegUsageAtEntry(const Node * node, OpndKind regKind)const
{ return getRegUsageFromLiveSet(getLiveAtEntry(node), regKind); }
/** returns the reg usage information for node exit
as U_32 containing bit mask for used registers */
void getRegUsageAtExit(const Node * node, OpndKind regKind, U_32 & mask)const;
/** use this function to update reg usage in a single postorder pass when liveness info is calculated */
void updateRegUsage(const Inst * inst, OpndKind regKind, U_32 & mask)const;
U_32 getRegUsageFromLiveSet(BitSet * ls, OpndKind regKind)const;
/** returns Constraint containing bit mask for all registers used in the method */
U_32 getTotalRegUsage(OpndKind regKind)const;
void calculateTotalRegUsage(OpndKind regKind);
static bool isGCSafePoint(const Inst* inst);
static bool isThreadInterruptablePoint(const Inst* inst) {
if ( inst->getMnemonic() == Mnemonic_CALL ) {
Opnd::RuntimeInfo* ri = inst->getOpnd(((ControlTransferInst*)inst)->getTargetOpndIndex())->getRuntimeInfo();
return ri ? ri->getKind() == Opnd::RuntimeInfo::Kind_HelperAddress : false;
} else {
return false;
}
}
//-----------------------------------------------------------------------------------------------
bool isOnlyPrologSuccessor(Node * bb) ;
/** returns true if the specified node is an epilog basic block
A node is considered epilog if it is a basic block and is connected to the exit node of the CFG
*/
bool isEpilog(const Node * node) const
{ return node->isBlockNode() && node->isConnectedTo(true, fg->getExitNode());}
/** fix edge profile
if CFG is not annotated with edge profile assigns heuristics based (loops-only)
profile to CFG.
CFG has valid profile after this method call.
*/
void fixEdgeProfile();
/** returns true if the CFG has been laid out
(layout successor attributes of nodes are valid) */
bool isLaidOut() const {return laidOut;}
void setLaidOut(bool v) {laidOut = true;}
/** returns the pointer to the emitted native code for the method. */
void * getCodeStartAddr() const {return codeStartAddr;}
void setCodeStartAddr(void* addr) {codeStartAddr = addr;}
ControlFlowGraph* createSubCFG(bool withReturn, bool withUnwind);
/** expands SystemExceptionCheckPseudoInst */
void expandSystemExceptions(U_32 reservedForFlags);
/** generater code to throw noted type exception, set for code BC offset and include into basic block*/
void throwException(ObjectType* excType, uint16 bcOffset, Node* basicBlock);
/** changes all Extended insts to Native form by calling makeInstNative */
void translateToNativeForm();
void eliminateSameOpndMoves();
//-----------------------------------------------------------------------------------------------
/**this function performs the following things in a single postorder pass:
1) indexes instruction in topological (reverse post-order)
2) (re-)calculates operand statistics (ref counts)
3) If reindex is true it sets new sequential Opnd::id
for all operands with refcount>0(used for packOpnds)*/
U_32 calculateOpndStatistics(bool reindex=false);
/** this function performs the following:
1) calls calculateOpndStatistics().
2) removes operands with zero ref counts from the internal
array of all operands accessed via IRManager::getOpnd(U_32).
*/
void packOpnds();
/** returns the number of operands in the internal
array of all operands accessed via IRManager::getOpnd(U_32).
This number is affected by packOpnds which removes from the array all operands which
are no longer in the LIR.
*/
U_32 getOpndCount()const
{ return (U_32)opnds.size(); }
/** returns an operand from the internal array of all operands by its ID. */
Opnd * getOpnd(U_32 id)const
{
assert(id<opnds.size()); return opnds[id];
}
/** sets the the Calculateable opnd constraints to Initial ones */
void resetOpndConstraints();
//-----------------------------------------------------------------------------------------------
U_32 assignInnerMemOpnd(Opnd * outerOpnd, Opnd* innerOpnds, U_32 offset);
void assignInnerMemOpnds(Opnd * outerOpnd, Opnd** innerOpnds, U_32 innerOpndCount);
void layoutAliasPseudoInstOpnds(AliasPseudoInst * inst);
void getAliasRelations(AliasRelation * relations);
void layoutAliasOpnds();
U_32 getLayoutOpndAlignment(Opnd * opnd);
void finalizeCallSites();
/** Calculates displacement from stack entry point
for every instruction and returns maximum stack depth needed for a method
*/
U_32 calculateStackDepth();
//-----------------------------------------------------------------------------------------------
bool verify();
bool verifyOpnds() const;
bool verifyLiveness();
bool verifyHeapAddressTypes();
//-----------------------------------------------------------------------------------------------
/** Assigns all instructions in the CFG sequential indexes ordering them in topological order
*/
void indexInsts();
//-----------------------------------------------------------------------------------------------
/** space optimization: return a pre-existent operand representing physical register regName */
Opnd * getRegOpnd(RegName regName);
bool isPreallocatedRegOpnd(Opnd * opnd);
//-----------------------------------------------------------------------------------------------
/** returns the initial constraint for the given type */
Constraint getInitialConstraint(Type * type)const
{ return initialConstraints[type->tag]; }
/** converts Type::Tag into pointer to Type instance */
Type * getTypeFromTag(Type::Tag)const;
Type * getManagedPtrType(Type * sourceType);
/** returns the size of location occupied by an operand of type type */
static OpndSize getTypeSize(Type::Tag tag);
static OpndSize getTypeSize(Type * type){ return getTypeSize(type->tag); }
/** checks loop info, returns FALSE if loop info is not valid,
should be used in debug assert checks*/
bool ensureLivenessInfoIsValid();
/** returns true if regs of kind regKind cannot be assigned anymore to operands
This means reg allocations is done and the new assignment can conflict with existing ones
*/
bool isRegisterSetLocked(OpndKind regKind){ return true; }
void lockRegisterSet(OpndKind regKind){ }
void setVerificationLevel(U_32 v){ verificationLevel=v; }
U_32 getVerificationLevel()const{ return verificationLevel; }
bool refsAreCompressed() {return refsCompressed;}
protected:
//control flow graph factory methods
virtual Node* createNode(MemoryManager& mm, Node::Kind kind);
virtual Edge* createEdge(MemoryManager& mm, Node::Kind srcKind, Node::Kind dstKind);
void addOpnd(Opnd * opnd);
void addAliasRelation(AliasRelation * relations, Opnd * opndOuter, Opnd * opndInner, U_32 offset);
Inst * newCopySequence(Opnd * targetOpnd, Opnd * sourceOpnd, U_32 regUsageMask=(U_32)~0, U_32 flagsRegUsageMask=(U_32)~0);
Inst * newPushPopSequence(Mnemonic mn, Opnd * opnd, U_32 regUsageMask=(U_32)~0);
Inst * newMemMovSequence(Opnd * targetOpnd, Opnd * sourceOpnd, U_32 regUsageMask, bool checkSource=false);
void initInitialConstraints();
Constraint createInitialConstraint(Type::Tag t)const;
//-------------------------------------------------------------------------------------
MemoryManager& memoryManager;
ControlFlowGraph* fg;
TypeManager & typeManager;
MethodDesc & methodDesc;
CompilationInterface & compilationInterface;
U_32 opndId;
U_32 instId;
OpndVector opnds;
U_32 gpTotalRegUsage;
EntryPointPseudoInst * entryPointInst;
Opnd * regOpnds[IRMaxRegNames];
bool _hasLivenessInfo;
InternalHelperInfos internalHelperInfos;
ConstCharStringToVoidPtrMap infoMap;
U_32 verificationLevel;
bool hasCalls;
bool hasNonExceptionCalls;
Constraint initialConstraints[Type::NumTypeTags];
bool laidOut;
void * codeStartAddr;
CGFlags flags;
bool refsCompressed;
};
#define VERIFY_OUT(s) { if (Log::isEnabled()) Log::out() << s; std::cerr << s; }
//========================================================================================
// class Ia32::SessionAction
//========================================================================================
/**
class Ia32::SessionAction is the base class for all IR transformations external to IRManager:
optimizations, etc.
*/
class SessionAction : public ::Jitrino::SessionAction
{
public:
enum SideEffect
{
SideEffect_InvalidatesLivenessInfo=0x1,
SideEffect_InvalidatesLoopInfo=0x2,
};
enum NeedInfo
{
NeedInfo_LivenessInfo=0x1,
NeedInfo_LoopInfo=0x2,
};
SessionAction () :irManager(0) {}
void run ();
IRManager& getIRManager() const {return *getCompilationContext()->getLIRManager();}
const char * getTagName() const {return getName();}
protected:
/** Implementors override run() to perform all operations of the pass
Required */
virtual void runImpl () = 0;
/** Implementors override getNeedInfo() to indicate what info
must be up to date for the pass.
The returned value is |-ed from the NeedInfo enum
Optional, defaults to all possible info */
virtual U_32 getNeedInfo()const;
/** Implementors override getSideEffects() to indicate the side effect
of the pass (currently in terms of common info like liveness, loop, etc.)
The returned value is |-ed from the SideEffect enum
Optional, defaults to all possible side effects */
virtual U_32 getSideEffects()const;
/**
* Forces dominator tree to be valid
*/
void computeDominators(void);
virtual bool verify(bool force=false);
virtual void debugOutput(const char * subKind);
virtual void dumpIR(const char * subKind1, const char * subKind2=0);
virtual void printDot(const char * subKind1, const char * subKind2=0);
virtual bool isIRDumpEnabled(){ return true; }
int getVerificationLevel() const
{
return getIntArg("verify", irManager->getVerificationLevel());
}
IRManager * irManager;
U_32 stageId;
};
}}; // namespace Ia32
#endif