Checkpoint.
This commit is contained in:
commit
873eab4922
68 changed files with 6998 additions and 0 deletions
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Ephemeral: regenerable via setup.sh
|
||||
tools/
|
||||
.cache/
|
||||
|
||||
# Claude Code tool state
|
||||
.claude/
|
||||
|
||||
# Editor / OS
|
||||
*.swp
|
||||
*.swo
|
||||
.DS_Store
|
||||
478
LLVM_65816_DESIGN.md
Normal file
478
LLVM_65816_DESIGN.md
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
# LLVM 65816 Backend for Apple IIgs
|
||||
## Project Design and Handoff Document
|
||||
|
||||
---
|
||||
|
||||
## 1. Project Goal
|
||||
|
||||
Build an optimized Clang/LLVM C compiler backend targeting the WDC 65816
|
||||
processor, specifically for Apple IIgs development. The backend must produce
|
||||
genuinely optimized output — not just correct code, but tight, cycle-efficient
|
||||
65816 assembly that takes full advantage of the architecture.
|
||||
|
||||
ORCA/C is the existing open source option and its code generation quality is
|
||||
poor. Calypsi is the commercial alternative with good output quality but is
|
||||
closed source. This project aims to produce an open source backend that matches
|
||||
or exceeds Calypsi's output quality.
|
||||
|
||||
---
|
||||
|
||||
## 2. Background and Context
|
||||
|
||||
### 2.1 The 65816 Architecture
|
||||
|
||||
The WDC W65C816S is a 16-bit microprocessor used in the Apple IIgs (and SNES).
|
||||
Key characteristics relevant to code generation:
|
||||
|
||||
- **Dynamic register width:** The A (accumulator), X, and Y registers can be
|
||||
either 8-bit or 16-bit depending on the M and X bits in the processor status
|
||||
register. Mode switching is done via REP (Reset bits) and SEP (Set bits)
|
||||
instructions. REP #$20 sets 16-bit accumulator mode; SEP #$20 sets 8-bit.
|
||||
REP #$10 sets 16-bit index registers; SEP #$10 sets 8-bit.
|
||||
|
||||
- **Direct page:** A relocatable 256-byte window in bank 0 RAM, similar to
|
||||
zero page on the 6502 but moveable. The Direct Page Register (DP) holds the
|
||||
base address. Direct page addressing saves one byte per instruction and one
|
||||
cycle over absolute addressing. Highly valuable for hot variables.
|
||||
|
||||
- **24-bit address space:** Addresses are bank:offset. The Data Bank Register
|
||||
(DBR) implicitly provides the bank for 16-bit absolute data accesses. The
|
||||
Program Bank Register (PBR) provides the bank for code. Long (24-bit)
|
||||
addressing is available for cross-bank access.
|
||||
|
||||
- **Stack:** 16-bit stack pointer in bank 0. Stack-relative addressing is
|
||||
available but expensive.
|
||||
|
||||
- **Native vs. emulation mode:** At reset the CPU starts in emulation mode
|
||||
(behaves like 65C02). Native mode (16-bit capable) is entered by clearing
|
||||
the emulation bit. All IIgs native code runs in native mode.
|
||||
|
||||
### 2.2 Why This Is Hard for LLVM
|
||||
|
||||
LLVM's register allocator assumes fixed-width registers. The 65816's dynamic
|
||||
register widths are a fundamental mismatch. Key problems:
|
||||
|
||||
1. **REP/SEP scheduling:** Optimal code minimizes mode switches since each
|
||||
REP/SEP costs 3 cycles. The backend must decide register width per
|
||||
region of code, coalescing regions that use the same width to minimize
|
||||
transitions. This is a global dataflow problem.
|
||||
|
||||
2. **Direct page as a register file:** LLVM has no model for a relocatable
|
||||
zero page. Hot variables should be allocated to direct page offsets, but
|
||||
GS/OS (the IIgs operating system) reserves certain direct page locations
|
||||
for its own use. The allocator must know which regions are safe.
|
||||
|
||||
3. **Bank register management:** DBR affects all 16-bit absolute data accesses.
|
||||
The backend must ensure DBR is set correctly for cross-bank data access.
|
||||
|
||||
4. **24-bit address space in 32-bit ELF:** LLVM's ELF format uses 32-bit
|
||||
addresses. The llvm-mos project has already defined an ELF extension for
|
||||
MOS processors that handles this correctly (ELFCLASS32 with unused high
|
||||
bits zeroed).
|
||||
|
||||
5. **GS/OS calling conventions:** Apple IIgs toolbox calls use a stack-based
|
||||
parameter protocol different from any standard calling convention LLVM
|
||||
knows. Custom lowering is required for toolbox calls.
|
||||
|
||||
### 2.3 Existing Work
|
||||
|
||||
- **llvm-mos:** An open source LLVM fork providing first-class support for
|
||||
MOS Technology 65xx processors. The 6502 backend is production quality.
|
||||
The 65816 assembler (llvm-mc) has partial support — direct page assumed
|
||||
at 0x0000, 24-bit long addressing supported, 16-bit immediates supported.
|
||||
The **compiler backend** (C to 65816) is incomplete. Issues #32 and #321
|
||||
in the llvm-mos GitHub track this work. The core blocker is REP/SEP
|
||||
register width management.
|
||||
|
||||
- **jeremysrand/llvm-65816:** A separate LLVM backend attempt specifically
|
||||
targeting Apple IIgs, aiming to output Merlin 32-compatible assembly.
|
||||
The repo explicitly states "Don't even try to use it yet" and appears
|
||||
stalled.
|
||||
|
||||
- **WorldsApartDevTeam/65816-c:** Claims C11 compliance for 65C816. Only
|
||||
2 stars, 28 commits, no releases. Very early stage.
|
||||
|
||||
- **Calypsi:** Commercial closed-source compiler, version 5.16 released
|
||||
April 15, 2026. Best available 65816 C compiler. Free for hobby use.
|
||||
Use its output as a quality benchmark.
|
||||
|
||||
- **ORCA/C:** Open source, runs on-machine or via emulator. Mature but
|
||||
generates poor code. The baseline we need to beat significantly.
|
||||
|
||||
### 2.4 llvm-mos as the Foundation
|
||||
|
||||
This project is built on top of llvm-mos, not vanilla LLVM:
|
||||
|
||||
- llvm-mos already has the MOS ELF specification implemented
|
||||
- It has the 6502 backend as a reference for 65xx-family conventions
|
||||
- It has the assembler-level 65816 support to build on
|
||||
- It has the SDK infrastructure for platform-specific runtime support
|
||||
- GitHub: https://github.com/llvm-mos/llvm-mos
|
||||
|
||||
### 2.5 Architectural Decision: Separate W65816 Target
|
||||
|
||||
llvm-mos already defines `FeatureW65816` as a subtarget feature of MOS, and
|
||||
tracks the missing codegen support in issue #321. We could have added 16-bit
|
||||
register handling to the existing MOS target as a subtarget feature.
|
||||
|
||||
**We did not.** This project is a **separate W65816 target**, maintained as
|
||||
our own fork of llvm-mos. Reasons:
|
||||
|
||||
- Clean register model. MOS's register classes assume 8-bit hardware; aliasing
|
||||
A/X/Y across 8 and 16-bit widths inside MOS would require invasive changes
|
||||
to an already-shipping target. A fresh target can define
|
||||
`Acc8`/`Acc16`/`Idx8`/`Idx16` classes directly and let the REP/SEP pass
|
||||
operate at the MIR level.
|
||||
- Independent evolution. This codebase can move at its own pace without
|
||||
coordinating against llvm-mos's 6502 stability guarantees.
|
||||
- No upstream burden. We are not attempting to land this in llvm-mos; this is
|
||||
not a subtarget-feature extension to MOS, it is a sibling target.
|
||||
|
||||
The MOS target remains available in the same source tree, unchanged. We
|
||||
borrow infrastructure (ELF writer, MC layer patterns) but implement our own
|
||||
registers, instructions, scheduling, and lowering.
|
||||
|
||||
---
|
||||
|
||||
## 3. Architecture
|
||||
|
||||
### 3.1 LLVM Backend Structure
|
||||
|
||||
A standard LLVM backend consists of:
|
||||
|
||||
```
|
||||
Clang frontend
|
||||
│
|
||||
▼
|
||||
LLVM IR (architecture-independent)
|
||||
│
|
||||
▼
|
||||
Target-specific lowering (SelectionDAG or GlobalISel)
|
||||
│
|
||||
▼
|
||||
Machine IR (MIR)
|
||||
│
|
||||
▼
|
||||
Register allocation
|
||||
│
|
||||
▼
|
||||
Instruction scheduling
|
||||
│
|
||||
▼
|
||||
Code emission (assembly or object file)
|
||||
```
|
||||
|
||||
The backend adds:
|
||||
- **TableGen definitions:** Register file, instruction encodings, calling
|
||||
conventions, operand types, instruction selection patterns
|
||||
- **Target machine:** Configuration, subtarget features, data layout
|
||||
- **Instruction selector:** Lowers LLVM IR operations to 65816 instructions
|
||||
- **Register allocator customization:** Handles the special register width
|
||||
and direct page constraints
|
||||
- **Pass pipeline:** Custom optimization passes for REP/SEP scheduling and
|
||||
direct page allocation
|
||||
|
||||
### 3.2 Register File Definition
|
||||
|
||||
```tablegen
|
||||
// Processor status register bits
|
||||
def MBit : Register<"m">; // accumulator width (0=16-bit, 1=8-bit)
|
||||
def XBit : Register<"x">; // index width (0=16-bit, 1=8-bit)
|
||||
|
||||
// Main registers
|
||||
def A : Register<"a">; // accumulator (8 or 16-bit)
|
||||
def X : Register<"x">; // index X (8 or 16-bit)
|
||||
def Y : Register<"y">; // index Y (8 or 16-bit)
|
||||
def SP : Register<"sp">; // stack pointer (16-bit)
|
||||
def DP : Register<"dp">; // direct page register (16-bit)
|
||||
def DBR : Register<"dbr">; // data bank register (8-bit)
|
||||
def PBR : Register<"pbr">; // program bank register (8-bit)
|
||||
def PC : Register<"pc">; // program counter (16-bit)
|
||||
def P : Register<"p">; // processor status
|
||||
|
||||
// Register classes
|
||||
def Acc8 : RegisterClass<"W65816", [i8], 8, (add A)>;
|
||||
def Acc16 : RegisterClass<"W65816", [i16], 16, (add A)>;
|
||||
def Idx8 : RegisterClass<"W65816", [i8], 8, (add X, Y)>;
|
||||
def Idx16 : RegisterClass<"W65816", [i16], 16, (add X, Y)>;
|
||||
```
|
||||
|
||||
### 3.3 REP/SEP Scheduling Strategy
|
||||
|
||||
The core algorithmic challenge. Proposed approach:
|
||||
|
||||
**Phase 1 — Width inference:** For each basic block, determine the "natural"
|
||||
width of each operation: loads/stores of i8 values prefer 8-bit mode, i16
|
||||
values prefer 16-bit mode.
|
||||
|
||||
**Phase 2 — Width coalescing:** Run a dataflow analysis across the CFG. Assign
|
||||
each basic block a preferred width that minimizes the total number of mode
|
||||
switches on all edges. This is a minimum cut / graph partitioning problem.
|
||||
A greedy approach works well in practice.
|
||||
|
||||
**Phase 3 — Transition insertion:** At points where the width changes, insert
|
||||
REP or SEP pseudo-instructions. These are later lowered to real REP/SEP.
|
||||
|
||||
**Phase 4 — Peephole cleanup:** Eliminate redundant REP/SEP pairs. If a
|
||||
block ends with SEP #$20 and the successor starts with REP #$20, and no
|
||||
path skips the block, one of them is redundant.
|
||||
|
||||
### 3.4 Direct Page Allocation
|
||||
|
||||
A custom LLVM pass runs after register allocation:
|
||||
|
||||
1. Identify the GS/OS-safe direct page region. GS/OS uses $00-$9F for
|
||||
toolbox and system use. The safe region for user code is $A0-$FF
|
||||
(96 bytes). This may need to be configurable per project.
|
||||
|
||||
2. Score all local variables and spill slots by access frequency (using
|
||||
LLVM's block frequency analysis).
|
||||
|
||||
3. Greedily allocate the highest-frequency variables to direct page
|
||||
offsets, packing them tightly to fit within the 96-byte budget.
|
||||
|
||||
4. Rewrite all accesses to direct-page-allocated variables to use DP
|
||||
addressing mode instead of stack-relative or absolute addressing.
|
||||
|
||||
### 3.5 Calling Convention
|
||||
|
||||
Standard function calls:
|
||||
- Parameters passed on stack (push right-to-left, caller cleans)
|
||||
- Return value in A (8-bit) or A (16-bit) or A:X (32-bit)
|
||||
- Callee saves: DP, DBR, direct page contents if modified
|
||||
- Stack frame: return address (3 bytes in native mode), then locals
|
||||
|
||||
GS/OS toolbox calls:
|
||||
- Parameters pushed as a parameter block
|
||||
- Tool call via JSL $E10000 (tool dispatcher)
|
||||
- Custom call lowering needed — mark with a special ISD node and lower
|
||||
to JSL with the correct tool number encoding
|
||||
|
||||
### 3.6 Memory Model
|
||||
|
||||
The Apple IIgs memory map relevant to code generation:
|
||||
|
||||
```
|
||||
Bank 00: $0000-$01FF Zero page + stack (system reserved)
|
||||
$0200-$9FFF User RAM (conventional)
|
||||
$A000-$BFFF User RAM or LC bank
|
||||
$C000-$CFFF I/O space
|
||||
$D000-$FFFF ROM / language card
|
||||
Bank 01: $0000-$FFFF Additional RAM
|
||||
Bank E0: $0000-$FFFF Mega II (Apple //e on a chip) I/O space
|
||||
Bank E1: $0000-$FFFF Video/reserved
|
||||
Bank FC-FF: ROM banks
|
||||
```
|
||||
|
||||
The default code model places code in bank 0 (or bank 1 for large programs)
|
||||
and data in bank 0. Long (24-bit) pointers are needed for cross-bank access.
|
||||
|
||||
---
|
||||
|
||||
## 4. Key Optimizations
|
||||
|
||||
### 4.1 Register Width Selection (highest impact)
|
||||
|
||||
Using 16-bit accumulator for word operations vs. two 8-bit operations cuts
|
||||
cycle counts roughly in half for arithmetic. The REP/SEP scheduling described
|
||||
above is the primary mechanism.
|
||||
|
||||
### 4.2 Direct Page Promotion (second highest impact)
|
||||
|
||||
Direct page addressing saves 1 byte and 1 cycle per instruction versus
|
||||
absolute addressing. For variables accessed in tight loops, this compounds
|
||||
significantly. The allocation pass described above handles this.
|
||||
|
||||
### 4.3 Addressing Mode Selection
|
||||
|
||||
The instruction selector must choose the most efficient addressing mode:
|
||||
- Direct page (8-bit offset): preferred for hot variables
|
||||
- Absolute (16-bit address): for global/static data in same bank
|
||||
- Long (24-bit address): for cross-bank data access
|
||||
- Stack-relative: avoid where possible, expensive on 65816
|
||||
- Indexed: X or Y register indexed variants of the above
|
||||
|
||||
### 4.4 MVN/MVP for Block Moves
|
||||
|
||||
The 65816 has block move instructions (MVN = move negative, MVP = move
|
||||
positive) that move memory efficiently. These map to LLVM's memcpy/memmove
|
||||
intrinsics. The backend should recognize and emit these for small to medium
|
||||
copies rather than emitting a loop.
|
||||
|
||||
### 4.5 Stack Frame Minimization
|
||||
|
||||
Minimize spills to the stack — they use expensive stack-relative addressing.
|
||||
Prefer direct page allocation for spill slots (via the DP allocator above).
|
||||
|
||||
---
|
||||
|
||||
## 5. Testing Strategy
|
||||
|
||||
### 5.1 MAME Lua Interface for Automated Testing
|
||||
|
||||
MAME's Lua scripting interface allows automated correctness testing against
|
||||
real IIgs hardware emulation.
|
||||
|
||||
Test harness workflow:
|
||||
1. Compile a C test case with the backend
|
||||
2. Load the binary into MAME's IIgs emulation
|
||||
3. A Lua script sets breakpoints, runs the code, checks register and
|
||||
memory state against expected values
|
||||
4. Report pass/fail
|
||||
|
||||
Example Lua test script structure:
|
||||
```lua
|
||||
emu.add_machine_reset_notifier(function()
|
||||
local cpu = manager.machine.devices[":maincpu"]
|
||||
|
||||
-- breakpoint at end of test function
|
||||
cpu.debug:bpset(0x010200, "", function()
|
||||
local a = cpu.state["A"].value
|
||||
local p = cpu.state["P"].value
|
||||
local mBit = (p >> 5) & 1 -- M bit: 0=16-bit, 1=8-bit
|
||||
|
||||
-- validate result and register width state
|
||||
if a ~= 0x1234 then
|
||||
print(string.format("FAIL: A=0x%04X expected 0x1234", a))
|
||||
elseif mBit ~= 0 then
|
||||
print("FAIL: expected 16-bit accumulator mode")
|
||||
else
|
||||
print("PASS")
|
||||
end
|
||||
manager.machine:exit()
|
||||
end)
|
||||
end)
|
||||
```
|
||||
|
||||
REP/SEP tracing — validate mode switch sequences:
|
||||
```lua
|
||||
-- trace all REP/SEP instructions to validate width scheduling
|
||||
cpu.debug:wpset(0x000000, 1, "x", "", function()
|
||||
-- check if instruction at PC is REP or SEP
|
||||
local pc = cpu.state["PC"].value
|
||||
local opcode = manager.machine.spaces["program"]:read_u8(pc)
|
||||
if opcode == 0xC2 then -- REP
|
||||
local operand = manager.machine.spaces["program"]:read_u8(pc+1)
|
||||
print(string.format("REP #$%02X at $%04X", operand, pc))
|
||||
elseif opcode == 0xE2 then -- SEP
|
||||
local operand = manager.machine.spaces["program"]:read_u8(pc+1)
|
||||
print(string.format("SEP #$%02X at $%04X", operand, pc))
|
||||
end
|
||||
end)
|
||||
```
|
||||
|
||||
### 5.2 Calypsi Output Comparison
|
||||
|
||||
Compile the same C code with Calypsi and with our backend. Diff the assembly
|
||||
output. Cases where our output is significantly worse (more instructions,
|
||||
more REP/SEP transitions, missing direct page usage) are optimization bugs.
|
||||
|
||||
### 5.3 Test Categories
|
||||
|
||||
- Basic arithmetic (8-bit and 16-bit, mixed width)
|
||||
- Pointer operations (direct page, absolute, long)
|
||||
- Function calls (stack frame setup/teardown)
|
||||
- GS/OS toolbox calls
|
||||
- Struct/array access
|
||||
- Mixed 8/16-bit operations (the REP/SEP stress test)
|
||||
- memcpy/memset (MVN/MVP emission)
|
||||
- Interrupt handlers (bank register save/restore)
|
||||
|
||||
---
|
||||
|
||||
## 6. GS/OS Integration
|
||||
|
||||
### 6.1 Runtime Library
|
||||
|
||||
A minimal runtime library is needed:
|
||||
- `crt0.s`: startup code, sets native mode, initializes DP and DBR,
|
||||
sets up stack, calls `main()`
|
||||
- `libc`: subset of C standard library functions adapted for GS/OS
|
||||
- GS/OS system call wrappers (file I/O, memory management, etc.)
|
||||
|
||||
### 6.2 ProDOS 16 / GS/OS Output Format
|
||||
|
||||
The linker must produce output in a format the IIgs can load:
|
||||
- OMF (Object Module Format) — the native Apple IIgs executable format
|
||||
- Alternatively, a raw binary image for simple programs
|
||||
- The llvm-mos SDK's infrastructure for platform-specific output formats
|
||||
can be adapted here
|
||||
|
||||
---
|
||||
|
||||
## 7. Implementation Order
|
||||
|
||||
1. **Fork llvm-mos** and create the W65816 target directory structure
|
||||
2. **TableGen: registers and instruction set** — describe all 65816 registers,
|
||||
addressing modes, and instructions
|
||||
3. **Target machine descriptor** — data layout, calling convention basics,
|
||||
feature flags (native mode, emulation mode)
|
||||
4. **Instruction selector** — basic patterns for arithmetic, load/store,
|
||||
branches; initially target a fixed 16-bit accumulator mode (no REP/SEP)
|
||||
5. **Calling convention lowering** — stack-based parameter passing, return
|
||||
values
|
||||
6. **Basic test suite** — compile simple C functions, verify correctness in
|
||||
MAME via Lua harness
|
||||
7. **REP/SEP scheduling pass** — the core optimization; width inference,
|
||||
coalescing, transition insertion
|
||||
8. **Direct page allocator** — promote hot variables to DP addressing
|
||||
9. **GS/OS toolbox call lowering** — custom ISD nodes for JSL-based calls
|
||||
10. **Runtime library** — crt0, minimal libc, GS/OS wrappers
|
||||
11. **OMF output** — linker support for IIgs executable format
|
||||
12. **Optimization tuning** — compare against Calypsi, close gaps
|
||||
|
||||
---
|
||||
|
||||
## 8. Open Questions
|
||||
|
||||
1. **GS/OS direct page reservation:** Exactly which direct page bytes does
|
||||
GS/OS reserve vs. what is safe for user code? Need to verify against
|
||||
the GS/OS reference documentation.
|
||||
|
||||
2. **Bank model:** Should the default code model assume everything fits in
|
||||
bank 0/1, or should we support a large memory model with 24-bit pointers
|
||||
by default? Small model is simpler; large model is more powerful.
|
||||
|
||||
3. **Interrupt handler ABI:** What registers must an IIgs interrupt handler
|
||||
save/restore? DBR and DP especially — they may be in an unknown state
|
||||
when an interrupt fires.
|
||||
|
||||
4. **ORCA/C compatibility:** Do we try to be ABI-compatible with ORCA/C to
|
||||
allow linking against existing IIgs libraries? Or define a clean new ABI?
|
||||
ORCA/C compatibility would be valuable but constraining.
|
||||
|
||||
5. **REP/SEP in leaf functions:** For small leaf functions that only use one
|
||||
width, we can omit mode switches entirely if the caller guarantees a
|
||||
known state. Is a "width contract" calling convention attribute worth
|
||||
implementing?
|
||||
|
||||
6. **Cycle counting in MAME:** MAME's IIgs emulation may not be fully
|
||||
cycle-accurate. For performance regression testing, do we need real
|
||||
hardware or a more cycle-accurate emulator?
|
||||
|
||||
---
|
||||
|
||||
## 9. Coding Conventions
|
||||
|
||||
- `camelCase` for functions and variables
|
||||
- `PascalCase` with `T` suffix for types (e.g., `RegWidthT`, `DpSlotT`)
|
||||
- `//` single-line comments throughout
|
||||
- Target language: C++ (LLVM requirement)
|
||||
- Follow llvm-mos coding style for backend code
|
||||
- Follow LLVM coding standards for everything else
|
||||
|
||||
---
|
||||
|
||||
## 10. References
|
||||
|
||||
- llvm-mos repository: https://github.com/llvm-mos/llvm-mos
|
||||
- llvm-mos SDK: https://github.com/llvm-mos/llvm-mos-sdk
|
||||
- llvm-mos ELF spec: https://llvm-mos.org/wiki/ELF_specification
|
||||
- llvm-mos 65816 issue #32: https://github.com/llvm-mos/llvm-mos/issues/32
|
||||
- llvm-mos 65816 issue #321: https://github.com/llvm-mos/llvm-mos/issues/321
|
||||
- WDC 65816 data sheet: available from westerndesigncenter.com
|
||||
- Apple IIgs Hardware Reference: archive.org
|
||||
- Apple IIgs GS/OS Reference: archive.org
|
||||
- Calypsi compiler (quality benchmark): https://www.calypsi.cc/
|
||||
- ORCA/C (open source reference): https://github.com/byteworksinc/ORCA-C
|
||||
- jeremysrand/llvm-65816 (prior attempt): https://github.com/jeremysrand/llvm-65816
|
||||
563
SESSION_STATE.md
Normal file
563
SESSION_STATE.md
Normal file
|
|
@ -0,0 +1,563 @@
|
|||
# Session Resume — llvm816 project
|
||||
|
||||
Drop this into a new Claude Code session and say "read SESSION_STATE.md and
|
||||
continue where we left off." Pairs with `LLVM_65816_DESIGN.md` (the design
|
||||
doc — read that second).
|
||||
|
||||
---
|
||||
|
||||
## 1. Project in one sentence
|
||||
|
||||
Build an open-source LLVM/Clang backend for the WDC 65816 (Apple IIgs) that
|
||||
matches or exceeds Calypsi's output quality, forked from llvm-mos but
|
||||
maintained as our own separate W65816 target. User is Scott; expert C dev,
|
||||
doesn't want hand-holding on LLVM or 65816 basics.
|
||||
|
||||
## 2. Where we are in the plan
|
||||
|
||||
Design doc section 7 lists a 12-step implementation order. We are at:
|
||||
|
||||
- [x] **Setup toolchain** (prior session)
|
||||
- [x] **Architectural decision: separate W65816 target** (design doc §2.5)
|
||||
- [x] **Repo-layout decision:** `src/` holds our authored files, `patches/`
|
||||
holds modifications to upstream llvm-mos files, `tools/llvm-mos/` is
|
||||
ephemeral and gitignored. `scripts/applyBackend.sh` stitches src +
|
||||
patches into the clone.
|
||||
- [x] **Step 1 — scaffold W65816 target directory.** 41 files under
|
||||
`src/llvm/lib/Target/W65816/` + 2 files under
|
||||
`src/clang/lib/Basic/Targets/`. 4 upstream patches under `patches/`.
|
||||
- [x] **Step 2 — verify the skeleton fully compiles and links.** All 8
|
||||
tablegen generators run clean, three static libs
|
||||
(LLVMW65816Info/Desc/CodeGen) build, llc links with the target
|
||||
registered, zero warnings in the W65816-local build.
|
||||
`./bin/llc -march=w65816 -filetype=null /dev/null` → exit 0.
|
||||
- [x] **Step 2a — real MC-layer instructions.** `W65816InstrInfo.td` now
|
||||
holds ~90 real 65816 opcodes (LDA/STA/LDX/LDY/STX/STY across
|
||||
immediate/DP/abs/DPX/AbsX/AbsY/long where applicable; ADC/SBC/CMP/
|
||||
AND/ORA/EOR/BIT; INC/DEC/ASL/LSR/ROL/ROR; all transfers; stack
|
||||
push/pull; REP/SEP/CLC/SEC/XCE/XBA; branches; JMP/JML/JSR/JSL;
|
||||
RTS/RTL/RTI; MVN/MVP). Instructions whose size depends on M or X
|
||||
bits exist as `_Imm8`/`_Imm16` pairs carrying the appropriate
|
||||
TSFlag bits (MLow/MHigh/XLow/XHigh) for the future REP/SEP pass.
|
||||
- [x] **Step 2b — wire MCCodeEmitter.** Tablegen `-gen-emitter` runs
|
||||
cleanly; `W65816MCCodeEmitter.cpp` calls the tablegen-provided
|
||||
`getBinaryCodeForInstr` and emits Size bytes little-endian.
|
||||
- [x] **Step 2c — symbolic fixups.** Each operand class (imm8/imm16/
|
||||
addrDP/addrAbs/addrLong/pcrel8/pcrel16) has its own
|
||||
`EncoderMethod` that emits a `W65816::fixup_*` at the correct
|
||||
byte offset for expression operands. `W65816AsmBackend::applyFixup`
|
||||
patches the data bytes little-endian for resolved fixups and
|
||||
defers to `maybeAddReloc` for unresolved ones.
|
||||
`W65816ELFObjectWriter::getRelocType` returns placeholder
|
||||
relocation numbers 1-5 (swap for canonical R_W65816_* names once
|
||||
the ELF `EM_` is decided — §7 item 1).
|
||||
- [x] **Step 2d — patch 0005 eliminates data-layout warning.** The
|
||||
data-layout string for `Triple::w65816` lives in
|
||||
`llvm/lib/TargetParser/TargetDataLayout.cpp`; `W65816TargetMachine`
|
||||
now calls `TT.computeDataLayout()`. Zero warnings in the W65816
|
||||
build.
|
||||
- [x] **Step 2e — AsmParser scaffold.** 441-line
|
||||
`AsmParser/W65816AsmParser.cpp` ported from MSP430, stripped of
|
||||
register-operand handling (65816 has no MC register operands),
|
||||
with width-narrowing predicates on each operand class so the
|
||||
matcher picks the narrowest instruction variant the value fits
|
||||
(e.g. `sta $10` → STA_DP, `sta $1000` → STA_Abs, `sta $10000` →
|
||||
STA_Long). `#` is emitted as a literal token to match the AsmString
|
||||
tokenisation. Block-move (MVN/MVP) uses `addrDP` for both bank
|
||||
bytes so `mvn $01, $02` parses.
|
||||
- [x] **Step 2f — operand bit-field wiring.** Every `Inst*` class in
|
||||
`W65816InstrFormats.td` now assigns named bitfields into
|
||||
`Inst{N-8}` (e.g. `let Inst{15-8} = imm;`). Without this
|
||||
tablegen emits an encoder that writes the opcode but leaves the
|
||||
operand bytes as zero — we had that bug for an iteration.
|
||||
- [x] **Step 2g — smoke-test script.** `scripts/smokeTest.sh` checks
|
||||
llc registration, empty-module codegen, and llvm-mc encoding of
|
||||
a representative instruction mix. Run with `--build` to rebuild
|
||||
first.
|
||||
- [x] **Step 2h — end-to-end ELF object.** `llvm-mc -filetype=obj`
|
||||
produces a valid ELF with relocations at correct byte offsets.
|
||||
Relocations are placeholder numbers 1-5 (§7 item 1 — decide
|
||||
EM_/R_* mapping).
|
||||
- [x] **Step 2i — Disassembler.** 190-line
|
||||
`Disassembler/W65816Disassembler.cpp` tries 1/2/3/4-byte decode
|
||||
tables in ascending size order. Custom decoder callbacks for
|
||||
imm/addr/pcrel operands wrap raw bits into MCOperands. Mode-
|
||||
ambiguous opcodes (LDA/LDX/LDY/ADC/SBC/CMP/AND/ORA/EOR/BIT/CPX/
|
||||
CPY immediate forms) are parked in separate
|
||||
DecoderTableW65816{MHigh,XHigh}16 tables and the scaffold only
|
||||
reads the default tables — so those opcodes always disassemble
|
||||
as 3-byte 16-bit-immediate forms until a mode-aware decoder
|
||||
lands (alongside REP/SEP).
|
||||
- [x] **Step 2j — register operands in the AsmParser.** Key fix found
|
||||
via round-trip test: tablegen treats `a`, `x`, `y` in AsmStrings
|
||||
(e.g. `"inc a"`, `"lda\t$addr, x"`) as references to the real
|
||||
register records, so the matcher expects register operands, not
|
||||
literal tokens. AsmParser now produces `k_Reg` operands for
|
||||
these identifiers. Verified: `inc a` → 0x1A, `lda $1000, x` →
|
||||
0xBD,0x00,0x10, full ELF round-trip passes.
|
||||
- [x] **Step 2k — smoke test covers disassembly.** The smoke test
|
||||
now feeds raw bytes through `llvm-mc --disassemble` and checks
|
||||
for expected mnemonics, so encoder/decoder asymmetries surface
|
||||
immediately.
|
||||
- [x] **Step 3a — first DAG patterns.** Type-as-mode model (approved).
|
||||
`LDAi16imm` pseudo for i16 constants; `RTL` for retglue;
|
||||
`emitPrologue` emits canonical `REP #$30`. Mode-dependent
|
||||
`_Imm8` variants are `isCodeGenOnly` so the asm matcher never
|
||||
picks them.
|
||||
- [x] **Step 3c — single-arg function calls.** `LowerFormalArguments`
|
||||
receives arg 0 in A; `LowerCall` passes arg 0 in A and JSL's
|
||||
via a JSL pseudo to bridge the i16 symbol operand to the MC
|
||||
`JSL_Long`'s 24-bit operand class. Result is back in A.
|
||||
Multi-arg call lowering still wants a `PUSHA` SDNode + SP unwind
|
||||
sequence — caller side currently fatals on >1 args.
|
||||
- [x] **Step 3d — multi-arg via stack (callee side).**
|
||||
`LowerFormalArguments` now reads arg 1+ from stack via
|
||||
FrameIndex + load. `eliminateFrameIndex` translates LDAfi /
|
||||
STAfi / ADCfi / SBCfi / ANDfi / ORAfi / EORfi / CMPfi pseudos
|
||||
to their `LDA d,S` etc. counterparts with the offset baked in.
|
||||
Stack-relative MC instructions are in place; AsmParser
|
||||
recognises the `,s` suffix. Callee-side fully working: a
|
||||
`define i16 @sum3(i16 %a, i16 %b, i16 %c)` compiles to
|
||||
`clc; adc 4,s; clc; adc 6,s; rtl`.
|
||||
- [x] **Step 3e — frame-index spill plumbing.** `storeRegToStackSlot`
|
||||
and `loadRegFromStackSlot` emit STAfi / LDAfi pseudos so the
|
||||
register allocator can spill Acc16 values when needed.
|
||||
- [x] **Step 3f — multiplications via shifts.** Multiply by power-of-2
|
||||
constants inherits the `shl` patterns (1/2/3/4 bits unrolled to
|
||||
`asl a` sequences). Multiply by arbitrary constants and
|
||||
runtime values fail at ISel pending library functions.
|
||||
- [x] **Step 3h — clang front end builds.** Real C → 65816 machine
|
||||
code via the full `clang -target w65816 -c` pipeline. Bumped
|
||||
clang's `IntAlign`/`LongAlign`/`PointerAlign`/`SuitableAlign`
|
||||
from 8 to 16; also overrode `allowsMisalignedMemoryAccesses` to
|
||||
return true. `scripts/cDemo.sh` shows the full front-end
|
||||
pipeline on a built-in 7-function demo. Additional patterns:
|
||||
`INC_Abs`/`DEC_Abs` for `*p = *p + 1`; `ASRA16` (PHA;ASL;PLA;ROR
|
||||
sequence) for signed shift-right by 1.
|
||||
- [x] **Step 3i — frame reservation + epilogue.** `emitPrologue`
|
||||
now emits `TSC; SEC; SBC #N; TCS` to reserve N bytes for locals
|
||||
and spills, then `emitEpilogue` reverses with `TSC; CLC; ADC #N;
|
||||
TCS` before the RTL. `eliminateFrameIndex` translates
|
||||
FrameIndex operands into stack-relative offsets via
|
||||
`disp = FrameOffset + StackSize`. `hasFPImpl` returns false
|
||||
(no native FP — direct page would be the logical home). This
|
||||
unblocks `clang -O0 -c` for pure-arithmetic functions (each
|
||||
arg gets spilled to its own stack slot). Stack-relative
|
||||
addressing modes for ADC/SBC/AND/ORA/EOR/CMP let the codegen
|
||||
fold loads from frame indices into the carry-arithmetic ops.
|
||||
- [x] **Step 3g — basic i8 codegen.** Acc8 patterns now cover:
|
||||
`LDAi8imm` (constants), `INA_PSEUDO8` / `DEA_PSEUDO8` (inc/dec),
|
||||
`ADCi8imm` / `SBCi8imm` (add/sub immediate), `ANDi8imm` /
|
||||
`ORAi8imm` / `EORi8imm` (bitwise immediate), `LDA8abs` /
|
||||
`STA8abs` (load/store via global), `ASLA8` / `LSRA8` (1-bit
|
||||
shifts), `CMPi8imm` (compare against immediate, with BR_CC i8
|
||||
lowering). Frame lowering scans the function IR for any i8
|
||||
type usage (return, args, instruction values, operands) and
|
||||
picks `REP #$10; SEP #$20` prologue when found, else
|
||||
`REP #$30`. AsmPrinter masks i8 immediates to 8 bits before
|
||||
printing so `i8 -16` shows `0xf0` rather than `0xfff0`.
|
||||
Limitations: i8 mode is per-function only — mixed-mode
|
||||
functions get the i8 prologue (8-bit A) and i16 ops fail.
|
||||
Asm round-trip for i8 still loses M-mode info (the parser
|
||||
can't disambiguate `lda #imm` between Imm8 and Imm16); use
|
||||
`-filetype=obj` directly from llc to get the right encoding.
|
||||
- [x] **Step 3b — globals, loads, stores, arithmetic, branches,
|
||||
bitwise.** `LowerOperation` custom-lowers `GlobalAddress` and
|
||||
`ExternalSymbol` to `W65816Wrapper(target...)`. Pseudo +
|
||||
AsmPrinter-expansion family covers:
|
||||
|
||||
- `LDAi16imm`, `LDAabs`, `STAabs` (load/store/materialise via
|
||||
Wrapper of global)
|
||||
- `ADCi16imm`, `ADCabs`, `SBCi16imm`, `SBCabs` (add/sub with the
|
||||
required CLC/SEC carry prefix)
|
||||
- `ANDi16imm`, `ORAi16imm`, `EORi16imm` and their `*abs`
|
||||
memory-fold variants
|
||||
- `CMPi16imm`, `CMPabs` plus `W65816ISD::CMP` / `W65816ISD::BR_CC`
|
||||
SDNodes; `LowerBR_CC` swaps constant-on-LHS forms and rewrites
|
||||
SETULE/SETUGT/SETLE/SETGT to SETULT/SETUGE/SETLT/SETGE+1 so
|
||||
the canonicalised DAG hits our patterns; condition-code map
|
||||
covers BEQ/BNE/BCS/BCC plus signed BMI/BPL.
|
||||
- `BRA` for unconditional `br`.
|
||||
- `INA_PSEUDO` / `DEA_PSEUDO` for `add x, ±1` → `inc a` / `dec a`
|
||||
- `ASLA16` / `LSRA16` for `shl x, 1` and `lshr x, 1` → `asl a` /
|
||||
`lsr a`
|
||||
- `NEGA16` for `0 - x` → `eor #$ffff; inc a`
|
||||
- `(xor x, -1)` → `eor #$ffff` (bitwise NOT)
|
||||
- Zero-extending byte load: `lda addr; and #$ff`
|
||||
|
||||
The end-to-end pipeline can now compile and assemble functions
|
||||
that read/write globals, do arithmetic on them, and branch
|
||||
conditionally — all with optimal-looking 65816 idioms (e.g.
|
||||
`lda x ; clc ; adc y` for `*x + *y`).
|
||||
- [ ] **Step 3i — open codegen gaps:**
|
||||
|
||||
1. **Multi-arg call lowering** (caller side). Callee side works;
|
||||
caller still bails on >1 arg. Needs PUSHA SDNode + SP-unwind
|
||||
in ADJCALLSTACKUP.
|
||||
2. **Frame-reserved scratch space.** Prologue doesn't reserve
|
||||
stack space for locals/spills, so any alloca'd value or
|
||||
allocator-spilled value lands at a negative SP offset and
|
||||
eliminateFrameIndex bails. Blocks: -O0 compilation of
|
||||
functions with parameters; loops with PHIs that need to
|
||||
compare two computed values; two-Acc16 binary ops in
|
||||
general. Fix: emit `TSC; CLC; ADC #-N; TCS` (or PHA-loop)
|
||||
in emitPrologue and the inverse in emitEpilogue, where N
|
||||
is the function's frame size.
|
||||
3. **Mixed-mode i8/i16.** Per-function mode only — the prologue
|
||||
picks one mode; the other type's ops fail. REP/SEP scheduling
|
||||
pass needed.
|
||||
4. **Signed `(a - b)` overflow handling.** BMI/BPL based signed
|
||||
comparisons are correct only when the subtraction can't
|
||||
overflow; pathological values give wrong results.
|
||||
5. **`sub imm, var`** and **`mul var, var`** (or non-power-of-2
|
||||
constants). Need libcall support.
|
||||
6. **SETCC and SELECT_CC i16.** Boolean conversions like
|
||||
`(int)(cond != 0)` and `(cond) ? a : b` aren't selectable.
|
||||
Custom lowering needed.
|
||||
7. **Library functions.** `__mulhi3`, etc. — no runtime yet.
|
||||
- [ ] **Step 4 — real frame lowering, calling convention, REP/SEP
|
||||
scheduling pass.** The prologue `REP #$30` is unconditional;
|
||||
the REP/SEP pass will remove it when redundant.
|
||||
|
||||
## 3. What is installed and where
|
||||
|
||||
All under `/home/scott/claude/llvm816/tools/`:
|
||||
|
||||
| Tool | Path | Notes |
|
||||
|---|---|---|
|
||||
| llvm-mos source | `tools/llvm-mos/` | shallow clone. Backend files are symlinked in from `src/`; patches applied on top. Reset cleanly via `scripts/updateLlvmMos.sh`. |
|
||||
| llvm-mos build dir | `tools/llvm-mos-build/` | cmake-generated, ephemeral |
|
||||
| llvm-mos-sdk | `tools/llvm-mos-sdk/` | prebuilt toolchain |
|
||||
| MAME 0.264 | `/usr/games/mame` (apt) | supports `-console` (Lua) |
|
||||
| Apple IIgs ROMs | `tools/mame/roms/apple2gs.zip`, `apple2gsr1.zip` | from archive.org |
|
||||
| Calypsi 5.16 | `tools/calypsi/` | extracted .deb |
|
||||
| ORCA/C source | `tools/orca-c/` | reference only |
|
||||
|
||||
`./setup.sh --verify-only` passed all checks as of the prior session.
|
||||
|
||||
## 4. Repo layout (current)
|
||||
|
||||
```
|
||||
llvm816/ # git repo, branch main
|
||||
├── LLVM_65816_DESIGN.md # tracked
|
||||
├── SESSION_STATE.md # this file
|
||||
├── setup.sh # tracked
|
||||
├── scripts/ # tracked
|
||||
│ ├── common.sh
|
||||
│ ├── installDeps.sh installCalypsi.sh installOrcaC.sh
|
||||
│ ├── installLlvmMos.sh # non-destructive (see §8)
|
||||
│ ├── installMame.sh verify.sh
|
||||
│ ├── applyBackend.sh # src/ + patches/ -> tools/llvm-mos/
|
||||
│ └── updateLlvmMos.sh # reset clone, re-apply backend
|
||||
├── src/ # authored files, tracked
|
||||
│ ├── llvm/lib/Target/W65816/ # 41 files
|
||||
│ │ ├── MCTargetDesc/ (10 files)
|
||||
│ │ ├── TargetInfo/ (3 files)
|
||||
│ │ └── (28 top-level files)
|
||||
│ └── clang/lib/Basic/Targets/
|
||||
│ ├── W65816.h
|
||||
│ └── W65816.cpp
|
||||
├── patches/ # unified diffs, tracked
|
||||
│ ├── 0001-triple-add-w65816-arch.patch
|
||||
│ ├── 0002-triple-cpp-add-w65816-cases.patch
|
||||
│ ├── 0003-clang-basic-dispatch-w65816.patch
|
||||
│ └── 0004-cmake-add-w65816-experimental.patch
|
||||
├── tools/ # gitignored, ephemeral
|
||||
└── .gitignore # excludes tools/, .cache/
|
||||
```
|
||||
|
||||
## 5. Key architectural decisions
|
||||
|
||||
### 5.1 Separate target, not MOS subtarget feature
|
||||
|
||||
llvm-mos has `FeatureW65816` declared in `MOSDevices.td` but codegen
|
||||
unimplemented (issue #321). We are NOT extending MOS. Reasons:
|
||||
- We cannot upstream an AI-assisted backend to llvm-mos anyway.
|
||||
- Clean register model: `Acc8`/`Acc16`/`Idx8`/`Idx16` as separate classes.
|
||||
- Independent evolution.
|
||||
|
||||
Recorded in design doc §2.5.
|
||||
|
||||
### 5.2 Symlinks + patches, not a fork
|
||||
|
||||
`applyBackend.sh` symlinks every file under `src/` into the corresponding
|
||||
path under `tools/llvm-mos/`, then applies each `patches/*.patch` with
|
||||
`git apply`. Idempotent: skips already-current symlinks and
|
||||
already-applied patches (detected via `git apply --reverse --check`).
|
||||
|
||||
`updateLlvmMos.sh` is the ONLY script allowed to destructively reset the
|
||||
clone. It reverses all patches, removes our symlinks, `git reset --hard
|
||||
FETCH_HEAD`, then re-runs `applyBackend.sh`.
|
||||
|
||||
`installLlvmMos.sh` refuses to touch the clone if it is dirty or off
|
||||
main — this is deliberate to protect applied patches.
|
||||
|
||||
## 6. Concrete next actions (in order)
|
||||
|
||||
### 6.1 Function arguments
|
||||
|
||||
`LowerFormalArguments` and `LowerCall` still fatal-error. Without
|
||||
arguments, every function we test has to use globals as inputs. The
|
||||
plan: pass i8/i16 args via the stack (push right-to-left, caller
|
||||
cleans), with the first 1-2 args optionally going in A or X for
|
||||
register-passing. Calypsi output is the reference for ABI choices.
|
||||
|
||||
### 6.2 i8 codegen
|
||||
|
||||
Currently every function gets `REP #$30` (16-bit mode). For i8 ops
|
||||
we need either:
|
||||
|
||||
- A scan-and-prepend approach: if the function has any i8 op, emit
|
||||
`SEP #$20` after the REP for whichever mode dominates, plus
|
||||
toggle pseudos around the off-mode regions.
|
||||
- Or commit to widening all i8 to i16 pre-ISel (simpler, but uses 2x
|
||||
the cycles for byte-heavy code).
|
||||
|
||||
This is the natural lead-in to the REP/SEP scheduling pass (§6.4).
|
||||
|
||||
### 6.3 Frame indices, stack locals
|
||||
|
||||
Add `eliminateFrameIndex` and frame-pointer pseudos so we can spill
|
||||
to the stack. Today `W65816RegisterInfo::eliminateFrameIndex` is
|
||||
`llvm_unreachable`. Stack accesses on 65816 are `,s` and `(,s),y`
|
||||
indirect — needs new operand classes.
|
||||
|
||||
### 6.4 REP/SEP scheduling pass
|
||||
|
||||
The core algorithmic work. TSFlag bits on every mode-dependent
|
||||
instruction are already in place; the pass walks MIR, dataflows the
|
||||
required mode per region, and inserts/removes REP/SEP transitions to
|
||||
minimise total mode switches. Design doc §3.3.
|
||||
|
||||
### 6.2 Wire frame lowering + calling convention (real)
|
||||
|
||||
`W65816FrameLowering.cpp` is still `llvm_unreachable`. The simplest
|
||||
working version: establish an i16 stack pointer-based frame using the
|
||||
native SP, locals accessed via stack-relative indirect via Y. Calypsi
|
||||
output for a trivial function is a good model.
|
||||
|
||||
`W65816CallingConv.td` covers i8/i16 return in A but nothing for
|
||||
arguments. Start with stack-based (push right-to-left, caller cleans)
|
||||
per design doc §3.5.
|
||||
|
||||
### 6.3 Disassembler mode-aware decoding (deferred)
|
||||
|
||||
The scaffold disassembler always decodes LDA/LDX/LDY/ADC/SBC/CMP/AND/
|
||||
ORA/EOR/BIT/CPX/CPY immediate forms as 3-byte 16-bit-immediate
|
||||
variants. A real decoder should track the M/X bits across the stream
|
||||
(consuming REP/SEP, XCE transitions) and choose between
|
||||
`DecoderTableW65816` (default) and `DecoderTableW65816{MHigh,XHigh}16`
|
||||
per instruction. Naturally pairs with the REP/SEP codegen pass since
|
||||
both need the same M/X tracking model.
|
||||
|
||||
### 6.4 REP/SEP scheduling pass
|
||||
|
||||
The core algorithmic work (design doc §3.3). Every real instruction
|
||||
now carries TSFlag bits indicating which M/X mode it requires. The
|
||||
pass reads those, does the width-inference / coalescing / transition
|
||||
insertion dataflow, and emits REP/SEP instructions at block
|
||||
boundaries. Plan to spend multiple sessions.
|
||||
|
||||
### 6.5 Tidy-ups (can happen in any order)
|
||||
|
||||
- Decide ELF `EM_` value (§7 item 1). Currently `EM_NONE`, with
|
||||
placeholder relocation numbers 1-5 in `W65816ELFObjectWriter`.
|
||||
Swap for canonical `R_W65816_*` names once chosen.
|
||||
- Replace ASCII-art mnemonics (`inc a`, `dec a`, `asl a`, etc.) with
|
||||
proper InstAliases so both `INA` and `INC A` assemble to the same
|
||||
opcode. Requires AsmParser (§6.3).
|
||||
|
||||
## 7. Open design questions flagged by the scaffold
|
||||
|
||||
1. **ELF `EM_` machine number.** `W65816ELFObjectWriter.cpp` uses
|
||||
`ELF::EM_NONE` as a placeholder. llvm-mos uses `EM_MOS = 0x1966` for
|
||||
the 6502 family. Decide: share `EM_MOS`, or pick a new value?
|
||||
2. **Data layout string** is hardcoded in
|
||||
`W65816TargetMachine.cpp` rather than routing through
|
||||
`Triple::computeDataLayout()`. That is OK for now — when we're
|
||||
ready to consolidate, add a case in `TargetDataLayout.cpp` and
|
||||
switch to `TT.computeDataLayout()`.
|
||||
3. **i32 return convention** — does i32 return in A:X or via a hidden
|
||||
pointer? Currently `W65816CallingConv.td` only handles i8/i16. Design
|
||||
doc §3.5 says "A:X for 32-bit" but this isn't modelled yet.
|
||||
4. **Register aliasing for mode-dependent widths.** `Acc8` and `Acc16`
|
||||
both contain physical register `A`. LLVM's allocator will not cope
|
||||
with this correctly. The REP/SEP management pass (§3.3) is required.
|
||||
Flagged per the design doc.
|
||||
5. **Open questions from design doc §8** (GS/OS DP reservation, bank
|
||||
memory model, interrupt ABI, ORCA/C ABI compat, width-contract
|
||||
attribute, MAME cycle accuracy) — still unresolved. Punt until after
|
||||
we have a working instruction set.
|
||||
|
||||
## 8. Gotchas + hard-won knowledge
|
||||
|
||||
- **`installLlvmMos.sh` is non-destructive now.** It refuses to reset
|
||||
the clone if it is dirty or off main. Use `scripts/updateLlvmMos.sh`
|
||||
to refresh (the only script allowed to reset).
|
||||
- **MAME `-console` flag** is listed by `-showusage`, NOT `-help`.
|
||||
- **`log()` in `common.sh` writes to stderr.** Don't change it.
|
||||
- **llvm-mos has `FeatureW65816` but not working codegen** (issue #321).
|
||||
- **`RemapAllTargetPseudoPointerOperands<PtrRegs>` is required** in
|
||||
`W65816.td` or tablegen fails with 8 "missing target override for
|
||||
pseudoinstruction using PointerLikeRegClass" errors. Don't remove it.
|
||||
- **`Triple::w65816` placement in Triple.h:** inserted right after
|
||||
`mos,` to keep the 65xx family clustered. See patch 0001.
|
||||
- **Added to `LLVM_ALL_EXPERIMENTAL_TARGETS`** in `llvm/CMakeLists.txt`
|
||||
so `-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=all` picks up W65816. Not
|
||||
strictly required — passing the name explicitly also works.
|
||||
- **Operand `OperandType` field wants LLVM's enum spelling**, not
|
||||
shortened. Use `OPERAND_IMMEDIATE`, `OPERAND_MEMORY`, `OPERAND_PCREL`
|
||||
(see `llvm/include/llvm/MC/MCInstrDesc.h` MCOI::OperandType).
|
||||
`OPERAND_IMM` / `OPERAND_IMM8` / `OPERAND_IMM16` are NOT valid.
|
||||
- **`PrintMethod` signature for PC-rel operands takes `Address`.**
|
||||
Tablegen generates `printPCRel8(MI, Address, OpNo, O)` — 4 args, not
|
||||
3. Non-PC-rel PrintMethods use the 3-arg form `(MI, OpNo, O)`.
|
||||
- **Several `.cpp` files needed explicit `#include`s beyond what MSP430
|
||||
ships with** because the tablegen-generated `.inc` references full
|
||||
types: `W65816RegisterInfo.cpp` needs `W65816Subtarget.h` and
|
||||
`W65816FrameLowering.h` (for `GET_REGINFO_TARGET_DESC`);
|
||||
`W65816InstPrinter.cpp` needs `llvm/MC/MCAsmInfo.h` (for
|
||||
`MAI.printExpr`).
|
||||
- **Marker classes can't override mayLoad/mayStore via `let`.**
|
||||
TableGen's multi-inheritance doesn't let unrelated sibling classes
|
||||
touch fields from the base `Instruction`. Use `let isReturn = 1, ...
|
||||
in { ... }` blocks at def sites instead (idiomatic LLVM style).
|
||||
- **Data layout is hardcoded** in `W65816TargetMachine.cpp` rather than
|
||||
computed from `TT.computeDataLayout()`, because `TargetDataLayout.cpp`
|
||||
doesn't have a case for `w65816` yet. This produces one `-Wswitch`
|
||||
warning in the llvm-mos build. §6.5 notes adding a 5th patch to
|
||||
silence it.
|
||||
|
||||
## 9. Disk space recovery
|
||||
|
||||
If space is tight before resume:
|
||||
|
||||
```
|
||||
# safe to delete — regenerable from setup.sh + applyBackend.sh:
|
||||
rm -rf /home/scott/claude/llvm816/tools/
|
||||
rm -rf /home/scott/claude/llvm816/.cache/
|
||||
```
|
||||
|
||||
Regenerate with `./setup.sh` then `./scripts/applyBackend.sh`.
|
||||
|
||||
The `tools/llvm-mos-build/` directory alone is ~2 GB after a full
|
||||
configure+tablegen. A full ninja build will be much more.
|
||||
|
||||
## 10. Quick verification commands for resume
|
||||
|
||||
```
|
||||
# Verify the scaffold is in place:
|
||||
ls src/llvm/lib/Target/W65816/ | wc -l # expect ~20 top-level files
|
||||
ls patches/ # expect 4 .patch files
|
||||
|
||||
# Verify apply is clean:
|
||||
./scripts/applyBackend.sh # expect 0 new, 44 current symlinks; 0 new, 4 applied patches
|
||||
|
||||
# Verify cmake configures:
|
||||
cmake -S tools/llvm-mos/llvm -B tools/llvm-mos-build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_TARGETS_TO_BUILD="" \
|
||||
-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS;W65816" \
|
||||
-DLLVM_ENABLE_PROJECTS="clang" \
|
||||
-DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF \
|
||||
-DLLVM_INCLUDE_BENCHMARKS=OFF
|
||||
|
||||
# Verify full build + llc registration (slow first time, cached after):
|
||||
( cd tools/llvm-mos-build && ninja LLVMW65816Info LLVMW65816Desc LLVMW65816CodeGen llc )
|
||||
./tools/llvm-mos-build/bin/llc --version | grep w65816
|
||||
./tools/llvm-mos-build/bin/llc -march=w65816 -filetype=null /dev/null ; echo $?
|
||||
# Expect: grep matches; llc exits 0.
|
||||
```
|
||||
|
||||
## 11. Files changed this session (not yet committed by user)
|
||||
|
||||
```
|
||||
scripts/applyBackend.sh # idempotent src+patches apply
|
||||
scripts/updateLlvmMos.sh # safe reset+reapply
|
||||
scripts/installLlvmMos.sh # no longer destructively resets
|
||||
scripts/smokeTest.sh # regression smoke test
|
||||
src/llvm/lib/Target/W65816/ # full MC layer + first codegen:
|
||||
# CodeGen scaffolds (~40 files)
|
||||
# AsmParser/ (2 files)
|
||||
# Disassembler/ (2 files)
|
||||
# MCTargetDesc/ (11 files)
|
||||
# TargetInfo/ (3 files)
|
||||
# ~90 real instruction defs
|
||||
# ~25 codegen pseudos +
|
||||
# AsmPrinter expansion
|
||||
src/clang/lib/Basic/Targets/W65816.{h,cpp}
|
||||
patches/0001..0005.patch # upstream llvm-mos mods
|
||||
SESSION_STATE.md # this file
|
||||
```
|
||||
|
||||
The tools/ tree is all ephemeral (gitignored).
|
||||
|
||||
### What now works end-to-end
|
||||
|
||||
Try it yourself:
|
||||
|
||||
```
|
||||
./scripts/cDemo.sh # built-in demo
|
||||
./scripts/cDemo.sh path/to/your.c
|
||||
```
|
||||
|
||||
Sample output for the built-in demo (real C → real 65816):
|
||||
|
||||
```
|
||||
get_counter: lda counter ; rtl
|
||||
set_counter: sta counter ; rtl
|
||||
sum_with_target: clc ; adc target ; rtl
|
||||
doubler: asl a ; rtl
|
||||
half: lsr a ; rtl
|
||||
reset: lda #0 ; sta counter ; rtl
|
||||
answer: lda #42 ; rtl
|
||||
```
|
||||
|
||||
### Detail: command-line invocations
|
||||
|
||||
```
|
||||
# Round-trip asm -> bytes -> asm:
|
||||
echo ' lda #0x1234' | ./bin/llvm-mc -arch=w65816 -show-encoding
|
||||
# -> lda #0x1234 ; encoding: [0xa9,0x34,0x12]
|
||||
|
||||
echo '0xea 0xa9 0x34 0x12 0x6b' | ./bin/llvm-mc --disassemble --triple=w65816
|
||||
# -> nop ; lda #0x1234 ; rtl
|
||||
|
||||
# Full asm -> ELF -> disasm:
|
||||
./bin/llvm-mc -arch=w65816 -filetype=obj foo.s -o foo.o
|
||||
./bin/llvm-objdump --triple=w65816 -d foo.o
|
||||
|
||||
# Real codegen. This .ll compiles cleanly:
|
||||
@x = global i16 0
|
||||
@y = global i16 0
|
||||
define i16 @fib_step() {
|
||||
%a = load i16, ptr @x
|
||||
%b = load i16, ptr @y
|
||||
%s = add i16 %a, %b
|
||||
store i16 %a, ptr @y
|
||||
store i16 %s, ptr @x
|
||||
ret i16 %s
|
||||
}
|
||||
# llc emits idiomatic 65816:
|
||||
# rep #0x30
|
||||
# lda x; clc; adc y ; A = a + b
|
||||
# sta x ; x = a + b
|
||||
# ...
|
||||
```
|
||||
|
||||
### What doesn't work yet
|
||||
|
||||
- **Multi-arg calls** (caller side). Callee accepts stack-passed
|
||||
args; the matching push side is unimplemented. Functions with
|
||||
more than one arg can be defined and compile correctly, but
|
||||
cannot be called from another function.
|
||||
- **Two-Acc16 cmp.** Loops with PHIs that need to compare two
|
||||
computed values fail at ISel — only one A.
|
||||
- **i8 ops** (always 16-bit mode for now).
|
||||
- **Signed overflow** in CMP-based branches: BMI/BPL test the N flag
|
||||
of the subtraction, which is incorrect when the subtract overflows.
|
||||
- **`mul var, var`** (or by non-power-of-2 constants). Needs library
|
||||
functions (`__mulhi3` etc.).
|
||||
- **`sub imm, var`** (only `sub var, imm` works).
|
||||
|
||||
See §6.1-§6.4 for the next steps.
|
||||
12
patches/0001-triple-add-w65816-arch.patch
Normal file
12
patches/0001-triple-add-w65816-arch.patch
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
|
||||
index b31520b17..d51ac8bc1 100644
|
||||
--- a/llvm/include/llvm/TargetParser/Triple.h
|
||||
+++ b/llvm/include/llvm/TargetParser/Triple.h
|
||||
@@ -69,6 +69,7 @@ public:
|
||||
mips64, // MIPS64: mips64, mips64r6, mipsn32, mipsn32r6
|
||||
mips64el, // MIPS64EL: mips64el, mips64r6el, mipsn32el, mipsn32r6el
|
||||
mos, // MOS: mos 65xx
|
||||
+ w65816, // WDC 65816: w65816 (Apple IIgs)
|
||||
msp430, // MSP430: msp430
|
||||
ppc, // PPC: powerpc
|
||||
ppcle, // PPCLE: powerpc (little endian)
|
||||
77
patches/0002-triple-cpp-add-w65816-cases.patch
Normal file
77
patches/0002-triple-cpp-add-w65816-cases.patch
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
|
||||
index 8aef55224..b6e467274 100644
|
||||
--- a/llvm/lib/TargetParser/Triple.cpp
|
||||
+++ b/llvm/lib/TargetParser/Triple.cpp
|
||||
@@ -80,6 +80,8 @@ StringRef Triple::getArchTypeName(ArchType Kind) {
|
||||
return "mipsel";
|
||||
case mos:
|
||||
return "mos";
|
||||
+ case w65816:
|
||||
+ return "w65816";
|
||||
case msp430:
|
||||
return "msp430";
|
||||
case nvptx64:
|
||||
@@ -678,6 +680,7 @@ Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) {
|
||||
.Case("mips64", mips64)
|
||||
.Case("mips64el", mips64el)
|
||||
.Case("msp430", msp430)
|
||||
+ .Case("w65816", w65816)
|
||||
.Case("ppc64", ppc64)
|
||||
.Case("ppc32", ppc)
|
||||
.Case("ppc", ppc)
|
||||
@@ -828,6 +831,7 @@ Triple::ArchType Triple::parseArch(StringRef ArchName) {
|
||||
.Case("m68k", Triple::m68k)
|
||||
.Case("mos", Triple::mos)
|
||||
.Case("msp430", Triple::msp430)
|
||||
+ .Case("w65816", Triple::w65816)
|
||||
.Cases({"mips", "mipseb", "mipsallegrex", "mipsisa32r6", "mipsr6"},
|
||||
Triple::mips)
|
||||
.Cases({"mipsel", "mipsallegrexel", "mipsisa32r6el", "mipsr6el"},
|
||||
@@ -1223,6 +1227,7 @@ static Triple::ObjectFormatType getDefaultFormat(const Triple &T) {
|
||||
case Triple::mips:
|
||||
case Triple::mos:
|
||||
case Triple::msp430:
|
||||
+ case Triple::w65816:
|
||||
case Triple::nvptx64:
|
||||
case Triple::nvptx:
|
||||
case Triple::ppc64le:
|
||||
@@ -1948,6 +1953,7 @@ unsigned Triple::getArchPointerBitWidth(llvm::Triple::ArchType Arch) {
|
||||
case llvm::Triple::avr:
|
||||
case llvm::Triple::mos:
|
||||
case llvm::Triple::msp430:
|
||||
+ case llvm::Triple::w65816:
|
||||
return 16;
|
||||
|
||||
case llvm::Triple::aarch64_32:
|
||||
@@ -2057,6 +2063,7 @@ Triple Triple::get32BitArchVariant() const {
|
||||
case Triple::bpfel:
|
||||
case Triple::mos:
|
||||
case Triple::msp430:
|
||||
+ case Triple::w65816:
|
||||
case Triple::systemz:
|
||||
case Triple::ve:
|
||||
T.setArch(UnknownArch);
|
||||
@@ -2176,6 +2183,7 @@ Triple Triple::get64BitArchVariant() const {
|
||||
case Triple::m68k:
|
||||
case Triple::mos:
|
||||
case Triple::msp430:
|
||||
+ case Triple::w65816:
|
||||
case Triple::r600:
|
||||
case Triple::shave:
|
||||
case Triple::sparcel:
|
||||
@@ -2303,6 +2311,7 @@ Triple Triple::getBigEndianArchVariant() const {
|
||||
case Triple::loongarch64:
|
||||
case Triple::mos:
|
||||
case Triple::msp430:
|
||||
+ case Triple::w65816:
|
||||
case Triple::nvptx64:
|
||||
case Triple::nvptx:
|
||||
case Triple::r600:
|
||||
@@ -2444,6 +2453,7 @@ bool Triple::isLittleEndian() const {
|
||||
case Triple::mipsel:
|
||||
case Triple::mos:
|
||||
case Triple::msp430:
|
||||
+ case Triple::w65816:
|
||||
case Triple::nvptx64:
|
||||
case Triple::nvptx:
|
||||
case Triple::ppcle:
|
||||
34
patches/0003-clang-basic-dispatch-w65816.patch
Normal file
34
patches/0003-clang-basic-dispatch-w65816.patch
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt
|
||||
index c71c1c6f0..a0c5fbb64 100644
|
||||
--- a/clang/lib/Basic/CMakeLists.txt
|
||||
+++ b/clang/lib/Basic/CMakeLists.txt
|
||||
@@ -111,6 +111,7 @@ add_clang_library(clangBasic
|
||||
Targets/M68k.cpp
|
||||
Targets/MOS.cpp
|
||||
Targets/MSP430.cpp
|
||||
+ Targets/W65816.cpp
|
||||
Targets/Mips.cpp
|
||||
Targets/NVPTX.cpp
|
||||
Targets/OSTargets.cpp
|
||||
diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp
|
||||
index 4c0fdfdbc..fccc921de 100644
|
||||
--- a/clang/lib/Basic/Targets.cpp
|
||||
+++ b/clang/lib/Basic/Targets.cpp
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "Targets/M68k.h"
|
||||
#include "Targets/MOS.h"
|
||||
#include "Targets/MSP430.h"
|
||||
+#include "Targets/W65816.h"
|
||||
#include "Targets/Mips.h"
|
||||
#include "Targets/NVPTX.h"
|
||||
#include "Targets/OSTargets.h"
|
||||
@@ -287,6 +288,9 @@ std::unique_ptr<TargetInfo> AllocateTarget(const llvm::Triple &Triple,
|
||||
case llvm::Triple::msp430:
|
||||
return std::make_unique<MSP430TargetInfo>(Triple, Opts);
|
||||
|
||||
+ case llvm::Triple::w65816:
|
||||
+ return std::make_unique<W65816TargetInfo>(Triple, Opts);
|
||||
+
|
||||
case llvm::Triple::mips:
|
||||
switch (os) {
|
||||
case llvm::Triple::Linux:
|
||||
12
patches/0004-cmake-add-w65816-experimental.patch
Normal file
12
patches/0004-cmake-add-w65816-experimental.patch
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt
|
||||
index 6001928f9..015a239b5 100644
|
||||
--- a/llvm/CMakeLists.txt
|
||||
+++ b/llvm/CMakeLists.txt
|
||||
@@ -596,6 +596,7 @@ set(LLVM_ALL_EXPERIMENTAL_TARGETS
|
||||
CSKY
|
||||
DirectX
|
||||
M68k
|
||||
+ W65816
|
||||
Xtensa
|
||||
)
|
||||
|
||||
13
patches/0005-target-data-layout-w65816.patch
Normal file
13
patches/0005-target-data-layout-w65816.patch
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
diff --git a/llvm/lib/TargetParser/TargetDataLayout.cpp b/llvm/lib/TargetParser/TargetDataLayout.cpp
|
||||
index 8837d2f91..b796d9e86 100644
|
||||
--- a/llvm/lib/TargetParser/TargetDataLayout.cpp
|
||||
+++ b/llvm/lib/TargetParser/TargetDataLayout.cpp
|
||||
@@ -582,6 +582,8 @@ std::string Triple::computeDataLayout(StringRef ABIName) const {
|
||||
return "e-m:e-p:16:8-p1:8:8-i16:8-i32:8-i64:8-f32:8-f64:8-a:8-Fi8-n8";
|
||||
case Triple::msp430:
|
||||
return "e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16";
|
||||
+ case Triple::w65816:
|
||||
+ return "e-m:e-p:16:8-i16:16-i32:16-n8:16-S16";
|
||||
case Triple::ppc:
|
||||
case Triple::ppcle:
|
||||
case Triple::ppc64:
|
||||
77
scripts/applyBackend.sh
Executable file
77
scripts/applyBackend.sh
Executable file
|
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/env bash
|
||||
# Stitch our W65816 backend into the ephemeral llvm-mos checkout.
|
||||
#
|
||||
# Two-step process:
|
||||
# 1. For every file under src/, create a symlink at the matching path
|
||||
# inside tools/llvm-mos/. That way editing a file under src/ is
|
||||
# immediately reflected in the build tree.
|
||||
# 2. Apply every patch under patches/ with `git apply`. Patches that
|
||||
# are already applied (git detects this) are skipped silently.
|
||||
#
|
||||
# Safe to re-run.
|
||||
|
||||
set -euo pipefail
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
LLVM_SRC="$TOOLS_DIR/llvm-mos"
|
||||
SRC_TREE="$PROJECT_ROOT/src"
|
||||
PATCH_DIR="$PROJECT_ROOT/patches"
|
||||
|
||||
[ -d "$LLVM_SRC/.git" ] || die "llvm-mos checkout not found at $LLVM_SRC; run setup.sh first"
|
||||
[ -d "$SRC_TREE" ] || die "src/ tree missing"
|
||||
|
||||
# 1. Symlinks. For each file in src/<rel> we want a symlink at
|
||||
# $LLVM_SRC/<rel>. Relative target path keeps the clone portable if
|
||||
# someone moves it, though our setup.sh always uses an absolute path.
|
||||
log "linking src/ files into $LLVM_SRC"
|
||||
linkedCount=0
|
||||
skippedCount=0
|
||||
while IFS= read -r -d '' file; do
|
||||
rel="${file#$SRC_TREE/}"
|
||||
dest="$LLVM_SRC/$rel"
|
||||
destDir="$(dirname "$dest")"
|
||||
install -d "$destDir"
|
||||
|
||||
if [ -L "$dest" ]; then
|
||||
currentTarget="$(readlink "$dest")"
|
||||
if [ "$currentTarget" = "$file" ]; then
|
||||
skippedCount=$((skippedCount + 1))
|
||||
continue
|
||||
fi
|
||||
warn "replacing stale symlink: $rel"
|
||||
rm "$dest"
|
||||
elif [ -e "$dest" ]; then
|
||||
die "refusing to overwrite non-symlink file: $dest"
|
||||
fi
|
||||
|
||||
ln -s "$file" "$dest"
|
||||
linkedCount=$((linkedCount + 1))
|
||||
done < <(find "$SRC_TREE" -type f -print0)
|
||||
|
||||
log "symlinks: $linkedCount created, $skippedCount already current"
|
||||
|
||||
# 2. Patches. git apply is idempotent via --reverse-check: if a patch
|
||||
# is already applied, `git apply --reverse --check` succeeds and we skip.
|
||||
log "applying patches from $PATCH_DIR"
|
||||
appliedCount=0
|
||||
reappliedCount=0
|
||||
for patch in "$PATCH_DIR"/*.patch; do
|
||||
[ -f "$patch" ] || continue
|
||||
name="$(basename "$patch")"
|
||||
|
||||
if git -C "$LLVM_SRC" apply --reverse --check "$patch" >/dev/null 2>&1; then
|
||||
reappliedCount=$((reappliedCount + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! git -C "$LLVM_SRC" apply --check "$patch" >/dev/null 2>&1; then
|
||||
die "patch fails to apply cleanly: $name. The llvm-mos tree may have drifted; run scripts/updateLlvmMos.sh."
|
||||
fi
|
||||
|
||||
git -C "$LLVM_SRC" apply "$patch"
|
||||
log " applied: $name"
|
||||
appliedCount=$((appliedCount + 1))
|
||||
done
|
||||
|
||||
log "patches: $appliedCount newly applied, $reappliedCount already present"
|
||||
log "backend stitched in at $LLVM_SRC"
|
||||
46
scripts/cDemo.sh
Executable file
46
scripts/cDemo.sh
Executable file
|
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env bash
|
||||
# Compile a small C file with clang -target w65816 and disassemble
|
||||
# the result. Demonstrates the full toolchain front-to-back.
|
||||
#
|
||||
# Usage: scripts/cDemo.sh [source.c]
|
||||
# Without an argument, compiles a built-in demo program.
|
||||
|
||||
set -euo pipefail
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
BUILD_DIR="$TOOLS_DIR/llvm-mos-build"
|
||||
CLANG="$BUILD_DIR/bin/clang"
|
||||
OBJDUMP="$BUILD_DIR/bin/llvm-objdump"
|
||||
|
||||
[ -x "$CLANG" ] || die "clang not built; run: ninja -C $BUILD_DIR clang"
|
||||
[ -x "$OBJDUMP" ] || die "llvm-objdump not built"
|
||||
|
||||
src="${1:-}"
|
||||
if [ -z "$src" ]; then
|
||||
src="$(mktemp --suffix=.c)"
|
||||
obj_cleanup_extra="$src"
|
||||
cat > "$src" <<'EOF'
|
||||
// Built-in demo: simple integer functions
|
||||
int counter;
|
||||
int target = 100;
|
||||
|
||||
int get_counter(void) { return counter; }
|
||||
int set_counter(int v) { counter = v; return v; }
|
||||
int sum_with_target(int x) { return x + target; }
|
||||
int doubler(int x) { return x * 2; }
|
||||
unsigned half(unsigned x) { return x / 2; } // unsigned: uses lsr
|
||||
int reset(void) { counter = 0; return 0; }
|
||||
int answer(void) { return 42; }
|
||||
EOF
|
||||
log "(no source given; using built-in demo)"
|
||||
fi
|
||||
|
||||
obj="$(mktemp --suffix=.o)"
|
||||
trap 'rm -f "$obj" ${obj_cleanup_extra:-}' EXIT
|
||||
|
||||
log "compiling $src with clang -target w65816 -O2"
|
||||
"$CLANG" --target=w65816 -O2 -c "$src" -o "$obj"
|
||||
|
||||
log "disassembling result:"
|
||||
echo
|
||||
"$OBJDUMP" --triple=w65816 -d "$obj"
|
||||
45
scripts/common.sh
Executable file
45
scripts/common.sh
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
# Shared helpers for all install scripts. Sourced, not executed.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
TOOLS_DIR="$PROJECT_ROOT/tools"
|
||||
DOWNLOAD_CACHE="$PROJECT_ROOT/.cache/downloads"
|
||||
|
||||
install -d "$TOOLS_DIR" "$DOWNLOAD_CACHE"
|
||||
|
||||
log() {
|
||||
printf '\033[1;34m[llvm816]\033[0m %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
warn() {
|
||||
printf '\033[1;33m[llvm816 WARN]\033[0m %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
die() {
|
||||
printf '\033[1;31m[llvm816 FAIL]\033[0m %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Download to cache unless already present. $1=url, $2=dest filename in cache.
|
||||
fetchCached() {
|
||||
local url="$1"
|
||||
local name="$2"
|
||||
local dest="$DOWNLOAD_CACHE/$name"
|
||||
if [ -s "$dest" ]; then
|
||||
log "cached: $name"
|
||||
else
|
||||
log "fetching: $url"
|
||||
curl -fL --retry 3 --progress-bar -o "$dest.part" "$url"
|
||||
mv "$dest.part" "$dest"
|
||||
fi
|
||||
printf '%s\n' "$dest"
|
||||
}
|
||||
|
||||
needCmd() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
|
||||
}
|
||||
|
||||
haveCmd() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
50
scripts/installCalypsi.sh
Executable file
50
scripts/installCalypsi.sh
Executable file
|
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/env bash
|
||||
# Install Calypsi 65C816 toolchain as the output-quality benchmark.
|
||||
# Self-contained: extract .deb payload into tools/calypsi rather than
|
||||
# dpkg-install system-wide.
|
||||
|
||||
set -euo pipefail
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
CALYPSI_VERSION="5.16"
|
||||
CALYPSI_DIR="$TOOLS_DIR/calypsi"
|
||||
CALYPSI_DEB_URL="https://github.com/hth313/Calypsi-tool-chains/releases/download/${CALYPSI_VERSION}/calypsi-65816-${CALYPSI_VERSION}.deb"
|
||||
|
||||
if [ -x "$CALYPSI_DIR/usr/local/calypsi-65816/bin/cc65816" ] \
|
||||
|| [ -x "$CALYPSI_DIR/opt/calypsi-65816/bin/cc65816" ] \
|
||||
|| find "$CALYPSI_DIR" -maxdepth 6 -type f -name 'cc65816' 2>/dev/null | grep -q .; then
|
||||
log "calypsi already installed under $CALYPSI_DIR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
deb="$(fetchCached "$CALYPSI_DEB_URL" "calypsi-65816-${CALYPSI_VERSION}.deb")"
|
||||
|
||||
log "extracting calypsi .deb payload"
|
||||
install -d "$CALYPSI_DIR"
|
||||
tmp="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
( cd "$tmp" && ar x "$deb" )
|
||||
|
||||
payload=""
|
||||
for cand in data.tar.xz data.tar.zst data.tar.gz data.tar.bz2; do
|
||||
if [ -f "$tmp/$cand" ]; then
|
||||
payload="$tmp/$cand"
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ -n "$payload" ] || die "could not find data.tar.* inside $deb"
|
||||
|
||||
tar -xf "$payload" -C "$CALYPSI_DIR"
|
||||
|
||||
# Locate the bin directory. Calypsi puts tools under /opt or /usr/local
|
||||
# depending on package revision; find it and symlink into tools/calypsi/bin
|
||||
# for a stable path.
|
||||
ccBin="$(find "$CALYPSI_DIR" -maxdepth 6 -type f -name 'cc65816' -executable 2>/dev/null | head -1 || true)"
|
||||
[ -n "$ccBin" ] || die "calypsi cc65816 not found after extract (inspect $CALYPSI_DIR)"
|
||||
binDir="$(dirname "$ccBin")"
|
||||
ln -sfn "$binDir" "$CALYPSI_DIR/bin"
|
||||
|
||||
log "calypsi install done"
|
||||
log " version: $CALYPSI_VERSION"
|
||||
log " root: $CALYPSI_DIR"
|
||||
log " binaries: $CALYPSI_DIR/bin -> $binDir"
|
||||
38
scripts/installDeps.sh
Executable file
38
scripts/installDeps.sh
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env bash
|
||||
# Install system packages needed for llvm-mos build, MAME usage, and general dev.
|
||||
|
||||
set -euo pipefail
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
APT_PACKAGES=(
|
||||
# llvm-mos build toolchain
|
||||
build-essential
|
||||
cmake
|
||||
ninja-build
|
||||
clang
|
||||
lld
|
||||
python3
|
||||
python3-pip
|
||||
git
|
||||
zlib1g-dev
|
||||
libedit-dev
|
||||
libxml2-dev
|
||||
libncurses-dev
|
||||
# archive handling (calypsi ships zst; rom zips; llvm-mos-sdk tar.xz)
|
||||
zstd
|
||||
xz-utils
|
||||
unzip
|
||||
tar
|
||||
# MAME Lua scripting & debug
|
||||
lua5.4
|
||||
liblua5.4-dev
|
||||
# runtime utilities used by install scripts
|
||||
curl
|
||||
ca-certificates
|
||||
)
|
||||
|
||||
log "installing apt packages (sudo required)"
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y --no-install-recommends "${APT_PACKAGES[@]}"
|
||||
|
||||
log "system dependencies installed"
|
||||
83
scripts/installLlvmMos.sh
Executable file
83
scripts/installLlvmMos.sh
Executable file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env bash
|
||||
# Install llvm-mos: clone source tree for backend development, plus
|
||||
# download prebuilt SDK for reference/smoke-testing existing 6502 targets.
|
||||
#
|
||||
# Flags:
|
||||
# --build also build the source tree with cmake/ninja (slow, ~30-60 min)
|
||||
|
||||
set -euo pipefail
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
doBuild=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--build) doBuild=1 ;;
|
||||
*) die "unknown flag: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
LLVM_SRC="$TOOLS_DIR/llvm-mos"
|
||||
LLVM_BUILD="$TOOLS_DIR/llvm-mos-build"
|
||||
LLVM_SDK="$TOOLS_DIR/llvm-mos-sdk"
|
||||
SDK_URL="https://github.com/llvm-mos/llvm-mos-sdk/releases/latest/download/llvm-mos-linux.tar.xz"
|
||||
|
||||
# 1. Source tree for backend development.
|
||||
#
|
||||
# IMPORTANT: this clone is ephemeral scaffolding, not where we do work. Our
|
||||
# own sources live in $PROJECT_ROOT/src/ and $PROJECT_ROOT/patches/ and are
|
||||
# stitched into this tree by applyBackend.sh. That means a destructive
|
||||
# reset here would stomp applied symlinks/patches — so we refuse to touch
|
||||
# the clone if it looks modified. Use updateLlvmMos.sh to refresh safely.
|
||||
if [ -d "$LLVM_SRC/.git" ]; then
|
||||
currentBranch="$(git -C "$LLVM_SRC" rev-parse --abbrev-ref HEAD)"
|
||||
if [ "$currentBranch" != "main" ]; then
|
||||
warn "llvm-mos clone is on branch '$currentBranch' (not main); leaving untouched"
|
||||
elif ! git -C "$LLVM_SRC" diff --quiet || ! git -C "$LLVM_SRC" diff --cached --quiet; then
|
||||
warn "llvm-mos clone has local modifications; leaving untouched. Use scripts/updateLlvmMos.sh to refresh."
|
||||
else
|
||||
log "llvm-mos source already cloned; fetching and fast-forwarding main"
|
||||
git -C "$LLVM_SRC" fetch --depth=1 origin main
|
||||
git -C "$LLVM_SRC" merge --ff-only FETCH_HEAD || warn "fast-forward failed; leaving clone as-is"
|
||||
fi
|
||||
else
|
||||
log "cloning llvm-mos (shallow)"
|
||||
git clone --depth=1 https://github.com/llvm-mos/llvm-mos.git "$LLVM_SRC"
|
||||
fi
|
||||
|
||||
# 2. Prebuilt SDK for testing/reference.
|
||||
if [ -x "$LLVM_SDK/bin/mos-common-clang" ] || [ -x "$LLVM_SDK/bin/clang" ]; then
|
||||
log "llvm-mos-sdk already extracted"
|
||||
else
|
||||
archive="$(fetchCached "$SDK_URL" "llvm-mos-linux.tar.xz")"
|
||||
log "extracting llvm-mos-sdk"
|
||||
install -d "$LLVM_SDK"
|
||||
tar -xJf "$archive" -C "$LLVM_SDK" --strip-components=1
|
||||
fi
|
||||
|
||||
# 3. Optional source build.
|
||||
if [ "$doBuild" -eq 1 ]; then
|
||||
needCmd cmake
|
||||
needCmd ninja
|
||||
log "configuring llvm-mos build (this takes a while)"
|
||||
install -d "$LLVM_BUILD"
|
||||
cmake -S "$LLVM_SRC/llvm" -B "$LLVM_BUILD" -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_TARGETS_TO_BUILD="" \
|
||||
-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="MOS" \
|
||||
-DLLVM_ENABLE_PROJECTS="clang;lld" \
|
||||
-DLLVM_PARALLEL_LINK_JOBS=1 \
|
||||
-DLLVM_USE_LINKER=lld \
|
||||
-DLLVM_INCLUDE_TESTS=OFF \
|
||||
-DLLVM_INCLUDE_EXAMPLES=OFF \
|
||||
-DLLVM_INCLUDE_BENCHMARKS=OFF
|
||||
log "building llvm-mos (background-friendly: use --build only when you have time)"
|
||||
ninja -C "$LLVM_BUILD"
|
||||
log "llvm-mos build complete: $LLVM_BUILD/bin/clang"
|
||||
else
|
||||
log "skipped source build; rerun with --build when ready (cmake+ninja)"
|
||||
fi
|
||||
|
||||
log "llvm-mos install done"
|
||||
log " source: $LLVM_SRC"
|
||||
log " sdk: $LLVM_SDK"
|
||||
[ "$doBuild" -eq 1 ] && log " build: $LLVM_BUILD"
|
||||
46
scripts/installMame.sh
Executable file
46
scripts/installMame.sh
Executable file
|
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env bash
|
||||
# Install MAME (via apt) and fetch Apple IIgs ROM sets into the project-local
|
||||
# rompath. Self-contained — MAME is launched with -rompath to point here, so
|
||||
# this does not touch the user's ~/.mame directory.
|
||||
|
||||
set -euo pipefail
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
MAME_ROMS="$TOOLS_DIR/mame/roms"
|
||||
ROM_BASE="https://archive.org/download/mame-0.240-roms-split_202201/MAME%200.240%20ROMs%20(split)"
|
||||
|
||||
install -d "$MAME_ROMS"
|
||||
|
||||
# 1. MAME binary via apt. Ubuntu noble ships 0.264.
|
||||
if ! haveCmd mame; then
|
||||
log "installing mame"
|
||||
sudo apt-get install -y --no-install-recommends mame mame-tools
|
||||
else
|
||||
log "mame already installed: $(mame -version 2>/dev/null | head -1 || true)"
|
||||
fi
|
||||
|
||||
# 2. ROM sets. apple2gs is current ROM 03; apple2gsr1 is ROM 01 (many 1986-era
|
||||
# titles require this variant). Pull both so we can test against either.
|
||||
for romName in apple2gs.zip apple2gsr1.zip; do
|
||||
dest="$MAME_ROMS/$romName"
|
||||
if [ -s "$dest" ]; then
|
||||
log "rom already present: $romName"
|
||||
continue
|
||||
fi
|
||||
log "downloading $romName"
|
||||
curl -fL --retry 3 --progress-bar \
|
||||
-o "$dest.part" "$ROM_BASE/$romName"
|
||||
mv "$dest.part" "$dest"
|
||||
done
|
||||
|
||||
# 3. Quick Lua capability check — we rely on -console for the test harness.
|
||||
if mame -showusage 2>&1 | grep -q -- '-console'; then
|
||||
log "mame Lua console available"
|
||||
else
|
||||
warn "mame binary does not appear to support -console / Lua scripting"
|
||||
fi
|
||||
|
||||
log "mame install done"
|
||||
log " binary: $(command -v mame)"
|
||||
log " rompath: $MAME_ROMS"
|
||||
log " launch: mame -rompath $MAME_ROMS apple2gs"
|
||||
19
scripts/installOrcaC.sh
Executable file
19
scripts/installOrcaC.sh
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env bash
|
||||
# Clone ORCA/C as an on-disk reference. It's an on-machine compiler — we only
|
||||
# need the source to study codegen choices, not to build.
|
||||
|
||||
set -euo pipefail
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
ORCA_DIR="$TOOLS_DIR/orca-c"
|
||||
ORCA_URL="https://github.com/byteworksinc/ORCA-C.git"
|
||||
|
||||
if [ -d "$ORCA_DIR/.git" ]; then
|
||||
log "orca-c already cloned; pulling latest"
|
||||
git -C "$ORCA_DIR" pull --ff-only
|
||||
else
|
||||
log "cloning ORCA/C reference source"
|
||||
git clone --depth=1 "$ORCA_URL" "$ORCA_DIR"
|
||||
fi
|
||||
|
||||
log "orca-c reference at $ORCA_DIR"
|
||||
275
scripts/smokeTest.sh
Executable file
275
scripts/smokeTest.sh
Executable file
|
|
@ -0,0 +1,275 @@
|
|||
#!/usr/bin/env bash
|
||||
# W65816 backend smoke test. Run after any change to confirm the
|
||||
# scaffold still builds and llc still registers the target. Non-zero
|
||||
# exit on any failure.
|
||||
#
|
||||
# Usage: scripts/smokeTest.sh [--build]
|
||||
# --build Run ninja to (re)build LLVMW65816* + llc before testing.
|
||||
# Without this flag the script assumes tools/llvm-mos-build
|
||||
# is already up to date.
|
||||
|
||||
set -euo pipefail
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
BUILD_DIR="$TOOLS_DIR/llvm-mos-build"
|
||||
LLC="$BUILD_DIR/bin/llc"
|
||||
LLVM_MC="$BUILD_DIR/bin/llvm-mc"
|
||||
|
||||
doBuild=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--build) doBuild=1 ;;
|
||||
*) die "unknown flag: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -x "$LLC" ] || die "llc not found at $LLC; run setup.sh and applyBackend.sh, or pass --build"
|
||||
|
||||
if [ "$doBuild" -eq 1 ]; then
|
||||
log "ninja LLVMW65816* llc llvm-mc llvm-objdump"
|
||||
ninja -C "$BUILD_DIR" LLVMW65816Info LLVMW65816Desc LLVMW65816CodeGen \
|
||||
LLVMW65816AsmParser LLVMW65816Disassembler llc llvm-mc llvm-objdump
|
||||
fi
|
||||
|
||||
# 1. Target must be registered.
|
||||
log "check: llc --version lists w65816"
|
||||
if ! "$LLC" --version 2>/dev/null | grep -q "^[[:space:]]*w65816[[:space:]]"; then
|
||||
die "llc does not list the w65816 target"
|
||||
fi
|
||||
|
||||
# 2. Empty IR must compile to nothing.
|
||||
log "check: llc -march=w65816 -filetype=null /dev/null exits 0"
|
||||
"$LLC" -march=w65816 -filetype=null /dev/null
|
||||
|
||||
# 3. Trivial IR that shouldn't touch our (unimplemented) codegen paths.
|
||||
tmp="$(mktemp --suffix=.ll)"
|
||||
trap 'rm -f "$tmp"' EXIT
|
||||
cat > "$tmp" <<'EOF'
|
||||
; ModuleID = 'smoke'
|
||||
target triple = "w65816-unknown-unknown"
|
||||
|
||||
; Empty module: exercises target initialization only.
|
||||
EOF
|
||||
log "check: llc accepts an empty module with w65816 triple"
|
||||
"$LLC" -filetype=null "$tmp"
|
||||
|
||||
# 4. MC layer round-trip. Assemble a representative mix of addressing
|
||||
# modes and mode-switching instructions and grep for the expected
|
||||
# encoding bytes. Hex-byte strings are stable across llvm-mc
|
||||
# formatting changes, unlike full-line string matching.
|
||||
if [ -x "$LLVM_MC" ]; then
|
||||
log "check: llvm-mc -arch=w65816 emits expected encodings"
|
||||
# Only exercise instructions that round-trip cleanly:
|
||||
# - LDA/LDX/LDY immediates without explicit force use the _Imm16
|
||||
# form (codegen-dominant path). A pure `lda #x` assembles to
|
||||
# LDA_Imm16 since the _Imm8 variant is isCodeGenOnly.
|
||||
mcInput=' nop
|
||||
rep #0x30
|
||||
sep #0x20
|
||||
lda #0x1234
|
||||
sta 0x10
|
||||
sta 0x1000
|
||||
sta 0x010000
|
||||
mvn 0x01, 0x02
|
||||
jsl 0x012345'
|
||||
mcOut="$(printf '%s\n' "$mcInput" | "$LLVM_MC" -arch=w65816 -show-encoding 2>&1)"
|
||||
|
||||
assertHas() {
|
||||
if ! printf '%s\n' "$mcOut" | grep -qF "$1"; then
|
||||
warn "missing expected encoding: $1"
|
||||
printf '%s\n' "$mcOut" >&2
|
||||
die "llvm-mc did not produce expected encoding"
|
||||
fi
|
||||
}
|
||||
|
||||
assertHas "[0xea]"
|
||||
assertHas "[0xc2,0x30]"
|
||||
assertHas "[0xe2,0x20]"
|
||||
assertHas "[0xa9,0x34,0x12]"
|
||||
assertHas "[0x85,0x10]"
|
||||
assertHas "[0x8d,0x00,0x10]"
|
||||
assertHas "[0x8f,0x00,0x00,0x01]"
|
||||
assertHas "[0x54,0x01,0x02]"
|
||||
assertHas "[0x22,0x45,0x23,0x01]"
|
||||
else
|
||||
warn "llvm-mc not built; skipping MC round-trip check"
|
||||
fi
|
||||
|
||||
# 5. Disassembler round-trip. A raw byte stream fed to llvm-mc
|
||||
# --disassemble should produce the mnemonic we expect.
|
||||
if [ -x "$LLVM_MC" ]; then
|
||||
log "check: llvm-mc --disassemble decodes bytes back to mnemonics"
|
||||
disasmOut="$(printf '0xea 0xa9 0x34 0x12 0x85 0x10 0x8d 0x00 0x10 0x6b\n' \
|
||||
| "$LLVM_MC" --disassemble --triple=w65816 2>&1)"
|
||||
for mnem in "nop" "lda #0x1234" "sta 0x10" "sta 0x1000" "rtl"; do
|
||||
if ! printf '%s\n' "$disasmOut" | grep -qF "$mnem"; then
|
||||
warn "disassembler missing: $mnem"
|
||||
printf '%s\n' "$disasmOut" >&2
|
||||
die "disassembler round-trip failed"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 6. End-to-end codegen: IR -> asm -> ELF -> disassembly.
|
||||
# This is the first real codegen test: verifies that our LowerReturn,
|
||||
# DAG pattern for the i16 constant pseudo, and prologue-emitting
|
||||
# frame lowering produce runnable 65816 machine code.
|
||||
OBJDUMP="$BUILD_DIR/bin/llvm-objdump"
|
||||
if [ -x "$LLC" ] && [ -x "$LLVM_MC" ] && [ -x "$OBJDUMP" ]; then
|
||||
log "check: end-to-end IR -> asm -> ELF -> disasm for a trivial function"
|
||||
irFile="$(mktemp --suffix=.ll)"
|
||||
sFile="$(mktemp --suffix=.s)"
|
||||
oFile="$(mktemp --suffix=.o)"
|
||||
trap 'rm -f "$irFile" "$sFile" "$oFile"' EXIT
|
||||
cat > "$irFile" <<'EOF'
|
||||
target triple = "w65816-unknown-unknown"
|
||||
define i16 @answer() { ret i16 42 }
|
||||
EOF
|
||||
"$LLC" -march=w65816 "$irFile" -o "$sFile"
|
||||
"$LLVM_MC" -arch=w65816 -filetype=obj "$sFile" -o "$oFile"
|
||||
disasm="$("$OBJDUMP" --triple=w65816 -d "$oFile" 2>&1)"
|
||||
for expect in "rep #0x30" "lda #0x2a" "rtl"; do
|
||||
if ! printf '%s\n' "$disasm" | grep -qF "$expect"; then
|
||||
warn "end-to-end pipeline missing: $expect"
|
||||
printf '%s\n' "$disasm" >&2
|
||||
die "end-to-end pipeline failed"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 7. Real codegen check: a non-trivial function exercising globals,
|
||||
# arithmetic, branches, bitwise. This tests our DAG selection
|
||||
# patterns and AsmPrinter pseudo expansions.
|
||||
if [ -x "$LLC" ]; then
|
||||
log "check: llc compiles a multi-pattern function"
|
||||
irFile="$(mktemp --suffix=.ll)"
|
||||
sFile="$(mktemp --suffix=.s)"
|
||||
trap 'rm -f "$irFile" "$sFile"' EXIT
|
||||
cat > "$irFile" <<'EOF'
|
||||
target triple = "w65816-unknown-unknown"
|
||||
@a = global i16 0
|
||||
@b = global i16 0
|
||||
define i16 @demo() {
|
||||
%x = load i16, ptr @a
|
||||
%y = load i16, ptr @b
|
||||
%s = add i16 %x, %y
|
||||
%m = and i16 %s, 4095
|
||||
%c = icmp ult i16 %m, 100
|
||||
br i1 %c, label %lo, label %hi
|
||||
lo:
|
||||
ret i16 0
|
||||
hi:
|
||||
ret i16 %m
|
||||
}
|
||||
EOF
|
||||
"$LLC" -march=w65816 "$irFile" -o "$sFile"
|
||||
for expect in "rep #0x30" "lda a" "clc" "adc b" "and #0xfff" "cmp #0x64" "bcs" "rtl"; do
|
||||
if ! grep -qF "$expect" "$sFile"; then
|
||||
warn "multi-pattern test missing: $expect"
|
||||
cat "$sFile" >&2
|
||||
die "multi-pattern test failed"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 8. Function call check: caller passes i16 in A, callee adds, returns.
|
||||
if [ -x "$LLC" ]; then
|
||||
log "check: llc compiles a function call (single i16 arg in A)"
|
||||
irCallFile="$(mktemp --suffix=.ll)"
|
||||
sCallFile="$(mktemp --suffix=.s)"
|
||||
trap 'rm -f "$irFile" "$sFile" "$irCallFile" "$sCallFile"' EXIT
|
||||
cat > "$irCallFile" <<'EOF'
|
||||
target triple = "w65816-unknown-unknown"
|
||||
define i16 @inc(i16 %x) {
|
||||
%r = add i16 %x, 1
|
||||
ret i16 %r
|
||||
}
|
||||
define i16 @caller() {
|
||||
%r = call i16 @inc(i16 41)
|
||||
ret i16 %r
|
||||
}
|
||||
EOF
|
||||
"$LLC" -march=w65816 "$irCallFile" -o "$sCallFile"
|
||||
# Caller passes 41 in A and JSL's inc. Inc is now an `inc a`
|
||||
# peephole (was clc; adc #1 before the INA_PSEUDO pattern).
|
||||
for expect in "lda #0x29" "jsl inc" "inc a"; do
|
||||
if ! grep -qF "$expect" "$sCallFile"; then
|
||||
warn "call test missing: $expect"
|
||||
cat "$sCallFile" >&2
|
||||
die "call test failed"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 9. Multi-arg sum: 3-arg function reads args 1 and 2 via stack-relative
|
||||
# addressing.
|
||||
if [ -x "$LLC" ]; then
|
||||
log "check: llc compiles a 3-arg function (stack-relative reads)"
|
||||
irMaFile="$(mktemp --suffix=.ll)"
|
||||
sMaFile="$(mktemp --suffix=.s)"
|
||||
trap 'rm -f "$irFile" "$sFile" "$irCallFile" "$sCallFile" "$irMaFile" "$sMaFile"' EXIT
|
||||
cat > "$irMaFile" <<'EOF'
|
||||
target triple = "w65816-unknown-unknown"
|
||||
define i16 @sum3(i16 %a, i16 %b, i16 %c) {
|
||||
%ab = add i16 %a, %b
|
||||
%r = add i16 %ab, %c
|
||||
ret i16 %r
|
||||
}
|
||||
EOF
|
||||
"$LLC" -march=w65816 "$irMaFile" -o "$sMaFile"
|
||||
for expect in "adc 0x4, s" "adc 0x6, s" "rtl"; do
|
||||
if ! grep -qF "$expect" "$sMaFile"; then
|
||||
warn "multi-arg test missing: $expect"
|
||||
cat "$sMaFile" >&2
|
||||
die "multi-arg test failed"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 10. i8 codegen: pure-i8 function uses SEP #$20 prologue and `inc a`.
|
||||
if [ -x "$LLC" ]; then
|
||||
log "check: llc compiles a pure-i8 function (SEP #\$20 prologue)"
|
||||
irI8File="$(mktemp --suffix=.ll)"
|
||||
sI8File="$(mktemp --suffix=.s)"
|
||||
trap 'rm -f "$irFile" "$sFile" "$irCallFile" "$sCallFile" "$irMaFile" "$sMaFile" "$irI8File" "$sI8File"' EXIT
|
||||
cat > "$irI8File" <<'EOF'
|
||||
target triple = "w65816-unknown-unknown"
|
||||
define i8 @i8_inc(i8 %x) {
|
||||
%r = add i8 %x, 1
|
||||
ret i8 %r
|
||||
}
|
||||
EOF
|
||||
"$LLC" -march=w65816 "$irI8File" -o "$sI8File"
|
||||
for expect in "sep #0x20" "inc a" "rtl"; do
|
||||
if ! grep -qF "$expect" "$sI8File"; then
|
||||
warn "i8 test missing: $expect"
|
||||
cat "$sI8File" >&2
|
||||
die "i8 test failed"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# 11. Real C through clang. Uses the clang front-end if it has been
|
||||
# built; skipped otherwise (clang takes 15-30 minutes to build the
|
||||
# first time; afterwards rebuilds are fast).
|
||||
CLANG="$BUILD_DIR/bin/clang"
|
||||
if [ -x "$CLANG" ] && [ -x "$OBJDUMP" ]; then
|
||||
log "check: clang -target w65816 -O2 compiles a tiny C function"
|
||||
cFile="$(mktemp --suffix=.c)"
|
||||
oFile2="$(mktemp --suffix=.o)"
|
||||
trap 'rm -f "$irFile" "$sFile" "$irCallFile" "$sCallFile" "$irMaFile" "$sMaFile" "$irI8File" "$sI8File" "$cFile" "$oFile2"' EXIT
|
||||
cat > "$cFile" <<'EOF'
|
||||
int answer(void) { return 42; }
|
||||
EOF
|
||||
"$CLANG" --target=w65816 -O2 -c "$cFile" -o "$oFile2"
|
||||
disasm="$("$OBJDUMP" --triple=w65816 -d "$oFile2" 2>&1)"
|
||||
for expect in "rep #0x30" "lda #0x2a" "rtl"; do
|
||||
if ! printf '%s\n' "$disasm" | grep -qF "$expect"; then
|
||||
warn "clang test missing: $expect"
|
||||
printf '%s\n' "$disasm" >&2
|
||||
die "clang end-to-end test failed"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
log "all smoke checks passed"
|
||||
56
scripts/updateLlvmMos.sh
Executable file
56
scripts/updateLlvmMos.sh
Executable file
|
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env bash
|
||||
# Refresh the ephemeral llvm-mos checkout to the latest upstream main,
|
||||
# then re-apply our backend (symlinks + patches).
|
||||
#
|
||||
# This is the ONLY script that is allowed to destructively reset the
|
||||
# clone. installLlvmMos.sh intentionally refuses to do so because a
|
||||
# reset would destroy any applied patches.
|
||||
|
||||
set -euo pipefail
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
LLVM_SRC="$TOOLS_DIR/llvm-mos"
|
||||
|
||||
[ -d "$LLVM_SRC/.git" ] || die "llvm-mos checkout not found; run setup.sh first"
|
||||
|
||||
currentBranch="$(git -C "$LLVM_SRC" rev-parse --abbrev-ref HEAD)"
|
||||
if [ "$currentBranch" != "main" ]; then
|
||||
die "llvm-mos is on branch '$currentBranch', expected main. Refusing to reset."
|
||||
fi
|
||||
|
||||
# First un-apply our patches so the reset only discards upstream drift,
|
||||
# not our work. Symlinks are unaffected by git reset since untracked
|
||||
# directories are left alone (our symlinks live inside tracked dirs but
|
||||
# are untracked files themselves).
|
||||
log "reverting any applied patches"
|
||||
for patch in "$PROJECT_ROOT"/patches/*.patch; do
|
||||
[ -f "$patch" ] || continue
|
||||
if git -C "$LLVM_SRC" apply --reverse --check "$patch" >/dev/null 2>&1; then
|
||||
git -C "$LLVM_SRC" apply --reverse "$patch"
|
||||
log " reverted: $(basename "$patch")"
|
||||
fi
|
||||
done
|
||||
|
||||
# Symlinks from applyBackend.sh are untracked files inside tracked
|
||||
# directories. git reset --hard leaves them in place, but we clean
|
||||
# them proactively so the fast-forward sees a truly clean tree.
|
||||
log "removing symlinks under $LLVM_SRC pointing into $PROJECT_ROOT/src"
|
||||
find "$LLVM_SRC" -type l | while read -r link; do
|
||||
target="$(readlink -f "$link" || true)"
|
||||
case "$target" in
|
||||
"$PROJECT_ROOT"/src/*) rm "$link" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if ! git -C "$LLVM_SRC" diff --quiet || ! git -C "$LLVM_SRC" diff --cached --quiet; then
|
||||
die "llvm-mos checkout still has local changes after cleanup. Inspect with: git -C $LLVM_SRC status"
|
||||
fi
|
||||
|
||||
log "fetching and resetting to origin/main"
|
||||
git -C "$LLVM_SRC" fetch --depth=1 origin main
|
||||
git -C "$LLVM_SRC" reset --hard FETCH_HEAD
|
||||
|
||||
log "re-applying backend"
|
||||
"$PROJECT_ROOT/scripts/applyBackend.sh"
|
||||
|
||||
log "update complete"
|
||||
49
scripts/verify.sh
Executable file
49
scripts/verify.sh
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env bash
|
||||
# Sanity-check every installed tool. Exits non-zero if anything is missing.
|
||||
|
||||
set -euo pipefail
|
||||
source "$(dirname "$0")/common.sh"
|
||||
|
||||
fails=0
|
||||
|
||||
check() {
|
||||
local label="$1"; shift
|
||||
if "$@" >/dev/null 2>&1; then
|
||||
printf ' [ OK ] %s\n' "$label"
|
||||
else
|
||||
printf ' [FAIL] %s (%s)\n' "$label" "$*"
|
||||
fails=$((fails + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
log "verifying toolchain"
|
||||
|
||||
# Build tools
|
||||
check "cmake" cmake --version
|
||||
check "ninja" ninja --version
|
||||
check "clang (host)" clang --version
|
||||
check "git" git --version
|
||||
|
||||
# llvm-mos source + SDK
|
||||
check "llvm-mos source tree" test -d "$TOOLS_DIR/llvm-mos/.git"
|
||||
check "llvm-mos-sdk extracted" test -x "$TOOLS_DIR/llvm-mos-sdk/bin/mos-common-clang"
|
||||
|
||||
# MAME + ROMs
|
||||
check "mame binary" command -v mame
|
||||
check "mame Lua console support" bash -c "mame -showusage 2>&1 | grep -q -- '-console'"
|
||||
check "rom: apple2gs.zip" test -s "$TOOLS_DIR/mame/roms/apple2gs.zip"
|
||||
check "rom: apple2gsr1.zip" test -s "$TOOLS_DIR/mame/roms/apple2gsr1.zip"
|
||||
|
||||
# Calypsi benchmark
|
||||
check "calypsi cc65816" test -x "$TOOLS_DIR/calypsi/bin/cc65816"
|
||||
|
||||
# ORCA/C reference
|
||||
check "orca-c source" test -d "$TOOLS_DIR/orca-c/.git"
|
||||
|
||||
echo
|
||||
if [ "$fails" -eq 0 ]; then
|
||||
log "all checks passed"
|
||||
exit 0
|
||||
else
|
||||
die "$fails check(s) failed"
|
||||
fi
|
||||
88
setup.sh
Executable file
88
setup.sh
Executable file
|
|
@ -0,0 +1,88 @@
|
|||
#!/usr/bin/env bash
|
||||
# Top-level installer for the llvm816 project. Installs everything into
|
||||
# ./tools/ so the tree is self-contained and deletable.
|
||||
#
|
||||
# Usage:
|
||||
# ./setup.sh # install everything (no llvm-mos source build)
|
||||
# ./setup.sh --build-llvm # also cmake+ninja build llvm-mos (slow)
|
||||
# ./setup.sh --skip-deps # skip apt packages
|
||||
# ./setup.sh --skip-llvm # skip llvm-mos
|
||||
# ./setup.sh --skip-mame # skip MAME + ROM fetch
|
||||
# ./setup.sh --skip-calypsi # skip Calypsi
|
||||
# ./setup.sh --skip-orca # skip ORCA/C reference
|
||||
# ./setup.sh --verify-only # run verification only
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/scripts/common.sh"
|
||||
|
||||
doDeps=1
|
||||
doLlvm=1
|
||||
doMame=1
|
||||
doCalypsi=1
|
||||
doOrca=1
|
||||
doVerify=1
|
||||
buildLlvm=0
|
||||
verifyOnly=0
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--skip-deps) doDeps=0 ;;
|
||||
--skip-llvm) doLlvm=0 ;;
|
||||
--skip-mame) doMame=0 ;;
|
||||
--skip-calypsi) doCalypsi=0 ;;
|
||||
--skip-orca) doOrca=0 ;;
|
||||
--skip-verify) doVerify=0 ;;
|
||||
--build-llvm) buildLlvm=1 ;;
|
||||
--verify-only) verifyOnly=1 ;;
|
||||
-h|--help)
|
||||
sed -n '2,15p' "$0"
|
||||
exit 0
|
||||
;;
|
||||
*) die "unknown flag: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$verifyOnly" -eq 1 ]; then
|
||||
exec "$SCRIPT_DIR/scripts/verify.sh"
|
||||
fi
|
||||
|
||||
log "project root: $PROJECT_ROOT"
|
||||
log "tools dir: $TOOLS_DIR"
|
||||
|
||||
if [ "$doDeps" -eq 1 ]; then
|
||||
log "=== 1/5 system dependencies ==="
|
||||
bash "$SCRIPT_DIR/scripts/installDeps.sh"
|
||||
fi
|
||||
|
||||
if [ "$doLlvm" -eq 1 ]; then
|
||||
log "=== 2/5 llvm-mos ==="
|
||||
if [ "$buildLlvm" -eq 1 ]; then
|
||||
bash "$SCRIPT_DIR/scripts/installLlvmMos.sh" --build
|
||||
else
|
||||
bash "$SCRIPT_DIR/scripts/installLlvmMos.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$doMame" -eq 1 ]; then
|
||||
log "=== 3/5 mame + iigs roms ==="
|
||||
bash "$SCRIPT_DIR/scripts/installMame.sh"
|
||||
fi
|
||||
|
||||
if [ "$doCalypsi" -eq 1 ]; then
|
||||
log "=== 4/5 calypsi ==="
|
||||
bash "$SCRIPT_DIR/scripts/installCalypsi.sh"
|
||||
fi
|
||||
|
||||
if [ "$doOrca" -eq 1 ]; then
|
||||
log "=== 5/5 orca-c reference ==="
|
||||
bash "$SCRIPT_DIR/scripts/installOrcaC.sh"
|
||||
fi
|
||||
|
||||
if [ "$doVerify" -eq 1 ]; then
|
||||
log "=== verify ==="
|
||||
bash "$SCRIPT_DIR/scripts/verify.sh" || warn "verification reported failures; see above"
|
||||
fi
|
||||
|
||||
log "setup complete"
|
||||
32
src/clang/lib/Basic/Targets/W65816.cpp
Normal file
32
src/clang/lib/Basic/Targets/W65816.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
//===--- W65816.cpp - Implement W65816 target feature support -------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements W65816 TargetInfo objects.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816.h"
|
||||
#include "clang/Basic/MacroBuilder.h"
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::targets;
|
||||
|
||||
const char *const W65816TargetInfo::GCCRegNames[] = {
|
||||
"a", "x", "y", "sp", "dp", "dbr", "pbr", "pc", "p"
|
||||
};
|
||||
|
||||
ArrayRef<const char *> W65816TargetInfo::getGCCRegNames() const {
|
||||
return llvm::ArrayRef(GCCRegNames);
|
||||
}
|
||||
|
||||
void W65816TargetInfo::getTargetDefines(const LangOptions &Opts,
|
||||
MacroBuilder &Builder) const {
|
||||
Builder.defineMacro("W65816");
|
||||
Builder.defineMacro("__W65816__");
|
||||
Builder.defineMacro("__APPLE2GS__");
|
||||
}
|
||||
84
src/clang/lib/Basic/Targets/W65816.h
Normal file
84
src/clang/lib/Basic/Targets/W65816.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
//===--- W65816.h - Declare W65816 target feature support -------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file declares W65816 TargetInfo objects.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_LIB_BASIC_TARGETS_W65816_H
|
||||
#define LLVM_CLANG_LIB_BASIC_TARGETS_W65816_H
|
||||
|
||||
#include "clang/Basic/TargetInfo.h"
|
||||
#include "clang/Basic/TargetOptions.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/TargetParser/Triple.h"
|
||||
|
||||
namespace clang {
|
||||
namespace targets {
|
||||
|
||||
class LLVM_LIBRARY_VISIBILITY W65816TargetInfo : public TargetInfo {
|
||||
static const char *const GCCRegNames[];
|
||||
|
||||
public:
|
||||
W65816TargetInfo(const llvm::Triple &Triple, const TargetOptions &)
|
||||
: TargetInfo(Triple) {
|
||||
TLSSupported = false;
|
||||
IntWidth = 16;
|
||||
IntAlign = 16;
|
||||
LongWidth = 32;
|
||||
LongLongWidth = 64;
|
||||
LongAlign = LongLongAlign = 16;
|
||||
FloatWidth = 32;
|
||||
FloatAlign = 16;
|
||||
DoubleWidth = LongDoubleWidth = 64;
|
||||
DoubleAlign = LongDoubleAlign = 16;
|
||||
PointerWidth = 16;
|
||||
PointerAlign = 16;
|
||||
SuitableAlign = 16;
|
||||
SizeType = UnsignedInt;
|
||||
IntMaxType = SignedLongLong;
|
||||
IntPtrType = SignedInt;
|
||||
PtrDiffType = SignedInt;
|
||||
SigAtomicType = SignedLong;
|
||||
resetDataLayout("e-m:e-p:16:8-i16:16-i32:16-n8:16-S16");
|
||||
}
|
||||
|
||||
void getTargetDefines(const LangOptions &Opts,
|
||||
MacroBuilder &Builder) const override;
|
||||
|
||||
llvm::SmallVector<Builtin::InfosShard> getTargetBuiltins() const override {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool allowsLargerPreferedTypeAlignment() const override { return false; }
|
||||
|
||||
bool hasFeature(StringRef Feature) const override {
|
||||
return Feature == "w65816";
|
||||
}
|
||||
|
||||
ArrayRef<const char *> getGCCRegNames() const override;
|
||||
|
||||
ArrayRef<TargetInfo::GCCRegAlias> getGCCRegAliases() const override {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool validateAsmConstraint(const char *&Name,
|
||||
TargetInfo::ConstraintInfo &info) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string_view getClobbers() const override { return ""; }
|
||||
|
||||
BuiltinVaListKind getBuiltinVaListKind() const override {
|
||||
return TargetInfo::CharPtrBuiltinVaList;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace targets
|
||||
} // namespace clang
|
||||
#endif // LLVM_CLANG_LIB_BASIC_TARGETS_W65816_H
|
||||
15
src/llvm/lib/Target/W65816/AsmParser/CMakeLists.txt
Normal file
15
src/llvm/lib/Target/W65816/AsmParser/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
add_llvm_component_library(LLVMW65816AsmParser
|
||||
W65816AsmParser.cpp
|
||||
|
||||
LINK_COMPONENTS
|
||||
CodeGenTypes
|
||||
MC
|
||||
MCParser
|
||||
Support
|
||||
TargetParser
|
||||
W65816Desc
|
||||
W65816Info
|
||||
|
||||
ADD_TO_COMPONENT
|
||||
W65816
|
||||
)
|
||||
511
src/llvm/lib/Target/W65816/AsmParser/W65816AsmParser.cpp
Normal file
511
src/llvm/lib/Target/W65816/AsmParser/W65816AsmParser.cpp
Normal file
|
|
@ -0,0 +1,511 @@
|
|||
//===- W65816AsmParser.cpp - Parse W65816 assembly to MCInst instructions -===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Scaffold AsmParser for the WDC 65816 / Apple IIgs target. Modeled after
|
||||
// MSP430AsmParser.cpp but stripped of register-operand and addressing-mode
|
||||
// handling that does not apply to the 65816:
|
||||
//
|
||||
// * The 65816 MC operand set is value-typed only (immediate, address,
|
||||
// PC-relative). Instructions implicitly operate on A, X, Y, S, DBR, PBR,
|
||||
// and D, so the parser never builds register operands. The ", x" / ", y"
|
||||
// suffixes on indexed addressing modes are tokens, not registers.
|
||||
//
|
||||
// * There is no ".b" / ".w" mnemonic suffix; the 65816 encodes operand
|
||||
// width in the opcode byte itself, and DP/Abs/Long are chosen by value
|
||||
// fit, not by mnemonic spelling.
|
||||
//
|
||||
// KNOWN LIMITATION (AsmString vs parser):
|
||||
//
|
||||
// W65816InstrFormats.td bakes the literal text ", x" and ", y" into the
|
||||
// AsmString for indexed forms, e.g. the AsmString for LDA_AbsX is
|
||||
// "lda\t$addr, x". TableGen's AsmMatcher expects distinct operand classes
|
||||
// for each addressing variant rather than literal sub-operand text, so the
|
||||
// generated matcher may fail to route "lda $1000, x" to LDA_AbsX even
|
||||
// though the parser produces (token "lda", addr $1000, token "x").
|
||||
//
|
||||
// The correct long-term fix is to split the operand classes by indexing
|
||||
// (addrAbsIdxX, addrAbsIdxY, addrDPIdxX, addrDPIdxY, ...) and either give
|
||||
// them custom parser methods that consume ", x" / ", y" or let the matcher
|
||||
// own those tokens via ParserMethod/SuperClasses. That is out of scope for
|
||||
// this scaffolding task; for now we accept the matcher miss on indexed
|
||||
// forms while the non-indexed and immediate forms match correctly.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MCTargetDesc/W65816MCTargetDesc.h"
|
||||
#include "TargetInfo/W65816TargetInfo.h"
|
||||
|
||||
#include "llvm/ADT/APInt.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCExpr.h"
|
||||
#include "llvm/MC/MCInst.h"
|
||||
#include "llvm/MC/MCInstrInfo.h"
|
||||
#include "llvm/MC/MCParser/AsmLexer.h"
|
||||
#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
|
||||
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
|
||||
#include "llvm/MC/MCStreamer.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/MC/MCSymbol.h"
|
||||
#include "llvm/MC/TargetRegistry.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/SMLoc.h"
|
||||
|
||||
#define DEBUG_TYPE "w65816-asm-parser"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
/// A parsed W65816 assembly operand.
|
||||
///
|
||||
/// The 65816 MC layer sees only three operand flavors: tokens (mnemonic and
|
||||
/// the literal ", x" / ", y" suffixes baked into AsmStrings), immediates
|
||||
/// (prefixed with '#' in source), and values used as addresses or branch
|
||||
/// targets. There are no register operands on this target; all register
|
||||
/// use is implicit in the opcode.
|
||||
class W65816Operand : public MCParsedAsmOperand {
|
||||
enum KindTy {
|
||||
k_Tok,
|
||||
k_Reg,
|
||||
k_Imm,
|
||||
k_Addr
|
||||
} Kind;
|
||||
|
||||
struct TokTy {
|
||||
const char *Data;
|
||||
unsigned Length;
|
||||
};
|
||||
|
||||
union {
|
||||
TokTy Tok;
|
||||
unsigned RegNum;
|
||||
const MCExpr *Imm;
|
||||
const MCExpr *Addr;
|
||||
};
|
||||
|
||||
SMLoc Start, End;
|
||||
|
||||
public:
|
||||
W65816Operand(StringRef T, SMLoc S)
|
||||
: Kind(k_Tok), Tok({T.data(), (unsigned)T.size()}), Start(S),
|
||||
End(SMLoc::getFromPointer(T.data() + T.size())) {}
|
||||
W65816Operand(unsigned R, SMLoc S, SMLoc E)
|
||||
: Kind(k_Reg), RegNum(R), Start(S), End(E) {}
|
||||
W65816Operand(KindTy K, const MCExpr *E, SMLoc S, SMLoc E2)
|
||||
: Kind(K), Start(S), End(E2) {
|
||||
if (K == k_Imm)
|
||||
Imm = E;
|
||||
else
|
||||
Addr = E;
|
||||
}
|
||||
|
||||
// --- Factory helpers ----------------------------------------------------
|
||||
|
||||
static std::unique_ptr<W65816Operand> createToken(StringRef Str, SMLoc S) {
|
||||
return std::make_unique<W65816Operand>(Str, S);
|
||||
}
|
||||
|
||||
static std::unique_ptr<W65816Operand> createReg(unsigned R, SMLoc S,
|
||||
SMLoc E) {
|
||||
return std::make_unique<W65816Operand>(R, S, E);
|
||||
}
|
||||
|
||||
static std::unique_ptr<W65816Operand> createImm(const MCExpr *Val, SMLoc S,
|
||||
SMLoc E) {
|
||||
return std::make_unique<W65816Operand>(k_Imm, Val, S, E);
|
||||
}
|
||||
|
||||
static std::unique_ptr<W65816Operand> createAddr(const MCExpr *Val, SMLoc S,
|
||||
SMLoc E) {
|
||||
return std::make_unique<W65816Operand>(k_Addr, Val, S, E);
|
||||
}
|
||||
|
||||
// --- MCParsedAsmOperand hooks ------------------------------------------
|
||||
|
||||
bool isReg() const override { return Kind == k_Reg; }
|
||||
bool isImm() const override { return Kind == k_Imm; }
|
||||
bool isToken() const override { return Kind == k_Tok; }
|
||||
bool isMem() const override { return false; }
|
||||
|
||||
MCRegister getReg() const override {
|
||||
assert(Kind == k_Reg && "not a register");
|
||||
return MCRegister(RegNum);
|
||||
}
|
||||
|
||||
StringRef getToken() const {
|
||||
assert(Kind == k_Tok && "not a token");
|
||||
return StringRef(Tok.Data, Tok.Length);
|
||||
}
|
||||
|
||||
SMLoc getStartLoc() const override { return Start; }
|
||||
SMLoc getEndLoc() const override { return End; }
|
||||
|
||||
// --- AsmMatcher operand-class predicates -------------------------------
|
||||
//
|
||||
// The matcher picks the narrowest instruction variant whose operand
|
||||
// predicates all match. Constants narrow by value fit; symbolic
|
||||
// expressions (labels, globals) default to the canonical 16-bit form
|
||||
// for absolute addresses and PC-relative branches, falling through
|
||||
// narrower variants only when the value is a constant that fits.
|
||||
// 24-bit `long` and `pcrel16` are opt-in (only constants in the wider
|
||||
// range, or future explicit addressing-mode syntax).
|
||||
|
||||
static bool constFitsUnsigned(const MCExpr *Expr, unsigned width) {
|
||||
if (const auto *CE = dyn_cast<MCConstantExpr>(Expr)) {
|
||||
int64_t V = CE->getValue();
|
||||
uint64_t mask = width >= 64 ? ~uint64_t(0) : ((uint64_t(1) << width) - 1);
|
||||
if (V >= 0)
|
||||
return uint64_t(V) <= mask;
|
||||
int64_t minSigned = width >= 64 ? INT64_MIN
|
||||
: -(int64_t(1) << (width - 1));
|
||||
return V >= minSigned;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isConstant(const MCExpr *Expr) {
|
||||
return Expr && isa<MCConstantExpr>(Expr);
|
||||
}
|
||||
|
||||
bool isImm8() const { return isImm() && constFitsUnsigned(Imm, 8); }
|
||||
bool isImm16() const {
|
||||
return isImm() && (!isConstant(Imm) || constFitsUnsigned(Imm, 16));
|
||||
}
|
||||
|
||||
bool isAddrDP() const {
|
||||
return Kind == k_Addr && isConstant(Addr) && constFitsUnsigned(Addr, 8);
|
||||
}
|
||||
bool isAddrAbs() const {
|
||||
return Kind == k_Addr &&
|
||||
(!isConstant(Addr) || constFitsUnsigned(Addr, 16));
|
||||
}
|
||||
bool isAddrLong() const {
|
||||
if (Kind != k_Addr)
|
||||
return false;
|
||||
if (!isConstant(Addr))
|
||||
// Symbolic — accept; if a same-mnemonic narrower form exists (Abs)
|
||||
// the matcher will prefer it.
|
||||
return true;
|
||||
// Constant — only match if it doesn't already fit in 16 bits, so
|
||||
// pure constants in the 0..0xFFFF range route to AddrAbs.
|
||||
return constFitsUnsigned(Addr, 24) && !constFitsUnsigned(Addr, 16);
|
||||
}
|
||||
|
||||
bool isPCRel8() const {
|
||||
return Kind == k_Addr && isConstant(Addr) && constFitsUnsigned(Addr, 8);
|
||||
}
|
||||
bool isPCRel16() const {
|
||||
return Kind == k_Addr &&
|
||||
(!isConstant(Addr) || constFitsUnsigned(Addr, 16));
|
||||
}
|
||||
|
||||
// --- addXxxOperands used by the generated matcher ----------------------
|
||||
|
||||
void addExprOperand(MCInst &Inst, const MCExpr *Expr) const {
|
||||
if (!Expr)
|
||||
Inst.addOperand(MCOperand::createImm(0));
|
||||
else if (const MCConstantExpr *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");
|
||||
assert(Kind == k_Imm && "not an immediate");
|
||||
addExprOperand(Inst, Imm);
|
||||
}
|
||||
|
||||
void addRegOperands(MCInst &Inst, unsigned N) const {
|
||||
assert(N == 1 && "invalid number of operands");
|
||||
assert(Kind == k_Reg && "not a register");
|
||||
Inst.addOperand(MCOperand::createReg(MCRegister(RegNum)));
|
||||
}
|
||||
|
||||
// Address operand classes (DP/Abs/Long) and PC-relative branch targets
|
||||
// all render through the same path: emit either a constant or an
|
||||
// MCExpr. The width-specific encoding is the encoder's job, not ours.
|
||||
void addAddrDPOperands(MCInst &Inst, unsigned N) const {
|
||||
assert(N == 1 && "invalid number of operands");
|
||||
assert(Kind == k_Addr && "not an address");
|
||||
addExprOperand(Inst, Addr);
|
||||
}
|
||||
void addAddrAbsOperands(MCInst &Inst, unsigned N) const {
|
||||
addAddrDPOperands(Inst, N);
|
||||
}
|
||||
void addAddrLongOperands(MCInst &Inst, unsigned N) const {
|
||||
addAddrDPOperands(Inst, N);
|
||||
}
|
||||
void addPCRel8Operands(MCInst &Inst, unsigned N) const {
|
||||
addAddrDPOperands(Inst, N);
|
||||
}
|
||||
void addPCRel16Operands(MCInst &Inst, unsigned N) const {
|
||||
addAddrDPOperands(Inst, N);
|
||||
}
|
||||
|
||||
void print(raw_ostream &O, const MCAsmInfo &MAI) const override {
|
||||
switch (Kind) {
|
||||
case k_Tok:
|
||||
O << "Token " << StringRef(Tok.Data, Tok.Length);
|
||||
break;
|
||||
case k_Imm:
|
||||
O << "Immediate ";
|
||||
MAI.printExpr(O, *Imm);
|
||||
break;
|
||||
case k_Addr:
|
||||
O << "Address ";
|
||||
MAI.printExpr(O, *Addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Parses W65816 assembly from a stream.
|
||||
class W65816AsmParser : public MCTargetAsmParser {
|
||||
MCAsmParser &Parser;
|
||||
|
||||
bool matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
|
||||
OperandVector &Operands, MCStreamer &Out,
|
||||
uint64_t &ErrorInfo,
|
||||
bool MatchingInlineAsm) override;
|
||||
|
||||
// The 65816 has no register operands. Both register hooks always
|
||||
// report no-match; the lexer-driven operand parser never asks for one
|
||||
// either. We still have to implement them because MCTargetAsmParser
|
||||
// declares them pure virtual / abstract.
|
||||
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 parseLiteralValues(unsigned Size, SMLoc L);
|
||||
|
||||
bool parseOperand(OperandVector &Operands);
|
||||
|
||||
MCAsmParser &getParser() const { return Parser; }
|
||||
AsmLexer &getLexer() const { return Parser.getLexer(); }
|
||||
|
||||
/// @name Auto-generated Matcher Functions
|
||||
/// {
|
||||
|
||||
#define GET_ASSEMBLER_HEADER
|
||||
#include "W65816GenAsmMatcher.inc"
|
||||
|
||||
/// }
|
||||
|
||||
public:
|
||||
W65816AsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,
|
||||
const MCInstrInfo &MII, const MCTargetOptions &Options)
|
||||
: MCTargetAsmParser(Options, STI, MII), Parser(Parser) {
|
||||
MCAsmParserExtension::Initialize(Parser);
|
||||
setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
bool W65816AsmParser::matchAndEmitInstruction(SMLoc Loc, 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(Loc);
|
||||
Out.emitInstruction(Inst, *STI);
|
||||
return false;
|
||||
case Match_MnemonicFail:
|
||||
return Error(Loc, "invalid instruction mnemonic");
|
||||
case Match_InvalidOperand: {
|
||||
SMLoc ErrorLoc = Loc;
|
||||
if (ErrorInfo != ~0U) {
|
||||
if (ErrorInfo >= Operands.size())
|
||||
return Error(ErrorLoc, "too few operands for instruction");
|
||||
ErrorLoc = ((W65816Operand &)*Operands[ErrorInfo]).getStartLoc();
|
||||
if (ErrorLoc == SMLoc())
|
||||
ErrorLoc = Loc;
|
||||
}
|
||||
return Error(ErrorLoc, "invalid operand for instruction");
|
||||
}
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool W65816AsmParser::parseRegister(MCRegister &Reg, SMLoc &StartLoc,
|
||||
SMLoc &EndLoc) {
|
||||
// No register operands on the 65816. A true return signals failure.
|
||||
return true;
|
||||
}
|
||||
|
||||
ParseStatus W65816AsmParser::tryParseRegister(MCRegister &Reg, SMLoc &StartLoc,
|
||||
SMLoc &EndLoc) {
|
||||
return ParseStatus::NoMatch;
|
||||
}
|
||||
|
||||
bool W65816AsmParser::parseInstruction(ParseInstructionInfo &Info,
|
||||
StringRef Name, SMLoc NameLoc,
|
||||
OperandVector &Operands) {
|
||||
// Some 65816 implied-accumulator forms appear in our AsmStrings as a
|
||||
// two-word spelling ("inc a", "asl a", etc.). Fold the " a" back into
|
||||
// the mnemonic token here so the matcher sees exactly what the printer
|
||||
// emitted. The bare "inc"/"asl"/... spellings remain routed to their
|
||||
// memory variants.
|
||||
Operands.push_back(W65816Operand::createToken(Name, NameLoc));
|
||||
|
||||
// If there are no operands, we're done (covers nop, rts, rtl, clc, etc.
|
||||
// and the implied-accumulator forms handled above).
|
||||
if (getLexer().is(AsmToken::EndOfStatement)) {
|
||||
Parser.Lex();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implied-accumulator form: "<mnem> a" (e.g. "inc a", "asl a"). The
|
||||
// AsmString for these uses the A register as an operand, so the matcher
|
||||
// expects a register operand here, not a literal token.
|
||||
if (getLexer().is(AsmToken::Identifier) &&
|
||||
getLexer().getTok().getIdentifier().equals_insensitive("a")) {
|
||||
SMLoc S = getLexer().getTok().getLoc();
|
||||
SMLoc E = SMLoc::getFromPointer(S.getPointer() + 1);
|
||||
Operands.push_back(W65816Operand::createReg(W65816::A, S, E));
|
||||
Parser.Lex(); // eat "a"
|
||||
if (getLexer().isNot(AsmToken::EndOfStatement)) {
|
||||
SMLoc ErrLoc = getLexer().getLoc();
|
||||
Parser.eatToEndOfStatement();
|
||||
return Error(ErrLoc, "unexpected token");
|
||||
}
|
||||
Parser.Lex();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the first real operand (immediate or address expression).
|
||||
if (parseOperand(Operands))
|
||||
return true;
|
||||
|
||||
// Handle trailing ", x" / ", y" / ", <expr>" (block-move second operand).
|
||||
while (getLexer().is(AsmToken::Comma)) {
|
||||
Parser.Lex(); // eat comma
|
||||
|
||||
// Indexed suffix: ", x" / ", y" / ", s". X and Y are real
|
||||
// register operands at the matcher level (the matcher generates
|
||||
// MCK_X / MCK_Y from the named registers). S has no register def
|
||||
// — the matcher treats it as a literal token (MCK_s) — so we push
|
||||
// it as a token instead of as a register.
|
||||
if (getLexer().is(AsmToken::Identifier)) {
|
||||
StringRef Ident = getLexer().getTok().getIdentifier();
|
||||
if (Ident.equals_insensitive("x") || Ident.equals_insensitive("y")) {
|
||||
SMLoc S = getLexer().getTok().getLoc();
|
||||
SMLoc E = SMLoc::getFromPointer(S.getPointer() + 1);
|
||||
unsigned Reg = Ident.equals_insensitive("x") ? W65816::X
|
||||
: W65816::Y;
|
||||
Operands.push_back(W65816Operand::createReg(Reg, S, E));
|
||||
Parser.Lex();
|
||||
continue;
|
||||
}
|
||||
if (Ident.equals_insensitive("s")) {
|
||||
SMLoc S = getLexer().getTok().getLoc();
|
||||
Operands.push_back(W65816Operand::createToken("s", S));
|
||||
Parser.Lex();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise it must be another expression operand (e.g. block-move
|
||||
// "mvn dst, src" where both operands are immediate bank bytes).
|
||||
if (parseOperand(Operands))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getLexer().isNot(AsmToken::EndOfStatement)) {
|
||||
SMLoc ErrLoc = getLexer().getLoc();
|
||||
Parser.eatToEndOfStatement();
|
||||
return Error(ErrLoc, "unexpected token");
|
||||
}
|
||||
|
||||
Parser.Lex(); // consume EndOfStatement
|
||||
return false;
|
||||
}
|
||||
|
||||
bool W65816AsmParser::parseOperand(OperandVector &Operands) {
|
||||
SMLoc S = getLexer().getLoc();
|
||||
|
||||
// Immediate form: '#' <expression>. The generated AsmMatcher tokenises
|
||||
// '#' in the AsmString (e.g. "lda\t#$imm") as a separate MCK__HASH_
|
||||
// token, so we push it as a literal and then the immediate operand.
|
||||
if (getLexer().is(AsmToken::Hash)) {
|
||||
SMLoc HashLoc = S;
|
||||
Parser.Lex(); // eat '#'
|
||||
Operands.push_back(W65816Operand::createToken("#", HashLoc));
|
||||
SMLoc ImmStart = getLexer().getLoc();
|
||||
const MCExpr *Val;
|
||||
if (Parser.parseExpression(Val))
|
||||
return Error(HashLoc, "expected immediate expression after '#'");
|
||||
SMLoc E = getLexer().getLoc();
|
||||
Operands.push_back(W65816Operand::createImm(Val, ImmStart, E));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Anything else is an address / branch-target / block-move byte. We
|
||||
// rely on the standard LLVM asm expression parser, which accepts
|
||||
// identifiers (labels), decimal integers, hex ($... via MCAsmInfo
|
||||
// configuration or 0x...), binary (%... when the lexer allows it),
|
||||
// and arithmetic.
|
||||
const MCExpr *Val;
|
||||
if (Parser.parseExpression(Val))
|
||||
return Error(S, "expected expression");
|
||||
SMLoc E = getLexer().getLoc();
|
||||
Operands.push_back(W65816Operand::createAddr(Val, S, E));
|
||||
return false;
|
||||
}
|
||||
|
||||
ParseStatus W65816AsmParser::parseDirective(AsmToken DirectiveID) {
|
||||
StringRef IDVal = DirectiveID.getIdentifier().lower();
|
||||
unsigned Size = 0;
|
||||
if (IDVal == ".long")
|
||||
Size = 4;
|
||||
else if (IDVal == ".word" || IDVal == ".short")
|
||||
Size = 2;
|
||||
else if (IDVal == ".byte")
|
||||
Size = 1;
|
||||
else
|
||||
return ParseStatus::NoMatch;
|
||||
|
||||
if (parseLiteralValues(Size, DirectiveID.getLoc()))
|
||||
return ParseStatus::Failure;
|
||||
return ParseStatus::Success;
|
||||
}
|
||||
|
||||
bool W65816AsmParser::parseLiteralValues(unsigned Size, SMLoc L) {
|
||||
auto parseOne = [&]() -> bool {
|
||||
const MCExpr *Value;
|
||||
if (getParser().parseExpression(Value))
|
||||
return true;
|
||||
getParser().getStreamer().emitValue(Value, Size, L);
|
||||
return false;
|
||||
};
|
||||
return (parseMany(parseOne));
|
||||
}
|
||||
|
||||
extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
|
||||
LLVMInitializeW65816AsmParser() {
|
||||
RegisterMCAsmParser<W65816AsmParser> X(getTheW65816Target());
|
||||
}
|
||||
|
||||
#define GET_REGISTER_MATCHER
|
||||
#define GET_MATCHER_IMPLEMENTATION
|
||||
#include "W65816GenAsmMatcher.inc"
|
||||
51
src/llvm/lib/Target/W65816/CMakeLists.txt
Normal file
51
src/llvm/lib/Target/W65816/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
add_llvm_component_group(W65816)
|
||||
|
||||
set(LLVM_TARGET_DEFINITIONS W65816.td)
|
||||
|
||||
tablegen(LLVM W65816GenAsmMatcher.inc -gen-asm-matcher)
|
||||
tablegen(LLVM W65816GenAsmWriter.inc -gen-asm-writer)
|
||||
tablegen(LLVM W65816GenCallingConv.inc -gen-callingconv)
|
||||
tablegen(LLVM W65816GenDAGISel.inc -gen-dag-isel)
|
||||
tablegen(LLVM W65816GenDisassemblerTables.inc -gen-disassembler)
|
||||
tablegen(LLVM W65816GenInstrInfo.inc -gen-instr-info)
|
||||
tablegen(LLVM W65816GenMCCodeEmitter.inc -gen-emitter)
|
||||
tablegen(LLVM W65816GenRegisterInfo.inc -gen-register-info)
|
||||
tablegen(LLVM W65816GenSDNodeInfo.inc -gen-sd-node-info)
|
||||
tablegen(LLVM W65816GenSubtargetInfo.inc -gen-subtarget)
|
||||
|
||||
add_public_tablegen_target(W65816CommonTableGen)
|
||||
|
||||
add_llvm_target(W65816CodeGen
|
||||
W65816ISelDAGToDAG.cpp
|
||||
W65816ISelLowering.cpp
|
||||
W65816InstrInfo.cpp
|
||||
W65816FrameLowering.cpp
|
||||
W65816MachineFunctionInfo.cpp
|
||||
W65816RegisterInfo.cpp
|
||||
W65816SelectionDAGInfo.cpp
|
||||
W65816Subtarget.cpp
|
||||
W65816TargetMachine.cpp
|
||||
W65816AsmPrinter.cpp
|
||||
W65816MCInstLower.cpp
|
||||
|
||||
LINK_COMPONENTS
|
||||
AsmPrinter
|
||||
CodeGen
|
||||
CodeGenTypes
|
||||
Core
|
||||
MC
|
||||
SelectionDAG
|
||||
Support
|
||||
Target
|
||||
TargetParser
|
||||
W65816Desc
|
||||
W65816Info
|
||||
|
||||
ADD_TO_COMPONENT
|
||||
W65816
|
||||
)
|
||||
|
||||
add_subdirectory(AsmParser)
|
||||
add_subdirectory(Disassembler)
|
||||
add_subdirectory(MCTargetDesc)
|
||||
add_subdirectory(TargetInfo)
|
||||
13
src/llvm/lib/Target/W65816/Disassembler/CMakeLists.txt
Normal file
13
src/llvm/lib/Target/W65816/Disassembler/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
add_llvm_component_library(LLVMW65816Disassembler
|
||||
W65816Disassembler.cpp
|
||||
|
||||
LINK_COMPONENTS
|
||||
MCDisassembler
|
||||
Support
|
||||
TargetParser
|
||||
W65816Desc
|
||||
W65816Info
|
||||
|
||||
ADD_TO_COMPONENT
|
||||
W65816
|
||||
)
|
||||
190
src/llvm/lib/Target/W65816/Disassembler/W65816Disassembler.cpp
Normal file
190
src/llvm/lib/Target/W65816/Disassembler/W65816Disassembler.cpp
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
//===-- W65816Disassembler.cpp - Disassembler for W65816 -----------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the W65816Disassembler class, which converts a byte
|
||||
// stream into a stream of MCInsts. W65816 instructions are 1-4 bytes long:
|
||||
// a single-byte opcode followed by 0-3 bytes of little-endian operand data.
|
||||
//
|
||||
// For every opcode that has both an _Imm8 and _Imm16 variant (LDA, LDX, LDY,
|
||||
// ADC, SBC, CMP, AND, ORA, EOR, BIT, CPX, CPY), the bytes on the wire are
|
||||
// identical for both forms up to the opcode; the real operand width depends
|
||||
// on the M/X processor-mode bits. The TableGen descriptions put the _Imm8
|
||||
// forms into the "W65816MHigh" / "W65816XHigh" decoder namespaces, so the
|
||||
// default "W65816" table contains only the 3-byte _Imm16 forms. This
|
||||
// scaffold disassembler reads exclusively from the default table, so those
|
||||
// mnemonics always decode as 3-byte (16-bit) immediates. A future mode-
|
||||
// tracking pass will consult the MHigh / XHigh tables when MReg / XReg are
|
||||
// set by preceding REP/SEP instructions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MCTargetDesc/W65816MCTargetDesc.h"
|
||||
#include "TargetInfo/W65816TargetInfo.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCDecoder.h"
|
||||
#include "llvm/MC/MCDecoderOps.h"
|
||||
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
|
||||
#include "llvm/MC/MCInst.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/MC/TargetRegistry.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::MCD;
|
||||
|
||||
#define DEBUG_TYPE "w65816-disassembler"
|
||||
|
||||
using DecodeStatus = MCDisassembler::DecodeStatus;
|
||||
|
||||
namespace {
|
||||
|
||||
class W65816Disassembler : public MCDisassembler {
|
||||
public:
|
||||
W65816Disassembler(const MCSubtargetInfo &STI, MCContext &Ctx)
|
||||
: MCDisassembler(STI, Ctx) {}
|
||||
|
||||
DecodeStatus getInstruction(MCInst &MI, uint64_t &Size,
|
||||
ArrayRef<uint8_t> Bytes, uint64_t Address,
|
||||
raw_ostream &CStream) const override;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Operand decoder callbacks. The encodings are little-endian immediates
|
||||
// immediately following the opcode; each callback just creates an MCOperand
|
||||
// whose integer value is the raw field bits. The printer does the pretty-
|
||||
// printing (hex, '$' prefix, etc.).
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static DecodeStatus decodeImm8(MCInst &Inst, uint64_t Imm, uint64_t Address,
|
||||
const MCDisassembler *Decoder) {
|
||||
Inst.addOperand(MCOperand::createImm(Imm & 0xFF));
|
||||
return MCDisassembler::Success;
|
||||
}
|
||||
|
||||
|
||||
static DecodeStatus decodeImm16(MCInst &Inst, uint64_t Imm, uint64_t Address,
|
||||
const MCDisassembler *Decoder) {
|
||||
Inst.addOperand(MCOperand::createImm(Imm & 0xFFFF));
|
||||
return MCDisassembler::Success;
|
||||
}
|
||||
|
||||
|
||||
static DecodeStatus decodeAddr8(MCInst &Inst, uint64_t Imm, uint64_t Address,
|
||||
const MCDisassembler *Decoder) {
|
||||
Inst.addOperand(MCOperand::createImm(Imm & 0xFF));
|
||||
return MCDisassembler::Success;
|
||||
}
|
||||
|
||||
|
||||
static DecodeStatus decodeAddr16(MCInst &Inst, uint64_t Imm, uint64_t Address,
|
||||
const MCDisassembler *Decoder) {
|
||||
Inst.addOperand(MCOperand::createImm(Imm & 0xFFFF));
|
||||
return MCDisassembler::Success;
|
||||
}
|
||||
|
||||
|
||||
static DecodeStatus decodeAddr24(MCInst &Inst, uint64_t Imm, uint64_t Address,
|
||||
const MCDisassembler *Decoder) {
|
||||
Inst.addOperand(MCOperand::createImm(Imm & 0xFFFFFF));
|
||||
return MCDisassembler::Success;
|
||||
}
|
||||
|
||||
|
||||
static DecodeStatus decodePCRel8(MCInst &Inst, uint64_t Imm, uint64_t Address,
|
||||
const MCDisassembler *Decoder) {
|
||||
// Sign-extend the 8-bit displacement so the printer shows the correct
|
||||
// signed offset. Address-symbolization happens later if requested.
|
||||
int8_t Disp = static_cast<int8_t>(Imm & 0xFF);
|
||||
Inst.addOperand(MCOperand::createImm(Disp));
|
||||
return MCDisassembler::Success;
|
||||
}
|
||||
|
||||
|
||||
static DecodeStatus decodePCRel16(MCInst &Inst, uint64_t Imm, uint64_t Address,
|
||||
const MCDisassembler *Decoder) {
|
||||
int16_t Disp = static_cast<int16_t>(Imm & 0xFFFF);
|
||||
Inst.addOperand(MCOperand::createImm(Disp));
|
||||
return MCDisassembler::Success;
|
||||
}
|
||||
|
||||
|
||||
// The generated decoder tables reference the decode callbacks above by name,
|
||||
// so this include must come AFTER their declarations.
|
||||
#include "W65816GenDisassemblerTables.inc"
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Size-dispatch: try each 1/2/3/4-byte table in turn from shortest to longest,
|
||||
// accepting the first successful decode. The tablegen tables are keyed on
|
||||
// the full little-endian byte pattern, so a single-byte implied-operand
|
||||
// opcode like NOP (0xEA) matches in DecoderTableW658168 and shorter-circuits
|
||||
// any accidental collision with a longer encoding that happens to begin with
|
||||
// the same byte. Every W65816 opcode byte uniquely determines the
|
||||
// instruction (modulo the M/X-mode ambiguity documented above), so the
|
||||
// first successful decode is always the correct one.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
DecodeStatus W65816Disassembler::getInstruction(MCInst &MI, uint64_t &Size,
|
||||
ArrayRef<uint8_t> Bytes,
|
||||
uint64_t Address,
|
||||
raw_ostream &CStream) const {
|
||||
struct TableEntry {
|
||||
const uint8_t *Table;
|
||||
unsigned Bytes;
|
||||
};
|
||||
|
||||
static const TableEntry Tables[] = {
|
||||
{ DecoderTableW658168, 1 },
|
||||
{ DecoderTableW6581616, 2 },
|
||||
{ DecoderTableW6581624, 3 },
|
||||
{ DecoderTableW6581632, 4 },
|
||||
};
|
||||
|
||||
for (const TableEntry &T : Tables) {
|
||||
if (Bytes.size() < T.Bytes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t Insn = 0;
|
||||
for (unsigned I = 0; I < T.Bytes; ++I) {
|
||||
Insn |= static_cast<uint64_t>(Bytes[I]) << (8 * I);
|
||||
}
|
||||
|
||||
MCInst Candidate;
|
||||
DecodeStatus Result = decodeInstruction(T.Table, Candidate, Insn, Address,
|
||||
this, STI);
|
||||
if (Result != MCDisassembler::Fail) {
|
||||
MI = Candidate;
|
||||
Size = T.Bytes;
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
Size = 1;
|
||||
return MCDisassembler::Fail;
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Registration.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static MCDisassembler *createW65816Disassembler(const Target &T,
|
||||
const MCSubtargetInfo &STI,
|
||||
MCContext &Ctx) {
|
||||
return new W65816Disassembler(STI, Ctx);
|
||||
}
|
||||
|
||||
|
||||
extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
|
||||
LLVMInitializeW65816Disassembler() {
|
||||
TargetRegistry::RegisterMCDisassembler(getTheW65816Target(),
|
||||
createW65816Disassembler);
|
||||
}
|
||||
17
src/llvm/lib/Target/W65816/MCTargetDesc/CMakeLists.txt
Normal file
17
src/llvm/lib/Target/W65816/MCTargetDesc/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
add_llvm_component_library(LLVMW65816Desc
|
||||
W65816AsmBackend.cpp
|
||||
W65816ELFObjectWriter.cpp
|
||||
W65816ELFStreamer.cpp
|
||||
W65816InstPrinter.cpp
|
||||
W65816MCAsmInfo.cpp
|
||||
W65816MCCodeEmitter.cpp
|
||||
W65816MCTargetDesc.cpp
|
||||
|
||||
LINK_COMPONENTS
|
||||
MC
|
||||
Support
|
||||
W65816Info
|
||||
|
||||
ADD_TO_COMPONENT
|
||||
W65816
|
||||
)
|
||||
116
src/llvm/lib/Target/W65816/MCTargetDesc/W65816AsmBackend.cpp
Normal file
116
src/llvm/lib/Target/W65816/MCTargetDesc/W65816AsmBackend.cpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
//===-- W65816AsmBackend.cpp - W65816 Assembler Backend -------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Skeleton assembler backend. Fixup resolution, relaxation and nop
|
||||
// generation are left unimplemented; they will be filled in once the
|
||||
// instruction encodings are defined.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MCTargetDesc/W65816FixupKinds.h"
|
||||
#include "MCTargetDesc/W65816MCTargetDesc.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/MC/MCAsmBackend.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCELFObjectWriter.h"
|
||||
#include "llvm/MC/MCObjectWriter.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/MC/MCTargetOptions.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
class W65816AsmBackend : public MCAsmBackend {
|
||||
uint8_t OSABI;
|
||||
|
||||
public:
|
||||
W65816AsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI)
|
||||
: MCAsmBackend(llvm::endianness::little), OSABI(OSABI) {}
|
||||
~W65816AsmBackend() override = default;
|
||||
|
||||
void applyFixup(const MCFragment &F, const MCFixup &Fixup,
|
||||
const MCValue &Target, uint8_t *Data, uint64_t Value,
|
||||
bool IsResolved) override {
|
||||
maybeAddReloc(F, Fixup, Target, Value, IsResolved);
|
||||
if (!IsResolved) {
|
||||
// Leave the bytes zero; the linker will fill them via the recorded
|
||||
// relocation.
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned Offset = Fixup.getOffset();
|
||||
unsigned Width;
|
||||
switch (Fixup.getKind()) {
|
||||
case W65816::fixup_8:
|
||||
case W65816::fixup_8_pcrel:
|
||||
Width = 1;
|
||||
break;
|
||||
case W65816::fixup_16:
|
||||
case W65816::fixup_16_pcrel:
|
||||
Width = 2;
|
||||
break;
|
||||
case W65816::fixup_24:
|
||||
Width = 3;
|
||||
break;
|
||||
default:
|
||||
// Generic FK_Data_* kinds are already handled by the generic code
|
||||
// in the object writer; nothing to patch here.
|
||||
return;
|
||||
}
|
||||
|
||||
// Little-endian patch.
|
||||
for (unsigned i = 0; i < Width; ++i) {
|
||||
Data[Offset + i] = static_cast<uint8_t>((Value >> (8 * i)) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<MCObjectTargetWriter>
|
||||
createObjectTargetWriter() const override {
|
||||
return createW65816ELFObjectWriter(OSABI);
|
||||
}
|
||||
|
||||
MCFixupKindInfo getFixupKindInfo(MCFixupKind Kind) const override {
|
||||
// clang-format off
|
||||
const static MCFixupKindInfo Infos[W65816::NumTargetFixupKinds] = {
|
||||
// name offset bits flags
|
||||
{"fixup_8", 0, 8, 0},
|
||||
{"fixup_16", 0, 16, 0},
|
||||
{"fixup_24", 0, 24, 0},
|
||||
{"fixup_8_pcrel", 0, 8, 0},
|
||||
{"fixup_16_pcrel", 0, 16, 0},
|
||||
};
|
||||
// clang-format on
|
||||
static_assert(std::size(Infos) == W65816::NumTargetFixupKinds,
|
||||
"Not all fixup kinds added to Infos array");
|
||||
|
||||
if (Kind < FirstTargetFixupKind)
|
||||
return MCAsmBackend::getFixupKindInfo(Kind);
|
||||
|
||||
return Infos[Kind - FirstTargetFixupKind];
|
||||
}
|
||||
|
||||
bool writeNopData(raw_ostream &OS, uint64_t Count,
|
||||
const MCSubtargetInfo *STI) const override {
|
||||
// The 65816 NOP is a single 0xEA byte.
|
||||
for (uint64_t I = 0; I < Count; ++I)
|
||||
OS << char(0xEA);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
MCAsmBackend *llvm::createW65816MCAsmBackend(const Target &T,
|
||||
const MCSubtargetInfo &STI,
|
||||
const MCRegisterInfo &MRI,
|
||||
const MCTargetOptions &Options) {
|
||||
return new W65816AsmBackend(STI, ELF::ELFOSABI_STANDALONE);
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
//===-- W65816ELFObjectWriter.cpp - W65816 ELF Writer ---------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Skeleton ELF object writer. Relocation types will be assigned once the
|
||||
// W65816 ELF ABI is finalised.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MCTargetDesc/W65816FixupKinds.h"
|
||||
#include "MCTargetDesc/W65816MCTargetDesc.h"
|
||||
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/MC/MCELFObjectWriter.h"
|
||||
#include "llvm/MC/MCFixup.h"
|
||||
#include "llvm/MC/MCObjectWriter.h"
|
||||
#include "llvm/MC/MCValue.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
class W65816ELFObjectWriter : public MCELFObjectTargetWriter {
|
||||
public:
|
||||
// EM_NONE is a placeholder -- the real EM_ value for 65816 will be supplied
|
||||
// once the llvm-mos ELF specification is extended for the W65816 target.
|
||||
explicit W65816ELFObjectWriter(uint8_t OSABI)
|
||||
: MCELFObjectTargetWriter(/*Is64Bit=*/false, OSABI, ELF::EM_NONE,
|
||||
/*HasRelocationAddend=*/true) {}
|
||||
|
||||
~W65816ELFObjectWriter() override = default;
|
||||
|
||||
protected:
|
||||
unsigned getRelocType(const MCFixup &Fixup, const MCValue &,
|
||||
bool IsPCRel) const override {
|
||||
// Placeholder relocation numbers. We are using EM_NONE so the full
|
||||
// (EM_, R_*) pair is unique; once a real EM_ value is assigned for the
|
||||
// W65816 target (see SESSION_STATE.md open question on ELF EM_), swap
|
||||
// these for the canonical R_W65816_* names.
|
||||
switch (Fixup.getKind()) {
|
||||
case W65816::fixup_8: return 1; // R_W65816_IMM8
|
||||
case W65816::fixup_16: return 2; // R_W65816_IMM16
|
||||
case W65816::fixup_24: return 3; // R_W65816_IMM24
|
||||
case W65816::fixup_8_pcrel: return 4; // R_W65816_PCREL8
|
||||
case W65816::fixup_16_pcrel: return 5; // R_W65816_PCREL16
|
||||
default:
|
||||
llvm_unreachable("W65816: unknown fixup kind");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
std::unique_ptr<MCObjectTargetWriter>
|
||||
llvm::createW65816ELFObjectWriter(uint8_t OSABI) {
|
||||
return std::make_unique<W65816ELFObjectWriter>(OSABI);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
//===-- W65816ELFStreamer.cpp - W65816 ELF Target Streamer Methods --------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides a stub W65816 ELF target streamer. The MSP430 reference
|
||||
// emits a vendor build-attributes section here; the W65816 equivalent (code
|
||||
// model, DP reservation, etc.) will be added later.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816MCTargetDesc.h"
|
||||
#include "llvm/MC/MCELFStreamer.h"
|
||||
#include "llvm/MC/MCStreamer.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class W65816TargetELFStreamer : public MCTargetStreamer {
|
||||
public:
|
||||
W65816TargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI)
|
||||
: MCTargetStreamer(S) {
|
||||
// No vendor attributes are emitted yet; this is intentionally empty.
|
||||
}
|
||||
};
|
||||
|
||||
MCTargetStreamer *
|
||||
createW65816ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) {
|
||||
const Triple &TT = STI.getTargetTriple();
|
||||
if (TT.isOSBinFormatELF())
|
||||
return new W65816TargetELFStreamer(S, STI);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace llvm
|
||||
41
src/llvm/lib/Target/W65816/MCTargetDesc/W65816FixupKinds.h
Normal file
41
src/llvm/lib/Target/W65816/MCTargetDesc/W65816FixupKinds.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//===-- W65816FixupKinds.h - W65816 Specific Fixup Entries ------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_MCTARGETDESC_W65816FIXUPKINDS_H
|
||||
#define LLVM_LIB_TARGET_W65816_MCTARGETDESC_W65816FIXUPKINDS_H
|
||||
|
||||
#include "llvm/MC/MCFixup.h"
|
||||
|
||||
#undef W65816
|
||||
|
||||
namespace llvm {
|
||||
namespace W65816 {
|
||||
|
||||
// This table must stay in sync with the MCFixupKindInfo table in
|
||||
// W65816AsmBackend.cpp. Only a bare minimum is defined for the skeleton.
|
||||
enum Fixups {
|
||||
// 8-bit absolute fixup (immediate byte).
|
||||
fixup_8 = FirstTargetFixupKind,
|
||||
// 16-bit absolute fixup (low two bytes of a 24-bit address).
|
||||
fixup_16,
|
||||
// 24-bit absolute fixup (long address).
|
||||
fixup_24,
|
||||
// 8-bit PC-relative fixup (short branch).
|
||||
fixup_8_pcrel,
|
||||
// 16-bit PC-relative fixup (long branch / BRL).
|
||||
fixup_16_pcrel,
|
||||
|
||||
// Marker
|
||||
LastTargetFixupKind,
|
||||
NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind
|
||||
};
|
||||
|
||||
} // end namespace W65816
|
||||
} // end namespace llvm
|
||||
|
||||
#endif
|
||||
118
src/llvm/lib/Target/W65816/MCTargetDesc/W65816InstPrinter.cpp
Normal file
118
src/llvm/lib/Target/W65816/MCTargetDesc/W65816InstPrinter.cpp
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
//===-- W65816InstPrinter.cpp - Convert W65816 MCInst to assembly syntax --===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This class prints a W65816 MCInst to a .s file. The skeleton does not yet
|
||||
// implement any real instructions so the printer mostly defers to the table-
|
||||
// generated routines and reports unimplemented cases with llvm_unreachable.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816InstPrinter.h"
|
||||
#include "llvm/MC/MCAsmInfo.h"
|
||||
#include "llvm/MC/MCExpr.h"
|
||||
#include "llvm/MC/MCInst.h"
|
||||
#include "llvm/MC/MCRegisterInfo.h"
|
||||
#include "llvm/MC/MCSymbol.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "asm-printer"
|
||||
|
||||
// Include the auto-generated portion of the assembly writer.
|
||||
#define PRINT_ALIAS_INSTR
|
||||
#include "W65816GenAsmWriter.inc"
|
||||
|
||||
void W65816InstPrinter::printRegName(raw_ostream &O, MCRegister Reg) {
|
||||
O << getRegisterName(Reg);
|
||||
}
|
||||
|
||||
void W65816InstPrinter::printInst(const MCInst *MI, uint64_t Address,
|
||||
StringRef Annot, const MCSubtargetInfo &STI,
|
||||
raw_ostream &O) {
|
||||
if (!printAliasInstr(MI, Address, O))
|
||||
printInstruction(MI, Address, O);
|
||||
printAnnotation(O, Annot);
|
||||
}
|
||||
|
||||
void W65816InstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
|
||||
raw_ostream &O) {
|
||||
const MCOperand &Op = MI->getOperand(OpNo);
|
||||
if (Op.isReg()) {
|
||||
O << getRegisterName(Op.getReg());
|
||||
return;
|
||||
}
|
||||
if (Op.isImm()) {
|
||||
// The '#' prefix lives in the AsmString (e.g. "lda\t#$imm") so we
|
||||
// only emit the numeric value here. 65816 immediates are at most
|
||||
// 16 bits; we print the low 16 to avoid sign-extended int64 noise
|
||||
// like `0xffffffffffffffff` for i16 -1.
|
||||
O << "0x";
|
||||
O.write_hex(Op.getImm() & 0xffff);
|
||||
return;
|
||||
}
|
||||
assert(Op.isExpr() && "unknown operand kind in printOperand");
|
||||
MAI.printExpr(O, *Op.getExpr());
|
||||
}
|
||||
|
||||
// Address operand helpers. We use LLVM's standard `0x` hex prefix rather
|
||||
// than the 65816-native `$` prefix so that our own AsmParser (which uses
|
||||
// the stock LLVM expression lexer) can round-trip our output. Teaching
|
||||
// the lexer about `$` is a future cleanup; until then the printed form
|
||||
// is compatible-with-itself rather than idiomatic WDC syntax.
|
||||
static void printAddrImm(const MCOperand &Op, unsigned width, raw_ostream &O,
|
||||
const MCAsmInfo &MAI) {
|
||||
if (Op.isImm()) {
|
||||
uint64_t mask = width >= 64 ? ~uint64_t(0) : ((uint64_t(1) << width) - 1);
|
||||
O << "0x";
|
||||
O.write_hex(Op.getImm() & mask);
|
||||
return;
|
||||
}
|
||||
assert(Op.isExpr() && "unknown address operand kind");
|
||||
MAI.printExpr(O, *Op.getExpr());
|
||||
}
|
||||
|
||||
void W65816InstPrinter::printAddrDP(const MCInst *MI, unsigned OpNo,
|
||||
raw_ostream &O) {
|
||||
printAddrImm(MI->getOperand(OpNo), 8, O, MAI);
|
||||
}
|
||||
|
||||
void W65816InstPrinter::printAddrAbs(const MCInst *MI, unsigned OpNo,
|
||||
raw_ostream &O) {
|
||||
printAddrImm(MI->getOperand(OpNo), 16, O, MAI);
|
||||
}
|
||||
|
||||
void W65816InstPrinter::printAddrLong(const MCInst *MI, unsigned OpNo,
|
||||
raw_ostream &O) {
|
||||
printAddrImm(MI->getOperand(OpNo), 24, O, MAI);
|
||||
}
|
||||
|
||||
void W65816InstPrinter::printPCRel8(const MCInst *MI, uint64_t Address,
|
||||
unsigned OpNo, raw_ostream &O) {
|
||||
const MCOperand &Op = MI->getOperand(OpNo);
|
||||
if (Op.isImm()) {
|
||||
O << Op.getImm();
|
||||
return;
|
||||
}
|
||||
assert(Op.isExpr() && "unknown pc-rel operand kind");
|
||||
MAI.printExpr(O, *Op.getExpr());
|
||||
}
|
||||
|
||||
void W65816InstPrinter::printPCRel16(const MCInst *MI, uint64_t Address,
|
||||
unsigned OpNo, raw_ostream &O) {
|
||||
printPCRel8(MI, Address, OpNo, O);
|
||||
}
|
||||
|
||||
void W65816InstPrinter::printFrameMem(const MCInst *MI, unsigned OpNo,
|
||||
raw_ostream &O) {
|
||||
// Frame-index loads/stores are expanded into LDA/STA d,S in
|
||||
// W65816RegisterInfo::eliminateFrameIndex before they ever reach
|
||||
// the printer. This callback exists only to satisfy tablegen.
|
||||
llvm_unreachable("W65816: frame-mem pseudo printed without expansion");
|
||||
}
|
||||
55
src/llvm/lib/Target/W65816/MCTargetDesc/W65816InstPrinter.h
Normal file
55
src/llvm/lib/Target/W65816/MCTargetDesc/W65816InstPrinter.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
//= W65816InstPrinter.h - Convert W65816 MCInst to assembly syntax -*- C++ -*-//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This class prints a W65816 MCInst to a .s file.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_MCTARGETDESC_W65816INSTPRINTER_H
|
||||
#define LLVM_LIB_TARGET_W65816_MCTARGETDESC_W65816INSTPRINTER_H
|
||||
|
||||
#include "llvm/MC/MCInstPrinter.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class W65816InstPrinter : public MCInstPrinter {
|
||||
public:
|
||||
W65816InstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII,
|
||||
const MCRegisterInfo &MRI)
|
||||
: MCInstPrinter(MAI, MII, MRI) {}
|
||||
|
||||
void printRegName(raw_ostream &O, MCRegister Reg) override;
|
||||
void printInst(const MCInst *MI, uint64_t Address, StringRef Annot,
|
||||
const MCSubtargetInfo &STI, raw_ostream &O) override;
|
||||
|
||||
// Autogenerated by tblgen.
|
||||
std::pair<const char *, uint64_t> getMnemonic(const MCInst &MI) const override;
|
||||
void printInstruction(const MCInst *MI, uint64_t Address, raw_ostream &O);
|
||||
bool printAliasInstr(const MCInst *MI, uint64_t Address, raw_ostream &O);
|
||||
void printCustomAliasOperand(const MCInst *MI, uint64_t Address,
|
||||
unsigned OpIdx, unsigned PrintMethodIdx,
|
||||
raw_ostream &O);
|
||||
static const char *getRegisterName(MCRegister Reg);
|
||||
|
||||
private:
|
||||
void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O);
|
||||
void printAddrDP(const MCInst *MI, unsigned OpNo, raw_ostream &O);
|
||||
void printAddrAbs(const MCInst *MI, unsigned OpNo, raw_ostream &O);
|
||||
void printAddrLong(const MCInst *MI, unsigned OpNo, raw_ostream &O);
|
||||
void printPCRel8(const MCInst *MI, uint64_t Address, unsigned OpNo,
|
||||
raw_ostream &O);
|
||||
void printPCRel16(const MCInst *MI, uint64_t Address, unsigned OpNo,
|
||||
raw_ostream &O);
|
||||
// Frame-index pseudos never reach the printer (they are expanded in
|
||||
// the AsmPrinter), but tablegen still requires the declaration.
|
||||
void printFrameMem(const MCInst *MI, unsigned OpNo, raw_ostream &O);
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
30
src/llvm/lib/Target/W65816/MCTargetDesc/W65816MCAsmInfo.cpp
Normal file
30
src/llvm/lib/Target/W65816/MCTargetDesc/W65816MCAsmInfo.cpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//===-- W65816MCAsmInfo.cpp - W65816 asm properties -----------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains the implementation of the W65816MCAsmInfo class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816MCAsmInfo.h"
|
||||
using namespace llvm;
|
||||
|
||||
void W65816MCAsmInfo::anchor() {}
|
||||
|
||||
W65816MCAsmInfo::W65816MCAsmInfo(const Triple &TT) {
|
||||
// The 65816 address space is 24 bits but LLVM's ELF writer emits 32-bit
|
||||
// pointer-sized DWARF entries; store pointers as 32 bits for DWARF.
|
||||
CodePointerSize = 4;
|
||||
CalleeSaveStackSlotSize = 2;
|
||||
|
||||
CommentString = ";";
|
||||
|
||||
AlignmentIsInBytes = false;
|
||||
UsesELFSectionDirectiveForBSS = true;
|
||||
|
||||
SupportsDebugInformation = true;
|
||||
}
|
||||
30
src/llvm/lib/Target/W65816/MCTargetDesc/W65816MCAsmInfo.h
Normal file
30
src/llvm/lib/Target/W65816/MCTargetDesc/W65816MCAsmInfo.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//===-- W65816MCAsmInfo.h - W65816 asm properties --------------*- C++ -*--===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains the declaration of the W65816MCAsmInfo class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_MCTARGETDESC_W65816MCASMINFO_H
|
||||
#define LLVM_LIB_TARGET_W65816_MCTARGETDESC_W65816MCASMINFO_H
|
||||
|
||||
#include "llvm/MC/MCAsmInfoELF.h"
|
||||
|
||||
namespace llvm {
|
||||
class Triple;
|
||||
|
||||
class W65816MCAsmInfo : public MCAsmInfoELF {
|
||||
void anchor() override;
|
||||
|
||||
public:
|
||||
explicit W65816MCAsmInfo(const Triple &TT);
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
200
src/llvm/lib/Target/W65816/MCTargetDesc/W65816MCCodeEmitter.cpp
Normal file
200
src/llvm/lib/Target/W65816/MCTargetDesc/W65816MCCodeEmitter.cpp
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
//===-- W65816MCCodeEmitter.cpp - Convert W65816 code to machine code -----===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 65816 instructions are 1-4 bytes: an 8-bit opcode followed by 0-3 bytes of
|
||||
// operand data (immediate / direct-page / absolute / long / relative). The
|
||||
// tablegen-generated `getBinaryCodeForInstr` packs the full instruction into
|
||||
// the low Size*8 bits of a uint64_t in little-endian order; we just copy
|
||||
// that many bytes out.
|
||||
//
|
||||
// Each operand class carries an `EncoderMethod` naming one of the
|
||||
// `encode*` helpers below. Those helpers return the immediate value for
|
||||
// literal operands and emit a relocation fixup for symbolic operands,
|
||||
// tracking the per-instruction byte offset so block-move and other
|
||||
// multi-operand instructions get correctly-positioned fixups.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MCTargetDesc/W65816FixupKinds.h"
|
||||
#include "MCTargetDesc/W65816MCTargetDesc.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/MC/MCCodeEmitter.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCExpr.h"
|
||||
#include "llvm/MC/MCFixup.h"
|
||||
#include "llvm/MC/MCInst.h"
|
||||
#include "llvm/MC/MCInstrInfo.h"
|
||||
#include "llvm/MC/MCRegisterInfo.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/Support/EndianStream.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
#define DEBUG_TYPE "mccodeemitter"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class W65816MCCodeEmitter : public MCCodeEmitter {
|
||||
MCContext &Ctx;
|
||||
const MCInstrInfo &MCII;
|
||||
|
||||
// Byte position within the current instruction for the next operand's
|
||||
// fixup. Reset to 1 at the start of each encodeInstruction call (byte 0
|
||||
// is always the opcode).
|
||||
mutable unsigned OperandOffset = 1;
|
||||
|
||||
// Tablegen-provided.
|
||||
uint64_t getBinaryCodeForInstr(const MCInst &MI,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const;
|
||||
|
||||
// Fallback operand encoder used when a custom EncoderMethod is not set.
|
||||
unsigned getMachineOpValue(const MCInst &MI, const MCOperand &MO,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const;
|
||||
|
||||
// Per-operand encoders. Each pulls one MCOperand from the instruction,
|
||||
// returns its literal bits for immediate operands, or emits a fixup of
|
||||
// the appropriate kind (and returns 0) for expression operands.
|
||||
unsigned encodeImm8(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const;
|
||||
unsigned encodeImm16(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const;
|
||||
unsigned encodeAddr8(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const;
|
||||
unsigned encodeAddr16(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const;
|
||||
unsigned encodeAddr24(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const;
|
||||
unsigned encodePCRel8(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const;
|
||||
unsigned encodePCRel16(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const;
|
||||
|
||||
// Shared implementation for every encode* helper. `width` is the number
|
||||
// of bytes of the operand as it appears in the instruction stream; it
|
||||
// both advances the per-instruction offset and selects the fixup kind.
|
||||
unsigned encodeOperand(const MCInst &MI, unsigned OpIdx, unsigned width,
|
||||
W65816::Fixups fixupKind,
|
||||
SmallVectorImpl<MCFixup> &Fixups) const;
|
||||
|
||||
public:
|
||||
W65816MCCodeEmitter(MCContext &Ctx, const MCInstrInfo &MCII)
|
||||
: Ctx(Ctx), MCII(MCII) {}
|
||||
|
||||
void encodeInstruction(const MCInst &MI, SmallVectorImpl<char> &CB,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const override;
|
||||
};
|
||||
|
||||
void W65816MCCodeEmitter::encodeInstruction(const MCInst &MI,
|
||||
SmallVectorImpl<char> &CB,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const {
|
||||
const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
|
||||
unsigned Size = Desc.getSize();
|
||||
|
||||
// First operand byte lives right after the opcode.
|
||||
OperandOffset = 1;
|
||||
uint64_t Bits = getBinaryCodeForInstr(MI, Fixups, STI);
|
||||
|
||||
for (unsigned i = 0; i < Size; ++i) {
|
||||
CB.push_back(static_cast<char>((Bits >> (8 * i)) & 0xff));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned
|
||||
W65816MCCodeEmitter::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());
|
||||
}
|
||||
// All symbolic operands should be routed through the per-operand-class
|
||||
// EncoderMethods below. Reaching this path means an operand was defined
|
||||
// without a custom encoder.
|
||||
llvm_unreachable("W65816: symbolic operand lacks EncoderMethod");
|
||||
}
|
||||
|
||||
unsigned W65816MCCodeEmitter::encodeOperand(const MCInst &MI, unsigned OpIdx,
|
||||
unsigned width,
|
||||
W65816::Fixups fixupKind,
|
||||
SmallVectorImpl<MCFixup> &Fixups) const {
|
||||
const MCOperand &MO = MI.getOperand(OpIdx);
|
||||
unsigned offsetForThisOperand = OperandOffset;
|
||||
OperandOffset += width;
|
||||
|
||||
if (MO.isImm()) {
|
||||
return static_cast<unsigned>(MO.getImm());
|
||||
}
|
||||
|
||||
assert(MO.isExpr() && "unexpected operand kind for encoded operand");
|
||||
Fixups.push_back(MCFixup::create(offsetForThisOperand, MO.getExpr(),
|
||||
MCFixupKind(fixupKind)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned W65816MCCodeEmitter::encodeImm8(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const {
|
||||
return encodeOperand(MI, OpIdx, 1, W65816::fixup_8, Fixups);
|
||||
}
|
||||
|
||||
unsigned W65816MCCodeEmitter::encodeImm16(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const {
|
||||
return encodeOperand(MI, OpIdx, 2, W65816::fixup_16, Fixups);
|
||||
}
|
||||
|
||||
unsigned W65816MCCodeEmitter::encodeAddr8(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const {
|
||||
return encodeOperand(MI, OpIdx, 1, W65816::fixup_8, Fixups);
|
||||
}
|
||||
|
||||
unsigned W65816MCCodeEmitter::encodeAddr16(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const {
|
||||
return encodeOperand(MI, OpIdx, 2, W65816::fixup_16, Fixups);
|
||||
}
|
||||
|
||||
unsigned W65816MCCodeEmitter::encodeAddr24(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const {
|
||||
return encodeOperand(MI, OpIdx, 3, W65816::fixup_24, Fixups);
|
||||
}
|
||||
|
||||
unsigned W65816MCCodeEmitter::encodePCRel8(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const {
|
||||
return encodeOperand(MI, OpIdx, 1, W65816::fixup_8_pcrel, Fixups);
|
||||
}
|
||||
|
||||
unsigned W65816MCCodeEmitter::encodePCRel16(const MCInst &MI, unsigned OpIdx,
|
||||
SmallVectorImpl<MCFixup> &Fixups,
|
||||
const MCSubtargetInfo &STI) const {
|
||||
return encodeOperand(MI, OpIdx, 2, W65816::fixup_16_pcrel, Fixups);
|
||||
}
|
||||
|
||||
MCCodeEmitter *createW65816MCCodeEmitter(const MCInstrInfo &MCII,
|
||||
MCContext &Ctx) {
|
||||
return new W65816MCCodeEmitter(Ctx, MCII);
|
||||
}
|
||||
|
||||
#include "W65816GenMCCodeEmitter.inc"
|
||||
|
||||
} // namespace llvm
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
//===-- W65816MCTargetDesc.cpp - W65816 Target Descriptions ---------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides W65816 specific target descriptions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816MCTargetDesc.h"
|
||||
#include "W65816InstPrinter.h"
|
||||
#include "W65816MCAsmInfo.h"
|
||||
#include "TargetInfo/W65816TargetInfo.h"
|
||||
#include "llvm/MC/MCInstrInfo.h"
|
||||
#include "llvm/MC/MCRegisterInfo.h"
|
||||
#include "llvm/MC/MCSubtargetInfo.h"
|
||||
#include "llvm/MC/TargetRegistry.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define GET_INSTRINFO_MC_DESC
|
||||
#define ENABLE_INSTR_PREDICATE_VERIFIER
|
||||
#include "W65816GenInstrInfo.inc"
|
||||
|
||||
#define GET_SUBTARGETINFO_MC_DESC
|
||||
#include "W65816GenSubtargetInfo.inc"
|
||||
|
||||
#define GET_REGINFO_MC_DESC
|
||||
#include "W65816GenRegisterInfo.inc"
|
||||
|
||||
static MCInstrInfo *createW65816MCInstrInfo() {
|
||||
MCInstrInfo *X = new MCInstrInfo();
|
||||
InitW65816MCInstrInfo(X);
|
||||
return X;
|
||||
}
|
||||
|
||||
static MCRegisterInfo *createW65816MCRegisterInfo(const Triple &TT) {
|
||||
MCRegisterInfo *X = new MCRegisterInfo();
|
||||
InitW65816MCRegisterInfo(X, W65816::PC);
|
||||
return X;
|
||||
}
|
||||
|
||||
static MCAsmInfo *createW65816MCAsmInfo(const MCRegisterInfo &MRI,
|
||||
const Triple &TT,
|
||||
const MCTargetOptions &Options) {
|
||||
// Initial DWARF CFI frame state is deliberately omitted for the skeleton;
|
||||
// it will be added once frame lowering is fully described.
|
||||
return new W65816MCAsmInfo(TT);
|
||||
}
|
||||
|
||||
static MCSubtargetInfo *
|
||||
createW65816MCSubtargetInfo(const Triple &TT, StringRef CPU, StringRef FS) {
|
||||
return createW65816MCSubtargetInfoImpl(TT, CPU, /*TuneCPU=*/CPU, FS);
|
||||
}
|
||||
|
||||
static MCInstPrinter *createW65816MCInstPrinter(const Triple &T,
|
||||
unsigned SyntaxVariant,
|
||||
const MCAsmInfo &MAI,
|
||||
const MCInstrInfo &MII,
|
||||
const MCRegisterInfo &MRI) {
|
||||
if (SyntaxVariant == 0)
|
||||
return new W65816InstPrinter(MAI, MII, MRI);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
|
||||
LLVMInitializeW65816TargetMC() {
|
||||
Target &T = getTheW65816Target();
|
||||
|
||||
TargetRegistry::RegisterMCAsmInfo(T, createW65816MCAsmInfo);
|
||||
TargetRegistry::RegisterMCInstrInfo(T, createW65816MCInstrInfo);
|
||||
TargetRegistry::RegisterMCRegInfo(T, createW65816MCRegisterInfo);
|
||||
TargetRegistry::RegisterMCSubtargetInfo(T, createW65816MCSubtargetInfo);
|
||||
TargetRegistry::RegisterMCInstPrinter(T, createW65816MCInstPrinter);
|
||||
TargetRegistry::RegisterMCCodeEmitter(T, createW65816MCCodeEmitter);
|
||||
TargetRegistry::RegisterMCAsmBackend(T, createW65816MCAsmBackend);
|
||||
TargetRegistry::RegisterObjectTargetStreamer(
|
||||
T, createW65816ObjectTargetStreamer);
|
||||
}
|
||||
61
src/llvm/lib/Target/W65816/MCTargetDesc/W65816MCTargetDesc.h
Normal file
61
src/llvm/lib/Target/W65816/MCTargetDesc/W65816MCTargetDesc.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//===-- W65816MCTargetDesc.h - W65816 Target Descriptions -------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides W65816 specific target descriptions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_MCTARGETDESC_W65816MCTARGETDESC_H
|
||||
#define LLVM_LIB_TARGET_W65816_MCTARGETDESC_W65816MCTARGETDESC_H
|
||||
|
||||
#include "llvm/Support/DataTypes.h"
|
||||
#include <memory>
|
||||
|
||||
namespace llvm {
|
||||
class Target;
|
||||
class MCAsmBackend;
|
||||
class MCCodeEmitter;
|
||||
class MCInstrInfo;
|
||||
class MCSubtargetInfo;
|
||||
class MCRegisterInfo;
|
||||
class MCContext;
|
||||
class MCTargetOptions;
|
||||
class MCObjectTargetWriter;
|
||||
class MCStreamer;
|
||||
class MCTargetStreamer;
|
||||
|
||||
/// Creates a machine code emitter for W65816.
|
||||
MCCodeEmitter *createW65816MCCodeEmitter(const MCInstrInfo &MCII,
|
||||
MCContext &Ctx);
|
||||
|
||||
MCAsmBackend *createW65816MCAsmBackend(const Target &T,
|
||||
const MCSubtargetInfo &STI,
|
||||
const MCRegisterInfo &MRI,
|
||||
const MCTargetOptions &Options);
|
||||
|
||||
MCTargetStreamer *
|
||||
createW65816ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI);
|
||||
|
||||
std::unique_ptr<MCObjectTargetWriter>
|
||||
createW65816ELFObjectWriter(uint8_t OSABI);
|
||||
|
||||
} // End llvm namespace
|
||||
|
||||
// Defines symbolic names for W65816 registers.
|
||||
#define GET_REGINFO_ENUM
|
||||
#include "W65816GenRegisterInfo.inc"
|
||||
|
||||
// Defines symbolic names for the W65816 instructions.
|
||||
#define GET_INSTRINFO_ENUM
|
||||
#define GET_INSTRINFO_MC_HELPER_DECLS
|
||||
#include "W65816GenInstrInfo.inc"
|
||||
|
||||
#define GET_SUBTARGETINFO_ENUM
|
||||
#include "W65816GenSubtargetInfo.inc"
|
||||
|
||||
#endif
|
||||
10
src/llvm/lib/Target/W65816/TargetInfo/CMakeLists.txt
Normal file
10
src/llvm/lib/Target/W65816/TargetInfo/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
add_llvm_component_library(LLVMW65816Info
|
||||
W65816TargetInfo.cpp
|
||||
|
||||
LINK_COMPONENTS
|
||||
MC
|
||||
Support
|
||||
|
||||
ADD_TO_COMPONENT
|
||||
W65816
|
||||
)
|
||||
23
src/llvm/lib/Target/W65816/TargetInfo/W65816TargetInfo.cpp
Normal file
23
src/llvm/lib/Target/W65816/TargetInfo/W65816TargetInfo.cpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
//===-- W65816TargetInfo.cpp - W65816 Target Implementation ---------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "TargetInfo/W65816TargetInfo.h"
|
||||
#include "llvm/MC/TargetRegistry.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
using namespace llvm;
|
||||
|
||||
Target &llvm::getTheW65816Target() {
|
||||
static Target TheW65816Target;
|
||||
return TheW65816Target;
|
||||
}
|
||||
|
||||
extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
|
||||
LLVMInitializeW65816TargetInfo() {
|
||||
RegisterTarget<Triple::w65816> X(getTheW65816Target(), "w65816",
|
||||
"WDC 65816 [experimental]", "W65816");
|
||||
}
|
||||
20
src/llvm/lib/Target/W65816/TargetInfo/W65816TargetInfo.h
Normal file
20
src/llvm/lib/Target/W65816/TargetInfo/W65816TargetInfo.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//===-- W65816TargetInfo.h - W65816 Target Implementation -------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_TARGETINFO_W65816TARGETINFO_H
|
||||
#define LLVM_LIB_TARGET_W65816_TARGETINFO_W65816TARGETINFO_H
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class Target;
|
||||
|
||||
Target &getTheW65816Target();
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_LIB_TARGET_W65816_TARGETINFO_W65816TARGETINFO_H
|
||||
50
src/llvm/lib/Target/W65816/W65816.h
Normal file
50
src/llvm/lib/Target/W65816/W65816.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
//==-- W65816.h - Top-level interface for W65816 representation --*- C++ -*-==//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains the entry points for global functions defined in the
|
||||
// LLVM W65816 backend.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_W65816_H
|
||||
#define LLVM_LIB_TARGET_W65816_W65816_H
|
||||
|
||||
#include "MCTargetDesc/W65816MCTargetDesc.h"
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
|
||||
namespace W65816CC {
|
||||
// 65816 branch condition codes. Encoded as i8 immediate operands in
|
||||
// the BR_CC SDNode and tablegen patterns.
|
||||
enum CondCode {
|
||||
COND_EQ = 0, // BEQ
|
||||
COND_NE = 1, // BNE
|
||||
COND_HS = 2, // BCS (unsigned >=)
|
||||
COND_LO = 3, // BCC (unsigned <)
|
||||
COND_MI = 4, // BMI (negative)
|
||||
COND_PL = 5, // BPL (non-negative)
|
||||
COND_VS = 6, // BVS (overflow)
|
||||
COND_VC = 7, // BVC (no overflow)
|
||||
COND_INVALID = -1
|
||||
};
|
||||
} // namespace W65816CC
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class FunctionPass;
|
||||
class W65816TargetMachine;
|
||||
class PassRegistry;
|
||||
|
||||
FunctionPass *createW65816ISelDag(W65816TargetMachine &TM,
|
||||
CodeGenOptLevel OptLevel);
|
||||
|
||||
void initializeW65816AsmPrinterPass(PassRegistry &);
|
||||
void initializeW65816DAGToDAGISelLegacyPass(PassRegistry &);
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
76
src/llvm/lib/Target/W65816/W65816.td
Normal file
76
src/llvm/lib/Target/W65816/W65816.td
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
//===-- W65816.td - Describe the W65816 Target Machine -----*- tablegen -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This is the top level entry point for the W65816 target.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Target-independent interfaces
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
include "llvm/Target/Target.td"
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Subtarget Features.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// No subtarget features are defined for the skeleton. Later work will add
|
||||
// features for emulation/native mode, DP location, etc.
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// W65816 supported processors.
|
||||
//===----------------------------------------------------------------------===//
|
||||
class Proc<string Name, list<SubtargetFeature> Features>
|
||||
: Processor<Name, NoItineraries, Features>;
|
||||
|
||||
def : Proc<"generic", []>;
|
||||
def : Proc<"w65816", []>;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Register File Description
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
include "W65816RegisterInfo.td"
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Calling Convention Description
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
include "W65816CallingConv.td"
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Instruction Descriptions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
include "W65816InstrInfo.td"
|
||||
|
||||
defm : RemapAllTargetPseudoPointerOperands<PtrRegs>;
|
||||
|
||||
def W65816InstrInfo : InstrInfo;
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
// Assembly Printers
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
def W65816AsmWriter : AsmWriter {
|
||||
string AsmWriterClassName = "InstPrinter";
|
||||
}
|
||||
|
||||
//===---------------------------------------------------------------------===//
|
||||
// Assembly Parsers
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
def W65816AsmParser : AsmParser;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Target Declaration
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def W65816 : Target {
|
||||
let InstructionSet = W65816InstrInfo;
|
||||
let AssemblyParsers = [W65816AsmParser];
|
||||
}
|
||||
323
src/llvm/lib/Target/W65816/W65816AsmPrinter.cpp
Normal file
323
src/llvm/lib/Target/W65816/W65816AsmPrinter.cpp
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
//===-- W65816AsmPrinter.cpp - W65816 LLVM assembly writer ----------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Skeleton assembly printer. The MCInst lowering path is wired up but no
|
||||
// target-specific operand formatting is implemented yet.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MCTargetDesc/W65816InstPrinter.h"
|
||||
#include "W65816MCInstLower.h"
|
||||
#include "W65816TargetMachine.h"
|
||||
#include "TargetInfo/W65816TargetInfo.h"
|
||||
#include "llvm/CodeGen/AsmPrinter.h"
|
||||
#include "llvm/CodeGen/MachineInstr.h"
|
||||
#include "llvm/MC/MCInst.h"
|
||||
#include "llvm/MC/MCStreamer.h"
|
||||
#include "llvm/MC/TargetRegistry.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "asm-printer"
|
||||
|
||||
namespace {
|
||||
|
||||
class W65816AsmPrinter : public AsmPrinter {
|
||||
public:
|
||||
W65816AsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
|
||||
: AsmPrinter(TM, std::move(Streamer), ID) {}
|
||||
|
||||
StringRef getPassName() const override { return "W65816 Assembly Printer"; }
|
||||
|
||||
void emitInstruction(const MachineInstr *MI) override;
|
||||
|
||||
static char ID;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
// Convert a single MachineOperand to an MCOperand using the standard
|
||||
// rules (register / immediate / global / external / etc.). Used by
|
||||
// pseudo expansion to lift symbolic operands; mirrors the per-operand
|
||||
// path in W65816MCInstLower::Lower.
|
||||
static MCOperand lowerOperand(const MachineOperand &MO,
|
||||
W65816MCInstLower &Lower) {
|
||||
switch (MO.getType()) {
|
||||
case MachineOperand::MO_Register:
|
||||
return MCOperand::createReg(MO.getReg());
|
||||
case MachineOperand::MO_Immediate:
|
||||
return MCOperand::createImm(MO.getImm());
|
||||
case MachineOperand::MO_GlobalAddress:
|
||||
return Lower.LowerSymbolOperand(MO, Lower.GetGlobalAddressSymbol(MO));
|
||||
case MachineOperand::MO_ExternalSymbol:
|
||||
return Lower.LowerSymbolOperand(MO, Lower.GetExternalSymbolSymbol(MO));
|
||||
case MachineOperand::MO_BlockAddress:
|
||||
return Lower.LowerSymbolOperand(MO, Lower.GetBlockAddressSymbol(MO));
|
||||
case MachineOperand::MO_JumpTableIndex:
|
||||
return Lower.LowerSymbolOperand(MO, Lower.GetJumpTableSymbol(MO));
|
||||
case MachineOperand::MO_ConstantPoolIndex:
|
||||
return Lower.LowerSymbolOperand(MO, Lower.GetConstantPoolIndexSymbol(MO));
|
||||
default:
|
||||
llvm_unreachable("W65816: unsupported operand type in pseudo lower");
|
||||
}
|
||||
}
|
||||
|
||||
void W65816AsmPrinter::emitInstruction(const MachineInstr *MI) {
|
||||
W65816_MC::verifyInstructionPredicates(MI->getOpcode(),
|
||||
getSubtargetInfo().getFeatureBits());
|
||||
|
||||
W65816MCInstLower MCInstLowering(OutContext, *this);
|
||||
|
||||
// Expand codegen pseudos into their MC-layer realisations. Keep this
|
||||
// switch in sync with the pseudo defs at the bottom of
|
||||
// W65816InstrInfo.td. All single-output pseudos here have operand
|
||||
// layout (outs $dst, ins $value); we drop $dst (it lives implicitly in
|
||||
// A) and lift $value into the MC instruction's single source operand.
|
||||
switch (MI->getOpcode()) {
|
||||
default:
|
||||
break;
|
||||
case W65816::LDAi16imm: {
|
||||
MCInst Lda;
|
||||
Lda.setOpcode(W65816::LDA_Imm16);
|
||||
Lda.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
|
||||
EmitToStreamer(*OutStreamer, Lda);
|
||||
return;
|
||||
}
|
||||
case W65816::LDAi8imm: {
|
||||
MCInst Lda;
|
||||
Lda.setOpcode(W65816::LDA_Imm8);
|
||||
int64_t Val = MI->getOperand(1).getImm() & 0xFF;
|
||||
Lda.addOperand(MCOperand::createImm(Val));
|
||||
EmitToStreamer(*OutStreamer, Lda);
|
||||
return;
|
||||
}
|
||||
case W65816::LDAabs: {
|
||||
MCInst Lda;
|
||||
Lda.setOpcode(W65816::LDA_Abs);
|
||||
Lda.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
|
||||
EmitToStreamer(*OutStreamer, Lda);
|
||||
return;
|
||||
}
|
||||
case W65816::STAabs: {
|
||||
// STAabs is (outs), (ins Acc16:$src, addr:$addr). The MC STA_Abs
|
||||
// takes only $addr; $src lives in the implicit A.
|
||||
MCInst Sta;
|
||||
Sta.setOpcode(W65816::STA_Abs);
|
||||
Sta.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
|
||||
EmitToStreamer(*OutStreamer, Sta);
|
||||
return;
|
||||
}
|
||||
case W65816::ADCi16imm:
|
||||
case W65816::SBCi16imm: {
|
||||
bool IsSub = MI->getOpcode() == W65816::SBCi16imm;
|
||||
MCInst Carry;
|
||||
Carry.setOpcode(IsSub ? W65816::SEC : W65816::CLC);
|
||||
EmitToStreamer(*OutStreamer, Carry);
|
||||
|
||||
MCInst Op;
|
||||
Op.setOpcode(IsSub ? W65816::SBC_Imm16 : W65816::ADC_Imm16);
|
||||
Op.addOperand(lowerOperand(MI->getOperand(2), MCInstLowering));
|
||||
EmitToStreamer(*OutStreamer, Op);
|
||||
return;
|
||||
}
|
||||
case W65816::ADCi8imm:
|
||||
case W65816::SBCi8imm: {
|
||||
bool IsSub = MI->getOpcode() == W65816::SBCi8imm;
|
||||
MCInst Carry;
|
||||
Carry.setOpcode(IsSub ? W65816::SEC : W65816::CLC);
|
||||
EmitToStreamer(*OutStreamer, Carry);
|
||||
MCInst Op;
|
||||
Op.setOpcode(IsSub ? W65816::SBC_Imm8 : W65816::ADC_Imm8);
|
||||
int64_t Val = MI->getOperand(2).getImm() & 0xFF;
|
||||
Op.addOperand(MCOperand::createImm(Val));
|
||||
EmitToStreamer(*OutStreamer, Op);
|
||||
return;
|
||||
}
|
||||
case W65816::ANDi8imm:
|
||||
case W65816::ORAi8imm:
|
||||
case W65816::EORi8imm: {
|
||||
MCInst Op;
|
||||
unsigned mc = 0;
|
||||
switch (MI->getOpcode()) {
|
||||
case W65816::ANDi8imm: mc = W65816::AND_Imm8; break;
|
||||
case W65816::ORAi8imm: mc = W65816::ORA_Imm8; break;
|
||||
case W65816::EORi8imm: mc = W65816::EOR_Imm8; break;
|
||||
}
|
||||
Op.setOpcode(mc);
|
||||
// Mask to 8 bits so the printer doesn't show the sign-extended
|
||||
// i8 value as a wider hex literal (e.g. -16 → 0xFFF0); the
|
||||
// encoder only takes the low byte anyway.
|
||||
int64_t Val = MI->getOperand(2).getImm() & 0xFF;
|
||||
Op.addOperand(MCOperand::createImm(Val));
|
||||
EmitToStreamer(*OutStreamer, Op);
|
||||
return;
|
||||
}
|
||||
case W65816::LDA8abs: {
|
||||
MCInst Lda;
|
||||
Lda.setOpcode(W65816::LDA_Abs);
|
||||
Lda.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
|
||||
EmitToStreamer(*OutStreamer, Lda);
|
||||
return;
|
||||
}
|
||||
case W65816::STA8abs: {
|
||||
MCInst Sta;
|
||||
Sta.setOpcode(W65816::STA_Abs);
|
||||
Sta.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
|
||||
EmitToStreamer(*OutStreamer, Sta);
|
||||
return;
|
||||
}
|
||||
case W65816::ADCabs:
|
||||
case W65816::SBCabs: {
|
||||
bool IsSub = MI->getOpcode() == W65816::SBCabs;
|
||||
MCInst Carry;
|
||||
Carry.setOpcode(IsSub ? W65816::SEC : W65816::CLC);
|
||||
EmitToStreamer(*OutStreamer, Carry);
|
||||
|
||||
MCInst Op;
|
||||
Op.setOpcode(IsSub ? W65816::SBC_Abs : W65816::ADC_Abs);
|
||||
Op.addOperand(lowerOperand(MI->getOperand(2), MCInstLowering));
|
||||
EmitToStreamer(*OutStreamer, Op);
|
||||
return;
|
||||
}
|
||||
case W65816::CMPi16imm: {
|
||||
// CMPi16imm has (outs), (ins Acc16:$lhs, i16imm:$rhs); MC needs only
|
||||
// the immediate.
|
||||
MCInst Cmp;
|
||||
Cmp.setOpcode(W65816::CMP_Imm16);
|
||||
Cmp.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
|
||||
EmitToStreamer(*OutStreamer, Cmp);
|
||||
return;
|
||||
}
|
||||
case W65816::CMPi8imm: {
|
||||
MCInst Cmp;
|
||||
Cmp.setOpcode(W65816::CMP_Imm8);
|
||||
int64_t Val = MI->getOperand(1).getImm() & 0xFF;
|
||||
Cmp.addOperand(MCOperand::createImm(Val));
|
||||
EmitToStreamer(*OutStreamer, Cmp);
|
||||
return;
|
||||
}
|
||||
case W65816::CMPabs: {
|
||||
MCInst Cmp;
|
||||
Cmp.setOpcode(W65816::CMP_Abs);
|
||||
Cmp.addOperand(lowerOperand(MI->getOperand(1), MCInstLowering));
|
||||
EmitToStreamer(*OutStreamer, Cmp);
|
||||
return;
|
||||
}
|
||||
// Bitwise immediate / memory pseudos: simple opcode swap, no carry
|
||||
// prefix. Operand 0 = $dst (output, tied), 1 = $src (use), 2 = imm/addr.
|
||||
case W65816::ANDi16imm:
|
||||
case W65816::ORAi16imm:
|
||||
case W65816::EORi16imm: {
|
||||
MCInst Op;
|
||||
unsigned mc = 0;
|
||||
switch (MI->getOpcode()) {
|
||||
case W65816::ANDi16imm: mc = W65816::AND_Imm16; break;
|
||||
case W65816::ORAi16imm: mc = W65816::ORA_Imm16; break;
|
||||
case W65816::EORi16imm: mc = W65816::EOR_Imm16; break;
|
||||
}
|
||||
Op.setOpcode(mc);
|
||||
Op.addOperand(lowerOperand(MI->getOperand(2), MCInstLowering));
|
||||
EmitToStreamer(*OutStreamer, Op);
|
||||
return;
|
||||
}
|
||||
case W65816::ANDabs:
|
||||
case W65816::ORAabs:
|
||||
case W65816::EORabs: {
|
||||
MCInst Op;
|
||||
unsigned mc = 0;
|
||||
switch (MI->getOpcode()) {
|
||||
case W65816::ANDabs: mc = W65816::AND_Abs; break;
|
||||
case W65816::ORAabs: mc = W65816::ORA_Abs; break;
|
||||
case W65816::EORabs: mc = W65816::EOR_Abs; break;
|
||||
}
|
||||
Op.setOpcode(mc);
|
||||
Op.addOperand(lowerOperand(MI->getOperand(2), MCInstLowering));
|
||||
EmitToStreamer(*OutStreamer, Op);
|
||||
return;
|
||||
}
|
||||
case W65816::JSLpseudo: {
|
||||
MCInst Jsl;
|
||||
Jsl.setOpcode(W65816::JSL_Long);
|
||||
Jsl.addOperand(lowerOperand(MI->getOperand(0), MCInstLowering));
|
||||
EmitToStreamer(*OutStreamer, Jsl);
|
||||
return;
|
||||
}
|
||||
case W65816::ASLA16: {
|
||||
MCInst Asl;
|
||||
Asl.setOpcode(W65816::ASL_A);
|
||||
EmitToStreamer(*OutStreamer, Asl);
|
||||
return;
|
||||
}
|
||||
case W65816::LSRA16:
|
||||
case W65816::LSRA8: {
|
||||
MCInst Lsr;
|
||||
Lsr.setOpcode(W65816::LSR_A);
|
||||
EmitToStreamer(*OutStreamer, Lsr);
|
||||
return;
|
||||
}
|
||||
case W65816::ASLA8: {
|
||||
MCInst Asl;
|
||||
Asl.setOpcode(W65816::ASL_A);
|
||||
EmitToStreamer(*OutStreamer, Asl);
|
||||
return;
|
||||
}
|
||||
case W65816::ASRA16: {
|
||||
// PHA ; ASL A (sets carry from sign bit) ; PLA ; ROR A
|
||||
MCInst pha; pha.setOpcode(W65816::PHA); EmitToStreamer(*OutStreamer, pha);
|
||||
MCInst asl; asl.setOpcode(W65816::ASL_A); EmitToStreamer(*OutStreamer, asl);
|
||||
MCInst pla; pla.setOpcode(W65816::PLA); EmitToStreamer(*OutStreamer, pla);
|
||||
MCInst ror; ror.setOpcode(W65816::ROR_A); EmitToStreamer(*OutStreamer, ror);
|
||||
return;
|
||||
}
|
||||
case W65816::INA_PSEUDO: {
|
||||
MCInst In;
|
||||
In.setOpcode(W65816::INA);
|
||||
EmitToStreamer(*OutStreamer, In);
|
||||
return;
|
||||
}
|
||||
case W65816::DEA_PSEUDO:
|
||||
case W65816::DEA_PSEUDO8: {
|
||||
MCInst De;
|
||||
De.setOpcode(W65816::DEA);
|
||||
EmitToStreamer(*OutStreamer, De);
|
||||
return;
|
||||
}
|
||||
case W65816::INA_PSEUDO8: {
|
||||
MCInst In;
|
||||
In.setOpcode(W65816::INA);
|
||||
EmitToStreamer(*OutStreamer, In);
|
||||
return;
|
||||
}
|
||||
case W65816::NEGA16: {
|
||||
// EOR #$FFFF; INC A.
|
||||
MCInst Eor;
|
||||
Eor.setOpcode(W65816::EOR_Imm16);
|
||||
Eor.addOperand(MCOperand::createImm(0xFFFF));
|
||||
EmitToStreamer(*OutStreamer, Eor);
|
||||
MCInst Inc;
|
||||
Inc.setOpcode(W65816::INA);
|
||||
EmitToStreamer(*OutStreamer, Inc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MCInst TmpInst;
|
||||
MCInstLowering.Lower(MI, TmpInst);
|
||||
EmitToStreamer(*OutStreamer, TmpInst);
|
||||
}
|
||||
|
||||
char W65816AsmPrinter::ID = 0;
|
||||
|
||||
INITIALIZE_PASS(W65816AsmPrinter, "w65816-asm-printer",
|
||||
"W65816 Assembly Printer", false, false)
|
||||
|
||||
extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
|
||||
LLVMInitializeW65816AsmPrinter() {
|
||||
RegisterAsmPrinter<W65816AsmPrinter> X(getTheW65816Target());
|
||||
}
|
||||
37
src/llvm/lib/Target/W65816/W65816CallingConv.td
Normal file
37
src/llvm/lib/Target/W65816/W65816CallingConv.td
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
//==- W65816CallingConv.td - Calling Conventions for W65816 -*- tablegen -*-==//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// This describes the calling conventions for the W65816 architecture.
|
||||
//
|
||||
// The 65816 standard C calling convention passes parameters on the stack and
|
||||
// returns values in the accumulator. Tool-call and interrupt ABIs will be
|
||||
// added in a later change; only the skeleton is defined here.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// W65816 Return Value Calling Convention
|
||||
//===----------------------------------------------------------------------===//
|
||||
def RetCC_W65816 : CallingConv<[
|
||||
// i8 values are returned in the 8-bit accumulator.
|
||||
CCIfType<[i8], CCAssignToReg<[A]>>,
|
||||
// i16 values are returned in the 16-bit accumulator (same physical reg).
|
||||
CCIfType<[i16], CCAssignToReg<[A]>>
|
||||
]>;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// W65816 Argument Calling Convention
|
||||
//===----------------------------------------------------------------------===//
|
||||
def CC_W65816 : CallingConv<[
|
||||
// Pass byval aggregates on the stack.
|
||||
CCIfByVal<CCPassByVal<2, 1>>,
|
||||
|
||||
// Promote i8 arguments to i16 when passed on the stack.
|
||||
CCIfType<[i8], CCPromoteToType<i16>>,
|
||||
|
||||
// Integer arguments occupy 2-byte stack slots with 1-byte alignment.
|
||||
CCIfType<[i16], CCAssignToStack<2, 1>>
|
||||
]>;
|
||||
148
src/llvm/lib/Target/W65816/W65816FrameLowering.cpp
Normal file
148
src/llvm/lib/Target/W65816/W65816FrameLowering.cpp
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
//===-- W65816FrameLowering.cpp - W65816 Frame Information ----------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Skeleton frame lowering. The 65816 stack grows downward and has a 1-byte
|
||||
// alignment; prologue and epilogue emission will be implemented alongside
|
||||
// the full calling convention.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816FrameLowering.h"
|
||||
#include "W65816InstrInfo.h"
|
||||
#include "W65816Subtarget.h"
|
||||
#include "llvm/CodeGen/MachineFrameInfo.h"
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
||||
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
W65816FrameLowering::W65816FrameLowering(const W65816Subtarget &STI)
|
||||
: TargetFrameLowering(TargetFrameLowering::StackGrowsDown, Align(1), 0,
|
||||
Align(1)) {}
|
||||
|
||||
bool W65816FrameLowering::hasFPImpl(const MachineFunction &MF) const {
|
||||
// The 65816 has no native frame pointer — direct page would be the
|
||||
// logical home for one but we don't reserve it. Return false so
|
||||
// every function uses pure stack-relative addressing for locals.
|
||||
// Variable-size objects (alloca with non-constant size) and
|
||||
// frameaddress() intrinsics will need a real FP eventually.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool W65816FrameLowering::hasReservedCallFrame(const MachineFunction &MF) const {
|
||||
return !MF.getFrameInfo().hasVarSizedObjects();
|
||||
}
|
||||
|
||||
void W65816FrameLowering::emitPrologue(MachineFunction &MF,
|
||||
MachineBasicBlock &MBB) const {
|
||||
// Pick canonical mode based on whether the function uses any 8-bit
|
||||
// accumulator values. Pure-i16 functions get REP #$30 (16-bit M+X);
|
||||
// any-i8 functions get REP #$10 + SEP #$20 (16-bit X, 8-bit A).
|
||||
// This is a coarse heuristic until the REP/SEP scheduling pass
|
||||
// (design doc §3.3) can insert mode transitions per-region.
|
||||
const W65816Subtarget &STI = MF.getSubtarget<W65816Subtarget>();
|
||||
const W65816InstrInfo &TII = *STI.getInstrInfo();
|
||||
const MachineRegisterInfo &MRI = MF.getRegInfo();
|
||||
MachineBasicBlock::iterator MBBI = MBB.begin();
|
||||
DebugLoc DL;
|
||||
|
||||
// Heuristic: scan the function body for any value with i8 type.
|
||||
// Captures both signature types and internal i8 ops (e.g. a void
|
||||
// function that loads / stores bytes). An eventual full
|
||||
// mode-dependence analysis (the REP/SEP pass) will replace this.
|
||||
bool UsesAcc8 = false;
|
||||
const Function &F = MF.getFunction();
|
||||
auto isI8 = [](Type *T) { return T && T->isIntegerTy(8); };
|
||||
if (isI8(F.getReturnType()))
|
||||
UsesAcc8 = true;
|
||||
for (const Argument &Arg : F.args()) {
|
||||
if (isI8(Arg.getType())) {
|
||||
UsesAcc8 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!UsesAcc8) {
|
||||
for (const BasicBlock &BB : F) {
|
||||
if (UsesAcc8) break;
|
||||
for (const Instruction &I : BB) {
|
||||
if (isI8(I.getType())) {
|
||||
UsesAcc8 = true;
|
||||
break;
|
||||
}
|
||||
for (const Value *Op : I.operands()) {
|
||||
if (isI8(Op->getType())) {
|
||||
UsesAcc8 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (UsesAcc8) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
(void)MRI;
|
||||
|
||||
if (UsesAcc8) {
|
||||
BuildMI(MBB, MBBI, DL, TII.get(W65816::REP)).addImm(0x10);
|
||||
BuildMI(MBB, MBBI, DL, TII.get(W65816::SEP)).addImm(0x20);
|
||||
} else {
|
||||
BuildMI(MBB, MBBI, DL, TII.get(W65816::REP)).addImm(0x30);
|
||||
}
|
||||
|
||||
// Reserve stack space for locals/spills if any. Sequence is
|
||||
// `TSC ; SEC ; SBC #N ; TCS` to subtract N from S in 16-bit mode.
|
||||
// Skipped for i8 functions for now since the stack adjustment uses
|
||||
// the 16-bit accumulator (would need a save/restore around it).
|
||||
uint64_t StackSize = MF.getFrameInfo().getStackSize();
|
||||
if (StackSize > 0 && !UsesAcc8) {
|
||||
BuildMI(MBB, MBBI, DL, TII.get(W65816::TSC));
|
||||
BuildMI(MBB, MBBI, DL, TII.get(W65816::SEC));
|
||||
BuildMI(MBB, MBBI, DL, TII.get(W65816::SBC_Imm16))
|
||||
.addImm(StackSize);
|
||||
BuildMI(MBB, MBBI, DL, TII.get(W65816::TCS));
|
||||
}
|
||||
}
|
||||
|
||||
void W65816FrameLowering::emitEpilogue(MachineFunction &MF,
|
||||
MachineBasicBlock &MBB) const {
|
||||
// Mirror image of the prologue: release any reserved frame bytes
|
||||
// before the RTL.
|
||||
uint64_t StackSize = MF.getFrameInfo().getStackSize();
|
||||
if (StackSize == 0)
|
||||
return;
|
||||
|
||||
const W65816Subtarget &STI = MF.getSubtarget<W65816Subtarget>();
|
||||
const W65816InstrInfo &TII = *STI.getInstrInfo();
|
||||
MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr();
|
||||
// Insert before the terminator (the return).
|
||||
DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc();
|
||||
|
||||
const Function &F = MF.getFunction();
|
||||
bool UsesAcc8 = F.getReturnType()->isIntegerTy(8);
|
||||
if (!UsesAcc8) {
|
||||
for (const Argument &Arg : F.args()) {
|
||||
if (Arg.getType()->isIntegerTy(8)) { UsesAcc8 = true; break; }
|
||||
}
|
||||
}
|
||||
if (UsesAcc8) return; // Cannot 16-bit math while in 8-bit mode.
|
||||
|
||||
BuildMI(MBB, MBBI, DL, TII.get(W65816::TSC));
|
||||
BuildMI(MBB, MBBI, DL, TII.get(W65816::CLC));
|
||||
BuildMI(MBB, MBBI, DL, TII.get(W65816::ADC_Imm16))
|
||||
.addImm(StackSize);
|
||||
BuildMI(MBB, MBBI, DL, TII.get(W65816::TCS));
|
||||
}
|
||||
|
||||
MachineBasicBlock::iterator W65816FrameLowering::eliminateCallFramePseudoInstr(
|
||||
MachineFunction &MF, MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::iterator I) const {
|
||||
// Drop ADJCALLSTACKDOWN/UP with no replacement for now.
|
||||
return MBB.erase(I);
|
||||
}
|
||||
39
src/llvm/lib/Target/W65816/W65816FrameLowering.h
Normal file
39
src/llvm/lib/Target/W65816/W65816FrameLowering.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
//==- W65816FrameLowering.h - Define frame lowering for W65816 --*- C++ -*--==//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_W65816FRAMELOWERING_H
|
||||
#define LLVM_LIB_TARGET_W65816_W65816FRAMELOWERING_H
|
||||
|
||||
#include "llvm/CodeGen/TargetFrameLowering.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class W65816Subtarget;
|
||||
|
||||
class W65816FrameLowering : public TargetFrameLowering {
|
||||
protected:
|
||||
bool hasFPImpl(const MachineFunction &MF) const override;
|
||||
|
||||
public:
|
||||
explicit W65816FrameLowering(const W65816Subtarget &STI);
|
||||
|
||||
void emitPrologue(MachineFunction &MF,
|
||||
MachineBasicBlock &MBB) const override;
|
||||
void emitEpilogue(MachineFunction &MF,
|
||||
MachineBasicBlock &MBB) const override;
|
||||
|
||||
MachineBasicBlock::iterator
|
||||
eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::iterator I) const override;
|
||||
|
||||
bool hasReservedCallFrame(const MachineFunction &MF) const override;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
87
src/llvm/lib/Target/W65816/W65816ISelDAGToDAG.cpp
Normal file
87
src/llvm/lib/Target/W65816/W65816ISelDAGToDAG.cpp
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
//===-- W65816ISelDAGToDAG.cpp - DAG to DAG inst selector for W65816 ------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Skeleton DAG-to-DAG selector for the W65816. Falls back to the TableGen
|
||||
// selector for every node; real custom selection (frame-index materialisation,
|
||||
// addressing-mode matching, etc.) will be added once instructions exist.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816.h"
|
||||
#include "W65816SelectionDAGInfo.h"
|
||||
#include "W65816TargetMachine.h"
|
||||
#include "llvm/CodeGen/SelectionDAG.h"
|
||||
#include "llvm/CodeGen/SelectionDAGISel.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "w65816-isel"
|
||||
#define PASS_NAME "W65816 DAG->DAG Pattern Instruction Selection"
|
||||
|
||||
namespace {
|
||||
|
||||
class W65816DAGToDAGISel : public SelectionDAGISel {
|
||||
public:
|
||||
W65816DAGToDAGISel() = delete;
|
||||
|
||||
W65816DAGToDAGISel(W65816TargetMachine &TM, CodeGenOptLevel OptLevel)
|
||||
: SelectionDAGISel(TM, OptLevel) {}
|
||||
|
||||
private:
|
||||
#include "W65816GenDAGISel.inc"
|
||||
|
||||
void Select(SDNode *N) override;
|
||||
|
||||
// ComplexPattern selector: matches a FrameIndex SDValue and returns
|
||||
// it (plus a zero offset) so LDAfi / STAfi can fold the FI directly
|
||||
// into their operand list.
|
||||
bool SelectFrameIndex(SDValue N, SDValue &Base, SDValue &Offset);
|
||||
};
|
||||
|
||||
class W65816DAGToDAGISelLegacy : public SelectionDAGISelLegacy {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
W65816DAGToDAGISelLegacy(W65816TargetMachine &TM, CodeGenOptLevel OptLevel)
|
||||
: SelectionDAGISelLegacy(
|
||||
ID, std::make_unique<W65816DAGToDAGISel>(TM, OptLevel)) {}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
char W65816DAGToDAGISelLegacy::ID;
|
||||
|
||||
INITIALIZE_PASS(W65816DAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)
|
||||
|
||||
FunctionPass *llvm::createW65816ISelDag(W65816TargetMachine &TM,
|
||||
CodeGenOptLevel OptLevel) {
|
||||
return new W65816DAGToDAGISelLegacy(TM, OptLevel);
|
||||
}
|
||||
|
||||
void W65816DAGToDAGISel::Select(SDNode *Node) {
|
||||
if (Node->isMachineOpcode()) {
|
||||
Node->setNodeId(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Defer to the auto-generated selector for everything else. Custom
|
||||
// selection paths (frame-index, wrapper, etc.) will land here later.
|
||||
SelectCode(Node);
|
||||
}
|
||||
|
||||
bool W65816DAGToDAGISel::SelectFrameIndex(SDValue N, SDValue &Base,
|
||||
SDValue &Offset) {
|
||||
if (auto *FIN = dyn_cast<FrameIndexSDNode>(N)) {
|
||||
Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i16);
|
||||
Offset = CurDAG->getTargetConstant(0, SDLoc(N), MVT::i16);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
329
src/llvm/lib/Target/W65816/W65816ISelLowering.cpp
Normal file
329
src/llvm/lib/Target/W65816/W65816ISelLowering.cpp
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
//===-- W65816ISelLowering.cpp - W65816 DAG Lowering Implementation -------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Minimum DAG lowering sufficient for a no-argument function returning an
|
||||
// i16 constant. Argument passing and non-trivial calls still unimplemented.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816ISelLowering.h"
|
||||
#include "W65816SelectionDAGInfo.h"
|
||||
#include "W65816Subtarget.h"
|
||||
#include "llvm/CodeGen/MachineFrameInfo.h"
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
||||
#include "llvm/CodeGen/SelectionDAG.h"
|
||||
#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "w65816-lower"
|
||||
|
||||
W65816TargetLowering::W65816TargetLowering(const TargetMachine &TM,
|
||||
const W65816Subtarget &STI)
|
||||
: TargetLowering(TM, STI) {
|
||||
// Register classes for the two scalar modes. The register allocator sees
|
||||
// A, X and Y as both 8-bit and 16-bit; a later REP/SEP pass is responsible
|
||||
// for ensuring the dynamic mode matches the selected class.
|
||||
addRegisterClass(MVT::i8, &W65816::Acc8RegClass);
|
||||
addRegisterClass(MVT::i16, &W65816::Acc16RegClass);
|
||||
|
||||
computeRegisterProperties(STI.getRegisterInfo());
|
||||
|
||||
setStackPointerRegisterToSaveRestore(W65816::SP);
|
||||
setBooleanContents(ZeroOrOneBooleanContent);
|
||||
setBooleanVectorContents(ZeroOrOneBooleanContent);
|
||||
|
||||
// GlobalAddress and ExternalSymbol: lower to W65816ISD::Wrapper so a
|
||||
// tablegen pattern can fold them into instruction operands.
|
||||
setOperationAction(ISD::GlobalAddress, MVT::i16, Custom);
|
||||
setOperationAction(ISD::ExternalSymbol, MVT::i16, Custom);
|
||||
|
||||
// BR_CC is custom-lowered to a CMP + W65816ISD::BR_CC chain so we can
|
||||
// emit the right BEQ/BNE/BCS/BCC mnemonic per condition.
|
||||
setOperationAction(ISD::BR_CC, MVT::i16, Custom);
|
||||
setOperationAction(ISD::BR_CC, MVT::i8, Custom);
|
||||
setOperationAction(ISD::BRCOND, MVT::Other, Expand);
|
||||
setOperationAction(ISD::BR_JT, MVT::Other, Expand);
|
||||
|
||||
// SELECT / SELECT_CC: leave as default for now. Expanding either
|
||||
// currently infinite-loops because they cross-expand into each other
|
||||
// without a base case. Custom lowering to a Bxx + branch + phi
|
||||
// pattern is the right fix; tracked separately.
|
||||
|
||||
// The 65816 has no hardware multiplier or divider. Multiply by a
|
||||
// power-of-two constant is auto-rewritten to shifts by the DAG
|
||||
// combiner; arbitrary multiply / divide / mod fail to select today.
|
||||
// Real support needs (a) library functions (`__mulhi3` etc.) and
|
||||
// (b) multi-arg call lowering — both are tracked separately.
|
||||
setOperationAction(ISD::MULHU, MVT::i16, Expand);
|
||||
setOperationAction(ISD::MULHS, MVT::i16, Expand);
|
||||
setOperationAction(ISD::SMUL_LOHI, MVT::i16, Expand);
|
||||
setOperationAction(ISD::UMUL_LOHI, MVT::i16, Expand);
|
||||
}
|
||||
|
||||
// Map an LLVM SETCC condition to a W65816 branch. Returns the condition
|
||||
// code along with possibly-swapped LHS/RHS; some signed comparisons are
|
||||
// rewritten to use unsigned ones with a tweaked operand because the
|
||||
// 65816 has no native signed branch other than BMI/BPL on a value, not
|
||||
// on a comparison result.
|
||||
// Map an LLVM SETCC condition to a 65816 branch. Unsigned codes use
|
||||
// BCS/BCC after CMP. Signed SETLT/SETGE map to BMI/BPL — correct only
|
||||
// when the comparison cannot overflow. For values produced by typical
|
||||
// C arithmetic on i16 this is usually fine; values near INT16_MIN/MAX
|
||||
// could give wrong results until we emit the BVS handling sequence.
|
||||
// SETGT / SETLE are rewritten to SETLT / SETGE with constant + 1 in
|
||||
// LowerBR_CC, mirroring the SETULE / SETUGT path.
|
||||
static W65816CC::CondCode mapCC(ISD::CondCode CC) {
|
||||
switch (CC) {
|
||||
case ISD::SETEQ: return W65816CC::COND_EQ;
|
||||
case ISD::SETNE: return W65816CC::COND_NE;
|
||||
case ISD::SETUGE: return W65816CC::COND_HS;
|
||||
case ISD::SETULT: return W65816CC::COND_LO;
|
||||
case ISD::SETLT: return W65816CC::COND_MI;
|
||||
case ISD::SETGE: return W65816CC::COND_PL;
|
||||
default:
|
||||
return W65816CC::COND_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
SDValue W65816TargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const {
|
||||
SDValue Chain = Op.getOperand(0);
|
||||
ISD::CondCode CC = cast<CondCodeSDNode>(Op.getOperand(1))->get();
|
||||
SDValue LHS = Op.getOperand(2);
|
||||
SDValue RHS = Op.getOperand(3);
|
||||
SDValue Dest = Op.getOperand(4);
|
||||
SDLoc DL(Op);
|
||||
|
||||
// CMP wants the comparand (constant or memory) on the right. If a DAG
|
||||
// pre-pass put the constant on the left, swap and flip the condition.
|
||||
if (isa<ConstantSDNode>(LHS) && !isa<ConstantSDNode>(RHS)) {
|
||||
std::swap(LHS, RHS);
|
||||
CC = ISD::getSetCCSwappedOperands(CC);
|
||||
}
|
||||
|
||||
// Rewrite SETULE / SETUGT / SETLE / SETGT to SETULT / SETUGE / SETLT /
|
||||
// SETGE with constant +/- 1. This keeps the variable on the LHS (so
|
||||
// our pattern matches) and lets us use the BCS / BCC / BMI / BPL
|
||||
// mnemonics natively. Only valid when the constant is not at its
|
||||
// signed/unsigned boundary; for now we just bail in that pathological
|
||||
// case.
|
||||
if (auto *RhsConst = dyn_cast<ConstantSDNode>(RHS)) {
|
||||
int64_t V = RhsConst->getSExtValue();
|
||||
if (CC == ISD::SETULE && (uint64_t)V < 0xffff) {
|
||||
RHS = DAG.getConstant(V + 1, DL, RHS.getValueType());
|
||||
CC = ISD::SETULT;
|
||||
} else if (CC == ISD::SETUGT && (uint64_t)V < 0xffff) {
|
||||
RHS = DAG.getConstant(V + 1, DL, RHS.getValueType());
|
||||
CC = ISD::SETUGE;
|
||||
} else if (CC == ISD::SETLE && V < 0x7fff) {
|
||||
RHS = DAG.getConstant(V + 1, DL, RHS.getValueType());
|
||||
CC = ISD::SETLT;
|
||||
} else if (CC == ISD::SETGT && V < 0x7fff) {
|
||||
RHS = DAG.getConstant(V + 1, DL, RHS.getValueType());
|
||||
CC = ISD::SETGE;
|
||||
}
|
||||
}
|
||||
|
||||
// Final fallback: any condition we didn't handle yet might still be
|
||||
// representable by swapping operands (e.g. SETUGT b a → SETULT a b).
|
||||
// Try once if the direct map doesn't recognise it.
|
||||
W65816CC::CondCode TCC = mapCC(CC);
|
||||
if (TCC == W65816CC::COND_INVALID) {
|
||||
std::swap(LHS, RHS);
|
||||
CC = ISD::getSetCCSwappedOperands(CC);
|
||||
TCC = mapCC(CC);
|
||||
}
|
||||
if (TCC == W65816CC::COND_INVALID)
|
||||
report_fatal_error("W65816: branch condition not yet implemented");
|
||||
|
||||
SDValue Glue = DAG.getNode(W65816ISD::CMP, DL, MVT::Glue, LHS, RHS);
|
||||
SDValue CCOp = DAG.getTargetConstant(TCC, DL, MVT::i8);
|
||||
return DAG.getNode(W65816ISD::BR_CC, DL, MVT::Other, Chain, Dest, CCOp,
|
||||
Glue);
|
||||
}
|
||||
|
||||
SDValue W65816TargetLowering::LowerOperation(SDValue Op,
|
||||
SelectionDAG &DAG) const {
|
||||
switch (Op.getOpcode()) {
|
||||
case ISD::GlobalAddress: return LowerGlobalAddress(Op, DAG);
|
||||
case ISD::ExternalSymbol: return LowerExternalSymbol(Op, DAG);
|
||||
case ISD::BR_CC: return LowerBR_CC(Op, DAG);
|
||||
default:
|
||||
llvm_unreachable("W65816: unexpected operation in LowerOperation");
|
||||
}
|
||||
}
|
||||
|
||||
SDValue W65816TargetLowering::LowerGlobalAddress(SDValue Op,
|
||||
SelectionDAG &DAG) const {
|
||||
auto *GA = cast<GlobalAddressSDNode>(Op);
|
||||
SDLoc DL(Op);
|
||||
SDValue Tgt = DAG.getTargetGlobalAddress(GA->getGlobal(), DL, MVT::i16,
|
||||
GA->getOffset());
|
||||
return DAG.getNode(W65816ISD::Wrapper, DL, MVT::i16, Tgt);
|
||||
}
|
||||
|
||||
SDValue W65816TargetLowering::LowerExternalSymbol(SDValue Op,
|
||||
SelectionDAG &DAG) const {
|
||||
auto *ES = cast<ExternalSymbolSDNode>(Op);
|
||||
SDLoc DL(Op);
|
||||
SDValue Tgt = DAG.getTargetExternalSymbol(ES->getSymbol(), MVT::i16);
|
||||
return DAG.getNode(W65816ISD::Wrapper, DL, MVT::i16, Tgt);
|
||||
}
|
||||
|
||||
SDValue W65816TargetLowering::LowerFormalArguments(
|
||||
SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL,
|
||||
SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const {
|
||||
// ABI: first i16/i8 argument is passed in A; remaining arguments are
|
||||
// pushed by the caller right-to-left and read via stack-relative
|
||||
// addressing. After JSL pushes 3 bytes of return address, the layout
|
||||
// viewed from the callee is:
|
||||
// (high addr) arg N-1
|
||||
// ...
|
||||
// arg 1
|
||||
// ret-addr-bank <- (4,S) when M=0
|
||||
// ret-addr-hi <- (3,S)
|
||||
// ret-addr-lo <- (2,S)
|
||||
// (low addr) <next push> <- (1,S)
|
||||
//
|
||||
// Each i16 stack arg occupies 2 bytes. arg 1 lives at (4,S).
|
||||
if (IsVarArg)
|
||||
report_fatal_error("W65816: vararg functions not yet supported");
|
||||
|
||||
MachineFunction &MF = DAG.getMachineFunction();
|
||||
MachineFrameInfo &MFI = MF.getFrameInfo();
|
||||
MachineRegisterInfo &MRI = MF.getRegInfo();
|
||||
|
||||
unsigned ArgIdx = 0;
|
||||
// Stack offset is measured from S+1 (the WDC convention) and grows
|
||||
// upward as we walk through the stack-passed args.
|
||||
unsigned StackOffset = 4; // Skip 3 ret-addr bytes; first slot at S+4.
|
||||
for (const ISD::InputArg &Arg : Ins) {
|
||||
MVT VT = Arg.VT;
|
||||
if (VT != MVT::i16 && VT != MVT::i8)
|
||||
report_fatal_error("W65816: argument type not yet supported");
|
||||
|
||||
if (ArgIdx == 0) {
|
||||
// First arg in A.
|
||||
Register VReg = MRI.createVirtualRegister(
|
||||
VT == MVT::i16 ? &W65816::Acc16RegClass : &W65816::Acc8RegClass);
|
||||
MRI.addLiveIn(W65816::A, VReg);
|
||||
InVals.push_back(DAG.getCopyFromReg(Chain, DL, VReg, VT));
|
||||
} else {
|
||||
// Subsequent args are loaded from the stack. Use a fixed frame
|
||||
// object positioned at the absolute stack offset; the
|
||||
// eliminateFrameIndex pass turns it into LDA d,S.
|
||||
unsigned ObjSize = (VT == MVT::i16) ? 2 : 1;
|
||||
int FI = MFI.CreateFixedObject(ObjSize, StackOffset, /*Immutable*/true);
|
||||
StackOffset += ObjSize;
|
||||
SDValue FIN = DAG.getFrameIndex(FI, MVT::i16);
|
||||
InVals.push_back(DAG.getLoad(
|
||||
VT, DL, Chain, FIN,
|
||||
MachinePointerInfo::getFixedStack(MF, FI)));
|
||||
}
|
||||
++ArgIdx;
|
||||
}
|
||||
return Chain;
|
||||
}
|
||||
|
||||
SDValue
|
||||
W65816TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
||||
SmallVectorImpl<SDValue> &InVals) const {
|
||||
// Single-arg version: arg 0 in A; LowerFormalArguments accepts
|
||||
// additional args via the stack, but this side doesn't yet emit the
|
||||
// pushes. Multi-arg call lowering wants a PUSHA pseudo with proper
|
||||
// SP unwinding via TSC/ADC #N/TCS in the ADJCALLSTACKUP pseudo —
|
||||
// tracked separately.
|
||||
SelectionDAG &DAG = CLI.DAG;
|
||||
SDLoc &DL = CLI.DL;
|
||||
SDValue Chain = CLI.Chain;
|
||||
SDValue Callee = CLI.Callee;
|
||||
auto &Outs = CLI.Outs;
|
||||
auto &OutVals = CLI.OutVals;
|
||||
auto &Ins = CLI.Ins;
|
||||
|
||||
if (CLI.IsTailCall)
|
||||
CLI.IsTailCall = false;
|
||||
if (Outs.size() > 1)
|
||||
report_fatal_error("W65816: multi-argument calls not yet supported");
|
||||
if (Ins.size() > 1)
|
||||
report_fatal_error("W65816: multi-return calls not yet supported");
|
||||
|
||||
Chain = DAG.getCALLSEQ_START(Chain, 0, 0, DL);
|
||||
|
||||
SDValue Glue;
|
||||
if (!OutVals.empty()) {
|
||||
MVT VT = Outs[0].VT;
|
||||
Chain = DAG.getCopyToReg(Chain, DL, W65816::A, OutVals[0], Glue);
|
||||
Glue = Chain.getValue(1);
|
||||
}
|
||||
|
||||
if (auto *GA = dyn_cast<GlobalAddressSDNode>(Callee))
|
||||
Callee = DAG.getTargetGlobalAddress(GA->getGlobal(), DL, MVT::i16);
|
||||
else if (auto *ES = dyn_cast<ExternalSymbolSDNode>(Callee))
|
||||
Callee = DAG.getTargetExternalSymbol(ES->getSymbol(), MVT::i16);
|
||||
|
||||
SmallVector<SDValue, 4> CallOps = {Chain, Callee};
|
||||
if (!OutVals.empty())
|
||||
CallOps.push_back(DAG.getRegister(W65816::A, Outs[0].VT));
|
||||
if (Glue.getNode())
|
||||
CallOps.push_back(Glue);
|
||||
|
||||
Chain = DAG.getNode(W65816ISD::CALL, DL,
|
||||
DAG.getVTList(MVT::Other, MVT::Glue), CallOps);
|
||||
Glue = Chain.getValue(1);
|
||||
|
||||
Chain = DAG.getCALLSEQ_END(Chain, 0, 0, Glue, DL);
|
||||
Glue = Chain.getValue(1);
|
||||
|
||||
for (const ISD::InputArg &Arg : Ins) {
|
||||
MVT VT = Arg.VT;
|
||||
if (VT != MVT::i16 && VT != MVT::i8)
|
||||
report_fatal_error("W65816: return type not yet supported");
|
||||
SDValue V = DAG.getCopyFromReg(Chain, DL, W65816::A, VT, Glue);
|
||||
Chain = V.getValue(1);
|
||||
Glue = V.getValue(2);
|
||||
InVals.push_back(V);
|
||||
}
|
||||
|
||||
return Chain;
|
||||
}
|
||||
|
||||
SDValue W65816TargetLowering::LowerReturn(
|
||||
SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
|
||||
SelectionDAG &DAG) const {
|
||||
// Copy scalar return values into A and emit a retglue chain. Supports
|
||||
// one i16 return today; i8 would use the same A register in 8-bit mode,
|
||||
// and larger returns (i32 A:X, structures via hidden pointer) are future
|
||||
// work.
|
||||
// Copy each scalar return value into A and reference A in the RET_GLUE
|
||||
// operand list so the register allocator keeps the defining instructions
|
||||
// alive (otherwise dead-MI elimination strips them — the physreg copy
|
||||
// alone is not enough of a liveness signal).
|
||||
SDValue Glue;
|
||||
SmallVector<SDValue, 4> RetOps(1, Chain);
|
||||
for (unsigned i = 0, e = Outs.size(); i != e; ++i) {
|
||||
MVT VT = Outs[i].VT;
|
||||
if (VT != MVT::i16 && VT != MVT::i8)
|
||||
report_fatal_error("W65816: return type not yet supported");
|
||||
Chain = DAG.getCopyToReg(Chain, DL, W65816::A, OutVals[i], Glue);
|
||||
Glue = Chain.getValue(1);
|
||||
RetOps.push_back(DAG.getRegister(W65816::A, VT));
|
||||
}
|
||||
|
||||
RetOps[0] = Chain;
|
||||
if (Glue.getNode())
|
||||
RetOps.push_back(Glue);
|
||||
|
||||
return DAG.getNode(W65816ISD::RET_GLUE, DL, MVT::Other, RetOps);
|
||||
}
|
||||
70
src/llvm/lib/Target/W65816/W65816ISelLowering.h
Normal file
70
src/llvm/lib/Target/W65816/W65816ISelLowering.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
//===-- W65816ISelLowering.h - W65816 DAG Lowering Interface ----*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the interfaces that W65816 uses to lower LLVM code into a
|
||||
// selection DAG.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_W65816ISELLOWERING_H
|
||||
#define LLVM_LIB_TARGET_W65816_W65816ISELLOWERING_H
|
||||
|
||||
#include "W65816.h"
|
||||
#include "llvm/CodeGen/TargetLowering.h"
|
||||
|
||||
// W65816ISD::{Wrapper, CALL, RET_GLUE, ...} are defined by TableGen in
|
||||
// W65816GenSDNodeInfo.inc; that header is included transitively via
|
||||
// W65816SelectionDAGInfo.h.
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class W65816Subtarget;
|
||||
|
||||
class W65816TargetLowering : public TargetLowering {
|
||||
public:
|
||||
explicit W65816TargetLowering(const TargetMachine &TM,
|
||||
const W65816Subtarget &STI);
|
||||
|
||||
SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv,
|
||||
bool IsVarArg,
|
||||
const SmallVectorImpl<ISD::InputArg> &Ins,
|
||||
const SDLoc &DL, SelectionDAG &DAG,
|
||||
SmallVectorImpl<SDValue> &InVals) const override;
|
||||
|
||||
SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
||||
SmallVectorImpl<SDValue> &InVals) const override;
|
||||
|
||||
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;
|
||||
|
||||
SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override;
|
||||
|
||||
// The 65816 has no alignment requirement on memory access — any
|
||||
// address is fine. Telling LLVM this lets it emit single 16-bit
|
||||
// loads/stores even when the IR alignment is 1, instead of
|
||||
// synthesising the value from two byte loads + shl-or.
|
||||
bool allowsMisalignedMemoryAccesses(EVT VT, unsigned AddrSpace = 0,
|
||||
Align Alignment = Align(1),
|
||||
MachineMemOperand::Flags Flags =
|
||||
MachineMemOperand::MONone,
|
||||
unsigned *Fast = nullptr) const override {
|
||||
if (Fast) *Fast = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
|
||||
SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const;
|
||||
SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
281
src/llvm/lib/Target/W65816/W65816InstrFormats.td
Normal file
281
src/llvm/lib/Target/W65816/W65816InstrFormats.td
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
//===-- W65816InstrFormats.td - W65816 Instruction Formats -*- tablegen -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Instruction format definitions for the W65816. Every WDC 65816 instruction
|
||||
// is 1-4 bytes: a single-byte opcode followed by 0-3 bytes of immediate data,
|
||||
// direct-page offset, absolute address, long address, or PC-relative
|
||||
// displacement.
|
||||
//
|
||||
// TSFlags encode which processor-mode bits (M, X) an instruction depends on.
|
||||
// Bit 0: instruction assumes M clear (16-bit accumulator).
|
||||
// Bit 1: instruction assumes M set (8-bit accumulator).
|
||||
// Bit 2: instruction assumes X clear (16-bit index).
|
||||
// Bit 3: instruction assumes X set (8-bit index).
|
||||
// Instructions whose behavior does not depend on M/X leave all four bits 0.
|
||||
// The REP/SEP scheduling pass consumes these bits to decide where to insert
|
||||
// mode transitions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Base instruction class. Subclasses set Size and fill in the opcode byte
|
||||
// and operand fields.
|
||||
class W65816Inst<dag outs, dag ins, string asmstr, list<dag> pattern = []>
|
||||
: Instruction {
|
||||
let Namespace = "W65816";
|
||||
// The disassembler scaffold reads from the default "W65816" decoder table.
|
||||
// Mode-ambiguous variants (LDA_Imm8, LDX_Imm8, ...) override this to
|
||||
// "W65816MHigh" or "W65816XHigh" so they end up in separate tables that
|
||||
// the scaffold does NOT consult, eliminating same-opcode conflicts in the
|
||||
// default table. See comment in W65816InstrInfo.td.
|
||||
let DecoderNamespace = "W65816";
|
||||
let OutOperandList = outs;
|
||||
let InOperandList = ins;
|
||||
let AsmString = asmstr;
|
||||
let Pattern = pattern;
|
||||
|
||||
// MC-layer-only instructions conservatively mark side effects so the
|
||||
// scheduler can't reorder them. Lowering targets override as needed.
|
||||
let mayLoad = true;
|
||||
let mayStore = true;
|
||||
let hasSideEffects = true;
|
||||
|
||||
// Mode-dependence bits consumed by the REP/SEP pass.
|
||||
bit MLow = 0;
|
||||
bit MHigh = 0;
|
||||
bit XLow = 0;
|
||||
bit XHigh = 0;
|
||||
let TSFlags{0} = MLow;
|
||||
let TSFlags{1} = MHigh;
|
||||
let TSFlags{2} = XLow;
|
||||
let TSFlags{3} = XHigh;
|
||||
}
|
||||
|
||||
// Pseudo-instruction: expands away before emission.
|
||||
class W65816Pseudo<dag outs, dag ins, string asmstr, list<dag> pattern>
|
||||
: W65816Inst<outs, ins, asmstr, pattern> {
|
||||
let isPseudo = 1;
|
||||
let Size = 0;
|
||||
let mayLoad = false;
|
||||
let mayStore = false;
|
||||
let hasSideEffects = false;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Operand classes.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Custom DiagnosticType values would require matching Match_<type> enum
|
||||
// values in the parser; we rely on the generic Match_InvalidOperand
|
||||
// message for now.
|
||||
class W65816AsmOperand<string name> : AsmOperandClass {
|
||||
let Name = name;
|
||||
}
|
||||
|
||||
class W65816ImmOp<string name> : W65816AsmOperand<name> {
|
||||
let RenderMethod = "addImmOperands";
|
||||
}
|
||||
|
||||
class W65816AddrOp<string name> : W65816AsmOperand<name>;
|
||||
class W65816PCRelOp<string name> : W65816AsmOperand<name>;
|
||||
|
||||
def imm8 : Operand<i16> {
|
||||
let ParserMatchClass = W65816ImmOp<"Imm8">;
|
||||
let OperandType = "OPERAND_IMMEDIATE";
|
||||
let Type = i8;
|
||||
let EncoderMethod = "encodeImm8";
|
||||
let DecoderMethod = "decodeImm8";
|
||||
}
|
||||
|
||||
def imm16 : Operand<i16> {
|
||||
let ParserMatchClass = W65816ImmOp<"Imm16">;
|
||||
let OperandType = "OPERAND_IMMEDIATE";
|
||||
let Type = i16;
|
||||
let EncoderMethod = "encodeImm16";
|
||||
let DecoderMethod = "decodeImm16";
|
||||
}
|
||||
|
||||
def addrDP : Operand<i16> {
|
||||
let ParserMatchClass = W65816AddrOp<"AddrDP">;
|
||||
let OperandType = "OPERAND_MEMORY";
|
||||
let Type = i8;
|
||||
let PrintMethod = "printAddrDP";
|
||||
let EncoderMethod = "encodeAddr8";
|
||||
let DecoderMethod = "decodeAddr8";
|
||||
}
|
||||
|
||||
def addrAbs : Operand<i16> {
|
||||
let ParserMatchClass = W65816AddrOp<"AddrAbs">;
|
||||
let OperandType = "OPERAND_MEMORY";
|
||||
let Type = i16;
|
||||
let PrintMethod = "printAddrAbs";
|
||||
let EncoderMethod = "encodeAddr16";
|
||||
let DecoderMethod = "decodeAddr16";
|
||||
}
|
||||
|
||||
def addrLong : Operand<i32> {
|
||||
let ParserMatchClass = W65816AddrOp<"AddrLong">;
|
||||
let OperandType = "OPERAND_MEMORY";
|
||||
let Type = i32;
|
||||
let PrintMethod = "printAddrLong";
|
||||
let EncoderMethod = "encodeAddr24";
|
||||
let DecoderMethod = "decodeAddr24";
|
||||
}
|
||||
|
||||
def pcrel8 : Operand<OtherVT> {
|
||||
let ParserMatchClass = W65816PCRelOp<"PCRel8">;
|
||||
let OperandType = "OPERAND_PCREL";
|
||||
let PrintMethod = "printPCRel8";
|
||||
let EncoderMethod = "encodePCRel8";
|
||||
let DecoderMethod = "decodePCRel8";
|
||||
}
|
||||
|
||||
def pcrel16 : Operand<OtherVT> {
|
||||
let ParserMatchClass = W65816PCRelOp<"PCRel16">;
|
||||
let OperandType = "OPERAND_PCREL";
|
||||
let PrintMethod = "printPCRel16";
|
||||
let EncoderMethod = "encodePCRel16";
|
||||
let DecoderMethod = "decodePCRel16";
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Size-classified base instructions. Size is in bytes including opcode.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Single-byte implied-operand instruction.
|
||||
class Inst1<bits<8> opcode, string asmstr>
|
||||
: W65816Inst<(outs), (ins), asmstr> {
|
||||
let Size = 1;
|
||||
bits<8> Inst;
|
||||
let Inst{7-0} = opcode;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Addressing-mode shorthand classes.
|
||||
//
|
||||
// Each class declares a concrete 16/24/32-bit instruction encoding and wires
|
||||
// the named operand(s) into the bit positions following the opcode byte.
|
||||
// Without the explicit `let Inst{...} = name;` assignments, tablegen emits an
|
||||
// encoder that writes the opcode but leaves the operand bytes as zero.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
class InstImplied<bits<8> op, string mnem> : Inst1<op, mnem>;
|
||||
|
||||
class InstImm8<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins imm8:$imm), !strconcat(mnem, "\t#$imm")> {
|
||||
let Size = 2;
|
||||
bits<8> imm;
|
||||
bits<16> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{15-8} = imm;
|
||||
}
|
||||
|
||||
class InstImm16<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins imm16:$imm), !strconcat(mnem, "\t#$imm")> {
|
||||
let Size = 3;
|
||||
bits<16> imm;
|
||||
bits<24> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{23-8} = imm;
|
||||
}
|
||||
|
||||
class InstDP<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins addrDP:$addr), !strconcat(mnem, "\t$addr")> {
|
||||
let Size = 2;
|
||||
bits<8> addr;
|
||||
bits<16> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{15-8} = addr;
|
||||
}
|
||||
|
||||
class InstAbs<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins addrAbs:$addr), !strconcat(mnem, "\t$addr")> {
|
||||
let Size = 3;
|
||||
bits<16> addr;
|
||||
bits<24> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{23-8} = addr;
|
||||
}
|
||||
|
||||
class InstAbsLong<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins addrLong:$addr), !strconcat(mnem, "\t$addr")> {
|
||||
let Size = 4;
|
||||
bits<24> addr;
|
||||
bits<32> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{31-8} = addr;
|
||||
}
|
||||
|
||||
class InstAbsX<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins addrAbs:$addr), !strconcat(mnem, "\t$addr, x")> {
|
||||
let Size = 3;
|
||||
bits<16> addr;
|
||||
bits<24> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{23-8} = addr;
|
||||
}
|
||||
|
||||
class InstAbsY<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins addrAbs:$addr), !strconcat(mnem, "\t$addr, y")> {
|
||||
let Size = 3;
|
||||
bits<16> addr;
|
||||
bits<24> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{23-8} = addr;
|
||||
}
|
||||
|
||||
class InstDPX<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins addrDP:$addr), !strconcat(mnem, "\t$addr, x")> {
|
||||
let Size = 2;
|
||||
bits<8> addr;
|
||||
bits<16> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{15-8} = addr;
|
||||
}
|
||||
|
||||
class InstDPY<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins addrDP:$addr), !strconcat(mnem, "\t$addr, y")> {
|
||||
let Size = 2;
|
||||
bits<8> addr;
|
||||
bits<16> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{15-8} = addr;
|
||||
}
|
||||
|
||||
// Stack-relative addressing. Operand is an unsigned 8-bit offset added
|
||||
// to S; reads / writes the byte (or word, in 16-bit M mode) at that
|
||||
// stack address. Shares the addrDP operand class for parsing.
|
||||
class InstStackRel<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins addrDP:$off), !strconcat(mnem, "\t$off, s")> {
|
||||
let Size = 2;
|
||||
bits<8> off;
|
||||
bits<16> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{15-8} = off;
|
||||
}
|
||||
|
||||
class InstPCRel8<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins pcrel8:$dest), !strconcat(mnem, "\t$dest")> {
|
||||
let Size = 2;
|
||||
bits<8> dest;
|
||||
bits<16> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{15-8} = dest;
|
||||
}
|
||||
|
||||
class InstPCRel16<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins pcrel16:$dest), !strconcat(mnem, "\t$dest")> {
|
||||
let Size = 3;
|
||||
bits<16> dest;
|
||||
bits<24> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{23-8} = dest;
|
||||
}
|
||||
|
||||
// Branch / return / call flags are applied at the def site via `let`
|
||||
// blocks (idiomatic LLVM approach) rather than through mixin classes, since
|
||||
// TableGen's multi-inheritance can't override fields from unrelated bases.
|
||||
73
src/llvm/lib/Target/W65816/W65816InstrInfo.cpp
Normal file
73
src/llvm/lib/Target/W65816/W65816InstrInfo.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//===-- W65816InstrInfo.cpp - W65816 Instruction Information --------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Skeleton instruction-info implementation. Real register copy and stack
|
||||
// spill/reload lowering will be added once the instruction set is described.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816InstrInfo.h"
|
||||
#include "W65816.h"
|
||||
#include "W65816Subtarget.h"
|
||||
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define GET_INSTRINFO_CTOR_DTOR
|
||||
#include "W65816GenInstrInfo.inc"
|
||||
|
||||
void W65816InstrInfo::anchor() {}
|
||||
|
||||
W65816InstrInfo::W65816InstrInfo(const W65816Subtarget &STI)
|
||||
: W65816GenInstrInfo(STI, RI, W65816::ADJCALLSTACKDOWN,
|
||||
W65816::ADJCALLSTACKUP),
|
||||
RI() {}
|
||||
|
||||
void W65816InstrInfo::copyPhysReg(MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::iterator I,
|
||||
const DebugLoc &DL, Register DestReg,
|
||||
Register SrcReg, bool KillSrc,
|
||||
bool RenamableDest, bool RenamableSrc) const {
|
||||
// The only Acc16 register is A; copies between A and itself are no-ops.
|
||||
// Cross-class copies (e.g. A → X) need TAX/TXA pairs which we don't
|
||||
// need yet — bail loudly so we notice when the time comes.
|
||||
if (DestReg == SrcReg)
|
||||
return;
|
||||
if (DestReg == W65816::A && SrcReg == W65816::A)
|
||||
return;
|
||||
llvm_unreachable("W65816: cross-class copyPhysReg not yet implemented");
|
||||
}
|
||||
|
||||
void W65816InstrInfo::storeRegToStackSlot(
|
||||
MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg,
|
||||
bool isKill, int FrameIdx, const TargetRegisterClass *RC, Register VReg,
|
||||
MachineInstr::MIFlag Flags) const {
|
||||
// STAfi gets eliminated by W65816RegisterInfo::eliminateFrameIndex into
|
||||
// a real STA d,S. Source is implicit A; emit the pseudo with the FI
|
||||
// and zero offset.
|
||||
DebugLoc DL = MI != MBB.end() ? MI->getDebugLoc() : DebugLoc();
|
||||
BuildMI(MBB, MI, DL, get(W65816::STAfi))
|
||||
.addReg(SrcReg, getKillRegState(isKill))
|
||||
.addFrameIndex(FrameIdx)
|
||||
.addImm(0);
|
||||
}
|
||||
|
||||
void W65816InstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
|
||||
MachineBasicBlock::iterator MI,
|
||||
Register DestReg, int FrameIdx,
|
||||
const TargetRegisterClass *RC,
|
||||
Register VReg, unsigned SubReg,
|
||||
MachineInstr::MIFlag Flags) const {
|
||||
// Mirror image of storeRegToStackSlot: emit LDAfi, which the frame
|
||||
// index pass turns into LDA d,S.
|
||||
DebugLoc DL = MI != MBB.end() ? MI->getDebugLoc() : DebugLoc();
|
||||
BuildMI(MBB, MI, DL, get(W65816::LDAfi), DestReg)
|
||||
.addFrameIndex(FrameIdx)
|
||||
.addImm(0);
|
||||
}
|
||||
53
src/llvm/lib/Target/W65816/W65816InstrInfo.h
Normal file
53
src/llvm/lib/Target/W65816/W65816InstrInfo.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
//===-- W65816InstrInfo.h - W65816 Instruction Information ------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains the W65816 implementation of the TargetInstrInfo class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_W65816INSTRINFO_H
|
||||
#define LLVM_LIB_TARGET_W65816_W65816INSTRINFO_H
|
||||
|
||||
#include "W65816RegisterInfo.h"
|
||||
#include "llvm/CodeGen/TargetInstrInfo.h"
|
||||
|
||||
#define GET_INSTRINFO_HEADER
|
||||
#include "W65816GenInstrInfo.inc"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class W65816Subtarget;
|
||||
|
||||
class W65816InstrInfo : public W65816GenInstrInfo {
|
||||
const W65816RegisterInfo RI;
|
||||
virtual void anchor();
|
||||
|
||||
public:
|
||||
explicit W65816InstrInfo(const W65816Subtarget &STI);
|
||||
|
||||
const W65816RegisterInfo &getRegisterInfo() const { return RI; }
|
||||
|
||||
void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I,
|
||||
const DebugLoc &DL, Register DestReg, Register SrcReg,
|
||||
bool KillSrc, bool RenamableDest = false,
|
||||
bool RenamableSrc = false) const override;
|
||||
|
||||
void storeRegToStackSlot(
|
||||
MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register SrcReg,
|
||||
bool isKill, int FrameIndex, const TargetRegisterClass *RC, Register VReg,
|
||||
MachineInstr::MIFlag Flags = MachineInstr::NoFlags) const override;
|
||||
void loadRegFromStackSlot(
|
||||
MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, Register DestReg,
|
||||
int FrameIdx, const TargetRegisterClass *RC, Register VReg,
|
||||
unsigned SubReg = 0,
|
||||
MachineInstr::MIFlag Flags = MachineInstr::NoFlags) const override;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
776
src/llvm/lib/Target/W65816/W65816InstrInfo.td
Normal file
776
src/llvm/lib/Target/W65816/W65816InstrInfo.td
Normal file
|
|
@ -0,0 +1,776 @@
|
|||
//===-- W65816InstrInfo.td - W65816 Instruction defs -------*- tablegen -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// W65816 instruction description. This file defines the MC-layer instruction
|
||||
// encodings for the core 65816 instruction set. DAG-selection patterns will
|
||||
// be added incrementally on top of these MC instructions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
include "W65816InstrFormats.td"
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Type Profiles
|
||||
//===----------------------------------------------------------------------===//
|
||||
def SDT_W65816Call : SDTypeProfile<0, -1, [SDTCisVT<0, iPTR>]>;
|
||||
def SDT_W65816CallSeqStart : SDCallSeqStart<[SDTCisVT<0, i16>,
|
||||
SDTCisVT<1, i16>]>;
|
||||
def SDT_W65816CallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i16>, SDTCisVT<1, i16>]>;
|
||||
def SDT_W65816Wrapper : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>,
|
||||
SDTCisPtrTy<0>]>;
|
||||
def SDT_W65816Cmp : SDTypeProfile<0, 2, [SDTCisSameAs<0, 1>,
|
||||
SDTCisInt<0>]>;
|
||||
// (CMP allows both i16 and i8 operands.)
|
||||
def SDT_W65816BrCC : SDTypeProfile<0, 2, [SDTCisVT<0, OtherVT>,
|
||||
SDTCisVT<1, i8>]>;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// W65816-specific SDNodes
|
||||
//===----------------------------------------------------------------------===//
|
||||
def W65816retglue : SDNode<"W65816ISD::RET_GLUE", SDTNone,
|
||||
[SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
|
||||
|
||||
def W65816call : SDNode<"W65816ISD::CALL", SDT_W65816Call,
|
||||
[SDNPHasChain, SDNPOutGlue, SDNPOptInGlue,
|
||||
SDNPVariadic]>;
|
||||
|
||||
def W65816callseq_start :
|
||||
SDNode<"ISD::CALLSEQ_START", SDT_W65816CallSeqStart,
|
||||
[SDNPHasChain, SDNPOutGlue]>;
|
||||
def W65816callseq_end :
|
||||
SDNode<"ISD::CALLSEQ_END", SDT_W65816CallSeqEnd,
|
||||
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>;
|
||||
|
||||
def W65816Wrapper : SDNode<"W65816ISD::Wrapper", SDT_W65816Wrapper>;
|
||||
|
||||
// Comparison: produces a Glue value (carrying processor flags).
|
||||
def W65816cmp : SDNode<"W65816ISD::CMP", SDT_W65816Cmp, [SDNPOutGlue]>;
|
||||
// Conditional branch: takes (Chain, Dest, CC, Glue from CMP).
|
||||
def W65816brcc : SDNode<"W65816ISD::BR_CC", SDT_W65816BrCC,
|
||||
[SDNPHasChain, SDNPInGlue]>;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Pseudo Instructions
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
let Defs = [SP], Uses = [SP] in {
|
||||
def ADJCALLSTACKDOWN : W65816Pseudo<(outs),
|
||||
(ins i16imm:$amt1, i16imm:$amt2),
|
||||
"# ADJCALLSTACKDOWN $amt1 $amt2",
|
||||
[(W65816callseq_start timm:$amt1,
|
||||
timm:$amt2)]>;
|
||||
def ADJCALLSTACKUP : W65816Pseudo<(outs),
|
||||
(ins i16imm:$amt1, i16imm:$amt2),
|
||||
"# ADJCALLSTACKUP $amt1 $amt2",
|
||||
[(W65816callseq_end timm:$amt1,
|
||||
timm:$amt2)]>;
|
||||
}
|
||||
|
||||
let isReMaterializable = 1 in
|
||||
def ADDframe : W65816Pseudo<(outs PtrRegs:$dst),
|
||||
(ins i16imm:$base, i16imm:$offset),
|
||||
"# ADDframe PSEUDO", []>;
|
||||
|
||||
// The retglue node lowers directly to RTL (see Returns section below).
|
||||
// No separate RET pseudo — the real MC instruction handles the pattern.
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Codegen pseudos that expand to MC instructions in the AsmPrinter.
|
||||
//
|
||||
// These pseudos carry DAG patterns with explicit output operands so the
|
||||
// generic code generator can allocate them; the MC-layer instructions they
|
||||
// expand to have the opcode encoding but no virtual output (the result lives
|
||||
// in the implicit A register). W65816AsmPrinter::emitInstruction maps each
|
||||
// pseudo here to its real MC counterpart.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
let isAsCheapAsAMove = 1, isReMaterializable = 1,
|
||||
hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
|
||||
def LDAi16imm : W65816Pseudo<(outs Acc16:$dst), (ins i16imm:$imm),
|
||||
"# LDAi16imm $dst, $imm",
|
||||
[(set Acc16:$dst, (i16 imm:$imm))]>;
|
||||
def LDAi8imm : W65816Pseudo<(outs Acc8:$dst), (ins i8imm:$imm),
|
||||
"# LDAi8imm $dst, $imm",
|
||||
[(set Acc8:$dst, (i8 imm:$imm))]>;
|
||||
}
|
||||
|
||||
// Materialise a 16-bit address (global / external symbol) into A. Same
|
||||
// pseudo as for an immediate constant — it expands to LDA_Imm16 with the
|
||||
// symbol as the operand, which the MC encoder turns into a fixup_16.
|
||||
def : Pat<(i16 (W65816Wrapper tglobaladdr:$g)),
|
||||
(LDAi16imm tglobaladdr:$g)>;
|
||||
def : Pat<(i16 (W65816Wrapper texternalsym:$s)),
|
||||
(LDAi16imm texternalsym:$s)>;
|
||||
|
||||
// 8-bit add/sub of an immediate.
|
||||
let Constraints = "$src = $dst",
|
||||
hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
|
||||
def ADCi8imm : W65816Pseudo<(outs Acc8:$dst),
|
||||
(ins Acc8:$src, i8imm:$imm),
|
||||
"# ADCi8imm $dst, $src, $imm",
|
||||
[(set Acc8:$dst, (add Acc8:$src, imm:$imm))]>;
|
||||
def SBCi8imm : W65816Pseudo<(outs Acc8:$dst),
|
||||
(ins Acc8:$src, i8imm:$imm),
|
||||
"# SBCi8imm $dst, $src, $imm",
|
||||
[(set Acc8:$dst, (sub Acc8:$src, imm:$imm))]>;
|
||||
def ANDi8imm : W65816Pseudo<(outs Acc8:$dst),
|
||||
(ins Acc8:$src, i8imm:$imm),
|
||||
"# ANDi8imm $dst, $src, $imm",
|
||||
[(set Acc8:$dst, (and Acc8:$src, imm:$imm))]>;
|
||||
def ORAi8imm : W65816Pseudo<(outs Acc8:$dst),
|
||||
(ins Acc8:$src, i8imm:$imm),
|
||||
"# ORAi8imm $dst, $src, $imm",
|
||||
[(set Acc8:$dst, (or Acc8:$src, imm:$imm))]>;
|
||||
def EORi8imm : W65816Pseudo<(outs Acc8:$dst),
|
||||
(ins Acc8:$src, i8imm:$imm),
|
||||
"# EORi8imm $dst, $src, $imm",
|
||||
[(set Acc8:$dst, (xor Acc8:$src, imm:$imm))]>;
|
||||
}
|
||||
|
||||
// 8-bit load / store via a 16-bit absolute address.
|
||||
let mayLoad = 1, hasSideEffects = 0, mayStore = 0 in {
|
||||
def LDA8abs : W65816Pseudo<(outs Acc8:$dst), (ins i32imm:$addr),
|
||||
"# LDA8abs $dst, $addr", []>;
|
||||
}
|
||||
let mayStore = 1, hasSideEffects = 0, mayLoad = 0 in {
|
||||
def STA8abs : W65816Pseudo<(outs), (ins Acc8:$src, i32imm:$addr),
|
||||
"# STA8abs $src, $addr", []>;
|
||||
}
|
||||
def : Pat<(i8 (load (W65816Wrapper tglobaladdr:$g))),
|
||||
(LDA8abs tglobaladdr:$g)>;
|
||||
def : Pat<(i8 (load (W65816Wrapper texternalsym:$s))),
|
||||
(LDA8abs texternalsym:$s)>;
|
||||
def : Pat<(store Acc8:$src, (W65816Wrapper tglobaladdr:$g)),
|
||||
(STA8abs Acc8:$src, tglobaladdr:$g)>;
|
||||
def : Pat<(store Acc8:$src, (W65816Wrapper texternalsym:$s)),
|
||||
(STA8abs Acc8:$src, texternalsym:$s)>;
|
||||
|
||||
// Load 16 bits via a 16-bit absolute address. Currently only matches
|
||||
// loads from a Wrapper(global); direct constant-pointer loads come once
|
||||
// we add an addressing-mode complex pattern.
|
||||
let mayLoad = 1, hasSideEffects = 0, mayStore = 0 in {
|
||||
def LDAabs : W65816Pseudo<(outs Acc16:$dst), (ins i32imm:$addr),
|
||||
"# LDAabs $dst, $addr", []>;
|
||||
}
|
||||
def : Pat<(i16 (load (W65816Wrapper tglobaladdr:$g))),
|
||||
(LDAabs tglobaladdr:$g)>;
|
||||
def : Pat<(i16 (load (W65816Wrapper texternalsym:$s))),
|
||||
(LDAabs texternalsym:$s)>;
|
||||
|
||||
// Store 16 bits to a 16-bit absolute address.
|
||||
let mayStore = 1, hasSideEffects = 0, mayLoad = 0 in {
|
||||
def STAabs : W65816Pseudo<(outs), (ins Acc16:$src, i32imm:$addr),
|
||||
"# STAabs $src, $addr", []>;
|
||||
}
|
||||
def : Pat<(store Acc16:$src, (W65816Wrapper tglobaladdr:$g)),
|
||||
(STAabs Acc16:$src, tglobaladdr:$g)>;
|
||||
def : Pat<(store Acc16:$src, (W65816Wrapper texternalsym:$s)),
|
||||
(STAabs Acc16:$src, texternalsym:$s)>;
|
||||
|
||||
// 16-bit ADD: expands to CLC + ADC_Imm16. The 65816 ADC sums with the
|
||||
// carry flag, so a clean add needs CLC first. Constraints tie the
|
||||
// source and dest to A — there is only one Acc16 register so this is
|
||||
// implicit, but stating it lets the register allocator coalesce
|
||||
// without needing a COPY.
|
||||
let Constraints = "$src = $dst",
|
||||
hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
|
||||
def ADCi16imm : W65816Pseudo<(outs Acc16:$dst),
|
||||
(ins Acc16:$src, i16imm:$imm),
|
||||
"# ADCi16imm $dst, $src, $imm",
|
||||
[(set Acc16:$dst,
|
||||
(add Acc16:$src, imm:$imm))]>;
|
||||
def SBCi16imm : W65816Pseudo<(outs Acc16:$dst),
|
||||
(ins Acc16:$src, i16imm:$imm),
|
||||
"# SBCi16imm $dst, $src, $imm",
|
||||
[(set Acc16:$dst,
|
||||
(sub Acc16:$src, imm:$imm))]>;
|
||||
}
|
||||
|
||||
// ADC/SBC from a 16-bit absolute address. Folds a load on the
|
||||
// right-hand side of an add/sub into the carry-arithmetic op.
|
||||
let Constraints = "$src = $dst",
|
||||
hasSideEffects = 0, mayLoad = 1, mayStore = 0 in {
|
||||
def ADCabs : W65816Pseudo<(outs Acc16:$dst),
|
||||
(ins Acc16:$src, i32imm:$addr),
|
||||
"# ADCabs $dst, $src, $addr", []>;
|
||||
def SBCabs : W65816Pseudo<(outs Acc16:$dst),
|
||||
(ins Acc16:$src, i32imm:$addr),
|
||||
"# SBCabs $dst, $src, $addr", []>;
|
||||
}
|
||||
def : Pat<(add Acc16:$src,
|
||||
(i16 (load (W65816Wrapper tglobaladdr:$g)))),
|
||||
(ADCabs Acc16:$src, tglobaladdr:$g)>;
|
||||
def : Pat<(add Acc16:$src,
|
||||
(i16 (load (W65816Wrapper texternalsym:$s)))),
|
||||
(ADCabs Acc16:$src, texternalsym:$s)>;
|
||||
def : Pat<(sub Acc16:$src,
|
||||
(i16 (load (W65816Wrapper tglobaladdr:$g)))),
|
||||
(SBCabs Acc16:$src, tglobaladdr:$g)>;
|
||||
def : Pat<(sub Acc16:$src,
|
||||
(i16 (load (W65816Wrapper texternalsym:$s)))),
|
||||
(SBCabs Acc16:$src, texternalsym:$s)>;
|
||||
|
||||
// (add Acc16, Acc16) — same value added to itself, equivalent to a 1-bit
|
||||
// left shift. Pattern needs a tied input so the result lands in A.
|
||||
let Constraints = "$src = $dst",
|
||||
hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
|
||||
def ASLA16 : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src),
|
||||
"# ASLA16 $dst, $src",
|
||||
[(set Acc16:$dst, (add Acc16:$src, Acc16:$src))]>;
|
||||
}
|
||||
// 1-bit shift left of the accumulator: shl x, 1.
|
||||
def : Pat<(shl Acc16:$src, (i16 1)), (ASLA16 Acc16:$src)>;
|
||||
|
||||
// 1-bit logical shift right. Pseudo because the MC LSR_A has no
|
||||
// virtual output operand.
|
||||
let Constraints = "$src = $dst",
|
||||
hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
|
||||
def LSRA16 : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src),
|
||||
"# LSRA16 $dst, $src",
|
||||
[(set Acc16:$dst, (srl Acc16:$src, (i16 1)))]>;
|
||||
def ASLA8 : W65816Pseudo<(outs Acc8:$dst), (ins Acc8:$src),
|
||||
"# ASLA8 $dst, $src",
|
||||
[(set Acc8:$dst, (shl Acc8:$src, (i8 1)))]>;
|
||||
def LSRA8 : W65816Pseudo<(outs Acc8:$dst), (ins Acc8:$src),
|
||||
"# LSRA8 $dst, $src",
|
||||
[(set Acc8:$dst, (srl Acc8:$src, (i8 1)))]>;
|
||||
// Signed shift right by 1: copy A's high bit into carry, then ROR
|
||||
// to bring it back into A's high bit while halving the rest. The
|
||||
// AsmPrinter expands this to the 4-instruction PHA;ASL;PLA;ROR
|
||||
// sequence.
|
||||
def ASRA16 : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src),
|
||||
"# ASRA16 $dst, $src",
|
||||
[(set Acc16:$dst, (sra Acc16:$src, (i16 1)))]> {
|
||||
let Constraints = "$src = $dst";
|
||||
}
|
||||
}
|
||||
|
||||
// Shifts by small constants — unroll into 2-4 single-bit shifts.
|
||||
// Anything beyond 4 bits would benefit from a loop or a XBA-and-mask
|
||||
// trick; left for a future peephole.
|
||||
def : Pat<(shl Acc16:$src, (i16 2)), (ASLA16 (ASLA16 Acc16:$src))>;
|
||||
def : Pat<(shl Acc16:$src, (i16 3)),
|
||||
(ASLA16 (ASLA16 (ASLA16 Acc16:$src)))>;
|
||||
def : Pat<(shl Acc16:$src, (i16 4)),
|
||||
(ASLA16 (ASLA16 (ASLA16 (ASLA16 Acc16:$src))))>;
|
||||
|
||||
def : Pat<(srl Acc16:$src, (i16 2)), (LSRA16 (LSRA16 Acc16:$src))>;
|
||||
def : Pat<(srl Acc16:$src, (i16 3)),
|
||||
(LSRA16 (LSRA16 (LSRA16 Acc16:$src)))>;
|
||||
def : Pat<(srl Acc16:$src, (i16 4)),
|
||||
(LSRA16 (LSRA16 (LSRA16 (LSRA16 Acc16:$src))))>;
|
||||
|
||||
// Increment / decrement of A by 1. Match `(add x, 1)` and `(add x, -1)`
|
||||
// (LLVM canonicalises sub-by-1 to add-by-(-1)).
|
||||
let Constraints = "$src = $dst",
|
||||
hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
|
||||
def INA_PSEUDO : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src),
|
||||
"# INA_PSEUDO $dst, $src",
|
||||
[(set Acc16:$dst, (add Acc16:$src, (i16 1)))]>;
|
||||
def DEA_PSEUDO : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src),
|
||||
"# DEA_PSEUDO $dst, $src",
|
||||
[(set Acc16:$dst, (add Acc16:$src, (i16 -1)))]>;
|
||||
def INA_PSEUDO8 : W65816Pseudo<(outs Acc8:$dst), (ins Acc8:$src),
|
||||
"# INA_PSEUDO8 $dst, $src",
|
||||
[(set Acc8:$dst, (add Acc8:$src, (i8 1)))]>;
|
||||
def DEA_PSEUDO8 : W65816Pseudo<(outs Acc8:$dst), (ins Acc8:$src),
|
||||
"# DEA_PSEUDO8 $dst, $src",
|
||||
[(set Acc8:$dst, (add Acc8:$src, (i8 -1)))]>;
|
||||
}
|
||||
|
||||
// Two's-complement negation: `0 - x` → `EOR #$FFFF; INC A` (i.e.
|
||||
// bitwise-not then add 1). Catches (sub 0, x) which LLVM uses for
|
||||
// `-x` and the `abs` intrinsic.
|
||||
let Constraints = "$src = $dst",
|
||||
hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
|
||||
def NEGA16 : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src),
|
||||
"# NEGA16 $dst, $src",
|
||||
[(set Acc16:$dst, (sub (i16 0), Acc16:$src))]>;
|
||||
}
|
||||
|
||||
// Bitwise NOT pattern moved below EORi16imm definition.
|
||||
|
||||
// 16-bit bitwise ops: AND / OR / XOR against an immediate or memory
|
||||
// operand. Same shape as ADCi16imm / ADCabs minus the carry prefix
|
||||
// (these don't read/write the carry flag).
|
||||
let Constraints = "$src = $dst",
|
||||
hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
|
||||
def ANDi16imm : W65816Pseudo<(outs Acc16:$dst),
|
||||
(ins Acc16:$src, i16imm:$imm),
|
||||
"# ANDi16imm $dst, $src, $imm",
|
||||
[(set Acc16:$dst,
|
||||
(and Acc16:$src, imm:$imm))]>;
|
||||
def ORAi16imm : W65816Pseudo<(outs Acc16:$dst),
|
||||
(ins Acc16:$src, i16imm:$imm),
|
||||
"# ORAi16imm $dst, $src, $imm",
|
||||
[(set Acc16:$dst,
|
||||
(or Acc16:$src, imm:$imm))]>;
|
||||
def EORi16imm : W65816Pseudo<(outs Acc16:$dst),
|
||||
(ins Acc16:$src, i16imm:$imm),
|
||||
"# EORi16imm $dst, $src, $imm",
|
||||
[(set Acc16:$dst,
|
||||
(xor Acc16:$src, imm:$imm))]>;
|
||||
}
|
||||
let Constraints = "$src = $dst",
|
||||
hasSideEffects = 0, mayLoad = 1, mayStore = 0 in {
|
||||
def ANDabs : W65816Pseudo<(outs Acc16:$dst),
|
||||
(ins Acc16:$src, i32imm:$addr),
|
||||
"# ANDabs $dst, $src, $addr", []>;
|
||||
def ORAabs : W65816Pseudo<(outs Acc16:$dst),
|
||||
(ins Acc16:$src, i32imm:$addr),
|
||||
"# ORAabs $dst, $src, $addr", []>;
|
||||
def EORabs : W65816Pseudo<(outs Acc16:$dst),
|
||||
(ins Acc16:$src, i32imm:$addr),
|
||||
"# EORabs $dst, $src, $addr", []>;
|
||||
}
|
||||
def : Pat<(and Acc16:$src, (i16 (load (W65816Wrapper tglobaladdr:$g)))),
|
||||
(ANDabs Acc16:$src, tglobaladdr:$g)>;
|
||||
def : Pat<(or Acc16:$src, (i16 (load (W65816Wrapper tglobaladdr:$g)))),
|
||||
(ORAabs Acc16:$src, tglobaladdr:$g)>;
|
||||
def : Pat<(xor Acc16:$src, (i16 (load (W65816Wrapper tglobaladdr:$g)))),
|
||||
(EORabs Acc16:$src, tglobaladdr:$g)>;
|
||||
|
||||
// Bitwise NOT: x ^ 0xFFFF. LLVM lowers `~x` and i1 inversion through
|
||||
// this; emit a single EOR #$FFFF via the bitwise pseudo above.
|
||||
def : Pat<(xor Acc16:$src, (i16 -1)),
|
||||
(EORi16imm Acc16:$src, 0xFFFF)>;
|
||||
|
||||
// Frame-index loads/stores: take a FrameIndex + offset (packed into a
|
||||
// single MIOperandInfo) and expand (in eliminateFrameIndex) into an
|
||||
// LDA / STA d,S with the offset baked in. Used by LowerFormalArguments
|
||||
// to read stack-passed arguments and by spill/reload via
|
||||
// storeRegToStackSlot.
|
||||
def memfi : Operand<i16> {
|
||||
let MIOperandInfo = (ops i32imm, i32imm);
|
||||
let PrintMethod = "printFrameMem";
|
||||
}
|
||||
|
||||
let mayLoad = 1, hasSideEffects = 0, mayStore = 0 in {
|
||||
def LDAfi : W65816Pseudo<(outs Acc16:$dst), (ins memfi:$addr),
|
||||
"# LDAfi $dst, $addr", []>;
|
||||
}
|
||||
let mayStore = 1, hasSideEffects = 0, mayLoad = 0 in {
|
||||
def STAfi : W65816Pseudo<(outs),
|
||||
(ins Acc16:$src, memfi:$addr),
|
||||
"# STAfi $src, $addr", []>;
|
||||
}
|
||||
|
||||
// ComplexPattern bridging FrameIndex SDValues to memfi. See
|
||||
// SelectFrameIndex in W65816ISelDAGToDAG.cpp.
|
||||
def addr_fi : ComplexPattern<i16, 2, "SelectFrameIndex", [frameindex]>;
|
||||
|
||||
def : Pat<(i16 (load addr_fi:$addr)),
|
||||
(LDAfi addr_fi:$addr)>;
|
||||
def : Pat<(store Acc16:$src, addr_fi:$addr),
|
||||
(STAfi Acc16:$src, addr_fi:$addr)>;
|
||||
|
||||
// Frame-index folding into ADC / SBC / AND / ORA / EOR / CMP. Same
|
||||
// shape as the *abs variants but the second operand is a stack slot.
|
||||
let Constraints = "$src = $dst",
|
||||
hasSideEffects = 0, mayLoad = 1, mayStore = 0 in {
|
||||
def ADCfi : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src, memfi:$addr),
|
||||
"# ADCfi $dst, $src, $addr", []>;
|
||||
def SBCfi : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src, memfi:$addr),
|
||||
"# SBCfi $dst, $src, $addr", []>;
|
||||
def ANDfi : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src, memfi:$addr),
|
||||
"# ANDfi $dst, $src, $addr", []>;
|
||||
def ORAfi : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src, memfi:$addr),
|
||||
"# ORAfi $dst, $src, $addr", []>;
|
||||
def EORfi : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src, memfi:$addr),
|
||||
"# EORfi $dst, $src, $addr", []>;
|
||||
}
|
||||
let hasSideEffects = 0, mayLoad = 1, mayStore = 0, Defs = [P] in {
|
||||
def CMPfi : W65816Pseudo<(outs), (ins Acc16:$lhs, memfi:$addr),
|
||||
"# CMPfi $lhs, $addr", []>;
|
||||
}
|
||||
def : Pat<(add Acc16:$src, (i16 (load addr_fi:$addr))),
|
||||
(ADCfi Acc16:$src, addr_fi:$addr)>;
|
||||
def : Pat<(sub Acc16:$src, (i16 (load addr_fi:$addr))),
|
||||
(SBCfi Acc16:$src, addr_fi:$addr)>;
|
||||
def : Pat<(and Acc16:$src, (i16 (load addr_fi:$addr))),
|
||||
(ANDfi Acc16:$src, addr_fi:$addr)>;
|
||||
def : Pat<(or Acc16:$src, (i16 (load addr_fi:$addr))),
|
||||
(ORAfi Acc16:$src, addr_fi:$addr)>;
|
||||
def : Pat<(xor Acc16:$src, (i16 (load addr_fi:$addr))),
|
||||
(EORfi Acc16:$src, addr_fi:$addr)>;
|
||||
def : Pat<(W65816cmp Acc16:$lhs, (i16 (load addr_fi:$addr))),
|
||||
(CMPfi Acc16:$lhs, addr_fi:$addr)>;
|
||||
|
||||
// Zero-extending byte load: 16-bit LDA reads two bytes (the byte we want
|
||||
// plus the next byte), then mask the high byte with AND #$00FF. Reads
|
||||
// one byte past the source — fine for standalone bytes in the bank-0
|
||||
// data area but caller must ensure addr+1 is safe to read. A future
|
||||
// optimisation could use SEP/REP transitions to do a true 8-bit load.
|
||||
def : Pat<(i16 (zextloadi8 (W65816Wrapper tglobaladdr:$g))),
|
||||
(ANDi16imm (LDAabs tglobaladdr:$g), 0xFF)>;
|
||||
def : Pat<(i16 (zextloadi8 (W65816Wrapper texternalsym:$s))),
|
||||
(ANDi16imm (LDAabs texternalsym:$s), 0xFF)>;
|
||||
|
||||
// CMP / branches. CMP sets the flags via the W65816cmp SDNode (glue
|
||||
// out); the W65816brcc node consumes the glue and dispatches to the
|
||||
// right Bxx instruction by condition code.
|
||||
let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Defs = [P] in {
|
||||
def CMPi16imm : W65816Pseudo<(outs), (ins Acc16:$lhs, i16imm:$rhs),
|
||||
"# CMPi16imm $lhs, $rhs",
|
||||
[(W65816cmp Acc16:$lhs, (i16 imm:$rhs))]>;
|
||||
def CMPi8imm : W65816Pseudo<(outs), (ins Acc8:$lhs, i8imm:$rhs),
|
||||
"# CMPi8imm $lhs, $rhs",
|
||||
[(W65816cmp Acc8:$lhs, (i8 imm:$rhs))]>;
|
||||
}
|
||||
let hasSideEffects = 0, mayLoad = 1, mayStore = 0, Defs = [P] in {
|
||||
def CMPabs : W65816Pseudo<(outs), (ins Acc16:$lhs, i32imm:$addr),
|
||||
"# CMPabs $lhs, $addr", []>;
|
||||
}
|
||||
def : Pat<(W65816cmp Acc16:$lhs,
|
||||
(i16 (load (W65816Wrapper tglobaladdr:$g)))),
|
||||
(CMPabs Acc16:$lhs, tglobaladdr:$g)>;
|
||||
def : Pat<(W65816cmp Acc16:$lhs,
|
||||
(i16 (load (W65816Wrapper texternalsym:$s)))),
|
||||
(CMPabs Acc16:$lhs, texternalsym:$s)>;
|
||||
|
||||
// Two-Acc16 ops: deferred — needs proper frame setup so the register
|
||||
// allocator can spill one operand to a local stack slot. Without
|
||||
// reserved frame space, the spill goes to a negative SP offset and
|
||||
// eliminateFrameIndex bails. See SESSION_STATE §6 for the
|
||||
// dependency chain.
|
||||
|
||||
// (memory inc/dec patterns moved below INC_Abs/DEC_Abs defs.)
|
||||
|
||||
// (Branch patterns moved below the Real Instructions section since
|
||||
// they reference instruction defs.)
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Real Instructions
|
||||
//
|
||||
// Opcodes taken from the WDC W65C816S data sheet. Instructions whose size
|
||||
// depends on the M or X bits exist in two variants (Imm8 / Imm16) and carry
|
||||
// TSFlags bits indicating which processor mode they assume; the REP/SEP
|
||||
// scheduling pass uses those to verify/insert mode transitions.
|
||||
//
|
||||
// Disassembler note: for every opcode that has both an _Imm8 and an _Imm16
|
||||
// form (LDA/LDX/LDY/ADC/SBC/CMP/AND/ORA/EOR/BIT/CPX/CPY), the two forms share
|
||||
// the same opcode byte but differ in operand width according to M/X mode.
|
||||
// The scaffold disassembler only consults the default "W65816" decoder
|
||||
// table, so we push the _Imm8 variants into namespaces "W65816MHigh" /
|
||||
// "W65816XHigh". That keeps only one variant per opcode in the default
|
||||
// table (the 3-byte _Imm16 form for M-dependent insns, and the 3-byte
|
||||
// _Imm16 form for X-dependent insns), so `llvm-objdump -d` always decodes
|
||||
// these as 16-bit immediates until the mode-aware decoder lands.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//---------------------------------------------------------------- CPU control
|
||||
def NOP : InstImplied<0xEA, "nop"> {
|
||||
let mayLoad = 0; let mayStore = 0; let hasSideEffects = 0;
|
||||
}
|
||||
|
||||
def REP : InstImm8<0xC2, "rep"> {
|
||||
let hasSideEffects = 1;
|
||||
let mayLoad = 0; let mayStore = 0;
|
||||
}
|
||||
def SEP : InstImm8<0xE2, "sep"> {
|
||||
let hasSideEffects = 1;
|
||||
let mayLoad = 0; let mayStore = 0;
|
||||
}
|
||||
|
||||
def CLC : InstImplied<0x18, "clc"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def SEC : InstImplied<0x38, "sec"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def CLI : InstImplied<0x58, "cli"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def SEI : InstImplied<0x78, "sei"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def CLD : InstImplied<0xD8, "cld"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def SED : InstImplied<0xF8, "sed"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def CLV : InstImplied<0xB8, "clv"> { let mayLoad = 0; let mayStore = 0; }
|
||||
|
||||
def XCE : InstImplied<0xFB, "xce"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def XBA : InstImplied<0xEB, "xba"> { let mayLoad = 0; let mayStore = 0; }
|
||||
|
||||
def WAI : InstImplied<0xCB, "wai">;
|
||||
def STP : InstImplied<0xDB, "stp">;
|
||||
|
||||
//---------------------------------------------------------------- LDA (load A)
|
||||
// The `_Imm8` forms of the mode-dependent load/arith/compare ops are
|
||||
// marked isCodeGenOnly so the asm matcher never picks them — our
|
||||
// AsmParser has no way to know the current M/X bits, so it always
|
||||
// reaches for the _Imm16 form. Codegen can still select _Imm8
|
||||
// explicitly once we have 8-bit patterns.
|
||||
def LDA_Imm8 : InstImm8<0xA9, "lda"> { let MHigh = 1; let DecoderNamespace = "W65816MHigh"; let isCodeGenOnly = 1; }
|
||||
def LDA_Imm16 : InstImm16<0xA9, "lda"> { let MLow = 1; }
|
||||
def LDA_DP : InstDP<0xA5, "lda">;
|
||||
def LDA_Abs : InstAbs<0xAD, "lda">;
|
||||
def LDA_Long : InstAbsLong<0xAF, "lda">;
|
||||
def LDA_DPX : InstDPX<0xB5, "lda">;
|
||||
def LDA_AbsX : InstAbsX<0xBD, "lda">;
|
||||
def LDA_AbsY : InstAbsY<0xB9, "lda">;
|
||||
|
||||
//---------------------------------------------------------------- STA (store A)
|
||||
def STA_DP : InstDP<0x85, "sta">;
|
||||
def STA_Abs : InstAbs<0x8D, "sta">;
|
||||
def STA_Long : InstAbsLong<0x8F, "sta">;
|
||||
def STA_DPX : InstDPX<0x95, "sta">;
|
||||
def STA_AbsX : InstAbsX<0x9D, "sta">;
|
||||
def STA_AbsY : InstAbsY<0x99, "sta">;
|
||||
|
||||
//---------------------------------------------------------------- LDX (load X)
|
||||
def LDX_Imm8 : InstImm8<0xA2, "ldx"> { let XHigh = 1; let DecoderNamespace = "W65816XHigh"; let isCodeGenOnly = 1; }
|
||||
def LDX_Imm16 : InstImm16<0xA2, "ldx"> { let XLow = 1; }
|
||||
def LDX_DP : InstDP<0xA6, "ldx">;
|
||||
def LDX_Abs : InstAbs<0xAE, "ldx">;
|
||||
def LDX_DPY : InstDPY<0xB6, "ldx">;
|
||||
def LDX_AbsY : InstAbsY<0xBE, "ldx">;
|
||||
|
||||
//---------------------------------------------------------------- STX (store X)
|
||||
def STX_DP : InstDP<0x86, "stx">;
|
||||
def STX_Abs : InstAbs<0x8E, "stx">;
|
||||
def STX_DPY : InstDPY<0x96, "stx">;
|
||||
|
||||
//---------------------------------------------------------------- LDY (load Y)
|
||||
def LDY_Imm8 : InstImm8<0xA0, "ldy"> { let XHigh = 1; let DecoderNamespace = "W65816XHigh"; let isCodeGenOnly = 1; }
|
||||
def LDY_Imm16 : InstImm16<0xA0, "ldy"> { let XLow = 1; }
|
||||
def LDY_DP : InstDP<0xA4, "ldy">;
|
||||
def LDY_Abs : InstAbs<0xAC, "ldy">;
|
||||
def LDY_DPX : InstDPX<0xB4, "ldy">;
|
||||
def LDY_AbsX : InstAbsX<0xBC, "ldy">;
|
||||
|
||||
//---------------------------------------------------------------- STY (store Y)
|
||||
def STY_DP : InstDP<0x84, "sty">;
|
||||
def STY_Abs : InstAbs<0x8C, "sty">;
|
||||
def STY_DPX : InstDPX<0x94, "sty">;
|
||||
|
||||
//------------------------------------------------------------------------- ADC
|
||||
def ADC_Imm8 : InstImm8<0x69, "adc"> { let MHigh = 1; let DecoderNamespace = "W65816MHigh"; let isCodeGenOnly = 1; }
|
||||
def ADC_Imm16 : InstImm16<0x69, "adc"> { let MLow = 1; }
|
||||
def ADC_DP : InstDP<0x65, "adc">;
|
||||
def ADC_Abs : InstAbs<0x6D, "adc">;
|
||||
def ADC_DPX : InstDPX<0x75, "adc">;
|
||||
def ADC_AbsX : InstAbsX<0x7D, "adc">;
|
||||
def ADC_AbsY : InstAbsY<0x79, "adc">;
|
||||
|
||||
//------------------------------------------------------------------------- SBC
|
||||
def SBC_Imm8 : InstImm8<0xE9, "sbc"> { let MHigh = 1; let DecoderNamespace = "W65816MHigh"; let isCodeGenOnly = 1; }
|
||||
def SBC_Imm16 : InstImm16<0xE9, "sbc"> { let MLow = 1; }
|
||||
def SBC_DP : InstDP<0xE5, "sbc">;
|
||||
def SBC_Abs : InstAbs<0xED, "sbc">;
|
||||
def SBC_DPX : InstDPX<0xF5, "sbc">;
|
||||
def SBC_AbsX : InstAbsX<0xFD, "sbc">;
|
||||
def SBC_AbsY : InstAbsY<0xF9, "sbc">;
|
||||
|
||||
//------------------------------------------------------------------------- CMP
|
||||
def CMP_Imm8 : InstImm8<0xC9, "cmp"> { let MHigh = 1; let mayLoad=0; let mayStore=0; let DecoderNamespace = "W65816MHigh"; let isCodeGenOnly = 1; }
|
||||
def CMP_Imm16 : InstImm16<0xC9, "cmp"> { let MLow = 1; let mayLoad=0; let mayStore=0; }
|
||||
def CMP_DP : InstDP<0xC5, "cmp"> { let mayStore = 0; }
|
||||
def CMP_Abs : InstAbs<0xCD, "cmp"> { let mayStore = 0; }
|
||||
def CMP_DPX : InstDPX<0xD5, "cmp"> { let mayStore = 0; }
|
||||
def CMP_AbsX : InstAbsX<0xDD, "cmp"> { let mayStore = 0; }
|
||||
def CMP_AbsY : InstAbsY<0xD9, "cmp"> { let mayStore = 0; }
|
||||
|
||||
//---------------------------------------------------------------- CPX/CPY
|
||||
def CPX_Imm8 : InstImm8<0xE0, "cpx"> { let XHigh = 1; let mayLoad=0; let mayStore=0; let DecoderNamespace = "W65816XHigh"; let isCodeGenOnly = 1; }
|
||||
def CPX_Imm16 : InstImm16<0xE0, "cpx"> { let XLow = 1; let mayLoad=0; let mayStore=0; }
|
||||
def CPX_DP : InstDP<0xE4, "cpx"> { let mayStore = 0; }
|
||||
def CPX_Abs : InstAbs<0xEC, "cpx"> { let mayStore = 0; }
|
||||
def CPY_Imm8 : InstImm8<0xC0, "cpy"> { let XHigh = 1; let mayLoad=0; let mayStore=0; let DecoderNamespace = "W65816XHigh"; let isCodeGenOnly = 1; }
|
||||
def CPY_Imm16 : InstImm16<0xC0, "cpy"> { let XLow = 1; let mayLoad=0; let mayStore=0; }
|
||||
def CPY_DP : InstDP<0xC4, "cpy"> { let mayStore = 0; }
|
||||
def CPY_Abs : InstAbs<0xCC, "cpy"> { let mayStore = 0; }
|
||||
|
||||
//---------------------------------------------------------------- AND/ORA/EOR
|
||||
def AND_Imm8 : InstImm8<0x29, "and"> { let MHigh = 1; let mayLoad=0; let mayStore=0; let DecoderNamespace = "W65816MHigh"; let isCodeGenOnly = 1; }
|
||||
def AND_Imm16 : InstImm16<0x29, "and"> { let MLow = 1; let mayLoad=0; let mayStore=0; }
|
||||
def AND_DP : InstDP<0x25, "and"> { let mayStore = 0; }
|
||||
def AND_Abs : InstAbs<0x2D, "and"> { let mayStore = 0; }
|
||||
|
||||
def ORA_Imm8 : InstImm8<0x09, "ora"> { let MHigh = 1; let mayLoad=0; let mayStore=0; let DecoderNamespace = "W65816MHigh"; let isCodeGenOnly = 1; }
|
||||
def ORA_Imm16 : InstImm16<0x09, "ora"> { let MLow = 1; let mayLoad=0; let mayStore=0; }
|
||||
def ORA_DP : InstDP<0x05, "ora"> { let mayStore = 0; }
|
||||
def ORA_Abs : InstAbs<0x0D, "ora"> { let mayStore = 0; }
|
||||
|
||||
def EOR_Imm8 : InstImm8<0x49, "eor"> { let MHigh = 1; let mayLoad=0; let mayStore=0; let DecoderNamespace = "W65816MHigh"; let isCodeGenOnly = 1; }
|
||||
def EOR_Imm16 : InstImm16<0x49, "eor"> { let MLow = 1; let mayLoad=0; let mayStore=0; }
|
||||
def EOR_DP : InstDP<0x45, "eor"> { let mayStore = 0; }
|
||||
def EOR_Abs : InstAbs<0x4D, "eor"> { let mayStore = 0; }
|
||||
|
||||
def BIT_Imm8 : InstImm8<0x89, "bit"> { let MHigh = 1; let mayLoad=0; let mayStore=0; let DecoderNamespace = "W65816MHigh"; let isCodeGenOnly = 1; }
|
||||
def BIT_Imm16 : InstImm16<0x89, "bit"> { let MLow = 1; let mayLoad=0; let mayStore=0; }
|
||||
def BIT_DP : InstDP<0x24, "bit"> { let mayStore = 0; }
|
||||
def BIT_Abs : InstAbs<0x2C, "bit"> { let mayStore = 0; }
|
||||
|
||||
//---------------------------------------------------------------- INC/DEC
|
||||
def INA : InstImplied<0x1A, "inc a"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def DEA : InstImplied<0x3A, "dec a"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def INX : InstImplied<0xE8, "inx"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def DEX : InstImplied<0xCA, "dex"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def INY : InstImplied<0xC8, "iny"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def DEY : InstImplied<0x88, "dey"> { let mayLoad = 0; let mayStore = 0; }
|
||||
|
||||
def INC_DP : InstDP<0xE6, "inc">;
|
||||
def INC_Abs : InstAbs<0xEE, "inc">;
|
||||
def INC_DPX : InstDPX<0xF6, "inc">;
|
||||
def INC_AbsX: InstAbsX<0xFE, "inc">;
|
||||
|
||||
def DEC_DP : InstDP<0xC6, "dec">;
|
||||
def DEC_Abs : InstAbs<0xCE, "dec">;
|
||||
def DEC_DPX : InstDPX<0xD6, "dec">;
|
||||
def DEC_AbsX: InstAbsX<0xDE, "dec">;
|
||||
|
||||
//---------------------------------------------------------------- Shifts
|
||||
def ASL_A : InstImplied<0x0A, "asl a"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def LSR_A : InstImplied<0x4A, "lsr a"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def ROL_A : InstImplied<0x2A, "rol a"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def ROR_A : InstImplied<0x6A, "ror a"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def ASL_DP : InstDP<0x06, "asl">;
|
||||
def ASL_Abs : InstAbs<0x0E, "asl">;
|
||||
def LSR_DP : InstDP<0x46, "lsr">;
|
||||
def LSR_Abs : InstAbs<0x4E, "lsr">;
|
||||
def ROL_DP : InstDP<0x26, "rol">;
|
||||
def ROL_Abs : InstAbs<0x2E, "rol">;
|
||||
def ROR_DP : InstDP<0x66, "ror">;
|
||||
def ROR_Abs : InstAbs<0x6E, "ror">;
|
||||
|
||||
//---------------------------------------------------------------- Transfers
|
||||
def TAX : InstImplied<0xAA, "tax"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def TAY : InstImplied<0xA8, "tay"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def TXA : InstImplied<0x8A, "txa"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def TYA : InstImplied<0x98, "tya"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def TXY : InstImplied<0x9B, "txy"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def TYX : InstImplied<0xBB, "tyx"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def TXS : InstImplied<0x9A, "txs"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def TSX : InstImplied<0xBA, "tsx"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def TCD : InstImplied<0x5B, "tcd"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def TDC : InstImplied<0x7B, "tdc"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def TCS : InstImplied<0x1B, "tcs"> { let mayLoad = 0; let mayStore = 0; }
|
||||
def TSC : InstImplied<0x3B, "tsc"> { let mayLoad = 0; let mayStore = 0; }
|
||||
|
||||
//---------------------------------------------------------------- Stack push/pull
|
||||
def PHA : InstImplied<0x48, "pha">;
|
||||
def PLA : InstImplied<0x68, "pla">;
|
||||
def PHX : InstImplied<0xDA, "phx">;
|
||||
def PLX : InstImplied<0xFA, "plx">;
|
||||
def PHY : InstImplied<0x5A, "phy">;
|
||||
def PLY : InstImplied<0x7A, "ply">;
|
||||
def PHP : InstImplied<0x08, "php">;
|
||||
def PLP : InstImplied<0x28, "plp">;
|
||||
def PHB : InstImplied<0x8B, "phb">;
|
||||
def PLB : InstImplied<0xAB, "plb">;
|
||||
def PHD : InstImplied<0x0B, "phd">;
|
||||
def PLD : InstImplied<0x2B, "pld">;
|
||||
def PHK : InstImplied<0x4B, "phk">;
|
||||
def PEA : InstAbs<0xF4, "pea">;
|
||||
def PER : InstPCRel16<0x62, "per">;
|
||||
|
||||
//---------------------------------------------------------------- Branches
|
||||
let isBranch = 1, isTerminator = 1, mayLoad = 0, mayStore = 0 in {
|
||||
def BEQ : InstPCRel8<0xF0, "beq">;
|
||||
def BNE : InstPCRel8<0xD0, "bne">;
|
||||
def BCS : InstPCRel8<0xB0, "bcs">;
|
||||
def BCC : InstPCRel8<0x90, "bcc">;
|
||||
def BMI : InstPCRel8<0x30, "bmi">;
|
||||
def BPL : InstPCRel8<0x10, "bpl">;
|
||||
def BVS : InstPCRel8<0x70, "bvs">;
|
||||
def BVC : InstPCRel8<0x50, "bvc">;
|
||||
}
|
||||
|
||||
let isBranch = 1, isTerminator = 1, isBarrier = 1, mayLoad = 0, mayStore = 0 in {
|
||||
def BRA : InstPCRel8<0x80, "bra">;
|
||||
def BRL : InstPCRel16<0x82, "brl">;
|
||||
def JMP_Abs : InstAbs<0x4C, "jmp">;
|
||||
def JML_Long : InstAbsLong<0x5C, "jml">;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------- Calls
|
||||
let isCall = 1, mayLoad = 0, mayStore = 0 in {
|
||||
def JSR_Abs : InstAbs<0x20, "jsr">;
|
||||
def JSL_Long : InstAbsLong<0x22, "jsl">;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------- Returns
|
||||
let isReturn = 1, isTerminator = 1, isBarrier = 1, mayLoad = 0, mayStore = 0 in {
|
||||
def RTS : InstImplied<0x60, "rts">;
|
||||
def RTI : InstImplied<0x40, "rti">;
|
||||
// RTL is the 65816 long return; we select it for the generic retglue node.
|
||||
def RTL : InstImplied<0x6B, "rtl"> {
|
||||
let Pattern = [(W65816retglue)];
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------- Block move
|
||||
// MVN/MVP are 3 bytes: opcode + destBank + srcBank. WDC writes the
|
||||
// operand order as "dst, src" but the bytes on the wire are dst-then-src.
|
||||
// Block-move operands are bank bytes written without a '#' prefix
|
||||
// (e.g. `mvn $01, $02`), so the parser produces AddrDP-kind operands,
|
||||
// not immediates. Use addrDP here to match that; the encoder path is
|
||||
// identical since both are single-byte values.
|
||||
class InstBlockMove<bits<8> op, string mnem>
|
||||
: W65816Inst<(outs), (ins addrDP:$dst, addrDP:$src),
|
||||
!strconcat(mnem, "\t$dst, $src")> {
|
||||
let Size = 3;
|
||||
bits<8> dst;
|
||||
bits<8> src;
|
||||
bits<24> Inst;
|
||||
let Inst{7-0} = op;
|
||||
let Inst{15-8} = dst;
|
||||
let Inst{23-16} = src;
|
||||
}
|
||||
|
||||
def MVN : InstBlockMove<0x54, "mvn">;
|
||||
def MVP : InstBlockMove<0x44, "mvp">;
|
||||
|
||||
//---------------------------------------------------------------- Stack-rel
|
||||
def LDA_StackRel : InstStackRel<0xA3, "lda">;
|
||||
def STA_StackRel : InstStackRel<0x83, "sta">;
|
||||
def ADC_StackRel : InstStackRel<0x63, "adc">;
|
||||
def SBC_StackRel : InstStackRel<0xE3, "sbc">;
|
||||
def CMP_StackRel : InstStackRel<0xC3, "cmp">;
|
||||
def AND_StackRel : InstStackRel<0x23, "and">;
|
||||
def ORA_StackRel : InstStackRel<0x03, "ora">;
|
||||
def EOR_StackRel : InstStackRel<0x43, "eor">;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Branch patterns (placed after the Bxx defs).
|
||||
//
|
||||
// W65816brcc takes (Dest, CondCode) plus a glue from W65816cmp. The CC
|
||||
// constant maps to one of the eight Bxx instructions. Values mirror
|
||||
// W65816CC::CondCode in W65816.h.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def : Pat<(W65816brcc bb:$dest, (i8 0)), (BEQ bb:$dest)>;
|
||||
def : Pat<(W65816brcc bb:$dest, (i8 1)), (BNE bb:$dest)>;
|
||||
def : Pat<(W65816brcc bb:$dest, (i8 2)), (BCS bb:$dest)>;
|
||||
def : Pat<(W65816brcc bb:$dest, (i8 3)), (BCC bb:$dest)>;
|
||||
def : Pat<(W65816brcc bb:$dest, (i8 4)), (BMI bb:$dest)>;
|
||||
def : Pat<(W65816brcc bb:$dest, (i8 5)), (BPL bb:$dest)>;
|
||||
def : Pat<(W65816brcc bb:$dest, (i8 6)), (BVS bb:$dest)>;
|
||||
def : Pat<(W65816brcc bb:$dest, (i8 7)), (BVC bb:$dest)>;
|
||||
|
||||
// Unconditional branch from generic ISD::BR.
|
||||
def : Pat<(br bb:$dest), (BRA bb:$dest)>;
|
||||
|
||||
// Memory inc/dec: `*p = *p + 1` → `INC abs`. Single-instruction RMW
|
||||
// instead of LDA → CLC → ADC #1 → STA.
|
||||
def : Pat<(store
|
||||
(i16 (add (i16 (load (W65816Wrapper tglobaladdr:$g))),
|
||||
(i16 1))),
|
||||
(W65816Wrapper tglobaladdr:$g)),
|
||||
(INC_Abs tglobaladdr:$g)>;
|
||||
def : Pat<(store
|
||||
(i16 (add (i16 (load (W65816Wrapper tglobaladdr:$g))),
|
||||
(i16 -1))),
|
||||
(W65816Wrapper tglobaladdr:$g)),
|
||||
(DEC_Abs tglobaladdr:$g)>;
|
||||
|
||||
// Direct call to a global / external symbol. We use JSL (24-bit
|
||||
// long jump-and-link) and RTL pairing throughout — matches the
|
||||
// IIgs convention where main is entered via JSL, and means a
|
||||
// function doesn't have to know how it was called to choose its
|
||||
// return instruction. A pseudo bridges the i16 symbol operand
|
||||
// to JSL_Long's 24-bit operand class.
|
||||
let isCall = 1, hasSideEffects = 0, mayLoad = 0, mayStore = 0,
|
||||
Defs = [A] in {
|
||||
def JSLpseudo : W65816Pseudo<(outs), (ins i16imm:$dst),
|
||||
"# JSLpseudo $dst", []>;
|
||||
}
|
||||
def : Pat<(W65816call (i16 tglobaladdr:$dst)), (JSLpseudo tglobaladdr:$dst)>;
|
||||
def : Pat<(W65816call (i16 texternalsym:$dst)), (JSLpseudo texternalsym:$dst)>;
|
||||
120
src/llvm/lib/Target/W65816/W65816MCInstLower.cpp
Normal file
120
src/llvm/lib/Target/W65816/W65816MCInstLower.cpp
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
//===-- W65816MCInstLower.cpp - Convert W65816 MachineInstr to MCInst -----===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains code to lower W65816 MachineInstrs to their
|
||||
// corresponding MCInst records. The skeleton follows the MSP430 pattern
|
||||
// closely but does not yet understand any target-specific operand flags.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816MCInstLower.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/CodeGen/AsmPrinter.h"
|
||||
#include "llvm/CodeGen/MachineBasicBlock.h"
|
||||
#include "llvm/CodeGen/MachineInstr.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include "llvm/MC/MCContext.h"
|
||||
#include "llvm/MC/MCExpr.h"
|
||||
#include "llvm/MC/MCInst.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
MCSymbol *
|
||||
W65816MCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
|
||||
assert(MO.getTargetFlags() == 0 && "unknown target flag on GV operand");
|
||||
return Printer.getSymbol(MO.getGlobal());
|
||||
}
|
||||
|
||||
MCSymbol *
|
||||
W65816MCInstLower::GetExternalSymbolSymbol(const MachineOperand &MO) const {
|
||||
assert(MO.getTargetFlags() == 0 && "unknown target flag on ES operand");
|
||||
return Printer.GetExternalSymbolSymbol(MO.getSymbolName());
|
||||
}
|
||||
|
||||
MCSymbol *W65816MCInstLower::GetJumpTableSymbol(const MachineOperand &MO) const {
|
||||
const DataLayout &DL = Printer.getDataLayout();
|
||||
SmallString<256> Name;
|
||||
raw_svector_ostream(Name)
|
||||
<< DL.getInternalSymbolPrefix() << "JTI" << Printer.getFunctionNumber()
|
||||
<< '_' << MO.getIndex();
|
||||
assert(MO.getTargetFlags() == 0 && "unknown target flag on JT operand");
|
||||
return Ctx.getOrCreateSymbol(Name);
|
||||
}
|
||||
|
||||
MCSymbol *
|
||||
W65816MCInstLower::GetConstantPoolIndexSymbol(const MachineOperand &MO) const {
|
||||
const DataLayout &DL = Printer.getDataLayout();
|
||||
SmallString<256> Name;
|
||||
raw_svector_ostream(Name)
|
||||
<< DL.getInternalSymbolPrefix() << "CPI" << Printer.getFunctionNumber()
|
||||
<< '_' << MO.getIndex();
|
||||
assert(MO.getTargetFlags() == 0 && "unknown target flag on CPI operand");
|
||||
return Ctx.getOrCreateSymbol(Name);
|
||||
}
|
||||
|
||||
MCSymbol *
|
||||
W65816MCInstLower::GetBlockAddressSymbol(const MachineOperand &MO) const {
|
||||
assert(MO.getTargetFlags() == 0 && "unknown target flag on BA operand");
|
||||
return Printer.GetBlockAddressSymbol(MO.getBlockAddress());
|
||||
}
|
||||
|
||||
MCOperand W65816MCInstLower::LowerSymbolOperand(const MachineOperand &MO,
|
||||
MCSymbol *Sym) const {
|
||||
const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx);
|
||||
if (!MO.isJTI() && MO.getOffset())
|
||||
Expr = MCBinaryExpr::createAdd(
|
||||
Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
|
||||
return MCOperand::createExpr(Expr);
|
||||
}
|
||||
|
||||
void W65816MCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const {
|
||||
OutMI.setOpcode(MI->getOpcode());
|
||||
|
||||
for (const MachineOperand &MO : MI->operands()) {
|
||||
MCOperand MCOp;
|
||||
switch (MO.getType()) {
|
||||
default:
|
||||
MI->print(errs());
|
||||
llvm_unreachable("W65816: unknown operand type in MCInstLower");
|
||||
case MachineOperand::MO_Register:
|
||||
if (MO.isImplicit())
|
||||
continue;
|
||||
MCOp = MCOperand::createReg(MO.getReg());
|
||||
break;
|
||||
case MachineOperand::MO_Immediate:
|
||||
MCOp = MCOperand::createImm(MO.getImm());
|
||||
break;
|
||||
case MachineOperand::MO_MachineBasicBlock:
|
||||
MCOp = MCOperand::createExpr(
|
||||
MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), Ctx));
|
||||
break;
|
||||
case MachineOperand::MO_GlobalAddress:
|
||||
MCOp = LowerSymbolOperand(MO, GetGlobalAddressSymbol(MO));
|
||||
break;
|
||||
case MachineOperand::MO_ExternalSymbol:
|
||||
MCOp = LowerSymbolOperand(MO, GetExternalSymbolSymbol(MO));
|
||||
break;
|
||||
case MachineOperand::MO_JumpTableIndex:
|
||||
MCOp = LowerSymbolOperand(MO, GetJumpTableSymbol(MO));
|
||||
break;
|
||||
case MachineOperand::MO_ConstantPoolIndex:
|
||||
MCOp = LowerSymbolOperand(MO, GetConstantPoolIndexSymbol(MO));
|
||||
break;
|
||||
case MachineOperand::MO_BlockAddress:
|
||||
MCOp = LowerSymbolOperand(MO, GetBlockAddressSymbol(MO));
|
||||
break;
|
||||
case MachineOperand::MO_RegisterMask:
|
||||
continue;
|
||||
}
|
||||
|
||||
OutMI.addOperand(MCOp);
|
||||
}
|
||||
}
|
||||
46
src/llvm/lib/Target/W65816/W65816MCInstLower.h
Normal file
46
src/llvm/lib/Target/W65816/W65816MCInstLower.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
//===-- W65816MCInstLower.h - Lower MachineInstr to MCInst ------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_W65816MCINSTLOWER_H
|
||||
#define LLVM_LIB_TARGET_W65816_W65816MCINSTLOWER_H
|
||||
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class AsmPrinter;
|
||||
class MCContext;
|
||||
class MCInst;
|
||||
class MCOperand;
|
||||
class MCSymbol;
|
||||
class MachineInstr;
|
||||
class MachineOperand;
|
||||
|
||||
/// W65816MCInstLower - Lower a MachineInstr into an MCInst.
|
||||
class LLVM_LIBRARY_VISIBILITY W65816MCInstLower {
|
||||
MCContext &Ctx;
|
||||
AsmPrinter &Printer;
|
||||
|
||||
public:
|
||||
W65816MCInstLower(MCContext &Ctx, AsmPrinter &Printer)
|
||||
: Ctx(Ctx), Printer(Printer) {}
|
||||
|
||||
void Lower(const MachineInstr *MI, MCInst &OutMI) const;
|
||||
|
||||
MCOperand LowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const;
|
||||
|
||||
MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const;
|
||||
MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const;
|
||||
MCSymbol *GetJumpTableSymbol(const MachineOperand &MO) const;
|
||||
MCSymbol *GetConstantPoolIndexSymbol(const MachineOperand &MO) const;
|
||||
MCSymbol *GetBlockAddressSymbol(const MachineOperand &MO) const;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
20
src/llvm/lib/Target/W65816/W65816MachineFunctionInfo.cpp
Normal file
20
src/llvm/lib/Target/W65816/W65816MachineFunctionInfo.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//===-- W65816MachineFunctionInfo.cpp - W65816 machine function info ------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816MachineFunctionInfo.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
void W65816MachineFunctionInfo::anchor() {}
|
||||
|
||||
MachineFunctionInfo *W65816MachineFunctionInfo::clone(
|
||||
BumpPtrAllocator &Allocator, MachineFunction &DestMF,
|
||||
const DenseMap<MachineBasicBlock *, MachineBasicBlock *> &Src2DstMBB)
|
||||
const {
|
||||
return DestMF.cloneInfo<W65816MachineFunctionInfo>(*this);
|
||||
}
|
||||
63
src/llvm/lib/Target/W65816/W65816MachineFunctionInfo.h
Normal file
63
src/llvm/lib/Target/W65816/W65816MachineFunctionInfo.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
//=== W65816MachineFunctionInfo.h - W65816 machine function info -*- C++ -*-==//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file declares W65816-specific per-machine-function information.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_W65816MACHINEFUNCTIONINFO_H
|
||||
#define LLVM_LIB_TARGET_W65816_W65816MACHINEFUNCTIONINFO_H
|
||||
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// W65816MachineFunctionInfo - per-MachineFunction private target-specific
|
||||
/// information for the W65816.
|
||||
class W65816MachineFunctionInfo : public MachineFunctionInfo {
|
||||
virtual void anchor();
|
||||
|
||||
/// Size of the callee-saved register portion of the stack frame in bytes.
|
||||
unsigned CalleeSavedFrameSize = 0;
|
||||
|
||||
/// FrameIndex for the return-address slot.
|
||||
int ReturnAddrIndex = 0;
|
||||
|
||||
/// FrameIndex for the start of the varargs area.
|
||||
int VarArgsFrameIndex = 0;
|
||||
|
||||
/// Virtual register holding the struct-return pointer for sret returns.
|
||||
Register SRetReturnReg;
|
||||
|
||||
public:
|
||||
W65816MachineFunctionInfo() = default;
|
||||
|
||||
W65816MachineFunctionInfo(const Function &F, const TargetSubtargetInfo *STI) {
|
||||
}
|
||||
|
||||
MachineFunctionInfo *
|
||||
clone(BumpPtrAllocator &Allocator, MachineFunction &DestMF,
|
||||
const DenseMap<MachineBasicBlock *, MachineBasicBlock *> &Src2DstMBB)
|
||||
const override;
|
||||
|
||||
unsigned getCalleeSavedFrameSize() const { return CalleeSavedFrameSize; }
|
||||
void setCalleeSavedFrameSize(unsigned Bytes) { CalleeSavedFrameSize = Bytes; }
|
||||
|
||||
Register getSRetReturnReg() const { return SRetReturnReg; }
|
||||
void setSRetReturnReg(Register Reg) { SRetReturnReg = Reg; }
|
||||
|
||||
int getRAIndex() const { return ReturnAddrIndex; }
|
||||
void setRAIndex(int Index) { ReturnAddrIndex = Index; }
|
||||
|
||||
int getVarArgsFrameIndex() const { return VarArgsFrameIndex; }
|
||||
void setVarArgsFrameIndex(int Index) { VarArgsFrameIndex = Index; }
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
119
src/llvm/lib/Target/W65816/W65816RegisterInfo.cpp
Normal file
119
src/llvm/lib/Target/W65816/W65816RegisterInfo.cpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
//===-- W65816RegisterInfo.cpp - W65816 Register Information --------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Skeleton implementation of the W65816 register info. The callee-saved
|
||||
// register list, reserved set and frame-index elimination logic are
|
||||
// deliberately minimal; they will be fleshed out alongside frame lowering.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816RegisterInfo.h"
|
||||
#include "W65816.h"
|
||||
#include "W65816FrameLowering.h"
|
||||
#include "W65816InstrInfo.h"
|
||||
#include "W65816Subtarget.h"
|
||||
#include "llvm/ADT/BitVector.h"
|
||||
#include "llvm/CodeGen/MachineFrameInfo.h"
|
||||
#include "llvm/CodeGen/MachineFunction.h"
|
||||
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "w65816-reg-info"
|
||||
|
||||
#define GET_REGINFO_TARGET_DESC
|
||||
#include "W65816GenRegisterInfo.inc"
|
||||
|
||||
W65816RegisterInfo::W65816RegisterInfo() : W65816GenRegisterInfo(W65816::PC) {}
|
||||
|
||||
const MCPhysReg *
|
||||
W65816RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
|
||||
// The 65816 C calling convention preserves DP and DBR across calls.
|
||||
static const MCPhysReg CalleeSavedRegs[] = {W65816::DP, W65816::DBR, 0};
|
||||
return CalleeSavedRegs;
|
||||
}
|
||||
|
||||
BitVector W65816RegisterInfo::getReservedRegs(const MachineFunction &MF) const {
|
||||
BitVector Reserved(getNumRegs());
|
||||
|
||||
// SP, PC, P, PBR and DBR are all special-purpose registers the allocator
|
||||
// must never pick. DP is allocatable in principle but is treated as
|
||||
// reserved for the skeleton until direct-page management lands.
|
||||
Reserved.set(W65816::SP);
|
||||
Reserved.set(W65816::PC);
|
||||
Reserved.set(W65816::P);
|
||||
Reserved.set(W65816::PBR);
|
||||
Reserved.set(W65816::DBR);
|
||||
Reserved.set(W65816::DP);
|
||||
|
||||
return Reserved;
|
||||
}
|
||||
|
||||
const TargetRegisterClass *
|
||||
W65816RegisterInfo::getPointerRegClass(unsigned Kind) const {
|
||||
return &W65816::PtrRegsRegClass;
|
||||
}
|
||||
|
||||
bool W65816RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
|
||||
int SPAdj, unsigned FIOperandNum,
|
||||
RegScavenger *RS) const {
|
||||
MachineInstr &MI = *II;
|
||||
MachineFunction &MF = *MI.getParent()->getParent();
|
||||
const MachineFrameInfo &MFI = MF.getFrameInfo();
|
||||
const W65816InstrInfo &TII = *MF.getSubtarget<W65816Subtarget>().getInstrInfo();
|
||||
|
||||
unsigned Opc = MI.getOpcode();
|
||||
unsigned NewOpc = 0;
|
||||
bool NeedsCarryPrefix = false;
|
||||
bool IsSub = false;
|
||||
switch (Opc) {
|
||||
case W65816::LDAfi: NewOpc = W65816::LDA_StackRel; break;
|
||||
case W65816::STAfi: NewOpc = W65816::STA_StackRel; break;
|
||||
case W65816::ADCfi: NewOpc = W65816::ADC_StackRel; NeedsCarryPrefix = true; break;
|
||||
case W65816::SBCfi: NewOpc = W65816::SBC_StackRel; NeedsCarryPrefix = true; IsSub = true; break;
|
||||
case W65816::ANDfi: NewOpc = W65816::AND_StackRel; break;
|
||||
case W65816::ORAfi: NewOpc = W65816::ORA_StackRel; break;
|
||||
case W65816::EORfi: NewOpc = W65816::EOR_StackRel; break;
|
||||
case W65816::CMPfi: NewOpc = W65816::CMP_StackRel; break;
|
||||
default:
|
||||
llvm_unreachable("W65816: unhandled instruction in eliminateFrameIndex");
|
||||
}
|
||||
|
||||
int FI = MI.getOperand(FIOperandNum).getIndex();
|
||||
int FrameOffset = MFI.getObjectOffset(FI);
|
||||
int ImmOffset = MI.getOperand(FIOperandNum + 1).getImm();
|
||||
|
||||
// WDC stack-relative addressing: `LDA disp,S` computes effective
|
||||
// address S + disp. Both fixed objects (args) and local objects
|
||||
// are stored at addresses relative to entry-SP; my prologue has
|
||||
// shifted S down by StackSize. So:
|
||||
// address = entry_S + FrameOffset
|
||||
// S = entry_S - StackSize
|
||||
// disp = address - S
|
||||
// = FrameOffset + StackSize
|
||||
int Offset = FrameOffset + ImmOffset + (int)MFI.getStackSize();
|
||||
|
||||
if (Offset < 0 || Offset > 0xFF) {
|
||||
report_fatal_error("W65816: frame offset out of stack-relative range");
|
||||
}
|
||||
|
||||
// Emit the carry-prep instruction first if the operation needs it.
|
||||
if (NeedsCarryPrefix) {
|
||||
BuildMI(*MI.getParent(), II, MI.getDebugLoc(),
|
||||
TII.get(IsSub ? W65816::SEC : W65816::CLC));
|
||||
}
|
||||
BuildMI(*MI.getParent(), II, MI.getDebugLoc(), TII.get(NewOpc))
|
||||
.addImm(Offset);
|
||||
MI.eraseFromParent();
|
||||
return true;
|
||||
}
|
||||
|
||||
Register W65816RegisterInfo::getFrameRegister(const MachineFunction &MF) const {
|
||||
return W65816::SP;
|
||||
}
|
||||
43
src/llvm/lib/Target/W65816/W65816RegisterInfo.h
Normal file
43
src/llvm/lib/Target/W65816/W65816RegisterInfo.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//===-- W65816RegisterInfo.h - W65816 Register Information Impl -*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains the W65816 implementation of the TargetRegisterInfo
|
||||
// class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_W65816REGISTERINFO_H
|
||||
#define LLVM_LIB_TARGET_W65816_W65816REGISTERINFO_H
|
||||
|
||||
#include "llvm/CodeGen/TargetRegisterInfo.h"
|
||||
|
||||
#define GET_REGINFO_HEADER
|
||||
#include "W65816GenRegisterInfo.inc"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class W65816RegisterInfo : public W65816GenRegisterInfo {
|
||||
public:
|
||||
W65816RegisterInfo();
|
||||
|
||||
const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override;
|
||||
|
||||
BitVector getReservedRegs(const MachineFunction &MF) const override;
|
||||
const TargetRegisterClass *
|
||||
getPointerRegClass(unsigned Kind = 0) const override;
|
||||
|
||||
bool eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj,
|
||||
unsigned FIOperandNum,
|
||||
RegScavenger *RS = nullptr) const override;
|
||||
|
||||
Register getFrameRegister(const MachineFunction &MF) const override;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
61
src/llvm/lib/Target/W65816/W65816RegisterInfo.td
Normal file
61
src/llvm/lib/Target/W65816/W65816RegisterInfo.td
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//===-- W65816RegisterInfo.td - W65816 Register defs -------*- tablegen -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Declarations that describe the W65816 register file
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
class W65816Reg<bits<4> num, string n> : Register<n> {
|
||||
field bits<4> Num = num;
|
||||
let Namespace = "W65816";
|
||||
let HWEncoding{3-0} = num;
|
||||
let DwarfNumbers = [num];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Registers
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The 65816 registers are variable-width: A, X and Y are each 8 or 16 bits
|
||||
// wide depending on the M and X bits in the processor status register. For
|
||||
// the skeleton we model each physical register once and create parallel 8-bit
|
||||
// and 16-bit register classes that share the same physical register. A later
|
||||
// pass is responsible for managing REP/SEP transitions and verifying that the
|
||||
// selected width matches the current processor mode.
|
||||
//
|
||||
def A : W65816Reg<0, "a">, DwarfRegNum<[0]>;
|
||||
def X : W65816Reg<1, "x">, DwarfRegNum<[1]>;
|
||||
def Y : W65816Reg<2, "y">, DwarfRegNum<[2]>;
|
||||
def SP : W65816Reg<3, "sp">, DwarfRegNum<[3]>;
|
||||
def DP : W65816Reg<4, "dp">, DwarfRegNum<[4]>;
|
||||
def DBR : W65816Reg<5, "dbr">, DwarfRegNum<[5]>;
|
||||
def PBR : W65816Reg<6, "pbr">, DwarfRegNum<[6]>;
|
||||
def PC : W65816Reg<7, "pc">, DwarfRegNum<[7]>;
|
||||
def P : W65816Reg<8, "p">, DwarfRegNum<[8]>;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Register Classes
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Acc8/Acc16 hold the accumulator A in 8-bit and 16-bit mode respectively.
|
||||
// Idx8/Idx16 hold the index registers X and Y in 8-bit and 16-bit mode.
|
||||
// PtrRegs holds the stack pointer for framing and pointer arithmetic; DP is
|
||||
// reserved and not allocatable.
|
||||
|
||||
def Acc8 : RegisterClass<"W65816", [i8], 8, (add A)>;
|
||||
def Acc16 : RegisterClass<"W65816", [i16], 16, (add A)>;
|
||||
def Idx8 : RegisterClass<"W65816", [i8], 8, (add X, Y)>;
|
||||
def Idx16 : RegisterClass<"W65816", [i16], 16, (add X, Y)>;
|
||||
|
||||
def PtrRegs : RegisterClass<"W65816", [i16], 16, (add SP)>;
|
||||
|
||||
// Single-register class for the processor status register, used for condition
|
||||
// code modeling. Not currently allocatable.
|
||||
def StatusReg : RegisterClass<"W65816", [i8], 8, (add P)> {
|
||||
let isAllocatable = 0;
|
||||
}
|
||||
19
src/llvm/lib/Target/W65816/W65816SelectionDAGInfo.cpp
Normal file
19
src/llvm/lib/Target/W65816/W65816SelectionDAGInfo.cpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//===-- W65816SelectionDAGInfo.cpp - W65816 SelectionDAG Info -------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816SelectionDAGInfo.h"
|
||||
|
||||
#define GET_SDNODE_DESC
|
||||
#include "W65816GenSDNodeInfo.inc"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
W65816SelectionDAGInfo::W65816SelectionDAGInfo()
|
||||
: SelectionDAGGenTargetInfo(W65816GenSDNodeInfo) {}
|
||||
|
||||
W65816SelectionDAGInfo::~W65816SelectionDAGInfo() = default;
|
||||
27
src/llvm/lib/Target/W65816/W65816SelectionDAGInfo.h
Normal file
27
src/llvm/lib/Target/W65816/W65816SelectionDAGInfo.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//===-- W65816SelectionDAGInfo.h - W65816 SelectionDAG Info -----*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_W65816SELECTIONDAGINFO_H
|
||||
#define LLVM_LIB_TARGET_W65816_W65816SELECTIONDAGINFO_H
|
||||
|
||||
#include "llvm/CodeGen/SelectionDAGTargetInfo.h"
|
||||
|
||||
#define GET_SDNODE_ENUM
|
||||
#include "W65816GenSDNodeInfo.inc"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class W65816SelectionDAGInfo : public SelectionDAGGenTargetInfo {
|
||||
public:
|
||||
W65816SelectionDAGInfo();
|
||||
~W65816SelectionDAGInfo() override;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
49
src/llvm/lib/Target/W65816/W65816Subtarget.cpp
Normal file
49
src/llvm/lib/Target/W65816/W65816Subtarget.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
//===-- W65816Subtarget.cpp - W65816 Subtarget Information ----------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the W65816 specific subclass of TargetSubtargetInfo.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816Subtarget.h"
|
||||
#include "W65816SelectionDAGInfo.h"
|
||||
#include "llvm/MC/TargetRegistry.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
#define DEBUG_TYPE "w65816-subtarget"
|
||||
|
||||
#define GET_SUBTARGETINFO_TARGET_DESC
|
||||
#define GET_SUBTARGETINFO_CTOR
|
||||
#include "W65816GenSubtargetInfo.inc"
|
||||
|
||||
void W65816Subtarget::anchor() {}
|
||||
|
||||
W65816Subtarget &
|
||||
W65816Subtarget::initializeSubtargetDependencies(StringRef CPU, StringRef FS) {
|
||||
StringRef CPUName = CPU;
|
||||
if (CPUName.empty())
|
||||
CPUName = "w65816";
|
||||
|
||||
ParseSubtargetFeatures(CPUName, /*TuneCPU=*/CPUName, FS);
|
||||
return *this;
|
||||
}
|
||||
|
||||
W65816Subtarget::W65816Subtarget(const Triple &TT, const std::string &CPU,
|
||||
const std::string &FS, const TargetMachine &TM)
|
||||
: W65816GenSubtargetInfo(TT, CPU, /*TuneCPU=*/CPU, FS),
|
||||
InstrInfo(initializeSubtargetDependencies(CPU, FS)), TLInfo(TM, *this),
|
||||
FrameLowering(*this) {
|
||||
TSInfo = std::make_unique<W65816SelectionDAGInfo>();
|
||||
}
|
||||
|
||||
W65816Subtarget::~W65816Subtarget() = default;
|
||||
|
||||
const SelectionDAGTargetInfo *W65816Subtarget::getSelectionDAGInfo() const {
|
||||
return TSInfo.get();
|
||||
}
|
||||
67
src/llvm/lib/Target/W65816/W65816Subtarget.h
Normal file
67
src/llvm/lib/Target/W65816/W65816Subtarget.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
//===-- W65816Subtarget.h - Define Subtarget for the W65816 ----*- C++ -*--===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file declares the W65816 specific subclass of TargetSubtargetInfo.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_W65816SUBTARGET_H
|
||||
#define LLVM_LIB_TARGET_W65816_W65816SUBTARGET_H
|
||||
|
||||
#include "W65816FrameLowering.h"
|
||||
#include "W65816ISelLowering.h"
|
||||
#include "W65816InstrInfo.h"
|
||||
#include "W65816RegisterInfo.h"
|
||||
#include "llvm/CodeGen/TargetSubtargetInfo.h"
|
||||
#include "llvm/IR/DataLayout.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#define GET_SUBTARGETINFO_HEADER
|
||||
#include "W65816GenSubtargetInfo.inc"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class StringRef;
|
||||
|
||||
class W65816Subtarget : public W65816GenSubtargetInfo {
|
||||
virtual void anchor();
|
||||
|
||||
W65816InstrInfo InstrInfo;
|
||||
W65816TargetLowering TLInfo;
|
||||
std::unique_ptr<const SelectionDAGTargetInfo> TSInfo;
|
||||
W65816FrameLowering FrameLowering;
|
||||
|
||||
public:
|
||||
W65816Subtarget(const Triple &TT, const std::string &CPU,
|
||||
const std::string &FS, const TargetMachine &TM);
|
||||
|
||||
~W65816Subtarget() override;
|
||||
|
||||
W65816Subtarget &initializeSubtargetDependencies(StringRef CPU, StringRef FS);
|
||||
|
||||
/// Parses features string setting specified subtarget options. Generated
|
||||
/// by TableGen.
|
||||
void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS);
|
||||
|
||||
const TargetFrameLowering *getFrameLowering() const override {
|
||||
return &FrameLowering;
|
||||
}
|
||||
const W65816InstrInfo *getInstrInfo() const override { return &InstrInfo; }
|
||||
const W65816RegisterInfo *getRegisterInfo() const override {
|
||||
return &getInstrInfo()->getRegisterInfo();
|
||||
}
|
||||
const W65816TargetLowering *getTargetLowering() const override {
|
||||
return &TLInfo;
|
||||
}
|
||||
const SelectionDAGTargetInfo *getSelectionDAGInfo() const override;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
95
src/llvm/lib/Target/W65816/W65816TargetMachine.cpp
Normal file
95
src/llvm/lib/Target/W65816/W65816TargetMachine.cpp
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
//===-- W65816TargetMachine.cpp - Define TargetMachine for W65816 ---------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Top-level implementation for the W65816 target.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "W65816TargetMachine.h"
|
||||
#include "W65816.h"
|
||||
#include "W65816MachineFunctionInfo.h"
|
||||
#include "TargetInfo/W65816TargetInfo.h"
|
||||
#include "llvm/CodeGen/Passes.h"
|
||||
#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
|
||||
#include "llvm/CodeGen/TargetPassConfig.h"
|
||||
#include "llvm/MC/TargetRegistry.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include <optional>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
// Data layout for the 65816 lives in Triple::computeDataLayout via
|
||||
// patches/0005-target-data-layout-w65816.patch. The string is:
|
||||
// e - little endian
|
||||
// m:e - ELF-style symbol mangling
|
||||
// p:16:8 - 16-bit pointers, 8-bit stack alignment
|
||||
// i16:16 - 16-bit integers aligned to 16 bits
|
||||
// i32:16 - 32-bit integers aligned to 16 bits
|
||||
// n8:16 - native integer widths
|
||||
// S16 - 16-bit natural stack alignment
|
||||
|
||||
extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
|
||||
LLVMInitializeW65816Target() {
|
||||
RegisterTargetMachine<W65816TargetMachine> X(getTheW65816Target());
|
||||
PassRegistry &PR = *PassRegistry::getPassRegistry();
|
||||
initializeW65816AsmPrinterPass(PR);
|
||||
initializeW65816DAGToDAGISelLegacyPass(PR);
|
||||
}
|
||||
|
||||
static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM) {
|
||||
return RM.value_or(Reloc::Static);
|
||||
}
|
||||
|
||||
W65816TargetMachine::W65816TargetMachine(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, TT.computeDataLayout(), TT, CPU, FS, Options,
|
||||
getEffectiveRelocModel(RM),
|
||||
getEffectiveCodeModel(CM, CodeModel::Small), OL),
|
||||
TLOF(std::make_unique<TargetLoweringObjectFileELF>()),
|
||||
Subtarget(TT, std::string(CPU), std::string(FS), *this) {
|
||||
initAsmInfo();
|
||||
}
|
||||
|
||||
W65816TargetMachine::~W65816TargetMachine() = default;
|
||||
|
||||
namespace {
|
||||
|
||||
/// W65816 Code Generator Pass Configuration Options.
|
||||
class W65816PassConfig : public TargetPassConfig {
|
||||
public:
|
||||
W65816PassConfig(W65816TargetMachine &TM, PassManagerBase &PM)
|
||||
: TargetPassConfig(TM, PM) {}
|
||||
|
||||
W65816TargetMachine &getW65816TargetMachine() const {
|
||||
return getTM<W65816TargetMachine>();
|
||||
}
|
||||
|
||||
bool addInstSelector() override;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TargetPassConfig *W65816TargetMachine::createPassConfig(PassManagerBase &PM) {
|
||||
return new W65816PassConfig(*this, PM);
|
||||
}
|
||||
|
||||
MachineFunctionInfo *W65816TargetMachine::createMachineFunctionInfo(
|
||||
BumpPtrAllocator &Allocator, const Function &F,
|
||||
const TargetSubtargetInfo *STI) const {
|
||||
return W65816MachineFunctionInfo::create<W65816MachineFunctionInfo>(Allocator,
|
||||
F, STI);
|
||||
}
|
||||
|
||||
bool W65816PassConfig::addInstSelector() {
|
||||
addPass(createW65816ISelDag(getW65816TargetMachine(), getOptLevel()));
|
||||
return false;
|
||||
}
|
||||
53
src/llvm/lib/Target/W65816/W65816TargetMachine.h
Normal file
53
src/llvm/lib/Target/W65816/W65816TargetMachine.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
//===-- W65816TargetMachine.h - Define TargetMachine for W65816 -*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file declares the W65816 specific subclass of TargetMachine.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIB_TARGET_W65816_W65816TARGETMACHINE_H
|
||||
#define LLVM_LIB_TARGET_W65816_W65816TARGETMACHINE_H
|
||||
|
||||
#include "W65816Subtarget.h"
|
||||
#include "llvm/CodeGen/CodeGenTargetMachineImpl.h"
|
||||
#include <optional>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class StringRef;
|
||||
|
||||
class W65816TargetMachine : public CodeGenTargetMachineImpl {
|
||||
std::unique_ptr<TargetLoweringObjectFile> TLOF;
|
||||
W65816Subtarget Subtarget;
|
||||
|
||||
public:
|
||||
W65816TargetMachine(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);
|
||||
~W65816TargetMachine() override;
|
||||
|
||||
const W65816Subtarget *getSubtargetImpl(const Function &F) const override {
|
||||
return &Subtarget;
|
||||
}
|
||||
|
||||
TargetPassConfig *createPassConfig(PassManagerBase &PM) override;
|
||||
|
||||
TargetLoweringObjectFile *getObjFileLowering() const override {
|
||||
return TLOF.get();
|
||||
}
|
||||
|
||||
MachineFunctionInfo *
|
||||
createMachineFunctionInfo(BumpPtrAllocator &Allocator, const Function &F,
|
||||
const TargetSubtargetInfo *STI) const override;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Reference in a new issue