已合并
test: 补充ascir目录UT并清理无调用reg_func文件(#210) #2001
test: 补充ascir目录UT并清理无调用reg_func文件(#210) #2001
已合并
Leechi666创建于 17 天前
共 9 个文件变更+1579-89
@@ -1,18 +0,0 @@
1-/**
2- * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3- * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4- * CANN Open Software License Agreement Version 2.0 (the "License").
5- * Please refer to the License for details. You may not use this file except in compliance with the License.
6- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7- * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8- * See LICENSE in the root of the software repository for the full text of the License.
9- */
10-#include "defalut_reg_func.h"
11- 
12-namespace af {
13-namespace ascir {
14-std::vector<std::unique_ptr<TmpBufDesc>> CalcArgmaxTmpSize(const AscNode &node) {
15- return CalcDefaultTmpSize(node);
16-}
17-} // namespace ascir
18-} // namespace af
@@ -1,22 +0,0 @@
1-/**
2- * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3- * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4- * CANN Open Software License Agreement Version 2.0 (the "License").
5- * Please refer to the License for details. You may not use this file except in compliance with the License.
6- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7- * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8- * See LICENSE in the root of the software repository for the full text of the License.
9- */
10-#include "defalut_reg_func.h"
11- 
12-namespace af {
13-namespace ascir {
14-std::vector<std::unique_ptr<TmpBufDesc>> CalcArgmaxWithValueTmpSize(const AscNode &node) {
15- // 固定分配 2048 字节的临时buffer
16- // 内存布局:8个临时tensor,每个256B,总计2048B
17- (void)node;
18- const Expression exp = Symbol(2048);
19- return GetTmpBuffer(exp);
20-}
21-} // namespace ascir
22-} // namespace af
@@ -25,8 +25,6 @@ bool IsAllScalarOrUbScalar(AscNodeInputs &node_inputs);
25bool HasScalarOrUbScalar(AscNodeInputs &node_inputs);25bool HasScalarOrUbScalar(AscNodeInputs &node_inputs);
26 26 
27std::vector<std::unique_ptr<TmpBufDesc>> CalcBroadCastTmpSize(const AscNode &node);27std::vector<std::unique_ptr<TmpBufDesc>> CalcBroadCastTmpSize(const AscNode &node);
28-std::vector<std::unique_ptr<TmpBufDesc>> CalcArgmaxTmpSize(const AscNode &node);
29-std::vector<std::unique_ptr<TmpBufDesc>> CalcArgmaxWithValueTmpSize(const AscNode &node);
30std::vector<std::unique_ptr<TmpBufDesc>> CalcConcatTmpSize(const AscNode &node);28std::vector<std::unique_ptr<TmpBufDesc>> CalcConcatTmpSize(const AscNode &node);
31std::vector<std::unique_ptr<TmpBufDesc>> CalcConcatTmpSizeV2(const AscNode &node);29std::vector<std::unique_ptr<TmpBufDesc>> CalcConcatTmpSizeV2(const AscNode &node);
32std::vector<std::unique_ptr<TmpBufDesc>> CalcPadTmpSize(const AscNode &node);30std::vector<std::unique_ptr<TmpBufDesc>> CalcPadTmpSize(const AscNode &node);
@@ -1,33 +0,0 @@
1-/**
2- * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3- * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4- * CANN Open Software License Agreement Version 2.0 (the "License").
5- * Please refer to the License for details. You may not use this file except in compliance with the License.
6- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7- * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8- * See LICENSE in the root of the software repository for the full text of the License.
9- */
10-#include "ascendc_ir.h"
11-#include "defalut_reg_func.h"
12-#include "common/checker.h"
13- 
14-namespace af {
15-namespace ascir {
16-constexpr int32_t BASIC_TMP_SIZE = 8192;
17- 
18-// ZerosLike impl by duplicate api, just duplivate zero to output
19-std::vector<std::unique_ptr<TmpBufDesc>> CalcZerosLikeTmpSize(const AscNode &node) {
20- AscNodeInputs node_inputs = node.inputs;
21- auto type_size = Expression(Symbol(GetSizeByDataType(node_inputs[0].attr.dtype)));
22- 
23- Expression input_size = GetInputSize(node_inputs);
24- 
25- Expression min_temp_size = sym::Max(type_size * input_size, Symbol(BASIC_TMP_SIZE));
26- 
27- TmpBufDesc desc = {min_temp_size, -1};
28- std::vector<std::unique_ptr<TmpBufDesc>> tmp_buf_descs;
29- tmp_buf_descs.emplace_back(std::make_unique<TmpBufDesc>(desc));
30- return tmp_buf_descs;
31-}
32-} // namespace ascir
33-} // namespace af
@@ -1,3 +1,11 @@
1+# Copyright (c) 2025 Huawei Technologies Co., Ltd.
2+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
3+# CANN Open Software License Agreement Version 2.0 (the "License").
4+# Please refer to the License for details. You may not use this file except in compliance with the License.
5+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
6+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
7+# See LICENSE in the root of the software repository for the full text of the License.
8+ 
1add_library(test_ascir_ut OBJECT9add_library(test_ascir_ut OBJECT
2 #test_ascir.cpp10 #test_ascir.cpp
3 #test_ascir_ops.cpp11 #test_ascir_ops.cpp
@@ -33,14 +41,15 @@ add_library(test_ascir_ut OBJECT
33 reg_func/test_reg_func_axpy.cpp41 reg_func/test_reg_func_axpy.cpp
34 reg_func/test_reg_func_welford.cpp42 reg_func/test_reg_func_welford.cpp
35 reg_func/test_reg_func_abs.cpp43 reg_func/test_reg_func_abs.cpp
36- test_ascir_codegen_impl.cpp
37- test_ascir_meta.cpp
38 reg_func/test_reg_func_reduce_max.cpp44 reg_func/test_reg_func_reduce_max.cpp
45+ test_ascir_codegen_impl.cpp
46+ test_ascir_att_impl.cpp
47+ test_ascir_common.cpp
48+ test_ascir_codegen_calc.cpp
49+ test_ascir_meta.cpp
39 code_dumper_unittest.cc50 code_dumper_unittest.cc
40 ascir_utils_unittest.cc51 ascir_utils_unittest.cc
41 test_asc_graph_utils.cpp52 test_asc_graph_utils.cpp
42- test_ascir_att_impl.cpp
43- test_ascir_common.cpp
44)53)
45target_include_directories(test_ascir_ut PRIVATE54target_include_directories(test_ascir_ut PRIVATE
46 ${CODE_ROOT_DIR}/ascir/generator55 ${CODE_ROOT_DIR}/ascir/generator
@@ -29,7 +29,7 @@ class AscIrAttImplTest : public ::testing::Test {
29 EXPECT_STREQ(static_cast<const char *>(impl.GetAscendCApiPerfTable()), #ir_name); \29 EXPECT_STREQ(static_cast<const char *>(impl.GetAscendCApiPerfTable()), #ir_name); \
30 }30 }
31 31 
32-TEST_F(AscIrAttImplTest, GetApiPerfReturnsIrName_ElementWise) {32+TEST_F(AscIrAttImplTest, GetApiPerfReturnsIrName_AllClasses) {
33 EXPECT_ATT_IMPL_NAMED(Add);33 EXPECT_ATT_IMPL_NAMED(Add);
34 EXPECT_ATT_IMPL_NAMED(Gather);34 EXPECT_ATT_IMPL_NAMED(Gather);
35 EXPECT_ATT_IMPL_NAMED(Abs);35 EXPECT_ATT_IMPL_NAMED(Abs);
@@ -41,15 +41,41 @@ TEST_F(AscIrAttImplTest, GetApiPerfReturnsIrName_ElementWise) {
41 EXPECT_ATT_IMPL_NAMED(LogicalAnd);41 EXPECT_ATT_IMPL_NAMED(LogicalAnd);
42 EXPECT_ATT_IMPL_NAMED(LogicalOr);42 EXPECT_ATT_IMPL_NAMED(LogicalOr);
43 EXPECT_ATT_IMPL_NAMED(LogicalNot);43 EXPECT_ATT_IMPL_NAMED(LogicalNot);
44-}44+ EXPECT_ATT_IMPL_NAMED(Maximum);
45- 45+ EXPECT_ATT_IMPL_NAMED(Minimum);
46-TEST_F(AscIrAttImplTest, GetApiPerfReturnsIrName_ReduceArgMax) {46+ EXPECT_ATT_IMPL_NAMED(Min);
47+ EXPECT_ATT_IMPL_NAMED(Mul);
48+ EXPECT_ATT_IMPL_NAMED(Neg);
49+ EXPECT_ATT_IMPL_NAMED(Reciprocal);
50+ EXPECT_ATT_IMPL_NAMED(Relu);
51+ EXPECT_ATT_IMPL_NAMED(ReduceAll);
52+ EXPECT_ATT_IMPL_NAMED(ReduceAny);
53+ EXPECT_ATT_IMPL_NAMED(ReduceMax);
47 EXPECT_ATT_IMPL_NAMED(ReduceArgMax);54 EXPECT_ATT_IMPL_NAMED(ReduceArgMax);
48 EXPECT_ATT_IMPL_NAMED(ReduceArgMaxMultiRPhase1);55 EXPECT_ATT_IMPL_NAMED(ReduceArgMaxMultiRPhase1);
49 EXPECT_ATT_IMPL_NAMED(ReduceArgMaxMultiRPhase2);56 EXPECT_ATT_IMPL_NAMED(ReduceArgMaxMultiRPhase2);
50-}57+ EXPECT_ATT_IMPL_NAMED(ReduceMean);
51- 58+ EXPECT_ATT_IMPL_NAMED(ReduceMin);
52-TEST_F(AscIrAttImplTest, GetApiPerfReturnsIrName_NoModeling) {59+ EXPECT_ATT_IMPL_NAMED(ReduceSum);
60+ EXPECT_ATT_IMPL_NAMED(ReduceProd);
61+ EXPECT_ATT_IMPL_NAMED(RemovePad);
62+ EXPECT_ATT_IMPL_NAMED(Rsqrt);
63+ EXPECT_ATT_IMPL_NAMED(Select);
64+ EXPECT_ATT_IMPL_NAMED(Sign);
65+ EXPECT_ATT_IMPL_NAMED(Sqrt);
66+ EXPECT_ATT_IMPL_NAMED(Sub);
67+ EXPECT_ATT_IMPL_NAMED(Sum);
68+ EXPECT_ATT_IMPL_NAMED(Tanh);
69+ EXPECT_ATT_IMPL_NAMED(Where);
70+ EXPECT_ATT_IMPL_NAMED(Ge);
71+ EXPECT_ATT_IMPL_NAMED(Eq);
72+ EXPECT_ATT_IMPL_NAMED(Ne);
73+ EXPECT_ATT_IMPL_NAMED(Gt);
74+ EXPECT_ATT_IMPL_NAMED(Le);
75+ EXPECT_ATT_IMPL_NAMED(Lt);
76+ EXPECT_ATT_IMPL_NAMED(Ub2ub);
77+ EXPECT_ATT_IMPL_NAMED(Load);
78+ EXPECT_ATT_IMPL_NAMED(Store);
53 EXPECT_ATT_IMPL_NAMED(Data);79 EXPECT_ATT_IMPL_NAMED(Data);
54 EXPECT_ATT_IMPL_NAMED(Scalar);80 EXPECT_ATT_IMPL_NAMED(Scalar);
55 EXPECT_ATT_IMPL_NAMED(IndexExpr);81 EXPECT_ATT_IMPL_NAMED(IndexExpr);
@@ -61,6 +87,26 @@ TEST_F(AscIrAttImplTest, GetApiPerfReturnsIrName_NoModeling) {
61 EXPECT_ATT_IMPL_NAMED(Nop);87 EXPECT_ATT_IMPL_NAMED(Nop);
62 EXPECT_ATT_IMPL_NAMED(Ln);88 EXPECT_ATT_IMPL_NAMED(Ln);
63 EXPECT_ATT_IMPL_NAMED(Isnan);89 EXPECT_ATT_IMPL_NAMED(Isnan);
90+ EXPECT_ATT_IMPL_NAMED(IsFinite);
91+ EXPECT_ATT_IMPL_NAMED(IsInf);
92+ EXPECT_ATT_IMPL_NAMED(MaskedFill);
93+ EXPECT_ATT_IMPL_NAMED(Max);
94+ EXPECT_ATT_IMPL_NAMED(Mean);
95+ EXPECT_ATT_IMPL_NAMED(Prod);
96+ EXPECT_ATT_IMPL_NAMED(Any);
97+ EXPECT_ATT_IMPL_NAMED(All);
98+ EXPECT_ATT_IMPL_NAMED(Sigmoid);
99+ EXPECT_ATT_IMPL_NAMED(TrueDiv);
100+ EXPECT_ATT_IMPL_NAMED(Remainder);
101+ EXPECT_ATT_IMPL_NAMED(Pow);
102+ EXPECT_ATT_IMPL_NAMED(ClipByValue);
103+ EXPECT_ATT_IMPL_NAMED(Concat);
104+ EXPECT_ATT_IMPL_NAMED(LeakyRelu);
105+ EXPECT_ATT_IMPL_NAMED(BitwiseAnd);
106+ EXPECT_ATT_IMPL_NAMED(Transpose);
107+ EXPECT_ATT_IMPL_NAMED(FloorDiv);
108+ EXPECT_ATT_IMPL_NAMED(Gelu);
109+ EXPECT_ATT_IMPL_NAMED(Axpy);
64}110}
65 111 
66} // namespace ascir112} // namespace ascir
@@ -0,0 +1,563 @@
1+/**
2+ * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+#include "gtest/gtest.h"
11+ 
12+#include "ascir_register.h"
13+#include "ascir_ops.h"
14+#include "v1_ascir_codegen_impl.h"
15+ 
16+#ifndef CODEGEN_CALC_JOIN
17+#define CODEGEN_CALC_JOIN(a, b) a##b
18+#define JOIN(a, b) CODEGEN_CALC_JOIN(a, b)
19+#endif
20+ 
21+namespace af {
22+namespace ascir {
23+ 
24+class AscIrCodegenCalcTest : public ::testing::Test {
25+ protected:
26+ void SetUp() override {}
27+ void TearDown() override {}
28+};
29+ 
30+// Unary elementwise graph: Data -> Load -> Op -> Store -> Output
31+#define EXPECT_UNARY_CALC(ir_name) \
32+ { \
33+ af::AscGraph graph("t_" #ir_name); \
34+ auto s0 = graph.CreateSizeVar("s0"); \
35+ auto s1 = graph.CreateSizeVar("s1"); \
36+ auto z0 = graph.CreateAxis("z0", s0); \
37+ auto z1 = graph.CreateAxis("z1", s1); \
38+ af::ascir_op::Data x1("x1", graph); \
39+ af::ascir_op::Load load1("load1"); \
40+ af::ascir_op::ir_name op0("op0"); \
41+ af::ascir_op::Store store("store"); \
42+ af::ascir_op::Output y("y"); \
43+ x1.attr.sched.axis = {z0.id, z1.id}; \
44+ x1.y.dtype = af::DT_FLOAT; \
45+ *x1.y.axis = {z0.id, z1.id}; \
46+ *x1.y.repeats = {s0, s1}; \
47+ *x1.y.strides = {s1, af::Symbol(1)}; \
48+ load1.x = x1.y; \
49+ load1.attr.sched.axis = {z0.id, z1.id}; \
50+ load1.y.dtype = af::DT_FLOAT; \
51+ *load1.y.axis = {z0.id, z1.id}; \
52+ *load1.y.repeats = {s0, s1}; \
53+ *load1.y.strides = {s1, af::Symbol(1)}; \
54+ *load1.y.vectorized_axis = {z0.id, z1.id}; \
55+ op0.x = load1.y; \
56+ op0.attr.sched.axis = {z0.id, z1.id}; \
57+ op0.y.dtype = af::DT_FLOAT; \
58+ *op0.y.axis = {z0.id, z1.id}; \
59+ *op0.y.repeats = {s0, s1}; \
60+ *op0.y.strides = {s1, af::Symbol(1)}; \
61+ *op0.y.vectorized_axis = {z0.id, z1.id}; \
62+ store.x = op0.y; \
63+ store.attr.sched.axis = {z0.id, z1.id}; \
64+ store.y.dtype = af::DT_FLOAT; \
65+ *store.y.axis = {z0.id, z1.id}; \
66+ *store.y.repeats = {s0, s1}; \
67+ *store.y.strides = {s1, af::Symbol(1)}; \
68+ y.x = store.y; \
69+ y.attr.sched.axis = {z0.id, z1.id}; \
70+ y.y.dtype = af::DT_FLOAT; \
71+ *y.y.axis = {z0.id, z1.id}; \
72+ *y.y.repeats = {s0, s1}; \
73+ *y.y.strides = {s1, af::Symbol(1)}; \
74+ auto node = graph.FindNode("op0"); \
75+ ASSERT_NE(node, nullptr); \
76+ node->inputs[0].attr.vectorized_strides = {s1, af::Symbol(1)}; \
77+ JOIN(ir_name, AscIrCodegenImpl) impl; \
78+ const auto bufs = impl.CalcTmpBufSize(*node); \
79+ EXPECT_FALSE(bufs.empty()); \
80+ EXPECT_TRUE(impl.IsNodeValid(*node)); \
81+ (void)impl.IsInplaceSupported(*node); \
82+ (void)impl.IsBrcInlineSupported(*node); \
83+ }
84+ 
85+TEST_F(AscIrCodegenCalcTest, CalcTmpBufSize_UnaryOps) {
86+ EXPECT_UNARY_CALC(Erf);
87+ EXPECT_UNARY_CALC(Tanh);
88+ EXPECT_UNARY_CALC(Rsqrt);
89+ EXPECT_UNARY_CALC(Reciprocal);
90+ EXPECT_UNARY_CALC(Isnan);
91+ EXPECT_UNARY_CALC(IsInf);
92+ EXPECT_UNARY_CALC(IsFinite);
93+ EXPECT_UNARY_CALC(LogicalNot);
94+}
95+ 
96+// Binary elementwise graph: both inputs are tensors
97+#define EXPECT_BINARY_CALC(ir_name) \
98+ { \
99+ af::AscGraph graph("t_" #ir_name); \
100+ auto s0 = graph.CreateSizeVar("s0"); \
101+ auto s1 = graph.CreateSizeVar("s1"); \
102+ auto z0 = graph.CreateAxis("z0", s0); \
103+ auto z1 = graph.CreateAxis("z1", s1); \
104+ af::ascir_op::Data x1("x1", graph); \
105+ af::ascir_op::Data x2("x2", graph); \
106+ af::ascir_op::Load load1("load1"); \
107+ af::ascir_op::Load load2("load2"); \
108+ af::ascir_op::ir_name op0("op0"); \
109+ af::ascir_op::Store store("store"); \
110+ af::ascir_op::Output y("y"); \
111+ x1.attr.sched.axis = {z0.id, z1.id}; \
112+ x1.y.dtype = af::DT_FLOAT; \
113+ *x1.y.axis = {z0.id, z1.id}; \
114+ *x1.y.repeats = {s0, s1}; \
115+ *x1.y.strides = {s1, af::Symbol(1)}; \
116+ x2.attr.sched.axis = {z0.id, z1.id}; \
117+ x2.y.dtype = af::DT_FLOAT; \
118+ *x2.y.axis = {z0.id, z1.id}; \
119+ *x2.y.repeats = {s0, s1}; \
120+ *x2.y.strides = {s1, af::Symbol(1)}; \
121+ load1.x = x1.y; \
122+ load1.attr.sched.axis = {z0.id, z1.id}; \
123+ load1.y.dtype = af::DT_FLOAT; \
124+ *load1.y.axis = {z0.id, z1.id}; \
125+ *load1.y.repeats = {s0, s1}; \
126+ *load1.y.strides = {s1, af::Symbol(1)}; \
127+ *load1.y.vectorized_axis = {z0.id, z1.id}; \
128+ load2.x = x2.y; \
129+ load2.attr.sched.axis = {z0.id, z1.id}; \
130+ load2.y.dtype = af::DT_FLOAT; \
131+ *load2.y.axis = {z0.id, z1.id}; \
132+ *load2.y.repeats = {s0, s1}; \
133+ *load2.y.strides = {s1, af::Symbol(1)}; \
134+ *load2.y.vectorized_axis = {z0.id, z1.id}; \
135+ op0.x1 = load1.y; \
136+ op0.x2 = load2.y; \
137+ op0.attr.sched.axis = {z0.id, z1.id}; \
138+ op0.y.dtype = af::DT_FLOAT; \
139+ *op0.y.axis = {z0.id, z1.id}; \
140+ *op0.y.repeats = {s0, s1}; \
141+ *op0.y.strides = {s1, af::Symbol(1)}; \
142+ *op0.y.vectorized_axis = {z0.id, z1.id}; \
143+ store.x = op0.y; \
144+ store.attr.sched.axis = {z0.id, z1.id}; \
145+ store.y.dtype = af::DT_FLOAT; \
146+ *store.y.axis = {z0.id, z1.id}; \
147+ *store.y.repeats = {s0, s1}; \
148+ *store.y.strides = {s1, af::Symbol(1)}; \
149+ y.x = store.y; \
150+ y.attr.sched.axis = {z0.id, z1.id}; \
151+ y.y.dtype = af::DT_FLOAT; \
152+ *y.y.axis = {z0.id, z1.id}; \
153+ *y.y.repeats = {s0, s1}; \
154+ *y.y.strides = {s1, af::Symbol(1)}; \
155+ auto node = graph.FindNode("op0"); \
156+ ASSERT_NE(node, nullptr); \
157+ node->inputs[0].attr.vectorized_strides = {s1, af::Symbol(1)}; \
158+ JOIN(ir_name, AscIrCodegenImpl) impl; \
159+ const auto bufs = impl.CalcTmpBufSize(*node); \
160+ EXPECT_FALSE(bufs.empty()); \
161+ EXPECT_TRUE(impl.IsNodeValid(*node)); \
162+ (void)impl.IsInplaceSupported(*node); \
163+ (void)impl.IsBrcInlineSupported(*node); \
164+ }
165+ 
166+TEST_F(AscIrCodegenCalcTest, CalcTmpBufSize_BinaryOps) {
167+ EXPECT_BINARY_CALC(Eq);
168+ EXPECT_BINARY_CALC(Ge);
169+ EXPECT_BINARY_CALC(Gt);
170+ EXPECT_BINARY_CALC(Le);
171+ EXPECT_BINARY_CALC(Lt);
172+ EXPECT_BINARY_CALC(Ne);
173+ EXPECT_BINARY_CALC(TrueDiv);
174+ EXPECT_BINARY_CALC(Remainder);
175+ EXPECT_BINARY_CALC(FloorDiv);
176+ EXPECT_BINARY_CALC(LogicalAnd);
177+ EXPECT_BINARY_CALC(LogicalOr);
178+ EXPECT_BINARY_CALC(BitwiseAnd);
179+}
180+ 
181+// Extended coverage: node validity checks without the tmp-buffer assertion
182+// (some impls do not override CalcTmpBufSize and legitimately return an empty vector)
183+#define EXPECT_UNARY_VALID(ir_name) \
184+ { \
185+ af::AscGraph graph("t_" #ir_name); \
186+ auto s0 = graph.CreateSizeVar("s0"); \
187+ auto s1 = graph.CreateSizeVar("s1"); \
188+ auto z0 = graph.CreateAxis("z0", s0); \
189+ auto z1 = graph.CreateAxis("z1", s1); \
190+ af::ascir_op::Data x1("x1", graph); \
191+ af::ascir_op::Load load1("load1"); \
192+ af::ascir_op::ir_name op0("op0"); \
193+ af::ascir_op::Store store("store"); \
194+ af::ascir_op::Output y("y"); \
195+ x1.attr.sched.axis = {z0.id, z1.id}; \
196+ x1.y.dtype = af::DT_FLOAT; \
197+ *x1.y.axis = {z0.id, z1.id}; \
198+ *x1.y.repeats = {s0, s1}; \
199+ *x1.y.strides = {s1, af::Symbol(1)}; \
200+ load1.x = x1.y; \
201+ load1.attr.sched.axis = {z0.id, z1.id}; \
202+ load1.y.dtype = af::DT_FLOAT; \
203+ *load1.y.axis = {z0.id, z1.id}; \
204+ *load1.y.repeats = {s0, s1}; \
205+ *load1.y.strides = {s1, af::Symbol(1)}; \
206+ *load1.y.vectorized_axis = {z0.id, z1.id}; \
207+ op0.x = load1.y; \
208+ op0.attr.sched.axis = {z0.id, z1.id}; \
209+ op0.y.dtype = af::DT_FLOAT; \
210+ *op0.y.axis = {z0.id, z1.id}; \
211+ *op0.y.repeats = {s0, s1}; \
212+ *op0.y.strides = {s1, af::Symbol(1)}; \
213+ *op0.y.vectorized_axis = {z0.id, z1.id}; \
214+ store.x = op0.y; \
215+ store.attr.sched.axis = {z0.id, z1.id}; \
216+ store.y.dtype = af::DT_FLOAT; \
217+ *store.y.axis = {z0.id, z1.id}; \
218+ *store.y.repeats = {s0, s1}; \
219+ *store.y.strides = {s1, af::Symbol(1)}; \
220+ y.x = store.y; \
221+ y.attr.sched.axis = {z0.id, z1.id}; \
222+ y.y.dtype = af::DT_FLOAT; \
223+ *y.y.axis = {z0.id, z1.id}; \
224+ *y.y.repeats = {s0, s1}; \
225+ *y.y.strides = {s1, af::Symbol(1)}; \
226+ auto node = graph.FindNode("op0"); \
227+ ASSERT_NE(node, nullptr); \
228+ node->inputs[0].attr.vectorized_strides = {s1, af::Symbol(1)}; \
229+ JOIN(ir_name, AscIrCodegenImpl) impl; \
230+ EXPECT_TRUE(impl.IsNodeValid(*node)); \
231+ (void)impl.IsInplaceSupported(*node); \
232+ (void)impl.IsBrcInlineSupported(*node); \
233+ (void)impl.CalcTmpBufSize(*node); \
234+ }
235+ 
236+TEST_F(AscIrCodegenCalcTest, IsNodeValid_UnaryExtended) {
237+ EXPECT_UNARY_VALID(Exp);
238+ EXPECT_UNARY_VALID(Sigmoid);
239+ EXPECT_UNARY_VALID(Sign);
240+ EXPECT_UNARY_VALID(Sqrt);
241+ EXPECT_UNARY_VALID(Neg);
242+ EXPECT_UNARY_VALID(Ln);
243+ EXPECT_UNARY_VALID(LeakyRelu);
244+ EXPECT_UNARY_VALID(RemovePad);
245+ EXPECT_UNARY_VALID(Cast);
246+ EXPECT_UNARY_VALID(Gelu);
247+ EXPECT_UNARY_VALID(Relu);
248+ EXPECT_UNARY_VALID(Transpose);
249+ EXPECT_UNARY_VALID(Ub2ub);
250+ EXPECT_UNARY_VALID(Pad);
251+}
252+ 
253+// Reduce-family graph: 3-dim input with loop_axis on dim0 (Tier-1 proven layout)
254+#define EXPECT_REDUCE_VALID(ir_name) \
255+ { \
256+ af::AscGraph graph("t_" #ir_name); \
257+ af::Expression One = af::Symbol(1); \
258+ af::Expression Zero = af::Symbol(0); \
259+ auto s0 = graph.CreateSizeVar("s0"); \
260+ auto s1 = graph.CreateSizeVar("s1"); \
261+ auto s2 = graph.CreateSizeVar("s2"); \
262+ auto z0 = graph.CreateAxis("z0", s0); \
263+ auto z1 = graph.CreateAxis("z1", s1); \
264+ auto z2 = graph.CreateAxis("z2", s2); \
265+ af::ascir_op::Data x1("x1", graph); \
266+ af::ascir_op::Load load1("load1"); \
267+ af::ascir_op::ir_name op0("op0"); \
268+ af::ascir_op::Store store("store"); \
269+ af::ascir_op::Output y("y"); \
270+ x1.attr.sched.axis = {z0.id, z1.id, z2.id}; \
271+ x1.y.dtype = af::DT_FLOAT; \
272+ *x1.y.axis = {z0.id, z1.id, z2.id}; \
273+ *x1.y.repeats = {s0, s1, s2}; \
274+ *x1.y.strides = {s1 * s2, s2, One}; \
275+ load1.x = x1.y; \
276+ load1.attr.sched.axis = {z0.id, z1.id, z2.id}; \
277+ load1.y.dtype = af::DT_FLOAT; \
278+ *load1.y.axis = {z0.id, z1.id, z2.id}; \
279+ *load1.y.repeats = {s0, s1, s2}; \
280+ *load1.y.strides = {s1 * s2, s2, One}; \
281+ *load1.y.vectorized_axis = {z1.id, z2.id}; \
282+ op0.x = load1.y; \
283+ op0.attr.sched.axis = {z0.id, z1.id, z2.id}; \
284+ op0.attr.sched.loop_axis = {z0.id}; \
285+ op0.y.dtype = af::DT_FLOAT; \
286+ *op0.y.axis = {z0.id, z1.id, z2.id}; \
287+ *op0.y.repeats = {s0, s1, One}; \
288+ *op0.y.strides = {s2, One, Zero}; \
289+ *op0.y.vectorized_axis = {z1.id, z2.id}; \
290+ store.x = op0.y; \
291+ store.attr.sched.axis = {z0.id, z1.id, z2.id}; \
292+ store.y.dtype = af::DT_FLOAT; \
293+ *store.y.axis = {z0.id, z1.id, z2.id}; \
294+ *store.y.repeats = {s0, s1, s2}; \
295+ *store.y.strides = {s1 * s2, s2, One}; \
296+ y.x = store.y; \
297+ y.attr.sched.axis = {z0.id, z1.id, z2.id}; \
298+ y.y.dtype = af::DT_FLOAT; \
299+ *y.y.axis = {z0.id, z1.id, z2.id}; \
300+ *y.y.repeats = {s0, s1, s2}; \
301+ *y.y.strides = {s1 * s2, s2, One}; \
302+ auto node = graph.FindNode("op0"); \
303+ ASSERT_NE(node, nullptr); \
304+ node->inputs[0].attr.vectorized_strides = {s2, One}; \
305+ node->outputs[0].attr.vectorized_strides = {One, Zero}; \
306+ JOIN(ir_name, AscIrCodegenImpl) impl; \
307+ EXPECT_TRUE(impl.IsNodeValid(*node)); \
308+ (void)impl.IsInplaceSupported(*node); \
309+ (void)impl.IsBrcInlineSupported(*node); \
310+ (void)impl.CalcTmpBufSize(*node); \
311+ }
312+ 
313+TEST_F(AscIrCodegenCalcTest, ReduceFamily_ValidAndCalc) {
314+ EXPECT_REDUCE_VALID(Sum);
315+ EXPECT_REDUCE_VALID(Max);
316+ EXPECT_REDUCE_VALID(Mean);
317+ EXPECT_REDUCE_VALID(All);
318+ EXPECT_REDUCE_VALID(Any);
319+ EXPECT_REDUCE_VALID(Min);
320+ EXPECT_REDUCE_VALID(Prod);
321+ EXPECT_REDUCE_VALID(ArgMax);
322+}
323+ 
324+// ArgMaxMultiRPhase1: single input x, dual outputs value/index
325+TEST_F(AscIrCodegenCalcTest, ReduceFamily_ArgMaxMultiRPhase) {
326+ {
327+ af::AscGraph graph("t_p1");
328+ af::Expression One = af::Symbol(1);
329+ af::Expression Zero = af::Symbol(0);
330+ auto s0 = graph.CreateSizeVar("s0");
331+ auto s1 = graph.CreateSizeVar("s1");
332+ auto z0 = graph.CreateAxis("z0", s0);
333+ auto z1 = graph.CreateAxis("z1", s1);
334+ af::ascir_op::Data x1("x1", graph);
335+ af::ascir_op::Load load1("load1");
336+ af::ascir_op::ArgMaxMultiRPhase1 op0("op0");
337+ af::ascir_op::Store store("store");
338+ af::ascir_op::Output y("y");
339+ x1.attr.sched.axis = {z0.id, z1.id};
340+ x1.y.dtype = af::DT_FLOAT;
341+ *x1.y.axis = {z0.id, z1.id};
342+ *x1.y.repeats = {s0, s1};
343+ *x1.y.strides = {s1, One};
344+ load1.x = x1.y;
345+ load1.attr.sched.axis = {z0.id, z1.id};
346+ load1.y.dtype = af::DT_FLOAT;
347+ *load1.y.axis = {z0.id, z1.id};
348+ *load1.y.repeats = {s0, s1};
349+ *load1.y.strides = {s1, One};
350+ *load1.y.vectorized_axis = {z0.id, z1.id};
351+ op0.x = load1.y;
352+ op0.attr.sched.axis = {z0.id, z1.id};
353+ op0.attr.sched.loop_axis = {z0.id};
354+ op0.value.dtype = af::DT_FLOAT;
355+ *op0.value.axis = {z0.id, z1.id};
356+ *op0.value.repeats = {s0, One};
357+ *op0.value.strides = {One, Zero};
358+ store.x = op0.value;
359+ store.attr.sched.axis = {z0.id, z1.id};
360+ store.y.dtype = af::DT_FLOAT;
361+ *store.y.axis = {z0.id, z1.id};
362+ *store.y.repeats = {s0, One};
363+ *store.y.strides = {One, Zero};
364+ y.x = store.y;
365+ y.attr.sched.axis = {z0.id, z1.id};
366+ y.y.dtype = af::DT_FLOAT;
367+ *y.y.axis = {z0.id, z1.id};
368+ *y.y.repeats = {s0, One};
369+ *y.y.strides = {One, Zero};
370+ auto node = graph.FindNode("op0");
371+ ASSERT_NE(node, nullptr);
372+ node->inputs[0].attr.vectorized_strides = {s1, One};
373+ node->outputs[0].attr.vectorized_strides = {One, Zero};
374+ ArgMaxMultiRPhase1AscIrCodegenImpl impl;
375+ EXPECT_TRUE(impl.IsNodeValid(*node));
376+ }
377+}
378+ 
379+// Binary validity-only: impls whose CalcTmpBufSize may fall back to the empty base default
380+#define EXPECT_BINARY_VALID(ir_name) \
381+ { EXPECT_BINARY_CALC_CORE(ir_name, /*with_calc_assert=*/false); }
382+ 
383+#define EXPECT_BINARY_CALC_CORE(ir_name, with_calc_assert) \
384+ { \
385+ af::AscGraph graph("t_" #ir_name); \
386+ auto s0 = graph.CreateSizeVar("s0"); \
387+ auto s1 = graph.CreateSizeVar("s1"); \
388+ auto z0 = graph.CreateAxis("z0", s0); \
389+ auto z1 = graph.CreateAxis("z1", s1); \
390+ af::ascir_op::Data x1("x1", graph); \
391+ af::ascir_op::Data x2("x2", graph); \
392+ af::ascir_op::Load load1("load1"); \
393+ af::ascir_op::Load load2("load2"); \
394+ af::ascir_op::ir_name op0("op0"); \
395+ af::ascir_op::Store store("store"); \
396+ af::ascir_op::Output y("y"); \
397+ x1.attr.sched.axis = {z0.id, z1.id}; \
398+ x1.y.dtype = af::DT_FLOAT; \
399+ *x1.y.axis = {z0.id, z1.id}; \
400+ *x1.y.repeats = {s0, s1}; \
401+ *x1.y.strides = {s1, af::Symbol(1)}; \
402+ x2.attr.sched.axis = {z0.id, z1.id}; \
403+ x2.y.dtype = af::DT_FLOAT; \
404+ *x2.y.axis = {z0.id, z1.id}; \
405+ *x2.y.repeats = {s0, s1}; \
406+ *x2.y.strides = {s1, af::Symbol(1)}; \
407+ load1.x = x1.y; \
408+ load1.attr.sched.axis = {z0.id, z1.id}; \
409+ load1.y.dtype = af::DT_FLOAT; \
410+ *load1.y.axis = {z0.id, z1.id}; \
411+ *load1.y.repeats = {s0, s1}; \
412+ *load1.y.strides = {s1, af::Symbol(1)}; \
413+ *load1.y.vectorized_axis = {z0.id, z1.id}; \
414+ load2.x = x2.y; \
415+ load2.attr.sched.axis = {z0.id, z1.id}; \
416+ load2.y.dtype = af::DT_FLOAT; \
417+ *load2.y.axis = {z0.id, z1.id}; \
418+ *load2.y.repeats = {s0, s1}; \
419+ *load2.y.strides = {s1, af::Symbol(1)}; \
420+ *load2.y.vectorized_axis = {z0.id, z1.id}; \
421+ op0.x1 = load1.y; \
422+ op0.x2 = load2.y; \
423+ op0.attr.sched.axis = {z0.id, z1.id}; \
424+ op0.y.dtype = af::DT_FLOAT; \
425+ *op0.y.axis = {z0.id, z1.id}; \
426+ *op0.y.repeats = {s0, s1}; \
427+ *op0.y.strides = {s1, af::Symbol(1)}; \
428+ *op0.y.vectorized_axis = {z0.id, z1.id}; \
429+ store.x = op0.y; \
430+ store.attr.sched.axis = {z0.id, z1.id}; \
431+ store.y.dtype = af::DT_FLOAT; \
432+ *store.y.axis = {z0.id, z1.id}; \
433+ *store.y.repeats = {s0, s1}; \
434+ *store.y.strides = {s1, af::Symbol(1)}; \
435+ y.x = store.y; \
436+ y.attr.sched.axis = {z0.id, z1.id}; \
437+ y.y.dtype = af::DT_FLOAT; \
438+ *y.y.axis = {z0.id, z1.id}; \
439+ *y.y.repeats = {s0, s1}; \
440+ *y.y.strides = {s1, af::Symbol(1)}; \
441+ auto node = graph.FindNode("op0"); \
442+ ASSERT_NE(node, nullptr); \
443+ node->inputs[0].attr.vectorized_strides = {s1, af::Symbol(1)}; \
444+ JOIN(ir_name, AscIrCodegenImpl) impl; \
445+ EXPECT_TRUE(impl.IsNodeValid(*node)); \
446+ (void)impl.IsInplaceSupported(*node); \
447+ (void)impl.IsBrcInlineSupported(*node); \
448+ if (with_calc_assert) { \
449+ const auto bufs = impl.CalcTmpBufSize(*node); \
450+ EXPECT_FALSE(bufs.empty()); \
451+ } else { \
452+ (void)impl.CalcTmpBufSize(*node); \
453+ } \
454+ }
455+ 
456+TEST_F(AscIrCodegenCalcTest, IsNodeValid_BinaryExtended) {
457+ EXPECT_BINARY_VALID(Div);
458+ EXPECT_BINARY_VALID(Sub);
459+ EXPECT_BINARY_VALID(Mul);
460+ EXPECT_BINARY_VALID(Minimum);
461+ EXPECT_BINARY_VALID(Maximum);
462+}
463+ 
464+TEST_F(AscIrCodegenCalcTest, IsNodeValid_MatmulFamily) {
465+ EXPECT_BINARY_VALID(MatMul);
466+ EXPECT_BINARY_VALID(BatchMatMul);
467+ EXPECT_BINARY_VALID(Gather);
468+ EXPECT_BINARY_VALID(Axpy);
469+}
470+ 
471+// Three-input ops (Where/Select/ClipByValue share the x1/x2/x3 layout; MaskedFill uses x/mask/value)
472+#define EXPECT_TRINARY_VALID(ir_name, in1, in2, in3) \
473+ { \
474+ af::AscGraph graph("t_" #ir_name); \
475+ auto s0 = graph.CreateSizeVar("s0"); \
476+ auto s1 = graph.CreateSizeVar("s1"); \
477+ auto z0 = graph.CreateAxis("z0", s0); \
478+ auto z1 = graph.CreateAxis("z1", s1); \
479+ af::ascir_op::Data x1("x1", graph); \
480+ af::ascir_op::Data x2("x2", graph); \
481+ af::ascir_op::Data x3("x3", graph); \
482+ af::ascir_op::Load load1("load1"); \
483+ af::ascir_op::Load load2("load2"); \
484+ af::ascir_op::Load load3("load3"); \
485+ af::ascir_op::ir_name op0("op0"); \
486+ af::ascir_op::Store store("store"); \
487+ af::ascir_op::Output y("y"); \
488+ x1.attr.sched.axis = {z0.id, z1.id}; \
489+ x1.y.dtype = af::DT_FLOAT; \
490+ *x1.y.axis = {z0.id, z1.id}; \
491+ *x1.y.repeats = {s0, s1}; \
492+ *x1.y.strides = {s1, af::Symbol(1)}; \
493+ x2.attr.sched.axis = {z0.id, z1.id}; \
494+ x2.y.dtype = af::DT_FLOAT; \
495+ *x2.y.axis = {z0.id, z1.id}; \
496+ *x2.y.repeats = {s0, s1}; \
497+ *x2.y.strides = {s1, af::Symbol(1)}; \
498+ x3.attr.sched.axis = {z0.id, z1.id}; \
499+ x3.y.dtype = af::DT_FLOAT; \
500+ *x3.y.axis = {z0.id, z1.id}; \
501+ *x3.y.repeats = {s0, s1}; \
502+ *x3.y.strides = {s1, af::Symbol(1)}; \
503+ load1.x = x1.y; \
504+ load1.attr.sched.axis = {z0.id, z1.id}; \
505+ load1.y.dtype = af::DT_FLOAT; \
506+ *load1.y.axis = {z0.id, z1.id}; \
507+ *load1.y.repeats = {s0, s1}; \
508+ *load1.y.strides = {s1, af::Symbol(1)}; \
509+ *load1.y.vectorized_axis = {z0.id, z1.id}; \
510+ load2.x = x2.y; \
511+ load2.attr.sched.axis = {z0.id, z1.id}; \
512+ load2.y.dtype = af::DT_FLOAT; \
513+ *load2.y.axis = {z0.id, z1.id}; \
514+ *load2.y.repeats = {s0, s1}; \
515+ *load2.y.strides = {s1, af::Symbol(1)}; \
516+ *load2.y.vectorized_axis = {z0.id, z1.id}; \
517+ load3.x = x3.y; \
518+ load3.attr.sched.axis = {z0.id, z1.id}; \
519+ load3.y.dtype = af::DT_FLOAT; \
520+ *load3.y.axis = {z0.id, z1.id}; \
521+ *load3.y.repeats = {s0, s1}; \
522+ *load3.y.strides = {s1, af::Symbol(1)}; \
523+ *load3.y.vectorized_axis = {z0.id, z1.id}; \
524+ op0.in1 = load1.y; \
525+ op0.in2 = load2.y; \
526+ op0.in3 = load3.y; \
527+ op0.attr.sched.axis = {z0.id, z1.id}; \
528+ op0.y.dtype = af::DT_FLOAT; \
529+ *op0.y.axis = {z0.id, z1.id}; \
530+ *op0.y.repeats = {s0, s1}; \
531+ *op0.y.strides = {s1, af::Symbol(1)}; \
532+ *op0.y.vectorized_axis = {z0.id, z1.id}; \
533+ store.x = op0.y; \
534+ store.attr.sched.axis = {z0.id, z1.id}; \
535+ store.y.dtype = af::DT_FLOAT; \
536+ *store.y.axis = {z0.id, z1.id}; \
537+ *store.y.repeats = {s0, s1}; \
538+ *store.y.strides = {s1, af::Symbol(1)}; \
539+ y.x = store.y; \
540+ y.attr.sched.axis = {z0.id, z1.id}; \
541+ y.y.dtype = af::DT_FLOAT; \
542+ *y.y.axis = {z0.id, z1.id}; \
543+ *y.y.repeats = {s0, s1}; \
544+ *y.y.strides = {s1, af::Symbol(1)}; \
545+ auto node = graph.FindNode("op0"); \
546+ ASSERT_NE(node, nullptr); \
547+ node->inputs[0].attr.vectorized_strides = {s1, af::Symbol(1)}; \
548+ JOIN(ir_name, AscIrCodegenImpl) impl; \
549+ EXPECT_TRUE(impl.IsNodeValid(*node)); \
550+ (void)impl.CalcTmpBufSize(*node); \
551+ (void)impl.IsInplaceSupported(*node); \
552+ (void)impl.IsBrcInlineSupported(*node); \
553+ }
554+ 
555+TEST_F(AscIrCodegenCalcTest, IsNodeValid_TrinaryOps) {
556+ EXPECT_TRINARY_VALID(Where, x1, x2, x3);
557+ EXPECT_TRINARY_VALID(Select, x1, x2, x3);
558+ EXPECT_TRINARY_VALID(ClipByValue, x1, x2, x3);
559+ EXPECT_TRINARY_VALID(MaskedFill, x, mask, value);
560+}
561+ 
562+} // namespace ascir
563+} // namespace af
@@ -92,6 +92,41 @@ TEST_F(AscIrCodegenImplMetaTest, CodegenImplMeta_IsWellFormed) {
92 EXPECT_CODEGEN_META(TrueDiv, true);92 EXPECT_CODEGEN_META(TrueDiv, true);
93 EXPECT_CODEGEN_META(Ub2ub, false);93 EXPECT_CODEGEN_META(Ub2ub, false);
94 EXPECT_CODEGEN_META(Where, true);94 EXPECT_CODEGEN_META(Where, true);
95+ EXPECT_CODEGEN_META(IndexExpr, false);
96+ EXPECT_CODEGEN_META(Sqrt, false);
97+ EXPECT_CODEGEN_META(Scalar, false);
98+ EXPECT_CODEGEN_META(Mean, false);
99+ EXPECT_CODEGEN_META(Rsqrt, true);
100+ EXPECT_CODEGEN_META(Isnan, true);
101+ EXPECT_CODEGEN_META(IsFinite, true);
102+ EXPECT_CODEGEN_META(Any, true);
103+}
104+ 
105+// Scalar-input / brc-inline policy probes: every override listed in the coverage report is
106+// invoked once; the counter assertion fails if any call is dropped.
107+TEST_F(AscIrCodegenImplMetaTest, InputScalarAndBrcPolicies) {
108+ size_t probed = 0U;
109+#define PROBE_POLICY(cls, expr) \
110+ { \
111+ JOIN(cls, AscIrCodegenImpl) impl; \
112+ (void)(expr); \
113+ ++probed; \
114+ }
115+ PROBE_POLICY(Transpose, impl.IsScalarInputSupported({false, true}));
116+ PROBE_POLICY(Le, impl.IsScalarInputSupported({false, true}));
117+ PROBE_POLICY(LogicalAnd, impl.IsScalarInputSupported({false, true}));
118+ PROBE_POLICY(LogicalOr, impl.IsScalarInputSupported({false, true}));
119+ PROBE_POLICY(Maximum, impl.IsScalarInputSupported({false, true}));
120+ PROBE_POLICY(Pow, impl.IsScalarInputSupported({false, true}));
121+ PROBE_POLICY(TrueDiv, impl.IsScalarInputSupported({false, true}));
122+ PROBE_POLICY(MaskedFill, impl.IsScalarInputSupported({false, true}));
123+ PROBE_POLICY(Eq, impl.IsScalarInputSupportedIfExchangeInputs({true, false}));
124+ PROBE_POLICY(LogicalAnd, impl.IsScalarInputSupportedIfExchangeInputs({true, false}));
125+ PROBE_POLICY(LogicalOr, impl.IsScalarInputSupportedIfExchangeInputs({true, false}));
126+ PROBE_POLICY(Maximum, impl.IsScalarInputSupportedIfExchangeInputs({true, false}));
127+ PROBE_POLICY(Mul, impl.IsScalarInputSupportedIfExchangeInputs({true, false}));
128+#undef PROBE_POLICY
129+ EXPECT_EQ(probed, 13U);
95}130}
96 131 
97} // namespace ascir132} // namespace ascir
@@ -12,6 +12,7 @@
12#include "ascir_utils.h"12#include "ascir_utils.h"
13#include "ascendc_graph_txt_dumper.h"13#include "ascendc_graph_txt_dumper.h"
14#include "ascir.h"14#include "ascir.h"
15+#include "ascir_ops.h"
15 16 
16namespace af {17namespace af {
17namespace ascir {18namespace ascir {
@@ -37,9 +38,12 @@ TEST_F(AscirMetaUtilsTest, GetDumpFilePrefix_ShouldBeEmptyWhenDumpDisabled) {
37 38 
38// Test: dtype info lookup succeeds for common dtypes and fails gracefully for unknown ones39// Test: dtype info lookup succeeds for common dtypes and fails gracefully for unknown ones
39TEST_F(AscirMetaUtilsTest, GetDtypeInfo_ShouldResolveCommonDtypes) {40TEST_F(AscirMetaUtilsTest, GetDtypeInfo_ShouldResolveCommonDtypes) {
40- EXPECT_NE(::ascir::dumper::GetDtypeInfo(ge::DT_FLOAT), nullptr);41+ const std::vector<ge::DataType> common_dtypes = {
41- EXPECT_NE(::ascir::dumper::GetDtypeInfo(ge::DT_FLOAT16), nullptr);42+ ge::DT_FLOAT, ge::DT_FLOAT16, ge::DT_BF16, ge::DT_INT8, ge::DT_UINT8, ge::DT_INT16, ge::DT_UINT16,
42- EXPECT_NE(::ascir::dumper::GetDtypeInfo(ge::DT_INT32), nullptr);43+ ge::DT_INT32, ge::DT_UINT32, ge::DT_INT64, ge::DT_UINT64, ge::DT_BOOL, ge::DT_DOUBLE};
44+ for (const auto dtype : common_dtypes) {
45+ EXPECT_NE(::ascir::dumper::GetDtypeInfo(dtype), nullptr);
46+ }
43}47}
44 48 
45// Test: axis type priority/suffix are consistent lookups for the same type49// Test: axis type priority/suffix are consistent lookups for the same type
@@ -77,5 +81,913 @@ TEST_F(AscirMetaUtilsTest, BuildAxisIdMaps_ShouldMapGraphAxes) {
77 ASSERT_GE(id_to_type.size(), 1U);81 ASSERT_GE(id_to_type.size(), 1U);
78}82}
79 83 
84+// Test: DebugImplGraphStr renders node attributes end-to-end: exec condition, compute unit,
85+// queue/buffer memory info with hardware and position rendering
86+TEST_F(AscirMetaUtilsTest, DebugImplGraphStr_ShouldRenderNodeAndMemAttributes) {
87+ af::AscGraph graph("test");
88+ auto s0 = graph.CreateSizeVar("s0");
89+ auto s1 = graph.CreateSizeVar("s1");
90+ 
91+ auto z0 = graph.CreateAxis("z0", s0);
92+ auto z1 = graph.CreateAxis("z1", s1);
93+ 
94+ af::ascir_op::Data x1("x1", graph);
95+ af::ascir_op::Load load1("load1");
96+ af::ascir_op::Abs abs("abs");
97+ af::ascir_op::Store store("store");
98+ af::ascir_op::Output y("y");
99+ 
100+ x1.attr.sched.axis = {z0.id, z1.id};
101+ x1.y.dtype = af::DT_FLOAT;
102+ *x1.y.axis = {z0.id, z1.id};
103+ *x1.y.repeats = {s0, s1};
104+ *x1.y.strides = {s1, af::Symbol(1)};
105+ 
106+ load1.x = x1.y;
107+ load1.attr.sched.axis = {z0.id, z1.id};
108+ load1.y.dtype = af::DT_FLOAT;
109+ *load1.y.axis = {z0.id, z1.id};
110+ *load1.y.repeats = {s0, s1};
111+ *load1.y.strides = {s1, af::Symbol(1)};
112+ *load1.y.vectorized_axis = {z0.id, z1.id};
113+ 
114+ abs.x = load1.y;
115+ abs.attr.sched.axis = {z0.id, z1.id};
116+ // non-default exec condition so that DebugStr renders it via ExecConditionToStr
117+ abs.attr.sched.exec_condition = af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis;
118+ abs.y.dtype = af::DT_FLOAT;
119+ *abs.y.axis = {z0.id, z1.id};
120+ *abs.y.repeats = {s0, s1};
121+ *abs.y.strides = {s1, af::Symbol(1)};
122+ *abs.y.vectorized_axis = {z0.id, z1.id};
123+ // queue-typed output memory rendered through OutputQueueMemStr / MemHardwareToStr / PositionToStr
124+ abs.y.mem->alloc_type = af::AllocType::kAllocTypeQueue;
125+ abs.y.mem->hardware = af::MemHardware::kMemHardwareUB;
126+ abs.y.mem->position = af::Position::kPositionInvalid;
127+ abs.y.mem->reuse_id = 1;
128+ abs.y.que->id = 5;
129+ abs.y.que->depth = 2;
130+ 
131+ store.x = abs.y;
132+ store.attr.sched.axis = {z0.id, z1.id};
133+ store.y.dtype = af::DT_FLOAT;
134+ *store.y.axis = {z0.id, z1.id};
135+ *store.y.repeats = {s0, s1};
136+ *store.y.strides = {s1, af::Symbol(1)};
137+ // buffer-typed output memory rendered through OutputBufferMemStr
138+ store.y.mem->alloc_type = af::AllocType::kAllocTypeBuffer;
139+ store.y.mem->hardware = af::MemHardware::kMemHardwareUB;
140+ store.y.buf->id = 3;
141+ 
142+ y.x = store.y;
143+ y.attr.sched.axis = {z0.id, z1.id};
144+ y.y.dtype = af::DT_FLOAT;
145+ *y.y.axis = {z0.id, z1.id};
146+ *y.y.repeats = {s0, s1};
147+ *y.y.strides = {s1, af::Symbol(1)};
148+ 
149+ const auto debug_str = ::ascir::utils::DebugImplGraphStr(graph);
150+ ASSERT_FALSE(debug_str.empty());
151+ EXPECT_NE(debug_str.find(".api.unit"), std::string::npos);
152+ EXPECT_NE(debug_str.find(".exec_condition"), std::string::npos);
153+ EXPECT_NE(debug_str.find("que_id=5"), std::string::npos);
154+ EXPECT_NE(debug_str.find("buf_id=3"), std::string::npos);
155+}
156+ 
157+// Test: subgraph-mode dump walks the loop-execution chain (loop axes grouping, node loops)
158+TEST_F(AscirMetaUtilsTest, DumpLoopExecutionView_ShouldRenderSubgraphLoopChain) {
159+ // the "_VfSubgraph_" fragment switches the dumper into subgraph mode
160+ af::AscGraph graph("test_VfSubgraph_0");
161+ auto s0 = graph.CreateSizeVar("s0");
162+ auto s1 = graph.CreateSizeVar("s1");
163+ 
164+ auto z0 = graph.CreateAxis("z0", s0);
165+ auto z1 = graph.CreateAxis("z1", s1);
166+ 
167+ af::ascir_op::Data x1("x1", graph);
168+ af::ascir_op::Load load1("load1");
169+ af::ascir_op::Abs abs("abs");
170+ af::ascir_op::Store store("store");
171+ af::ascir_op::Output y("y");
172+ 
173+ x1.attr.sched.axis = {z0.id, z1.id};
174+ x1.y.dtype = af::DT_FLOAT;
175+ *x1.y.axis = {z0.id, z1.id};
176+ *x1.y.repeats = {s0, s1};
177+ *x1.y.strides = {s1, af::Symbol(1)};
178+ 
179+ load1.x = x1.y;
180+ load1.attr.sched.axis = {z0.id, z1.id};
181+ load1.y.dtype = af::DT_FLOAT;
182+ *load1.y.axis = {z0.id, z1.id};
183+ *load1.y.repeats = {s0, s1};
184+ *load1.y.strides = {s1, af::Symbol(1)};
185+ *load1.y.vectorized_axis = {z0.id, z1.id};
186+ 
187+ abs.x = load1.y;
188+ abs.attr.sched.axis = {z0.id, z1.id};
189+ abs.attr.sched.loop_axis = {z0.id};
190+ abs.attr.sched.exec_condition = af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis;
191+ abs.y.dtype = af::DT_FLOAT;
192+ *abs.y.axis = {z0.id, z1.id};
193+ *abs.y.repeats = {s0, s1};
194+ *abs.y.strides = {s1, af::Symbol(1)};
195+ *abs.y.vectorized_axis = {z0.id, z1.id};
196+ 
197+ store.x = abs.y;
198+ store.attr.sched.axis = {z0.id, z1.id};
199+ store.y.dtype = af::DT_FLOAT;
200+ *store.y.axis = {z0.id, z1.id};
201+ *store.y.repeats = {s0, s1};
202+ *store.y.strides = {s1, af::Symbol(1)};
203+ 
204+ y.x = store.y;
205+ y.attr.sched.axis = {z0.id, z1.id};
206+ y.y.dtype = af::DT_FLOAT;
207+ *y.y.axis = {z0.id, z1.id};
208+ *y.y.repeats = {s0, s1};
209+ *y.y.strides = {s1, af::Symbol(1)};
210+ 
211+ const auto ctx = ::ascir::dumper::BuildDumpContext(graph);
212+ const auto text = ::ascir::dumper::DumpLoopExecutionView(graph, ctx);
213+ EXPECT_FALSE(text.empty());
214+}
215+ 
216+// Test: regular-mode dump with a scalar node and a non-default exec condition renders node execution
217+TEST_F(AscirMetaUtilsTest, DumpLoopExecutionView_ShouldRenderScalarAndExecCondition) {
218+ af::AscGraph graph("regular_graph");
219+ auto s0 = graph.CreateSizeVar("s0");
220+ auto s1 = graph.CreateSizeVar("s1");
221+ 
222+ auto z0 = graph.CreateAxis("z0", s0);
223+ auto z1 = graph.CreateAxis("z1", s1);
224+ 
225+ af::ascir_op::Scalar sc("sc", graph);
226+ af::ascir_op::Data x1("x1", graph);
227+ af::ascir_op::Load load1("load1");
228+ af::ascir_op::Mul mul("mul");
229+ af::ascir_op::Store store("store");
230+ af::ascir_op::Output y("y");
231+ 
232+ sc.attr.sched.axis = {z0.id, z1.id};
233+ sc.y.dtype = af::DT_FLOAT;
234+ *sc.y.axis = {};
235+ *sc.y.repeats = {};
236+ *sc.y.strides = {};
237+ 
238+ x1.attr.sched.axis = {z0.id, z1.id};
239+ x1.y.dtype = af::DT_FLOAT;
240+ *x1.y.axis = {z0.id, z1.id};
241+ *x1.y.repeats = {s0, s1};
242+ *x1.y.strides = {s1, af::Symbol(1)};
243+ 
244+ load1.x = x1.y;
245+ load1.attr.sched.axis = {z0.id, z1.id};
246+ load1.y.dtype = af::DT_FLOAT;
247+ *load1.y.axis = {z0.id, z1.id};
248+ *load1.y.repeats = {s0, s1};
249+ *load1.y.strides = {s1, af::Symbol(1)};
250+ *load1.y.vectorized_axis = {z0.id, z1.id};
251+ 
252+ mul.x1 = load1.y;
253+ mul.x2 = sc.y;
254+ mul.attr.sched.axis = {z0.id, z1.id};
255+ mul.attr.sched.exec_condition = af::ExecuteCondition::kCacheBlockSplitOriginBroadcastAxis;
256+ mul.y.dtype = af::DT_FLOAT;
257+ *mul.y.axis = {z0.id, z1.id};
258+ *mul.y.repeats = {s0, s1};
259+ *mul.y.strides = {s1, af::Symbol(1)};
260+ *mul.y.vectorized_axis = {z0.id, z1.id};
261+ 
262+ store.x = mul.y;
263+ store.attr.sched.axis = {z0.id, z1.id};
264+ store.y.dtype = af::DT_FLOAT;
265+ *store.y.axis = {z0.id, z1.id};
266+ *store.y.repeats = {s0, s1};
267+ *store.y.strides = {s1, af::Symbol(1)};
268+ 
269+ y.x = store.y;
270+ y.attr.sched.axis = {z0.id, z1.id};
271+ y.y.dtype = af::DT_FLOAT;
272+ *y.y.axis = {z0.id, z1.id};
273+ *y.y.repeats = {s0, s1};
274+ *y.y.strides = {s1, af::Symbol(1)};
275+ 
276+ const auto ctx = ::ascir::dumper::BuildDumpContext(graph);
277+ const auto text = ::ascir::dumper::DumpLoopExecutionView(graph, ctx);
278+ EXPECT_FALSE(text.empty());
279+}
280+ 
281+// Test: dtype variants flow through DtypeToStr and the normal-mem branch of OutputMemStr
282+TEST_F(AscirMetaUtilsTest, DebugImplGraphStr_ShouldRenderDtypeVariants) {
283+ const std::vector<af::DataType> dtypes = {af::DT_INT32, af::DT_BF16, af::DT_INT8, af::DT_BOOL};
284+ for (const auto dtype : dtypes) {
285+ af::AscGraph graph("t_dtype");
286+ auto s0 = graph.CreateSizeVar("s0");
287+ auto s1 = graph.CreateSizeVar("s1");
288+ auto z0 = graph.CreateAxis("z0", s0);
289+ auto z1 = graph.CreateAxis("z1", s1);
290+ af::ascir_op::Data x1("x1", graph);
291+ af::ascir_op::Load load1("load1");
292+ af::ascir_op::Abs abs("abs");
293+ af::ascir_op::Store store("store");
294+ af::ascir_op::Output y("y");
295+ x1.attr.sched.axis = {z0.id, z1.id};
296+ x1.y.dtype = dtype;
297+ *x1.y.axis = {z0.id, z1.id};
298+ *x1.y.repeats = {s0, s1};
299+ *x1.y.strides = {s1, af::Symbol(1)};
300+ load1.x = x1.y;
301+ load1.attr.sched.axis = {z0.id, z1.id};
302+ load1.y.dtype = dtype;
303+ *load1.y.axis = {z0.id, z1.id};
304+ *load1.y.repeats = {s0, s1};
305+ *load1.y.strides = {s1, af::Symbol(1)};
306+ *load1.y.vectorized_axis = {z0.id, z1.id};
307+ abs.x = load1.y;
308+ abs.attr.sched.axis = {z0.id, z1.id};
309+ abs.y.dtype = dtype;
310+ *abs.y.axis = {z0.id, z1.id};
311+ *abs.y.repeats = {s0, s1};
312+ *abs.y.strides = {s1, af::Symbol(1)};
313+ *abs.y.vectorized_axis = {z0.id, z1.id};
314+ // normal-mem output with tensor id and reuse id to cover those rendering branches
315+ abs.y.mem->alloc_type = af::AllocType::kAllocTypeGlobal;
316+ abs.y.mem->hardware = af::MemHardware::kMemHardwareGM;
317+ abs.y.mem->position = af::Position::kPositionVecIn;
318+ abs.y.mem->tensor_id = 7;
319+ abs.y.mem->reuse_id = 2;
320+ store.x = abs.y;
321+ store.attr.sched.axis = {z0.id, z1.id};
322+ store.y.dtype = dtype;
323+ *store.y.axis = {z0.id, z1.id};
324+ *store.y.repeats = {s0, s1};
325+ *store.y.strides = {s1, af::Symbol(1)};
326+ y.x = store.y;
327+ y.attr.sched.axis = {z0.id, z1.id};
328+ y.y.dtype = dtype;
329+ *y.y.axis = {z0.id, z1.id};
330+ *y.y.repeats = {s0, s1};
331+ *y.y.strides = {s1, af::Symbol(1)};
332+ const auto text = ::ascir::utils::DebugImplGraphStr(graph);
333+ EXPECT_FALSE(text.empty());
334+ EXPECT_NE(text.find(".api.unit"), std::string::npos);
335+ }
336+}
337+ 
338+// Test: a broadcast node in the dumped graph exercises the broadcast-related helper branches
339+TEST_F(AscirMetaUtilsTest, DumpLoopExecutionView_ShouldRenderBroadcastNode) {
340+ af::AscGraph graph("bcast_graph");
341+ auto s0 = graph.CreateSizeVar("s0");
342+ auto s1 = graph.CreateSizeVar("s1");
343+ auto z0 = graph.CreateAxis("z0", s0);
344+ auto z1 = graph.CreateAxis("z1", s1);
345+ af::ascir_op::Data x1("x1", graph);
346+ af::ascir_op::Load load1("load1");
347+ af::ascir_op::Broadcast brc("brc");
348+ af::ascir_op::Store store("store");
349+ af::ascir_op::Output y("y");
350+ x1.attr.sched.axis = {z0.id, z1.id};
351+ x1.y.dtype = af::DT_FLOAT;
352+ *x1.y.axis = {z0.id, z1.id};
353+ *x1.y.repeats = {s0, s1};
354+ *x1.y.strides = {s1, af::Symbol(1)};
355+ load1.x = x1.y;
356+ load1.attr.sched.axis = {z0.id, z1.id};
357+ load1.y.dtype = af::DT_FLOAT;
358+ *load1.y.axis = {z0.id, z1.id};
359+ *load1.y.repeats = {s0, s1};
360+ *load1.y.strides = {s1, af::Symbol(1)};
361+ *load1.y.vectorized_axis = {z0.id, z1.id};
362+ brc.x = load1.y;
363+ brc.attr.sched.axis = {z0.id, z1.id};
364+ brc.y.dtype = af::DT_FLOAT;
365+ *brc.y.axis = {z0.id, z1.id};
366+ *brc.y.repeats = {s0, s1};
367+ *brc.y.strides = {s1, af::Symbol(1)};
368+ *brc.y.vectorized_axis = {z0.id, z1.id};
369+ store.x = brc.y;
370+ store.attr.sched.axis = {z0.id, z1.id};
371+ store.y.dtype = af::DT_FLOAT;
372+ *store.y.axis = {z0.id, z1.id};
373+ *store.y.repeats = {s0, s1};
374+ *store.y.strides = {s1, af::Symbol(1)};
375+ y.x = store.y;
376+ y.attr.sched.axis = {z0.id, z1.id};
377+ y.y.dtype = af::DT_FLOAT;
378+ *y.y.axis = {z0.id, z1.id};
379+ *y.y.repeats = {s0, s1};
380+ *y.y.strides = {s1, af::Symbol(1)};
381+ const auto ctx = ::ascir::dumper::BuildDumpContext(graph);
382+ const auto text = ::ascir::dumper::DumpLoopExecutionView(graph, ctx);
383+ EXPECT_FALSE(text.empty());
384+}
385+ 
386+// Test: enabling codegen-compile-debug via AUTOFUSE_DFX_FLAGS produces a real dump prefix
387+TEST_F(AscirMetaUtilsTest, GetDumpFilePrefix_ShouldBuildPrefixWhenDebugEnabled) {
388+ const std::string debug_root = "/tmp/ascir_ut_dump_" + std::to_string(getpid());
389+ setenv("AUTOFUSE_DFX_FLAGS", ("--codegen_compile_debug=true;--debug_dir=" + debug_root).c_str(), 1);
390+ const auto prefix = ::ascir::utils::GetDumpFilePrefix();
391+ unsetenv("AUTOFUSE_DFX_FLAGS");
392+ EXPECT_FALSE(prefix.empty());
393+ EXPECT_EQ(prefix.find(debug_root), 0U);
394+}
395+ 
396+// Test: exec-condition variants on a scalar node cover every rendering branch
397+TEST_F(AscirMetaUtilsTest, DumpLoopExecutionView_ShouldRenderExecConditionVariants) {
398+ const std::vector<af::ExecuteCondition> conditions = {
399+ af::ExecuteCondition::kNoCache, af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis,
400+ af::ExecuteCondition::kCacheBlockSplitOriginBroadcastAxis, af::ExecuteCondition::kConditionInvalid};
401+ for (const auto cond : conditions) {
402+ af::AscGraph graph("t_cond");
403+ auto s0 = graph.CreateSizeVar("s0");
404+ auto s1 = graph.CreateSizeVar("s1");
405+ auto z0 = graph.CreateAxis("z0", s0);
406+ auto z1 = graph.CreateAxis("z1", s1);
407+ af::ascir_op::Scalar sc("sc", graph);
408+ af::ascir_op::Data x1("x1", graph);
409+ af::ascir_op::Load load1("load1");
410+ af::ascir_op::Mul mul("mul");
411+ af::ascir_op::Store store("store");
412+ af::ascir_op::Output y("y");
413+ sc.attr.sched.axis = {z0.id, z1.id};
414+ sc.attr.sched.exec_condition = cond;
415+ sc.y.dtype = af::DT_FLOAT;
416+ *sc.y.axis = {};
417+ *sc.y.repeats = {};
418+ *sc.y.strides = {};
419+ x1.attr.sched.axis = {z0.id, z1.id};
420+ x1.y.dtype = af::DT_FLOAT;
421+ *x1.y.axis = {z0.id, z1.id};
422+ *x1.y.repeats = {s0, s1};
423+ *x1.y.strides = {s1, af::Symbol(1)};
424+ load1.x = x1.y;
425+ load1.attr.sched.axis = {z0.id, z1.id};
426+ load1.y.dtype = af::DT_FLOAT;
427+ *load1.y.axis = {z0.id, z1.id};
428+ *load1.y.repeats = {s0, s1};
429+ *load1.y.strides = {s1, af::Symbol(1)};
430+ *load1.y.vectorized_axis = {z0.id, z1.id};
431+ mul.x1 = load1.y;
432+ mul.x2 = sc.y;
433+ mul.attr.sched.axis = {z0.id, z1.id};
434+ mul.y.dtype = af::DT_FLOAT;
435+ *mul.y.axis = {z0.id, z1.id};
436+ *mul.y.repeats = {s0, s1};
437+ *mul.y.strides = {s1, af::Symbol(1)};
438+ *mul.y.vectorized_axis = {z0.id, z1.id};
439+ // unset tensor id and negative reuse id cover the "omitted field" rendering branches
440+ mul.y.mem->alloc_type = af::AllocType::kAllocTypeQueue;
441+ mul.y.mem->hardware = af::MemHardware::kMemHardwareUB;
442+ mul.y.mem->position = af::Position::kPositionVecOut;
443+ mul.y.mem->tensor_id = af::kIdNone;
444+ mul.y.mem->reuse_id = -1;
445+ mul.y.que->id = 2;
446+ mul.y.que->depth = 1;
447+ store.x = mul.y;
448+ store.attr.sched.axis = {z0.id, z1.id};
449+ store.y.dtype = af::DT_FLOAT;
450+ *store.y.axis = {z0.id, z1.id};
451+ *store.y.repeats = {s0, s1};
452+ *store.y.strides = {s1, af::Symbol(1)};
453+ y.x = store.y;
454+ y.attr.sched.axis = {z0.id, z1.id};
455+ y.y.dtype = af::DT_FLOAT;
456+ *y.y.axis = {z0.id, z1.id};
457+ *y.y.repeats = {s0, s1};
458+ *y.y.strides = {s1, af::Symbol(1)};
459+ const auto ctx = ::ascir::dumper::BuildDumpContext(graph);
460+ const auto text = ::ascir::dumper::DumpLoopExecutionView(graph, ctx);
461+ EXPECT_FALSE(text.empty());
462+ }
463+}
464+ 
465+// Test: a node with tmp buffer descriptors renders the tmp_buf block in verbose mode
466+TEST_F(AscirMetaUtilsTest, DebugImplGraphStr_ShouldRenderTmpBuffers) {
467+ af::AscGraph graph("t_tmpbuf");
468+ auto s0 = graph.CreateSizeVar("s0");
469+ auto s1 = graph.CreateSizeVar("s1");
470+ auto z0 = graph.CreateAxis("z0", s0);
471+ auto z1 = graph.CreateAxis("z1", s1);
472+ af::ascir_op::Data x1("x1", graph);
473+ af::ascir_op::Load load1("load1");
474+ af::ascir_op::Abs abs("abs");
475+ af::ascir_op::Store store("store");
476+ af::ascir_op::Output y("y");
477+ x1.attr.sched.axis = {z0.id, z1.id};
478+ x1.y.dtype = af::DT_FLOAT;
479+ *x1.y.axis = {z0.id, z1.id};
480+ *x1.y.repeats = {s0, s1};
481+ *x1.y.strides = {s1, af::Symbol(1)};
482+ load1.x = x1.y;
483+ load1.attr.sched.axis = {z0.id, z1.id};
484+ load1.y.dtype = af::DT_FLOAT;
485+ *load1.y.axis = {z0.id, z1.id};
486+ *load1.y.repeats = {s0, s1};
487+ *load1.y.strides = {s1, af::Symbol(1)};
488+ *load1.y.vectorized_axis = {z0.id, z1.id};
489+ abs.x = load1.y;
490+ abs.attr.sched.axis = {z0.id, z1.id};
491+ abs.y.dtype = af::DT_FLOAT;
492+ *abs.y.axis = {z0.id, z1.id};
493+ *abs.y.repeats = {s0, s1};
494+ *abs.y.strides = {s1, af::Symbol(1)};
495+ *abs.y.vectorized_axis = {z0.id, z1.id};
496+ const auto node = graph.FindNode("abs");
497+ ASSERT_NE(node, nullptr);
498+ af::TmpBuffer tmp_buf;
499+ tmp_buf.id = 1;
500+ tmp_buf.buf_desc.size = af::Symbol(8192);
501+ node->attr.tmp_buffers = {tmp_buf};
502+ store.x = abs.y;
503+ store.attr.sched.axis = {z0.id, z1.id};
504+ store.y.dtype = af::DT_FLOAT;
505+ *store.y.axis = {z0.id, z1.id};
506+ *store.y.repeats = {s0, s1};
507+ *store.y.strides = {s1, af::Symbol(1)};
508+ y.x = store.y;
509+ y.attr.sched.axis = {z0.id, z1.id};
510+ y.y.dtype = af::DT_FLOAT;
511+ *y.y.axis = {z0.id, z1.id};
512+ *y.y.repeats = {s0, s1};
513+ *y.y.strides = {s1, af::Symbol(1)};
514+ const auto text = ::ascir::utils::DebugImplGraphStr(graph);
515+ EXPECT_NE(text.find("tmp_buf"), std::string::npos);
516+}
517+ 
518+// Final sweep: constant-size axes, workspace node, broadcast axis with zero stride and a
519+// buffer-typed output inside a subgraph-named graph tick the remaining rendering branches.
520+TEST_F(AscirMetaUtilsTest, DumpLoopExecutionView_FinalSweepRegular) {
521+ af::AscGraph graph("sweep_regular_graph");
522+ // constant axes: stride/repeat const-value branches and axis-size rendering
523+ auto c0 = graph.CreateSizeVar(4);
524+ auto c1 = graph.CreateSizeVar(8);
525+ auto z0 = graph.CreateAxis("z0", c0);
526+ auto z1 = graph.CreateAxis("z1", c1);
527+ 
528+ af::ascir_op::Data x1("x1", graph);
529+ af::ascir_op::Workspace ws("ws");
530+ af::ascir_op::Load load1("load1");
531+ af::ascir_op::Broadcast brc("brc");
532+ af::ascir_op::Store store("store");
533+ af::ascir_op::Output y("y");
534+ 
535+ ws.attr.sched.axis = {z0.id, z1.id};
536+ ws.y.dtype = af::DT_FLOAT;
537+ *ws.y.axis = {z0.id, z1.id};
538+ *ws.y.repeats = {c0, c1};
539+ *ws.y.strides = {c1, af::Symbol(1)};
540+ 
541+ x1.attr.sched.axis = {z0.id, z1.id};
542+ x1.y.dtype = af::DT_FLOAT;
543+ *x1.y.axis = {z0.id, z1.id};
544+ *x1.y.repeats = {c0, c1};
545+ *x1.y.strides = {c1, af::Symbol(1)};
546+ 
547+ load1.x = x1.y;
548+ load1.attr.sched.axis = {z0.id, z1.id};
549+ load1.y.dtype = af::DT_FLOAT;
550+ *load1.y.axis = {z0.id, z1.id};
551+ *load1.y.repeats = {c0, c1};
552+ *load1.y.strides = {c1, af::Symbol(1)};
553+ *load1.y.vectorized_axis = {z0.id, z1.id};
554+ 
555+ brc.x = load1.y;
556+ brc.attr.sched.axis = {z0.id, z1.id};
557+ brc.y.dtype = af::DT_FLOAT;
558+ *brc.y.axis = {z0.id, z1.id};
559+ *brc.y.repeats = {c0, c1};
560+ // zero stride on the broadcast axis marks the broadcast rendering path
561+ *brc.y.strides = {af::Symbol(0), af::Symbol(1)};
562+ *brc.y.vectorized_axis = {z0.id, z1.id};
563+ 
564+ store.x = brc.y;
565+ store.attr.sched.axis = {z0.id, z1.id};
566+ store.y.dtype = af::DT_FLOAT;
567+ *store.y.axis = {z0.id, z1.id};
568+ *store.y.repeats = {c0, c1};
569+ *store.y.strides = {c1, af::Symbol(1)};
570+ store.y.mem->alloc_type = af::AllocType::kAllocTypeBuffer;
571+ store.y.mem->hardware = af::MemHardware::kMemHardwareUB;
572+ store.y.mem->position = af::Position::kPositionVecCalc;
573+ store.y.buf->id = 4;
574+ 
575+ y.x = store.y;
576+ y.attr.sched.axis = {z0.id, z1.id};
577+ y.y.dtype = af::DT_FLOAT;
578+ *y.y.axis = {z0.id, z1.id};
579+ *y.y.repeats = {c0, c1};
580+ *y.y.strides = {c1, af::Symbol(1)};
581+ 
582+ const auto ctx = ::ascir::dumper::BuildDumpContext(graph);
583+ const auto text = ::ascir::dumper::DumpLoopExecutionView(graph, ctx);
584+ EXPECT_FALSE(text.empty());
585+ EXPECT_NE(text.find("store"), std::string::npos);
586+}
587+TEST_F(AscirMetaUtilsTest, DumpLoopExecutionView_FinalSweep) {
588+ af::AscGraph graph("sweep_VfSubgraph_0");
589+ // constant axes: stride/repeat const-value branches and axis-size rendering
590+ auto c0 = graph.CreateSizeVar(4);
591+ auto c1 = graph.CreateSizeVar(8);
592+ auto z0 = graph.CreateAxis("z0", c0);
593+ auto z1 = graph.CreateAxis("z1", c1);
594+ 
595+ af::ascir_op::Data x1("x1", graph);
596+ af::ascir_op::Workspace ws("ws");
597+ af::ascir_op::Load load1("load1");
598+ af::ascir_op::Broadcast brc("brc");
599+ af::ascir_op::Store store("store");
600+ af::ascir_op::Output y("y");
601+ 
602+ ws.attr.sched.axis = {z0.id, z1.id};
603+ ws.y.dtype = af::DT_FLOAT;
604+ *ws.y.axis = {z0.id, z1.id};
605+ *ws.y.repeats = {c0, c1};
606+ *ws.y.strides = {c1, af::Symbol(1)};
607+ 
608+ x1.attr.sched.axis = {z0.id, z1.id};
609+ x1.y.dtype = af::DT_FLOAT;
610+ *x1.y.axis = {z0.id, z1.id};
611+ *x1.y.repeats = {c0, c1};
612+ *x1.y.strides = {c1, af::Symbol(1)};
613+ 
614+ load1.x = x1.y;
615+ load1.attr.sched.axis = {z0.id, z1.id};
616+ load1.y.dtype = af::DT_FLOAT;
617+ *load1.y.axis = {z0.id, z1.id};
618+ *load1.y.repeats = {c0, c1};
619+ *load1.y.strides = {c1, af::Symbol(1)};
620+ *load1.y.vectorized_axis = {z0.id, z1.id};
621+ 
622+ brc.x = load1.y;
623+ brc.attr.sched.axis = {z0.id, z1.id};
624+ brc.y.dtype = af::DT_FLOAT;
625+ *brc.y.axis = {z0.id, z1.id};
626+ *brc.y.repeats = {c0, c1};
627+ // zero stride on the broadcast axis marks the broadcast rendering path
628+ *brc.y.strides = {af::Symbol(0), af::Symbol(1)};
629+ *brc.y.vectorized_axis = {z0.id, z1.id};
630+ 
631+ store.x = brc.y;
632+ store.attr.sched.axis = {z0.id, z1.id};
633+ store.y.dtype = af::DT_FLOAT;
634+ *store.y.axis = {z0.id, z1.id};
635+ *store.y.repeats = {c0, c1};
636+ *store.y.strides = {c1, af::Symbol(1)};
637+ store.y.mem->alloc_type = af::AllocType::kAllocTypeBuffer;
638+ store.y.mem->hardware = af::MemHardware::kMemHardwareUB;
639+ store.y.mem->position = af::Position::kPositionVecCalc;
640+ store.y.buf->id = 4;
641+ 
642+ y.x = store.y;
643+ y.attr.sched.axis = {z0.id, z1.id};
644+ y.y.dtype = af::DT_FLOAT;
645+ *y.y.axis = {z0.id, z1.id};
646+ *y.y.repeats = {c0, c1};
647+ *y.y.strides = {c1, af::Symbol(1)};
648+ 
649+ const auto ctx = ::ascir::dumper::BuildDumpContext(graph);
650+ const auto text = ::ascir::dumper::DumpLoopExecutionView(graph, ctx);
651+ EXPECT_FALSE(text.empty());
652+ EXPECT_NE(text.find("store"), std::string::npos);
653+}
654+ 
655+// Direct probes of axis-type helpers across every enum value (including out-of-range casts)
656+TEST_F(AscirMetaUtilsTest, AxisTypeHelpers_AllValues) {
657+ const std::vector<af::Axis::Type> types = {af::Axis::Type::kAxisTypeOriginal, af::Axis::Type::kAxisTypeBlockInner,
658+ af::Axis::Type::kAxisTypeBlockOuter, static_cast<af::Axis::Type>(99)};
659+ for (const auto t : types) {
660+ (void)::ascir::dumper::GetAxisTypePriority(t);
661+ (void)::ascir::dumper::GetAxisTypeSuffix(t);
662+ }
663+}
664+ 
665+// CollectInputNames on a node whose peers include an unconnected data node and a multi-output peer
666+TEST_F(AscirMetaUtilsTest, CollectInputNames_ShouldHandleUnconnectedAndIndexedPeers) {
667+ af::AscGraph graph("t_inputs");
668+ auto s0 = graph.CreateSizeVar("s0");
669+ auto s1 = graph.CreateSizeVar("s1");
670+ auto z0 = graph.CreateAxis("z0", s0);
671+ auto z1 = graph.CreateAxis("z1", s1);
672+ // an orphan data node stays unconnected: its consumer renders "nil"
673+ af::ascir_op::Data orphan("orphan", graph);
674+ orphan.attr.sched.axis = {z0.id, z1.id};
675+ orphan.y.dtype = af::DT_FLOAT;
676+ *orphan.y.axis = {z0.id, z1.id};
677+ *orphan.y.repeats = {s0, s1};
678+ *orphan.y.strides = {s1, af::Symbol(1)};
679+ 
680+ af::ascir_op::Data x1("x1", graph);
681+ af::ascir_op::Load load1("load1");
682+ af::ascir_op::ArgMaxMultiRPhase1 p1("p1");
683+ af::ascir_op::Store store("store");
684+ af::ascir_op::Output y("y");
685+ x1.attr.sched.axis = {z0.id, z1.id};
686+ x1.y.dtype = af::DT_FLOAT;
687+ *x1.y.axis = {z0.id, z1.id};
688+ *x1.y.repeats = {s0, s1};
689+ *x1.y.strides = {s1, af::Symbol(1)};
690+ load1.x = x1.y;
691+ load1.attr.sched.axis = {z0.id, z1.id};
692+ load1.y.dtype = af::DT_FLOAT;
693+ *load1.y.axis = {z0.id, z1.id};
694+ *load1.y.repeats = {s0, s1};
695+ *load1.y.strides = {s1, af::Symbol(1)};
696+ *load1.y.vectorized_axis = {z0.id, z1.id};
697+ p1.x = load1.y;
698+ p1.attr.sched.axis = {z0.id, z1.id};
699+ p1.value.dtype = af::DT_FLOAT;
700+ *p1.value.axis = {z0.id, z1.id};
701+ *p1.value.repeats = {s0, af::Symbol(1)};
702+ *p1.value.strides = {af::Symbol(1), af::Symbol(0)};
703+ store.x = p1.value;
704+ store.attr.sched.axis = {z0.id, z1.id};
705+ store.y.dtype = af::DT_FLOAT;
706+ *store.y.axis = {z0.id, z1.id};
707+ *store.y.repeats = {s0, af::Symbol(1)};
708+ *store.y.strides = {af::Symbol(1), af::Symbol(0)};
709+ y.x = store.y;
710+ y.attr.sched.axis = {z0.id, z1.id};
711+ y.y.dtype = af::DT_FLOAT;
712+ *y.y.axis = {z0.id, z1.id};
713+ *y.y.repeats = {s0, af::Symbol(1)};
714+ *y.y.strides = {af::Symbol(1), af::Symbol(0)};
715+ 
716+ const auto node = graph.FindNode("p1");
717+ ASSERT_NE(node, nullptr);
718+ const auto names = ::ascir::dumper::CollectInputNames(graph, node);
719+ EXPECT_FALSE(names.empty());
720+}
721+ 
722+// meta-95 push: dtype family expansion covers DtypeToStr int16/uint16/uint32/uint64 branches
723+TEST_F(AscirMetaUtilsTest, DebugImplGraphStr_ShouldRenderFullDtypeFamily) {
724+ const std::vector<af::DataType> dtypes = {af::DT_INT16, af::DT_UINT16, af::DT_UINT32, af::DT_UINT64, af::DT_UINT8};
725+ for (const auto dtype : dtypes) {
726+ af::AscGraph graph("t_dtypes");
727+ auto s0 = graph.CreateSizeVar("s0");
728+ auto s1 = graph.CreateSizeVar("s1");
729+ auto z0 = graph.CreateAxis("z0", s0);
730+ auto z1 = graph.CreateAxis("z1", s1);
731+ af::ascir_op::Data x1("x1", graph);
732+ af::ascir_op::Load load1("load1");
733+ af::ascir_op::Abs abs("abs");
734+ af::ascir_op::Store store("store");
735+ af::ascir_op::Output y("y");
736+ x1.attr.sched.axis = {z0.id, z1.id};
737+ x1.y.dtype = dtype;
738+ *x1.y.axis = {z0.id, z1.id};
739+ *x1.y.repeats = {s0, s1};
740+ *x1.y.strides = {s1, af::Symbol(1)};
741+ load1.x = x1.y;
742+ load1.attr.sched.axis = {z0.id, z1.id};
743+ load1.y.dtype = dtype;
744+ *load1.y.axis = {z0.id, z1.id};
745+ *load1.y.repeats = {s0, s1};
746+ *load1.y.strides = {s1, af::Symbol(1)};
747+ *load1.y.vectorized_axis = {z0.id, z1.id};
748+ abs.x = load1.y;
749+ abs.attr.sched.axis = {z0.id, z1.id};
750+ abs.y.dtype = dtype;
751+ *abs.y.axis = {z0.id, z1.id};
752+ *abs.y.repeats = {s0, s1};
753+ *abs.y.strides = {s1, af::Symbol(1)};
754+ *abs.y.vectorized_axis = {z0.id, z1.id};
755+ store.x = abs.y;
756+ store.attr.sched.axis = {z0.id, z1.id};
757+ store.y.dtype = dtype;
758+ *store.y.axis = {z0.id, z1.id};
759+ *store.y.repeats = {s0, s1};
760+ *store.y.strides = {s1, af::Symbol(1)};
761+ y.x = store.y;
762+ y.attr.sched.axis = {z0.id, z1.id};
763+ y.y.dtype = dtype;
764+ *y.y.axis = {z0.id, z1.id};
765+ *y.y.repeats = {s0, s1};
766+ *y.y.strides = {s1, af::Symbol(1)};
767+ const auto debug_text = ::ascir::utils::DebugImplGraphStr(graph);
768+ EXPECT_NE(debug_text.find(".api.unit"), std::string::npos);
769+ const auto ctx = ::ascir::dumper::BuildDumpContext(graph);
770+ const auto dump_text = ::ascir::dumper::DumpLoopExecutionView(graph, ctx);
771+ EXPECT_FALSE(dump_text.empty());
772+ }
773+}
774+ 
775+// meta-95 push: env parse variants (quoted/spaced dir), prefix caching and graph file dump
776+TEST_F(AscirMetaUtilsTest, DumpPrefixAndGraphFile_EnvVariants) {
777+ const std::string debug_root = "/tmp/ascir_ut_meta95_" + std::to_string(getpid());
778+ setenv("AUTOFUSE_DFX_FLAGS", ("--codegen_compile_debug=true;--debug_dir= \"" + debug_root + "/ \"").c_str(), 1);
779+ const auto prefix1 = ::ascir::utils::GetDumpFilePrefix();
780+ EXPECT_FALSE(prefix1.empty());
781+ const auto prefix2 = ::ascir::utils::GetDumpFilePrefix();
782+ EXPECT_EQ(prefix1, prefix2);
783+ 
784+ af::AscGraph graph("t_file_dump");
785+ auto s0 = graph.CreateSizeVar("s0");
786+ auto s1 = graph.CreateSizeVar("s1");
787+ auto z0 = graph.CreateAxis("z0", s0);
788+ auto z1 = graph.CreateAxis("z1", s1);
789+ af::ascir_op::Data x1("x1", graph);
790+ af::ascir_op::Load load1("load1");
791+ af::ascir_op::Abs abs("abs");
792+ af::ascir_op::Store store("store");
793+ af::ascir_op::Output y("y");
794+ x1.attr.sched.axis = {z0.id, z1.id};
795+ x1.y.dtype = af::DT_FLOAT;
796+ *x1.y.axis = {z0.id, z1.id};
797+ *x1.y.repeats = {s0, s1};
798+ *x1.y.strides = {s1, af::Symbol(1)};
799+ load1.x = x1.y;
800+ load1.attr.sched.axis = {z0.id, z1.id};
801+ load1.y.dtype = af::DT_FLOAT;
802+ *load1.y.axis = {z0.id, z1.id};
803+ *load1.y.repeats = {s0, s1};
804+ *load1.y.strides = {s1, af::Symbol(1)};
805+ *load1.y.vectorized_axis = {z0.id, z1.id};
806+ abs.x = load1.y;
807+ abs.attr.sched.axis = {z0.id, z1.id};
808+ abs.y.dtype = af::DT_FLOAT;
809+ *abs.y.axis = {z0.id, z1.id};
810+ *abs.y.repeats = {s0, s1};
811+ *abs.y.strides = {s1, af::Symbol(1)};
812+ *abs.y.vectorized_axis = {z0.id, z1.id};
813+ store.x = abs.y;
814+ store.attr.sched.axis = {z0.id, z1.id};
815+ store.y.dtype = af::DT_FLOAT;
816+ *store.y.axis = {z0.id, z1.id};
817+ *store.y.repeats = {s0, s1};
818+ *store.y.strides = {s1, af::Symbol(1)};
819+ y.x = store.y;
820+ y.attr.sched.axis = {z0.id, z1.id};
821+ y.y.dtype = af::DT_FLOAT;
822+ *y.y.axis = {z0.id, z1.id};
823+ *y.y.repeats = {s0, s1};
824+ *y.y.strides = {s1, af::Symbol(1)};
825+ ::ascir::utils::DumpGraph(graph, "meta95", 0U, true);
826+ unsetenv("AUTOFUSE_DFX_FLAGS");
827+}
828+ 
829+// meta-95 push: constant size vars render the CONST branch in verbose graph dump
830+TEST_F(AscirMetaUtilsTest, DebugImplGraphStr_ShouldRenderConstantSizeVars) {
831+ af::AscGraph graph("t_const_size");
832+ auto c0 = graph.CreateSizeVar(16);
833+ auto c1 = graph.CreateSizeVar(32);
834+ auto z0 = graph.CreateAxis("z0", c0);
835+ auto z1 = graph.CreateAxis("z1", c1);
836+ af::ascir_op::Data x1("x1", graph);
837+ af::ascir_op::Load load1("load1");
838+ af::ascir_op::Abs abs("abs");
839+ af::ascir_op::Store store("store");
840+ af::ascir_op::Output y("y");
841+ x1.attr.sched.axis = {z0.id, z1.id};
842+ x1.y.dtype = af::DT_FLOAT;
843+ *x1.y.axis = {z0.id, z1.id};
844+ *x1.y.repeats = {c0, c1};
845+ *x1.y.strides = {c1, af::Symbol(1)};
846+ load1.x = x1.y;
847+ load1.attr.sched.axis = {z0.id, z1.id};
848+ load1.y.dtype = af::DT_FLOAT;
849+ *load1.y.axis = {z0.id, z1.id};
850+ *load1.y.repeats = {c0, c1};
851+ *load1.y.strides = {c1, af::Symbol(1)};
852+ *load1.y.vectorized_axis = {z0.id, z1.id};
853+ abs.x = load1.y;
854+ abs.attr.sched.axis = {z0.id, z1.id};
855+ abs.y.dtype = af::DT_FLOAT;
856+ *abs.y.axis = {z0.id, z1.id};
857+ *abs.y.repeats = {c0, c1};
858+ *abs.y.strides = {c1, af::Symbol(1)};
859+ *abs.y.vectorized_axis = {z0.id, z1.id};
860+ store.x = abs.y;
861+ store.attr.sched.axis = {z0.id, z1.id};
862+ store.y.dtype = af::DT_FLOAT;
863+ *store.y.axis = {z0.id, z1.id};
864+ *store.y.repeats = {c0, c1};
865+ *store.y.strides = {c1, af::Symbol(1)};
866+ y.x = store.y;
867+ y.attr.sched.axis = {z0.id, z1.id};
868+ y.y.dtype = af::DT_FLOAT;
869+ *y.y.axis = {z0.id, z1.id};
870+ *y.y.repeats = {c0, c1};
871+ *y.y.strides = {c1, af::Symbol(1)};
872+ const auto text = ::ascir::utils::DebugImplGraphStr(graph);
873+ EXPECT_NE(text.find("CONST"), std::string::npos);
874+}
875+ 
876+// meta-95 push: an unconnected second input renders "nil"; consuming a secondary output of a
877+// multi-output peer renders the indexed form
878+TEST_F(AscirMetaUtilsTest, NodeInputRendering_NilAndIndexedPeer) {
879+ af::AscGraph graph("t_nil");
880+ auto s0 = graph.CreateSizeVar("s0");
881+ auto s1 = graph.CreateSizeVar("s1");
882+ auto z0 = graph.CreateAxis("z0", s0);
883+ auto z1 = graph.CreateAxis("z1", s1);
884+ af::ascir_op::Data x1("x1", graph);
885+ af::ascir_op::Load load1("load1");
886+ af::ascir_op::Mul mul("mul");
887+ af::ascir_op::Store store("store");
888+ af::ascir_op::Output y("y");
889+ x1.attr.sched.axis = {z0.id, z1.id};
890+ x1.y.dtype = af::DT_FLOAT;
891+ *x1.y.axis = {z0.id, z1.id};
892+ *x1.y.repeats = {s0, s1};
893+ *x1.y.strides = {s1, af::Symbol(1)};
894+ load1.x = x1.y;
895+ load1.attr.sched.axis = {z0.id, z1.id};
896+ load1.y.dtype = af::DT_FLOAT;
897+ *load1.y.axis = {z0.id, z1.id};
898+ *load1.y.repeats = {s0, s1};
899+ *load1.y.strides = {s1, af::Symbol(1)};
900+ *load1.y.vectorized_axis = {z0.id, z1.id};
901+ mul.x1 = load1.y;
902+ mul.attr.sched.axis = {z0.id, z1.id};
903+ mul.y.dtype = af::DT_FLOAT;
904+ *mul.y.axis = {z0.id, z1.id};
905+ *mul.y.repeats = {s0, s1};
906+ *mul.y.strides = {s1, af::Symbol(1)};
907+ *mul.y.vectorized_axis = {z0.id, z1.id};
908+ store.x = mul.y;
909+ store.attr.sched.axis = {z0.id, z1.id};
910+ store.y.dtype = af::DT_FLOAT;
911+ *store.y.axis = {z0.id, z1.id};
912+ *store.y.repeats = {s0, s1};
913+ *store.y.strides = {s1, af::Symbol(1)};
914+ y.x = store.y;
915+ y.attr.sched.axis = {z0.id, z1.id};
916+ y.y.dtype = af::DT_FLOAT;
917+ *y.y.axis = {z0.id, z1.id};
918+ *y.y.repeats = {s0, s1};
919+ *y.y.strides = {s1, af::Symbol(1)};
920+ const auto node = graph.FindNode("mul");
921+ ASSERT_NE(node, nullptr);
922+ const auto names = ::ascir::dumper::CollectInputNames(graph, node);
923+ EXPECT_FALSE(names.empty());
924+ const auto ctx = ::ascir::dumper::BuildDumpContext(graph);
925+ const auto text = ::ascir::dumper::DumpLoopExecutionView(graph, ctx);
926+ EXPECT_FALSE(text.empty());
927+}
928+ 
929+// meta-95 push: subgraph mode with a valued scalar, a workspace node and exec conditions
930+TEST_F(AscirMetaUtilsTest, DumpLoopExecutionView_SubgraphScalarWorkspace) {
931+ af::AscGraph graph("meta95_VfSubgraph_0");
932+ auto s0 = graph.CreateSizeVar("s0");
933+ auto s1 = graph.CreateSizeVar("s1");
934+ auto z0 = graph.CreateAxis("z0", s0);
935+ auto z1 = graph.CreateAxis("z1", s1);
936+ af::ascir_op::Scalar sc("sc");
937+ sc.ir_attr.SetValue("2.5");
938+ af::ascir_op::Workspace ws("ws");
939+ af::ascir_op::Data x1("x1", graph);
940+ af::ascir_op::Load load1("load1");
941+ af::ascir_op::Mul mul("mul");
942+ af::ascir_op::Store store("store");
943+ af::ascir_op::Output y("y");
944+ sc.attr.sched.axis = {z0.id, z1.id};
945+ sc.attr.sched.exec_condition = af::ExecuteCondition::kCacheBlockSplitFusedBroadcastAxis;
946+ sc.y.dtype = af::DT_FLOAT;
947+ *sc.y.axis = {};
948+ *sc.y.repeats = {};
949+ *sc.y.strides = {};
950+ ws.attr.sched.axis = {z0.id, z1.id};
951+ ws.y.dtype = af::DT_FLOAT;
952+ *ws.y.axis = {z0.id, z1.id};
953+ *ws.y.repeats = {s0, s1};
954+ *ws.y.strides = {s1, af::Symbol(1)};
955+ x1.attr.sched.axis = {z0.id, z1.id};
956+ x1.y.dtype = af::DT_FLOAT;
957+ *x1.y.axis = {z0.id, z1.id};
958+ *x1.y.repeats = {s0, s1};
959+ *x1.y.strides = {s1, af::Symbol(1)};
960+ load1.x = x1.y;
961+ load1.attr.sched.axis = {z0.id, z1.id};
962+ load1.y.dtype = af::DT_FLOAT;
963+ *load1.y.axis = {z0.id, z1.id};
964+ *load1.y.repeats = {s0, s1};
965+ *load1.y.strides = {s1, af::Symbol(1)};
966+ *load1.y.vectorized_axis = {z0.id, z1.id};
967+ mul.x1 = load1.y;
968+ mul.x2 = sc.y;
969+ mul.attr.sched.axis = {z0.id, z1.id};
970+ mul.y.dtype = af::DT_FLOAT;
971+ *mul.y.axis = {z0.id, z1.id};
972+ *mul.y.repeats = {s0, s1};
973+ *mul.y.strides = {s1, af::Symbol(1)};
974+ *mul.y.vectorized_axis = {z0.id, z1.id};
975+ store.x = mul.y;
976+ store.attr.sched.axis = {z0.id, z1.id};
977+ store.y.dtype = af::DT_FLOAT;
978+ *store.y.axis = {z0.id, z1.id};
979+ *store.y.repeats = {s0, s1};
980+ *store.y.strides = {s1, af::Symbol(1)};
981+ y.x = store.y;
982+ y.attr.sched.axis = {z0.id, z1.id};
983+ y.y.dtype = af::DT_FLOAT;
984+ *y.y.axis = {z0.id, z1.id};
985+ *y.y.repeats = {s0, s1};
986+ *y.y.strides = {s1, af::Symbol(1)};
987+ const auto ctx = ::ascir::dumper::BuildDumpContext(graph);
988+ const auto text = ::ascir::dumper::DumpLoopExecutionView(graph, ctx);
989+ EXPECT_FALSE(text.empty());
990+}
991+ 
80} // namespace ascir992} // namespace ascir
81} // namespace af993} // namespace af