This is an automated email from the ASF dual-hosted git repository.

masahi pushed a commit to branch unity
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/unity by this push:
     new 17be2d71ad [Unity] Support causal mask for `R.nn.attention` (#14907)
17be2d71ad is described below

commit 17be2d71ad6052f085e22739235cd60c109337b3
Author: Yaxing Cai <[email protected]>
AuthorDate: Tue May 23 18:07:46 2023 -0700

    [Unity] Support causal mask for `R.nn.attention` (#14907)
    
    * [Unity]Support causal mask for `R.nn.attention`
    
    This PR introduces the causal mask for `R.nn.attention` and its cutlass 
dispatch and legalization. The `causal_mask` argument accepts 2 types of causal 
masks - "TopLeft" and "BottomRight".
    For example, with `seq_len = 4`, `seq_len_kv = 2`,
    mask for "TopLeft":
    [[1, 0],
     [1, 1],
     [1, 1],
     [1, 1]]
    mask for "BottomRight":
    [[1, 1],
     [1, 1],
     [1, 1],
     [1, 1]]
    with `seq_len = 2`, `seq_len_kv = 4`,
    mask for "TopLeft":
    [[1, 0, 0, 0],
     [1, 1, 0, 0]]
    mask for "BottomRight":
    [[1, 1, 1, 0],
     [1, 1, 1, 1]]
    
    * update fx_translator and legalize_ops unit test
    
    * apply code review suggestions
---
 include/tvm/relax/attrs/nn.h                       |   3 +
 python/tvm/contrib/cutlass/attention_operation.py  |   1 +
 python/tvm/contrib/cutlass/build.py                |  10 ++
 python/tvm/contrib/cutlass/gen_tensor_op.py        |   2 +
 python/tvm/relax/frontend/torch/fx_translator.py   |   9 +-
 python/tvm/relax/op/nn/nn.py                       |  28 +++++-
 python/tvm/relax/transform/legalize_ops/nn.py      |  26 ++++-
 src/relax/op/nn/attention.cc                       |   7 +-
 src/relax/op/nn/attention.h                        |   3 +-
 tests/python/relax/test_codegen_cutlass.py         |  59 ++++++++++--
 tests/python/relax/test_frontend_from_fx.py        |  36 +++++++
 .../python/relax/test_transform_legalize_ops_nn.py | 105 ++++++++++++---------
 12 files changed, 229 insertions(+), 60 deletions(-)

diff --git a/include/tvm/relax/attrs/nn.h b/include/tvm/relax/attrs/nn.h
index c1ca468fc9..b759ce6c26 100644
--- a/include/tvm/relax/attrs/nn.h
+++ b/include/tvm/relax/attrs/nn.h
@@ -313,10 +313,13 @@ struct DropoutAttrs : public tvm::AttrsNode<DropoutAttrs> 
{
 /*! \brief Attributes used in dropout operator */
 struct AttentionAttrs : public tvm::AttrsNode<AttentionAttrs> {
   Optional<FloatImm> scale;
+  Optional<String> causal_mask;
 
   TVM_DECLARE_ATTRS(AttentionAttrs, "relax.attrs.AttentionAttrs") {
     TVM_ATTR_FIELD(scale).describe(
         "The custom scale applied before the softmax. The default value is 1 / 
sqrt(head_dim).");
+    TVM_ATTR_FIELD(causal_mask)
+        .describe("The type of the causal mask, i.e. 'TopLeft' and 
'BottomRight'.");
   }
 };  // struct AttentionAttrs
 
diff --git a/python/tvm/contrib/cutlass/attention_operation.py 
b/python/tvm/contrib/cutlass/attention_operation.py
index c728f7fe4b..8a96e70fe4 100644
--- a/python/tvm/contrib/cutlass/attention_operation.py
+++ b/python/tvm/contrib/cutlass/attention_operation.py
@@ -108,6 +108,7 @@ def instantiate_attention_template(attrs):
   p.num_queries = ${num_queries}; // S
   p.num_keys = ${num_keys}; // S'
   p.scale = ${scale};
+  p.custom_mask_type = ${custom_mask_type};
 
 
   p.o_strideM = p.head_dim_value * p.num_heads; // H' * N
diff --git a/python/tvm/contrib/cutlass/build.py 
b/python/tvm/contrib/cutlass/build.py
index f263c9c22f..b8b65d27bd 100644
--- a/python/tvm/contrib/cutlass/build.py
+++ b/python/tvm/contrib/cutlass/build.py
@@ -831,6 +831,15 @@ class CutlassRelaxFunctionAnnotator(relax.PyExprMutator):
         _, _, _, head_dim_value = v_shape
         scale = op_attrs.scale
 
+        if op_attrs.causal_mask is None:
+            custom_mask_type = 0
+        if op_attrs.causal_mask == "TopLeft":
+            custom_mask_type = 1
+        elif op_attrs.causal_mask == "BottomRight":
+            custom_mask_type = 2
+        else:
+            raise NotImplementedError()
+
         return f.with_attrs(
             {
                 "op_type": op_type,
@@ -845,6 +854,7 @@ class CutlassRelaxFunctionAnnotator(relax.PyExprMutator):
                 "scale": scale,
                 "arch": self.options["sm"],
                 "qkv_layout": qkv_layout,
+                "custom_mask_type": custom_mask_type,
                 **arg,
             }
         )
diff --git a/python/tvm/contrib/cutlass/gen_tensor_op.py 
b/python/tvm/contrib/cutlass/gen_tensor_op.py
index f94d7ef467..abc13d3570 100644
--- a/python/tvm/contrib/cutlass/gen_tensor_op.py
+++ b/python/tvm/contrib/cutlass/gen_tensor_op.py
@@ -721,6 +721,8 @@ def instantiate_template(func_name, annotations, func_args):
         attrs["scale"] = (
             float(1 / math.sqrt(h.value)) if annotations["scale"] is None else 
annotations["scale"]
         )
+        attrs["custom_mask_type"] = annotations["custom_mask_type"]
+
         assert (
             attrs["scale"] > 0 or attrs["scale"] < 0
         ), "Cutlass may generate nan occasionally when scale == 0.0"
diff --git a/python/tvm/relax/frontend/torch/fx_translator.py 
b/python/tvm/relax/frontend/torch/fx_translator.py
index c5d65e2f0d..ab6a707cb0 100644
--- a/python/tvm/relax/frontend/torch/fx_translator.py
+++ b/python/tvm/relax/frontend/torch/fx_translator.py
@@ -1015,19 +1015,22 @@ class TorchFXImporter:
         )
 
     def _scaled_dot_product_attention(self, node: fx.node.Node) -> relax.Var:
-        assert len(node.args) <= 4, "Dropout, and causal masking are not 
supported."
+        assert (
+            len(node.args) <= 4
+        ), "Dropout is not supported, and is_causal should be called by 
kwargs."
         transpose_S_H = lambda tensor: relax.op.permute_dims(tensor, [0, 2, 1, 
3])
         query = transpose_S_H(self.env[node.args[0]])
         key = transpose_S_H(self.env[node.args[1]])
         value = transpose_S_H(self.env[node.args[2]])
+        causal_mask = "TopLeft" if node.kwargs.get("is_causal", False) else 
None
 
         if len(node.args) == 4:
             mask = self.env[node.args[3]]
             msg = "Only a float mask is supported for the attn_mask input."
             assert "float" in mask.struct_info.dtype, msg
-            attn = relax.op.nn.attention(query, key, value, bias=mask)
+            attn = relax.op.nn.attention(query, key, value, bias=mask, 
causal_mask=causal_mask)
         else:
-            attn = relax.op.nn.attention(query, key, value)
+            attn = relax.op.nn.attention(query, key, value, 
causal_mask=causal_mask)
 
         return self.block_builder.emit(attn)
 
diff --git a/python/tvm/relax/op/nn/nn.py b/python/tvm/relax/op/nn/nn.py
index 601a0c8439..92bb7c2042 100644
--- a/python/tvm/relax/op/nn/nn.py
+++ b/python/tvm/relax/op/nn/nn.py
@@ -1005,6 +1005,7 @@ def attention(
     value: Expr,
     bias: Optional[Expr] = None,
     scale: Optional[FloatImm] = None,
+    causal_mask: Optional[str] = None,
 ) -> Expr:
     r"""Computes fused multi head attention.
 
@@ -1035,8 +1036,29 @@ def attention(
         a 4-D tensor ending with seq_len_kv, and broadcastable to
         (batch_size, num_head, seq_len, seq_len_kv).
 
-    scale: Optional[FloatImm]
-        The custom scale applied before the softmax. The default value is 1 / 
sqrt(head_dim).
+    causal_mask: Optional[str]
+        The optional causal mask, i.e. 'TopLeft' and 'BottomRight'.
+        For 'TopLeft', the mask matrix is as `np.tril(*, k=0)`,
+        while for 'BottomRight', the mask matrix is as `np.tril(*, 
k=abs(seq_len - seq_len_kv))`
+        For example, with seq_len = 4, seq_len_kv = 2,
+        mask for 'TopLeft':
+        [[1, 0],
+         [1, 1],
+         [1, 1],
+         [1, 1]]
+        mask for 'BottomRight':
+        [[1, 1],
+         [1, 1],
+         [1, 1],
+         [1, 1]]
+        with seq_len = 2, seq_len_kv = 4,
+        mask for 'TopLeft':
+        [[1, 0, 0, 0],
+         [1, 1, 0, 0]]
+        mask for 'BottomRight':
+        [[1, 1, 1, 0],
+         [1, 1, 1, 1]]
+
 
     Returns
     -------
@@ -1044,4 +1066,4 @@ def attention(
         The computed result. The layout of the output should be
         (batch_size, seq_len, num_head, head_dim_v).
     """
-    return _ffi_api.attention(query, key, value, bias, scale)  # type: ignore
+    return _ffi_api.attention(query, key, value, bias, scale, causal_mask)  # 
type: ignore
diff --git a/python/tvm/relax/transform/legalize_ops/nn.py 
b/python/tvm/relax/transform/legalize_ops/nn.py
index 9eea40b3fb..514c3d0782 100644
--- a/python/tvm/relax/transform/legalize_ops/nn.py
+++ b/python/tvm/relax/transform/legalize_ops/nn.py
@@ -18,6 +18,7 @@
 """Default legalization function for neural network operators."""
 import logging
 import math
+from typing import Optional
 
 from tvm import topi, tir, te
 from ...block_builder import BlockBuilder
@@ -335,7 +336,12 @@ def _nn_dropout(bb: BlockBuilder, call: Call) -> Expr:
 
 
 def _te_attention(
-    q: te.Tensor, k: te.Tensor, v: te.Tensor, bias: te.Tensor, scale: 
tir.FloatImm
+    q: te.Tensor,
+    k: te.Tensor,
+    v: te.Tensor,
+    bias: te.Tensor,
+    scale: tir.FloatImm,
+    causal_mask: Optional[str],
 ) -> te.Tensor:
     batch_size, seq_len, num_head, head_dim = q.shape
     _, seq_len_kv, _, head_dim_v = v.shape
@@ -354,7 +360,21 @@ def _te_attention(
         p = topi.reshape(p, [batch_size, num_head, seq_len, seq_len_kv])
         p = topi.add(p, bias)
         p = topi.reshape(p, [batch_size * num_head, seq_len, seq_len_kv])
-    s = topi.nn.softmax(p)
+    if causal_mask is None:
+        s = topi.nn.softmax(p)
+    else:
+        if causal_mask == "TopLeft":
+            offset = tir.IntImm("int32", 0)
+        elif causal_mask == "BottomRight":
+            offset = tir.IntImm("int32", abs(seq_len - seq_len_kv))
+        else:
+            raise NotImplementedError()
+        p_masked = topi.trilu(p, k=offset, upper=False)
+        p_masked_exp = topi.trilu(
+            topi.exp(p_masked - topi.max(p_masked, axis=-1, keepdims=True)), 
k=offset, upper=False
+        )
+        p_masked_sum = topi.sum(p_masked_exp, axis=-1, keepdims=True)
+        s = topi.divide(p_masked_exp, p_masked_sum)
     o = topi.nn.batch_matmul(s, v, transpose_b=False)
     o = topi.reshape(o, [batch_size, num_head, seq_len, head_dim_v])
     return topi.transpose(o, [0, 2, 1, 3])
@@ -369,6 +389,7 @@ def _nn_attention(bb: BlockBuilder, call: Call) -> Expr:
         call.args[2],
         None,
         call.attrs.scale,
+        call.attrs.causal_mask,
         primfunc_name_hint="attention",
     )
 
@@ -382,6 +403,7 @@ def _nn_attention_bias(bb: BlockBuilder, call: Call) -> 
Expr:
         call.args[2],
         call.args[3],
         call.attrs.scale,
+        call.attrs.causal_mask,
         primfunc_name_hint="attention_bias",
     )
 
diff --git a/src/relax/op/nn/attention.cc b/src/relax/op/nn/attention.cc
index c83e49c70c..55757552db 100644
--- a/src/relax/op/nn/attention.cc
+++ b/src/relax/op/nn/attention.cc
@@ -28,9 +28,11 @@ namespace relax {
 /* relax.nn.attention */
 TVM_REGISTER_NODE_TYPE(AttentionAttrs);
 
-Expr attention(Expr query, Expr key, Expr value, Optional<Expr> bias, 
Optional<FloatImm> scale) {
+Expr attention(Expr query, Expr key, Expr value, Optional<Expr> bias, 
Optional<FloatImm> scale,
+               Optional<String> causal_mask) {
   ObjectPtr<AttentionAttrs> attrs = make_object<AttentionAttrs>();
   attrs->scale = scale;
+  attrs->causal_mask = causal_mask;
   if (bias.defined()) {
     return Call(Op::Get("relax.nn.attention_bias"),
                 {std::move(query), std::move(key), std::move(value), 
std::move(bias.value())},
@@ -110,7 +112,8 @@ StructInfo InferStructInfoAttention(const Call& call, const 
BlockBuilder& ctx) {
 }
 
 Call InferMixedPrecisionAttention(const Call& call, const DataType& out_dtype) 
{
-  return Downcast<Call>(attention(call->args[0], call->args[1], call->args[2], 
NullOpt, NullOpt));
+  return Downcast<Call>(
+      attention(call->args[0], call->args[1], call->args[2], NullOpt, NullOpt, 
NullOpt));
 }
 
 TVM_REGISTER_OP("relax.nn.attention")
diff --git a/src/relax/op/nn/attention.h b/src/relax/op/nn/attention.h
index 7eda30b408..8bbf2596ce 100644
--- a/src/relax/op/nn/attention.h
+++ b/src/relax/op/nn/attention.h
@@ -33,7 +33,8 @@ namespace tvm {
 namespace relax {
 
 /*! \brief fused multi head attention */
-Expr attention(Expr query, Expr key, Expr value, Optional<Expr> bias, 
Optional<FloatImm> scale);
+Expr attention(Expr query, Expr key, Expr value, Optional<Expr> bias, 
Optional<FloatImm> scale,
+               Optional<String> causal_mask);
 
 }  // namespace relax
 }  // namespace tvm
diff --git a/tests/python/relax/test_codegen_cutlass.py 
b/tests/python/relax/test_codegen_cutlass.py
index 000bea7a0c..c0a72fffb9 100644
--- a/tests/python/relax/test_codegen_cutlass.py
+++ b/tests/python/relax/test_codegen_cutlass.py
@@ -552,7 +552,7 @@ def attention_size(request):
     return request.param
 
 
-def get_relax_attention_module(q, k, v, bias=None, qk_scale=None):
+def get_relax_attention_module(q, k, v, bias=None, qk_scale=None, causal=None):
     dtype = str(q.dtype)
 
     from tvm.script.ir_builder import IRBuilder
@@ -571,7 +571,7 @@ def get_relax_attention_module(q, k, v, bias=None, 
qk_scale=None):
             if bias is not None:
                 bias = R.arg("bias", R.Tensor(bias.shape, dtype))
             with R.dataflow() as frame:
-                result = R.emit(R.nn.attention(q, k, v, bias, qk_scale))
+                result = R.emit(R.nn.attention(q, k, v, bias, qk_scale, 
causal))
                 R.output(result)
 
             R.func_ret_value(frame.output_vars[0])
@@ -581,7 +581,7 @@ def get_relax_attention_module(q, k, v, bias=None, 
qk_scale=None):
 
 
 @memoize("topi.tests.test_codegen_cutlass.test_attention_offload")
-def get_numpy_attention_ref(b, s, s_kv, n, h, h_v, bias_shape, qk_scale, 
dtype):
+def get_numpy_attention_ref(b, s, s_kv, n, h, h_v, bias_shape, qk_scale, 
causal, dtype):
     q = np.random.randn(b, s, n, h).astype(dtype)
     k = np.random.randn(b, s_kv, n, h).astype(dtype)
     v = np.random.randn(b, s_kv, n, h_v).astype(dtype)
@@ -596,7 +596,21 @@ def get_numpy_attention_ref(b, s, s_kv, n, h, h_v, 
bias_shape, qk_scale, dtype):
         score = score + bias  # b, n, s, s_kv
     else:
         bias = None
-    attn = tvm.topi.testing.softmax_python(score, -1)
+    if causal == "none":
+        attn = tvm.topi.testing.softmax_python(score, -1)
+    else:
+        if causal == "TopLeft":
+            offset = 0
+        elif causal == "BottomRight":
+            offset = abs(s - s_kv)
+        else:
+            raise NotImplementedError()
+        score_masked = np.tril(score, k=offset)
+        score_masked_exp = np.tril(
+            np.exp(score_masked - np.max(score_masked, axis=-1, 
keepdims=True)), k=offset
+        )
+        score_masked_sum = np.sum(score_masked_exp, axis=-1, keepdims=True)
+        attn = np.divide(score_masked_exp, score_masked_sum)
     vt = v.transpose(0, 2, 1, 3)  # b, n, s_kv, h_v
     ref = attn @ vt  # b, n, s, h_v
     return q, k, v, bias, ref.transpose(0, 2, 1, 3)  # b, s, n, h_v
@@ -605,7 +619,7 @@ def get_numpy_attention_ref(b, s, s_kv, n, h, h_v, 
bias_shape, qk_scale, dtype):
 def test_attention_offload(attention_size, attention_dtype):
     b, (s, s_kv), n, (h, h_v) = attention_size
     q, k, v, _, ref = get_numpy_attention_ref(
-        b, s, s_kv, n, h, h_v, "none", "none", attention_dtype
+        b, s, s_kv, n, h, h_v, "none", "none", "none", attention_dtype
     )
 
     mod = get_relax_attention_module(q, k, v)
@@ -634,7 +648,7 @@ def attention_bias_size(request):
 def test_attention_bias_offload(attention_bias_size):
     b, (s, s_kv), n, (h, h_v), bias_shape = attention_bias_size
     q, k, v, bias, ref = get_numpy_attention_ref(
-        b, s, s_kv, n, h, h_v, bias_shape, "none", "float32"
+        b, s, s_kv, n, h, h_v, bias_shape, "none", "none", "float32"
     )
 
     mod = get_relax_attention_module(q, k, v, bias)
@@ -662,7 +676,7 @@ def attention_scale(request):
 def test_attention_scale_offload(attention_scale_size, attention_scale):
     b, (s, s_kv), n, (h, h_v), bias_shape = attention_scale_size
     q, k, v, bias, ref = get_numpy_attention_ref(
-        b, s, s_kv, n, h, h_v, bias_shape, attention_scale, "float32"
+        b, s, s_kv, n, h, h_v, bias_shape, attention_scale, "none", "float32"
     )
 
     mod = get_relax_attention_module(q, k, v, bias, attention_scale)
@@ -673,6 +687,37 @@ def test_attention_scale_offload(attention_scale_size, 
attention_scale):
     tvm.testing.assert_allclose(out, ref, rtol=1e-2, atol=1e-2)
 
 
[email protected](
+    params=[
+        # B, S, N, H, bias_shape
+        (2, (16, 8), 4, (8, 16), "none"),
+        (2, (8, 16), 4, (8, 16), "none"),
+        (2, (16, 8), 4, (8, 16), (2, 4, 16, 8)),
+    ]
+)
+def attention_causal_size(request):
+    return request.param
+
+
[email protected](params=["TopLeft", "BottomRight"])
+def attention_causal(request):
+    return request.param
+
+
+def test_attention_causal_offload(attention_causal_size, attention_causal):
+    b, (s, s_kv), n, (h, h_v), bias_shape = attention_causal_size
+    q, k, v, bias, ref = get_numpy_attention_ref(
+        b, s, s_kv, n, h, h_v, bias_shape, "none", attention_causal, "float32"
+    )
+
+    mod = get_relax_attention_module(q, k, v, bias, None, attention_causal)
+    if bias is None:
+        out = get_result_with_relax_cutlass_offload(mod, q, k, v, 
num_final_bindings=3)
+    else:
+        out = get_result_with_relax_cutlass_offload(mod, q, k, v, bias, 
num_final_bindings=3)
+    tvm.testing.assert_allclose(out, ref, rtol=1e-2, atol=1e-2)
+
+
 @memoize("topi.tests.test_codegen_cutlass.test_stacked_attention_offload")
 def get_numpy_stacked_attention_ref(b, s, n, h, h_v, bias_shape, qk_scale, 
dtype):
     qkv = np.random.randn(b, s, n * h + n * h + n * h_v).astype(dtype)
diff --git a/tests/python/relax/test_frontend_from_fx.py 
b/tests/python/relax/test_frontend_from_fx.py
index 40b9519386..ba8f776aa6 100644
--- a/tests/python/relax/test_frontend_from_fx.py
+++ b/tests/python/relax/test_frontend_from_fx.py
@@ -2644,6 +2644,31 @@ def test_attention():
                 R.output(gv)
             return gv
 
+    @I.ir_module
+    class Expected3:
+        @R.function
+        def main(
+            inp_0: R.Tensor((32, 8, 128, 64), dtype="float32"),
+            inp_1: R.Tensor((32, 8, 128, 64), dtype="float32"),
+            inp_2: R.Tensor((32, 8, 128, 64), dtype="float32"),
+        ) -> R.Tensor((32, 128, 8, 64), dtype="float32"):
+            with R.dataflow():
+                lv: R.Tensor((32, 128, 8, 64), dtype="float32") = 
R.permute_dims(
+                    inp_0, axes=[0, 2, 1, 3]
+                )
+                lv1: R.Tensor((32, 128, 8, 64), dtype="float32") = 
R.permute_dims(
+                    inp_1, axes=[0, 2, 1, 3]
+                )
+                lv2: R.Tensor((32, 128, 8, 64), dtype="float32") = 
R.permute_dims(
+                    inp_2, axes=[0, 2, 1, 3]
+                )
+                lv3: R.Tensor((32, 128, 8, 64), dtype="float32") = 
R.nn.attention(
+                    lv, lv1, lv2, scale=None, causal_mask="TopLeft"
+                )
+                gv: R.Tensor((32, 128, 8, 64), dtype="float32") = lv3
+                R.output(gv)
+            return gv
+
     verify_model(
         lambda q, k, v: F.scaled_dot_product_attention(q, k, v),
         [
@@ -2667,6 +2692,17 @@ def test_attention():
         Expected2,
     )
 
+    verify_model(
+        lambda q, k, v: F.scaled_dot_product_attention(q, k, v, 
is_causal=True),
+        [
+            ([32, 8, 128, 64], "float32"),
+            ([32, 8, 128, 64], "float32"),
+            ([32, 8, 128, 64], "float32"),
+        ],
+        {},
+        Expected3,
+    )
+
 
 if __name__ == "__main__":
     tvm.testing.main()
diff --git a/tests/python/relax/test_transform_legalize_ops_nn.py 
b/tests/python/relax/test_transform_legalize_ops_nn.py
index 1ff0569629..cbbacbabda 100644
--- a/tests/python/relax/test_transform_legalize_ops_nn.py
+++ b/tests/python/relax/test_transform_legalize_ops_nn.py
@@ -2676,14 +2676,14 @@ def test_attention():
         @R.function
         def main(q: R.Tensor((4, 16, 32, 8), "float32"), k: R.Tensor((4, 8, 
32, 8), "float32"), v: R.Tensor((4, 8, 32, 16), "float32"), bias: R.Tensor((4, 
32, 16, 8), "float32")):
             scale = T.FloatImm("float32", 0.1)
-            gv: R.Tensor((4, 16, 32, 16), "float32") = R.nn.attention(q, k, v, 
bias, scale)
+            gv: R.Tensor((4, 16, 32, 16), "float32") = R.nn.attention(q, k, v, 
bias, scale=scale, causal_mask="TopLeft")
             return gv
 
     @tvm.script.ir_module
     class Expected:
         @T.prim_func
-        def attention_bias(rxplaceholder: T.Buffer((T.int64(4), T.int64(16), 
T.int64(32), T.int64(8)), "float32"), rxplaceholder_1: T.Buffer((T.int64(4), 
T.int64(8), T.int64(32), T.int64(8)), "float32"), rxplaceholder_2: 
T.Buffer((T.int64(4), T.int64(8), T.int64(32), T.int64(16)), "float32"), 
rxplaceholder_3: T.Buffer((T.int64(4), T.int64(32), T.int64(16), T.int64(8)), 
"float32"), T_transpose: T.Buffer((T.int64(4), T.int64(16), T.int64(32), 
T.int64(16)), "float32")):
-            T.func_attr({"tir.noalias": True})
+        def attention_bias(A: T.Buffer((T.int64(4), T.int64(16), T.int64(32), 
T.int64(8)), "float32"), B: T.Buffer((T.int64(4), T.int64(8), T.int64(32), 
T.int64(8)), "float32"), C: T.Buffer((T.int64(4), T.int64(8), T.int64(32), 
T.int64(16)), "float32"), D: T.Buffer((T.int64(4), T.int64(32), T.int64(16), 
T.int64(8)), "float32"), T_transpose: T.Buffer((T.int64(4), T.int64(16), 
T.int64(32), T.int64(16)), "float32")):
+            T.func_attr({"tir.noalias": T.bool(True)})
             # with T.block("root"):
             T_transpose_1 = T.alloc_buffer((T.int64(4), T.int64(32), 
T.int64(16), T.int64(8)))
             T_reshape = T.alloc_buffer((T.int64(128), T.int64(16), T.int64(8)))
@@ -2694,10 +2694,13 @@ def test_attention():
             T_reshape_2 = T.alloc_buffer((T.int64(4), T.int64(32), 
T.int64(16), T.int64(8)))
             T_add = T.alloc_buffer((T.int64(4), T.int64(32), T.int64(16), 
T.int64(8)))
             T_reshape_3 = T.alloc_buffer((T.int64(128), T.int64(16), 
T.int64(8)))
-            T_softmax_maxelem = T.alloc_buffer((T.int64(128), T.int64(16)))
-            T_softmax_exp = T.alloc_buffer((T.int64(128), T.int64(16), 
T.int64(8)))
-            T_softmax_expsum = T.alloc_buffer((T.int64(128), T.int64(16)))
-            T_softmax_norm = T.alloc_buffer((T.int64(128), T.int64(16), 
T.int64(8)))
+            trilu = T.alloc_buffer((T.int64(128), T.int64(16), T.int64(8)))
+            trilu_red = T.alloc_buffer((T.int64(128), T.int64(16), T.int64(1)))
+            T_subtract = T.alloc_buffer((T.int64(128), T.int64(16), 
T.int64(8)))
+            compute = T.alloc_buffer((T.int64(128), T.int64(16), T.int64(8)))
+            trilu_1 = T.alloc_buffer((T.int64(128), T.int64(16), T.int64(8)))
+            trilu_red_1 = T.alloc_buffer((T.int64(128), T.int64(16), 
T.int64(1)))
+            T_divide = T.alloc_buffer((T.int64(128), T.int64(16), T.int64(8)))
             T_transpose_3 = T.alloc_buffer((T.int64(4), T.int64(32), 
T.int64(8), T.int64(16)))
             T_reshape_4 = T.alloc_buffer((T.int64(128), T.int64(8), 
T.int64(16)))
             T_batch_matmul_NN = T.alloc_buffer((T.int64(128), T.int64(16), 
T.int64(16)))
@@ -2705,9 +2708,9 @@ def test_attention():
             for ax0, ax1, ax2, ax3 in T.grid(T.int64(4), T.int64(32), 
T.int64(16), T.int64(8)):
                 with T.block("T_transpose"):
                     v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, 
ax1, ax2, ax3])
-                    T.reads(rxplaceholder[v_ax0, v_ax2, v_ax1, v_ax3])
+                    T.reads(A[v_ax0, v_ax2, v_ax1, v_ax3])
                     T.writes(T_transpose_1[v_ax0, v_ax1, v_ax2, v_ax3])
-                    T_transpose_1[v_ax0, v_ax1, v_ax2, v_ax3] = 
rxplaceholder[v_ax0, v_ax2, v_ax1, v_ax3]
+                    T_transpose_1[v_ax0, v_ax1, v_ax2, v_ax3] = A[v_ax0, 
v_ax2, v_ax1, v_ax3]
             for ax0, ax1, ax2 in T.grid(T.int64(128), T.int64(16), T.int64(8)):
                 with T.block("T_reshape"):
                     v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2])
@@ -2717,9 +2720,9 @@ def test_attention():
             for ax0, ax1, ax2, ax3 in T.grid(T.int64(4), T.int64(32), 
T.int64(8), T.int64(8)):
                 with T.block("T_transpose_1"):
                     v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, 
ax1, ax2, ax3])
-                    T.reads(rxplaceholder_1[v_ax0, v_ax2, v_ax1, v_ax3])
+                    T.reads(B[v_ax0, v_ax2, v_ax1, v_ax3])
                     T.writes(T_transpose_2[v_ax0, v_ax1, v_ax2, v_ax3])
-                    T_transpose_2[v_ax0, v_ax1, v_ax2, v_ax3] = 
rxplaceholder_1[v_ax0, v_ax2, v_ax1, v_ax3]
+                    T_transpose_2[v_ax0, v_ax1, v_ax2, v_ax3] = B[v_ax0, 
v_ax2, v_ax1, v_ax3]
             for ax0, ax1, ax2 in T.grid(T.int64(128), T.int64(8), T.int64(8)):
                 with T.block("T_reshape_1"):
                     v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2])
@@ -2750,50 +2753,67 @@ def test_attention():
             for ax0, ax1, ax2, ax3 in T.grid(T.int64(4), T.int64(32), 
T.int64(16), T.int64(8)):
                 with T.block("T_add"):
                     v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, 
ax1, ax2, ax3])
-                    T.reads(T_reshape_2[v_ax0, v_ax1, v_ax2, v_ax3], 
rxplaceholder_3[v_ax0, v_ax1, v_ax2, v_ax3])
+                    T.reads(T_reshape_2[v_ax0, v_ax1, v_ax2, v_ax3], D[v_ax0, 
v_ax1, v_ax2, v_ax3])
                     T.writes(T_add[v_ax0, v_ax1, v_ax2, v_ax3])
-                    T_add[v_ax0, v_ax1, v_ax2, v_ax3] = T_reshape_2[v_ax0, 
v_ax1, v_ax2, v_ax3] + rxplaceholder_3[v_ax0, v_ax1, v_ax2, v_ax3]
+                    T_add[v_ax0, v_ax1, v_ax2, v_ax3] = T_reshape_2[v_ax0, 
v_ax1, v_ax2, v_ax3] + D[v_ax0, v_ax1, v_ax2, v_ax3]
             for ax0, ax1, ax2 in T.grid(T.int64(128), T.int64(16), T.int64(8)):
                 with T.block("T_reshape_3"):
                     v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2])
                     T.reads(T_add[((v_ax2 // T.int64(8) + v_ax1) // 
T.int64(16) + v_ax0) % T.int64(128) // T.int64(32), ((v_ax2 // T.int64(8) + 
v_ax1) // T.int64(16) + v_ax0) % T.int64(32), (v_ax2 // T.int64(8) + v_ax1) % 
T.int64(16), v_ax2 % T.int64(8)])
                     T.writes(T_reshape_3[v_ax0, v_ax1, v_ax2])
                     T_reshape_3[v_ax0, v_ax1, v_ax2] = T_add[((v_ax2 // 
T.int64(8) + v_ax1) // T.int64(16) + v_ax0) % T.int64(128) // T.int64(32), 
((v_ax2 // T.int64(8) + v_ax1) // T.int64(16) + v_ax0) % T.int64(32), (v_ax2 // 
T.int64(8) + v_ax1) % T.int64(16), v_ax2 % T.int64(8)]
-            for i0, i1, k in T.grid(T.int64(128), T.int64(16), T.int64(8)):
-                with T.block("T_softmax_maxelem"):
-                    v_i0, v_i1, v_k = T.axis.remap("SSR", [i0, i1, k])
-                    T.reads(T_reshape_3[v_i0, v_i1, v_k])
-                    T.writes(T_softmax_maxelem[v_i0, v_i1])
-                    with T.init():
-                        T_softmax_maxelem[v_i0, v_i1] = 
T.float32(-3.4028234663852886e+38)
-                    T_softmax_maxelem[v_i0, v_i1] = 
T.max(T_softmax_maxelem[v_i0, v_i1], T_reshape_3[v_i0, v_i1, v_k])
             for i0, i1, i2 in T.grid(T.int64(128), T.int64(16), T.int64(8)):
-                with T.block("T_softmax_exp"):
+                with T.block("trilu"):
                     v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2])
