Skip to content

Commit a521bee

Browse files
committed
LLVMCodeAnalyzer: Fix unknown types after if statements and loops
1 parent 0f8399f commit a521bee

File tree

4 files changed

+309
-7
lines changed

4 files changed

+309
-7
lines changed

src/engine/internal/llvm/llvmcodeanalyzer.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const
5858
if (primaryBranch && primaryBranch->elseBranch) {
5959
// The previous variable types can be ignored in if/else statements
6060
overrideVariableTypes(primaryBranch, previousBranch);
61-
mergeListTypes(primaryBranch, previousBranch);
61+
mergeListTypes(primaryBranch, previousBranch, false);
6262

6363
mergeVariableTypes(primaryBranch->elseBranch.get(), previousBranch);
64-
mergeListTypes(primaryBranch->elseBranch.get(), previousBranch);
64+
mergeListTypes(primaryBranch->elseBranch.get(), previousBranch, true);
6565
} else {
6666
mergeVariableTypes(primaryBranch, previousBranch);
67-
mergeListTypes(primaryBranch, previousBranch);
67+
mergeListTypes(primaryBranch, previousBranch, true);
6868
}
6969

7070
// Remove the branch
@@ -196,7 +196,7 @@ void LLVMCodeAnalyzer::mergeVariableTypes(Branch *branch, Branch *previousBranch
196196
auto it = previousBranch->variableTypes.find(var);
197197

198198
if (it == previousBranch->variableTypes.cend())
199-
previousBranch->variableTypes[var] = type;
199+
previousBranch->variableTypes[var] = Compiler::StaticType::Unknown;
200200
else
201201
it->second |= type;
202202
}
@@ -208,13 +208,13 @@ void LLVMCodeAnalyzer::overrideVariableTypes(Branch *branch, Branch *previousBra
208208
previousBranch->variableTypes[var] = type;
209209
}
210210

211-
void LLVMCodeAnalyzer::mergeListTypes(Branch *branch, Branch *previousBranch) const
211+
void LLVMCodeAnalyzer::mergeListTypes(Branch *branch, Branch *previousBranch, bool firstUnknown) const
212212
{
213213
for (const auto &[list, type] : branch->listTypes) {
214214
auto it = previousBranch->listTypes.find(list);
215215

216216
if (it == previousBranch->listTypes.cend())
217-
previousBranch->listTypes[list] = type;
217+
previousBranch->listTypes[list] = firstUnknown ? Compiler::StaticType::Unknown : type;
218218
else
219219
it->second |= type;
220220
}

src/engine/internal/llvm/llvmcodeanalyzer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class LLVMCodeAnalyzer
3232

3333
void mergeVariableTypes(Branch *branch, Branch *previousBranch) const;
3434
void overrideVariableTypes(Branch *branch, Branch *previousBranch) const;
35-
void mergeListTypes(Branch *branch, Branch *previousBranch) const;
35+
void mergeListTypes(Branch *branch, Branch *previousBranch, bool firstUnknown) const;
3636

3737
bool isLoopStart(const LLVMInstruction *ins) const;
3838
bool isLoopEnd(const LLVMInstruction *ins) const;

test/llvm/code_analyzer/list_type_analysis.cpp

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,167 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopSingleWrite_AfterClear)
351351
ASSERT_EQ(appendList->targetType, Compiler::StaticType::Number);
352352
}
353353

