Commit 866b6b19 by John Porto

Subzero. ARM32. Folding rematerializable offsets in address operands.

parent 92a6e5b0
...@@ -241,6 +241,9 @@ OperandARM32Mem::OperandARM32Mem(Cfg *Func, Type Ty, Variable *Base, ...@@ -241,6 +241,9 @@ OperandARM32Mem::OperandARM32Mem(Cfg *Func, Type Ty, Variable *Base,
uint16_t ShiftAmt, AddrMode Mode) uint16_t ShiftAmt, AddrMode Mode)
: OperandARM32(kMem, Ty), Base(Base), ImmOffset(0), Index(Index), : OperandARM32(kMem, Ty), Base(Base), ImmOffset(0), Index(Index),
ShiftOp(ShiftOp), ShiftAmt(ShiftAmt), Mode(Mode) { ShiftOp(ShiftOp), ShiftAmt(ShiftAmt), Mode(Mode) {
if (Index->isRematerializable()) {
llvm::report_fatal_error("Rematerializable Index Register is not allowed.");
}
NumVars = 2; NumVars = 2;
Vars = Func->allocateArrayOf<Variable *>(2); Vars = Func->allocateArrayOf<Variable *>(2);
Vars[0] = Base; Vars[0] = Base;
...@@ -257,6 +260,10 @@ bool OperandARM32Mem::canHoldOffset(Type Ty, bool SignExt, int32_t Offset) { ...@@ -257,6 +260,10 @@ bool OperandARM32Mem::canHoldOffset(Type Ty, bool SignExt, int32_t Offset) {
return Offset == 0; return Offset == 0;
// Note that encodings for offsets are sign-magnitude for ARM, so we check // Note that encodings for offsets are sign-magnitude for ARM, so we check
// with IsAbsoluteUint(). // with IsAbsoluteUint().
// Scalar fp, and vector types require an offset that is aligned to a multiple
// of 4.
if (isScalarFloatingType(Ty) || isVectorType(Ty))
return Utils::IsAligned(Offset, 4) && Utils::IsAbsoluteUint(Bits, Offset);
return Utils::IsAbsoluteUint(Bits, Offset); return Utils::IsAbsoluteUint(Bits, Offset);
} }
......
...@@ -681,10 +681,10 @@ void TargetARM32::translateO2() { ...@@ -681,10 +681,10 @@ void TargetARM32::translateO2() {
return; return;
Func->dump("After stack frame mapping"); Func->dump("After stack frame mapping");
legalizeStackSlots(); postLowerLegalization();
if (Func->hasError()) if (Func->hasError())
return; return;
Func->dump("After legalizeStackSlots"); Func->dump("After postLowerLegalization");
Func->contractEmptyNodes(); Func->contractEmptyNodes();
Func->reorderNodes(); Func->reorderNodes();
...@@ -746,10 +746,10 @@ void TargetARM32::translateOm1() { ...@@ -746,10 +746,10 @@ void TargetARM32::translateOm1() {
return; return;
Func->dump("After stack frame mapping"); Func->dump("After stack frame mapping");
legalizeStackSlots(); postLowerLegalization();
if (Func->hasError()) if (Func->hasError())
return; return;
Func->dump("After legalizeStackSlots"); Func->dump("After postLowerLegalization");
// Nop insertion // Nop insertion
if (Ctx->getFlags().shouldDoNopInsertion()) { if (Ctx->getFlags().shouldDoNopInsertion()) {
...@@ -1336,55 +1336,101 @@ bool TargetARM32::isLegalMemOffset(Type Ty, int32_t Offset) const { ...@@ -1336,55 +1336,101 @@ bool TargetARM32::isLegalMemOffset(Type Ty, int32_t Offset) const {
return OperandARM32Mem::canHoldOffset(Ty, ZeroExt, Offset); return OperandARM32Mem::canHoldOffset(Ty, ZeroExt, Offset);
} }
Variable *TargetARM32::newBaseRegister(int32_t Offset, Variable *OrigBaseReg) { Variable *TargetARM32::PostLoweringLegalizer::newBaseRegister(
Variable *Base, int32_t Offset, int32_t ScratchRegNum) {
// Legalize will likely need a movw/movt combination, but if the top bits are // 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. // all 0 from negating the offset and subtracting, we could use that instead.
bool ShouldSub = (-Offset & 0xFFFF0000) == 0; const bool ShouldSub = Offset != 0 && (-Offset & 0xFFFF0000) == 0;
if (ShouldSub) Variable *ScratchReg = Target->makeReg(IceType_i32, ScratchRegNum);
Offset = -Offset; if (ShouldSub) {
Operand *OffsetVal = legalize(Ctx->getConstantInt32(Offset), Operand *OffsetVal =
Legal_Reg | Legal_Flex, getReservedTmpReg()); Target->legalize(Target->Ctx->getConstantInt32(-Offset),
Variable *ScratchReg = makeReg(IceType_i32, getReservedTmpReg()); Legal_Reg | Legal_Flex, ScratchRegNum);
if (ShouldSub) Target->_sub(ScratchReg, Base, OffsetVal);
_sub(ScratchReg, OrigBaseReg, OffsetVal); } else {
else Operand *OffsetVal =
_add(ScratchReg, OrigBaseReg, OffsetVal); Target->legalize(Target->Ctx->getConstantInt32(Offset),
Legal_Reg | Legal_Flex, ScratchRegNum);
Target->_add(ScratchReg, Base, OffsetVal);
}
if (ScratchRegNum == Target->getReservedTmpReg()) {
const bool BaseIsStackOrFramePtr =
Base->getRegNum() == static_cast<int32_t>(Target->getFrameOrStackReg());
// There is currently no code path that would trigger this assertion, so we
// leave this assertion here in case it is ever violated. This is not a
// fatal error (thus the use of assert() and not llvm::report_fatal_error)
// as the program compiled by subzero will still work correctly.
assert(BaseIsStackOrFramePtr);
// Side-effect: updates TempBase to reflect the new Temporary.
if (BaseIsStackOrFramePtr) {
TempBaseReg = ScratchReg;
TempBaseOffset = Offset;
} else {
TempBaseReg = nullptr;
TempBaseOffset = 0;
}
}
return ScratchReg; return ScratchReg;
} }
OperandARM32Mem *TargetARM32::createMemOperand(Type Ty, int32_t Offset, OperandARM32Mem *TargetARM32::PostLoweringLegalizer::createMemOperand(
Variable *OrigBaseReg, Type Ty, Variable *Base, int32_t Offset, bool AllowOffsets) {
Variable **NewBaseReg, assert(!Base->isRematerializable());
int32_t *NewBaseOffset) { if (AllowOffsets && Target->isLegalMemOffset(Ty, Offset)) {
assert(!OrigBaseReg->isRematerializable());
if (isLegalMemOffset(Ty, Offset)) {
return OperandARM32Mem::create( return OperandARM32Mem::create(
Func, Ty, OrigBaseReg, Target->Func, Ty, Base,
llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(Offset)), llvm::cast<ConstantInteger32>(Target->Ctx->getConstantInt32(Offset)),
OperandARM32Mem::Offset); OperandARM32Mem::Offset);
} }
if (*NewBaseReg == nullptr) { if (!AllowOffsets || TempBaseReg == nullptr) {
*NewBaseReg = newBaseRegister(Offset, OrigBaseReg); newBaseRegister(Base, Offset, Target->getReservedTmpReg());
*NewBaseOffset = Offset;
} }
int32_t OffsetDiff = Offset - *NewBaseOffset; int32_t OffsetDiff = Offset - TempBaseOffset;
if (!isLegalMemOffset(Ty, OffsetDiff)) { assert(AllowOffsets || OffsetDiff == 0);
*NewBaseReg = newBaseRegister(Offset, OrigBaseReg);
*NewBaseOffset = Offset; if (!Target->isLegalMemOffset(Ty, OffsetDiff)) {
newBaseRegister(Base, Offset, Target->getReservedTmpReg());
OffsetDiff = 0; OffsetDiff = 0;
} }
assert(!(*NewBaseReg)->isRematerializable()); assert(!TempBaseReg->isRematerializable());
return OperandARM32Mem::create( return OperandARM32Mem::create(
Func, Ty, *NewBaseReg, Target->Func, Ty, TempBaseReg,
llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(OffsetDiff)), llvm::cast<ConstantInteger32>(Target->Ctx->getConstantInt32(OffsetDiff)),
OperandARM32Mem::Offset); OperandARM32Mem::Offset);
} }
void TargetARM32::legalizeMov(InstARM32Mov *MovInstr, Variable *OrigBaseReg, void TargetARM32::PostLoweringLegalizer::resetTempBaseIfClobberedBy(
Variable **NewBaseReg, int32_t *NewBaseOffset) { const Inst *Instr) {
bool ClobbersTempBase = false;
if (TempBaseReg != nullptr) {
Variable *Dest = Instr->getDest();
if (llvm::isa<InstARM32Call>(Instr)) {
// The following assertion is an invariant, so we remove it from the if
// test. If the invariant is ever broken/invalidated/changed, remember
// to add it back to the if condition.
assert(TempBaseReg->getRegNum() == Target->getReservedTmpReg());
// The linker may need to clobber IP if the call is too far from PC. Thus,
// we assume IP will be overwritten.
ClobbersTempBase = true;
} else if (Dest != nullptr &&
Dest->getRegNum() == TempBaseReg->getRegNum()) {
// Register redefinition.
ClobbersTempBase = true;
}
}
if (ClobbersTempBase) {
TempBaseReg = nullptr;
TempBaseOffset = 0;
}
}
void TargetARM32::PostLoweringLegalizer::legalizeMov(InstARM32Mov *MovInstr) {
Variable *Dest = MovInstr->getDest(); Variable *Dest = MovInstr->getDest();
assert(Dest != nullptr); assert(Dest != nullptr);
Type DestTy = Dest->getType(); Type DestTy = Dest->getType();
...@@ -1405,31 +1451,33 @@ void TargetARM32::legalizeMov(InstARM32Mov *MovInstr, Variable *OrigBaseReg, ...@@ -1405,31 +1451,33 @@ void TargetARM32::legalizeMov(InstARM32Mov *MovInstr, Variable *OrigBaseReg,
assert(!SrcR->isRematerializable()); assert(!SrcR->isRematerializable());
const int32_t Offset = Dest->getStackOffset(); const int32_t Offset = Dest->getStackOffset();
// This is a _mov(Mem(), Variable), i.e., a store. // This is a _mov(Mem(), Variable), i.e., a store.
_str(SrcR, createMemOperand(DestTy, Offset, OrigBaseReg, NewBaseReg, Target->_str(SrcR, createMemOperand(DestTy, StackOrFrameReg, Offset),
NewBaseOffset), MovInstr->getPredicate());
MovInstr->getPredicate());
// _str() does not have a Dest, so we add a fake-def(Dest). // _str() does not have a Dest, so we add a fake-def(Dest).
Context.insert(InstFakeDef::create(Func, Dest)); Target->Context.insert(InstFakeDef::create(Target->Func, Dest));
Legalized = true; Legalized = true;
} else if (auto *Var = llvm::dyn_cast<Variable>(Src)) { } else if (auto *Var = llvm::dyn_cast<Variable>(Src)) {
if (Var->isRematerializable()) { if (Var->isRematerializable()) {
// Rematerialization arithmetic. // This is equivalent to an x86 _lea(RematOffset(%esp/%ebp), Variable).
// ExtraOffset is only needed for frame-pointer based frames as we have
// to account for spill storage.
const int32_t ExtraOffset = const int32_t ExtraOffset =
(static_cast<SizeT>(Var->getRegNum()) == getFrameReg()) (static_cast<SizeT>(Var->getRegNum()) == Target->getFrameReg())
? getFrameFixedAllocaOffset() ? Target->getFrameFixedAllocaOffset()
: 0; : 0;
const int32_t Offset = Var->getStackOffset() + ExtraOffset; const int32_t Offset = Var->getStackOffset() + ExtraOffset;
Operand *OffsetRF = legalize(Ctx->getConstantInt32(Offset), Variable *Base = Target->getPhysicalRegister(Var->getRegNum());
Legal_Reg | Legal_Flex, Dest->getRegNum()); Variable *T = newBaseRegister(Base, Offset, Dest->getRegNum());
_add(Dest, Var, OffsetRF); Target->_mov(Dest, T);
Legalized = true; Legalized = true;
} else { } else {
if (!Var->hasReg()) { if (!Var->hasReg()) {
// This is a _mov(Variable, Mem()), i.e., a load.
const int32_t Offset = Var->getStackOffset(); const int32_t Offset = Var->getStackOffset();
_ldr(Dest, createMemOperand(DestTy, Offset, OrigBaseReg, NewBaseReg, Target->_ldr(Dest, createMemOperand(DestTy, StackOrFrameReg, Offset),
NewBaseOffset), MovInstr->getPredicate());
MovInstr->getPredicate());
Legalized = true; Legalized = true;
} }
} }
...@@ -1437,13 +1485,105 @@ void TargetARM32::legalizeMov(InstARM32Mov *MovInstr, Variable *OrigBaseReg, ...@@ -1437,13 +1485,105 @@ void TargetARM32::legalizeMov(InstARM32Mov *MovInstr, Variable *OrigBaseReg,
if (Legalized) { if (Legalized) {
if (MovInstr->isDestRedefined()) { if (MovInstr->isDestRedefined()) {
_set_dest_redefined(); Target->_set_dest_redefined();
} }
MovInstr->setDeleted(); MovInstr->setDeleted();
} }
} }
void TargetARM32::legalizeStackSlots() { // 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::PostLoweringLegalizer::legalizeMemOperand(OperandARM32Mem *Mem,
bool AllowOffsets) {
assert(!Mem->isRegReg() || !Mem->getIndex()->isRematerializable());
assert(
Mem->isRegReg() ||
Target->isLegalMemOffset(Mem->getType(), Mem->getOffset()->getValue()));
bool Legalized = false;
Variable *Base = Mem->getBase();
int32_t Offset = Mem->isRegReg() ? 0 : Mem->getOffset()->getValue();
if (Base->isRematerializable()) {
const int32_t ExtraOffset =
(static_cast<SizeT>(Base->getRegNum()) == Target->getFrameReg())
? Target->getFrameFixedAllocaOffset()
: 0;
Offset += Base->getStackOffset() + ExtraOffset;
Base = Target->getPhysicalRegister(Base->getRegNum());
assert(!Base->isRematerializable());
Legalized = true;
}
if (!Legalized) {
return nullptr;
}
if (!Mem->isRegReg()) {
return createMemOperand(Mem->getType(), Base, Offset, AllowOffsets);
}
assert(MemTraits[Mem->getType()].CanHaveIndex);
if (Offset != 0) {
if (TempBaseReg == nullptr) {
Base = newBaseRegister(Base, Offset, Target->getReservedTmpReg());
} else {
uint32_t Imm8, Rotate;
const int32_t OffsetDiff = Offset - TempBaseOffset;
if (OffsetDiff == 0) {
Base = TempBaseReg;
} else if (OperandARM32FlexImm::canHoldImm(OffsetDiff, &Rotate, &Imm8)) {
auto *OffsetDiffF = OperandARM32FlexImm::create(
Target->Func, IceType_i32, Imm8, Rotate);
Target->_add(TempBaseReg, TempBaseReg, OffsetDiffF);
TempBaseOffset += OffsetDiff;
Base = TempBaseReg;
} else if (OperandARM32FlexImm::canHoldImm(-OffsetDiff, &Rotate, &Imm8)) {
auto *OffsetDiffF = OperandARM32FlexImm::create(
Target->Func, IceType_i32, Imm8, Rotate);
Target->_sub(TempBaseReg, TempBaseReg, OffsetDiffF);
TempBaseOffset += OffsetDiff;
Base = TempBaseReg;
} else {
Base = newBaseRegister(Base, Offset, Target->getReservedTmpReg());
}
}
}
return OperandARM32Mem::create(Target->Func, Mem->getType(), Base,
Mem->getIndex(), Mem->getShiftOp(),
Mem->getShiftAmt(), Mem->getAddrMode());
}
void TargetARM32::postLowerLegalization() {
// If a stack variable's frame offset doesn't fit, convert from: // If a stack variable's frame offset doesn't fit, convert from:
// ldr X, OFF[SP] // ldr X, OFF[SP]
// to: // to:
...@@ -1453,9 +1593,8 @@ void TargetARM32::legalizeStackSlots() { ...@@ -1453,9 +1593,8 @@ void TargetARM32::legalizeStackSlots() {
// //
// This is safe because we have reserved TMP, and add for ARM does not // This is safe because we have reserved TMP, and add for ARM does not
// clobber the flags register. // clobber the flags register.
Func->dump("Before legalizeStackSlots"); Func->dump("Before postLowerLegalization");
assert(hasComputedFrame()); assert(hasComputedFrame());
Variable *OrigBaseReg = getPhysicalRegister(getFrameOrStackReg());
// Do a fairly naive greedy clustering for now. Pick the first stack slot // Do a fairly naive greedy clustering for now. Pick the first stack slot
// that's out of bounds and make a new base reg using the architecture's temp // that's out of bounds and make a new base reg using the architecture's temp
// register. If that works for the next slot, then great. Otherwise, create a // register. If that works for the next slot, then great. Otherwise, create a
...@@ -1467,24 +1606,53 @@ void TargetARM32::legalizeStackSlots() { ...@@ -1467,24 +1606,53 @@ void TargetARM32::legalizeStackSlots() {
// don't depend on this legalization. // don't depend on this legalization.
for (CfgNode *Node : Func->getNodes()) { for (CfgNode *Node : Func->getNodes()) {
Context.init(Node); Context.init(Node);
Variable *NewBaseReg = nullptr; // One legalizer per basic block, otherwise we would share the Temporary
int32_t NewBaseOffset = 0; // Base Register between basic blocks.
PostLoweringLegalizer Legalizer(this);
while (!Context.atEnd()) { while (!Context.atEnd()) {
PostIncrLoweringContext PostIncrement(Context); PostIncrLoweringContext PostIncrement(Context);
Inst *CurInstr = Context.getCur(); Inst *CurInstr = Context.getCur();
Variable *Dest = CurInstr->getDest();
// Check if the previous TempBaseReg is clobbered, and reset if needed.
// Check if the previous NewBaseReg is clobbered, and reset if needed. Legalizer.resetTempBaseIfClobberedBy(CurInstr);
if ((Dest && NewBaseReg && Dest->hasReg() &&
Dest->getRegNum() == NewBaseReg->getBaseRegNum()) ||
llvm::isa<InstFakeKill>(CurInstr)) {
NewBaseReg = nullptr;
NewBaseOffset = 0;
}
if (auto *MovInstr = llvm::dyn_cast<InstARM32Mov>(CurInstr)) { if (auto *MovInstr = llvm::dyn_cast<InstARM32Mov>(CurInstr)) {
legalizeMov(MovInstr, OrigBaseReg, &NewBaseReg, &NewBaseOffset); Legalizer.legalizeMov(MovInstr);
} else if (auto *LdrInstr = llvm::dyn_cast<InstARM32Ldr>(CurInstr)) {
if (OperandARM32Mem *LegalMem = Legalizer.legalizeMemOperand(
llvm::cast<OperandARM32Mem>(LdrInstr->getSrc(0)))) {
_ldr(CurInstr->getDest(), LegalMem, LdrInstr->getPredicate());
CurInstr->setDeleted();
}
} else if (auto *LdrexInstr = llvm::dyn_cast<InstARM32Ldrex>(CurInstr)) {
constexpr bool DisallowOffsetsBecauseLdrex = false;
if (OperandARM32Mem *LegalMem = Legalizer.legalizeMemOperand(
llvm::cast<OperandARM32Mem>(LdrexInstr->getSrc(0)),
DisallowOffsetsBecauseLdrex)) {
_ldrex(CurInstr->getDest(), LegalMem, LdrexInstr->getPredicate());
CurInstr->setDeleted();
}
} else if (auto *StrInstr = llvm::dyn_cast<InstARM32Str>(CurInstr)) {
if (OperandARM32Mem *LegalMem = Legalizer.legalizeMemOperand(
llvm::cast<OperandARM32Mem>(StrInstr->getSrc(1)))) {
_str(llvm::cast<Variable>(CurInstr->getSrc(0)), LegalMem,
StrInstr->getPredicate());
CurInstr->setDeleted();
}
} else if (auto *StrexInstr = llvm::dyn_cast<InstARM32Strex>(CurInstr)) {
constexpr bool DisallowOffsetsBecauseStrex = false;
if (OperandARM32Mem *LegalMem = Legalizer.legalizeMemOperand(
llvm::cast<OperandARM32Mem>(StrexInstr->getSrc(1)),
DisallowOffsetsBecauseStrex)) {
_strex(CurInstr->getDest(), llvm::cast<Variable>(CurInstr->getSrc(0)),
LegalMem, StrexInstr->getPredicate());
CurInstr->setDeleted();
}
} }
// Sanity-check: the Legalizer will either have no Temp, or it will be
// bound to IP.
Legalizer.assertNoTempOrAssignedToIP();
} }
} }
} }
...@@ -1502,15 +1670,14 @@ Operand *TargetARM32::loOperand(Operand *Operand) { ...@@ -1502,15 +1670,14 @@ Operand *TargetARM32::loOperand(Operand *Operand) {
// increment) in case of duplication. // increment) in case of duplication.
assert(Mem->getAddrMode() == OperandARM32Mem::Offset || assert(Mem->getAddrMode() == OperandARM32Mem::Offset ||
Mem->getAddrMode() == OperandARM32Mem::NegOffset); Mem->getAddrMode() == OperandARM32Mem::NegOffset);
Variable *BaseR = legalizeToReg(Mem->getBase());
if (Mem->isRegReg()) { if (Mem->isRegReg()) {
Variable *IndexR = legalizeToReg(Mem->getIndex()); Variable *IndexR = legalizeToReg(Mem->getIndex());
return OperandARM32Mem::create(Func, IceType_i32, BaseR, IndexR, return OperandARM32Mem::create(Func, IceType_i32, Mem->getBase(), IndexR,
Mem->getShiftOp(), Mem->getShiftAmt(), Mem->getShiftOp(), Mem->getShiftAmt(),
Mem->getAddrMode()); Mem->getAddrMode());
} else { } else {
return OperandARM32Mem::create(Func, IceType_i32, BaseR, Mem->getOffset(), return OperandARM32Mem::create(Func, IceType_i32, Mem->getBase(),
Mem->getAddrMode()); Mem->getOffset(), Mem->getAddrMode());
} }
} }
llvm::report_fatal_error("Unsupported operand type"); llvm::report_fatal_error("Unsupported operand type");
...@@ -4791,35 +4958,6 @@ bool matchOffsetBase(const VariablesMetadata *VMetadata, Variable **Base, ...@@ -4791,35 +4958,6 @@ bool matchOffsetBase(const VariablesMetadata *VMetadata, Variable **Base,
} }
} // end of anonymous namespace } // 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, OperandARM32Mem *TargetARM32::formAddressingMode(Type Ty, Cfg *Func,
const Inst *LdSt, const Inst *LdSt,
Operand *Base) { Operand *Base) {
...@@ -4955,17 +5093,15 @@ OperandARM32Mem *TargetARM32::formAddressingMode(Type Ty, Cfg *Func, ...@@ -4955,17 +5093,15 @@ OperandARM32Mem *TargetARM32::formAddressingMode(Type Ty, Cfg *Func,
assert(OffsetImm < 0 ? (ValidImmMask & -OffsetImm) == -OffsetImm assert(OffsetImm < 0 ? (ValidImmMask & -OffsetImm) == -OffsetImm
: (ValidImmMask & OffsetImm) == OffsetImm); : (ValidImmMask & OffsetImm) == OffsetImm);
Variable *BaseR = makeReg(getPointerType());
Context.insert(InstAssign::create(Func, BaseR, BaseVar));
if (OffsetReg != nullptr) { if (OffsetReg != nullptr) {
Variable *OffsetR = makeReg(getPointerType()); Variable *OffsetR = makeReg(getPointerType());
Context.insert(InstAssign::create(Func, OffsetR, OffsetReg)); Context.insert(InstAssign::create(Func, OffsetR, OffsetReg));
return OperandARM32Mem::create(Func, Ty, BaseR, OffsetR, ShiftKind, return OperandARM32Mem::create(Func, Ty, BaseVar, OffsetR, ShiftKind,
OffsetRegShamt); OffsetRegShamt);
} }
return OperandARM32Mem::create( return OperandARM32Mem::create(
Func, Ty, BaseR, Func, Ty, BaseVar,
llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(OffsetImm))); llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(OffsetImm)));
} }
...@@ -5189,7 +5325,8 @@ Operand *TargetARM32::legalize(Operand *From, LegalMask Allowed, ...@@ -5189,7 +5325,8 @@ Operand *TargetARM32::legalize(Operand *From, LegalMask Allowed,
Variable *RegBase = nullptr; Variable *RegBase = nullptr;
Variable *RegIndex = nullptr; Variable *RegIndex = nullptr;
assert(Base); assert(Base);
RegBase = legalizeToReg(Base); RegBase = llvm::cast<Variable>(
legalize(Base, Legal_Reg | Legal_Rematerializable));
assert(Ty < MemTraitsSize); assert(Ty < MemTraitsSize);
if (Index) { if (Index) {
assert(Offset == nullptr); assert(Offset == nullptr);
...@@ -5324,6 +5461,10 @@ Operand *TargetARM32::legalize(Operand *From, LegalMask Allowed, ...@@ -5324,6 +5461,10 @@ Operand *TargetARM32::legalize(Operand *From, LegalMask Allowed,
if (auto *Var = llvm::dyn_cast<Variable>(From)) { if (auto *Var = llvm::dyn_cast<Variable>(From)) {
if (Var->isRematerializable()) { if (Var->isRematerializable()) {
if (Allowed & Legal_Rematerializable) {
return From;
}
// TODO(jpp): We don't need to rematerialize Var if legalize() was invoked // TODO(jpp): We don't need to rematerialize Var if legalize() was invoked
// for a Variable in a Mem operand. // for a Variable in a Mem operand.
Variable *T = makeReg(Var->getType(), RegNum); Variable *T = makeReg(Var->getType(), RegNum);
...@@ -5386,9 +5527,10 @@ OperandARM32Mem *TargetARM32::formMemoryOperand(Operand *Operand, Type Ty) { ...@@ -5386,9 +5527,10 @@ OperandARM32Mem *TargetARM32::formMemoryOperand(Operand *Operand, Type Ty) {
// If we didn't do address mode optimization, then we only have a // 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 // base/offset to work with. ARM always requires a base register, so
// just use that to hold the operand. // just use that to hold the operand.
Variable *BaseR = legalizeToReg(Operand); Variable *Base = llvm::cast<Variable>(
legalize(Operand, Legal_Reg | Legal_Rematerializable));
return OperandARM32Mem::create( return OperandARM32Mem::create(
Func, Ty, BaseR, Func, Ty, Base,
llvm::cast<ConstantInteger32>(Ctx->getConstantZero(IceType_i32))); llvm::cast<ConstantInteger32>(Ctx->getConstantZero(IceType_i32)));
} }
......
...@@ -90,7 +90,7 @@ public: ...@@ -90,7 +90,7 @@ public:
SizeT getFrameOrStackReg() const override { SizeT getFrameOrStackReg() const override {
return UsesFramePointer ? getFrameReg() : getStackReg(); return UsesFramePointer ? getFrameReg() : getStackReg();
} }
SizeT getReservedTmpReg() const { return RegARM32::Reg_ip; } int32_t getReservedTmpReg() const { return RegARM32::Reg_ip; }
size_t typeWidthInBytesOnStack(Type Ty) const override { size_t typeWidthInBytesOnStack(Type Ty) const override {
// Round up to the next multiple of 4 bytes. In particular, i1, i8, and i16 // Round up to the next multiple of 4 bytes. In particular, i1, i8, and i16
...@@ -141,12 +141,12 @@ public: ...@@ -141,12 +141,12 @@ public:
} }
enum OperandLegalization { enum OperandLegalization {
Legal_None = 0,
Legal_Reg = 1 << 0, /// physical register, not stack location Legal_Reg = 1 << 0, /// physical register, not stack location
Legal_Flex = 1 << 1, /// A flexible operand2, which can hold rotated small Legal_Flex = 1 << 1, /// A flexible operand2, which can hold rotated small
/// immediates, shifted registers, or modified fp imm. /// immediates, shifted registers, or modified fp imm.
Legal_Mem = 1 << 2, /// includes [r0, r1 lsl #2] as well as [sp, #12] Legal_Mem = 1 << 2, /// includes [r0, r1 lsl #2] as well as [sp, #12]
Legal_All = ~Legal_None Legal_Rematerializable = 1 << 3,
Legal_All = ~Legal_Rematerializable,
}; };
using LegalMask = uint32_t; using LegalMask = uint32_t;
...@@ -816,35 +816,67 @@ protected: ...@@ -816,35 +816,67 @@ protected:
// method that the Parser could call. // method that the Parser could call.
void findMaxStackOutArgsSize(); void findMaxStackOutArgsSize();
/// Run a pass through stack variables and ensure that the offsets are legal. /// Returns true if the given Offset can be represented in a Load/Store Mem
/// If the offset is not legal, use a new base register that accounts for the /// Operand.
/// offset, such that the addressing mode offset bits are now legal.
void legalizeStackSlots();
/// Returns true if the given Offset can be represented in a ldr/str.
bool isLegalMemOffset(Type Ty, int32_t Offset) const; bool isLegalMemOffset(Type Ty, int32_t Offset) const;
// Creates a new Base register centered around
// [OrigBaseReg, +/- Offset]. void postLowerLegalization();
Variable *newBaseRegister(int32_t Offset, Variable *OrigBaseReg);
/// Creates a new, legal OperandARM32Mem for accessing OrigBase + Offset. The class PostLoweringLegalizer {
/// returned mem operand is a legal operand for accessing memory that is of PostLoweringLegalizer() = delete;
/// type Ty. PostLoweringLegalizer(const PostLoweringLegalizer &) = delete;
/// PostLoweringLegalizer &operator=(const PostLoweringLegalizer &) = delete;
/// If [OrigBaseReg, #Offset] is encodable, then the method returns a Mem
/// operand expressing it. Otherwise, public:
/// explicit PostLoweringLegalizer(TargetARM32 *Target)
/// if [*NewBaseReg, #Offset-*NewBaseOffset] is encodable, the method will : Target(Target), StackOrFrameReg(Target->getPhysicalRegister(
/// return that. Otherwise, Target->getFrameOrStackReg())) {}
///
/// a new base register ip=OrigBaseReg+Offset is created, and the method void resetTempBaseIfClobberedBy(const Inst *Instr);
/// returns [ip, #0].
OperandARM32Mem *createMemOperand(Type Ty, int32_t Offset, // Ensures that the TempBase register held by the this legalizer (if any) is
Variable *OrigBaseReg, // assigned to IP.
Variable **NewBaseReg, void assertNoTempOrAssignedToIP() const {
int32_t *NewBaseOffset); assert(TempBaseReg == nullptr ||
/// Legalizes Mov if its Source (or Destination) is a spilled Variable. Moves TempBaseReg->getRegNum() == Target->getReservedTmpReg());
/// to memory become store instructions, and moves from memory, loads. }
void legalizeMov(InstARM32Mov *Mov, Variable *OrigBaseReg,
Variable **NewBaseReg, int32_t *NewBaseOffset); // Legalizes Mem. if Mem.Base is a Reamaterializable variable, Mem.Offset is
// fixed up.
OperandARM32Mem *legalizeMemOperand(OperandARM32Mem *Mem,
bool AllowOffsets = true);
/// Legalizes Mov if its Source (or Destination) is a spilled Variable, or
/// if its Source is a Rematerializable variable (this form is used in lieu
/// of lea, which is not available in ARM.)
///
/// Moves to memory become store instructions, and moves from memory, loads.
void legalizeMov(InstARM32Mov *Mov);
private:
/// Creates a new Base register centered around [Base, +/- Offset].
Variable *newBaseRegister(Variable *Base, int32_t Offset,
int32_t ScratchRegNum);
/// Creates a new, legal OperandARM32Mem for accessing Base + Offset.
/// The returned mem operand is a legal operand for accessing memory that is
/// of type Ty.
///
/// If [Base, #Offset] is encodable, then the method returns a Mem operand
/// expressing it. Otherwise,
///
/// if [TempBaseReg, #Offset-TempBaseOffset] is a valid memory operand, the
/// method will return that. Otherwise,
///
/// a new base register ip=Base+Offset is created, and the method returns a
/// memory operand expressing [ip, #0].
OperandARM32Mem *createMemOperand(Type Ty, Variable *Base, int32_t Offset,
bool AllowOffsets = true);
TargetARM32 *const Target;
Variable *const StackOrFrameReg;
Variable *TempBaseReg = nullptr;
int32_t TempBaseOffset = 0;
};
TargetARM32Features CPUFeatures; TargetARM32Features CPUFeatures;
bool UsesFramePointer = false; bool UsesFramePointer = false;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment