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
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,
const IceString &Name) {
- SizeT Index = Variables.size();
- Variables.push_back(Variable::create(this, Ty, Node, Index, Name));
- return Variables[Index];
+ return makeVariable<Variable>(Ty, Node, Name);
}
void Cfg::addArg(Variable *Arg) {
}
Str << ") {\n";
}
- setCurrentNode(NULL);
+ resetCurrentNode();
if (getContext()->isVerbose(IceV_Liveness)) {
// Print summary info about variables
for (VarList::const_iterator I = Variables.begin(), E = Variables.end();
InstNumberT newInstNumber() { return NextInstNumber++; }
// 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,
const IceString &Name = "");
SizeT getNumVariables() const { return Variables.size(); }
// Manage the CurrentNode field, which is used for validating the
// Variable::DefNode field during dumping/emitting.
void setCurrentNode(const CfgNode *Node) { CurrentNode = Node; }
+ void resetCurrentNode() { setCurrentNode(NULL); }
const CfgNode *getCurrentNode() const { return CurrentNode; }
void emit();
// CurrentNode is maintained during dumping/emitting just for
// validating Variable::DefNode. Normally, a traversal over
// CfgNodes maintains this, but before global operations like
- // register allocation, setCurrentNode(NULL) should be called to
- // avoid spurious validation failures.
+ // register allocation, resetCurrentNode() should be called to avoid
+ // spurious validation failures.
const CfgNode *CurrentNode;
Cfg(const Cfg &) LLVM_DELETED_FUNCTION;
// This is a fatal liveness consistency error. Print some
// diagnostics and abort.
Ostream &Str = Func->getContext()->getStrDump();
- Func->setCurrentNode(NULL);
+ Func->resetCurrentNode();
Str << "LiveOrig-Live =";
for (SizeT i = Live.size(); i < LiveOrig.size(); ++i) {
if (LiveOrig.test(i)) {
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 {
public:
enum InstKindX8632 {
}
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.StackOffset = StackOffset;
return V;
class Operand {
public:
+ static const size_t MaxTargetKinds = 10;
enum OperandKind {
kConst_Base,
kConstInteger32,
kConstDouble,
kConstRelocatable,
kConstUndef,
- kConst_Num,
+ kConst_Target, // leave space for target-specific constant kinds
+ kConst_Num = kConst_Target + MaxTargetKinds,
kVariable,
+ kVariable_Target, // leave space for target-specific variable kinds
+ kVariable_Num = kVariable_Target + MaxTargetKinds,
// Target-specific operand classes use kTarget as the starting
// point for their Kind enum space.
kTarget
// stack-allocated. If it is register-allocated, it will ultimately
// have a non-negative RegNum field.
class Variable : public Operand {
+ Variable(const Variable &) LLVM_DELETED_FUNCTION;
+ Variable &operator=(const Variable &) LLVM_DELETED_FUNCTION;
+
public:
static Variable *create(Cfg *Func, Type Ty, const CfgNode *Node, SizeT Index,
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; }
virtual void dump(const Cfg *Func, Ostream &Str) const;
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.
virtual ~Variable() {}
-private:
- Variable(Type Ty, const CfgNode *Node, SizeT Index, const IceString &Name)
- : Operand(kVariable, Ty), Number(Index), Name(Name), DefInst(NULL),
- DefNode(Node), IsMultidef(false), IsArgument(false), StackOffset(0),
+protected:
+ Variable(OperandKind K, Type Ty, const CfgNode *Node, SizeT Index,
+ const IceString &Name)
+ : Operand(K, Ty), Number(Index), Name(Name), DefInst(NULL), DefNode(Node),
+ IsMultidef(false), IsArgument(false), StackOffset(0),
RegNum(NoRegister), RegNumTmp(NoRegister), Weight(1),
RegisterPreference(NULL), AllowRegisterOverlap(false), LoVar(NULL),
HiVar(NULL) {
Vars[0] = this;
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
// (bit)vector index for liveness analysis.
const SizeT Number;
// two interfering variables to share the same register in certain
// cases.
//
-// Requires running Cfg::liveness(Liveness_RangesFull) in
+// Requires running Cfg::liveness(Liveness_Intervals) in
// preparation. Results are assigned to Variable::RegNum for each
// Variable.
void LinearScan::scan(const llvm::SmallBitVector &RegMaskFull) {
Inactive.clear();
Active.clear();
Ostream &Str = Func->getContext()->getStrDump();
- Func->setCurrentNode(NULL);
+ Func->resetCurrentNode();
// Gather the live ranges of all variables and add them to the
// Unhandled set. TODO: Unhandled is a set<> which is based on a
Ostream &Str = Func->getContext()->getStrDump();
if (!Func->getContext()->isVerbose(IceV_LinearScan))
return;
- Func->setCurrentNode(NULL);
+ Func->resetCurrentNode();
Str << "**** Current regalloc state:\n";
Str << "++++++ Handled:\n";
for (UnorderedRanges::const_iterator I = Handled.begin(), E = Handled.end();
RegsUsed = llvm::SmallBitVector(CalleeSaves.size());
const VarList &Variables = Func->getVariables();
const VarList &Args = Func->getArgs();
- VarList SpilledVariables, SortedSpilledVariables,
- VariablesLinkedToSpillSplots;
+ VarList SpilledVariables, SortedSpilledVariables, VariablesLinkedToSpillSlots;
// If there is a separate locals area, this specifies the alignment
// for it.
continue;
// A spill slot linked to a variable with a stack slot should reuse
// that stack slot.
- if (Var->getWeight() == RegWeight::Zero && Var->getRegisterOverlap()) {
- if (Variable *Linked = Var->getPreferredRegister()) {
- if (!Linked->hasReg()) {
- VariablesLinkedToSpillSplots.push_back(Var);
- continue;
- }
+ if (SpillVariable *SpillVar = llvm::dyn_cast<SpillVariable>(Var)) {
+ assert(Var->getWeight() == RegWeight::Zero);
+ if (!SpillVar->getLinkedTo()->hasReg()) {
+ VariablesLinkedToSpillSlots.push_back(Var);
+ continue;
}
}
SpilledVariables.push_back(Var);
// Assign stack offsets to variables that have been linked to spilled
// variables.
- for (VarList::const_iterator I = VariablesLinkedToSpillSplots.begin(),
- E = VariablesLinkedToSpillSplots.end();
+ for (VarList::const_iterator I = VariablesLinkedToSpillSlots.begin(),
+ E = VariablesLinkedToSpillSlots.end();
I != E; ++I) {
Variable *Var = *I;
- Variable *Linked = Var->getPreferredRegister();
+ Variable *Linked = (llvm::cast<SpillVariable>(Var))->getLinkedTo();
Var->setStackOffset(Linked->getStackOffset());
}
Variable *T = NULL;
// TODO: Should be able to force a spill setup by calling legalize() with
// 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->setPreferredRegister(Dest, true);
_mov(T, Src0RM);
_mov(Spill, T);
_mov(Dest, Spill);
// a_lo.i32 = t_lo.i32
// t_hi.i32 = hi(s.f64)
// 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->setPreferredRegister(llvm::dyn_cast<Variable>(Src0RM), true);
_movq(Spill, Src0RM);
Variable *DestLo = llvm::cast<Variable>(loOperand(Dest));
// t_hi.i32 = b_hi.i32
// hi(s.f64) = t_hi.i32
// 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->setPreferredRegister(Dest, true);
Variable *T_Lo = NULL, *T_Hi = NULL;
VariableSplit *SpillLo =
void computeAddressOpt(Cfg *Func, const Inst *Instr, Variable *&Base,
Variable *&Index, uint16_t &Shift, int32_t &Offset) {
- Func->setCurrentNode(NULL);
+ Func->resetCurrentNode();
if (Func->getContext()->isVerbose(IceV_AddrOpt)) {
Ostream &Str = Func->getContext()->getStrDump();
Str << "\nStarting computeAddressOpt for instruction:\n ";