Commit 800dab29 by Jim Stichnoth

Subzero: Change the way bitcast stack slot lowering is handled.

When doing a bitcast between int and FP types, the way lowering works is that a spill temporary is created, with regalloc weight of zero to inhibit register allocation, and this spill temporary is used for the cvt instruction. If the other variable does not get register-allocated, then addProlog() forces the spill temporary to share the same stack slot as the other variable. Currently, the lowering code passes this information to addProlog() by using the setPreferredRegister() mechanism. This is changed by creating a target-specific subclass of Variable, so that only the spill temporaries need to carry this extra information. Ultimately, many of the existing Variable fields will be refactored into a separate structure, and only generated/used as needed by various optimization passes. The spill temporary linkage is the one thing that is still needed with Om1 when no optimizations are enabled, motivating this change. A couple other minor cleanups are also done here. The key test is that the cast cross tests continue to work, specifically the bitcast tests. BUG= none R=jvoung@chromium.org Review URL: https://codereview.chromium.org/586943003
parent 4c127ba7
...@@ -45,13 +45,9 @@ CfgNode *Cfg::makeNode(const IceString &Name) { ...@@ -45,13 +45,9 @@ CfgNode *Cfg::makeNode(const IceString &Name) {
return Node; return Node;
} }
// Create a new Variable with a particular type and an optional
// name. The Node argument is the node where the variable is defined.
Variable *Cfg::makeVariable(Type Ty, const CfgNode *Node, Variable *Cfg::makeVariable(Type Ty, const CfgNode *Node,
const IceString &Name) { const IceString &Name) {
SizeT Index = Variables.size(); return makeVariable<Variable>(Ty, Node, Name);
Variables.push_back(Variable::create(this, Ty, Node, Index, Name));
return Variables[Index];
} }
void Cfg::addArg(Variable *Arg) { void Cfg::addArg(Variable *Arg) {
...@@ -359,7 +355,7 @@ void Cfg::dump(const IceString &Message) { ...@@ -359,7 +355,7 @@ void Cfg::dump(const IceString &Message) {
} }
Str << ") {\n"; Str << ") {\n";
} }
setCurrentNode(NULL); resetCurrentNode();
if (getContext()->isVerbose(IceV_Liveness)) { if (getContext()->isVerbose(IceV_Liveness)) {
// Print summary info about variables // Print summary info about variables
for (VarList::const_iterator I = Variables.begin(), E = Variables.end(); for (VarList::const_iterator I = Variables.begin(), E = Variables.end();
......
...@@ -61,6 +61,17 @@ public: ...@@ -61,6 +61,17 @@ public:
InstNumberT newInstNumber() { return NextInstNumber++; } InstNumberT newInstNumber() { return NextInstNumber++; }
// Manage Variables. // Manage Variables.
// Create a new Variable with a particular type and an optional
// name. The Node argument is the node where the variable is defined.
template <typename T>
T *makeVariable(Type Ty, const CfgNode *Node, const IceString &Name = "") {
SizeT Index = Variables.size();
T *Var = T::create(this, Ty, Node, Index, Name);
Variables.push_back(Var);
return Var;
}
// TODO(stichnot): Remove this function with C++11, and use default
// argument <typename T=Variable> above.
Variable *makeVariable(Type Ty, const CfgNode *Node, Variable *makeVariable(Type Ty, const CfgNode *Node,
const IceString &Name = ""); const IceString &Name = "");
SizeT getNumVariables() const { return Variables.size(); } SizeT getNumVariables() const { return Variables.size(); }
...@@ -99,6 +110,7 @@ public: ...@@ -99,6 +110,7 @@ public:
// Manage the CurrentNode field, which is used for validating the // Manage the CurrentNode field, which is used for validating the
// Variable::DefNode field during dumping/emitting. // Variable::DefNode field during dumping/emitting.
void setCurrentNode(const CfgNode *Node) { CurrentNode = Node; } void setCurrentNode(const CfgNode *Node) { CurrentNode = Node; }
void resetCurrentNode() { setCurrentNode(NULL); }
const CfgNode *getCurrentNode() const { return CurrentNode; } const CfgNode *getCurrentNode() const { return CurrentNode; }
void emit(); void emit();
...@@ -155,8 +167,8 @@ private: ...@@ -155,8 +167,8 @@ private:
// CurrentNode is maintained during dumping/emitting just for // CurrentNode is maintained during dumping/emitting just for
// validating Variable::DefNode. Normally, a traversal over // validating Variable::DefNode. Normally, a traversal over
// CfgNodes maintains this, but before global operations like // CfgNodes maintains this, but before global operations like
// register allocation, setCurrentNode(NULL) should be called to // register allocation, resetCurrentNode() should be called to avoid
// avoid spurious validation failures. // spurious validation failures.
const CfgNode *CurrentNode; const CfgNode *CurrentNode;
Cfg(const Cfg &) LLVM_DELETED_FUNCTION; Cfg(const Cfg &) LLVM_DELETED_FUNCTION;
......
...@@ -351,7 +351,7 @@ bool CfgNode::liveness(Liveness *Liveness) { ...@@ -351,7 +351,7 @@ bool CfgNode::liveness(Liveness *Liveness) {
// This is a fatal liveness consistency error. Print some // This is a fatal liveness consistency error. Print some
// diagnostics and abort. // diagnostics and abort.
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
Func->setCurrentNode(NULL); Func->resetCurrentNode();
Str << "LiveOrig-Live ="; Str << "LiveOrig-Live =";
for (SizeT i = Live.size(); i < LiveOrig.size(); ++i) { for (SizeT i = Live.size(); i < LiveOrig.size(); ++i) {
if (LiveOrig.test(i)) { if (LiveOrig.test(i)) {
......
...@@ -135,6 +135,33 @@ private: ...@@ -135,6 +135,33 @@ private:
Portion Part; Portion Part;
}; };
// SpillVariable decorates a Variable by linking it to another
// Variable. When stack frame offsets are computed, the SpillVariable
// is given a distinct stack slot only if its linked Variable has a
// register. If the linked Variable has a stack slot, then the
// Variable and SpillVariable share that slot.
class SpillVariable : public Variable {
public:
static SpillVariable *create(Cfg *Func, Type Ty, const CfgNode *Node,
SizeT Index, const IceString &Name) {
return new (Func->allocate<SpillVariable>())
SpillVariable(Ty, Node, Index, Name);
}
const static OperandKind SpillVariableKind =
static_cast<OperandKind>(kVariable_Target);
static bool classof(const Operand *Operand) {
return Operand->getKind() == SpillVariableKind;
}
void setLinkedTo(Variable *Var) { LinkedTo = Var; }
Variable *getLinkedTo() const { return LinkedTo; }
// Inherit dump() and emit() from Variable.
private:
SpillVariable(Type Ty, const CfgNode *Node, SizeT Index,
const IceString &Name)
: Variable(SpillVariableKind, Ty, Node, Index, Name), LinkedTo(NULL) {}
Variable *LinkedTo;
};
class InstX8632 : public InstTarget { class InstX8632 : public InstTarget {
public: public:
enum InstKindX8632 { enum InstKindX8632 {
......
...@@ -189,7 +189,9 @@ IceString Variable::getName() const { ...@@ -189,7 +189,9 @@ IceString Variable::getName() const {
} }
Variable Variable::asType(Type Ty) { Variable Variable::asType(Type Ty) {
Variable V(Ty, DefNode, Number, Name); // Note: This returns a Variable, even if the "this" object is a
// subclass of Variable.
Variable V(kVariable, Ty, DefNode, Number, Name);
V.RegNum = RegNum; V.RegNum = RegNum;
V.StackOffset = StackOffset; V.StackOffset = StackOffset;
return V; return V;
......
...@@ -25,6 +25,7 @@ namespace Ice { ...@@ -25,6 +25,7 @@ namespace Ice {
class Operand { class Operand {
public: public:
static const size_t MaxTargetKinds = 10;
enum OperandKind { enum OperandKind {
kConst_Base, kConst_Base,
kConstInteger32, kConstInteger32,
...@@ -33,8 +34,11 @@ public: ...@@ -33,8 +34,11 @@ public:
kConstDouble, kConstDouble,
kConstRelocatable, kConstRelocatable,
kConstUndef, kConstUndef,
kConst_Num, kConst_Target, // leave space for target-specific constant kinds
kConst_Num = kConst_Target + MaxTargetKinds,
kVariable, kVariable,
kVariable_Target, // leave space for target-specific variable kinds
kVariable_Num = kVariable_Target + MaxTargetKinds,
// Target-specific operand classes use kTarget as the starting // Target-specific operand classes use kTarget as the starting
// point for their Kind enum space. // point for their Kind enum space.
kTarget kTarget
...@@ -339,10 +343,14 @@ Ostream &operator<<(Ostream &Str, const LiveRange &L); ...@@ -339,10 +343,14 @@ Ostream &operator<<(Ostream &Str, const LiveRange &L);
// stack-allocated. If it is register-allocated, it will ultimately // stack-allocated. If it is register-allocated, it will ultimately
// have a non-negative RegNum field. // have a non-negative RegNum field.
class Variable : public Operand { class Variable : public Operand {
Variable(const Variable &) LLVM_DELETED_FUNCTION;
Variable &operator=(const Variable &) LLVM_DELETED_FUNCTION;
public: public:
static Variable *create(Cfg *Func, Type Ty, const CfgNode *Node, SizeT Index, static Variable *create(Cfg *Func, Type Ty, const CfgNode *Node, SizeT Index,
const IceString &Name) { const IceString &Name) {
return new (Func->allocate<Variable>()) Variable(Ty, Node, Index, Name); return new (Func->allocate<Variable>())
Variable(kVariable, Ty, Node, Index, Name);
} }
SizeT getIndex() const { return Number; } SizeT getIndex() const { return Number; }
...@@ -431,16 +439,18 @@ public: ...@@ -431,16 +439,18 @@ public:
virtual void dump(const Cfg *Func, Ostream &Str) const; virtual void dump(const Cfg *Func, Ostream &Str) const;
static bool classof(const Operand *Operand) { static bool classof(const Operand *Operand) {
return Operand->getKind() == kVariable; OperandKind Kind = Operand->getKind();
return Kind >= kVariable && Kind <= kVariable_Num;
} }
// The destructor is public because of the asType() method. // The destructor is public because of the asType() method.
virtual ~Variable() {} virtual ~Variable() {}
private: protected:
Variable(Type Ty, const CfgNode *Node, SizeT Index, const IceString &Name) Variable(OperandKind K, Type Ty, const CfgNode *Node, SizeT Index,
: Operand(kVariable, Ty), Number(Index), Name(Name), DefInst(NULL), const IceString &Name)
DefNode(Node), IsMultidef(false), IsArgument(false), StackOffset(0), : Operand(K, Ty), Number(Index), Name(Name), DefInst(NULL), DefNode(Node),
IsMultidef(false), IsArgument(false), StackOffset(0),
RegNum(NoRegister), RegNumTmp(NoRegister), Weight(1), RegNum(NoRegister), RegNumTmp(NoRegister), Weight(1),
RegisterPreference(NULL), AllowRegisterOverlap(false), LoVar(NULL), RegisterPreference(NULL), AllowRegisterOverlap(false), LoVar(NULL),
HiVar(NULL) { HiVar(NULL) {
...@@ -448,8 +458,6 @@ private: ...@@ -448,8 +458,6 @@ private:
Vars[0] = this; Vars[0] = this;
NumVars = 1; NumVars = 1;
} }
Variable(const Variable &) LLVM_DELETED_FUNCTION;
Variable &operator=(const Variable &) LLVM_DELETED_FUNCTION;
// Number is unique across all variables, and is used as a // Number is unique across all variables, and is used as a
// (bit)vector index for liveness analysis. // (bit)vector index for liveness analysis.
const SizeT Number; const SizeT Number;
......
...@@ -29,7 +29,7 @@ namespace Ice { ...@@ -29,7 +29,7 @@ namespace Ice {
// two interfering variables to share the same register in certain // two interfering variables to share the same register in certain
// cases. // cases.
// //
// Requires running Cfg::liveness(Liveness_RangesFull) in // Requires running Cfg::liveness(Liveness_Intervals) in
// preparation. Results are assigned to Variable::RegNum for each // preparation. Results are assigned to Variable::RegNum for each
// Variable. // Variable.
void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) { void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) {
...@@ -39,7 +39,7 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) { ...@@ -39,7 +39,7 @@ void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) {
Inactive.clear(); Inactive.clear();
Active.clear(); Active.clear();
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
Func->setCurrentNode(NULL); Func->resetCurrentNode();
// 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
...@@ -447,7 +447,7 @@ void LinearScan::dump(Cfg *Func) const { ...@@ -447,7 +447,7 @@ void LinearScan::dump(Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
if (!Func->getContext()->isVerbose(IceV_LinearScan)) if (!Func->getContext()->isVerbose(IceV_LinearScan))
return; return;
Func->setCurrentNode(NULL); Func->resetCurrentNode();
Str << "**** Current regalloc state:\n"; Str << "**** Current regalloc state:\n";
Str << "++++++ Handled:\n"; Str << "++++++ Handled:\n";
for (UnorderedRanges::const_iterator I = Handled.begin(), E = Handled.end(); for (UnorderedRanges::const_iterator I = Handled.begin(), E = Handled.end();
......
...@@ -700,8 +700,7 @@ void TargetX8632::addProlog(CfgNode *Node) { ...@@ -700,8 +700,7 @@ void TargetX8632::addProlog(CfgNode *Node) {
RegsUsed = llvm::SmallBitVector(CalleeSaves.size()); RegsUsed = llvm::SmallBitVector(CalleeSaves.size());
const VarList &Variables = Func->getVariables(); const VarList &Variables = Func->getVariables();
const VarList &Args = Func->getArgs(); const VarList &Args = Func->getArgs();
VarList SpilledVariables, SortedSpilledVariables, VarList SpilledVariables, SortedSpilledVariables, VariablesLinkedToSpillSlots;
VariablesLinkedToSpillSplots;
// If there is a separate locals area, this specifies the alignment // If there is a separate locals area, this specifies the alignment
// for it. // for it.
...@@ -725,12 +724,11 @@ void TargetX8632::addProlog(CfgNode *Node) { ...@@ -725,12 +724,11 @@ void TargetX8632::addProlog(CfgNode *Node) {
continue; continue;
// A spill slot linked to a variable with a stack slot should reuse // A spill slot linked to a variable with a stack slot should reuse
// that stack slot. // that stack slot.
if (Var->getWeight() == RegWeight::Zero && Var->getRegisterOverlap()) { if (SpillVariable *SpillVar = llvm::dyn_cast<SpillVariable>(Var)) {
if (Variable *Linked = Var->getPreferredRegister()) { assert(Var->getWeight() == RegWeight::Zero);
if (!Linked->hasReg()) { if (!SpillVar->getLinkedTo()->hasReg()) {
VariablesLinkedToSpillSplots.push_back(Var); VariablesLinkedToSpillSlots.push_back(Var);
continue; continue;
}
} }
} }
SpilledVariables.push_back(Var); SpilledVariables.push_back(Var);
...@@ -878,11 +876,11 @@ void TargetX8632::addProlog(CfgNode *Node) { ...@@ -878,11 +876,11 @@ void TargetX8632::addProlog(CfgNode *Node) {
// Assign stack offsets to variables that have been linked to spilled // Assign stack offsets to variables that have been linked to spilled
// variables. // variables.
for (VarList::const_iterator I = VariablesLinkedToSpillSplots.begin(), for (VarList::const_iterator I = VariablesLinkedToSpillSlots.begin(),
E = VariablesLinkedToSpillSplots.end(); E = VariablesLinkedToSpillSlots.end();
I != E; ++I) { I != E; ++I) {
Variable *Var = *I; Variable *Var = *I;
Variable *Linked = Var->getPreferredRegister(); Variable *Linked = (llvm::cast<SpillVariable>(Var))->getLinkedTo();
Var->setStackOffset(Linked->getStackOffset()); Var->setStackOffset(Linked->getStackOffset());
} }
...@@ -2278,9 +2276,11 @@ void TargetX8632::lowerCast(const InstCast *Inst) { ...@@ -2278,9 +2276,11 @@ void TargetX8632::lowerCast(const InstCast *Inst) {
Variable *T = NULL; Variable *T = NULL;
// TODO: Should be able to force a spill setup by calling legalize() with // TODO: Should be able to force a spill setup by calling legalize() with
// Legal_Mem and not Legal_Reg or Legal_Imm. // Legal_Mem and not Legal_Reg or Legal_Imm.
Variable *Spill = Func->makeVariable(SrcType, Context.getNode()); SpillVariable *SpillVar =
Func->makeVariable<SpillVariable>(SrcType, Context.getNode());
SpillVar->setLinkedTo(Dest);
Variable *Spill = SpillVar;
Spill->setWeight(RegWeight::Zero); Spill->setWeight(RegWeight::Zero);
Spill->setPreferredRegister(Dest, true);
_mov(T, Src0RM); _mov(T, Src0RM);
_mov(Spill, T); _mov(Spill, T);
_mov(Dest, Spill); _mov(Dest, Spill);
...@@ -2294,9 +2294,11 @@ void TargetX8632::lowerCast(const InstCast *Inst) { ...@@ -2294,9 +2294,11 @@ void TargetX8632::lowerCast(const InstCast *Inst) {
// a_lo.i32 = t_lo.i32 // a_lo.i32 = t_lo.i32
// t_hi.i32 = hi(s.f64) // t_hi.i32 = hi(s.f64)
// a_hi.i32 = t_hi.i32 // a_hi.i32 = t_hi.i32
Variable *Spill = Func->makeVariable(IceType_f64, Context.getNode()); SpillVariable *SpillVar =
Func->makeVariable<SpillVariable>(IceType_f64, Context.getNode());
SpillVar->setLinkedTo(llvm::dyn_cast<Variable>(Src0RM));
Variable *Spill = SpillVar;
Spill->setWeight(RegWeight::Zero); Spill->setWeight(RegWeight::Zero);
Spill->setPreferredRegister(llvm::dyn_cast<Variable>(Src0RM), true);
_movq(Spill, Src0RM); _movq(Spill, Src0RM);
Variable *DestLo = llvm::cast<Variable>(loOperand(Dest)); Variable *DestLo = llvm::cast<Variable>(loOperand(Dest));
...@@ -2323,9 +2325,11 @@ void TargetX8632::lowerCast(const InstCast *Inst) { ...@@ -2323,9 +2325,11 @@ void TargetX8632::lowerCast(const InstCast *Inst) {
// t_hi.i32 = b_hi.i32 // t_hi.i32 = b_hi.i32
// hi(s.f64) = t_hi.i32 // hi(s.f64) = t_hi.i32
// a.f64 = s.f64 // a.f64 = s.f64
Variable *Spill = Func->makeVariable(IceType_f64, Context.getNode()); SpillVariable *SpillVar =
Func->makeVariable<SpillVariable>(IceType_f64, Context.getNode());
SpillVar->setLinkedTo(Dest);
Variable *Spill = SpillVar;
Spill->setWeight(RegWeight::Zero); Spill->setWeight(RegWeight::Zero);
Spill->setPreferredRegister(Dest, true);
Variable *T_Lo = NULL, *T_Hi = NULL; Variable *T_Lo = NULL, *T_Hi = NULL;
VariableSplit *SpillLo = VariableSplit *SpillLo =
...@@ -3720,7 +3724,7 @@ bool matchOffsetBase(Variable *&Base, int32_t &Offset, const Inst *&Reason) { ...@@ -3720,7 +3724,7 @@ bool matchOffsetBase(Variable *&Base, int32_t &Offset, const Inst *&Reason) {
void computeAddressOpt(Cfg *Func, const Inst *Instr, Variable *&Base, void computeAddressOpt(Cfg *Func, const Inst *Instr, Variable *&Base,
Variable *&Index, uint16_t &Shift, int32_t &Offset) { Variable *&Index, uint16_t &Shift, int32_t &Offset) {
Func->setCurrentNode(NULL); Func->resetCurrentNode();
if (Func->getContext()->isVerbose(IceV_AddrOpt)) { if (Func->getContext()->isVerbose(IceV_AddrOpt)) {
Ostream &Str = Func->getContext()->getStrDump(); Ostream &Str = Func->getContext()->getStrDump();
Str << "\nStarting computeAddressOpt for instruction:\n "; Str << "\nStarting computeAddressOpt for instruction:\n ";
......
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