From c5206367a672d52b6761490b144e0221f8dafcf3 Mon Sep 17 00:00:00 2001 From: Tom Vijlbrief Date: Sun, 3 Aug 2025 09:15:58 +0200 Subject: [PATCH 1/7] Improve AVR loop code generation --- clang/lib/Basic/Targets/AVR.h | 3 +- llvm/lib/Target/AVR/AVRTargetMachine.cpp | 11 ++- llvm/lib/Target/AVR/AVRTargetMachine.h | 2 + llvm/lib/Target/AVR/AVRTargetTransformInfo.h | 79 ++++++++++++++++++++ 4 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 llvm/lib/Target/AVR/AVRTargetTransformInfo.h diff --git a/clang/lib/Basic/Targets/AVR.h b/clang/lib/Basic/Targets/AVR.h index 75c969fd59dc9..11aa844f2ce55 100644 --- a/clang/lib/Basic/Targets/AVR.h +++ b/clang/lib/Basic/Targets/AVR.h @@ -57,7 +57,8 @@ class LLVM_LIBRARY_VISIBILITY AVRTargetInfo : public TargetInfo { Int16Type = SignedInt; Char32Type = UnsignedLong; SigAtomicType = SignedChar; - resetDataLayout("e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"); + resetDataLayout( + "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-n16:8-a:8"); } void getTargetDefines(const LangOptions &Opts, diff --git a/llvm/lib/Target/AVR/AVRTargetMachine.cpp b/llvm/lib/Target/AVR/AVRTargetMachine.cpp index b75417a0896a5..8d05005e287f4 100644 --- a/llvm/lib/Target/AVR/AVRTargetMachine.cpp +++ b/llvm/lib/Target/AVR/AVRTargetMachine.cpp @@ -19,6 +19,7 @@ #include "AVR.h" #include "AVRMachineFunctionInfo.h" +#include "AVRTargetTransformInfo.h" #include "AVRTargetObjectFile.h" #include "MCTargetDesc/AVRMCTargetDesc.h" #include "TargetInfo/AVRTargetInfo.h" @@ -28,7 +29,7 @@ namespace llvm { static const char *AVRDataLayout = - "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"; + "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-n16:8-a:8"; /// Processes a CPU name. static StringRef getCPU(StringRef CPU) { @@ -62,7 +63,9 @@ namespace { class AVRPassConfig : public TargetPassConfig { public: AVRPassConfig(AVRTargetMachine &TM, PassManagerBase &PM) - : TargetPassConfig(TM, PM) {} + : TargetPassConfig(TM, PM) { + EnableLoopTermFold = true; + } AVRTargetMachine &getAVRTargetMachine() const { return getTM(); @@ -107,6 +110,10 @@ const AVRSubtarget *AVRTargetMachine::getSubtargetImpl(const Function &) const { return &SubTarget; } +TargetTransformInfo AVRTargetMachine::getTargetTransformInfo(const Function &F) const { + return TargetTransformInfo(std::make_unique(this, F)); +} + MachineFunctionInfo *AVRTargetMachine::createMachineFunctionInfo( BumpPtrAllocator &Allocator, const Function &F, const TargetSubtargetInfo *STI) const { diff --git a/llvm/lib/Target/AVR/AVRTargetMachine.h b/llvm/lib/Target/AVR/AVRTargetMachine.h index 167d0076e9581..9452b3d8cd8a5 100644 --- a/llvm/lib/Target/AVR/AVRTargetMachine.h +++ b/llvm/lib/Target/AVR/AVRTargetMachine.h @@ -48,6 +48,8 @@ class AVRTargetMachine : public CodeGenTargetMachineImpl { createMachineFunctionInfo(BumpPtrAllocator &Allocator, const Function &F, const TargetSubtargetInfo *STI) const override; + TargetTransformInfo getTargetTransformInfo(const Function &F) const override; + bool isNoopAddrSpaceCast(unsigned SrcAs, unsigned DestAs) const override { // While AVR has different address spaces, they are all represented by // 16-bit pointers that can be freely casted between (of course, a pointer diff --git a/llvm/lib/Target/AVR/AVRTargetTransformInfo.h b/llvm/lib/Target/AVR/AVRTargetTransformInfo.h new file mode 100644 index 0000000000000..e1abd10619823 --- /dev/null +++ b/llvm/lib/Target/AVR/AVRTargetTransformInfo.h @@ -0,0 +1,79 @@ +//===- AVRTargetTransformInfo.h - AVR specific TTI ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file defines a TargetTransformInfoImplBase conforming object specific +/// to the AVR target machine. It uses the target's detailed information to +/// provide more precise answers to certain TTI queries, while letting the +/// target independent and default TTI implementations handle the rest. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_AVR_AVRTARGETTRANSFORMINFO_H +#define LLVM_LIB_TARGET_AVR_AVRTARGETTRANSFORMINFO_H + +#include "AVRSubtarget.h" +#include "AVRTargetMachine.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/BasicTTIImpl.h" +#include "llvm/IR/Function.h" +#include + +namespace llvm { + +class AVRTTIImpl final : public BasicTTIImplBase { + using BaseT = BasicTTIImplBase; + using TTI = TargetTransformInfo; + + friend BaseT; + + const AVRSubtarget *ST; + const AVRTargetLowering *TLI; + + const AVRSubtarget *getST() const { return ST; } + const AVRTargetLowering *getTLI() const { return TLI; } + +public: + explicit AVRTTIImpl(const AVRTargetMachine *TM, const Function &F) + : BaseT(TM, F.getDataLayout()), ST(TM->getSubtargetImpl(F)), + TLI(ST->getTargetLowering()) {} + +#if 0 // TODO Examine if these options result in better code generation + /// Return the cost of materializing an immediate for a value operand of + /// a store instruction. + InstructionCost getStoreImmCost(Type *VecTy, TTI::OperandValueInfo OpInfo, + TTI::TargetCostKind CostKind) const; + + InstructionCost getIntImmCost(const APInt &Imm, Type *Ty, + TTI::TargetCostKind CostKind) const override; + InstructionCost getIntImmCostInst(unsigned Opcode, unsigned Idx, + const APInt &Imm, Type *Ty, + TTI::TargetCostKind CostKind, + Instruction *Inst = nullptr) const override; + InstructionCost + getIntImmCostIntrin(Intrinsic::ID IID, unsigned Idx, const APInt &Imm, + Type *Ty, TTI::TargetCostKind CostKind) const override; + + InstructionCost + getIntrinsicInstrCost(const IntrinsicCostAttributes &ICA, + TTI::TargetCostKind CostKind) const override; + + InstructionCost getCmpSelInstrCost( + unsigned Opcode, Type *ValTy, Type *CondTy, CmpInst::Predicate VecPred, + TTI::TargetCostKind CostKind, + TTI::OperandValueInfo Op1Info = {TTI::OK_AnyValue, TTI::OP_None}, + TTI::OperandValueInfo Op2Info = {TTI::OK_AnyValue, TTI::OP_None}, + const Instruction *I = nullptr) const override; +#endif + + bool isLSRCostLess(const TargetTransformInfo::LSRCost &C1, + const TargetTransformInfo::LSRCost &C2) const override {return C1.Insns < C2.Insns;} +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_AVR_AVRTARGETTRANSFORMINFO_H From 68222e983264aa5eb50ab4e655ab260da75ed8e2 Mon Sep 17 00:00:00 2001 From: Tom Vijlbrief Date: Mon, 4 Aug 2025 22:25:00 +0200 Subject: [PATCH 2/7] handle pre/post and complete tests --- llvm/lib/Target/AVR/AVRTargetTransformInfo.h | 49 ++++++++------------ llvm/test/CodeGen/AVR/bug-143247.ll | 4 +- llvm/test/CodeGen/AVR/load.ll | 34 +++++++------- llvm/test/CodeGen/AVR/shift.ll | 2 +- 4 files changed, 39 insertions(+), 50 deletions(-) diff --git a/llvm/lib/Target/AVR/AVRTargetTransformInfo.h b/llvm/lib/Target/AVR/AVRTargetTransformInfo.h index e1abd10619823..b877918a18151 100644 --- a/llvm/lib/Target/AVR/AVRTargetTransformInfo.h +++ b/llvm/lib/Target/AVR/AVRTargetTransformInfo.h @@ -33,6 +33,7 @@ class AVRTTIImpl final : public BasicTTIImplBase { const AVRSubtarget *ST; const AVRTargetLowering *TLI; + const Function *currentF; const AVRSubtarget *getST() const { return ST; } const AVRTargetLowering *getTLI() const { return TLI; } @@ -40,38 +41,26 @@ class AVRTTIImpl final : public BasicTTIImplBase { public: explicit AVRTTIImpl(const AVRTargetMachine *TM, const Function &F) : BaseT(TM, F.getDataLayout()), ST(TM->getSubtargetImpl(F)), - TLI(ST->getTargetLowering()) {} - -#if 0 // TODO Examine if these options result in better code generation - /// Return the cost of materializing an immediate for a value operand of - /// a store instruction. - InstructionCost getStoreImmCost(Type *VecTy, TTI::OperandValueInfo OpInfo, - TTI::TargetCostKind CostKind) const; - - InstructionCost getIntImmCost(const APInt &Imm, Type *Ty, - TTI::TargetCostKind CostKind) const override; - InstructionCost getIntImmCostInst(unsigned Opcode, unsigned Idx, - const APInt &Imm, Type *Ty, - TTI::TargetCostKind CostKind, - Instruction *Inst = nullptr) const override; - InstructionCost - getIntImmCostIntrin(Intrinsic::ID IID, unsigned Idx, const APInt &Imm, - Type *Ty, TTI::TargetCostKind CostKind) const override; - - InstructionCost - getIntrinsicInstrCost(const IntrinsicCostAttributes &ICA, - TTI::TargetCostKind CostKind) const override; - - InstructionCost getCmpSelInstrCost( - unsigned Opcode, Type *ValTy, Type *CondTy, CmpInst::Predicate VecPred, - TTI::TargetCostKind CostKind, - TTI::OperandValueInfo Op1Info = {TTI::OK_AnyValue, TTI::OP_None}, - TTI::OperandValueInfo Op2Info = {TTI::OK_AnyValue, TTI::OP_None}, - const Instruction *I = nullptr) const override; -#endif + TLI(ST->getTargetLowering()), currentF(&F) {} bool isLSRCostLess(const TargetTransformInfo::LSRCost &C1, - const TargetTransformInfo::LSRCost &C2) const override {return C1.Insns < C2.Insns;} + const TargetTransformInfo::LSRCost &C2) const override { + // Detect %incdec.ptr because loop-reduce loses them + for (const BasicBlock &BB : *currentF) { + if (BB.getName().find("while.body") != std::string::npos) { + for (const Instruction &I : BB) { + std::string str; + llvm::raw_string_ostream(str) << I; + if (str.find("%incdec.ptr") != std::string::npos) + return false; + } + } + } + if (C2.Insns == ~0u) + return true; + return 2 * C1.Insns + C1.AddRecCost + C1.SetupCost + C1.NumRegs < + 2 * C2.Insns + C2.AddRecCost + C2.SetupCost + C2.NumRegs; + } }; } // end namespace llvm diff --git a/llvm/test/CodeGen/AVR/bug-143247.ll b/llvm/test/CodeGen/AVR/bug-143247.ll index 07c4c6562c950..d4493272af76d 100644 --- a/llvm/test/CodeGen/AVR/bug-143247.ll +++ b/llvm/test/CodeGen/AVR/bug-143247.ll @@ -8,18 +8,18 @@ define void @complex_sbi() { ; CHECK: ; %bb.0: ; %entry ; CHECK-NEXT: push r16 ; CHECK-NEXT: push r17 -; CHECK-NEXT: ldi r24, 0 +; CHECK-NEXT: ldi r24, 1 ; CHECK-NEXT: ldi r25, 0 ; CHECK-NEXT: .LBB0_1: ; %while.cond ; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1 ; CHECK-NEXT: sbi 1, 7 -; CHECK-NEXT: adiw r24, 1 ; CHECK-NEXT: movw r16, r24 ; CHECK-NEXT: andi r24, 15 ; CHECK-NEXT: andi r25, 0 ; CHECK-NEXT: adiw r24, 1 ; CHECK-NEXT: call nil ; CHECK-NEXT: movw r24, r16 +; CHECK-NEXT: adiw r24, 1 ; CHECK-NEXT: rjmp .LBB0_1 entry: br label %while.cond diff --git a/llvm/test/CodeGen/AVR/load.ll b/llvm/test/CodeGen/AVR/load.ll index 5de6b48652914..e2e7844b49c02 100644 --- a/llvm/test/CodeGen/AVR/load.ll +++ b/llvm/test/CodeGen/AVR/load.ll @@ -1,4 +1,4 @@ -; RUN: llc -mattr=avr6,sram < %s -mtriple=avr -verify-machineinstrs | FileCheck %s +; RUN: llc -mattr=avr6,sram < %s -mtriple=avr-none -verify-machineinstrs | FileCheck %s define i8 @load8(ptr %x) { ; CHECK-LABEL: load8: @@ -76,26 +76,26 @@ while.end: ; preds = %while.body, %entry ret i8 %r.0.lcssa } -define i16 @load16postinc(ptr %x, i16 %y) { +define i16 @load16postinc(ptr %p, i16 %cnt) { ; CHECK-LABEL: load16postinc: ; CHECK: ld {{.*}}, {{[XYZ]}}+ ; CHECK: ld {{.*}}, {{[XYZ]}}+ entry: - %tobool2 = icmp eq i16 %y, 0 - br i1 %tobool2, label %while.end, label %while.body -while.body: ; preds = %entry, %while.body - %r.05 = phi i16 [ %add, %while.body ], [ 0, %entry ] - %y.addr.04 = phi i16 [ %dec, %while.body ], [ %y, %entry ] - %x.addr.03 = phi ptr [ %incdec.ptr, %while.body ], [ %x, %entry ] - %dec = add nsw i16 %y.addr.04, -1 - %incdec.ptr = getelementptr inbounds i16, ptr %x.addr.03, i16 1 - %0 = load i16, ptr %x.addr.03 - %add = add nsw i16 %0, %r.05 - %tobool = icmp eq i16 %dec, 0 - br i1 %tobool, label %while.end, label %while.body -while.end: ; preds = %while.body, %entry - %r.0.lcssa = phi i16 [ 0, %entry ], [ %add, %while.body ] - ret i16 %r.0.lcssa + %cmp3 = icmp sgt i16 %cnt, 0 + br i1 %cmp3, label %for.body, label %for.cond.cleanup +for.cond.cleanup: ; preds = %for.body, %entry + %sum.0.lcssa = phi i16 [ 0, %entry ], [ %add, %for.body ] + ret i16 %sum.0.lcssa +for.body: ; preds = %entry, %for.body + %i.06 = phi i16 [ %inc, %for.body ], [ 0, %entry ] + %sum.05 = phi i16 [ %add, %for.body ], [ 0, %entry ] + %p.addr.04 = phi ptr [ %incdec.ptr, %for.body ], [ %p, %entry ] + %incdec.ptr = getelementptr inbounds nuw i8, ptr %p.addr.04, i16 2 + %0 = load i16, ptr %p.addr.04, align 1 + %add = add nsw i16 %0, %sum.05 + %inc = add nuw nsw i16 %i.06, 1 + %exitcond.not = icmp eq i16 %inc, %cnt + br i1 %exitcond.not, label %for.cond.cleanup, label %for.body } define i8 @load8predec(ptr %x, i8 %y) { diff --git a/llvm/test/CodeGen/AVR/shift.ll b/llvm/test/CodeGen/AVR/shift.ll index 9836f93527b3c..1bd9b45999d7b 100644 --- a/llvm/test/CodeGen/AVR/shift.ll +++ b/llvm/test/CodeGen/AVR/shift.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py -; RUN: llc < %s -mtriple=avr -mtriple=avr -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -mtriple=avr-none -verify-machineinstrs | FileCheck %s ; Optimize for speed. define i8 @shift_i8_i8_speed(i8 %a, i8 %b) { From 38ca2245d0747deb8752f8495187e54cfefc8a8d Mon Sep 17 00:00:00 2001 From: Tom Vijlbrief Date: Mon, 4 Aug 2025 22:39:46 +0200 Subject: [PATCH 3/7] formatting --- llvm/lib/Target/AVR/AVRTargetMachine.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Target/AVR/AVRTargetMachine.cpp b/llvm/lib/Target/AVR/AVRTargetMachine.cpp index 8d05005e287f4..02212d2151f4c 100644 --- a/llvm/lib/Target/AVR/AVRTargetMachine.cpp +++ b/llvm/lib/Target/AVR/AVRTargetMachine.cpp @@ -19,8 +19,8 @@ #include "AVR.h" #include "AVRMachineFunctionInfo.h" -#include "AVRTargetTransformInfo.h" #include "AVRTargetObjectFile.h" +#include "AVRTargetTransformInfo.h" #include "MCTargetDesc/AVRMCTargetDesc.h" #include "TargetInfo/AVRTargetInfo.h" @@ -110,7 +110,8 @@ const AVRSubtarget *AVRTargetMachine::getSubtargetImpl(const Function &) const { return &SubTarget; } -TargetTransformInfo AVRTargetMachine::getTargetTransformInfo(const Function &F) const { +TargetTransformInfo +AVRTargetMachine::getTargetTransformInfo(const Function &F) const { return TargetTransformInfo(std::make_unique(this, F)); } From 3355cccc20d4293cb2b93b569b306309dbde1c04 Mon Sep 17 00:00:00 2001 From: Tom Vijlbrief Date: Tue, 5 Aug 2025 09:56:24 +0200 Subject: [PATCH 4/7] cleanup code and tests --- llvm/lib/Target/AVR/AVRTargetTransformInfo.h | 14 +------------- llvm/test/CodeGen/AVR/load.ll | 6 +++--- llvm/test/CodeGen/AVR/store.ll | 6 +++--- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/llvm/lib/Target/AVR/AVRTargetTransformInfo.h b/llvm/lib/Target/AVR/AVRTargetTransformInfo.h index b877918a18151..77c2a6153f7d5 100644 --- a/llvm/lib/Target/AVR/AVRTargetTransformInfo.h +++ b/llvm/lib/Target/AVR/AVRTargetTransformInfo.h @@ -33,7 +33,6 @@ class AVRTTIImpl final : public BasicTTIImplBase { const AVRSubtarget *ST; const AVRTargetLowering *TLI; - const Function *currentF; const AVRSubtarget *getST() const { return ST; } const AVRTargetLowering *getTLI() const { return TLI; } @@ -41,21 +40,10 @@ class AVRTTIImpl final : public BasicTTIImplBase { public: explicit AVRTTIImpl(const AVRTargetMachine *TM, const Function &F) : BaseT(TM, F.getDataLayout()), ST(TM->getSubtargetImpl(F)), - TLI(ST->getTargetLowering()), currentF(&F) {} + TLI(ST->getTargetLowering()) {} bool isLSRCostLess(const TargetTransformInfo::LSRCost &C1, const TargetTransformInfo::LSRCost &C2) const override { - // Detect %incdec.ptr because loop-reduce loses them - for (const BasicBlock &BB : *currentF) { - if (BB.getName().find("while.body") != std::string::npos) { - for (const Instruction &I : BB) { - std::string str; - llvm::raw_string_ostream(str) << I; - if (str.find("%incdec.ptr") != std::string::npos) - return false; - } - } - } if (C2.Insns == ~0u) return true; return 2 * C1.Insns + C1.AddRecCost + C1.SetupCost + C1.NumRegs < diff --git a/llvm/test/CodeGen/AVR/load.ll b/llvm/test/CodeGen/AVR/load.ll index e2e7844b49c02..7df786c1500d6 100644 --- a/llvm/test/CodeGen/AVR/load.ll +++ b/llvm/test/CodeGen/AVR/load.ll @@ -100,7 +100,7 @@ for.body: ; preds = %entry, %for.body define i8 @load8predec(ptr %x, i8 %y) { ; CHECK-LABEL: load8predec: -; CHECK: ld {{.*}}, -{{[XYZ]}} +; TODO: ld {{.*}}, -{{[XYZ]}} entry: %tobool6 = icmp eq i8 %y, 0 br i1 %tobool6, label %while.end, label %while.body @@ -121,8 +121,8 @@ while.end: ; preds = %while.body, %entry define i16 @load16predec(ptr %x, i16 %y) { ; CHECK-LABEL: load16predec: -; CHECK: ld {{.*}}, -{{[XYZ]}} -; CHECK: ld {{.*}}, -{{[XYZ]}} +; TODO: ld {{.*}}, -{{[XYZ]}} +; TODO: ld {{.*}}, -{{[XYZ]}} entry: %tobool2 = icmp eq i16 %y, 0 br i1 %tobool2, label %while.end, label %while.body diff --git a/llvm/test/CodeGen/AVR/store.ll b/llvm/test/CodeGen/AVR/store.ll index aab02709de7a6..8e7a73d24e4cd 100644 --- a/llvm/test/CodeGen/AVR/store.ll +++ b/llvm/test/CodeGen/AVR/store.ll @@ -94,7 +94,7 @@ while.end: ; preds = %while.body, %entry define void @store8predec(ptr %x, i8 %y) { ; CHECK-LABEL: store8predec: -; CHECK: st -{{[XYZ]}}, {{.*}} +; TODO: st -{{[XYZ]}}, {{.*}} entry: %tobool3 = icmp eq i8 %y, 0 br i1 %tobool3, label %while.end, label %while.body @@ -112,8 +112,8 @@ while.end: ; preds = %while.body, %entry define void @store16predec(ptr %x, i16 %y) { ; CHECK-LABEL: store16predec: -; CHECK: st -{{[XYZ]}}, {{.*}} -; CHECK: st -{{[XYZ]}}, {{.*}} +; TODO: st -{{[XYZ]}}, {{.*}} +; TODO: st -{{[XYZ]}}, {{.*}} entry: %tobool3 = icmp eq i16 %y, 0 br i1 %tobool3, label %while.end, label %while.body From c32207d9d6d947d4c2ef3e1ae6c5ede2a13bbeb9 Mon Sep 17 00:00:00 2001 From: Tom Vijlbrief Date: Tue, 5 Aug 2025 10:34:22 +0200 Subject: [PATCH 5/7] keep existing postinc test --- llvm/test/CodeGen/AVR/load.ll | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/llvm/test/CodeGen/AVR/load.ll b/llvm/test/CodeGen/AVR/load.ll index 7df786c1500d6..e8b799e8a87eb 100644 --- a/llvm/test/CodeGen/AVR/load.ll +++ b/llvm/test/CodeGen/AVR/load.ll @@ -76,10 +76,32 @@ while.end: ; preds = %while.body, %entry ret i8 %r.0.lcssa } -define i16 @load16postinc(ptr %p, i16 %cnt) { +define i16 @load16postinc(ptr %x, i16 %y) { ; CHECK-LABEL: load16postinc: ; CHECK: ld {{.*}}, {{[XYZ]}}+ ; CHECK: ld {{.*}}, {{[XYZ]}}+ +entry: + %tobool2 = icmp eq i16 %y, 0 + br i1 %tobool2, label %while.end, label %while.body +while.body: ; preds = %entry, %while.body + %r.05 = phi i16 [ %add, %while.body ], [ 0, %entry ] + %y.addr.04 = phi i16 [ %dec, %while.body ], [ %y, %entry ] + %x.addr.03 = phi ptr [ %incdec.ptr, %while.body ], [ %x, %entry ] + %dec = add nsw i16 %y.addr.04, -1 + %incdec.ptr = getelementptr inbounds i16, ptr %x.addr.03, i16 1 + %0 = load i16, ptr %x.addr.03 + %add = add nsw i16 %0, %r.05 + %tobool = icmp eq i16 %dec, 0 + br i1 %tobool, label %while.end, label %while.body +while.end: ; preds = %while.body, %entry + %r.0.lcssa = phi i16 [ 0, %entry ], [ %add, %while.body ] + ret i16 %r.0.lcssa +} + +define i16 @load16postincloopreduce(ptr %p, i16 %cnt) { +; CHECK-LABEL: load16postincloopreduce: +; CHECK: ld {{.*}}, {{[XYZ]}}+ +; CHECK: ld {{.*}}, {{[XYZ]}}+ entry: %cmp3 = icmp sgt i16 %cnt, 0 br i1 %cmp3, label %for.body, label %for.cond.cleanup From 6f220a09d584c75f7f014e8c89d88fad37dce783 Mon Sep 17 00:00:00 2001 From: Tom Vijlbrief Date: Tue, 5 Aug 2025 12:25:26 +0200 Subject: [PATCH 6/7] add test for issue --- llvm/test/CodeGen/AVR/issue-151080.ll | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 llvm/test/CodeGen/AVR/issue-151080.ll diff --git a/llvm/test/CodeGen/AVR/issue-151080.ll b/llvm/test/CodeGen/AVR/issue-151080.ll new file mode 100644 index 0000000000000..3f51a16ee9db4 --- /dev/null +++ b/llvm/test/CodeGen/AVR/issue-151080.ll @@ -0,0 +1,48 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=avr-none -verify-machineinstrs | FileCheck %s + +@ci = dso_local global [30 x i16] zeroinitializer, align 1 +declare dso_local void @foo(i16 noundef) addrspace(1) +define void @loopreduce() { +; CHECK-LABEL: loopreduce: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: push r14 +; CHECK-NEXT: push r15 +; CHECK-NEXT: push r16 +; CHECK-NEXT: push r17 +; CHECK-NEXT: ldi r26, lo8(ci) +; CHECK-NEXT: ldi r27, hi8(ci) +; CHECK-NEXT: ldi r16, lo8(ci+60) +; CHECK-NEXT: ldi r17, hi8(ci+60) +; CHECK-NEXT: .LBB0_1: ; %for.body +; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: ld r24, X+ +; CHECK-NEXT: ld r25, X+ +; CHECK-NEXT: mov r14, r26 +; CHECK-NEXT: mov r15, r27 +; CHECK-NEXT: rcall foo +; CHECK-NEXT: mov r26, r14 +; CHECK-NEXT: mov r27, r15 +; CHECK-NEXT: cp r26, r16 +; CHECK-NEXT: cpc r27, r17 +; CHECK-NEXT: brne .LBB0_1 +; CHECK-NEXT: ; %bb.2: ; %for.cond.cleanup +; CHECK-NEXT: pop r17 +; CHECK-NEXT: pop r16 +; CHECK-NEXT: pop r15 +; CHECK-NEXT: pop r14 +; CHECK-NEXT: ret +entry: + br label %for.body +for.body: ; preds = %entry, %for.body + %i.03 = phi i16 [ 0, %entry ], [ %inc, %for.body ] + %arrayidx = getelementptr inbounds nuw [30 x i16], ptr @ci, i16 0, i16 %i.03 + %0 = load i16, ptr %arrayidx, align 1 + tail call addrspace(1) void @foo(i16 noundef %0) #3 + %inc = add nuw nsw i16 %i.03, 1 + %exitcond.not = icmp eq i16 %inc, 30 + br i1 %exitcond.not, label %for.cond.cleanup, label %for.body +; Exit blocks +for.cond.cleanup: ; preds = %for.body + ret void +} From c1f620aa68c5cd3e1426c6c6d48cb296478c2d64 Mon Sep 17 00:00:00 2001 From: Tom Vijlbrief Date: Tue, 5 Aug 2025 14:39:59 +0200 Subject: [PATCH 7/7] add test patterns --- llvm/test/CodeGen/AVR/issue-151080.ll | 59 ++++++++++++++++++++++++++- llvm/test/CodeGen/AVR/load.ll | 6 +++ llvm/test/CodeGen/AVR/store.ll | 5 +++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/llvm/test/CodeGen/AVR/issue-151080.ll b/llvm/test/CodeGen/AVR/issue-151080.ll index 3f51a16ee9db4..f0afc47a24458 100644 --- a/llvm/test/CodeGen/AVR/issue-151080.ll +++ b/llvm/test/CodeGen/AVR/issue-151080.ll @@ -1,8 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc < %s -mtriple=avr-none -verify-machineinstrs | FileCheck %s -@ci = dso_local global [30 x i16] zeroinitializer, align 1 declare dso_local void @foo(i16 noundef) addrspace(1) +@ci = dso_local global [30 x i16] zeroinitializer, align 1 define void @loopreduce() { ; CHECK-LABEL: loopreduce: ; CHECK: ; %bb.0: ; %entry @@ -46,3 +46,60 @@ for.body: ; preds = %entry, %for.body for.cond.cleanup: ; preds = %for.body ret void } + + +define void @induction() { +; CHECK-LABEL: induction: +; CHECK: ; %bb.0: ; %entry +; CHECK-NEXT: push r12 +; CHECK-NEXT: push r13 +; CHECK-NEXT: push r14 +; CHECK-NEXT: push r15 +; CHECK-NEXT: push r17 +; CHECK-NEXT: ldi r24, 8 +; CHECK-NEXT: ldi r25, 0 +; CHECK-NEXT: mov r14, r24 +; CHECK-NEXT: mov r15, r25 +; CHECK-NEXT: ldi r24, 1 +; CHECK-NEXT: ldi r25, 0 +; CHECK-NEXT: mov r12, r24 +; CHECK-NEXT: mov r13, r25 +; CHECK-NEXT: ldi r17, 3 +; CHECK-NEXT: .LBB1_1: ; %for.body +; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: mov r24, r12 +; CHECK-NEXT: mov r25, r13 +; CHECK-NEXT: rcall foo +; CHECK-NEXT: mov r22, r14 +; CHECK-NEXT: mov r23, r15 +; CHECK-NEXT: mov r24, r22 +; CHECK-NEXT: mov r25, r23 +; CHECK-NEXT: rcall __mulhi3 +; CHECK-NEXT: mov r30, r14 +; CHECK-NEXT: mov r31, r15 +; CHECK-NEXT: adiw r30, 1 +; CHECK-NEXT: mov r14, r30 +; CHECK-NEXT: mov r15, r31 +; CHECK-NEXT: cpi r24, -24 +; CHECK-NEXT: cpc r25, r17 +; CHECK-NEXT: brlo .LBB1_1 +; CHECK-NEXT: ; %bb.2: ; %for.cond.cleanup +; CHECK-NEXT: pop r17 +; CHECK-NEXT: pop r15 +; CHECK-NEXT: pop r14 +; CHECK-NEXT: pop r13 +; CHECK-NEXT: pop r12 +; CHECK-NEXT: ret +entry: + br label %for.body +for.body: ; preds = %entry, %for.body + %i.03 = phi i16 [ 7, %entry ], [ %inc, %for.body ] + tail call addrspace(1) void @foo(i16 noundef 1) #2 + %inc = add nuw nsw i16 %i.03, 1 + %mul = mul nuw nsw i16 %inc, %inc + %cmp = icmp samesign ult i16 %mul, 1000 + br i1 %cmp, label %for.body, label %for.cond.cleanup +for.cond.cleanup: ; preds = %for.body + ret void +} + diff --git a/llvm/test/CodeGen/AVR/load.ll b/llvm/test/CodeGen/AVR/load.ll index e8b799e8a87eb..070a888e69fa9 100644 --- a/llvm/test/CodeGen/AVR/load.ll +++ b/llvm/test/CodeGen/AVR/load.ll @@ -123,6 +123,9 @@ for.body: ; preds = %entry, %for.body define i8 @load8predec(ptr %x, i8 %y) { ; CHECK-LABEL: load8predec: ; TODO: ld {{.*}}, -{{[XYZ]}} +; TODO: ld {{.*}}, -{{[XYZ]}} +; CHECK: sbiw {{.*}}, 1 +; CHECK: ld {{.*}}, {{[XYZ]}} entry: %tobool6 = icmp eq i8 %y, 0 br i1 %tobool6, label %while.end, label %while.body @@ -145,6 +148,9 @@ define i16 @load16predec(ptr %x, i16 %y) { ; CHECK-LABEL: load16predec: ; TODO: ld {{.*}}, -{{[XYZ]}} ; TODO: ld {{.*}}, -{{[XYZ]}} +; CHECK: sbiw {{.*}}, 2 +; CHECK: ld {{.*}}, {{[XZ]}} +; CHECK: ldd {{.*}}, {{[XZ]}}+1 entry: %tobool2 = icmp eq i16 %y, 0 br i1 %tobool2, label %while.end, label %while.body diff --git a/llvm/test/CodeGen/AVR/store.ll b/llvm/test/CodeGen/AVR/store.ll index 8e7a73d24e4cd..a1eca5d862805 100644 --- a/llvm/test/CodeGen/AVR/store.ll +++ b/llvm/test/CodeGen/AVR/store.ll @@ -95,6 +95,8 @@ while.end: ; preds = %while.body, %entry define void @store8predec(ptr %x, i8 %y) { ; CHECK-LABEL: store8predec: ; TODO: st -{{[XYZ]}}, {{.*}} +; CHECK: sbiw {{..*}}, 1 +; CHECK: st {{[XYZ]}}, {{.*}} entry: %tobool3 = icmp eq i8 %y, 0 br i1 %tobool3, label %while.end, label %while.body @@ -114,6 +116,9 @@ define void @store16predec(ptr %x, i16 %y) { ; CHECK-LABEL: store16predec: ; TODO: st -{{[XYZ]}}, {{.*}} ; TODO: st -{{[XYZ]}}, {{.*}} +; CHECK: sbiw {{..*}}, 2 +; CHECK: std {{[XYZ]}}+1, {{.*}} +; CHECK: st {{[XYZ]}}, {{.*}} entry: %tobool3 = icmp eq i16 %y, 0 br i1 %tobool3, label %while.end, label %while.body