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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 afe07ad0da7cbb0ca06ebcfdc98677f3e0b11263 Mon Sep 17 00:00:00 2001 From: Robin Mueller <[email protected]> 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..2010bd4a66afd5 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" +@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 +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
