[llvm] Fix/typo (PR #191405)
Axel Ibarrondo via llvm-commits
llvm-commits at lists.llvm.org
Fri Apr 10 05:05:25 PDT 2026
https://github.com/Axel84727 created https://github.com/llvm/llvm-project/pull/191405
This pull request adds initial assembler and machine code emission support for the new `LX32` target in LLVM. The changes introduce the assembler parser, MC layer, and ELF object writer for `LX32`, along with the necessary build system integration and target registration. This is a foundational step in enabling full assembler and code generation support for the `LX32` architecture.
**Build system and target registration:**
- Added `LX32` to the list of all LLVM targets and to the `Triple` enumeration, making it a recognized target throughout the LLVM codebase. [[1]](diffhunk://#diff-f6c3fac0de0e2bbd79ac93b0a6aabe77d9961c5487347a2170c78ac77770d8afR578) [[2]](diffhunk://#diff-5008de5706b85963c456532be4d2e8c5b478959be9d315f77c338946bd1fc586R79)
- Created and registered new CMake build targets and component groups for the `LX32` backend, including subdirectories for `AsmParser`, `MCTargetDesc`, and core target components. [[1]](diffhunk://#diff-b26a69d1ffd25d4e883bba355068f5eb56ba3122dd633fbbd8c4aaeea0ff4ef9R1-R5) [[2]](diffhunk://#diff-e9e613cb2e7c9b5e8b3691760780d54e2bee502da9469415366cb5aac7b10340R1-R13) [[3]](diffhunk://#diff-fdf8aa644e32bc790392b263274fd0da1c9c244c603c1153dcc2d482a55bc80eR1-R16)
**Assembler and MC layer implementation:**
- Implemented the `LX32AsmParser` class to parse LX32 assembly into MCInst instructions, including operand parsing and instruction matching logic.
- Added the MC layer for `LX32`, including:
- `LX32MCAsmInfo` and its header for assembler information and defaults (e.g., endianness, pointer size, comment style). [[1]](diffhunk://#diff-2c05f47f8bc02b64fab1b19b6a9e1913d10b37dd02e38d4aebd8cc47948ab7b1R1-R40) [[2]](diffhunk://#diff-9af6fdbe45b2182c7bca1908f3ce823c3f8913dfb84e7a1cdbd71b49ed446b25R1-R36)
- `LX32MCCodeEmitter` for translating MCInsts to binary machine code, with support for encoding instructions and operands.
- `LX32AsmBackend` for fixup application and NOP emission, handling instruction encoding details.
- `LX32ELFObjectWriter` for writing ELF object files, including relocation type selection for branch and jump instructions.
>From 76aaf194bd7e1e578bdbfb3effd5117b79ea540e Mon Sep 17 00:00:00 2001
From: Axel Ibarrondo <135344010+Axel84727 at users.noreply.github.com>
Date: Thu, 9 Apr 2026 12:05:19 -0300
Subject: [PATCH 1/3] Add LX32 target backend and triple support
---
llvm/CMakeLists.txt | 1 +
llvm/include/llvm/TargetParser/Triple.h | 1 +
llvm/lib/Target/LX32 | 1 +
llvm/lib/TargetParser/TargetDataLayout.cpp | 12 ++++++++++++
llvm/lib/TargetParser/Triple.cpp | 8 +++++++-
5 files changed, 22 insertions(+), 1 deletion(-)
create mode 120000 llvm/lib/Target/LX32
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index da012596cca93..93490779207e6 100644
--- a/llvm/CMakeLists.txt
+++ b/llvm/CMakeLists.txt
@@ -575,6 +575,7 @@ set(LLVM_ALL_TARGETS
AVR
BPF
Hexagon
+ lX32
Lanai
LoongArch
Mips
diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index 7c5aa52a1bd04..e55414d94da44 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -76,6 +76,7 @@ class Triple {
r600, // R600: AMD GPUs HD2XXX - HD6XXX
amdgcn, // AMDGCN: AMD GCN GPUs
riscv32, // RISC-V (32-bit, little endian): riscv32
+ lx32, // Lx32 (32-bit)
riscv64, // RISC-V (64-bit, little endian): riscv64
riscv32be, // RISC-V (32-bit, big endian): riscv32be
riscv64be, // RISC-V (64-bit, big endian): riscv64be
diff --git a/llvm/lib/Target/LX32 b/llvm/lib/Target/LX32
new file mode 120000
index 0000000000000..63ff44d2977c9
--- /dev/null
+++ b/llvm/lib/Target/LX32
@@ -0,0 +1 @@
+/Users/axel/lx32/tools/lx32_backend
\ No newline at end of file
diff --git a/llvm/lib/TargetParser/TargetDataLayout.cpp b/llvm/lib/TargetParser/TargetDataLayout.cpp
index 231d62f6c0f93..1268a98da72ec 100644
--- a/llvm/lib/TargetParser/TargetDataLayout.cpp
+++ b/llvm/lib/TargetParser/TargetDataLayout.cpp
@@ -278,7 +278,17 @@ static std::string computeAMDDataLayout(const Triple &TT) {
"v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-"
"v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9";
}
+static std::string computeLX32DataLayout(const Triple &TT) {
+
+ if(TT.isOSBinFormatMachO()) {
+ assert(false && "Lx32 not support Mach-O");
+ }
+ assert(TT.isLittleEndian() && "Invalid endianness");
+ assert(TT.isArch32Bit() && "Invalid triple");
+ return "e-m:e-p:32:32-i64:64-n32-S128";
+
+}
static std::string computeRISCVDataLayout(const Triple &TT, StringRef ABIName) {
if (TT.isOSBinFormatMachO()) {
assert(TT.isLittleEndian() && "Invalid endianness");
@@ -588,6 +598,8 @@ std::string Triple::computeDataLayout(StringRef ABIName) const {
case Triple::r600:
case Triple::amdgcn:
return computeAMDDataLayout(*this);
+ case Triple::lx32:
+ return computeLX32DataLayout(*this);
case Triple::riscv32:
case Triple::riscv64:
case Triple::riscv32be:
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index 7a907808c0f34..8632a099c41fa 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -62,6 +62,7 @@ StringRef Triple::getArchTypeName(ArchType Kind) {
case renderscript32: return "renderscript32";
case renderscript64: return "renderscript64";
case riscv32: return "riscv32";
+ case lx32: return "lx32";
case riscv64: return "riscv64";
case riscv32be:
return "riscv32be";
@@ -243,7 +244,7 @@ StringRef Triple::getArchTypePrefix(ArchType Kind) {
case shave: return "shave";
case wasm32:
case wasm64: return "wasm";
-
+ case lx32:
case riscv32:
case riscv64:
case riscv32be:
@@ -482,6 +483,7 @@ Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) {
.Case("ppc64le", ppc64le)
.Case("r600", r600)
.Case("amdgcn", amdgcn)
+ .Case("lx32", lx32)
.Case("riscv32", riscv32)
.Case("riscv64", riscv64)
.Case("riscv32be", riscv32be)
@@ -633,6 +635,7 @@ Triple::ArchType Triple::parseArch(StringRef ArchName) {
Triple::mips64el)
.Case("r600", Triple::r600)
.Case("amdgcn", Triple::amdgcn)
+ .Case("lx32", Triple::lx32)
.Case("riscv32", Triple::riscv32)
.Case("riscv64", Triple::riscv64)
.Case("riscv32be", Triple::riscv32be)
@@ -1021,6 +1024,7 @@ static Triple::ObjectFormatType getDefaultFormat(const Triple &T) {
case Triple::r600:
case Triple::renderscript32:
case Triple::renderscript64:
+ case Triple::lx32:
case Triple::riscv32:
case Triple::riscv64:
case Triple::riscv32be:
@@ -1760,6 +1764,7 @@ unsigned Triple::getArchPointerBitWidth(llvm::Triple::ArchType Arch) {
case llvm::Triple::ppcle:
case llvm::Triple::r600:
case llvm::Triple::renderscript32:
+ case llvm::Triple::lx32:
case llvm::Triple::riscv32:
case llvm::Triple::riscv32be:
case llvm::Triple::shave:
@@ -1870,6 +1875,7 @@ Triple Triple::get32BitArchVariant() const {
case Triple::ppcle:
case Triple::r600:
case Triple::renderscript32:
+ case Triple::lx32:
case Triple::riscv32:
case Triple::riscv32be:
case Triple::shave:
>From c238d1d88fb576ca0db75979b67289c089fd242c Mon Sep 17 00:00:00 2001
From: Axel Ibarrondo <135344010+Axel84727 at users.noreply.github.com>
Date: Thu, 9 Apr 2026 12:30:14 -0300
Subject: [PATCH 2/3] Replace LX32 symlink with full backend implementation
---
llvm/lib/Target/LX32 | 1 -
llvm/lib/Target/LX32/AsmParser/CMakeLists.txt | 13 +
.../Target/LX32/AsmParser/LX32AsmParser.cpp | 293 +++++++++
llvm/lib/Target/LX32/CMakeLists.txt | 5 +
.../Target/LX32/MCTargetDesc/CMakeLists.txt | 16 +
.../LX32/MCTargetDesc/LX32AsmBackend.cpp | 82 +++
.../LX32/MCTargetDesc/LX32ELFObjectWriter.cpp | 39 ++
.../LX32/MCTargetDesc/LX32MCAsmInfo.cpp | 40 ++
.../Target/LX32/MCTargetDesc/LX32MCAsmInfo.h | 36 ++
.../LX32/MCTargetDesc/LX32MCCodeEmitter.cpp | 104 ++++
.../LX32/MCTargetDesc/LX32MCTargetDesc.cpp | 216 +++++++
.../LX32/MCTargetDesc/LX32MCTargetDesc.h | 45 ++
llvm/lib/Target/LX32/TableGen/.gitignore | 2 +
llvm/lib/Target/LX32/TableGen/LX32.td | 93 +++
.../Target/LX32/TableGen/LX32CallingConv.td | 123 ++++
.../Target/LX32/TableGen/LX32InstrFormats.td | 301 ++++++++++
.../lib/Target/LX32/TableGen/LX32InstrInfo.td | 562 ++++++++++++++++++
.../Target/LX32/TableGen/LX32RegisterInfo.td | 126 ++++
.../lib/Target/LX32/TargetInfo/CMakeLists.txt | 11 +
.../Target/LX32/TargetInfo/LX32TargetInfo.cpp | 32 +
.../Target/LX32/TargetInfo/LX32TargetInfo.h | 29 +
llvm/lib/Target/LX32/core/CMakeLists.txt | 45 ++
llvm/lib/Target/LX32/core/LX32AsmPrinter.cpp | 229 +++++++
.../Target/LX32/core/LX32FrameLowering.cpp | 100 ++++
llvm/lib/Target/LX32/core/LX32FrameLowering.h | 212 +++++++
.../lib/Target/LX32/core/LX32ISelDAGToDAG.cpp | 237 ++++++++
llvm/lib/Target/LX32/core/LX32ISelDAGToDAG.h | 24 +
.../lib/Target/LX32/core/LX32ISelLowering.cpp | 279 +++++++++
llvm/lib/Target/LX32/core/LX32ISelLowering.h | 260 ++++++++
llvm/lib/Target/LX32/core/LX32InstrInfo.cpp | 412 +++++++++++++
llvm/lib/Target/LX32/core/LX32InstrInfo.h | 202 +++++++
.../lib/Target/LX32/core/LX32RegisterInfo.cpp | 263 ++++++++
llvm/lib/Target/LX32/core/LX32RegisterInfo.h | 166 ++++++
llvm/lib/Target/LX32/core/LX32Subtarget.cpp | 98 +++
llvm/lib/Target/LX32/core/LX32Subtarget.h | 139 +++++
.../Target/LX32/core/LX32TargetMachine.cpp | 228 +++++++
llvm/lib/Target/LX32/core/LX32TargetMachine.h | 160 +++++
llvm/lib/Target/LX32/target/LX32Target.cpp | 55 ++
38 files changed, 5277 insertions(+), 1 deletion(-)
delete mode 120000 llvm/lib/Target/LX32
create mode 100644 llvm/lib/Target/LX32/AsmParser/CMakeLists.txt
create mode 100644 llvm/lib/Target/LX32/AsmParser/LX32AsmParser.cpp
create mode 100644 llvm/lib/Target/LX32/CMakeLists.txt
create mode 100644 llvm/lib/Target/LX32/MCTargetDesc/CMakeLists.txt
create mode 100644 llvm/lib/Target/LX32/MCTargetDesc/LX32AsmBackend.cpp
create mode 100644 llvm/lib/Target/LX32/MCTargetDesc/LX32ELFObjectWriter.cpp
create mode 100644 llvm/lib/Target/LX32/MCTargetDesc/LX32MCAsmInfo.cpp
create mode 100644 llvm/lib/Target/LX32/MCTargetDesc/LX32MCAsmInfo.h
create mode 100644 llvm/lib/Target/LX32/MCTargetDesc/LX32MCCodeEmitter.cpp
create mode 100644 llvm/lib/Target/LX32/MCTargetDesc/LX32MCTargetDesc.cpp
create mode 100644 llvm/lib/Target/LX32/MCTargetDesc/LX32MCTargetDesc.h
create mode 100644 llvm/lib/Target/LX32/TableGen/.gitignore
create mode 100644 llvm/lib/Target/LX32/TableGen/LX32.td
create mode 100644 llvm/lib/Target/LX32/TableGen/LX32CallingConv.td
create mode 100644 llvm/lib/Target/LX32/TableGen/LX32InstrFormats.td
create mode 100644 llvm/lib/Target/LX32/TableGen/LX32InstrInfo.td
create mode 100644 llvm/lib/Target/LX32/TableGen/LX32RegisterInfo.td
create mode 100644 llvm/lib/Target/LX32/TargetInfo/CMakeLists.txt
create mode 100644 llvm/lib/Target/LX32/TargetInfo/LX32TargetInfo.cpp
create mode 100644 llvm/lib/Target/LX32/TargetInfo/LX32TargetInfo.h
create mode 100644 llvm/lib/Target/LX32/core/CMakeLists.txt
create mode 100644 llvm/lib/Target/LX32/core/LX32AsmPrinter.cpp
create mode 100644 llvm/lib/Target/LX32/core/LX32FrameLowering.cpp
create mode 100644 llvm/lib/Target/LX32/core/LX32FrameLowering.h
create mode 100644 llvm/lib/Target/LX32/core/LX32ISelDAGToDAG.cpp
create mode 100644 llvm/lib/Target/LX32/core/LX32ISelDAGToDAG.h
create mode 100644 llvm/lib/Target/LX32/core/LX32ISelLowering.cpp
create mode 100644 llvm/lib/Target/LX32/core/LX32ISelLowering.h
create mode 100644 llvm/lib/Target/LX32/core/LX32InstrInfo.cpp
create mode 100644 llvm/lib/Target/LX32/core/LX32InstrInfo.h
create mode 100644 llvm/lib/Target/LX32/core/LX32RegisterInfo.cpp
create mode 100644 llvm/lib/Target/LX32/core/LX32RegisterInfo.h
create mode 100644 llvm/lib/Target/LX32/core/LX32Subtarget.cpp
create mode 100644 llvm/lib/Target/LX32/core/LX32Subtarget.h
create mode 100644 llvm/lib/Target/LX32/core/LX32TargetMachine.cpp
create mode 100644 llvm/lib/Target/LX32/core/LX32TargetMachine.h
create mode 100644 llvm/lib/Target/LX32/target/LX32Target.cpp
diff --git a/llvm/lib/Target/LX32 b/llvm/lib/Target/LX32
deleted file mode 120000
index 63ff44d2977c9..0000000000000
--- a/llvm/lib/Target/LX32
+++ /dev/null
@@ -1 +0,0 @@
-/Users/axel/lx32/tools/lx32_backend
\ No newline at end of file
diff --git a/llvm/lib/Target/LX32/AsmParser/CMakeLists.txt b/llvm/lib/Target/LX32/AsmParser/CMakeLists.txt
new file mode 100644
index 0000000000000..4a3eadf1a4a57
--- /dev/null
+++ b/llvm/lib/Target/LX32/AsmParser/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_llvm_component_library(LLVMLX32AsmParser
+ LX32AsmParser.cpp
+
+ LINK_COMPONENTS
+ MC
+ MCParser
+ Support
+ LX32Desc
+ LX32Info
+
+ ADD_TO_COMPONENT
+ LX32
+ )
diff --git a/llvm/lib/Target/LX32/AsmParser/LX32AsmParser.cpp b/llvm/lib/Target/LX32/AsmParser/LX32AsmParser.cpp
new file mode 100644
index 0000000000000..85864be9140c5
--- /dev/null
+++ b/llvm/lib/Target/LX32/AsmParser/LX32AsmParser.cpp
@@ -0,0 +1,293 @@
+//===-- LX32AsmParser.cpp - Parse LX32 assembly to MCInst instructions --===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+
+#include "../MCTargetDesc/LX32MCTargetDesc.h"
+#include "llvm/MC/MCParser/MCAsmParser.h"
+#include "llvm/MC/MCParser/MCAsmParserExtension.h"
+#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
+#include "llvm/MC/MCParser/MCTargetAsmParser.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCExpr.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "../TargetInfo/LX32TargetInfo.h"
+
+using namespace llvm;
+
+// Put the enums here
+#define GET_REGINFO_ENUM
+#include "../TableGen/LX32GenRegisterInfo.inc"
+#define GET_INSTRINFO_ENUM
+#include "../TableGen/LX32GenInstrInfo.inc"
+
+namespace {
+
+class LX32Operand : public MCParsedAsmOperand {
+ enum KindTy {
+ Token,
+ Register,
+ Immediate,
+ } Kind;
+
+ StringRef Tok;
+ unsigned RegNum;
+ const MCExpr *ImmVal;
+ SMLoc StartLoc, EndLoc;
+
+public:
+ LX32Operand(KindTy K) : MCParsedAsmOperand(), Kind(K) {}
+
+ bool isToken() const override { return Kind == Token; }
+ bool isReg() const override { return Kind == Register; }
+ bool isImm() const override { return Kind == Immediate; }
+ bool isMem() const override { return false; }
+
+ static std::unique_ptr<LX32Operand> createToken(StringRef Str, SMLoc S) {
+ auto Op = std::make_unique<LX32Operand>(Token);
+ Op->Tok = Str;
+ Op->StartLoc = S;
+ Op->EndLoc = S;
+ return Op;
+ }
+
+ static std::unique_ptr<LX32Operand> createReg(unsigned RegNo, SMLoc S, SMLoc E) {
+ auto Op = std::make_unique<LX32Operand>(Register);
+ Op->RegNum = RegNo;
+ Op->StartLoc = S;
+ Op->EndLoc = E;
+ return Op;
+ }
+
+ static std::unique_ptr<LX32Operand> createImm(const MCExpr *Val, SMLoc S, SMLoc E) {
+ auto Op = std::make_unique<LX32Operand>(Immediate);
+ Op->ImmVal = Val;
+ Op->StartLoc = S;
+ Op->EndLoc = E;
+ return Op;
+ }
+
+ SMLoc getStartLoc() const override { return StartLoc; }
+ SMLoc getEndLoc() const override { return EndLoc; }
+
+ MCRegister getReg() const override {
+ assert(Kind == Register && "Invalid access!");
+ return RegNum;
+ }
+
+ const MCExpr *getImm() const {
+ assert(Kind == Immediate && "Invalid access!");
+ return ImmVal;
+ }
+
+ StringRef getToken() const {
+ assert(Kind == Token && "Invalid access!");
+ return Tok;
+ }
+
+ void print(raw_ostream &OS, const MCAsmInfo &MAI) const override {
+ switch (Kind) {
+ case Token: OS << "Token: " << Tok; break;
+ case Register: OS << "Reg: " << RegNum; break;
+ case Immediate: OS << "Imm"; break;
+ }
+ }
+
+ void addRegOperands(MCInst &Inst, unsigned N) const {
+ assert(N == 1 && "Invalid number of operands!");
+ Inst.addOperand(MCOperand::createReg(getReg()));
+ }
+
+ void addExpr(MCInst &Inst, const MCExpr *Expr) const {
+ if (auto *CE = dyn_cast<MCConstantExpr>(Expr))
+ Inst.addOperand(MCOperand::createImm(CE->getValue()));
+ else
+ Inst.addOperand(MCOperand::createExpr(Expr));
+ }
+
+ void addImmOperands(MCInst &Inst, unsigned N) const {
+ assert(N == 1 && "Invalid number of operands!");
+ addExpr(Inst, getImm());
+ }
+};
+
+} // end anonymous namespace
+
+namespace llvm {
+class LX32AsmParser : public MCTargetAsmParser {
+ const MCRegisterInfo *MRI;
+
+ bool matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
+ OperandVector &Operands, MCStreamer &Out,
+ uint64_t &ErrorInfo,
+ bool MatchingInlineAsm) override;
+
+ bool parseRegister(MCRegister &Reg, SMLoc &StartLoc, SMLoc &EndLoc) override;
+
+ ParseStatus tryParseRegister(MCRegister &Reg, SMLoc &StartLoc,
+ SMLoc &EndLoc) override;
+
+ bool parseInstruction(ParseInstructionInfo &Info, StringRef Name,
+ SMLoc NameLoc, OperandVector &Operands) override;
+
+ ParseStatus parseDirective(AsmToken DirectiveID) override;
+
+ bool parseOperand(OperandVector &Operands, StringRef Name);
+
+public:
+ LX32AsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,
+ const MCInstrInfo &MII, const MCTargetOptions &Options)
+ : MCTargetAsmParser(Options, STI, MII), MRI(Parser.getContext().getRegisterInfo()) {
+ setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
+ }
+
+#define GET_ASSEMBLER_HEADER
+#include "../TableGen/LX32GenAsmMatcher.inc"
+};
+} // end namespace llvm
+
+#define GET_REGISTER_MATCHER
+#define GET_MATCHER_IMPLEMENTATION
+#include "../TableGen/LX32GenAsmMatcher.inc"
+
+using namespace llvm;
+
+bool LX32AsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
+ OperandVector &Operands,
+ MCStreamer &Out,
+ uint64_t &ErrorInfo,
+ bool MatchingInlineAsm) {
+ MCInst Inst;
+ unsigned MatchResult = MatchInstructionImpl(Operands, Inst, ErrorInfo,
+ MatchingInlineAsm);
+ switch (MatchResult) {
+ case Match_Success:
+ Inst.setLoc(IDLoc);
+ Out.emitInstruction(Inst, getSTI());
+ return false;
+ case Match_MissingFeature:
+ return Error(IDLoc, "instruction requires a CPU feature not currently enabled");
+ case Match_InvalidOperand:
+ return Error(IDLoc, "invalid operand for instruction");
+ case Match_MnemonicFail:
+ return Error(IDLoc, "invalid instruction mnemonic");
+ default:
+ return Error(IDLoc, "unknown error matching instruction");
+ }
+}
+
+bool LX32AsmParser::parseRegister(MCRegister &Reg,
+ SMLoc &StartLoc,
+ SMLoc &EndLoc) {
+ return tryParseRegister(Reg, StartLoc, EndLoc).isSuccess() ? false : true;
+}
+
+ParseStatus LX32AsmParser::tryParseRegister(MCRegister &Reg,
+ SMLoc &StartLoc,
+ SMLoc &EndLoc) {
+ const AsmToken &Tok = getParser().getTok();
+ StartLoc = Tok.getLoc();
+ EndLoc = Tok.getEndLoc();
+ if (Tok.isNot(AsmToken::Identifier))
+ return ParseStatus::NoMatch;
+
+ StringRef Name = Tok.getString();
+ unsigned RegNum = MatchRegisterName(Name.lower());
+ if (RegNum == 0)
+ return ParseStatus::NoMatch;
+
+ Reg = RegNum;
+ getParser().Lex(); // consume the identifier
+ return ParseStatus::Success;
+}
+
+bool LX32AsmParser::parseOperand(OperandVector &Operands,
+ StringRef Mnemonic) {
+ SMLoc S = getTok().getLoc();
+ if (getLexer().is(AsmToken::LParen) || getLexer().is(AsmToken::RParen)) {
+ Operands.push_back(LX32Operand::createToken(getTok().getString(), S));
+ getLexer().Lex();
+ return false;
+ }
+
+ MCRegister Reg;
+ if (tryParseRegister(Reg, S, S).isSuccess()) {
+ Operands.push_back(LX32Operand::createReg(Reg, S, getTok().getLoc()));
+ return false;
+ }
+
+ // Handle immediate
+ if (getLexer().is(AsmToken::Integer) || getLexer().is(AsmToken::Minus) ||
+ getLexer().is(AsmToken::Identifier)) {
+ const MCExpr *IdVal;
+ if (getParser().parseExpression(IdVal))
+ return true;
+
+ // Special handling for memory %lo / %hi wrappers could be here if LX32 uses them.
+ // For MVP, just accept basic expressions.
+ Operands.push_back(LX32Operand::createImm(IdVal, S, getTok().getLoc()));
+ return false;
+ }
+
+ // Not an operand we know how to parse.
+ return true;
+}
+
+bool LX32AsmParser::parseInstruction(ParseInstructionInfo &Info, StringRef Name,
+ SMLoc NameLoc, OperandVector &Operands) {
+ // First operand is token for instruction
+ Operands.push_back(LX32Operand::createToken(Name, NameLoc));
+
+ if (getLexer().is(AsmToken::EndOfStatement))
+ return false;
+
+ // Parse operands
+ while (true) {
+ if (parseOperand(Operands, Name)) {
+ return Error(getTok().getLoc(), "unexpected token in operand");
+ }
+
+ if (getLexer().is(AsmToken::EndOfStatement))
+ break;
+
+ if (getLexer().is(AsmToken::Comma)) {
+ getLexer().Lex(); // Consume comma
+ } else if (getLexer().is(AsmToken::LParen) || getLexer().is(AsmToken::RParen)) {
+ // Handled in next loop iteration
+ } else if (Operands.size() > 1 && static_cast<LX32Operand*>(Operands.back().get())->isToken() &&
+ (static_cast<LX32Operand*>(Operands.back().get())->getToken() == "(" ||
+ static_cast<LX32Operand*>(Operands.back().get())->getToken() == ")")) {
+ // Previous token was a parenthesis, no comma required before next operand
+ } else if (Operands.size() > 2 && static_cast<LX32Operand*>(Operands[Operands.size()-2].get())->isToken() &&
+ static_cast<LX32Operand*>(Operands[Operands.size()-2].get())->getToken() == "(" &&
+ static_cast<LX32Operand*>(Operands.back().get())->isReg()) {
+ // Inside parenthesis, parsed a register, RParen comes next
+ } else {
+ return Error(getTok().getLoc(), "unexpected token in operand list");
+ }
+ }
+
+ return false;
+}
+
+ParseStatus LX32AsmParser::parseDirective(AsmToken DirectiveID) {
+ return ParseStatus::NoMatch; // Use default parser for directives
+}
+
+extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
+LLVMInitializeLX32AsmParser() {
+ TargetRegistry::RegisterMCAsmParser(getTheLX32TargetInfo(),
+ [](const MCSubtargetInfo &STI,
+ MCAsmParser &Parser,
+ const MCInstrInfo &MII,
+ const MCTargetOptions &Options) -> MCTargetAsmParser * {
+ return new LX32AsmParser(STI, Parser, MII, Options);
+ });
+}
diff --git a/llvm/lib/Target/LX32/CMakeLists.txt b/llvm/lib/Target/LX32/CMakeLists.txt
new file mode 100644
index 0000000000000..d2bd9861ac5c3
--- /dev/null
+++ b/llvm/lib/Target/LX32/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_llvm_component_group(LX32)
+add_subdirectory(TargetInfo)
+add_subdirectory(MCTargetDesc)
+add_subdirectory(AsmParser)
+add_subdirectory(core)
diff --git a/llvm/lib/Target/LX32/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/LX32/MCTargetDesc/CMakeLists.txt
new file mode 100644
index 0000000000000..03d00fad5ce02
--- /dev/null
+++ b/llvm/lib/Target/LX32/MCTargetDesc/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_llvm_component_library(LLVMLX32Desc
+ LX32AsmBackend.cpp
+ LX32ELFObjectWriter.cpp
+ LX32MCAsmInfo.cpp
+ LX32MCCodeEmitter.cpp
+ LX32MCTargetDesc.cpp
+
+ LINK_COMPONENTS
+ MC
+ LX32Info
+ Support
+ TargetParser
+
+ ADD_TO_COMPONENT
+ LX32
+)
\ No newline at end of file
diff --git a/llvm/lib/Target/LX32/MCTargetDesc/LX32AsmBackend.cpp b/llvm/lib/Target/LX32/MCTargetDesc/LX32AsmBackend.cpp
new file mode 100644
index 0000000000000..57849d4f990c0
--- /dev/null
+++ b/llvm/lib/Target/LX32/MCTargetDesc/LX32AsmBackend.cpp
@@ -0,0 +1,82 @@
+//===-- LX32AsmBackend.cpp - LX32 Assembler Backend -------------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+
+#include "LX32MCTargetDesc.h"
+#include "llvm/MC/MCAsmBackend.h"
+#include "llvm/MC/MCAssembler.h"
+#include "llvm/MC/MCDirectives.h"
+#include "llvm/MC/MCELFObjectWriter.h"
+#include "llvm/MC/MCObjectWriter.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCExpr.h"
+#include "llvm/MC/MCValue.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+namespace {
+class LX32AsmBackend : public MCAsmBackend {
+public:
+ LX32AsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI)
+ : MCAsmBackend(llvm::endianness::little) {}
+ ~LX32AsmBackend() override {}
+
+ void applyFixup(const MCFragment &Fragment, const MCFixup &Fixup,
+ const MCValue &Target, uint8_t *Data, uint64_t Value,
+ bool IsResolved) override {
+ if (!IsResolved) return;
+ uint32_t CurVal = 0;
+ CurVal = (uint32_t)Data[0] | ((uint32_t)Data[1] << 8) |
+ ((uint32_t)Data[2] << 16) | ((uint32_t)Data[3] << 24);
+
+ if (Fixup.getKind() == (MCFixupKind)1 /* branch */) {
+ uint32_t imm = Value;
+ uint32_t bit11 = (imm >> 11) & 1;
+ uint32_t bit4_1 = (imm >> 1) & 0xF;
+ uint32_t bit10_5 = (imm >> 5) & 0x3F;
+ uint32_t bit12 = (imm >> 12) & 1;
+ CurVal |= (bit11 << 7) | (bit4_1 << 8) | (bit10_5 << 25) | (bit12 << 31);
+ } else if (Fixup.getKind() == (MCFixupKind)2 /* jump */) {
+ uint32_t imm = Value;
+ uint32_t bit19_12 = (imm >> 12) & 0xFF;
+ uint32_t bit11 = (imm >> 11) & 1;
+ uint32_t bit10_1 = (imm >> 1) & 0x3FF;
+ uint32_t bit20 = (imm >> 20) & 1;
+ CurVal |= (bit19_12 << 12) | (bit11 << 20) | (bit10_1 << 21) | (bit20 << 31);
+ }
+
+ Data[0] = CurVal & 0xFF;
+ Data[1] = (CurVal >> 8) & 0xFF;
+ Data[2] = (CurVal >> 16) & 0xFF;
+ Data[3] = (CurVal >> 24) & 0xFF;
+ }
+
+ bool writeNopData(raw_ostream &OS, uint64_t Count,
+ const MCSubtargetInfo *STI) const override {
+ // 4-byte NOPs = addi x0, x0, 0 = 0x00000013
+ uint64_t NumNops = Count / 4;
+ for (uint64_t i = 0; i != NumNops; ++i)
+ OS.write("\x13\x00\x00\x00", 4);
+
+ OS.write_zeros(Count % 4);
+ return true;
+ }
+
+ std::unique_ptr<MCObjectTargetWriter>
+ createObjectTargetWriter() const override {
+ return createLX32ELFObjectWriter(0);
+ }
+};
+} // end anonymous namespace
+
+MCAsmBackend *llvm::createLX32AsmBackend(const Target &T,
+ const MCSubtargetInfo &STI,
+ const MCRegisterInfo &MRI,
+ const MCTargetOptions &Options) {
+ return new LX32AsmBackend(STI, 0); // OSABI 0
+}
diff --git a/llvm/lib/Target/LX32/MCTargetDesc/LX32ELFObjectWriter.cpp b/llvm/lib/Target/LX32/MCTargetDesc/LX32ELFObjectWriter.cpp
new file mode 100644
index 0000000000000..c6c263313ad1d
--- /dev/null
+++ b/llvm/lib/Target/LX32/MCTargetDesc/LX32ELFObjectWriter.cpp
@@ -0,0 +1,39 @@
+//===-- LX32ELFObjectWriter.cpp - LX32 ELF Writer ---------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+#include "LX32MCTargetDesc.h"
+#include "llvm/MC/MCELFObjectWriter.h"
+#include "llvm/MC/MCFixup.h"
+#include "llvm/MC/MCObjectWriter.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/BinaryFormat/ELF.h"
+
+using namespace llvm;
+
+namespace {
+class LX32ELFObjectWriter : public MCELFObjectTargetWriter {
+public:
+ LX32ELFObjectWriter(uint8_t OSABI)
+ : MCELFObjectTargetWriter(/*Is64Bit*/ false, OSABI, ELF::EM_RISCV,
+ /*HasRelocationAddend*/ true) {}
+
+ ~LX32ELFObjectWriter() override {}
+
+protected:
+ unsigned getRelocType(const MCFixup &Fixup, const MCValue &Target, bool IsPCRel) const override {
+ if (Fixup.getKind() == (MCFixupKind)1 /* branch */)
+ return ELF::R_RISCV_BRANCH;
+ if (Fixup.getKind() == (MCFixupKind)2 /* jump */)
+ return ELF::R_RISCV_JAL;
+ return ELF::R_RISCV_NONE; // R_NONE is always 0
+ }
+};
+} // end anonymous namespace
+
+std::unique_ptr<MCObjectTargetWriter>
+llvm::createLX32ELFObjectWriter(uint8_t OSABI) {
+ return std::make_unique<LX32ELFObjectWriter>(OSABI);
+}
diff --git a/llvm/lib/Target/LX32/MCTargetDesc/LX32MCAsmInfo.cpp b/llvm/lib/Target/LX32/MCTargetDesc/LX32MCAsmInfo.cpp
new file mode 100644
index 0000000000000..2fe467b2bc468
--- /dev/null
+++ b/llvm/lib/Target/LX32/MCTargetDesc/LX32MCAsmInfo.cpp
@@ -0,0 +1,40 @@
+//===-- LX32MCAsmInfo.cpp - LX32 MC Assembler Information ----------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines LX32 MCAsmInfo defaults used by assembly/object emission.
+// It is organized into the following sections:
+//
+// Section 0 — Class anchor
+// Section 1 — Constructor defaults and ABI choices
+//
+//===----------------------------------------------------------------------===//
+
+#include "LX32MCAsmInfo.h"
+#include "llvm/TargetParser/Triple.h"
+
+void LX32MCAsmInfo::anchor() {}
+
+//===----------------------------------------------------------------------===//
+// Section 1 — Constructor defaults and ABI choices
+//===----------------------------------------------------------------------===//
+
+LX32MCAsmInfo::LX32MCAsmInfo(const llvm::Triple &TT) {
+ // LX32 is little-endian and uses 32-bit pointers/stack slots.
+ IsLittleEndian = true;
+ CodePointerSize = 4;
+ CalleeSaveStackSlotSize = 4;
+
+ // GNU-style assembler comments.
+ CommentString = "#";
+ AlignmentIsInBytes = false;
+ SupportsDebugInformation = true;
+ // Must match Triple default; CodeGenTargetMachineImpl::initAsmInfo asserts
+ // if MCAsmInfo and Triple disagree.
+ ExceptionsType = TT.getDefaultExceptionHandling();
+ Data16bitsDirective = "\t.half\t";
+ Data32bitsDirective = "\t.word\t";
+}
\ No newline at end of file
diff --git a/llvm/lib/Target/LX32/MCTargetDesc/LX32MCAsmInfo.h b/llvm/lib/Target/LX32/MCTargetDesc/LX32MCAsmInfo.h
new file mode 100644
index 0000000000000..5738350ad35c0
--- /dev/null
+++ b/llvm/lib/Target/LX32/MCTargetDesc/LX32MCAsmInfo.h
@@ -0,0 +1,36 @@
+//===-- LX32MCAsmInfo.h - LX32 MC Assembler Information ------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the LX32-specific MCAsmInfo class declaration.
+// It is organized into the following sections:
+//
+// Section 0 — Forward declarations
+// Section 1 — LX32MCAsmInfo declaration
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_LX32_MCTARGETDESC_LX32MCASMINFO_H
+#define LLVM_LIB_TARGET_LX32_MCTARGETDESC_LX32MCASMINFO_H
+
+#include "llvm/MC/MCAsmInfoELF.h"
+
+namespace llvm {
+class Triple;
+} // namespace llvm
+
+//===----------------------------------------------------------------------===//
+// Section 1 — LX32MCAsmInfo declaration
+//===----------------------------------------------------------------------===//
+
+class LX32MCAsmInfo : public llvm::MCAsmInfoELF {
+ void anchor() override;
+public:
+ // Build MC assembler defaults for the selected target triple.
+ explicit LX32MCAsmInfo(const llvm::Triple &TargetTriple);
+};
+
+#endif // LLVM_LIB_TARGET_LX32_MCTARGETDESC_LX32MCASMINFO_H
diff --git a/llvm/lib/Target/LX32/MCTargetDesc/LX32MCCodeEmitter.cpp b/llvm/lib/Target/LX32/MCTargetDesc/LX32MCCodeEmitter.cpp
new file mode 100644
index 0000000000000..02ab66302251f
--- /dev/null
+++ b/llvm/lib/Target/LX32/MCTargetDesc/LX32MCCodeEmitter.cpp
@@ -0,0 +1,104 @@
+//===-- LX32MCCodeEmitter.cpp - Convert LX32 code to machine code --------===//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//===----------------------------------------------------------------------===//
+#include "LX32MCTargetDesc.h"
+#include "llvm/MC/MCCodeEmitter.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCExpr.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/Endian.h"
+
+using namespace llvm;
+
+#define GET_INSTRINFO_ENUM
+#include "../TableGen/LX32GenInstrInfo.inc"
+
+#define DEBUG_TYPE "mccodeemitter"
+
+namespace {
+class LX32MCCodeEmitter : public MCCodeEmitter {
+ const MCInstrInfo &MCII;
+ MCContext &Ctx;
+
+public:
+ LX32MCCodeEmitter(const MCInstrInfo &mcii, MCContext &ctx)
+ : MCII(mcii), Ctx(ctx) {}
+
+ void encodeInstruction(const MCInst &MI, SmallVectorImpl<char> &CB,
+ SmallVectorImpl<MCFixup> &Fixups,
+ const MCSubtargetInfo &STI) const override;
+
+ uint64_t getBinaryCodeForInstr(const MCInst &MI,
+ SmallVectorImpl<MCFixup> &Fixups,
+ const MCSubtargetInfo &STI) const;
+
+ unsigned getMachineOpValue(const MCInst &MI, const MCOperand &MO,
+ SmallVectorImpl<MCFixup> &Fixups,
+ const MCSubtargetInfo &STI) const;
+
+ unsigned getImmOpValue(const MCInst &MI, unsigned OpNo,
+ SmallVectorImpl<MCFixup> &Fixups,
+ const MCSubtargetInfo &STI) const {
+ return getMachineOpValue(MI, MI.getOperand(OpNo), Fixups, STI);
+ }
+
+ unsigned getBranchTargetOpValue(const MCInst &MI, unsigned OpNo,
+ SmallVectorImpl<MCFixup> &Fixups,
+ const MCSubtargetInfo &STI) const {
+ const MCOperand &MO = MI.getOperand(OpNo);
+ if (MO.isExpr()) {
+ Fixups.push_back(MCFixup::create(0, MO.getExpr(),
+ (MCFixupKind)1));
+ return 0;
+ }
+ return getMachineOpValue(MI, MO, Fixups, STI);
+ }
+
+ unsigned getJumpTargetOpValue(const MCInst &MI, unsigned OpNo,
+ SmallVectorImpl<MCFixup> &Fixups,
+ const MCSubtargetInfo &STI) const {
+ const MCOperand &MO = MI.getOperand(OpNo);
+ if (MO.isExpr()) {
+ Fixups.push_back(MCFixup::create(0, MO.getExpr(),
+ (MCFixupKind)2));
+ return 0;
+ }
+ return getMachineOpValue(MI, MO, Fixups, STI);
+ }
+};
+} // end anonymous namespace
+
+MCCodeEmitter *llvm::createLX32MCCodeEmitter(const MCInstrInfo &MCII,
+ MCContext &Ctx) {
+ return new LX32MCCodeEmitter(MCII, Ctx);
+}
+
+void LX32MCCodeEmitter::encodeInstruction(const MCInst &MI, SmallVectorImpl<char> &CB,
+ SmallVectorImpl<MCFixup> &Fixups,
+ const MCSubtargetInfo &STI) const {
+ uint64_t Bits = getBinaryCodeForInstr(MI, Fixups, STI);
+ support::endian::write<uint32_t>(CB, Bits, llvm::endianness::little);
+}
+
+unsigned LX32MCCodeEmitter::getMachineOpValue(const MCInst &MI, const MCOperand &MO,
+ SmallVectorImpl<MCFixup> &Fixups,
+ const MCSubtargetInfo &STI) const {
+ if (MO.isReg())
+ return Ctx.getRegisterInfo()->getEncodingValue(MO.getReg());
+
+ if (MO.isImm())
+ return static_cast<unsigned>(MO.getImm());
+
+ if (MO.isExpr()) {
+ return 0; // Expr values unhandled in dummy code emitter
+ }
+ return 0;
+}
+
+#include "../TableGen/LX32GenMCCodeEmitter.inc"
+
diff --git a/llvm/lib/Target/LX32/MCTargetDesc/LX32MCTargetDesc.cpp b/llvm/lib/Target/LX32/MCTargetDesc/LX32MCTargetDesc.cpp
new file mode 100644
index 0000000000000..d9b1258d716f8
--- /dev/null
+++ b/llvm/lib/Target/LX32/MCTargetDesc/LX32MCTargetDesc.cpp
@@ -0,0 +1,216 @@
+//===-- LX32MCTargetDesc.cpp - LX32 MC Target Description ----------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines MC-layer registration and factories for LX32.
+// It is organized into the following sections:
+//
+// Section 0 — Generated descriptor imports
+// Section 1 — Minimal MCInstPrinter implementation
+// Section 2 — Factory functions (MCAsmInfo/MCInstrInfo/MCSubtargetInfo/...)
+// Section 3 — Target registration entry point
+//
+//===----------------------------------------------------------------------===//
+
+#include "LX32MCTargetDesc.h"
+#include "LX32MCAsmInfo.h"
+#include "../TargetInfo/LX32TargetInfo.h"
+
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCDwarf.h" // for MCCFIInstruction
+#include "llvm/MC/MCExpr.h" // for MCExpr::print
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/Triple.h"
+
+// Pull in TableGen-generated enums and init helpers.
+//
+// The generated MC descriptor tables reference symbols like LX32::X1 and
+// LX32::GPRRegClassID. Those are emitted by the *_ENUM sections.
+// If we skip them, the compiler will fail with:
+// error: use of undeclared identifier 'LX32'
+
+#define GET_REGINFO_ENUM
+#include "../TableGen/LX32GenRegisterInfo.inc"
+
+#define GET_INSTRINFO_ENUM
+#include "../TableGen/LX32GenInstrInfo.inc"
+
+// Now pull in the generated Init* functions and createLX32MCSubtargetInfoImpl.
+#define GET_INSTRINFO_MC_DESC
+#include "../TableGen/LX32GenInstrInfo.inc"
+
+#define GET_REGINFO_MC_DESC
+#include "../TableGen/LX32GenRegisterInfo.inc"
+
+// Emits: createLX32MCSubtargetInfoImpl(), LX32WriteProcResTable, etc.
+#define GET_SUBTARGETINFO_MC_DESC
+#include "../TableGen/LX32GenSubtargetInfo.inc"
+
+using namespace llvm;
+
+//===----------------------------------------------------------------------===//
+// MCInstPrinter — minimal implementation
+//===----------------------------------------------------------------------===//
+//
+// LLVM requires a registered MCInstPrinter before it can create an
+// AsmStreamer. The printer is responsible for converting an MCInst to human-
+// readable text. A skeleton implementation that prints register/immediate
+// operands is sufficient to pass the assertion and produce readable (if
+// incomplete) assembly output.
+
+namespace {
+
+class LX32InstPrinter : public MCInstPrinter {
+public:
+ LX32InstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII,
+ const MCRegisterInfo &MRI)
+ : MCInstPrinter(MAI, MII, MRI) {}
+
+ // Print a full instruction. For now we fall back to the raw opcode number
+ // and operand list so that the pipeline produces _something_ instead of
+ // crashing. This will be replaced by the TableGen-generated AsmWriter once
+ // LX32AsmWriter.inc is available.
+ void printInst(const MCInst *MI, uint64_t Address, StringRef Annot,
+ const MCSubtargetInfo &STI, raw_ostream &OS) override {
+ // Try to get the instruction name from the generated table.
+ StringRef Name = MII.getName(MI->getOpcode());
+ if (!Name.empty())
+ OS << "\t" << Name;
+ else
+ OS << "\t<opcode:" << MI->getOpcode() << ">";
+
+ // Print operands separated by ", ".
+ for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
+ OS << (i == 0 ? "\t" : ", ");
+ const MCOperand &Op = MI->getOperand(i);
+ if (Op.isReg())
+ // Use the ABI register name (alt name index 0 = ABIRegAltName).
+ OS << getRegisterName(Op.getReg());
+ else if (Op.isImm())
+ OS << Op.getImm();
+ else if (Op.isExpr())
+ // Some LLVM versions keep MCExpr::print() private and do not provide an
+ // operator<< overload. Until we hook up a real asm writer, print a
+ // stable placeholder instead of failing to compile.
+ OS << "<expr>";
+ else
+ OS << "<unknown operand>";
+ }
+
+ // Emit any inline annotation (e.g. branch target comment).
+ printAnnotation(OS, Annot);
+ }
+
+ // Required: return the register name. We use ABIRegAltName (index 0)
+ // so that x10 prints as "a0", x1 as "ra", etc.
+ static const char *getRegisterName(MCRegister Reg) {
+ // IMPORTANT:
+ // Returning nullptr here is UB for callers and has been observed to
+ // trigger asserts/crashes when the MC layer tries to print instructions.
+ //
+ // Until we hook up the TableGen-generated AsmWriter (which provides proper
+ // ABI names like "sp", "ra", "a0", ...), we provide a tiny but safe
+ // fallback that prints registers as "x<N>".
+ // TableGen allocates registers starting from ID 1, so we subtract 1.
+ static thread_local char Buf[16];
+ unsigned R = static_cast<unsigned>(Reg.id());
+ if (R > 0) R -= 1;
+ (void)snprintf(Buf, sizeof(Buf), "x%u", R);
+ return Buf;
+ }
+
+ // Required pure virtual from MCInstPrinter — return the instruction mnemonic.
+ std::pair<const char *, uint64_t>
+ getMnemonic(const MCInst &MI) const override {
+ StringRef Name = MII.getName(MI.getOpcode());
+ return {Name.data(), 0};
+ }
+};
+
+} // anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Factory functions
+//===----------------------------------------------------------------------===//
+
+static MCInstrInfo *createLX32MCInstrInfo() {
+ MCInstrInfo *X = new MCInstrInfo();
+ InitLX32MCInstrInfo(X); // generated by GET_INSTRINFO_MC_DESC
+ return X;
+}
+
+static MCRegisterInfo *createLX32MCRegisterInfo(const Triple &TT) {
+ MCRegisterInfo *X = new MCRegisterInfo();
+ // X1 = ra (return address register — used for DWARF CFA).
+ InitLX32MCRegisterInfo(X, LX32::X1); // generated by GET_REGINFO_MC_DESC
+ return X;
+}
+
+static MCAsmInfo *createLX32MCAsmInfo(const MCRegisterInfo &MRI,
+ const Triple &TT,
+ const MCTargetOptions &Options) {
+ if (!TT.isOSBinFormatELF())
+ llvm::report_fatal_error("lx32: only ELF object format is supported");
+
+ MCAsmInfo *MAI = new LX32MCAsmInfo(TT);
+
+ // Set the initial frame state so DWARF unwinding works.
+ // SP (x2) is register number 2 in the DWARF mapping.
+ unsigned SP = MRI.getDwarfRegNum(LX32::X2, /*isEH=*/true);
+ MCCFIInstruction Inst = MCCFIInstruction::cfiDefCfa(nullptr, SP, 0);
+ MAI->addInitialFrameState(Inst);
+
+ return MAI;
+}
+
+static MCSubtargetInfo *createLX32MCSubtargetInfo(const Triple &TT,
+ StringRef CPU,
+ StringRef FS) {
+ // Use the TableGen-generated factory. This ensures the processor table
+ // (LX32SubTypeKV) is populated and "generic" is recognised.
+ // The previous manual construction with empty arrays was the root cause of:
+ // 'generic' is not a recognized processor for this target (ignoring processor)
+ if (CPU.empty())
+ CPU = "generic";
+ return createLX32MCSubtargetInfoImpl(TT, CPU, /*TuneCPU=*/CPU, FS);
+}
+
+static MCInstPrinter *createLX32MCInstPrinter(const Triple &TT,
+ unsigned SyntaxVariant,
+ const MCAsmInfo &MAI,
+ const MCInstrInfo &MII,
+ const MCRegisterInfo &MRI) {
+ // Only one syntax variant is defined for lx32.
+ if (SyntaxVariant != 0)
+ return nullptr;
+ return new LX32InstPrinter(MAI, MII, MRI);
+}
+
+//===----------------------------------------------------------------------===//
+// Registration
+//===----------------------------------------------------------------------===//
+
+extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
+LLVMInitializeLX32TargetMC() {
+ Target &T = getTheLX32TargetInfo();
+
+ TargetRegistry::RegisterMCAsmInfo(T, createLX32MCAsmInfo);
+ TargetRegistry::RegisterMCInstrInfo(T, createLX32MCInstrInfo);
+ TargetRegistry::RegisterMCRegInfo(T, createLX32MCRegisterInfo);
+ TargetRegistry::RegisterMCSubtargetInfo(T, createLX32MCSubtargetInfo);
+ TargetRegistry::RegisterMCInstPrinter(T, createLX32MCInstPrinter);
+
+ TargetRegistry::RegisterMCCodeEmitter(T, createLX32MCCodeEmitter);
+ TargetRegistry::RegisterMCAsmBackend(T, createLX32AsmBackend);
+}
\ No newline at end of file
diff --git a/llvm/lib/Target/LX32/MCTargetDesc/LX32MCTargetDesc.h b/llvm/lib/Target/LX32/MCTargetDesc/LX32MCTargetDesc.h
new file mode 100644
index 0000000000000..89e33e65e1de9
--- /dev/null
+++ b/llvm/lib/Target/LX32/MCTargetDesc/LX32MCTargetDesc.h
@@ -0,0 +1,45 @@
+//===-- LX32MCTargetDesc.h - LX32 MC Target Description ------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares LX32 MC target-description forward declarations.
+// It is organized into the following sections:
+//
+// Section 0 — Required LLVM includes
+// Section 1 — Forward declarations used by MC factories
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_LIB_TARGET_LX32_MCTARGETDESC_LX32MCTARGETDESC_H
+#define LLVM_LIB_TARGET_LX32_MCTARGETDESC_LX32MCTARGETDESC_H
+
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/Support/DataTypes.h"
+#include <memory>
+
+namespace llvm {
+class Target;
+class MCInstrInfo;
+class MCRegisterInfo;
+class MCSubtargetInfo;
+class MCContext;
+class MCCodeEmitter;
+class MCAsmBackend;
+class MCTargetOptions;
+class MCObjectTargetWriter;
+
+MCCodeEmitter *createLX32MCCodeEmitter(const MCInstrInfo &MCII,
+ MCContext &Ctx);
+
+MCAsmBackend *createLX32AsmBackend(const Target &T,
+ const MCSubtargetInfo &STI,
+ const MCRegisterInfo &MRI,
+ const MCTargetOptions &Options);
+
+std::unique_ptr<MCObjectTargetWriter> createLX32ELFObjectWriter(uint8_t OSABI);
+
+} // End llvm namespace
+
+#endif // LLVM_LIB_TARGET_LX32_MCTARGETDESC_LX32MCTARGETDESC_H
diff --git a/llvm/lib/Target/LX32/TableGen/.gitignore b/llvm/lib/Target/LX32/TableGen/.gitignore
new file mode 100644
index 0000000000000..383b1a18ada1d
--- /dev/null
+++ b/llvm/lib/Target/LX32/TableGen/.gitignore
@@ -0,0 +1,2 @@
+*.inc
+
diff --git a/llvm/lib/Target/LX32/TableGen/LX32.td b/llvm/lib/Target/LX32/TableGen/LX32.td
new file mode 100644
index 0000000000000..c99fa780460e8
--- /dev/null
+++ b/llvm/lib/Target/LX32/TableGen/LX32.td
@@ -0,0 +1,93 @@
+//===-- LX32.td - LX32 Target Machine Description -------------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This is the top-level TableGen file for the LX32 target. It performs three
+// tasks:
+//
+// 1. Includes the LLVM target infrastructure (Target.td) so that base
+// classes like Target, InstrInfo, RegisterClass, etc. are available.
+//
+// 2. Includes the four LX32-specific sub-files in dependency order:
+// LX32RegisterInfo.td — registers and register classes
+// LX32InstrFormats.td — instruction encoding base classes
+// LX32InstrInfo.td — instruction definitions, patterns, pseudos
+// LX32CallingConv.td — calling convention and callee-saved sets
+// The order matters: each file may reference definitions from the files
+// listed before it.
+//
+// 3. Defines the top-level target objects (LX32InstrInfo and LX32) that
+// llvm-tblgen uses to drive code generation.
+//
+// File should remain a thin coordinator. No instruction definitions,
+// register definitions, or patterns belong here.
+//
+//===----------------------------------------------------------------------===//
+
+include "llvm/Target/Target.td"
+
+//===----------------------------------------------------------------------===//
+// Target-specific includes — order is significant
+//===----------------------------------------------------------------------===//
+
+include "LX32RegisterInfo.td"
+include "LX32InstrFormats.td"
+include "LX32InstrInfo.td"
+include "LX32CallingConv.td"
+
+// Minimal processor model so TableGen emits a non-empty CPU table and
+// `-mcpu=generic` is recognized by MCSubtargetInfo.
+def : ProcessorModel<"generic", NoSchedModel, []>;
+
+//===----------------------------------------------------------------------===//
+// Instruction set descriptor
+//
+// LX32InstrInfo wraps the instruction definitions for use by the Target
+// record below. guessInstructionProperties = 0 disables TableGen's
+// heuristic inference of mayLoad / mayStore / hasSideEffects — every
+// instruction must declare these attributes explicitly, which avoids
+// silent mis-classification and keeps the backend predictable.
+//===----------------------------------------------------------------------===//
+
+def LX32InstrInfo : InstrInfo {
+ let guessInstructionProperties = 0;
+}
+
+def LX32AsmParser : AsmParser {
+ let ShouldEmitMatchRegisterName = 1;
+}
+
+def LX32AsmWriter : AsmWriter {
+ int PassSubtarget = 1;
+}
+
+//===----------------------------------------------------------------------===//
+// Pointer-operand remapping
+//
+// RemapAllTargetPseudoPointerOperands rewrites the pointer-typed operands of
+// LLVM's built-in pseudo-instructions (LOAD_STACK_GUARD, PREALLOCATED_SETUP,
+// etc.) to use the LX32 GPR register class. Without this, those pseudos
+// carry LLVM's default "ptr" operand type, which would be 64-bit on a
+// 64-bit host and would mismatch the 32-bit lx32 register class.
+//===----------------------------------------------------------------------===//
+
+defm : RemapAllTargetPseudoPointerOperands<GPR>;
+
+//===----------------------------------------------------------------------===//
+// Target record
+//
+// LX32 ties the instruction set to the target and enables register renaming.
+// AllowRegisterRenaming = 1 permits the register allocator to rename
+// registers to reduce false dependencies — safe for an in-order machine
+// without register-file constraints.
+//===----------------------------------------------------------------------===//
+
+def LX32 : Target {
+ let InstructionSet = LX32InstrInfo;
+ let AssemblyParsers = [LX32AsmParser];
+ let AssemblyWriters = [LX32AsmWriter];
+ let AllowRegisterRenaming = 1;
+}
diff --git a/llvm/lib/Target/LX32/TableGen/LX32CallingConv.td b/llvm/lib/Target/LX32/TableGen/LX32CallingConv.td
new file mode 100644
index 0000000000000..6324704dee1c1
--- /dev/null
+++ b/llvm/lib/Target/LX32/TableGen/LX32CallingConv.td
@@ -0,0 +1,123 @@
+//===-- LX32CallingConv.td - LX32 Calling Convention ----------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines three things:
+//
+// 1. Callee-saved register sets (CSR_*) — used by the register allocator
+// and frame lowering to know which registers a function must preserve
+// across calls.
+//
+// 2. RetCC_LX32 — the return-value convention. Governs which registers
+// carry values back from callee to caller.
+//
+// 3. CC_LX32 — the argument-passing convention. Governs which registers
+// (or stack slots) carry values from caller to callee.
+//
+// The convention implemented here is ILP32: 32-bit pointers, 32-bit int,
+// 64-bit long long passed in register pairs. There is no hardware FPU,
+// so floating-point values must be converted to integer before reaching
+// this layer (handled in LX32ISelLowering).
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Callee-saved register sets
+//===----------------------------------------------------------------------===//
+
+/// CSR_LX32_ILP32 — standard ILP32 callee-saved set.
+///
+/// A function that modifies any of these registers must save the original
+/// value on entry and restore it before returning, so that the caller sees
+/// them unchanged.
+///
+/// X1 (ra) : return address — saved so nested calls can overwrite it
+/// X8 (s0/fp) : frame pointer / callee-saved s0
+/// X9 (s1) : callee-saved s1
+/// X18-X27 (s2-s11) : callee-saved s2 through s11
+def CSR_LX32_ILP32 : CalleeSavedRegs<(add X1, X8, X9, (sequence "X%u", 18, 27))>;
+
+/// CSR_NoRegs — empty set.
+/// Used for functions that are known to clobber no registers (e.g., naked
+/// functions or explicit no-callee-save calling conventions).
+def CSR_NoRegs : CalleeSavedRegs<(add)>;
+
+/// CSR_IPRA — minimal set for Inter-Procedural Register Allocation.
+/// When IPRA is active, LLVM computes per-function clobber sets and uses
+/// this as the baseline: only ra (X1) is unconditionally preserved.
+def CSR_IPRA : CalleeSavedRegs<(add X1)>;
+
+//===----------------------------------------------------------------------===//
+// Return-value convention — RetCC_LX32
+//
+// Promotion: i1/i8/i16 are widened to i32 before register assignment.
+//
+// i32 return values: up to two values fit in a0 (X10) and a1 (X11).
+// Single i32 → a0
+// Pair i32 → a0, a1 (e.g., struct { int x, y; } returned by value)
+//
+// i64 return value: the low 32 bits go in a0 (X10), the high 32 bits go in
+// a1 (X11). CCAssignToRegWithShadow<[X10], [X11]> models this pair:
+// allocating X10 also reserves X11 as its shadow, preventing any other
+// value from landing there.
+//
+// Overflow: anything that does not fit in registers goes to the stack at
+// 4-byte alignment. This case is unusual for return values (most ABIs
+// handle large returns via a hidden pointer argument), but the rule is
+// included for completeness.
+//===----------------------------------------------------------------------===//
+
+def RetCC_LX32 : CallingConv<[
+ // Widen sub-word integer types to i32.
+ CCIfType<[i1, i8, i16], CCPromoteToType<i32>>,
+
+ // i32 return values: a0, then a1.
+ CCIfType<[i32], CCAssignToReg<[X10, X11]>>,
+
+ // i64 return value: low word in a0 (X10), high word in a1 (X11).
+ CCIfType<[i64], CCAssignToRegWithShadow<[X10], [X11]>>,
+
+ // Overflow to stack: 4-byte size, 4-byte alignment.
+ CCAssignToStack<4, 4>
+]>;
+
+//===----------------------------------------------------------------------===//
+// Argument-passing convention — CC_LX32
+//
+// Promotion: i1/i8/i16 are widened to i32 before register assignment,
+// matching the ILP32 ABI requirement that sub-word arguments occupy a
+// full register.
+//
+// i32 arguments: up to eight values are passed in a0-a7 (X10-X17) in order.
+// The ninth and subsequent i32 arguments spill to the stack at 4-byte
+// alignment.
+//
+// i64 arguments: passed in aligned register pairs — only even-indexed
+// argument registers are valid low-word slots (a0, a2, a4, a6).
+// CCAssignToRegWithShadow pairs each even register with the next odd
+// register as its shadow. If a pair cannot fit (e.g., only a7 remains),
+// the i64 spills entirely to the stack at 8-byte alignment.
+//
+// Note on floats: lx32 has no FPU. Float arguments are converted to
+// integer in LX32ISelLowering before reaching this convention, so no
+// float rules are needed here.
+//===----------------------------------------------------------------------===//
+
+def CC_LX32 : CallingConv<[
+ // Widen sub-word integer types to i32.
+ CCIfType<[i1, i8, i16], CCPromoteToType<i32>>,
+
+ // i32 arguments: a0-a7 in order, then stack.
+ CCIfType<[i32], CCAssignToReg<[X10, X11, X12, X13, X14, X15, X16, X17]>>,
+
+ // i64 arguments: aligned register pairs (a0+a1, a2+a3, a4+a5, a6+a7).
+ CCIfType<[i64], CCAssignToRegWithShadow<[X10, X12, X14, X16],
+ [X11, X13, X15, X17]>>,
+
+ // Stack overflow rules: i32 at 4-byte, i64 at 8-byte alignment.
+ CCIfType<[i32], CCAssignToStack<4, 4>>,
+ CCIfType<[i64], CCAssignToStack<8, 8>>
+]>;
diff --git a/llvm/lib/Target/LX32/TableGen/LX32InstrFormats.td b/llvm/lib/Target/LX32/TableGen/LX32InstrFormats.td
new file mode 100644
index 0000000000000..8f0d19161d146
--- /dev/null
+++ b/llvm/lib/Target/LX32/TableGen/LX32InstrFormats.td
@@ -0,0 +1,301 @@
+//===-- LX32InstrFormats.td - LX32 Instruction Format Definitions ---------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the encoding-level building blocks for all LX32
+// instruction formats. Every class here models exactly one concern:
+//
+// Field mixins — carry a single named bit-field (rd, rs1, rs2, funct3,
+// and the immediate variants).
+// LX32Opcode — pairs a 7-bit opcode value with a human-readable name.
+// LXInstCommon — the root Instruction subclass shared by all formats.
+// LXInst — places the 7-bit opcode in Inst{6-0}.
+// LXInstR/I/S/B/U/J — one class per ISA format; each sets the remaining
+// bit fields according to the LX32 encoding specification.
+// RegisterOperand helpers — typed wrappers around GPR/GPRNOX0 for use in
+// instruction operand lists.
+//
+// Nothing in this file sets semantic attributes (mayLoad, mayStore,
+// isBranch, etc.). Those belong in LX32InstrInfo.td where the actual
+// instruction definitions live.
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Minimal field mixins — single source of truth for bit-field names
+//===----------------------------------------------------------------------===//
+
+/// Destination register field (bits [11:7] in all formats that have one).
+class LXInstRD { bits<5> rd; }
+
+/// Source register 1 field (bits [19:15]).
+class LXInstRS1 { bits<5> rs1; }
+
+/// Source register 2 field (bits [24:20]).
+class LXInstRS2 { bits<5> rs2; }
+
+/// Convenience mixin: both source registers.
+class LXInstRS1_RS2 : LXInstRS1, LXInstRS2 {}
+
+/// funct3 discriminator field (bits [14:12]).
+class LXInstFunct3<bits<3> funct3_val> {
+ bits<3> funct3 = funct3_val;
+}
+
+//===----------------------------------------------------------------------===//
+// Immediate field mixins
+//===----------------------------------------------------------------------===//
+
+/// 12-bit sign-extended immediate. Used by I-type and S-type.
+class LXImm12 { bits<12> imm12; }
+
+/// 13-bit sign-extended branch offset.
+/// Models bits [12:1] of the byte offset — bit 0 is always zero and is
+/// not stored. The B-type encoder scrambles these bits across the word;
+/// see LXInstBBase below for the exact mapping.
+class LXImm13 { bits<13> imm13; }
+
+/// 20-bit immediate. Used by U-type (LUI / AUIPC) placed in Inst[31:12].
+class LXImm20 { bits<20> imm20; }
+
+/// 21-bit sign-extended jump offset.
+/// Models bits [20:1] of the byte offset — bit 0 is always zero and is
+/// not stored. The J-type encoder scrambles these bits; see LXInstJBase.
+class LXImm21 { bits<21> imm21; }
+
+//===----------------------------------------------------------------------===//
+// Opcode descriptor
+//
+// LX32Opcode pairs the 7-bit opcode value with a name string used in
+// diagnostics. One instance per opcode group (e.g., OPC_ARITH = 0110011).
+//===----------------------------------------------------------------------===//
+
+class LX32Opcode<string name, bits<7> val> {
+ string Name = name;
+ bits<7> Val = val;
+}
+
+//===----------------------------------------------------------------------===//
+// Instruction base classes
+//===----------------------------------------------------------------------===//
+
+/// LXInstCommon — root for all LX32 instructions.
+///
+/// Defines the 32-bit Inst field, wires OutOperandList / InOperandList,
+/// and assembles the AsmString from the opcode mnemonic and argument string.
+/// The AsmString logic: if argstr is non-empty the result is
+/// "opcodestr\targstr"; if argstr is empty the result is just "opcodestr"
+/// (used for pseudo-instructions like "ret" and "nop").
+class LXInstCommon<dag outs, dag ins, string opcodestr, string argstr>
+ : Instruction {
+ let Namespace = "LX32";
+ field bits<32> Inst;
+ let Size = 4;
+ dag OutOperandList = outs;
+ dag InOperandList = ins;
+ let AsmString = !if(!empty(argstr),
+ opcodestr,
+ opcodestr # "\t" # argstr);
+}
+
+/// LXInst — places the 7-bit opcode in Inst{6-0} and inherits LXInstCommon.
+/// All format-specific classes derive from this.
+class LXInst<LX32Opcode opcode, dag outs, dag ins,
+ string opcodestr, string argstr>
+ : LXInstCommon<outs, ins, opcodestr, argstr> {
+ let Inst{6-0} = opcode.Val;
+}
+
+//===----------------------------------------------------------------------===//
+// RegisterOperand helpers
+//
+// Typed wrappers used in instruction operand lists. Using RegisterOperand
+// (rather than raw RegisterClass) lets the assembler parser attach the
+// correct register class to each operand position without guessing.
+//===----------------------------------------------------------------------===//
+
+def LX32GPROut : RegisterOperand<GPR>;
+def LX32GPRIn : RegisterOperand<GPR>;
+def LX32GPRNOX0Out : RegisterOperand<GPRNOX0>;
+
+//===----------------------------------------------------------------------===//
+// R-Type format — register-register operations
+//
+// 31 25 24 20 19 15 14 12 11 7 6 0
+// [ funct7 ] [ rs2 ] [ rs1 ] [fn3] [ rd ] [opcode]
+//===----------------------------------------------------------------------===//
+
+/// LXInstRBase — R-type without the funct7 field.
+/// Used as an intermediate when funct7 is supplied by a derived class.
+class LXInstRBase<bits<3> funct3, LX32Opcode opcode,
+ dag outs, dag ins, string opcodestr, string argstr>
+ : LXInst<opcode, outs, ins, opcodestr, argstr>,
+ LXInstRD, LXInstRS1_RS2, LXInstFunct3<funct3> {
+ let Inst{11-7} = rd;
+ let Inst{19-15} = rs1;
+ let Inst{24-20} = rs2;
+ let Inst{14-12} = funct3;
+}
+
+/// LXInstR — full R-type including funct7.
+class LXInstR<bits<7> funct7, bits<3> funct3, LX32Opcode opcode,
+ dag outs, dag ins, string opcodestr, string argstr>
+ : LXInstRBase<funct3, opcode, outs, ins, opcodestr, argstr> {
+ let Inst{31-25} = funct7;
+}
+
+//===----------------------------------------------------------------------===//
+// I-Type format — immediate arithmetic, loads, JALR
+//
+// 31 20 19 15 14 12 11 7 6 0
+// [ imm12 ] [ rs1 ] [fn3] [ rd ] [opcode]
+//===----------------------------------------------------------------------===//
+
+/// LXInstIBase — I-type without semantic attributes.
+class LXInstIBase<bits<3> funct3, LX32Opcode opcode,
+ dag outs, dag ins, string opcodestr, string argstr>
+ : LXInst<opcode, outs, ins, opcodestr, argstr>,
+ LXInstRD, LXInstRS1, LXInstFunct3<funct3>, LXImm12 {
+ let Inst{11-7} = rd;
+ let Inst{19-15} = rs1;
+ let Inst{14-12} = funct3;
+ let Inst{31-20} = imm12;
+}
+
+/// LXInstI — I-type for immediate arithmetic and JALR.
+/// Semantic attributes (mayLoad, hasSideEffects, etc.) are set in
+/// LX32InstrInfo.td via let-blocks at the instruction-definition site.
+class LXInstI<bits<3> funct3, LX32Opcode opcode,
+ dag outs, dag ins, string opcodestr, string argstr>
+ : LXInstIBase<funct3, opcode, outs, ins, opcodestr, argstr> {}
+
+//===----------------------------------------------------------------------===//
+// S-Type format — stores
+//
+// 31 25 24 20 19 15 14 12 11 7 6 0
+// [imm12[11:5]] [rs2] [ rs1 ] [fn3] [imm12[4:0]] [opcode]
+//===----------------------------------------------------------------------===//
+
+/// LXInstMemBase — S-type field layout.
+/// imm12 is split: the upper 7 bits go to Inst{31:25}, the lower 5 bits to
+/// Inst{11:7}. No rd field in stores.
+class LXInstMemBase<bits<3> funct3, LX32Opcode opcode,
+ dag outs, dag ins, string opcodestr, string argstr>
+ : LXInst<opcode, outs, ins, opcodestr, argstr>,
+ LXInstRS1_RS2, LXInstFunct3<funct3>, LXImm12 {
+ let Inst{19-15} = rs1;
+ let Inst{24-20} = rs2;
+ let Inst{14-12} = funct3;
+ let Inst{31-25} = imm12{11-5};
+ let Inst{11-7} = imm12{4-0};
+}
+
+/// LXInstS — S-type for store instructions.
+class LXInstS<bits<3> funct3, LX32Opcode opcode,
+ dag outs, dag ins, string opcodestr, string argstr>
+ : LXInstMemBase<funct3, opcode, outs, ins, opcodestr, argstr> {
+ let mayStore = 1;
+ let hasSideEffects = 1;
+}
+
+//===----------------------------------------------------------------------===//
+// B-Type format — conditional branches
+//
+// 31 25 24 20 19 15 14 12 11 7 6 0
+// [12|10:5] [ rs2 ] [ rs1 ] [fn3] [4:1|11] [opcode]
+//
+// imm13 represents bits [12:1] of the signed byte offset.
+// Bit 0 is always zero (instructions are 4-byte aligned) and is not encoded.
+// The bit scrambling here matches the LX32 B-type specification:
+// Inst[31] ← imm13[12] (sign bit)
+// Inst[30:25] ← imm13[10:5]
+// Inst[11:8] ← imm13[4:1]
+// Inst[7] ← imm13[11]
+//===----------------------------------------------------------------------===//
+
+/// LXInstBBase — B-type field layout and bit scrambling.
+class LXInstBBase<bits<3> funct3, LX32Opcode opcode,
+ dag outs, dag ins, string opcodestr, string argstr>
+ : LXInst<opcode, outs, ins, opcodestr, argstr>,
+ LXInstRS1_RS2, LXInstFunct3<funct3>, LXImm13 {
+ let Inst{19-15} = rs1;
+ let Inst{24-20} = rs2;
+ let Inst{14-12} = funct3;
+ let Inst{31} = imm13{12};
+ let Inst{30-25} = imm13{10-5};
+ let Inst{11-8} = imm13{4-1};
+ let Inst{7} = imm13{11};
+}
+
+/// LXInstB — B-type for conditional branches.
+/// isBranch, isTerminator, and hasSideEffects are set here because they
+/// apply uniformly to every B-type instruction.
+class LXInstB<bits<3> funct3, LX32Opcode opcode,
+ dag outs, dag ins, string opcodestr, string argstr>
+ : LXInstBBase<funct3, opcode, outs, ins, opcodestr, argstr> {
+ let isBranch = 1;
+ let isTerminator = 1;
+ let hasSideEffects = 1;
+}
+
+//===----------------------------------------------------------------------===//
+// U-Type format — upper immediate (LUI / AUIPC)
+//
+// 31 12 11 7 6 0
+// [ imm20 ] [ rd ] [opcode]
+//===----------------------------------------------------------------------===//
+
+/// LXInstUBase — U-type field layout.
+class LXInstUBase<LX32Opcode opcode,
+ dag outs, dag ins, string opcodestr, string argstr>
+ : LXInst<opcode, outs, ins, opcodestr, argstr>, LXInstRD, LXImm20 {
+ let Inst{11-7} = rd;
+ let Inst{31-12} = imm20;
+}
+
+/// LXInstU — U-type. Semantic attributes set per-instruction in
+/// LX32InstrInfo.td.
+class LXInstU<LX32Opcode opcode,
+ dag outs, dag ins, string opcodestr, string argstr>
+ : LXInstUBase<opcode, outs, ins, opcodestr, argstr> {}
+
+//===----------------------------------------------------------------------===//
+// J-Type format — unconditional jump-and-link (JAL)
+//
+// 31 12 11 7 6 0
+// [imm21 scrambled] [ rd ] [opcode]
+//
+// imm21 represents bits [20:1] of the signed byte offset.
+// Bit 0 is always zero and is not encoded.
+// The bit scrambling matches the LX32 J-type specification:
+// Inst[31] ← imm21[20] (sign bit)
+// Inst[30:21] ← imm21[10:1]
+// Inst[20] ← imm21[11]
+// Inst[19:12] ← imm21[19:12]
+//===----------------------------------------------------------------------===//
+
+/// LXInstJBase — J-type field layout and bit scrambling.
+class LXInstJBase<LX32Opcode opcode,
+ dag outs, dag ins, string opcodestr, string argstr>
+ : LXInst<opcode, outs, ins, opcodestr, argstr>, LXInstRD, LXImm21 {
+ let Inst{11-7} = rd;
+ let Inst{31} = imm21{20};
+ let Inst{30-21} = imm21{10-1};
+ let Inst{20} = imm21{11};
+ let Inst{19-12} = imm21{19-12};
+}
+
+/// LXInstJ — J-type for JAL.
+/// isBranch, isTerminator, isBarrier, and hasSideEffects are set here
+/// because they apply to every J-type instruction.
+class LXInstJ<LX32Opcode opcode,
+ dag outs, dag ins, string opcodestr, string argstr>
+ : LXInstJBase<opcode, outs, ins, opcodestr, argstr> {
+ let isBranch = 1;
+ let isTerminator = 1;
+ let isBarrier = 1;
+ let hasSideEffects = 1;
+}
diff --git a/llvm/lib/Target/LX32/TableGen/LX32InstrInfo.td b/llvm/lib/Target/LX32/TableGen/LX32InstrInfo.td
new file mode 100644
index 0000000000000..542471acbf86b
--- /dev/null
+++ b/llvm/lib/Target/LX32/TableGen/LX32InstrInfo.td
@@ -0,0 +1,562 @@
+//===-- LX32InstrInfo.td - LX32 Instruction Definitions -------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the complete LX32 instruction set at the TableGen level.
+// It is organized into the following sections:
+//
+// Section 0 — Custom SDNodes and pattern fragments
+// Section 0.5 — Operand transformers (HI20 / LO12)
+// Section 1 — Operand type definitions
+// Section 2 — Opcode map
+// Section 3 — R-type instructions (register-register ALU)
+// Section 4 — I-type instructions (immediate ALU)
+// Section 5 — Load instructions
+// Section 6 — Store instructions
+// Section 7 — Branch instructions (B-type)
+// Section 8 — Jump instructions (JAL / JALR)
+// Section 9 — Upper-immediate instructions (LUI / AUIPC)
+// Section 10 — Constant-materialization patterns
+// Section 11 — Pseudo-instructions
+// Section 12 — Codegen-only pseudo overrides (stack adjustment, guards)
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Section 0 — Custom SDNodes and Pattern Fragments
+//===----------------------------------------------------------------------===//
+
+// LX32ret — custom DAG node for the LX32 return sequence.
+//
+// SDTypeProfile<0, 0, []> means zero results and zero fixed operands.
+// The SDNPHasChain flag makes the node part of the chain so it is always
+// scheduled last. SDNPOptInGlue allows an optional glue edge from the
+// CopyToReg nodes that load the return value into a0/a1. SDNPVariadic
+// permits the glue chain to hold an arbitrary number of glued nodes.
+def SDT_LX32Ret : SDTypeProfile<0, 0, []>;
+def LX32ret : SDNode<"LX32ISD::RET", SDT_LX32Ret,
+ [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
+
+// Load pattern fragments — model sign- and zero-extending sub-word loads.
+// These wrap the generic LLVM load nodes so that the instruction patterns
+// in Section 5 can match them with a single, readable name.
+def lx32_lh : PatFrag<(ops node:$ptr), (i32 (sextloadi16 node:$ptr))>;
+def lx32_lhu : PatFrag<(ops node:$ptr), (i32 (zextloadi16 node:$ptr))>;
+def lx32_lb : PatFrag<(ops node:$ptr), (i32 (sextloadi8 node:$ptr))>;
+def lx32_lbu : PatFrag<(ops node:$ptr), (i32 (zextloadi8 node:$ptr))>;
+
+//===----------------------------------------------------------------------===//
+// Section 0.5 — Operand Transformers
+//
+// HI20 and LO12 split a 32-bit constant into the two pieces needed for
+// the LUI + ADDI materialization sequence:
+//
+// LUI rd, ((imm + 0x800) >> 12) & 0xFFFFF ← upper 20 bits
+// ADDI rd, rd, sign_extend_12(imm & 0xFFF) ← lower 12 bits
+//
+// The +0x800 bias in HI20 compensates for the sign extension that ADDI
+// applies to its 12-bit immediate: if bit 11 of the low part is 1, ADDI
+// sign-extends it to a negative value, so LUI must load one higher.
+//
+// LO12 sign-extends the 12-bit result so that ADDI receives a value in
+// the range [-2048, 2047]. Using getZExtValue() & 0xFFF without sign
+// extension would produce values up to 4095, which do not fit in simm12
+// and would silently truncate or mis-encode.
+//===----------------------------------------------------------------------===//
+
+def HI20 : SDNodeXForm<imm, [{
+ return CurDAG->getTargetConstant(
+ ((uint64_t)N->getZExtValue() + 0x800) >> 12, SDLoc(N), MVT::i32);
+}]>;
+
+def LO12 : SDNodeXForm<imm, [{
+ // Sign-extend the low 12 bits so that the result is a valid simm12.
+ return CurDAG->getTargetConstant(
+ SignExtend64<12>(N->getZExtValue() & 0xfff), SDLoc(N), MVT::i32);
+}]>;
+
+//===----------------------------------------------------------------------===//
+// Section 1 — Operand Definitions
+//
+// bare_symbol — a symbolic address operand with no relocation modifier.
+// Used by pseudo-instructions such as PseudoLA and PseudoCALL that take
+// a label or function name as an operand.
+//
+// simm12 / uimm5 / uimm12 / uimm20 — typed immediates.
+// Each specifies an EncoderMethod so that the MCCodeEmitter knows which
+// C++ function to call when encoding the operand into a binary word.
+// "getImmOpValue" is the generic encoder that simply writes the immediate
+// bits directly — suitable for any immediate that requires no bit
+// shuffling beyond what the format class already handles.
+//
+// simm13 / simm21 — branch and jump target operands.
+// These use specialized encoder methods because the branch (B-type) and
+// jump (J-type) formats scatter the immediate bits across the instruction
+// word in a non-contiguous order. The encoder methods handle relocations
+// (symbols resolved at link time) by emitting fixup records.
+//
+// imm12 / imm5 — PatLeaf predicates.
+// Used in instruction patterns (not in operand lists) to restrict which
+// constant values a pattern may match. TableGen would crash if a raw
+// Operand were used directly in a pattern — PatLeaf is the correct idiom.
+//===----------------------------------------------------------------------===//
+
+def bare_symbol : Operand<i32>;
+def simm12 : Operand<i32> { let EncoderMethod = "getImmOpValue"; }
+def uimm5 : Operand<i32> { let EncoderMethod = "getImmOpValue"; }
+def uimm12 : Operand<i32> { let EncoderMethod = "getImmOpValue"; }
+def uimm20 : Operand<i32> { let EncoderMethod = "getImmOpValue"; }
+def simm13 : Operand<OtherVT> { let EncoderMethod = "getBranchTargetOpValue"; }
+def simm21 : Operand<OtherVT> { let EncoderMethod = "getJumpTargetOpValue"; }
+
+// Pattern predicates — restrict immediate matching without using Operand
+// directly in a pattern dag, which would trigger a TableGen assertion.
+def imm12 : PatLeaf<(i32 imm), [{ return isInt<12>(N->getSExtValue()); }]>;
+def imm5 : PatLeaf<(i32 imm), [{ return isUInt<5>(N->getZExtValue()); }]>;
+
+//===----------------------------------------------------------------------===//
+// Section 2 — Opcode Map
+//
+// One LX32Opcode instance per 7-bit opcode group. The Name field is used
+// only in diagnostics; the Val field is what gets placed in Inst[6:0].
+//===----------------------------------------------------------------------===//
+
+def OPC_LOAD : LX32Opcode<"load", 0b0000011>;
+def OPC_STORE : LX32Opcode<"store", 0b0100011>;
+def OPC_BRANCH : LX32Opcode<"branch", 0b1100011>;
+def OPC_JAL : LX32Opcode<"jal", 0b1101111>;
+def OPC_JALR : LX32Opcode<"jalr", 0b1100111>;
+def OPC_LUI : LX32Opcode<"lui", 0b0110111>;
+def OPC_AUIPC : LX32Opcode<"auipc", 0b0010111>;
+def OPC_IMM : LX32Opcode<"imm", 0b0010011>;
+def OPC_ARITH : LX32Opcode<"arith", 0b0110011>;
+def OPC_SYSTEM : LX32Opcode<"system", 0b1110011>;
+
+//===----------------------------------------------------------------------===//
+// Section 3 — R-Type Instructions (register-register ALU)
+//
+// All R-type instructions share:
+// hasSideEffects = 0 — pure computation, no observable side effects
+// mayLoad = 0 — no memory reads
+// mayStore = 0 — no memory writes
+//
+// The class ALU_R_Pat bundles the LXInstR encoding with a pattern list so
+// that each instruction can declare its DAG pattern inline.
+//===----------------------------------------------------------------------===//
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
+class ALU_R_Pat<bits<7> f7, bits<3> f3, string op, list<dag> pat>
+ : LXInstR<f7, f3, OPC_ARITH,
+ (outs GPR:$rd), (ins GPR:$rs1, GPR:$rs2),
+ op, "$rd, $rs1, $rs2"> {
+ let Pattern = pat;
+}
+
+def ADD : ALU_R_Pat<0b0000000, 0b000, "add", [(set GPR:$rd, (add GPR:$rs1, GPR:$rs2))]>;
+def SUB : ALU_R_Pat<0b0100000, 0b000, "sub", [(set GPR:$rd, (sub GPR:$rs1, GPR:$rs2))]>;
+def SLL : ALU_R_Pat<0b0000000, 0b001, "sll", [(set GPR:$rd, (shl GPR:$rs1, GPR:$rs2))]>;
+def SLT : ALU_R_Pat<0b0000000, 0b010, "slt", [(set GPR:$rd, (setlt GPR:$rs1, GPR:$rs2))]>;
+def SLTU : ALU_R_Pat<0b0000000, 0b011, "sltu", [(set GPR:$rd, (setult GPR:$rs1, GPR:$rs2))]>;
+def XOR : ALU_R_Pat<0b0000000, 0b100, "xor", [(set GPR:$rd, (xor GPR:$rs1, GPR:$rs2))]>;
+def SRL : ALU_R_Pat<0b0000000, 0b101, "srl", [(set GPR:$rd, (srl GPR:$rs1, GPR:$rs2))]>;
+def SRA : ALU_R_Pat<0b0100000, 0b101, "sra", [(set GPR:$rd, (sra GPR:$rs1, GPR:$rs2))]>;
+def OR : ALU_R_Pat<0b0000000, 0b110, "or", [(set GPR:$rd, (or GPR:$rs1, GPR:$rs2))]>;
+def AND : ALU_R_Pat<0b0000000, 0b111, "and", [(set GPR:$rd, (and GPR:$rs1, GPR:$rs2))]>;
+} // hasSideEffects = 0, mayLoad = 0, mayStore = 0
+
+//===----------------------------------------------------------------------===//
+// Section 4 — I-Type Instructions (immediate ALU)
+//
+// ALU_I_Pat uses simm12 as the operand type (for the assembler and encoder)
+// and imm12 as the PatLeaf in the pattern (for the instruction selector).
+// The distinction is necessary: simm12 is an Operand (used at MC level),
+// while imm12 is a PatLeaf (used at SelectionDAG level). Mixing them would
+// cause a TableGen assertion.
+//
+// Shifts (SLLI, SRLI, SRAI) use uimm5 as the operand type because only bits
+// [4:0] are meaningful; bits [11:5] of the immediate field carry the funct7
+// discriminator (0b0000000 for logical shifts, 0b0100000 for arithmetic).
+// Those bits are set statically via "let imm12{11-5} = ..." at definition
+// time so the encoder always produces the correct word.
+//===----------------------------------------------------------------------===//
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
+class ALU_I_Pat<bits<3> f3, string op, list<dag> pat>
+ : LXInstI<f3, OPC_IMM,
+ (outs GPR:$rd), (ins GPR:$rs1, simm12:$imm12),
+ op, "$rd, $rs1, $imm12"> {
+ let Pattern = pat;
+}
+
+def ADDI : ALU_I_Pat<0b000, "addi", [(set GPR:$rd, (add GPR:$rs1, imm12:$imm12))]>;
+def SLTI : ALU_I_Pat<0b010, "slti", [(set GPR:$rd, (setlt GPR:$rs1, imm12:$imm12))]>;
+def SLTIU : ALU_I_Pat<0b011, "sltiu", [(set GPR:$rd, (setult GPR:$rs1, imm12:$imm12))]>;
+def XORI : ALU_I_Pat<0b100, "xori", [(set GPR:$rd, (xor GPR:$rs1, imm12:$imm12))]>;
+def ORI : ALU_I_Pat<0b110, "ori", [(set GPR:$rd, (or GPR:$rs1, imm12:$imm12))]>;
+def ANDI : ALU_I_Pat<0b111, "andi", [(set GPR:$rd, (and GPR:$rs1, imm12:$imm12))]>;
+
+// Shift-immediate instructions.
+// uimm5 is used for the shift amount operand (only 5 bits are meaningful).
+// imm12{11-5} is set statically to encode the funct7 discriminator:
+// SLLI / SRLI: 0b0000000 (logical shift)
+// SRAI: 0b0100000 (arithmetic right shift)
+// imm5 in the pattern (PatLeaf) restricts matching to constants in [0, 31].
+def SLLI : LXInstI<0b001, OPC_IMM,
+ (outs GPR:$rd), (ins GPR:$rs1, uimm5:$imm12),
+ "slli", "$rd, $rs1, $imm12"> {
+ let Pattern = [(set GPR:$rd, (shl GPR:$rs1, imm5:$imm12))];
+ let imm12{11-5} = 0b0000000;
+}
+def SRLI : LXInstI<0b101, OPC_IMM,
+ (outs GPR:$rd), (ins GPR:$rs1, uimm5:$imm12),
+ "srli", "$rd, $rs1, $imm12"> {
+ let Pattern = [(set GPR:$rd, (srl GPR:$rs1, imm5:$imm12))];
+ let imm12{11-5} = 0b0000000;
+}
+def SRAI : LXInstI<0b101, OPC_IMM,
+ (outs GPR:$rd), (ins GPR:$rs1, uimm5:$imm12),
+ "srai", "$rd, $rs1, $imm12"> {
+ let Pattern = [(set GPR:$rd, (sra GPR:$rs1, imm5:$imm12))];
+ let imm12{11-5} = 0b0100000;
+}
+} // hasSideEffects = 0, mayLoad = 0, mayStore = 0
+
+//===----------------------------------------------------------------------===//
+// Section 5 — Load Instructions
+//
+// All loads use the I-type encoding: rd = MEM[rs1 + simm12].
+// The assembly syntax is "rd, offset(base)" rather than "rd, rs1, imm"
+// to match conventional assembler notation.
+//
+// Pattern fragments (lx32_lh, lx32_lhu, lx32_lb, lx32_lbu) are defined in
+// Section 0 and wrap the LLVM sign/zero-extending load nodes so that each
+// instruction below can match the correct load variant with a single name.
+//===----------------------------------------------------------------------===//
+
+let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in {
+class Load_Pat<bits<3> f3, string op, list<dag> pat>
+ : LXInstI<f3, OPC_LOAD,
+ (outs GPR:$rd), (ins GPR:$rs1, simm12:$imm12),
+ op, "$rd, ${imm12}(${rs1})"> {
+ let Pattern = pat;
+}
+
+// LW — load 32-bit word (sign-extended to 32 bits, though LX32 is already
+// 32-bit so no extension occurs).
+def LW : Load_Pat<0b010, "lw", [(set GPR:$rd, (load (add GPR:$rs1, imm12:$imm12)))]>;
+
+// LH — load 16-bit halfword, sign-extended to 32 bits.
+def LH : Load_Pat<0b001, "lh", [(set GPR:$rd, (lx32_lh (add GPR:$rs1, imm12:$imm12)))]>;
+
+// LHU — load 16-bit halfword, zero-extended to 32 bits.
+def LHU : Load_Pat<0b101, "lhu", [(set GPR:$rd, (lx32_lhu (add GPR:$rs1, imm12:$imm12)))]>;
+
+// LB — load 8-bit byte, sign-extended to 32 bits.
+def LB : Load_Pat<0b000, "lb", [(set GPR:$rd, (lx32_lb (add GPR:$rs1, imm12:$imm12)))]>;
+
+// LBU — load 8-bit byte, zero-extended to 32 bits.
+def LBU : Load_Pat<0b100, "lbu", [(set GPR:$rd, (lx32_lbu (add GPR:$rs1, imm12:$imm12)))]>;
+} // hasSideEffects = 0, mayLoad = 1, mayStore = 0
+
+//===----------------------------------------------------------------------===//
+// Section 6 — Store Instructions
+//
+// Stores use the S-type encoding: MEM[rs1 + simm12] = rs2.
+// The assembly syntax is "rs2, offset(base)".
+//
+// Note on truncating stores:
+// SW stores all 32 bits — pattern uses the generic (store ...) node.
+// SH stores the low 16 bits — pattern uses (truncstorei16 ...) directly.
+// SB stores the low 8 bits — pattern uses (truncstorei8 ...) directly.
+//
+// The original implementation used PatFrag helpers (lx32_sh / lx32_sb)
+// to wrap truncstorei16/truncstorei8. Those fragments are incorrect
+// because truncstorei* are chain nodes and PatFrag does not propagate
+// chain/memory flags — the selector would not recognize the pattern or
+// would generate incorrect code. Using the LLVM DAG nodes directly in
+// the pattern is the right approach, as shown for SW and used here.
+//===----------------------------------------------------------------------===//
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in {
+class Store_Pat<bits<3> f3, string op, list<dag> pat>
+ : LXInstS<f3, OPC_STORE,
+ (outs), (ins GPR:$rs2, GPR:$rs1, simm12:$imm12),
+ op, "$rs2, ${imm12}(${rs1})"> {
+ let Pattern = pat;
+}
+
+// SW — store 32-bit word.
+def SW : Store_Pat<0b010, "sw",
+ [(store GPR:$rs2, (add GPR:$rs1, imm12:$imm12))]>;
+
+// SH — store low 16 bits (truncating store).
+def SH : Store_Pat<0b001, "sh",
+ [(truncstorei16 GPR:$rs2, (add GPR:$rs1, imm12:$imm12))]>;
+
+// SB — store low 8 bits (truncating store).
+def SB : Store_Pat<0b000, "sb",
+ [(truncstorei8 GPR:$rs2, (add GPR:$rs1, imm12:$imm12))]>;
+} // hasSideEffects = 0, mayLoad = 0, mayStore = 1
+
+// Additional addressing patterns for pointer-dereference IR where the address
+// node is already a plain base register and no explicit add-immediate appears.
+def : Pat<(load GPR:$rs1), (LW GPR:$rs1, 0)>;
+def : Pat<(store GPR:$rs2, GPR:$rs1), (SW GPR:$rs2, GPR:$rs1, 0)>;
+
+//===----------------------------------------------------------------------===//
+// Section 7 — Branch Instructions (B-type)
+//
+// Branches compare two registers and jump to a PC-relative address if the
+// condition holds. They have no result register.
+//
+// The operand simm13 carries the 13-bit signed offset (bits [12:1] of the
+// byte offset; bit 0 is implicit zero). The EncoderMethod
+// "getBranchTargetOpValue" handles both literal offsets and symbolic labels
+// (the latter emit a fixup record for the linker).
+//
+// No DAG patterns are defined here. Branches are lowered to machine
+// instructions by LX32ISelLowering::LowerBR_CC (Day 9). Defining patterns
+// directly for branches is fragile because the DAG conditional branch node
+// (ISD::BRCOND) requires a separate condition-code lowering step.
+//===----------------------------------------------------------------------===//
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0,
+ isTerminator = 1, isBranch = 1 in {
+class Branch_B<bits<3> funct3, string opcodestr>
+ : LXInstB<funct3, OPC_BRANCH,
+ (outs), (ins GPR:$rs1, GPR:$rs2, simm13:$imm13),
+ opcodestr, "$rs1, $rs2, $imm13">;
+
+def BEQ : Branch_B<0b000, "beq">;
+def BNE : Branch_B<0b001, "bne">;
+def BLT : Branch_B<0b100, "blt">;
+def BGE : Branch_B<0b101, "bge">;
+def BLTU : Branch_B<0b110, "bltu">;
+def BGEU : Branch_B<0b111, "bgeu">;
+} // hasSideEffects = 0, mayLoad = 0, mayStore = 0
+
+//===----------------------------------------------------------------------===//
+// Section 8 — Jump Instructions (JAL / JALR)
+//
+// JAL (J-type) — unconditional jump with link.
+// Writes PC+4 into rd, then jumps to PC + sign_extend(imm21).
+// rd = X0 is used for unconditional jumps without link ("j" pseudo).
+// rd = X1 (ra) is used for calls ("jal ra, target").
+// isCall is NOT set here because JAL with rd=X0 is not a call.
+// PseudoCALL (Section 11) handles the call case explicitly and carries
+// Defs = [X1] so the register allocator knows ra is clobbered.
+//
+// JALR (I-type) — indirect jump with link.
+// Writes PC+4 into rd, then jumps to (rs1 + sign_extend(imm12)) & ~1.
+// Used to implement function returns (JALR x0, ra, 0 = "ret") and
+// indirect calls. Like JAL, isCall and Defs are not set here; callers
+// use PseudoRET or PseudoCALL instead of emitting JALR directly.
+//
+// TODO (Day 7): if JALR is ever emitted directly as a call (e.g., for
+// indirect calls through a function pointer), add a variant with
+// isCall = 1 and Defs = [X1] so the RA treats ra as clobbered.
+//===----------------------------------------------------------------------===//
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0,
+ isTerminator = 1, isBarrier = 1 in {
+ def JAL : LXInstJ<OPC_JAL,
+ (outs GPR:$rd), (ins simm21:$imm21),
+ "jal", "$rd, $imm21">;
+ def JALR : LXInstI<0b000, OPC_JALR,
+ (outs GPR:$rd), (ins GPR:$rs1, simm12:$imm12),
+ "jalr", "$rd, ${imm12}(${rs1})">;
+}
+
+//===----------------------------------------------------------------------===//
+// Section 9 — Upper-Immediate Instructions (LUI / AUIPC)
+//
+// LUI — loads a 20-bit immediate into bits [31:12] of rd; bits [11:0] = 0.
+// Used as the first instruction of the two-instruction constant
+// materialization sequence (LUI + ADDI) for 32-bit constants.
+//
+// AUIPC — adds a 20-bit immediate (shifted left 12) to the current PC and
+// places the result in rd. Used for PC-relative addressing of global
+// symbols: AUIPC rd, hi20(sym); LW rd, lo12(sym)(rd).
+//
+// Both use uimm20 as the operand type (20 unsigned bits, range [0, 0xFFFFF]).
+//===----------------------------------------------------------------------===//
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
+ def LUI : LXInstU<OPC_LUI, (outs GPR:$rd), (ins uimm20:$imm20),
+ "lui", "$rd, $imm20">;
+ def AUIPC : LXInstU<OPC_AUIPC, (outs GPR:$rd), (ins uimm20:$imm20),
+ "auipc", "$rd, $imm20">;
+}
+
+//===----------------------------------------------------------------------===//
+// Section 10 — Constant-Materialization Patterns
+//
+// Two patterns cover all 32-bit integer constants:
+//
+// Small constants (fits in simm12): ADDI rd, x0, imm
+// The imm12 PatLeaf (Section 1) restricts this pattern to values in
+// the range [-2048, 2047].
+//
+// Large constants (does not fit in simm12): LUI rd, hi20; ADDI rd, rd, lo12
+// HI20 and LO12 (Section 0.5) compute the two pieces. LO12 produces a
+// sign-extended 12-bit value so that ADDI receives a valid simm12.
+//
+// TODO (Day 10): when LO12 == 0, this pattern emits "ADDI rd, rd, 0" which
+// is a no-op. The ISelDAGToDAG pass (Day 10) will suppress that case in
+// C++ to avoid the redundant instruction.
+//===----------------------------------------------------------------------===//
+
+// Small constant: fits directly in the ADDI immediate field.
+def : Pat<(i32 imm12:$imm), (ADDI X0, imm12:$imm)>;
+
+// Large constant: upper 20 bits via LUI, lower 12 bits via ADDI.
+def : Pat<(i32 imm:$imm), (ADDI (LUI (HI20 imm:$imm)), (LO12 imm:$imm))>;
+
+//===----------------------------------------------------------------------===//
+// Section 11 — Pseudo-Instructions
+//
+// Pseudo-instructions are emitted by the backend but have no direct hardware
+// encoding. They are expanded to real instructions either in the AsmPrinter
+// (for assembler-level pseudos like "ret") or in LX32InstrInfo::
+// expandPostRAPseudo (for codegen pseudos like PseudoRET, Day 7).
+//
+// LX32Pseudo base class: sets isPseudo = 1 and accepts a pattern list so
+// that individual pseudos can declare DAG patterns where appropriate.
+//
+// PseudoRET — models a function return. Expands to "JALR x0, ra, 0".
+// Pattern [(LX32ret)] causes the instruction selector to emit PseudoRET
+// whenever it encounters the LX32ISD::RET node from ISelLowering (Day 9).
+// Uses = [X1] informs the RA that the return address register is consumed.
+//
+// PseudoCALL — models a direct call to a register-held address.
+// No pattern: the call sequence is built explicitly in LX32ISelLowering::
+// LowerCall (Day 9), which emits this pseudo with the callee address.
+// isCall = 1 triggers call-site handling in the frame lowering.
+// Defs = [X1] tells the RA that ra (X1) is clobbered by the call.
+// hasSideEffects = 1 prevents the optimizer from eliminating calls.
+//
+// PseudoLA — load address of a symbol into a register.
+// Expands to "AUIPC rd, hi20(addr); ADDI rd, rd, lo12(addr)" in the
+// AsmPrinter. No DAG pattern: materialization of global addresses is
+// handled in LX32ISelLowering::lowerGlobalAddress (Day 9).
+//
+// PseudoNOP — explicit no-operation. Expands to "ADDI x0, x0, 0".
+// Used by the assembler and occasionally by peephole passes.
+//===----------------------------------------------------------------------===//
+
+class LX32Pseudo<dag outs, dag ins, string opcodestr, list<dag> pattern>
+ : LXInstCommon<outs, ins, opcodestr, ""> {
+ let isPseudo = 1;
+ let Pattern = pattern;
+}
+
+let isPseudo = 1, hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
+
+ def PseudoRET : LX32Pseudo<(outs), (ins), "ret", [(LX32ret)]> {
+ let isReturn = 1;
+ let isTerminator = 1;
+ let isBarrier = 1;
+ // Keep return-value registers live across RA so return-copy chains are not
+ // deleted as dead defs before pseudo expansion.
+ let Uses = [X1, X10, X11];
+ }
+
+ def PseudoBR : LX32Pseudo<(outs), (ins simm21:$target), "br\t$target", []> {
+ let isTerminator = 1;
+ let isBranch = 1;
+ let isBarrier = 1;
+ }
+
+ def PseudoCALL : LX32Pseudo<(outs), (ins bare_symbol:$target), "call\t$target", []> {
+ let isCall = 1;
+ // Model ABI caller-saved clobbers so RA spills/reloads across calls.
+ let Defs = [X1, X5, X6, X7,
+ X10, X11, X12, X13, X14, X15, X16, X17,
+ X28, X29, X30, X31];
+ let hasSideEffects = 1;
+ }
+
+ def PseudoLA : LX32Pseudo<(outs GPR:$rd), (ins bare_symbol:$addr),
+ "la\t$rd, $addr", []>;
+
+ def PseudoNOP : LX32Pseudo<(outs), (ins), "nop", []>;
+
+} // isPseudo = 1
+
+//===----------------------------------------------------------------------===//
+// Section 12 — Codegen-Only Pseudo Overrides
+//
+// These pseudos exist only during code generation and are never seen by the
+// assembler. isCodeGenOnly = 1 prevents them from appearing in the
+// instruction enum exposed to the MC layer.
+//
+// LX32_LOAD_STACK_GUARD — loads the stack-canary value used by
+// -fstack-protector. Expanded in the backend to a load from the
+// platform-defined canary address.
+//
+// LX32_PREALLOCATED_ARG — marker for a pre-allocated outgoing argument slot.
+// Used by the preallocated-call-frame lowering pass.
+//
+// ADJCALLSTACKDOWN / ADJCALLSTACKUP — abstract stack-pointer adjustments
+// that bracket call sequences. Expanded by LX32FrameLowering::
+// eliminateCallFramePseudoInstr (Day 8) into real ADDI sp, sp, ±N
+// instructions or eliminated entirely when the frame has reserved call
+// frame space.
+//===----------------------------------------------------------------------===//
+
+let isPseudo = 1, isCodeGenOnly = 1, Namespace = "LX32" in {
+
+ class PseudoCondBr<string Name>
+ : LX32Pseudo<(outs), (ins GPR:$rs1, GPR:$rs2, simm13:$target), Name, []> {
+ let hasSideEffects = 0;
+ let mayLoad = 0;
+ let mayStore = 0;
+ let isTerminator = 1;
+ let isBranch = 1;
+ }
+
+ def PseudoBEQ : PseudoCondBr<"pbeq\t$rs1, $rs2, $target">;
+ def PseudoBNE : PseudoCondBr<"pbne\t$rs1, $rs2, $target">;
+ def PseudoBLT : PseudoCondBr<"pblt\t$rs1, $rs2, $target">;
+ def PseudoBGE : PseudoCondBr<"pbge\t$rs1, $rs2, $target">;
+ def PseudoBLTU : PseudoCondBr<"pbltu\t$rs1, $rs2, $target">;
+ def PseudoBGEU : PseudoCondBr<"pbgeu\t$rs1, $rs2, $target">;
+
+ def LX32_LOAD_STACK_GUARD : LX32Pseudo<(outs GPR:$dst), (ins),
+ "load_stack_guard", []> {
+ let mayLoad = 1;
+ let hasSideEffects = 0;
+ let mayStore = 0;
+ }
+
+ def LX32_PREALLOCATED_ARG : LX32Pseudo<(outs), (ins i32imm:$p),
+ "preallocated_arg", []> {
+ let hasSideEffects = 1;
+ let mayLoad = 0;
+ let mayStore = 0;
+ }
+
+ // ADJCALLSTACKDOWN amt1, amt2
+ // Emitted before a call. amt1 is the amount to decrement sp;
+ // amt2 is the pre-allocated frame size (non-zero when hasReservedCallFrame
+ // returns false).
+ def ADJCALLSTACKDOWN : LX32Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2),
+ "adjcallstackdown", []> {
+ let hasSideEffects = 1;
+ let mayLoad = 0;
+ let mayStore = 0;
+ }
+
+ // ADJCALLSTACKUP amt1, amt2
+ // Emitted after a call. Mirrors ADJCALLSTACKDOWN.
+ def ADJCALLSTACKUP : LX32Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2),
+ "adjcallstackup", []> {
+ let hasSideEffects = 1;
+ let mayLoad = 0;
+ let mayStore = 0;
+ }
+
+} // isPseudo = 1, isCodeGenOnly = 1
diff --git a/llvm/lib/Target/LX32/TableGen/LX32RegisterInfo.td b/llvm/lib/Target/LX32/TableGen/LX32RegisterInfo.td
new file mode 100644
index 0000000000000..618d9423e8c69
--- /dev/null
+++ b/llvm/lib/Target/LX32/TableGen/LX32RegisterInfo.td
@@ -0,0 +1,126 @@
+//===-- LX32RegisterInfo.td - LX32 Register Definitions -------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the LX32 register file: 32 general-purpose 32-bit
+// registers (X0-X31), their ABI aliases, two register classes (GPR and
+// GPRNOX0), and an alternate-name index for ABI names.
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Register base class
+//===----------------------------------------------------------------------===//
+
+let Namespace = "LX32" in {
+class LX32Reg<bits<5> Enc, string n, list<string> alt = []> : Register<n> {
+ let HWEncoding{4-0} = Enc;
+ let AltNames = alt;
+}
+}
+
+//===----------------------------------------------------------------------===//
+// Alternate name index
+//
+// ABIRegAltName provides the standard ABI aliases (zero, ra, sp, ...).
+// FallbackRegAltNameIndex = NoRegAltName means that registers without an
+// explicit ABI alias fall back to the canonical hardware name.
+//===----------------------------------------------------------------------===//
+
+let FallbackRegAltNameIndex = NoRegAltName in
+def ABIRegAltName : RegAltNameIndex;
+
+//===----------------------------------------------------------------------===//
+// Register definitions
+//
+// Registers are listed in hardware-encoding order (X0-X31).
+// ABI aliases follow standard ILP32 conventions.
+// X0 : hardwired zero — marked isConstant so the allocator never
+// assigns it as a destination.
+// X1 (ra) : return address
+// X2 (sp) : stack pointer
+// X3 (gp) : global pointer
+// X4 (tp) : thread pointer
+// X5-X7 : temporaries t0-t2 (caller-saved)
+// X8 (s0/fp): frame pointer / callee-saved s0
+// X9 (s1) : callee-saved
+// X10-X17 : arguments a0-a7 / return values a0-a1
+// X18-X27 : callee-saved s2-s11
+// X28-X31 : temporaries t3-t6 (caller-saved)
+//===----------------------------------------------------------------------===//
+
+let RegAltNameIndices = [ABIRegAltName] in {
+ let isConstant = true in
+ def X0 : LX32Reg<0, "x0", ["zero"]>;
+
+ def X1 : LX32Reg<1, "x1", ["ra"]>;
+ def X2 : LX32Reg<2, "x2", ["sp"]>;
+ def X3 : LX32Reg<3, "x3", ["gp"]>;
+ def X4 : LX32Reg<4, "x4", ["tp"]>;
+ def X5 : LX32Reg<5, "x5", ["t0"]>;
+ def X6 : LX32Reg<6, "x6", ["t1"]>;
+ def X7 : LX32Reg<7, "x7", ["t2"]>;
+ def X8 : LX32Reg<8, "x8", ["s0", "fp"]>;
+ def X9 : LX32Reg<9, "x9", ["s1"]>;
+ def X10 : LX32Reg<10, "x10", ["a0"]>;
+ def X11 : LX32Reg<11, "x11", ["a1"]>;
+ def X12 : LX32Reg<12, "x12", ["a2"]>;
+ def X13 : LX32Reg<13, "x13", ["a3"]>;
+ def X14 : LX32Reg<14, "x14", ["a4"]>;
+ def X15 : LX32Reg<15, "x15", ["a5"]>;
+ def X16 : LX32Reg<16, "x16", ["a6"]>;
+ def X17 : LX32Reg<17, "x17", ["a7"]>;
+ def X18 : LX32Reg<18, "x18", ["s2"]>;
+ def X19 : LX32Reg<19, "x19", ["s3"]>;
+ def X20 : LX32Reg<20, "x20", ["s4"]>;
+ def X21 : LX32Reg<21, "x21", ["s5"]>;
+ def X22 : LX32Reg<22, "x22", ["s6"]>;
+ def X23 : LX32Reg<23, "x23", ["s7"]>;
+ def X24 : LX32Reg<24, "x24", ["s8"]>;
+ def X25 : LX32Reg<25, "x25", ["s9"]>;
+ def X26 : LX32Reg<26, "x26", ["s10"]>;
+ def X27 : LX32Reg<27, "x27", ["s11"]>;
+ def X28 : LX32Reg<28, "x28", ["t3"]>;
+ def X29 : LX32Reg<29, "x29", ["t4"]>;
+ def X30 : LX32Reg<30, "x30", ["t5"]>;
+ def X31 : LX32Reg<31, "x31", ["t6"]>;
+}
+
+//===----------------------------------------------------------------------===//
+// Register classes
+//
+// GPR — all 32 registers, ordered by allocation priority:
+// 1. Argument / return-value registers (a0-a7): highest priority so that
+// the allocator places values in calling-convention registers first,
+// minimizing moves at call boundaries.
+// 2. Caller-saved temporaries (t0-t2, t3-t6): free to use without saving.
+// 3. Callee-saved registers (s0-s11): usable but must be saved/restored.
+// 4. Special pointers (ra, sp, gp, tp): the allocator avoids these by
+// convention; they are reserved in LX32RegisterInfo::getReservedRegs.
+// 5. Zero register (x0): listed last so the allocator never picks it as
+// a destination; it is also marked isConstant above.
+//
+// GPRNOX0 — same as GPR minus X0. Used for instruction operands that must
+// not target the zero register (e.g., JALR destination when used as call).
+// X0 is excluded via (sub GPR, X0) so TableGen keeps allocation order.
+//===----------------------------------------------------------------------===//
+
+def GPR : RegisterClass<"LX32", [i32], 32, (add
+ // Arguments and return values — highest priority
+ X10, X11, X12, X13, X14, X15, X16, X17,
+ // Caller-saved temporaries
+ X5, X6, X7,
+ X28, X29, X30, X31,
+ // Callee-saved
+ X8, X9,
+ X18, X19, X20, X21, X22, X23, X24, X25, X26, X27,
+ // Special pointers — allocator avoids these (reserved in RegisterInfo)
+ X1, X2, X3, X4,
+ // Zero register — never allocatable
+ X0
+)>;
+
+def GPRNOX0 : RegisterClass<"LX32", [i32], 32, (sub GPR, X0)>;
diff --git a/llvm/lib/Target/LX32/TargetInfo/CMakeLists.txt b/llvm/lib/Target/LX32/TargetInfo/CMakeLists.txt
new file mode 100644
index 0000000000000..5823dce8adc8f
--- /dev/null
+++ b/llvm/lib/Target/LX32/TargetInfo/CMakeLists.txt
@@ -0,0 +1,11 @@
+# TargetInfo
+add_llvm_component_library(LLVMLX32Info
+ LX32TargetInfo.cpp
+
+ LINK_COMPONENTS
+ MC
+ Support
+
+ ADD_TO_COMPONENT
+ LX32
+)
\ No newline at end of file
diff --git a/llvm/lib/Target/LX32/TargetInfo/LX32TargetInfo.cpp b/llvm/lib/Target/LX32/TargetInfo/LX32TargetInfo.cpp
new file mode 100644
index 0000000000000..1e204600dafd4
--- /dev/null
+++ b/llvm/lib/Target/LX32/TargetInfo/LX32TargetInfo.cpp
@@ -0,0 +1,32 @@
+//===-- LX32TargetInfo.cpp - LX32 Target Implementation -------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements TargetInfo registration for LX32.
+//
+// This file must remain tiny and deterministic: it only owns the global Target
+// singleton and registers lx32 triple/name metadata.
+//
+// It is organized into the following sections:
+//
+// Section 0 — Target singleton accessor
+// Section 1 — LLVMInitializeLX32TargetInfo registration entry point
+
+#include "LX32TargetInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/TargetParser/Triple.h"
+
+llvm::Target &llvm::getTheLX32TargetInfo() {
+ static Target TheLX32Target;
+ return TheLX32Target;
+}
+
+extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
+LLVMInitializeLX32TargetInfo() {
+ llvm::RegisterTarget<llvm::Triple::lx32, /*HasJIT=*/false> X(
+ llvm::getTheLX32TargetInfo(), "lx32", "32-bit LX32", "LX32");
+}
\ No newline at end of file
diff --git a/llvm/lib/Target/LX32/TargetInfo/LX32TargetInfo.h b/llvm/lib/Target/LX32/TargetInfo/LX32TargetInfo.h
new file mode 100644
index 0000000000000..41e618f77c3fe
--- /dev/null
+++ b/llvm/lib/Target/LX32/TargetInfo/LX32TargetInfo.h
@@ -0,0 +1,29 @@
+//===-- LX32TargetInfo.h - LX32 Target Implementation -------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// Declares the target-info entry points for LX32.
+//
+// TargetInfo is the first layer in LLVM target initialization. It provides:
+// - the singleton Target object backing the lx32 backend
+// - registration function invoked by LLVMInitializeAllTargetInfos()
+//
+// It is organized into the following sections:
+//
+// Section 0 — Forward declarations
+// Section 1 — TargetInfo entry-point declarations
+//
+#ifndef LLVM_LIB_TARGET_LX32_TARGETINFO_LX32TARGETINFO_H
+#define LLVM_LIB_TARGET_LX32_TARGETINFO_LX32TARGETINFO_H
+
+namespace llvm {
+class Target;
+
+Target &getTheLX32TargetInfo();
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_LX32_TARGETINFO_LX32TARGETINFO_H
\ No newline at end of file
diff --git a/llvm/lib/Target/LX32/core/CMakeLists.txt b/llvm/lib/Target/LX32/core/CMakeLists.txt
new file mode 100644
index 0000000000000..66807388fbdc6
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/CMakeLists.txt
@@ -0,0 +1,45 @@
+# core/CMakeLists.txt
+#
+# Builds LLVMLX32CodeGen — the CodeGen layer of the LX32 backend.
+#
+# Source files are listed in build-dependency order: each file may only
+# include headers from files listed earlier in this list.
+#
+# LX32Subtarget.cpp — TableGen-generated subtarget tables + constructor.
+# No dependencies on other LX32 CodeGen sources.
+# LX32TargetMachine.cpp — Entry point; depends on LX32Subtarget.
+#
+#
+# LINK_COMPONENTS declares which LLVM component libraries must be linked.
+# Each component is resolved by the LLVM CMake infrastructure to the
+# correct library (e.g., CodeGen → LLVMCodeGen.a / libLLVMCodeGen.so).
+#
+# Required components for the current skeleton:
+# CodeGen — MachineFunction, TargetPassConfig, RA infrastructure
+# Core — Type, Module, IRBuilder (needed by ISelLowering, Day 9)
+# MC — MCAsmInfo, MCInstrInfo, MCRegisterInfo
+# SelectionDAG — SelectionDAG, DAGToDAGISel (needed by ISelDAGToDAG, Day 10)
+# Support — ErrorHandling, Debug, raw_ostream
+# Target — TargetMachine base class, TargetOptions
+
+add_llvm_component_library(LLVMLX32CodeGen
+ LX32AsmPrinter.cpp
+ LX32FrameLowering.cpp
+ LX32ISelDAGToDAG.cpp
+ LX32InstrInfo.cpp
+ LX32ISelLowering.cpp
+ LX32RegisterInfo.cpp
+ LX32Subtarget.cpp
+ LX32TargetMachine.cpp
+
+ LINK_COMPONENTS
+ CodeGen
+ Core
+ MC
+ SelectionDAG
+ Support
+ Target
+
+ ADD_TO_COMPONENT
+ LX32
+)
diff --git a/llvm/lib/Target/LX32/core/LX32AsmPrinter.cpp b/llvm/lib/Target/LX32/core/LX32AsmPrinter.cpp
new file mode 100644
index 0000000000000..aea4f3243ed29
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32AsmPrinter.cpp
@@ -0,0 +1,229 @@
+//===-- LX32AsmPrinter.cpp - LX32 Assembly Printer ------------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+
+#include "LX32InstrInfo.h"
+#include "LX32TargetMachine.h"
+
+#include "../TargetInfo/LX32TargetInfo.h"
+
+#include "llvm/CodeGen/AsmPrinter.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/MC/MCExpr.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstBuilder.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+namespace {
+
+class LX32MCInstLower {
+ AsmPrinter &Printer;
+ MCContext &Ctx;
+
+public:
+ explicit LX32MCInstLower(AsmPrinter &Printer, MCContext &Ctx)
+ : Printer(Printer), Ctx(Ctx) {}
+
+ const MCExpr *lowerSymbolOperand(const MachineOperand &MO) const {
+ const MCSymbol *Sym = nullptr;
+ int64_t Offset = 0;
+
+ switch (MO.getType()) {
+ default:
+ return nullptr;
+ case MachineOperand::MO_GlobalAddress:
+ Sym = Printer.getSymbol(MO.getGlobal());
+ Offset = MO.getOffset();
+ break;
+ case MachineOperand::MO_MachineBasicBlock:
+ Sym = MO.getMBB()->getSymbol();
+ break;
+ case MachineOperand::MO_ExternalSymbol:
+ Sym = Printer.GetExternalSymbolSymbol(MO.getSymbolName());
+ break;
+ case MachineOperand::MO_BlockAddress:
+ Sym = Printer.GetBlockAddressSymbol(MO.getBlockAddress());
+ Offset = MO.getOffset();
+ break;
+ case MachineOperand::MO_ConstantPoolIndex:
+ Sym = Printer.GetCPISymbol(MO.getIndex());
+ Offset = MO.getOffset();
+ break;
+ case MachineOperand::MO_JumpTableIndex:
+ Sym = Printer.GetJTISymbol(MO.getIndex());
+ break;
+ }
+
+ const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx);
+ if (Offset != 0)
+ Expr = MCBinaryExpr::createAdd(
+ Expr, MCConstantExpr::create(Offset, Ctx), Ctx);
+ return Expr;
+ }
+
+ bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp) const {
+ switch (MO.getType()) {
+ default:
+ return false;
+ case MachineOperand::MO_Register:
+ if (MO.isImplicit())
+ return false;
+ MCOp = MCOperand::createReg(MO.getReg());
+ return true;
+ case MachineOperand::MO_Immediate:
+ MCOp = MCOperand::createImm(MO.getImm());
+ return true;
+ case MachineOperand::MO_CImmediate:
+ MCOp = MCOperand::createImm(MO.getCImm()->getSExtValue());
+ return true;
+ case MachineOperand::MO_FPImmediate:
+ return false;
+ case MachineOperand::MO_MachineBasicBlock:
+ case MachineOperand::MO_GlobalAddress:
+ case MachineOperand::MO_ExternalSymbol:
+ case MachineOperand::MO_BlockAddress:
+ case MachineOperand::MO_ConstantPoolIndex:
+ case MachineOperand::MO_JumpTableIndex: {
+ const MCExpr *Expr = lowerSymbolOperand(MO);
+ if (!Expr)
+ return false;
+ MCOp = MCOperand::createExpr(Expr);
+ return true;
+ }
+ }
+ }
+
+ void lower(const MachineInstr *MI, MCInst &OutMI) const {
+ OutMI.setOpcode(MI->getOpcode());
+ for (const MachineOperand &MO : MI->operands()) {
+ MCOperand MCOp;
+ if (lowerOperand(MO, MCOp))
+ OutMI.addOperand(MCOp);
+ }
+ }
+};
+
+class LX32AsmPrinter : public AsmPrinter {
+ LX32MCInstLower MCILower;
+
+public:
+ explicit LX32AsmPrinter(TargetMachine &TM,
+ std::unique_ptr<MCStreamer> Streamer)
+ : AsmPrinter(TM, std::move(Streamer)), MCILower(*this, OutContext) {}
+
+ StringRef getPassName() const override { return "LX32 Assembly Printer"; }
+
+ void emitInstruction(const MachineInstr *MI) override {
+ switch (MI->getOpcode()) {
+ default:
+ break;
+ case LX32::PseudoRET: {
+ MCInst Ret;
+ Ret.setOpcode(LX32::JALR);
+ Ret.addOperand(MCOperand::createReg(LX32::X0));
+ Ret.addOperand(MCOperand::createReg(LX32::X1));
+ Ret.addOperand(MCOperand::createImm(0));
+ EmitToStreamer(*OutStreamer, Ret);
+ return;
+ }
+ case LX32::PseudoNOP: {
+ MCInst Nop;
+ Nop.setOpcode(LX32::ADDI);
+ Nop.addOperand(MCOperand::createReg(LX32::X0));
+ Nop.addOperand(MCOperand::createReg(LX32::X0));
+ Nop.addOperand(MCOperand::createImm(0));
+ EmitToStreamer(*OutStreamer, Nop);
+ return;
+ }
+ case LX32::PseudoCALL: {
+ // Support both call forms used by the backend:
+ // 1) register-held target -> jalr ra, rs1, 0
+ // 2) direct symbol target -> jal ra, symbol
+ Register Base = 0;
+ MCOperand TargetSym;
+ bool HasTargetSym = false;
+ for (const MachineOperand &MO : MI->operands()) {
+ if (MO.isReg() && MO.getReg() != 0 && !MO.isImplicit()) {
+ Base = MO.getReg();
+ break;
+ }
+ if (!HasTargetSym &&
+ (MO.isGlobal() || MO.isSymbol() || MO.isMBB() ||
+ MO.isBlockAddress() || MO.isCPI() || MO.isJTI())) {
+ HasTargetSym = MCILower.lowerOperand(MO, TargetSym);
+ }
+ }
+
+ if (Base) {
+ MCInst Call;
+ Call.setOpcode(LX32::JALR);
+ Call.addOperand(MCOperand::createReg(LX32::X1));
+ Call.addOperand(MCOperand::createReg(Base));
+ Call.addOperand(MCOperand::createImm(0));
+ EmitToStreamer(*OutStreamer, Call);
+ return;
+ }
+
+ if (HasTargetSym) {
+ MCInst Call;
+ Call.setOpcode(LX32::JAL);
+ Call.addOperand(MCOperand::createReg(LX32::X1));
+ Call.addOperand(TargetSym);
+ EmitToStreamer(*OutStreamer, Call);
+ return;
+ }
+
+ std::string Dump;
+ raw_string_ostream OS(Dump);
+ MI->print(OS);
+ report_fatal_error(Twine("lx32: malformed PseudoCALL (missing callable target operand): ") +
+ OS.str());
+ }
+ case LX32::PseudoLA: {
+ // Minimal symbol materialization for asm path.
+ MCOperand RD, SymOp;
+ if (!MCILower.lowerOperand(MI->getOperand(0), RD) ||
+ !MCILower.lowerOperand(MI->getOperand(1), SymOp))
+ report_fatal_error("lx32: failed to lower PseudoLA operands");
+
+ MCInst Auipc;
+ Auipc.setOpcode(LX32::AUIPC);
+ Auipc.addOperand(RD);
+ Auipc.addOperand(SymOp);
+ EmitToStreamer(*OutStreamer, Auipc);
+
+ MCInst Addi;
+ Addi.setOpcode(LX32::ADDI);
+ Addi.addOperand(RD);
+ Addi.addOperand(RD);
+ Addi.addOperand(SymOp);
+ EmitToStreamer(*OutStreamer, Addi);
+ return;
+ }
+ }
+
+ MCInst TmpInst;
+ MCILower.lower(MI, TmpInst);
+ EmitToStreamer(*OutStreamer, TmpInst);
+ }
+};
+
+} // end anonymous namespace
+
+extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLX32AsmPrinter() {
+ RegisterAsmPrinter<LX32AsmPrinter> X(getTheLX32TargetInfo());
+}
+
+
+
+
+
diff --git a/llvm/lib/Target/LX32/core/LX32FrameLowering.cpp b/llvm/lib/Target/LX32/core/LX32FrameLowering.cpp
new file mode 100644
index 0000000000000..ec62e7763ac4e
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32FrameLowering.cpp
@@ -0,0 +1,100 @@
+//===-- LX32FrameLowering.cpp - LX32 Frame Lowering Implementation -------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+
+#include "LX32FrameLowering.h"
+
+#include "LX32InstrInfo.h"
+#include "LX32RegisterInfo.h"
+#include "LX32Subtarget.h"
+
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/Target/TargetMachine.h"
+
+using namespace llvm;
+
+LX32FrameLowering::LX32FrameLowering(const LX32Subtarget &STI)
+ : TargetFrameLowering(StackGrowsDown, Align(16), 0), STI(STI) {}
+
+void LX32FrameLowering::emitPrologue(MachineFunction &MF,
+ MachineBasicBlock &MBB) const {
+ const MachineFrameInfo &MFI = MF.getFrameInfo();
+ int64_t StackSize = MFI.getStackSize();
+ if (StackSize == 0)
+ return;
+
+ const LX32InstrInfo &TII = static_cast<const LX32InstrInfo &>(*STI.getInstrInfo());
+ MachineBasicBlock::iterator MBBI = MBB.begin();
+ DebugLoc DL = (MBBI != MBB.end()) ? MBBI->getDebugLoc() : DebugLoc();
+ TII.adjustReg(MBB, MBBI, DL, LX32::X2, LX32::X2, -StackSize,
+ MachineInstr::FrameSetup);
+
+ if (hasFP(MF))
+ TII.adjustReg(MBB, MBBI, DL, LX32::X8, LX32::X2, 0,
+ MachineInstr::FrameSetup);
+}
+
+void LX32FrameLowering::emitEpilogue(MachineFunction &MF,
+ MachineBasicBlock &MBB) const {
+ const MachineFrameInfo &MFI = MF.getFrameInfo();
+ int64_t StackSize = MFI.getStackSize();
+ if (StackSize == 0)
+ return;
+
+ const LX32InstrInfo &TII = static_cast<const LX32InstrInfo &>(*STI.getInstrInfo());
+ MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator();
+ DebugLoc DL = (MBBI != MBB.end()) ? MBBI->getDebugLoc() : DebugLoc();
+
+ if (hasFP(MF))
+ TII.adjustReg(MBB, MBBI, DL, LX32::X2, LX32::X8, 0,
+ MachineInstr::FrameDestroy);
+
+ TII.adjustReg(MBB, MBBI, DL, LX32::X2, LX32::X2, StackSize,
+ MachineInstr::FrameDestroy);
+}
+
+bool LX32FrameLowering::hasFPImpl(const MachineFunction &MF) const {
+ const MachineFrameInfo &MFI = MF.getFrameInfo();
+ // Keep FP only when structurally required. The backend does not yet
+ // preserve X8 as a dedicated frame pointer across calls.
+ return MFI.hasVarSizedObjects() || MFI.isFrameAddressTaken();
+}
+
+bool LX32FrameLowering::hasReservedCallFrame(const MachineFunction &MF) const {
+ return !MF.getFrameInfo().hasVarSizedObjects();
+}
+
+MachineBasicBlock::iterator LX32FrameLowering::eliminateCallFramePseudoInstr(
+ MachineFunction &MF, MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MI) const {
+ if (!hasReservedCallFrame(MF)) {
+ const LX32InstrInfo &TII =
+ static_cast<const LX32InstrInfo &>(*STI.getInstrInfo());
+ DebugLoc DL = MI->getDebugLoc();
+ int64_t Amount = MI->getOperand(0).getImm();
+
+ if (Amount != 0) {
+ if (MI->getOpcode() == LX32::ADJCALLSTACKDOWN)
+ Amount = -Amount;
+ TII.adjustReg(MBB, MI, DL, LX32::X2, LX32::X2, Amount,
+ MI->getOpcode() == LX32::ADJCALLSTACKDOWN
+ ? MachineInstr::FrameSetup
+ : MachineInstr::FrameDestroy);
+ }
+ }
+
+ return MBB.erase(MI);
+}
+
+StackOffset LX32FrameLowering::getFrameIndexReference(const MachineFunction &MF,
+ int FI,
+ Register &FrameReg) const {
+ const MachineFrameInfo &MFI = MF.getFrameInfo();
+ FrameReg = hasFP(MF) ? LX32::X8 : LX32::X2;
+ return StackOffset::getFixed(MFI.getObjectOffset(FI) + MFI.getStackSize());
+}
+
+
diff --git a/llvm/lib/Target/LX32/core/LX32FrameLowering.h b/llvm/lib/Target/LX32/core/LX32FrameLowering.h
new file mode 100644
index 0000000000000..def5e86c5cec8
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32FrameLowering.h
@@ -0,0 +1,212 @@
+//===-- LX32FrameLowering.h - LX32 Frame Lowering Interface --------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares LX32FrameLowering, the class responsible for generating
+// function prologue and epilogue code for the LX32 backend.
+//
+// It is organized into the following sections:
+//
+// Section 0 — Role in the backend pipeline
+// Section 1 — Class declaration and constructor
+// Section 2 — Prologue and epilogue emission
+// Section 3 — Frame-pointer policy
+// Section 4 — Call-frame pseudo elimination
+// Section 5 — Frame-index reference resolution
+//
+//===----------------------------------------------------------------------===//
+//
+// Section 0 — Role in the backend pipeline
+//
+// FrameLowering is responsible for everything related to the activation record
+// (stack frame) of a function. Its main responsibilities are:
+//
+// emitPrologue (Section 2)
+// Emits the instructions at function entry that set up the stack frame:
+// 1. Decrement sp by the total frame size (ADDI sp, sp, -N).
+// 2. Save all callee-saved registers that the function uses (SW reg, k(sp)).
+// 3. Optionally establish fp (ADDI fp, sp, N) when a frame pointer is needed.
+// 4. Emit CFI directives so debuggers and unwinders can reconstruct the
+// caller's frame.
+//
+// emitEpilogue (Section 2)
+// Emits the instructions before a return that tear down the stack frame:
+// 1. Restore all callee-saved registers (LW reg, k(sp)).
+// 2. Restore sp (ADDI sp, sp, +N or ADDI sp, fp, 0 when dynamic).
+// Note: the actual JALR x0,ra,0 return instruction comes from the
+// PseudoRET expansion in LX32InstrInfo::expandPostRAPseudo.
+//
+// hasFP (Section 3)
+// Decides whether the function needs a dedicated frame pointer register
+// (X8/fp). Required when the frame size is not statically known — e.g.,
+// alloca(), variable-length arrays, or -fno-omit-frame-pointer.
+//
+// eliminateCallFramePseudoInstr (Section 4)
+// Removes or converts ADJCALLSTACKDOWN/ADJCALLSTACKUP pseudos that bracket
+// call sequences. When the frame has a reserved call-frame area (static
+// allocation), the pseudos are simply deleted. Otherwise they are
+// converted to real ADDI sp, sp, ±N instructions.
+//
+// getFrameIndexReference (Section 5)
+// Computes the byte offset from the frame anchor register (sp or fp) for
+// a given FrameIndex. Called by LX32RegisterInfo::eliminateFrameIndex to
+// resolve abstract FI references to concrete addresses.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LX32_LX32FRAMELOWERING_H
+#define LX32_LX32FRAMELOWERING_H
+
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/TargetFrameLowering.h"
+
+namespace llvm {
+
+class LX32Subtarget;
+class LX32InstrInfo;
+
+//===----------------------------------------------------------------------===//
+// Section 1 — Class declaration and constructor
+//===----------------------------------------------------------------------===//
+
+class LX32FrameLowering : public TargetFrameLowering {
+ const LX32Subtarget &STI;
+
+public:
+ // Construct with the active subtarget.
+ //
+ // TargetFrameLowering constructor parameters:
+ // StackGrowsDown — LX32 uses a downward-growing stack (sp decrements on
+ // function entry), which is the standard convention.
+ // StackAlignment — sp must be aligned to 16 bytes before each call to
+ // maintain ABI compatibility with functions that require
+ // aligned arguments on the stack.
+ // LocalAreaOffset — 0, meaning local variables start immediately at the
+ // top of the frame (no reserved area above locals).
+ explicit LX32FrameLowering(const LX32Subtarget &STI);
+
+ //===--------------------------------------------------------------------===//
+ // Section 2 — Prologue and epilogue emission
+ //===--------------------------------------------------------------------===//
+
+ // emitPrologue — emit function-entry frame setup code.
+ //
+ // The prologue performs these steps in order:
+ //
+ // 1. Compute the total frame size:
+ // local variables (assigned by the RA and MachineFrameInfo)
+ // + callee-saved slots (one 4-byte slot per callee-saved register used)
+ // + alignment padding (to keep sp 16-byte aligned before calls)
+ //
+ // 2. ADDI sp, sp, -FrameSize
+ // Decrements the stack pointer. Uses adjustReg from LX32InstrInfo
+ // which handles the rare case where FrameSize > 2047.
+ //
+ // 3. .cfi_def_cfa_offset FrameSize
+ // CFI directive so the unwinder knows the new CFA offset.
+ //
+ // 4. For each callee-saved register in CalleeSavedInfo:
+ // SW reg, offset(sp) — save the register
+ // .cfi_offset reg, offset-FrameSize — CFI for the saved register
+ //
+ // 5. If hasFP(MF):
+ // ADDI fp, sp, FrameSize — fp points to the pre-prologue sp
+ // .cfi_def_cfa_register fp — update CFA base to fp
+ //
+ // Leaf functions (no calls, no stack usage) skip steps 1-3 and return early.
+ void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
+
+ // emitEpilogue — emit function-exit frame teardown code.
+ //
+ // The epilogue performs these steps in reverse order from the prologue:
+ //
+ // 1. If hasFP(MF) and the frame has dynamic-size objects (alloca):
+ // ADDI sp, fp, 0 — restore sp from fp before reloading saves
+ //
+ // 2. For each callee-saved register (in reverse save order):
+ // LW reg, offset(sp) — restore the register
+ //
+ // 3. ADDI sp, sp, +FrameSize — restore sp to its pre-prologue value
+ //
+ // The return instruction (JALR x0, ra, 0) is emitted separately by
+ // LX32InstrInfo::expandPostRAPseudo when it expands PseudoRET.
+ void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
+
+ //===--------------------------------------------------------------------===//
+ // Section 3 — Frame-pointer policy
+ //===--------------------------------------------------------------------===//
+
+ // hasFP — return true if this function requires a dedicated frame pointer.
+ //
+ // A frame pointer is required when:
+ // - The frame has variable-size objects (alloca, VLAs). Without fp, sp
+ // moves during the function body and locals can no longer be addressed
+ // as sp + fixed_offset.
+ // - The function takes the address of its own frame (llvm.frameaddress).
+ // - Frame pointer elimination is disabled (-fno-omit-frame-pointer or the
+ // MachineTargetOptions::DisableFramePointerElim flag).
+ bool hasFPImpl(const MachineFunction &MF) const override;
+
+ // hasReservedCallFrame — return true if the function pre-allocates space
+ // for outgoing call arguments in the frame.
+ //
+ // When true, ADJCALLSTACKDOWN/ADJCALLSTACKUP pseudos are eliminated without
+ // emitting any real instructions. When false, they are converted to sp
+ // adjustments around each call.
+ //
+ // LX32 reserves call-frame space only when the function does not use alloca
+ // (i.e., the frame size is known at compile time). Dynamic frames cannot
+ // pre-allocate because the required space depends on the maximum call in the
+ // function, which may vary at runtime.
+ bool hasReservedCallFrame(const MachineFunction &MF) const override;
+
+ //===--------------------------------------------------------------------===//
+ // Section 4 — Call-frame pseudo elimination
+ //===--------------------------------------------------------------------===//
+
+ // eliminateCallFramePseudoInstr — convert or remove ADJCALLSTACKDOWN/UP.
+ //
+ // ADJCALLSTACKDOWN and ADJCALLSTACKUP bracket every call sequence:
+ // ADJCALLSTACKDOWN N — sp must decrease by N before the call
+ // < argument setup >
+ // CALL target
+ // ADJCALLSTACKUP N — sp must be restored by N after the call
+ //
+ // This function is called for each such pseudo. It either:
+ // - Deletes the pseudo (when hasReservedCallFrame is true, meaning the
+ // frame already includes the call-argument space and no runtime sp
+ // adjustment is needed), or
+ // - Converts it to ADDI sp, sp, ±N (when the frame is dynamic).
+ MachineBasicBlock::iterator
+ eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MI) const override;
+
+ //===--------------------------------------------------------------------===//
+ // Section 5 — Frame-index reference resolution
+ //===--------------------------------------------------------------------===//
+
+ // getFrameIndexReference — compute the offset from the frame anchor to a FI.
+ //
+ // Called by LX32RegisterInfo::eliminateFrameIndex to resolve abstract
+ // FrameIndex values to concrete (register, offset) pairs.
+ //
+ // Sets FrameReg to:
+ // X8 (fp) — when hasFP(MF) is true (fp is the stable reference point)
+ // X2 (sp) — otherwise
+ //
+ // Returns the signed byte offset from FrameReg to the start of the slot
+ // identified by FI. Positive offsets are above the current sp; negative
+ // offsets are below (into the frame).
+ StackOffset getFrameIndexReference(const MachineFunction &MF, int FI,
+ Register &FrameReg) const override;
+
+ // LX32 v1 does not use a dedicated base pointer.
+};
+
+} // namespace llvm
+
+#endif // LX32_LX32FRAMELOWERING_H
diff --git a/llvm/lib/Target/LX32/core/LX32ISelDAGToDAG.cpp b/llvm/lib/Target/LX32/core/LX32ISelDAGToDAG.cpp
new file mode 100644
index 0000000000000..d73a466fe0b0f
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32ISelDAGToDAG.cpp
@@ -0,0 +1,237 @@
+//===-- LX32ISelDAGToDAG.cpp - LX32 DAG->DAG Instruction Selector --------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+
+#include "LX32ISelDAGToDAG.h"
+
+#include "LX32ISelLowering.h"
+#include "LX32Subtarget.h"
+#include "LX32TargetMachine.h"
+
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/SelectionDAGISel.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include <memory>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "lx32-isel"
+
+namespace {
+
+class LX32DAGToDAGISel : public SelectionDAGISel {
+ const LX32Subtarget *Subtarget = nullptr;
+
+public:
+ explicit LX32DAGToDAGISel(LX32TargetMachine &TM, CodeGenOptLevel OptLevel)
+ : SelectionDAGISel(TM, OptLevel) {}
+
+ bool runOnMachineFunction(MachineFunction &MF) override {
+ Subtarget = &MF.getSubtarget<LX32Subtarget>();
+ return SelectionDAGISel::runOnMachineFunction(MF);
+ }
+
+ void Select(SDNode *Node) override;
+
+private:
+ void SelectFrameIndex(SDNode *Node);
+
+ // Include the auto-generated selection matcher.
+ #include "../TableGen/LX32GenDAGISel.inc"
+};
+
+class LX32DAGToDAGISelLegacy : public SelectionDAGISelLegacy {
+public:
+ static char ID;
+
+ LX32DAGToDAGISelLegacy(LX32TargetMachine &TM, CodeGenOptLevel OptLevel)
+ : SelectionDAGISelLegacy(
+ ID, std::make_unique<LX32DAGToDAGISel>(TM, OptLevel)) {}
+
+ StringRef getPassName() const override {
+ return "LX32 DAG->DAG Instruction Selection";
+ }
+};
+
+} // end anonymous namespace
+
+char LX32DAGToDAGISelLegacy::ID = 0;
+
+void LX32DAGToDAGISel::SelectFrameIndex(SDNode *Node) {
+ SDLoc DL(Node);
+ int FI = cast<FrameIndexSDNode>(Node)->getIndex();
+ SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::i32);
+ SDValue Zero = CurDAG->getTargetConstant(0, DL, MVT::i32);
+
+ SDNode *Result = CurDAG->getMachineNode(LX32::ADDI, DL, MVT::i32, TFI, Zero);
+ ReplaceNode(Node, Result);
+}
+
+void LX32DAGToDAGISel::Select(SDNode *Node) {
+ if (Node->isMachineOpcode()) {
+ Node->setNodeId(-1);
+ return;
+ }
+
+ switch (Node->getOpcode()) {
+ case ISD::BR: {
+ SDLoc DL(Node);
+
+ if (Node->getNumOperands() < 2)
+ report_fatal_error("lx32: malformed BR node");
+
+ SDValue Chain = Node->getOperand(0);
+ SDValue Target = Node->getOperand(1);
+
+ // Create a PseudoBR pseudo-instruction that will be expanded later
+ SDNode *Jump = CurDAG->getMachineNode(
+ LX32::PseudoBR, DL, MVT::Other, Chain, Target);
+ ReplaceNode(Node, Jump);
+ return;
+ }
+ case LX32ISD::CALL: {
+ SDLoc DL(Node);
+
+ if (Node->getNumOperands() < 2)
+ report_fatal_error("lx32: malformed CALL node");
+
+ SDValue Callee = Node->getOperand(1);
+ if (Callee.getOpcode() != ISD::TargetGlobalAddress &&
+ Callee.getOpcode() != ISD::TargetExternalSymbol)
+ report_fatal_error("lx32: CALL expects target global/external symbol");
+
+ SmallVector<SDValue, 4> Ops;
+ Ops.push_back(Node->getOperand(0)); // chain
+ Ops.push_back(Callee); // direct call target symbol
+ if (Node->getNumOperands() > 2)
+ Ops.push_back(Node->getOperand(2)); // optional glue
+
+ SDNode *Call = CurDAG->getMachineNode(
+ LX32::PseudoCALL, DL, CurDAG->getVTList(MVT::Other, MVT::Glue), Ops);
+ ReplaceNode(Node, Call);
+ return;
+ }
+ case LX32ISD::RET: {
+ SDLoc DL(Node);
+ SmallVector<SDValue, 4> RetOps;
+ for (const SDValue &Op : Node->ops())
+ RetOps.push_back(Op);
+
+ SDVTList VTs = CurDAG->getVTList(MVT::Other);
+ SDNode *Ret = CurDAG->getMachineNode(LX32::PseudoRET, DL, VTs, RetOps);
+ ReplaceNode(Node, Ret);
+ return;
+ }
+ case ISD::BR_CC: {
+ SDLoc DL(Node);
+ if (Node->getNumOperands() < 5)
+ report_fatal_error("lx32: malformed BR_CC node");
+
+ const auto *CCNode = dyn_cast<CondCodeSDNode>(Node->getOperand(1));
+ if (!CCNode)
+ report_fatal_error("lx32: BR_CC missing condition code");
+
+ SDValue Chain = Node->getOperand(0);
+ // LX32 BR_CC nodes arrive as: chain, cc, rhs, target, lhs.
+ // Keep names aligned with semantic role (lhs/rhs), not raw index.
+ SDValue RHS = Node->getOperand(2);
+ SDValue Target = Node->getOperand(3);
+ SDValue LHS = Node->getOperand(4);
+
+ auto emitCondPseudo = [&](unsigned Opc, SDValue OpA, SDValue OpB) {
+ SmallVector<SDValue, 4> BrOps;
+ BrOps.push_back(Chain);
+ BrOps.push_back(OpA);
+ BrOps.push_back(OpB);
+ BrOps.push_back(Target);
+ SDNode *Br = CurDAG->getMachineNode(Opc, DL, MVT::Other, BrOps);
+ ReplaceNode(Node, Br);
+ return;
+ };
+
+ unsigned BrOpc = 0;
+ bool Swap = false;
+
+ switch (CCNode->get()) {
+ case ISD::SETEQ:
+ BrOpc = LX32::PseudoBEQ;
+ break;
+ case ISD::SETNE:
+ BrOpc = LX32::PseudoBNE;
+ break;
+ case ISD::SETLT:
+ BrOpc = LX32::PseudoBLT;
+ break;
+ case ISD::SETGE:
+ BrOpc = LX32::PseudoBGE;
+ break;
+ case ISD::SETULT:
+ BrOpc = LX32::PseudoBLTU;
+ break;
+ case ISD::SETUGE:
+ BrOpc = LX32::PseudoBGEU;
+ break;
+ case ISD::SETGT:
+ // Keep the historical ordering used by this backend for signed GT.
+ // This path is intentionally explicit because generic swap handling
+ // does not produce equivalent semantics with the current BR_CC layout.
+ {
+ SmallVector<SDValue, 4> BrOps;
+ BrOps.push_back(Chain);
+ BrOps.push_back(LHS);
+ BrOps.push_back(Target);
+ BrOps.push_back(RHS);
+ SDNode *Br = CurDAG->getMachineNode(LX32::PseudoBLT, DL, MVT::Other, BrOps);
+ ReplaceNode(Node, Br);
+ }
+ return;
+ case ISD::SETLE:
+ BrOpc = LX32::PseudoBGE;
+ Swap = true;
+ break;
+ case ISD::SETUGT:
+ BrOpc = LX32::PseudoBLTU;
+ Swap = true;
+ break;
+ case ISD::SETULE:
+ BrOpc = LX32::PseudoBGEU;
+ Swap = true;
+ break;
+ default:
+ report_fatal_error("lx32: unsupported BR_CC condition code");
+ }
+
+ SDValue Op0 = Swap ? RHS : LHS;
+ SDValue Op1 = Swap ? LHS : RHS;
+ emitCondPseudo(BrOpc, Op0, Op1);
+ return;
+ }
+ case ISD::FrameIndex:
+ SelectFrameIndex(Node);
+ return;
+ default:
+ break;
+ }
+
+ SelectCode(Node);
+}
+
+FunctionPass *llvm::createLX32ISelDag(LX32TargetMachine &TM,
+ CodeGenOptLevel OptLevel) {
+ return new LX32DAGToDAGISelLegacy(TM, OptLevel);
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/llvm/lib/Target/LX32/core/LX32ISelDAGToDAG.h b/llvm/lib/Target/LX32/core/LX32ISelDAGToDAG.h
new file mode 100644
index 0000000000000..d56958fc02528
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32ISelDAGToDAG.h
@@ -0,0 +1,24 @@
+//===-- LX32ISelDAGToDAG.h - LX32 DAG->DAG Instruction Selector ----------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_LX32_CORE_LX32ISELDAGTODAG_H
+#define LLVM_LIB_TARGET_LX32_CORE_LX32ISELDAGTODAG_H
+
+#include "llvm/Support/CodeGen.h"
+
+namespace llvm {
+
+class FunctionPass;
+class LX32TargetMachine;
+
+FunctionPass *createLX32ISelDag(LX32TargetMachine &TM, CodeGenOptLevel OptLevel);
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_LX32_CORE_LX32ISELDAGTODAG_H
+
+
diff --git a/llvm/lib/Target/LX32/core/LX32ISelLowering.cpp b/llvm/lib/Target/LX32/core/LX32ISelLowering.cpp
new file mode 100644
index 0000000000000..73e1cfc0e9c87
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32ISelLowering.cpp
@@ -0,0 +1,279 @@
+//===-- LX32ISelLowering.cpp - LX32 SelectionDAG Lowering ----------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+
+#include "LX32ISelLowering.h"
+
+#include "LX32RegisterInfo.h"
+#include "LX32Subtarget.h"
+
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#define DEBUG_TYPE "lx32-lower"
+
+using namespace llvm;
+
+#include "../TableGen/LX32GenCallingConv.inc"
+
+static SDValue lowerCCValue(SDValue Val, CCValAssign::LocInfo LocInfo,
+ EVT ValVT, SelectionDAG &DAG,
+ const SDLoc &DL) {
+ switch (LocInfo) {
+ case CCValAssign::Full:
+ return Val;
+ case CCValAssign::BCvt:
+ return DAG.getNode(ISD::BITCAST, DL, ValVT, Val);
+ case CCValAssign::SExt:
+ if (Val.getValueType() == ValVT)
+ return Val;
+ return DAG.getNode(ISD::AssertSext, DL, Val.getValueType(), Val,
+ DAG.getValueType(ValVT));
+ case CCValAssign::ZExt:
+ if (Val.getValueType() == ValVT)
+ return Val;
+ return DAG.getNode(ISD::AssertZext, DL, Val.getValueType(), Val,
+ DAG.getValueType(ValVT));
+ case CCValAssign::AExt:
+ if (Val.getValueType() == ValVT)
+ return Val;
+ return DAG.getNode(ISD::TRUNCATE, DL, ValVT, Val);
+ default:
+ report_fatal_error("lx32: unsupported CC value location info");
+ }
+}
+
+LX32TargetLowering::LX32TargetLowering(const TargetMachine &TM,
+ const LX32Subtarget &STI)
+ : TargetLowering(TM, STI), STI(STI) {
+ addRegisterClass(MVT::i32, &LX32::GPRRegClass);
+ setStackPointerRegisterToSaveRestore(LX32::X2);
+
+ setOperationAction(ISD::SDIV, MVT::i32, Expand);
+ setOperationAction(ISD::UDIV, MVT::i32, Expand);
+ setOperationAction(ISD::SREM, MVT::i32, Expand);
+ setOperationAction(ISD::UREM, MVT::i32, Expand);
+
+ setOperationAction(ISD::ROTL, MVT::i32, Expand);
+ setOperationAction(ISD::ROTR, MVT::i32, Expand);
+ setOperationAction(ISD::CTLZ, MVT::i32, Expand);
+ setOperationAction(ISD::CTTZ, MVT::i32, Expand);
+ setOperationAction(ISD::CTPOP, MVT::i32, Expand);
+
+ // First functional slice: keep select lowering on generic expansion.
+ setOperationAction(ISD::SELECT, MVT::i32, Expand);
+
+ setOperationAction(ISD::GlobalAddress, MVT::i32, Expand);
+ setOperationAction(ISD::BlockAddress, MVT::i32, Expand);
+ setOperationAction(ISD::ConstantPool, MVT::i32, Expand);
+
+ setMaxAtomicSizeInBitsSupported(0);
+
+ computeRegisterProperties(STI.getRegisterInfo());
+}
+
+const char *LX32TargetLowering::getTargetNodeName(unsigned Opcode) const {
+ switch (Opcode) {
+ case LX32ISD::RET:
+ return "LX32ISD::RET";
+ case LX32ISD::CALL:
+ return "LX32ISD::CALL";
+ case LX32ISD::SELECT_CC:
+ return "LX32ISD::SELECT_CC";
+ default:
+ return nullptr;
+ }
+}
+
+SDValue LX32TargetLowering::LowerFormalArguments(
+ SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
+ const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL,
+ SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const {
+ if (IsVarArg)
+ report_fatal_error("lx32: varargs lowering is not implemented yet");
+
+ MachineFunction &MF = DAG.getMachineFunction();
+ MachineRegisterInfo &RegInfo = MF.getRegInfo();
+ MachineFrameInfo &MFI = MF.getFrameInfo();
+
+ SmallVector<CCValAssign, 16> ArgLocs;
+ CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext());
+ CCInfo.AnalyzeFormalArguments(Ins, CC_LX32);
+
+ for (unsigned I = 0, E = Ins.size(); I != E; ++I) {
+ const CCValAssign &VA = ArgLocs[I];
+
+ SDValue Val;
+ if (VA.isRegLoc()) {
+ Register VReg = RegInfo.createVirtualRegister(&LX32::GPRRegClass);
+ RegInfo.addLiveIn(VA.getLocReg(), VReg);
+
+ SDValue Arg = DAG.getCopyFromReg(Chain, DL, VReg, VA.getLocVT());
+ Chain = Arg.getValue(1);
+ Val = Arg;
+ } else {
+ int FI = MFI.CreateFixedObject(VA.getLocVT().getStoreSize(),
+ VA.getLocMemOffset(), true);
+ SDValue FIN = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout()));
+ SDValue Ld = DAG.getLoad(VA.getLocVT(), DL, Chain, FIN,
+ MachinePointerInfo::getFixedStack(MF, FI));
+ Chain = Ld.getValue(1);
+ Val = Ld;
+ }
+
+ EVT ValVT = Ins[I].VT;
+ Val = lowerCCValue(Val, VA.getLocInfo(), ValVT, DAG, DL);
+ if (Val.getValueType() != ValVT)
+ Val = DAG.getNode(ISD::TRUNCATE, DL, ValVT, Val);
+ InVals.push_back(Val);
+ }
+
+ return Chain;
+}
+
+SDValue LX32TargetLowering::LowerCall(
+ TargetLowering::CallLoweringInfo &CLI,
+ SmallVectorImpl<SDValue> &InVals) const {
+ SelectionDAG &DAG = CLI.DAG;
+ SDLoc DL = CLI.DL;
+ MachineFunction &MF = DAG.getMachineFunction();
+
+ if (CLI.IsVarArg)
+ report_fatal_error("lx32: varargs call lowering is not implemented yet");
+
+ EVT PtrVT = getPointerTy(DAG.getDataLayout());
+
+ SDValue Chain = CLI.Chain;
+ SDValue Glue;
+
+ SmallVector<CCValAssign, 16> ArgLocs;
+ CCState CCInfo(CLI.CallConv, CLI.IsVarArg, MF, ArgLocs, *DAG.getContext());
+ CCInfo.AnalyzeCallOperands(CLI.Outs, CC_LX32);
+
+ for (unsigned I = 0, E = ArgLocs.size(); I != E; ++I) {
+ const CCValAssign &VA = ArgLocs[I];
+ SDValue Val = CLI.OutVals[I];
+
+ switch (VA.getLocInfo()) {
+ case CCValAssign::Full:
+ break;
+ case CCValAssign::SExt:
+ Val = DAG.getNode(ISD::SIGN_EXTEND, DL, VA.getLocVT(), Val);
+ break;
+ case CCValAssign::ZExt:
+ Val = DAG.getNode(ISD::ZERO_EXTEND, DL, VA.getLocVT(), Val);
+ break;
+ case CCValAssign::AExt:
+ Val = DAG.getNode(ISD::ANY_EXTEND, DL, VA.getLocVT(), Val);
+ break;
+ case CCValAssign::BCvt:
+ Val = DAG.getNode(ISD::BITCAST, DL, VA.getLocVT(), Val);
+ break;
+ default:
+ report_fatal_error("lx32: unsupported call argument location info");
+ }
+
+ if (!VA.isRegLoc())
+ report_fatal_error("lx32: stack-passed call arguments are not implemented yet");
+
+ Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), Val, Glue);
+ Glue = Chain.getValue(1);
+ }
+
+ SDValue Callee = CLI.Callee;
+ if (const auto *ES = dyn_cast<ExternalSymbolSDNode>(Callee)) {
+ Callee = DAG.getTargetExternalSymbol(ES->getSymbol(), PtrVT);
+ } else if (const auto *GA = dyn_cast<GlobalAddressSDNode>(Callee)) {
+ Callee = DAG.getTargetGlobalAddress(GA->getGlobal(), DL, PtrVT, GA->getOffset());
+ } else {
+ report_fatal_error("lx32: only direct global/external calls are supported");
+ }
+
+ SmallVector<SDValue, 4> CallOps;
+ CallOps.push_back(Chain);
+ CallOps.push_back(Callee);
+ if (Glue)
+ CallOps.push_back(Glue);
+
+ SDValue Call = DAG.getNode(LX32ISD::CALL, DL, DAG.getVTList(MVT::Other, MVT::Glue),
+ CallOps);
+ Chain = Call.getValue(0);
+ Glue = Call.getValue(1);
+
+ SmallVector<CCValAssign, 4> RetLocs;
+ CCState RetCC(CLI.CallConv, CLI.IsVarArg, MF, RetLocs, *DAG.getContext());
+ RetCC.AnalyzeCallResult(CLI.Ins, RetCC_LX32);
+
+ for (unsigned I = 0, E = RetLocs.size(); I != E; ++I) {
+ const CCValAssign &VA = RetLocs[I];
+ SDValue Ret = DAG.getCopyFromReg(Chain, DL, VA.getLocReg(), VA.getLocVT(), Glue);
+ Chain = Ret.getValue(1);
+ Glue = Ret.getValue(2);
+ InVals.push_back(lowerCCValue(Ret, VA.getLocInfo(), CLI.Ins[I].VT, DAG, DL));
+ }
+
+ return Chain;
+}
+
+SDValue LX32TargetLowering::LowerReturn(
+ SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
+ const SmallVectorImpl<ISD::OutputArg> &Outs,
+ const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
+ SelectionDAG &DAG) const {
+ if (IsVarArg)
+ report_fatal_error("lx32: varargs return lowering is not implemented yet");
+
+ MachineFunction &MF = DAG.getMachineFunction();
+ SmallVector<CCValAssign, 16> RetLocs;
+ CCState RetCC(CallConv, IsVarArg, MF, RetLocs, *DAG.getContext());
+ RetCC.AnalyzeReturn(Outs, RetCC_LX32);
+
+ SDValue Flag;
+ for (unsigned I = 0, E = RetLocs.size(); I != E; ++I) {
+ const CCValAssign &VA = RetLocs[I];
+ SDValue Val = OutVals[I];
+
+ switch (VA.getLocInfo()) {
+ case CCValAssign::Full:
+ break;
+ case CCValAssign::SExt:
+ Val = DAG.getNode(ISD::SIGN_EXTEND, DL, VA.getLocVT(), Val);
+ break;
+ case CCValAssign::ZExt:
+ Val = DAG.getNode(ISD::ZERO_EXTEND, DL, VA.getLocVT(), Val);
+ break;
+ case CCValAssign::AExt:
+ Val = DAG.getNode(ISD::ANY_EXTEND, DL, VA.getLocVT(), Val);
+ break;
+ case CCValAssign::BCvt:
+ Val = DAG.getNode(ISD::BITCAST, DL, VA.getLocVT(), Val);
+ break;
+ default:
+ report_fatal_error("lx32: unsupported return value location info");
+ }
+
+ Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), Val, Flag);
+ Flag = Chain.getValue(1);
+ }
+
+ SmallVector<SDValue, 4> RetOps;
+ RetOps.push_back(Chain);
+ if (Flag)
+ RetOps.push_back(Flag);
+ return DAG.getNode(LX32ISD::RET, DL, MVT::Other, RetOps);
+}
+
+SDValue LX32TargetLowering::LowerOperation(SDValue Op,
+ SelectionDAG &DAG) const {
+ switch (Op.getOpcode()) {
+ default:
+ llvm_unreachable("lx32: unexpected custom-lowered operation");
+ }
+}
+
+
diff --git a/llvm/lib/Target/LX32/core/LX32ISelLowering.h b/llvm/lib/Target/LX32/core/LX32ISelLowering.h
new file mode 100644
index 0000000000000..03e3c756da871
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32ISelLowering.h
@@ -0,0 +1,260 @@
+//===-- LX32ISelLowering.h - LX32 SelectionDAG Lowering Interface --------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares LX32TargetLowering, the class that controls how LLVM IR
+// operations are lowered to LX32 SelectionDAG nodes during instruction
+// selection.
+//
+// It is organized into the following sections:
+//
+// Section 0 — Role in the backend pipeline
+// Section 1 — Custom DAG node opcodes (LX32ISD)
+// Section 2 — Class declaration and constructor
+// Section 3 — Calling convention lowering
+// Section 4 — Custom operation lowering helpers
+//
+//===----------------------------------------------------------------------===//
+//
+// Section 0 — Role in the backend pipeline
+//
+// The SelectionDAG instruction selection pipeline runs in three stages:
+//
+// 1. IR → DAG lowering (this class, via LowerOperation and the CC helpers)
+// Converts LLVM IR intrinsics, calling conventions, and operations that
+// have no direct LX32 instruction into legal SelectionDAG nodes.
+//
+// 2. DAG → DAG legalization (controlled by setOperationAction calls in the
+// constructor)
+// Tells the legalizer what to do with each ISD node on LX32:
+// Legal — the node maps directly to an instruction; leave it alone.
+// Expand — decompose into simpler nodes (e.g., sdiv → __divsi3 call).
+// Custom — call LowerOperation to produce a hand-written DAG sequence.
+//
+// 3. DAG → MachineInstr selection (LX32ISelDAGToDAG, Day 10)
+// Pattern-matches the legalised DAG against the patterns declared in
+// LX32InstrInfo.td and emits concrete MachineInstrs.
+//
+// The constructor (Section 2) is where all setOperationAction calls live.
+// This is the first thing to implement when adding support for a new
+// operation: decide whether it is Legal, Expand, or Custom, then add the
+// corresponding entry.
+//
+// Operations marked Expand that map to runtime library calls (e.g., __divsi3,
+// soft-float functions) require no additional implementation — the legalizer
+// generates the call automatically using the names registered with the
+// RuntimeLibcallsInfo.
+//
+// Operations marked Custom require a corresponding case in LowerOperation and
+// a private helper method (Section 4).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LX32_LX32ISELLOWERING_H
+#define LX32_LX32ISELLOWERING_H
+
+#include "llvm/CodeGen/CallingConvLower.h"
+#include "llvm/CodeGen/SelectionDAG.h"
+#include "llvm/CodeGen/SelectionDAGNodes.h"
+#include "llvm/CodeGen/TargetLowering.h"
+
+namespace llvm {
+
+class LX32Subtarget;
+
+//===----------------------------------------------------------------------===//
+// Section 1 — Custom DAG node opcodes (LX32ISD)
+//
+// These opcodes identify SelectionDAG nodes that have LX32-specific semantics
+// and cannot be expressed using the generic ISD namespace nodes.
+//
+// Each opcode is used in two places:
+// 1. ISelLowering creates nodes with these opcodes during LowerOperation.
+// 2. ISelDAGToDAG (Day 10) pattern-matches them to concrete instructions.
+//
+// The mapping to assembly:
+// LX32ISD::RET → PseudoRET → JALR x0, ra, 0
+// LX32ISD::CALL → PseudoCALL (direct) or JALR rs, 0 (indirect)
+//
+//===----------------------------------------------------------------------===//
+
+namespace LX32ISD {
+enum NodeType : unsigned {
+ // FIRST_NUMBER marks the start of the LX32-specific range so that the
+ // opcodes do not overlap with generic ISD opcodes.
+ FIRST_NUMBER = ISD::BUILTIN_OP_END,
+
+ // RET — function return node.
+ //
+ // Produced by LowerReturn(). Carries an optional glue operand (from
+ // CopyToReg nodes that place return values in a0/a1) and a chain.
+ // Pattern in LX32InstrInfo.td: [(LX32ret)] → PseudoRET → JALR x0, ra, 0.
+ RET,
+
+ // CALL — direct function call node.
+ //
+ // Produced by LowerCall() for calls to named symbols. The callee address
+ // is the first operand. Chains and glue operands follow.
+ // Lowered in LX32ISelDAGToDAG to PseudoCALL, which expands to
+ // AUIPC ra, hi20(sym) + JALR ra, lo12(sym)(ra).
+ CALL,
+
+ // SELECT_CC — conditional select with explicit condition code.
+ //
+ // Produced by LowerSELECT_CC when the legalizer cannot handle ISD::SELECT
+ // directly. Lowered in LX32ISelDAGToDAG to a branch sequence.
+ SELECT_CC,
+};
+} // namespace LX32ISD
+
+//===----------------------------------------------------------------------===//
+// Section 2 — Class declaration and constructor
+//===----------------------------------------------------------------------===//
+
+class LX32TargetLowering : public TargetLowering {
+ const LX32Subtarget &STI;
+
+public:
+ // Construct with a reference to the active TargetMachine and subtarget.
+ //
+ // The constructor body (in LX32ISelLowering.cpp) calls setOperationAction
+ // for every LLVM IR operation to declare whether LX32 can handle it
+ // directly (Legal), needs the legalizer to decompose it (Expand), or
+ // requires a custom DAG sequence (Custom).
+ //
+ // Key legalisation decisions in LX32 v1:
+ // - No hardware divider: SDIV/UDIV/SREM/UREM → Expand (→ __divsi3 etc.)
+ // - No FPU: all floating-point ops → Expand (→ soft-float library)
+ // - No SELECT instruction: SELECT → Custom (→ branch sequence)
+ // - No flags register: SETCC variants → Custom (→ SLT/SLTU/SUB combos)
+ // - Global addresses: GlobalAddress → Custom (→ AUIPC + ADDI)
+ // - Variadic calls: VASTART → Custom (→ spill of argument registers)
+ explicit LX32TargetLowering(const TargetMachine &TM,
+ const LX32Subtarget &STI);
+
+ // getTargetNodeName — return a human-readable name for a LX32ISD opcode.
+ //
+ // Used by the SelectionDAG printer (llc -view-dag-combine1) and error
+ // messages. Returns nullptr for unknown opcodes.
+ const char *getTargetNodeName(unsigned Opcode) const override;
+
+ //===--------------------------------------------------------------------===//
+ // Section 3 — Calling convention lowering
+ //===--------------------------------------------------------------------===//
+
+ // LowerFormalArguments — lower incoming function arguments.
+ //
+ // Called once at the start of each function. Assigns incoming argument
+ // values to virtual registers (for register arguments a0-a7) or loads them
+ // from the caller's stack (for stack arguments beyond the 8-register limit).
+ //
+ // The ILP32 calling convention is defined in LX32CallingConv.td as CC_LX32.
+ // This function uses CCState to query the convention and emits CopyFromReg
+ // or load nodes for each argument.
+ //
+ // Returns the function's chain value, extended with all argument setup nodes.
+ SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv,
+ bool IsVarArg,
+ const SmallVectorImpl<ISD::InputArg> &Ins,
+ const SDLoc &DL, SelectionDAG &DAG,
+ SmallVectorImpl<SDValue> &InVals) const override;
+
+ // LowerCall — lower an outgoing function call.
+ //
+ // Emits:
+ // 1. ADJCALLSTACKDOWN — reserve stack space for call arguments.
+ // 2. Argument placement — CopyToReg for register arguments (a0-a7),
+ // stores for stack arguments.
+ // 3. LX32ISD::CALL (direct) or JALR (indirect).
+ // 4. ADJCALLSTACKUP — release call-argument stack space.
+ // 5. CopyFromReg for return values from a0/a1.
+ //
+ // Uses CC_LX32 and RetCC_LX32 from LX32CallingConv.td.
+ SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI,
+ SmallVectorImpl<SDValue> &InVals) const override;
+
+ // LowerReturn — lower the function return sequence.
+ //
+ // Emits CopyToReg nodes to place return values in a0 (and a1 for i64),
+ // then creates the LX32ISD::RET node. The RetCC_LX32 convention from
+ // LX32CallingConv.td governs which registers carry return values.
+ SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
+ const SmallVectorImpl<ISD::OutputArg> &Outs,
+ const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
+ SelectionDAG &DAG) const override;
+
+ // LowerOperation — dispatch Custom-legalised operations to their handlers.
+ //
+ // Called by the legalizer for any operation registered as Custom in the
+ // constructor. Dispatches to the appropriate private helper (Section 4).
+ SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override;
+
+ //===--------------------------------------------------------------------===//
+ // Section 4 — Custom operation lowering helpers
+ //
+ // Each helper implements the Custom lowering for one ISD node type.
+ // They are private because only LowerOperation dispatches to them.
+ //===--------------------------------------------------------------------===//
+
+private:
+ // lowerGlobalAddress — lower ISD::GlobalAddress to AUIPC + ADDI.
+ //
+ // LX32 uses PC-relative addressing for globals:
+ // AUIPC rd, %pcrel_hi(sym) — rd = PC + hi20(sym - PC)
+ // ADDI rd, rd, %pcrel_lo(.) — rd = rd + lo12(sym - PC)
+ // The two-instruction sequence is needed because a single 12-bit ADDI
+ // cannot reach arbitrary 32-bit addresses.
+ SDValue lowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
+
+ // lowerBlockAddress — lower ISD::BlockAddress (address of a basic block).
+ //
+ // Used by computed gotos and jump tables. Same AUIPC+ADDI sequence as
+ // lowerGlobalAddress, but with a block-address relocation.
+ SDValue lowerBlockAddress(SDValue Op, SelectionDAG &DAG) const;
+
+ // lowerSELECT — lower ISD::SELECT to a branch sequence.
+ //
+ // LX32 has no conditional-move instruction. A SELECT is lowered to:
+ // BEQZ cond, else_bb — jump if condition is false
+ // then_bb:
+ // < use true_val >
+ // J end_bb
+ // else_bb:
+ // < use false_val >
+ // end_bb:
+ // PHI result, then_bb:true_val, else_bb:false_val
+ //
+ // This introduces a branch misprediction cost in the common case, but it
+ // is correct and simple. A peephole optimiser could convert some SELECT
+ // sequences to arithmetic idioms (e.g., select(a < b, a, b) → MIN via SLT).
+ SDValue lowerSELECT(SDValue Op, SelectionDAG &DAG) const;
+
+ // lowerSETCC — lower ISD::SETCC to SLT/SLTU/XOR/ADD combinations.
+ //
+ // LX32 has no condition-flag register. Comparison results live in GPRs.
+ // The available comparison instructions are SLT (signed) and SLTU (unsigned).
+ // All other condition codes are synthesised:
+ //
+ // SETEQ a, b → (a XOR b) == 0 → XORI (a XOR b), 1 using SLTIU
+ // SETNE a, b → (a XOR b) != 0 → SLTU x0, (a XOR b)
+ // SETLE a, b → NOT (b < a) → XORI (SLT b, a), 1
+ // SETGE a, b → NOT (a < b) → XORI (SLT a, b), 1
+ // SETGT a, b → b < a → SLT b, a
+ // ... (unsigned variants use SLTU instead of SLT)
+ SDValue lowerSETCC(SDValue Op, SelectionDAG &DAG) const;
+
+ // lowerVASTART — lower ISD::VASTART for variadic function support.
+ //
+ // At the start of a variadic function, the register arguments that were not
+ // consumed by named parameters are spilled to a contiguous area of the
+ // frame. lowerVASTART emits a store of the address of that area into the
+ // va_list structure, so that va_arg can walk it.
+ SDValue lowerVASTART(SDValue Op, SelectionDAG &DAG) const;
+};
+
+} // namespace llvm
+
+#endif // LX32_LX32ISELLOWERING_H
diff --git a/llvm/lib/Target/LX32/core/LX32InstrInfo.cpp b/llvm/lib/Target/LX32/core/LX32InstrInfo.cpp
new file mode 100644
index 0000000000000..c441033e70006
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32InstrInfo.cpp
@@ -0,0 +1,412 @@
+//===-- LX32InstrInfo.cpp - LX32 Instruction Info Implementation ---------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements LX32InstrInfo, the runtime instruction-management class
+// used by the LX32 backend's code generation pipeline.
+//
+// It is organized into the following sections:
+//
+// Section 0 — TableGen-generated instruction descriptor tables
+// Section 1 — Constructor
+// Section 2 — Physical register copy
+// Section 3 — Stack-slot spill and reload
+// Section 4 — Post-RA pseudo expansion
+// Section 5 — Stack-adjustment utility
+//
+//===----------------------------------------------------------------------===//
+
+#include "LX32InstrInfo.h"
+#include "LX32Subtarget.h"
+
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#define DEBUG_TYPE "lx32-instrinfo"
+
+//===----------------------------------------------------------------------===//
+// Section 0 — TableGen-generated instruction descriptor tables
+//
+// GET_INSTRINFO_MC_DESC expands to the implementation of:
+// LX32GenInstrInfo::LX32GenInstrInfo() — fills the MCInstrDesc tables
+// InitLX32MCInstrInfo() — used by the MC layer
+// These must appear at file scope before any namespace.
+//===----------------------------------------------------------------------===//
+
+#define GET_INSTRINFO_CTOR_DTOR
+#include "../TableGen/LX32GenInstrInfo.inc"
+
+using namespace llvm;
+
+//===----------------------------------------------------------------------===//
+// Section 1 — Constructor
+//===----------------------------------------------------------------------===//
+
+LX32InstrInfo::LX32InstrInfo(const LX32Subtarget &STI)
+ // LX32GenInstrInfo(CallFrameSetupOpcode, CallFrameDestroyOpcode)
+ // ADJCALLSTACKDOWN — decrements sp before a call to reserve argument space.
+ // ADJCALLSTACKUP — restores sp after a call.
+ // Both are defined as codegen-only pseudos in Section 12 of
+ // LX32InstrInfo.td and expanded by LX32FrameLowering::
+ // eliminateCallFramePseudoInstr (Day 8).
+ : LX32GenInstrInfo(STI, *STI.getRegisterInfo(),
+ LX32::ADJCALLSTACKDOWN, LX32::ADJCALLSTACKUP),
+ STI(STI) {}
+
+//===----------------------------------------------------------------------===//
+// Section 2 — Physical register copy
+//
+// LX32 has no dedicated MOV instruction. A register copy is expressed as:
+//
+// ADD rd, rs, x0
+//
+// where x0 is the zero register (always 0), so the result is simply the
+// value of rs. This is the standard idiom for register copies on LX32 base-
+// derived ISAs.
+//
+// The RegisterAllocator emits copyPhysReg whenever it needs to move a value
+// between two physical registers — for example, when placing an argument into
+// a calling-convention register or when a value is live in a non-preferred
+// register and must be relocated.
+//===----------------------------------------------------------------------===//
+
+void LX32InstrInfo::copyPhysReg(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI,
+ const DebugLoc &DL, Register DstReg,
+ Register SrcReg, bool KillSrc,
+ bool RenamableDest,
+ bool RenamableSrc) const {
+ // LX32 v1 only has integer GPRs. If either register is outside the GPR
+ // class, the backend has an internal inconsistency.
+ if (!LX32::GPRRegClass.contains(DstReg, SrcReg))
+ llvm_unreachable("LX32InstrInfo::copyPhysReg: unsupported register class "
+ "(only GPR → GPR copies are legal in LX32 v1)");
+
+ // ADD DstReg, SrcReg, x0
+ // SrcReg is marked Kill if KillSrc is true, which tells downstream passes
+ // that SrcReg is no longer live after this instruction.
+ BuildMI(MBB, MBBI, DL, get(LX32::ADD), DstReg)
+ .addReg(SrcReg, getKillRegState(KillSrc))
+ .addReg(LX32::X0);
+}
+
+//===----------------------------------------------------------------------===//
+// Section 3 — Stack-slot spill and reload
+//
+// When the register allocator runs out of physical registers, it must spill
+// the contents of a live virtual register to the stack and reload it later.
+//
+// storeRegToStackSlot:
+// Emits SW SrcReg, FrameIndex + 0
+// The FrameIndex is an abstract placeholder; LX32RegisterInfo::
+// eliminateFrameIndex rewrites it to (sp + concrete_offset) after the
+// frame layout is computed.
+//
+// loadRegFromStackSlot:
+// Emits LW DstReg, FrameIndex + 0
+// Symmetric to the store. After eliminateFrameIndex, this becomes
+// LW DstReg, concrete_offset(sp).
+//
+// Both functions assert that the register class is GPR (i32). Other register
+// classes (FPR, vector) do not exist in LX32 v1 and would require different
+// store/load widths.
+//===----------------------------------------------------------------------===//
+
+void LX32InstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI,
+ Register SrcReg, bool isKill,
+ int FrameIndex,
+ const TargetRegisterClass *RC,
+ Register VReg,
+ MachineInstr::MIFlag Flags) const {
+ // Only 32-bit GPR spills are supported in LX32 v1.
+ assert(RC == &LX32::GPRRegClass &&
+ "storeRegToStackSlot: only GPR class is supported");
+
+ DebugLoc DL =
+ MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc();
+
+ // SW SrcReg, FrameIndex + 0
+ // The offset of 0 is a placeholder that eliminateFrameIndex will replace
+ // with the real sp-relative byte offset once the frame layout is known.
+ BuildMI(MBB, MBBI, DL, get(LX32::SW))
+ .addReg(SrcReg, getKillRegState(isKill))
+ .addFrameIndex(FrameIndex)
+ .addImm(0)
+ .setMIFlag(Flags);
+}
+
+void LX32InstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI,
+ Register DstReg, int FrameIndex,
+ const TargetRegisterClass *RC,
+ Register VReg, unsigned SubReg,
+ MachineInstr::MIFlag Flags) const {
+ assert(RC == &LX32::GPRRegClass &&
+ "loadRegFromStackSlot: only GPR class is supported");
+
+ DebugLoc DL =
+ MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc();
+
+ // LW DstReg, FrameIndex + 0
+ BuildMI(MBB, MBBI, DL, get(LX32::LW), DstReg)
+ .addFrameIndex(FrameIndex)
+ .addImm(0)
+ .setMIFlag(Flags);
+}
+
+//===----------------------------------------------------------------------===//
+// Section 4 — Post-RA pseudo expansion
+//
+// expandPostRAPseudo is called by the PseudoExpansionPass after register
+// allocation. It converts pseudo-instructions (which have no hardware
+// encoding) to real instructions that the MCCodeEmitter can emit.
+//
+// The pseudos expanded here are defined as isCodeGenOnly in LX32InstrInfo.td.
+// After expansion the pseudo is erased from the MBB, so the function returns
+// true to signal that the iterator has been invalidated.
+//
+// PseudoRET:
+// The architecturally-correct return sequence on LX32 is:
+// JALR x0, ra, 0
+// x0 as destination discards the return-address link value (we are jumping
+// to the return address, not saving a new one). The instruction reads X1
+// (ra) as declared in the Uses list of PseudoRET in LX32InstrInfo.td.
+//
+// PseudoNOP:
+// The canonical NOP encoding is ADDI x0, x0, 0 — add zero to the zero
+// register and discard the result. Because x0 is hardwired to zero and
+// the destination is x0, this instruction has no observable effect.
+//===----------------------------------------------------------------------===//
+
+bool LX32InstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
+ MachineBasicBlock &MBB = *MI.getParent();
+ DebugLoc DL = MI.getDebugLoc();
+
+ auto isCondBranchOpc = [](unsigned Opc) {
+ switch (Opc) {
+ default:
+ return false;
+ case LX32::PseudoBEQ:
+ case LX32::PseudoBNE:
+ case LX32::PseudoBLT:
+ case LX32::PseudoBGE:
+ case LX32::PseudoBLTU:
+ case LX32::PseudoBGEU:
+ case LX32::BEQ:
+ case LX32::BNE:
+ case LX32::BLT:
+ case LX32::BGE:
+ case LX32::BLTU:
+ case LX32::BGEU:
+ return true;
+ }
+ };
+
+ auto getBranchTargetMBB = [](const MachineInstr &BrMI) -> MachineBasicBlock * {
+ for (const MachineOperand &MO : BrMI.operands())
+ if (MO.isMBB())
+ return MO.getMBB();
+ return nullptr;
+ };
+
+ auto expandCondBr = [&](unsigned RealOpc) {
+ SmallVector<MachineOperand, 4> RegOps;
+ const MachineOperand *TargetMBBOp = nullptr;
+ for (const MachineOperand &MO : MI.operands()) {
+ if (MO.isMBB() && !TargetMBBOp) {
+ TargetMBBOp = &MO;
+ continue;
+ }
+ if (!MO.isReg() || MO.getReg() == 0 || MO.isImplicit())
+ continue;
+ RegOps.push_back(MO);
+ }
+ if (RegOps.size() < 2)
+ report_fatal_error("lx32: malformed conditional-branch pseudo operands");
+ if (!TargetMBBOp)
+ report_fatal_error("lx32: conditional branch pseudo missing target MBB");
+
+ auto MIB = BuildMI(MBB, MI, DL, get(RealOpc));
+ MIB->addOperand(RegOps[RegOps.size() - 2]);
+ MIB->addOperand(RegOps[RegOps.size() - 1]);
+ MIB->addOperand(*TargetMBBOp);
+ MBB.erase(MI);
+ return true;
+ };
+
+ switch (MI.getOpcode()) {
+ default:
+ return false; // Unknown pseudo — leave it for another pass.
+
+ case LX32::PseudoRET:
+ // Expand to: JALR x0, ra, 0
+ // x0 (define, dead) — result register; marked Dead because nobody reads
+ // it (x0 is always zero anyway, but marking it Dead lets the register
+ // allocator know the result is intentionally discarded).
+ // X1 (ra, kill) — the return address; Kill means ra is consumed.
+ // 0 — no offset added to the return address.
+ BuildMI(MBB, MI, DL, get(LX32::JALR))
+ .addReg(LX32::X0, RegState::Define | RegState::Dead)
+ .addReg(LX32::X1, RegState::Kill)
+ .addImm(0);
+ MBB.erase(MI);
+ return true;
+
+ case LX32::PseudoBR: {
+ // Expand to: JAL x0, target
+ // x0 (define, dead) — result register; we discard the PC+4 return address
+ // target — the branch target (simm21 immediate)
+ MachineBasicBlock *TargetMBB = getBranchTargetMBB(MI);
+
+ MachineBasicBlock *CondTarget = nullptr;
+ for (MachineInstr *Prev = MI.getPrevNode(); Prev; Prev = Prev->getPrevNode()) {
+ if (Prev->isDebugInstr())
+ continue;
+ if (isCondBranchOpc(Prev->getOpcode()))
+ CondTarget = getBranchTargetMBB(*Prev);
+ break;
+ }
+
+ if (CondTarget) {
+ MachineBasicBlock *OtherSucc = nullptr;
+ for (MachineBasicBlock *Succ : MBB.successors()) {
+ if (Succ == CondTarget)
+ continue;
+ if (!OtherSucc) {
+ OtherSucc = Succ;
+ continue;
+ }
+ if (OtherSucc != Succ)
+ report_fatal_error("lx32: ambiguous non-conditional successor for PseudoBR");
+ }
+ if (OtherSucc)
+ TargetMBB = OtherSucc;
+ }
+
+ if (!TargetMBB) {
+ if (MBB.succ_empty())
+ report_fatal_error("lx32: branch pseudo has no branch target");
+ if (MBB.succ_size() > 1)
+ report_fatal_error("lx32: branch pseudo target is ambiguous without explicit MBB operand");
+ TargetMBB = *MBB.succ_begin();
+ }
+
+ BuildMI(MBB, MI, DL, get(LX32::JAL))
+ .addReg(LX32::X0, RegState::Define | RegState::Dead)
+ .addMBB(TargetMBB);
+ MBB.erase(MI);
+ return true;
+ }
+
+ case LX32::PseudoBEQ:
+ return expandCondBr(LX32::BEQ);
+ case LX32::PseudoBNE:
+ return expandCondBr(LX32::BNE);
+ case LX32::PseudoBLT:
+ return expandCondBr(LX32::BLT);
+ case LX32::PseudoBGE:
+ return expandCondBr(LX32::BGE);
+ case LX32::PseudoBLTU:
+ return expandCondBr(LX32::BLTU);
+ case LX32::PseudoBGEU:
+ return expandCondBr(LX32::BGEU);
+
+ case LX32::PseudoNOP:
+ // Expand to: ADDI x0, x0, 0
+ // Both source and destination are x0, immediate is 0. The instruction
+ // has no effect but occupies one instruction slot for alignment or
+ // pipeline padding purposes.
+ BuildMI(MBB, MI, DL, get(LX32::ADDI))
+ .addReg(LX32::X0, RegState::Define | RegState::Dead)
+ .addReg(LX32::X0, RegState::Kill)
+ .addImm(0);
+ MBB.erase(MI);
+ return true;
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Section 5 — Stack-adjustment utility
+//
+// adjustReg is a helper for FrameLowering. It emits the instruction(s)
+// needed to compute DstReg = SrcReg + Val.
+//
+// When Val fits in a 12-bit signed immediate (simm12, range [-2048, 2047]):
+// ADDI DstReg, SrcReg, Val — one instruction
+//
+// When Val is larger (rare; happens with frames > 2 KB):
+// LUI scratch, hi20(Val)
+// ADD DstReg, SrcReg, scratch
+// ADDI DstReg, DstReg, lo12(Val) — omitted when lo12 == 0
+//
+// The slow path requires that there is a free scratch register at the
+// insertion point. FrameLowering is responsible for ensuring this (it
+// calls adjustReg before any callee-saved registers have been spilled, so
+// temporaries are available).
+//
+// The hi20/lo12 decomposition uses the same +0x800 bias as the constant-
+// materialisation patterns in LX32InstrInfo.td Section 10, compensating for
+// the sign extension that ADDI applies to its 12-bit immediate.
+//===----------------------------------------------------------------------===//
+
+void LX32InstrInfo::adjustReg(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI,
+ const DebugLoc &DL, Register DstReg,
+ Register SrcReg, int64_t Val,
+ MachineInstr::MIFlag Flag) const {
+ if (Val == 0) {
+ // Zero adjustment: if DstReg == SrcReg, nothing to do.
+ // If DstReg != SrcReg, emit a register copy (ADD DstReg, SrcReg, x0).
+ if (DstReg != SrcReg)
+ copyPhysReg(MBB, MBBI, DL, DstReg, SrcReg, /*KillSrc=*/false);
+ return;
+ }
+
+ // Fast path: adjustment fits in simm12.
+ if (isInt<12>(Val)) {
+ BuildMI(MBB, MBBI, DL, get(LX32::ADDI), DstReg)
+ .addReg(SrcReg)
+ .addImm(Val)
+ .setMIFlag(Flag);
+ return;
+ }
+
+ // Slow path: adjustment does not fit in simm12.
+ // This is only expected for functions with stack frames larger than 2 KB.
+ assert(isInt<32>(Val) && "Frame adjustment exceeds 32-bit range");
+
+ // Decompose Val into hi20 (upper bits) and lo12 (lower 12, sign-extended).
+ int64_t Hi20 = ((Val + 0x800) >> 12) & 0xFFFFF;
+ int64_t Lo12 = Val - (Hi20 << 12);
+
+ // Use a caller-saved temporary as scratch. t0 (X5) is used here because
+ // adjustReg is only called from FrameLowering where t0 is not live.
+ // A more robust implementation would ask the RegScavenger, but for the
+ // current skeleton the direct choice is sufficient.
+ Register Scratch = LX32::X5; // t0 — caller-saved, safe in prologue/epilogue
+
+ // LUI scratch, hi20
+ BuildMI(MBB, MBBI, DL, get(LX32::LUI), Scratch)
+ .addImm(Hi20)
+ .setMIFlag(Flag);
+
+ // ADD DstReg, SrcReg, scratch
+ BuildMI(MBB, MBBI, DL, get(LX32::ADD), DstReg)
+ .addReg(SrcReg)
+ .addReg(Scratch)
+ .setMIFlag(Flag);
+
+ // ADDI DstReg, DstReg, lo12 (skip if lo12 == 0 to avoid a redundant NOP)
+ if (Lo12 != 0) {
+ BuildMI(MBB, MBBI, DL, get(LX32::ADDI), DstReg)
+ .addReg(DstReg)
+ .addImm(Lo12)
+ .setMIFlag(Flag);
+ }
+}
\ No newline at end of file
diff --git a/llvm/lib/Target/LX32/core/LX32InstrInfo.h b/llvm/lib/Target/LX32/core/LX32InstrInfo.h
new file mode 100644
index 0000000000000..d949bb6ffb779
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32InstrInfo.h
@@ -0,0 +1,202 @@
+//===-- LX32InstrInfo.h - LX32 Instruction Info Interface ----------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares LX32InstrInfo, the runtime instruction-management class
+// used by LLVM's code generation pipeline.
+//
+// It is organized into the following sections:
+//
+// Section 0 — Role in the backend pipeline
+// Section 1 — Includes and generated base class
+// Section 2 — Class declaration
+// Section 3 — Physical register copy
+// Section 4 — Stack-slot spill and reload
+// Section 5 — Post-RA pseudo expansion
+// Section 6 — Stack-adjustment utility
+//
+//===----------------------------------------------------------------------===//
+//
+// Section 0 — Role in the backend pipeline
+//
+// LX32InstrInfo is the primary interface between LLVM's machine-code passes
+// and the LX32 instruction set. Its responsibilities are:
+//
+// copyPhysReg (Section 3)
+// Called by the register allocator and copy propagation passes whenever
+// a value must be moved between two physical registers. On LX32 there is
+// no dedicated MOV instruction; a register copy is encoded as
+// ADD rd, rs, x0 (adding zero to the source register).
+//
+// storeRegToStackSlot / loadRegFromStackSlot (Section 4)
+// Called by the RA when it needs to spill a live register to the stack
+// (store) or reload it after a spill (load). The implementation emits
+// the appropriate SW / LW instruction with a FrameIndex operand;
+// eliminateFrameIndex() in LX32RegisterInfo resolves the FI to a concrete
+// sp+offset address after the frame layout is finalised.
+//
+// expandPostRAPseudo (Section 5)
+// Called by the PseudoExpansionPass after register allocation. Expands
+// codegen-only pseudo-instructions (PseudoRET, PseudoNOP) to their real
+// machine-instruction equivalents.
+//
+// adjustReg (Section 6)
+// A helper used internally by FrameLowering to emit the ADDI sp, sp, ±N
+// instruction in function prologues and epilogues. Handles the edge case
+// where the adjustment exceeds the simm12 range by falling back to a
+// LUI+ADD+ADDI sequence.
+//
+// All static instruction metadata (encoding, operand types, scheduling
+// attributes, DAG patterns) lives in the TableGen-generated base class
+// LX32GenInstrInfo, derived from the definitions in LX32InstrInfo.td.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LX32_LX32INSTRINFO_H
+#define LX32_LX32INSTRINFO_H
+
+#include "LX32RegisterInfo.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
+
+// Pull in the TableGen-generated LX32 opcode enum first, then the
+// LX32GenInstrInfo class declaration.
+#define GET_INSTRINFO_ENUM
+#include "../TableGen/LX32GenInstrInfo.inc"
+
+// GET_INSTRINFO_HEADER emits the LX32GenInstrInfo class declaration.
+#define GET_INSTRINFO_HEADER
+#include "../TableGen/LX32GenInstrInfo.inc"
+
+namespace llvm {
+
+class LX32Subtarget;
+
+//===----------------------------------------------------------------------===//
+// Section 2 — Class declaration
+//===----------------------------------------------------------------------===//
+
+class LX32InstrInfo : public LX32GenInstrInfo {
+ const LX32Subtarget &STI;
+
+public:
+ // Construct with a reference to the active subtarget.
+ // The subtarget is stored so that register-class and feature queries can be
+ // made without passing the subtarget through every call site.
+ explicit LX32InstrInfo(const LX32Subtarget &STI);
+
+ //===--------------------------------------------------------------------===//
+ // Section 3 — Physical register copy
+ //===--------------------------------------------------------------------===//
+
+ // copyPhysReg — emit an instruction to copy SrcReg into DstReg.
+ //
+ // LX32 has no dedicated register-move instruction. A copy is encoded as:
+ // ADD DstReg, SrcReg, x0
+ // which adds the zero register (always 0) to SrcReg and stores the result
+ // in DstReg. This is the canonical register copy idiom for RV32I-derived
+ // architectures.
+ //
+ // KillSrc: if true, the copy consumes SrcReg (marks it as killed). The RA
+ // sets this when SrcReg is no longer live after the copy, allowing downstream
+ // passes to reclaim the register.
+ //
+ // Asserts if DstReg or SrcReg are not in the GPR register class, because LX32
+ // v1 has no floating-point or vector registers.
+ void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
+ const DebugLoc &DL, Register DstReg, Register SrcReg,
+ bool KillSrc, bool RenamableDest = false,
+ bool RenamableSrc = false) const override;
+
+ //===--------------------------------------------------------------------===//
+ // Section 4 — Stack-slot spill and reload
+ //===--------------------------------------------------------------------===//
+
+ // storeRegToStackSlot — spill SrcReg to a stack frame slot.
+ //
+ // Emits: SW SrcReg, FrameIndex + 0
+ // The FrameIndex is an abstract slot identifier; eliminateFrameIndex in
+ // LX32RegisterInfo.cpp later rewrites it to the concrete sp+offset form.
+ //
+ // RC specifies the register class of SrcReg. For LX32, the only valid
+ // class is GPR (i32); wider classes do not exist yet.
+ //
+ // VReg is the virtual register being spilled. It is informational only
+ // (used for debug output) and does not affect code generation.
+ void storeRegToStackSlot(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI, Register SrcReg,
+ bool isKill, int FrameIndex,
+ const TargetRegisterClass *RC,
+ Register VReg,
+ MachineInstr::MIFlag Flags =
+ MachineInstr::NoFlags) const override;
+
+ // loadRegFromStackSlot — reload DstReg from a stack frame slot.
+ //
+ // Emits: LW DstReg, FrameIndex + 0
+ // Symmetric to storeRegToStackSlot; the FrameIndex is resolved by
+ // eliminateFrameIndex after the frame layout is finalised.
+ void loadRegFromStackSlot(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator MBBI, Register DstReg,
+ int FrameIndex, const TargetRegisterClass *RC,
+ Register VReg, unsigned SubReg = 0,
+ MachineInstr::MIFlag Flags =
+ MachineInstr::NoFlags) const override;
+
+ //===--------------------------------------------------------------------===//
+ // Section 5 — Post-RA pseudo expansion
+ //===--------------------------------------------------------------------===//
+
+ // expandPostRAPseudo — expand codegen-only pseudos to real instructions.
+ //
+ // Called by the PseudoExpansionPass after register allocation. Returns true
+ // if the pseudo was expanded (and therefore removed from the MBB), false if
+ // the opcode was not recognised.
+ //
+ // Pseudos handled:
+ //
+ // PseudoRET — function return.
+ // Expands to: JALR x0, ra, 0
+ // x0 as destination discards the link address (we are returning, not
+ // calling). Reads X1 (ra) as the return address.
+ //
+ // PseudoNOP — explicit no-operation.
+ // Expands to: ADDI x0, x0, 0
+ // The canonical NOP encoding on RV32I-derived architectures.
+ bool expandPostRAPseudo(MachineInstr &MI) const override;
+
+ //===--------------------------------------------------------------------===//
+ // Section 6 — Stack-adjustment utility
+ //===--------------------------------------------------------------------===//
+
+ // adjustReg — emit an ADDI (or LUI+ADD+ADDI) to adjust a register by Val.
+ //
+ // Used by LX32FrameLowering to generate the prologue/epilogue stack
+ // pointer adjustments:
+ // Prologue: adjustReg(MBB, MBBI, DL, sp, sp, -FrameSize, FrameSetup)
+ // Epilogue: adjustReg(MBB, MBBI, DL, sp, sp, +FrameSize, FrameDestroy)
+ //
+ // Fast path (|Val| <= 2047): emits a single ADDI DstReg, SrcReg, Val.
+ //
+ // Slow path (|Val| > 2047): emits a three-instruction sequence:
+ // LUI scratch, hi20(Val)
+ // ADD DstReg, SrcReg, scratch
+ // ADDI DstReg, DstReg, lo12(Val) (omitted when lo12 == 0)
+ // The slow path requires a scratch register that is not live at the
+ // insertion point; the caller must ensure one is available.
+ //
+ // Flag should be MachineInstr::FrameSetup for prologue instructions and
+ // MachineInstr::FrameDestroy for epilogue instructions. These flags cause
+ // LLVM to emit correct .cfi_adjust_cfa_offset directives.
+ void adjustReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
+ const DebugLoc &DL, Register DstReg, Register SrcReg,
+ int64_t Val, MachineInstr::MIFlag Flag) const;
+};
+
+} // namespace llvm
+
+#endif // LX32_LX32INSTRINFO_H
diff --git a/llvm/lib/Target/LX32/core/LX32RegisterInfo.cpp b/llvm/lib/Target/LX32/core/LX32RegisterInfo.cpp
new file mode 100644
index 0000000000000..80b186d4e930b
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32RegisterInfo.cpp
@@ -0,0 +1,263 @@
+//===-- LX32RegisterInfo.cpp - LX32 Register Info Implementation ---------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements LX32RegisterInfo, the runtime register-management
+// class used by the LX32 backend's code generation pipeline.
+//
+// It is organized into the following sections:
+//
+// Section 0 — TableGen-generated descriptor tables
+// Section 1 — Constructor
+// Section 2 — Reserved register set
+// Section 3 — Callee-saved register set
+// Section 4 — Frame-index elimination
+// Section 5 — Frame register selection
+//
+//===----------------------------------------------------------------------===//
+
+#include "LX32RegisterInfo.h"
+#include "LX32FrameLowering.h"
+#include "LX32Subtarget.h"
+
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/RegisterScavenging.h"
+#include "llvm/CodeGen/TargetFrameLowering.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#define DEBUG_TYPE "lx32-reginfo"
+
+//===----------------------------------------------------------------------===//
+// Section 0 — TableGen-generated descriptor tables
+//
+// These macros expand to the implementation bodies that the TableGen .td files
+// describe statically. They must appear at file scope, before any namespace.
+//
+// GET_REGINFO_TARGET_DESC — emits:
+// LX32GenRegisterInfo constructor (initialises register descriptor tables)
+// LX32GenRegisterInfo::getRegClassWeight(...)
+// LX32GenRegisterInfo::getRegUnitWeight(...)
+// ... and all other pure-table methods declared in the generated header.
+//===----------------------------------------------------------------------===//
+
+#define GET_REGINFO_TARGET_DESC
+#include "../TableGen/LX32GenRegisterInfo.inc"
+
+
+using namespace llvm;
+
+//===----------------------------------------------------------------------===//
+// Section 1 — Constructor
+//===----------------------------------------------------------------------===//
+
+LX32RegisterInfo::LX32RegisterInfo(unsigned HwMode)
+ // LX32GenRegisterInfo(RA, DwarfFlavour, EHFlavour, PC, HwMode)
+ // RA = X1 (ra) — the return-address register used by DWARF
+ // unwinders to locate the caller's return address.
+ // DwarfFlavour = 0 (default; no alternative DWARF numbering)
+ // EHFlavour = 0 (default; no separate EH register numbering)
+ // PC = 0 (no architecturally-visible program counter register)
+ // HwMode = hardware mode index from the subtarget
+ : LX32GenRegisterInfo(LX32::X1, 0, 0, 0, HwMode) {}
+
+//===----------------------------------------------------------------------===//
+// Section 2 — Reserved register set
+//
+// getReservedRegs returns a BitVector where bit N is set if physical register
+// N must never be assigned by the register allocator.
+//
+// Rationale for each reserved register:
+//
+// X0 (zero) — hardwired to zero by the processor. Writing it is a no-op.
+// Marking it reserved prevents the RA from wasting an
+// allocation slot on a register whose writes vanish.
+//
+// X1 (ra) — return address. FrameLowering saves/restores it in the
+// prologue/epilogue. If the RA could freely allocate ra, it
+// might spill a live value there, corrupting the return address.
+//
+// X2 (sp) — stack pointer. Only FrameLowering adjusts sp (ADDI sp,sp,N
+// in the prologue and epilogue). Allowing the RA to use sp as
+// a general register would corrupt the stack.
+//
+// X3 (gp) — global pointer. Initialised by the runtime linker stub
+// (crt0) to point at the .sdata/.sbss region. The RA must
+// never overwrite it.
+//
+// X4 (tp) — thread pointer. Points to the thread-local storage block.
+// Like gp, it is set by the OS/runtime and must not be clobbered.
+//
+// X8 (fp) — frame pointer. Reserved *only when* the function uses a
+// frame pointer (alloca, VLAs, or -fno-omit-frame-pointer).
+// When hasFP() is false, X8 is available as the callee-saved
+// register s0 and the RA may allocate it freely.
+//
+// All other registers (temporaries t0-t6, arguments a0-a7, callee-saved
+// s1-s11) are left unreserved and may be assigned by the RA subject to the
+// callee-saved rules in getCalleeSavedRegs().
+//===----------------------------------------------------------------------===//
+
+BitVector
+LX32RegisterInfo::getReservedRegs(const MachineFunction &MF) const {
+ BitVector Reserved(getNumRegs());
+
+ // x0/zero — hardwired to zero, writes silently discarded by hardware.
+ markSuperRegs(Reserved, LX32::X0);
+
+ // x1/ra — managed exclusively by FrameLowering (save in prologue, restore
+ // in epilogue). The RA must not use it as a scratch register.
+ markSuperRegs(Reserved, LX32::X1);
+
+ // x2/sp — stack pointer, adjusted only by FrameLowering.
+ markSuperRegs(Reserved, LX32::X2);
+
+ // x3/gp — global pointer, initialised by the runtime linker.
+ markSuperRegs(Reserved, LX32::X3);
+
+ // x4/tp — thread pointer, initialised by the OS/runtime.
+ markSuperRegs(Reserved, LX32::X4);
+
+ // x8/fp — reserved only when the function uses a dedicated frame pointer.
+ // hasFP() returns true when the function contains alloca() calls, variable-
+ // length arrays, or was compiled with -fno-omit-frame-pointer.
+ const auto *TFI =
+ static_cast<const LX32FrameLowering *>(MF.getSubtarget().getFrameLowering());
+ if (TFI && TFI->hasFP(MF))
+ markSuperRegs(Reserved, LX32::X8);
+
+ return Reserved;
+}
+
+//===----------------------------------------------------------------------===//
+// Section 3 — Callee-saved register set
+//
+// getCalleeSavedRegs returns a null-terminated array of physical registers
+// that the ABI requires a callee to preserve across calls.
+//
+// The ILP32 callee-saved set is defined in LX32CallingConv.td as
+// CSR_LX32_ILP32 and consists of:
+// X1 (ra) — return address
+// X8 (s0/fp) — frame pointer / callee-saved s0
+// X9 (s1) — callee-saved s1
+// X18-X27 (s2-s11) — callee-saved s2 through s11
+//
+// The RA uses this list to determine which registers it must insert
+// save/restore code for when it allocates them inside a function body.
+// FrameLowering reads the MachineFunction's CalleeSavedInfo (built from this
+// list) to emit the actual SW/LW instructions in the prologue and epilogue.
+//===----------------------------------------------------------------------===//
+
+const MCPhysReg *
+LX32RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
+ // The array is generated by TableGen from the CSR_LX32_ILP32 definition in
+ // LX32CallingConv.td. It is null-terminated so the caller can iterate
+ // without knowing the length in advance.
+ return CSR_LX32_ILP32_SaveList;
+}
+
+//===----------------------------------------------------------------------===//
+// Section 4 — Frame-index elimination
+//
+// eliminateFrameIndex is called once per MachineInstr operand that holds a
+// FrameIndex (FI) after register allocation. Its job is to replace the
+// abstract FI with a concrete (base-register, offset) pair.
+//
+// How frame indices work:
+// Before RA, the backend records each local variable / spill slot as a
+// FrameIndex — an integer index into the MachineFrameInfo table. The real
+// sp-relative byte offset is not known until FrameLowering::calculateFrameSize
+// runs (which happens after RA). eliminateFrameIndex is the bridge that
+// converts FI → real offset once the frame layout is finalised.
+//
+// The LX32 implementation handles two cases:
+//
+// Fast path — offset fits in simm12 (the common case):
+// The FI operand is replaced in-place with the base register (sp or fp)
+// and the adjacent immediate operand is set to the byte offset. No new
+// instructions are inserted.
+//
+// Slow path — offset exceeds simm12 range (very large frames, > 2 KB):
+// A scratch register is borrowed from the RegScavenger. The full base
+// address (base + offset) is materialised into the scratch register using
+// a LUI + ADD + ADDI sequence, and the instruction is rewritten to use
+// scratch as the base with an immediate of 0.
+//
+// Return value:
+// false — the caller (PrologEpilogInserter) should continue processing.
+// true — the instruction was fully rewritten; caller should not touch it
+// further. LX32 never returns true.
+//===----------------------------------------------------------------------===//
+
+bool LX32RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
+ int SPAdj, unsigned FIOperandNum,
+ RegScavenger *RS) const {
+ MachineInstr &MI = *II;
+ MachineFunction &MF = *MI.getMF();
+ MachineFrameInfo &MFI = MF.getFrameInfo();
+ const LX32FrameLowering *TFI =
+ static_cast<const LX32FrameLowering *>(MF.getSubtarget().getFrameLowering());
+
+ // --- Determine the concrete (base register, offset) for this frame index ---
+
+ int FrameIndex = MI.getOperand(FIOperandNum).getIndex();
+ Register FrameReg;
+
+ // getFrameIndexReference returns the offset from whichever register is used
+ // as the frame anchor (sp when hasFP() == false, fp otherwise), and sets
+ // FrameReg to that anchor register.
+ StackOffset Offset = TFI->getFrameIndexReference(MF, FrameIndex, FrameReg);
+
+ // Add any additional immediate offset already encoded in the instruction
+ // (e.g., LW rd, FI + 4 — the +4 is in the operand adjacent to the FI).
+ Offset += StackOffset::getFixed(MI.getOperand(FIOperandNum + 1).getImm());
+
+ // Add the stack-pointer adjustment accumulated by ADJCALLSTACKDOWN /
+ // ADJCALLSTACKUP pseudos that have not yet been eliminated.
+ Offset += StackOffset::getFixed(SPAdj);
+
+ int64_t OffsetVal = Offset.getFixed();
+
+ // -----------------------------------------------------------------------
+ // Fast path: offset fits in a 12-bit signed immediate (simm12).
+ // All LX32 load/store instructions use a simm12 offset field, so if the
+ // offset is in [-2048, 2047] we can rewrite directly without any new
+ // instructions.
+ // -----------------------------------------------------------------------
+ if (isInt<12>(OffsetVal)) {
+ MI.getOperand(FIOperandNum).ChangeToRegister(FrameReg, /*isDef=*/false);
+ MI.getOperand(FIOperandNum + 1).ChangeToImmediate(OffsetVal);
+ return false;
+ }
+
+ report_fatal_error(
+ "LX32RegisterInfo::eliminateFrameIndex: frame offset out of simm12 range");
+}
+
+//===----------------------------------------------------------------------===//
+// Section 5 — Frame register selection
+//
+// getFrameRegister returns the register that LLVM should treat as the
+// "canonical" frame anchor for this function.
+//
+// When the function uses a frame pointer (hasFP() == true), the frame pointer
+// register X8 (fp/s0) is the stable reference point throughout the function
+// body. This is necessary when the stack pointer moves dynamically (alloca,
+// variable-length arrays) because sp no longer has a fixed relationship to the
+// function's local variables.
+//
+// When the function does not use a frame pointer (the common case), the stack
+// pointer X2 (sp) is the anchor. LLVM's DWARF emitter uses this register
+// as the CFA (Canonical Frame Address) base for .debug_frame/.eh_frame.
+//===----------------------------------------------------------------------===//
+
+Register
+LX32RegisterInfo::getFrameRegister(const MachineFunction &MF) const {
+ const auto *TFI =
+ static_cast<const LX32FrameLowering *>(MF.getSubtarget().getFrameLowering());
+ return (TFI && TFI->hasFP(MF)) ? LX32::X8 : LX32::X2;
+}
\ No newline at end of file
diff --git a/llvm/lib/Target/LX32/core/LX32RegisterInfo.h b/llvm/lib/Target/LX32/core/LX32RegisterInfo.h
new file mode 100644
index 0000000000000..6c5d1888bfe0f
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32RegisterInfo.h
@@ -0,0 +1,166 @@
+//===-- LX32RegisterInfo.h - LX32 Register Info Interface ----------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares LX32RegisterInfo, the runtime register-management class
+// used by LLVM's code generation pipeline.
+//
+// It is organized into the following sections:
+//
+// Section 0 — Role in the backend pipeline
+// Section 1 — Class declaration and ABI hooks
+// Section 2 — Frame-index elimination
+// Section 3 — Utility and policy overrides
+//
+//===----------------------------------------------------------------------===//
+//
+// Section 0 — Role in the backend pipeline
+//
+// LLVM's register allocator (RA) needs two things the TableGen .td file alone
+// cannot provide:
+//
+// 1. A runtime BitVector of *reserved* registers — registers the RA must
+// never assign to a virtual register. Examples: x0 (hardwired zero),
+// x2 (sp), x3 (gp), x4 (tp). These are set in getReservedRegs().
+//
+// 2. A way to *resolve* abstract frame-index references after allocation.
+// Before RA, the backend uses symbolic frame indices (FI) instead of
+// concrete sp+offset addresses. After RA, eliminateFrameIndex() rewrites
+// each FI reference to "base register + offset", where the base is sp or
+// fp depending on whether the function uses a frame pointer.
+//
+// The TableGen-generated base class (LX32GenRegisterInfo) provides the static
+// descriptor tables — register enumeration, register classes, callee-saved
+// sets, DWARF register numbers, etc. LX32RegisterInfo adds the runtime logic
+// on top of that static foundation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LX32_LX32REGISTERINFO_H
+#define LX32_LX32REGISTERINFO_H
+
+#include "llvm/ADT/BitVector.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/Register.h"
+#include "llvm/CodeGen/TargetRegisterInfo.h"
+
+// Pull in generated register enums first so X0..X31 and *RegClassID are
+// visible everywhere this header is included.
+#define GET_REGINFO_ENUM
+#include "../TableGen/LX32GenRegisterInfo.inc"
+
+// Pull in the TableGen-generated LX32GenRegisterInfo class declaration.
+// GET_REGINFO_HEADER emits:
+// struct LX32GenRegisterInfo : public TargetRegisterInfo { ... };
+// namespace LX32 { extern const TargetRegisterClass GPRRegClass; ... }
+#define GET_REGINFO_HEADER
+#include "../TableGen/LX32GenRegisterInfo.inc"
+
+namespace llvm {
+
+class RegScavenger;
+
+//===----------------------------------------------------------------------===//
+// Section 1 — Class declaration and ABI hooks
+//===----------------------------------------------------------------------===//
+
+struct LX32RegisterInfo : public LX32GenRegisterInfo {
+ // Construct with the hardware mode index produced by TableGen.
+ // For LX32 v1 there is only one hardware mode (index 0), but passing the
+ // correct value keeps the API compatible with future multi-mode extensions.
+ explicit LX32RegisterInfo(unsigned HwMode);
+
+ //===--------------------------------------------------------------------===//
+ // ABI / calling-convention hooks
+ //===--------------------------------------------------------------------===//
+
+ // getCalleeSavedRegs — return the set of registers a function must preserve.
+ //
+ // The returned array is parallel with LX32CallingConv.td's CSR_* definitions:
+ // CSR_LX32_ILP32 : { X1(ra), X8(s0/fp), X9(s1), X18..X27(s2..s11) }
+ //
+ // The RA uses this list to decide which registers it must spill/restore in
+ // the function prologue/epilogue if it wants to use them.
+ const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override;
+
+ // getReservedRegs — return the BitVector of always-reserved registers.
+ //
+ // Reserved registers are never assigned by the RA, regardless of liveness.
+ // See Section 2 of LX32RegisterInfo.cpp for the complete rationale for each
+ // reserved register.
+ BitVector getReservedRegs(const MachineFunction &MF) const override;
+
+ //===--------------------------------------------------------------------===//
+ // Section 2 — Frame-index elimination (see .cpp for full implementation)
+ //===--------------------------------------------------------------------===//
+
+ // eliminateFrameIndex — rewrite a frame-index reference to base+offset.
+ //
+ // Called once for every MachineInstr operand that holds a FrameIndex after
+ // register allocation. The implementation in LX32RegisterInfo.cpp handles
+ // two sub-cases:
+ //
+ // Fast path (common): offset fits in simm12
+ // Replace FI operand with the base register (sp or fp) and set the
+ // adjacent immediate operand to the concrete byte offset.
+ //
+ // Slow path (large frame): offset does not fit in simm12
+ // Use the RegScavenger to find a free scratch register, materialise
+ // the full address with LUI+ADD+ADDI, and rewrite the instruction to
+ // use that scratch register as the base with immediate 0.
+ //
+ // Returns false in both cases (true would mean the instruction was
+ // completely rewritten and the caller should not touch it further — LX32
+ // never needs that path).
+ bool eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj,
+ unsigned FIOperandNum,
+ RegScavenger *RS = nullptr) const override;
+
+ // getFrameRegister — return the register that acts as the frame anchor.
+ //
+ // Returns X8 (fp) when the function uses a frame pointer (alloca, VLAs,
+ // -fno-omit-frame-pointer), or X2 (sp) otherwise. LLVM's DWARF CFI
+ // directives use this to describe where the caller's frame is.
+ Register getFrameRegister(const MachineFunction &MF) const override;
+
+ //===--------------------------------------------------------------------===//
+ // Section 3 — Utility and policy overrides
+ //===--------------------------------------------------------------------===//
+
+ // requiresRegisterScavenging — allow the RA to use a RegScavenger.
+ //
+ // Must be true so that eliminateFrameIndex's slow path (large-frame offsets)
+ // can call RS->scavengeRegisterBackwards() to borrow a scratch register.
+ // Without scavenging, large frames would have no way to materialise
+ // addresses that exceed the 12-bit simm range.
+ bool requiresRegisterScavenging(const MachineFunction &MF) const override {
+ return true;
+ }
+
+ // requiresFrameIndexScavenging — enable frame-index scavenging.
+ //
+ // When true, the RA pre-allocates a scavenge slot in the frame so that
+ // eliminateFrameIndex always has a valid slot to spill the scratch register
+ // into, even in the worst case where no register is free at the point of the
+ // large-frame access.
+ bool requiresFrameIndexScavenging(const MachineFunction &MF) const override {
+ return true;
+ }
+
+ // getPointerRegClass — return the register class for pointer-typed values.
+ //
+ // All pointer operations on LX32 use the 32-bit GPR class. This override
+ // ensures that the RA and legalization layers select GPR for i32* types.
+ const TargetRegisterClass *
+ getPointerRegClass(unsigned Kind = 0) const override {
+ return &LX32::GPRRegClass;
+ }
+};
+
+} // namespace llvm
+
+#endif // LX32_LX32REGISTERINFO_H
diff --git a/llvm/lib/Target/LX32/core/LX32Subtarget.cpp b/llvm/lib/Target/LX32/core/LX32Subtarget.cpp
new file mode 100644
index 0000000000000..502772afcd9f6
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32Subtarget.cpp
@@ -0,0 +1,98 @@
+//===-- LX32Subtarget.cpp - LX32 Subtarget Implementation ----------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements LX32Subtarget using TableGen-generated subtarget data.
+//
+// It is organized into the following sections:
+//
+// Section 0 — TableGen-generated constructor and feature parser
+// Section 1 — LX32Subtarget constructor
+//
+//===----------------------------------------------------------------------===//
+
+#include "LX32Subtarget.h"
+#include "LX32TargetMachine.h"
+
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "lx32-subtarget"
+
+//===----------------------------------------------------------------------===//
+// Section 0 — TableGen-generated constructor and feature parser
+//
+// Two macros expand to the concrete bodies generated from LX32Processors.td
+// and LX32.td. They must appear at file scope (not inside a namespace)
+// because the generated code opens its own `namespace llvm { ... }` blocks.
+//
+// GET_SUBTARGETINFO_CTOR
+// Emits the body of LX32GenSubtargetInfo::LX32GenSubtargetInfo(...).
+// This constructor passes the real scheduling tables (LX32WriteProcResTable,
+// LX32WriteLatencyTable, LX32ReadAdvanceTable) to TargetSubtargetInfo
+// instead of empty arrays. Without this macro, the linker reports:
+// undefined symbol: LX32GenSubtargetInfo::LX32GenSubtargetInfo(...)
+// It also emits LX32GenSubtargetInfo::resolveSchedClass and
+// resolveVariantSchedClass, which are required by the virtual dispatch
+// table of TargetSubtargetInfo.
+//
+// GET_SUBTARGETINFO_TARGET_DESC
+// Emits the body of LX32Subtarget::ParseSubtargetFeatures(...).
+// For LX32 v1 (no optional features) the generated body only prints debug
+// output — but the symbol must be present or the linker will fail:
+// undefined symbol: llvm::LX32Subtarget::ParseSubtargetFeatures(...)
+// When optional extensions are added to LX32Processors.td, the generated
+// body will also set the corresponding boolean feature-bit fields declared
+// in LX32Subtarget (e.g., HasMExtension).
+//===----------------------------------------------------------------------===//
+
+#define GET_SUBTARGETINFO_CTOR
+#define GET_SUBTARGETINFO_TARGET_DESC
+#include "../TableGen/LX32GenSubtargetInfo.inc"
+
+namespace llvm {
+
+//===----------------------------------------------------------------------===//
+// Section 1 — LX32Subtarget constructor
+//===----------------------------------------------------------------------===//
+
+LX32Subtarget::LX32Subtarget(const Triple &TT, StringRef CPU,
+ StringRef TuneCPU, StringRef FS,
+ const LX32TargetMachine &TM)
+ // Delegate to the TableGen-generated base constructor.
+ //
+ // LX32GenSubtargetInfo(TT, CPU, TuneCPU, FS) passes the real scheduling
+ // tables to TargetSubtargetInfo. This is important: the previous skeleton
+ // version constructed TargetSubtargetInfo directly with empty arrays, which
+ // caused the processor-name lookup to fail with:
+ // 'generic' is not a recognized processor for this target (ignoring)
+ // Using the generated constructor fixes that because it passes the real
+ // LX32SubTypeKV array (populated from LX32Processors.td) as the proc-desc.
+ : LX32GenSubtargetInfo(TT, CPU, TuneCPU, FS),
+ RegInfo(getHwMode()),
+ InstrInfo(*this),
+ FrameLowering(*this),
+ TLInfo(TM, *this)
+{
+
+ // Normalise the CPU name before calling ParseSubtargetFeatures.
+ //
+ // LX32TargetMachine::getSubtargetImpl already normalises the CPU string
+ // before constructing the subtarget, but we apply the same normalisation
+ // here as a defensive measure — the subtarget constructor can be called
+ // directly from tests or the MC layer where the normalisation may not have
+ // happened yet.
+ StringRef NormCPU = CPU.empty() ? "generic" : CPU;
+ StringRef NormTune = TuneCPU.empty() ? NormCPU : TuneCPU;
+
+ // ParseSubtargetFeatures is generated by GET_SUBTARGETINFO_TARGET_DESC above.
+ // It processes the feature string FS and sets feature-bit fields in *this.
+ // For LX32 v1, it is essentially a no-op (debug output only), but the call
+ // must be present so that feature plumbing works correctly when optional
+ // extensions are added.
+ ParseSubtargetFeatures(NormCPU, NormTune, FS);
+}
+
+} // namespace llvm
diff --git a/llvm/lib/Target/LX32/core/LX32Subtarget.h b/llvm/lib/Target/LX32/core/LX32Subtarget.h
new file mode 100644
index 0000000000000..f78435de91a15
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32Subtarget.h
@@ -0,0 +1,139 @@
+//===-- LX32Subtarget.h - LX32 Subtarget Declaration ---------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares LX32Subtarget, the class that holds per-function target
+// configuration and owns instances of the backend helper components.
+//
+// It is organized into the following sections:
+//
+// Section 0 — Role in the backend pipeline
+// Section 1 — TableGen-generated base class import
+// Section 2 — Class declaration and constructor
+// Section 3 — Component accessors
+//
+//===----------------------------------------------------------------------===//
+//
+// Section 0 — Role in the backend pipeline
+//
+// LX32Subtarget serves two purposes:
+//
+// 1. Feature container.
+// Holds the set of enabled ISA features (e.g., whether the M-extension
+// multiply instructions are available). For LX32 v1 there are no
+// optional features, but the infrastructure is in place. Features are
+// parsed from -mcpu / -mattr flags by ParseSubtargetFeatures, whose body
+// is generated by TableGen from LX32Processors.td.
+//
+// 2. Component owner.
+// Owns by-value instances of the four main backend helper classes:
+// LX32RegisterInfo — register descriptor + frame-index elimination
+// LX32InstrInfo — instruction helpers + pseudo expansion
+// LX32FrameLowering — prologue / epilogue generation
+// LX32TargetLowering — ISD operation legalisation (Day 9)
+// Owning them by value (not pointer) avoids a heap allocation per
+// subtarget and ties each component's lifetime to the subtarget.
+//
+// The subtarget is created by LX32TargetMachine::getSubtargetImpl and cached
+// in a StringMap keyed by (CPU, FeatureString). Different functions in the
+// same module can have different subtargets via target-cpu / target-features
+// function attributes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_LX32_CORE_LX32SUBTARGET_H
+#define LLVM_LIB_TARGET_LX32_CORE_LX32SUBTARGET_H
+
+#include "LX32FrameLowering.h"
+#include "LX32ISelLowering.h"
+#include "LX32InstrInfo.h"
+#include "LX32RegisterInfo.h"
+#include "llvm/ADT/StringRef.h"
+
+//===----------------------------------------------------------------------===//
+// Section 1 — TableGen-generated base class import
+//
+// GET_SUBTARGETINFO_HEADER emits the declaration of LX32GenSubtargetInfo,
+// which inherits from TargetSubtargetInfo and provides:
+// - The ParseSubtargetFeatures declaration (body emitted in the .cpp).
+// - resolveSchedClass / resolveVariantSchedClass for scheduling.
+// - DFAPacketizer support (unused in LX32 v1).
+//
+// This include must appear before any component headers because they may
+// reference types declared in the generated class.
+//===----------------------------------------------------------------------===//
+
+#define GET_SUBTARGETINFO_HEADER
+#include "../TableGen/LX32GenSubtargetInfo.inc"
+
+namespace llvm {
+
+class Triple;
+class LX32TargetMachine;
+
+//===----------------------------------------------------------------------===//
+// Section 2 — Class declaration and constructor
+//===----------------------------------------------------------------------===//
+
+class LX32Subtarget : public LX32GenSubtargetInfo {
+ LX32RegisterInfo RegInfo;
+ LX32InstrInfo InstrInfo;
+ LX32FrameLowering FrameLowering;
+ LX32TargetLowering TLInfo;
+
+public:
+ // Construct a subtarget for the given triple / CPU / feature combination.
+ //
+ // TT — target triple (used by the base class for OS/arch queries)
+ // CPU — CPU name from -mcpu (e.g., "generic-lx32")
+ // TuneCPU — tuning target from -mtune (may differ from CPU for scheduling)
+ // FS — feature string from -mattr (e.g., "+m" to enable multiply)
+ // TM — the owning TargetMachine (needed by LX32TargetLowering)
+ //
+ // The constructor normalises CPU to "generic-lx32" when the caller passes
+ // an empty string, preventing ParseSubtargetFeatures from warning about
+ // an unrecognised processor.
+ LX32Subtarget(const Triple &TT, StringRef CPU, StringRef TuneCPU,
+ StringRef FS, const LX32TargetMachine &TM);
+
+ // ParseSubtargetFeatures — parse -mcpu / -mattr flags into feature bits.
+ //
+ // The declaration is required by C++ even though the body is generated by
+ // TableGen (emitted when GET_SUBTARGETINFO_TARGET_DESC is active in the
+ // .cpp file). Do not implement this manually — the generated body is the
+ // source of truth.
+ //
+ // For LX32 v1 (no optional features) this is effectively a no-op beyond
+ // printing debug output. When optional extensions are added, the generated
+ // body will set the corresponding feature-bit fields automatically.
+ void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS);
+
+ //===--------------------------------------------------------------------===//
+ // Section 3 — Component accessors
+ //
+ // Override the pure-virtual hooks in TargetSubtargetInfo. Each returns
+ // a pointer to the corresponding by-value member once that component has
+ // been implemented; nullptr is returned until then.
+ //
+ // Build-day status (update this comment as each day completes):
+ // Day 4 (this file) — all return nullptr; skeleton compiles cleanly.
+ // Day 6 (RegisterInfo) — getRegisterInfo() returns &RegInfo.
+ // Day 7 (InstrInfo) — getInstrInfo() returns &InstrInfo.
+ // Day 8 (FrameLowering) — getFrameLowering() returns &FrameLowering.
+ // Day 9 (ISelLowering) — getTargetLowering() returns &TLInfo.
+ //===--------------------------------------------------------------------===//
+
+ const TargetRegisterInfo *getRegisterInfo() const override { return &RegInfo; }
+ const TargetInstrInfo *getInstrInfo() const override { return &InstrInfo; }
+ const TargetFrameLowering *getFrameLowering() const override {
+ return &FrameLowering;
+ }
+ const TargetLowering *getTargetLowering() const override { return &TLInfo; }
+};
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_LX32_CORE_LX32SUBTARGET_H
diff --git a/llvm/lib/Target/LX32/core/LX32TargetMachine.cpp b/llvm/lib/Target/LX32/core/LX32TargetMachine.cpp
new file mode 100644
index 0000000000000..fc29fa7063d5b
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32TargetMachine.cpp
@@ -0,0 +1,228 @@
+//===-- LX32TargetMachine.cpp - LX32 TargetMachine Implementation --------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements LX32TargetMachine, the entry point for the LX32 LLVM
+// backend.
+//
+// It is organized into the following sections:
+//
+// Section 0 — Data layout and relocation policy
+// Section 1 — Pass pipeline configuration (LX32PassConfig)
+// Section 2 — LX32TargetMachine methods
+// Section 3 — Target registration entry points
+//
+//===----------------------------------------------------------------------===//
+
+#include "LX32TargetMachine.h"
+#include "LX32ISelDAGToDAG.h"
+
+#include "../TargetInfo/LX32TargetInfo.h"
+
+#include "llvm/CodeGen/Passes.h"
+#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/CodeGen.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm;
+
+// Forward declaration — see Section 3.
+extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLX32TargetMC();
+extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLX32AsmPrinter();
+
+//===----------------------------------------------------------------------===//
+// Section 0 — Data layout and relocation policy
+//
+// The DataLayout string is the authoritative description of LX32's memory
+// model. The LLVM optimizer and code generator query it to determine type
+// sizes, alignments, and pointer widths. Getting it wrong silently produces
+// incorrect code, so every field is documented below:
+//
+// e — little-endian: the least significant byte has the lowest address.
+// LX32 is little-endian by design.
+//
+// m:e — ELF symbol mangling: LLVM uses ELF naming rules for symbols.
+// Required for the GNU linker and objdump to work correctly.
+//
+// p:32:32 — pointers are 32 bits wide, ABI-aligned to 32-bit (4-byte)
+// boundaries. This matches the ILP32 ABI: sizeof(void*) == 4.
+//
+// i64:64 — 64-bit integers are aligned to 64-bit (8-byte) boundaries.
+// This is critical for the ILP32 long-long convention: a 64-bit
+// value passed in a register pair (a0+a1 or a1+a2) requires the
+// low-word register to be even-indexed, which the calling
+// convention enforces only when i64 has 8-byte alignment.
+//
+// n32 — the native integer width is 32 bits. The optimizer uses this
+// to decide whether widening or narrowing operations are free.
+// On LX32, 32-bit arithmetic is single-instruction; 64-bit
+// arithmetic requires two-instruction sequences.
+//
+// S32 — the preferred stack alignment is 32 bits (4 bytes). Call sites
+// use a stricter 16-byte alignment (enforced in FrameLowering) to
+// keep sp aligned for any ABI-compliant callee.
+//
+//===----------------------------------------------------------------------===//
+
+static constexpr const char *LX32DataLayout = "e-m:e-p:32:32-i64:64-n32-S32";
+
+// getEffectiveRelocModel — select the relocation model for LX32.
+//
+// LX32 v1 supports only static relocation. Position-independent code (PIC)
+// requires a Global Offset Table (GOT) and Procedure Linkage Table (PLT),
+// neither of which is implemented yet. If the user explicitly requests PIC
+// (-fPIC), the backend falls back to static and the linker may warn.
+//
+// When PIC support is added:
+// return RM.value_or(Reloc::PIC_);
+static Reloc::Model
+getEffectiveRelocModel(std::optional<Reloc::Model> RM) {
+ return RM.value_or(Reloc::Static);
+}
+
+//===----------------------------------------------------------------------===//
+// Section 1 — Pass pipeline configuration
+//
+// LX32PassConfig installs the LX32-specific code generation passes in the
+// order the LLVM backend framework expects.
+//
+// The minimum viable pipeline for text-assembly output (-filetype=asm):
+// addInstSelector() — instruction selection (added Day 10)
+// (all other passes are provided by TargetPassConfig defaults)
+//
+// For object-file output (-filetype=obj), additional passes are required:
+// AsmPrinter — Day 12
+// MCCodeEmitter — Day 11
+//
+// The current implementation returns false from addInstSelector(), which
+// tells the framework that no custom selector was added. llc will report
+// "unable to select instruction" when it reaches instruction selection.
+// This is the expected failure mode for the skeleton backend — a diagnostic,
+// not a crash.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class LX32PassConfig final : public TargetPassConfig {
+public:
+ LX32PassConfig(LX32TargetMachine &TM, PassManagerBase &PM)
+ : TargetPassConfig(TM, PM) {}
+
+ LX32TargetMachine &getLX32TargetMachine() const {
+ return getTM<LX32TargetMachine>();
+ }
+
+ // addInstSelector — register the LX32 DAG selector.
+ bool addInstSelector() override {
+ addPass(createLX32ISelDag(getLX32TargetMachine(), getOptLevel()));
+ return false;
+ }
+};
+
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Section 2 — LX32TargetMachine methods
+//===----------------------------------------------------------------------===//
+
+namespace llvm {
+
+LX32TargetMachine::LX32TargetMachine(
+ const Target &T, const Triple &TT, StringRef CPU, StringRef FS,
+ const TargetOptions &Options, std::optional<Reloc::Model> RM,
+ std::optional<CodeModel::Model> CM, CodeGenOptLevel OL, bool JIT)
+ : CodeGenTargetMachineImpl(T, LX32DataLayout, TT, CPU, FS, Options,
+ getEffectiveRelocModel(RM),
+ // CodeModel::Small: all code and data fit in
+ // a single 32-bit address range; no medium or
+ // large model is needed for LX32 v1.
+ getEffectiveCodeModel(CM, CodeModel::Small), OL),
+ TLOF(std::make_unique<TargetLoweringObjectFileELF>())
+{
+ (void)JIT; // JIT compilation is not supported in LX32 v1.
+
+ // initAsmInfo — wire up MCAsmInfo, MCInstrInfo, MCRegisterInfo, and
+ // MCSubtargetInfo for this target machine. Must be called before
+ // createPassConfig; otherwise TargetPassConfig will assert when it tries
+ // to access the MC descriptors.
+ initAsmInfo();
+}
+
+LX32TargetMachine::~LX32TargetMachine() = default;
+
+// getSubtargetImpl — return the subtarget for the given function.
+//
+// The function's target-cpu and target-features attributes override the
+// module-level CPU and feature strings. This is how per-function
+// __attribute__((target("..."))) is implemented in LLVM.
+//
+// The SubtargetMap cache prevents reconstructing the subtarget tables on
+// every call. In the common case (no per-function attributes) every call
+// uses the same key and the map lookup returns the already-constructed entry.
+const LX32Subtarget *
+LX32TargetMachine::getSubtargetImpl(const Function &F) const {
+ Attribute CPUAttr = F.getFnAttribute("target-cpu");
+ Attribute FSAttr = F.getFnAttribute("target-features");
+
+ // Use the function attribute if present; fall back to the module-level value.
+ std::string CPU =
+ CPUAttr.isValid() ? CPUAttr.getValueAsString().str() : TargetCPU;
+ std::string FS =
+ FSAttr.isValid() ? FSAttr.getValueAsString().str() : TargetFS;
+
+ // Normalise the CPU name here as a second line of defence. The subtarget
+ // constructor also normalises, but doing it here avoids constructing a
+ // "generic" subtarget and then immediately discarding it from the cache.
+ if (CPU.empty())
+ CPU = "generic";
+
+ std::string Key = CPU + FS;
+ auto &Entry = SubtargetMap[Key];
+ if (!Entry)
+ Entry = std::make_unique<LX32Subtarget>(TargetTriple, CPU,
+ /*TuneCPU=*/CPU, FS, *this);
+ return Entry.get();
+}
+
+TargetPassConfig *
+LX32TargetMachine::createPassConfig(PassManagerBase &PM) {
+ return new LX32PassConfig(*this, PM);
+}
+
+//===----------------------------------------------------------------------===//
+// Section 3 — Target registration entry points
+//
+// LLVM discovers targets through a pair of extern "C" init functions whose
+// names are constructed as LLVMInitialize<Target><Layer>():
+//
+// LLVMInitializeLX32TargetInfo — registers the target name and triple
+// (defined in target/TargetInfo/)
+// LLVMInitializeLX32TargetMC — registers MC-layer factories: MCAsmInfo,
+// MCInstrInfo, MCRegisterInfo, MCSubtargetInfo,
+// MCInstPrinter (defined in mc/)
+// LLVMInitializeLX32Target — registers LX32TargetMachine (this function)
+//
+// The LLVM build system generates calls to these functions in the tool
+// initialisation path. The order matters: TargetInfo must be initialised
+// before TargetMC, and TargetMC before Target. We enforce this by calling
+// LLVMInitializeLX32TargetMC explicitly from LLVMInitializeLX32Target.
+//===----------------------------------------------------------------------===//
+
+extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
+LLVMInitializeLX32Target() {
+ // Ensure the MC-layer registrations are in place before TargetMachine
+ // construction. initAsmInfo() (called from our constructor) will assert
+ // if MCAsmInfo is not yet registered.
+ LLVMInitializeLX32TargetMC();
+ LLVMInitializeLX32AsmPrinter();
+
+ // Register LX32TargetMachine as the handler for the "lx32" architecture.
+ // After this call, `llc -march=lx32` constructs an LX32TargetMachine.
+ RegisterTargetMachine<LX32TargetMachine> X(getTheLX32TargetInfo());
+}
+
+} // namespace llvm
diff --git a/llvm/lib/Target/LX32/core/LX32TargetMachine.h b/llvm/lib/Target/LX32/core/LX32TargetMachine.h
new file mode 100644
index 0000000000000..61d5ed1e0ac22
--- /dev/null
+++ b/llvm/lib/Target/LX32/core/LX32TargetMachine.h
@@ -0,0 +1,160 @@
+//===-- LX32TargetMachine.h - LX32 TargetMachine Declaration -------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares LX32TargetMachine, the top-level entry point for the
+// LX32 LLVM backend.
+//
+// It is organized into the following sections:
+//
+// Section 0 — Role in the backend pipeline
+// Section 1 — Includes and dependencies
+// Section 2 — Class declaration
+// Section 3 — Public interface
+//
+//===----------------------------------------------------------------------===//
+//
+// Section 0 — Role in the backend pipeline
+//
+// LX32TargetMachine is the first object the LLVM driver instantiates when it
+// targets LX32 (via -march=lx32 or a lx32-unknown-elf triple). It is
+// responsible for three things:
+//
+// 1. Global target configuration.
+// Stores the DataLayout string, relocation model, code model, and
+// optimization level. These values are immutable after construction and
+// are shared by all functions compiled in the same module.
+//
+// DataLayout "e-m:e-p:32:32-i64:64-n32-S32" encodes:
+// e — little-endian byte order
+// m:e — ELF symbol mangling
+// p:32:32 — 32-bit pointers, 32-bit ABI alignment
+// i64:64 — 64-bit integers aligned to 8 bytes
+// n32 — the native integer width is 32 bits
+// S32 — minimum stack alignment is 4 bytes (call sites use 16)
+//
+// 2. Subtarget management.
+// Creates and caches LX32Subtarget instances. A subtarget encapsulates
+// the combination of CPU and feature flags for a particular function.
+// Because LLVM allows per-function target overrides via function
+// attributes (e.g., __attribute__((target("cpu=fast-lx32")))), the
+// TargetMachine maintains a StringMap cache rather than a single global
+// subtarget.
+//
+// 3. Pass pipeline construction.
+// createPassConfig returns a TargetPassConfig subclass (LX32PassConfig)
+// that registers the LX32-specific code generation passes in the correct
+// order. The most important pass is the instruction selector, added in
+// LX32PassConfig::addInstSelector (Day 10).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_LX32_CORE_LX32TARGETMACHINE_H
+#define LLVM_LIB_TARGET_LX32_CORE_LX32TARGETMACHINE_H
+
+//===----------------------------------------------------------------------===//
+// Section 1 — Includes and dependencies
+//===----------------------------------------------------------------------===//
+
+// LX32Subtarget must be included before CodeGenTargetMachineImpl because the
+// subtarget cache uses std::unique_ptr<LX32Subtarget>, which requires the
+// complete type at the point of the StringMap declaration.
+#include "LX32Subtarget.h"
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/CodeGen/CodeGenTargetMachineImpl.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/IR/Function.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
+#include "llvm/TargetParser/Triple.h"
+
+#include <memory>
+#include <optional>
+
+namespace llvm {
+
+//===----------------------------------------------------------------------===//
+// Section 2 — Class declaration
+//===----------------------------------------------------------------------===//
+
+class LX32TargetMachine final : public CodeGenTargetMachineImpl {
+ // TLOF — target lowering object file.
+ //
+ // Provides the MC-layer object-file policies: section naming, relocation
+ // kinds, debug info format, etc. For LX32 v1, we use the standard ELF
+ // implementation (TargetLoweringObjectFileELF) because LX32 targets Linux-
+ // style ELF binaries.
+ //
+ // Owned via unique_ptr because TargetLoweringObjectFile is polymorphic.
+ std::unique_ptr<TargetLoweringObjectFile> TLOF;
+
+ // SubtargetMap — cache of constructed subtargets.
+ //
+ // Key: CPU + FeatureString (concatenated, used as a single opaque key).
+ // Value: the LX32Subtarget instance for that combination.
+ //
+ // Most compilations use a single subtarget (the module-level -mcpu / -mattr
+ // combination). The cache exists to handle the rare case of per-function
+ // target attributes without rebuilding the subtarget on every getSubtargetImpl
+ // call.
+ //
+ // Declared mutable because getSubtargetImpl is const (required by the LLVM
+ // API) but may insert into the map on the first call for a new key.
+ mutable StringMap<std::unique_ptr<LX32Subtarget>> SubtargetMap;
+
+public:
+ //===--------------------------------------------------------------------===//
+ // Section 3 — Public interface
+ //===--------------------------------------------------------------------===//
+
+ // Constructor — initialise the target machine with the given parameters.
+ //
+ // T — the Target object registered by LLVMInitializeLX32TargetInfo
+ // TT — the target triple (lx32-unknown-elf, lx32-linux-elf, etc.)
+ // CPU — the CPU string from -mcpu (empty → "generic-lx32")
+ // FS — the feature string from -mattr
+ // Options — optimisation-level flags from the driver
+ // RM — relocation model (Static for LX32 v1; PIC is not yet supported)
+ // CM — code model (Small; the entire image fits in a single 32-bit range)
+ // OL — optimisation level (None / Less / Default / Aggressive)
+ // JIT — true when constructing for JIT use (not supported in LX32 v1)
+ LX32TargetMachine(const Target &T, const Triple &TT, StringRef CPU,
+ StringRef FS, const TargetOptions &Options,
+ std::optional<Reloc::Model> RM,
+ std::optional<CodeModel::Model> CM,
+ CodeGenOptLevel OL, bool JIT);
+
+ ~LX32TargetMachine() override;
+
+ // getSubtargetImpl — return the subtarget for the given function.
+ //
+ // Reads target-cpu and target-features function attributes and returns the
+ // cached subtarget for that combination, constructing it on first call.
+ //
+ // The returned pointer is valid as long as this TargetMachine lives.
+ const LX32Subtarget *getSubtargetImpl(const Function &F) const override;
+
+ // createPassConfig — build the code generation pass pipeline.
+ //
+ // Returns a LX32PassConfig instance that registers the LX32-specific
+ // passes in the correct order. Called once per module by the LLVM driver.
+ TargetPassConfig *createPassConfig(PassManagerBase &PM) override;
+
+ // getObjFileLowering — return the ELF object-file lowering instance.
+ //
+ // Used by AsmPrinter and the MC layer to determine section placements,
+ // relocation kinds, and debug-info formats.
+ TargetLoweringObjectFile *getObjFileLowering() const override {
+ return TLOF.get();
+ }
+};
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_LX32_CORE_LX32TARGETMACHINE_H
diff --git a/llvm/lib/Target/LX32/target/LX32Target.cpp b/llvm/lib/Target/LX32/target/LX32Target.cpp
new file mode 100644
index 0000000000000..625bd2bfb2104
--- /dev/null
+++ b/llvm/lib/Target/LX32/target/LX32Target.cpp
@@ -0,0 +1,55 @@
+//===-- LX32Target.cpp - LX32 Target Initialization -----------------------===//
+//
+// Part of the LX32 Project
+// SPDX-License-Identifier: MIT
+//
+// This file is the single entry-point LLVM uses to initialize the target.
+//
+// Why this exists:
+// LLVM splits target initialization into multiple optional layers:
+// - TargetInfo (Target triple/name registration)
+// - Target (TargetMachine registration)
+// - TargetMC (MC layer: asm info, instr info, inst printer, ...)
+//
+// If TargetMC isn't initialized, llc can still *recognize* -march=lx32 but
+// may crash later when it tries to create an assembly streamer.
+//
+// Keeping this file tiny and explicit makes the backend robust and avoids
+// "half-registered" targets.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the Target-layer initialization entry point for LX32.
+// It is organized into the following sections:
+//
+// Section 0 — External includes and registration dependencies
+// Section 1 — LLVMInitializeLX32Target implementation
+//
+//===----------------------------------------------------------------------===//
+
+#include "TargetInfo/LX32TargetInfo.h"
+
+#include "../core/LX32TargetMachine.h"
+
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/Compiler.h"
+
+// This file provides the *Target* layer entry point.
+//
+// Important:
+// Do NOT try to call the function from itself to "bundle" initialization.
+// LLVM drivers (llc/opt/clang) will call the individual init functions:
+// - LLVMInitializeLX32TargetInfo()
+// - LLVMInitializeLX32Target()
+// - LLVMInitializeLX32TargetMC()
+//
+// The MC layer initializer lives in mc/LX32MCTargetDesc.cpp.
+
+extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLX32Target() {
+ // Register the TargetMachine so llc/clang can construct it for -march=lx32.
+ //
+ // Keep this boring and explicit: all target policy/configuration belongs in
+ // LX32TargetMachine; this file should only perform registration.
+ RegisterTargetMachine<llvm::LX32TargetMachine> X(llvm::getTheLX32TargetInfo());
+}
+
>From f02c91831e01548f3530ada2d30f1b42bc5ceabd Mon Sep 17 00:00:00 2001
From: Axel Ibarrondo <135344010+Axel84727 at users.noreply.github.com>
Date: Fri, 10 Apr 2026 09:02:29 -0300
Subject: [PATCH 3/3] fix: fixed lX32 typo to LX32 on CMakeLists.txt
---
llvm/CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
index 93490779207e6..be111beaf46d0 100644
--- a/llvm/CMakeLists.txt
+++ b/llvm/CMakeLists.txt
@@ -575,7 +575,7 @@ set(LLVM_ALL_TARGETS
AVR
BPF
Hexagon
- lX32
+ LX32
Lanai
LoongArch
Mips
More information about the llvm-commits
mailing list