354+
TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfStatement_IfBranch)
355+
{
356+
LLVMCodeAnalyzer analyzer;
357+
LLVMInstructionList list;
358+
List targetList("", "");
359+
360+
auto ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, false);
361+
list.addInstruction(ifStart);
362+
363+
auto clearList = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::ClearList, false);
364+
clearList->targetList = &targetList;
365+
list.addInstruction(clearList);
366+
367+
auto appendList1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, false);
368+
LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25);
369+
appendList1->targetList = &targetList;
370+
appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
371+
list.addInstruction(appendList1);
372+
373+
auto ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, false);
374+
list.addInstruction(ifEnd);
375+
376+
auto appendList2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, false);
377+
LLVMConstantRegister value2(Compiler::StaticType::Bool, true);
378+
appendList2->targetList = &targetList;
379+
appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
380+
list.addInstruction(appendList2);
381+
382+
analyzer.analyzeScript(list);
383+
384+
ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void);
385+
386+
// The type is Unknown because the if statement might not run at all
387+
ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown);
388+
}
389+
390+
TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfStatement_ElseBranch)
391+
{
392+
LLVMCodeAnalyzer analyzer;
393+
LLVMInstructionList list;
394+
List targetList("", "");
395+
396+
auto ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, false);
397+
list.addInstruction(ifStart);
398+
399+
auto elseStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginElse, false);
400+
list.addInstruction(elseStart);
401+
402+
auto clearList = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::ClearList, false);
403+
clearList->targetList = &targetList;
404+
list.addInstruction(clearList);
405+
406+
auto appendList1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, false);
407+
LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25);
408+
appendList1->targetList = &targetList;
409+
appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
410+
list.addInstruction(appendList1);
411+
412+
auto ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, false);
413+
list.addInstruction(ifEnd);
414+
415+
auto appendList2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, false);
416+
LLVMConstantRegister value2(Compiler::StaticType::Bool, true);
417+
appendList2->targetList = &targetList;
418+
appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
419+
list.addInstruction(appendList2);
420+
421+
analyzer.analyzeScript(list);
422+
423+
ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void);
424+
425+
// The type is Unknown because the if statement might not run at all
426+
ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown);
427+
}
428+
429+
TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfElse)
430+
{
431+
LLVMCodeAnalyzer analyzer;
432+
LLVMInstructionList list;
433+
List targetList("", "");
434+
435+
auto ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, false);
436+
list.addInstruction(ifStart);
437+
438+
auto clearList1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::ClearList, false);
439+
clearList1->targetList = &targetList;
440+
list.addInstruction(clearList1);
441+
442+
auto appendList1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, false);
443+
LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25);
444+
appendList1->targetList = &targetList;
445+
appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
446+
list.addInstruction(appendList1);
447+
448+
auto elseStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginElse, false);
449+
list.addInstruction(elseStart);
450+
451+
auto clearList2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::ClearList, false);
452+
clearList2->targetList = &targetList;
453+
list.addInstruction(clearList2);
454+
455+
auto appendList2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, false);
456+
LLVMConstantRegister value2(Compiler::StaticType::String, "hello");
457+
appendList2->targetList = &targetList;
458+
appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
459+
list.addInstruction(appendList2);
460+
461+
auto ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, false);
462+
list.addInstruction(ifEnd);
463+
464+
auto appendList3 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, false);
465+
LLVMConstantRegister value3(Compiler::StaticType::Bool, true);
466+
appendList3->targetList = &targetList;
467+
appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 });
468+
list.addInstruction(appendList3);
469+
470+
analyzer.analyzeScript(list);
471+
472+
ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void);
473+
ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Void);
474+
475+
// The type is Number | String because any of the branches may run
476+
ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String);
477+
}
478+
479+
TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInLoop)
480+
{
481+
LLVMCodeAnalyzer analyzer;
482+
LLVMInstructionList list;
483+
List targetList("", "");
484+
485+
auto loopStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginRepeatLoop, false);
486+
list.addInstruction(loopStart);
487+
488+
auto clearList = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::ClearList, false);
489+
clearList->targetList = &targetList;
490+
list.addInstruction(clearList);
491+
492+
auto appendList1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, false);
493+
LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25);
494+
appendList1->targetList = &targetList;
495+
appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
496+
list.addInstruction(appendList1);
497+
498+
auto loopEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndLoop, false);
499+
list.addInstruction(loopEnd);
500+
501+
auto appendList2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, false);
502+
LLVMConstantRegister value2(Compiler::StaticType::Bool, true);
503+
appendList2->targetList = &targetList;
504+
appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
505+
list.addInstruction(appendList2);
506+
507+
analyzer.analyzeScript(list);
508+
509+
ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void);
510+
511+
// The type is Unknown because the loop might not run at all
512+
ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown);
513+
}
514+
354515
TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAfterWriteInLoop)
355516
{
356517
LLVMCodeAnalyzer analyzer;

test/llvm/code_analyzer/variable_type_analysis.cpp

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,147 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfElse)
629629
ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String);
630630
}
631631

