Skip to content

Commit 0a14d89

Browse files
committed
LLVMCodeAnalyzer: Implement list type analysis
1 parent 507ce25 commit 0a14d89

File tree

4 files changed

+1393
-14
lines changed

4 files changed

+1393
-14
lines changed

src/engine/internal/llvm/llvmcodeanalyzer.cpp

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ using namespace libscratchcpp;
1010
static const std::unordered_set<LLVMInstruction::Type>
1111
BEGIN_LOOP_INSTRUCTIONS = { LLVMInstruction::Type::BeginRepeatLoop, LLVMInstruction::Type::BeginWhileLoop, LLVMInstruction::Type::BeginRepeatUntilLoop };
1212

13+
static const std::unordered_set<LLVMInstruction::Type> LIST_WRITE_INSTRUCTIONS = { LLVMInstruction::Type::AppendToList, LLVMInstruction::Type::InsertToList, LLVMInstruction::Type::ListReplace };
14+
1315
void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const
1416
{
1517
std::unordered_set<LLVMInstruction *> typeAssignedInstructions;
@@ -28,6 +30,7 @@ void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const
2830
auto branch = std::make_unique<Branch>();
2931
branch->start = ins;
3032
branch->variableTypes = currentBranch->variableTypes;
33+
branch->listTypes = currentBranch->listTypes;
3134
currentBranch = branch.get();
3235
branches.push_back(std::move(branch));
3336
} else if (isElse(ins)) {
@@ -39,6 +42,7 @@ void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const
3942
currentBranch = currentBranch->elseBranch.get();
4043
currentBranch->start = ins;
4144
currentBranch->variableTypes = previousBranch->variableTypes;
45+
currentBranch->listTypes = previousBranch->listTypes;
4246
} else if (isIfEnd(ins) || isLoopEnd(ins)) {
4347
if (isLoopEnd(ins) && currentBranch->typeChanges) {
4448
// Next iteration
@@ -52,11 +56,16 @@ void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const
5256
assert(primaryBranch);
5357

5458
if (primaryBranch && primaryBranch->elseBranch) {
55-
// The previous types can be ignored in if/else statements
56-
overrideBranchTypes(primaryBranch, previousBranch);
57-
mergeBranchTypes(primaryBranch->elseBranch.get(), previousBranch);
58-
} else
59-
mergeBranchTypes(primaryBranch, previousBranch);
59+
// The previous variable types can be ignored in if/else statements
60+
overrideVariableTypes(primaryBranch, previousBranch);
61+
mergeListTypes(primaryBranch, previousBranch);
62+
63+
mergeVariableTypes(primaryBranch->elseBranch.get(), previousBranch);
64+
mergeListTypes(primaryBranch->elseBranch.get(), previousBranch);
65+
} else {
66+
mergeVariableTypes(primaryBranch, previousBranch);
67+
mergeListTypes(primaryBranch, previousBranch);
68+
}
6069

6170
// Remove the branch
6271
branches.pop_back();
@@ -72,6 +81,24 @@ void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const
7281
// Type before the read
7382
updateVariableType(currentBranch, ins, typeAssignedInstructions, false);
7483

84+
// Store the type in the return register
85+
ins->functionReturnReg->setType(ins->targetType);
86+
} else if (isListClear(ins)) {
87+
// Type before the read
88+
updateListType(currentBranch, ins, typeAssignedInstructions, true);
89+
90+
// Clear the list type
91+
currentBranch->listTypes[ins->targetList] = Compiler::StaticType::Void;
92+
} else if (isListWrite(ins)) {
93+
// Type before the write
94+
updateListType(currentBranch, ins, typeAssignedInstructions, true);
95+
96+
// Type after the write
97+
currentBranch->listTypes[ins->targetList] |= writeType(ins);
98+
} else if (isListRead(ins)) {
99+
// Type before the read
100+
updateListType(currentBranch, ins, typeAssignedInstructions, false);
101+
75102
// Store the type in the return register
76103
ins->functionReturnReg->setType(ins->targetType);
77104
}
@@ -110,9 +137,37 @@ void LLVMCodeAnalyzer::updateVariableType(Branch *branch, LLVMInstruction *ins,
110137
}
111138
}
112139

113-
void LLVMCodeAnalyzer::mergeBranchTypes(Branch *branch, Branch *previousBranch) const
140+
void LLVMCodeAnalyzer::updateListType(Branch *branch, LLVMInstruction *ins, std::unordered_set<LLVMInstruction *> &typeAssignedInstructions, bool isWrite) const
141+
{
142+
auto it = branch->listTypes.find(ins->targetList);
143+
144+
if (it == branch->listTypes.cend()) {
145+
if (typeAssignedInstructions.find(ins) == typeAssignedInstructions.cend()) {
146+
if (isWrite)
147+
branch->typeChanges = true;
148+
149+
typeAssignedInstructions.insert(ins);
150+
ins->targetType = Compiler::StaticType::Unknown;
151+
branch->listTypes[ins->targetList] = ins->targetType;
152+
}
153+
} else {
154+
if (typeAssignedInstructions.find(ins) == typeAssignedInstructions.cend()) {
155+
if (isWrite)
156+
branch->typeChanges = true;
157+
158+
ins->targetType = it->second;
159+
typeAssignedInstructions.insert(ins);
160+
} else {
161+
if (isWrite && ((ins->targetType | it->second) != ins->targetType))
162+
branch->typeChanges = true;
163+
164+
ins->targetType |= it->second;
165+
}
166+
}
167+
}
168+
169+
void LLVMCodeAnalyzer::mergeVariableTypes(Branch *branch, Branch *previousBranch) const
114170
{
115-
// Variables
116171
for (const auto &[var, type] : branch->variableTypes) {
117172
auto it = previousBranch->variableTypes.find(var);
118173

@@ -123,13 +178,24 @@ void LLVMCodeAnalyzer::mergeBranchTypes(Branch *branch, Branch *previousBranch)
123178
}
124179
}
125180

