65816-llvm-mos/src/llvm/lib/Target/W65816/W65816ISelDAGToDAG.cpp
Scott Duensing 465f8ba947 Checkpoint
2026-05-05 22:00:34 -05:00

153 lines
6.1 KiB
C++

//===-- 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;
}
// Custom selection: bare FrameIndex SDValue used as a pointer value
// (e.g. `&arr[0]` for a stack-allocated array). The auto-generated
// selector has no pattern for `(i16 frameindex)` because tablegen
// doesn't expose FrameIndex as a leaf type — so ISel fails with
// "Cannot select: FrameIndex" before ever reaching a load/store-
// context fold. Convert to ADDframe (FI, 0); the frame-index
// elimination pass turns ADDframe into TSC + CLC + ADC #(offset +
// stackSize), producing SP+offset in A.
//
// ptr32 mode: a `(i32 frameindex)` is `&local` typed as a 32-bit
// pointer (bank+addr). Lower as REG_SEQUENCE(ADDframe, sub_lo, 0,
// sub_hi). Hi=0 reflects the program-bank assumption (stack lives
// in bank 0 for our crt0 startup). Without this, ISel hits
// "Cannot select: t# = FrameIndex<N>" and the pass crashes —
// observed for softDouble's __adddf3 calling dclass(a, &sa, &ea,
// &ma) where the latter three become i32 frameindex SDValues.
if (Node->getOpcode() == ISD::FrameIndex) {
SDLoc DL(Node);
int FI = cast<FrameIndexSDNode>(Node)->getIndex();
EVT VT = Node->getValueType(0);
SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::i16);
SDValue Zero16 = CurDAG->getTargetConstant(0, DL, MVT::i16);
if (VT == MVT::i16) {
CurDAG->SelectNodeTo(Node, W65816::ADDframe, MVT::i16, TFI, Zero16);
return;
}
if (VT == MVT::i32) {
// Build (REG_SEQUENCE Wide32RC, ADDframe(FI,0), sub_lo, MOVi16(0),
// sub_hi). ADDframe materialises lo as an i16 SDValue; the hi
// half is the literal bank byte (0).
SDNode *Lo = CurDAG->getMachineNode(W65816::ADDframe, DL,
MVT::i16, TFI, Zero16);
SDValue HiC = CurDAG->getTargetConstant(0, DL, MVT::i16);
// For the high half, just materialise an i16 zero via MOVi16imm.
SDNode *Hi = CurDAG->getMachineNode(W65816::LDAi16imm, DL,
MVT::i16, HiC);
SDValue RC = CurDAG->getTargetConstant(W65816::Wide32RegClassID,
DL, MVT::i32);
SDValue SubLo = CurDAG->getTargetConstant(llvm::sub_lo, DL, MVT::i32);
SDValue SubHi = CurDAG->getTargetConstant(llvm::sub_hi, DL, MVT::i32);
CurDAG->SelectNodeTo(Node, TargetOpcode::REG_SEQUENCE, MVT::i32,
{RC, SDValue(Lo, 0), SubLo, SDValue(Hi, 0),
SubHi});
return;
}
report_fatal_error("W65816: FrameIndex selection: unsupported VT");
}
// Defer to the auto-generated selector for everything else.
SelectCode(Node);
}
bool W65816DAGToDAGISel::SelectFrameIndex(SDValue N, SDValue &Base,
SDValue &Offset) {
// Bare FrameIndex: offset 0.
if (auto *FIN = dyn_cast<FrameIndexSDNode>(N)) {
Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i16);
Offset = CurDAG->getTargetConstant(0, SDLoc(N), MVT::i16);
return true;
}
// (add FrameIndex, const): fold the const into the memfi offset.
// Type legalization emits this shape when splitting a multi-byte
// load/store at a stack slot into multiple smaller loads (e.g. an
// i32 spill becomes two i16 loads, with the high load at FI+2).
// Without this, the bare FrameIndex inside the add is left as an
// unmatched i16 leaf and ISel reports "Cannot select FrameIndex".
if (N.getOpcode() == ISD::ADD) {
SDValue LHS = N.getOperand(0);
SDValue RHS = N.getOperand(1);
if (auto *FIN = dyn_cast<FrameIndexSDNode>(LHS)) {
if (auto *CN = dyn_cast<ConstantSDNode>(RHS)) {
Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i16);
Offset = CurDAG->getTargetConstant(CN->getSExtValue(),
SDLoc(N), MVT::i16);
return true;
}
}
}
return false;
}