| //===- LowerToLLVMOpenCLMetadata.cpp - OpenCL metadata lowering -----------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "LowerToLLVMOpenCLMetadata.h" |
| |
| #include "mlir/Dialect/LLVMIR/LLVMAttrs.h" |
| #include "mlir/Dialect/LLVMIR/LLVMDialect.h" |
| #include "mlir/IR/Builders.h" |
| #include "clang/CIR/Dialect/IR/CIRDialect.h" |
| #include "llvm/ADT/TypeSwitch.h" |
| #include "llvm/Support/ErrorHandling.h" |
| |
| namespace cir { |
| namespace direct { |
| |
| namespace { |
| class LLVMMetadataNodeBuilder { |
| public: |
| explicit LLVMMetadataNodeBuilder(mlir::MLIRContext *ctx) : ctx(ctx) {} |
| |
| mlir::LLVM::MDConstantAttr getI32(unsigned value) const { |
| mlir::IntegerType intTy = mlir::IntegerType::get(ctx, 32); |
| mlir::IntegerAttr intAttr = mlir::IntegerAttr::get(intTy, value); |
| return mlir::LLVM::MDConstantAttr::get(ctx, intAttr); |
| } |
| |
| mlir::LLVM::MDStringAttr getString(llvm::StringRef value) const { |
| return mlir::LLVM::MDStringAttr::get(ctx, |
| mlir::StringAttr::get(ctx, value)); |
| } |
| |
| mlir::LLVM::MDNodeAttr |
| getNode(llvm::ArrayRef<mlir::Attribute> metadata) const { |
| return mlir::LLVM::MDNodeAttr::get(ctx, metadata); |
| } |
| |
| mlir::LLVM::MDNodeAttr getI32Node(llvm::ArrayRef<unsigned> values) const { |
| llvm::SmallVector<mlir::Attribute> metadata; |
| for (unsigned value : values) |
| metadata.push_back(getI32(value)); |
| return getNode(metadata); |
| } |
| |
| mlir::LLVM::MDNodeAttr getStringNode(mlir::ArrayAttr attrs) const { |
| llvm::SmallVector<mlir::Attribute> metadata; |
| for (mlir::StringAttr attr : attrs.getAsRange<mlir::StringAttr>()) |
| metadata.push_back(getString(attr.getValue())); |
| return getNode(metadata); |
| } |
| |
| private: |
| mlir::MLIRContext *ctx; |
| }; |
| |
| using KernelArgStringMetadataGetter = |
| mlir::ArrayAttr (cir::OpenCLKernelArgMetadataAttr::*)() const; |
| |
| struct KernelArgStringMetadataMapping { |
| llvm::StringLiteral metadataName; |
| KernelArgStringMetadataGetter getMetadata; |
| bool optional; |
| }; |
| |
| static unsigned getOpenCLArgInfoAddressSpace(cir::LangAddressSpace as) { |
| switch (as) { |
| case cir::LangAddressSpace::Default: |
| case cir::LangAddressSpace::OffloadPrivate: |
| return 0; |
| case cir::LangAddressSpace::OffloadGlobal: |
| return 1; |
| case cir::LangAddressSpace::OffloadConstant: |
| return 2; |
| case cir::LangAddressSpace::OffloadLocal: |
| return 3; |
| case cir::LangAddressSpace::OffloadGeneric: |
| return 4; |
| case cir::LangAddressSpace::OffloadGlobalDevice: |
| return 5; |
| case cir::LangAddressSpace::OffloadGlobalHost: |
| return 6; |
| } |
| llvm_unreachable("unknown CIR language address space"); |
| } |
| |
| static mlir::LLVM::MDNodeAttr |
| getAddrSpaceMetadataNode(cir::OpenCLKernelArgMetadataAttr clArgMetadata, |
| const LLVMMetadataNodeBuilder &metadataBuilder) { |
| llvm::SmallVector<unsigned> addrSpaces; |
| for (cir::LangAddressSpaceAttr addressSpace : |
| clArgMetadata.getAddrSpace().getAsRange<cir::LangAddressSpaceAttr>()) |
| addrSpaces.push_back(getOpenCLArgInfoAddressSpace(addressSpace.getValue())); |
| return metadataBuilder.getI32Node(addrSpaces); |
| } |
| |
| static void addOpenCLKernelArgFunctionMetadata( |
| mlir::MLIRContext *ctx, llvm::SmallVectorImpl<mlir::Attribute> &entries, |
| llvm::StringRef name, mlir::LLVM::MDNodeAttr node) { |
| entries.push_back(mlir::LLVM::FunctionMetadataAttr::get( |
| ctx, mlir::StringAttr::get(ctx, name), node)); |
| } |
| |
| } // namespace |
| |
| static void convertOpenCLKernelArgMetadata( |
| cir::OpenCLKernelArgMetadataAttr clArgMetadata, |
| llvm::SmallVectorImpl<mlir::Attribute> &entries) { |
| mlir::MLIRContext *ctx = clArgMetadata.getContext(); |
| LLVMMetadataNodeBuilder metadataBuilder(ctx); |
| |
| addOpenCLKernelArgFunctionMetadata( |
| ctx, entries, "kernel_arg_addr_space", |
| getAddrSpaceMetadataNode(clArgMetadata, metadataBuilder)); |
| |
| static constexpr KernelArgStringMetadataMapping stringMetadataMappings[] = { |
| {"kernel_arg_access_qual", |
| &cir::OpenCLKernelArgMetadataAttr::getAccessQual, |
| /*optional=*/false}, |
| {"kernel_arg_type", &cir::OpenCLKernelArgMetadataAttr::getType, |
| /*optional=*/false}, |
| {"kernel_arg_base_type", &cir::OpenCLKernelArgMetadataAttr::getBaseType, |
| /*optional=*/false}, |
| {"kernel_arg_type_qual", &cir::OpenCLKernelArgMetadataAttr::getTypeQual, |
| /*optional=*/false}, |
| {"kernel_arg_name", &cir::OpenCLKernelArgMetadataAttr::getName, |
| /*optional=*/true}, |
| }; |
| |
| for (const KernelArgStringMetadataMapping &mapping : stringMetadataMappings) { |
| mlir::ArrayAttr metadata = (clArgMetadata.*mapping.getMetadata)(); |
| if (mapping.optional && !metadata) |
| continue; |
| addOpenCLKernelArgFunctionMetadata(ctx, entries, mapping.metadataName, |
| metadataBuilder.getStringNode(metadata)); |
| } |
| } |
| |
| OpenCLFunctionMetadataLowering::OpenCLFunctionMetadataLowering( |
| mlir::MLIRContext *ctx) |
| : ctx(ctx) {} |
| |
| bool OpenCLFunctionMetadataLowering::lower(mlir::NamedAttribute attr, |
| bool includeFunctionOnlyAttrs) { |
| return llvm::TypeSwitch<mlir::Attribute, bool>(attr.getValue()) |
| .Case<cir::OpenCLKernelArgMetadataAttr>( |
| [&](cir::OpenCLKernelArgMetadataAttr clArgMetadata) { |
| if (!includeFunctionOnlyAttrs) |
| return true; |
| lower(clArgMetadata); |
| return true; |
| }) |
| .Default(false); |
| } |
| |
| void OpenCLFunctionMetadataLowering::appendAttrs( |
| llvm::SmallVectorImpl<mlir::NamedAttribute> &result) const { |
| if (!functionMetadata.empty()) { |
| result.push_back(mlir::NamedAttribute( |
| mlir::LLVM::LLVMFuncOp::getFunctionMetadataAttrName(mlir::OperationName( |
| mlir::LLVM::LLVMFuncOp::getOperationName(), ctx)), |
| mlir::ArrayAttr::get(ctx, functionMetadata))); |
| } |
| } |
| |
| void OpenCLFunctionMetadataLowering::lower( |
| cir::OpenCLKernelArgMetadataAttr clArgMetadata) { |
| convertOpenCLKernelArgMetadata(clArgMetadata, functionMetadata); |
| } |
| |
| static void createOpenCLVersionNamedMetadata(mlir::ModuleOp module, |
| llvm::StringRef name, |
| mlir::LLVM::MDNodeAttr node) { |
| mlir::MLIRContext *ctx = module.getContext(); |
| mlir::OpBuilder builder(ctx); |
| builder.setInsertionPointToEnd(module.getBody()); |
| mlir::LLVM::NamedMetadataOp::create(builder, module.getLoc(), name, |
| mlir::ArrayAttr::get(ctx, node)); |
| } |
| |
| static void emitOpenCLVersionMetadata(mlir::ModuleOp module, |
| llvm::StringRef metadataName, |
| mlir::Attribute versionAttr) { |
| auto version = mlir::cast<cir::OpenCLVersionAttr>(versionAttr); |
| mlir::MLIRContext *ctx = module.getContext(); |
| LLVMMetadataNodeBuilder metadataBuilder(ctx); |
| |
| unsigned versionMetadata[] = {static_cast<unsigned>(version.getMajor()), |
| static_cast<unsigned>(version.getMinor())}; |
| createOpenCLVersionNamedMetadata(module, metadataName, |
| metadataBuilder.getI32Node(versionMetadata)); |
| } |
| |
| void lowerOpenCLModuleMetadataAttrs(mlir::ModuleOp module) { |
| struct OpenCLModuleMetadataMapping { |
| llvm::StringRef cirAttrName; |
| llvm::StringRef llvmMetadataName; |
| }; |
| |
| const OpenCLModuleMetadataMapping moduleMetadataMappings[] = { |
| {cir::CIRDialect::getOpenCLVersionAttrName(), "opencl.ocl.version"}, |
| {cir::CIRDialect::getOpenCLCXXVersionAttrName(), "opencl.cxx.version"}, |
| }; |
| |
| for (const OpenCLModuleMetadataMapping &mapping : moduleMetadataMappings) { |
| mlir::Attribute version = module->getAttr(mapping.cirAttrName); |
| if (!version) |
| continue; |
| emitOpenCLVersionMetadata(module, mapping.llvmMetadataName, version); |
| module->removeAttr(mapping.cirAttrName); |
| } |
| } |
| |
| } // namespace direct |
| } // namespace cir |