From 89f44259f04e2b7c506bb20185ffd8305af0f08a Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:06:46 +0100 Subject: [PATCH 01/21] LLVMCodeBuilder: Do not recreate var pointers when vars are used --- src/dev/engine/internal/llvm/llvmcodebuilder.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index e300f705..74a56115 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -1251,7 +1251,9 @@ CompilerValue *LLVMCodeBuilder::addVariableValue(Variable *variable) { LLVMInstruction ins(LLVMInstruction::Type::ReadVariable); ins.workVariable = variable; - m_variablePtrs[variable] = LLVMVariablePtr(); + + if (m_variablePtrs.find(variable) == m_variablePtrs.cend()) + m_variablePtrs[variable] = LLVMVariablePtr(); auto ret = std::make_shared(Compiler::StaticType::Unknown); ret->isRawValue = false; @@ -1506,8 +1508,10 @@ void LLVMCodeBuilder::createVariableWrite(Variable *variable, CompilerValue *val { LLVMInstruction ins(LLVMInstruction::Type::WriteVariable); ins.workVariable = variable; - m_variablePtrs[variable] = LLVMVariablePtr(); createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Unknown, { value }); + + if (m_variablePtrs.find(variable) == m_variablePtrs.cend()) + m_variablePtrs[variable] = LLVMVariablePtr(); } void LLVMCodeBuilder::createListClear(List *list) From 1fa99e70731e826633c654de2d9ea8e69667c337 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:10:33 +0100 Subject: [PATCH 02/21] LLVMCodeBuilder: Do not recreate list pointers when lists are used --- .../engine/internal/llvm/llvmcodebuilder.cpp | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index 74a56115..fec1b92c 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -1267,7 +1267,10 @@ CompilerValue *LLVMCodeBuilder::addListContents(List *list) { LLVMInstruction ins(LLVMInstruction::Type::GetListContents); ins.workList = list; - m_listPtrs[list] = LLVMListPtr(); + + if (m_listPtrs.find(list) == m_listPtrs.cend()) + m_listPtrs[list] = LLVMListPtr(); + return createOp(ins, Compiler::StaticType::String); } @@ -1275,7 +1278,9 @@ CompilerValue *LLVMCodeBuilder::addListItem(List *list, CompilerValue *index) { LLVMInstruction ins(LLVMInstruction::Type::GetListItem); ins.workList = list; - m_listPtrs[list] = LLVMListPtr(); + + if (m_listPtrs.find(list) == m_listPtrs.cend()) + m_listPtrs[list] = LLVMListPtr(); ins.args.push_back({ Compiler::StaticType::Number, static_cast(index) }); @@ -1291,7 +1296,10 @@ CompilerValue *LLVMCodeBuilder::addListItemIndex(List *list, CompilerValue *item { LLVMInstruction ins(LLVMInstruction::Type::GetListItemIndex); ins.workList = list; - m_listPtrs[list] = LLVMListPtr(); + + if (m_listPtrs.find(list) == m_listPtrs.cend()) + m_listPtrs[list] = LLVMListPtr(); + return createOp(ins, Compiler::StaticType::Number, Compiler::StaticType::Unknown, { item }); } @@ -1299,7 +1307,10 @@ CompilerValue *LLVMCodeBuilder::addListContains(List *list, CompilerValue *item) { LLVMInstruction ins(LLVMInstruction::Type::ListContainsItem); ins.workList = list; - m_listPtrs[list] = LLVMListPtr(); + + if (m_listPtrs.find(list) == m_listPtrs.cend()) + m_listPtrs[list] = LLVMListPtr(); + return createOp(ins, Compiler::StaticType::Bool, Compiler::StaticType::Unknown, { item }); } @@ -1307,7 +1318,10 @@ CompilerValue *LLVMCodeBuilder::addListSize(List *list) { LLVMInstruction ins(LLVMInstruction::Type::GetListSize); ins.workList = list; - m_listPtrs[list] = LLVMListPtr(); + + if (m_listPtrs.find(list) == m_listPtrs.cend()) + m_listPtrs[list] = LLVMListPtr(); + return createOp(ins, Compiler::StaticType::Number); } @@ -1518,40 +1532,50 @@ void LLVMCodeBuilder::createListClear(List *list) { LLVMInstruction ins(LLVMInstruction::Type::ClearList); ins.workList = list; - m_listPtrs[list] = LLVMListPtr(); createOp(ins, Compiler::StaticType::Void); + + if (m_listPtrs.find(list) == m_listPtrs.cend()) + m_listPtrs[list] = LLVMListPtr(); } void LLVMCodeBuilder::createListRemove(List *list, CompilerValue *index) { LLVMInstruction ins(LLVMInstruction::Type::RemoveListItem); ins.workList = list; - m_listPtrs[list] = LLVMListPtr(); createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Number, { index }); + + if (m_listPtrs.find(list) == m_listPtrs.cend()) + m_listPtrs[list] = LLVMListPtr(); } void LLVMCodeBuilder::createListAppend(List *list, CompilerValue *item) { LLVMInstruction ins(LLVMInstruction::Type::AppendToList); ins.workList = list; - m_listPtrs[list] = LLVMListPtr(); createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Unknown, { item }); + + if (m_listPtrs.find(list) == m_listPtrs.cend()) + m_listPtrs[list] = LLVMListPtr(); } void LLVMCodeBuilder::createListInsert(List *list, CompilerValue *index, CompilerValue *item) { LLVMInstruction ins(LLVMInstruction::Type::InsertToList); ins.workList = list; - m_listPtrs[list] = LLVMListPtr(); createOp(ins, Compiler::StaticType::Void, { Compiler::StaticType::Number, Compiler::StaticType::Unknown }, { index, item }); + + if (m_listPtrs.find(list) == m_listPtrs.cend()) + m_listPtrs[list] = LLVMListPtr(); } void LLVMCodeBuilder::createListReplace(List *list, CompilerValue *index, CompilerValue *item) { LLVMInstruction ins(LLVMInstruction::Type::ListReplace); ins.workList = list; - m_listPtrs[list] = LLVMListPtr(); createOp(ins, Compiler::StaticType::Void, { Compiler::StaticType::Number, Compiler::StaticType::Unknown }, { index, item }); + + if (m_listPtrs.find(list) == m_listPtrs.cend()) + m_listPtrs[list] = LLVMListPtr(); } void LLVMCodeBuilder::beginIfStatement(CompilerValue *cond) From 6727c57c743d7a8084b26de43ab12d3501d616ca Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:05:28 +0100 Subject: [PATCH 03/21] LLVMCodeBuilder: Add loop scopes --- src/dev/engine/internal/llvm/CMakeLists.txt | 1 + .../engine/internal/llvm/llvmcodebuilder.cpp | 159 ++++++++++++------ .../engine/internal/llvm/llvmcodebuilder.h | 12 +- .../engine/internal/llvm/llvminstruction.h | 7 +- src/dev/engine/internal/llvm/llvmloopscope.h | 18 ++ 5 files changed, 145 insertions(+), 52 deletions(-) create mode 100644 src/dev/engine/internal/llvm/llvmloopscope.h diff --git a/src/dev/engine/internal/llvm/CMakeLists.txt b/src/dev/engine/internal/llvm/CMakeLists.txt index e1bb3ef5..38b7e4d2 100644 --- a/src/dev/engine/internal/llvm/CMakeLists.txt +++ b/src/dev/engine/internal/llvm/CMakeLists.txt @@ -16,6 +16,7 @@ target_sources(scratchcpp llvmtypes.cpp llvmtypes.h llvmfunctions.cpp + llvmloopscope.h llvmcompilercontext.cpp llvmcompilercontext.h llvmexecutablecode.cpp diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index fec1b92c..0d52e115 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -16,6 +16,7 @@ #include "llvmifstatement.h" #include "llvmloop.h" #include "llvmtypes.h" +#include "llvmloopscope.h" using namespace libscratchcpp; @@ -44,8 +45,8 @@ std::shared_ptr LLVMCodeBuilder::finalize() { if (!m_warp) { // Do not create coroutine if there are no yield instructions nor non-warp procedure calls - auto it = std::find_if(m_instructions.begin(), m_instructions.end(), [](const LLVMInstruction &step) { - return step.type == LLVMInstruction::Type::Yield || (step.type == LLVMInstruction::Type::CallProcedure && step.procedurePrototype && !step.procedurePrototype->warp()); + auto it = std::find_if(m_instructions.begin(), m_instructions.end(), [](const std::shared_ptr &step) { + return step->type == LLVMInstruction::Type::Yield || (step->type == LLVMInstruction::Type::CallProcedure && step->procedurePrototype && !step->procedurePrototype->warp()); }); if (it == m_instructions.end()) @@ -122,12 +123,16 @@ std::shared_ptr LLVMCodeBuilder::finalize() m_builder.CreateStore(m_builder.getInt1(false), listPtr.dataPtrDirty); } + assert(m_loopScope == -1); + m_loopScope = -1; m_scopeVariables.clear(); m_scopeLists.clear(); pushScopeLevel(); // Execute recorded steps - for (const LLVMInstruction &step : m_instructions) { + for (const auto insPtr : m_instructions) { + const LLVMInstruction &step = *insPtr; + switch (step.type) { case LLVMInstruction::Type::FunctionCall: { std::vector types; @@ -1007,6 +1012,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() loops.push_back(loop); pushScopeLevel(); + pushLoopScope(true); break; } @@ -1036,6 +1042,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() // Switch to body branch m_builder.SetInsertPoint(body); pushScopeLevel(); + pushLoopScope(true); break; } @@ -1057,6 +1064,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() // Switch to body branch m_builder.SetInsertPoint(body); pushScopeLevel(); + pushLoopScope(true); break; } @@ -1090,6 +1098,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() loops.pop_back(); popScopeLevel(); + popLoopScope(); break; } @@ -1198,16 +1207,16 @@ CompilerValue *LLVMCodeBuilder::addFunctionCall(const std::string &functionName, { assert(argTypes.size() == args.size()); - LLVMInstruction ins(LLVMInstruction::Type::FunctionCall); - ins.functionName = functionName; + auto ins = std::make_shared(LLVMInstruction::Type::FunctionCall, currentLoopScope()); + ins->functionName = functionName; for (size_t i = 0; i < args.size(); i++) - ins.args.push_back({ argTypes[i], static_cast(args[i]) }); + ins->args.push_back({ argTypes[i], static_cast(args[i]) }); if (returnType != Compiler::StaticType::Void) { auto reg = std::make_shared(returnType); reg->isRawValue = true; - ins.functionReturnReg = reg.get(); + ins->functionReturnReg = reg.get(); m_instructions.push_back(ins); return addReg(reg); } @@ -1219,14 +1228,14 @@ CompilerValue *LLVMCodeBuilder::addFunctionCall(const std::string &functionName, CompilerValue *LLVMCodeBuilder::addTargetFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) { CompilerValue *ret = addFunctionCall(functionName, returnType, argTypes, args); - m_instructions.back().functionTargetArg = true; + m_instructions.back()->functionTargetArg = true; return ret; } CompilerValue *LLVMCodeBuilder::addFunctionCallWithCtx(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) { CompilerValue *ret = addFunctionCall(functionName, returnType, argTypes, args); - m_instructions.back().functionCtxArg = true; + m_instructions.back()->functionCtxArg = true; return ret; } @@ -1249,15 +1258,15 @@ CompilerValue *LLVMCodeBuilder::addLocalVariableValue(CompilerLocalVariable *var CompilerValue *LLVMCodeBuilder::addVariableValue(Variable *variable) { - LLVMInstruction ins(LLVMInstruction::Type::ReadVariable); - ins.workVariable = variable; + auto ins = std::make_shared(LLVMInstruction::Type::ReadVariable, currentLoopScope()); + ins->workVariable = variable; if (m_variablePtrs.find(variable) == m_variablePtrs.cend()) m_variablePtrs[variable] = LLVMVariablePtr(); auto ret = std::make_shared(Compiler::StaticType::Unknown); ret->isRawValue = false; - ins.functionReturnReg = ret.get(); + ins->functionReturnReg = ret.get(); m_instructions.push_back(ins); return addReg(ret); @@ -1265,7 +1274,7 @@ CompilerValue *LLVMCodeBuilder::addVariableValue(Variable *variable) CompilerValue *LLVMCodeBuilder::addListContents(List *list) { - LLVMInstruction ins(LLVMInstruction::Type::GetListContents); + LLVMInstruction ins(LLVMInstruction::Type::GetListContents, currentLoopScope()); ins.workList = list; if (m_listPtrs.find(list) == m_listPtrs.cend()) @@ -1276,17 +1285,17 @@ CompilerValue *LLVMCodeBuilder::addListContents(List *list) CompilerValue *LLVMCodeBuilder::addListItem(List *list, CompilerValue *index) { - LLVMInstruction ins(LLVMInstruction::Type::GetListItem); - ins.workList = list; + auto ins = std::make_shared(LLVMInstruction::Type::GetListItem, currentLoopScope()); + ins->workList = list; if (m_listPtrs.find(list) == m_listPtrs.cend()) m_listPtrs[list] = LLVMListPtr(); - ins.args.push_back({ Compiler::StaticType::Number, static_cast(index) }); + ins->args.push_back({ Compiler::StaticType::Number, static_cast(index) }); auto ret = std::make_shared(Compiler::StaticType::Unknown); ret->isRawValue = false; - ins.functionReturnReg = ret.get(); + ins->functionReturnReg = ret.get(); m_instructions.push_back(ins); return addReg(ret); @@ -1294,7 +1303,7 @@ CompilerValue *LLVMCodeBuilder::addListItem(List *list, CompilerValue *index) CompilerValue *LLVMCodeBuilder::addListItemIndex(List *list, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::GetListItemIndex); + LLVMInstruction ins(LLVMInstruction::Type::GetListItemIndex, currentLoopScope()); ins.workList = list; if (m_listPtrs.find(list) == m_listPtrs.cend()) @@ -1305,7 +1314,7 @@ CompilerValue *LLVMCodeBuilder::addListItemIndex(List *list, CompilerValue *item CompilerValue *LLVMCodeBuilder::addListContains(List *list, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::ListContainsItem); + LLVMInstruction ins(LLVMInstruction::Type::ListContainsItem, currentLoopScope()); ins.workList = list; if (m_listPtrs.find(list) == m_listPtrs.cend()) @@ -1316,7 +1325,7 @@ CompilerValue *LLVMCodeBuilder::addListContains(List *list, CompilerValue *item) CompilerValue *LLVMCodeBuilder::addListSize(List *list) { - LLVMInstruction ins(LLVMInstruction::Type::GetListSize); + LLVMInstruction ins(LLVMInstruction::Type::GetListSize, currentLoopScope()); ins.workList = list; if (m_listPtrs.find(list) == m_listPtrs.cend()) @@ -1340,11 +1349,11 @@ CompilerValue *LLVMCodeBuilder::addProcedureArgument(const std::string &name) const auto index = it - argNames.begin(); const Compiler::StaticType type = getProcedureArgType(m_procedurePrototype->argumentTypes()[index]); - LLVMInstruction ins(LLVMInstruction::Type::ProcedureArg); + auto ins = std::make_shared(LLVMInstruction::Type::ProcedureArg, currentLoopScope()); auto ret = std::make_shared(type); ret->isRawValue = (type != Compiler::StaticType::Unknown); - ins.functionReturnReg = ret.get(); - ins.procedureArgIndex = index; + ins->functionReturnReg = ret.get(); + ins->procedureArgIndex = index; m_instructions.push_back(ins); return addReg(ret); @@ -1520,7 +1529,7 @@ void LLVMCodeBuilder::createLocalVariableWrite(CompilerLocalVariable *variable, void LLVMCodeBuilder::createVariableWrite(Variable *variable, CompilerValue *value) { - LLVMInstruction ins(LLVMInstruction::Type::WriteVariable); + LLVMInstruction ins(LLVMInstruction::Type::WriteVariable, currentLoopScope()); ins.workVariable = variable; createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Unknown, { value }); @@ -1530,7 +1539,7 @@ void LLVMCodeBuilder::createVariableWrite(Variable *variable, CompilerValue *val void LLVMCodeBuilder::createListClear(List *list) { - LLVMInstruction ins(LLVMInstruction::Type::ClearList); + LLVMInstruction ins(LLVMInstruction::Type::ClearList, currentLoopScope()); ins.workList = list; createOp(ins, Compiler::StaticType::Void); @@ -1540,7 +1549,7 @@ void LLVMCodeBuilder::createListClear(List *list) void LLVMCodeBuilder::createListRemove(List *list, CompilerValue *index) { - LLVMInstruction ins(LLVMInstruction::Type::RemoveListItem); + LLVMInstruction ins(LLVMInstruction::Type::RemoveListItem, currentLoopScope()); ins.workList = list; createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Number, { index }); @@ -1550,7 +1559,7 @@ void LLVMCodeBuilder::createListRemove(List *list, CompilerValue *index) void LLVMCodeBuilder::createListAppend(List *list, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::AppendToList); + LLVMInstruction ins(LLVMInstruction::Type::AppendToList, currentLoopScope()); ins.workList = list; createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Unknown, { item }); @@ -1560,7 +1569,7 @@ void LLVMCodeBuilder::createListAppend(List *list, CompilerValue *item) void LLVMCodeBuilder::createListInsert(List *list, CompilerValue *index, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::InsertToList); + LLVMInstruction ins(LLVMInstruction::Type::InsertToList, currentLoopScope()); ins.workList = list; createOp(ins, Compiler::StaticType::Void, { Compiler::StaticType::Number, Compiler::StaticType::Unknown }, { index, item }); @@ -1570,7 +1579,7 @@ void LLVMCodeBuilder::createListInsert(List *list, CompilerValue *index, Compile void LLVMCodeBuilder::createListReplace(List *list, CompilerValue *index, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::ListReplace); + LLVMInstruction ins(LLVMInstruction::Type::ListReplace, currentLoopScope()); ins.workList = list; createOp(ins, Compiler::StaticType::Void, { Compiler::StaticType::Number, Compiler::StaticType::Unknown }, { index, item }); @@ -1580,63 +1589,70 @@ void LLVMCodeBuilder::createListReplace(List *list, CompilerValue *index, Compil void LLVMCodeBuilder::beginIfStatement(CompilerValue *cond) { - LLVMInstruction ins(LLVMInstruction::Type::BeginIf); - ins.args.push_back({ Compiler::StaticType::Bool, static_cast(cond) }); + auto ins = std::make_shared(LLVMInstruction::Type::BeginIf, currentLoopScope()); + ins->args.push_back({ Compiler::StaticType::Bool, static_cast(cond) }); m_instructions.push_back(ins); } void LLVMCodeBuilder::beginElseBranch() { - m_instructions.push_back(LLVMInstruction(LLVMInstruction::Type::BeginElse)); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::BeginElse, currentLoopScope())); } void LLVMCodeBuilder::endIf() { - m_instructions.push_back(LLVMInstruction(LLVMInstruction::Type::EndIf)); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::EndIf, currentLoopScope())); } void LLVMCodeBuilder::beginRepeatLoop(CompilerValue *count) { - LLVMInstruction ins(LLVMInstruction::Type::BeginRepeatLoop); - ins.args.push_back({ Compiler::StaticType::Number, static_cast(count) }); + auto ins = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, currentLoopScope()); + ins->args.push_back({ Compiler::StaticType::Number, static_cast(count) }); m_instructions.push_back(ins); + pushLoopScope(false); } void LLVMCodeBuilder::beginWhileLoop(CompilerValue *cond) { - LLVMInstruction ins(LLVMInstruction::Type::BeginWhileLoop); - ins.args.push_back({ Compiler::StaticType::Bool, static_cast(cond) }); + auto ins = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, currentLoopScope()); + ins->args.push_back({ Compiler::StaticType::Bool, static_cast(cond) }); m_instructions.push_back(ins); + pushLoopScope(false); } void LLVMCodeBuilder::beginRepeatUntilLoop(CompilerValue *cond) { - LLVMInstruction ins(LLVMInstruction::Type::BeginRepeatUntilLoop); - ins.args.push_back({ Compiler::StaticType::Bool, static_cast(cond) }); + auto ins = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, currentLoopScope()); + ins->args.push_back({ Compiler::StaticType::Bool, static_cast(cond) }); m_instructions.push_back(ins); + pushLoopScope(false); } void LLVMCodeBuilder::beginLoopCondition() { - m_instructions.push_back({ LLVMInstruction::Type::BeginLoopCondition }); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::BeginLoopCondition, currentLoopScope())); } void LLVMCodeBuilder::endLoop() { if (!m_warp) - m_instructions.push_back(LLVMInstruction(LLVMInstruction::Type::Yield)); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::Yield, currentLoopScope())); - m_instructions.push_back(LLVMInstruction(LLVMInstruction::Type::EndLoop)); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::EndLoop, currentLoopScope())); + popLoopScope(); } void LLVMCodeBuilder::yield() { - m_instructions.push_back({ LLVMInstruction::Type::Yield }); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::Yield, currentLoopScope())); + + if (m_loopScope >= 0) + m_loopScopes[m_loopScope]->containsYield = true; } void LLVMCodeBuilder::createStop() { - m_instructions.push_back({ LLVMInstruction::Type::Stop }); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::Stop, currentLoopScope())); } void LLVMCodeBuilder::createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args) @@ -1649,7 +1665,7 @@ void LLVMCodeBuilder::createProcedureCall(BlockPrototype *prototype, const Compi for (BlockPrototype::ArgType type : procedureArgs) types.push_back(getProcedureArgType(type)); - LLVMInstruction ins(LLVMInstruction::Type::CallProcedure); + LLVMInstruction ins(LLVMInstruction::Type::CallProcedure, currentLoopScope()); ins.procedurePrototype = prototype; createOp(ins, Compiler::StaticType::Void, types, args); } @@ -1762,6 +1778,36 @@ void LLVMCodeBuilder::popScopeLevel() m_heap.pop_back(); } +void LLVMCodeBuilder::pushLoopScope(bool buildPhase) +{ + if (buildPhase) + m_loopScope = m_loopScopeCounter++; + else { + auto scope = std::make_shared(); + m_loopScopes.push_back(scope); + + if (m_loopScope >= 0) { + auto currentScope = m_loopScopes[m_loopScope]; + currentScope->childScopes.push_back(scope); + scope->parentScope = currentScope; + } + + m_loopScope = m_loopScopes.size() - 1; + } + + m_loopScopeTree.push_back(m_loopScope); +} + +void LLVMCodeBuilder::popLoopScope() +{ + m_loopScopeTree.pop_back(); + + if (m_loopScopeTree.empty()) { + m_loopScope = -1; + } else + m_loopScope = m_loopScopeTree.back(); +} + std::string LLVMCodeBuilder::getMainFunctionName(BlockPrototype *procedurePrototype) { return procedurePrototype ? "proc." + procedurePrototype->procCode() : "script"; @@ -2147,6 +2193,16 @@ void LLVMCodeBuilder::updateListDataPtr(const LLVMListPtr &listPtr) m_builder.CreateStore(m_builder.getInt1(false), listPtr.dataPtrDirty); } +LLVMRegister *LLVMCodeBuilder::createOp(LLVMInstruction::Type type, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args) +{ + return createOp({ type, currentLoopScope() }, retType, argType, args); +} + +LLVMRegister *LLVMCodeBuilder::createOp(LLVMInstruction::Type type, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) +{ + return createOp({ type, currentLoopScope() }, retType, argTypes, args); +} + LLVMRegister *LLVMCodeBuilder::createOp(const LLVMInstruction &ins, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args) { std::vector types; @@ -2160,22 +2216,27 @@ LLVMRegister *LLVMCodeBuilder::createOp(const LLVMInstruction &ins, Compiler::St LLVMRegister *LLVMCodeBuilder::createOp(const LLVMInstruction &ins, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) { - m_instructions.push_back(ins); - LLVMInstruction &createdIns = m_instructions.back(); + auto createdIns = std::make_shared(ins); + m_instructions.push_back(createdIns); for (size_t i = 0; i < args.size(); i++) - createdIns.args.push_back({ argTypes[i], static_cast(args[i]) }); + createdIns->args.push_back({ argTypes[i], static_cast(args[i]) }); if (retType != Compiler::StaticType::Void) { auto ret = std::make_shared(retType); ret->isRawValue = true; - createdIns.functionReturnReg = ret.get(); + createdIns->functionReturnReg = ret.get(); return addReg(ret); } return nullptr; } +std::shared_ptr LLVMCodeBuilder::currentLoopScope() const +{ + return m_loopScope >= 0 ? m_loopScopes[m_loopScope] : nullptr; +} + void LLVMCodeBuilder::createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType) { llvm::Value *converted = nullptr; diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.h b/src/dev/engine/internal/llvm/llvmcodebuilder.h index f6a6142e..bd5affe1 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.h @@ -20,6 +20,7 @@ namespace libscratchcpp class LLVMCompilerContext; class LLVMConstantRegister; +class LLVMLoopScope; class LLVMCodeBuilder : public ICodeBuilder { @@ -119,6 +120,8 @@ class LLVMCodeBuilder : public ICodeBuilder void createListMap(); void pushScopeLevel(); void popScopeLevel(); + void pushLoopScope(bool buildPhase); + void popLoopScope(); std::string getMainFunctionName(BlockPrototype *procedurePrototype); std::string getResumeFunctionName(BlockPrototype *procedurePrototype); @@ -147,8 +150,11 @@ class LLVMCodeBuilder : public ICodeBuilder void reloadLists(); void updateListDataPtr(const LLVMListPtr &listPtr); + LLVMRegister *createOp(LLVMInstruction::Type type, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args); + LLVMRegister *createOp(LLVMInstruction::Type type, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes = {}, const Compiler::Args &args = {}); LLVMRegister *createOp(const LLVMInstruction &ins, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args); LLVMRegister *createOp(const LLVMInstruction &ins, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes = {}, const Compiler::Args &args = {}); + std::shared_ptr currentLoopScope() const; void createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType); void createReusedValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType); @@ -215,7 +221,7 @@ class LLVMCodeBuilder : public ICodeBuilder llvm::StructType *m_valueDataType = nullptr; llvm::FunctionType *m_resumeFuncType = nullptr; - std::vector m_instructions; + std::vector> m_instructions; std::vector> m_regs; std::vector> m_localVars; BlockPrototype *m_procedurePrototype = nullptr; @@ -223,6 +229,10 @@ class LLVMCodeBuilder : public ICodeBuilder bool m_warp = false; int m_defaultArgCount = 0; + long m_loopScope = -1; // index + std::vector> m_loopScopes; + long m_loopScopeCounter = 0; // replacement for m_loopScopes size in build phase + std::vector m_loopScopeTree; std::vector> m_heap; // scopes std::shared_ptr m_output; diff --git a/src/dev/engine/internal/llvm/llvminstruction.h b/src/dev/engine/internal/llvm/llvminstruction.h index ddb39677..4f1787f5 100644 --- a/src/dev/engine/internal/llvm/llvminstruction.h +++ b/src/dev/engine/internal/llvm/llvminstruction.h @@ -10,6 +10,7 @@ namespace libscratchcpp { class BlockPrototype; +class LLVMLoopScope; struct LLVMInstruction { @@ -77,8 +78,9 @@ struct LLVMInstruction ProcedureArg }; - LLVMInstruction(Type type) : - type(type) + LLVMInstruction(Type type, std::shared_ptr loopScope) : + type(type), + loopScope(loopScope) { } @@ -92,6 +94,7 @@ struct LLVMInstruction List *workList = nullptr; // for lists BlockPrototype *procedurePrototype = nullptr; size_t procedureArgIndex = 0; + std::shared_ptr loopScope; }; } // namespace libscratchcpp diff --git a/src/dev/engine/internal/llvm/llvmloopscope.h b/src/dev/engine/internal/llvm/llvmloopscope.h new file mode 100644 index 00000000..859c2cb1 --- /dev/null +++ b/src/dev/engine/internal/llvm/llvmloopscope.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +namespace libscratchcpp +{ + +struct LLVMLoopScope +{ + bool containsYield = false; + std::shared_ptr parentScope; + std::vector> childScopes; +}; + +} // namespace libscratchcpp From df3c103835046aa8b44143fca365e5244427432e Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:13:17 +0100 Subject: [PATCH 04/21] LLVMCodeBuilder: Store source instruction in registers --- src/dev/engine/internal/llvm/llvmcodebuilder.cpp | 15 ++++++++------- src/dev/engine/internal/llvm/llvmcodebuilder.h | 2 +- src/dev/engine/internal/llvm/llvmregisterbase.h | 3 +++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index 0d52e115..4e241b0c 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -1218,7 +1218,7 @@ CompilerValue *LLVMCodeBuilder::addFunctionCall(const std::string &functionName, reg->isRawValue = true; ins->functionReturnReg = reg.get(); m_instructions.push_back(ins); - return addReg(reg); + return addReg(reg, ins); } m_instructions.push_back(ins); @@ -1243,7 +1243,7 @@ CompilerConstant *LLVMCodeBuilder::addConstValue(const Value &value) { auto constReg = std::make_shared(TYPE_MAP[value.type()], value); auto reg = std::reinterpret_pointer_cast(constReg); - return static_cast(static_cast(addReg(reg))); + return static_cast(static_cast(addReg(reg, nullptr))); } CompilerValue *LLVMCodeBuilder::addLoopIndex() @@ -1269,7 +1269,7 @@ CompilerValue *LLVMCodeBuilder::addVariableValue(Variable *variable) ins->functionReturnReg = ret.get(); m_instructions.push_back(ins); - return addReg(ret); + return addReg(ret, ins); } CompilerValue *LLVMCodeBuilder::addListContents(List *list) @@ -1298,7 +1298,7 @@ CompilerValue *LLVMCodeBuilder::addListItem(List *list, CompilerValue *index) ins->functionReturnReg = ret.get(); m_instructions.push_back(ins); - return addReg(ret); + return addReg(ret, ins); } CompilerValue *LLVMCodeBuilder::addListItemIndex(List *list, CompilerValue *item) @@ -1356,7 +1356,7 @@ CompilerValue *LLVMCodeBuilder::addProcedureArgument(const std::string &name) ins->procedureArgIndex = index; m_instructions.push_back(ins); - return addReg(ret); + return addReg(ret, ins); } CompilerValue *LLVMCodeBuilder::createAdd(CompilerValue *operand1, CompilerValue *operand2) @@ -1857,8 +1857,9 @@ void LLVMCodeBuilder::verifyFunction(llvm::Function *func) } } -LLVMRegister *LLVMCodeBuilder::addReg(std::shared_ptr reg) +LLVMRegister *LLVMCodeBuilder::addReg(std::shared_ptr reg, std::shared_ptr ins) { + reg->instruction = ins; m_regs.push_back(reg); return reg.get(); } @@ -2226,7 +2227,7 @@ LLVMRegister *LLVMCodeBuilder::createOp(const LLVMInstruction &ins, Compiler::St auto ret = std::make_shared(retType); ret->isRawValue = true; createdIns->functionReturnReg = ret.get(); - return addReg(ret); + return addReg(ret, createdIns); } return nullptr; diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.h b/src/dev/engine/internal/llvm/llvmcodebuilder.h index bd5affe1..0cec7fb9 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.h @@ -129,7 +129,7 @@ class LLVMCodeBuilder : public ICodeBuilder llvm::Function *getOrCreateFunction(const std::string &name, llvm::FunctionType *type); void verifyFunction(llvm::Function *func); - LLVMRegister *addReg(std::shared_ptr reg); + LLVMRegister *addReg(std::shared_ptr reg, std::shared_ptr ins); llvm::Value *addAlloca(llvm::Type *type); void freeLater(llvm::Value *value); diff --git a/src/dev/engine/internal/llvm/llvmregisterbase.h b/src/dev/engine/internal/llvm/llvmregisterbase.h index 2267a6df..33aace03 100644 --- a/src/dev/engine/internal/llvm/llvmregisterbase.h +++ b/src/dev/engine/internal/llvm/llvmregisterbase.h @@ -15,12 +15,15 @@ class Value; namespace libscratchcpp { +class LLVMInstruction; + struct LLVMRegisterBase { virtual const Value &constValue() const = 0; llvm::Value *value = nullptr; bool isRawValue = false; + std::shared_ptr instruction; }; } // namespace libscratchcpp From fa9de140766f216a34065aa47d303e9094c0f49d Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:16:42 +0100 Subject: [PATCH 05/21] LLVMCodeBuilder: Store variable instructions --- src/dev/engine/internal/llvm/llvmcodebuilder.cpp | 3 +++ src/dev/engine/internal/llvm/llvmcodebuilder.h | 1 + 2 files changed, 4 insertions(+) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index 4e241b0c..132472ed 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -1269,6 +1269,7 @@ CompilerValue *LLVMCodeBuilder::addVariableValue(Variable *variable) ins->functionReturnReg = ret.get(); m_instructions.push_back(ins); + m_variableInstructions.push_back(m_instructions.back()); return addReg(ret, ins); } @@ -1535,6 +1536,8 @@ void LLVMCodeBuilder::createVariableWrite(Variable *variable, CompilerValue *val if (m_variablePtrs.find(variable) == m_variablePtrs.cend()) m_variablePtrs[variable] = LLVMVariablePtr(); + + m_variableInstructions.push_back(m_instructions.back()); } void LLVMCodeBuilder::createListClear(List *list) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.h b/src/dev/engine/internal/llvm/llvmcodebuilder.h index 0cec7fb9..4a34ffd2 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.h @@ -233,6 +233,7 @@ class LLVMCodeBuilder : public ICodeBuilder std::vector> m_loopScopes; long m_loopScopeCounter = 0; // replacement for m_loopScopes size in build phase std::vector m_loopScopeTree; + std::vector> m_variableInstructions; std::vector> m_heap; // scopes std::shared_ptr m_output; From 469c3e8af90461687c82003baaa7bcf3bacae07c Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:19:22 +0100 Subject: [PATCH 06/21] LLVMCodeBuilder: Initialize variable stack copies outside loops --- src/dev/engine/internal/llvm/llvmcodebuilder.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index 132472ed..e6d5cf82 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -106,7 +106,18 @@ std::shared_ptr LLVMCodeBuilder::finalize() // All variables are currently created on the stack and synced later (seems to be faster) // NOTE: Strings are NOT copied, only the pointer and string size are copied varPtr.stackPtr = m_builder.CreateAlloca(m_valueDataType); - varPtr.onStack = false; // use heap before the first assignment + + // If there are no write operations outside loops, initialize the stack variable now + Variable *variable = var; + auto it = std::find_if(m_variableInstructions.begin(), m_variableInstructions.end(), [variable](const std::shared_ptr &ins) { + return ins->type == LLVMInstruction::Type::WriteVariable && ins->workVariable == variable && !ins->loopScope; + }); + + if (it == m_variableInstructions.end()) { + createValueCopy(ptr, varPtr.stackPtr); + varPtr.onStack = true; + } else + varPtr.onStack = false; // use heap before the first assignment } // Create list pointers From 9f5f45ce0eb756d1ede89a718679f715993877ca Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 16 Jan 2025 23:22:19 +0100 Subject: [PATCH 07/21] LLVMCodeBuilder: Store loop variable write instructions --- src/dev/engine/internal/llvm/llvmcodebuilder.cpp | 5 +++++ src/dev/engine/internal/llvm/llvmvariableptr.h | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index e6d5cf82..491c3036 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -1548,6 +1548,11 @@ void LLVMCodeBuilder::createVariableWrite(Variable *variable, CompilerValue *val if (m_variablePtrs.find(variable) == m_variablePtrs.cend()) m_variablePtrs[variable] = LLVMVariablePtr(); + if (m_loopScope >= 0) { + auto scope = m_loopScopes[m_loopScope]; + m_variablePtrs[variable].loopVariableWrites[scope].push_back(m_instructions.back()); + } + m_variableInstructions.push_back(m_instructions.back()); } diff --git a/src/dev/engine/internal/llvm/llvmvariableptr.h b/src/dev/engine/internal/llvm/llvmvariableptr.h index c2525dfd..28105f19 100644 --- a/src/dev/engine/internal/llvm/llvmvariableptr.h +++ b/src/dev/engine/internal/llvm/llvmvariableptr.h @@ -3,6 +3,7 @@ #pragma once #include +#include namespace llvm { @@ -14,6 +15,9 @@ class Value; namespace libscratchcpp { +class LLVMLoopScope; +class LLVMInstruction; + struct LLVMVariablePtr { llvm::Value *stackPtr = nullptr; @@ -21,6 +25,9 @@ struct LLVMVariablePtr Compiler::StaticType type = Compiler::StaticType::Unknown; bool onStack = false; bool changed = false; + + // Used in build phase to check the type safety of variables in loops + std::unordered_map, std::vector>> loopVariableWrites; // loop scope, write instructions }; } // namespace libscratchcpp From f6e001345080cddb3fce047d6ac6f1130b92bdf7 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 17 Jan 2025 00:19:09 +0100 Subject: [PATCH 08/21] LLVMCodeBuilder: Implement loop type analysis for variables --- .../engine/internal/llvm/llvmcodebuilder.cpp | 173 ++++- .../engine/internal/llvm/llvmcodebuilder.h | 5 +- test/dev/llvm/llvmcodebuilder_test.cpp | 672 ++++++++++++++++++ 3 files changed, 847 insertions(+), 3 deletions(-) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index 491c3036..d674bc44 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -632,6 +632,8 @@ std::shared_ptr LLVMCodeBuilder::finalize() LLVMVariablePtr &varPtr = m_variablePtrs[step.workVariable]; varPtr.changed = true; + const bool safe = isVariableTypeSafe(insPtr, varPtr.type); + // Initialize stack variable on first assignment if (!varPtr.onStack) { varPtr.onStack = true; @@ -652,6 +654,9 @@ std::shared_ptr LLVMCodeBuilder::finalize() m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typeField); } + if (!safe) + varPtr.type = Compiler::StaticType::Unknown; + createValueStore(arg.second, varPtr.stackPtr, type, varPtr.type); varPtr.type = type; m_scopeVariables.back()[&varPtr] = varPtr.type; @@ -660,7 +665,11 @@ std::shared_ptr LLVMCodeBuilder::finalize() case LLVMInstruction::Type::ReadVariable: { assert(step.args.size() == 0); - const LLVMVariablePtr &varPtr = m_variablePtrs[step.workVariable]; + LLVMVariablePtr &varPtr = m_variablePtrs[step.workVariable]; + + if (!isVariableTypeSafe(insPtr, varPtr.type)) + varPtr.type = Compiler::StaticType::Unknown; + step.functionReturnReg->value = varPtr.onStack ? varPtr.stackPtr : varPtr.heapPtr; step.functionReturnReg->setType(varPtr.type); break; @@ -2091,7 +2100,7 @@ llvm::Constant *LLVMCodeBuilder::castConstValue(const Value &value, Compiler::St } } -Compiler::StaticType LLVMCodeBuilder::optimizeRegisterType(LLVMRegister *reg) +Compiler::StaticType LLVMCodeBuilder::optimizeRegisterType(LLVMRegister *reg) const { Compiler::StaticType ret = reg->type(); @@ -2213,6 +2222,166 @@ void LLVMCodeBuilder::updateListDataPtr(const LLVMListPtr &listPtr) m_builder.CreateStore(m_builder.getInt1(false), listPtr.dataPtrDirty); } +bool LLVMCodeBuilder::isVariableTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType) const +{ + std::unordered_set processed; + return isVariableTypeSafe(ins, expectedType, processed); +} + +bool LLVMCodeBuilder::isVariableTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, std::unordered_set &processed) const +{ + /* + * The main part of the loop type analyzer. + * + * This is a recursive function which is called when variable read + * instruction is created. It checks the last write to the + * variable in one of the loop scopes. + * + * If the last write operation writes a value with a different + * type, it will return false, otherwise true. + * + * If the last written value is from a variable, this function + * is called for it to check its type safety (that's why it is + * recursive). + * + * If the variable had a write operation before (in the same, + * parent or child loop scope), it is checked recursively. + */ + + if (!ins) + return false; + + /* + * If we are processing something that has been already + * processed, give up to avoid infinite recursion. + * + * This can happen in edge cases like this: + * var = var + * + * or this: + * x = y + * y = x + * + * This code isn't considered valid, so don't bother + * optimizing. + */ + if (processed.find(ins.get()) != processed.cend()) + return false; + + processed.insert(ins.get()); + + assert(std::find(m_instructions.begin(), m_instructions.end(), ins) != m_instructions.end()); + const LLVMVariablePtr &varPtr = m_variablePtrs.at(ins->workVariable); + auto scope = ins->loopScope; + + // If we aren't in a loop, we're safe + if (!scope) + return true; + + // If the loop scope contains a suspend and this is a non-warp script, the type may change between suspend and resume + if (scope->containsYield && !m_warp) + return false; + + std::shared_ptr write; + + // Find this instruction + auto it = std::find(m_variableInstructions.begin(), m_variableInstructions.end(), ins); + assert(it != m_variableInstructions.end()); + + // Find previous write instruction in this, parent or child loop scope + size_t index = it - m_variableInstructions.begin(); + + if (index > 0) { + bool found = false; + + do { + index--; + write = m_variableInstructions[index]; + found = (write->loopScope && write->type == LLVMInstruction::Type::WriteVariable && write->workVariable == ins->workVariable); + } while (index > 0 && !found); + + if (found) { + // Check if the write operation is in this or child scope + auto parentScope = write->loopScope; + + while (parentScope && parentScope != scope) + parentScope = parentScope->parentScope; + + if (!parentScope) { + // Check if the write operation is in any of the parent scopes + parentScope = scope; + + do { + parentScope = parentScope->parentScope; + } while (parentScope && parentScope != write->loopScope); + } + + // If there was a write operation before this instruction (in this, parent or child scope), check it + if (parentScope) { + if (parentScope == scope) + return isVariableWriteResultTypeSafe(write, expectedType, true, processed); + else + return isVariableTypeSafe(write, expectedType, processed); + } + } + } + + // Get last write operation + write = nullptr; + + // Find root loop scope + auto checkScope = scope; + + while (checkScope->parentScope) { + checkScope = checkScope->parentScope; + } + + // Find last loop scope (may be a parent or child scope) + while (checkScope) { + auto it = varPtr.loopVariableWrites.find(checkScope); + + if (it != varPtr.loopVariableWrites.cend()) { + assert(!it->second.empty()); + write = it->second.back(); + } + + if (checkScope->childScopes.empty()) + checkScope = nullptr; + else + checkScope = checkScope->childScopes.back(); + } + + // If there aren't any write operations, we're safe + if (!write) + return true; + + bool safe = true; + + if (ins->type == LLVMInstruction::Type::WriteVariable) + safe = isVariableWriteResultTypeSafe(ins, expectedType, false, processed); + + if (safe) + return isVariableWriteResultTypeSafe(write, expectedType, false, processed); + else + return false; +} + +bool LLVMCodeBuilder::isVariableWriteResultTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set &processed) + const +{ + const LLVMVariablePtr &varPtr = m_variablePtrs.at(ins->workVariable); + + // If the write operation writes the value of another variable, recursively check its type safety + // TODO: Check get list item instruction + auto argIns = ins->args[0].second->instruction; + + if (argIns && argIns->type == LLVMInstruction::Type::ReadVariable) + return isVariableTypeSafe(argIns, expectedType, processed); + + // Check written type + return optimizeRegisterType(ins->args[0].second) == expectedType && (varPtr.type == expectedType || ignoreSavedType); +} + LLVMRegister *LLVMCodeBuilder::createOp(LLVMInstruction::Type type, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args) { return createOp({ type, currentLoopScope() }, retType, argType, args); diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.h b/src/dev/engine/internal/llvm/llvmcodebuilder.h index 4a34ffd2..79977e29 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.h @@ -137,7 +137,7 @@ class LLVMCodeBuilder : public ICodeBuilder llvm::Value *castValue(LLVMRegister *reg, Compiler::StaticType targetType); llvm::Value *castRawValue(LLVMRegister *reg, Compiler::StaticType targetType); llvm::Constant *castConstValue(const Value &value, Compiler::StaticType targetType); - Compiler::StaticType optimizeRegisterType(LLVMRegister *reg); + Compiler::StaticType optimizeRegisterType(LLVMRegister *reg) const; llvm::Type *getType(Compiler::StaticType type); Compiler::StaticType getProcedureArgType(BlockPrototype::ArgType type); llvm::Value *isNaN(llvm::Value *num); @@ -149,6 +149,9 @@ class LLVMCodeBuilder : public ICodeBuilder void reloadVariables(llvm::Value *targetVariables); void reloadLists(); void updateListDataPtr(const LLVMListPtr &listPtr); + bool isVariableTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType) const; + bool isVariableTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, std::unordered_set &processed) const; + bool isVariableWriteResultTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set &processed) const; LLVMRegister *createOp(LLVMInstruction::Type type, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args); LLVMRegister *createOp(LLVMInstruction::Type type, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes = {}, const Compiler::Args &args = {}); diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 723c8101..c0a8ece8 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -2832,6 +2832,17 @@ TEST_F(LLVMCodeBuilderTest, VariablesAfterSuspend) v = m_builder->addVariableValue(localVar.get()); m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue(12.5); + m_builder->createVariableWrite(globalVar.get(), v); + } + m_builder->endLoop(); + + v = m_builder->addVariableValue(globalVar.get()); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + std::string expected = "hello world\n" "-4.8\n"; @@ -2851,6 +2862,20 @@ TEST_F(LLVMCodeBuilderTest, VariablesAfterSuspend) testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); + ASSERT_FALSE(code->isFinished(ctx.get())); + + globalVar->setValue("test"); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), ""); + ASSERT_FALSE(code->isFinished(ctx.get())); + + globalVar->setValue(true); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), "true\n"); } TEST_F(LLVMCodeBuilderTest, ListsAfterSuspend) @@ -3893,6 +3918,653 @@ TEST_F(LLVMCodeBuilderTest, LoopVariables) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis1) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalVar = std::make_shared("", ""); + stage.addVariable(globalVar); + + auto localVar = std::make_shared("", ""); + sprite.addVariable(localVar); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(5.25); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a string is assigned later + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addConstValue("test"); + m_builder->createVariableWrite(localVar.get(), v); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis2) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalVar = std::make_shared("", ""); + stage.addVariable(globalVar); + + auto localVar = std::make_shared("", ""); + sprite.addVariable(localVar); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(5.25); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is known here because a number is assigned later + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addConstValue(12.5); + m_builder->createVariableWrite(localVar.get(), v); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "12.5\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis3) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalVar = std::make_shared("", ""); + stage.addVariable(globalVar); + + auto localVar = std::make_shared("", ""); + sprite.addVariable(localVar); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(0); + m_builder->createVariableWrite(globalVar.get(), v); + + v = m_builder->addConstValue(5.25); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a variable with unknown type is assigned (the variable has unknown type because a string is assigned later) + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addVariableValue(globalVar.get()); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue("test"); + m_builder->createVariableWrite(globalVar.get(), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis4) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalVar = std::make_shared("", ""); + stage.addVariable(globalVar); + + auto localVar = std::make_shared("", ""); + sprite.addVariable(localVar); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(0); + m_builder->createVariableWrite(globalVar.get(), v); + + v = m_builder->addConstValue(5.25); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is known here because a variable is assigned which has a number assigned later + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addVariableValue(globalVar.get()); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(12.5); + m_builder->createVariableWrite(globalVar.get(), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "12.5\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis5) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalVar = std::make_shared("", ""); + stage.addVariable(globalVar); + + auto localVar = std::make_shared("", ""); + sprite.addVariable(localVar); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue("test"); + m_builder->createVariableWrite(globalVar.get(), v); + + v = m_builder->addConstValue(5.25); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(3); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a string variable is assigned later which has a number assigned as well + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addVariableValue(globalVar.get()); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(12.5); + m_builder->createVariableWrite(globalVar.get(), v); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n" + "12.5\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis6) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalVar = std::make_shared("", ""); + stage.addVariable(globalVar); + + auto localVar = std::make_shared("", ""); + sprite.addVariable(localVar); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(5.25); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is known here because a variable with known type is assigned later (even though the variable has a string assigned later, there's a number assigned before the read operation) + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue(10); + m_builder->createVariableWrite(globalVar.get(), v); + + v = m_builder->addVariableValue(globalVar.get()); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue("test"); + m_builder->createVariableWrite(globalVar.get(), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "10\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis7) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalVar = std::make_shared("", ""); + stage.addVariable(globalVar); + + auto localVar = std::make_shared("", ""); + sprite.addVariable(localVar); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(5.25); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a variable with a known, but different type is assigned later + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue("test"); + m_builder->createVariableWrite(globalVar.get(), v); + + v = m_builder->addVariableValue(globalVar.get()); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(10); + m_builder->createVariableWrite(globalVar.get(), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis8) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalVar = std::make_shared("", ""); + stage.addVariable(globalVar); + + auto localVar = std::make_shared("", ""); + sprite.addVariable(localVar); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(5.25); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a variable with a known, but different type is assigned later + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue("test"); + m_builder->createVariableWrite(globalVar.get(), v); + + v = m_builder->addVariableValue(globalVar.get()); + m_builder->createVariableWrite(localVar.get(), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis9) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalVar = std::make_shared("", "", "123"); + stage.addVariable(globalVar); + + auto localVar = std::make_shared("", ""); + sprite.addVariable(localVar); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(5.25); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addVariableValue(globalVar.get()); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(5); + m_builder->createVariableWrite(globalVar.get(), v); + } + m_builder->endLoop(); + + // Type is unknown here because a variable of unknown type is assigned before the read operation + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue(10); + m_builder->createVariableWrite(globalVar.get(), v); + + v = m_builder->addVariableValue(globalVar.get()); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue("test"); + m_builder->createVariableWrite(globalVar.get(), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "5\n" + "5\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis10) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalVar = std::make_shared("", ""); + stage.addVariable(globalVar); + + auto localVar = std::make_shared("", "", "123"); + sprite.addVariable(localVar); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue("test"); + m_builder->createVariableWrite(globalVar.get(), v); + + v = m_builder->addConstValue(3); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a variable of unknown type is assigned later + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addVariableValue(globalVar.get()); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(12.5); + m_builder->createVariableWrite(globalVar.get(), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "123\n" + "0\n" + "12.5\n" + "12.5\n" + "0\n" + "12.5\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis11) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalVar = std::make_shared("", ""); + stage.addVariable(globalVar); + + auto localVar = std::make_shared("", "", "123"); + sprite.addVariable(localVar); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue("test"); + m_builder->createVariableWrite(globalVar.get(), v); + + v = m_builder->addConstValue(3); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because the variable is assigned to itself later + // This case is not checked because the code isn't considered valid + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addVariableValue(localVar.get()); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addConstValue(12.5); + m_builder->createVariableWrite(globalVar.get(), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "123\n" + "123\n" + "123\n" + "123\n" + "123\n" + "123\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis12) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalVar = std::make_shared("", ""); + stage.addVariable(globalVar); + + auto localVar = std::make_shared("", "", "123"); + sprite.addVariable(localVar); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue("test"); + m_builder->createVariableWrite(globalVar.get(), v); + + v = m_builder->addConstValue(3); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because another variable is assigned later, but it has this variable assigned + // This case is not checked because the code isn't considered valid + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addVariableValue(globalVar.get()); + m_builder->createVariableWrite(localVar.get(), v); + + v = m_builder->addVariableValue(localVar.get()); + m_builder->createVariableWrite(globalVar.get(), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "123\n" + "0\n" + "0\n" + "0\n" + "0\n" + "0\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + TEST_F(LLVMCodeBuilderTest, LoopLists) { Stage stage; From 30c7ad9295e229f5d3af0a7dc2b0b9c4a8e07a77 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:35:37 +0100 Subject: [PATCH 09/21] LLVMCodeBuilder: Store loop condition info in instructions --- .../engine/internal/llvm/llvmcodebuilder.cpp | 66 +++++++++++-------- .../engine/internal/llvm/llvmcodebuilder.h | 1 + .../engine/internal/llvm/llvminstruction.h | 6 +- 3 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index d674bc44..d3a82b90 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -1227,7 +1227,7 @@ CompilerValue *LLVMCodeBuilder::addFunctionCall(const std::string &functionName, { assert(argTypes.size() == args.size()); - auto ins = std::make_shared(LLVMInstruction::Type::FunctionCall, currentLoopScope()); + auto ins = std::make_shared(LLVMInstruction::Type::FunctionCall, currentLoopScope(), m_loopCondition); ins->functionName = functionName; for (size_t i = 0; i < args.size(); i++) @@ -1278,7 +1278,7 @@ CompilerValue *LLVMCodeBuilder::addLocalVariableValue(CompilerLocalVariable *var CompilerValue *LLVMCodeBuilder::addVariableValue(Variable *variable) { - auto ins = std::make_shared(LLVMInstruction::Type::ReadVariable, currentLoopScope()); + auto ins = std::make_shared(LLVMInstruction::Type::ReadVariable, currentLoopScope(), m_loopCondition); ins->workVariable = variable; if (m_variablePtrs.find(variable) == m_variablePtrs.cend()) @@ -1295,7 +1295,7 @@ CompilerValue *LLVMCodeBuilder::addVariableValue(Variable *variable) CompilerValue *LLVMCodeBuilder::addListContents(List *list) { - LLVMInstruction ins(LLVMInstruction::Type::GetListContents, currentLoopScope()); + LLVMInstruction ins(LLVMInstruction::Type::GetListContents, currentLoopScope(), m_loopCondition); ins.workList = list; if (m_listPtrs.find(list) == m_listPtrs.cend()) @@ -1306,7 +1306,7 @@ CompilerValue *LLVMCodeBuilder::addListContents(List *list) CompilerValue *LLVMCodeBuilder::addListItem(List *list, CompilerValue *index) { - auto ins = std::make_shared(LLVMInstruction::Type::GetListItem, currentLoopScope()); + auto ins = std::make_shared(LLVMInstruction::Type::GetListItem, currentLoopScope(), m_loopCondition); ins->workList = list; if (m_listPtrs.find(list) == m_listPtrs.cend()) @@ -1324,7 +1324,7 @@ CompilerValue *LLVMCodeBuilder::addListItem(List *list, CompilerValue *index) CompilerValue *LLVMCodeBuilder::addListItemIndex(List *list, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::GetListItemIndex, currentLoopScope()); + LLVMInstruction ins(LLVMInstruction::Type::GetListItemIndex, currentLoopScope(), m_loopCondition); ins.workList = list; if (m_listPtrs.find(list) == m_listPtrs.cend()) @@ -1335,7 +1335,7 @@ CompilerValue *LLVMCodeBuilder::addListItemIndex(List *list, CompilerValue *item CompilerValue *LLVMCodeBuilder::addListContains(List *list, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::ListContainsItem, currentLoopScope()); + LLVMInstruction ins(LLVMInstruction::Type::ListContainsItem, currentLoopScope(), m_loopCondition); ins.workList = list; if (m_listPtrs.find(list) == m_listPtrs.cend()) @@ -1346,7 +1346,7 @@ CompilerValue *LLVMCodeBuilder::addListContains(List *list, CompilerValue *item) CompilerValue *LLVMCodeBuilder::addListSize(List *list) { - LLVMInstruction ins(LLVMInstruction::Type::GetListSize, currentLoopScope()); + LLVMInstruction ins(LLVMInstruction::Type::GetListSize, currentLoopScope(), m_loopCondition); ins.workList = list; if (m_listPtrs.find(list) == m_listPtrs.cend()) @@ -1370,7 +1370,7 @@ CompilerValue *LLVMCodeBuilder::addProcedureArgument(const std::string &name) const auto index = it - argNames.begin(); const Compiler::StaticType type = getProcedureArgType(m_procedurePrototype->argumentTypes()[index]); - auto ins = std::make_shared(LLVMInstruction::Type::ProcedureArg, currentLoopScope()); + auto ins = std::make_shared(LLVMInstruction::Type::ProcedureArg, currentLoopScope(), m_loopCondition); auto ret = std::make_shared(type); ret->isRawValue = (type != Compiler::StaticType::Unknown); ins->functionReturnReg = ret.get(); @@ -1550,7 +1550,7 @@ void LLVMCodeBuilder::createLocalVariableWrite(CompilerLocalVariable *variable, void LLVMCodeBuilder::createVariableWrite(Variable *variable, CompilerValue *value) { - LLVMInstruction ins(LLVMInstruction::Type::WriteVariable, currentLoopScope()); + LLVMInstruction ins(LLVMInstruction::Type::WriteVariable, currentLoopScope(), m_loopCondition); ins.workVariable = variable; createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Unknown, { value }); @@ -1567,7 +1567,7 @@ void LLVMCodeBuilder::createVariableWrite(Variable *variable, CompilerValue *val void LLVMCodeBuilder::createListClear(List *list) { - LLVMInstruction ins(LLVMInstruction::Type::ClearList, currentLoopScope()); + LLVMInstruction ins(LLVMInstruction::Type::ClearList, currentLoopScope(), m_loopCondition); ins.workList = list; createOp(ins, Compiler::StaticType::Void); @@ -1577,7 +1577,7 @@ void LLVMCodeBuilder::createListClear(List *list) void LLVMCodeBuilder::createListRemove(List *list, CompilerValue *index) { - LLVMInstruction ins(LLVMInstruction::Type::RemoveListItem, currentLoopScope()); + LLVMInstruction ins(LLVMInstruction::Type::RemoveListItem, currentLoopScope(), m_loopCondition); ins.workList = list; createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Number, { index }); @@ -1587,7 +1587,7 @@ void LLVMCodeBuilder::createListRemove(List *list, CompilerValue *index) void LLVMCodeBuilder::createListAppend(List *list, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::AppendToList, currentLoopScope()); + LLVMInstruction ins(LLVMInstruction::Type::AppendToList, currentLoopScope(), m_loopCondition); ins.workList = list; createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Unknown, { item }); @@ -1597,7 +1597,7 @@ void LLVMCodeBuilder::createListAppend(List *list, CompilerValue *item) void LLVMCodeBuilder::createListInsert(List *list, CompilerValue *index, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::InsertToList, currentLoopScope()); + LLVMInstruction ins(LLVMInstruction::Type::InsertToList, currentLoopScope(), m_loopCondition); ins.workList = list; createOp(ins, Compiler::StaticType::Void, { Compiler::StaticType::Number, Compiler::StaticType::Unknown }, { index, item }); @@ -1607,7 +1607,7 @@ void LLVMCodeBuilder::createListInsert(List *list, CompilerValue *index, Compile void LLVMCodeBuilder::createListReplace(List *list, CompilerValue *index, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::ListReplace, currentLoopScope()); + LLVMInstruction ins(LLVMInstruction::Type::ListReplace, currentLoopScope(), m_loopCondition); ins.workList = list; createOp(ins, Compiler::StaticType::Void, { Compiler::StaticType::Number, Compiler::StaticType::Unknown }, { index, item }); @@ -1617,24 +1617,26 @@ void LLVMCodeBuilder::createListReplace(List *list, CompilerValue *index, Compil void LLVMCodeBuilder::beginIfStatement(CompilerValue *cond) { - auto ins = std::make_shared(LLVMInstruction::Type::BeginIf, currentLoopScope()); + auto ins = std::make_shared(LLVMInstruction::Type::BeginIf, currentLoopScope(), m_loopCondition); ins->args.push_back({ Compiler::StaticType::Bool, static_cast(cond) }); m_instructions.push_back(ins); } void LLVMCodeBuilder::beginElseBranch() { - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::BeginElse, currentLoopScope())); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::BeginElse, currentLoopScope(), m_loopCondition)); } void LLVMCodeBuilder::endIf() { - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::EndIf, currentLoopScope())); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::EndIf, currentLoopScope(), m_loopCondition)); } void LLVMCodeBuilder::beginRepeatLoop(CompilerValue *count) { - auto ins = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, currentLoopScope()); + assert(!m_loopCondition); + + auto ins = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, currentLoopScope(), m_loopCondition); ins->args.push_back({ Compiler::StaticType::Number, static_cast(count) }); m_instructions.push_back(ins); pushLoopScope(false); @@ -1642,7 +1644,10 @@ void LLVMCodeBuilder::beginRepeatLoop(CompilerValue *count) void LLVMCodeBuilder::beginWhileLoop(CompilerValue *cond) { - auto ins = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, currentLoopScope()); + assert(m_loopCondition); + m_loopCondition = false; + + auto ins = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, currentLoopScope(), m_loopCondition); ins->args.push_back({ Compiler::StaticType::Bool, static_cast(cond) }); m_instructions.push_back(ins); pushLoopScope(false); @@ -1650,7 +1655,10 @@ void LLVMCodeBuilder::beginWhileLoop(CompilerValue *cond) void LLVMCodeBuilder::beginRepeatUntilLoop(CompilerValue *cond) { - auto ins = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, currentLoopScope()); + assert(m_loopCondition); + m_loopCondition = false; + + auto ins = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, currentLoopScope(), m_loopCondition); ins->args.push_back({ Compiler::StaticType::Bool, static_cast(cond) }); m_instructions.push_back(ins); pushLoopScope(false); @@ -1658,21 +1666,23 @@ void LLVMCodeBuilder::beginRepeatUntilLoop(CompilerValue *cond) void LLVMCodeBuilder::beginLoopCondition() { - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::BeginLoopCondition, currentLoopScope())); + assert(!m_loopCondition); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::BeginLoopCondition, currentLoopScope(), m_loopCondition)); + m_loopCondition = true; } void LLVMCodeBuilder::endLoop() { if (!m_warp) - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::Yield, currentLoopScope())); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::Yield, currentLoopScope(), m_loopCondition)); - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::EndLoop, currentLoopScope())); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::EndLoop, currentLoopScope(), m_loopCondition)); popLoopScope(); } void LLVMCodeBuilder::yield() { - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::Yield, currentLoopScope())); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::Yield, currentLoopScope(), m_loopCondition)); if (m_loopScope >= 0) m_loopScopes[m_loopScope]->containsYield = true; @@ -1680,7 +1690,7 @@ void LLVMCodeBuilder::yield() void LLVMCodeBuilder::createStop() { - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::Stop, currentLoopScope())); + m_instructions.push_back(std::make_shared(LLVMInstruction::Type::Stop, currentLoopScope(), m_loopCondition)); } void LLVMCodeBuilder::createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args) @@ -1693,7 +1703,7 @@ void LLVMCodeBuilder::createProcedureCall(BlockPrototype *prototype, const Compi for (BlockPrototype::ArgType type : procedureArgs) types.push_back(getProcedureArgType(type)); - LLVMInstruction ins(LLVMInstruction::Type::CallProcedure, currentLoopScope()); + LLVMInstruction ins(LLVMInstruction::Type::CallProcedure, currentLoopScope(), m_loopCondition); ins.procedurePrototype = prototype; createOp(ins, Compiler::StaticType::Void, types, args); } @@ -2384,12 +2394,12 @@ bool LLVMCodeBuilder::isVariableWriteResultTypeSafe(std::shared_ptr> m_loopScopes; long m_loopScopeCounter = 0; // replacement for m_loopScopes size in build phase std::vector m_loopScopeTree; + bool m_loopCondition = false; // whether we're currently compiling a loop condition std::vector> m_variableInstructions; std::vector> m_heap; // scopes diff --git a/src/dev/engine/internal/llvm/llvminstruction.h b/src/dev/engine/internal/llvm/llvminstruction.h index 4f1787f5..8e88e887 100644 --- a/src/dev/engine/internal/llvm/llvminstruction.h +++ b/src/dev/engine/internal/llvm/llvminstruction.h @@ -78,9 +78,10 @@ struct LLVMInstruction ProcedureArg }; - LLVMInstruction(Type type, std::shared_ptr loopScope) : + LLVMInstruction(Type type, std::shared_ptr loopScope, bool loopCondition) : type(type), - loopScope(loopScope) + loopScope(loopScope), + loopCondition(loopCondition) { } @@ -95,6 +96,7 @@ struct LLVMInstruction BlockPrototype *procedurePrototype = nullptr; size_t procedureArgIndex = 0; std::shared_ptr loopScope; + bool loopCondition = false; // whether the instruction is part of a loop condition }; } // namespace libscratchcpp From 3bc6370c125b60c146afd1fde5c5450828cf1bdf Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:36:24 +0100 Subject: [PATCH 10/21] LLVMCodeBuilder: Force variable heap pointer when in non-warp loop cond --- .../engine/internal/llvm/llvmcodebuilder.cpp | 2 +- test/dev/llvm/llvmcodebuilder_test.cpp | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index d3a82b90..a0166674 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -670,7 +670,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() if (!isVariableTypeSafe(insPtr, varPtr.type)) varPtr.type = Compiler::StaticType::Unknown; - step.functionReturnReg->value = varPtr.onStack ? varPtr.stackPtr : varPtr.heapPtr; + step.functionReturnReg->value = varPtr.onStack && !(step.loopCondition && !m_warp) ? varPtr.stackPtr : varPtr.heapPtr; step.functionReturnReg->setType(varPtr.type); break; } diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index c0a8ece8..2789deec 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -2843,6 +2843,28 @@ TEST_F(LLVMCodeBuilderTest, VariablesAfterSuspend) v = m_builder->addVariableValue(globalVar.get()); m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = m_builder->addConstValue(0); + m_builder->createVariableWrite(localVar.get(), v); + + m_builder->beginLoopCondition(); + v = m_builder->createCmpLT(m_builder->addVariableValue(localVar.get()), m_builder->addConstValue(3)); + m_builder->beginWhileLoop(v); + m_builder->endLoop(); + + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + v = m_builder->addConstValue(0); + m_builder->createVariableWrite(localVar.get(), v); + + m_builder->beginLoopCondition(); + v = m_builder->createCmpEQ(m_builder->addVariableValue(localVar.get()), m_builder->addConstValue(2)); + m_builder->beginRepeatUntilLoop(v); + m_builder->endLoop(); + + v = m_builder->addVariableValue(localVar.get()); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + std::string expected = "hello world\n" "-4.8\n"; @@ -2876,6 +2898,41 @@ TEST_F(LLVMCodeBuilderTest, VariablesAfterSuspend) testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), "true\n"); + ASSERT_FALSE(code->isFinished(ctx.get())); + + localVar->setValue(1); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), ""); + ASSERT_FALSE(code->isFinished(ctx.get())); + + localVar->setValue("2"); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), ""); + ASSERT_FALSE(code->isFinished(ctx.get())); + + localVar->setValue(3); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), "3\n"); + ASSERT_FALSE(code->isFinished(ctx.get())); + + localVar->setValue(1); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), ""); + ASSERT_FALSE(code->isFinished(ctx.get())); + + localVar->setValue(2); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), "2\n"); } TEST_F(LLVMCodeBuilderTest, ListsAfterSuspend) From ad04bcef40821f80dcaf90633a1c5eb39b1b52a5 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 18 Jan 2025 11:24:47 +0100 Subject: [PATCH 11/21] LLVMCodeBuilder: Store list read/write instructions --- src/dev/engine/internal/llvm/llvmcodebuilder.cpp | 15 +++++++++++++-- src/dev/engine/internal/llvm/llvmcodebuilder.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index a0166674..a8e9ec6f 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -1319,6 +1319,7 @@ CompilerValue *LLVMCodeBuilder::addListItem(List *list, CompilerValue *index) ins->functionReturnReg = ret.get(); m_instructions.push_back(ins); + m_listInstructions.push_back(m_instructions.back()); return addReg(ret, ins); } @@ -1330,7 +1331,9 @@ CompilerValue *LLVMCodeBuilder::addListItemIndex(List *list, CompilerValue *item if (m_listPtrs.find(list) == m_listPtrs.cend()) m_listPtrs[list] = LLVMListPtr(); - return createOp(ins, Compiler::StaticType::Number, Compiler::StaticType::Unknown, { item }); + auto ret = createOp(ins, Compiler::StaticType::Number, Compiler::StaticType::Unknown, { item }); + m_listInstructions.push_back(m_instructions.back()); + return ret; } CompilerValue *LLVMCodeBuilder::addListContains(List *list, CompilerValue *item) @@ -1341,7 +1344,9 @@ CompilerValue *LLVMCodeBuilder::addListContains(List *list, CompilerValue *item) if (m_listPtrs.find(list) == m_listPtrs.cend()) m_listPtrs[list] = LLVMListPtr(); - return createOp(ins, Compiler::StaticType::Bool, Compiler::StaticType::Unknown, { item }); + auto ret = createOp(ins, Compiler::StaticType::Bool, Compiler::StaticType::Unknown, { item }); + m_listInstructions.push_back(m_instructions.back()); + return ret; } CompilerValue *LLVMCodeBuilder::addListSize(List *list) @@ -1593,6 +1598,8 @@ void LLVMCodeBuilder::createListAppend(List *list, CompilerValue *item) if (m_listPtrs.find(list) == m_listPtrs.cend()) m_listPtrs[list] = LLVMListPtr(); + + m_listInstructions.push_back(m_instructions.back()); } void LLVMCodeBuilder::createListInsert(List *list, CompilerValue *index, CompilerValue *item) @@ -1603,6 +1610,8 @@ void LLVMCodeBuilder::createListInsert(List *list, CompilerValue *index, Compile if (m_listPtrs.find(list) == m_listPtrs.cend()) m_listPtrs[list] = LLVMListPtr(); + + m_listInstructions.push_back(m_instructions.back()); } void LLVMCodeBuilder::createListReplace(List *list, CompilerValue *index, CompilerValue *item) @@ -1613,6 +1622,8 @@ void LLVMCodeBuilder::createListReplace(List *list, CompilerValue *index, Compil if (m_listPtrs.find(list) == m_listPtrs.cend()) m_listPtrs[list] = LLVMListPtr(); + + m_listInstructions.push_back(m_instructions.back()); } void LLVMCodeBuilder::beginIfStatement(CompilerValue *cond) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.h b/src/dev/engine/internal/llvm/llvmcodebuilder.h index 7a657d20..d8d7b6f9 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.h @@ -238,6 +238,7 @@ class LLVMCodeBuilder : public ICodeBuilder std::vector m_loopScopeTree; bool m_loopCondition = false; // whether we're currently compiling a loop condition std::vector> m_variableInstructions; + std::vector> m_listInstructions; std::vector> m_heap; // scopes std::shared_ptr m_output; From 2ba3e126d1ddef3f28902898c2e013e8d831bb7b Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 18 Jan 2025 11:33:23 +0100 Subject: [PATCH 12/21] LLVMCodeBuilder: Store loop list write instructions --- src/dev/engine/internal/llvm/llvmcodebuilder.cpp | 15 +++++++++++++++ src/dev/engine/internal/llvm/llvmlistptr.h | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index a8e9ec6f..a0bc7d1e 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -1599,6 +1599,11 @@ void LLVMCodeBuilder::createListAppend(List *list, CompilerValue *item) if (m_listPtrs.find(list) == m_listPtrs.cend()) m_listPtrs[list] = LLVMListPtr(); + if (m_loopScope >= 0) { + auto scope = m_loopScopes[m_loopScope]; + m_listPtrs[list].loopListWrites[scope].push_back(m_instructions.back()); + } + m_listInstructions.push_back(m_instructions.back()); } @@ -1611,6 +1616,11 @@ void LLVMCodeBuilder::createListInsert(List *list, CompilerValue *index, Compile if (m_listPtrs.find(list) == m_listPtrs.cend()) m_listPtrs[list] = LLVMListPtr(); + if (m_loopScope >= 0) { + auto scope = m_loopScopes[m_loopScope]; + m_listPtrs[list].loopListWrites[scope].push_back(m_instructions.back()); + } + m_listInstructions.push_back(m_instructions.back()); } @@ -1623,6 +1633,11 @@ void LLVMCodeBuilder::createListReplace(List *list, CompilerValue *index, Compil if (m_listPtrs.find(list) == m_listPtrs.cend()) m_listPtrs[list] = LLVMListPtr(); + if (m_loopScope >= 0) { + auto scope = m_loopScopes[m_loopScope]; + m_listPtrs[list].loopListWrites[scope].push_back(m_instructions.back()); + } + m_listInstructions.push_back(m_instructions.back()); } diff --git a/src/dev/engine/internal/llvm/llvmlistptr.h b/src/dev/engine/internal/llvm/llvmlistptr.h index 6b427572..ca97bc0e 100644 --- a/src/dev/engine/internal/llvm/llvmlistptr.h +++ b/src/dev/engine/internal/llvm/llvmlistptr.h @@ -3,6 +3,7 @@ #pragma once #include +#include namespace llvm { @@ -14,6 +15,9 @@ class Value; namespace libscratchcpp { +class LLVMLoopScope; +class LLVMInstruction; + struct LLVMListPtr { llvm::Value *ptr = nullptr; @@ -22,6 +26,9 @@ struct LLVMListPtr llvm::Value *allocatedSizePtr = nullptr; llvm::Value *dataPtrDirty = nullptr; Compiler::StaticType type = Compiler::StaticType::Unknown; + + // Used in build phase to check the type safety of lists in loops + std::unordered_map, std::vector>> loopListWrites; // loop scope, write instructions }; } // namespace libscratchcpp From 1ee060a64bfcb0fe327a221b18bf72c8789bad17 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 18 Jan 2025 12:48:08 +0100 Subject: [PATCH 13/21] LLVMCodeBuilder: Implement loop type analysis for lists --- .../engine/internal/llvm/llvmcodebuilder.cpp | 116 ++- .../engine/internal/llvm/llvmcodebuilder.h | 6 +- test/dev/llvm/llvmcodebuilder_test.cpp | 825 ++++++++++++++++++ 3 files changed, 907 insertions(+), 40 deletions(-) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index a0bc7d1e..c19e4bcb 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -23,6 +23,9 @@ using namespace libscratchcpp; static std::unordered_map TYPE_MAP = { { ValueType::Number, Compiler::StaticType::Number }, { ValueType::Bool, Compiler::StaticType::Bool }, { ValueType::String, Compiler::StaticType::String } }; +static const std::unordered_set + VAR_LIST_READ_INSTRUCTIONS = { LLVMInstruction::Type::ReadVariable, LLVMInstruction::Type::GetListItem, LLVMInstruction::Type::GetListItemIndex, LLVMInstruction::Type::ListContainsItem }; + LLVMCodeBuilder::LLVMCodeBuilder(LLVMCompilerContext *ctx, BlockPrototype *procedurePrototype) : m_ctx(ctx), m_target(ctx->target()), @@ -632,7 +635,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() LLVMVariablePtr &varPtr = m_variablePtrs[step.workVariable]; varPtr.changed = true; - const bool safe = isVariableTypeSafe(insPtr, varPtr.type); + const bool safe = isVarOrListTypeSafe(insPtr, varPtr.type); // Initialize stack variable on first assignment if (!varPtr.onStack) { @@ -667,7 +670,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() assert(step.args.size() == 0); LLVMVariablePtr &varPtr = m_variablePtrs[step.workVariable]; - if (!isVariableTypeSafe(insPtr, varPtr.type)) + if (!isVarOrListTypeSafe(insPtr, varPtr.type)) varPtr.type = Compiler::StaticType::Unknown; step.functionReturnReg->value = varPtr.onStack && !(step.loopCondition && !m_warp) ? varPtr.stackPtr : varPtr.heapPtr; @@ -693,7 +696,10 @@ std::shared_ptr LLVMCodeBuilder::finalize() case LLVMInstruction::Type::RemoveListItem: { assert(step.args.size() == 1); const auto &arg = step.args[0]; - const LLVMListPtr &listPtr = m_listPtrs[step.workList]; + LLVMListPtr &listPtr = m_listPtrs[step.workList]; + + if (!isVarOrListTypeSafe(insPtr, listPtr.type)) + listPtr.type = Compiler::StaticType::Unknown; // Range check llvm::Value *min = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); @@ -722,6 +728,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() Compiler::StaticType type = optimizeRegisterType(arg.second); LLVMListPtr &listPtr = m_listPtrs[step.workList]; + const bool safe = isVarOrListTypeSafe(insPtr, listPtr.type); auto &typeMap = m_scopeLists.back(); if (typeMap.find(&listPtr) == typeMap.cend()) { @@ -732,6 +739,9 @@ std::shared_ptr LLVMCodeBuilder::finalize() typeMap[&listPtr] = listPtr.type; } + if (!safe) + listPtr.type = Compiler::StaticType::Unknown; + // Check if enough space is allocated llvm::Value *allocatedSize = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.allocatedSizePtr); llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); @@ -767,6 +777,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() Compiler::StaticType type = optimizeRegisterType(valueArg.second); LLVMListPtr &listPtr = m_listPtrs[step.workList]; + const bool safe = isVarOrListTypeSafe(insPtr, listPtr.type); auto &typeMap = m_scopeLists.back(); if (typeMap.find(&listPtr) == typeMap.cend()) { @@ -777,6 +788,9 @@ std::shared_ptr LLVMCodeBuilder::finalize() typeMap[&listPtr] = listPtr.type; } + if (!safe) + listPtr.type = Compiler::StaticType::Unknown; + llvm::Value *oldAllocatedSize = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.allocatedSizePtr); // Range check @@ -813,6 +827,9 @@ std::shared_ptr LLVMCodeBuilder::finalize() Compiler::StaticType type = optimizeRegisterType(valueArg.second); LLVMListPtr &listPtr = m_listPtrs[step.workList]; + if (!isVarOrListTypeSafe(insPtr, listPtr.type)) + listPtr.type = Compiler::StaticType::Unknown; + // Range check llvm::Value *min = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); @@ -856,7 +873,10 @@ std::shared_ptr LLVMCodeBuilder::finalize() case LLVMInstruction::Type::GetListItem: { assert(step.args.size() == 1); const auto &arg = step.args[0]; - const LLVMListPtr &listPtr = m_listPtrs[step.workList]; + LLVMListPtr &listPtr = m_listPtrs[step.workList]; + + if (!isVarOrListTypeSafe(insPtr, listPtr.type)) + listPtr.type = Compiler::StaticType::Unknown; llvm::Value *min = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); @@ -884,7 +904,11 @@ std::shared_ptr LLVMCodeBuilder::finalize() case LLVMInstruction::Type::GetListItemIndex: { assert(step.args.size() == 1); const auto &arg = step.args[0]; - const LLVMListPtr &listPtr = m_listPtrs[step.workList]; + LLVMListPtr &listPtr = m_listPtrs[step.workList]; + + if (!isVarOrListTypeSafe(insPtr, listPtr.type)) + listPtr.type = Compiler::StaticType::Unknown; + step.functionReturnReg->value = m_builder.CreateSIToFP(getListItemIndex(listPtr, arg.second), m_builder.getDoubleTy()); break; } @@ -892,7 +916,11 @@ std::shared_ptr LLVMCodeBuilder::finalize() case LLVMInstruction::Type::ListContainsItem: { assert(step.args.size() == 1); const auto &arg = step.args[0]; - const LLVMListPtr &listPtr = m_listPtrs[step.workList]; + LLVMListPtr &listPtr = m_listPtrs[step.workList]; + + if (!isVarOrListTypeSafe(insPtr, listPtr.type)) + listPtr.type = Compiler::StaticType::Unknown; + llvm::Value *index = getListItemIndex(listPtr, arg.second); step.functionReturnReg->value = m_builder.CreateICmpSGT(index, llvm::ConstantInt::get(m_builder.getInt64Ty(), -1, true)); break; @@ -2258,30 +2286,31 @@ void LLVMCodeBuilder::updateListDataPtr(const LLVMListPtr &listPtr) m_builder.CreateStore(m_builder.getInt1(false), listPtr.dataPtrDirty); } -bool LLVMCodeBuilder::isVariableTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType) const +bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType) const { std::unordered_set processed; - return isVariableTypeSafe(ins, expectedType, processed); + return isVarOrListTypeSafe(ins, expectedType, processed); } -bool LLVMCodeBuilder::isVariableTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, std::unordered_set &processed) const +bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, std::unordered_set &processed) const { /* * The main part of the loop type analyzer. * - * This is a recursive function which is called when variable read - * instruction is created. It checks the last write to the - * variable in one of the loop scopes. + * This is a recursive function which is called when variable + * or list instruction is created. It checks the last write to + * the variable or list in one of the loop scopes. * * If the last write operation writes a value with a different * type, it will return false, otherwise true. * - * If the last written value is from a variable, this function - * is called for it to check its type safety (that's why it is - * recursive). + * If the last written value is from a variable or list, this + * function is called for it to check its type safety (that's + * why it is recursive). * - * If the variable had a write operation before (in the same, - * parent or child loop scope), it is checked recursively. + * If the variable or list had a write operation before (in + * the same, parent or child loop scope), it is checked + * recursively. */ if (!ins) @@ -2307,7 +2336,9 @@ bool LLVMCodeBuilder::isVariableTypeSafe(std::shared_ptr ins, C processed.insert(ins.get()); assert(std::find(m_instructions.begin(), m_instructions.end(), ins) != m_instructions.end()); - const LLVMVariablePtr &varPtr = m_variablePtrs.at(ins->workVariable); + const LLVMVariablePtr *varPtr = ins->workVariable ? &m_variablePtrs.at(ins->workVariable) : nullptr; + const LLVMListPtr *listPtr = ins->workList ? &m_listPtrs.at(ins->workList) : nullptr; + assert((varPtr || listPtr) && !(varPtr && listPtr)); auto scope = ins->loopScope; // If we aren't in a loop, we're safe @@ -2319,21 +2350,23 @@ bool LLVMCodeBuilder::isVariableTypeSafe(std::shared_ptr ins, C return false; std::shared_ptr write; + const auto &instructions = varPtr ? m_variableInstructions : m_listInstructions; // Find this instruction - auto it = std::find(m_variableInstructions.begin(), m_variableInstructions.end(), ins); - assert(it != m_variableInstructions.end()); + auto it = std::find(instructions.begin(), instructions.end(), ins); + assert(it != instructions.end()); // Find previous write instruction in this, parent or child loop scope - size_t index = it - m_variableInstructions.begin(); + size_t index = it - instructions.begin(); if (index > 0) { bool found = false; do { index--; - write = m_variableInstructions[index]; - found = (write->loopScope && write->type == LLVMInstruction::Type::WriteVariable && write->workVariable == ins->workVariable); + write = instructions[index]; + const bool isWrite = (VAR_LIST_READ_INSTRUCTIONS.find(write->type) == VAR_LIST_READ_INSTRUCTIONS.cend()); + found = (write->loopScope && isWrite && write->workVariable == ins->workVariable); } while (index > 0 && !found); if (found) { @@ -2355,13 +2388,15 @@ bool LLVMCodeBuilder::isVariableTypeSafe(std::shared_ptr ins, C // If there was a write operation before this instruction (in this, parent or child scope), check it if (parentScope) { if (parentScope == scope) - return isVariableWriteResultTypeSafe(write, expectedType, true, processed); + return isVarOrListWriteResultTypeSafe(write, expectedType, true, processed); else - return isVariableTypeSafe(write, expectedType, processed); + return isVarOrListTypeSafe(write, expectedType, processed); } } } + const auto &loopWrites = varPtr ? varPtr->loopVariableWrites : listPtr->loopListWrites; + // Get last write operation write = nullptr; @@ -2374,9 +2409,9 @@ bool LLVMCodeBuilder::isVariableTypeSafe(std::shared_ptr ins, C // Find last loop scope (may be a parent or child scope) while (checkScope) { - auto it = varPtr.loopVariableWrites.find(checkScope); + auto it = loopWrites.find(checkScope); - if (it != varPtr.loopVariableWrites.cend()) { + if (it != loopWrites.cend()) { assert(!it->second.empty()); write = it->second.back(); } @@ -2393,29 +2428,36 @@ bool LLVMCodeBuilder::isVariableTypeSafe(std::shared_ptr ins, C bool safe = true; - if (ins->type == LLVMInstruction::Type::WriteVariable) - safe = isVariableWriteResultTypeSafe(ins, expectedType, false, processed); + if (VAR_LIST_READ_INSTRUCTIONS.find(ins->type) == VAR_LIST_READ_INSTRUCTIONS.cend()) // write + safe = isVarOrListWriteResultTypeSafe(ins, expectedType, false, processed); if (safe) - return isVariableWriteResultTypeSafe(write, expectedType, false, processed); + return isVarOrListWriteResultTypeSafe(write, expectedType, false, processed); else return false; } -bool LLVMCodeBuilder::isVariableWriteResultTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set &processed) +bool LLVMCodeBuilder::isVarOrListWriteResultTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set &processed) const { - const LLVMVariablePtr &varPtr = m_variablePtrs.at(ins->workVariable); + const LLVMVariablePtr *varPtr = ins->workVariable ? &m_variablePtrs.at(ins->workVariable) : nullptr; + const LLVMListPtr *listPtr = ins->workList ? &m_listPtrs.at(ins->workList) : nullptr; + assert((varPtr || listPtr) && !(varPtr && listPtr)); // If the write operation writes the value of another variable, recursively check its type safety - // TODO: Check get list item instruction - auto argIns = ins->args[0].second->instruction; + const auto arg = ins->args.back().second; // value is always the last argument + auto argIns = arg->instruction; - if (argIns && argIns->type == LLVMInstruction::Type::ReadVariable) - return isVariableTypeSafe(argIns, expectedType, processed); + if (argIns && (argIns->type == LLVMInstruction::Type::ReadVariable || argIns->type == LLVMInstruction::Type::GetListItem)) + return isVarOrListTypeSafe(argIns, expectedType, processed); // Check written type - return optimizeRegisterType(ins->args[0].second) == expectedType && (varPtr.type == expectedType || ignoreSavedType); + const bool typeMatches = (optimizeRegisterType(arg) == expectedType); + + if (varPtr) + return typeMatches && (varPtr->type == expectedType || ignoreSavedType); + else + return typeMatches && (listPtr->type == expectedType || ignoreSavedType); } LLVMRegister *LLVMCodeBuilder::createOp(LLVMInstruction::Type type, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.h b/src/dev/engine/internal/llvm/llvmcodebuilder.h index d8d7b6f9..22fa1609 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.h @@ -149,9 +149,9 @@ class LLVMCodeBuilder : public ICodeBuilder void reloadVariables(llvm::Value *targetVariables); void reloadLists(); void updateListDataPtr(const LLVMListPtr &listPtr); - bool isVariableTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType) const; - bool isVariableTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, std::unordered_set &processed) const; - bool isVariableWriteResultTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set &processed) const; + bool isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType) const; + bool isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, std::unordered_set &processed) const; + bool isVarOrListWriteResultTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set &processed) const; LLVMRegister *createOp(LLVMInstruction::Type type, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args); LLVMRegister *createOp(LLVMInstruction::Type type, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes = {}, const Compiler::Args &args = {}); diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 2789deec..e2ebe5d1 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -4815,6 +4815,831 @@ TEST_F(LLVMCodeBuilderTest, LoopLists) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis1) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalList = std::make_shared("", ""); + stage.addList(globalList); + + auto localList = std::make_shared("", ""); + sprite.addList(localList); + + createBuilder(&sprite, true); + + m_builder->createListClear(localList.get()); + + CompilerValue *v = m_builder->addConstValue(5.25); + m_builder->createListAppend(localList.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a string is added later + v = m_builder->createSub(m_builder->addListSize(localList.get()), m_builder->addConstValue(1)); + v = m_builder->addListItem(localList.get(), v); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addConstValue("test"); + m_builder->createListAppend(localList.get(), v); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n" + "1\n" + "0\n" + "0\n" + "1\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis2) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalList = std::make_shared("", ""); + stage.addList(globalList); + + auto localList = std::make_shared("", ""); + sprite.addList(localList); + + createBuilder(&sprite, true); + + m_builder->createListClear(localList.get()); + + CompilerValue *v = m_builder->addConstValue(5.25); + m_builder->createListAppend(localList.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is known here because a number is assigned later + v = m_builder->addConstValue(0); + v = m_builder->addListItem(localList.get(), v); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addConstValue(12.5); + m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n" + "1\n" + "12.5\n" + "-1\n" + "0\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis3) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalList = std::make_shared("", ""); + stage.addList(globalList); + + auto localList = std::make_shared("", ""); + sprite.addList(localList); + + createBuilder(&sprite, true); + + m_builder->createListClear(globalList.get()); + m_builder->createListClear(localList.get()); + + CompilerValue *v = m_builder->addConstValue(0); + m_builder->createListAppend(globalList.get(), v); + + v = m_builder->addConstValue(5.25); + m_builder->createListAppend(localList.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a list item with unknown type is inserted (the item has unknown type because a string is assigned later) + v = m_builder->addConstValue(0); + v = m_builder->addListItem(localList.get(), v); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); + m_builder->createListInsert(localList.get(), m_builder->addConstValue(0), v); + + v = m_builder->addConstValue("test"); + m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n" + "1\n" + "0\n" + "2\n" + "1\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis4) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalList = std::make_shared("", ""); + stage.addList(globalList); + + auto localList = std::make_shared("", ""); + sprite.addList(localList); + + createBuilder(&sprite, true); + + m_builder->createListClear(globalList.get()); + m_builder->createListClear(localList.get()); + + CompilerValue *v = m_builder->addConstValue(0); + m_builder->createListAppend(globalList.get(), v); + + v = m_builder->addConstValue(5.25); + m_builder->createListAppend(localList.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is known here because a number list item is added later + v = m_builder->createSub(m_builder->addListSize(localList.get()), m_builder->addConstValue(1)); + v = m_builder->addListItem(localList.get(), v); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); + m_builder->createListAppend(localList.get(), v); + + v = m_builder->addConstValue(12.5); + m_builder->createListInsert(globalList.get(), m_builder->addConstValue(0), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n" + "1\n" + "12.5\n" + "0\n" + "1\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis5) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalList = std::make_shared("", ""); + stage.addList(globalList); + + auto localList = std::make_shared("", ""); + sprite.addList(localList); + + createBuilder(&sprite, true); + + m_builder->createListClear(globalList.get()); + m_builder->createListClear(localList.get()); + + CompilerValue *v = m_builder->addConstValue("test"); + m_builder->createListAppend(globalList.get(), v); + + v = m_builder->addConstValue(5.25); + m_builder->createListAppend(localList.get(), v); + + v = m_builder->addConstValue(3); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a string list item is assigned later which becomes a number later + v = m_builder->addConstValue(0); + v = m_builder->addListItem(localList.get(), v); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); + m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); + + v = m_builder->addConstValue(12.5); + m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n" + "1\n" + "0\n" + "-1\n" + "0\n" + "12.5\n" + "-1\n" + "0\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis6) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalList = std::make_shared("", ""); + stage.addList(globalList); + + auto localList = std::make_shared("", ""); + sprite.addList(localList); + + createBuilder(&sprite, true); + + m_builder->createListClear(localList.get()); + + CompilerValue *v = m_builder->addConstValue(5.25); + m_builder->createListAppend(localList.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a list item with unknown type is added later (even though the list item has the original type assigned, it cannot be determined at compile time) + v = m_builder->createSub(m_builder->addListSize(localList.get()), m_builder->addConstValue(1)); + v = m_builder->addListItem(localList.get(), v); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue(10); + m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); + + v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); + m_builder->createListAppend(localList.get(), v); + + v = m_builder->addConstValue("test"); + m_builder->createListAppend(globalList.get(), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n" + "1\n" + "10\n" + "0\n" + "1\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis7) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalList = std::make_shared("", ""); + stage.addList(globalList); + + auto localList = std::make_shared("", ""); + sprite.addList(localList); + + createBuilder(&sprite, true); + + m_builder->createListClear(localList.get()); + + CompilerValue *v = m_builder->addConstValue(5.25); + m_builder->createListAppend(localList.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a list item with unknown type is assigned later + v = m_builder->addConstValue(0); + v = m_builder->addListItem(localList.get(), v); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue("test"); + m_builder->createListInsert(globalList.get(), m_builder->addConstValue(0), v); + + v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); + m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); + + v = m_builder->addConstValue(10); + m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n" + "1\n" + "0\n" + "-1\n" + "0\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis8) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalList = std::make_shared("", ""); + stage.addList(globalList); + + auto localList = std::make_shared("", ""); + sprite.addList(localList); + + createBuilder(&sprite, true); + + m_builder->createListClear(localList.get()); + + CompilerValue *v = m_builder->addConstValue(5.25); + m_builder->createListAppend(localList.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a list item with a unknown type is assigned later + v = m_builder->addConstValue(0); + v = m_builder->addListItem(localList.get(), v); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue("test"); + m_builder->createListInsert(globalList.get(), m_builder->addConstValue(0), v); + + v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); + m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n" + "1\n" + "0\n" + "-1\n" + "0\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis9) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalList = std::make_shared("", ""); + stage.addList(globalList); + + auto localList = std::make_shared("", ""); + sprite.addList(localList); + + createBuilder(&sprite, true); + + m_builder->createListClear(localList.get()); + + CompilerValue *v = m_builder->addConstValue(5.25); + m_builder->createListAppend(localList.get(), v); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); + m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); + + v = m_builder->addConstValue(5); + m_builder->createListInsert(globalList.get(), m_builder->addConstValue(0), v); + } + m_builder->endLoop(); + + // Type is unknown here because a list item with unknown type is assigned before the read operation + v = m_builder->addListItem(localList.get(), m_builder->addConstValue(0)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue(10); + m_builder->createListInsert(globalList.get(), m_builder->addConstValue(0), v); + + v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); + m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); + + v = m_builder->addConstValue("test"); + m_builder->createListInsert(globalList.get(), m_builder->addConstValue(0), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "5\n" + "0\n" + "1\n" + "5\n" + "0\n" + "1\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis10) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalList = std::make_shared("", ""); + stage.addList(globalList); + + auto localList = std::make_shared("", ""); + sprite.addList(localList); + localList->append(123); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue("test"); + m_builder->createListAppend(globalList.get(), v); + + v = m_builder->addConstValue(3); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a list item with unknown type is assigned later + v = m_builder->addListItem(localList.get(), m_builder->addConstValue(0)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(123)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(localList.get(), m_builder->addConstValue(123)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); + m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); + + v = m_builder->addConstValue(12.5); + m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "123\n" + "0\n" + "1\n" + "0\n" + "-1\n" + "0\n" + "12.5\n" + "-1\n" + "0\n" + "12.5\n" + "-1\n" + "0\n" + "12.5\n" + "-1\n" + "0\n" + "12.5\n" + "-1\n" + "0\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis11) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalList = std::make_shared("", ""); + stage.addList(globalList); + + auto localList = std::make_shared("", ""); + sprite.addList(localList); + localList->append(123); + localList->append("10"); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue("test"); + m_builder->createListAppend(globalList.get(), v); + + v = m_builder->addConstValue(3); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because an item from the same list is assigned later, but the list has unknown type + v = m_builder->addListItem(localList.get(), m_builder->addConstValue(0)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(123)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(localList.get(), m_builder->addConstValue(123)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addListItem(localList.get(), m_builder->addConstValue(1)); + m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); + + v = m_builder->addConstValue(12.5); + m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "123\n" + "0\n" + "1\n" + "10\n" + "-1\n" + "0\n" + "10\n" + "-1\n" + "0\n" + "10\n" + "-1\n" + "0\n" + "10\n" + "-1\n" + "0\n" + "10\n" + "-1\n" + "0\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis12) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto globalList = std::make_shared("", ""); + stage.addList(globalList); + + auto localList = std::make_shared("", ""); + sprite.addList(localList); + localList->append(123); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(2); + m_builder->beginRepeatLoop(v); + { + v = m_builder->addConstValue("test"); + m_builder->createListAppend(globalList.get(), v); + + v = m_builder->addConstValue(3); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because an item from another list is assigned later, but it has an item from this list assigned + // This case is not checked because the code isn't considered valid + v = m_builder->addListItem(localList.get(), m_builder->addConstValue(0)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(123)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(localList.get(), m_builder->addConstValue(123)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); + m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); + + v = m_builder->addListItem(localList.get(), m_builder->addConstValue(0)); + m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); + } + m_builder->endLoop(); + } + m_builder->endLoop(); + + std::string expected = + "123\n" + "0\n" + "1\n" + "0\n" + "-1\n" + "0\n" + "0\n" + "-1\n" + "0\n" + "0\n" + "-1\n" + "0\n" + "0\n" + "-1\n" + "0\n" + "0\n" + "-1\n" + "0\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + TEST_F(LLVMCodeBuilderTest, StopNoWarp) { Sprite sprite; From 17b37917793a92b07b35c56e3059e91ec58e3dea Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 18 Jan 2025 19:55:45 +0100 Subject: [PATCH 14/21] LLVMCodeBuilder: Add test for variable and list loop type analysis --- test/dev/llvm/llvmcodebuilder_test.cpp | 66 ++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index e2ebe5d1..1da6bf08 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -5640,6 +5640,72 @@ TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis12) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } +TEST_F(LLVMCodeBuilderTest, VarAndListLoopTypeAnalysis) +{ + Stage stage; + Sprite sprite; + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + + auto var = std::make_shared("", ""); + sprite.addVariable(var); + + auto list = std::make_shared("", ""); + sprite.addList(list); + + createBuilder(&sprite, true); + + CompilerValue *v = m_builder->addConstValue(-2); + m_builder->createVariableWrite(var.get(), v); + + m_builder->createListClear(list.get()); + + v = m_builder->addConstValue(5.25); + m_builder->createListAppend(list.get(), v); + + v = m_builder->addConstValue(3); + m_builder->beginRepeatLoop(v); + { + // Type is unknown here because a variable value with unknown type is added later + v = m_builder->createSub(m_builder->addListSize(list.get()), m_builder->addConstValue(1)); + v = m_builder->addListItem(list.get(), v); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListItemIndex(list.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = m_builder->addListContains(list.get(), m_builder->addConstValue(5.25)); + m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = m_builder->addVariableValue(var.get()); + m_builder->createListAppend(list.get(), v); + + v = m_builder->addConstValue("test"); + m_builder->createVariableWrite(var.get(), v); + } + m_builder->endLoop(); + + std::string expected = + "5.25\n" + "0\n" + "1\n" + "-2\n" + "0\n" + "1\n" + "0\n" + "0\n" + "1\n"; + + auto code = m_builder->finalize(); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + TEST_F(LLVMCodeBuilderTest, StopNoWarp) { Sprite sprite; From bd019d96ef6efff57e8edbec2a113d845b9e2892 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 18 Jan 2025 23:22:07 +0100 Subject: [PATCH 15/21] LLVMCodeBuilder: Optimize variables and lists recursively used in loops --- .../engine/internal/llvm/llvmcodebuilder.cpp | 59 ++++++++++--------- .../engine/internal/llvm/llvmcodebuilder.h | 4 +- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index c19e4bcb..04d23b39 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -728,7 +728,6 @@ std::shared_ptr LLVMCodeBuilder::finalize() Compiler::StaticType type = optimizeRegisterType(arg.second); LLVMListPtr &listPtr = m_listPtrs[step.workList]; - const bool safe = isVarOrListTypeSafe(insPtr, listPtr.type); auto &typeMap = m_scopeLists.back(); if (typeMap.find(&listPtr) == typeMap.cend()) { @@ -739,7 +738,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() typeMap[&listPtr] = listPtr.type; } - if (!safe) + if (!isVarOrListTypeSafe(insPtr, listPtr.type)) listPtr.type = Compiler::StaticType::Unknown; // Check if enough space is allocated @@ -777,7 +776,6 @@ std::shared_ptr LLVMCodeBuilder::finalize() Compiler::StaticType type = optimizeRegisterType(valueArg.second); LLVMListPtr &listPtr = m_listPtrs[step.workList]; - const bool safe = isVarOrListTypeSafe(insPtr, listPtr.type); auto &typeMap = m_scopeLists.back(); if (typeMap.find(&listPtr) == typeMap.cend()) { @@ -788,7 +786,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() typeMap[&listPtr] = listPtr.type; } - if (!safe) + if (!isVarOrListTypeSafe(insPtr, listPtr.type)) listPtr.type = Compiler::StaticType::Unknown; llvm::Value *oldAllocatedSize = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.allocatedSizePtr); @@ -2289,10 +2287,11 @@ void LLVMCodeBuilder::updateListDataPtr(const LLVMListPtr &listPtr) bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType) const { std::unordered_set processed; - return isVarOrListTypeSafe(ins, expectedType, processed); + int counter = 0; + return isVarOrListTypeSafe(ins, expectedType, processed, counter); } -bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, std::unordered_set &processed) const +bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, std::unordered_set &log, int &c) const { /* * The main part of the loop type analyzer. @@ -2318,22 +2317,20 @@ bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, /* * If we are processing something that has been already - * processed, give up to avoid infinite recursion. - * - * This can happen in edge cases like this: - * var = var + * processed, it means there's a case like this: + * x = x * * or this: * x = y + * ... * y = x * - * This code isn't considered valid, so don't bother - * optimizing. + * Increment counter to ignore last n write operations. */ - if (processed.find(ins.get()) != processed.cend()) - return false; - - processed.insert(ins.get()); + if (log.find(ins.get()) != log.cend()) + c++; + else + log.insert(ins.get()); assert(std::find(m_instructions.begin(), m_instructions.end(), ins) != m_instructions.end()); const LLVMVariablePtr *varPtr = ins->workVariable ? &m_variablePtrs.at(ins->workVariable) : nullptr; @@ -2388,18 +2385,15 @@ bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, // If there was a write operation before this instruction (in this, parent or child scope), check it if (parentScope) { if (parentScope == scope) - return isVarOrListWriteResultTypeSafe(write, expectedType, true, processed); + return isVarOrListWriteResultTypeSafe(write, expectedType, true, log, c); else - return isVarOrListTypeSafe(write, expectedType, processed); + return isVarOrListTypeSafe(write, expectedType, log, c); } } } const auto &loopWrites = varPtr ? varPtr->loopVariableWrites : listPtr->loopListWrites; - // Get last write operation - write = nullptr; - // Find root loop scope auto checkScope = scope; @@ -2407,13 +2401,18 @@ bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, checkScope = checkScope->parentScope; } - // Find last loop scope (may be a parent or child scope) + // Find n-th last write operation based on counter (may be in a parent or child scope) + std::vector> lastWrites; + while (checkScope) { auto it = loopWrites.find(checkScope); if (it != loopWrites.cend()) { assert(!it->second.empty()); - write = it->second.back(); + const auto &writes = it->second; + + for (auto w : writes) + lastWrites.push_back(w); } if (checkScope->childScopes.empty()) @@ -2422,22 +2421,24 @@ bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, checkScope = checkScope->childScopes.back(); } - // If there aren't any write operations, we're safe - if (!write) + // If there aren't any write operations or all of them are ignored, we're safe + if (c >= lastWrites.size()) return true; + write = lastWrites[lastWrites.size() - c - 1]; // Ignore last c writes + bool safe = true; if (VAR_LIST_READ_INSTRUCTIONS.find(ins->type) == VAR_LIST_READ_INSTRUCTIONS.cend()) // write - safe = isVarOrListWriteResultTypeSafe(ins, expectedType, false, processed); + safe = isVarOrListWriteResultTypeSafe(ins, expectedType, false, log, c); if (safe) - return isVarOrListWriteResultTypeSafe(write, expectedType, false, processed); + return isVarOrListWriteResultTypeSafe(write, expectedType, false, log, c); else return false; } -bool LLVMCodeBuilder::isVarOrListWriteResultTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set &processed) +bool LLVMCodeBuilder::isVarOrListWriteResultTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set &log, int &c) const { const LLVMVariablePtr *varPtr = ins->workVariable ? &m_variablePtrs.at(ins->workVariable) : nullptr; @@ -2449,7 +2450,7 @@ bool LLVMCodeBuilder::isVarOrListWriteResultTypeSafe(std::shared_ptrinstruction; if (argIns && (argIns->type == LLVMInstruction::Type::ReadVariable || argIns->type == LLVMInstruction::Type::GetListItem)) - return isVarOrListTypeSafe(argIns, expectedType, processed); + return isVarOrListTypeSafe(argIns, expectedType, log, c); // Check written type const bool typeMatches = (optimizeRegisterType(arg) == expectedType); diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.h b/src/dev/engine/internal/llvm/llvmcodebuilder.h index 22fa1609..fb49fe64 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.h @@ -150,8 +150,8 @@ class LLVMCodeBuilder : public ICodeBuilder void reloadLists(); void updateListDataPtr(const LLVMListPtr &listPtr); bool isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType) const; - bool isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, std::unordered_set &processed) const; - bool isVarOrListWriteResultTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set &processed) const; + bool isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, std::unordered_set &log, int &c) const; + bool isVarOrListWriteResultTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set &log, int &c) const; LLVMRegister *createOp(LLVMInstruction::Type type, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args); LLVMRegister *createOp(LLVMInstruction::Type type, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes = {}, const Compiler::Args &args = {}); From b3117dc0ebdc72bd2d3800219867fca59c4b8b78 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 19 Jan 2025 00:29:33 +0100 Subject: [PATCH 16/21] LLVMCodeBuilder: Fix list type change before last write in loop --- .../engine/internal/llvm/llvmcodebuilder.cpp | 19 +++++++++++++++---- test/dev/llvm/llvmcodebuilder_test.cpp | 13 +++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index 04d23b39..0c0a9743 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -2356,7 +2356,7 @@ bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, // Find previous write instruction in this, parent or child loop scope size_t index = it - instructions.begin(); - if (index > 0) { + if (varPtr && index > 0) { // this is only needed for variables bool found = false; do { @@ -2401,7 +2401,7 @@ bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, checkScope = checkScope->parentScope; } - // Find n-th last write operation based on counter (may be in a parent or child scope) + // Get all write operations in all loop scopes (from the root loop scope) std::vector> lastWrites; while (checkScope) { @@ -2425,7 +2425,18 @@ bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, if (c >= lastWrites.size()) return true; - write = lastWrites[lastWrites.size() - c - 1]; // Ignore last c writes + if (varPtr) + write = lastWrites[lastWrites.size() - c - 1]; // Ignore last c writes + else { + // If this is a list instruction, check last write operations except current + for (long i = lastWrites.size() - c - 1; i >= 0; i--) { // Ignore last c writes + if (lastWrites[i] == ins) + continue; + + if (!isVarOrListWriteResultTypeSafe(lastWrites[i], expectedType, false, log, c)) + return false; + } + } bool safe = true; @@ -2433,7 +2444,7 @@ bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, safe = isVarOrListWriteResultTypeSafe(ins, expectedType, false, log, c); if (safe) - return isVarOrListWriteResultTypeSafe(write, expectedType, false, log, c); + return write ? isVarOrListWriteResultTypeSafe(write, expectedType, false, log, c) : true; else return false; } diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 1da6bf08..e107097b 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -5149,15 +5149,19 @@ TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis6) createBuilder(&sprite, true); + m_builder->createListClear(globalList.get()); m_builder->createListClear(localList.get()); - CompilerValue *v = m_builder->addConstValue(5.25); + CompilerValue *v = m_builder->addConstValue(-156.07); + m_builder->createListAppend(globalList.get(), v); + + v = m_builder->addConstValue(5.25); m_builder->createListAppend(localList.get(), v); v = m_builder->addConstValue(2); m_builder->beginRepeatLoop(v); { - // Type is unknown here because a list item with unknown type is added later (even though the list item has the original type assigned, it cannot be determined at compile time) + // Type is unknown here because a list item with unknown type is added later v = m_builder->createSub(m_builder->addListSize(localList.get()), m_builder->addConstValue(1)); v = m_builder->addListItem(localList.get(), v); m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); @@ -5174,7 +5178,8 @@ TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis6) v = m_builder->addConstValue(10); m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); - v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); + v = m_builder->createSub(m_builder->addListSize(globalList.get()), m_builder->addConstValue(1)); + v = m_builder->addListItem(globalList.get(), v); m_builder->createListAppend(localList.get(), v); v = m_builder->addConstValue("test"); @@ -5188,7 +5193,7 @@ TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis6) "5.25\n" "0\n" "1\n" - "10\n" + "0\n" "0\n" "1\n"; From 5d6f27351502d36ee4f9cac7c2fb8d5cc3f8a67c Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 26 Jan 2025 15:37:59 +0100 Subject: [PATCH 17/21] Add color() method to extensions Resolves: #582 --- include/scratchcpp/iextension.h | 7 ++++--- src/blocks/controlblocks.cpp | 5 +++++ src/blocks/controlblocks.h | 1 + src/blocks/customblocks.cpp | 5 +++++ src/blocks/customblocks.h | 1 + src/blocks/eventblocks.cpp | 5 +++++ src/blocks/eventblocks.h | 1 + src/blocks/listblocks.cpp | 5 +++++ src/blocks/listblocks.h | 1 + src/blocks/looksblocks.cpp | 5 +++++ src/blocks/looksblocks.h | 1 + src/blocks/motionblocks.cpp | 5 +++++ src/blocks/motionblocks.h | 1 + src/blocks/operatorblocks.cpp | 5 +++++ src/blocks/operatorblocks.h | 1 + src/blocks/sensingblocks.cpp | 5 +++++ src/blocks/sensingblocks.h | 1 + src/blocks/soundblocks.cpp | 5 +++++ src/blocks/soundblocks.h | 1 + src/blocks/variableblocks.cpp | 5 +++++ src/blocks/variableblocks.h | 1 + src/dev/blocks/controlblocks.cpp | 5 +++++ src/dev/blocks/controlblocks.h | 1 + src/dev/blocks/customblocks.cpp | 5 +++++ src/dev/blocks/customblocks.h | 1 + src/dev/blocks/eventblocks.cpp | 5 +++++ src/dev/blocks/eventblocks.h | 1 + src/dev/blocks/listblocks.cpp | 5 +++++ src/dev/blocks/listblocks.h | 1 + src/dev/blocks/looksblocks.cpp | 5 +++++ src/dev/blocks/looksblocks.h | 1 + src/dev/blocks/motionblocks.cpp | 5 +++++ src/dev/blocks/motionblocks.h | 1 + src/dev/blocks/operatorblocks.cpp | 5 +++++ src/dev/blocks/operatorblocks.h | 1 + src/dev/blocks/sensingblocks.cpp | 5 +++++ src/dev/blocks/sensingblocks.h | 1 + src/dev/blocks/soundblocks.cpp | 5 +++++ src/dev/blocks/soundblocks.h | 1 + src/dev/blocks/variableblocks.cpp | 5 +++++ src/dev/blocks/variableblocks.h | 1 + test/compiler/testextension.cpp | 5 +++++ test/compiler/testextension.h | 1 + test/dev/test_api/testextension.cpp | 5 +++++ test/dev/test_api/testextension.h | 1 + test/extensions/testextension.cpp | 5 +++++ test/extensions/testextension.h | 1 + test/mocks/extensionmock.h | 1 + test/scratch_classes/testextension.cpp | 5 +++++ test/scratch_classes/testextension.h | 1 + test/scratchconfiguration/extension1.cpp | 5 +++++ test/scratchconfiguration/extension1.h | 1 + test/scratchconfiguration/extension2.cpp | 5 +++++ test/scratchconfiguration/extension2.h | 1 + test/scratchconfiguration/extension3.cpp | 5 +++++ test/scratchconfiguration/extension3.h | 1 + 56 files changed, 167 insertions(+), 3 deletions(-) diff --git a/include/scratchcpp/iextension.h b/include/scratchcpp/iextension.h index a53b8356..0a4bff08 100644 --- a/include/scratchcpp/iextension.h +++ b/include/scratchcpp/iextension.h @@ -2,9 +2,7 @@ #pragma once -#include - -#include "global.h" +#include "value_functions.h" namespace libscratchcpp { @@ -27,6 +25,9 @@ class LIBSCRATCHCPP_EXPORT IExtension /*! Returns the description of the extension. */ virtual std::string description() const = 0; + /*! Returns the block color of the extension. */ + virtual Rgb color() const = 0; + /*! Override this method to register blocks. */ virtual void registerBlocks(IEngine *engine) = 0; diff --git a/src/blocks/controlblocks.cpp b/src/blocks/controlblocks.cpp index ab5aab69..aa4ebbce 100644 --- a/src/blocks/controlblocks.cpp +++ b/src/blocks/controlblocks.cpp @@ -25,6 +25,11 @@ std::string ControlBlocks::description() const return name() + " blocks"; } +Rgb ControlBlocks::color() const +{ + return rgb(255, 171, 25); +} + void ControlBlocks::registerBlocks(IEngine *engine) { // Blocks diff --git a/src/blocks/controlblocks.h b/src/blocks/controlblocks.h index aa0c8710..623ab651 100644 --- a/src/blocks/controlblocks.h +++ b/src/blocks/controlblocks.h @@ -43,6 +43,7 @@ class ControlBlocks : public IExtension std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/src/blocks/customblocks.cpp b/src/blocks/customblocks.cpp index aee3b757..8066a025 100644 --- a/src/blocks/customblocks.cpp +++ b/src/blocks/customblocks.cpp @@ -20,6 +20,11 @@ std::string CustomBlocks::description() const return name(); } +Rgb CustomBlocks::color() const +{ + return rgb(255, 102, 128); +} + void CustomBlocks::registerBlocks(IEngine *engine) { // Blocks diff --git a/src/blocks/customblocks.h b/src/blocks/customblocks.h index a40e37c3..23482886 100644 --- a/src/blocks/customblocks.h +++ b/src/blocks/customblocks.h @@ -24,6 +24,7 @@ class CustomBlocks : public IExtension std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/src/blocks/eventblocks.cpp b/src/blocks/eventblocks.cpp index 757e2bda..85ec9235 100644 --- a/src/blocks/eventblocks.cpp +++ b/src/blocks/eventblocks.cpp @@ -30,6 +30,11 @@ std::string libscratchcpp::EventBlocks::description() const return "Event blocks"; } +Rgb EventBlocks::color() const +{ + return rgb(255, 191, 0); +} + void EventBlocks::registerBlocks(IEngine *engine) { // Blocks diff --git a/src/blocks/eventblocks.h b/src/blocks/eventblocks.h index e4d28f4a..9ddca5b6 100644 --- a/src/blocks/eventblocks.h +++ b/src/blocks/eventblocks.h @@ -38,6 +38,7 @@ class EventBlocks : public IExtension std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/src/blocks/listblocks.cpp b/src/blocks/listblocks.cpp index 135dd0bd..4302c3bc 100644 --- a/src/blocks/listblocks.cpp +++ b/src/blocks/listblocks.cpp @@ -23,6 +23,11 @@ std::string ListBlocks::description() const return "List blocks"; } +Rgb ListBlocks::color() const +{ + return rgb(255, 102, 26); +} + void ListBlocks::registerBlocks(IEngine *engine) { // Blocks diff --git a/src/blocks/listblocks.h b/src/blocks/listblocks.h index 69e49aa3..ed621314 100644 --- a/src/blocks/listblocks.h +++ b/src/blocks/listblocks.h @@ -28,6 +28,7 @@ class ListBlocks : public IExtension std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/src/blocks/looksblocks.cpp b/src/blocks/looksblocks.cpp index f142f34c..4a5ddf31 100644 --- a/src/blocks/looksblocks.cpp +++ b/src/blocks/looksblocks.cpp @@ -42,6 +42,11 @@ std::string LooksBlocks::description() const return name() + " blocks"; } +Rgb LooksBlocks::color() const +{ + return rgb(153, 102, 255); +} + void LooksBlocks::registerBlocks(IEngine *engine) { // Blocks diff --git a/src/blocks/looksblocks.h b/src/blocks/looksblocks.h index 8881d7eb..eca733de 100644 --- a/src/blocks/looksblocks.h +++ b/src/blocks/looksblocks.h @@ -60,6 +60,7 @@ class LooksBlocks : public IExtension std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; void onInit(IEngine *engine) override; diff --git a/src/blocks/motionblocks.cpp b/src/blocks/motionblocks.cpp index c5a96004..62fd28ae 100644 --- a/src/blocks/motionblocks.cpp +++ b/src/blocks/motionblocks.cpp @@ -29,6 +29,11 @@ std::string MotionBlocks::description() const return name() + " blocks"; } +Rgb MotionBlocks::color() const +{ + return rgb(76, 151, 255); +} + void MotionBlocks::registerBlocks(IEngine *engine) { // Blocks diff --git a/src/blocks/motionblocks.h b/src/blocks/motionblocks.h index b545822e..b86e9481 100644 --- a/src/blocks/motionblocks.h +++ b/src/blocks/motionblocks.h @@ -45,6 +45,7 @@ class MotionBlocks : public IExtension std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; void onInit(IEngine *engine) override; diff --git a/src/blocks/operatorblocks.cpp b/src/blocks/operatorblocks.cpp index 376ae27a..567d4a15 100644 --- a/src/blocks/operatorblocks.cpp +++ b/src/blocks/operatorblocks.cpp @@ -18,6 +18,11 @@ std::string OperatorBlocks::description() const return "Operator blocks"; } +Rgb OperatorBlocks::color() const +{ + return rgb(89, 192, 89); +} + void OperatorBlocks::registerBlocks(IEngine *engine) { // Blocks diff --git a/src/blocks/operatorblocks.h b/src/blocks/operatorblocks.h index e9dd932a..f2f5b729 100644 --- a/src/blocks/operatorblocks.h +++ b/src/blocks/operatorblocks.h @@ -55,6 +55,7 @@ class OperatorBlocks : public IExtension std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/src/blocks/sensingblocks.cpp b/src/blocks/sensingblocks.cpp index af1349ed..f5b18ce7 100644 --- a/src/blocks/sensingblocks.cpp +++ b/src/blocks/sensingblocks.cpp @@ -34,6 +34,11 @@ std::string SensingBlocks::description() const return name() + " blocks"; } +Rgb SensingBlocks::color() const +{ + return rgb(92, 177, 214); +} + void SensingBlocks::registerBlocks(IEngine *engine) { // Blocks diff --git a/src/blocks/sensingblocks.h b/src/blocks/sensingblocks.h index 8153a18b..5b3fc638 100644 --- a/src/blocks/sensingblocks.h +++ b/src/blocks/sensingblocks.h @@ -60,6 +60,7 @@ class SensingBlocks : public IExtension std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; void onInit(IEngine *engine) override; diff --git a/src/blocks/soundblocks.cpp b/src/blocks/soundblocks.cpp index 6cc2aa7b..f001fa7b 100644 --- a/src/blocks/soundblocks.cpp +++ b/src/blocks/soundblocks.cpp @@ -34,6 +34,11 @@ std::string SoundBlocks::description() const return name() + " blocks"; } +Rgb SoundBlocks::color() const +{ + return rgb(207, 99, 207); +} + void SoundBlocks::registerBlocks(IEngine *engine) { // Blocks diff --git a/src/blocks/soundblocks.h b/src/blocks/soundblocks.h index d31b8a62..0535eb73 100644 --- a/src/blocks/soundblocks.h +++ b/src/blocks/soundblocks.h @@ -37,6 +37,7 @@ class SoundBlocks : public IExtension std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; void onInit(IEngine *engine) override; diff --git a/src/blocks/variableblocks.cpp b/src/blocks/variableblocks.cpp index 81961e49..17236a30 100644 --- a/src/blocks/variableblocks.cpp +++ b/src/blocks/variableblocks.cpp @@ -23,6 +23,11 @@ std::string VariableBlocks::description() const return "Variable blocks"; } +Rgb VariableBlocks::color() const +{ + return rgb(255, 140, 26); +} + void VariableBlocks::registerBlocks(IEngine *engine) { // Blocks diff --git a/src/blocks/variableblocks.h b/src/blocks/variableblocks.h index 2372660b..f4b2922c 100644 --- a/src/blocks/variableblocks.h +++ b/src/blocks/variableblocks.h @@ -27,6 +27,7 @@ class VariableBlocks : public IExtension std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/src/dev/blocks/controlblocks.cpp b/src/dev/blocks/controlblocks.cpp index 9567f69f..0ca0524d 100644 --- a/src/dev/blocks/controlblocks.cpp +++ b/src/dev/blocks/controlblocks.cpp @@ -26,6 +26,11 @@ std::string ControlBlocks::description() const return name() + " blocks"; } +Rgb ControlBlocks::color() const +{ + return rgb(255, 171, 25); +} + void ControlBlocks::registerBlocks(IEngine *engine) { engine->addCompileFunction(this, "control_forever", &compileForever); diff --git a/src/dev/blocks/controlblocks.h b/src/dev/blocks/controlblocks.h index 17850824..0f44982c 100644 --- a/src/dev/blocks/controlblocks.h +++ b/src/dev/blocks/controlblocks.h @@ -12,6 +12,7 @@ class ControlBlocks : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/src/dev/blocks/customblocks.cpp b/src/dev/blocks/customblocks.cpp index e78048bc..655e4d3b 100644 --- a/src/dev/blocks/customblocks.cpp +++ b/src/dev/blocks/customblocks.cpp @@ -20,6 +20,11 @@ std::string CustomBlocks::description() const return name(); } +Rgb CustomBlocks::color() const +{ + return rgb(255, 102, 128); +} + void CustomBlocks::registerBlocks(IEngine *engine) { engine->addCompileFunction(this, "procedures_definition", [](Compiler *) -> CompilerValue * { return nullptr; }); diff --git a/src/dev/blocks/customblocks.h b/src/dev/blocks/customblocks.h index 55572ba9..b2a2613f 100644 --- a/src/dev/blocks/customblocks.h +++ b/src/dev/blocks/customblocks.h @@ -12,6 +12,7 @@ class CustomBlocks : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/src/dev/blocks/eventblocks.cpp b/src/dev/blocks/eventblocks.cpp index a2b18b3b..ba4d2663 100644 --- a/src/dev/blocks/eventblocks.cpp +++ b/src/dev/blocks/eventblocks.cpp @@ -24,6 +24,11 @@ std::string libscratchcpp::EventBlocks::description() const return "Event blocks"; } +Rgb EventBlocks::color() const +{ + return rgb(255, 191, 0); +} + void EventBlocks::registerBlocks(IEngine *engine) { engine->addCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObject); diff --git a/src/dev/blocks/eventblocks.h b/src/dev/blocks/eventblocks.h index 34454ebc..512bae0c 100644 --- a/src/dev/blocks/eventblocks.h +++ b/src/dev/blocks/eventblocks.h @@ -12,6 +12,7 @@ class EventBlocks : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/src/dev/blocks/listblocks.cpp b/src/dev/blocks/listblocks.cpp index 653875e4..64b44135 100644 --- a/src/dev/blocks/listblocks.cpp +++ b/src/dev/blocks/listblocks.cpp @@ -20,6 +20,11 @@ std::string ListBlocks::description() const return "List blocks"; } +Rgb ListBlocks::color() const +{ + return rgb(255, 102, 26); +} + void ListBlocks::registerBlocks(IEngine *engine) { engine->addCompileFunction(this, "data_addtolist", &compileAddToList); diff --git a/src/dev/blocks/listblocks.h b/src/dev/blocks/listblocks.h index 3a8d0126..02dbd79c 100644 --- a/src/dev/blocks/listblocks.h +++ b/src/dev/blocks/listblocks.h @@ -14,6 +14,7 @@ class ListBlocks : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/src/dev/blocks/looksblocks.cpp b/src/dev/blocks/looksblocks.cpp index 0d471fb0..8da59e7a 100644 --- a/src/dev/blocks/looksblocks.cpp +++ b/src/dev/blocks/looksblocks.cpp @@ -14,6 +14,11 @@ std::string LooksBlocks::description() const return name() + " blocks"; } +Rgb LooksBlocks::color() const +{ + return rgb(153, 102, 255); +} + void LooksBlocks::registerBlocks(IEngine *engine) { } diff --git a/src/dev/blocks/looksblocks.h b/src/dev/blocks/looksblocks.h index 8bfa5f18..b7529754 100644 --- a/src/dev/blocks/looksblocks.h +++ b/src/dev/blocks/looksblocks.h @@ -12,6 +12,7 @@ class LooksBlocks : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; }; diff --git a/src/dev/blocks/motionblocks.cpp b/src/dev/blocks/motionblocks.cpp index e44a673c..a249cdff 100644 --- a/src/dev/blocks/motionblocks.cpp +++ b/src/dev/blocks/motionblocks.cpp @@ -14,6 +14,11 @@ std::string MotionBlocks::description() const return name() + " blocks"; } +Rgb MotionBlocks::color() const +{ + return rgb(76, 151, 255); +} + void MotionBlocks::registerBlocks(IEngine *engine) { } diff --git a/src/dev/blocks/motionblocks.h b/src/dev/blocks/motionblocks.h index 125b4073..44d7c0bd 100644 --- a/src/dev/blocks/motionblocks.h +++ b/src/dev/blocks/motionblocks.h @@ -12,6 +12,7 @@ class MotionBlocks : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; }; diff --git a/src/dev/blocks/operatorblocks.cpp b/src/dev/blocks/operatorblocks.cpp index b962d3c7..508d41a1 100644 --- a/src/dev/blocks/operatorblocks.cpp +++ b/src/dev/blocks/operatorblocks.cpp @@ -21,6 +21,11 @@ std::string OperatorBlocks::description() const return "Operator blocks"; } +Rgb OperatorBlocks::color() const +{ + return rgb(89, 192, 89); +} + void OperatorBlocks::registerBlocks(IEngine *engine) { engine->addCompileFunction(this, "operator_add", &compileAdd); diff --git a/src/dev/blocks/operatorblocks.h b/src/dev/blocks/operatorblocks.h index 1804e931..647512e6 100644 --- a/src/dev/blocks/operatorblocks.h +++ b/src/dev/blocks/operatorblocks.h @@ -14,6 +14,7 @@ class OperatorBlocks : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/src/dev/blocks/sensingblocks.cpp b/src/dev/blocks/sensingblocks.cpp index 42896ecb..8e3b18ca 100644 --- a/src/dev/blocks/sensingblocks.cpp +++ b/src/dev/blocks/sensingblocks.cpp @@ -14,6 +14,11 @@ std::string SensingBlocks::description() const return name() + " blocks"; } +Rgb SensingBlocks::color() const +{ + return rgb(92, 177, 214); +} + void SensingBlocks::registerBlocks(IEngine *engine) { } diff --git a/src/dev/blocks/sensingblocks.h b/src/dev/blocks/sensingblocks.h index ab934fb0..9a0f367f 100644 --- a/src/dev/blocks/sensingblocks.h +++ b/src/dev/blocks/sensingblocks.h @@ -12,6 +12,7 @@ class SensingBlocks : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; }; diff --git a/src/dev/blocks/soundblocks.cpp b/src/dev/blocks/soundblocks.cpp index 4a440a9a..df7611ab 100644 --- a/src/dev/blocks/soundblocks.cpp +++ b/src/dev/blocks/soundblocks.cpp @@ -14,6 +14,11 @@ std::string SoundBlocks::description() const return name() + " blocks"; } +Rgb SoundBlocks::color() const +{ + return rgb(207, 99, 207); +} + void SoundBlocks::registerBlocks(IEngine *engine) { } diff --git a/src/dev/blocks/soundblocks.h b/src/dev/blocks/soundblocks.h index afae6c9c..f605c7b6 100644 --- a/src/dev/blocks/soundblocks.h +++ b/src/dev/blocks/soundblocks.h @@ -12,6 +12,7 @@ class SoundBlocks : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; }; diff --git a/src/dev/blocks/variableblocks.cpp b/src/dev/blocks/variableblocks.cpp index 177d8dab..9e77c7bf 100644 --- a/src/dev/blocks/variableblocks.cpp +++ b/src/dev/blocks/variableblocks.cpp @@ -21,6 +21,11 @@ std::string VariableBlocks::description() const return "Variable blocks"; } +Rgb VariableBlocks::color() const +{ + return rgb(255, 140, 26); +} + void VariableBlocks::registerBlocks(IEngine *engine) { engine->addCompileFunction(this, "data_variable", &compileVariable); diff --git a/src/dev/blocks/variableblocks.h b/src/dev/blocks/variableblocks.h index 9cb0bcc4..423085d3 100644 --- a/src/dev/blocks/variableblocks.h +++ b/src/dev/blocks/variableblocks.h @@ -12,6 +12,7 @@ class VariableBlocks : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/test/compiler/testextension.cpp b/test/compiler/testextension.cpp index cb001061..ce26bbc3 100644 --- a/test/compiler/testextension.cpp +++ b/test/compiler/testextension.cpp @@ -18,6 +18,11 @@ std::string TestExtension::description() const return ""; } +Rgb TestExtension::color() const +{ + return rgb(0, 0, 0); +} + void TestExtension::registerBlocks(IEngine *engine) { engine->addInput(this, "INPUT1", INPUT1); diff --git a/test/compiler/testextension.h b/test/compiler/testextension.h index c7bdf102..6efc819d 100644 --- a/test/compiler/testextension.h +++ b/test/compiler/testextension.h @@ -25,6 +25,7 @@ class TestExtension : public IExtension std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/test/dev/test_api/testextension.cpp b/test/dev/test_api/testextension.cpp index c637e522..24eebec2 100644 --- a/test/dev/test_api/testextension.cpp +++ b/test/dev/test_api/testextension.cpp @@ -21,6 +21,11 @@ std::string TestExtension::description() const return ""; } +Rgb TestExtension::color() const +{ + return rgb(0, 0, 0); +} + void TestExtension::registerBlocks(IEngine *engine) { engine->addCompileFunction(this, "test_simple", &compileSimple); diff --git a/test/dev/test_api/testextension.h b/test/dev/test_api/testextension.h index e69bbcdd..12f4e729 100644 --- a/test/dev/test_api/testextension.h +++ b/test/dev/test_api/testextension.h @@ -10,6 +10,7 @@ class TestExtension : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; diff --git a/test/extensions/testextension.cpp b/test/extensions/testextension.cpp index d7a8dd5b..5d122d48 100644 --- a/test/extensions/testextension.cpp +++ b/test/extensions/testextension.cpp @@ -14,6 +14,11 @@ std::string TestExtension::description() const return "Test extension"; } +Rgb TestExtension::color() const +{ + return rgb(0, 0, 0); +} + void TestExtension::registerBlocks(IEngine *engine) { engine->clear(); diff --git a/test/extensions/testextension.h b/test/extensions/testextension.h index 7bad056d..7e89e77d 100644 --- a/test/extensions/testextension.h +++ b/test/extensions/testextension.h @@ -10,6 +10,7 @@ class TestExtension : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; void onInit(IEngine *engine) override; diff --git a/test/mocks/extensionmock.h b/test/mocks/extensionmock.h index 19f47d6e..e0750c37 100644 --- a/test/mocks/extensionmock.h +++ b/test/mocks/extensionmock.h @@ -10,6 +10,7 @@ class ExtensionMock : public IExtension public: MOCK_METHOD(std::string, name, (), (const, override)); MOCK_METHOD(std::string, description, (), (const, override)); + MOCK_METHOD(Rgb, color, (), (const, override)); MOCK_METHOD(void, registerBlocks, (IEngine *), (override)); MOCK_METHOD(void, onInit, (IEngine *), (override)); diff --git a/test/scratch_classes/testextension.cpp b/test/scratch_classes/testextension.cpp index cd848ca6..9d21e10e 100644 --- a/test/scratch_classes/testextension.cpp +++ b/test/scratch_classes/testextension.cpp @@ -14,6 +14,11 @@ std::string TestExtension::description() const return ""; } +Rgb TestExtension::color() const +{ + return rgb(0, 0, 0); +} + void TestExtension::registerBlocks(IEngine *engine) { } diff --git a/test/scratch_classes/testextension.h b/test/scratch_classes/testextension.h index 88162cba..d15b2094 100644 --- a/test/scratch_classes/testextension.h +++ b/test/scratch_classes/testextension.h @@ -10,6 +10,7 @@ class TestExtension : public IExtension public: std::string name() const override; std::string description() const override; + Rgb color() const override; void registerBlocks(IEngine *engine) override; }; diff --git a/test/scratchconfiguration/extension1.cpp b/test/scratchconfiguration/extension1.cpp index 2e8fa183..b49f2b0e 100644 --- a/test/scratchconfiguration/extension1.cpp +++ b/test/scratchconfiguration/extension1.cpp @@ -6,3 +6,8 @@ std::string Extension1::name() const { return "ext 1"; } + +Rgb Extension1::color() const +{ + return rgb(0, 0, 0); +} diff --git a/test/scratchconfiguration/extension1.h b/test/scratchconfiguration/extension1.h index f7f8009e..ea08ea87 100644 --- a/test/scratchconfiguration/extension1.h +++ b/test/scratchconfiguration/extension1.h @@ -9,6 +9,7 @@ class Extension1 : public ExtensionBase { public: std::string name() const override; + Rgb color() const override; }; } // namespace libscratchcpp diff --git a/test/scratchconfiguration/extension2.cpp b/test/scratchconfiguration/extension2.cpp index 5c58c11e..5efe0561 100644 --- a/test/scratchconfiguration/extension2.cpp +++ b/test/scratchconfiguration/extension2.cpp @@ -6,3 +6,8 @@ std::string Extension2::name() const { return "ext 2"; } + +Rgb Extension2::color() const +{ + return rgb(0, 0, 0); +} diff --git a/test/scratchconfiguration/extension2.h b/test/scratchconfiguration/extension2.h index 22caf7f1..a6e03cd8 100644 --- a/test/scratchconfiguration/extension2.h +++ b/test/scratchconfiguration/extension2.h @@ -9,6 +9,7 @@ class Extension2 : public ExtensionBase { public: std::string name() const override; + Rgb color() const override; }; } // namespace libscratchcpp diff --git a/test/scratchconfiguration/extension3.cpp b/test/scratchconfiguration/extension3.cpp index fb28af9b..021524f1 100644 --- a/test/scratchconfiguration/extension3.cpp +++ b/test/scratchconfiguration/extension3.cpp @@ -6,3 +6,8 @@ std::string Extension3::name() const { return "ext 3"; } + +Rgb Extension3::color() const +{ + return rgb(0, 0, 0); +} diff --git a/test/scratchconfiguration/extension3.h b/test/scratchconfiguration/extension3.h index d434d754..5ebe8c4d 100644 --- a/test/scratchconfiguration/extension3.h +++ b/test/scratchconfiguration/extension3.h @@ -9,6 +9,7 @@ class Extension3 : public ExtensionBase { public: std::string name() const override; + Rgb color() const override; }; } // namespace libscratchcpp From a053380920c880541248147078a82e705c01a284 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 28 Jan 2025 14:41:01 +0100 Subject: [PATCH 18/21] audio: Fix a race condition in miniaudio https://github.com/mackron/miniaudio/issues/932 --- .../internal/thirdparty/miniaudio/miniaudio.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/audio/internal/thirdparty/miniaudio/miniaudio.c b/src/audio/internal/thirdparty/miniaudio/miniaudio.c index 651843b2..077c396c 100644 --- a/src/audio/internal/thirdparty/miniaudio/miniaudio.c +++ b/src/audio/internal/thirdparty/miniaudio/miniaudio.c @@ -18616,15 +18616,20 @@ static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFra static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) { static int g_StreamCounter = 0; + static ma_mutex g_StreamCounterMutex; char actualStreamName[256]; - if (pStreamName != NULL) { - ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); - } else { - ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:"); - ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */ + ma_mutex_lock(&g_StreamCounterMutex); + { + if (pStreamName != NULL) { + ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); + } else { + ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:"); + ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */ + } + g_StreamCounter += 1; } - g_StreamCounter += 1; + ma_mutex_unlock(&g_StreamCounterMutex); return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); } From 7c1e08a5c2f0bc192e66c432fb5800d716db1bb2 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 25 Jan 2025 22:47:57 +0100 Subject: [PATCH 19/21] Refactor CMake configuration --- CMakeLists.txt | 28 ++++++++++++++++++---------- build/zip.cmake | 9 +++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 build/zip.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ca8c1aa..bd773c01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,26 +95,27 @@ else() endif() include(FetchContent) -set(ZIP_SRC thirdparty/zip/src) -set(UTFCPP_SRC thirdparty/utfcpp/source) -add_library(zip SHARED - ${ZIP_SRC}/zip.c - ${ZIP_SRC}/zip.h - ${ZIP_SRC}/miniz.h -) -target_include_directories(scratchcpp PUBLIC ${ZIP_SRC}) +# zip +include(build/zip.cmake) +target_link_libraries(scratchcpp PRIVATE zip) +# utfcpp +set(UTFCPP_SRC thirdparty/utfcpp/source) target_include_directories(scratchcpp PUBLIC ${UTFCPP_SRC}) + +# spimpl include_directories(thirdparty/spimpl) +# JSON FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz) FetchContent_MakeAvailable(json) - target_link_libraries(scratchcpp PRIVATE nlohmann_json::nlohmann_json) -target_link_libraries(scratchcpp PRIVATE zip) + +# Audio target_link_libraries(scratchcpp PRIVATE scratchcpp-audio) +# Network if (LIBSCRATCHCPP_NETWORK_SUPPORT) FetchContent_Declare(cpr GIT_REPOSITORY https://github.com/libcpr/cpr.git GIT_TAG 225b7454877805f089b3895260438e929bd6d123) # 09-22-2024 @@ -123,18 +124,25 @@ if (LIBSCRATCHCPP_NETWORK_SUPPORT) target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_NETWORK_SUPPORT) endif() +# LLVM if (LIBSCRATCHCPP_USE_LLVM) include(build/HunterPackages.cmake) include(build/LLVM.cmake) target_link_libraries(scratchcpp PRIVATE LLVM) endif() +if(LIBSCRATCHCPP_PRINT_LLVM_IR) + target_compile_definitions(scratchcpp PRIVATE PRINT_LLVM_IR) +endif() + +# Macros target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_LIBRARY) target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_VERSION="${PROJECT_VERSION}") target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}) target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_VERSION_MINOR=${PROJECT_VERSION_MINOR}) target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_VERSION_PATCH=${PROJECT_VERSION_PATCH}) +# Unit tests if (LIBSCRATCHCPP_BUILD_UNIT_TESTS) enable_testing() add_subdirectory(test) diff --git a/build/zip.cmake b/build/zip.cmake new file mode 100644 index 00000000..cf970fb8 --- /dev/null +++ b/build/zip.cmake @@ -0,0 +1,9 @@ +set(ZIP_SRC ${PROJECT_SOURCE_DIR}/thirdparty/zip/src) + +add_library(zip SHARED + ${ZIP_SRC}/zip.c + ${ZIP_SRC}/zip.h + ${ZIP_SRC}/miniz.h +) + +target_include_directories(zip PUBLIC ${ZIP_SRC}) From 30ca4d39b77648e35cee24feeff828d0a8abaf8e Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 25 Jan 2025 22:59:02 +0100 Subject: [PATCH 20/21] Configure CMake installation --- CMakeLists.txt | 1 + build/zip.cmake | 1 + 2 files changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd773c01..810a5382 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ add_library(scratchcpp SHARED) add_subdirectory(src) include_directories(src) # TODO: Remove this line include_directories(include) +install(TARGETS scratchcpp DESTINATION lib) if (LIBSCRATCHCPP_COMPUTED_GOTO) target_compile_definitions(scratchcpp PRIVATE ENABLE_COMPUTED_GOTO) diff --git a/build/zip.cmake b/build/zip.cmake index cf970fb8..1165aba8 100644 --- a/build/zip.cmake +++ b/build/zip.cmake @@ -7,3 +7,4 @@ add_library(zip SHARED ) target_include_directories(zip PUBLIC ${ZIP_SRC}) +install(TARGETS zip DESTINATION lib) From a5c4dbe9acc177af46c1e70c5a5817f55faed58b Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 28 Jan 2025 15:21:32 +0100 Subject: [PATCH 21/21] Link compiler test against zip library --- test/compiler/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/compiler/CMakeLists.txt b/test/compiler/CMakeLists.txt index 9dc335df..0b7d501a 100644 --- a/test/compiler/CMakeLists.txt +++ b/test/compiler/CMakeLists.txt @@ -9,6 +9,7 @@ target_link_libraries( compiler_test GTest::gtest_main scratchcpp + zip nlohmann_json::nlohmann_json )