blob: df5a2448bd77968d9704c0194622aa7c691c65bf [file] [log] [blame]
//===- BasicPtxBuilderInterface.td - PTX builder interface -*- tablegen -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Defines the interface to build PTX (Parallel Thread Execution) from NVVM Ops
// automatically. It is used by NVVM to LLVM pass.
//
//===----------------------------------------------------------------------===//
#ifndef BASICPTXBUILDER_OP_INTERFACE
#define BASICPTXBUILDER_OP_INTERFACE
include "mlir/IR/EnumAttr.td"
include "mlir/Dialect/GPU/IR/CompilationAttrInterfaces.td"
include "mlir/Dialect/LLVMIR/LLVMOpBase.td"
//===----------------------------------------------------------------------===//
// Basic PTX Builder Interface
//===----------------------------------------------------------------------===//
def PtxPredicate : Optional<I1>;
def BasicPtxBuilderOpInterface : OpInterface<"BasicPtxBuilderInterface"> {
let description = [{
This interface is used to generate inline assembly with PTX for basic
operations. It's utilized in the `convert-nvvm-to-llvm pass` to lower
NVVM Ops that implement this interface to PTX (parallel thread execution)
using inline assembly Ops. Interface methods play a crucial role in this
lowering process.
Here's an example of an Op with the `BasicPtxBuilderOpInterface`:
```tablegen
def NVVM_SpecialOp : NVVM_Op<"special.op",
[DeclareOpInterfaceMethods<BasicPtxBuilderOpInterface>]>,
Results<(outs LLVM_Type:$res)>,
Arguments<(ins LLVM_i64ptr_any:$op1, I32:$op2)> {
...
let extraClassDefinition = [{
std::string $cppClass::getPtx() {
return std::string("special.op %0, %1, %2;");
}
} ];
```
In the above NVVM Op example:
```mlir
%0 = nvvm.special.op %1, %2 : !llvm.ptr, i32 -> i32
```
The `convert-nvvm-to-llvm` pass generates the inline assembly like below.
The order of arguments is retained, and the read and write modifiers are
set based on the input and result types:
```mlir
%0 = llvm.inline_asm
has_side_effects
asm_dialect =
att "special.op %0, %1, %2;", "=r,l,r" %arg0, %arg1
: (!llvm.ptr, i32) -> i32
```
}];
let cppNamespace = "::mlir::NVVM";
let methods = [
InterfaceMethod<
/*desc=*/[{
Optional function for setting a predicate, which
always returns a `PtxPredicate` value of type i1. If no predicate is
provided, the instruction is unguarded; otherwise, it's guarded by the
predicate value. The `PtxPredicate` value must always be the last argument.
The provided PTX code by `getPtx` should not include the predicate usage.
The interface automatically handles predicate usage in the generated
PTX code when necessary.
}],
/*retType=*/"std::optional<::mlir::Value>",
/*methodName=*/"getPredicate",
/*args=*/(ins),
/*methodBody=*/"",
/*defaultImplementation=*/"return {};"
>,
InterfaceMethod<
/*desc=*/[{ Returns PTX assembly with operand number. }],
/*retType=*/"std::string",
/*methodName=*/"getPtx"
>,
InterfaceMethod<
/*desc=*/[{
This function indicates whether the operation is supported by LLVM
intrinsics. It's particularly useful for operations that have
specific cases with LLVM intrinsic support.
}],
/*retType=*/"bool",
/*methodName=*/"hasIntrinsic",
/*args=*/(ins),
/*methodBody=*/"",
/*defaultImplementation=*/"return false;"
>,
InterfaceMethod<
/*desc=*/[{Return whether the operation has memory side effects.}],
/*retType=*/"bool",
/*methodName=*/"hasSideEffect",
/*args=*/(ins),
/*methodBody=*/"",
/*defaultImplementation=*/"return true;"
>,
InterfaceMethod<
/*desc=*/[{Helper function to generate i32 constant value.}],
/*retType=*/"::mlir::Value",
/*methodName=*/"makeConstantI32",
/*args=*/(ins "::mlir::RewriterBase &":$rewriter, "int" : $val),
/*methodBody=*/"",
/*defaultImpl=*/ [{
mlir::Operation* op = $_op;
return rewriter.create<LLVM::ConstantOp>(
op->getLoc(), rewriter.getIntegerType(32), val);
}]
>,
InterfaceMethod<
/*desc=*/[{
This function supplies the necessary arguments for passing PTX code,
following this order:
1) Adds results
2) Adds operands
3) Adds attributes
}],
/*retType=*/"void",
/*methodName=*/"getAsmValues",
/*args=*/(ins "::mlir::RewriterBase &":$rewriter,
"llvm::SmallVectorImpl<std::pair<mlir::Value, mlir::NVVM::PTXRegisterMod>>&" : $asmValues),
/*methodBody=*/"",
/*defaultImpl=*/ [{
mlir::Operation* op = $_op;
// Step 1. Add results
for (auto val : op->getResults())
asmValues.push_back({val, mlir::NVVM::PTXRegisterMod::Write});
// Step 2. Add operands
for (auto val : op->getOperands())
asmValues.push_back({val, mlir::NVVM::PTXRegisterMod::Read});
// Step 3. Add attributes
for (auto attr : op->getAttrs()) {
if (auto intAttr = dyn_cast<mlir::IntegerAttr>(attr.getValue())) {
::mlir::Value val = makeConstantI32(rewriter, intAttr.getInt());
asmValues.push_back({val, mlir::NVVM::PTXRegisterMod::Read});
}
}
}]
>
];
}
#endif // BASICPTXBUILDER_OP_INTERFACE