65816-llvm-mos/src/llvm/lib/Target/W65816/W65816RegisterInfo.td
2026-04-30 18:49:00 -05:00

113 lines
5.4 KiB
TableGen

//===-- 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];
}
//===----------------------------------------------------------------------===//
// 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 $D0..$DE.
// 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. Caller-save: callees may
// freely overwrite them, so regalloc spills around any call that
// might touch them. Their HWEncoding is never emitted (asmprinter
// translates IMGn references into LDA/STA dp with the right address).
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]>;
// 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)>;
// 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)>;
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;
}