Commit ad403539 by Jim Stichnoth

Subzero: Automatically infer regalloc preferences and overlap.

Originally, for a given Variable, register preference and overlap were manually specified. That is, when choosing a free register for a Variable, it would be manually specified which (if any) related Variable would be a good choice for register selection, all things being equal. Also, it allowed the rather dangerous "AllowOverlap" specification which let the Variable use its preferred Variable's register, even if their live ranges overlap. Now, all this selection is automatic, and the machinery for manual specification is removed. A few other changes in this CL: - Address mode inference leverages the more precise - Better regalloc dump messages to follow the logic - "-verbose most" enables all verbose options except regalloc and time - "-ias" is an alias for "-integrated-as" - Bug fix: prevent 8-bit register ah from being used in register allocation, unless it is pre-colored - Bug fix: the _mov helper where Dest is NULL wasn't always actually creating a new Variable - A few tests are updated based on slightly different O2 register allocation decisions The static stats actually improve slightly across the board (around 1%), except that frame size improves by 6-10%. This is probably from smarter register allocation decisions, particularly involving phi lowering temporaries, where the manual hints weren't too good to start with. BUG= none R=jvoung@chromium.org Review URL: https://codereview.chromium.org/597003004
parent 2a5324a1
...@@ -377,13 +377,6 @@ void Cfg::dump(const IceString &Message) { ...@@ -377,13 +377,6 @@ void Cfg::dump(const IceString &Message) {
Str << "?"; Str << "?";
Str << " weight=" << Var->getWeight() << " "; Str << " weight=" << Var->getWeight() << " ";
Var->dump(this); Var->dump(this);
if (Variable *Pref = Var->getPreferredRegister()) {
Str << " pref=";
Pref->dump(this);
if (Var->getRegisterOverlap())
Str << ",overlap";
Str << " ";
}
Str << " LIVE=" << Var->getLiveRange() << "\n"; Str << " LIVE=" << Var->getLiveRange() << "\n";
} }
} }
......
...@@ -200,13 +200,6 @@ void CfgNode::placePhiStores() { ...@@ -200,13 +200,6 @@ void CfgNode::placePhiStores() {
Variable *Dest = (*I2)->getDest(); Variable *Dest = (*I2)->getDest();
assert(Dest); assert(Dest);
InstAssign *NewInst = InstAssign::create(Func, Dest, Operand); InstAssign *NewInst = InstAssign::create(Func, Dest, Operand);
// If Src is a variable, set the Src and Dest variables to
// prefer each other for register allocation.
if (Variable *Src = llvm::dyn_cast<Variable>(Operand)) {
bool AllowOverlap = false;
Dest->setPreferredRegister(Src, AllowOverlap);
Src->setPreferredRegister(Dest, AllowOverlap);
}
if (CmpInstDest == Operand) if (CmpInstDest == Operand)
Insts.insert(SafeInsertionPoint, NewInst); Insts.insert(SafeInsertionPoint, NewInst);
else else
......
...@@ -105,7 +105,8 @@ enum VerboseItem { ...@@ -105,7 +105,8 @@ enum VerboseItem {
IceV_Frame = 1 << 9, IceV_Frame = 1 << 9,
IceV_Timing = 1 << 10, IceV_Timing = 1 << 10,
IceV_AddrOpt = 1 << 11, IceV_AddrOpt = 1 << 11,
IceV_All = ~IceV_None IceV_All = ~IceV_None,
IceV_Most = IceV_All & ~(IceV_Timing | IceV_LinearScan)
}; };
typedef uint32_t VerboseMask; typedef uint32_t VerboseMask;
......
...@@ -346,12 +346,7 @@ Inst *InstPhi::lower(Cfg *Func) { ...@@ -346,12 +346,7 @@ Inst *InstPhi::lower(Cfg *Func) {
IceString PhiName = Dest->getName() + "_phi"; IceString PhiName = Dest->getName() + "_phi";
Variable *NewSrc = Func->makeVariable(Dest->getType(), PhiName); Variable *NewSrc = Func->makeVariable(Dest->getType(), PhiName);
this->Dest = NewSrc; this->Dest = NewSrc;
InstAssign *NewInst = InstAssign::create(Func, Dest, NewSrc); return InstAssign::create(Func, Dest, NewSrc);
// Set Dest and NewSrc to have affinity with each other, as a hint
// for register allocation.
Dest->setPreferredRegister(NewSrc, false);
NewSrc->setPreferredRegister(Dest, false);
return NewInst;
} }
InstRet::InstRet(Cfg *Func, Operand *RetValue) InstRet::InstRet(Cfg *Func, Operand *RetValue)
......
...@@ -92,6 +92,8 @@ public: ...@@ -92,6 +92,8 @@ public:
return NodeList(); return NodeList();
} }
virtual bool isSimpleAssign() const { return false; }
void livenessLightweight(Cfg *Func, llvm::BitVector &Live); void livenessLightweight(Cfg *Func, llvm::BitVector &Live);
void liveness(InstNumberT InstNumber, llvm::BitVector &Live, void liveness(InstNumberT InstNumber, llvm::BitVector &Live,
Liveness *Liveness, const CfgNode *Node); Liveness *Liveness, const CfgNode *Node);
...@@ -233,6 +235,7 @@ public: ...@@ -233,6 +235,7 @@ public:
return new (Func->allocateInst<InstAssign>()) return new (Func->allocateInst<InstAssign>())
InstAssign(Func, Dest, Source); InstAssign(Func, Dest, Source);
} }
virtual bool isSimpleAssign() const { return true; }
virtual void dump(const Cfg *Func) const; virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Assign; } static bool classof(const Inst *Inst) { return Inst->getKind() == Assign; }
......
...@@ -44,11 +44,16 @@ ...@@ -44,11 +44,16 @@
// all of the registers are considered and have distinct numberings. // all of the registers are considered and have distinct numberings.
// This is in contrast to the above, where the "encode" is based on how // This is in contrast to the above, where the "encode" is based on how
// the register numbers will be encoded in binaries and values can overlap. // the register numbers will be encoded in binaries and values can overlap.
// Note that the isI8 attributed of Reg_ah is not set. In general we
// don't want the register allocator choosing Reg_ah, in particular
// for lowering insertelement to pinsrb where internally we use an
// 8-bit operand but externally pinsrb uses a 32-bit register, in
// which Reg_ah doesn't map to eax.
#define REGX8632_TABLE \ #define REGX8632_TABLE \
/* val, encode, name, name16, name8, scratch, preserved, stackptr, \ /* val, encode, name, name16, name8, scratch, preserved, stackptr, \
frameptr, isI8, isInt, isFP */ \ frameptr, isI8, isInt, isFP */ \
REGX8632_GPR_TABLE \ REGX8632_GPR_TABLE \
X(Reg_ah, = Reg_eax + 4, "???", "" , "ah", 0, 0, 0, 0, 1, 0, 0) \ X(Reg_ah, = Reg_eax + 4, "???", "" , "ah", 0, 0, 0, 0, 0, 0, 0) \
REGX8632_XMM_TABLE REGX8632_XMM_TABLE
//#define X(val, encode, name, name16, name8, scratch, preserved, stackptr, //#define X(val, encode, name, name16, name8, scratch, preserved, stackptr,
// frameptr, isI8, isInt, isFP) // frameptr, isI8, isInt, isFP)
......
...@@ -744,6 +744,7 @@ public: ...@@ -744,6 +744,7 @@ public:
virtual bool isRedundantAssign() const { virtual bool isRedundantAssign() const {
return checkForRedundantAssign(getDest(), getSrc(0)); return checkForRedundantAssign(getDest(), getSrc(0));
} }
virtual bool isSimpleAssign() const { return true; }
virtual void emit(const Cfg *Func) const; virtual void emit(const Cfg *Func) const;
virtual void dump(const Cfg *Func) const { virtual void dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
...@@ -1199,7 +1200,7 @@ private: ...@@ -1199,7 +1200,7 @@ private:
virtual ~InstX8632Movsx() {} virtual ~InstX8632Movsx() {}
}; };
// Movsx - copy from a narrower integer type to a wider integer // Movzx - copy from a narrower integer type to a wider integer
// type, with zero extension. // type, with zero extension.
class InstX8632Movzx : public InstX8632 { class InstX8632Movzx : public InstX8632 {
public: public:
......
...@@ -165,13 +165,13 @@ void VariableTracking::markUse(const Inst *Instr, const CfgNode *Node, ...@@ -165,13 +165,13 @@ void VariableTracking::markUse(const Inst *Instr, const CfgNode *Node,
// of the block that actually uses a Variable. // of the block that actually uses a Variable.
assert(Node); assert(Node);
bool MakeMulti = false; bool MakeMulti = false;
if (IsImplicit)
MakeMulti = true;
// A phi source variable conservatively needs to be marked as // A phi source variable conservatively needs to be marked as
// multi-block, even if its definition is in the same block. This // multi-block, even if its definition is in the same block. This
// is because there can be additional control flow before branching // is because there can be additional control flow before branching
// back to this node, and the variable is live throughout those // back to this node, and the variable is live throughout those
// nodes. // nodes.
if (IsImplicit)
MakeMulti = true;
if (!IsFromDef && Instr && llvm::isa<InstPhi>(Instr)) if (!IsFromDef && Instr && llvm::isa<InstPhi>(Instr))
MakeMulti = true; MakeMulti = true;
...@@ -201,23 +201,63 @@ void VariableTracking::markDef(const Inst *Instr, const CfgNode *Node) { ...@@ -201,23 +201,63 @@ void VariableTracking::markDef(const Inst *Instr, const CfgNode *Node) {
// of the block, consider not marking this as a separate use. But // of the block, consider not marking this as a separate use. But
// be careful not to omit all uses of the variable if markDef() and // be careful not to omit all uses of the variable if markDef() and
// markUse() both use this optimization. // markUse() both use this optimization.
assert(Node);
Definitions.push_back(Instr);
const bool IsFromDef = true; const bool IsFromDef = true;
const bool IsImplicit = false; const bool IsImplicit = false;
markUse(Instr, Node, IsFromDef, IsImplicit); markUse(Instr, Node, IsFromDef, IsImplicit);
switch (MultiDef) { switch (MultiDef) {
case MDS_Unknown: case MDS_Unknown:
assert(SingleDefNode == NULL);
MultiDef = MDS_SingleDef; MultiDef = MDS_SingleDef;
SingleDefInst = Instr; SingleDefNode = Node;
break; break;
case MDS_SingleDef: case MDS_SingleDef:
MultiDef = MDS_MultiDef; assert(SingleDefNode);
SingleDefInst = NULL; if (Node == SingleDefNode) {
MultiDef = MDS_MultiDefSingleBlock;
} else {
MultiDef = MDS_MultiDefMultiBlock;
SingleDefNode = NULL;
}
break;
case MDS_MultiDefSingleBlock:
assert(SingleDefNode);
if (Node != SingleDefNode) {
MultiDef = MDS_MultiDefMultiBlock;
SingleDefNode = NULL;
}
break; break;
case MDS_MultiDef: case MDS_MultiDefMultiBlock:
assert(SingleDefNode == NULL);
break; break;
} }
} }
const Inst *VariableTracking::getFirstDefinition() const {
switch (MultiDef) {
case MDS_Unknown:
case MDS_MultiDefMultiBlock:
return NULL;
case MDS_SingleDef:
case MDS_MultiDefSingleBlock:
assert(!Definitions.empty());
return Definitions[0];
}
}
const Inst *VariableTracking::getSingleDefinition() const {
switch (MultiDef) {
case MDS_Unknown:
case MDS_MultiDefMultiBlock:
case MDS_MultiDefSingleBlock:
return NULL;
case MDS_SingleDef:
assert(!Definitions.empty());
return Definitions[0];
}
}
void VariablesMetadata::init() { void VariablesMetadata::init() {
Metadata.clear(); Metadata.clear();
Metadata.resize(Func->getNumVariables()); Metadata.resize(Func->getNumVariables());
...@@ -243,6 +283,18 @@ void VariablesMetadata::init() { ...@@ -243,6 +283,18 @@ void VariablesMetadata::init() {
++I) { ++I) {
if ((*I)->isDeleted()) if ((*I)->isDeleted())
continue; continue;
if (InstFakeKill *Kill = llvm::dyn_cast<InstFakeKill>(*I)) {
// A FakeKill instruction indicates certain Variables (usually
// physical scratch registers) are redefined, so we register
// them as defs.
for (SizeT SrcNum = 0; SrcNum < (*I)->getSrcSize(); ++SrcNum) {
Variable *Var = llvm::cast<Variable>((*I)->getSrc(SrcNum));
SizeT VarNum = Var->getIndex();
assert(VarNum < Metadata.size());
Metadata[VarNum].markDef(Kill, Node);
}
continue; // no point in executing the rest
}
if (Variable *Dest = (*I)->getDest()) { if (Variable *Dest = (*I)->getDest()) {
SizeT DestNum = Dest->getIndex(); SizeT DestNum = Dest->getIndex();
assert(DestNum < Metadata.size()); assert(DestNum < Metadata.size());
...@@ -275,18 +327,35 @@ bool VariablesMetadata::isMultiDef(const Variable *Var) const { ...@@ -275,18 +327,35 @@ bool VariablesMetadata::isMultiDef(const Variable *Var) const {
} }
bool VariablesMetadata::isMultiBlock(const Variable *Var) const { bool VariablesMetadata::isMultiBlock(const Variable *Var) const {
if (getDefinition(Var) == NULL) if (Var->getIsArg())
return true; return true;
if (!isTracked(Var))
return true; // conservative answer
SizeT VarNum = Var->getIndex(); SizeT VarNum = Var->getIndex();
// Conservatively return true if the state is unknown. // Conservatively return true if the state is unknown.
return Metadata[VarNum].getMultiBlock() != VariableTracking::MBS_SingleBlock; return Metadata[VarNum].getMultiBlock() != VariableTracking::MBS_SingleBlock;
} }
const Inst *VariablesMetadata::getDefinition(const Variable *Var) const { const Inst *VariablesMetadata::getFirstDefinition(const Variable *Var) const {
if (!isTracked(Var))
return NULL; // conservative answer
SizeT VarNum = Var->getIndex();
return Metadata[VarNum].getFirstDefinition();
}
const Inst *VariablesMetadata::getSingleDefinition(const Variable *Var) const {
if (!isTracked(Var)) if (!isTracked(Var))
return NULL; // conservative answer return NULL; // conservative answer
SizeT VarNum = Var->getIndex(); SizeT VarNum = Var->getIndex();
return Metadata[VarNum].getDefinition(); return Metadata[VarNum].getSingleDefinition();
}
const InstDefList &
VariablesMetadata::getDefinitions(const Variable *Var) const {
if (!isTracked(Var))
return NoDefinitions;
SizeT VarNum = Var->getIndex();
return Metadata[VarNum].getDefinitions();
} }
const CfgNode *VariablesMetadata::getLocalUseNode(const Variable *Var) const { const CfgNode *VariablesMetadata::getLocalUseNode(const Variable *Var) const {
...@@ -296,6 +365,8 @@ const CfgNode *VariablesMetadata::getLocalUseNode(const Variable *Var) const { ...@@ -296,6 +365,8 @@ const CfgNode *VariablesMetadata::getLocalUseNode(const Variable *Var) const {
return Metadata[VarNum].getNode(); return Metadata[VarNum].getNode();
} }
const InstDefList VariablesMetadata::NoDefinitions;
// ======================== dump routines ======================== // // ======================== dump routines ======================== //
void Variable::emit(const Cfg *Func) const { void Variable::emit(const Cfg *Func) const {
......
...@@ -385,13 +385,6 @@ public: ...@@ -385,13 +385,6 @@ public:
void setWeight(uint32_t NewWeight) { Weight = NewWeight; } void setWeight(uint32_t NewWeight) { Weight = NewWeight; }
void setWeightInfinite() { Weight = RegWeight::Inf; } void setWeightInfinite() { Weight = RegWeight::Inf; }
Variable *getPreferredRegister() const { return RegisterPreference; }
bool getRegisterOverlap() const { return AllowRegisterOverlap; }
void setPreferredRegister(Variable *Prefer, bool Overlap) {
RegisterPreference = Prefer;
AllowRegisterOverlap = Overlap;
}
const LiveRange &getLiveRange() const { return Live; } const LiveRange &getLiveRange() const { return Live; }
void setLiveRange(const LiveRange &Range) { Live = Range; } void setLiveRange(const LiveRange &Range) { Live = Range; }
void resetLiveRange() { Live.reset(); } void resetLiveRange() { Live.reset(); }
...@@ -416,8 +409,8 @@ public: ...@@ -416,8 +409,8 @@ public:
// Creates a temporary copy of the variable with a different type. // Creates a temporary copy of the variable with a different type.
// Used primarily for syntactic correctness of textual assembly // Used primarily for syntactic correctness of textual assembly
// emission. Note that only basic information is copied, in // emission. Note that only basic information is copied, in
// particular not DefInst, IsArgument, Weight, RegisterPreference, // particular not DefInst, IsArgument, Weight, LoVar, HiVar,
// AllowRegisterOverlap, LoVar, HiVar, VarsReal. // VarsReal.
Variable asType(Type Ty); Variable asType(Type Ty);
virtual void emit(const Cfg *Func) const; virtual void emit(const Cfg *Func) const;
...@@ -436,8 +429,7 @@ protected: ...@@ -436,8 +429,7 @@ protected:
Variable(OperandKind K, Type Ty, SizeT Index, const IceString &Name) Variable(OperandKind K, Type Ty, SizeT Index, const IceString &Name)
: Operand(K, Ty), Number(Index), Name(Name), IsArgument(false), : Operand(K, Ty), Number(Index), Name(Name), IsArgument(false),
IsImplicitArgument(false), StackOffset(0), RegNum(NoRegister), IsImplicitArgument(false), StackOffset(0), RegNum(NoRegister),
RegNumTmp(NoRegister), Weight(1), RegisterPreference(NULL), RegNumTmp(NoRegister), Weight(1), LoVar(NULL), HiVar(NULL) {
AllowRegisterOverlap(false), LoVar(NULL), HiVar(NULL) {
Vars = VarsReal; Vars = VarsReal;
Vars[0] = this; Vars[0] = this;
NumVars = 1; NumVars = 1;
...@@ -450,7 +442,7 @@ protected: ...@@ -450,7 +442,7 @@ protected:
bool IsArgument; bool IsArgument;
bool IsImplicitArgument; bool IsImplicitArgument;
// StackOffset is the canonical location on stack (only if // StackOffset is the canonical location on stack (only if
// RegNum<0 || IsArgument). // RegNum==NoRegister || IsArgument).
int32_t StackOffset; int32_t StackOffset;
// RegNum is the allocated register, or NoRegister if it isn't // RegNum is the allocated register, or NoRegister if it isn't
// register-allocated. // register-allocated.
...@@ -458,16 +450,6 @@ protected: ...@@ -458,16 +450,6 @@ protected:
// RegNumTmp is the tentative assignment during register allocation. // RegNumTmp is the tentative assignment during register allocation.
int32_t RegNumTmp; int32_t RegNumTmp;
RegWeight Weight; // Register allocation priority RegWeight Weight; // Register allocation priority
// RegisterPreference says that if possible, the register allocator
// should prefer the register that was assigned to this linked
// variable. It also allows a spill slot to share its stack
// location with another variable, if that variable does not get
// register-allocated and therefore has a stack location.
Variable *RegisterPreference;
// AllowRegisterOverlap says that it is OK to honor
// RegisterPreference and "share" a register even if the two live
// ranges overlap.
bool AllowRegisterOverlap;
LiveRange Live; LiveRange Live;
// LoVar and HiVar are needed for lowering from 64 to 32 bits. When // LoVar and HiVar are needed for lowering from 64 to 32 bits. When
// lowering from I64 to I32 on a 32-bit architecture, we split the // lowering from I64 to I32 on a 32-bit architecture, we split the
...@@ -483,14 +465,18 @@ protected: ...@@ -483,14 +465,18 @@ protected:
Variable *VarsReal[1]; Variable *VarsReal[1];
}; };
// VariableTracking tracks the metadata for a single variable. typedef std::vector<const Inst *> InstDefList;
// VariableTracking tracks the metadata for a single variable. It is
// only meant to be used internally by VariablesMetadata.
class VariableTracking { class VariableTracking {
public: public:
enum MultiDefState { enum MultiDefState {
// TODO(stichnot): Consider using just a simple counter. // TODO(stichnot): Consider using just a simple counter.
MDS_Unknown, MDS_Unknown,
MDS_SingleDef, MDS_SingleDef,
MDS_MultiDef MDS_MultiDefSingleBlock,
MDS_MultiDefMultiBlock
}; };
enum MultiBlockState { enum MultiBlockState {
MBS_Unknown, MBS_Unknown,
...@@ -499,10 +485,12 @@ public: ...@@ -499,10 +485,12 @@ public:
}; };
VariableTracking() VariableTracking()
: MultiDef(MDS_Unknown), MultiBlock(MBS_Unknown), SingleUseNode(NULL), : MultiDef(MDS_Unknown), MultiBlock(MBS_Unknown), SingleUseNode(NULL),
SingleDefInst(NULL) {} SingleDefNode(NULL) {}
MultiDefState getMultiDef() const { return MultiDef; } MultiDefState getMultiDef() const { return MultiDef; }
MultiBlockState getMultiBlock() const { return MultiBlock; } MultiBlockState getMultiBlock() const { return MultiBlock; }
const Inst *getDefinition() const { return SingleDefInst; } const Inst *getFirstDefinition() const;
const Inst *getSingleDefinition() const;
const InstDefList &getDefinitions() const { return Definitions; }
const CfgNode *getNode() const { return SingleUseNode; } const CfgNode *getNode() const { return SingleUseNode; }
void markUse(const Inst *Instr, const CfgNode *Node, bool IsFromDef, void markUse(const Inst *Instr, const CfgNode *Node, bool IsFromDef,
bool IsImplicit); bool IsImplicit);
...@@ -513,7 +501,12 @@ private: ...@@ -513,7 +501,12 @@ private:
MultiDefState MultiDef; MultiDefState MultiDef;
MultiBlockState MultiBlock; MultiBlockState MultiBlock;
const CfgNode *SingleUseNode; const CfgNode *SingleUseNode;
const Inst *SingleDefInst; const CfgNode *SingleDefNode;
// All definitions of the variable are collected here, in the order
// encountered. Definitions in the same basic block are in
// instruction order, but there's no guarantee for the basic block
// order.
InstDefList Definitions;
}; };
// VariablesMetadata analyzes and summarizes the metadata for the // VariablesMetadata analyzes and summarizes the metadata for the
...@@ -521,18 +514,50 @@ private: ...@@ -521,18 +514,50 @@ private:
class VariablesMetadata { class VariablesMetadata {
public: public:
VariablesMetadata(const Cfg *Func) : Func(Func) {} VariablesMetadata(const Cfg *Func) : Func(Func) {}
// Initialize the state by traversing all instructions/variables in
// the CFG.
void init(); void init();
// Returns whether the given Variable is tracked in this object. It
// should only return false if changes were made to the CFG after
// running init(), in which case the state is stale and the results
// shouldn't be trusted (but it may be OK e.g. for dumping).
bool isTracked(const Variable *Var) const { bool isTracked(const Variable *Var) const {
return Var->getIndex() < Metadata.size(); return Var->getIndex() < Metadata.size();
} }
// Returns whether the given Variable has multiple definitions.
bool isMultiDef(const Variable *Var) const; bool isMultiDef(const Variable *Var) const;
const Inst *getDefinition(const Variable *Var) const; // Returns the first definition instruction of the given Variable.
// This is only valid for variables whose definitions are all within
// the same block, e.g. T after the lowered sequence "T=B; T+=C;
// A=T", for which getFirstDefinition(T) would return the "T=B"
// instruction. For variables with definitions span multiple
// blocks, NULL is returned.
const Inst *getFirstDefinition(const Variable *Var) const;
// Returns the definition instruction of the given Variable, when
// the variable has exactly one definition. Otherwise, NULL is
// returned.
const Inst *getSingleDefinition(const Variable *Var) const;
// Returns the list of all definition instructions of the given
// Variable.
const InstDefList &getDefinitions(const Variable *Var) const;
// Returns whether the given Variable is live across multiple
// blocks. Mainly, this is used to partition Variables into
// single-block versus multi-block sets for leveraging sparsity in
// liveness analysis, and for implementing simple stack slot
// coalescing. As a special case, function arguments are always
// considered multi-block because they are live coming into the
// entry block.
bool isMultiBlock(const Variable *Var) const; bool isMultiBlock(const Variable *Var) const;
// Returns the node that the given Variable is used in, assuming
// isMultiBlock() returns false. Otherwise, NULL is returned.
const CfgNode *getLocalUseNode(const Variable *Var) const; const CfgNode *getLocalUseNode(const Variable *Var) const;
private: private:
const Cfg *Func; const Cfg *Func;
std::vector<VariableTracking> Metadata; std::vector<VariableTracking> Metadata;
const static InstDefList NoDefinitions;
VariablesMetadata(const VariablesMetadata &) LLVM_DELETED_FUNCTION; VariablesMetadata(const VariablesMetadata &) LLVM_DELETED_FUNCTION;
VariablesMetadata &operator=(const VariablesMetadata &) LLVM_DELETED_FUNCTION; VariablesMetadata &operator=(const VariablesMetadata &) LLVM_DELETED_FUNCTION;
}; };
......
...@@ -21,6 +21,37 @@ ...@@ -21,6 +21,37 @@
namespace Ice { namespace Ice {
namespace {
// Returns true if Var has any definitions within Item's live range.
bool overlapsDefs(const Cfg *Func, const LiveRangeWrapper &Item,
const Variable *Var) {
const InstDefList &Defs = Func->getVMetadata()->getDefinitions(Var);
for (size_t i = 0; i < Defs.size(); ++i) {
if (Item.range().overlaps(Defs[i]->getNumber()))
return true;
}
return false;
}
void dumpDisableOverlap(const Cfg *Func, const Variable *Var,
const char *Reason) {
if (Func->getContext()->isVerbose(IceV_LinearScan)) {
Ostream &Str = Func->getContext()->getStrDump();
Str << "Disabling Overlap due to " << Reason << " " << *Var
<< " LIVE=" << Var->getLiveRange() << " Defs=";
const InstDefList &Defs = Func->getVMetadata()->getDefinitions(Var);
for (size_t i = 0; i < Defs.size(); ++i) {
if (i > 0)
Str << ",";
Str << Defs[i]->getNumber();
}
Str << "\n";
}
}
} // end of anonymous namespace
// Implements the linear-scan algorithm. Based on "Linear Scan // Implements the linear-scan algorithm. Based on "Linear Scan
// Register Allocation in the Context of SSA Form and Register // Register Allocation in the Context of SSA Form and Register
// Constraints" by Hanspeter Mössenböck and Michael Pfeiffer, // Constraints" by Hanspeter Mössenböck and Michael Pfeiffer,
...@@ -40,6 +71,7 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) { ...@@ -40,6 +71,7 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) {
Active.clear(); Active.clear();
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
Func->resetCurrentNode(); Func->resetCurrentNode();
VariablesMetadata *VMetadata = Func->getVMetadata();
// Gather the live ranges of all variables and add them to the // Gather the live ranges of all variables and add them to the
// Unhandled set. TODO: Unhandled is a set<> which is based on a // Unhandled set. TODO: Unhandled is a set<> which is based on a
...@@ -185,6 +217,58 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) { ...@@ -185,6 +217,58 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) {
Free[i] = false; Free[i] = false;
} }
// Infer register preference and allowable overlap. Only form a
// preference when the current Variable has an unambiguous "first"
// definition. The preference is some source Variable of the
// defining instruction that either is assigned a register that is
// currently free, or that is assigned a register that is not free
// but overlap is allowed. Overlap is allowed when the Variable
// under consideration is single-definition, and its definition is
// a simple assignment - i.e., the register gets copied/aliased
// but is never modified. Furthermore, overlap is only allowed
// when preferred Variable definition instructions do not appear
// within the current Variable's live range.
Variable *Prefer = NULL;
int32_t PreferReg = Variable::NoRegister;
bool AllowOverlap = false;
if (const Inst *DefInst = VMetadata->getFirstDefinition(Cur.Var)) {
assert(DefInst->getDest() == Cur.Var);
bool IsAssign = DefInst->isSimpleAssign();
bool IsSingleDef = !VMetadata->isMultiDef(Cur.Var);
for (SizeT i = 0; i < DefInst->getSrcSize(); ++i) {
// TODO(stichnot): Iterate through the actual Variables of the
// instruction, not just the source operands. This could
// capture Load instructions, including address mode
// optimization, for Prefer (but not for AllowOverlap).
if (Variable *SrcVar = llvm::dyn_cast<Variable>(DefInst->getSrc(i))) {
int32_t SrcReg = SrcVar->getRegNumTmp();
// Only consider source variables that have (so far) been
// assigned a register. That register must be one in the
// RegMask set, e.g. don't try to prefer the stack pointer
// as a result of the stacksave intrinsic.
if (SrcVar->hasRegTmp() && RegMask[SrcReg]) {
if (!Free[SrcReg]) {
// Don't bother trying to enable AllowOverlap if the
// register is already free.
AllowOverlap =
IsSingleDef && IsAssign && !overlapsDefs(Func, Cur, SrcVar);
}
if (AllowOverlap || Free[SrcReg]) {
Prefer = SrcVar;
PreferReg = SrcReg;
}
}
}
}
}
if (Func->getContext()->isVerbose(IceV_LinearScan)) {
if (Prefer) {
Str << "Initial Prefer=" << *Prefer << " R=" << PreferReg
<< " LIVE=" << Prefer->getLiveRange() << " Overlap=" << AllowOverlap
<< "\n";
}
}
// Remove registers from the Free[] list where an Inactive range // Remove registers from the Free[] list where an Inactive range
// overlaps with the current range. // overlaps with the current range.
for (UnorderedRanges::const_iterator I = Inactive.begin(), for (UnorderedRanges::const_iterator I = Inactive.begin(),
...@@ -198,6 +282,28 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) { ...@@ -198,6 +282,28 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) {
// variables that were allowed marked with // variables that were allowed marked with
// AllowRegisterOverlap. // AllowRegisterOverlap.
Free[RegNum] = false; Free[RegNum] = false;
// Disable AllowOverlap if an Inactive variable, which is not
// Prefer, shares Prefer's register, and has a definition
// within Cur's live range.
if (AllowOverlap && Item.Var != Prefer && RegNum == PreferReg &&
overlapsDefs(Func, Cur, Item.Var)) {
AllowOverlap = false;
dumpDisableOverlap(Func, Item.Var, "Inactive");
}
}
}
// Disable AllowOverlap if an Active variable, which is not
// Prefer, shares Prefer's register, and has a definition within
// Cur's live range.
for (UnorderedRanges::iterator I = Active.begin(), E = Active.end();
AllowOverlap && I != E; ++I) {
LiveRangeWrapper Item = *I;
int32_t RegNum = Item.Var->getRegNumTmp();
if (Item.Var != Prefer && RegNum == PreferReg &&
overlapsDefs(Func, Cur, Item.Var)) {
AllowOverlap = false;
dumpDisableOverlap(Func, Item.Var, "Active");
} }
} }
...@@ -206,13 +312,21 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) { ...@@ -206,13 +312,21 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) {
// Cur.endsBefore(*I) is an early exit check that turns a // Cur.endsBefore(*I) is an early exit check that turns a
// guaranteed O(N^2) algorithm into expected linear complexity. // guaranteed O(N^2) algorithm into expected linear complexity.
llvm::SmallBitVector PrecoloredUnhandled(RegMask.size()); llvm::SmallBitVector PrecoloredUnhandled(RegMask.size());
// Note: PrecoloredUnhandled is only used for dumping.
for (OrderedRanges::const_iterator I = Unhandled.begin(), for (OrderedRanges::const_iterator I = Unhandled.begin(),
E = Unhandled.end(); E = Unhandled.end();
I != E && !Cur.endsBefore(*I); ++I) { I != E && !Cur.endsBefore(*I); ++I) {
LiveRangeWrapper Item = *I; LiveRangeWrapper Item = *I;
if (Item.Var->hasReg() && Item.overlaps(Cur)) { if (Item.Var->hasReg() && Item.overlaps(Cur)) {
Free[Item.Var->getRegNum()] = false; // Note: getRegNum not getRegNumTmp int32_t ItemReg = Item.Var->getRegNum(); // Note: not getRegNumTmp()
PrecoloredUnhandled[Item.Var->getRegNum()] = true; Free[ItemReg] = false;
PrecoloredUnhandled[ItemReg] = true;
// Disable AllowOverlap if the preferred register is one of
// these precolored unhandled overlapping ranges.
if (AllowOverlap && ItemReg == PreferReg) {
AllowOverlap = false;
dumpDisableOverlap(Func, Item.Var, "PrecoloredUnhandled");
}
} }
} }
...@@ -228,15 +342,7 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) { ...@@ -228,15 +342,7 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) {
Str << "\n"; Str << "\n";
} }
Variable *Prefer = Cur.Var->getPreferredRegister(); if (Prefer && (AllowOverlap || Free[PreferReg])) {
int32_t PreferReg = Prefer && Prefer->hasRegTmp() ? Prefer->getRegNumTmp()
: Variable::NoRegister;
bool AllowedToOverlap = Cur.Var->getRegisterOverlap() &&
PreferReg != Variable::NoRegister &&
RegMask[PreferReg] &&
!PrecoloredUnhandled[PreferReg];
if (PreferReg != Variable::NoRegister &&
(AllowedToOverlap || Free[PreferReg])) {
// First choice: a preferred register that is either free or is // First choice: a preferred register that is either free or is
// allowed to overlap with its linked variable. // allowed to overlap with its linked variable.
Cur.Var->setRegNumTmp(PreferReg); Cur.Var->setRegNumTmp(PreferReg);
......
...@@ -382,6 +382,7 @@ void TargetX8632::translateO2() { ...@@ -382,6 +382,7 @@ void TargetX8632::translateO2() {
// associated cleanup, to make the dump cleaner and more useful. // associated cleanup, to make the dump cleaner and more useful.
Func->dump("After initial x8632 codegen"); Func->dump("After initial x8632 codegen");
Timer T_regAlloc; Timer T_regAlloc;
Func->getVMetadata()->init();
regAlloc(); regAlloc();
if (Func->hasError()) if (Func->hasError())
return; return;
...@@ -1590,21 +1591,21 @@ void TargetX8632::lowerArithmetic(const InstArithmetic *Inst) { ...@@ -1590,21 +1591,21 @@ void TargetX8632::lowerArithmetic(const InstArithmetic *Inst) {
case InstArithmetic::Shl: case InstArithmetic::Shl:
_mov(T, Src0); _mov(T, Src0);
if (!llvm::isa<Constant>(Src1)) if (!llvm::isa<Constant>(Src1))
Src1 = legalizeToVar(Src1, false, RegX8632::Reg_ecx); Src1 = legalizeToVar(Src1, RegX8632::Reg_ecx);
_shl(T, Src1); _shl(T, Src1);
_mov(Dest, T); _mov(Dest, T);
break; break;
case InstArithmetic::Lshr: case InstArithmetic::Lshr:
_mov(T, Src0); _mov(T, Src0);
if (!llvm::isa<Constant>(Src1)) if (!llvm::isa<Constant>(Src1))
Src1 = legalizeToVar(Src1, false, RegX8632::Reg_ecx); Src1 = legalizeToVar(Src1, RegX8632::Reg_ecx);
_shr(T, Src1); _shr(T, Src1);
_mov(Dest, T); _mov(Dest, T);
break; break;
case InstArithmetic::Ashr: case InstArithmetic::Ashr:
_mov(T, Src0); _mov(T, Src0);
if (!llvm::isa<Constant>(Src1)) if (!llvm::isa<Constant>(Src1))
Src1 = legalizeToVar(Src1, false, RegX8632::Reg_ecx); Src1 = legalizeToVar(Src1, RegX8632::Reg_ecx);
_sar(T, Src1); _sar(T, Src1);
_mov(Dest, T); _mov(Dest, T);
break; break;
...@@ -1725,9 +1726,8 @@ void TargetX8632::lowerAssign(const InstAssign *Inst) { ...@@ -1725,9 +1726,8 @@ void TargetX8632::lowerAssign(const InstAssign *Inst) {
_mov(T_Hi, Src0Hi); _mov(T_Hi, Src0Hi);
_mov(DestHi, T_Hi); _mov(DestHi, T_Hi);
} else { } else {
const bool AllowOverlap = true;
// RI is either a physical register or an immediate. // RI is either a physical register or an immediate.
Operand *RI = legalize(Src0, Legal_Reg | Legal_Imm, AllowOverlap); Operand *RI = legalize(Src0, Legal_Reg | Legal_Imm);
if (isVectorType(Dest->getType())) if (isVectorType(Dest->getType()))
_movp(Dest, RI); _movp(Dest, RI);
else else
...@@ -1830,7 +1830,7 @@ void TargetX8632::lowerCall(const InstCall *Instr) { ...@@ -1830,7 +1830,7 @@ void TargetX8632::lowerCall(const InstCall *Instr) {
// code, as the memory operand displacements may end up being smaller // code, as the memory operand displacements may end up being smaller
// before any stack adjustment is done. // before any stack adjustment is done.
for (SizeT i = 0, NumXmmArgs = XmmArgs.size(); i < NumXmmArgs; ++i) { for (SizeT i = 0, NumXmmArgs = XmmArgs.size(); i < NumXmmArgs; ++i) {
Variable *Reg = legalizeToVar(XmmArgs[i], false, RegX8632::Reg_xmm0 + i); Variable *Reg = legalizeToVar(XmmArgs[i], RegX8632::Reg_xmm0 + i);
// Generate a FakeUse of register arguments so that they do not get // Generate a FakeUse of register arguments so that they do not get
// dead code eliminated as a result of the FakeKill of scratch // dead code eliminated as a result of the FakeKill of scratch
// registers after the call. // registers after the call.
...@@ -1914,15 +1914,12 @@ void TargetX8632::lowerCall(const InstCall *Instr) { ...@@ -1914,15 +1914,12 @@ void TargetX8632::lowerCall(const InstCall *Instr) {
split64(Dest); split64(Dest);
Variable *DestLo = Dest->getLo(); Variable *DestLo = Dest->getLo();
Variable *DestHi = Dest->getHi(); Variable *DestHi = Dest->getHi();
DestLo->setPreferredRegister(ReturnReg, false);
DestHi->setPreferredRegister(ReturnRegHi, false);
_mov(DestLo, ReturnReg); _mov(DestLo, ReturnReg);
_mov(DestHi, ReturnRegHi); _mov(DestHi, ReturnRegHi);
} else { } else {
assert(Dest->getType() == IceType_i32 || Dest->getType() == IceType_i16 || assert(Dest->getType() == IceType_i32 || Dest->getType() == IceType_i16 ||
Dest->getType() == IceType_i8 || Dest->getType() == IceType_i1 || Dest->getType() == IceType_i8 || Dest->getType() == IceType_i1 ||
isVectorType(Dest->getType())); isVectorType(Dest->getType()));
Dest->setPreferredRegister(ReturnReg, false);
if (isVectorType(Dest->getType())) { if (isVectorType(Dest->getType())) {
_movp(Dest, ReturnReg); _movp(Dest, ReturnReg);
} else { } else {
...@@ -2137,7 +2134,6 @@ void TargetX8632::lowerCast(const InstCast *Inst) { ...@@ -2137,7 +2134,6 @@ void TargetX8632::lowerCast(const InstCast *Inst) {
if (Dest->getType() == IceType_i1) if (Dest->getType() == IceType_i1)
_and(T_2, Ctx->getConstantInt32(IceType_i1, 1)); _and(T_2, Ctx->getConstantInt32(IceType_i1, 1));
_mov(Dest, T_2); _mov(Dest, T_2);
T_2->setPreferredRegister(T_1, true);
} }
break; break;
case InstCast::Fptoui: case InstCast::Fptoui:
...@@ -2174,7 +2170,6 @@ void TargetX8632::lowerCast(const InstCast *Inst) { ...@@ -2174,7 +2170,6 @@ void TargetX8632::lowerCast(const InstCast *Inst) {
if (Dest->getType() == IceType_i1) if (Dest->getType() == IceType_i1)
_and(T_2, Ctx->getConstantInt32(IceType_i1, 1)); _and(T_2, Ctx->getConstantInt32(IceType_i1, 1));
_mov(Dest, T_2); _mov(Dest, T_2);
T_2->setPreferredRegister(T_1, true);
} }
break; break;
case InstCast::Sitofp: case InstCast::Sitofp:
...@@ -2686,8 +2681,8 @@ void TargetX8632::lowerIcmp(const InstIcmp *Inst) { ...@@ -2686,8 +2681,8 @@ void TargetX8632::lowerIcmp(const InstIcmp *Inst) {
if (Src0->getType() != IceType_i64 && !NextBr->isUnconditional() && if (Src0->getType() != IceType_i64 && !NextBr->isUnconditional() &&
Dest == NextBr->getSrc(0) && NextBr->isLastUse(Dest)) { Dest == NextBr->getSrc(0) && NextBr->isLastUse(Dest)) {
NextBr->setDeleted(); NextBr->setDeleted();
Operand *Src0RM = legalize( Operand *Src0RM =
Src0, IsSrc1ImmOrReg ? (Legal_Reg | Legal_Mem) : Legal_Reg, true); legalize(Src0, IsSrc1ImmOrReg ? (Legal_Reg | Legal_Mem) : Legal_Reg);
_cmp(Src0RM, Src1); _cmp(Src0RM, Src1);
_br(getIcmp32Mapping(Inst->getCondition()), NextBr->getTargetTrue(), _br(getIcmp32Mapping(Inst->getCondition()), NextBr->getTargetTrue(),
NextBr->getTargetFalse()); NextBr->getTargetFalse());
...@@ -2736,8 +2731,8 @@ void TargetX8632::lowerIcmp(const InstIcmp *Inst) { ...@@ -2736,8 +2731,8 @@ void TargetX8632::lowerIcmp(const InstIcmp *Inst) {
} }
// cmp b, c // cmp b, c
Operand *Src0RM = legalize( Operand *Src0RM =
Src0, IsSrc1ImmOrReg ? (Legal_Reg | Legal_Mem) : Legal_Reg, true); legalize(Src0, IsSrc1ImmOrReg ? (Legal_Reg | Legal_Mem) : Legal_Reg);
InstX8632Label *Label = InstX8632Label::create(Func, this); InstX8632Label *Label = InstX8632Label::create(Func, this);
_cmp(Src0RM, Src1); _cmp(Src0RM, Src1);
_mov(Dest, One); _mov(Dest, One);
...@@ -3588,7 +3583,8 @@ bool matchTransitiveAssign(const VariablesMetadata *VMetadata, Variable *&Var, ...@@ -3588,7 +3583,8 @@ bool matchTransitiveAssign(const VariablesMetadata *VMetadata, Variable *&Var,
// set Var:=SrcVar // set Var:=SrcVar
if (Var == NULL) if (Var == NULL)
return false; return false;
if (const Inst *VarAssign = VMetadata->getDefinition(Var)) { if (const Inst *VarAssign = VMetadata->getSingleDefinition(Var)) {
assert(!VMetadata->isMultiDef(Var));
if (llvm::isa<InstAssign>(VarAssign)) { if (llvm::isa<InstAssign>(VarAssign)) {
Operand *SrcOp = VarAssign->getSrc(0); Operand *SrcOp = VarAssign->getSrc(0);
assert(SrcOp); assert(SrcOp);
...@@ -3615,9 +3611,10 @@ bool matchCombinedBaseIndex(const VariablesMetadata *VMetadata, Variable *&Base, ...@@ -3615,9 +3611,10 @@ bool matchCombinedBaseIndex(const VariablesMetadata *VMetadata, Variable *&Base,
return false; return false;
if (Index != NULL) if (Index != NULL)
return false; return false;
const Inst *BaseInst = VMetadata->getDefinition(Base); const Inst *BaseInst = VMetadata->getSingleDefinition(Base);
if (BaseInst == NULL) if (BaseInst == NULL)
return false; return false;
assert(!VMetadata->isMultiDef(Base));
if (BaseInst->getSrcSize() < 2) if (BaseInst->getSrcSize() < 2)
return false; return false;
if (Variable *Var1 = llvm::dyn_cast<Variable>(BaseInst->getSrc(0))) { if (Variable *Var1 = llvm::dyn_cast<Variable>(BaseInst->getSrc(0))) {
...@@ -3646,9 +3643,10 @@ bool matchShiftedIndex(const VariablesMetadata *VMetadata, Variable *&Index, ...@@ -3646,9 +3643,10 @@ bool matchShiftedIndex(const VariablesMetadata *VMetadata, Variable *&Index,
// Index=Var, Shift+=log2(Const) // Index=Var, Shift+=log2(Const)
if (Index == NULL) if (Index == NULL)
return false; return false;
const Inst *IndexInst = VMetadata->getDefinition(Index); const Inst *IndexInst = VMetadata->getSingleDefinition(Index);
if (IndexInst == NULL) if (IndexInst == NULL)
return false; return false;
assert(!VMetadata->isMultiDef(Index));
if (IndexInst->getSrcSize() < 2) if (IndexInst->getSrcSize() < 2)
return false; return false;
if (const InstArithmetic *ArithInst = if (const InstArithmetic *ArithInst =
...@@ -3697,9 +3695,10 @@ bool matchOffsetBase(const VariablesMetadata *VMetadata, Variable *&Base, ...@@ -3697,9 +3695,10 @@ bool matchOffsetBase(const VariablesMetadata *VMetadata, Variable *&Base,
// set Base=Var, Offset-=Const // set Base=Var, Offset-=Const
if (Base == NULL) if (Base == NULL)
return false; return false;
const Inst *BaseInst = VMetadata->getDefinition(Base); const Inst *BaseInst = VMetadata->getSingleDefinition(Base);
if (BaseInst == NULL) if (BaseInst == NULL)
return false; return false;
assert(!VMetadata->isMultiDef(Base));
if (const InstArithmetic *ArithInst = if (const InstArithmetic *ArithInst =
llvm::dyn_cast<const InstArithmetic>(BaseInst)) { llvm::dyn_cast<const InstArithmetic>(BaseInst)) {
if (ArithInst->getOp() != InstArithmetic::Add && if (ArithInst->getOp() != InstArithmetic::Add &&
...@@ -3878,15 +3877,15 @@ void TargetX8632::lowerRet(const InstRet *Inst) { ...@@ -3878,15 +3877,15 @@ void TargetX8632::lowerRet(const InstRet *Inst) {
if (Inst->hasRetValue()) { if (Inst->hasRetValue()) {
Operand *Src0 = legalize(Inst->getRetValue()); Operand *Src0 = legalize(Inst->getRetValue());
if (Src0->getType() == IceType_i64) { if (Src0->getType() == IceType_i64) {
Variable *eax = legalizeToVar(loOperand(Src0), false, RegX8632::Reg_eax); Variable *eax = legalizeToVar(loOperand(Src0), RegX8632::Reg_eax);
Variable *edx = legalizeToVar(hiOperand(Src0), false, RegX8632::Reg_edx); Variable *edx = legalizeToVar(hiOperand(Src0), RegX8632::Reg_edx);
Reg = eax; Reg = eax;
Context.insert(InstFakeUse::create(Func, edx)); Context.insert(InstFakeUse::create(Func, edx));
} else if (Src0->getType() == IceType_f32 || } else if (Src0->getType() == IceType_f32 ||
Src0->getType() == IceType_f64) { Src0->getType() == IceType_f64) {
_fld(Src0); _fld(Src0);
} else if (isVectorType(Src0->getType())) { } else if (isVectorType(Src0->getType())) {
Reg = legalizeToVar(Src0, false, RegX8632::Reg_xmm0); Reg = legalizeToVar(Src0, RegX8632::Reg_xmm0);
} else { } else {
_mov(Reg, Src0, RegX8632::Reg_eax); _mov(Reg, Src0, RegX8632::Reg_eax);
} }
...@@ -3973,8 +3972,8 @@ void TargetX8632::lowerSelect(const InstSelect *Inst) { ...@@ -3973,8 +3972,8 @@ void TargetX8632::lowerSelect(const InstSelect *Inst) {
if (Dest->getType() == IceType_i64) { if (Dest->getType() == IceType_i64) {
Variable *DestLo = llvm::cast<Variable>(loOperand(Dest)); Variable *DestLo = llvm::cast<Variable>(loOperand(Dest));
Variable *DestHi = llvm::cast<Variable>(hiOperand(Dest)); Variable *DestHi = llvm::cast<Variable>(hiOperand(Dest));
Operand *SrcLoRI = legalize(loOperand(SrcT), Legal_Reg | Legal_Imm, true); Operand *SrcLoRI = legalize(loOperand(SrcT), Legal_Reg | Legal_Imm);
Operand *SrcHiRI = legalize(hiOperand(SrcT), Legal_Reg | Legal_Imm, true); Operand *SrcHiRI = legalize(hiOperand(SrcT), Legal_Reg | Legal_Imm);
_cmp(ConditionRM, Zero); _cmp(ConditionRM, Zero);
_mov(DestLo, SrcLoRI); _mov(DestLo, SrcLoRI);
_mov(DestHi, SrcHiRI); _mov(DestHi, SrcHiRI);
...@@ -3983,17 +3982,17 @@ void TargetX8632::lowerSelect(const InstSelect *Inst) { ...@@ -3983,17 +3982,17 @@ void TargetX8632::lowerSelect(const InstSelect *Inst) {
Context.insert(InstFakeUse::create(Func, DestHi)); Context.insert(InstFakeUse::create(Func, DestHi));
Operand *SrcFLo = loOperand(SrcF); Operand *SrcFLo = loOperand(SrcF);
Operand *SrcFHi = hiOperand(SrcF); Operand *SrcFHi = hiOperand(SrcF);
SrcLoRI = legalize(SrcFLo, Legal_Reg | Legal_Imm, true); SrcLoRI = legalize(SrcFLo, Legal_Reg | Legal_Imm);
SrcHiRI = legalize(SrcFHi, Legal_Reg | Legal_Imm, true); SrcHiRI = legalize(SrcFHi, Legal_Reg | Legal_Imm);
_mov(DestLo, SrcLoRI); _mov(DestLo, SrcLoRI);
_mov(DestHi, SrcHiRI); _mov(DestHi, SrcHiRI);
} else { } else {
_cmp(ConditionRM, Zero); _cmp(ConditionRM, Zero);
SrcT = legalize(SrcT, Legal_Reg | Legal_Imm, true); SrcT = legalize(SrcT, Legal_Reg | Legal_Imm);
_mov(Dest, SrcT); _mov(Dest, SrcT);
_br(CondX86::Br_ne, Label); _br(CondX86::Br_ne, Label);
Context.insert(InstFakeUse::create(Func, Dest)); Context.insert(InstFakeUse::create(Func, Dest));
SrcF = legalize(SrcF, Legal_Reg | Legal_Imm, true); SrcF = legalize(SrcF, Legal_Reg | Legal_Imm);
_mov(Dest, SrcF); _mov(Dest, SrcF);
} }
...@@ -4008,14 +4007,14 @@ void TargetX8632::lowerStore(const InstStore *Inst) { ...@@ -4008,14 +4007,14 @@ void TargetX8632::lowerStore(const InstStore *Inst) {
if (Ty == IceType_i64) { if (Ty == IceType_i64) {
Value = legalize(Value); Value = legalize(Value);
Operand *ValueHi = legalize(hiOperand(Value), Legal_Reg | Legal_Imm, true); Operand *ValueHi = legalize(hiOperand(Value), Legal_Reg | Legal_Imm);
Operand *ValueLo = legalize(loOperand(Value), Legal_Reg | Legal_Imm, true); Operand *ValueLo = legalize(loOperand(Value), Legal_Reg | Legal_Imm);
_store(ValueHi, llvm::cast<OperandX8632Mem>(hiOperand(NewAddr))); _store(ValueHi, llvm::cast<OperandX8632Mem>(hiOperand(NewAddr)));
_store(ValueLo, llvm::cast<OperandX8632Mem>(loOperand(NewAddr))); _store(ValueLo, llvm::cast<OperandX8632Mem>(loOperand(NewAddr)));
} else if (isVectorType(Ty)) { } else if (isVectorType(Ty)) {
_storep(legalizeToVar(Value), NewAddr); _storep(legalizeToVar(Value), NewAddr);
} else { } else {
Value = legalize(Value, Legal_Reg | Legal_Imm, true); Value = legalize(Value, Legal_Reg | Legal_Imm);
_store(Value, NewAddr); _store(Value, NewAddr);
} }
} }
...@@ -4054,7 +4053,7 @@ void TargetX8632::lowerSwitch(const InstSwitch *Inst) { ...@@ -4054,7 +4053,7 @@ void TargetX8632::lowerSwitch(const InstSwitch *Inst) {
if (NumCases >= 2) if (NumCases >= 2)
Src0 = legalizeToVar(Src0, true); Src0 = legalizeToVar(Src0, true);
else else
Src0 = legalize(Src0, Legal_Reg | Legal_Mem, true); Src0 = legalize(Src0, Legal_Reg | Legal_Mem);
for (SizeT I = 0; I < NumCases; ++I) { for (SizeT I = 0; I < NumCases; ++I) {
// TODO(stichnot): Correct lowering for IceType_i64. // TODO(stichnot): Correct lowering for IceType_i64.
Constant *Value = Ctx->getConstantInt32(IceType_i32, Inst->getValue(I)); Constant *Value = Ctx->getConstantInt32(IceType_i32, Inst->getValue(I));
...@@ -4209,7 +4208,7 @@ Variable *TargetX8632::copyToReg(Operand *Src, int32_t RegNum) { ...@@ -4209,7 +4208,7 @@ Variable *TargetX8632::copyToReg(Operand *Src, int32_t RegNum) {
} }
Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed, Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed,
bool AllowOverlap, int32_t RegNum) { int32_t RegNum) {
// Assert that a physical register is allowed. To date, all calls // Assert that a physical register is allowed. To date, all calls
// to legalize() allow a physical register. If a physical register // to legalize() allow a physical register. If a physical register
// needs to be explicitly disallowed, then new code will need to be // needs to be explicitly disallowed, then new code will need to be
...@@ -4228,10 +4227,10 @@ Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed, ...@@ -4228,10 +4227,10 @@ Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed,
Variable *RegBase = NULL; Variable *RegBase = NULL;
Variable *RegIndex = NULL; Variable *RegIndex = NULL;
if (Base) { if (Base) {
RegBase = legalizeToVar(Base, true); RegBase = legalizeToVar(Base);
} }
if (Index) { if (Index) {
RegIndex = legalizeToVar(Index, true); RegIndex = legalizeToVar(Index);
} }
if (Base != RegBase || Index != RegIndex) { if (Base != RegBase || Index != RegIndex) {
From = OperandX8632Mem::create( From = OperandX8632Mem::create(
...@@ -4293,11 +4292,7 @@ Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed, ...@@ -4293,11 +4292,7 @@ Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed,
// RegNum is required and Var->getRegNum() doesn't match. // RegNum is required and Var->getRegNum() doesn't match.
if ((!(Allowed & Legal_Mem) && !MustHaveRegister) || if ((!(Allowed & Legal_Mem) && !MustHaveRegister) ||
(RegNum != Variable::NoRegister && RegNum != Var->getRegNum())) { (RegNum != Variable::NoRegister && RegNum != Var->getRegNum())) {
Variable *Reg = copyToReg(From, RegNum); From = copyToReg(From, RegNum);
if (RegNum == Variable::NoRegister) {
Reg->setPreferredRegister(Var, AllowOverlap);
}
From = Reg;
} }
return From; return From;
} }
...@@ -4306,9 +4301,8 @@ Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed, ...@@ -4306,9 +4301,8 @@ Operand *TargetX8632::legalize(Operand *From, LegalMask Allowed,
} }
// Provide a trivial wrapper to legalize() for this common usage. // Provide a trivial wrapper to legalize() for this common usage.
Variable *TargetX8632::legalizeToVar(Operand *From, bool AllowOverlap, Variable *TargetX8632::legalizeToVar(Operand *From, int32_t RegNum) {
int32_t RegNum) { return llvm::cast<Variable>(legalize(From, Legal_Reg, RegNum));
return llvm::cast<Variable>(legalize(From, Legal_Reg, AllowOverlap, RegNum));
} }
OperandX8632Mem *TargetX8632::FormMemoryOperand(Operand *Operand, Type Ty) { OperandX8632Mem *TargetX8632::FormMemoryOperand(Operand *Operand, Type Ty) {
......
...@@ -147,10 +147,8 @@ protected: ...@@ -147,10 +147,8 @@ protected:
}; };
typedef uint32_t LegalMask; typedef uint32_t LegalMask;
Operand *legalize(Operand *From, LegalMask Allowed = Legal_All & ~Legal_Reloc, Operand *legalize(Operand *From, LegalMask Allowed = Legal_All & ~Legal_Reloc,
bool AllowOverlap = false,
int32_t RegNum = Variable::NoRegister); int32_t RegNum = Variable::NoRegister);
Variable *legalizeToVar(Operand *From, bool AllowOverlap = false, Variable *legalizeToVar(Operand *From, int32_t RegNum = Variable::NoRegister);
int32_t RegNum = Variable::NoRegister);
// Turn a pointer operand into a memory operand that can be // Turn a pointer operand into a memory operand that can be
// used by a real load/store operation. Legalizes the operand as well. // used by a real load/store operation. Legalizes the operand as well.
// This is a nop if the operand is already a legal memory operand. // This is a nop if the operand is already a legal memory operand.
...@@ -297,11 +295,9 @@ protected: ...@@ -297,11 +295,9 @@ protected:
// in/out Dest argument. // in/out Dest argument.
void _mov(Variable *&Dest, Operand *Src0, void _mov(Variable *&Dest, Operand *Src0,
int32_t RegNum = Variable::NoRegister) { int32_t RegNum = Variable::NoRegister) {
if (Dest == NULL) { if (Dest == NULL)
Dest = legalizeToVar(Src0, false, RegNum); Dest = makeReg(Src0->getType(), RegNum);
} else { Context.insert(InstX8632Mov::create(Func, Dest, Src0));
Context.insert(InstX8632Mov::create(Func, Dest, Src0));
}
} }
void _movd(Variable *Dest, Operand *Src0) { void _movd(Variable *Dest, Operand *Src0) {
Context.insert(InstX8632Movd::create(Func, Dest, Src0)); Context.insert(InstX8632Movd::create(Func, Dest, Src0));
......
...@@ -47,6 +47,8 @@ static cl::list<Ice::VerboseItem> VerboseList( ...@@ -47,6 +47,8 @@ static cl::list<Ice::VerboseItem> VerboseList(
clEnumValN(Ice::IceV_Timing, "time", "Pass timing details"), clEnumValN(Ice::IceV_Timing, "time", "Pass timing details"),
clEnumValN(Ice::IceV_AddrOpt, "addropt", "Address mode optimization"), clEnumValN(Ice::IceV_AddrOpt, "addropt", "Address mode optimization"),
clEnumValN(Ice::IceV_All, "all", "Use all verbose options"), clEnumValN(Ice::IceV_All, "all", "Use all verbose options"),
clEnumValN(Ice::IceV_Most, "most",
"Use all verbose options except 'regalloc' and 'time'"),
clEnumValN(Ice::IceV_None, "none", "No verbosity"), clEnumValEnd)); clEnumValN(Ice::IceV_None, "none", "No verbosity"), clEnumValEnd));
static cl::opt<Ice::TargetArch> TargetArch( static cl::opt<Ice::TargetArch> TargetArch(
"target", cl::desc("Target architecture:"), cl::init(Ice::Target_X8632), "target", cl::desc("Target architecture:"), cl::init(Ice::Target_X8632),
...@@ -134,6 +136,8 @@ static cl::opt<bool> ...@@ -134,6 +136,8 @@ static cl::opt<bool>
UseIntegratedAssembler("integrated-as", UseIntegratedAssembler("integrated-as",
cl::desc("Use integrated assembler (default yes)"), cl::desc("Use integrated assembler (default yes)"),
cl::init(true)); cl::init(true));
static cl::alias UseIas("ias", cl::desc("Alias for -integrated-as"),
cl::NotHidden, cl::aliasopt(UseIntegratedAssembler));
int main(int argc, char **argv) { int main(int argc, char **argv) {
......
...@@ -46,7 +46,7 @@ entry: ...@@ -46,7 +46,7 @@ entry:
%addr.load = load float* %addr.ptr, align 4 %addr.load = load float* %addr.ptr, align 4
ret float %addr.load ret float %addr.load
; CHECK-LABEL: load_200000_minus_arg: ; CHECK-LABEL: load_200000_minus_arg:
; CHECK: movss xmm0, dword ptr [eax] ; CHECK: movss xmm0, dword ptr [e{{..}}]
} }
define float @address_mode_opt_chaining(float* %arg) { define float @address_mode_opt_chaining(float* %arg) {
......
...@@ -205,7 +205,7 @@ entry: ...@@ -205,7 +205,7 @@ entry:
; CHECK-LABEL: test_atomic_rmw_add_8 ; CHECK-LABEL: test_atomic_rmw_add_8
; CHECK: lock ; CHECK: lock
; CHECK-NEXT: xadd byte {{.*}}, [[REG:.*]] ; CHECK-NEXT: xadd byte {{.*}}, [[REG:.*]]
; CHECK: mov {{.*}}, [[REG]] ; CHECK: {{mov|movzx}} {{.*}}, [[REG]]
define i32 @test_atomic_rmw_add_16(i32 %iptr, i32 %v) { define i32 @test_atomic_rmw_add_16(i32 %iptr, i32 %v) {
entry: entry:
...@@ -218,7 +218,7 @@ entry: ...@@ -218,7 +218,7 @@ entry:
; CHECK-LABEL: test_atomic_rmw_add_16 ; CHECK-LABEL: test_atomic_rmw_add_16
; CHECK: lock ; CHECK: lock
; CHECK-NEXT: xadd word {{.*}}, [[REG:.*]] ; CHECK-NEXT: xadd word {{.*}}, [[REG:.*]]
; CHECK: mov {{.*}}, [[REG]] ; CHECK: {{mov|movzx}} {{.*}}, [[REG]]
define i32 @test_atomic_rmw_add_32(i32 %iptr, i32 %v) { define i32 @test_atomic_rmw_add_32(i32 %iptr, i32 %v) {
entry: entry:
...@@ -347,7 +347,7 @@ entry: ...@@ -347,7 +347,7 @@ entry:
; CHECK: neg [[REG:.*]] ; CHECK: neg [[REG:.*]]
; CHECK: lock ; CHECK: lock
; CHECK-NEXT: xadd byte {{.*}}, [[REG]] ; CHECK-NEXT: xadd byte {{.*}}, [[REG]]
; CHECK: mov {{.*}}, [[REG]] ; CHECK: {{mov|movzx}} {{.*}}, [[REG]]
define i32 @test_atomic_rmw_sub_16(i32 %iptr, i32 %v) { define i32 @test_atomic_rmw_sub_16(i32 %iptr, i32 %v) {
entry: entry:
...@@ -361,7 +361,7 @@ entry: ...@@ -361,7 +361,7 @@ entry:
; CHECK: neg [[REG:.*]] ; CHECK: neg [[REG:.*]]
; CHECK: lock ; CHECK: lock
; CHECK-NEXT: xadd word {{.*}}, [[REG]] ; CHECK-NEXT: xadd word {{.*}}, [[REG]]
; CHECK: mov {{.*}}, [[REG]] ; CHECK: {{mov|movzx}} {{.*}}, [[REG]]
define i32 @test_atomic_rmw_sub_32(i32 %iptr, i32 %v) { define i32 @test_atomic_rmw_sub_32(i32 %iptr, i32 %v) {
entry: entry:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment