Index: src/IceTargetLoweringARM32.h |
diff --git a/src/IceTargetLoweringARM32.h b/src/IceTargetLoweringARM32.h |
index 7620afd1812c16d063b3e45815c792d1613a7c14..de144c9737b71f93b8239951d8be68d2f2e0f75f 100644 |
--- a/src/IceTargetLoweringARM32.h |
+++ b/src/IceTargetLoweringARM32.h |
@@ -58,6 +58,12 @@ public: |
// TODO(jvoung): return a unique_ptr. |
static TargetARM32 *create(Cfg *Func) { return new TargetARM32(Func); } |
+ void initNodeForLowering(CfgNode *Node) override { |
+ BoolComputations.forgetProducers(); |
+ BoolComputations.recordProducers(Node); |
+ BoolComputations.dump(Func); |
+ } |
+ |
void translateOm1() override; |
void translateO2() override; |
bool doBranchOpt(Inst *I, const CfgNode *NextNode) override; |
@@ -130,8 +136,13 @@ protected: |
void lowerCall(const InstCall *Inst) override; |
void lowerCast(const InstCast *Inst) override; |
void lowerExtractElement(const InstExtractElement *Inst) override; |
- void lowerFcmp(const InstFcmp *Inst) override; |
- void lowerIcmp(const InstIcmp *Inst) override; |
+ void lowerFcmpCond(const InstFcmp *Instr, CondARM32::Cond *CondIfTrue0, |
+ CondARM32::Cond *CondIfTrue1, |
+ CondARM32::Cond *CondIfFalse); |
+ void lowerFcmp(const InstFcmp *Instr) override; |
+ void lowerIcmpCond(const InstIcmp *Instr, CondARM32::Cond *CondIfTrue, |
+ CondARM32::Cond *CondIfFalse); |
+ void lowerIcmp(const InstIcmp *Instr) override; |
void lowerAtomicRMW(Variable *Dest, uint32_t Operation, Operand *Ptr, |
Operand *Val); |
void lowerIntrinsicCall(const InstIntrinsicCall *Inst) override; |
@@ -316,6 +327,60 @@ protected: |
Context.insert(InstFakeDef::create(Func, Instr->getDestHi())); |
} |
} |
+ |
+ // _mov_i1_to_flags is used for bool folding. If "Boolean" is folded, this |
+ // method returns true, and sets "CondIfTrue0" and "CondIfTrue1" to the |
+ // appropriate ARM condition codes. If "Boolean" is not to be folded, then this |
+ // method returns false. |
+ bool _mov_i1_to_flags(Operand *Boolean, CondARM32::Cond *CondIfTrue0, |
+ CondARM32::Cond *CondIfTrue1, |
+ CondARM32::Cond *CondIfFalse); |
+ |
+ // _cmov is a pseudo instruction that is used for boolean folding. It emits |
+ // code that moves "SrcIfTrue" to dest if either "CondIfTrue0" or |
+ // "CondIfTrue1" holds, and "SrcIfFalse", if "CondIfFalse" holds. It requires |
+ // "Dest" to be an infinite-weight temporary. |
+ void _cmov(Variable *Dest, Operand *SrcIfTrue, CondARM32::Cond CondIfTrue0, |
+ CondARM32::Cond CondIfTrue1, Operand *SrcIfFalse, |
+ CondARM32::Cond CondIfFalse) { |
+ assert(Dest->mustHaveReg()); |
+ |
+ if (CondIfFalse == CondARM32::kNone) { |
+ assert(CondIfTrue0 == CondARM32::AL); |
+ assert(CondIfTrue1 == CondARM32::kNone); |
+ } |
+ |
+ if (CondIfTrue0 == CondARM32::kNone) { |
+ assert(CondIfFalse == CondARM32::AL); |
+ assert(CondIfTrue1 == CondARM32::kNone); |
+ } |
+ |
+ if (CondIfTrue1 != CondARM32::kNone) { |
+ assert(CondIfFalse == CondARM32::AL); |
+ assert(CondIfTrue1 != CondARM32::kNone); |
+ } |
+ |
+ bool RedefineT = false; |
+ if (CondIfFalse != CondARM32::kNone) { |
+ _mov(Dest, SrcIfFalse, CondIfFalse); |
+ RedefineT = true; |
+ } |
+ |
+ if (CondIfTrue0 != CondARM32::kNone) { |
+ if (RedefineT) { |
+ _mov_redefined(Dest, SrcIfTrue, CondIfTrue0); |
+ } else { |
+ _mov(Dest, SrcIfTrue, CondIfTrue0); |
+ } |
+ RedefineT = true; |
+ } |
+ |
+ if (CondIfTrue1 != CondARM32::kNone) { |
+ assert(RedefineT); |
+ _mov_redefined(Dest, SrcIfTrue, CondIfTrue1); |
+ } |
+ } |
+ |
/// The Operand can only be a 16-bit immediate or a ConstantRelocatable (with |
/// an upper16 relocation). |
void _movt(Variable *Dest, Operand *Src0, |
@@ -542,6 +607,64 @@ protected: |
private: |
~TargetARM32() override = default; |
+ |
+ void lowerTruncToFlags(Operand *Src, CondARM32::Cond *CondIfTrue, |
+ CondARM32::Cond *CondIfFalse); |
+ class BoolComputationTracker { |
+ public: |
+ BoolComputationTracker() = default; |
+ ~BoolComputationTracker() = default; |
+ |
+ void forgetProducers() { KnownComputations.clear(); } |
+ void recordProducers(CfgNode *Node); |
+ |
+ const Inst *getProducerOf(const Operand *Opnd) const { |
+ auto *Var = llvm::dyn_cast<Variable>(Opnd); |
+ if (Var == nullptr) { |
+ return nullptr; |
+ } |
+ |
+ auto Iter = KnownComputations.find(Var->getIndex()); |
+ if (Iter == KnownComputations.end()) { |
+ return nullptr; |
+ } |
+ |
+ return Iter->second.Instr; |
+ } |
+ |
+ void dump(const Cfg *Func) const { |
+ if (!BuildDefs::dump() || !Func->isVerbose(IceV_Folding)) |
+ return; |
+ OstreamLocker L(Func->getContext()); |
+ Ostream &Str = Func->getContext()->getStrDump(); |
+ Str << "foldable producer:\n "; |
+ for (const auto &Computation : KnownComputations) { |
+ Str << " "; |
+ Computation.second.Instr->dump(Func); |
+ Str << "\n"; |
+ } |
+ Str << "\n"; |
+ } |
+ |
+ private: |
+ class BoolComputationEntry { |
+ public: |
+ explicit BoolComputationEntry(Inst *I) : Instr(I) {} |
+ Inst *const Instr; |
+ // Boolean folding is disabled for variables whose live range is multi |
+ // block. We conservatively initialize IsLiveOut to true, and set it to |
+ // false once we find the end of the live range for the variable defined |
+ // by this instruction. If liveness analysis is not performed (e.g., in |
+ // Om1 mode) IsLiveOut will never be set to false, and folding will be |
+ // disabled. |
+ bool IsLiveOut = true; |
+ }; |
+ |
+ using BoolComputationMap = std::unordered_map<SizeT, BoolComputationEntry>; |
+ BoolComputationMap KnownComputations; |
+ }; |
+ |
+ BoolComputationTracker BoolComputations; |
}; |
class TargetDataARM32 final : public TargetDataLowering { |