已合并
feat: support (built-in)indirect_load for unstructure load #682
candyhong创建于 2025年11月4日
feat: support (built-in)indirect_load for unstructure load #682
已合并
candyhong创建于 2025年11月4日
共 10 个文件变更+208-1
@@ -108,7 +108,7 @@ def ttir_to_linalg(mod, metadata, opt, *, named_ops=False):
108 "--triton-linearize",108 "--triton-linearize",
109 "--discrete-mask-access-conversion",109 "--discrete-mask-access-conversion",
110 "--triton-to-annotation",110 "--triton-to-annotation",
111- "--triton-to-unstructure",111+ f"--triton-to-unstructure=compile-on-a5={compile_on_a5}",
112 "--triton-to-hivm",112 "--triton-to-hivm",
113 "--triton-to-hfusion",113 "--triton-to-hfusion",
114 "--triton-to-llvm",114 "--triton-to-llvm",
@@ -0,0 +1,73 @@
1+// RUN: triton-adapter-opt --triton-to-unstructure=compile-on-a5=true %s | FileCheck %s
2+ 
3+tt.func public @triton_ldst_indirect_05_kernel(%arg0: !tt.ptr<f32>, %arg1: !tt.ptr<i64>, %arg2: !tt.ptr<f32>, %arg3: i32) attributes {noinline = false} {
4+ %cst = arith.constant dense<16> : tensor<8x1xi32>
5+ %c8_i32 = arith.constant 8 : i32
6+ %0 = tt.get_program_id x : i32
7+ %1 = arith.muli %0, %c8_i32 : i32
8+ %2 = tt.make_range {end = 8 : i32, start = 0 : i32} : tensor<8xi32>
9+ %3 = tt.splat %1 : i32 -> tensor<8xi32>
10+ %4 = arith.addi %3, %2 : tensor<8xi32>
11+ %5 = tt.make_range {end = 16 : i32, start = 0 : i32} : tensor<16xi32>
12+ %6 = tt.splat %arg1 : !tt.ptr<i64> -> tensor<16x!tt.ptr<i64>>
13+ %7 = tt.addptr %6, %5 : tensor<16x!tt.ptr<i64>>, tensor<16xi32>
14+ %8 = tt.load %7 : tensor<16x!tt.ptr<i64>>
15+ %9 = tt.expand_dims %2 {axis = 1 : i32} : tensor<8xi32> -> tensor<8x1xi32>
16+ %10 = tt.splat %arg3 : i32 -> tensor<8x1xi32>
17+ %11 = arith.muli %9, %10 : tensor<8x1xi32>
18+ %12 = tt.expand_dims %8 {axis = 0 : i32} : tensor<16xi64> -> tensor<1x16xi64>
19+ %13 = arith.extsi %11 : tensor<8x1xi32> to tensor<8x1xi64>
20+ %14 = tt.broadcast %13 : tensor<8x1xi64> -> tensor<8x16xi64>
21+ %15 = tt.broadcast %12 : tensor<1x16xi64> -> tensor<8x16xi64>
22+ %16 = arith.addi %14, %15 : tensor<8x16xi64>
23+ %17 = tt.splat %arg2 : !tt.ptr<f32> -> tensor<8x16x!tt.ptr<f32>>
24+ %18 = tt.addptr %17, %16 : tensor<8x16x!tt.ptr<f32>>, tensor<8x16xi64>
25+ %19 = tt.load %18 : tensor<8x16x!tt.ptr<f32>>
26+ %20 = math.exp %19 : tensor<8x16xf32>
27+ %21 = tt.expand_dims %4 {axis = 1 : i32} : tensor<8xi32> -> tensor<8x1xi32>
28+ %22 = arith.muli %21, %cst : tensor<8x1xi32>
29+ %23 = tt.expand_dims %5 {axis = 0 : i32} : tensor<16xi32> -> tensor<1x16xi32>
30+ %24 = tt.broadcast %22 : tensor<8x1xi32> -> tensor<8x16xi32>
31+ %25 = tt.broadcast %23 : tensor<1x16xi32> -> tensor<8x16xi32>
32+ %26 = arith.addi %24, %25 : tensor<8x16xi32>
33+ %27 = tt.splat %arg0 : !tt.ptr<f32> -> tensor<8x16x!tt.ptr<f32>>
34+ %28 = tt.addptr %27, %26 : tensor<8x16x!tt.ptr<f32>>, tensor<8x16xi32>
35+ tt.store %28, %20 : tensor<8x16x!tt.ptr<f32>>
36+ tt.return
37+ }
38+ 
39+ 
40+// CHECK-LABEL: tt.func public @triton_ldst_indirect_05_kernel(
41+// CHECK-SAME: %[[VAL_0:.*]]: !tt.ptr<f32>, %[[VAL_1:.*]]: !tt.ptr<i64>, %[[VAL_2:.*]]: !tt.ptr<f32>, %[[VAL_3:.*]]: i32) attributes {noinline = false} {
42+// CHECK: %[[VAL_4:.*]] = arith.constant dense<16> : tensor<8x1xi32>
43+// CHECK: %[[VAL_5:.*]] = arith.constant 8 : i32
44+// CHECK: %[[VAL_6:.*]] = tt.get_program_id x : i32
45+// CHECK: %[[VAL_7:.*]] = arith.muli %[[VAL_6]], %[[VAL_5]] : i32
46+// CHECK: %[[VAL_8:.*]] = tt.make_range {end = 8 : i32, start = 0 : i32} : tensor<8xi32>
47+// CHECK: %[[VAL_9:.*]] = tt.splat %[[VAL_7]] : i32 -> tensor<8xi32>
48+// CHECK: %[[VAL_10:.*]] = arith.addi %[[VAL_9]], %[[VAL_8]] : tensor<8xi32>
49+// CHECK: %[[VAL_11:.*]] = tt.make_range {end = 16 : i32, start = 0 : i32} : tensor<16xi32>
50+// CHECK: %[[VAL_12:.*]] = tt.splat %[[VAL_1:.*]] : !tt.ptr<i64> -> tensor<16x!tt.ptr<i64>>
51+// CHECK: %[[VAL_13:.*]] = tt.addptr %[[VAL_12]], %[[VAL_11]] : tensor<16x!tt.ptr<i64>>, tensor<16xi32>
52+// CHECK: %[[VAL_14:.*]] = tt.load %[[VAL_13]] : tensor<16x!tt.ptr<i64>>
53+// CHECK: %[[VAL_15:.*]] = tt.expand_dims %[[VAL_8]] {axis = 1 : i32} : tensor<8xi32> -> tensor<8x1xi32>
54+// CHECK: %[[VAL_16:.*]] = tt.splat %[[VAL_3:.*]] : i32 -> tensor<8x1xi32>
55+// CHECK: %[[VAL_17:.*]] = arith.muli %[[VAL_15]], %[[VAL_16]] : tensor<8x1xi32>
56+// CHECK: %[[VAL_18:.*]] = tt.expand_dims %[[VAL_14]] {axis = 0 : i32} : tensor<16xi64> -> tensor<1x16xi64>
57+// CHECK: %[[VAL_19:.*]] = arith.extsi %[[VAL_17]] : tensor<8x1xi32> to tensor<8x1xi64>
58+// CHECK: %[[VAL_20:.*]] = tt.broadcast %[[VAL_19]] : tensor<8x1xi64> -> tensor<8x16xi64>
59+// CHECK: %[[VAL_21:.*]] = tt.broadcast %[[VAL_18]] : tensor<1x16xi64> -> tensor<8x16xi64>
60+// CHECK: %[[VAL_22:.*]] = arith.addi %[[VAL_20]], %[[VAL_21]] : tensor<8x16xi64>
61+// CHECK: %[[VAL_23:.*]] = tt.indirect_load %[[VAL_2:.*]] : <f32>, %[[VAL_22]] : tensor<8x16xi64> -> tensor<8x16xf32>
62+// CHECK: %[[VAL_24:.*]] = math.exp %[[VAL_23]] : tensor<8x16xf32>
63+// CHECK: %[[VAL_25:.*]] = tt.expand_dims %[[VAL_10]] {axis = 1 : i32} : tensor<8xi32> -> tensor<8x1xi32>
64+// CHECK: %[[VAL_26:.*]] = arith.muli %[[VAL_25]], %[[VAL_4]] : tensor<8x1xi32>
65+// CHECK: %[[VAL_27:.*]] = tt.expand_dims %[[VAL_11]] {axis = 0 : i32} : tensor<16xi32> -> tensor<1x16xi32>
66+// CHECK: %[[VAL_28:.*]] = tt.broadcast %[[VAL_26]] : tensor<8x1xi32> -> tensor<8x16xi32>
67+// CHECK: %[[VAL_29:.*]] = tt.broadcast %[[VAL_27]] : tensor<1x16xi32> -> tensor<8x16xi32>
68+// CHECK: %[[VAL_30:.*]] = arith.addi %[[VAL_28]], %[[VAL_29]] : tensor<8x16xi32>
69+// CHECK: %[[VAL_31:.*]] = tt.splat %[[VAL_0:.*]] : !tt.ptr<f32> -> tensor<8x16x!tt.ptr<f32>>
70+// CHECK: %[[VAL_32:.*]] = tt.addptr %[[VAL_31]], %[[VAL_30]] : tensor<8x16x!tt.ptr<f32>>, tensor<8x16xi32>
71+// CHECK: tt.store %[[VAL_32]], %[[VAL_24]] : tensor<8x16x!tt.ptr<f32>>
72+// CHECK: tt.return
73+// CHECK: }
@@ -576,6 +576,16 @@ private:
576 static constexpr llvm::StringRef funcNameBase = "triton_embedding_gather";576 static constexpr llvm::StringRef funcNameBase = "triton_embedding_gather";
577};577};
578 578 
579+class IndirectLoadConverter : public OpConversionPattern<triton::IndirectLoadOp> {
580+public:
581+ using OpConversionPattern<triton::IndirectLoadOp>::OpConversionPattern;
582+ LogicalResult
583+ matchAndRewrite(triton::IndirectLoadOp op, OpAdaptor adaptor,
584+ ConversionPatternRewriter &rewriter) const override;
585+private:
586+ static constexpr llvm::StringRef funcNameBase = "triton_indirect_load";
587+};
588+ 
579} // end of namespace TTOpConverters589} // end of namespace TTOpConverters
580 590 
581#endif591#endif
@@ -9,6 +9,9 @@ def TritonToUnstructure : Pass<"triton-to-unstructure", "mlir::ModuleOp"> {
9 let options = [9 let options = [
10 Option<"forceScalarizeMode", "force-scalarize-mode", "bool", "false", 10 Option<"forceScalarizeMode", "force-scalarize-mode", "bool", "false",
11 "Scalarize unstructured memory access even if structured dimensions are mixed.">,11 "Scalarize unstructured memory access even if structured dimensions are mixed.">,
12+ Option<"compileOnA5", "compile-on-a5",
13+ "bool", /*default*/"false",
14+ "compile on a5">
12 ];15 ];
13}16}
14 17 
@@ -35,6 +35,8 @@
35#define GEN_PASS_DEF_TRITONTOUNSTRUCTURE35#define GEN_PASS_DEF_TRITONTOUNSTRUCTURE
36#include "ascend/triton-adapter/include/TritonToUnstructure/Passes.h.inc"36#include "ascend/triton-adapter/include/TritonToUnstructure/Passes.h.inc"
37 37 
38+extern bool compileOnA5Flag;
39+ 
38namespace mlir {40namespace mlir {
39namespace triton {41namespace triton {
40 42 
@@ -2036,4 +2036,48 @@ EmbeddingGatherConverter::matchAndRewrite(triton::EmbeddingGatherOp op, OpAdapto
2036 return success();2036 return success();
2037}2037}
2038 2038 
2039+ 
2040+LogicalResult
2041+IndirectLoadConverter::matchAndRewrite(triton::IndirectLoadOp op, OpAdaptor adaptor,
2042+ ConversionPatternRewriter &rewriter) const {
2043+ auto loc = op.getLoc();
2044+ 
2045+ auto moduleOp = op->getParentOfType<ModuleOp>();
2046+ rewriter.setInsertionPoint(moduleOp.getBody(),
2047+ std::prev(moduleOp.getBody()->end()));
2048+ 
2049+ llvm::SmallString<128> funcName = funcNameBase;
2050+ int uniqueId = 0;
2051+ while (SymbolTable::lookupSymbolIn(moduleOp, funcName)) {
2052+ funcName = funcNameBase;
2053+ funcName += ("_" + std::to_string(uniqueId++));
2054+ }
2055+ 
2056+ auto src = adaptor.getSrc();
2057+ auto offsets = op.getOffsets();
2058+ auto mask = op.getMask();
2059+ auto other = op.getOther();
2060+ auto res = op.getResult();
2061+ auto resTy = res.getType();
2062+ 
2063+ // convert !tt.ptr<f32> to memref<?xf32>
2064+ auto srcTy = cast<MemRefType>(src.getType());
2065+ SmallVector<Type> inputTypes({srcTy, offsets.getType()});
2066+ if (mask) inputTypes.push_back(mask.getType());
2067+ if (other) inputTypes.push_back(other.getType());
2068+ auto libFnType = rewriter.getFunctionType(inputTypes, {resTy});
2069+ auto funcOp = rewriter.create<func::FuncOp>(loc, funcName.str(), libFnType);
2070+ SymbolTable::setSymbolVisibility(funcOp, SymbolTable::Visibility::Private);
2071+ 
2072+ rewriter.setInsertionPoint(op);
2073+ SmallVector<Value> inputVals({src, offsets});
2074+ if (mask) inputVals.push_back(mask);
2075+ if (other) inputVals.push_back(other);
2076+ auto callOp = rewriter.create<func::CallOp>(loc, funcOp.getSymNameAttr(),
2077+ TypeRange({resTy}),
2078+ inputVals);
2079+ rewriter.replaceOp(op, callOp);
2080+ return success();
2081+}
2082+ 
2039} // namespace TTOpConverters2083} // namespace TTOpConverters
@@ -584,6 +584,7 @@ void TritonToLinalgPass::populateTritonToLinalgConversionPatterns(
584 patterns.add<TTOpConverters::SortOpConverter>(patterns.getContext());584 patterns.add<TTOpConverters::SortOpConverter>(patterns.getContext());
585 patterns.add<TTOpConverters::DotScaledConverter>(patterns.getContext());585 patterns.add<TTOpConverters::DotScaledConverter>(patterns.getContext());
586 patterns.add<TTOpConverters::PtrToIntConverter>(patterns.getContext());586 patterns.add<TTOpConverters::PtrToIntConverter>(patterns.getContext());
587+ patterns.add<TTOpConverters::IndirectLoadConverter>(patterns.getContext());
587 588 
588 if (!this->namedOps) {589 if (!this->namedOps) {
589 linalg::populateElementwiseToLinalgConversionPatterns(patterns);590 linalg::populateElementwiseToLinalgConversionPatterns(patterns);
@@ -297,6 +297,20 @@ LogicalResult UnstructuredMemAccessConverter<MemAccOpTy>::matchAndRewrite(
297 // Force scalarize if memory is not aligned297 // Force scalarize if memory is not aligned
298 if (sizeInByte % 32 != 0)298 if (sizeInByte % 32 != 0)
299 ptrOffsetInfo.setUnstructured(ptrOffsetInfo.getRank());299 ptrOffsetInfo.setUnstructured(ptrOffsetInfo.getRank());
300+
301+ // Fast path on A5: rewrite tl.load to tt.indirect_load directly.
302+ if constexpr (std::is_same_v<MemAccOpTy, triton::LoadOp>) {
303+ if (compileOnA5Flag && ptrOffsetInfo.isUnstructured()) {
304+ assert(!isa<RankedTensorType>(srcPtr.getType()) && "src must be ptr type");
305+ Value mask = op.getMask();
306+ Value other = op.getOther();
307+ auto resultType = op.getType();
308+ auto indirect = rewriter.create<triton::IndirectLoadOp>(
309+ loc, resultType, srcPtr, ptrOffset, mask, other);
310+ rewriter.replaceOp(op, indirect.getResult());
311+ return success();
312+ }
313+ }
300 314 
301 Value iterArg = nullptr;315 Value iterArg = nullptr;
302 316 
@@ -572,6 +586,8 @@ TritonToUnstructurePass::TritonToUnstructurePass(
572 : TritonToUnstructureBase(options) {}586 : TritonToUnstructureBase(options) {}
573 587 
574void TritonToUnstructurePass::runOnOperation() {588void TritonToUnstructurePass::runOnOperation() {
589+ compileOnA5Flag = this->compileOnA5;
590+ 
575 ModuleOp moduleOp = getOperation();591 ModuleOp moduleOp = getOperation();
576 MLIRContext *ctx = &getContext();592 MLIRContext *ctx = &getContext();
577 593 
@@ -1515,4 +1515,54 @@ def TT_CustomOp : TT_Op<"custom", [Pure, MemoryEffects<[MemWrite]>]> {
1515 let assemblyFormat = "$op_name attr-dict ($args^ `:` type($args))?";1515 let assemblyFormat = "$op_name attr-dict ($args^ `:` type($args))?";
1516}1516}
1517 1517 
1518+//
1519+// Built-in: IndirectLoad Op
1520+//
1521+def TT_IndirectLoadOp : TT_Op<"indirect_load", [
1522+ DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
1523+ AttrSizedOperandSegments
1524+]> {
1525+ let summary = "Built-in: indirect load from global memory using per-element offsets with optional mask/other";
1526+ 
1527+ let description = [{
1528+ Built-in operation emitted by the compiler for unstructured (discrete) memory
1529+ accesses.These are not written directly in the user IR.
1530+ 
1531+ Load values from global memory based on per-element offsets. If `mask`
1532+ is provided, false lanes return `other`.
1533+ 
1534+ The operation takes:
1535+ - src: Source pointer
1536+ - offsets: Tensor of per-element offsets (relative to `src`) for accessing source memory
1537+ - mask (optional): if mask[idx] is false, do not load the data at address pointer[idx]
1538+ - other (optional): if mask[idx] is false, return other[idx]
1539+ }];
1540+ 
1541+ let arguments = (
1542+ ins TT_Ptr:$src,
1543+ TT_IntTensor:$offsets,
1544+ Optional<TT_BoolLike>:$mask,
1545+ Optional<TT_Type>:$other
1546+ );
1547+ 
1548+ let results = (outs TT_Tensor:$result);
1549+ 
1550+ let assemblyFormat = [{
1551+ $src `:` type($src) `,`
1552+ $offsets `:` type($offsets)
1553+ (`,` $mask^ `:` type($mask))?
1554+ (`,` $other^ `:` type($other))?
1555+ attr-dict `->` type($result)
1556+ }];
1557+ 
1558+ let builders = [
1559+ OpBuilder<(ins
1560+ "Value":$src,
1561+ "Value":$offsets,
1562+ "Value":$mask,
1563+ "Value":$blocksize
1564+ )>
1565+ ];
1566+}
1567+ 
1518#endif // Triton_OPS1568#endif // Triton_OPS
@@ -55,6 +55,14 @@ void EmbeddingGatherOp::getEffects(
55 triton::GlobalMemory::get());55 triton::GlobalMemory::get());
56}56}
57 57 
58+ 
59+void IndirectLoadOp::getEffects(
60+ SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
61+ &effects) {
62+ effects.emplace_back(MemoryEffects::Read::get(), &getSrcMutable(),
63+ triton::GlobalMemory::get());
64+}
65+ 
58} // namespace triton66} // namespace triton
59} // namespace mlir67} // namespace mlir
60 68