This PR is to add vector reduction instructions according to https://llvm.org/docs/GlobalISel/GenericOpcode.html#vector-reduction-operations and widen in such a way a range of successful supported conversions, covering new cases of vector reduction instructions which IRTranslator is unable to resolve. By legalizing vector reduction instructions we introduce a new instruction patterns that should be addressed, including patterns that are delegated to pre-legalize step. To address this problem, a new pass is added that is to bring newly generated instructions after legalization to an aspect required by instruction selection. Expected overheads for existing cases is minimal, because a new pass is working only with newly introduced instructions, otherwise it's just a additional code traverse without any actions.
171 lines
6.4 KiB
C++
171 lines
6.4 KiB
C++
//===-- SPIRVPostLegalizer.cpp - ammend info after legalization -*- C++ -*-===//
|
|
//
|
|
// which may appear after the legalizer pass
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// The pass partially apply pre-legalization logic to new instructions inserted
|
|
// as a result of legalization:
|
|
// - assigns SPIR-V types to registers for new instructions.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "SPIRV.h"
|
|
#include "SPIRVSubtarget.h"
|
|
#include "SPIRVUtils.h"
|
|
#include "llvm/ADT/PostOrderIterator.h"
|
|
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
|
|
#include "llvm/IR/Attributes.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DebugInfoMetadata.h"
|
|
#include "llvm/IR/IntrinsicsSPIRV.h"
|
|
#include "llvm/Target/TargetIntrinsicInfo.h"
|
|
|
|
#define DEBUG_TYPE "spirv-postlegalizer"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
class SPIRVPostLegalizer : public MachineFunctionPass {
|
|
public:
|
|
static char ID;
|
|
SPIRVPostLegalizer() : MachineFunctionPass(ID) {
|
|
initializeSPIRVPostLegalizerPass(*PassRegistry::getPassRegistry());
|
|
}
|
|
bool runOnMachineFunction(MachineFunction &MF) override;
|
|
};
|
|
} // namespace
|
|
|
|
// Defined in SPIRVLegalizerInfo.cpp.
|
|
extern bool isTypeFoldingSupported(unsigned Opcode);
|
|
|
|
namespace llvm {
|
|
// Defined in SPIRVPreLegalizer.cpp.
|
|
extern Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,
|
|
SPIRVGlobalRegistry *GR,
|
|
MachineIRBuilder &MIB,
|
|
MachineRegisterInfo &MRI);
|
|
extern void processInstr(MachineInstr &MI, MachineIRBuilder &MIB,
|
|
MachineRegisterInfo &MRI, SPIRVGlobalRegistry *GR);
|
|
} // namespace llvm
|
|
|
|
static bool isMetaInstrGET(unsigned Opcode) {
|
|
return Opcode == SPIRV::GET_ID || Opcode == SPIRV::GET_fID ||
|
|
Opcode == SPIRV::GET_pID || Opcode == SPIRV::GET_vID ||
|
|
Opcode == SPIRV::GET_vfID;
|
|
}
|
|
|
|
static bool mayBeInserted(unsigned Opcode) {
|
|
switch (Opcode) {
|
|
case TargetOpcode::G_SMAX:
|
|
case TargetOpcode::G_UMAX:
|
|
case TargetOpcode::G_SMIN:
|
|
case TargetOpcode::G_UMIN:
|
|
case TargetOpcode::G_FMINNUM:
|
|
case TargetOpcode::G_FMINIMUM:
|
|
case TargetOpcode::G_FMAXNUM:
|
|
case TargetOpcode::G_FMAXIMUM:
|
|
return true;
|
|
default:
|
|
return isTypeFoldingSupported(Opcode);
|
|
}
|
|
}
|
|
|
|
static void processNewInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
|
|
MachineIRBuilder MIB) {
|
|
MachineRegisterInfo &MRI = MF.getRegInfo();
|
|
|
|
for (MachineBasicBlock &MBB : MF) {
|
|
for (MachineInstr &I : MBB) {
|
|
const unsigned Opcode = I.getOpcode();
|
|
if (Opcode == TargetOpcode::G_UNMERGE_VALUES) {
|
|
unsigned ArgI = I.getNumOperands() - 1;
|
|
Register SrcReg = I.getOperand(ArgI).isReg()
|
|
? I.getOperand(ArgI).getReg()
|
|
: Register(0);
|
|
SPIRVType *DefType =
|
|
SrcReg.isValid() ? GR->getSPIRVTypeForVReg(SrcReg) : nullptr;
|
|
if (!DefType || DefType->getOpcode() != SPIRV::OpTypeVector)
|
|
report_fatal_error(
|
|
"cannot select G_UNMERGE_VALUES with a non-vector argument");
|
|
SPIRVType *ScalarType =
|
|
GR->getSPIRVTypeForVReg(DefType->getOperand(1).getReg());
|
|
for (unsigned i = 0; i < I.getNumDefs(); ++i) {
|
|
Register ResVReg = I.getOperand(i).getReg();
|
|
SPIRVType *ResType = GR->getSPIRVTypeForVReg(ResVReg);
|
|
if (!ResType) {
|
|
// There was no "assign type" actions, let's fix this now
|
|
ResType = ScalarType;
|
|
MRI.setRegClass(ResVReg, &SPIRV::IDRegClass);
|
|
MRI.setType(ResVReg,
|
|
LLT::scalar(GR->getScalarOrVectorBitWidth(ResType)));
|
|
GR->assignSPIRVTypeToVReg(ResType, ResVReg, *GR->CurMF);
|
|
}
|
|
}
|
|
} else if (mayBeInserted(Opcode) && I.getNumDefs() == 1 &&
|
|
I.getNumOperands() > 1 && I.getOperand(1).isReg()) {
|
|
// Legalizer may have added a new instructions and introduced new
|
|
// registers, we must decorate them as if they were introduced in a
|
|
// non-automatic way
|
|
Register ResVReg = I.getOperand(0).getReg();
|
|
SPIRVType *ResVType = GR->getSPIRVTypeForVReg(ResVReg);
|
|
// Check if the register defined by the instruction is newly generated
|
|
// or already processed
|
|
if (!ResVType) {
|
|
// Set type of the defined register
|
|
ResVType = GR->getSPIRVTypeForVReg(I.getOperand(1).getReg());
|
|
// Check if we have type defined for operands of the new instruction
|
|
if (!ResVType)
|
|
continue;
|
|
// Set type & class
|
|
MRI.setRegClass(ResVReg, &SPIRV::IDRegClass);
|
|
MRI.setType(ResVReg,
|
|
LLT::scalar(GR->getScalarOrVectorBitWidth(ResVType)));
|
|
GR->assignSPIRVTypeToVReg(ResVType, ResVReg, *GR->CurMF);
|
|
}
|
|
// If this is a simple operation that is to be reduced by TableGen
|
|
// definition we must apply some of pre-legalizer rules here
|
|
if (isTypeFoldingSupported(Opcode)) {
|
|
// Check if the instruction newly generated or already processed
|
|
MachineInstr *NextMI = I.getNextNode();
|
|
if (NextMI && isMetaInstrGET(NextMI->getOpcode()))
|
|
continue;
|
|
// Restore usual instructions pattern for the newly inserted
|
|
// instruction
|
|
MRI.setRegClass(ResVReg, MRI.getType(ResVReg).isVector()
|
|
? &SPIRV::IDRegClass
|
|
: &SPIRV::ANYIDRegClass);
|
|
MRI.setType(ResVReg, LLT::scalar(32));
|
|
insertAssignInstr(ResVReg, nullptr, ResVType, GR, MIB, MRI);
|
|
processInstr(I, MIB, MRI, GR);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SPIRVPostLegalizer::runOnMachineFunction(MachineFunction &MF) {
|
|
// Initialize the type registry.
|
|
const SPIRVSubtarget &ST = MF.getSubtarget<SPIRVSubtarget>();
|
|
SPIRVGlobalRegistry *GR = ST.getSPIRVGlobalRegistry();
|
|
GR->setCurrentFunc(MF);
|
|
MachineIRBuilder MIB(MF);
|
|
|
|
processNewInstrs(MF, GR, MIB);
|
|
|
|
return true;
|
|
}
|
|
|
|
INITIALIZE_PASS(SPIRVPostLegalizer, DEBUG_TYPE, "SPIRV post legalizer", false,
|
|
false)
|
|
|
|
char SPIRVPostLegalizer::ID = 0;
|
|
|
|
FunctionPass *llvm::createSPIRVPostLegalizerPass() {
|
|
return new SPIRVPostLegalizer();
|
|
}
|