已合并
feat: 支持 IndirectLoad Load-Transpose 布局并完善 SIMD/SIMT codegen #2029
xiebangrui2025创建于 9月9日
feat: 支持 IndirectLoad Load-Transpose 布局并完善 SIMD/SIMT codegen #2029
已合并
共 19 个文件变更+2862-1324
| @@ -11,13 +11,16 @@ | |||
| 11 | #include "indirect_load_utils.h" | 11 | #include "indirect_load_utils.h" |
| 12 | 12 | ||
| 13 | #include <algorithm> | 13 | #include <algorithm> |
| 14 | +#include <limits> | ||
| 14 | #include <string> | 15 | #include <string> |
| 16 | +#include <unordered_map> | ||
| 15 | #include <unordered_set> | 17 | #include <unordered_set> |
| 16 | 18 | ||
| 17 | #include "ascir_ops.h" | 19 | #include "ascir_ops.h" |
| 18 | #include "ascir_ops_utils.h" | 20 | #include "ascir_ops_utils.h" |
| 19 | #include "graph/symbolizer/symbolic_utils.h" | 21 | #include "graph/symbolizer/symbolic_utils.h" |
| 20 | #include "schedule_result.h" | 22 | #include "schedule_result.h" |
| 23 | +#include "utils/extern_math_util.h" | ||
| 21 | 24 | ||
| 22 | namespace ascgen_utils::indirect_load { | 25 | namespace ascgen_utils::indirect_load { |
| 23 | namespace { | 26 | namespace { |
| @@ -24,6 +24,7 @@ constexpr size_t kInputTensorIndex = 0UL; | |||
| 24 | constexpr size_t kIndexTensorIndex = 1UL; | 24 | constexpr size_t kIndexTensorIndex = 1UL; |
| 25 | constexpr char kTemplateLogicalViewAttr[] = "af.internal.indirect_load.logical_view"; | 25 | constexpr char kTemplateLogicalViewAttr[] = "af.internal.indirect_load.logical_view"; |
| 26 | constexpr char kAccessInfoAttr[] = "af.internal.indirect_load.access_info"; | 26 | constexpr char kAccessInfoAttr[] = "af.internal.indirect_load.access_info"; |
| 27 | +constexpr char kLoweringMetadataAttr[] = "af.internal.indirect_load.lowering_metadata"; | ||
| 27 | 28 | ||
| 28 | enum class TemplateRole : int64_t { | 29 | enum class TemplateRole : int64_t { |
| 29 | kNone, | 30 | kNone, |
| @@ -103,6 +104,97 @@ struct IndirectLoadAccessInfo { | |||
| 103 | bool can_use_simd_embedding = false; | 104 | bool can_use_simd_embedding = false; |
| 104 | }; | 105 | }; |
| 105 | 106 | ||
| 107 | +enum class SimdFallback : int64_t { | ||
| 108 | + kRegisterGather = 0, | ||
| 109 | + kGatherApi = 1, | ||
| 110 | + kStrided = 2, | ||
| 111 | +}; | ||
| 112 | + | ||
| 113 | +struct SimdLoweringMetadata { | ||
| 114 | + SimdFallback fallback = SimdFallback::kRegisterGather; | ||
| 115 | + bool try_embedding = false; | ||
| 116 | +}; | ||
| 117 | + | ||
| 118 | +enum class SimtAddressPolicy : int64_t { | ||
| 119 | + kStaticPowerOfTwo = 0, | ||
| 120 | + kStaticInner = 1, | ||
| 121 | + kStructuredMagic = 2, | ||
| 122 | + kEmbedding = 3, | ||
| 123 | + kRecursive = 4, | ||
| 124 | + kStrided = 5, | ||
| 125 | +}; | ||
| 126 | + | ||
| 127 | +enum class SimtOffsetWidth : int64_t { | ||
| 128 | + kUint32, | ||
| 129 | + kUint64, | ||
| 130 | +}; | ||
| 131 | + | ||
| 132 | +enum class SimtLoadAddressSource : int64_t { | ||
| 133 | + kZeroOffset, | ||
| 134 | + kOutputOffset, | ||
| 135 | + kIndexOffset, | ||
| 136 | +}; | ||
| 137 | + | ||
| 138 | +struct SimtPolicyMetadata { | ||
| 139 | + SimtAddressPolicy policy = SimtAddressPolicy::kRecursive; | ||
| 140 | + SimtOffsetWidth offset_width = SimtOffsetWidth::kUint64; | ||
| 141 | + uint64_t inner_span = 0U; | ||
| 142 | + uint64_t output_axis_span = 0U; | ||
| 143 | + uint64_t input_axis_stride = 0U; | ||
| 144 | + uint64_t input_axis_span = 0U; | ||
| 145 | + uint64_t input_stride_mask = 0U; | ||
| 146 | + uint64_t index_stride_mask = 0U; | ||
| 147 | + std::vector<af::Expression> runtime_params; | ||
| 148 | +}; | ||
| 149 | + | ||
| 150 | +struct SimtGmTensorMetadata { | ||
| 151 | + ascir::TensorId value_tensor_id = af::kIdNone; | ||
| 152 | + ascir::TensorId gm_tensor_id = af::kIdNone; | ||
| 153 | + af::DataType dtype = af::DT_UNDEFINED; | ||
| 154 | + bool is_scalar = false; | ||
| 155 | +}; | ||
| 156 | + | ||
| 157 | +struct SimtLoadMetadata { | ||
| 158 | + std::string node_name; | ||
| 159 | + SimtLoadAddressSource address_source = SimtLoadAddressSource::kOutputOffset; | ||
| 160 | + bool use_logical_offset = false; | ||
| 161 | + LogicalTensorView physical_view; | ||
| 162 | +}; | ||
| 163 | + | ||
| 164 | +struct SimtOutputChainMetadata { | ||
| 165 | + std::vector<std::string> node_names; | ||
| 166 | + ascir::TensorId result_tensor_id = af::kIdNone; | ||
| 167 | + ascir::TensorId target_tensor_id = af::kIdNone; | ||
| 168 | + af::DataType dtype = af::DT_UNDEFINED; | ||
| 169 | + bool local_target = false; | ||
| 170 | +}; | ||
| 171 | + | ||
| 172 | +struct SimtLoweringMetadata { | ||
| 173 | + bool has_post_reduce = false; | ||
| 174 | + std::vector<std::string> index_node_names; | ||
| 175 | + std::vector<std::string> output_node_names; | ||
| 176 | + std::vector<SimtLoadMetadata> index_loads; | ||
| 177 | + std::vector<SimtLoadMetadata> output_loads; | ||
| 178 | + std::vector<SimtOutputChainMetadata> output_chains; | ||
| 179 | + std::vector<SimtGmTensorMetadata> gm_tensors; | ||
| 180 | + SimtPolicyMetadata policy; | ||
| 181 | + ascir::TensorId index_result_tensor_id = af::kIdNone; | ||
| 182 | + ascir::TensorId value_tensor_id = af::kIdNone; | ||
| 183 | + ascir::TensorId output_result_tensor_id = af::kIdNone; | ||
| 184 | + af::DataType index_dtype = af::DT_UNDEFINED; | ||
| 185 | + af::DataType output_dtype = af::DT_UNDEFINED; | ||
| 186 | +}; | ||
| 187 | + | ||
| 188 | +struct IndirectLoadLoweringMetadata { | ||
| 189 | + uint32_t version = 1U; | ||
| 190 | + int64_t axis = -1L; | ||
| 191 | + af::AxisId outer_axis = af::kIdNone; | ||
| 192 | + TemplateLogicalView logical_view; | ||
| 193 | + IndirectLoadAccessInfo access_info; | ||
| 194 | + SimdLoweringMetadata simd; | ||
| 195 | + SimtLoweringMetadata simt; | ||
| 196 | +}; | ||
| 197 | + | ||
| 106 | TemplateBehavior GetTemplateBehavior(const af::AscNodePtr &node); | 198 | TemplateBehavior GetTemplateBehavior(const af::AscNodePtr &node); |
| 107 | TemplateRole GetTemplateRole(const af::AscNodePtr &node); | 199 | TemplateRole GetTemplateRole(const af::AscNodePtr &node); |
| 108 | af::AscNodePtr GetPostReduceConsumer(const af::AscNodePtr &node); | 200 | af::AscNodePtr GetPostReduceConsumer(const af::AscNodePtr &node); |
| @@ -118,6 +210,10 @@ af::Status SetTemplateLogicalView(const af::AscNodePtr &node, const TemplateLogi | |||
| 118 | af::Status GetTemplateLogicalView(const af::AscNodePtr &node, TemplateLogicalView &view); | 210 | af::Status GetTemplateLogicalView(const af::AscNodePtr &node, TemplateLogicalView &view); |
| 119 | af::Status SetIndirectLoadAccessInfo(const af::AscNodePtr &node, const IndirectLoadAccessInfo &info); | 211 | af::Status SetIndirectLoadAccessInfo(const af::AscNodePtr &node, const IndirectLoadAccessInfo &info); |
| 120 | af::Status GetIndirectLoadAccessInfo(const af::AscNodePtr &node, IndirectLoadAccessInfo &info); | 212 | af::Status GetIndirectLoadAccessInfo(const af::AscNodePtr &node, IndirectLoadAccessInfo &info); |
| 213 | +af::Status SetLoweringMetadata(const af::AscNodePtr &node, const IndirectLoadLoweringMetadata &metadata); | ||
| 214 | +af::Status GetLoweringMetadata(const af::AscNodePtr &node, IndirectLoadLoweringMetadata &metadata); | ||
| 215 | +bool HasLoweringMetadata(const af::AscNodePtr &node); | ||
| 216 | +af::Status FinalizeLoweringMetadata(const af::AscNodePtr &node, bool &is_supported); | ||
| 121 | af::Status AnalyzeIndirectLoadAccess(const af::AscNodePtr &node, const TemplateLogicalView &logical_view, | 217 | af::Status AnalyzeIndirectLoadAccess(const af::AscNodePtr &node, const TemplateLogicalView &logical_view, |
| 122 | IndirectLoadAccessInfo &info); | 218 | IndirectLoadAccessInfo &info); |
| 123 | af::Status SetImplementation(const af::AscNodePtr &node, Implementation implementation); | 219 | af::Status SetImplementation(const af::AscNodePtr &node, Implementation implementation); |
| @@ -629,13 +629,6 @@ Status Scheduler::InitIndirectLoadScheduleCase() { | |||
| 629 | return af::SUCCESS; | 629 | return af::SUCCESS; |
| 630 | } | 630 | } |
| 631 | 631 | ||
| 632 | - for (const af::AscNodePtr &node : graph_.GetAllNodes()) { | ||
| 633 | - const auto role = ascgen_utils::indirect_load::GetTemplateRole(node); | ||
| 634 | - if (role == ascgen_utils::indirect_load::TemplateRole::kStridedUbPath || | ||
| 635 | - role == ascgen_utils::indirect_load::TemplateRole::kSimdInputPreStridedUbPath) { | ||
| 636 | - indirect_load_info_.aligned_strided_path.emplace_back(node); | ||
| 637 | - } | ||
| 638 | - } | ||
| 639 | GE_ASSERT_NOTNULL(tiling_case_.ub_tiling_y.first); | 632 | GE_ASSERT_NOTNULL(tiling_case_.ub_tiling_y.first); |
| 640 | GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::GetTemplateAxes(indirect_load, indirect_load_info_.axes)); | 633 | GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::GetTemplateAxes(indirect_load, indirect_load_info_.axes)); |
| 641 | indirect_load_info_.active = true; | 634 | indirect_load_info_.active = true; |
| @@ -772,14 +765,6 @@ Status Scheduler::DoScheduler() { | |||
| 772 | } | 765 | } |
| 773 | GE_ASSERT_SUCCESS(NodeCacheMarker(graph_).MarkIfNodeNeedsCache()); | 766 | GE_ASSERT_SUCCESS(NodeCacheMarker(graph_).MarkIfNodeNeedsCache()); |
| 774 | GE_ASSERT_SUCCESS(AlignmentHandler::ModifyVectorizedStrides(graph_)); | 767 | GE_ASSERT_SUCCESS(AlignmentHandler::ModifyVectorizedStrides(graph_)); |
| 775 | - for (const af::AscNodePtr &node : indirect_load_info_.aligned_strided_path) { | ||
| 776 | - for (const auto &output : node->outputs()) { | ||
| 777 | - if (!output->attr.vectorized_axis.empty()) { | ||
| 778 | - GE_ASSERT_SUCCESS( | ||
| 779 | - BaseAlignmentStrategy::SetVectorizedStridesForTensor(node, output->attr, AlignmentType::kAligned)); | ||
| 780 | - } | ||
| 781 | - } | ||
| 782 | - } | ||
| 783 | ascir::utils::DumpGraph(graph_, "AfterDoTiling"); | 768 | ascir::utils::DumpGraph(graph_, "AfterDoTiling"); |
| 784 | return af::SUCCESS; | 769 | return af::SUCCESS; |
| 785 | } | 770 | } |
| @@ -43,7 +43,6 @@ struct TilingCase { | |||
| 43 | 43 | ||
| 44 | struct IndirectLoadInfo { | 44 | struct IndirectLoadInfo { |
| 45 | bool active = false; | 45 | bool active = false; |
| 46 | - std::vector<af::AscNodePtr> aligned_strided_path; | ||
| 47 | ascgen_utils::indirect_load::TemplateAxes axes; | 46 | ascgen_utils::indirect_load::TemplateAxes axes; |
| 48 | }; | 47 | }; |
| 49 | 48 | ||
| @@ -56,6 +56,7 @@ struct RewrittenGraphAnalysis { | |||
| 56 | NodePath input_path; | 56 | NodePath input_path; |
| 57 | NodePath index_path; | 57 | NodePath index_path; |
| 58 | af::AscNodePtr input_root; | 58 | af::AscNodePtr input_root; |
| 59 | + af::AscNodePtr input_boundary; | ||
| 59 | af::AscNodePtr index_root; | 60 | af::AscNodePtr index_root; |
| 60 | af::AscNodePtr output_store; | 61 | af::AscNodePtr output_store; |
| 61 | af::AscNodePtr post_reduce; | 62 | af::AscNodePtr post_reduce; |
| @@ -66,6 +67,7 @@ struct RewrittenGraphAnalysis { | |||
| 66 | 67 | ||
| 67 | struct InputViewPlan { | 68 | struct InputViewPlan { |
| 68 | NodePath path; | 69 | NodePath path; |
| 70 | + af::AscNodePtr load_transpose; | ||
| 69 | ascgen_utils::indirect_load::IndirectLoadTensorLayout layout; | 71 | ascgen_utils::indirect_load::IndirectLoadTensorLayout layout; |
| 70 | int64_t path_broadcast_index = kInvalidBroadcastIndex; | 72 | int64_t path_broadcast_index = kInvalidBroadcastIndex; |
| 71 | bool simd_index_uses_output_inner_axis = false; | 73 | bool simd_index_uses_output_inner_axis = false; |
| @@ -85,10 +87,24 @@ struct PostReduceLayout { | |||
| 85 | size_t first_reduce; | 87 | size_t first_reduce; |
| 86 | }; | 88 | }; |
| 87 | 89 | ||
| 88 | -bool IsInputRegionBoundary(const af::AscNodePtr &node) { | 90 | +bool IsInputDataSource(const af::AscNodePtr &node) { |
| 89 | return ScheduleUtils::IsDataInput(node) || af::ops::IsOps<af::ascir_op::Scalar>(node); | 91 | return ScheduleUtils::IsDataInput(node) || af::ops::IsOps<af::ascir_op::Scalar>(node); |
| 90 | } | 92 | } |
| 91 | 93 | ||
| 94 | +// A retained Transpose separates two physical coordinate systems. Do not overwrite its source GM view. | ||
| 95 | +bool IsInputRegionBoundary(const af::AscNodePtr &node) { | ||
| 96 | + if (IsInputDataSource(node)) { | ||
| 97 | + return true; | ||
| 98 | + } | ||
| 99 | + if (!af::ops::IsOps<af::ascir_op::Load>(node)) { | ||
| 100 | + return false; | ||
| 101 | + } | ||
| 102 | + const auto consumers = node->GetOutDataNodes(); | ||
| 103 | + return std::any_of(consumers.begin(), consumers.end(), [](const af::NodePtr &consumer) { | ||
| 104 | + return af::ops::IsOps<af::ascir_op::Transpose>(std::dynamic_pointer_cast<af::AscNode>(consumer)); | ||
| 105 | + }); | ||
| 106 | +} | ||
| 107 | + | ||
| 92 | bool HasControlEdge(const af::AscNodePtr &node) { | 108 | bool HasControlEdge(const af::AscNodePtr &node) { |
| 93 | return node->GetInControlNodesSize() != 0UL || node->GetOutControlNodesSize() != 0UL; | 109 | return node->GetInControlNodesSize() != 0UL || node->GetOutControlNodesSize() != 0UL; |
| 94 | } | 110 | } |
| @@ -153,7 +169,10 @@ af::Status GetBroadcastPhysicalAttr(const af::AscNodePtr &broadcast, ascir::Temp | |||
| 153 | static_cast<int32_t>(template_id), broadcast->GetNamePtr()); | 169 | static_cast<int32_t>(template_id), broadcast->GetNamePtr()); |
| 154 | GE_ASSERT_NOTNULL(producer_outputs[output_idx], "IndirectLoad template[%d] Broadcast node[%s] source output is null.", | 170 | GE_ASSERT_NOTNULL(producer_outputs[output_idx], "IndirectLoad template[%d] Broadcast node[%s] source output is null.", |
| 155 | static_cast<int32_t>(template_id), broadcast->GetNamePtr()); | 171 | static_cast<int32_t>(template_id), broadcast->GetNamePtr()); |
| 156 | - physical_attr = producer_outputs[output_idx]->attr; | 172 | + const auto &producer_attr = producer_outputs[output_idx]->attr; |
| 173 | + physical_attr.axis = producer_attr.axis; | ||
| 174 | + physical_attr.repeats = producer_attr.repeats; | ||
| 175 | + physical_attr.strides = producer_attr.strides; | ||
| 157 | return af::SUCCESS; | 176 | return af::SUCCESS; |
| 158 | } | 177 | } |
| 159 | 178 | ||
| @@ -241,7 +260,7 @@ bool NeedsAlignedUbWindow(const ascgen_utils::indirect_load::IndirectLoadTensorL | |||
| 241 | 260 | ||
| 242 | af::Status AnnotateStridedUbPath(const NodePath &path) { | 261 | af::Status AnnotateStridedUbPath(const NodePath &path) { |
| 243 | for (const af::AscNodePtr &node : path) { | 262 | for (const af::AscNodePtr &node : path) { |
| 244 | - if (IsInputRegionBoundary(node) || ScheduleUtils::IsBuffer(node)) { | 263 | + if (IsInputDataSource(node) || ScheduleUtils::IsBuffer(node)) { |
| 245 | continue; | 264 | continue; |
| 246 | } | 265 | } |
| 247 | const auto role = ascgen_utils::indirect_load::GetTemplateRole(node); | 266 | const auto role = ascgen_utils::indirect_load::GetTemplateRole(node); |
| @@ -315,6 +334,19 @@ af::Status TraverseOutputConsumers(const NodePath &roots, NodeSet &visited, cons | |||
| 315 | return af::SUCCESS; | 334 | return af::SUCCESS; |
| 316 | } | 335 | } |
| 317 | 336 | ||
| 337 | +af::AscNodePtr GetLoadTransposeSource(const af::AscNodePtr &node) { | ||
| 338 | + if (node == nullptr || !af::ops::IsOps<af::ascir_op::Transpose>(node)) { | ||
| 339 | + return nullptr; | ||
| 340 | + } | ||
| 341 | + const auto producer = ascgen_utils::indirect_load::GetInputProducer(node, 0UL); | ||
| 342 | + return producer != nullptr && af::ops::IsOps<af::ascir_op::Load>(producer) ? producer : nullptr; | ||
| 343 | +} | ||
| 344 | + | ||
| 345 | +af::AscNodePtr GetFoldableLoadTransposeSource(const af::AscNodePtr &node) { | ||
| 346 | + const auto load = GetLoadTransposeSource(node); | ||
| 347 | + return IsSingleConsumerWithoutControlEdge(node) && IsSingleConsumerWithoutControlEdge(load) ? load : nullptr; | ||
| 348 | +} | ||
| 349 | + | ||
| 318 | bool IsSupportedIndirectLoadTopologyNode(const af::AscNodePtr &node) { | 350 | bool IsSupportedIndirectLoadTopologyNode(const af::AscNodePtr &node) { |
| 319 | if (node == nullptr) { | 351 | if (node == nullptr) { |
| 320 | return false; | 352 | return false; |
| @@ -323,6 +355,9 @@ bool IsSupportedIndirectLoadTopologyNode(const af::AscNodePtr &node) { | |||
| 323 | af::ops::IsOps<af::ascir_op::IndirectLoad>(node)) { | 355 | af::ops::IsOps<af::ascir_op::IndirectLoad>(node)) { |
| 324 | return true; | 356 | return true; |
| 325 | } | 357 | } |
| 358 | + if (GetLoadTransposeSource(node) != nullptr) { | ||
| 359 | + return true; | ||
| 360 | + } | ||
| 326 | return ScheduleUtils::IsLoad(node) || ScheduleUtils::IsStore(node) || ScheduleUtils::IsElewise(node) || | 361 | return ScheduleUtils::IsLoad(node) || ScheduleUtils::IsStore(node) || ScheduleUtils::IsElewise(node) || |
| 327 | ScheduleUtils::IsBroadcast(node) || ScheduleUtils::IsReduce(node); | 362 | ScheduleUtils::IsBroadcast(node) || ScheduleUtils::IsReduce(node); |
| 328 | } | 363 | } |
| @@ -366,7 +401,7 @@ bool CollectSimtBackwardRegion(const NodePath &roots, const af::AscNodePtr &indi | |||
| 366 | if (node == nullptr) { | 401 | if (node == nullptr) { |
| 367 | return false; | 402 | return false; |
| 368 | } | 403 | } |
| 369 | - if (node == indirect_load || IsInputRegionBoundary(node)) { | 404 | + if (node == indirect_load || IsInputDataSource(node)) { |
| 370 | continue; | 405 | continue; |
| 371 | } | 406 | } |
| 372 | if (!region.emplace(node.get()).second || af::ops::IsOps<af::ascir_op::Load>(node)) { | 407 | if (!region.emplace(node.get()).second || af::ops::IsOps<af::ascir_op::Load>(node)) { |
| @@ -921,7 +956,7 @@ af::Status ReplaceAxisPrefix(std::vector<af::AxisId> &target_axes, const std::ve | |||
| 921 | af::Status ReplaceRegionAxisPrefix(const NodePath ®ion, const std::vector<af::AxisId> &output_axes, | 956 | af::Status ReplaceRegionAxisPrefix(const NodePath ®ion, const std::vector<af::AxisId> &output_axes, |
| 922 | size_t axis_index, bool is_input_region) { | 957 | size_t axis_index, bool is_input_region) { |
| 923 | for (const af::AscNodePtr &node : region) { | 958 | for (const af::AscNodePtr &node : region) { |
| 924 | - if (!is_input_region && IsInputRegionBoundary(node)) { | 959 | + if (!is_input_region && IsInputDataSource(node)) { |
| 925 | continue; | 960 | continue; |
| 926 | } | 961 | } |
| 927 | const auto role = ascgen_utils::indirect_load::GetTemplateRole(node); | 962 | const auto role = ascgen_utils::indirect_load::GetTemplateRole(node); |
| @@ -1012,6 +1047,27 @@ bool CanEmitSimtScalar(const af::AscNodePtr &node) { | |||
| 1012 | return v2_impl != nullptr && v2_impl->IsSimtScalarSupported(*node); | 1047 | return v2_impl != nullptr && v2_impl->IsSimtScalarSupported(*node); |
| 1013 | } | 1048 | } |
| 1014 | 1049 | ||
| 1050 | +// The IndirectLoad logical view is expressed in the Transpose output order, | ||
| 1051 | +// while the direct-GM boundary still reads the original Load buffer. Map the | ||
| 1052 | +// source Load strides into that output order for the strided address policy. | ||
| 1053 | +af::Status BuildLoadTransposeSourceView(const af::AscNodePtr &transpose, const af::AscTensorAttr &logical_attr, | ||
| 1054 | + ascgen_utils::indirect_load::LogicalTensorView &view) { | ||
| 1055 | + const auto load = ascgen_utils::indirect_load::GetInputProducer(transpose, 0UL); | ||
| 1056 | + const auto &source = load->outputs[0].attr; | ||
| 1057 | + GE_ASSERT_TRUE(source.axis.size() == source.strides.size() && | ||
| 1058 | + logical_attr.axis.size() == logical_attr.repeats.size() && | ||
| 1059 | + logical_attr.axis.size() == logical_attr.strides.size(), | ||
| 1060 | + "IndirectLoad Load->Transpose tensor metadata rank mismatch."); | ||
| 1061 | + view = {logical_attr.axis, logical_attr.repeats, logical_attr.strides}; | ||
| 1062 | + for (size_t output_dim = 0UL; output_dim < logical_attr.axis.size(); ++output_dim) { | ||
| 1063 | + const auto source_axis = std::find(source.axis.begin(), source.axis.end(), logical_attr.axis[output_dim]); | ||
| 1064 | + GE_ASSERT_TRUE(source_axis != source.axis.end(), "IndirectLoad Transpose output axis is absent from Load source."); | ||
| 1065 | + const size_t source_dim = static_cast<size_t>(std::distance(source.axis.begin(), source_axis)); | ||
| 1066 | + view.strides[output_dim] = source.strides[source_dim]; | ||
| 1067 | + } | ||
| 1068 | + return af::SUCCESS; | ||
| 1069 | +} | ||
| 1070 | + | ||
| 1015 | af::Status ValidateReduceOutput(const af::AscNodePtr &reduce) { | 1071 | af::Status ValidateReduceOutput(const af::AscNodePtr &reduce) { |
| 1016 | const auto reduce_outputs = reduce->GetOutDataNodes(); | 1072 | const auto reduce_outputs = reduce->GetOutDataNodes(); |
| 1017 | GE_ASSERT_TRUE(reduce_outputs.size() == 1UL, "[IndirectLoad] Reduce[%s] must have one Store output, got %zu.", | 1073 | GE_ASSERT_TRUE(reduce_outputs.size() == 1UL, "[IndirectLoad] Reduce[%s] must have one Store output, got %zu.", |
| @@ -1067,6 +1123,10 @@ af::Status CollectOutputBoundaries(const af::AscNodePtr &indirect_load, Rewritte | |||
| 1067 | af::Status CollectRewrittenBoundaries(const af::AscNodePtr &indirect_load, RewrittenGraphAnalysis &analysis) { | 1123 | af::Status CollectRewrittenBoundaries(const af::AscNodePtr &indirect_load, RewrittenGraphAnalysis &analysis) { |
| 1068 | analysis.input_root = | 1124 | analysis.input_root = |
| 1069 | ascgen_utils::indirect_load::GetInputProducer(indirect_load, ascgen_utils::indirect_load::kInputTensorIndex); | 1125 | ascgen_utils::indirect_load::GetInputProducer(indirect_load, ascgen_utils::indirect_load::kInputTensorIndex); |
| 1126 | + analysis.input_boundary = GetLoadTransposeSource(analysis.input_root); | ||
| 1127 | + if (analysis.input_boundary == nullptr) { | ||
| 1128 | + analysis.input_boundary = analysis.input_root; | ||
| 1129 | + } | ||
| 1070 | analysis.index_root = | 1130 | analysis.index_root = |
| 1071 | ascgen_utils::indirect_load::GetInputProducer(indirect_load, ascgen_utils::indirect_load::kIndexTensorIndex); | 1131 | ascgen_utils::indirect_load::GetInputProducer(indirect_load, ascgen_utils::indirect_load::kIndexTensorIndex); |
| 1072 | GE_ASSERT_SUCCESS(CollectOutputBoundaries(indirect_load, analysis)); | 1132 | GE_ASSERT_SUCCESS(CollectOutputBoundaries(indirect_load, analysis)); |
| @@ -1160,6 +1220,14 @@ af::Status RewriteInputPreNodes(af::AscGraph &graph, const af::AscNodePtr &indir | |||
| 1160 | indirect_load->GetNamePtr(), af::ToString(input_shape).c_str(), af::ToString(output_shape).c_str()); | 1220 | indirect_load->GetNamePtr(), af::ToString(input_shape).c_str(), af::ToString(output_shape).c_str()); |
| 1161 | return af::SUCCESS; | 1221 | return af::SUCCESS; |
| 1162 | } | 1222 | } |
| 1223 | + // Keep a direct Load->Transpose boundary on the SIMT input side. Its | ||
| 1224 | + // permutation is represented by the SIMT address policy; moving it across | ||
| 1225 | + // IndirectLoad would discard the source physical view. | ||
| 1226 | + const auto input_producer = | ||
| 1227 | + ascgen_utils::indirect_load::GetInputProducer(indirect_load, ascgen_utils::indirect_load::kInputTensorIndex); | ||
| 1228 | + if (template_id == ascir::TemplateId::kIndirectLoadSimt && GetLoadTransposeSource(input_producer) != nullptr) { | ||
| 1229 | + return af::SUCCESS; | ||
| 1230 | + } | ||
| 1163 | NodePath movable_nodes; | 1231 | NodePath movable_nodes; |
| 1164 | af::AscNodePtr consumer = indirect_load; | 1232 | af::AscNodePtr consumer = indirect_load; |
| 1165 | for (af::AscNodePtr node = | 1233 | for (af::AscNodePtr node = |
| @@ -1262,47 +1330,71 @@ void CollectInputPaths(const af::AscNodePtr &indirect_load, ascir::TemplateId te | |||
| 1262 | } | 1330 | } |
| 1263 | } | 1331 | } |
| 1264 | 1332 | ||
| 1265 | -af::Status AnalyzeInputPath(const af::AscNodePtr &indirect_load, size_t input_idx, ascir::TemplateId template_id, | 1333 | +af::Status ResolveInputPhysicalView(const af::AscTensorAttr &logical_attr, ascir::TemplateId template_id, |
| 1266 | - InputViewPlan *const plans[], bool &is_path_supported) { | 1334 | + InputViewPlan &plan, af::AscTensorAttr &physical_attr, bool &is_supported) { |
| 1267 | - // 返回 SUCCESS 表示分析流程正常;is_path_supported 为 false 表示当前模板淘汰,不中止 Generate。 | 1335 | + physical_attr = logical_attr; |
| 1268 | - is_path_supported = true; | 1336 | + plan.load_transpose = nullptr; |
| 1269 | - const auto inputs = indirect_load->inputs(); | ||
| 1270 | - const size_t axis_index = GetIndirectLoadAxisIndex(indirect_load); | ||
| 1271 | - GE_ASSERT_TRUE(axis_index != kIndirectLoadInvalidAxisIndex, "IndirectLoad axis index is invalid."); | ||
| 1272 | - InputViewPlan &plan = *plans[input_idx]; | ||
| 1273 | - const auto &logical_attr = inputs[input_idx]->attr; | ||
| 1274 | - plan.simd_index_uses_output_inner_axis = false; | ||
| 1275 | - ascgen_utils::indirect_load::LogicalTensorView view; | ||
| 1276 | const bool has_broadcast = plan.path_broadcast_index != kInvalidBroadcastIndex; | 1337 | const bool has_broadcast = plan.path_broadcast_index != kInvalidBroadcastIndex; |
| 1277 | - if (!has_broadcast) { | 1338 | + af::AscNodePtr source = plan.path.empty() ? nullptr : plan.path.front(); |
| 1278 | - // 无直连广播(SIMD/SIMT)或无广播(SK):按逻辑视图直接分类,非直连广播由通用逻辑承载。 | 1339 | + if (has_broadcast) { |
| 1279 | - view = {logical_attr.axis, logical_attr.repeats, logical_attr.strides}; | 1340 | + const auto broadcast = plan.path[static_cast<size_t>(plan.path_broadcast_index)]; |
| 1280 | - } else { | 1341 | + source = ascgen_utils::indirect_load::GetInputProducer(broadcast, 0UL); |
| 1281 | - const size_t broadcast_index = static_cast<size_t>(plan.path_broadcast_index); | 1342 | + if (source == nullptr) { |
| 1282 | - const af::AscNodePtr &broadcast = plan.path[broadcast_index]; | ||
| 1283 | - // 取源物理属性前保留源存在性检查。 | ||
| 1284 | - if (ascgen_utils::indirect_load::GetInputProducer(broadcast, 0UL) == nullptr) { | ||
| 1285 | GELOGI("[IndirectLoad] Reject candidate[%d]: Broadcast node[%s] source is invalid.", | 1343 | GELOGI("[IndirectLoad] Reject candidate[%d]: Broadcast node[%s] source is invalid.", |
| 1286 | static_cast<int32_t>(template_id), broadcast->GetNamePtr()); | 1344 | static_cast<int32_t>(template_id), broadcast->GetNamePtr()); |
| 1287 | - is_path_supported = false; | 1345 | + is_supported = false; |
| 1288 | return af::SUCCESS; | 1346 | return af::SUCCESS; |
| 1289 | } | 1347 | } |
| 1290 | - // 基于广播源物理属性构造执行视图:源形状为 1 而逻辑视图非 1 的维度置 stride 0。 | ||
| 1291 | - af::AscTensorAttr physical_attr; | ||
| 1292 | GE_ASSERT_SUCCESS(GetBroadcastPhysicalAttr(broadcast, template_id, physical_attr)); | 1348 | GE_ASSERT_SUCCESS(GetBroadcastPhysicalAttr(broadcast, template_id, physical_attr)); |
| 1349 | + } | ||
| 1350 | + if (template_id == ascir::TemplateId::kIndirectLoadSK || GetLoadTransposeSource(source) == nullptr) { | ||
| 1351 | + return af::SUCCESS; | ||
| 1352 | + } | ||
| 1353 | + // SIMD may use the source Load only when the intervening Transpose can be folded safely. | ||
| 1354 | + // SIMT already reads GM directly, so it only needs the axis-mapped view, not a graph rewrite. | ||
| 1355 | + if (has_broadcast && template_id == ascir::TemplateId::kIndirectLoadSimd && | ||
| 1356 | + GetFoldableLoadTransposeSource(source) == nullptr) { | ||
| 1357 | + return af::SUCCESS; | ||
| 1358 | + } | ||
| 1359 | + plan.load_transpose = source; | ||
| 1360 | + ascgen_utils::indirect_load::LogicalTensorView source_view; | ||
| 1361 | + GE_ASSERT_SUCCESS(BuildLoadTransposeSourceView(source, physical_attr, source_view)); | ||
| 1362 | + physical_attr.strides = std::move(source_view.strides); | ||
| 1363 | + return af::SUCCESS; | ||
| 1364 | +} | ||
| 1365 | + | ||
| 1366 | +af::Status AnalyzeInputPath(const af::AscNodePtr &indirect_load, size_t input_idx, ascir::TemplateId template_id, | ||
| 1367 | + InputViewPlan &plan, bool &is_path_supported) { | ||
| 1368 | + is_path_supported = true; | ||
| 1369 | + const auto inputs = indirect_load->inputs(); | ||
| 1370 | + const auto &logical_attr = inputs[input_idx]->attr; | ||
| 1371 | + af::AscTensorAttr physical_attr; | ||
| 1372 | + GE_ASSERT_SUCCESS(ResolveInputPhysicalView(logical_attr, template_id, plan, physical_attr, is_path_supported)); | ||
| 1373 | + if (!is_path_supported) { | ||
| 1374 | + return af::SUCCESS; | ||
| 1375 | + } | ||
| 1376 | + const bool has_broadcast = plan.path_broadcast_index != kInvalidBroadcastIndex; | ||
| 1377 | + ascgen_utils::indirect_load::LogicalTensorView view{logical_attr.axis, logical_attr.repeats, physical_attr.strides}; | ||
| 1378 | + if (has_broadcast) { | ||
| 1293 | GE_ASSERT_SUCCESS(BuildBroadcastLogicalView(logical_attr, physical_attr, template_id, view)); | 1379 | GE_ASSERT_SUCCESS(BuildBroadcastLogicalView(logical_attr, physical_attr, template_id, view)); |
| 1294 | } | 1380 | } |
| 1295 | - | 1381 | + plan.simd_index_uses_output_inner_axis = template_id == ascir::TemplateId::kIndirectLoadSimd && |
| 1296 | - const bool allow_simt_strided_broadcast = template_id == ascir::TemplateId::kIndirectLoadSimt && has_broadcast; | 1382 | + input_idx == ascgen_utils::indirect_load::kIndexTensorIndex && |
| 1297 | - GE_ASSERT_SUCCESS( | 1383 | + (has_broadcast || plan.load_transpose != nullptr); |
| 1298 | - ascgen_utils::indirect_load::ClassifyIndirectLoadLayout(view, plan.layout, allow_simt_strided_broadcast)); | 1384 | + if (plan.load_transpose != nullptr) { |
| 1385 | + // Permutations are valid non-overlapping strided views even when strides are not monotonically decreasing. | ||
| 1386 | + plan.layout = {{view.axis_ids, view.sizes, view.strides}, | ||
| 1387 | + ascgen_utils::indirect_load::IndirectLoadLayoutKind::kStrided, | ||
| 1388 | + physical_attr.repeats}; | ||
| 1389 | + } else { | ||
| 1390 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::ClassifyIndirectLoadLayout( | ||
| 1391 | + view, plan.layout, template_id == ascir::TemplateId::kIndirectLoadSimt && has_broadcast)); | ||
| 1392 | + } | ||
| 1299 | if (plan.layout.kind == ascgen_utils::indirect_load::IndirectLoadLayoutKind::kUnsupported) { | 1393 | if (plan.layout.kind == ascgen_utils::indirect_load::IndirectLoadLayoutKind::kUnsupported) { |
| 1300 | GELOGI("[IndirectLoad] Reject candidate[%d]: input path layout%s is unsupported.", | 1394 | GELOGI("[IndirectLoad] Reject candidate[%d]: input path layout%s is unsupported.", |
| 1301 | static_cast<int32_t>(template_id), has_broadcast ? " with Broadcast source view" : ""); | 1395 | static_cast<int32_t>(template_id), has_broadcast ? " with Broadcast source view" : ""); |
| 1302 | is_path_supported = false; | 1396 | is_path_supported = false; |
| 1303 | } | 1397 | } |
| 1304 | - plan.simd_index_uses_output_inner_axis = template_id == ascir::TemplateId::kIndirectLoadSimd && | ||
| 1305 | - input_idx == ascgen_utils::indirect_load::kIndexTensorIndex && has_broadcast; | ||
| 1306 | return af::SUCCESS; | 1398 | return af::SUCCESS; |
| 1307 | } | 1399 | } |
| 1308 | 1400 | ||
| @@ -1319,6 +1411,24 @@ af::Status RewriteSkInputPaths(PhysicalViewPreparation &preparation, RewrittenGr | |||
| 1319 | return af::SUCCESS; | 1411 | return af::SUCCESS; |
| 1320 | } | 1412 | } |
| 1321 | 1413 | ||
| 1414 | +af::Status RewriteSimdInputLayouts(PhysicalViewPreparation &preparation) { | ||
| 1415 | + for (InputViewPlan *plan : {&preparation.input, &preparation.index}) { | ||
| 1416 | + if (plan->path_broadcast_index == kInvalidBroadcastIndex || plan->load_transpose == nullptr) { | ||
| 1417 | + continue; | ||
| 1418 | + } | ||
| 1419 | + const auto broadcast = plan->path[static_cast<size_t>(plan->path_broadcast_index)]; | ||
| 1420 | + const auto &transpose = plan->load_transpose; | ||
| 1421 | + const auto load = ascgen_utils::indirect_load::GetInputProducer(transpose, 0UL); | ||
| 1422 | + GE_ASSERT_NOTNULL(load); | ||
| 1423 | + const auto owner = transpose->GetOwnerComputeGraph(); | ||
| 1424 | + GE_ASSERT_GRAPH_SUCCESS(af::GraphUtils::IsolateNodeOneIO(transpose)); | ||
| 1425 | + GE_ASSERT_GRAPH_SUCCESS(af::GraphUtils::RemoveNodeWithoutRelink(owner, transpose)); | ||
| 1426 | + // Once the Transpose is removed, the Load and Broadcast share the resolved physical view. | ||
| 1427 | + GE_ASSERT_SUCCESS(ApplyPhysicalView(NodePath{load, broadcast}, plan->layout)); | ||
| 1428 | + } | ||
| 1429 | + return af::SUCCESS; | ||
| 1430 | +} | ||
| 1431 | + | ||
| 1322 | af::Status RewriteBroadcastPaths(const af::AscNodePtr &indirect_load, ascir::TemplateId template_id, | 1432 | af::Status RewriteBroadcastPaths(const af::AscNodePtr &indirect_load, ascir::TemplateId template_id, |
| 1323 | PhysicalViewPreparation &preparation, RewrittenGraphAnalysis &analysis, | 1433 | PhysicalViewPreparation &preparation, RewrittenGraphAnalysis &analysis, |
| 1324 | bool &is_candidate_legal) { | 1434 | bool &is_candidate_legal) { |
| @@ -1328,7 +1438,7 @@ af::Status RewriteBroadcastPaths(const af::AscNodePtr &indirect_load, ascir::Tem | |||
| 1328 | if (template_id == ascir::TemplateId::kIndirectLoadSimt) { | 1438 | if (template_id == ascir::TemplateId::kIndirectLoadSimt) { |
| 1329 | return RewriteSimtInputBroadcast(indirect_load, preparation.input, is_candidate_legal); | 1439 | return RewriteSimtInputBroadcast(indirect_load, preparation.input, is_candidate_legal); |
| 1330 | } | 1440 | } |
| 1331 | - return af::SUCCESS; | 1441 | + return RewriteSimdInputLayouts(preparation); |
| 1332 | } | 1442 | } |
| 1333 | 1443 | ||
| 1334 | af::Status PreparePhysicalViews(const af::AscNodePtr &indirect_load, ascir::TemplateId template_id, | 1444 | af::Status PreparePhysicalViews(const af::AscNodePtr &indirect_load, ascir::TemplateId template_id, |
| @@ -1336,10 +1446,11 @@ af::Status PreparePhysicalViews(const af::AscNodePtr &indirect_load, ascir::Temp | |||
| 1336 | // 输入/输出个数已在 Generate 入口统一校验,axis 由 AnalyzeRewrittenGraph 统一获取,此处不再重复获取。 | 1446 | // 输入/输出个数已在 Generate 入口统一校验,axis 由 AnalyzeRewrittenGraph 统一获取,此处不再重复获取。 |
| 1337 | is_candidate_legal = true; | 1447 | is_candidate_legal = true; |
| 1338 | CollectInputPaths(indirect_load, template_id, preparation); | 1448 | CollectInputPaths(indirect_load, template_id, preparation); |
| 1339 | - InputViewPlan *const plans[] = {&preparation.input, &preparation.index}; | ||
| 1340 | for (size_t input_idx = 0UL; input_idx < kIndirectLoadInputCount; ++input_idx) { | 1449 | for (size_t input_idx = 0UL; input_idx < kIndirectLoadInputCount; ++input_idx) { |
| 1450 | + InputViewPlan &plan = | ||
| 1451 | + input_idx == ascgen_utils::indirect_load::kInputTensorIndex ? preparation.input : preparation.index; | ||
| 1341 | bool is_path_supported = true; | 1452 | bool is_path_supported = true; |
| 1342 | - GE_ASSERT_SUCCESS(AnalyzeInputPath(indirect_load, input_idx, template_id, plans, is_path_supported)); | 1453 | + GE_ASSERT_SUCCESS(AnalyzeInputPath(indirect_load, input_idx, template_id, plan, is_path_supported)); |
| 1343 | if (!is_path_supported) { | 1454 | if (!is_path_supported) { |
| 1344 | is_candidate_legal = false; | 1455 | is_candidate_legal = false; |
| 1345 | const char *const path_name = input_idx == ascgen_utils::indirect_load::kIndexTensorIndex ? "index" : "input"; | 1456 | const char *const path_name = input_idx == ascgen_utils::indirect_load::kIndexTensorIndex ? "index" : "input"; |
| @@ -1450,7 +1561,7 @@ af::Status AnalyzeRewrittenGraph(af::AscGraph &graph, const af::AscNodePtr &indi | |||
| 1450 | 1561 | ||
| 1451 | af::Status AnnotateSimdTemplateRoles(const RewrittenGraphAnalysis &analysis) { | 1562 | af::Status AnnotateSimdTemplateRoles(const RewrittenGraphAnalysis &analysis) { |
| 1452 | for (const af::AscNodePtr &node : analysis.input_region) { | 1563 | for (const af::AscNodePtr &node : analysis.input_region) { |
| 1453 | - if (IsInputRegionBoundary(node)) { | 1564 | + if (IsInputDataSource(node)) { |
| 1454 | continue; | 1565 | continue; |
| 1455 | } | 1566 | } |
| 1456 | const auto role = ascgen_utils::indirect_load::GetTemplateRole(node); | 1567 | const auto role = ascgen_utils::indirect_load::GetTemplateRole(node); |
| @@ -1461,7 +1572,7 @@ af::Status AnnotateSimdTemplateRoles(const RewrittenGraphAnalysis &analysis) { | |||
| 1461 | } | 1572 | } |
| 1462 | if (analysis.simd_index_uses_output_inner_axis) { | 1573 | if (analysis.simd_index_uses_output_inner_axis) { |
| 1463 | for (const af::AscNodePtr &node : analysis.index_region) { | 1574 | for (const af::AscNodePtr &node : analysis.index_region) { |
| 1464 | - if (IsInputRegionBoundary(node)) { | 1575 | + if (IsInputDataSource(node)) { |
| 1465 | continue; | 1576 | continue; |
| 1466 | } | 1577 | } |
| 1467 | GE_ASSERT_SUCCESS( | 1578 | GE_ASSERT_SUCCESS( |
| @@ -1473,10 +1584,13 @@ af::Status AnnotateSimdTemplateRoles(const RewrittenGraphAnalysis &analysis) { | |||
| 1473 | 1584 | ||
| 1474 | af::Status ValidateSimtTemplateRegion(const RewrittenGraphAnalysis &analysis, bool &is_candidate_legal) { | 1585 | af::Status ValidateSimtTemplateRegion(const RewrittenGraphAnalysis &analysis, bool &is_candidate_legal) { |
| 1475 | is_candidate_legal = false; | 1586 | is_candidate_legal = false; |
| 1476 | - if (!af::ops::IsOps<af::ascir_op::Load>(analysis.input_root) || analysis.index_region.empty()) { | 1587 | + if (!af::ops::IsOps<af::ascir_op::Load>(analysis.input_boundary) || analysis.index_region.empty()) { |
| 1477 | return af::SUCCESS; | 1588 | return af::SUCCESS; |
| 1478 | } | 1589 | } |
| 1479 | for (const af::AscNodePtr &node : analysis.index_region) { | 1590 | for (const af::AscNodePtr &node : analysis.index_region) { |
| 1591 | + if (GetLoadTransposeSource(node) != nullptr) { | ||
| 1592 | + continue; | ||
| 1593 | + } | ||
| 1480 | // Compile-time Scalar values are emitted as local constants by the SIMT evaluator. | 1594 | // Compile-time Scalar values are emitted as local constants by the SIMT evaluator. |
| 1481 | // ScalarData is runtime input and still requires an explicit context/GM binding. | 1595 | // ScalarData is runtime input and still requires an explicit context/GM binding. |
| 1482 | if (af::ops::IsOps<af::ascir_op::ScalarData>(node) || HasControlEdge(node)) { | 1596 | if (af::ops::IsOps<af::ascir_op::ScalarData>(node) || HasControlEdge(node)) { |
| @@ -1494,8 +1608,13 @@ af::Status ValidateSimtTemplateRegion(const RewrittenGraphAnalysis &analysis, bo | |||
| 1494 | } | 1608 | } |
| 1495 | 1609 | ||
| 1496 | af::Status AnnotateSimtTemplateRoles(const af::AscNodePtr &indirect_load, const RewrittenGraphAnalysis &analysis) { | 1610 | af::Status AnnotateSimtTemplateRoles(const af::AscNodePtr &indirect_load, const RewrittenGraphAnalysis &analysis) { |
| 1611 | + GE_ASSERT_NOTNULL(analysis.input_boundary, "IndirectLoad SIMT input boundary is missing."); | ||
| 1497 | GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::SetTemplateRole( | 1612 | GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::SetTemplateRole( |
| 1498 | - analysis.input_root, ascgen_utils::indirect_load::TemplateRole::kSimtInputBoundary)); | 1613 | + analysis.input_boundary, ascgen_utils::indirect_load::TemplateRole::kSimtInputBoundary)); |
| 1614 | + if (analysis.input_boundary != analysis.input_root) { | ||
| 1615 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::SetTemplateRole( | ||
| 1616 | + analysis.input_root, ascgen_utils::indirect_load::TemplateRole::kSimtInlineTransform)); | ||
| 1617 | + } | ||
| 1499 | GE_ASSERT_SUCCESS( | 1618 | GE_ASSERT_SUCCESS( |
| 1500 | ascgen_utils::indirect_load::SetTemplateRole(indirect_load, ascgen_utils::indirect_load::TemplateRole::kSimtOp)); | 1619 | ascgen_utils::indirect_load::SetTemplateRole(indirect_load, ascgen_utils::indirect_load::TemplateRole::kSimtOp)); |
| 1501 | for (const af::AscNodePtr &node : analysis.index_region) { | 1620 | for (const af::AscNodePtr &node : analysis.index_region) { |
| @@ -1526,7 +1645,7 @@ af::Status AnnotateSimtMainOutputPath(const af::AscNodePtr &indirect_load, const | |||
| 1526 | if (node == nullptr || node == indirect_load || !visited.emplace(node.get()).second) { | 1645 | if (node == nullptr || node == indirect_load || !visited.emplace(node.get()).second) { |
| 1527 | continue; | 1646 | continue; |
| 1528 | } | 1647 | } |
| 1529 | - if (!IsInputRegionBoundary(node) && !ScheduleUtils::IsReduce(node) && !ScheduleUtils::IsStore(node)) { | 1648 | + if (!IsInputDataSource(node) && !ScheduleUtils::IsReduce(node) && !ScheduleUtils::IsStore(node)) { |
| 1530 | GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::SetTemplateRole( | 1649 | GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::SetTemplateRole( |
| 1531 | node, ascgen_utils::indirect_load::TemplateRole::kSimtInlineTransform)); | 1650 | node, ascgen_utils::indirect_load::TemplateRole::kSimtInlineTransform)); |
| 1532 | } | 1651 | } |
| @@ -1555,7 +1674,7 @@ af::Status AnnotateSimtSideInputClosure(const af::AscNodePtr &branch_node, const | |||
| 1555 | ScheduleUtils::IsReduce(producer) || !visited.emplace(producer.get()).second) { | 1674 | ScheduleUtils::IsReduce(producer) || !visited.emplace(producer.get()).second) { |
| 1556 | continue; | 1675 | continue; |
| 1557 | } | 1676 | } |
| 1558 | - if (IsInputRegionBoundary(producer) && !af::ops::IsOps<af::ascir_op::Load>(producer)) { | 1677 | + if (IsInputDataSource(producer) && !af::ops::IsOps<af::ascir_op::Load>(producer)) { |
| 1559 | continue; | 1678 | continue; |
| 1560 | } | 1679 | } |
| 1561 | const auto role = ascgen_utils::indirect_load::GetTemplateRole(producer); | 1680 | const auto role = ascgen_utils::indirect_load::GetTemplateRole(producer); |
| @@ -1601,7 +1720,7 @@ af::Status AnnotateSimtFanoutBranches(const af::AscNodePtr &indirect_load, const | |||
| 1601 | } | 1720 | } |
| 1602 | const auto visit = [&](const af::AscNodePtr &node, bool &stop) -> af::Status { | 1721 | const auto visit = [&](const af::AscNodePtr &node, bool &stop) -> af::Status { |
| 1603 | const bool is_fanout_branch = | 1722 | const bool is_fanout_branch = |
| 1604 | - selected.count(node.get()) == 0UL && !IsInputRegionBoundary(node) && !ScheduleUtils::IsReduce(node); | 1723 | + selected.count(node.get()) == 0UL && !IsInputDataSource(node) && !ScheduleUtils::IsReduce(node); |
| 1605 | if (is_fanout_branch) { | 1724 | if (is_fanout_branch) { |
| 1606 | GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::SetTemplateRole( | 1725 | GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::SetTemplateRole( |
| 1607 | node, ascgen_utils::indirect_load::TemplateRole::kSimtFanoutBranch)); | 1726 | node, ascgen_utils::indirect_load::TemplateRole::kSimtFanoutBranch)); |
| @@ -1661,8 +1780,20 @@ af::Status FinalizeTemplate(const af::AscNodePtr &indirect_load, ascir::Template | |||
| 1661 | GE_ASSERT_TRUE(false, "IndirectLoad template id %d is invalid.", static_cast<int32_t>(template_id)); | 1780 | GE_ASSERT_TRUE(false, "IndirectLoad template id %d is invalid.", static_cast<int32_t>(template_id)); |
| 1662 | } | 1781 | } |
| 1663 | 1782 | ||
| 1664 | -af::Status ApplySkGraphPass(af::AscGraph &graph, const af::AscNodePtr &indirect_load, | 1783 | +af::Status ApplySkGraphPass(af::AscGraph &graph, const af::AscNodePtr &indirect_load, bool &is_candidate_legal) { |
| 1665 | - const RewrittenGraphAnalysis &analysis, bool &is_candidate_legal) { | 1784 | + is_candidate_legal = false; |
| 1785 | + for (const auto &node : graph.GetAllNodes()) { | ||
| 1786 | + if (af::ops::IsOps<af::ascir_op::Transpose>(node)) { | ||
| 1787 | + GELOGI("[IndirectLoad] Skip SK candidate: Transpose is only supported by SIMD/SIMT."); | ||
| 1788 | + return af::SUCCESS; | ||
| 1789 | + } | ||
| 1790 | + } | ||
| 1791 | + RewrittenGraphAnalysis analysis; | ||
| 1792 | + GE_ASSERT_SUCCESS( | ||
| 1793 | + AnalyzeRewrittenGraph(graph, indirect_load, ascir::TemplateId::kIndirectLoadSK, is_candidate_legal, analysis)); | ||
| 1794 | + if (!is_candidate_legal) { | ||
| 1795 | + return af::SUCCESS; | ||
| 1796 | + } | ||
| 1666 | is_candidate_legal = false; | 1797 | is_candidate_legal = false; |
| 1667 | if (!IsSkTemplateCandidateLegal(indirect_load)) { | 1798 | if (!IsSkTemplateCandidateLegal(indirect_load)) { |
| 1668 | GELOGI("[IndirectLoad] Reject SK candidate for node[%s]: candidate legality check failed.", | 1799 | GELOGI("[IndirectLoad] Reject SK candidate for node[%s]: candidate legality check failed.", |
| @@ -1691,14 +1822,14 @@ af::Status ApplyGraphPass(af::AscGraph &graph, const af::AscNodePtr &indirect_lo | |||
| 1691 | GELOGD("[IndirectLoad] Apply graph pass for node[%s], template_id[%d].", indirect_load->GetNamePtr(), | 1822 | GELOGD("[IndirectLoad] Apply graph pass for node[%s], template_id[%d].", indirect_load->GetNamePtr(), |
| 1692 | static_cast<int32_t>(template_id)); | 1823 | static_cast<int32_t>(template_id)); |
| 1693 | is_candidate_legal = true; | 1824 | is_candidate_legal = true; |
| 1825 | + if (template_id == ascir::TemplateId::kIndirectLoadSK) { | ||
| 1826 | + return ApplySkGraphPass(graph, indirect_load, is_candidate_legal); | ||
| 1827 | + } | ||
| 1694 | RewrittenGraphAnalysis analysis; | 1828 | RewrittenGraphAnalysis analysis; |
| 1695 | GE_ASSERT_SUCCESS(AnalyzeRewrittenGraph(graph, indirect_load, template_id, is_candidate_legal, analysis)); | 1829 | GE_ASSERT_SUCCESS(AnalyzeRewrittenGraph(graph, indirect_load, template_id, is_candidate_legal, analysis)); |
| 1696 | if (!is_candidate_legal) { | 1830 | if (!is_candidate_legal) { |
| 1697 | return af::SUCCESS; | 1831 | return af::SUCCESS; |
| 1698 | } | 1832 | } |
| 1699 | - if (template_id == ascir::TemplateId::kIndirectLoadSK) { | ||
| 1700 | - return ApplySkGraphPass(graph, indirect_load, analysis, is_candidate_legal); | ||
| 1701 | - } | ||
| 1702 | GE_ASSERT_SUCCESS(CompleteInputDataTensorAttrs(analysis)); | 1833 | GE_ASSERT_SUCCESS(CompleteInputDataTensorAttrs(analysis)); |
| 1703 | 1834 | ||
| 1704 | // The SIMT template overwrites this boundary from its post-reduce layout; without a post Reduce the | 1835 | // The SIMT template overwrites this boundary from its post-reduce layout; without a post Reduce the |
| @@ -1716,7 +1847,13 @@ af::Status ApplyGraphPass(af::AscGraph &graph, const af::AscNodePtr &indirect_lo | |||
| 1716 | } | 1847 | } |
| 1717 | GE_ASSERT_SUCCESS(NormalizeTemplateAxes(graph, indirect_load, template_id, analysis, boundary)); | 1848 | GE_ASSERT_SUCCESS(NormalizeTemplateAxes(graph, indirect_load, template_id, analysis, boundary)); |
| 1718 | GE_ASSERT_SUCCESS(CompletePreservedVectorizedViews(graph, indirect_load)); | 1849 | GE_ASSERT_SUCCESS(CompletePreservedVectorizedViews(graph, indirect_load)); |
| 1719 | - return FinalizeTemplate(indirect_load, template_id); | 1850 | + GE_ASSERT_SUCCESS(FinalizeTemplate(indirect_load, template_id)); |
| 1851 | + bool metadata_supported = false; | ||
| 1852 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::FinalizeLoweringMetadata(indirect_load, metadata_supported)); | ||
| 1853 | + if (!metadata_supported) { | ||
| 1854 | + is_candidate_legal = false; | ||
| 1855 | + } | ||
| 1856 | + return af::SUCCESS; | ||
| 1720 | } | 1857 | } |
| 1721 | 1858 | ||
| 1722 | bool IsEmbeddingFastPathCapable(const TemplateCase &template_case, | 1859 | bool IsEmbeddingFastPathCapable(const TemplateCase &template_case, |
| @@ -1803,6 +1940,25 @@ af::Status ReduceTaskGraphCount(std::vector<ascir::ImplGraph> &grouped_graphs, c | |||
| 1803 | GE_ASSERT_NOTNULL(backend_spec); | 1940 | GE_ASSERT_NOTNULL(backend_spec); |
| 1804 | return ScheduleGroupGraphPartitioner::ReduceGraphCount(grouped_graphs, backend_spec->max_group_num_per_compile_unit); | 1941 | return ScheduleGroupGraphPartitioner::ReduceGraphCount(grouped_graphs, backend_spec->max_group_num_per_compile_unit); |
| 1805 | } | 1942 | } |
| 1943 | + | ||
| 1944 | +af::Status FinalizeGroupedGraphLoweringMetadata(std::vector<ascir::ImplGraph> &grouped_graphs) { | ||
| 1945 | + for (auto &graph : grouped_graphs) { | ||
| 1946 | + af::AscNodePtr indirect_load; | ||
| 1947 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::ValidateSingleIndirectLoadNode(graph, indirect_load)); | ||
| 1948 | + if (indirect_load == nullptr) { | ||
| 1949 | + continue; | ||
| 1950 | + } | ||
| 1951 | + const auto template_id = ascir::GetTemplateIdOrDefault(*indirect_load); | ||
| 1952 | + if (template_id != ascir::TemplateId::kIndirectLoadSimd && template_id != ascir::TemplateId::kIndirectLoadSimt) { | ||
| 1953 | + continue; | ||
| 1954 | + } | ||
| 1955 | + bool metadata_supported = false; | ||
| 1956 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::FinalizeLoweringMetadata(indirect_load, metadata_supported)); | ||
| 1957 | + GE_ASSERT_TRUE(metadata_supported, "IndirectLoad lowering metadata is unsupported in grouped graph[%s], node[%s].", | ||
| 1958 | + graph.GetName().c_str(), indirect_load->GetNamePtr()); | ||
| 1959 | + } | ||
| 1960 | + return af::SUCCESS; | ||
| 1961 | +} | ||
| 1806 | } // namespace | 1962 | } // namespace |
| 1807 | 1963 | ||
| 1808 | Status IndirectLoadScheduleCaseGenerator::Generate(ascir::HintGraph &graph, std::vector<ascir::ImplGraph> &graphs, | 1964 | Status IndirectLoadScheduleCaseGenerator::Generate(ascir::HintGraph &graph, std::vector<ascir::ImplGraph> &graphs, |
| @@ -1867,6 +2023,7 @@ Status IndirectLoadScheduleCaseGenerator::GeneratorTask(ascir::HintGraph &optimi | |||
| 1867 | "Failed to partition graph"); | 2023 | "Failed to partition graph"); |
| 1868 | GE_ASSERT_SUCCESS(RefreshTaskAxisSizes(task.grouped_graphs, need_update_axis, is_sk_template)); | 2024 | GE_ASSERT_SUCCESS(RefreshTaskAxisSizes(task.grouped_graphs, need_update_axis, is_sk_template)); |
| 1869 | GE_CHK_STATUS_RET(ReduceTaskGraphCount(task.grouped_graphs, options), "Failed to reduce graph count"); | 2025 | GE_CHK_STATUS_RET(ReduceTaskGraphCount(task.grouped_graphs, options), "Failed to reduce graph count"); |
| 2026 | + GE_ASSERT_SUCCESS(FinalizeGroupedGraphLoweringMetadata(task.grouped_graphs)); | ||
| 1870 | tasks.emplace_back(std::move(task)); | 2027 | tasks.emplace_back(std::move(task)); |
| 1871 | } | 2028 | } |
| 1872 | return af::SUCCESS; | 2029 | return af::SUCCESS; |
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "ascir_utils.h" | 17 | #include "ascir_utils.h" |
| 18 | #include "ascir_ops_utils.h" | 18 | #include "ascir_ops_utils.h" |
| 19 | #include "ascir_ops.h" | 19 | #include "ascir_ops.h" |
| 20 | +#include "indirect_load_utils.h" | ||
| 20 | #include "schedule_utils.h" | 21 | #include "schedule_utils.h" |
| 21 | #include "graph_properties_cache.h" | 22 | #include "graph_properties_cache.h" |
| 22 | 23 | ||
| @@ -97,6 +98,12 @@ Status TransposeFusionCaseGenerator::TransposeConvertProcess(ascir::HintGraph &g | |||
| 97 | 98 | ||
| 98 | Status TransposeFusionCaseGenerator::Generate(ascir::HintGraph &graph, std::vector<ascir::ImplGraph> &graphs, | 99 | Status TransposeFusionCaseGenerator::Generate(ascir::HintGraph &graph, std::vector<ascir::ImplGraph> &graphs, |
| 99 | std::vector<std::string> &score_functions) { | 100 | std::vector<std::string> &score_functions) { |
| 101 | + const auto indirect_load = ascgen_utils::indirect_load::FindIndirectLoadNode(graph); | ||
| 102 | + if (indirect_load != nullptr) { | ||
| 103 | + GELOGI("Graph %s has indirect load, skip transpose task generation; handled by IndirectLoad tasks.", | ||
| 104 | + graph.GetName().c_str()); | ||
| 105 | + return af::SUCCESS; | ||
| 106 | + } | ||
| 100 | /* | 107 | /* |
| 101 | 单个Transpose场景: | 108 | 单个Transpose场景: |
| 102 | 场景1: 尾轴转置, 需要UB重排,Transpose节点保留; | 109 | 场景1: 尾轴转置, 需要UB重排,Transpose节点保留; |
| @@ -16,6 +16,8 @@ | |||
| 16 | #include "codegen_kernel.h" | 16 | #include "codegen_kernel.h" |
| 17 | #include "common_utils.h" | 17 | #include "common_utils.h" |
| 18 | #include "graph/ascendc_ir/utils/asc_tensor_utils.h" | 18 | #include "graph/ascendc_ir/utils/asc_tensor_utils.h" |
| 19 | +#include "graph/ascendc_ir/utils/asc_graph_utils.h" | ||
| 20 | +#include "graph/utils/graph_utils.h" | ||
| 19 | #include "indirect_load_utils.h" | 21 | #include "indirect_load_utils.h" |
| 20 | #include "platform_context.h" | 22 | #include "platform_context.h" |
| 21 | #include "runtime_stub.h" | 23 | #include "runtime_stub.h" |
| @@ -1013,7 +1015,9 @@ TEST(IndirectLoadApiCallTest, GenerateSimdDenseSelectsDenseApi) { | |||
| 1013 | 1015 | ||
| 1014 | EXPECT_NE(result.find("IndirectLoadSimd<half, int32_t, 2, 1>"), std::string::npos); | 1016 | EXPECT_NE(result.find("IndirectLoadSimd<half, int32_t, 2, 1>"), std::string::npos); |
| 1015 | EXPECT_EQ(result.find("IndirectLoadSimdStrided"), std::string::npos); | 1017 | EXPECT_EQ(result.find("IndirectLoadSimdStrided"), std::string::npos); |
| 1016 | - EXPECT_EQ(result.find("indirect_load_simd_params"), std::string::npos); | 1018 | + EXPECT_NE(result.find("IndirectLoadSimdFallback>(0), false>"), std::string::npos); |
| 1019 | + EXPECT_NE(result.find("IndirectLoadSimdParams<IndirectLoadSimdCase, 2> indirect_load_simd_params"), | ||
| 1020 | + std::string::npos); | ||
| 1017 | } | 1021 | } |
| 1018 | 1022 | ||
| 1019 | TEST(IndirectLoadApiCallTest, GenerateSimdDenseGatherSelectsGatherApi) { | 1023 | TEST(IndirectLoadApiCallTest, GenerateSimdDenseGatherSelectsGatherApi) { |
| @@ -1089,7 +1093,7 @@ TEST(IndirectLoadApiCallTest, GenerateFuncDefinitionSimtProducesBodyStruct) { | |||
| 1089 | EXPECT_NE(def.find("using Context = IndirectLoadSimtContext_"), std::string::npos); | 1093 | EXPECT_NE(def.find("using Context = IndirectLoadSimtContext_"), std::string::npos); |
| 1090 | EXPECT_NE(def.find("__simt_callee__ __aicore__ inline static"), std::string::npos); | 1094 | EXPECT_NE(def.find("__simt_callee__ __aicore__ inline static"), std::string::npos); |
| 1091 | EXPECT_NE(def.find(" Index(uint64_t output_index, const Context &context)"), std::string::npos); | 1095 | EXPECT_NE(def.find(" Index(uint64_t output_index, const Context &context)"), std::string::npos); |
| 1092 | - EXPECT_NE(def.find(" Output("), std::string::npos); | 1096 | + EXPECT_NE(def.find(" Outputs("), std::string::npos); |
| 1093 | } | 1097 | } |
| 1094 | 1098 | ||
| 1095 | TEST(IndirectLoadApiCallTest, GenerateFuncDefinitionSimtEmitsMultiOutputEvaluator) { | 1099 | TEST(IndirectLoadApiCallTest, GenerateFuncDefinitionSimtEmitsMultiOutputEvaluator) { |
| @@ -1117,6 +1121,79 @@ TEST(IndirectLoadApiCallTest, GenerateFuncDefinitionUsesUint32OffsetsForStaticSh | |||
| 1117 | EXPECT_NE(def.find("uint32_t output_index"), std::string::npos); | 1121 | EXPECT_NE(def.find("uint32_t output_index"), std::string::npos); |
| 1118 | } | 1122 | } |
| 1119 | 1123 | ||
| 1124 | +void InsertSimtIdentityBroadcast(ILTestGraph &g, const char *name, const char *producer_name, const char *consumer_name, | ||
| 1125 | + int32_t input_index, ascir::TensorId tensor_id) { | ||
| 1126 | + const auto producer = g.graph.FindNode(producer_name); | ||
| 1127 | + const auto consumer = g.graph.FindNode(consumer_name); | ||
| 1128 | + ASSERT_NE(producer, nullptr); | ||
| 1129 | + ASSERT_NE(consumer, nullptr); | ||
| 1130 | + Broadcast broadcast(name); | ||
| 1131 | + g.graph.AddNode(broadcast); | ||
| 1132 | + const auto node = g.graph.FindNode(name); | ||
| 1133 | + ASSERT_NE(node, nullptr); | ||
| 1134 | + node->attr.sched.axis = producer->attr.sched.axis; | ||
| 1135 | + node->attr.api.compute_type = af::ComputeType::kComputeBroadcast; | ||
| 1136 | + node->outputs[0].attr.dtype = producer->outputs[0].attr.dtype; | ||
| 1137 | + node->outputs[0].attr.axis = producer->outputs[0].attr.axis; | ||
| 1138 | + node->outputs[0].attr.repeats = producer->outputs[0].attr.repeats; | ||
| 1139 | + node->outputs[0].attr.strides = producer->outputs[0].attr.strides; | ||
| 1140 | + node->outputs[0].attr.mem.tensor_id = tensor_id; | ||
| 1141 | + ASSERT_EQ(SetTemplateRole(node, TemplateRole::kSimtInlineTransform), af::SUCCESS); | ||
| 1142 | + ASSERT_EQ(af::GraphUtils::ReplaceEdgeSrc(producer->GetOutDataAnchor(0), consumer->GetInDataAnchor(input_index), | ||
| 1143 | + node->GetOutDataAnchor(0)), | ||
| 1144 | + af::SUCCESS); | ||
| 1145 | + ASSERT_EQ(af::GraphUtils::AddEdge(producer->GetOutDataAnchor(0), node->GetInDataAnchor(0)), af::SUCCESS); | ||
| 1146 | + ASSERT_EQ(af::AscGraphUtils::GetComputeGraph(g.graph)->TopologicalSorting(), af::SUCCESS); | ||
| 1147 | +} | ||
| 1148 | + | ||
| 1149 | +TEST_F(IndirectLoadConstructFromNodesTest, OmitsIdentityScalarTemporaries) { | ||
| 1150 | + ILTestGraph g("simt_identity_chain", 2, 8, 2, 8); | ||
| 1151 | + BuildSimtGraph(g); | ||
| 1152 | + ASSERT_NO_FATAL_FAILURE(InsertSimtIdentityBroadcast(g, "identity0", "indirect_load", "store", 0, 20)); | ||
| 1153 | + ASSERT_NO_FATAL_FAILURE(InsertSimtIdentityBroadcast(g, "identity1", "identity0", "store", 0, 21)); | ||
| 1154 | + AnnotateSimtTemplate(g.graph); | ||
| 1155 | + std::string definition; | ||
| 1156 | + GenerateSimtFuncDefinitionFromGraph(g, definition); | ||
| 1157 | + EXPECT_EQ(definition.find("v_20 ="), std::string::npos); | ||
| 1158 | + EXPECT_EQ(definition.find("v_21 ="), std::string::npos); | ||
| 1159 | + EXPECT_NE(definition.find("outputs.output0 = value;"), std::string::npos); | ||
| 1160 | +} | ||
| 1161 | + | ||
| 1162 | +TEST_F(IndirectLoadConstructFromNodesTest, KeepsGmLoadMaterializedThroughIdentityBroadcast) { | ||
| 1163 | + ILTestGraph g("simt_identity_gm_load"); | ||
| 1164 | + BuildSimtPostReduceGraph(g); | ||
| 1165 | + ASSERT_NO_FATAL_FAILURE(InsertSimtIdentityBroadcast(g, "load_identity", "addend_load", "output_transform", 1, 50)); | ||
| 1166 | + std::string definition; | ||
| 1167 | + GenerateSimtPostReduceFuncDefinitionFromGraph(g, definition); | ||
| 1168 | + EXPECT_NE(definition.find("half v_50 = context.gm_9["), std::string::npos); | ||
| 1169 | + const auto load = definition.find("context.gm_9["); | ||
| 1170 | + ASSERT_NE(load, std::string::npos); | ||
| 1171 | + EXPECT_EQ(definition.find("context.gm_9[", load + 1), std::string::npos); | ||
| 1172 | +} | ||
| 1173 | + | ||
| 1174 | +TEST_F(IndirectLoadConstructFromNodesTest, InlinesCompileTimeScalarWithoutTemporary) { | ||
| 1175 | + ILTestGraph g("simt_inline_scalar"); | ||
| 1176 | + BuildSimtPostReduceGraph(g); | ||
| 1177 | + Scalar scalar("constant_bias", g.graph); | ||
| 1178 | + scalar.ir_attr.SetValue("2.5"); | ||
| 1179 | + scalar.y.dtype = af::DT_FLOAT16; | ||
| 1180 | + const auto scalar_node = g.graph.FindNode("constant_bias"); | ||
| 1181 | + const auto load = g.graph.FindNode("addend_load"); | ||
| 1182 | + const auto add = g.graph.FindNode("output_transform"); | ||
| 1183 | + ASSERT_NE(scalar_node, nullptr); | ||
| 1184 | + ASSERT_NE(load, nullptr); | ||
| 1185 | + ASSERT_NE(add, nullptr); | ||
| 1186 | + scalar_node->outputs[0].attr.mem.tensor_id = 40; | ||
| 1187 | + ASSERT_EQ(af::GraphUtils::ReplaceEdgeSrc(load->GetOutDataAnchor(0), add->GetInDataAnchor(1), | ||
| 1188 | + scalar_node->GetOutDataAnchor(0)), | ||
| 1189 | + af::SUCCESS); | ||
| 1190 | + ASSERT_EQ(af::AscGraphUtils::GetComputeGraph(g.graph)->TopologicalSorting(), af::SUCCESS); | ||
| 1191 | + std::string definition; | ||
| 1192 | + GenerateSimtPostReduceFuncDefinitionFromGraph(g, definition); | ||
| 1193 | + EXPECT_EQ(definition.find("v_40 ="), std::string::npos); | ||
| 1194 | + EXPECT_NE(definition.find("static_cast<half>(2.5"), std::string::npos); | ||
| 1195 | +} | ||
| 1196 | + | ||
| 1120 | TEST(IndirectLoadApiCallTest, GenerateSimtOutputUsesIndexOffsetForMatchingLoadView) { | 1197 | TEST(IndirectLoadApiCallTest, GenerateSimtOutputUsesIndexOffsetForMatchingLoadView) { |
| 1121 | ILTestGraph g("simt_post_offsets"); | 1198 | ILTestGraph g("simt_post_offsets"); |
| 1122 | std::string definition; | 1199 | std::string definition; |
| @@ -1139,6 +1216,101 @@ TEST(IndirectLoadApiCallTest, GenerateSimtOutputUsesOutputOffsetForDistinctLoadV | |||
| 1139 | EXPECT_NE(definition.find("context.gm_9[output_index]"), std::string::npos); | 1216 | EXPECT_NE(definition.find("context.gm_9[output_index]"), std::string::npos); |
| 1140 | } | 1217 | } |
| 1141 | 1218 | ||
| 1219 | +TEST(IndirectLoadApiCallTest, GenerateSimtOutputMapsBroadcastLoadToColumn) { | ||
| 1220 | + ILTestGraph g("simt_post_broadcast_offset", 2, 8, 2, 8); | ||
| 1221 | + BuildSimtPostReduceGraph(g); | ||
| 1222 | + // Reuse the side-input fixture with a direct Store, matching the position-bias output chain. | ||
| 1223 | + const auto reduce = g.graph.FindNode("reduce"); | ||
| 1224 | + ASSERT_NE(reduce, nullptr); | ||
| 1225 | + ASSERT_EQ(af::GraphUtils::IsolateNodeOneIO(reduce), af::SUCCESS); | ||
| 1226 | + ASSERT_EQ(af::GraphUtils::RemoveNodeWithoutRelink(af::AscGraphUtils::GetComputeGraph(g.graph), reduce), af::SUCCESS); | ||
| 1227 | + const auto addend_load = g.graph.FindNode("addend_load"); | ||
| 1228 | + ASSERT_NE(addend_load, nullptr); | ||
| 1229 | + addend_load->outputs[0].attr.repeats = {af::sym::kSymbolOne, g.s3}; | ||
| 1230 | + addend_load->outputs[0].attr.strides = {af::sym::kSymbolZero, af::sym::kSymbolOne}; | ||
| 1231 | + | ||
| 1232 | + std::string definition; | ||
| 1233 | + GenerateSimtPostReduceFuncDefinitionFromGraph(g, definition); | ||
| 1234 | + EXPECT_NE(definition.find("context.gm_9[index_coord_0]"), std::string::npos); | ||
| 1235 | + EXPECT_NE(definition.find("index_coord_0 = static_cast<uint32_t>(output_index % 8);"), std::string::npos); | ||
| 1236 | + EXPECT_EQ(definition.find("context.gm_9[output_index]"), std::string::npos); | ||
| 1237 | + EXPECT_EQ(definition.find("context.gm_9[index_offset]"), std::string::npos); | ||
| 1238 | +} | ||
| 1239 | + | ||
| 1240 | +void GenerateSimtBroadcastPairDefinition(int32_t second_extent, std::string &definition) { | ||
| 1241 | + SimtCodegenContext context; | ||
| 1242 | + ILTestGraph g("simt_broadcast_pair", 2, 8, 2, 8); | ||
| 1243 | + BuildSimtPostReduceGraph(g); | ||
| 1244 | + const auto reduce = g.graph.FindNode("reduce"); | ||
| 1245 | + ASSERT_NE(reduce, nullptr); | ||
| 1246 | + ASSERT_EQ(af::GraphUtils::IsolateNodeOneIO(reduce), af::SUCCESS); | ||
| 1247 | + ASSERT_EQ(af::GraphUtils::RemoveNodeWithoutRelink(af::AscGraphUtils::GetComputeGraph(g.graph), reduce), af::SUCCESS); | ||
| 1248 | + const auto first_load = g.graph.FindNode("addend_load"); | ||
| 1249 | + ASSERT_NE(first_load, nullptr); | ||
| 1250 | + first_load->outputs[0].attr.repeats = {af::ops::One, g.s3}; | ||
| 1251 | + first_load->outputs[0].attr.strides = {af::ops::Zero, af::ops::One}; | ||
| 1252 | + | ||
| 1253 | + Data extra("extra", g.graph); | ||
| 1254 | + extra.ir_attr.SetIndex(3); | ||
| 1255 | + extra.y.dtype = af::DT_FLOAT16; | ||
| 1256 | + g.graph.FindNode("extra")->outputs[0].attr.mem.tensor_id = 41; | ||
| 1257 | + Load second_load("second_bias_load"); | ||
| 1258 | + g.graph.AddNode(second_load); | ||
| 1259 | + second_load.x = extra.y; | ||
| 1260 | + second_load.y.dtype = af::DT_FLOAT16; | ||
| 1261 | + second_load.attr.sched.axis = {g.z2.id, g.z3.id}; | ||
| 1262 | + *second_load.y.axis = {g.z2.id, g.z3.id}; | ||
| 1263 | + *second_load.y.repeats = {af::ops::One, af::Symbol(second_extent)}; | ||
| 1264 | + *second_load.y.strides = {af::ops::Zero, af::ops::One}; | ||
| 1265 | + const auto second_node = g.graph.FindNode("second_bias_load"); | ||
| 1266 | + ASSERT_NE(second_node, nullptr); | ||
| 1267 | + second_node->outputs[0].attr.mem.tensor_id = 51; | ||
| 1268 | + ASSERT_EQ(SetTemplateRole(second_node, TemplateRole::kSimtDirectGmBoundary), af::SUCCESS); | ||
| 1269 | + | ||
| 1270 | + Add add("add_second_bias"); | ||
| 1271 | + g.graph.AddNode(add); | ||
| 1272 | + const auto add_node = g.graph.FindNode("add_second_bias"); | ||
| 1273 | + const auto first_add = g.graph.FindNode("output_transform"); | ||
| 1274 | + const auto store = g.graph.FindNode("store"); | ||
| 1275 | + ASSERT_NE(add_node, nullptr); | ||
| 1276 | + ASSERT_NE(first_add, nullptr); | ||
| 1277 | + ASSERT_NE(store, nullptr); | ||
| 1278 | + add.y.dtype = af::DT_FLOAT16; | ||
| 1279 | + add.attr.sched.axis = first_add->attr.sched.axis; | ||
| 1280 | + *add.y.axis = first_add->outputs[0].attr.axis; | ||
| 1281 | + *add.y.repeats = first_add->outputs[0].attr.repeats; | ||
| 1282 | + *add.y.strides = first_add->outputs[0].attr.strides; | ||
| 1283 | + add_node->outputs[0].attr.mem.tensor_id = 52; | ||
| 1284 | + ASSERT_EQ(SetTemplateRole(add_node, TemplateRole::kSimtInlineTransform), af::SUCCESS); | ||
| 1285 | + ASSERT_EQ(af::GraphUtils::ReplaceEdgeSrc(first_add->GetOutDataAnchor(0), store->GetInDataAnchor(0), | ||
| 1286 | + add_node->GetOutDataAnchor(0)), | ||
| 1287 | + af::SUCCESS); | ||
| 1288 | + ASSERT_EQ(af::GraphUtils::AddEdge(first_add->GetOutDataAnchor(0), add_node->GetInDataAnchor(0)), af::SUCCESS); | ||
| 1289 | + ASSERT_EQ(af::GraphUtils::AddEdge(second_node->GetOutDataAnchor(0), add_node->GetInDataAnchor(1)), af::SUCCESS); | ||
| 1290 | + ASSERT_EQ(af::AscGraphUtils::GetComputeGraph(g.graph)->TopologicalSorting(), af::SUCCESS); | ||
| 1291 | + GenerateSimtPostReduceFuncDefinitionFromGraph(g, definition); | ||
| 1292 | +} | ||
| 1293 | + | ||
| 1294 | +TEST(IndirectLoadApiCallTest, ReusesBroadcastCoordinatesAcrossLoads) { | ||
| 1295 | + std::string definition; | ||
| 1296 | + GenerateSimtBroadcastPairDefinition(8, definition); | ||
| 1297 | + EXPECT_NE(definition.find("context.gm_9[index_coord_0]"), std::string::npos); | ||
| 1298 | + EXPECT_NE(definition.find("context.gm_51[index_coord_0]"), std::string::npos); | ||
| 1299 | + const auto coordinate = definition.find("output_index % 8"); | ||
| 1300 | + ASSERT_NE(coordinate, std::string::npos); | ||
| 1301 | + EXPECT_EQ(definition.find("output_index % 8", coordinate + 1), std::string::npos); | ||
| 1302 | + EXPECT_EQ(definition.find("index_coord_1"), std::string::npos); | ||
| 1303 | +} | ||
| 1304 | + | ||
| 1305 | +TEST(IndirectLoadApiCallTest, KeepsDistinctPhysicalExtentsInBroadcastCoordinates) { | ||
| 1306 | + std::string definition; | ||
| 1307 | + GenerateSimtBroadcastPairDefinition(4, definition); | ||
| 1308 | + EXPECT_NE(definition.find("output_index % 8"), std::string::npos); | ||
| 1309 | + EXPECT_NE(definition.find("output_index % 4"), std::string::npos); | ||
| 1310 | + EXPECT_NE(definition.find("context.gm_9[index_coord_0]"), std::string::npos); | ||
| 1311 | + EXPECT_NE(definition.find("context.gm_51[index_coord_1]"), std::string::npos); | ||
| 1312 | +} | ||
| 1313 | + | ||
| 1142 | TEST(IndirectLoadApiCallTest, GenerateSimtOutputUsesZeroOffsetForScalarLoadView) { | 1314 | TEST(IndirectLoadApiCallTest, GenerateSimtOutputUsesZeroOffsetForScalarLoadView) { |
| 1143 | ILTestGraph g("simt_post_zero_offset"); | 1315 | ILTestGraph g("simt_post_zero_offset"); |
| 1144 | BuildSimtPostReduceGraph(g); | 1316 | BuildSimtPostReduceGraph(g); |
| @@ -1263,7 +1435,7 @@ TEST_F(IndirectLoadConstructFromNodesTest, ConnectsRedirectedSimtPostReduceWithP | |||
| 1263 | 1435 | ||
| 1264 | std::string generated; | 1436 | std::string generated; |
| 1265 | ASSERT_EQ(kernel.root_loop.Generate(kernel.tiler, kernel.tpipe, generated), af::SUCCESS); | 1437 | ASSERT_EQ(kernel.root_loop.Generate(kernel.tiler, kernel.tpipe, generated), af::SUCCESS); |
| 1266 | - EXPECT_NE(generated.find("IndirectLoadSimtMulti"), std::string::npos); | 1438 | + EXPECT_NE(generated.find("AscendC::IndirectLoadSimt<"), std::string::npos); |
| 1267 | EXPECT_NE(generated.find("global_10.GetPhyAddr()"), std::string::npos); | 1439 | EXPECT_NE(generated.find("global_10.GetPhyAddr()"), std::string::npos); |
| 1268 | } | 1440 | } |
| 1269 | 1441 | ||
| @@ -1319,7 +1491,7 @@ TEST_F(IndirectLoadConstructFromNodesTest, GeneratesSimtMultiOutputCall) { | |||
| 1319 | Kernel kernel("simt_multi_output_call"); | 1491 | Kernel kernel("simt_multi_output_call"); |
| 1320 | std::string generated; | 1492 | std::string generated; |
| 1321 | ASSERT_EQ(ParseGraphAndGenerateLoop(g, {"x", "idx"}, kernel, generated, {"y", "second_y"}), af::SUCCESS); | 1493 | ASSERT_EQ(ParseGraphAndGenerateLoop(g, {"x", "idx"}, kernel, generated, {"y", "second_y"}), af::SUCCESS); |
| 1322 | - EXPECT_NE(generated.find("IndirectLoadSimtMulti"), std::string::npos); | 1494 | + EXPECT_NE(generated.find("AscendC::IndirectLoadSimt<"), std::string::npos); |
| 1323 | EXPECT_NE(generated.find("OutputTargets"), std::string::npos); | 1495 | EXPECT_NE(generated.find("OutputTargets"), std::string::npos); |
| 1324 | EXPECT_NE(generated.find("global_2.GetPhyAddr()"), std::string::npos); | 1496 | EXPECT_NE(generated.find("global_2.GetPhyAddr()"), std::string::npos); |
| 1325 | EXPECT_NE(generated.find("global_10.GetPhyAddr()"), std::string::npos); | 1497 | EXPECT_NE(generated.find("global_10.GetPhyAddr()"), std::string::npos); |
| @@ -1336,7 +1508,8 @@ TEST_F(IndirectLoadConstructFromNodesTest, GeneratesStaticPowerOfTwoPolicyWithUi | |||
| 1336 | Kernel kernel("simt_static_power_of_two"); | 1508 | Kernel kernel("simt_static_power_of_two"); |
| 1337 | std::string generated; | 1509 | std::string generated; |
| 1338 | ASSERT_EQ(ParseGraphAndGenerateLoop(g, {"x", "idx"}, kernel, generated), af::SUCCESS); | 1510 | ASSERT_EQ(ParseGraphAndGenerateLoop(g, {"x", "idx"}, kernel, generated), af::SUCCESS); |
| 1339 | - EXPECT_NE(generated.find("IndirectLoadSimtStaticPowerOfTwoPolicy<uint32_t, 1ULL, 8ULL, 1ULL, 8ULL>"), | 1511 | + EXPECT_NE(generated.find("IndirectLoadSimtCase>(0), uint32_t, 2, 1, 1ULL, 8ULL, 1ULL, 8ULL"), std::string::npos); |
| 1512 | + EXPECT_NE(generated.find("IndirectLoadSimtParams<IndirectLoadSimtCase> indirect_load_simt_params{}"), | ||
| 1340 | std::string::npos); | 1513 | std::string::npos); |
| 1341 | } | 1514 | } |
| 1342 | 1515 | ||
| @@ -1351,8 +1524,8 @@ TEST_F(IndirectLoadConstructFromNodesTest, GeneratesStaticInnerPolicyForLastAxis | |||
| 1351 | Kernel kernel("simt_static_inner"); | 1524 | Kernel kernel("simt_static_inner"); |
| 1352 | std::string generated; | 1525 | std::string generated; |
| 1353 | ASSERT_EQ(ParseGraphAndGenerateLoop(g, {"x", "idx"}, kernel, generated), af::SUCCESS); | 1526 | ASSERT_EQ(ParseGraphAndGenerateLoop(g, {"x", "idx"}, kernel, generated), af::SUCCESS); |
| 1354 | - EXPECT_NE(generated.find("IndirectLoadSimtStaticInnerPolicy<uint32_t, 1ULL, 1ULL, 7ULL>"), std::string::npos); | 1527 | + EXPECT_NE(generated.find("IndirectLoadSimtCase>(1), uint32_t, 2, 1, 1ULL, 7ULL, 1ULL, 7ULL"), std::string::npos); |
| 1355 | - EXPECT_NE(generated.find(", 7);"), std::string::npos); | 1528 | + EXPECT_NE(generated.find("indirect_load_simt_params{static_cast<uint32_t>(7)}"), std::string::npos); |
| 1356 | } | 1529 | } |
| 1357 | 1530 | ||
| 1358 | TEST_F(IndirectLoadConstructFromNodesTest, GeneratesSimtPostReduceCallWithUbOutput) { | 1531 | TEST_F(IndirectLoadConstructFromNodesTest, GeneratesSimtPostReduceCallWithUbOutput) { |
| @@ -309,7 +309,7 @@ void BuildBroadcastInputPreChain(const af::AscOpOutput &broadcast_output, const | |||
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | af::AscGraph BuildIndirectLoadBroadcastGraph(bool with_input_element = true, bool with_index_broadcast = false, | 311 | af::AscGraph BuildIndirectLoadBroadcastGraph(bool with_input_element = true, bool with_index_broadcast = false, |
| 312 | - int64_t axis = 1L) { | 312 | + int64_t axis = 1L, bool transpose_input = false) { |
| 313 | af::AscGraph graph("indirect_load_broadcast_ut_graph"); | 313 | af::AscGraph graph("indirect_load_broadcast_ut_graph"); |
| 314 | const BroadcastGraphView view = CreateBroadcastGraphView(graph); | 314 | const BroadcastGraphView view = CreateBroadcastGraphView(graph); |
| 315 | const std::vector<af::Expression> source_strides = {view.source_repeats[2], view.source_repeats[2], af::ops::One}; | 315 | const std::vector<af::Expression> source_strides = {view.source_repeats[2], view.source_repeats[2], af::ops::One}; |
| @@ -325,9 +325,20 @@ af::AscGraph BuildIndirectLoadBroadcastGraph(bool with_input_element = true, boo | |||
| 325 | af::ascir_op::Load input_load("broadcast_input_load"); | 325 | af::ascir_op::Load input_load("broadcast_input_load"); |
| 326 | input_load.x = x.y; | 326 | input_load.x = x.y; |
| 327 | SetNodeView(input_load, af::DT_FLOAT16, view.source_axes, view.source_repeats, source_strides); | 327 | SetNodeView(input_load, af::DT_FLOAT16, view.source_axes, view.source_repeats, source_strides); |
| 328 | + af::ascir_op::Transpose transpose("broadcast_input_transpose"); | ||
| 329 | + if (transpose_input) { | ||
| 330 | + auto physical_axes = view.source_axes; | ||
| 331 | + auto physical_repeats = view.source_repeats; | ||
| 332 | + std::swap(physical_axes[0], physical_axes[2]); | ||
| 333 | + std::swap(physical_repeats[0], physical_repeats[2]); | ||
| 334 | + SetNodeView(input_load, af::DT_FLOAT16, physical_axes, physical_repeats, | ||
| 335 | + {af::Symbol(2), af::Symbol(2), af::ops::One}); | ||
| 336 | + transpose.x = input_load.y; | ||
| 337 | + SetNodeView(transpose, af::DT_FLOAT16, view.source_axes, view.source_repeats, source_strides); | ||
| 338 | + } | ||
| 328 | af::ascir_op::Broadcast broadcast("input_broadcast"); | 339 | af::ascir_op::Broadcast broadcast("input_broadcast"); |
| 329 | broadcast.attr.api.compute_type = af::ComputeType::kComputeBroadcast; | 340 | broadcast.attr.api.compute_type = af::ComputeType::kComputeBroadcast; |
| 330 | - broadcast.x = input_load.y; | 341 | + broadcast.x = transpose_input ? transpose.y : input_load.y; |
| 331 | SetNodeView(broadcast, af::DT_FLOAT16, view.input_axes, view.input_repeats, input_strides); | 342 | SetNodeView(broadcast, af::DT_FLOAT16, view.input_axes, view.input_repeats, input_strides); |
| 332 | auto index_source_repeats = view.output_repeats; | 343 | auto index_source_repeats = view.output_repeats; |
| 333 | auto index_source_strides = index_strides; | 344 | auto index_source_strides = index_strides; |
| @@ -1139,6 +1150,91 @@ TEST(IndirectLoadScheduleCaseGeneratorTest, SimtRewritesInputBroadcastAndSetsTem | |||
| 1139 | EXPECT_TRUE(indirect_load->outputs()[0]->attr.vectorized_strides.empty()); | 1150 | EXPECT_TRUE(indirect_load->outputs()[0]->attr.vectorized_strides.empty()); |
| 1140 | } | 1151 | } |
| 1141 | 1152 | ||
| 1153 | +TEST(IndirectLoadScheduleCaseGeneratorTest, BroadcastAfterTransposePreservesSourcePhysicalView) { | ||
| 1154 | + PlatformContextReset platform_reset; | ||
| 1155 | + ge::PlatformContext::GetInstance().Reset(); | ||
| 1156 | + ge::PlatformInfo platform_info; | ||
| 1157 | + platform_info.soc_ver = "3510"; | ||
| 1158 | + platform_info.ub_size = 256 * 1024; | ||
| 1159 | + platform_info.aiv_num = 48; | ||
| 1160 | + ge::PlatformContext::GetInstance().SetPlatformInfo(platform_info); | ||
| 1161 | + auto graph = BuildIndirectLoadBroadcastGraph(false, false, 2L, true); | ||
| 1162 | + const auto source = graph.FindNode("broadcast_input_load")->outputs[0].attr; | ||
| 1163 | + optimize::IndirectLoadScheduleCaseGenerator generator; | ||
| 1164 | + std::vector<af::AscGraph> graphs; | ||
| 1165 | + std::vector<std::string> score_functions; | ||
| 1166 | + ASSERT_EQ(generator.Generate(graph, graphs, score_functions), af::SUCCESS); | ||
| 1167 | + ASSERT_EQ(graphs.size(), 3UL); | ||
| 1168 | + for (auto &candidate : graphs) { | ||
| 1169 | + const auto il = candidate.FindNode("indirect_load"); | ||
| 1170 | + ASSERT_NE(il, nullptr); | ||
| 1171 | + EXPECT_NE(ascir::GetTemplateIdOrDefault(*il), ascir::TemplateId::kIndirectLoadSK); | ||
| 1172 | + const auto load = candidate.FindNode("broadcast_input_load"); | ||
| 1173 | + ASSERT_NE(load, nullptr); | ||
| 1174 | + const auto transpose = candidate.FindNode("broadcast_input_transpose"); | ||
| 1175 | + ascgen_utils::indirect_load::TemplateLogicalView view; | ||
| 1176 | + ASSERT_EQ(ascgen_utils::indirect_load::GetTemplateLogicalView(il, view), af::SUCCESS); | ||
| 1177 | + // The scheduler must publish the final GM address strides for SIMT as well as SIMD. | ||
| 1178 | + EXPECT_EQ(view.input.strides, (std::vector<af::Expression>{af::ops::One, af::ops::Zero, af::Symbol(2)})); | ||
| 1179 | + if (ascir::GetTemplateIdOrDefault(*il) == ascir::TemplateId::kIndirectLoadSimd) { | ||
| 1180 | + EXPECT_EQ(transpose, nullptr); | ||
| 1181 | + EXPECT_EQ(load->outputs[0].attr.strides, view.input.strides); | ||
| 1182 | + continue; | ||
| 1183 | + } | ||
| 1184 | + ASSERT_NE(transpose, nullptr); | ||
| 1185 | + EXPECT_EQ(load->outputs[0].attr.axis.front(), transpose->outputs[0].attr.axis.back()); | ||
| 1186 | + EXPECT_EQ(load->outputs[0].attr.axis.back(), transpose->outputs[0].attr.axis.front()); | ||
| 1187 | + EXPECT_EQ(load->outputs[0].attr.repeats, source.repeats); | ||
| 1188 | + EXPECT_EQ(load->outputs[0].attr.strides, source.strides); | ||
| 1189 | + } | ||
| 1190 | +} | ||
| 1191 | + | ||
| 1192 | +TEST(IndirectLoadScheduleCaseGeneratorTest, PreservesSharedOrControlledTransposeLoadBoundary) { | ||
| 1193 | + PlatformContextReset platform_reset; | ||
| 1194 | + ge::PlatformContext::GetInstance().Reset(); | ||
| 1195 | + ge::PlatformInfo platform_info; | ||
| 1196 | + platform_info.soc_ver = "3510"; | ||
| 1197 | + platform_info.ub_size = 256 * 1024; | ||
| 1198 | + platform_info.aiv_num = 48; | ||
| 1199 | + ge::PlatformContext::GetInstance().SetPlatformInfo(platform_info); | ||
| 1200 | + for (const bool control_edge : {false, true}) { | ||
| 1201 | + SCOPED_TRACE(control_edge); | ||
| 1202 | + auto graph = BuildIndirectLoadBroadcastGraph(false, false, 2L, true); | ||
| 1203 | + const auto load = graph.FindNode("broadcast_input_load"); | ||
| 1204 | + ASSERT_NE(load, nullptr); | ||
| 1205 | + const auto source = load->outputs[0].attr; | ||
| 1206 | + if (control_edge) { | ||
| 1207 | + const auto index = graph.FindNode("broadcast_index_load"); | ||
| 1208 | + ASSERT_NE(index, nullptr); | ||
| 1209 | + ASSERT_EQ(af::GraphUtils::AddEdge(load->GetOutControlAnchor(), index->GetInControlAnchor()), af::SUCCESS); | ||
| 1210 | + } else { | ||
| 1211 | + af::ascir_op::Store shared_store("shared_store"); | ||
| 1212 | + graph.AddNode(shared_store); | ||
| 1213 | + SetNodeView(shared_store, source.dtype, source.axis, source.repeats, source.strides); | ||
| 1214 | + ASSERT_EQ(af::GraphUtils::AddEdge(load->GetOutDataAnchor(0), graph.FindNode("shared_store")->GetInDataAnchor(0)), | ||
| 1215 | + af::SUCCESS); | ||
| 1216 | + af::ascir_op::Output shared_output("shared_output"); | ||
| 1217 | + graph.AddNode(shared_output); | ||
| 1218 | + shared_output.ir_attr.SetIndex(1); | ||
| 1219 | + shared_output.x = shared_store.y; | ||
| 1220 | + SetNodeView(shared_output, source.dtype, source.axis, source.repeats, source.strides); | ||
| 1221 | + } | ||
| 1222 | + optimize::IndirectLoadScheduleCaseGenerator generator; | ||
| 1223 | + std::vector<af::AscGraph> graphs; | ||
| 1224 | + std::vector<std::string> score_functions; | ||
| 1225 | + ASSERT_EQ(generator.Generate(graph, graphs, score_functions), af::SUCCESS); | ||
| 1226 | + ASSERT_EQ(graphs.size(), 2UL); | ||
| 1227 | + for (auto &candidate : graphs) { | ||
| 1228 | + const auto input_load = candidate.FindNode("broadcast_input_load"); | ||
| 1229 | + ASSERT_NE(input_load, nullptr); | ||
| 1230 | + EXPECT_NE(candidate.FindNode("broadcast_input_transpose"), nullptr); | ||
| 1231 | + EXPECT_EQ(input_load->outputs[0].attr.repeats, source.repeats); | ||
| 1232 | + EXPECT_EQ(input_load->outputs[0].attr.strides, source.strides); | ||
| 1233 | + EXPECT_TRUE(ascgen_utils::indirect_load::ShouldApplyInputInnerVectorization(input_load)); | ||
| 1234 | + } | ||
| 1235 | + } | ||
| 1236 | +} | ||
| 1237 | + | ||
| 1142 | TEST(IndirectLoadScheduleCaseGeneratorTest, BroadcastDirectPathUsesPhysicalViewForAllTemplates) { | 1238 | TEST(IndirectLoadScheduleCaseGeneratorTest, BroadcastDirectPathUsesPhysicalViewForAllTemplates) { |
| 1143 | PlatformContextReset platform_reset; | 1239 | PlatformContextReset platform_reset; |
| 1144 | ge::PlatformContext::GetInstance().Reset(); | 1240 | ge::PlatformContext::GetInstance().Reset(); |
| @@ -343,6 +343,35 @@ mark_indirect_load_codegen_and_e2e(indirect_load_broadcast_retained_simd_test IL | |||
| 343 | add_indirect_load_broadcast_test(indirect_load_broadcast_retained_simt_test simt 0 0 10 0 0 0 0) | 343 | add_indirect_load_broadcast_test(indirect_load_broadcast_retained_simt_test simt 0 0 10 0 0 0 0) |
| 344 | mark_indirect_load_codegen_and_e2e(indirect_load_broadcast_retained_simt_test IL_RETAIN_BROADCAST=1) | 344 | mark_indirect_load_codegen_and_e2e(indirect_load_broadcast_retained_simt_test IL_RETAIN_BROADCAST=1) |
| 345 | 345 | ||
| 346 | +# Retain the two-input Transpose baseline. The other existing targets below compose input Transpose, | ||
| 347 | +# Broadcast, a plain index, and an output-side broadcast Load without increasing the target count. | ||
| 348 | +add_indirect_load_broadcast_test(indirect_load_both_transpose_simd_test simd 0 0 0 0 0 0 0) | ||
| 349 | +mark_indirect_load_static_shape(indirect_load_both_transpose_simd_test) | ||
| 350 | +mark_indirect_load_codegen_and_e2e(indirect_load_both_transpose_simd_test | ||
| 351 | + IL_BOTH_TRANSPOSE=1 IL_OUTPUT_S0=2 IL_OUTPUT_S1=2 IL_OUTPUT_S2=1 IL_OUTPUT_S3=4) | ||
| 352 | + | ||
| 353 | +# Exercise both SIMD tiling cases explicitly. Mixed cases transpose across the gather axis, broadcast | ||
| 354 | +# the singleton suffix, and add a column-only GM bias; non-square shapes and non-symmetric data expose | ||
| 355 | +# both permutation mistakes and output-linear offsets incorrectly used for the broadcast side input. | ||
| 356 | +add_indirect_load_broadcast_test(indirect_load_both_transpose_simd_fallback_test simd 0 0 8 0 0 1 0) | ||
| 357 | +mark_indirect_load_static_shape(indirect_load_both_transpose_simd_fallback_test) | ||
| 358 | +mark_indirect_load_codegen_and_e2e(indirect_load_both_transpose_simd_fallback_test | ||
| 359 | + IL_INPUT_TRANSPOSE=1 IL_OUTPUT_BROADCAST_ADD=1 IL_FORCE_TILING_CASE=0 | ||
| 360 | + IL_OUTPUT_S0=2 IL_OUTPUT_S1=3 IL_OUTPUT_S2=4 IL_OUTPUT_S3=32) | ||
| 361 | +mark_indirect_load_red(indirect_load_both_transpose_simd_fallback_test) | ||
| 362 | + | ||
| 363 | +add_indirect_load_broadcast_test(indirect_load_both_transpose_simd_nddma_test simd 0 0 8 0 0 1 0) | ||
| 364 | +mark_indirect_load_static_shape(indirect_load_both_transpose_simd_nddma_test) | ||
| 365 | +mark_indirect_load_codegen_and_e2e(indirect_load_both_transpose_simd_nddma_test | ||
| 366 | + IL_INPUT_TRANSPOSE=1 IL_OUTPUT_BROADCAST_ADD=1 IL_FORCE_TILING_CASE=1 | ||
| 367 | + IL_OUTPUT_S0=2 IL_OUTPUT_S1=3 IL_OUTPUT_S2=4 IL_OUTPUT_S3=32) | ||
| 368 | + | ||
| 369 | +add_indirect_load_broadcast_test(indirect_load_both_transpose_simt_test simt 0 0 8 0 0 1 0) | ||
| 370 | +mark_indirect_load_static_shape(indirect_load_both_transpose_simt_test) | ||
| 371 | +mark_indirect_load_codegen_and_e2e(indirect_load_both_transpose_simt_test | ||
| 372 | + IL_INPUT_TRANSPOSE=1 IL_OUTPUT_BROADCAST_ADD=1 | ||
| 373 | + IL_OUTPUT_S0=2 IL_OUTPUT_S1=3 IL_OUTPUT_S2=4 IL_OUTPUT_S3=32) | ||
| 374 | + | ||
| 346 | # Keep one SK Broadcast regression outside the SIMD/SIMT matrix. | 375 | # Keep one SK Broadcast regression outside the SIMD/SIMT matrix. |
| 347 | add_indirect_load_broadcast_test(indirect_load_broadcast_elements_sk_test sk 2 2 3 0 0) | 376 | add_indirect_load_broadcast_test(indirect_load_broadcast_elements_sk_test sk 2 2 3 0 0) |
| 348 | add_indirect_load_broadcast_test(indirect_load_broadcast_index_physical_view_simt_test simt 2 2 3 0 0) | 377 | add_indirect_load_broadcast_test(indirect_load_broadcast_index_physical_view_simt_test simt 2 2 3 0 0) |
| @@ -417,6 +446,11 @@ do_backend_e2e_st_test(indirect_load_graph_hint_simd_repro | |||
| 417 | mark_indirect_load_codegen_and_e2e(indirect_load_graph_hint_simd_repro | 446 | mark_indirect_load_codegen_and_e2e(indirect_load_graph_hint_simd_repro |
| 418 | IL_CASE_BROADCAST_WHERE IL_GRAPH_HINT_SIMD_REPRO) | 447 | IL_CASE_BROADCAST_WHERE IL_GRAPH_HINT_SIMD_REPRO) |
| 419 | 448 | ||
| 449 | +# Exact reproduction of the user-provided embedding slice GraphHint. | ||
| 450 | +add_indirect_load_graph_case(indirect_load_graph_hint_embedding_slice | ||
| 451 | + indirect_load_graph_hint_embedding_slice 0 | ||
| 452 | + IL_CASE_BROADCAST_WHERE IL_GRAPH_HINT_EMBEDDING_SLICE) | ||
| 453 | + | ||
| 420 | # Minimal SIMT reproducer: a partially zero-stride mask view is enough to trigger the bad evaluator offset. | 454 | # Minimal SIMT reproducer: a partially zero-stride mask view is enough to trigger the bad evaluator offset. |
| 421 | add_indirect_load_graph_case(indirect_load_user_masked_embedding_minimal | 455 | add_indirect_load_graph_case(indirect_load_user_masked_embedding_minimal |
| 422 | indirect_load_user_masked_embedding_minimal 1 | 456 | indirect_load_user_masked_embedding_minimal 1 |
| @@ -492,6 +526,10 @@ add_indirect_load_user_fanout_case(indirect_load_user_fanout_post_reduce_simd | |||
| 492 | add_indirect_load_user_fanout_case(indirect_load_user_fanout_post_reduce_simt | 526 | add_indirect_load_user_fanout_case(indirect_load_user_fanout_post_reduce_simt |
| 493 | IL_USER_FANOUT_POST IL_USER_FANOUT_REDUCE) | 527 | IL_USER_FANOUT_POST IL_USER_FANOUT_REDUCE) |
| 494 | 528 | ||
| 529 | +# Regression: two GM output chains, with one chain reading a broadcast GM side input. | ||
| 530 | +add_indirect_load_graph_case(indirect_load_user_fanout_side_input_simt | ||
| 531 | + indirect_load_user_fanout_side_input 1 IL_USER_FANOUT_SIDE_INPUT) | ||
| 532 | + | ||
| 495 | # Minimal red regression: a post-IndirectLoad fanout Add consumes an independent Load side input. | 533 | # Minimal red regression: a post-IndirectLoad fanout Add consumes an independent Load side input. |
| 496 | add_indirect_load_graph_case(indirect_load_user_side_input_fanout_simd | 534 | add_indirect_load_graph_case(indirect_load_user_side_input_fanout_simd |
| 497 | indirect_load_user_side_input_fanout_simd 0 | 535 | indirect_load_user_side_input_fanout_simd 0 |
| @@ -11,6 +11,7 @@ | |||
| 11 | #ifndef AUTOFUSE_TESTS_V35_ST_BACKEND_E2E_V2_INDIRECT_LOAD_STORE_TEST_INDIRECT_LOAD_KERNEL_TEST_COMMON_H_ | 11 | #ifndef AUTOFUSE_TESTS_V35_ST_BACKEND_E2E_V2_INDIRECT_LOAD_STORE_TEST_INDIRECT_LOAD_KERNEL_TEST_COMMON_H_ |
| 12 | #define AUTOFUSE_TESTS_V35_ST_BACKEND_E2E_V2_INDIRECT_LOAD_STORE_TEST_INDIRECT_LOAD_KERNEL_TEST_COMMON_H_ | 12 | #define AUTOFUSE_TESTS_V35_ST_BACKEND_E2E_V2_INDIRECT_LOAD_STORE_TEST_INDIRECT_LOAD_KERNEL_TEST_COMMON_H_ |
| 13 | 13 | ||
| 14 | +#include <algorithm> | ||
| 14 | #include <cstdint> | 15 | #include <cstdint> |
| 15 | #include <memory> | 16 | #include <memory> |
| 16 | #include <vector> | 17 | #include <vector> |
| @@ -21,12 +22,19 @@ | |||
| 21 | #include "autofuse_tiling_data.h" | 22 | #include "autofuse_tiling_data.h" |
| 22 | 23 | ||
| 23 | #if !defined(IL_CASE_STORE) && !defined(IL_CASE_MIXED) | 24 | #if !defined(IL_CASE_STORE) && !defined(IL_CASE_MIXED) |
| 25 | +struct ResLimit; | ||
| 24 | extern "C" int64_t AutofuseTiling(AutofuseTilingData *, uint32_t *, uint32_t *, uint32_t, uint32_t); | 26 | extern "C" int64_t AutofuseTiling(AutofuseTilingData *, uint32_t *, uint32_t *, uint32_t, uint32_t); |
| 27 | +extern "C" int64_t AutofuseTilingWithConfig(const char *, AutofuseTilingData *, uint32_t *, uint32_t *, ResLimit *, | ||
| 28 | + int32_t); | ||
| 25 | #endif | 29 | #endif |
| 26 | 30 | ||
| 27 | #if defined(IL_USER_FANOUT) | 31 | #if defined(IL_USER_FANOUT) |
| 28 | extern "C" __global__ __aicore__ void user_fanout(GM_ADDR indices, GM_ADDR embedding, GM_ADDR weight, GM_ADDR output0, | 32 | extern "C" __global__ __aicore__ void user_fanout(GM_ADDR indices, GM_ADDR embedding, GM_ADDR weight, GM_ADDR output0, |
| 29 | GM_ADDR output1, GM_ADDR workspace, GM_ADDR gm_tiling_data); | 33 | GM_ADDR output1, GM_ADDR workspace, GM_ADDR gm_tiling_data); |
| 34 | +#elif defined(IL_USER_FANOUT_SIDE_INPUT) | ||
| 35 | +extern "C" __global__ __aicore__ void user_fanout_side_input(GM_ADDR indices, GM_ADDR embedding, GM_ADDR side_input, | ||
| 36 | + GM_ADDR output0, GM_ADDR output1, GM_ADDR workspace, | ||
| 37 | + GM_ADDR gm_tiling_data); | ||
| 30 | #elif defined(IL_USER_SIDE_INPUT_FANOUT) | 38 | #elif defined(IL_USER_SIDE_INPUT_FANOUT) |
| 31 | extern "C" __global__ __aicore__ void user_side_input_fanout(GM_ADDR input0, GM_ADDR input1, GM_ADDR input2, | 39 | extern "C" __global__ __aicore__ void user_side_input_fanout(GM_ADDR input0, GM_ADDR input1, GM_ADDR input2, |
| 32 | GM_ADDR input3, GM_ADDR input4, GM_ADDR input5, | 40 | GM_ADDR input3, GM_ADDR input4, GM_ADDR input5, |
| @@ -83,6 +91,10 @@ struct KernelData { | |||
| 83 | struct KernelTiling { | 91 | struct KernelTiling { |
| 84 | explicit KernelTiling(uint32_t core_num = 48U) : workspace(nullptr, GmFree) { | 92 | explicit KernelTiling(uint32_t core_num = 48U) : workspace(nullptr, GmFree) { |
| 85 | EXPECT_EQ(AutofuseTiling(&data, &workspace_size, &block_dim, core_num, 192U * 1024U), 0); | 93 | EXPECT_EQ(AutofuseTiling(&data, &workspace_size, &block_dim, core_num, 192U * 1024U), 0); |
| 94 | +#ifdef IL_FORCE_TILING_CASE | ||
| 95 | + EXPECT_EQ(AutofuseTilingWithConfig(nullptr, &data, &workspace_size, &block_dim, nullptr, IL_FORCE_TILING_CASE), 0); | ||
| 96 | + EXPECT_EQ(data.graph0_result0_g0_tiling_data.tiling_key, static_cast<uint32_t>(IL_FORCE_TILING_CASE)); | ||
| 97 | +#endif | ||
| 86 | EXPECT_GT(data.block_dim, 0U); | 98 | EXPECT_GT(data.block_dim, 0U); |
| 87 | if (workspace_size != 0U) { | 99 | if (workspace_size != 0U) { |
| 88 | workspace.reset(reinterpret_cast<uint8_t *>(AscendC::GmAlloc(workspace_size))); | 100 | workspace.reset(reinterpret_cast<uint8_t *>(AscendC::GmAlloc(workspace_size))); |
| @@ -189,7 +201,9 @@ using OutputType = float; | |||
| 189 | #else | 201 | #else |
| 190 | using OutputType = DataType; | 202 | using OutputType = DataType; |
| 191 | #endif | 203 | #endif |
| 192 | -#ifdef IL_INDEX_INT64 | 204 | +#if IL_BOTH_TRANSPOSE |
| 205 | +using IndexType = int32_t; | ||
| 206 | +#elif defined(IL_INDEX_INT64) | ||
| 193 | using IndexType = int64_t; | 207 | using IndexType = int64_t; |
| 194 | #else | 208 | #else |
| 195 | using IndexType = int32_t; | 209 | using IndexType = int32_t; |
| @@ -212,6 +226,12 @@ float BesselK0Reference(float x) { | |||
| 212 | } | 226 | } |
| 213 | 227 | ||
| 214 | void RunTiling(AutofuseTilingData &tiling, uint32_t &workspace_size, uint32_t &block_dim) { | 228 | void RunTiling(AutofuseTilingData &tiling, uint32_t &workspace_size, uint32_t &block_dim) { |
| 229 | +#ifdef IL_FORCE_TILING_CASE | ||
| 230 | + ASSERT_EQ(AutofuseTilingWithConfig(nullptr, &tiling, &workspace_size, &block_dim, nullptr, IL_FORCE_TILING_CASE), 0); | ||
| 231 | + ASSERT_EQ(tiling.graph0_tiling_key, 0U); | ||
| 232 | + ASSERT_EQ(tiling.graph0_result0_g0_tiling_data.tiling_key, static_cast<uint32_t>(IL_FORCE_TILING_CASE)); | ||
| 233 | + return; | ||
| 234 | +#endif | ||
| 215 | #ifdef IL_STATIC_SHAPE | 235 | #ifdef IL_STATIC_SHAPE |
| 216 | AutofuseTiling(&tiling, &workspace_size, &block_dim, 48, 192 * 1024); | 236 | AutofuseTiling(&tiling, &workspace_size, &block_dim, 48, 192 * 1024); |
| 217 | #elif IL_RANK == 2 | 237 | #elif IL_RANK == 2 |
| @@ -648,6 +668,12 @@ TEST(E2EIndirectLoadStore, GeneratedKernelMatchesReference) { | |||
| 648 | #ifndef IL_INPUT_ABS_BEFORE_BROADCAST | 668 | #ifndef IL_INPUT_ABS_BEFORE_BROADCAST |
| 649 | #define IL_INPUT_ABS_BEFORE_BROADCAST 0 | 669 | #define IL_INPUT_ABS_BEFORE_BROADCAST 0 |
| 650 | #endif | 670 | #endif |
| 671 | +#ifndef IL_BOTH_TRANSPOSE | ||
| 672 | +#define IL_BOTH_TRANSPOSE 0 | ||
| 673 | +#endif | ||
| 674 | +#ifndef IL_INPUT_TRANSPOSE | ||
| 675 | +#define IL_INPUT_TRANSPOSE 0 | ||
| 676 | +#endif | ||
| 651 | #ifndef IL_OUTPUT_S0 | 677 | #ifndef IL_OUTPUT_S0 |
| 652 | #define IL_OUTPUT_S0 4 | 678 | #define IL_OUTPUT_S0 4 |
| 653 | #endif | 679 | #endif |
| @@ -664,6 +690,9 @@ TEST(E2EIndirectLoadStore, GeneratedKernelMatchesReference) { | |||
| 664 | #if IL_AIC_REPRO | 690 | #if IL_AIC_REPRO |
| 665 | extern "C" __global__ __aicore__ void indirect_load_aic_repro(GM_ADDR input, GM_ADDR index, GM_ADDR output, | 691 | extern "C" __global__ __aicore__ void indirect_load_aic_repro(GM_ADDR input, GM_ADDR index, GM_ADDR output, |
| 666 | GM_ADDR workspace, GM_ADDR tiling); | 692 | GM_ADDR workspace, GM_ADDR tiling); |
| 693 | +#elif IL_OUTPUT_BROADCAST_ADD | ||
| 694 | +extern "C" __global__ __aicore__ void indirect_load_broadcast_test(GM_ADDR x, GM_ADDR index, GM_ADDR bias, GM_ADDR y, | ||
| 695 | + GM_ADDR workspace, GM_ADDR tiling); | ||
| 667 | #else | 696 | #else |
| 668 | extern "C" __global__ __aicore__ void indirect_load_broadcast_test(GM_ADDR x, GM_ADDR index, GM_ADDR y, | 697 | extern "C" __global__ __aicore__ void indirect_load_broadcast_test(GM_ADDR x, GM_ADDR index, GM_ADDR y, |
| 669 | GM_ADDR workspace, GM_ADDR tiling); | 698 | GM_ADDR workspace, GM_ADDR tiling); |
| @@ -694,7 +723,11 @@ void InitializeAicReproData(DataType *input, IndexType *index, DataType *expecte | |||
| 694 | } | 723 | } |
| 695 | #else | 724 | #else |
| 696 | using DataType = half; | 725 | using DataType = half; |
| 726 | +#if IL_BOTH_TRANSPOSE | ||
| 727 | +using IndexType = int32_t; | ||
| 728 | +#else | ||
| 697 | using IndexType = int64_t; | 729 | using IndexType = int64_t; |
| 730 | +#endif | ||
| 698 | constexpr std::array<int32_t, 4> kOutputShape = {IL_OUTPUT_S0, IL_OUTPUT_S1, IL_OUTPUT_S2, IL_OUTPUT_S3}; | 731 | constexpr std::array<int32_t, 4> kOutputShape = {IL_OUTPUT_S0, IL_OUTPUT_S1, IL_OUTPUT_S2, IL_OUTPUT_S3}; |
| 699 | constexpr bool kInputBroadcast = IL_INPUT_BROADCAST; | 732 | constexpr bool kInputBroadcast = IL_INPUT_BROADCAST; |
| 700 | constexpr bool kIndexBroadcast = IL_INDEX_BROADCAST; | 733 | constexpr bool kIndexBroadcast = IL_INDEX_BROADCAST; |
| @@ -707,6 +740,8 @@ constexpr bool kRetainBroadcast = IL_RETAIN_BROADCAST; | |||
| 707 | constexpr bool kBroadcastPostReduce = IL_BROADCAST_POST_REDUCE; | 740 | constexpr bool kBroadcastPostReduce = IL_BROADCAST_POST_REDUCE; |
| 708 | constexpr bool kIndexMixedView = IL_INDEX_MIXED_VIEW; | 741 | constexpr bool kIndexMixedView = IL_INDEX_MIXED_VIEW; |
| 709 | constexpr bool kInputAbsBeforeBroadcast = IL_INPUT_ABS_BEFORE_BROADCAST; | 742 | constexpr bool kInputAbsBeforeBroadcast = IL_INPUT_ABS_BEFORE_BROADCAST; |
| 743 | +constexpr bool kBothTranspose = IL_BOTH_TRANSPOSE; | ||
| 744 | +constexpr bool kInputTranspose = IL_INPUT_TRANSPOSE; | ||
| 710 | constexpr uint32_t kBroadcastAxesMask = IL_BROADCAST_AXES_MASK; | 745 | constexpr uint32_t kBroadcastAxesMask = IL_BROADCAST_AXES_MASK; |
| 711 | 746 | ||
| 712 | constexpr std::array<int32_t, 4> MakeBroadcastSourceShape() { | 747 | constexpr std::array<int32_t, 4> MakeBroadcastSourceShape() { |
| @@ -720,8 +755,19 @@ constexpr std::array<int32_t, 4> MakeBroadcastSourceShape() { | |||
| 720 | } | 755 | } |
| 721 | 756 | ||
| 722 | constexpr std::array<int32_t, 4> kBroadcastSourceShape = MakeBroadcastSourceShape(); | 757 | constexpr std::array<int32_t, 4> kBroadcastSourceShape = MakeBroadcastSourceShape(); |
| 723 | -constexpr std::array<int32_t, 4> kInputShape = kInputBroadcast && !kComplexSimt ? kBroadcastSourceShape : kOutputShape; | 758 | +constexpr std::array<int32_t, 4> MakeTransposeSourceShape(const std::array<int32_t, 4> &shape) { |
| 724 | -constexpr std::array<int32_t, 4> kIndexShape = kIndexBroadcast ? kBroadcastSourceShape : kOutputShape; | 759 | + return {shape[1], shape[0], shape[2], shape[3]}; |
| 760 | +} | ||
| 761 | +constexpr std::array<int32_t, 4> kTransposeInputShape = MakeTransposeSourceShape(kOutputShape); | ||
| 762 | +constexpr std::array<int32_t, 4> MakeGatherTransposeSourceShape(std::array<int32_t, 4> shape) { | ||
| 763 | + return {shape[2], shape[1], shape[0], shape[3]}; | ||
| 764 | +} | ||
| 765 | +constexpr std::array<int32_t, 4> kInputShape = | ||
| 766 | + kBothTranspose ? kTransposeInputShape | ||
| 767 | + : kInputTranspose ? MakeGatherTransposeSourceShape(kInputBroadcast ? kBroadcastSourceShape : kOutputShape) | ||
| 768 | + : (kInputBroadcast && !kComplexSimt ? kBroadcastSourceShape : kOutputShape); | ||
| 769 | +constexpr std::array<int32_t, 4> kIndexShape = | ||
| 770 | + kBothTranspose ? kTransposeInputShape : (kIndexBroadcast ? kBroadcastSourceShape : kOutputShape); | ||
| 725 | constexpr int32_t kInputElementCount = IL_HAS_INPUT_ELEMENT; | 771 | constexpr int32_t kInputElementCount = IL_HAS_INPUT_ELEMENT; |
| 726 | constexpr int32_t kIndexElementCount = IL_HAS_INDEX_ELEMENT; | 772 | constexpr int32_t kIndexElementCount = IL_HAS_INDEX_ELEMENT; |
| 727 | constexpr bool kHasOutputRelu = IL_HAS_OUTPUT_RELU; | 773 | constexpr bool kHasOutputRelu = IL_HAS_OUTPUT_RELU; |
| @@ -751,12 +797,38 @@ int32_t DenseOffset(const std::array<int32_t, 4> &coordinate, const std::array<i | |||
| 751 | } | 797 | } |
| 752 | 798 | ||
| 753 | void InitializeData(DataType *x, IndexType *index, DataType *expected) { | 799 | void InitializeData(DataType *x, IndexType *index, DataType *expected) { |
| 754 | - for (int32_t i = 0; i < ElementCount(kInputShape); ++i) { | 800 | + if constexpr (kBothTranspose) { |
| 755 | - x[i] = static_cast<DataType>(static_cast<float>((i % 29) - 14) * 0.25F); | 801 | + for (int32_t a = 0; a < kInputShape[0]; ++a) { |
| 756 | - } | 802 | + for (int32_t b = 0; b < kInputShape[1]; ++b) { |
| 757 | - for (int32_t i = 0; i < ElementCount(kIndexShape); ++i) { | 803 | + for (int32_t c = 0; c < kInputShape[2]; ++c) { |
| 758 | - const int32_t gathered_axis = (i * 3 + 1) % kInputShape[2]; | 804 | + for (int32_t d = 0; d < kInputShape[3]; ++d) { |
| 759 | - index[i] = static_cast<IndexType>(kIndexElementCount == 0 || i % 2 == 0 ? gathered_axis : -gathered_axis); | 805 | + const std::array<int32_t, 4> coordinate = {a, b, c, d}; |
| 806 | + const int32_t value = | ||
| 807 | + kOutputShape[0] == kOutputShape[1] ? (a + b) * 7 + c * 3 + d : a * 11 + b * 3 + c * 5 + d; | ||
| 808 | + x[DenseOffset(coordinate, kInputShape)] = static_cast<DataType>(static_cast<float>(value) * 0.125F); | ||
| 809 | + } | ||
| 810 | + } | ||
| 811 | + } | ||
| 812 | + } | ||
| 813 | + for (int32_t a = 0; a < kIndexShape[0]; ++a) { | ||
| 814 | + for (int32_t b = 0; b < kIndexShape[1]; ++b) { | ||
| 815 | + for (int32_t c = 0; c < kIndexShape[2]; ++c) { | ||
| 816 | + for (int32_t d = 0; d < kIndexShape[3]; ++d) { | ||
| 817 | + const std::array<int32_t, 4> coordinate = {a, b, c, d}; | ||
| 818 | + const int32_t value = kOutputShape[0] == kOutputShape[1] ? a + b + c + d : a * 5 + b * 2 + c + d; | ||
| 819 | + index[DenseOffset(coordinate, kIndexShape)] = static_cast<IndexType>(value % kInputShape[2]); | ||
| 820 | + } | ||
| 821 | + } | ||
| 822 | + } | ||
| 823 | + } | ||
| 824 | + } else { | ||
| 825 | + for (int32_t i = 0; i < ElementCount(kInputShape); ++i) { | ||
| 826 | + x[i] = static_cast<DataType>(static_cast<float>((i % 29) - 14) * 0.25F); | ||
| 827 | + } | ||
| 828 | + for (int32_t i = 0; i < ElementCount(kIndexShape); ++i) { | ||
| 829 | + const int32_t gathered_axis = (i * 3 + 1) % kInputShape[2]; | ||
| 830 | + index[i] = static_cast<IndexType>(kIndexElementCount == 0 || i % 2 == 0 ? gathered_axis : -gathered_axis); | ||
| 831 | + } | ||
| 760 | } | 832 | } |
| 761 | for (int32_t i = 0; i < ElementCount(kOutputShape); ++i) { | 833 | for (int32_t i = 0; i < ElementCount(kOutputShape); ++i) { |
| 762 | int32_t coordinate = i; | 834 | int32_t coordinate = i; |
| @@ -767,7 +839,8 @@ void InitializeData(DataType *x, IndexType *index, DataType *expected) { | |||
| 767 | const int32_t b = coordinate % kOutputShape[1]; | 839 | const int32_t b = coordinate % kOutputShape[1]; |
| 768 | coordinate /= kOutputShape[1]; | 840 | coordinate /= kOutputShape[1]; |
| 769 | const int32_t a = coordinate; | 841 | const int32_t a = coordinate; |
| 770 | - std::array<int32_t, 4> index_coordinate = {a, b, c, d}; | 842 | + std::array<int32_t, 4> index_coordinate = |
| 843 | + kBothTranspose ? std::array<int32_t, 4>{b, a, c, d} : std::array<int32_t, 4>{a, b, c, d}; | ||
| 771 | if (kIndexBroadcast) { | 844 | if (kIndexBroadcast) { |
| 772 | for (size_t dim = 0UL; dim < index_coordinate.size(); ++dim) { | 845 | for (size_t dim = 0UL; dim < index_coordinate.size(); ++dim) { |
| 773 | if ((kBroadcastAxesMask & (1U << dim)) != 0U) { | 846 | if ((kBroadcastAxesMask & (1U << dim)) != 0U) { |
| @@ -788,7 +861,8 @@ void InitializeData(DataType *x, IndexType *index, DataType *expected) { | |||
| 788 | gathered_index = std::abs(gathered_index); | 861 | gathered_index = std::abs(gathered_index); |
| 789 | } | 862 | } |
| 790 | const int32_t gathered_axis = static_cast<int32_t>(gathered_index); | 863 | const int32_t gathered_axis = static_cast<int32_t>(gathered_index); |
| 791 | - std::array<int32_t, 4> input_coordinate = {a, b, gathered_axis, d}; | 864 | + std::array<int32_t, 4> input_coordinate = kBothTranspose ? std::array<int32_t, 4>{b, a, gathered_axis, d} |
| 865 | + : std::array<int32_t, 4>{a, b, gathered_axis, d}; | ||
| 792 | if (kInputBroadcast && !kComplexSimt) { | 866 | if (kInputBroadcast && !kComplexSimt) { |
| 793 | for (size_t dim = 0UL; dim < input_coordinate.size(); ++dim) { | 867 | for (size_t dim = 0UL; dim < input_coordinate.size(); ++dim) { |
| 794 | if ((kBroadcastAxesMask & (1U << dim)) != 0U) { | 868 | if ((kBroadcastAxesMask & (1U << dim)) != 0U) { |
| @@ -796,6 +870,9 @@ void InitializeData(DataType *x, IndexType *index, DataType *expected) { | |||
| 796 | } | 870 | } |
| 797 | } | 871 | } |
| 798 | } | 872 | } |
| 873 | + if constexpr (kInputTranspose) { | ||
| 874 | + std::swap(input_coordinate[0], input_coordinate[2]); | ||
| 875 | + } | ||
| 799 | const int32_t input_offset = DenseOffset(input_coordinate, kInputShape); | 876 | const int32_t input_offset = DenseOffset(input_coordinate, kInputShape); |
| 800 | float value = static_cast<float>(x[input_offset]); | 877 | float value = static_cast<float>(x[input_offset]); |
| 801 | if constexpr (kInputAbsBeforeBroadcast) { | 878 | if constexpr (kInputAbsBeforeBroadcast) { |
| @@ -863,9 +940,25 @@ TEST(E2EIndirectLoadBroadcast, GeneratedKernelMatchesReference) { | |||
| 863 | ASSERT_TRUE(tiling.IsValid()); | 940 | ASSERT_TRUE(tiling.IsValid()); |
| 864 | 941 | ||
| 865 | AscendC::SetKernelMode(KernelMode::AIV_MODE); | 942 | AscendC::SetKernelMode(KernelMode::AIV_MODE); |
| 943 | +#if IL_OUTPUT_BROADCAST_ADD | ||
| 944 | + auto bias = indirect_load_test::AllocGmBuffer<DataType>(kOutputShape[3]); | ||
| 945 | + ASSERT_NE(bias, nullptr); | ||
| 946 | + for (int32_t d = 0; d < kOutputShape[3]; ++d) { | ||
| 947 | + bias.get()[d] = static_cast<DataType>(static_cast<float>((d * 7) % 19 - 9) * 0.125F); | ||
| 948 | + } | ||
| 949 | + for (int32_t i = 0; i < output_count; ++i) { | ||
| 950 | + buffers.expected[i] = static_cast<DataType>(static_cast<float>(buffers.expected[i]) + | ||
| 951 | + static_cast<float>(bias.get()[i % kOutputShape[3]])); | ||
| 952 | + } | ||
| 953 | + ICPU_RUN_KF(indirect_load_broadcast_test, tiling.data.block_dim, reinterpret_cast<uint8_t *>(buffers.input.get()), | ||
| 954 | + reinterpret_cast<uint8_t *>(buffers.index.get()), reinterpret_cast<uint8_t *>(bias.get()), | ||
| 955 | + reinterpret_cast<uint8_t *>(buffers.output.get()), tiling.workspace.get(), | ||
| 956 | + reinterpret_cast<uint8_t *>(&tiling.data)); | ||
| 957 | +#else | ||
| 866 | ICPU_RUN_KF(indirect_load_broadcast_test, tiling.data.block_dim, reinterpret_cast<uint8_t *>(buffers.input.get()), | 958 | ICPU_RUN_KF(indirect_load_broadcast_test, tiling.data.block_dim, reinterpret_cast<uint8_t *>(buffers.input.get()), |
| 867 | reinterpret_cast<uint8_t *>(buffers.index.get()), reinterpret_cast<uint8_t *>(buffers.output.get()), | 959 | reinterpret_cast<uint8_t *>(buffers.index.get()), reinterpret_cast<uint8_t *>(buffers.output.get()), |
| 868 | tiling.workspace.get(), reinterpret_cast<uint8_t *>(&tiling.data)); | 960 | tiling.workspace.get(), reinterpret_cast<uint8_t *>(&tiling.data)); |
| 961 | +#endif | ||
| 869 | for (int32_t i = 0; i < output_count; ++i) { | 962 | for (int32_t i = 0; i < output_count; ++i) { |
| 870 | EXPECT_NEAR(static_cast<float>(buffers.output.get()[i]), | 963 | EXPECT_NEAR(static_cast<float>(buffers.output.get()[i]), |
| 871 | static_cast<float>(buffers.expected[static_cast<size_t>(i)]), 0.0625F) | 964 | static_cast<float>(buffers.expected[static_cast<size_t>(i)]), 0.0625F) |
| @@ -941,6 +1034,51 @@ TEST(UserGraphConstruction, GeneratedKernelMatchesReference) { | |||
| 941 | } | 1034 | } |
| 942 | } | 1035 | } |
| 943 | 1036 | ||
| 1037 | +#elif defined(IL_USER_FANOUT_SIDE_INPUT) | ||
| 1038 | +TEST(UserGraphConstruction, GeneratedKernelMatchesReference) { | ||
| 1039 | + constexpr int32_t kRows = 2; | ||
| 1040 | + constexpr int32_t kDim = 16; | ||
| 1041 | + constexpr int32_t kTableRows = 32; | ||
| 1042 | + auto indices = indirect_load_test::AllocGmBuffer<int64_t>(kRows); | ||
| 1043 | + auto embedding = indirect_load_test::AllocGmBuffer<bfloat16_t>(kTableRows * kDim); | ||
| 1044 | + auto side_input = indirect_load_test::AllocGmBuffer<bfloat16_t>(kDim); | ||
| 1045 | + auto output0 = indirect_load_test::AllocGmBuffer<bfloat16_t>(kRows * kDim); | ||
| 1046 | + auto output1 = indirect_load_test::AllocGmBuffer<bfloat16_t>(kRows * kDim); | ||
| 1047 | + ASSERT_TRUE(indices && embedding && side_input && output0 && output1); | ||
| 1048 | + for (int32_t row = 0; row < kRows; ++row) { | ||
| 1049 | + indices.get()[row] = row + 1; | ||
| 1050 | + } | ||
| 1051 | + for (int32_t row = 0; row < kTableRows; ++row) { | ||
| 1052 | + for (int32_t col = 0; col < kDim; ++col) { | ||
| 1053 | + embedding.get()[row * kDim + col] = static_cast<bfloat16_t>((row + 1) * 0.01F + col * 0.001F); | ||
| 1054 | + } | ||
| 1055 | + } | ||
| 1056 | + for (int32_t col = 0; col < kDim; ++col) { | ||
| 1057 | + side_input.get()[col] = static_cast<bfloat16_t>((col - 3) * 0.02F); | ||
| 1058 | + } | ||
| 1059 | + std::fill_n(output0.get(), kRows * kDim, static_cast<bfloat16_t>(0.0F)); | ||
| 1060 | + std::fill_n(output1.get(), kRows * kDim, static_cast<bfloat16_t>(0.0F)); | ||
| 1061 | + | ||
| 1062 | + indirect_load_test::KernelTiling tiling; | ||
| 1063 | + ASSERT_TRUE(tiling.IsValid()); | ||
| 1064 | + AscendC::SetKernelMode(KernelMode::AIV_MODE); | ||
| 1065 | + ICPU_RUN_KF(user_fanout_side_input, tiling.block_dim, reinterpret_cast<uint8_t *>(indices.get()), | ||
| 1066 | + reinterpret_cast<uint8_t *>(embedding.get()), reinterpret_cast<uint8_t *>(side_input.get()), | ||
| 1067 | + reinterpret_cast<uint8_t *>(output0.get()), reinterpret_cast<uint8_t *>(output1.get()), | ||
| 1068 | + reinterpret_cast<uint8_t *>(tiling.workspace.get()), reinterpret_cast<uint8_t *>(&tiling.data)); | ||
| 1069 | + | ||
| 1070 | + for (int32_t row = 0; row < kRows; ++row) { | ||
| 1071 | + for (int32_t col = 0; col < kDim; ++col) { | ||
| 1072 | + const float value = static_cast<float>(embedding.get()[indices.get()[row] * kDim + col]); | ||
| 1073 | + const float bias = static_cast<float>(side_input.get()[col]); | ||
| 1074 | + EXPECT_NEAR(static_cast<float>(output0.get()[row * kDim + col]), value, 0.125F) | ||
| 1075 | + << "output0 row=" << row << ", col=" << col; | ||
| 1076 | + EXPECT_NEAR(static_cast<float>(output1.get()[row * kDim + col]), value + bias, 0.125F) | ||
| 1077 | + << "output1 row=" << row << ", col=" << col; | ||
| 1078 | + } | ||
| 1079 | + } | ||
| 1080 | +} | ||
| 1081 | + | ||
| 944 | #elif defined(IL_USER_SIDE_INPUT_FANOUT) | 1082 | #elif defined(IL_USER_SIDE_INPUT_FANOUT) |
| 945 | TEST(UserGraphConstruction, GeneratedKernelMatchesReference) { | 1083 | TEST(UserGraphConstruction, GeneratedKernelMatchesReference) { |
| 946 | constexpr int32_t kElements = 64; | 1084 | constexpr int32_t kElements = 64; |
| @@ -1290,6 +1428,9 @@ extern "C" int64_t AutofuseTiling(AutofuseTilingData *, uint32_t *, uint32_t *, | |||
| 1290 | #if defined(IL_USER_MASKED_EMBEDDING_MINIMAL) || defined(IL_USER_MASKED_EMBEDDING_SUM_FULL) | 1428 | #if defined(IL_USER_MASKED_EMBEDDING_MINIMAL) || defined(IL_USER_MASKED_EMBEDDING_SUM_FULL) |
| 1291 | extern "C" __global__ __aicore__ void user_masked_embedding_sum(GM_ADDR input0, GM_ADDR input1, GM_ADDR input2, | 1429 | extern "C" __global__ __aicore__ void user_masked_embedding_sum(GM_ADDR input0, GM_ADDR input1, GM_ADDR input2, |
| 1292 | GM_ADDR output, GM_ADDR workspace, GM_ADDR tiling); | 1430 | GM_ADDR output, GM_ADDR workspace, GM_ADDR tiling); |
| 1431 | +#elif defined(IL_GRAPH_HINT_EMBEDDING_SLICE) | ||
| 1432 | +extern "C" __global__ __aicore__ void autofused_to_copy_embedding_slice_161da55df6362966862381c5d053b7e7( | ||
| 1433 | + GM_ADDR input0, GM_ADDR input1, GM_ADDR output, GM_ADDR workspace, GM_ADDR tiling); | ||
| 1293 | #elif defined(IL_GRAPH_HINT_SIMD_REPRO) | 1434 | #elif defined(IL_GRAPH_HINT_SIMD_REPRO) |
| 1294 | extern "C" __global__ __aicore__ void indirect_load_graph_hint_simd_repro(GM_ADDR input0, GM_ADDR input1, | 1435 | extern "C" __global__ __aicore__ void indirect_load_graph_hint_simd_repro(GM_ADDR input0, GM_ADDR input1, |
| 1295 | GM_ADDR output, GM_ADDR workspace, | 1436 | GM_ADDR output, GM_ADDR workspace, |
| @@ -1390,6 +1531,60 @@ TEST(E2EUserMaskedEmbeddingSum, GeneratedKernelMatchesReference) { | |||
| 1390 | } | 1531 | } |
| 1391 | } | 1532 | } |
| 1392 | } | 1533 | } |
| 1534 | +#elif defined(IL_GRAPH_HINT_EMBEDDING_SLICE) | ||
| 1535 | +constexpr int32_t kGraphHintEmbeddingSliceRows = 128; | ||
| 1536 | +constexpr int32_t kGraphHintEmbeddingSliceColumns = 512; | ||
| 1537 | +constexpr int32_t kGraphHintEmbeddingSliceTableRows = 65536; | ||
| 1538 | +constexpr int32_t kGraphHintEmbeddingSliceIndexStride = 39; | ||
| 1539 | +constexpr int32_t kGraphHintEmbeddingSliceIndexOffset = 13; | ||
| 1540 | + | ||
| 1541 | +TEST(E2EIndirectLoadGraphHintEmbeddingSlice, GeneratedKernelMatchesReference) { | ||
| 1542 | + constexpr int64_t table_count = | ||
| 1543 | + static_cast<int64_t>(kGraphHintEmbeddingSliceTableRows) * kGraphHintEmbeddingSliceColumns; | ||
| 1544 | + constexpr int64_t index_count = | ||
| 1545 | + static_cast<int64_t>(kGraphHintEmbeddingSliceIndexOffset) + | ||
| 1546 | + static_cast<int64_t>(kGraphHintEmbeddingSliceRows - 1) * kGraphHintEmbeddingSliceIndexStride + 1; | ||
| 1547 | + constexpr int64_t output_count = static_cast<int64_t>(kGraphHintEmbeddingSliceRows) * kGraphHintEmbeddingSliceColumns; | ||
| 1548 | + auto table = indirect_load_test::AllocGmBuffer<float>(table_count); | ||
| 1549 | + auto index = indirect_load_test::AllocGmBuffer<float>(index_count); | ||
| 1550 | + auto output = indirect_load_test::AllocGmBuffer<float>(output_count); | ||
| 1551 | + ASSERT_TRUE(table && index && output); | ||
| 1552 | + | ||
| 1553 | + for (int32_t row = 0; row < kGraphHintEmbeddingSliceTableRows; ++row) { | ||
| 1554 | + for (int32_t column = 0; column < kGraphHintEmbeddingSliceColumns; ++column) { | ||
| 1555 | + table.get()[static_cast<int64_t>(row) * kGraphHintEmbeddingSliceColumns + column] = | ||
| 1556 | + static_cast<float>((row % 97) * 0.25F + (column % 31) * 0.001F); | ||
| 1557 | + } | ||
| 1558 | + } | ||
| 1559 | + std::fill_n(index.get(), index_count, 0.0F); | ||
| 1560 | + for (int32_t row = 0; row < kGraphHintEmbeddingSliceRows; ++row) { | ||
| 1561 | + const int32_t positive_index = (row * 997 + 17) % kGraphHintEmbeddingSliceTableRows; | ||
| 1562 | + const int32_t index_value = positive_index; | ||
| 1563 | + index.get()[static_cast<int64_t>(kGraphHintEmbeddingSliceIndexOffset) + | ||
| 1564 | + static_cast<int64_t>(row) * kGraphHintEmbeddingSliceIndexStride] = static_cast<float>(index_value); | ||
| 1565 | + } | ||
| 1566 | + std::fill_n(output.get(), output_count, 0.0F); | ||
| 1567 | + | ||
| 1568 | + indirect_load_test::KernelTiling tiling; | ||
| 1569 | + ASSERT_TRUE(tiling.IsValid()); | ||
| 1570 | + AscendC::SetKernelMode(KernelMode::AIV_MODE); | ||
| 1571 | + ICPU_RUN_KF(autofused_to_copy_embedding_slice_161da55df6362966862381c5d053b7e7, tiling.block_dim, | ||
| 1572 | + reinterpret_cast<uint8_t *>(table.get()), reinterpret_cast<uint8_t *>(index.get()), | ||
| 1573 | + reinterpret_cast<uint8_t *>(output.get()), reinterpret_cast<uint8_t *>(tiling.workspace.get()), | ||
| 1574 | + reinterpret_cast<uint8_t *>(&tiling.data)); | ||
| 1575 | + | ||
| 1576 | + for (int32_t row = 0; row < kGraphHintEmbeddingSliceRows; ++row) { | ||
| 1577 | + const int32_t raw_index = | ||
| 1578 | + static_cast<int32_t>(index.get()[static_cast<int64_t>(kGraphHintEmbeddingSliceIndexOffset) + | ||
| 1579 | + static_cast<int64_t>(row) * kGraphHintEmbeddingSliceIndexStride]); | ||
| 1580 | + const int32_t selected_index = raw_index; | ||
| 1581 | + for (int32_t column = 0; column < kGraphHintEmbeddingSliceColumns; ++column) { | ||
| 1582 | + const int64_t offset = static_cast<int64_t>(row) * kGraphHintEmbeddingSliceColumns + column; | ||
| 1583 | + const int64_t table_offset = static_cast<int64_t>(selected_index) * kGraphHintEmbeddingSliceColumns + column; | ||
| 1584 | + EXPECT_FLOAT_EQ(output.get()[offset], table.get()[table_offset]) << "row=" << row << ", column=" << column; | ||
| 1585 | + } | ||
| 1586 | + } | ||
| 1587 | +} | ||
| 1393 | #elif defined(IL_GRAPH_HINT_SIMD_REPRO) | 1588 | #elif defined(IL_GRAPH_HINT_SIMD_REPRO) |
| 1394 | constexpr int32_t kGraphHintSimdRows = 30; | 1589 | constexpr int32_t kGraphHintSimdRows = 30; |
| 1395 | constexpr int32_t kGraphHintSimdIndexColumns = 3; | 1590 | constexpr int32_t kGraphHintSimdIndexColumns = 3; |
| @@ -12,12 +12,22 @@ | |||
| 12 | #define AUTOFUSE_V35_ASCENDC_API_REGBASE_INDIRECT_LOAD_SIMD_H_ | 12 | #define AUTOFUSE_V35_ASCENDC_API_REGBASE_INDIRECT_LOAD_SIMD_H_ |
| 13 | 13 | ||
| 14 | #include <type_traits> | 14 | #include <type_traits> |
| 15 | +#include <utility> | ||
| 15 | 16 | ||
| 16 | #ifndef AUTOFUSE_V35_ASCENDC_API_REGBASE_INDIRECT_LOAD_SIMD_POLICY_H_ | 17 | #ifndef AUTOFUSE_V35_ASCENDC_API_REGBASE_INDIRECT_LOAD_SIMD_POLICY_H_ |
| 17 | #include "indirect_load_simd_policy.h" | 18 | #include "indirect_load_simd_policy.h" |
| 18 | #endif | 19 | #endif |
| 19 | 20 | ||
| 20 | namespace AscendC { | 21 | namespace AscendC { |
| 22 | +enum class IndirectLoadSimdFallback : uint8_t { | ||
| 23 | + kRegisterGather = 0, | ||
| 24 | + kGatherApi = 1, | ||
| 25 | + kStrided = 2, | ||
| 26 | +}; | ||
| 27 | + | ||
| 28 | +template <IndirectLoadSimdFallback Fallback, bool TryEmbedding> | ||
| 29 | +struct IndirectLoadSimdCaseTag {}; | ||
| 30 | + | ||
| 21 | template <int32_t Rank> | 31 | template <int32_t Rank> |
| 22 | struct IndirectLoadSimdStridedParams { | 32 | struct IndirectLoadSimdStridedParams { |
| 23 | uint32_t logical_size; | 33 | uint32_t logical_size; |
| @@ -29,6 +39,29 @@ struct IndirectLoadSimdStridedParams { | |||
| 29 | int64_t output_strides[Rank]; | 39 | int64_t output_strides[Rank]; |
| 30 | }; | 40 | }; |
| 31 | 41 | ||
| 42 | +template <int32_t Rank> | ||
| 43 | +struct IndirectLoadSimdDenseParams { | ||
| 44 | + uint32_t physical_size; | ||
| 45 | + uint32_t input_actual_size; | ||
| 46 | + int64_t output_offset; | ||
| 47 | + int64_t input_axis; | ||
| 48 | + int64_t index_sizes[Rank]; | ||
| 49 | + int64_t input_strides[Rank]; | ||
| 50 | +}; | ||
| 51 | + | ||
| 52 | +template <int32_t Rank> | ||
| 53 | +struct IndirectLoadSimdEmbeddingParams { | ||
| 54 | + uint32_t logical_size; | ||
| 55 | + uint32_t physical_size; | ||
| 56 | + uint32_t input_actual_size; | ||
| 57 | + int64_t output_offset; | ||
| 58 | + int64_t input_axis; | ||
| 59 | + int64_t index_sizes[Rank]; | ||
| 60 | + int64_t input_strides[Rank]; | ||
| 61 | + int64_t index_strides[Rank]; | ||
| 62 | + int64_t output_strides[Rank]; | ||
| 63 | +}; | ||
| 64 | + | ||
| 32 | namespace Internal { | 65 | namespace Internal { |
| 33 | template <int32_t Dim, int32_t Axis, int32_t StrideBase> | 66 | template <int32_t Dim, int32_t Axis, int32_t StrideBase> |
| 34 | struct IndirectLoadSimdInnerOffset { | 67 | struct IndirectLoadSimdInnerOffset { |
| @@ -396,14 +429,19 @@ __aicore__ inline bool TryIndirectLoadSimdEmbedding(const LocalTensor<X> &x, con | |||
| 396 | int64_t expected_input_stride = 1; | 429 | int64_t expected_input_stride = 1; |
| 397 | for (int32_t dim = Rank - 1; dim > Axis; --dim) { | 430 | for (int32_t dim = Rank - 1; dim > Axis; --dim) { |
| 398 | if (shape[dim] <= 0 || shape[index_stride_base + dim] != 0 || | 431 | if (shape[dim] <= 0 || shape[index_stride_base + dim] != 0 || |
| 399 | - shape[input_stride_base + dim] != expected_input_stride) { | 432 | + shape[input_stride_base + dim] != expected_input_stride || shape[output_stride_base + dim] != payload_span) { |
| 400 | return false; | 433 | return false; |
| 401 | } | 434 | } |
| 402 | payload_span *= shape[dim]; | 435 | payload_span *= shape[dim]; |
| 403 | expected_input_stride *= shape[dim]; | 436 | expected_input_stride *= shape[dim]; |
| 404 | } | 437 | } |
| 438 | + const int64_t output_axis_stride = shape[output_stride_base + Axis]; | ||
| 405 | if (payload_span <= 0 || shape[input_stride_base + Axis] != expected_input_stride || output_offset < 0 || | 439 | if (payload_span <= 0 || shape[input_stride_base + Axis] != expected_input_stride || output_offset < 0 || |
| 406 | - shape[output_stride_base + Axis] < payload_span) { | 440 | + output_axis_stride <= 0 || output_axis_stride < payload_span) { |
| 441 | + return false; | ||
| 442 | + } | ||
| 443 | + const int64_t index_axis_stride = shape[index_stride_base + Axis]; | ||
| 444 | + if (index_axis_stride <= 0) { | ||
| 407 | return false; | 445 | return false; |
| 408 | } | 446 | } |
| 409 | const int64_t output_slice_span = shape[Axis] * payload_span; | 447 | const int64_t output_slice_span = shape[Axis] * payload_span; |
| @@ -448,7 +486,7 @@ __aicore__ inline bool TryIndirectLoadSimdEmbedding(const LocalTensor<X> &x, con | |||
| 448 | input_offset += coordinate * shape[input_stride_base + dim]; | 486 | input_offset += coordinate * shape[input_stride_base + dim]; |
| 449 | } | 487 | } |
| 450 | } | 488 | } |
| 451 | - const int64_t index_value = static_cast<int64_t>(index.GetValue(index_axis_coordinate)); | 489 | + const int64_t index_value = static_cast<int64_t>(index.GetValue(index_axis_coordinate * index_axis_stride)); |
| 452 | const int64_t source_offset = index_value * shape[input_stride_base + Axis] + input_offset + payload_offset; | 490 | const int64_t source_offset = index_value * shape[input_stride_base + Axis] + input_offset + payload_offset; |
| 453 | const int64_t payload_remaining = payload_span - payload_offset; | 491 | const int64_t payload_remaining = payload_span - payload_offset; |
| 454 | const uint32_t copy_count = static_cast<uint32_t>( | 492 | const uint32_t copy_count = static_cast<uint32_t>( |
| @@ -656,6 +694,116 @@ __aicore__ inline void IndirectLoadSimdGatherApi(const LocalTensor<X> &x, const | |||
| 656 | Gather(y, x, offsets, 0U, actual_size); | 694 | Gather(y, x, offsets, 0U, actual_size); |
| 657 | } | 695 | } |
| 658 | 696 | ||
| 697 | +namespace Internal { | ||
| 698 | +template <typename CaseTag, int32_t Rank> | ||
| 699 | +struct IndirectLoadSimdCaseSelector; | ||
| 700 | + | ||
| 701 | +template <IndirectLoadSimdFallback Fallback, bool TryEmbedding, int32_t Rank> | ||
| 702 | +struct IndirectLoadSimdCaseSelector<IndirectLoadSimdCaseTag<Fallback, TryEmbedding>, Rank> { | ||
| 703 | + using Params = | ||
| 704 | + std::conditional_t<TryEmbedding, IndirectLoadSimdEmbeddingParams<Rank>, | ||
| 705 | + std::conditional_t<Fallback == IndirectLoadSimdFallback::kStrided, | ||
| 706 | + IndirectLoadSimdStridedParams<Rank>, IndirectLoadSimdDenseParams<Rank>>>; | ||
| 707 | + | ||
| 708 | + template <typename X, typename Index, int32_t Axis> | ||
| 709 | + __aicore__ inline static void RunDense(const LocalTensor<X> &x, const LocalTensor<Index> &index, | ||
| 710 | + const LocalTensor<X> &y, const Params ¶ms) { | ||
| 711 | + static_assert(Fallback != IndirectLoadSimdFallback::kStrided, | ||
| 712 | + "IndirectLoad SIMD dense facade requires a dense fallback."); | ||
| 713 | + if constexpr (TryEmbedding) { | ||
| 714 | + if (TryEmbeddingImpl<X, Index, Axis>(x, index, y, params, std::make_index_sequence<Rank>{})) { | ||
| 715 | + return; | ||
| 716 | + } | ||
| 717 | + } | ||
| 718 | + RunDenseFallback<X, Index, Axis>(x, index, y, params, std::make_index_sequence<Rank>{}); | ||
| 719 | + } | ||
| 720 | + | ||
| 721 | + template <typename X, typename Index, int32_t Axis> | ||
| 722 | + __aicore__ inline static void RunStrided(const LocalTensor<X> &x, const LocalTensor<Index> &index, | ||
| 723 | + const LocalTensor<X> &y, const LocalTensor<uint8_t> &tmp, | ||
| 724 | + const Params ¶ms) { | ||
| 725 | + static_assert(Fallback == IndirectLoadSimdFallback::kStrided, | ||
| 726 | + "IndirectLoad SIMD strided facade requires a strided fallback."); | ||
| 727 | + if constexpr (TryEmbedding) { | ||
| 728 | + if (TryEmbeddingImpl<X, Index, Axis>(x, index, y, params, std::make_index_sequence<Rank>{})) { | ||
| 729 | + return; | ||
| 730 | + } | ||
| 731 | + } | ||
| 732 | + if constexpr (TryEmbedding) { | ||
| 733 | + IndirectLoadSimdStridedParams<Rank> strided_params{ | ||
| 734 | + params.logical_size, params.physical_size, params.output_offset, {}, {}, {}, {}}; | ||
| 735 | + for (int32_t dim = 0; dim < Rank; ++dim) { | ||
| 736 | + strided_params.index_sizes[dim] = params.index_sizes[dim]; | ||
| 737 | + strided_params.input_strides[dim] = params.input_strides[dim]; | ||
| 738 | + strided_params.index_strides[dim] = params.index_strides[dim]; | ||
| 739 | + strided_params.output_strides[dim] = params.output_strides[dim]; | ||
| 740 | + } | ||
| 741 | + AscendC::IndirectLoadSimdStrided<X, Index, Rank, Axis>(x, index, y, tmp, strided_params); | ||
| 742 | + } else { | ||
| 743 | + AscendC::IndirectLoadSimdStrided<X, Index, Rank, Axis>(x, index, y, tmp, params); | ||
| 744 | + } | ||
| 745 | + } | ||
| 746 | + | ||
| 747 | + private: | ||
| 748 | + template <typename X, typename Index, int32_t Axis, size_t... Dims> | ||
| 749 | + __aicore__ inline static bool TryEmbeddingImpl(const LocalTensor<X> &x, const LocalTensor<Index> &index, | ||
| 750 | + const LocalTensor<X> &y, const Params ¶ms, | ||
| 751 | + std::index_sequence<Dims...>) { | ||
| 752 | + return TryIndirectLoadSimdEmbedding<X, Index, Rank, Axis>( | ||
| 753 | + x, index, y, params.logical_size, params.output_offset, params.index_sizes[Dims]..., | ||
| 754 | + params.input_strides[Dims]..., params.index_strides[Dims]..., params.output_strides[Dims]...); | ||
| 755 | + } | ||
| 756 | + | ||
| 757 | + template <typename X, typename Index, int32_t Axis, size_t... Dims> | ||
| 758 | + __aicore__ inline static void RunDenseFallback(const LocalTensor<X> &x, const LocalTensor<Index> &index, | ||
| 759 | + const LocalTensor<X> &y, const Params ¶ms, | ||
| 760 | + std::index_sequence<Dims...>) { | ||
| 761 | + if constexpr (Fallback == IndirectLoadSimdFallback::kRegisterGather) { | ||
| 762 | + AscendC::IndirectLoadSimd<X, Index, Rank, Axis>(x, index, y, params.physical_size, params.output_offset, | ||
| 763 | + params.input_actual_size, params.input_axis, | ||
| 764 | + params.index_sizes[Dims]..., params.input_strides[Dims]...); | ||
| 765 | + } else { | ||
| 766 | + AscendC::IndirectLoadSimdGatherApi<X, Index, Rank, Axis>( | ||
| 767 | + x, index, y, params.physical_size, params.output_offset, params.input_actual_size, params.input_axis, | ||
| 768 | + params.index_sizes[Dims]..., params.input_strides[Dims]...); | ||
| 769 | + } | ||
| 770 | + } | ||
| 771 | +}; | ||
| 772 | +} // namespace Internal | ||
| 773 | + | ||
| 774 | +template <typename CaseTag, int32_t Rank> | ||
| 775 | +using IndirectLoadSimdParams = typename Internal::IndirectLoadSimdCaseSelector<CaseTag, Rank>::Params; | ||
| 776 | + | ||
| 777 | +template <typename X, typename Index, int32_t Rank, int32_t Axis, bool TryEmbedding> | ||
| 778 | +__aicore__ inline void IndirectLoadSimd( | ||
| 779 | + const LocalTensor<X> &x, const LocalTensor<Index> &index, const LocalTensor<X> &y, | ||
| 780 | + IndirectLoadSimdCaseTag<IndirectLoadSimdFallback::kRegisterGather, TryEmbedding>, | ||
| 781 | + const IndirectLoadSimdParams<IndirectLoadSimdCaseTag<IndirectLoadSimdFallback::kRegisterGather, TryEmbedding>, Rank> | ||
| 782 | + ¶ms) { | ||
| 783 | + using CaseTag = IndirectLoadSimdCaseTag<IndirectLoadSimdFallback::kRegisterGather, TryEmbedding>; | ||
| 784 | + Internal::IndirectLoadSimdCaseSelector<CaseTag, Rank>::template RunDense<X, Index, Axis>(x, index, y, params); | ||
| 785 | +} | ||
| 786 | + | ||
| 787 | +template <typename X, typename Index, int32_t Rank, int32_t Axis, bool TryEmbedding> | ||
| 788 | +__aicore__ inline void IndirectLoadSimdGatherApi( | ||
| 789 | + const LocalTensor<X> &x, const LocalTensor<Index> &index, const LocalTensor<X> &y, | ||
| 790 | + IndirectLoadSimdCaseTag<IndirectLoadSimdFallback::kGatherApi, TryEmbedding>, | ||
| 791 | + const IndirectLoadSimdParams<IndirectLoadSimdCaseTag<IndirectLoadSimdFallback::kGatherApi, TryEmbedding>, Rank> | ||
| 792 | + ¶ms) { | ||
| 793 | + using CaseTag = IndirectLoadSimdCaseTag<IndirectLoadSimdFallback::kGatherApi, TryEmbedding>; | ||
| 794 | + Internal::IndirectLoadSimdCaseSelector<CaseTag, Rank>::template RunDense<X, Index, Axis>(x, index, y, params); | ||
| 795 | +} | ||
| 796 | + | ||
| 797 | +template <typename X, typename Index, int32_t Rank, int32_t Axis, bool TryEmbedding> | ||
| 798 | +__aicore__ inline void IndirectLoadSimdStrided( | ||
| 799 | + const LocalTensor<X> &x, const LocalTensor<Index> &index, const LocalTensor<X> &y, const LocalTensor<uint8_t> &tmp, | ||
| 800 | + IndirectLoadSimdCaseTag<IndirectLoadSimdFallback::kStrided, TryEmbedding>, | ||
| 801 | + const IndirectLoadSimdParams<IndirectLoadSimdCaseTag<IndirectLoadSimdFallback::kStrided, TryEmbedding>, Rank> | ||
| 802 | + ¶ms) { | ||
| 803 | + using CaseTag = IndirectLoadSimdCaseTag<IndirectLoadSimdFallback::kStrided, TryEmbedding>; | ||
| 804 | + Internal::IndirectLoadSimdCaseSelector<CaseTag, Rank>::template RunStrided<X, Index, Axis>(x, index, y, tmp, params); | ||
| 805 | +} | ||
| 806 | + | ||
| 659 | } // namespace AscendC | 807 | } // namespace AscendC |
| 660 | 808 | ||
| 661 | #endif // AUTOFUSE_V35_ASCENDC_API_REGBASE_INDIRECT_LOAD_SIMD_H_ | 809 | #endif // AUTOFUSE_V35_ASCENDC_API_REGBASE_INDIRECT_LOAD_SIMD_H_ |
| @@ -11,7 +11,61 @@ | |||
| 11 | #ifndef AUTOFUSE_V35_ASCENDC_API_REGBASE_INDIRECT_LOAD_SIMT_H_ | 11 | #ifndef AUTOFUSE_V35_ASCENDC_API_REGBASE_INDIRECT_LOAD_SIMT_H_ |
| 12 | #define AUTOFUSE_V35_ASCENDC_API_REGBASE_INDIRECT_LOAD_SIMT_H_ | 12 | #define AUTOFUSE_V35_ASCENDC_API_REGBASE_INDIRECT_LOAD_SIMT_H_ |
| 13 | 13 | ||
| 14 | +#include <type_traits> | ||
| 15 | + | ||
| 14 | namespace AscendC { | 16 | namespace AscendC { |
| 17 | +enum class IndirectLoadSimtCase : uint8_t { | ||
| 18 | + kStaticPowerOfTwo = 0, | ||
| 19 | + kStaticInner = 1, | ||
| 20 | + kStructuredMagic = 2, | ||
| 21 | + kEmbedding = 3, | ||
| 22 | + kRecursive = 4, | ||
| 23 | + kStrided = 5, | ||
| 24 | +}; | ||
| 25 | + | ||
| 26 | +template <IndirectLoadSimtCase Case, typename OffsetT, int32_t Rank, int32_t Axis, uint64_t InnerSpan = 0U, | ||
| 27 | + uint64_t OutputAxisSpan = 0U, uint64_t InputAxisStride = 0U, uint64_t InputAxisSpan = 0U, | ||
| 28 | + uint64_t InputStrideMask = 0U, uint64_t IndexStrideMask = 0U> | ||
| 29 | +struct IndirectLoadSimtCaseTag {}; | ||
| 30 | + | ||
| 31 | +template <int32_t Rank> | ||
| 32 | +struct IndirectLoadSimtShapeLayout { | ||
| 33 | + static constexpr int32_t kInputStrideBase = Rank; | ||
| 34 | + static constexpr int32_t kIndexStrideBase = kInputStrideBase + Rank; | ||
| 35 | + static constexpr int32_t kRecursiveShapeSize = kIndexStrideBase; | ||
| 36 | + static constexpr int32_t kStridedShapeSize = kIndexStrideBase + Rank; | ||
| 37 | +}; | ||
| 38 | + | ||
| 39 | +struct IndirectLoadSimtStaticPowerOfTwoParams {}; | ||
| 40 | + | ||
| 41 | +template <typename OffsetT> | ||
| 42 | +struct IndirectLoadSimtStaticInnerParams { | ||
| 43 | + OffsetT output_axis_span; | ||
| 44 | +}; | ||
| 45 | + | ||
| 46 | +template <typename OffsetT> | ||
| 47 | +struct IndirectLoadSimtStructuredMagicParams { | ||
| 48 | + OffsetT inner_span; | ||
| 49 | + OffsetT output_axis_span; | ||
| 50 | + OffsetT input_axis_stride; | ||
| 51 | + OffsetT input_axis_span; | ||
| 52 | +}; | ||
| 53 | + | ||
| 54 | +template <typename OffsetT, int32_t Rank> | ||
| 55 | +struct IndirectLoadSimtEmbeddingParams { | ||
| 56 | + OffsetT shape[IndirectLoadSimtShapeLayout<Rank>::kStridedShapeSize]; | ||
| 57 | +}; | ||
| 58 | + | ||
| 59 | +template <typename OffsetT, int32_t Rank> | ||
| 60 | +struct IndirectLoadSimtRecursiveParams { | ||
| 61 | + OffsetT shape[IndirectLoadSimtShapeLayout<Rank>::kRecursiveShapeSize]; | ||
| 62 | +}; | ||
| 63 | + | ||
| 64 | +template <typename OffsetT, int32_t Rank> | ||
| 65 | +struct IndirectLoadSimtStridedParams { | ||
| 66 | + OffsetT shape[IndirectLoadSimtShapeLayout<Rank>::kStridedShapeSize]; | ||
| 67 | +}; | ||
| 68 | + | ||
| 15 | namespace Internal { | 69 | namespace Internal { |
| 16 | __aicore__ inline uint64_t IndirectLoadGetUintDivMagic(uint64_t dividend, uint64_t divisor) { | 70 | __aicore__ inline uint64_t IndirectLoadGetUintDivMagic(uint64_t dividend, uint64_t divisor) { |
| 17 | uint64_t quotient = 0U; | 71 | uint64_t quotient = 0U; |
| @@ -14,20 +14,12 @@ | |||
| 14 | #include <cstdint> | 14 | #include <cstdint> |
| 15 | #include <string> | 15 | #include <string> |
| 16 | #include <unordered_map> | 16 | #include <unordered_map> |
| 17 | -#include <unordered_set> | ||
| 18 | #include <vector> | 17 | #include <vector> |
| 19 | 18 | ||
| 20 | #include "codegen_kernel.h" | 19 | #include "codegen_kernel.h" |
| 21 | #include "indirect_load_utils.h" | 20 | #include "indirect_load_utils.h" |
| 22 | 21 | ||
| 23 | namespace codegen { | 22 | namespace codegen { |
| 24 | -struct SimtGmTensor { | ||
| 25 | - ascir::TensorId value_tensor_id; | ||
| 26 | - ascir::TensorId gm_tensor_id; | ||
| 27 | - af::DataType dtype; | ||
| 28 | - bool is_scalar{false}; | ||
| 29 | -}; | ||
| 30 | - | ||
| 31 | struct SimtOutputChain { | 23 | struct SimtOutputChain { |
| 32 | std::vector<af::AscNodePtr> nodes; | 24 | std::vector<af::AscNodePtr> nodes; |
| 33 | ascir::TensorId result_tensor_id{af::kIdNone}; | 25 | ascir::TensorId result_tensor_id{af::kIdNone}; |
| @@ -47,39 +39,9 @@ struct LogicalTensorInfo { | |||
| 47 | std::vector<ascir::SizeExpr> strides; | 39 | std::vector<ascir::SizeExpr> strides; |
| 48 | }; | 40 | }; |
| 49 | 41 | ||
| 50 | -enum class SimtAddressPolicy { | 42 | +using SimtLoadMetadataMap = std::unordered_map<std::string, ascgen_utils::indirect_load::SimtLoadMetadata>; |
| 51 | - kStaticPowerOfTwo, | 43 | +using SimtLoadIndexOffsetExpressions = std::unordered_map<std::string, std::string>; |
| 52 | - kStaticInner, | 44 | +using SimtGraphNodeMap = std::unordered_map<std::string, af::AscNodePtr>; |
| 53 | - kStructuredMagic, | ||
| 54 | - kEmbedding, | ||
| 55 | - kRecursive, | ||
| 56 | - kStrided, | ||
| 57 | -}; | ||
| 58 | - | ||
| 59 | -struct SimtCodegenPlan { | ||
| 60 | - SimtAddressPolicy policy = SimtAddressPolicy::kRecursive; | ||
| 61 | - std::string offset_type = "uint64_t"; | ||
| 62 | - af::Expression inner_span = af::sym::kSymbolOne; | ||
| 63 | - af::Expression output_axis_span = af::sym::kSymbolOne; | ||
| 64 | - af::Expression input_axis_stride = af::sym::kSymbolOne; | ||
| 65 | - af::Expression input_axis_span = af::sym::kSymbolOne; | ||
| 66 | - uint64_t inner_span_value = 0U; | ||
| 67 | - uint64_t output_axis_span_value = 0U; | ||
| 68 | - uint64_t input_axis_stride_value = 0U; | ||
| 69 | - uint64_t input_axis_span_value = 0U; | ||
| 70 | - uint64_t input_stride_mask = 0U; | ||
| 71 | - uint64_t index_stride_mask = 0U; | ||
| 72 | - bool use_per_load_index_offsets = false; | ||
| 73 | -}; | ||
| 74 | - | ||
| 75 | -enum class SimtLoadAddressSource { | ||
| 76 | - kZeroOffset, | ||
| 77 | - kOutputOffset, | ||
| 78 | - kIndexOffset, | ||
| 79 | -}; | ||
| 80 | - | ||
| 81 | -using SimtLoadAddressSources = std::unordered_map<const af::AscNode *, SimtLoadAddressSource>; | ||
| 82 | -using SimtLoadIndexOffsetExpressions = std::unordered_map<const af::AscNode *, std::string>; | ||
| 83 | 45 | ||
| 84 | class IndirectLoadRegApiCall final : public ApiCall { | 46 | class IndirectLoadRegApiCall final : public ApiCall { |
| 85 | public: | 47 | public: |
| @@ -96,7 +58,9 @@ class IndirectLoadRegApiCall final : public ApiCall { | |||
| 96 | Status ParseAttr(const ascir::NodeView &node) override; | 58 | Status ParseAttr(const ascir::NodeView &node) override; |
| 97 | 59 | ||
| 98 | private: | 60 | private: |
| 99 | - Status ParseSimtAttr(const ascir::NodeView &node); | 61 | + Status ResolveSimtMetadata(const ascir::NodeView &node, |
| 62 | + const ascgen_utils::indirect_load::SimtLoweringMetadata &metadata, | ||
| 63 | + const SimtGraphNodeMap &node_map); | ||
| 100 | Status GenerateSk(const TPipe &tpipe, const std::vector<ascir::AxisId> ¤t_axis, | 64 | Status GenerateSk(const TPipe &tpipe, const std::vector<ascir::AxisId> ¤t_axis, |
| 101 | const std::vector<std::reference_wrapper<const Tensor>> &inputs, | 65 | const std::vector<std::reference_wrapper<const Tensor>> &inputs, |
| 102 | const std::vector<std::reference_wrapper<const Tensor>> &outputs, std::string &result) const; | 66 | const std::vector<std::reference_wrapper<const Tensor>> &outputs, std::string &result) const; |
| @@ -113,22 +77,19 @@ class IndirectLoadRegApiCall final : public ApiCall { | |||
| 113 | ascir::TemplateId template_id_{ascir::TemplateId::kDefault}; | 77 | ascir::TemplateId template_id_{ascir::TemplateId::kDefault}; |
| 114 | ascgen_utils::indirect_load::TemplateLogicalView logical_view_; | 78 | ascgen_utils::indirect_load::TemplateLogicalView logical_view_; |
| 115 | ascgen_utils::indirect_load::IndirectLoadAccessInfo access_info_; | 79 | ascgen_utils::indirect_load::IndirectLoadAccessInfo access_info_; |
| 80 | + ascgen_utils::indirect_load::SimdLoweringMetadata simd_metadata_; | ||
| 116 | bool has_post_reduce_{false}; | 81 | bool has_post_reduce_{false}; |
| 117 | std::vector<af::AscNodePtr> index_nodes_; | 82 | std::vector<af::AscNodePtr> index_nodes_; |
| 118 | std::vector<af::AscNodePtr> output_nodes_; | 83 | std::vector<af::AscNodePtr> output_nodes_; |
| 119 | std::vector<SimtOutputChain> output_chains_; | 84 | std::vector<SimtOutputChain> output_chains_; |
| 120 | - // SIMT layout metadata and policy are built once during ParseSimtAttr and reused by both emitters. | 85 | + ascgen_utils::indirect_load::SimtPolicyMetadata simt_policy_; |
| 121 | - LogicalTensorInfo simt_input_info_; | 86 | + SimtLoadMetadataMap simt_index_loads_; |
| 122 | - LogicalTensorInfo simt_index_info_; | 87 | + SimtLoadMetadataMap simt_output_loads_; |
| 123 | - SimtCodegenPlan simt_plan_; | 88 | + std::vector<ascgen_utils::indirect_load::SimtGmTensorMetadata> simt_gm_tensors_; |
| 124 | - SimtLoadAddressSources simt_load_address_sources_; | ||
| 125 | - std::vector<SimtGmTensor> simt_gm_tensors_; | ||
| 126 | - std::unordered_set<ascir::TensorId> simt_gm_tensor_ids_; | ||
| 127 | ascir::TensorId index_result_tensor_id_{af::kIdNone}; | 89 | ascir::TensorId index_result_tensor_id_{af::kIdNone}; |
| 128 | ascir::TensorId simt_value_tensor_id_{af::kIdNone}; | 90 | ascir::TensorId simt_value_tensor_id_{af::kIdNone}; |
| 129 | ascir::TensorId output_result_tensor_id_{af::kIdNone}; | 91 | ascir::TensorId output_result_tensor_id_{af::kIdNone}; |
| 130 | af::DataType index_dtype_{af::DT_UNDEFINED}; | 92 | af::DataType index_dtype_{af::DT_UNDEFINED}; |
| 131 | - af::DataType output_dtype_{af::DT_UNDEFINED}; | ||
| 132 | ascgen_utils::indirect_load::Implementation implementation_{ascgen_utils::indirect_load::Implementation::kDefault}; | 93 | ascgen_utils::indirect_load::Implementation implementation_{ascgen_utils::indirect_load::Implementation::kDefault}; |
| 133 | }; | 94 | }; |
| 134 | } // namespace codegen | 95 | } // namespace codegen |
| @@ -28,6 +28,25 @@ constexpr char kTransposePrefix[] = "transpose_"; | |||
| 28 | bool IsNddma(const af::AscNodePtr &node) { | 28 | bool IsNddma(const af::AscNodePtr &node) { |
| 29 | return ScheduleUtils::IsLoad(node) && node->attr.type == "Nddma"; | 29 | return ScheduleUtils::IsLoad(node) && node->attr.type == "Nddma"; |
| 30 | } | 30 | } |
| 31 | + | ||
| 32 | +Status UpdateSimdIndirectLoadDenseInputLayouts(const af::OutDataAnchorPtr &producer_anchor) { | ||
| 33 | + for (const auto &consumer_anchor : producer_anchor->GetPeerInDataAnchors()) { | ||
| 34 | + const auto consumer = std::dynamic_pointer_cast<af::AscNode>(consumer_anchor->GetOwnerNode()); | ||
| 35 | + if (!af::ops::IsOps<af::ascir_op::IndirectLoad>(consumer) || | ||
| 36 | + ascir::GetTemplateIdOrDefault(*consumer) != ascir::TemplateId::kIndirectLoadSimd) { | ||
| 37 | + continue; | ||
| 38 | + } | ||
| 39 | + ascgen_utils::indirect_load::TemplateLogicalView logical_view; | ||
| 40 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::GetTemplateLogicalView(consumer, logical_view)); | ||
| 41 | + const auto input_index = static_cast<size_t>(consumer_anchor->GetIdx()); | ||
| 42 | + auto &layout = | ||
| 43 | + input_index == ascgen_utils::indirect_load::kInputTensorIndex ? logical_view.input : logical_view.index; | ||
| 44 | + GE_ASSERT_SUCCESS(ScheduleUtils::RecalculateStridesFromRepeats(layout.sizes, layout.strides)); | ||
| 45 | + layout.kind = ascgen_utils::indirect_load::IndirectLoadLayoutKind::kDense; | ||
| 46 | + GE_ASSERT_SUCCESS(ascgen_utils::indirect_load::SetTemplateLogicalView(consumer, logical_view)); | ||
| 47 | + } | ||
| 48 | + return af::SUCCESS; | ||
| 49 | +} | ||
| 31 | } // namespace | 50 | } // namespace |
| 32 | 51 | ||
| 33 | std::string NddmaTemplate::GenName(const std::string &general_case_name) { | 52 | std::string NddmaTemplate::GenName(const std::string &general_case_name) { |
| @@ -143,6 +162,7 @@ Status NddmaTemplate::MergeLoadAndTranspose(const af::AscNodePtr &load_node, af: | |||
| 143 | load_node->attr.type = "Nddma"; | 162 | load_node->attr.type = "Nddma"; |
| 144 | // 删除transpose节点 | 163 | // 删除transpose节点 |
| 145 | GE_ASSERT_SUCCESS(ScheduleUtils::RemoveNode(new_case, out_node, load_node->GetOutDataAnchor(0))); | 164 | GE_ASSERT_SUCCESS(ScheduleUtils::RemoveNode(new_case, out_node, load_node->GetOutDataAnchor(0))); |
| 165 | + GE_ASSERT_SUCCESS(UpdateSimdIndirectLoadDenseInputLayouts(load_out_anchor)); | ||
| 146 | return af::SUCCESS; | 166 | return af::SUCCESS; |
| 147 | } | 167 | } |
| 148 | 168 | ||
| @@ -241,6 +261,14 @@ af::Status NddmaTemplate::ProcessTransposeNodes(af::AscGraph &new_case, bool &is | |||
| 241 | 261 | ||
| 242 | af::Status NddmaTemplate::Generate([[maybe_unused]] const af::AscGraph &origin_graph, | 262 | af::Status NddmaTemplate::Generate([[maybe_unused]] const af::AscGraph &origin_graph, |
| 243 | [[maybe_unused]] const af::AscGraph &based_case, af::AscGraph &new_case) { | 263 | [[maybe_unused]] const af::AscGraph &based_case, af::AscGraph &new_case) { |
| 264 | + const auto indirect_load = ascgen_utils::indirect_load::FindIndirectLoadNode(new_case); | ||
| 265 | + const bool is_simd_indirect_load = | ||
| 266 | + indirect_load != nullptr && ascir::GetTemplateIdOrDefault(*indirect_load) == ascir::TemplateId::kIndirectLoadSimd; | ||
| 267 | + if (indirect_load != nullptr && !is_simd_indirect_load && | ||
| 268 | + ScheduleUtils::HasComputeType(origin_graph, af::ComputeType::kComputeTranspose)) { | ||
| 269 | + GELOGD("Skip NDDMA transpose template for IndirectLoad graph[%s].", new_case.GetName().c_str()); | ||
| 270 | + return af::FAILED; | ||
| 271 | + } | ||
| 244 | bool is_nddma_generated = false; | 272 | bool is_nddma_generated = false; |
| 245 | GE_ASSERT_SUCCESS(ProcessTransposeNodes(new_case, is_nddma_generated)); | 273 | GE_ASSERT_SUCCESS(ProcessTransposeNodes(new_case, is_nddma_generated)); |
| 246 | bool is_transpose_nddma_generated = is_nddma_generated; | 274 | bool is_transpose_nddma_generated = is_nddma_generated; |
| @@ -283,7 +311,7 @@ af::Status NddmaTemplate::Generate([[maybe_unused]] const af::AscGraph &origin_g | |||
| 283 | GELOGD("No nddma template generated."); | 311 | GELOGD("No nddma template generated."); |
| 284 | return af::FAILED; | 312 | return af::FAILED; |
| 285 | } | 313 | } |
| 286 | - if (is_transpose_nddma_generated) { | 314 | + if (is_transpose_nddma_generated && indirect_load == nullptr) { |
| 287 | GE_ASSERT_SUCCESS( | 315 | GE_ASSERT_SUCCESS( |
| 288 | UnAlignmentStrategy::ModifyTransposeFusionVectorizedStrides(new_case, BaseAlignmentStrategy::GetAlignWidth())); | 316 | UnAlignmentStrategy::ModifyTransposeFusionVectorizedStrides(new_case, BaseAlignmentStrategy::GetAlignWidth())); |
| 289 | } | 317 | } |
| @@ -13,10 +13,36 @@ | |||
| 13 | #include <stack> | 13 | #include <stack> |
| 14 | #include "ascir_utils.h" | 14 | #include "ascir_utils.h" |
| 15 | #include "common_utils.h" | 15 | #include "common_utils.h" |
| 16 | +#include "indirect_load_utils.h" | ||
| 16 | #include "tensor_layout_utils.h" | 17 | #include "tensor_layout_utils.h" |
| 17 | #include "platform/v1/alignment_strategy.h" | 18 | #include "platform/v1/alignment_strategy.h" |
| 18 | 19 | ||
| 19 | namespace optimize { | 20 | namespace optimize { |
| 21 | +namespace { | ||
| 22 | +af::Status NormalizeStoreSingletonStrides(const af::AscNodePtr &node) { | ||
| 23 | + GE_ASSERT_TRUE(node->outputs().size() == 1UL); | ||
| 24 | + auto &attr = node->outputs[0].attr; | ||
| 25 | + GE_ASSERT_TRUE(attr.vectorized_axis.size() == attr.vectorized_strides.size()); | ||
| 26 | + GE_ASSERT_TRUE(attr.axis.size() == attr.repeats.size()); | ||
| 27 | + af::Expression inner_span = af::sym::kSymbolOne; | ||
| 28 | + for (size_t index = attr.vectorized_axis.size(); index > 0UL; --index) { | ||
| 29 | + const size_t vector_index = index - 1UL; | ||
| 30 | + const auto axis_iter = std::find(attr.axis.begin(), attr.axis.end(), attr.vectorized_axis[vector_index]); | ||
| 31 | + GE_ASSERT_TRUE(axis_iter != attr.axis.end()); | ||
| 32 | + const size_t axis_index = static_cast<size_t>(std::distance(attr.axis.begin(), axis_iter)); | ||
| 33 | + auto &stride = attr.vectorized_strides[vector_index]; | ||
| 34 | + if (af::SymbolicUtils::StaticCheckEq(attr.repeats[axis_index], af::sym::kSymbolOne) == af::TriBool::kTrue && | ||
| 35 | + af::SymbolicUtils::StaticCheckEq(stride, af::sym::kSymbolZero) == af::TriBool::kTrue) { | ||
| 36 | + stride = inner_span; | ||
| 37 | + } | ||
| 38 | + if (af::SymbolicUtils::StaticCheckEq(stride, af::sym::kSymbolZero) != af::TriBool::kTrue) { | ||
| 39 | + inner_span = af::sym::Mul(stride, attr.repeats[axis_index]); | ||
| 40 | + } | ||
| 41 | + } | ||
| 42 | + return af::SUCCESS; | ||
| 43 | +} | ||
| 44 | +} // namespace | ||
| 45 | + | ||
| 20 | AlignmentType UnAlignmentStrategy::GetDefaultAlignmentType() { | 46 | AlignmentType UnAlignmentStrategy::GetDefaultAlignmentType() { |
| 21 | return AlignmentType::kNotAligned; | 47 | return AlignmentType::kNotAligned; |
| 22 | } | 48 | } |
| @@ -440,7 +466,45 @@ Status UnAlignmentStrategy::ModifyTransposeFusionVectorizedStrides(af::AscGraph | |||
| 440 | return af::SUCCESS; | 466 | return af::SUCCESS; |
| 441 | } | 467 | } |
| 442 | 468 | ||
| 469 | +Status UnAlignmentStrategy::ModifyIndirectLoadVectorizedStrides(ascir::ImplGraph &impl_graph) { | ||
| 470 | + bool has_transpose = false; | ||
| 471 | + bool has_nddma_load = false; | ||
| 472 | + for (const auto &node : impl_graph.GetAllNodes()) { | ||
| 473 | + has_transpose = has_transpose || node->attr.api.compute_type == af::ComputeType::kComputeTranspose; | ||
| 474 | + has_nddma_load = has_nddma_load || (ScheduleUtils::IsLoad(node) && node->attr.type == "Nddma"); | ||
| 475 | + const auto role = ascgen_utils::indirect_load::GetTemplateRole(node); | ||
| 476 | + if (role != ascgen_utils::indirect_load::TemplateRole::kStridedUbPath && | ||
| 477 | + role != ascgen_utils::indirect_load::TemplateRole::kSimdInputPreStridedUbPath) { | ||
| 478 | + continue; | ||
| 479 | + } | ||
| 480 | + for (const auto &output : node->outputs()) { | ||
| 481 | + if (!output->attr.vectorized_axis.empty()) { | ||
| 482 | + GE_ASSERT_SUCCESS( | ||
| 483 | + BaseAlignmentStrategy::SetVectorizedStridesForTensor(node, output->attr, AlignmentType::kAligned)); | ||
| 484 | + } | ||
| 485 | + } | ||
| 486 | + } | ||
| 487 | + // Only the direct IL->Store boundary inherits the transposed/NDDMA layout. | ||
| 488 | + // Other IL stores (for example, stores after elementwise or reduce nodes) use | ||
| 489 | + // their own layout and must not be normalized here. | ||
| 490 | + if (has_transpose || has_nddma_load) { | ||
| 491 | + for (const auto &node : impl_graph.GetAllNodes()) { | ||
| 492 | + if (!af::ops::IsOps<af::ascir_op::Store>(node) || node->inputs.Size() != 1UL || node->outputs().size() != 1UL) { | ||
| 493 | + continue; | ||
| 494 | + } | ||
| 495 | + const auto producer = ascgen_utils::indirect_load::GetInputProducer(node, 0UL); | ||
| 496 | + if (af::ops::IsOps<af::ascir_op::IndirectLoad>(producer)) { | ||
| 497 | + GE_ASSERT_SUCCESS(NormalizeStoreSingletonStrides(node)); | ||
| 498 | + } | ||
| 499 | + } | ||
| 500 | + } | ||
| 501 | + return af::SUCCESS; | ||
| 502 | +} | ||
| 503 | + | ||
| 443 | Status UnAlignmentStrategy::ModifyVectorizedStrides(af::AscGraph &impl_graph) { | 504 | Status UnAlignmentStrategy::ModifyVectorizedStrides(af::AscGraph &impl_graph) { |
| 505 | + if (ascgen_utils::indirect_load::FindIndirectLoadNode(impl_graph) != nullptr) { | ||
| 506 | + return ModifyIndirectLoadVectorizedStrides(impl_graph); | ||
| 507 | + } | ||
| 444 | bool has_transpose = false; | 508 | bool has_transpose = false; |
| 445 | for (auto node : impl_graph.GetAllNodes()) { | 509 | for (auto node : impl_graph.GetAllNodes()) { |
| 446 | if (af::ops::IsOps<af::ascir_op::Transpose>(node)) { | 510 | if (af::ops::IsOps<af::ascir_op::Transpose>(node)) { |
| @@ -37,6 +37,7 @@ class UnAlignmentStrategy : public BaseAlignmentStrategy { | |||
| 37 | static af::Status CollectTransposePreNodes(const af::AscGraph &graph, std::set<af::AscNodePtr> &transpose_pre_nodes); | 37 | static af::Status CollectTransposePreNodes(const af::AscGraph &graph, std::set<af::AscNodePtr> &transpose_pre_nodes); |
| 38 | static af::Status UpdateOutputVectorizedStrides(const af::AscNodePtr &node, uint32_t continuous_tail_axis_num, | 38 | static af::Status UpdateOutputVectorizedStrides(const af::AscNodePtr &node, uint32_t continuous_tail_axis_num, |
| 39 | uint32_t align_width); | 39 | uint32_t align_width); |
| 40 | + static af::Status ModifyIndirectLoadVectorizedStrides(ascir::ImplGraph &impl_graph); | ||
| 40 | }; | 41 | }; |
| 41 | 42 | ||
| 42 | Status GenLoadToGenNddmaNode(const af::AscNodePtr &node_load); | 43 | Status GenLoadToGenNddmaNode(const af::AscNodePtr &node_load); |
| @@ -733,7 +733,6 @@ build_backend() { | |||
| 733 | cmake $CMAKE_ARGS ../ | 733 | cmake $CMAKE_ARGS ../ |
| 734 | 734 | ||
| 735 | # st用例可执行文件的列表,inductor split_compile 仅保留 presubmit 代表用例,其余放 nightly。 | 735 | # st用例可执行文件的列表,inductor split_compile 仅保留 presubmit 代表用例,其余放 nightly。 |
| 736 | - # Temporarily exclude embedding_test and graph_hint_simd_repro because the SIMD embedding fast path has an index-stride issue. | ||
| 737 | MAKE_TARGET_LIST="add_abs_test_e2e \ | 736 | MAKE_TARGET_LIST="add_abs_test_e2e \ |
| 738 | axpy_abs_test_e2e \ | 737 | axpy_abs_test_e2e \ |
| 739 | sub_abs_test_e2e \ | 738 | sub_abs_test_e2e \ |
| @@ -845,6 +844,7 @@ build_backend() { | |||
| 845 | indirect_load_broadcast_inner_adjacent_simd_test_e2e_v2 \ | 844 | indirect_load_broadcast_inner_adjacent_simd_test_e2e_v2 \ |
| 846 | indirect_load_broadcast_continuous_simd_test_e2e_v2 \ | 845 | indirect_load_broadcast_continuous_simd_test_e2e_v2 \ |
| 847 | indirect_load_broadcast_continuous_index_simt_test_e2e_v2 \ | 846 | indirect_load_broadcast_continuous_index_simt_test_e2e_v2 \ |
| 847 | + indirect_load_embedding_test_e2e_v2 \ | ||
| 848 | indirect_load_embedding_tail_simd_e2e_v2 \ | 848 | indirect_load_embedding_tail_simd_e2e_v2 \ |
| 849 | indirect_load_embedding_tail_simt_e2e_v2 \ | 849 | indirect_load_embedding_tail_simt_e2e_v2 \ |
| 850 | indirect_load_embedding_aligned_simd_e2e_v2 \ | 850 | indirect_load_embedding_aligned_simd_e2e_v2 \ |
| @@ -855,8 +855,12 @@ build_backend() { | |||
| 855 | indirect_load_complex_input_broadcast_simd_test_e2e_v2 \ | 855 | indirect_load_complex_input_broadcast_simd_test_e2e_v2 \ |
| 856 | indirect_load_broadcast_cross_boundary_simt_test_e2e_v2 \ | 856 | indirect_load_broadcast_cross_boundary_simt_test_e2e_v2 \ |
| 857 | indirect_load_broadcast_axis_simt_test_e2e_v2 \ | 857 | indirect_load_broadcast_axis_simt_test_e2e_v2 \ |
| 858 | - indirect_load_broadcast_reduce_simt_fallback_test_e2e_v2 \ | 858 | + indirect_load_broadcast_reduce_simt_fallback_test_e2e_v2 \ |
| 859 | indirect_load_broadcast_retained_simt_test_e2e_v2 \ | 859 | indirect_load_broadcast_retained_simt_test_e2e_v2 \ |
| 860 | + indirect_load_both_transpose_simd_test_e2e_v2 \ | ||
| 861 | + indirect_load_both_transpose_simd_fallback_test_e2e_v2 \ | ||
| 862 | + indirect_load_both_transpose_simd_nddma_test_e2e_v2 \ | ||
| 863 | + indirect_load_both_transpose_simt_test_e2e_v2 \ | ||
| 860 | indirect_load_broadcast_elements_sk_test_e2e_v2 \ | 864 | indirect_load_broadcast_elements_sk_test_e2e_v2 \ |
| 861 | indirect_load_broadcast_index_physical_view_simt_test_e2e_v2 \ | 865 | indirect_load_broadcast_index_physical_view_simt_test_e2e_v2 \ |
| 862 | indirect_load_broadcast_index_mixed_view_simt_test_e2e_v2 \ | 866 | indirect_load_broadcast_index_mixed_view_simt_test_e2e_v2 \ |
| @@ -872,6 +876,8 @@ build_backend() { | |||
| 872 | indirect_load_rank3_axis1_input_index_outer_gap_simt_test_e2e_v2 \ | 876 | indirect_load_rank3_axis1_input_index_outer_gap_simt_test_e2e_v2 \ |
| 873 | indirect_load_rank3_axis1_torch_gather_frontend_e2e_v2 \ | 877 | indirect_load_rank3_axis1_torch_gather_frontend_e2e_v2 \ |
| 874 | indirect_load_graph_hint_reduce_simt_test_e2e_v2 \ | 878 | indirect_load_graph_hint_reduce_simt_test_e2e_v2 \ |
| 879 | + indirect_load_graph_hint_simd_repro_e2e_v2 \ | ||
| 880 | + indirect_load_graph_hint_embedding_slice_e2e_v2 \ | ||
| 875 | indirect_load_embedding_reduce_simt_test_e2e_v2 \ | 881 | indirect_load_embedding_reduce_simt_test_e2e_v2 \ |
| 876 | indirect_load_add_il_reduce_test_e2e_v2 \ | 882 | indirect_load_add_il_reduce_test_e2e_v2 \ |
| 877 | indirect_load_user_masked_embedding_minimal_e2e_v2 \ | 883 | indirect_load_user_masked_embedding_minimal_e2e_v2 \ |
| @@ -893,6 +899,7 @@ build_backend() { | |||
| 893 | indirect_load_user_fanout_post_stores_simt_e2e_v2 \ | 899 | indirect_load_user_fanout_post_stores_simt_e2e_v2 \ |
| 894 | indirect_load_user_fanout_post_reduce_simd_e2e_v2 \ | 900 | indirect_load_user_fanout_post_reduce_simd_e2e_v2 \ |
| 895 | indirect_load_user_fanout_post_reduce_simt_e2e_v2 \ | 901 | indirect_load_user_fanout_post_reduce_simt_e2e_v2 \ |
| 902 | + indirect_load_user_fanout_side_input_simt_e2e_v2 \ | ||
| 896 | indirect_load_user_side_input_fanout_simd_e2e_v2 \ | 903 | indirect_load_user_side_input_fanout_simd_e2e_v2 \ |
| 897 | indirect_load_user_side_input_fanout_simt_e2e_v2 \ | 904 | indirect_load_user_side_input_fanout_simt_e2e_v2 \ |
| 898 | load_where_x2_x3_is_ubscalar_store_test_e2e_v2 \ | 905 | load_where_x2_x3_is_ubscalar_store_test_e2e_v2 \ |