Commit 47ef0be8 by Jaydeep Patil Committed by Jim Stichnoth

[SubZero] Implement address optimization for MIPS

The patch implements address optimization for MIPS. R=stichnot@chromium.org Review URL: https://codereview.chromium.org/2313293002 . Patch from Jaydeep Patil <jaydeep.patil@imgtec.com>.
parent f315f0d9
......@@ -2889,7 +2889,234 @@ void TargetMIPS32::lowerLoad(const InstLoad *Instr) {
lowerAssign(Assign);
}
void TargetMIPS32::doAddressOptLoad() { UnimplementedError(getFlags()); }
namespace {
void dumpAddressOpt(const Cfg *Func, const Variable *Base, int32_t Offset,
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 << ", 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);
bool Optimized = false;
if (auto *SrcVar = llvm::dyn_cast<Variable>(SrcOp)) {
if (!VMetadata->isMultiDef(SrcVar) ||
// TODO: ensure SrcVar stays single-BB
false) {
Optimized = true;
*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;
Optimized = true;
}
}
if (Optimized) {
*Reason = VarAssign;
}
return Optimized;
}
bool isAddOrSub(const Inst *Instr, InstArithmetic::OpKind *Kind) {
if (const auto *Arith = llvm::dyn_cast<InstArithmetic>(Instr)) {
switch (Arith->getOp()) {
default:
return false;
case InstArithmetic::Add:
case InstArithmetic::Sub:
*Kind = Arith->getOp();
return true;
}
}
return false;
}
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
OperandMIPS32Mem *TargetMIPS32::formAddressingMode(Type Ty, Cfg *Func,
const Inst *LdSt,
Operand *Base) {
assert(Base != nullptr);
int32_t OffsetImm = 0;
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)) {
UnimplementedError(getFlags());
return nullptr;
}
auto *BaseVar = llvm::dyn_cast<Variable>(Base);
if (BaseVar == nullptr)
return nullptr;
const VariablesMetadata *VMetadata = Func->getVMetadata();
const Inst *Reason = nullptr;
do {
if (Reason != nullptr) {
dumpAddressOpt(Func, BaseVar, OffsetImm, Reason);
Reason = nullptr;
}
if (matchAssign(VMetadata, &BaseVar, &OffsetImm, &Reason)) {
continue;
}
if (matchOffsetBase(VMetadata, &BaseVar, &OffsetImm, &Reason)) {
continue;
}
} while (Reason);
if (BaseVar == nullptr) {
// We need base register rather than just OffsetImm. Move the OffsetImm to
// BaseVar and form 0(BaseVar) addressing.
const Type PointerType = getPointerType();
BaseVar = makeReg(PointerType);
Context.insert<InstAssign>(BaseVar, Ctx->getConstantInt32(OffsetImm));
OffsetImm = 0;
} else if (OffsetImm != 0) {
// If the OffsetImm is more than signed 16-bit value then add it in the
// BaseVar and form 0(BaseVar) addressing.
const int32_t PositiveOffset = OffsetImm > 0 ? OffsetImm : -OffsetImm;
const InstArithmetic::OpKind Op =
OffsetImm > 0 ? InstArithmetic::Add : InstArithmetic::Sub;
constexpr bool ZeroExt = false;
if (!OperandMIPS32Mem::canHoldOffset(Ty, ZeroExt, OffsetImm)) {
const Type PointerType = getPointerType();
Variable *T = makeReg(PointerType);
Context.insert<InstArithmetic>(Op, T, BaseVar,
Ctx->getConstantInt32(PositiveOffset));
BaseVar = T;
OffsetImm = 0;
}
}
assert(BaseVar != nullptr);
assert(OffsetImm < 0 ? (-OffsetImm & 0x0000ffff) == -OffsetImm
: (OffsetImm & 0x0000ffff) == OffsetImm);
return OperandMIPS32Mem::create(
Func, Ty, BaseVar,
llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(OffsetImm)));
}
void TargetMIPS32::doAddressOptLoad() {
Inst *Instr = iteratorToInst(Context.getCur());
assert(llvm::isa<InstLoad>(Instr));
Variable *Dest = Instr->getDest();
Operand *Addr = Instr->getSrc(0);
if (OperandMIPS32Mem *Mem =
formAddressingMode(Dest->getType(), Func, Instr, Addr)) {
Instr->setDeleted();
Context.insert<InstLoad>(Dest, Mem);
}
}
void TargetMIPS32::randomlyInsertNop(float Probability,
RandomNumberGenerator &RNG) {
......@@ -2959,7 +3186,17 @@ void TargetMIPS32::lowerStore(const InstStore *Instr) {
}
}
void TargetMIPS32::doAddressOptStore() { UnimplementedError(getFlags()); }
void TargetMIPS32::doAddressOptStore() {
Inst *Instr = iteratorToInst(Context.getCur());
assert(llvm::isa<InstStore>(Instr));
Operand *Src = Instr->getSrc(0);
Operand *Addr = Instr->getSrc(1);
if (OperandMIPS32Mem *Mem =
formAddressingMode(Src->getType(), Func, Instr, Addr)) {
Instr->setDeleted();
Context.insert<InstStore>(Src, Mem);
}
}
void TargetMIPS32::lowerSwitch(const InstSwitch *Instr) {
Operand *Src = Instr->getComparison();
......
......@@ -714,6 +714,9 @@ protected:
private:
ENABLE_MAKE_UNIQUE;
OperandMIPS32Mem *formAddressingMode(Type Ty, Cfg *Func, const Inst *LdSt,
Operand *Base);
class ComputationTracker {
public:
ComputationTracker() = default;
......
; This file checks support for address mode optimization.
; This test file is same as address-mode-opt.ll however the functions in this
; file are relevant to MIPS only.
; RUN: %if --need=target_MIPS32 --need=allow_dump \
; RUN: --command %p2i --filetype=asm --assemble --disassemble \
; RUN: --target mips32 -i %s --args -O2 --skip-unimplemented \
; RUN: -allow-externally-defined-symbols \
; RUN: | %if --need=target_MIPS32 --need=allow_dump \
; RUN: --command FileCheck --check-prefix MIPS32 %s
define internal float @load_arg_plus_offset(float* %arg) {
entry:
%arg.int = ptrtoint float* %arg to i32
%addr.int = add i32 %arg.int, 16
%addr.ptr = inttoptr i32 %addr.int to float*
%addr.load = load float, float* %addr.ptr, align 4
ret float %addr.load
}
; MIPS32-LABEL: load_arg_plus_offset
; MIPS32: lwc1 $f0,16(a0)
define internal float @load_arg_minus_offset(float* %arg) {
entry:
%arg.int = ptrtoint float* %arg to i32
%addr.int = sub i32 %arg.int, 16
%addr.ptr = inttoptr i32 %addr.int to float*
%addr.load = load float, float* %addr.ptr, align 4
ret float %addr.load
}
; MIPS32-LABEL: load_arg_minus_offset
; MIPS32 lwc1 $f0,-16(a0)
define internal float @address_mode_opt_chaining(float* %arg) {
entry:
%arg.int = ptrtoint float* %arg to i32
%addr1.int = add i32 12, %arg.int
%addr2.int = sub i32 %addr1.int, 4
%addr2.ptr = inttoptr i32 %addr2.int to float*
%addr2.load = load float, float* %addr2.ptr, align 4
ret float %addr2.load
}
; MIPS32-LABEL: address_mode_opt_chaining
; MIPS32 lwc1 $f0,8(a0)
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