Skip to content

Commit 9fc99eb

Browse files
committed
LLVMTypeAnalyzer: Implement listType() for basic cases
1 parent ac4920e commit 9fc99eb

File tree

4 files changed

+3249
-16
lines changed

4 files changed

+3249
-16
lines changed

src/engine/internal/llvm/llvmtypeanalyzer.cpp

Lines changed: 125 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,105 @@ Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranch(Variable *var, LL
2222
return variableTypeAfterBranch(var, start, previousType, visitedInstructions);
2323
}
2424

25+
Compiler::StaticType LLVMTypeAnalyzer::listType(List *list, LLVMInstruction *pos, Compiler::StaticType previousType, bool isEmpty) const
26+
{
27+
if (!list || !pos)
28+
return Compiler::StaticType::Unknown;
29+
30+
InstructionSet visitedInstructions; // TODO: Handle cross-variable/list dependencies
31+
32+
LLVMInstruction *ins = pos;
33+
LLVMInstruction *previous = nullptr;
34+
std::pair<LLVMInstruction *, int> firstBranch = { nullptr, 0 };
35+
std::pair<LLVMInstruction *, int> lastClear = { nullptr, 0 };
36+
int level = 0;
37+
38+
// Find a start instruction (list clear in the top level or the first instruction)
39+
while (ins) {
40+
if (isLoopEnd(ins) || isIfEnd(ins))
41+
level++;
42+
else if (isLoopStart(ins) || isIfStart(ins) || isElse(ins)) {
43+
if (!isElse(ins))
44+
level--;
45+
46+
if (!firstBranch.first || level < firstBranch.second)
47+
firstBranch = { ins, level };
48+
} else if (isListClear(ins, list)) {
49+
if (!lastClear.first || level < lastClear.second)
50+
lastClear = { ins, level };
51+
}
52+
53+
previous = ins;
54+
ins = ins->previous;
55+
}
56+
57+
if (firstBranch.first) {
58+
assert(firstBranch.second == level);
59+
60+
// The first branch must be above the query point level
61+
if (firstBranch.second == 0)
62+
firstBranch.first = nullptr;
63+
}
64+
65+
// Clear must be in the top level
66+
if (lastClear.second != level)
67+
lastClear.first = nullptr;
68+
69+
if (lastClear.first) {
70+
ins = lastClear.first;
71+
isEmpty = true;
72+
} else
73+
ins = previous;
74+
75+
// Process from the start instruction
76+
while (ins && ins != pos) {
77+
if (isLoopStart(ins) || isIfStart(ins)) {
78+
do {
79+
bool write;
80+
Compiler::StaticType type = listTypeAfterBranch(list, ins, previousType, isEmpty, pos, write);
81+
82+
// If this branch contains the query point, return the final type
83+
if (ins == firstBranch.first) {
84+
if (isEmpty || typesMatch(type, previousType))
85+
return type;
86+
else
87+
return Compiler::StaticType::Unknown;
88+
}
89+
90+
// If there was a write, the list is no longer empty
91+
if (write) {
92+
isEmpty = false;
93+
94+
// The write could change the type
95+
if (!typesMatch(type, previousType))
96+
previousType = type;
97+
}
98+
99+
// Skip the branch
100+
ins = branchEnd(ins);
101+
} while (isElse(ins)); // handle else branch
102+
} else if (isListWrite(ins, list)) {
103+
// List write instruction
104+
Compiler::StaticType writeType = writeValueType(ins, visitedInstructions);
105+
106+
if (!handleListWrite(writeType, previousType, isEmpty))
107+
return Compiler::StaticType::Unknown;
108+
}
109+
110+
ins = ins->next;
111+
}
112+
113+
assert(ins);
114+
return previousType;
115+
}
116+
25117
Compiler::StaticType LLVMTypeAnalyzer::listTypeAfterBranch(List *list, LLVMInstruction *start, Compiler::StaticType previousType, bool isEmpty) const
26118
{
27119
if (!list || !start)
28120
return previousType;
29121

30122
bool write = false; // only used internally (the compiler doesn't need this)
31-
return listTypeAfterBranch(list, start, previousType, isEmpty, write);
123+
return listTypeAfterBranch(list, start, previousType, isEmpty, nullptr, write);
32124
}
33125

34126
Compiler::StaticType LLVMTypeAnalyzer::variableType(Variable *var, LLVMInstruction *pos, Compiler::StaticType previousType, InstructionSet &visitedInstructions) const
@@ -181,19 +273,23 @@ Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranchFromEnd(Variable *
181273
return previousType;
182274
}
183275

