blob: c06792ca53b0a103c6b52d3d0d8f18684d28edcc [file] [edit]
//===- GPUBase.cpp - GPU types and attributes -----------------------------===//
//
// 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 implements GPU types, attributes, and enums.
//
//===----------------------------------------------------------------------===//
#include "mlir/Dialect/GPU/IR/GPUBase.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/DialectImplementation.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/StringSaver.h"
using namespace mlir;
using namespace mlir::gpu;
//===----------------------------------------------------------------------===//
// GPU Device Mapping Attributes
//===----------------------------------------------------------------------===//
int64_t GPUBlockMappingAttr::getMappingId() const {
return static_cast<int64_t>(getBlock());
}
bool GPUBlockMappingAttr::isLinearMapping() const {
return getMappingId() >= static_cast<int64_t>(MappingId::LinearDim0);
}
int64_t GPUBlockMappingAttr::getRelativeIndex() const {
return isLinearMapping()
? getMappingId() - static_cast<int64_t>(MappingId::LinearDim0)
: getMappingId();
}
int64_t GPUWarpgroupMappingAttr::getMappingId() const {
return static_cast<int64_t>(getWarpgroup());
}
bool GPUWarpgroupMappingAttr::isLinearMapping() const {
return getMappingId() >= static_cast<int64_t>(MappingId::LinearDim0);
}
int64_t GPUWarpgroupMappingAttr::getRelativeIndex() const {
return isLinearMapping()
? getMappingId() - static_cast<int64_t>(MappingId::LinearDim0)
: getMappingId();
}
int64_t GPUWarpMappingAttr::getMappingId() const {
return static_cast<int64_t>(getWarp());
}
bool GPUWarpMappingAttr::isLinearMapping() const {
return getMappingId() >= static_cast<int64_t>(MappingId::LinearDim0);
}
int64_t GPUWarpMappingAttr::getRelativeIndex() const {
return isLinearMapping()
? getMappingId() - static_cast<int64_t>(MappingId::LinearDim0)
: getMappingId();
}
int64_t GPUThreadMappingAttr::getMappingId() const {
return static_cast<int64_t>(getThread());
}
bool GPUThreadMappingAttr::isLinearMapping() const {
return getMappingId() >= static_cast<int64_t>(MappingId::LinearDim0);
}
int64_t GPUThreadMappingAttr::getRelativeIndex() const {
return isLinearMapping()
? getMappingId() - static_cast<int64_t>(MappingId::LinearDim0)
: getMappingId();
}
int64_t GPULaneMappingAttr::getMappingId() const {
return static_cast<int64_t>(getLane());
}
bool GPULaneMappingAttr::isLinearMapping() const {
return getMappingId() >= static_cast<int64_t>(MappingId::LinearDim0);
}
int64_t GPULaneMappingAttr::getRelativeIndex() const {
return isLinearMapping()
? getMappingId() - static_cast<int64_t>(MappingId::LinearDim0)
: getMappingId();
}
int64_t GPUMemorySpaceMappingAttr::getMappingId() const {
return static_cast<int64_t>(getAddressSpace());
}
bool GPUMemorySpaceMappingAttr::isLinearMapping() const {
llvm_unreachable("GPUMemorySpaceMappingAttr does not support linear mapping");
}
int64_t GPUMemorySpaceMappingAttr::getRelativeIndex() const {
llvm_unreachable("GPUMemorySpaceMappingAttr does not support relative index");
}
//===----------------------------------------------------------------------===//
// MMAMatrixType
//===----------------------------------------------------------------------===//
MMAMatrixType MMAMatrixType::get(ArrayRef<int64_t> shape, Type elementType,
StringRef operand) {
return Base::get(elementType.getContext(), shape, elementType, operand);
}
MMAMatrixType
MMAMatrixType::getChecked(function_ref<InFlightDiagnostic()> emitError,
ArrayRef<int64_t> shape, Type elementType,
StringRef operand) {
return Base::getChecked(emitError, elementType.getContext(), shape,
elementType, operand);
}
unsigned MMAMatrixType::getNumDims() const { return getImpl()->numDims; }
ArrayRef<int64_t> MMAMatrixType::getShape() const {
return getImpl()->getShape();
}
Type MMAMatrixType::getElementType() const { return getImpl()->elementType; }
StringRef MMAMatrixType::getOperand() const { return getImpl()->getOperand(); }
bool MMAMatrixType::isValidElementType(Type elementType) {
return elementType.isF16() || elementType.isF32() || elementType.isF64() ||
elementType.isUnsignedInteger(8) || elementType.isSignedInteger(8) ||
elementType.isInteger(32);
}
LogicalResult
MMAMatrixType::verifyInvariants(function_ref<InFlightDiagnostic()> emitError,
ArrayRef<int64_t> shape, Type elementType,
StringRef operand) {
if (operand != "AOp" && operand != "BOp" && operand != "COp")
return emitError() << "operand expected to be one of AOp, BOp or COp";
if (shape.size() != 2)
return emitError() << "MMAMatrixType must have exactly two dimensions";
if (!MMAMatrixType::isValidElementType(elementType))
return emitError()
<< "MMAMatrixType elements must be SI8, UI8, I32, F16, F32, or F64";
return success();
}
//===----------------------------------------------------------------------===//
// GPUDialect
//===----------------------------------------------------------------------===//
bool GPUDialect::isWorkgroupMemoryAddressSpace(Attribute memorySpace) {
if (!memorySpace)
return false;
if (auto gpuAttr = llvm::dyn_cast<gpu::AddressSpaceAttr>(memorySpace))
return gpuAttr.getValue() == getWorkgroupAddressSpace();
return false;
}
bool GPUDialect::hasWorkgroupMemoryAddressSpace(MemRefType type) {
Attribute memorySpace = type.getMemorySpace();
return isWorkgroupMemoryAddressSpace(memorySpace);
}
bool GPUDialect::isConstantMemoryAddressSpace(Attribute memorySpace) {
if (!memorySpace)
return false;
if (auto gpuAttr = llvm::dyn_cast<gpu::AddressSpaceAttr>(memorySpace))
return gpuAttr.getValue() == getConstantAddressSpace();
return false;
}
bool GPUDialect::hasConstantMemoryAddressSpace(MemRefType type) {
Attribute memorySpace = type.getMemorySpace();
return isConstantMemoryAddressSpace(memorySpace);
}
//===----------------------------------------------------------------------===//
// GPU object attribute
//===----------------------------------------------------------------------===//
LogicalResult ObjectAttr::verify(function_ref<InFlightDiagnostic()> emitError,
Attribute target, CompilationTarget format,
StringAttr object, DictionaryAttr properties,
KernelTableAttr kernels) {
if (!target)
return emitError() << "the target attribute cannot be null";
if (target.hasPromiseOrImplementsInterface<TargetAttrInterface>())
return success();
return emitError() << "the target attribute must implement or promise the "
"`gpu::TargetAttrInterface`";
}
namespace {
ParseResult parseObject(AsmParser &odsParser, CompilationTarget &format,
StringAttr &object) {
std::optional<CompilationTarget> formatResult;
StringRef enumKeyword;
auto loc = odsParser.getCurrentLocation();
if (failed(odsParser.parseOptionalKeyword(&enumKeyword)))
formatResult = CompilationTarget::Fatbin;
if (!formatResult &&
(formatResult =
gpu::symbolizeEnum<gpu::CompilationTarget>(enumKeyword)) &&
odsParser.parseEqual())
return odsParser.emitError(loc, "expected an equal sign");
if (!formatResult)
return odsParser.emitError(loc, "expected keyword for GPU object format");
FailureOr<StringAttr> objectResult =
FieldParser<StringAttr>::parse(odsParser);
if (failed(objectResult))
return odsParser.emitError(odsParser.getCurrentLocation(),
"failed to parse GPU_ObjectAttr parameter "
"'object' which is to be a `StringAttr`");
format = *formatResult;
object = *objectResult;
return success();
}
void printObject(AsmPrinter &odsParser, CompilationTarget format,
StringAttr object) {
if (format != CompilationTarget::Fatbin)
odsParser << stringifyEnum(format) << " = ";
odsParser << object;
}
} // namespace
//===----------------------------------------------------------------------===//
// GPU select object attribute
//===----------------------------------------------------------------------===//
LogicalResult
gpu::SelectObjectAttr::verify(function_ref<InFlightDiagnostic()> emitError,
Attribute target) {
// Check `target`, it can be null, an integer attr or a GPU Target attribute.
if (target) {
if (auto intAttr = mlir::dyn_cast<IntegerAttr>(target)) {
if (intAttr.getInt() < 0) {
return emitError() << "the object index must be positive";
}
} else if (!target.hasPromiseOrImplementsInterface<TargetAttrInterface>()) {
return emitError()
<< "the target attribute must be a GPU Target attribute";
}
}
return success();
}
//===----------------------------------------------------------------------===//
// GPU KernelMetadataAttr
//===----------------------------------------------------------------------===//
KernelMetadataAttr KernelMetadataAttr::get(FunctionOpInterface kernel,
DictionaryAttr metadata) {
assert(kernel && "invalid kernel");
return get(kernel.getNameAttr(), kernel.getFunctionType(),
kernel.getAllArgAttrs(), metadata);
}
KernelMetadataAttr
KernelMetadataAttr::getChecked(function_ref<InFlightDiagnostic()> emitError,
FunctionOpInterface kernel,
DictionaryAttr metadata) {
assert(kernel && "invalid kernel");
return getChecked(emitError, kernel.getNameAttr(), kernel.getFunctionType(),
kernel.getAllArgAttrs(), metadata);
}
KernelMetadataAttr
KernelMetadataAttr::appendMetadata(ArrayRef<NamedAttribute> attrs) const {
if (attrs.empty())
return *this;
NamedAttrList attrList;
if (DictionaryAttr dict = getMetadata())
attrList.append(dict);
attrList.append(attrs);
return KernelMetadataAttr::get(getName(), getFunctionType(), getArgAttrs(),
attrList.getDictionary(getContext()));
}
LogicalResult
KernelMetadataAttr::verify(function_ref<InFlightDiagnostic()> emitError,
StringAttr name, Type functionType,
ArrayAttr argAttrs, DictionaryAttr metadata) {
if (name.empty())
return emitError() << "the kernel name can't be empty";
if (argAttrs) {
if (llvm::any_of(argAttrs, [](Attribute attr) {
return !llvm::isa<DictionaryAttr>(attr);
}))
return emitError()
<< "all attributes in the array must be a dictionary attribute";
}
return success();
}
//===----------------------------------------------------------------------===//
// GPU KernelTableAttr
//===----------------------------------------------------------------------===//
KernelTableAttr KernelTableAttr::get(MLIRContext *context,
ArrayRef<KernelMetadataAttr> kernels,
bool isSorted) {
// Note that `is_sorted` is always only invoked once even with assertions ON.
assert((!isSorted || llvm::is_sorted(kernels)) &&
"expected a sorted kernel array");
// Immediately return the attribute if the array is sorted.
if (isSorted || llvm::is_sorted(kernels))
return Base::get(context, kernels);
// Sort the array.
SmallVector<KernelMetadataAttr> kernelsTmp(kernels);
llvm::array_pod_sort(kernelsTmp.begin(), kernelsTmp.end());
return Base::get(context, kernelsTmp);
}
KernelTableAttr KernelTableAttr::getChecked(
function_ref<InFlightDiagnostic()> emitError, MLIRContext *context,
ArrayRef<KernelMetadataAttr> kernels, bool isSorted) {
// Note that `is_sorted` is always only invoked once even with assertions ON.
assert((!isSorted || llvm::is_sorted(kernels)) &&
"expected a sorted kernel array");
// Immediately return the attribute if the array is sorted.
if (isSorted || llvm::is_sorted(kernels))
return Base::getChecked(emitError, context, kernels);
// Sort the array.
SmallVector<KernelMetadataAttr> kernelsTmp(kernels);
llvm::array_pod_sort(kernelsTmp.begin(), kernelsTmp.end());
return Base::getChecked(emitError, context, kernelsTmp);
}
LogicalResult
KernelTableAttr::verify(function_ref<InFlightDiagnostic()> emitError,
ArrayRef<KernelMetadataAttr> kernels) {
if (kernels.size() < 2)
return success();
// Check that the kernels are uniquely named.
if (std::adjacent_find(kernels.begin(), kernels.end(),
[](KernelMetadataAttr l, KernelMetadataAttr r) {
return l.getName() == r.getName();
}) != kernels.end()) {
return emitError() << "expected all kernels to be uniquely named";
}
return success();
}
KernelMetadataAttr KernelTableAttr::lookup(StringRef key) const {
auto [iterator, found] = impl::findAttrSorted(begin(), end(), key);
return found ? *iterator : KernelMetadataAttr();
}
KernelMetadataAttr KernelTableAttr::lookup(StringAttr key) const {
auto [iterator, found] = impl::findAttrSorted(begin(), end(), key);
return found ? *iterator : KernelMetadataAttr();
}
//===----------------------------------------------------------------------===//
// GPU target options
//===----------------------------------------------------------------------===//
TargetOptions::TargetOptions(
StringRef toolkitPath, ArrayRef<Attribute> librariesToLink,
StringRef cmdOptions, StringRef elfSection,
CompilationTarget compilationTarget,
function_ref<SymbolTable *()> getSymbolTableCallback,
function_ref<void(llvm::Module &)> initialLlvmIRCallback,
function_ref<void(llvm::Module &)> linkedLlvmIRCallback,
function_ref<void(llvm::Module &)> optimizedLlvmIRCallback,
function_ref<void(StringRef)> isaCallback)
: TargetOptions(TypeID::get<TargetOptions>(), toolkitPath, librariesToLink,
cmdOptions, elfSection, compilationTarget,
getSymbolTableCallback, initialLlvmIRCallback,
linkedLlvmIRCallback, optimizedLlvmIRCallback,
isaCallback) {}
TargetOptions::TargetOptions(
TypeID typeID, StringRef toolkitPath, ArrayRef<Attribute> librariesToLink,
StringRef cmdOptions, StringRef elfSection,
CompilationTarget compilationTarget,
function_ref<SymbolTable *()> getSymbolTableCallback,
function_ref<void(llvm::Module &)> initialLlvmIRCallback,
function_ref<void(llvm::Module &)> linkedLlvmIRCallback,
function_ref<void(llvm::Module &)> optimizedLlvmIRCallback,
function_ref<void(StringRef)> isaCallback)
: toolkitPath(toolkitPath.str()), librariesToLink(librariesToLink),
cmdOptions(cmdOptions.str()), elfSection(elfSection.str()),
compilationTarget(compilationTarget),
getSymbolTableCallback(getSymbolTableCallback),
initialLlvmIRCallback(initialLlvmIRCallback),
linkedLlvmIRCallback(linkedLlvmIRCallback),
optimizedLlvmIRCallback(optimizedLlvmIRCallback),
isaCallback(isaCallback), typeID(typeID) {}
TypeID TargetOptions::getTypeID() const { return typeID; }
StringRef TargetOptions::getToolkitPath() const { return toolkitPath; }
ArrayRef<Attribute> TargetOptions::getLibrariesToLink() const {
return librariesToLink;
}
StringRef TargetOptions::getCmdOptions() const { return cmdOptions; }
StringRef TargetOptions::getELFSection() const { return elfSection; }
SymbolTable *TargetOptions::getSymbolTable() const {
return getSymbolTableCallback ? getSymbolTableCallback() : nullptr;
}
function_ref<void(llvm::Module &)>
TargetOptions::getInitialLlvmIRCallback() const {
return initialLlvmIRCallback;
}
function_ref<void(llvm::Module &)>
TargetOptions::getLinkedLlvmIRCallback() const {
return linkedLlvmIRCallback;
}
function_ref<void(llvm::Module &)>
TargetOptions::getOptimizedLlvmIRCallback() const {
return optimizedLlvmIRCallback;
}
function_ref<void(StringRef)> TargetOptions::getISACallback() const {
return isaCallback;
}
CompilationTarget TargetOptions::getCompilationTarget() const {
return compilationTarget;
}
CompilationTarget TargetOptions::getDefaultCompilationTarget() {
return CompilationTarget::Fatbin;
}
std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>>
TargetOptions::tokenizeCmdOptions(const std::string &cmdOptions) {
std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>> options;
llvm::StringSaver stringSaver(options.first);
StringRef opts = cmdOptions;
// For a correct tokenization of the command line options `opts` must be
// unquoted, otherwise the tokenization function returns a single string: the
// unquoted `cmdOptions` -which is not the desired behavior.
// Remove any quotes if they are at the beginning and end of the string:
if (!opts.empty() && opts.front() == '"' && opts.back() == '"')
opts.consume_front("\""), opts.consume_back("\"");
if (!opts.empty() && opts.front() == '\'' && opts.back() == '\'')
opts.consume_front("'"), opts.consume_back("'");
#ifdef _WIN32
llvm::cl::TokenizeWindowsCommandLine(opts, stringSaver, options.second,
/*MarkEOLs=*/false);
#else
llvm::cl::TokenizeGNUCommandLine(opts, stringSaver, options.second,
/*MarkEOLs=*/false);
#endif // _WIN32
return options;
}
std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>>
TargetOptions::tokenizeCmdOptions() const {
return tokenizeCmdOptions(cmdOptions);
}
std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>>
TargetOptions::tokenizeAndRemoveSuffixCmdOptions(llvm::StringRef startsWith) {
size_t startPos = cmdOptions.find(startsWith);
if (startPos == std::string::npos)
return {llvm::BumpPtrAllocator(), SmallVector<const char *>()};
auto tokenized =
tokenizeCmdOptions(cmdOptions.substr(startPos + startsWith.size()));
cmdOptions.resize(startPos);
return tokenized;
}
MLIR_DEFINE_EXPLICIT_TYPE_ID(::mlir::gpu::TargetOptions)
#include "mlir/Dialect/GPU/IR/GPUOpsEnums.cpp.inc"
#define GET_ATTRDEF_CLASSES
#include "mlir/Dialect/GPU/IR/GPUOpsAttributes.cpp.inc"
namespace {
template <typename... Attrs>
void registerAttributeStorageImpl(MLIRContext *context) {
(mlir::detail::AttributeUniquer::registerAttribute<Attrs>(context), ...);
}
} // namespace
void mlir::gpu::detail::registerAttributeStorage(MLIRContext *context) {
registerAttributeStorageImpl<
#define GET_ATTRDEF_LIST
#include "mlir/Dialect/GPU/IR/GPUOpsAttributes.cpp.inc"
>(context);
}
#include "mlir/Dialect/GPU/IR/CompilationAttrInterfaces.cpp.inc"