• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <sstream>
17 
18 #include <gtest/gtest.h>
19 
20 #include "libabckit/include/c/abckit.h"
21 #include "libabckit/include/c/isa/isa_dynamic.h"
22 #include "helpers/helpers_runtime.h"
23 
24 #include "helpers/helpers.h"
25 #include "libabckit/src/logger.h"
26 
27 namespace {
28 
29 constexpr AbckitApiVersion VERSION = ABCKIT_VERSION_RELEASE_1_0_0;
30 auto *g_impl = AbckitGetApiImpl(VERSION);
31 const AbckitInspectApi *g_implI = AbckitGetInspectApiImpl(VERSION);
32 const AbckitGraphApi *g_implG = AbckitGetGraphApiImpl(VERSION);
33 const AbckitIsaApiDynamic *g_dynG = AbckitGetIsaApiDynamicImpl(VERSION);
34 const AbckitModifyApi *g_implM = AbckitGetModifyApiImpl(VERSION);
35 
36 struct ConstantInfo {
37     std::string path;
38     std::string objName;    // Config
39     std::string fieldName;  // isDebug
40     bool fieldValue;        // field is constant true or false
41 };
42 
43 using ConstantInfoIndexType = uint32_t;
44 using SuspectsType = std::unordered_map<AbckitCoreImportDescriptor *, std::vector<ConstantInfoIndexType>>;
45 constexpr ConstantInfoIndexType INVALID_INDEX = std::numeric_limits<ConstantInfoIndexType>::max();
46 
47 template <class ModuleCallBack>
EnumerateModules(const ModuleCallBack & cb,AbckitFile * file)48 inline void EnumerateModules(const ModuleCallBack &cb, AbckitFile *file)
49 {
50     LIBABCKIT_LOG_FUNC;
51 
52     g_implI->fileEnumerateModules(file, (void *)(&cb), [](AbckitCoreModule *mod, void *data) {
53         const auto &cb = *((ModuleCallBack *)(data));
54         cb(mod);
55         return true;
56     });
57 }
58 
59 template <class FunctionCallBack>
EnumerateModuleTopLevelFunctions(AbckitCoreModule * mod,const FunctionCallBack & cb)60 inline void EnumerateModuleTopLevelFunctions(AbckitCoreModule *mod, const FunctionCallBack &cb)
61 {
62     LIBABCKIT_LOG_FUNC;
63 
64     g_implI->moduleEnumerateTopLevelFunctions(mod, (void *)(&cb), [](AbckitCoreFunction *method, void *data) {
65         const auto &cb = *((FunctionCallBack *)data);
66         cb(method);
67         return true;
68     });
69 }
70 
71 template <class ClassCallBack>
EnumerateModuleClasses(AbckitCoreModule * mod,const ClassCallBack & cb)72 inline void EnumerateModuleClasses(AbckitCoreModule *mod, const ClassCallBack &cb)
73 {
74     LIBABCKIT_LOG_FUNC;
75 
76     g_implI->moduleEnumerateClasses(mod, (void *)(&cb), [](AbckitCoreClass *klass, void *data) {
77         const auto &cb = *((ClassCallBack *)data);
78         cb(klass);
79         return true;
80     });
81 }
82 
83 template <class MethodCallBack>
EnumerateClassMethods(AbckitCoreClass * klass,const MethodCallBack & cb)84 inline void EnumerateClassMethods(AbckitCoreClass *klass, const MethodCallBack &cb)
85 {
86     LIBABCKIT_LOG_FUNC;
87     g_implI->classEnumerateMethods(klass, (void *)(&cb), [](AbckitCoreFunction *method, void *data) {
88         const auto &cb = *((MethodCallBack *)data);
89         cb(method);
90         return true;
91     });
92 }
93 
94 template <class FunctionCallBack>
EnumerateModuleFunctions(AbckitCoreModule * mod,const FunctionCallBack & cb)95 inline void EnumerateModuleFunctions(AbckitCoreModule *mod, const FunctionCallBack &cb)
96 {
97     LIBABCKIT_LOG_FUNC;
98     // NOTE: currently we can only enumerate class methods and top level functions. need to update.
99     EnumerateModuleTopLevelFunctions(mod, cb);
100     EnumerateModuleClasses(mod, [&](AbckitCoreClass *klass) { EnumerateClassMethods(klass, cb); });
101 }
102 
103 template <class UserCallBack>
EnumerateInstUsers(AbckitInst * inst,const UserCallBack & cb)104 inline void EnumerateInstUsers(AbckitInst *inst, const UserCallBack &cb)
105 {
106     LIBABCKIT_LOG_FUNC;
107 
108     g_implG->iVisitUsers(inst, (void *)(&cb), [](AbckitInst *user, void *data) {
109         const auto &cb = *((UserCallBack *)data);
110         cb(user);
111         return true;
112     });
113 }
114 
115 struct CapturedData {
116     void *callback = nullptr;
117     const AbckitGraphApi *implG = nullptr;
118 };
119 
120 template <class InstCallBack>
EnumerateGraphInsts(AbckitGraph * graph,const InstCallBack & cb)121 inline void EnumerateGraphInsts(AbckitGraph *graph, const InstCallBack &cb)
122 {
123     CapturedData captured {(void *)(&cb), g_implG};
124 
125     g_implG->gVisitBlocksRpo(graph, &captured, [](AbckitBasicBlock *bb, void *data) {
126         auto *captured = reinterpret_cast<CapturedData *>(data);
127         const auto &cb = *((InstCallBack *)(captured->callback));
128         auto *implG = captured->implG;
129         for (auto *inst = implG->bbGetFirstInst(bb); inst != nullptr; inst = implG->iGetNext(inst)) {
130             cb(inst);
131         }
132         return true;
133     });
134 }
135 
136 template <class InstCallBack>
GraphInstsFindIf(AbckitGraph * graph,const InstCallBack & cb)137 inline AbckitInst *GraphInstsFindIf(AbckitGraph *graph, const InstCallBack &cb)
138 {
139     AbckitInst *ret = nullptr;
140     bool found = false;
141     EnumerateGraphInsts(graph, [&](AbckitInst *inst) {
142         if (!found && cb(inst)) {
143             found = true;
144             ret = inst;
145         }
146     });
147 
148     return ret;
149 }
150 
151 template <class ImportCallBack>
EnumerateModuleImports(AbckitCoreModule * mod,const ImportCallBack & cb)152 inline void EnumerateModuleImports(AbckitCoreModule *mod, const ImportCallBack &cb)
153 {
154     LIBABCKIT_LOG_FUNC;
155 
156     g_implI->moduleEnumerateImports(mod, (void *)(&cb), [](AbckitCoreImportDescriptor *i, void *data) {
157         const auto &cb = *((ImportCallBack *)(data));
158         cb(i);
159         return true;
160     });
161 }
162 
GetSuspects(AbckitCoreModule * mod,const std::vector<ConstantInfo> & constants,SuspectsType & suspects)163 bool GetSuspects(AbckitCoreModule *mod, const std::vector<ConstantInfo> &constants, SuspectsType &suspects)
164 {
165     EnumerateModuleImports(mod, [&](AbckitCoreImportDescriptor *id) {
166         auto importName = g_implI->abckitStringToString(g_implI->importDescriptorGetName(id));
167         auto *importedModule = g_implI->importDescriptorGetImportedModule(id);
168         auto path = g_implI->abckitStringToString(g_implI->moduleGetName(importedModule));
169         for (ConstantInfoIndexType i = 0; i < constants.size(); ++i) {
170             const auto &constInfo = constants[i];
171             if (constInfo.path != path || constInfo.objName != importName) {
172                 continue;
173             }
174             auto iter = suspects.find(id);
175             if (iter == suspects.end()) {
176                 std::vector<ConstantInfoIndexType> vec = {i};
177                 suspects.emplace(id, vec);
178             } else {
179                 iter->second.push_back(i);
180             }
181         }
182     });
183     return !suspects.empty();
184 }
185 
GetConstantInfoIndex(AbckitInst * inst,const std::vector<ConstantInfo> & constants,const SuspectsType & suspects)186 ConstantInfoIndexType GetConstantInfoIndex(AbckitInst *inst, const std::vector<ConstantInfo> &constants,
187                                            const SuspectsType &suspects)
188 {
189     if (g_dynG->iGetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDOBJBYNAME) {
190         return INVALID_INDEX;
191     }
192     auto *ldExternalModuleVar = g_implG->iGetInput(inst, 0);
193     if (g_dynG->iGetOpcode(ldExternalModuleVar) != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDEXTERNALMODULEVAR) {
194         return INVALID_INDEX;
195     }
196     auto *id = g_dynG->iGetImportDescriptor(ldExternalModuleVar);
197     auto iter = suspects.find(id);
198     if (iter == suspects.end()) {
199         return INVALID_INDEX;
200     }
201     const auto &constInfoIndexes = iter->second;
202     const auto propName = g_implI->abckitStringToString(g_implG->iGetString(inst));  // "isDebug"
203     for (auto index : constInfoIndexes) {
204         EXPECT_TRUE(index >= 0);
205         EXPECT_TRUE(index < constants.size());
206         if (constants[index].fieldName == propName) {
207             return index;
208         }
209     }
210 
211     return INVALID_INDEX;
212 }
213 
ReplaceUsers(AbckitInst * oldInst,AbckitInst * newInst)214 void ReplaceUsers(AbckitInst *oldInst, AbckitInst *newInst)
215 {
216     EnumerateInstUsers(oldInst, [&](AbckitInst *user) {
217         auto inputCount = g_implG->iGetInputCount(user);
218         for (uint64_t i = 0; i < inputCount; ++i) {
219             if (g_implG->iGetInput(user, i) == oldInst) {
220                 g_implG->iSetInput(user, newInst, i);
221             }
222         }
223     });
224 }
225 
ReplaceLdObjByNameWithBoolean(AbckitInst * ldObjByName,ConstantInfoIndexType constInfoIndex,const std::vector<ConstantInfo> & constants)226 void ReplaceLdObjByNameWithBoolean(AbckitInst *ldObjByName, ConstantInfoIndexType constInfoIndex,
227                                    const std::vector<ConstantInfo> &constants)
228 {
229     bool fieldVal = constants[constInfoIndex].fieldValue;
230     auto *bb = g_implG->iGetBasicBlock(ldObjByName);
231     auto *graph = g_implG->bbGetGraph(bb);
232     auto *value = fieldVal ? g_dynG->iCreateLdtrue(graph) : g_dynG->iCreateLdfalse(graph);
233     g_implG->iInsertAfter(value, ldObjByName);
234     ReplaceUsers(ldObjByName, value);
235 
236     auto *ldExternalModuleVar = g_implG->iGetInput(ldObjByName, 0);
237     bool isRemovable = true;
238     std::vector<AbckitInst *> checks;
239     EnumerateInstUsers(ldExternalModuleVar, [&](AbckitInst *inst) {
240         if (inst != ldObjByName) {
241             if (g_dynG->iGetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_THROW_UNDEFINEDIFHOLEWITHNAME) {
242                 isRemovable = false;
243             } else {
244                 checks.emplace_back(inst);
245             }
246         }
247     });
248 
249     if (isRemovable) {
250         for (auto *check : checks) {
251             g_implG->iRemove(check);
252         }
253         g_implG->iRemove(ldExternalModuleVar);
254     }
255     g_implG->iRemove(ldObjByName);
256 }
257 
ReplaceModuleVarByConstant(AbckitGraph * graph,const std::vector<ConstantInfo> & constants,const SuspectsType & suspects)258 bool ReplaceModuleVarByConstant(AbckitGraph *graph, const std::vector<ConstantInfo> &constants,
259                                 const SuspectsType &suspects)
260 {
261     // find the following patter:
262     // ...
263     // 1. ldExternalModuleVar (importdescriptor)
264     // 2. ldObjByName v1, "isDebug"
265     //
266     // and replace it by
267     // 3. ldtrue
268 
269     std::unordered_map<AbckitInst *, ConstantInfoIndexType> moduleVars;
270     EnumerateGraphInsts(graph, [&](AbckitInst *inst) {
271         auto constInfoIndex = GetConstantInfoIndex(inst, constants, suspects);
272         if (constInfoIndex != INVALID_INDEX) {
273             moduleVars.emplace(inst, constInfoIndex);
274         }
275     });
276     for (auto &[ldObjByName, constInfoIndex] : moduleVars) {
277         EXPECT_TRUE(ldObjByName != nullptr);
278         EXPECT_TRUE(constInfoIndex != INVALID_INDEX);
279         ReplaceLdObjByNameWithBoolean(ldObjByName, constInfoIndex, constants);
280     }
281 
282     return !moduleVars.empty();
283 }
284 
GetInstAsBool(AbckitInst * inst)285 bool GetInstAsBool(AbckitInst *inst)
286 {
287     switch (g_dynG->iGetOpcode(inst)) {
288         case ABCKIT_ISA_API_DYNAMIC_OPCODE_CONSTANT:
289             return g_implG->iGetConstantValueI64(inst) != 0;
290         case ABCKIT_ISA_API_DYNAMIC_OPCODE_LDTRUE:
291         case ABCKIT_ISA_API_DYNAMIC_OPCODE_ISTRUE:
292         case ABCKIT_ISA_API_DYNAMIC_OPCODE_CALLRUNTIME_ISTRUE:
293             return true;
294         case ABCKIT_ISA_API_DYNAMIC_OPCODE_LDFALSE:
295         case ABCKIT_ISA_API_DYNAMIC_OPCODE_ISFALSE:
296         case ABCKIT_ISA_API_DYNAMIC_OPCODE_CALLRUNTIME_ISFALSE:
297             return false;
298         default:
299             EXPECT_TRUE(false);  // UNREACHABLE
300     }
301     return false;
302 }
303 
CanBeRemoved(AbckitInst * inst)304 bool CanBeRemoved(AbckitInst *inst)
305 {
306     switch (g_dynG->iGetOpcode(inst)) {
307         case ABCKIT_ISA_API_DYNAMIC_OPCODE_CONSTANT:
308         case ABCKIT_ISA_API_DYNAMIC_OPCODE_LDTRUE:
309         case ABCKIT_ISA_API_DYNAMIC_OPCODE_ISTRUE:
310         case ABCKIT_ISA_API_DYNAMIC_OPCODE_LDFALSE:
311         case ABCKIT_ISA_API_DYNAMIC_OPCODE_ISFALSE:
312             return true;
313         default:
314             return false;
315     }
316 }
317 
RemoveUnusedInsts(AbckitGraph * graph)318 bool RemoveUnusedInsts(AbckitGraph *graph)
319 {
320     bool hasGraphChanged = false;
321     bool continueLoop = true;
322 
323     while (continueLoop) {
324         std::vector<AbckitInst *> removableInsts;
325         EnumerateGraphInsts(graph, [&](AbckitInst *inst) {
326             if (g_implG->iGetUserCount(inst) == 0 && CanBeRemoved(inst)) {
327                 removableInsts.emplace_back(inst);
328             }
329         });
330 
331         if (removableInsts.empty()) {
332             continueLoop = false;
333         } else {
334             EXPECT_TRUE(!removableInsts.empty());
335             for (auto *inst : removableInsts) {
336                 g_implG->iRemove(inst);
337             }
338             hasGraphChanged = true;
339         }
340     }
341 
342     return hasGraphChanged;
343 }
344 
LoweringConstants(AbckitGraph * graph)345 bool LoweringConstants(AbckitGraph *graph)
346 {
347     bool hasGraphChanged = false;
348     auto doCb = [graph, &hasGraphChanged]() -> bool {
349         std::vector<AbckitInst *> users;
350 
351         auto instFindUsers = [&users](AbckitInst *user) {
352             auto userOp = g_dynG->iGetOpcode(user);
353             if (userOp == ABCKIT_ISA_API_DYNAMIC_OPCODE_ISTRUE || userOp == ABCKIT_ISA_API_DYNAMIC_OPCODE_ISFALSE ||
354                 userOp == ABCKIT_ISA_API_DYNAMIC_OPCODE_CALLRUNTIME_ISTRUE ||
355                 userOp == ABCKIT_ISA_API_DYNAMIC_OPCODE_CALLRUNTIME_ISFALSE) {
356                 users.emplace_back(user);
357             }
358         };
359 
360         AbckitInst *ldBool = GraphInstsFindIf(graph, [&](AbckitInst *inst) {
361             auto op = g_dynG->iGetOpcode(inst);
362             if (op != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDTRUE && op != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDFALSE) {
363                 return false;
364             }
365 
366             EnumerateInstUsers(inst, instFindUsers);
367 
368             return !users.empty();
369         });
370 
371         if (ldBool == nullptr) {
372             return false;
373         }
374 
375         auto ldBoolVal = GetInstAsBool(ldBool);
376         for (auto *isBool : users) {
377             auto isBoolVal = GetInstAsBool(isBool);
378             auto *ldBool = ldBoolVal == isBoolVal ? g_dynG->iCreateLdtrue(graph) : g_dynG->iCreateLdfalse(graph);
379             g_implG->iInsertAfter(ldBool, isBool);
380             ReplaceUsers(isBool, ldBool);
381             g_implG->iRemove(isBool);
382         }
383 
384         hasGraphChanged = true;
385 
386         return true;
387     };
388 
389     while (doCb()) {
390     }
391 
392     hasGraphChanged |= RemoveUnusedInsts(graph);
393 
394     return hasGraphChanged;
395 }
396 
IsEliminatableIfInst(AbckitInst * inst)397 bool IsEliminatableIfInst(AbckitInst *inst)
398 {
399     if (g_dynG->iGetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_IF || g_implG->iGetInputCount(inst) != 2U) {
400         return false;
401     }
402     auto *ldBool = g_implG->iGetInput(inst, 0);
403     auto op = g_dynG->iGetOpcode(ldBool);
404     if (op != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDTRUE && op != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDFALSE) {
405         return false;
406     }
407     auto *constInst = g_implG->iGetInput(inst, 1);
408     return g_dynG->iGetOpcode(constInst) == ABCKIT_ISA_API_DYNAMIC_OPCODE_CONSTANT;
409 }
410 
DeleteUnreachableBranch(AbckitInst * ifInst)411 void DeleteUnreachableBranch(AbckitInst *ifInst)
412 {
413     // compute result of If compare
414     auto *ldBool = g_implG->iGetInput(ifInst, 0);     // 0: ldtrue or ldfalse
415     auto *constInst = g_implG->iGetInput(ifInst, 1);  // 1: ConstantInst
416     auto valLeft = GetInstAsBool(ldBool);
417     auto valRight = GetInstAsBool(constInst);
418     auto conditionCode = g_dynG->iGetConditionCode(ifInst);
419 
420     EXPECT_TRUE(conditionCode == ABCKIT_ISA_API_DYNAMIC_CONDITION_CODE_CC_EQ ||
421                 conditionCode == ABCKIT_ISA_API_DYNAMIC_CONDITION_CODE_CC_NE);
422     bool result = (conditionCode == ABCKIT_ISA_API_DYNAMIC_CONDITION_CODE_CC_EQ && valLeft == valRight) ||
423                   (conditionCode == ABCKIT_ISA_API_DYNAMIC_CONDITION_CODE_CC_NE && valLeft != valRight);
424 
425     auto *bb = g_implG->iGetBasicBlock(ifInst);
426     // if true ==> delete false branch
427     g_implG->iRemove(ifInst);
428 
429     g_implG->bbDisconnectSuccBlock(bb, result ? 1 : 0);
430     g_implG->gRunPassRemoveUnreachableBlocks(g_implG->bbGetGraph(bb));
431 }
432 
433 template <class FunctionCallBack>
TransformMethod(AbckitCoreFunction * f,const FunctionCallBack & cb)434 inline void TransformMethod(AbckitCoreFunction *f, const FunctionCallBack &cb)
435 {
436     cb(g_implI->functionGetFile(f), f);
437 }
438 
EliminateBranchWithSuspect(AbckitCoreFunction * method,const std::vector<ConstantInfo> & constants,const SuspectsType & suspects)439 void EliminateBranchWithSuspect(AbckitCoreFunction *method, const std::vector<ConstantInfo> &constants,
440                                 const SuspectsType &suspects)
441 {
442     TransformMethod(method, [&](AbckitFile *, AbckitCoreFunction *method) {
443         AbckitGraph *graph = g_implI->createGraphFromFunction(method);
444         bool hasGraphChanged = ReplaceModuleVarByConstant(graph, constants, suspects);
445         bool continueLoop = true;
446         while (continueLoop) {
447             hasGraphChanged |= LoweringConstants(graph);
448 
449             auto *ifInst = GraphInstsFindIf(graph, [&](AbckitInst *inst) { return IsEliminatableIfInst(inst); });
450 
451             if (ifInst == nullptr) {
452                 continueLoop = false;
453             } else {
454                 DeleteUnreachableBranch(ifInst);
455                 hasGraphChanged = true;
456             }
457         }
458 
459         if (hasGraphChanged) {
460             g_implM->functionSetGraph(method, graph);
461         }
462         g_impl->destroyGraph(graph);
463     });
464 }
465 
Run(const std::vector<ConstantInfo> & constants,AbckitFile * file)466 void Run(const std::vector<ConstantInfo> &constants, AbckitFile *file)
467 {
468     if (constants.empty()) {
469         return;
470     }
471     EnumerateModules(
472         [&](AbckitCoreModule *mod) {
473             SuspectsType suspects;
474             if (!GetSuspects(mod, constants, suspects)) {
475                 return;
476             }
477             EnumerateModuleFunctions(
478                 mod, [&](AbckitCoreFunction *func) { EliminateBranchWithSuspect(func, constants, suspects); });
479         },
480         file);
481 }
482 }  // namespace
483 
484 namespace libabckit::test {
485 
MethodHasBranch(const std::string & moduleName,const std::string & methodName,AbckitFile * file)486 static bool MethodHasBranch(const std::string &moduleName, const std::string &methodName, AbckitFile *file)
487 {
488     bool found = false;
489     bool ret = false;
490     EnumerateModules(
491         [&](AbckitCoreModule *mod) {
492             if (g_implI->abckitStringToString(g_implI->moduleGetName(mod)) != moduleName) {
493                 return;
494             }
495             EnumerateModuleFunctions(mod, [&](AbckitCoreFunction *func) {
496                 if (found || g_implI->abckitStringToString(g_implI->functionGetName(func)) != methodName) {
497                     return;
498                 }
499                 found = true;
500                 auto *graph = g_implI->createGraphFromFunction(func);
501                 auto *ifInst = GraphInstsFindIf(graph, [&](AbckitInst *inst) {
502                     return g_dynG->iGetOpcode(inst) == ABCKIT_ISA_API_DYNAMIC_OPCODE_IF;
503                 });
504                 ret = ifInst != nullptr;
505                 g_impl->destroyGraph(graph);
506             });
507         },
508         file);
509 
510     EXPECT_TRUE(found);
511     return ret;
512 }
513 
514 class AbckitScenarioCTestClean : public ::testing::Test {};
515 
516 static constexpr bool CONFIG_IS_DEBUG_ORIGIN_VALUE = false;
517 
518 /*
519  * @param value: the value of isDebug when we handle, it can be different from configIsDebugOriginValue
520  */
GetExpectOutput(bool value)521 static std::string GetExpectOutput(bool value)
522 {
523     std::stringstream expectOutput;
524     expectOutput << std::boolalpha;
525     expectOutput << "Config.isDebug = " << value << std::endl;
526     expectOutput << "myfoo: Config.isDebug is " << value << std::endl;
527     expectOutput << "Mybar.test1: Config.isDebug is " << value << std::endl;
528     expectOutput << "Mybar.test2: Config.isDebug is " << value << std::endl;
529     return expectOutput.str();
530 }
531 
GeneralBranchEliminatorTest(bool configIsDebugFinal)532 static void GeneralBranchEliminatorTest(bool configIsDebugFinal)
533 {
534     constexpr auto INPUT_PATH = ABCKIT_ABC_DIR "clean_scenarios/c_api/dynamic/branch_eliminator/branch_eliminator.abc";
535     AbckitFile *file = g_impl->openAbc(INPUT_PATH, strlen(INPUT_PATH));
536     ASSERT_NE(file, nullptr);
537 
538     const auto actualOutput = helpers::ExecuteDynamicAbc(INPUT_PATH, "branch_eliminator");
539     auto expectedOutput = GetExpectOutput(CONFIG_IS_DEBUG_ORIGIN_VALUE);
540     EXPECT_TRUE(helpers::Match(actualOutput, expectedOutput));
541 
542     ASSERT_TRUE(MethodHasBranch("modules/myfoo", "myfoo", file));
543     ASSERT_TRUE(MethodHasBranch("modules/mybar", "test1", file));
544     ASSERT_TRUE(MethodHasBranch("modules/mybar", "test2", file));
545     // Delete true branch
546     const std::vector<ConstantInfo> infos = {{"modules/config", "Config", "isDebug", configIsDebugFinal}};
547     Run(infos, file);
548     ASSERT_FALSE(MethodHasBranch("modules/myfoo", "myfoo", file));
549     ASSERT_FALSE(MethodHasBranch("modules/mybar", "test1", file));
550     ASSERT_FALSE(MethodHasBranch("modules/mybar", "test2", file));
551 
552     constexpr auto OUTPUT_PATH =
553         ABCKIT_ABC_DIR "clean_scenarios/c_api/dynamic/branch_eliminator/branch_eliminator_modified.abc";
554     g_impl->writeAbc(file, OUTPUT_PATH, strlen(OUTPUT_PATH));
555     g_impl->closeFile(file);
556     auto output = helpers::ExecuteDynamicAbc(OUTPUT_PATH, "branch_eliminator");
557     auto expectOutput2 = GetExpectOutput(configIsDebugFinal);
558     ASSERT_EQ(output, expectOutput2);
559 }
560 
561 // Test: test-kind=scenario, abc-kind=ArkTS1, category=positive, extension=c
TEST_F(AbckitScenarioCTestClean,LibAbcKitTestDynamicBranchEliminatorClean1)562 TEST_F(AbckitScenarioCTestClean, LibAbcKitTestDynamicBranchEliminatorClean1)
563 {
564     GeneralBranchEliminatorTest(false);
565 }
566 
567 // Test: test-kind=scenario, abc-kind=ArkTS1, category=positive, extension=c
TEST_F(AbckitScenarioCTestClean,LibAbcKitTestDynamicBranchEliminatorClean2)568 TEST_F(AbckitScenarioCTestClean, LibAbcKitTestDynamicBranchEliminatorClean2)
569 {
570     GeneralBranchEliminatorTest(true);
571 }
572 
573 }  // namespace libabckit::test
574