65816-llvm-mos/src/llvm/lib/Target/W65816/W65816RegisterInfo.td
2026-10-17 15:40:15 -05:00

197 lines
9.9 KiB
TableGen
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- 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<8> num, string n> : Register<n> {
field bits<8> Num = num;
let Namespace = "W65816";
let HWEncoding{7-0} = num;
let DwarfNumbers = [num];
}
// SubRegIndices for synthetic 32-bit register pairs. sub_lo addresses the
// low 16 bits (the natural i16-aligned half), sub_hi the high 16 bits.
// Used by Acc32 / Wide32 / AnyWide32 to model i32 (i.e. ptr32) values as
// pairs of i16 physical registers.
def sub_lo : SubRegIndex<16, 0>;
def sub_hi : SubRegIndex<16, 16>;
//===----------------------------------------------------------------------===//
// 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]>;
// Imaginary 16-bit registers backed by direct-page slots $C0..$DE
// (16 slots = 32 DP bytes). The regalloc treats them as physical
// registers with cheap LDA/STA dp inter-register moves. This
// relieves pressure on the single Acc16 register (A) so greedy
// regalloc can succeed on functions with multiple simultaneously-
// live i16 vregs. IMG0..IMG7 are caller-save: a call clobbers them,
// so regalloc spills around any call. IMG8..IMG15 are callee-saved:
// W65816ImgCalleeSave saves and restores them in any function that
// writes them, and callers keep values in them across calls, so hand
// asm called from C must not overwrite them without doing the same.
// Their HWEncoding is never emitted (asmprinter translates IMGn
// references into LDA/STA dp with the right address).
//
// Layout: IMG0..IMG7 at $D0..$DE (legacy slot block); IMG8..IMG15
// at $C0..$CE. Avoid stepping on user DP allocations below $C0.
def IMG0 : W65816Reg<16, "img0">, DwarfRegNum<[16]>;
def IMG1 : W65816Reg<17, "img1">, DwarfRegNum<[17]>;
def IMG2 : W65816Reg<18, "img2">, DwarfRegNum<[18]>;
def IMG3 : W65816Reg<19, "img3">, DwarfRegNum<[19]>;
def IMG4 : W65816Reg<20, "img4">, DwarfRegNum<[20]>;
def IMG5 : W65816Reg<21, "img5">, DwarfRegNum<[21]>;
def IMG6 : W65816Reg<22, "img6">, DwarfRegNum<[22]>;
def IMG7 : W65816Reg<23, "img7">, DwarfRegNum<[23]>;
def IMG8 : W65816Reg<32, "img8">, DwarfRegNum<[32]>;
def IMG9 : W65816Reg<33, "img9">, DwarfRegNum<[33]>;
def IMG10 : W65816Reg<34, "img10">, DwarfRegNum<[34]>;
def IMG11 : W65816Reg<35, "img11">, DwarfRegNum<[35]>;
def IMG12 : W65816Reg<36, "img12">, DwarfRegNum<[36]>;
def IMG13 : W65816Reg<37, "img13">, DwarfRegNum<[37]>;
def IMG14 : W65816Reg<38, "img14">, DwarfRegNum<[38]>;
def IMG15 : W65816Reg<39, "img15">, DwarfRegNum<[39]>;
// DPF0 — pseudo-physreg modeling the i16 storage at DP $F0..$F1.
// Used as the carrier for the highest 16 bits of an i64/double
// return. JSLpseudo Defs DPF0 so the SDAG combiner / scheduler
// can't merge or reorder reads of it across calls; we plumb the
// 4th return half via CopyFromReg(DPF0) in LowerCall, which lowers
// to `LDA $F0` via copyPhysReg. Never allocated to a vreg —
// always a transient bridge from DP[$F0] to A.
def DPF0 : W65816Reg<24, "dpf0">, DwarfRegNum<[24]>;
//===----------------------------------------------------------------------===//
// 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)>;
// Imaginary i16 registers backed by DP slots $D0..$DE. Vregs in this
// class lower to LDA/STA dp on cross-class moves to A (4 cyc each
// way). Used by ABridgeViaX (and future regalloc-pressure passes) as
// an alternative parking spot to stack spills. Caller-save: a callee
// may freely overwrite $D0..$DF, so the allocator must spill IMGn
// vregs around any call.
def Img16 : RegisterClass<"W65816", [i16], 16,
(add IMG0, IMG1, IMG2, IMG3,
IMG4, IMG5, IMG6, IMG7,
IMG8, IMG9, IMG10, IMG11,
IMG12, IMG13, IMG14, IMG15)>;
// Acc-or-IMG combined class. Vregs that are not constrained to A
// (i.e., not the source of an arithmetic op) get widened to this
// class pre-RA so greedy regalloc can pick A or any IMGn. Listing
// A first so the allocator's default order prefers A; cross-class
// moves to/from A are LDA/STA dp via copyPhysReg.
def Wide16 : RegisterClass<"W65816", [i16], 16,
(add A, IMG0, IMG1, IMG2, IMG3,
IMG4, IMG5, IMG6, IMG7,
IMG8, IMG9, IMG10, IMG11,
IMG12, IMG13, IMG14, IMG15)>;
def PtrRegs : RegisterClass<"W65816", [i16], 16, (add SP)>;
// Single-register class for DPF0, the i64-return high-half carrier.
// Not allocatable — only used as a CopyFromReg source in LowerCall;
// copyPhysReg lowers DPF0 → A by emitting `LDA $F0`.
def DPF0Reg : RegisterClass<"W65816", [i16], 16, (add DPF0)> {
let isAllocatable = 0;
}
// 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;
}
//===----------------------------------------------------------------------===//
// Synthetic 32-bit Register Pairs (for ptr32 mode)
//===----------------------------------------------------------------------===//
//
// The W65816 has no native 32-bit registers. For 32-bit-pointer mode and
// any other i32 traffic we synthesize register pairs whose halves are
// existing i16 registers, accessed via sub_lo / sub_hi.
//
// AX32 pairs A:X for the calling-convention slot (first i32 arg/return).
// Heterogeneous: sub_lo is in Acc16, sub_hi is in Idx16. Because of the
// heterogeneity, AX32 lives in its own single-element class (Acc32) — if
// it were grouped with the homogeneous IMG pairs in Wide32, TableGen would
// auto-derive a "wide32_with_sub_hi_in_idx8" subclass that pins the whole
// class to AX32.
//
// IMG01..IMG1415 pair adjacent IMG slots (each pair is 4 bytes of DP) into
// homogeneous i16-i16 pairs. These hold ptr32 values backed entirely by
// direct page, so register-pair allocation can spill cleanly via Img16's
// existing rules.
//
// Acc32 / Wide32 / AnyWide32:
// Acc32 = {AX32} — calling-convention slot only; not for general allocation.
// Wide32 = {IMG01..IMG1415} — homogeneous i16-i16 pairs, freely allocatable.
// AnyWide32 = Acc32 ∪ Wide32 — pre-RA flexibility for ptr32 vregs that
// are not constrained to AX32; greedy regalloc can pick AX32 or any
// Wide32 pair.
let SubRegIndices = [sub_lo, sub_hi], CoveredBySubRegs = 1 in {
def AX32 : RegisterWithSubRegs<"ax32", [A, X]>,
DwarfRegNum<[40]> { let Namespace = "W65816"; }
def IMG01 : RegisterWithSubRegs<"img01", [IMG0, IMG1]>,
DwarfRegNum<[41]> { let Namespace = "W65816"; }
def IMG23 : RegisterWithSubRegs<"img23", [IMG2, IMG3]>,
DwarfRegNum<[42]> { let Namespace = "W65816"; }
def IMG45 : RegisterWithSubRegs<"img45", [IMG4, IMG5]>,
DwarfRegNum<[43]> { let Namespace = "W65816"; }
def IMG67 : RegisterWithSubRegs<"img67", [IMG6, IMG7]>,
DwarfRegNum<[44]> { let Namespace = "W65816"; }
def IMG89 : RegisterWithSubRegs<"img89", [IMG8, IMG9]>,
DwarfRegNum<[45]> { let Namespace = "W65816"; }
def IMG1011 : RegisterWithSubRegs<"img1011", [IMG10, IMG11]>,
DwarfRegNum<[46]> { let Namespace = "W65816"; }
def IMG1213 : RegisterWithSubRegs<"img1213", [IMG12, IMG13]>,
DwarfRegNum<[47]> { let Namespace = "W65816"; }
def IMG1415 : RegisterWithSubRegs<"img1415", [IMG14, IMG15]>,
DwarfRegNum<[48]> { let Namespace = "W65816"; }
}
def Acc32 : RegisterClass<"W65816", [i32], 16, (add AX32)>;
def Wide32 : RegisterClass<"W65816", [i32], 16,
(add IMG01, IMG23, IMG45, IMG67,
IMG89, IMG1011, IMG1213, IMG1415)>;
def AnyWide32 : RegisterClass<"W65816", [i32], 16,
(add AX32,
IMG01, IMG23, IMG45, IMG67,
IMG89, IMG1011, IMG1213, IMG1415)>;