#ifndef TRITON_CONVERSION_TRITONGPU_TO_LLVM_UTILITY_H #define TRITON_CONVERSION_TRITONGPU_TO_LLVM_UTILITY_H #include #include "mlir/Conversion/LLVMCommon/Pattern.h" #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/IR/ImplicitLocOpBuilder.h" #include "mlir/Interfaces/FunctionInterfaces.h" #include "triton/Analysis/Utility.h" #include "triton/Conversion/MLIRTypes.h" #include "triton/Conversion/TritonGPUToLLVM/TargetInfoBase.h" #include "triton/Dialect/Triton/IR/Dialect.h" #include "triton/Dialect/Triton/IR/Utility.h" #include "triton/Dialect/TritonGPU/IR/Dialect.h" #include "triton/Dialect/TritonGPU/IR/LinearLayoutConversions.h" #include "triton/Dialect/TritonGPU/IR/Types.h" #include "triton/Dialect/TritonNvidiaGPU/IR/Dialect.h" #include "triton/Tools/LinearLayout.h" #include "triton/Tools/StrUtil.h" #include "triton/Tools/Sys/GetEnv.hpp" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/ErrorHandling.h" #define DEBUG_TYPE "ttgpu_to_llvm" #define DBGS() (llvm::dbgs() << "[" DEBUG_TYPE "]: ") #define LDBG(X) LLVM_DEBUG(DBGS() << X << "\n") using namespace mlir; using namespace mlir::triton; namespace mlir::LLVM { using namespace mlir::triton; Value createConstantI1(Location loc, OpBuilder &rewriter, bool v); Value createConstantI32(Location loc, OpBuilder &rewriter, int32_t v); Value createConstantI64(Location loc, OpBuilder &rewriter, int64_t v); Value createConstantF16(Location loc, OpBuilder &rewriter, float v); Value createConstantBF16(Location loc, OpBuilder &rewriter, float v); Value createConstantF32(Location loc, OpBuilder &rewriter, float v); Value createConstantF64(Location loc, OpBuilder &rewriter, double v); Value createNaNConstant(Location loc, OpBuilder &rewriter, Type type); Value createIndexConstant(OpBuilder &builder, Location loc, const TypeConverter *converter, int64_t value); Value createLLVMIntegerConstant(OpBuilder &builder, Location loc, short width, int64_t value); LLVM::CallOp createLLVMCallOp(OpBuilder &builder, Location loc, LLVMFuncOp funcOp, ValueRange args); LLVM::CallIntrinsicOp createLLVMIntrinsicCallOp(OpBuilder &builder, Location loc, StringRef intrinsic, TypeRange types, ValueRange args); } // namespace mlir::LLVM namespace mlir::triton { struct TritonLLVMOpBuilder { TritonLLVMOpBuilder(Location loc, OpBuilder &builder) : loc(loc), builder(&builder) {} // Shortcuts for some commonly used LLVM ops to keep code simple and intuitive // Operators template LLVM::SIToFPOp inttofloat(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::IntToPtrOp inttoptr(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::PtrToIntOp ptrtoint(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::ZExtOp zext(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::SExtOp sext(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::FPExtOp fpext(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::FPTruncOp fptrunc(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::TruncOp trunc(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::UDivOp udiv(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::SDivOp sdiv(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::URemOp urem(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::AddOp add(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::SubOp sub(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::FAddOp fadd(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::MulOp mul(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::FMulOp fmul(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::FMAOp fma(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::FNegOp neg(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::SMaxOp smax(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::UMaxOp umax(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::MaxNumOp fmax(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::SMinOp smin(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::UMinOp umin(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::MinNumOp fmin(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::ShlOp shl(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::LShrOp lshr(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::AShrOp ashr(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::AndOp and_(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::XOrOp xor_(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::OrOp or_(Args &&...args) { return builder->create(loc, std::forward(args)...); } LLVM::BitcastOp bitcast(Value val, Type type) { return builder->create(loc, type, val); } template LLVM::AddrSpaceCastOp addrspacecast(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::GEPOp gep(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::InsertValueOp insert_val(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::ExtractValueOp extract_val(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::InsertElementOp insert_element(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::ExtractElementOp extract_element(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::LoadOp load(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::StoreOp store(Args &&...args) { return builder->create(loc, std::forward(args)...); } LLVM::FCmpOp fcmp_ogt(Value lhs, Value rhs) { return builder->create(loc, builder->getI1Type(), LLVM::FCmpPredicate::ogt, lhs, rhs); } LLVM::FCmpOp fcmp_olt(Value lhs, Value rhs) { return builder->create(loc, builder->getI1Type(), LLVM::FCmpPredicate::olt, lhs, rhs); } LLVM::FCmpOp fcmp_eq(Value lhs, Value rhs) { return builder->create(loc, builder->getI1Type(), LLVM::FCmpPredicate::oeq, lhs, rhs); } template LLVM::ICmpOp icmp_eq(Args &&...args) { return builder->create(loc, LLVM::ICmpPredicate::eq, std::forward(args)...); } template LLVM::ICmpOp icmp_ne(Args &&...args) { return builder->create(loc, LLVM::ICmpPredicate::ne, std::forward(args)...); } template LLVM::ICmpOp icmp_slt(Args &&...args) { return builder->create(loc, LLVM::ICmpPredicate::slt, std::forward(args)...); } template LLVM::ICmpOp icmp_sle(Args &&...args) { return builder->create(loc, LLVM::ICmpPredicate::sle, std::forward(args)...); } template LLVM::ICmpOp icmp_sgt(Args &&...args) { return builder->create(loc, LLVM::ICmpPredicate::sgt, std::forward(args)...); } template LLVM::ICmpOp icmp_sge(Args &&...args) { return builder->create(loc, LLVM::ICmpPredicate::sge, std::forward(args)...); } template LLVM::ICmpOp icmp_ult(Args &&...args) { return builder->create(loc, LLVM::ICmpPredicate::ult, std::forward(args)...); } template LLVM::ICmpOp icmp_ule(Args &&...args) { return builder->create(loc, LLVM::ICmpPredicate::ule, std::forward(args)...); } template LLVM::ICmpOp icmp_ugt(Args &&...args) { return builder->create(loc, LLVM::ICmpPredicate::ugt, std::forward(args)...); } template LLVM::ICmpOp icmp_uge(Args &&...args) { return builder->create(loc, LLVM::ICmpPredicate::uge, std::forward(args)...); } template LLVM::SelectOp select(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::AddressOfOp address_of(Args &&...args) { return builder->create(loc, std::forward(args)...); } mlir::gpu::BarrierOp barrier() { return builder->create(loc); } template LLVM::UndefOp undef(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::ZeroOp null(Args &&...args) { return builder->create(loc, std::forward(args)...); } template LLVM::CallOp call(Args &&...args) { return builder->create(loc, std::forward(args)...); } // Constants Value int_val(short bitwidth, int64_t val) { Type ty = builder->getIntegerType(bitwidth); return builder->create(loc, ty, builder->getIntegerAttr(ty, val)); } Value i1_val(int64_t val) { return int_val(1, val); } Value true_val() { return int_val(1, true); } Value false_val() { return int_val(1, false); } Value f16_val(float v) { return LLVM::createConstantF16(loc, *builder, v); } Value bf16_val(float v) { return LLVM::createConstantBF16(loc, *builder, v); } Value f32_val(float v) { return LLVM::createConstantF32(loc, *builder, v); } Value f64_val(double v) { return LLVM::createConstantF64(loc, *builder, v); } Value i8_val(int64_t val) { return int_val(8, val); } Value i16_val(int64_t val) { return int_val(16, val); } Value i32_val(int64_t val) { return int_val(32, val); } Value i64_val(int64_t val) { return int_val(64, val); } Location loc; OpBuilder *builder; }; // This builder combines an IRRewriter and a TritonLLVMOpBuilder into one, // making it easy to create operations with an implicit location and create LLVM // operations with shorthands. class TritonLLVMIRRewriter : public IRRewriter, public TritonLLVMOpBuilder { public: // Create a builder with an implicit location. Arguments are forwarded to // IRRewriter's constructor. template TritonLLVMIRRewriter(Location loc, Args &&...args) : IRRewriter(std::forward(args)...), TritonLLVMOpBuilder(loc, *this) {} // Get the implicit location. Location getLoc() const { return loc; } // Set the implicit location used to build ops. void setLoc(Location loc) { this->loc = loc; } // Wrapper for op creation that passes an implicit location. template OpTy create(Args &&...args) { return OpBuilder::create(loc, std::forward(args)...); } }; } // namespace mlir::triton // Types #define ptr_ty(...) LLVM::LLVMPointerType::get(__VA_ARGS__) #define int_ty(width) rewriter.getIntegerType(width) #define i64_ty rewriter.getIntegerType(64) #define i32_ty rewriter.getIntegerType(32) #define i16_ty rewriter.getIntegerType(16) #define i32_ty rewriter.getIntegerType(32) #define i64_ty rewriter.getIntegerType(64) #define ui32_ty rewriter.getIntegerType(32, false) #define ui64_ty rewriter.getIntegerType(64, false) #define f16_ty rewriter.getF16Type() #define bf16_ty rewriter.getBF16Type() #define i8_ty rewriter.getIntegerType(8) #define i1_ty rewriter.getI1Type() #define f32_ty rewriter.getF32Type() #define f64_ty rewriter.getF64Type() #define vec_ty(type, num) VectorType::get(num, type) #define void_ty(ctx) LLVM::LLVMVoidType::get(ctx) #define struct_ty(...) LLVM::LLVMStructType::getLiteral(ctx, __VA_ARGS__) #define array_ty(elemTy, count) LLVM::LLVMArrayType::get(elemTy, count) // Attributes #define i32_arr_attr(...) rewriter.getI32ArrayAttr({__VA_ARGS__}) #define i64_arr_attr(...) rewriter.getI64ArrayAttr({__VA_ARGS__}) #define str_attr(str) ::mlir::StringAttr::get(ctx, (str)) namespace mlir { namespace triton { namespace gpu { Type getFunctionType(Type resultType, ValueRange operands); LLVM::LLVMFuncOp appendOrGetExternFuncOp(RewriterBase &rewriter, Operation *op, StringRef funcName, Type funcType, StringRef libname = "", StringRef libpath = ""); } // namespace gpu } // namespace triton namespace LLVM { using namespace mlir::triton; class SharedMemoryObject { public: SharedMemoryObject(Value base, Type baseElemType, ArrayRef offsets) : base(base), baseElemType(baseElemType), offsets(offsets.begin(), offsets.end()) {} SharedMemoryObject(Value base, Type baseElemType, int64_t rank, Location loc, RewriterBase &rewriter) : base(base), baseElemType(baseElemType) { auto b = TritonLLVMOpBuilder(loc, rewriter); offsets.append(rank, b.i32_val(0)); } SmallVector getOffsets() const { return offsets; } Value getBase() const { return base; } Type getBaseElemType() const { return baseElemType; } SmallVector getElems() const { SmallVector elems; elems.push_back(base); elems.append(offsets.begin(), offsets.end()); return elems; } SmallVector getTypes() const { SmallVector types; types.push_back(base.getType()); types.append(offsets.size(), IntegerType::get(base.getContext(), 32)); return types; } SmallVector getStrides(triton::gpu::MemDescType memDesc, Location loc, RewriterBase &rewriter) const { auto allocShape = memDesc.getAllocShape(); auto allocShapePerCTA = triton::gpu::getAllocationShapePerCTA( memDesc.getEncoding(), allocShape); auto layoutOrder = triton::gpu::getOrder(memDesc); auto allocStrides = SharedMemoryObject::getStridesForShape( allocShapePerCTA, layoutOrder, loc, rewriter); return SmallVector(allocStrides.end() - offsets.size(), allocStrides.end()); } // TODO(Keren): deprecate the method once AMD backend has cleaned up Value getCSwizzleOffset(int dim) const { assert(dim >= 0 && dim < offsets.size()); return offsets[dim]; } // TODO(Keren): deprecate the method once AMD backend has cleaned up Value getBaseBeforeSlice(int dim, Location loc, RewriterBase &rewriter) const { auto b = TritonLLVMOpBuilder(loc, rewriter); Value cSwizzleOffset = getCSwizzleOffset(dim); Value offset = b.sub(b.i32_val(0), cSwizzleOffset); Type type = base.getType(); return b.gep(type, baseElemType, base, offset); } private: static SmallVector getOrderForShape(ArrayRef shape, ArrayRef layoutOrder) { SmallVector order(shape.size()); // Default minor-to-major order std::iota(order.rbegin(), order.rend(), 0); if (layoutOrder.size() > 0) { // If a layout order is provided, we assume it specifies the order in // which the dimensions are first accessed, and unspecified dimensions // retain the minor-to-major order. For example, if order = [2, 1, 0] and // layoutOrder = [0, 1], we need to shift `layoutOrder` // by -1 (move them right). The resulting order will then be [1, 2, 0]. int rankDiff = layoutOrder.size() - shape.size(); auto minRank = std::min(shape.size(), layoutOrder.size()); for (size_t i = 0; i < minRank; ++i) order[i] = layoutOrder[i] - rankDiff; } assert(isPermutationOfIota(order) && "Invalid order"); return order; } static SmallVector getStridesForShape(ArrayRef shape, ArrayRef layoutOrder, Location loc, RewriterBase &rewriter) { SmallVector strides(shape.size()); auto order = SharedMemoryObject::getOrderForShape(shape, layoutOrder); int64_t stride = 1; auto b = TritonLLVMOpBuilder(loc, rewriter); for (auto idx : order) { strides[idx] = b.i32_val(stride); stride *= shape[idx]; } return strides; } Value base; // i32 ptr. The start address of the shared memory object. Type baseElemType; SmallVector offsets; // i32 int. The offsets are zero at the initial allocation. }; Value getStructFromSharedMemoryObject(Location loc, const SharedMemoryObject &smemObj, RewriterBase &rewriter); SharedMemoryObject getSharedMemoryObjectFromStruct(Location loc, Value llvmStruct, Type elemTy, RewriterBase &rewriter); // Convert an \param index to a multi-dim coordinate given \param shape and // \param order. SmallVector delinearize(RewriterBase &rewriter, Location loc, Value linear, ArrayRef shape, ArrayRef order); SmallVector delinearize(RewriterBase &rewriter, Location loc, unsigned linear, ArrayRef shape); SmallVector delinearize(RewriterBase &rewriter, Location loc, Value linear, ArrayRef shape); SmallVector delinearize(unsigned linear, ArrayRef shape, ArrayRef order); // Returns a tuple with the delinearized coordinates and a boolean which is true // iff the Value is not broadcasted (equivalently, if the value is the "first" // lane/thread/etc. that holds the given value). In mathy terms, the boolean is // true if the element is the canonical representative of the class. std::tuple, Value> delinearize(RewriterBase &rewriter, Location loc, triton::gpu::DistributedEncodingTrait layout, ArrayRef shape, StringAttr dimName, Value linear); Value linearize(RewriterBase &rewriter, Location loc, ArrayRef multiDim, ArrayRef shape, ArrayRef order); Value linearize(RewriterBase &rewriter, Location loc, ArrayRef multiDim, ArrayRef shape); size_t linearize(ArrayRef multiDim, ArrayRef shape, ArrayRef order); Value addStringToModule(Location loc, RewriterBase &rewriter, StringRef key, StringRef content); inline bool isKernel(FunctionOpInterface funcOp) { return funcOp.getVisibility() == SymbolTable::Visibility::Public; } inline Value getStackPointer(RewriterBase &rewriter, FunctionOpInterface funcOp) { // See NOTE: [Additional Function Arguments] if (!isKernel(funcOp)) { return funcOp.getArgument(funcOp.getNumArguments() - 2); } auto mod = funcOp->getParentOfType(); auto globalBase = dyn_cast(mod.lookupSymbol("global_smem")); assert(globalBase); return rewriter.create(funcOp.getLoc(), globalBase); } inline Value getGlobalScratchPtr(Location loc, RewriterBase &rewriter, const TargetInfoBase &targetInfo, FunctionOpInterface funcOp, Value allocOffset = {}) { // See NOTE: [Additional Function Arguments] if (!isKernel(funcOp)) { // Base for this function auto gmemBase = funcOp.getArgument(funcOp.getNumArguments() - 1); if (!allocOffset) { return gmemBase; } auto ptrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext(), 1); auto b = TritonLLVMOpBuilder(loc, rewriter); return b.gep(ptrTy, i8_ty, gmemBase, allocOffset); } // Base for entire kernel auto gmemBase = funcOp.getArgument(funcOp.getNumArguments() - 1); ModuleOp mod = funcOp.getOperation()->getParentOfType(); auto allocSizeAttr = mod.getOperation()->getAttrOfType( "ttg.global_scratch_memory_size"); if (!allocSizeAttr) { return gmemBase; } Value gridIdx[3]; Value gridDim[2]; for (int k = 0; k < 3; ++k) { gridIdx[k] = rewriter.create(loc, k); } for (int k = 0; k < 2; ++k) { gridDim[k] = rewriter.create(loc, k); } auto b = TritonLLVMOpBuilder(loc, rewriter); Value linearId = gridIdx[2]; for (int k = 0; k < 2; ++k) { linearId = b.add(gridIdx[1 - k], b.mul(linearId, gridDim[1 - k])); } auto numCTAs = triton::gpu::TritonGPUDialect::getNumCTAs(mod); if (numCTAs > 1) { linearId = b.mul(linearId, b.i32_val(numCTAs)); linearId = b.add(linearId, targetInfo.getClusterCTAId(rewriter, loc)); } auto allocSize = allocSizeAttr.getValue().getZExtValue(); Value offset = b.mul(linearId, b.i32_val(allocSize)); if (allocOffset) { offset = b.add(offset, allocOffset); } auto *ctx = rewriter.getContext(); auto res = b.gep(mlir::LLVM::LLVMPointerType::get(ctx, 1), i8_ty, gmemBase, offset); return res; } inline Value getSharedMemoryBase(Location loc, RewriterBase &rewriter, const TargetInfoBase &target, Operation *op) { auto ptrTy = LLVM::LLVMPointerType::get(rewriter.getContext(), target.getSharedAddressSpace()); auto func = op->template getParentOfType(); if (!func) func = cast(op); assert(op->hasAttr("allocation.offset")); size_t offset = cast(op->getAttr("allocation.offset")) .getValue() .getZExtValue(); auto b = TritonLLVMOpBuilder(loc, rewriter); Value offVal = b.i32_val(offset); Value base = b.gep(ptrTy, i8_ty, LLVM::getStackPointer(rewriter, func), offVal); return base; } // ----------------------------------------------------------------------- // MXFP utilities // ----------------------------------------------------------------------- // Scale a mxfp4 value by a given scale. Value mxfpScaleBf16(RewriterBase &rewriter, Location loc, Value v, Value scale, bool fastMath); } // namespace LLVM // ----------------------------------------------------------------------- // Hardware Indices // ----------------------------------------------------------------------- // If an operation is contained within a warp specialize region, this returns // the thread ID offset of that warpgroup. std::optional getWarpGroupStartThreadId(Block *block); // Returns CTA level thread ID. Value getThreadId(OpBuilder &rewriter, Location loc); // Get the lane ID, which is index of the thread within its warp. Value getLaneId(OpBuilder &rewriter, Location loc); // Get the lane ID and warp ID. std::pair getLaneAndWarpId(OpBuilder &rewriter, Location loc); // ----------------------------------------------------------------------- // Shared memory utilities // ----------------------------------------------------------------------- using LLVM::SharedMemoryObject; using ::mlir::LLVM::delinearize; using ::mlir::LLVM::SharedMemoryObject; using ::mlir::triton::gpu::AMDMfmaEncodingAttr; using ::mlir::triton::gpu::AMDWmmaEncodingAttr; using ::mlir::triton::gpu::BlockedEncodingAttr; using ::mlir::triton::gpu::CTALayoutAttr; using ::mlir::triton::gpu::DotOperandEncodingAttr; using ::mlir::triton::gpu::NvidiaMmaEncodingAttr; using ::mlir::triton::gpu::SliceEncodingAttr; inline Value dot(RewriterBase &rewriter, Location loc, ArrayRef offsets, ArrayRef strides) { assert(offsets.size() == strides.size()); auto b = TritonLLVMOpBuilder(loc, rewriter); Value ret = b.i32_val(0); for (auto [offset, stride] : llvm::zip(offsets, strides)) { ret = b.add(ret, b.mul(offset, stride)); } return ret; } /// Extend 2d shared object to 3d. /// /// If tensor has 3 dimensions, returns original shared object. /// If tensor shape is [M, N], return shared object describing shape [1, M, N] /// /// This Function is used to simplify processing of 2d and 3d dot operands, /// particularly in the conversion of local_load operation. /// /// \param rewriter /// \param loc /// \param smemObj /// \param shape shape of a tensor represented by smemObj /// \returns shared object describing 3d tensor SharedMemoryObject getExpandedSharedMemoryObject(ConversionPatternRewriter &rewriter, Location loc, SharedMemoryObject smemObj, ArrayRef shape); // "Applies" the given layout by computing layout(indices) and returning the // resulting Values. // // In other words, this generates LLVM-dialect MLIR code to "run" the layout // function. SmallVector> applyLinearLayout(Location loc, RewriterBase &rewriter, const LinearLayout &layout, ArrayRef> indices); SmallVector> emitOffsetForLayout(Attribute layout, RankedTensorType type); // Emit indices calculation within each ConversionPattern, and returns a // [elemsPerThread X rank] index matrix. // // For example, for a thread a owns `elemsPerThread` elements of a tensor with // type `type` and layout `layout`, the result will contain `elemsPerThread` // vectors. Each vector contains the SSA values of the indices required to // access the corresponding element, starting from the inner dimension. SmallVector> emitIndices(Location loc, RewriterBase &rewriter, const TargetInfoBase &target, Attribute layout, RankedTensorType type, bool withCTAOffset); // Emits IR to load data from shared memory into registers, or to store data // from registers into shared memory. // // You supply perVectorCallback, which is called once per group of register // elements to transfer. You can use this callback to emit IR to load or store // data from or to shared memory. // // elemLlvmTy should be dstTy's element type converted to an LLVM-dialect type. // // If maxVecElems is provided, we won't vectorize more than this many elements. // // Returns true on success. [[nodiscard]] bool emitTransferBetweenRegistersAndShared( RankedTensorType registerTy, triton::gpu::MemDescType sharedTy, Type elemLlvmTy, std::optional maxVecElems, const SharedMemoryObject &smemObj, Location loc, RewriterBase &rewriter, const TargetInfoBase &target, std::function perVectorCallback); [[nodiscard]] bool emitTransferBetweenRegistersAndShared( LinearLayout ®Layout, triton::gpu::MemDescType sharedTy, Type elemLlvmTy, std::optional maxVecElems, const SharedMemoryObject &smemObj, Location loc, RewriterBase &rewriter, const TargetInfoBase &target, std::function perVectorCallback); SmallVector loadSharedToDistributed(triton::gpu::LocalLoadOp localLoadOp, Type elemLlvmTy, const SharedMemoryObject &smemObj, Location loc, RewriterBase &rewriter, const TargetInfoBase &target); void storeDistributedToShared( triton::gpu::MemDescType dstTy, RankedTensorType srcTy, Type elemLlvmTy, ArrayRef srcVals, const SharedMemoryObject &smemObj, Location loc, RewriterBase &rewriter, const TargetInfoBase &target, std::pair *const llvmOpCount = nullptr); SmallVector unpackLLElements(Location loc, Value llvmStruct, RewriterBase &rewriter); Value packLLElements(Location loc, const LLVMTypeConverter *typeConverter, ValueRange resultVals, RewriterBase &rewriter, Type type); SmallVector unpackLLVector(Location loc, Value llvmVec, RewriterBase &rewriter); Value packLLVector(Location loc, ValueRange vals, RewriterBase &rewriter); inline bool isSimpleSharedMemoryAccess(ArrayRef shape, ArrayRef allocShape, triton::gpu::SharedEncodingTrait sharedEnc) { auto rank = shape.size(); auto swizzledLayout = dyn_cast(sharedEnc); bool noSwizzling = swizzledLayout && swizzledLayout.getMaxPhase() == 1; return /*no swizzling*/ noSwizzling || /*swizzling but same shape*/ shape == allocShape || /*swizzling and rank-reduced and rank >= 2*/ (shape == allocShape.take_back(rank) && rank >= 2); } inline llvm::MapVector getAllFreeVarMasks(MLIRContext *ctx) { // Mask where all elements are redundant auto kReg = str_attr("reg"); auto kLane = str_attr("lane"); auto kWarp = str_attr("warp"); auto kBlock = str_attr("block"); int32_t fullMask = -1; llvm::MapVector ret; for (auto dimName : {kReg, kLane, kWarp, kBlock}) { ret[dimName] = fullMask; } return ret; } inline llvm::MapVector getFreeVariableMasks(Type type) { auto ctx = type.getContext(); auto tensorTy = dyn_cast(type); if (!tensorTy) { return getAllFreeVarMasks(ctx); } auto ll = triton::gpu::toLinearLayout(tensorTy.getShape(), tensorTy.getEncoding()); return ll.getFreeVariableMasks(); } inline bool isCanonicalIndex(unsigned index, unsigned freeVarMask) { return (index & freeVarMask) == 0; } // Certain lowerings may introduce references to function arguments. Keep warp // group code isolated from above by invoking this function. void makeAllWarpGroupsIsolatedFromAbove(Operation *op); } // namespace mlir #endif