@@ -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+
25117Compiler::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
34126Compiler::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+
239344LLVMInstruction *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+
328438Compiler::StaticType LLVMTypeAnalyzer::optimizeRegisterType (LLVMRegister *reg) const
329439{
330440 // TODO: Move this method out if it's used in LLVMCodeBuilder too
0 commit comments