[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>.
diff --git a/src/IceTargetLoweringMIPS32.cpp b/src/IceTargetLoweringMIPS32.cpp index 6f602c9..237eef4 100644 --- a/src/IceTargetLoweringMIPS32.cpp +++ b/src/IceTargetLoweringMIPS32.cpp
@@ -2889,7 +2889,234 @@ 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::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();
diff --git a/src/IceTargetLoweringMIPS32.h b/src/IceTargetLoweringMIPS32.h index 5b722f6..717b7d9 100644 --- a/src/IceTargetLoweringMIPS32.h +++ b/src/IceTargetLoweringMIPS32.h
@@ -714,6 +714,9 @@ private: ENABLE_MAKE_UNIQUE; + OperandMIPS32Mem *formAddressingMode(Type Ty, Cfg *Func, const Inst *LdSt, + Operand *Base); + class ComputationTracker { public: ComputationTracker() = default;
diff --git a/tests_lit/llvm2ice_tests/mips-address-mode-opt.ll b/tests_lit/llvm2ice_tests/mips-address-mode-opt.ll new file mode 100644 index 0000000..1cb53c1 --- /dev/null +++ b/tests_lit/llvm2ice_tests/mips-address-mode-opt.ll
@@ -0,0 +1,44 @@ +; 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)