Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(850)

Unified Diff: src/IceTargetLoweringARM32.cpp

Issue 1422753010: Subzero. ARM32. Address mode formation. (Closed) Base URL: https://chromium.googlesource.com/native_client/pnacl-subzero.git@master
Patch Set: Addresses comment. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/IceTargetLoweringARM32.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « src/IceTargetLoweringARM32.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698