-                    T.reads(T_reshape_3[v_i0, v_i1, v_i2], 
T_softmax_maxelem[v_i0, v_i1])
-                    T.writes(T_softmax_exp[v_i0, v_i1, v_i2])
-                    T_softmax_exp[v_i0, v_i1, v_i2] = T.exp(T_reshape_3[v_i0, 
v_i1, v_i2] - T_softmax_maxelem[v_i0, v_i1])
-            for i0, i1, k in T.grid(T.int64(128), T.int64(16), T.int64(8)):
-                with T.block("T_softmax_expsum"):
-                    v_i0, v_i1, v_k = T.axis.remap("SSR", [i0, i1, k])
-                    T.reads(T_softmax_exp[v_i0, v_i1, v_k])
-                    T.writes(T_softmax_expsum[v_i0, v_i1])
+                    T.reads(T_reshape_3[v_i0, v_i1, v_i2])
+                    T.writes(trilu[v_i0, v_i1, v_i2])
+                    trilu[v_i0, v_i1, v_i2] = T.Select(v_i2 <= v_i1, 
T_reshape_3[v_i0, v_i1, v_i2], T.float32(0))
+            for ax0, ax1, ax2, k2 in T.grid(T.int64(128), T.int64(16), 
T.int64(1), T.int64(8)):
+                with T.block("trilu_red"):
+                    v_ax0, v_ax1, v_ax2, v_k2 = T.axis.remap("SSSR", [ax0, 
ax1, ax2, k2])
+                    T.reads(trilu[v_ax0, v_ax1, v_k2])
+                    T.writes(trilu_red[v_ax0, v_ax1, v_ax2])
                     with T.init():
