[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