Merged
fix(tile_with_axis): Tiling溢出/CeilDiv回绕与标量UB越界加固,补动态shape范围推导及golden #6647
tan_xinAdd on 19 days ago
fix(tile_with_axis): Tiling溢出/CeilDiv回绕与标量UB越界加固,补动态shape范围推导及golden #6647
Merged
tan_xinAdd on 19 days ago
Total: 15 files changed+680-292
@@ -4,12 +4,12 @@
4 4 
5| 产品 | 是否支持 |5| 产品 | 是否支持 |
6| :----------------------------------------------------------- | :------: |6| :----------------------------------------------------------- | :------: |
7-| Ascend 950PR&950DT系列产品 | √ |7+| <term>Ascend 950PR&950DT系列产品</term> | √ |
8-| Atlas A3系列产品 | √ |8+| <term>Atlas A3系列产品</term> | √ |
9-| Atlas A2系列产品 | √ |9+| <term>Atlas A2系列产品</term> | √ |
10-| Atlas 200I/500 A2推理产品 | √ |10+| <term>Atlas 200I/500 A2推理产品</term> | √ |
11-| Atlas推理系列产品 | √ |11+| <term>Atlas推理系列产品</term> | √ |
12-| Atlas训练系列产品 | √ |12+| <term>Atlas训练系列产品</term> | √ |
13 13 
14## 功能说明14## 功能说明
15 15 
@@ -27,10 +27,10 @@
27 27 
28| 参数名 | 输入/输出/属性 | 描述 | 数据类型 | 数据格式 |28| 参数名 | 输入/输出/属性 | 描述 | 数据类型 | 数据格式 |
29|--------|---------------|------|----------|----------|29|--------|---------------|------|----------|----------|
30-| x | 输入 | 输入张量 | FLOAT16、FLOAT、INT8、INT16、INT32、INT64、UINT8、UINT16、UINT32、UINT64 | ND |30+| x | 输入 | 输入张量。 | BFLOAT16、FLOAT16、FLOAT、INT32、INT8、UINT8 | ND |
31-| axis | 属性(可选) | 指定复制的维度,默认为1 | INT | - |31+| y | 输出 | 输出张量,与x具有相同的数据类型和格式。 | 与x一致 | ND |
32-| tiles | 属性(必选) | 复制次数,必须大于0 | INT | - |32+| axis | 可选属性 | 指定复制的维度,默认为1。 | INT | - |
33-| y | 输出 | 输出张量,与x具有相同的数据类型和格式 | 与x一致 | ND |33+| tiles | 属性 | 复制次数,必须大于0。 | INT | - |
34 34 
35## 约束说明35## 约束说明
36 36 
@@ -38,9 +38,11 @@
38- tiles 必须大于0。38- tiles 必须大于0。
39- 输出Tensor在 axis 维度的大小必须等于输入Tensor在 axis 维度的大小乘以 tiles。39- 输出Tensor在 axis 维度的大小必须等于输入Tensor在 axis 维度的大小乘以 tiles。
40- 输入Tensor维度不超过8维。40- 输入Tensor维度不超过8维。
41+- 支持标量输入(rank=0):输出 shape 为 [tiles],即输入标量值复制 tiles 份;此时 axis 不生效。
42+- 输入Tensor任一维允许为 0(空 Tensor):输出对应维仍为 0,输出Tensor为空。
41 43 
42## 调用说明44## 调用说明
43 45 
44| 调用方式 | 样例代码 | 说明 |46| 调用方式 | 样例代码 | 说明 |
45|------------|----------|------|47|------------|----------|------|
46-| 图模式调用 | [test_geir_tile_with_axis](./examples/test_geir_tile_with_axis.cpp) | 通过[算子IR](./op_graph/tile_with_axis_proto.h)构图方式调用TileWithAxis算子。 |48+| GE图模式 | [test_geir_tile_with_axis](./examples/test_geir_tile_with_axis.cpp) | 通过[算子IR](./op_graph/tile_with_axis_proto.h)构图方式调用TileWithAxis算子。 |
@@ -184,7 +184,7 @@ int32_t WriteDataToFile(string bin_file, uint64_t data_size, uint8_t* inputData)
184 return SUCCESS;184 return SUCCESS;
185}185}
186 186 
187-void ProcessInputData(vector<Tensor>& input)187+int ProcessInputData(vector<Tensor>& input)
188{188{
189 for (size_t i = 0; i < input.size(); i++) {189 for (size_t i = 0; i < input.size(); i++) {
190 string input_file = "./tc_ge_irrun_test_0008_npu_input_" + std::to_string(i) + ".bin";190 string input_file = "./tc_ge_irrun_test_0008_npu_input_" + std::to_string(i) + ".bin";
@@ -193,14 +193,18 @@ void ProcessInputData(vector<Tensor>& input)
193 uint32_t type_size = GetDataTypeSize(input[i].GetTensorDesc().GetDataType());193 uint32_t type_size = GetDataTypeSize(input[i].GetTensorDesc().GetDataType());
194 if (type_size == 0U) {194 if (type_size == 0U) {
195 printf("ERROR: input %zu has unsupported dtype\n", i);195 printf("ERROR: input %zu has unsupported dtype\n", i);
196- continue;196+ return FAILED;
197 }197 }
198 uint32_t data_size = static_cast<uint32_t>(input_shape * type_size);198 uint32_t data_size = static_cast<uint32_t>(input_shape * type_size);
199- WriteDataToFile(input_file.c_str(), data_size, input_data_i);199+ if (WriteDataToFile(input_file.c_str(), data_size, input_data_i) != SUCCESS) {
200+ printf("ERROR: write input bin %s failed\n", input_file.c_str());
201+ return FAILED;
202+ }
200 }203 }
204+ return SUCCESS;
201}205}
202 206 
203-void ProcessOutputData(vector<Tensor>& output)207+int ProcessOutputData(vector<Tensor>& output)
204{208{
205 for (size_t i = 0; i < output.size(); i++) {209 for (size_t i = 0; i < output.size(); i++) {
206 string output_file = "./tc_ge_irrun_test_0008_npu_output_" + std::to_string(i) + ".bin";210 string output_file = "./tc_ge_irrun_test_0008_npu_output_" + std::to_string(i) + ".bin";
@@ -209,11 +213,15 @@ void ProcessOutputData(vector<Tensor>& output)
209 uint32_t type_size = GetDataTypeSize(output[i].GetTensorDesc().GetDataType());213 uint32_t type_size = GetDataTypeSize(output[i].GetTensorDesc().GetDataType());
210 if (type_size == 0U) {214 if (type_size == 0U) {
211 printf("ERROR: output %zu has unsupported dtype\n", i);215 printf("ERROR: output %zu has unsupported dtype\n", i);
212- continue;216+ return FAILED;
213 }217 }
214 uint32_t data_size = static_cast<uint32_t>(output_shape * type_size);218 uint32_t data_size = static_cast<uint32_t>(output_shape * type_size);
215- WriteDataToFile(output_file.c_str(), data_size, output_data_i);219+ if (WriteDataToFile(output_file.c_str(), data_size, output_data_i) != SUCCESS) {
220+ printf("ERROR: write output bin %s failed\n", output_file.c_str());
221+ return FAILED;
222+ }
216 }223 }
224+ return SUCCESS;
217}225}
218 226 
219int CreateOppInGraph(DataType inDtype, vector<Tensor>& input, vector<Operator>& inputs, vector<Operator>& outputs,227int CreateOppInGraph(DataType inDtype, vector<Tensor>& input, vector<Operator>& inputs, vector<Operator>& outputs,
@@ -280,6 +288,7 @@ int main(int argc, char* argv[])
280 ret = CreateOppInGraph(inDtype, input, inputs, outputs, graph);288 ret = CreateOppInGraph(inDtype, input, inputs, outputs, graph);
281 if (ret != SUCCESS) {289 if (ret != SUCCESS) {
282 printf("%s - ERROR - [XIR]: Create graph failed\n", GetTime().c_str());290 printf("%s - ERROR - [XIR]: Create graph failed\n", GetTime().c_str());
291+ GEFinalize();
283 return FAILED;292 return FAILED;
284 }293 }
285 294 
@@ -291,6 +300,7 @@ int main(int argc, char* argv[])
291 ge::Session* session = new Session(build_options);300 ge::Session* session = new Session(build_options);
292 if (session == nullptr) {301 if (session == nullptr) {
293 printf("%s - ERROR - [XIR]: Create session failed\n", GetTime().c_str());302 printf("%s - ERROR - [XIR]: Create session failed\n", GetTime().c_str());
303+ GEFinalize();
294 return FAILED;304 return FAILED;
295 }305 }
296 306 
@@ -314,8 +324,18 @@ int main(int argc, char* argv[])
314 }324 }
315 printf("%s - INFO - [XIR]: Run graph success\n", GetTime().c_str());325 printf("%s - INFO - [XIR]: Run graph success\n", GetTime().c_str());
316 326 
317- ProcessInputData(input);327+ if (ProcessInputData(input) != SUCCESS) {
318- ProcessOutputData(output);328+ printf("%s - ERROR - [XIR]: Process input data failed\n", GetTime().c_str());
329+ delete session;
330+ GEFinalize();
331+ return FAILED;
332+ }
333+ if (ProcessOutputData(output) != SUCCESS) {
334+ printf("%s - ERROR - [XIR]: Process output data failed\n", GetTime().c_str());
335+ delete session;
336+ GEFinalize();
337+ return FAILED;
338+ }
319 339 
320 delete session;340 delete session;
321 ret = ge::GEFinalize();341 ret = ge::GEFinalize();
@@ -25,7 +25,7 @@ namespace ge {
25 25 
26* @par Inputs:26* @par Inputs:
27* One input:27* One input:
28-* x: A Tensor with any format. Must be one of the following types:28+* x: A Tensor with ND format. Must be one of the following types:
29* bfloat16, float16, float32, int8, int16, int32, int64, uint8, uint16, uint32, uint64 . \n29* bfloat16, float16, float32, int8, int16, int32, int64, uint8, uint16, uint32, uint64 . \n
30 30 
31* @par Attributes:31* @par Attributes:
@@ -46,6 +46,22 @@ static constexpr int32_t TILING_KEY_ELEMENT_COUNT = 2;
46// ============================================================================46// ============================================================================
47// Helper: Get platform info (ubSize, coreNum)47// Helper: Get platform info (ubSize, coreNum)
48// ============================================================================48// ============================================================================
49+// ============================================================================
50+// Helper: guard an int64 multiplication against signed overflow
51+// ============================================================================
52+static ge::graphStatus CheckMulNoOverflow(gert::TilingContext* context, int64_t a, int64_t b, const char* product_name)
53+{
54+ if (a < 0 || b < 0) {
55+ OP_LOGE(context, "%s operand is negative: %ld * %ld", product_name, a, b);
56+ return ge::GRAPH_FAILED;
57+ }
58+ if (a != 0 && b != 0 && a > INT64_MAX / b) {
59+ OP_LOGE(context, "%s overflows int64: %ld * %ld", product_name, a, b);
60+ return ge::GRAPH_FAILED;
61+ }
62+ return ge::GRAPH_SUCCESS;
63+}
64+ 
49static ge::graphStatus GetPlatformInfo(gert::TilingContext* context, uint64_t* ubSize, int64_t* coreNum)65static ge::graphStatus GetPlatformInfo(gert::TilingContext* context, uint64_t* ubSize, int64_t* coreNum)
50{66{
51 fe::PlatFormInfos* platformInfoPtr = context->GetPlatformInfo();67 fe::PlatFormInfos* platformInfoPtr = context->GetPlatformInfo();
@@ -82,7 +98,8 @@ static void LogTilingData(gert::TilingContext* context, const TileWithAxisTiling
82 tiling->bufferSize, tiling->tiles, tiling->rowLength);98 tiling->bufferSize, tiling->tiles, tiling->rowLength);
83}99}
84 100 
85-static ge::graphStatus HandleScalarInput(gert::TilingContext* context, int64_t tiles, uint64_t ubSize)101+static ge::graphStatus HandleScalarInput(gert::TilingContext* context, int64_t tiles, uint64_t ubSize,
102+ int64_t dtypeSize, int64_t coreNum)
86{103{
87 TileWithAxisTilingData* tiling = context->GetTilingData<TileWithAxisTilingData>();104 TileWithAxisTilingData* tiling = context->GetTilingData<TileWithAxisTilingData>();
88 OP_CHECK_NULL_WITH_CONTEXT(context, tiling);105 OP_CHECK_NULL_WITH_CONTEXT(context, tiling);
@@ -94,16 +111,38 @@ static ge::graphStatus HandleScalarInput(gert::TilingContext* context, int64_t t
94 tiling->outShape[0] = 1;111 tiling->outShape[0] = 1;
95 tiling->outShape[1] = tiles;112 tiling->outShape[1] = tiles;
96 tiling->outShape[2] = 1;113 tiling->outShape[2] = 1;
97- tiling->totalCount = 1;
98- tiling->perCoreCount = 1;
99- tiling->ubAxis = 0;
100- tiling->ubFactor = 1;
101 tiling->bufferSize = ubSize / 2;114 tiling->bufferSize = ubSize / 2;
102 tiling->tiles = tiles;115 tiling->tiles = tiles;
103 tiling->rowLength = 1;116 tiling->rowLength = 1;
104- LogTilingData(context, tiling);117+ 
105- context->SetBlockDim(1);118+ // 标量输出共 tiles 个元素;单缓冲容量 bufferSize/dtypeSize 个元素。
106- ASCENDC_TPL_SEL_PARAM(context, static_cast<uint32_t>(0));119+ // tiles 超过单缓冲容量时沿 tiles 维分块(ubAxis=1: NDDMA dim1 源 stride=0 广播读,见 kernel
120+ // InitNddmaParams),每块不超过单缓冲,消除原实现的 UB 越界写。
121+ int64_t bufferSizeElements = static_cast<int64_t>(tiling->bufferSize) / dtypeSize;
122+ OP_CHECK_IF(bufferSizeElements <= 0,
123+ OP_LOGE(context, "UB half buffer %u bytes too small for dtype size %ld", tiling->bufferSize, dtypeSize),
124+ return ge::GRAPH_FAILED);
125+ if (tiles <= bufferSizeElements) {
126+ tiling->totalCount = 1;
127+ tiling->perCoreCount = 1;
128+ tiling->ubAxis = 0;
129+ tiling->ubFactor = 1;
130+ LogTilingData(context, tiling);
131+ context->SetBlockDim(1);
132+ ASCENDC_TPL_SEL_PARAM(context, static_cast<uint32_t>(0));
133+ } else {
134+ uint32_t ubFactor = static_cast<uint32_t>(bufferSizeElements);
135+ uint64_t totalCount = static_cast<uint64_t>(Ops::Base::CeilDiv(tiles, static_cast<int64_t>(ubFactor)));
136+ uint64_t perCoreCount = static_cast<uint64_t>(Ops::Base::CeilDiv(static_cast<int64_t>(totalCount), coreNum));
137+ int64_t realCoreNum = Ops::Base::CeilDiv(static_cast<int64_t>(totalCount), static_cast<int64_t>(perCoreCount));
138+ tiling->totalCount = totalCount;
139+ tiling->perCoreCount = perCoreCount;
140+ tiling->ubAxis = 1;
141+ tiling->ubFactor = ubFactor;
142+ LogTilingData(context, tiling);
143+ context->SetBlockDim(realCoreNum);
144+ ASCENDC_TPL_SEL_PARAM(context, static_cast<uint32_t>(1));
145+ }
107 OP_CHECK_IF(GetWorkspaceSize(context) != ge::GRAPH_SUCCESS, OP_LOGE(context, "GetWorkspaceSize error"),146 OP_CHECK_IF(GetWorkspaceSize(context) != ge::GRAPH_SUCCESS, OP_LOGE(context, "GetWorkspaceSize error"),
108 return ge::GRAPH_FAILED);147 return ge::GRAPH_FAILED);
109 return ge::GRAPH_SUCCESS;148 return ge::GRAPH_SUCCESS;
@@ -131,9 +170,12 @@ static int64_t MaxBlocksOnAxis(uint8_t ax, int64_t outerDim, int64_t tiles, int6
131{170{
132 if (ax == 0)171 if (ax == 0)
133 return outerDim;172 return outerDim;
134- if (ax == 1)173+ if (ax == 1) {
135- return outerDim * tiles;174+ // 仅 rowLength>0 时可达(此时 outerDim*tiles 已被入口总乘积检查约束)
136- return outerDim * tiles * rowLength;175+ return (rowLength == 0) ? 0 : outerDim * tiles;
176+ }
177+ // ax == 2
178+ return (rowLength == 0) ? 0 : outerDim * tiles * rowLength;
137}179}
138 180 
139// ============================================================================181// ============================================================================
@@ -148,6 +190,10 @@ static uint32_t ComputeUbFactor(uint8_t ax, int64_t target, int64_t outerDim, in
148 int64_t bpo = CeilDiv(target, outerDim);190 int64_t bpo = CeilDiv(target, outerDim);
149 return static_cast<uint32_t>(std::max(static_cast<int64_t>(1), CeilDiv(tiles, bpo)));191 return static_cast<uint32_t>(std::max(static_cast<int64_t>(1), CeilDiv(tiles, bpo)));
150 }192 }
193+ // ax == 2;rowLength==0 为空张量,无需按块计算 ubFactor
194+ if (rowLength == 0) {
195+ return 1;
196+ }
151 int64_t bpot = CeilDiv(target, outerDim * tiles);197 int64_t bpot = CeilDiv(target, outerDim * tiles);
152 return static_cast<uint32_t>(std::max(static_cast<int64_t>(1), CeilDiv(rowLength, bpot)));198 return static_cast<uint32_t>(std::max(static_cast<int64_t>(1), CeilDiv(rowLength, bpot)));
153}199}
@@ -161,7 +207,8 @@ static uint64_t ComputeTotalCount(uint8_t ax, int64_t uf, int64_t outerDim, int6
161 return static_cast<uint64_t>(CeilDiv(outerDim, uf));207 return static_cast<uint64_t>(CeilDiv(outerDim, uf));
162 if (ax == 1)208 if (ax == 1)
163 return static_cast<uint64_t>(outerDim * CeilDiv(tiles, uf));209 return static_cast<uint64_t>(outerDim * CeilDiv(tiles, uf));
164- return static_cast<uint64_t>(outerDim * tiles * CeilDiv(rowLength, uf));210+ // ax == 2;rowLength==0 为空张量,块数为 0
211+ return (rowLength == 0) ? 0U : static_cast<uint64_t>(outerDim * tiles * CeilDiv(rowLength, uf));
165}212}
166 213 
167// ============================================================================214// ============================================================================
@@ -182,7 +229,8 @@ static void SelectInitialUbAxis(int64_t ubFactor0, int64_t ubFactor1, int64_t ub
182 } else {229 } else {
183 ubAxis = 2;230 ubAxis = 2;
184 ubFactor = static_cast<uint32_t>(std::max(ubFactor2, static_cast<int64_t>(1)));231 ubFactor = static_cast<uint32_t>(std::max(ubFactor2, static_cast<int64_t>(1)));
185- totalCount = static_cast<uint64_t>(outerDim * tiles * CeilDiv(rowLength, ubFactor2));232+ // rowLength==0 为空张量,块数为 0
233+ totalCount = (rowLength == 0) ? 0U : static_cast<uint64_t>(outerDim * tiles * CeilDiv(rowLength, ubFactor2));
186 }234 }
187}235}
188 236 
@@ -274,8 +322,11 @@ static ge::graphStatus TileWithAxisTilingFunc(gert::TilingContext* context)
274 OP_CHECK_NULL_WITH_CONTEXT(context, attrs);322 OP_CHECK_NULL_WITH_CONTEXT(context, attrs);
275 const int64_t* axis_ptr = attrs->GetAttrPointer<int64_t>(ATTR_AXIS_IDX);323 const int64_t* axis_ptr = attrs->GetAttrPointer<int64_t>(ATTR_AXIS_IDX);
276 const int64_t* tiles_ptr = attrs->GetAttrPointer<int64_t>(ATTR_TILES_IDX);324 const int64_t* tiles_ptr = attrs->GetAttrPointer<int64_t>(ATTR_TILES_IDX);
277- int64_t axis = (axis_ptr != nullptr) ? *axis_ptr : 0;325+ // tiles 为必选属性(proto REQUIRED_ATTR / def REQUIRED / README):缺失时拒绝,不静默回退
278- int64_t tiles = (tiles_ptr != nullptr) ? *tiles_ptr : 1;326+ OP_CHECK_IF(tiles_ptr == nullptr, OP_LOGE(context, "tiles is required but missing"), return ge::GRAPH_FAILED);
327+ int64_t tiles = *tiles_ptr;
328+ // axis 默认 1(与 TBE/AICPU 缺省一致);属性缺失时兜底 1
329+ int64_t axis = (axis_ptr != nullptr) ? *axis_ptr : 1;
279 330 
280 OP_CHECK_IF(tiles <= 0, OP_LOGE(context, "tiles must be > 0, got %ld", tiles), return ge::GRAPH_FAILED);331 OP_CHECK_IF(tiles <= 0, OP_LOGE(context, "tiles must be > 0, got %ld", tiles), return ge::GRAPH_FAILED);
281 332 
@@ -283,7 +334,11 @@ static ge::graphStatus TileWithAxisTilingFunc(gert::TilingContext* context)
283 334 
284 // 3. 标量输入特殊处理335 // 3. 标量输入特殊处理
285 if (rank == 0) {336 if (rank == 0) {
286- return HandleScalarInput(context, tiles, ubSize);337+ int64_t scalarDtypeSize = static_cast<int64_t>(ge::GetSizeByDataType(dataType));
338+ OP_CHECK_IF(scalarDtypeSize <= 0,
339+ OP_LOGE(context, "unsupported dtype, GetSizeByDataType returned %ld", scalarDtypeSize),
340+ return ge::GRAPH_FAILED);
341+ return HandleScalarInput(context, tiles, ubSize, scalarDtypeSize, coreNum);
287 }342 }
288 343 
289 // 4. 校验并归一化 axis344 // 4. 校验并归一化 axis
@@ -293,13 +348,21 @@ static ge::graphStatus TileWithAxisTilingFunc(gert::TilingContext* context)
293 // 5. 计算展平参数 (v1.6 折叠 axisDim 进 rowLength)348 // 5. 计算展平参数 (v1.6 折叠 axisDim 进 rowLength)
294 int64_t outerDim = 1;349 int64_t outerDim = 1;
295 for (int64_t i = 0; i < axis; i++) {350 for (int64_t i = 0; i < axis; i++) {
296- outerDim *= inputShape.GetDim(i);351+ int64_t dim = inputShape.GetDim(i);
352+ OP_CHECK_IF(CheckMulNoOverflow(context, outerDim, dim, "outerDim") != ge::GRAPH_SUCCESS,
353+ OP_LOGE(context, "outerDim overflow"), return ge::GRAPH_FAILED);
354+ outerDim *= dim;
297 }355 }
298 int64_t axisDim = inputShape.GetDim(axis);356 int64_t axisDim = inputShape.GetDim(axis);
299 int64_t innerDim = 1;357 int64_t innerDim = 1;
300 for (int64_t i = axis + 1; i < rank; i++) {358 for (int64_t i = axis + 1; i < rank; i++) {
301- innerDim *= inputShape.GetDim(i);359+ int64_t dim = inputShape.GetDim(i);
360+ OP_CHECK_IF(CheckMulNoOverflow(context, innerDim, dim, "innerDim") != ge::GRAPH_SUCCESS,
361+ OP_LOGE(context, "innerDim overflow"), return ge::GRAPH_FAILED);
362+ innerDim *= dim;
302 }363 }
364+ OP_CHECK_IF(CheckMulNoOverflow(context, axisDim, innerDim, "rowLength") != ge::GRAPH_SUCCESS,
365+ OP_LOGE(context, "rowLength overflow"), return ge::GRAPH_FAILED);
303 int64_t rowLength = axisDim * innerDim;366 int64_t rowLength = axisDim * innerDim;
304 367 
305 // 6. TilingData 空间分配368 // 6. TilingData 空间分配
@@ -313,7 +376,13 @@ static ge::graphStatus TileWithAxisTilingFunc(gert::TilingContext* context)
313 OP_CHECK_IF(dtypeSize <= 0, OP_LOGE(context, "unsupported dtype, GetSizeByDataType returned %ld", dtypeSize),376 OP_CHECK_IF(dtypeSize <= 0, OP_LOGE(context, "unsupported dtype, GetSizeByDataType returned %ld", dtypeSize),
314 return ge::GRAPH_FAILED);377 return ge::GRAPH_FAILED);
315 int64_t bufferSizeElements = (static_cast<int64_t>(ubSize) / 2) / dtypeSize;378 int64_t bufferSizeElements = (static_cast<int64_t>(ubSize) / 2) / dtypeSize;
379+ OP_CHECK_IF(CheckMulNoOverflow(context, tiles, rowLength, "rowOutElements") != ge::GRAPH_SUCCESS,
380+ OP_LOGE(context, "rowOutElements overflow"), return ge::GRAPH_FAILED);
316 int64_t rowOutElements = tiles * rowLength;381 int64_t rowOutElements = tiles * rowLength;
382+ // 总输出元素数 outerDim*tiles*rowLength:下游 AdjustForCoreUtilization 的 totalOutElements、
383+ // MaxBlocksOnAxis 与 ComputeTotalCount 均为这三个因子的子集/缩放,验证后不再溢出。
384+ OP_CHECK_IF(CheckMulNoOverflow(context, outerDim, rowOutElements, "totalOutElements") != ge::GRAPH_SUCCESS,
385+ OP_LOGE(context, "totalOutElements overflow"), return ge::GRAPH_FAILED);
317 386 
318 int64_t ubFactor0 = (rowOutElements > 0) ? std::min(outerDim, bufferSizeElements / rowOutElements) : 0;387 int64_t ubFactor0 = (rowOutElements > 0) ? std::min(outerDim, bufferSizeElements / rowOutElements) : 0;
319 int64_t ubFactor1 = (rowLength > 0) ? std::min(tiles, bufferSizeElements / rowLength) : 0;388 int64_t ubFactor1 = (rowLength > 0) ? std::min(tiles, bufferSizeElements / rowLength) : 0;
@@ -328,9 +397,13 @@ static ge::graphStatus TileWithAxisTilingFunc(gert::TilingContext* context)
328 AdjustForCoreUtilization(ubAxis, ubFactor, totalCount, ubFactor0, ubFactor1, ubFactor2, outerDim, tiles, rowLength,397 AdjustForCoreUtilization(ubAxis, ubFactor, totalCount, ubFactor0, ubFactor1, ubFactor2, outerDim, tiles, rowLength,
329 coreNum);398 coreNum);
330 399 
331- // 8. 多核切分400+ // 8. 多核切分(空张量兜底:totalCount==0 时 SetBlockDim(1),kernel 空循环,见 REQUIREMENTS §3.5.2)
332- uint64_t perCoreCount = static_cast<uint64_t>(CeilDiv(static_cast<int64_t>(totalCount), coreNum));401+ uint64_t perCoreCount = 1;
333- int64_t realCoreNum = CeilDiv(static_cast<int64_t>(totalCount), static_cast<int64_t>(perCoreCount));402+ int64_t realCoreNum = 1;
403+ if (totalCount > 0) {
404+ perCoreCount = static_cast<uint64_t>(CeilDiv(static_cast<int64_t>(totalCount), coreNum));
405+ realCoreNum = CeilDiv(static_cast<int64_t>(totalCount), static_cast<int64_t>(perCoreCount));
406+ }
334 407 
335 // 9. 填充 TilingData408 // 9. 填充 TilingData
336 tiling->inShape[0] = outerDim;409 tiling->inShape[0] = outerDim;
@@ -12,8 +12,7 @@
12 "paramType": "required",12 "paramType": "required",
13 "shape": [13 "shape": [
14 -214 -2
15- ],15+ ]
16- "dtype_match_mode": "DtypeByte"
17 }16 }
18 ],17 ],
19 "outputs": [18 "outputs": [
@@ -25,8 +24,7 @@
25 "paramType": "required",24 "paramType": "required",
26 "shape": [25 "shape": [
27 -226 -2
28- ],27+ ]
29- "dtype_match_mode": "DtypeByte"
30 }28 }
31 ],29 ],
32 "attrs": [30 "attrs": [
@@ -53,8 +51,7 @@
53 "paramType": "required",51 "paramType": "required",
54 "shape": [52 "shape": [
55 -253 -2
56- ],54+ ]
57- "dtype_match_mode": "DtypeByte"
58 }55 }
59 ],56 ],
60 "outputs": [57 "outputs": [
@@ -66,8 +63,7 @@
66 "paramType": "required",63 "paramType": "required",
67 "shape": [64 "shape": [
68 -265 -2
69- ],66+ ]
70- "dtype_match_mode": "DtypeByte"
71 }67 }
72 ],68 ],
73 "attrs": [69 "attrs": [
@@ -94,8 +90,7 @@
94 "paramType": "required",90 "paramType": "required",
95 "shape": [91 "shape": [
96 -292 -2
97- ],93+ ]
98- "dtype_match_mode": "DtypeByte"
99 }94 }
100 ],95 ],
101 "outputs": [96 "outputs": [
@@ -107,8 +102,7 @@
107 "paramType": "required",102 "paramType": "required",
108 "shape": [103 "shape": [
109 -2104 -2
110- ],105+ ]
111- "dtype_match_mode": "DtypeByte"
112 }106 }
113 ],107 ],
114 "attrs": [108 "attrs": [
@@ -135,8 +129,7 @@
135 "paramType": "required",129 "paramType": "required",
136 "shape": [130 "shape": [
137 -2131 -2
138- ],132+ ]
139- "dtype_match_mode": "DtypeByte"
140 }133 }
141 ],134 ],
142 "outputs": [135 "outputs": [
@@ -148,8 +141,7 @@
148 "paramType": "required",141 "paramType": "required",
149 "shape": [142 "shape": [
150 -2143 -2
151- ],144+ ]
152- "dtype_match_mode": "DtypeByte"
153 }145 }
154 ],146 ],
155 "attrs": [147 "attrs": [
@@ -176,8 +168,7 @@
176 "paramType": "required",168 "paramType": "required",
177 "shape": [169 "shape": [
178 -2170 -2
179- ],171+ ]
180- "dtype_match_mode": "DtypeByte"
181 }172 }
182 ],173 ],
183 "outputs": [174 "outputs": [
@@ -189,8 +180,7 @@
189 "paramType": "required",180 "paramType": "required",
190 "shape": [181 "shape": [
191 -2182 -2
192- ],183+ ]
193- "dtype_match_mode": "DtypeByte"
194 }184 }
195 ],185 ],
196 "attrs": [186 "attrs": [
@@ -217,8 +207,7 @@
217 "paramType": "required",207 "paramType": "required",
218 "shape": [208 "shape": [
219 -2209 -2
220- ],210+ ]
221- "dtype_match_mode": "DtypeByte"
222 }211 }
223 ],212 ],
224 "outputs": [213 "outputs": [
@@ -230,8 +219,7 @@
230 "paramType": "required",219 "paramType": "required",
231 "shape": [220 "shape": [
232 -2221 -2
233- ],222+ ]
234- "dtype_match_mode": "DtypeByte"
235 }223 }
236 ],224 ],
237 "attrs": [225 "attrs": [
@@ -12,8 +12,7 @@
12 "paramType": "required",12 "paramType": "required",
13 "shape": [13 "shape": [
14 -214 -2
15- ],15+ ]
16- "dtype_match_mode": "DtypeByte"
17 }16 }
18 ],17 ],
19 "outputs": [18 "outputs": [
@@ -25,8 +24,7 @@
25 "paramType": "required",24 "paramType": "required",
26 "shape": [25 "shape": [
27 -226 -2
28- ],27+ ]
29- "dtype_match_mode": "DtypeByte"
30 }28 }
31 ],29 ],
32 "attrs": [30 "attrs": [
@@ -53,8 +51,7 @@
53 "paramType": "required",51 "paramType": "required",
54 "shape": [52 "shape": [
55 -253 -2
56- ],54+ ]
57- "dtype_match_mode": "DtypeByte"
58 }55 }
59 ],56 ],
60 "outputs": [57 "outputs": [
@@ -66,8 +63,7 @@
66 "paramType": "required",63 "paramType": "required",
67 "shape": [64 "shape": [
68 -265 -2
69- ],66+ ]
70- "dtype_match_mode": "DtypeByte"
71 }67 }
72 ],68 ],
73 "attrs": [69 "attrs": [
@@ -94,8 +90,7 @@
94 "paramType": "required",90 "paramType": "required",
95 "shape": [91 "shape": [
96 -292 -2
97- ],93+ ]
98- "dtype_match_mode": "DtypeByte"
99 }94 }
100 ],95 ],
101 "outputs": [96 "outputs": [
@@ -107,8 +102,7 @@
107 "paramType": "required",102 "paramType": "required",
108 "shape": [103 "shape": [
109 -2104 -2
110- ],105+ ]
111- "dtype_match_mode": "DtypeByte"
112 }106 }
113 ],107 ],
114 "attrs": [108 "attrs": [
@@ -135,8 +129,7 @@
135 "paramType": "required",129 "paramType": "required",
136 "shape": [130 "shape": [
137 -2131 -2
138- ],132+ ]
139- "dtype_match_mode": "DtypeByte"
140 }133 }
141 ],134 ],
142 "outputs": [135 "outputs": [
@@ -148,8 +141,7 @@
148 "paramType": "required",141 "paramType": "required",
149 "shape": [142 "shape": [
150 -2143 -2
151- ],144+ ]
152- "dtype_match_mode": "DtypeByte"
153 }145 }
154 ],146 ],
155 "attrs": [147 "attrs": [
@@ -176,8 +168,7 @@
176 "paramType": "required",168 "paramType": "required",
177 "shape": [169 "shape": [
178 -2170 -2
179- ],171+ ]
180- "dtype_match_mode": "DtypeByte"
181 }172 }
182 ],173 ],
183 "outputs": [174 "outputs": [
@@ -189,8 +180,7 @@
189 "paramType": "required",180 "paramType": "required",
190 "shape": [181 "shape": [
191 -2182 -2
192- ],183+ ]
193- "dtype_match_mode": "DtypeByte"
194 }184 }
195 ],185 ],
196 "attrs": [186 "attrs": [
@@ -217,8 +207,7 @@
217 "paramType": "required",207 "paramType": "required",
218 "shape": [208 "shape": [
219 -2209 -2
220- ],210+ ]
221- "dtype_match_mode": "DtypeByte"
222 }211 }
223 ],212 ],
224 "outputs": [213 "outputs": [
@@ -230,8 +219,7 @@
230 "paramType": "required",219 "paramType": "required",
231 "shape": [220 "shape": [
232 -2221 -2
233- ],222+ ]
234- "dtype_match_mode": "DtypeByte"
235 }223 }
236 ],224 ],
237 "attrs": [225 "attrs": [
@@ -37,11 +37,11 @@ public:
37 {ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})37 {ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND, ge::FORMAT_ND})
38 .AutoContiguous();38 .AutoContiguous();
39 39 
40- // 属性: axis (int64_t) -- 复制维度40+ // 属性: axis (int64_t) -- 复制维度(缺省按实现默认 1 处理,与 TBE/AICPU 一致)
41- this->Attr("axis").AttrType(OPTIONAL).Int();41+ this->Attr("axis").AttrType(OPTIONAL).Int(1);
42 42 
43- // 属性: tiles (int64_t) -- 复制次数43+ // 属性: tiles (int64_t) -- 复制次数(必选,与 proto REQUIRED_ATTR / README / AICPU def 一致)
44- this->Attr("tiles").AttrType(OPTIONAL).Int();44+ this->Attr("tiles").AttrType(REQUIRED).Int();
45 45 
46 // Ascend950 (DAV_3510) 配置46 // Ascend950 (DAV_3510) 配置
47 OpAICoreConfig aicoreConfig950;47 OpAICoreConfig aicoreConfig950;
@@ -14,18 +14,44 @@
14 *14 *
15 * 设计依据: DESIGN.md v1.615 * 设计依据: DESIGN.md v1.6
16 * 输出 y 的 shape: y.shape[axis] = x.shape[axis] * tiles, 其余维度与 x 相同16 * 输出 y 的 shape: y.shape[axis] = x.shape[axis] * tiles, 其余维度与 x 相同
17- * rank=0 时 y.shape = [tiles]17+ * rank=0 时 y.shape = [tiles](人工裁决:支持标量输入)
18+ * 动态 shape: axis 维为未知维标记(-1/-2)时原样传播,不参与乘法
18 */19 */
19 20 
20#include "register/op_impl_registry.h"21#include "register/op_impl_registry.h"
21#include "exe_graph/runtime/infer_shape_context.h"22#include "exe_graph/runtime/infer_shape_context.h"
23+#include "exe_graph/runtime/infer_shape_range_context.h"
22#include "op_common/log/log.h"24#include "op_common/log/log.h"
23#include "op_host/util/shape_util.h"25#include "op_host/util/shape_util.h"
24 26 
27+#include <cstdint>
28+ 
25using namespace ge;29using namespace ge;
26 30 
27namespace ops {31namespace ops {
28 32 
33+namespace {
34+constexpr int64_t ATTR_AXIS_IDX = 0;
35+constexpr int64_t ATTR_TILES_IDX = 1;
36+// 仓库约定(对齐 circular_pad_infershape.cpp):-1 为未知维标记;
37+// -2 为未知 rank 标记,known-rank shape 中出现时同样原样传播(对齐 canndev legacy 基线)。
38+constexpr int64_t UNKNOWN_DIM = -1;
39+constexpr int64_t UNKNOWN_RANK_DIM = -2;
40+ 
41+// axis 维输出值:未知维标记原样传播,其余乘 tiles 并做 int64 溢出检查
42+ge::graphStatus ScaleAxisDim(int64_t xDim, int64_t tiles, int64_t& yDim, const char* node_name)
43+{
44+ if (xDim == UNKNOWN_DIM || xDim == UNKNOWN_RANK_DIM) {
45+ yDim = xDim;
46+ return ge::GRAPH_SUCCESS;
47+ }
48+ OP_CHECK_IF(xDim > 0 && xDim > INT64_MAX / tiles,
49+ OP_LOGE(node_name, "axis dim %ld * tiles %ld overflows int64", xDim, tiles), return ge::GRAPH_FAILED);
50+ yDim = xDim * tiles;
51+ return ge::GRAPH_SUCCESS;
52+}
53+} // namespace
54+ 
29static ge::graphStatus InferShape4TileWithAxis(gert::InferShapeContext* context)55static ge::graphStatus InferShape4TileWithAxis(gert::InferShapeContext* context)
30{56{
31 const gert::Shape* input_shape = context->GetInputShape(0);57 const gert::Shape* input_shape = context->GetInputShape(0);
@@ -35,13 +61,14 @@ static ge::graphStatus InferShape4TileWithAxis(gert::InferShapeContext* context)
35 OP_CHECK_NULL_WITH_CONTEXT(context, output_shape);61 OP_CHECK_NULL_WITH_CONTEXT(context, output_shape);
36 62 
37 // 获取属性 axis 和 tiles63 // 获取属性 axis 和 tiles
38- constexpr size_t ATTR_AXIS_IDX = 0;
39- constexpr size_t ATTR_TILES_IDX = 1;
40 auto attrs = context->GetAttrs();64 auto attrs = context->GetAttrs();
41 const int64_t* axis_ptr = attrs->GetAttrPointer<int64_t>(ATTR_AXIS_IDX);65 const int64_t* axis_ptr = attrs->GetAttrPointer<int64_t>(ATTR_AXIS_IDX);
42 const int64_t* tiles_ptr = attrs->GetAttrPointer<int64_t>(ATTR_TILES_IDX);66 const int64_t* tiles_ptr = attrs->GetAttrPointer<int64_t>(ATTR_TILES_IDX);
43- int64_t axis = (axis_ptr != nullptr) ? *axis_ptr : 0;67+ // tiles 为必选属性(proto REQUIRED_ATTR / def REQUIRED / README):缺失时拒绝,不静默回退
44- int64_t tiles = (tiles_ptr != nullptr) ? *tiles_ptr : 1;68+ OP_CHECK_IF(tiles_ptr == nullptr, OP_LOGE(context, "tiles is required but missing"), return ge::GRAPH_FAILED);
69+ int64_t tiles = *tiles_ptr;
70+ // axis 默认 1(与 TBE/AICPU 缺省一致);属性缺失时兜底 1
71+ int64_t axis = (axis_ptr != nullptr) ? *axis_ptr : 1;
45 72 
46 OP_CHECK_IF(tiles <= 0, OP_LOGE(context, "tiles must be > 0, got %ld", tiles), return ge::GRAPH_FAILED);73 OP_CHECK_IF(tiles <= 0, OP_LOGE(context, "tiles must be > 0, got %ld", tiles), return ge::GRAPH_FAILED);
47 74 
@@ -71,11 +98,14 @@ static ge::graphStatus InferShape4TileWithAxis(gert::InferShapeContext* context)
71 98 
72 OP_CHECK_IF(axis < 0 || axis >= rank, OP_LOGE(context, "axis is out of range"), return ge::GRAPH_FAILED);99 OP_CHECK_IF(axis < 0 || axis >= rank, OP_LOGE(context, "axis is out of range"), return ge::GRAPH_FAILED);
73 100 
74- // 复制 input shape 到 output shape101+ // 复制 input shape 到 output shape(axis 维: 未知维传播 + 溢出检查)
75 output_shape->SetDimNum(rank);102 output_shape->SetDimNum(rank);
76 for (int64_t i = 0; i < rank; i++) {103 for (int64_t i = 0; i < rank; i++) {
77 if (i == axis) {104 if (i == axis) {
78- output_shape->SetDim(i, input_shape->GetDim(i) * tiles);105+ int64_t yDim = 0;
106+ OP_CHECK_IF(ScaleAxisDim(input_shape->GetDim(i), tiles, yDim, context->GetNodeName()) != ge::GRAPH_SUCCESS,
107+ OP_LOGE(context, "scale axis dim failed"), return ge::GRAPH_FAILED);
108+ output_shape->SetDim(i, yDim);
79 } else {109 } else {
80 output_shape->SetDim(i, input_shape->GetDim(i));110 output_shape->SetDim(i, input_shape->GetDim(i));
81 }111 }
@@ -85,12 +115,65 @@ static ge::graphStatus InferShape4TileWithAxis(gert::InferShapeContext* context)
85 return ge::GRAPH_SUCCESS;115 return ge::GRAPH_SUCCESS;
86}116}
87 117 
118+// 动态 shape 的 range 推导:输出 range = 输入 range,axis 维乘 tiles(未知维标记原样传播)
119+static ge::graphStatus InferShapeRange4TileWithAxis(gert::InferShapeRangeContext* context)
120+{
121+ auto attrs = context->GetAttrs();
122+ OP_CHECK_NULL_WITH_CONTEXT(context, attrs);
123+ const int64_t* axis_ptr = attrs->GetAttrPointer<int64_t>(ATTR_AXIS_IDX);
124+ const int64_t* tiles_ptr = attrs->GetAttrPointer<int64_t>(ATTR_TILES_IDX);
125+ OP_CHECK_IF(tiles_ptr == nullptr || *tiles_ptr <= 0, OP_LOGE(context, "tiles is required and must be > 0"),
126+ return ge::GRAPH_FAILED);
127+ const int64_t tiles = *tiles_ptr;
128+ int64_t axis = (axis_ptr != nullptr) ? *axis_ptr : 1;
129+ 
130+ const auto* xRange = context->GetInputShapeRange(0);
131+ OP_CHECK_NULL_WITH_CONTEXT(context, xRange);
132+ auto* xRangeMin = xRange->GetMin();
133+ auto* xRangeMax = xRange->GetMax();
134+ OP_CHECK_NULL_WITH_CONTEXT(context, xRangeMin);
135+ OP_CHECK_NULL_WITH_CONTEXT(context, xRangeMax);
136+ 
137+ auto* yRange = context->GetOutputShapeRange(0);
138+ OP_CHECK_NULL_WITH_CONTEXT(context, yRange);
139+ auto* yRangeMin = yRange->GetMin();
140+ auto* yRangeMax = yRange->GetMax();
141+ OP_CHECK_NULL_WITH_CONTEXT(context, yRangeMin);
142+ OP_CHECK_NULL_WITH_CONTEXT(context, yRangeMax);
143+ 
144+ for (auto* yBound : {yRangeMin, yRangeMax}) {
145+ const auto* xBound = (yBound == yRangeMin) ? xRangeMin : xRangeMax;
146+ *yBound = *xBound;
147+ int64_t boundRank = static_cast<int64_t>(xBound->GetDimNum());
148+ if (boundRank == 0) {
149+ // 标量: 输出 range = [tiles, tiles]
150+ yBound->SetDimNum(1);
151+ yBound->SetDim(0, tiles);
152+ continue;
153+ }
154+ int64_t boundAxis = axis;
155+ if (boundAxis < 0) {
156+ boundAxis += boundRank;
157+ }
158+ OP_CHECK_IF(boundAxis < 0 || boundAxis >= boundRank, OP_LOGE(context, "axis is out of range"),
159+ return ge::GRAPH_FAILED);
160+ int64_t yDim = 0;
161+ OP_CHECK_IF(ScaleAxisDim(xBound->GetDim(boundAxis), tiles, yDim, context->GetNodeName()) != ge::GRAPH_SUCCESS,
162+ OP_LOGE(context, "scale axis range dim failed"), return ge::GRAPH_FAILED);
163+ yBound->SetDim(boundAxis, yDim);
164+ }
165+ return ge::GRAPH_SUCCESS;
166+}
167+ 
88static ge::graphStatus InferDataType4TileWithAxis(gert::InferDataTypeContext* context)168static ge::graphStatus InferDataType4TileWithAxis(gert::InferDataTypeContext* context)
89{169{
90 context->SetOutputDataType(0, context->GetInputDataType(0));170 context->SetOutputDataType(0, context->GetInputDataType(0));
91 return ge::GRAPH_SUCCESS;171 return ge::GRAPH_SUCCESS;
92}172}
93 173 
94-IMPL_OP_INFERSHAPE(TileWithAxis).InferShape(InferShape4TileWithAxis).InferDataType(InferDataType4TileWithAxis);174+IMPL_OP_INFERSHAPE(TileWithAxis)
175+ .InferShape(InferShape4TileWithAxis)
176+ .InferDataType(InferDataType4TileWithAxis)
177+ .InferShapeRange(InferShapeRange4TileWithAxis);
95 178 
96} // namespace ops179} // namespace ops
@@ -42,26 +42,29 @@
42 42 
43using namespace AscendC;43using namespace AscendC;
44 44 
45-// CeilDiv: ceil(a / b) for positive integers45+// CeilDiv: ceil(a / b) for positive integers, without a+b-1 overflow
46template <typename IntT>46template <typename IntT>
47-__aicore__ inline IntT CeilDiv(IntT a, IntT b) { return (a + b - 1) / b; }47+__aicore__ inline IntT CeilDiv(IntT a, IntT b)
48+{
49+ return a / b + (a % b != 0);
50+}
48 51 
49// NDDMA 维度: 3 维 (ubAxis=0 用满 3 维; ubAxis=1 退化为 2 维; ubAxis=2 不使用)52// NDDMA 维度: 3 维 (ubAxis=0 用满 3 维; ubAxis=1 退化为 2 维; ubAxis=2 不使用)
50static constexpr uint8_t NDDMA_DIM = 3;53static constexpr uint8_t NDDMA_DIM = 3;
51static constexpr int AXIS_COUNT = 3;54static constexpr int AXIS_COUNT = 3;
52 55 
53struct AxisLayout {56struct AxisLayout {
54- int64_t inStart; // 输入起始坐标57+ int64_t inStart; // 输入起始坐标
55- int64_t outStart; // 输出起始坐标58+ int64_t outStart; // 输出起始坐标
56- int64_t length; // 搬运长度59+ int64_t length; // 搬运长度
57- int64_t inStride; // 输入 stride (InitLayout 一次性计算)60+ int64_t inStride; // 输入 stride (InitLayout 一次性计算)
58- int64_t outStride; // 输出 stride (InitLayout 一次性计算)61+ int64_t outStride; // 输出 stride (InitLayout 一次性计算)
59};62};
60 63 
61struct TileLayout {64struct TileLayout {
62 AxisLayout axes[AXIS_COUNT];65 AxisLayout axes[AXIS_COUNT];
63- int64_t inOffset; // = SUM(inStart[axis] * inStride[axis])66+ int64_t inOffset; // = SUM(inStart[axis] * inStride[axis])
64- int64_t outOffset; // = SUM(outStart[axis] * outStride[axis])67+ int64_t outOffset; // = SUM(outStart[axis] * outStride[axis])
65};68};
66 69 
67template <typename T, int UB_AXIS>70template <typename T, int UB_AXIS>
@@ -70,8 +73,7 @@ public:
70 // ================================================================73 // ================================================================
71 // Init: 一次性完成 stride 计算、buffer 申请、NDDMA 参数预计算74 // Init: 一次性完成 stride 计算、buffer 申请、NDDMA 参数预计算
72 // ================================================================75 // ================================================================
73- __aicore__ inline void Init(GM_ADDR x, GM_ADDR y,76+ __aicore__ inline void Init(GM_ADDR x, GM_ADDR y, const TileWithAxisTilingData* tilingData)
74- const TileWithAxisTilingData* tilingData)
75 {77 {
76 td_ = tilingData;78 td_ = tilingData;
77 79 
@@ -83,9 +85,9 @@ public:
83 InitLayout();85 InitLayout();
84 InitNddmaParams();86 InitNddmaParams();
85 87 
86- uint64_t blockIdx = GetBlockIdx();88+ uint64_t blockIdx = GetBlockIdx();
87 startBlock_ = blockIdx * td_->perCoreCount;89 startBlock_ = blockIdx * td_->perCoreCount;
88- endBlock_ = min(startBlock_ + td_->perCoreCount, td_->totalCount);90+ endBlock_ = min(startBlock_ + td_->perCoreCount, td_->totalCount);
89 }91 }
90 92 
91 // ================================================================93 // ================================================================
@@ -120,13 +122,13 @@ private:
120 uint64_t endBlock_;122 uint64_t endBlock_;
121 123 
122 // 预计算常量124 // 预计算常量
123- int64_t rowInLength_; // = axisDim * innerDim = rowLength125+ int64_t rowInLength_; // = axisDim * innerDim = rowLength
124- int64_t rowOutLength_; // = tiles * rowLength126+ int64_t rowOutLength_; // = tiles * rowLength
125 127 
126 // NDDMA 预计算参数 (仅 ubAxis=0/1 使用)128 // NDDMA 预计算参数 (仅 ubAxis=0/1 使用)
127 AscendC::NdDmaLoopInfo<NDDMA_DIM> nddmaLoopInfo_;129 AscendC::NdDmaLoopInfo<NDDMA_DIM> nddmaLoopInfo_;
128- int64_t nddmaBlockLen_; // NDDMA 每次连续搬运的元素数130+ int64_t nddmaBlockLen_; // NDDMA 每次连续搬运的元素数
129- int64_t nddmaRepeat_; // NDDMA dim1 重复次数131+ int64_t nddmaRepeat_; // NDDMA dim1 重复次数
130 132 
131 // ================================================================133 // ================================================================
132 // InitLayout: 基于 3D 模型 inShape=[outerDim,1,rowLength] outShape=[outerDim,tiles,rowLength]134 // InitLayout: 基于 3D 模型 inShape=[outerDim,1,rowLength] outShape=[outerDim,tiles,rowLength]
@@ -134,16 +136,16 @@ private:
134 __aicore__ inline void InitLayout()136 __aicore__ inline void InitLayout()
135 {137 {
136 // 输入 stride: inShape = [outerDim, 1, rowLength]138 // 输入 stride: inShape = [outerDim, 1, rowLength]
137- layout_.axes[2].inStride = 1; // rowLength 维 stride=1139+ layout_.axes[2].inStride = 1; // rowLength 维 stride=1
138- layout_.axes[1].inStride = td_->inShape[2]; // 1 维 stride = rowLength140+ layout_.axes[1].inStride = td_->inShape[2]; // 1 维 stride = rowLength
139- layout_.axes[0].inStride = td_->inShape[1] * td_->inShape[2]; // outer 维 stride = 1 * rowLength141+ layout_.axes[0].inStride = td_->inShape[1] * td_->inShape[2]; // outer 维 stride = 1 * rowLength
140 142 
141 // 输出 stride: outShape = [outerDim, tiles, rowLength]143 // 输出 stride: outShape = [outerDim, tiles, rowLength]
142- layout_.axes[2].outStride = 1; // rowLength 维 stride=1144+ layout_.axes[2].outStride = 1; // rowLength 维 stride=1
143- layout_.axes[1].outStride = td_->outShape[2]; // tiles 维 stride = rowLength145+ layout_.axes[1].outStride = td_->outShape[2]; // tiles 维 stride = rowLength
144- layout_.axes[0].outStride = td_->outShape[1] * td_->outShape[2]; // outer 维 stride = tiles * rowLength146+ layout_.axes[0].outStride = td_->outShape[1] * td_->outShape[2]; // outer 维 stride = tiles * rowLength
145 147 
146- rowInLength_ = td_->rowLength;148+ rowInLength_ = td_->rowLength;
147 rowOutLength_ = td_->tiles * td_->rowLength;149 rowOutLength_ = td_->tiles * td_->rowLength;
148 }150 }
149 151 
@@ -156,11 +158,11 @@ private:
156 __aicore__ inline void InitNddmaParams()158 __aicore__ inline void InitNddmaParams()
157 {159 {
158 if constexpr (UB_AXIS > 1) {160 if constexpr (UB_AXIS > 1) {
159- return; // UB_AXIS=2 不使用 NDDMA161+ return; // UB_AXIS=2 不使用 NDDMA
160 }162 }
161 // ---- 公共部分 ----163 // ---- 公共部分 ----
162 nddmaBlockLen_ = rowInLength_;164 nddmaBlockLen_ = rowInLength_;
163- nddmaRepeat_ = td_->ubFactor; // UB_AXIS=1 默认值165+ nddmaRepeat_ = td_->ubFactor; // UB_AXIS=1 默认值
164 nddmaLoopInfo_.loopSrcStride[0] = 1;166 nddmaLoopInfo_.loopSrcStride[0] = 1;
165 nddmaLoopInfo_.loopSrcStride[1] = 0;167 nddmaLoopInfo_.loopSrcStride[1] = 0;
166 nddmaLoopInfo_.loopSrcStride[2] = 0;168 nddmaLoopInfo_.loopSrcStride[2] = 0;
@@ -170,14 +172,18 @@ private:
170 nddmaLoopInfo_.loopSize[0] = nddmaBlockLen_;172 nddmaLoopInfo_.loopSize[0] = nddmaBlockLen_;
171 nddmaLoopInfo_.loopSize[1] = nddmaRepeat_;173 nddmaLoopInfo_.loopSize[1] = nddmaRepeat_;
172 nddmaLoopInfo_.loopSize[2] = 1;174 nddmaLoopInfo_.loopSize[2] = 1;
173- nddmaLoopInfo_.loopLpSize[0] = 0; nddmaLoopInfo_.loopLpSize[1] = 0; nddmaLoopInfo_.loopLpSize[2] = 0;175+ nddmaLoopInfo_.loopLpSize[0] = 0;
174- nddmaLoopInfo_.loopRpSize[0] = 0; nddmaLoopInfo_.loopRpSize[1] = 0; nddmaLoopInfo_.loopRpSize[2] = 0;176+ nddmaLoopInfo_.loopLpSize[1] = 0;
177+ nddmaLoopInfo_.loopLpSize[2] = 0;
178+ nddmaLoopInfo_.loopRpSize[0] = 0;
179+ nddmaLoopInfo_.loopRpSize[1] = 0;
180+ nddmaLoopInfo_.loopRpSize[2] = 0;
175 // ---- UB_AXIS=0 覆盖 dim2 参数(注意 loopSize[1] 需同步 nddmaRepeat_)----181 // ---- UB_AXIS=0 覆盖 dim2 参数(注意 loopSize[1] 需同步 nddmaRepeat_)----
176 if constexpr (UB_AXIS == 0) {182 if constexpr (UB_AXIS == 0) {
177- nddmaRepeat_ = td_->tiles;183+ nddmaRepeat_ = td_->tiles;
178 nddmaLoopInfo_.loopSrcStride[2] = nddmaBlockLen_;184 nddmaLoopInfo_.loopSrcStride[2] = nddmaBlockLen_;
179 nddmaLoopInfo_.loopDstStride[2] = nddmaBlockLen_ * nddmaRepeat_;185 nddmaLoopInfo_.loopDstStride[2] = nddmaBlockLen_ * nddmaRepeat_;
180- nddmaLoopInfo_.loopSize[1] = nddmaRepeat_; // 同步为 tiles186+ nddmaLoopInfo_.loopSize[1] = nddmaRepeat_; // 同步为 tiles
181 nddmaLoopInfo_.loopSize[2] = td_->ubFactor;187 nddmaLoopInfo_.loopSize[2] = td_->ubFactor;
182 }188 }
183 }189 }
@@ -200,90 +206,78 @@ private:
200 __aicore__ inline void UpdateLayoutAxis0(uint64_t blockIdx)206 __aicore__ inline void UpdateLayoutAxis0(uint64_t blockIdx)
201 {207 {
202 int64_t outerStart = static_cast<int64_t>(td_->ubFactor) * static_cast<int64_t>(blockIdx);208 int64_t outerStart = static_cast<int64_t>(td_->ubFactor) * static_cast<int64_t>(blockIdx);
203- int64_t outerCount = min(209+ int64_t outerCount = min(static_cast<int64_t>(td_->ubFactor), td_->outShape[0] - outerStart);
204- static_cast<int64_t>(td_->ubFactor),
205- td_->outShape[0] - outerStart);
206 210 
207- layout_.axes[0].inStart = outerStart;211+ layout_.axes[0].inStart = outerStart;
208 layout_.axes[0].outStart = outerStart;212 layout_.axes[0].outStart = outerStart;
209- layout_.axes[0].length = outerCount;213+ layout_.axes[0].length = outerCount;
210- layout_.axes[1].length = td_->outShape[1]; // tiles214+ layout_.axes[1].length = td_->outShape[1]; // tiles
211- layout_.axes[2].length = td_->outShape[2]; // rowLength215+ layout_.axes[2].length = td_->outShape[2]; // rowLength
212 216 
213- layout_.inOffset = outerStart * layout_.axes[0].inStride;217+ layout_.inOffset = outerStart * layout_.axes[0].inStride;
214- layout_.outOffset = outerStart * layout_.axes[0].outStride;218+ layout_.outOffset = outerStart * layout_.axes[0].outStride;
215 219 
216- ubLen_ = outerCount * rowOutLength_;220+ ubLen_ = outerCount * rowOutLength_;
217- outGmBase_ = layout_.outOffset;221+ outGmBase_ = layout_.outOffset;
218- inGmBase_ = layout_.inOffset;222+ inGmBase_ = layout_.inOffset;
219 }223 }
220 224 
221 // ---- 切 tiles (outShape=[outerDim,tiles,rowLength]) ----225 // ---- 切 tiles (outShape=[outerDim,tiles,rowLength]) ----
222 __aicore__ inline void UpdateLayoutAxis1(uint64_t blockIdx)226 __aicore__ inline void UpdateLayoutAxis1(uint64_t blockIdx)
223 {227 {
224- int64_t tileBlocksPerOuter = CeilDiv(228+ int64_t tileBlocksPerOuter = CeilDiv(td_->outShape[1], static_cast<int64_t>(td_->ubFactor));
225- td_->outShape[1], static_cast<int64_t>(td_->ubFactor));229+ int64_t outerIdx = static_cast<int64_t>(blockIdx) / tileBlocksPerOuter;
226- int64_t outerIdx = static_cast<int64_t>(blockIdx) / tileBlocksPerOuter;230+ int64_t tileStart = static_cast<int64_t>(td_->ubFactor) * (static_cast<int64_t>(blockIdx) % tileBlocksPerOuter);
227- int64_t tileStart = static_cast<int64_t>(td_->ubFactor)231+ int64_t tileCount = min(static_cast<int64_t>(td_->ubFactor), td_->outShape[1] - tileStart);
228- * (static_cast<int64_t>(blockIdx) % tileBlocksPerOuter);
229- int64_t tileCount = min(
230- static_cast<int64_t>(td_->ubFactor),
231- td_->outShape[1] - tileStart);
232 232 
233- layout_.axes[0].inStart = outerIdx;233+ layout_.axes[0].inStart = outerIdx;
234 layout_.axes[0].outStart = outerIdx;234 layout_.axes[0].outStart = outerIdx;
235- layout_.axes[0].length = 1;235+ layout_.axes[0].length = 1;
236 layout_.axes[1].outStart = tileStart;236 layout_.axes[1].outStart = tileStart;
237- layout_.axes[1].length = tileCount;237+ layout_.axes[1].length = tileCount;
238- layout_.axes[2].length = td_->outShape[2]; // rowLength238+ layout_.axes[2].length = td_->outShape[2]; // rowLength
239 239 
240- layout_.outOffset = outerIdx * layout_.axes[0].outStride240+ layout_.outOffset = outerIdx * layout_.axes[0].outStride + tileStart * layout_.axes[1].outStride;
241- + tileStart * layout_.axes[1].outStride;
242 241 
243 // 输入: 中间维恒为 1, tile 维 = 0242 // 输入: 中间维恒为 1, tile 维 = 0
244 layout_.inOffset = outerIdx * layout_.axes[0].inStride;243 layout_.inOffset = outerIdx * layout_.axes[0].inStride;
245 244 
246- ubLen_ = tileCount * td_->outShape[2];245+ ubLen_ = tileCount * td_->outShape[2];
247- outGmBase_ = layout_.outOffset;246+ outGmBase_ = layout_.outOffset;
248- inGmBase_ = layout_.inOffset;247+ inGmBase_ = layout_.inOffset;
249 }248 }
250 249 
251 // ---- 切 rowLength (outShape=[outerDim,tiles,rowLength])250 // ---- 切 rowLength (outShape=[outerDim,tiles,rowLength])
252 // 每 block = 1 outer 行 x 1 tile x ubFactor 个连续元素 ----251 // 每 block = 1 outer 行 x 1 tile x ubFactor 个连续元素 ----
253 __aicore__ inline void UpdateLayoutAxis2(uint64_t blockIdx)252 __aicore__ inline void UpdateLayoutAxis2(uint64_t blockIdx)
254 {253 {
255- int64_t rowBlocksPerTile = CeilDiv(254+ int64_t rowBlocksPerTile = CeilDiv(td_->outShape[2], static_cast<int64_t>(td_->ubFactor));
256- td_->outShape[2], static_cast<int64_t>(td_->ubFactor));255+ int64_t blocksPerOuter = td_->outShape[1] * rowBlocksPerTile;
257- int64_t blocksPerOuter = td_->outShape[1] * rowBlocksPerTile;256+ int64_t outerIdx = static_cast<int64_t>(blockIdx) / blocksPerOuter;
258- int64_t outerIdx = static_cast<int64_t>(blockIdx) / blocksPerOuter;257+ int64_t remainder = static_cast<int64_t>(blockIdx) % blocksPerOuter;
259- int64_t remainder = static_cast<int64_t>(blockIdx) % blocksPerOuter;258+ int64_t tileIdx = remainder / rowBlocksPerTile;
260- int64_t tileIdx = remainder / rowBlocksPerTile;259+ int64_t innerBlock = remainder % rowBlocksPerTile;
261- int64_t innerBlock = remainder % rowBlocksPerTile;260+ int64_t innerStart = static_cast<int64_t>(td_->ubFactor) * innerBlock;
262- int64_t innerStart = static_cast<int64_t>(td_->ubFactor) * innerBlock;261+ int64_t innerCount = min(static_cast<int64_t>(td_->ubFactor), td_->outShape[2] - innerStart);
263- int64_t innerCount = min(
264- static_cast<int64_t>(td_->ubFactor),
265- td_->outShape[2] - innerStart);
266 262 
267 // 输入: 中间维=1 (tile=0)263 // 输入: 中间维=1 (tile=0)
268- layout_.axes[0].inStart = outerIdx;264+ layout_.axes[0].inStart = outerIdx;
269 layout_.axes[0].outStart = outerIdx;265 layout_.axes[0].outStart = outerIdx;
270- layout_.axes[0].length = 1;266+ layout_.axes[0].length = 1;
271 layout_.axes[1].outStart = tileIdx;267 layout_.axes[1].outStart = tileIdx;
272- layout_.axes[1].inStart = 0; // 输入中间维=1268+ layout_.axes[1].inStart = 0; // 输入中间维=1
273- layout_.axes[1].length = 1; // 仅 1 个 tile269+ layout_.axes[1].length = 1; // 仅 1 个 tile
274- layout_.axes[2].inStart = innerStart;270+ layout_.axes[2].inStart = innerStart;
275 layout_.axes[2].outStart = innerStart;271 layout_.axes[2].outStart = innerStart;
276- layout_.axes[2].length = innerCount;272+ layout_.axes[2].length = innerCount;
277 273 
278- layout_.inOffset = outerIdx * layout_.axes[0].inStride274+ layout_.inOffset = outerIdx * layout_.axes[0].inStride + innerStart * layout_.axes[2].inStride;
279- + innerStart * layout_.axes[2].inStride;275+ layout_.outOffset = outerIdx * layout_.axes[0].outStride + tileIdx * layout_.axes[1].outStride +
280- layout_.outOffset = outerIdx * layout_.axes[0].outStride276+ innerStart * layout_.axes[2].outStride;
281- + tileIdx * layout_.axes[1].outStride
282- + innerStart * layout_.axes[2].outStride;
283 277 
284- ubLen_ = innerCount;278+ ubLen_ = innerCount;
285- outGmBase_ = layout_.outOffset;279+ outGmBase_ = layout_.outOffset;
286- inGmBase_ = layout_.inOffset;280+ inGmBase_ = layout_.inOffset;
287 }281 }
288 282 
289 // ================================================================283 // ================================================================
@@ -303,20 +297,15 @@ private:
303 }297 }
304 NdDmaDci();298 NdDmaDci();
305 AscendC::NdDmaParams<T, NDDMA_DIM> params = {loopInfo, 0};299 AscendC::NdDmaParams<T, NDDMA_DIM> params = {loopInfo, 0};
306- AscendC::DataCopy<T, NDDMA_DIM>(300+ AscendC::DataCopy<T, NDDMA_DIM>(ubLocal[bufOffset], xGm_[inGmBase_], params);
307- ubLocal[bufOffset],
308- xGm_[inGmBase_],
309- params);
310 } else {301 } else {
311 DataCopyExtParams copyIn;302 DataCopyExtParams copyIn;
312 copyIn.blockCount = 1;303 copyIn.blockCount = 1;
313- copyIn.blockLen = static_cast<uint32_t>(ubLen_ * sizeof(T));304+ copyIn.blockLen = static_cast<uint32_t>(ubLen_ * sizeof(T));
314- copyIn.srcStride = 0;305+ copyIn.srcStride = 0;
315- copyIn.dstStride = 0;306+ copyIn.dstStride = 0;
316 DataCopyPadExtParams<T> padParams{false, 0, 0, 0};307 DataCopyPadExtParams<T> padParams{false, 0, 0, 0};
317- DataCopyPad(ubLocal[bufOffset],308+ DataCopyPad(ubLocal[bufOffset], xGm_[inGmBase_], copyIn, padParams);
318- xGm_[inGmBase_],
319- copyIn, padParams);
320 }309 }
321 }310 }
322 311 
@@ -326,12 +315,8 @@ private:
326 // ================================================================315 // ================================================================
327 __aicore__ inline void CopyOut(const LocalTensor<T>& ubLocal, uint64_t bufOffset)316 __aicore__ inline void CopyOut(const LocalTensor<T>& ubLocal, uint64_t bufOffset)
328 {317 {
329- DataCopyExtParams copyOut{318+ DataCopyExtParams copyOut{1, static_cast<uint32_t>(ubLen_ * sizeof(T)), 0, 0, 0};
330- 1, static_cast<uint32_t>(ubLen_ * sizeof(T)), 0, 0, 0319+ DataCopyPad(yGm_[outGmBase_], ubLocal[bufOffset], copyOut);
331- };
332- DataCopyPad(yGm_[outGmBase_],
333- ubLocal[bufOffset],
334- copyOut);
335 }320 }
336 321 
337 // ================================================================322 // ================================================================
@@ -349,9 +334,9 @@ private:
349 // ================================================================334 // ================================================================
350 // 每 Block 运行时变量335 // 每 Block 运行时变量
351 // ================================================================336 // ================================================================
352- int64_t ubLen_; // 本 Block 输出元素数337+ int64_t ubLen_; // 本 Block 输出元素数
353- int64_t outGmBase_; // 输出 GM 基址338+ int64_t outGmBase_; // 输出 GM 基址
354- int64_t inGmBase_; // 输入 GM 基址339+ int64_t inGmBase_; // 输入 GM 基址
355};340};
356 341 
357#endif // TILE_WITH_AXIS_H342#endif // TILE_WITH_AXIS_H
@@ -28,17 +28,17 @@
28 28 
29struct TileWithAxisTilingData {29struct TileWithAxisTilingData {
30 // ==== A. LayoutTransform 单切分公共字段 ====30 // ==== A. LayoutTransform 单切分公共字段 ====
31- int64_t inShape[3]; // 输入展平 shape: [outerDim, 1, rowLength]31+ int64_t inShape[3]; // 输入展平 shape: [outerDim, 1, rowLength]
32- int64_t outShape[3]; // 输出展平 shape: [outerDim, tiles, rowLength]32+ int64_t outShape[3]; // 输出展平 shape: [outerDim, tiles, rowLength]
33- uint64_t totalCount; // UB 切分块总数(基于 outShape 计算)33+ uint64_t totalCount; // UB 切分块总数(基于 outShape 计算)
34- uint64_t perCoreCount; // 每核处理的块数(totalCount 均分给 coreNum 个核)34+ uint64_t perCoreCount; // 每核处理的块数(totalCount 均分给 coreNum 个核)
35- uint8_t ubAxis; // UB 切分轴(0=outerDim, 1=tiles, 2=rowLength)35+ uint8_t ubAxis; // UB 切分轴(0=outerDim, 1=tiles, 2=rowLength)
36- uint32_t ubFactor; // 切分因子(沿 ubAxis 每次搬运该轴多少个元素)36+ uint32_t ubFactor; // 切分因子(沿 ubAxis 每次搬运该轴多少个元素)
37- uint32_t bufferSize; // UB buffer 字节大小(ping-pong 单个 buffer 的字节数)37+ uint32_t bufferSize; // UB buffer 字节大小(ping-pong 单个 buffer 的字节数)
38 38 
39 // ==== B. TileWithAxis 特有字段 ====39 // ==== B. TileWithAxis 特有字段 ====
40- int64_t tiles; // 复制次数40+ int64_t tiles; // 复制次数
41- int64_t rowLength; // 折叠后每行长度 = axisDim * innerDim41+ int64_t rowLength; // 折叠后每行长度 = axisDim * innerDim
42};42};
43 43 
44#endif // _TILE_WITH_AXIS_TILING_DATA_H_44#endif // _TILE_WITH_AXIS_TILING_DATA_H_
@@ -22,14 +22,8 @@
22 22 
23#include "ascendc/host_api/tiling/template_argument.h"23#include "ascendc/host_api/tiling/template_argument.h"
24 24 
25-ASCENDC_TPL_ARGS_DECL(TileWithAxis,25+ASCENDC_TPL_ARGS_DECL(TileWithAxis, ASCENDC_TPL_UINT_DECL(UB_AXIS, 8, ASCENDC_TPL_UI_LIST, 0, 1, 2));
26- ASCENDC_TPL_UINT_DECL(UB_AXIS, 8, ASCENDC_TPL_UI_LIST, 0, 1, 2)
27-);
28 26 
29-ASCENDC_TPL_SEL(27+ASCENDC_TPL_SEL(ASCENDC_TPL_ARGS_SEL(ASCENDC_TPL_UINT_SEL(UB_AXIS, ASCENDC_TPL_UI_LIST, 0, 1, 2)));
30- ASCENDC_TPL_ARGS_SEL(
31- ASCENDC_TPL_UINT_SEL(UB_AXIS, ASCENDC_TPL_UI_LIST, 0, 1, 2)
32- )
33-);
34 28 
35#endif // __TILE_WITH_AXIS_TILING_KEY_H__29#endif // __TILE_WITH_AXIS_TILING_KEY_H__
@@ -25,8 +25,7 @@
25using namespace AscendC;25using namespace AscendC;
26 26 
27template <int UB_AXIS>27template <int UB_AXIS>
28-__aicore__ inline void LaunchKernel(GM_ADDR x, GM_ADDR y,28+__aicore__ inline void LaunchKernel(GM_ADDR x, GM_ADDR y, const TileWithAxisTilingData* td)
29- const TileWithAxisTilingData* td)
30{29{
31 TileWithAxisKernel<DTYPE_X, UB_AXIS> op;30 TileWithAxisKernel<DTYPE_X, UB_AXIS> op;
32 op.Init(x, y, td);31 op.Init(x, y, td);
@@ -34,8 +33,7 @@ __aicore__ inline void LaunchKernel(GM_ADDR x, GM_ADDR y,
34}33}
35 34 
36template <int UB_AXIS>35template <int UB_AXIS>
37-__global__ __aicore__ void tile_with_axis(36+__global__ __aicore__ void tile_with_axis(GM_ADDR x, GM_ADDR y, GM_ADDR workspace, GM_ADDR tiling)
38- GM_ADDR x, GM_ADDR y, GM_ADDR workspace, GM_ADDR tiling)
39{37{
40 SetSysWorkspace(workspace);38 SetSysWorkspace(workspace);
41 KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY);39 KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY);
@@ -0,0 +1,149 @@
1+#!/usr/bin/env python3
2+# -*- coding: UTF-8 -*-
3+# ----------------------------------------------------------------------------
4+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
5+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
6+# CANN Open Software License Agreement Version 2.0 (the "License").
7+# Please refer to the License for details. You may not use this file except in compliance with the License.
8+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
9+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
10+# See LICENSE in the root of the software repository for the full text of the License.
11+# ----------------------------------------------------------------------------
12+"""TileWithAxis 独立 Golden:Kernel/GEIR。
13+ 
14+沿指定 axis 将输入 x 复制 tiles 份并整块拼接(等价 concat([x]*tiles, dim=axis)),
15+仅 Caffe Tile 与之一一对应;PyTorch/TF 均无同名单算子 API
16+(torch.repeat / tf.tile 为全维度 multiples 形式,torch.repeat_interleave /
17+tf.repeat 为交错复制、语义不符)。
18+因此 golden 与三方标杆均以 torch 小算子拼接实现,并通过
19+torch_graph(torch.nn.Module)提供 TTK E2E torch.compile 模式的图封装。
20+ 
21+TTK kernel 模式消费:
22+ python3 -m ttk kernel -i xxx.csv --plugin tile_with_axis_golden_torch.py
23+ 
24+注册名 tile_with_axis 与 CSV 的 op_name 一致;
25+golden 入参为 numpy.ndarray(kernel 流程),返回 list(每个输出一个元素)。
26+"""
27+ 
28+import numpy as np
29+import torch
30+ 
31+__spec__ = {"tile_with_axis": "TileWithAxisTestSpec"}
32+ 
33+# 纯拷贝算子容差口径:
34+# - TTK tolerance 声明(本文件 _TOLERANCE):整型 binary_equal;浮点同为 binary_equal
35+# (拷贝语义下 golden 与 kernel 输出应逐位一致,为最严声明)。
36+# - spec.yaml numerical_tolerance 与 CSV precision_tolerances 对浮点另声明 rtol/atol
37+# (fp16 1e-3 / bf16 4e-3 / fp32 1e-5,stat_rel_err 口径),为三方精度报告的宽容差上限;
38+# 实际 run 收据按 stat_rel_err 记录。两者宽严不同但均为拷贝语义下的合法声明,不构成冲突。
39+_TOLERANCE = {
40+ "float16": {"standard": "binary_equal"},
41+ "bfloat16": {"standard": "binary_equal"},
42+ "float32": {"standard": "binary_equal"},
43+ "int8": {"standard": "binary_equal"},
44+ "int16": {"standard": "binary_equal"},
45+ "int32": {"standard": "binary_equal"},
46+ "int64": {"standard": "binary_equal"},
47+ "uint8": {"standard": "binary_equal"},
48+ "uint16": {"standard": "binary_equal"},
49+ "uint32": {"standard": "binary_equal"},
50+ "uint64": {"standard": "binary_equal"},
51+}
52+ 
53+ 
54+def _tile_with_axis_compute(x, axis, tiles):
55+ """核心计算(torch 小算子拼接):y = cat([x]*tiles, dim=axis)。
56+ 
57+ 整块复制拼接、非交错;负索引先规约到 [0, rank)。
58+ rank=0 标量输入(人工裁决 2026-10-20:支持):输出 [tiles],axis 不生效。
59+ """
60+ axis = int(axis)
61+ tiles = int(tiles)
62+ if tiles <= 0:
63+ raise ValueError(f"tiles must be > 0, got {tiles}")
64+ if x.dim() == 0:
65+ return x.repeat(tiles)
66+ if axis < 0:
67+ axis += x.dim()
68+ return torch.cat([x] * tiles, dim=axis)
69+ 
70+ 
71+def _numpy_to_torch(np_array):
72+ if np_array is None:
73+ return None
74+ if "bfloat16" in str(np_array.dtype):
75+ return torch.from_numpy(np_array.view(np.int16)).view(torch.bfloat16)
76+ return torch.from_numpy(np_array)
77+ 
78+ 
79+def _torch_to_numpy(torch_tensor, orig_dtype=None):
80+ if torch_tensor is None:
81+ return None
82+ if torch_tensor.dtype == torch.bfloat16:
83+ target = orig_dtype if orig_dtype is not None else np.dtype(np.int16)
84+ return torch_tensor.view(torch.int16).numpy().view(target)
85+ return torch_tensor.numpy()
86+ 
87+ 
88+def tile_with_axis_golden(x, *, axis, tiles, **kwargs):
89+ """Golden function for tile_with_axis (kernel mode, numpy in / numpy out)。"""
90+ del kwargs
91+ orig_dtype = x.dtype
92+ x_torch = _numpy_to_torch(x)
93+ result = _tile_with_axis_compute(x_torch, axis, tiles)
94+ return [_torch_to_numpy(result, orig_dtype)]
95+ 
96+ 
97+class _TorchTileWithAxis:
98+ """竞品 third_party:torch 小算子拼接实现(无一一对应竞品算子)。
99+ 
100+ __init__ 吸收 CSV attributes(axis / tiles);
101+ __call__ 直接使用输入张量计算,无多余 .to() 等数据转换,保证性能统计准确。
102+ 输入支持位置参数 torch.Tensor 或按名 kwargs(x / input)。
103+ """
104+ 
105+ def __init__(self, *, axis=1, tiles=1, **kwargs):
106+ del kwargs
107+ self.axis = axis
108+ self.tiles = tiles
109+ 
110+ def __call__(self, *args, **kwargs):
111+ x = next((a for a in args if isinstance(a, torch.Tensor)), None)
112+ if x is None:
113+ x = kwargs.get("x", kwargs.get("input"))
114+ if x is None:
115+ raise ValueError(
116+ "Missing tensor input; expected positional Tensor or one of: x, input"
117+ )
118+ return [_tile_with_axis_compute(x, self.axis, self.tiles)]
119+ 
120+ 
121+class TileWithAxisGraph(torch.nn.Module):
122+ """Graph module consumed by TTK E2E torch.compile modes.
123+ 
124+ 与 third_party 同一核心拼接实现,属性经 __init__ 固化为图常量。
125+ """
126+ 
127+ def __init__(self, *, axis=1, tiles=1, **kwargs):
128+ super().__init__()
129+ del kwargs
130+ self.axis = axis
131+ self.tiles = tiles
132+ 
133+ def forward(self, x, **kwargs):
134+ del kwargs
135+ return _tile_with_axis_compute(x, self.axis, self.tiles)
136+ 
137+ 
138+class TileWithAxisTestSpec:
139+ """TileWithAxis operator golden spec (kernel flow: numpy in, numpy out).
140+ 
141+ x: input Tensor (numpy.ndarray)
142+ axis: int, the axis along which to tile
143+ tiles: int, number of copies to concatenate along axis
144+ """
145+ 
146+ golden = staticmethod(tile_with_axis_golden)
147+ third_party = {"torch": _TorchTileWithAxis}
148+ torch_graph = TileWithAxisGraph
149+ tolerance = _TOLERANCE
@@ -36,43 +36,31 @@ static const std::string OP_NAME = "TileWithAxis";
36// ============================================================================36// ============================================================================
37// Helper: build TilingContextPara with attributes37// Helper: build TilingContextPara with attributes
38// ============================================================================38// ============================================================================
39-static TilingContextPara MakeTilingPara(39+static TilingContextPara MakeTilingPara(const string& caseName, initializer_list<int64_t> xShape, DataType xDtype,
40- const string& caseName,40+ int64_t axis, int64_t tiles,
41- initializer_list<int64_t> xShape,41+ uint64_t ubSize = 253952, // Ascend950 UB = 248KB
42- DataType xDtype,42+ uint64_t coreNum = 64, uint64_t tilingDataSize = 4096, bool includeTiles = true)
43- int64_t axis,
44- int64_t tiles,
45- uint64_t ubSize = 253952, // Ascend950 UB = 248KB
46- uint64_t coreNum = 64,
47- uint64_t tilingDataSize = 4096)
48{43{
49 StorageShape shape = {xShape, xShape};44 StorageShape shape = {xShape, xShape};
50- vector<TilingContextPara::TensorDescription> inputs = {45+ vector<TilingContextPara::TensorDescription> inputs = {{shape, xDtype, FORMAT_ND}};
51- {shape, xDtype, FORMAT_ND}
52- };
53 // Output shape: not used by tiling function directly; infershape computes it46 // Output shape: not used by tiling function directly; infershape computes it
54- StorageShape outShape = {xShape, xShape}; // placeholder47+ StorageShape outShape = {xShape, xShape}; // placeholder
55- vector<TilingContextPara::TensorDescription> outputs = {48+ vector<TilingContextPara::TensorDescription> outputs = {{outShape, xDtype, FORMAT_ND}};
56- {outShape, xDtype, FORMAT_ND}
57- };
58 49 
59- vector<TilingContextPara::OpAttr> attrs = {50+ vector<TilingContextPara::OpAttr> attrs = {{"axis", Ops::Math::AnyValue::CreateFrom<int64_t>(axis)}};
60- {"axis", Ops::Math::AnyValue::CreateFrom<int64_t>(axis)},51+ if (includeTiles) {
61- {"tiles", Ops::Math::AnyValue::CreateFrom<int64_t>(tiles)}52+ attrs.push_back({"tiles", Ops::Math::AnyValue::CreateFrom<int64_t>(tiles)});
62- };53+ }
63 54 
64- struct TileWithAxisCompileInfo {} compileInfo;55+ struct TileWithAxisCompileInfo {
65- return TilingContextPara(OP_NAME, inputs, outputs, attrs, &compileInfo,56+ } compileInfo;
66- coreNum, ubSize, tilingDataSize);57+ return TilingContextPara(OP_NAME, inputs, outputs, attrs, &compileInfo, coreNum, ubSize, tilingDataSize);
67}58}
68 59 
69// ============================================================================60// ============================================================================
70// Helper: execute tiling and extract TilingData61// Helper: execute tiling and extract TilingData
71// ============================================================================62// ============================================================================
72-static bool GetTilingData(const TilingContextPara& para, TilingInfo& info)63+static bool GetTilingData(const TilingContextPara& para, TilingInfo& info) { return ExecuteTiling(para, info); }
73-{
74- return ExecuteTiling(para, info);
75-}
76 64 
77// ============================================================================65// ============================================================================
78// P0: ubAxis=0 分支 — 切 outerDim66// P0: ubAxis=0 分支 — 切 outerDim
@@ -83,21 +71,20 @@ static bool GetTilingData(const TilingContextPara& para, TilingInfo& info)
83// ============================================================================71// ============================================================================
84TEST(TileWithAxisTiling, ubAxis0_cut_outerDim_fp16)72TEST(TileWithAxisTiling, ubAxis0_cut_outerDim_fp16)
85{73{
86- auto para = MakeTilingPara("ubAxis0_fp16",74+ auto para = MakeTilingPara("ubAxis0_fp16", {100, 10, 10}, DT_FLOAT16, 1, 3);
87- {100, 10, 10}, DT_FLOAT16, 1, 3);
88 TilingInfo info;75 TilingInfo info;
89 ASSERT_TRUE(GetTilingData(para, info));76 ASSERT_TRUE(GetTilingData(para, info));
90 77 
91 auto* td = reinterpret_cast<const TileWithAxisTilingData*>(info.tilingData.get());78 auto* td = reinterpret_cast<const TileWithAxisTilingData*>(info.tilingData.get());
92 EXPECT_EQ(td->ubAxis, static_cast<uint8_t>(0));79 EXPECT_EQ(td->ubAxis, static_cast<uint8_t>(0));
93 EXPECT_EQ(td->tiles, 3);80 EXPECT_EQ(td->tiles, 3);
94- EXPECT_EQ(td->rowLength, 100); // axisDim=10 * innerDim=1081+ EXPECT_EQ(td->rowLength, 100); // axisDim=10 * innerDim=10
95- EXPECT_EQ(td->inShape[0], 100); // outerDim82+ EXPECT_EQ(td->inShape[0], 100); // outerDim
96- EXPECT_EQ(td->inShape[1], 1); // 输入中间维=183+ EXPECT_EQ(td->inShape[1], 1); // 输入中间维=1
97- EXPECT_EQ(td->inShape[2], 100); // rowLength84+ EXPECT_EQ(td->inShape[2], 100); // rowLength
98- EXPECT_EQ(td->outShape[0], 100); // outerDim85+ EXPECT_EQ(td->outShape[0], 100); // outerDim
99- EXPECT_EQ(td->outShape[1], 3); // tiles86+ EXPECT_EQ(td->outShape[1], 3); // tiles
100- EXPECT_EQ(td->outShape[2], 100); // rowLength87+ EXPECT_EQ(td->outShape[2], 100); // rowLength
101 // Step 2.5 核数感知: totalCount=1 < threshold=51, 降 ubFactor 增加 block88 // Step 2.5 核数感知: totalCount=1 < threshold=51, 降 ubFactor 增加 block
102 // targetBlocks=min(51, 30000/4096=7, 100)=7, ubFactor=CeilDiv(100,7)=1589 // targetBlocks=min(51, 30000/4096=7, 100)=7, ubFactor=CeilDiv(100,7)=15
103 EXPECT_EQ(td->totalCount, static_cast<uint64_t>(7));90 EXPECT_EQ(td->totalCount, static_cast<uint64_t>(7));
@@ -115,14 +102,13 @@ TEST(TileWithAxisTiling, ubAxis0_cut_outerDim_fp16)
115// ============================================================================102// ============================================================================
116TEST(TileWithAxisTiling, ubAxis0_cut_outerDim_fp32)103TEST(TileWithAxisTiling, ubAxis0_cut_outerDim_fp32)
117{104{
118- auto para = MakeTilingPara("ubAxis0_fp32",105+ auto para = MakeTilingPara("ubAxis0_fp32", {100, 6, 50}, DT_FLOAT, 1, 3);
119- {100, 6, 50}, DT_FLOAT, 1, 3);
120 TilingInfo info;106 TilingInfo info;
121 ASSERT_TRUE(GetTilingData(para, info));107 ASSERT_TRUE(GetTilingData(para, info));
122 108 
123 auto* td = reinterpret_cast<const TileWithAxisTilingData*>(info.tilingData.get());109 auto* td = reinterpret_cast<const TileWithAxisTilingData*>(info.tilingData.get());
124 EXPECT_EQ(td->ubAxis, static_cast<uint8_t>(0));110 EXPECT_EQ(td->ubAxis, static_cast<uint8_t>(0));
125- EXPECT_EQ(td->rowLength, 300); // axisDim=6 * innerDim=50111+ EXPECT_EQ(td->rowLength, 300); // axisDim=6 * innerDim=50
126 EXPECT_EQ(td->outShape[0], 100);112 EXPECT_EQ(td->outShape[0], 100);
127 EXPECT_EQ(td->outShape[1], 3);113 EXPECT_EQ(td->outShape[1], 3);
128 EXPECT_EQ(td->outShape[2], 300);114 EXPECT_EQ(td->outShape[2], 300);
@@ -141,8 +127,7 @@ TEST(TileWithAxisTiling, ubAxis0_cut_outerDim_fp32)
141// ============================================================================127// ============================================================================
142TEST(TileWithAxisTiling, ubAxis1_cut_tiles_fp16)128TEST(TileWithAxisTiling, ubAxis1_cut_tiles_fp16)
143{129{
144- auto para = MakeTilingPara("ubAxis1_fp16",130+ auto para = MakeTilingPara("ubAxis1_fp16", {3, 1000, 60}, DT_FLOAT16, 1, 3);
145- {3, 1000, 60}, DT_FLOAT16, 1, 3);
146 TilingInfo info;131 TilingInfo info;
147 ASSERT_TRUE(GetTilingData(para, info));132 ASSERT_TRUE(GetTilingData(para, info));
148 133 
@@ -151,7 +136,7 @@ TEST(TileWithAxisTiling, ubAxis1_cut_tiles_fp16)
151 // candTarget=51, bpot=CeilDiv(51,9)=6, ubFactor=CeilDiv(60000,6)=10000136 // candTarget=51, bpot=CeilDiv(51,9)=6, ubFactor=CeilDiv(60000,6)=10000
152 EXPECT_EQ(td->ubAxis, static_cast<uint8_t>(2));137 EXPECT_EQ(td->ubAxis, static_cast<uint8_t>(2));
153 EXPECT_EQ(td->ubFactor, static_cast<uint32_t>(10000));138 EXPECT_EQ(td->ubFactor, static_cast<uint32_t>(10000));
154- EXPECT_EQ(td->rowLength, 60000); // axisDim=1000 * innerDim=60139+ EXPECT_EQ(td->rowLength, 60000); // axisDim=1000 * innerDim=60
155 EXPECT_EQ(td->outShape[0], 3);140 EXPECT_EQ(td->outShape[0], 3);
156 EXPECT_EQ(td->outShape[1], 3);141 EXPECT_EQ(td->outShape[1], 3);
157 EXPECT_EQ(td->outShape[2], 60000);142 EXPECT_EQ(td->outShape[2], 60000);
@@ -167,8 +152,7 @@ TEST(TileWithAxisTiling, ubAxis1_cut_tiles_fp16)
167// ============================================================================152// ============================================================================
168TEST(TileWithAxisTiling, ubAxis1_cut_tiles_fp32)153TEST(TileWithAxisTiling, ubAxis1_cut_tiles_fp32)
169{154{
170- auto para = MakeTilingPara("ubAxis1_fp32",155+ auto para = MakeTilingPara("ubAxis1_fp32", {3, 500, 40}, DT_FLOAT, 1, 5);
171- {3, 500, 40}, DT_FLOAT, 1, 5);
172 TilingInfo info;156 TilingInfo info;
173 ASSERT_TRUE(GetTilingData(para, info));157 ASSERT_TRUE(GetTilingData(para, info));
174 158 
@@ -192,8 +176,7 @@ TEST(TileWithAxisTiling, ubAxis1_cut_tiles_fp32)
192// ============================================================================176// ============================================================================
193TEST(TileWithAxisTiling, ubAxis2_cut_rowLength_fp16)177TEST(TileWithAxisTiling, ubAxis2_cut_rowLength_fp16)
194{178{
195- auto para = MakeTilingPara("ubAxis2_fp16",179+ auto para = MakeTilingPara("ubAxis2_fp16", {2, 65536, 16}, DT_FLOAT16, 1, 3);
196- {2, 65536, 16}, DT_FLOAT16, 1, 3);
197 TilingInfo info;180 TilingInfo info;
198 ASSERT_TRUE(GetTilingData(para, info));181 ASSERT_TRUE(GetTilingData(para, info));
199 182 
@@ -217,8 +200,7 @@ TEST(TileWithAxisTiling, ubAxis2_cut_rowLength_fp16)
217// ============================================================================200// ============================================================================
218TEST(TileWithAxisTiling, ubAxis2_cut_rowLength_fp32)201TEST(TileWithAxisTiling, ubAxis2_cut_rowLength_fp32)
219{202{
220- auto para = MakeTilingPara("ubAxis2_fp32",203+ auto para = MakeTilingPara("ubAxis2_fp32", {2, 32768, 16}, DT_FLOAT, 1, 3);
221- {2, 32768, 16}, DT_FLOAT, 1, 3);
222 TilingInfo info;204 TilingInfo info;
223 ASSERT_TRUE(GetTilingData(para, info));205 ASSERT_TRUE(GetTilingData(para, info));
224 206 
@@ -237,8 +219,7 @@ TEST(TileWithAxisTiling, ubAxis2_cut_rowLength_fp32)
237// ============================================================================219// ============================================================================
238TEST(TileWithAxisTiling, scalar_input)220TEST(TileWithAxisTiling, scalar_input)
239{221{
240- auto para = MakeTilingPara("scalar",222+ auto para = MakeTilingPara("scalar", {}, DT_FLOAT16, 0, 5);
241- {}, DT_FLOAT16, 0, 5);
242 TilingInfo info;223 TilingInfo info;
243 ASSERT_TRUE(GetTilingData(para, info));224 ASSERT_TRUE(GetTilingData(para, info));
244 225 
@@ -265,8 +246,7 @@ TEST(TileWithAxisTiling, scalar_input)
265// ============================================================================246// ============================================================================
266TEST(TileWithAxisTiling, negative_axis)247TEST(TileWithAxisTiling, negative_axis)
267{248{
268- auto para = MakeTilingPara("negative_axis",249+ auto para = MakeTilingPara("negative_axis", {2, 3, 4}, DT_FLOAT16, -1, 3);
269- {2, 3, 4}, DT_FLOAT16, -1, 3);
270 TilingInfo info;250 TilingInfo info;
271 ASSERT_TRUE(GetTilingData(para, info));251 ASSERT_TRUE(GetTilingData(para, info));
272 252 
@@ -275,8 +255,8 @@ TEST(TileWithAxisTiling, negative_axis)
275 // candTarget=12, bpot=CeilDiv(12,6)=2, ubFactor=CeilDiv(4,2)=2255 // candTarget=12, bpot=CeilDiv(12,6)=2, ubFactor=CeilDiv(4,2)=2
276 EXPECT_EQ(td->ubAxis, static_cast<uint8_t>(2));256 EXPECT_EQ(td->ubAxis, static_cast<uint8_t>(2));
277 EXPECT_EQ(td->tiles, 3);257 EXPECT_EQ(td->tiles, 3);
278- EXPECT_EQ(td->rowLength, 4); // axisDim=4, innerDim=1258+ EXPECT_EQ(td->rowLength, 4); // axisDim=4, innerDim=1
279- EXPECT_EQ(td->inShape[0], 6); // outerDim = 2*3 = 6259+ EXPECT_EQ(td->inShape[0], 6); // outerDim = 2*3 = 6
280 EXPECT_EQ(td->outShape[0], 6);260 EXPECT_EQ(td->outShape[0], 6);
281 EXPECT_EQ(td->outShape[1], 3);261 EXPECT_EQ(td->outShape[1], 3);
282 EXPECT_EQ(td->outShape[2], 4);262 EXPECT_EQ(td->outShape[2], 4);
@@ -289,8 +269,7 @@ TEST(TileWithAxisTiling, negative_axis)
289// ============================================================================269// ============================================================================
290TEST(TileWithAxisTiling, tiles_equals_one)270TEST(TileWithAxisTiling, tiles_equals_one)
291{271{
292- auto para = MakeTilingPara("tiles_1",272+ auto para = MakeTilingPara("tiles_1", {2, 3, 4}, DT_FLOAT16, 1, 1);
293- {2, 3, 4}, DT_FLOAT16, 1, 1);
294 TilingInfo info;273 TilingInfo info;
295 ASSERT_TRUE(GetTilingData(para, info));274 ASSERT_TRUE(GetTilingData(para, info));
296 275 
@@ -305,8 +284,7 @@ TEST(TileWithAxisTiling, tiles_equals_one)
305// ============================================================================284// ============================================================================
306TEST(TileWithAxisTiling, tiles_invalid_zero)285TEST(TileWithAxisTiling, tiles_invalid_zero)
307{286{
308- auto para = MakeTilingPara("tiles_0",287+ auto para = MakeTilingPara("tiles_0", {2, 3, 4}, DT_FLOAT16, 0, 0);
309- {2, 3, 4}, DT_FLOAT16, 0, 0);
310 TilingInfo info;288 TilingInfo info;
311 EXPECT_FALSE(GetTilingData(para, info));289 EXPECT_FALSE(GetTilingData(para, info));
312}290}
@@ -317,8 +295,7 @@ TEST(TileWithAxisTiling, tiles_invalid_zero)
317TEST(TileWithAxisTiling, axis_out_of_range)295TEST(TileWithAxisTiling, axis_out_of_range)
318{296{
319 // rank=3, axis=3 → 越界297 // rank=3, axis=3 → 越界
320- auto para = MakeTilingPara("axis_oob",298+ auto para = MakeTilingPara("axis_oob", {2, 3, 4}, DT_FLOAT16, 3, 2);
321- {2, 3, 4}, DT_FLOAT16, 3, 2);
322 TilingInfo info;299 TilingInfo info;
323 EXPECT_FALSE(ExecuteTiling(para, info));300 EXPECT_FALSE(ExecuteTiling(para, info));
324}301}
@@ -331,8 +308,7 @@ TEST(TileWithAxisTiling, axis_out_of_range)
331// ============================================================================308// ============================================================================
332TEST(TileWithAxisTiling, high_dim_4d_axis_2)309TEST(TileWithAxisTiling, high_dim_4d_axis_2)
333{310{
334- auto para = MakeTilingPara("4d_axis2",311+ auto para = MakeTilingPara("4d_axis2", {2, 4, 8, 16}, DT_FLOAT16, 2, 3);
335- {2, 4, 8, 16}, DT_FLOAT16, 2, 3);
336 TilingInfo info;312 TilingInfo info;
337 ASSERT_TRUE(GetTilingData(para, info));313 ASSERT_TRUE(GetTilingData(para, info));
338 314 
@@ -340,10 +316,103 @@ TEST(TileWithAxisTiling, high_dim_4d_axis_2)
340 // Step 2.5 跨轴回退: ubAxis=0 totalCount=1 < threshold=51, 回退到 ubAxis=2316 // Step 2.5 跨轴回退: ubAxis=0 totalCount=1 < threshold=51, 回退到 ubAxis=2
341 // candTarget=51, bpot=CeilDiv(51,24)=3, ubFactor=CeilDiv(128,3)=43317 // candTarget=51, bpot=CeilDiv(51,24)=3, ubFactor=CeilDiv(128,3)=43
342 EXPECT_EQ(td->ubAxis, static_cast<uint8_t>(2));318 EXPECT_EQ(td->ubAxis, static_cast<uint8_t>(2));
343- EXPECT_EQ(td->rowLength, 128); // axisDim=8 * innerDim=16319+ EXPECT_EQ(td->rowLength, 128); // axisDim=8 * innerDim=16
344- EXPECT_EQ(td->inShape[0], 8); // outerDim = 2*4320+ EXPECT_EQ(td->inShape[0], 8); // outerDim = 2*4
345 EXPECT_EQ(td->outShape[1], 3);321 EXPECT_EQ(td->outShape[1], 3);
346 EXPECT_EQ(td->outShape[2], 128);322 EXPECT_EQ(td->outShape[2], 128);
347}323}
348 324 
325+// ============================================================================
326+// 回归(SAFE.01/1 / CODE.09/1): 标量输入 tiles 超过单缓冲容量时按 ubAxis=1 分块
327+// shape=[], fp16, tiles=200000, ubSize=253952
328+// bufferSize=126976B, bufferSizeElements=63488 < 200000
329+// → ubAxis=1, ubFactor=63488, totalCount=CeilDiv(200000,63488)=4
330+// perCoreCount=CeilDiv(4,64)=1, realCoreNum=CeilDiv(4,1)=4
331+// ============================================================================
332+TEST(TileWithAxisTiling, scalar_input_huge_tiles_chunked)
333+{
334+ auto para = MakeTilingPara("scalar_huge_tiles", {}, DT_FLOAT16, 0, 200000);
335+ TilingInfo info;
336+ ASSERT_TRUE(GetTilingData(para, info));
337+ 
338+ auto* td = reinterpret_cast<const TileWithAxisTilingData*>(info.tilingData.get());
339+ EXPECT_EQ(td->ubAxis, static_cast<uint8_t>(1));
340+ EXPECT_EQ(td->ubFactor, static_cast<uint32_t>(63488));
341+ EXPECT_EQ(td->totalCount, static_cast<uint64_t>(4));
342+ EXPECT_EQ(td->outShape[0], 1);
343+ EXPECT_EQ(td->outShape[1], 200000);
344+ EXPECT_EQ(td->outShape[2], 1);
345+ EXPECT_EQ(info.blockNum, static_cast<size_t>(4));
346+}
347+ 
348+// ============================================================================
349+// 回归(空张量兜底,REQUIREMENTS §3.5.2): 某维为 0 时 totalCount=0
350+// SetBlockDim(1)(而非 0),kernel 空循环
351+// shape=[2, 0, 3], axis=1, tiles=2, fp16: rowLength=0 → totalCount=0
352+// ============================================================================
353+TEST(TileWithAxisTiling, empty_tensor_blockdim_one)
354+{
355+ auto para = MakeTilingPara("empty_tensor", {2, 0, 3}, DT_FLOAT16, 1, 2);
356+ TilingInfo info;
357+ ASSERT_TRUE(GetTilingData(para, info));
358+ 
359+ auto* td = reinterpret_cast<const TileWithAxisTilingData*>(info.tilingData.get());
360+ EXPECT_EQ(td->totalCount, static_cast<uint64_t>(0));
361+ EXPECT_EQ(td->perCoreCount, static_cast<uint64_t>(1));
362+ EXPECT_EQ(info.blockNum, static_cast<size_t>(1));
363+}
364+ 
365+// ============================================================================
366+// 回归(CODE.18/1 / CODE.06/1): int64 维度乘积溢出时 tiling 干净拒绝
367+// rowLength = axisDim * innerDim = 4e18 * 2 = 8e18(int64 可表示),
368+// 但 rowOutElements = tiles * rowLength = 2 * 8e18 = 1.6e19 溢出 int64,
369+// 应返回 GRAPH_FAILED,而不是生成错误 TilingData。
370+// ============================================================================
371+TEST(TileWithAxisTiling, int64_product_overflow_rejected)
372+{
373+ auto para = MakeTilingPara("int64_overflow", {1, 4000000000000000000LL, 2}, DT_FLOAT16, 1, 2);
374+ TilingInfo info;
375+ EXPECT_FALSE(ExecuteTiling(para, info));
376+}
377+ 
378+// ============================================================================
379+// 回归(CODE.18/1 / CODE.06/1): rowLength = axisDim * innerDim 溢出时干净拒绝
380+// rank=2, axis=0: axisDim=4e18, innerDim=3 → rowLength=1.2e19 溢出 int64
381+// ============================================================================
382+TEST(TileWithAxisTiling, rowLength_product_overflow_rejected)
383+{
384+ auto para = MakeTilingPara("rowLength_overflow", {4000000000000000000LL, 3}, DT_FLOAT16, 0, 2);
385+ TilingInfo info;
386+ EXPECT_FALSE(ExecuteTiling(para, info));
387+}
388+ 
389+// ============================================================================
390+// 回归(CODE.18/1 / CODE.06/1): outerDim 连乘溢出时干净拒绝
391+// rank=3, axis=2: outerDim = 2e9 * 3e9 = 6e18 溢出 int64
392+// ============================================================================
393+TEST(TileWithAxisTiling, outerDim_product_overflow_rejected)
394+{
395+ auto para = MakeTilingPara("outerDim_overflow", {2000000000LL, 3000000000LL, 4}, DT_FLOAT16, 2, 2);
396+ TilingInfo info;
397+ EXPECT_FALSE(ExecuteTiling(para, info));
398+}
399+ 
400+// ============================================================================
401+// 回归(CODE.18/1 / CODE.06/1): totalOutElements = outerDim*tiles*rowLength 溢出时干净拒绝
402+// outerDim=4e18, rowLength=3 → total = 4e18*2*3 = 2.4e19 溢出 int64
403+// ============================================================================
404+TEST(TileWithAxisTiling, totalOutElements_overflow_rejected)
405+{
406+ auto para = MakeTilingPara("total_overflow", {4000000000000000000LL, 3}, DT_FLOAT16, 1, 2);
407+ TilingInfo info;
408+ EXPECT_FALSE(ExecuteTiling(para, info));
409+}
410+ 
411+TEST(TileWithAxisTiling, tiles_missing_rejected)
412+{
413+ auto para = MakeTilingPara("tiles_missing", {2, 3, 4}, DT_FLOAT16, 1, 0, 253952, 64, 4096, false);
414+ TilingInfo info;
415+ EXPECT_FALSE(ExecuteTiling(para, info));
416+}
417+ 
349} // namespace TileWithAxisUT418} // namespace TileWithAxisUT
@@ -34,7 +34,7 @@ static const string OP_NAME = "TileWithAxis";
34// Helper: create InfershapeContextPara with axis/tiles attributes34// Helper: create InfershapeContextPara with axis/tiles attributes
35// ============================================================================35// ============================================================================
36static InfershapeContextPara MakeInfershapePara(const string& caseName, initializer_list<int64_t> xShape,36static InfershapeContextPara MakeInfershapePara(const string& caseName, initializer_list<int64_t> xShape,
37- DataType dtype, int64_t axis, int64_t tiles)37+ DataType dtype, int64_t axis, int64_t tiles, bool includeTiles = true)
38{38{
39 StorageShape inputShape = {xShape, xShape};39 StorageShape inputShape = {xShape, xShape};
40 // Output placeholder: unknown shape40 // Output placeholder: unknown shape
@@ -42,8 +42,10 @@ static InfershapeContextPara MakeInfershapePara(const string& caseName, initiali
42 42 
43 vector<InfershapeContextPara::TensorDescription> inputs = {{inputShape, dtype, FORMAT_ND}};43 vector<InfershapeContextPara::TensorDescription> inputs = {{inputShape, dtype, FORMAT_ND}};
44 vector<InfershapeContextPara::TensorDescription> outputs = {{outputPlaceholder, dtype, FORMAT_ND}};44 vector<InfershapeContextPara::TensorDescription> outputs = {{outputPlaceholder, dtype, FORMAT_ND}};
45- vector<InfershapeContextPara::OpAttr> attrs = {{"axis", Ops::Math::AnyValue::CreateFrom<int64_t>(axis)},45+ vector<InfershapeContextPara::OpAttr> attrs = {{"axis", Ops::Math::AnyValue::CreateFrom<int64_t>(axis)}};
46- {"tiles", Ops::Math::AnyValue::CreateFrom<int64_t>(tiles)}};46+ if (includeTiles) {
47+ attrs.push_back({"tiles", Ops::Math::AnyValue::CreateFrom<int64_t>(tiles)});
48+ }
47 49 
48 return InfershapeContextPara(OP_NAME, inputs, outputs, attrs);50 return InfershapeContextPara(OP_NAME, inputs, outputs, attrs);
49}51}
@@ -159,4 +161,41 @@ TEST(TileWithAxisInfershape, invalid_axis_negative_is_rejected)
159 ExecuteTestCase(para, GRAPH_FAILED);161 ExecuteTestCase(para, GRAPH_FAILED);
160}162}
161 163 
164+// ============================================================================
165+// 回归: axis 维为未知维标记 -1 时原样传播(COMPAT.02/1 / GEIR.03/2)
166+// 输入 shape=[-1, 3, 4], axis=1, tiles=2 → 输出 shape=[-1, 6, 4](非 -1*2=-2)
167+// ============================================================================
168+TEST(TileWithAxisInfershape, unknown_dim_axis_propagated)
169+{
170+ auto para = MakeInfershapePara("unknown_dim_axis", {-1, 3, 4}, DT_FLOAT16, 1, 2);
171+ vector<vector<int64_t>> expected = {{-1, 6, 4}};
172+ ExecuteTestCase(para, GRAPH_SUCCESS, expected);
173+}
174+ 
175+TEST(TileWithAxisInfershape, unknown_dim_negative_axis_propagated)
176+{
177+ auto para = MakeInfershapePara("unknown_dim_neg_axis", {2, -1, 4}, DT_FLOAT16, -1, 3);
178+ vector<vector<int64_t>> expected = {{2, -1, 12}};
179+ ExecuteTestCase(para, GRAPH_SUCCESS, expected);
180+}
181+ 
182+// ============================================================================
183+// 回归: axis 维乘法 int64 溢出时拒绝(GEIR.04/1)
184+// dim=5e9, tiles=2e9 → 乘积 1e19 > INT64_MAX,返回 GRAPH_FAILED
185+// ============================================================================
186+TEST(TileWithAxisInfershape, axis_dim_overflow_rejected)
187+{
188+ auto para = MakeInfershapePara("axis_dim_overflow", {5000000000LL, 3}, DT_FLOAT16, 0, 2000000000LL);
189+ ExecuteTestCase(para, GRAPH_FAILED);
190+}
191+ 
192+// ============================================================================
193+// 回归: tiles 属性缺失时拒绝而非静默回退 1(GEIR.07/1 / CODE.01/1)
194+// ============================================================================
195+TEST(TileWithAxisInfershape, tiles_missing_rejected)
196+{
197+ auto para = MakeInfershapePara("tiles_missing", {2, 3, 4}, DT_FLOAT16, 1, 0, false);
198+ ExecuteTestCase(para, GRAPH_FAILED);
199+}
200+ 
162} // namespace TileWithAxisUT201} // namespace TileWithAxisUT