From 1c67ddc74c4a6596e77c9a190d1f995a4620d8f9 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 17 Aug 2025 11:23:05 +0200 Subject: [PATCH 01/42] Store variables on the stack in warp scripts --- .../internal/llvm/instructions/variables.cpp | 28 ++---------- src/engine/internal/llvm/llvmbuildutils.cpp | 43 ++++++++----------- src/engine/internal/llvm/llvmvariableptr.h | 1 - 3 files changed, 20 insertions(+), 52 deletions(-) diff --git a/src/engine/internal/llvm/instructions/variables.cpp b/src/engine/internal/llvm/instructions/variables.cpp index 9298c3d5..372aed25 100644 --- a/src/engine/internal/llvm/instructions/variables.cpp +++ b/src/engine/internal/llvm/instructions/variables.cpp @@ -111,30 +111,8 @@ LLVMInstruction *Variables::buildWriteVariable(LLVMInstruction *ins) const auto &arg = ins->args[0]; Compiler::StaticType argType = m_utils.optimizeRegisterType(arg.second); LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->targetVariable); - varPtr.changed = true; // TODO: Handle loops and if statements - - // Initialize stack variable on first assignment - // TODO: Use stack in the top level (outside loops and if statements) - /*if (!varPtr.onStack) { - varPtr.onStack = true; - varPtr.type = type; // don't care about unknown type on first assignment - - ValueType mappedType; - - if (type == Compiler::StaticType::String || type == Compiler::StaticType::Unknown) { - // Value functions are used for these types, so don't break them - mappedType = ValueType::Number; - } else { - auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [type](const std::pair &pair) { return pair.second == type; }); - assert(it != TYPE_MAP.cend()); - mappedType = it->first; - } - - llvm::Value *typeField = m_builder.CreateStructGEP(m_valueDataType, varPtr.stackPtr, 1); - m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typeField); - }*/ - - m_utils.createValueStore(arg.second, varPtr.stackPtr, ins->targetType, argType); + + m_utils.createValueStore(arg.second, varPtr.onStack ? varPtr.stackPtr : varPtr.heapPtr, ins->targetType, argType); return ins->next; } @@ -143,6 +121,6 @@ LLVMInstruction *Variables::buildReadVariable(LLVMInstruction *ins) assert(ins->args.size() == 0); LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->targetVariable); - ins->functionReturnReg->value = varPtr.onStack && !(ins->loopCondition && !m_utils.warp()) ? varPtr.stackPtr : varPtr.heapPtr; + ins->functionReturnReg->value = varPtr.onStack ? varPtr.stackPtr : varPtr.heapPtr; return ins->next; } diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index f61efe75..e3c5891c 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -63,26 +63,12 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro // Direct access varPtr.heapPtr = ptr; - // All variables are currently created on the stack and synced later (seems to be faster) + // In warp scripts, all variables are created on the stack and synced later (seems to be faster) // NOTE: Strings are NOT copied, only the pointer is copied - // TODO: Restore this feature - // varPtr.stackPtr = m_builder.CreateAlloca(m_valueDataType); - varPtr.stackPtr = varPtr.heapPtr; - varPtr.onStack = false; - - // If there are no write operations outside loops, initialize the stack variable now - /*Variable *variable = var; - // TODO: Loop scope was used here, replace it with some "inside loop" flag if needed - auto it = std::find_if(m_variableInstructions.begin(), m_variableInstructions.end(), [variable](const LLVMInstruction *ins) { - return ins->type == LLVMInstruction::Type::WriteVariable && ins->targetVariable == 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 - */ + varPtr.onStack = m_warp; + + if (varPtr.onStack) + varPtr.stackPtr = m_builder.CreateAlloca(m_valueDataType); } // Create list pointers @@ -104,6 +90,9 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro } } + reloadVariables(m_targetVariables); + reloadLists(); + // Create end branch m_endBranch = llvm::BasicBlock::Create(m_llvmCtx, "end", m_function); } @@ -313,19 +302,21 @@ void LLVMBuildUtils::syncVariables(llvm::Value *targetVariables) { // Copy stack variables to the actual variables for (auto &[var, varPtr] : m_variablePtrs) { - if (varPtr.onStack && varPtr.changed) + if (varPtr.onStack) createValueCopy(varPtr.stackPtr, getVariablePtr(targetVariables, var)); - - varPtr.changed = false; } } void LLVMBuildUtils::reloadVariables(llvm::Value *targetVariables) { - // Reset variables to use heap - for (auto &[var, varPtr] : m_variablePtrs) { - varPtr.onStack = false; - varPtr.changed = false; + // Load variables to stack + if (m_warp) { + for (auto &[var, varPtr] : m_variablePtrs) { + if (varPtr.onStack) { + llvm::Value *ptr = getVariablePtr(m_targetVariables, var); + createValueCopy(ptr, varPtr.stackPtr); + } + } } } diff --git a/src/engine/internal/llvm/llvmvariableptr.h b/src/engine/internal/llvm/llvmvariableptr.h index 2e5b3d01..1999f3db 100644 --- a/src/engine/internal/llvm/llvmvariableptr.h +++ b/src/engine/internal/llvm/llvmvariableptr.h @@ -21,7 +21,6 @@ struct LLVMVariablePtr llvm::Value *stackPtr = nullptr; llvm::Value *heapPtr = nullptr; bool onStack = false; - bool changed = false; }; } // namespace libscratchcpp From a2e4ee14b0bb3026527c08b14646c09bf06224f3 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 17 Aug 2025 20:25:44 +0200 Subject: [PATCH 02/42] Enable ValueType bitmasking --- include/scratchcpp/valuedata.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/include/scratchcpp/valuedata.h b/include/scratchcpp/valuedata.h index adfffa9f..b7073293 100644 --- a/include/scratchcpp/valuedata.h +++ b/include/scratchcpp/valuedata.h @@ -5,6 +5,7 @@ #include #include "global.h" +#include "enum_bitmask.h" namespace libscratchcpp { @@ -13,12 +14,15 @@ struct StringPtr; enum class LIBSCRATCHCPP_EXPORT ValueType { - Number = 0, - Bool = 1, - String = 2, - Pointer = 3 + Void = 0, + Number = 1 << 0, + Bool = 1 << 1, + String = 1 << 2, + Pointer = 1 << 3 }; +constexpr bool enable_enum_bitmask(ValueType); + extern "C" { /*! \brief The ValueData struct holds the data of Value. It's used in compiled Scratch code for better performance. */ From 269e011ae997c6c2314839169c59364ea5a3bea0 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 17 Aug 2025 20:29:01 +0200 Subject: [PATCH 03/42] Store list types locally in warp scripts --- .../internal/llvm/instructions/lists.cpp | 51 ++++++++++++++++++- src/engine/internal/llvm/instructions/lists.h | 14 ++++- src/engine/internal/llvm/llvmbuildutils.cpp | 17 +++++++ src/engine/internal/llvm/llvmbuildutils.h | 1 + src/engine/internal/llvm/llvmlistptr.h | 1 + 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 5c0927a5..c274582b 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -4,6 +4,7 @@ #include "../llvminstruction.h" #include "../llvmbuildutils.h" #include "../llvmconstantregister.h" +#include "../llvmcompilercontext.h" using namespace libscratchcpp; using namespace libscratchcpp::llvmins; @@ -72,6 +73,11 @@ LLVMInstruction *Lists::buildClearList(LLVMInstruction *ins) // Update size m_builder.CreateStore(m_builder.getInt64(0), listPtr.size); } + + if (listPtr.type) { + // Update type + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Void)), listPtr.type); + } } return ins->next; @@ -161,6 +167,7 @@ LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) m_builder.CreateStore(size, listPtr.size); } + createListTypeUpdate(listPtr, arg.second); return ins->next; } @@ -198,6 +205,7 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) m_builder.CreateStore(size, listPtr.size); } + createListTypeUpdate(listPtr, valueArg.second); m_builder.CreateBr(nextBlock); m_builder.SetInsertPoint(nextBlock); @@ -236,6 +244,7 @@ LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); llvm::Value *itemPtr = m_utils.getListItem(listPtr, index); m_utils.createValueStore(valueArg.second, itemPtr, listType, type); + createListTypeUpdate(listPtr, valueArg.second); m_builder.CreateBr(nextBlock); m_builder.SetInsertPoint(nextBlock); @@ -276,7 +285,29 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) llvm::Value *null = m_utils.createValue(static_cast(&nullReg)); index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); - ins->functionReturnReg->value = m_builder.CreateSelect(inRange, m_utils.getListItem(listPtr, index), null); + llvm::Value *itemPtr = m_builder.CreateSelect(inRange, m_utils.getListItem(listPtr, index), null); + + ins->functionReturnReg->value = itemPtr; + + if (listPtr.type) { + // Load the runtime list type information + llvm::Value *listTypeFlags = m_builder.CreateLoad(m_builder.getInt32Ty(), listPtr.type); + + // The result is an empty string if index is out of range + llvm::Value *withString = m_builder.CreateOr(listTypeFlags, m_builder.getInt32(static_cast(ValueType::String))); + listTypeFlags = m_builder.CreateSelect(inRange, listTypeFlags, withString); + + // Load the actual item type from ValueData + llvm::Value *itemTypePtr = m_builder.CreateStructGEP(m_utils.compilerCtx()->valueDataType(), itemPtr, 1); + llvm::Value *actualItemType = m_builder.CreateLoad(m_builder.getInt32Ty(), itemTypePtr); + + // Create assumption that the actual type is contained in the list type flags + llvm::Value *typeIsValid = m_builder.CreateICmpEQ(m_builder.CreateAnd(listTypeFlags, actualItemType), actualItemType); + + // Tell LLVM to assume this is true + llvm::Function *assumeIntrinsic = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::assume); + m_builder.CreateCall(assumeIntrinsic, typeIsValid); + } return ins->next; } @@ -316,3 +347,21 @@ LLVMInstruction *Lists::buildListContainsItem(LLVMInstruction *ins) return ins->next; } + +void Lists::createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue) +{ + if (listPtr.type) { + // Update type + llvm::Value *currentType = m_builder.CreateLoad(m_builder.getInt32Ty(), listPtr.type); + llvm::Value *newTypeFlag; + + if (newValue->isRawValue) + newTypeFlag = m_builder.getInt32(static_cast(m_utils.mapType(newValue->type()))); + else { + llvm::Value *typeField = m_builder.CreateStructGEP(m_utils.compilerCtx()->valueDataType(), newValue->value, 1); + newTypeFlag = m_builder.CreateLoad(m_builder.getInt32Ty(), typeField); + } + + m_builder.CreateStore(m_builder.CreateOr(currentType, newTypeFlag), listPtr.type); + } +} diff --git a/src/engine/internal/llvm/instructions/lists.h b/src/engine/internal/llvm/instructions/lists.h index 59aed889..a24e059d 100644 --- a/src/engine/internal/llvm/instructions/lists.h +++ b/src/engine/internal/llvm/instructions/lists.h @@ -4,7 +4,13 @@ #include "instructiongroup.h" -namespace libscratchcpp::llvmins +namespace libscratchcpp +{ + +class LLVMListPtr; +class LLVMRegister; + +namespace llvmins { class Lists : public InstructionGroup @@ -25,6 +31,10 @@ class Lists : public InstructionGroup LLVMInstruction *buildGetListSize(LLVMInstruction *ins); LLVMInstruction *buildGetListItemIndex(LLVMInstruction *ins); LLVMInstruction *buildListContainsItem(LLVMInstruction *ins); + + void createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue); }; -} // namespace libscratchcpp::llvmins +} // namespace llvmins + +} // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index e3c5891c..c314e8c5 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -21,6 +21,13 @@ static std::unordered_map TYPE_MAP = { { ValueType::Pointer, Compiler::StaticType::Pointer } }; +static std::unordered_map REVERSE_TYPE_MAP = { + { Compiler::StaticType::Number, ValueType::Number }, + { Compiler::StaticType::Bool, ValueType::Bool }, + { Compiler::StaticType::String, ValueType::String }, + { Compiler::StaticType::Pointer, ValueType::Pointer } +}; + LLVMBuildUtils::LLVMBuildUtils(LLVMCompilerContext *ctx, llvm::IRBuilder<> &builder, Compiler::CodeType codeType) : m_ctx(ctx), m_llvmCtx(*ctx->llvmCtx()), @@ -87,6 +94,10 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); m_builder.CreateStore(size, listPtr.size); + + // Store list type locally to leave static type analysis to LLVM + listPtr.type = m_builder.CreateAlloca(m_builder.getInt32Ty(), nullptr, list->name() + ".type"); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Number | ValueType::Bool | ValueType::String)), listPtr.type); } } @@ -393,6 +404,12 @@ Compiler::StaticType LLVMBuildUtils::mapType(ValueType type) return TYPE_MAP[type]; } +ValueType LLVMBuildUtils::mapType(Compiler::StaticType type) +{ + assert(REVERSE_TYPE_MAP.find(type) != REVERSE_TYPE_MAP.cend()); + return REVERSE_TYPE_MAP[type]; +} + bool LLVMBuildUtils::isSingleType(Compiler::StaticType type) { // Check if the type is a power of 2 (only one bit set) diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index 371f177c..7159275c 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -76,6 +76,7 @@ class LLVMBuildUtils static Compiler::StaticType optimizeRegisterType(const LLVMRegister *reg); static Compiler::StaticType mapType(ValueType type); + static ValueType mapType(Compiler::StaticType type); static bool isSingleType(Compiler::StaticType type); llvm::Value *addAlloca(llvm::Type *type); diff --git a/src/engine/internal/llvm/llvmlistptr.h b/src/engine/internal/llvm/llvmlistptr.h index 194737ff..92835502 100644 --- a/src/engine/internal/llvm/llvmlistptr.h +++ b/src/engine/internal/llvm/llvmlistptr.h @@ -23,6 +23,7 @@ struct LLVMListPtr llvm::Value *sizePtr = nullptr; llvm::Value *allocatedSizePtr = nullptr; llvm::Value *size = nullptr; + llvm::Value *type = nullptr; }; } // namespace libscratchcpp From 00b98b9b38dc06e3b251664b9447dd9c39a13e02 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 18 Aug 2025 10:10:34 +0200 Subject: [PATCH 04/42] Variable & list optimizations * always use stack variables * pass a custom destination type variable to createValueStore() * improve list type assumption --- .../internal/llvm/instructions/functions.cpp | 2 +- .../internal/llvm/instructions/lists.cpp | 71 ++++++++++------ src/engine/internal/llvm/instructions/lists.h | 2 + .../internal/llvm/instructions/procedures.cpp | 4 +- .../internal/llvm/instructions/variables.cpp | 4 +- src/engine/internal/llvm/llvmbuildutils.cpp | 82 +++++++++---------- src/engine/internal/llvm/llvmbuildutils.h | 11 ++- src/engine/internal/llvm/llvmregister.h | 1 + src/engine/internal/llvm/llvmvariableptr.h | 3 +- 9 files changed, 100 insertions(+), 80 deletions(-) diff --git a/src/engine/internal/llvm/instructions/functions.cpp b/src/engine/internal/llvm/instructions/functions.cpp index 94cc4bc0..a40a8c79 100644 --- a/src/engine/internal/llvm/instructions/functions.cpp +++ b/src/engine/internal/llvm/instructions/functions.cpp @@ -30,7 +30,7 @@ LLVMInstruction *Functions::buildFunctionCall(LLVMInstruction *ins) std::vector args; // Variables must be synchronized because the function can read them - m_utils.syncVariables(m_utils.targetVariables()); + m_utils.syncVariables(); // Add execution context arg if (ins->functionCtxArg) { diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index c274582b..ab6229a8 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -147,7 +147,7 @@ LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) // If there's enough space, use the allocated memory m_builder.SetInsertPoint(ifBlock); llvm::Value *itemPtr = m_utils.getListItem(listPtr, size); - m_utils.createValueStore(arg.second, itemPtr, type); + m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), arg.second, type); m_builder.CreateStore(m_builder.CreateAdd(size, m_builder.getInt64(1)), listPtr.sizePtr); // update size stored in *sizePtr m_builder.CreateBr(nextBlock); @@ -155,7 +155,7 @@ LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) m_builder.SetInsertPoint(elseBlock); itemPtr = m_builder.CreateCall(m_utils.functions().resolve_list_append_empty(), listPtr.ptr); // NOTE: Items created using appendEmpty() are always numbers - m_utils.createValueStore(arg.second, itemPtr, Compiler::StaticType::Number, type); + m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), arg.second, Compiler::StaticType::Number, type); m_builder.CreateBr(nextBlock); m_builder.SetInsertPoint(nextBlock); @@ -196,7 +196,7 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) m_builder.SetInsertPoint(insertBlock); index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); llvm::Value *itemPtr = m_builder.CreateCall(m_utils.functions().resolve_list_insert_empty(), { listPtr.ptr, index }); - m_utils.createValueStore(valueArg.second, itemPtr, type); + m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), valueArg.second, type); if (listPtr.size) { // Update size @@ -242,8 +242,13 @@ LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) // Replace m_builder.SetInsertPoint(replaceBlock); index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); + llvm::Value *itemPtr = m_utils.getListItem(listPtr, index); - m_utils.createValueStore(valueArg.second, itemPtr, listType, type); + llvm::Value *itemType = m_builder.CreateLoad(m_builder.getInt32Ty(), m_utils.getValueTypePtr(itemPtr)); + llvm::Value *typeVar = createListTypeVar(listPtr, itemType); + createListTypeAssumption(listPtr, itemType, typeVar); + + m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), valueArg.second, listType, type); createListTypeUpdate(listPtr, valueArg.second); m_builder.CreateBr(nextBlock); @@ -279,35 +284,21 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) llvm::Value *size = m_utils.getListSize(listPtr); size = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); llvm::Value *index = m_utils.castValue(arg.second, arg.first); - llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLT(index, size)); + llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLT(index, size), "inRange"); LLVMConstantRegister nullReg(Compiler::StaticType::String, ""); llvm::Value *null = m_utils.createValue(static_cast(&nullReg)); index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); + llvm::Value *itemPtr = m_builder.CreateSelect(inRange, m_utils.getListItem(listPtr, index), null); + llvm::Value *stringType = m_builder.getInt32(static_cast(ValueType::String)); + llvm::Value *type = m_builder.CreateSelect(inRange, m_builder.CreateLoad(m_builder.getInt32Ty(), m_utils.getValueTypePtr(itemPtr)), stringType); + llvm::Value *typeVar = createListTypeVar(listPtr, type); ins->functionReturnReg->value = itemPtr; - - if (listPtr.type) { - // Load the runtime list type information - llvm::Value *listTypeFlags = m_builder.CreateLoad(m_builder.getInt32Ty(), listPtr.type); - - // The result is an empty string if index is out of range - llvm::Value *withString = m_builder.CreateOr(listTypeFlags, m_builder.getInt32(static_cast(ValueType::String))); - listTypeFlags = m_builder.CreateSelect(inRange, listTypeFlags, withString); - - // Load the actual item type from ValueData - llvm::Value *itemTypePtr = m_builder.CreateStructGEP(m_utils.compilerCtx()->valueDataType(), itemPtr, 1); - llvm::Value *actualItemType = m_builder.CreateLoad(m_builder.getInt32Ty(), itemTypePtr); - - // Create assumption that the actual type is contained in the list type flags - llvm::Value *typeIsValid = m_builder.CreateICmpEQ(m_builder.CreateAnd(listTypeFlags, actualItemType), actualItemType); - - // Tell LLVM to assume this is true - llvm::Function *assumeIntrinsic = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::assume); - m_builder.CreateCall(assumeIntrinsic, typeIsValid); - } + ins->functionReturnReg->typeVar = typeVar; + createListTypeAssumption(listPtr, type, typeVar, inRange); return ins->next; } @@ -365,3 +356,33 @@ void Lists::createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister m_builder.CreateStore(m_builder.CreateOr(currentType, newTypeFlag), listPtr.type); } } + +llvm::Value *Lists::createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *itemType) +{ + llvm::Value *typeVar = m_utils.addAlloca(m_builder.getInt32Ty()); + m_builder.CreateStore(itemType, typeVar); + return typeVar; +} + +void Lists::createListTypeAssumption(const LLVMListPtr &listPtr, llvm::Value *itemType, llvm::Value *typeVar, llvm::Value *inRange) +{ + if (listPtr.type) { + llvm::Function *assumeIntrinsic = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::assume); + + // Load the runtime list type information + llvm::Value *listTypeFlags = m_builder.CreateLoad(m_builder.getInt32Ty(), listPtr.type); + + // Create assumption that the item type is contained in the list type flags + llvm::Value *typeIsValid = m_builder.CreateICmpEQ(m_builder.CreateAnd(listTypeFlags, itemType), itemType); + + if (inRange) { + llvm::Value *stringType = m_builder.getInt32(static_cast(ValueType::String)); + llvm::Value *canNotBeString = m_builder.CreateICmpNE(m_builder.CreateAnd(listTypeFlags, stringType), stringType); + llvm::Value *isString = m_builder.CreateICmpEQ(itemType, stringType); + llvm::Value *impossible = m_builder.CreateAnd(m_builder.CreateAnd(inRange, canNotBeString), isString); + typeIsValid = m_builder.CreateAnd(typeIsValid, m_builder.CreateNot(impossible)); + } + + m_builder.CreateCall(assumeIntrinsic, typeIsValid); + } +} diff --git a/src/engine/internal/llvm/instructions/lists.h b/src/engine/internal/llvm/instructions/lists.h index a24e059d..39f9cffd 100644 --- a/src/engine/internal/llvm/instructions/lists.h +++ b/src/engine/internal/llvm/instructions/lists.h @@ -33,6 +33,8 @@ class Lists : public InstructionGroup LLVMInstruction *buildListContainsItem(LLVMInstruction *ins); void createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue); + llvm::Value *createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *itemType); + void createListTypeAssumption(const LLVMListPtr &listPtr, llvm::Value *itemType, llvm::Value *typeVar, llvm::Value *inRange = nullptr); }; } // namespace llvmins diff --git a/src/engine/internal/llvm/instructions/procedures.cpp b/src/engine/internal/llvm/instructions/procedures.cpp index 5b6f9f75..f6022458 100644 --- a/src/engine/internal/llvm/instructions/procedures.cpp +++ b/src/engine/internal/llvm/instructions/procedures.cpp @@ -39,7 +39,7 @@ LLVMInstruction *Procedures::buildCallProcedure(LLVMInstruction *ins) assert(ins->procedurePrototype); assert(ins->args.size() == ins->procedurePrototype->argumentTypes().size()); m_utils.freeScopeHeap(); - m_utils.syncVariables(m_utils.targetVariables()); + m_utils.syncVariables(); std::string name = m_utils.scriptFunctionName(ins->procedurePrototype); llvm::FunctionType *type = m_utils.scriptFunctionType(ins->procedurePrototype); @@ -80,7 +80,7 @@ LLVMInstruction *Procedures::buildCallProcedure(LLVMInstruction *ins) m_builder.SetInsertPoint(nextBranch); } - m_utils.reloadVariables(m_utils.targetVariables()); + m_utils.reloadVariables(); m_utils.reloadLists(); return ins->next; } diff --git a/src/engine/internal/llvm/instructions/variables.cpp b/src/engine/internal/llvm/instructions/variables.cpp index 372aed25..8d9bee72 100644 --- a/src/engine/internal/llvm/instructions/variables.cpp +++ b/src/engine/internal/llvm/instructions/variables.cpp @@ -112,7 +112,7 @@ LLVMInstruction *Variables::buildWriteVariable(LLVMInstruction *ins) Compiler::StaticType argType = m_utils.optimizeRegisterType(arg.second); LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->targetVariable); - m_utils.createValueStore(arg.second, varPtr.onStack ? varPtr.stackPtr : varPtr.heapPtr, ins->targetType, argType); + m_utils.createValueStore(varPtr.stackPtr, m_utils.getValueTypePtr(varPtr.stackPtr), arg.second, ins->targetType, argType); return ins->next; } @@ -121,6 +121,6 @@ LLVMInstruction *Variables::buildReadVariable(LLVMInstruction *ins) assert(ins->args.size() == 0); LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->targetVariable); - ins->functionReturnReg->value = varPtr.onStack ? varPtr.stackPtr : varPtr.heapPtr; + ins->functionReturnReg->value = varPtr.stackPtr; return ins->next; } diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index c314e8c5..2fbaeff0 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -66,16 +66,10 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro // Create variable pointers for (auto &[var, varPtr] : m_variablePtrs) { llvm::Value *ptr = getVariablePtr(m_targetVariables, var); - - // Direct access varPtr.heapPtr = ptr; - // In warp scripts, all variables are created on the stack and synced later (seems to be faster) - // NOTE: Strings are NOT copied, only the pointer is copied - varPtr.onStack = m_warp; - - if (varPtr.onStack) - varPtr.stackPtr = m_builder.CreateAlloca(m_valueDataType); + // Store variables locally to enable optimizations + varPtr.stackPtr = m_builder.CreateAlloca(m_valueDataType); } // Create list pointers @@ -101,7 +95,7 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro } } - reloadVariables(m_targetVariables); + reloadVariables(); reloadLists(); // Create end branch @@ -116,7 +110,7 @@ void LLVMBuildUtils::end(LLVMInstruction *lastInstruction, LLVMRegister *lastCon m_builder.CreateBr(m_endBranch); m_builder.SetInsertPoint(m_endBranch); - syncVariables(m_targetVariables); + syncVariables(); // End the script function llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); @@ -309,25 +303,19 @@ LLVMListPtr &LLVMBuildUtils::listPtr(List *list) return m_listPtrs[list]; } -void LLVMBuildUtils::syncVariables(llvm::Value *targetVariables) +void LLVMBuildUtils::syncVariables() { // Copy stack variables to the actual variables - for (auto &[var, varPtr] : m_variablePtrs) { - if (varPtr.onStack) - createValueCopy(varPtr.stackPtr, getVariablePtr(targetVariables, var)); - } + for (auto &[var, varPtr] : m_variablePtrs) + createValueCopy(varPtr.stackPtr, getVariablePtr(m_targetVariables, var)); } -void LLVMBuildUtils::reloadVariables(llvm::Value *targetVariables) +void LLVMBuildUtils::reloadVariables() { // Load variables to stack - if (m_warp) { - for (auto &[var, varPtr] : m_variablePtrs) { - if (varPtr.onStack) { - llvm::Value *ptr = getVariablePtr(m_targetVariables, var); - createValueCopy(ptr, varPtr.stackPtr); - } - } + for (auto &[var, varPtr] : m_variablePtrs) { + llvm::Value *ptr = getVariablePtr(m_targetVariables, var); + createValueCopy(ptr, varPtr.stackPtr); } } @@ -449,7 +437,7 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t if (isSingleType(targetType)) { // Handle multiple source type cases with runtime switch - typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); + typePtr = getValueTypePtr(reg); loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); @@ -651,7 +639,7 @@ llvm::Value *LLVMBuildUtils::removeNaN(llvm::Value *num) return m_builder.CreateSelect(isNaN(num), llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)), num); } -void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType destType, Compiler::StaticType targetType) +void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTypePtr, LLVMRegister *reg, Compiler::StaticType destType, Compiler::StaticType targetType) { llvm::Value *targetPtr = nullptr; const bool targetTypeIsSingle = isSingleType(targetType); @@ -670,11 +658,10 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, C loadedTargetType = m_builder.getInt32(static_cast(mappedType)); else { assert(!reg->isConst()); - llvm::Value *targetTypePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); + llvm::Value *targetTypePtr = getValueTypePtr(reg); loadedTargetType = m_builder.CreateLoad(m_builder.getInt32Ty(), targetTypePtr); } - llvm::Value *destTypePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); llvm::Value *loadedDestType = m_builder.CreateLoad(m_builder.getInt32Ty(), destTypePtr); llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); @@ -723,8 +710,7 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, C // Write bool to number value directly and change type llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); m_builder.CreateStore(targetPtr, ptr); - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); - m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Bool)), typePtr); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Bool)), destTypePtr); m_builder.CreateBr(mergeBlock); } @@ -744,8 +730,7 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, C llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); llvm::Value *destStringPtr = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(false)); - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); - m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::String)), typePtr); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::String)), destTypePtr); m_builder.CreateStore(destStringPtr, ptr); m_builder.CreateCall(m_functions.resolve_string_assign(), { destStringPtr, targetPtr }); @@ -775,9 +760,8 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, C // Write number to bool value directly and change type llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); m_builder.CreateStore(targetPtr, ptr); - m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Number)), typePtr); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Number)), destTypePtr); m_builder.CreateBr(mergeBlock); } @@ -815,8 +799,7 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, C llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); llvm::Value *destStringPtr = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(false)); - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); - m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::String)), typePtr); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::String)), destTypePtr); m_builder.CreateStore(destStringPtr, ptr); m_builder.CreateCall(m_functions.resolve_string_assign(), { destStringPtr, targetPtr }); @@ -849,8 +832,7 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, C llvm::Value *destStringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); m_builder.CreateCall(m_functions.resolve_string_pool_free(), destStringPtr); m_builder.CreateStore(targetPtr, ptr); - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); - m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Number)), typePtr); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Number)), destTypePtr); m_builder.CreateBr(mergeBlock); } @@ -871,8 +853,7 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, C llvm::Value *destStringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); m_builder.CreateCall(m_functions.resolve_string_pool_free(), destStringPtr); m_builder.CreateStore(targetPtr, ptr); - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); - m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Bool)), typePtr); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Bool)), destTypePtr); m_builder.CreateBr(mergeBlock); } @@ -903,10 +884,23 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, C m_builder.SetInsertPoint(mergeBlock); } -void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType targetType) +void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTypePtr, LLVMRegister *reg, Compiler::StaticType targetType) { // Same as createValueStore(), but the destination type is unknown at compile time - createValueStore(reg, destPtr, Compiler::StaticType::Unknown, targetType); + createValueStore(destPtr, destTypePtr, reg, Compiler::StaticType::Unknown, targetType); +} + +llvm::Value *LLVMBuildUtils::getValueTypePtr(llvm::Value *value) +{ + return m_builder.CreateStructGEP(m_valueDataType, value, 1); +} + +llvm::Value *LLVMBuildUtils::getValueTypePtr(LLVMRegister *reg) +{ + if (reg->typeVar) + return reg->typeVar; + else + return getValueTypePtr(reg->value); } llvm::Value *LLVMBuildUtils::getListSize(const LLVMListPtr &listPtr) @@ -1018,7 +1012,7 @@ llvm::Value *LLVMBuildUtils::createValue(LLVMRegister *reg) } // Store type - llvm::Value *typeField = m_builder.CreateStructGEP(m_valueDataType, ret, 1); + llvm::Value *typeField = getValueTypePtr(ret); ValueType type = it->first; m_builder.CreateStore(m_builder.getInt32(static_cast(type)), typeField); @@ -1272,9 +1266,9 @@ void LLVMBuildUtils::createSuspend() m_builder.SetInsertPoint(suspendBranch); } - syncVariables(m_targetVariables); + syncVariables(); m_coroutine->createSuspend(); - reloadVariables(m_targetVariables); + reloadVariables(); reloadLists(); if (m_warpArg) { diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index 7159275c..3dfafe54 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -61,8 +61,8 @@ class LLVMBuildUtils LLVMVariablePtr &variablePtr(Variable *variable); LLVMListPtr &listPtr(List *list); - void syncVariables(llvm::Value *targetVariables); - void reloadVariables(llvm::Value *targetVariables); + void syncVariables(); + void reloadVariables(); void reloadLists(); void pushScopeLevel(); @@ -85,8 +85,11 @@ class LLVMBuildUtils llvm::Value *isNaN(llvm::Value *num); llvm::Value *removeNaN(llvm::Value *num); - void createValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType destType, Compiler::StaticType targetType); - void createValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType targetType); + void createValueStore(llvm::Value *destPtr, llvm::Value *destTypePtr, LLVMRegister *reg, Compiler::StaticType destType, Compiler::StaticType targetType); + void createValueStore(llvm::Value *destPtr, llvm::Value *destTypePtr, LLVMRegister *reg, Compiler::StaticType targetType); + + llvm::Value *getValueTypePtr(llvm::Value *value); + llvm::Value *getValueTypePtr(LLVMRegister *reg); llvm::Value *getListSize(const LLVMListPtr &listPtr); llvm::Value *getListItem(const LLVMListPtr &listPtr, llvm::Value *index); diff --git a/src/engine/internal/llvm/llvmregister.h b/src/engine/internal/llvm/llvmregister.h index 3bd90cc8..31840c34 100644 --- a/src/engine/internal/llvm/llvmregister.h +++ b/src/engine/internal/llvm/llvmregister.h @@ -31,6 +31,7 @@ struct LLVMRegister : public virtual CompilerValue } llvm::Value *value = nullptr; + llvm::Value *typeVar = nullptr; bool isRawValue = false; std::shared_ptr instruction; }; diff --git a/src/engine/internal/llvm/llvmvariableptr.h b/src/engine/internal/llvm/llvmvariableptr.h index 1999f3db..cae8653c 100644 --- a/src/engine/internal/llvm/llvmvariableptr.h +++ b/src/engine/internal/llvm/llvmvariableptr.h @@ -18,9 +18,8 @@ class LLVMInstruction; struct LLVMVariablePtr { - llvm::Value *stackPtr = nullptr; llvm::Value *heapPtr = nullptr; - bool onStack = false; + llvm::Value *stackPtr = nullptr; }; } // namespace libscratchcpp From 0a06c6c82f39e2723585e3f56d8819c38aeecfe7 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 18 Aug 2025 22:41:36 +0200 Subject: [PATCH 05/42] Optimize list type info --- .../internal/llvm/instructions/lists.cpp | 198 +++++++++++++++--- src/engine/internal/llvm/instructions/lists.h | 8 +- src/engine/internal/llvm/llvmbuildutils.cpp | 13 +- src/engine/internal/llvm/llvmlistptr.h | 5 +- 4 files changed, 182 insertions(+), 42 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index ab6229a8..0daf3d61 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -74,9 +74,11 @@ LLVMInstruction *Lists::buildClearList(LLVMInstruction *ins) m_builder.CreateStore(m_builder.getInt64(0), listPtr.size); } - if (listPtr.type) { - // Update type - m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Void)), listPtr.type); + if (listPtr.hasNumber && listPtr.hasBool && listPtr.hasString) { + // Reset type info + m_builder.CreateStore(m_builder.getInt1(false), listPtr.hasNumber); + m_builder.CreateStore(m_builder.getInt1(false), listPtr.hasBool); + m_builder.CreateStore(m_builder.getInt1(false), listPtr.hasString); } } @@ -167,7 +169,7 @@ LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) m_builder.CreateStore(size, listPtr.size); } - createListTypeUpdate(listPtr, arg.second); + createListTypeUpdate(listPtr, arg.second, type); return ins->next; } @@ -205,7 +207,7 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) m_builder.CreateStore(size, listPtr.size); } - createListTypeUpdate(listPtr, valueArg.second); + createListTypeUpdate(listPtr, valueArg.second, type); m_builder.CreateBr(nextBlock); m_builder.SetInsertPoint(nextBlock); @@ -244,12 +246,16 @@ LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); llvm::Value *itemPtr = m_utils.getListItem(listPtr, index); - llvm::Value *itemType = m_builder.CreateLoad(m_builder.getInt32Ty(), m_utils.getValueTypePtr(itemPtr)); - llvm::Value *typeVar = createListTypeVar(listPtr, itemType); - createListTypeAssumption(listPtr, itemType, typeVar); + // llvm::Value *typeVar = createListTypeVar(listPtr, itemPtr); + // createListTypeAssumption(listPtr, typeVar, ins->targetType); - m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), valueArg.second, listType, type); - createListTypeUpdate(listPtr, valueArg.second); + /*llvm::Value *typeVar = m_utils.addAlloca(m_builder.getInt32Ty()); + llvm::Value *t = m_builder.CreateLoad(m_builder.getInt32Ty(), m_utils.getValueTypePtr(itemPtr)); + m_builder.CreateStore(t, typeVar);*/ + llvm::Value *typeVar = m_utils.getValueTypePtr(itemPtr); + + m_utils.createValueStore(itemPtr, typeVar, valueArg.second, listType, type); + createListTypeUpdate(listPtr, valueArg.second, type); m_builder.CreateBr(nextBlock); m_builder.SetInsertPoint(nextBlock); @@ -292,13 +298,11 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); llvm::Value *itemPtr = m_builder.CreateSelect(inRange, m_utils.getListItem(listPtr, index), null); - llvm::Value *stringType = m_builder.getInt32(static_cast(ValueType::String)); - llvm::Value *type = m_builder.CreateSelect(inRange, m_builder.CreateLoad(m_builder.getInt32Ty(), m_utils.getValueTypePtr(itemPtr)), stringType); - llvm::Value *typeVar = createListTypeVar(listPtr, type); + llvm::Value *typeVar = createListTypeVar(listPtr, itemPtr, inRange); ins->functionReturnReg->value = itemPtr; ins->functionReturnReg->typeVar = typeVar; - createListTypeAssumption(listPtr, type, typeVar, inRange); + createListTypeAssumption(listPtr, typeVar, ins->targetType, inRange); return ins->next; } @@ -339,50 +343,176 @@ LLVMInstruction *Lists::buildListContainsItem(LLVMInstruction *ins) return ins->next; } -void Lists::createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue) +void Lists::createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue, Compiler::StaticType newValueType) { - if (listPtr.type) { - // Update type - llvm::Value *currentType = m_builder.CreateLoad(m_builder.getInt32Ty(), listPtr.type); - llvm::Value *newTypeFlag; + if (listPtr.hasNumber && listPtr.hasBool && listPtr.hasString) { + // Get the new type + llvm::Value *newType; if (newValue->isRawValue) - newTypeFlag = m_builder.getInt32(static_cast(m_utils.mapType(newValue->type()))); + newType = m_builder.getInt32(static_cast(m_utils.mapType(newValue->type()))); else { llvm::Value *typeField = m_builder.CreateStructGEP(m_utils.compilerCtx()->valueDataType(), newValue->value, 1); - newTypeFlag = m_builder.CreateLoad(m_builder.getInt32Ty(), typeField); + newType = m_builder.CreateLoad(m_builder.getInt32Ty(), typeField); + } + + // Set the appropriate type flag + llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "updateListType.default", m_utils.function()); + llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "updateListType.merge", m_utils.function()); + llvm::SwitchInst *sw = m_builder.CreateSwitch(newType, defaultBlock, 4); + + // Number case + if ((newValueType & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "updateListType.number", m_utils.function()); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); + m_builder.SetInsertPoint(numberBlock); + m_builder.CreateStore(m_builder.getInt1(true), listPtr.hasNumber); + m_builder.CreateBr(mergeBlock); + } + + // Bool case + if ((newValueType & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "updateListType.bool", m_utils.function()); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); + m_builder.SetInsertPoint(boolBlock); + m_builder.CreateStore(m_builder.getInt1(true), listPtr.hasBool); + m_builder.CreateBr(mergeBlock); } - m_builder.CreateStore(m_builder.CreateOr(currentType, newTypeFlag), listPtr.type); + // String case + if ((newValueType & Compiler::StaticType::String) == Compiler::StaticType::String) { + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "updateListType.string", m_utils.function()); + sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); + m_builder.SetInsertPoint(stringBlock); + m_builder.CreateStore(m_builder.getInt1(true), listPtr.hasString); + m_builder.CreateBr(mergeBlock); + } + + // Default case + m_builder.SetInsertPoint(defaultBlock); + m_builder.CreateBr(mergeBlock); + + m_builder.SetInsertPoint(mergeBlock); } } -llvm::Value *Lists::createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *itemType) +llvm::Value *Lists::createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *itemPtr, llvm::Value *inRange) { llvm::Value *typeVar = m_utils.addAlloca(m_builder.getInt32Ty()); - m_builder.CreateStore(itemType, typeVar); + llvm::BasicBlock *inRangeBlock = nullptr; + llvm::BasicBlock *outOfRangeBlock = nullptr; + llvm::BasicBlock *nextBlock = nullptr; + + if (inRange) { + inRangeBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); + outOfRangeBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); + nextBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); + + m_builder.CreateCondBr(inRange, inRangeBlock, outOfRangeBlock); + m_builder.SetInsertPoint(inRangeBlock); + } + + llvm::Value *type = m_builder.CreateLoad(m_builder.getInt32Ty(), m_utils.getValueTypePtr(itemPtr)); + m_builder.CreateStore(type, typeVar); + + if (inRange) { + m_builder.CreateBr(nextBlock); + m_builder.SetInsertPoint(outOfRangeBlock); + + llvm::Value *type = m_builder.getInt32(static_cast(ValueType::String)); + m_builder.CreateStore(type, typeVar); + m_builder.CreateBr(nextBlock); + + m_builder.SetInsertPoint(nextBlock); + } + return typeVar; } -void Lists::createListTypeAssumption(const LLVMListPtr &listPtr, llvm::Value *itemType, llvm::Value *typeVar, llvm::Value *inRange) +void Lists::createListTypeAssumption(const LLVMListPtr &listPtr, llvm::Value *typeVar, Compiler::StaticType staticType, llvm::Value *inRange) { - if (listPtr.type) { + if (listPtr.hasNumber && listPtr.hasBool && listPtr.hasString) { llvm::Function *assumeIntrinsic = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::assume); + // Load the compile time list type information + bool staticHasNumber = (staticType & Compiler::StaticType::Number) == Compiler::StaticType::Number; + bool staticHasBool = (staticType & Compiler::StaticType::Bool) == Compiler::StaticType::Bool; + bool staticHasString = (staticType & Compiler::StaticType::String) == Compiler::StaticType::String; + // Load the runtime list type information - llvm::Value *listTypeFlags = m_builder.CreateLoad(m_builder.getInt32Ty(), listPtr.type); + llvm::Value *hasNumber; - // Create assumption that the item type is contained in the list type flags - llvm::Value *typeIsValid = m_builder.CreateICmpEQ(m_builder.CreateAnd(listTypeFlags, itemType), itemType); + if (staticHasNumber) + hasNumber = m_builder.CreateLoad(m_builder.getInt1Ty(), listPtr.hasNumber); + else + hasNumber = m_builder.getInt1(false); + + llvm::Value *hasBool; + + if (staticHasBool) + hasBool = m_builder.CreateLoad(m_builder.getInt1Ty(), listPtr.hasBool); + else + hasBool = m_builder.getInt1(false); + + llvm::Value *hasString; + + if (staticHasString) + hasString = m_builder.CreateLoad(m_builder.getInt1Ty(), listPtr.hasString); + else + hasString = m_builder.getInt1(false); + + llvm::Value *type = m_builder.CreateLoad(m_builder.getInt32Ty(), typeVar); + + llvm::Value *numberType = m_builder.getInt32(static_cast(ValueType::Number)); + llvm::Value *boolType = m_builder.getInt32(static_cast(ValueType::Bool)); + llvm::Value *stringType = m_builder.getInt32(static_cast(ValueType::String)); + + // Number + llvm::BasicBlock *noNumberBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "listTypeAssumption.noNumber", m_utils.function()); + llvm::BasicBlock *afterNoNumberBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "listTypeAssumption.afterNoNumber", m_utils.function()); + m_builder.CreateCondBr(hasNumber, afterNoNumberBlock, noNumberBlock); + + m_builder.SetInsertPoint(noNumberBlock); + llvm::Value *isNotNumber = m_builder.CreateICmpNE(type, numberType); + m_builder.CreateCall(assumeIntrinsic, isNotNumber); + m_builder.CreateBr(afterNoNumberBlock); + + m_builder.SetInsertPoint(afterNoNumberBlock); + + // Bool + llvm::BasicBlock *noBoolBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "listTypeAssumption.noBool", m_utils.function()); + llvm::BasicBlock *afterNoBoolBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "listTypeAssumption.afterNoBool", m_utils.function()); + m_builder.CreateCondBr(hasBool, afterNoBoolBlock, noBoolBlock); + + m_builder.SetInsertPoint(noBoolBlock); + llvm::Value *isNotBool = m_builder.CreateICmpNE(type, boolType); + m_builder.CreateCall(assumeIntrinsic, isNotBool); + m_builder.CreateBr(afterNoBoolBlock); + + m_builder.SetInsertPoint(afterNoBoolBlock); + + // String + llvm::BasicBlock *noStringBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "listTypeAssumption.noString", m_utils.function()); + llvm::BasicBlock *afterNoStringBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "listTypeAssumption.afterNoString", m_utils.function()); + + if (inRange) + m_builder.CreateCondBr(m_builder.CreateAnd(m_builder.CreateNot(hasString), inRange), noStringBlock, afterNoStringBlock); + else + m_builder.CreateCondBr(hasString, afterNoStringBlock, noStringBlock); + + m_builder.SetInsertPoint(noStringBlock); + llvm::Value *isNotString = m_builder.CreateICmpNE(type, stringType); + m_builder.CreateCall(assumeIntrinsic, isNotString); + m_builder.CreateBr(afterNoStringBlock); + + m_builder.SetInsertPoint(afterNoStringBlock); if (inRange) { llvm::Value *stringType = m_builder.getInt32(static_cast(ValueType::String)); - llvm::Value *canNotBeString = m_builder.CreateICmpNE(m_builder.CreateAnd(listTypeFlags, stringType), stringType); - llvm::Value *isString = m_builder.CreateICmpEQ(itemType, stringType); + llvm::Value *canNotBeString = m_builder.CreateNot(hasString); + llvm::Value *isString = m_builder.CreateICmpEQ(type, stringType); llvm::Value *impossible = m_builder.CreateAnd(m_builder.CreateAnd(inRange, canNotBeString), isString); - typeIsValid = m_builder.CreateAnd(typeIsValid, m_builder.CreateNot(impossible)); + m_builder.CreateCall(assumeIntrinsic, m_builder.CreateNot(impossible)); } - - m_builder.CreateCall(assumeIntrinsic, typeIsValid); } } diff --git a/src/engine/internal/llvm/instructions/lists.h b/src/engine/internal/llvm/instructions/lists.h index 39f9cffd..259b0ba2 100644 --- a/src/engine/internal/llvm/instructions/lists.h +++ b/src/engine/internal/llvm/instructions/lists.h @@ -2,6 +2,8 @@ #pragma once +#include + #include "instructiongroup.h" namespace libscratchcpp @@ -32,9 +34,9 @@ class Lists : public InstructionGroup LLVMInstruction *buildGetListItemIndex(LLVMInstruction *ins); LLVMInstruction *buildListContainsItem(LLVMInstruction *ins); - void createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue); - llvm::Value *createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *itemType); - void createListTypeAssumption(const LLVMListPtr &listPtr, llvm::Value *itemType, llvm::Value *typeVar, llvm::Value *inRange = nullptr); + void createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue, Compiler::StaticType newValueType); + llvm::Value *createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *itemPtr, llvm::Value *inRange = nullptr); + void createListTypeAssumption(const LLVMListPtr &listPtr, llvm::Value *typeVar, Compiler::StaticType staticType, llvm::Value *inRange); }; } // namespace llvmins diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 2fbaeff0..7db02008 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -89,9 +89,10 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); m_builder.CreateStore(size, listPtr.size); - // Store list type locally to leave static type analysis to LLVM - listPtr.type = m_builder.CreateAlloca(m_builder.getInt32Ty(), nullptr, list->name() + ".type"); - m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Number | ValueType::Bool | ValueType::String)), listPtr.type); + // Store list type info locally to leave static type analysis to LLVM + listPtr.hasNumber = m_builder.CreateAlloca(m_builder.getInt1Ty(), nullptr, list->name() + ".hasNumber"); + listPtr.hasBool = m_builder.CreateAlloca(m_builder.getInt1Ty(), nullptr, list->name() + ".hasBool"); + listPtr.hasString = m_builder.CreateAlloca(m_builder.getInt1Ty(), nullptr, list->name() + ".hasString"); } } @@ -321,11 +322,15 @@ void LLVMBuildUtils::reloadVariables() void LLVMBuildUtils::reloadLists() { - // Load list size info + // Load list size and type info if (m_warp) { for (auto &[list, listPtr] : m_listPtrs) { llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); m_builder.CreateStore(size, listPtr.size); + + m_builder.CreateStore(m_builder.getInt1(true), listPtr.hasNumber); + m_builder.CreateStore(m_builder.getInt1(true), listPtr.hasBool); + m_builder.CreateStore(m_builder.getInt1(true), listPtr.hasString); } } } diff --git a/src/engine/internal/llvm/llvmlistptr.h b/src/engine/internal/llvm/llvmlistptr.h index 92835502..12f1ced7 100644 --- a/src/engine/internal/llvm/llvmlistptr.h +++ b/src/engine/internal/llvm/llvmlistptr.h @@ -23,7 +23,10 @@ struct LLVMListPtr llvm::Value *sizePtr = nullptr; llvm::Value *allocatedSizePtr = nullptr; llvm::Value *size = nullptr; - llvm::Value *type = nullptr; + + llvm::Value *hasNumber = nullptr; + llvm::Value *hasBool = nullptr; + llvm::Value *hasString = nullptr; }; } // namespace libscratchcpp From f116437a18444fd9c403722155a11eceabb4d2be Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 18 Aug 2025 23:59:41 +0200 Subject: [PATCH 06/42] Use PHI node in get list item instruction --- .../internal/llvm/instructions/lists.cpp | 63 +++++++++---------- src/engine/internal/llvm/instructions/lists.h | 2 +- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 0daf3d61..7a763e88 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -290,17 +290,40 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) llvm::Value *size = m_utils.getListSize(listPtr); size = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); llvm::Value *index = m_utils.castValue(arg.second, arg.first); - llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLT(index, size), "inRange"); + llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLT(index, size), "getListItem.indexInRange"); - LLVMConstantRegister nullReg(Compiler::StaticType::String, ""); - llvm::Value *null = m_utils.createValue(static_cast(&nullReg)); + llvm::BasicBlock *inRangeBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "getListItem.inRange", m_utils.function()); + llvm::BasicBlock *outOfRangeBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "getListItem.outOfRange", m_utils.function()); + llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "getListItem.next", m_utils.function()); + m_builder.CreateCondBr(inRange, inRangeBlock, outOfRangeBlock); + // In range + m_builder.SetInsertPoint(inRangeBlock); index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); + llvm::Value *itemPtr = m_utils.getListItem(listPtr, index); + llvm::Value *itemType = m_builder.CreateLoad(m_builder.getInt32Ty(), m_utils.getValueTypePtr(itemPtr)); + m_builder.CreateBr(nextBlock); + + // Out of range + m_builder.SetInsertPoint(outOfRangeBlock); + LLVMConstantRegister emptyStringReg(Compiler::StaticType::String, ""); + llvm::Value *emptyString = m_utils.createValue(static_cast(&emptyStringReg)); + llvm::Value *stringType = m_builder.getInt32(static_cast(ValueType::String)); + m_builder.CreateBr(nextBlock); - llvm::Value *itemPtr = m_builder.CreateSelect(inRange, m_utils.getListItem(listPtr, index), null); - llvm::Value *typeVar = createListTypeVar(listPtr, itemPtr, inRange); + m_builder.SetInsertPoint(nextBlock); + + // Result + llvm::PHINode *result = m_builder.CreatePHI(itemPtr->getType(), 2, "getListItem.result"); + result->addIncoming(itemPtr, inRangeBlock); + result->addIncoming(emptyString, outOfRangeBlock); - ins->functionReturnReg->value = itemPtr; + llvm::PHINode *itemTypeResult = m_builder.CreatePHI(m_builder.getInt32Ty(), 2, "getListItem.itemType"); + itemTypeResult->addIncoming(itemType, inRangeBlock); + itemTypeResult->addIncoming(stringType, outOfRangeBlock); + + llvm::Value *typeVar = createListTypeVar(listPtr, itemTypeResult); + ins->functionReturnReg->value = result; ins->functionReturnReg->typeVar = typeVar; createListTypeAssumption(listPtr, typeVar, ins->targetType, inRange); @@ -396,36 +419,10 @@ void Lists::createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister } } -llvm::Value *Lists::createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *itemPtr, llvm::Value *inRange) +llvm::Value *Lists::createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *type) { llvm::Value *typeVar = m_utils.addAlloca(m_builder.getInt32Ty()); - llvm::BasicBlock *inRangeBlock = nullptr; - llvm::BasicBlock *outOfRangeBlock = nullptr; - llvm::BasicBlock *nextBlock = nullptr; - - if (inRange) { - inRangeBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); - outOfRangeBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); - nextBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); - - m_builder.CreateCondBr(inRange, inRangeBlock, outOfRangeBlock); - m_builder.SetInsertPoint(inRangeBlock); - } - - llvm::Value *type = m_builder.CreateLoad(m_builder.getInt32Ty(), m_utils.getValueTypePtr(itemPtr)); m_builder.CreateStore(type, typeVar); - - if (inRange) { - m_builder.CreateBr(nextBlock); - m_builder.SetInsertPoint(outOfRangeBlock); - - llvm::Value *type = m_builder.getInt32(static_cast(ValueType::String)); - m_builder.CreateStore(type, typeVar); - m_builder.CreateBr(nextBlock); - - m_builder.SetInsertPoint(nextBlock); - } - return typeVar; } diff --git a/src/engine/internal/llvm/instructions/lists.h b/src/engine/internal/llvm/instructions/lists.h index 259b0ba2..4f092065 100644 --- a/src/engine/internal/llvm/instructions/lists.h +++ b/src/engine/internal/llvm/instructions/lists.h @@ -35,7 +35,7 @@ class Lists : public InstructionGroup LLVMInstruction *buildListContainsItem(LLVMInstruction *ins); void createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue, Compiler::StaticType newValueType); - llvm::Value *createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *itemPtr, llvm::Value *inRange = nullptr); + llvm::Value *createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *type); void createListTypeAssumption(const LLVMListPtr &listPtr, llvm::Value *typeVar, Compiler::StaticType staticType, llvm::Value *inRange); }; From c848360ecfd9579fe5c19e761dc13971698735b0 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:13:14 +0200 Subject: [PATCH 07/42] Fix new type in list type update --- src/engine/internal/llvm/instructions/lists.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 7a763e88..40d96360 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -373,7 +373,7 @@ void Lists::createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister llvm::Value *newType; if (newValue->isRawValue) - newType = m_builder.getInt32(static_cast(m_utils.mapType(newValue->type()))); + newType = m_builder.getInt32(static_cast(m_utils.mapType(newValueType))); else { llvm::Value *typeField = m_builder.CreateStructGEP(m_utils.compilerCtx()->valueDataType(), newValue->value, 1); newType = m_builder.CreateLoad(m_builder.getInt32Ty(), typeField); From 01006dc6d7198e5481c72cd2588cd3fd37ae6e3b Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:15:38 +0200 Subject: [PATCH 08/42] Optimize list index range check --- .../internal/llvm/instructions/lists.cpp | 70 +++++++++++-------- src/engine/internal/llvm/instructions/lists.h | 3 + 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 40d96360..1b4941fd 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -100,19 +100,17 @@ LLVMInstruction *Lists::buildRemoveListItem(LLVMInstruction *ins) LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); // Range check - llvm::Value *min = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); - llvm::Value *size = m_utils.getListSize(listPtr); - size = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); - llvm::Value *index = m_utils.castValue(arg.second, arg.first); - llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLT(index, size)); + llvm::Value *indexDouble = m_utils.castValue(arg.second, arg.first); + llvm::Value *indexInt = getIndex(listPtr, indexDouble); + llvm::Value *inRange = createSizeRangeCheck(listPtr, indexInt, "removeListItem.indexInRange"); + llvm::BasicBlock *removeBlock = llvm::BasicBlock::Create(llvmCtx, "", function); llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(llvmCtx, "", function); m_builder.CreateCondBr(inRange, removeBlock, nextBlock); // Remove m_builder.SetInsertPoint(removeBlock); - index = m_builder.CreateFPToUI(m_utils.castValue(arg.second, arg.first), m_builder.getInt64Ty()); - m_builder.CreateCall(m_utils.functions().resolve_list_remove(), { listPtr.ptr, index }); + m_builder.CreateCall(m_utils.functions().resolve_list_remove(), { listPtr.ptr, indexInt }); if (listPtr.size) { // Update size @@ -185,19 +183,17 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); // Range check - llvm::Value *size = m_utils.getListSize(listPtr); - llvm::Value *min = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); - size = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); - llvm::Value *index = m_utils.castValue(indexArg.second, indexArg.first); - llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLE(index, size)); + llvm::Value *indexDouble = m_utils.castValue(indexArg.second, indexArg.first); + llvm::Value *indexInt = getIndex(listPtr, indexDouble); + llvm::Value *inRange = createSizeRangeCheck(listPtr, indexInt, "insertToList.indexInRange"); + llvm::BasicBlock *insertBlock = llvm::BasicBlock::Create(llvmCtx, "", function); llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(llvmCtx, "", function); m_builder.CreateCondBr(inRange, insertBlock, nextBlock); // Insert m_builder.SetInsertPoint(insertBlock); - index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); - llvm::Value *itemPtr = m_builder.CreateCall(m_utils.functions().resolve_list_insert_empty(), { listPtr.ptr, index }); + llvm::Value *itemPtr = m_builder.CreateCall(m_utils.functions().resolve_list_insert_empty(), { listPtr.ptr, indexInt }); m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), valueArg.second, type); if (listPtr.size) { @@ -232,20 +228,18 @@ LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) Compiler::StaticType listType = ins->targetType; // Range check - llvm::Value *min = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); - llvm::Value *size = m_utils.getListSize(listPtr); - size = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); - llvm::Value *index = m_utils.castValue(indexArg.second, indexArg.first); - llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLT(index, size)); + llvm::Value *indexDouble = m_utils.castValue(indexArg.second, indexArg.first); + llvm::Value *indexInt = getIndex(listPtr, indexDouble); + llvm::Value *inRange = createSizeRangeCheck(listPtr, indexInt, "listReplace.indexInRange"); + llvm::BasicBlock *replaceBlock = llvm::BasicBlock::Create(llvmCtx, "", function); llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(llvmCtx, "", function); m_builder.CreateCondBr(inRange, replaceBlock, nextBlock); // Replace m_builder.SetInsertPoint(replaceBlock); - index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); - llvm::Value *itemPtr = m_utils.getListItem(listPtr, index); + llvm::Value *itemPtr = m_utils.getListItem(listPtr, indexInt); // llvm::Value *typeVar = createListTypeVar(listPtr, itemPtr); // createListTypeAssumption(listPtr, typeVar, ins->targetType); @@ -275,6 +269,9 @@ LLVMInstruction *Lists::buildGetListContents(LLVMInstruction *ins) LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) { + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Function *function = m_utils.function(); + // Return empty string for empty lists if (ins->targetType == Compiler::StaticType::Void) { LLVMConstantRegister nullReg(Compiler::StaticType::String, ""); @@ -286,21 +283,19 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) const auto &arg = ins->args[0]; LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); - llvm::Value *min = llvm::ConstantFP::get(m_utils.llvmCtx(), llvm::APFloat(0.0)); - llvm::Value *size = m_utils.getListSize(listPtr); - size = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); - llvm::Value *index = m_utils.castValue(arg.second, arg.first); - llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLT(index, size), "getListItem.indexInRange"); + // Range check + llvm::Value *indexDouble = m_utils.castValue(arg.second, arg.first); + llvm::Value *indexInt = getIndex(listPtr, indexDouble); + llvm::Value *inRange = createSizeRangeCheck(listPtr, indexInt, "getListItem.indexInRange"); - llvm::BasicBlock *inRangeBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "getListItem.inRange", m_utils.function()); - llvm::BasicBlock *outOfRangeBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "getListItem.outOfRange", m_utils.function()); - llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "getListItem.next", m_utils.function()); + llvm::BasicBlock *inRangeBlock = llvm::BasicBlock::Create(llvmCtx, "getListItem.inRange", function); + llvm::BasicBlock *outOfRangeBlock = llvm::BasicBlock::Create(llvmCtx, "getListItem.outOfRange", function); + llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(llvmCtx, "getListItem.next", function); m_builder.CreateCondBr(inRange, inRangeBlock, outOfRangeBlock); // In range m_builder.SetInsertPoint(inRangeBlock); - index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); - llvm::Value *itemPtr = m_utils.getListItem(listPtr, index); + llvm::Value *itemPtr = m_utils.getListItem(listPtr, indexInt); llvm::Value *itemType = m_builder.CreateLoad(m_builder.getInt32Ty(), m_utils.getValueTypePtr(itemPtr)); m_builder.CreateBr(nextBlock); @@ -366,6 +361,19 @@ LLVMInstruction *Lists::buildListContainsItem(LLVMInstruction *ins) return ins->next; } +llvm::Value *Lists::getIndex(const LLVMListPtr &listPtr, llvm::Value *indexDouble) +{ + llvm::Value *zero = llvm::ConstantFP::get(m_utils.llvmCtx(), llvm::APFloat(0.0)); + llvm::Value *isNegative = m_builder.CreateFCmpOLT(indexDouble, zero, "listIndex.isNegative"); + return m_builder.CreateSelect(isNegative, llvm::ConstantInt::get(m_builder.getInt64Ty(), INT64_MAX), m_builder.CreateFPToUI(indexDouble, m_builder.getInt64Ty(), "listIndex.int")); +} + +llvm::Value *Lists::createSizeRangeCheck(const LLVMListPtr &listPtr, llvm::Value *indexInt, const std::string &name) +{ + llvm::Value *size = m_utils.getListSize(listPtr); + return m_builder.CreateICmpULT(indexInt, size, name); +} + void Lists::createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue, Compiler::StaticType newValueType) { if (listPtr.hasNumber && listPtr.hasBool && listPtr.hasString) { diff --git a/src/engine/internal/llvm/instructions/lists.h b/src/engine/internal/llvm/instructions/lists.h index 4f092065..e97e9a56 100644 --- a/src/engine/internal/llvm/instructions/lists.h +++ b/src/engine/internal/llvm/instructions/lists.h @@ -34,6 +34,9 @@ class Lists : public InstructionGroup LLVMInstruction *buildGetListItemIndex(LLVMInstruction *ins); LLVMInstruction *buildListContainsItem(LLVMInstruction *ins); + llvm::Value *getIndex(const LLVMListPtr &listPtr, llvm::Value *indexDouble); + llvm::Value *createSizeRangeCheck(const LLVMListPtr &listPtr, llvm::Value *indexInt, const std::string &name); + void createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue, Compiler::StaticType newValueType); llvm::Value *createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *type); void createListTypeAssumption(const LLVMListPtr &listPtr, llvm::Value *typeVar, Compiler::StaticType staticType, llvm::Value *inRange); From e5f03c3c7a7657234ebe0e0ccbf9a79c72f36427 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:16:06 +0200 Subject: [PATCH 09/42] Optimize list type update --- .../internal/llvm/instructions/lists.cpp | 63 +++++++++---------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 1b4941fd..a62e1abd 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -387,43 +387,40 @@ void Lists::createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister newType = m_builder.CreateLoad(m_builder.getInt32Ty(), typeField); } - // Set the appropriate type flag - llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "updateListType.default", m_utils.function()); - llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "updateListType.merge", m_utils.function()); - llvm::SwitchInst *sw = m_builder.CreateSwitch(newType, defaultBlock, 4); - - // Number case - if ((newValueType & Compiler::StaticType::Number) == Compiler::StaticType::Number) { - llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "updateListType.number", m_utils.function()); - sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); - m_builder.SetInsertPoint(numberBlock); - m_builder.CreateStore(m_builder.getInt1(true), listPtr.hasNumber); - m_builder.CreateBr(mergeBlock); - } + bool staticHasNumber = (newValueType & Compiler::StaticType::Number) == Compiler::StaticType::Number; + bool staticHasBool = (newValueType & Compiler::StaticType::Bool) == Compiler::StaticType::Bool; + bool staticHasString = (newValueType & Compiler::StaticType::String) == Compiler::StaticType::String; - // Bool case - if ((newValueType & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { - llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "updateListType.bool", m_utils.function()); - sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); - m_builder.SetInsertPoint(boolBlock); - m_builder.CreateStore(m_builder.getInt1(true), listPtr.hasBool); - m_builder.CreateBr(mergeBlock); - } + llvm::Value *isNumber; - // String case - if ((newValueType & Compiler::StaticType::String) == Compiler::StaticType::String) { - llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "updateListType.string", m_utils.function()); - sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); - m_builder.SetInsertPoint(stringBlock); - m_builder.CreateStore(m_builder.getInt1(true), listPtr.hasString); - m_builder.CreateBr(mergeBlock); - } + if (staticHasNumber) + isNumber = m_builder.CreateICmpEQ(newType, m_builder.getInt32(static_cast(ValueType::Number))); + else + isNumber = m_builder.getInt1(false); + + llvm::Value *isBool; + + if (staticHasBool) + isBool = m_builder.CreateICmpEQ(newType, m_builder.getInt32(static_cast(ValueType::Bool))); + else + isBool = m_builder.getInt1(false); + + llvm::Value *isString; + + if (staticHasString) + isString = m_builder.CreateICmpEQ(newType, m_builder.getInt32(static_cast(ValueType::String))); + else + isString = m_builder.getInt1(false); + + // Update flags + llvm::Value *previous = m_builder.CreateLoad(m_builder.getInt1Ty(), listPtr.hasNumber); + m_builder.CreateStore(m_builder.CreateOr(previous, isNumber), listPtr.hasNumber); - // Default case - m_builder.SetInsertPoint(defaultBlock); - m_builder.CreateBr(mergeBlock); + previous = m_builder.CreateLoad(m_builder.getInt1Ty(), listPtr.hasBool); + m_builder.CreateStore(m_builder.CreateOr(previous, isBool), listPtr.hasBool); - m_builder.SetInsertPoint(mergeBlock); + previous = m_builder.CreateLoad(m_builder.getInt1Ty(), listPtr.hasString); + m_builder.CreateStore(m_builder.CreateOr(previous, isString), listPtr.hasString); } } From 878dfe0d96115eead2ca0d4d4a855d6465b16cb4 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:17:02 +0200 Subject: [PATCH 10/42] Do not assume appendEmpty() returns numbers --- src/engine/internal/llvm/instructions/lists.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index a62e1abd..87e826be 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -154,8 +154,7 @@ LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) // Otherwise call appendEmpty() m_builder.SetInsertPoint(elseBlock); itemPtr = m_builder.CreateCall(m_utils.functions().resolve_list_append_empty(), listPtr.ptr); - // NOTE: Items created using appendEmpty() are always numbers - m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), arg.second, Compiler::StaticType::Number, type); + m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), arg.second, type); m_builder.CreateBr(nextBlock); m_builder.SetInsertPoint(nextBlock); From 13f7fae976f4bb2ba83253f4bbf7cdc85a0d13b4 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 19 Aug 2025 17:17:25 +0200 Subject: [PATCH 11/42] LLVMBuildUtils: Add missing checks for runtime list info --- src/engine/internal/llvm/llvmbuildutils.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 7db02008..7026b6b8 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -323,11 +323,13 @@ void LLVMBuildUtils::reloadVariables() void LLVMBuildUtils::reloadLists() { // Load list size and type info - if (m_warp) { - for (auto &[list, listPtr] : m_listPtrs) { + for (auto &[list, listPtr] : m_listPtrs) { + if (listPtr.size) { llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); m_builder.CreateStore(size, listPtr.size); + } + if (listPtr.hasNumber && listPtr.hasBool && listPtr.hasString) { m_builder.CreateStore(m_builder.getInt1(true), listPtr.hasNumber); m_builder.CreateStore(m_builder.getInt1(true), listPtr.hasBool); m_builder.CreateStore(m_builder.getInt1(true), listPtr.hasString); From 492ccb0aade76021ce443e6fe96ec2a334eaf3a0 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 19 Aug 2025 18:00:36 +0200 Subject: [PATCH 12/42] Fix list insert index range check --- src/engine/internal/llvm/instructions/lists.cpp | 10 +++++++--- src/engine/internal/llvm/instructions/lists.h | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 87e826be..c25dff59 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -184,7 +184,7 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) // Range check llvm::Value *indexDouble = m_utils.castValue(indexArg.second, indexArg.first); llvm::Value *indexInt = getIndex(listPtr, indexDouble); - llvm::Value *inRange = createSizeRangeCheck(listPtr, indexInt, "insertToList.indexInRange"); + llvm::Value *inRange = createSizeRangeCheck(listPtr, indexInt, "insertToList.indexInRange", true); llvm::BasicBlock *insertBlock = llvm::BasicBlock::Create(llvmCtx, "", function); llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(llvmCtx, "", function); @@ -367,10 +367,14 @@ llvm::Value *Lists::getIndex(const LLVMListPtr &listPtr, llvm::Value *indexDoubl return m_builder.CreateSelect(isNegative, llvm::ConstantInt::get(m_builder.getInt64Ty(), INT64_MAX), m_builder.CreateFPToUI(indexDouble, m_builder.getInt64Ty(), "listIndex.int")); } -llvm::Value *Lists::createSizeRangeCheck(const LLVMListPtr &listPtr, llvm::Value *indexInt, const std::string &name) +llvm::Value *Lists::createSizeRangeCheck(const LLVMListPtr &listPtr, llvm::Value *indexInt, const std::string &name, bool includeSize) { llvm::Value *size = m_utils.getListSize(listPtr); - return m_builder.CreateICmpULT(indexInt, size, name); + + if (includeSize) + return m_builder.CreateICmpULE(indexInt, size, name); + else + return m_builder.CreateICmpULT(indexInt, size, name); } void Lists::createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue, Compiler::StaticType newValueType) diff --git a/src/engine/internal/llvm/instructions/lists.h b/src/engine/internal/llvm/instructions/lists.h index e97e9a56..4388a8d5 100644 --- a/src/engine/internal/llvm/instructions/lists.h +++ b/src/engine/internal/llvm/instructions/lists.h @@ -35,7 +35,7 @@ class Lists : public InstructionGroup LLVMInstruction *buildListContainsItem(LLVMInstruction *ins); llvm::Value *getIndex(const LLVMListPtr &listPtr, llvm::Value *indexDouble); - llvm::Value *createSizeRangeCheck(const LLVMListPtr &listPtr, llvm::Value *indexInt, const std::string &name); + llvm::Value *createSizeRangeCheck(const LLVMListPtr &listPtr, llvm::Value *indexInt, const std::string &name, bool includeSize = false); void createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue, Compiler::StaticType newValueType); llvm::Value *createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *type); From a47a612546fa748a144ea0504de59d6f77539121 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 19 Aug 2025 19:53:33 +0200 Subject: [PATCH 13/42] Clean up Lists::getIndex() --- src/engine/internal/llvm/instructions/lists.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index c25dff59..12a2e97b 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -364,7 +364,9 @@ llvm::Value *Lists::getIndex(const LLVMListPtr &listPtr, llvm::Value *indexDoubl { llvm::Value *zero = llvm::ConstantFP::get(m_utils.llvmCtx(), llvm::APFloat(0.0)); llvm::Value *isNegative = m_builder.CreateFCmpOLT(indexDouble, zero, "listIndex.isNegative"); - return m_builder.CreateSelect(isNegative, llvm::ConstantInt::get(m_builder.getInt64Ty(), INT64_MAX), m_builder.CreateFPToUI(indexDouble, m_builder.getInt64Ty(), "listIndex.int")); + llvm::Value *intMax = llvm::ConstantInt::get(m_builder.getInt64Ty(), INT64_MAX); + llvm::Value *intIndex = m_builder.CreateFPToUI(indexDouble, m_builder.getInt64Ty(), "listIndex.int"); + return m_builder.CreateSelect(isNegative, intMax, intIndex); } llvm::Value *Lists::createSizeRangeCheck(const LLVMListPtr &listPtr, llvm::Value *indexInt, const std::string &name, bool includeSize) From 04c305a6b3f163f67ad9be764d3094b0f453a2f4 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 19 Aug 2025 19:53:54 +0200 Subject: [PATCH 14/42] Rewrite list type assumptions --- .../internal/llvm/instructions/lists.cpp | 82 ++++++++++--------- 1 file changed, 45 insertions(+), 37 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 12a2e97b..0260e2a9 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -474,52 +474,60 @@ void Lists::createListTypeAssumption(const LLVMListPtr &listPtr, llvm::Value *ty llvm::Value *boolType = m_builder.getInt32(static_cast(ValueType::Bool)); llvm::Value *stringType = m_builder.getInt32(static_cast(ValueType::String)); - // Number - llvm::BasicBlock *noNumberBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "listTypeAssumption.noNumber", m_utils.function()); - llvm::BasicBlock *afterNoNumberBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "listTypeAssumption.afterNoNumber", m_utils.function()); - m_builder.CreateCondBr(hasNumber, afterNoNumberBlock, noNumberBlock); + // Create assumptions + llvm::BasicBlock *outOfRangeBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "outOfRange", m_utils.function()); + llvm::BasicBlock *inRangeBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "inRange", m_utils.function()); + llvm::BasicBlock *afterBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "afterAssume", m_utils.function()); - m_builder.SetInsertPoint(noNumberBlock); - llvm::Value *isNotNumber = m_builder.CreateICmpNE(type, numberType); - m_builder.CreateCall(assumeIntrinsic, isNotNumber); - m_builder.CreateBr(afterNoNumberBlock); + m_builder.CreateCondBr(inRange, inRangeBlock, outOfRangeBlock); - m_builder.SetInsertPoint(afterNoNumberBlock); + // In-range assumptions + m_builder.SetInsertPoint(inRangeBlock); - // Bool - llvm::BasicBlock *noBoolBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "listTypeAssumption.noBool", m_utils.function()); - llvm::BasicBlock *afterNoBoolBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "listTypeAssumption.afterNoBool", m_utils.function()); - m_builder.CreateCondBr(hasBool, afterNoBoolBlock, noBoolBlock); + auto assume = [&](llvm::Value *cond) { m_builder.CreateCall(assumeIntrinsic, cond); }; - m_builder.SetInsertPoint(noBoolBlock); - llvm::Value *isNotBool = m_builder.CreateICmpNE(type, boolType); - m_builder.CreateCall(assumeIntrinsic, isNotBool); - m_builder.CreateBr(afterNoBoolBlock); + llvm::Value *notNumber = m_builder.CreateNot(hasNumber); + llvm::Value *notBool = m_builder.CreateNot(hasBool); + llvm::Value *notString = m_builder.CreateNot(hasString); - m_builder.SetInsertPoint(afterNoBoolBlock); + // if (!hasBool && !hasString) type == Number + llvm::Value *cond1 = m_builder.CreateAnd(notBool, notString); + llvm::Value *assume1 = m_builder.CreateICmpEQ(type, numberType); + assume(m_builder.CreateSelect(cond1, assume1, m_builder.getInt1(true))); - // String - llvm::BasicBlock *noStringBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "listTypeAssumption.noString", m_utils.function()); - llvm::BasicBlock *afterNoStringBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "listTypeAssumption.afterNoString", m_utils.function()); + // else if (!hasNumber && !hasString) type == Bool + llvm::Value *cond2 = m_builder.CreateAnd(notNumber, notString); + llvm::Value *assume2 = m_builder.CreateICmpEQ(type, boolType); + assume(m_builder.CreateSelect(cond2, assume2, m_builder.getInt1(true))); - if (inRange) - m_builder.CreateCondBr(m_builder.CreateAnd(m_builder.CreateNot(hasString), inRange), noStringBlock, afterNoStringBlock); - else - m_builder.CreateCondBr(hasString, afterNoStringBlock, noStringBlock); + // else if (!hasNumber && !hasBool) type == String + llvm::Value *cond3 = m_builder.CreateAnd(notNumber, notBool); + llvm::Value *assume3 = m_builder.CreateICmpEQ(type, stringType); + assume(m_builder.CreateSelect(cond3, assume3, m_builder.getInt1(true))); - m_builder.SetInsertPoint(noStringBlock); - llvm::Value *isNotString = m_builder.CreateICmpNE(type, stringType); - m_builder.CreateCall(assumeIntrinsic, isNotString); - m_builder.CreateBr(afterNoStringBlock); + // else if (!hasBool) type == Number || type == String + llvm::Value *cond4 = notBool; + llvm::Value *assume4 = m_builder.CreateOr(m_builder.CreateICmpEQ(type, numberType), m_builder.CreateICmpEQ(type, stringType)); + assume(m_builder.CreateSelect(cond4, assume4, m_builder.getInt1(true))); - m_builder.SetInsertPoint(afterNoStringBlock); + // else if (!hasNumber) type == Bool || type == String + llvm::Value *cond5 = notNumber; + llvm::Value *assume5 = m_builder.CreateOr(m_builder.CreateICmpEQ(type, boolType), m_builder.CreateICmpEQ(type, stringType)); + assume(m_builder.CreateSelect(cond5, assume5, m_builder.getInt1(true))); - if (inRange) { - llvm::Value *stringType = m_builder.getInt32(static_cast(ValueType::String)); - llvm::Value *canNotBeString = m_builder.CreateNot(hasString); - llvm::Value *isString = m_builder.CreateICmpEQ(type, stringType); - llvm::Value *impossible = m_builder.CreateAnd(m_builder.CreateAnd(inRange, canNotBeString), isString); - m_builder.CreateCall(assumeIntrinsic, m_builder.CreateNot(impossible)); - } + // else if (!hasString) type == Number || type == Bool + llvm::Value *cond6 = notString; + llvm::Value *assume6 = m_builder.CreateOr(m_builder.CreateICmpEQ(type, numberType), m_builder.CreateICmpEQ(type, boolType)); + assume(m_builder.CreateSelect(cond6, assume6, m_builder.getInt1(true))); + + m_builder.CreateBr(afterBlock); + + // Out-of-range: always string + m_builder.SetInsertPoint(outOfRangeBlock); + llvm::Value *isString = m_builder.CreateICmpEQ(type, stringType); + assume(isString); + m_builder.CreateBr(afterBlock); + + m_builder.SetInsertPoint(afterBlock); } } From cdfacf753560b35ea1fb3e5e88a5b42694a56ccb Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 19 Aug 2025 20:51:56 +0200 Subject: [PATCH 15/42] Restore list replace type assumption --- .../internal/llvm/instructions/lists.cpp | 19 ++++++++++++------- src/engine/internal/llvm/instructions/lists.h | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 0260e2a9..2a74ff26 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -239,15 +239,17 @@ LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) m_builder.SetInsertPoint(replaceBlock); llvm::Value *itemPtr = m_utils.getListItem(listPtr, indexInt); - // llvm::Value *typeVar = createListTypeVar(listPtr, itemPtr); - // createListTypeAssumption(listPtr, typeVar, ins->targetType); - - /*llvm::Value *typeVar = m_utils.addAlloca(m_builder.getInt32Ty()); - llvm::Value *t = m_builder.CreateLoad(m_builder.getInt32Ty(), m_utils.getValueTypePtr(itemPtr)); - m_builder.CreateStore(t, typeVar);*/ - llvm::Value *typeVar = m_utils.getValueTypePtr(itemPtr); + llvm::Value *typePtr = m_utils.getValueTypePtr(itemPtr); + llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); + llvm::Value *typeVar = createListTypeVar(listPtr, loadedType); + createListTypeAssumption(listPtr, typeVar, ins->targetType); m_utils.createValueStore(itemPtr, typeVar, valueArg.second, listType, type); + + // Value store may change type, make sure to update it + loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typeVar); + m_builder.CreateStore(loadedType, typePtr); + createListTypeUpdate(listPtr, valueArg.second, type); m_builder.CreateBr(nextBlock); @@ -470,6 +472,9 @@ void Lists::createListTypeAssumption(const LLVMListPtr &listPtr, llvm::Value *ty llvm::Value *type = m_builder.CreateLoad(m_builder.getInt32Ty(), typeVar); + if (!inRange) + inRange = m_builder.getInt1(true); + llvm::Value *numberType = m_builder.getInt32(static_cast(ValueType::Number)); llvm::Value *boolType = m_builder.getInt32(static_cast(ValueType::Bool)); llvm::Value *stringType = m_builder.getInt32(static_cast(ValueType::String)); diff --git a/src/engine/internal/llvm/instructions/lists.h b/src/engine/internal/llvm/instructions/lists.h index 4388a8d5..f7797621 100644 --- a/src/engine/internal/llvm/instructions/lists.h +++ b/src/engine/internal/llvm/instructions/lists.h @@ -39,7 +39,7 @@ class Lists : public InstructionGroup void createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue, Compiler::StaticType newValueType); llvm::Value *createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *type); - void createListTypeAssumption(const LLVMListPtr &listPtr, llvm::Value *typeVar, Compiler::StaticType staticType, llvm::Value *inRange); + void createListTypeAssumption(const LLVMListPtr &listPtr, llvm::Value *typeVar, Compiler::StaticType staticType, llvm::Value *inRange = nullptr); }; } // namespace llvmins From 6b7770badb7d5ad5ac5441f10d964051b5ea78cd Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:11:57 +0200 Subject: [PATCH 16/42] Add expectations for list index in range branches --- src/engine/internal/llvm/instructions/lists.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 2a74ff26..1ae5a45e 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -364,21 +364,32 @@ LLVMInstruction *Lists::buildListContainsItem(LLVMInstruction *ins) llvm::Value *Lists::getIndex(const LLVMListPtr &listPtr, llvm::Value *indexDouble) { + llvm::Function *expectIntrinsic = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::expect, m_builder.getInt64Ty()); + llvm::Value *zero = llvm::ConstantFP::get(m_utils.llvmCtx(), llvm::APFloat(0.0)); llvm::Value *isNegative = m_builder.CreateFCmpOLT(indexDouble, zero, "listIndex.isNegative"); llvm::Value *intMax = llvm::ConstantInt::get(m_builder.getInt64Ty(), INT64_MAX); llvm::Value *intIndex = m_builder.CreateFPToUI(indexDouble, m_builder.getInt64Ty(), "listIndex.int"); - return m_builder.CreateSelect(isNegative, intMax, intIndex); + + // Tell the optimizer that negative indices are uncommon + llvm::Value *index = m_builder.CreateSelect(isNegative, intMax, intIndex); + return m_builder.CreateCall(expectIntrinsic, { index, intIndex }); } llvm::Value *Lists::createSizeRangeCheck(const LLVMListPtr &listPtr, llvm::Value *indexInt, const std::string &name, bool includeSize) { + llvm::Function *expectIntrinsic = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::expect, m_builder.getInt1Ty()); + llvm::Value *size = m_utils.getListSize(listPtr); + llvm::Value *inRange; if (includeSize) - return m_builder.CreateICmpULE(indexInt, size, name); + inRange = m_builder.CreateICmpULE(indexInt, size, name); else - return m_builder.CreateICmpULT(indexInt, size, name); + inRange = m_builder.CreateICmpULT(indexInt, size, name); + + // Tell the optimizer that indices in range are more common + return m_builder.CreateCall(expectIntrinsic, { inRange, m_builder.getInt1(true) }); } void Lists::createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue, Compiler::StaticType newValueType) From e505c20b8c58689236fbd1d0c34e713ebd90010d Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:34:02 +0200 Subject: [PATCH 17/42] Refactor LLVM optimization pipeline --- .../internal/llvm/llvmcompilercontext.cpp | 82 +++++++++++++++---- .../internal/llvm/llvmcompilercontext.h | 4 + 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/engine/internal/llvm/llvmcompilercontext.cpp b/src/engine/internal/llvm/llvmcompilercontext.cpp index f879f930..5ec8c6b4 100644 --- a/src/engine/internal/llvm/llvmcompilercontext.cpp +++ b/src/engine/internal/llvm/llvmcompilercontext.cpp @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include +#include #include #include @@ -70,20 +72,7 @@ void LLVMCompilerContext::initJit() #endif // Optimize - llvm::PassBuilder passBuilder; - llvm::LoopAnalysisManager loopAnalysisManager; - llvm::FunctionAnalysisManager functionAnalysisManager; - llvm::CGSCCAnalysisManager cGSCCAnalysisManager; - llvm::ModuleAnalysisManager moduleAnalysisManager; - - passBuilder.registerModuleAnalyses(moduleAnalysisManager); - passBuilder.registerCGSCCAnalyses(cGSCCAnalysisManager); - passBuilder.registerFunctionAnalyses(functionAnalysisManager); - passBuilder.registerLoopAnalyses(loopAnalysisManager); - passBuilder.crossRegisterProxies(loopAnalysisManager, functionAnalysisManager, cGSCCAnalysisManager, moduleAnalysisManager); - - llvm::ModulePassManager modulePassManager = passBuilder.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O3); - modulePassManager.run(*m_module, moduleAnalysisManager); + optimize(llvm::OptimizationLevel::O3); const auto &functions = m_module->getFunctionList(); std::vector lookupNames; @@ -151,6 +140,71 @@ void LLVMCompilerContext::initTarget() llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); llvm::InitializeNativeTargetAsmParser(); + + createTargetMachine(); + + m_module->setDataLayout(m_targetMachine->createDataLayout()); +} + +void LLVMCompilerContext::createTargetMachine() +{ + std::string error; + std::string targetTriple = llvm::sys::getDefaultTargetTriple(); + m_module->setTargetTriple(targetTriple); + + const llvm::Target *target = llvm::TargetRegistry::lookupTarget(targetTriple, error); + + if (!target) { + llvm::errs() << error; + return; + } + + llvm::TargetOptions opt; + const char *cpu = "generic"; + const char *features = ""; + + m_targetMachine = std::unique_ptr(target->createTargetMachine(targetTriple, cpu, features, opt, llvm::Reloc::PIC_)); +} + +void LLVMCompilerContext::optimize(llvm::OptimizationLevel optLevel) +{ + llvm::PassBuilder passBuilder(m_targetMachine.get()); + llvm::LoopAnalysisManager loopAnalysisManager; + llvm::FunctionAnalysisManager functionAnalysisManager; + llvm::CGSCCAnalysisManager cGSCCAnalysisManager; + llvm::ModuleAnalysisManager moduleAnalysisManager; + + passBuilder.registerModuleAnalyses(moduleAnalysisManager); + passBuilder.registerCGSCCAnalyses(cGSCCAnalysisManager); + passBuilder.registerFunctionAnalyses(functionAnalysisManager); + passBuilder.registerLoopAnalyses(loopAnalysisManager); + passBuilder.crossRegisterProxies(loopAnalysisManager, functionAnalysisManager, cGSCCAnalysisManager, moduleAnalysisManager); + + llvm::ModulePassManager modulePassManager = passBuilder.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O3); + + std::string pipeline; + + if (optLevel == llvm::OptimizationLevel::O0) + pipeline = "default"; + else if (optLevel == llvm::OptimizationLevel::O1) + pipeline = "default"; + else if (optLevel == llvm::OptimizationLevel::O2) + pipeline = "default"; + else if (optLevel == llvm::OptimizationLevel::O3) + pipeline = "default"; + else if (optLevel == llvm::OptimizationLevel::Os) + pipeline = "default"; + else if (optLevel == llvm::OptimizationLevel::Oz) + pipeline = "default"; + else + assert(false); + + if (passBuilder.parsePassPipeline(modulePassManager, pipeline)) { + llvm::errs() << "Failed to parse pipeline\n"; + return; + } + + modulePassManager.run(*m_module, moduleAnalysisManager); } llvm::Function *LLVMCompilerContext::createCoroResumeFunction() diff --git a/src/engine/internal/llvm/llvmcompilercontext.h b/src/engine/internal/llvm/llvmcompilercontext.h index 637e4961..ce606980 100644 --- a/src/engine/internal/llvm/llvmcompilercontext.h +++ b/src/engine/internal/llvm/llvmcompilercontext.h @@ -4,6 +4,7 @@ #include #include +#include #include @@ -52,6 +53,8 @@ class LLVMCompilerContext : public CompilerContext using DestroyCoroFuncType = void (*)(void *); void initTarget(); + void createTargetMachine(); + void optimize(llvm::OptimizationLevel optLevel); llvm::Function *createCoroResumeFunction(); llvm::Function *createCoroDestroyFunction(); @@ -62,6 +65,7 @@ class LLVMCompilerContext : public CompilerContext std::unique_ptr m_module; llvm::LLVMContext *m_llvmCtxPtr = nullptr; llvm::Module *m_modulePtr = nullptr; + std::unique_ptr m_targetMachine; llvm::Expected> m_jit; bool m_jitInitialized = false; From 8e4315456b264da751d87fb6d6bb3adf8a8ed791 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:07:14 +0200 Subject: [PATCH 18/42] Fix delete this clone stopping behavior --- src/blocks/controlblocks.cpp | 18 ++++++-- test/blocks/control_blocks_test.cpp | 72 +++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/src/blocks/controlblocks.cpp b/src/blocks/controlblocks.cpp index c29134c4..eacc593c 100644 --- a/src/blocks/controlblocks.cpp +++ b/src/blocks/controlblocks.cpp @@ -178,7 +178,10 @@ CompilerValue *ControlBlocks::compileCreateCloneOf(Compiler *compiler) CompilerValue *ControlBlocks::compileDeleteThisClone(Compiler *compiler) { - compiler->addTargetFunctionCall("control_delete_this_clone"); + CompilerValue *deleted = compiler->addTargetFunctionCall("control_delete_this_clone", Compiler::StaticType::Bool); + compiler->beginIfStatement(deleted); + compiler->createStop(); + compiler->endIf(); return nullptr; } @@ -234,10 +237,17 @@ extern "C" void control_create_clone(ExecutionContext *ctx, const StringPtr *spr } } -extern "C" void control_delete_this_clone(Target *target) +extern "C" bool control_delete_this_clone(Target *target) { if (!target->isStage()) { - target->engine()->stopTarget(target, nullptr); - static_cast(target)->deleteClone(); + Sprite *sprite = static_cast(target); + + if (sprite->isClone()) { + target->engine()->stopTarget(target, nullptr); + sprite->deleteClone(); + return true; + } } + + return false; } diff --git a/test/blocks/control_blocks_test.cpp b/test/blocks/control_blocks_test.cpp index e126241f..4364edc7 100644 --- a/test/blocks/control_blocks_test.cpp +++ b/test/blocks/control_blocks_test.cpp @@ -1064,45 +1064,96 @@ TEST_F(ControlBlocksTest, CreateCloneOfStage) } } -TEST_F(ControlBlocksTest, DeleteThisClone) +TEST_F(ControlBlocksTest, DeleteThisClone_Clone) { - Sprite sprite; - sprite.setEngine(&m_engineMock); + auto sprite = std::make_shared(); + sprite->setEngine(&m_engineMock); + + auto var = std::make_shared("", ""); + sprite->addVariable(var); std::shared_ptr clone; EXPECT_CALL(m_engineMock, cloneLimit()).WillRepeatedly(Return(-1)); EXPECT_CALL(m_engineMock, initClone(_)).WillOnce(SaveArg<0>(&clone)); - EXPECT_CALL(m_engineMock, moveDrawableBehindOther(_, &sprite)); + EXPECT_CALL(m_engineMock, moveDrawableBehindOther(_, sprite.get())); EXPECT_CALL(m_engineMock, requestRedraw()); - sprite.clone(); + sprite->clone(); ASSERT_TRUE(clone); - ScriptBuilder builder(m_extension.get(), m_engine, clone); + ScriptBuilder builder(m_extension.get(), m_engine, sprite); builder.addBlock("control_delete_this_clone"); auto block = builder.currentBlock(); - Compiler compiler(&m_engineMock, clone.get()); + builder.addBlock("test_set_var"); + builder.addEntityField("VARIABLE", var); + builder.addValueInput("VALUE", true); + builder.currentBlock(); + + Compiler compiler(&m_engineMock, sprite.get()); auto code = compiler.compile(block); - Script script(clone.get(), block, &m_engineMock); + Script script(sprite.get(), block, &m_engineMock); script.setCode(code); Thread thread(clone.get(), &m_engineMock, &script); EXPECT_CALL(m_engineMock, stopTarget(clone.get(), nullptr)); EXPECT_CALL(m_engineMock, deinitClone(clone)); thread.run(); + + // The script should stop (variable value is false) + ASSERT_FALSE(clone->variableAt(0)->value().toBool()); } -TEST_F(ControlBlocksTest, DeleteThisCloneStage) +TEST_F(ControlBlocksTest, DeleteThisClone_NotCloneSprite) +{ + auto sprite = std::make_shared(); + sprite->setEngine(&m_engineMock); + + auto var = std::make_shared("", ""); + sprite->addVariable(var); + + ScriptBuilder builder(m_extension.get(), m_engine, sprite); + + builder.addBlock("control_delete_this_clone"); + auto block = builder.currentBlock(); + + builder.addBlock("test_set_var"); + builder.addEntityField("VARIABLE", var); + builder.addValueInput("VALUE", true); + builder.currentBlock(); + + Compiler compiler(&m_engineMock, sprite.get()); + auto code = compiler.compile(block); + Script script(sprite.get(), block, &m_engineMock); + script.setCode(code); + Thread thread(sprite.get(), &m_engineMock, &script); + + EXPECT_CALL(m_engineMock, stopTarget).Times(0); + EXPECT_CALL(m_engineMock, deinitClone).Times(0); + thread.run(); + + // The script should NOT stop (variable value is true) + ASSERT_TRUE(var->value().toBool()); +} + +TEST_F(ControlBlocksTest, DeleteThisClone_Stage) { auto target = std::make_shared(); target->setEngine(&m_engineMock); + auto var = std::make_shared("", ""); + target->addVariable(var); + ScriptBuilder builder(m_extension.get(), m_engine, target); builder.addBlock("control_delete_this_clone"); auto block = builder.currentBlock(); + builder.addBlock("test_set_var"); + builder.addEntityField("VARIABLE", var); + builder.addValueInput("VALUE", true); + builder.currentBlock(); + Compiler compiler(&m_engineMock, target.get()); auto code = compiler.compile(block); Script script(target.get(), block, &m_engineMock); @@ -1112,4 +1163,7 @@ TEST_F(ControlBlocksTest, DeleteThisCloneStage) EXPECT_CALL(m_engineMock, stopTarget).Times(0); EXPECT_CALL(m_engineMock, deinitClone).Times(0); thread.run(); + + // The script should NOT stop (variable value is true) + ASSERT_TRUE(var->value().toBool()); } From 386559cabf76cd70d9840a17e8eecab80395c846 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:28:48 +0200 Subject: [PATCH 19/42] Add stop without sync instruction --- src/engine/internal/icodebuilder.h | 1 + src/engine/internal/llvm/instructions/control.cpp | 10 ++++++++++ src/engine/internal/llvm/instructions/control.h | 1 + src/engine/internal/llvm/llvmbuildutils.cpp | 7 ++++++- src/engine/internal/llvm/llvmcodebuilder.cpp | 6 ++++++ src/engine/internal/llvm/llvmcodebuilder.h | 1 + src/engine/internal/llvm/llvminstruction.h | 1 + test/mocks/codebuildermock.h | 1 + 8 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/engine/internal/icodebuilder.h b/src/engine/internal/icodebuilder.h index d175c850..1c793c1d 100644 --- a/src/engine/internal/icodebuilder.h +++ b/src/engine/internal/icodebuilder.h @@ -99,6 +99,7 @@ class ICodeBuilder virtual void yield() = 0; virtual void createStop() = 0; + virtual void createStopWithoutSync() = 0; virtual void createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args) = 0; }; diff --git a/src/engine/internal/llvm/instructions/control.cpp b/src/engine/internal/llvm/instructions/control.cpp index 75c4d9fc..ce1344ae 100644 --- a/src/engine/internal/llvm/instructions/control.cpp +++ b/src/engine/internal/llvm/instructions/control.cpp @@ -60,6 +60,10 @@ ProcessResult Control::process(LLVMInstruction *ins) ret.next = buildStop(ins); break; + case LLVMInstruction::Type::StopWithoutSync: + ret.next = buildStopWithoutSync(ins); + break; + default: ret.match = false; break; @@ -336,6 +340,12 @@ LLVMInstruction *Control::buildEndLoop(LLVMInstruction *ins) } LLVMInstruction *Control::buildStop(LLVMInstruction *ins) +{ + m_utils.syncVariables(); + return buildStopWithoutSync(ins); +} + +LLVMInstruction *Control::buildStopWithoutSync(LLVMInstruction *ins) { m_utils.freeScopeHeap(); m_builder.CreateBr(m_utils.endBranch()); diff --git a/src/engine/internal/llvm/instructions/control.h b/src/engine/internal/llvm/instructions/control.h index 0c80f817..bb294f9b 100644 --- a/src/engine/internal/llvm/instructions/control.h +++ b/src/engine/internal/llvm/instructions/control.h @@ -27,6 +27,7 @@ class Control : public InstructionGroup LLVMInstruction *buildBeginLoopCondition(LLVMInstruction *ins); LLVMInstruction *buildEndLoop(LLVMInstruction *ins); LLVMInstruction *buildStop(LLVMInstruction *ins); + LLVMInstruction *buildStopWithoutSync(LLVMInstruction *ins); }; } // namespace libscratchcpp::llvmins diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 7026b6b8..b8df3cb5 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -108,10 +108,15 @@ void LLVMBuildUtils::end(LLVMInstruction *lastInstruction, LLVMRegister *lastCon assert(m_stringHeap.size() == 1); freeScopeHeap(); + // Sync + llvm::BasicBlock *syncBranch = llvm::BasicBlock::Create(m_llvmCtx, "sync", m_function); + m_builder.CreateBr(syncBranch); + + m_builder.SetInsertPoint(syncBranch); + syncVariables(); m_builder.CreateBr(m_endBranch); m_builder.SetInsertPoint(m_endBranch); - syncVariables(); // End the script function llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index 3cba9270..53b36881 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -552,6 +552,12 @@ void LLVMCodeBuilder::createStop() m_instructions.addInstruction(ins); } +void LLVMCodeBuilder::createStopWithoutSync() +{ + auto ins = std::make_shared(LLVMInstruction::Type::StopWithoutSync, m_loopCondition); + m_instructions.addInstruction(ins); +} + void LLVMCodeBuilder::createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args) { assert(prototype); diff --git a/src/engine/internal/llvm/llvmcodebuilder.h b/src/engine/internal/llvm/llvmcodebuilder.h index 37c060c3..7fd9b928 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.h +++ b/src/engine/internal/llvm/llvmcodebuilder.h @@ -112,6 +112,7 @@ class LLVMCodeBuilder : public ICodeBuilder void yield() override; void createStop() override; + void createStopWithoutSync() override; void createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args) override; diff --git a/src/engine/internal/llvm/llvminstruction.h b/src/engine/internal/llvm/llvminstruction.h index e6a5212e..d4875d7d 100644 --- a/src/engine/internal/llvm/llvminstruction.h +++ b/src/engine/internal/llvm/llvminstruction.h @@ -76,6 +76,7 @@ struct LLVMInstruction BeginLoopCondition, EndLoop, Stop, + StopWithoutSync, CallProcedure, ProcedureArg }; diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h index 6ff635c1..1d9225a2 100644 --- a/test/mocks/codebuildermock.h +++ b/test/mocks/codebuildermock.h @@ -88,6 +88,7 @@ class CodeBuilderMock : public ICodeBuilder MOCK_METHOD(void, yield, (), (override)); MOCK_METHOD(void, createStop, (), (override)); + MOCK_METHOD(void, createStopWithoutSync, (), (override)); MOCK_METHOD(void, createProcedureCall, (BlockPrototype *, const Compiler::Args &), (override)); }; From 478a8dd14df531d6faf7268b1a77dbb5de3d3edf Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:34:39 +0200 Subject: [PATCH 20/42] Compiler: Add createStopWithoutSync() method --- include/scratchcpp/compiler.h | 1 + src/engine/compiler.cpp | 10 ++++++++++ test/compiler/compiler_test.cpp | 14 ++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/include/scratchcpp/compiler.h b/include/scratchcpp/compiler.h index cf534783..2b48866d 100644 --- a/include/scratchcpp/compiler.h +++ b/include/scratchcpp/compiler.h @@ -148,6 +148,7 @@ class LIBSCRATCHCPP_EXPORT Compiler void createYield(); void createStop(); + void createStopWithoutSync(); void createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args); diff --git a/src/engine/compiler.cpp b/src/engine/compiler.cpp index 90f95afe..5c96d88d 100644 --- a/src/engine/compiler.cpp +++ b/src/engine/compiler.cpp @@ -705,6 +705,16 @@ void Compiler::createStop() impl->builder->createStop(); } +/*! + * Creates a stop script without synchronization instruction.\n + * Use this if synchronization is not possible at the stop point. + * \note Only use this when everything is synchronized, e. g. after a function call. + */ +void Compiler::createStopWithoutSync() +{ + impl->builder->createStopWithoutSync(); +} + /*! Creates a call to the procedure with the given prototype. */ void Compiler::createProcedureCall(BlockPrototype *prototype, const libscratchcpp::Compiler::Args &args) { diff --git a/test/compiler/compiler_test.cpp b/test/compiler/compiler_test.cpp index 55d8d161..9cdbf44f 100644 --- a/test/compiler/compiler_test.cpp +++ b/test/compiler/compiler_test.cpp @@ -1673,6 +1673,20 @@ TEST_F(CompilerTest, CreateStop) compile(m_compiler.get(), block.get()); } +TEST_F(CompilerTest, CreateStopWithoutSync) +{ + + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { + EXPECT_CALL(*m_builder, createStopWithoutSync()); + compiler->createStopWithoutSync(); + return nullptr; + }); + + compile(m_compiler.get(), block.get()); +} + TEST_F(CompilerTest, CreateProcedureCall) { From bb27299f852b65fc30db3df582e4dc05a2f5cd0a Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:44:13 +0200 Subject: [PATCH 21/42] Do not synchronize stopped scripts after delete this clone block --- src/blocks/controlblocks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blocks/controlblocks.cpp b/src/blocks/controlblocks.cpp index eacc593c..53d7f90f 100644 --- a/src/blocks/controlblocks.cpp +++ b/src/blocks/controlblocks.cpp @@ -180,7 +180,7 @@ CompilerValue *ControlBlocks::compileDeleteThisClone(Compiler *compiler) { CompilerValue *deleted = compiler->addTargetFunctionCall("control_delete_this_clone", Compiler::StaticType::Bool); compiler->beginIfStatement(deleted); - compiler->createStop(); + compiler->createStopWithoutSync(); // sync happens before the function call compiler->endIf(); return nullptr; } From 1acbafe7d6b5be7ce3940c8f7b5f3141f2b236d0 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:45:29 +0200 Subject: [PATCH 22/42] LLVMCoroutine: Fix uninitialized local in resume --- src/engine/internal/llvm/llvmcoroutine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/internal/llvm/llvmcoroutine.cpp b/src/engine/internal/llvm/llvmcoroutine.cpp index f4b268fb..02c4ecb1 100644 --- a/src/engine/internal/llvm/llvmcoroutine.cpp +++ b/src/engine/internal/llvm/llvmcoroutine.cpp @@ -112,7 +112,7 @@ llvm::Value *LLVMCoroutine::createResume(llvm::Module *module, llvm::IRBuilder<> llvm::Value *ret = builder->CreateAlloca(builder->getInt1Ty()); llvm::Value *done = builder->CreateCall(coroDone, { coroHandle }); - done = builder->CreateCall(coroDone, { coroHandle }); + builder->CreateStore(done, ret); llvm::BasicBlock *destroyBranch = llvm::BasicBlock::Create(ctx, "", function); llvm::BasicBlock *resumeBranch = llvm::BasicBlock::Create(ctx, "", function); From 8e42c38aa66baf9faf0b0ea353402558824d5ec2 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:46:07 +0200 Subject: [PATCH 23/42] LLVMCodeBuilder: Fix a typo in code analyzer comment --- src/engine/internal/llvm/llvmcodebuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index 53b36881..e0636c13 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -55,7 +55,7 @@ std::shared_ptr LLVMCodeBuilder::build() if (m_warp) { #ifdef ENABLE_CODE_ANALYZER // Analyze the script (type analysis, optimizations, etc.) - // NOTE: Do this only for non-warp scripts + // NOTE: Do this only for warp scripts m_codeAnalyzer.analyzeScript(m_instructions); #endif } From 6173c3435aff531722ec346138d949b6ce16c8dc Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 20 Aug 2025 16:47:57 +0200 Subject: [PATCH 24/42] Compiler: Update docs for add function call methods --- src/engine/compiler.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/compiler.cpp b/src/engine/compiler.cpp index 5c96d88d..33d97516 100644 --- a/src/engine/compiler.cpp +++ b/src/engine/compiler.cpp @@ -136,7 +136,7 @@ void Compiler::preoptimize() /*! * Adds a call to the given function.\n - * For example: extern "C" bool some_block(double arg1, const char *arg2) + * For example: extern "C" bool some_block(double arg1, const StringPtr *arg2) */ CompilerValue *Compiler::addFunctionCall(const std::string &functionName, StaticType returnType, const ArgTypes &argTypes, const Args &args) { @@ -146,7 +146,7 @@ CompilerValue *Compiler::addFunctionCall(const std::string &functionName, Static /*! * Adds a call to the given function with a target parameter.\n - * For example: extern "C" bool some_block(Target *target, double arg1, const char *arg2) + * For example: extern "C" bool some_block(Target *target, double arg1, const StringPtr *arg2) */ CompilerValue *Compiler::addTargetFunctionCall(const std::string &functionName, StaticType returnType, const ArgTypes &argTypes, const Args &args) { @@ -156,7 +156,7 @@ CompilerValue *Compiler::addTargetFunctionCall(const std::string &functionName, /*! * Adds a call to the given function with an execution context parameter.\n - * For example: extern "C" bool some_block(ExecutionContext *ctx, double arg1, const char *arg2) + * For example: extern "C" bool some_block(ExecutionContext *ctx, double arg1, const StringPtr *arg2) */ CompilerValue *Compiler::addFunctionCallWithCtx(const std::string &functionName, StaticType returnType, const ArgTypes &argTypes, const Args &args) { From 4521382a288937e9f1a0ff62f80533cb336ea7de Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 25 Aug 2025 19:35:26 +0200 Subject: [PATCH 25/42] Add initial LLVM integer value support --- .../internal/llvm/instructions/lists.cpp | 75 +++---- src/engine/internal/llvm/instructions/lists.h | 3 +- .../internal/llvm/instructions/variables.cpp | 4 +- src/engine/internal/llvm/llvmbuildutils.cpp | 211 ++++++++++++------ src/engine/internal/llvm/llvmbuildutils.h | 26 ++- src/engine/internal/llvm/llvmcodebuilder.cpp | 2 +- src/engine/internal/llvm/llvmregister.h | 5 + src/engine/internal/llvm/llvmvariableptr.h | 3 + 8 files changed, 205 insertions(+), 124 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 1ae5a45e..88a75a4d 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -100,9 +100,8 @@ LLVMInstruction *Lists::buildRemoveListItem(LLVMInstruction *ins) LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); // Range check - llvm::Value *indexDouble = m_utils.castValue(arg.second, arg.first); - llvm::Value *indexInt = getIndex(listPtr, indexDouble); - llvm::Value *inRange = createSizeRangeCheck(listPtr, indexInt, "removeListItem.indexInRange"); + llvm::Value *index = m_utils.castValue(arg.second, Compiler::StaticType::Number, LLVMBuildUtils::NumberType::Int); + llvm::Value *inRange = createIndexRangeCheck(listPtr, index, "removeListItem.indexInRange"); llvm::BasicBlock *removeBlock = llvm::BasicBlock::Create(llvmCtx, "", function); llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(llvmCtx, "", function); @@ -110,7 +109,7 @@ LLVMInstruction *Lists::buildRemoveListItem(LLVMInstruction *ins) // Remove m_builder.SetInsertPoint(removeBlock); - m_builder.CreateCall(m_utils.functions().resolve_list_remove(), { listPtr.ptr, indexInt }); + m_builder.CreateCall(m_utils.functions().resolve_list_remove(), { listPtr.ptr, index }); if (listPtr.size) { // Update size @@ -144,17 +143,21 @@ LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(llvmCtx, "", function); m_builder.CreateCondBr(isAllocated, ifBlock, elseBlock); + // TODO: Add integer support for lists + llvm::Value *isIntVar = m_utils.addAlloca(m_builder.getInt1Ty()); + llvm::Value *intVar = m_utils.addAlloca(m_builder.getInt64Ty()); + // If there's enough space, use the allocated memory m_builder.SetInsertPoint(ifBlock); llvm::Value *itemPtr = m_utils.getListItem(listPtr, size); - m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), arg.second, type); + m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), isIntVar, intVar, arg.second, type); m_builder.CreateStore(m_builder.CreateAdd(size, m_builder.getInt64(1)), listPtr.sizePtr); // update size stored in *sizePtr m_builder.CreateBr(nextBlock); // Otherwise call appendEmpty() m_builder.SetInsertPoint(elseBlock); itemPtr = m_builder.CreateCall(m_utils.functions().resolve_list_append_empty(), listPtr.ptr); - m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), arg.second, type); + m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), isIntVar, intVar, arg.second, type); m_builder.CreateBr(nextBlock); m_builder.SetInsertPoint(nextBlock); @@ -182,18 +185,21 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); // Range check - llvm::Value *indexDouble = m_utils.castValue(indexArg.second, indexArg.first); - llvm::Value *indexInt = getIndex(listPtr, indexDouble); - llvm::Value *inRange = createSizeRangeCheck(listPtr, indexInt, "insertToList.indexInRange", true); + llvm::Value *index = m_utils.castValue(indexArg.second, Compiler::StaticType::Number, LLVMBuildUtils::NumberType::Int); + llvm::Value *inRange = createIndexRangeCheck(listPtr, index, "insertToList.indexInRange", true); llvm::BasicBlock *insertBlock = llvm::BasicBlock::Create(llvmCtx, "", function); llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(llvmCtx, "", function); m_builder.CreateCondBr(inRange, insertBlock, nextBlock); + // TODO: Add integer support for lists + llvm::Value *isIntVar = m_utils.addAlloca(m_builder.getInt1Ty()); + llvm::Value *intVar = m_utils.addAlloca(m_builder.getInt64Ty()); + // Insert m_builder.SetInsertPoint(insertBlock); - llvm::Value *itemPtr = m_builder.CreateCall(m_utils.functions().resolve_list_insert_empty(), { listPtr.ptr, indexInt }); - m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), valueArg.second, type); + llvm::Value *itemPtr = m_builder.CreateCall(m_utils.functions().resolve_list_insert_empty(), { listPtr.ptr, index }); + m_utils.createValueStore(itemPtr, m_utils.getValueTypePtr(itemPtr), isIntVar, intVar, valueArg.second, type); if (listPtr.size) { // Update size @@ -227,9 +233,8 @@ LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) Compiler::StaticType listType = ins->targetType; // Range check - llvm::Value *indexDouble = m_utils.castValue(indexArg.second, indexArg.first); - llvm::Value *indexInt = getIndex(listPtr, indexDouble); - llvm::Value *inRange = createSizeRangeCheck(listPtr, indexInt, "listReplace.indexInRange"); + llvm::Value *index = m_utils.castValue(indexArg.second, Compiler::StaticType::Number, LLVMBuildUtils::NumberType::Int); + llvm::Value *inRange = createIndexRangeCheck(listPtr, index, "listReplace.indexInRange"); llvm::BasicBlock *replaceBlock = llvm::BasicBlock::Create(llvmCtx, "", function); llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(llvmCtx, "", function); @@ -238,13 +243,17 @@ LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) // Replace m_builder.SetInsertPoint(replaceBlock); - llvm::Value *itemPtr = m_utils.getListItem(listPtr, indexInt); + llvm::Value *itemPtr = m_utils.getListItem(listPtr, index); llvm::Value *typePtr = m_utils.getValueTypePtr(itemPtr); llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); llvm::Value *typeVar = createListTypeVar(listPtr, loadedType); + // TODO: Add integer support for lists + llvm::Value *isIntVar = m_utils.addAlloca(m_builder.getInt1Ty()); + llvm::Value *intVar = m_utils.addAlloca(m_builder.getInt64Ty()); + createListTypeAssumption(listPtr, typeVar, ins->targetType); - m_utils.createValueStore(itemPtr, typeVar, valueArg.second, listType, type); + m_utils.createValueStore(itemPtr, typeVar, isIntVar, intVar, valueArg.second, listType, type); // Value store may change type, make sure to update it loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typeVar); @@ -285,9 +294,8 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); // Range check - llvm::Value *indexDouble = m_utils.castValue(arg.second, arg.first); - llvm::Value *indexInt = getIndex(listPtr, indexDouble); - llvm::Value *inRange = createSizeRangeCheck(listPtr, indexInt, "getListItem.indexInRange"); + llvm::Value *index = m_utils.castValue(arg.second, Compiler::StaticType::Number, LLVMBuildUtils::NumberType::Int); + llvm::Value *inRange = createIndexRangeCheck(listPtr, index, "getListItem.indexInRange"); llvm::BasicBlock *inRangeBlock = llvm::BasicBlock::Create(llvmCtx, "getListItem.inRange", function); llvm::BasicBlock *outOfRangeBlock = llvm::BasicBlock::Create(llvmCtx, "getListItem.outOfRange", function); @@ -296,7 +304,7 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) // In range m_builder.SetInsertPoint(inRangeBlock); - llvm::Value *itemPtr = m_utils.getListItem(listPtr, indexInt); + llvm::Value *itemPtr = m_utils.getListItem(listPtr, index); llvm::Value *itemType = m_builder.CreateLoad(m_builder.getInt32Ty(), m_utils.getValueTypePtr(itemPtr)); m_builder.CreateBr(nextBlock); @@ -332,6 +340,8 @@ LLVMInstruction *Lists::buildGetListSize(LLVMInstruction *ins) const LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); llvm::Value *size = m_utils.getListSize(listPtr); ins->functionReturnReg->value = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); + ins->functionReturnReg->isInt = m_builder.getInt1(true); + ins->functionReturnReg->intValue = size; return ins->next; } @@ -362,31 +372,14 @@ LLVMInstruction *Lists::buildListContainsItem(LLVMInstruction *ins) return ins->next; } -llvm::Value *Lists::getIndex(const LLVMListPtr &listPtr, llvm::Value *indexDouble) -{ - llvm::Function *expectIntrinsic = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::expect, m_builder.getInt64Ty()); - - llvm::Value *zero = llvm::ConstantFP::get(m_utils.llvmCtx(), llvm::APFloat(0.0)); - llvm::Value *isNegative = m_builder.CreateFCmpOLT(indexDouble, zero, "listIndex.isNegative"); - llvm::Value *intMax = llvm::ConstantInt::get(m_builder.getInt64Ty(), INT64_MAX); - llvm::Value *intIndex = m_builder.CreateFPToUI(indexDouble, m_builder.getInt64Ty(), "listIndex.int"); - - // Tell the optimizer that negative indices are uncommon - llvm::Value *index = m_builder.CreateSelect(isNegative, intMax, intIndex); - return m_builder.CreateCall(expectIntrinsic, { index, intIndex }); -} - -llvm::Value *Lists::createSizeRangeCheck(const LLVMListPtr &listPtr, llvm::Value *indexInt, const std::string &name, bool includeSize) +llvm::Value *Lists::createIndexRangeCheck(const LLVMListPtr &listPtr, llvm::Value *index, const std::string &name, bool includeSize) { llvm::Function *expectIntrinsic = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::expect, m_builder.getInt1Ty()); + llvm::Value *min = llvm::ConstantInt::get(m_builder.getInt64Ty(), 0, true); llvm::Value *size = m_utils.getListSize(listPtr); - llvm::Value *inRange; - - if (includeSize) - inRange = m_builder.CreateICmpULE(indexInt, size, name); - else - inRange = m_builder.CreateICmpULT(indexInt, size, name); + llvm::Value *sizeCheck = includeSize ? m_builder.CreateICmpSLE(index, size) : m_builder.CreateICmpSLT(index, size); + llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateICmpSGE(index, min), sizeCheck, name); // Tell the optimizer that indices in range are more common return m_builder.CreateCall(expectIntrinsic, { inRange, m_builder.getInt1(true) }); diff --git a/src/engine/internal/llvm/instructions/lists.h b/src/engine/internal/llvm/instructions/lists.h index f7797621..06ceedee 100644 --- a/src/engine/internal/llvm/instructions/lists.h +++ b/src/engine/internal/llvm/instructions/lists.h @@ -34,8 +34,7 @@ class Lists : public InstructionGroup LLVMInstruction *buildGetListItemIndex(LLVMInstruction *ins); LLVMInstruction *buildListContainsItem(LLVMInstruction *ins); - llvm::Value *getIndex(const LLVMListPtr &listPtr, llvm::Value *indexDouble); - llvm::Value *createSizeRangeCheck(const LLVMListPtr &listPtr, llvm::Value *indexInt, const std::string &name, bool includeSize = false); + llvm::Value *createIndexRangeCheck(const LLVMListPtr &listPtr, llvm::Value *index, const std::string &name, bool includeSize = false); void createListTypeUpdate(const LLVMListPtr &listPtr, const LLVMRegister *newValue, Compiler::StaticType newValueType); llvm::Value *createListTypeVar(const LLVMListPtr &listPtr, llvm::Value *type); diff --git a/src/engine/internal/llvm/instructions/variables.cpp b/src/engine/internal/llvm/instructions/variables.cpp index 8d9bee72..2d8dae2c 100644 --- a/src/engine/internal/llvm/instructions/variables.cpp +++ b/src/engine/internal/llvm/instructions/variables.cpp @@ -112,7 +112,7 @@ LLVMInstruction *Variables::buildWriteVariable(LLVMInstruction *ins) Compiler::StaticType argType = m_utils.optimizeRegisterType(arg.second); LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->targetVariable); - m_utils.createValueStore(varPtr.stackPtr, m_utils.getValueTypePtr(varPtr.stackPtr), arg.second, ins->targetType, argType); + m_utils.createValueStore(varPtr.stackPtr, m_utils.getValueTypePtr(varPtr.stackPtr), varPtr.isInt, varPtr.intValue, arg.second, ins->targetType, argType); return ins->next; } @@ -122,5 +122,7 @@ LLVMInstruction *Variables::buildReadVariable(LLVMInstruction *ins) LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->targetVariable); ins->functionReturnReg->value = varPtr.stackPtr; + ins->functionReturnReg->isInt = m_builder.CreateLoad(m_builder.getInt1Ty(), varPtr.isInt); + ins->functionReturnReg->intValue = m_builder.CreateLoad(m_builder.getInt64Ty(), varPtr.intValue); return ins->next; } diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index b8df3cb5..e53082b6 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -41,7 +41,7 @@ LLVMBuildUtils::LLVMBuildUtils(LLVMCompilerContext *ctx, llvm::IRBuilder<> &buil createListMap(); } -void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePrototype, bool warp) +void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePrototype, bool warp, const std::vector> ®s) { m_function = function; m_procedurePrototype = procedurePrototype; @@ -63,6 +63,13 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro if (!m_warp) m_coroutine = std::make_unique(m_ctx->module(), &m_builder, m_function); + // Init registers + for (auto reg : regs) { + bool isIntConst = (reg->isConst() && optimizeRegisterType(reg.get()) == Compiler::StaticType::Number && reg->constValue().toDouble() == std::floor(reg->constValue().toDouble())); + reg->isInt = m_builder.getInt1(isIntConst); + reg->intValue = llvm::ConstantInt::get(m_builder.getInt64Ty(), reg->constValue().toDouble(), true); + } + // Create variable pointers for (auto &[var, varPtr] : m_variablePtrs) { llvm::Value *ptr = getVariablePtr(m_targetVariables, var); @@ -70,6 +77,12 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro // Store variables locally to enable optimizations varPtr.stackPtr = m_builder.CreateAlloca(m_valueDataType); + + // Integer support + varPtr.isInt = m_builder.CreateAlloca(m_builder.getInt1Ty(), nullptr, var->name() + ".isInt"); + varPtr.intValue = m_builder.CreateAlloca(m_builder.getInt64Ty(), nullptr, var->name() + ".intValue"); + m_builder.CreateStore(m_builder.getInt1(false), varPtr.isInt); + m_builder.CreateStore(llvm::ConstantInt::get(m_builder.getInt64Ty(), 0, true), varPtr.isInt); } // Create list pointers @@ -322,6 +335,7 @@ void LLVMBuildUtils::reloadVariables() for (auto &[var, varPtr] : m_variablePtrs) { llvm::Value *ptr = getVariablePtr(m_targetVariables, var); createValueCopy(ptr, varPtr.stackPtr); + m_builder.CreateStore(m_builder.getInt1(false), varPtr.isInt); } } @@ -426,17 +440,17 @@ llvm::Value *LLVMBuildUtils::addAlloca(llvm::Type *type) return ret; } -llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType targetType) +llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType targetType, NumberType targetNumType) { if (reg->isConst()) { if (!isSingleType(targetType)) return createValue(reg); else - return castConstValue(reg->constValue(), targetType); + return castConstValue(reg->constValue(), targetType, targetNumType); } if (reg->isRawValue) - return castRawValue(reg, targetType); + return castRawValue(reg, targetType, targetNumType); assert(reg->type() != Compiler::StaticType::Void && targetType != Compiler::StaticType::Void); @@ -468,8 +482,20 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); m_builder.SetInsertPoint(numberBlock); - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *numberResult = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + llvm::Value *numberResult; + + if (targetNumType == NumberType::Int) { + // double/int -> int + llvm::Value *doubleInt = m_builder.CreateFPToSI(reg->intValue, m_builder.getInt64Ty()); + numberResult = m_builder.CreateSelect(reg->isInt, reg->intValue, doubleInt); + } else { + // double/int -> double + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *doubleValue = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + llvm::Value *intDouble = m_builder.CreateSIToFP(reg->intValue, m_builder.getDoubleTy()); + numberResult = m_builder.CreateSelect(reg->isInt, intDouble, doubleValue); + } + m_builder.CreateBr(mergeBlock); results.push_back({ numberBlock, numberResult }); } @@ -482,7 +508,16 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t m_builder.SetInsertPoint(boolBlock); llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); llvm::Value *boolValue = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - llvm::Value *boolResult = m_builder.CreateUIToFP(boolValue, m_builder.getDoubleTy()); + llvm::Value *boolResult; + + if (targetNumType == NumberType::Int) { + // bool -> int + boolResult = m_builder.CreateZExt(boolValue, m_builder.getInt64Ty()); + } else { + // bool -> double + boolResult = m_builder.CreateUIToFP(boolValue, m_builder.getDoubleTy()); + } + m_builder.CreateBr(mergeBlock); results.push_back({ boolBlock, boolResult }); } @@ -496,6 +531,10 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_value_stringToDouble(), stringPtr); + + if (targetNumType == NumberType::Int) + stringResult = m_builder.CreateFPToSI(stringResult, m_builder.getInt64Ty()); + m_builder.CreateBr(mergeBlock); results.push_back({ stringBlock, stringResult }); } @@ -512,7 +551,11 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t m_builder.SetInsertPoint(numberBlock); llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); llvm::Value *numberValue = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - llvm::Value *numberResult = m_builder.CreateFCmpONE(numberValue, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); + + llvm::Value *intResult = m_builder.CreateICmpNE(reg->intValue, llvm::ConstantInt::get(m_builder.getInt64Ty(), 0, true)); + llvm::Value *doubleResult = m_builder.CreateFCmpONE(numberValue, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); + llvm::Value *numberResult = m_builder.CreateSelect(reg->isInt, intResult, doubleResult); + m_builder.CreateBr(mergeBlock); results.push_back({ numberBlock, numberResult }); } @@ -553,7 +596,11 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t m_builder.SetInsertPoint(numberBlock); llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *value = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + llvm::Value *doubleValue = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + + llvm::Value *intCast = m_builder.CreateSIToFP(reg->intValue, m_builder.getDoubleTy()); + llvm::Value *value = m_builder.CreateSelect(reg->isInt, intCast, doubleValue); + llvm::Value *numberResult = m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), value); m_builder.CreateBr(mergeBlock); results.push_back({ numberBlock, numberResult }); @@ -605,7 +652,12 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t // Create phi node to merge results m_builder.SetInsertPoint(mergeBlock); - llvm::PHINode *result = m_builder.CreatePHI(getType(targetType, false), results.size()); + llvm::Type *phiType = getType(targetType, false); + + if (targetType == Compiler::StaticType::Number && targetNumType == NumberType::Int) + phiType = m_builder.getInt64Ty(); + + llvm::PHINode *result = m_builder.CreatePHI(phiType, results.size()); for (auto &pair : results) result->addIncoming(pair.second, pair.first); @@ -642,6 +694,7 @@ llvm::Type *LLVMBuildUtils::getType(Compiler::StaticType type, bool isReturnType llvm::Value *LLVMBuildUtils::isNaN(llvm::Value *num) { + assert(num->getType() == m_builder.getDoubleTy()); return m_builder.CreateFCmpUNO(num, num); } @@ -651,13 +704,17 @@ llvm::Value *LLVMBuildUtils::removeNaN(llvm::Value *num) return m_builder.CreateSelect(isNaN(num), llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)), num); } -void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTypePtr, LLVMRegister *reg, Compiler::StaticType destType, Compiler::StaticType targetType) +void LLVMBuildUtils::createValueStore( + llvm::Value *destPtr, + llvm::Value *destTypePtr, + llvm::Value *destIsIntVar, + llvm::Value *destIntVar, + LLVMRegister *reg, + Compiler::StaticType destType, + Compiler::StaticType targetType) { - llvm::Value *targetPtr = nullptr; - const bool targetTypeIsSingle = isSingleType(targetType); - - if (targetTypeIsSingle) - targetPtr = castValue(reg, targetType); + assert(destIsIntVar->getType()->isPointerTy()); + assert(destIntVar->getType()->isPointerTy()); auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [targetType](const std::pair &pair) { return pair.second == targetType; }); const ValueType mappedType = it == TYPE_MAP.cend() ? ValueType::Number : it->first; // unknown type can be ignored @@ -696,14 +753,15 @@ void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTyp m_builder.SetInsertPoint(numberBlock); // Load number - if (!targetTypeIsSingle) { - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - targetPtr = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - } + llvm::Value *doubleValue = castValue(reg, Compiler::StaticType::Number, NumberType::Double); // Write number to number directly llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); - m_builder.CreateStore(targetPtr, ptr); + m_builder.CreateStore(doubleValue, ptr); + + m_builder.CreateStore(reg->isInt, destIsIntVar); + m_builder.CreateStore(reg->intValue, destIntVar); + m_builder.CreateBr(mergeBlock); } @@ -714,10 +772,7 @@ void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTyp m_builder.SetInsertPoint(boolBlock); // Load bool - if (!targetTypeIsSingle) { - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - targetPtr = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - } + llvm::Value *targetPtr = castValue(reg, Compiler::StaticType::Bool); // Write bool to number value directly and change type llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); @@ -733,10 +788,7 @@ void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTyp m_builder.SetInsertPoint(stringBlock); // Load string pointer - if (!targetTypeIsSingle) { - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - targetPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); - } + llvm::Value *targetPtr = castValue(reg, Compiler::StaticType::String); // Create a new string, change type and assign llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); @@ -765,15 +817,16 @@ void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTyp m_builder.SetInsertPoint(numberBlock); // Load number - if (!targetTypeIsSingle) { - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - targetPtr = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - } + llvm::Value *doubleValue = castValue(reg, Compiler::StaticType::Number, NumberType::Double); // Write number to bool value directly and change type llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); - m_builder.CreateStore(targetPtr, ptr); + m_builder.CreateStore(doubleValue, ptr); m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Number)), destTypePtr); + + m_builder.CreateStore(reg->isInt, destIsIntVar); + m_builder.CreateStore(reg->intValue, destIntVar); + m_builder.CreateBr(mergeBlock); } @@ -784,10 +837,7 @@ void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTyp m_builder.SetInsertPoint(boolBlock); // Load bool - if (!targetTypeIsSingle) { - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - targetPtr = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - } + llvm::Value *targetPtr = castValue(reg, Compiler::StaticType::Bool); // Write bool to bool directly llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); @@ -802,10 +852,7 @@ void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTyp m_builder.SetInsertPoint(stringBlock); // Load string pointer - if (!targetTypeIsSingle) { - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - targetPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); - } + llvm::Value *targetPtr = castValue(reg, Compiler::StaticType::String); // Create a new string, change type and assign llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); @@ -834,17 +881,18 @@ void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTyp m_builder.SetInsertPoint(numberBlock); // Load number - if (!targetTypeIsSingle) { - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - targetPtr = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - } + llvm::Value *doubleValue = castValue(reg, Compiler::StaticType::Number, NumberType::Double); // Free the string, write the number and change type llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); llvm::Value *destStringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); m_builder.CreateCall(m_functions.resolve_string_pool_free(), destStringPtr); - m_builder.CreateStore(targetPtr, ptr); + m_builder.CreateStore(doubleValue, ptr); m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Number)), destTypePtr); + + m_builder.CreateStore(reg->isInt, destIsIntVar); + m_builder.CreateStore(reg->intValue, destIntVar); + m_builder.CreateBr(mergeBlock); } @@ -855,10 +903,7 @@ void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTyp m_builder.SetInsertPoint(boolBlock); // Load bool - if (!targetTypeIsSingle) { - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - targetPtr = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - } + llvm::Value *targetPtr = castValue(reg, Compiler::StaticType::Bool); // Free the string, write the bool and change type llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); @@ -876,10 +921,7 @@ void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTyp m_builder.SetInsertPoint(stringBlock); // Load string pointer - if (!targetTypeIsSingle) { - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - targetPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); - } + llvm::Value *targetPtr = castValue(reg, Compiler::StaticType::String); // Assign string directly llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); @@ -896,10 +938,10 @@ void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTyp m_builder.SetInsertPoint(mergeBlock); } -void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTypePtr, LLVMRegister *reg, Compiler::StaticType targetType) +void LLVMBuildUtils::createValueStore(llvm::Value *destPtr, llvm::Value *destTypePtr, llvm::Value *destIsIntVar, llvm::Value *destIntVar, LLVMRegister *reg, Compiler::StaticType targetType) { // Same as createValueStore(), but the destination type is unknown at compile time - createValueStore(destPtr, destTypePtr, reg, Compiler::StaticType::Unknown, targetType); + createValueStore(destPtr, destTypePtr, destIsIntVar, destIntVar, reg, Compiler::StaticType::Unknown, targetType); } llvm::Value *LLVMBuildUtils::getValueTypePtr(llvm::Value *value) @@ -979,7 +1021,7 @@ llvm::Value *LLVMBuildUtils::createValue(LLVMRegister *reg) { if (reg->isConst()) { // Create a constant ValueData instance and store it - llvm::Constant *value = castConstValue(reg->constValue(), TYPE_MAP[reg->constValue().type()]); + llvm::Constant *value = castConstValue(reg->constValue(), TYPE_MAP[reg->constValue().type()], NumberType::Double); llvm::Value *ret = addAlloca(m_valueDataType); switch (reg->constValue().type()) { @@ -1009,7 +1051,7 @@ llvm::Value *LLVMBuildUtils::createValue(LLVMRegister *reg) return ret; } else if (reg->isRawValue) { - llvm::Value *value = castRawValue(reg, reg->type()); + llvm::Value *value = castRawValue(reg, reg->type(), NumberType::Double); llvm::Value *ret = addAlloca(m_valueDataType); // Store value @@ -1356,21 +1398,34 @@ void LLVMBuildUtils::createListMap() } } -llvm::Value *LLVMBuildUtils::castRawValue(LLVMRegister *reg, Compiler::StaticType targetType) +llvm::Value *LLVMBuildUtils::castRawValue(LLVMRegister *reg, Compiler::StaticType targetType, NumberType targetNumType) { - if (reg->type() == targetType) - return reg->value; + if (reg->type() == targetType) { + if (targetType == Compiler::StaticType::Number && targetNumType == NumberType::Int) { + llvm::Value *cast = m_builder.CreateFPToSI(reg->value, m_builder.getInt64Ty()); + return m_builder.CreateSelect(reg->isInt, reg->intValue, cast); + } else + return reg->value; + } switch (targetType) { case Compiler::StaticType::Number: switch (reg->type()) { case Compiler::StaticType::Bool: - // Cast bool to double - return m_builder.CreateUIToFP(reg->value, m_builder.getDoubleTy()); + // Cast bool to double/int + if (targetNumType == NumberType::Int) + return m_builder.CreateZExt(reg->value, m_builder.getInt64Ty()); + else + return m_builder.CreateUIToFP(reg->value, m_builder.getDoubleTy()); case Compiler::StaticType::String: { - // Convert string to double - return m_builder.CreateCall(m_functions.resolve_value_stringToDouble(), reg->value); + // Convert string to double/int + llvm::Value *doubleValue = m_builder.CreateCall(m_functions.resolve_value_stringToDouble(), reg->value); + + if (targetNumType == NumberType::Int) + return m_builder.CreateFPToSI(doubleValue, m_builder.getInt64Ty()); + else + return doubleValue; } default: @@ -1380,9 +1435,12 @@ llvm::Value *LLVMBuildUtils::castRawValue(LLVMRegister *reg, Compiler::StaticTyp case Compiler::StaticType::Bool: switch (reg->type()) { - case Compiler::StaticType::Number: - // Cast double to bool (true if != 0) - return m_builder.CreateFCmpONE(reg->value, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); + case Compiler::StaticType::Number: { + // Cast double/int to bool (true if != 0) + llvm::Value *intResult = m_builder.CreateICmpNE(reg->intValue, llvm::ConstantInt::get(m_builder.getInt64Ty(), 0, true)); + llvm::Value *doubleResult = m_builder.CreateFCmpONE(reg->value, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); + return m_builder.CreateSelect(reg->isInt, intResult, doubleResult); + } case Compiler::StaticType::String: // Convert string to bool @@ -1396,8 +1454,10 @@ llvm::Value *LLVMBuildUtils::castRawValue(LLVMRegister *reg, Compiler::StaticTyp case Compiler::StaticType::String: switch (reg->type()) { case Compiler::StaticType::Number: { - // Convert double to string - llvm::Value *ptr = m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), reg->value); + // Convert double/int to string + llvm::Value *intCast = m_builder.CreateSIToFP(reg->intValue, m_builder.getDoubleTy()); + llvm::Value *doubleValue = m_builder.CreateSelect(reg->isInt, intCast, reg->value); + llvm::Value *ptr = m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), doubleValue); freeStringLater(ptr); return ptr; } @@ -1423,12 +1483,17 @@ llvm::Value *LLVMBuildUtils::castRawValue(LLVMRegister *reg, Compiler::StaticTyp } } -llvm::Constant *LLVMBuildUtils::castConstValue(const Value &value, Compiler::StaticType targetType) +llvm::Constant *LLVMBuildUtils::castConstValue(const Value &value, Compiler::StaticType targetType, NumberType targetNumType) { switch (targetType) { case Compiler::StaticType::Number: { const double nan = std::numeric_limits::quiet_NaN(); - return llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(value.isNaN() ? nan : value.toDouble())); + const double num = value.toDouble(); + + if (targetNumType == NumberType::Int) + return llvm::ConstantInt::get(m_builder.getInt64Ty(), num, true); + else + return llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(value.isNaN() ? nan : num)); } case Compiler::StaticType::Bool: diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index 3dfafe54..7d8d1427 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -27,9 +27,15 @@ class LLVMBuildUtils LT }; + enum class NumberType + { + Int, + Double + }; + LLVMBuildUtils(LLVMCompilerContext *ctx, llvm::IRBuilder<> &builder, Compiler::CodeType codeType); - void init(llvm::Function *function, BlockPrototype *procedurePrototype, bool warp); + void init(llvm::Function *function, BlockPrototype *procedurePrototype, bool warp, const std::vector> ®s); void end(LLVMInstruction *lastInstruction, LLVMRegister *lastConstant); LLVMCompilerContext *compilerCtx() const; @@ -80,13 +86,21 @@ class LLVMBuildUtils static bool isSingleType(Compiler::StaticType type); llvm::Value *addAlloca(llvm::Type *type); - llvm::Value *castValue(LLVMRegister *reg, Compiler::StaticType targetType); + llvm::Value *castValue(LLVMRegister *reg, Compiler::StaticType targetType, NumberType targetNumType = NumberType::Double); llvm::Type *getType(Compiler::StaticType type, bool isReturnType); llvm::Value *isNaN(llvm::Value *num); llvm::Value *removeNaN(llvm::Value *num); - void createValueStore(llvm::Value *destPtr, llvm::Value *destTypePtr, LLVMRegister *reg, Compiler::StaticType destType, Compiler::StaticType targetType); - void createValueStore(llvm::Value *destPtr, llvm::Value *destTypePtr, LLVMRegister *reg, Compiler::StaticType targetType); + void createValueStore( + llvm::Value *destPtr, + llvm::Value *destTypePtr, + llvm::Value *destIsIntVar, + llvm::Value *destIntVar, + LLVMRegister *reg, + Compiler::StaticType destType, + Compiler::StaticType targetType); + + void createValueStore(llvm::Value *destPtr, llvm::Value *destTypePtr, llvm::Value *destIsIntVar, llvm::Value *destIntVar, LLVMRegister *reg, Compiler::StaticType targetType); llvm::Value *getValueTypePtr(llvm::Value *value); llvm::Value *getValueTypePtr(LLVMRegister *reg); @@ -106,8 +120,8 @@ class LLVMBuildUtils void createVariableMap(); void createListMap(); - llvm::Value *castRawValue(LLVMRegister *reg, Compiler::StaticType targetType); - llvm::Constant *castConstValue(const Value &value, Compiler::StaticType targetType); + llvm::Value *castRawValue(LLVMRegister *reg, Compiler::StaticType targetType, NumberType targetNumType); + llvm::Constant *castConstValue(const Value &value, Compiler::StaticType targetType, NumberType targetNumType); void createValueCopy(llvm::Value *source, llvm::Value *target); void copyStructField(llvm::Value *source, llvm::Value *target, int index, llvm::StructType *structType, llvm::Type *fieldType); diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index e0636c13..ec1d4109 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -80,7 +80,7 @@ std::shared_ptr LLVMCodeBuilder::build() llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_llvmCtx, "entry", m_function); m_builder.SetInsertPoint(entry); - m_utils.init(m_function, m_procedurePrototype, m_warp); + m_utils.init(m_function, m_procedurePrototype, m_warp, m_regs); // Build recorded instructions LLVMInstruction *ins = m_instructions.first(); diff --git a/src/engine/internal/llvm/llvmregister.h b/src/engine/internal/llvm/llvmregister.h index 31840c34..d4e2eb78 100644 --- a/src/engine/internal/llvm/llvmregister.h +++ b/src/engine/internal/llvm/llvmregister.h @@ -32,7 +32,12 @@ struct LLVMRegister : public virtual CompilerValue llvm::Value *value = nullptr; llvm::Value *typeVar = nullptr; + + llvm::Value *isInt = nullptr; + llvm::Value *intValue = nullptr; + bool isRawValue = false; + std::shared_ptr instruction; }; diff --git a/src/engine/internal/llvm/llvmvariableptr.h b/src/engine/internal/llvm/llvmvariableptr.h index cae8653c..698649c8 100644 --- a/src/engine/internal/llvm/llvmvariableptr.h +++ b/src/engine/internal/llvm/llvmvariableptr.h @@ -20,6 +20,9 @@ struct LLVMVariablePtr { llvm::Value *heapPtr = nullptr; llvm::Value *stackPtr = nullptr; + + llvm::Value *isInt = nullptr; + llvm::Value *intValue = nullptr; }; } // namespace libscratchcpp From 78f993b1d653c10facf6a309fcec7cddb740aebd Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 25 Aug 2025 22:33:50 +0200 Subject: [PATCH 26/42] Use ValueData struct for local variables --- .../internal/llvm/instructions/variables.cpp | 55 ++++--------------- src/engine/internal/llvm/llvmcodebuilder.cpp | 11 +++- 2 files changed, 22 insertions(+), 44 deletions(-) diff --git a/src/engine/internal/llvm/instructions/variables.cpp b/src/engine/internal/llvm/instructions/variables.cpp index 2d8dae2c..513a233f 100644 --- a/src/engine/internal/llvm/instructions/variables.cpp +++ b/src/engine/internal/llvm/instructions/variables.cpp @@ -3,6 +3,7 @@ #include "variables.h" #include "../llvminstruction.h" #include "../llvmbuildutils.h" +#include "../llvmconstantregister.h" using namespace libscratchcpp; using namespace libscratchcpp::llvmins; @@ -43,31 +44,9 @@ ProcessResult Variables::process(LLVMInstruction *ins) LLVMInstruction *Variables::buildCreateLocalVariable(LLVMInstruction *ins) { assert(ins->args.empty()); - llvm::Type *type = nullptr; - switch (ins->functionReturnReg->type()) { - case Compiler::StaticType::Number: - type = m_builder.getDoubleTy(); - break; - - case Compiler::StaticType::Bool: - type = m_builder.getInt1Ty(); - break; - - case Compiler::StaticType::String: - std::cerr << "error: local variables do not support string type" << std::endl; - break; - - case Compiler::StaticType::Pointer: - std::cerr << "error: local variables do not support pointer type" << std::endl; - break; - - default: - assert(false); - break; - } - - ins->functionReturnReg->value = m_utils.addAlloca(type); + LLVMConstantRegister null(ins->functionReturnReg->type(), Value()); + ins->functionReturnReg->value = m_utils.createValue(&null); return ins->next; } @@ -76,8 +55,14 @@ LLVMInstruction *Variables::buildWriteLocalVariable(LLVMInstruction *ins) assert(ins->args.size() == 2); const auto &arg1 = ins->args[0]; const auto &arg2 = ins->args[1]; - llvm::Value *converted = m_utils.castValue(arg2.second, arg2.first); - m_builder.CreateStore(converted, arg1.second->value); + llvm::Value *typeVar = m_utils.addAlloca(m_builder.getInt32Ty()); + m_builder.CreateStore(m_builder.getInt32(static_cast(m_utils.mapType(arg2.first))), typeVar); + + // TODO: Add integer support for local variables + llvm::Value *isIntVar = m_utils.addAlloca(m_builder.getInt1Ty()); + llvm::Value *intVar = m_utils.addAlloca(m_builder.getInt64Ty()); + + m_utils.createValueStore(arg1.second->value, typeVar, isIntVar, intVar, arg2.second, arg2.first, arg2.first); return ins->next; } @@ -85,23 +70,7 @@ LLVMInstruction *Variables::buildReadLocalVariable(LLVMInstruction *ins) { assert(ins->args.size() == 1); const auto &arg = ins->args[0]; - llvm::Type *type = nullptr; - - switch (ins->functionReturnReg->type()) { - case Compiler::StaticType::Number: - type = m_builder.getDoubleTy(); - break; - - case Compiler::StaticType::Bool: - type = m_builder.getInt1Ty(); - break; - - default: - assert(false); - break; - } - - ins->functionReturnReg->value = m_builder.CreateLoad(type, arg.second->value); + ins->functionReturnReg->value = m_utils.castValue(arg.second, ins->functionReturnReg->type()); return ins->next; } diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index ec1d4109..32b861a2 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -158,7 +158,16 @@ CompilerValue *LLVMCodeBuilder::addLoopIndex() CompilerValue *LLVMCodeBuilder::addLocalVariableValue(CompilerLocalVariable *variable) { - return createOp(LLVMInstruction::Type::ReadLocalVariable, variable->type(), variable->type(), { variable->ptr() }); + auto ins = std::make_shared(LLVMInstruction::Type::ReadLocalVariable, m_loopCondition); + + ins->args.push_back({ variable->type(), dynamic_cast(variable->ptr()) }); + + auto ret = std::make_shared(variable->type()); + ret->isRawValue = false; + ins->functionReturnReg = ret.get(); + + m_instructions.addInstruction(ins); + return addReg(ret, ins); } CompilerValue *LLVMCodeBuilder::addVariableValue(Variable *variable) From f28ddd469e8ad255a26d1a58dad99ace4cf78679 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 25 Aug 2025 23:40:15 +0200 Subject: [PATCH 27/42] Add integer support for local variables --- src/engine/internal/llvm/CMakeLists.txt | 1 + .../internal/llvm/instructions/variables.cpp | 16 +++++++++----- src/engine/internal/llvm/llvmbuildutils.cpp | 12 +++++++++++ src/engine/internal/llvm/llvmbuildutils.h | 5 +++++ src/engine/internal/llvm/llvmcodebuilder.cpp | 4 ++++ src/engine/internal/llvm/llvminstruction.h | 2 ++ .../internal/llvm/llvmlocalvariableinfo.h | 21 +++++++++++++++++++ 7 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 src/engine/internal/llvm/llvmlocalvariableinfo.h diff --git a/src/engine/internal/llvm/CMakeLists.txt b/src/engine/internal/llvm/CMakeLists.txt index 54b79e88..818286c2 100644 --- a/src/engine/internal/llvm/CMakeLists.txt +++ b/src/engine/internal/llvm/CMakeLists.txt @@ -15,6 +15,7 @@ target_sources(scratchcpp llvmloop.h llvmcoroutine.cpp llvmcoroutine.h + llvmlocalvariableinfo.h llvmvariableptr.h llvmlistptr.h llvmtypes.cpp diff --git a/src/engine/internal/llvm/instructions/variables.cpp b/src/engine/internal/llvm/instructions/variables.cpp index 513a233f..4abdb170 100644 --- a/src/engine/internal/llvm/instructions/variables.cpp +++ b/src/engine/internal/llvm/instructions/variables.cpp @@ -44,6 +44,12 @@ ProcessResult Variables::process(LLVMInstruction *ins) LLVMInstruction *Variables::buildCreateLocalVariable(LLVMInstruction *ins) { assert(ins->args.empty()); + LLVMLocalVariableInfo *info = ins->localVarInfo; + + info->isInt = m_utils.addAlloca(m_builder.getInt1Ty()); + info->intValue = m_utils.addAlloca(m_builder.getInt64Ty()); + m_builder.CreateStore(m_builder.getInt1(false), info->isInt); + m_builder.CreateStore(llvm::ConstantInt::get(m_builder.getInt64Ty(), 0, true), info->isInt); LLVMConstantRegister null(ins->functionReturnReg->type(), Value()); ins->functionReturnReg->value = m_utils.createValue(&null); @@ -55,14 +61,11 @@ LLVMInstruction *Variables::buildWriteLocalVariable(LLVMInstruction *ins) assert(ins->args.size() == 2); const auto &arg1 = ins->args[0]; const auto &arg2 = ins->args[1]; + LLVMLocalVariableInfo *info = ins->localVarInfo; llvm::Value *typeVar = m_utils.addAlloca(m_builder.getInt32Ty()); m_builder.CreateStore(m_builder.getInt32(static_cast(m_utils.mapType(arg2.first))), typeVar); - // TODO: Add integer support for local variables - llvm::Value *isIntVar = m_utils.addAlloca(m_builder.getInt1Ty()); - llvm::Value *intVar = m_utils.addAlloca(m_builder.getInt64Ty()); - - m_utils.createValueStore(arg1.second->value, typeVar, isIntVar, intVar, arg2.second, arg2.first, arg2.first); + m_utils.createValueStore(arg1.second->value, typeVar, info->isInt, info->intValue, arg2.second, arg2.first, arg2.first); return ins->next; } @@ -70,7 +73,10 @@ LLVMInstruction *Variables::buildReadLocalVariable(LLVMInstruction *ins) { assert(ins->args.size() == 1); const auto &arg = ins->args[0]; + LLVMLocalVariableInfo *info = ins->localVarInfo; ins->functionReturnReg->value = m_utils.castValue(arg.second, ins->functionReturnReg->type()); + ins->functionReturnReg->isInt = m_builder.CreateLoad(m_builder.getInt1Ty(), info->isInt); + ins->functionReturnReg->intValue = m_builder.CreateLoad(m_builder.getInt64Ty(), info->intValue); return ins->next; } diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index e53082b6..152b6e88 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -298,6 +298,12 @@ LLVMCoroutine *LLVMBuildUtils::coroutine() const return m_coroutine.get(); } +void LLVMBuildUtils::createLocalVariableInfo(CompilerLocalVariable *variable) +{ + if (m_localVariables.find(variable) == m_localVariables.cend()) + m_localVariables[variable] = LLVMLocalVariableInfo(); +} + void LLVMBuildUtils::createVariablePtr(Variable *variable) { if (m_variablePtrs.find(variable) == m_variablePtrs.cend()) @@ -310,6 +316,12 @@ void LLVMBuildUtils::createListPtr(List *list) m_listPtrs[list] = LLVMListPtr(); } +LLVMLocalVariableInfo &LLVMBuildUtils::localVariableInfo(CompilerLocalVariable *variable) +{ + assert(m_localVariables.find(variable) != m_localVariables.cend()); + return m_localVariables[variable]; +} + LLVMVariablePtr &LLVMBuildUtils::variablePtr(Variable *variable) { assert(m_variablePtrs.find(variable) != m_variablePtrs.cend()); diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index 7d8d1427..8835384d 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -5,6 +5,7 @@ #include #include "llvmfunctions.h" +#include "llvmlocalvariableinfo.h" #include "llvmvariableptr.h" #include "llvmlistptr.h" #include "llvmcoroutine.h" @@ -61,9 +62,11 @@ class LLVMBuildUtils LLVMCoroutine *coroutine() const; + void createLocalVariableInfo(CompilerLocalVariable *variable); void createVariablePtr(Variable *variable); void createListPtr(List *list); + LLVMLocalVariableInfo &localVariableInfo(CompilerLocalVariable *variable); LLVMVariablePtr &variablePtr(Variable *variable); LLVMListPtr &listPtr(List *list); @@ -154,6 +157,8 @@ class LLVMBuildUtils std::unique_ptr m_coroutine; + std::unordered_map m_localVariables; + std::unordered_map m_targetVariableMap; std::unordered_map m_variablePtrs; diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index 32b861a2..07dcf003 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -159,6 +159,7 @@ CompilerValue *LLVMCodeBuilder::addLoopIndex() CompilerValue *LLVMCodeBuilder::addLocalVariableValue(CompilerLocalVariable *variable) { auto ins = std::make_shared(LLVMInstruction::Type::ReadLocalVariable, m_loopCondition); + ins->localVarInfo = &m_utils.localVariableInfo(variable); ins->args.push_back({ variable->type(), dynamic_cast(variable->ptr()) }); @@ -425,12 +426,15 @@ CompilerLocalVariable *LLVMCodeBuilder::createLocalVariable(Compiler::StaticType CompilerValue *ptr = createOp(LLVMInstruction::Type::CreateLocalVariable, type); auto var = std::make_shared(ptr); m_localVars.push_back(var); + m_utils.createLocalVariableInfo(var.get()); + m_instructions.last()->localVarInfo = &m_utils.localVariableInfo(var.get()); return var.get(); } void LLVMCodeBuilder::createLocalVariableWrite(CompilerLocalVariable *variable, CompilerValue *value) { createOp(LLVMInstruction::Type::WriteLocalVariable, Compiler::StaticType::Void, variable->type(), { variable->ptr(), value }); + m_instructions.last()->localVarInfo = &m_utils.localVariableInfo(variable); } void LLVMCodeBuilder::createVariableWrite(Variable *variable, CompilerValue *value) diff --git a/src/engine/internal/llvm/llvminstruction.h b/src/engine/internal/llvm/llvminstruction.h index d4875d7d..558258aa 100644 --- a/src/engine/internal/llvm/llvminstruction.h +++ b/src/engine/internal/llvm/llvminstruction.h @@ -9,6 +9,7 @@ namespace libscratchcpp { +class LLVMLocalVariableInfo; class BlockPrototype; struct LLVMInstruction @@ -93,6 +94,7 @@ struct LLVMInstruction LLVMRegister *functionReturnReg = nullptr; bool functionTargetArg = false; // whether to add target ptr to function parameters bool functionCtxArg = false; // whether to add execution context ptr to function parameters + LLVMLocalVariableInfo *localVarInfo = nullptr; // for local variables Variable *targetVariable = nullptr; // for variables List *targetList = nullptr; // for lists Compiler::StaticType targetType = Compiler::StaticType::Unknown; // variable or list type (before read/write operation) diff --git a/src/engine/internal/llvm/llvmlocalvariableinfo.h b/src/engine/internal/llvm/llvmlocalvariableinfo.h new file mode 100644 index 00000000..d0f0d465 --- /dev/null +++ b/src/engine/internal/llvm/llvmlocalvariableinfo.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +namespace llvm +{ + +class Value; + +} + +namespace libscratchcpp +{ + +struct LLVMLocalVariableInfo +{ + llvm::Value *isInt = nullptr; + llvm::Value *intValue = nullptr; +}; + +} // namespace libscratchcpp From 5cf8bfd40b427777064d2ebf19f97283914ac347 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 25 Aug 2025 23:49:40 +0200 Subject: [PATCH 28/42] Add integer support to control instructions --- src/engine/internal/llvm/instructions/control.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/engine/internal/llvm/instructions/control.cpp b/src/engine/internal/llvm/instructions/control.cpp index ce1344ae..b41dd292 100644 --- a/src/engine/internal/llvm/instructions/control.cpp +++ b/src/engine/internal/llvm/instructions/control.cpp @@ -92,6 +92,8 @@ LLVMInstruction *Control::buildSelect(LLVMInstruction *ins) } ins->functionReturnReg->value = m_builder.CreateSelect(cond, trueValue, falseValue); + ins->functionReturnReg->isInt = m_builder.CreateSelect(cond, arg2.second->isInt, arg3.second->isInt); + ins->functionReturnReg->intValue = m_builder.CreateSelect(cond, arg2.second->intValue, arg3.second->intValue); return ins->next; } @@ -245,6 +247,8 @@ LLVMInstruction *Control::buildLoopIndex(LLVMInstruction *ins) LLVMLoop &loop = m_utils.loops().back(); llvm::Value *index = m_builder.CreateLoad(m_builder.getInt64Ty(), loop.index); ins->functionReturnReg->value = m_builder.CreateUIToFP(index, m_builder.getDoubleTy()); + ins->functionReturnReg->intValue = index; + ins->functionReturnReg->isInt = m_builder.getInt1(true); return ins->next; } From 54c7f072440be1a701dcc2311bd5b1251d78a7eb Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 26 Aug 2025 14:33:11 +0200 Subject: [PATCH 29/42] Add integer support to math instructions --- .../internal/llvm/instructions/math.cpp | 158 ++++++++++++++---- src/engine/internal/llvm/llvmfunctions.cpp | 6 +- src/engine/internal/llvm/llvmfunctions.h | 2 +- test/llvm/operators/math/mod_test.cpp | 20 +++ test/llvm/operators/math/round_test.cpp | 10 ++ 5 files changed, 162 insertions(+), 34 deletions(-) diff --git a/src/engine/internal/llvm/instructions/math.cpp b/src/engine/internal/llvm/instructions/math.cpp index 1a6c452c..aeb95cc3 100644 --- a/src/engine/internal/llvm/instructions/math.cpp +++ b/src/engine/internal/llvm/instructions/math.cpp @@ -113,9 +113,15 @@ LLVMInstruction *Math::buildAdd(LLVMInstruction *ins) assert(ins->args.size() == 2); const auto &arg1 = ins->args[0]; const auto &arg2 = ins->args[1]; - llvm::Value *num1 = m_utils.removeNaN(m_utils.castValue(arg1.second, arg1.first)); - llvm::Value *num2 = m_utils.removeNaN(m_utils.castValue(arg2.second, arg2.first)); - ins->functionReturnReg->value = m_builder.CreateFAdd(num1, num2); + + llvm::Value *double1 = m_utils.removeNaN(m_utils.castValue(arg1.second, arg1.first, LLVMBuildUtils::NumberType::Double)); + llvm::Value *double2 = m_utils.removeNaN(m_utils.castValue(arg2.second, arg2.first, LLVMBuildUtils::NumberType::Double)); + ins->functionReturnReg->value = m_builder.CreateFAdd(double1, double2); + + llvm::Value *int1 = m_utils.castValue(arg1.second, arg1.first, LLVMBuildUtils::NumberType::Int); + llvm::Value *int2 = m_utils.castValue(arg2.second, arg2.first, LLVMBuildUtils::NumberType::Int); + ins->functionReturnReg->isInt = m_builder.CreateAnd(arg1.second->isInt, arg2.second->isInt); + ins->functionReturnReg->intValue = m_builder.CreateAdd(int1, int2); return ins->next; } @@ -125,9 +131,15 @@ LLVMInstruction *Math::buildSub(LLVMInstruction *ins) assert(ins->args.size() == 2); const auto &arg1 = ins->args[0]; const auto &arg2 = ins->args[1]; - llvm::Value *num1 = m_utils.removeNaN(m_utils.castValue(arg1.second, arg1.first)); - llvm::Value *num2 = m_utils.removeNaN(m_utils.castValue(arg2.second, arg2.first)); - ins->functionReturnReg->value = m_builder.CreateFSub(num1, num2); + + llvm::Value *double1 = m_utils.removeNaN(m_utils.castValue(arg1.second, arg1.first, LLVMBuildUtils::NumberType::Double)); + llvm::Value *double2 = m_utils.removeNaN(m_utils.castValue(arg2.second, arg2.first, LLVMBuildUtils::NumberType::Double)); + ins->functionReturnReg->value = m_builder.CreateFSub(double1, double2); + + llvm::Value *int1 = m_utils.castValue(arg1.second, arg1.first, LLVMBuildUtils::NumberType::Int); + llvm::Value *int2 = m_utils.castValue(arg2.second, arg2.first, LLVMBuildUtils::NumberType::Int); + ins->functionReturnReg->isInt = m_builder.CreateAnd(arg1.second->isInt, arg2.second->isInt); + ins->functionReturnReg->intValue = m_builder.CreateSub(int1, int2); return ins->next; } @@ -137,9 +149,15 @@ LLVMInstruction *Math::buildMul(LLVMInstruction *ins) assert(ins->args.size() == 2); const auto &arg1 = ins->args[0]; const auto &arg2 = ins->args[1]; - llvm::Value *num1 = m_utils.removeNaN(m_utils.castValue(arg1.second, arg1.first)); - llvm::Value *num2 = m_utils.removeNaN(m_utils.castValue(arg2.second, arg2.first)); - ins->functionReturnReg->value = m_builder.CreateFMul(num1, num2); + + llvm::Value *double1 = m_utils.removeNaN(m_utils.castValue(arg1.second, arg1.first, LLVMBuildUtils::NumberType::Double)); + llvm::Value *double2 = m_utils.removeNaN(m_utils.castValue(arg2.second, arg2.first, LLVMBuildUtils::NumberType::Double)); + ins->functionReturnReg->value = m_builder.CreateFMul(double1, double2); + + llvm::Value *int1 = m_utils.castValue(arg1.second, arg1.first, LLVMBuildUtils::NumberType::Int); + llvm::Value *int2 = m_utils.castValue(arg2.second, arg2.first, LLVMBuildUtils::NumberType::Int); + ins->functionReturnReg->isInt = m_builder.CreateAnd(arg1.second->isInt, arg2.second->isInt); + ins->functionReturnReg->intValue = m_builder.CreateMul(int1, int2); return ins->next; } @@ -197,7 +215,10 @@ LLVMInstruction *Math::buildRandomInt(LLVMInstruction *ins) const auto &arg2 = ins->args[1]; llvm::Value *from = m_builder.CreateFPToSI(m_utils.castValue(arg1.second, arg1.first), m_builder.getInt64Ty()); llvm::Value *to = m_builder.CreateFPToSI(m_utils.castValue(arg2.second, arg2.first), m_builder.getInt64Ty()); - ins->functionReturnReg->value = m_builder.CreateCall(m_utils.functions().resolve_llvm_random_long(), { m_utils.executionContextPtr(), from, to }); + llvm::Value *intValue = m_builder.CreateCall(m_utils.functions().resolve_llvm_random_int64(), { m_utils.executionContextPtr(), from, to }); + ins->functionReturnReg->value = m_builder.CreateSIToFP(intValue, m_builder.getDoubleTy()); + ins->functionReturnReg->intValue = intValue; + ins->functionReturnReg->isInt = m_builder.getInt1(true); return ins->next; } @@ -207,13 +228,49 @@ LLVMInstruction *Math::buildMod(LLVMInstruction *ins) assert(ins->args.size() == 2); const auto &arg1 = ins->args[0]; const auto &arg2 = ins->args[1]; - // rem(a, b) / b < 0.0 ? rem(a, b) + b : rem(a, b) - llvm::Constant *zero = llvm::ConstantFP::get(m_utils.llvmCtx(), llvm::APFloat(0.0)); - llvm::Value *num1 = m_utils.removeNaN(m_utils.castValue(arg1.second, arg1.first)); - llvm::Value *num2 = m_utils.removeNaN(m_utils.castValue(arg2.second, arg2.first)); - llvm::Value *value = m_builder.CreateFRem(num1, num2); // rem(a, b) - llvm::Value *cond = m_builder.CreateFCmpOLT(m_builder.CreateFDiv(value, num2), zero); // rem(a, b) / b < 0.0 // rem(a, b) - ins->functionReturnReg->value = m_builder.CreateSelect(cond, m_builder.CreateFAdd(value, num2), value); + + // double: rem(a, b) / b < 0.0 ? rem(a, b) + b : rem(a, b) + llvm::Constant *doubleZero = llvm::ConstantFP::get(m_utils.llvmCtx(), llvm::APFloat(0.0)); + llvm::Value *double1 = m_utils.removeNaN(m_utils.castValue(arg1.second, arg1.first)); + llvm::Value *double2 = m_utils.removeNaN(m_utils.castValue(arg2.second, arg2.first)); + llvm::Value *doubleRem = m_builder.CreateFRem(double1, double2); // rem(a, b) + llvm::Value *doubleCond = m_builder.CreateFCmpOLT(m_builder.CreateFDiv(doubleRem, double2), doubleZero); // rem(a, b) / b < 0.0 + ins->functionReturnReg->value = m_builder.CreateSelect(doubleCond, m_builder.CreateFAdd(doubleRem, double2), doubleRem); + + // int: b == 0 ? 0 (double fallback) : ((rem(a, b) < 0) != (b < 0) ? rem(a, b) + b : rem(a, b)) + llvm::Constant *intZero = llvm::ConstantInt::get(m_builder.getInt64Ty(), 0, true); + llvm::Value *int1 = m_utils.castValue(arg1.second, arg1.first, LLVMBuildUtils::NumberType::Int); + llvm::Value *int2 = m_utils.castValue(arg2.second, arg2.first, LLVMBuildUtils::NumberType::Int); + llvm::Value *nanResult = m_builder.CreateICmpEQ(int2, intZero); + + llvm::BasicBlock *nanBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); + llvm::BasicBlock *intBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); + llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); + m_builder.CreateCondBr(nanResult, nanBlock, intBlock); + + m_builder.SetInsertPoint(nanBlock); + llvm::Value *noInt = m_builder.getInt1(false); + m_builder.CreateBr(nextBlock); + + m_builder.SetInsertPoint(intBlock); + llvm::Value *isInt = m_builder.CreateAnd(arg1.second->isInt, arg2.second->isInt); + llvm::Value *intRem = m_builder.CreateSRem(int1, int2); // rem(a, b) + llvm::Value *intCond = m_builder.CreateICmpSLT(m_builder.CreateSDiv(intRem, int2), intZero); // rem(a, b) / b < 0 + llvm::Value *intResult = m_builder.CreateSelect(intCond, m_builder.CreateAdd(intRem, int2), intRem); + m_builder.CreateBr(nextBlock); + + m_builder.SetInsertPoint(nextBlock); + + llvm::PHINode *resultPhi = m_builder.CreatePHI(m_builder.getInt64Ty(), 2); + resultPhi->addIncoming(intZero, nanBlock); + resultPhi->addIncoming(intResult, intBlock); + + llvm::PHINode *isIntPhi = m_builder.CreatePHI(m_builder.getInt1Ty(), 2); + isIntPhi->addIncoming(noInt, nanBlock); + isIntPhi->addIncoming(isInt, intBlock); + + ins->functionReturnReg->intValue = resultPhi; + ins->functionReturnReg->isInt = isIntPhi; return ins->next; } @@ -225,18 +282,29 @@ LLVMInstruction *Math::buildRound(LLVMInstruction *ins) assert(ins->args.size() == 1); const auto &arg = ins->args[0]; - // x >= 0.0 ? round(x) : (x >= -0.5 ? -0.0 : floor(x + 0.5)) + + // double: x >= 0.0 ? round(x) : (x >= -0.5 ? -0.0 : floor(x + 0.5)) llvm::Constant *zero = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); llvm::Constant *negativeZero = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(-0.0)); llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::round, m_builder.getDoubleTy()); llvm::Function *floorFunc = llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::floor, m_builder.getDoubleTy()); - llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); - llvm::Value *notNegative = m_builder.CreateFCmpOGE(num, zero); // num >= 0.0 - llvm::Value *roundNum = m_builder.CreateCall(roundFunc, num); // round(num) - llvm::Value *negativeCond = m_builder.CreateFCmpOGE(num, llvm::ConstantFP::get(llvmCtx, llvm::APFloat(-0.5))); // num >= -0.5 - llvm::Value *negativeRound = m_builder.CreateCall(floorFunc, m_builder.CreateFAdd(num, llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.5)))); // floor(x + 0.5) + llvm::Value *doubleValue = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + llvm::Value *notNegative = m_builder.CreateFCmpOGE(doubleValue, zero); // num >= 0.0 + llvm::Value *roundNum = m_builder.CreateCall(roundFunc, doubleValue); // round(num) + llvm::Value *negativeCond = m_builder.CreateFCmpOGE(doubleValue, llvm::ConstantFP::get(llvmCtx, llvm::APFloat(-0.5))); // num >= -0.5 + llvm::Value *negativeRound = m_builder.CreateCall(floorFunc, m_builder.CreateFAdd(doubleValue, llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.5)))); // floor(x + 0.5) ins->functionReturnReg->value = m_builder.CreateSelect(notNegative, roundNum, m_builder.CreateSelect(negativeCond, negativeZero, negativeRound)); + // int: doubleX == inf || doubleX == -inf ? doubleX : intX + llvm::Constant *posInf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), false); + llvm::Constant *negInf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), true); + llvm::Value *isInt = arg.second->isInt; + llvm::Value *intValue = arg.second->intValue; + llvm::Value *isNotInf = m_builder.CreateAnd(m_builder.CreateFCmpONE(doubleValue, posInf), m_builder.CreateFCmpONE(doubleValue, negInf)); + llvm::Value *cast = m_builder.CreateFPToSI(ins->functionReturnReg->value, m_builder.getInt64Ty()); + ins->functionReturnReg->isInt = isNotInf; + ins->functionReturnReg->intValue = m_builder.CreateSelect(isInt, intValue, cast); + return ins->next; } @@ -244,9 +312,15 @@ LLVMInstruction *Math::buildAbs(LLVMInstruction *ins) { assert(ins->args.size() == 1); const auto &arg = ins->args[0]; - llvm::Function *absFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::fabs, m_builder.getDoubleTy()); - llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); - ins->functionReturnReg->value = m_builder.CreateCall(absFunc, num); + + llvm::Function *fabsFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::fabs, m_builder.getDoubleTy()); + llvm::Value *doubleValue = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + ins->functionReturnReg->value = m_builder.CreateCall(fabsFunc, doubleValue); + + llvm::Function *absFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::abs, m_builder.getInt64Ty()); + llvm::Value *intValue = arg.second->intValue; + ins->functionReturnReg->isInt = arg.second->isInt; + ins->functionReturnReg->intValue = m_builder.CreateCall(absFunc, { intValue, m_builder.getInt1(false) }); return ins->next; } @@ -255,9 +329,21 @@ LLVMInstruction *Math::buildFloor(LLVMInstruction *ins) { assert(ins->args.size() == 1); const auto &arg = ins->args[0]; + + // double: floor(doubleX) llvm::Function *floorFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::floor, m_builder.getDoubleTy()); - llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); - ins->functionReturnReg->value = m_builder.CreateCall(floorFunc, num); + llvm::Value *doubleValue = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + ins->functionReturnReg->value = m_builder.CreateCall(floorFunc, doubleValue); + + // int: doubleX == inf || doubleX == -inf ? doubleX : intX + llvm::Constant *posInf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), false); + llvm::Constant *negInf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), true); + llvm::Value *isInt = arg.second->isInt; + llvm::Value *intValue = arg.second->intValue; + llvm::Value *isNotInf = m_builder.CreateAnd(m_builder.CreateFCmpONE(doubleValue, posInf), m_builder.CreateFCmpONE(doubleValue, negInf)); + llvm::Value *cast = m_builder.CreateFPToSI(ins->functionReturnReg->value, m_builder.getInt64Ty()); + ins->functionReturnReg->isInt = isNotInf; + ins->functionReturnReg->intValue = m_builder.CreateSelect(isInt, intValue, cast); return ins->next; } @@ -266,9 +352,21 @@ LLVMInstruction *Math::buildCeil(LLVMInstruction *ins) { assert(ins->args.size() == 1); const auto &arg = ins->args[0]; + + // double: ceil(doubleX) llvm::Function *ceilFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::ceil, m_builder.getDoubleTy()); - llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); - ins->functionReturnReg->value = m_builder.CreateCall(ceilFunc, num); + llvm::Value *doubleValue = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + ins->functionReturnReg->value = m_builder.CreateCall(ceilFunc, doubleValue); + + // int: doubleX == inf || doubleX == -inf ? doubleX : intX + llvm::Constant *posInf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), false); + llvm::Constant *negInf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), true); + llvm::Value *isInt = arg.second->isInt; + llvm::Value *intValue = arg.second->intValue; + llvm::Value *isNotInf = m_builder.CreateAnd(m_builder.CreateFCmpONE(doubleValue, posInf), m_builder.CreateFCmpONE(doubleValue, negInf)); + llvm::Value *cast = m_builder.CreateFPToSI(ins->functionReturnReg->value, m_builder.getInt64Ty()); + ins->functionReturnReg->isInt = isNotInf; + ins->functionReturnReg->intValue = m_builder.CreateSelect(isInt, intValue, cast); return ins->next; } diff --git a/src/engine/internal/llvm/llvmfunctions.cpp b/src/engine/internal/llvm/llvmfunctions.cpp index 899254aa..0a9d4a3f 100644 --- a/src/engine/internal/llvm/llvmfunctions.cpp +++ b/src/engine/internal/llvm/llvmfunctions.cpp @@ -21,7 +21,7 @@ extern "C" return value_doubleIsInt(from) && value_doubleIsInt(to) ? ctx->rng()->randint(from, to) : ctx->rng()->randintDouble(from, to); } - double llvm_random_long(ExecutionContext *ctx, long from, long to) + int64_t llvm_random_int64(ExecutionContext *ctx, int64_t from, int64_t to) { return ctx->rng()->randint(from, to); } @@ -240,10 +240,10 @@ llvm::FunctionCallee LLVMFunctions::resolve_llvm_random_double() return resolveFunction("llvm_random_double", llvm::FunctionType::get(m_builder->getDoubleTy(), { pointerType, m_builder->getDoubleTy(), m_builder->getDoubleTy() }, false)); } -llvm::FunctionCallee LLVMFunctions::resolve_llvm_random_long() +llvm::FunctionCallee LLVMFunctions::resolve_llvm_random_int64() { llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); - return resolveFunction("llvm_random_long", llvm::FunctionType::get(m_builder->getDoubleTy(), { pointerType, m_builder->getInt64Ty(), m_builder->getInt64Ty() }, false)); + return resolveFunction("llvm_random_int64", llvm::FunctionType::get(m_builder->getInt64Ty(), { pointerType, m_builder->getInt64Ty(), m_builder->getInt64Ty() }, false)); } llvm::FunctionCallee LLVMFunctions::resolve_llvm_random_bool() diff --git a/src/engine/internal/llvm/llvmfunctions.h b/src/engine/internal/llvm/llvmfunctions.h index 2a257321..dc9cf7a4 100644 --- a/src/engine/internal/llvm/llvmfunctions.h +++ b/src/engine/internal/llvm/llvmfunctions.h @@ -44,7 +44,7 @@ class LLVMFunctions llvm::FunctionCallee resolve_list_to_string(); llvm::FunctionCallee resolve_llvm_random(); llvm::FunctionCallee resolve_llvm_random_double(); - llvm::FunctionCallee resolve_llvm_random_long(); + llvm::FunctionCallee resolve_llvm_random_int64(); llvm::FunctionCallee resolve_llvm_random_bool(); llvm::FunctionCallee resolve_string_pool_new(); llvm::FunctionCallee resolve_string_pool_free(); diff --git a/test/llvm/operators/math/mod_test.cpp b/test/llvm/operators/math/mod_test.cpp index 93190c86..e8c9e38b 100644 --- a/test/llvm/operators/math/mod_test.cpp +++ b/test/llvm/operators/math/mod_test.cpp @@ -159,6 +159,26 @@ TEST_F(LLVMModTest, NegativeFiveModZero_Const) ASSERT_NUM_OP2(m_utils, LLVMTestUtils::OpType::Mod, true, -5, 0); } +TEST_F(LLVMModTest, PositiveDecimalModZero) +{ + ASSERT_NUM_OP2(m_utils, LLVMTestUtils::OpType::Mod, false, 5.8, 0); +} + +TEST_F(LLVMModTest, PositiveDecimalModZero_Const) +{ + ASSERT_NUM_OP2(m_utils, LLVMTestUtils::OpType::Mod, true, 5.8, 0); +} + +TEST_F(LLVMModTest, NegativeDecimalModZero) +{ + ASSERT_NUM_OP2(m_utils, LLVMTestUtils::OpType::Mod, false, -5.8, 0); +} + +TEST_F(LLVMModTest, NegativeDecimalModZero_Const) +{ + ASSERT_NUM_OP2(m_utils, LLVMTestUtils::OpType::Mod, true, -5.8, 0); +} + TEST_F(LLVMModTest, NegativeDecimalModInfinity) { ASSERT_NUM_OP2(m_utils, LLVMTestUtils::OpType::Mod, false, -2.5, "Infinity"); diff --git a/test/llvm/operators/math/round_test.cpp b/test/llvm/operators/math/round_test.cpp index e5bb7b45..f1e3d4cd 100644 --- a/test/llvm/operators/math/round_test.cpp +++ b/test/llvm/operators/math/round_test.cpp @@ -19,6 +19,16 @@ TEST_F(LLVMRoundTest, FourPointZero_Const) ASSERT_EQ(m_utils.getOpResult(LLVMTestUtils::OpType::Round, true, 4.0).toDouble(), 4.0); } +TEST_F(LLVMRoundTest, NegativeFourPointZero) +{ + ASSERT_EQ(m_utils.getOpResult(LLVMTestUtils::OpType::Round, false, -4.0).toDouble(), -4.0); +} + +TEST_F(LLVMRoundTest, NegativeFourPointZero_Const) +{ + ASSERT_EQ(m_utils.getOpResult(LLVMTestUtils::OpType::Round, true, -4.0).toDouble(), -4.0); +} + TEST_F(LLVMRoundTest, ThreePointTwo) { ASSERT_EQ(m_utils.getOpResult(LLVMTestUtils::OpType::Round, false, 3.2).toDouble(), 3.0); From a84e99b5b7da847ce3ed659fbb03e8bd14f3cd60 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 27 Aug 2025 00:11:27 +0200 Subject: [PATCH 30/42] Make LLVM integer support optional --- CMakeLists.txt | 5 +++++ .../llvm/instructions/instructionbuilder.cpp | 13 +++++++++++-- .../internal/llvm/instructions/instructionbuilder.h | 1 + src/engine/internal/llvm/llvmbuildutils.cpp | 4 ++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e986c93e..c36a42dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ option(LIBSCRATCHCPP_BUILD_UNIT_TESTS "Build unit tests" ON) option(LIBSCRATCHCPP_NETWORK_SUPPORT "Support for downloading projects" ON) option(LIBSCRATCHCPP_PRINT_LLVM_IR "Print LLVM IR of compiled Scratch scripts (for debugging)" OFF) option(LIBSCRATCHCPP_ENABLE_CODE_ANALYZER "Analyze Scratch scripts to enable various optimizations" ON) +option(LIBSCRATCHCPP_LLVM_INTEGER_SUPPORT "Use integers when possible to enable various optimizations" ON) option(LIBSCRATCHCPP_ENABLE_SANITIZER "Enable sanitizer to detect memory issues" OFF) if (LIBSCRATCHCPP_ENABLE_SANITIZER) @@ -126,6 +127,10 @@ if(LIBSCRATCHCPP_ENABLE_CODE_ANALYZER) target_compile_definitions(scratchcpp PRIVATE ENABLE_CODE_ANALYZER) endif() +if(LIBSCRATCHCPP_LLVM_INTEGER_SUPPORT) + target_compile_definitions(scratchcpp PRIVATE LLVM_INTEGER_SUPPORT) +endif() + # Macros target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_LIBRARY) target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_VERSION="${PROJECT_VERSION}") diff --git a/src/engine/internal/llvm/instructions/instructionbuilder.cpp b/src/engine/internal/llvm/instructions/instructionbuilder.cpp index 2263f521..87977e32 100644 --- a/src/engine/internal/llvm/instructions/instructionbuilder.cpp +++ b/src/engine/internal/llvm/instructions/instructionbuilder.cpp @@ -10,11 +10,14 @@ #include "variables.h" #include "lists.h" #include "procedures.h" +#include "../llvminstruction.h" +#include "../llvmbuildutils.h" using namespace libscratchcpp; using namespace libscratchcpp::llvmins; -InstructionBuilder::InstructionBuilder(LLVMBuildUtils &utils) +InstructionBuilder::InstructionBuilder(LLVMBuildUtils &utils) : + m_utils(utils) { // Create groups m_groups.push_back(std::make_shared(utils)); @@ -34,8 +37,14 @@ LLVMInstruction *InstructionBuilder::process(LLVMInstruction *ins) for (const auto &group : m_groups) { ProcessResult result = group->process(ins); - if (result.match) + if (result.match) { +#ifndef LLVM_INTEGER_SUPPORT + if (ins->functionReturnReg) + ins->functionReturnReg->isInt = m_utils.builder().getInt1(false); +#endif + return result.next; + } } assert(false); // instruction not found diff --git a/src/engine/internal/llvm/instructions/instructionbuilder.h b/src/engine/internal/llvm/instructions/instructionbuilder.h index 4c1190ae..88356a37 100644 --- a/src/engine/internal/llvm/instructions/instructionbuilder.h +++ b/src/engine/internal/llvm/instructions/instructionbuilder.h @@ -15,6 +15,7 @@ class InstructionBuilder LLVMInstruction *process(LLVMInstruction *ins); private: + LLVMBuildUtils &m_utils; std::vector> m_groups; }; diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 152b6e88..d2cb3f9c 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -65,8 +65,12 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro // Init registers for (auto reg : regs) { +#ifdef LLVM_INTEGER_SUPPORT bool isIntConst = (reg->isConst() && optimizeRegisterType(reg.get()) == Compiler::StaticType::Number && reg->constValue().toDouble() == std::floor(reg->constValue().toDouble())); reg->isInt = m_builder.getInt1(isIntConst); +#else + reg->isInt = m_builder.getInt1(false); +#endif reg->intValue = llvm::ConstantInt::get(m_builder.getInt64Ty(), reg->constValue().toDouble(), true); } From 27b7a03d182ee0b917d60c9aa27bb8a0213bb91b Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 27 Aug 2025 00:13:57 +0200 Subject: [PATCH 31/42] Disable optional optimizations in minimal unit tests --- .github/workflows/utests-minimal.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/utests-minimal.yml b/.github/workflows/utests-minimal.yml index 6e2328de..6a416e45 100644 --- a/.github/workflows/utests-minimal.yml +++ b/.github/workflows/utests-minimal.yml @@ -19,7 +19,7 @@ jobs: submodules: true - name: Configure CMake - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBSCRATCHCPP_BUILD_UNIT_TESTS=ON -DLIBSCRATCHCPP_ENABLE_SANITIZER=ON -DLIBSCRATCHCPP_AUDIO_SUPPORT=OFF -DLIBSCRATCHCPP_NETWORK_SUPPORT=OFF + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBSCRATCHCPP_BUILD_UNIT_TESTS=ON -DLIBSCRATCHCPP_ENABLE_SANITIZER=ON -DLIBSCRATCHCPP_AUDIO_SUPPORT=OFF -DLIBSCRATCHCPP_NETWORK_SUPPORT=OFF -DLIBSCRATCHCPP_ENABLE_CODE_ANALYZER=OFF -DLIBSCRATCHCPP_LLVM_INTEGER_SUPPORT=OFF - name: Build run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j$(nproc --all) From c0e5697104fb5a185d8ac5d99162b9dd0d95b973 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 27 Aug 2025 09:06:05 +0200 Subject: [PATCH 32/42] LLVMBuildUtils: Move register type loading to a separate method --- src/engine/internal/llvm/llvmbuildutils.cpp | 26 ++++++++++----------- src/engine/internal/llvm/llvmbuildutils.h | 2 ++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index d2cb3f9c..462d45ed 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -732,21 +732,8 @@ void LLVMBuildUtils::createValueStore( assert(destIsIntVar->getType()->isPointerTy()); assert(destIntVar->getType()->isPointerTy()); - auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [targetType](const std::pair &pair) { return pair.second == targetType; }); - const ValueType mappedType = it == TYPE_MAP.cend() ? ValueType::Number : it->first; // unknown type can be ignored - assert(!(reg->isRawValue && it == TYPE_MAP.cend())); - // Handle multiple type cases with runtime switch - llvm::Value *loadedTargetType = nullptr; - - if (reg->isRawValue) - loadedTargetType = m_builder.getInt32(static_cast(mappedType)); - else { - assert(!reg->isConst()); - llvm::Value *targetTypePtr = getValueTypePtr(reg); - loadedTargetType = m_builder.CreateLoad(m_builder.getInt32Ty(), targetTypePtr); - } - + llvm::Value *loadedTargetType = loadRegisterType(reg, targetType); llvm::Value *loadedDestType = m_builder.CreateLoad(m_builder.getInt32Ty(), destTypePtr); llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); @@ -1414,6 +1401,17 @@ void LLVMBuildUtils::createListMap() } } +llvm::Value *LLVMBuildUtils::loadRegisterType(LLVMRegister *reg, Compiler::StaticType type) +{ + if (reg->isRawValue) + return m_builder.getInt32(static_cast(mapType(type))); + else { + assert(!reg->isConst()); + llvm::Value *typePtr = getValueTypePtr(reg); + return m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); + } +} + llvm::Value *LLVMBuildUtils::castRawValue(LLVMRegister *reg, Compiler::StaticType targetType, NumberType targetNumType) { if (reg->type() == targetType) { diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index 8835384d..9ca6d025 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -123,6 +123,8 @@ class LLVMBuildUtils void createVariableMap(); void createListMap(); + llvm::Value *loadRegisterType(LLVMRegister *reg, Compiler::StaticType type); + llvm::Value *castRawValue(LLVMRegister *reg, Compiler::StaticType targetType, NumberType targetNumType); llvm::Constant *castConstValue(const Value &value, Compiler::StaticType targetType, NumberType targetNumType); From 649912798c7b2e81a11ca7c5e40db44416c95285 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 27 Aug 2025 13:09:01 +0200 Subject: [PATCH 33/42] Add value_stringToDoubleWithCheck() function --- include/scratchcpp/value_functions.h | 1 + src/scratch/value_functions.cpp | 16 +++++++++ test/scratch_classes/value_test.cpp | 51 ++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) diff --git a/include/scratchcpp/value_functions.h b/include/scratchcpp/value_functions.h index 79738ee2..3849d5dc 100644 --- a/include/scratchcpp/value_functions.h +++ b/include/scratchcpp/value_functions.h @@ -85,6 +85,7 @@ extern "C" LIBSCRATCHCPP_EXPORT StringPtr *value_doubleToStringPtr(double v); LIBSCRATCHCPP_EXPORT const StringPtr *value_boolToStringPtr(bool v); LIBSCRATCHCPP_EXPORT double value_stringToDouble(const StringPtr *s); + LIBSCRATCHCPP_EXPORT double value_stringToDoubleWithCheck(const StringPtr *s, bool *ok); LIBSCRATCHCPP_EXPORT bool value_stringToBool(const StringPtr *s); LIBSCRATCHCPP_EXPORT void value_add(const ValueData *v1, const ValueData *v2, ValueData *dst); diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp index faaf1276..64a08300 100644 --- a/src/scratch/value_functions.cpp +++ b/src/scratch/value_functions.cpp @@ -501,6 +501,22 @@ extern "C" return value_stringToDoubleImpl(s->data, s->size); } + /*! + * Converts the given string to double. + * \param[out] ok Whether the conversion was successful. + */ + double value_stringToDoubleWithCheck(const StringPtr *s, bool *ok) + { + *ok = true; + + if (string_compare_case_sensitive(s, &INFINITY_STR) == 0) + return std::numeric_limits::infinity(); + else if (string_compare_case_sensitive(s, &NEGATIVE_INFINITY_STR) == 0) + return -std::numeric_limits::infinity(); + + return value_stringToDoubleImpl(s->data, s->size, ok); + } + /*! Converts the given string to boolean. */ bool value_stringToBool(const StringPtr *s) { diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp index 12c550f3..6851fb89 100644 --- a/test/scratch_classes/value_test.cpp +++ b/test/scratch_classes/value_test.cpp @@ -3387,6 +3387,57 @@ TEST(ValueTest, StringToDouble) ASSERT_EQ(value_stringToDouble(string_pool_new_assign("0b-1")), 0.0); } +TEST(ValueTest, StringToDoubleWithCheck) +{ + bool ok; + + ASSERT_EQ(value_stringToDoubleWithCheck(string_pool_new_assign("2147483647"), &ok), 2147483647.0); + ASSERT_TRUE(ok); + + ASSERT_EQ(value_stringToDoubleWithCheck(string_pool_new_assign("-255.625"), &ok), -255.625); + ASSERT_TRUE(ok); + + ASSERT_EQ(value_stringToDoubleWithCheck(string_pool_new_assign("0"), &ok), 0.0); + ASSERT_TRUE(ok); + + ASSERT_EQ(value_stringToDoubleWithCheck(string_pool_new_assign("-0"), &ok), -0.0); + ASSERT_TRUE(ok); + + ASSERT_EQ(value_stringToDoubleWithCheck(string_pool_new_assign("+.15"), &ok), 0.15); + ASSERT_TRUE(ok); + + ASSERT_EQ(value_stringToDoubleWithCheck(string_pool_new_assign("0+5"), &ok), 0.0); + ASSERT_FALSE(ok); + + ASSERT_EQ(value_stringToDoubleWithCheck(string_pool_new_assign("0-5"), &ok), 0.0); + ASSERT_FALSE(ok); + + ASSERT_EQ(value_stringToDoubleWithCheck(string_pool_new_assign("1 2 3"), &ok), 0.0); + ASSERT_FALSE(ok); + + ASSERT_EQ(value_stringToDoubleWithCheck(string_pool_new_assign("false"), &ok), 0.0); + ASSERT_FALSE(ok); + + ASSERT_EQ(value_stringToDoubleWithCheck(string_pool_new_assign("true"), &ok), 0.0); + ASSERT_FALSE(ok); + + double result = value_stringToDoubleWithCheck(string_pool_new_assign("Infinity"), &ok); + ASSERT_GT(result, 0); + ASSERT_TRUE(std::isinf(result)); + ASSERT_TRUE(ok); + + result = value_stringToDoubleWithCheck(string_pool_new_assign("-Infinity"), &ok); + ASSERT_LT(result, 0); + ASSERT_TRUE(std::isinf(result)); + ASSERT_TRUE(ok); + + ASSERT_EQ(value_stringToDoubleWithCheck(string_pool_new_assign("NaN"), &ok), 0.0); + ASSERT_FALSE(ok); + + ASSERT_EQ(value_stringToDoubleWithCheck(string_pool_new_assign("something"), &ok), 0.0); + ASSERT_FALSE(ok); +} + TEST(ValueTest, StringToBool) { ASSERT_TRUE(value_stringToBool(string_pool_new_assign("2147483647"))); From 0377021e04ab7d43d1ca6fad9a441b988bd92fcc Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 27 Aug 2025 13:10:20 +0200 Subject: [PATCH 34/42] LLVMBuildUtils: Set integer info in getListItemIndex() --- src/engine/internal/llvm/llvmbuildutils.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 462d45ed..71e25959 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -991,10 +991,13 @@ llvm::Value *LLVMBuildUtils::getListItemIndex(const LLVMListPtr &listPtr, Compil m_builder.CreateCondBr(cond, bodyBlock, notFoundBlock); // if (list[index] == item) + // TODO: Add integer support for lists m_builder.SetInsertPoint(bodyBlock); LLVMRegister currentItem(listType); currentItem.isRawValue = false; currentItem.value = getListItem(listPtr, m_builder.CreateLoad(m_builder.getInt64Ty(), index)); + currentItem.isInt = m_builder.getInt1(false); + currentItem.intValue = llvm::ConstantInt::get(m_builder.getInt64Ty(), 0, true); llvm::Value *cmp = createComparison(¤tItem, item, Comparison::EQ); m_builder.CreateCondBr(cmp, cmpIfBlock, cmpElseBlock); From 467ff1672435d594997bf4e85d2178840ec18b7d Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 27 Aug 2025 13:10:40 +0200 Subject: [PATCH 35/42] Rewrite LLVM value comparison logic --- src/engine/internal/llvm/llvmbuildutils.cpp | 574 +++++++++++++++----- src/engine/internal/llvm/llvmbuildutils.h | 10 + src/engine/internal/llvm/llvmfunctions.cpp | 9 + src/engine/internal/llvm/llvmfunctions.h | 1 + 4 files changed, 466 insertions(+), 128 deletions(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 71e25959..da946372 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -1121,26 +1121,6 @@ llvm::Value *LLVMBuildUtils::createComparison(LLVMRegister *arg1, LLVMRegister * return m_builder.getInt1(result); } else { - // Optimize comparison of constant with number/bool - if (arg1->isConst() && arg1->constValue().isValidNumber() && (type2 == Compiler::StaticType::Number || type2 == Compiler::StaticType::Bool)) - type1 = Compiler::StaticType::Number; - - if (arg2->isConst() && arg2->constValue().isValidNumber() && (type1 == Compiler::StaticType::Number || type1 == Compiler::StaticType::Bool)) - type2 = Compiler::StaticType::Number; - - // Optimize number and bool comparison - int optNumberBool = 0; - - if (type1 == Compiler::StaticType::Number && type2 == Compiler::StaticType::Bool) { - type2 = Compiler::StaticType::Number; - optNumberBool = 2; // operand 2 was bool - } - - if (type1 == Compiler::StaticType::Bool && type2 == Compiler::StaticType::Number) { - type1 = Compiler::StaticType::Number; - optNumberBool = 1; // operand 1 was bool - } - // Optimize number and string constant comparison // TODO: GT and LT comparison can be optimized here (e. g. by checking the string constant characters and comparing with numbers and .+-e) if (type == Comparison::EQ) { @@ -1149,132 +1129,162 @@ llvm::Value *LLVMBuildUtils::createComparison(LLVMRegister *arg1, LLVMRegister * return m_builder.getInt1(false); } - if (type1 != type2 || !isSingleType(type1) || !isSingleType(type2)) { - // If the types are different or at least one of them - // is unknown, we must use value functions - llvm::Value *value1 = createValue(arg1); - llvm::Value *value2 = createValue(arg2); + // Handle multiple type cases with runtime switch + llvm::Value *loadedType1 = loadRegisterType(arg1, type1); + llvm::Value *loadedType2 = loadRegisterType(arg2, type2); - switch (type) { - case Comparison::EQ: - return m_builder.CreateCall(m_functions.resolve_value_equals(), { value1, value2 }); + llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); + llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); + std::vector> results; - case Comparison::GT: - return m_builder.CreateCall(m_functions.resolve_value_greater(), { value1, value2 }); + llvm::SwitchInst *sw1 = m_builder.CreateSwitch(loadedType1, defaultBlock, 4); - case Comparison::LT: - return m_builder.CreateCall(m_functions.resolve_value_lower(), { value1, value2 }); + if ((type1 & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + sw1->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); + m_builder.SetInsertPoint(numberBlock); - default: - assert(false); - return nullptr; + llvm::SwitchInst *sw2 = m_builder.CreateSwitch(loadedType2, defaultBlock, 4); + + if ((type2 & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + // Number and number comparison + llvm::BasicBlock *block = llvm::BasicBlock::Create(m_llvmCtx, "numberAndNumberComparison", m_function); + sw2->addCase(m_builder.getInt32(static_cast(ValueType::Number)), block); + m_builder.SetInsertPoint(block); + + llvm::Value *result = createNumberAndNumberComparison(arg1, arg2, type); + + results.push_back({ m_builder.GetInsertBlock(), result }); + m_builder.CreateBr(mergeBlock); } - } else { - // Compare raw values - llvm::Value *value1 = castValue(arg1, type1); - llvm::Value *value2 = castValue(arg2, type2); - assert(type1 == type2); - switch (type1) { - case Compiler::StaticType::Number: { - // Compare two numbers - switch (type) { - case Comparison::EQ: { - llvm::Value *nan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN - llvm::Value *cmp = m_builder.CreateFCmpOEQ(value1, value2); - return m_builder.CreateSelect(nan, m_builder.getInt1(true), cmp); - } - - case Comparison::GT: { - llvm::Value *bothNan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN - llvm::Value *cmp = m_builder.CreateFCmpOGT(value1, value2); - llvm::Value *nan; - llvm::Value *nanCmp; - - if (optNumberBool == 1) { - nan = isNaN(value2); - nanCmp = castValue(arg1, Compiler::StaticType::Bool); - } else if (optNumberBool == 2) { - nan = isNaN(value1); - nanCmp = m_builder.CreateNot(castValue(arg2, Compiler::StaticType::Bool)); - } else { - nan = isNaN(value1); - nanCmp = m_builder.CreateFCmpUGT(value1, value2); - } - - return m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, cmp)); - } - - case Comparison::LT: { - llvm::Value *bothNan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN - llvm::Value *cmp = m_builder.CreateFCmpOLT(value1, value2); - llvm::Value *nan; - llvm::Value *nanCmp; - - if (optNumberBool == 1) { - nan = isNaN(value2); - nanCmp = m_builder.CreateNot(castValue(arg1, Compiler::StaticType::Bool)); - } else if (optNumberBool == 2) { - nan = isNaN(value1); - nanCmp = castValue(arg2, Compiler::StaticType::Bool); - } else { - nan = isNaN(value2); - nanCmp = m_builder.CreateFCmpULT(value1, value2); - } - - return m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, cmp)); - } - - default: - assert(false); - return nullptr; - } - } + if ((type2 & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + // Number and bool comparison + llvm::BasicBlock *block = llvm::BasicBlock::Create(m_llvmCtx, "numberAndBoolComparison", m_function); + sw2->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), block); + m_builder.SetInsertPoint(block); - case Compiler::StaticType::Bool: - // Compare two booleans - switch (type) { - case Comparison::EQ: - return m_builder.CreateICmpEQ(value1, value2); + llvm::Value *result = createNumberAndBoolComparison(arg1, arg2, type); - case Comparison::GT: - // value1 && !value2 - return m_builder.CreateAnd(value1, m_builder.CreateNot(value2)); + results.push_back({ m_builder.GetInsertBlock(), result }); + m_builder.CreateBr(mergeBlock); + } - case Comparison::LT: - // value2 && !value1 - return m_builder.CreateAnd(value2, m_builder.CreateNot(value1)); + if ((type2 & Compiler::StaticType::String) == Compiler::StaticType::String) { + // Number and string comparison + llvm::BasicBlock *block = llvm::BasicBlock::Create(m_llvmCtx, "numberAndStringComparison", m_function); + sw2->addCase(m_builder.getInt32(static_cast(ValueType::String)), block); + m_builder.SetInsertPoint(block); - default: - assert(false); - return nullptr; - } + llvm::Value *result = createNumberAndStringComparison(arg1, arg2, type); - case Compiler::StaticType::String: { - // Compare two strings - llvm::Value *cmpRet = m_builder.CreateCall(m_functions.resolve_string_compare_case_insensitive(), { value1, value2 }); + results.push_back({ m_builder.GetInsertBlock(), result }); + m_builder.CreateBr(mergeBlock); + } + } - switch (type) { - case Comparison::EQ: - return m_builder.CreateICmpEQ(cmpRet, m_builder.getInt32(0)); + if ((type1 & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + sw1->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); + m_builder.SetInsertPoint(boolBlock); - case Comparison::GT: - return m_builder.CreateICmpSGT(cmpRet, m_builder.getInt32(0)); + llvm::SwitchInst *sw2 = m_builder.CreateSwitch(loadedType2, defaultBlock, 4); - case Comparison::LT: - return m_builder.CreateICmpSLT(cmpRet, m_builder.getInt32(0)); + if ((type2 & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + // Bool and number comparison + llvm::BasicBlock *block = llvm::BasicBlock::Create(m_llvmCtx, "boolAndNumberComparison", m_function); + sw2->addCase(m_builder.getInt32(static_cast(ValueType::Number)), block); + m_builder.SetInsertPoint(block); - default: - assert(false); - return nullptr; - } - } + llvm::Value *result = createNumberAndBoolComparison(arg2, arg1, swapComparisonArgs(type)); - default: - assert(false); - return nullptr; + results.push_back({ m_builder.GetInsertBlock(), result }); + m_builder.CreateBr(mergeBlock); + } + + if ((type2 & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + // Bool and bool comparison + llvm::BasicBlock *block = llvm::BasicBlock::Create(m_llvmCtx, "boolAndBoolComparison", m_function); + sw2->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), block); + m_builder.SetInsertPoint(block); + + llvm::Value *result = createBoolAndBoolComparison(arg1, arg2, type); + + results.push_back({ m_builder.GetInsertBlock(), result }); + m_builder.CreateBr(mergeBlock); + } + + if ((type2 & Compiler::StaticType::String) == Compiler::StaticType::String) { + // Bool and string comparison + llvm::BasicBlock *block = llvm::BasicBlock::Create(m_llvmCtx, "boolAndStringComparison", m_function); + sw2->addCase(m_builder.getInt32(static_cast(ValueType::String)), block); + m_builder.SetInsertPoint(block); + + llvm::Value *result = createBoolAndStringComparison(arg1, arg2, type); + + results.push_back({ m_builder.GetInsertBlock(), result }); + m_builder.CreateBr(mergeBlock); + } + } + + if ((type1 & Compiler::StaticType::String) == Compiler::StaticType::String) { + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + sw1->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); + m_builder.SetInsertPoint(stringBlock); + + llvm::SwitchInst *sw2 = m_builder.CreateSwitch(loadedType2, defaultBlock, 4); + + if ((type2 & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + // String and number comparison + llvm::BasicBlock *block = llvm::BasicBlock::Create(m_llvmCtx, "stringAndNumberComparison", m_function); + sw2->addCase(m_builder.getInt32(static_cast(ValueType::Number)), block); + m_builder.SetInsertPoint(block); + + llvm::Value *result = createNumberAndStringComparison(arg2, arg1, swapComparisonArgs(type)); + + results.push_back({ m_builder.GetInsertBlock(), result }); + m_builder.CreateBr(mergeBlock); + } + + if ((type2 & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + // String and bool comparison + llvm::BasicBlock *block = llvm::BasicBlock::Create(m_llvmCtx, "stringAndBoolComparison", m_function); + sw2->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), block); + m_builder.SetInsertPoint(block); + + llvm::Value *result = createBoolAndStringComparison(arg2, arg1, swapComparisonArgs(type)); + + results.push_back({ m_builder.GetInsertBlock(), result }); + m_builder.CreateBr(mergeBlock); + } + + if ((type2 & Compiler::StaticType::String) == Compiler::StaticType::String) { + // String and string comparison + llvm::BasicBlock *block = llvm::BasicBlock::Create(m_llvmCtx, "stringAndStringComparison", m_function); + sw2->addCase(m_builder.getInt32(static_cast(ValueType::String)), block); + m_builder.SetInsertPoint(block); + + llvm::Value *result = createStringAndStringComparison(arg1, arg2, type); + + results.push_back({ m_builder.GetInsertBlock(), result }); + m_builder.CreateBr(mergeBlock); } } + + // Default case + m_builder.SetInsertPoint(defaultBlock); + + // All possible types are covered, mark as unreachable + m_builder.CreateUnreachable(); + + // Create phi node to merge results + m_builder.SetInsertPoint(mergeBlock); + llvm::PHINode *result = m_builder.CreatePHI(m_builder.getInt1Ty(), results.size()); + + for (auto &pair : results) + result->addIncoming(pair.second, pair.first); + + return result; } } @@ -1562,6 +1572,314 @@ void LLVMBuildUtils::copyStructField(llvm::Value *source, llvm::Value *target, i m_builder.CreateStore(m_builder.CreateLoad(fieldType, sourceField), targetField); } +LLVMBuildUtils::Comparison LLVMBuildUtils::swapComparisonArgs(Comparison type) +{ + switch (type) { + case Comparison::GT: + return Comparison::LT; + + case Comparison::LT: + return Comparison::GT; + + default: + return Comparison::EQ; + } +} + +llvm::Value *LLVMBuildUtils::createNumberAndNumberComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type) +{ + // TODO: Add integer support + llvm::Value *value1 = castValue(arg1, Compiler::StaticType::Number, NumberType::Double); + llvm::Value *value2 = castValue(arg2, Compiler::StaticType::Number, NumberType::Double); + + switch (type) { + case Comparison::EQ: { + llvm::Value *bothNan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN + llvm::Value *cmp = m_builder.CreateFCmpOEQ(value1, value2); + return m_builder.CreateOr(bothNan, cmp); + } + + case Comparison::GT: { + llvm::Value *bothNan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN + llvm::Value *cmp = m_builder.CreateFCmpOGT(value1, value2); + llvm::Value *nan = isNaN(value1); + llvm::Value *nanCmp = m_builder.CreateFCmpUGT(value1, value2); + return m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, cmp)); + } + + case Comparison::LT: { + llvm::Value *bothNan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN + llvm::Value *cmp = m_builder.CreateFCmpOLT(value1, value2); + llvm::Value *nan = isNaN(value2); + llvm::Value *nanCmp = m_builder.CreateFCmpULT(value1, value2); + return m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, cmp)); + } + + default: + assert(false); + return nullptr; + } +} + +llvm::Value *LLVMBuildUtils::createBoolAndBoolComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type) +{ + llvm::Value *value1 = castValue(arg1, Compiler::StaticType::Bool); + llvm::Value *value2 = castValue(arg2, Compiler::StaticType::Bool); + + switch (type) { + case Comparison::EQ: + return m_builder.CreateICmpEQ(value1, value2); + + case Comparison::GT: + // value1 && !value2 + return m_builder.CreateAnd(value1, m_builder.CreateNot(value2)); + + case Comparison::LT: + // value2 && !value1 + return m_builder.CreateAnd(value2, m_builder.CreateNot(value1)); + + default: + assert(false); + return nullptr; + } +} + +llvm::Value *LLVMBuildUtils::createStringAndStringComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type) +{ + llvm::Value *value1 = castValue(arg1, Compiler::StaticType::String); + llvm::Value *value2 = castValue(arg2, Compiler::StaticType::String); + + llvm::Value *cmp = m_builder.CreateCall(m_functions.resolve_string_compare_case_insensitive(), { value1, value2 }); + llvm::Value *zero = llvm::ConstantInt::get(m_builder.getInt32Ty(), 0, true); + + switch (type) { + case Comparison::EQ: + return m_builder.CreateICmpEQ(cmp, zero); + + case Comparison::GT: + return m_builder.CreateICmpSGT(cmp, zero); + + case Comparison::LT: + return m_builder.CreateICmpSLT(cmp, zero); + + default: + assert(false); + return nullptr; + } +} + +llvm::Value *LLVMBuildUtils::createNumberAndBoolComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type) +{ + // TODO: Add integer support + llvm::Value *value1 = castValue(arg1, Compiler::StaticType::Number, NumberType::Double); + llvm::Value *value2 = castValue(arg2, Compiler::StaticType::Bool); + + llvm::Value *doubleValue = m_builder.CreateUIToFP(value2, m_builder.getDoubleTy()); + + switch (type) { + case Comparison::EQ: + return m_builder.CreateFCmpOEQ(value1, doubleValue); + + case Comparison::GT: { + llvm::Value *cmp = m_builder.CreateFCmpOGT(value1, doubleValue); + llvm::Value *nan = isNaN(value1); + llvm::Value *nanCmp = m_builder.CreateFCmpUGT(value1, doubleValue); + return m_builder.CreateSelect(nan, nanCmp, cmp); + } + + case Comparison::LT: + return m_builder.CreateFCmpOLT(value1, doubleValue); + + default: + assert(false); + return nullptr; + } +} + +llvm::Value *LLVMBuildUtils::createNumberAndStringComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type) +{ + // TODO: Add integer support + llvm::Value *value1 = castValue(arg1, Compiler::StaticType::Number, NumberType::Double); + llvm::Value *value2 = castValue(arg2, Compiler::StaticType::String); + + // If the number is NaN, skip the string to double conversion + llvm::Value *nan = isNaN(value1); + + llvm::BasicBlock *nanBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + llvm::BasicBlock *stringCastBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + m_builder.CreateCondBr(nan, nanBlock, stringCastBlock); + + m_builder.SetInsertPoint(nanBlock); + m_builder.CreateBr(nextBlock); + + m_builder.SetInsertPoint(stringCastBlock); + llvm::Value *okPtr = addAlloca(m_builder.getInt1Ty()); + llvm::Value *doubleValue = m_builder.CreateCall(m_functions.resolve_value_stringToDoubleWithCheck(), { value2, okPtr }); + llvm::Value *ok = m_builder.CreateLoad(m_builder.getInt1Ty(), okPtr); + m_builder.CreateBr(nextBlock); + + m_builder.SetInsertPoint(nextBlock); + + llvm::PHINode *doubleValuePhi = m_builder.CreatePHI(m_builder.getDoubleTy(), 2); + doubleValuePhi->addIncoming(llvm::ConstantFP::get(m_builder.getDoubleTy(), 0.0), nanBlock); + doubleValuePhi->addIncoming(doubleValue, stringCastBlock); + + llvm::PHINode *okPhi = m_builder.CreatePHI(m_builder.getInt1Ty(), 2); + okPhi->addIncoming(m_builder.getInt1(false), nanBlock); + okPhi->addIncoming(ok, stringCastBlock); + + // If both arguments are valid numbers, compare them as numbers, otherwise as strings + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + nextBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + m_builder.CreateCondBr(okPhi, numberBlock, stringBlock); + + // Number comparison + m_builder.SetInsertPoint(numberBlock); + llvm::Value *numberCmp; + + switch (type) { + case Comparison::EQ: + numberCmp = m_builder.CreateFCmpOEQ(value1, doubleValuePhi); + break; + + case Comparison::GT: + numberCmp = m_builder.CreateFCmpOGT(value1, doubleValuePhi); + break; + + case Comparison::LT: + numberCmp = m_builder.CreateFCmpOLT(value1, doubleValuePhi); + break; + + default: + assert(false); + return nullptr; + } + + m_builder.CreateBr(nextBlock); + + // String comparison + m_builder.SetInsertPoint(stringBlock); + llvm::Value *stringValue = m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), { value1 }); + llvm::Value *cmp = m_builder.CreateCall(m_functions.resolve_string_compare_case_insensitive(), { stringValue, value2 }); + m_builder.CreateCall(m_functions.resolve_string_pool_free(), { stringValue }); // free the string immediately + + llvm::Value *zero = llvm::ConstantInt::get(m_builder.getInt32Ty(), 0, true); + llvm::Value *stringCmp; + + switch (type) { + case Comparison::EQ: + stringCmp = m_builder.CreateICmpEQ(cmp, zero); + break; + + case Comparison::GT: + stringCmp = m_builder.CreateICmpSGT(cmp, zero); + break; + + case Comparison::LT: + stringCmp = m_builder.CreateICmpSLT(cmp, zero); + break; + + default: + assert(false); + return nullptr; + } + + m_builder.CreateBr(nextBlock); + + // Merge the results + m_builder.SetInsertPoint(nextBlock); + + llvm::PHINode *result = m_builder.CreatePHI(m_builder.getInt1Ty(), 2); + result->addIncoming(numberCmp, numberBlock); + result->addIncoming(stringCmp, stringBlock); + + return result; +} + +llvm::Value *LLVMBuildUtils::createBoolAndStringComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type) +{ + llvm::Value *value1 = castValue(arg1, Compiler::StaticType::Bool); + llvm::Value *value2 = castValue(arg2, Compiler::StaticType::String); + + // NOTE: Bools are always valid numbers + + // Convert the string to double + llvm::Value *okPtr = addAlloca(m_builder.getInt1Ty()); + llvm::Value *doubleValue2 = m_builder.CreateCall(m_functions.resolve_value_stringToDoubleWithCheck(), { value2, okPtr }); + llvm::Value *ok = m_builder.CreateLoad(m_builder.getInt1Ty(), okPtr); + + // If the string is a valid number, compare the arguments as numbers, otherwise as strings + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + m_builder.CreateCondBr(ok, numberBlock, stringBlock); + + // Number comparison + m_builder.SetInsertPoint(numberBlock); + llvm::Value *doubleValue1 = m_builder.CreateUIToFP(value1, m_builder.getDoubleTy()); + llvm::Value *numberCmp; + + switch (type) { + case Comparison::EQ: + numberCmp = m_builder.CreateFCmpOEQ(doubleValue1, doubleValue2); + break; + + case Comparison::GT: + numberCmp = m_builder.CreateFCmpOGT(doubleValue1, doubleValue2); + break; + + case Comparison::LT: + numberCmp = m_builder.CreateFCmpOLT(doubleValue1, doubleValue2); + break; + + default: + assert(false); + return nullptr; + } + + m_builder.CreateBr(nextBlock); + + // String comparison + m_builder.SetInsertPoint(stringBlock); + llvm::Value *stringValue = m_builder.CreateCall(m_functions.resolve_value_boolToStringPtr(), { value1 }); + llvm::Value *cmp = m_builder.CreateCall(m_functions.resolve_string_compare_case_insensitive(), { stringValue, value2 }); + // NOTE: Do not free the string! + + llvm::Value *zero = llvm::ConstantInt::get(m_builder.getInt32Ty(), 0, true); + llvm::Value *stringCmp; + + switch (type) { + case Comparison::EQ: + stringCmp = m_builder.CreateICmpEQ(cmp, zero); + break; + + case Comparison::GT: + stringCmp = m_builder.CreateICmpSGT(cmp, zero); + break; + + case Comparison::LT: + stringCmp = m_builder.CreateICmpSLT(cmp, zero); + break; + + default: + assert(false); + return nullptr; + } + + m_builder.CreateBr(nextBlock); + + // Merge the results + m_builder.SetInsertPoint(nextBlock); + + llvm::PHINode *result = m_builder.CreatePHI(m_builder.getInt1Ty(), 2); + result->addIncoming(numberCmp, numberBlock); + result->addIncoming(stringCmp, stringBlock); + + return result; +} + llvm::Value *LLVMBuildUtils::getVariablePtr(llvm::Value *targetVariables, Variable *variable) { if (!m_target->isStage() && variable->target() == m_target) { diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index 9ca6d025..e6192ec9 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -131,6 +131,16 @@ class LLVMBuildUtils void createValueCopy(llvm::Value *source, llvm::Value *target); void copyStructField(llvm::Value *source, llvm::Value *target, int index, llvm::StructType *structType, llvm::Type *fieldType); + Comparison swapComparisonArgs(Comparison type); + + llvm::Value *createNumberAndNumberComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type); + llvm::Value *createBoolAndBoolComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type); + llvm::Value *createStringAndStringComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type); + + llvm::Value *createNumberAndBoolComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type); + llvm::Value *createNumberAndStringComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type); + llvm::Value *createBoolAndStringComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type); + llvm::Value *getVariablePtr(llvm::Value *targetVariables, Variable *variable); llvm::Value *getListPtr(llvm::Value *targetLists, List *list); llvm::Value *getListDataPtr(const LLVMListPtr &listPtr); diff --git a/src/engine/internal/llvm/llvmfunctions.cpp b/src/engine/internal/llvm/llvmfunctions.cpp index 0a9d4a3f..36ebcc83 100644 --- a/src/engine/internal/llvm/llvmfunctions.cpp +++ b/src/engine/internal/llvm/llvmfunctions.cpp @@ -132,6 +132,15 @@ llvm::FunctionCallee LLVMFunctions::resolve_value_stringToDouble() return callee; } +llvm::FunctionCallee LLVMFunctions::resolve_value_stringToDoubleWithCheck() +{ + llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + llvm::FunctionCallee callee = resolveFunction("value_stringToDoubleWithCheck", llvm::FunctionType::get(m_builder->getDoubleTy(), { pointerType, m_builder->getInt1Ty()->getPointerTo() }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + llvm::FunctionCallee LLVMFunctions::resolve_value_stringToBool() { llvm::FunctionCallee callee = resolveFunction("value_stringToBool", llvm::FunctionType::get(m_builder->getInt1Ty(), llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0), false)); diff --git a/src/engine/internal/llvm/llvmfunctions.h b/src/engine/internal/llvm/llvmfunctions.h index dc9cf7a4..13968175 100644 --- a/src/engine/internal/llvm/llvmfunctions.h +++ b/src/engine/internal/llvm/llvmfunctions.h @@ -30,6 +30,7 @@ class LLVMFunctions llvm::FunctionCallee resolve_value_doubleToStringPtr(); llvm::FunctionCallee resolve_value_boolToStringPtr(); llvm::FunctionCallee resolve_value_stringToDouble(); + llvm::FunctionCallee resolve_value_stringToDoubleWithCheck(); llvm::FunctionCallee resolve_value_stringToBool(); llvm::FunctionCallee resolve_value_equals(); llvm::FunctionCallee resolve_value_greater(); From 6998aec07a1207e9560d1fc0ad0c0e243b5741e0 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 27 Aug 2025 13:32:04 +0200 Subject: [PATCH 36/42] Only sync changed variables --- .../internal/llvm/instructions/variables.cpp | 1 + src/engine/internal/llvm/llvmbuildutils.cpp | 14 +++++++++++++- src/engine/internal/llvm/llvmvariableptr.h | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/engine/internal/llvm/instructions/variables.cpp b/src/engine/internal/llvm/instructions/variables.cpp index 4abdb170..6eec5cf7 100644 --- a/src/engine/internal/llvm/instructions/variables.cpp +++ b/src/engine/internal/llvm/instructions/variables.cpp @@ -88,6 +88,7 @@ LLVMInstruction *Variables::buildWriteVariable(LLVMInstruction *ins) LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->targetVariable); m_utils.createValueStore(varPtr.stackPtr, m_utils.getValueTypePtr(varPtr.stackPtr), varPtr.isInt, varPtr.intValue, arg.second, ins->targetType, argType); + m_builder.CreateStore(m_builder.getInt1(true), varPtr.changed); return ins->next; } diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index da946372..7f6f1f9f 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -81,6 +81,7 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro // Store variables locally to enable optimizations varPtr.stackPtr = m_builder.CreateAlloca(m_valueDataType); + varPtr.changed = m_builder.CreateAlloca(m_builder.getInt1Ty()); // Integer support varPtr.isInt = m_builder.CreateAlloca(m_builder.getInt1Ty(), nullptr, var->name() + ".isInt"); @@ -341,8 +342,18 @@ LLVMListPtr &LLVMBuildUtils::listPtr(List *list) void LLVMBuildUtils::syncVariables() { // Copy stack variables to the actual variables - for (auto &[var, varPtr] : m_variablePtrs) + for (auto &[var, varPtr] : m_variablePtrs) { + llvm::BasicBlock *copyBlock = llvm::BasicBlock::Create(m_llvmCtx, "syncVar", m_function); + llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_llvmCtx, "syncVar.next", m_function); + m_builder.CreateCondBr(m_builder.CreateLoad(m_builder.getInt1Ty(), varPtr.changed), copyBlock, nextBlock); + + m_builder.SetInsertPoint(copyBlock); createValueCopy(varPtr.stackPtr, getVariablePtr(m_targetVariables, var)); + m_builder.CreateStore(m_builder.getInt1(false), varPtr.changed); + m_builder.CreateBr(nextBlock); + + m_builder.SetInsertPoint(nextBlock); + } } void LLVMBuildUtils::reloadVariables() @@ -352,6 +363,7 @@ void LLVMBuildUtils::reloadVariables() llvm::Value *ptr = getVariablePtr(m_targetVariables, var); createValueCopy(ptr, varPtr.stackPtr); m_builder.CreateStore(m_builder.getInt1(false), varPtr.isInt); + m_builder.CreateStore(m_builder.getInt1(false), varPtr.changed); } } diff --git a/src/engine/internal/llvm/llvmvariableptr.h b/src/engine/internal/llvm/llvmvariableptr.h index 698649c8..0bf064ca 100644 --- a/src/engine/internal/llvm/llvmvariableptr.h +++ b/src/engine/internal/llvm/llvmvariableptr.h @@ -20,6 +20,7 @@ struct LLVMVariablePtr { llvm::Value *heapPtr = nullptr; llvm::Value *stackPtr = nullptr; + llvm::Value *changed = nullptr; llvm::Value *isInt = nullptr; llvm::Value *intValue = nullptr; From 0f8399f962415d0501c28cb5e75f9cb359a26570 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 27 Aug 2025 14:36:58 +0200 Subject: [PATCH 37/42] LLVMCodeAnalyzer: Reset types after procedure calls --- src/engine/internal/llvm/llvmcodeanalyzer.cpp | 28 +++++++++++++++ src/engine/internal/llvm/llvmcodeanalyzer.h | 2 ++ .../llvm/code_analyzer/list_type_analysis.cpp | 33 +++++++++++++++++ .../code_analyzer/variable_type_analysis.cpp | 35 +++++++++++++++++++ 4 files changed, 98 insertions(+) diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.cpp b/src/engine/internal/llvm/llvmcodeanalyzer.cpp index dfc3ccc3..373cb0cd 100644 --- a/src/engine/internal/llvm/llvmcodeanalyzer.cpp +++ b/src/engine/internal/llvm/llvmcodeanalyzer.cpp @@ -102,6 +102,29 @@ void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const // Store the type in the return register // NOTE: Get list item returns empty string if index is out of range ins->functionReturnReg->setType(ins->targetType | Compiler::StaticType::String); + } else if (isProcedureCall(ins)) { + // Variables/lists may change in procedures + for (auto &[var, type] : currentBranch->variableTypes) { + if (type != Compiler::StaticType::Unknown) { + type = Compiler::StaticType::Unknown; + + if (typeAssignedInstructions.find(ins) == typeAssignedInstructions.cend()) + currentBranch->typeChanges = true; + } + } + + for (auto &[list, type] : currentBranch->listTypes) { + if (type != Compiler::StaticType::Unknown) { + type = Compiler::StaticType::Unknown; + + if (typeAssignedInstructions.find(ins) == typeAssignedInstructions.cend()) { + typeAssignedInstructions.insert(ins); + currentBranch->typeChanges = true; + } + } + } + + typeAssignedInstructions.insert(ins); } ins = ins->next; @@ -247,6 +270,11 @@ bool LLVMCodeAnalyzer::isListClear(const LLVMInstruction *ins) const return (ins->type == LLVMInstruction::Type::ClearList); } +bool LLVMCodeAnalyzer::isProcedureCall(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::CallProcedure); +} + Compiler::StaticType LLVMCodeAnalyzer::writeType(LLVMInstruction *ins) const { assert(ins); diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.h b/src/engine/internal/llvm/llvmcodeanalyzer.h index 65c73c5d..16d55ff2 100644 --- a/src/engine/internal/llvm/llvmcodeanalyzer.h +++ b/src/engine/internal/llvm/llvmcodeanalyzer.h @@ -47,6 +47,8 @@ class LLVMCodeAnalyzer bool isListWrite(const LLVMInstruction *ins) const; bool isListClear(const LLVMInstruction *ins) const; + bool isProcedureCall(const LLVMInstruction *ins) const; + Compiler::StaticType writeType(LLVMInstruction *ins) const; }; diff --git a/test/llvm/code_analyzer/list_type_analysis.cpp b/test/llvm/code_analyzer/list_type_analysis.cpp index 021f0a0e..10c02d71 100644 --- a/test/llvm/code_analyzer/list_type_analysis.cpp +++ b/test/llvm/code_analyzer/list_type_analysis.cpp @@ -182,6 +182,39 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearListOperation) ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Void); } +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ProcedureCall) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::String, "hello"); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto procCall = std::make_shared(LLVMInstruction::Type::CallProcedure, false); + list.addInstruction(procCall); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::Number, 5.2); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + + // Type unknown due to procedure call + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); +} + TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MixedWriteOperationsSameType_AfterClear) { LLVMCodeAnalyzer analyzer; diff --git a/test/llvm/code_analyzer/variable_type_analysis.cpp b/test/llvm/code_analyzer/variable_type_analysis.cpp index 7e7e1124..85a4e573 100644 --- a/test/llvm/code_analyzer/variable_type_analysis.cpp +++ b/test/llvm/code_analyzer/variable_type_analysis.cpp @@ -191,6 +191,41 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, RepeatUntilLoop) ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); } +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ProcedureCallInLoop) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::Number, 5); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + auto procCall = std::make_shared(LLVMInstruction::Type::CallProcedure, false); + list.addInstruction(procCall); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + + // Type unknown due to procedure call + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Unknown); +} + TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopMultipleWrites_UnknownType) { LLVMCodeAnalyzer analyzer; From a521beef76c113727ad088d0c7adc3f0e72e590d Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 27 Aug 2025 18:30:25 +0200 Subject: [PATCH 38/42] LLVMCodeAnalyzer: Fix unknown types after if statements and loops --- src/engine/internal/llvm/llvmcodeanalyzer.cpp | 12 +- src/engine/internal/llvm/llvmcodeanalyzer.h | 2 +- .../llvm/code_analyzer/list_type_analysis.cpp | 161 ++++++++++++++++++ .../code_analyzer/variable_type_analysis.cpp | 141 +++++++++++++++ 4 files changed, 309 insertions(+), 7 deletions(-) diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.cpp b/src/engine/internal/llvm/llvmcodeanalyzer.cpp index 373cb0cd..c57fea2a 100644 --- a/src/engine/internal/llvm/llvmcodeanalyzer.cpp +++ b/src/engine/internal/llvm/llvmcodeanalyzer.cpp @@ -58,13 +58,13 @@ void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const if (primaryBranch && primaryBranch->elseBranch) { // The previous variable types can be ignored in if/else statements overrideVariableTypes(primaryBranch, previousBranch); - mergeListTypes(primaryBranch, previousBranch); + mergeListTypes(primaryBranch, previousBranch, false); mergeVariableTypes(primaryBranch->elseBranch.get(), previousBranch); - mergeListTypes(primaryBranch->elseBranch.get(), previousBranch); + mergeListTypes(primaryBranch->elseBranch.get(), previousBranch, true); } else { mergeVariableTypes(primaryBranch, previousBranch); - mergeListTypes(primaryBranch, previousBranch); + mergeListTypes(primaryBranch, previousBranch, true); } // Remove the branch @@ -196,7 +196,7 @@ void LLVMCodeAnalyzer::mergeVariableTypes(Branch *branch, Branch *previousBranch auto it = previousBranch->variableTypes.find(var); if (it == previousBranch->variableTypes.cend()) - previousBranch->variableTypes[var] = type; + previousBranch->variableTypes[var] = Compiler::StaticType::Unknown; else it->second |= type; } @@ -208,13 +208,13 @@ void LLVMCodeAnalyzer::overrideVariableTypes(Branch *branch, Branch *previousBra previousBranch->variableTypes[var] = type; } -void LLVMCodeAnalyzer::mergeListTypes(Branch *branch, Branch *previousBranch) const +void LLVMCodeAnalyzer::mergeListTypes(Branch *branch, Branch *previousBranch, bool firstUnknown) const { for (const auto &[list, type] : branch->listTypes) { auto it = previousBranch->listTypes.find(list); if (it == previousBranch->listTypes.cend()) - previousBranch->listTypes[list] = type; + previousBranch->listTypes[list] = firstUnknown ? Compiler::StaticType::Unknown : type; else it->second |= type; } diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.h b/src/engine/internal/llvm/llvmcodeanalyzer.h index 16d55ff2..4c9b6a4a 100644 --- a/src/engine/internal/llvm/llvmcodeanalyzer.h +++ b/src/engine/internal/llvm/llvmcodeanalyzer.h @@ -32,7 +32,7 @@ class LLVMCodeAnalyzer void mergeVariableTypes(Branch *branch, Branch *previousBranch) const; void overrideVariableTypes(Branch *branch, Branch *previousBranch) const; - void mergeListTypes(Branch *branch, Branch *previousBranch) const; + void mergeListTypes(Branch *branch, Branch *previousBranch, bool firstUnknown) const; bool isLoopStart(const LLVMInstruction *ins) const; bool isLoopEnd(const LLVMInstruction *ins) const; diff --git a/test/llvm/code_analyzer/list_type_analysis.cpp b/test/llvm/code_analyzer/list_type_analysis.cpp index 10c02d71..da98bd6e 100644 --- a/test/llvm/code_analyzer/list_type_analysis.cpp +++ b/test/llvm/code_analyzer/list_type_analysis.cpp @@ -351,6 +351,167 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopSingleWrite_AfterClear) ASSERT_EQ(appendList->targetType, Compiler::StaticType::Number); } +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfStatement_IfBranch) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + + // The type is Unknown because the if statement might not run at all + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfStatement_ElseBranch) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + + // The type is Unknown because the if statement might not run at all + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInIfElse) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto clearList1 = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList1->targetList = &targetList; + list.addInstruction(clearList1); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto clearList2 = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList2->targetList = &targetList; + list.addInstruction(clearList2); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + appendList3->targetList = &targetList; + appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(appendList3); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Void); + + // The type is Number | String because any of the branches may run + ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAndWriteInLoop) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + + // The type is Unknown because the loop might not run at all + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); +} + TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAfterWriteInLoop) { LLVMCodeAnalyzer analyzer; diff --git a/test/llvm/code_analyzer/variable_type_analysis.cpp b/test/llvm/code_analyzer/variable_type_analysis.cpp index 85a4e573..916e4209 100644 --- a/test/llvm/code_analyzer/variable_type_analysis.cpp +++ b/test/llvm/code_analyzer/variable_type_analysis.cpp @@ -629,6 +629,147 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfElse) ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfStatement_IfBranch) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto setVarInIfStatement = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueInIfStatement(Compiler::StaticType::Number, 42); + setVarInIfStatement->targetVariable = &var; + setVarInIfStatement->args.push_back({ Compiler::StaticType::Unknown, &valueInIfStatement }); + list.addInstruction(setVarInIfStatement); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, false); + setVar->targetVariable = &var; + setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(setVar); + + analyzer.analyzeScript(list); + + ASSERT_EQ(setVarInIfStatement->targetType, Compiler::StaticType::Unknown); + + // The type is Unknown because the if statement might not run at all + ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfStatement_ElseBranch) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto setVarInIfStatement = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueInIfStatement(Compiler::StaticType::Number, 42); + setVarInIfStatement->targetVariable = &var; + setVarInIfStatement->args.push_back({ Compiler::StaticType::Unknown, &valueInIfStatement }); + list.addInstruction(setVarInIfStatement); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, false); + setVar->targetVariable = &var; + setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(setVar); + + analyzer.analyzeScript(list); + + ASSERT_EQ(setVarInIfStatement->targetType, Compiler::StaticType::Unknown); + + // The type is Unknown because the if statement might not run at all + ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInIfElse) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + setVar3->targetVariable = &var; + setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(setVar3); + + analyzer.analyzeScript(list); + + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Unknown); + + // The type is Number | String because any of the branches may run + ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteInLoop) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + auto setVarInLoop = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueInLoop(Compiler::StaticType::Number, 42); + setVarInLoop->targetVariable = &var; + setVarInLoop->args.push_back({ Compiler::StaticType::Unknown, &valueInLoop }); + list.addInstruction(setVarInLoop); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, false); + setVar->targetVariable = &var; + setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(setVar); + + analyzer.analyzeScript(list); + + ASSERT_EQ(setVarInLoop->targetType, Compiler::StaticType::Unknown); + + // The type is Unknown because the loop might not run at all + ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown); +} + TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ComplexNestedControlFlow) { LLVMCodeAnalyzer analyzer; From 02868ee6d686e0866f5b3baf58e6963f06dc2110 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 27 Aug 2025 18:40:10 +0200 Subject: [PATCH 39/42] Revert "Enable ValueType bitmasking" This reverts commit a2e4ee14b0bb3026527c08b14646c09bf06224f3. --- include/scratchcpp/valuedata.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/include/scratchcpp/valuedata.h b/include/scratchcpp/valuedata.h index b7073293..adfffa9f 100644 --- a/include/scratchcpp/valuedata.h +++ b/include/scratchcpp/valuedata.h @@ -5,7 +5,6 @@ #include #include "global.h" -#include "enum_bitmask.h" namespace libscratchcpp { @@ -14,15 +13,12 @@ struct StringPtr; enum class LIBSCRATCHCPP_EXPORT ValueType { - Void = 0, - Number = 1 << 0, - Bool = 1 << 1, - String = 1 << 2, - Pointer = 1 << 3 + Number = 0, + Bool = 1, + String = 2, + Pointer = 3 }; -constexpr bool enable_enum_bitmask(ValueType); - extern "C" { /*! \brief The ValueData struct holds the data of Value. It's used in compiled Scratch code for better performance. */ From 82c8f0e0237de8c53a5e4005f7180a503d04d203 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 28 Aug 2025 15:57:20 +0200 Subject: [PATCH 40/42] LLVMCodeAnalyzer: Fix reads in loop conditions --- src/engine/internal/llvm/llvmcodeanalyzer.cpp | 4 +- .../llvm/code_analyzer/list_type_analysis.cpp | 48 ++++++++++++++----- .../code_analyzer/variable_type_analysis.cpp | 30 ++++++++++++ 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.cpp b/src/engine/internal/llvm/llvmcodeanalyzer.cpp index c57fea2a..f41b54eb 100644 --- a/src/engine/internal/llvm/llvmcodeanalyzer.cpp +++ b/src/engine/internal/llvm/llvmcodeanalyzer.cpp @@ -7,8 +7,8 @@ using namespace libscratchcpp; -static const std::unordered_set - BEGIN_LOOP_INSTRUCTIONS = { LLVMInstruction::Type::BeginRepeatLoop, LLVMInstruction::Type::BeginWhileLoop, LLVMInstruction::Type::BeginRepeatUntilLoop }; +// NOTE: The loop condition in repeat until and while loops is considered a part of the loop body +static const std::unordered_set BEGIN_LOOP_INSTRUCTIONS = { LLVMInstruction::Type::BeginRepeatLoop, LLVMInstruction::Type::BeginLoopCondition }; static const std::unordered_set LIST_WRITE_INSTRUCTIONS = { LLVMInstruction::Type::AppendToList, LLVMInstruction::Type::InsertToList, LLVMInstruction::Type::ListReplace }; diff --git a/test/llvm/code_analyzer/list_type_analysis.cpp b/test/llvm/code_analyzer/list_type_analysis.cpp index da98bd6e..05e76b13 100644 --- a/test/llvm/code_analyzer/list_type_analysis.cpp +++ b/test/llvm/code_analyzer/list_type_analysis.cpp @@ -529,7 +529,7 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAfterWriteInLoop) appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(appendList); - auto loopStart = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); list.addInstruction(loopStart); auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); @@ -567,25 +567,38 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WhileLoop) appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(appendList); + auto loopCond = std::make_shared(LLVMInstruction::Type::BeginLoopCondition, false); + list.addInstruction(loopCond); + + // Read an item in loop condition + auto getItem = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister index(Compiler::StaticType::Number, 0); + getItem->targetList = &targetList; + getItem->args.push_back({ Compiler::StaticType::Number, &index }); + list.addInstruction(getItem); + + LLVMRegister sourceValue(Compiler::StaticType::Unknown); + sourceValue.isRawValue = false; + sourceValue.instruction = getItem; + getItem->functionReturnReg = &sourceValue; + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); list.addInstruction(loopStart); + // Change the type in the loop auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); LLVMConstantRegister value1(Compiler::StaticType::Number, 5); appendList1->targetList = &targetList; appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); list.addInstruction(appendList1); - auto clearList2 = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList2->targetList = &targetList; - list.addInstruction(clearList2); - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); analyzer.analyzeScript(list); - ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Bool); + ASSERT_EQ(getItem->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); } TEST(LLVMCodeAnalyzer_ListTypeAnalysis, RepeatUntilLoop) @@ -605,25 +618,38 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, RepeatUntilLoop) appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(appendList); + auto loopCond = std::make_shared(LLVMInstruction::Type::BeginLoopCondition, false); + list.addInstruction(loopCond); + + // Read an item in loop condition + auto getItem = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister index(Compiler::StaticType::Number, 0); + getItem->targetList = &targetList; + getItem->args.push_back({ Compiler::StaticType::Number, &index }); + list.addInstruction(getItem); + + LLVMRegister sourceValue(Compiler::StaticType::Unknown); + sourceValue.isRawValue = false; + sourceValue.instruction = getItem; + getItem->functionReturnReg = &sourceValue; + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); list.addInstruction(loopStart); + // Change the type in the loop auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); LLVMConstantRegister value1(Compiler::StaticType::Number, 5); appendList1->targetList = &targetList; appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); list.addInstruction(appendList1); - auto clearList2 = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList2->targetList = &targetList; - list.addInstruction(clearList2); - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); list.addInstruction(loopEnd); analyzer.analyzeScript(list); - ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Bool); + ASSERT_EQ(getItem->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); } TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopMultipleWrites_UnknownType) diff --git a/test/llvm/code_analyzer/variable_type_analysis.cpp b/test/llvm/code_analyzer/variable_type_analysis.cpp index 916e4209..a09c4eb0 100644 --- a/test/llvm/code_analyzer/variable_type_analysis.cpp +++ b/test/llvm/code_analyzer/variable_type_analysis.cpp @@ -144,9 +144,23 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WhileLoop) setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(setVar); + auto loopCond = std::make_shared(LLVMInstruction::Type::BeginLoopCondition, false); + list.addInstruction(loopCond); + + // Read the variable in loop condition + auto readVar = std::make_shared(LLVMInstruction::Type::ReadVariable, true); + readVar->targetVariable = &var; + list.addInstruction(readVar); + + LLVMRegister varValue(Compiler::StaticType::Unknown); + varValue.isRawValue = false; + varValue.instruction = readVar; + readVar->functionReturnReg = &varValue; + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); list.addInstruction(loopStart); + // Change the type in the loop auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); LLVMConstantRegister value1(Compiler::StaticType::Number, 5); setVar1->targetVariable = &var; @@ -158,6 +172,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WhileLoop) analyzer.analyzeScript(list); + ASSERT_EQ(readVar->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); } @@ -174,9 +189,23 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, RepeatUntilLoop) setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); list.addInstruction(setVar); + auto loopCond = std::make_shared(LLVMInstruction::Type::BeginLoopCondition, false); + list.addInstruction(loopCond); + + // Read the variable in loop condition + auto readVar = std::make_shared(LLVMInstruction::Type::ReadVariable, true); + readVar->targetVariable = &var; + list.addInstruction(readVar); + + LLVMRegister varValue(Compiler::StaticType::Unknown); + varValue.isRawValue = false; + varValue.instruction = readVar; + readVar->functionReturnReg = &varValue; + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); list.addInstruction(loopStart); + // Change the type in the loop auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); LLVMConstantRegister value1(Compiler::StaticType::Number, 5); setVar1->targetVariable = &var; @@ -188,6 +217,7 @@ TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, RepeatUntilLoop) analyzer.analyzeScript(list); + ASSERT_EQ(readVar->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); } From e43091b080cfae7f3314891579e28ad31abfb43a Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 28 Aug 2025 16:33:20 +0200 Subject: [PATCH 41/42] LLVMBuildUtils: Add integer support to comparison instructions --- src/engine/internal/llvm/llvmbuildutils.cpp | 78 +++++++++++++-------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 7f6f1f9f..c8a70426 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -1600,31 +1600,39 @@ LLVMBuildUtils::Comparison LLVMBuildUtils::swapComparisonArgs(Comparison type) llvm::Value *LLVMBuildUtils::createNumberAndNumberComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type) { - // TODO: Add integer support - llvm::Value *value1 = castValue(arg1, Compiler::StaticType::Number, NumberType::Double); - llvm::Value *value2 = castValue(arg2, Compiler::StaticType::Number, NumberType::Double); + llvm::Value *double1 = castValue(arg1, Compiler::StaticType::Number, NumberType::Double); + llvm::Value *double2 = castValue(arg2, Compiler::StaticType::Number, NumberType::Double); + + llvm::Value *int1 = castValue(arg1, Compiler::StaticType::Number, NumberType::Int); + llvm::Value *int2 = castValue(arg2, Compiler::StaticType::Number, NumberType::Int); + + llvm::Value *isInt = m_builder.CreateAnd(arg1->isInt, arg2->isInt); + llvm::Value *bothNan = m_builder.CreateAnd(isNaN(double1), isNaN(double2)); // NaN == NaN switch (type) { case Comparison::EQ: { - llvm::Value *bothNan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN - llvm::Value *cmp = m_builder.CreateFCmpOEQ(value1, value2); - return m_builder.CreateOr(bothNan, cmp); + llvm::Value *fcmp = m_builder.CreateFCmpOEQ(double1, double2); + llvm::Value *doubleResult = m_builder.CreateOr(bothNan, fcmp); + llvm::Value *icmp = m_builder.CreateICmpEQ(int1, int2); + return m_builder.CreateSelect(isInt, icmp, doubleResult); } case Comparison::GT: { - llvm::Value *bothNan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN - llvm::Value *cmp = m_builder.CreateFCmpOGT(value1, value2); - llvm::Value *nan = isNaN(value1); - llvm::Value *nanCmp = m_builder.CreateFCmpUGT(value1, value2); - return m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, cmp)); + llvm::Value *fcmp = m_builder.CreateFCmpOGT(double1, double2); + llvm::Value *nan = isNaN(double1); + llvm::Value *nanCmp = m_builder.CreateFCmpUGT(double1, double2); + llvm::Value *doubleResult = m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, fcmp)); + llvm::Value *icmp = m_builder.CreateICmpSGT(int1, int2); + return m_builder.CreateSelect(isInt, icmp, doubleResult); } case Comparison::LT: { - llvm::Value *bothNan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN - llvm::Value *cmp = m_builder.CreateFCmpOLT(value1, value2); - llvm::Value *nan = isNaN(value2); - llvm::Value *nanCmp = m_builder.CreateFCmpULT(value1, value2); - return m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, cmp)); + llvm::Value *fcmp = m_builder.CreateFCmpOLT(double1, double2); + llvm::Value *nan = isNaN(double2); + llvm::Value *nanCmp = m_builder.CreateFCmpULT(double1, double2); + llvm::Value *doubleResult = m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, fcmp)); + llvm::Value *icmp = m_builder.CreateICmpSLT(int1, int2); + return m_builder.CreateSelect(isInt, icmp, doubleResult); } default: @@ -1682,25 +1690,36 @@ llvm::Value *LLVMBuildUtils::createStringAndStringComparison(LLVMRegister *arg1, llvm::Value *LLVMBuildUtils::createNumberAndBoolComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type) { - // TODO: Add integer support - llvm::Value *value1 = castValue(arg1, Compiler::StaticType::Number, NumberType::Double); - llvm::Value *value2 = castValue(arg2, Compiler::StaticType::Bool); + llvm::Value *doubleValue1 = castValue(arg1, Compiler::StaticType::Number, NumberType::Double); + llvm::Value *intValue1 = castValue(arg1, Compiler::StaticType::Number, NumberType::Int); + + llvm::Value *boolValue2 = castValue(arg2, Compiler::StaticType::Bool); + llvm::Value *intValue2 = castValue(arg2, Compiler::StaticType::Number, NumberType::Int); - llvm::Value *doubleValue = m_builder.CreateUIToFP(value2, m_builder.getDoubleTy()); + llvm::Value *doubleValue2 = m_builder.CreateUIToFP(boolValue2, m_builder.getDoubleTy()); + llvm::Value *isInt = arg1->isInt; switch (type) { - case Comparison::EQ: - return m_builder.CreateFCmpOEQ(value1, doubleValue); + case Comparison::EQ: { + llvm::Value *fcmp = m_builder.CreateFCmpOEQ(doubleValue1, doubleValue2); + llvm::Value *icmp = m_builder.CreateICmpEQ(intValue1, intValue2); + return m_builder.CreateSelect(isInt, icmp, fcmp); + } case Comparison::GT: { - llvm::Value *cmp = m_builder.CreateFCmpOGT(value1, doubleValue); - llvm::Value *nan = isNaN(value1); - llvm::Value *nanCmp = m_builder.CreateFCmpUGT(value1, doubleValue); - return m_builder.CreateSelect(nan, nanCmp, cmp); + llvm::Value *fcmp = m_builder.CreateFCmpOGT(doubleValue1, doubleValue2); + llvm::Value *nan = isNaN(doubleValue1); + llvm::Value *nanCmp = m_builder.CreateFCmpUGT(doubleValue1, doubleValue2); + llvm::Value *doubleResult = m_builder.CreateSelect(nan, nanCmp, fcmp); + llvm::Value *icmp = m_builder.CreateICmpSGT(intValue1, intValue2); + return m_builder.CreateSelect(isInt, icmp, doubleResult); } - case Comparison::LT: - return m_builder.CreateFCmpOLT(value1, doubleValue); + case Comparison::LT: { + llvm::Value *fcmp = m_builder.CreateFCmpOLT(doubleValue1, doubleValue2); + llvm::Value *icmp = m_builder.CreateICmpSLT(intValue1, intValue2); + return m_builder.CreateSelect(isInt, icmp, fcmp); + } default: assert(false); @@ -1710,12 +1729,11 @@ llvm::Value *LLVMBuildUtils::createNumberAndBoolComparison(LLVMRegister *arg1, L llvm::Value *LLVMBuildUtils::createNumberAndStringComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type) { - // TODO: Add integer support llvm::Value *value1 = castValue(arg1, Compiler::StaticType::Number, NumberType::Double); llvm::Value *value2 = castValue(arg2, Compiler::StaticType::String); // If the number is NaN, skip the string to double conversion - llvm::Value *nan = isNaN(value1); + llvm::Value *nan = m_builder.CreateAnd(m_builder.CreateNot(arg1->isInt), isNaN(value1)); llvm::BasicBlock *nanBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); llvm::BasicBlock *stringCastBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); From 33a69308060931c18d9961b46456a7da34a3ab17 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 28 Aug 2025 17:16:08 +0200 Subject: [PATCH 42/42] Add integer support to string instructions --- src/engine/internal/llvm/instructions/string.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/engine/internal/llvm/instructions/string.cpp b/src/engine/internal/llvm/instructions/string.cpp index c973196b..8cf7a0da 100644 --- a/src/engine/internal/llvm/instructions/string.cpp +++ b/src/engine/internal/llvm/instructions/string.cpp @@ -85,7 +85,7 @@ LLVMInstruction *String::buildStringChar(LLVMInstruction *ins) const auto &arg1 = ins->args[0]; const auto &arg2 = ins->args[1]; llvm::Value *str = m_utils.castValue(arg1.second, arg1.first); - llvm::Value *index = m_builder.CreateFPToSI(m_utils.castValue(arg2.second, arg2.first), m_builder.getInt64Ty()); + llvm::Value *index = m_utils.castValue(arg2.second, arg2.first, LLVMBuildUtils::NumberType::Int); llvm::PointerType *charPointerType = m_builder.getInt16Ty()->getPointerTo(); llvm::StructType *stringPtrType = m_utils.compilerCtx()->stringPtrType(); @@ -131,6 +131,8 @@ LLVMInstruction *String::buildStringLength(LLVMInstruction *ins) llvm::Value *sizeField = m_builder.CreateStructGEP(stringPtrType, str, 1); llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), sizeField); ins->functionReturnReg->value = m_builder.CreateSIToFP(size, m_builder.getDoubleTy()); + ins->functionReturnReg->intValue = size; + ins->functionReturnReg->isInt = m_builder.getInt1(true); return ins->next; }