-                        T_softmax_expsum[v_i0, v_i1] = T.float32(0)
-                    T_softmax_expsum[v_i0, v_i1] = T_softmax_expsum[v_i0, 
v_i1] + T_softmax_exp[v_i0, v_i1, v_k]
+                        trilu_red[v_ax0, v_ax1, v_ax2] = 
T.float32(-3.4028234663852886e+38)
+                    trilu_red[v_ax0, v_ax1, v_ax2] = T.max(trilu_red[v_ax0, 
v_ax1, v_ax2], trilu[v_ax0, v_ax1, v_k2])
+            for ax0, ax1, ax2 in T.grid(T.int64(128), T.int64(16), T.int64(8)):
+                with T.block("T_subtract"):
+                    v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2])
+                    T.reads(trilu[v_ax0, v_ax1, v_ax2], trilu_red[v_ax0, 
v_ax1, T.int64(0)])
+                    T.writes(T_subtract[v_ax0, v_ax1, v_ax2])
+                    T_subtract[v_ax0, v_ax1, v_ax2] = trilu[v_ax0, v_ax1, 
v_ax2] - trilu_red[v_ax0, v_ax1, T.int64(0)]
             for i0, i1, i2 in T.grid(T.int64(128), T.int64(16), T.int64(8)):
-                with T.block("T_softmax_norm"):
+                with T.block("compute"):
                     v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2])
