Skip to content

Commit 2ed0bf3

Browse files
authored
Merge pull request #622 from scratchcpp/fix_loop_type_prediction
Implement type analysis in loops
2 parents 7a86400 + 05cd43f commit 2ed0bf3

File tree

9 files changed

+2259
-209
lines changed

9 files changed

+2259
-209
lines changed

src/engine/internal/llvm/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ target_sources(scratchcpp
1616
llvmtypes.cpp
1717
llvmtypes.h
1818
llvmfunctions.cpp
19+
llvmloopscope.h
1920
llvmcompilercontext.cpp
2021
llvmcompilercontext.h
2122
llvmexecutablecode.cpp

src/engine/internal/llvm/llvmcodebuilder.cpp

Lines changed: 446 additions & 78 deletions
Large diffs are not rendered by default.

src/engine/internal/llvm/llvmcodebuilder.h

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ namespace libscratchcpp
2020

2121
class LLVMCompilerContext;
2222
class LLVMConstantRegister;
23+
class LLVMLoopScope;
2324

2425
class LLVMCodeBuilder : public ICodeBuilder
2526
{
@@ -119,22 +120,24 @@ class LLVMCodeBuilder : public ICodeBuilder
119120
void createListMap();
120121
void pushScopeLevel();
121122
void popScopeLevel();
123+
void pushLoopScope(bool buildPhase);
124+
void popLoopScope();
122125

123126
std::string getMainFunctionName(BlockPrototype *procedurePrototype);
124127
std::string getResumeFunctionName(BlockPrototype *procedurePrototype);
125128
llvm::FunctionType *getMainFunctionType(BlockPrototype *procedurePrototype);
126129
llvm::Function *getOrCreateFunction(const std::string &name, llvm::FunctionType *type);
127130
void verifyFunction(llvm::Function *func);
128131

129-
LLVMRegister *addReg(std::shared_ptr<LLVMRegister> reg);
132+
LLVMRegister *addReg(std::shared_ptr<LLVMRegister> reg, std::shared_ptr<LLVMInstruction> ins);
130133

131134
llvm::Value *addAlloca(llvm::Type *type);
132135
void freeLater(llvm::Value *value);
133136
void freeScopeHeap();
134137
llvm::Value *castValue(LLVMRegister *reg, Compiler::StaticType targetType);
135138
llvm::Value *castRawValue(LLVMRegister *reg, Compiler::StaticType targetType);
136139
llvm::Constant *castConstValue(const Value &value, Compiler::StaticType targetType);
137-
Compiler::StaticType optimizeRegisterType(LLVMRegister *reg);
140+
Compiler::StaticType optimizeRegisterType(LLVMRegister *reg) const;
138141
llvm::Type *getType(Compiler::StaticType type);
139142
Compiler::StaticType getProcedureArgType(BlockPrototype::ArgType type);
140143
llvm::Value *isNaN(llvm::Value *num);
@@ -146,9 +149,15 @@ class LLVMCodeBuilder : public ICodeBuilder
146149
void reloadVariables(llvm::Value *targetVariables);
147150
void reloadLists();
148151
void updateListDataPtr(const LLVMListPtr &listPtr);
152+
bool isVarOrListTypeSafe(std::shared_ptr<LLVMInstruction> ins, Compiler::StaticType expectedType) const;
153+
bool isVarOrListTypeSafe(std::shared_ptr<LLVMInstruction> ins, Compiler::StaticType expectedType, std::unordered_set<LLVMInstruction *> &log, int &c) const;
154+
bool isVarOrListWriteResultTypeSafe(std::shared_ptr<LLVMInstruction> ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set<LLVMInstruction *> &log, int &c) const;
149155

156+
LLVMRegister *createOp(LLVMInstruction::Type type, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args);
157+
LLVMRegister *createOp(LLVMInstruction::Type type, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes = {}, const Compiler::Args &args = {});
150158
LLVMRegister *createOp(const LLVMInstruction &ins, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args);
151159
LLVMRegister *createOp(const LLVMInstruction &ins, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes = {}, const Compiler::Args &args = {});
160+
std::shared_ptr<LLVMLoopScope> currentLoopScope() const;
152161

153162
void createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType);
154163
void createReusedValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType);
@@ -215,14 +224,21 @@ class LLVMCodeBuilder : public ICodeBuilder
215224
llvm::StructType *m_valueDataType = nullptr;
216225
llvm::FunctionType *m_resumeFuncType = nullptr;
217226

218-
std::vector<LLVMInstruction> m_instructions;
227+
std::vector<std::shared_ptr<LLVMInstruction>> m_instructions;
219228
std::vector<std::shared_ptr<LLVMRegister>> m_regs;
220229
std::vector<std::shared_ptr<CompilerLocalVariable>> m_localVars;
221230
BlockPrototype *m_procedurePrototype = nullptr;
222231
bool m_defaultWarp = false;
223232
bool m_warp = false;
224233
int m_defaultArgCount = 0;
225234

