https://github.com/robamu updated https://github.com/llvm/llvm-project/pull/231910
>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/2] [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 9e20f75a0258394cbc8372ef98ab757dcb59551c Mon Sep 17 00:00:00 2001 From: Robin Mueller <[email protected]> Date: Thu, 1 Oct 2026 15:56:25 +0200 Subject: [PATCH 2/2] [MSP430] Add assembler support for MSP430X shift instructions - Add RRCM, RRAM, RRUM, RLAM, RRCX, RRAX, RRUX and RLAX for -mcpu=msp430x - Encode extended instructions with the extension word in the low bits, matching their order in memory - Disassemble MSP430X address instructions and extension words - Report an error for instructions that need a disabled CPU feature. They were silently dropped before Based on https://reviews.llvm.org/D110723, reworked for current TableGen. Co-authored-by: Jozef Lawrynowicz <[email protected]> Assisted-by: Claude Opus 5.5 <[email protected]> --- .../MSP430/AsmParser/MSP430AsmParser.cpp | 21 +++- .../Disassembler/MSP430Disassembler.cpp | 56 +++++++++- .../MCTargetDesc/MSP430MCCodeEmitter.cpp | 18 ++++ llvm/lib/Target/MSP430/MSP430InstrFormats.td | 95 ++++++++++++++++ llvm/lib/Target/MSP430/MSP430InstrInfo.td | 9 ++ .../Target/MSP430/MSP430InstrInfoExtended.td | 84 +++++++++++++++ llvm/lib/Target/MSP430/MSP430Subtarget.h | 2 + .../Disassembler/MSP430/msp430x-invalid.txt | 13 +++ llvm/test/MC/Disassembler/MSP430/msp430x.txt | 18 ++++ llvm/test/MC/MSP430/msp430x-feature.s | 9 ++ llvm/test/MC/MSP430/shifts-430x-invalid.s | 23 ++++ llvm/test/MC/MSP430/shifts-430x.s | 101 ++++++++++++++++++ 12 files changed, 443 insertions(+), 6 deletions(-) create mode 100644 llvm/lib/Target/MSP430/MSP430InstrInfoExtended.td create mode 100644 llvm/test/MC/Disassembler/MSP430/msp430x-invalid.txt create mode 100644 llvm/test/MC/Disassembler/MSP430/msp430x.txt create mode 100644 llvm/test/MC/MSP430/msp430x-feature.s create mode 100644 llvm/test/MC/MSP430/shifts-430x-invalid.s create mode 100644 llvm/test/MC/MSP430/shifts-430x.s diff --git a/llvm/lib/Target/MSP430/AsmParser/MSP430AsmParser.cpp b/llvm/lib/Target/MSP430/AsmParser/MSP430AsmParser.cpp index 847ca9b6297042..d21e1cb024d907 100644 --- a/llvm/lib/Target/MSP430/AsmParser/MSP430AsmParser.cpp +++ b/llvm/lib/Target/MSP430/AsmParser/MSP430AsmParser.cpp @@ -162,11 +162,8 @@ class MSP430Operand : public MCParsedAsmOperand { bool isPostIndReg() const { return Kind == k_PostIndReg; } bool isCGImm() const { - if (Kind != k_Imm) - return false; - int64_t Val; - if (!Imm->evaluateAsAbsolute(Val)) + if (!isImm() || !Imm->evaluateAsAbsolute(Val)) return false; if (Val == 0 || Val == 1 || Val == 2 || Val == 4 || Val == 8 || Val == -1) @@ -175,6 +172,19 @@ class MSP430Operand : public MCParsedAsmOperand { return false; } + /// Shift count of the MSP430X multi-bit shifts (RRAM, RRUM, RLAM). It is + /// encoded as count - 1 in a 2-bit field, so only 1 to 4 is valid. + bool isRpt2Imm() const { + int64_t Val; + if (!isImm() || !Imm->evaluateAsAbsolute(Val)) + return false; + + if (Val >= 1 && Val <= 4) + return true; + + return false; + } + StringRef getToken() const { assert(Kind == k_Tok && "Invalid access!"); return Tok; @@ -265,6 +275,9 @@ bool MSP430AsmParser::matchAndEmitInstruction(SMLoc Loc, unsigned &Opcode, return false; case Match_MnemonicFail: return Error(Loc, "invalid instruction mnemonic"); + case Match_MissingFeature: + return Error(Loc, + "instruction requires a CPU feature not currently enabled"); case Match_InvalidOperand: { SMLoc ErrorLoc = Loc; if (ErrorInfo != ~0U) { diff --git a/llvm/lib/Target/MSP430/Disassembler/MSP430Disassembler.cpp b/llvm/lib/Target/MSP430/Disassembler/MSP430Disassembler.cpp index e6666e8cafdf9d..3f473bc1504801 100644 --- a/llvm/lib/Target/MSP430/Disassembler/MSP430Disassembler.cpp +++ b/llvm/lib/Target/MSP430/Disassembler/MSP430Disassembler.cpp @@ -44,6 +44,10 @@ class MSP430Disassembler : public MCDisassembler { ArrayRef<uint8_t> Bytes, uint64_t Address, raw_ostream &CStream) const; + DecodeStatus getInstructionX(MCInst &MI, uint64_t &Size, + ArrayRef<uint8_t> Bytes, uint64_t Address, + raw_ostream &CStream) const; + public: MSP430Disassembler(const MCSubtargetInfo &STI, MCContext &Ctx) : MCDisassembler(STI, Ctx) {} @@ -119,6 +123,15 @@ static DecodeStatus DecodeCGImm(MCInst &MI, uint64_t Bits, uint64_t Address, return MCDisassembler::Success; } +/// Decode the 2-bit shift count of RRCM, RRAM, RRUM and RLAM. It is stored as +/// count - 1. +static DecodeStatus decodeRpt2Imm(MCInst &MI, uint64_t Bits, uint64_t Address, + const MCDisassembler *Decoder) { + int64_t Imm = Bits + 1; + MI.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + static DecodeStatus DecodeMemOperand(MCInst &MI, uint64_t Bits, uint64_t Address, const MCDisassembler *Decoder) { @@ -353,6 +366,37 @@ DecodeStatus MSP430Disassembler::getInstructionCJ(MCInst &MI, uint64_t &Size, return DecodeStatus::Success; } +static bool isExtensionWord(uint64_t Word) { return (Word & 0xf800) == 0x1800; } + +DecodeStatus MSP430Disassembler::getInstructionX(MCInst &MI, uint64_t &Size, + ArrayRef<uint8_t> Bytes, + uint64_t Address, + raw_ostream &CStream) const { + uint64_t Insn = support::endian::read16le(Bytes.data()); + unsigned Words = 1; + if (isExtensionWord(Insn)) { + if (Bytes.size() < 4) { + Size = 2; + return DecodeStatus::Fail; + } + Insn |= (uint64_t)support::endian::read16le(Bytes.data() + 2) << 16; + Words = 2; + } + + // MSP430X32 decodes the extension word together with the following Format I + // or II word. Each combination is its own instruction, like RRCX_16 or + // ADDX16rr, so the plain Format I and II tables are not used here. + const uint8_t *DecoderTable = + Words == 2 ? DecoderTableMSP430X32 : DecoderTableMSP430X16; + DecodeStatus Result = + decodeInstruction(DecoderTable, MI, Insn, Address, this, STI); + // The word after an extension word is a valid Format I or II instruction on + // its own. Skip it on failure too, so it is not shown without the extension + // word. + Size = Words * 2; + return Result; +} + DecodeStatus MSP430Disassembler::getInstruction(MCInst &MI, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t Address, @@ -362,10 +406,18 @@ DecodeStatus MSP430Disassembler::getInstruction(MCInst &MI, uint64_t &Size, return MCDisassembler::Fail; } + // The first word selects the instruction format: + // 0x0000-0x0fff MSP430X address instructions + // 0x1000-0x17ff Format II + // 0x1800-0x1fff MSP430X extension word, followed by a Format I or II word + // 0x2000-0x3fff Jumps + // 0x4000-0xffff Format I uint64_t Insn = support::endian::read16le(Bytes.data()); - unsigned Opc = fieldFromInstruction(Insn, 13, 3); - switch (Opc) { + unsigned FormatBits = fieldFromInstruction(Insn, 13, 3); + switch (FormatBits) { case 0: + if (!(Insn & 0x1000) || isExtensionWord(Insn)) + return getInstructionX(MI, Size, Bytes, Address, CStream); return getInstructionII(MI, Size, Bytes, Address, CStream); case 1: return getInstructionCJ(MI, Size, Bytes, Address, CStream); diff --git a/llvm/lib/Target/MSP430/MCTargetDesc/MSP430MCCodeEmitter.cpp b/llvm/lib/Target/MSP430/MCTargetDesc/MSP430MCCodeEmitter.cpp index f018bdbeb0bfbc..0436434658049e 100644 --- a/llvm/lib/Target/MSP430/MCTargetDesc/MSP430MCCodeEmitter.cpp +++ b/llvm/lib/Target/MSP430/MCTargetDesc/MSP430MCCodeEmitter.cpp @@ -64,6 +64,12 @@ class MSP430MCCodeEmitter : public MCCodeEmitter { SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const; + /// Encode the repetition count for a 430X extended format exception + /// instruction. + unsigned getRpt2ImmOpValue(const MCInst &MI, unsigned Op, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getCCOpValue(const MCInst &MI, unsigned Op, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const; @@ -192,6 +198,18 @@ unsigned MSP430MCCodeEmitter::getCGImmOpValue(const MCInst &MI, unsigned Op, } } +unsigned +MSP430MCCodeEmitter::getRpt2ImmOpValue(const MCInst &MI, unsigned Op, + SmallVectorImpl<MCFixup> &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(Op); + assert(MO.isImm() && "expr operand expected"); + + int64_t Imm = MO.getImm(); + assert((Imm >= 1 && Imm <= 4) && "invalid repetition count"); + return Imm - 1; +} + unsigned MSP430MCCodeEmitter::getCCOpValue(const MCInst &MI, unsigned Op, SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const { diff --git a/llvm/lib/Target/MSP430/MSP430InstrFormats.td b/llvm/lib/Target/MSP430/MSP430InstrFormats.td index 0e784aa4ff9144..f850100cd2eddc 100644 --- a/llvm/lib/Target/MSP430/MSP430InstrFormats.td +++ b/llvm/lib/Target/MSP430/MSP430InstrFormats.td @@ -28,6 +28,13 @@ class DestMode<bit val> { def DstReg : DestMode<0>; // r def DstMem : DestMode<1>; // m +class ALKind<bit val> { + bit Value = val; +} + +def ALAddr : ALKind<0>; +def ALWord : ALKind<1>; + // Generic MSP430 Format class MSP430Inst<dag outs, dag ins, int size, string asmstr> : Instruction { field bits<48> Inst; @@ -432,6 +439,94 @@ class CJForm<dag outs, dag ins, string asmstr, list<dag> pattern> let Inst{9-0} = dst; } +// RRCM, RRAM, RRUM and RLAM. Single-word MSP430X instructions with their own +// layout in the Format II opcode space, without an extension word. TI calls +// them Format II exceptions. +class IIExtExcRotForm<bits<2> opcode, ALKind al, + dag outs, dag ins, string asmstr, list<dag> pattern> + : MSP430Inst<outs, ins, 2, asmstr> { + let Pattern = pattern; + let DecoderNamespace = "MSP430X"; + + bits<4> rd; + bits<2> cnt; + + let Inst{15-12} = 0b0000; + let Inst{11-10} = cnt; + let Inst{9-8} = opcode; + let Inst{7-5} = 0b010; + let Inst{4} = al.Value; + let Inst{3-0} = rd; +} + +class IIExtExcRotForm16<bits<2> opcode, + dag outs, dag ins, string asmstr, list<dag> pattern> + : IIExtExcRotForm<opcode, ALWord, outs, ins, asmstr, pattern>; + +// MSP430X extended instructions in register mode. +// +// The extension word precedes the instruction word in memory, so it occupies +// Inst{15-0} and the instruction word is in Inst{31-16}. +// +// FIXME: Add the non-register mode, which is needed for 20-bit memory +// operands. +class ExtRegForm<bit zc, bit rpt, ALKind al, + dag outs, dag ins, string asmstr, list<dag> pattern> + : MSP430Inst<outs, ins, 4, asmstr> { + let Pattern = pattern; + let DecoderNamespace = "MSP430X"; + + bits<4> cnt = 0; + + let Inst{15-9} = 0b0001100; + let Inst{8} = zc; + let Inst{7} = rpt; + let Inst{6} = al.Value; + let Inst{5-4} = 0b00; + let Inst{3-0} = cnt; +} + +// Same layout as IIForm, moved behind the extension word. +class IIExtRegForm<bits<3> opcode, bit bw, bit zc, bit rpt, + dag outs, dag ins, string asmstr, list<dag> pattern> + : ExtRegForm<zc, rpt, ALWord, outs, ins, asmstr, pattern> { + bits<4> rs; + + let Inst{31-26} = 0b000100; + let Inst{25-23} = opcode; + let Inst{22} = bw; + let Inst{21-20} = SrcReg.Value; + let Inst{19-16} = rs; +} + +// Same layout as IForm, moved behind the extension word. +class IExtRegForm<bits<4> opcode, bit bw, bit zc, bit rpt, + dag outs, dag ins, string asmstr, list<dag> pattern> + : ExtRegForm<zc, rpt, ALWord, outs, ins, asmstr, pattern> { + bits<4> rs; + bits<4> rd; + + let Inst{31-28} = opcode; + let Inst{27-24} = rs; + let Inst{23} = DstReg.Value; + let Inst{22} = bw; + let Inst{21-20} = SrcReg.Value; + let Inst{19-16} = rd; +} + +class II8rExtended<bits<3> opcode, bit zc, bit rpt, + dag outs, dag ins, string asmstr, list<dag> pattern> + : IIExtRegForm<opcode, 1, zc, rpt, outs, ins, asmstr, pattern>; +class II16rExtended<bits<3> opcode, bit zc, bit rpt, + dag outs, dag ins, string asmstr, list<dag> pattern> + : IIExtRegForm<opcode, 0, zc, rpt, outs, ins, asmstr, pattern>; +class I8rExtended<bits<4> opcode, bit zc, bit rpt, + dag outs, dag ins, string asmstr, list<dag> pattern> + : IExtRegForm<opcode, 1, zc, rpt, outs, ins, asmstr, pattern>; +class I16rExtended<bits<4> opcode, bit zc, bit rpt, + dag outs, dag ins, string asmstr, list<dag> pattern> + : IExtRegForm<opcode, 0, zc, rpt, outs, ins, asmstr, pattern>; + // Pseudo instructions class Pseudo<dag outs, dag ins, string asmstr, list<dag> pattern> : MSP430Inst<outs, ins, 0, asmstr> { diff --git a/llvm/lib/Target/MSP430/MSP430InstrInfo.td b/llvm/lib/Target/MSP430/MSP430InstrInfo.td index f25c96e3109a9d..3e870be0c0f588 100644 --- a/llvm/lib/Target/MSP430/MSP430InstrInfo.td +++ b/llvm/lib/Target/MSP430/MSP430InstrInfo.td @@ -12,6 +12,13 @@ include "MSP430InstrFormats.td" +//===----------------------------------------------------------------------===// +// Feature predicates. +//===----------------------------------------------------------------------===// + +def HasMSP430X : Predicate<"Subtarget->hasMSP430X()">, + AssemblerPredicate<(all_of FeatureX), "MSP430X">; + //===----------------------------------------------------------------------===// // Type Constraints. //===----------------------------------------------------------------------===// @@ -990,3 +997,5 @@ def : Pat<(and GR16:$src, 255), (ZEXT16r GR16:$src)>; def : Pat<(MSP430cmp (trunc (and_su GR16:$src, GR16:$src2)), 0), (BIT8rr (EXTRACT_SUBREG GR16:$src, subreg_8bit), (EXTRACT_SUBREG GR16:$src2, subreg_8bit))>; + +include "MSP430InstrInfoExtended.td" diff --git a/llvm/lib/Target/MSP430/MSP430InstrInfoExtended.td b/llvm/lib/Target/MSP430/MSP430InstrInfoExtended.td new file mode 100644 index 00000000000000..d56b42875955d3 --- /dev/null +++ b/llvm/lib/Target/MSP430/MSP430InstrInfoExtended.td @@ -0,0 +1,84 @@ +//===-- MSP430InstrInfoExtended.td - MSP430X Instruction defs -------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file describes the MSP430X extended instructions in TableGen format. +// +//===----------------------------------------------------------------------===// + +// TODO: Most extended instructions have a .A version that enables 20-bit +// operands. This requires support the large memory model to be implemented. +// +// Only the extended instructions that are useful in the small memory model +// (16-bit pointers) are implemented so far. Instructions like MOVX are not +// required in the small memory model, and have increased size/cycle count +// compared to their 430 versions. + +def Rpt2ImmAsmOperand : AsmOperandClass { + let Name = "Rpt2Imm"; + let RenderMethod = "addImmOperands"; +} + +def rpt2imm : Operand<i8>, + ImmLeaf<i8, [{return Imm >= 1 && Imm <= 4;}]> { + let ParserMatchClass = Rpt2ImmAsmOperand; + let EncoderMethod = "getRpt2ImmOpValue"; + let DecoderMethod = "decodeRpt2Imm"; +} + +//===----------------------------------------------------------------------===// +// Shift Instructions +// +let Predicates = [HasMSP430X] in { +let Constraints = "$rs = $rd", Defs = [SR] in { + multiclass IIExtExcRot<string Mnemonic, bits<2> OpCode, + list<Register> uses> { + let Uses = uses in + def _16 : IIExtExcRotForm16<OpCode, + (outs GR16:$rd), (ins GR16:$rs, rpt2imm:$cnt), + !strconcat(Mnemonic, "\t$cnt, $rd"), []>; + } + + defm RRCM : IIExtExcRot<"rrcm", 0b00, [SR]>; + defm RRAM : IIExtExcRot<"rram", 0b01, []>; + defm RRUM : IIExtExcRot<"rrum", 0b11, []>; + defm RLAM : IIExtExcRot<"rlam", 0b10, []>; + + multiclass IIExtRot<string Mnemonic, bits<3> OpCode, bit ZC, + list<Register> uses> { + let Uses = uses in { + def _8 : II8rExtended<OpCode, ZC, 0, + (outs GR8:$rd), (ins GR8:$rs), + !strconcat(Mnemonic, ".b\t$rd"), []>; + def _16 : II16rExtended<OpCode, ZC, 0, + (outs GR16:$rd), (ins GR16:$rs), + !strconcat(Mnemonic, "\t$rd"), []>; + } + } + defm RRCX : IIExtRot<"rrcx", 0b000, 0, [SR]>; + defm RRAX : IIExtRot<"rrax", 0b010, 0, []>; + // RRUX is encoded as RRCX with the ZC flag set. + defm RRUX : IIExtRot<"rrux", 0b000, 1, []>; + +} // Defs = [SR], Constraints = "$rs = $rd" + +//===----------------------------------------------------------------------===// +// Arithmetic Instructions +// +let Defs = [SR], Constraints = "$src2 = $rd" in { + def ADDX8rr : I8rExtended<0b0101, 0, 0, + (outs GR8:$rd), (ins GR8:$src2, GR8:$rs), + "addx.b\t$rs, $rd", []>; + def ADDX16rr : I16rExtended<0b0101, 0, 0, + (outs GR16:$rd), (ins GR16:$src2, GR16:$rs), + "addx\t$rs, $rd", []>; +} + +// Alias printing is disabled for the same reason as for RLA. +def RLAX8r : InstAlias<"rlax.b\t$dst", (ADDX8rr GR8:$dst, GR8:$dst), 0>; +def RLAX16r : InstAlias<"rlax\t$dst", (ADDX16rr GR16:$dst, GR16:$dst), 0>; +} // Predicates = [HasMSP430X] diff --git a/llvm/lib/Target/MSP430/MSP430Subtarget.h b/llvm/lib/Target/MSP430/MSP430Subtarget.h index d88bf11bddb3d7..cbb94ae970b91f 100644 --- a/llvm/lib/Target/MSP430/MSP430Subtarget.h +++ b/llvm/lib/Target/MSP430/MSP430Subtarget.h @@ -66,6 +66,8 @@ class MSP430Subtarget : public MSP430GenSubtargetInfo { bool hasHWMult32() const { return HWMultMode == MSP430::HWMult::HW32; } bool hasHWMultF5() const { return HWMultMode == MSP430::HWMult::F5; } + bool hasMSP430X() const { return ExtendedInsts; } + const TargetFrameLowering *getFrameLowering() const override { return &FrameLowering; } diff --git a/llvm/test/MC/Disassembler/MSP430/msp430x-invalid.txt b/llvm/test/MC/Disassembler/MSP430/msp430x-invalid.txt new file mode 100644 index 00000000000000..0ac8a359e23880 --- /dev/null +++ b/llvm/test/MC/Disassembler/MSP430/msp430x-invalid.txt @@ -0,0 +1,13 @@ +# RUN: llvm-mc -disassemble %s -triple=msp430 -mcpu=msp430x 2>&1 >/dev/null \ +# RUN: | FileCheck %s --check-prefix=WARN +# RUN: llvm-mc -disassemble %s -triple=msp430 -mcpu=msp430x 2>/dev/null \ +# RUN: | FileCheck %s + +# An unsupported extended instruction (rpt #4 + rrax r12) is skipped as a +# whole. Its instruction word must not show up as rra r12. +# CHECK: rram #1, r12 +# CHECK-NEXT: rram #2, r12 +# WARN: :[[@LINE+2]]:1: warning: invalid instruction encoding +0x5c 0x01 +0x43 0x18 0x0c 0x11 +0x5c 0x05 diff --git a/llvm/test/MC/Disassembler/MSP430/msp430x.txt b/llvm/test/MC/Disassembler/MSP430/msp430x.txt new file mode 100644 index 00000000000000..0ee456ef92dd31 --- /dev/null +++ b/llvm/test/MC/Disassembler/MSP430/msp430x.txt @@ -0,0 +1,18 @@ +# RUN: llvm-mc -disassemble %s -triple=msp430 -mcpu=msp430x | FileCheck %s + +0x5c 0x01 # CHECK: rram #1, r12 +0x5c 0x0d # CHECK: rram #4, r12 +0x55 0x07 # CHECK: rrum #2, r5 +0x5f 0x0a # CHECK: rlam #3, r15 +0x5c 0x00 # CHECK: rrcm #1, r12 +0x56 0x0c # CHECK: rrcm #4, r6 +0x40 0x18 0x0c 0x11 # CHECK: rrax r12 +0x40 0x18 0x4c 0x11 # CHECK: rrax.b r12 +0x40 0x18 0x0c 0x10 # CHECK: rrcx r12 +0x40 0x18 0x48 0x10 # CHECK: rrcx.b r8 +0x40 0x19 0x0c 0x10 # CHECK: rrux r12 +0x40 0x19 0x47 0x10 # CHECK: rrux.b r7 +0x40 0x18 0x0c 0x5c # CHECK: addx r12, r12 +0x40 0x18 0x49 0x59 # CHECK: addx.b r9, r9 +0x40 0x18 0x0c 0x55 # CHECK: addx r5, r12 +0x40 0x18 0x47 0x55 # CHECK: addx.b r5, r7 diff --git a/llvm/test/MC/MSP430/msp430x-feature.s b/llvm/test/MC/MSP430/msp430x-feature.s new file mode 100644 index 00000000000000..6d6ece721088e3 --- /dev/null +++ b/llvm/test/MC/MSP430/msp430x-feature.s @@ -0,0 +1,9 @@ +// MSP430X instructions require -mcpu=msp430x. +// RUN: not llvm-mc -triple msp430 %s 2>&1 | FileCheck %s + + rram #1, r12 +// CHECK: :[[@LINE-1]]:3: error: instruction requires a CPU feature not currently enabled + rrax r12 +// CHECK: :[[@LINE-1]]:3: error: instruction requires a CPU feature not currently enabled + rlax r12 +// CHECK: :[[@LINE-1]]:3: error: instruction requires a CPU feature not currently enabled diff --git a/llvm/test/MC/MSP430/shifts-430x-invalid.s b/llvm/test/MC/MSP430/shifts-430x-invalid.s new file mode 100644 index 00000000000000..c333b40e2304d4 --- /dev/null +++ b/llvm/test/MC/MSP430/shifts-430x-invalid.s @@ -0,0 +1,23 @@ +// Tests to check for incorrect use of MSP430X shift instructions. +// RUN: not llvm-mc --triple=msp430 -mcpu=msp430x < %s 2>&1 | FileCheck %s + +foo: +// Check for an error when the repetition count is too large. + rrum #5, r12 + rram #5, r12 + rlam #5, r12 + rrcm #5, r12 +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction + +// Check for an error when RxxX shifts have a shift amount operand. + rrcx #3, r12 + rrux #3, r12 + rrax #3, r12 + rlax #3, r12 +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction +// CHECK: :8: error: invalid operand for instruction diff --git a/llvm/test/MC/MSP430/shifts-430x.s b/llvm/test/MC/MSP430/shifts-430x.s new file mode 100644 index 00000000000000..ccf2d8ec9b6bd6 --- /dev/null +++ b/llvm/test/MC/MSP430/shifts-430x.s @@ -0,0 +1,101 @@ +// Test the encoding of shift instructions for the 430X CPU. + +// RUN: llvm-mc -triple msp430 -mcpu=msp430x -show-encoding < %s | FileCheck %s +rxxm: + rrum #1, r12 ; encoding: [0x5c,0x03] + rrum #2, r12 ; encoding: [0x5c,0x07] + rrum #3, r12 ; encoding: [0x5c,0x0b] + rrum #4, r12 ; encoding: [0x5c,0x0f] + + rram #1, r12 ; encoding: [0x5c,0x01] + rram #2, r12 ; encoding: [0x5c,0x05] + rram #3, r12 ; encoding: [0x5c,0x09] + rram #4, r12 ; encoding: [0x5c,0x0d] + + rlam #1, r12 ; encoding: [0x5c,0x02] + rlam #2, r12 ; encoding: [0x5c,0x06] + rlam #3, r12 ; encoding: [0x5c,0x0a] + rlam #4, r12 ; encoding: [0x5c,0x0e] + + rrcm #1, r12 ; encoding: [0x5c,0x00] + rrcm #2, r12 ; encoding: [0x5c,0x04] + rrcm #3, r12 ; encoding: [0x5c,0x08] + rrcm #4, r12 ; encoding: [0x5c,0x0c] + +// CHECK-LABEL: rxxm: +// CHECK-NEXT: rrum #1, r12 ; encoding: [0x5c,0x03] +// CHECK: rrum #2, r12 ; encoding: [0x5c,0x07] +// CHECK: rrum #3, r12 ; encoding: [0x5c,0x0b] +// CHECK: rrum #4, r12 ; encoding: [0x5c,0x0f] + +// CHECK: rram #1, r12 ; encoding: [0x5c,0x01] +// CHECK: rram #2, r12 ; encoding: [0x5c,0x05] +// CHECK: rram #3, r12 ; encoding: [0x5c,0x09] +// CHECK: rram #4, r12 ; encoding: [0x5c,0x0d] + +// CHECK: rlam #1, r12 ; encoding: [0x5c,0x02] +// CHECK: rlam #2, r12 ; encoding: [0x5c,0x06] +// CHECK: rlam #3, r12 ; encoding: [0x5c,0x0a] +// CHECK: rlam #4, r12 ; encoding: [0x5c,0x0e] + +// CHECK: rrcm #1, r12 ; encoding: [0x5c,0x00] +// CHECK: rrcm #2, r12 ; encoding: [0x5c,0x04] +// CHECK: rrcm #3, r12 ; encoding: [0x5c,0x08] +// CHECK: rrcm #4, r12 ; encoding: [0x5c,0x0c] + +rrcx: + rrcx r12 + +// CHECK-LABEL: rrcx: +// CHECK-NEXT: rrcx r12 ; encoding: [0x40,0x18,0x0c,0x10] + +rrcx_b: + rrcx.b r12 + +// CHECK-LABEL: rrcx_b: +// CHECK-NEXT: rrcx.b r12 ; encoding: [0x40,0x18,0x4c,0x10] + +rrux: + rrux r12 + +// CHECK-LABEL: rrux: +// CHECK-NEXT: rrux r12 ; encoding: [0x40,0x19,0x0c,0x10] + +rrux_b: + rrux.b r12 + +// CHECK-LABEL: rrux_b: +// CHECK-NEXT: rrux.b r12 ; encoding: [0x40,0x19,0x4c,0x10] + +rrax: + rrax r12 + +// CHECK-LABEL: rrax: +// CHECK-NEXT: rrax r12 ; encoding: [0x40,0x18,0x0c,0x11] + +rrax_b: + rrax.b r12 + +// CHECK-LABEL: rrax_b: +// CHECK-NEXT: rrax.b r12 ; encoding: [0x40,0x18,0x4c,0x11] + +// RLAX is an alias of ADDX with both registers equal. Alias printing is +// disabled, so it prints as ADDX. +rlax: + rlax r12 + +// CHECK-LABEL: rlax: +// CHECK-NEXT: addx r12, r12 ; encoding: [0x40,0x18,0x0c,0x5c] + +rlax_b: + rlax.b r12 + +// CHECK-LABEL: rlax_b: +// CHECK-NEXT: addx.b r12, r12 ; encoding: [0x40,0x18,0x4c,0x5c] + +// ADDX with different registers must not be treated as RLAX. +addx: + addx r5, r12 + +// CHECK-LABEL: addx: +// CHECK-NEXT: addx r5, r12 ; encoding: [0x40,0x18,0x0c,0x55] _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
