[clang] [llvm] [MSP430] mov indirect autoincrement codegen (PR #235515)

Robin Mueller via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 25 12:53:10 PDT 2026


https://github.com/robamu updated https://github.com/llvm/llvm-project/pull/235515

>From 57a5269cfa25e50d1afa7e586dc457fb03ccc710 Mon Sep 17 00:00:00 2001
From: Robin Mueller <robin.mueller.m at pm.me>
Date: Thu, 1 Oct 2026 13:52:04 +0200
Subject: [PATCH 1/3] [MSP430][Clang] MSP430X Target CPU selection

- Pass -target-cpu to LLVM, taken from `-mcpu` or derived from `-mmcu`.
- Unknown `-mcpu` values for MSP430 are rejected now
- `-mcpu=msp430x` and `-mcpu=msp430xv2` are both mapped to msp430x. The
  newer revision does not change the generated code, only CPU cycle
  counts
- The information can also be derived from `-mmcu` only using the target
  table, which contains the CPU type.
- Added `MSP430X` and `__MSP430X__` defines. The `__MSP430X__` define matches GCC
- Updated GCC Multilib finder to use the GCC root libraries (MSP430X) for
  the MSP430X CPU target. Otherwise, it will still use the /430
  subdirectory like before.

Based on https://reviews.llvm.org/D109174.

Co-authored-by: Jozef Lawrynowicz <jozefl.opensrc at gmail.com>
Assisted-by: Claude Code (Claude Opus 5.5)
---
 clang/lib/Basic/Targets/MSP430.cpp         |  2 ++
 clang/lib/Basic/Targets/MSP430.h           | 18 +++++++++++++-
 clang/lib/Driver/ToolChains/CommonArgs.cpp |  3 +++
 clang/lib/Driver/ToolChains/Gnu.cpp        | 23 +++++++++---------
 clang/lib/Driver/ToolChains/MSP430.cpp     | 18 ++++++++++++++
 clang/lib/Driver/ToolChains/MSP430.h       |  2 ++
 clang/test/Driver/msp430-cpu.c             | 25 +++++++++++++++++++
 clang/test/Driver/msp430-toolchain.c       | 28 ++++++++++++++++++++++
 clang/test/Preprocessor/msp430-defs.c      |  9 +++++++
 9 files changed, 115 insertions(+), 13 deletions(-)
 create mode 100644 clang/test/Driver/msp430-cpu.c
 create mode 100644 clang/test/Preprocessor/msp430-defs.c

diff --git a/clang/lib/Basic/Targets/MSP430.cpp b/clang/lib/Basic/Targets/MSP430.cpp
index 844f5d3af70379..989927a8c5ab69 100644
--- a/clang/lib/Basic/Targets/MSP430.cpp
+++ b/clang/lib/Basic/Targets/MSP430.cpp
@@ -29,5 +29,7 @@ void MSP430TargetInfo::getTargetDefines(const LangOptions &Opts,
                                         MacroBuilder &Builder) const {
   Builder.defineMacro("MSP430");
   Builder.defineMacro("__MSP430__");
+  if (CPU == "msp430x")
+    Builder.defineMacro("__MSP430X__");
   // FIXME: defines for different 'flavours' of MCU
 }
diff --git a/clang/lib/Basic/Targets/MSP430.h b/clang/lib/Basic/Targets/MSP430.h
index 96a243ef70fae5..fceb7c6e68e201 100644
--- a/clang/lib/Basic/Targets/MSP430.h
+++ b/clang/lib/Basic/Targets/MSP430.h
@@ -23,6 +23,7 @@ namespace targets {
 
 class LLVM_LIBRARY_VISIBILITY MSP430TargetInfo : public TargetInfo {
   static const char *const GCCRegNames[];
+  std::string CPU;
 
 public:
   MSP430TargetInfo(const llvm::Triple &Triple, const TargetOptions &)
@@ -55,10 +56,25 @@ class LLVM_LIBRARY_VISIBILITY MSP430TargetInfo : public TargetInfo {
     return {};
   }
 
+  bool isValidCPUName(StringRef Name) const override {
+    return Name == "msp430" || Name == "msp430x";
+  }
+
+  void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override {
+    Values.append({"msp430", "msp430x"});
+  }
+
+  bool setCPU(StringRef Name) override {
+    if (!isValidCPUName(Name))
+      return false;
+    CPU = Name;
+    return true;
+  }
+
   bool allowsLargerPreferedTypeAlignment() const override { return false; }
 
   bool hasFeature(StringRef Feature) const override {
-    return Feature == "msp430";
+    return Feature == "msp430" || (Feature == "msp430x" && CPU == "msp430x");
   }
 
   ArrayRef<const char *> getGCCRegNames() const override;
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 39dcec2f823bdd..08ae8329433452 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -810,6 +810,9 @@ std::string tools::getCPUName(const Driver &D, const ArgList &Args,
     return std::string(CPUName);
   }
 
+  case llvm::Triple::msp430:
+    return msp430::getMSP430TargetCPU(Args);
+
   case llvm::Triple::nvptx:
   case llvm::Triple::nvptx64:
     if (const Arg *A = Args.getLastArg(options::OPT_march_EQ))
diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp
index 90fc71c147364d..0a58b251d0e693 100644
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
@@ -1529,20 +1529,19 @@ static bool findMSP430Multilibs(const Driver &D,
                                 StringRef Path, const ArgList &Args,
                                 DetectedMultilibs &Result) {
   FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS());
-  MultilibBuilder WithoutExceptions =
-      MultilibBuilder("/430").flag("-exceptions", /*Disallow=*/true);
-  MultilibBuilder WithExceptions =
-      MultilibBuilder("/430/exceptions").flag("-exceptions");
-
-  // FIXME: when clang starts to support msp430x ISA additional logic
-  // to select between multilib must be implemented
-  // MultilibBuilder MSP430xMultilib = MultilibBuilder("/large");
-
-  Result.Multilibs.push_back(WithoutExceptions.makeMultilib());
-  Result.Multilibs.push_back(WithExceptions.makeMultilib());
-  Result.Multilibs.FilterOut(NonExistent);
+  // The MSP430X libraries are in the root, the plain MSP430 ones in /430.
+  // FIXME: Add the /large multilibs once LLVM supports the large memory model.
+  Result.Multilibs =
+      MultilibSetBuilder()
+          .Either(MultilibBuilder().flag("-msp430x"),
+                  MultilibBuilder("/430").flag("-msp430x", /*Disallow=*/true))
+          .Maybe(MultilibBuilder("/exceptions").flag("-exceptions"))
+          .makeMultilibSet()
+          .FilterOut(NonExistent);
 
   Multilib::flags_list Flags;
+  std::string CPU = tools::getCPUName(D, Args, TargetTriple);
+  addMultilibFlag(CPU == "msp430x", "-msp430x", Flags);
   addMultilibFlag(Args.hasFlag(options::OPT_fexceptions,
                                options::OPT_fno_exceptions, false),
                   "-exceptions", Flags);
diff --git a/clang/lib/Driver/ToolChains/MSP430.cpp b/clang/lib/Driver/ToolChains/MSP430.cpp
index 4bd12d1e130e53..5259e0721bdb26 100644
--- a/clang/lib/Driver/ToolChains/MSP430.cpp
+++ b/clang/lib/Driver/ToolChains/MSP430.cpp
@@ -13,6 +13,7 @@
 #include "clang/Driver/InputInfo.h"
 #include "clang/Driver/Multilib.h"
 #include "clang/Options/Options.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Support/Path.h"
 
@@ -52,6 +53,23 @@ static StringRef getHWMultLib(const ArgList &Args) {
       .Default("-lmul_none");
 }
 
+static bool isMSP430X(StringRef CPU) {
+  return CPU == "msp430x" || CPU == "msp430xv2";
+}
+
+std::string msp430::getMSP430TargetCPU(const ArgList &Args) {
+  if (const Arg *A = Args.getLastArg(options::OPT_mcpu_EQ))
+    return isMSP430X(A->getValue()) ? "msp430x" : A->getValue();
+  if (const Arg *A = Args.getLastArg(options::OPT_mmcu_EQ)) {
+    StringRef CPU = llvm::StringSwitch<StringRef>(A->getValue())
+#define MSP430_MCU(NAME, CPU, HWMULT) .Case(NAME, CPU)
+#include "clang/Basic/MSP430Target.def"
+                        .Default("");
+    return isMSP430X(CPU) ? "msp430x" : CPU.str();
+  }
+  return "";
+}
+
 void msp430::getMSP430TargetFeatures(const Driver &D, const ArgList &Args,
                                      std::vector<StringRef> &Features) {
   const Arg *MCU = Args.getLastArg(options::OPT_mmcu_EQ);
diff --git a/clang/lib/Driver/ToolChains/MSP430.h b/clang/lib/Driver/ToolChains/MSP430.h
index 865bcfb7cd3da6..3539c62430f675 100644
--- a/clang/lib/Driver/ToolChains/MSP430.h
+++ b/clang/lib/Driver/ToolChains/MSP430.h
@@ -80,6 +80,8 @@ class LLVM_LIBRARY_VISIBILITY Linker final : public Tool {
 
 void getMSP430TargetFeatures(const Driver &D, const llvm::opt::ArgList &Args,
                              std::vector<llvm::StringRef> &Features);
+std::string getMSP430TargetCPU(const llvm::opt::ArgList &Args);
+
 } // end namespace msp430
 } // end namespace tools
 } // end namespace driver
diff --git a/clang/test/Driver/msp430-cpu.c b/clang/test/Driver/msp430-cpu.c
new file mode 100644
index 00000000000000..d09272436f5700
--- /dev/null
+++ b/clang/test/Driver/msp430-cpu.c
@@ -0,0 +1,25 @@
+// RUN: %clang -### -c %s --target=msp430 2>&1 \
+// RUN:   | FileCheck -check-prefix=NO-CPU %s
+// NO-CPU-NOT: "-target-cpu"
+
+// RUN: %clang -### -c %s --target=msp430 -mcpu=msp430 2>&1 \
+// RUN:   | FileCheck -check-prefix=MSP430 %s
+// RUN: %clang -### -c %s --target=msp430 -mmcu=msp430c111 2>&1 \
+// RUN:   | FileCheck -check-prefix=MSP430 %s
+// MSP430: "-cc1"{{.*}} "-target-cpu" "msp430"
+
+// msp430xv2 is accepted for GCC compatibility and treated as msp430x.
+// RUN: %clang -### -c %s --target=msp430 -mcpu=msp430x 2>&1 \
+// RUN:   | FileCheck -check-prefix=MSP430X %s
+// RUN: %clang -### -c %s --target=msp430 -mcpu=msp430xv2 2>&1 \
+// RUN:   | FileCheck -check-prefix=MSP430X %s
+// RUN: %clang -### -c %s --target=msp430 -mmcu=msp430f2617 2>&1 \
+// RUN:   | FileCheck -check-prefix=MSP430X %s
+// RUN: %clang -### -c %s --target=msp430 -mmcu=msp430f5529 2>&1 \
+// RUN:   | FileCheck -check-prefix=MSP430X %s
+// MSP430X: "-cc1"{{.*}} "-target-cpu" "msp430x"
+
+// RUN: not %clang -c %s --target=msp430 -mcpu=not-a-cpu -o /dev/null 2>&1 \
+// RUN:   | FileCheck -check-prefix=INVALID %s
+// INVALID: error: unknown target CPU 'not-a-cpu'
+// INVALID-NEXT: note: valid target CPU values are: msp430, msp430x
diff --git a/clang/test/Driver/msp430-toolchain.c b/clang/test/Driver/msp430-toolchain.c
index 45010deb40a54d..45de069b0aafe9 100644
--- a/clang/test/Driver/msp430-toolchain.c
+++ b/clang/test/Driver/msp430-toolchain.c
@@ -126,6 +126,34 @@
 // LIBS-EXC-NEG-NOT: "{{.*}}/430"
 // LIBS-EXC-NEG-NOT: "{{.*}}430/crt{{.*}}"
 
+// The MSP430X multilibs are in the root, not in /430.
+// RUN: %clang -### %s --target=msp430 -mcpu=msp430x -rtlib=libgcc \
+// RUN:   --sysroot="%S/Inputs/basic_msp430_tree" > %t 2>&1
+// RUN: FileCheck -check-prefix=LIBS-430X-POS %s < %t
+// RUN: FileCheck -check-prefix=LIBS-430X-NEG %s < %t
+// RUN: %clang -### %s --target=msp430 -mmcu=msp430f5529 -rtlib=libgcc \
+// RUN:   --sysroot="%S/Inputs/basic_msp430_tree" > %t 2>&1
+// RUN: FileCheck -check-prefix=LIBS-430X-POS %s < %t
+// RUN: FileCheck -check-prefix=LIBS-430X-NEG %s < %t
+// LIBS-430X-POS: "{{.*}}/Inputs/basic_msp430_tree{{/|\\\\}}msp430-elf{{/|\\\\}}lib{{/|\\\\}}crt0.o"
+// LIBS-430X-POS: "{{.*}}/Inputs/basic_msp430_tree/lib/gcc/msp430-elf/8.3.1{{/|\\\\}}crtbegin_no_eh.o"
+// LIBS-430X-POS: "-L{{.*}}/Inputs/basic_msp430_tree/lib/gcc/msp430-elf/8.3.1{{/|\\\\}}"
+// LIBS-430X-POS: "-L{{.*}}/Inputs/basic_msp430_tree{{/|\\\\}}msp430-elf{{/|\\\\}}lib{{/|\\\\}}"
+// LIBS-430X-POS: "{{.*}}/Inputs/basic_msp430_tree/lib/gcc/msp430-elf/8.3.1{{/|\\\\}}crtend_no_eh.o"
+// LIBS-430X-NEG-NOT: /430
+// LIBS-430X-NEG-NOT: /exceptions
+
+// RUN: %clang -### %s --target=msp430 -mcpu=msp430x -rtlib=libgcc -fexceptions \
+// RUN:   --sysroot="%S/Inputs/basic_msp430_tree" > %t 2>&1
+// RUN: FileCheck -check-prefix=LIBS-430X-EXC-POS %s < %t
+// RUN: FileCheck -check-prefix=LIBS-430X-EXC-NEG %s < %t
+// LIBS-430X-EXC-POS: "{{.*}}/Inputs/basic_msp430_tree{{/|\\\\}}msp430-elf{{/|\\\\}}lib/exceptions{{/|\\\\}}crt0.o"
+// LIBS-430X-EXC-POS: "{{.*}}/Inputs/basic_msp430_tree/lib/gcc/msp430-elf/8.3.1/exceptions{{/|\\\\}}crtbegin.o"
+// LIBS-430X-EXC-POS: "-L{{.*}}/Inputs/basic_msp430_tree/lib/gcc/msp430-elf/8.3.1/exceptions"
+// LIBS-430X-EXC-POS: "-L{{.*}}/Inputs/basic_msp430_tree{{/|\\\\}}msp430-elf{{/|\\\\}}lib/exceptions"
+// LIBS-430X-EXC-POS: "{{.*}}/Inputs/basic_msp430_tree/lib/gcc/msp430-elf/8.3.1/exceptions{{/|\\\\}}crtend.o"
+// LIBS-430X-EXC-NEG-NOT: /430
+
 // RUN: %clang -### %s --target=msp430 -rtlib=libgcc \
 // RUN:   -fstack-protector  --sysroot="%S/Inputs/basic_msp430_tree" 2>&1 \
 // RUN:   | FileCheck -check-prefix=LIBS-SSP %s
diff --git a/clang/test/Preprocessor/msp430-defs.c b/clang/test/Preprocessor/msp430-defs.c
new file mode 100644
index 00000000000000..6eea5c6e7521cc
--- /dev/null
+++ b/clang/test/Preprocessor/msp430-defs.c
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -triple msp430 -E -dM %s -o - \
+// RUN:   | FileCheck -check-prefix=MSP430 %s
+// RUN: %clang_cc1 -triple msp430 -target-cpu msp430 -E -dM %s -o - \
+// RUN:   | FileCheck -check-prefix=MSP430 %s
+// MSP430-NOT: #define __MSP430X__
+
+// RUN: %clang_cc1 -triple msp430 -target-cpu msp430x -E -dM %s -o - \
+// RUN:   | FileCheck -check-prefix=MSP430X %s
+// MSP430X: #define __MSP430X__ 1

>From 3786b523e3f81950d59c671a281cd635712b186b Mon Sep 17 00:00:00 2001
From: Robin Mueller <robin.mueller.m at pm.me>
Date: Fri, 16 Oct 2026 11:30:26 +0200
Subject: [PATCH 2/3] [MSP430] Add support for MOV8mp and MOV16mp

Copying from memory to memory with automatic source address
post-increment, e.g. `mov @r8+, 0(r15)`

Assisted-by: Claude Code (Claude Opus 5.5)
---
 llvm/lib/Target/MSP430/MSP430InstrInfo.td   | 10 ++++++++++
 llvm/test/MC/Disassembler/MSP430/msp430.txt | 10 ++++++++++
 llvm/test/MC/MSP430/addrmode.s              | 18 ++++++++++++++++++
 3 files changed, 38 insertions(+)

diff --git a/llvm/lib/Target/MSP430/MSP430InstrInfo.td b/llvm/lib/Target/MSP430/MSP430InstrInfo.td
index f25c96e3109a9d..104bcd4fd31f5c 100644
--- a/llvm/lib/Target/MSP430/MSP430InstrInfo.td
+++ b/llvm/lib/Target/MSP430/MSP430InstrInfo.td
@@ -441,6 +441,16 @@ def MOV8mn  : I8mn<0b0100, (outs), (ins memdst:$dst, indreg:$rs),
 def MOV16mn : I16mn<0b0100, (outs), (ins memdst:$dst, indreg:$rs),
                     "mov\t{$rs, $dst}", []>;
 
+let mayLoad = 1, mayStore = 1, hasExtraDefRegAllocReq = 1,
+    Constraints = "$rs = $wb" in {
+def MOV8mp  : I8mp<0b0100, 
+                   (outs GR16:$wb), (ins memdst: $dst, postreg:$rs),
+                   "mov.b\t{$rs, $dst}", []>;
+def MOV16mp : I16mp<0b0100,
+                   (outs GR16:$wb), (ins memdst: $dst, postreg:$rs),
+                    "mov\t{$rs, $dst}", []>;
+}
+
 //===----------------------------------------------------------------------===//
 // Arithmetic Instructions
 
diff --git a/llvm/test/MC/Disassembler/MSP430/msp430.txt b/llvm/test/MC/Disassembler/MSP430/msp430.txt
index 8e06f9de6afd03..8da8f48d2afe04 100644
--- a/llvm/test/MC/Disassembler/MSP430/msp430.txt
+++ b/llvm/test/MC/Disassembler/MSP430/msp430.txt
@@ -26,3 +26,13 @@
 0xbf 0x40 0x2a 0x00 0x0c 0x00 # CHECK: mov #42, 12(r15)
 0xb7 0xb0 0x22 0x00 0x02 0x00 # CHECK: bit #34, 2(r7)
 0x9a 0xb9 0x10 0x00 0x08 0x00 # CHECK: bit 16(r9), 8(r10)
+
+0xbf 0x48 0x00 0x00           # CHECK: mov @r8+, 0(r15)
+0xbf 0x48 0x04 0x00           # CHECK: mov @r8+, 4(r15)
+0xbf 0x48 0x00 0x01           # CHECK: mov @r8+, 256(r15)
+0xb2 0x48 0x00 0x02           # CHECK: mov @r8+, &512
+0xb0 0x48 0x2a 0x00           # CHECK: mov @r8+, 42
+0x7f 0x48                     # CHECK: mov.b @r8+, r15
+0xff 0x48 0x00 0x00           # CHECK: mov.b @r8+, 0(r15)
+0xf2 0x48 0x00 0x02           # CHECK: mov.b @r8+, &512
+0xf0 0x48 0x2a 0x00           # CHECK: mov.b @r8+, 42
diff --git a/llvm/test/MC/MSP430/addrmode.s b/llvm/test/MC/MSP430/addrmode.s
index c787e683aeab8d..63b593e144b5aa 100644
--- a/llvm/test/MC/MSP430/addrmode.s
+++ b/llvm/test/MC/MSP430/addrmode.s
@@ -7,7 +7,16 @@ foo:
   mov &disp+2, r15
   mov @r8, r15
   mov @r8+, r15
+  mov @r8+, 0(r15)
+  mov @r8+, 4(r15)
+  mov @r8+, 256(r15)
+  mov @r8+, &0x200
+  mov @r8+, disp
   mov #disp+2, r15
+  mov.b @r8+, r15
+  mov.b @r8+, 0(r15)
+  mov.b @r8+, &0x200
+  mov.b @r8+, disp
 
 ; CHECK: mov r8, r15           ; encoding: [0x0f,0x48]
 ; CHECK: mov disp+2(r8), r15   ; encoding: [0x1f,0x48,A,A]
@@ -15,7 +24,16 @@ foo:
 ; CHECK: mov &disp+2, r15      ; encoding: [0x1f,0x42,A,A]
 ; CHECK: mov @r8, r15          ; encoding: [0x2f,0x48]
 ; CHECK: mov @r8+, r15         ; encoding: [0x3f,0x48]
+; CHECK: mov @r8+, 0(r15)      ; encoding: [0xbf,0x48,0x00,0x00]
+; CHECK: mov @r8+, 4(r15)      ; encoding: [0xbf,0x48,0x04,0x00]
+; CHECK: mov @r8+, 256(r15)    ; encoding: [0xbf,0x48,0x00,0x01]
+; CHECK: mov @r8+, &512        ; encoding: [0xb2,0x48,0x00,0x02]
+; CHECK: mov @r8+, disp        ; encoding: [0xb0,0x48,A,A]
 ; CHECK: mov #disp+2, r15      ; encoding: [0x3f,0x40,A,A]
+; CHECK: mov.b @r8+, r15       ; encoding: [0x7f,0x48]
+; CHECK: mov.b @r8+, 0(r15)    ; encoding: [0xff,0x48,0x00,0x00]
+; CHECK: mov.b @r8+, &512      ; encoding: [0xf2,0x48,0x00,0x02]
+; CHECK: mov.b @r8+, disp      ; encoding: [0xf0,0x48,A,A]
 
   mov #42, r15
   mov #42, 12(r15)

>From e13e0019fca0213c238d2c846909cf8c272b9a2b Mon Sep 17 00:00:00 2001
From: Robin Mueller <robin.mueller.m at pm.me>
Date: Sun, 25 Oct 2026 20:20:27 +0100
Subject: [PATCH 3/3] [MSP430] Support MOV8mp and MOV16mp in code generation

This adds support for a memory-to-memory copy with source address
auto-increment. This works by folding an post-increment load which is only
stored to another memory address into one instruction:

(store (load<post-inc> $src), $dst)  ->  (MOV16mp $dst, $src)

Assisted-by: Claude Code (Claude Opus 5.5)
---
 llvm/lib/Target/MSP430/MSP430ISelDAGToDAG.cpp | 89 +++++++++++++++++++
 llvm/test/CodeGen/MSP430/postinc.ll           | 73 +++++++++++++++
 2 files changed, 162 insertions(+)

diff --git a/llvm/lib/Target/MSP430/MSP430ISelDAGToDAG.cpp b/llvm/lib/Target/MSP430/MSP430ISelDAGToDAG.cpp
index 35e6150d26e051..deb64532492330 100644
--- a/llvm/lib/Target/MSP430/MSP430ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/MSP430/MSP430ISelDAGToDAG.cpp
@@ -108,6 +108,7 @@ namespace {
     void Select(SDNode *N) override;
 
     bool tryIndexedLoad(SDNode *Op);
+    bool tryIndexedStore(SDNode *Op);
     bool tryIndexedBinOp(SDNode *Op, SDValue N1, SDValue N2, unsigned Opc8,
                          unsigned Opc16);
 
@@ -331,6 +332,11 @@ static bool isValidIndexedLoad(const LoadSDNode *LD) {
   return true;
 }
 
+/// Select a post-increment load as MOV8rp/MOV16rp:
+///
+///   (load<post-inc> $src)  ->  (MOV16rp $src)
+///
+/// This turns `mov @r12, r13; incd r12` into `mov @r12+, r13`.
 bool MSP430DAGToDAGISel::tryIndexedLoad(SDNode *N) {
   LoadSDNode *LD = cast<LoadSDNode>(N);
   if (!isValidIndexedLoad(LD))
@@ -356,6 +362,84 @@ bool MSP430DAGToDAGISel::tryIndexedLoad(SDNode *N) {
   return true;
 }
 
+/// Fold a store of a post-increment load into MOV8mp/MOV16mp:
+///
+///   (store (load<post-inc> $src), $dst)  ->  (MOV16mp $dst, $src)
+///
+/// This turns `mov @r12+, r14; mov r14, 0(r13)` into `mov @r12+, 0(r13)`.
+bool MSP430DAGToDAGISel::tryIndexedStore(SDNode *N) {
+  StoreSDNode *ST = cast<StoreSDNode>(N);
+  SDValue Val = ST->getValue();
+  // Result 0 is the loaded value. Result 1 would be the incremented pointer.
+  if (Val.getOpcode() != ISD::LOAD || Val.getResNo() != 0 || !Val.hasOneUse())
+    return false;
+
+  LoadSDNode *LD = cast<LoadSDNode>(Val);
+  if (!isValidIndexedLoad(LD) || ST->getMemoryVT() != LD->getMemoryVT())
+    return false;
+
+  // Only fold if no other memory operation sits between the load and store.
+  if (ST->getChain() != SDValue(LD, 2) || !IsLegalToFold(Val, N, N, OptLevel))
+    return false;
+
+  unsigned Opcode = 0;
+  switch (ST->getMemoryVT().getSimpleVT().SimpleTy) {
+  case MVT::i8:
+    Opcode = MSP430::MOV8mp;
+    break;
+  case MVT::i16:
+    Opcode = MSP430::MOV16mp;
+    break;
+  default:
+    return false;
+  }
+
+  SDValue Base, Disp;
+  if (!SelectAddr(ST->getBasePtr(), Base, Disp))
+    return false;
+  // From the MSP430X family user guide:
+  //
+  //  > If the same register is used for source and destination, it contains
+  //  > the incremented address for the destination access.
+  //
+  // The increment happens before the store, so if the load and store address
+  // are the same, the store would actually write to the incremented address,
+  // which would be wrong. Skip the fold in this case.
+  if (Base == LD->getBasePtr())
+    return false;
+
+  // Machine node operands follow the ins list of the instruction, with complex
+  // operands split into their parts. The chain comes last. The results follow
+  // the outs list, again followed by the chain. From MSP430InstrInfo.td:
+  //
+  //   def MOV8mp : I8mp<0b0100,
+  //                     (outs GR16:$wb), (ins memdst:$dst, postreg:$rs), ...
+  //
+  // memdst is (ops GR16, i16imm), so $dst becomes base and displacement.
+  SDValue Ops[] = {Base, Disp,       // $dst
+                   LD->getBasePtr(), // $rs
+                   LD->getChain()};
+  MachineSDNode *Mov =
+      CurDAG->getMachineNode(Opcode, SDLoc(N), MVT::i16, MVT::Other, Ops);
+  CurDAG->setNodeMemRefs(Mov, {LD->getMemOperand(), ST->getMemOperand()});
+
+  // The load results are (value, writeback, chain) and the store result is
+  // (chain). Mov replaces both with the results of the operation: (writeback,
+  // chain). The loaded value needs no replacement because its only user was the
+  // store.
+  ReplaceUses(SDValue(LD, 1), SDValue(Mov, 0));
+  ReplaceUses(SDValue(LD, 2), SDValue(Mov, 1));
+  ReplaceUses(SDValue(N, 0), SDValue(Mov, 1));
+  CurDAG->RemoveDeadNode(N);
+  return true;
+}
+
+/// Fold a post-increment load N1 into the binary operation Op, with N2 as the
+/// other operand. For Opc16 = ADD16rp:
+///
+///   (add N2, (load<post-inc> $src))  ->  (ADD16rp N2, $src)
+///
+/// This turns `mov @r12+, r14; add r14, r13` into `add @r12+, r13`.
 bool MSP430DAGToDAGISel::tryIndexedBinOp(SDNode *Op, SDValue N1, SDValue N2,
                                          unsigned Opc8, unsigned Opc16) {
   if (N1.getOpcode() == ISD::LOAD &&
@@ -415,6 +499,11 @@ void MSP430DAGToDAGISel::Select(SDNode *Node) {
       return;
     // Other cases are autogenerated.
     break;
+  case ISD::STORE:
+    if (tryIndexedStore(Node))
+      return;
+    // Other cases are autogenerated.
+    break;
   case ISD::ADD:
     if (tryIndexedBinOp(Node, Node->getOperand(0), Node->getOperand(1),
                         MSP430::ADD8rp, MSP430::ADD16rp))
diff --git a/llvm/test/CodeGen/MSP430/postinc.ll b/llvm/test/CodeGen/MSP430/postinc.ll
index 3f1320d7134336..09743f787315e6 100644
--- a/llvm/test/CodeGen/MSP430/postinc.ll
+++ b/llvm/test/CodeGen/MSP430/postinc.ll
@@ -2,6 +2,8 @@
 target datalayout = "e-p:16:8:8-i8:8:8-i16:8:8-i32:8:8"
 target triple = "msp430"
 
+ at g = global i8 0
+
 define zeroext i16 @add(ptr nocapture %a, i16 zeroext %n) nounwind readonly {
 entry:
   %cmp8 = icmp eq i16 %n, 0                       ; <i1> [#uses=1]
@@ -112,3 +114,74 @@ for.end:                                          ; preds = %for.body, %entry
   ret i16 %sum.0.lcssa
 }
 
+define ptr @copy8_postinc(ptr %Src, ptr %Dest) nounwind {
+; CHECK-LABEL: copy8_postinc:
+; CHECK: mov.b @r{{[0-9]+}}+, 0(r{{[0-9]+}})
+  %incdec.ptr = getelementptr inbounds nuw i8, ptr %Src, i16 1
+  %val = load i8, ptr %Src, align 1
+  store i8 %val, ptr %Dest, align 1
+  ret ptr %incdec.ptr
+}
+
+define ptr @copy16_postinc(ptr %Src, ptr %Dest) nounwind {
+; CHECK-LABEL: copy16_postinc:
+; CHECK: mov @r{{[0-9]+}}+, 0(r{{[0-9]+}})
+  %incdec.ptr = getelementptr inbounds nuw i8, ptr %Src, i16 2
+  %val = load i16, ptr %Src, align 2
+  store i16 %val, ptr %Dest, align 2
+  ret ptr %incdec.ptr
+}
+
+; When the loaded value is used for something else, the value still has to be
+; loaded into a register.
+define ptr @copy8_value_used(ptr %Src, ptr %Dest, ptr %Out) nounwind {
+; CHECK-LABEL: copy8_value_used:
+; CHECK: mov.b @r{{[0-9]+}}+, r{{[0-9]+}}
+  %incdec.ptr = getelementptr inbounds nuw i8, ptr %Src, i16 1
+  %val = load i8, ptr %Src, align 1
+  store i8 %val, ptr %Dest, align 1
+  store i8 %val, ptr %Out, align 1
+  ret ptr %incdec.ptr
+}
+
+define ptr @copy8_store_between(ptr %Src, ptr %Dest, ptr %Other) nounwind {
+; CHECK-LABEL: copy8_store_between:
+; CHECK: mov.b @r{{[0-9]+}}+, r{{[0-9]+}}
+  %incdec.ptr = getelementptr inbounds nuw i8, ptr %Src, i16 1
+  %val = load i8, ptr %Src, align 1
+  ; Volatile store between the load of the source pointer and storing the value
+  ; to the target address. Folding the addition is not allowed.
+  store volatile i8 0, ptr %Other, align 1
+  store i8 %val, ptr %Dest, align 1
+  ret ptr %incdec.ptr
+}
+
+define ptr @copy8_to_global_postinc(ptr %Src, ptr %Dest) nounwind {
+; CHECK-LABEL: copy8_to_global_postinc:
+; CHECK: mov.b @r{{[0-9]+}}+, &g
+  %incdec.ptr = getelementptr inbounds nuw i8, ptr %Src, i16 1
+  %val = load i8, ptr %Src, align 1
+  store i8 %val, ptr @g, align 1
+  ret ptr %incdec.ptr
+}
+
+define ptr @copy8_displacement(ptr %Src, ptr %Dest) nounwind {
+; CHECK-LABEL: copy8_displacement:
+; CHECK: mov.b @r{{[0-9]+}}+, 4(r{{[0-9]+}})
+  %incdec.ptr = getelementptr inbounds nuw i8, ptr %Src, i16 1
+  %dst = getelementptr inbounds i8, ptr %Dest, i16 4
+  %val = load i8, ptr %Src, align 1
+  store i8 %val, ptr %dst, align 1
+  ret ptr %incdec.ptr
+}
+
+; The destination uses the source register. MSP430 computes the destination
+; address after the source increment, so folding would store to Src + 1.
+define ptr @copy8_same_register(ptr %Src) nounwind {
+; CHECK-LABEL: copy8_same_register:
+; CHECK-NOT: mov.b @r{{[0-9]+}}+, 0(r{{[0-9]+}})
+  %incdec.ptr = getelementptr inbounds nuw i8, ptr %Src, i16 1
+  %val = load volatile i8, ptr %Src, align 1
+  store volatile i8 %val, ptr %Src, align 1
+  ret ptr %incdec.ptr
+}



More information about the cfe-commits mailing list