* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
* \file test_scope_range.cpp
* \brief Unit tests for verifying scope tagging functionality on nodes
*/
#include <gtest/gtest.h>
#include <memory>
#include <bitset>
#define private public
#define protected public
#include "sk_node.h"
#include "sk_types.h"
#include "sk_graph.h"
class TestScopeSplitRange: public ::testing::Test {
protected:
std::unique_ptr<SuperKernelGraph> graph;
void SetUp() override {
graph = std::make_unique<SuperKernelGraph>();
}
void TearDown() override {
}
SuperKernelKernelNode* CreateKernelNode(uint64_t nodeId = 1, uint32_t streamIdx = 0, bool isFusible = false) {
auto task = std::make_unique<aclrtTask>();
auto node = std::make_unique<SuperKernelKernelNode>(
std::move(task),
ACL_MODEL_RI_TASK_KERNEL,
nodeId,
streamIdx,
INVALID_STREAM_ID,
INVALID_TASK_ID
);
node->SetNodeId(nodeId);
node->SetIsFusible(isFusible);
SuperKernelKernelNode* ptr = node.get();
graph->graphMap[nodeId] = std::move(node);
return ptr;
}
SuperKernelKernelNode* CreateScopeKernelNode(uint64_t nodeId = 1, uint32_t streamIdx = 0, std::string scopeName = "", bool isFusible = true, bool isScopeNode = true, bool isBegin = false, bool isEnd = false, bool isPlaceholder = false) {
auto task = std::make_unique<aclrtTask>();
auto node = std::make_unique<SuperKernelKernelNode>(
std::move(task),
ACL_MODEL_RI_TASK_KERNEL,
nodeId,
streamIdx,
INVALID_STREAM_ID,
INVALID_TASK_ID
);
node->SetNodeId(nodeId);
node->scopeName = scopeName;
node->isFusible = isFusible;
node->isScopeNode = isScopeNode;
node->isScopeBegin = isBegin;
node->isScopeEnd = isEnd;
node->isPlaceholder = isPlaceholder;
SuperKernelKernelNode* ptr = node.get();
graph->graphMap[nodeId] = std::move(node);
return ptr;
}
};
TEST_F(TestScopeSplitRange, TestNodeWithSingleScope) {
auto node1 = CreateKernelNode(1, 0, true);
auto scopeBeginNode = CreateScopeKernelNode(2,0,"scopeA",true,true,true,false,false);
auto scopeNodePlaceholder_1 = CreateScopeKernelNode(3,0,"scopeA",true,true,false,false,true);
auto scopeNodePlaceholder_2 = CreateScopeKernelNode(4,0,"scopeA",true,true,false,false,true);
auto node2 = CreateKernelNode(5, 0, true);
auto node3 = CreateKernelNode(6, 0, false);
auto scopeNodePlaceholder_3 = CreateScopeKernelNode(7,0,"scopeA",true,true,false,false,true);
auto scopeNodePlaceholder_4 = CreateScopeKernelNode(8,0,"scopeA",true,true,false,false,true);
auto scopeEndNode = CreateScopeKernelNode(9,0,"scopeA",true,true,false,true,false);
auto node4 = CreateKernelNode(10, 0, true);
graph->scopeNameToIdx["scopeA"] = 0;
graph->UpdateNodeScopeBitFlags();
EXPECT_TRUE(node1->GetScopeBitFlags().none());
EXPECT_FALSE(node1->IsFusible());
EXPECT_TRUE(scopeBeginNode->GetScopeBitFlags().test(0));
EXPECT_TRUE(scopeNodePlaceholder_1->GetScopeBitFlags().test(0));
EXPECT_TRUE(scopeNodePlaceholder_2->GetScopeBitFlags().test(0));
EXPECT_TRUE(node2->GetScopeBitFlags().test(0));
EXPECT_TRUE(node2->IsFusible());
EXPECT_FALSE(node3->IsFusible());
EXPECT_TRUE(scopeNodePlaceholder_3->GetScopeBitFlags().test(0));
EXPECT_TRUE(scopeNodePlaceholder_4->GetScopeBitFlags().test(0));
EXPECT_TRUE(scopeEndNode->GetScopeBitFlags().test(0));
EXPECT_TRUE(node4->GetScopeBitFlags().none());
EXPECT_FALSE(node4->IsFusible());
}
TEST_F(TestScopeSplitRange, TestNodeWithUnfusibleScope) {
auto scopeBeginNode = CreateScopeKernelNode(1, 0, "", false, true, true, false, false);
auto scopeNodePlaceholder_1 = CreateScopeKernelNode(2, 0, "", false, true, false, false, true);
auto scopeNodePlaceholder_2 = CreateScopeKernelNode(3, 0, "", false, true, false, false, true);
auto node1 = CreateKernelNode(4, 0, true);
auto scopeNodePlaceholder_3 = CreateScopeKernelNode(5, 0, "", false, true, false, false, true);
auto scopeNodePlaceholder_4 = CreateScopeKernelNode(6, 0, "", false, true, false, false, true);
auto scopeEndNode = CreateScopeKernelNode(7, 0, "", false, true, false, true, false);
auto node2 = CreateKernelNode(8, 0, true);
graph->UpdateNodeScopeBitFlags();
EXPECT_TRUE(scopeBeginNode->IsFusible());
EXPECT_TRUE(scopeNodePlaceholder_1->IsFusible());
EXPECT_TRUE(scopeNodePlaceholder_2->IsFusible());
EXPECT_FALSE(node1->IsFusible());
EXPECT_TRUE(scopeNodePlaceholder_3->IsFusible());
EXPECT_TRUE(scopeNodePlaceholder_4->IsFusible());
EXPECT_TRUE(scopeEndNode->IsFusible());
EXPECT_TRUE(node2->IsFusible());
}
TEST_F(TestScopeSplitRange, TestNodeWithMultiScopeIntersection) {
graph->scopeNameToIdx["scopeA"] = 0;
graph->scopeNameToIdx["scopeB"] = 1;
auto scopeBeginA = CreateScopeKernelNode(1, 0, "scopeA", true, true, true, false, false);
auto scopeNodePlaceholderA_1 = CreateScopeKernelNode(2, 0, "scopeA", true, true, false, false, true);
auto scopeNodePlaceholderA_2 = CreateScopeKernelNode(3, 0, "scopeA", true, true, false, false, true);
auto scopeBeginB = CreateScopeKernelNode(4, 0, "scopeB", true, true, true, false, false);
auto scopeNodePlaceholderB_1 = CreateScopeKernelNode(5, 0, "scopeB", true, true, false, false, true);
auto scopeNodePlaceholderB_2 = CreateScopeKernelNode(6, 0, "scopeB", true, true, false, false, true);
auto node1 = CreateKernelNode(7, 0, true);
auto scopeNodePlaceholderA_3 = CreateScopeKernelNode(8, 0, "scopeA", true, true, false, false, true);
auto scopeNodePlaceholderA_4 = CreateScopeKernelNode(9, 0, "scopeA", true, true, false, false, true);
auto scopeEndA = CreateScopeKernelNode(10, 0, "scopeA", true, true, false, true, false);
auto node2 = CreateKernelNode(11, 0, true);
auto scopeNodePlaceholderB_3 = CreateScopeKernelNode(12, 0, "scopeB", true, true, false, false, true);
auto scopeNodePlaceholderB_4 = CreateScopeKernelNode(13, 0, "scopeB", true, true, false, false, true);
auto scopeEndB = CreateScopeKernelNode(14, 0, "scopeB", true, true, false, true, false);
graph->UpdateNodeScopeBitFlags();
EXPECT_TRUE(node1->IsFusible());
EXPECT_EQ(node1->GetScopeBitFlags().count(), 2);
EXPECT_TRUE(node1->GetScopeBitFlags().test(0));
EXPECT_TRUE(node1->GetScopeBitFlags().test(1));
}
TEST_F(TestScopeSplitRange, TestRealScenarioWithMixedScopeInterleaving) {
graph->scopeNameToIdx["sk2"] = 0;
auto node0 = CreateScopeKernelNode(0, 0, "", false, true, true, false, false);
auto node1 = CreateScopeKernelNode(1, 0, "", false, true, false, false, true);
auto node2 = CreateScopeKernelNode(2, 0, "", false, true, false, false, true);
auto node3 = CreateKernelNode(3, 0, true);
auto node4 = CreateScopeKernelNode(4, 0, "sk2", true, true, true, false, false);
auto node5 = CreateScopeKernelNode(5, 0, "sk2", true, true, false, false, true);
auto node6 = CreateScopeKernelNode(6, 0, "sk2", true, true, false, false, true);
auto node7 = CreateScopeKernelNode(7, 0, "", false, true, false, false, true);
auto node8 = CreateScopeKernelNode(8, 0, "", false, true, false, false, true);
auto node9 = CreateScopeKernelNode(9, 0, "", false, true, false, true, false);
auto node10 = CreateKernelNode(10, 0, true);
auto node11 = CreateScopeKernelNode(11, 0, "sk2", true, true, false, false, true);
auto node12 = CreateScopeKernelNode(12, 0, "sk2", true, true, false, false, true);
auto node13 = CreateScopeKernelNode(13, 0, "sk2", true, true, false, true, false);
graph->UpdateNodeScopeBitFlags();
EXPECT_TRUE(node0->IsFusible());
EXPECT_TRUE(node0->GetScopeBitFlags().none());
EXPECT_TRUE(node1->IsFusible());
EXPECT_TRUE(node1->GetScopeBitFlags().none());
EXPECT_TRUE(node2->IsFusible());
EXPECT_TRUE(node2->GetScopeBitFlags().none());
EXPECT_FALSE(node3->IsFusible());
EXPECT_TRUE(node3->GetScopeBitFlags().none());
EXPECT_TRUE(node4->IsFusible());
EXPECT_TRUE(node4->GetScopeBitFlags().test(0));
EXPECT_TRUE(node5->IsFusible());
EXPECT_TRUE(node5->GetScopeBitFlags().test(0));
EXPECT_TRUE(node6->IsFusible());
EXPECT_TRUE(node6->GetScopeBitFlags().test(0));
EXPECT_TRUE(node7->IsFusible());
EXPECT_TRUE(node7->GetScopeBitFlags().test(0));
EXPECT_TRUE(node8->IsFusible());
EXPECT_TRUE(node8->GetScopeBitFlags().test(0));
EXPECT_TRUE(node9->IsFusible());
EXPECT_TRUE(node9->GetScopeBitFlags().test(0));
EXPECT_TRUE(node10->IsFusible());
EXPECT_TRUE(node10->GetScopeBitFlags().test(0));
EXPECT_TRUE(node11->IsFusible());
EXPECT_TRUE(node11->GetScopeBitFlags().test(0));
EXPECT_TRUE(node12->IsFusible());
EXPECT_TRUE(node12->GetScopeBitFlags().test(0));
EXPECT_TRUE(node13->IsFusible());
EXPECT_TRUE(node13->GetScopeBitFlags().test(0));
}
TEST_F(TestScopeSplitRange, TestNodeWithMultiScopeSubset) {
graph->scopeNameToIdx["scopeA"] = 0;
graph->scopeNameToIdx["scopeB"] = 1;
auto scopeBeginA = CreateScopeKernelNode(1, 0, "scopeA", true, true, true, false, false);
auto scopeNodePlaceholderA_1 = CreateScopeKernelNode(2, 0, "scopeA", true, true, false, false, true);
auto scopeNodePlaceholderA_2 = CreateScopeKernelNode(3, 0, "scopeA", true, true, false, false, true);
auto scopeBeginB = CreateScopeKernelNode(4, 0, "scopeB", true, true, true, false, false);
auto scopeNodePlaceholderB_1 = CreateScopeKernelNode(5, 0, "scopeB", true, true, false, false, true);
auto scopeNodePlaceholderB_2 = CreateScopeKernelNode(6, 0, "scopeB", true, true, false, false, true);
auto node1 = CreateKernelNode(7, 0, true);
auto scopeNodePlaceholderB_3 = CreateScopeKernelNode(8, 0, "scopeB", true, true, false, false, true);
auto scopeNodePlaceholderB_4 = CreateScopeKernelNode(9, 0, "scopeB", true, true, false, false, true);
auto scopeEndB = CreateScopeKernelNode(10, 0, "scopeB", true, true, false, true, false);
auto node2 = CreateKernelNode(11, 0, true);
auto scopeNodePlaceholderA_3 = CreateScopeKernelNode(12, 0, "scopeA", true, true, false, false, true);
auto scopeNodePlaceholderA_4 = CreateScopeKernelNode(13, 0, "scopeA", true, true, false, false, true);
auto scopeEndA = CreateScopeKernelNode(14, 0, "scopeA", true, true, false, true, false);
graph->UpdateNodeScopeBitFlags();
EXPECT_TRUE(node1->IsFusible());
EXPECT_EQ(node1->GetScopeBitFlags().count(), 2);
EXPECT_TRUE(node1->GetScopeBitFlags().test(0));
EXPECT_TRUE(node1->GetScopeBitFlags().test(1));
EXPECT_TRUE(node2->IsFusible());
EXPECT_EQ(node2->GetScopeBitFlags().count(), 1);
EXPECT_TRUE(node2->GetScopeBitFlags().test(0));
}
TEST_F(TestScopeSplitRange, TestMultipleSequentialSameScope) {
graph->scopeNameToIdx["scopeA"] = 0;
auto scopeBeginA1 = CreateScopeKernelNode(1, 0, "scopeA", true, true, true, false, false);
auto placeholderA1_1 = CreateScopeKernelNode(2, 0, "scopeA", true, true, false, false, true);
auto node1 = CreateKernelNode(3, 0, true);
auto placeholderA1_2 = CreateScopeKernelNode(4, 0, "scopeA", true, true, false, false, true);
auto scopeEndA1 = CreateScopeKernelNode(5, 0, "scopeA", true, true, false, true, false);
auto nodeBetween = CreateKernelNode(6, 0, true);
auto scopeBeginA2 = CreateScopeKernelNode(7, 0, "scopeA", true, true, true, false, false);
auto placeholderA2_1 = CreateScopeKernelNode(8, 0, "scopeA", true, true, false, false, true);
auto node2 = CreateKernelNode(9, 0, true);
auto placeholderA2_2 = CreateScopeKernelNode(10, 0, "scopeA", true, true, false, false, true);
auto scopeEndA2 = CreateScopeKernelNode(11, 0, "scopeA", true, true, false, true, false);
auto nodeAfter = CreateKernelNode(12, 0, true);
graph->UpdateNodeScopeBitFlags();
EXPECT_TRUE(scopeBeginA1->IsFusible());
EXPECT_TRUE(scopeBeginA1->GetScopeBitFlags().test(0));
EXPECT_TRUE(placeholderA1_1->IsFusible());
EXPECT_TRUE(placeholderA1_1->GetScopeBitFlags().test(0));
EXPECT_TRUE(node1->IsFusible());
EXPECT_TRUE(node1->GetScopeBitFlags().test(0));
EXPECT_TRUE(placeholderA1_2->IsFusible());
EXPECT_TRUE(placeholderA1_2->GetScopeBitFlags().test(0));
EXPECT_TRUE(scopeEndA1->IsFusible());
EXPECT_TRUE(scopeEndA1->GetScopeBitFlags().test(0));
EXPECT_FALSE(nodeBetween->IsFusible());
EXPECT_TRUE(nodeBetween->GetScopeBitFlags().none());
EXPECT_TRUE(scopeBeginA2->IsFusible());
EXPECT_TRUE(scopeBeginA2->GetScopeBitFlags().test(0));
EXPECT_TRUE(placeholderA2_1->IsFusible());
EXPECT_TRUE(placeholderA2_1->GetScopeBitFlags().test(0));
EXPECT_TRUE(node2->IsFusible());
EXPECT_TRUE(node2->GetScopeBitFlags().test(0));
EXPECT_TRUE(placeholderA2_2->IsFusible());
EXPECT_TRUE(placeholderA2_2->GetScopeBitFlags().test(0));
EXPECT_TRUE(scopeEndA2->IsFusible());
EXPECT_TRUE(scopeEndA2->GetScopeBitFlags().test(0));
EXPECT_FALSE(nodeAfter->IsFusible());
EXPECT_TRUE(nodeAfter->GetScopeBitFlags().none());
}
TEST_F(TestScopeSplitRange, TestNodeWithMultiScopeIntersectionUnfusible) {
graph->scopeNameToIdx["scopeA"] = 0;
auto scopeBeginA = CreateScopeKernelNode(1, 0, "scopeA", true, true, true, false, false);
auto scopeNodePlaceholderA_1 = CreateScopeKernelNode(2, 0, "scopeA", true, true, false, false, true);
auto scopeNodePlaceholderA_2 = CreateScopeKernelNode(3, 0, "scopeA", true, true, false, false, true);
auto scopeBeginB = CreateScopeKernelNode(4, 0, "", false, true, true, false, false);
auto scopeNodePlaceholderB_1 = CreateScopeKernelNode(5, 0, "", true, true, false, false, true);
auto scopeNodePlaceholderB_2 = CreateScopeKernelNode(6, 0, "", true, true, false, false, true);
auto node1 = CreateKernelNode(7, 0, true);
auto scopeNodePlaceholderA_3 = CreateScopeKernelNode(8, 0, "scopeA", true, true, false, false, true);
auto scopeNodePlaceholderA_4 = CreateScopeKernelNode(9, 0, "scopeA", true, true, false, false, true);
auto scopeEndA = CreateScopeKernelNode(10, 0, "scopeA", true, true, false, true, false);
auto node2 = CreateKernelNode(11, 0, true);
auto scopeNodePlaceholderB_3 = CreateScopeKernelNode(12, 0, "", true, true, false, false, true);
auto scopeNodePlaceholderB_4 = CreateScopeKernelNode(13, 0, "", true, true, false, false, true);
auto scopeEndB = CreateScopeKernelNode(14, 0, "", false, true, false, true, false);
graph->UpdateNodeScopeBitFlags();
EXPECT_FALSE(node1->IsFusible());
EXPECT_EQ(node1->GetScopeBitFlags().count(), 1);
EXPECT_TRUE(node1->GetScopeBitFlags().test(0));
EXPECT_FALSE(node2->IsFusible());
EXPECT_EQ(node2->GetScopeBitFlags().count(), 0);
}
TEST_F(TestScopeSplitRange, TestNodeWithMultiScopeSubsetUnfusible) {
graph->scopeNameToIdx["scopeA"] = 0;
auto scopeBeginA = CreateScopeKernelNode(1, 0, "scopeA", true, true, true, false, false);
auto scopeNodePlaceholderA_1 = CreateScopeKernelNode(2, 0, "scopeA", true, true, false, false, true);
auto scopeNodePlaceholderA_2 = CreateScopeKernelNode(3, 0, "scopeA", true, true, false, false, true);
auto scopeBeginB = CreateScopeKernelNode(4, 0, "", false, true, true, false, false);
auto scopeNodePlaceholderB_1 = CreateScopeKernelNode(5, 0, "", true, true, false, false, true);
auto scopeNodePlaceholderB_2 = CreateScopeKernelNode(6, 0, "", true, true, false, false, true);
auto node1 = CreateKernelNode(7, 0, true);
auto scopeNodePlaceholderB_3 = CreateScopeKernelNode(8, 0, "", true, true, false, false, true);
auto scopeNodePlaceholderB_4 = CreateScopeKernelNode(9, 0, "", true, true, false, false, true);
auto scopeEndB = CreateScopeKernelNode(10, 0, "", false, true, false, true, false);
auto node2 = CreateKernelNode(11, 0, true);
auto scopeNodePlaceholderA_3 = CreateScopeKernelNode(12, 0, "scopeA", true, true, false, false, true);
auto scopeNodePlaceholderA_4 = CreateScopeKernelNode(13, 0, "scopeA", true, true, false, false, true);
auto scopeEndA = CreateScopeKernelNode(14, 0, "scopeA", true, true, false, true, false);
graph->UpdateNodeScopeBitFlags();
EXPECT_FALSE(node1->IsFusible());
EXPECT_EQ(node1->GetScopeBitFlags().count(), 1);
EXPECT_TRUE(node1->GetScopeBitFlags().test(0));
EXPECT_TRUE(node2->IsFusible());
EXPECT_EQ(node2->GetScopeBitFlags().count(), 1);
EXPECT_TRUE(node2->GetScopeBitFlags().test(0));
}