632+
TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfStatement_IfBranch)
633+
{
634+
LLVMCodeAnalyzer analyzer;
635+
LLVMInstructionList list;
636+
Variable var("", "");
637+
638+
auto ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, false);
639+
list.addInstruction(ifStart);
640+
641+
auto setVarInIfStatement = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, false);
642+
LLVMConstantRegister valueInIfStatement(Compiler::StaticType::Number, 42);
643+
setVarInIfStatement->targetVariable = &var;
644+
setVarInIfStatement->args.push_back({ Compiler::StaticType::Unknown, &valueInIfStatement });
645+
list.addInstruction(setVarInIfStatement);
646+
647+
auto ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, false);
648+
list.addInstruction(ifEnd);
649+
650+
auto setVar = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, false);
651+
LLVMConstantRegister value(Compiler::StaticType::Bool, false);
652+
setVar->targetVariable = &var;
653+
setVar->args.push_back({ Compiler::StaticType::Unknown, &value });
654+
list.addInstruction(setVar);
655+
656+
analyzer.analyzeScript(list);
657+
658+
ASSERT_EQ(setVarInIfStatement->targetType, Compiler::StaticType::Unknown);
659+
660+
// The type is Unknown because the if statement might not run at all
661+
ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown);
662+
}
663+
664+
TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfStatement_ElseBranch)
665+
{
666+
LLVMCodeAnalyzer analyzer;
667+
LLVMInstructionList list;
668+
Variable var("", "");
669+
670+
auto ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, false);
671+
list.addInstruction(ifStart);
672+
673+
auto elseStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginElse, false);
674+
list.addInstruction(elseStart);
675+
676+
auto setVarInIfStatement = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, false);
677+
LLVMConstantRegister valueInIfStatement(Compiler::StaticType::Number, 42);
678+
setVarInIfStatement->targetVariable = &var;
679+
setVarInIfStatement->args.push_back({ Compiler::StaticType::Unknown, &valueInIfStatement });
680+
list.addInstruction(setVarInIfStatement);
681+
682+
auto ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, false);
683+
list.addInstruction(ifEnd);
684+
685+
auto setVar = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, false);
686+
LLVMConstantRegister value(Compiler::StaticType::Bool, false);
687+
setVar->targetVariable = &var;
688+
setVar->args.push_back({ Compiler::StaticType::Unknown, &value });
689+
list.addInstruction(setVar);
690+
691+
analyzer.analyzeScript(list);
692+
693+
ASSERT_EQ(setVarInIfStatement->targetType, Compiler::StaticType::Unknown);
694+
695+
// The type is Unknown because the if statement might not run at all
696+
ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown);
697+
}
698+
699+
TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfElse)
700+
{
701+
LLVMCodeAnalyzer analyzer;
702+
LLVMInstructionList list;
703+
Variable var("", "");
704+
705+
auto ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, false);
706+
list.addInstruction(ifStart);
707+
708+
auto setVar1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, false);
709+
LLVMConstantRegister value1(Compiler::StaticType::Number, 42);
710+
setVar1->targetVariable = &var;
711+
setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
712+
list.addInstruction(setVar1);
713+
714+
auto elseStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginElse, false);
715+
list.addInstruction(elseStart);
716+
717+
auto setVar2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, false);
718+
LLVMConstantRegister value2(Compiler::StaticType::String, "test");
719+
setVar2->targetVariable = &var;
720+
setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
721+
list.addInstruction(setVar2);
722+
723+
auto ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, false);
724+
list.addInstruction(ifEnd);
725+
726+
auto setVar3 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, false);
727+
LLVMConstantRegister value3(Compiler::StaticType::Bool, true);
728+
setVar3->targetVariable = &var;
729+
setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 });
730+
list.addInstruction(setVar3);
731+
732+
analyzer.analyzeScript(list);
733+
734+
ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown);
735+
ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Unknown);
736+
737+
// The type is Number | String because any of the branches may run
738+
ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String);
739+
}
740+
741+
TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInLoop)
742+
{
743+
LLVMCodeAnalyzer analyzer;
744+
LLVMInstructionList list;
745+
Variable var("", "");
746+
747+
auto loopStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginRepeatLoop, false);
748+
list.addInstruction(loopStart);
749+
750+
auto setVarInLoop = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, false);
751+
LLVMConstantRegister valueInLoop(Compiler::StaticType::Number, 42);
752+
setVarInLoop->targetVariable = &var;
753+
setVarInLoop->args.push_back({ Compiler::StaticType::Unknown, &valueInLoop });
754+
list.addInstruction(setVarInLoop);
755+
756+
auto loopEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndLoop, false);
757+
list.addInstruction(loopEnd);
758+
759+
auto setVar = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, false);
760+
LLVMConstantRegister value(Compiler::StaticType::Bool, false);
761+
setVar->targetVariable = &var;
762+
setVar->args.push_back({ Compiler::StaticType::Unknown, &value });
763+
list.addInstruction(setVar);
764+
765+
analyzer.analyzeScript(list);
766+
767+
ASSERT_EQ(setVarInLoop->targetType, Compiler::StaticType::Unknown);
768+
769+
// The type is Unknown because the loop might not run at all
770+
ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown);
771+
}
772+
632773
TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ComplexNestedControlFlow)
633774
{
634775
LLVMCodeAnalyzer analyzer;

0 commit comments

Comments
 (0)