235+
long m_loopScope = -1; // index
236+
std::vector<std::shared_ptr<LLVMLoopScope>> m_loopScopes;
237+
long m_loopScopeCounter = 0; // replacement for m_loopScopes size in build phase
238+
std::vector<long> m_loopScopeTree;
239+
bool m_loopCondition = false; // whether we're currently compiling a loop condition
240+
std::vector<std::shared_ptr<LLVMInstruction>> m_variableInstructions;
241+
std::vector<std::shared_ptr<LLVMInstruction>> m_listInstructions;
226242
std::vector<std::vector<llvm::Value *>> m_heap; // scopes
227243

228244
std::shared_ptr<ExecutableCode> m_output;

src/engine/internal/llvm/llvminstruction.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace libscratchcpp
1010
{
1111

1212
class BlockPrototype;
13+
class LLVMLoopScope;
1314

1415
struct LLVMInstruction
1516
{
@@ -77,8 +78,10 @@ struct LLVMInstruction
7778
ProcedureArg
7879
};
7980

80-
LLVMInstruction(Type type) :
81-
type(type)
81+
LLVMInstruction(Type type, std::shared_ptr<LLVMLoopScope> loopScope, bool loopCondition) :
82+
type(type),
83+
loopScope(loopScope),
84+
loopCondition(loopCondition)
8285
{
8386
}
8487

@@ -92,6 +95,8 @@ struct LLVMInstruction
9295
List *workList = nullptr; // for lists
9396
BlockPrototype *procedurePrototype = nullptr;
9497
size_t procedureArgIndex = 0;
98+
std::shared_ptr<LLVMLoopScope> loopScope;
99+
bool loopCondition = false; // whether the instruction is part of a loop condition
95100
};
96101

97102
} // namespace libscratchcpp

src/engine/internal/llvm/llvmlistptr.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#pragma once
44

55
#include <scratchcpp/compiler.h>
6+
#include <unordered_map>
67

78
namespace llvm
89
{
@@ -14,6 +15,9 @@ class Value;
1415
namespace libscratchcpp
1516
{
1617

18+
class LLVMLoopScope;
19+
class LLVMInstruction;
20+
1721
struct LLVMListPtr
1822
{
1923
llvm::Value *ptr = nullptr;
@@ -22,6 +26,9 @@ struct LLVMListPtr
2226
llvm::Value *allocatedSizePtr = nullptr;
2327
llvm::Value *dataPtrDirty = nullptr;
2428
Compiler::StaticType type = Compiler::StaticType::Unknown;
29+
30+
// Used in build phase to check the type safety of lists in loops
31+
std::unordered_map<std::shared_ptr<LLVMLoopScope>, std::vector<std::shared_ptr<LLVMInstruction>>> loopListWrites; // loop scope, write instructions
2532
};
2633

2734
} // namespace libscratchcpp
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include <vector>
6+
#include <memory>
7+
8+
namespace libscratchcpp
9+
{
10+
11+
struct LLVMLoopScope
12+
{
13+
bool containsYield = false;
14+
std::shared_ptr<LLVMLoopScope> parentScope;
15+
std::vector<std::shared_ptr<LLVMLoopScope>> childScopes;
16+
};
17+
18+
} // namespace libscratchcpp

src/engine/internal/llvm/llvmregisterbase.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ class Value;
1515
namespace libscratchcpp
1616
{
1717

18+
class LLVMInstruction;
19+
1820
struct LLVMRegisterBase
1921
{
2022
virtual const Value &constValue() const = 0;
2123

2224
llvm::Value *value = nullptr;
2325
bool isRawValue = false;
26+
std::shared_ptr<LLVMInstruction> instruction;
2427
};
2528

2629
} // namespace libscratchcpp

src/engine/internal/llvm/llvmvariableptr.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#pragma once
44

55
#include <scratchcpp/compiler.h>
6+
#include <unordered_map>
67

78
namespace llvm
89
{
@@ -14,13 +15,19 @@ class Value;
1415
namespace libscratchcpp
1516
{
1617

18+
class LLVMLoopScope;
19+
class LLVMInstruction;
20+
1721
struct LLVMVariablePtr
1822
{
1923
llvm::Value *stackPtr = nullptr;
2024
llvm::Value *heapPtr = nullptr;
2125
Compiler::StaticType type = Compiler::StaticType::Unknown;
2226
bool onStack = false;
2327
bool changed = false;
28+
29+
// Used in build phase to check the type safety of variables in loops
30+
std::unordered_map<std::shared_ptr<LLVMLoopScope>, std::vector<std::shared_ptr<LLVMInstruction>>> loopVariableWrites; // loop scope, write instructions
2431
};
2532

2633
} // namespace libscratchcpp

0 commit comments

Comments
 (0)