Index: src/IceTargetLoweringARM32.cpp |
diff --git a/src/IceTargetLoweringARM32.cpp b/src/IceTargetLoweringARM32.cpp |
index d2aa74ecf46dc6889f39549c5de77abe1c0213f5..17bd1b5876fc6f1cbaf0210859dabf61802b779f 100644 |
--- a/src/IceTargetLoweringARM32.cpp |
+++ b/src/IceTargetLoweringARM32.cpp |
@@ -465,7 +465,7 @@ void TargetARM32::emitVariable(const Variable *Var) const { |
Offset += getStackAdjustment(); |
} |
const Type VarTy = Var->getType(); |
- if (!isLegalVariableStackOffset(VarTy, Offset)) { |
+ if (!isLegalMemOffset(VarTy, Offset)) { |
llvm::report_fatal_error("Illegal stack offset"); |
} |
Str << "[" << getRegName(BaseRegNum, VarTy); |
@@ -837,7 +837,7 @@ void TargetARM32::addProlog(CfgNode *Node) { |
this->HasComputedFrame = true; |
if (BuildDefs::dump() && Func->isVerbose(IceV_Frame)) { |
- OstreamLocker L(Func->getContext()); |
+ OstreamLocker _(Func->getContext()); |
Ostream &Str = Func->getContext()->getStrDump(); |
Str << "Stack layout:\n"; |
@@ -947,15 +947,15 @@ void TargetARM32::addEpilog(CfgNode *Node) { |
RI->setDeleted(); |
} |
-bool TargetARM32::isLegalVariableStackOffset(Type Ty, int32_t Offset) const { |
- constexpr bool SignExt = false; |
- return OperandARM32Mem::canHoldOffset(Ty, SignExt, Offset); |
+bool TargetARM32::isLegalMemOffset(Type Ty, int32_t Offset) const { |
+ constexpr bool ZeroExt = false; |
+ return OperandARM32Mem::canHoldOffset(Ty, ZeroExt, Offset); |
} |
-StackVariable *TargetARM32::legalizeVariableSlot(Variable *Var, |
- int32_t StackAdjust, |
- Variable *OrigBaseReg) { |
- int32_t Offset = Var->getStackOffset() + StackAdjust; |
+Variable *TargetARM32::newBaseRegister(int32_t OriginalOffset, |
+ int32_t StackAdjust, |
+ Variable *OrigBaseReg) { |
+ int32_t Offset = OriginalOffset + StackAdjust; |
// Legalize will likely need a movw/movt combination, but if the top bits are |
// all 0 from negating the offset and subtracting, we could use that instead. |
bool ShouldSub = (-Offset & 0xFFFF0000) == 0; |
@@ -968,12 +968,85 @@ StackVariable *TargetARM32::legalizeVariableSlot(Variable *Var, |
_sub(ScratchReg, OrigBaseReg, OffsetVal); |
else |
_add(ScratchReg, OrigBaseReg, OffsetVal); |
- StackVariable *NewVar = Func->makeVariable<StackVariable>(stackSlotType()); |
- NewVar->setMustNotHaveReg(); |
- NewVar->setBaseRegNum(ScratchReg->getRegNum()); |
- constexpr int32_t NewOffset = 0; |
- NewVar->setStackOffset(NewOffset); |
- return NewVar; |
+ return ScratchReg; |
+} |
+ |
+StackVariable *TargetARM32::legalizeStackSlot(Type Ty, int32_t Offset, |
+ int32_t StackAdjust, |
+ Variable *OrigBaseReg, |
+ Variable **NewBaseReg, |
+ int32_t *NewBaseOffset) { |
+ if (*NewBaseReg == nullptr) { |
+ *NewBaseReg = newBaseRegister(Offset, StackAdjust, OrigBaseReg); |
+ *NewBaseOffset = Offset + StackAdjust; |
+ } |
+ |
+ int32_t OffsetDiff = Offset + StackAdjust - *NewBaseOffset; |
+ if (!isLegalMemOffset(Ty, OffsetDiff)) { |
+ *NewBaseReg = newBaseRegister(Offset, StackAdjust, OrigBaseReg); |
+ *NewBaseOffset = Offset + StackAdjust; |
+ OffsetDiff = 0; |
+ } |
+ |
+ StackVariable *NewDest = Func->makeVariable<StackVariable>(Ty); |
+ NewDest->setMustNotHaveReg(); |
+ NewDest->setBaseRegNum((*NewBaseReg)->getRegNum()); |
+ NewDest->setStackOffset(OffsetDiff); |
+ return NewDest; |
+} |
+ |
+void TargetARM32::legalizeMovStackAddrImm(InstARM32Mov *MovInstr, |
+ int32_t StackAdjust, |
+ Variable *OrigBaseReg, |
+ Variable **NewBaseReg, |
+ int32_t *NewBaseOffset) { |
+ Variable *Dest = MovInstr->getDest(); |
+ assert(Dest != nullptr); |
+ Type DestTy = Dest->getType(); |
+ assert(DestTy != IceType_i64); |
+ |
+ Operand *Src = MovInstr->getSrc(0); |
+ Type SrcTy = Src->getType(); |
+ assert(SrcTy != IceType_i64); |
+ |
+ if (MovInstr->isMultiDest() || MovInstr->isMultiSource()) |
+ return; |
+ |
+ bool Legalized = false; |
+ if (!Dest->hasReg()) { |
+ assert(llvm::cast<Variable>(Src)->hasReg()); |
+ const int32_t Offset = Dest->getStackOffset(); |
+ if (!isLegalMemOffset(DestTy, Offset + StackAdjust)) { |
+ Legalized = true; |
+ Dest = legalizeStackSlot(DestTy, Offset, StackAdjust, OrigBaseReg, |
+ NewBaseReg, NewBaseOffset); |
+ } |
+ } else if (auto *Var = llvm::dyn_cast<Variable>(Src)) { |
+ if (!Var->hasReg()) { |
+ const int32_t Offset = Var->getStackOffset(); |
+ if (!isLegalMemOffset(SrcTy, Offset + StackAdjust)) { |
+ Legalized = true; |
+ Src = legalizeStackSlot(SrcTy, Offset, StackAdjust, OrigBaseReg, |
+ NewBaseReg, NewBaseOffset); |
+ } |
+ } |
+ } else if (auto *Mem = llvm::dyn_cast<OperandARM32Mem>(Src)) { |
+ if (ConstantInteger32 *OffsetOp = Mem->getOffset()) { |
+ const int32_t Offset = OffsetOp->getValue(); |
+ if (!isLegalMemOffset(SrcTy, Offset + StackAdjust)) { |
+ assert(Mem->getBase()->hasReg()); |
+ assert(Mem->getBase()->getRegNum() == (int32_t)getFrameOrStackReg()); |
+ Legalized = true; |
+ Src = legalizeStackSlot(SrcTy, Offset, StackAdjust, OrigBaseReg, |
+ NewBaseReg, NewBaseOffset); |
+ } |
+ } |
+ } |
+ |
+ if (Legalized) { |
+ _mov(Dest, Src); |
+ MovInstr->setDeleted(); |
+ } |
} |
void TargetARM32::legalizeStackSlots() { |
@@ -991,7 +1064,7 @@ void TargetARM32::legalizeStackSlots() { |
// Early exit, if SpillAreaSizeBytes is really small. |
// TODO(jpp): this is not safe -- loads and stores of q registers can't have |
// offsets. |
- if (isLegalVariableStackOffset(IceType_v4i32, SpillAreaSizeBytes)) |
+ if (isLegalMemOffset(IceType_v4i32, SpillAreaSizeBytes)) |
return; |
Variable *OrigBaseReg = getPhysicalRegister(getFrameOrStackReg()); |
int32_t StackAdjust = 0; |
@@ -1006,12 +1079,13 @@ void TargetARM32::legalizeStackSlots() { |
// don't depend on this legalization. |
for (CfgNode *Node : Func->getNodes()) { |
Context.init(Node); |
- StackVariable *NewBaseReg = nullptr; |
+ Variable *NewBaseReg = nullptr; |
int32_t NewBaseOffset = 0; |
while (!Context.atEnd()) { |
PostIncrLoweringContext PostIncrement(Context); |
Inst *CurInstr = Context.getCur(); |
Variable *Dest = CurInstr->getDest(); |
+ |
// Check if the previous NewBaseReg is clobbered, and reset if needed. |
if ((Dest && NewBaseReg && Dest->hasReg() && |
Dest->getRegNum() == NewBaseReg->getBaseRegNum()) || |
@@ -1019,6 +1093,7 @@ void TargetARM32::legalizeStackSlots() { |
NewBaseReg = nullptr; |
NewBaseOffset = 0; |
} |
+ |
// The stack adjustment only matters if we are using SP instead of FP. |
if (!hasFramePointer()) { |
if (auto *AdjInst = llvm::dyn_cast<InstARM32AdjustStack>(CurInstr)) { |
@@ -1033,80 +1108,11 @@ void TargetARM32::legalizeStackSlots() { |
} |
} |
- // For now, only Mov instructions can have stack variables. We need to |
- // know the type of instruction because we currently create a fresh one |
- // to replace Dest/Source, rather than mutate in place. |
- bool MayNeedOffsetRewrite = false; |
+ // The Lowering ensures that ldr and str always have legal Mem operands. |
+ // The only other instruction that may access memory is mov. |
if (auto *MovInstr = llvm::dyn_cast<InstARM32Mov>(CurInstr)) { |
- MayNeedOffsetRewrite = |
- !MovInstr->isMultiDest() && !MovInstr->isMultiSource(); |
- } |
- |
- if (!MayNeedOffsetRewrite) { |
- continue; |
- } |
- |
- assert(Dest != nullptr); |
- Type DestTy = Dest->getType(); |
- assert(DestTy != IceType_i64); |
- if (!Dest->hasReg()) { |
- int32_t Offset = Dest->getStackOffset(); |
- Offset += StackAdjust; |
- if (!isLegalVariableStackOffset(DestTy, Offset)) { |
- if (NewBaseReg) { |
- int32_t OffsetDiff = Offset - NewBaseOffset; |
- if (isLegalVariableStackOffset(DestTy, OffsetDiff)) { |
- StackVariable *NewDest = |
- Func->makeVariable<StackVariable>(stackSlotType()); |
- NewDest->setMustNotHaveReg(); |
- NewDest->setBaseRegNum(NewBaseReg->getBaseRegNum()); |
- NewDest->setStackOffset(OffsetDiff); |
- Variable *NewDestVar = NewDest; |
- _mov(NewDestVar, CurInstr->getSrc(0)); |
- CurInstr->setDeleted(); |
- continue; |
- } |
- } |
- StackVariable *LegalDest = |
- legalizeVariableSlot(Dest, StackAdjust, OrigBaseReg); |
- assert(LegalDest != Dest); |
- Variable *LegalDestVar = LegalDest; |
- _mov(LegalDestVar, CurInstr->getSrc(0)); |
- CurInstr->setDeleted(); |
- NewBaseReg = LegalDest; |
- NewBaseOffset = Offset; |
- continue; |
- } |
- } |
- assert(CurInstr->getSrcSize() == 1); |
- Variable *Var = llvm::dyn_cast<Variable>(CurInstr->getSrc(0)); |
- if (Var && !Var->hasReg()) { |
- Type VarTy = Var->getType(); |
- int32_t Offset = Var->getStackOffset(); |
- Offset += StackAdjust; |
- if (!isLegalVariableStackOffset(VarTy, Offset)) { |
- if (NewBaseReg) { |
- int32_t OffsetDiff = Offset - NewBaseOffset; |
- if (isLegalVariableStackOffset(VarTy, OffsetDiff)) { |
- StackVariable *NewVar = |
- Func->makeVariable<StackVariable>(stackSlotType()); |
- NewVar->setMustNotHaveReg(); |
- NewVar->setBaseRegNum(NewBaseReg->getBaseRegNum()); |
- NewVar->setStackOffset(OffsetDiff); |
- _mov(Dest, NewVar); |
- CurInstr->setDeleted(); |
- continue; |
- } |
- } |
- StackVariable *LegalVar = |
- legalizeVariableSlot(Var, StackAdjust, OrigBaseReg); |
- assert(LegalVar != Var); |
- _mov(Dest, LegalVar); |
- CurInstr->setDeleted(); |
- NewBaseReg = LegalVar; |
- NewBaseOffset = Offset; |
- continue; |
- } |
+ legalizeMovStackAddrImm(MovInstr, StackAdjust, OrigBaseReg, &NewBaseReg, |
+ &NewBaseOffset); |
} |
} |
} |
@@ -1171,8 +1177,8 @@ Operand *TargetARM32::hiOperand(Operand *Operand) { |
ConstantInteger32 *Offset = Mem->getOffset(); |
assert(!Utils::WouldOverflowAdd(Offset->getValue(), 4)); |
int32_t NextOffsetVal = Offset->getValue() + 4; |
- const bool SignExt = false; |
- if (!OperandARM32Mem::canHoldOffset(SplitType, SignExt, NextOffsetVal)) { |
+ constexpr bool ZeroExt = false; |
+ if (!OperandARM32Mem::canHoldOffset(SplitType, ZeroExt, NextOffsetVal)) { |
// We have to make a temp variable and add 4 to either Base or Offset. |
// If we add 4 to Offset, this will convert a non-RegReg addressing |
// mode into a RegReg addressing mode. Since NaCl sandboxing disallows |
@@ -1819,7 +1825,7 @@ void TargetARM32::lowerBr(const InstBr *Instr) { |
CondARM32::Cond BrCondTrue1 = CondARM32::kNone; |
CondARM32::Cond BrCondFalse = CondARM32::kNone; |
if (!_mov_i1_to_flags(Cond, &BrCondTrue0, &BrCondTrue1, &BrCondFalse)) { |
- // "Cond" was not fold. |
+ // "Cond" was not folded. |
Type Ty = Cond->getType(); |
Variable *Src0R = legalizeToReg(Cond); |
assert(Ty == IceType_i1); |
@@ -3375,7 +3381,461 @@ void TargetARM32::lowerLoad(const InstLoad *Load) { |
lowerAssign(Assign); |
} |
-void TargetARM32::doAddressOptLoad() {} |
+namespace { |
+void dumpAddressOpt(const Cfg *Func, const Variable *Base, int32_t Offset, |
+ const Variable *OffsetReg, int16_t OffsetRegShAmt, |
+ const Inst *Reason) { |
+ if (!BuildDefs::dump()) |
+ return; |
+ if (!Func->isVerbose(IceV_AddrOpt)) |
+ return; |
+ OstreamLocker _(Func->getContext()); |
+ Ostream &Str = Func->getContext()->getStrDump(); |
+ Str << "Instruction: "; |
+ Reason->dumpDecorated(Func); |
+ Str << " results in Base="; |
+ if (Base) |
+ Base->dump(Func); |
+ else |
+ Str << "<null>"; |
+ Str << ", OffsetReg="; |
+ if (OffsetReg) |
+ OffsetReg->dump(Func); |
+ else |
+ Str << "<null>"; |
+ Str << ", Shift=" << OffsetRegShAmt << ", Offset=" << Offset << "\n"; |
+} |
+ |
+bool matchAssign(const VariablesMetadata *VMetadata, Variable **Var, |
+ int32_t *Offset, const Inst **Reason) { |
+ // Var originates from Var=SrcVar ==> set Var:=SrcVar |
+ if (*Var == nullptr) |
+ return false; |
+ const Inst *VarAssign = VMetadata->getSingleDefinition(*Var); |
+ if (!VarAssign) |
+ return false; |
+ assert(!VMetadata->isMultiDef(*Var)); |
+ if (!llvm::isa<InstAssign>(VarAssign)) |
+ return false; |
+ |
+ Operand *SrcOp = VarAssign->getSrc(0); |
+ if (auto *SrcVar = llvm::dyn_cast<Variable>(SrcOp)) { |
+ if (!VMetadata->isMultiDef(SrcVar) || |
+ // TODO: ensure SrcVar stays single-BB |
+ false) { |
+ *Var = SrcVar; |
+ } else if (auto *Const = llvm::dyn_cast<ConstantInteger32>(SrcOp)) { |
+ int32_t MoreOffset = Const->getValue(); |
+ int32_t NewOffset = MoreOffset + *Offset; |
+ if (Utils::WouldOverflowAdd(*Offset, MoreOffset)) |
+ return false; |
+ *Var = nullptr; |
+ *Offset += NewOffset; |
+ } |
+ |
+ *Reason = VarAssign; |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+bool isAddOrSub(const Inst *Inst, InstArithmetic::OpKind *Kind) { |
+ if (const auto *Arith = llvm::dyn_cast<InstArithmetic>(Inst)) { |
+ switch (Arith->getOp()) { |
+ default: |
+ return false; |
+ case InstArithmetic::Add: |
+ case InstArithmetic::Sub: |
+ *Kind = Arith->getOp(); |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+bool matchCombinedBaseIndex(const VariablesMetadata *VMetadata, Variable **Base, |
+ Variable **OffsetReg, int32_t OffsetRegShamt, |
+ const Inst **Reason) { |
+ // OffsetReg==nullptr && Base is Base=Var1+Var2 ==> |
+ // set Base=Var1, OffsetReg=Var2, Shift=0 |
+ if (*Base == nullptr) |
+ return false; |
+ if (*OffsetReg != nullptr) |
+ return false; |
+ (void)OffsetRegShamt; |
+ assert(OffsetRegShamt == 0); |
+ const Inst *BaseInst = VMetadata->getSingleDefinition(*Base); |
+ if (BaseInst == nullptr) |
+ return false; |
+ assert(!VMetadata->isMultiDef(*Base)); |
+ if (BaseInst->getSrcSize() < 2) |
+ return false; |
+ auto *Var1 = llvm::dyn_cast<Variable>(BaseInst->getSrc(0)); |
+ if (!Var1) |
+ return false; |
+ if (VMetadata->isMultiDef(Var1)) |
+ return false; |
+ auto *Var2 = llvm::dyn_cast<Variable>(BaseInst->getSrc(1)); |
+ if (!Var2) |
+ return false; |
+ if (VMetadata->isMultiDef(Var2)) |
+ return false; |
+ InstArithmetic::OpKind _; |
+ if (!isAddOrSub(BaseInst, &_) || |
+ // TODO: ensure Var1 and Var2 stay single-BB |
+ false) |
+ return false; |
+ *Base = Var1; |
+ *OffsetReg = Var2; |
+ // OffsetRegShamt is already 0. |
+ *Reason = BaseInst; |
+ return true; |
+} |
+ |
+bool matchShiftedOffsetReg(const VariablesMetadata *VMetadata, |
+ Variable **OffsetReg, OperandARM32::ShiftKind *Kind, |
+ int32_t *OffsetRegShamt, const Inst **Reason) { |
+ // OffsetReg is OffsetReg=Var*Const && log2(Const)+Shift<=32 ==> |
+ // OffsetReg=Var, Shift+=log2(Const) |
+ // OffsetReg is OffsetReg=Var<<Const && Const+Shift<=32 ==> |
+ // OffsetReg=Var, Shift+=Const |
+ // OffsetReg is OffsetReg=Var>>Const && Const-Shift>=-32 ==> |
+ // OffsetReg=Var, Shift-=Const |
+ OperandARM32::ShiftKind NewShiftKind = OperandARM32::kNoShift; |
+ if (*OffsetReg == nullptr) |
+ return false; |
+ auto *IndexInst = VMetadata->getSingleDefinition(*OffsetReg); |
+ if (IndexInst == nullptr) |
+ return false; |
+ assert(!VMetadata->isMultiDef(*OffsetReg)); |
+ if (IndexInst->getSrcSize() < 2) |
+ return false; |
+ auto *ArithInst = llvm::dyn_cast<InstArithmetic>(IndexInst); |
+ if (ArithInst == nullptr) |
+ return false; |
+ auto *Var = llvm::dyn_cast<Variable>(ArithInst->getSrc(0)); |
+ if (Var == nullptr) |
+ return false; |
+ auto *Const = llvm::dyn_cast<ConstantInteger32>(ArithInst->getSrc(1)); |
+ if (Const == nullptr) { |
+ assert(!llvm::isa<ConstantInteger32>(ArithInst->getSrc(0))); |
+ return false; |
+ } |
+ if (VMetadata->isMultiDef(Var) || Const->getType() != IceType_i32) |
+ return false; |
+ |
+ uint32_t NewShamt = -1; |
+ switch (ArithInst->getOp()) { |
+ default: |
+ return false; |
+ case InstArithmetic::Shl: { |
+ NewShiftKind = OperandARM32::LSL; |
+ NewShamt = Const->getValue(); |
+ if (NewShamt > 31) |
+ return false; |
+ } break; |
+ case InstArithmetic::Lshr: { |
+ NewShiftKind = OperandARM32::LSR; |
+ NewShamt = Const->getValue(); |
+ if (NewShamt > 31) |
+ return false; |
+ } break; |
+ case InstArithmetic::Ashr: { |
+ NewShiftKind = OperandARM32::ASR; |
+ NewShamt = Const->getValue(); |
+ if (NewShamt > 31) |
+ return false; |
+ } break; |
+ case InstArithmetic::Udiv: |
+ case InstArithmetic::Mul: { |
+ const uint32_t UnsignedConst = Const->getValue(); |
+ NewShamt = llvm::findFirstSet(UnsignedConst); |
+ if (NewShamt != llvm::findLastSet(UnsignedConst)) { |
+ // First bit set is not the same as the last bit set, so Const is not |
+ // a power of 2. |
+ return false; |
+ } |
+ NewShiftKind = ArithInst->getOp() == InstArithmetic::Udiv |
+ ? OperandARM32::LSR |
+ : OperandARM32::LSL; |
+ } break; |
+ } |
+ // Allowed "transitions": |
+ // kNoShift -> * iff NewShamt < 31 |
+ // LSL -> LSL iff NewShamt + OffsetRegShamt < 31 |
+ // LSR -> LSR iff NewShamt + OffsetRegShamt < 31 |
+ // ASR -> ASR iff NewShamt + OffsetRegShamt < 31 |
+ if (*Kind != OperandARM32::kNoShift && *Kind != NewShiftKind) { |
+ return false; |
+ } |
+ const int32_t NewOffsetRegShamt = *OffsetRegShamt + NewShamt; |
+ if (NewOffsetRegShamt > 31) |
+ return false; |
+ *OffsetReg = Var; |
+ *OffsetRegShamt = NewOffsetRegShamt; |
+ *Kind = NewShiftKind; |
+ *Reason = IndexInst; |
+ return true; |
+} |
+ |
+bool matchOffsetBase(const VariablesMetadata *VMetadata, Variable **Base, |
+ int32_t *Offset, const Inst **Reason) { |
+ // Base is Base=Var+Const || Base is Base=Const+Var ==> |
+ // set Base=Var, Offset+=Const |
+ // Base is Base=Var-Const ==> |
+ // set Base=Var, Offset-=Const |
+ if (*Base == nullptr) |
+ return false; |
+ const Inst *BaseInst = VMetadata->getSingleDefinition(*Base); |
+ if (BaseInst == nullptr) { |
+ return false; |
+ } |
+ assert(!VMetadata->isMultiDef(*Base)); |
+ |
+ auto *ArithInst = llvm::dyn_cast<const InstArithmetic>(BaseInst); |
+ if (ArithInst == nullptr) |
+ return false; |
+ InstArithmetic::OpKind Kind; |
+ if (!isAddOrSub(ArithInst, &Kind)) |
+ return false; |
+ bool IsAdd = Kind == InstArithmetic::Add; |
+ Operand *Src0 = ArithInst->getSrc(0); |
+ Operand *Src1 = ArithInst->getSrc(1); |
+ auto *Var0 = llvm::dyn_cast<Variable>(Src0); |
+ auto *Var1 = llvm::dyn_cast<Variable>(Src1); |
+ auto *Const0 = llvm::dyn_cast<ConstantInteger32>(Src0); |
+ auto *Const1 = llvm::dyn_cast<ConstantInteger32>(Src1); |
+ Variable *NewBase = nullptr; |
+ int32_t NewOffset = *Offset; |
+ |
+ if (Var0 == nullptr && Const0 == nullptr) { |
+ assert(llvm::isa<ConstantRelocatable>(Src0)); |
+ return false; |
+ } |
+ |
+ if (Var1 == nullptr && Const1 == nullptr) { |
+ assert(llvm::isa<ConstantRelocatable>(Src1)); |
+ return false; |
+ } |
+ |
+ if (Var0 && Var1) |
+ // TODO(jpp): merge base/index splitting into here. |
+ return false; |
+ if (!IsAdd && Var1) |
+ return false; |
+ if (Var0) |
+ NewBase = Var0; |
+ else if (Var1) |
+ NewBase = Var1; |
+ // Compute the updated constant offset. |
+ if (Const0) { |
+ int32_t MoreOffset = IsAdd ? Const0->getValue() : -Const0->getValue(); |
+ if (Utils::WouldOverflowAdd(NewOffset, MoreOffset)) |
+ return false; |
+ NewOffset += MoreOffset; |
+ } |
+ if (Const1) { |
+ int32_t MoreOffset = IsAdd ? Const1->getValue() : -Const1->getValue(); |
+ if (Utils::WouldOverflowAdd(NewOffset, MoreOffset)) |
+ return false; |
+ NewOffset += MoreOffset; |
+ } |
+ |
+ // Update the computed address parameters once we are sure optimization |
+ // is valid. |
+ *Base = NewBase; |
+ *Offset = NewOffset; |
+ *Reason = BaseInst; |
+ return true; |
+} |
+} // end of anonymous namespace |
+ |
+// ARM32 address modes: |
+// ld/st i[8|16|32]: [reg], [reg +/- imm12], [pc +/- imm12], |
+// [reg +/- reg << shamt5] |
+// ld/st f[32|64] : [reg], [reg +/- imm8] , [pc +/- imm8] |
+// ld/st vectors : [reg] |
+// |
+// For now, we don't handle address modes with Relocatables. |
+namespace { |
+// MemTraits contains per-type valid address mode information. |
+#define X(tag, elementty, int_width, vec_width, sbits, ubits, rraddr, shaddr) \ |
+ static_assert(!(shaddr) || rraddr, "Check ICETYPEARM32_TABLE::" #tag); |
+ICETYPEARM32_TABLE |
+#undef X |
+ |
+static const struct { |
+ int32_t ValidImmMask; |
+ bool CanHaveImm; |
+ bool CanHaveIndex; |
+ bool CanHaveShiftedIndex; |
+} MemTraits[] = { |
+#define X(tag, elementty, int_width, vec_width, sbits, ubits, rraddr, shaddr) \ |
+ { (1 << ubits) - 1, (ubits) > 0, rraddr, shaddr, } \ |
+ , |
+ ICETYPEARM32_TABLE |
+#undef X |
+}; |
+static constexpr SizeT MemTraitsSize = llvm::array_lengthof(MemTraits); |
+} // end of anonymous namespace |
+ |
+OperandARM32Mem *TargetARM32::formAddressingMode(Type Ty, Cfg *Func, |
+ const Inst *LdSt, |
+ Operand *Base) { |
+ assert(Base != nullptr); |
+ int32_t OffsetImm = 0; |
+ Variable *OffsetReg = nullptr; |
+ int32_t OffsetRegShamt = 0; |
+ OperandARM32::ShiftKind ShiftKind = OperandARM32::kNoShift; |
+ |
+ Func->resetCurrentNode(); |
+ if (Func->isVerbose(IceV_AddrOpt)) { |
+ OstreamLocker _(Func->getContext()); |
+ Ostream &Str = Func->getContext()->getStrDump(); |
+ Str << "\nAddress mode formation:\t"; |
+ LdSt->dumpDecorated(Func); |
+ } |
+ |
+ if (isVectorType(Ty)) |
+ // vector loads and stores do not allow offsets, and only support the |
+ // "[reg]" addressing mode (the other supported modes are write back.) |
+ return nullptr; |
+ |
+ auto *BaseVar = llvm::dyn_cast<Variable>(Base); |
+ if (BaseVar == nullptr) |
+ return nullptr; |
+ |
+ (void)MemTraitsSize; |
+ assert(Ty < MemTraitsSize); |
+ auto *TypeTraits = &MemTraits[Ty]; |
+ const bool CanHaveIndex = TypeTraits->CanHaveIndex; |
+ const bool CanHaveShiftedIndex = TypeTraits->CanHaveShiftedIndex; |
+ const bool CanHaveImm = TypeTraits->CanHaveImm; |
+ const int32_t ValidImmMask = TypeTraits->ValidImmMask; |
+ (void)ValidImmMask; |
+ assert(!CanHaveImm || ValidImmMask >= 0); |
+ |
+ const VariablesMetadata *VMetadata = Func->getVMetadata(); |
+ const Inst *Reason = nullptr; |
+ |
+ do { |
+ if (Reason != nullptr) { |
+ dumpAddressOpt(Func, BaseVar, OffsetImm, OffsetReg, OffsetRegShamt, |
+ Reason); |
+ Reason = nullptr; |
+ } |
+ |
+ if (matchAssign(VMetadata, &BaseVar, &OffsetImm, &Reason)) { |
+ continue; |
+ } |
+ |
+ if (CanHaveIndex && |
+ matchAssign(VMetadata, &OffsetReg, &OffsetImm, &Reason)) { |
+ continue; |
+ } |
+ |
+ if (CanHaveIndex && matchCombinedBaseIndex(VMetadata, &BaseVar, &OffsetReg, |
+ OffsetRegShamt, &Reason)) { |
+ continue; |
+ } |
+ |
+ if (CanHaveShiftedIndex) { |
+ if (matchShiftedOffsetReg(VMetadata, &OffsetReg, &ShiftKind, |
+ &OffsetRegShamt, &Reason)) { |
+ continue; |
+ } |
+ |
+ if ((OffsetRegShamt == 0) && |
+ matchShiftedOffsetReg(VMetadata, &BaseVar, &ShiftKind, |
+ &OffsetRegShamt, &Reason)) { |
+ std::swap(BaseVar, OffsetReg); |
+ continue; |
+ } |
+ } |
+ |
+ if (matchOffsetBase(VMetadata, &BaseVar, &OffsetImm, &Reason)) { |
+ continue; |
+ } |
+ } while (Reason); |
+ |
+ if (BaseVar == nullptr) { |
+ // [OffsetReg{, LSL Shamt}{, #OffsetImm}] is not legal in ARM, so we have to |
+ // legalize the addressing mode to [BaseReg, OffsetReg{, LSL Shamt}]. |
+ // Instead of a zeroed BaseReg, we initialize it with OffsetImm: |
+ // |
+ // [OffsetReg{, LSL Shamt}{, #OffsetImm}] -> |
+ // mov BaseReg, #OffsetImm |
+ // use of [BaseReg, OffsetReg{, LSL Shamt}] |
+ // |
+ const Type PointerType = getPointerType(); |
+ BaseVar = makeReg(PointerType); |
+ Context.insert( |
+ InstAssign::create(Func, BaseVar, Ctx->getConstantInt32(OffsetImm))); |
+ OffsetImm = 0; |
+ } else if (OffsetImm != 0) { |
+ // ARM Ldr/Str instructions have limited range immediates. The formation |
+ // loop above materialized an Immediate carelessly, so we ensure the |
+ // generated offset is sane. |
+ const int32_t PositiveOffset = OffsetImm > 0 ? OffsetImm : -OffsetImm; |
+ const InstArithmetic::OpKind Op = |
+ OffsetImm > 0 ? InstArithmetic::Add : InstArithmetic::Sub; |
+ |
+ if (!CanHaveImm || !isLegalMemOffset(Ty, OffsetImm) || |
+ OffsetReg != nullptr) { |
+ if (OffsetReg == nullptr) { |
+ // We formed a [Base, #const] addressing mode which is not encodable in |
+ // ARM. There is little point in forming an address mode now if we don't |
+ // have an offset. Effectively, we would end up with something like |
+ // |
+ // [Base, #const] -> add T, Base, #const |
+ // use of [T] |
+ // |
+ // Which is exactly what we already have. So we just bite the bullet |
+ // here and don't form any address mode. |
+ return nullptr; |
+ } |
+ // We formed [Base, Offset {, LSL Amnt}, #const]. Oops. Legalize it to |
+ // |
+ // [Base, Offset, {LSL amount}, #const] -> |
+ // add T, Base, #const |
+ // use of [T, Offset {, LSL amount}] |
+ const Type PointerType = getPointerType(); |
+ Variable *T = makeReg(PointerType); |
+ Context.insert(InstArithmetic::create( |
+ Func, Op, T, BaseVar, Ctx->getConstantInt32(PositiveOffset))); |
+ BaseVar = T; |
+ OffsetImm = 0; |
+ } |
+ } |
+ |
+ assert(BaseVar != nullptr); |
+ assert(OffsetImm == 0 || OffsetReg == nullptr); |
+ assert(OffsetReg == nullptr || CanHaveIndex); |
+ assert(OffsetImm < 0 ? (ValidImmMask & -OffsetImm) == -OffsetImm |
+ : (ValidImmMask & OffsetImm) == OffsetImm); |
+ |
+ if (OffsetReg != nullptr) { |
+ return OperandARM32Mem::create(Func, Ty, BaseVar, OffsetReg, ShiftKind, |
+ OffsetRegShamt); |
+ } |
+ |
+ return OperandARM32Mem::create( |
+ Func, Ty, BaseVar, |
+ llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(OffsetImm))); |
+} |
+ |
+void TargetARM32::doAddressOptLoad() { |
+ Inst *Instr = Context.getCur(); |
+ assert(llvm::isa<InstLoad>(Instr)); |
+ Variable *Dest = Instr->getDest(); |
+ Operand *Addr = Instr->getSrc(0); |
+ if (OperandARM32Mem *Mem = |
+ formAddressingMode(Dest->getType(), Func, Instr, Addr)) { |
+ Instr->setDeleted(); |
+ Context.insert(InstLoad::create(Func, Dest, Mem)); |
+ } |
+} |
void TargetARM32::randomlyInsertNop(float Probability, |
RandomNumberGenerator &RNG) { |
@@ -3576,7 +4036,17 @@ void TargetARM32::lowerStore(const InstStore *Inst) { |
} |
} |
-void TargetARM32::doAddressOptStore() {} |
+void TargetARM32::doAddressOptStore() { |
+ Inst *Instr = Context.getCur(); |
+ assert(llvm::isa<InstStore>(Instr)); |
+ Operand *Src = Instr->getSrc(0); |
+ Operand *Addr = Instr->getSrc(1); |
+ if (OperandARM32Mem *Mem = |
+ formAddressingMode(Src->getType(), Func, Instr, Addr)) { |
+ Instr->setDeleted(); |
+ Context.insert(InstStore::create(Func, Src, Mem)); |
+ } |
+} |
void TargetARM32::lowerSwitch(const InstSwitch *Inst) { |
// This implements the most naive possible lowering. |
@@ -3673,16 +4143,6 @@ Operand *TargetARM32::legalize(Operand *From, LegalMask Allowed, |
// type of operand is not legal (e.g., OperandARM32Mem and !Legal_Mem), we |
// can always copy to a register. |
if (auto *Mem = llvm::dyn_cast<OperandARM32Mem>(From)) { |
- static const struct { |
- bool CanHaveOffset; |
- bool CanHaveIndex; |
- } MemTraits[] = { |
-#define X(tag, elementty, int_width, vec_width, sbits, ubits, rraddr) \ |
- { (ubits) > 0, rraddr } \ |
- , |
- ICETYPEARM32_TABLE |
-#undef X |
- }; |
// Before doing anything with a Mem operand, we need to ensure that the |
// Base and Index components are in physical registers. |
Variable *Base = Mem->getBase(); |
@@ -3691,26 +4151,26 @@ Operand *TargetARM32::legalize(Operand *From, LegalMask Allowed, |
assert(Index == nullptr || Offset == nullptr); |
Variable *RegBase = nullptr; |
Variable *RegIndex = nullptr; |
- if (Base) { |
- RegBase = legalizeToReg(Base); |
- } |
+ assert(Base); |
+ RegBase = legalizeToReg(Base); |
+ bool InvalidImm = false; |
+ assert(Ty < MemTraitsSize); |
if (Index) { |
+ assert(Offset == nullptr); |
+ assert(MemTraits[Ty].CanHaveIndex); |
RegIndex = legalizeToReg(Index); |
- if (!MemTraits[Ty].CanHaveIndex) { |
- Variable *T = makeReg(IceType_i32, getReservedTmpReg()); |
- _add(T, RegBase, RegIndex); |
- RegBase = T; |
- RegIndex = nullptr; |
- } |
} |
if (Offset && Offset->getValue() != 0) { |
- static constexpr bool SignExt = false; |
- if (!MemTraits[Ty].CanHaveOffset || |
- !OperandARM32Mem::canHoldOffset(Ty, SignExt, Offset->getValue())) { |
- Variable *T = legalizeToReg(Offset, getReservedTmpReg()); |
- _add(T, T, RegBase); |
- RegBase = T; |
- Offset = llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(0)); |
+ assert(Index == nullptr); |
+ static constexpr bool ZeroExt = false; |
+ assert(MemTraits[Ty].CanHaveImm); |
+ if (!OperandARM32Mem::canHoldOffset(Ty, ZeroExt, Offset->getValue())) { |
+ assert(RegBase->hasReg()); |
+ assert(RegBase->getRegNum() == (int32_t)getFrameOrStackReg()); |
+ // We are a bit more lenient with invalid immediate when accessing the |
+ // stack here, and then rely on legalizeStackSlots() to fix things as |
+ // appropriate. |
+ InvalidImm = true; |
} |
} |
@@ -3718,7 +4178,7 @@ Operand *TargetARM32::legalize(Operand *From, LegalMask Allowed, |
if (Base != RegBase || Index != RegIndex) { |
// There is only a reg +/- reg or reg + imm form. |
// Figure out which to re-create. |
- if (RegBase && RegIndex) { |
+ if (RegIndex) { |
Mem = OperandARM32Mem::create(Func, Ty, RegBase, RegIndex, |
Mem->getShiftOp(), Mem->getShiftAmt(), |
Mem->getAddrMode()); |
@@ -3731,7 +4191,15 @@ Operand *TargetARM32::legalize(Operand *From, LegalMask Allowed, |
From = Mem; |
} else { |
Variable *Reg = makeReg(Ty, RegNum); |
- _ldr(Reg, Mem); |
+ if (InvalidImm) { |
+ // If Mem has an invalid immediate, we legalize it to a Reg using mov |
+ // instead of ldr because legalizeStackSlots() will later kick in and |
+ // fix the immediate for us. |
+ _mov(Reg, Mem); |
+ } else { |
+ _ldr(Reg, Mem); |
+ } |
+ |
From = Reg; |
} |
return From; |
@@ -3804,7 +4272,7 @@ Operand *TargetARM32::legalize(Operand *From, LegalMask Allowed, |
// TODO(jvoung): Allow certain immediates to be encoded directly in an |
// operand. See Table A7-18 of the ARM manual: "Floating-point modified |
// immediate constants". Or, for 32-bit floating point numbers, just |
- // encode the raw bits into a movw/movt pair to GPR, and vmov to an SREG, |
+ // encode the raw bits into a movw/movt pair to GPR, and vmov to an SREG |
// instead of using a movw/movt pair to get the const-pool address then |
// loading to SREG. |
std::string Buffer; |
@@ -3874,9 +4342,9 @@ OperandARM32Mem *TargetARM32::formMemoryOperand(Operand *Operand, Type Ty) { |
if (Mem) { |
return llvm::cast<OperandARM32Mem>(legalize(Mem)); |
} |
- // If we didn't do address mode optimization, then we only have a base/offset |
- // to work with. ARM always requires a base register, so just use that to |
- // hold the operand. |
+ // If we didn't do address mode optimization, then we only have a |
+ // base/offset to work with. ARM always requires a base register, so |
+ // just use that to hold the operand. |
Variable *Base = legalizeToReg(Operand); |
return OperandARM32Mem::create( |
Func, Ty, Base, |
@@ -4095,7 +4563,7 @@ void TargetDataARM32::lowerGlobals(const VariableDeclarationList &Vars, |
case FT_Asm: |
case FT_Iasm: { |
const IceString &TranslateOnly = Ctx->getFlags().getTranslateOnly(); |
- OstreamLocker L(Ctx); |
+ OstreamLocker _(Ctx); |
for (const VariableDeclaration *Var : Vars) { |
if (GlobalContext::matchSymbolName(Var->getName(), TranslateOnly)) { |
emitGlobal(*Var, SectionSuffix); |
@@ -4198,7 +4666,7 @@ void TargetDataARM32::lowerConstants() { |
break; |
case FT_Asm: |
case FT_Iasm: { |
- OstreamLocker L(Ctx); |
+ OstreamLocker _(Ctx); |
emitConstantPool<float>(Ctx); |
emitConstantPool<double>(Ctx); |
break; |
@@ -4227,7 +4695,7 @@ TargetHeaderARM32::TargetHeaderARM32(GlobalContext *Ctx) |
: TargetHeaderLowering(Ctx), CPUFeatures(Ctx->getFlags()) {} |
void TargetHeaderARM32::lower() { |
- OstreamLocker L(Ctx); |
+ OstreamLocker _(Ctx); |
Ostream &Str = Ctx->getStrEmit(); |
Str << ".syntax unified\n"; |
// Emit build attributes in format: .eabi_attribute TAG, VALUE. See Sec. 2 of |