Skip to content

Commit e89f882

Browse files
committed
LLVMTypeAnalyzer: Handle cross-variable dependencies
1 parent e8b26f5 commit e89f882

File tree

4 files changed

+1091
-27
lines changed

4 files changed

+1091
-27
lines changed

src/engine/internal/llvm/llvmtypeanalyzer.cpp

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,39 @@ static const std::unordered_set<LLVMInstruction::Type>
99
BEGIN_LOOP_INSTRUCTIONS = { LLVMInstruction::Type::BeginRepeatLoop, LLVMInstruction::Type::BeginWhileLoop, LLVMInstruction::Type::BeginRepeatUntilLoop };
1010

1111
Compiler::StaticType LLVMTypeAnalyzer::variableType(Variable *var, LLVMInstruction *pos, Compiler::StaticType previousType) const
12+
{
13+
InstructionSet visitedInstructions;
14+
return variableType(var, pos, previousType, visitedInstructions);
15+
}
16+
17+
Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranch(Variable *var, LLVMInstruction *start, Compiler::StaticType previousType) const
18+
{
19+
InstructionSet visitedInstructions;
20+
return variableTypeAfterBranch(var, start, previousType, visitedInstructions);
21+
}
22+
23+
Compiler::StaticType LLVMTypeAnalyzer::variableType(Variable *var, LLVMInstruction *pos, Compiler::StaticType previousType, InstructionSet &visitedInstructions) const
1224
{
1325
if (!var || !pos)
1426
return Compiler::StaticType::Unknown;
1527

28+
/*
29+
* If the given instruction has already been processed,
30+
* it means there's a case like this:
31+
* x = x
32+
*
33+
* or this:
34+
* x = y
35+
* ...
36+
* y = x
37+
*/
38+
if (visitedInstructions.find(pos) != visitedInstructions.cend()) {
39+
// Circular dependencies are rare (and bad) so don't optimize them
40+
return Compiler::StaticType::Unknown;
41+
}
42+
43+
visitedInstructions.insert(pos);
44+
1645
// Check the last write operation before the instruction
1746
LLVMInstruction *ins = pos;
1847
LLVMInstruction *write = nullptr;
@@ -35,7 +64,7 @@ Compiler::StaticType LLVMTypeAnalyzer::variableType(Variable *var, LLVMInstructi
3564
firstElseBranch = ins;
3665
ins = skipBranch(ins);
3766
continue;
38-
} else if (isVariableWrite(ins, var)) {
67+
} else if (isVariableWrite(ins, var) && !isWriteNoOp(ins)) {
3968
if (level <= 0) { // ignore nested branches (they're handled by the branch analyzer)
4069
write = ins;
4170
break;
@@ -50,14 +79,14 @@ Compiler::StaticType LLVMTypeAnalyzer::variableType(Variable *var, LLVMInstructi
5079
bool ignoreWriteAfterPos = (isIfStart(firstBranch) && firstBranch == ourBranch);
5180

5281
if (write)
53-
previousType = writeValueType(write); // write operation overrides previous type
82+
previousType = writeValueType(write, visitedInstructions); // write operation overrides previous type
5483

5584
Compiler::StaticType firstBranchType = previousType;
5685
Compiler::StaticType elseBranchType = previousType;
5786

5887
if (!ignoreWriteAfterPos) {
59-
firstBranchType = variableTypeAfterBranch(var, firstBranch, previousType);
60-
elseBranchType = variableTypeAfterBranch(var, firstElseBranch, previousType);
88+
firstBranchType = variableTypeAfterBranch(var, firstBranch, previousType, visitedInstructions);
89+
elseBranchType = variableTypeAfterBranch(var, firstElseBranch, previousType, visitedInstructions);
6190
}
6291

6392
if (typesMatch(firstBranchType, elseBranchType))
@@ -66,14 +95,14 @@ Compiler::StaticType LLVMTypeAnalyzer::variableType(Variable *var, LLVMInstructi
6695
return Compiler::StaticType::Unknown;
6796
} else if (write) {
6897
// There wasn't any branch found, so we can just check the last write operation
69-
return writeValueType(write);
98+
return writeValueType(write, visitedInstructions);
7099
}
71100

72101
// No write operation found
73102
return previousType;
74103
}
75104

76-
Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranch(Variable *var, LLVMInstruction *start, Compiler::StaticType previousType) const
105+
Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranch(Variable *var, LLVMInstruction *start, Compiler::StaticType previousType, InstructionSet &visitedInstructions) const
77106
{
78107
if (!var || !start)
79108
return previousType;
@@ -102,10 +131,10 @@ Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranch(Variable *var, LL
102131

103132
// Process the branch from end
104133
bool write = false; // only used internally (the compiler doesn't need this)
105-
return variableTypeAfterBranchFromEnd(var, ins, previousType, write);
134+
return variableTypeAfterBranchFromEnd(var, ins, previousType, write, visitedInstructions);
106135
}
107136

108-
Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranchFromEnd(Variable *var, LLVMInstruction *end, Compiler::StaticType previousType, bool &write) const
137+
Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranchFromEnd(Variable *var, LLVMInstruction *end, Compiler::StaticType previousType, bool &write, InstructionSet &visitedInstructions) const
109138
{
110139
// Find the last write instruction
111140
LLVMInstruction *ins = end->previous;
@@ -114,7 +143,7 @@ Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranchFromEnd(Variable *
114143
while (ins && !isLoopStart(ins) && !isIfStart(ins)) {
115144
if (isLoopEnd(ins) || isIfEnd(ins) || isElse(ins)) {
116145
// Process the nested loop or if statement
117-
Compiler::StaticType ret = variableTypeAfterBranchFromEnd(var, ins, previousType, write);
146+
Compiler::StaticType ret = variableTypeAfterBranchFromEnd(var, ins, previousType, write, visitedInstructions);
118147

119148
if (typesMatch(ret, previousType)) {
120149
if (write)
@@ -126,7 +155,7 @@ Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranchFromEnd(Variable *
126155

127156
if (isElse(ins)) {
128157
// Process if branch (the else branch is already processed)
129-
ret = variableTypeAfterBranchFromEnd(var, ins, previousType, write);
158+
ret = variableTypeAfterBranchFromEnd(var, ins, previousType, write, visitedInstructions);
130159

131160
if (typesMatch(ret, previousType)) {
132161
if (write) {
@@ -140,9 +169,9 @@ Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranchFromEnd(Variable *
140169

141170
ins = skipBranch(ins);
142171
}
143-
} else if (isVariableWrite(ins, var)) {
172+
} else if (isVariableWrite(ins, var) && !isWriteNoOp(ins)) {
144173
// Variable write instruction
145-
Compiler::StaticType writeType = writeValueType(ins);
174+
Compiler::StaticType writeType = writeValueType(ins, visitedInstructions);
146175
write = true;
147176

148177
if (typesMatch(writeType, previousType))
@@ -208,6 +237,11 @@ bool LLVMTypeAnalyzer::isIfEnd(LLVMInstruction *ins) const
208237
return (ins->type == LLVMInstruction::Type::EndIf);
209238
}
210239

240+
bool LLVMTypeAnalyzer::isVariableRead(LLVMInstruction *ins) const
241+
{
242+
return (ins->type == LLVMInstruction::Type::ReadVariable);
243+
}
244+
211245
bool LLVMTypeAnalyzer::isVariableWrite(LLVMInstruction *ins, Variable *var) const
212246
{
213247
return (ins->type == LLVMInstruction::Type::WriteVariable && ins->workVariable == var);
@@ -227,12 +261,40 @@ Compiler::StaticType LLVMTypeAnalyzer::optimizeRegisterType(LLVMRegister *reg) c
227261
return ret;
228262
}
229263

230-
Compiler::StaticType LLVMTypeAnalyzer::writeValueType(LLVMInstruction *ins) const
264+
bool LLVMTypeAnalyzer::isWriteNoOp(LLVMInstruction *ins) const
231265
{
232266
assert(ins);
233267
assert(!ins->args.empty());
234268
const auto arg = ins->args.back().second; // value is always the last argument in variable/list write instructions
235-
return optimizeRegisterType(arg);
269+
270+
if (arg->instruction) {
271+
// TODO: Handle list item
272+
if (isVariableRead(arg->instruction.get())) {
273+
// Self-assignment is a no-op
274+
return (ins->workVariable == arg->instruction->workVariable);
275+
}
276+
}
277+
278+
return false;
279+
}
280+
281+
Compiler::StaticType LLVMTypeAnalyzer::writeValueType(LLVMInstruction *ins, InstructionSet &visitedInstructions) const
282+
{
283+
assert(ins);
284+
assert(!ins->args.empty());
285+
const auto arg = ins->args.back().second; // value is always the last argument in variable/list write instructions
286+
287+
if (arg->instruction) {
288+
// TODO: Handle list item
289+
if (isVariableRead(arg->instruction.get())) {
290+
// If this is a variable read instruction, recursively get the variable type
291+
return variableType(arg->instruction->workVariable, arg->instruction.get(), Compiler::StaticType::Unknown, visitedInstructions);
292+
} else {
293+
// TODO: Use the instruction return register
294+
return optimizeRegisterType(arg);
295+
}
296+
} else
297+
return optimizeRegisterType(arg);
236298
}
237299

238300
bool LLVMTypeAnalyzer::typesMatch(Compiler::StaticType a, Compiler::StaticType b) const
@@ -241,13 +303,7 @@ bool LLVMTypeAnalyzer::typesMatch(Compiler::StaticType a, Compiler::StaticType b
241303
return (a == b) && (a != Compiler::StaticType::Unknown);
242304
}
243305

244-
bool LLVMTypeAnalyzer::writeTypesMatch(LLVMInstruction *ins, Compiler::StaticType expectedType) const
306+
bool LLVMTypeAnalyzer::writeTypesMatch(LLVMInstruction *ins, Compiler::StaticType expectedType, InstructionSet &visitedInstructions) const
245307
{
246-
// auto argIns = arg->instruction;
247-
248-
// TODO: Handle cross-variable dependencies
249-
/*if (argIns && (argIns->type == LLVMInstruction::Type::ReadVariable || argIns->type == LLVMInstruction::Type::GetListItem))
250-
return isVarOrListTypeSafe(argIns.get(), expectedType, log, c);*/
251-
252-
return typesMatch(writeValueType(ins), expectedType);
308+
return typesMatch(writeValueType(ins, visitedInstructions), expectedType);
253309
}

src/engine/internal/llvm/llvmtypeanalyzer.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,27 @@ class LLVMTypeAnalyzer
1515
Compiler::StaticType variableTypeAfterBranch(Variable *var, LLVMInstruction *start, Compiler::StaticType previousType) const;
1616

1717
private:
18-
Compiler::StaticType variableTypeAfterBranchFromEnd(Variable *var, LLVMInstruction *end, Compiler::StaticType previousType, bool &write) const;
18+
using InstructionSet = std::unordered_set<LLVMInstruction *>;
19+
20+
Compiler::StaticType variableType(Variable *var, LLVMInstruction *pos, Compiler::StaticType previousType, InstructionSet &visitedInstructions) const;
21+
Compiler::StaticType variableTypeAfterBranch(Variable *var, LLVMInstruction *start, Compiler::StaticType previousType, InstructionSet &visitedInstructions) const;
22+
Compiler::StaticType variableTypeAfterBranchFromEnd(Variable *var, LLVMInstruction *end, Compiler::StaticType previousType, bool &write, InstructionSet &visitedInstructions) const;
23+
1924
LLVMInstruction *skipBranch(LLVMInstruction *pos) const;
25+
2026
bool isLoopStart(LLVMInstruction *ins) const;
2127
bool isLoopEnd(LLVMInstruction *ins) const;
2228
bool isIfStart(LLVMInstruction *ins) const;
2329
bool isElse(LLVMInstruction *ins) const;
2430
bool isIfEnd(LLVMInstruction *ins) const;
31+
bool isVariableRead(LLVMInstruction *ins) const;
2532
bool isVariableWrite(LLVMInstruction *ins, Variable *var) const;
2633

2734
Compiler::StaticType optimizeRegisterType(LLVMRegister *reg) const;
28-
Compiler::StaticType writeValueType(LLVMInstruction *ins) const;
35+
bool isWriteNoOp(LLVMInstruction *ins) const;
36+
Compiler::StaticType writeValueType(LLVMInstruction *ins, InstructionSet &visitedInstructions) const;
2937
bool typesMatch(Compiler::StaticType a, Compiler::StaticType b) const;
30-
bool writeTypesMatch(LLVMInstruction *ins, Compiler::StaticType expectedType) const;
38+
bool writeTypesMatch(LLVMInstruction *ins, Compiler::StaticType expectedType, InstructionSet &visitedInstructions) const;
3139
};
3240

3341
} // namespace libscratchcpp

0 commit comments

Comments
 (0)