153 lines
6.1 KiB
C++
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;
|
|
}
|