163 lines
7.3 KiB
C++
163 lines
7.3 KiB
C++
//===-- 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<ISD::InputArg> &Ins,
|
|
const SDLoc &DL, SelectionDAG &DAG,
|
|
SmallVectorImpl<SDValue> &InVals) const override;
|
|
|
|
SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
|
SmallVectorImpl<SDValue> &InVals) const override;
|
|
|
|
SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
|
|
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
|
const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
|
|
SelectionDAG &DAG) const override;
|
|
|
|
SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override;
|
|
|
|
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<SDNode *> &Created) const override {
|
|
return SDValue(N, 0);
|
|
}
|
|
SDValue BuildSREMPow2(SDNode *N, const APInt &Divisor,
|
|
SelectionDAG &DAG,
|
|
SmallVectorImpl<SDNode *> &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<unsigned, const TargetRegisterClass *>
|
|
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);
|
|
}
|
|
|
|
private:
|
|
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
|
|
SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const;
|
|
SDValue LowerBR_CC(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;
|
|
};
|
|
|
|
} // namespace llvm
|
|
|
|
#endif
|