-                    T.reads(T_softmax_exp[v_i0, v_i1, v_i2], 
T_softmax_expsum[v_i0, v_i1])
-                    T.writes(T_softmax_norm[v_i0, v_i1, v_i2])
-                    T.block_attr({"axis": 2})
-                    T_softmax_norm[v_i0, v_i1, v_i2] = T_softmax_exp[v_i0, 
v_i1, v_i2] / T_softmax_expsum[v_i0, v_i1]
+                    T.reads(T_subtract[v_i0, v_i1, v_i2])
+                    T.writes(compute[v_i0, v_i1, v_i2])
+                    compute[v_i0, v_i1, v_i2] = T.exp(T_subtract[v_i0, v_i1, 
v_i2])
+            for i0, i1, i2 in T.grid(T.int64(128), T.int64(16), T.int64(8)):
+                with T.block("trilu_1"):
+                    v_i0, v_i1, v_i2 = T.axis.remap("SSS", [i0, i1, i2])
+                    T.reads(compute[v_i0, v_i1, v_i2])
+                    T.writes(trilu_1[v_i0, v_i1, v_i2])
+                    trilu_1[v_i0, v_i1, v_i2] = T.Select(v_i2 <= v_i1, 
compute[v_i0, v_i1, v_i2], T.float32(0))
+            for ax0, ax1, ax2, k2 in T.grid(T.int64(128), T.int64(16), 
T.int64(1), T.int64(8)):
+                with T.block("trilu_red_1"):
+                    v_ax0, v_ax1, v_ax2, v_k2 = T.axis.remap("SSSR", [ax0, 
ax1, ax2, k2])
+                    T.reads(trilu_1[v_ax0, v_ax1, v_k2])
+                    T.writes(trilu_red_1[v_ax0, v_ax1, v_ax2])
+                    with T.init():
+                        trilu_red_1[v_ax0, v_ax1, v_ax2] = T.float32(0)
+                    trilu_red_1[v_ax0, v_ax1, v_ax2] = trilu_red_1[v_ax0, 
v_ax1, v_ax2] + trilu_1[v_ax0, v_ax1, v_k2]
+            for ax0, ax1, ax2 in T.grid(T.int64(128), T.int64(16), T.int64(8)):
+                with T.block("T_divide"):
+                    v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2])
+                    T.reads(trilu_1[v_ax0, v_ax1, v_ax2], trilu_red_1[v_ax0, 
v_ax1, T.int64(0)])
+                    T.writes(T_divide[v_ax0, v_ax1, v_ax2])
+                    T_divide[v_ax0, v_ax1, v_ax2] = trilu_1[v_ax0, v_ax1, 
v_ax2] / trilu_red_1[v_ax0, v_ax1, T.int64(0)]
             for ax0, ax1, ax2, ax3 in T.grid(T.int64(4), T.int64(32), 
T.int64(8), T.int64(16)):
                 with T.block("T_transpose_2"):
                     v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, 