184-
Compiler::StaticType LLVMTypeAnalyzer::listTypeAfterBranch(List *list, LLVMInstruction *start, Compiler::StaticType previousType, bool isEmpty, bool &write) const
276+
Compiler::StaticType LLVMTypeAnalyzer::listTypeAfterBranch(List *list, LLVMInstruction *start, Compiler::StaticType previousType, bool isEmpty, LLVMInstruction *query, bool &write) const
185277
{
186278
assert(isLoopStart(start) || isIfStart(start) || isElse(start));
187279

188280
InstructionSet visitedInstructions; // TODO: Handle cross-variable/list dependencies
189281

282+
// Query point is only relevant in if statemenets because writes after them don't affect the result
283+
if (isLoopStart(start))
284+
query = nullptr;
285+
190286
LLVMInstruction *ins = start->next;
191287
write = false;
192288

193289
while (ins && !(isLoopEnd(ins) || isIfEnd(ins) || isElse(ins))) {
194290
if (isLoopStart(ins) || isIfStart(ins)) {
195291
do {
196-
Compiler::StaticType type = listTypeAfterBranch(list, ins, previousType, isEmpty, write);
292+
Compiler::StaticType type = listTypeAfterBranch(list, ins, previousType, isEmpty, query, write);
197293

198294
// If there was a write, the list is no longer empty
199295
if (write) {
@@ -212,22 +308,14 @@ Compiler::StaticType LLVMTypeAnalyzer::listTypeAfterBranch(List *list, LLVMInstr
212308
Compiler::StaticType writeType = writeValueType(ins, visitedInstructions);
213309
write = true;
214310

215-
if (isEmpty) {
216-
// In empty lists, writes of the same type determine the final type
217-
// This is the first write found, it might determine the final type
218-
previousType = writeType;
219-
220-
// The list is no longer empty
221-
isEmpty = false;
222-
} else if (!typesMatch(writeType, previousType)) {
223-
// For non-empty lists, we can just check the write type
311+
if (!handleListWrite(writeType, previousType, isEmpty))
224312
return Compiler::StaticType::Unknown;
225-
}
226-
} else if (ins->type == LLVMInstruction::Type::ClearList && ins->workList == list) {
313+
} else if (isListClear(ins, list)) {
227314
// The list is now empty
228315
isEmpty = true;
229316
write = false; // the write variable is only used to check if the list is still empty
230-
}
317+
} else if (ins == query)
318+
break;
231319

232320
ins = ins->next;
233321
}
@@ -236,6 +324,23 @@ Compiler::StaticType LLVMTypeAnalyzer::listTypeAfterBranch(List *list, LLVMInstr
236324
return previousType;
237325
}
238326

327+
bool LLVMTypeAnalyzer::handleListWrite(Compiler::StaticType writeType, Compiler::StaticType &previousType, bool &isEmpty) const
328+
{
329+
if (isEmpty) {
330+
// In empty lists, writes of the same type determine the final type
331+
// This is the first write found, it might determine the final type
332+
previousType = writeType;
333+
334+
// The list is no longer empty
335+
isEmpty = false;
336+
} else if (!typesMatch(writeType, previousType)) {
337+
// For non-empty lists, we can just check the write type
338+
return false;
339+
}
340+
341+
return true;
342+
}
343+
239344
LLVMInstruction *LLVMTypeAnalyzer::branchEnd(LLVMInstruction *start) const
240345
{
241346
assert(start);
@@ -325,6 +430,11 @@ bool LLVMTypeAnalyzer::isListWrite(LLVMInstruction *ins, List *list) const
325430
return (LIST_WRITE_INSTRUCTIONS.find(ins->type) != LIST_WRITE_INSTRUCTIONS.cend() && ins->workList == list);
326431
}
327432

433+
bool LLVMTypeAnalyzer::isListClear(LLVMInstruction *ins, List *list) const
434+
{
435+
return (ins->type == LLVMInstruction::Type::ClearList && ins->workList == list);
436+
}
437+
328438
Compiler::StaticType LLVMTypeAnalyzer::optimizeRegisterType(LLVMRegister *reg) const
329439
{
330440
// TODO: Move this method out if it's used in LLVMCodeBuilder too

src/engine/internal/llvm/llvmtypeanalyzer.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class LLVMTypeAnalyzer
1414
Compiler::StaticType variableType(Variable *var, LLVMInstruction *pos, Compiler::StaticType previousType) const;
1515
Compiler::StaticType variableTypeAfterBranch(Variable *var, LLVMInstruction *start, Compiler::StaticType previousType) const;
1616

17+
Compiler::StaticType listType(List *list, LLVMInstruction *pos, Compiler::StaticType previousType, bool isEmpty) const;
1718
Compiler::StaticType listTypeAfterBranch(List *list, LLVMInstruction *start, Compiler::StaticType previousType, bool isEmpty) const;
1819

1920
private:
@@ -23,7 +24,8 @@ class LLVMTypeAnalyzer
2324
Compiler::StaticType variableTypeAfterBranch(Variable *var, LLVMInstruction *start, Compiler::StaticType previousType, InstructionSet &visitedInstructions) const;
2425
Compiler::StaticType variableTypeAfterBranchFromEnd(Variable *var, LLVMInstruction *end, Compiler::StaticType previousType, bool &write, InstructionSet &visitedInstructions) const;
2526

26-
Compiler::StaticType listTypeAfterBranch(List *list, LLVMInstruction *start, Compiler::StaticType previousType, bool isEmpty, bool &write) const;
27+
Compiler::StaticType listTypeAfterBranch(List *list, LLVMInstruction *start, Compiler::StaticType previousType, bool isEmpty, LLVMInstruction *query, bool &write) const;
28+
bool handleListWrite(Compiler::StaticType writeType, Compiler::StaticType &previousType, bool &isEmpty) const;
2729

2830
LLVMInstruction *branchEnd(LLVMInstruction *start) const;
2931
LLVMInstruction *branchStart(LLVMInstruction *end) const;
@@ -38,6 +40,7 @@ class LLVMTypeAnalyzer
3840
bool isVariableWrite(LLVMInstruction *ins, Variable *var) const;
3941

4042
bool isListWrite(LLVMInstruction *ins, List *list) const;
43+
bool isListClear(LLVMInstruction *ins, List *list) const;
4144

4245
Compiler::StaticType optimizeRegisterType(LLVMRegister *reg) const;
4346
bool isWriteNoOp(LLVMInstruction *ins) const;

test/llvm/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_executable(
2323
type_analyzer/variabletypeafterbranch_test.cpp
2424
type_analyzer/variabletype_test.cpp
2525
type_analyzer/listtypeafterbranch_test.cpp
26+
type_analyzer/listtype_test.cpp
2627
)
2728

2829
target_link_libraries(

0 commit comments

Comments
 (0)