blob: 6cd93fed19c9219239634f7b289fc64e1002e054 [file] [edit]
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file contains NVVM-specific IR verification logic. These checks are
/// always compiled and linked as part of LLVMCore.
///
//===----------------------------------------------------------------------===//
#include "VerifierInternal.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/IntrinsicsNVPTX.h"
#include "llvm/Support/MathExtras.h"
#include <optional>
using namespace llvm;
#define Check(C, ...) \
do { \
if (!(C)) { \
VS.CheckFailed(__VA_ARGS__); \
return; \
} \
} while (false)
namespace {
struct SPVectorInfo {
unsigned ElemSize;
unsigned NumElements;
unsigned NumRegisters;
};
// Register layout of the sparse intrinsic operands, used by the IR verifier.
struct SPOperandLayout {
unsigned MetadataSize;
unsigned CompressedDataSize;
unsigned DataSize;
};
} // namespace
// PTX limits the combined vector size of the mdata, cdata, and data operands
// of spcompress and spdecompress to 253 32-bit registers.
constexpr unsigned MaxSPOperandRegisters = 253;
static bool isValidSPElemSize(unsigned ElemSize) {
return ElemSize == 8 || ElemSize == 16;
}
static bool isValidSPIdxSize(unsigned IdxSize) {
return IdxSize == 2 || IdxSize == 4;
}
static bool isValidSPRepeatFactor(unsigned RepeatFactor) {
return isPowerOf2_32(RepeatFactor) && RepeatFactor <= 64;
}
static bool isValidSPDecompressFactor(unsigned NumSrc, unsigned NumTgt) {
switch (NumSrc) {
case 1:
return NumTgt == 2 || NumTgt == 4 || NumTgt == 8 || NumTgt == 16;
case 2:
return NumTgt == 4 || NumTgt == 8 || NumTgt == 16;
case 4:
return NumTgt == 8 || NumTgt == 16;
default:
return false;
}
}
static std::optional<SPOperandLayout>
getSPCompressLayout(unsigned ElemSize, unsigned IdxSize,
unsigned RepeatFactor) {
if (!isValidSPElemSize(ElemSize) || !isValidSPIdxSize(IdxSize) ||
!isValidSPRepeatFactor(RepeatFactor))
return std::nullopt;
SPOperandLayout Layout = {divideCeil(RepeatFactor * IdxSize, ElemSize),
RepeatFactor, RepeatFactor * 2};
if (Layout.MetadataSize + Layout.CompressedDataSize + Layout.DataSize >
MaxSPOperandRegisters)
return std::nullopt;
return Layout;
}
static std::optional<SPOperandLayout>
getSPDecompressLayout(unsigned NumSrc, unsigned NumTgt, unsigned ElemSize,
unsigned IdxSize, unsigned RepeatFactor) {
if (!isValidSPDecompressFactor(NumSrc, NumTgt) ||
!isValidSPElemSize(ElemSize) || !isValidSPIdxSize(IdxSize) ||
!isValidSPRepeatFactor(RepeatFactor) || NumSrc * ElemSize > 32 ||
(IdxSize == 2 && NumTgt > 4))
return std::nullopt;
unsigned DataBits = NumTgt * ElemSize * RepeatFactor;
if (DataBits < 32 || DataBits > 4096)
return std::nullopt;
SPOperandLayout Layout = {divideCeil(NumSrc * IdxSize * RepeatFactor, 32),
divideCeil(NumSrc * ElemSize * RepeatFactor, 32),
divideCeil(DataBits, 32)};
if (Layout.MetadataSize + Layout.CompressedDataSize + Layout.DataSize >
MaxSPOperandRegisters)
return std::nullopt;
return Layout;
}
static std::optional<SPVectorInfo> getSPVectorInfo(Type *Ty) {
auto *VT = dyn_cast<FixedVectorType>(Ty);
if (!VT)
return std::nullopt;
auto *ElemTy = dyn_cast<IntegerType>(VT->getElementType());
if (!ElemTy)
return std::nullopt;
unsigned ElemSize = ElemTy->getBitWidth();
if (ElemSize != 8 && ElemSize != 16)
return std::nullopt;
unsigned NumElements = VT->getNumElements();
return SPVectorInfo{ElemSize, NumElements,
divideCeil(NumElements, 32 / ElemSize)};
}
static std::optional<unsigned> getSPMetadataRegisters(Type *Ty) {
if (Ty->isIntegerTy(32))
return 1;
auto *VT = dyn_cast<FixedVectorType>(Ty);
if (!VT || !VT->getElementType()->isIntegerTy(32) || VT->getNumElements() < 2)
return std::nullopt;
return VT->getNumElements();
}
static void verifySPCompress(VerifierSupport &VS, CallBase &Call) {
auto *ResultTy = dyn_cast<StructType>(Call.getType());
Check(ResultTy && ResultTy->getNumElements() == 2,
"invalid llvm.nvvm.spcompress result type", &Call);
auto MDataRegs = getSPMetadataRegisters(ResultTy->getElementType(0));
auto CData = getSPVectorInfo(ResultTy->getElementType(1));
auto Data = getSPVectorInfo(Call.getArgOperand(0)->getType());
Check(MDataRegs && CData && Data && CData->ElemSize == Data->ElemSize,
"invalid llvm.nvvm.spcompress operand or result type", &Call);
unsigned IdxSize = cast<ConstantInt>(Call.getArgOperand(2))->getZExtValue();
unsigned NumTgt = cast<ConstantInt>(Call.getArgOperand(3))->getZExtValue();
// spcompress only implements the 2:4 pattern: each group of num_tgt = 4
// data elements keeps 2 of them in cdata. The repeat factor counts pairs of
// data registers, so data must occupy an even number of them.
Check(NumTgt == 4 && Data->NumElements % NumTgt == 0 &&
CData->NumElements == 2 * (Data->NumElements / NumTgt) &&
Data->NumRegisters % 2 == 0,
"invalid llvm.nvvm.spcompress layout", &Call);
unsigned RepeatFactor = Data->NumRegisters / 2;
auto Layout = getSPCompressLayout(Data->ElemSize, IdxSize, RepeatFactor);
// The declared types must match the register layout PTX gives these
// qualifiers.
Check(Layout && *MDataRegs == Layout->MetadataSize &&
CData->NumRegisters == Layout->CompressedDataSize &&
Data->NumRegisters == Layout->DataSize,
"invalid llvm.nvvm.spcompress layout", &Call);
}
static void verifySPDecompress(VerifierSupport &VS, CallBase &Call) {
auto Data = getSPVectorInfo(Call.getType());
auto MDataRegs = getSPMetadataRegisters(Call.getArgOperand(0)->getType());
auto CData = getSPVectorInfo(Call.getArgOperand(1)->getType());
Check(Data && MDataRegs && CData && Data->ElemSize == CData->ElemSize,
"invalid llvm.nvvm.spdecompress operand or result type", &Call);
unsigned IdxSize = cast<ConstantInt>(Call.getArgOperand(2))->getZExtValue();
unsigned NumTgt = cast<ConstantInt>(Call.getArgOperand(3))->getZExtValue();
// data holds repeat_factor groups of num_tgt elements.
Check(NumTgt != 0 && Data->NumElements % NumTgt == 0,
"invalid llvm.nvvm.spdecompress layout", &Call);
unsigned RepeatFactor = Data->NumElements / NumTgt;
// cdata holds num_src elements for each of those groups.
Check(RepeatFactor != 0 && CData->NumElements % RepeatFactor == 0,
"invalid llvm.nvvm.spdecompress layout", &Call);
unsigned NumSrc = CData->NumElements / RepeatFactor;
auto Layout = getSPDecompressLayout(NumSrc, NumTgt, Data->ElemSize, IdxSize,
RepeatFactor);
// The declared types must match the register layout PTX gives these
// qualifiers.
Check(Layout && *MDataRegs == Layout->MetadataSize &&
CData->NumRegisters == Layout->CompressedDataSize &&
Data->NumRegisters == Layout->DataSize,
"invalid llvm.nvvm.spdecompress layout", &Call);
}
void llvm::verifyNVVMIntrinsicCall(VerifierSupport &VS, Intrinsic::ID ID,
CallBase &Call) {
switch (ID) {
default:
return;
case Intrinsic::nvvm_spcompress:
verifySPCompress(VS, Call);
return;
case Intrinsic::nvvm_spdecompress:
verifySPDecompress(VS, Call);
return;
}
}
#undef Check