blob: 4dc9e2acfe235afac52d046ef6b1f6c93e4b1352 [file] [edit]
//===- GPUToXeVMPipeline.cpp - Lowering pipeline to XeVM/LLVM -------------===//
//
// 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 a pass for testing the lowering to XeVM as a generally
// usable sink pass. If XeGPU ops are used, it expects the MLIR code to have
// XeGPU ops already embedded in gpu code.
//
//===----------------------------------------------------------------------===//
#include "mlir/Conversion/AffineToStandard/AffineToStandard.h"
#include "mlir/Conversion/GPUCommon/GPUCommonPass.h"
#include "mlir/Conversion/MathToXeVM/MathToXeVM.h"
#include "mlir/Conversion/Passes.h"
#include "mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h"
#include "mlir/Conversion/VectorToSCF/VectorToSCF.h"
#include "mlir/Conversion/XeGPUToXeVM/XeGPUToXeVM.h"
#include "mlir/Conversion/XeVMToLLVM/XeVMToLLVM.h"
#include "mlir/Dialect/Arith/Transforms/Passes.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
#include "mlir/Dialect/GPU/Pipelines/Passes.h"
#include "mlir/Dialect/GPU/Transforms/Passes.h"
#include "mlir/Dialect/LLVMIR/Transforms/RequestCWrappers.h"
#include "mlir/Dialect/Math/Transforms/Passes.h"
#include "mlir/Dialect/MemRef/Transforms/Passes.h"
#include "mlir/Dialect/XeGPU/Transforms/Passes.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Pass/PassOptions.h"
#include "mlir/Target/LLVM/XeVM/Target.h"
#include "mlir/Transforms/Passes.h"
using namespace mlir;
namespace {
//===----------------------------------------------------------------------===//
// Pre-GPU common pipeline for both Host and GPU.
//===----------------------------------------------------------------------===//
void buildPreGPUCommonPassPipeline(
OpPassManager &pm, const mlir::gpu::GPUToXeVMPipelineOptions &options) {
// builtin.module scope passes.
pm.addPass(createCSEPass());
{
GpuXeVMAttachTargetOptions xevmTargetOptions;
xevmTargetOptions.moduleMatcher = options.xevmModuleMatcher;
xevmTargetOptions.triple = options.zebinTriple;
xevmTargetOptions.chip = options.zebinChip;
xevmTargetOptions.optLevel = options.optLevel;
xevmTargetOptions.cmdOptions = options.cmdOptions;
pm.addPass(createGpuXeVMAttachTarget(xevmTargetOptions));
}
pm.addPass(createLowerAffinePass());
pm.addNestedPass<func::FuncOp>(createGpuAsyncRegionPass());
}
//===----------------------------------------------------------------------===//
// GPUModule-specific stuff.
//===----------------------------------------------------------------------===//
void buildGPUPassPipeline(OpPassManager &pm,
const mlir::gpu::GPUToXeVMPipelineOptions &options) {
xegpu::XeGPUPropagateLayoutOptions laneLayoutOptions;
laneLayoutOptions.indexBitWidth = options.use64bitIndex ? 64 : 32;
laneLayoutOptions.layoutKind = "lane";
pm.addNestedPass<ModuleOp>(createCSEPass());
if (options.enableVectorToXeGPU)
pm.addNestedPass<gpu::GPUModuleOp>(createConvertVectorToXeGPU());
if (options.xegpuOpLevel == "workgroup") {
xegpu::XeGPUPropagateLayoutOptions sgLayoutOptions;
sgLayoutOptions.layoutKind = "subgroup";
pm.addNestedPass<gpu::GPUModuleOp>(
xegpu::createXeGPUPropagateLayout(sgLayoutOptions));
pm.addNestedPass<gpu::GPUModuleOp>(xegpu::createXeGPUWgToSgDistribute());
pm.addNestedPass<gpu::GPUModuleOp>(createCSEPass());
pm.addNestedPass<gpu::GPUModuleOp>(createLowerAffinePass());
pm.addNestedPass<gpu::GPUModuleOp>(createCSEPass());
xegpu::XeGPUPropagateLayoutOptions instDataOptions;
instDataOptions.layoutKind = "inst";
pm.addNestedPass<gpu::GPUModuleOp>(
xegpu::createXeGPUPropagateLayout(instDataOptions));
pm.addNestedPass<gpu::GPUModuleOp>(xegpu::createXeGPUBlocking());
pm.addNestedPass<gpu::GPUModuleOp>(createCSEPass());
}
if (options.xegpuOpLevel == "subgroup" ||
options.xegpuOpLevel == "workgroup") {
pm.addNestedPass<gpu::GPUModuleOp>(
xegpu::createXeGPUPropagateLayout(laneLayoutOptions));
pm.addNestedPass<gpu::GPUModuleOp>(xegpu::createXeGPUPeepHoleOptimizer());
pm.addNestedPass<gpu::GPUModuleOp>(createCSEPass());
pm.addNestedPass<gpu::GPUModuleOp>(
xegpu::createXeGPUPropagateLayout(laneLayoutOptions));
pm.addNestedPass<gpu::GPUModuleOp>(xegpu::createXeGPUSgToLaneDistribute());
pm.addNestedPass<gpu::GPUModuleOp>(createCanonicalizerPass());
pm.addNestedPass<gpu::GPUModuleOp>(createCSEPass());
pm.addNestedPass<gpu::GPUModuleOp>(createLoopInvariantCodeMotionPass());
pm.addNestedPass<gpu::GPUModuleOp>(createCSEPass());
pm.addNestedPass<gpu::GPUModuleOp>(xegpu::createXeGPUVectorLinearize());
pm.addNestedPass<gpu::GPUModuleOp>(createCanonicalizerPass());
pm.addNestedPass<gpu::GPUModuleOp>(createCSEPass());
}
// Break down high-level micro-scaling (MX) ops (arith.scaling_extf and
// arith.scaling_truncf) into standard arith ops (extf/truncf + mulf), and
// expand extf/truncf on f8E8M0FNU into integer bit manipulation. This runs
// before the XeVM/LLVM conversions. The f4E2M1FN expansion patterns are
// intentionally left disabled: f4E2M1FN extf/truncf are lowered by the XeVM
// conversions (xevm.extf), whereas f8E8M0FNU is not supported there and so
// must be expanded here.
{
arith::ArithExpandOpsPassOptions arithExpandOptions;
arithExpandOptions.includeF8E8M0 = true;
pm.addNestedPass<gpu::GPUModuleOp>(
arith::createArithExpandOpsPass(arithExpandOptions));
}
pm.addNestedPass<gpu::GPUModuleOp>(createConvertMathToXeVM());
ConvertXeGPUToXeVMPassOptions xegpuToXeVMOptions;
xegpuToXeVMOptions.use64bitIndex = options.use64bitIndex;
pm.addNestedPass<gpu::GPUModuleOp>(
createConvertXeGPUToXeVMPass(xegpuToXeVMOptions));
{
ConvertGpuOpsToLLVMSPVOpsOptions gpuToLLVMSPVOptions;
gpuToLLVMSPVOptions.use64bitIndex = options.use64bitIndex;
pm.addNestedPass<gpu::GPUModuleOp>(
createConvertGpuOpsToLLVMSPVOps(gpuToLLVMSPVOptions));
}
// Legalize math/arith ops on floating-point types that the XeVM target
// cannot handle natively (e.g. bf16) by wrapping them with extf/truncf
// around a supported type (defaulting to f32).
{
math::MathExtendToSupportedTypesOptions mathExtendOptions;
mathExtendOptions.extraTypeStrs.assign(options.mathExtendExtraTypes.begin(),
options.mathExtendExtraTypes.end());
mathExtendOptions.targetTypeStr = options.supportedTargetTypes;
pm.addNestedPass<gpu::GPUModuleOp>(
math::createMathExtendToSupportedTypes(mathExtendOptions));
}
{
arith::ArithEmulateUnsupportedFloatsOptions arithEmulateOptions;
arithEmulateOptions.sourceTypeStrs.assign(
options.unsupportedSourceTypes.begin(),
options.unsupportedSourceTypes.end());
arithEmulateOptions.targetTypeStr = options.supportedTargetTypes;
pm.addNestedPass<gpu::GPUModuleOp>(
arith::createArithEmulateUnsupportedFloats(arithEmulateOptions));
}
pm.addNestedPass<gpu::GPUModuleOp>(createCSEPass());
pm.addNestedPass<gpu::GPUModuleOp>(createReconcileUnrealizedCastsPass());
}
//===----------------------------------------------------------------------===//
// Post-GPU pipeline for both Host and GPU.
//===----------------------------------------------------------------------===//
void buildPostGPUCommonPassPipeline(
OpPassManager &pm, const mlir::gpu::GPUToXeVMPipelineOptions &options) {
// builtin.module scope passes.
pm.addPass(createConvertVectorToSCFPass());
pm.addPass(createSCFToControlFlowPass());
pm.addPass(memref::createExpandStridedMetadataPass());
{
GpuToLLVMConversionPassOptions gpuToLLVMOptions;
gpuToLLVMOptions.hostBarePtrCallConv = options.hostBarePtrCallConv;
gpuToLLVMOptions.kernelBarePtrCallConv = options.kernelBarePtrCallConv;
pm.addPass(createGpuToLLVMConversionPass(gpuToLLVMOptions));
}
pm.addPass(createLowerAffinePass());
pm.addPass(createConvertVectorToLLVMPass());
pm.addPass(createConvertToLLVMPass());
pm.addPass(createReconcileUnrealizedCastsPass());
pm.addNestedPass<gpu::GPUModuleOp>(createCanonicalizerPass());
pm.addNestedPass<gpu::GPUModuleOp>(createCSEPass());
// XeVM-to-LLVM must be the last pass before gpu-module-to-binary.
pm.addNestedPass<gpu::GPUModuleOp>(createConvertXeVMToLLVMPass());
// gpu-module-to-binary
{
GpuModuleToBinaryPassOptions gpuToModuleBinOptions;
gpuToModuleBinOptions.compilationTarget = options.binaryFormat;
gpuToModuleBinOptions.cmdOptions = options.cmdOptions;
pm.addPass(createGpuModuleToBinaryPass(gpuToModuleBinOptions));
}
}
} // namespace
void mlir::gpu::buildLowerToXeVMPassPipeline(
OpPassManager &pm, const GPUToXeVMPipelineOptions &options) {
// Pre-GPU common pipelines.
buildPreGPUCommonPassPipeline(pm, options);
// GPUModule-specific stuff.
buildGPUPassPipeline(pm, options);
// Post-GPU pipeline for both Host and GPU.
buildPostGPUCommonPassPipeline(pm, options);
}
void mlir::gpu::registerGPUToXeVMPipeline() {
PassPipelineRegistration<GPUToXeVMPipelineOptions>(
"gpu-lower-to-xevm-pipeline",
"The default GPU to XeVM lowering pipeline. It starts by lowering GPU "
"code to the "
"specified compilation target (default is fatbin) then lowers the host "
"code.",
buildLowerToXeVMPassPipeline);
}