Skip to content

Commit 528d3cb

Browse files
committed
LLVMTypeAnalyzer: Fix sequential if statements/loops
1 parent 9fc99eb commit 528d3cb

File tree

3 files changed

+393
-8
lines changed

3 files changed

+393
-8
lines changed

src/engine/internal/llvm/llvmtypeanalyzer.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ Compiler::StaticType LLVMTypeAnalyzer::variableType(Variable *var, LLVMInstructi
148148
// Check the last write operation before the instruction
149149
LLVMInstruction *ins = pos;
150150
LLVMInstruction *write = nullptr;
151-
LLVMInstruction *firstBranch = nullptr;
152-
LLVMInstruction *firstElseBranch = nullptr;
151+
std::pair<LLVMInstruction *, int> firstBranch = { nullptr, 0 };
152+
std::pair<LLVMInstruction *, int> firstElseBranch = { nullptr, 0 };
153153
LLVMInstruction *ourBranch = nullptr;
154154
int level = 0;
155155

@@ -160,11 +160,15 @@ Compiler::StaticType LLVMTypeAnalyzer::variableType(Variable *var, LLVMInstructi
160160
if (!ourBranch && level == 0)
161161
ourBranch = ins;
162162

163+
if (!firstBranch.first || level < firstBranch.second)
164+
firstBranch = { ins, level };
165+
163166
level--;
164-
firstBranch = ins;
165167
} else if (isElse(ins)) {
166168
// Skip if branch if coming from else
167-
firstElseBranch = ins;
169+
if (!firstElseBranch.first || level < firstElseBranch.second)
170+
firstElseBranch = { ins, level };
171+
168172
ins = branchStart(ins);
169173
continue;
170174
} else if (isVariableWrite(ins, var) && !isWriteNoOp(ins)) {
@@ -177,9 +181,9 @@ Compiler::StaticType LLVMTypeAnalyzer::variableType(Variable *var, LLVMInstructi
177181
ins = ins->previous;
178182
}
179183

180-
if (firstBranch) {
184+
if (firstBranch.first) {
181185
// Analyze the first branch and else branch
182-
bool ignoreWriteAfterPos = (isIfStart(firstBranch) && firstBranch == ourBranch);
186+
bool ignoreWriteAfterPos = (isIfStart(firstBranch.first) && firstBranch.first == ourBranch);
183187

184188
if (write)
185189
previousType = writeValueType(write, visitedInstructions); // write operation overrides previous type
@@ -188,8 +192,8 @@ Compiler::StaticType LLVMTypeAnalyzer::variableType(Variable *var, LLVMInstructi
188192
Compiler::StaticType elseBranchType = previousType;
189193

190194
if (!ignoreWriteAfterPos) {
191-
firstBranchType = variableTypeAfterBranch(var, firstBranch, previousType, visitedInstructions);
192-
elseBranchType = variableTypeAfterBranch(var, firstElseBranch, previousType, visitedInstructions);
195+
firstBranchType = variableTypeAfterBranch(var, firstBranch.first, previousType, visitedInstructions);
196+
elseBranchType = variableTypeAfterBranch(var, firstElseBranch.first, previousType, visitedInstructions);
193197
}
194198

195199
if (typesMatch(firstBranchType, elseBranchType))

test/llvm/type_analyzer/listtype_test.cpp

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,260 @@ TEST(LLVMTypeAnalyzer_ListType, IfElseWithEqualTypes_DifferentTypes_Empty)
13061306
ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String);
13071307
}
13081308

1309+
TEST(LLVMTypeAnalyzer_ListType, IfStatementAfterLoop_SameTypes_NonEmpty)
1310+
{
1311+
LLVMTypeAnalyzer analyzer;
1312+
LLVMInstructionList instructionList;
1313+
List list("", "");
1314+
1315+
auto loopStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginRepeatLoop, nullptr, false);
1316+
instructionList.addInstruction(loopStart);
1317+
1318+
// First write - does not change type
1319+
auto appendList1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1320+
LLVMConstantRegister value1(Compiler::StaticType::Number, 5);
1321+
appendList1->workList = &list;
1322+
appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
1323+
instructionList.addInstruction(appendList1);
1324+
1325+
auto loopEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndLoop, nullptr, false);
1326+
instructionList.addInstruction(loopEnd);
1327+
1328+
auto ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, nullptr, false);
1329+
instructionList.addInstruction(ifStart);
1330+
1331+
// Second write - does not change type
1332+
auto appendList2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1333+
LLVMConstantRegister value2(Compiler::StaticType::Number, 2);
1334+
appendList2->workList = &list;
1335+
appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
1336+
instructionList.addInstruction(appendList2);
1337+
1338+
auto ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, nullptr, false);
1339+
instructionList.addInstruction(ifEnd);
1340+
1341+
auto funcCall = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::FunctionCall, nullptr, false);
1342+
instructionList.addInstruction(funcCall);
1343+
1344+
ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number);
1345+
}
1346+
1347+
TEST(LLVMTypeAnalyzer_ListType, IfStatementAfterLoop_SameTypes_Empty)
1348+
{
1349+
LLVMTypeAnalyzer analyzer;
1350+
LLVMInstructionList instructionList;
1351+
List list("", "");
1352+
1353+
auto loopStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginRepeatLoop, nullptr, false);
1354+
instructionList.addInstruction(loopStart);
1355+
1356+
// First write - establishes type for empty list
1357+
auto appendList1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1358+
LLVMConstantRegister value1(Compiler::StaticType::Number, 5);
1359+
appendList1->workList = &list;
1360+
appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
1361+
instructionList.addInstruction(appendList1);
1362+
1363+
auto loopEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndLoop, nullptr, false);
1364+
instructionList.addInstruction(loopEnd);
1365+
1366+
auto ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, nullptr, false);
1367+
instructionList.addInstruction(ifStart);
1368+
1369+
// Second write - same type
1370+
auto appendList2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1371+
LLVMConstantRegister value2(Compiler::StaticType::Number, 2);
1372+
appendList2->workList = &list;
1373+
appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
1374+
instructionList.addInstruction(appendList2);
1375+
1376+
auto ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, nullptr, false);
1377+
instructionList.addInstruction(ifEnd);
1378+
1379+
auto funcCall = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::FunctionCall, nullptr, false);
1380+
instructionList.addInstruction(funcCall);
1381+
1382+
ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number);
1383+
}
1384+
1385+
TEST(LLVMTypeAnalyzer_ListType, IfStatementAfterLoop_DifferentTypes_NonEmpty)
1386+
{
1387+
LLVMTypeAnalyzer analyzer;
1388+
LLVMInstructionList instructionList;
1389+
List list("", "");
1390+
1391+
auto loopStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginRepeatLoop, nullptr, false);
1392+
instructionList.addInstruction(loopStart);
1393+
1394+
// First write - does not change type
1395+
auto appendList1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1396+
LLVMConstantRegister value1(Compiler::StaticType::Number, 5);
1397+
appendList1->workList = &list;
1398+
appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
1399+
instructionList.addInstruction(appendList1);
1400+
1401+
auto loopEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndLoop, nullptr, false);
1402+
instructionList.addInstruction(loopEnd);
1403+
1404+
auto ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, nullptr, false);
1405+
instructionList.addInstruction(ifStart);
1406+
1407+
// Second write - changes type
1408+
auto appendList2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1409+
LLVMConstantRegister value2(Compiler::StaticType::String, "test");
1410+
appendList2->workList = &list;
1411+
appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
1412+
instructionList.addInstruction(appendList2);
1413+
1414+
auto ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, nullptr, false);
1415+
instructionList.addInstruction(ifEnd);
1416+
1417+
auto funcCall = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::FunctionCall, nullptr, false);
1418+
instructionList.addInstruction(funcCall);
1419+
1420+
ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown);
1421+
}
1422+
1423+
TEST(LLVMTypeAnalyzer_ListType, IfStatementAfterLoop_DifferentTypes_Empty)
1424+
{
1425+
LLVMTypeAnalyzer analyzer;
1426+
LLVMInstructionList instructionList;
1427+
List list("", "");
1428+
1429+
auto loopStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginRepeatLoop, nullptr, false);
1430+
instructionList.addInstruction(loopStart);
1431+
1432+
// First write - establishes Number type for empty list
1433+
auto appendList1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1434+
LLVMConstantRegister value1(Compiler::StaticType::Number, 5);
1435+
appendList1->workList = &list;
1436+
appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
1437+
instructionList.addInstruction(appendList1);
1438+
1439+
auto loopEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndLoop, nullptr, false);
1440+
instructionList.addInstruction(loopEnd);
1441+
1442+
auto ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, nullptr, false);
1443+
instructionList.addInstruction(ifStart);
1444+
1445+
// Second write - adds string
1446+
auto appendList2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1447+
LLVMConstantRegister value2(Compiler::StaticType::String, "test");
1448+
appendList2->workList = &list;
1449+
appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
1450+
instructionList.addInstruction(appendList2);
1451+
1452+
auto ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, nullptr, false);
1453+
instructionList.addInstruction(ifEnd);
1454+
1455+
auto funcCall = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::FunctionCall, nullptr, false);
1456+
instructionList.addInstruction(funcCall);
1457+
1458+
ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown);
1459+
}
1460+
1461+
TEST(LLVMTypeAnalyzer_ListType, TwoIfStatements_DifferentTypes_NonEmpty)
1462+
{
1463+
LLVMTypeAnalyzer analyzer;
1464+
LLVMInstructionList instructionList;
1465+
List list("", "");
1466+
1467+
auto ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, nullptr, false);
1468+
instructionList.addInstruction(ifStart);
1469+
1470+
// First write - does not change type
1471+
auto appendList1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1472+
LLVMConstantRegister value1(Compiler::StaticType::Number, 5);
1473+
appendList1->workList = &list;
1474+
appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
1475+
instructionList.addInstruction(appendList1);
1476+
1477+
auto elseStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginElse, nullptr, false);
1478+
instructionList.addInstruction(elseStart);
1479+
1480+
// Second write - does not change type
1481+
auto appendList2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1482+
LLVMConstantRegister value2(Compiler::StaticType::Number, 4);
1483+
appendList2->workList = &list;
1484+
appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
1485+
instructionList.addInstruction(appendList2);
1486+
1487+
auto ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, nullptr, false);
1488+
instructionList.addInstruction(ifEnd);
1489+
1490+
ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, nullptr, false);
1491+
instructionList.addInstruction(ifStart);
1492+
1493+
elseStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginElse, nullptr, false);
1494+
instructionList.addInstruction(elseStart);
1495+
1496+
// Third write - adds string (in else branch)
1497+
auto appendList3 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1498+
LLVMConstantRegister value3(Compiler::StaticType::String, "test");
1499+
appendList3->workList = &list;
1500+
appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 });
1501+
instructionList.addInstruction(appendList3);
1502+
1503+
ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, nullptr, false);
1504+
instructionList.addInstruction(ifEnd);
1505+
1506+
auto funcCall = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::FunctionCall, nullptr, false);
1507+
instructionList.addInstruction(funcCall);
1508+
1509+
ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown);
1510+
}
1511+
1512+
TEST(LLVMTypeAnalyzer_ListType, TwoIfStatements_DifferentTypes_Empty)
1513+
{
1514+
LLVMTypeAnalyzer analyzer;
1515+
LLVMInstructionList instructionList;
1516+
List list("", "");
1517+
1518+
auto ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, nullptr, false);
1519+
instructionList.addInstruction(ifStart);
1520+
1521+
// First write - establishes Number type
1522+
auto appendList1 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1523+
LLVMConstantRegister value1(Compiler::StaticType::Number, 5);
1524+
appendList1->workList = &list;
1525+
appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 });
1526+
instructionList.addInstruction(appendList1);
1527+
1528+
auto elseStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginElse, nullptr, false);
1529+
instructionList.addInstruction(elseStart);
1530+
1531+
// Second write - same Number type
1532+
auto appendList2 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1533+
LLVMConstantRegister value2(Compiler::StaticType::Number, 4);
1534+
appendList2->workList = &list;
1535+
appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 });
1536+
instructionList.addInstruction(appendList2);
1537+
1538+
auto ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, nullptr, false);
1539+
instructionList.addInstruction(ifEnd);
1540+
1541+
ifStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginIf, nullptr, false);
1542+
instructionList.addInstruction(ifStart);
1543+
1544+
elseStart = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::BeginElse, nullptr, false);
1545+
instructionList.addInstruction(elseStart);
1546+
1547+
// Third write - adds String (in else branch)
1548+
auto appendList3 = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::AppendToList, nullptr, false);
1549+
LLVMConstantRegister value3(Compiler::StaticType::String, "test");
1550+
appendList3->workList = &list;
1551+
appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 });
1552+
instructionList.addInstruction(appendList3);
1553+
1554+
ifEnd = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::EndIf, nullptr, false);
1555+
instructionList.addInstruction(ifEnd);
1556+
1557+
auto funcCall = std::make_shared<LLVMInstruction>(LLVMInstruction::Type::FunctionCall, nullptr, false);
1558+
instructionList.addInstruction(funcCall);
1559+
1560+
ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown);
1561+
}
1562+
13091563
TEST(LLVMTypeAnalyzer_ListType, NestedLoopWithTypeChange_Before_NonEmpty)
13101564
{
13111565
LLVMTypeAnalyzer analyzer;

0 commit comments

Comments
 (0)