126-
void LLVMCodeAnalyzer::overrideBranchTypes(Branch *branch, Branch *previousBranch) const
181+
void LLVMCodeAnalyzer::overrideVariableTypes(Branch *branch, Branch *previousBranch) const
127182
{
128-
// Variables
129183
for (const auto &[var, type] : branch->variableTypes)
130184
previousBranch->variableTypes[var] = type;
131185
}
132186

187+
void LLVMCodeAnalyzer::mergeListTypes(Branch *branch, Branch *previousBranch) const
188+
{
189+
for (const auto &[list, type] : branch->listTypes) {
190+
auto it = previousBranch->listTypes.find(list);
191+
192+
if (it == previousBranch->listTypes.cend())
193+
previousBranch->listTypes[list] = type;
194+
else
195+
it->second |= type;
196+
}
197+
}
198+
133199
bool LLVMCodeAnalyzer::isLoopStart(const LLVMInstruction *ins) const
134200
{
135201
return (BEGIN_LOOP_INSTRUCTIONS.find(ins->type) != BEGIN_LOOP_INSTRUCTIONS.cend());
@@ -165,6 +231,21 @@ bool LLVMCodeAnalyzer::isVariableWrite(const LLVMInstruction *ins) const
165231
return (ins->type == LLVMInstruction::Type::WriteVariable);
166232
}
167233

234+
bool LLVMCodeAnalyzer::isListRead(const LLVMInstruction *ins) const
235+
{
236+
return (ins->type == LLVMInstruction::Type::GetListItem);
237+
}
238+
239+
bool LLVMCodeAnalyzer::isListWrite(const LLVMInstruction *ins) const
240+
{
241+
return (LIST_WRITE_INSTRUCTIONS.find(ins->type) != LIST_WRITE_INSTRUCTIONS.cend());
242+
}
243+
244+
bool LLVMCodeAnalyzer::isListClear(const LLVMInstruction *ins) const
245+
{
246+
return (ins->type == LLVMInstruction::Type::ClearList);
247+
}
248+
168249
Compiler::StaticType LLVMCodeAnalyzer::writeType(LLVMInstruction *ins) const
169250
{
170251
assert(ins);
@@ -173,9 +254,8 @@ Compiler::StaticType LLVMCodeAnalyzer::writeType(LLVMInstruction *ins) const
173254
const LLVMRegister *argReg = arg.second;
174255

175256
if (argReg->instruction) {
176-
// TODO: Handle list item
177-
if (isVariableRead(argReg->instruction.get())) {
178-
// Store the variable type in the value argument
257+
if (isVariableRead(argReg->instruction.get()) || isListRead(argReg->instruction.get())) {
258+
// Store the variable/list type in the value argument
179259
arg.first = argReg->instruction->functionReturnReg->type();
180260
}
181261
}

src/engine/internal/llvm/llvmcodeanalyzer.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,17 @@ class LLVMCodeAnalyzer
2222
LLVMInstruction *start = nullptr;
2323
bool typeChanges = false;
2424
std::unordered_map<Variable *, Compiler::StaticType> variableTypes;
25+
std::unordered_map<List *, Compiler::StaticType> listTypes;
2526

2627
std::unique_ptr<Branch> elseBranch;
2728
};
2829

2930
void updateVariableType(Branch *branch, LLVMInstruction *ins, std::unordered_set<LLVMInstruction *> &typeAssignedInstructions, bool isWrite) const;
30-
void mergeBranchTypes(Branch *branch, Branch *previousBranch) const;
31-
void overrideBranchTypes(Branch *branch, Branch *previousBranch) const;
31+
void updateListType(Branch *branch, LLVMInstruction *ins, std::unordered_set<LLVMInstruction *> &typeAssignedInstructions, bool isWrite) const;
32+
33+
void mergeVariableTypes(Branch *branch, Branch *previousBranch) const;
34+
void overrideVariableTypes(Branch *branch, Branch *previousBranch) const;
35+
void mergeListTypes(Branch *branch, Branch *previousBranch) const;
3236

3337
bool isLoopStart(const LLVMInstruction *ins) const;
3438
bool isLoopEnd(const LLVMInstruction *ins) const;
@@ -39,6 +43,10 @@ class LLVMCodeAnalyzer
3943
bool isVariableRead(const LLVMInstruction *ins) const;
4044
bool isVariableWrite(const LLVMInstruction *ins) const;
4145

46+
bool isListRead(const LLVMInstruction *ins) const;
47+
bool isListWrite(const LLVMInstruction *ins) const;
48+
bool isListClear(const LLVMInstruction *ins) const;
49+
4250
Compiler::StaticType writeType(LLVMInstruction *ins) const;
4351
};
4452

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
llvmcodebuilder_test.cpp
2424
llvminstructionlist_test.cpp
2525
code_analyzer/variable_type_analysis.cpp
26+
code_analyzer/list_type_analysis.cpp
2627
code_analyzer/mixed_type_analysis.cpp
2728
operators/equal_comparison_test.cpp
2829
operators/greater_than_test.cpp

0 commit comments

Comments
 (0)