已合并
feat: support IndexExpr and Arange autofusion #1961
Jett_Woo创建于 13 天前
feat: support IndexExpr and Arange autofusion #1961
已合并
共 36 个文件变更+3156-121
| @@ -97,7 +97,7 @@ REG_ASC_IR(IndexExpr) | |||
| 97 | .Inputs({}) | 97 | .Inputs({}) |
| 98 | .Output("y", "T") | 98 | .Output("y", "T") |
| 99 | .StartNode() | 99 | .StartNode() |
| 100 | - .Attr<int64_t>("expr") | 100 | + .Attr<Expression>("expr") |
| 101 | .ComputeType(ComputeType::kComputeInvalid) | 101 | .ComputeType(ComputeType::kComputeInvalid) |
| 102 | .Impl(v1_soc_versions, {af::ascir::AscIrImplCreator<af::ascir::IndexExprAscIrAttImpl>(), | 102 | .Impl(v1_soc_versions, {af::ascir::AscIrImplCreator<af::ascir::IndexExprAscIrAttImpl>(), |
| 103 | af::ascir::AscIrImplCreator<af::ascir::IndexExprAscIrCodegenImpl>(), | 103 | af::ascir::AscIrImplCreator<af::ascir::IndexExprAscIrCodegenImpl>(), |
| @@ -126,6 +126,9 @@ bool CalculateDmaParams(const TPipe &tpipe, const Tensor &gm_tensor, const Tenso | |||
| 126 | const auto pos = std::find(gm_tensor.axis.begin(), gm_tensor.axis.end(), ub_tensor.vectorized_axis[vec_axis_pos]); | 126 | const auto pos = std::find(gm_tensor.axis.begin(), gm_tensor.axis.end(), ub_tensor.vectorized_axis[vec_axis_pos]); |
| 127 | GE_ASSERT_TRUE((pos != gm_tensor.axis.end()), "Codegen vectorized axis[%zu] not found", vec_axis_pos); | 127 | GE_ASSERT_TRUE((pos != gm_tensor.axis.end()), "Codegen vectorized axis[%zu] not found", vec_axis_pos); |
| 128 | const auto axis_pos = std::distance(gm_tensor.axis.begin(), pos); | 128 | const auto axis_pos = std::distance(gm_tensor.axis.begin(), pos); |
| 129 | + if (af::SymbolicUtils::StaticCheckEq(gm_tensor.axis_size[axis_pos], af::ops::One) == af::TriBool::kTrue) { | ||
| 130 | + continue; | ||
| 131 | + } | ||
| 129 | // 如果当前轴gm和ub上对应的stride均为0,如果前序轴的stride不为1,则保留当前轴 | 132 | // 如果当前轴gm和ub上对应的stride均为0,如果前序轴的stride不为1,则保留当前轴 |
| 130 | const bool ignore_zero_axis = | 133 | const bool ignore_zero_axis = |
| 131 | ascgen_utils::ShouldIgnoreDataCopyZeroAxis(has_non_zero_axis, vec_axis_pos, ub_tensor.vectorized_strides); | 134 | ascgen_utils::ShouldIgnoreDataCopyZeroAxis(has_non_zero_axis, vec_axis_pos, ub_tensor.vectorized_strides); |
| @@ -2062,8 +2062,8 @@ Status Kernel::AppendConstTensorInit(std::stringstream &ss) const { | |||
| 2062 | } | 2062 | } |
| 2063 | GELOGI("const_value_expr: %s", tensor->second.const_value_expr.Str().get()); | 2063 | GELOGI("const_value_expr: %s", tensor->second.const_value_expr.Str().get()); |
| 2064 | 2064 | ||
| 2065 | - string const_value = tensor->second.const_value_expr == 0 ? tensor->second.const_value | 2065 | + string const_value = tensor->second.const_value.empty() ? tiler.Size(tensor->second.const_value_expr, true) |
| 2066 | - : tiler.Size(tensor->second.const_value_expr, true); | 2066 | + : tensor->second.const_value; |
| 2067 | ss << tensor->second.DefineConst(const_value.c_str()) << std::endl; | 2067 | ss << tensor->second.DefineConst(const_value.c_str()) << std::endl; |
| 2068 | GELOGI("Define ss value: %s", ss.str().c_str()); | 2068 | GELOGI("Define ss value: %s", ss.str().c_str()); |
| 2069 | } | 2069 | } |
| @@ -2285,17 +2285,13 @@ Status Kernel::ParseGraph(const ascir::ImplGraph &graph, const ascir::FusedSched | |||
| 2285 | GE_CHK_STATUS_RET(kernel.tpipe.AddTensor(const_value, *output, tensor_name), "Codegen add tensor failed"); | 2285 | GE_CHK_STATUS_RET(kernel.tpipe.AddTensor(const_value, *output, tensor_name), "Codegen add tensor failed"); |
| 2286 | GE_CHK_STATUS_RET(kernel.ParseOptimizeInfo(node, *output)); | 2286 | GE_CHK_STATUS_RET(kernel.ParseOptimizeInfo(node, *output)); |
| 2287 | } else if (IsOps<IndexExpr>(node)) { | 2287 | } else if (IsOps<IndexExpr>(node)) { |
| 2288 | - int64_t size_id = 0; | 2288 | + af::Expression expr; |
| 2289 | auto ir_attr = node->attr.ir_attr.get(); | 2289 | auto ir_attr = node->attr.ir_attr.get(); |
| 2290 | - if (ir_attr->GetAttrValue("expr", size_id) != af::GRAPH_SUCCESS) { | 2290 | + if (ir_attr->GetAttrValue("expr", expr) != af::GRAPH_SUCCESS) { |
| 2291 | - GELOGE(af::FAILED, "GetAttrValue index expr failed, size_id = %lld", size_id); | 2291 | + GELOGE(af::FAILED, "GetAttrValue index expr failed"); |
| 2292 | return af::FAILED; | 2292 | return af::FAILED; |
| 2293 | } | 2293 | } |
| 2294 | - GELOGI("size_id = %lld", size_id); | 2294 | + GE_CHK_STATUS_RET(kernel.tpipe.AddTensor(*output, expr, tensor_name), "Codegen add tensor failed"); |
| 2295 | - // todo index在Expression上是指什么? 暂时按照AddSizeVar顺序 | ||
| 2296 | - auto all_sizevar = graph.GetAllSizeVar(); | ||
| 2297 | - GE_CHK_STATUS_RET(kernel.tpipe.AddTensor(*output, all_sizevar.at(size_id)->expr, tensor_name), | ||
| 2298 | - "Codegen add tensor failed"); | ||
| 2299 | } else if (IsOps<Workspace>(node)) { | 2295 | } else if (IsOps<Workspace>(node)) { |
| 2300 | GE_CHK_STATUS_RET(kernel.ParseWorkspaceTensor(output, fused_schedule_result, output_indices, | 2296 | GE_CHK_STATUS_RET(kernel.ParseWorkspaceTensor(output, fused_schedule_result, output_indices, |
| 2301 | output_tensorid_to_index, output_index_to_name), | 2297 | output_tensorid_to_index, output_index_to_name), |
| @@ -3773,8 +3769,8 @@ Status Kernel::GlobalTensorDefine(std::string &result) const { | |||
| 3773 | this->constant_tensors[i]); | 3769 | this->constant_tensors[i]); |
| 3774 | GELOGI("const_value_expr: %s", tensor->second.const_value_expr.Str().get()); | 3770 | GELOGI("const_value_expr: %s", tensor->second.const_value_expr.Str().get()); |
| 3775 | 3771 | ||
| 3776 | - string const_value = tensor->second.const_value_expr == 0 ? tensor->second.const_value | 3772 | + string const_value = tensor->second.const_value.empty() ? tiler.Size(tensor->second.const_value_expr, true) |
| 3777 | - : tiler.Size(tensor->second.const_value_expr, true); | 3773 | + : tensor->second.const_value; |
| 3778 | ss << " " << tensor->second.DefineConst(const_value.c_str()) << std::endl; | 3774 | ss << " " << tensor->second.DefineConst(const_value.c_str()) << std::endl; |
| 3779 | GELOGI("Define ss value: %s", ss.str().c_str()); | 3775 | GELOGI("Define ss value: %s", ss.str().c_str()); |
| 3780 | } | 3776 | } |
| @@ -162,7 +162,7 @@ class Tensor : public Variable { | |||
| 162 | return IsUbScalar() || IsConstScalar(); | 162 | return IsUbScalar() || IsConstScalar(); |
| 163 | } | 163 | } |
| 164 | std::string GetScalarValue() const { | 164 | std::string GetScalarValue() const { |
| 165 | - return IsUbScalar() ? ub_scalar_name : const_value; | 165 | + return IsUbScalar() ? ub_scalar_name : (const_value.empty() ? Str() : const_value); |
| 166 | } | 166 | } |
| 167 | inline ascir::SizeExpr GetTensorSize() const { | 167 | inline ascir::SizeExpr GetTensorSize() const { |
| 168 | ascir::SizeExpr size_expr = af::Symbol(1); | 168 | ascir::SizeExpr size_expr = af::Symbol(1); |
| @@ -37,6 +37,8 @@ inline constexpr char kAscBackendType[] = "AscBackend"; | |||
| 37 | inline constexpr char kIndexAttr[] = "index"; | 37 | inline constexpr char kIndexAttr[] = "index"; |
| 38 | inline constexpr char kValueAttr[] = "value"; | 38 | inline constexpr char kValueAttr[] = "value"; |
| 39 | inline constexpr char kExprAttr[] = "expr"; | 39 | inline constexpr char kExprAttr[] = "expr"; |
| 40 | +inline constexpr char kBaseAttr[] = "base"; | ||
| 41 | +inline constexpr char kStepAttr[] = "step"; | ||
| 40 | inline constexpr char kOffsetAttr[] = "offset"; | 42 | inline constexpr char kOffsetAttr[] = "offset"; |
| 41 | inline constexpr char kAxisAttr[] = "axis"; | 43 | inline constexpr char kAxisAttr[] = "axis"; |
| 42 | inline constexpr char kHasRelu[] = "has_relu"; | 44 | inline constexpr char kHasRelu[] = "has_relu"; |
| @@ -1177,14 +1179,46 @@ auto GetValidatedIrAttr(PyObject *self, const char *attr_type_name) -> AttrDefTy | |||
| 1177 | return attr ? (attr->SetMethod(ReverseConvFunc(value)), 0) : -1; \ | 1179 | return attr ? (attr->SetMethod(ReverseConvFunc(value)), 0) : -1; \ |
| 1178 | } | 1180 | } |
| 1179 | 1181 | ||
| 1182 | + | ||
| 1183 | + template <> \ | ||
| 1184 | + PyObject *OpsOperatorIrAttr<af::ascir_op::OpType, AttrName>::_getter(PyObject *self, void *closure) { \ | ||
| 1185 | + (void)closure; \ | ||
| 1186 | + auto *attr = GetValidatedIrAttr<af::ascir_op::OpType, af::ascir_op::OpType::AttrType>(self, #AttrType); \ | ||
| 1187 | + af::Expression value; \ | ||
| 1188 | + if (attr == nullptr) { \ | ||
| 1189 | + return nullptr; \ | ||
| 1190 | + } \ | ||
| 1191 | + PY_ASSERT_GRAPH_SUCCESS(attr->GetMethod(value), "%s attr %s getter failed", #OpType, AttrName); \ | ||
| 1192 | + return pyascir::SizeExpr::FromSizeExpr(value); \ | ||
| 1193 | + } \ | ||
| 1194 | + template <> \ | ||
| 1195 | + int OpsOperatorIrAttr<af::ascir_op::OpType, AttrName>::_setter(PyObject *self, PyObject *value, void *closure) { \ | ||
| 1196 | + (void)closure; \ | ||
| 1197 | + if (PyObject_IsInstance(value, reinterpret_cast<PyObject *>(&pyascir::SizeExpr::type)) != kPythonSuccess) { \ | ||
| 1198 | + PyErr_Format(PyExc_TypeError, "%s attr %s expected Expression", #OpType, AttrName); \ | ||
| 1199 | + return -1; \ | ||
| 1200 | + } \ | ||
| 1201 | + auto expression = pyascir::SizeExpr::AsExpression(value); \ | ||
| 1202 | + if (PyErr_Occurred() != nullptr) { \ | ||
| 1203 | + return -1; \ | ||
| 1204 | + } \ | ||
| 1205 | + if (!expression.IsValid()) { \ | ||
| 1206 | + PyErr_Format(PyExc_ValueError, "%s attr %s expected a valid Expression", #OpType, AttrName); \ | ||
| 1207 | + return -1; \ | ||
| 1208 | + } \ | ||
| 1209 | + auto *attr = GetValidatedIrAttr<af::ascir_op::OpType, af::ascir_op::OpType::AttrType>(self, #AttrType); \ | ||
| 1210 | + return attr ? (attr->SetMethod(expression), 0) : -1; \ | ||
| 1211 | + } | ||
| 1212 | + | ||
| 1180 | DEFINE_IR_ATTR_ACCESSORS(Data, AscDataIrAttrDef, kIndexAttr, int64_t, PyLong_Check, PyLong_FromLong, PyLong_AsLong, | 1213 | DEFINE_IR_ATTR_ACCESSORS(Data, AscDataIrAttrDef, kIndexAttr, int64_t, PyLong_Check, PyLong_FromLong, PyLong_AsLong, |
| 1181 | SetIndex, GetIndex) | 1214 | SetIndex, GetIndex) |
| 1182 | DEFINE_IR_ATTR_ACCESSORS(Output, AscOutputIrAttrDef, kIndexAttr, int64_t, PyLong_Check, PyLong_FromLong, PyLong_AsLong, | 1215 | DEFINE_IR_ATTR_ACCESSORS(Output, AscOutputIrAttrDef, kIndexAttr, int64_t, PyLong_Check, PyLong_FromLong, PyLong_AsLong, |
| 1183 | SetIndex, GetIndex) | 1216 | SetIndex, GetIndex) |
| 1184 | DEFINE_IR_ATTR_ACCESSORS(ScalarData, AscScalarDataIrAttrDef, kIndexAttr, int64_t, PyLong_Check, PyLong_FromLong, | 1217 | DEFINE_IR_ATTR_ACCESSORS(ScalarData, AscScalarDataIrAttrDef, kIndexAttr, int64_t, PyLong_Check, PyLong_FromLong, |
| 1185 | PyLong_AsLong, SetIndex, GetIndex) | 1218 | PyLong_AsLong, SetIndex, GetIndex) |
| 1186 | -DEFINE_IR_ATTR_ACCESSORS(IndexExpr, AscIndexExprIrAttrDef, kExprAttr, int64_t, PyLong_Check, PyLong_FromLong, | 1219 | +DEFINE_IR_EXPR_ATTR_ACCESSORS(IndexExpr, AscIndexExprIrAttrDef, kExprAttr, SetExpr, GetExpr) |
| 1187 | - PyLong_AsLong, SetExpr, GetExpr) | 1220 | +DEFINE_IR_EXPR_ATTR_ACCESSORS(Arange, AscArangeIrAttrDef, kBaseAttr, SetBase, GetBase) |
| 1221 | +DEFINE_IR_EXPR_ATTR_ACCESSORS(Arange, AscArangeIrAttrDef, kStepAttr, SetStep, GetStep) | ||
| 1188 | DEFINE_IR_ATTR_ACCESSORS(Gather, AscGatherIrAttrDef, kAxisAttr, int64_t, PyLong_Check, PyLong_FromLong, PyLong_AsLong, | 1222 | DEFINE_IR_ATTR_ACCESSORS(Gather, AscGatherIrAttrDef, kAxisAttr, int64_t, PyLong_Check, PyLong_FromLong, PyLong_AsLong, |
| 1189 | SetAxis, GetAxis) | 1223 | SetAxis, GetAxis) |
| 1190 | DEFINE_IR_ATTR_ACCESSORS(IndirectLoad, AscIndirectLoadIrAttrDef, kAxisAttr, int64_t, PyLong_Check, PyLong_FromLong, | 1224 | DEFINE_IR_ATTR_ACCESSORS(IndirectLoad, AscIndirectLoadIrAttrDef, kAxisAttr, int64_t, PyLong_Check, PyLong_FromLong, |
| @@ -1359,6 +1393,7 @@ const std::map<std::string, typename IrAttr<OpType>::handler> IrAttr<OpType>::at | |||
| 1359 | {"Scalar", AutoRegAttrHandle<af::ascir_op::Scalar, kValueAttr>::RegHandle}, | 1393 | {"Scalar", AutoRegAttrHandle<af::ascir_op::Scalar, kValueAttr>::RegHandle}, |
| 1360 | {"ScalarData", AutoRegAttrHandle<af::ascir_op::ScalarData, kIndexAttr>::RegHandle}, | 1394 | {"ScalarData", AutoRegAttrHandle<af::ascir_op::ScalarData, kIndexAttr>::RegHandle}, |
| 1361 | {"IndexExpr", AutoRegAttrHandle<af::ascir_op::IndexExpr, kExprAttr>::RegHandle}, | 1395 | {"IndexExpr", AutoRegAttrHandle<af::ascir_op::IndexExpr, kExprAttr>::RegHandle}, |
| 1396 | + {"Arange", AutoRegAttrHandle<af::ascir_op::Arange, kBaseAttr, kStepAttr>::RegHandle}, | ||
| 1362 | {"Output", AutoRegAttrHandle<af::ascir_op::Output, kIndexAttr>::RegHandle}, | 1397 | {"Output", AutoRegAttrHandle<af::ascir_op::Output, kIndexAttr>::RegHandle}, |
| 1363 | {"Load", AutoRegAttrHandle<af::ascir_op::Load, kOffsetAttr>::RegHandle}, | 1398 | {"Load", AutoRegAttrHandle<af::ascir_op::Load, kOffsetAttr>::RegHandle}, |
| 1364 | {"Store", AutoRegAttrHandle<af::ascir_op::Store, kOffsetAttr>::RegHandle}, | 1399 | {"Store", AutoRegAttrHandle<af::ascir_op::Store, kOffsetAttr>::RegHandle}, |
| @@ -35,6 +35,7 @@ PyMODINIT_FUNC PyInit_pyautofuse(void); | |||
| 35 | OP(Workspace) \ | 35 | OP(Workspace) \ |
| 36 | OP(Output) \ | 36 | OP(Output) \ |
| 37 | OP(IndexExpr) \ | 37 | OP(IndexExpr) \ |
| 38 | + OP(Arange) \ | ||
| 38 | OP(Load) \ | 39 | OP(Load) \ |
| 39 | OP(Broadcast) \ | 40 | OP(Broadcast) \ |
| 40 | OP(Store) \ | 41 | OP(Store) \ |
| @@ -340,6 +340,19 @@ const af::Expression SizeExpr::AsSizeExpr(PyObject *obj) { | |||
| 340 | return af::Expression(); | 340 | return af::Expression(); |
| 341 | } | 341 | } |
| 342 | 342 | ||
| 343 | +const af::Expression SizeExpr::AsExpression(PyObject *obj) { | ||
| 344 | + if (PyObject_IsInstance(obj, reinterpret_cast<PyObject *>(&SizeExpr::type)) == kPythonSuccess) { | ||
| 345 | + auto size = reinterpret_cast<SizeExpr::Object *>(obj); | ||
| 346 | + if (size->expression == nullptr) { | ||
| 347 | + PyErr_SetString(PyExc_ValueError, "Expression is not initialized"); | ||
| 348 | + return af::Expression(); | ||
| 349 | + } | ||
| 350 | + return af::Expression(*size->expression); | ||
| 351 | + } | ||
| 352 | + PyErr_Format(PyExc_TypeError, "Only support type of Expression"); | ||
| 353 | + return af::Expression(); | ||
| 354 | +} | ||
| 355 | + | ||
| 343 | PyObject *SizeExpr::Add(PyObject *self, PyObject *args) { | 356 | PyObject *SizeExpr::Add(PyObject *self, PyObject *args) { |
| 344 | af::Expression left = SizeExpr::AsSizeExpr(self); | 357 | af::Expression left = SizeExpr::AsSizeExpr(self); |
| 345 | PY_ASSERT_TRUE(left.IsValid(), "left operand of add is not a valid SizeExpr"); | 358 | PY_ASSERT_TRUE(left.IsValid(), "left operand of add is not a valid SizeExpr"); |
| @@ -39,6 +39,7 @@ class SizeExpr { | |||
| 39 | static int Init(PyObject *self_pyobject, const af::Expression &expr); | 39 | static int Init(PyObject *self_pyobject, const af::Expression &expr); |
| 40 | static PyObject *FromSizeExpr(const af::Expression &expr); | 40 | static PyObject *FromSizeExpr(const af::Expression &expr); |
| 41 | static const af::Expression AsSizeExpr(PyObject *obj); | 41 | static const af::Expression AsSizeExpr(PyObject *obj); |
| 42 | + static const af::Expression AsExpression(PyObject *obj); | ||
| 42 | static PyObject *Add(PyObject *self, PyObject *args); | 43 | static PyObject *Add(PyObject *self, PyObject *args); |
| 43 | static PyObject *Mul(PyObject *self, PyObject *args); | 44 | static PyObject *Mul(PyObject *self, PyObject *args); |
| 44 | static PyObject *Div(PyObject *self, PyObject *args); | 45 | static PyObject *Div(PyObject *self, PyObject *args); |
| @@ -263,8 +263,13 @@ def Output( | |||
| 263 | 263 | ||
| 264 | 264 | ||
| 265 | def IndexExpr( | 265 | def IndexExpr( |
| 266 | - owner_graph: ascir.HintGraph, *, dtype: ascir.dtypes, expr: Optional[int] = None | 266 | + owner_graph: ascir.HintGraph, |
Z | |||
| 267 | + *, | ||
| 268 | + dtype: ascir.dtypes, | ||
| 269 | + expr: ascir.SizeExpr, | ||
| 267 | ) -> ascir.OpsOperatorOutput: | 270 | ) -> ascir.OpsOperatorOutput: |
| 271 | + if not isinstance(expr, ascir.SizeExpr): | ||
| 272 | + raise TypeError("IndexExpr expr must be SizeExpr") | ||
| 268 | meta = _get_metadata(owner_graph) | 273 | meta = _get_metadata(owner_graph) |
| 269 | name = _generate_op_name(owner_graph, "indexexpr") | 274 | name = _generate_op_name(owner_graph, "indexexpr") |
| 270 | op = ascir.ops.IndexExpr(name, owner_graph) | 275 | op = ascir.ops.IndexExpr(name, owner_graph) |
| @@ -275,6 +280,55 @@ def IndexExpr( | |||
| 275 | return op.y | 280 | return op.y |
| 276 | 281 | ||
| 277 | 282 | ||
| 283 | +def arange( | ||
| 284 | + owner_graph: ascir.HintGraph, | ||
| 285 | + *, | ||
| 286 | + dtype: ascir.dtypes, | ||
| 287 | + base: ascir.SizeExpr, | ||
| 288 | + step: ascir.SizeExpr, | ||
| 289 | + axis: List[ascir.Axis], | ||
| 290 | + size: Optional[List[ascir.SizeExpr]] = None, | ||
| 291 | + stride: Optional[List[ascir.SizeExpr]] = None, | ||
| 292 | +) -> ascir.OpsOperatorOutput: | ||
| 293 | + if not isinstance(base, ascir.SizeExpr) or not isinstance(step, ascir.SizeExpr): | ||
| 294 | + raise TypeError("Arange base and step must be SizeExpr") | ||
| 295 | + if not axis: | ||
| 296 | + raise ValueError("Arange axis must not be empty") | ||
| 297 | + if stride is not None and len(stride) != len(axis): | ||
| 298 | + raise ValueError("stride should be same with axis len") | ||
| 299 | + if stride is not None: | ||
| 300 | + last_nonzero = next( | ||
| 301 | + ( | ||
| 302 | + (index, value) | ||
| 303 | + for index, value in reversed(list(enumerate(stride))) | ||
| 304 | + if not (value == ascir.SizeExpr(0)) | ||
| 305 | + ), | ||
| 306 | + None, | ||
| 307 | + ) | ||
| 308 | + if last_nonzero is not None and not (last_nonzero[1] == ascir.SizeExpr(1)): | ||
| 309 | + raise ValueError( | ||
| 310 | + "Arange requires unit physical stride on the vectorized axis" | ||
| 311 | + ) | ||
| 312 | + effective_size = size if size is not None else [item.size for item in axis] | ||
| 313 | + singleton_start = 0 if last_nonzero is None else last_nonzero[0] + 1 | ||
| 314 | + if any( | ||
| 315 | + not (value == ascir.SizeExpr(1)) | ||
| 316 | + for value in effective_size[singleton_start:] | ||
| 317 | + ): | ||
| 318 | + raise ValueError("Arange zero strides require singleton dimensions") | ||
| 319 | + meta = _get_metadata(owner_graph) | ||
| 320 | + name = _generate_op_name(owner_graph, "arange") | ||
| 321 | + op = ascir.ops.Arange(name, owner_graph) | ||
| 322 | + meta.ops.append(op) | ||
| 323 | + op.attr.ir_attr.base = base | ||
| 324 | + op.attr.ir_attr.step = step | ||
| 325 | + op.y.dtype = dtype | ||
| 326 | + _infer_or_set_view(op.y, axis, size, stride) | ||
| 327 | + op.attr.sched.axis = axis | ||
| 328 | + op.infer_dtype() | ||
| 329 | + return op.y | ||
| 330 | + | ||
| 331 | + | ||
| 278 | def Load( | 332 | def Load( |
| 279 | owner_graph: ascir.HintGraph, | 333 | owner_graph: ascir.HintGraph, |
| 280 | x: ascir.OpsOperatorOutput, | 334 | x: ascir.OpsOperatorOutput, |
| @@ -380,6 +380,22 @@ Status BufQueAllocator::SetOutputTensorAttr(const af::AscGraph &impl_graph) cons | |||
| 380 | } | 380 | } |
| 381 | } | 381 | } |
| 382 | } | 382 | } |
| 383 | + | ||
| 384 | + // VF子图中的常量Scalar副本在分区时拷贝了未分配的tensor_id, 此处按命名映射回填根图分配结果。 | ||
| 385 | + constexpr std::string_view kSubgraphScalarPrefix = "Scalar_"; | ||
| 386 | + std::vector<af::AscGraph> sub_graphs; | ||
| 387 | + GE_ASSERT_GRAPH_SUCCESS(impl_graph.GetAllSubGraphs(sub_graphs)); | ||
| 388 | + for (auto &sub_graph : sub_graphs) { | ||
| 389 | + for (const auto &node : sub_graph.GetAllNodes()) { | ||
| 390 | + if (!IsOps<Scalar>(node) || node->GetName().size() <= kSubgraphScalarPrefix.size()) { | ||
| 391 | + continue; | ||
| 392 | + } | ||
| 393 | + const auto root_node = impl_graph.FindNode(node->GetName().substr(kSubgraphScalarPrefix.size()).c_str()); | ||
| 394 | + if ((root_node != nullptr) && (IsOps<Scalar>(root_node) || IsOps<IndexExpr>(root_node))) { | ||
| 395 | + node->outputs[0].attr.mem.tensor_id = root_node->outputs[0].attr.mem.tensor_id; | ||
| 396 | + } | ||
| 397 | + } | ||
| 398 | + } | ||
| 383 | return ge::GRAPH_SUCCESS; | 399 | return ge::GRAPH_SUCCESS; |
| 384 | } | 400 | } |
| 385 | 401 | ||
| @@ -14,6 +14,7 @@ | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | + | ||
| 17 | 18 | ||
| 18 | namespace optimize { | 19 | namespace optimize { |
| 19 | namespace { | 20 | namespace { |
| @@ -21,6 +22,15 @@ bool ShouldSkipRegularAlignment(const af::AscNodePtr &node) { | |||
| 21 | return ScheduleUtils::IsBuffer(node) || | 22 | return ScheduleUtils::IsBuffer(node) || |
| 22 | ascgen_utils::indirect_load::GetTemplateBehavior(node).uses_direct_gm_pipeline; | 23 | ascgen_utils::indirect_load::GetTemplateBehavior(node).uses_direct_gm_pipeline; |
| 23 | } | 24 | } |
| 25 | + | ||
| 26 | +bool IsLegalZeroInputNode(const af::AscNodePtr &node) { | ||
| 27 | + if (!node->inputs().empty()) { | ||
| 28 | + return true; | ||
| 29 | + } | ||
| 30 | + const auto &definitions = af::ascir::AscirRegistry::GetInstance().GetAll(); | ||
| 31 | + const auto definition = definitions.find(node->GetType()); | ||
| 32 | + return definition != definitions.end() && definition->second.IsStartNode(); | ||
| 33 | +} | ||
| 24 | } // namespace | 34 | } // namespace |
| 25 | 35 | ||
| 26 | const char *BaseAlignmentStrategy::AlignmentTypeToString(AlignmentType type) { | 36 | const char *BaseAlignmentStrategy::AlignmentTypeToString(AlignmentType type) { |
| @@ -391,10 +401,8 @@ af::Status BaseAlignmentStrategy::AlignVectorizedStrides(ascir::ImplGraph &impl_ | |||
| 391 | } | 401 | } |
| 392 | 402 | ||
| 393 | af::Status BaseAlignmentStrategy::InferAlignmentForOneNode(ascir::ImplGraph &, const af::AscNodePtr &node, bool &) { | 403 | af::Status BaseAlignmentStrategy::InferAlignmentForOneNode(ascir::ImplGraph &, const af::AscNodePtr &node, bool &) { |
| 394 | - GE_ASSERT_TRUE( | ||
| 395 | - !node->inputs().empty() || af::ops::IsOps<af::ascir_op::Rand>(node) || af::ops::IsOps<af::ascir_op::Randn>(node), | ||
| 396 | - "The inputs of %s(%s) is empty.", node->GetTypePtr(), node->GetNamePtr()); | ||
| 397 | GE_ASSERT_TRUE(!node->outputs().empty(), "The output of %s(%s) is empty.", node->GetTypePtr(), node->GetNamePtr()); | 404 | GE_ASSERT_TRUE(!node->outputs().empty(), "The output of %s(%s) is empty.", node->GetTypePtr(), node->GetNamePtr()); |
| 405 | + GE_ASSERT_TRUE(IsLegalZeroInputNode(node), "The inputs of %s(%s) are empty.", node->GetTypePtr(), node->GetNamePtr()); | ||
| 398 | af::ComputeType compute_type = node->attr.api.compute_type; | 406 | af::ComputeType compute_type = node->attr.api.compute_type; |
| 399 | auto it = compute_type_to_infer_func_.find(compute_type); | 407 | auto it = compute_type_to_infer_func_.find(compute_type); |
| 400 | if (it != compute_type_to_infer_func_.end()) { | 408 | if (it != compute_type_to_infer_func_.end()) { |
| @@ -88,8 +88,10 @@ class ScheduleUtils { | |||
| 88 | static bool HasComputeType(const ascir::ImplGraph &impl_graph, const af::ComputeType compute_type); | 88 | static bool HasComputeType(const ascir::ImplGraph &impl_graph, const af::ComputeType compute_type); |
| 89 | 89 | ||
| 90 | static bool IsIOBuffer(const af::NodePtr &node) { | 90 | static bool IsIOBuffer(const af::NodePtr &node) { |
| 91 | - return af::ops::IsOps<af::ascir_op::Scalar>(node) || IsDataInput(node) || | 91 | + // IndexExpr 是无输入标量表达式节点(sched.axis 为空), 与 Scalar 同属不参与 |
| 92 | - af::ops::IsOps<af::ascir_op::Output>(node); | 92 | + // 全 reduce 展维的标量路径, 需一并跳过, 否则空 axis 触发展维断言。 |
| 93 | + return af::ops::IsOps<af::ascir_op::Scalar>(node) || af::ops::IsOps<af::ascir_op::IndexExpr>(node) || | ||
| 94 | + IsDataInput(node) || af::ops::IsOps<af::ascir_op::Output>(node); | ||
| 93 | } | 95 | } |
| 94 | 96 | ||
| 95 | static bool IsDataInput(const af::NodePtr &node) { | 97 | static bool IsDataInput(const af::NodePtr &node) { |
| @@ -100,12 +102,17 @@ class ScheduleUtils { | |||
| 100 | return af::ops::IsOps<af::ascir_op::Data>(node) || af::ops::IsOps<af::ascir_op::ScalarData>(node); | 102 | return af::ops::IsOps<af::ascir_op::Data>(node) || af::ops::IsOps<af::ascir_op::ScalarData>(node); |
| 101 | } | 103 | } |
| 102 | 104 | ||
| 105 | + // IndexExpr 是标量表达式节点(输出无 axis/repeats), 与 Scalar/ScalarData 同属 | ||
| 106 | + // 标量语义; 标量路径的调度/缓存判定需将其一并识别, 否则以标量为输入的节点 | ||
| 107 | + // 会因空 axis 与输出轴数不一致而触发断言。 | ||
| 103 | static bool IsScalarLikeNode(const af::NodePtr &node) { | 108 | static bool IsScalarLikeNode(const af::NodePtr &node) { |
| 104 | - return af::ops::IsOps<af::ascir_op::Scalar>(node) || af::ops::IsOps<af::ascir_op::ScalarData>(node); | 109 | + return af::ops::IsOps<af::ascir_op::Scalar>(node) || af::ops::IsOps<af::ascir_op::ScalarData>(node) || |
| 110 | + af::ops::IsOps<af::ascir_op::IndexExpr>(node); | ||
| 105 | } | 111 | } |
| 106 | 112 | ||
| 107 | static bool IsScalarLikeNode(const af::Node *const node) { | 113 | static bool IsScalarLikeNode(const af::Node *const node) { |
| 108 | - return af::ops::IsOps<af::ascir_op::Scalar>(node) || af::ops::IsOps<af::ascir_op::ScalarData>(node); | 114 | + return af::ops::IsOps<af::ascir_op::Scalar>(node) || af::ops::IsOps<af::ascir_op::ScalarData>(node) || |
| 115 | + af::ops::IsOps<af::ascir_op::IndexExpr>(node); | ||
| 109 | } | 116 | } |
| 110 | 117 | ||
| 111 | static bool IsConstantScalar(const af::Node *const node) { | 118 | static bool IsConstantScalar(const af::Node *const node) { |
| @@ -46,3 +46,14 @@ target_link_libraries(test_ascir_ut | |||
| 46 | ascir_builtin_ops | 46 | ascir_builtin_ops |
| 47 | #ge_compiler | 47 | #ge_compiler |
| 48 | GTest::gtest) | 48 | GTest::gtest) |
| 49 | + | ||
| 50 | +add_executable(test_index_expr_arange_schema test_index_expr_arange_schema.cpp) | ||
| 51 | +target_link_libraries(test_index_expr_arange_schema PRIVATE | ||
| 52 | + ascir | ||
| 53 | + ascir_builtin_ops | ||
| 54 | + ascir_ops_headers | ||
| 55 | + aihac_symbolizer_af | ||
| 56 | + metadef | ||
| 57 | + GTest::gtest | ||
| 58 | + GTest::gtest_main) | ||
| 59 | +gtest_discover_tests(test_index_expr_arange_schema) | ||
| @@ -0,0 +1,105 @@ | |||
| 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 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +TEST(IndexExprSchema, StoresExpressionAttribute) { | ||
| 17 | + af::AscGraph graph("index_expr_schema"); | ||
| 18 | + const auto expr = af::Expression::Parse("s0 + 2"); | ||
| 19 | + | ||
| 20 | + (void)af::ascir::cg::IndexExpr("index", graph, af::DT_INT64, {}, {}, {}, expr); | ||
| 21 | + | ||
| 22 | + auto index_node = graph.FindNode("index"); | ||
| 23 | + ASSERT_NE(index_node, nullptr); | ||
| 24 | + const auto &index_attr = | ||
| 25 | + dynamic_cast<const af::ascir_op::IndexExpr::AscIndexExprIrAttrDef &>(*index_node->attr.ir_attr); | ||
| 26 | + af::Expression stored_expr; | ||
| 27 | + ASSERT_EQ(index_attr.GetExpr(stored_expr), af::GRAPH_SUCCESS); | ||
| 28 | + EXPECT_EQ(stored_expr, expr); | ||
| 29 | + EXPECT_EQ(index_node->outputs[0].attr.dtype, af::DT_INT64); | ||
| 30 | + EXPECT_TRUE(index_node->outputs[0].attr.axis.empty()); | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +TEST(IndexExprSchema, SupportsOnlyV2Int32AndInt64) { | ||
| 34 | + for (const auto dtype : {af::DT_INT32, af::DT_INT64}) { | ||
| 35 | + for (const auto *npu_arch : {"3510", "5102"}) { | ||
| 36 | + std::vector<af::DataType> output_dtypes{dtype}; | ||
| 37 | + EXPECT_EQ(af::ascir_op::IndexExpr::InferDataType({}, output_dtypes, npu_arch), af::SUCCESS); | ||
| 38 | + } | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + std::vector<af::DataType> float_output{af::DT_FLOAT}; | ||
| 42 | + EXPECT_NE(af::ascir_op::IndexExpr::InferDataType({}, float_output, "3510"), af::SUCCESS); | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | +TEST(IndexExprSchema, MissingExpressionAttributeFails) { | ||
| 46 | + af::AscGraph graph("missing_index_expr"); | ||
| 47 | + af::ascir_op::IndexExpr index("index", graph); | ||
| 48 | + af::Expression expr; | ||
| 49 | + | ||
| 50 | + EXPECT_NE(index.ir_attr.GetExpr(expr), af::GRAPH_SUCCESS); | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +TEST(ArangeSchema, StoresAttributesAndOneDimensionalLaneView) { | ||
| 54 | + af::AscGraph graph("arange_schema"); | ||
| 55 | + const auto lane_count = graph.CreateSizeVar("lane_count"); | ||
| 56 | + const auto lane = graph.CreateAxis("lane", lane_count); | ||
| 57 | + const auto base = af::Expression::Parse("s0 + 1"); | ||
| 58 | + const auto step = af::Expression::Parse("2"); | ||
| 59 | + | ||
| 60 | + (void)af::ascir::cg::Arange("arange", graph, af::DT_INT32, {lane.id}, std::vector<af::Expression>{lane_count}, | ||
| 61 | + std::vector<af::Expression>{af::Expression::Parse("1")}, base, step); | ||
| 62 | + | ||
| 63 | + auto arange_node = graph.FindNode("arange"); | ||
| 64 | + ASSERT_NE(arange_node, nullptr); | ||
| 65 | + const auto &arange_attr = dynamic_cast<const af::ascir_op::Arange::AscArangeIrAttrDef &>(*arange_node->attr.ir_attr); | ||
| 66 | + af::Expression stored_base; | ||
| 67 | + af::Expression stored_step; | ||
| 68 | + ASSERT_EQ(arange_attr.GetBase(stored_base), af::GRAPH_SUCCESS); | ||
| 69 | + ASSERT_EQ(arange_attr.GetStep(stored_step), af::GRAPH_SUCCESS); | ||
| 70 | + EXPECT_EQ(stored_base, base); | ||
| 71 | + EXPECT_EQ(stored_step, step); | ||
| 72 | + EXPECT_EQ(arange_node->outputs[0].attr.dtype, af::DT_INT32); | ||
| 73 | + EXPECT_EQ(arange_node->outputs[0].attr.axis, std::vector<af::AxisId>{lane.id}); | ||
| 74 | + EXPECT_EQ(arange_node->outputs[0].attr.repeats, std::vector<af::Expression>{lane_count}); | ||
| 75 | + EXPECT_EQ(arange_node->outputs[0].attr.strides, std::vector<af::Expression>{af::Expression::Parse("1")}); | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +TEST(ArangeSchema, SupportsOnlyV2Int32AndInt64) { | ||
| 79 | + for (const auto dtype : {af::DT_INT32, af::DT_INT64}) { | ||
| 80 | + for (const auto *npu_arch : {"3510", "5102"}) { | ||
| 81 | + std::vector<af::DataType> output_dtypes{dtype}; | ||
| 82 | + EXPECT_EQ(af::ascir_op::Arange::InferDataType({}, output_dtypes, npu_arch), af::SUCCESS); | ||
| 83 | + } | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + std::vector<af::DataType> float_output{af::DT_FLOAT}; | ||
| 87 | + const auto invalid_dtype_status = af::ascir_op::Arange::InferDataType({}, float_output, "3510"); | ||
| 88 | + EXPECT_NE(invalid_dtype_status, af::SUCCESS); | ||
| 89 | + | ||
| 90 | + std::vector<af::DataType> v1_output{af::DT_INT32}; | ||
| 91 | + EXPECT_NE(af::ascir_op::Arange::InferDataType({}, v1_output, "2201"), af::SUCCESS); | ||
| 92 | + | ||
| 93 | + std::vector<af::DataType> unknown_platform_output{af::DT_INT64}; | ||
| 94 | + EXPECT_NE(af::ascir_op::Arange::InferDataType({}, unknown_platform_output, "unknown"), af::SUCCESS); | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +TEST(ArangeSchema, MissingBaseOrStepAttributeFails) { | ||
| 98 | + af::AscGraph graph("missing_arange_attrs"); | ||
| 99 | + af::ascir_op::Arange arange("arange", graph); | ||
| 100 | + af::Expression base; | ||
| 101 | + af::Expression step; | ||
| 102 | + | ||
| 103 | + EXPECT_NE(arange.ir_attr.GetBase(base), af::GRAPH_SUCCESS); | ||
| 104 | + EXPECT_NE(arange.ir_attr.GetStep(step), af::GRAPH_SUCCESS); | ||
| 105 | +} | ||
| @@ -127,7 +127,7 @@ TEST(CodegenKernel, StoreApiCall_TwoStoreOneOutput) { | |||
| 127 | 127 | ||
| 128 | std::string result; | 128 | std::string result; |
| 129 | call_0.Generate(tpipe, vector<af::AxisId>{}, result); | 129 | call_0.Generate(tpipe, vector<af::AxisId>{}, result); |
| 130 | - EXPECT_EQ(result, std::string{"DataCopyPadExtend(local_1[0 + 0], local_0, 1, 1, 16 - 1, 0);\n"}); | 130 | + EXPECT_EQ(result, std::string{"DataCopyPadExtend(local_1[0 + 0], local_0, 1, 1, 0, 0);\n"}); |
| 131 | 131 | ||
| 132 | codegen::StoreApiCall call_1("Store"); | 132 | codegen::StoreApiCall call_1("Store"); |
| 133 | EXPECT_EQ(call_1.Init(store_1), 0); | 133 | EXPECT_EQ(call_1.Init(store_1), 0); |
| @@ -238,7 +238,7 @@ TEST(CodegenKernel, StoreApiCall_NeetMte3SyncMte2) { | |||
| 238 | call_1.inputs.push_back(&x1); | 238 | call_1.inputs.push_back(&x1); |
| 239 | x1.reads.push_back(&call_1); | 239 | x1.reads.push_back(&call_1); |
| 240 | call_1.Generate(kernel.tpipe, vector<af::AxisId>{}, result); | 240 | call_1.Generate(kernel.tpipe, vector<af::AxisId>{}, result); |
| 241 | - EXPECT_EQ(result, std::string{"DataCopyPadExtend(local_1[0 + 0], local_0, 1, 1, 16 - 1, 0);\n" | 241 | + EXPECT_EQ(result, std::string{"DataCopyPadExtend(local_1[0 + 0], local_0, 1, 1, 0, 0);\n" |
| 242 | "auto local_0_e_mte3_2_mte2_t_0 = tpipe.AllocEventID<HardEvent::MTE3_MTE2>();\n" | 242 | "auto local_0_e_mte3_2_mte2_t_0 = tpipe.AllocEventID<HardEvent::MTE3_MTE2>();\n" |
| 243 | "TQueSync<PIPE_MTE3, PIPE_MTE2> local_0_s_mte3_2_mte2_t_0;\n" | 243 | "TQueSync<PIPE_MTE3, PIPE_MTE2> local_0_s_mte3_2_mte2_t_0;\n" |
| 244 | "local_0_s_mte3_2_mte2_t_0.SetFlag(local_0_e_mte3_2_mte2_t_0);\n" | 244 | "local_0_s_mte3_2_mte2_t_0.SetFlag(local_0_e_mte3_2_mte2_t_0);\n" |
| @@ -4192,11 +4192,12 @@ TEST(CodegenKernel, Kernel_ConstantTensorInit) { | |||
| 4192 | TEST(CodegenKernel, Kernel_IndexExprTensorInit) { | 4192 | TEST(CodegenKernel, Kernel_IndexExprTensorInit) { |
| 4193 | af::AscGraph graph("test_graph"); | 4193 | af::AscGraph graph("test_graph"); |
| 4194 | auto s0 = graph.CreateSizeVar("s0"); | 4194 | auto s0 = graph.CreateSizeVar("s0"); |
| 4195 | + auto s1 = graph.CreateSizeVar("s1"); | ||
| 4195 | 4196 | ||
| 4196 | af::ascir_op::IndexExpr index("index"); | 4197 | af::ascir_op::IndexExpr index("index"); |
| 4197 | graph.AddNode(index); | 4198 | graph.AddNode(index); |
| 4198 | - index.ir_attr.SetExpr(0); | 4199 | + index.ir_attr.SetExpr(s0 + s1 * af::Symbol(2)); |
| 4199 | - index.y.dtype = ge::DT_FLOAT16; | 4200 | + index.y.dtype = ge::DT_INT64; |
| 4200 | 4201 | ||
| 4201 | // graph.SetInputs({index}); | 4202 | // graph.SetInputs({index}); |
| 4202 | 4203 | ||
| @@ -4210,7 +4211,96 @@ TEST(CodegenKernel, Kernel_IndexExprTensorInit) { | |||
| 4210 | codegen::Kernel::ParseGraph(graph, fused_schedule_result, kernel); | 4211 | codegen::Kernel::ParseGraph(graph, fused_schedule_result, kernel); |
| 4211 | std::string result; | 4212 | std::string result; |
| 4212 | kernel.GlobalTensorInit(result); | 4213 | kernel.GlobalTensorInit(result); |
| 4213 | - EXPECT_EQ(result, std::string{"const half scalar_0 = (t->s0)/(1);\n"}); | 4214 | + EXPECT_EQ(result, std::string{"const int64_t scalar_0 = (((2 * t->s1) + t->s0))/(1);\n"}); |
| 4215 | +} | ||
| 4216 | + | ||
| 4217 | +TEST(CodegenKernel, Kernel_ZeroIndexExprTensorInit) { | ||
| 4218 | + af::AscGraph graph("test_graph"); | ||
| 4219 | + af::ascir_op::IndexExpr index("index", graph); | ||
| 4220 | + index.ir_attr.SetExpr(af::Symbol(0)); | ||
| 4221 | + index.y.dtype = ge::DT_INT32; | ||
| 4222 | + | ||
| 4223 | + auto index_node = graph.FindNode("index"); | ||
| 4224 | + index_node->outputs[0].attr.mem.alloc_type = af::AllocType::kAllocTypeInvalid; | ||
| 4225 | + index_node->outputs[0].attr.mem.tensor_id = 0; | ||
| 4226 | + index_node->outputs[0].attr.mem.position = af::Position::kPositionInvalid; | ||
| 4227 | + | ||
| 4228 | + ::ascir::FusedScheduledResult fused_schedule_result; | ||
| 4229 | + codegen::Kernel kernel(graph.GetName()); | ||
| 4230 | + ASSERT_EQ(codegen::Kernel::ParseGraph(graph, fused_schedule_result, kernel), af::SUCCESS); | ||
| 4231 | + std::string result; | ||
| 4232 | + ASSERT_EQ(kernel.GlobalTensorInit(result), af::SUCCESS); | ||
| 4233 | + EXPECT_EQ(result, std::string{"const int32_t scalar_0 = 0;\n"}); | ||
| 4234 | + ASSERT_NE(kernel.tpipe.GetTensor(0), nullptr); | ||
| 4235 | + EXPECT_EQ(kernel.tpipe.GetTensor(0)->GetScalarValue(), "scalar_0"); | ||
| 4236 | +} | ||
| 4237 | + | ||
| 4238 | +TEST(CodegenKernel, Kernel_IndexExprScalarConsumerDoesNotAllocateVectorTensor) { | ||
| 4239 | + af::AscGraph graph("index_expr_scalar_consumer"); | ||
| 4240 | + const auto s1 = graph.CreateSizeVar("s1"); | ||
| 4241 | + const auto z0 = graph.CreateAxis("z0", s1); | ||
| 4242 | + af::ascir_op::Data data("data", graph); | ||
| 4243 | + data.ir_attr.SetIndex(0); | ||
| 4244 | + data.y.dtype = ge::DT_INT32; | ||
| 4245 | + af::ascir_op::Load load("load"); | ||
| 4246 | + graph.AddNode(load); | ||
| 4247 | + load.x = data.y; | ||
| 4248 | + load.y.dtype = ge::DT_INT32; | ||
| 4249 | + af::ascir_op::IndexExpr index("index", graph); | ||
| 4250 | + index.ir_attr.SetExpr(s1 + af::Symbol(2)); | ||
| 4251 | + index.y.dtype = ge::DT_INT32; | ||
| 4252 | + af::ascir_op::Add add("add"); | ||
| 4253 | + graph.AddNode(add); | ||
| 4254 | + add.x1 = load.y; | ||
| 4255 | + add.x2 = index.y; | ||
| 4256 | + add.y.dtype = ge::DT_INT32; | ||
| 4257 | + | ||
| 4258 | + auto data_node = graph.FindNode("data"); | ||
| 4259 | + auto load_node = graph.FindNode("load"); | ||
| 4260 | + auto index_node = graph.FindNode("index"); | ||
| 4261 | + auto add_node = graph.FindNode("add"); | ||
| 4262 | + data_node->outputs[0].attr.mem.tensor_id = 0; | ||
| 4263 | + data_node->outputs[0].attr.mem.alloc_type = af::AllocType::kAllocTypeGlobal; | ||
| 4264 | + load_node->outputs[0].attr.axis = {z0.id}; | ||
| 4265 | + load_node->outputs[0].attr.vectorized_axis = {z0.id}; | ||
| 4266 | + load_node->outputs[0].attr.vectorized_strides = {One}; | ||
| 4267 | + load_node->outputs[0].attr.repeats = {s1}; | ||
| 4268 | + load_node->outputs[0].attr.strides = {One}; | ||
| 4269 | + load_node->outputs[0].attr.mem.tensor_id = 1; | ||
| 4270 | + load_node->outputs[0].attr.mem.alloc_type = af::AllocType::kAllocTypeBuffer; | ||
| 4271 | + load_node->outputs[0].attr.mem.position = af::Position::kPositionVecIn; | ||
| 4272 | + load_node->outputs[0].attr.buf.id = 0; | ||
| 4273 | + index_node->outputs[0].attr.mem.tensor_id = 2; | ||
| 4274 | + index_node->outputs[0].attr.mem.alloc_type = af::AllocType::kAllocTypeInvalid; | ||
| 4275 | + index_node->outputs[0].attr.mem.position = af::Position::kPositionInvalid; | ||
| 4276 | + add_node->outputs[0].attr.axis = {z0.id}; | ||
| 4277 | + add_node->outputs[0].attr.vectorized_axis = {z0.id}; | ||
| 4278 | + add_node->outputs[0].attr.vectorized_strides = {One}; | ||
| 4279 | + add_node->outputs[0].attr.repeats = {s1}; | ||
| 4280 | + add_node->outputs[0].attr.strides = {One}; | ||
| 4281 | + add_node->outputs[0].attr.mem.tensor_id = 3; | ||
| 4282 | + add_node->outputs[0].attr.mem.alloc_type = af::AllocType::kAllocTypeBuffer; | ||
| 4283 | + add_node->outputs[0].attr.mem.position = af::Position::kPositionVecCalc; | ||
| 4284 | + add_node->outputs[0].attr.buf.id = 1; | ||
| 4285 | + | ||
| 4286 | + ::ascir::FusedScheduledResult fused_schedule_result; | ||
| 4287 | + fused_schedule_result.input_nodes.push_back(data_node); | ||
| 4288 | + codegen::Kernel kernel(graph.GetName()); | ||
| 4289 | + ASSERT_EQ(codegen::Kernel::ParseGraph(graph, fused_schedule_result, kernel), af::SUCCESS); | ||
| 4290 | + ASSERT_NE(kernel.tpipe.GetTensor(2), nullptr); | ||
| 4291 | + EXPECT_TRUE(kernel.tpipe.GetTensor(2)->is_constant); | ||
| 4292 | + | ||
| 4293 | + std::string scalar_init; | ||
| 4294 | + ASSERT_EQ(kernel.GlobalTensorInit(scalar_init), af::SUCCESS); | ||
| 4295 | + EXPECT_NE(scalar_init.find("const int32_t scalar_2 = ((2 + t->s1))/(1);"), std::string::npos); | ||
| 4296 | + | ||
| 4297 | + std::string local_tensors; | ||
| 4298 | + ASSERT_EQ(kernel.tpipe.LocalTensorDefine(local_tensors), af::SUCCESS); | ||
| 4299 | + EXPECT_EQ(local_tensors.find("LocalTensor<int32_t> local_2"), std::string::npos); | ||
| 4300 | + | ||
| 4301 | + std::string consumer_code; | ||
| 4302 | + ASSERT_EQ(kernel.root_loop.Generate(kernel.tiler, kernel.tpipe, consumer_code), af::SUCCESS); | ||
| 4303 | + EXPECT_NE(consumer_code.find("Adds(local_3[0], local_1[0], (int32_t)scalar_2"), std::string::npos) << consumer_code; | ||
| 4214 | } | 4304 | } |
| 4215 | 4305 | ||
| 4216 | TEST(CodegenKernel, Kernel_KernelFunctionDeclare) { | 4306 | TEST(CodegenKernel, Kernel_KernelFunctionDeclare) { |
| @@ -487,3 +487,63 @@ TEST(CodegenLoadStore, CalculateDmaParams_LastAxisDisContinuous) { | |||
| 487 | EXPECT_EQ(param.gm_strides.size(), 2); | 487 | EXPECT_EQ(param.gm_strides.size(), 2); |
| 488 | EXPECT_EQ(param.ub_strides.size(), 2); | 488 | EXPECT_EQ(param.ub_strides.size(), 2); |
| 489 | } | 489 | } |
| 490 | + | ||
| 491 | +TEST(CodegenLoadStore, CalculateDmaParams_IgnoresStaticSingletonAxes) { | ||
| 492 | + af::Axis z0{.id = 0, .name = "z0", .type = af::Axis::Type::kAxisTypeOriginal, .size = af::Symbol(8)}; | ||
| 493 | + af::Axis z1{.id = 1, .name = "z1", .type = af::Axis::Type::kAxisTypeOriginal, .size = One}; | ||
| 494 | + af::Axis z2{.id = 2, .name = "z2", .type = af::Axis::Type::kAxisTypeOriginal, .size = One}; | ||
| 495 | + codegen::Tiler tiler; | ||
| 496 | + tiler.AddAxis(z0); | ||
| 497 | + tiler.AddAxis(z1); | ||
| 498 | + tiler.AddAxis(z2); | ||
| 499 | + codegen::TPipe tpipe("tpipe", tiler); | ||
| 500 | + | ||
| 501 | + af::AscGraph graph("static_singleton_dma"); | ||
| 502 | + af::ascir_op::Data x("x", graph); | ||
| 503 | + auto tensor = graph.FindNode("x")->outputs[0]; | ||
| 504 | + tensor.attr.axis = {z0.id, z1.id, z2.id}; | ||
| 505 | + tensor.attr.vectorized_axis = tensor.attr.axis; | ||
| 506 | + tensor.attr.repeats = {z0.size, One, One}; | ||
| 507 | + tensor.attr.strides = {af::Symbol(6), af::Symbol(3), One}; | ||
| 508 | + tensor.attr.vectorized_strides = {One, Zero, Zero}; | ||
| 509 | + | ||
| 510 | + std::string dtype_name; | ||
| 511 | + Tensor::DtypeName(tensor.attr.dtype, dtype_name); | ||
| 512 | + Tensor gm_tensor(tensor, dtype_name); | ||
| 513 | + Tensor ub_tensor(tensor, dtype_name); | ||
| 514 | + gm_tensor.vectorized_axis_pos = {0, 1, 2}; | ||
| 515 | + ub_tensor.vectorized_axis_pos = {0, 1, 2}; | ||
| 516 | + DataCopyParams param; | ||
| 517 | + ASSERT_TRUE(CalculateDmaParams(tpipe, gm_tensor, ub_tensor, param, true)); | ||
| 518 | + ASSERT_EQ(param.repeats.size(), 1UL); | ||
| 519 | + EXPECT_EQ(param.repeats[0], af::Symbol(8)); | ||
| 520 | +} | ||
| 521 | + | ||
| 522 | +TEST(CodegenLoadStore, CalculateDmaParams_KeepsDynamicAxes) { | ||
| 523 | + af::SizeVar dynamic_size(af::Symbol("dynamic_size")); | ||
| 524 | + af::Axis z0{.id = 0, .name = "z0", .type = af::Axis::Type::kAxisTypeOriginal, .size = af::Symbol(8)}; | ||
| 525 | + af::Axis z1{.id = 1, .name = "z1", .type = af::Axis::Type::kAxisTypeOriginal, .size = dynamic_size.expr}; | ||
| 526 | + codegen::Tiler tiler; | ||
| 527 | + tiler.AddAxis(z0); | ||
| 528 | + tiler.AddAxis(z1); | ||
| 529 | + tiler.AddSizeVar(dynamic_size); | ||
| 530 | + codegen::TPipe tpipe("tpipe", tiler); | ||
| 531 | + | ||
| 532 | + af::AscGraph graph("dynamic_dma"); | ||
| 533 | + af::ascir_op::Data x("x", graph); | ||
| 534 | + auto tensor = graph.FindNode("x")->outputs[0]; | ||
| 535 | + tensor.attr.axis = {z0.id, z1.id}; | ||
| 536 | + tensor.attr.vectorized_axis = tensor.attr.axis; | ||
| 537 | + tensor.attr.repeats = {z0.size, z1.size}; | ||
| 538 | + tensor.attr.strides = {z1.size, One}; | ||
| 539 | + tensor.attr.vectorized_strides = {z1.size, One}; | ||
| 540 | + | ||
| 541 | + std::string dtype_name; | ||
| 542 | + Tensor::DtypeName(tensor.attr.dtype, dtype_name); | ||
| 543 | + Tensor gm_tensor(tensor, dtype_name); | ||
| 544 | + Tensor ub_tensor(tensor, dtype_name); | ||
| 545 | + DataCopyParams param; | ||
| 546 | + ASSERT_TRUE(CalculateDmaParams(tpipe, gm_tensor, ub_tensor, param, true)); | ||
| 547 | + ASSERT_EQ(param.repeats.size(), 1UL); | ||
| 548 | + EXPECT_EQ(param.repeats[0], af::Symbol(8) * dynamic_size.expr); | ||
| 549 | +} | ||
| @@ -99,6 +99,44 @@ static ascir::FusedScheduledResult MakeFusedScheduledResultWithGraphs(std::vecto | |||
| 99 | return fused_result; | 99 | return fused_result; |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | +TEST_F(BufQueAllocatorUT, ArangeOutputUsesNormalUbBufferAllocation) { | ||
| 103 | + ge::PlatformContext::GetInstance().SetPlatform("3510"); | ||
| 104 | + af::AscGraph graph("arange_ub_allocation"); | ||
| 105 | + const auto size = graph.CreateSizeVar("size"); | ||
| 106 | + const auto axis = graph.CreateAxis("axis", size); | ||
| 107 | + | ||
| 108 | + af::ascir_op::Arange arange("arange"); | ||
| 109 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 110 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 111 | + arange.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 112 | + arange.attr.sched.axis = {axis.id}; | ||
| 113 | + arange.y.dtype = af::DT_INT32; | ||
| 114 | + *arange.y.axis = {axis.id}; | ||
| 115 | + *arange.y.repeats = {size}; | ||
| 116 | + *arange.y.strides = {af::ops::One}; | ||
| 117 | + | ||
| 118 | + ASSERT_NE(graph.AddNode(arange), nullptr); | ||
| 119 | + ASSERT_EQ(BufQueAllocator().SetOutputTensorAttr(graph), af::SUCCESS); | ||
| 120 | + | ||
| 121 | + const auto arange_node = graph.FindNode("arange"); | ||
| 122 | + ASSERT_NE(arange_node, nullptr); | ||
| 123 | + EXPECT_EQ(arange_node->outputs[0].attr.mem.hardware, af::MemHardware::kMemHardwareUB); | ||
| 124 | + EXPECT_EQ(arange_node->outputs[0].attr.mem.alloc_type, af::AllocType::kAllocTypeBuffer); | ||
| 125 | + EXPECT_EQ(arange_node->outputs[0].attr.mem.position, af::Position::kPositionVecCalc); | ||
| 126 | +} | ||
| 127 | + | ||
| 128 | +TEST_F(BufQueAllocatorUT, IndexExprKeepsScalarNoUbAllocation) { | ||
| 129 | + ge::PlatformContext::GetInstance().SetPlatform("3510"); | ||
| 130 | + af::AscGraph graph("index_expr_no_ub"); | ||
| 131 | + (void)af::ascir::cg::IndexExpr("index", graph, af::DT_INT32, {}, {}, {}, af::Symbol(3)); | ||
| 132 | + ASSERT_EQ(BufQueAllocator().SetOutputTensorAttr(graph), af::SUCCESS); | ||
| 133 | + | ||
| 134 | + const auto index_node = graph.FindNode("index"); | ||
| 135 | + ASSERT_NE(index_node, nullptr); | ||
| 136 | + EXPECT_NE(index_node->outputs[0].attr.mem.hardware, af::MemHardware::kMemHardwareUB); | ||
| 137 | + EXPECT_NE(index_node->outputs[0].attr.mem.alloc_type, af::AllocType::kAllocTypeBuffer); | ||
| 138 | +} | ||
| 139 | + | ||
| 102 | TEST_F(BufQueAllocatorUT, ShortenVecoutLifetimeInsertsUb2ubBeforeStore) { | 140 | TEST_F(BufQueAllocatorUT, ShortenVecoutLifetimeInsertsUb2ubBeforeStore) { |
| 103 | auto graph = MakeStaticLoadStoreGraph("shorten_vecout", 32); | 141 | auto graph = MakeStaticLoadStoreGraph("shorten_vecout", 32); |
| 104 | ASSERT_EQ(ScheduleUtils::TopologicalSorting(graph), af::GRAPH_SUCCESS); | 142 | ASSERT_EQ(ScheduleUtils::TopologicalSorting(graph), af::GRAPH_SUCCESS); |
| @@ -11,6 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | + | ||
| 14 | 15 | ||
| 15 | 16 | ||
| 16 | 17 | ||
| @@ -44,6 +45,8 @@ | |||
| 44 | 45 | ||
| 45 | 46 | ||
| 46 | 47 | ||
| 48 | + | ||
| 49 | + | ||
| 47 | 50 | ||
| 48 | using namespace af; | 51 | using namespace af; |
| 49 | using namespace af::ops; | 52 | using namespace af::ops; |
| @@ -53,6 +56,243 @@ using ge::InputValueSumSourceStub; | |||
| 53 | using ge::RuntimeStub; | 56 | using ge::RuntimeStub; |
| 54 | 57 | ||
| 55 | namespace { | 58 | namespace { |
| 59 | +enum class ArangeConsumer { | ||
| 60 | + kAdd, | ||
| 61 | + kSub, | ||
| 62 | + kMul, | ||
| 63 | + kCompare, | ||
| 64 | + kFloorDiv, | ||
| 65 | + kMinimum, | ||
| 66 | + kMaximum, | ||
| 67 | +}; | ||
| 68 | + | ||
| 69 | +std::vector<std::string> GetNodeNames(const af::AscGraph &graph) { | ||
| 70 | + std::vector<std::string> names; | ||
| 71 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 72 | + names.emplace_back(node->GetNamePtr()); | ||
| 73 | + } | ||
| 74 | + return names; | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +std::string GetLineContaining(const std::string &text, const std::string &needle) { | ||
| 78 | + const auto offset = text.find(needle); | ||
| 79 | + if (offset == std::string::npos) { | ||
| 80 | + return {}; | ||
| 81 | + } | ||
| 82 | + const auto line_begin = text.rfind('\n', offset); | ||
| 83 | + const auto line_end = text.find('\n', offset); | ||
| 84 | + return text.substr(line_begin == std::string::npos ? 0UL : line_begin + 1UL, | ||
| 85 | + line_end == std::string::npos ? std::string::npos : line_end - line_begin - 1UL); | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +af::AscGraph CreateArangeConsumerGraph(const std::string &name, ArangeConsumer consumer, | ||
| 89 | + const af::Expression &vector_stride = af::ops::One, | ||
| 90 | + af::DataType dtype = af::DT_INT32, | ||
| 91 | + const std::array<af::Expression, 4> &arange_params = { | ||
| 92 | + af::Symbol(0), af::Symbol(1), af::Symbol(1), af::Symbol(1)}) { | ||
| 93 | + af::AscGraph graph(name.c_str()); | ||
| 94 | + const auto size = graph.CreateSizeVar("size"); | ||
| 95 | + const auto axis = graph.CreateAxis("axis", size); | ||
| 96 | + | ||
| 97 | + Arange arange("arange"); | ||
| 98 | + arange.ir_attr.SetBase(arange_params[0]); | ||
| 99 | + arange.ir_attr.SetStep(arange_params[1]); | ||
| 100 | + arange.attr.sched.axis = {axis.id}; | ||
| 101 | + arange.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 102 | + arange.y.dtype = dtype; | ||
| 103 | + *arange.y.axis = {axis.id}; | ||
| 104 | + *arange.y.repeats = {size}; | ||
| 105 | + *arange.y.strides = {af::ops::One}; | ||
| 106 | + *arange.y.vectorized_axis = {axis.id}; | ||
| 107 | + *arange.y.vectorized_strides = {af::ops::One}; | ||
| 108 | + (void)graph.AddNode(arange); | ||
| 109 | + | ||
| 110 | + Arange other_arange("other_arange"); | ||
| 111 | + other_arange.ir_attr.SetBase(arange_params[2]); | ||
| 112 | + other_arange.ir_attr.SetStep(arange_params[3]); | ||
| 113 | + other_arange.attr.sched.axis = {axis.id}; | ||
| 114 | + other_arange.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 115 | + other_arange.y.dtype = dtype; | ||
| 116 | + *other_arange.y.axis = {axis.id}; | ||
| 117 | + *other_arange.y.repeats = {size}; | ||
| 118 | + *other_arange.y.strides = {af::ops::One}; | ||
| 119 | + *other_arange.y.vectorized_axis = {axis.id}; | ||
| 120 | + *other_arange.y.vectorized_strides = {af::ops::One}; | ||
| 121 | + (void)graph.AddNode(other_arange); | ||
| 122 | + | ||
| 123 | + Store store("store"); | ||
| 124 | + if (consumer == ArangeConsumer::kCompare) { | ||
| 125 | + Lt compare("compare"); | ||
| 126 | + compare.x1 = arange.y; | ||
| 127 | + compare.x2 = other_arange.y; | ||
| 128 | + compare.attr.sched.axis = {axis.id}; | ||
| 129 | + compare.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 130 | + compare.y.dtype = af::DT_UINT8; | ||
| 131 | + *compare.y.axis = {axis.id}; | ||
| 132 | + *compare.y.repeats = {size}; | ||
| 133 | + *compare.y.strides = {vector_stride}; | ||
| 134 | + *compare.y.vectorized_axis = {axis.id}; | ||
| 135 | + *compare.y.vectorized_strides = {vector_stride}; | ||
| 136 | + store.x = compare.y; | ||
| 137 | + } else if (consumer == ArangeConsumer::kFloorDiv) { | ||
| 138 | + FloorDiv floor_div("floor_div"); | ||
| 139 | + floor_div.x1 = arange.y; | ||
| 140 | + floor_div.x2 = other_arange.y; | ||
| 141 | + floor_div.attr.sched.axis = {axis.id}; | ||
| 142 | + floor_div.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 143 | + floor_div.y.dtype = dtype; | ||
| 144 | + *floor_div.y.axis = {axis.id}; | ||
| 145 | + *floor_div.y.repeats = {size}; | ||
| 146 | + *floor_div.y.strides = {af::ops::One}; | ||
| 147 | + *floor_div.y.vectorized_axis = {axis.id}; | ||
| 148 | + *floor_div.y.vectorized_strides = {af::ops::One}; | ||
| 149 | + store.x = floor_div.y; | ||
| 150 | + } else { | ||
| 151 | + auto configure = [&](auto &op) { | ||
| 152 | + op.attr.sched.axis = {axis.id}; | ||
| 153 | + op.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 154 | + op.y.dtype = dtype; | ||
| 155 | + *op.y.axis = {axis.id}; | ||
| 156 | + *op.y.repeats = {size}; | ||
| 157 | + *op.y.strides = {af::ops::One}; | ||
| 158 | + *op.y.vectorized_axis = {axis.id}; | ||
| 159 | + *op.y.vectorized_strides = {af::ops::One}; | ||
| 160 | + store.x = op.y; | ||
| 161 | + }; | ||
| 162 | + if (consumer == ArangeConsumer::kAdd) { | ||
| 163 | + Add op("add"); | ||
| 164 | + op.x1 = arange.y; | ||
| 165 | + op.x2 = other_arange.y; | ||
| 166 | + configure(op); | ||
| 167 | + } else if (consumer == ArangeConsumer::kSub) { | ||
| 168 | + Sub op("sub"); | ||
| 169 | + op.x1 = arange.y; | ||
| 170 | + op.x2 = other_arange.y; | ||
| 171 | + configure(op); | ||
| 172 | + } else if (consumer == ArangeConsumer::kMul) { | ||
| 173 | + Mul op("mul"); | ||
| 174 | + op.x1 = arange.y; | ||
| 175 | + op.x2 = other_arange.y; | ||
| 176 | + configure(op); | ||
| 177 | + } else if (consumer == ArangeConsumer::kMinimum) { | ||
| 178 | + Minimum op("minimum"); | ||
| 179 | + op.x1 = arange.y; | ||
| 180 | + op.x2 = other_arange.y; | ||
| 181 | + configure(op); | ||
| 182 | + } else if (consumer == ArangeConsumer::kMaximum) { | ||
| 183 | + Maximum op("maximum"); | ||
| 184 | + op.x1 = arange.y; | ||
| 185 | + op.x2 = other_arange.y; | ||
| 186 | + configure(op); | ||
| 187 | + } | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + store.attr.sched.axis = {axis.id}; | ||
| 191 | + store.attr.api.unit = af::ComputeUnit::kUnitMTE3; | ||
| 192 | + store.y.dtype = consumer == ArangeConsumer::kCompare ? af::DT_UINT8 : dtype; | ||
| 193 | + *store.y.axis = {axis.id}; | ||
| 194 | + *store.y.repeats = {size}; | ||
| 195 | + *store.y.strides = {af::ops::One}; | ||
| 196 | + *store.y.vectorized_axis = {axis.id}; | ||
| 197 | + *store.y.vectorized_strides = {af::ops::One}; | ||
| 198 | + | ||
| 199 | + Output output("output"); | ||
| 200 | + output.x = store.y; | ||
| 201 | + output.ir_attr.SetIndex(0); | ||
| 202 | + return graph; | ||
| 203 | +} | ||
| 204 | + | ||
| 205 | +af::AscGraph CreateWindowMathGraph(const std::string &name) { | ||
| 206 | + af::AscGraph graph(name.c_str()); | ||
| 207 | + const auto size = graph.CreateSizeVar("size"); | ||
| 208 | + const auto axis = graph.CreateAxis("axis", size); | ||
| 209 | + auto set_tensor = [&](auto &tensor) { | ||
| 210 | + tensor.dtype = af::DT_INT64; | ||
| 211 | + *tensor.axis = {axis.id}; | ||
| 212 | + *tensor.repeats = {size}; | ||
| 213 | + *tensor.strides = {af::ops::One}; | ||
| 214 | + *tensor.vectorized_axis = {axis.id}; | ||
| 215 | + *tensor.vectorized_strides = {af::ops::One}; | ||
| 216 | + }; | ||
| 217 | + | ||
| 218 | + Arange arange("arange"); | ||
| 219 | + arange.ir_attr.SetBase(af::Symbol(-17)); | ||
| 220 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 221 | + arange.attr.sched.axis = {axis.id}; | ||
| 222 | + arange.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 223 | + set_tensor(arange.y); | ||
| 224 | + (void)graph.AddNode(arange); | ||
| 225 | + Arange divisor("divisor"); | ||
| 226 | + divisor.ir_attr.SetBase(af::Symbol(3)); | ||
| 227 | + divisor.ir_attr.SetStep(af::Symbol(0)); | ||
| 228 | + divisor.attr.sched.axis = {axis.id}; | ||
| 229 | + divisor.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 230 | + set_tensor(divisor.y); | ||
| 231 | + (void)graph.AddNode(divisor); | ||
| 232 | + FloorDiv floor_div("floor_div"); | ||
| 233 | + floor_div.x1 = arange.y; | ||
| 234 | + floor_div.x2 = divisor.y; | ||
| 235 | + floor_div.attr.sched.axis = {axis.id}; | ||
| 236 | + floor_div.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 237 | + set_tensor(floor_div.y); | ||
| 238 | + Minimum minimum("minimum"); | ||
| 239 | + minimum.x1 = floor_div.y; | ||
| 240 | + minimum.x2 = divisor.y; | ||
| 241 | + minimum.attr.sched.axis = {axis.id}; | ||
| 242 | + minimum.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 243 | + set_tensor(minimum.y); | ||
| 244 | + Maximum maximum("maximum"); | ||
| 245 | + maximum.x1 = minimum.y; | ||
| 246 | + maximum.x2 = arange.y; | ||
| 247 | + maximum.attr.sched.axis = {axis.id}; | ||
| 248 | + maximum.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 249 | + set_tensor(maximum.y); | ||
| 250 | + Store store("store"); | ||
| 251 | + store.x = maximum.y; | ||
| 252 | + store.attr.sched.axis = {axis.id}; | ||
| 253 | + store.attr.api.unit = af::ComputeUnit::kUnitMTE3; | ||
| 254 | + set_tensor(store.y); | ||
| 255 | + Output output("output"); | ||
| 256 | + output.x = store.y; | ||
| 257 | + output.ir_attr.SetIndex(0); | ||
| 258 | + return graph; | ||
| 259 | +} | ||
| 260 | + | ||
| 261 | +af::AscGraph CreateArangeStoreGraph(const std::string &name) { | ||
| 262 | + af::AscGraph graph(name.c_str()); | ||
| 263 | + const auto size = graph.CreateSizeVar("size"); | ||
| 264 | + const auto axis = graph.CreateAxis("axis", size); | ||
| 265 | + | ||
| 266 | + Arange arange("arange"); | ||
| 267 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 268 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 269 | + arange.attr.sched.axis = {axis.id}; | ||
| 270 | + arange.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 271 | + arange.y.dtype = af::DT_INT32; | ||
| 272 | + *arange.y.axis = {axis.id}; | ||
| 273 | + *arange.y.repeats = {size}; | ||
| 274 | + *arange.y.strides = {af::ops::One}; | ||
| 275 | + *arange.y.vectorized_axis = {axis.id}; | ||
| 276 | + *arange.y.vectorized_strides = {af::ops::One}; | ||
| 277 | + (void)graph.AddNode(arange); | ||
| 278 | + | ||
| 279 | + Store store("store"); | ||
| 280 | + store.x = arange.y; | ||
| 281 | + store.attr.sched.axis = {axis.id}; | ||
| 282 | + store.attr.api.unit = af::ComputeUnit::kUnitMTE3; | ||
| 283 | + store.y.dtype = af::DT_INT32; | ||
| 284 | + *store.y.axis = {axis.id}; | ||
| 285 | + *store.y.repeats = {size}; | ||
| 286 | + *store.y.strides = {af::ops::One}; | ||
| 287 | + *store.y.vectorized_axis = {axis.id}; | ||
| 288 | + *store.y.vectorized_strides = {af::ops::One}; | ||
| 289 | + | ||
| 290 | + Output output("output"); | ||
| 291 | + output.x = store.y; | ||
| 292 | + output.ir_attr.SetIndex(0); | ||
| 293 | + return graph; | ||
| 294 | +} | ||
| 295 | + | ||
| 56 | class GraphBuilder { | 296 | class GraphBuilder { |
| 57 | public: | 297 | public: |
| 58 | explicit GraphBuilder(const std::string &name) { | 298 | explicit GraphBuilder(const std::string &name) { |
| @@ -114,6 +354,10 @@ class TestOptimizer : public ::testing::Test { | |||
| 114 | 354 | ||
| 115 | TestOptimizer() : optimizer(optimize::OptimizerOptions{}) {} | 355 | TestOptimizer() : optimizer(optimize::OptimizerOptions{}) {} |
| 116 | 356 | ||
| 357 | + static void SetUpV2RuntimeStub() { | ||
| 358 | + RuntimeStub::SetInstance(std::make_shared<af::RuntimeStubV2>()); | ||
| 359 | + } | ||
| 360 | + | ||
| 117 | static std::stringstream &SizeExprListStr(std::stringstream &ss, const af::AscGraph &graph, | 361 | static std::stringstream &SizeExprListStr(std::stringstream &ss, const af::AscGraph &graph, |
| 118 | const std::vector<af::Expression> &size_expr_list) { | 362 | const std::vector<af::Expression> &size_expr_list) { |
| 119 | for (auto &size_expr : size_expr_list) { | 363 | for (auto &size_expr : size_expr_list) { |
| @@ -202,6 +446,447 @@ TEST_F(TestOptimizer, TwoWorkspace) { | |||
| 202 | EXPECT_EQ(workspace2->outputs[0].attr.mem.tensor_id, store2->outputs[0].attr.mem.tensor_id); | 446 | EXPECT_EQ(workspace2->outputs[0].attr.mem.tensor_id, store2->outputs[0].attr.mem.tensor_id); |
| 203 | } | 447 | } |
| 204 | 448 | ||
| 449 | +TEST_F(TestOptimizer, V2ArangeCompareOptimizeProducesDeterministicVectorFunc) { | ||
| 450 | + SetUpV2RuntimeStub(); | ||
| 451 | + ge::PlatformContext::GetInstance().SetPlatform("3510"); | ||
| 452 | + std::vector<std::string> expected_root_order; | ||
| 453 | + std::vector<std::string> expected_vf_order; | ||
| 454 | + for (size_t run = 0UL; run < 3UL; ++run) { | ||
| 455 | + auto graph = CreateArangeConsumerGraph("arange_compare_vf", ArangeConsumer::kCompare); | ||
| 456 | + ::ascir::FusedScheduledResult result; | ||
| 457 | + ASSERT_EQ(optimizer.Optimize(graph, result), af::SUCCESS); | ||
| 458 | + ASSERT_EQ(result.node_idx_to_scheduled_results.size(), 1UL); | ||
| 459 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0].size(), 1UL); | ||
| 460 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0][0].schedule_groups.size(), 1UL); | ||
| 461 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs.size(), 1UL); | ||
| 462 | + const auto &impl_graph = result.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs[0]; | ||
| 463 | + std::vector<AscGraph> subgraphs; | ||
| 464 | + ASSERT_EQ(impl_graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 465 | + ASSERT_EQ(subgraphs.size(), 1UL); | ||
| 466 | + const auto root_order = GetNodeNames(impl_graph); | ||
| 467 | + const auto vf_order = GetNodeNames(subgraphs[0]); | ||
| 468 | + EXPECT_EQ(root_order, (std::vector<std::string>{"arange_compare_vf_0_B0Y0_S0G0C0_VfNode_0", "store", "output"})); | ||
| 469 | + EXPECT_EQ(vf_order, | ||
| 470 | + (std::vector<std::string>{"arange", "other_arange", "compare", "Store_compare0", "Output_compare0"})); | ||
| 471 | + if (run == 0UL) { | ||
| 472 | + expected_root_order = root_order; | ||
| 473 | + expected_vf_order = vf_order; | ||
| 474 | + } else { | ||
| 475 | + EXPECT_EQ(root_order, expected_root_order); | ||
| 476 | + EXPECT_EQ(vf_order, expected_vf_order); | ||
| 477 | + } | ||
| 478 | + } | ||
| 479 | +} | ||
| 480 | + | ||
| 481 | +TEST_F(TestOptimizer, V2ArangeStoreOptimizeProducesSingletonVectorFunc) { | ||
| 482 | + SetUpV2RuntimeStub(); | ||
| 483 | + ge::PlatformContext::GetInstance().SetPlatform("3510"); | ||
| 484 | + auto graph = CreateArangeStoreGraph("arange_store_vf"); | ||
| 485 | + ::ascir::FusedScheduledResult result; | ||
| 486 | + | ||
| 487 | + ASSERT_EQ(optimizer.Optimize(graph, result), af::SUCCESS); | ||
| 488 | + ASSERT_EQ(result.node_idx_to_scheduled_results.size(), 1UL); | ||
| 489 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0].size(), 1UL); | ||
| 490 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0][0].schedule_groups.size(), 1UL); | ||
| 491 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs.size(), 1UL); | ||
| 492 | + | ||
| 493 | + const auto &impl_graph = result.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs[0]; | ||
| 494 | + std::vector<AscGraph> subgraphs; | ||
| 495 | + ASSERT_EQ(impl_graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 496 | + ASSERT_EQ(subgraphs.size(), 1UL); | ||
| 497 | + EXPECT_EQ(GetNodeNames(impl_graph), | ||
| 498 | + (std::vector<std::string>{"arange_store_vf_0_B0Y0_S0G0C0_VfNode_0", "store", "output"})); | ||
| 499 | + EXPECT_EQ(GetNodeNames(subgraphs[0]), (std::vector<std::string>{"arange", "Store_arange0", "Output_arange0"})); | ||
| 500 | +} | ||
| 501 | + | ||
| 502 | +TEST_F(TestOptimizer, V2HighRankArangeOptimizeAndCodegenOuterFor) { | ||
| 503 | + SetUpV2RuntimeStub(); | ||
| 504 | + ge::PlatformContext::GetInstance().SetPlatform("3510"); | ||
| 505 | + af::AscGraph graph("high_rank_arange_pipeline"); | ||
| 506 | + std::vector<af::Expression> sizes; | ||
| 507 | + std::vector<af::AxisId> axes; | ||
| 508 | + for (size_t i = 0UL; i < 5UL; ++i) { | ||
| 509 | + sizes.push_back(graph.CreateSizeVar("size" + std::to_string(i))); | ||
| 510 | + axes.push_back(graph.CreateAxis("axis" + std::to_string(i), sizes.back()).id); | ||
| 511 | + } | ||
| 512 | + const std::vector<af::Expression> strides = {sizes[1] * sizes[2] * sizes[3] * sizes[4] * af::Symbol(5), | ||
| 513 | + sizes[2] * sizes[3] * sizes[4] * af::Symbol(4), | ||
| 514 | + sizes[3] * sizes[4] * af::Symbol(3), sizes[4] * af::Symbol(2), One}; | ||
| 515 | + | ||
| 516 | + Arange arange("arange"); | ||
| 517 | + arange.ir_attr.SetBase(af::Symbol(3)); | ||
| 518 | + arange.ir_attr.SetStep(af::Symbol(2)); | ||
| 519 | + arange.attr.sched.axis = axes; | ||
| 520 | + arange.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 521 | + arange.y.dtype = af::DT_INT64; | ||
| 522 | + *arange.y.axis = axes; | ||
| 523 | + *arange.y.repeats = sizes; | ||
| 524 | + *arange.y.strides = strides; | ||
| 525 | + ASSERT_NE(graph.AddNode(arange), nullptr); | ||
| 526 | + | ||
| 527 | + Abs abs("abs"); | ||
| 528 | + abs.x = arange.y; | ||
| 529 | + abs.attr.sched.axis = axes; | ||
| 530 | + abs.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 531 | + abs.y.dtype = af::DT_INT64; | ||
| 532 | + *abs.y.axis = axes; | ||
| 533 | + *abs.y.repeats = sizes; | ||
| 534 | + *abs.y.strides = strides; | ||
| 535 | + | ||
| 536 | + Store store("store"); | ||
| 537 | + store.x = abs.y; | ||
| 538 | + store.attr.sched.axis = axes; | ||
| 539 | + store.attr.api.unit = af::ComputeUnit::kUnitMTE3; | ||
| 540 | + store.y.dtype = af::DT_INT64; | ||
| 541 | + *store.y.axis = axes; | ||
| 542 | + *store.y.repeats = sizes; | ||
| 543 | + *store.y.strides = strides; | ||
| 544 | + | ||
| 545 | + Output output("output"); | ||
| 546 | + output.x = store.y; | ||
| 547 | + output.ir_attr.SetIndex(0); | ||
| 548 | + | ||
| 549 | + ::ascir::FusedScheduledResult scheduled; | ||
| 550 | + ASSERT_EQ(optimizer.Optimize(graph, scheduled), af::SUCCESS); | ||
| 551 | + ASSERT_EQ(scheduled.node_idx_to_scheduled_results.size(), 1UL); | ||
| 552 | + ASSERT_EQ(scheduled.node_idx_to_scheduled_results[0].size(), 1UL); | ||
| 553 | + ASSERT_EQ(scheduled.node_idx_to_scheduled_results[0][0].schedule_groups.size(), 1UL); | ||
| 554 | + ASSERT_FALSE(scheduled.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs.empty()); | ||
| 555 | + | ||
| 556 | + codegen::CodegenResult generated; | ||
| 557 | + const std::map<std::string, std::string> shape_info = { | ||
| 558 | + {"size0", "2"}, {"size1", "2"}, {"size2", "2"}, {"size3", "2"}, {"size4", "65"}}; | ||
| 559 | + ASSERT_EQ(codegen::Codegen(codegen::CodegenOptions{}).Generate(shape_info, scheduled, generated), af::SUCCESS); | ||
| 560 | + EXPECT_NE(generated.kernel.find("outer_for_0"), std::string::npos) << generated.kernel; | ||
| 561 | + EXPECT_NE(generated.kernel.find("arange_offset_"), std::string::npos) << generated.kernel; | ||
| 562 | +} | ||
| 563 | + | ||
| 564 | +TEST_F(TestOptimizer, V2ArangeFloorDivOptimizeProducesDeterministicKernel) { | ||
| 565 | + SetUpV2RuntimeStub(); | ||
| 566 | + ge::PlatformContext::GetInstance().SetPlatform("3510"); | ||
| 567 | + std::vector<std::string> expected_root_order; | ||
| 568 | + for (size_t run = 0UL; run < 3UL; ++run) { | ||
| 569 | + auto graph = CreateArangeConsumerGraph("arange_floor_div_vf", ArangeConsumer::kFloorDiv); | ||
| 570 | + ::ascir::FusedScheduledResult result; | ||
| 571 | + ASSERT_EQ(optimizer.Optimize(graph, result), af::SUCCESS); | ||
| 572 | + ASSERT_EQ(result.node_idx_to_scheduled_results.size(), 1UL); | ||
| 573 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0].size(), 1UL); | ||
| 574 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0][0].schedule_groups.size(), 1UL); | ||
| 575 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs.size(), 1UL); | ||
| 576 | + const auto &impl_graph = result.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs[0]; | ||
| 577 | + std::vector<AscGraph> subgraphs; | ||
| 578 | + ASSERT_EQ(impl_graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 579 | + ASSERT_EQ(subgraphs.size(), 2UL); | ||
| 580 | + const auto root_order = GetNodeNames(impl_graph); | ||
| 581 | + EXPECT_EQ(root_order, | ||
| 582 | + (std::vector<std::string>{"arange_floor_div_vf_0_B0Y0_S0G0C0_VfNode_0", | ||
| 583 | + "arange_floor_div_vf_0_B0Y0_S0G0C0_VfNode_1", "floor_div", "store", "output"})); | ||
| 584 | + if (run == 0UL) { | ||
| 585 | + expected_root_order = root_order; | ||
| 586 | + } else { | ||
| 587 | + EXPECT_EQ(root_order, expected_root_order); | ||
| 588 | + } | ||
| 589 | + } | ||
| 590 | +} | ||
| 591 | + | ||
| 592 | +TEST_F(TestOptimizer, V2Int64ArangeFloorDivKeepsInt64Composition) { | ||
| 593 | + SetUpV2RuntimeStub(); | ||
| 594 | + ge::PlatformContext::GetInstance().SetPlatform("3510"); | ||
| 595 | + auto graph = | ||
| 596 | + CreateArangeConsumerGraph("arange_int64_floor_div", ArangeConsumer::kFloorDiv, af::ops::One, af::DT_INT64); | ||
| 597 | + ::ascir::FusedScheduledResult result; | ||
| 598 | + ASSERT_EQ(optimizer.Optimize(graph, result), af::SUCCESS); | ||
| 599 | + ASSERT_EQ(result.node_idx_to_scheduled_results.size(), 1UL); | ||
| 600 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0].size(), 1UL); | ||
| 601 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0][0].schedule_groups.size(), 1UL); | ||
| 602 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs.size(), 1UL); | ||
| 603 | + const auto &impl_graph = result.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs[0]; | ||
| 604 | + std::vector<AscGraph> subgraphs; | ||
| 605 | + ASSERT_EQ(impl_graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 606 | + ASSERT_EQ(subgraphs.size(), 2UL); | ||
| 607 | + EXPECT_EQ(GetNodeNames(impl_graph), (std::vector<std::string>{"arange_int64_floor_div_0_B0Y0_S0G0C0_VfNode_0", | ||
| 608 | + "arange_int64_floor_div_0_B0Y0_S0G0C0_VfNode_1", | ||
| 609 | + "floor_div", "store", "output"})); | ||
| 610 | + EXPECT_EQ(subgraphs[0].FindNode("arange")->outputs[0].attr.dtype, af::DT_INT64); | ||
| 611 | + EXPECT_EQ(subgraphs[1].FindNode("other_arange")->outputs[0].attr.dtype, af::DT_INT64); | ||
| 612 | + EXPECT_EQ(impl_graph.FindNode("floor_div")->outputs[0].attr.dtype, af::DT_INT64); | ||
| 613 | +} | ||
| 614 | + | ||
| 615 | +TEST_F(TestOptimizer, V2WindowMathOptimizeProducesOneDeterministicKernel) { | ||
| 616 | + SetUpV2RuntimeStub(); | ||
| 617 | + ge::PlatformContext::GetInstance().SetPlatform("3510"); | ||
| 618 | + std::vector<std::string> expected_root_order; | ||
| 619 | + std::vector<std::vector<std::string>> expected_vf_orders; | ||
| 620 | + for (size_t run = 0UL; run < 3UL; ++run) { | ||
| 621 | + auto graph = CreateWindowMathGraph("window_math"); | ||
| 622 | + ::ascir::FusedScheduledResult result; | ||
| 623 | + ASSERT_EQ(optimizer.Optimize(graph, result), af::SUCCESS); | ||
| 624 | + ASSERT_EQ(result.node_idx_to_scheduled_results.size(), 1UL); | ||
| 625 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0].size(), 1UL); | ||
| 626 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0][0].schedule_groups.size(), 1UL); | ||
| 627 | + ASSERT_EQ(result.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs.size(), 1UL); | ||
| 628 | + const auto &impl_graph = result.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs[0]; | ||
| 629 | + std::vector<AscGraph> subgraphs; | ||
| 630 | + ASSERT_EQ(impl_graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 631 | + ASSERT_EQ(subgraphs.size(), 3UL); | ||
| 632 | + const auto root_order = GetNodeNames(impl_graph); | ||
| 633 | + std::vector<std::vector<std::string>> vf_orders; | ||
| 634 | + for (const auto &subgraph : subgraphs) { | ||
| 635 | + vf_orders.push_back(GetNodeNames(subgraph)); | ||
| 636 | + } | ||
| 637 | + if (run == 0UL) { | ||
| 638 | + expected_root_order = root_order; | ||
| 639 | + expected_vf_orders = vf_orders; | ||
| 640 | + } else { | ||
| 641 | + EXPECT_EQ(root_order, expected_root_order); | ||
| 642 | + EXPECT_EQ(vf_orders, expected_vf_orders); | ||
| 643 | + } | ||
| 644 | + EXPECT_EQ(root_order, | ||
| 645 | + (std::vector<std::string>{"window_math_0_B0Y0_S0G0C0_VfNode_0", "window_math_0_B0Y0_S0G0C0_VfNode_1", | ||
| 646 | + "floor_div", "window_math_0_B0Y0_S0G0C0_VfNode_2", "store", "output"})); | ||
| 647 | + EXPECT_EQ(vf_orders[0], (std::vector<std::string>{"arange", "Store_arange0", "Output_arange0"})); | ||
| 648 | + EXPECT_EQ(vf_orders[1], (std::vector<std::string>{"divisor", "Store_divisor0", "Output_divisor0"})); | ||
| 649 | + EXPECT_EQ(vf_orders[2], (std::vector<std::string>{"Data_floor_div0", "Data_window_math_0_B0Y0_S0G0C0_VfNode_01", | ||
| 650 | + "Data_window_math_0_B0Y0_S0G0C0_VfNode_12", "Load_floor_div0", | ||
| 651 | + "Load_window_math_0_B0Y0_S0G0C0_VfNode_01", | ||
| 652 | + "Load_window_math_0_B0Y0_S0G0C0_VfNode_12", "minimum", "maximum", | ||
| 653 | + "Store_maximum0", "Output_maximum0"})); | ||
| 654 | + } | ||
| 655 | +} | ||
| 656 | + | ||
| 657 | +TEST_F(TestOptimizer, V2MultiArangeOptimizeKeepsOrderEdgesAndParamsIsolated) { | ||
| 658 | + SetUpV2RuntimeStub(); | ||
| 659 | + ge::PlatformContext::GetInstance().SetPlatform("3510"); | ||
| 660 | + const std::vector<std::string> root_golden = {"multi_arange_0_B0Y0_S0G0C0_VfNode_0", "store", "output"}; | ||
| 661 | + const std::vector<std::string> edge_golden = {"arange:0->add:0", "other_arange:0->add:1"}; | ||
| 662 | + std::vector<std::string> expected_root_order; | ||
| 663 | + std::vector<std::string> expected_vf_order; | ||
| 664 | + std::vector<std::string> expected_add_edges; | ||
| 665 | + for (size_t run = 0UL; run < 3UL; ++run) { | ||
| 666 | + auto graph = CreateArangeConsumerGraph("multi_arange", ArangeConsumer::kAdd, af::ops::One, af::DT_INT64, | ||
| 667 | + {af::Symbol(1), af::Symbol(2), af::Symbol(7), af::Symbol(4)}); | ||
| 668 | + | ||
| 669 | + ::ascir::FusedScheduledResult result; | ||
| 670 | + ASSERT_EQ(optimizer.Optimize(graph, result), af::SUCCESS); | ||
| 671 | + const auto &impl_graph = result.node_idx_to_scheduled_results.at(0).at(0).schedule_groups.at(0).impl_graphs.at(0); | ||
| 672 | + std::vector<AscGraph> subgraphs; | ||
| 673 | + ASSERT_EQ(impl_graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 674 | + ASSERT_EQ(subgraphs.size(), 1UL); | ||
| 675 | + const auto root_order = GetNodeNames(impl_graph); | ||
| 676 | + const auto vf_order = GetNodeNames(subgraphs[0]); | ||
| 677 | + EXPECT_EQ(root_order, root_golden); | ||
| 678 | + EXPECT_EQ(vf_order, (std::vector<std::string>{"arange", "other_arange", "add", "Store_add0", "Output_add0"})); | ||
| 679 | + auto vf_arange = subgraphs[0].FindNode("arange"); | ||
| 680 | + auto vf_other_arange = subgraphs[0].FindNode("other_arange"); | ||
| 681 | + auto vf_add = subgraphs[0].FindNode("add"); | ||
| 682 | + ASSERT_NE(vf_arange, nullptr); | ||
| 683 | + ASSERT_NE(vf_other_arange, nullptr); | ||
| 684 | + ASSERT_NE(vf_add, nullptr); | ||
| 685 | + af::Expression base; | ||
| 686 | + af::Expression step; | ||
| 687 | + ASSERT_EQ(vf_arange->attr.ir_attr->GetAttrValue("base", base), af::GRAPH_SUCCESS); | ||
| 688 | + ASSERT_EQ(vf_arange->attr.ir_attr->GetAttrValue("step", step), af::GRAPH_SUCCESS); | ||
| 689 | + EXPECT_EQ(base, af::Symbol(1)); | ||
| 690 | + EXPECT_EQ(step, af::Symbol(2)); | ||
| 691 | + ASSERT_EQ(vf_other_arange->attr.ir_attr->GetAttrValue("base", base), af::GRAPH_SUCCESS); | ||
| 692 | + ASSERT_EQ(vf_other_arange->attr.ir_attr->GetAttrValue("step", step), af::GRAPH_SUCCESS); | ||
| 693 | + EXPECT_EQ(base, af::Symbol(7)); | ||
| 694 | + EXPECT_EQ(step, af::Symbol(4)); | ||
| 695 | + ASSERT_EQ(vf_add->GetAllInDataAnchorsSize(), 2UL); | ||
| 696 | + ASSERT_NE(vf_add->GetInDataAnchor(0)->GetPeerOutAnchor(), nullptr); | ||
| 697 | + ASSERT_NE(vf_add->GetInDataAnchor(1)->GetPeerOutAnchor(), nullptr); | ||
| 698 | + EXPECT_EQ(vf_add->GetInDataAnchor(0)->GetPeerOutAnchor()->GetOwnerNode()->GetName(), "arange"); | ||
| 699 | + EXPECT_EQ(vf_add->GetInDataAnchor(1)->GetPeerOutAnchor()->GetOwnerNode()->GetName(), "other_arange"); | ||
| 700 | + const std::vector<std::string> add_edges = { | ||
| 701 | + vf_add->GetInDataAnchor(0)->GetPeerOutAnchor()->GetOwnerNode()->GetName() + ":" + | ||
| 702 | + std::to_string(vf_add->GetInDataAnchor(0)->GetPeerOutAnchor()->GetIdx()) + | ||
| 703 | + "->add:" + std::to_string(vf_add->GetInDataAnchor(0)->GetIdx()), | ||
| 704 | + vf_add->GetInDataAnchor(1)->GetPeerOutAnchor()->GetOwnerNode()->GetName() + ":" + | ||
| 705 | + std::to_string(vf_add->GetInDataAnchor(1)->GetPeerOutAnchor()->GetIdx()) + | ||
| 706 | + "->add:" + std::to_string(vf_add->GetInDataAnchor(1)->GetIdx())}; | ||
| 707 | + EXPECT_EQ(add_edges, edge_golden); | ||
| 708 | + if (run == 0UL) { | ||
| 709 | + expected_root_order = root_order; | ||
| 710 | + expected_vf_order = vf_order; | ||
| 711 | + expected_add_edges = add_edges; | ||
| 712 | + } else { | ||
| 713 | + EXPECT_EQ(root_order, expected_root_order); | ||
| 714 | + EXPECT_EQ(vf_order, expected_vf_order); | ||
| 715 | + EXPECT_EQ(add_edges, expected_add_edges); | ||
| 716 | + } | ||
| 717 | + } | ||
| 718 | +} | ||
| 719 | + | ||
| 720 | +struct Int64ConsumerCodegenCase { | ||
| 721 | + ArangeConsumer consumer; | ||
| 722 | + const char *consumer_name; | ||
| 723 | + const char *expected_api; | ||
| 724 | + const char *expected_call; | ||
| 725 | +}; | ||
| 726 | + | ||
| 727 | +class Int64ArangeConsumerCodegenTest : public TestOptimizer, | ||
| 728 | + public ::testing::WithParamInterface<Int64ConsumerCodegenCase> {}; | ||
| 729 | + | ||
| 730 | +TEST_P(Int64ArangeConsumerCodegenTest, GeneratesInt64ConsumerWithoutNarrowing) { | ||
| 731 | + SetUpV2RuntimeStub(); | ||
| 732 | + ge::PlatformContext::GetInstance().SetPlatform("3510"); | ||
| 733 | + const auto &test_case = GetParam(); | ||
| 734 | + auto graph = CreateArangeConsumerGraph("int64_consumer", test_case.consumer, af::ops::One, af::DT_INT64); | ||
| 735 | + ::ascir::FusedScheduledResult scheduled; | ||
| 736 | + ASSERT_EQ(optimizer.Optimize(graph, scheduled), af::SUCCESS); | ||
| 737 | + const auto &impl_graph = scheduled.node_idx_to_scheduled_results.at(0).at(0).schedule_groups.at(0).impl_graphs.at(0); | ||
| 738 | + std::vector<AscGraph> subgraphs; | ||
| 739 | + ASSERT_EQ(impl_graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 740 | + ASSERT_EQ(subgraphs.size(), 1UL); | ||
| 741 | + const auto consumer = subgraphs[0].FindNode(test_case.consumer_name); | ||
| 742 | + ASSERT_NE(consumer, nullptr); | ||
| 743 | + ASSERT_EQ(consumer->inputs.Size(), 2UL); | ||
| 744 | + ASSERT_EQ(consumer->outputs().size(), 1UL); | ||
| 745 | + EXPECT_EQ(consumer->inputs[0].attr.dtype, af::DT_INT64); | ||
| 746 | + EXPECT_EQ(consumer->inputs[1].attr.dtype, af::DT_INT64); | ||
| 747 | + EXPECT_EQ(consumer->outputs[0].attr.dtype, af::DT_INT64); | ||
| 748 | + codegen::CodegenResult generated; | ||
| 749 | + ASSERT_EQ(codegen::Codegen(codegen::CodegenOptions{}).Generate({{"size", "stub_size"}}, scheduled, generated), | ||
| 750 | + af::SUCCESS); | ||
| 751 | + const auto consumer_call = GetLineContaining(generated.kernel, test_case.expected_api); | ||
| 752 | + ASSERT_FALSE(consumer_call.empty()); | ||
| 753 | + EXPECT_EQ(consumer_call, test_case.expected_call); | ||
| 754 | + if (test_case.consumer == ArangeConsumer::kAdd) { | ||
| 755 | + const auto vf_signature = GetLineContaining(generated.kernel, "int64_t arange_offset_0"); | ||
| 756 | + ASSERT_FALSE(vf_signature.empty()); | ||
| 757 | + EXPECT_EQ(vf_signature, | ||
| 758 | + "inline __simd_vf__ void VFCallint64_consumer_0_B0Y0_S0G0C0_VfNode_0(__local_mem__ int64_t " | ||
| 759 | + "*local_1_addr, uint32_t output_dims_0, int64_t arange_offset_0, int64_t arange_base_0, int64_t " | ||
| 760 | + "arange_step_0, int64_t arange_offset_1, int64_t arange_base_1, int64_t arange_step_1)"); | ||
| 761 | + } | ||
| 762 | +} | ||
| 763 | + | ||
| 764 | +INSTANTIATE_TEST_SUITE_P( | ||
| 765 | + Int64Consumers, Int64ArangeConsumerCodegenTest, | ||
| 766 | + ::testing::Values(Int64ConsumerCodegenCase{ArangeConsumer::kAdd, "add", "AscendC::MicroAPI::Add", | ||
| 767 | + " AscendC::MicroAPI::Add(vreg_2, vreg_0, vreg_1, preg_0);"}, | ||
| 768 | + Int64ConsumerCodegenCase{ArangeConsumer::kSub, "sub", "AscendC::MicroAPI::Sub", | ||
| 769 | + " AscendC::MicroAPI::Sub(vreg_2, vreg_0, vreg_1, preg_0);"}, | ||
| 770 | + Int64ConsumerCodegenCase{ArangeConsumer::kMul, "mul", "AscendC::MicroAPI::Mul", | ||
| 771 | + " AscendC::MicroAPI::Mul(vreg_2, vreg_0, vreg_1, preg_0);"}, | ||
| 772 | + Int64ConsumerCodegenCase{ArangeConsumer::kMinimum, "minimum", "AscendC::MicroAPI::Min", | ||
| 773 | + " AscendC::MicroAPI::Min(vreg_2, vreg_0, vreg_1, preg_0);"}, | ||
| 774 | + Int64ConsumerCodegenCase{ArangeConsumer::kMaximum, "maximum", "AscendC::MicroAPI::Max", | ||
| 775 | + " AscendC::MicroAPI::Max(vreg_2, vreg_0, vreg_1, preg_0);"})); | ||
| 776 | + | ||
| 777 | +TEST_F(TestOptimizer, V2ScalarVectorFuncPreservesRootScalarThroughCodegen) { | ||
| 778 | + SetUpV2RuntimeStub(); | ||
| 779 | + ge::PlatformContext::GetInstance().SetPlatform("3510"); | ||
| 780 | + af::AscGraph graph("scalar_vf_regression"); | ||
| 781 | + const auto size = graph.CreateSizeVar("size"); | ||
| 782 | + const auto axis = graph.CreateAxis("axis", size); | ||
| 783 | + | ||
| 784 | + Data data("data", graph); | ||
| 785 | + data.ir_attr.SetIndex(0); | ||
| 786 | + data.y.dtype = af::DT_FLOAT; | ||
| 787 | + | ||
| 788 | + Load load("load"); | ||
| 789 | + load.x = data.y; | ||
| 790 | + load.attr.sched.axis = {axis.id}; | ||
| 791 | + load.attr.api.unit = af::ComputeUnit::kUnitMTE2; | ||
| 792 | + load.y.dtype = af::DT_FLOAT; | ||
| 793 | + *load.y.axis = {axis.id}; | ||
| 794 | + *load.y.repeats = {size}; | ||
| 795 | + *load.y.strides = {One}; | ||
| 796 | + *load.y.vectorized_axis = {axis.id}; | ||
| 797 | + *load.y.vectorized_strides = {One}; | ||
| 798 | + | ||
| 799 | + Scalar scalar("scalar", graph); | ||
| 800 | + scalar.ir_attr.SetValue("2.0"); | ||
| 801 | + scalar.y.dtype = af::DT_FLOAT; | ||
| 802 | + | ||
| 803 | + Add add("add"); | ||
| 804 | + add.x1 = load.y; | ||
| 805 | + add.x2 = scalar.y; | ||
| 806 | + add.attr.sched.axis = {axis.id}; | ||
| 807 | + add.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 808 | + add.y.dtype = af::DT_FLOAT; | ||
| 809 | + *add.y.axis = {axis.id}; | ||
| 810 | + *add.y.repeats = {size}; | ||
| 811 | + *add.y.strides = {One}; | ||
| 812 | + *add.y.vectorized_axis = {axis.id}; | ||
| 813 | + *add.y.vectorized_strides = {One}; | ||
| 814 | + | ||
| 815 | + Abs abs("abs"); | ||
| 816 | + abs.x = add.y; | ||
| 817 | + abs.attr.sched.axis = {axis.id}; | ||
| 818 | + abs.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 819 | + abs.y.dtype = af::DT_FLOAT; | ||
| 820 | + *abs.y.axis = {axis.id}; | ||
| 821 | + *abs.y.repeats = {size}; | ||
| 822 | + *abs.y.strides = {One}; | ||
| 823 | + *abs.y.vectorized_axis = {axis.id}; | ||
| 824 | + *abs.y.vectorized_strides = {One}; | ||
| 825 | + | ||
| 826 | + Store store("store"); | ||
| 827 | + store.x = abs.y; | ||
| 828 | + store.attr.sched.axis = {axis.id}; | ||
| 829 | + store.attr.api.unit = af::ComputeUnit::kUnitMTE3; | ||
| 830 | + store.y.dtype = af::DT_FLOAT; | ||
| 831 | + *store.y.axis = {axis.id}; | ||
| 832 | + *store.y.repeats = {size}; | ||
| 833 | + *store.y.strides = {One}; | ||
| 834 | + *store.y.vectorized_axis = {axis.id}; | ||
| 835 | + *store.y.vectorized_strides = {One}; | ||
| 836 | + | ||
| 837 | + Output output("output"); | ||
| 838 | + output.x = store.y; | ||
| 839 | + output.ir_attr.SetIndex(0); | ||
| 840 | + | ||
| 841 | + ::ascir::FusedScheduledResult scheduled; | ||
| 842 | + ASSERT_EQ(optimizer.Optimize(graph, scheduled), af::SUCCESS); | ||
| 843 | + ASSERT_EQ(scheduled.node_idx_to_scheduled_results.size(), 1UL); | ||
| 844 | + ASSERT_EQ(scheduled.node_idx_to_scheduled_results[0].size(), 1UL); | ||
| 845 | + ASSERT_EQ(scheduled.node_idx_to_scheduled_results[0][0].schedule_groups.size(), 1UL); | ||
| 846 | + ASSERT_EQ(scheduled.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs.size(), 1UL); | ||
| 847 | + const auto &impl_graph = scheduled.node_idx_to_scheduled_results[0][0].schedule_groups[0].impl_graphs[0]; | ||
| 848 | + | ||
| 849 | + const auto root_scalar = impl_graph.FindNode("scalar"); | ||
| 850 | + ASSERT_NE(root_scalar, nullptr); | ||
| 851 | + ASSERT_NE(root_scalar->outputs[0].attr.mem.tensor_id, af::kIdNone); | ||
| 852 | + EXPECT_NE(root_scalar->outputs[0].attr.mem.alloc_type, af::AllocType::kAllocTypeBuffer); | ||
| 853 | + | ||
| 854 | + af::AscNodePtr vf_node; | ||
| 855 | + for (const auto &node : impl_graph.GetAllNodes()) { | ||
| 856 | + if (node->GetType() == VectorFunc::Type) { | ||
| 857 | + vf_node = node; | ||
| 858 | + break; | ||
| 859 | + } | ||
| 860 | + } | ||
| 861 | + ASSERT_NE(vf_node, nullptr); | ||
| 862 | + std::vector<AscGraph> subgraphs; | ||
| 863 | + ASSERT_EQ(impl_graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 864 | + ASSERT_EQ(subgraphs.size(), 1UL); | ||
| 865 | + const auto sub_scalar = subgraphs[0].FindNode("Scalar_scalar"); | ||
| 866 | + ASSERT_NE(sub_scalar, nullptr); | ||
| 867 | + EXPECT_EQ(sub_scalar->outputs[0].attr.mem.tensor_id, root_scalar->outputs[0].attr.mem.tensor_id); | ||
| 868 | + | ||
| 869 | + codegen::CodegenResult generated; | ||
| 870 | + ASSERT_EQ(codegen::Codegen(codegen::CodegenOptions{}).Generate({{"size", "stub_size"}}, scheduled, generated), | ||
| 871 | + af::SUCCESS); | ||
| 872 | + const std::string scalar_name = "scalar_" + std::to_string(root_scalar->outputs[0].attr.mem.tensor_id); | ||
| 873 | + const std::string function_name = "VFCall" + vf_node->GetName(); | ||
| 874 | + const auto signature_offset = generated.kernel.find("inline __simd_vf__ void " + function_name + "("); | ||
| 875 | + ASSERT_NE(signature_offset, std::string::npos); | ||
| 876 | + const auto signature_end = generated.kernel.find('\n', signature_offset); | ||
| 877 | + const auto signature = generated.kernel.substr(signature_offset, signature_end - signature_offset); | ||
| 878 | + const std::string scalar_param = "float " + scalar_name; | ||
| 879 | + const auto scalar_param_offset = signature.find(scalar_param); | ||
| 880 | + ASSERT_NE(scalar_param_offset, std::string::npos) << signature; | ||
| 881 | + EXPECT_EQ(signature.find(scalar_param, scalar_param_offset + scalar_param.size()), std::string::npos) << signature; | ||
| 882 | + | ||
| 883 | + const auto call_offset = generated.kernel.find(function_name + "(", signature_end); | ||
| 884 | + ASSERT_NE(call_offset, std::string::npos); | ||
| 885 | + const auto call_end = generated.kernel.find('\n', call_offset); | ||
| 886 | + const auto call = generated.kernel.substr(call_offset, call_end - call_offset); | ||
| 887 | + EXPECT_NE(call.find(scalar_name), std::string::npos) << call; | ||
| 888 | +} | ||
| 889 | + | ||
| 205 | TEST_F(TestOptimizer, ReOrderMergeAxisGraph_scheduler) { | 890 | TEST_F(TestOptimizer, ReOrderMergeAxisGraph_scheduler) { |
| 206 | // z0, z1, z2 mergeaxis | 891 | // z0, z1, z2 mergeaxis |
| 207 | af::AscGraph graph("test_graph"); | 892 | af::AscGraph graph("test_graph"); |
| @@ -173,6 +173,103 @@ class TestAscir: | |||
| 173 | == "Check dtype failed for cast_0 Cast; input_dtypes: [DT_INT8], output_dtypes: [DT_INT4]" | 173 | == "Check dtype failed for cast_0 Cast; input_dtypes: [DT_INT8], output_dtypes: [DT_INT4]" |
| 174 | ) | 174 | ) |
| 175 | 175 | ||
| 176 | + | ||
| 177 | + def test_index_expr_and_arange_api(): | ||
| 178 | + ascir.utils.set_platform("3510", 1, 245760) | ||
| 179 | + try: | ||
| 180 | + graph = ascir.HintGraph("index_expr_arange_api") | ||
| 181 | + size = graph.create_size("size") | ||
| 182 | + axis = graph.create_axis("axis", size) | ||
| 183 | + | ||
| 184 | + index = ascir_api.IndexExpr(graph, dtype=ascir.dtypes.int64, expr=size + 1) | ||
| 185 | + values = ascir_api.arange( | ||
| 186 | + graph, | ||
| 187 | + dtype=ascir.dtypes.int64, | ||
| 188 | + base=ascir.SizeExpr(3), | ||
| 189 | + step=ascir.SizeExpr(2), | ||
| 190 | + axis=[axis], | ||
| 191 | + size=[size], | ||
| 192 | + stride=[ascir.SizeExpr(1)], | ||
| 193 | + ) | ||
| 194 | + | ||
| 195 | + assert index.dtype == ascir.dtypes.int64 | ||
| 196 | + assert values.dtype == ascir.dtypes.int64 | ||
| 197 | + assert values.axis == [axis.id] | ||
| 198 | + assert values.size == [size] | ||
| 199 | + assert values.strides == [ascir.SizeExpr(1)] | ||
| 200 | + assert not hasattr(values, "vectorized_axis") | ||
| 201 | + assert not hasattr(values, "vectorized_strides") | ||
| 202 | + | ||
| 203 | + singleton = graph.create_axis("singleton", 1) | ||
| 204 | + trailing_singleton = ascir_api.arange( | ||
| 205 | + graph, | ||
| 206 | + dtype=ascir.dtypes.int64, | ||
| 207 | + base=ascir.SizeExpr(0), | ||
| 208 | + step=ascir.SizeExpr(1), | ||
| 209 | + axis=[axis, singleton], | ||
| 210 | + size=[size, ascir.SizeExpr(1)], | ||
| 211 | + stride=[ascir.SizeExpr(1), ascir.SizeExpr(0)], | ||
| 212 | + ) | ||
| 213 | + assert trailing_singleton.strides == [ascir.SizeExpr(1), ascir.SizeExpr(0)] | ||
| 214 | + | ||
| 215 | + stored = ascir_api.Store( | ||
| 216 | + graph, | ||
| 217 | + values, | ||
| 218 | + axis=[axis], | ||
| 219 | + size=[size], | ||
| 220 | + stride=[ascir.SizeExpr(1)], | ||
| 221 | + ) | ||
| 222 | + ascir_api.Output(graph, stored, dtype=ascir.dtypes.int64) | ||
| 223 | + Autofuser(AutofuserOptions()).schedule(graph) | ||
| 224 | + | ||
| 225 | + with pytest.raises(ValueError, match="axis must not be empty"): | ||
| 226 | + ascir_api.arange( | ||
| 227 | + graph, | ||
| 228 | + dtype=ascir.dtypes.int32, | ||
| 229 | + base=ascir.SizeExpr(0), | ||
| 230 | + step=ascir.SizeExpr(1), | ||
| 231 | + axis=[], | ||
| 232 | + ) | ||
| 233 | + with pytest.raises(ValueError, match="unit physical stride"): | ||
| 234 | + ascir_api.arange( | ||
| 235 | + graph, | ||
| 236 | + dtype=ascir.dtypes.int32, | ||
| 237 | + base=ascir.SizeExpr(0), | ||
| 238 | + step=ascir.SizeExpr(1), | ||
| 239 | + axis=[axis], | ||
| 240 | + size=[size], | ||
| 241 | + stride=[ascir.SizeExpr(2)], | ||
| 242 | + ) | ||
| 243 | + with pytest.raises(ValueError, match="zero strides require singleton"): | ||
| 244 | + ascir_api.arange( | ||
| 245 | + graph, | ||
| 246 | + dtype=ascir.dtypes.int32, | ||
| 247 | + base=ascir.SizeExpr(0), | ||
| 248 | + step=ascir.SizeExpr(1), | ||
| 249 | + axis=[axis, singleton], | ||
| 250 | + size=[size, ascir.SizeExpr(2)], | ||
| 251 | + stride=[ascir.SizeExpr(1), ascir.SizeExpr(0)], | ||
| 252 | + ) | ||
| 253 | + with pytest.raises(ValueError, match="same with axis len"): | ||
| 254 | + ascir_api.arange( | ||
| 255 | + graph, | ||
| 256 | + dtype=ascir.dtypes.int32, | ||
| 257 | + base=ascir.SizeExpr(0), | ||
| 258 | + step=ascir.SizeExpr(1), | ||
| 259 | + axis=[axis], | ||
| 260 | + stride=[], | ||
| 261 | + ) | ||
| 262 | + with pytest.raises(Exception, match="Check dtype failed"): | ||
| 263 | + ascir_api.arange( | ||
| 264 | + graph, | ||
| 265 | + dtype=ascir.dtypes.float32, | ||
| 266 | + base=ascir.SizeExpr(0), | ||
| 267 | + step=ascir.SizeExpr(1), | ||
| 268 | + axis=[axis], | ||
| 269 | + ) | ||
| 270 | + finally: | ||
| 271 | + ascir.utils.set_platform("2201", 1, 245760) | ||
| 272 | + | ||
| 176 | 273 | ||
| 177 | def test_indirect_load_max_expression_attr(): | 274 | def test_indirect_load_max_expression_attr(): |
| 178 | graph = ascir.HintGraph("test_indirect_load_max") | 275 | graph = ascir.HintGraph("test_indirect_load_max") |
| @@ -0,0 +1,151 @@ | |||
| 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 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | +namespace codegen { | ||
| 25 | +namespace { | ||
| 26 | +struct ArangeMicroFixture { | ||
| 27 | + af::AscGraph graph{"micro_arange"}; | ||
| 28 | + af::AscNodePtr arange; | ||
| 29 | + TensorManager tensor_manager; | ||
| 30 | + Tiler tiler; | ||
| 31 | + TPipe tpipe{"tpipe", tiler}; | ||
| 32 | + | ||
| 33 | + ArangeMicroFixture(const af::Expression &base, const af::Expression &step, ge::DataType dtype = ge::DT_INT32) { | ||
| 34 | + const auto size = graph.CreateSizeVar("size"); | ||
| 35 | + const auto axis = graph.CreateAxis("axis", size); | ||
| 36 | + af::ascir_op::Arange arange_op("arange"); | ||
| 37 | + graph.AddNode(arange_op); | ||
| 38 | + arange_op.ir_attr.SetBase(base); | ||
| 39 | + arange_op.ir_attr.SetStep(step); | ||
| 40 | + arange_op.y.dtype = dtype; | ||
| 41 | + *arange_op.y.axis = {axis.id}; | ||
| 42 | + *arange_op.y.repeats = {size}; | ||
| 43 | + *arange_op.y.strides = {af::sym::kSymbolOne}; | ||
| 44 | + *arange_op.y.vectorized_axis = {axis.id}; | ||
| 45 | + *arange_op.y.vectorized_strides = {af::sym::kSymbolOne}; | ||
| 46 | + | ||
| 47 | + arange = graph.FindNode("arange"); | ||
| 48 | + arange->outputs[0].attr.mem.tensor_id = 0; | ||
| 49 | + std::string dtype_name; | ||
| 50 | + EXPECT_EQ(Tensor::DtypeName(dtype, dtype_name), af::SUCCESS); | ||
| 51 | + EXPECT_EQ(tensor_manager.AddTensor(MicroApiTensor(arange->outputs[0], dtype_name)), af::SUCCESS); | ||
| 52 | + tiler.AddSizeVar(af::SizeVar(size)); | ||
| 53 | + } | ||
| 54 | +}; | ||
| 55 | + | ||
| 56 | +class MicroArangeApiCallTest : public testing::Test { | ||
| 57 | + protected: | ||
| 58 | + void SetUp() override { | ||
| 59 | + ge::PlatformContext::GetInstance().Reset(); | ||
| 60 | + ge::RuntimeStub::SetInstance(std::make_shared<ge::RuntimeStubV2Common>()); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + void TearDown() override { | ||
| 64 | + ge::RuntimeStub::Reset(); | ||
| 65 | + ge::PlatformContext::GetInstance().Reset(); | ||
| 66 | + } | ||
| 67 | +}; | ||
| 68 | +} // namespace | ||
| 69 | + | ||
| 70 | +TEST_F(MicroArangeApiCallTest, GeneratesDirectArangeForUnitStep) { | ||
| 71 | + ArangeMicroFixture fixture(af::Symbol(3), af::Symbol(1)); | ||
| 72 | + MicroArangeApiCall call("Arange"); | ||
| 73 | + ASSERT_EQ(call.Init(fixture.arange), af::SUCCESS); | ||
| 74 | + call.AddOutput(0); | ||
| 75 | + | ||
| 76 | + CallParam param{"p_reg", "", "int32_t"}; | ||
| 77 | + std::string result; | ||
| 78 | + ASSERT_EQ(call.Generate(fixture.tensor_manager, fixture.tpipe, param, result), af::SUCCESS); | ||
| 79 | + EXPECT_EQ(result, "AscendC::Reg::Arange(vreg_0, static_cast<int32_t>(3));\n"); | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +TEST_F(MicroArangeApiCallTest, GeneratesInt64WithVectorBlockOffset) { | ||
| 83 | + ArangeMicroFixture fixture(af::Symbol(3), af::Symbol(2), ge::DT_INT64); | ||
| 84 | + MicroArangeApiCall call("Arange"); | ||
| 85 | + ASSERT_EQ(call.Init(fixture.arange), af::SUCCESS); | ||
| 86 | + call.AddOutput(0); | ||
| 87 | + | ||
| 88 | + CallParam param{"p_reg", "axis * ELEMENT_PER_VECTOR_LENGTH * 3", "int64_t"}; | ||
| 89 | + std::string result; | ||
| 90 | + ASSERT_EQ(call.Generate(fixture.tensor_manager, fixture.tpipe, param, result), af::SUCCESS); | ||
| 91 | + EXPECT_NE(result.find("axis * ELEMENT_PER_VECTOR_LENGTH * 3"), std::string::npos); | ||
| 92 | + EXPECT_NE(result.find("3 + (axis * ELEMENT_PER_VECTOR_LENGTH * 3) * (2)"), std::string::npos); | ||
| 93 | + EXPECT_NE(result.find("static_cast<int64_t>(2)"), std::string::npos); | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +TEST_F(MicroArangeApiCallTest, GeneratesInt32WithVectorBlockOffset) { | ||
| 97 | + ArangeMicroFixture fixture(af::Symbol(3), af::Symbol(2)); | ||
| 98 | + MicroArangeApiCall call("Arange"); | ||
| 99 | + ASSERT_EQ(call.Init(fixture.arange), af::SUCCESS); | ||
| 100 | + call.AddOutput(0); | ||
| 101 | + | ||
| 102 | + CallParam param{"p_reg", "axis * ELEMENT_PER_VECTOR_LENGTH * 3", "int32_t"}; | ||
| 103 | + std::string result; | ||
| 104 | + ASSERT_EQ(call.Generate(fixture.tensor_manager, fixture.tpipe, param, result), af::SUCCESS); | ||
| 105 | + EXPECT_NE(result.find("axis * ELEMENT_PER_VECTOR_LENGTH * 3"), std::string::npos); | ||
| 106 | + EXPECT_NE(result.find("3 + (axis * ELEMENT_PER_VECTOR_LENGTH * 3) * (2)"), std::string::npos); | ||
| 107 | + EXPECT_NE(result.find("static_cast<int32_t>(2)"), std::string::npos); | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +TEST_F(MicroArangeApiCallTest, GeneratesScaledArangeWithDynamicBaseAndStep) { | ||
| 111 | + ArangeMicroFixture fixture(af::Expression::Parse("size + 1"), af::Expression::Parse("size * 2")); | ||
| 112 | + MicroArangeApiCall call("Arange"); | ||
| 113 | + ASSERT_EQ(call.Init(fixture.arange), af::SUCCESS); | ||
| 114 | + call.AddOutput(0); | ||
| 115 | + | ||
| 116 | + CallParam param{"p_reg", "", "int32_t"}; | ||
| 117 | + std::string result; | ||
| 118 | + ASSERT_EQ(call.Generate(fixture.tensor_manager, fixture.tpipe, param, result), af::SUCCESS); | ||
| 119 | + EXPECT_EQ(result, | ||
| 120 | + "AscendC::Reg::Arange(vreg_0, static_cast<int32_t>(0));\n" | ||
| 121 | + "AscendC::Reg::Muls(vreg_0, vreg_0, static_cast<int32_t>((2 * t->size)), p_reg);\n" | ||
| 122 | + "AscendC::Reg::Adds(vreg_0, vreg_0, static_cast<int32_t>((1 + t->size)), p_reg);\n"); | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +TEST_F(MicroArangeApiCallTest, UsesVfArangeParamsWhenProvided) { | ||
| 126 | + ArangeMicroFixture fixture(af::Symbol(3), af::Symbol(2)); | ||
| 127 | + MicroArangeApiCall call("Arange"); | ||
| 128 | + ASSERT_EQ(call.Init(fixture.arange), af::SUCCESS); | ||
| 129 | + call.AddOutput(0); | ||
| 130 | + | ||
| 131 | + CallParam param{"p_reg", "", "int32_t", {true, "arange_base_0", "arange_step_0"}}; | ||
| 132 | + std::string result; | ||
| 133 | + ASSERT_EQ(call.Generate(fixture.tensor_manager, fixture.tpipe, param, result), af::SUCCESS); | ||
| 134 | + EXPECT_NE(result.find("arange_base_0"), std::string::npos); | ||
| 135 | + EXPECT_NE(result.find("arange_step_0"), std::string::npos); | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | +TEST_F(MicroArangeApiCallTest, FactoryCreatesArangeCall) { | ||
| 139 | + ArangeMicroFixture fixture(af::Symbol(0), af::Symbol(1)); | ||
| 140 | + std::unique_ptr<MicroApiCall> call(CreateMicroApiCallObject(fixture.arange)); | ||
| 141 | + ASSERT_NE(call, nullptr); | ||
| 142 | + EXPECT_NE(dynamic_cast<MicroArangeApiCall *>(call.get()), nullptr); | ||
| 143 | + EXPECT_EQ(call->Init(fixture.arange), af::SUCCESS); | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +TEST_F(MicroArangeApiCallTest, RejectsUnsupportedDtype) { | ||
| 147 | + ArangeMicroFixture fixture(af::Symbol(0), af::Symbol(1), ge::DT_FLOAT); | ||
| 148 | + MicroArangeApiCall call("Arange"); | ||
| 149 | + EXPECT_NE(call.Init(fixture.arange), af::SUCCESS); | ||
| 150 | +} | ||
| 151 | +} // namespace codegen | ||
| @@ -129,8 +129,7 @@ TEST(CodegenKernel, StoreRegApiCall_TwoStoreOneOutput) { | |||
| 129 | call_0.Generate(tpipe, vector<af::AxisId>{}, result); | 129 | call_0.Generate(tpipe, vector<af::AxisId>{}, result); |
| 130 | EXPECT_EQ( | 130 | EXPECT_EQ( |
| 131 | result, | 131 | result, |
| 132 | - std::string{ | 132 | + std::string{"DataCopyPadExtend<float, AscendC::PaddingMode::Normal>(local_1[0 + 0], local_0[0], 1, 1, 0, 0);\n"}); |
| 133 | - "DataCopyPadExtend<float, AscendC::PaddingMode::Normal>(local_1[0 + 0], local_0[0], 1, 1, (16 - 1), 0);\n"}); | ||
| 134 | 133 | ||
| 135 | codegen::StoreRegApiCall call_1("DataCopyPadExtend"); | 134 | codegen::StoreRegApiCall call_1("DataCopyPadExtend"); |
| 136 | EXPECT_EQ(call_1.Init(store_1), 0); | 135 | EXPECT_EQ(call_1.Init(store_1), 0); |
| @@ -351,13 +350,12 @@ TEST(CodegenKernel, StoreRegApiCall_NeetMte3SyncMte2) { | |||
| 351 | call_1.Generate(kernel.tpipe, vector<af::AxisId>{}, result); | 350 | call_1.Generate(kernel.tpipe, vector<af::AxisId>{}, result); |
| 352 | EXPECT_EQ( | 351 | EXPECT_EQ( |
| 353 | result, | 352 | result, |
| 354 | - std::string{ | 353 | + std::string{"DataCopyPadExtend<float, AscendC::PaddingMode::Normal>(local_1[0 + 0], local_0[0], 1, 1, 0, 0);\n" |
| 355 | - "DataCopyPadExtend<float, AscendC::PaddingMode::Normal>(local_1[0 + 0], local_0[0], 1, 1, (16 - 1), 0);\n" | 354 | + "auto local_0_e_mte3_2_mte2_t_0 = tpipe.AllocEventID<HardEvent::MTE3_MTE2>();\n" |
| 356 | - "auto local_0_e_mte3_2_mte2_t_0 = tpipe.AllocEventID<HardEvent::MTE3_MTE2>();\n" | 355 | + "TQueSync<PIPE_MTE3, PIPE_MTE2> local_0_s_mte3_2_mte2_t_0;\n" |
| 357 | - "TQueSync<PIPE_MTE3, PIPE_MTE2> local_0_s_mte3_2_mte2_t_0;\n" | 356 | + "local_0_s_mte3_2_mte2_t_0.SetFlag(local_0_e_mte3_2_mte2_t_0);\n" |
| 358 | - "local_0_s_mte3_2_mte2_t_0.SetFlag(local_0_e_mte3_2_mte2_t_0);\n" | 357 | + "local_0_s_mte3_2_mte2_t_0.WaitFlag(local_0_e_mte3_2_mte2_t_0);\n" |
| 359 | - "local_0_s_mte3_2_mte2_t_0.WaitFlag(local_0_e_mte3_2_mte2_t_0);\n" | 358 | + "tpipe.ReleaseEventID<HardEvent::MTE3_MTE2>(local_0_e_mte3_2_mte2_t_0);\n"}); |
| 360 | - "tpipe.ReleaseEventID<HardEvent::MTE3_MTE2>(local_0_e_mte3_2_mte2_t_0);\n"}); | ||
| 361 | } | 359 | } |
| 362 | 360 | ||
| 363 | TEST(CodegenKernel, StoreRegApiCall_ThreeDimStore) { | 361 | TEST(CodegenKernel, StoreRegApiCall_ThreeDimStore) { |
| @@ -20,6 +20,8 @@ | |||
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | 22 | ||
| 23 | + | ||
| 24 | + | ||
| 23 | 25 | ||
| 24 | 26 | ||
| 25 | 27 | ||
| @@ -30,6 +32,47 @@ using namespace af::ascir_op; | |||
| 30 | using namespace codegen; | 32 | using namespace codegen; |
| 31 | 33 | ||
| 32 | namespace { | 34 | namespace { |
| 35 | +std::string GetLineContaining(const std::string &text, const std::string &needle) { | ||
| 36 | + const auto offset = text.find(needle); | ||
| 37 | + if (offset == std::string::npos) { | ||
| 38 | + return {}; | ||
| 39 | + } | ||
| 40 | + const auto line_begin = text.rfind('\n', offset); | ||
| 41 | + const auto line_end = text.find('\n', offset); | ||
| 42 | + return text.substr(line_begin == std::string::npos ? 0UL : line_begin + 1UL, | ||
| 43 | + line_end == std::string::npos ? std::string::npos : line_end - line_begin - 1UL); | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +class FailingMicroApiCall final : public MicroApiCall { | ||
| 47 | + public: | ||
| 48 | + FailingMicroApiCall() : MicroApiCall("FailingMicroApiCall") {} | ||
| 49 | + | ||
| 50 | + Status Generate(const TensorManager &, const TPipe &, CallParam &, std::string &) override { | ||
| 51 | + return af::FAILED; | ||
| 52 | + } | ||
| 53 | +}; | ||
| 54 | + | ||
| 55 | +class FixedArangeMicroApiCall final : public MicroApiCall { | ||
| 56 | + public: | ||
| 57 | + FixedArangeMicroApiCall(int64_t tensor_id, std::string base, std::string step) | ||
| 58 | + : MicroApiCall("FixedArangeMicroApiCall"), base_(std::move(base)), step_(std::move(step)) { | ||
| 59 | + AddOutput(tensor_id); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + bool HasArangeParam() const override { | ||
| 63 | + return true; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + void GetArangeParams(const TPipe &, std::string &base, std::string &step) const override { | ||
| 67 | + base = base_; | ||
| 68 | + step = step_; | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + private: | ||
| 72 | + std::string base_; | ||
| 73 | + std::string step_; | ||
| 74 | +}; | ||
| 75 | + | ||
| 33 | template <typename TensorLike> | 76 | template <typename TensorLike> |
| 34 | void SetTwoDimSchedule(TensorLike &tensor, const af::Axis &z0, const af::Axis &z1, const af::Expression &s0, | 77 | void SetTwoDimSchedule(TensorLike &tensor, const af::Axis &z0, const af::Axis &z1, const af::Expression &s0, |
| 35 | const af::Expression &s1) { | 78 | const af::Expression &s1) { |
| @@ -63,7 +106,7 @@ codegen::Tensor MakeCvUbFuseTensor(af::AscGraph &graph, ge::DataType dtype, int6 | |||
| 63 | } | 106 | } |
| 64 | 107 | ||
| 65 | void InitScalarDataVfGraph(VectorFunc &vf_op, Store &store_op, Broadcast &sub_brc_op, Abs &abs_op, Store &sub_store_op, | 108 | void InitScalarDataVfGraph(VectorFunc &vf_op, Store &store_op, Broadcast &sub_brc_op, Abs &abs_op, Store &sub_store_op, |
| 66 | - Output &sub_output_op, const ScalarData &scalar_data_op, const Scalar &sub_scalar_op, | 109 | + Output &sub_output_op, const ScalarData &scalar_data_op, Scalar &sub_scalar_op, |
| 67 | const af::Axis &z0, const af::Axis &z1, const af::Expression &s0, const af::Expression &s1) { | 110 | const af::Axis &z0, const af::Axis &z1, const af::Expression &s0, const af::Expression &s1) { |
| 68 | vf_op.InstanceOutputy(1); | 111 | vf_op.InstanceOutputy(1); |
| 69 | vf_op.x = {scalar_data_op.y}; | 112 | vf_op.x = {scalar_data_op.y}; |
| @@ -75,6 +118,7 @@ void InitScalarDataVfGraph(VectorFunc &vf_op, Store &store_op, Broadcast &sub_br | |||
| 75 | SetTwoDimSchedule(store_op.y, z0, z1, s0, s1); | 118 | SetTwoDimSchedule(store_op.y, z0, z1, s0, s1); |
| 76 | 119 | ||
| 77 | sub_brc_op.x = sub_scalar_op.y; | 120 | sub_brc_op.x = sub_scalar_op.y; |
| 121 | + sub_scalar_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 78 | sub_brc_op.attr.sched.axis = {z0.id, z1.id}; | 122 | sub_brc_op.attr.sched.axis = {z0.id, z1.id}; |
| 79 | SetTwoDimSchedule(sub_brc_op.y, z0, z1, s0, s1); | 123 | SetTwoDimSchedule(sub_brc_op.y, z0, z1, s0, s1); |
| 80 | 124 | ||
| @@ -86,6 +130,7 @@ void InitScalarDataVfGraph(VectorFunc &vf_op, Store &store_op, Broadcast &sub_br | |||
| 86 | sub_store_op.attr.sched.axis = {z0.id, z1.id}; | 130 | sub_store_op.attr.sched.axis = {z0.id, z1.id}; |
| 87 | SetTwoDimSchedule(sub_store_op.y, z0, z1, s0, s1); | 131 | SetTwoDimSchedule(sub_store_op.y, z0, z1, s0, s1); |
| 88 | sub_output_op.x = sub_store_op.y; | 132 | sub_output_op.x = sub_store_op.y; |
| 133 | + sub_output_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 89 | } | 134 | } |
| 90 | 135 | ||
| 91 | void InitScalarDataVfTensorAttrs(AscGraph &graph, AscGraph &vf_sub_graph, const af::Axis &z0, const af::Axis &z1, | 136 | void InitScalarDataVfTensorAttrs(AscGraph &graph, AscGraph &vf_sub_graph, const af::Axis &z0, const af::Axis &z1, |
| @@ -130,8 +175,9 @@ std::string GenerateScalarDataVfCall(AscGraph &graph, const af::Axis &z0, const | |||
| 130 | auto vf = graph.FindNode("vf"); | 175 | auto vf = graph.FindNode("vf"); |
| 131 | codegen::Tiler tiler; | 176 | codegen::Tiler tiler; |
| 132 | codegen::TPipe tpipe("tpipe", tiler); | 177 | codegen::TPipe tpipe("tpipe", tiler); |
| 178 | + EXPECT_EQ(tpipe.CollectQues(graph), af::SUCCESS); | ||
| 133 | EXPECT_EQ(tpipe.AddTensor(scalar_data->outputs[0], "scalar_data_y"), 0); | 179 | EXPECT_EQ(tpipe.AddTensor(scalar_data->outputs[0], "scalar_data_y"), 0); |
| 134 | - tpipe.AddTensor(vf->outputs[0]); | 180 | + EXPECT_EQ(tpipe.AddTensor(vf->outputs[0]), af::SUCCESS); |
| 135 | 181 | ||
| 136 | tiler.AddAxis(z0); | 182 | tiler.AddAxis(z0); |
| 137 | tiler.AddAxis(z1); | 183 | tiler.AddAxis(z1); |
| @@ -237,6 +283,9 @@ void PrepareDoubleLoopCodegen(AscGraph &graph, const std::vector<af::Axis> &axes | |||
| 237 | codegen::VfCall &call, std::stringstream &func_def) { | 283 | codegen::VfCall &call, std::stringstream &func_def) { |
| 238 | auto load = graph.FindNode("load"); | 284 | auto load = graph.FindNode("load"); |
| 239 | auto vf = graph.FindNode("vf"); | 285 | auto vf = graph.FindNode("vf"); |
| 286 | + // 模拟生产 Kernel::ParseGraph 流程: queue 类型 tensor 入 tpipe 前需先从图收集 que 定义, | ||
| 287 | + // 否则 AddTensor 报 "Cannot find que"。 | ||
| 288 | + ASSERT_EQ(tpipe.CollectQues(graph), af::SUCCESS); | ||
| 240 | tpipe.AddTensor(load->outputs[0]); | 289 | tpipe.AddTensor(load->outputs[0]); |
| 241 | tpipe.AddTensor(vf->outputs[0]); | 290 | tpipe.AddTensor(vf->outputs[0]); |
| 242 | for (const auto &axis : axes) { | 291 | for (const auto &axis : axes) { |
| @@ -259,6 +308,10 @@ void RegisterDoubleLoopNodes(AscGraph &graph, AscGraph &vf_sub_graph, VectorFunc | |||
| 259 | vf_op.SetAttr("sub_graph_name", sub_graph_name); | 308 | vf_op.SetAttr("sub_graph_name", sub_graph_name); |
| 260 | sub_x_op.ir_attr.SetIndex(0); | 309 | sub_x_op.ir_attr.SetIndex(0); |
| 261 | sub_output_op.ir_attr.SetIndex(0); | 310 | sub_output_op.ir_attr.SetIndex(0); |
| 311 | + // 生产图 CompleteApiInfo 会将 Data/Output 的 api.unit 置为 kUnitNone(不发射代码); | ||
| 312 | + // 手工 fixture 需显式设置, 否则 base MicroApiCall 会参与 Generate 并失败。 | ||
| 313 | + sub_x_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 314 | + sub_output_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 262 | graph.AddNode(load_op); | 315 | graph.AddNode(load_op); |
| 263 | graph.AddSubGraph(vf_sub_graph); | 316 | graph.AddSubGraph(vf_sub_graph); |
| 264 | graph.AddNode(store_op); | 317 | graph.AddNode(store_op); |
| @@ -279,6 +332,588 @@ void CheckDoubleLoopVectorFuncParams(const af::AscNodePtr &vf) { | |||
| 279 | } | 332 | } |
| 280 | } // namespace | 333 | } // namespace |
| 281 | 334 | ||
| 335 | +TEST(VFLoopTest, PropagatesFailureWhenMicroApiCallFails) { | ||
| 336 | + // 生成失败必须上抛: 吞错会在真机产生未初始化寄存器参与计算的静默错误数据。 | ||
| 337 | + VFLoop loop(af::kIdNone); | ||
| 338 | + loop.AddCall(new FailingMicroApiCall()); | ||
| 339 | + | ||
| 340 | + Tiler tiler; | ||
| 341 | + TPipe tpipe("tpipe", tiler); | ||
| 342 | + TensorManager tensor_manager; | ||
| 343 | + std::string result; | ||
| 344 | + std::string loop_size; | ||
| 345 | + int32_t max_depth = -1; | ||
| 346 | + std::vector<std::string> loop_sizes; | ||
| 347 | + EXPECT_NE(loop.Generate(tpipe, tensor_manager, 0, result, loop_size, max_depth, loop_sizes), af::SUCCESS); | ||
| 348 | + EXPECT_NE(loop.GenerateCvUbFuse(tpipe, tensor_manager, result, loop_size), af::SUCCESS); | ||
| 349 | + loop.Destruct(); | ||
| 350 | +} | ||
| 351 | + | ||
| 352 | +TEST(VFLoopTest, GeneratesInt64ArangeAcrossVectorBlocks) { | ||
| 353 | + ge::SetupRuntimeStub(); | ||
| 354 | + af::AscGraph graph("arange_vf_loop"); | ||
| 355 | + const auto size = graph.CreateSizeVar("size"); | ||
| 356 | + const auto axis = graph.CreateAxis("axis", size); | ||
| 357 | + af::AscGraph vf_sub_graph("arange_vf_sub_graph"); | ||
| 358 | + VectorFunc vf_op("vf"); | ||
| 359 | + vf_op.InstanceOutputy(1); | ||
| 360 | + vf_op.SetAttr("sub_graph_name", "arange_vf_sub_graph"); | ||
| 361 | + graph.AddSubGraph(vf_sub_graph); | ||
| 362 | + graph.AddNode(vf_op); | ||
| 363 | + | ||
| 364 | + Arange arange_op("arange"); | ||
| 365 | + Abs abs_op("abs"); | ||
| 366 | + Output output_op("output"); | ||
| 367 | + arange_op.ir_attr.SetBase(af::Symbol(3)); | ||
| 368 | + arange_op.ir_attr.SetStep(af::Symbol(2)); | ||
| 369 | + arange_op.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 370 | + arange_op.attr.sched.axis = {axis.id}; | ||
| 371 | + arange_op.y.dtype = af::DT_INT64; | ||
| 372 | + *arange_op.y.axis = {axis.id}; | ||
| 373 | + *arange_op.y.repeats = {size}; | ||
| 374 | + *arange_op.y.strides = {One}; | ||
| 375 | + *arange_op.y.vectorized_axis = {axis.id}; | ||
| 376 | + *arange_op.y.vectorized_strides = {One}; | ||
| 377 | + abs_op.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 378 | + abs_op.attr.sched.axis = {axis.id}; | ||
| 379 | + abs_op.y.dtype = af::DT_INT64; | ||
| 380 | + *abs_op.y.axis = {axis.id}; | ||
| 381 | + *abs_op.y.repeats = {size}; | ||
| 382 | + *abs_op.y.strides = {One}; | ||
| 383 | + *abs_op.y.vectorized_axis = {axis.id}; | ||
| 384 | + *abs_op.y.vectorized_strides = {One}; | ||
| 385 | + output_op.ir_attr.SetIndex(0); | ||
| 386 | + vf_sub_graph.AddNode(arange_op); | ||
| 387 | + vf_sub_graph.AddNode(abs_op); | ||
| 388 | + vf_sub_graph.AddNode(output_op); | ||
| 389 | + abs_op.x = arange_op.y; | ||
| 390 | + output_op.x = abs_op.y; | ||
| 391 | + const auto node = vf_sub_graph.FindNode("arange"); | ||
| 392 | + node->outputs[0].attr.mem.tensor_id = 0; | ||
| 393 | + auto abs_node = vf_sub_graph.FindNode("abs"); | ||
| 394 | + abs_node->outputs[0].attr.mem.tensor_id = 2; | ||
| 395 | + auto vf_node = graph.FindNode("vf"); | ||
| 396 | + vf_node->outputs[0].attr.dtype = af::DT_INT64; | ||
| 397 | + vf_node->outputs[0].attr.mem.tensor_id = 1; | ||
| 398 | + vf_node->outputs[0].attr.mem.position = af::Position::kPositionVecOut; | ||
| 399 | + | ||
| 400 | + std::string dtype_name; | ||
| 401 | + ASSERT_EQ(codegen::Tensor::DtypeName(af::DT_INT64, dtype_name), af::SUCCESS); | ||
| 402 | + TensorManager tensor_manager; | ||
| 403 | + ASSERT_EQ(tensor_manager.AddTensor(MicroApiTensor(node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 404 | + ASSERT_EQ(tensor_manager.AddTensor(MicroApiTensor(vf_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 405 | + ASSERT_EQ(tensor_manager.AddTensor(MicroApiTensor(abs_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 406 | + Tiler tiler; | ||
| 407 | + tiler.AddAxis(axis); | ||
| 408 | + tiler.AddSizeVar(af::SizeVar(size)); | ||
| 409 | + TPipe tpipe("tpipe", tiler); | ||
| 410 | + ASSERT_EQ(tpipe.AddTensor(vf_node->outputs[0]), af::SUCCESS); | ||
| 411 | + | ||
| 412 | + VfCall vf_call; | ||
| 413 | + ASSERT_EQ(vf_call.Init(vf_node), af::SUCCESS); | ||
| 414 | + tpipe.cv_fusion_type = ::ascir::CubeTemplateType::kUBFuse; | ||
| 415 | + std::stringstream ub_fuse_definition; | ||
| 416 | + EXPECT_NE(vf_call.GenerateFuncDefinition(tpipe, tiler, ub_fuse_definition), af::SUCCESS); | ||
| 417 | + tpipe.cv_fusion_type = ::ascir::CubeTemplateType::kDefault; | ||
| 418 | + | ||
| 419 | + VFLoop loop(axis.id); | ||
| 420 | + loop.SetMaxDtypeSize("int64_t"); | ||
| 421 | + ASSERT_EQ(loop.ConstructFromNodes(vf_sub_graph.GetAllNodes(), vf_node), af::SUCCESS); | ||
| 422 | + | ||
| 423 | + std::string result; | ||
| 424 | + std::string loop_size; | ||
| 425 | + int32_t max_depth = -1; | ||
| 426 | + std::vector<std::string> loop_sizes; | ||
| 427 | + ASSERT_EQ( | ||
| 428 | + loop.Generate(tpipe, tensor_manager, 0, result, loop_size, max_depth, loop_sizes, {{0, "device_block_offset"}}), | ||
| 429 | + af::SUCCESS); | ||
| 430 | + EXPECT_NE(result.find("for (uint16_t axis"), std::string::npos); | ||
| 431 | + const auto arange_adds = GetLineContaining(result, "AscendC::Reg::Adds"); | ||
| 432 | + ASSERT_FALSE(arange_adds.empty()); | ||
| 433 | + EXPECT_EQ(arange_adds, | ||
| 434 | + "AscendC::Reg::Adds(vreg_0, vreg_0, static_cast<int64_t>((arange_base_0 + (device_block_offset + " | ||
| 435 | + "(axis * ELEMENT_PER_VECTOR_LENGTH)) * (arange_step_0))), preg_0);"); | ||
| 436 | + loop.Destruct(); | ||
| 437 | +} | ||
| 438 | + | ||
| 439 | +TEST(CodegenKernel, HighRankArangeOuterForUsesLogicalOffset) { | ||
| 440 | + for (const size_t rank : {5UL, 6UL}) { | ||
| 441 | + ge::SetupRuntimeStub(); | ||
| 442 | + af::AscGraph graph(("high_rank_arange_" + std::to_string(rank)).c_str()); | ||
| 443 | + std::vector<af::Axis> axes; | ||
| 444 | + std::vector<af::AxisId> axis_ids; | ||
| 445 | + std::vector<af::Expression> logical_strides; | ||
| 446 | + for (size_t i = 0UL; i < rank; ++i) { | ||
| 447 | + axes.push_back(graph.CreateAxis("axis" + std::to_string(i), af::Symbol(2))); | ||
| 448 | + axis_ids.push_back(axes.back().id); | ||
| 449 | + logical_strides.push_back(af::Symbol(static_cast<int64_t>(1UL << (rank - i - 1UL)))); | ||
| 450 | + } | ||
| 451 | + const std::vector<af::Expression> repeats(rank, af::Symbol(2)); | ||
| 452 | + const std::vector<af::Expression> physical_strides = | ||
| 453 | + rank == 5UL ? std::vector<af::Expression>{af::Symbol(64), af::Symbol(24), af::Symbol(10), af::Symbol(3), One} | ||
| 454 | + : std::vector<af::Expression>{af::Symbol(180), af::Symbol(70), af::Symbol(26), | ||
| 455 | + af::Symbol(10), af::Symbol(3), One}; | ||
| 456 | + | ||
| 457 | + af::AscGraph subgraph(("high_rank_arange_subgraph_" + std::to_string(rank)).c_str()); | ||
| 458 | + VectorFunc vf_op("vf"); | ||
| 459 | + vf_op.InstanceOutputy(1); | ||
| 460 | + vf_op.SetAttr("sub_graph_name", "high_rank_arange_subgraph_" + std::to_string(rank)); | ||
| 461 | + graph.AddSubGraph(subgraph); | ||
| 462 | + graph.AddNode(vf_op); | ||
| 463 | + | ||
| 464 | + Arange arange_op("arange"); | ||
| 465 | + arange_op.ir_attr.SetBase(af::Symbol(3)); | ||
| 466 | + arange_op.ir_attr.SetStep(af::Symbol(2)); | ||
| 467 | + arange_op.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 468 | + arange_op.attr.sched.axis = axis_ids; | ||
| 469 | + arange_op.attr.sched.loop_axis = axes.back().id; | ||
| 470 | + arange_op.y.dtype = af::DT_INT64; | ||
| 471 | + *arange_op.y.axis = axis_ids; | ||
| 472 | + *arange_op.y.repeats = repeats; | ||
| 473 | + *arange_op.y.strides = logical_strides; | ||
| 474 | + *arange_op.y.vectorized_axis = axis_ids; | ||
| 475 | + *arange_op.y.vectorized_strides = physical_strides; | ||
| 476 | + | ||
| 477 | + Abs abs_op("abs"); | ||
| 478 | + abs_op.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 479 | + abs_op.attr.sched.axis = axis_ids; | ||
| 480 | + abs_op.attr.sched.loop_axis = axes.back().id; | ||
| 481 | + abs_op.y.dtype = af::DT_INT64; | ||
| 482 | + *abs_op.y.axis = axis_ids; | ||
| 483 | + *abs_op.y.repeats = repeats; | ||
| 484 | + *abs_op.y.strides = logical_strides; | ||
| 485 | + *abs_op.y.vectorized_axis = axis_ids; | ||
| 486 | + *abs_op.y.vectorized_strides = physical_strides; | ||
| 487 | + | ||
| 488 | + Store store_op("store"); | ||
| 489 | + store_op.attr.api.unit = af::ComputeUnit::kUnitMTE3; | ||
| 490 | + store_op.attr.sched.axis = axis_ids; | ||
| 491 | + store_op.y.dtype = af::DT_INT64; | ||
| 492 | + *store_op.y.axis = axis_ids; | ||
| 493 | + *store_op.y.repeats = repeats; | ||
| 494 | + *store_op.y.strides = logical_strides; | ||
| 495 | + *store_op.y.vectorized_axis = axis_ids; | ||
| 496 | + *store_op.y.vectorized_strides = physical_strides; | ||
| 497 | + | ||
| 498 | + Output output_op("output"); | ||
| 499 | + output_op.ir_attr.SetIndex(0); | ||
| 500 | + subgraph.AddNode(arange_op); | ||
| 501 | + subgraph.AddNode(abs_op); | ||
| 502 | + subgraph.AddNode(store_op); | ||
| 503 | + subgraph.AddNode(output_op); | ||
| 504 | + abs_op.x = arange_op.y; | ||
| 505 | + store_op.x = abs_op.y; | ||
| 506 | + output_op.x = store_op.y; | ||
| 507 | + | ||
| 508 | + const auto arange_node = subgraph.FindNode("arange"); | ||
| 509 | + const auto abs_node = subgraph.FindNode("abs"); | ||
| 510 | + ASSERT_NE(arange_node, nullptr); | ||
| 511 | + ASSERT_NE(abs_node, nullptr); | ||
| 512 | + arange_node->outputs[0].attr.mem.tensor_id = 0; | ||
| 513 | + abs_node->outputs[0].attr.mem.tensor_id = 1; | ||
| 514 | + const auto store_node = subgraph.FindNode("store"); | ||
| 515 | + ASSERT_NE(store_node, nullptr); | ||
| 516 | + store_node->outputs[0].attr.mem.tensor_id = 2; | ||
| 517 | + const auto vf_node = graph.FindNode("vf"); | ||
| 518 | + ASSERT_NE(vf_node, nullptr); | ||
| 519 | + vf_node->outputs[0].attr.dtype = af::DT_INT64; | ||
| 520 | + vf_node->outputs[0].attr.axis = axis_ids; | ||
| 521 | + vf_node->outputs[0].attr.repeats = repeats; | ||
| 522 | + vf_node->outputs[0].attr.strides = physical_strides; | ||
| 523 | + vf_node->outputs[0].attr.vectorized_axis = axis_ids; | ||
| 524 | + vf_node->outputs[0].attr.vectorized_strides = physical_strides; | ||
| 525 | + vf_node->outputs[0].attr.mem.tensor_id = 3; | ||
| 526 | + vf_node->outputs[0].attr.mem.position = af::Position::kPositionVecOut; | ||
| 527 | + | ||
| 528 | + Tiler tiler; | ||
| 529 | + for (const auto &axis : axes) { | ||
| 530 | + ASSERT_EQ(tiler.AddAxis(axis), af::SUCCESS); | ||
| 531 | + } | ||
| 532 | + TPipe tpipe("tpipe", tiler); | ||
| 533 | + ASSERT_EQ(tpipe.AddTensor(vf_node->outputs[0]), af::SUCCESS); | ||
| 534 | + VfCall call; | ||
| 535 | + ASSERT_EQ(call.Init(vf_node), af::SUCCESS); | ||
| 536 | + | ||
| 537 | + std::stringstream definition; | ||
| 538 | + ASSERT_EQ(call.GenerateFuncDefinition(tpipe, tiler, definition), af::SUCCESS); | ||
| 539 | + std::string invocation; | ||
| 540 | + ASSERT_EQ(call.Generate(tpipe, {}, invocation), af::SUCCESS); | ||
| 541 | + EXPECT_NE(invocation.find("for(int outer_for_0 = 0;"), std::string::npos) << invocation; | ||
| 542 | + const auto vf_call = GetLineContaining(invocation, "VFCallvf("); | ||
| 543 | + ASSERT_FALSE(vf_call.empty()) << invocation; | ||
| 544 | + EXPECT_NE(vf_call.find("outer_for_0 * " + std::to_string(1UL << (rank - 1UL))), std::string::npos) << vf_call; | ||
| 545 | + EXPECT_EQ(vf_call.find("0 + outer_for_0 * " + std::to_string(rank == 5UL ? 64UL : 180UL)), std::string::npos) | ||
| 546 | + << vf_call; | ||
| 547 | + if (rank == 6UL) { | ||
| 548 | + EXPECT_NE(invocation.find("for(int outer_for_1 = 0;"), std::string::npos) << invocation; | ||
| 549 | + EXPECT_NE(vf_call.find("outer_for_1 * 16"), std::string::npos) << vf_call; | ||
| 550 | + } | ||
| 551 | + } | ||
| 552 | +} | ||
| 553 | + | ||
| 554 | +TEST(VFLoopTest, UsesLogicalOuterStrideForAlignedArangeLayout) { | ||
| 555 | + ge::SetupRuntimeStub(); | ||
| 556 | + af::AscGraph graph("aligned_arange_vf_loop"); | ||
| 557 | + const auto rows = graph.CreateSizeVar("rows"); | ||
| 558 | + const auto cols = graph.CreateSizeVar("cols"); | ||
| 559 | + const auto row_axis = graph.CreateAxis("row", rows); | ||
| 560 | + const auto col_axis = graph.CreateAxis("col", cols); | ||
| 561 | + af::AscGraph vf_sub_graph("aligned_arange_vf_sub_graph"); | ||
| 562 | + VectorFunc vf_op("vf"); | ||
| 563 | + vf_op.InstanceOutputy(1); | ||
| 564 | + vf_op.SetAttr("sub_graph_name", "aligned_arange_vf_sub_graph"); | ||
| 565 | + graph.AddSubGraph(vf_sub_graph); | ||
| 566 | + graph.AddNode(vf_op); | ||
| 567 | + | ||
| 568 | + Arange arange_op("arange"); | ||
| 569 | + Abs abs_op("abs"); | ||
| 570 | + Output output_op("output"); | ||
| 571 | + arange_op.ir_attr.SetBase(af::Symbol(0)); | ||
| 572 | + arange_op.ir_attr.SetStep(af::Symbol(1)); | ||
| 573 | + arange_op.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 574 | + arange_op.attr.sched.axis = {row_axis.id, col_axis.id}; | ||
| 575 | + arange_op.attr.sched.loop_axis = col_axis.id; | ||
| 576 | + arange_op.y.dtype = af::DT_INT64; | ||
| 577 | + *arange_op.y.axis = {row_axis.id, col_axis.id}; | ||
| 578 | + *arange_op.y.repeats = {rows, cols}; | ||
| 579 | + *arange_op.y.strides = {cols, One}; | ||
| 580 | + *arange_op.y.vectorized_axis = {row_axis.id, col_axis.id}; | ||
| 581 | + *arange_op.y.vectorized_strides = {af::Symbol(16), One}; | ||
| 582 | + abs_op.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 583 | + abs_op.attr.sched.axis = {row_axis.id, col_axis.id}; | ||
| 584 | + abs_op.attr.sched.loop_axis = col_axis.id; | ||
| 585 | + abs_op.y.dtype = af::DT_INT64; | ||
| 586 | + *abs_op.y.axis = {row_axis.id, col_axis.id}; | ||
| 587 | + *abs_op.y.repeats = {rows, cols}; | ||
| 588 | + *abs_op.y.strides = {cols, One}; | ||
| 589 | + *abs_op.y.vectorized_axis = {row_axis.id, col_axis.id}; | ||
| 590 | + *abs_op.y.vectorized_strides = {af::Symbol(16), One}; | ||
| 591 | + output_op.ir_attr.SetIndex(0); | ||
| 592 | + vf_sub_graph.AddNode(arange_op); | ||
| 593 | + vf_sub_graph.AddNode(abs_op); | ||
| 594 | + vf_sub_graph.AddNode(output_op); | ||
| 595 | + abs_op.x = arange_op.y; | ||
| 596 | + output_op.x = abs_op.y; | ||
| 597 | + const auto arange_node = vf_sub_graph.FindNode("arange"); | ||
| 598 | + arange_node->outputs[0].attr.mem.tensor_id = 0; | ||
| 599 | + const auto abs_node = vf_sub_graph.FindNode("abs"); | ||
| 600 | + abs_node->outputs[0].attr.mem.tensor_id = 2; | ||
| 601 | + auto vf_node = graph.FindNode("vf"); | ||
| 602 | + vf_node->outputs[0].attr.dtype = af::DT_INT64; | ||
| 603 | + vf_node->outputs[0].attr.mem.tensor_id = 1; | ||
| 604 | + vf_node->outputs[0].attr.mem.position = af::Position::kPositionVecOut; | ||
| 605 | + | ||
| 606 | + std::string dtype_name; | ||
| 607 | + ASSERT_EQ(codegen::Tensor::DtypeName(af::DT_INT64, dtype_name), af::SUCCESS); | ||
| 608 | + TensorManager tensor_manager; | ||
| 609 | + ASSERT_EQ(tensor_manager.AddTensor(MicroApiTensor(arange_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 610 | + ASSERT_EQ(tensor_manager.AddTensor(MicroApiTensor(vf_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 611 | + ASSERT_EQ(tensor_manager.AddTensor(MicroApiTensor(abs_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 612 | + Tiler tiler; | ||
| 613 | + tiler.AddAxis(row_axis); | ||
| 614 | + tiler.AddAxis(col_axis); | ||
| 615 | + tiler.AddSizeVar(af::SizeVar(rows)); | ||
| 616 | + tiler.AddSizeVar(af::SizeVar(cols)); | ||
| 617 | + TPipe tpipe("tpipe", tiler); | ||
| 618 | + ASSERT_EQ(tpipe.AddTensor(vf_node->outputs[0]), af::SUCCESS); | ||
| 619 | + | ||
| 620 | + VFLoop loop(af::kIdNone); | ||
| 621 | + loop.SetMaxDtypeSize("int64_t"); | ||
| 622 | + ASSERT_EQ(loop.ConstructFromNodes(vf_sub_graph.GetAllNodes(), vf_node), af::SUCCESS); | ||
| 623 | + | ||
| 624 | + std::string result; | ||
| 625 | + std::string loop_size; | ||
| 626 | + int32_t max_depth = -1; | ||
| 627 | + std::vector<std::string> loop_sizes; | ||
| 628 | + ASSERT_EQ( | ||
| 629 | + loop.Generate(tpipe, tensor_manager, 0, result, loop_size, max_depth, loop_sizes, {{0, "device_block_offset"}}), | ||
| 630 | + af::SUCCESS); | ||
| 631 | + const auto arange_call = GetLineContaining(result, "AscendC::Reg::Arange"); | ||
| 632 | + ASSERT_FALSE(arange_call.empty()) << result; | ||
| 633 | + EXPECT_NE(arange_call.find("row * t->cols"), std::string::npos); | ||
| 634 | + EXPECT_NE(arange_call.find("col * ELEMENT_PER_VECTOR_LENGTH"), std::string::npos); | ||
| 635 | + EXPECT_EQ(arange_call.find("row * 16"), std::string::npos); | ||
| 636 | + loop.Destruct(); | ||
| 637 | + | ||
| 638 | + arange_node->attr.sched.axis = {row_axis.id}; | ||
| 639 | + arange_node->attr.sched.loop_axis = row_axis.id; | ||
| 640 | + arange_node->outputs[0].attr.repeats = {rows, One}; | ||
| 641 | + arange_node->outputs[0].attr.strides = {One, Zero}; | ||
| 642 | + arange_node->outputs[0].attr.vectorized_strides = {One, Zero}; | ||
| 643 | + abs_node->attr.sched.axis = {row_axis.id}; | ||
| 644 | + abs_node->attr.sched.loop_axis = row_axis.id; | ||
| 645 | + abs_node->outputs[0].attr.repeats = {rows, One}; | ||
| 646 | + abs_node->outputs[0].attr.strides = {One, Zero}; | ||
| 647 | + abs_node->outputs[0].attr.vectorized_strides = {One, Zero}; | ||
| 648 | + TensorManager trailing_singleton_tensor_manager; | ||
| 649 | + ASSERT_EQ(trailing_singleton_tensor_manager.AddTensor(MicroApiTensor(arange_node->outputs[0], dtype_name)), | ||
| 650 | + af::SUCCESS); | ||
| 651 | + ASSERT_EQ(trailing_singleton_tensor_manager.AddTensor(MicroApiTensor(abs_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 652 | + ASSERT_EQ(trailing_singleton_tensor_manager.AddTensor(MicroApiTensor(vf_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 653 | + VFLoop trailing_singleton_loop(af::kIdNone); | ||
| 654 | + trailing_singleton_loop.SetMaxDtypeSize("int64_t"); | ||
| 655 | + ASSERT_EQ(trailing_singleton_loop.ConstructFromNodes(vf_sub_graph.GetAllNodes(), vf_node), af::SUCCESS); | ||
| 656 | + result.clear(); | ||
| 657 | + loop_size.clear(); | ||
| 658 | + max_depth = -1; | ||
| 659 | + loop_sizes.clear(); | ||
| 660 | + ASSERT_EQ(trailing_singleton_loop.Generate(tpipe, trailing_singleton_tensor_manager, 0, result, loop_size, max_depth, | ||
| 661 | + loop_sizes), | ||
| 662 | + af::SUCCESS); | ||
| 663 | + const auto trailing_singleton_call = GetLineContaining(result, "AscendC::Reg::Arange"); | ||
| 664 | + ASSERT_FALSE(trailing_singleton_call.empty()) << result; | ||
| 665 | + EXPECT_NE(trailing_singleton_call.find("row * ELEMENT_PER_VECTOR_LENGTH"), std::string::npos) | ||
| 666 | + << trailing_singleton_call; | ||
| 667 | + trailing_singleton_loop.Destruct(); | ||
| 668 | + | ||
| 669 | + arange_node->attr.sched.axis.clear(); | ||
| 670 | + arange_node->attr.sched.loop_axis = af::kIdNone; | ||
| 671 | + abs_node->attr.sched.axis.clear(); | ||
| 672 | + abs_node->attr.sched.loop_axis = af::kIdNone; | ||
| 673 | + VFLoop singleton_loop(af::kIdNone); | ||
| 674 | + singleton_loop.SetMaxDtypeSize("int64_t"); | ||
| 675 | + ASSERT_EQ(singleton_loop.ConstructFromNodes(vf_sub_graph.GetAllNodes(), vf_node), af::SUCCESS); | ||
| 676 | + result.clear(); | ||
| 677 | + loop_size.clear(); | ||
| 678 | + max_depth = -1; | ||
| 679 | + loop_sizes.clear(); | ||
| 680 | + ASSERT_EQ(singleton_loop.Generate(tpipe, tensor_manager, 0, result, loop_size, max_depth, loop_sizes, | ||
| 681 | + {{0, "device_block_offset"}}), | ||
| 682 | + af::SUCCESS); | ||
| 683 | + const auto singleton_arange_call = GetLineContaining(result, "AscendC::Reg::Arange"); | ||
| 684 | + ASSERT_FALSE(singleton_arange_call.empty()) << result; | ||
| 685 | + EXPECT_NE(singleton_arange_call.find("device_block_offset + (0)"), std::string::npos); | ||
| 686 | + EXPECT_EQ(singleton_arange_call.find("row *"), std::string::npos); | ||
| 687 | + EXPECT_EQ(singleton_arange_call.find("col *"), std::string::npos); | ||
| 688 | + singleton_loop.Destruct(); | ||
| 689 | +} | ||
| 690 | + | ||
| 691 | +TEST(VFLoopTest, UsesInnermostLogicalStrideForPartiallyMergedArangeAxis) { | ||
| 692 | + ge::SetupRuntimeStub(); | ||
| 693 | + af::AscGraph graph("partially_merged_arange"); | ||
| 694 | + const auto outer = graph.CreateAxis("outer", af::Symbol(2)); | ||
| 695 | + const auto middle = graph.CreateAxis("middle", af::Symbol(3)); | ||
| 696 | + const auto inner = graph.CreateAxis("inner", af::Symbol(3)); | ||
| 697 | + const auto merged = graph.MergeAxis({middle.id, outer.id}); | ||
| 698 | + ASSERT_NE(merged, nullptr); | ||
| 699 | + af::AscGraph vf_sub_graph("partially_merged_arange_subgraph"); | ||
| 700 | + VectorFunc vf_op("vf"); | ||
| 701 | + vf_op.InstanceOutputy(1); | ||
| 702 | + vf_op.SetAttr("sub_graph_name", "partially_merged_arange_subgraph"); | ||
| 703 | + graph.AddSubGraph(vf_sub_graph); | ||
| 704 | + graph.AddNode(vf_op); | ||
| 705 | + Arange arange_op("arange"); | ||
| 706 | + Abs abs_op("abs"); | ||
| 707 | + Output output_op("output"); | ||
| 708 | + arange_op.ir_attr.SetBase(af::Symbol(0)); | ||
| 709 | + arange_op.ir_attr.SetStep(af::Symbol(1)); | ||
| 710 | + arange_op.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 711 | + arange_op.attr.sched.axis = {merged->id, inner.id}; | ||
| 712 | + arange_op.attr.sched.loop_axis = inner.id; | ||
| 713 | + arange_op.y.dtype = af::DT_INT64; | ||
| 714 | + *arange_op.y.axis = {outer.id, middle.id, inner.id}; | ||
| 715 | + *arange_op.y.repeats = {af::Symbol(2), af::Symbol(3), af::Symbol(3)}; | ||
| 716 | + *arange_op.y.strides = {af::Symbol(9), af::Symbol(3), One}; | ||
| 717 | + *arange_op.y.vectorized_axis = {merged->id, inner.id}; | ||
| 718 | + *arange_op.y.vectorized_strides = {af::Symbol(4), One}; | ||
| 719 | + abs_op.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 720 | + abs_op.attr.sched.axis = {merged->id, inner.id}; | ||
| 721 | + abs_op.attr.sched.loop_axis = inner.id; | ||
| 722 | + abs_op.y.dtype = af::DT_INT64; | ||
| 723 | + *abs_op.y.axis = *arange_op.y.axis; | ||
| 724 | + *abs_op.y.repeats = *arange_op.y.repeats; | ||
| 725 | + *abs_op.y.strides = *arange_op.y.strides; | ||
| 726 | + *abs_op.y.vectorized_axis = *arange_op.y.vectorized_axis; | ||
| 727 | + *abs_op.y.vectorized_strides = *arange_op.y.vectorized_strides; | ||
| 728 | + output_op.ir_attr.SetIndex(0); | ||
| 729 | + vf_sub_graph.AddNode(arange_op); | ||
| 730 | + vf_sub_graph.AddNode(abs_op); | ||
| 731 | + vf_sub_graph.AddNode(output_op); | ||
| 732 | + abs_op.x = arange_op.y; | ||
| 733 | + output_op.x = abs_op.y; | ||
| 734 | + const auto arange_node = vf_sub_graph.FindNode("arange"); | ||
| 735 | + arange_node->outputs[0].attr.mem.tensor_id = 0; | ||
| 736 | + const auto abs_node = vf_sub_graph.FindNode("abs"); | ||
| 737 | + abs_node->outputs[0].attr.mem.tensor_id = 2; | ||
| 738 | + const auto vf_node = graph.FindNode("vf"); | ||
| 739 | + vf_node->outputs[0].attr.dtype = af::DT_INT64; | ||
| 740 | + vf_node->outputs[0].attr.axis = abs_node->outputs[0].attr.axis; | ||
| 741 | + vf_node->outputs[0].attr.repeats = abs_node->outputs[0].attr.repeats; | ||
| 742 | + vf_node->outputs[0].attr.strides = abs_node->outputs[0].attr.strides; | ||
| 743 | + vf_node->outputs[0].attr.vectorized_axis = abs_node->outputs[0].attr.vectorized_axis; | ||
| 744 | + vf_node->outputs[0].attr.vectorized_strides = abs_node->outputs[0].attr.vectorized_strides; | ||
| 745 | + vf_node->outputs[0].attr.mem.tensor_id = 1; | ||
| 746 | + vf_node->outputs[0].attr.mem.position = af::Position::kPositionVecOut; | ||
| 747 | + | ||
| 748 | + std::string dtype_name; | ||
| 749 | + ASSERT_EQ(codegen::Tensor::DtypeName(af::DT_INT64, dtype_name), af::SUCCESS); | ||
| 750 | + TensorManager tensor_manager; | ||
| 751 | + ASSERT_EQ(tensor_manager.AddTensor(MicroApiTensor(arange_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 752 | + ASSERT_EQ(tensor_manager.AddTensor(MicroApiTensor(vf_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 753 | + ASSERT_EQ(tensor_manager.AddTensor(MicroApiTensor(abs_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 754 | + Tiler tiler; | ||
| 755 | + for (const auto &axis : graph.GetAllAxis()) { | ||
| 756 | + ASSERT_EQ(tiler.AddAxis(*axis), af::SUCCESS); | ||
| 757 | + } | ||
| 758 | + TPipe tpipe("tpipe", tiler); | ||
| 759 | + VFLoop loop(af::kIdNone); | ||
| 760 | + loop.SetMaxDtypeSize("int64_t"); | ||
| 761 | + ASSERT_EQ(loop.ConstructFromNodes(vf_sub_graph.GetAllNodes(), vf_node), af::SUCCESS); | ||
| 762 | + | ||
| 763 | + std::string result; | ||
| 764 | + std::string loop_size; | ||
| 765 | + int32_t max_depth = -1; | ||
| 766 | + std::vector<std::string> loop_sizes; | ||
| 767 | + EXPECT_NE(loop.Generate(tpipe, tensor_manager, 0, result, loop_size, max_depth, loop_sizes), af::SUCCESS); | ||
| 768 | + loop.Destruct(); | ||
| 769 | +} | ||
| 770 | + | ||
| 771 | +TEST(VFLoopTest, RejectsInterleavedNestedMergedArangeAsOnlyVectorizedAxis) { | ||
| 772 | + ge::SetupRuntimeStub(); | ||
| 773 | + af::AscGraph graph("interleaved_nested_merged_arange"); | ||
| 774 | + const auto axis0 = graph.CreateAxis("axis0", af::Symbol(2)); | ||
| 775 | + const auto axis1 = graph.CreateAxis("axis1", af::Symbol(2)); | ||
| 776 | + const auto axis2 = graph.CreateAxis("axis2", af::Symbol(2)); | ||
| 777 | + const auto axis3 = graph.CreateAxis("axis3", af::Symbol(2)); | ||
| 778 | + const auto left = graph.MergeAxis({axis0.id, axis2.id}); | ||
| 779 | + const auto right = graph.MergeAxis({axis1.id, axis3.id}); | ||
| 780 | + ASSERT_NE(left, nullptr); | ||
| 781 | + ASSERT_NE(right, nullptr); | ||
| 782 | + const auto merged = graph.MergeAxis({left->id, right->id}); | ||
| 783 | + ASSERT_NE(merged, nullptr); | ||
| 784 | + af::AscGraph vf_sub_graph("interleaved_nested_merged_arange_subgraph"); | ||
| 785 | + VectorFunc vf_op("vf"); | ||
| 786 | + vf_op.InstanceOutputy(1); | ||
| 787 | + vf_op.SetAttr("sub_graph_name", "interleaved_nested_merged_arange_subgraph"); | ||
| 788 | + graph.AddSubGraph(vf_sub_graph); | ||
| 789 | + graph.AddNode(vf_op); | ||
| 790 | + Arange arange_op("arange"); | ||
| 791 | + Abs abs_op("abs"); | ||
| 792 | + Output output_op("output"); | ||
| 793 | + arange_op.ir_attr.SetBase(af::Symbol(0)); | ||
| 794 | + arange_op.ir_attr.SetStep(af::Symbol(1)); | ||
| 795 | + arange_op.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 796 | + arange_op.attr.sched.axis = {merged->id}; | ||
| 797 | + arange_op.attr.sched.loop_axis = merged->id; | ||
| 798 | + arange_op.y.dtype = af::DT_INT64; | ||
| 799 | + *arange_op.y.axis = {axis0.id, axis1.id, axis2.id, axis3.id}; | ||
| 800 | + *arange_op.y.repeats = {af::Symbol(2), af::Symbol(2), af::Symbol(2), af::Symbol(2)}; | ||
| 801 | + *arange_op.y.strides = {af::Symbol(8), af::Symbol(4), af::Symbol(2), One}; | ||
| 802 | + *arange_op.y.vectorized_axis = {merged->id}; | ||
| 803 | + *arange_op.y.vectorized_strides = {One}; | ||
| 804 | + abs_op.attr.api.unit = af::ComputeUnit::kUnitVector; | ||
| 805 | + abs_op.attr.sched.axis = {merged->id}; | ||
| 806 | + abs_op.attr.sched.loop_axis = merged->id; | ||
| 807 | + abs_op.y.dtype = af::DT_INT64; | ||
| 808 | + *abs_op.y.axis = *arange_op.y.axis; | ||
| 809 | + *abs_op.y.repeats = *arange_op.y.repeats; | ||
| 810 | + *abs_op.y.strides = *arange_op.y.strides; | ||
| 811 | + *abs_op.y.vectorized_axis = *arange_op.y.vectorized_axis; | ||
| 812 | + *abs_op.y.vectorized_strides = *arange_op.y.vectorized_strides; | ||
| 813 | + output_op.ir_attr.SetIndex(0); | ||
| 814 | + vf_sub_graph.AddNode(arange_op); | ||
| 815 | + vf_sub_graph.AddNode(abs_op); | ||
| 816 | + vf_sub_graph.AddNode(output_op); | ||
| 817 | + abs_op.x = arange_op.y; | ||
| 818 | + output_op.x = abs_op.y; | ||
| 819 | + const auto arange_node = vf_sub_graph.FindNode("arange"); | ||
| 820 | + arange_node->outputs[0].attr.mem.tensor_id = 0; | ||
| 821 | + const auto abs_node = vf_sub_graph.FindNode("abs"); | ||
| 822 | + abs_node->outputs[0].attr.mem.tensor_id = 2; | ||
| 823 | + const auto vf_node = graph.FindNode("vf"); | ||
| 824 | + vf_node->outputs[0].attr.dtype = af::DT_INT64; | ||
| 825 | + vf_node->outputs[0].attr.mem.tensor_id = 1; | ||
| 826 | + vf_node->outputs[0].attr.mem.position = af::Position::kPositionVecOut; | ||
| 827 | + | ||
| 828 | + std::string dtype_name; | ||
| 829 | + ASSERT_EQ(codegen::Tensor::DtypeName(af::DT_INT64, dtype_name), af::SUCCESS); | ||
| 830 | + TensorManager tensor_manager; | ||
| 831 | + ASSERT_EQ(tensor_manager.AddTensor(MicroApiTensor(arange_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 832 | + ASSERT_EQ(tensor_manager.AddTensor(MicroApiTensor(vf_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 833 | + ASSERT_EQ(tensor_manager.AddTensor(MicroApiTensor(abs_node->outputs[0], dtype_name)), af::SUCCESS); | ||
| 834 | + Tiler tiler; | ||
| 835 | + for (const auto &axis : graph.GetAllAxis()) { | ||
| 836 | + ASSERT_EQ(tiler.AddAxis(*axis), af::SUCCESS); | ||
| 837 | + } | ||
| 838 | + TPipe tpipe("tpipe", tiler); | ||
| 839 | + ASSERT_EQ(tpipe.AddTensor(vf_node->outputs[0]), af::SUCCESS); | ||
| 840 | + VFLoop loop(af::kIdNone); | ||
| 841 | + loop.SetMaxDtypeSize("int64_t"); | ||
| 842 | + ASSERT_EQ(loop.ConstructFromNodes(vf_sub_graph.GetAllNodes(), vf_node), af::SUCCESS); | ||
| 843 | + | ||
| 844 | + std::string result; | ||
| 845 | + std::string loop_size; | ||
| 846 | + int32_t max_depth = -1; | ||
| 847 | + std::vector<std::string> loop_sizes; | ||
| 848 | + EXPECT_NE(loop.Generate(tpipe, tensor_manager, 0, result, loop_size, max_depth, loop_sizes), af::SUCCESS); | ||
| 849 | + loop.Destruct(); | ||
| 850 | +} | ||
| 851 | + | ||
| 852 | +TEST(VFLoopTest, KeepsDistinctParamsForMultipleArangeCalls) { | ||
| 853 | + Tiler tiler; | ||
| 854 | + TPipe tpipe("tpipe", tiler); | ||
| 855 | + VFLoop loop(af::kIdNone); | ||
| 856 | + loop.AddCall(new FixedArangeMicroApiCall(10, "1", "2")); | ||
| 857 | + loop.AddCall(new FixedArangeMicroApiCall(11, "7", "4")); | ||
| 858 | + | ||
| 859 | + std::vector<ArangeParam> params; | ||
| 860 | + loop.CollectArangeParams(tpipe, params); | ||
| 861 | + ASSERT_EQ(params.size(), 2U); | ||
| 862 | + EXPECT_EQ(params[0].tensor_id, 10); | ||
| 863 | + EXPECT_EQ(params[0].base, "1"); | ||
| 864 | + EXPECT_EQ(params[0].step, "2"); | ||
| 865 | + EXPECT_EQ(params[1].tensor_id, 11); | ||
| 866 | + EXPECT_EQ(params[1].base, "7"); | ||
| 867 | + EXPECT_EQ(params[1].step, "4"); | ||
| 868 | + loop.Destruct(); | ||
| 869 | +} | ||
| 870 | + | ||
| 871 | +TEST(VFLoopTest, MapsBoundaryOrdinalsToConnectedRootInputs) { | ||
| 872 | + ge::SetupRuntimeStub(); | ||
| 873 | + af::AscGraph graph("vf_input_mapping"); | ||
| 874 | + af::AscGraph subgraph("vf_subgraph"); | ||
| 875 | + const auto axis = graph.CreateAxis("axis", af::Symbol(8)); | ||
| 876 | + af::ascir_op::Data root_data("root_data", graph); | ||
| 877 | + root_data.ir_attr.SetIndex(0); | ||
| 878 | + root_data.y.dtype = af::DT_FLOAT; | ||
| 879 | + af::ascir_op::VectorFunc vf("vf"); | ||
| 880 | + vf.InstanceOutputy(1); | ||
| 881 | + // 与 VfCall_TwoDimLoad 相同的隐式入图模式: vf 由输入连接隐式创建并绑定 operator, | ||
| 882 | + // 显式 AddNode 会使 operator 与节点绑定分裂(inputs 为空)。 | ||
| 883 | + vf.x = {root_data.y}; | ||
| 884 | + auto root = graph.FindNode("vf"); | ||
| 885 | + ASSERT_NE(root, nullptr); | ||
| 886 | + | ||
| 887 | + af::ascir_op::Data data("data", subgraph); | ||
| 888 | + data.ir_attr.SetIndex(1); | ||
| 889 | + data.y.dtype = af::DT_FLOAT; | ||
| 890 | + af::ascir_op::Load load("load"); | ||
| 891 | + load.x = data.y; | ||
| 892 | + load.attr.api.unit = af::ComputeUnit::kUnitMTE2; | ||
| 893 | + load.attr.sched.axis = {axis.id}; | ||
| 894 | + load.y.dtype = af::DT_FLOAT; | ||
| 895 | + *load.y.axis = {axis.id}; | ||
| 896 | + *load.y.repeats = {af::Symbol(8)}; | ||
| 897 | + *load.y.strides = {One}; | ||
| 898 | + *load.y.vectorized_axis = {axis.id}; | ||
| 899 | + *load.y.vectorized_strides = {One}; | ||
| 900 | + af::ascir_op::Output output("output"); | ||
| 901 | + subgraph.AddNode(output); | ||
| 902 | + output.ir_attr.SetIndex(0); | ||
| 903 | + output.x = load.y; | ||
| 904 | + ASSERT_NE(subgraph.FindNode("data"), nullptr); | ||
| 905 | + ASSERT_NE(subgraph.FindNode("load"), nullptr); | ||
| 906 | + ASSERT_NE(subgraph.FindNode("output"), nullptr); | ||
| 907 | + ASSERT_TRUE(af::AttrUtils::SetInt(subgraph.FindNode("data")->GetOpDesc(), "vf_root_input_index", 0)); | ||
| 908 | + root->outputs[0].attr.mem.tensor_id = 10; | ||
| 909 | + | ||
| 910 | + VFLoop loop(af::kIdNone); | ||
| 911 | + loop.SetMaxDtypeSize("float"); | ||
| 912 | + const VFInputMapping mapping = {{1, 0}}; | ||
| 913 | + EXPECT_EQ(loop.ConstructFromNodes(subgraph.GetAllNodes(), root, mapping), af::SUCCESS); | ||
| 914 | + loop.Destruct(); | ||
| 915 | +} | ||
| 916 | + | ||
| 282 | TEST(CodegenKernel, VfCall_TwoDimLoad) { | 917 | TEST(CodegenKernel, VfCall_TwoDimLoad) { |
| 283 | ge::SetupRuntimeStub(); | 918 | ge::SetupRuntimeStub(); |
| 284 | af::AscGraph graph("test_graph"); | 919 | af::AscGraph graph("test_graph"); |
| @@ -303,12 +938,14 @@ TEST(CodegenKernel, VfCall_TwoDimLoad) { | |||
| 303 | 938 | ||
| 304 | Data sub_x_op("sub_x", vf_sub_graph); | 939 | Data sub_x_op("sub_x", vf_sub_graph); |
| 305 | sub_x_op.ir_attr.SetIndex(0); | 940 | sub_x_op.ir_attr.SetIndex(0); |
| 941 | + sub_x_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 306 | 942 | ||
| 307 | Load sub_load_op("sub_load"); | 943 | Load sub_load_op("sub_load"); |
| 308 | Abs abs_op("abs"); | 944 | Abs abs_op("abs"); |
| 309 | Store sub_store_op("sub_store"); | 945 | Store sub_store_op("sub_store"); |
| 310 | Output sub_output_op("sub_output"); | 946 | Output sub_output_op("sub_output"); |
| 311 | sub_output_op.ir_attr.SetIndex(0); | 947 | sub_output_op.ir_attr.SetIndex(0); |
| 948 | + sub_output_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 312 | 949 | ||
| 313 | Store store_op("store"); | 950 | Store store_op("store"); |
| 314 | graph.AddNode(load_op); | 951 | graph.AddNode(load_op); |
| @@ -421,8 +1058,9 @@ TEST(CodegenKernel, VfCall_TwoDimLoad) { | |||
| 421 | 1058 | ||
| 422 | codegen::Tiler tiler; | 1059 | codegen::Tiler tiler; |
| 423 | codegen::TPipe tpipe("tpipe", tiler); | 1060 | codegen::TPipe tpipe("tpipe", tiler); |
| 424 | - tpipe.AddTensor(load->outputs[0]); | 1061 | + ASSERT_EQ(tpipe.CollectQues(graph), af::SUCCESS); |
| 425 | - tpipe.AddTensor(vf->outputs[0]); | 1062 | + ASSERT_EQ(tpipe.AddTensor(load->outputs[0]), af::SUCCESS); |
| 1063 | + ASSERT_EQ(tpipe.AddTensor(vf->outputs[0]), af::SUCCESS); | ||
| 426 | 1064 | ||
| 427 | tiler.AddAxis(z0); | 1065 | tiler.AddAxis(z0); |
| 428 | tiler.AddAxis(z1); | 1066 | tiler.AddAxis(z1); |
| @@ -534,12 +1172,14 @@ TEST(CodegenKernel, VfCall_TwoDimLoad_VFLoop) { | |||
| 534 | 1172 | ||
| 535 | Data sub_x_op("sub_x", vf_sub_graph); | 1173 | Data sub_x_op("sub_x", vf_sub_graph); |
| 536 | sub_x_op.ir_attr.SetIndex(0); | 1174 | sub_x_op.ir_attr.SetIndex(0); |
| 1175 | + sub_x_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 537 | 1176 | ||
| 538 | Load sub_load_op("sub_load"); | 1177 | Load sub_load_op("sub_load"); |
| 539 | Abs abs_op("abs"); | 1178 | Abs abs_op("abs"); |
| 540 | Store sub_store_op("sub_store"); | 1179 | Store sub_store_op("sub_store"); |
| 541 | Output sub_output_op("sub_output"); | 1180 | Output sub_output_op("sub_output"); |
| 542 | sub_output_op.ir_attr.SetIndex(0); | 1181 | sub_output_op.ir_attr.SetIndex(0); |
| 1182 | + sub_output_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 543 | 1183 | ||
| 544 | Store store_op("store"); | 1184 | Store store_op("store"); |
| 545 | graph.AddNode(load_op); | 1185 | graph.AddNode(load_op); |
| @@ -647,8 +1287,9 @@ TEST(CodegenKernel, VfCall_TwoDimLoad_VFLoop) { | |||
| 647 | 1287 | ||
| 648 | codegen::Tiler tiler; | 1288 | codegen::Tiler tiler; |
| 649 | codegen::TPipe tpipe("tpipe", tiler); | 1289 | codegen::TPipe tpipe("tpipe", tiler); |
| 650 | - tpipe.AddTensor(load->outputs[0]); | 1290 | + ASSERT_EQ(tpipe.CollectQues(graph), af::SUCCESS); |
| 651 | - tpipe.AddTensor(vf->outputs[0]); | 1291 | + ASSERT_EQ(tpipe.AddTensor(load->outputs[0]), af::SUCCESS); |
| 1292 | + ASSERT_EQ(tpipe.AddTensor(vf->outputs[0]), af::SUCCESS); | ||
| 652 | 1293 | ||
| 653 | tiler.AddAxis(z0); | 1294 | tiler.AddAxis(z0); |
| 654 | tiler.AddAxis(z1); | 1295 | tiler.AddAxis(z1); |
| @@ -698,12 +1339,14 @@ TEST(CodegenKernel, VfCall_TwoDim_Scalar) { | |||
| 698 | Scalar sub_x_op("sub_x", vf_sub_graph); | 1339 | Scalar sub_x_op("sub_x", vf_sub_graph); |
| 699 | sub_x_op.ir_attr.SetValue("2.0"); | 1340 | sub_x_op.ir_attr.SetValue("2.0"); |
| 700 | sub_x_op.ir_attr.SetIndex(0); | 1341 | sub_x_op.ir_attr.SetIndex(0); |
| 1342 | + sub_x_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 701 | 1343 | ||
| 702 | Broadcast sub_brc_op("sub_brc"); | 1344 | Broadcast sub_brc_op("sub_brc"); |
| 703 | Abs abs_op("abs"); | 1345 | Abs abs_op("abs"); |
| 704 | Store sub_store_op("sub_store"); | 1346 | Store sub_store_op("sub_store"); |
| 705 | Output sub_output_op("sub_output"); | 1347 | Output sub_output_op("sub_output"); |
| 706 | sub_output_op.ir_attr.SetIndex(0); | 1348 | sub_output_op.ir_attr.SetIndex(0); |
| 1349 | + sub_output_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 707 | 1350 | ||
| 708 | Store store_op("store"); | 1351 | Store store_op("store"); |
| 709 | graph.AddNode(x_op); | 1352 | graph.AddNode(x_op); |
| @@ -804,8 +1447,9 @@ TEST(CodegenKernel, VfCall_TwoDim_Scalar) { | |||
| 804 | 1447 | ||
| 805 | codegen::Tiler tiler; | 1448 | codegen::Tiler tiler; |
| 806 | codegen::TPipe tpipe("tpipe", tiler); | 1449 | codegen::TPipe tpipe("tpipe", tiler); |
| 807 | - tpipe.AddTensor("2.0", x->outputs[0], "scalar_x"); | 1450 | + ASSERT_EQ(tpipe.CollectQues(graph), af::SUCCESS); |
| 808 | - tpipe.AddTensor(vf->outputs[0]); | 1451 | + ASSERT_EQ(tpipe.AddTensor("2.0", x->outputs[0], "scalar_x"), af::SUCCESS); |
| 1452 | + ASSERT_EQ(tpipe.AddTensor(vf->outputs[0]), af::SUCCESS); | ||
| 809 | 1453 | ||
| 810 | tiler.AddAxis(z0); | 1454 | tiler.AddAxis(z0); |
| 811 | tiler.AddAxis(z1); | 1455 | tiler.AddAxis(z1); |
| @@ -904,12 +1548,14 @@ TEST(CodegenKernel, VfCall_ThreeDimLoad) { | |||
| 904 | 1548 | ||
| 905 | Data sub_x_op("sub_x", vf_sub_graph); | 1549 | Data sub_x_op("sub_x", vf_sub_graph); |
| 906 | sub_x_op.ir_attr.SetIndex(0); | 1550 | sub_x_op.ir_attr.SetIndex(0); |
| 1551 | + sub_x_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 907 | 1552 | ||
| 908 | Load sub_load_op("sub_load"); | 1553 | Load sub_load_op("sub_load"); |
| 909 | Abs abs_op("abs"); | 1554 | Abs abs_op("abs"); |
| 910 | Store sub_store_op("sub_store"); | 1555 | Store sub_store_op("sub_store"); |
| 911 | Output sub_output_op("sub_output"); | 1556 | Output sub_output_op("sub_output"); |
| 912 | sub_output_op.ir_attr.SetIndex(0); | 1557 | sub_output_op.ir_attr.SetIndex(0); |
| 1558 | + sub_output_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 913 | 1559 | ||
| 914 | Store store_op("store"); | 1560 | Store store_op("store"); |
| 915 | graph.AddNode(load_op); | 1561 | graph.AddNode(load_op); |
| @@ -1019,8 +1665,9 @@ TEST(CodegenKernel, VfCall_ThreeDimLoad) { | |||
| 1019 | 1665 | ||
| 1020 | codegen::Tiler tiler; | 1666 | codegen::Tiler tiler; |
| 1021 | codegen::TPipe tpipe("tpipe", tiler); | 1667 | codegen::TPipe tpipe("tpipe", tiler); |
| 1022 | - tpipe.AddTensor(load->outputs[0]); | 1668 | + ASSERT_EQ(tpipe.CollectQues(graph), af::SUCCESS); |
| 1023 | - tpipe.AddTensor(vf->outputs[0]); | 1669 | + ASSERT_EQ(tpipe.AddTensor(load->outputs[0]), af::SUCCESS); |
| 1670 | + ASSERT_EQ(tpipe.AddTensor(vf->outputs[0]), af::SUCCESS); | ||
| 1024 | 1671 | ||
| 1025 | tiler.AddAxis(z0); | 1672 | tiler.AddAxis(z0); |
| 1026 | tiler.AddAxis(z1); | 1673 | tiler.AddAxis(z1); |
| @@ -1086,12 +1733,14 @@ TEST(CodegenKernel, VfCall_FiveDimLoad) { | |||
| 1086 | 1733 | ||
| 1087 | Data sub_x_op("sub_x", vf_sub_graph); | 1734 | Data sub_x_op("sub_x", vf_sub_graph); |
| 1088 | sub_x_op.ir_attr.SetIndex(0); | 1735 | sub_x_op.ir_attr.SetIndex(0); |
| 1736 | + sub_x_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 1089 | 1737 | ||
| 1090 | Load sub_load_op("sub_load"); | 1738 | Load sub_load_op("sub_load"); |
| 1091 | Abs abs_op("abs"); | 1739 | Abs abs_op("abs"); |
| 1092 | Store sub_store_op("sub_store"); | 1740 | Store sub_store_op("sub_store"); |
| 1093 | Output sub_output_op("sub_output"); | 1741 | Output sub_output_op("sub_output"); |
| 1094 | sub_output_op.ir_attr.SetIndex(0); | 1742 | sub_output_op.ir_attr.SetIndex(0); |
| 1743 | + sub_output_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 1095 | 1744 | ||
| 1096 | Store store_op("store"); | 1745 | Store store_op("store"); |
| 1097 | graph.AddNode(load_op); | 1746 | graph.AddNode(load_op); |
| @@ -1196,8 +1845,9 @@ TEST(CodegenKernel, VfCall_FiveDimLoad) { | |||
| 1196 | 1845 | ||
| 1197 | codegen::Tiler tiler; | 1846 | codegen::Tiler tiler; |
| 1198 | codegen::TPipe tpipe("tpipe", tiler); | 1847 | codegen::TPipe tpipe("tpipe", tiler); |
| 1199 | - tpipe.AddTensor(load->outputs[0]); | 1848 | + ASSERT_EQ(tpipe.CollectQues(graph), af::SUCCESS); |
| 1200 | - tpipe.AddTensor(vf->outputs[0]); | 1849 | + ASSERT_EQ(tpipe.AddTensor(load->outputs[0]), af::SUCCESS); |
| 1850 | + ASSERT_EQ(tpipe.AddTensor(vf->outputs[0]), af::SUCCESS); | ||
| 1201 | 1851 | ||
| 1202 | tiler.AddAxis(z0); | 1852 | tiler.AddAxis(z0); |
| 1203 | tiler.AddAxis(z1); | 1853 | tiler.AddAxis(z1); |
| @@ -1313,12 +1963,14 @@ TEST(CodegenKernel, VfCall_OneDim_NoOptimization) { | |||
| 1313 | 1963 | ||
| 1314 | Data sub_x_op("sub_x", vf_sub_graph); | 1964 | Data sub_x_op("sub_x", vf_sub_graph); |
| 1315 | sub_x_op.ir_attr.SetIndex(0); | 1965 | sub_x_op.ir_attr.SetIndex(0); |
| 1966 | + sub_x_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 1316 | 1967 | ||
| 1317 | Load sub_load_op("sub_load"); | 1968 | Load sub_load_op("sub_load"); |
| 1318 | Abs abs_op("abs"); | 1969 | Abs abs_op("abs"); |
| 1319 | Store sub_store_op("sub_store"); | 1970 | Store sub_store_op("sub_store"); |
| 1320 | Output sub_output_op("sub_output"); | 1971 | Output sub_output_op("sub_output"); |
| 1321 | sub_output_op.ir_attr.SetIndex(0); | 1972 | sub_output_op.ir_attr.SetIndex(0); |
| 1973 | + sub_output_op.attr.api.unit = af::ComputeUnit::kUnitNone; | ||
| 1322 | 1974 | ||
| 1323 | Store store_op("store"); | 1975 | Store store_op("store"); |
| 1324 | graph.AddNode(load_op); | 1976 | graph.AddNode(load_op); |
| @@ -1429,8 +2081,9 @@ TEST(CodegenKernel, VfCall_OneDim_NoOptimization) { | |||
| 1429 | 2081 | ||
| 1430 | codegen::Tiler tiler; | 2082 | codegen::Tiler tiler; |
| 1431 | codegen::TPipe tpipe("tpipe", tiler); | 2083 | codegen::TPipe tpipe("tpipe", tiler); |
| 1432 | - tpipe.AddTensor(load->outputs[0]); | 2084 | + ASSERT_EQ(tpipe.CollectQues(graph), af::SUCCESS); |
| 1433 | - tpipe.AddTensor(vf->outputs[0]); | 2085 | + ASSERT_EQ(tpipe.AddTensor(load->outputs[0]), af::SUCCESS); |
| 2086 | + ASSERT_EQ(tpipe.AddTensor(vf->outputs[0]), af::SUCCESS); | ||
| 1434 | 2087 | ||
| 1435 | tiler.AddAxis(z0); | 2088 | tiler.AddAxis(z0); |
| 1436 | tiler.AddSizeVar(af::SizeVar(s0)); | 2089 | tiler.AddSizeVar(af::SizeVar(s0)); |
| @@ -35,7 +35,7 @@ namespace optimize { | |||
| 35 | using namespace ge; | 35 | using namespace ge; |
| 36 | 36 | ||
| 37 | namespace { | 37 | namespace { |
| 38 | -void SetupGraphAxes(af::AscGraph &graph, const std::vector<af::Symbol> &loops) { | 38 | +void SetupGraphAxes(af::AscGraph &graph, const std::vector<af::Expression> &loops) { |
| 39 | for (size_t i = 0UL; i < loops.size(); ++i) { | 39 | for (size_t i = 0UL; i < loops.size(); ++i) { |
| 40 | graph.CreateAxis("z" + std::to_string(i), loops[i]); | 40 | graph.CreateAxis("z" + std::to_string(i), loops[i]); |
| 41 | } | 41 | } |
| @@ -65,8 +65,9 @@ void SetupGraphAxes(af::AscGraph &graph, const std::vector<af::Symbol> &loops) { | |||
| 65 | for (const auto &node : graph.GetAllNodes()) { | 65 | for (const auto &node : graph.GetAllNodes()) { |
| 66 | node->attr.sched.axis = axis_ids; | 66 | node->attr.sched.axis = axis_ids; |
| 67 | if (ScheduleUtils::IsBuffer(node)) continue; | 67 | if (ScheduleUtils::IsBuffer(node)) continue; |
| 68 | - bool is_follow_input = (node->attr.api.compute_type == af::ComputeType::kComputeElewise || | 68 | + bool is_follow_input = |
| 69 | - node->attr.api.compute_type == af::ComputeType::kComputeStore); | 69 | + !node->inputs().empty() && (node->attr.api.compute_type == af::ComputeType::kComputeElewise || |
| 70 | + node->attr.api.compute_type == af::ComputeType::kComputeStore); | ||
| 70 | for (auto &output : node->outputs()) { | 71 | for (auto &output : node->outputs()) { |
| 71 | output->attr.axis = axis_ids; | 72 | output->attr.axis = axis_ids; |
| 72 | output->attr.vectorized_axis = axis_ids; | 73 | output->attr.vectorized_axis = axis_ids; |
| @@ -111,6 +112,75 @@ af::AscGraph BuildParallelVfGraph(bool reverse_branch_order) { | |||
| 111 | return graph; | 112 | return graph; |
| 112 | } | 113 | } |
| 113 | 114 | ||
| 115 | +af::AscGraph BuildIndexExprArangeCompositionGraph() { | ||
| 116 | + af::AscGraph graph("index_expr_arange_composition_python"); | ||
| 117 | + const auto row = af::Symbol("row"); | ||
| 118 | + const auto col = af::Symbol("col"); | ||
| 119 | + const auto row_axis = graph.CreateAxis("row", row); | ||
| 120 | + const auto col_axis = graph.CreateAxis("col", col); | ||
| 121 | + const std::vector<af::AxisId> axes = {row_axis.id, col_axis.id}; | ||
| 122 | + const std::vector<af::Expression> shape = {row, col}; | ||
| 123 | + const std::vector<af::Expression> strides = {col, One}; | ||
| 124 | + | ||
| 125 | + af::ascir_op::Data data("data_0", graph); | ||
| 126 | + data.ir_attr.SetIndex(0); | ||
| 127 | + data.y.dtype = af::DT_INT32; | ||
| 128 | + auto data_node = graph.FindNode("data_0"); | ||
| 129 | + af::ascir_op::Load load("load_0"); | ||
| 130 | + load.y.dtype = af::DT_INT32; | ||
| 131 | + auto load_node = graph.AddNode(load); | ||
| 132 | + af::ascir_op::Arange arange("arange_0", graph); | ||
| 133 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 134 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 135 | + arange.y.dtype = af::DT_INT32; | ||
| 136 | + auto arange_node = graph.FindNode("arange_0"); | ||
| 137 | + af::ascir_op::IndexExpr index_expr("indexexpr_0", graph); | ||
| 138 | + index_expr.ir_attr.SetExpr(row + col); | ||
| 139 | + index_expr.y.dtype = af::DT_INT32; | ||
| 140 | + auto index_expr_node = graph.FindNode("indexexpr_0"); | ||
| 141 | + af::ascir_op::Broadcast broadcast("broadcast_0"); | ||
| 142 | + broadcast.y.dtype = af::DT_INT32; | ||
| 143 | + auto broadcast_node = graph.AddNode(broadcast); | ||
| 144 | + af::ascir_op::Add offset("add_0"); | ||
| 145 | + offset.y.dtype = af::DT_INT32; | ||
| 146 | + auto offset_node = graph.AddNode(offset); | ||
| 147 | + af::ascir_op::Add add("add_1"); | ||
| 148 | + add.y.dtype = af::DT_INT32; | ||
| 149 | + auto add_node = graph.AddNode(add); | ||
| 150 | + af::ascir_op::Store store("store_0"); | ||
| 151 | + store.y.dtype = af::DT_INT32; | ||
| 152 | + auto store_node = graph.AddNode(store); | ||
| 153 | + af::ascir_op::Output output("output_0"); | ||
| 154 | + output.ir_attr.SetIndex(0); | ||
| 155 | + output.y.dtype = af::DT_INT32; | ||
| 156 | + auto output_node = graph.AddNode(output); | ||
| 157 | + if (data_node == nullptr || load_node == nullptr || arange_node == nullptr || index_expr_node == nullptr || | ||
| 158 | + broadcast_node == nullptr || offset_node == nullptr || add_node == nullptr || store_node == nullptr || | ||
| 159 | + output_node == nullptr) { | ||
| 160 | + return af::AscGraph("invalid_index_expr_arange_composition"); | ||
| 161 | + } | ||
| 162 | + load.x = data.y; | ||
| 163 | + broadcast.x = index_expr.y; | ||
| 164 | + offset.x1 = arange.y; | ||
| 165 | + offset.x2 = broadcast.y; | ||
| 166 | + add.x1 = load.y; | ||
| 167 | + add.x2 = offset.y; | ||
| 168 | + store.x = add.y; | ||
| 169 | + output.x = store.y; | ||
| 170 | + SetupGraphAxes(graph, {row, col}); | ||
| 171 | + index_expr_node->outputs[0].attr.axis.clear(); | ||
| 172 | + index_expr_node->outputs[0].attr.repeats.clear(); | ||
| 173 | + index_expr_node->outputs[0].attr.strides.clear(); | ||
| 174 | + index_expr_node->outputs[0].attr.vectorized_axis.clear(); | ||
| 175 | + index_expr_node->outputs[0].attr.vectorized_strides.clear(); | ||
| 176 | + broadcast_node->outputs[0].attr.axis = axes; | ||
| 177 | + broadcast_node->outputs[0].attr.repeats = shape; | ||
| 178 | + broadcast_node->outputs[0].attr.vectorized_axis = axes; | ||
| 179 | + broadcast_node->outputs[0].attr.strides = {Zero, Zero}; | ||
| 180 | + broadcast_node->outputs[0].attr.vectorized_strides = {Zero, Zero}; | ||
| 181 | + return graph; | ||
| 182 | +} | ||
| 183 | + | ||
| 114 | std::vector<std::string> GetVfOutputConsumers(af::AscGraph &graph) { | 184 | std::vector<std::string> GetVfOutputConsumers(af::AscGraph &graph) { |
| 115 | EXPECT_EQ(AlignmentHandler::AlignVectorizedStrides(graph), af::SUCCESS); | 185 | EXPECT_EQ(AlignmentHandler::AlignVectorizedStrides(graph), af::SUCCESS); |
| 116 | VectorFuncPartitioner partitioner(graph); | 186 | VectorFuncPartitioner partitioner(graph); |
| @@ -162,6 +232,7 @@ std::vector<std::string> GetGraphNodeNames(const af::AscGraph &graph) { | |||
| 162 | } | 232 | } |
| 163 | return node_names; | 233 | return node_names; |
| 164 | } | 234 | } |
| 235 | + | ||
| 165 | } // namespace | 236 | } // namespace |
| 166 | 237 | ||
| 167 | class VfPartition : public testing::Test { | 238 | class VfPartition : public testing::Test { |
| @@ -340,6 +411,419 @@ TEST_F(VfPartition, brc_only_revert) { | |||
| 340 | EXPECT_TRUE(sub_graphs.empty()); | 411 | EXPECT_TRUE(sub_graphs.empty()); |
| 341 | } | 412 | } |
| 342 | 413 | ||
| 414 | +TEST_F(VfPartition, ArangeStoreControlEdgeRejectsSingletonPartition) { | ||
| 415 | + af::AscGraph graph("arange_store_control"); | ||
| 416 | + const auto size = af::Symbol(32); | ||
| 417 | + const auto axis = graph.CreateAxis("z0", size); | ||
| 418 | + af::ascir_op::Arange arange("arange"); | ||
| 419 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 420 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 421 | + arange.y.dtype = af::DT_INT32; | ||
| 422 | + af::ascir_op::Store store("store"); | ||
| 423 | + store.y.dtype = af::DT_INT32; | ||
| 424 | + af::ascir_op::Output output("output"); | ||
| 425 | + ASSERT_NE(graph.AddNode(arange), nullptr); | ||
| 426 | + ASSERT_NE(graph.AddNode(store), nullptr); | ||
| 427 | + ASSERT_NE(graph.AddNode(output), nullptr); | ||
| 428 | + store.x = arange.y; | ||
| 429 | + output.x = store.y; | ||
| 430 | + output.ir_attr.SetIndex(0); | ||
| 431 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 432 | + node->attr.sched.axis = {axis.id}; | ||
| 433 | + for (auto &node_output : node->outputs()) { | ||
| 434 | + node_output->attr.axis = {axis.id}; | ||
| 435 | + node_output->attr.repeats = {size}; | ||
| 436 | + node_output->attr.strides = {One}; | ||
| 437 | + node_output->attr.vectorized_axis = {axis.id}; | ||
| 438 | + node_output->attr.vectorized_strides = {One}; | ||
| 439 | + } | ||
| 440 | + } | ||
| 441 | + optimize::AscGraphInfoComplete::CompleteApiInfo(graph); | ||
| 442 | + ASSERT_EQ(af::GraphUtils::AddEdge(graph.FindNode("arange")->GetOutControlAnchor(), | ||
| 443 | + graph.FindNode("store")->GetInControlAnchor()), | ||
| 444 | + af::GRAPH_SUCCESS); | ||
| 445 | + | ||
| 446 | + auto cluster = std::make_shared<Cluster>(graph.FindNode("arange"), 0UL); | ||
| 447 | + cluster->meta_data_.enable_vf = true; | ||
| 448 | + cluster->out_nodes_.insert(graph.FindNode("arange")); | ||
| 449 | + VectorFuncPartitioner partitioner(graph); | ||
| 450 | + partitioner.cluster_dict_.AddCluster(cluster); | ||
| 451 | + ASSERT_NE(partitioner.BuildSubgraphs(), af::SUCCESS); | ||
| 452 | + std::vector<af::AscGraph> subgraphs; | ||
| 453 | + ASSERT_EQ(graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 454 | + EXPECT_TRUE(subgraphs.empty()); | ||
| 455 | + EXPECT_EQ(graph.FindNode("arange")->GetOutControlNodesSize(), 1UL); | ||
| 456 | + EXPECT_EQ(graph.FindNode("store")->GetInControlNodesSize(), 1UL); | ||
| 457 | +} | ||
| 458 | + | ||
| 459 | +TEST_F(VfPartition, ArangeWithMismatchedVectorizedLayoutIsNotPartitioned) { | ||
| 460 | + af::AscGraph graph("arange_invalid_vectorized_layout"); | ||
| 461 | + const auto size = af::Symbol(32); | ||
| 462 | + const auto axis = graph.CreateAxis("z0", size); | ||
| 463 | + af::ascir_op::Arange arange("arange", graph); | ||
| 464 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 465 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 466 | + arange.y.dtype = af::DT_INT32; | ||
| 467 | + af::ascir_op::Store store("store"); | ||
| 468 | + store.x = arange.y; | ||
| 469 | + store.y.dtype = af::DT_INT32; | ||
| 470 | + af::ascir_op::Output output("output"); | ||
| 471 | + output.x = store.y; | ||
| 472 | + output.ir_attr.SetIndex(0); | ||
| 473 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 474 | + node->attr.sched.axis = {axis.id}; | ||
| 475 | + for (auto &node_output : node->outputs()) { | ||
| 476 | + node_output->attr.axis = {axis.id}; | ||
| 477 | + node_output->attr.repeats = {size}; | ||
| 478 | + node_output->attr.strides = {One}; | ||
| 479 | + node_output->attr.vectorized_axis = {axis.id}; | ||
| 480 | + node_output->attr.vectorized_strides = {One}; | ||
| 481 | + } | ||
| 482 | + } | ||
| 483 | + graph.FindNode("arange")->outputs[0].attr.vectorized_strides.clear(); | ||
| 484 | + optimize::AscGraphInfoComplete::CompleteApiInfo(graph); | ||
| 485 | + | ||
| 486 | + VectorFuncPartitioner partitioner(graph); | ||
| 487 | + ASSERT_EQ(partitioner.Partition(), af::SUCCESS); | ||
| 488 | + | ||
| 489 | + std::vector<af::AscGraph> subgraphs; | ||
| 490 | + ASSERT_EQ(graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 491 | + EXPECT_TRUE(subgraphs.empty()); | ||
| 492 | + EXPECT_NE(graph.FindNode("arange"), nullptr); | ||
| 493 | +} | ||
| 494 | + | ||
| 495 | +TEST_F(VfPartition, ArangeVectorFunctionRequiresUnitStride) { | ||
| 496 | + af::AscGraph graph("arange_vf_capability"); | ||
| 497 | + const auto size = af::Symbol(32); | ||
| 498 | + const auto axis = graph.CreateAxis("z0", size); | ||
| 499 | + af::ascir_op::Arange arange("arange", graph); | ||
| 500 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 501 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 502 | + arange.y.dtype = af::DT_INT32; | ||
| 503 | + *arange.y.vectorized_axis = {axis.id}; | ||
| 504 | + *arange.y.vectorized_strides = {One}; | ||
| 505 | + auto arange_node = graph.FindNode("arange"); | ||
| 506 | + ASSERT_NE(arange_node, nullptr); | ||
| 507 | + auto codegen_impl = ascgen_utils::GetAscIrCodegenImpl(arange_node->GetType()); | ||
| 508 | + ASSERT_NE(codegen_impl, nullptr); | ||
| 509 | + EXPECT_TRUE(codegen_impl->IsVectorFunctionSupported(*arange_node)); | ||
| 510 | + | ||
| 511 | + arange_node->outputs[0].attr.vectorized_strides = {Zero}; | ||
| 512 | + EXPECT_FALSE(codegen_impl->IsVectorFunctionSupported(*arange_node)); | ||
| 513 | + arange_node->outputs[0].attr.vectorized_strides = {af::Symbol(2)}; | ||
| 514 | + EXPECT_FALSE(codegen_impl->IsVectorFunctionSupported(*arange_node)); | ||
| 515 | + arange_node->outputs[0].attr.vectorized_strides.clear(); | ||
| 516 | + EXPECT_FALSE(codegen_impl->IsVectorFunctionSupported(*arange_node)); | ||
| 517 | +} | ||
| 518 | + | ||
| 519 | +TEST_F(VfPartition, ArangeVectorFunctionSupportsContiguousMultiAxisLayout) { | ||
| 520 | + af::AscGraph graph("arange_contiguous_multi_axis"); | ||
| 521 | + const auto rows = af::Symbol(4); | ||
| 522 | + const auto cols = af::Symbol(8); | ||
| 523 | + const auto row_axis = graph.CreateAxis("row", rows); | ||
| 524 | + const auto col_axis = graph.CreateAxis("col", cols); | ||
| 525 | + af::ascir_op::Arange arange("arange", graph); | ||
| 526 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 527 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 528 | + arange.y.dtype = af::DT_INT64; | ||
| 529 | + *arange.y.axis = {row_axis.id, col_axis.id}; | ||
| 530 | + *arange.y.repeats = {rows, cols}; | ||
| 531 | + *arange.y.strides = {cols, One}; | ||
| 532 | + *arange.y.vectorized_axis = {row_axis.id, col_axis.id}; | ||
| 533 | + *arange.y.vectorized_strides = {cols, One}; | ||
| 534 | + | ||
| 535 | + auto arange_node = graph.FindNode("arange"); | ||
| 536 | + ASSERT_NE(arange_node, nullptr); | ||
| 537 | + auto codegen_impl = ascgen_utils::GetAscIrCodegenImpl(arange_node->GetType()); | ||
| 538 | + ASSERT_NE(codegen_impl, nullptr); | ||
| 539 | + EXPECT_TRUE(codegen_impl->IsVectorFunctionSupported(*arange_node)); | ||
| 540 | +} | ||
| 541 | + | ||
| 542 | +TEST_F(VfPartition, ArangeVectorFunctionSupportsAlignedOuterVectorizedStride) { | ||
| 543 | + af::AscGraph graph("arange_aligned_outer_stride"); | ||
| 544 | + const auto rows = af::Symbol(4); | ||
| 545 | + const auto cols = af::Symbol(8); | ||
| 546 | + const auto row_axis = graph.CreateAxis("row", rows); | ||
| 547 | + const auto col_axis = graph.CreateAxis("col", cols); | ||
| 548 | + af::ascir_op::Arange arange("arange", graph); | ||
| 549 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 550 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 551 | + arange.y.dtype = af::DT_INT64; | ||
| 552 | + *arange.y.axis = {row_axis.id, col_axis.id}; | ||
| 553 | + *arange.y.repeats = {rows, cols}; | ||
| 554 | + *arange.y.strides = {cols, One}; | ||
| 555 | + *arange.y.vectorized_axis = {row_axis.id, col_axis.id}; | ||
| 556 | + *arange.y.vectorized_strides = {af::Symbol(16), One}; | ||
| 557 | + | ||
| 558 | + auto arange_node = graph.FindNode("arange"); | ||
| 559 | + ASSERT_NE(arange_node, nullptr); | ||
| 560 | + auto codegen_impl = ascgen_utils::GetAscIrCodegenImpl(arange_node->GetType()); | ||
| 561 | + ASSERT_NE(codegen_impl, nullptr); | ||
| 562 | + EXPECT_TRUE(codegen_impl->IsVectorFunctionSupported(*arange_node)); | ||
| 563 | +} | ||
| 564 | + | ||
| 565 | +TEST_F(VfPartition, ArangeVectorFunctionUsesLastNonZeroStride) { | ||
| 566 | + af::AscGraph graph("arange_trailing_singleton"); | ||
| 567 | + const auto rows = af::Symbol(8); | ||
| 568 | + const auto row_axis = graph.CreateAxis("row", rows); | ||
| 569 | + const auto singleton_axis = graph.CreateAxis("singleton", One); | ||
| 570 | + af::ascir_op::Arange arange("arange", graph); | ||
| 571 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 572 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 573 | + arange.y.dtype = af::DT_INT64; | ||
| 574 | + *arange.y.axis = {row_axis.id, singleton_axis.id}; | ||
| 575 | + *arange.y.repeats = {rows, One}; | ||
| 576 | + *arange.y.strides = {One, Zero}; | ||
| 577 | + *arange.y.vectorized_axis = {row_axis.id, singleton_axis.id}; | ||
| 578 | + *arange.y.vectorized_strides = {One, Zero}; | ||
| 579 | + | ||
| 580 | + auto arange_node = graph.FindNode("arange"); | ||
| 581 | + ASSERT_NE(arange_node, nullptr); | ||
| 582 | + auto codegen_impl = ascgen_utils::GetAscIrCodegenImpl(arange_node->GetType()); | ||
| 583 | + ASSERT_NE(codegen_impl, nullptr); | ||
| 584 | + EXPECT_TRUE(codegen_impl->IsVectorFunctionSupported(*arange_node)); | ||
| 585 | + | ||
| 586 | + arange_node->outputs[0].attr.vectorized_strides = {af::Symbol(2), Zero}; | ||
| 587 | + EXPECT_FALSE(codegen_impl->IsVectorFunctionSupported(*arange_node)); | ||
| 588 | + arange_node->outputs[0].attr.vectorized_strides = {Zero, Zero}; | ||
| 589 | + EXPECT_FALSE(codegen_impl->IsVectorFunctionSupported(*arange_node)); | ||
| 590 | +} | ||
| 591 | + | ||
| 592 | +TEST_F(VfPartition, ArangeWithMoreThanFourUnmergedAxesSupportsPartition) { | ||
| 593 | + af::AscGraph graph("high_rank_arange"); | ||
| 594 | + std::vector<af::AxisId> axes; | ||
| 595 | + for (size_t i = 0UL; i < 5UL; ++i) { | ||
| 596 | + axes.push_back(graph.CreateAxis("z" + std::to_string(i), af::Symbol(2)).id); | ||
| 597 | + } | ||
| 598 | + af::ascir_op::Arange arange("arange", graph); | ||
| 599 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 600 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 601 | + arange.y.dtype = af::DT_INT32; | ||
| 602 | + af::ascir_op::Store store("store"); | ||
| 603 | + store.y.dtype = af::DT_INT32; | ||
| 604 | + af::ascir_op::Output output("output"); | ||
| 605 | + output.ir_attr.SetIndex(0); | ||
| 606 | + output.y.dtype = af::DT_INT32; | ||
| 607 | + ASSERT_NE(graph.AddNode(store), nullptr); | ||
| 608 | + ASSERT_NE(graph.AddNode(output), nullptr); | ||
| 609 | + store.x = arange.y; | ||
| 610 | + output.x = store.y; | ||
| 611 | + const std::vector<af::Expression> repeats(5UL, af::Symbol(2)); | ||
| 612 | + const std::vector<af::Expression> strides = {af::Symbol(16), af::Symbol(8), af::Symbol(4), af::Symbol(2), One}; | ||
| 613 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 614 | + node->attr.sched.axis = axes; | ||
| 615 | + for (auto &node_output : node->outputs()) { | ||
| 616 | + node_output->attr.axis = axes; | ||
| 617 | + node_output->attr.repeats = repeats; | ||
| 618 | + node_output->attr.strides = strides; | ||
| 619 | + node_output->attr.vectorized_axis = axes; | ||
| 620 | + node_output->attr.vectorized_strides = strides; | ||
| 621 | + } | ||
| 622 | + } | ||
| 623 | + optimize::AscGraphInfoComplete::CompleteApiInfo(graph); | ||
| 624 | + | ||
| 625 | + VectorFuncPartitioner partitioner(graph); | ||
| 626 | + ASSERT_EQ(partitioner.Partition(), af::SUCCESS); | ||
| 627 | + EXPECT_EQ(graph.FindNode("arange"), nullptr); | ||
| 628 | + EXPECT_NE(graph.FindNode("store"), nullptr); | ||
| 629 | + EXPECT_NE(graph.FindNode("output"), nullptr); | ||
| 630 | + const auto vf_node = graph.FindNode("high_rank_arange_VfNode_0"); | ||
| 631 | + ASSERT_NE(vf_node, nullptr); | ||
| 632 | + EXPECT_EQ(vf_node->GetAllInDataAnchorsSize(), 0UL); | ||
| 633 | + EXPECT_EQ(vf_node->GetAllOutDataAnchorsSize(), 1UL); | ||
| 634 | + std::vector<af::AscGraph> subgraphs; | ||
| 635 | + ASSERT_EQ(graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 636 | + ASSERT_EQ(subgraphs.size(), 1UL); | ||
| 637 | + EXPECT_NE(subgraphs[0].FindNode("arange"), nullptr); | ||
| 638 | + const auto store_inputs = graph.FindNode("store")->GetInDataNodes(); | ||
| 639 | + ASSERT_EQ(store_inputs.size(), 1UL); | ||
| 640 | + EXPECT_EQ((*store_inputs.begin())->GetName(), "high_rank_arange_VfNode_0"); | ||
| 641 | + const auto output_inputs = graph.FindNode("output")->GetInDataNodes(); | ||
| 642 | + ASSERT_EQ(output_inputs.size(), 1UL); | ||
| 643 | + EXPECT_EQ((*output_inputs.begin())->GetName(), "store"); | ||
| 644 | +} | ||
| 645 | + | ||
| 646 | +TEST_F(VfPartition, SingletonArangeIsPartitionedByFullPipeline) { | ||
| 647 | + af::AscGraph graph("singleton_arange"); | ||
| 648 | + af::ascir_op::Arange arange("arange", graph); | ||
| 649 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 650 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 651 | + arange.y.dtype = af::DT_INT32; | ||
| 652 | + af::ascir_op::Store store("store"); | ||
| 653 | + store.y.dtype = af::DT_INT32; | ||
| 654 | + af::ascir_op::Output output("output"); | ||
| 655 | + output.ir_attr.SetIndex(0); | ||
| 656 | + output.y.dtype = af::DT_INT32; | ||
| 657 | + ASSERT_NE(graph.AddNode(store), nullptr); | ||
| 658 | + ASSERT_NE(graph.AddNode(output), nullptr); | ||
| 659 | + store.x = arange.y; | ||
| 660 | + output.x = store.y; | ||
| 661 | + SetupGraphAxes(graph, {One, One}); | ||
| 662 | + ASSERT_EQ(AlignmentHandler::AlignVectorizedStrides(graph), af::SUCCESS); | ||
| 663 | + | ||
| 664 | + VectorFuncPartitioner partitioner(graph); | ||
| 665 | + ASSERT_EQ(partitioner.Partition(), af::SUCCESS); | ||
| 666 | + | ||
| 667 | + EXPECT_EQ(graph.FindNode("arange"), nullptr); | ||
| 668 | + EXPECT_NE(graph.FindNode("singleton_arange_VfNode_0"), nullptr); | ||
| 669 | + std::vector<af::AscGraph> subgraphs; | ||
| 670 | + ASSERT_EQ(graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 671 | + ASSERT_EQ(subgraphs.size(), 1UL); | ||
| 672 | + EXPECT_NE(subgraphs[0].FindNode("arange"), nullptr); | ||
| 673 | +} | ||
| 674 | + | ||
| 675 | +TEST_F(VfPartition, CompareRejectsIndexExprOnlyOnFirstInput) { | ||
| 676 | + const auto is_supported = [](bool index_expr_is_first) { | ||
| 677 | + af::AscGraph graph(index_expr_is_first ? "index_expr_first" : "index_expr_second"); | ||
| 678 | + const auto size = af::Symbol(32); | ||
| 679 | + const auto axis = graph.CreateAxis("z0", size); | ||
| 680 | + af::ascir_op::Arange arange("arange", graph); | ||
| 681 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 682 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 683 | + arange.y.dtype = af::DT_INT32; | ||
| 684 | + af::ascir_op::IndexExpr index_expr("index_expr", graph); | ||
| 685 | + index_expr.ir_attr.SetExpr(af::Symbol(7)); | ||
| 686 | + index_expr.y.dtype = af::DT_INT32; | ||
| 687 | + af::ascir_op::Lt compare("compare"); | ||
| 688 | + compare.y.dtype = af::DT_UINT8; | ||
| 689 | + EXPECT_NE(graph.AddNode(compare), nullptr); | ||
| 690 | + if (index_expr_is_first) { | ||
| 691 | + compare.x1 = index_expr.y; | ||
| 692 | + compare.x2 = arange.y; | ||
| 693 | + } else { | ||
| 694 | + compare.x1 = arange.y; | ||
| 695 | + compare.x2 = index_expr.y; | ||
| 696 | + } | ||
| 697 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 698 | + if (node->outputs().empty()) { | ||
| 699 | + continue; | ||
| 700 | + } | ||
| 701 | + node->outputs[0].attr.vectorized_axis = {axis.id}; | ||
| 702 | + node->outputs[0].attr.vectorized_strides = {One}; | ||
| 703 | + } | ||
| 704 | + auto compare_node = graph.FindNode("compare"); | ||
| 705 | + EXPECT_NE(compare_node, nullptr); | ||
| 706 | + auto codegen_impl = ascgen_utils::GetAscIrCodegenImpl(compare_node->GetType()); | ||
| 707 | + EXPECT_NE(codegen_impl, nullptr); | ||
| 708 | + return codegen_impl->IsVectorFunctionSupported(*compare_node); | ||
| 709 | + }; | ||
| 710 | + | ||
| 711 | + EXPECT_FALSE(is_supported(true)); | ||
| 712 | + EXPECT_TRUE(is_supported(false)); | ||
| 713 | +} | ||
| 714 | + | ||
| 715 | +TEST_F(VfPartition, ArangeZeroInputClusterBuildsSubgraphWithoutDynamicInputs) { | ||
| 716 | + af::AscGraph graph("arange_store_zero_input"); | ||
| 717 | + const auto size = af::Symbol(65); | ||
| 718 | + const auto axis = graph.CreateAxis("z0", size); | ||
| 719 | + af::ascir_op::Arange arange("arange"); | ||
| 720 | + arange.ir_attr.SetBase(af::Symbol(0)); | ||
| 721 | + arange.ir_attr.SetStep(af::Symbol(1)); | ||
| 722 | + arange.y.dtype = af::DT_INT32; | ||
| 723 | + af::ascir_op::Store store("store"); | ||
| 724 | + store.y.dtype = af::DT_INT32; | ||
| 725 | + af::ascir_op::Output output("output"); | ||
| 726 | + ASSERT_NE(graph.AddNode(arange), nullptr); | ||
| 727 | + ASSERT_NE(graph.AddNode(store), nullptr); | ||
| 728 | + ASSERT_NE(graph.AddNode(output), nullptr); | ||
| 729 | + store.x = arange.y; | ||
| 730 | + output.x = store.y; | ||
| 731 | + output.ir_attr.SetIndex(0); | ||
| 732 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 733 | + node->attr.sched.axis = {axis.id}; | ||
| 734 | + for (auto &node_output : node->outputs()) { | ||
| 735 | + node_output->attr.axis = {axis.id}; | ||
| 736 | + node_output->attr.repeats = {size}; | ||
| 737 | + node_output->attr.strides = {One}; | ||
| 738 | + node_output->attr.vectorized_axis = {axis.id}; | ||
| 739 | + node_output->attr.vectorized_strides = {One}; | ||
| 740 | + } | ||
| 741 | + } | ||
| 742 | + optimize::AscGraphInfoComplete::CompleteApiInfo(graph); | ||
| 743 | + | ||
| 744 | + auto cluster = std::make_shared<Cluster>(graph.FindNode("arange"), 0UL); | ||
| 745 | + cluster->meta_data_.enable_vf = true; | ||
| 746 | + cluster->out_nodes_.insert(graph.FindNode("arange")); | ||
| 747 | + VectorFuncPartitioner partitioner(graph); | ||
| 748 | + partitioner.root_graph_ = af::AscGraphUtils::GetComputeGraph(graph); | ||
| 749 | + partitioner.cluster_dict_.AddCluster(cluster); | ||
| 750 | + ASSERT_EQ(partitioner.BuildSubgraphs(), af::SUCCESS); | ||
| 751 | + | ||
| 752 | + std::vector<af::AscGraph> subgraphs; | ||
| 753 | + ASSERT_EQ(graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 754 | + ASSERT_EQ(subgraphs.size(), 1UL); | ||
| 755 | + const auto vf_node = graph.FindNode("arange_store_zero_input_VfNode_0"); | ||
| 756 | + ASSERT_NE(vf_node, nullptr); | ||
| 757 | + EXPECT_EQ(vf_node->GetAllInDataAnchorsSize(), 0UL); | ||
| 758 | + EXPECT_EQ(vf_node->GetAllOutDataAnchorsSize(), 1UL); | ||
| 759 | + EXPECT_NE(subgraphs[0].FindNode("arange"), nullptr); | ||
| 760 | + EXPECT_EQ(subgraphs[0].FindNode("store"), nullptr); | ||
| 761 | +} | ||
| 762 | + | ||
| 763 | +TEST_F(VfPartition, IndexExprBoundaryBuildsScalarPlaceholder) { | ||
| 764 | + af::AscGraph graph("index_expr_boundary"); | ||
| 765 | + af::ascir_op::IndexExpr index_expr("index_expr", graph); | ||
| 766 | + index_expr.ir_attr.SetExpr(af::Symbol(7)); | ||
| 767 | + index_expr.y.dtype = af::DT_INT32; | ||
| 768 | + af::ascir_op::Add add("add"); | ||
| 769 | + add.y.dtype = af::DT_INT32; | ||
| 770 | + ASSERT_NE(graph.AddNode(add), nullptr); | ||
| 771 | + add.x1 = index_expr.y; | ||
| 772 | + | ||
| 773 | + auto index_node = graph.FindNode("index_expr"); | ||
| 774 | + auto add_node = graph.FindNode("add"); | ||
| 775 | + ASSERT_NE(index_node, nullptr); | ||
| 776 | + ASSERT_NE(add_node, nullptr); | ||
| 777 | + af::AscGraph vf_graph("index_expr_boundary_vf"); | ||
| 778 | + ASSERT_EQ(VectorFuncPartitioner::InsertScalarNode(vf_graph, index_node->GetOutDataAnchor(0), | ||
| 779 | + {add_node->GetInDataAnchor(0)}, 0), | ||
| 780 | + af::SUCCESS); | ||
| 781 | + | ||
| 782 | + auto scalar_node = vf_graph.FindNode("Scalar_index_expr"); | ||
| 783 | + ASSERT_NE(scalar_node, nullptr); | ||
| 784 | + int64_t index = -1; | ||
| 785 | + ASSERT_EQ(scalar_node->attr.ir_attr->GetAttrValue("index", index), af::GRAPH_SUCCESS); | ||
| 786 | + EXPECT_EQ(index, 0); | ||
| 787 | +} | ||
| 788 | + | ||
| 789 | +TEST_F(VfPartition, IndexExprArangeCompositionPreservesStoreOutputBoundary) { | ||
| 790 | + auto graph = BuildIndexExprArangeCompositionGraph(); | ||
| 791 | + ASSERT_EQ(AlignmentHandler::AlignVectorizedStrides(graph), af::SUCCESS); | ||
| 792 | + | ||
| 793 | + VectorFuncPartitioner partitioner(graph); | ||
| 794 | + ASSERT_EQ(partitioner.Partition(), af::SUCCESS); | ||
| 795 | + | ||
| 796 | + // Partition 阶段不做 FinalizeIndexedGraphs 的图名重排(_B0Y0_S0G0C0 后缀由 Optimize | ||
| 797 | + // 主流程追加), 此处使用 Partition 直接产出的原始 VF 节点名。 | ||
| 798 | + const auto vf_node = graph.FindNode("index_expr_arange_composition_python_VfNode_0"); | ||
| 799 | + ASSERT_NE(vf_node, nullptr); | ||
| 800 | + ASSERT_EQ(vf_node->GetAllOutDataAnchorsSize(), 1UL); | ||
| 801 | + const auto peers = vf_node->GetOutDataAnchor(0)->GetPeerInDataAnchors(); | ||
| 802 | + ASSERT_EQ(peers.size(), 1UL); | ||
| 803 | + EXPECT_EQ((*peers.begin())->GetOwnerNodeBarePtr()->GetName(), "store_0"); | ||
| 804 | + std::vector<af::AscGraph> subgraphs; | ||
| 805 | + ASSERT_EQ(graph.GetAllSubGraphs(subgraphs), af::SUCCESS); | ||
| 806 | + ASSERT_EQ(subgraphs.size(), 1UL); | ||
| 807 | + EXPECT_EQ(subgraphs[0].FindNode("index_expr_arange_composition_python_VfNode_0"), nullptr); | ||
| 808 | + // 子图出口副本命名: kNamePrefix + 源节点名 + parent_out_idx(无分隔符拼接)。 | ||
| 809 | + EXPECT_NE(subgraphs[0].FindNode("Store_add_10"), nullptr); | ||
| 810 | + EXPECT_NE(subgraphs[0].FindNode("Output_add_10"), nullptr); | ||
| 811 | + | ||
| 812 | + std::vector<int64_t> tensor_ids; | ||
| 813 | + for (const auto &node : subgraphs[0].GetAllNodes()) { | ||
| 814 | + if (ScheduleUtils::IsBuffer(node)) { | ||
| 815 | + continue; | ||
| 816 | + } | ||
| 817 | + for (const auto &node_output : node->outputs()) { | ||
| 818 | + tensor_ids.push_back(node_output->attr.mem.tensor_id); | ||
| 819 | + } | ||
| 820 | + } | ||
| 821 | + std::sort(tensor_ids.begin(), tensor_ids.end()); | ||
| 822 | + for (size_t i = 0UL; i < tensor_ids.size(); ++i) { | ||
| 823 | + EXPECT_EQ(tensor_ids[i], static_cast<int64_t>(i)); | ||
| 824 | + } | ||
| 825 | +} | ||
| 826 | + | ||
| 343 | TEST_F(VfPartition, brc_with_cycle) { | 827 | TEST_F(VfPartition, brc_with_cycle) { |
| 344 | af::AscGraph graph("brc_abs"); | 828 | af::AscGraph graph("brc_abs"); |
| 345 | auto s0 = af::Symbol(10); | 829 | auto s0 = af::Symbol(10); |
| @@ -1854,6 +2338,67 @@ TEST_F(VfPartition, tail_axis_stride_not_one_disable_vf) { | |||
| 1854 | EXPECT_EQ(sub_graphs.size(), 0UL); | 2338 | EXPECT_EQ(sub_graphs.size(), 0UL); |
| 1855 | } | 2339 | } |
| 1856 | 2340 | ||
| 2341 | +TEST_F(VfPartition, OriginalTailStrideDoesNotDisableContinuousVectorizedVf) { | ||
| 2342 | + af::AscGraph graph("original_tail_stride_test"); | ||
| 2343 | + const auto s0 = af::Symbol(10); | ||
| 2344 | + const auto s1 = af::Symbol(10); | ||
| 2345 | + const auto axis0 = graph.CreateAxis("z0", s0); | ||
| 2346 | + const auto axis1 = graph.CreateAxis("z1", s1); | ||
| 2347 | + af::ascir_op::Data data("data", graph); | ||
| 2348 | + data.ir_attr.SetIndex(0); | ||
| 2349 | + af::ascir_op::Load load("load"); | ||
| 2350 | + load.x = data.y; | ||
| 2351 | + load.y.dtype = ge::DT_FLOAT; | ||
| 2352 | + af::ascir_op::Abs abs("abs"); | ||
| 2353 | + abs.x = load.y; | ||
| 2354 | + abs.y.dtype = ge::DT_FLOAT; | ||
| 2355 | + optimize::AscGraphInfoComplete::CompleteApiInfo(graph); | ||
| 2356 | + | ||
| 2357 | + auto abs_node = graph.FindNode("abs"); | ||
| 2358 | + ASSERT_NE(abs_node, nullptr); | ||
| 2359 | + abs_node->outputs[0].attr.axis = {axis0.id, axis1.id}; | ||
| 2360 | + abs_node->outputs[0].attr.repeats = {s0, s1}; | ||
| 2361 | + abs_node->outputs[0].attr.strides = {s1 * af::Symbol(2), af::Symbol(2)}; | ||
| 2362 | + abs_node->outputs[0].attr.vectorized_axis = {axis0.id, axis1.id}; | ||
| 2363 | + abs_node->outputs[0].attr.vectorized_strides = {s1, One}; | ||
| 2364 | + | ||
| 2365 | + auto cluster = std::make_shared<Cluster>(abs_node, 0UL); | ||
| 2366 | + cluster->meta_data_.enable_vf = true; | ||
| 2367 | + auto codegen_impl = ascgen_utils::GetAscIrCodegenImpl(abs_node->GetType()); | ||
| 2368 | + ASSERT_NE(codegen_impl, nullptr); | ||
| 2369 | + ASSERT_EQ(VectorFuncPartitioner::InitClusterAttr(codegen_impl, abs_node, cluster), af::SUCCESS); | ||
| 2370 | + EXPECT_TRUE(cluster->meta_data_.enable_vf); | ||
| 2371 | +} | ||
| 2372 | + | ||
| 2373 | +TEST_F(VfPartition, EmptyVectorizedAxisDoesNotSkipTensorId) { | ||
| 2374 | + af::AscGraph graph("empty_vectorized_axis_tensor_id"); | ||
| 2375 | + const auto size = af::Symbol(16); | ||
| 2376 | + const auto axis = graph.CreateAxis("z0", size); | ||
| 2377 | + af::ascir_op::Data data("data", graph); | ||
| 2378 | + data.ir_attr.SetIndex(0); | ||
| 2379 | + af::ascir_op::Load load("load"); | ||
| 2380 | + load.x = data.y; | ||
| 2381 | + load.y.dtype = ge::DT_FLOAT; | ||
| 2382 | + af::ascir_op::Abs abs("abs"); | ||
| 2383 | + abs.x = load.y; | ||
| 2384 | + abs.y.dtype = ge::DT_FLOAT; | ||
| 2385 | + optimize::AscGraphInfoComplete::CompleteApiInfo(graph); | ||
| 2386 | + | ||
| 2387 | + auto load_node = graph.FindNode("load"); | ||
| 2388 | + auto abs_node = graph.FindNode("abs"); | ||
| 2389 | + ASSERT_NE(load_node, nullptr); | ||
| 2390 | + ASSERT_NE(abs_node, nullptr); | ||
| 2391 | + load_node->outputs[0].attr.vectorized_axis.clear(); | ||
| 2392 | + load_node->outputs[0].attr.vectorized_strides.clear(); | ||
| 2393 | + abs_node->outputs[0].attr.vectorized_axis = {axis.id}; | ||
| 2394 | + abs_node->outputs[0].attr.vectorized_strides = {One}; | ||
| 2395 | + | ||
| 2396 | + VectorFuncPartitioner partitioner(graph); | ||
| 2397 | + ASSERT_EQ(partitioner.SetSubGraphAttrs(graph), af::SUCCESS); | ||
| 2398 | + EXPECT_EQ(load_node->outputs[0].attr.mem.tensor_id, 0); | ||
| 2399 | + EXPECT_EQ(abs_node->outputs[0].attr.mem.tensor_id, 1); | ||
| 2400 | +} | ||
| 2401 | + | ||
| 1857 | TEST_F(VfPartition, topological_sort_for_vf_graph_keeps_load_before_consumer) { | 2402 | TEST_F(VfPartition, topological_sort_for_vf_graph_keeps_load_before_consumer) { |
| 1858 | af::AscGraph graph("vf_sort_keep_dependency"); | 2403 | af::AscGraph graph("vf_sort_keep_dependency"); |
| 1859 | auto z0 = graph.CreateAxis("z0", af::Symbol(2)); | 2404 | auto z0 = graph.CreateAxis("z0", af::Symbol(2)); |
| @@ -489,11 +489,20 @@ REG_ASC_IR(ScalarData) | |||
| 489 | {{"T", TensorType{DT_INT8, DT_UINT8, DT_INT16, DT_UINT16, DT_INT32, DT_UINT32, DT_INT64, | 489 | {{"T", TensorType{DT_INT8, DT_UINT8, DT_INT16, DT_UINT16, DT_INT32, DT_UINT32, DT_INT64, |
| 490 | DT_UINT64, DT_FLOAT16, DT_FLOAT, DT_BF16, DT_BOOL}}}}); | 490 | DT_UINT64, DT_FLOAT16, DT_FLOAT, DT_BF16, DT_BOOL}}}}); |
| 491 | 491 | ||
| 492 | -REG_ASC_IR(IndexExpr).Impl(v2_soc_versions, | 492 | +REG_ASC_IR(IndexExpr).Impl(v2_soc_versions, {af::ascir::AscIrImplCreator<af::ascir::IndexExprAscIrAttImplV2>(), |
| 493 | - {af::ascir::AscIrImplCreator<af::ascir::IndexExprAscIrAttImplV2>(), | 493 | + af::ascir::AscIrImplCreator<af::ascir::IndexExprAscIrCodegenImplV2>(), |
| 494 | - af::ascir::AscIrImplCreator<af::ascir::IndexExprAscIrCodegenImplV2>(), | 494 | + {{"T", TensorType{DT_INT32, DT_INT64}}}}); |
| 495 | - {{"T", TensorType{DT_INT8, DT_UINT8, DT_INT16, DT_UINT16, DT_INT32, DT_UINT32, DT_INT64, | 495 | + |
| 496 | - DT_UINT64, DT_FLOAT16, DT_FLOAT}}}}); | 496 | +REG_ASC_IR(Arange) |
Z 新增ASCIR需要提供一下典型Shape的性能测试,是否涉及外抛for循环,是否会导致Vector bound ![]() ![]() | |||
| 497 | + .Inputs({}) | ||
| 498 | + .Output("y", "T") | ||
| 499 | + .StartNode() | ||
| 500 | + .Attr<Expression>("base") | ||
| 501 | + .Attr<Expression>("step") | ||
| 502 | + .ComputeType(ComputeType::kComputeElewise) | ||
| 503 | + .Impl(v2_soc_versions, {af::ascir::AscIrImplCreator<af::ascir::AscIrAttStubV2>(), | ||
| 504 | + af::ascir::AscIrImplCreator<af::ascir::ArangeAscIrCodegenImplV2>(), | ||
| 505 | + {{"T", TensorType{DT_INT32, DT_INT64}}}}); | ||
| 497 | 506 | ||
| 498 | REG_ASC_IR(Output).Impl(v2_soc_versions, | 507 | REG_ASC_IR(Output).Impl(v2_soc_versions, |
| 499 | {af::ascir::AscIrImplCreator<af::ascir::OutputAscIrAttImplV2>(), | 508 | {af::ascir::AscIrImplCreator<af::ascir::OutputAscIrAttImplV2>(), |
| @@ -206,6 +206,60 @@ class IndexExprAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 206 | } | 206 | } |
| 207 | }; | 207 | }; |
| 208 | 208 | ||
| 209 | +class ArangeAscIrCodegenImplV2 : public AscIrCodegenV2 { | ||
| 210 | + public: | ||
| 211 | + [[nodiscard]] bool IsVectorFunctionSupported(const AscNode &node) const override { | ||
| 212 | + auto &mutable_node = const_cast<AscNode &>(node); | ||
| 213 | + if (mutable_node.outputs().size() != 1UL) { | ||
| 214 | + return false; | ||
| 215 | + } | ||
| 216 | + const auto &output = mutable_node.outputs[0].attr; | ||
| 217 | + if (output.dtype != DT_INT32 && output.dtype != DT_INT64) { | ||
| 218 | + return false; | ||
| 219 | + } | ||
| 220 | + if (output.vectorized_axis.empty() || output.vectorized_axis.size() != output.vectorized_strides.size()) { | ||
| 221 | + return false; | ||
| 222 | + } | ||
| 223 | + for (auto iter = output.vectorized_strides.rbegin(); iter != output.vectorized_strides.rend(); ++iter) { | ||
| 224 | + const auto stride = iter->Simplify(); | ||
| 225 | + if (af::SymbolicUtils::StaticCheckEq(stride, af::sym::kSymbolZero) == af::TriBool::kTrue) { | ||
| 226 | + continue; | ||
| 227 | + } | ||
| 228 | + return af::SymbolicUtils::StaticCheckEq(stride, af::sym::kSymbolOne) == af::TriBool::kTrue; | ||
| 229 | + } | ||
| 230 | + for (size_t i = 0UL; i < output.vectorized_axis.size(); ++i) { | ||
| 231 | + const auto axis_iter = std::find(output.axis.begin(), output.axis.end(), output.vectorized_axis[i]); | ||
| 232 | + if (axis_iter == output.axis.end()) { | ||
| 233 | + return false; | ||
| 234 | + } | ||
| 235 | + const auto axis_index = static_cast<size_t>(std::distance(output.axis.begin(), axis_iter)); | ||
| 236 | + if (axis_index >= output.repeats.size() || | ||
| 237 | + af::SymbolicUtils::StaticCheckEq(output.repeats[axis_index].Simplify(), af::sym::kSymbolOne) != | ||
| 238 | + af::TriBool::kTrue) { | ||
| 239 | + return false; | ||
| 240 | + } | ||
| 241 | + } | ||
| 242 | + return true; | ||
| 243 | + } | ||
| 244 | + [[nodiscard]] std::string GetApiCallName() const override { | ||
| 245 | + return ""; | ||
| 246 | + } | ||
| 247 | + [[nodiscard]] std::string GetApiName() const override { | ||
| 248 | + return "Arange"; | ||
| 249 | + } | ||
| 250 | + [[nodiscard]] std::string GetMicroApiCallName() const override { | ||
| 251 | + return "MicroArangeApiCall"; | ||
| 252 | + } | ||
| 253 | + [[nodiscard]] std::string GetMicroApiName() const override { | ||
| 254 | + return "Arange"; | ||
| 255 | + } | ||
| 256 | + [[nodiscard]] std::vector<std::string> IncludeApiHeaderFiles() const override { | ||
| 257 | + return { | ||
| 258 | + "basic_api/reg_compute/kernel_reg_compute_intf.h", | ||
| 259 | + }; | ||
| 260 | + } | ||
| 261 | +}; | ||
| 262 | + | ||
| 209 | class OutputAscIrCodegenImplV2 : public AscIrCodegenV2 { | 263 | class OutputAscIrCodegenImplV2 : public AscIrCodegenV2 { |
| 210 | public: | 264 | public: |
| 211 | [[nodiscard]] std::string GetApiCallName() const override { | 265 | [[nodiscard]] std::string GetApiCallName() const override { |
| @@ -2645,7 +2699,8 @@ class CompareAscIrCodegenImplV2 : public AscIrCodegenV2 { | |||
| 2645 | if (!IsAllVecAxisContinuous(node)) { | 2699 | if (!IsAllVecAxisContinuous(node)) { |
| 2646 | return false; | 2700 | return false; |
| 2647 | } | 2701 | } |
| 2648 | - if (node.GetInDataNodes().at(0)->GetType() == "Scalar") { | 2702 | + const auto first_input_type = node.GetInDataNodes().at(0)->GetType(); |
| 2703 | + if (first_input_type == "Scalar" || first_input_type == "IndexExpr") { | ||
| 2649 | return false; | 2704 | return false; |
| 2650 | } | 2705 | } |
| 2651 | return true; | 2706 | return true; |
| @@ -17,10 +17,17 @@ | |||
| 17 | 17 | ||
| 18 | namespace codegen { | 18 | namespace codegen { |
| 19 | 19 | ||
| 20 | +struct ArangeParams { | ||
| 21 | + bool valid = false; | ||
| 22 | + std::string base; | ||
| 23 | + std::string step; | ||
| 24 | +}; | ||
| 25 | + | ||
| 20 | struct CallParam { | 26 | struct CallParam { |
| 21 | std::string p_reg; | 27 | std::string p_reg; |
| 22 | std::string offset; | 28 | std::string offset; |
| 23 | std::string max_dtype_size; | 29 | std::string max_dtype_size; |
| 30 | + ArangeParams arange; | ||
| 24 | }; | 31 | }; |
| 25 | 32 | ||
| 26 | enum class TensorType : int8_t { | 33 | enum class TensorType : int8_t { |
| @@ -68,6 +75,10 @@ class MicroApiCall { | |||
| 68 | 75 | ||
| 69 | // 生成micro api的调用 | 76 | // 生成micro api的调用 |
| 70 | virtual Status Generate(const TensorManager &tensor_mng, const TPipe &tpipe, CallParam ¶m, std::string &result); | 77 | virtual Status Generate(const TensorManager &tensor_mng, const TPipe &tpipe, CallParam ¶m, std::string &result); |
| 78 | + virtual bool HasArangeParam() const { | ||
| 79 | + return false; | ||
| 80 | + } | ||
| 81 | + virtual void GetArangeParams(const TPipe &, std::string &, std::string &) const {} | ||
| 71 | 82 | ||
| 72 | // 生成outputs; | 83 | // 生成outputs; |
| 73 | virtual Status Init([[maybe_unused]] const ascir::NodeView &node) { | 84 | virtual Status Init([[maybe_unused]] const ascir::NodeView &node) { |
| @@ -0,0 +1,67 @@ | |||
| 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 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | +namespace codegen { | ||
| 18 | +Status MicroArangeApiCall::Init(const ascir::NodeView &node) { | ||
| 19 | + GE_CHK_STATUS_RET(MicroApiCall::Init(node)); | ||
| 20 | + GE_ASSERT_TRUE(node->outputs[0].attr.dtype == ge::DT_INT32 || node->outputs[0].attr.dtype == ge::DT_INT64, | ||
| 21 | + "Arange supports only int32 and int64, dtype:%d", static_cast<int32_t>(node->outputs[0].attr.dtype)); | ||
| 22 | + GE_CHK_GRAPH_STATUS_RET(node->attr.ir_attr->GetAttrValue("base", base_), "Failed to get Arange base attr"); | ||
| 23 | + GE_CHK_GRAPH_STATUS_RET(node->attr.ir_attr->GetAttrValue("step", step_), "Failed to get Arange step attr"); | ||
| 24 | + return af::SUCCESS; | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +void MicroArangeApiCall::GetArangeParams(const TPipe &tpipe, std::string &base, std::string &step) const { | ||
| 28 | + base = tpipe.tiler.ActualSize(base_); | ||
| 29 | + step = tpipe.tiler.ActualSize(step_); | ||
| 30 | +} | ||
| 31 | + | ||
| 32 | +Status MicroArangeApiCall::Generate(const TensorManager &tensor_mng, const TPipe &tpipe, CallParam ¶m, | ||
| 33 | + std::string &result) { | ||
| 34 | + GE_ASSERT_TRUE(outputs_.size() == 1U, "Arange micro api call must have one output"); | ||
| 35 | + const auto *output = tensor_mng.GetTensor(outputs_[0].second); | ||
| 36 | + GE_ASSERT_NOTNULL(output); | ||
| 37 | + | ||
| 38 | + std::string dtype_name; | ||
| 39 | + GE_CHK_STATUS_RET(Tensor::DtypeName(output->dtype_, dtype_name), "Get Arange dtype:%d failed", | ||
| 40 | + static_cast<int32_t>(output->dtype_)); | ||
| 41 | + const auto base = tpipe.tiler.ActualSize(base_); | ||
| 42 | + const auto step = tpipe.tiler.ActualSize(step_); | ||
| 43 | + GE_ASSERT_TRUE(!base.empty() && !step.empty(), "Failed to generate Arange base or step expression"); | ||
| 44 | + const std::string block_offset = param.offset.empty() ? "0" : param.offset; | ||
| 45 | + const auto vf_base = param.arange.valid ? param.arange.base : base; | ||
| 46 | + const auto vf_step = param.arange.valid ? param.arange.step : step; | ||
| 47 | + GE_ASSERT_TRUE(!vf_base.empty() && !vf_step.empty(), "Failed to generate Arange base or step expression"); | ||
| 48 | + const std::string block_base = | ||
| 49 | + param.offset.empty() ? vf_base : "(" + vf_base + " + (" + block_offset + ") * (" + vf_step + "))"; | ||
| 50 | + | ||
| 51 | + std::stringstream ss; | ||
| 52 | + if (af::SymbolicUtils::StaticCheckEq(step_, af::sym::kSymbolOne) == af::TriBool::kTrue) { | ||
| 53 | + ss << "AscendC::Reg::Arange(" << output->name << ", static_cast<" << dtype_name << ">(" << block_base << "));" | ||
| 54 | + << std::endl; | ||
| 55 | + } else { | ||
| 56 | + ss << "AscendC::Reg::Arange(" << output->name << ", static_cast<" << dtype_name << ">(0));" << std::endl; | ||
| 57 | + ss << "AscendC::Reg::Muls(" << output->name << ", " << output->name << ", static_cast<" << dtype_name << ">(" | ||
| 58 | + << vf_step << "), " << param.p_reg << ");" << std::endl; | ||
| 59 | + ss << "AscendC::Reg::Adds(" << output->name << ", " << output->name << ", static_cast<" << dtype_name << ">(" | ||
| 60 | + << block_base << "), " << param.p_reg << ");" << std::endl; | ||
| 61 | + } | ||
| 62 | + result = ss.str(); | ||
| 63 | + return af::SUCCESS; | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +static MicroApiCallRegister<MicroArangeApiCall> register_micro_arange_api_call("MicroArangeApiCall"); | ||
| 67 | +} // namespace codegen | ||
| @@ -0,0 +1,34 @@ | |||
| 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 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +namespace codegen { | ||
| 16 | +class MicroArangeApiCall final : public MicroApiCall { | ||
| 17 | + public: | ||
| 18 | + explicit MicroArangeApiCall(const std::string &api_name) : MicroApiCall(api_name) {} | ||
| 19 | + ~MicroArangeApiCall() final = default; | ||
| 20 | + | ||
| 21 | + Status Init(const ascir::NodeView &node) override; | ||
| 22 | + Status Generate(const TensorManager &tensor_mng, const TPipe &tpipe, CallParam ¶m, std::string &result) override; | ||
| 23 | + bool HasArangeParam() const override { | ||
| 24 | + return true; | ||
| 25 | + } | ||
| 26 | + void GetArangeParams(const TPipe &tpipe, std::string &base, std::string &step) const override; | ||
| 27 | + | ||
| 28 | + private: | ||
| 29 | + af::Expression base_; | ||
| 30 | + af::Expression step_; | ||
| 31 | +}; | ||
| 32 | +} // namespace codegen | ||
| 33 | + | ||
| 34 | + | ||
| @@ -15,7 +15,8 @@ | |||
| 15 | namespace codegen { | 15 | namespace codegen { |
| 16 | namespace { | 16 | namespace { |
| 17 | bool IsScalarNodeType(const std::string &node_type) { | 17 | bool IsScalarNodeType(const std::string &node_type) { |
| 18 | - return node_type == af::ascir_op::Scalar::Type || node_type == af::ascir_op::ScalarData::Type; | 18 | + return node_type == af::ascir_op::Scalar::Type || node_type == af::ascir_op::ScalarData::Type || |
| 19 | + node_type == af::ascir_op::IndexExpr::Type; | ||
| 19 | } | 20 | } |
| 20 | 21 | ||
| 21 | Status GenerateInput(const std::pair<TensorType, ascir::TensorId> &input, const codegen::TensorManager &tensor_mng, | 22 | Status GenerateInput(const std::pair<TensorType, ascir::TensorId> &input, const codegen::TensorManager &tensor_mng, |
| @@ -26,15 +26,16 @@ Status MicroCompareApiCall::Generate(const codegen::TensorManager &tensor_mng, [ | |||
| 26 | // Compare 输出到 MaskReg(实际输出或临时) | 26 | // Compare 输出到 MaskReg(实际输出或临时) |
| 27 | std::string output_name = is_mask_reg ? output_tensor->name : (output_tensor->name + "_temp_mask"); | 27 | std::string output_name = is_mask_reg ? output_tensor->name : (output_tensor->name + "_temp_mask"); |
| 28 | 28 | ||
| 29 | - auto dtype = tensor_mng.GetTensor(this->inputs_[0].second)->dtype_; | 29 | + const auto *first_input = tensor_mng.GetTensor(this->inputs_[0].second); |
| 30 | + GE_ASSERT_NOTNULL(first_input); | ||
| 31 | + auto dtype = first_input->dtype_; | ||
| 30 | string dtype_name; | 32 | string dtype_name; |
| 31 | Tensor::DtypeName(dtype, dtype_name); | 33 | Tensor::DtypeName(dtype, dtype_name); |
| 32 | ss << "AscendC::MicroAPI::" << "Compare" << (this->second_input_scalar_ ? "s" : ""); | 34 | ss << "AscendC::MicroAPI::" << "Compare" << (this->second_input_scalar_ ? "s" : ""); |
| 33 | ss << "<" << dtype_name << ", CMPMODE::" << this->api_name_ << ">("; | 35 | ss << "<" << dtype_name << ", CMPMODE::" << this->api_name_ << ">("; |
| 34 | ss << output_name << ", "; | 36 | ss << output_name << ", "; |
| 35 | 37 | ||
| 36 | - GE_ASSERT_NOTNULL(tensor_mng.GetTensor(this->inputs_[0].second)); | 38 | + ss << *first_input << ", "; |
| 37 | - ss << *(tensor_mng.GetTensor(this->inputs_[0].second)) << ", "; | ||
| 38 | if (inputs_[1].first != TensorType::REG_TENSOR) { | 39 | if (inputs_[1].first != TensorType::REG_TENSOR) { |
| 39 | GE_ASSERT_NOTNULL(tpipe.GetTensor(this->inputs_[1].second)); | 40 | GE_ASSERT_NOTNULL(tpipe.GetTensor(this->inputs_[1].second)); |
| 40 | ss << *(tpipe.GetTensor(inputs_[1].second)) << ", "; | 41 | ss << *(tpipe.GetTensor(inputs_[1].second)) << ", "; |
| @@ -57,7 +58,8 @@ Status MicroCompareApiCall::Generate(const codegen::TensorManager &tensor_mng, [ | |||
| 57 | 58 | ||
| 58 | Status MicroCompareApiCall::Init(const ascir::NodeView &node) { | 59 | Status MicroCompareApiCall::Init(const ascir::NodeView &node) { |
| 59 | // 判断第二个输入是否是scalar | 60 | // 判断第二个输入是否是scalar |
| 60 | - if (node->GetInDataNodes().at(1)->GetType() == "Scalar") { | 61 | + if (node->GetInDataNodes().at(1)->GetType() == "Scalar" || |
| 62 | + node->GetInDataNodes().at(1)->GetType() == af::ascir_op::IndexExpr::Type) { | ||
| 61 | this->second_input_scalar_ = true; | 63 | this->second_input_scalar_ = true; |
| 62 | } | 64 | } |
| 63 | GELOGI("name:%s, second input scalar:%d", node->GetNamePtr(), this->second_input_scalar_); | 65 | GELOGI("name:%s, second input scalar:%d", node->GetNamePtr(), this->second_input_scalar_); |
| @@ -184,17 +184,22 @@ void CreateDimAndStrideParmas(const TPipe &tpipe, const VectorizedAxisLoopMergeS | |||
| 184 | void CreateVFCall(const TPipe &tpipe, const std::string &vf_call_name, const std::vector<Tensor> &inputs, | 184 | void CreateVFCall(const TPipe &tpipe, const std::string &vf_call_name, const std::vector<Tensor> &inputs, |
| 185 | const std::vector<Tensor> &outputs, const std::vector<std::string> &input_ub_offsets, | 185 | const std::vector<Tensor> &outputs, const std::vector<std::string> &input_ub_offsets, |
| 186 | const std::vector<std::string> &output_ub_offsets, const std::vector<Tensor> &tensors_scalar, | 186 | const std::vector<std::string> &output_ub_offsets, const std::vector<Tensor> &tensors_scalar, |
| 187 | - const VectorizedAxisLoopMergeStatus &merge_info, std::stringstream &ss) { | 187 | + const VectorizedAxisLoopMergeStatus &merge_info, const std::vector<ArangeParam> &arange_params, |
| 188 | + std::stringstream &ss) { | ||
| 188 | ss << vf_call_name << "("; | 189 | ss << vf_call_name << "("; |
| 189 | CreateTensorAddr(outputs, output_ub_offsets, {}, ss); | 190 | CreateTensorAddr(outputs, output_ub_offsets, {}, ss); |
| 190 | CreateTensorAddr(inputs, input_ub_offsets, tensors_scalar, ss); | 191 | CreateTensorAddr(inputs, input_ub_offsets, tensors_scalar, ss); |
| 191 | CreateDimAndStrideParmas(tpipe, merge_info, ss); | 192 | CreateDimAndStrideParmas(tpipe, merge_info, ss); |
| 193 | + for (const auto ¶m : arange_params) { | ||
| 194 | + ss << ", " << param.offset << ", " << param.base << ", " << param.step; | ||
| 195 | + } | ||
| 192 | ss << ");" << std::endl; | 196 | ss << ");" << std::endl; |
| 193 | } | 197 | } |
| 194 | 198 | ||
| 195 | void CreateOuterForVFCall(const TPipe &tpipe, const std::string &vf_call_name, const std::vector<Tensor> &inputs, | 199 | void CreateOuterForVFCall(const TPipe &tpipe, const std::string &vf_call_name, const std::vector<Tensor> &inputs, |
| 196 | const std::vector<Tensor> &outputs, const std::vector<Tensor> &tensors_scalar, | 200 | const std::vector<Tensor> &outputs, const std::vector<Tensor> &tensors_scalar, |
| 197 | - const VectorizedAxisLoopMergeStatus &merge_info, std::stringstream &ss) { | 201 | + const VectorizedAxisLoopMergeStatus &merge_info, |
| 202 | + const std::vector<ArangeParam> &arange_params, std::stringstream &ss) { | ||
| 198 | std::vector<std::string> repeats(merge_info.merge_repeats_str.begin(), | 203 | std::vector<std::string> repeats(merge_info.merge_repeats_str.begin(), |
| 199 | merge_info.merge_repeats_str.end() - kVFMaxLoop); | 204 | merge_info.merge_repeats_str.end() - kVFMaxLoop); |
| 200 | std::vector<std::vector<ascir::SizeExpr>> inputs_strides; | 205 | std::vector<std::vector<ascir::SizeExpr>> inputs_strides; |
| @@ -207,7 +212,7 @@ void CreateOuterForVFCall(const TPipe &tpipe, const std::string &vf_call_name, c | |||
| 207 | GetOuterForOffset(tpipe, outputs_strides, outputs_ub_offsets); | 212 | GetOuterForOffset(tpipe, outputs_strides, outputs_ub_offsets); |
| 208 | std::stringstream ss1; | 213 | std::stringstream ss1; |
| 209 | CreateVFCall(tpipe, vf_call_name, inputs, outputs, inputs_ub_offsets, outputs_ub_offsets, tensors_scalar, merge_info, | 214 | CreateVFCall(tpipe, vf_call_name, inputs, outputs, inputs_ub_offsets, outputs_ub_offsets, tensors_scalar, merge_info, |
| 210 | - ss1); | 215 | + arange_params, ss1); |
| 211 | CreateComputeNodeOuterFor(repeats, ss1, ss, 0); | 216 | CreateComputeNodeOuterFor(repeats, ss1, ss, 0); |
| 212 | } | 217 | } |
| 213 | 218 | ||
| @@ -300,9 +305,35 @@ Status VfCall::ParseSubGraph(const ascir::NodeView &vf_node, const ascir::ImplGr | |||
| 300 | GELOGI("VF node:%s, sub_graph_name:%s", vf_node->GetNamePtr(), graph_name->c_str()); | 305 | GELOGI("VF node:%s, sub_graph_name:%s", vf_node->GetNamePtr(), graph_name->c_str()); |
| 301 | 306 | ||
| 302 | uint32_t max_dtype_size = 0; | 307 | uint32_t max_dtype_size = 0; |
| 308 | + VFInputMapping input_mapping; | ||
| 303 | for (auto node : sub_graph.GetAllNodes()) { | 309 | for (auto node : sub_graph.GetAllNodes()) { |
| 304 | // subgraph上的Load api直接使用Tpipe上保存的UB tensor, 因此vf子图上Data节点的输出Tensor不必保存在tensor manager中. | 310 | // subgraph上的Load api直接使用Tpipe上保存的UB tensor, 因此vf子图上Data节点的输出Tensor不必保存在tensor manager中. |
| 305 | - if (IsOps<Output>(node) || IsOps<Data>(node) || IsOps<Scalar>(node)) { | 311 | + if (IsOps<Output>(node) || IsOps<Data>(node) || IsOps<Scalar>(node) || IsOps<ScalarData>(node) || |
| 312 | + IsOps<IndexExpr>(node)) { | ||
| 313 | + if (IsOps<Scalar>(node)) { | ||
| 314 | + const auto scalar_tensor_id = node->outputs[0].attr.mem.tensor_id; | ||
| 315 | + if (scalar_tensor_id != af::kIdNone) { | ||
| 316 | + subgraph_scalar_ids_.emplace_back(scalar_tensor_id); | ||
| 317 | + } | ||
| 318 | + } | ||
| 319 | + if (IsOps<Data>(node) || IsOps<ScalarData>(node)) { | ||
| 320 | + int64_t boundary_index = 0; | ||
| 321 | + int64_t root_input_index = 0; | ||
| 322 | + GE_CHK_GRAPH_STATUS_RET(node->attr.ir_attr->GetAttrValue("index", boundary_index), | ||
| 323 | + "Get boundary index failed, node:%s", node->GetNamePtr()); | ||
| 324 | + if (!af::AttrUtils::GetInt(node->GetOpDesc(), "vf_root_input_index", root_input_index)) { | ||
| 325 | + // vf_root_input_index 由 VectorFuncPartitioner 在建边后写入; 缺失说明该子图未经过分区 | ||
| 326 | + // (如手工构造的UT fixture)。回退用 boundary index, 对"按序连接边界"的子图语义等价。 | ||
| 327 | + GELOGW("Node [%s] lacks vf_root_input_index; fall back to boundary index [%ld].", node->GetNamePtr(), | ||
| 328 | + boundary_index); | ||
| 329 | + root_input_index = boundary_index; | ||
| 330 | + } | ||
| 331 | + GE_CHK_BOOL_RET_STATUS( | ||
| 332 | + root_input_index >= 0 && static_cast<uint64_t>(root_input_index) < vf_node->inputs.Size(), af::FAILED, | ||
| 333 | + "Invalid root input index:%ld for node:%s", root_input_index, node->GetNamePtr()); | ||
| 334 | + GE_CHK_BOOL_RET_STATUS(input_mapping.emplace(boundary_index, static_cast<uint32_t>(root_input_index)).second, | ||
| 335 | + af::FAILED, "Duplicate root input mapping for boundary index:%ld", boundary_index); | ||
| 336 | + } | ||
| 306 | continue; | 337 | continue; |
| 307 | } | 338 | } |
| 308 | // broadcast inline场景,子图内会对轴进行重排序,生成vf代码时,需要找到原始最内层轴所在的位置,用于生成UpdateMask动作 | 339 | // broadcast inline场景,子图内会对轴进行重排序,生成vf代码时,需要找到原始最内层轴所在的位置,用于生成UpdateMask动作 |
| @@ -328,7 +359,7 @@ Status VfCall::ParseSubGraph(const ascir::NodeView &vf_node, const ascir::ImplGr | |||
| 328 | } | 359 | } |
| 329 | root_loop_.SetMaxDtypeSize(this->max_dtype_size_); | 360 | root_loop_.SetMaxDtypeSize(this->max_dtype_size_); |
| 330 | // Parse for loop | 361 | // Parse for loop |
| 331 | - return root_loop_.ConstructFromNodes(sub_graph.GetAllNodes(), vf_node); | 362 | + return root_loop_.ConstructFromNodes(sub_graph.GetAllNodes(), vf_node, input_mapping); |
| 332 | } | 363 | } |
| 333 | 364 | ||
| 334 | Status VfCall::ParseInputOutputInfo(const TPipe &tpipe) const { | 365 | Status VfCall::ParseInputOutputInfo(const TPipe &tpipe) const { |
| @@ -347,6 +378,18 @@ Status VfCall::ParseInputOutputInfo(const TPipe &tpipe) const { | |||
| 347 | GE_CHK_BOOL_RET_STATUS(tensor_ptr != nullptr, af::FAILED, "Check[Param] tensor_ptr is nullptr"); | 378 | GE_CHK_BOOL_RET_STATUS(tensor_ptr != nullptr, af::FAILED, "Check[Param] tensor_ptr is nullptr"); |
| 348 | ub_outputs_.emplace_back(*tensor_ptr); | 379 | ub_outputs_.emplace_back(*tensor_ptr); |
| 349 | } | 380 | } |
| 381 | + | ||
| 382 | + // VF子图内消费的常量Scalar副本不是根图边界输入, 其const tensor需单独补入scalar参数。 | ||
| 383 | + for (const auto scalar_id : subgraph_scalar_ids_) { | ||
| 384 | + bool already_collected = false; | ||
| 385 | + for (const auto &scalar : scalar_inputs_) { | ||
| 386 | + already_collected = already_collected || (scalar.id == scalar_id); | ||
| 387 | + } | ||
| 388 | + const auto tensor_ptr = already_collected ? nullptr : tpipe.GetTensor(scalar_id); | ||
| 389 | + if ((tensor_ptr != nullptr) && tensor_ptr->IsConstScalar()) { | ||
| 390 | + scalar_inputs_.emplace_back(*tensor_ptr); | ||
| 391 | + } | ||
| 392 | + } | ||
| 350 | // 处理合轴信息 | 393 | // 处理合轴信息 |
| 351 | return af::SUCCESS; | 394 | return af::SUCCESS; |
| 352 | } | 395 | } |
| @@ -448,12 +491,19 @@ void GenerateTensorDefs(const TPipe &tpipe, const TensorManager &tensor_mgr, con | |||
| 448 | 491 | ||
| 449 | void GenerateVfCallFuncHeader(const TPipe &tpipe, const std::string &vf_call_name, const std::vector<Tensor> &inputs, | 492 | void GenerateVfCallFuncHeader(const TPipe &tpipe, const std::string &vf_call_name, const std::vector<Tensor> &inputs, |
| 450 | const std::vector<Tensor> &scalar_inputs, const std::vector<Tensor> &outputs, | 493 | const std::vector<Tensor> &scalar_inputs, const std::vector<Tensor> &outputs, |
| 451 | - const VectorizedAxisLoopMergeStatus &merge_info, std::stringstream &ss) { | 494 | + const VectorizedAxisLoopMergeStatus &merge_info, |
| 495 | + const std::vector<ArangeParam> &arange_params, std::stringstream &ss) { | ||
| 452 | ss << "#if defined(__DAV_C310__) || " | 496 | ss << "#if defined(__DAV_C310__) || " |
| 453 | "(defined(__NPU_ARCH__) && (__NPU_ARCH__ == 5102 || __NPU_ARCH__ == 3510 || __NPU_ARCH__ == 9202))" | 497 | "(defined(__NPU_ARCH__) && (__NPU_ARCH__ == 5102 || __NPU_ARCH__ == 3510 || __NPU_ARCH__ == 9202))" |
| 454 | << std::endl; | 498 | << std::endl; |
| 455 | ss << "\ninline __simd_vf__ void " << vf_call_name << "("; | 499 | ss << "\ninline __simd_vf__ void " << vf_call_name << "("; |
| 456 | CreateVFCallDimAndStrideParmas(tpipe, inputs, scalar_inputs, outputs, merge_info, ss); | 500 | CreateVFCallDimAndStrideParmas(tpipe, inputs, scalar_inputs, outputs, merge_info, ss); |
| 501 | + if (!arange_params.empty()) { | ||
| 502 | + for (const auto &arange_param : arange_params) { | ||
| 503 | + ss << ", int64_t arange_offset_" << arange_param.tensor_id << ", int64_t arange_base_" << arange_param.tensor_id | ||
| 504 | + << ", int64_t arange_step_" << arange_param.tensor_id; | ||
| 505 | + } | ||
| 506 | + } | ||
| 457 | ss << ")" << std::endl; | 507 | ss << ")" << std::endl; |
| 458 | } | 508 | } |
| 459 | 509 | ||
| @@ -510,9 +560,18 @@ Status GenerateVfCallLoopBody(const TPipe &tpipe, const TensorManager &tensor_mg | |||
| 510 | int32_t only_loop_max_depth = -1; | 560 | int32_t only_loop_max_depth = -1; |
| 511 | std::vector<std::string> loop_size_vec; | 561 | std::vector<std::string> loop_size_vec; |
| 512 | if (tpipe.cv_fusion_type == ascir::CubeTemplateType::kUBFuse) { | 562 | if (tpipe.cv_fusion_type == ascir::CubeTemplateType::kUBFuse) { |
| 513 | - root_loop.GenerateCvUbFuse(tpipe, tensor_mgr, loop_body, loop_size); | 563 | + GE_CHK_STATUS_RET(root_loop.GenerateCvUbFuse(tpipe, tensor_mgr, loop_body, loop_size), |
| 564 | + "Generate CV UBFuse VectorFunc body failed"); | ||
| 514 | } else { | 565 | } else { |
| 515 | - root_loop.Generate(tpipe, tensor_mgr, stride_depth, loop_body, loop_size, only_loop_max_depth, loop_size_vec); | 566 | + ArangeOffsetMap arange_offsets; |
| 567 | + std::vector<ArangeParam> arange_params; | ||
| 568 | + root_loop.CollectArangeParams(tpipe, arange_params); | ||
| 569 | + for (const auto ¶m : arange_params) { | ||
| 570 | + arange_offsets.emplace(param.tensor_id, "arange_offset_" + std::to_string(param.tensor_id)); | ||
| 571 | + } | ||
| 572 | + GE_CHK_STATUS_RET(root_loop.Generate(tpipe, tensor_mgr, stride_depth, loop_body, loop_size, only_loop_max_depth, | ||
| 573 | + loop_size_vec, arange_offsets), | ||
| 574 | + "Generate VectorFunc body failed"); | ||
| 516 | } | 575 | } |
| 517 | const bool is_double_loop = tpipe.cv_fusion_type != ascir::CubeTemplateType::kUBFuse && | 576 | const bool is_double_loop = tpipe.cv_fusion_type != ascir::CubeTemplateType::kUBFuse && |
| 518 | stride_depth == MAX_VF_AXIS_MERGE_SIZE - 1 && | 577 | stride_depth == MAX_VF_AXIS_MERGE_SIZE - 1 && |
| @@ -544,6 +603,10 @@ af::Status UpdateVectorFuncNodeParams(const af::AscNodePtr &node, const Vectoriz | |||
| 544 | } | 603 | } |
| 545 | 604 | ||
| 546 | Status VfCall::GenerateFuncDefinition(const TPipe &tpipe, const Tiler &tiler, std::stringstream &ss) const { | 605 | Status VfCall::GenerateFuncDefinition(const TPipe &tpipe, const Tiler &tiler, std::stringstream &ss) const { |
| 606 | + std::vector<ArangeParam> arange_params; | ||
| 607 | + root_loop_.CollectArangeParams(tpipe, arange_params); | ||
| 608 | + GE_ASSERT_TRUE(tpipe.cv_fusion_type != ascir::CubeTemplateType::kUBFuse || arange_params.empty(), | ||
| 609 | + "Arange is not supported in CV UBFuse VectorFunc."); | ||
| 547 | // 收集输入输出信息,由于GenInnerLoopSizeAndActualSize函数中会刷新tiler对象中的actual_sizes字段, | 610 | // 收集输入输出信息,由于GenInnerLoopSizeAndActualSize函数中会刷新tiler对象中的actual_sizes字段, |
| 548 | // 导致生成函数签名和函数调用时,获取到的size信息不一致,因此生成函数签名和函数调用时均需要调用合轴函数 | 611 | // 导致生成函数签名和函数调用时,获取到的size信息不一致,因此生成函数签名和函数调用时均需要调用合轴函数 |
| 549 | GE_ASSERT_SUCCESS(ParseInputOutputInfo(tpipe)); | 612 | GE_ASSERT_SUCCESS(ParseInputOutputInfo(tpipe)); |
| @@ -554,7 +617,7 @@ Status VfCall::GenerateFuncDefinition(const TPipe &tpipe, const Tiler &tiler, st | |||
| 554 | GE_ASSERT_TRUE(status, "GenerateVectorizedAxisMergeStatus failed"); | 617 | GE_ASSERT_TRUE(status, "GenerateVectorizedAxisMergeStatus failed"); |
| 555 | 618 | ||
| 556 | GenerateVfCallFuncHeader(tpipe, this->vf_call_name_, this->ub_inputs_, this->scalar_inputs_, this->ub_outputs_, | 619 | GenerateVfCallFuncHeader(tpipe, this->vf_call_name_, this->ub_inputs_, this->scalar_inputs_, this->ub_outputs_, |
| 557 | - merge_info, ss); | 620 | + merge_info, arange_params, ss); |
| 558 | 621 | ||
| 559 | // func body | 622 | // func body |
| 560 | std::stringstream params; | 623 | std::stringstream params; |
| @@ -589,7 +652,25 @@ Status VfCall::Generate(const TPipe &tpipe, [[maybe_unused]] const std::vector<a | |||
| 589 | GE_ASSERT_TRUE(status, "GenerateVectorizedAxisMergeStatus failed"); | 652 | GE_ASSERT_TRUE(status, "GenerateVectorizedAxisMergeStatus failed"); |
| 590 | 653 | ||
| 591 | std::stringstream ss; | 654 | std::stringstream ss; |
| 655 | + std::vector<ArangeParam> arange_params; | ||
| 656 | + root_loop_.CollectArangeParams(tpipe, arange_params); | ||
| 592 | size_t loop_num = merge_info.merge_repeats_str.size(); | 657 | size_t loop_num = merge_info.merge_repeats_str.size(); |
| 658 | + const size_t outer_loop_num = loop_num > kVFMaxLoop ? loop_num - kVFMaxLoop : 0UL; | ||
| 659 | + for (auto ¶m : arange_params) { | ||
| 660 | + const auto *arange_tensor = tensor_mgr_.GetTensor(param.tensor_id); | ||
| 661 | + GE_ASSERT_NOTNULL(arange_tensor); | ||
| 662 | + std::stringstream offset; | ||
| 663 | + offset << (current_axis.empty() | ||
| 664 | + ? "0" | ||
| 665 | + : tpipe.tiler.Offset(current_axis, arange_tensor->axis_, arange_tensor->axis_strides_)); | ||
| 666 | + for (size_t i = 0UL; i < outer_loop_num; ++i) { | ||
| 667 | + std::string stride; | ||
| 668 | + GE_CHK_STATUS_RET(GetArangeLogicalStride(tpipe, arange_tensor, merge_info.merge_axis_ids[i], stride), | ||
| 669 | + "Generate Arange outer-for stride failed"); | ||
| 670 | + offset << " + outer_for_" << i << " * " << stride; | ||
| 671 | + } | ||
| 672 | + param.offset = offset.str(); | ||
| 673 | + } | ||
| 593 | ss << "#if defined(__DAV_C310__) || " | 674 | ss << "#if defined(__DAV_C310__) || " |
| 594 | "(defined(__NPU_ARCH__) && (__NPU_ARCH__ == 5102 || __NPU_ARCH__ == 3510 || __NPU_ARCH__ == 9202))" | 675 | "(defined(__NPU_ARCH__) && (__NPU_ARCH__ == 5102 || __NPU_ARCH__ == 3510 || __NPU_ARCH__ == 9202))" |
| 595 | << std::endl; | 676 | << std::endl; |
| @@ -598,10 +679,10 @@ Status VfCall::Generate(const TPipe &tpipe, [[maybe_unused]] const std::vector<a | |||
| 598 | std::vector<std::string> inputs_ub_offsets = {}; | 679 | std::vector<std::string> inputs_ub_offsets = {}; |
| 599 | std::vector<std::string> outputs_ub_offsets = {}; | 680 | std::vector<std::string> outputs_ub_offsets = {}; |
| 600 | CreateVFCall(tpipe, this->vf_call_name_, this->ub_inputs_, this->ub_outputs_, inputs_ub_offsets, outputs_ub_offsets, | 681 | CreateVFCall(tpipe, this->vf_call_name_, this->ub_inputs_, this->ub_outputs_, inputs_ub_offsets, outputs_ub_offsets, |
| 601 | - this->scalar_inputs_, merge_info, ss); | 682 | + this->scalar_inputs_, merge_info, arange_params, ss); |
| 602 | } else { | 683 | } else { |
| 603 | CreateOuterForVFCall(tpipe, this->vf_call_name_, this->ub_inputs_, this->ub_outputs_, this->scalar_inputs_, | 684 | CreateOuterForVFCall(tpipe, this->vf_call_name_, this->ub_inputs_, this->ub_outputs_, this->scalar_inputs_, |
| 604 | - merge_info, ss); | 685 | + merge_info, arange_params, ss); |
| 605 | } | 686 | } |
| 606 | ss << "#endif" << std::endl; | 687 | ss << "#endif" << std::endl; |
| 607 | result = ss.str(); | 688 | result = ss.str(); |
| @@ -50,6 +50,7 @@ class VfCall final : public ApiCall { | |||
| 50 | mutable std::vector<Tensor> ub_inputs_; | 50 | mutable std::vector<Tensor> ub_inputs_; |
| 51 | mutable std::vector<Tensor> ub_outputs_; | 51 | mutable std::vector<Tensor> ub_outputs_; |
| 52 | mutable std::vector<Tensor> scalar_inputs_; | 52 | mutable std::vector<Tensor> scalar_inputs_; |
| 53 | + mutable std::vector<ascir::TensorId> subgraph_scalar_ids_; | ||
| 53 | }; | 54 | }; |
| 54 | } // namespace codegen | 55 | } // namespace codegen |
| 55 | 56 | ||


接口变更是否要考虑兼容性?