blob: 28e35f8f2a548a3128ea0b054b13b013825a39d8 [file] [log] [blame]
//===- ARCISelDAGToDAG.cpp - ARC dag to dag inst selector -------*- 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 an instruction selector for the ARC target.
//
//===----------------------------------------------------------------------===//
#include "ARC.h"
#include "ARCTargetMachine.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
#define DEBUG_TYPE "arc-isel"
#define PASS_NAME "ARC DAG->DAG Pattern Instruction Selection"
/// ARCDAGToDAGISel - ARC specific code to select ARC machine
/// instructions for SelectionDAG operations.
namespace {
class ARCDAGToDAGISel : public SelectionDAGISel {
public:
static char ID;
ARCDAGToDAGISel() = delete;
ARCDAGToDAGISel(ARCTargetMachine &TM, CodeGenOptLevel OptLevel)
: SelectionDAGISel(ID, TM, OptLevel) {}
void Select(SDNode *N) override;
// Complex Pattern Selectors.
bool SelectFrameADDR_ri(SDValue Addr, SDValue &Base, SDValue &Offset);
bool SelectAddrModeS9(SDValue Addr, SDValue &Base, SDValue &Offset);
bool SelectAddrModeImm(SDValue Addr, SDValue &Base, SDValue &Offset);
bool SelectAddrModeFar(SDValue Addr, SDValue &Base, SDValue &Offset);
// Include the pieces autogenerated from the target description.
#include "ARCGenDAGISel.inc"
};
char ARCDAGToDAGISel::ID;
} // end anonymous namespace
INITIALIZE_PASS(ARCDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false)
/// This pass converts a legalized DAG into a ARC-specific DAG, ready for
/// instruction scheduling.
FunctionPass *llvm::createARCISelDag(ARCTargetMachine &TM,
CodeGenOptLevel OptLevel) {
return new ARCDAGToDAGISel(TM, OptLevel);
}
bool ARCDAGToDAGISel::SelectAddrModeImm(SDValue Addr, SDValue &Base,
SDValue &Offset) {
if (Addr.getOpcode() == ARCISD::GAWRAPPER) {
Base = Addr.getOperand(0);
Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
return true;
}
return false;
}
bool ARCDAGToDAGISel::SelectAddrModeS9(SDValue Addr, SDValue &Base,
SDValue &Offset) {
if (Addr.getOpcode() == ARCISD::GAWRAPPER) {
return false;
}
if (Addr.getOpcode() != ISD::ADD && Addr.getOpcode() != ISD::SUB &&
!CurDAG->isBaseWithConstantOffset(Addr)) {
if (Addr.getOpcode() == ISD::FrameIndex) {
// Match frame index.
int FI = cast<FrameIndexSDNode>(Addr)->getIndex();
Base = CurDAG->getTargetFrameIndex(
FI, TLI->getPointerTy(CurDAG->getDataLayout()));
} else {
Base = Addr;
}
Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
return true;
}
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(Addr.getOperand(1))) {
int32_t RHSC = RHS->getSExtValue();
if (Addr.getOpcode() == ISD::SUB)
RHSC = -RHSC;
// Do we need more than 9 bits to encode?
if (!isInt<9>(RHSC))
return false;
Base = Addr.getOperand(0);
if (Base.getOpcode() == ISD::FrameIndex) {
int FI = cast<FrameIndexSDNode>(Base)->getIndex();
Base = CurDAG->getTargetFrameIndex(
FI, TLI->getPointerTy(CurDAG->getDataLayout()));
}
Offset = CurDAG->getTargetConstant(RHSC, SDLoc(Addr), MVT::i32);
return true;
}
Base = Addr;
Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
return true;
}
bool ARCDAGToDAGISel::SelectAddrModeFar(SDValue Addr, SDValue &Base,
SDValue &Offset) {
if (SelectAddrModeS9(Addr, Base, Offset))
return false;
if (Addr.getOpcode() == ARCISD::GAWRAPPER) {
return false;
}
if (ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(Addr.getOperand(1))) {
int32_t RHSC = RHS->getSExtValue();
if (Addr.getOpcode() == ISD::SUB)
RHSC = -RHSC;
Base = Addr.getOperand(0);
Offset = CurDAG->getTargetConstant(RHSC, SDLoc(Addr), MVT::i32);
return true;
}
return false;
}
// Is this a legal frame index addressing expression.
bool ARCDAGToDAGISel::SelectFrameADDR_ri(SDValue Addr, SDValue &Base,
SDValue &Offset) {
FrameIndexSDNode *FIN = nullptr;
if ((FIN = dyn_cast<FrameIndexSDNode>(Addr))) {
Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i32);
Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);
return true;
}
if (Addr.getOpcode() == ISD::ADD) {
ConstantSDNode *CN = nullptr;
if ((FIN = dyn_cast<FrameIndexSDNode>(Addr.getOperand(0))) &&
(CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1))) &&
(CN->getSExtValue() % 4 == 0 && CN->getSExtValue() >= 0)) {
// Constant positive word offset from frame index
Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), MVT::i32);
Offset =
CurDAG->getTargetConstant(CN->getSExtValue(), SDLoc(Addr), MVT::i32);
return true;
}
}
return false;
}
void ARCDAGToDAGISel::Select(SDNode *N) {
switch (N->getOpcode()) {
case ISD::Constant: {
uint64_t CVal = cast<ConstantSDNode>(N)->getZExtValue();
ReplaceNode(N, CurDAG->getMachineNode(
isInt<12>(CVal) ? ARC::MOV_rs12 : ARC::MOV_rlimm,
SDLoc(N), MVT::i32,
CurDAG->getTargetConstant(CVal, SDLoc(N), MVT::i32)));
return;
}
}
SelectCode(N);
}