ax1, ax2, ax3])
-                    T.reads(rxplaceholder_2[v_ax0, v_ax2, v_ax1, v_ax3])
+                    T.reads(C[v_ax0, v_ax2, v_ax1, v_ax3])
                     T.writes(T_transpose_3[v_ax0, v_ax1, v_ax2, v_ax3])
-                    T_transpose_3[v_ax0, v_ax1, v_ax2, v_ax3] = 
rxplaceholder_2[v_ax0, v_ax2, v_ax1, v_ax3]
+                    T_transpose_3[v_ax0, v_ax1, v_ax2, v_ax3] = C[v_ax0, 
v_ax2, v_ax1, v_ax3]
             for ax0, ax1, ax2 in T.grid(T.int64(128), T.int64(8), T.int64(16)):
                 with T.block("T_reshape_4"):
                     v_ax0, v_ax1, v_ax2 = T.axis.remap("SSS", [ax0, ax1, ax2])
@@ -2803,12 +2823,12 @@ def test_attention():
             for b, i, j, k in T.grid(T.int64(128), T.int64(16), T.int64(16), 
T.int64(8)):
                 with T.block("T_batch_matmul_NN"):
                     v_b, v_i, v_j, v_k = T.axis.remap("SSSR", [b, i, j, k])
-                    T.reads(T_softmax_norm[v_b, v_i, v_k], T_reshape_4[v_b, 
v_k, v_j])
+                    T.reads(T_divide[v_b, v_i, v_k], T_reshape_4[v_b, v_k, 
v_j])
                     T.writes(T_batch_matmul_NN[v_b, v_i, v_j])
                     T.block_attr({"layout_free_placeholders": [T_reshape_4]})
                     with T.init():
                         T_batch_matmul_NN[v_b, v_i, v_j] = T.float32(0)
