Index: lib/Target/RISCV/CMakeLists.txt =================================================================== --- lib/Target/RISCV/CMakeLists.txt +++ lib/Target/RISCV/CMakeLists.txt @@ -1,6 +1,8 @@ set(LLVM_TARGET_DEFINITIONS RISCV.td) +tablegen(LLVM RISCVGenRegisterBank.inc -gen-register-bank) tablegen(LLVM RISCVGenRegisterInfo.inc -gen-register-info) +tablegen(LLVM RISCVGenGlobalISel.inc -gen-global-isel) tablegen(LLVM RISCVGenInstrInfo.inc -gen-instr-info) tablegen(LLVM RISCVGenMCCodeEmitter.inc -gen-emitter) tablegen(LLVM RISCVGenMCPseudoLowering.inc -gen-pseudo-lowering) @@ -14,11 +16,15 @@ add_llvm_target(RISCVCodeGen RISCVAsmPrinter.cpp + RISCVCallLowering.cpp RISCVFrameLowering.cpp RISCVInstrInfo.cpp + RISCVInstructionSelector.cpp RISCVISelDAGToDAG.cpp RISCVISelLowering.cpp + RISCVLegalizerInfo.cpp RISCVMCInstLower.cpp + RISCVRegisterBankInfo.cpp RISCVRegisterInfo.cpp RISCVSubtarget.cpp RISCVTargetMachine.cpp Index: lib/Target/RISCV/LLVMBuild.txt =================================================================== --- lib/Target/RISCV/LLVMBuild.txt +++ lib/Target/RISCV/LLVMBuild.txt @@ -30,6 +30,6 @@ type = Library name = RISCVCodeGen parent = RISCV -required_libraries = AsmPrinter Core CodeGen MC RISCVAsmPrinter RISCVDesc - RISCVInfo SelectionDAG Support Target +required_libraries = AsmPrinter Core CodeGen GlobalISel MC RISCVAsmPrinter + RISCVDesc RISCVInfo SelectionDAG Support Target add_to_library_groups = RISCV Index: lib/Target/RISCV/RISCV.h =================================================================== --- lib/Target/RISCV/RISCV.h +++ lib/Target/RISCV/RISCV.h @@ -21,11 +21,18 @@ class RISCVTargetMachine; class AsmPrinter; class FunctionPass; +class InstructionSelector; class MCInst; class MCOperand; class MachineInstr; class MachineOperand; +class RISCVSubtarget; +class RISCVRegisterBankInfo; +InstructionSelector * +createRISCVInstructionSelector(const RISCVTargetMachine &TM, + const RISCVSubtarget &STI, + const RISCVRegisterBankInfo &RBI); void LowerRISCVMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI, const AsmPrinter &AP); bool LowerRISCVMachineOperandToMCOperand(const MachineOperand &MO, Index: lib/Target/RISCV/RISCV.td =================================================================== --- lib/Target/RISCV/RISCV.td +++ lib/Target/RISCV/RISCV.td @@ -60,6 +60,7 @@ //===----------------------------------------------------------------------===// include "RISCVRegisterInfo.td" +include "RISCVRegisterBanks.td" include "RISCVCallingConv.td" include "RISCVInstrInfo.td" Index: lib/Target/RISCV/RISCVCallLowering.h =================================================================== --- lib/Target/RISCV/RISCVCallLowering.h +++ lib/Target/RISCV/RISCVCallLowering.h @@ -0,0 +1,58 @@ +//=== llvm/lib/Target/RISCV/RISCVCallLowering.h - Call lowering -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file +/// This file describes how to lower LLVM calls to machine code calls. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVCALLLOWERING_H +#define LLVM_LIB_TARGET_RISCV_RISCVCALLLOWERING_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/CodeGen/GlobalISel/CallLowering.h" +#include "llvm/IR/CallingConv.h" +#include +#include + +namespace llvm { + +class RISCVTargetLowering; +class MachineFunction; +class MachineInstrBuilder; +class MachineIRBuilder; +class Value; + +class RISCVCallLowering : public CallLowering { +public: + RISCVCallLowering(const RISCVTargetLowering &TLI); + + bool lowerReturn(MachineIRBuilder &MIRBuiler, const Value *Val, + unsigned VReg) const override; + + bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F, + ArrayRef VRegs) const override; + + bool lowerCall(MachineIRBuilder &MIRBuilder, CallingConv::ID CallConv, + const MachineOperand &Callee, const ArgInfo &OrigRet, + ArrayRef OrigArgs) const override; + +private: + /// A function of this type is used to perform value split action. + using SplitArgTy = std::function; + + void splitToValueTypes(const ArgInfo &OrigArgInfo, + SmallVectorImpl &SplitArgs, + MachineFunction &MF, + const SplitArgTy &PerformArgSplit) const; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_RISCV_RISCVCALLLOWERING_H Index: lib/Target/RISCV/RISCVCallLowering.cpp =================================================================== --- lib/Target/RISCV/RISCVCallLowering.cpp +++ lib/Target/RISCV/RISCVCallLowering.cpp @@ -0,0 +1,512 @@ +//===- llvm/lib/Target/RISCV/RISCVCallLowering.cpp - Call lowering --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file +/// This file implements the lowering of LLVM calls to machine code calls for +/// GlobalISel. +// +//===----------------------------------------------------------------------===// + +#include "RISCVCallLowering.h" +#include "RISCVISelLowering.h" +#include "RISCVSubtarget.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/Analysis.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/CodeGen/GlobalISel/Utils.h" +#include "llvm/CodeGen/LowLevelType.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/MachineValueType.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/Value.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/LowLevelTypeImpl.h" +#include +#include +#include +#include + +using namespace llvm; + +RISCVCallLowering::RISCVCallLowering(const RISCVTargetLowering &TLI) + : CallLowering(&TLI) {} + +static bool isSupportedType(const DataLayout &DL, + const RISCVTargetLowering &TLI, Type *T) { + if (T->isArrayTy()) + return true; + + if (T->isStructTy()) { + // For now we only allow homogeneous structs that we can manipulate with + // G_MERGE_VALUES and G_UNMERGE_VALUES + auto StructT = cast(T); + for (unsigned i = 1, e = StructT->getNumElements(); i != e; ++i) { + if (StructT->getElementType(i) != StructT->getElementType(0)) + return false; + } + return true; + } + + EVT VT = TLI.getValueType(DL, T, true); + if (!VT.isSimple() || VT.isVector() || + !(VT.isInteger() || VT.isFloatingPoint())) + return false; + + unsigned VTSize = VT.getSimpleVT().getSizeInBits(); + + return VTSize == 1 || VTSize == 8 || VTSize == 16 || VTSize == 32 || + VTSize == 64 || VTSize == 128; +} + +void RISCVCallLowering::splitToValueTypes( + const ArgInfo &OrigArg, SmallVectorImpl &SplitArgs, + MachineFunction &MF, const SplitArgTy &PerformArgSplit) const { + const RISCVTargetLowering &TLI = *getTLI(); + LLVMContext &Ctx = OrigArg.Ty->getContext(); + const DataLayout &DL = MF.getDataLayout(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const Function &F = MF.getFunction(); + + SmallVector SplitVTs; + SmallVector Offsets; + ComputeValueVTs(TLI, DL, OrigArg.Ty, SplitVTs, &Offsets, 0); + + if (SplitVTs.size() == 1) { + // Even if there is no splitting to do, we still want to replace the + // original type (e.g. pointer type -> integer). + auto Flags = OrigArg.Flags; + unsigned OriginalAlignment = DL.getABITypeAlignment(OrigArg.Ty); + Flags.setOrigAlign(OriginalAlignment); + SplitArgs.emplace_back(OrigArg.Reg, SplitVTs[0].getTypeForEVT(Ctx), Flags, + OrigArg.IsFixed); + return; + } + + unsigned FirstRegIdx = SplitArgs.size(); + for (unsigned i = 0, e = SplitVTs.size(); i != e; ++i) { + EVT SplitVT = SplitVTs[i]; + Type *SplitTy = SplitVT.getTypeForEVT(Ctx); + auto Flags = OrigArg.Flags; + + unsigned OriginalAlignment = DL.getABITypeAlignment(SplitTy); + Flags.setOrigAlign(OriginalAlignment); + + bool NeedsConsecutiveRegisters = + TLI.functionArgumentNeedsConsecutiveRegisters( + SplitTy, F.getCallingConv(), F.isVarArg()); + if (NeedsConsecutiveRegisters) { + Flags.setInConsecutiveRegs(); + if (i == e - 1) + Flags.setInConsecutiveRegsLast(); + } + + SplitArgs.push_back( + ArgInfo{MRI.createGenericVirtualRegister(getLLTForType(*SplitTy, DL)), + SplitTy, Flags, OrigArg.IsFixed}); + } + + for (unsigned i = 0; i < Offsets.size(); ++i) + PerformArgSplit(SplitArgs[FirstRegIdx + i].Reg, Offsets[i] * 8); +} + +namespace { + +/// Helper class for values going out through an ABI boundary (used for handling +/// function return values and call parameters). +struct OutgoingValueHandler : public CallLowering::ValueHandler { + OutgoingValueHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + MachineInstrBuilder &MIB, CCAssignFn *AssignFn) + : ValueHandler(MIRBuilder, MRI, AssignFn), MIB(MIB), + DL(MIRBuilder.getMF().getDataLayout()) {} + + unsigned getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO) override { + assert((Size == 1 || Size == 2 || Size == 4 || Size == 8) && + "Unsupported size"); + + LLT p0 = LLT::pointer(0, DL.getPointerSizeInBits(0)); + LLT s32 = LLT::scalar(DL.getPointerSizeInBits(0)); + unsigned SPReg = MRI.createGenericVirtualRegister(p0); + MIRBuilder.buildCopy(SPReg, RISCV::X2); + + unsigned OffsetReg = MRI.createGenericVirtualRegister(s32); + MIRBuilder.buildConstant(OffsetReg, Offset); + + unsigned AddrReg = MRI.createGenericVirtualRegister(p0); + MIRBuilder.buildGEP(AddrReg, SPReg, OffsetReg); + + MPO = MachinePointerInfo::getStack(MIRBuilder.getMF(), Offset); + return AddrReg; + } + + void assignValueToReg(unsigned ValVReg, unsigned PhysReg, + CCValAssign &VA) override { + assert(VA.isRegLoc() && "Value shouldn't be assigned to reg"); + assert(VA.getLocReg() == PhysReg && "Assigning to the wrong reg?"); + assert(VA.getValVT().getSizeInBits() <= 64 && "Unsupported value size"); + assert(VA.getLocVT().getSizeInBits() <= 64 && "Unsupported location size"); + + unsigned ExtReg = extendRegister(ValVReg, VA); + MIRBuilder.buildCopy(PhysReg, ExtReg); + MIB.addUse(PhysReg, RegState::Implicit); + } + + void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + assert((Size == 1 || Size == 2 || Size == 4 || Size == 8) && + "Unsupported size"); + + unsigned ExtReg = extendRegister(ValVReg, VA); + auto MMO = MIRBuilder.getMF().getMachineMemOperand( + MPO, MachineMemOperand::MOStore, VA.getLocVT().getStoreSize(), 0); + MIRBuilder.buildStore(ExtReg, Addr, *MMO); + } + + unsigned assignCustomValue(const CallLowering::ArgInfo &Arg, + ArrayRef VAs) override { + return 1; + } + + bool assignArg(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, + const CallLowering::ArgInfo &Info, CCState &State) override { + if (AssignFn && AssignFn(ValNo, ValVT, LocVT, LocInfo, Info.Flags, State)) + return true; + + StackSize = State.getNextStackOffset(); + + return false; + } + + MachineInstrBuilder &MIB; + uint64_t StackSize = 0; + +protected: + const DataLayout &DL; +}; + +} // end anonymous namespace + +bool RISCVCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, + const Value *Val, unsigned VReg) const { + assert(!Val == !VReg && "Return value without a vreg"); + + auto MIB = MIRBuilder.buildInstrNoInsert(RISCV::PseudoRET); + + if (Val) { + auto &MF = MIRBuilder.getMF(); + auto &DL = MF.getDataLayout(); + const auto &F = MF.getFunction(); + auto &TLI = *getTLI(); + if (!isSupportedType(DL, TLI, Val->getType())) + return false; + + SmallVector SplitVTs; + SmallVector Regs; + ArgInfo RetInfo(VReg, Val->getType()); + setArgFlags(RetInfo, AttributeList::ReturnIndex, DL, F); + splitToValueTypes( + RetInfo, SplitVTs, MF, + [&](unsigned Reg, uint64_t Offset) { Regs.push_back(Reg); }); + + if (Regs.size() > 1) + MIRBuilder.buildUnmerge(Regs, VReg); + + // FIXME: Workaround for CC_RISCV. + CCAssignFn *AssignFn = nullptr; + + OutgoingValueHandler RetHandler(MIRBuilder, MF.getRegInfo(), MIB, AssignFn); + if (AssignFn && !handleAssignments(MIRBuilder, SplitVTs, RetHandler)) + return false; + } + + MIRBuilder.insertInstr(MIB); + return true; +} + +namespace { + +/// Helper class for values coming in through an ABI boundary (used for handling +/// formal arguments and call return values). +struct IncomingValueHandler : public CallLowering::ValueHandler { + IncomingValueHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + CCAssignFn AssignFn) + : ValueHandler(MIRBuilder, MRI, AssignFn), + DL(MIRBuilder.getMF().getDataLayout()) {} + + unsigned getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO) override { + assert((Size == 1 || Size == 2 || Size == 4 || Size == 8) && + "Unsupported size"); + + auto &MFI = MIRBuilder.getMF().getFrameInfo(); + + int FI = MFI.CreateFixedObject(Size, Offset, true); + MPO = MachinePointerInfo::getFixedStack(MIRBuilder.getMF(), FI); + + unsigned AddrReg = MRI.createGenericVirtualRegister( + LLT::pointer(MPO.getAddrSpace(), DL.getPointerSizeInBits(0))); + MIRBuilder.buildFrameIndex(AddrReg, FI); + + return AddrReg; + } + + void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + assert((Size == 1 || Size == 2 || Size == 4 || Size == 8) && + "Unsupported size"); + + if (VA.getLocInfo() == CCValAssign::SExt || + VA.getLocInfo() == CCValAssign::ZExt) { + // If the value is zero- or sign-extended, its size becomes 4 bytes, so + // that's what we should load. + Size = 4; + assert(MRI.getType(ValVReg).isScalar() && "Only scalars supported atm"); + + auto LoadVReg = MRI.createGenericVirtualRegister( + LLT::scalar(DL.getPointerSizeInBits(0))); + buildLoad(LoadVReg, Addr, Size, /* Alignment */ 0, MPO); + MIRBuilder.buildTrunc(ValVReg, LoadVReg); + } else { + // If the value is not extended, a simple load will suffice. + buildLoad(ValVReg, Addr, Size, /* Alignment */ 0, MPO); + } + } + + void buildLoad(unsigned Val, unsigned Addr, uint64_t Size, unsigned Alignment, + MachinePointerInfo &MPO) { + auto MMO = MIRBuilder.getMF().getMachineMemOperand( + MPO, MachineMemOperand::MOLoad, Size, Alignment); + MIRBuilder.buildLoad(Val, Addr, *MMO); + } + + void assignValueToReg(unsigned ValVReg, unsigned PhysReg, + CCValAssign &VA) override { + assert(VA.isRegLoc() && "Value shouldn't be assigned to reg"); + assert(VA.getLocReg() == PhysReg && "Assigning to the wrong reg?"); + + auto ValSize = VA.getValVT().getSizeInBits(); + auto LocSize = VA.getLocVT().getSizeInBits(); + + assert(ValSize <= 64 && "Unsupported value size"); + assert(LocSize <= 64 && "Unsupported location size"); + + markPhysRegUsed(PhysReg); + if (ValSize == LocSize) { + MIRBuilder.buildCopy(ValVReg, PhysReg); + } else { + assert(ValSize < LocSize && "Extensions not supported"); + + // We cannot create a truncating copy, nor a trunc of a physical register. + // Therefore, we need to copy the content of the physical register into a + // virtual one and then truncate that. + auto PhysRegToVReg = + MRI.createGenericVirtualRegister(LLT::scalar(LocSize)); + MIRBuilder.buildCopy(PhysRegToVReg, PhysReg); + MIRBuilder.buildTrunc(ValVReg, PhysRegToVReg); + } + } + + unsigned assignCustomValue(const RISCVCallLowering::ArgInfo &Arg, + ArrayRef VAs) override { + return 1; + } + + /// Marking a physical register as used is different between formal + /// parameters, where it's a basic block live-in, and call returns, where it's + /// an implicit-def of the call instruction. + virtual void markPhysRegUsed(unsigned PhysReg) = 0; + +protected: + const DataLayout &DL; +}; + +struct FormalArgHandler : public IncomingValueHandler { + FormalArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + CCAssignFn AssignFn) + : IncomingValueHandler(MIRBuilder, MRI, AssignFn) {} + + void markPhysRegUsed(unsigned PhysReg) override { + MIRBuilder.getMBB().addLiveIn(PhysReg); + } +}; + +} // end anonymous namespace + +bool RISCVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder, + const Function &F, + ArrayRef VRegs) const { + auto &TLI = *getTLI(); + + // Quick exit if there aren't any args + if (F.arg_empty()) + return true; + + if (F.isVarArg()) + return false; + + auto &MF = MIRBuilder.getMF(); + auto &MBB = MIRBuilder.getMBB(); + auto DL = MF.getDataLayout(); + + for (auto &Arg : F.args()) { + if (!isSupportedType(DL, TLI, Arg.getType())) + return false; + if (Arg.hasByValOrInAllocaAttr()) + return false; + } + + // FIXME: Workaround for CC_RISCV. + CCAssignFn *AssignFn = nullptr; + + FormalArgHandler ArgHandler(MIRBuilder, MIRBuilder.getMF().getRegInfo(), + AssignFn); + + SmallVector ArgInfos; + SmallVector SplitRegs; + unsigned Idx = 0; + for (auto &Arg : F.args()) { + ArgInfo AInfo(VRegs[Idx], Arg.getType()); + setArgFlags(AInfo, Idx + AttributeList::FirstArgIndex, DL, F); + + SplitRegs.clear(); + + splitToValueTypes(AInfo, ArgInfos, MF, [&](unsigned Reg, uint64_t Offset) { + SplitRegs.push_back(Reg); + }); + + if (!SplitRegs.empty()) + MIRBuilder.buildMerge(VRegs[Idx], SplitRegs); + + Idx++; + } + + if (!MBB.empty()) + MIRBuilder.setInstr(*MBB.begin()); + + return AssignFn ? handleAssignments(MIRBuilder, ArgInfos, ArgHandler) : true; +} + +namespace { + +struct CallReturnHandler : public IncomingValueHandler { + CallReturnHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + MachineInstrBuilder MIB, CCAssignFn *AssignFn) + : IncomingValueHandler(MIRBuilder, MRI, AssignFn), MIB(MIB) {} + + void markPhysRegUsed(unsigned PhysReg) override { + MIB.addDef(PhysReg, RegState::Implicit); + } + + MachineInstrBuilder MIB; +}; + +} // end anonymous namespace + +bool RISCVCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, + CallingConv::ID CallConv, + const MachineOperand &Callee, + const ArgInfo &OrigRet, + ArrayRef OrigArgs) const { + MachineFunction &MF = MIRBuilder.getMF(); + const auto &TLI = *getTLI(); + const auto &DL = MF.getDataLayout(); + const auto &STI = MF.getSubtarget(); + const TargetRegisterInfo *TRI = STI.getRegisterInfo(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + + auto CallSeqStart = MIRBuilder.buildInstr(RISCV::ADJCALLSTACKDOWN); + + // Create the call instruction. + auto MIB = MIRBuilder.buildInstrNoInsert(RISCV::PseudoCALL) + .add(Callee) + .addRegMask(TRI->getCallPreservedMask(MF, CallConv)); + if (Callee.isReg()) { + auto CalleeReg = Callee.getReg(); + if (CalleeReg && !TRI->isPhysicalRegister(CalleeReg)) { + MIB->getOperand(0).setReg(constrainOperandRegClass( + MF, *TRI, MRI, *STI.getInstrInfo(), *STI.getRegBankInfo(), + *MIB.getInstr(), MIB->getDesc(), CalleeReg, 0)); + } + } + + SmallVector ArgInfos; + for (auto Arg : OrigArgs) { + if (!isSupportedType(DL, TLI, Arg.Ty)) + return false; + + if (!Arg.IsFixed) + return false; + + if (Arg.Flags.isByVal()) + return false; + + SmallVector Regs; + splitToValueTypes(Arg, ArgInfos, MF, [&](unsigned Reg, uint64_t Offset) { + Regs.push_back(Reg); + }); + + if (Regs.size() > 1) + MIRBuilder.buildUnmerge(Regs, Arg.Reg); + } + + // FIXME: Workaround for CC_RISCV. + CCAssignFn *ArgAssignFn = nullptr; + OutgoingValueHandler ArgHandler(MIRBuilder, MRI, MIB, ArgAssignFn); + if (ArgAssignFn && !handleAssignments(MIRBuilder, ArgInfos, ArgHandler)) + return false; + + // Add the actual call instruction to the correct basic block. + MIRBuilder.insertInstr(MIB); + + if (!OrigRet.Ty->isVoidTy()) { + if (!isSupportedType(DL, TLI, OrigRet.Ty)) + return false; + + ArgInfos.clear(); + SmallVector SplitRegs; + splitToValueTypes( + OrigRet, ArgInfos, MF, + [&](unsigned Reg, uint64_t Offset) { SplitRegs.push_back(Reg); }); + + // FIXME: Workaround for CC_RISCV. + CCAssignFn *RetAssignFn = nullptr; + CallReturnHandler RetHandler(MIRBuilder, MRI, MIB, RetAssignFn); + if (RetAssignFn && !handleAssignments(MIRBuilder, ArgInfos, RetHandler)) + return false; + + if (!SplitRegs.empty()) { + // Build up split the value and allocated each individual piece. + MIRBuilder.buildMerge(OrigRet.Reg, SplitRegs); + } + } + + CallSeqStart.addImm(ArgHandler.StackSize).addImm(0); + + // Update the ADJCALLSTACKDOWN. + MIRBuilder.buildInstr(RISCV::ADJCALLSTACKUP) + .addImm(ArgHandler.StackSize) + .addImm(0); + + return true; +} Index: lib/Target/RISCV/RISCVInstructionSelector.cpp =================================================================== --- lib/Target/RISCV/RISCVInstructionSelector.cpp +++ lib/Target/RISCV/RISCVInstructionSelector.cpp @@ -0,0 +1,427 @@ +//===- RISCVInstructionSelector.cpp ------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the InstructionSelector class for +/// RISCV. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "RISCVRegisterBankInfo.h" +#include "RISCVSubtarget.h" +#include "RISCVTargetMachine.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "riscv-isel" + +using namespace llvm; +using namespace TargetOpcode; + +namespace { + +#define GET_GLOBALISEL_PREDICATE_BITSET +#include "RISCVGenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATE_BITSET + +class RISCVInstructionSelector : public InstructionSelector { +public: + RISCVInstructionSelector(const RISCVTargetMachine &TM, + const RISCVSubtarget &STI, + const RISCVRegisterBankInfo &RBI); + + bool select(MachineInstr &I, CodeGenCoverage &CoverageInfo) const override; + static const char *getName() { return DEBUG_TYPE; } + +private: + bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const; + + struct CmpConstants; + struct InsertInfo; + + bool selectCmp(CmpConstants Helper, MachineInstrBuilder &MIB, + MachineRegisterInfo &MRI) const; + + // Set \p DestReg to \p Constant. + void putConstant(InsertInfo I, unsigned DestReg, unsigned Constant) const; + + bool selectGlobal(MachineInstrBuilder &MIB, MachineRegisterInfo &MRI) const; + bool selectSelect(MachineInstrBuilder &MIB, MachineRegisterInfo &MRI) const; + bool selectShift(unsigned ShiftOpc, MachineInstrBuilder &MIB) const; + + // Check if the types match and both operands have the expected size and + // register bank. + bool validOpRegPair(MachineRegisterInfo &MRI, unsigned LHS, unsigned RHS, + unsigned ExpectedSize, unsigned ExpectedRegBankID) const; + + // Check if the register has the expected size and register bank. + bool validReg(MachineRegisterInfo &MRI, unsigned Reg, unsigned ExpectedSize, + unsigned ExpectedRegBankID) const; + + const RISCVInstrInfo &TII; + const RISCVRegisterInfo &TRI; + const RISCVTargetMachine &TM; + const RISCVRegisterBankInfo &RBI; + const RISCVSubtarget &STI; + const RISCVSubtarget *Subtarget; + +#define GET_GLOBALISEL_PREDICATES_DECL +#include "RISCVGenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATES_DECL + +// We declare the temporaries used by selectImpl() in the class to minimize the +// cost of constructing placeholder values. +#define GET_GLOBALISEL_TEMPORARIES_DECL +#include "RISCVGenGlobalISel.inc" +#undef GET_GLOBALISEL_TEMPORARIES_DECL +}; +} // end anonymous namespace + +namespace llvm { +InstructionSelector * +createRISCVInstructionSelector(const RISCVTargetMachine &TM, + const RISCVSubtarget &STI, + const RISCVRegisterBankInfo &RBI) { + return new RISCVInstructionSelector(TM, STI, RBI); +} +} + +#define GET_GLOBALISEL_IMPL +#include "RISCVGenGlobalISel.inc" +#undef GET_GLOBALISEL_IMPL + +RISCVInstructionSelector::RISCVInstructionSelector( + const RISCVTargetMachine &TM, const RISCVSubtarget &STI, + const RISCVRegisterBankInfo &RBI) + : InstructionSelector(), TII(*STI.getInstrInfo()), + TRI(*STI.getRegisterInfo()), TM(TM), RBI(RBI), STI(STI), Subtarget(&STI), +#define GET_GLOBALISEL_PREDICATES_INIT +#include "RISCVGenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATES_INIT +#define GET_GLOBALISEL_TEMPORARIES_INIT +#include "RISCVGenGlobalISel.inc" +#undef GET_GLOBALISEL_TEMPORARIES_INIT +{ +} + +static bool selectCopy(MachineInstr &I, const TargetInstrInfo &TII, + MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, + const RegisterBankInfo &RBI) { + unsigned DstReg = I.getOperand(0).getReg(); + if (TargetRegisterInfo::isPhysicalRegister(DstReg)) + return true; + + const RegisterBank *RegBank = RBI.getRegBank(DstReg, MRI, TRI); + (void)RegBank; + assert(RegBank && "Can't get reg bank for virtual register"); + + const unsigned DstSize = MRI.getType(DstReg).getSizeInBits(); + assert((RegBank->getID() == RISCV::GPRRegBankID || + RegBank->getID() == RISCV::FPRRegBankID) && + "Unsupported reg bank"); + + const TargetRegisterClass *RC = &RISCV::GPRRegClass; + + if (RegBank->getID() == RISCV::FPRRegBankID) { + if (DstSize == 32) + RC = &RISCV::FPR32RegClass; + else if (DstSize == 64) + RC = &RISCV::FPR64RegClass; + else + llvm_unreachable("Unsupported destination size"); + } + + // No need to constrain SrcReg. It will get constrained when + // we hit another of its uses or its defs. + // Copies do not have constraints. + if (!RBI.constrainGenericRegister(DstReg, *RC, MRI)) { + DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode()) + << " operand\n"); + return false; + } + return true; +} + +/// Select the opcode for simple loads and stores. For types smaller than 32 +/// bits, the value will be zero extended. Returns the original opcode if it +/// doesn't know how to select a better one. +static unsigned selectLoadStoreOpCode(unsigned Opc, unsigned RegBank, + unsigned Size) { + bool isStore = Opc == TargetOpcode::G_STORE; + + if (RegBank == RISCV::GPRRegBankID) { + switch (Size) { + case 1: + case 8: + return isStore ? RISCV::SB : RISCV::LB; + case 16: + return isStore ? RISCV::SH : RISCV::LH; + case 32: + return isStore ? RISCV::SW : RISCV::LW; + default: + return Opc; + } + } + + return Opc; +} + +struct RISCVInstructionSelector::CmpConstants { + CmpConstants(unsigned CmpOpcode, unsigned FlagsOpcode, unsigned OpRegBank, + unsigned OpSize) + : ComparisonOpcode(CmpOpcode), ReadFlagsOpcode(FlagsOpcode), + OperandRegBankID(OpRegBank), OperandSize(OpSize) {} + + // The opcode used for performing the comparison. + const unsigned ComparisonOpcode; + + // The opcode used for reading the flags set by the comparison. May be + // RISCV::INSTRUCTION_LIST_END if we don't need to read the flags. + const unsigned ReadFlagsOpcode; + + // The assumed register bank ID for the operands. + const unsigned OperandRegBankID; + + // The assumed size in bits for the operands. + const unsigned OperandSize; +}; + +struct RISCVInstructionSelector::InsertInfo { + InsertInfo(MachineInstrBuilder &MIB) + : MBB(*MIB->getParent()), InsertBefore(std::next(MIB->getIterator())), + DbgLoc(MIB->getDebugLoc()) {} + + MachineBasicBlock &MBB; + const MachineBasicBlock::instr_iterator InsertBefore; + const DebugLoc &DbgLoc; +}; + +void RISCVInstructionSelector::putConstant(InsertInfo I, unsigned DestReg, + unsigned Constant) const {} + +bool RISCVInstructionSelector::validOpRegPair( + MachineRegisterInfo &MRI, unsigned LHSReg, unsigned RHSReg, + unsigned ExpectedSize, unsigned ExpectedRegBankID) const { + return true; +} + +bool RISCVInstructionSelector::validReg(MachineRegisterInfo &MRI, unsigned Reg, + unsigned ExpectedSize, + unsigned ExpectedRegBankID) const { + return true; +} + +bool RISCVInstructionSelector::selectCmp(CmpConstants Helper, + MachineInstrBuilder &MIB, + MachineRegisterInfo &MRI) const { + return true; +} + +bool RISCVInstructionSelector::selectGlobal(MachineInstrBuilder &MIB, + MachineRegisterInfo &MRI) const { + return true; +} + +bool RISCVInstructionSelector::selectSelect(MachineInstrBuilder &MIB, + MachineRegisterInfo &MRI) const { + return true; +} + +bool RISCVInstructionSelector::selectShift(unsigned ShiftOpc, + MachineInstrBuilder &MIB) const { + return true; +} + +bool RISCVInstructionSelector::select(MachineInstr &I, + CodeGenCoverage &CoverageInfo) const { + assert(I.getParent() && "Instruction should be in a basic block!"); + assert(I.getParent()->getParent() && "Instruction should be in a function!"); + + auto &MBB = *I.getParent(); + auto &MF = *MBB.getParent(); + auto &MRI = MF.getRegInfo(); + auto &DL = MF.getDataLayout(); + + if (!isPreISelGenericOpcode(I.getOpcode())) { + if (I.isCopy()) + return selectCopy(I, TII, MRI, TRI, RBI); + + return true; + } + + if (I.getOpcode() == G_CONSTANT) { + // Pointer constants should be treated the same as integer constants. + // Change the type and let TableGen handle it. + unsigned ResultReg = I.getOperand(0).getReg(); + LLT Ty = MRI.getType(ResultReg); + if (Ty.isPointer()) + MRI.setType(ResultReg, LLT::scalar(DL.getPointerSizeInBits(0))); + } + + if (selectImpl(I, CoverageInfo)) + return true; + + MachineInstrBuilder MIB{MF, I}; + bool isSExt = false; + + switch (I.getOpcode()) { + case G_SEXT: + isSExt = true; + LLVM_FALLTHROUGH; + case G_ZEXT: { + LLT DstTy = MRI.getType(I.getOperand(0).getReg()); + // FIXME: Smaller destination sizes coming soon! + if (DstTy.getSizeInBits() != 32 && DstTy.getSizeInBits() != 64) { + DEBUG(dbgs() << "Unsupported destination size for extension"); + return false; + } + + LLT SrcTy = MRI.getType(I.getOperand(1).getReg()); + unsigned SrcSize = SrcTy.getSizeInBits(); + switch (SrcSize) { + case 1: { + // ZExt boils down to & 0x1; for SExt we also subtract that from 0 + I.setDesc(TII.get(RISCV::ANDI)); + MIB.addImm(1); + + if (isSExt) { + unsigned SExtResult = I.getOperand(0).getReg(); + + // Use a new virtual register for the result of the AND + unsigned AndResult = MRI.createVirtualRegister(&RISCV::GPRRegClass); + I.getOperand(0).setReg(AndResult); + + auto InsertBefore = std::next(I.getIterator()); + auto SubI = + BuildMI(MBB, InsertBefore, I.getDebugLoc(), + TII.get(MRI.getType(SExtResult).getSizeInBits() == 64 + ? RISCV::SUBW + : RISCV::SUB)) + .addDef(SExtResult) + .addUse(AndResult) + .addImm(0); + if (!constrainSelectedInstRegOperands(*SubI, TII, TRI, RBI)) + return false; + } + break; + } + default: + DEBUG(dbgs() << "Unsupported source size for extension"); + return false; + } + break; + } + case G_ANYEXT: + case G_TRUNC: { + // The high bits are undefined, so there's nothing special to do, just + // treat it as a copy. + auto SrcReg = I.getOperand(1).getReg(); + auto DstReg = I.getOperand(0).getReg(); + + const auto &SrcRegBank = *RBI.getRegBank(SrcReg, MRI, TRI); + const auto &DstRegBank = *RBI.getRegBank(DstReg, MRI, TRI); + + if (SrcRegBank.getID() != DstRegBank.getID()) { + DEBUG( + dbgs() << "G_TRUNC/G_ANYEXT operands on different register banks\n"); + return false; + } + + if (SrcRegBank.getID() != RISCV::GPRRegBankID) { + DEBUG(dbgs() << "G_TRUNC/G_ANYEXT on non-GPR not supported yet\n"); + return false; + } + + I.setDesc(TII.get(COPY)); + return selectCopy(I, TII, MRI, TRI, RBI); + } + case G_INTTOPTR: + case G_PTRTOINT: { + auto SrcReg = I.getOperand(1).getReg(); + auto DstReg = I.getOperand(0).getReg(); + + const auto &SrcRegBank = *RBI.getRegBank(SrcReg, MRI, TRI); + const auto &DstRegBank = *RBI.getRegBank(DstReg, MRI, TRI); + + if (SrcRegBank.getID() != DstRegBank.getID()) { + DEBUG(dbgs() + << "G_INTTOPTR/G_PTRTOINT operands on different register banks\n"); + return false; + } + + if (SrcRegBank.getID() != RISCV::GPRRegBankID) { + DEBUG(dbgs() << "G_INTTOPTR/G_PTRTOINT on non-GPR not supported yet\n"); + return false; + } + + I.setDesc(TII.get(COPY)); + return selectCopy(I, TII, MRI, TRI, RBI); + } + case G_SELECT: + return selectSelect(MIB, MRI); + case G_LSHR: + return selectShift(MRI.getType(I.getOperand(1).getReg()).getSizeInBits() == + 64 + ? RISCV::SRLIW + : RISCV::SRLI, + MIB); + case G_ASHR: + return selectShift(MRI.getType(I.getOperand(1).getReg()).getSizeInBits() == + 64 + ? RISCV::SRAIW + : RISCV::SRAI, + MIB); + case G_SHL: + return selectShift(MRI.getType(I.getOperand(1).getReg()).getSizeInBits() == + 64 + ? RISCV::SLLIW + : RISCV::SLLI, + MIB); + case G_GEP: + I.setDesc( + TII.get(MRI.getType(I.getOperand(1).getReg()).getSizeInBits() == 64 + ? RISCV::ADDIW + : RISCV::ADDI)); + break; + case G_GLOBAL_VALUE: + return selectGlobal(MIB, MRI); + case G_STORE: + case G_LOAD: { + const auto &MemOp = **I.memoperands_begin(); + if (MemOp.getOrdering() != AtomicOrdering::NotAtomic) { + DEBUG(dbgs() << "Atomic load/store not supported yet\n"); + return false; + } + + unsigned Reg = I.getOperand(0).getReg(); + unsigned RegBank = RBI.getRegBank(Reg, MRI, TRI)->getID(); + + LLT ValTy = MRI.getType(Reg); + const auto ValSize = ValTy.getSizeInBits(); + + const auto NewOpc = selectLoadStoreOpCode(I.getOpcode(), RegBank, ValSize); + if (NewOpc == G_LOAD || NewOpc == G_STORE) + return false; + + I.setDesc(TII.get(NewOpc)); + + if (NewOpc == RISCV::LW || NewOpc == RISCV::SW) + MIB.addReg(0); + MIB.addImm(0); + break; + } + default: + return false; + } + + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); +} Index: lib/Target/RISCV/RISCVLegalizerInfo.h =================================================================== --- lib/Target/RISCV/RISCVLegalizerInfo.h +++ lib/Target/RISCV/RISCVLegalizerInfo.h @@ -0,0 +1,43 @@ +//===- RISCVLegalizerInfo ----------------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This file declares the targeting of the Machinelegalizer class for RISCV. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVMACHINELEGALIZER_H +#define LLVM_LIB_TARGET_RISCV_RISCVMACHINELEGALIZER_H + +#include "llvm/ADT/IndexedMap.h" +#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" +#include "llvm/CodeGen/RuntimeLibcalls.h" +#include "llvm/IR/Instructions.h" + +namespace llvm { + +class RISCVSubtarget; +class RISCVTargetMachine; + +/// This class provides the information for the target register banks. +class RISCVLegalizerInfo : public LegalizerInfo { +private: + const RISCVSubtarget &Subtarget; + const RISCVTargetMachine &TM; + + void setLegalizerInfo32bit(); + void setLegalizerInfo64bit(); + +public: + RISCVLegalizerInfo(const RISCVSubtarget &ST, const RISCVTargetMachine &TM); + + bool legalizeCustom(MachineInstr &MI, MachineRegisterInfo &MRI, + MachineIRBuilder &MIRBuilder) const override; +}; +} // End llvm namespace. +#endif Index: lib/Target/RISCV/RISCVLegalizerInfo.cpp =================================================================== --- lib/Target/RISCV/RISCVLegalizerInfo.cpp +++ lib/Target/RISCV/RISCVLegalizerInfo.cpp @@ -0,0 +1,158 @@ +//===- RISCVLegalizerInfo.cpp ------------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the Machinelegalizer class for RISCV. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "RISCVLegalizerInfo.h" +#include "RISCVCallLowering.h" +#include "RISCVSubtarget.h" +#include "RISCVTargetMachine.h" +#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h" +#include "llvm/CodeGen/LowLevelType.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetOpcodes.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Type.h" + +using namespace llvm; +using namespace TargetOpcode; + +RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST, + const RISCVTargetMachine &TM) + : Subtarget(ST), TM(TM) { + setLegalizerInfo32bit(); + setLegalizerInfo64bit(); + + for (unsigned MemOp : {G_LOAD, G_STORE}) + setLegalizeScalarToDifferentSizeStrategy(MemOp, 0, + narrowToSmallerAndWidenToSmallest); + setLegalizeScalarToDifferentSizeStrategy( + G_GEP, 1, widenToLargerTypesUnsupportedOtherwise); + setLegalizeScalarToDifferentSizeStrategy( + G_CONSTANT, 0, widenToLargerTypesAndNarrowToLargest); + + computeTables(); +} + +void RISCVLegalizerInfo::setLegalizerInfo32bit() { + const LLT p0 = LLT::pointer(0, TM.getPointerSize() * 8); + const LLT s1 = LLT::scalar(1); + const LLT s8 = LLT::scalar(8); + const LLT s16 = LLT::scalar(16); + const LLT s32 = LLT::scalar(32); + const LLT s64 = LLT::scalar(64); + const LLT s128 = LLT::scalar(128); + + for (auto Ty : {p0, s1, s8, s16, s32}) + setAction({G_IMPLICIT_DEF, Ty}, Legal); + + for (auto Ty : {s8, s16, s32, p0}) + setAction({G_PHI, Ty}, Legal); + + for (unsigned BinOp : {G_ADD, G_SUB, G_MUL, G_AND, G_OR, G_XOR}) + for (auto Ty : {s8, s16, s32}) + setAction({BinOp, Ty}, Legal); + + for (unsigned Op : {G_UADDE}) { + setAction({Op, s32}, Legal); + setAction({Op, 1, s1}, Legal); + } + + for (unsigned MemOp : {G_LOAD, G_STORE}) { + for (auto Ty : {s8, s16, s32, p0}) + setAction({MemOp, Ty}, Legal); + + // And everything's fine in addrspace 0. + setAction({MemOp, 1, p0}, Legal); + } + + // Pointer-handling + setAction({G_FRAME_INDEX, p0}, Legal); + setAction({G_GLOBAL_VALUE, p0}, Legal); + + setAction({G_GEP, p0}, Legal); + setAction({G_GEP, 1, s32}, Legal); + + // Control-flow + setAction({G_BRCOND, s1}, Legal); + + // Constants + for (auto Ty : {s8, s16, s32, p0}) + setAction({TargetOpcode::G_CONSTANT, Ty}, Legal); + + // Extensions + for (auto Ty : {s8, s16, s32}) { + setAction({G_ZEXT, Ty}, Legal); + setAction({G_SEXT, Ty}, Legal); + setAction({G_ANYEXT, Ty}, Legal); + } + + // Comparison + setAction({G_ICMP, s1}, Legal); + + for (auto Ty : {s8, s16, s32, s64, s128, p0}) + setAction({G_ICMP, 1, Ty}, Legal); + + // Merge/Unmerge + for (const auto &Ty : {s16, s32, s64}) { + setAction({G_MERGE_VALUES, Ty}, Legal); + setAction({G_UNMERGE_VALUES, 1, Ty}, Legal); + } + for (const auto &Ty : {s8, s16, s32}) { + setAction({G_MERGE_VALUES, 1, Ty}, Legal); + setAction({G_UNMERGE_VALUES, Ty}, Legal); + } +} + +void RISCVLegalizerInfo::setLegalizerInfo64bit() { + if (!Subtarget.is64Bit()) + return; + + const LLT s64 = LLT::scalar(64); + const LLT s128 = LLT::scalar(128); + + setAction({G_IMPLICIT_DEF, s64}, Legal); + + setAction({G_PHI, s64}, Legal); + + for (unsigned BinOp : {G_ADD, G_SUB, G_MUL, G_AND, G_OR, G_XOR}) + setAction({BinOp, s64}, Legal); + + for (unsigned MemOp : {G_LOAD, G_STORE}) + setAction({MemOp, s64}, Legal); + + // Pointer-handling + setAction({G_GEP, 1, s64}, Legal); + + // Constants + setAction({TargetOpcode::G_CONSTANT, s64}, Legal); + + // Extensions + for (unsigned extOp : {G_ZEXT, G_SEXT, G_ANYEXT}) { + setAction({extOp, s64}, Legal); + } + + // Comparison + setAction({G_ICMP, 1, s64}, Legal); + + // Merge/Unmerge + setAction({G_MERGE_VALUES, s128}, Legal); + setAction({G_UNMERGE_VALUES, 1, s128}, Legal); + setAction({G_MERGE_VALUES, 1, s128}, Legal); + setAction({G_UNMERGE_VALUES, s128}, Legal); +} + +bool RISCVLegalizerInfo::legalizeCustom(MachineInstr &MI, + MachineRegisterInfo &MRI, + MachineIRBuilder &MIRBuilder) const { + return true; +} Index: lib/Target/RISCV/RISCVRegisterBankInfo.h =================================================================== --- lib/Target/RISCV/RISCVRegisterBankInfo.h +++ lib/Target/RISCV/RISCVRegisterBankInfo.h @@ -0,0 +1,43 @@ +//===- RISCVRegisterBankInfo -------------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This file declares the targeting of the RegisterBankInfo class for RISCV. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVREGISTERBANKINFO_H +#define LLVM_LIB_TARGET_RISCV_RISCVREGISTERBANKINFO_H + +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" + +#define GET_REGBANK_DECLARATIONS +#include "RISCVGenRegisterBank.inc" + +namespace llvm { + +class TargetRegisterInfo; + +class RISCVGenRegisterBankInfo : public RegisterBankInfo { +#define GET_TARGET_REGBANK_CLASS +#include "RISCVGenRegisterBank.inc" +}; + +/// This class provides the information for the target register banks. +class RISCVRegisterBankInfo final : public RISCVGenRegisterBankInfo { +public: + RISCVRegisterBankInfo(const TargetRegisterInfo &TRI); + + const RegisterBank & + getRegBankFromRegClass(const TargetRegisterClass &RC) const override; + + const InstructionMapping & + getInstrMapping(const MachineInstr &MI) const override; +}; +} // End llvm namespace. +#endif Index: lib/Target/RISCV/RISCVRegisterBankInfo.cpp =================================================================== --- lib/Target/RISCV/RISCVRegisterBankInfo.cpp +++ lib/Target/RISCV/RISCVRegisterBankInfo.cpp @@ -0,0 +1,260 @@ +//===- RISCVRegisterBankInfo.cpp ---------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the RegisterBankInfo class for RISCV. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "RISCVRegisterBankInfo.h" +#include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "RISCVInstrInfo.h" // For the register classes +#include "RISCVSubtarget.h" +#include "llvm/CodeGen/GlobalISel/RegisterBank.h" +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" + +#define GET_TARGET_REGBANK_IMPL +#include "RISCVGenRegisterBank.inc" + +using namespace llvm; + +namespace llvm { +namespace RISCV { +enum PartialMappingIdx { + PMI_GPR, + PMI_FPR32, + PMI_FPR64, + PMI_Min = PMI_GPR, +}; + +RegisterBankInfo::PartialMapping PartMappings[]{ + // GPR Partial Mapping + {0, 32, GPRRegBank}, + // FPR32 Partial Mapping + {0, 32, FPRRegBank}, + // FPR64 Partial Mapping + {0, 64, FPRRegBank}, +}; + +enum ValueMappingIdx { + InvalidIdx = 0, + GPROpsIdx, + FPR32OpsIdx, + FPR64OpsIdx, +}; + +RegisterBankInfo::ValueMapping ValueMappings[] = { + // invalid + {nullptr, 0}, + // GPR + {&PartMappings[PMI_GPR - PMI_Min], 1}, + // FPR32 + {&PartMappings[PMI_FPR32 - PMI_Min], 1}, + // FPR64 + {&PartMappings[PMI_FPR64 - PMI_Min], 1}, +}; +} // end namespace riscv +} // end namespace llvm + +RISCVRegisterBankInfo::RISCVRegisterBankInfo(const TargetRegisterInfo &TRI) + : RISCVGenRegisterBankInfo() {} + +const RegisterBank &RISCVRegisterBankInfo::getRegBankFromRegClass( + const TargetRegisterClass &RC) const { + using namespace RISCV; + + switch (RC.getID()) { + case GPRRegClassID: + case GPRNoX0RegClassID: + return getRegBank(RISCV::GPRRegBankID); + case FPR32RegClassID: + case FPR64RegClassID: + return getRegBank(RISCV::FPRRegBankID); + default: + llvm_unreachable("Unsupported register kind"); + } + + llvm_unreachable("Switch should handle all register classes"); +} + +const RegisterBankInfo::InstructionMapping & +RISCVRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const { + auto Opc = MI.getOpcode(); + + // Try the default logic for non-generic instructions that are either copies + // or already have some operands assigned to banks. + if (!isPreISelGenericOpcode(Opc) || Opc == TargetOpcode::G_PHI) { + const InstructionMapping &Mapping = getInstrMappingImpl(MI); + if (Mapping.isValid()) + return Mapping; + } + + using namespace TargetOpcode; + + const MachineFunction &MF = *MI.getParent()->getParent(); + const MachineRegisterInfo &MRI = MF.getRegInfo(); + unsigned NumOperands = MI.getNumOperands(); + const ValueMapping *OperandsMapping = &RISCV::ValueMappings[RISCV::GPROpsIdx]; + + switch (Opc) { + case G_ADD: + case G_SUB: + case G_MUL: + case G_AND: + case G_OR: + case G_XOR: + case G_LSHR: + case G_ASHR: + case G_SHL: + case G_SDIV: + case G_UDIV: + case G_SEXT: + case G_ZEXT: + case G_ANYEXT: + case G_GEP: + case G_INTTOPTR: + case G_PTRTOINT: + // FIXME: We're abusing the fact that everything lives in a GPR for now; in + // the real world we would use different mappings. + OperandsMapping = &RISCV::ValueMappings[RISCV::GPROpsIdx]; + break; + case G_TRUNC: { + // In some cases we may end up with a G_TRUNC from a 64-bit value to a + // 32-bit value. This isn't a real floating point trunc (that would be a + // G_FPTRUNC). Instead it is an integer trunc in disguise, which can appear + // because the legalizer doesn't distinguish between integer and floating + // point values so it may leave some 64-bit integers un-narrowed. Until we + // have a more principled solution that doesn't let such things sneak all + // the way to this point, just map the source to a DPR and the destination + // to a GPR. + LLT LargeTy = MRI.getType(MI.getOperand(1).getReg()); + OperandsMapping = + LargeTy.getSizeInBits() <= 32 + ? &RISCV::ValueMappings[RISCV::GPROpsIdx] + : getOperandsMapping({&RISCV::ValueMappings[RISCV::GPROpsIdx], + &RISCV::ValueMappings[RISCV::FPR64OpsIdx]}); + break; + } + case G_LOAD: + case G_STORE: { + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + OperandsMapping = + Ty.getSizeInBits() == 64 + ? getOperandsMapping({&RISCV::ValueMappings[RISCV::FPR64OpsIdx], + &RISCV::ValueMappings[RISCV::GPROpsIdx]}) + : &RISCV::ValueMappings[RISCV::GPROpsIdx]; + break; + } + case G_FADD: + case G_FSUB: + case G_FMUL: + case G_FDIV: { + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + OperandsMapping = Ty.getSizeInBits() == 64 + ? &RISCV::ValueMappings[RISCV::FPR64OpsIdx] + : &RISCV::ValueMappings[RISCV::FPR32OpsIdx]; + break; + } + case G_CONSTANT: + case G_FRAME_INDEX: + case G_GLOBAL_VALUE: + OperandsMapping = + getOperandsMapping({&RISCV::ValueMappings[RISCV::GPROpsIdx], nullptr}); + break; + case G_SELECT: { + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + (void)Ty; + LLT Ty2 = MRI.getType(MI.getOperand(1).getReg()); + (void)Ty2; + assert(Ty.getSizeInBits() == 32 && "Unsupported size for G_SELECT"); + assert(Ty2.getSizeInBits() == 1 && "Unsupported size for G_SELECT"); + OperandsMapping = + getOperandsMapping({&RISCV::ValueMappings[RISCV::GPROpsIdx], + &RISCV::ValueMappings[RISCV::GPROpsIdx], + &RISCV::ValueMappings[RISCV::GPROpsIdx], + &RISCV::ValueMappings[RISCV::GPROpsIdx]}); + break; + } + case G_ICMP: { + LLT Ty2 = MRI.getType(MI.getOperand(2).getReg()); + (void)Ty2; + assert(Ty2.getSizeInBits() == 32 || Ty2.getSizeInBits() == 64 || + Ty2.getSizeInBits() == 128 && "Unsupported size for G_ICMP"); + OperandsMapping = + getOperandsMapping({&RISCV::ValueMappings[RISCV::GPROpsIdx], nullptr, + &RISCV::ValueMappings[RISCV::GPROpsIdx], + &RISCV::ValueMappings[RISCV::GPROpsIdx]}); + break; + } + case G_FCMP: { + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + (void)Ty; + LLT Ty1 = MRI.getType(MI.getOperand(2).getReg()); + LLT Ty2 = MRI.getType(MI.getOperand(3).getReg()); + (void)Ty2; + assert(Ty.getSizeInBits() == 1 && "Unsupported size for G_FCMP"); + assert(Ty1.getSizeInBits() == Ty2.getSizeInBits() && + "Mismatched operand sizes for G_FCMP"); + + unsigned Size = Ty1.getSizeInBits(); + assert((Size == 32 || Size == 64) && "Unsupported size for G_FCMP"); + + auto FPRValueMapping = Size == 32 + ? &RISCV::ValueMappings[RISCV::FPR32OpsIdx] + : &RISCV::ValueMappings[RISCV::FPR64OpsIdx]; + OperandsMapping = + getOperandsMapping({&RISCV::ValueMappings[RISCV::GPROpsIdx], nullptr, + FPRValueMapping, FPRValueMapping}); + break; + } + case G_MERGE_VALUES: { + // We only support G_MERGE_VALUES for creating a double precision floating + // point value out of two GPRs. + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + LLT Ty1 = MRI.getType(MI.getOperand(1).getReg()); + LLT Ty2 = MRI.getType(MI.getOperand(2).getReg()); + if (Ty.getSizeInBits() != 64 || Ty1.getSizeInBits() != 32 || + Ty2.getSizeInBits() != 32) + return getInvalidInstructionMapping(); + OperandsMapping = + getOperandsMapping({&RISCV::ValueMappings[RISCV::FPR64OpsIdx], + &RISCV::ValueMappings[RISCV::GPROpsIdx], + &RISCV::ValueMappings[RISCV::GPROpsIdx]}); + break; + } + case G_UNMERGE_VALUES: { + // We only support G_UNMERGE_VALUES for splitting a double precision + // floating point value into two GPRs. + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + LLT Ty1 = MRI.getType(MI.getOperand(1).getReg()); + LLT Ty2 = MRI.getType(MI.getOperand(2).getReg()); + if (Ty.getSizeInBits() != 32 || Ty1.getSizeInBits() != 32 || + Ty2.getSizeInBits() != 64) + return getInvalidInstructionMapping(); + OperandsMapping = + getOperandsMapping({&RISCV::ValueMappings[RISCV::GPROpsIdx], + &RISCV::ValueMappings[RISCV::GPROpsIdx], + &RISCV::ValueMappings[RISCV::FPR64OpsIdx]}); + break; + } + case G_BR: + OperandsMapping = getOperandsMapping({nullptr}); + break; + case G_BRCOND: + OperandsMapping = + getOperandsMapping({&RISCV::ValueMappings[RISCV::GPROpsIdx], nullptr}); + break; + default: + return getInvalidInstructionMapping(); + } + + return getInstructionMapping(DefaultMappingID, /*Cost=*/1, OperandsMapping, + NumOperands); +} Index: lib/Target/RISCV/RISCVRegisterBanks.td =================================================================== --- lib/Target/RISCV/RISCVRegisterBanks.td +++ lib/Target/RISCV/RISCVRegisterBanks.td @@ -0,0 +1,17 @@ +//=- RISCVRegisterBank.td - Describe the RISCV Banks ---------*- tablegen -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +// FIXME: How to support variable-sized register classes? +// https://reviews.llvm.org/D24631 +// GPR64 had been merged into GPR? https://reviews.llvm.org/rL316159 +def GPRRegBank : RegisterBank<"GPRB", [GPR, GPRNoX0, GPRNoX0X2, GPRC]>; +def FPRRegBank : RegisterBank<"FPRB", [FPR32, FPR32C, FPR64, FPR64C]>; Index: lib/Target/RISCV/RISCVSubtarget.h =================================================================== --- lib/Target/RISCV/RISCVSubtarget.h +++ lib/Target/RISCV/RISCVSubtarget.h @@ -20,12 +20,12 @@ #include "llvm/CodeGen/SelectionDAGTargetInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/IR/DataLayout.h" -#include "llvm/Target/TargetMachine.h" #define GET_SUBTARGETINFO_HEADER #include "RISCVGenSubtargetInfo.inc" namespace llvm { +class RISCVTargetMachine; class StringRef; class RISCVSubtarget : public RISCVGenSubtargetInfo { @@ -52,7 +52,7 @@ public: // Initializes the data members to match that of the specified triple. RISCVSubtarget(const Triple &TT, const std::string &CPU, - const std::string &FS, const TargetMachine &TM); + const std::string &FS, const RISCVTargetMachine &TM); // Parses features string setting specified subtarget options. The // definition of this function is auto-generated by tblgen. @@ -71,6 +71,11 @@ const SelectionDAGTargetInfo *getSelectionDAGInfo() const override { return &TSInfo; } + const CallLowering *getCallLowering() const override; + const InstructionSelector *getInstructionSelector() const override; + const LegalizerInfo *getLegalizerInfo() const override; + const RegisterBankInfo *getRegBankInfo() const override; + bool hasStdExtM() const { return HasStdExtM; } bool hasStdExtA() const { return HasStdExtA; } bool hasStdExtF() const { return HasStdExtF; } @@ -79,6 +84,13 @@ bool is64Bit() const { return HasRV64; } MVT getXLenVT() const { return XLenVT; } unsigned getXLen() const { return XLen; } + +private: + /// GlobalISel related APIs. + std::unique_ptr CallLoweringInfo; + std::unique_ptr InstSelector; + std::unique_ptr Legalizer; + std::unique_ptr RegBankInfo; }; } // End llvm namespace Index: lib/Target/RISCV/RISCVSubtarget.cpp =================================================================== --- lib/Target/RISCV/RISCVSubtarget.cpp +++ lib/Target/RISCV/RISCVSubtarget.cpp @@ -11,9 +11,14 @@ // //===----------------------------------------------------------------------===// +#include "RISCVCallLowering.h" +#include "RISCVLegalizerInfo.h" +#include "RISCVRegisterBankInfo.h" #include "RISCVSubtarget.h" +#include "RISCVTargetMachine.h" #include "RISCV.h" #include "RISCVFrameLowering.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelect.h" #include "llvm/Support/TargetRegistry.h" using namespace llvm; @@ -42,7 +47,33 @@ } RISCVSubtarget::RISCVSubtarget(const Triple &TT, const std::string &CPU, - const std::string &FS, const TargetMachine &TM) + const std::string &FS, + const RISCVTargetMachine &TM) : RISCVGenSubtargetInfo(TT, CPU, FS), FrameLowering(initializeSubtargetDependencies(CPU, FS, TT.isArch64Bit())), - InstrInfo(), RegInfo(getHwMode()), TLInfo(TM, *this) {} + InstrInfo(), RegInfo(getHwMode()), TLInfo(TM, *this) { + CallLoweringInfo.reset(new RISCVCallLowering(*getTargetLowering())); + Legalizer.reset(new RISCVLegalizerInfo(*this, TM)); + + auto *RBI = new RISCVRegisterBankInfo(*getRegisterInfo()); + InstSelector.reset(createRISCVInstructionSelector( + *static_cast(&TM), *this, *RBI)); + + RegBankInfo.reset(RBI); +} + +const CallLowering *RISCVSubtarget::getCallLowering() const { + return CallLoweringInfo.get(); +} + +const InstructionSelector *RISCVSubtarget::getInstructionSelector() const { + return InstSelector.get(); +} + +const LegalizerInfo *RISCVSubtarget::getLegalizerInfo() const { + return Legalizer.get(); +} + +const RegisterBankInfo *RISCVSubtarget::getRegBankInfo() const { + return RegBankInfo.get(); +} Index: lib/Target/RISCV/RISCVTargetMachine.cpp =================================================================== --- lib/Target/RISCV/RISCVTargetMachine.cpp +++ lib/Target/RISCV/RISCVTargetMachine.cpp @@ -14,6 +14,14 @@ #include "RISCV.h" #include "RISCVTargetMachine.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/CodeGen/GlobalISel/CallLowering.h" +#include "llvm/CodeGen/GlobalISel/IRTranslator.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelect.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/GlobalISel/Legalizer.h" +#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" +#include "llvm/CodeGen/GlobalISel/RegBankSelect.h" +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/CodeGen/TargetPassConfig.h" @@ -26,6 +34,9 @@ extern "C" void LLVMInitializeRISCVTarget() { RegisterTargetMachine X(getTheRISCV32Target()); RegisterTargetMachine Y(getTheRISCV64Target()); + + PassRegistry &Registry = *PassRegistry::getPassRegistry(); + initializeGlobalISel(Registry); } static std::string computeDataLayout(const Triple &TT) { @@ -74,7 +85,11 @@ return getTM(); } + bool addGlobalInstructionSelect() override; bool addInstSelector() override; + bool addIRTranslator() override; + bool addLegalizeMachineIR() override; + bool addRegBankSelect() override; }; } @@ -82,8 +97,28 @@ return new RISCVPassConfig(*this, PM); } +bool RISCVPassConfig::addGlobalInstructionSelect() { + addPass(new InstructionSelect()); + return false; +} + bool RISCVPassConfig::addInstSelector() { addPass(createRISCVISelDag(getRISCVTargetMachine())); return false; } + +bool RISCVPassConfig::addIRTranslator() { + addPass(new IRTranslator()); + return false; +} + +bool RISCVPassConfig::addLegalizeMachineIR() { + addPass(new Legalizer()); + return false; +} + +bool RISCVPassConfig::addRegBankSelect() { + addPass(new RegBankSelect()); + return false; +}