OLD | NEW |
1 //===- subzero/src/IceInst.cpp - High-level instruction implementation ----===// | 1 //===- subzero/src/IceInst.cpp - High-level instruction implementation ----===// |
2 // | 2 // |
3 // The Subzero Code Generator | 3 // The Subzero Code Generator |
4 // | 4 // |
5 // This file is distributed under the University of Illinois Open Source | 5 // This file is distributed under the University of Illinois Open Source |
6 // License. See LICENSE.TXT for details. | 6 // License. See LICENSE.TXT for details. |
7 // | 7 // |
8 //===----------------------------------------------------------------------===// | 8 //===----------------------------------------------------------------------===// |
9 /// | 9 /// |
10 /// \file | 10 /// \file |
11 /// This file implements the Inst class, primarily the various | 11 /// This file implements the Inst class, primarily the various subclass |
12 /// subclass constructors and dump routines. | 12 /// constructors and dump routines. |
13 /// | 13 /// |
14 //===----------------------------------------------------------------------===// | 14 //===----------------------------------------------------------------------===// |
15 | 15 |
16 #include "IceInst.h" | 16 #include "IceInst.h" |
17 | 17 |
18 #include "IceCfg.h" | 18 #include "IceCfg.h" |
19 #include "IceCfgNode.h" | 19 #include "IceCfgNode.h" |
20 #include "IceInstVarIter.h" | 20 #include "IceInstVarIter.h" |
21 #include "IceLiveness.h" | 21 #include "IceLiveness.h" |
22 #include "IceOperand.h" | 22 #include "IceOperand.h" |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
75 | 75 |
76 Inst::Inst(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest) | 76 Inst::Inst(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest) |
77 : Kind(Kind), Number(Func->newInstNumber()), Dest(Dest), MaxSrcs(MaxSrcs), | 77 : Kind(Kind), Number(Func->newInstNumber()), Dest(Dest), MaxSrcs(MaxSrcs), |
78 Srcs(Func->allocateArrayOf<Operand *>(MaxSrcs)), LiveRangesEnded(0) {} | 78 Srcs(Func->allocateArrayOf<Operand *>(MaxSrcs)), LiveRangesEnded(0) {} |
79 | 79 |
80 // Assign the instruction a new number. | 80 // Assign the instruction a new number. |
81 void Inst::renumber(Cfg *Func) { | 81 void Inst::renumber(Cfg *Func) { |
82 Number = isDeleted() ? NumberDeleted : Func->newInstNumber(); | 82 Number = isDeleted() ? NumberDeleted : Func->newInstNumber(); |
83 } | 83 } |
84 | 84 |
85 // Delete the instruction if its tentative Dead flag is still set | 85 // Delete the instruction if its tentative Dead flag is still set after |
86 // after liveness analysis. | 86 // liveness analysis. |
87 void Inst::deleteIfDead() { | 87 void Inst::deleteIfDead() { |
88 if (Dead) | 88 if (Dead) |
89 setDeleted(); | 89 setDeleted(); |
90 } | 90 } |
91 | 91 |
92 // If Src is a Variable, it returns true if this instruction ends | 92 // If Src is a Variable, it returns true if this instruction ends Src's live |
93 // Src's live range. Otherwise, returns false. | 93 // range. Otherwise, returns false. |
94 bool Inst::isLastUse(const Operand *TestSrc) const { | 94 bool Inst::isLastUse(const Operand *TestSrc) const { |
95 if (LiveRangesEnded == 0) | 95 if (LiveRangesEnded == 0) |
96 return false; // early-exit optimization | 96 return false; // early-exit optimization |
97 if (const Variable *TestVar = llvm::dyn_cast<const Variable>(TestSrc)) { | 97 if (const Variable *TestVar = llvm::dyn_cast<const Variable>(TestSrc)) { |
98 LREndedBits Mask = LiveRangesEnded; | 98 LREndedBits Mask = LiveRangesEnded; |
99 FOREACH_VAR_IN_INST(Var, *this) { | 99 FOREACH_VAR_IN_INST(Var, *this) { |
100 if (Var == TestVar) { | 100 if (Var == TestVar) { |
101 // We've found where the variable is used in the instruction. | 101 // We've found where the variable is used in the instruction. |
102 return Mask & 1; | 102 return Mask & 1; |
103 } | 103 } |
104 Mask >>= 1; | 104 Mask >>= 1; |
105 if (Mask == 0) | 105 if (Mask == 0) |
106 return false; // another early-exit optimization | 106 return false; // another early-exit optimization |
107 } | 107 } |
108 } | 108 } |
109 return false; | 109 return false; |
110 } | 110 } |
111 | 111 |
112 // Given an instruction like: | 112 // Given an instruction like: |
113 // a = b + c + [x,y] + e | 113 // a = b + c + [x,y] + e |
114 // which was created from OrigInst: | 114 // which was created from OrigInst: |
115 // a = b + c + d + e | 115 // a = b + c + d + e |
116 // with SpliceAssn spliced in: | 116 // with SpliceAssn spliced in: |
117 // d = [x,y] | 117 // d = [x,y] |
118 // | 118 // |
119 // Reconstruct the LiveRangesEnded bitmask in this instruction by | 119 // Reconstruct the LiveRangesEnded bitmask in this instruction by combining the |
120 // combining the LiveRangesEnded values of OrigInst and SpliceAssn. | 120 // LiveRangesEnded values of OrigInst and SpliceAssn. If operands d and [x,y] |
121 // If operands d and [x,y] contain a different number of variables, | 121 // contain a different number of variables, then the bitmask position for e may |
122 // then the bitmask position for e may be different in OrigInst and | 122 // be different in OrigInst and the current instruction, requiring extra shifts |
123 // the current instruction, requiring extra shifts and masks in the | 123 // and masks in the computation. In the example above, OrigInst has variable e |
124 // computation. In the example above, OrigInst has variable e in bit | 124 // in bit position 3, whereas the current instruction has e in bit position 4 |
125 // position 3, whereas the current instruction has e in bit position 4 | |
126 // because [x,y] consumes 2 bitmask slots while d only consumed 1. | 125 // because [x,y] consumes 2 bitmask slots while d only consumed 1. |
127 // | 126 // |
128 // Additionally, set HasSideEffects if either OrigInst or SpliceAssn | 127 // Additionally, set HasSideEffects if either OrigInst or SpliceAssn have |
129 // have HasSideEffects set. | 128 // HasSideEffects set. |
130 void Inst::spliceLivenessInfo(Inst *OrigInst, Inst *SpliceAssn) { | 129 void Inst::spliceLivenessInfo(Inst *OrigInst, Inst *SpliceAssn) { |
131 HasSideEffects |= OrigInst->HasSideEffects; | 130 HasSideEffects |= OrigInst->HasSideEffects; |
132 HasSideEffects |= SpliceAssn->HasSideEffects; | 131 HasSideEffects |= SpliceAssn->HasSideEffects; |
133 // Find the bitmask index of SpliceAssn's dest within OrigInst. | 132 // Find the bitmask index of SpliceAssn's dest within OrigInst. |
134 Variable *SpliceDest = SpliceAssn->getDest(); | 133 Variable *SpliceDest = SpliceAssn->getDest(); |
135 SizeT Index = 0; | 134 SizeT Index = 0; |
136 for (SizeT I = 0; I < OrigInst->getSrcSize(); ++I) { | 135 for (SizeT I = 0; I < OrigInst->getSrcSize(); ++I) { |
137 Operand *Src = OrigInst->getSrc(I); | 136 Operand *Src = OrigInst->getSrc(I); |
138 if (Src == SpliceDest) { | 137 if (Src == SpliceDest) { |
139 LREndedBits LeftMask = OrigInst->LiveRangesEnded & ((1 << Index) - 1); | 138 LREndedBits LeftMask = OrigInst->LiveRangesEnded & ((1 << Index) - 1); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 LiveBegin->push_back(std::make_pair(VarNum, InstNumber)); | 176 LiveBegin->push_back(std::make_pair(VarNum, InstNumber)); |
178 } | 177 } |
179 } | 178 } |
180 } else { | 179 } else { |
181 if (!hasSideEffects()) | 180 if (!hasSideEffects()) |
182 Dead = true; | 181 Dead = true; |
183 } | 182 } |
184 } | 183 } |
185 if (Dead) | 184 if (Dead) |
186 return false; | 185 return false; |
187 // Phi arguments only get added to Live in the predecessor node, but | 186 // Phi arguments only get added to Live in the predecessor node, but we still |
188 // we still need to update LiveRangesEnded. | 187 // need to update LiveRangesEnded. |
189 bool IsPhi = llvm::isa<InstPhi>(this); | 188 bool IsPhi = llvm::isa<InstPhi>(this); |
190 resetLastUses(); | 189 resetLastUses(); |
191 FOREACH_VAR_IN_INST(Var, *this) { | 190 FOREACH_VAR_IN_INST(Var, *this) { |
192 SizeT VarNum = Liveness->getLiveIndex(Var->getIndex()); | 191 SizeT VarNum = Liveness->getLiveIndex(Var->getIndex()); |
193 if (!Live[VarNum]) { | 192 if (!Live[VarNum]) { |
194 setLastUse(IndexOfVarInInst(Var)); | 193 setLastUse(IndexOfVarInInst(Var)); |
195 if (!IsPhi) { | 194 if (!IsPhi) { |
196 Live[VarNum] = true; | 195 Live[VarNum] = true; |
197 // For a variable in SSA form, its live range can end at most once in a | 196 // For a variable in SSA form, its live range can end at most once in a |
198 // basic block. However, after lowering to two-address instructions, we | 197 // basic block. However, after lowering to two-address instructions, we |
199 // end up with sequences like "t=b;t+=c;a=t" where t's live range begins | 198 // end up with sequences like "t=b;t+=c;a=t" where t's live range |
200 // and ends twice. ICE only allows a variable to have a single liveness | 199 // begins and ends twice. ICE only allows a variable to have a single |
201 // interval in a basic block (except for blocks where a variable is | 200 // liveness interval in a basic block (except for blocks where a |
202 // live-in and live-out but there is a gap in the middle). Therefore, | 201 // variable is live-in and live-out but there is a gap in the middle). |
203 // this lowered sequence needs to represent a single conservative live | 202 // Therefore, this lowered sequence needs to represent a single |
204 // range for t. Since the instructions are being traversed backwards, | 203 // conservative live range for t. Since the instructions are being |
205 // we make sure LiveEnd is only set once by setting it only when | 204 // traversed backwards, we make sure LiveEnd is only set once by |
206 // LiveEnd[VarNum]==0 (sentinel value). Note that it's OK to set | 205 // setting it only when LiveEnd[VarNum]==0 (sentinel value). Note that |
207 // LiveBegin multiple times because of the backwards traversal. | 206 // it's OK to set LiveBegin multiple times because of the backwards |
| 207 // traversal. |
208 if (LiveEnd && Liveness->getRangeMask(Var->getIndex())) { | 208 if (LiveEnd && Liveness->getRangeMask(Var->getIndex())) { |
209 // Ideally, we would verify that VarNum wasn't already added in this | 209 // Ideally, we would verify that VarNum wasn't already added in this |
210 // block, but this can't be done very efficiently with LiveEnd as a | 210 // block, but this can't be done very efficiently with LiveEnd as a |
211 // vector. Instead, livenessPostprocess() verifies this after the | 211 // vector. Instead, livenessPostprocess() verifies this after the |
212 // vector has been sorted. | 212 // vector has been sorted. |
213 LiveEnd->push_back(std::make_pair(VarNum, InstNumber)); | 213 LiveEnd->push_back(std::make_pair(VarNum, InstNumber)); |
214 } | 214 } |
215 } | 215 } |
216 } | 216 } |
217 } | 217 } |
218 return true; | 218 return true; |
219 } | 219 } |
220 | 220 |
221 InstAlloca::InstAlloca(Cfg *Func, Operand *ByteCount, uint32_t AlignInBytes, | 221 InstAlloca::InstAlloca(Cfg *Func, Operand *ByteCount, uint32_t AlignInBytes, |
(...skipping 20 matching lines...) Expand all Loading... |
242 | 242 |
243 bool InstArithmetic::isCommutative() const { | 243 bool InstArithmetic::isCommutative() const { |
244 return InstArithmeticAttributes[getOp()].IsCommutative; | 244 return InstArithmeticAttributes[getOp()].IsCommutative; |
245 } | 245 } |
246 | 246 |
247 InstAssign::InstAssign(Cfg *Func, Variable *Dest, Operand *Source) | 247 InstAssign::InstAssign(Cfg *Func, Variable *Dest, Operand *Source) |
248 : InstHighLevel(Func, Inst::Assign, 1, Dest) { | 248 : InstHighLevel(Func, Inst::Assign, 1, Dest) { |
249 addSource(Source); | 249 addSource(Source); |
250 } | 250 } |
251 | 251 |
252 // If TargetTrue==TargetFalse, we turn it into an unconditional | 252 // If TargetTrue==TargetFalse, we turn it into an unconditional branch. This |
253 // branch. This ensures that, along with the 'switch' instruction | 253 // ensures that, along with the 'switch' instruction semantics, there is at |
254 // semantics, there is at most one edge from one node to another. | 254 // most one edge from one node to another. |
255 InstBr::InstBr(Cfg *Func, Operand *Source, CfgNode *TargetTrue_, | 255 InstBr::InstBr(Cfg *Func, Operand *Source, CfgNode *TargetTrue_, |
256 CfgNode *TargetFalse_) | 256 CfgNode *TargetFalse_) |
257 : InstHighLevel(Func, Inst::Br, 1, nullptr), TargetFalse(TargetFalse_), | 257 : InstHighLevel(Func, Inst::Br, 1, nullptr), TargetFalse(TargetFalse_), |
258 TargetTrue(TargetTrue_) { | 258 TargetTrue(TargetTrue_) { |
259 if (TargetTrue == TargetFalse) { | 259 if (TargetTrue == TargetFalse) { |
260 TargetTrue = nullptr; // turn into unconditional version | 260 TargetTrue = nullptr; // turn into unconditional version |
261 } else { | 261 } else { |
262 addSource(Source); | 262 addSource(Source); |
263 } | 263 } |
264 } | 264 } |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 InstLoad::InstLoad(Cfg *Func, Variable *Dest, Operand *SourceAddr) | 327 InstLoad::InstLoad(Cfg *Func, Variable *Dest, Operand *SourceAddr) |
328 : InstHighLevel(Func, Inst::Load, 1, Dest) { | 328 : InstHighLevel(Func, Inst::Load, 1, Dest) { |
329 addSource(SourceAddr); | 329 addSource(SourceAddr); |
330 } | 330 } |
331 | 331 |
332 InstPhi::InstPhi(Cfg *Func, SizeT MaxSrcs, Variable *Dest) | 332 InstPhi::InstPhi(Cfg *Func, SizeT MaxSrcs, Variable *Dest) |
333 : InstHighLevel(Func, Phi, MaxSrcs, Dest) { | 333 : InstHighLevel(Func, Phi, MaxSrcs, Dest) { |
334 Labels = Func->allocateArrayOf<CfgNode *>(MaxSrcs); | 334 Labels = Func->allocateArrayOf<CfgNode *>(MaxSrcs); |
335 } | 335 } |
336 | 336 |
337 // TODO: A Switch instruction (and maybe others) can add duplicate | 337 // TODO: A Switch instruction (and maybe others) can add duplicate edges. We |
338 // edges. We may want to de-dup Phis and validate consistency (i.e., | 338 // may want to de-dup Phis and validate consistency (i.e., the source operands |
339 // the source operands are the same for duplicate edges), though it | 339 // are the same for duplicate edges), though it seems the current lowering code |
340 // seems the current lowering code is OK with this situation. | 340 // is OK with this situation. |
341 void InstPhi::addArgument(Operand *Source, CfgNode *Label) { | 341 void InstPhi::addArgument(Operand *Source, CfgNode *Label) { |
342 Labels[getSrcSize()] = Label; | 342 Labels[getSrcSize()] = Label; |
343 addSource(Source); | 343 addSource(Source); |
344 } | 344 } |
345 | 345 |
346 // Find the source operand corresponding to the incoming edge for the | 346 // Find the source operand corresponding to the incoming edge for the given |
347 // given node. TODO: This uses a linear-time search, which could be | 347 // node. TODO: This uses a linear-time search, which could be improved if it |
348 // improved if it becomes a problem. | 348 // becomes a problem. |
349 Operand *InstPhi::getOperandForTarget(CfgNode *Target) const { | 349 Operand *InstPhi::getOperandForTarget(CfgNode *Target) const { |
350 for (SizeT I = 0; I < getSrcSize(); ++I) { | 350 for (SizeT I = 0; I < getSrcSize(); ++I) { |
351 if (Labels[I] == Target) | 351 if (Labels[I] == Target) |
352 return getSrc(I); | 352 return getSrc(I); |
353 } | 353 } |
354 llvm_unreachable("Phi target not found"); | 354 llvm_unreachable("Phi target not found"); |
355 return nullptr; | 355 return nullptr; |
356 } | 356 } |
357 | 357 |
358 // Updates liveness for a particular operand based on the given | 358 // Updates liveness for a particular operand based on the given predecessor |
359 // predecessor edge. Doesn't mark the operand as live if the Phi | 359 // edge. Doesn't mark the operand as live if the Phi instruction is dead or |
360 // instruction is dead or deleted. | 360 // deleted. |
361 void InstPhi::livenessPhiOperand(LivenessBV &Live, CfgNode *Target, | 361 void InstPhi::livenessPhiOperand(LivenessBV &Live, CfgNode *Target, |
362 Liveness *Liveness) { | 362 Liveness *Liveness) { |
363 if (isDeleted() || Dead) | 363 if (isDeleted() || Dead) |
364 return; | 364 return; |
365 for (SizeT I = 0; I < getSrcSize(); ++I) { | 365 for (SizeT I = 0; I < getSrcSize(); ++I) { |
366 if (Labels[I] == Target) { | 366 if (Labels[I] == Target) { |
367 if (Variable *Var = llvm::dyn_cast<Variable>(getSrc(I))) { | 367 if (Variable *Var = llvm::dyn_cast<Variable>(getSrc(I))) { |
368 SizeT SrcIndex = Liveness->getLiveIndex(Var->getIndex()); | 368 SizeT SrcIndex = Liveness->getLiveIndex(Var->getIndex()); |
369 if (!Live[SrcIndex]) { | 369 if (!Live[SrcIndex]) { |
370 setLastUse(I); | 370 setLastUse(I); |
371 Live[SrcIndex] = true; | 371 Live[SrcIndex] = true; |
372 } | 372 } |
373 } | 373 } |
374 return; | 374 return; |
375 } | 375 } |
376 } | 376 } |
377 llvm_unreachable("Phi operand not found for specified target node"); | 377 llvm_unreachable("Phi operand not found for specified target node"); |
378 } | 378 } |
379 | 379 |
380 // Change "a=phi(...)" to "a_phi=phi(...)" and return a new | 380 // Change "a=phi(...)" to "a_phi=phi(...)" and return a new instruction |
381 // instruction "a=a_phi". | 381 // "a=a_phi". |
382 Inst *InstPhi::lower(Cfg *Func) { | 382 Inst *InstPhi::lower(Cfg *Func) { |
383 Variable *Dest = getDest(); | 383 Variable *Dest = getDest(); |
384 assert(Dest); | 384 assert(Dest); |
385 Variable *NewSrc = Func->makeVariable(Dest->getType()); | 385 Variable *NewSrc = Func->makeVariable(Dest->getType()); |
386 if (BuildDefs::dump()) | 386 if (BuildDefs::dump()) |
387 NewSrc->setName(Func, Dest->getName(Func) + "_phi"); | 387 NewSrc->setName(Func, Dest->getName(Func) + "_phi"); |
388 this->Dest = NewSrc; | 388 this->Dest = NewSrc; |
389 return InstAssign::create(Func, Dest, NewSrc); | 389 return InstAssign::create(Func, Dest, NewSrc); |
390 } | 390 } |
391 | 391 |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
555 dumpDest(Func); | 555 dumpDest(Func); |
556 Str << " =~ "; | 556 Str << " =~ "; |
557 dumpSources(Func); | 557 dumpSources(Func); |
558 } | 558 } |
559 | 559 |
560 void Inst::dumpExtras(const Cfg *Func) const { | 560 void Inst::dumpExtras(const Cfg *Func) const { |
561 if (!BuildDefs::dump()) | 561 if (!BuildDefs::dump()) |
562 return; | 562 return; |
563 Ostream &Str = Func->getContext()->getStrDump(); | 563 Ostream &Str = Func->getContext()->getStrDump(); |
564 bool First = true; | 564 bool First = true; |
565 // Print "LIVEEND={a,b,c}" for all source operands whose live ranges | 565 // Print "LIVEEND={a,b,c}" for all source operands whose live ranges are |
566 // are known to end at this instruction. | 566 // known to end at this instruction. |
567 if (Func->isVerbose(IceV_Liveness)) { | 567 if (Func->isVerbose(IceV_Liveness)) { |
568 FOREACH_VAR_IN_INST(Var, *this) { | 568 FOREACH_VAR_IN_INST(Var, *this) { |
569 if (isLastUse(Var)) { | 569 if (isLastUse(Var)) { |
570 if (First) | 570 if (First) |
571 Str << " // LIVEEND={"; | 571 Str << " // LIVEEND={"; |
572 else | 572 else |
573 Str << ","; | 573 Str << ","; |
574 Var->dump(Func); | 574 Var->dump(Func); |
575 First = false; | 575 First = false; |
576 } | 576 } |
(...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
879 void InstBundleUnlock::dump(const Cfg *Func) const { | 879 void InstBundleUnlock::dump(const Cfg *Func) const { |
880 if (!BuildDefs::dump()) | 880 if (!BuildDefs::dump()) |
881 return; | 881 return; |
882 Ostream &Str = Func->getContext()->getStrDump(); | 882 Ostream &Str = Func->getContext()->getStrDump(); |
883 Str << "bundle_unlock"; | 883 Str << "bundle_unlock"; |
884 } | 884 } |
885 | 885 |
886 void InstFakeDef::emit(const Cfg *Func) const { | 886 void InstFakeDef::emit(const Cfg *Func) const { |
887 if (!BuildDefs::dump()) | 887 if (!BuildDefs::dump()) |
888 return; | 888 return; |
889 // Go ahead and "emit" these for now, since they are relatively | 889 // Go ahead and "emit" these for now, since they are relatively rare. |
890 // rare. | |
891 Ostream &Str = Func->getContext()->getStrEmit(); | 890 Ostream &Str = Func->getContext()->getStrEmit(); |
892 Str << "\t# "; | 891 Str << "\t# "; |
893 getDest()->emit(Func); | 892 getDest()->emit(Func); |
894 Str << " = def.pseudo "; | 893 Str << " = def.pseudo "; |
895 emitSources(Func); | 894 emitSources(Func); |
896 } | 895 } |
897 | 896 |
898 void InstFakeDef::dump(const Cfg *Func) const { | 897 void InstFakeDef::dump(const Cfg *Func) const { |
899 if (!BuildDefs::dump()) | 898 if (!BuildDefs::dump()) |
900 return; | 899 return; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
941 Ostream &Str = Func->getContext()->getStrDump(); | 940 Ostream &Str = Func->getContext()->getStrDump(); |
942 Str << "[TARGET] "; | 941 Str << "[TARGET] "; |
943 Inst::dump(Func); | 942 Inst::dump(Func); |
944 } | 943 } |
945 | 944 |
946 bool checkForRedundantAssign(const Variable *Dest, const Operand *Source) { | 945 bool checkForRedundantAssign(const Variable *Dest, const Operand *Source) { |
947 const auto SrcVar = llvm::dyn_cast<const Variable>(Source); | 946 const auto SrcVar = llvm::dyn_cast<const Variable>(Source); |
948 if (!SrcVar) | 947 if (!SrcVar) |
949 return false; | 948 return false; |
950 if (Dest->hasReg() && Dest->getRegNum() == SrcVar->getRegNum()) { | 949 if (Dest->hasReg() && Dest->getRegNum() == SrcVar->getRegNum()) { |
951 // TODO: On x86-64, instructions like "mov eax, eax" are used to | 950 // TODO: On x86-64, instructions like "mov eax, eax" are used to clear the |
952 // clear the upper 32 bits of rax. We need to recognize and | 951 // upper 32 bits of rax. We need to recognize and preserve these. |
953 // preserve these. | |
954 return true; | 952 return true; |
955 } | 953 } |
956 if (!Dest->hasReg() && !SrcVar->hasReg() && | 954 if (!Dest->hasReg() && !SrcVar->hasReg() && |
957 Dest->getStackOffset() == SrcVar->getStackOffset()) | 955 Dest->getStackOffset() == SrcVar->getStackOffset()) |
958 return true; | 956 return true; |
959 return false; | 957 return false; |
960 } | 958 } |
961 | 959 |
962 } // end of namespace Ice | 960 } // end of namespace Ice |
OLD | NEW |