-                    T_batch_matmul_NN[v_b, v_i, v_j] = T_batch_matmul_NN[v_b, 
v_i, v_j] + T_softmax_norm[v_b, v_i, v_k] * T_reshape_4[v_b, v_k, v_j]
+                    T_batch_matmul_NN[v_b, v_i, v_j] = T_batch_matmul_NN[v_b, 
v_i, v_j] + T_divide[v_b, v_i, v_k] * T_reshape_4[v_b, v_k, v_j]
             for ax0, ax1, ax2, ax3 in T.grid(T.int64(4), T.int64(32), 
T.int64(16), T.int64(16)):
                 with T.block("T_reshape_5"):
                     v_ax0, v_ax1, v_ax2, v_ax3 = T.axis.remap("SSSS", [ax0, 
ax1, ax2, ax3])
@@ -2824,7 +2844,8 @@ def test_attention():
 
         @R.function
         def main(q: R.Tensor((4, 16, 32, 8), dtype="float32"), k: R.Tensor((4, 
8, 32, 8), dtype="float32"), v: R.Tensor((4, 8, 32, 16), dtype="float32"), 
bias: R.Tensor((4, 32, 16, 8), dtype="float32")) -> R.Tensor((4, 16, 32, 16), 
dtype="float32"):
-            gv = R.call_tir(Expected.attention_bias, (q, k, v, bias), 
out_sinfo=R.Tensor((4, 16, 32, 16), dtype="float32"))
+            cls = Expected
+            gv = R.call_tir(cls.attention_bias, (q, k, v, bias), 
out_sinfo=R.Tensor((4, 16, 32, 16), dtype="float32"))
             return gv
 
     # fmt: on

Reply via email to