Skip to content

Commit 8698a95

Browse files
committed
LLVMTypeAnalyzer: Support more edge cases
1 parent bcf26d7 commit 8698a95

File tree

3 files changed

+164
-11
lines changed

3 files changed

+164
-11
lines changed

src/engine/internal/llvm/llvmtypeanalyzer.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,11 @@ Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranch(LLVMVariablePtr *
9191
}
9292

9393
// Process the branch from end
94-
return variableTypeAfterBranchFromEnd(varPtr, ins, previousType);
94+
bool write = false; // only used internally (the compiler doesn't need this)
95+
return variableTypeAfterBranchFromEnd(varPtr, ins, previousType, write);
9596
}
9697

97-
Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranchFromEnd(LLVMVariablePtr *varPtr, LLVMInstruction *end, Compiler::StaticType previousType) const
98+
Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranchFromEnd(LLVMVariablePtr *varPtr, LLVMInstruction *end, Compiler::StaticType previousType, bool &write) const
9899
{
99100
// Find the last write instruction
100101
LLVMInstruction *ins = end->previous;
@@ -103,29 +104,36 @@ Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranchFromEnd(LLVMVariab
103104
while (ins && !isLoopStart(ins) && !isIfStart(ins)) {
104105
if (isLoopEnd(ins) || isIfEnd(ins) || isElse(ins)) {
105106
// Process the nested loop or if statement
106-
Compiler::StaticType ret = variableTypeAfterBranchFromEnd(varPtr, ins, previousType);
107+
Compiler::StaticType ret = variableTypeAfterBranchFromEnd(varPtr, ins, previousType, write);
107108

108-
if (typesMatch(ret, previousType))
109-
typeMightReset = true;
110-
else
109+
if (typesMatch(ret, previousType)) {
110+
if (write)
111+
typeMightReset = true;
112+
} else
111113
return Compiler::StaticType::Unknown;
112114

113115
ins = skipBranch(ins);
114116

115117
if (isElse(ins)) {
116118
// Process if branch (the else branch is already processed)
117-
ret = variableTypeAfterBranchFromEnd(varPtr, ins, previousType);
119+
ret = variableTypeAfterBranchFromEnd(varPtr, ins, previousType, write);
118120

119-
if (typesMatch(ret, previousType))
120-
typeMightReset = true;
121-
else
121+
if (typesMatch(ret, previousType)) {
122+
if (write) {
123+
if (typeMightReset)
124+
return previousType;
125+
126+
typeMightReset = true;
127+
}
128+
} else
122129
return Compiler::StaticType::Unknown;
123130

124131
ins = skipBranch(ins);
125132
}
126133
} else if (ins->type == LLVMInstruction::Type::WriteVariable && ins->workVariable == varPtr->var) {
127134
// Variable write instruction
128135
Compiler::StaticType writeType = writeValueType(ins);
136+
write = true;
129137

130138
if (typesMatch(writeType, previousType))
131139
return writeType;
@@ -136,6 +144,7 @@ Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranchFromEnd(LLVMVariab
136144
ins = ins->previous;
137145
}
138146

147+
write = false;
139148
return previousType;
140149
}
141150

src/engine/internal/llvm/llvmtypeanalyzer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class LLVMTypeAnalyzer
1616
Compiler::StaticType variableTypeAfterBranch(LLVMVariablePtr *varPtr, LLVMInstruction *start, Compiler::StaticType previousType) const;
1717

1818
private:
19-
Compiler::StaticType variableTypeAfterBranchFromEnd(LLVMVariablePtr *varPtr, LLVMInstruction *end, Compiler::StaticType previousType) const;
19+
Compiler::StaticType variableTypeAfterBranchFromEnd(LLVMVariablePtr *varPtr, LLVMInstruction *end, Compiler::StaticType previousType, bool &write) const;
2020
LLVMInstruction *skipBranch(LLVMInstruction *pos) const;
2121
bool isLoopStart(LLVMInstruction *ins) const;
2222
bool isLoopEnd(LLVMInstruction *ins) const;

test/llvm/type_analyzer/variabletypeafterbranch_test.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,150 @@ TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, NestedLoopWithTypeChangeBeforeLoo
13081308
ASSERT_EQ(analyzer.variableTypeAfterBranch(&varPtr, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown);
13091309
}
13101310

1311+
TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, IfStatementInLoopWithTypeChangeBeforeLoop_TypeChangeInIfBranch)
1312+
{
1313+
LLVMTypeAnalyzer analyzer;
1314+
LLVMInstructionList list;
1315+
LLVMVariablePtr varPtr;
1316+
Variable var("", "");
1317+
varPtr.var = &var;
1318+
1319+
// Outer loop begin
1320+
auto outerLoop = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginRepeatUntilLoop, nullptr, false);
1321+
list.addInstruction(outerLoop);
1322+
1323+
// Variable write inside outer loop
1324+
auto setVar1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, nullptr, false);
1325+
LLVMConstantRegister value1(Compiler::StaticType::String, "test");
1326+
setVar1->workVariable = &var;
1327+
setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
1328+
list.addInstruction(setVar1);
1329+
1330+
// Inner if statement begin
1331+
auto innerIf = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, nullptr, false);
1332+
list.addInstruction(innerIf);
1333+
1334+
// Variable write inside inner if statement if branch
1335+
auto setVar2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, nullptr, false);
1336+
LLVMConstantRegister value2(Compiler::StaticType::Number, 5);
1337+
setVar2->workVariable = &var;
1338+
setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
1339+
list.addInstruction(setVar2);
1340+
1341+
// Inner if statement else branch begin
1342+
auto innerElse = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginElse, nullptr, false);
1343+
list.addInstruction(innerElse);
1344+
1345+
// Inner if statement end
1346+
auto innerEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, nullptr, false);
1347+
list.addInstruction(innerEnd);
1348+
1349+
// Outer loop end
1350+
auto outerEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndLoop, nullptr, false);
1351+
list.addInstruction(outerEnd);
1352+
1353+
// Returns unknown type because if statements can be skipped
1354+
ASSERT_EQ(analyzer.variableTypeAfterBranch(&varPtr, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown);
1355+
}
1356+
1357+
TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, IfStatementInLoopWithTypeChangeBeforeLoop_TypeChangeInElseBranch)
1358+
{
1359+
LLVMTypeAnalyzer analyzer;
1360+
LLVMInstructionList list;
1361+
LLVMVariablePtr varPtr;
1362+
Variable var("", "");
1363+
varPtr.var = &var;
1364+
1365+
// Outer loop begin
1366+
auto outerLoop = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginRepeatUntilLoop, nullptr, false);
1367+
list.addInstruction(outerLoop);
1368+
1369+
// Variable write inside outer loop
1370+
auto setVar1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, nullptr, false);
1371+
LLVMConstantRegister value1(Compiler::StaticType::String, "test");
1372+
setVar1->workVariable = &var;
1373+
setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
1374+
list.addInstruction(setVar1);
1375+
1376+
// Inner if statement begin
1377+
auto innerIf = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, nullptr, false);
1378+
list.addInstruction(innerIf);
1379+
1380+
// Inner if statement else branch begin
1381+
auto innerElse = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginElse, nullptr, false);
1382+
list.addInstruction(innerElse);
1383+
1384+
// Variable write inside inner if statement else branch
1385+
auto setVar2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, nullptr, false);
1386+
LLVMConstantRegister value2(Compiler::StaticType::Number, 5);
1387+
setVar2->workVariable = &var;
1388+
setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
1389+
list.addInstruction(setVar2);
1390+
1391+
// Inner if statement end
1392+
auto innerEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, nullptr, false);
1393+
list.addInstruction(innerEnd);
1394+
1395+
// Outer loop end
1396+
auto outerEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndLoop, nullptr, false);
1397+
list.addInstruction(outerEnd);
1398+
1399+
// Returns unknown type because if statements can be skipped
1400+
ASSERT_EQ(analyzer.variableTypeAfterBranch(&varPtr, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown);
1401+
}
1402+
1403+
TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, IfStatementInLoopWithTypeChangeBeforeLoop_TypeChangeInBothBranches)
1404+
{
1405+
LLVMTypeAnalyzer analyzer;
1406+
LLVMInstructionList list;
1407+
LLVMVariablePtr varPtr;
1408+
Variable var("", "");
1409+
varPtr.var = &var;
1410+
1411+
// Outer loop begin
1412+
auto outerLoop = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginRepeatUntilLoop, nullptr, false);
1413+
list.addInstruction(outerLoop);
1414+
1415+
// Variable write inside outer loop
1416+
auto setVar1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, nullptr, false);
1417+
LLVMConstantRegister value1(Compiler::StaticType::String, "test");
1418+
setVar1->workVariable = &var;
1419+
setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
1420+
list.addInstruction(setVar1);
1421+
1422+
// Inner if statement begin
1423+
auto innerIf = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, nullptr, false);
1424+
list.addInstruction(innerIf);
1425+
1426+
auto setVar = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, nullptr, false);
1427+
LLVMConstantRegister value(Compiler::StaticType::Number, 5);
1428+
setVar->workVariable = &var;
1429+
setVar->args.push_back({ Compiler::StaticType::Unknown, &value });
1430+
list.addInstruction(setVar);
1431+
1432+
// Inner if statement else branch begin
1433+
auto innerElse = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginElse, nullptr, false);
1434+
list.addInstruction(innerElse);
1435+
1436+
// Variable write inside inner if statement else branch
1437+
auto setVar2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::WriteVariable, nullptr, false);
1438+
LLVMConstantRegister value2(Compiler::StaticType::Number, 5);
1439+
setVar2->workVariable = &var;
1440+
setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
1441+
list.addInstruction(setVar2);
1442+
1443+
// Inner if statement end
1444+
auto innerEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, nullptr, false);
1445+
list.addInstruction(innerEnd);
1446+
1447+
// Outer loop end
1448+
auto outerEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndLoop, nullptr, false);
1449+
list.addInstruction(outerEnd);
1450+
1451+
// Returns number type because the type always changes to number
1452+
ASSERT_EQ(analyzer.variableTypeAfterBranch(&varPtr, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Number);
1453+
}
1454+
13111455
TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleNestedLoopsWithTypeChange)
13121456
{
13131457
LLVMTypeAnalyzer analyzer;

0 commit comments

Comments
 (0)