This PR contains changes in virtual register processing aimed to improve correctness of emitted MIR between passes from the perspective of MachineVerifier. This potentially helps to detect previously missed flaws in code emission and harden the test suite. As a measure of correctness and usefulness of this PR we may use a mode with expensive checks set on, and MachineVerifier reports problems in the test suite. In order to satisfy Machine Verifier requirements to MIR correctness not only a rework of usage of virtual registers' types and classes is required, but also corrections into pre-legalizer and instruction selection logics. Namely, the following changes are introduced: * scalar virtual registers have proper bit width, * detect register class by SPIR-V type, * add a superclass for id virtual register classes, * fix Tablegen rules used for instruction selection, * fixes of minor existed issues (missed flag for proper representation of a null constant for OpenCL vs. HLSL, wrong usage of integer virtual registers as a synonym of any non-type virtual register).
173 lines
6.6 KiB
C++
173 lines
6.6 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_ID64 ||
|
|
Opcode == SPIRV::GET_fID || Opcode == SPIRV::GET_fID64 ||
|
|
Opcode == SPIRV::GET_pID32 || Opcode == SPIRV::GET_pID64 ||
|
|
Opcode == SPIRV::GET_vID || Opcode == SPIRV::GET_vfID ||
|
|
Opcode == SPIRV::GET_vpID32 || Opcode == SPIRV::GET_vpID64;
|
|
}
|
|
|
|
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::iIDRegClass);
|
|
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::iIDRegClass);
|
|
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::iIDRegClass
|
|
: &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();
|
|
}
|