From 9baa20a9232fefe7e7cb1c800671d278358288c7 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 28 Aug 2025 20:15:01 +0200 Subject: [PATCH 1/3] Check if the string is same when optimizing numeric strings --- src/engine/internal/llvm/llvmbuildutils.cpp | 11 +++++-- .../llvm/code_analyzer/list_type_analysis.cpp | 30 +++++++++++++++++++ .../code_analyzer/variable_type_analysis.cpp | 25 ++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index c8a70426..216bd327 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -434,8 +434,15 @@ Compiler::StaticType LLVMBuildUtils::optimizeRegisterType(const LLVMRegister *re Compiler::StaticType ret = reg->type(); // Optimize string constants that represent numbers - if (reg->isConst() && reg->type() == Compiler::StaticType::String && reg->constValue().isValidNumber()) - ret = Compiler::StaticType::Number; + if (reg->isConst() && reg->type() == Compiler::StaticType::String) { + const Value &value = reg->constValue(); + Value numberValue(value.toDouble()); + + // Apply this optimization only if the number matches the string + // TODO: Exclude unsafe constants + if (value.isValidNumber() && numberValue.toString() == value.toString()) + ret = Compiler::StaticType::Number; + } return ret; } diff --git a/test/llvm/code_analyzer/list_type_analysis.cpp b/test/llvm/code_analyzer/list_type_analysis.cpp index 05e76b13..373c611c 100644 --- a/test/llvm/code_analyzer/list_type_analysis.cpp +++ b/test/llvm/code_analyzer/list_type_analysis.cpp @@ -152,6 +152,36 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear) ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Number); } +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear_DifferentString) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::String, "1.0"); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + + // String "1.0" does NOT get optimized to Number because it would convert to "1" + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::String); +} + TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearListOperation) { LLVMCodeAnalyzer analyzer; diff --git a/test/llvm/code_analyzer/variable_type_analysis.cpp b/test/llvm/code_analyzer/variable_type_analysis.cpp index a09c4eb0..6495c99d 100644 --- a/test/llvm/code_analyzer/variable_type_analysis.cpp +++ b/test/llvm/code_analyzer/variable_type_analysis.cpp @@ -107,6 +107,31 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization) ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); } +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization_DifferentString) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::String, "1.0"); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + analyzer.analyzeScript(list); + + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + // String "1.0" does NOT get optimized to Number because it would convert to "1" + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::String); +} + TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopSingleWrite) { LLVMCodeAnalyzer analyzer; From 378ff2882fd1e81e0c0f13ad22c8d2832bb1721d Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 30 Aug 2025 19:09:55 +0200 Subject: [PATCH 2/3] Pass LLVMBuildUtils to LLVMCodeAnalyzer --- src/engine/internal/llvm/llvmcodeanalyzer.cpp | 5 + src/engine/internal/llvm/llvmcodeanalyzer.h | 6 + src/engine/internal/llvm/llvmcodebuilder.cpp | 1 + .../llvm/code_analyzer/list_type_analysis.cpp | 207 +++++++++--------- .../code_analyzer/mixed_type_analysis.cpp | 77 ++++--- .../code_analyzer/variable_type_analysis.cpp | 187 ++++++++-------- 6 files changed, 249 insertions(+), 234 deletions(-) diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.cpp b/src/engine/internal/llvm/llvmcodeanalyzer.cpp index f41b54eb..6a52e0a2 100644 --- a/src/engine/internal/llvm/llvmcodeanalyzer.cpp +++ b/src/engine/internal/llvm/llvmcodeanalyzer.cpp @@ -12,6 +12,11 @@ static const std::unordered_set BEGIN_LOOP_INSTRUCTIONS = static const std::unordered_set LIST_WRITE_INSTRUCTIONS = { LLVMInstruction::Type::AppendToList, LLVMInstruction::Type::InsertToList, LLVMInstruction::Type::ListReplace }; +LLVMCodeAnalyzer::LLVMCodeAnalyzer(const LLVMBuildUtils &utils) : + m_utils(utils) +{ +} + void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const { std::unordered_set typeAssignedInstructions; diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.h b/src/engine/internal/llvm/llvmcodeanalyzer.h index 4c9b6a4a..94e8ef1d 100644 --- a/src/engine/internal/llvm/llvmcodeanalyzer.h +++ b/src/engine/internal/llvm/llvmcodeanalyzer.h @@ -8,12 +8,16 @@ namespace libscratchcpp { +class LLVMBuildUtils; class LLVMInstructionList; class LLVMInstruction; class LLVMCodeAnalyzer { public: + LLVMCodeAnalyzer(const LLVMBuildUtils &utils); + LLVMCodeAnalyzer(const LLVMCodeAnalyzer &) = delete; + void analyzeScript(const LLVMInstructionList &script) const; private: @@ -50,6 +54,8 @@ class LLVMCodeAnalyzer bool isProcedureCall(const LLVMInstruction *ins) const; Compiler::StaticType writeType(LLVMInstruction *ins) const; + + const LLVMBuildUtils &m_utils; }; } // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index 07dcf003..72dd67bf 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -28,6 +28,7 @@ LLVMCodeBuilder::LLVMCodeBuilder(LLVMCompilerContext *ctx, BlockPrototype *proce m_module(ctx->module()), m_builder(m_llvmCtx), m_utils(ctx, m_builder, codeType), + m_codeAnalyzer(m_utils), m_procedurePrototype(procedurePrototype), m_defaultWarp(procedurePrototype ? procedurePrototype->warp() : false), m_warp(m_defaultWarp), diff --git a/test/llvm/code_analyzer/list_type_analysis.cpp b/test/llvm/code_analyzer/list_type_analysis.cpp index 373c611c..3c59fdb8 100644 --- a/test/llvm/code_analyzer/list_type_analysis.cpp +++ b/test/llvm/code_analyzer/list_type_analysis.cpp @@ -1,15 +1,41 @@ +#include +#include #include #include +#include +#include #include #include #include +#include #include using namespace libscratchcpp; -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, FirstListWrite) +class LLVMCodeAnalyzer_ListTypeAnalysis : public testing::Test +{ + public: + void SetUp() override + { + auto engine = m_project.engine(); + m_ctx = std::make_unique(engine.get(), &m_target); + m_builder = std::make_unique>(*m_ctx->llvmCtx()); + m_utils = std::make_unique(m_ctx.get(), *m_builder, Compiler::CodeType::Script); + m_analyzer = std::make_unique(*m_utils); + } + + std::unique_ptr m_analyzer; + + private: + Project m_project; + Target m_target; + std::unique_ptr m_ctx; + std::unique_ptr> m_builder; + std::unique_ptr m_utils; +}; + +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, FirstListWrite) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -19,15 +45,14 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, FirstListWrite) appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(appendList); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // First write should have Unknown targetType (no previous type) ASSERT_EQ(appendList->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, SecondListWrite) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, SecondListWrite) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -43,7 +68,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, SecondListWrite) appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(appendList2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // First write has no previous type ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Unknown); @@ -52,9 +77,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, SecondListWrite) ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MultipleWritesSameType_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, MultipleWritesSameType_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -80,16 +104,15 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MultipleWritesSameType_AfterClear) appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(appendList3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); ASSERT_EQ(appendList2->targetType, Compiler::StaticType::String); ASSERT_EQ(appendList3->targetType, Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MultipleWritesDifferentTypes_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, MultipleWritesDifferentTypes_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -115,16 +138,15 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MultipleWritesDifferentTypes_AfterClear) appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(appendList3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Number); ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -144,7 +166,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear) appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(appendList2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); @@ -152,9 +174,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear) ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear_DifferentString) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear_DifferentString) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -174,7 +195,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear_DifferentS appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(appendList2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); @@ -182,9 +203,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear_DifferentS ASSERT_EQ(appendList2->targetType, Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearListOperation) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, ClearListOperation) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -204,7 +224,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearListOperation) appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(appendList2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Unknown); @@ -212,9 +232,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearListOperation) ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Void); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ProcedureCall) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, ProcedureCall) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -237,7 +256,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ProcedureCall) appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(appendList2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); @@ -245,9 +264,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ProcedureCall) ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MixedWriteOperationsSameType_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, MixedWriteOperationsSameType_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -277,16 +295,15 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MixedWriteOperationsSameType_AfterClear) replaceList->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(replaceList); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList->targetType, Compiler::StaticType::Void); ASSERT_EQ(insertList->targetType, Compiler::StaticType::String); ASSERT_EQ(replaceList->targetType, Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MixedWriteOperationsDifferentTypes_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, MixedWriteOperationsDifferentTypes_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -322,7 +339,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MixedWriteOperationsDifferentTypes_After appendList2->args.push_back({ Compiler::StaticType::Unknown, &value4 }); list.addInstruction(appendList2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList->targetType, Compiler::StaticType::Void); ASSERT_EQ(insertList->targetType, Compiler::StaticType::String); @@ -330,9 +347,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MixedWriteOperationsDifferentTypes_After ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopSingleWrite) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, LoopSingleWrite) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -348,15 +364,14 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopSingleWrite) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Loop convergence: first iteration Unknown, subsequent iterations Number ASSERT_EQ(appendList->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopSingleWrite_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, LoopSingleWrite_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -376,14 +391,13 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopSingleWrite_AfterClear) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList->targetType, Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfStatement_IfBranch) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfStatement_IfBranch) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -409,7 +423,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfStatement_IfBranch) appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(appendList2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); @@ -417,9 +431,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfStatement_IfBranch) ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfStatement_ElseBranch) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfStatement_ElseBranch) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -448,7 +461,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfStatement_ElseBranch) appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(appendList2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); @@ -456,9 +469,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfStatement_ElseBranch) ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfElse) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfElse) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -497,7 +509,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfElse) appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(appendList3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Void); @@ -506,9 +518,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfElse) ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInLoop) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInLoop) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -534,7 +545,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInLoop) appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(appendList2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); @@ -542,9 +553,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInLoop) ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAfterWriteInLoop) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAfterWriteInLoop) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -575,14 +585,13 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAfterWriteInLoop) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Bool); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WhileLoop) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, WhileLoop) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -625,15 +634,14 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WhileLoop) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(getItem->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, RepeatUntilLoop) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, RepeatUntilLoop) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -676,15 +684,14 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, RepeatUntilLoop) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(getItem->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopMultipleWrites_UnknownType) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, LoopMultipleWrites_UnknownType) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -706,7 +713,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopMultipleWrites_UnknownType) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // First write: Unknown (unknown before loop) ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Unknown); @@ -715,9 +722,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopMultipleWrites_UnknownType) ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopMultipleWrites_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, LoopMultipleWrites_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -744,16 +750,15 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopMultipleWrites_AfterClear) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Number | String (from loop iterations) ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentTypes) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentTypes) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -784,7 +789,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentTypes) appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(appendList3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Both writes see Unknown before the if-else ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Unknown); @@ -794,9 +799,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentTypes) ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentTypes_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentTypes_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -831,7 +835,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentTypes_AfterClear appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(appendList3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Both writes see Void (empty list) before the if-else ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); @@ -841,9 +845,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentTypes_AfterClear ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentType_IfBranch_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentType_IfBranch_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -875,7 +878,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentType_IfBranch_Af appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(appendList3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Number); @@ -884,9 +887,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentType_IfBranch_Af ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentType_ElseBranch_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentType_ElseBranch_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -921,7 +923,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentType_ElseBranch_ appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(appendList3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Number); @@ -930,9 +932,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentType_ElseBranch_ ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeLoop_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeLoop_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -964,14 +965,13 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeLoop_AfterClear) appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(appendList); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfStatement_IfBranch_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfStatement_IfBranch_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -1003,14 +1003,13 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfStatement_IfBranch_AfterCle appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(appendList); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfStatement_ElseBranch_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfStatement_ElseBranch_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -1045,14 +1044,13 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfStatement_ElseBranch_AfterC appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(appendList); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(appendList->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfElse_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfElse_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -1093,7 +1091,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfElse_AfterClear) appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(appendList3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Write before if-else establishes Bool type ASSERT_EQ(appendListBefore->targetType, Compiler::StaticType::Void); @@ -1106,9 +1104,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfElse_AfterClear) ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ComplexNestedControlFlow_AfterClear) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, ComplexNestedControlFlow_AfterClear) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList("", ""); @@ -1161,7 +1158,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ComplexNestedControlFlow_AfterClear) appendList4->args.push_back({ Compiler::StaticType::Unknown, &value4 }); list.addInstruction(appendList4); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Complex analysis with multiple execution paths and loop convergence ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Unknown); @@ -1170,9 +1167,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ComplexNestedControlFlow_AfterClear) ASSERT_EQ(appendList4->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MultipleListsSeparateAnalysis) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, MultipleListsSeparateAnalysis) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List targetList1("", ""), targetList2("", ""); @@ -1192,16 +1188,15 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MultipleListsSeparateAnalysis) appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(appendList2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Both are first writes for their respective lists ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_SingleType) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_SingleType) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List sourceList("", ""), targetList("", ""); @@ -1241,7 +1236,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_SingleType) appendList1_2->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); list.addInstruction(appendList1_2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // sourceList first write has no previous type ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Void); @@ -1256,9 +1251,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_SingleType) ASSERT_EQ(appendList1_2->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_MultipleTypes) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_MultipleTypes) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List sourceList("", ""), targetList("", ""); @@ -1305,7 +1299,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_MultipleTypes) appendList4->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); list.addInstruction(appendList4); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // sourceList has Number or Bool type at read operation ASSERT_EQ(getItem->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); @@ -1317,9 +1311,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_MultipleTypes) ASSERT_EQ(appendList4->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ChainedAssignmentsInLoop) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, ChainedAssignmentsInLoop) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List listA("a", ""), listB("b", ""), listC("c", ""); @@ -1442,7 +1435,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ChainedAssignmentsInLoop) appendA3->args.push_back({ Compiler::StaticType::Unknown, &cValue2 }); list.addInstruction(appendA3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Check the type of 'a' before the final clear operation // 'a' could be Number (from initial assignment) or String (from loop iterations) @@ -1455,9 +1448,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ChainedAssignmentsInLoop) ASSERT_EQ(getC2->targetType, expectedCType); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ListReadReturnRegType) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, ListReadReturnRegType) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List sourceList("", ""), targetList("", ""); @@ -1491,15 +1483,14 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ListReadReturnRegType) appendList1_1->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); list.addInstruction(appendList1_1); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // sourceList read return register has Number | String type (get list item can always return string) ASSERT_EQ(sourceValue.type(), Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListWriteArgType) +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListWriteArgType) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List sourceList("", ""), targetList("", ""); @@ -1533,7 +1524,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListWriteArgType) appendList1_1->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); list.addInstruction(appendList1_1); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // last write argument has Number | String type (get list item can always return string) ASSERT_EQ(appendList1_1->args.back().first, Compiler::StaticType::Number | Compiler::StaticType::String); diff --git a/test/llvm/code_analyzer/mixed_type_analysis.cpp b/test/llvm/code_analyzer/mixed_type_analysis.cpp index b1dbde86..a082ee92 100644 --- a/test/llvm/code_analyzer/mixed_type_analysis.cpp +++ b/test/llvm/code_analyzer/mixed_type_analysis.cpp @@ -1,35 +1,59 @@ +#include +#include #include #include #include +#include +#include #include #include #include +#include #include using namespace libscratchcpp; -TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, EmptyScript) +class LLVMCodeAnalyzer_MixedTypeAnalysis : public testing::Test +{ + public: + void SetUp() override + { + auto engine = m_project.engine(); + m_ctx = std::make_unique(engine.get(), &m_target); + m_builder = std::make_unique>(*m_ctx->llvmCtx()); + m_utils = std::make_unique(m_ctx.get(), *m_builder, Compiler::CodeType::Script); + m_analyzer = std::make_unique(*m_utils); + } + + std::unique_ptr m_analyzer; + + private: + Project m_project; + Target m_target; + std::unique_ptr m_ctx; + std::unique_ptr> m_builder; + std::unique_ptr m_utils; +}; + +TEST_F(LLVMCodeAnalyzer_MixedTypeAnalysis, EmptyScript) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); } -TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, NoOperations) +TEST_F(LLVMCodeAnalyzer_MixedTypeAnalysis, NoOperations) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); list.addInstruction(funcCall); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); } -TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, VariableToList_SimpleTransfer) +TEST_F(LLVMCodeAnalyzer_MixedTypeAnalysis, VariableToList_SimpleTransfer) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable sourceVar("sourceVar", ""); List targetList("targetList", ""); @@ -60,7 +84,7 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, VariableToList_SimpleTransfer) appendList->args.push_back({ Compiler::StaticType::Unknown, &readVarReg }); list.addInstruction(appendList); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Variable write should see Unknown (first write) ASSERT_EQ(writeVar->targetType, Compiler::StaticType::Unknown); @@ -75,9 +99,8 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, VariableToList_SimpleTransfer) ASSERT_EQ(readVarReg.type(), Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ListToVariable_SimpleTransfer) +TEST_F(LLVMCodeAnalyzer_MixedTypeAnalysis, ListToVariable_SimpleTransfer) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List sourceList("sourceList", ""); Variable targetVar("targetVar", ""); @@ -110,7 +133,7 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ListToVariable_SimpleTransfer) writeVar->args.push_back({ Compiler::StaticType::Unknown, &readListReg }); list.addInstruction(writeVar); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // List append should see Void (empty list after clear) ASSERT_EQ(appendList->targetType, Compiler::StaticType::Void); @@ -125,9 +148,8 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ListToVariable_SimpleTransfer) ASSERT_EQ(readListReg.type(), Compiler::StaticType::Bool | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, CircularVarListDependency) +TEST_F(LLVMCodeAnalyzer_MixedTypeAnalysis, CircularVarListDependency) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("var", ""); List targetList("list", ""); @@ -176,7 +198,7 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, CircularVarListDependency) writeVar2->args.push_back({ Compiler::StaticType::Unknown, &readListReg }); list.addInstruction(writeVar2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Should handle circular dependency gracefully ASSERT_EQ(writeVar1->targetType, Compiler::StaticType::Unknown); @@ -186,9 +208,8 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, CircularVarListDependency) ASSERT_EQ(writeVar2->targetType, Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, LoopWithVarListInteraction_TypeConflict) +TEST_F(LLVMCodeAnalyzer_MixedTypeAnalysis, LoopWithVarListInteraction_TypeConflict) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("var", ""); List targetList("list", ""); @@ -232,7 +253,7 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, LoopWithVarListInteraction_TypeConflict auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Should show type accumulation through loop iterations ASSERT_EQ(writeVar1->targetType, Compiler::StaticType::Unknown); @@ -241,9 +262,8 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, LoopWithVarListInteraction_TypeConflict ASSERT_EQ(writeVar2->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ConditionalVarListTransfer_TypeConflict) +TEST_F(LLVMCodeAnalyzer_MixedTypeAnalysis, ConditionalVarListTransfer_TypeConflict) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("var", ""); List targetList("list", ""); @@ -303,7 +323,7 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ConditionalVarListTransfer_TypeConflict auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); list.addInstruction(ifEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Should show type conflicts from conditional branches ASSERT_EQ(writeVar1->targetType, Compiler::StaticType::Unknown); @@ -312,9 +332,8 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ConditionalVarListTransfer_TypeConflict ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Void); } -TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, MultipleVarsToSingleList_TypePropagation) +TEST_F(LLVMCodeAnalyzer_MixedTypeAnalysis, MultipleVarsToSingleList_TypePropagation) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var1("var1", ""); Variable var2("var2", ""); @@ -368,16 +387,15 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, MultipleVarsToSingleList_TypePropagatio appendList2->args.push_back({ Compiler::StaticType::Unknown, &readVar2Reg }); list.addInstruction(appendList2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // List should have Bool type from both variables ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Bool); } -TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, SingleListToMultipleVars_TypePropagation) +TEST_F(LLVMCodeAnalyzer_MixedTypeAnalysis, SingleListToMultipleVars_TypePropagation) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; List sourceList("sourceList", ""); Variable var1("var1", ""); @@ -434,7 +452,7 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, SingleListToMultipleVars_TypePropagatio writeVar2->args.push_back({ Compiler::StaticType::Unknown, &readList2Reg }); list.addInstruction(writeVar2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Both variables should have String type from the list ASSERT_EQ(writeVar1->targetType, Compiler::StaticType::Unknown); @@ -443,9 +461,8 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, SingleListToMultipleVars_TypePropagatio ASSERT_EQ(readList2Reg.type(), Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ComplexChain_VarToListToVar_TypePropagation) +TEST_F(LLVMCodeAnalyzer_MixedTypeAnalysis, ComplexChain_VarToListToVar_TypePropagation) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable sourceVar("sourceVar", ""); List intermediateList("intermediateList", ""); @@ -494,7 +511,7 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ComplexChain_VarToListToVar_TypePropaga writeTargetVar->args.push_back({ Compiler::StaticType::Unknown, &readListReg }); list.addInstruction(writeTargetVar); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Type should propagate through the chain: sourceVar -> intermediateList -> targetVar ASSERT_EQ(writeSourceVar->targetType, Compiler::StaticType::Unknown); diff --git a/test/llvm/code_analyzer/variable_type_analysis.cpp b/test/llvm/code_analyzer/variable_type_analysis.cpp index 6495c99d..81fc1468 100644 --- a/test/llvm/code_analyzer/variable_type_analysis.cpp +++ b/test/llvm/code_analyzer/variable_type_analysis.cpp @@ -1,15 +1,41 @@ +#include +#include #include #include +#include +#include #include #include #include +#include #include using namespace libscratchcpp; -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, FirstVariableWrite) +class LLVMCodeAnalyzer_VariableTypeAnalysis : public testing::Test +{ + public: + void SetUp() override + { + auto engine = m_project.engine(); + m_ctx = std::make_unique(engine.get(), &m_target); + m_builder = std::make_unique>(*m_ctx->llvmCtx()); + m_utils = std::make_unique(m_ctx.get(), *m_builder, Compiler::CodeType::Script); + m_analyzer = std::make_unique(*m_utils); + } + + std::unique_ptr m_analyzer; + + private: + Project m_project; + Target m_target; + std::unique_ptr m_ctx; + std::unique_ptr> m_builder; + std::unique_ptr m_utils; +}; + +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, FirstVariableWrite) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -19,15 +45,14 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, FirstVariableWrite) setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(setVar); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // First write should have Unknown targetType (no previous type) ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, SecondVariableWrite) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, SecondVariableWrite) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -43,7 +68,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, SecondVariableWrite) setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(setVar2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // First write has no previous type ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); @@ -51,9 +76,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, SecondVariableWrite) ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, MultipleWritesSameType) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, MultipleWritesSameType) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -75,16 +99,15 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, MultipleWritesSameType) setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(setVar3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); ASSERT_EQ(setVar2->targetType, Compiler::StaticType::String); ASSERT_EQ(setVar3->targetType, Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -100,16 +123,15 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization) setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(setVar2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); // String "3.14" optimized to Number, so second write sees Number type ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization_DifferentString) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization_DifferentString) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -125,16 +147,15 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization_DifferentString) setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(setVar2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); // String "1.0" does NOT get optimized to Number because it would convert to "1" ASSERT_EQ(setVar2->targetType, Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopSingleWrite) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopSingleWrite) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -150,15 +171,14 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopSingleWrite) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Loop convergence: first iteration Unknown, subsequent iterations Number ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WhileLoop) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, WhileLoop) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -195,15 +215,14 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WhileLoop) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(readVar->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, RepeatUntilLoop) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, RepeatUntilLoop) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -240,15 +259,14 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, RepeatUntilLoop) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(readVar->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ProcedureCallInLoop) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, ProcedureCallInLoop) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -273,7 +291,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ProcedureCallInLoop) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); @@ -281,9 +299,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ProcedureCallInLoop) ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopMultipleWrites_UnknownType) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopMultipleWrites_UnknownType) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -305,7 +322,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopMultipleWrites_UnknownType) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // First write: Unknown (unknown before loop) ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); @@ -314,9 +331,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopMultipleWrites_UnknownType) ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopMultipleWrites_KnownType) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopMultipleWrites_KnownType) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -345,7 +361,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopMultipleWrites_KnownType) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // First write: Bool | String (from loop iterations) ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Bool | Compiler::StaticType::String); @@ -354,9 +370,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopMultipleWrites_KnownType) ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementSameTypes) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementSameTypes) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -387,7 +402,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementSameTypes) setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(setVar3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Both writes see Unknown before the if-else ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); @@ -397,9 +412,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementSameTypes) ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentTypes) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentTypes) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -430,7 +444,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentTypes) setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(setVar3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Both writes see Unknown before the if-else ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); @@ -440,9 +454,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentTypes) ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentType_IfBranch) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentType_IfBranch) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -470,7 +483,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentType_IfBranc setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(setVar3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); @@ -479,9 +492,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentType_IfBranc ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentType_ElseBranch) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentType_ElseBranch) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -512,7 +524,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentType_ElseBra setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(setVar3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); @@ -521,9 +533,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentType_ElseBra ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeLoop) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeLoop) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -551,15 +562,14 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeLoop) setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(setVar); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // The type might be String or Number because the loop might or might not run ASSERT_EQ(setVar->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfStatement_IfBranch) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfStatement_IfBranch) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -587,15 +597,14 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfStatement_IfBranch) setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(setVar); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // The type might be String or Number because the if statement might or might not run ASSERT_EQ(setVar->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfStatement_ElseBranch) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfStatement_ElseBranch) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -626,15 +635,14 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfStatement_ElseBranch) setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(setVar); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // The type might be String or Number because the else branch might or might not run ASSERT_EQ(setVar->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfElse) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfElse) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -671,7 +679,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfElse) setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(setVar3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Write before if-else establishes Bool type ASSERT_EQ(setVarBefore->targetType, Compiler::StaticType::Unknown); @@ -684,9 +692,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfElse) ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfStatement_IfBranch) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfStatement_IfBranch) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -708,7 +715,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfStatement_IfBranch) setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(setVar); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(setVarInIfStatement->targetType, Compiler::StaticType::Unknown); @@ -716,9 +723,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfStatement_IfBranch) ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfStatement_ElseBranch) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfStatement_ElseBranch) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -743,7 +749,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfStatement_ElseBranch) setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(setVar); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(setVarInIfStatement->targetType, Compiler::StaticType::Unknown); @@ -751,9 +757,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfStatement_ElseBranch) ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfElse) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfElse) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -784,7 +789,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfElse) setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(setVar3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Unknown); @@ -793,9 +798,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfElse) ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInLoop) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInLoop) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -817,7 +821,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInLoop) setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(setVar); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); ASSERT_EQ(setVarInLoop->targetType, Compiler::StaticType::Unknown); @@ -825,9 +829,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInLoop) ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ComplexNestedControlFlow) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, ComplexNestedControlFlow) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -876,7 +879,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ComplexNestedControlFlow) setVar4->args.push_back({ Compiler::StaticType::Unknown, &value4 }); list.addInstruction(setVar4); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Complex analysis with multiple execution paths and loop convergence ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); @@ -885,9 +888,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ComplexNestedControlFlow) ASSERT_EQ(setVar4->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, MultipleVariablesSeparateAnalysis) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, MultipleVariablesSeparateAnalysis) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var1("", ""), var2("", ""); @@ -903,16 +905,15 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, MultipleVariablesSeparateAnalysis) setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); list.addInstruction(setVar2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Both are first writes for their respective variables ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableDependency_SingleType) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableDependency_SingleType) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var1("", ""), var2("", ""); @@ -942,7 +943,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableDependency_SingleType) setVar1_2->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); list.addInstruction(setVar1_2); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // var2 first write has no previous type ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Unknown); @@ -957,9 +958,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableDependency_SingleType) ASSERT_EQ(setVar1_2->targetType, Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableDependency_MultipleTypes) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableDependency_MultipleTypes) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var1("", ""), var2("", ""); @@ -1005,7 +1005,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableDependency_MultipleType setVar4->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); list.addInstruction(setVar4); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // var2 has Number or Bool type at read operation ASSERT_EQ(readVar2->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); @@ -1017,9 +1017,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableDependency_MultipleType ASSERT_EQ(setVar4->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ChainedAssignmentsInLoop) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, ChainedAssignmentsInLoop) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable varA("a", ""), varB("b", ""), varC("c", ""); @@ -1104,7 +1103,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ChainedAssignmentsInLoop) setA3->args.push_back({ Compiler::StaticType::Unknown, &cValue2 }); list.addInstruction(setA3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Check the type of 'a' before the final a = c assignment // 'a' could be Number (from initial assignment) or String (from loop iterations where a=b, b=c, c="test") @@ -1119,9 +1118,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ChainedAssignmentsInLoop) ASSERT_EQ(readC2->targetType, expectedCType); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, SelfAssignment) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, SelfAssignment) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -1154,7 +1152,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, SelfAssignment) setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); list.addInstruction(setVar3); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // First write should have Unknown targetType (no previous type) ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); @@ -1166,9 +1164,8 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, SelfAssignment) ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, SelfAssignmentInLoop) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, SelfAssignmentInLoop) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var("", ""); @@ -1193,16 +1190,15 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, SelfAssignmentInLoop) auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // Self-assignment in loop should maintain Unknown type since it's a no-op // and doesn't change the variable's type across iterations ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, VariableReadReturnRegType) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, VariableReadReturnRegType) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var1("", ""), var2("", ""); @@ -1226,15 +1222,14 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, VariableReadReturnRegType) setVar1_1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); list.addInstruction(setVar1_1); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // var2 read return register has Number type ASSERT_EQ(var2Value.type(), Compiler::StaticType::Number); } -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableWriteArgType) +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableWriteArgType) { - LLVMCodeAnalyzer analyzer; LLVMInstructionList list; Variable var1("", ""), var2("", ""); @@ -1258,7 +1253,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableWriteArgType) setVar1_1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); list.addInstruction(setVar1_1); - analyzer.analyzeScript(list); + m_analyzer->analyzeScript(list); // last write argument has Number type ASSERT_EQ(setVar1_1->args.front().first, Compiler::StaticType::Number); From d0d6ca90c654884a1eb29126118b7d6f3af27370 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 30 Aug 2025 19:26:11 +0200 Subject: [PATCH 3/3] Don't optimize numeric string constants used for costumes/sounds --- src/engine/internal/llvm/llvmbuildutils.cpp | 39 ++++++++- src/engine/internal/llvm/llvmbuildutils.h | 5 +- src/engine/internal/llvm/llvmcodeanalyzer.cpp | 2 +- test/blocks/control_blocks_test.cpp | 3 + test/blocks/event_blocks_test.cpp | 3 + test/blocks/looks_blocks_test.cpp | 3 + test/blocks/motion_blocks_test.cpp | 3 + test/blocks/sensing_blocks_test.cpp | 2 + .../llvm/code_analyzer/list_type_analysis.cpp | 87 +++++++++++++++++-- .../code_analyzer/variable_type_analysis.cpp | 77 ++++++++++++++-- test/llvm/llvmtestutils.cpp | 4 + test/llvm/llvmtestutils.h | 1 + 12 files changed, 213 insertions(+), 16 deletions(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 216bd327..c873ed95 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "llvmbuildutils.h" #include "llvmfunctions.h" @@ -39,6 +41,35 @@ LLVMBuildUtils::LLVMBuildUtils(LLVMCompilerContext *ctx, llvm::IRBuilder<> &buil initTypes(); createVariableMap(); createListMap(); + + // Find unsafe numeric string constants in costume and sound names + if (m_target) { + IEngine *engine = m_target->engine(); + + if (engine) { + auto checkConstant = [this](const std::string &str) { + Value stringValue(str); + Value numberValue(stringValue.toDouble()); + + if (stringValue.isValidNumber() && str == numberValue.toString()) + m_unsafeConstants.insert(str); + }; + + const auto &targets = engine->targets(); + bool found = false; + + for (const auto &target : targets) { + const auto &costumes = target->costumes(); + const auto &sounds = target->sounds(); + + for (const auto &costume : costumes) + checkConstant(costume->name()); + + for (const auto &sound : sounds) + checkConstant(sound->name()); + } + } + } } void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePrototype, bool warp, const std::vector> ®s) @@ -429,7 +460,7 @@ std::vector &LLVMBuildUtils::loops() return m_loops; } -Compiler::StaticType LLVMBuildUtils::optimizeRegisterType(const LLVMRegister *reg) +Compiler::StaticType LLVMBuildUtils::optimizeRegisterType(const LLVMRegister *reg) const { Compiler::StaticType ret = reg->type(); @@ -437,10 +468,10 @@ Compiler::StaticType LLVMBuildUtils::optimizeRegisterType(const LLVMRegister *re if (reg->isConst() && reg->type() == Compiler::StaticType::String) { const Value &value = reg->constValue(); Value numberValue(value.toDouble()); + std::string str = value.toString(); - // Apply this optimization only if the number matches the string - // TODO: Exclude unsafe constants - if (value.isValidNumber() && numberValue.toString() == value.toString()) + // Apply this optimization only if the number matches the string and the constant is safe + if (value.isValidNumber() && numberValue.toString() == str && m_unsafeConstants.find(str) == m_unsafeConstants.cend()) ret = Compiler::StaticType::Number; } diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index e6192ec9..35b77b78 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -83,7 +83,8 @@ class LLVMBuildUtils std::vector &ifStatements(); std::vector &loops(); - static Compiler::StaticType optimizeRegisterType(const LLVMRegister *reg); + Compiler::StaticType optimizeRegisterType(const LLVMRegister *reg) const; + static Compiler::StaticType mapType(ValueType type); static ValueType mapType(Compiler::StaticType type); static bool isSingleType(Compiler::StaticType type); @@ -161,6 +162,8 @@ class LLVMBuildUtils bool m_warp = false; Compiler::CodeType m_codeType = Compiler::CodeType::Script; + std::unordered_set m_unsafeConstants; + llvm::Value *m_executionContextPtr = nullptr; llvm::Value *m_targetPtr = nullptr; llvm::Value *m_targetVariables = nullptr; diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.cpp b/src/engine/internal/llvm/llvmcodeanalyzer.cpp index 6a52e0a2..5bcc58b6 100644 --- a/src/engine/internal/llvm/llvmcodeanalyzer.cpp +++ b/src/engine/internal/llvm/llvmcodeanalyzer.cpp @@ -294,5 +294,5 @@ Compiler::StaticType LLVMCodeAnalyzer::writeType(LLVMInstruction *ins) const } } - return LLVMBuildUtils::optimizeRegisterType(argReg); + return m_utils.optimizeRegisterType(argReg); } diff --git a/test/blocks/control_blocks_test.cpp b/test/blocks/control_blocks_test.cpp index 03b75fe5..c45812ec 100644 --- a/test/blocks/control_blocks_test.cpp +++ b/test/blocks/control_blocks_test.cpp @@ -23,6 +23,7 @@ using namespace libscratchcpp; using namespace libscratchcpp::test; using ::testing::Return; +using ::testing::ReturnRef; using ::testing::SaveArg; using ::testing::_; @@ -35,6 +36,8 @@ class ControlBlocksTest : public testing::Test m_engine = m_project.engine().get(); m_extension->registerBlocks(m_engine); registerBlocks(m_engine, m_extension.get()); + + EXPECT_CALL(m_engineMock, targets()).WillRepeatedly(ReturnRef(m_engine->targets())); } std::unique_ptr m_extension; diff --git a/test/blocks/event_blocks_test.cpp b/test/blocks/event_blocks_test.cpp index 03a354bd..299bd680 100644 --- a/test/blocks/event_blocks_test.cpp +++ b/test/blocks/event_blocks_test.cpp @@ -20,6 +20,7 @@ using namespace libscratchcpp; using namespace libscratchcpp::test; using ::testing::Return; +using ::testing::ReturnRef; class EventBlocksTest : public testing::Test { @@ -29,6 +30,8 @@ class EventBlocksTest : public testing::Test m_extension = std::make_unique(); m_engine = m_project.engine().get(); m_extension->registerBlocks(m_engine); + + EXPECT_CALL(m_engineMock, targets()).WillRepeatedly(ReturnRef(m_engine->targets())); } std::unique_ptr m_extension; diff --git a/test/blocks/looks_blocks_test.cpp b/test/blocks/looks_blocks_test.cpp index 7caca7c5..55a0b463 100644 --- a/test/blocks/looks_blocks_test.cpp +++ b/test/blocks/looks_blocks_test.cpp @@ -23,6 +23,7 @@ using namespace libscratchcpp; using namespace libscratchcpp::test; using ::testing::Return; +using ::testing::ReturnRef; using ::testing::ReturnArg; using ::testing::_; @@ -36,6 +37,8 @@ class LooksBlocksTest : public testing::Test m_extension->registerBlocks(m_engine); m_extension->onInit(m_engine); + EXPECT_CALL(m_engineMock, targets()).WillRepeatedly(ReturnRef(m_engine->targets())); + // Create and register fake graphic effects auto colorEffect = std::make_shared(); auto fisheyeEffect = std::make_shared(); diff --git a/test/blocks/motion_blocks_test.cpp b/test/blocks/motion_blocks_test.cpp index a6788c57..b5758bae 100644 --- a/test/blocks/motion_blocks_test.cpp +++ b/test/blocks/motion_blocks_test.cpp @@ -20,6 +20,7 @@ using namespace libscratchcpp; using namespace libscratchcpp::test; using ::testing::Return; +using ::testing::ReturnRef; class MotionBlocksTest : public testing::Test { @@ -29,6 +30,8 @@ class MotionBlocksTest : public testing::Test m_extension = std::make_unique(); m_engine = m_project.engine().get(); m_extension->registerBlocks(m_engine); + + EXPECT_CALL(m_engineMock, targets()).WillRepeatedly(ReturnRef(m_engine->targets())); } std::unique_ptr m_extension; diff --git a/test/blocks/sensing_blocks_test.cpp b/test/blocks/sensing_blocks_test.cpp index 356b94ac..88305621 100644 --- a/test/blocks/sensing_blocks_test.cpp +++ b/test/blocks/sensing_blocks_test.cpp @@ -47,6 +47,8 @@ class SensingBlocksTest : public testing::Test EXPECT_CALL(m_audioInput, audioLoudness()).WillRepeatedly(Return(m_audioLoudness)); SensingBlocks::clock = &m_clock; + + EXPECT_CALL(m_engineMock, targets()).WillRepeatedly(ReturnRef(m_engine->targets())); } void TearDown() override diff --git a/test/llvm/code_analyzer/list_type_analysis.cpp b/test/llvm/code_analyzer/list_type_analysis.cpp index 3c59fdb8..45d479fb 100644 --- a/test/llvm/code_analyzer/list_type_analysis.cpp +++ b/test/llvm/code_analyzer/list_type_analysis.cpp @@ -1,5 +1,8 @@ #include -#include +#include +#include +#include +#include #include #include #include @@ -18,7 +21,18 @@ class LLVMCodeAnalyzer_ListTypeAnalysis : public testing::Test void SetUp() override { auto engine = m_project.engine(); - m_ctx = std::make_unique(engine.get(), &m_target); + m_target = std::make_shared(); + m_spriteWithUnsafeConstants = std::make_shared(); + + auto costume = std::make_shared(m_unsafeCostumeNumConstant, "", ""); + m_spriteWithUnsafeConstants->addCostume(costume); + + auto sound = std::make_shared(m_unsafeSoundNumConstant, "", ""); + m_spriteWithUnsafeConstants->addSound(sound); + + engine->setTargets({ m_target, m_spriteWithUnsafeConstants }); + + m_ctx = std::make_unique(engine.get(), m_target.get()); m_builder = std::make_unique>(*m_ctx->llvmCtx()); m_utils = std::make_unique(m_ctx.get(), *m_builder, Compiler::CodeType::Script); m_analyzer = std::make_unique(*m_utils); @@ -26,9 +40,14 @@ class LLVMCodeAnalyzer_ListTypeAnalysis : public testing::Test std::unique_ptr m_analyzer; + const std::string m_safeNumConstant = "3.14"; + const std::string m_unsafeCostumeNumConstant = "12"; + const std::string m_unsafeSoundNumConstant = "-27.672"; + private: Project m_project; - Target m_target; + std::shared_ptr m_target; + std::shared_ptr m_spriteWithUnsafeConstants; std::unique_ptr m_ctx; std::unique_ptr> m_builder; std::unique_ptr m_utils; @@ -155,7 +174,7 @@ TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear) list.addInstruction(clearList); auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "3.14"); + LLVMConstantRegister value1(Compiler::StaticType::String, m_safeNumConstant); appendList1->targetList = &targetList; appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); list.addInstruction(appendList1); @@ -170,7 +189,7 @@ TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear) ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); - // String "3.14" optimized to Number, so second write sees Number type + // String gets optimized to Number, so second write sees Number type ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Number); } @@ -203,6 +222,64 @@ TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear_Differen ASSERT_EQ(appendList2->targetType, Compiler::StaticType::String); } +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear_UnsafeCostumeConstant) +{ + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::String, m_unsafeCostumeNumConstant); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + m_analyzer->analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + + // String does NOT get optimized to Number because there's a costume with the same name + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::String); +} + +TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear_UnsafeSoundConstant) +{ + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::String, m_unsafeSoundNumConstant); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + m_analyzer->analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + + // String does NOT get optimized to Number because there's a sound with the same name + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::String); +} + TEST_F(LLVMCodeAnalyzer_ListTypeAnalysis, ClearListOperation) { LLVMInstructionList list; diff --git a/test/llvm/code_analyzer/variable_type_analysis.cpp b/test/llvm/code_analyzer/variable_type_analysis.cpp index 81fc1468..1546c87c 100644 --- a/test/llvm/code_analyzer/variable_type_analysis.cpp +++ b/test/llvm/code_analyzer/variable_type_analysis.cpp @@ -1,5 +1,8 @@ #include -#include +#include +#include +#include +#include #include #include #include @@ -18,7 +21,18 @@ class LLVMCodeAnalyzer_VariableTypeAnalysis : public testing::Test void SetUp() override { auto engine = m_project.engine(); - m_ctx = std::make_unique(engine.get(), &m_target); + m_target = std::make_shared(); + m_spriteWithUnsafeConstants = std::make_shared(); + + auto costume = std::make_shared(m_unsafeCostumeNumConstant, "", ""); + m_spriteWithUnsafeConstants->addCostume(costume); + + auto sound = std::make_shared(m_unsafeSoundNumConstant, "", ""); + m_spriteWithUnsafeConstants->addSound(sound); + + engine->setTargets({ m_target, m_spriteWithUnsafeConstants }); + + m_ctx = std::make_unique(engine.get(), m_target.get()); m_builder = std::make_unique>(*m_ctx->llvmCtx()); m_utils = std::make_unique(m_ctx.get(), *m_builder, Compiler::CodeType::Script); m_analyzer = std::make_unique(*m_utils); @@ -26,9 +40,14 @@ class LLVMCodeAnalyzer_VariableTypeAnalysis : public testing::Test std::unique_ptr m_analyzer; + const std::string m_safeNumConstant = "3.14"; + const std::string m_unsafeCostumeNumConstant = "12"; + const std::string m_unsafeSoundNumConstant = "-27.672"; + private: Project m_project; - Target m_target; + std::shared_ptr m_target; + std::shared_ptr m_spriteWithUnsafeConstants; std::unique_ptr m_ctx; std::unique_ptr> m_builder; std::unique_ptr m_utils; @@ -112,7 +131,7 @@ TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization) Variable var("", ""); auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "3.14"); + LLVMConstantRegister value1(Compiler::StaticType::String, m_safeNumConstant); setVar1->targetVariable = &var; setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); list.addInstruction(setVar1); @@ -126,7 +145,7 @@ TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization) m_analyzer->analyzeScript(list); ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); - // String "3.14" optimized to Number, so second write sees Number type + // String gets optimized to Number, so second write sees Number type ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); } @@ -154,6 +173,54 @@ TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization_DifferentString ASSERT_EQ(setVar2->targetType, Compiler::StaticType::String); } +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization_UnsafeCostumeConstant) +{ + LLVMInstructionList list; + Variable var("", ""); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::String, m_unsafeCostumeNumConstant); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + m_analyzer->analyzeScript(list); + + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + // String does NOT get optimized to Number because there's a costume with the same name + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::String); +} + +TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization_UnsafeSoundConstant) +{ + LLVMInstructionList list; + Variable var("", ""); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::String, m_unsafeSoundNumConstant); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + m_analyzer->analyzeScript(list); + + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + // String does NOT get optimized to Number because there's a sound with the same name + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::String); +} + TEST_F(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopSingleWrite) { LLVMInstructionList list; diff --git a/test/llvm/llvmtestutils.cpp b/test/llvm/llvmtestutils.cpp index 2089493f..d5cc21f6 100644 --- a/test/llvm/llvmtestutils.cpp +++ b/test/llvm/llvmtestutils.cpp @@ -14,9 +14,13 @@ using namespace libscratchcpp; +using ::testing::ReturnRef; + LLVMTestUtils::LLVMTestUtils() { test_function(nullptr, nullptr, nullptr, nullptr, nullptr); // force dependency + + EXPECT_CALL(m_engine, targets()).WillRepeatedly(ReturnRef(m_emptyTargets)); } LLVMCodeBuilder *LLVMTestUtils::createBuilder(Target *target, BlockPrototype *procedurePrototype, Compiler::CodeType codeType) diff --git a/test/llvm/llvmtestutils.h b/test/llvm/llvmtestutils.h index afbc3c52..64380bc7 100644 --- a/test/llvm/llvmtestutils.h +++ b/test/llvm/llvmtestutils.h @@ -102,6 +102,7 @@ class LLVMTestUtils EngineMock m_engine; TargetMock m_target; // NOTE: isStage() is used for call expectations RandomGeneratorMock m_rng; + const std::vector> m_emptyTargets; }; } // namespace libscratchcpp