//===-- 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 &Ins, const SDLoc &DL, SelectionDAG &DAG, SmallVectorImpl &InVals) const override; SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI, SmallVectorImpl &InVals) const override; SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, const SmallVectorImpl &Outs, const SmallVectorImpl &OutVals, const SDLoc &DL, SelectionDAG &DAG) const override; SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; // Lock i16 shift amounts to i16 (not i32) even when i32 is a legal // type. Without this, the DAG combiner promotes i16 shift amounts // to i32 once i32 is registered as legal, leaving (sra i16, i32:K) // with no matching pattern. Only narrow when LHS is i16; leave i32 // shifts (which go to libcall via LowerShift) alone. MVT getScalarShiftAmountTy(const DataLayout &DL, EVT LHSTy) const override { if (LHSTy == MVT::i16 || LHSTy == MVT::i8) return MVT::i16; return TargetLoweringBase::getScalarShiftAmountTy(DL, LHSTy); } // ptr32-mode hook: with patches/0007-targetlowering-virtual- // gettypeconversion making the base function virtual, this can be // overridden to force i64 to expand directly to i16 halves rather // than going through i32 (the next-smaller-legal type). Currently // not overridden — the override-calling-base passthrough caused // regressions in unrelated functions (likely due to subtle // de-virtualization changes when the function becomes virtual). // Future fix needs to test the override more carefully. MachineBasicBlock * EmitInstrWithCustomInserter(MachineInstr &MI, MachineBasicBlock *MBB) 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; } // Disable LLVM's magic-constant expansion of sdiv/srem by power-of-2. // The default expansion generates BUILD_VECTOR (used as a "splat shifter" // intermediate) which we can't lower; without an override, every sdiv/srem // by a pow2 constant crashes ISel. Returning the original node leaves it // intact for the libcall lowering path (SDIV/SREM are LibCall in our // ctor — see setOperationAction calls above). SDValue BuildSDIVPow2(SDNode *N, const APInt &Divisor, SelectionDAG &DAG, SmallVectorImpl &Created) const override { return SDValue(N, 0); } SDValue BuildSREMPow2(SDNode *N, const APInt &Divisor, SelectionDAG &DAG, SmallVectorImpl &Created) const override { return SDValue(N, 0); } SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override; // Inline-asm register constraints. Supports: // "a" / "{a}" — accumulator (A) — Acc16 (or Acc8 for i8 type) // "x" / "{x}" — index X — Idx16 (or Idx8) // "y" / "{y}" — index Y — Idx16 (or Idx8) // "r" — any allocatable register — Acc16 by default // Letting users name A/X/Y opens up direct toolbox-call sequences, // hand-written math kernels, and any other place where the back-end // doesn't already know to use a particular reg. std::pair getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const override; // Classify single-letter constraints 'a','x','y' as register-class // constraints so SelectionDAGBuilder routes them to the resolver // above rather than reporting "unknown asm constraint." ConstraintType getConstraintType(StringRef Constraint) const override { if (Constraint.size() == 1) { switch (Constraint[0]) { case 'a': case 'x': case 'y': case 'r': return C_RegisterClass; default: break; } } return TargetLowering::getConstraintType(Constraint); } // Force i32 / i64 shifts through a libcall (__ashlsi3 / __lshrsi3 / // __ashrsi3) instead of LLVM's default ExpandToParts strategy, which // emits an SHL_PARTS node we have no pattern for. ExpandToParts also // produces a long select-based sequence; the libcall is both smaller // and matches our existing libcall-based approach for i16 mul/div. ShiftLegalizationStrategy preferredShiftLegalizationStrategy(SelectionDAG &DAG, SDNode *N, unsigned ExpansionFactor) const override { if (N->getValueType(0).getSizeInBits() > 16) return ShiftLegalizationStrategy::LowerToLibcall; return TargetLowering::preferredShiftLegalizationStrategy(DAG, N, ExpansionFactor); } // i16 MUL goes through __mulhi3 libcall. Tell the DAG combiner that // decomposing a constant multiply into shifts and adds is profitable: // a libcall is ~12 instructions, while `(mul x, 3)` -> `(add x, (shl // x, 1))` is 5. i32 stays libcall — the per-half shift+add+chain // expansion comes out larger than the __mulsi3 call. bool decomposeMulByConstant(LLVMContext &Context, EVT VT, SDValue C) const override { return VT == MVT::i16; } // The DAG combiner has a transform `(trunc (shl X, K)) -> (shl (trunc X), K)` // gated on `isTypeDesirableForOp(SHL, NarrowVT)`. Our LowerShift expands // i8 SHL/SRL/SRA to `(trunc (shift (zext X), K))`; the combiner then // narrows it back to `(shift X, K)` of i8, which re-enters LowerShift — // an infinite loop that hangs `unsigned char x << 1` at -O1/-O2. // Return false for shifts on i8 to disable that narrowing combine and // keep the operation in i16 once we've widened it. bool isTypeDesirableForOp(unsigned Opc, EVT VT) const override { if (VT == MVT::i8 && (Opc == ISD::SHL || Opc == ISD::SRL || Opc == ISD::SRA)) return false; return TargetLowering::isTypeDesirableForOp(Opc, VT); } // Disallow merging stores into wider ones. With ptr32 active and i32 // a Custom-lowered op, the SDAG combiner's MergeConsecutiveStores // takes our LowerStore-split pair (2x i16 stores at &t and &t+2) and // merges them back into a single i32 store, which re-enters // LowerStore, splits again, and loops forever — observed as // "LLVM ERROR: out of memory" on `*t = K` for any K (including 0 // when the SDAG state lets the combiner pick the merge ahead of any // STZ-pattern simplification). Anything wider than i16 has no // legal ptr-store pattern in our backend anyway, so merging into // wider VTs is purely counterproductive. bool canMergeStoresTo(unsigned AS, EVT MemVT, const MachineFunction &MF) const override { if (MemVT.isInteger() && MemVT.getSizeInBits() > 16) return false; return TargetLowering::canMergeStoresTo(AS, MemVT, MF); } private: SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBRIND(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSETCC(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const; SDValue LowerSignExtend(SDValue Op, SelectionDAG &DAG) const; SDValue LowerShift(SDValue Op, SelectionDAG &DAG) const; SDValue LowerDynamicStackalloc(SDValue Op, SelectionDAG &DAG) const; // Foundation hooks for ptr32 mode. In ptr16 mode (current default), // both return SDValue() so the legalizer falls through to the default // i16-pointer LDAptr/STAptr selection. When ptr32 mode is enabled // (PointerWidth=32 + Wide32 added as i32 reg class), they detect i32 // addresses and wrap the load/store in W65816ISD::LD_PTR / ST_PTR / // STB_PTR so the [dp],Y inserter takes the bank byte from the // pointer's hi half instead of forcing 0. SDValue LowerLoad(SDValue Op, SelectionDAG &DAG) const; SDValue LowerStore(SDValue Op, SelectionDAG &DAG) const; // ZERO/SIGN/ANY_EXTEND i16 -> i32 and TRUNCATE i32 -> i16 lowering // via REG_SEQUENCE / EXTRACT_SUBREG on the sub_lo/sub_hi indexes of // the Wide32 register class. Active once i32 is registered as a // legal type. SDValue LowerExtend(SDValue Op, SelectionDAG &DAG) const; SDValue LowerTruncate(SDValue Op, SelectionDAG &DAG) const; // SIGN_EXTEND_INREG i32 with inner type i1 / i8 / i16: sign-extend // the low N bits of the i32 input to fill all 32 bits. Splits to // (sext_inreg lo, innerVT) for the low half and SRA #15 of the // resulting i16 for the high half. SDValue LowerSignExtendInReg(SDValue Op, SelectionDAG &DAG) const; // ADD/SUB/AND/OR/XOR i32 split into per-half i16 ops. The carry- // chain ADDC/ADDE pseudos handle the cross-half link for ADD/SUB. SDValue LowerI32Bin(SDValue Op, SelectionDAG &DAG) const; // i32 ConstantNode: split into two i16 constants and REG_SEQUENCE. SDValue LowerI32Constant(SDValue Op, SelectionDAG &DAG) const; // i32 MUL: detect (zext i16 a) * (zext i16 b) — or operands with // provably-zero high 16 bits — and emit __umulhisi3 (16x16 -> 32) // instead of __mulsi3 (32x32 -> 32). Cuts ~30% off the canonical // sumSquares-style loop. SDValue LowerMUL_I32(SDValue Op, SelectionDAG &DAG) const; }; } // namespace llvm #endif