diff --git a/include/scratchcpp/list.h b/include/scratchcpp/list.h index 9d5e4b686..47c6782c2 100644 --- a/include/scratchcpp/list.h +++ b/include/scratchcpp/list.h @@ -41,7 +41,13 @@ class LIBSCRATCHCPP_EXPORT List : public Entity void setMonitor(Monitor *monitor); /*! Returns a pointer to the raw list data. */ - inline ValueData *data() const { return m_dataPtr->data(); } + inline ValueData *data() const { return m_rawDataPtr; } + + /*! + * Returns a pointer to pointer to the raw list data. + * \note This is used internally by compiled code for various optimizations. + */ + inline ValueData *const *dataPtr() const { return &m_rawDataPtr; } /*! * Returns a pointer to the list size. @@ -247,6 +253,8 @@ class LIBSCRATCHCPP_EXPORT List : public Entity value_free(&m_dataPtr->back()); m_dataPtr->erase(m_dataPtr->end()); } + + m_rawDataPtr = m_dataPtr->data(); } inline size_t getAllocSize(size_t x) @@ -264,6 +272,7 @@ class LIBSCRATCHCPP_EXPORT List : public Entity spimpl::unique_impl_ptr impl; veque::veque *m_dataPtr = nullptr; // NOTE: accessing through pointer is faster! (from benchmarks) + ValueData *m_rawDataPtr = nullptr; size_t m_size = 0; }; diff --git a/src/engine/compiler.cpp b/src/engine/compiler.cpp index a7b40c257..90f95afe1 100644 --- a/src/engine/compiler.cpp +++ b/src/engine/compiler.cpp @@ -83,7 +83,7 @@ std::shared_ptr Compiler::compile(Block *startBlock, CodeType co } impl->block = nullptr; - return impl->builder->finalize(); + return impl->builder->build(); } while (impl->block) { @@ -122,7 +122,7 @@ std::shared_ptr Compiler::compile(Block *startBlock, CodeType co impl->substackEnd(); } - return impl->builder->finalize(); + return impl->builder->build(); } /*! diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp index 98612f577..ac446a560 100644 --- a/src/engine/internal/engine.cpp +++ b/src/engine/internal/engine.cpp @@ -528,8 +528,8 @@ void Engine::step() m_frameActivity = !m_threads.empty(); // Resolve stopped broadcast scripts - std::vector resolved; - std::vector resolvedThreads; + std::unordered_map stoppedBroadcasts; // sender thread + std::unordered_map runningStatus; // sender thread for (const auto &[broadcast, senderThread] : m_broadcastSenders) { std::unordered_map> *broadcastMap = nullptr; @@ -544,44 +544,41 @@ void Engine::step() broadcastMap = &m_broadcastMap; } - bool found = false; + bool isRunning = false; if (broadcastMap->find(broadcast) != broadcastMap->cend()) { const auto &scripts = (*broadcastMap)[broadcast]; for (auto script : scripts) { if (std::find_if(m_threads.begin(), m_threads.end(), [script](std::shared_ptr thread) { return thread->script() == script; }) != m_threads.end()) { - found = true; + isRunning = true; break; } } } - if (found) { - // If a broadcast with the same name but different case - // was considered stopped before, restore the promise. - if (std::find(resolvedThreads.begin(), resolvedThreads.end(), senderThread) != resolvedThreads.end()) { - senderThread->promise(); - resolvedThreads.erase(std::remove(resolvedThreads.begin(), resolvedThreads.end(), senderThread), resolvedThreads.end()); - } - } else { - Thread *th = senderThread; + if (runningStatus.find(senderThread) == runningStatus.cend() || isRunning) + runningStatus[senderThread] = isRunning; - if (std::find_if(m_threads.begin(), m_threads.end(), [th](std::shared_ptr thread) { return thread.get() == th; }) != m_threads.end()) { - auto promise = th->promise(); + if (!isRunning) + stoppedBroadcasts[broadcast] = senderThread; + } - if (promise) - promise->resolve(); - } + for (const auto &[broadcast, senderThread] : stoppedBroadcasts) { + m_broadcastSenders.erase(broadcast); - resolved.push_back(broadcast); - resolvedThreads.push_back(th); + // Resolve broadcast promise + Thread *th = senderThread; + + if (std::find_if(m_threads.begin(), m_threads.end(), [th](std::shared_ptr thread) { return thread.get() == th; }) != m_threads.end()) { + auto promise = th->promise().get(); + + // Resolve only if all broadcasts of the same name but different case are stopped + if (promise && !runningStatus[senderThread]) + promise->resolve(); } } - for (Broadcast *broadcast : resolved) - m_broadcastSenders.erase(broadcast); - m_redrawRequested = false; // Step threads diff --git a/src/engine/internal/icodebuilder.h b/src/engine/internal/icodebuilder.h index 0170aa8be..d175c850d 100644 --- a/src/engine/internal/icodebuilder.h +++ b/src/engine/internal/icodebuilder.h @@ -18,7 +18,7 @@ class ICodeBuilder public: virtual ~ICodeBuilder() { } - virtual std::shared_ptr finalize() = 0; + virtual std::shared_ptr build() = 0; virtual CompilerValue *addFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) = 0; virtual CompilerValue *addTargetFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) = 0; diff --git a/src/engine/internal/llvm/CMakeLists.txt b/src/engine/internal/llvm/CMakeLists.txt index 01520d08a..629d1a28b 100644 --- a/src/engine/internal/llvm/CMakeLists.txt +++ b/src/engine/internal/llvm/CMakeLists.txt @@ -1,21 +1,26 @@ target_sources(scratchcpp PRIVATE + llvmbuildutils.cpp + llvmbuildutils.h llvmcodebuilder.cpp llvmcodebuilder.h llvmregister.h llvmconstantregister.h llvminstruction.h + llvminstructionlist.cpp + llvminstructionlist.h llvmifstatement.h llvmloop.h llvmcoroutine.cpp llvmcoroutine.h llvmvariableptr.h llvmlistptr.h - llvmprocedure.h llvmtypes.cpp llvmtypes.h llvmfunctions.cpp - llvmloopscope.h + llvmfunctions.h + llvmtypeanalyzer.cpp + llvmtypeanalyzer.h llvmcompilercontext.cpp llvmcompilercontext.h llvmexecutablecode.cpp @@ -23,3 +28,5 @@ target_sources(scratchcpp llvmexecutioncontext.cpp llvmexecutioncontext.h ) + +add_subdirectory(instructions) diff --git a/src/engine/internal/llvm/instructions/CMakeLists.txt b/src/engine/internal/llvm/instructions/CMakeLists.txt new file mode 100644 index 000000000..bdf67e3ac --- /dev/null +++ b/src/engine/internal/llvm/instructions/CMakeLists.txt @@ -0,0 +1,30 @@ +target_sources(scratchcpp + PRIVATE + instructionbuilder.cpp + instructionbuilder.h + instructiongroup.cpp + instructiongroup.h + processresult.h +) + +target_sources(scratchcpp + PRIVATE + functions.cpp + functions.h + math.cpp + math.h + comparison.cpp + comparison.h + string.cpp + string.h + logic.cpp + logic.h + control.cpp + control.h + variables.cpp + variables.h + lists.cpp + lists.h + procedures.cpp + procedures.h +) diff --git a/src/engine/internal/llvm/instructions/comparison.cpp b/src/engine/internal/llvm/instructions/comparison.cpp new file mode 100644 index 000000000..9a76ce24c --- /dev/null +++ b/src/engine/internal/llvm/instructions/comparison.cpp @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "comparison.h" +#include "../llvminstruction.h" +#include "../llvmbuildutils.h" + +using namespace libscratchcpp; +using namespace libscratchcpp::llvmins; + +ProcessResult Comparison::process(LLVMInstruction *ins) +{ + ProcessResult ret(true, ins); + + switch (ins->type) { + case LLVMInstruction::Type::CmpEQ: + ret.next = buildCmpEQ(ins); + break; + + case LLVMInstruction::Type::CmpGT: + ret.next = buildCmpGT(ins); + break; + + case LLVMInstruction::Type::CmpLT: + ret.next = buildCmpLT(ins); + break; + + case LLVMInstruction::Type::StrCmpEQCS: + ret.next = buildStrCmpEQCS(ins); + break; + + case LLVMInstruction::Type::StrCmpEQCI: + ret.next = buildStrCmpEQCI(ins); + break; + + default: + ret.match = false; + break; + } + + return ret; +} + +LLVMInstruction *Comparison::buildCmpEQ(LLVMInstruction *ins) +{ + assert(ins->args.size() == 2); + const auto &arg1 = ins->args[0].second; + const auto &arg2 = ins->args[1].second; + ins->functionReturnReg->value = m_utils.createComparison(arg1, arg2, LLVMBuildUtils::Comparison::EQ); + + return ins->next; +} + +LLVMInstruction *Comparison::buildCmpGT(LLVMInstruction *ins) +{ + assert(ins->args.size() == 2); + const auto &arg1 = ins->args[0].second; + const auto &arg2 = ins->args[1].second; + ins->functionReturnReg->value = m_utils.createComparison(arg1, arg2, LLVMBuildUtils::Comparison::GT); + + return ins->next; +} + +LLVMInstruction *Comparison::buildCmpLT(LLVMInstruction *ins) +{ + assert(ins->args.size() == 2); + const auto &arg1 = ins->args[0].second; + const auto &arg2 = ins->args[1].second; + ins->functionReturnReg->value = m_utils.createComparison(arg1, arg2, LLVMBuildUtils::Comparison::LT); + + return ins->next; +} + +LLVMInstruction *Comparison::buildStrCmpEQCS(LLVMInstruction *ins) +{ + assert(ins->args.size() == 2); + const auto &arg1 = ins->args[0].second; + const auto &arg2 = ins->args[1].second; + ins->functionReturnReg->value = m_utils.createStringComparison(arg1, arg2, true); + + return ins->next; +} + +LLVMInstruction *Comparison::buildStrCmpEQCI(LLVMInstruction *ins) +{ + assert(ins->args.size() == 2); + const auto &arg1 = ins->args[0].second; + const auto &arg2 = ins->args[1].second; + ins->functionReturnReg->value = m_utils.createStringComparison(arg1, arg2, false); + + return ins->next; +} diff --git a/src/engine/internal/llvm/instructions/comparison.h b/src/engine/internal/llvm/instructions/comparison.h new file mode 100644 index 000000000..0bc7f28e2 --- /dev/null +++ b/src/engine/internal/llvm/instructions/comparison.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "instructiongroup.h" + +namespace libscratchcpp::llvmins +{ + +class Comparison : public InstructionGroup +{ + public: + using InstructionGroup::InstructionGroup; + + ProcessResult process(LLVMInstruction *ins) override; + + private: + LLVMInstruction *buildCmpEQ(LLVMInstruction *ins); + LLVMInstruction *buildCmpGT(LLVMInstruction *ins); + LLVMInstruction *buildCmpLT(LLVMInstruction *ins); + LLVMInstruction *buildStrCmpEQCS(LLVMInstruction *ins); + LLVMInstruction *buildStrCmpEQCI(LLVMInstruction *ins); +}; + +} // namespace libscratchcpp::llvmins diff --git a/src/engine/internal/llvm/instructions/control.cpp b/src/engine/internal/llvm/instructions/control.cpp new file mode 100644 index 000000000..d3b769af3 --- /dev/null +++ b/src/engine/internal/llvm/instructions/control.cpp @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "control.h" +#include "../llvminstruction.h" +#include "../llvmbuildutils.h" + +using namespace libscratchcpp; +using namespace libscratchcpp::llvmins; + +ProcessResult Control::process(LLVMInstruction *ins) +{ + ProcessResult ret(true, ins); + + switch (ins->type) { + case LLVMInstruction::Type::Select: + ret.next = buildSelect(ins); + break; + + case LLVMInstruction::Type::Yield: + ret.next = buildYield(ins); + break; + + case LLVMInstruction::Type::BeginIf: + ret.next = buildBeginIf(ins); + break; + + case LLVMInstruction::Type::BeginElse: + ret.next = buildBeginElse(ins); + break; + + case LLVMInstruction::Type::EndIf: + ret.next = buildEndIf(ins); + break; + + case LLVMInstruction::Type::BeginRepeatLoop: + ret.next = buildBeginRepeatLoop(ins); + break; + + case LLVMInstruction::Type::LoopIndex: + ret.next = buildLoopIndex(ins); + break; + + case LLVMInstruction::Type::BeginWhileLoop: + ret.next = buildBeginWhileLoop(ins); + break; + + case LLVMInstruction::Type::BeginRepeatUntilLoop: + ret.next = buildBeginRepeatUntilLoop(ins); + break; + + case LLVMInstruction::Type::BeginLoopCondition: + ret.next = buildBeginLoopCondition(ins); + break; + + case LLVMInstruction::Type::EndLoop: + ret.next = buildEndLoop(ins); + break; + + case LLVMInstruction::Type::Stop: + ret.next = buildStop(ins); + break; + + default: + ret.match = false; + break; + } + + return ret; +} + +LLVMInstruction *Control::buildSelect(LLVMInstruction *ins) +{ + assert(ins->args.size() == 3); + const auto &arg1 = ins->args[0]; + const auto &arg2 = ins->args[1]; + const auto &arg3 = ins->args[2]; + auto type = arg2.first; + llvm::Value *cond = m_utils.castValue(arg1.second, arg1.first); + llvm::Value *trueValue; + llvm::Value *falseValue; + + if (type == Compiler::StaticType::Unknown) { + trueValue = m_utils.createValue(arg2.second); + falseValue = m_utils.createValue(arg3.second); + } else { + trueValue = m_utils.castValue(arg2.second, type); + falseValue = m_utils.castValue(arg3.second, type); + } + + ins->functionReturnReg->value = m_builder.CreateSelect(cond, trueValue, falseValue); + return ins->next; +} + +LLVMInstruction *Control::buildYield(LLVMInstruction *ins) +{ + m_utils.createSuspend(); + return ins->next; +} + +LLVMInstruction *Control::buildBeginIf(LLVMInstruction *ins) +{ + LLVMIfStatement statement; + statement.beforeIf = m_builder.GetInsertBlock(); + statement.body = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); + + // Use last reg + assert(ins->args.size() == 1); + const auto ® = ins->args[0]; + assert(reg.first == Compiler::StaticType::Bool); + statement.condition = m_utils.castValue(reg.second, reg.first); + + // Switch to body branch + m_builder.SetInsertPoint(statement.body); + + m_utils.ifStatements().push_back(statement); + m_utils.pushScopeLevel(); + + return ins->next; +} + +LLVMInstruction *Control::buildBeginElse(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Function *function = m_utils.function(); + + assert(!m_utils.ifStatements().empty()); + LLVMIfStatement &statement = m_utils.ifStatements().back(); + + // Jump to the branch after the if statement + assert(!statement.afterIf); + statement.afterIf = llvm::BasicBlock::Create(llvmCtx, "", function); + m_utils.freeScopeHeap(); + m_builder.CreateBr(statement.afterIf); + + // Create else branch + assert(!statement.elseBranch); + statement.elseBranch = llvm::BasicBlock::Create(llvmCtx, "", function); + + // Since there's an else branch, the conditional instruction should jump to it + m_builder.SetInsertPoint(statement.beforeIf); + m_builder.CreateCondBr(statement.condition, statement.body, statement.elseBranch); + + // Switch to the else branch + m_builder.SetInsertPoint(statement.elseBranch); + + return ins->next; +} + +LLVMInstruction *Control::buildEndIf(LLVMInstruction *ins) +{ + auto &ifStatements = m_utils.ifStatements(); + assert(!ifStatements.empty()); + LLVMIfStatement &statement = ifStatements.back(); + m_utils.freeScopeHeap(); + + // Jump to the branch after the if statement + if (!statement.afterIf) + statement.afterIf = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); + + m_builder.CreateBr(statement.afterIf); + + if (statement.elseBranch) { + } else { + // If there wasn't an 'else' branch, create a conditional instruction which skips the if statement if false + m_builder.SetInsertPoint(statement.beforeIf); + m_builder.CreateCondBr(statement.condition, statement.body, statement.afterIf); + } + + // Switch to the branch after the if statement + m_builder.SetInsertPoint(statement.afterIf); + + ifStatements.pop_back(); + m_utils.popScopeLevel(); + + return ins->next; +} + +LLVMInstruction *Control::buildBeginRepeatLoop(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Function *function = m_utils.function(); + + LLVMLoop loop; + loop.isRepeatLoop = true; + + // index = 0 + llvm::Constant *zero = llvm::ConstantInt::get(m_builder.getInt64Ty(), 0, true); + loop.index = m_utils.addAlloca(m_builder.getInt64Ty()); + m_builder.CreateStore(zero, loop.index); + + // Create branches + llvm::BasicBlock *roundBranch = llvm::BasicBlock::Create(llvmCtx, "", function); + loop.conditionBranch = llvm::BasicBlock::Create(llvmCtx, "", function); + loop.afterLoop = llvm::BasicBlock::Create(llvmCtx, "", function); + + // Use last reg for count + assert(ins->args.size() == 1); + const auto ® = ins->args[0]; + assert(reg.first == Compiler::StaticType::Number); + llvm::Value *count = m_utils.castValue(reg.second, reg.first); + llvm::Value *isInf = m_builder.CreateFCmpOEQ(count, llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), false)); + + // Clamp count if <= 0 (we can skip the loop if count is not positive) + llvm::Value *comparison = m_builder.CreateFCmpULE(count, llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0))); + m_builder.CreateCondBr(comparison, loop.afterLoop, roundBranch); + + // Round (Scratch-specific behavior) + m_builder.SetInsertPoint(roundBranch); + llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::round, { count->getType() }); + count = m_builder.CreateCall(roundFunc, { count }); + count = m_builder.CreateFPToUI(count, m_builder.getInt64Ty()); // cast to unsigned integer + count = m_builder.CreateSelect(isInf, zero, count); + + // Jump to condition branch + m_builder.CreateBr(loop.conditionBranch); + + // Check index + m_builder.SetInsertPoint(loop.conditionBranch); + + llvm::BasicBlock *body = llvm::BasicBlock::Create(llvmCtx, "", function); + + if (!loop.afterLoop) + loop.afterLoop = llvm::BasicBlock::Create(llvmCtx, "", function); + + llvm::Value *currentIndex = m_builder.CreateLoad(m_builder.getInt64Ty(), loop.index); + comparison = m_builder.CreateOr(isInf, m_builder.CreateICmpULT(currentIndex, count)); + m_builder.CreateCondBr(comparison, body, loop.afterLoop); + + // Switch to body branch + m_builder.SetInsertPoint(body); + + m_utils.loops().push_back(loop); + m_utils.pushScopeLevel(); + + return ins->next; +} + +LLVMInstruction *Control::buildLoopIndex(LLVMInstruction *ins) +{ + assert(!m_utils.loops().empty()); + 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()); + + return ins->next; +} + +LLVMInstruction *Control::buildBeginWhileLoop(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Function *function = m_utils.function(); + + assert(!m_utils.loops().empty()); + LLVMLoop &loop = m_utils.loops().back(); + + // Create branches + llvm::BasicBlock *body = llvm::BasicBlock::Create(llvmCtx, "", function); + loop.afterLoop = llvm::BasicBlock::Create(llvmCtx, "", function); + + // Use last reg + assert(ins->args.size() == 1); + const auto ® = ins->args[0]; + assert(reg.first == Compiler::StaticType::Bool); + llvm::Value *condition = m_utils.castValue(reg.second, reg.first); + m_builder.CreateCondBr(condition, body, loop.afterLoop); + + // Switch to body branch + m_builder.SetInsertPoint(body); + m_utils.pushScopeLevel(); + + return ins->next; +} + +LLVMInstruction *Control::buildBeginRepeatUntilLoop(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Function *function = m_utils.function(); + + assert(!m_utils.loops().empty()); + LLVMLoop &loop = m_utils.loops().back(); + + // Create branches + llvm::BasicBlock *body = llvm::BasicBlock::Create(llvmCtx, "", function); + loop.afterLoop = llvm::BasicBlock::Create(llvmCtx, "", function); + + // Use last reg + assert(ins->args.size() == 1); + const auto ® = ins->args[0]; + assert(reg.first == Compiler::StaticType::Bool); + llvm::Value *condition = m_utils.castValue(reg.second, reg.first); + m_builder.CreateCondBr(condition, loop.afterLoop, body); + + // Switch to body branch + m_builder.SetInsertPoint(body); + m_utils.pushScopeLevel(); + + return ins->next; +} + +LLVMInstruction *Control::buildBeginLoopCondition(LLVMInstruction *ins) +{ + LLVMLoop loop; + loop.isRepeatLoop = false; + loop.conditionBranch = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); + m_builder.CreateBr(loop.conditionBranch); + m_builder.SetInsertPoint(loop.conditionBranch); + m_utils.loops().push_back(loop); + + return ins->next; +} + +LLVMInstruction *Control::buildEndLoop(LLVMInstruction *ins) +{ + auto &loops = m_utils.loops(); + assert(!loops.empty()); + LLVMLoop &loop = loops.back(); + + if (loop.isRepeatLoop) { + // Increment index + llvm::Value *currentIndex = m_builder.CreateLoad(m_builder.getInt64Ty(), loop.index); + llvm::Value *incremented = m_builder.CreateAdd(currentIndex, llvm::ConstantInt::get(m_builder.getInt64Ty(), 1, false)); + m_builder.CreateStore(incremented, loop.index); + } + + // Jump to the condition branch + m_utils.freeScopeHeap(); + m_builder.CreateBr(loop.conditionBranch); + + // Switch to the branch after the loop + m_builder.SetInsertPoint(loop.afterLoop); + + loops.pop_back(); + m_utils.popScopeLevel(); + + return ins->next; +} + +LLVMInstruction *Control::buildStop(LLVMInstruction *ins) +{ + m_utils.freeScopeHeap(); + m_builder.CreateBr(m_utils.endBranch()); + llvm::BasicBlock *nextBranch = llvm::BasicBlock::Create(m_utils.llvmCtx(), "", m_utils.function()); + m_builder.SetInsertPoint(nextBranch); + + return ins->next; +} diff --git a/src/engine/internal/llvm/instructions/control.h b/src/engine/internal/llvm/instructions/control.h new file mode 100644 index 000000000..0c80f8178 --- /dev/null +++ b/src/engine/internal/llvm/instructions/control.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "instructiongroup.h" + +namespace libscratchcpp::llvmins +{ + +class Control : public InstructionGroup +{ + public: + using InstructionGroup::InstructionGroup; + + ProcessResult process(LLVMInstruction *ins) override; + + private: + LLVMInstruction *buildSelect(LLVMInstruction *ins); + LLVMInstruction *buildYield(LLVMInstruction *ins); + LLVMInstruction *buildBeginIf(LLVMInstruction *ins); + LLVMInstruction *buildBeginElse(LLVMInstruction *ins); + LLVMInstruction *buildEndIf(LLVMInstruction *ins); + LLVMInstruction *buildBeginRepeatLoop(LLVMInstruction *ins); + LLVMInstruction *buildLoopIndex(LLVMInstruction *ins); + LLVMInstruction *buildBeginWhileLoop(LLVMInstruction *ins); + LLVMInstruction *buildBeginRepeatUntilLoop(LLVMInstruction *ins); + LLVMInstruction *buildBeginLoopCondition(LLVMInstruction *ins); + LLVMInstruction *buildEndLoop(LLVMInstruction *ins); + LLVMInstruction *buildStop(LLVMInstruction *ins); +}; + +} // namespace libscratchcpp::llvmins diff --git a/src/engine/internal/llvm/instructions/functions.cpp b/src/engine/internal/llvm/instructions/functions.cpp new file mode 100644 index 000000000..cc5c9679d --- /dev/null +++ b/src/engine/internal/llvm/instructions/functions.cpp @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "functions.h" +#include "../llvminstruction.h" +#include "../llvmbuildutils.h" + +using namespace libscratchcpp; +using namespace libscratchcpp::llvmins; + +ProcessResult Functions::process(LLVMInstruction *ins) +{ + ProcessResult ret(true, ins); + + switch (ins->type) { + case LLVMInstruction::Type::FunctionCall: + ret.next = buildFunctionCall(ins); + break; + + default: + ret.match = false; + break; + } + + return ret; +} + +LLVMInstruction *Functions::buildFunctionCall(LLVMInstruction *ins) +{ + std::vector types; + std::vector args; + + // Variables must be synchronized because the function can read them + m_utils.syncVariables(m_utils.targetVariables()); + + // Add execution context arg + if (ins->functionCtxArg) { + types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_utils.llvmCtx()), 0)); + args.push_back(m_utils.executionContextPtr()); + } + + // Add target pointer arg + if (ins->functionTargetArg) { + types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_utils.llvmCtx()), 0)); + args.push_back(m_utils.targetPtr()); + } + + // Args + for (auto &arg : ins->args) { + types.push_back(m_utils.getType(arg.first)); + args.push_back(m_utils.castValue(arg.second, arg.first)); + } + + llvm::Type *retType = m_utils.getType(ins->functionReturnReg ? ins->functionReturnReg->type() : Compiler::StaticType::Void); + llvm::Value *ret = m_builder.CreateCall(m_utils.functions().resolveFunction(ins->functionName, llvm::FunctionType::get(retType, types, false)), args); + + if (ins->functionReturnReg) { + ins->functionReturnReg->value = ret; + + if (ins->functionReturnReg->type() == Compiler::StaticType::String) + m_utils.freeStringLater(ins->functionReturnReg->value); + } + + return ins->next; +} diff --git a/src/engine/internal/llvm/instructions/functions.h b/src/engine/internal/llvm/instructions/functions.h new file mode 100644 index 000000000..2485ecbf4 --- /dev/null +++ b/src/engine/internal/llvm/instructions/functions.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "instructiongroup.h" + +namespace libscratchcpp::llvmins +{ + +class Functions : public InstructionGroup +{ + public: + using InstructionGroup::InstructionGroup; + + ProcessResult process(LLVMInstruction *ins) override; + + private: + LLVMInstruction *buildFunctionCall(LLVMInstruction *ins); +}; + +} // namespace libscratchcpp::llvmins diff --git a/src/engine/internal/llvm/instructions/instructionbuilder.cpp b/src/engine/internal/llvm/instructions/instructionbuilder.cpp new file mode 100644 index 000000000..2263f521f --- /dev/null +++ b/src/engine/internal/llvm/instructions/instructionbuilder.cpp @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "instructionbuilder.h" +#include "functions.h" +#include "math.h" +#include "comparison.h" +#include "string.h" +#include "logic.h" +#include "control.h" +#include "variables.h" +#include "lists.h" +#include "procedures.h" + +using namespace libscratchcpp; +using namespace libscratchcpp::llvmins; + +InstructionBuilder::InstructionBuilder(LLVMBuildUtils &utils) +{ + // Create groups + m_groups.push_back(std::make_shared(utils)); + m_groups.push_back(std::make_shared(utils)); + m_groups.push_back(std::make_shared(utils)); + m_groups.push_back(std::make_shared(utils)); + m_groups.push_back(std::make_shared(utils)); + m_groups.push_back(std::make_shared(utils)); + m_groups.push_back(std::make_shared(utils)); + m_groups.push_back(std::make_shared(utils)); + m_groups.push_back(std::make_shared(utils)); +} + +LLVMInstruction *InstructionBuilder::process(LLVMInstruction *ins) +{ + // Process all groups + for (const auto &group : m_groups) { + ProcessResult result = group->process(ins); + + if (result.match) + return result.next; + } + + assert(false); // instruction not found + return ins; +} diff --git a/src/engine/internal/llvm/instructions/instructionbuilder.h b/src/engine/internal/llvm/instructions/instructionbuilder.h new file mode 100644 index 000000000..4c1190ae2 --- /dev/null +++ b/src/engine/internal/llvm/instructions/instructionbuilder.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "instructiongroup.h" + +namespace libscratchcpp::llvmins +{ + +class InstructionBuilder +{ + public: + InstructionBuilder(LLVMBuildUtils &utils); + + LLVMInstruction *process(LLVMInstruction *ins); + + private: + std::vector> m_groups; +}; + +} // namespace libscratchcpp::llvmins diff --git a/src/engine/internal/llvm/instructions/instructiongroup.cpp b/src/engine/internal/llvm/instructions/instructiongroup.cpp new file mode 100644 index 000000000..acc00ba89 --- /dev/null +++ b/src/engine/internal/llvm/instructions/instructiongroup.cpp @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "instructiongroup.h" +#include "../llvmbuildutils.h" + +using namespace libscratchcpp::llvmins; + +InstructionGroup::InstructionGroup(LLVMBuildUtils &utils) : + m_utils(utils), + m_builder(utils.builder()) +{ +} diff --git a/src/engine/internal/llvm/instructions/instructiongroup.h b/src/engine/internal/llvm/instructions/instructiongroup.h new file mode 100644 index 000000000..6017f2ab4 --- /dev/null +++ b/src/engine/internal/llvm/instructions/instructiongroup.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include "processresult.h" + +namespace libscratchcpp +{ + +class LLVMBuildUtils; + +namespace llvmins +{ + +class InstructionGroup +{ + public: + InstructionGroup(LLVMBuildUtils &utils); + + virtual ProcessResult process(LLVMInstruction *ins) = 0; + + protected: + LLVMBuildUtils &m_utils; + llvm::IRBuilder<> &m_builder; +}; + +} // namespace llvmins +} // namespace libscratchcpp diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp new file mode 100644 index 000000000..5d83856b3 --- /dev/null +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "lists.h" +#include "../llvminstruction.h" +#include "../llvmbuildutils.h" +#include "../llvmconstantregister.h" + +using namespace libscratchcpp; +using namespace libscratchcpp::llvmins; + +ProcessResult Lists::process(LLVMInstruction *ins) +{ + ProcessResult ret(true, ins); + + switch (ins->type) { + case LLVMInstruction::Type::ClearList: + ret.next = buildClearList(ins); + break; + + case LLVMInstruction::Type::RemoveListItem: + ret.next = buildRemoveListItem(ins); + break; + + case LLVMInstruction::Type::AppendToList: + ret.next = buildAppendToList(ins); + break; + + case LLVMInstruction::Type::InsertToList: + ret.next = buildInsertToList(ins); + break; + + case LLVMInstruction::Type::ListReplace: + ret.next = buildListReplace(ins); + break; + + case LLVMInstruction::Type::GetListContents: + ret.next = buildGetListContents(ins); + break; + + case LLVMInstruction::Type::GetListItem: + ret.next = buildGetListItem(ins); + break; + + case LLVMInstruction::Type::GetListSize: + ret.next = buildGetListSize(ins); + break; + + case LLVMInstruction::Type::GetListItemIndex: + ret.next = buildGetListItemIndex(ins); + break; + + case LLVMInstruction::Type::ListContainsItem: + ret.next = buildListContainsItem(ins); + break; + + default: + ret.match = false; + break; + } + + return ret; +} + +LLVMInstruction *Lists::buildClearList(LLVMInstruction *ins) +{ + assert(ins->args.size() == 0); + LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + m_builder.CreateCall(m_utils.functions().resolve_list_clear(), listPtr.ptr); + + return ins->next; +} + +LLVMInstruction *Lists::buildRemoveListItem(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Function *function = m_utils.function(); + + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + + // Range check + llvm::Value *min = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); + llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); + 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::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.CreateBr(nextBlock); + + m_builder.SetInsertPoint(nextBlock); + return ins->next; +} + +LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Function *function = m_utils.function(); + + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + Compiler::StaticType type = m_utils.optimizeRegisterType(arg.second); + LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + + Compiler::StaticType listType = Compiler::StaticType::Unknown; + + if (m_utils.warp()) + listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false); + + // Check if enough space is allocated + llvm::Value *allocatedSize = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.allocatedSizePtr); + llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); + llvm::Value *isAllocated = m_builder.CreateICmpUGT(allocatedSize, size); + llvm::BasicBlock *ifBlock = llvm::BasicBlock::Create(llvmCtx, "", function); + llvm::BasicBlock *elseBlock = llvm::BasicBlock::Create(llvmCtx, "", function); + llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(llvmCtx, "", function); + m_builder.CreateCondBr(isAllocated, ifBlock, elseBlock); + + // If there's enough space, use the allocated memory + m_builder.SetInsertPoint(ifBlock); + llvm::Value *itemPtr = m_utils.getListItem(listPtr, size); + m_utils.createReusedValueStore(arg.second, itemPtr, type, listType); + m_builder.CreateStore(m_builder.CreateAdd(size, m_builder.getInt64(1)), listPtr.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.createReusedValueStore(arg.second, itemPtr, type, listType); + m_builder.CreateBr(nextBlock); + + m_builder.SetInsertPoint(nextBlock); + return ins->next; +} + +LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Function *function = m_utils.function(); + + assert(ins->args.size() == 2); + const auto &indexArg = ins->args[0]; + const auto &valueArg = ins->args[1]; + Compiler::StaticType type = m_utils.optimizeRegisterType(valueArg.second); + LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + + Compiler::StaticType listType = Compiler::StaticType::Unknown; + + if (m_utils.warp()) + listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false); + + // Range check + llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); + 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::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 }); + m_utils.createReusedValueStore(valueArg.second, itemPtr, type, listType); + + m_builder.CreateBr(nextBlock); + + m_builder.SetInsertPoint(nextBlock); + return ins->next; +} + +LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Function *function = m_utils.function(); + + assert(ins->args.size() == 2); + const auto &indexArg = ins->args[0]; + const auto &valueArg = ins->args[1]; + Compiler::StaticType type = m_utils.optimizeRegisterType(valueArg.second); + LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + + Compiler::StaticType listType = Compiler::StaticType::Unknown; + + if (m_utils.warp()) + listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false); + + // Range check + llvm::Value *min = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); + llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); + 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::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); + m_utils.createValueStore(valueArg.second, itemPtr, type, listType); + m_builder.CreateBr(nextBlock); + + m_builder.SetInsertPoint(nextBlock); + return ins->next; +} + +LLVMInstruction *Lists::buildGetListContents(LLVMInstruction *ins) +{ + assert(ins->args.size() == 0); + const LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + llvm::Value *ptr = m_builder.CreateCall(m_utils.functions().resolve_list_to_string(), listPtr.ptr); + m_utils.freeStringLater(ptr); + ins->functionReturnReg->value = ptr; + + return ins->next; +} + +LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) +{ + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + + Compiler::StaticType listType = Compiler::StaticType::Unknown; + + if (m_utils.warp()) + listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false); + + llvm::Value *min = llvm::ConstantFP::get(m_utils.llvmCtx(), llvm::APFloat(0.0)); + llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); + 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)); + + LLVMConstantRegister nullReg(listType == Compiler::StaticType::Unknown ? Compiler::StaticType::Number : listType, Value()); + 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); + ins->functionReturnReg->setType(listType); + + return ins->next; +} + +LLVMInstruction *Lists::buildGetListSize(LLVMInstruction *ins) +{ + assert(ins->args.size() == 0); + const LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); + ins->functionReturnReg->value = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); + + return ins->next; +} + +LLVMInstruction *Lists::buildGetListItemIndex(LLVMInstruction *ins) +{ + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + + Compiler::StaticType listType = Compiler::StaticType::Unknown; + + if (m_utils.warp()) + listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false); + + ins->functionReturnReg->value = m_builder.CreateSIToFP(m_utils.getListItemIndex(listPtr, listType, arg.second), m_builder.getDoubleTy()); + return ins->next; +} + +LLVMInstruction *Lists::buildListContainsItem(LLVMInstruction *ins) +{ + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + + Compiler::StaticType listType = Compiler::StaticType::Unknown; + + if (m_utils.warp()) + listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false); + + llvm::Value *index = m_utils.getListItemIndex(listPtr, listType, arg.second); + ins->functionReturnReg->value = m_builder.CreateICmpSGT(index, llvm::ConstantInt::get(m_builder.getInt64Ty(), -1, true)); + + return ins->next; +} diff --git a/src/engine/internal/llvm/instructions/lists.h b/src/engine/internal/llvm/instructions/lists.h new file mode 100644 index 000000000..59aed8899 --- /dev/null +++ b/src/engine/internal/llvm/instructions/lists.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "instructiongroup.h" + +namespace libscratchcpp::llvmins +{ + +class Lists : public InstructionGroup +{ + public: + using InstructionGroup::InstructionGroup; + + ProcessResult process(LLVMInstruction *ins) override; + + private: + LLVMInstruction *buildClearList(LLVMInstruction *ins); + LLVMInstruction *buildRemoveListItem(LLVMInstruction *ins); + LLVMInstruction *buildAppendToList(LLVMInstruction *ins); + LLVMInstruction *buildInsertToList(LLVMInstruction *ins); + LLVMInstruction *buildListReplace(LLVMInstruction *ins); + LLVMInstruction *buildGetListContents(LLVMInstruction *ins); + LLVMInstruction *buildGetListItem(LLVMInstruction *ins); + LLVMInstruction *buildGetListSize(LLVMInstruction *ins); + LLVMInstruction *buildGetListItemIndex(LLVMInstruction *ins); + LLVMInstruction *buildListContainsItem(LLVMInstruction *ins); +}; + +} // namespace libscratchcpp::llvmins diff --git a/src/engine/internal/llvm/instructions/logic.cpp b/src/engine/internal/llvm/instructions/logic.cpp new file mode 100644 index 000000000..e4410286b --- /dev/null +++ b/src/engine/internal/llvm/instructions/logic.cpp @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "logic.h" +#include "../llvminstruction.h" +#include "../llvmbuildutils.h" + +using namespace libscratchcpp; +using namespace libscratchcpp::llvmins; + +ProcessResult Logic::process(LLVMInstruction *ins) +{ + ProcessResult ret(true, ins); + + switch (ins->type) { + case LLVMInstruction::Type::And: + ret.next = buildAnd(ins); + break; + + case LLVMInstruction::Type::Or: + ret.next = buildOr(ins); + break; + + case LLVMInstruction::Type::Not: + ret.next = buildNot(ins); + break; + + default: + ret.match = false; + ret.next = (ins); + break; + } + + return ret; +} + +LLVMInstruction *Logic::buildAnd(LLVMInstruction *ins) +{ + assert(ins->args.size() == 2); + const auto &arg1 = ins->args[0]; + const auto &arg2 = ins->args[1]; + llvm::Value *bool1 = m_utils.castValue(arg1.second, arg1.first); + llvm::Value *bool2 = m_utils.castValue(arg2.second, arg2.first); + ins->functionReturnReg->value = m_builder.CreateAnd(bool1, bool2); + + return ins->next; +} + +LLVMInstruction *Logic::buildOr(LLVMInstruction *ins) +{ + assert(ins->args.size() == 2); + const auto &arg1 = ins->args[0]; + const auto &arg2 = ins->args[1]; + llvm::Value *bool1 = m_utils.castValue(arg1.second, arg1.first); + llvm::Value *bool2 = m_utils.castValue(arg2.second, arg2.first); + ins->functionReturnReg->value = m_builder.CreateOr(bool1, bool2); + + return ins->next; +} + +LLVMInstruction *Logic::buildNot(LLVMInstruction *ins) +{ + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + llvm::Value *value = m_utils.castValue(arg.second, arg.first); + ins->functionReturnReg->value = m_builder.CreateNot(value); + + return ins->next; +} diff --git a/src/engine/internal/llvm/instructions/logic.h b/src/engine/internal/llvm/instructions/logic.h new file mode 100644 index 000000000..8ad284daa --- /dev/null +++ b/src/engine/internal/llvm/instructions/logic.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "instructiongroup.h" + +namespace libscratchcpp::llvmins +{ + +class Logic : public InstructionGroup +{ + public: + using InstructionGroup::InstructionGroup; + + ProcessResult process(LLVMInstruction *ins) override; + + private: + LLVMInstruction *buildAnd(LLVMInstruction *ins); + LLVMInstruction *buildOr(LLVMInstruction *ins); + LLVMInstruction *buildNot(LLVMInstruction *ins); +}; + +} // namespace libscratchcpp::llvmins diff --git a/src/engine/internal/llvm/instructions/math.cpp b/src/engine/internal/llvm/instructions/math.cpp new file mode 100644 index 000000000..1a6c452c4 --- /dev/null +++ b/src/engine/internal/llvm/instructions/math.cpp @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "math.h" +#include "../llvminstruction.h" +#include "../llvmbuildutils.h" + +using namespace libscratchcpp; +using namespace libscratchcpp::llvmins; + +ProcessResult Math::process(LLVMInstruction *ins) +{ + ProcessResult ret(true, ins); + + switch (ins->type) { + case LLVMInstruction::Type::Add: + ret.next = buildAdd(ins); + break; + + case LLVMInstruction::Type::Sub: + ret.next = buildSub(ins); + break; + + case LLVMInstruction::Type::Mul: + ret.next = buildMul(ins); + break; + + case LLVMInstruction::Type::Div: + ret.next = buildDiv(ins); + break; + + case LLVMInstruction::Type::Random: + ret.next = buildRandom(ins); + break; + + case LLVMInstruction::Type::RandomInt: + ret.next = buildRandomInt(ins); + break; + + case LLVMInstruction::Type::Mod: + ret.next = buildMod(ins); + break; + + case LLVMInstruction::Type::Round: + ret.next = buildRound(ins); + break; + + case LLVMInstruction::Type::Abs: + ret.next = buildAbs(ins); + break; + + case LLVMInstruction::Type::Floor: + ret.next = buildFloor(ins); + break; + + case LLVMInstruction::Type::Ceil: + ret.next = buildCeil(ins); + break; + + case LLVMInstruction::Type::Sqrt: + ret.next = buildSqrt(ins); + break; + + case LLVMInstruction::Type::Sin: + ret.next = buildSin(ins); + break; + + case LLVMInstruction::Type::Cos: + ret.next = buildCos(ins); + break; + + case LLVMInstruction::Type::Tan: + ret.next = buildTan(ins); + break; + + case LLVMInstruction::Type::Asin: + ret.next = buildAsin(ins); + break; + + case LLVMInstruction::Type::Acos: + ret.next = buildAcos(ins); + break; + + case LLVMInstruction::Type::Atan: + ret.next = buildAtan(ins); + break; + + case LLVMInstruction::Type::Ln: + ret.next = buildLn(ins); + break; + + case LLVMInstruction::Type::Log10: + ret.next = buildLog10(ins); + break; + + case LLVMInstruction::Type::Exp: + ret.next = buildExp(ins); + break; + + case LLVMInstruction::Type::Exp10: + ret.next = buildExp10(ins); + break; + + default: + ret.match = false; + break; + } + + return ret; +} + +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); + + return ins->next; +} + +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); + + return ins->next; +} + +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); + + return ins->next; +} + +LLVMInstruction *Math::buildDiv(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.CreateFDiv(num1, num2); + + return ins->next; +} + +LLVMInstruction *Math::buildRandom(LLVMInstruction *ins) +{ + assert(ins->args.size() == 2); + const auto &arg1 = ins->args[0]; + const auto &arg2 = ins->args[1]; + LLVMRegister *reg1 = arg1.second; + LLVMRegister *reg2 = arg2.second; + + if (reg1->type() == Compiler::StaticType::Bool && reg2->type() == Compiler::StaticType::Bool) { + llvm::Value *bool1 = m_utils.castValue(arg1.second, Compiler::StaticType::Bool); + llvm::Value *bool2 = m_utils.castValue(arg2.second, Compiler::StaticType::Bool); + ins->functionReturnReg->value = m_builder.CreateCall(m_utils.functions().resolve_llvm_random_bool(), { m_utils.executionContextPtr(), bool1, bool2 }); + } else { + llvm::Constant *inf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), false); + llvm::Value *num1 = m_utils.removeNaN(m_utils.castValue(arg1.second, Compiler::StaticType::Number)); + llvm::Value *num2 = m_utils.removeNaN(m_utils.castValue(arg2.second, Compiler::StaticType::Number)); + llvm::Value *sum = m_builder.CreateFAdd(num1, num2); + llvm::Value *sumDiv = m_builder.CreateFDiv(sum, inf); + llvm::Value *isInfOrNaN = m_utils.isNaN(sumDiv); + + // NOTE: The random function will be called even in edge cases where it isn't needed, but they're rare, so it shouldn't be an issue + if (reg1->type() == Compiler::StaticType::Number && reg2->type() == Compiler::StaticType::Number) + ins->functionReturnReg->value = + m_builder.CreateSelect(isInfOrNaN, sum, m_builder.CreateCall(m_utils.functions().resolve_llvm_random_double(), { m_utils.executionContextPtr(), num1, num2 })); + else { + llvm::Value *value1 = m_utils.createValue(reg1); + llvm::Value *value2 = m_utils.createValue(reg2); + ins->functionReturnReg->value = m_builder.CreateSelect(isInfOrNaN, sum, m_builder.CreateCall(m_utils.functions().resolve_llvm_random(), { m_utils.executionContextPtr(), value1, value2 })); + } + } + + return ins->next; +} + +LLVMInstruction *Math::buildRandomInt(LLVMInstruction *ins) +{ + assert(ins->args.size() == 2); + const auto &arg1 = ins->args[0]; + 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 }); + + return ins->next; +} + +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); + + return ins->next; +} + +LLVMInstruction *Math::buildRound(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Module *module = m_utils.module(); + + 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)) + 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) + ins->functionReturnReg->value = m_builder.CreateSelect(notNegative, roundNum, m_builder.CreateSelect(negativeCond, negativeZero, negativeRound)); + + return ins->next; +} + +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); + + return ins->next; +} + +LLVMInstruction *Math::buildFloor(LLVMInstruction *ins) +{ + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + 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); + + return ins->next; +} + +LLVMInstruction *Math::buildCeil(LLVMInstruction *ins) +{ + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + 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); + + return ins->next; +} + +LLVMInstruction *Math::buildSqrt(LLVMInstruction *ins) +{ + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + // sqrt(x) + 0.0 + // This avoids negative zero + llvm::Constant *zero = llvm::ConstantFP::get(m_utils.llvmCtx(), llvm::APFloat(0.0)); + llvm::Function *sqrtFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::sqrt, m_builder.getDoubleTy()); + llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + ins->functionReturnReg->value = m_builder.CreateFAdd(m_builder.CreateCall(sqrtFunc, num), zero); + + return ins->next; +} + +LLVMInstruction *Math::buildSin(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Module *module = m_utils.module(); + + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + // round(sin(x * pi / 180.0) * 1e10) / 1e10 + 0.0 + // +0.0 to avoid -0.0 + llvm::Constant *zero = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); + llvm::Constant *pi = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(std::acos(-1.0))); + llvm::Constant *piDeg = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(180.0)); + llvm::Constant *factor = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(1e10)); + llvm::Function *sinFunc = llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::sin, m_builder.getDoubleTy()); + llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::round, m_builder.getDoubleTy()); + llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + llvm::Value *sinResult = m_builder.CreateCall(sinFunc, m_builder.CreateFDiv(m_builder.CreateFMul(num, pi), piDeg)); // sin(x * pi / 180) + llvm::Value *rounded = m_builder.CreateCall(roundFunc, m_builder.CreateFMul(sinResult, factor)); // round(sin(x * 180) * 1e10) + ins->functionReturnReg->value = m_builder.CreateFAdd(m_builder.CreateFDiv(rounded, factor), zero); + + return ins->next; +} + +LLVMInstruction *Math::buildCos(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Module *module = m_utils.module(); + + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + // round(cos(x * pi / 180.0) * 1e10) / 1e10 + llvm::Constant *pi = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(std::acos(-1.0))); + llvm::Constant *piDeg = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(180.0)); + llvm::Constant *factor = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(1e10)); + llvm::Function *cosFunc = llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::cos, m_builder.getDoubleTy()); + llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::round, m_builder.getDoubleTy()); + llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + llvm::Value *cosResult = m_builder.CreateCall(cosFunc, m_builder.CreateFDiv(m_builder.CreateFMul(num, pi), piDeg)); // cos(x * pi / 180) + llvm::Value *rounded = m_builder.CreateCall(roundFunc, m_builder.CreateFMul(cosResult, factor)); // round(cos(x * 180) * 1e10) + ins->functionReturnReg->value = m_builder.CreateFDiv(rounded, factor); + + return ins->next; +} + +LLVMInstruction *Math::buildTan(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Module *module = m_utils.module(); + + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + // ((mod = rem(x, 360.0)) == -270.0 || mod == 90.0) ? inf : ((mod == -90.0 || mod == 270.0) ? -inf : round(tan(x * pi / 180.0) * 1e10) / 1e10 + 0.0) + // +0.0 to avoid -0.0 + llvm::Constant *zero = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); + llvm::Constant *full = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(360.0)); + llvm::Constant *posInf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), false); + llvm::Constant *negInf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), true); + llvm::Constant *undefined1 = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(-270.0)); + llvm::Constant *undefined2 = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(90.0)); + llvm::Constant *undefined3 = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(-90.0)); + llvm::Constant *undefined4 = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(270.0)); + llvm::Constant *pi = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(std::acos(-1.0))); + llvm::Constant *piDeg = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(180.0)); + llvm::Constant *factor = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(1e10)); + llvm::Function *tanFunc = llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::tan, m_builder.getDoubleTy()); + llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::round, m_builder.getDoubleTy()); + llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + llvm::Value *mod = m_builder.CreateFRem(num, full); + llvm::Value *isUndefined1 = m_builder.CreateFCmpOEQ(mod, undefined1); // rem(x, 360.0) == -270.0 + llvm::Value *isUndefined2 = m_builder.CreateFCmpOEQ(mod, undefined2); // rem(x, 360.0) == 90.0 + llvm::Value *isUndefined3 = m_builder.CreateFCmpOEQ(mod, undefined3); // rem(x, 360.0) == -90.0 + llvm::Value *isUndefined4 = m_builder.CreateFCmpOEQ(mod, undefined4); // rem(x, 360.0) == 270.0 + llvm::Value *tanResult = m_builder.CreateCall(tanFunc, m_builder.CreateFDiv(m_builder.CreateFMul(num, pi), piDeg)); // tan(x * pi / 180) + llvm::Value *rounded = m_builder.CreateCall(roundFunc, m_builder.CreateFMul(tanResult, factor)); // round(tan(x * 180) * 1e10) + llvm::Value *result = m_builder.CreateFAdd(m_builder.CreateFDiv(rounded, factor), zero); // round(tan(x * pi / 180.0) * 1e10) / 1e10 + 0.0 + llvm::Value *inner = m_builder.CreateSelect(m_builder.CreateOr(isUndefined3, isUndefined4), negInf, result); + ins->functionReturnReg->value = m_builder.CreateSelect(m_builder.CreateOr(isUndefined1, isUndefined2), posInf, inner); + + return ins->next; +} + +LLVMInstruction *Math::buildAsin(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + // asin(x) * 180.0 / pi + 0.0 + // +0.0 to avoid -0.0 + llvm::Constant *zero = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); + llvm::Constant *pi = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(std::acos(-1.0))); + llvm::Constant *piDeg = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(180.0)); + llvm::Function *asinFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::asin, m_builder.getDoubleTy()); + llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + ins->functionReturnReg->value = m_builder.CreateFAdd(m_builder.CreateFDiv(m_builder.CreateFMul(m_builder.CreateCall(asinFunc, num), piDeg), pi), zero); + + return ins->next; +} + +LLVMInstruction *Math::buildAcos(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + // acos(x) * 180.0 / pi + llvm::Constant *pi = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(std::acos(-1.0))); + llvm::Constant *piDeg = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(180.0)); + llvm::Function *acosFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::acos, m_builder.getDoubleTy()); + llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + ins->functionReturnReg->value = m_builder.CreateFDiv(m_builder.CreateFMul(m_builder.CreateCall(acosFunc, num), piDeg), pi); + + return ins->next; +} + +LLVMInstruction *Math::buildAtan(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + // atan(x) * 180.0 / pi + llvm::Constant *pi = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(std::acos(-1.0))); + llvm::Constant *piDeg = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(180.0)); + llvm::Function *atanFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::atan, m_builder.getDoubleTy()); + llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + ins->functionReturnReg->value = m_builder.CreateFDiv(m_builder.CreateFMul(m_builder.CreateCall(atanFunc, num), piDeg), pi); + + return ins->next; +} + +LLVMInstruction *Math::buildLn(LLVMInstruction *ins) +{ + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + // log(x) + llvm::Function *logFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::log, m_builder.getDoubleTy()); + llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + ins->functionReturnReg->value = m_builder.CreateCall(logFunc, num); + + return ins->next; +} + +LLVMInstruction *Math::buildLog10(LLVMInstruction *ins) +{ + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + // log10(x) + llvm::Function *log10Func = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::log10, m_builder.getDoubleTy()); + llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + ins->functionReturnReg->value = m_builder.CreateCall(log10Func, num); + + return ins->next; +} + +LLVMInstruction *Math::buildExp(LLVMInstruction *ins) +{ + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + // exp(x) + llvm::Function *expFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::exp, m_builder.getDoubleTy()); + llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + ins->functionReturnReg->value = m_builder.CreateCall(expFunc, num); + + return ins->next; +} + +LLVMInstruction *Math::buildExp10(LLVMInstruction *ins) +{ + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + // exp10(x) + llvm::Function *expFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::exp10, m_builder.getDoubleTy()); + llvm::Value *num = m_utils.removeNaN(m_utils.castValue(arg.second, arg.first)); + ins->functionReturnReg->value = m_builder.CreateCall(expFunc, num); + + return ins->next; +} diff --git a/src/engine/internal/llvm/instructions/math.h b/src/engine/internal/llvm/instructions/math.h new file mode 100644 index 000000000..c079e3b1d --- /dev/null +++ b/src/engine/internal/llvm/instructions/math.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "instructiongroup.h" + +namespace libscratchcpp::llvmins +{ + +class Math : public InstructionGroup +{ + public: + using InstructionGroup::InstructionGroup; + + ProcessResult process(LLVMInstruction *ins) override; + + private: + LLVMInstruction *buildAdd(LLVMInstruction *ins); + LLVMInstruction *buildSub(LLVMInstruction *ins); + LLVMInstruction *buildMul(LLVMInstruction *ins); + LLVMInstruction *buildDiv(LLVMInstruction *ins); + LLVMInstruction *buildRandom(LLVMInstruction *ins); + LLVMInstruction *buildRandomInt(LLVMInstruction *ins); + LLVMInstruction *buildMod(LLVMInstruction *ins); + LLVMInstruction *buildRound(LLVMInstruction *ins); + LLVMInstruction *buildAbs(LLVMInstruction *ins); + LLVMInstruction *buildFloor(LLVMInstruction *ins); + LLVMInstruction *buildCeil(LLVMInstruction *ins); + LLVMInstruction *buildSqrt(LLVMInstruction *ins); + LLVMInstruction *buildSin(LLVMInstruction *ins); + LLVMInstruction *buildCos(LLVMInstruction *ins); + LLVMInstruction *buildTan(LLVMInstruction *ins); + LLVMInstruction *buildAsin(LLVMInstruction *ins); + LLVMInstruction *buildAcos(LLVMInstruction *ins); + LLVMInstruction *buildAtan(LLVMInstruction *ins); + LLVMInstruction *buildLn(LLVMInstruction *ins); + LLVMInstruction *buildLog10(LLVMInstruction *ins); + LLVMInstruction *buildExp(LLVMInstruction *ins); + LLVMInstruction *buildExp10(LLVMInstruction *ins); +}; + +} // namespace libscratchcpp::llvmins diff --git a/src/engine/internal/llvm/instructions/procedures.cpp b/src/engine/internal/llvm/instructions/procedures.cpp new file mode 100644 index 000000000..a6de43508 --- /dev/null +++ b/src/engine/internal/llvm/instructions/procedures.cpp @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "procedures.h" +#include "../llvminstruction.h" +#include "../llvmbuildutils.h" +#include "../llvmcompilercontext.h" + +using namespace libscratchcpp; +using namespace libscratchcpp::llvmins; + +ProcessResult Procedures::process(LLVMInstruction *ins) +{ + ProcessResult ret(true, ins); + + switch (ins->type) { + case LLVMInstruction::Type::CallProcedure: + ret.next = buildCallProcedure(ins); + break; + + case LLVMInstruction::Type::ProcedureArg: + ret.next = buildProcedureArg(ins); + break; + + default: + ret.match = false; + break; + } + + return ret; +} + +LLVMInstruction *Procedures::buildCallProcedure(LLVMInstruction *ins) +{ + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); + llvm::Function *function = m_utils.function(); + + assert(ins->procedurePrototype); + assert(ins->args.size() == ins->procedurePrototype->argumentTypes().size()); + m_utils.freeScopeHeap(); + m_utils.syncVariables(m_utils.targetVariables()); + + std::string name = m_utils.scriptFunctionName(ins->procedurePrototype); + llvm::FunctionType *type = m_utils.scriptFunctionType(ins->procedurePrototype); + std::vector args; + + llvm::FunctionType *funcType = m_utils.scriptFunctionType(nullptr); + int passArgCount = funcType->getNumParams(); + + for (size_t i = 0; i < passArgCount; i++) + args.push_back(function->getArg(i)); + + // Add warp arg + if (m_utils.warp()) + args.push_back(m_builder.getInt1(true)); + else + args.push_back(m_utils.procedurePrototype() ? m_utils.warpArg() : m_builder.getInt1(false)); + + // Add procedure args + for (const auto &arg : ins->args) { + if (arg.first == Compiler::StaticType::Unknown) + args.push_back(m_utils.createValue(arg.second)); + else + args.push_back(m_utils.castValue(arg.second, arg.first)); + } + + llvm::Value *handle = m_builder.CreateCall(m_utils.functions().resolveFunction(name, type), args); + + if (!m_utils.warp() && !ins->procedurePrototype->warp()) { + llvm::BasicBlock *suspendBranch = llvm::BasicBlock::Create(llvmCtx, "", function); + llvm::BasicBlock *nextBranch = llvm::BasicBlock::Create(llvmCtx, "", function); + m_builder.CreateCondBr(m_builder.CreateIsNull(handle), nextBranch, suspendBranch); + + m_builder.SetInsertPoint(suspendBranch); + m_utils.createSuspend(); + llvm::Value *done = m_builder.CreateCall(m_utils.compilerCtx()->coroutineResumeFunction(), { handle }); + m_builder.CreateCondBr(done, nextBranch, suspendBranch); + + m_builder.SetInsertPoint(nextBranch); + } + + m_utils.reloadVariables(m_utils.targetVariables()); + return ins->next; +} + +LLVMInstruction *Procedures::buildProcedureArg(LLVMInstruction *ins) +{ + assert(m_utils.procedurePrototype()); + llvm::FunctionType *funcType = m_utils.scriptFunctionType(nullptr); + int passArgCount = funcType->getNumParams(); + llvm::Value *arg = m_utils.function()->getArg(passArgCount + 1 + ins->procedureArgIndex); // omit warp arg + ins->functionReturnReg->value = arg; + + return ins->next; +} diff --git a/src/engine/internal/llvm/instructions/procedures.h b/src/engine/internal/llvm/instructions/procedures.h new file mode 100644 index 000000000..281277ac8 --- /dev/null +++ b/src/engine/internal/llvm/instructions/procedures.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "instructiongroup.h" + +namespace libscratchcpp::llvmins +{ + +class Procedures : public InstructionGroup +{ + public: + using InstructionGroup::InstructionGroup; + + ProcessResult process(LLVMInstruction *ins) override; + + private: + LLVMInstruction *buildCallProcedure(LLVMInstruction *ins); + LLVMInstruction *buildProcedureArg(LLVMInstruction *ins); +}; + +} // namespace libscratchcpp::llvmins diff --git a/src/engine/internal/llvm/instructions/processresult.h b/src/engine/internal/llvm/instructions/processresult.h new file mode 100644 index 000000000..0846c2bea --- /dev/null +++ b/src/engine/internal/llvm/instructions/processresult.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +namespace libscratchcpp +{ + +class LLVMInstruction; + +namespace llvmins +{ + +// Result of group processing +struct ProcessResult +{ + ProcessResult(bool match, LLVMInstruction *next) : + match(match), + next(next) + { + } + + bool match = false; // whether the desired instruction was found + LLVMInstruction *next = nullptr; // next instruction (state) +}; + +} // namespace llvmins +} // namespace libscratchcpp diff --git a/src/engine/internal/llvm/instructions/string.cpp b/src/engine/internal/llvm/instructions/string.cpp new file mode 100644 index 000000000..c973196bb --- /dev/null +++ b/src/engine/internal/llvm/instructions/string.cpp @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "string.h" +#include "../llvminstruction.h" +#include "../llvmbuildutils.h" +#include "../llvmcompilercontext.h" + +using namespace libscratchcpp; +using namespace libscratchcpp::llvmins; + +ProcessResult String::process(LLVMInstruction *ins) +{ + ProcessResult ret(true, ins); + + switch (ins->type) { + case LLVMInstruction::Type::StringConcat: + ret.next = buildStringConcat(ins); + break; + + case LLVMInstruction::Type::StringChar: + ret.next = buildStringChar(ins); + break; + + case LLVMInstruction::Type::StringLength: + ret.next = buildStringLength(ins); + break; + + default: + ret.match = false; + break; + } + + return ret; +} + +LLVMInstruction *String::buildStringConcat(LLVMInstruction *ins) +{ + assert(ins->args.size() == 2); + const auto &arg1 = ins->args[0]; + const auto &arg2 = ins->args[1]; + llvm::Value *str1 = m_utils.castValue(arg1.second, arg1.first); + llvm::Value *str2 = m_utils.castValue(arg2.second, arg2.first); + llvm::PointerType *charPointerType = m_builder.getInt16Ty()->getPointerTo(); + llvm::StructType *stringPtrType = m_utils.compilerCtx()->stringPtrType(); + llvm::Function *memcpyFunc = llvm::Intrinsic::getDeclaration(m_utils.module(), llvm::Intrinsic::memcpy_inline, { charPointerType, charPointerType, m_builder.getInt64Ty() }); + + // StringPtr *result = string_pool_new(true) + llvm::Value *result = m_builder.CreateCall(m_utils.functions().resolve_string_pool_new(), m_builder.getInt1(true)); + m_utils.freeStringLater(result); + + // result->size = string1->size + string2->size + llvm::Value *sizeField1 = m_builder.CreateStructGEP(stringPtrType, str1, 1); + llvm::Value *sizeField2 = m_builder.CreateStructGEP(stringPtrType, str2, 1); + llvm::Value *size1 = m_builder.CreateLoad(m_builder.getInt64Ty(), sizeField1); + llvm::Value *size2 = m_builder.CreateLoad(m_builder.getInt64Ty(), sizeField2); + llvm::Value *resultSize = m_builder.CreateAdd(size1, size2); + llvm::Value *resultSizeField = m_builder.CreateStructGEP(stringPtrType, result, 1); + m_builder.CreateStore(resultSize, resultSizeField); + + // string_alloc(result, result->size) + m_builder.CreateCall(m_utils.functions().resolve_string_alloc(), { result, resultSize }); + + // memcpy(result->data, string1->data, string1->size * sizeof(char16_t)) + llvm::Value *dataField1 = m_builder.CreateStructGEP(stringPtrType, str1, 0); + llvm::Value *data1 = m_builder.CreateLoad(charPointerType, dataField1); + llvm::Value *resultDataField = m_builder.CreateStructGEP(stringPtrType, result, 0); + llvm::Value *writePtr = m_builder.CreateLoad(charPointerType, resultDataField); + m_builder.CreateCall(memcpyFunc, { writePtr, data1, m_builder.CreateMul(size1, m_builder.getInt64(2)), m_builder.getInt1(false) }); + + // memcpy(result->data + string1->size, string2->data, (string2->size + 1) * sizeof(char16_t)) + // +1: null-terminate + llvm::Value *dataField2 = m_builder.CreateStructGEP(stringPtrType, str2, 0); + llvm::Value *data2 = m_builder.CreateLoad(charPointerType, dataField2); + writePtr = m_builder.CreateGEP(m_builder.getInt16Ty(), writePtr, size1); + m_builder.CreateCall(memcpyFunc, { writePtr, data2, m_builder.CreateMul(m_builder.CreateAdd(size2, m_builder.getInt64(1)), m_builder.getInt64(2)), m_builder.getInt1(false) }); + + ins->functionReturnReg->value = result; + + return ins->next; +} + +LLVMInstruction *String::buildStringChar(LLVMInstruction *ins) +{ + assert(ins->args.size() == 2); + 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::PointerType *charPointerType = m_builder.getInt16Ty()->getPointerTo(); + llvm::StructType *stringPtrType = m_utils.compilerCtx()->stringPtrType(); + + // Get data ptr and size + llvm::Value *dataField = m_builder.CreateStructGEP(stringPtrType, str, 0); + llvm::Value *data = m_builder.CreateLoad(charPointerType, dataField); + llvm::Value *sizeField = m_builder.CreateStructGEP(stringPtrType, str, 1); + llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), sizeField); + + // Check range, get character ptr + llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateICmpSGE(index, m_builder.getInt64(0)), m_builder.CreateICmpSLT(index, size)); + llvm::Value *charPtr = m_builder.CreateGEP(m_builder.getInt16Ty(), data, index); + + // Allocate string + llvm::Value *result = m_builder.CreateCall(m_utils.functions().resolve_string_pool_new(), m_builder.getInt1(true)); + m_utils.freeStringLater(result); + m_builder.CreateCall(m_utils.functions().resolve_string_alloc(), { result, m_builder.getInt64(1) }); // size 1 to avoid branching + + // Get result data ptr + dataField = m_builder.CreateStructGEP(stringPtrType, result, 0); + data = m_builder.CreateLoad(charPointerType, dataField); + + // Write result + llvm::Value *char1 = m_builder.CreateGEP(m_builder.getInt16Ty(), data, m_builder.getInt64(0)); + llvm::Value *char2 = m_builder.CreateGEP(m_builder.getInt16Ty(), data, m_builder.getInt64(1)); + sizeField = m_builder.CreateStructGEP(stringPtrType, result, 1); + m_builder.CreateStore(m_builder.CreateSelect(inRange, m_builder.CreateLoad(m_builder.getInt16Ty(), charPtr), m_builder.getInt16(0)), char1); + m_builder.CreateStore(m_builder.getInt16(0), char2); + m_builder.CreateStore(m_builder.CreateSelect(inRange, m_builder.getInt64(1), m_builder.getInt64(0)), sizeField); + + ins->functionReturnReg->value = result; + + return ins->next; +} + +LLVMInstruction *String::buildStringLength(LLVMInstruction *ins) +{ + llvm::StructType *stringPtrType = m_utils.compilerCtx()->stringPtrType(); + + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + llvm::Value *str = m_utils.castValue(arg.second, arg.first); + 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()); + + return ins->next; +} diff --git a/src/engine/internal/llvm/instructions/string.h b/src/engine/internal/llvm/instructions/string.h new file mode 100644 index 000000000..3558471d2 --- /dev/null +++ b/src/engine/internal/llvm/instructions/string.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "instructiongroup.h" + +namespace libscratchcpp::llvmins +{ + +class String : public InstructionGroup +{ + public: + using InstructionGroup::InstructionGroup; + + ProcessResult process(LLVMInstruction *ins) override; + + private: + LLVMInstruction *buildStringConcat(LLVMInstruction *ins); + LLVMInstruction *buildStringChar(LLVMInstruction *ins); + LLVMInstruction *buildStringLength(LLVMInstruction *ins); +}; + +} // namespace libscratchcpp::llvmins diff --git a/src/engine/internal/llvm/instructions/variables.cpp b/src/engine/internal/llvm/instructions/variables.cpp new file mode 100644 index 000000000..204fee064 --- /dev/null +++ b/src/engine/internal/llvm/instructions/variables.cpp @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "variables.h" +#include "../llvminstruction.h" +#include "../llvmbuildutils.h" + +using namespace libscratchcpp; +using namespace libscratchcpp::llvmins; + +ProcessResult Variables::process(LLVMInstruction *ins) +{ + ProcessResult ret(true, ins); + + switch (ins->type) { + case LLVMInstruction::Type::CreateLocalVariable: + ret.next = buildCreateLocalVariable(ins); + break; + + case LLVMInstruction::Type::WriteLocalVariable: + ret.next = buildWriteLocalVariable(ins); + break; + + case LLVMInstruction::Type::ReadLocalVariable: + ret.next = buildReadLocalVariable(ins); + break; + + case LLVMInstruction::Type::WriteVariable: + ret.next = buildWriteVariable(ins); + break; + + case LLVMInstruction::Type::ReadVariable: + ret.next = buildReadVariable(ins); + break; + + default: + ret.match = false; + break; + } + + return ret; +} + +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); + return ins->next; +} + +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); + return ins->next; +} + +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); + return ins->next; +} + +LLVMInstruction *Variables::buildWriteVariable(LLVMInstruction *ins) +{ + assert(ins->args.size() == 1); + const auto &arg = ins->args[0]; + Compiler::StaticType argType = m_utils.optimizeRegisterType(arg.second); + LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->workVariable); + varPtr.changed = true; // TODO: Handle loops and if statements + + Compiler::StaticType varType = Compiler::StaticType::Unknown; + + if (m_utils.warp()) + varType = m_utils.typeAnalyzer().variableType(ins->workVariable, ins, Compiler::StaticType::Unknown); + + // 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, argType, varType); + return ins->next; +} + +LLVMInstruction *Variables::buildReadVariable(LLVMInstruction *ins) +{ + assert(ins->args.size() == 0); + LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->workVariable); + Compiler::StaticType type = Compiler::StaticType::Unknown; + + if (m_utils.warp()) + type = m_utils.typeAnalyzer().variableType(ins->workVariable, ins, Compiler::StaticType::Unknown); + + ins->functionReturnReg->value = varPtr.onStack && !(ins->loopCondition && !m_utils.warp()) ? varPtr.stackPtr : varPtr.heapPtr; + ins->functionReturnReg->setType(type); + return ins->next; +} diff --git a/src/engine/internal/llvm/instructions/variables.h b/src/engine/internal/llvm/instructions/variables.h new file mode 100644 index 000000000..77c44193c --- /dev/null +++ b/src/engine/internal/llvm/instructions/variables.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "instructiongroup.h" + +namespace libscratchcpp::llvmins +{ + +class Variables : public InstructionGroup +{ + public: + using InstructionGroup::InstructionGroup; + + ProcessResult process(LLVMInstruction *ins) override; + + private: + LLVMInstruction *buildCreateLocalVariable(LLVMInstruction *ins); + LLVMInstruction *buildWriteLocalVariable(LLVMInstruction *ins); + LLVMInstruction *buildReadLocalVariable(LLVMInstruction *ins); + LLVMInstruction *buildWriteVariable(LLVMInstruction *ins); + LLVMInstruction *buildReadVariable(LLVMInstruction *ins); +}; + +} // namespace libscratchcpp::llvmins diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp new file mode 100644 index 000000000..d1b78ecea --- /dev/null +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -0,0 +1,1221 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include + +#include "llvmbuildutils.h" +#include "llvmfunctions.h" +#include "llvmcompilercontext.h" +#include "llvmregister.h" +#include "llvminstruction.h" + +using namespace libscratchcpp; + +static std::unordered_map TYPE_MAP = { + { ValueType::Number, Compiler::StaticType::Number }, + { ValueType::Bool, Compiler::StaticType::Bool }, + { ValueType::String, Compiler::StaticType::String }, + { ValueType::Pointer, Compiler::StaticType::Pointer } +}; + +LLVMBuildUtils::LLVMBuildUtils(LLVMCompilerContext *ctx, llvm::IRBuilder<> &builder, Compiler::CodeType codeType) : + m_ctx(ctx), + m_llvmCtx(*ctx->llvmCtx()), + m_builder(builder), + m_functions(ctx, &builder), + m_target(ctx->target()), + m_codeType(codeType) +{ + initTypes(); + createVariableMap(); + createListMap(); +} + +void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePrototype, bool warp) +{ + m_function = function; + m_procedurePrototype = procedurePrototype; + m_warp = warp; + + m_executionContextPtr = m_function->getArg(0); + m_targetPtr = m_function->getArg(1); + m_targetVariables = m_function->getArg(2); + m_targetLists = m_function->getArg(3); + m_warpArg = m_procedurePrototype ? m_function->getArg(4) : nullptr; + + if (m_procedurePrototype && m_warp) + m_function->addFnAttr(llvm::Attribute::InlineHint); + + m_stringHeap.clear(); + pushScopeLevel(); + + // Init coroutine + if (!m_warp) + m_coroutine = std::make_unique(m_ctx->module(), &m_builder, m_function); + + // Create variable pointers + for (auto &[var, varPtr] : m_variablePtrs) { + llvm::Value *ptr = getVariablePtr(m_targetVariables, var); + + // Direct access + varPtr.heapPtr = ptr; + + // All variables are currently 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->workVariable == variable && !ins->loopScope; + }); + + if (it == m_variableInstructions.end()) { + createValueCopy(ptr, varPtr.stackPtr); + varPtr.onStack = true; + } else + varPtr.onStack = false; // use heap before the first assignment + */ + } + + // Create list pointers + for (auto &[list, listPtr] : m_listPtrs) { + listPtr.ptr = getListPtr(m_targetLists, list); + + listPtr.dataPtr = m_builder.CreateAlloca(m_valueDataType->getPointerTo()->getPointerTo()); + m_builder.CreateStore(m_builder.CreateCall(m_functions.resolve_list_data_ptr(), listPtr.ptr), listPtr.dataPtr); + + listPtr.sizePtr = m_builder.CreateCall(m_functions.resolve_list_size_ptr(), listPtr.ptr); + listPtr.allocatedSizePtr = m_builder.CreateCall(m_functions.resolve_list_alloc_size_ptr(), listPtr.ptr); + } + + // Create end branch + m_endBranch = llvm::BasicBlock::Create(m_llvmCtx, "end", m_function); +} + +void LLVMBuildUtils::end(LLVMInstruction *lastInstruction, LLVMRegister *lastConstant) +{ + assert(m_stringHeap.size() == 1); + freeScopeHeap(); + + m_builder.CreateBr(m_endBranch); + + m_builder.SetInsertPoint(m_endBranch); + syncVariables(m_targetVariables); + + // End the script function + llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); + + switch (m_codeType) { + case Compiler::CodeType::Script: + if (m_warp) + m_builder.CreateRet(llvm::ConstantPointerNull::get(pointerType)); + else + m_coroutine->end(); + break; + + case Compiler::CodeType::Reporter: { + // Use last instruction return value (or last constant) and create a ValueData instance + assert(lastInstruction || lastConstant); + LLVMRegister *ret = lastInstruction ? lastInstruction->functionReturnReg : lastConstant; + llvm::Value *copy = createNewValue(ret); + m_builder.CreateRet(m_builder.CreateLoad(m_valueDataType, copy)); + break; + } + + case Compiler::CodeType::HatPredicate: + // Use last instruction return value (or last constant) + assert(lastInstruction || lastConstant); + + if (lastInstruction) + m_builder.CreateRet(castValue(lastInstruction->functionReturnReg, Compiler::StaticType::Bool)); + else + m_builder.CreateRet(castValue(lastConstant, Compiler::StaticType::Bool)); + break; + } +} + +LLVMCompilerContext *LLVMBuildUtils::compilerCtx() const +{ + return m_ctx; +} + +llvm::LLVMContext &LLVMBuildUtils::llvmCtx() +{ + return m_llvmCtx; +} + +llvm::Module *LLVMBuildUtils::module() const +{ + return m_ctx->module(); +} + +llvm::IRBuilder<> &LLVMBuildUtils::builder() +{ + return m_builder; +} + +llvm::Function *LLVMBuildUtils::function() const +{ + return m_function; +} + +LLVMFunctions &LLVMBuildUtils::functions() +{ + return m_functions; +} + +LLVMTypeAnalyzer &LLVMBuildUtils::typeAnalyzer() +{ + return m_typeAnalyzer; +} + +std::string LLVMBuildUtils::scriptFunctionName(BlockPrototype *procedurePrototype) +{ + std::string name; + + switch (m_codeType) { + case Compiler::CodeType::Script: + name = "script"; + break; + + case Compiler::CodeType::Reporter: + name = "reporter"; + break; + + case Compiler::CodeType::HatPredicate: + name = "predicate"; + break; + } + + return procedurePrototype ? "proc." + procedurePrototype->procCode() : name; +} + +llvm::FunctionType *LLVMBuildUtils::scriptFunctionType(BlockPrototype *procedurePrototype) +{ + // void *f(ExecutionContext *, Target *, ValueData **, List **, (warp arg), (procedure args...)) + // ValueData f(...) (reporters) + // bool f(...) (hat predicates) + llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); + std::vector argTypes = { pointerType, pointerType, pointerType, pointerType }; + + if (procedurePrototype) { + argTypes.push_back(m_builder.getInt1Ty()); // warp arg (only in procedures) + const auto &types = procedurePrototype->argumentTypes(); + + for (BlockPrototype::ArgType type : types) { + if (type == BlockPrototype::ArgType::Bool) + argTypes.push_back(m_builder.getInt1Ty()); + else + argTypes.push_back(m_valueDataType->getPointerTo()); + } + } + + llvm::Type *retType = nullptr; + + switch (m_codeType) { + case Compiler::CodeType::Script: + retType = pointerType; + break; + + case Compiler::CodeType::Reporter: + retType = m_valueDataType; + break; + + case Compiler::CodeType::HatPredicate: + retType = m_builder.getInt1Ty(); + break; + } + + return llvm::FunctionType::get(retType, argTypes, false); +} + +llvm::BasicBlock *LLVMBuildUtils::endBranch() const +{ + return m_endBranch; +} + +BlockPrototype *LLVMBuildUtils::procedurePrototype() const +{ + return m_procedurePrototype; +} + +bool LLVMBuildUtils::warp() const +{ + return m_warp; +} + +llvm::Value *LLVMBuildUtils::executionContextPtr() +{ + return m_executionContextPtr; +} + +llvm::Value *LLVMBuildUtils::targetPtr() +{ + return m_targetPtr; +} + +llvm::Value *LLVMBuildUtils::targetVariables() +{ + return m_targetVariables; +} + +llvm::Value *LLVMBuildUtils::targetLists() +{ + return m_targetLists; +} + +llvm::Value *LLVMBuildUtils::warpArg() +{ + return m_warpArg; +} + +LLVMCoroutine *LLVMBuildUtils::coroutine() const +{ + return m_coroutine.get(); +} + +void LLVMBuildUtils::createVariablePtr(Variable *variable) +{ + if (m_variablePtrs.find(variable) == m_variablePtrs.cend()) + m_variablePtrs[variable] = LLVMVariablePtr(); +} + +void LLVMBuildUtils::createListPtr(List *list) +{ + if (m_listPtrs.find(list) == m_listPtrs.cend()) + m_listPtrs[list] = LLVMListPtr(); +} + +LLVMVariablePtr &LLVMBuildUtils::variablePtr(Variable *variable) +{ + assert(m_variablePtrs.find(variable) != m_variablePtrs.cend()); + return m_variablePtrs[variable]; +} + +LLVMListPtr &LLVMBuildUtils::listPtr(List *list) +{ + assert(m_listPtrs.find(list) != m_listPtrs.cend()); + return m_listPtrs[list]; +} + +void LLVMBuildUtils::syncVariables(llvm::Value *targetVariables) +{ + // Copy stack variables to the actual variables + for (auto &[var, varPtr] : m_variablePtrs) { + if (varPtr.onStack && varPtr.changed) + 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; + } +} + +void LLVMBuildUtils::pushScopeLevel() +{ + m_stringHeap.push_back({}); +} + +void LLVMBuildUtils::popScopeLevel() +{ + freeScopeHeap(); + m_stringHeap.pop_back(); +} + +void LLVMBuildUtils::freeStringLater(llvm::Value *value) +{ + assert(!m_stringHeap.empty()); + + if (m_stringHeap.empty()) + return; + + m_stringHeap.back().push_back(value); +} + +void LLVMBuildUtils::freeScopeHeap() +{ + if (m_stringHeap.empty()) + return; + + // Free strings in current scope + auto &heap = m_stringHeap.back(); + + for (llvm::Value *ptr : heap) + m_builder.CreateCall(m_functions.resolve_string_pool_free(), { ptr }); + + heap.clear(); +} + +std::vector &LLVMBuildUtils::ifStatements() +{ + return m_ifStatements; +} + +std::vector &LLVMBuildUtils::loops() +{ + return m_loops; +} + +Compiler::StaticType LLVMBuildUtils::optimizeRegisterType(LLVMRegister *reg) +{ + Compiler::StaticType ret = reg->type(); + + // Optimize string constants that represent numbers + if (reg->isConst() && reg->type() == Compiler::StaticType::String && reg->constValue().isValidNumber()) + ret = Compiler::StaticType::Number; + + return ret; +} + +Compiler::StaticType LLVMBuildUtils::mapType(ValueType type) +{ + assert(TYPE_MAP.find(type) != TYPE_MAP.cend()); + return TYPE_MAP[type]; +} + +llvm::Value *LLVMBuildUtils::addAlloca(llvm::Type *type) +{ + // Add an alloca to the entry block because allocas must be there (to avoid stack overflow) + llvm::BasicBlock *block = m_builder.GetInsertBlock(); + m_builder.SetInsertPointPastAllocas(m_function); + llvm::Value *ret = m_builder.CreateAlloca(type); + m_builder.SetInsertPoint(block); + return ret; +} + +llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType targetType) +{ + if (reg->isConst()) { + if (targetType == Compiler::StaticType::Unknown) + return createValue(reg); + else + return castConstValue(reg->constValue(), targetType); + } + + if (reg->isRawValue) + return castRawValue(reg, targetType); + + assert(reg->type() != Compiler::StaticType::Void && targetType != Compiler::StaticType::Void); + + switch (targetType) { + case Compiler::StaticType::Number: + switch (reg->type()) { + case Compiler::StaticType::Number: { + // Read number directly + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + return m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + } + + case Compiler::StaticType::Bool: { + // Read boolean and cast to double + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *boolValue = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + return m_builder.CreateSIToFP(boolValue, m_builder.getDoubleTy()); + } + + case Compiler::StaticType::String: + case Compiler::StaticType::Unknown: { + // Convert to double + return m_builder.CreateCall(m_functions.resolve_value_toDouble(), reg->value); + } + + default: + assert(false); + return nullptr; + } + + case Compiler::StaticType::Bool: + switch (reg->type()) { + case Compiler::StaticType::Number: { + // True if != 0 + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *numberValue = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + return m_builder.CreateFCmpONE(numberValue, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); + } + + case Compiler::StaticType::Bool: { + // Read boolean directly + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + return m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + } + + case Compiler::StaticType::String: + case Compiler::StaticType::Unknown: + // Convert to bool + return m_builder.CreateCall(m_functions.resolve_value_toBool(), reg->value); + + default: + assert(false); + return nullptr; + } + + case Compiler::StaticType::String: + switch (reg->type()) { + case Compiler::StaticType::Number: + case Compiler::StaticType::Bool: + case Compiler::StaticType::Unknown: { + // Cast to string + // TODO: Use value_stringToDouble() and value_stringToBool() + llvm::Value *ptr = m_builder.CreateCall(m_functions.resolve_value_toStringPtr(), reg->value); + freeStringLater(ptr); + return ptr; + } + + case Compiler::StaticType::String: { + // Read string pointer directly + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + return m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + } + + default: + assert(false); + return nullptr; + } + + case Compiler::StaticType::Unknown: + return createValue(reg); + + default: + assert(false); + return nullptr; + } +} + +llvm::Type *LLVMBuildUtils::getType(Compiler::StaticType type) +{ + switch (type) { + case Compiler::StaticType::Void: + return m_builder.getVoidTy(); + + case Compiler::StaticType::Number: + return m_builder.getDoubleTy(); + + case Compiler::StaticType::Bool: + return m_builder.getInt1Ty(); + + case Compiler::StaticType::String: + return m_stringPtrType->getPointerTo(); + + case Compiler::StaticType::Pointer: + return m_builder.getVoidTy()->getPointerTo(); + + case Compiler::StaticType::Unknown: + return m_valueDataType->getPointerTo(); + + default: + assert(false); + return nullptr; + } +} + +llvm::Value *LLVMBuildUtils::isNaN(llvm::Value *num) +{ + return m_builder.CreateFCmpUNO(num, num); +} + +llvm::Value *LLVMBuildUtils::removeNaN(llvm::Value *num) +{ + // Replace NaN with zero + return m_builder.CreateSelect(isNaN(num), llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)), num); +} + +void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType) +{ + llvm::Value *converted = nullptr; + + if (sourceType != Compiler::StaticType::Unknown) + converted = castValue(reg, sourceType); + + auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [sourceType](const std::pair &pair) { return pair.second == sourceType; }); + const ValueType mappedType = it == TYPE_MAP.cend() ? ValueType::Number : it->first; // unknown type can be ignored + + switch (sourceType) { + case Compiler::StaticType::Number: + switch (targetType) { + case Compiler::StaticType::Number: { + // Write number to number directly + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); + m_builder.CreateStore(converted, ptr); + break; + } + + case Compiler::StaticType::Bool: { + // Write number to bool value directly and change type + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 1); + m_builder.CreateStore(converted, ptr); + m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); + break; + } + + default: + m_builder.CreateCall(m_functions.resolve_value_assign_double(), { targetPtr, converted }); + break; + } + + break; + + case Compiler::StaticType::Bool: + switch (targetType) { + case Compiler::StaticType::Number: { + // Write bool to number value directly and change type + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); + m_builder.CreateStore(converted, ptr); + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 1); + m_builder.CreateStore(converted, ptr); + m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); + break; + } + + case Compiler::StaticType::Bool: { + // Write bool to bool directly + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); + m_builder.CreateStore(converted, ptr); + break; + } + + default: + m_builder.CreateCall(m_functions.resolve_value_assign_bool(), { targetPtr, converted }); + break; + } + + break; + + case Compiler::StaticType::String: + m_builder.CreateCall(m_functions.resolve_value_assign_stringPtr(), { targetPtr, converted }); + break; + + case Compiler::StaticType::Unknown: + m_builder.CreateCall(m_functions.resolve_value_assign_copy(), { targetPtr, reg->value }); + break; + + default: + assert(false); + break; + } +} + +void LLVMBuildUtils::createReusedValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType) +{ + // Same as createValueStore(), but ensures that type is updated + createValueStore(reg, targetPtr, sourceType, targetType); + + auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [sourceType](const std::pair &pair) { return pair.second == sourceType; }); + const ValueType mappedType = it == TYPE_MAP.cend() ? ValueType::Number : it->first; // unknown type can be ignored + + if ((sourceType == Compiler::StaticType::Number || sourceType == Compiler::StaticType::Bool) && sourceType == targetType) { + // Update type when writing number to number and bool to bool + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 1); + m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); + } +} + +llvm::Value *LLVMBuildUtils::getListItem(const LLVMListPtr &listPtr, llvm::Value *index) +{ + return m_builder.CreateGEP(m_valueDataType, getListDataPtr(listPtr), index); +} + +llvm::Value *LLVMBuildUtils::getListItemIndex(const LLVMListPtr &listPtr, Compiler::StaticType listType, LLVMRegister *item) +{ + llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); + llvm::BasicBlock *condBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + llvm::BasicBlock *bodyBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + llvm::BasicBlock *cmpIfBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + llvm::BasicBlock *cmpElseBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + llvm::BasicBlock *notFoundBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + + // index = 0 + llvm::Value *index = addAlloca(m_builder.getInt64Ty()); + m_builder.CreateStore(m_builder.getInt64(0), index); + m_builder.CreateBr(condBlock); + + // while (index < size) + m_builder.SetInsertPoint(condBlock); + llvm::Value *cond = m_builder.CreateICmpULT(m_builder.CreateLoad(m_builder.getInt64Ty(), index), size); + m_builder.CreateCondBr(cond, bodyBlock, notFoundBlock); + + // if (list[index] == item) + m_builder.SetInsertPoint(bodyBlock); + LLVMRegister currentItem(listType); + currentItem.isRawValue = false; + currentItem.value = getListItem(listPtr, m_builder.CreateLoad(m_builder.getInt64Ty(), index)); + llvm::Value *cmp = createComparison(¤tItem, item, Comparison::EQ); + m_builder.CreateCondBr(cmp, cmpIfBlock, cmpElseBlock); + + // goto nextBlock + m_builder.SetInsertPoint(cmpIfBlock); + m_builder.CreateBr(nextBlock); + + // else index++ + m_builder.SetInsertPoint(cmpElseBlock); + m_builder.CreateStore(m_builder.CreateAdd(m_builder.CreateLoad(m_builder.getInt64Ty(), index), m_builder.getInt64(1)), index); + m_builder.CreateBr(condBlock); + + // notFoundBlock: + // index = -1 + // goto nextBlock + m_builder.SetInsertPoint(notFoundBlock); + m_builder.CreateStore(llvm::ConstantInt::get(llvm::Type::getInt64Ty(m_llvmCtx), -1, true), index); + m_builder.CreateBr(nextBlock); + + // nextBlock: + m_builder.SetInsertPoint(nextBlock); + + return m_builder.CreateLoad(m_builder.getInt64Ty(), index); +} + +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::Value *ret = addAlloca(m_valueDataType); + + switch (reg->constValue().type()) { + case ValueType::Number: + value = llvm::ConstantExpr::getBitCast(value, m_valueDataType->getElementType(0)); + break; + + case ValueType::Bool: + // Assuming union type is int64 + value = m_builder.getInt64(reg->constValue().toBool()); + break; + + case ValueType::String: + case ValueType::Pointer: + value = llvm::ConstantExpr::getPtrToInt(value, m_valueDataType->getElementType(0)); + break; + + default: + assert(false); + break; + } + + llvm::Constant *type = m_builder.getInt32(static_cast(reg->constValue().type())); + llvm::Constant *padding = m_builder.getInt32(0); + llvm::Constant *constValue = llvm::ConstantStruct::get(m_valueDataType, { value, type, padding }); + m_builder.CreateStore(constValue, ret); + + return ret; + } else if (reg->isRawValue) { + llvm::Value *value = castRawValue(reg, reg->type()); + llvm::Value *ret = addAlloca(m_valueDataType); + + // Store value + llvm::Value *valueField = m_builder.CreateStructGEP(m_valueDataType, ret, 0); + m_builder.CreateStore(value, valueField); + + auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [®](const std::pair &pair) { return pair.second == reg->type(); }); + + if (it == TYPE_MAP.end()) { + assert(false); + return nullptr; + } + + // Store type + llvm::Value *typeField = m_builder.CreateStructGEP(m_valueDataType, ret, 1); + ValueType type = it->first; + m_builder.CreateStore(m_builder.getInt32(static_cast(type)), typeField); + + return ret; + } else + return reg->value; +} + +llvm::Value *LLVMBuildUtils::createNewValue(LLVMRegister *reg) +{ + // Same as createValue(), but creates a copy of the contents + // NOTE: It is the caller's responsibility to free the value. + llvm::Value *value = createValue(reg); + llvm::Value *ret = addAlloca(m_valueDataType); + m_builder.CreateCall(m_functions.resolve_value_init(), { ret }); + m_builder.CreateCall(m_functions.resolve_value_assign_copy(), { ret, value }); + return ret; +} + +llvm::Value *LLVMBuildUtils::createComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type) +{ + auto type1 = arg1->type(); + auto type2 = arg2->type(); + + if (arg1->isConst() && arg2->isConst()) { + // If both operands are constant, perform the comparison at compile time + bool result = false; + + switch (type) { + case Comparison::EQ: + result = arg1->constValue() == arg2->constValue(); + break; + + case Comparison::GT: + result = arg1->constValue() > arg2->constValue(); + break; + + case Comparison::LT: + result = arg1->constValue() < arg2->constValue(); + break; + + default: + assert(false); + return nullptr; + } + + 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) { + if ((type1 == Compiler::StaticType::Number && type2 == Compiler::StaticType::String && arg2->isConst() && !arg2->constValue().isValidNumber()) || + (type1 == Compiler::StaticType::String && type2 == Compiler::StaticType::Number && arg1->isConst() && !arg1->constValue().isValidNumber())) + return m_builder.getInt1(false); + } + + if (type1 != type2 || type1 == Compiler::StaticType::Unknown || type2 == Compiler::StaticType::Unknown) { + // 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); + + switch (type) { + case Comparison::EQ: + return m_builder.CreateCall(m_functions.resolve_value_equals(), { value1, value2 }); + + case Comparison::GT: + return m_builder.CreateCall(m_functions.resolve_value_greater(), { value1, value2 }); + + case Comparison::LT: + return m_builder.CreateCall(m_functions.resolve_value_lower(), { value1, value2 }); + + default: + assert(false); + return nullptr; + } + } 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; + } + } + + case Compiler::StaticType::Bool: + // Compare two booleans + 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; + } + + case Compiler::StaticType::String: { + // Compare two strings + llvm::Value *cmpRet = m_builder.CreateCall(m_functions.resolve_string_compare_case_insensitive(), { value1, value2 }); + + switch (type) { + case Comparison::EQ: + return m_builder.CreateICmpEQ(cmpRet, m_builder.getInt32(0)); + + case Comparison::GT: + return m_builder.CreateICmpSGT(cmpRet, m_builder.getInt32(0)); + + case Comparison::LT: + return m_builder.CreateICmpSLT(cmpRet, m_builder.getInt32(0)); + + default: + assert(false); + return nullptr; + } + } + + default: + assert(false); + return nullptr; + } + } + } +} + +llvm::Value *LLVMBuildUtils::createStringComparison(LLVMRegister *arg1, LLVMRegister *arg2, bool caseSensitive) +{ + auto type1 = arg1->type(); + auto type2 = arg2->type(); + + if (arg1->isConst() && arg2->isConst()) { + // If both operands are constant, perform the comparison at compile time + StringPtr *str1 = value_toStringPtr(&arg1->constValue().data()); + StringPtr *str2 = value_toStringPtr(&arg2->constValue().data()); + bool result; + + if (caseSensitive) + result = string_compare_case_sensitive(str1, str2) == 0; + else { + result = string_compare_case_insensitive(str1, str2) == 0; + } + + string_pool_free(str1); + string_pool_free(str2); + return m_builder.getInt1(result); + } else { + // Optimize number and string constant comparison + // TODO: Optimize bool and string constant comparison (in compare() as well) + if ((type1 == Compiler::StaticType::Number && type2 == Compiler::StaticType::String && arg2->isConst() && !arg2->constValue().isValidNumber()) || + (type1 == Compiler::StaticType::String && type2 == Compiler::StaticType::Number && arg1->isConst() && !arg1->constValue().isValidNumber())) + return m_builder.getInt1(false); + + // Explicitly cast to string + llvm::Value *string1 = castValue(arg1, Compiler::StaticType::String); + llvm::Value *string2 = castValue(arg2, Compiler::StaticType::String); + llvm::Value *cmp = m_builder.CreateCall(caseSensitive ? m_functions.resolve_string_compare_case_sensitive() : m_functions.resolve_string_compare_case_insensitive(), { string1, string2 }); + return m_builder.CreateICmpEQ(cmp, m_builder.getInt32(0)); + } +} + +void LLVMBuildUtils::createSuspend() +{ + if (m_coroutine) { + assert(!m_warp); + llvm::BasicBlock *suspendBranch, *nextBranch; + + if (m_warpArg) { + suspendBranch = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + nextBranch = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); + m_builder.CreateCondBr(m_warpArg, nextBranch, suspendBranch); + m_builder.SetInsertPoint(suspendBranch); + } + + syncVariables(m_targetVariables); + m_coroutine->createSuspend(); + reloadVariables(m_targetVariables); + + if (m_warpArg) { + m_builder.CreateBr(nextBranch); + m_builder.SetInsertPoint(nextBranch); + } + } +} + +void LLVMBuildUtils::initTypes() +{ + m_valueDataType = m_ctx->valueDataType(); + m_stringPtrType = m_ctx->stringPtrType(); +} + +void LLVMBuildUtils::createVariableMap() +{ + if (!m_target) + return; + + // Map variable pointers to variable data array indices + const auto &variables = m_target->variables(); + ValueData **variableData = m_target->variableData(); + const size_t len = variables.size(); + m_targetVariableMap.clear(); + m_targetVariableMap.reserve(len); + + size_t i, j; + + for (i = 0; i < len; i++) { + Variable *var = variables[i].get(); + + // Find the data for this variable + for (j = 0; j < len; j++) { + if (variableData[j] == &var->valuePtr()->data()) + break; + } + + if (j < len) + m_targetVariableMap[var] = j; + else + assert(false); + } +} + +void LLVMBuildUtils::createListMap() +{ + if (!m_target) + return; + + // Map list pointers to list array indices + const auto &lists = m_target->lists(); + List **listData = m_target->listData(); + const size_t len = lists.size(); + m_targetListMap.clear(); + m_targetListMap.reserve(len); + + size_t i, j; + + for (i = 0; i < len; i++) { + List *list = lists[i].get(); + + // Find this list + for (j = 0; j < len; j++) { + if (listData[j] == list) + break; + } + + if (j < len) + m_targetListMap[list] = j; + else + assert(false); + } +} + +llvm::Value *LLVMBuildUtils::castRawValue(LLVMRegister *reg, Compiler::StaticType targetType) +{ + if (reg->type() == targetType) + 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()); + + case Compiler::StaticType::String: { + // Convert string to double + return m_builder.CreateCall(m_functions.resolve_value_stringToDouble(), reg->value); + } + + default: + assert(false); + return nullptr; + } + + 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::String: + // Convert string to bool + return m_builder.CreateCall(m_functions.resolve_value_stringToBool(), reg->value); + + default: + assert(false); + return nullptr; + } + + 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); + freeStringLater(ptr); + return ptr; + } + + case Compiler::StaticType::Bool: { + // Convert bool to string + llvm::Value *ptr = m_builder.CreateCall(m_functions.resolve_value_boolToStringPtr(), reg->value); + // NOTE: Dot not deallocate later + return ptr; + } + + default: + assert(false); + return nullptr; + } + + case Compiler::StaticType::Unknown: + return createValue(reg); + + default: + assert(false); + return nullptr; + } +} + +llvm::Constant *LLVMBuildUtils::castConstValue(const Value &value, Compiler::StaticType targetType) +{ + 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())); + } + + case Compiler::StaticType::Bool: + return m_builder.getInt1(value.toBool()); + + case Compiler::StaticType::String: { + std::u16string str = value.toUtf16(); + + // Create a constant array for the string + std::vector elements; + for (char16_t ch : str) + elements.push_back(m_builder.getInt16(ch)); + + elements.push_back(m_builder.getInt16(0)); // null terminator + + llvm::ArrayType *arrayType = llvm::ArrayType::get(m_builder.getInt16Ty(), elements.size()); + llvm::Constant *constArray = llvm::ConstantArray::get(arrayType, elements); + + llvm::Module &module = *m_ctx->module(); + + llvm::Constant *globalStr = new llvm::GlobalVariable(module, arrayType, true, llvm::GlobalValue::PrivateLinkage, constArray, "string"); + llvm::Constant *stringStruct = llvm::ConstantStruct::get(m_stringPtrType, { globalStr, m_builder.getInt64(str.size()), m_builder.getInt64(str.size() + 1) }); + return new llvm::GlobalVariable(module, m_stringPtrType, true, llvm::GlobalValue::PrivateLinkage, stringStruct, "stringPtr"); + } + + case Compiler::StaticType::Pointer: { + llvm::Constant *addr = m_builder.getInt64((uintptr_t)value.toPointer()); + return llvm::ConstantExpr::getIntToPtr(addr, m_builder.getVoidTy()->getPointerTo()); + } + + default: + assert(false); + return nullptr; + } +} + +void LLVMBuildUtils::createValueCopy(llvm::Value *source, llvm::Value *target) +{ + // NOTE: This doesn't copy strings, but only the pointers + copyStructField(source, target, 0, m_valueDataType, m_builder.getInt64Ty()); // value + copyStructField(source, target, 1, m_valueDataType, m_builder.getInt32Ty()); // type + /* 2: padding */ +} + +void LLVMBuildUtils::copyStructField(llvm::Value *source, llvm::Value *target, int index, llvm::StructType *structType, llvm::Type *fieldType) +{ + llvm::Value *sourceField = m_builder.CreateStructGEP(structType, source, index); + llvm::Value *targetField = m_builder.CreateStructGEP(structType, target, index); + m_builder.CreateStore(m_builder.CreateLoad(fieldType, sourceField), targetField); +} + +llvm::Value *LLVMBuildUtils::getVariablePtr(llvm::Value *targetVariables, Variable *variable) +{ + if (!m_target->isStage() && variable->target() == m_target) { + // If this is a local sprite variable, use the variable array at runtime (for clones) + assert(m_targetVariableMap.find(variable) != m_targetVariableMap.cend()); + const size_t index = m_targetVariableMap[variable]; + llvm::Value *ptr = m_builder.CreateGEP(m_valueDataType->getPointerTo(), targetVariables, m_builder.getInt64(index)); + return m_builder.CreateLoad(m_valueDataType->getPointerTo(), ptr); + } + + // Otherwise create a raw pointer at compile time + llvm::Value *addr = m_builder.getInt64((uintptr_t)&variable->value().data()); + return m_builder.CreateIntToPtr(addr, m_valueDataType->getPointerTo()); +} + +llvm::Value *LLVMBuildUtils::getListPtr(llvm::Value *targetLists, List *list) +{ + if (!m_target->isStage() && list->target() == m_target) { + // If this is a local sprite list, use the list array at runtime (for clones) + assert(m_targetListMap.find(list) != m_targetListMap.cend()); + const size_t index = m_targetListMap[list]; + auto pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); + llvm::Value *ptr = m_builder.CreateGEP(pointerType, targetLists, m_builder.getInt64(index)); + return m_builder.CreateLoad(pointerType, ptr); + } + + // Otherwise create a raw pointer at compile time + llvm::Value *addr = m_builder.getInt64((uintptr_t)list); + return m_builder.CreateIntToPtr(addr, m_valueDataType->getPointerTo()); +} + +llvm::Value *LLVMBuildUtils::getListDataPtr(const LLVMListPtr &listPtr) +{ + return m_builder.CreateLoad(m_valueDataType->getPointerTo(), m_builder.CreateLoad(m_valueDataType->getPointerTo()->getPointerTo(), listPtr.dataPtr)); +} diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h new file mode 100644 index 000000000..9ca28a1ef --- /dev/null +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include "llvmfunctions.h" +#include "llvmvariableptr.h" +#include "llvmlistptr.h" +#include "llvmtypeanalyzer.h" +#include "llvmcoroutine.h" +#include "llvmifstatement.h" +#include "llvmloop.h" + +namespace libscratchcpp +{ + +class LLVMRegister; +class LLVMCoroutine; + +class LLVMBuildUtils +{ + public: + enum class Comparison + { + EQ, + GT, + LT + }; + + LLVMBuildUtils(LLVMCompilerContext *ctx, llvm::IRBuilder<> &builder, Compiler::CodeType codeType); + + void init(llvm::Function *function, BlockPrototype *procedurePrototype, bool warp); + void end(LLVMInstruction *lastInstruction, LLVMRegister *lastConstant); + + LLVMCompilerContext *compilerCtx() const; + llvm::LLVMContext &llvmCtx(); + llvm::Module *module() const; + llvm::IRBuilder<> &builder(); + llvm::Function *function() const; + LLVMFunctions &functions(); + LLVMTypeAnalyzer &typeAnalyzer(); + + std::string scriptFunctionName(BlockPrototype *procedurePrototype); + llvm::FunctionType *scriptFunctionType(BlockPrototype *procedurePrototype); + + llvm::BasicBlock *endBranch() const; + + BlockPrototype *procedurePrototype() const; + bool warp() const; + + llvm::Value *executionContextPtr(); + llvm::Value *targetPtr(); + llvm::Value *targetVariables(); + llvm::Value *targetLists(); + llvm::Value *warpArg(); + + LLVMCoroutine *coroutine() const; + + void createVariablePtr(Variable *variable); + void createListPtr(List *list); + + LLVMVariablePtr &variablePtr(Variable *variable); + LLVMListPtr &listPtr(List *list); + + void syncVariables(llvm::Value *targetVariables); + void reloadVariables(llvm::Value *targetVariables); + + void pushScopeLevel(); + void popScopeLevel(); + + void freeStringLater(llvm::Value *value); + void freeScopeHeap(); + + std::vector &ifStatements(); + std::vector &loops(); + + static Compiler::StaticType optimizeRegisterType(LLVMRegister *reg); + static Compiler::StaticType mapType(ValueType type); + + llvm::Value *addAlloca(llvm::Type *type); + llvm::Value *castValue(LLVMRegister *reg, Compiler::StaticType targetType); + llvm::Type *getType(Compiler::StaticType type); + llvm::Value *isNaN(llvm::Value *num); + llvm::Value *removeNaN(llvm::Value *num); + + void createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType); + void createReusedValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType); + + llvm::Value *getListItem(const LLVMListPtr &listPtr, llvm::Value *index); + llvm::Value *getListItemIndex(const LLVMListPtr &listPtr, Compiler::StaticType listType, LLVMRegister *item); + llvm::Value *createValue(LLVMRegister *reg); + llvm::Value *createNewValue(LLVMRegister *reg); + llvm::Value *createComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type); + llvm::Value *createStringComparison(LLVMRegister *arg1, LLVMRegister *arg2, bool caseSensitive); + + void createSuspend(); + + private: + void initTypes(); + void createVariableMap(); + void createListMap(); + + llvm::Value *castRawValue(LLVMRegister *reg, Compiler::StaticType targetType); + llvm::Constant *castConstValue(const Value &value, Compiler::StaticType targetType); + + void createValueCopy(llvm::Value *source, llvm::Value *target); + void copyStructField(llvm::Value *source, llvm::Value *target, int index, llvm::StructType *structType, llvm::Type *fieldType); + + llvm::Value *getVariablePtr(llvm::Value *targetVariables, Variable *variable); + llvm::Value *getListPtr(llvm::Value *targetLists, List *list); + llvm::Value *getListDataPtr(const LLVMListPtr &listPtr); + + LLVMCompilerContext *m_ctx = nullptr; + llvm::LLVMContext &m_llvmCtx; + llvm::IRBuilder<> &m_builder; + LLVMFunctions m_functions; + LLVMTypeAnalyzer m_typeAnalyzer; + Target *m_target = nullptr; + llvm::Function *m_function = nullptr; + + llvm::BasicBlock *m_endBranch = nullptr; + + llvm::StructType *m_valueDataType = nullptr; + llvm::StructType *m_stringPtrType = nullptr; + + BlockPrototype *m_procedurePrototype = nullptr; + bool m_warp = false; + Compiler::CodeType m_codeType = Compiler::CodeType::Script; + + llvm::Value *m_executionContextPtr = nullptr; + llvm::Value *m_targetPtr = nullptr; + llvm::Value *m_targetVariables = nullptr; + llvm::Value *m_targetLists = nullptr; + llvm::Value *m_warpArg = nullptr; + + std::unique_ptr m_coroutine; + + std::unordered_map m_targetVariableMap; + std::unordered_map m_variablePtrs; + + std::unordered_map m_targetListMap; + std::unordered_map m_listPtrs; + + std::vector> m_stringHeap; // scopes + + std::vector m_ifStatements; + std::vector m_loops; +}; + +} // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index e0e44e72b..e5cc10a10 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -15,18 +15,9 @@ #include "llvmconstantregister.h" #include "llvmifstatement.h" #include "llvmloop.h" -#include "llvmtypes.h" -#include "llvmloopscope.h" using namespace libscratchcpp; -static std::unordered_map TYPE_MAP = { - { ValueType::Number, Compiler::StaticType::Number }, - { ValueType::Bool, Compiler::StaticType::Bool }, - { ValueType::String, Compiler::StaticType::String }, - { ValueType::Pointer, Compiler::StaticType::Pointer } -}; - static const std::unordered_set VAR_LIST_READ_INSTRUCTIONS = { LLVMInstruction::Type::ReadVariable, LLVMInstruction::Type::GetListItem, LLVMInstruction::Type::GetListItemIndex, LLVMInstruction::Type::ListContainsItem }; @@ -36,29 +27,25 @@ LLVMCodeBuilder::LLVMCodeBuilder(LLVMCompilerContext *ctx, BlockPrototype *proce m_llvmCtx(*ctx->llvmCtx()), m_module(ctx->module()), m_builder(m_llvmCtx), + m_utils(ctx, m_builder, codeType), m_procedurePrototype(procedurePrototype), m_defaultWarp(procedurePrototype ? procedurePrototype->warp() : false), m_warp(m_defaultWarp), - m_codeType(codeType) + m_codeType(codeType), + m_instructionBuilder(m_utils) { initTypes(); - createVariableMap(); - createListMap(); - - llvm::FunctionType *funcType = getMainFunctionType(nullptr); - m_defaultArgCount = funcType->getNumParams(); } -std::shared_ptr LLVMCodeBuilder::finalize() +std::shared_ptr LLVMCodeBuilder::build() { if (!m_warp) { // Do not create coroutine if there are no yield instructions nor non-warp procedure calls - auto it = std::find_if(m_instructions.begin(), m_instructions.end(), [](const std::shared_ptr &step) { - return step->type == LLVMInstruction::Type::Yield || (step->type == LLVMInstruction::Type::CallProcedure && step->procedurePrototype && !step->procedurePrototype->warp()); - }); - - if (it == m_instructions.end()) + if (!m_instructions.containsInstruction([](const LLVMInstruction *step) { + return step->type == LLVMInstruction::Type::Yield || (step->type == LLVMInstruction::Type::CallProcedure && step->procedurePrototype && !step->procedurePrototype->warp()); + })) { m_warp = true; + } // Only create coroutines in scripts if (m_codeType != Compiler::CodeType::Script) @@ -74,1351 +61,70 @@ std::shared_ptr LLVMCodeBuilder::finalize() m_builder.setFastMathFlags(fmf); // Create function - std::string funcName = getMainFunctionName(m_procedurePrototype); - llvm::FunctionType *funcType = getMainFunctionType(m_procedurePrototype); + std::string funcName = m_utils.scriptFunctionName(m_procedurePrototype); + llvm::FunctionType *funcType = m_utils.scriptFunctionType(m_procedurePrototype); if (m_procedurePrototype) m_function = getOrCreateFunction(funcName, funcType); else m_function = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, funcName, m_module); - llvm::Value *executionContextPtr = m_function->getArg(0); - llvm::Value *targetPtr = m_function->getArg(1); - llvm::Value *targetVariables = m_function->getArg(2); - llvm::Value *targetLists = m_function->getArg(3); - llvm::Value *warpArg = nullptr; - - if (m_procedurePrototype) - warpArg = m_function->getArg(4); - - if (m_procedurePrototype && m_warp) - m_function->addFnAttr(llvm::Attribute::InlineHint); - llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_llvmCtx, "entry", m_function); - llvm::BasicBlock *endBranch = llvm::BasicBlock::Create(m_llvmCtx, "end", m_function); m_builder.SetInsertPoint(entry); - // Init coroutine - std::unique_ptr coro; + m_utils.init(m_function, m_procedurePrototype, m_warp); - if (!m_warp) - coro = std::make_unique(m_module, &m_builder, m_function); - - std::vector ifStatements; - std::vector loops; - m_stringHeap.clear(); - - // Create variable pointers - for (auto &[var, varPtr] : m_variablePtrs) { - llvm::Value *ptr = getVariablePtr(targetVariables, var); - - // Direct access - varPtr.heapPtr = ptr; - - // All variables are currently created on the stack and synced later (seems to be faster) - // NOTE: Strings are NOT copied, only the pointer is copied - varPtr.stackPtr = m_builder.CreateAlloca(m_valueDataType); - - // If there are no write operations outside loops, initialize the stack variable now - Variable *variable = var; - auto it = std::find_if(m_variableInstructions.begin(), m_variableInstructions.end(), [variable](const std::shared_ptr &ins) { - return ins->type == LLVMInstruction::Type::WriteVariable && ins->workVariable == variable && !ins->loopScope; - }); - - if (it == m_variableInstructions.end()) { - createValueCopy(ptr, varPtr.stackPtr); - varPtr.onStack = true; - } else - varPtr.onStack = false; // use heap before the first assignment - } + // Build recorded instructions + LLVMInstruction *ins = m_instructions.first(); - // Create list pointers - for (auto &[list, listPtr] : m_listPtrs) { - listPtr.ptr = getListPtr(targetLists, list); - - listPtr.dataPtr = m_builder.CreateAlloca(m_valueDataType->getPointerTo()); - m_builder.CreateStore(m_builder.CreateCall(resolve_list_data(), listPtr.ptr), listPtr.dataPtr); - - listPtr.sizePtr = m_builder.CreateCall(resolve_list_size_ptr(), listPtr.ptr); - listPtr.allocatedSizePtr = m_builder.CreateCall(resolve_list_alloc_size_ptr(), listPtr.ptr); - - listPtr.dataPtrDirty = m_builder.CreateAlloca(m_builder.getInt1Ty()); - m_builder.CreateStore(m_builder.getInt1(false), listPtr.dataPtrDirty); - } - - assert(m_loopScope == -1); - m_loopScope = -1; - m_scopeVariables.clear(); - m_scopeLists.clear(); - pushScopeLevel(); - - // Execute recorded steps - for (const auto insPtr : m_instructions) { - const LLVMInstruction &step = *insPtr; - - switch (step.type) { - case LLVMInstruction::Type::FunctionCall: { - std::vector types; - std::vector args; - - // Variables must be synchronized because the function can read them - syncVariables(targetVariables); - - // Add execution context arg - if (step.functionCtxArg) { - types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0)); - args.push_back(executionContextPtr); - } - - // Add target pointer arg - if (step.functionTargetArg) { - types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0)); - args.push_back(targetPtr); - } - - // Args - for (auto &arg : step.args) { - types.push_back(getType(arg.first)); - args.push_back(castValue(arg.second, arg.first)); - } - - llvm::Type *retType = getType(step.functionReturnReg ? step.functionReturnReg->type() : Compiler::StaticType::Void); - llvm::Value *ret = m_builder.CreateCall(resolveFunction(step.functionName, llvm::FunctionType::get(retType, types, false)), args); - - if (step.functionReturnReg) { - step.functionReturnReg->value = ret; - - if (step.functionReturnReg->type() == Compiler::StaticType::String) - freeStringLater(step.functionReturnReg->value); - } - - break; - } - - case LLVMInstruction::Type::Add: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first)); - llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first)); - step.functionReturnReg->value = m_builder.CreateFAdd(num1, num2); - break; - } - - case LLVMInstruction::Type::Sub: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first)); - llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first)); - step.functionReturnReg->value = m_builder.CreateFSub(num1, num2); - break; - } - - case LLVMInstruction::Type::Mul: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first)); - llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first)); - step.functionReturnReg->value = m_builder.CreateFMul(num1, num2); - break; - } - - case LLVMInstruction::Type::Div: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first)); - llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first)); - step.functionReturnReg->value = m_builder.CreateFDiv(num1, num2); - break; - } - - case LLVMInstruction::Type::Random: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - LLVMRegister *reg1 = arg1.second; - LLVMRegister *reg2 = arg2.second; - - if (reg1->type() == Compiler::StaticType::Bool && reg2->type() == Compiler::StaticType::Bool) { - llvm::Value *bool1 = castValue(arg1.second, Compiler::StaticType::Bool); - llvm::Value *bool2 = castValue(arg2.second, Compiler::StaticType::Bool); - step.functionReturnReg->value = m_builder.CreateCall(resolve_llvm_random_bool(), { executionContextPtr, bool1, bool2 }); - } else { - llvm::Constant *inf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), false); - llvm::Value *num1 = removeNaN(castValue(arg1.second, Compiler::StaticType::Number)); - llvm::Value *num2 = removeNaN(castValue(arg2.second, Compiler::StaticType::Number)); - llvm::Value *sum = m_builder.CreateFAdd(num1, num2); - llvm::Value *sumDiv = m_builder.CreateFDiv(sum, inf); - llvm::Value *isInfOrNaN = isNaN(sumDiv); - - // NOTE: The random function will be called even in edge cases where it isn't needed, but they're rare, so it shouldn't be an issue - if (reg1->type() == Compiler::StaticType::Number && reg2->type() == Compiler::StaticType::Number) - step.functionReturnReg->value = m_builder.CreateSelect(isInfOrNaN, sum, m_builder.CreateCall(resolve_llvm_random_double(), { executionContextPtr, num1, num2 })); - else { - llvm::Value *value1 = createValue(reg1); - llvm::Value *value2 = createValue(reg2); - step.functionReturnReg->value = m_builder.CreateSelect(isInfOrNaN, sum, m_builder.CreateCall(resolve_llvm_random(), { executionContextPtr, value1, value2 })); - } - } - - break; - } - - case LLVMInstruction::Type::RandomInt: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - llvm::Value *from = m_builder.CreateFPToSI(castValue(arg1.second, arg1.first), m_builder.getInt64Ty()); - llvm::Value *to = m_builder.CreateFPToSI(castValue(arg2.second, arg2.first), m_builder.getInt64Ty()); - step.functionReturnReg->value = m_builder.CreateCall(resolve_llvm_random_long(), { executionContextPtr, from, to }); - break; - } - - case LLVMInstruction::Type::CmpEQ: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0].second; - const auto &arg2 = step.args[1].second; - step.functionReturnReg->value = createComparison(arg1, arg2, Comparison::EQ); - break; - } - - case LLVMInstruction::Type::CmpGT: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0].second; - const auto &arg2 = step.args[1].second; - step.functionReturnReg->value = createComparison(arg1, arg2, Comparison::GT); - break; - } - - case LLVMInstruction::Type::CmpLT: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0].second; - const auto &arg2 = step.args[1].second; - step.functionReturnReg->value = createComparison(arg1, arg2, Comparison::LT); - break; - } - - case LLVMInstruction::Type::StrCmpEQCS: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0].second; - const auto &arg2 = step.args[1].second; - step.functionReturnReg->value = createStringComparison(arg1, arg2, true); - break; - } - - case LLVMInstruction::Type::StrCmpEQCI: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0].second; - const auto &arg2 = step.args[1].second; - step.functionReturnReg->value = createStringComparison(arg1, arg2, false); - break; - } - - case LLVMInstruction::Type::And: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - llvm::Value *bool1 = castValue(arg1.second, arg1.first); - llvm::Value *bool2 = castValue(arg2.second, arg2.first); - step.functionReturnReg->value = m_builder.CreateAnd(bool1, bool2); - break; - } - - case LLVMInstruction::Type::Or: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - llvm::Value *bool1 = castValue(arg1.second, arg1.first); - llvm::Value *bool2 = castValue(arg2.second, arg2.first); - step.functionReturnReg->value = m_builder.CreateOr(bool1, bool2); - break; - } - - case LLVMInstruction::Type::Not: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - llvm::Value *value = castValue(arg.second, arg.first); - step.functionReturnReg->value = m_builder.CreateNot(value); - break; - } - - case LLVMInstruction::Type::Mod: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - // rem(a, b) / b < 0.0 ? rem(a, b) + b : rem(a, b) - llvm::Constant *zero = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); - llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first)); - llvm::Value *num2 = removeNaN(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) - step.functionReturnReg->value = m_builder.CreateSelect(cond, m_builder.CreateFAdd(value, num2), value); - break; - } - - case LLVMInstruction::Type::Round: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - // x >= 0.0 ? round(x) : (x >= -0.5 ? -0.0 : floor(x + 0.5)) - llvm::Constant *zero = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); - llvm::Constant *negativeZero = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(-0.0)); - llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::round, m_builder.getDoubleTy()); - llvm::Function *floorFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::floor, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(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(m_llvmCtx, llvm::APFloat(-0.5))); // num >= -0.5 - llvm::Value *negativeRound = m_builder.CreateCall(floorFunc, m_builder.CreateFAdd(num, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.5)))); // floor(x + 0.5) - step.functionReturnReg->value = m_builder.CreateSelect(notNegative, roundNum, m_builder.CreateSelect(negativeCond, negativeZero, negativeRound)); - break; - } - - case LLVMInstruction::Type::Abs: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - llvm::Function *absFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::fabs, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - step.functionReturnReg->value = m_builder.CreateCall(absFunc, num); - break; - } - - case LLVMInstruction::Type::Floor: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - llvm::Function *floorFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::floor, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - step.functionReturnReg->value = m_builder.CreateCall(floorFunc, num); - break; - } - - case LLVMInstruction::Type::Ceil: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - llvm::Function *ceilFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::ceil, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - step.functionReturnReg->value = m_builder.CreateCall(ceilFunc, num); - break; - } - - case LLVMInstruction::Type::Sqrt: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - // sqrt(x) + 0.0 - // This avoids negative zero - llvm::Constant *zero = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); - llvm::Function *sqrtFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::sqrt, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - step.functionReturnReg->value = m_builder.CreateFAdd(m_builder.CreateCall(sqrtFunc, num), zero); - break; - } - - case LLVMInstruction::Type::Sin: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - // round(sin(x * pi / 180.0) * 1e10) / 1e10 + 0.0 - // +0.0 to avoid -0.0 - llvm::Constant *zero = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); - llvm::Constant *pi = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(std::acos(-1.0))); - llvm::Constant *piDeg = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(180.0)); - llvm::Constant *factor = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(1e10)); - llvm::Function *sinFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::sin, m_builder.getDoubleTy()); - llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::round, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - llvm::Value *sinResult = m_builder.CreateCall(sinFunc, m_builder.CreateFDiv(m_builder.CreateFMul(num, pi), piDeg)); // sin(x * pi / 180) - llvm::Value *rounded = m_builder.CreateCall(roundFunc, m_builder.CreateFMul(sinResult, factor)); // round(sin(x * 180) * 1e10) - step.functionReturnReg->value = m_builder.CreateFAdd(m_builder.CreateFDiv(rounded, factor), zero); - break; - } - - case LLVMInstruction::Type::Cos: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - // round(cos(x * pi / 180.0) * 1e10) / 1e10 - llvm::Constant *pi = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(std::acos(-1.0))); - llvm::Constant *piDeg = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(180.0)); - llvm::Constant *factor = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(1e10)); - llvm::Function *cosFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::cos, m_builder.getDoubleTy()); - llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::round, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - llvm::Value *cosResult = m_builder.CreateCall(cosFunc, m_builder.CreateFDiv(m_builder.CreateFMul(num, pi), piDeg)); // cos(x * pi / 180) - llvm::Value *rounded = m_builder.CreateCall(roundFunc, m_builder.CreateFMul(cosResult, factor)); // round(cos(x * 180) * 1e10) - step.functionReturnReg->value = m_builder.CreateFDiv(rounded, factor); - break; - } - - case LLVMInstruction::Type::Tan: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - // ((mod = rem(x, 360.0)) == -270.0 || mod == 90.0) ? inf : ((mod == -90.0 || mod == 270.0) ? -inf : round(tan(x * pi / 180.0) * 1e10) / 1e10 + 0.0) - // +0.0 to avoid -0.0 - llvm::Constant *zero = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); - llvm::Constant *full = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(360.0)); - llvm::Constant *posInf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), false); - llvm::Constant *negInf = llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), true); - llvm::Constant *undefined1 = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(-270.0)); - llvm::Constant *undefined2 = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(90.0)); - llvm::Constant *undefined3 = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(-90.0)); - llvm::Constant *undefined4 = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(270.0)); - llvm::Constant *pi = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(std::acos(-1.0))); - llvm::Constant *piDeg = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(180.0)); - llvm::Constant *factor = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(1e10)); - llvm::Function *tanFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::tan, m_builder.getDoubleTy()); - llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::round, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - llvm::Value *mod = m_builder.CreateFRem(num, full); - llvm::Value *isUndefined1 = m_builder.CreateFCmpOEQ(mod, undefined1); // rem(x, 360.0) == -270.0 - llvm::Value *isUndefined2 = m_builder.CreateFCmpOEQ(mod, undefined2); // rem(x, 360.0) == 90.0 - llvm::Value *isUndefined3 = m_builder.CreateFCmpOEQ(mod, undefined3); // rem(x, 360.0) == -90.0 - llvm::Value *isUndefined4 = m_builder.CreateFCmpOEQ(mod, undefined4); // rem(x, 360.0) == 270.0 - llvm::Value *tanResult = m_builder.CreateCall(tanFunc, m_builder.CreateFDiv(m_builder.CreateFMul(num, pi), piDeg)); // tan(x * pi / 180) - llvm::Value *rounded = m_builder.CreateCall(roundFunc, m_builder.CreateFMul(tanResult, factor)); // round(tan(x * 180) * 1e10) - llvm::Value *result = m_builder.CreateFAdd(m_builder.CreateFDiv(rounded, factor), zero); // round(tan(x * pi / 180.0) * 1e10) / 1e10 + 0.0 - llvm::Value *inner = m_builder.CreateSelect(m_builder.CreateOr(isUndefined3, isUndefined4), negInf, result); - step.functionReturnReg->value = m_builder.CreateSelect(m_builder.CreateOr(isUndefined1, isUndefined2), posInf, inner); - break; - } - - case LLVMInstruction::Type::Asin: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - // asin(x) * 180.0 / pi + 0.0 - // +0.0 to avoid -0.0 - llvm::Constant *zero = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); - llvm::Constant *pi = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(std::acos(-1.0))); - llvm::Constant *piDeg = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(180.0)); - llvm::Function *asinFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::asin, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - step.functionReturnReg->value = m_builder.CreateFAdd(m_builder.CreateFDiv(m_builder.CreateFMul(m_builder.CreateCall(asinFunc, num), piDeg), pi), zero); - break; - } - - case LLVMInstruction::Type::Acos: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - // acos(x) * 180.0 / pi - llvm::Constant *pi = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(std::acos(-1.0))); - llvm::Constant *piDeg = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(180.0)); - llvm::Function *acosFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::acos, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - step.functionReturnReg->value = m_builder.CreateFDiv(m_builder.CreateFMul(m_builder.CreateCall(acosFunc, num), piDeg), pi); - break; - } - - case LLVMInstruction::Type::Atan: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - // atan(x) * 180.0 / pi - llvm::Constant *pi = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(std::acos(-1.0))); - llvm::Constant *piDeg = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(180.0)); - llvm::Function *atanFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::atan, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - step.functionReturnReg->value = m_builder.CreateFDiv(m_builder.CreateFMul(m_builder.CreateCall(atanFunc, num), piDeg), pi); - break; - } - - case LLVMInstruction::Type::Ln: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - // log(x) - llvm::Function *logFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::log, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - step.functionReturnReg->value = m_builder.CreateCall(logFunc, num); - break; - } - - case LLVMInstruction::Type::Log10: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - // log10(x) - llvm::Function *log10Func = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::log10, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - step.functionReturnReg->value = m_builder.CreateCall(log10Func, num); - break; - } - - case LLVMInstruction::Type::Exp: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - // exp(x) - llvm::Function *expFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::exp, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - step.functionReturnReg->value = m_builder.CreateCall(expFunc, num); - break; - } - - case LLVMInstruction::Type::Exp10: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - // exp10(x) - llvm::Function *expFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::exp10, m_builder.getDoubleTy()); - llvm::Value *num = removeNaN(castValue(arg.second, arg.first)); - step.functionReturnReg->value = m_builder.CreateCall(expFunc, num); - break; - } - - case LLVMInstruction::Type::StringConcat: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - llvm::Value *str1 = castValue(arg1.second, arg1.first); - llvm::Value *str2 = castValue(arg2.second, arg2.first); - llvm::PointerType *charPointerType = m_builder.getInt16Ty()->getPointerTo(); - llvm::Function *memcpyFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::memcpy_inline, { charPointerType, charPointerType, m_builder.getInt64Ty() }); - - // StringPtr *result = string_pool_new(true) - llvm::Value *result = m_builder.CreateCall(resolve_string_pool_new(), m_builder.getInt1(true)); - freeStringLater(result); - - // result->size = string1->size + string2->size - llvm::Value *sizeField1 = m_builder.CreateStructGEP(m_stringPtrType, str1, 1); - llvm::Value *sizeField2 = m_builder.CreateStructGEP(m_stringPtrType, str2, 1); - llvm::Value *size1 = m_builder.CreateLoad(m_builder.getInt64Ty(), sizeField1); - llvm::Value *size2 = m_builder.CreateLoad(m_builder.getInt64Ty(), sizeField2); - llvm::Value *resultSize = m_builder.CreateAdd(size1, size2); - llvm::Value *resultSizeField = m_builder.CreateStructGEP(m_stringPtrType, result, 1); - m_builder.CreateStore(resultSize, resultSizeField); - - // string_alloc(result, result->size) - m_builder.CreateCall(resolve_string_alloc(), { result, resultSize }); - - // memcpy(result->data, string1->data, string1->size * sizeof(char16_t)) - llvm::Value *dataField1 = m_builder.CreateStructGEP(m_stringPtrType, str1, 0); - llvm::Value *data1 = m_builder.CreateLoad(charPointerType, dataField1); - llvm::Value *resultDataField = m_builder.CreateStructGEP(m_stringPtrType, result, 0); - llvm::Value *writePtr = m_builder.CreateLoad(charPointerType, resultDataField); - m_builder.CreateCall(memcpyFunc, { writePtr, data1, m_builder.CreateMul(size1, m_builder.getInt64(2)), m_builder.getInt1(false) }); - - // memcpy(result->data + string1->size, string2->data, (string2->size + 1) * sizeof(char16_t)) - // +1: null-terminate - llvm::Value *dataField2 = m_builder.CreateStructGEP(m_stringPtrType, str2, 0); - llvm::Value *data2 = m_builder.CreateLoad(charPointerType, dataField2); - writePtr = m_builder.CreateGEP(m_builder.getInt16Ty(), writePtr, size1); - m_builder.CreateCall(memcpyFunc, { writePtr, data2, m_builder.CreateMul(m_builder.CreateAdd(size2, m_builder.getInt64(1)), m_builder.getInt64(2)), m_builder.getInt1(false) }); - - step.functionReturnReg->value = result; - break; - } - - case LLVMInstruction::Type::StringChar: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - llvm::Value *str = castValue(arg1.second, arg1.first); - llvm::Value *index = m_builder.CreateFPToSI(castValue(arg2.second, arg2.first), m_builder.getInt64Ty()); - llvm::PointerType *charPointerType = m_builder.getInt16Ty()->getPointerTo(); - - // Get data ptr and size - llvm::Value *dataField = m_builder.CreateStructGEP(m_stringPtrType, str, 0); - llvm::Value *data = m_builder.CreateLoad(charPointerType, dataField); - llvm::Value *sizeField = m_builder.CreateStructGEP(m_stringPtrType, str, 1); - llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), sizeField); - - // Check range, get character ptr - llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateICmpSGE(index, m_builder.getInt64(0)), m_builder.CreateICmpSLT(index, size)); - llvm::Value *charPtr = m_builder.CreateGEP(m_builder.getInt16Ty(), data, index); - - // Allocate string - llvm::Value *result = m_builder.CreateCall(resolve_string_pool_new(), m_builder.getInt1(true)); - freeStringLater(result); - m_builder.CreateCall(resolve_string_alloc(), { result, m_builder.getInt64(1) }); // size 1 to avoid branching - - // Get result data ptr - dataField = m_builder.CreateStructGEP(m_stringPtrType, result, 0); - data = m_builder.CreateLoad(charPointerType, dataField); - - // Write result - llvm::Value *char1 = m_builder.CreateGEP(m_builder.getInt16Ty(), data, m_builder.getInt64(0)); - llvm::Value *char2 = m_builder.CreateGEP(m_builder.getInt16Ty(), data, m_builder.getInt64(1)); - sizeField = m_builder.CreateStructGEP(m_stringPtrType, result, 1); - m_builder.CreateStore(m_builder.CreateSelect(inRange, m_builder.CreateLoad(m_builder.getInt16Ty(), charPtr), m_builder.getInt16(0)), char1); - m_builder.CreateStore(m_builder.getInt16(0), char2); - m_builder.CreateStore(m_builder.CreateSelect(inRange, m_builder.getInt64(1), m_builder.getInt64(0)), sizeField); - - step.functionReturnReg->value = result; - break; - } - - case LLVMInstruction::Type::StringLength: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - llvm::Value *str = castValue(arg.second, arg.first); - llvm::Value *sizeField = m_builder.CreateStructGEP(m_stringPtrType, str, 1); - llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), sizeField); - step.functionReturnReg->value = m_builder.CreateSIToFP(size, m_builder.getDoubleTy()); - break; - } - - case LLVMInstruction::Type::Select: { - assert(step.args.size() == 3); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - const auto &arg3 = step.args[2]; - auto type = arg2.first; - llvm::Value *cond = castValue(arg1.second, arg1.first); - llvm::Value *trueValue; - llvm::Value *falseValue; - - if (type == Compiler::StaticType::Unknown) { - trueValue = createValue(arg2.second); - falseValue = createValue(arg3.second); - } else { - trueValue = castValue(arg2.second, type); - falseValue = castValue(arg3.second, type); - } - - step.functionReturnReg->value = m_builder.CreateSelect(cond, trueValue, falseValue); - break; - } - - case LLVMInstruction::Type::CreateLocalVariable: { - assert(step.args.empty()); - llvm::Type *type = nullptr; - - switch (step.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; - } - - step.functionReturnReg->value = addAlloca(type); - break; - } - - case LLVMInstruction::Type::WriteLocalVariable: { - assert(step.args.size() == 2); - const auto &arg1 = step.args[0]; - const auto &arg2 = step.args[1]; - llvm::Value *converted = castValue(arg2.second, arg2.first); - m_builder.CreateStore(converted, arg1.second->value); - break; - } - - case LLVMInstruction::Type::ReadLocalVariable: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - llvm::Type *type = nullptr; - - switch (step.functionReturnReg->type()) { - case Compiler::StaticType::Number: - type = m_builder.getDoubleTy(); - break; - - case Compiler::StaticType::Bool: - type = m_builder.getInt1Ty(); - break; - - default: - assert(false); - break; - } - - step.functionReturnReg->value = m_builder.CreateLoad(type, arg.second->value); - break; - } - - case LLVMInstruction::Type::WriteVariable: { - assert(step.args.size() == 1); - assert(m_variablePtrs.find(step.workVariable) != m_variablePtrs.cend()); - const auto &arg = step.args[0]; - Compiler::StaticType type = optimizeRegisterType(arg.second); - LLVMVariablePtr &varPtr = m_variablePtrs[step.workVariable]; - varPtr.changed = true; - - const bool safe = isVarOrListTypeSafe(insPtr, varPtr.type); - - // Initialize stack variable on first assignment - 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); - } - - if (!safe) - varPtr.type = Compiler::StaticType::Unknown; - - createValueStore(arg.second, varPtr.stackPtr, type, varPtr.type); - varPtr.type = type; - m_scopeVariables.back()[&varPtr] = varPtr.type; - break; - } - - case LLVMInstruction::Type::ReadVariable: { - assert(step.args.size() == 0); - LLVMVariablePtr &varPtr = m_variablePtrs[step.workVariable]; - - if (!isVarOrListTypeSafe(insPtr, varPtr.type)) - varPtr.type = Compiler::StaticType::Unknown; - - step.functionReturnReg->value = varPtr.onStack && !(step.loopCondition && !m_warp) ? varPtr.stackPtr : varPtr.heapPtr; - step.functionReturnReg->setType(varPtr.type); - break; - } - - case LLVMInstruction::Type::ClearList: { - assert(step.args.size() == 0); - LLVMListPtr &listPtr = m_listPtrs[step.workList]; - llvm::Value *oldAllocatedSize = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.allocatedSizePtr); - m_builder.CreateCall(resolve_list_clear(), listPtr.ptr); - - // Clearing may deallocate, so check if the allocated size changed - llvm::Value *dataPtrDirty = m_builder.CreateLoad(m_builder.getInt1Ty(), listPtr.dataPtrDirty); - llvm::Value *allocatedSize = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.allocatedSizePtr); - m_builder.CreateStore(m_builder.CreateOr(dataPtrDirty, m_builder.CreateICmpNE(allocatedSize, oldAllocatedSize)), listPtr.dataPtrDirty); - - m_scopeLists.back().erase(&listPtr); - break; - } - - case LLVMInstruction::Type::RemoveListItem: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - LLVMListPtr &listPtr = m_listPtrs[step.workList]; - - if (!isVarOrListTypeSafe(insPtr, listPtr.type)) - listPtr.type = Compiler::StaticType::Unknown; - - // Range check - llvm::Value *min = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); - llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); - size = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); - llvm::Value *index = castValue(arg.second, arg.first); - llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLT(index, size)); - llvm::BasicBlock *removeBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - m_builder.CreateCondBr(inRange, removeBlock, nextBlock); - - // Remove - m_builder.SetInsertPoint(removeBlock); - index = m_builder.CreateFPToUI(castValue(arg.second, arg.first), m_builder.getInt64Ty()); - m_builder.CreateCall(resolve_list_remove(), { listPtr.ptr, index }); - // NOTE: Removing doesn't deallocate (see List::removeAt()), so there's no need to update the data pointer - m_builder.CreateBr(nextBlock); - - m_builder.SetInsertPoint(nextBlock); - break; - } - - case LLVMInstruction::Type::AppendToList: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - Compiler::StaticType type = optimizeRegisterType(arg.second); - LLVMListPtr &listPtr = m_listPtrs[step.workList]; - - auto &typeMap = m_scopeLists.back(); - - if (typeMap.find(&listPtr) == typeMap.cend()) { - listPtr.type = type; - typeMap[&listPtr] = listPtr.type; - } else if (listPtr.type != type) { - listPtr.type = Compiler::StaticType::Unknown; - typeMap[&listPtr] = listPtr.type; - } - - if (!isVarOrListTypeSafe(insPtr, listPtr.type)) - listPtr.type = Compiler::StaticType::Unknown; - - // Check if enough space is allocated - llvm::Value *allocatedSize = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.allocatedSizePtr); - llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); - llvm::Value *isAllocated = m_builder.CreateICmpUGT(allocatedSize, size); - llvm::BasicBlock *ifBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - llvm::BasicBlock *elseBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - m_builder.CreateCondBr(isAllocated, ifBlock, elseBlock); - - // If there's enough space, use the allocated memory - m_builder.SetInsertPoint(ifBlock); - llvm::Value *itemPtr = getListItem(listPtr, size); - createReusedValueStore(arg.second, itemPtr, type, listPtr.type); - m_builder.CreateStore(m_builder.CreateAdd(size, m_builder.getInt64(1)), listPtr.sizePtr); - m_builder.CreateBr(nextBlock); - - // Otherwise call appendEmpty() - m_builder.SetInsertPoint(elseBlock); - itemPtr = m_builder.CreateCall(resolve_list_append_empty(), listPtr.ptr); - createReusedValueStore(arg.second, itemPtr, type, listPtr.type); - m_builder.CreateStore(m_builder.getInt1(true), listPtr.dataPtrDirty); - m_builder.CreateBr(nextBlock); - - m_builder.SetInsertPoint(nextBlock); - - break; - } - - case LLVMInstruction::Type::InsertToList: { - assert(step.args.size() == 2); - const auto &indexArg = step.args[0]; - const auto &valueArg = step.args[1]; - Compiler::StaticType type = optimizeRegisterType(valueArg.second); - LLVMListPtr &listPtr = m_listPtrs[step.workList]; - - auto &typeMap = m_scopeLists.back(); - - if (typeMap.find(&listPtr) == typeMap.cend()) { - listPtr.type = type; - typeMap[&listPtr] = listPtr.type; - } else if (listPtr.type != type) { - listPtr.type = Compiler::StaticType::Unknown; - typeMap[&listPtr] = listPtr.type; - } - - if (!isVarOrListTypeSafe(insPtr, listPtr.type)) - listPtr.type = Compiler::StaticType::Unknown; - - llvm::Value *oldAllocatedSize = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.allocatedSizePtr); - - // Range check - llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); - llvm::Value *min = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); - size = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); - llvm::Value *index = castValue(indexArg.second, indexArg.first); - llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLE(index, size)); - llvm::BasicBlock *insertBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_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(resolve_list_insert_empty(), { listPtr.ptr, index }); - createReusedValueStore(valueArg.second, itemPtr, type, listPtr.type); - - // Check if the allocated size changed - llvm::Value *dataPtrDirty = m_builder.CreateLoad(m_builder.getInt1Ty(), listPtr.dataPtrDirty); - llvm::Value *allocatedSize = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.allocatedSizePtr); - m_builder.CreateStore(m_builder.CreateOr(dataPtrDirty, m_builder.CreateICmpNE(allocatedSize, oldAllocatedSize)), listPtr.dataPtrDirty); - - m_builder.CreateBr(nextBlock); - - m_builder.SetInsertPoint(nextBlock); - break; - } - - case LLVMInstruction::Type::ListReplace: { - assert(step.args.size() == 2); - const auto &indexArg = step.args[0]; - const auto &valueArg = step.args[1]; - Compiler::StaticType type = optimizeRegisterType(valueArg.second); - LLVMListPtr &listPtr = m_listPtrs[step.workList]; - - if (!isVarOrListTypeSafe(insPtr, listPtr.type)) - listPtr.type = Compiler::StaticType::Unknown; - - // Range check - llvm::Value *min = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); - llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); - size = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); - llvm::Value *index = castValue(indexArg.second, indexArg.first); - llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLT(index, size)); - llvm::BasicBlock *replaceBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - m_builder.CreateCondBr(inRange, replaceBlock, nextBlock); - - // Replace - m_builder.SetInsertPoint(replaceBlock); - index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); - llvm::Value *itemPtr = getListItem(listPtr, index); - createValueStore(valueArg.second, itemPtr, type, listPtr.type); - m_builder.CreateBr(nextBlock); - - auto &typeMap = m_scopeLists.back(); - - if (typeMap.find(&listPtr) == typeMap.cend()) { - listPtr.type = type; - typeMap[&listPtr] = listPtr.type; - } else if (listPtr.type != type) { - listPtr.type = Compiler::StaticType::Unknown; - typeMap[&listPtr] = listPtr.type; - } - - m_builder.SetInsertPoint(nextBlock); - break; - } - - case LLVMInstruction::Type::GetListContents: { - assert(step.args.size() == 0); - const LLVMListPtr &listPtr = m_listPtrs[step.workList]; - llvm::Value *ptr = m_builder.CreateCall(resolve_list_to_string(), listPtr.ptr); - freeStringLater(ptr); - step.functionReturnReg->value = ptr; - break; - } - - case LLVMInstruction::Type::GetListItem: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - LLVMListPtr &listPtr = m_listPtrs[step.workList]; - - if (!isVarOrListTypeSafe(insPtr, listPtr.type)) - listPtr.type = Compiler::StaticType::Unknown; - - llvm::Value *min = llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)); - llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); - size = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); - llvm::Value *index = castValue(arg.second, arg.first); - llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLT(index, size)); - - LLVMConstantRegister nullReg(listPtr.type == Compiler::StaticType::Unknown ? Compiler::StaticType::Number : listPtr.type, Value()); - llvm::Value *null = createValue(static_cast(&nullReg)); - - index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); - step.functionReturnReg->value = m_builder.CreateSelect(inRange, getListItem(listPtr, index), null); - step.functionReturnReg->setType(listPtr.type); - break; - } - - case LLVMInstruction::Type::GetListSize: { - assert(step.args.size() == 0); - const LLVMListPtr &listPtr = m_listPtrs[step.workList]; - llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); - step.functionReturnReg->value = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); - break; - } - - case LLVMInstruction::Type::GetListItemIndex: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - LLVMListPtr &listPtr = m_listPtrs[step.workList]; - - if (!isVarOrListTypeSafe(insPtr, listPtr.type)) - listPtr.type = Compiler::StaticType::Unknown; - - step.functionReturnReg->value = m_builder.CreateSIToFP(getListItemIndex(listPtr, arg.second), m_builder.getDoubleTy()); - break; - } - - case LLVMInstruction::Type::ListContainsItem: { - assert(step.args.size() == 1); - const auto &arg = step.args[0]; - LLVMListPtr &listPtr = m_listPtrs[step.workList]; - - if (!isVarOrListTypeSafe(insPtr, listPtr.type)) - listPtr.type = Compiler::StaticType::Unknown; - - llvm::Value *index = getListItemIndex(listPtr, arg.second); - step.functionReturnReg->value = m_builder.CreateICmpSGT(index, llvm::ConstantInt::get(m_builder.getInt64Ty(), -1, true)); - break; - } - - case LLVMInstruction::Type::Yield: - createSuspend(coro.get(), warpArg, targetVariables); - break; - - case LLVMInstruction::Type::BeginIf: { - LLVMIfStatement statement; - statement.beforeIf = m_builder.GetInsertBlock(); - statement.body = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - - // Use last reg - assert(step.args.size() == 1); - const auto ® = step.args[0]; - assert(reg.first == Compiler::StaticType::Bool); - statement.condition = castValue(reg.second, reg.first); - - // Switch to body branch - m_builder.SetInsertPoint(statement.body); - - ifStatements.push_back(statement); - pushScopeLevel(); - break; - } - - case LLVMInstruction::Type::BeginElse: { - assert(!ifStatements.empty()); - LLVMIfStatement &statement = ifStatements.back(); - - // Restore types from parent scope - std::unordered_map parentScopeVariables = m_scopeVariables[m_scopeVariables.size() - 2]; // no reference! - popScopeLevel(); - - for (auto &[ptr, type] : parentScopeVariables) - ptr->type = type; - - pushScopeLevel(); - - // Jump to the branch after the if statement - assert(!statement.afterIf); - statement.afterIf = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - freeScopeHeap(); - m_builder.CreateBr(statement.afterIf); - - // Create else branch - assert(!statement.elseBranch); - statement.elseBranch = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - - // Since there's an else branch, the conditional instruction should jump to it - m_builder.SetInsertPoint(statement.beforeIf); - m_builder.CreateCondBr(statement.condition, statement.body, statement.elseBranch); - - // Switch to the else branch - m_builder.SetInsertPoint(statement.elseBranch); - break; - } - - case LLVMInstruction::Type::EndIf: { - assert(!ifStatements.empty()); - LLVMIfStatement &statement = ifStatements.back(); - freeScopeHeap(); - - // Jump to the branch after the if statement - if (!statement.afterIf) - statement.afterIf = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - - m_builder.CreateBr(statement.afterIf); - - if (statement.elseBranch) { - } else { - // If there wasn't an 'else' branch, create a conditional instruction which skips the if statement if false - m_builder.SetInsertPoint(statement.beforeIf); - m_builder.CreateCondBr(statement.condition, statement.body, statement.afterIf); - } - - // Switch to the branch after the if statement - m_builder.SetInsertPoint(statement.afterIf); - - ifStatements.pop_back(); - popScopeLevel(); - break; - } - - case LLVMInstruction::Type::BeginRepeatLoop: { - LLVMLoop loop; - loop.isRepeatLoop = true; - - // index = 0 - llvm::Constant *zero = llvm::ConstantInt::get(m_builder.getInt64Ty(), 0, true); - loop.index = addAlloca(m_builder.getInt64Ty()); - m_builder.CreateStore(zero, loop.index); - - // Create branches - llvm::BasicBlock *roundBranch = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - loop.conditionBranch = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - loop.afterLoop = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - - // Use last reg for count - assert(step.args.size() == 1); - const auto ® = step.args[0]; - assert(reg.first == Compiler::StaticType::Number); - llvm::Value *count = castValue(reg.second, reg.first); - llvm::Value *isInf = m_builder.CreateFCmpOEQ(count, llvm::ConstantFP::getInfinity(m_builder.getDoubleTy(), false)); - - // Clamp count if <= 0 (we can skip the loop if count is not positive) - llvm::Value *comparison = m_builder.CreateFCmpULE(count, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); - m_builder.CreateCondBr(comparison, loop.afterLoop, roundBranch); - - // Round (Scratch-specific behavior) - m_builder.SetInsertPoint(roundBranch); - llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::round, { count->getType() }); - count = m_builder.CreateCall(roundFunc, { count }); - count = m_builder.CreateFPToUI(count, m_builder.getInt64Ty()); // cast to unsigned integer - count = m_builder.CreateSelect(isInf, zero, count); - - // Jump to condition branch - m_builder.CreateBr(loop.conditionBranch); - - // Check index - m_builder.SetInsertPoint(loop.conditionBranch); - - llvm::BasicBlock *body = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - - if (!loop.afterLoop) - loop.afterLoop = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - - llvm::Value *currentIndex = m_builder.CreateLoad(m_builder.getInt64Ty(), loop.index); - comparison = m_builder.CreateOr(isInf, m_builder.CreateICmpULT(currentIndex, count)); - m_builder.CreateCondBr(comparison, body, loop.afterLoop); - - // Switch to body branch - m_builder.SetInsertPoint(body); - - loops.push_back(loop); - pushScopeLevel(); - pushLoopScope(true); - break; - } - - case LLVMInstruction::Type::LoopIndex: { - assert(!loops.empty()); - LLVMLoop &loop = loops.back(); - llvm::Value *index = m_builder.CreateLoad(m_builder.getInt64Ty(), loop.index); - step.functionReturnReg->value = m_builder.CreateUIToFP(index, m_builder.getDoubleTy()); - break; - } - - case LLVMInstruction::Type::BeginWhileLoop: { - assert(!loops.empty()); - LLVMLoop &loop = loops.back(); - - // Create branches - llvm::BasicBlock *body = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - loop.afterLoop = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - - // Use last reg - assert(step.args.size() == 1); - const auto ® = step.args[0]; - assert(reg.first == Compiler::StaticType::Bool); - llvm::Value *condition = castValue(reg.second, reg.first); - m_builder.CreateCondBr(condition, body, loop.afterLoop); - - // Switch to body branch - m_builder.SetInsertPoint(body); - pushScopeLevel(); - pushLoopScope(true); - break; - } - - case LLVMInstruction::Type::BeginRepeatUntilLoop: { - assert(!loops.empty()); - LLVMLoop &loop = loops.back(); - - // Create branches - llvm::BasicBlock *body = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - loop.afterLoop = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - - // Use last reg - assert(step.args.size() == 1); - const auto ® = step.args[0]; - assert(reg.first == Compiler::StaticType::Bool); - llvm::Value *condition = castValue(reg.second, reg.first); - m_builder.CreateCondBr(condition, loop.afterLoop, body); - - // Switch to body branch - m_builder.SetInsertPoint(body); - pushScopeLevel(); - pushLoopScope(true); - break; - } - - case LLVMInstruction::Type::BeginLoopCondition: { - LLVMLoop loop; - loop.isRepeatLoop = false; - loop.conditionBranch = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - m_builder.CreateBr(loop.conditionBranch); - m_builder.SetInsertPoint(loop.conditionBranch); - loops.push_back(loop); - break; - } - - case LLVMInstruction::Type::EndLoop: { - assert(!loops.empty()); - LLVMLoop &loop = loops.back(); - - if (loop.isRepeatLoop) { - // Increment index - llvm::Value *currentIndex = m_builder.CreateLoad(m_builder.getInt64Ty(), loop.index); - llvm::Value *incremented = m_builder.CreateAdd(currentIndex, llvm::ConstantInt::get(m_builder.getInt64Ty(), 1, false)); - m_builder.CreateStore(incremented, loop.index); - } - - // Jump to the condition branch - freeScopeHeap(); - m_builder.CreateBr(loop.conditionBranch); - - // Switch to the branch after the loop - m_builder.SetInsertPoint(loop.afterLoop); - - loops.pop_back(); - popScopeLevel(); - popLoopScope(); - break; - } - - case LLVMInstruction::Type::Stop: { - freeScopeHeap(); - m_builder.CreateBr(endBranch); - llvm::BasicBlock *nextBranch = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - m_builder.SetInsertPoint(nextBranch); - break; - } - - case LLVMInstruction::Type::CallProcedure: { - assert(step.procedurePrototype); - assert(step.args.size() == step.procedurePrototype->argumentTypes().size()); - freeScopeHeap(); - syncVariables(targetVariables); - - std::string name = getMainFunctionName(step.procedurePrototype); - llvm::FunctionType *type = getMainFunctionType(step.procedurePrototype); - std::vector args; - - for (size_t i = 0; i < m_defaultArgCount; i++) - args.push_back(m_function->getArg(i)); - - // Add warp arg - if (m_warp) - args.push_back(m_builder.getInt1(true)); - else - args.push_back(m_procedurePrototype ? warpArg : m_builder.getInt1(false)); - - // Add procedure args - for (const auto &arg : step.args) { - if (arg.first == Compiler::StaticType::Unknown) - args.push_back(createValue(arg.second)); - else - args.push_back(castValue(arg.second, arg.first)); - } - - llvm::Value *handle = m_builder.CreateCall(resolveFunction(name, type), args); - - if (!m_warp && !step.procedurePrototype->warp()) { - llvm::BasicBlock *suspendBranch = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - llvm::BasicBlock *nextBranch = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - m_builder.CreateCondBr(m_builder.CreateIsNull(handle), nextBranch, suspendBranch); - - m_builder.SetInsertPoint(suspendBranch); - createSuspend(coro.get(), warpArg, targetVariables); - name = getResumeFunctionName(step.procedurePrototype); - llvm::Value *done = m_builder.CreateCall(resolveFunction(name, m_resumeFuncType), { handle }); - m_builder.CreateCondBr(done, nextBranch, suspendBranch); - - m_builder.SetInsertPoint(nextBranch); - } - - reloadVariables(targetVariables); - reloadLists(); - break; - } - - case LLVMInstruction::Type::ProcedureArg: { - assert(m_procedurePrototype); - llvm::Value *arg = m_function->getArg(m_defaultArgCount + 1 + step.procedureArgIndex); // omit warp arg - step.functionReturnReg->value = arg; - break; - } - } - } - - assert(m_stringHeap.size() == 1); - freeScopeHeap(); - m_builder.CreateBr(endBranch); - - m_builder.SetInsertPoint(endBranch); - syncVariables(targetVariables); - - // End and verify the function - llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - - switch (m_codeType) { - case Compiler::CodeType::Script: - if (m_warp) - m_builder.CreateRet(llvm::ConstantPointerNull::get(pointerType)); - else - coro->end(); - break; - - case Compiler::CodeType::Reporter: { - // Use last instruction return value (or last constant) and create a ValueData instance - assert(!m_instructions.empty() || m_lastConstValue); - LLVMRegister *ret = m_instructions.empty() ? m_lastConstValue : m_instructions.back()->functionReturnReg; - llvm::Value *copy = createNewValue(ret); - m_builder.CreateRet(m_builder.CreateLoad(m_valueDataType, copy)); - break; - } - - case Compiler::CodeType::HatPredicate: - // Use last instruction return value (or last constant) - assert(!m_instructions.empty() || m_lastConstValue); - - if (m_instructions.empty()) - m_builder.CreateRet(castValue(m_lastConstValue, Compiler::StaticType::Bool)); - else - m_builder.CreateRet(castValue(m_instructions.back()->functionReturnReg, Compiler::StaticType::Bool)); - break; - } + while (ins) + ins = m_instructionBuilder.process(ins); + m_utils.end(m_instructions.empty() ? nullptr : m_instructions.last(), m_lastConstValue); verifyFunction(m_function); - // Create resume function - // bool resume(void *) - funcName = getResumeFunctionName(m_procedurePrototype); - llvm::Function *resumeFunc = getOrCreateFunction(funcName, m_resumeFuncType); - resumeFunc->addFnAttr(llvm::Attribute::NoInline); - resumeFunc->addFnAttr(llvm::Attribute::OptimizeNone); - - entry = llvm::BasicBlock::Create(m_llvmCtx, "entry", resumeFunc); - m_builder.SetInsertPoint(entry); - - if (m_warp) - m_builder.CreateRet(m_builder.getInt1(true)); - else - m_builder.CreateRet(coro->createResume(resumeFunc, resumeFunc->getArg(0))); - - verifyFunction(resumeFunc); - - return std::make_shared(m_ctx, m_function->getName().str(), resumeFunc->getName().str(), m_codeType); + return std::make_shared(m_ctx, m_function->getName().str(), m_ctx->coroutineResumeFunction()->getName().str(), m_codeType); } CompilerValue *LLVMCodeBuilder::addFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) { assert(argTypes.size() == args.size()); - auto ins = std::make_shared(LLVMInstruction::Type::FunctionCall, currentLoopScope(), m_loopCondition); + auto ins = std::make_shared(LLVMInstruction::Type::FunctionCall, m_loopCondition); ins->functionName = functionName; for (size_t i = 0; i < args.size(); i++) ins->args.push_back({ argTypes[i], dynamic_cast(args[i]) }); + m_instructions.addInstruction(ins); + if (returnType != Compiler::StaticType::Void) { auto reg = std::make_shared(returnType); reg->isRawValue = true; ins->functionReturnReg = reg.get(); - m_instructions.push_back(ins); return addReg(reg, ins); } - m_instructions.push_back(ins); return nullptr; } CompilerValue *LLVMCodeBuilder::addTargetFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) { CompilerValue *ret = addFunctionCall(functionName, returnType, argTypes, args); - m_instructions.back()->functionTargetArg = true; + m_instructions.last()->functionTargetArg = true; return ret; } CompilerValue *LLVMCodeBuilder::addFunctionCallWithCtx(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) { CompilerValue *ret = addFunctionCall(functionName, returnType, argTypes, args); - m_instructions.back()->functionCtxArg = true; + m_instructions.last()->functionCtxArg = true; return ret; } CompilerConstant *LLVMCodeBuilder::addConstValue(const Value &value) { - auto constReg = std::make_shared(TYPE_MAP[value.type()], value); + auto constReg = std::make_shared(m_utils.mapType(value.type()), value); auto reg = std::static_pointer_cast(constReg); m_lastConstValue = reg.get(); return static_cast(static_cast(addReg(reg, nullptr))); @@ -1446,39 +152,32 @@ CompilerValue *LLVMCodeBuilder::addLocalVariableValue(CompilerLocalVariable *var CompilerValue *LLVMCodeBuilder::addVariableValue(Variable *variable) { - auto ins = std::make_shared(LLVMInstruction::Type::ReadVariable, currentLoopScope(), m_loopCondition); + auto ins = std::make_shared(LLVMInstruction::Type::ReadVariable, m_loopCondition); ins->workVariable = variable; - - if (m_variablePtrs.find(variable) == m_variablePtrs.cend()) - m_variablePtrs[variable] = LLVMVariablePtr(); + m_utils.createVariablePtr(variable); auto ret = std::make_shared(Compiler::StaticType::Unknown); ret->isRawValue = false; ins->functionReturnReg = ret.get(); - m_instructions.push_back(ins); - m_variableInstructions.push_back(m_instructions.back()); + m_instructions.addInstruction(ins); return addReg(ret, ins); } CompilerValue *LLVMCodeBuilder::addListContents(List *list) { - LLVMInstruction ins(LLVMInstruction::Type::GetListContents, currentLoopScope(), m_loopCondition); + LLVMInstruction ins(LLVMInstruction::Type::GetListContents, m_loopCondition); ins.workList = list; - - if (m_listPtrs.find(list) == m_listPtrs.cend()) - m_listPtrs[list] = LLVMListPtr(); + m_utils.createListPtr(list); return createOp(ins, Compiler::StaticType::String); } CompilerValue *LLVMCodeBuilder::addListItem(List *list, CompilerValue *index) { - auto ins = std::make_shared(LLVMInstruction::Type::GetListItem, currentLoopScope(), m_loopCondition); + auto ins = std::make_shared(LLVMInstruction::Type::GetListItem, m_loopCondition); ins->workList = list; - - if (m_listPtrs.find(list) == m_listPtrs.cend()) - m_listPtrs[list] = LLVMListPtr(); + m_utils.createListPtr(list); ins->args.push_back({ Compiler::StaticType::Number, dynamic_cast(index) }); @@ -1486,44 +185,35 @@ CompilerValue *LLVMCodeBuilder::addListItem(List *list, CompilerValue *index) ret->isRawValue = false; ins->functionReturnReg = ret.get(); - m_instructions.push_back(ins); - m_listInstructions.push_back(m_instructions.back()); + m_instructions.addInstruction(ins); return addReg(ret, ins); } CompilerValue *LLVMCodeBuilder::addListItemIndex(List *list, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::GetListItemIndex, currentLoopScope(), m_loopCondition); + LLVMInstruction ins(LLVMInstruction::Type::GetListItemIndex, m_loopCondition); ins.workList = list; - - if (m_listPtrs.find(list) == m_listPtrs.cend()) - m_listPtrs[list] = LLVMListPtr(); + m_utils.createListPtr(list); auto ret = createOp(ins, Compiler::StaticType::Number, Compiler::StaticType::Unknown, { item }); - m_listInstructions.push_back(m_instructions.back()); return ret; } CompilerValue *LLVMCodeBuilder::addListContains(List *list, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::ListContainsItem, currentLoopScope(), m_loopCondition); + LLVMInstruction ins(LLVMInstruction::Type::ListContainsItem, m_loopCondition); ins.workList = list; - - if (m_listPtrs.find(list) == m_listPtrs.cend()) - m_listPtrs[list] = LLVMListPtr(); + m_utils.createListPtr(list); auto ret = createOp(ins, Compiler::StaticType::Bool, Compiler::StaticType::Unknown, { item }); - m_listInstructions.push_back(m_instructions.back()); return ret; } CompilerValue *LLVMCodeBuilder::addListSize(List *list) { - LLVMInstruction ins(LLVMInstruction::Type::GetListSize, currentLoopScope(), m_loopCondition); + LLVMInstruction ins(LLVMInstruction::Type::GetListSize, m_loopCondition); ins.workList = list; - - if (m_listPtrs.find(list) == m_listPtrs.cend()) - m_listPtrs[list] = LLVMListPtr(); + m_utils.createListPtr(list); return createOp(ins, Compiler::StaticType::Number); } @@ -1543,13 +233,13 @@ CompilerValue *LLVMCodeBuilder::addProcedureArgument(const std::string &name) const auto index = it - argNames.begin(); const Compiler::StaticType type = getProcedureArgType(m_procedurePrototype->argumentTypes()[index]); - auto ins = std::make_shared(LLVMInstruction::Type::ProcedureArg, currentLoopScope(), m_loopCondition); + auto ins = std::make_shared(LLVMInstruction::Type::ProcedureArg, m_loopCondition); auto ret = std::make_shared(type); ret->isRawValue = (type != Compiler::StaticType::Unknown); ins->functionReturnReg = ret.get(); ins->procedureArgIndex = index; - m_instructions.push_back(ins); + m_instructions.addInstruction(ins); return addReg(ret, ins); } @@ -1728,117 +418,78 @@ void LLVMCodeBuilder::createLocalVariableWrite(CompilerLocalVariable *variable, void LLVMCodeBuilder::createVariableWrite(Variable *variable, CompilerValue *value) { - LLVMInstruction ins(LLVMInstruction::Type::WriteVariable, currentLoopScope(), m_loopCondition); + LLVMInstruction ins(LLVMInstruction::Type::WriteVariable, m_loopCondition); ins.workVariable = variable; createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Unknown, { value }); - - if (m_variablePtrs.find(variable) == m_variablePtrs.cend()) - m_variablePtrs[variable] = LLVMVariablePtr(); - - if (m_loopScope >= 0) { - auto scope = m_loopScopes[m_loopScope]; - m_variablePtrs[variable].loopVariableWrites[scope.get()].push_back(m_instructions.back()); - } - - m_variableInstructions.push_back(m_instructions.back()); + m_utils.createVariablePtr(variable); } void LLVMCodeBuilder::createListClear(List *list) { - LLVMInstruction ins(LLVMInstruction::Type::ClearList, currentLoopScope(), m_loopCondition); + LLVMInstruction ins(LLVMInstruction::Type::ClearList, m_loopCondition); ins.workList = list; createOp(ins, Compiler::StaticType::Void); - - if (m_listPtrs.find(list) == m_listPtrs.cend()) - m_listPtrs[list] = LLVMListPtr(); + m_utils.createListPtr(list); } void LLVMCodeBuilder::createListRemove(List *list, CompilerValue *index) { - LLVMInstruction ins(LLVMInstruction::Type::RemoveListItem, currentLoopScope(), m_loopCondition); + LLVMInstruction ins(LLVMInstruction::Type::RemoveListItem, m_loopCondition); ins.workList = list; createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Number, { index }); - - if (m_listPtrs.find(list) == m_listPtrs.cend()) - m_listPtrs[list] = LLVMListPtr(); + m_utils.createListPtr(list); } void LLVMCodeBuilder::createListAppend(List *list, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::AppendToList, currentLoopScope(), m_loopCondition); + LLVMInstruction ins(LLVMInstruction::Type::AppendToList, m_loopCondition); ins.workList = list; createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Unknown, { item }); - - if (m_listPtrs.find(list) == m_listPtrs.cend()) - m_listPtrs[list] = LLVMListPtr(); - - if (m_loopScope >= 0) { - auto scope = m_loopScopes[m_loopScope]; - m_listPtrs[list].loopListWrites[scope.get()].push_back(m_instructions.back()); - } - - m_listInstructions.push_back(m_instructions.back()); + m_utils.createListPtr(list); } void LLVMCodeBuilder::createListInsert(List *list, CompilerValue *index, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::InsertToList, currentLoopScope(), m_loopCondition); + LLVMInstruction ins(LLVMInstruction::Type::InsertToList, m_loopCondition); ins.workList = list; createOp(ins, Compiler::StaticType::Void, { Compiler::StaticType::Number, Compiler::StaticType::Unknown }, { index, item }); - - if (m_listPtrs.find(list) == m_listPtrs.cend()) - m_listPtrs[list] = LLVMListPtr(); - - if (m_loopScope >= 0) { - auto scope = m_loopScopes[m_loopScope]; - m_listPtrs[list].loopListWrites[scope.get()].push_back(m_instructions.back()); - } - - m_listInstructions.push_back(m_instructions.back()); + m_utils.createListPtr(list); } void LLVMCodeBuilder::createListReplace(List *list, CompilerValue *index, CompilerValue *item) { - LLVMInstruction ins(LLVMInstruction::Type::ListReplace, currentLoopScope(), m_loopCondition); + LLVMInstruction ins(LLVMInstruction::Type::ListReplace, m_loopCondition); ins.workList = list; createOp(ins, Compiler::StaticType::Void, { Compiler::StaticType::Number, Compiler::StaticType::Unknown }, { index, item }); - - if (m_listPtrs.find(list) == m_listPtrs.cend()) - m_listPtrs[list] = LLVMListPtr(); - - if (m_loopScope >= 0) { - auto scope = m_loopScopes[m_loopScope]; - m_listPtrs[list].loopListWrites[scope.get()].push_back(m_instructions.back()); - } - - m_listInstructions.push_back(m_instructions.back()); + m_utils.createListPtr(list); } void LLVMCodeBuilder::beginIfStatement(CompilerValue *cond) { - auto ins = std::make_shared(LLVMInstruction::Type::BeginIf, currentLoopScope(), m_loopCondition); + auto ins = std::make_shared(LLVMInstruction::Type::BeginIf, m_loopCondition); ins->args.push_back({ Compiler::StaticType::Bool, dynamic_cast(cond) }); - m_instructions.push_back(ins); + m_instructions.addInstruction(ins); } void LLVMCodeBuilder::beginElseBranch() { - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::BeginElse, currentLoopScope(), m_loopCondition)); + auto ins = std::make_shared(LLVMInstruction::Type::BeginElse, m_loopCondition); + m_instructions.addInstruction(ins); } void LLVMCodeBuilder::endIf() { - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::EndIf, currentLoopScope(), m_loopCondition)); + auto ins = std::make_shared(LLVMInstruction::Type::EndIf, m_loopCondition); + m_instructions.addInstruction(ins); } void LLVMCodeBuilder::beginRepeatLoop(CompilerValue *count) { assert(!m_loopCondition); - auto ins = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, currentLoopScope(), m_loopCondition); + auto ins = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, m_loopCondition); ins->args.push_back({ Compiler::StaticType::Number, dynamic_cast(count) }); - m_instructions.push_back(ins); - pushLoopScope(false); + m_instructions.addInstruction(ins); } void LLVMCodeBuilder::beginWhileLoop(CompilerValue *cond) @@ -1846,10 +497,9 @@ void LLVMCodeBuilder::beginWhileLoop(CompilerValue *cond) assert(m_loopCondition); m_loopCondition = false; - auto ins = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, currentLoopScope(), m_loopCondition); + auto ins = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, m_loopCondition); ins->args.push_back({ Compiler::StaticType::Bool, dynamic_cast(cond) }); - m_instructions.push_back(ins); - pushLoopScope(false); + m_instructions.addInstruction(ins); } void LLVMCodeBuilder::beginRepeatUntilLoop(CompilerValue *cond) @@ -1857,39 +507,41 @@ void LLVMCodeBuilder::beginRepeatUntilLoop(CompilerValue *cond) assert(m_loopCondition); m_loopCondition = false; - auto ins = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, currentLoopScope(), m_loopCondition); + auto ins = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, m_loopCondition); ins->args.push_back({ Compiler::StaticType::Bool, dynamic_cast(cond) }); - m_instructions.push_back(ins); - pushLoopScope(false); + m_instructions.addInstruction(ins); } void LLVMCodeBuilder::beginLoopCondition() { assert(!m_loopCondition); - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::BeginLoopCondition, currentLoopScope(), m_loopCondition)); + + auto ins = std::make_shared(LLVMInstruction::Type::BeginLoopCondition, m_loopCondition); + m_instructions.addInstruction(ins); m_loopCondition = true; } void LLVMCodeBuilder::endLoop() { - if (!m_warp) - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::Yield, currentLoopScope(), m_loopCondition)); + if (!m_warp) { + auto ins = std::make_shared(LLVMInstruction::Type::Yield, m_loopCondition); + m_instructions.addInstruction(ins); + } - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::EndLoop, currentLoopScope(), m_loopCondition)); - popLoopScope(); + auto ins = std::make_shared(LLVMInstruction::Type::EndLoop, m_loopCondition); + m_instructions.addInstruction(ins); } void LLVMCodeBuilder::yield() { - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::Yield, currentLoopScope(), m_loopCondition)); - - if (m_loopScope >= 0) - m_loopScopes[m_loopScope]->containsYield = true; + auto ins = std::make_shared(LLVMInstruction::Type::Yield, m_loopCondition); + m_instructions.addInstruction(ins); } void LLVMCodeBuilder::createStop() { - m_instructions.push_back(std::make_shared(LLVMInstruction::Type::Stop, currentLoopScope(), m_loopCondition)); + auto ins = std::make_shared(LLVMInstruction::Type::Stop, m_loopCondition); + m_instructions.addInstruction(ins); } void LLVMCodeBuilder::createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args) @@ -1902,213 +554,15 @@ void LLVMCodeBuilder::createProcedureCall(BlockPrototype *prototype, const Compi for (BlockPrototype::ArgType type : procedureArgs) types.push_back(getProcedureArgType(type)); - LLVMInstruction ins(LLVMInstruction::Type::CallProcedure, currentLoopScope(), m_loopCondition); + LLVMInstruction ins(LLVMInstruction::Type::CallProcedure, m_loopCondition); ins.procedurePrototype = prototype; createOp(ins, Compiler::StaticType::Void, types, args); } void LLVMCodeBuilder::initTypes() { - llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - m_valueDataType = LLVMTypes::createValueDataType(&m_builder); - m_stringPtrType = LLVMTypes::createStringPtrType(&m_builder); - m_resumeFuncType = llvm::FunctionType::get(m_builder.getInt1Ty(), pointerType, false); -} - -void LLVMCodeBuilder::createVariableMap() -{ - if (!m_target) - return; - - // Map variable pointers to variable data array indices - const auto &variables = m_target->variables(); - ValueData **variableData = m_target->variableData(); - const size_t len = variables.size(); - m_targetVariableMap.clear(); - m_targetVariableMap.reserve(len); - - size_t i, j; - - for (i = 0; i < len; i++) { - Variable *var = variables[i].get(); - - // Find the data for this variable - for (j = 0; j < len; j++) { - if (variableData[j] == &var->valuePtr()->data()) - break; - } - - if (j < len) - m_targetVariableMap[var] = j; - else - assert(false); - } -} - -void LLVMCodeBuilder::createListMap() -{ - if (!m_target) - return; - - // Map list pointers to list array indices - const auto &lists = m_target->lists(); - List **listData = m_target->listData(); - const size_t len = lists.size(); - m_targetListMap.clear(); - m_targetListMap.reserve(len); - - size_t i, j; - - for (i = 0; i < len; i++) { - List *list = lists[i].get(); - - // Find this list - for (j = 0; j < len; j++) { - if (listData[j] == list) - break; - } - - if (j < len) - m_targetListMap[list] = j; - else - assert(false); - } -} - -void LLVMCodeBuilder::pushScopeLevel() -{ - m_scopeVariables.push_back({}); - - if (m_scopeLists.empty()) { - std::unordered_map listTypes; - - for (auto &[list, listPtr] : m_listPtrs) - listTypes[&listPtr] = Compiler::StaticType::Unknown; - - m_scopeLists.push_back(listTypes); - } else - m_scopeLists.push_back(m_scopeLists.back()); - - m_stringHeap.push_back({}); -} - -void LLVMCodeBuilder::popScopeLevel() -{ - for (size_t i = 0; i < m_scopeVariables.size() - 1; i++) { - for (auto &[ptr, type] : m_scopeVariables[i]) { - if (ptr->type != type) - ptr->type = Compiler::StaticType::Unknown; - } - } - - m_scopeVariables.pop_back(); - - for (size_t i = 0; i < m_scopeLists.size() - 1; i++) { - for (auto &[ptr, type] : m_scopeLists[i]) { - if (ptr->type != type) - ptr->type = Compiler::StaticType::Unknown; - } - } - - m_scopeLists.pop_back(); - - freeScopeHeap(); - m_stringHeap.pop_back(); -} - -void LLVMCodeBuilder::pushLoopScope(bool buildPhase) -{ - if (buildPhase) - m_loopScope = m_loopScopeCounter++; - else { - auto scope = std::make_shared(); - m_loopScopes.push_back(scope); - - if (m_loopScope >= 0) { - auto currentScope = m_loopScopes[m_loopScope]; - currentScope->childScopes.push_back(scope.get()); - scope->parentScope = currentScope.get(); - } - - m_loopScope = m_loopScopes.size() - 1; - } - - m_loopScopeTree.push_back(m_loopScope); -} - -void LLVMCodeBuilder::popLoopScope() -{ - m_loopScopeTree.pop_back(); - - if (m_loopScopeTree.empty()) { - m_loopScope = -1; - } else - m_loopScope = m_loopScopeTree.back(); -} - -std::string LLVMCodeBuilder::getMainFunctionName(BlockPrototype *procedurePrototype) -{ - std::string name; - - switch (m_codeType) { - case Compiler::CodeType::Script: - name = "script"; - break; - - case Compiler::CodeType::Reporter: - name = "reporter"; - break; - - case Compiler::CodeType::HatPredicate: - name = "predicate"; - break; - } - - return procedurePrototype ? "proc." + procedurePrototype->procCode() : name; -} - -std::string LLVMCodeBuilder::getResumeFunctionName(BlockPrototype *procedurePrototype) -{ - return procedurePrototype ? "resume.proc." + procedurePrototype->procCode() : "resume.script"; -} - -llvm::FunctionType *LLVMCodeBuilder::getMainFunctionType(BlockPrototype *procedurePrototype) -{ - // void *f(ExecutionContext *, Target *, ValueData **, List **, (warp arg), (procedure args...)) - // ValueData f(...) (reporters) - // bool f(...) (hat predicates) - llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - std::vector argTypes = { pointerType, pointerType, pointerType, pointerType }; - - if (procedurePrototype) { - argTypes.push_back(m_builder.getInt1Ty()); // warp arg (only in procedures) - const auto &types = procedurePrototype->argumentTypes(); - - for (BlockPrototype::ArgType type : types) { - if (type == BlockPrototype::ArgType::Bool) - argTypes.push_back(m_builder.getInt1Ty()); - else - argTypes.push_back(m_valueDataType->getPointerTo()); - } - } - - llvm::Type *retType = nullptr; - - switch (m_codeType) { - case Compiler::CodeType::Script: - retType = pointerType; - break; - - case Compiler::CodeType::Reporter: - retType = m_valueDataType; - break; - - case Compiler::CodeType::HatPredicate: - retType = m_builder.getInt1Ty(); - break; - } - - return llvm::FunctionType::get(retType, argTypes, false); + m_valueDataType = m_ctx->valueDataType(); + m_stringPtrType = m_ctx->stringPtrType(); } llvm::Function *LLVMCodeBuilder::getOrCreateFunction(const std::string &name, llvm::FunctionType *type) @@ -2136,569 +590,19 @@ LLVMRegister *LLVMCodeBuilder::addReg(std::shared_ptr reg, std::sh return reg.get(); } -llvm::Value *LLVMCodeBuilder::addAlloca(llvm::Type *type) -{ - // Add an alloca to the entry block because allocas must be there (to avoid stack overflow) - llvm::BasicBlock *block = m_builder.GetInsertBlock(); - m_builder.SetInsertPointPastAllocas(m_function); - llvm::Value *ret = m_builder.CreateAlloca(type); - m_builder.SetInsertPoint(block); - return ret; -} - -void LLVMCodeBuilder::freeStringLater(llvm::Value *value) -{ - assert(!m_stringHeap.empty()); - - if (m_stringHeap.empty()) - return; - - m_stringHeap.back().push_back(value); -} - -void LLVMCodeBuilder::freeScopeHeap() -{ - if (m_stringHeap.empty()) - return; - - // Free strings in current scope - auto &heap = m_stringHeap.back(); - - for (llvm::Value *ptr : heap) - m_builder.CreateCall(resolve_string_pool_free(), { ptr }); - - heap.clear(); -} - -llvm::Value *LLVMCodeBuilder::castValue(LLVMRegister *reg, Compiler::StaticType targetType) -{ - if (reg->isConst()) { - if (targetType == Compiler::StaticType::Unknown) - return createValue(reg); - else - return castConstValue(reg->constValue(), targetType); - } - - if (reg->isRawValue) - return castRawValue(reg, targetType); - - assert(reg->type() != Compiler::StaticType::Void && targetType != Compiler::StaticType::Void); - - switch (targetType) { - case Compiler::StaticType::Number: - switch (reg->type()) { - case Compiler::StaticType::Number: { - // Read number directly - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - return m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - } - - case Compiler::StaticType::Bool: { - // Read boolean and cast to double - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *boolValue = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - return m_builder.CreateSIToFP(boolValue, m_builder.getDoubleTy()); - } - - case Compiler::StaticType::String: - case Compiler::StaticType::Unknown: { - // Convert to double - return m_builder.CreateCall(resolve_value_toDouble(), reg->value); - } - - default: - assert(false); - return nullptr; - } - - case Compiler::StaticType::Bool: - switch (reg->type()) { - case Compiler::StaticType::Number: { - // True if != 0 - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *numberValue = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - return m_builder.CreateFCmpONE(numberValue, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); - } - - case Compiler::StaticType::Bool: { - // Read boolean directly - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - return m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - } - - case Compiler::StaticType::String: - case Compiler::StaticType::Unknown: - // Convert to bool - return m_builder.CreateCall(resolve_value_toBool(), reg->value); - - default: - assert(false); - return nullptr; - } - - case Compiler::StaticType::String: - switch (reg->type()) { - case Compiler::StaticType::Number: - case Compiler::StaticType::Bool: - case Compiler::StaticType::Unknown: { - // Cast to string - // TODO: Use value_stringToDouble() and value_stringToBool() - llvm::Value *ptr = m_builder.CreateCall(resolve_value_toStringPtr(), reg->value); - freeStringLater(ptr); - return ptr; - } - - case Compiler::StaticType::String: { - // Read string pointer directly - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - return m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); - } - - default: - assert(false); - return nullptr; - } - - case Compiler::StaticType::Unknown: - return createValue(reg); - - default: - assert(false); - return nullptr; - } -} - -llvm::Value *LLVMCodeBuilder::castRawValue(LLVMRegister *reg, Compiler::StaticType targetType) -{ - if (reg->type() == targetType) - 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()); - - case Compiler::StaticType::String: { - // Convert string to double - return m_builder.CreateCall(resolve_value_stringToDouble(), reg->value); - } - - default: - assert(false); - return nullptr; - } - - 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::String: - // Convert string to bool - return m_builder.CreateCall(resolve_value_stringToBool(), reg->value); - - default: - assert(false); - return nullptr; - } - - case Compiler::StaticType::String: - switch (reg->type()) { - case Compiler::StaticType::Number: { - // Convert double to string - llvm::Value *ptr = m_builder.CreateCall(resolve_value_doubleToStringPtr(), reg->value); - freeStringLater(ptr); - return ptr; - } - - case Compiler::StaticType::Bool: { - // Convert bool to string - llvm::Value *ptr = m_builder.CreateCall(resolve_value_boolToStringPtr(), reg->value); - // NOTE: Dot not deallocate later - return ptr; - } - - default: - assert(false); - return nullptr; - } - - case Compiler::StaticType::Unknown: - return createValue(reg); - - default: - assert(false); - return nullptr; - } -} - -llvm::Constant *LLVMCodeBuilder::castConstValue(const Value &value, Compiler::StaticType targetType) -{ - 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())); - } - - case Compiler::StaticType::Bool: - return m_builder.getInt1(value.toBool()); - - case Compiler::StaticType::String: { - std::u16string str = value.toUtf16(); - - // Create a constant array for the string - std::vector elements; - for (char16_t ch : str) - elements.push_back(m_builder.getInt16(ch)); - - elements.push_back(m_builder.getInt16(0)); // null terminator - - llvm::ArrayType *arrayType = llvm::ArrayType::get(m_builder.getInt16Ty(), elements.size()); - llvm::Constant *constArray = llvm::ConstantArray::get(arrayType, elements); - - llvm::Constant *globalStr = new llvm::GlobalVariable(*m_module, arrayType, true, llvm::GlobalValue::PrivateLinkage, constArray, "string"); - llvm::Constant *stringStruct = llvm::ConstantStruct::get(m_stringPtrType, { globalStr, m_builder.getInt64(str.size()), m_builder.getInt64(str.size() + 1) }); - return new llvm::GlobalVariable(*m_module, m_stringPtrType, true, llvm::GlobalValue::PrivateLinkage, stringStruct, "stringPtr"); - } - - case Compiler::StaticType::Pointer: { - llvm::Constant *addr = m_builder.getInt64((uintptr_t)value.toPointer()); - return llvm::ConstantExpr::getIntToPtr(addr, m_builder.getVoidTy()->getPointerTo()); - } - - default: - assert(false); - return nullptr; - } -} - -Compiler::StaticType LLVMCodeBuilder::optimizeRegisterType(LLVMRegister *reg) const -{ - Compiler::StaticType ret = reg->type(); - - // Optimize string constants that represent numbers - if (reg->isConst() && reg->type() == Compiler::StaticType::String && reg->constValue().isValidNumber()) - ret = Compiler::StaticType::Number; - - return ret; -} - -llvm::Type *LLVMCodeBuilder::getType(Compiler::StaticType type) -{ - switch (type) { - case Compiler::StaticType::Void: - return m_builder.getVoidTy(); - - case Compiler::StaticType::Number: - return m_builder.getDoubleTy(); - - case Compiler::StaticType::Bool: - return m_builder.getInt1Ty(); - - case Compiler::StaticType::String: - return m_stringPtrType->getPointerTo(); - - case Compiler::StaticType::Pointer: - return m_builder.getVoidTy()->getPointerTo(); - - case Compiler::StaticType::Unknown: - return m_valueDataType->getPointerTo(); - - default: - assert(false); - return nullptr; - } -} - Compiler::StaticType LLVMCodeBuilder::getProcedureArgType(BlockPrototype::ArgType type) { return type == BlockPrototype::ArgType::Bool ? Compiler::StaticType::Bool : Compiler::StaticType::Unknown; } -llvm::Value *LLVMCodeBuilder::isNaN(llvm::Value *num) -{ - return m_builder.CreateFCmpUNO(num, num); -} - -llvm::Value *LLVMCodeBuilder::removeNaN(llvm::Value *num) -{ - // Replace NaN with zero - return m_builder.CreateSelect(isNaN(num), llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)), num); -} - -llvm::Value *LLVMCodeBuilder::getVariablePtr(llvm::Value *targetVariables, Variable *variable) -{ - if (!m_target->isStage() && variable->target() == m_target) { - // If this is a local sprite variable, use the variable array at runtime (for clones) - assert(m_targetVariableMap.find(variable) != m_targetVariableMap.cend()); - const size_t index = m_targetVariableMap[variable]; - llvm::Value *ptr = m_builder.CreateGEP(m_valueDataType->getPointerTo(), targetVariables, m_builder.getInt64(index)); - return m_builder.CreateLoad(m_valueDataType->getPointerTo(), ptr); - } - - // Otherwise create a raw pointer at compile time - llvm::Value *addr = m_builder.getInt64((uintptr_t)&variable->value().data()); - return m_builder.CreateIntToPtr(addr, m_valueDataType->getPointerTo()); -} - -llvm::Value *LLVMCodeBuilder::getListPtr(llvm::Value *targetLists, List *list) -{ - if (!m_target->isStage() && list->target() == m_target) { - // If this is a local sprite list, use the list array at runtime (for clones) - assert(m_targetListMap.find(list) != m_targetListMap.cend()); - const size_t index = m_targetListMap[list]; - auto pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - llvm::Value *ptr = m_builder.CreateGEP(pointerType, targetLists, m_builder.getInt64(index)); - return m_builder.CreateLoad(pointerType, ptr); - } - - // Otherwise create a raw pointer at compile time - llvm::Value *addr = m_builder.getInt64((uintptr_t)list); - return m_builder.CreateIntToPtr(addr, m_valueDataType->getPointerTo()); -} - -void LLVMCodeBuilder::syncVariables(llvm::Value *targetVariables) -{ - // Copy stack variables to the actual variables - for (auto &[var, varPtr] : m_variablePtrs) { - if (varPtr.onStack && varPtr.changed) - createValueCopy(varPtr.stackPtr, getVariablePtr(targetVariables, var)); - - varPtr.changed = false; - } -} - -void LLVMCodeBuilder::reloadVariables(llvm::Value *targetVariables) -{ - // Reset variables to use heap - for (auto &[var, varPtr] : m_variablePtrs) { - varPtr.onStack = false; - varPtr.changed = false; - varPtr.type = Compiler::StaticType::Unknown; - } -} - -void LLVMCodeBuilder::reloadLists() -{ - // Reset list data dirty and list types - auto &typeMap = m_scopeLists.back(); - - for (auto &[list, listPtr] : m_listPtrs) { - m_builder.CreateStore(m_builder.getInt1(true), listPtr.dataPtrDirty); - listPtr.type = Compiler::StaticType::Unknown; - typeMap[&listPtr] = listPtr.type; - } -} - -void LLVMCodeBuilder::updateListDataPtr(const LLVMListPtr &listPtr) -{ - // dataPtr = dirty ? list_data(list) : dataPtr - // dirty = false - llvm::Value *dirty = m_builder.CreateLoad(m_builder.getInt1Ty(), listPtr.dataPtrDirty); - llvm::Value *dataPtr = m_builder.CreateSelect(dirty, m_builder.CreateCall(resolve_list_data(), listPtr.ptr), m_builder.CreateLoad(m_valueDataType->getPointerTo(), listPtr.dataPtr)); - m_builder.CreateStore(dataPtr, listPtr.dataPtr); - m_builder.CreateStore(m_builder.getInt1(false), listPtr.dataPtrDirty); -} - -bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType) const -{ - std::unordered_set processed; - int counter = 0; - return isVarOrListTypeSafe(ins, expectedType, processed, counter); -} - -bool LLVMCodeBuilder::isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, std::unordered_set &log, int &c) const -{ - /* - * The main part of the loop type analyzer. - * - * This is a recursive function which is called when variable - * or list instruction is created. It checks the last write to - * the variable or list in one of the loop scopes. - * - * If the last write operation writes a value with a different - * type, it will return false, otherwise true. - * - * If the last written value is from a variable or list, this - * function is called for it to check its type safety (that's - * why it is recursive). - * - * If the variable or list had a write operation before (in - * the same, parent or child loop scope), it is checked - * recursively. - */ - - if (!ins) - return false; - - /* - * If we are processing something that has been already - * processed, it means there's a case like this: - * x = x - * - * or this: - * x = y - * ... - * y = x - * - * Increment counter to ignore last n write operations. - */ - if (log.find(ins.get()) != log.cend()) - c++; - else - log.insert(ins.get()); - - assert(std::find(m_instructions.begin(), m_instructions.end(), ins) != m_instructions.end()); - const LLVMVariablePtr *varPtr = ins->workVariable ? &m_variablePtrs.at(ins->workVariable) : nullptr; - const LLVMListPtr *listPtr = ins->workList ? &m_listPtrs.at(ins->workList) : nullptr; - assert((varPtr || listPtr) && !(varPtr && listPtr)); - auto scope = ins->loopScope; - - // If we aren't in a loop, we're safe - if (!scope) - return true; - - // If the loop scope contains a suspend and this is a non-warp script, the type may change between suspend and resume - if (scope->containsYield && !m_warp) - return false; - - std::shared_ptr write; - const auto &instructions = varPtr ? m_variableInstructions : m_listInstructions; - - // Find this instruction - auto it = std::find(instructions.begin(), instructions.end(), ins); - assert(it != instructions.end()); - - // Find previous write instruction in this, parent or child loop scope - size_t index = it - instructions.begin(); - - if (varPtr && index > 0) { // this is only needed for variables - bool found = false; - - do { - index--; - write = instructions[index]; - const bool isWrite = (VAR_LIST_READ_INSTRUCTIONS.find(write->type) == VAR_LIST_READ_INSTRUCTIONS.cend()); - found = (write->loopScope && isWrite && write->workVariable == ins->workVariable); - } while (index > 0 && !found); - - if (found) { - // Check if the write operation is in this or child scope - auto parentScope = write->loopScope; - - while (parentScope && parentScope != scope) - parentScope = parentScope->parentScope; - - if (!parentScope) { - // Check if the write operation is in any of the parent scopes - parentScope = scope; - - do { - parentScope = parentScope->parentScope; - } while (parentScope && parentScope != write->loopScope); - } - - // If there was a write operation before this instruction (in this, parent or child scope), check it - if (parentScope) { - if (parentScope == scope) - return isVarOrListWriteResultTypeSafe(write, expectedType, true, log, c); - else - return isVarOrListTypeSafe(write, expectedType, log, c); - } - } - } - - const auto &loopWrites = varPtr ? varPtr->loopVariableWrites : listPtr->loopListWrites; - - // Find root loop scope - auto checkScope = scope; - - while (checkScope->parentScope) { - checkScope = checkScope->parentScope; - } - - // Get all write operations in all loop scopes (from the root loop scope) - std::vector> lastWrites; - - while (checkScope) { - auto it = loopWrites.find(checkScope); - - if (it != loopWrites.cend()) { - assert(!it->second.empty()); - const auto &writes = it->second; - - for (auto w : writes) - lastWrites.push_back(w); - } - - if (checkScope->childScopes.empty()) - checkScope = nullptr; - else - checkScope = checkScope->childScopes.back(); - } - - // If there aren't any write operations or all of them are ignored, we're safe - if (c >= lastWrites.size()) - return true; - - if (varPtr) - write = lastWrites[lastWrites.size() - c - 1]; // Ignore last c writes - else { - // If this is a list instruction, check last write operations except current - for (long i = lastWrites.size() - c - 1; i >= 0; i--) { // Ignore last c writes - if (lastWrites[i] == ins) - continue; - - if (!isVarOrListWriteResultTypeSafe(lastWrites[i], expectedType, false, log, c)) - return false; - } - } - - bool safe = true; - - if (VAR_LIST_READ_INSTRUCTIONS.find(ins->type) == VAR_LIST_READ_INSTRUCTIONS.cend()) // write - safe = isVarOrListWriteResultTypeSafe(ins, expectedType, false, log, c); - - if (safe) - return write ? isVarOrListWriteResultTypeSafe(write, expectedType, false, log, c) : true; - else - return false; -} - -bool LLVMCodeBuilder::isVarOrListWriteResultTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set &log, int &c) - const -{ - const LLVMVariablePtr *varPtr = ins->workVariable ? &m_variablePtrs.at(ins->workVariable) : nullptr; - const LLVMListPtr *listPtr = ins->workList ? &m_listPtrs.at(ins->workList) : nullptr; - assert((varPtr || listPtr) && !(varPtr && listPtr)); - - // If the write operation writes the value of another variable, recursively check its type safety - const auto arg = ins->args.back().second; // value is always the last argument - auto argIns = arg->instruction; - - if (argIns && (argIns->type == LLVMInstruction::Type::ReadVariable || argIns->type == LLVMInstruction::Type::GetListItem)) - return isVarOrListTypeSafe(argIns, expectedType, log, c); - - // Check written type - const bool typeMatches = (optimizeRegisterType(arg) == expectedType); - - if (varPtr) - return typeMatches && (varPtr->type == expectedType || ignoreSavedType); - else - return typeMatches && (listPtr->type == expectedType || ignoreSavedType); -} - LLVMRegister *LLVMCodeBuilder::createOp(LLVMInstruction::Type type, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args) { - return createOp({ type, currentLoopScope(), m_loopCondition }, retType, argType, args); + return createOp({ type, m_loopCondition }, retType, argType, args); } LLVMRegister *LLVMCodeBuilder::createOp(LLVMInstruction::Type type, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) { - return createOp({ type, currentLoopScope(), m_loopCondition }, retType, argTypes, args); + return createOp({ type, m_loopCondition }, retType, argTypes, args); } LLVMRegister *LLVMCodeBuilder::createOp(const LLVMInstruction &ins, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args) @@ -2715,7 +619,7 @@ LLVMRegister *LLVMCodeBuilder::createOp(const LLVMInstruction &ins, Compiler::St LLVMRegister *LLVMCodeBuilder::createOp(const LLVMInstruction &ins, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) { auto createdIns = std::make_shared(ins); - m_instructions.push_back(createdIns); + m_instructions.addInstruction(createdIns); for (size_t i = 0; i < args.size(); i++) createdIns->args.push_back({ argTypes[i], dynamic_cast(args[i]) }); @@ -2729,732 +633,3 @@ LLVMRegister *LLVMCodeBuilder::createOp(const LLVMInstruction &ins, Compiler::St return nullptr; } - -LLVMLoopScope *LLVMCodeBuilder::currentLoopScope() const -{ - return m_loopScope >= 0 ? m_loopScopes[m_loopScope].get() : nullptr; -} - -void LLVMCodeBuilder::createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType) -{ - llvm::Value *converted = nullptr; - - if (sourceType != Compiler::StaticType::Unknown) - converted = castValue(reg, sourceType); - - auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [sourceType](const std::pair &pair) { return pair.second == sourceType; }); - const ValueType mappedType = it == TYPE_MAP.cend() ? ValueType::Number : it->first; // unknown type can be ignored - - switch (sourceType) { - case Compiler::StaticType::Number: - switch (targetType) { - case Compiler::StaticType::Number: { - // Write number to number directly - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); - m_builder.CreateStore(converted, ptr); - break; - } - - case Compiler::StaticType::Bool: { - // Write number to bool value directly and change type - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 1); - m_builder.CreateStore(converted, ptr); - m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); - break; - } - - default: - m_builder.CreateCall(resolve_value_assign_double(), { targetPtr, converted }); - break; - } - - break; - - case Compiler::StaticType::Bool: - switch (targetType) { - case Compiler::StaticType::Number: { - // Write bool to number value directly and change type - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); - m_builder.CreateStore(converted, ptr); - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 1); - m_builder.CreateStore(converted, ptr); - m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); - break; - } - - case Compiler::StaticType::Bool: { - // Write bool to bool directly - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); - m_builder.CreateStore(converted, ptr); - break; - } - - default: - m_builder.CreateCall(resolve_value_assign_bool(), { targetPtr, converted }); - break; - } - - break; - - case Compiler::StaticType::String: - m_builder.CreateCall(resolve_value_assign_stringPtr(), { targetPtr, converted }); - break; - - case Compiler::StaticType::Unknown: - m_builder.CreateCall(resolve_value_assign_copy(), { targetPtr, reg->value }); - break; - - default: - assert(false); - break; - } -} - -void LLVMCodeBuilder::createReusedValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType) -{ - // Same as createValueStore(), but ensures that type is updated - createValueStore(reg, targetPtr, sourceType, targetType); - - auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [sourceType](const std::pair &pair) { return pair.second == sourceType; }); - const ValueType mappedType = it == TYPE_MAP.cend() ? ValueType::Number : it->first; // unknown type can be ignored - - if ((sourceType == Compiler::StaticType::Number || sourceType == Compiler::StaticType::Bool) && sourceType == targetType) { - // Update type when writing number to number and bool to bool - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 1); - m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); - } -} - -void LLVMCodeBuilder::createValueCopy(llvm::Value *source, llvm::Value *target) -{ - // NOTE: This doesn't copy strings, but only the pointers - copyStructField(source, target, 0, m_valueDataType, m_builder.getInt64Ty()); // value - copyStructField(source, target, 1, m_valueDataType, m_builder.getInt32Ty()); // type - /* 2: padding */ -} - -void LLVMCodeBuilder::copyStructField(llvm::Value *source, llvm::Value *target, int index, llvm::StructType *structType, llvm::Type *fieldType) -{ - llvm::Value *sourceField = m_builder.CreateStructGEP(structType, source, index); - llvm::Value *targetField = m_builder.CreateStructGEP(structType, target, index); - m_builder.CreateStore(m_builder.CreateLoad(fieldType, sourceField), targetField); -} - -llvm::Value *LLVMCodeBuilder::getListItem(const LLVMListPtr &listPtr, llvm::Value *index) -{ - updateListDataPtr(listPtr); - return m_builder.CreateGEP(m_valueDataType, m_builder.CreateLoad(m_valueDataType->getPointerTo(), listPtr.dataPtr), index); -} - -llvm::Value *LLVMCodeBuilder::getListItemIndex(const LLVMListPtr &listPtr, LLVMRegister *item) -{ - llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); - llvm::BasicBlock *condBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - llvm::BasicBlock *bodyBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - llvm::BasicBlock *cmpIfBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - llvm::BasicBlock *cmpElseBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - llvm::BasicBlock *notFoundBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - - // index = 0 - llvm::Value *index = addAlloca(m_builder.getInt64Ty()); - m_builder.CreateStore(m_builder.getInt64(0), index); - m_builder.CreateBr(condBlock); - - // while (index < size) - m_builder.SetInsertPoint(condBlock); - llvm::Value *cond = m_builder.CreateICmpULT(m_builder.CreateLoad(m_builder.getInt64Ty(), index), size); - m_builder.CreateCondBr(cond, bodyBlock, notFoundBlock); - - // if (list[index] == item) - m_builder.SetInsertPoint(bodyBlock); - LLVMRegister currentItem(listPtr.type); - currentItem.isRawValue = false; - currentItem.value = getListItem(listPtr, m_builder.CreateLoad(m_builder.getInt64Ty(), index)); - llvm::Value *cmp = createComparison(¤tItem, item, Comparison::EQ); - m_builder.CreateCondBr(cmp, cmpIfBlock, cmpElseBlock); - - // goto nextBlock - m_builder.SetInsertPoint(cmpIfBlock); - m_builder.CreateBr(nextBlock); - - // else index++ - m_builder.SetInsertPoint(cmpElseBlock); - m_builder.CreateStore(m_builder.CreateAdd(m_builder.CreateLoad(m_builder.getInt64Ty(), index), m_builder.getInt64(1)), index); - m_builder.CreateBr(condBlock); - - // notFoundBlock: - // index = -1 - // goto nextBlock - m_builder.SetInsertPoint(notFoundBlock); - m_builder.CreateStore(llvm::ConstantInt::get(llvm::Type::getInt64Ty(m_llvmCtx), -1, true), index); - m_builder.CreateBr(nextBlock); - - // nextBlock: - m_builder.SetInsertPoint(nextBlock); - - return m_builder.CreateLoad(m_builder.getInt64Ty(), index); -} - -llvm::Value *LLVMCodeBuilder::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::Value *ret = addAlloca(m_valueDataType); - - switch (reg->constValue().type()) { - case ValueType::Number: - value = llvm::ConstantExpr::getBitCast(value, m_valueDataType->getElementType(0)); - break; - - case ValueType::Bool: - // Assuming union type is int64 - value = m_builder.getInt64(reg->constValue().toBool()); - break; - - case ValueType::String: - case ValueType::Pointer: - value = llvm::ConstantExpr::getPtrToInt(value, m_valueDataType->getElementType(0)); - break; - - default: - assert(false); - break; - } - - llvm::Constant *type = m_builder.getInt32(static_cast(reg->constValue().type())); - llvm::Constant *padding = m_builder.getInt32(0); - llvm::Constant *constValue = llvm::ConstantStruct::get(m_valueDataType, { value, type, padding }); - m_builder.CreateStore(constValue, ret); - - return ret; - } else if (reg->isRawValue) { - llvm::Value *value = castRawValue(reg, reg->type()); - llvm::Value *ret = addAlloca(m_valueDataType); - - // Store value - llvm::Value *valueField = m_builder.CreateStructGEP(m_valueDataType, ret, 0); - m_builder.CreateStore(value, valueField); - - auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [®](const std::pair &pair) { return pair.second == reg->type(); }); - - if (it == TYPE_MAP.end()) { - assert(false); - return nullptr; - } - - // Store type - llvm::Value *typeField = m_builder.CreateStructGEP(m_valueDataType, ret, 1); - ValueType type = it->first; - m_builder.CreateStore(m_builder.getInt32(static_cast(type)), typeField); - - return ret; - } else - return reg->value; -} - -llvm::Value *LLVMCodeBuilder::createNewValue(LLVMRegister *reg) -{ - // Same as createValue(), but creates a copy of the contents - // NOTE: It is the caller's responsibility to free the value. - llvm::Value *value = createValue(reg); - llvm::Value *ret = addAlloca(m_valueDataType); - m_builder.CreateCall(resolve_value_init(), { ret }); - m_builder.CreateCall(resolve_value_assign_copy(), { ret, value }); - return ret; -} - -llvm::Value *LLVMCodeBuilder::createComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type) -{ - auto type1 = arg1->type(); - auto type2 = arg2->type(); - - if (arg1->isConst() && arg2->isConst()) { - // If both operands are constant, perform the comparison at compile time - bool result = false; - - switch (type) { - case Comparison::EQ: - result = arg1->constValue() == arg2->constValue(); - break; - - case Comparison::GT: - result = arg1->constValue() > arg2->constValue(); - break; - - case Comparison::LT: - result = arg1->constValue() < arg2->constValue(); - break; - - default: - assert(false); - return nullptr; - } - - 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) { - if ((type1 == Compiler::StaticType::Number && type2 == Compiler::StaticType::String && arg2->isConst() && !arg2->constValue().isValidNumber()) || - (type1 == Compiler::StaticType::String && type2 == Compiler::StaticType::Number && arg1->isConst() && !arg1->constValue().isValidNumber())) - return m_builder.getInt1(false); - } - - if (type1 != type2 || type1 == Compiler::StaticType::Unknown || type2 == Compiler::StaticType::Unknown) { - // 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); - - switch (type) { - case Comparison::EQ: - return m_builder.CreateCall(resolve_value_equals(), { value1, value2 }); - - case Comparison::GT: - return m_builder.CreateCall(resolve_value_greater(), { value1, value2 }); - - case Comparison::LT: - return m_builder.CreateCall(resolve_value_lower(), { value1, value2 }); - - default: - assert(false); - return nullptr; - } - } 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; - } - } - - case Compiler::StaticType::Bool: - // Compare two booleans - 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; - } - - case Compiler::StaticType::String: { - // Compare two strings - llvm::Value *cmpRet = m_builder.CreateCall(resolve_string_compare_case_insensitive(), { value1, value2 }); - - switch (type) { - case Comparison::EQ: - return m_builder.CreateICmpEQ(cmpRet, m_builder.getInt32(0)); - - case Comparison::GT: - return m_builder.CreateICmpSGT(cmpRet, m_builder.getInt32(0)); - - case Comparison::LT: - return m_builder.CreateICmpSLT(cmpRet, m_builder.getInt32(0)); - - default: - assert(false); - return nullptr; - } - } - - default: - assert(false); - return nullptr; - } - } - } -} - -llvm::Value *LLVMCodeBuilder::createStringComparison(LLVMRegister *arg1, LLVMRegister *arg2, bool caseSensitive) -{ - auto type1 = arg1->type(); - auto type2 = arg2->type(); - - if (arg1->isConst() && arg2->isConst()) { - // If both operands are constant, perform the comparison at compile time - StringPtr *str1 = value_toStringPtr(&arg1->constValue().data()); - StringPtr *str2 = value_toStringPtr(&arg2->constValue().data()); - bool result; - - if (caseSensitive) - result = string_compare_case_sensitive(str1, str2) == 0; - else { - result = string_compare_case_insensitive(str1, str2) == 0; - } - - string_pool_free(str1); - string_pool_free(str2); - return m_builder.getInt1(result); - } else { - // Optimize number and string constant comparison - // TODO: Optimize bool and string constant comparison (in compare() as well) - if ((type1 == Compiler::StaticType::Number && type2 == Compiler::StaticType::String && arg2->isConst() && !arg2->constValue().isValidNumber()) || - (type1 == Compiler::StaticType::String && type2 == Compiler::StaticType::Number && arg1->isConst() && !arg1->constValue().isValidNumber())) - return m_builder.getInt1(false); - - // Explicitly cast to string - llvm::Value *string1 = castValue(arg1, Compiler::StaticType::String); - llvm::Value *string2 = castValue(arg2, Compiler::StaticType::String); - llvm::Value *cmp = m_builder.CreateCall(caseSensitive ? resolve_string_compare_case_sensitive() : resolve_string_compare_case_insensitive(), { string1, string2 }); - return m_builder.CreateICmpEQ(cmp, m_builder.getInt32(0)); - } -} - -void LLVMCodeBuilder::createSuspend(LLVMCoroutine *coro, llvm::Value *warpArg, llvm::Value *targetVariables) -{ - if (!m_warp) { - llvm::BasicBlock *suspendBranch, *nextBranch; - - if (warpArg) { - suspendBranch = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - nextBranch = llvm::BasicBlock::Create(m_llvmCtx, "", m_function); - m_builder.CreateCondBr(warpArg, nextBranch, suspendBranch); - m_builder.SetInsertPoint(suspendBranch); - } - - syncVariables(targetVariables); - coro->createSuspend(); - reloadVariables(targetVariables); - reloadLists(); - - if (warpArg) { - m_builder.CreateBr(nextBranch); - m_builder.SetInsertPoint(nextBranch); - } - } -} - -llvm::FunctionCallee LLVMCodeBuilder::resolveFunction(const std::string name, llvm::FunctionType *type) -{ - return m_module->getOrInsertFunction(name, type); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_init() -{ - return resolveFunction("value_init", llvm::FunctionType::get(m_builder.getVoidTy(), m_valueDataType->getPointerTo(), false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_free() -{ - return resolveFunction("value_free", llvm::FunctionType::get(m_builder.getVoidTy(), m_valueDataType->getPointerTo(), false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_long() -{ - return resolveFunction("value_assign_long", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getInt64Ty() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_double() -{ - return resolveFunction("value_assign_double", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getDoubleTy() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_bool() -{ - return resolveFunction("value_assign_bool", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getInt1Ty() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_stringPtr() -{ - return resolveFunction("value_assign_stringPtr", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_stringPtrType->getPointerTo() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_special() -{ - return resolveFunction("value_assign_special", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_builder.getInt32Ty() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_copy() -{ - return resolveFunction("value_assign_copy", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), m_valueDataType->getPointerTo() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toDouble() -{ - llvm::FunctionCallee callee = resolveFunction("value_toDouble", llvm::FunctionType::get(m_builder.getDoubleTy(), m_valueDataType->getPointerTo(), false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toBool() -{ - llvm::FunctionCallee callee = resolveFunction("value_toBool", llvm::FunctionType::get(m_builder.getInt1Ty(), m_valueDataType->getPointerTo(), false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toStringPtr() -{ - // NOTE: This function can't be marked read-only because it allocates on the heap - return resolveFunction("value_toStringPtr", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), m_valueDataType->getPointerTo(), false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_doubleToStringPtr() -{ - // NOTE: This function can't be marked read-only because it allocates on the heap - return resolveFunction("value_doubleToStringPtr", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), m_builder.getDoubleTy(), false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_boolToStringPtr() -{ - // NOTE: This function can be marked read-only because it does NOT allocate on the heap ("true" and "false" constants) - llvm::FunctionCallee callee = resolveFunction("value_boolToStringPtr", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), m_builder.getInt1Ty(), false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_stringToDouble() -{ - llvm::FunctionCallee callee = resolveFunction("value_stringToDouble", llvm::FunctionType::get(m_builder.getDoubleTy(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0), false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_stringToBool() -{ - llvm::FunctionCallee callee = resolveFunction("value_stringToBool", llvm::FunctionType::get(m_builder.getInt1Ty(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0), false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_equals() -{ - llvm::Type *valuePtr = m_valueDataType->getPointerTo(); - llvm::FunctionCallee callee = resolveFunction("value_equals", llvm::FunctionType::get(m_builder.getInt1Ty(), { valuePtr, valuePtr }, false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_greater() -{ - llvm::Type *valuePtr = m_valueDataType->getPointerTo(); - llvm::FunctionCallee callee = resolveFunction("value_greater", llvm::FunctionType::get(m_builder.getInt1Ty(), { valuePtr, valuePtr }, false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_value_lower() -{ - llvm::Type *valuePtr = m_valueDataType->getPointerTo(); - llvm::FunctionCallee callee = resolveFunction("value_lower", llvm::FunctionType::get(m_builder.getInt1Ty(), { valuePtr, valuePtr }, false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_list_clear() -{ - llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - return resolveFunction("list_clear", llvm::FunctionType::get(m_builder.getVoidTy(), { listPtr }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_list_remove() -{ - llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - return resolveFunction("list_remove", llvm::FunctionType::get(m_builder.getVoidTy(), { listPtr, m_builder.getInt64Ty() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_list_append_empty() -{ - llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - return resolveFunction("list_append_empty", llvm::FunctionType::get(m_valueDataType->getPointerTo(), { listPtr }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_list_insert_empty() -{ - llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - return resolveFunction("list_insert_empty", llvm::FunctionType::get(m_valueDataType->getPointerTo(), { listPtr, m_builder.getInt64Ty() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_list_data() -{ - llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - llvm::FunctionCallee callee = resolveFunction("list_data", llvm::FunctionType::get(m_valueDataType->getPointerTo(), { listPtr }, false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_list_size_ptr() -{ - llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - llvm::FunctionCallee callee = resolveFunction("list_size_ptr", llvm::FunctionType::get(m_builder.getInt64Ty()->getPointerTo()->getPointerTo(), { listPtr }, false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_list_alloc_size_ptr() -{ - llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - llvm::FunctionCallee callee = resolveFunction("list_alloc_size_ptr", llvm::FunctionType::get(m_builder.getInt64Ty()->getPointerTo()->getPointerTo(), { listPtr }, false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_list_to_string() -{ - llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - llvm::FunctionCallee callee = resolveFunction("list_to_string", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), { pointerType }, false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_llvm_random() -{ - llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - llvm::Type *valuePtr = m_valueDataType->getPointerTo(); - return resolveFunction("llvm_random", llvm::FunctionType::get(m_builder.getDoubleTy(), { pointerType, valuePtr, valuePtr }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_llvm_random_double() -{ - llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - return resolveFunction("llvm_random_double", llvm::FunctionType::get(m_builder.getDoubleTy(), { pointerType, m_builder.getDoubleTy(), m_builder.getDoubleTy() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_llvm_random_long() -{ - llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - return resolveFunction("llvm_random_long", llvm::FunctionType::get(m_builder.getDoubleTy(), { pointerType, m_builder.getInt64Ty(), m_builder.getInt64Ty() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_llvm_random_bool() -{ - llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); - return resolveFunction("llvm_random_bool", llvm::FunctionType::get(m_builder.getDoubleTy(), { pointerType, m_builder.getInt1Ty(), m_builder.getInt1Ty() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_string_pool_new() -{ - return resolveFunction("string_pool_new", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), { m_builder.getInt1Ty() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_string_pool_free() -{ - return resolveFunction("string_pool_free", llvm::FunctionType::get(m_builder.getVoidTy(), { m_stringPtrType->getPointerTo() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_string_alloc() -{ - return resolveFunction("string_alloc", llvm::FunctionType::get(m_builder.getVoidTy(), { m_stringPtrType->getPointerTo(), m_builder.getInt64Ty() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_string_assign() -{ - return resolveFunction("string_assign", llvm::FunctionType::get(m_builder.getVoidTy(), { m_stringPtrType->getPointerTo(), m_stringPtrType->getPointerTo() }, false)); -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_string_compare_case_sensitive() -{ - llvm::Type *stringPtr = m_stringPtrType->getPointerTo(); - llvm::FunctionCallee callee = resolveFunction("string_compare_case_sensitive", llvm::FunctionType::get(m_builder.getInt32Ty(), { stringPtr, stringPtr }, false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} - -llvm::FunctionCallee LLVMCodeBuilder::resolve_string_compare_case_insensitive() -{ - llvm::Type *stringPtr = m_stringPtrType->getPointerTo(); - llvm::FunctionCallee callee = resolveFunction("string_compare_case_insensitive", llvm::FunctionType::get(m_builder.getInt32Ty(), { stringPtr, stringPtr }, false)); - llvm::Function *func = llvm::cast(callee.getCallee()); - func->addFnAttr(llvm::Attribute::ReadOnly); - return callee; -} diff --git a/src/engine/internal/llvm/llvmcodebuilder.h b/src/engine/internal/llvm/llvmcodebuilder.h index 68bb517ea..ecb1f4bf5 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.h +++ b/src/engine/internal/llvm/llvmcodebuilder.h @@ -10,24 +10,27 @@ #include #include "../icodebuilder.h" +#include "llvmbuildutils.h" #include "llvminstruction.h" +#include "llvminstructionlist.h" #include "llvmcoroutine.h" #include "llvmvariableptr.h" #include "llvmlistptr.h" +#include "llvmfunctions.h" +#include "instructions/instructionbuilder.h" namespace libscratchcpp { class LLVMCompilerContext; class LLVMConstantRegister; -class LLVMLoopScope; class LLVMCodeBuilder : public ICodeBuilder { public: LLVMCodeBuilder(LLVMCompilerContext *ctx, BlockPrototype *procedurePrototype = nullptr, Compiler::CodeType codeType = Compiler::CodeType::Script); - std::shared_ptr finalize() override; + std::shared_ptr build() override; CompilerValue *addFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) override; CompilerValue *addTargetFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) override; @@ -112,148 +115,44 @@ class LLVMCodeBuilder : public ICodeBuilder void createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args) override; private: - enum class Comparison - { - EQ, - GT, - LT - }; - void initTypes(); - void createVariableMap(); - void createListMap(); - void pushScopeLevel(); - void popScopeLevel(); - void pushLoopScope(bool buildPhase); - void popLoopScope(); - - std::string getMainFunctionName(BlockPrototype *procedurePrototype); - std::string getResumeFunctionName(BlockPrototype *procedurePrototype); - llvm::FunctionType *getMainFunctionType(BlockPrototype *procedurePrototype); + llvm::Function *getOrCreateFunction(const std::string &name, llvm::FunctionType *type); void verifyFunction(llvm::Function *func); LLVMRegister *addReg(std::shared_ptr reg, std::shared_ptr ins); - llvm::Value *addAlloca(llvm::Type *type); - void freeStringLater(llvm::Value *value); - void freeScopeHeap(); - llvm::Value *castValue(LLVMRegister *reg, Compiler::StaticType targetType); - llvm::Value *castRawValue(LLVMRegister *reg, Compiler::StaticType targetType); - llvm::Constant *castConstValue(const Value &value, Compiler::StaticType targetType); - Compiler::StaticType optimizeRegisterType(LLVMRegister *reg) const; - llvm::Type *getType(Compiler::StaticType type); Compiler::StaticType getProcedureArgType(BlockPrototype::ArgType type); - llvm::Value *isNaN(llvm::Value *num); - llvm::Value *removeNaN(llvm::Value *num); - - llvm::Value *getVariablePtr(llvm::Value *targetVariables, Variable *variable); - llvm::Value *getListPtr(llvm::Value *targetLists, List *list); - void syncVariables(llvm::Value *targetVariables); - void reloadVariables(llvm::Value *targetVariables); - void reloadLists(); - void updateListDataPtr(const LLVMListPtr &listPtr); - bool isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType) const; - bool isVarOrListTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, std::unordered_set &log, int &c) const; - bool isVarOrListWriteResultTypeSafe(std::shared_ptr ins, Compiler::StaticType expectedType, bool ignoreSavedType, std::unordered_set &log, int &c) const; LLVMRegister *createOp(LLVMInstruction::Type type, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args); LLVMRegister *createOp(LLVMInstruction::Type type, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes = {}, const Compiler::Args &args = {}); LLVMRegister *createOp(const LLVMInstruction &ins, Compiler::StaticType retType, Compiler::StaticType argType, const Compiler::Args &args); LLVMRegister *createOp(const LLVMInstruction &ins, Compiler::StaticType retType, const Compiler::ArgTypes &argTypes = {}, const Compiler::Args &args = {}); - LLVMLoopScope *currentLoopScope() const; - - void createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType); - void createReusedValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType); - void createValueCopy(llvm::Value *source, llvm::Value *target); - void copyStructField(llvm::Value *source, llvm::Value *target, int index, llvm::StructType *structType, llvm::Type *fieldType); - llvm::Value *getListItem(const LLVMListPtr &listPtr, llvm::Value *index); - llvm::Value *getListItemIndex(const LLVMListPtr &listPtr, LLVMRegister *item); - llvm::Value *createValue(LLVMRegister *reg); - llvm::Value *createNewValue(LLVMRegister *reg); - llvm::Value *createComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type); - llvm::Value *createStringComparison(LLVMRegister *arg1, LLVMRegister *arg2, bool caseSensitive); - - void createSuspend(LLVMCoroutine *coro, llvm::Value *warpArg, llvm::Value *targetVariables); - - llvm::FunctionCallee resolveFunction(const std::string name, llvm::FunctionType *type); - llvm::FunctionCallee resolve_value_init(); - llvm::FunctionCallee resolve_value_free(); - llvm::FunctionCallee resolve_value_assign_long(); - llvm::FunctionCallee resolve_value_assign_double(); - llvm::FunctionCallee resolve_value_assign_bool(); - llvm::FunctionCallee resolve_value_assign_stringPtr(); - llvm::FunctionCallee resolve_value_assign_special(); - llvm::FunctionCallee resolve_value_assign_copy(); - llvm::FunctionCallee resolve_value_toDouble(); - llvm::FunctionCallee resolve_value_toBool(); - llvm::FunctionCallee resolve_value_toStringPtr(); - llvm::FunctionCallee resolve_value_doubleToStringPtr(); - llvm::FunctionCallee resolve_value_boolToStringPtr(); - llvm::FunctionCallee resolve_value_stringToDouble(); - llvm::FunctionCallee resolve_value_stringToBool(); - llvm::FunctionCallee resolve_value_equals(); - llvm::FunctionCallee resolve_value_greater(); - llvm::FunctionCallee resolve_value_lower(); - llvm::FunctionCallee resolve_list_clear(); - llvm::FunctionCallee resolve_list_remove(); - llvm::FunctionCallee resolve_list_append_empty(); - llvm::FunctionCallee resolve_list_insert_empty(); - llvm::FunctionCallee resolve_list_data(); - llvm::FunctionCallee resolve_list_size_ptr(); - llvm::FunctionCallee resolve_list_alloc_size_ptr(); - 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_bool(); - llvm::FunctionCallee resolve_string_pool_new(); - llvm::FunctionCallee resolve_string_pool_free(); - llvm::FunctionCallee resolve_string_alloc(); - llvm::FunctionCallee resolve_string_assign(); - llvm::FunctionCallee resolve_string_compare_case_sensitive(); - llvm::FunctionCallee resolve_string_compare_case_insensitive(); Target *m_target = nullptr; - std::unordered_map m_targetVariableMap; - std::unordered_map m_variablePtrs; - std::vector> m_scopeVariables; - - std::unordered_map m_targetListMap; - std::unordered_map m_listPtrs; - std::vector> m_scopeLists; - LLVMCompilerContext *m_ctx; llvm::LLVMContext &m_llvmCtx; llvm::Module *m_module = nullptr; llvm::IRBuilder<> m_builder; llvm::Function *m_function = nullptr; + LLVMBuildUtils m_utils; llvm::StructType *m_valueDataType = nullptr; llvm::StructType *m_stringPtrType = nullptr; - llvm::FunctionType *m_resumeFuncType = nullptr; - std::vector> m_instructions; + LLVMInstructionList m_instructions; std::vector> m_regs; std::vector> m_localVars; LLVMRegister *m_lastConstValue = nullptr; // for reporters and hat predicates BlockPrototype *m_procedurePrototype = nullptr; bool m_defaultWarp = false; bool m_warp = false; - int m_defaultArgCount = 0; Compiler::CodeType m_codeType = Compiler::CodeType::Script; - long m_loopScope = -1; // index - std::vector> m_loopScopes; - long m_loopScopeCounter = 0; // replacement for m_loopScopes size in build phase - std::vector m_loopScopeTree; bool m_loopCondition = false; // whether we're currently compiling a loop condition - std::vector> m_variableInstructions; - std::vector> m_listInstructions; - std::vector> m_stringHeap; // scopes - std::shared_ptr m_output; + llvmins::InstructionBuilder m_instructionBuilder; }; } // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmcompilercontext.cpp b/src/engine/internal/llvm/llvmcompilercontext.cpp index 74199aa1d..f879f930a 100644 --- a/src/engine/internal/llvm/llvmcompilercontext.cpp +++ b/src/engine/internal/llvm/llvmcompilercontext.cpp @@ -8,6 +8,8 @@ #include #include "llvmcompilercontext.h" +#include "llvmcoroutine.h" +#include "llvmtypes.h" using namespace libscratchcpp; @@ -17,9 +19,16 @@ LLVMCompilerContext::LLVMCompilerContext(IEngine *engine, Target *target) : m_module(std::make_unique(target ? target->name() : "", *m_llvmCtx)), m_llvmCtxPtr(m_llvmCtx.get()), m_modulePtr(m_module.get()), - m_jit((initTarget(), llvm::orc::LLJITBuilder().create())), - m_llvmCoroDestroyFunction(createCoroDestroyFunction()) + m_jit((initTarget(), llvm::orc::LLJITBuilder().create())) { + // Create functions + m_llvmCoroResumeFunction = createCoroResumeFunction(); + m_llvmCoroDestroyFunction = createCoroDestroyFunction(); + + // Create types + m_valueDataType = LLVMTypes::createValueDataType(*m_llvmCtx); + m_stringPtrType = LLVMTypes::createStringPtrType(*m_llvmCtx); + if (!m_jit) { llvm::errs() << "error: failed to create JIT: " << toString(m_jit.takeError()) << "\n"; return; @@ -111,6 +120,11 @@ bool LLVMCompilerContext::jitInitialized() const return m_jitInitialized; } +llvm::Function *LLVMCompilerContext::coroutineResumeFunction() const +{ + return m_llvmCoroResumeFunction; +} + void LLVMCompilerContext::destroyCoroutine(void *handle) { if (!m_jitInitialized) { @@ -122,6 +136,16 @@ void LLVMCompilerContext::destroyCoroutine(void *handle) m_coroDestroyFunction(handle); } +llvm::StructType *LLVMCompilerContext::valueDataType() const +{ + return m_valueDataType; +} + +llvm::StructType *LLVMCompilerContext::stringPtrType() const +{ + return m_stringPtrType; +} + void LLVMCompilerContext::initTarget() { llvm::InitializeNativeTarget(); @@ -129,6 +153,26 @@ void LLVMCompilerContext::initTarget() llvm::InitializeNativeTargetAsmParser(); } +llvm::Function *LLVMCompilerContext::createCoroResumeFunction() +{ + llvm::IRBuilder<> builder(*m_llvmCtx); + + // bool coro_resume(void *handle) + llvm::FunctionType *funcType = llvm::FunctionType::get(builder.getInt1Ty(), builder.getVoidTy()->getPointerTo(), false); + llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "coro_resume", m_module.get()); + func->setComdat(m_module->getOrInsertComdat(func->getName())); + func->setDSOLocal(true); + func->addFnAttr(llvm::Attribute::NoInline); + func->addFnAttr(llvm::Attribute::OptimizeNone); + + llvm::BasicBlock *entry = llvm::BasicBlock::Create(*m_llvmCtx, "entry", func); + builder.SetInsertPoint(entry); + builder.CreateRet(LLVMCoroutine::createResume(m_module.get(), &builder, func, func->getArg(0))); + + verifyFunction(func); + return func; +} + llvm::Function *LLVMCompilerContext::createCoroDestroyFunction() { llvm::IRBuilder<> builder(*m_llvmCtx); @@ -146,10 +190,14 @@ llvm::Function *LLVMCompilerContext::createCoroDestroyFunction() builder.CreateCall(llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::coro_destroy), { func->getArg(0) }); builder.CreateRetVoid(); - if (llvm::verifyFunction(*func, &llvm::errs())) { - llvm::errs() << "error: coro_destroy() function verficiation failed!\n"; + verifyFunction(func); + return func; +} + +void LLVMCompilerContext::verifyFunction(llvm::Function *function) +{ + if (llvm::verifyFunction(*function, &llvm::errs())) { + llvm::errs() << "error: " << function->getName() << "function verficiation failed!\n"; llvm::errs() << "module name: " << m_module->getName() << "\n"; } - - return func; } diff --git a/src/engine/internal/llvm/llvmcompilercontext.h b/src/engine/internal/llvm/llvmcompilercontext.h index 27878ffe4..637e4961a 100644 --- a/src/engine/internal/llvm/llvmcompilercontext.h +++ b/src/engine/internal/llvm/llvmcompilercontext.h @@ -28,8 +28,12 @@ class LLVMCompilerContext : public CompilerContext void initJit(); bool jitInitialized() const; + llvm::Function *coroutineResumeFunction() const; void destroyCoroutine(void *handle); + llvm::StructType *valueDataType() const; + llvm::StructType *stringPtrType() const; + template T lookupFunction(const std::string &name) { @@ -44,19 +48,30 @@ class LLVMCompilerContext : public CompilerContext } private: + using ResumeCoroFuncType = bool (*)(void *); using DestroyCoroFuncType = void (*)(void *); void initTarget(); + + llvm::Function *createCoroResumeFunction(); llvm::Function *createCoroDestroyFunction(); + void verifyFunction(llvm::Function *function); + std::unique_ptr m_llvmCtx; std::unique_ptr m_module; llvm::LLVMContext *m_llvmCtxPtr = nullptr; llvm::Module *m_modulePtr = nullptr; llvm::Expected> m_jit; + bool m_jitInitialized = false; + + llvm::Function *m_llvmCoroResumeFunction = nullptr; + llvm::Function *m_llvmCoroDestroyFunction = nullptr; DestroyCoroFuncType m_coroDestroyFunction = nullptr; - bool m_jitInitialized = false; + + llvm::StructType *m_valueDataType = nullptr; + llvm::StructType *m_stringPtrType = nullptr; }; } // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmcoroutine.cpp b/src/engine/internal/llvm/llvmcoroutine.cpp index 97baff396..f4b268fbe 100644 --- a/src/engine/internal/llvm/llvmcoroutine.cpp +++ b/src/engine/internal/llvm/llvmcoroutine.cpp @@ -105,32 +105,32 @@ void LLVMCoroutine::createSuspend() m_builder->SetInsertPoint(resumeBranch); } -llvm::Value *LLVMCoroutine::createResume(llvm::Function *function, llvm::Value *coroHandle) +llvm::Value *LLVMCoroutine::createResume(llvm::Module *module, llvm::IRBuilder<> *builder, llvm::Function *function, llvm::Value *coroHandle) { - llvm::LLVMContext &ctx = m_builder->getContext(); - llvm::Function *coroDone = llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::coro_done); + llvm::LLVMContext &ctx = builder->getContext(); + llvm::Function *coroDone = llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::coro_done); - llvm::Value *ret = m_builder->CreateAlloca(m_builder->getInt1Ty()); - llvm::Value *done = m_builder->CreateCall(coroDone, { coroHandle }); - done = m_builder->CreateCall(coroDone, { coroHandle }); + llvm::Value *ret = builder->CreateAlloca(builder->getInt1Ty()); + llvm::Value *done = builder->CreateCall(coroDone, { coroHandle }); + done = builder->CreateCall(coroDone, { coroHandle }); llvm::BasicBlock *destroyBranch = llvm::BasicBlock::Create(ctx, "", function); llvm::BasicBlock *resumeBranch = llvm::BasicBlock::Create(ctx, "", function); llvm::BasicBlock *nextBranch = llvm::BasicBlock::Create(ctx, "", function); - m_builder->CreateCondBr(done, destroyBranch, resumeBranch); + builder->CreateCondBr(done, destroyBranch, resumeBranch); - m_builder->SetInsertPoint(destroyBranch); - m_builder->CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::coro_destroy), { coroHandle }); - m_builder->CreateBr(nextBranch); + builder->SetInsertPoint(destroyBranch); + builder->CreateCall(llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::coro_destroy), { coroHandle }); + builder->CreateBr(nextBranch); - m_builder->SetInsertPoint(resumeBranch); - m_builder->CreateCall(llvm::Intrinsic::getDeclaration(m_module, llvm::Intrinsic::coro_resume), { coroHandle }); - done = m_builder->CreateCall(coroDone, { coroHandle }); - m_builder->CreateStore(done, ret); - m_builder->CreateCondBr(done, destroyBranch, nextBranch); + builder->SetInsertPoint(resumeBranch); + builder->CreateCall(llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::coro_resume), { coroHandle }); + done = builder->CreateCall(coroDone, { coroHandle }); + builder->CreateStore(done, ret); + builder->CreateCondBr(done, destroyBranch, nextBranch); - m_builder->SetInsertPoint(nextBranch); - return m_builder->CreateLoad(m_builder->getInt1Ty(), ret); + builder->SetInsertPoint(nextBranch); + return builder->CreateLoad(builder->getInt1Ty(), ret); } void LLVMCoroutine::end() diff --git a/src/engine/internal/llvm/llvmcoroutine.h b/src/engine/internal/llvm/llvmcoroutine.h index 071798d93..fa3d79fd9 100644 --- a/src/engine/internal/llvm/llvmcoroutine.h +++ b/src/engine/internal/llvm/llvmcoroutine.h @@ -22,7 +22,7 @@ class LLVMCoroutine llvm::Value *didSuspendVar() const; void createSuspend(); - llvm::Value *createResume(llvm::Function *function, llvm::Value *coroHandle); + static llvm::Value *createResume(llvm::Module *module, llvm::IRBuilder<> *builder, llvm::Function *function, llvm::Value *coroHandle); void end(); private: diff --git a/src/engine/internal/llvm/llvmfunctions.cpp b/src/engine/internal/llvm/llvmfunctions.cpp index 0304382b9..899254aa8 100644 --- a/src/engine/internal/llvm/llvmfunctions.cpp +++ b/src/engine/internal/llvm/llvmfunctions.cpp @@ -4,8 +4,10 @@ #include #include -namespace libscratchcpp -{ +#include "llvmfunctions.h" +#include "llvmcompilercontext.h" + +using namespace libscratchcpp; extern "C" { @@ -30,4 +32,260 @@ extern "C" } } -} // namespace libscratchcpp +LLVMFunctions::LLVMFunctions(LLVMCompilerContext *ctx, llvm::IRBuilder<> *builder) : + m_ctx(ctx), + m_builder(builder) +{ + // Custom types + m_stringPtrType = m_ctx->stringPtrType(); + m_valueDataType = m_ctx->valueDataType(); +} + +llvm::FunctionCallee LLVMFunctions::resolveFunction(const std::string name, llvm::FunctionType *type) +{ + return m_ctx->module()->getOrInsertFunction(name, type); +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_init() +{ + return resolveFunction("value_init", llvm::FunctionType::get(m_builder->getVoidTy(), m_valueDataType->getPointerTo(), false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_free() +{ + return resolveFunction("value_free", llvm::FunctionType::get(m_builder->getVoidTy(), m_valueDataType->getPointerTo(), false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_assign_long() +{ + return resolveFunction("value_assign_long", llvm::FunctionType::get(m_builder->getVoidTy(), { m_valueDataType->getPointerTo(), m_builder->getInt64Ty() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_assign_double() +{ + return resolveFunction("value_assign_double", llvm::FunctionType::get(m_builder->getVoidTy(), { m_valueDataType->getPointerTo(), m_builder->getDoubleTy() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_assign_bool() +{ + return resolveFunction("value_assign_bool", llvm::FunctionType::get(m_builder->getVoidTy(), { m_valueDataType->getPointerTo(), m_builder->getInt1Ty() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_assign_stringPtr() +{ + return resolveFunction("value_assign_stringPtr", llvm::FunctionType::get(m_builder->getVoidTy(), { m_valueDataType->getPointerTo(), m_stringPtrType->getPointerTo() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_assign_special() +{ + return resolveFunction("value_assign_special", llvm::FunctionType::get(m_builder->getVoidTy(), { m_valueDataType->getPointerTo(), m_builder->getInt32Ty() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_assign_copy() +{ + return resolveFunction("value_assign_copy", llvm::FunctionType::get(m_builder->getVoidTy(), { m_valueDataType->getPointerTo(), m_valueDataType->getPointerTo() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_toDouble() +{ + llvm::FunctionCallee callee = resolveFunction("value_toDouble", llvm::FunctionType::get(m_builder->getDoubleTy(), m_valueDataType->getPointerTo(), false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_toBool() +{ + llvm::FunctionCallee callee = resolveFunction("value_toBool", llvm::FunctionType::get(m_builder->getInt1Ty(), m_valueDataType->getPointerTo(), false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_toStringPtr() +{ + // NOTE: This function can't be marked read-only because it allocates on the heap + return resolveFunction("value_toStringPtr", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), m_valueDataType->getPointerTo(), false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_doubleToStringPtr() +{ + // NOTE: This function can't be marked read-only because it allocates on the heap + return resolveFunction("value_doubleToStringPtr", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), m_builder->getDoubleTy(), false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_boolToStringPtr() +{ + // NOTE: This function can be marked read-only because it does NOT allocate on the heap ("true" and "false" constants) + llvm::FunctionCallee callee = resolveFunction("value_boolToStringPtr", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), m_builder->getInt1Ty(), false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_stringToDouble() +{ + llvm::FunctionCallee callee = + resolveFunction("value_stringToDouble", llvm::FunctionType::get(m_builder->getDoubleTy(), llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0), 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)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_equals() +{ + llvm::Type *valuePtr = m_valueDataType->getPointerTo(); + llvm::FunctionCallee callee = resolveFunction("value_equals", llvm::FunctionType::get(m_builder->getInt1Ty(), { valuePtr, valuePtr }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_greater() +{ + llvm::Type *valuePtr = m_valueDataType->getPointerTo(); + llvm::FunctionCallee callee = resolveFunction("value_greater", llvm::FunctionType::get(m_builder->getInt1Ty(), { valuePtr, valuePtr }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + +llvm::FunctionCallee LLVMFunctions::resolve_value_lower() +{ + llvm::Type *valuePtr = m_valueDataType->getPointerTo(); + llvm::FunctionCallee callee = resolveFunction("value_lower", llvm::FunctionType::get(m_builder->getInt1Ty(), { valuePtr, valuePtr }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + +llvm::FunctionCallee LLVMFunctions::resolve_list_clear() +{ + llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + return resolveFunction("list_clear", llvm::FunctionType::get(m_builder->getVoidTy(), { listPtr }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_list_remove() +{ + llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + return resolveFunction("list_remove", llvm::FunctionType::get(m_builder->getVoidTy(), { listPtr, m_builder->getInt64Ty() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_list_append_empty() +{ + llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + return resolveFunction("list_append_empty", llvm::FunctionType::get(m_valueDataType->getPointerTo(), { listPtr }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_list_insert_empty() +{ + llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + return resolveFunction("list_insert_empty", llvm::FunctionType::get(m_valueDataType->getPointerTo(), { listPtr, m_builder->getInt64Ty() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_list_data_ptr() +{ + llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + llvm::FunctionCallee callee = resolveFunction("list_data_ptr", llvm::FunctionType::get(m_valueDataType->getPointerTo()->getPointerTo(), { listPtr }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + +llvm::FunctionCallee LLVMFunctions::resolve_list_size_ptr() +{ + llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + llvm::FunctionCallee callee = resolveFunction("list_size_ptr", llvm::FunctionType::get(m_builder->getInt64Ty()->getPointerTo()->getPointerTo(), { listPtr }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + +llvm::FunctionCallee LLVMFunctions::resolve_list_alloc_size_ptr() +{ + llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + llvm::FunctionCallee callee = resolveFunction("list_alloc_size_ptr", llvm::FunctionType::get(m_builder->getInt64Ty()->getPointerTo()->getPointerTo(), { listPtr }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + +llvm::FunctionCallee LLVMFunctions::resolve_list_to_string() +{ + llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + llvm::FunctionCallee callee = resolveFunction("list_to_string", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), { pointerType }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + +llvm::FunctionCallee LLVMFunctions::resolve_llvm_random() +{ + llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + llvm::Type *valuePtr = m_valueDataType->getPointerTo(); + return resolveFunction("llvm_random", llvm::FunctionType::get(m_builder->getDoubleTy(), { pointerType, valuePtr, valuePtr }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_llvm_random_double() +{ + llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + 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::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)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_llvm_random_bool() +{ + llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(*m_ctx->llvmCtx()), 0); + return resolveFunction("llvm_random_bool", llvm::FunctionType::get(m_builder->getDoubleTy(), { pointerType, m_builder->getInt1Ty(), m_builder->getInt1Ty() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_string_pool_new() +{ + return resolveFunction("string_pool_new", llvm::FunctionType::get(m_stringPtrType->getPointerTo(), { m_builder->getInt1Ty() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_string_pool_free() +{ + return resolveFunction("string_pool_free", llvm::FunctionType::get(m_builder->getVoidTy(), { m_stringPtrType->getPointerTo() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_string_alloc() +{ + return resolveFunction("string_alloc", llvm::FunctionType::get(m_builder->getVoidTy(), { m_stringPtrType->getPointerTo(), m_builder->getInt64Ty() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_string_assign() +{ + return resolveFunction("string_assign", llvm::FunctionType::get(m_builder->getVoidTy(), { m_stringPtrType->getPointerTo(), m_stringPtrType->getPointerTo() }, false)); +} + +llvm::FunctionCallee LLVMFunctions::resolve_string_compare_case_sensitive() +{ + llvm::Type *stringPtr = m_stringPtrType->getPointerTo(); + llvm::FunctionCallee callee = resolveFunction("string_compare_case_sensitive", llvm::FunctionType::get(m_builder->getInt32Ty(), { stringPtr, stringPtr }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} + +llvm::FunctionCallee LLVMFunctions::resolve_string_compare_case_insensitive() +{ + llvm::Type *stringPtr = m_stringPtrType->getPointerTo(); + llvm::FunctionCallee callee = resolveFunction("string_compare_case_insensitive", llvm::FunctionType::get(m_builder->getInt32Ty(), { stringPtr, stringPtr }, false)); + llvm::Function *func = llvm::cast(callee.getCallee()); + func->addFnAttr(llvm::Attribute::ReadOnly); + return callee; +} diff --git a/src/engine/internal/llvm/llvmfunctions.h b/src/engine/internal/llvm/llvmfunctions.h new file mode 100644 index 000000000..2a257321f --- /dev/null +++ b/src/engine/internal/llvm/llvmfunctions.h @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +namespace libscratchcpp +{ + +class LLVMCompilerContext; + +class LLVMFunctions +{ + public: + LLVMFunctions(LLVMCompilerContext *ctx, llvm::IRBuilder<> *builder); + + llvm::FunctionCallee resolveFunction(const std::string name, llvm::FunctionType *type); + + llvm::FunctionCallee resolve_value_init(); + llvm::FunctionCallee resolve_value_free(); + llvm::FunctionCallee resolve_value_assign_long(); + llvm::FunctionCallee resolve_value_assign_double(); + llvm::FunctionCallee resolve_value_assign_bool(); + llvm::FunctionCallee resolve_value_assign_stringPtr(); + llvm::FunctionCallee resolve_value_assign_special(); + llvm::FunctionCallee resolve_value_assign_copy(); + llvm::FunctionCallee resolve_value_toDouble(); + llvm::FunctionCallee resolve_value_toBool(); + llvm::FunctionCallee resolve_value_toStringPtr(); + llvm::FunctionCallee resolve_value_doubleToStringPtr(); + llvm::FunctionCallee resolve_value_boolToStringPtr(); + llvm::FunctionCallee resolve_value_stringToDouble(); + llvm::FunctionCallee resolve_value_stringToBool(); + llvm::FunctionCallee resolve_value_equals(); + llvm::FunctionCallee resolve_value_greater(); + llvm::FunctionCallee resolve_value_lower(); + llvm::FunctionCallee resolve_list_clear(); + llvm::FunctionCallee resolve_list_remove(); + llvm::FunctionCallee resolve_list_append_empty(); + llvm::FunctionCallee resolve_list_insert_empty(); + llvm::FunctionCallee resolve_list_data_ptr(); + llvm::FunctionCallee resolve_list_size_ptr(); + llvm::FunctionCallee resolve_list_alloc_size_ptr(); + 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_bool(); + llvm::FunctionCallee resolve_string_pool_new(); + llvm::FunctionCallee resolve_string_pool_free(); + llvm::FunctionCallee resolve_string_alloc(); + llvm::FunctionCallee resolve_string_assign(); + llvm::FunctionCallee resolve_string_compare_case_sensitive(); + llvm::FunctionCallee resolve_string_compare_case_insensitive(); + + private: + LLVMCompilerContext *m_ctx = nullptr; + llvm::IRBuilder<> *m_builder = nullptr; + + llvm::StructType *m_stringPtrType = nullptr; + llvm::StructType *m_valueDataType = nullptr; +}; + +} // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvminstruction.h b/src/engine/internal/llvm/llvminstruction.h index 11c6ad316..e4fa059a3 100644 --- a/src/engine/internal/llvm/llvminstruction.h +++ b/src/engine/internal/llvm/llvminstruction.h @@ -10,7 +10,6 @@ namespace libscratchcpp { class BlockPrototype; -class LLVMLoopScope; struct LLVMInstruction { @@ -81,9 +80,8 @@ struct LLVMInstruction ProcedureArg }; - LLVMInstruction(Type type, LLVMLoopScope *loopScope, bool loopCondition) : + LLVMInstruction(Type type, bool loopCondition) : type(type), - loopScope(loopScope), loopCondition(loopCondition) { } @@ -98,8 +96,12 @@ struct LLVMInstruction List *workList = nullptr; // for lists BlockPrototype *procedurePrototype = nullptr; size_t procedureArgIndex = 0; - LLVMLoopScope *loopScope = nullptr; bool loopCondition = false; // whether the instruction is part of a loop condition + + // Linked list + LLVMInstruction *previous = nullptr; + LLVMInstruction *next = nullptr; + std::shared_ptr _next; // ensure items are not deleted }; } // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvminstructionlist.cpp b/src/engine/internal/llvm/llvminstructionlist.cpp new file mode 100644 index 000000000..61b8d6c38 --- /dev/null +++ b/src/engine/internal/llvm/llvminstructionlist.cpp @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "llvminstructionlist.h" +#include "llvminstruction.h" + +using namespace libscratchcpp; + +LLVMInstruction *LLVMInstructionList::first() const +{ + return m_first.get(); +} + +LLVMInstruction *LLVMInstructionList::last() const +{ + return m_last.get(); +} + +bool LLVMInstructionList::containsInstruction(LLVMInstruction *ins) const +{ + LLVMInstruction *ptr = m_first.get(); + + while (ptr) { + if (ptr == ins) + return true; + + ptr = ptr->next; + } + + return false; +} + +bool LLVMInstructionList::containsInstruction(std::function func) const +{ + LLVMInstruction *ptr = m_first.get(); + + while (ptr) { + if (func(ptr)) + return true; + + ptr = ptr->next; + } + + return false; +} + +void LLVMInstructionList::addInstruction(std::shared_ptr ins) +{ + if (!(m_first && m_last)) { + assert(!m_first && !m_last); + + m_first = ins; + m_last = ins; + + ins->previous = nullptr; + ins->next = nullptr; + ins->_next = nullptr; + } else { + m_last->next = ins.get(); + m_last->_next = ins; + ins->previous = m_last.get(); + m_last = ins; + } +} diff --git a/src/engine/internal/llvm/llvminstructionlist.h b/src/engine/internal/llvm/llvminstructionlist.h new file mode 100644 index 000000000..e7cadb9c4 --- /dev/null +++ b/src/engine/internal/llvm/llvminstructionlist.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +namespace libscratchcpp +{ + +struct LLVMInstruction; + +class LLVMInstructionList +{ + public: + LLVMInstructionList() = default; + LLVMInstructionList(const LLVMInstructionList &) = delete; + + LLVMInstruction *first() const; + LLVMInstruction *last() const; + + bool empty() const { return !first(); } + + bool containsInstruction(LLVMInstruction *ins) const; + bool containsInstruction(std::function func) const; + + void addInstruction(std::shared_ptr ins); + + private: + std::shared_ptr m_first; + std::shared_ptr m_last; +}; + +} // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmlistptr.h b/src/engine/internal/llvm/llvmlistptr.h index 99fc74a37..1dd42c60c 100644 --- a/src/engine/internal/llvm/llvmlistptr.h +++ b/src/engine/internal/llvm/llvmlistptr.h @@ -3,7 +3,6 @@ #pragma once #include -#include namespace llvm { @@ -15,7 +14,6 @@ class Value; namespace libscratchcpp { -class LLVMLoopScope; class LLVMInstruction; struct LLVMListPtr @@ -24,11 +22,6 @@ struct LLVMListPtr llvm::Value *dataPtr = nullptr; llvm::Value *sizePtr = nullptr; llvm::Value *allocatedSizePtr = nullptr; - llvm::Value *dataPtrDirty = nullptr; - Compiler::StaticType type = Compiler::StaticType::Unknown; - - // Used in build phase to check the type safety of lists in loops - std::unordered_map>> loopListWrites; // loop scope, write instructions }; } // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmloopscope.h b/src/engine/internal/llvm/llvmloopscope.h deleted file mode 100644 index 5e34647ca..000000000 --- a/src/engine/internal/llvm/llvmloopscope.h +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -namespace libscratchcpp -{ - -struct LLVMLoopScope -{ - bool containsYield = false; - LLVMLoopScope *parentScope = nullptr; - std::vector childScopes; -}; - -} // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmprocedure.h b/src/engine/internal/llvm/llvmprocedure.h deleted file mode 100644 index 7cb576794..000000000 --- a/src/engine/internal/llvm/llvmprocedure.h +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -namespace libscratchcpp -{ - -struct LLVMProcedure -{ - // TODO: Implement procedures - bool warp = false; -}; - -} // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmtypeanalyzer.cpp b/src/engine/internal/llvm/llvmtypeanalyzer.cpp new file mode 100644 index 000000000..3445c7834 --- /dev/null +++ b/src/engine/internal/llvm/llvmtypeanalyzer.cpp @@ -0,0 +1,565 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "llvmtypeanalyzer.h" +#include "llvminstruction.h" + +using namespace libscratchcpp; + +static const std::unordered_set + BEGIN_LOOP_INSTRUCTIONS = { LLVMInstruction::Type::BeginRepeatLoop, LLVMInstruction::Type::BeginWhileLoop, LLVMInstruction::Type::BeginRepeatUntilLoop }; + +static const std::unordered_set LIST_WRITE_INSTRUCTIONS = { LLVMInstruction::Type::AppendToList, LLVMInstruction::Type::InsertToList, LLVMInstruction::Type::ListReplace }; + +Compiler::StaticType LLVMTypeAnalyzer::variableType(const Variable *var, const LLVMInstruction *pos, Compiler::StaticType previousType) const +{ + InstructionSet visitedInstructions; + std::unordered_map listTypes; + return variableType(var, pos, previousType, listTypes, visitedInstructions); +} + +Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranch(const Variable *var, const LLVMInstruction *start, Compiler::StaticType previousType) const +{ + InstructionSet visitedInstructions; + return variableTypeAfterBranch(var, start, previousType, visitedInstructions); +} + +Compiler::StaticType LLVMTypeAnalyzer::listType(const List *list, const LLVMInstruction *pos, Compiler::StaticType previousType, bool isEmpty) const +{ + InstructionSet visitedInstructions; + std::unordered_map listTypes; + return listType(list, pos, previousType, isEmpty, listTypes, visitedInstructions); +} + +Compiler::StaticType LLVMTypeAnalyzer::listTypeAfterBranch(const List *list, const LLVMInstruction *start, Compiler::StaticType previousType, bool isEmpty) const +{ + bool write = false; // only used internally (the compiler doesn't need this) + InstructionSet visitedInstructions; + std::unordered_map listTypes; + return listTypeAfterBranch(list, start, previousType, isEmpty, nullptr, write, listTypes, visitedInstructions); +} + +Compiler::StaticType LLVMTypeAnalyzer::variableType( + const Variable *var, + const LLVMInstruction *pos, + Compiler::StaticType previousType, + std::unordered_map listTypes, + InstructionSet &visitedInstructions) const +{ + if (!var || !pos) + return Compiler::StaticType::Unknown; + + /* + * If the given instruction has already been processed, + * it means there's a case like this: + * x = x + * + * or this: + * x = y + * ... + * y = x + */ + if (visitedInstructions.find(pos) != visitedInstructions.cend()) { + // Circular dependencies are rare (and bad) so don't optimize them + return Compiler::StaticType::Unknown; + } + + visitedInstructions.insert(pos); + + // Check the last write operation before the instruction + const LLVMInstruction *ins = pos->previous; + const LLVMInstruction *write = nullptr; + std::pair firstBranch = { nullptr, 0 }; + std::pair firstElseBranch = { nullptr, 0 }; + const LLVMInstruction *ourBranch = nullptr; + int level = 0; + + while (ins) { + if (isLoopEnd(ins) || isIfEnd(ins)) + level++; + else if (isLoopStart(ins) || isIfStart(ins)) { + if (!ourBranch && level == 0) + ourBranch = ins; + + if (!firstBranch.first || level < firstBranch.second) + firstBranch = { ins, level }; + + level--; + } else if (isElse(ins)) { + // Skip if branch if coming from else + if (!firstElseBranch.first || level < firstElseBranch.second) + firstElseBranch = { ins, level }; + + ins = branchStart(ins); + continue; + } else if (isVariableWrite(ins, var) && !isWriteNoOp(ins)) { + if (level <= 0) { // ignore nested branches (they're handled by the branch analyzer) + write = ins; + break; + } + } + + ins = ins->previous; + } + + if (firstBranch.first) { + // Analyze the first branch and else branch + bool ignoreWriteAfterPos = (isIfStart(firstBranch.first) && firstBranch.first == ourBranch); + + if (write) + previousType = writeValueType(write, listTypes, visitedInstructions); // write operation overrides previous type + + Compiler::StaticType firstBranchType = previousType; + Compiler::StaticType elseBranchType = previousType; + + if (!ignoreWriteAfterPos) { + firstBranchType = variableTypeAfterBranch(var, firstBranch.first, previousType, visitedInstructions); + elseBranchType = variableTypeAfterBranch(var, firstElseBranch.first, previousType, visitedInstructions); + } + + if (typesMatch(firstBranchType, elseBranchType)) + return firstBranchType; + else + return Compiler::StaticType::Unknown; + } else if (write) { + // There wasn't any branch found, so we can just check the last write operation + return writeValueType(write, listTypes, visitedInstructions); + } + + // No write operation found + return previousType; +} + +Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranch(const Variable *var, const LLVMInstruction *start, Compiler::StaticType previousType, InstructionSet &visitedInstructions) const +{ + if (!var || !start) + return previousType; + + const LLVMInstruction *end = branchEnd(start); + + if (!end) + return Compiler::StaticType::Unknown; + + // Process the branch from end + bool write = false; // only used internally (the compiler doesn't need this) + std::unordered_map listTypes; + return variableTypeAfterBranchFromEnd(var, end, previousType, write, listTypes, visitedInstructions); +} + +Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranchFromEnd( + const Variable *var, + const LLVMInstruction *end, + Compiler::StaticType previousType, + bool &write, + std::unordered_map listTypes, + InstructionSet &visitedInstructions) const +{ + // Find the last write instruction + const LLVMInstruction *ins = end->previous; + bool typeMightReset = false; + + while (ins && !isLoopStart(ins) && !isIfStart(ins)) { + if (isLoopEnd(ins) || isIfEnd(ins) || isElse(ins)) { + // Process the nested loop or if statement + Compiler::StaticType ret = variableTypeAfterBranchFromEnd(var, ins, previousType, write, listTypes, visitedInstructions); + + if (typesMatch(ret, previousType)) { + if (write) + typeMightReset = true; + } else + return Compiler::StaticType::Unknown; + + ins = branchStart(ins); + + if (isElse(ins)) { + // Process if branch (the else branch is already processed) + ret = variableTypeAfterBranchFromEnd(var, ins, previousType, write, listTypes, visitedInstructions); + + if (typesMatch(ret, previousType)) { + if (write) { + if (typeMightReset) + return previousType; + + typeMightReset = true; + } + } else + return Compiler::StaticType::Unknown; + + ins = branchStart(ins); + } + } else if (isVariableWrite(ins, var) && !isWriteNoOp(ins)) { + // Variable write instruction + Compiler::StaticType writeType = writeValueType(ins, listTypes, visitedInstructions); + write = true; + + if (typesMatch(writeType, previousType)) + return writeType; + else + return typeMightReset ? Compiler::StaticType::Unknown : writeType; + } + + ins = ins->previous; + } + + write = false; + return previousType; +} + +Compiler::StaticType LLVMTypeAnalyzer::listType( + const List *list, + const LLVMInstruction *pos, + Compiler::StaticType previousType, + bool isEmpty, + std::unordered_map listTypes, + InstructionSet &visitedInstructions) const +{ + if (!list || !pos) + return Compiler::StaticType::Unknown; + + /* + * If the given instruction has already been processed, + * it means there's a circular dependency with an unknown type. + * See variableType() + */ + if (visitedInstructions.find(pos) != visitedInstructions.cend()) { + // Circular dependencies are rare (and bad) so don't optimize them + // TODO: The (previousType != Compiler::StaticType::Unknown) case isn't handled in tests + // Add a test case for it if you find it... + assert(previousType == Compiler::StaticType::Unknown); + return /*Compiler::StaticType::Unknown*/ previousType; + } + + visitedInstructions.insert(pos); + listTypes[list] = previousType; + + const LLVMInstruction *ins = pos; + const LLVMInstruction *previous = nullptr; + std::pair firstBranch = { nullptr, 0 }; + std::pair lastClear = { nullptr, 0 }; + int level = 0; + + // Find a start instruction (list clear in the top level or the first instruction) + while (ins) { + if (isLoopEnd(ins) || isIfEnd(ins)) + level++; + else if (isLoopStart(ins) || isIfStart(ins) || isElse(ins)) { + if (!isElse(ins)) + level--; + + if (!firstBranch.first || level < firstBranch.second) + firstBranch = { ins, level }; + } else if (isListClear(ins, list)) { + if (!lastClear.first || level < lastClear.second) + lastClear = { ins, level }; + } + + previous = ins; + ins = ins->previous; + } + + if (firstBranch.first) { + assert(firstBranch.second == level); + + // The first branch must be above the query point level + if (firstBranch.second == 0) + firstBranch.first = nullptr; + } + + // Clear must be in the top level + if (lastClear.second != level) + lastClear.first = nullptr; + + if (lastClear.first) { + ins = lastClear.first; + isEmpty = true; + } else + ins = previous; + + // Process from the start instruction + while (ins && ins != pos) { + if (isLoopStart(ins) || isIfStart(ins)) { + do { + bool write; + Compiler::StaticType type = listTypeAfterBranch(list, ins, previousType, isEmpty, pos, write, listTypes, visitedInstructions); + + // If this branch contains the query point, return the final type + if (ins == firstBranch.first) + return type; + + // If there was a write, the list is no longer empty + if (write) { + isEmpty = false; + + // The write could change the type + if (!typesMatch(type, previousType)) + previousType = type; + } + + // Skip the branch + ins = branchEnd(ins); + } while (isElse(ins)); // handle else branch + } else if (isListWrite(ins, list)) { + // List write instruction + Compiler::StaticType writeType = writeValueType(ins, listTypes, visitedInstructions); + + if (!handleListWrite(writeType, previousType, isEmpty)) + return Compiler::StaticType::Unknown; + } + + ins = ins->next; + } + + assert(ins); + return previousType; +} + +Compiler::StaticType LLVMTypeAnalyzer::listTypeAfterBranch( + const List *list, + const LLVMInstruction *start, + Compiler::StaticType previousType, + bool isEmpty, + const LLVMInstruction *query, + bool &write, + std::unordered_map listTypes, + InstructionSet &visitedInstructions) const +{ + write = false; + + if (!list || !start) + return previousType; + + assert(isLoopStart(start) || isIfStart(start) || isElse(start)); + + bool isLoop = isLoopStart(start); + bool clearBeforeQueryPoint = false; + const LLVMInstruction *ins = start->next; + + while (ins && !(isLoopEnd(ins) || isIfEnd(ins) || isElse(ins))) { + if (isLoopStart(ins) || isIfStart(ins)) { + do { + Compiler::StaticType type = listTypeAfterBranch(list, ins, previousType, isEmpty, query, write, listTypes, visitedInstructions); + + // If there was a write, the list is no longer empty + if (write) { + isEmpty = false; + + // The write could change the type + if (!typesMatch(type, previousType)) + return Compiler::StaticType::Unknown; + } + + // Skip the branch + ins = branchEnd(ins); + } while (isElse(ins)); // handle else branch + } else if (isListWrite(ins, list)) { + // List write instruction + Compiler::StaticType writeType = writeValueType(ins, listTypes, visitedInstructions); + write = true; + + if (!handleListWrite(writeType, previousType, isEmpty)) + return Compiler::StaticType::Unknown; + } else if (isListClear(ins, list)) { + // The list is now empty + isEmpty = true; + clearBeforeQueryPoint = true; + write = false; // the write variable is only used to check if the list is still empty + } else if (ins == query) { + if (!isLoop || clearBeforeQueryPoint) + break; + + clearBeforeQueryPoint = false; + } + + ins = ins->next; + } + + assert(ins); + return previousType; +} + +bool LLVMTypeAnalyzer::handleListWrite(Compiler::StaticType writeType, Compiler::StaticType &previousType, bool &isEmpty) const +{ + if (isEmpty) { + // In empty lists, writes of the same type determine the final type + // This is the first write found, it might determine the final type + previousType = writeType; + + // The list is no longer empty + isEmpty = false; + } else if (!typesMatch(writeType, previousType)) { + // For non-empty lists, we can just check the write type + return false; + } + + return true; +} + +const LLVMInstruction *LLVMTypeAnalyzer::branchEnd(const LLVMInstruction *start) const +{ + assert(start); + assert(isLoopStart(start) || isIfStart(start) || isElse(start)); + + // Find loop/if statement end or else branch + LLVMInstruction *ins = start->next; + int level = 0; + + while (ins && !((isLoopEnd(ins) || isIfEnd(ins) || isElse(ins)) && level == 0)) { + if (isLoopStart(ins) || isIfStart(ins)) + level++; + else if (isLoopEnd(ins) || isIfEnd(ins)) { + assert(level > 0); + level--; + } + + ins = ins->next; + } + + assert(ins); + return ins; +} + +const LLVMInstruction *LLVMTypeAnalyzer::branchStart(const LLVMInstruction *end) const +{ + assert(end); + assert(isLoopEnd(end) || isIfEnd(end) || isElse(end)); + + // Find loop/if statement/else branch start + LLVMInstruction *ins = end->previous; + int level = 0; + + while (ins && !((isLoopStart(ins) || isIfStart(ins) || isElse(ins)) && level == 0)) { + if (isLoopStart(ins) || isIfStart(ins)) { + assert(level > 0); + level--; + } + + if (isLoopEnd(ins) || isIfEnd(ins)) + level++; + + ins = ins->previous; + }; + + assert(ins); + return ins; +} + +bool LLVMTypeAnalyzer::isLoopStart(const LLVMInstruction *ins) const +{ + return (BEGIN_LOOP_INSTRUCTIONS.find(ins->type) != BEGIN_LOOP_INSTRUCTIONS.cend()); +} + +bool LLVMTypeAnalyzer::isLoopEnd(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::EndLoop); +} + +bool LLVMTypeAnalyzer::isIfStart(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::BeginIf); +} + +bool LLVMTypeAnalyzer::isElse(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::BeginElse); +} + +bool LLVMTypeAnalyzer::isIfEnd(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::EndIf); +} + +bool LLVMTypeAnalyzer::isVariableRead(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::ReadVariable); +} + +bool LLVMTypeAnalyzer::isVariableWrite(const LLVMInstruction *ins, const Variable *var) const +{ + return (ins->type == LLVMInstruction::Type::WriteVariable && ins->workVariable == var); +} + +bool LLVMTypeAnalyzer::isListRead(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::GetListItem); +} + +bool LLVMTypeAnalyzer::isListWrite(const LLVMInstruction *ins, const List *list) const +{ + return (LIST_WRITE_INSTRUCTIONS.find(ins->type) != LIST_WRITE_INSTRUCTIONS.cend() && ins->workList == list); +} + +bool LLVMTypeAnalyzer::isListClear(const LLVMInstruction *ins, const List *list) const +{ + return (ins->type == LLVMInstruction::Type::ClearList && ins->workList == list); +} + +Compiler::StaticType LLVMTypeAnalyzer::optimizeRegisterType(LLVMRegister *reg) const +{ + // TODO: Move this method out if it's used in LLVMCodeBuilder too + assert(reg); + + Compiler::StaticType ret = reg->type(); + + // Optimize string constants that represent numbers + if (reg->isConst() && reg->type() == Compiler::StaticType::String && reg->constValue().isValidNumber()) + ret = Compiler::StaticType::Number; + + return ret; +} + +bool LLVMTypeAnalyzer::isWriteNoOp(const LLVMInstruction *ins) const +{ + assert(ins); + assert(!ins->args.empty()); + const auto arg = ins->args.back().second; // value is always the last argument in variable/list write instructions + + if (arg->instruction) { + // TODO: Handle list item + if (isVariableRead(arg->instruction.get())) { + // Self-assignment is a no-op + return (ins->workVariable == arg->instruction->workVariable); + } + } + + return false; +} + +Compiler::StaticType LLVMTypeAnalyzer::writeValueType(const LLVMInstruction *ins, std::unordered_map listTypes, InstructionSet &visitedInstructions) const +{ + assert(ins); + assert(!ins->args.empty()); + const auto arg = ins->args.back().second; // value is always the last argument in variable/list write instructions + + if (arg->instruction) { + // TODO: Handle list item + if (isVariableRead(arg->instruction.get())) { + // If this is a variable read instruction, recursively get the variable type + return variableType(arg->instruction->workVariable, arg->instruction.get(), Compiler::StaticType::Unknown, listTypes, visitedInstructions); + } else if (isListRead(arg->instruction.get())) { + // If this is a list read instruction, recursively get the list type + Compiler::StaticType type = Compiler::StaticType::Unknown; + auto it = listTypes.find(arg->instruction->workList); + + if (it != listTypes.cend()) + type = it->second; + + // NOTE: The isEmpty parameter is useless here + return listType(arg->instruction->workList, arg->instruction.get(), type, false, listTypes, visitedInstructions); + } else { + // The write argument already has the instruction return type + return optimizeRegisterType(arg); + } + } else + return optimizeRegisterType(arg); +} + +bool LLVMTypeAnalyzer::typesMatch(Compiler::StaticType a, Compiler::StaticType b) const +{ + // Equal unknown types are not considered a match + return (a == b) && (a != Compiler::StaticType::Unknown); +} + +bool LLVMTypeAnalyzer:: + writeTypesMatch(const LLVMInstruction *ins, Compiler::StaticType expectedType, std::unordered_map listTypes, InstructionSet &visitedInstructions) const +{ + return typesMatch(writeValueType(ins, listTypes, visitedInstructions), expectedType); +} diff --git a/src/engine/internal/llvm/llvmtypeanalyzer.h b/src/engine/internal/llvm/llvmtypeanalyzer.h new file mode 100644 index 000000000..a1d553771 --- /dev/null +++ b/src/engine/internal/llvm/llvmtypeanalyzer.h @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +namespace libscratchcpp +{ + +struct LLVMInstruction; +struct LLVMRegister; + +class LLVMTypeAnalyzer +{ + public: + Compiler::StaticType variableType(const Variable *var, const LLVMInstruction *pos, Compiler::StaticType previousType) const; + Compiler::StaticType variableTypeAfterBranch(const Variable *var, const LLVMInstruction *start, Compiler::StaticType previousType) const; + + Compiler::StaticType listType(const List *list, const LLVMInstruction *pos, Compiler::StaticType previousType, bool isEmpty) const; + Compiler::StaticType listTypeAfterBranch(const List *list, const LLVMInstruction *start, Compiler::StaticType previousType, bool isEmpty) const; + + private: + using InstructionSet = std::unordered_set; + + Compiler::StaticType variableType( + const Variable *var, + const LLVMInstruction *pos, + Compiler::StaticType previousType, + std::unordered_map listTypes, + InstructionSet &visitedInstructions) const; + + Compiler::StaticType variableTypeAfterBranch(const Variable *var, const LLVMInstruction *start, Compiler::StaticType previousType, InstructionSet &visitedInstructions) const; + + Compiler::StaticType variableTypeAfterBranchFromEnd( + const Variable *var, + const LLVMInstruction *end, + Compiler::StaticType previousType, + bool &write, + std::unordered_map listTypes, + InstructionSet &visitedInstructions) const; + + Compiler::StaticType listType( + const List *list, + const LLVMInstruction *pos, + Compiler::StaticType previousType, + bool isEmpty, + std::unordered_map listTypes, + InstructionSet &visitedInstructions) const; + + Compiler::StaticType listTypeAfterBranch( + const List *list, + const LLVMInstruction *start, + Compiler::StaticType previousType, + bool isEmpty, + const LLVMInstruction *query, + bool &write, + std::unordered_map listTypes, + InstructionSet &visitedInstructions) const; + + bool handleListWrite(Compiler::StaticType writeType, Compiler::StaticType &previousType, bool &isEmpty) const; + + const LLVMInstruction *branchEnd(const LLVMInstruction *start) const; + const LLVMInstruction *branchStart(const LLVMInstruction *end) const; + + bool isLoopStart(const LLVMInstruction *ins) const; + bool isLoopEnd(const LLVMInstruction *ins) const; + bool isIfStart(const LLVMInstruction *ins) const; + bool isElse(const LLVMInstruction *ins) const; + bool isIfEnd(const LLVMInstruction *ins) const; + + bool isVariableRead(const LLVMInstruction *ins) const; + bool isVariableWrite(const LLVMInstruction *ins, const Variable *var) const; + + bool isListRead(const LLVMInstruction *ins) const; + bool isListWrite(const LLVMInstruction *ins, const List *list) const; + bool isListClear(const LLVMInstruction *ins, const List *list) const; + + Compiler::StaticType optimizeRegisterType(LLVMRegister *reg) const; + bool isWriteNoOp(const LLVMInstruction *ins) const; + Compiler::StaticType writeValueType(const LLVMInstruction *ins, std::unordered_map listTypes, InstructionSet &visitedInstructions) const; + bool typesMatch(Compiler::StaticType a, Compiler::StaticType b) const; + bool + writeTypesMatch(const LLVMInstruction *ins, Compiler::StaticType expectedType, std::unordered_map listTypes, InstructionSet &visitedInstructions) const; +}; + +} // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmtypes.cpp b/src/engine/internal/llvm/llvmtypes.cpp index 8c4e1e660..8d46440a1 100644 --- a/src/engine/internal/llvm/llvmtypes.cpp +++ b/src/engine/internal/llvm/llvmtypes.cpp @@ -6,12 +6,10 @@ using namespace libscratchcpp; -llvm::StructType *LLVMTypes::createValueDataType(llvm::IRBuilder<> *builder) +llvm::StructType *LLVMTypes::createValueDataType(llvm::LLVMContext &ctx) { - llvm::LLVMContext &ctx = builder->getContext(); - // Create the ValueData struct - llvm::Type *unionType = builder->getInt64Ty(); // 64 bits is the largest size + llvm::Type *unionType = llvm::Type::getInt64Ty(ctx); // 64 bits is the largest size llvm::Type *valueType = llvm::Type::getInt32Ty(ctx); // Assuming ValueType is a 32-bit enum llvm::Type *padding = llvm::Type::getInt32Ty(ctx); // Padding for alignment @@ -22,10 +20,8 @@ llvm::StructType *LLVMTypes::createValueDataType(llvm::IRBuilder<> *builder) return ret; } -llvm::StructType *LLVMTypes::createStringPtrType(llvm::IRBuilder<> *builder) +llvm::StructType *LLVMTypes::createStringPtrType(llvm::LLVMContext &ctx) { - llvm::LLVMContext &ctx = builder->getContext(); - // Create the StringPtr struct llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(ctx), 0); llvm::Type *sizeType = llvm::Type::getInt64Ty(ctx); diff --git a/src/engine/internal/llvm/llvmtypes.h b/src/engine/internal/llvm/llvmtypes.h index 61c5f401f..cbfefa601 100644 --- a/src/engine/internal/llvm/llvmtypes.h +++ b/src/engine/internal/llvm/llvmtypes.h @@ -17,8 +17,8 @@ namespace libscratchcpp class LLVMTypes { public: - static llvm::StructType *createValueDataType(llvm::IRBuilder<> *builder); - static llvm::StructType *createStringPtrType(llvm::IRBuilder<> *builder); + static llvm::StructType *createValueDataType(llvm::LLVMContext &ctx); + static llvm::StructType *createStringPtrType(llvm::LLVMContext &ctx); }; } // namespace libscratchcpp diff --git a/src/engine/internal/llvm/llvmvariableptr.h b/src/engine/internal/llvm/llvmvariableptr.h index 88ddcc03f..2e5b3d010 100644 --- a/src/engine/internal/llvm/llvmvariableptr.h +++ b/src/engine/internal/llvm/llvmvariableptr.h @@ -3,7 +3,6 @@ #pragma once #include -#include namespace llvm { @@ -15,19 +14,14 @@ class Value; namespace libscratchcpp { -class LLVMLoopScope; class LLVMInstruction; struct LLVMVariablePtr { llvm::Value *stackPtr = nullptr; llvm::Value *heapPtr = nullptr; - Compiler::StaticType type = Compiler::StaticType::Unknown; bool onStack = false; bool changed = false; - - // Used in build phase to check the type safety of variables in loops - std::unordered_map>> loopVariableWrites; // loop scope, write instructions }; } // namespace libscratchcpp diff --git a/src/scratch/list_functions.cpp b/src/scratch/list_functions.cpp index 948e3e10e..5ef5e681e 100644 --- a/src/scratch/list_functions.cpp +++ b/src/scratch/list_functions.cpp @@ -38,6 +38,11 @@ extern "C" return list->data(); } + ValueData *const *list_data_ptr(List *list) + { + return list->dataPtr(); + } + size_t *list_size_ptr(List *list) { return list->sizePtr(); diff --git a/src/scratch/list_functions.h b/src/scratch/list_functions.h index 7080281c9..f68374194 100644 --- a/src/scratch/list_functions.h +++ b/src/scratch/list_functions.h @@ -21,6 +21,7 @@ extern "C" ValueData *list_get_item(List *list, size_t index); ValueData *list_data(List *list); + ValueData *const *list_data_ptr(List *list); size_t *list_size_ptr(List *list); const size_t *list_alloc_size_ptr(List *list); size_t list_size(List *list); diff --git a/test/compiler/compiler_test.cpp b/test/compiler/compiler_test.cpp index 87d40c60e..55d8d161e 100644 --- a/test/compiler/compiler_test.cpp +++ b/test/compiler/compiler_test.cpp @@ -50,7 +50,7 @@ class CompilerTest : public testing::Test { ASSERT_EQ(compiler->block(), nullptr); EXPECT_CALL(m_builderFactory, create(m_ctx.get(), procedurePrototype, codeType)).WillOnce(Return(m_builder)); - EXPECT_CALL(*m_builder, finalize()).WillOnce(Return(m_code)); + EXPECT_CALL(*m_builder, build()).WillOnce(Return(m_code)); ASSERT_EQ(compiler->compile(block, codeType), m_code); ASSERT_EQ(compiler->block(), nullptr); } diff --git a/test/engine/engine_test.cpp b/test/engine/engine_test.cpp index c14fb6d48..cf79f5949 100644 --- a/test/engine/engine_test.cpp +++ b/test/engine/engine_test.cpp @@ -396,7 +396,7 @@ TEST(EngineTest, Fps) ASSERT_EQ(engine.fps(), 250); } -/*TEST(EngineTest, FpsProject) +TEST(EngineTest, FpsProject) { Project p("2_frames.sb3"); ASSERT_TRUE(p.load()); @@ -466,7 +466,7 @@ TEST(EngineTest, Fps) EXPECT_CALL(clock, sleep).Times(0); EXPECT_CALL(redrawMock, redraw()); p.run(); -}*/ +} TEST(EngineTest, TurboModeEnabled) { @@ -480,7 +480,7 @@ TEST(EngineTest, TurboModeEnabled) ASSERT_FALSE(engine.turboModeEnabled()); } -/*TEST(EngineTest, ExecutionOrder) +TEST(EngineTest, ExecutionOrder) { Project p("execution_order.sb3"); ASSERT_TRUE(p.load()); @@ -509,7 +509,7 @@ TEST(EngineTest, TurboModeEnabled) ASSERT_EQ(Value((*list)[10]).toString(), "Sprite1 2 msg"); ASSERT_EQ(Value((*list)[11]).toString(), "Sprite1 3 msg"); ASSERT_EQ(Value((*list)[12]).toString(), "Stage msg"); -}*/ +} TEST(EngineTest, KeyState) { @@ -1702,7 +1702,7 @@ void questionFunction(const std::string &) { } -/*TEST(EngineTest, Clones) +TEST(EngineTest, Clones) { Project p("clones.sb3"); ASSERT_TRUE(p.load()); @@ -1861,9 +1861,9 @@ TEST(EngineTest, BroadcastsProject) ASSERT_EQ(GET_VAR(stage, "test4")->value().toInt(), 10); ASSERT_VAR(stage, "test5"); ASSERT_EQ(GET_VAR(stage, "test5")->value().toString(), "2 2 0 0"); -}*/ +} -/*TEST(EngineTest, StopAll) +TEST(EngineTest, StopAll) { Project p("stop_all.sb3"); ASSERT_TRUE(p.load()); @@ -1877,7 +1877,7 @@ TEST(EngineTest, BroadcastsProject) ASSERT_VAR(stage, "i"); ASSERT_EQ(GET_VAR(stage, "i")->value().toInt(), 11); ASSERT_FALSE(engine->isRunning()); -}*/ +} TEST(EngineTest, StopAllBypass) { @@ -2064,7 +2064,7 @@ TEST(EngineTest, NoRefreshWhenCallingRunningBroadcast) ASSERT_TRUE(GET_VAR(stage, "passed2")->value().toBool()); } -/*TEST(EngineTest, NoStopWhenCallingRunningBroadcastFromCustomBlock) +TEST(EngineTest, NoStopWhenCallingRunningBroadcastFromCustomBlock) { // Regtest for #257 Project p("regtest_projects/277_custom_block_call_running_broadcast_stop.sb3"); @@ -2081,7 +2081,7 @@ TEST(EngineTest, NoRefreshWhenCallingRunningBroadcast) ASSERT_VAR(stage, "passed2"); ASSERT_TRUE(GET_VAR(stage, "passed2")->value().toBool()); -}*/ +} TEST(EngineTest, ResetRunningHats) { @@ -2238,7 +2238,7 @@ TEST(EngineTest, DuplicateVariableOrListIDs) ASSERT_TRUE(GET_VAR(stage, "passed")->value().toBool()); } -/*TEST(EngineTest, BroadcastStopsWaitBlocks) +TEST(EngineTest, BroadcastStopsWaitBlocks) { // Regtest for #563 Project p("regtest_projects/563_broadcast_stops_wait_blocks.sb3"); @@ -2272,4 +2272,4 @@ TEST(EngineTest, BroadcastAndWaitCaseInsensitive) ASSERT_VAR(stage, "passed"); ASSERT_TRUE(GET_VAR(stage, "passed")->value().toBool()); -}*/ +} diff --git a/test/llvm/CMakeLists.txt b/test/llvm/CMakeLists.txt index abfc4d982..80ac82f6d 100644 --- a/test/llvm/CMakeLists.txt +++ b/test/llvm/CMakeLists.txt @@ -16,9 +16,50 @@ target_link_libraries( add_executable( llvm_test main.cpp + llvmtestutils.cpp + llvmtestutils.h llvmexecutioncontext_test.cpp llvmexecutablecode_test.cpp llvmcodebuilder_test.cpp + llvminstructionlist_test.cpp + operators/equal_comparison_test.cpp + operators/greater_than_test.cpp + operators/less_than_test.cpp + operators/math/add_test.cpp + operators/math/subtract_test.cpp + operators/math/multiply_test.cpp + operators/math/divide_test.cpp + operators/math/random_test.cpp + operators/math/random_int_test.cpp + operators/math/mod_test.cpp + operators/math/abs_test.cpp + operators/math/round_test.cpp + operators/math/floor_test.cpp + operators/math/ceil_test.cpp + operators/math/sqrt_test.cpp + operators/math/sin_test.cpp + operators/math/cos_test.cpp + operators/math/tan_test.cpp + operators/math/asin_test.cpp + operators/math/acos_test.cpp + operators/math/atan_test.cpp + operators/math/ln_test.cpp + operators/math/log10_test.cpp + operators/math/exp_test.cpp + operators/math/exp10_test.cpp + operators/logic/and_test.cpp + operators/logic/or_test.cpp + operators/logic/not_test.cpp + operators/string/string_equal_case_sensitive_test.cpp + operators/string/string_equal_case_insensitive_test.cpp + operators/string/string_concat_test.cpp + operators/string/string_char_test.cpp + operators/string/string_length_test.cpp + type_analyzer/variabletypeafterbranch_test.cpp + type_analyzer/variabletype_test.cpp + type_analyzer/listtypeafterbranch_test.cpp + type_analyzer/listtype_test.cpp + type_analyzer/mixed_test.cpp ) target_link_libraries( diff --git a/test/llvm/llvmcodebuilder_test.cpp b/test/llvm/llvmcodebuilder_test.cpp index f32ad8328..69a24e345 100644 --- a/test/llvm/llvmcodebuilder_test.cpp +++ b/test/llvm/llvmcodebuilder_test.cpp @@ -16,7 +16,7 @@ #include #include -#include "testfunctions.h" +#include "llvmtestutils.h" using namespace libscratchcpp; @@ -26,422 +26,7 @@ using ::testing::Eq; class LLVMCodeBuilderTest : public testing::Test { public: - enum class OpType - { - Add, - Sub, - Mul, - Div, - Random, - RandomInt, - CmpEQ, - CmpGT, - CmpLT, - StrCmpEQCS, - StrCmpEQCI, - And, - Or, - Not, - Mod, - Round, - Abs, - Floor, - Ceil, - Sqrt, - Sin, - Cos, - Tan, - Asin, - Acos, - Atan, - Ln, - Log10, - Exp, - Exp10, - StringConcat, - StringChar, - StringLength - }; - - void SetUp() override - { - test_function(nullptr, nullptr, nullptr, nullptr, nullptr); // force dependency - } - - void createBuilder(Target *target, BlockPrototype *procedurePrototype, Compiler::CodeType codeType = Compiler::CodeType::Script) - { - if (m_contexts.find(target) == m_contexts.cend() || !target) - m_contexts[target] = std::make_shared(&m_engine, target); - - m_contextList.push_back(m_contexts[target]); - m_builder = std::make_unique(m_contexts[target].get(), procedurePrototype, codeType); - } - - void createBuilder(Target *target, bool warp) - { - m_procedurePrototype = std::make_shared("test"); - m_procedurePrototype->setWarp(warp); - createBuilder(target, m_procedurePrototype.get()); - } - - void createReporterBuilder(Target *target) { createBuilder(target, nullptr, Compiler::CodeType::Reporter); } - - void createPredicateBuilder(Target *target) { createBuilder(target, nullptr, Compiler::CodeType::HatPredicate); } - - void createBuilder(bool warp) { createBuilder(nullptr, warp); } - - CompilerValue *callConstFuncForType(ValueType type, CompilerValue *arg) - { - switch (type) { - case ValueType::Number: - return m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }, { arg }); - - case ValueType::Bool: - return m_builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { arg }); - - case ValueType::String: - return m_builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String }, { arg }); - - default: - EXPECT_TRUE(false); - return nullptr; - } - } - - CompilerValue *addOp(OpType type, CompilerValue *arg1, CompilerValue *arg2) - { - switch (type) { - case OpType::Add: - return m_builder->createAdd(arg1, arg2); - - case OpType::Sub: - return m_builder->createSub(arg1, arg2); - - case OpType::Mul: - return m_builder->createMul(arg1, arg2); - - case OpType::Div: - return m_builder->createDiv(arg1, arg2); - - case OpType::Random: - return m_builder->createRandom(arg1, arg2); - - case OpType::RandomInt: - return m_builder->createRandomInt(arg1, arg2); - - case OpType::CmpEQ: - return m_builder->createCmpEQ(arg1, arg2); - - case OpType::CmpGT: - return m_builder->createCmpGT(arg1, arg2); - - case OpType::CmpLT: - return m_builder->createCmpLT(arg1, arg2); - - case OpType::StrCmpEQCS: - return m_builder->createStrCmpEQ(arg1, arg2, true); - - case OpType::StrCmpEQCI: - return m_builder->createStrCmpEQ(arg1, arg2, false); - - case OpType::And: - return m_builder->createAnd(arg1, arg2); - - case OpType::Or: - return m_builder->createOr(arg1, arg2); - - case OpType::Mod: - return m_builder->createMod(arg1, arg2); - - case OpType::StringConcat: - return m_builder->createStringConcat(arg1, arg2); - - case OpType::StringChar: - return m_builder->addStringChar(arg1, arg2); - - default: - EXPECT_TRUE(false); - return nullptr; - } - } - - CompilerValue *addOp(OpType type, CompilerValue *arg) - { - switch (type) { - case OpType::Not: - return m_builder->createNot(arg); - - case OpType::Round: - return m_builder->createRound(arg); - - case OpType::Abs: - return m_builder->createAbs(arg); - - case OpType::Floor: - return m_builder->createFloor(arg); - - case OpType::Ceil: - return m_builder->createCeil(arg); - - case OpType::Sqrt: - return m_builder->createSqrt(arg); - - case OpType::Sin: - return m_builder->createSin(arg); - - case OpType::Cos: - return m_builder->createCos(arg); - - case OpType::Tan: - return m_builder->createTan(arg); - - case OpType::Asin: - return m_builder->createAsin(arg); - - case OpType::Acos: - return m_builder->createAcos(arg); - - case OpType::Atan: - return m_builder->createAtan(arg); - - case OpType::Ln: - return m_builder->createLn(arg); - - case OpType::Log10: - return m_builder->createLog10(arg); - - case OpType::Exp: - return m_builder->createExp(arg); - - case OpType::Exp10: - return m_builder->createExp10(arg); - - case OpType::StringLength: - return m_builder->addStringLength(arg); - - default: - EXPECT_TRUE(false); - return nullptr; - } - } - - Value doOp(OpType type, const Value &v1, const Value &v2) - { - switch (type) { - case OpType::Add: - return v1 + v2; - - case OpType::Sub: - return v1 - v2; - - case OpType::Mul: - return v1 * v2; - - case OpType::Div: - return v1 / v2; - - case OpType::Random: { - const double sum = v1.toDouble() + v2.toDouble(); - - if (std::isnan(sum) || std::isinf(sum)) - return sum; - - return v1.isInt() && v2.isInt() ? m_rng.randint(v1.toLong(), v2.toLong()) : m_rng.randintDouble(v1.toDouble(), v2.toDouble()); - } - - case OpType::RandomInt: - return m_rng.randint(v1.toLong(), v2.toLong()); - - case OpType::CmpEQ: - return v1 == v2; - - case OpType::CmpGT: - return v1 > v2; - - case OpType::CmpLT: - return v1 < v2; - - case OpType::StrCmpEQCS: - return string_compare_case_sensitive(value_toStringPtr(&v1.data()), value_toStringPtr(&v2.data())) == 0; - - case OpType::StrCmpEQCI: - return string_compare_case_insensitive(value_toStringPtr(&v1.data()), value_toStringPtr(&v2.data())) == 0; - - case OpType::And: - return v1.toBool() && v2.toBool(); - - case OpType::Or: - return v1.toBool() || v2.toBool(); - - case OpType::Mod: - return v1 % v2; - - case OpType::StringConcat: { - const StringPtr *string1 = value_toStringPtr(&v1.data()); - const StringPtr *string2 = value_toStringPtr(&v2.data()); - StringPtr *result = string_pool_new(true); - - result->size = string1->size + string2->size; - string_alloc(result, result->size); - memcpy(result->data, string1->data, string1->size * sizeof(typeof(*string1->data))); - memcpy(result->data + string1->size, string2->data, (string2->size + 1) * sizeof(typeof(*string2->data))); // +1: null-terminate - - Value ret; - value_assign_stringPtr(&ret.data(), result); - return ret; - } - - case OpType::StringChar: { - const StringPtr *string = value_toStringPtr(&v1.data()); - const double index = v2.toDouble(); - const bool inRange = (index >= 0 && index < string->size); - StringPtr *result = string_pool_new(true); - - string_alloc(result, 1); - result->data[0] = inRange ? string->data[static_cast(index)] : u'\0'; - result->data[1] = u'\0'; - result->size = inRange; - - Value ret; - value_assign_stringPtr(&ret.data(), result); - return ret; - } - - default: - EXPECT_TRUE(false); - return Value(); - } - } - - Value doOp(OpType type, const Value &v) - { - switch (type) { - case OpType::Not: - return !v.toBool(); - - case OpType::StringLength: - return v.toUtf16().size(); - - default: - EXPECT_TRUE(false); - return Value(); - } - } - - void runOpTestCommon(OpType type, const Value &v1, const Value &v2) - { - createBuilder(true); - - CompilerValue *arg1 = m_builder->addConstValue(v1); - CompilerValue *arg2 = m_builder->addConstValue(v2); - CompilerValue *ret = addOp(type, arg1, arg2); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { ret }); - - arg1 = m_builder->addConstValue(v1); - arg1 = callConstFuncForType(v1.type(), arg1); - arg2 = m_builder->addConstValue(v2); - arg2 = callConstFuncForType(v2.type(), arg2); - ret = addOp(type, arg1, arg2); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { ret }); - - auto code = m_builder->finalize(); - Script script(&m_target, nullptr, nullptr); - script.setCode(code); - Thread thread(&m_target, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - ctx->setRng(&m_rng); - - testing::internal::CaptureStdout(); - code->run(ctx.get()); - } - - void checkOpTest(const Value &v1, const Value &v2, const std::string &expected) - { - const std::string quotes1 = v1.isString() ? "\"" : ""; - const std::string quotes2 = v2.isString() ? "\"" : ""; - ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; - } - - void runOpTest(OpType type, const Value &v1, const Value &v2, const Value &expected) - { - std::string str = expected.toString(); - runOpTestCommon(type, v1, v2); - checkOpTest(v1, v2, str + '\n' + str + '\n'); - }; - - void runOpTest(OpType type, const Value &v1, const Value &v2) - { - runOpTestCommon(type, v1, v2); - std::string str = doOp(type, v1, v2).toString() + '\n'; - std::string expected = str + str; - checkOpTest(v1, v2, expected); - }; - - void runOpTest(OpType type, const Value &v) - { - createBuilder(true); - - CompilerValue *arg = m_builder->addConstValue(v); - CompilerValue *ret = addOp(type, arg); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { ret }); - - arg = m_builder->addConstValue(v); - arg = callConstFuncForType(v.type(), arg); - ret = addOp(type, arg); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { ret }); - - std::string str = doOp(type, v).toString() + '\n'; - std::string expected = str + str; - - auto code = m_builder->finalize(); - Script script(&m_target, nullptr, nullptr); - script.setCode(code); - Thread thread(&m_target, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - - testing::internal::CaptureStdout(); - code->run(ctx.get()); - const std::string quotes = v.isString() ? "\"" : ""; - ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << "NOT: " << quotes << v.toString() << quotes; - }; - - void runUnaryNumOpTest(OpType type, const Value &v, double expectedResult) - { - createBuilder(true); - - CompilerValue *arg = m_builder->addConstValue(v); - CompilerValue *ret = addOp(type, arg); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { ret }); - - arg = m_builder->addConstValue(v); - arg = callConstFuncForType(v.type(), arg); - ret = addOp(type, arg); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { ret }); - - std::stringstream stream; - stream << expectedResult; - std::string str = stream.str() + '\n'; - std::string expected = str + str; - - auto code = m_builder->finalize(); - Script script(&m_target, nullptr, nullptr); - script.setCode(code); - Thread thread(&m_target, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - - testing::internal::CaptureStdout(); - code->run(ctx.get()); - const std::string quotes = v.isString() ? "\"" : ""; - ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes << v.toString() << quotes; - }; - - std::unordered_map> m_contexts; - std::vector> m_contextList; - std::unique_ptr m_builder; - std::shared_ptr m_procedurePrototype; - EngineMock m_engine; - TargetMock m_target; // NOTE: isStage() is used for call expectations - RandomGeneratorMock m_rng; + LLVMTestUtils m_utils; }; TEST_F(LLVMCodeBuilderTest, FunctionCalls) @@ -449,62 +34,61 @@ TEST_F(LLVMCodeBuilderTest, FunctionCalls) static const std::vector warpList = { false, true }; for (bool warp : warpList) { - createBuilder(warp); + LLVMCodeBuilder *builder = m_utils.createBuilder(warp); - m_builder->addFunctionCall("test_empty_function", Compiler::StaticType::Void, {}, {}); + builder->addFunctionCall("test_empty_function", Compiler::StaticType::Void, {}, {}); - CompilerValue *v = m_builder->addConstValue("test"); - m_builder->addFunctionCallWithCtx("test_ctx_function", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + CompilerValue *v = builder->addConstValue("test"); + builder->addFunctionCallWithCtx("test_ctx_function", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - v = m_builder->addTargetFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {}, {}); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addTargetFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {}, {}); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue("1"); - v = m_builder->addTargetFunctionCall("test_function_1_arg_ret", Compiler::StaticType::String, { Compiler::StaticType::String }, { v }); - CompilerValue *v1 = m_builder->addConstValue("2"); - CompilerValue *v2 = m_builder->addConstValue("3"); - m_builder - ->addTargetFunctionCall("test_function_3_args", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String }, { v, v1, v2 }); + v = builder->addConstValue("1"); + v = builder->addTargetFunctionCall("test_function_1_arg_ret", Compiler::StaticType::String, { Compiler::StaticType::String }, { v }); + CompilerValue *v1 = builder->addConstValue("2"); + CompilerValue *v2 = builder->addConstValue("3"); + builder->addTargetFunctionCall("test_function_3_args", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String }, { v, v1, v2 }); - v = m_builder->addConstValue("test"); - v1 = m_builder->addConstValue("4"); - v2 = m_builder->addConstValue("5"); - v = m_builder->addTargetFunctionCall( + v = builder->addConstValue("test"); + v1 = builder->addConstValue("4"); + v2 = builder->addConstValue("5"); + v = builder->addTargetFunctionCall( "test_function_3_args_ret", Compiler::StaticType::String, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String }, { v, v1, v2 }); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(123); - v = m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }, { v }); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue(123); + v = builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }, { v }); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addConstValue(true); - v = m_builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { v }); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue(true); + v = builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { v }); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - v = m_builder->addConstValue(321.5); - m_builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); - v = m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }, { v }); - m_builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + v = builder->addConstValue(321.5); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + v = builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }, { v }); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); - v = m_builder->addConstValue("test"); - m_builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); - v = m_builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String }, { v }); - m_builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + v = builder->addConstValue("test"); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + v = builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String }, { v }); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); - v = m_builder->addConstValue(true); - m_builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); - v = m_builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { v }); - m_builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + v = builder->addConstValue(true); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + v = builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { v }); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); - auto code = m_builder->finalize(); - Script script(&m_target, nullptr, nullptr); + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); script.setCode(code); - Thread thread(&m_target, nullptr, &script); + Thread thread(&m_utils.target(), nullptr, &script); auto ctx = code->createExecutionContext(&thread); std::stringstream s; @@ -531,7 +115,7 @@ TEST_F(LLVMCodeBuilderTest, FunctionCalls) "true\n" "true\n"; - EXPECT_CALL(m_target, isStage()).Times(7); + EXPECT_CALL(m_utils.target(), isStage()).Times(7); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -541,27 +125,27 @@ TEST_F(LLVMCodeBuilderTest, FunctionCalls) TEST_F(LLVMCodeBuilderTest, FunctionCallsWithPointers) { - createBuilder(true); + LLVMCodeBuilder *builder = m_utils.createBuilder(true); int var = 12; - CompilerValue *v = m_builder->addConstValue(&var); - v = m_builder->addTargetFunctionCall("test_function_1_ptr_arg_ret", Compiler::StaticType::Pointer, { Compiler::StaticType::Pointer }, { v }); + CompilerValue *v = builder->addConstValue(&var); + v = builder->addTargetFunctionCall("test_function_1_ptr_arg_ret", Compiler::StaticType::Pointer, { Compiler::StaticType::Pointer }, { v }); - m_builder->addFunctionCall("test_print_pointer", Compiler::StaticType::Void, { Compiler::StaticType::Pointer }, { v }); + builder->addFunctionCall("test_print_pointer", Compiler::StaticType::Void, { Compiler::StaticType::Pointer }, { v }); - auto code = m_builder->finalize(); - Script script(&m_target, nullptr, nullptr); + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); script.setCode(code); - Thread thread(&m_target, nullptr, &script); + Thread thread(&m_utils.target(), nullptr, &script); auto ctx = code->createExecutionContext(&thread); std::stringstream s; - s << &m_target; + s << &m_utils.target(); std::string ptr = s.str(); const std::string expected = "1_arg_ret 12\n" + ptr + "\n"; - EXPECT_CALL(m_target, isStage()); + EXPECT_CALL(m_utils.target(), isStage()); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -570,32 +154,32 @@ TEST_F(LLVMCodeBuilderTest, FunctionCallsWithPointers) TEST_F(LLVMCodeBuilderTest, ConstCasting) { - createBuilder(true); + LLVMCodeBuilder *builder = m_utils.createBuilder(true); - CompilerValue *v = m_builder->addConstValue(5.2); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addConstValue("-24.156"); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + CompilerValue *v = builder->addConstValue(5.2); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue("-24.156"); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addConstValue(true); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue(true); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - v = m_builder->addConstValue(0); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue(0); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - v = m_builder->addConstValue("false"); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue("false"); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - v = m_builder->addConstValue("123"); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue("123"); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue("hello world"); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue("hello world"); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - auto code = m_builder->finalize(); - Script script(&m_target, nullptr, nullptr); + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); script.setCode(code); - Thread thread(&m_target, nullptr, &script); + Thread thread(&m_utils.target(), nullptr, &script); auto ctx = code->createExecutionContext(&thread); static const std::string expected = @@ -614,77 +198,77 @@ TEST_F(LLVMCodeBuilderTest, ConstCasting) TEST_F(LLVMCodeBuilderTest, RawValueCasting) { - createBuilder(true); + LLVMCodeBuilder *builder = m_utils.createBuilder(true); // Number -> number - CompilerValue *v = m_builder->addConstValue(5.2); - v = callConstFuncForType(ValueType::Number, v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + CompilerValue *v = builder->addConstValue(5.2); + v = m_utils.callConstFuncForType(ValueType::Number, v); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); // Number -> bool - v = m_builder->addConstValue(-24.156); - v = callConstFuncForType(ValueType::Number, v); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue(-24.156); + v = m_utils.callConstFuncForType(ValueType::Number, v); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - v = m_builder->addConstValue(0); - v = callConstFuncForType(ValueType::Number, v); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue(0); + v = m_utils.callConstFuncForType(ValueType::Number, v); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); // Number -> string - v = m_builder->addConstValue(59.8); - v = callConstFuncForType(ValueType::Number, v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(59.8); + v = m_utils.callConstFuncForType(ValueType::Number, v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); // Bool -> number - v = m_builder->addConstValue(true); - v = callConstFuncForType(ValueType::Bool, v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue(true); + v = m_utils.callConstFuncForType(ValueType::Bool, v); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addConstValue(false); - v = callConstFuncForType(ValueType::Bool, v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue(false); + v = m_utils.callConstFuncForType(ValueType::Bool, v); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); // Bool -> bool - v = m_builder->addConstValue(true); - v = callConstFuncForType(ValueType::Bool, v); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue(true); + v = m_utils.callConstFuncForType(ValueType::Bool, v); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - v = m_builder->addConstValue(false); - v = callConstFuncForType(ValueType::Bool, v); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue(false); + v = m_utils.callConstFuncForType(ValueType::Bool, v); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); // Bool -> string - v = m_builder->addConstValue(true); - v = callConstFuncForType(ValueType::Bool, v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(true); + v = m_utils.callConstFuncForType(ValueType::Bool, v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(false); - v = callConstFuncForType(ValueType::Bool, v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(false); + v = m_utils.callConstFuncForType(ValueType::Bool, v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); // String -> number - v = m_builder->addConstValue("5.2"); - v = callConstFuncForType(ValueType::String, v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue("5.2"); + v = m_utils.callConstFuncForType(ValueType::String, v); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); // String -> bool - v = m_builder->addConstValue("abc"); - v = callConstFuncForType(ValueType::String, v); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue("abc"); + v = m_utils.callConstFuncForType(ValueType::String, v); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - v = m_builder->addConstValue("false"); - v = callConstFuncForType(ValueType::String, v); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue("false"); + v = m_utils.callConstFuncForType(ValueType::String, v); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); // String -> string - v = m_builder->addConstValue("hello world"); - v = callConstFuncForType(ValueType::String, v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue("hello world"); + v = m_utils.callConstFuncForType(ValueType::String, v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - auto code = m_builder->finalize(); - Script script(&m_target, nullptr, nullptr); + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); script.setCode(code); - Thread thread(&m_target, nullptr, &script); + Thread thread(&m_utils.target(), nullptr, &script); auto ctx = code->createExecutionContext(&thread); static const std::string expected = @@ -708,1177 +292,45 @@ TEST_F(LLVMCodeBuilderTest, RawValueCasting) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } -TEST_F(LLVMCodeBuilderTest, Add) -{ - runOpTest(OpType::Add, 50, 25); - runOpTest(OpType::Add, -500, 25); - runOpTest(OpType::Add, -500, -25); - runOpTest(OpType::Add, "2.54", "6.28"); - runOpTest(OpType::Add, 2.54, "-6.28"); - runOpTest(OpType::Add, true, true); - runOpTest(OpType::Add, "Infinity", "Infinity"); - runOpTest(OpType::Add, "Infinity", "-Infinity"); - runOpTest(OpType::Add, "-Infinity", "Infinity"); - runOpTest(OpType::Add, "-Infinity", "-Infinity"); - runOpTest(OpType::Add, 1, "NaN"); - runOpTest(OpType::Add, "NaN", 1); -} - -TEST_F(LLVMCodeBuilderTest, Subtract) -{ - runOpTest(OpType::Sub, 50, 25); - runOpTest(OpType::Sub, -500, 25); - runOpTest(OpType::Sub, -500, -25); - runOpTest(OpType::Sub, "2.54", "6.28"); - runOpTest(OpType::Sub, 2.54, "-6.28"); - runOpTest(OpType::Sub, true, true); - runOpTest(OpType::Sub, "Infinity", "Infinity"); - runOpTest(OpType::Sub, "Infinity", "-Infinity"); - runOpTest(OpType::Sub, "-Infinity", "Infinity"); - runOpTest(OpType::Sub, "-Infinity", "-Infinity"); - runOpTest(OpType::Sub, 1, "NaN"); - runOpTest(OpType::Sub, "NaN", 1); -} - -TEST_F(LLVMCodeBuilderTest, Multiply) -{ - runOpTest(OpType::Mul, 50, 2); - runOpTest(OpType::Mul, -500, 25); - runOpTest(OpType::Mul, "-500", -25); - runOpTest(OpType::Mul, "2.54", "6.28"); - runOpTest(OpType::Mul, true, true); - runOpTest(OpType::Mul, "Infinity", "Infinity"); - runOpTest(OpType::Mul, "Infinity", 0); - runOpTest(OpType::Mul, "Infinity", 2); - runOpTest(OpType::Mul, "Infinity", -2); - runOpTest(OpType::Mul, "Infinity", "-Infinity"); - runOpTest(OpType::Mul, "-Infinity", "Infinity"); - runOpTest(OpType::Mul, "-Infinity", 0); - runOpTest(OpType::Mul, "-Infinity", 2); - runOpTest(OpType::Mul, "-Infinity", -2); - runOpTest(OpType::Mul, "-Infinity", "-Infinity"); - runOpTest(OpType::Mul, 1, "NaN"); - runOpTest(OpType::Mul, "NaN", 1); -} - -TEST_F(LLVMCodeBuilderTest, Divide) -{ - runOpTest(OpType::Div, 50, 2); - runOpTest(OpType::Div, -500, 25); - runOpTest(OpType::Div, "-500", -25); - runOpTest(OpType::Div, "3.5", "2.5"); - runOpTest(OpType::Div, true, true); - runOpTest(OpType::Div, "Infinity", "Infinity"); - runOpTest(OpType::Div, "Infinity", 0); - runOpTest(OpType::Div, "Infinity", 2); - runOpTest(OpType::Div, "Infinity", -2); - runOpTest(OpType::Div, "Infinity", "-Infinity"); - runOpTest(OpType::Div, "-Infinity", "Infinity"); - runOpTest(OpType::Div, "-Infinity", 0); - runOpTest(OpType::Div, "-Infinity", 2); - runOpTest(OpType::Div, "-Infinity", -2); - runOpTest(OpType::Div, "-Infinity", "-Infinity"); - runOpTest(OpType::Div, 0, "Infinity"); - runOpTest(OpType::Div, 2, "Infinity"); - runOpTest(OpType::Div, -2, "Infinity"); - runOpTest(OpType::Div, 0, "-Infinity"); - runOpTest(OpType::Div, 2, "-Infinity"); - runOpTest(OpType::Div, -2, "-Infinity"); - runOpTest(OpType::Div, 1, "NaN"); - runOpTest(OpType::Div, "NaN", 1); - runOpTest(OpType::Div, 5, 0); - runOpTest(OpType::Div, -5, 0); - runOpTest(OpType::Div, 0, 0); -} - -TEST_F(LLVMCodeBuilderTest, Random) -{ - EXPECT_CALL(m_rng, randint(-45, 12)).Times(3).WillRepeatedly(Return(-18)); - runOpTest(OpType::Random, -45, 12); - - EXPECT_CALL(m_rng, randint(-45, 12)).Times(3).WillRepeatedly(Return(5)); - runOpTest(OpType::Random, -45.0, 12.0); - - EXPECT_CALL(m_rng, randintDouble(12, 6.05)).Times(3).WillRepeatedly(Return(3.486789)); - runOpTest(OpType::Random, 12, 6.05); - - EXPECT_CALL(m_rng, randintDouble(-78.686, -45)).Times(3).WillRepeatedly(Return(-59.468873)); - runOpTest(OpType::Random, -78.686, -45); - - EXPECT_CALL(m_rng, randintDouble(6.05, -78.686)).Times(3).WillRepeatedly(Return(-28.648764)); - runOpTest(OpType::Random, 6.05, -78.686); - - EXPECT_CALL(m_rng, randint(-45, 12)).Times(3).WillRepeatedly(Return(0)); - runOpTest(OpType::Random, "-45", "12"); - - EXPECT_CALL(m_rng, randintDouble(-45, 12)).Times(3).WillRepeatedly(Return(5.2)); - runOpTest(OpType::Random, "-45.0", "12"); - - EXPECT_CALL(m_rng, randintDouble(-45, 12)).Times(3).WillRepeatedly(Return(-15.5787)); - runOpTest(OpType::Random, "-45", "12.0"); - - EXPECT_CALL(m_rng, randintDouble(-45, 12)).Times(3).WillRepeatedly(Return(2.587964)); - runOpTest(OpType::Random, "-45.0", "12.0"); - - EXPECT_CALL(m_rng, randintDouble(6.05, -78.686)).Times(3).WillRepeatedly(Return(5.648764)); - runOpTest(OpType::Random, "6.05", "-78.686"); - - EXPECT_CALL(m_rng, randint(-45, 12)).Times(3).WillRepeatedly(Return(0)); - runOpTest(OpType::Random, "-45", 12); - - EXPECT_CALL(m_rng, randint(-45, 12)).Times(3).WillRepeatedly(Return(0)); - runOpTest(OpType::Random, -45, "12"); - - EXPECT_CALL(m_rng, randintDouble(-45, 12)).Times(3).WillRepeatedly(Return(5.2)); - runOpTest(OpType::Random, "-45.0", 12); - - EXPECT_CALL(m_rng, randintDouble(-45, 12)).Times(3).WillRepeatedly(Return(-15.5787)); - runOpTest(OpType::Random, -45, "12.0"); - - EXPECT_CALL(m_rng, randintDouble(6.05, -78.686)).Times(3).WillRepeatedly(Return(5.648764)); - runOpTest(OpType::Random, 6.05, "-78.686"); - - EXPECT_CALL(m_rng, randintDouble(6.05, -78.686)).Times(3).WillRepeatedly(Return(5.648764)); - runOpTest(OpType::Random, "6.05", -78.686); - - EXPECT_CALL(m_rng, randint(0, 1)).Times(3).WillRepeatedly(Return(1)); - runOpTest(OpType::Random, false, true); - - EXPECT_CALL(m_rng, randint(1, 5)).Times(3).WillRepeatedly(Return(1)); - runOpTest(OpType::Random, true, 5); - - EXPECT_CALL(m_rng, randint(8, 0)).Times(3).WillRepeatedly(Return(1)); - runOpTest(OpType::Random, 8, false); - - const double inf = std::numeric_limits::infinity(); - const double nan = std::numeric_limits::quiet_NaN(); - - EXPECT_CALL(m_rng, randint(0, 5)).Times(3).WillRepeatedly(Return(5)); - runOpTest(OpType::Random, nan, 5); - - EXPECT_CALL(m_rng, randint(5, 0)).Times(3).WillRepeatedly(Return(3)); - runOpTest(OpType::Random, 5, nan); - - EXPECT_CALL(m_rng, randint(0, 0)).Times(3).WillRepeatedly(Return(0)); - runOpTest(OpType::Random, nan, nan); - - EXPECT_CALL(m_rng, randint).WillRepeatedly(Return(0)); - EXPECT_CALL(m_rng, randintDouble).WillRepeatedly(Return(0)); - - runOpTest(OpType::Random, inf, 2, inf); - runOpTest(OpType::Random, -8, inf, inf); - runOpTest(OpType::Random, -inf, -2, -inf); - runOpTest(OpType::Random, 8, -inf, -inf); - - runOpTest(OpType::Random, inf, 2.5, inf); - runOpTest(OpType::Random, -8.09, inf, inf); - runOpTest(OpType::Random, -inf, -2.5, -inf); - runOpTest(OpType::Random, 8.09, -inf, -inf); - - runOpTest(OpType::Random, inf, inf, inf); - runOpTest(OpType::Random, -inf, -inf, -inf); - runOpTest(OpType::Random, inf, -inf, nan); - runOpTest(OpType::Random, -inf, inf, nan); -} - -TEST_F(LLVMCodeBuilderTest, RandomInt) -{ - EXPECT_CALL(m_rng, randint(-45, 12)).Times(3).WillRepeatedly(Return(-18)); - runOpTest(OpType::RandomInt, -45, 12); - - EXPECT_CALL(m_rng, randint(-45, 12)).Times(3).WillRepeatedly(Return(5)); - runOpTest(OpType::RandomInt, -45.0, 12.0); - - EXPECT_CALL(m_rng, randint(12, 6)).Times(3).WillRepeatedly(Return(3)); - runOpTest(OpType::RandomInt, 12, 6.05); - - EXPECT_CALL(m_rng, randint(-78, -45)).Times(3).WillRepeatedly(Return(-59)); - runOpTest(OpType::RandomInt, -78.686, -45); - - EXPECT_CALL(m_rng, randint(-45, 12)).Times(3).WillRepeatedly(Return(0)); - runOpTest(OpType::RandomInt, "-45", "12"); - - EXPECT_CALL(m_rng, randint(-45, 12)).Times(3).WillRepeatedly(Return(5)); - runOpTest(OpType::RandomInt, "-45.0", "12"); - - EXPECT_CALL(m_rng, randint(-45, 12)).Times(3).WillRepeatedly(Return(-15)); - runOpTest(OpType::RandomInt, "-45", "12.0"); - - EXPECT_CALL(m_rng, randint(0, 1)).Times(3).WillRepeatedly(Return(1)); - runOpTest(OpType::RandomInt, false, true); - - EXPECT_CALL(m_rng, randint(1, 5)).Times(3).WillRepeatedly(Return(1)); - runOpTest(OpType::RandomInt, true, 5); - - EXPECT_CALL(m_rng, randint(8, 0)).Times(3).WillRepeatedly(Return(1)); - runOpTest(OpType::RandomInt, 8, false); - - // NOTE: Infinity, -Infinity and NaN behavior is undefined -} - -TEST_F(LLVMCodeBuilderTest, EqualComparison) -{ - runOpTest(OpType::CmpEQ, 10, 10); - runOpTest(OpType::CmpEQ, 10, 8); - runOpTest(OpType::CmpEQ, 8, 10); - - runOpTest(OpType::CmpEQ, -4.25, -4.25); - runOpTest(OpType::CmpEQ, -4.25, 5.312); - runOpTest(OpType::CmpEQ, 5.312, -4.25); - - runOpTest(OpType::CmpEQ, true, true); - runOpTest(OpType::CmpEQ, true, false); - runOpTest(OpType::CmpEQ, false, true); - - runOpTest(OpType::CmpEQ, 1, true); - runOpTest(OpType::CmpEQ, 1, false); - - runOpTest(OpType::CmpEQ, "abC def", "abC def"); - runOpTest(OpType::CmpEQ, "abC def", "abc dEf"); - runOpTest(OpType::CmpEQ, "abC def", "ghi Jkl"); - runOpTest(OpType::CmpEQ, "abC def", "hello world"); - - runOpTest(OpType::CmpEQ, " ", ""); - runOpTest(OpType::CmpEQ, " ", "0"); - runOpTest(OpType::CmpEQ, " ", 0); - runOpTest(OpType::CmpEQ, 0, " "); - runOpTest(OpType::CmpEQ, "", "0"); - runOpTest(OpType::CmpEQ, "", 0); - runOpTest(OpType::CmpEQ, 0, ""); - runOpTest(OpType::CmpEQ, "0", 0); - runOpTest(OpType::CmpEQ, 0, "0"); - - runOpTest(OpType::CmpEQ, 5.25, "5.25"); - runOpTest(OpType::CmpEQ, "5.25", 5.25); - runOpTest(OpType::CmpEQ, 5.25, " 5.25"); - runOpTest(OpType::CmpEQ, " 5.25", 5.25); - runOpTest(OpType::CmpEQ, 5.25, "5.25 "); - runOpTest(OpType::CmpEQ, "5.25 ", 5.25); - runOpTest(OpType::CmpEQ, 5.25, " 5.25 "); - runOpTest(OpType::CmpEQ, " 5.25 ", 5.25); - runOpTest(OpType::CmpEQ, 5.25, "5.26"); - runOpTest(OpType::CmpEQ, "5.26", 5.25); - runOpTest(OpType::CmpEQ, "5.25", "5.26"); - runOpTest(OpType::CmpEQ, 5, "5 "); - runOpTest(OpType::CmpEQ, "5 ", 5); - runOpTest(OpType::CmpEQ, 0, "1"); - runOpTest(OpType::CmpEQ, "1", 0); - runOpTest(OpType::CmpEQ, 0, "test"); - runOpTest(OpType::CmpEQ, "test", 0); - - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runOpTest(OpType::CmpEQ, inf, inf); - runOpTest(OpType::CmpEQ, -inf, -inf); - runOpTest(OpType::CmpEQ, nan, nan); - runOpTest(OpType::CmpEQ, inf, -inf); - runOpTest(OpType::CmpEQ, -inf, inf); - runOpTest(OpType::CmpEQ, inf, nan); - runOpTest(OpType::CmpEQ, nan, inf); - runOpTest(OpType::CmpEQ, -inf, nan); - runOpTest(OpType::CmpEQ, nan, -inf); - - runOpTest(OpType::CmpEQ, 5, inf); - runOpTest(OpType::CmpEQ, 5, -inf); - runOpTest(OpType::CmpEQ, 5, nan); - runOpTest(OpType::CmpEQ, 0, nan); - - runOpTest(OpType::CmpEQ, true, "true"); - runOpTest(OpType::CmpEQ, "true", true); - runOpTest(OpType::CmpEQ, false, "false"); - runOpTest(OpType::CmpEQ, "false", false); - runOpTest(OpType::CmpEQ, false, "true"); - runOpTest(OpType::CmpEQ, "true", false); - runOpTest(OpType::CmpEQ, true, "false"); - runOpTest(OpType::CmpEQ, "false", true); - runOpTest(OpType::CmpEQ, true, "TRUE"); - runOpTest(OpType::CmpEQ, "TRUE", true); - runOpTest(OpType::CmpEQ, false, "FALSE"); - runOpTest(OpType::CmpEQ, "FALSE", false); - - runOpTest(OpType::CmpEQ, true, "00001"); - runOpTest(OpType::CmpEQ, "00001", true); - runOpTest(OpType::CmpEQ, true, "00000"); - runOpTest(OpType::CmpEQ, "00000", true); - runOpTest(OpType::CmpEQ, false, "00000"); - runOpTest(OpType::CmpEQ, "00000", false); - - runOpTest(OpType::CmpEQ, "true", 1); - runOpTest(OpType::CmpEQ, 1, "true"); - runOpTest(OpType::CmpEQ, "true", 0); - runOpTest(OpType::CmpEQ, 0, "true"); - runOpTest(OpType::CmpEQ, "false", 0); - runOpTest(OpType::CmpEQ, 0, "false"); - runOpTest(OpType::CmpEQ, "false", 1); - runOpTest(OpType::CmpEQ, 1, "false"); - - runOpTest(OpType::CmpEQ, "true", "TRUE"); - runOpTest(OpType::CmpEQ, "true", "FALSE"); - runOpTest(OpType::CmpEQ, "false", "FALSE"); - runOpTest(OpType::CmpEQ, "false", "TRUE"); - - runOpTest(OpType::CmpEQ, true, inf); - runOpTest(OpType::CmpEQ, true, -inf); - runOpTest(OpType::CmpEQ, true, nan); - runOpTest(OpType::CmpEQ, false, inf); - runOpTest(OpType::CmpEQ, false, -inf); - runOpTest(OpType::CmpEQ, false, nan); - - runOpTest(OpType::CmpEQ, "Infinity", inf); - runOpTest(OpType::CmpEQ, "Infinity", -inf); - runOpTest(OpType::CmpEQ, "Infinity", nan); - runOpTest(OpType::CmpEQ, "infinity", inf); - runOpTest(OpType::CmpEQ, "infinity", -inf); - runOpTest(OpType::CmpEQ, "infinity", nan); - runOpTest(OpType::CmpEQ, "-Infinity", inf); - runOpTest(OpType::CmpEQ, "-Infinity", -inf); - runOpTest(OpType::CmpEQ, "-Infinity", nan); - runOpTest(OpType::CmpEQ, "-infinity", inf); - runOpTest(OpType::CmpEQ, "-infinity", -inf); - runOpTest(OpType::CmpEQ, "-infinity", nan); - runOpTest(OpType::CmpEQ, "NaN", inf); - runOpTest(OpType::CmpEQ, "NaN", -inf); - runOpTest(OpType::CmpEQ, "NaN", nan); - runOpTest(OpType::CmpEQ, "nan", inf); - runOpTest(OpType::CmpEQ, "nan", -inf); - runOpTest(OpType::CmpEQ, "nan", nan); - - runOpTest(OpType::CmpEQ, inf, "abc"); - runOpTest(OpType::CmpEQ, inf, " "); - runOpTest(OpType::CmpEQ, inf, ""); - runOpTest(OpType::CmpEQ, inf, "0"); - runOpTest(OpType::CmpEQ, -inf, "abc"); - runOpTest(OpType::CmpEQ, -inf, " "); - runOpTest(OpType::CmpEQ, -inf, ""); - runOpTest(OpType::CmpEQ, -inf, "0"); - runOpTest(OpType::CmpEQ, nan, "abc"); - runOpTest(OpType::CmpEQ, nan, " "); - runOpTest(OpType::CmpEQ, nan, ""); - runOpTest(OpType::CmpEQ, nan, "0"); -} - -TEST_F(LLVMCodeBuilderTest, GreaterAndLowerThanComparison) -{ - std::vector opTypes = { OpType::CmpGT, OpType::CmpLT }; - - for (OpType type : opTypes) { - runOpTest(type, 10, 10); - runOpTest(type, 10, 8); - runOpTest(type, 8, 10); - - runOpTest(type, -4.25, -4.25); - runOpTest(type, -4.25, 5.312); - runOpTest(type, 5.312, -4.25); - - runOpTest(type, true, true); - runOpTest(type, true, false); - runOpTest(type, false, true); - - runOpTest(type, 1, true); - runOpTest(type, 1, false); - - runOpTest(type, "abC def", "abC def"); - runOpTest(type, "abC def", "abc dEf"); - runOpTest(type, "abC def", "ghi Jkl"); - runOpTest(type, "ghi Jkl", "abC def"); - runOpTest(type, "abC def", "hello world"); - - runOpTest(type, " ", ""); - runOpTest(type, " ", "0"); - runOpTest(type, " ", 0); - runOpTest(type, 0, " "); - runOpTest(type, "", "0"); - runOpTest(type, "", 0); - runOpTest(type, 0, ""); - runOpTest(type, "0", 0); - runOpTest(type, 0, "0"); - - runOpTest(type, 5.25, "5.25"); - runOpTest(type, "5.25", 5.25); - runOpTest(type, 5.25, " 5.25"); - runOpTest(type, " 5.25", 5.25); - runOpTest(type, 5.25, "5.25 "); - runOpTest(type, "5.25 ", 5.25); - runOpTest(type, 5.25, " 5.25 "); - runOpTest(type, " 5.25 ", 5.25); - runOpTest(type, 5.25, "5.26"); - runOpTest(type, "5.26", 5.25); - runOpTest(type, "5.25", "5.26"); - runOpTest(type, 5, "5 "); - runOpTest(type, "5 ", 5); - runOpTest(type, 0, "1"); - runOpTest(type, "1", 0); - runOpTest(type, 0, "test"); - runOpTest(type, "test", 0); - runOpTest(type, 55, "abc"); - runOpTest(type, "abc", 55); - - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runOpTest(type, inf, inf); - runOpTest(type, -inf, -inf); - runOpTest(type, nan, nan); - runOpTest(type, inf, -inf); - runOpTest(type, -inf, inf); - runOpTest(type, inf, nan); - runOpTest(type, nan, inf); - runOpTest(type, -inf, nan); - runOpTest(type, nan, -inf); - - runOpTest(type, 5, inf); - runOpTest(type, inf, 5); - runOpTest(type, 5, -inf); - runOpTest(type, -inf, 5); - runOpTest(type, 5, nan); - runOpTest(type, nan, 5); - runOpTest(type, 0, nan); - runOpTest(type, nan, 0); - - runOpTest(type, true, "true"); - runOpTest(type, "true", true); - runOpTest(type, false, "false"); - runOpTest(type, "false", false); - runOpTest(type, false, "true"); - runOpTest(type, "true", false); - runOpTest(type, true, "false"); - runOpTest(type, "false", true); - runOpTest(type, true, "TRUE"); - runOpTest(type, "TRUE", true); - runOpTest(type, false, "FALSE"); - runOpTest(type, "FALSE", false); - - runOpTest(type, true, "00001"); - runOpTest(type, "00001", true); - runOpTest(type, true, "00000"); - runOpTest(type, "00000", true); - runOpTest(type, false, "00000"); - runOpTest(type, "00000", false); - - runOpTest(type, "true", 1); - runOpTest(type, 1, "true"); - runOpTest(type, "true", 0); - runOpTest(type, 0, "true"); - runOpTest(type, "false", 0); - runOpTest(type, 0, "false"); - runOpTest(type, "false", 1); - runOpTest(type, 1, "false"); - - runOpTest(type, "true", "TRUE"); - runOpTest(type, "true", "FALSE"); - runOpTest(type, "false", "FALSE"); - runOpTest(type, "false", "TRUE"); - - runOpTest(type, true, inf); - runOpTest(type, inf, true); - runOpTest(type, true, -inf); - runOpTest(type, -inf, true); - runOpTest(type, true, nan); - runOpTest(type, nan, true); - runOpTest(type, false, inf); - runOpTest(type, inf, false); - runOpTest(type, false, -inf); - runOpTest(type, -inf, false); - runOpTest(type, false, nan); - runOpTest(type, nan, false); - - runOpTest(type, "Infinity", inf); - runOpTest(type, "Infinity", -inf); - runOpTest(type, "Infinity", nan); - runOpTest(type, "infinity", inf); - runOpTest(type, "infinity", -inf); - runOpTest(type, "infinity", nan); - runOpTest(type, "-Infinity", inf); - runOpTest(type, "-Infinity", -inf); - runOpTest(type, "-Infinity", nan); - runOpTest(type, "-infinity", inf); - runOpTest(type, "-infinity", -inf); - runOpTest(type, "-infinity", nan); - runOpTest(type, "NaN", inf); - runOpTest(type, "NaN", -inf); - runOpTest(type, "NaN", nan); - runOpTest(type, "nan", inf); - runOpTest(type, "nan", -inf); - runOpTest(type, "nan", nan); - - runOpTest(type, inf, "abc"); - runOpTest(type, inf, " "); - runOpTest(type, inf, ""); - runOpTest(type, inf, "0"); - runOpTest(type, -inf, "abc"); - runOpTest(type, -inf, " "); - runOpTest(type, -inf, ""); - runOpTest(type, -inf, "0"); - runOpTest(type, nan, "abc"); - runOpTest(type, nan, " "); - runOpTest(type, nan, ""); - runOpTest(type, nan, "0"); - } -} - -TEST_F(LLVMCodeBuilderTest, StringEqualComparison) -{ - std::vector types = { OpType::StrCmpEQCS, OpType::StrCmpEQCI }; - - for (OpType type : types) { - runOpTest(type, 10, 10); - runOpTest(type, 10, 8); - runOpTest(type, 8, 10); - - runOpTest(type, -4.25, -4.25); - runOpTest(type, -4.25, 5.312); - runOpTest(type, 5.312, -4.25); - - runOpTest(type, true, true); - runOpTest(type, true, false); - runOpTest(type, false, true); - - runOpTest(type, 1, true); - runOpTest(type, 1, false); - - runOpTest(type, "abC def", "abC def"); - runOpTest(type, "abC def", "abc dEf"); - runOpTest(type, "abC def", "ghi Jkl"); - runOpTest(type, "abC def", "hello world"); - - runOpTest(type, " ", ""); - runOpTest(type, " ", "0"); - runOpTest(type, " ", 0); - runOpTest(type, 0, " "); - runOpTest(type, "", "0"); - runOpTest(type, "", 0); - runOpTest(type, 0, ""); - runOpTest(type, "0", 0); - runOpTest(type, 0, "0"); - - runOpTest(type, 5.25, "5.25"); - runOpTest(type, "5.25", 5.25); - runOpTest(type, 5.25, " 5.25"); - runOpTest(type, " 5.25", 5.25); - runOpTest(type, 5.25, "5.25 "); - runOpTest(type, "5.25 ", 5.25); - runOpTest(type, 5.25, " 5.25 "); - runOpTest(type, " 5.25 ", 5.25); - runOpTest(type, 5.25, "5.26"); - runOpTest(type, "5.26", 5.25); - runOpTest(type, "5.25", "5.26"); - runOpTest(type, 5, "5 "); - runOpTest(type, "5 ", 5); - runOpTest(type, 0, "1"); - runOpTest(type, "1", 0); - runOpTest(type, 0, "test"); - runOpTest(type, "test", 0); - - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runOpTest(type, inf, inf); - runOpTest(type, -inf, -inf); - runOpTest(type, nan, nan); - runOpTest(type, inf, -inf); - runOpTest(type, -inf, inf); - runOpTest(type, inf, nan); - runOpTest(type, nan, inf); - runOpTest(type, -inf, nan); - runOpTest(type, nan, -inf); - - runOpTest(type, 5, inf); - runOpTest(type, 5, -inf); - runOpTest(type, 5, nan); - runOpTest(type, 0, nan); - - runOpTest(type, true, "true"); - runOpTest(type, "true", true); - runOpTest(type, false, "false"); - runOpTest(type, "false", false); - runOpTest(type, false, "true"); - runOpTest(type, "true", false); - runOpTest(type, true, "false"); - runOpTest(type, "false", true); - runOpTest(type, true, "TRUE"); - runOpTest(type, "TRUE", true); - runOpTest(type, false, "FALSE"); - runOpTest(type, "FALSE", false); - - runOpTest(type, true, "00001"); - runOpTest(type, "00001", true); - runOpTest(type, true, "00000"); - runOpTest(type, "00000", true); - runOpTest(type, false, "00000"); - runOpTest(type, "00000", false); - - runOpTest(type, "true", 1); - runOpTest(type, 1, "true"); - runOpTest(type, "true", 0); - runOpTest(type, 0, "true"); - runOpTest(type, "false", 0); - runOpTest(type, 0, "false"); - runOpTest(type, "false", 1); - runOpTest(type, 1, "false"); - - runOpTest(type, "true", "TRUE"); - runOpTest(type, "true", "FALSE"); - runOpTest(type, "false", "FALSE"); - runOpTest(type, "false", "TRUE"); - - runOpTest(type, true, inf); - runOpTest(type, true, -inf); - runOpTest(type, true, nan); - runOpTest(type, false, inf); - runOpTest(type, false, -inf); - runOpTest(type, false, nan); - - runOpTest(type, "Infinity", inf); - runOpTest(type, "Infinity", -inf); - runOpTest(type, "Infinity", nan); - runOpTest(type, "infinity", inf); - runOpTest(type, "infinity", -inf); - runOpTest(type, "infinity", nan); - runOpTest(type, "-Infinity", inf); - runOpTest(type, "-Infinity", -inf); - runOpTest(type, "-Infinity", nan); - runOpTest(type, "-infinity", inf); - runOpTest(type, "-infinity", -inf); - runOpTest(type, "-infinity", nan); - runOpTest(type, "NaN", inf); - runOpTest(type, "NaN", -inf); - runOpTest(type, "NaN", nan); - runOpTest(type, "nan", inf); - runOpTest(type, "nan", -inf); - runOpTest(type, "nan", nan); - - runOpTest(type, inf, "abc"); - runOpTest(type, inf, " "); - runOpTest(type, inf, ""); - runOpTest(type, inf, "0"); - runOpTest(type, -inf, "abc"); - runOpTest(type, -inf, " "); - runOpTest(type, -inf, ""); - runOpTest(type, -inf, "0"); - runOpTest(type, nan, "abc"); - runOpTest(type, nan, " "); - runOpTest(type, nan, ""); - runOpTest(type, nan, "0"); - } -} - -TEST_F(LLVMCodeBuilderTest, AndOr) -{ - std::vector opTypes = { OpType::And, OpType::Or }; - - for (OpType type : opTypes) { - runOpTest(type, 10, 8); - runOpTest(type, -4.25, -4.25); - runOpTest(type, -4.25, 5.312); - - runOpTest(type, true, true); - runOpTest(type, true, false); - runOpTest(type, false, true); - - runOpTest(type, 1, true); - runOpTest(type, 1, false); - - runOpTest(type, "abc", "def"); - runOpTest(type, "true", "true"); - runOpTest(type, "true", "false"); - runOpTest(type, "false", "true"); - runOpTest(type, "false", "false"); - - runOpTest(type, 5.25, "5.25"); - runOpTest(type, "5.25", 5.25); - runOpTest(type, 0, "1"); - runOpTest(type, "1", 0); - runOpTest(type, 0, "test"); - runOpTest(type, "test", 0); - - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runOpTest(type, inf, inf); - runOpTest(type, -inf, -inf); - runOpTest(type, nan, nan); - runOpTest(type, inf, -inf); - runOpTest(type, -inf, inf); - runOpTest(type, inf, nan); - runOpTest(type, nan, inf); - runOpTest(type, -inf, nan); - runOpTest(type, nan, -inf); - - runOpTest(type, true, "true"); - runOpTest(type, "true", true); - runOpTest(type, false, "false"); - runOpTest(type, "false", false); - runOpTest(type, false, "true"); - runOpTest(type, "true", false); - runOpTest(type, true, "false"); - runOpTest(type, "false", true); - runOpTest(type, true, "TRUE"); - runOpTest(type, "TRUE", true); - runOpTest(type, false, "FALSE"); - runOpTest(type, "FALSE", false); - - runOpTest(type, true, "00001"); - runOpTest(type, "00001", true); - runOpTest(type, true, "00000"); - runOpTest(type, "00000", true); - runOpTest(type, false, "00000"); - runOpTest(type, "00000", false); - - runOpTest(type, "true", 1); - runOpTest(type, 1, "true"); - runOpTest(type, "true", 0); - runOpTest(type, 0, "true"); - runOpTest(type, "false", 0); - runOpTest(type, 0, "false"); - runOpTest(type, "false", 1); - runOpTest(type, 1, "false"); - - runOpTest(type, "true", "TRUE"); - runOpTest(type, "true", "FALSE"); - runOpTest(type, "false", "FALSE"); - runOpTest(type, "false", "TRUE"); - - runOpTest(type, true, inf); - runOpTest(type, inf, true); - runOpTest(type, true, -inf); - runOpTest(type, -inf, true); - runOpTest(type, true, nan); - runOpTest(type, nan, true); - runOpTest(type, false, inf); - runOpTest(type, inf, false); - runOpTest(type, false, -inf); - runOpTest(type, -inf, false); - runOpTest(type, false, nan); - runOpTest(type, nan, false); - - runOpTest(type, "Infinity", inf); - runOpTest(type, "Infinity", -inf); - runOpTest(type, "Infinity", nan); - runOpTest(type, "infinity", inf); - runOpTest(type, "infinity", -inf); - runOpTest(type, "infinity", nan); - runOpTest(type, "-Infinity", inf); - runOpTest(type, "-Infinity", -inf); - runOpTest(type, "-Infinity", nan); - runOpTest(type, "-infinity", inf); - runOpTest(type, "-infinity", -inf); - runOpTest(type, "-infinity", nan); - runOpTest(type, "NaN", inf); - runOpTest(type, "NaN", -inf); - runOpTest(type, "NaN", nan); - runOpTest(type, "nan", inf); - runOpTest(type, "nan", -inf); - runOpTest(type, "nan", nan); - } -} - -TEST_F(LLVMCodeBuilderTest, Not) -{ - runOpTest(OpType::Not, 10); - runOpTest(OpType::Not, -4.25); - runOpTest(OpType::Not, 5.312); - runOpTest(OpType::Not, 1); - runOpTest(OpType::Not, 0); - - runOpTest(OpType::Not, true); - runOpTest(OpType::Not, false); - - runOpTest(OpType::Not, "abc"); - runOpTest(OpType::Not, "5.25"); - runOpTest(OpType::Not, "0"); - - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runOpTest(OpType::Not, inf); - runOpTest(OpType::Not, -inf); - runOpTest(OpType::Not, nan); - - runOpTest(OpType::Not, "true"); - runOpTest(OpType::Not, "false"); - runOpTest(OpType::Not, "TRUE"); - runOpTest(OpType::Not, "FALSE"); - - runOpTest(OpType::Not, "00001"); - runOpTest(OpType::Not, "00000"); - - runOpTest(OpType::Not, "Infinity"); - runOpTest(OpType::Not, "infinity"); - runOpTest(OpType::Not, "-Infinity"); - runOpTest(OpType::Not, "-infinity"); - runOpTest(OpType::Not, "NaN"); - runOpTest(OpType::Not, "nan"); -} - -TEST_F(LLVMCodeBuilderTest, Mod) -{ - runOpTest(OpType::Mod, 4, 3); - runOpTest(OpType::Mod, 3, 3); - runOpTest(OpType::Mod, 2, 3); - runOpTest(OpType::Mod, 1, 3); - runOpTest(OpType::Mod, 0, 3); - runOpTest(OpType::Mod, -1, 3); - runOpTest(OpType::Mod, -2, 3); - runOpTest(OpType::Mod, -3, 3); - runOpTest(OpType::Mod, -4, 3); - runOpTest(OpType::Mod, 4.75, 2); - runOpTest(OpType::Mod, -4.75, 2); - runOpTest(OpType::Mod, -4.75, -2); - runOpTest(OpType::Mod, 4.75, -2); - runOpTest(OpType::Mod, 5, 0); - runOpTest(OpType::Mod, -5, 0); - runOpTest(OpType::Mod, -2.5, "Infinity"); - runOpTest(OpType::Mod, -1.2, "-Infinity"); - runOpTest(OpType::Mod, 2.5, "Infinity"); - runOpTest(OpType::Mod, 1.2, "-Infinity"); - runOpTest(OpType::Mod, "Infinity", 2); - runOpTest(OpType::Mod, "-Infinity", 2); - runOpTest(OpType::Mod, "Infinity", -2); - runOpTest(OpType::Mod, "-Infinity", -2); - runOpTest(OpType::Mod, 3, "NaN"); - runOpTest(OpType::Mod, -3, "NaN"); - runOpTest(OpType::Mod, "NaN", 5); - runOpTest(OpType::Mod, "NaN", -5); -} - -TEST_F(LLVMCodeBuilderTest, Round) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Round, 4.0, 4.0); - runUnaryNumOpTest(OpType::Round, 3.2, 3.0); - runUnaryNumOpTest(OpType::Round, 3.5, 4.0); - runUnaryNumOpTest(OpType::Round, 3.6, 4.0); - runUnaryNumOpTest(OpType::Round, -2.4, -2.0); - runUnaryNumOpTest(OpType::Round, -2.5, -2.0); - runUnaryNumOpTest(OpType::Round, -2.6, -3.0); - runUnaryNumOpTest(OpType::Round, -0.4, -0.0); - runUnaryNumOpTest(OpType::Round, -0.5, -0.0); - runUnaryNumOpTest(OpType::Round, -0.51, -1.0); - runUnaryNumOpTest(OpType::Round, inf, inf); - runUnaryNumOpTest(OpType::Round, -inf, -inf); - runUnaryNumOpTest(OpType::Round, nan, 0); -} - -TEST_F(LLVMCodeBuilderTest, Abs) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Abs, 4.0, 4.0); - runUnaryNumOpTest(OpType::Abs, 3.2, 3.2); - runUnaryNumOpTest(OpType::Abs, -2.0, 2.0); - runUnaryNumOpTest(OpType::Abs, -2.5, 2.5); - runUnaryNumOpTest(OpType::Abs, -2.6, 2.6); - runUnaryNumOpTest(OpType::Abs, 0.0, 0.0); - runUnaryNumOpTest(OpType::Abs, -0.0, 0.0); - runUnaryNumOpTest(OpType::Abs, inf, inf); - runUnaryNumOpTest(OpType::Abs, -inf, inf); - runUnaryNumOpTest(OpType::Abs, nan, 0); -} - -TEST_F(LLVMCodeBuilderTest, Floor) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Floor, 4.0, 4.0); - runUnaryNumOpTest(OpType::Floor, 3.2, 3.0); - runUnaryNumOpTest(OpType::Floor, 3.5, 3.0); - runUnaryNumOpTest(OpType::Floor, 3.6, 3.0); - runUnaryNumOpTest(OpType::Floor, 0.0, 0.0); - runUnaryNumOpTest(OpType::Floor, -0.0, -0.0); - runUnaryNumOpTest(OpType::Floor, -2.4, -3.0); - runUnaryNumOpTest(OpType::Floor, -2.5, -3.0); - runUnaryNumOpTest(OpType::Floor, -2.6, -3.0); - runUnaryNumOpTest(OpType::Floor, -0.4, -1.0); - runUnaryNumOpTest(OpType::Floor, -0.5, -1.0); - runUnaryNumOpTest(OpType::Floor, -0.51, -1.0); - runUnaryNumOpTest(OpType::Floor, inf, inf); - runUnaryNumOpTest(OpType::Floor, -inf, -inf); - runUnaryNumOpTest(OpType::Floor, nan, 0); -} - -TEST_F(LLVMCodeBuilderTest, Ceil) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Ceil, 8.0, 8.0); - runUnaryNumOpTest(OpType::Ceil, 3.2, 4.0); - runUnaryNumOpTest(OpType::Ceil, 3.5, 4.0); - runUnaryNumOpTest(OpType::Ceil, 3.6, 4.0); - runUnaryNumOpTest(OpType::Ceil, 0.4, 1.0); - runUnaryNumOpTest(OpType::Ceil, 0.0, 0.0); - runUnaryNumOpTest(OpType::Ceil, -0.0, -0.0); - runUnaryNumOpTest(OpType::Ceil, -2.4, -2.0); - runUnaryNumOpTest(OpType::Ceil, -2.5, -2.0); - runUnaryNumOpTest(OpType::Ceil, -2.6, -2.0); - runUnaryNumOpTest(OpType::Ceil, -0.4, -0.0); - runUnaryNumOpTest(OpType::Ceil, -0.5, -0.0); - runUnaryNumOpTest(OpType::Ceil, -0.51, -0.0); - runUnaryNumOpTest(OpType::Ceil, inf, inf); - runUnaryNumOpTest(OpType::Ceil, -inf, -inf); - runUnaryNumOpTest(OpType::Ceil, nan, 0); -} - -TEST_F(LLVMCodeBuilderTest, Sqrt) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Sqrt, 16.0, 4.0); - runUnaryNumOpTest(OpType::Sqrt, 0.04, 0.2); - runUnaryNumOpTest(OpType::Sqrt, 0.0, 0.0); - runUnaryNumOpTest(OpType::Sqrt, -0.0, 0.0); - runUnaryNumOpTest(OpType::Sqrt, -4.0, -nan); // negative NaN shouldn't be a problem - runUnaryNumOpTest(OpType::Sqrt, inf, inf); - runUnaryNumOpTest(OpType::Sqrt, -inf, -nan); - runUnaryNumOpTest(OpType::Sqrt, nan, 0); -} - -TEST_F(LLVMCodeBuilderTest, Sin) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Sin, 30.0, 0.5); - runUnaryNumOpTest(OpType::Sin, 90.0, 1.0); - runUnaryNumOpTest(OpType::Sin, 2.8e-9, 0.0); - runUnaryNumOpTest(OpType::Sin, 2.9e-9, 1e-10); - runUnaryNumOpTest(OpType::Sin, 570.0, -0.5); - runUnaryNumOpTest(OpType::Sin, -30.0, -0.5); - runUnaryNumOpTest(OpType::Sin, -90.0, -1.0); - runUnaryNumOpTest(OpType::Sin, 0.0, 0.0); - runUnaryNumOpTest(OpType::Sin, -0.0, 0.0); - runUnaryNumOpTest(OpType::Sin, inf, -nan); // negative NaN shouldn't be a problem - runUnaryNumOpTest(OpType::Sin, -inf, -nan); - runUnaryNumOpTest(OpType::Sin, nan, 0); -} - -TEST_F(LLVMCodeBuilderTest, Cos) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Cos, 60.0, 0.5); - runUnaryNumOpTest(OpType::Cos, 90.0, 0.0); - runUnaryNumOpTest(OpType::Cos, 600.0, -0.5); - runUnaryNumOpTest(OpType::Cos, 89.9999999971352, 1e-10); - runUnaryNumOpTest(OpType::Cos, 89.999999999, 0.0); - runUnaryNumOpTest(OpType::Cos, -60.0, 0.5); - runUnaryNumOpTest(OpType::Cos, -90.0, 0.0); - runUnaryNumOpTest(OpType::Cos, 0.0, 1.0); - runUnaryNumOpTest(OpType::Cos, -0.0, 1.0); - runUnaryNumOpTest(OpType::Cos, inf, -nan); // negative NaN shouldn't be a problem - runUnaryNumOpTest(OpType::Cos, -inf, -nan); - runUnaryNumOpTest(OpType::Cos, nan, 1.0); -} - -TEST_F(LLVMCodeBuilderTest, Tan) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Tan, 45.0, 1.0); - runUnaryNumOpTest(OpType::Tan, 90.0, inf); - runUnaryNumOpTest(OpType::Tan, 270.0, -inf); - runUnaryNumOpTest(OpType::Tan, 450.0, inf); - runUnaryNumOpTest(OpType::Tan, -90.0, -inf); - runUnaryNumOpTest(OpType::Tan, -270.0, inf); - runUnaryNumOpTest(OpType::Tan, -450.0, -inf); - runUnaryNumOpTest(OpType::Tan, 180.0, 0.0); - runUnaryNumOpTest(OpType::Tan, -180.0, 0.0); - runUnaryNumOpTest(OpType::Tan, 2.87e-9, 1e-10); - runUnaryNumOpTest(OpType::Tan, 2.8647e-9, 0.0); - runUnaryNumOpTest(OpType::Tan, 0.0, 0.0); - runUnaryNumOpTest(OpType::Tan, -0.0, 0.0); - runUnaryNumOpTest(OpType::Tan, inf, -nan); // negative NaN shouldn't be a problem - runUnaryNumOpTest(OpType::Tan, -inf, -nan); - runUnaryNumOpTest(OpType::Tan, nan, 0.0); -} - -TEST_F(LLVMCodeBuilderTest, Asin) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Asin, 1.0, 90.0); - runUnaryNumOpTest(OpType::Asin, 0.5, 30.0); - runUnaryNumOpTest(OpType::Asin, 0.0, 0.0); - runUnaryNumOpTest(OpType::Asin, -0.0, 0.0); - runUnaryNumOpTest(OpType::Asin, -0.5, -30.0); - runUnaryNumOpTest(OpType::Asin, -1.0, -90.0); - runUnaryNumOpTest(OpType::Asin, 1.1, nan); - runUnaryNumOpTest(OpType::Asin, -1.2, nan); - runUnaryNumOpTest(OpType::Asin, inf, nan); - runUnaryNumOpTest(OpType::Asin, -inf, nan); - runUnaryNumOpTest(OpType::Asin, nan, 0.0); -} - -TEST_F(LLVMCodeBuilderTest, Acos) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Acos, 1.0, 0.0); - runUnaryNumOpTest(OpType::Acos, 0.5, 60.0); - runUnaryNumOpTest(OpType::Acos, 0.0, 90.0); - runUnaryNumOpTest(OpType::Acos, -0.0, 90.0); - runUnaryNumOpTest(OpType::Acos, -0.5, 120.0); - runUnaryNumOpTest(OpType::Acos, -1.0, 180.0); - runUnaryNumOpTest(OpType::Acos, 1.1, nan); - runUnaryNumOpTest(OpType::Acos, -1.2, nan); - runUnaryNumOpTest(OpType::Acos, inf, nan); - runUnaryNumOpTest(OpType::Acos, -inf, nan); - runUnaryNumOpTest(OpType::Acos, nan, 90.0); -} - -TEST_F(LLVMCodeBuilderTest, Atan) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Atan, 1.0, 45.0); - runUnaryNumOpTest(OpType::Atan, 0.0, 0.0); - runUnaryNumOpTest(OpType::Atan, -0.0, -0.0); - runUnaryNumOpTest(OpType::Atan, -1.0, -45.0); - runUnaryNumOpTest(OpType::Atan, inf, 90.0); - runUnaryNumOpTest(OpType::Atan, -inf, -90.0); - runUnaryNumOpTest(OpType::Atan, nan, 0.0); -} - -TEST_F(LLVMCodeBuilderTest, Ln) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Ln, std::exp(1.0), 1.0); - runUnaryNumOpTest(OpType::Ln, std::exp(2.0), 2.0); - runUnaryNumOpTest(OpType::Ln, std::exp(0.3), 0.3); - runUnaryNumOpTest(OpType::Ln, 1.0, 0.0); - runUnaryNumOpTest(OpType::Ln, 0.0, -inf); - runUnaryNumOpTest(OpType::Ln, -0.0, -inf); - runUnaryNumOpTest(OpType::Ln, -0.7, -nan); // negative NaN shouldn't be a problem - runUnaryNumOpTest(OpType::Ln, inf, inf); - runUnaryNumOpTest(OpType::Ln, -inf, -nan); - runUnaryNumOpTest(OpType::Ln, nan, -inf); -} - -TEST_F(LLVMCodeBuilderTest, Log10) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Log10, 10.0, 1.0); - runUnaryNumOpTest(OpType::Log10, 1000.0, 3.0); - runUnaryNumOpTest(OpType::Log10, 0.01, -2.0); - runUnaryNumOpTest(OpType::Log10, 0.0, -inf); - runUnaryNumOpTest(OpType::Log10, -0.0, -inf); - runUnaryNumOpTest(OpType::Log10, -0.7, nan); - runUnaryNumOpTest(OpType::Log10, inf, inf); - runUnaryNumOpTest(OpType::Log10, -inf, nan); - runUnaryNumOpTest(OpType::Log10, nan, -inf); -} - -TEST_F(LLVMCodeBuilderTest, Exp) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Exp, 1.0, std::exp(1.0)); - runUnaryNumOpTest(OpType::Exp, 0.5, std::exp(0.5)); - runUnaryNumOpTest(OpType::Exp, 0.0, 1.0); - runUnaryNumOpTest(OpType::Exp, -0.0, 1.0); - runUnaryNumOpTest(OpType::Exp, -0.7, std::exp(-0.7)); - runUnaryNumOpTest(OpType::Exp, inf, inf); - runUnaryNumOpTest(OpType::Exp, -inf, 0.0); - runUnaryNumOpTest(OpType::Exp, nan, 1.0); -} - -TEST_F(LLVMCodeBuilderTest, Exp10) -{ - static const double inf = std::numeric_limits::infinity(); - static const double nan = std::numeric_limits::quiet_NaN(); - - runUnaryNumOpTest(OpType::Exp10, 1.0, 10.0); - runUnaryNumOpTest(OpType::Exp10, 3.0, 1000.0); - runUnaryNumOpTest(OpType::Exp10, 0.0, 1.0); - runUnaryNumOpTest(OpType::Exp10, -0.0, 1.0); - runUnaryNumOpTest(OpType::Exp10, -1.0, 0.1); - runUnaryNumOpTest(OpType::Exp10, -5.0, 0.00001); - runUnaryNumOpTest(OpType::Exp10, inf, inf); - runUnaryNumOpTest(OpType::Exp10, -inf, 0.0); - runUnaryNumOpTest(OpType::Exp10, nan, 1.0); -} - -TEST_F(LLVMCodeBuilderTest, StringConcat) -{ - runOpTest(OpType::StringConcat, "Hello ", "world"); - runOpTest(OpType::StringConcat, "abc", "def"); - runOpTest(OpType::StringConcat, "ábč", "ďéfgh"); -} - -TEST_F(LLVMCodeBuilderTest, StringChar) -{ - runOpTest(OpType::StringChar, "Hello world", 1); - runOpTest(OpType::StringChar, "Hello world", 0); - runOpTest(OpType::StringChar, "Hello world", 11); - runOpTest(OpType::StringChar, "abc", 2); - runOpTest(OpType::StringChar, "abc", -1); - runOpTest(OpType::StringChar, "abc", 3); - runOpTest(OpType::StringChar, "ábč", 0); -} - -TEST_F(LLVMCodeBuilderTest, StringLength) -{ - runUnaryNumOpTest(OpType::StringLength, "Hello world", 11); - runUnaryNumOpTest(OpType::StringLength, "abc", 3); - runUnaryNumOpTest(OpType::StringLength, "abcdef", 6); - runUnaryNumOpTest(OpType::StringLength, "ábč", 3); -} - TEST_F(LLVMCodeBuilderTest, LocalVariables) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerLocalVariable *var1 = m_builder->createLocalVariable(Compiler::StaticType::Number); - CompilerLocalVariable *var2 = m_builder->createLocalVariable(Compiler::StaticType::Number); - CompilerLocalVariable *var3 = m_builder->createLocalVariable(Compiler::StaticType::Bool); - CompilerLocalVariable *var4 = m_builder->createLocalVariable(Compiler::StaticType::Bool); + CompilerLocalVariable *var1 = builder->createLocalVariable(Compiler::StaticType::Number); + CompilerLocalVariable *var2 = builder->createLocalVariable(Compiler::StaticType::Number); + CompilerLocalVariable *var3 = builder->createLocalVariable(Compiler::StaticType::Bool); + CompilerLocalVariable *var4 = builder->createLocalVariable(Compiler::StaticType::Bool); - CompilerValue *v = m_builder->addConstValue(5); - m_builder->createLocalVariableWrite(var1, v); + CompilerValue *v = builder->addConstValue(5); + builder->createLocalVariableWrite(var1, v); - v = m_builder->addConstValue(-23.5); - v = callConstFuncForType(ValueType::Number, v); - m_builder->createLocalVariableWrite(var2, v); + v = builder->addConstValue(-23.5); + v = m_utils.callConstFuncForType(ValueType::Number, v); + builder->createLocalVariableWrite(var2, v); - v = m_builder->addConstValue(5.2); - v = callConstFuncForType(ValueType::Number, v); - m_builder->createLocalVariableWrite(var2, v); + v = builder->addConstValue(5.2); + v = m_utils.callConstFuncForType(ValueType::Number, v); + builder->createLocalVariableWrite(var2, v); - v = m_builder->addConstValue(false); - m_builder->createLocalVariableWrite(var3, v); + v = builder->addConstValue(false); + builder->createLocalVariableWrite(var3, v); - v = m_builder->addConstValue(true); - m_builder->createLocalVariableWrite(var3, v); + v = builder->addConstValue(true); + builder->createLocalVariableWrite(var3, v); - v = m_builder->addConstValue(false); - v = callConstFuncForType(ValueType::Bool, v); - m_builder->createLocalVariableWrite(var4, v); + v = builder->addConstValue(false); + v = m_utils.callConstFuncForType(ValueType::Bool, v); + builder->createLocalVariableWrite(var4, v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { m_builder->addLocalVariableValue(var1) }); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { m_builder->addLocalVariableValue(var2) }); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { m_builder->addLocalVariableValue(var3) }); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { m_builder->addLocalVariableValue(var4) }); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { builder->addLocalVariableValue(var1) }); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { builder->addLocalVariableValue(var2) }); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { builder->addLocalVariableValue(var3) }); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { builder->addLocalVariableValue(var4) }); static const std::string expected = "5\n" @@ -1886,7 +338,7 @@ TEST_F(LLVMCodeBuilderTest, LocalVariables) "true\n" "false\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); Thread thread(&sprite, nullptr, &script); @@ -1900,8 +352,8 @@ TEST_F(LLVMCodeBuilderTest, WriteVariable) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalVar1 = std::make_shared("", ""); auto globalVar2 = std::make_shared("", ""); @@ -1917,33 +369,33 @@ TEST_F(LLVMCodeBuilderTest, WriteVariable) sprite.addVariable(localVar2); sprite.addVariable(localVar3); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addConstValue(5); - m_builder->createVariableWrite(globalVar1.get(), v); + CompilerValue *v = builder->addConstValue(5); + builder->createVariableWrite(globalVar1.get(), v); - v = m_builder->addConstValue(-23.5); - v = callConstFuncForType(ValueType::Number, v); - m_builder->createVariableWrite(globalVar2.get(), v); + v = builder->addConstValue(-23.5); + v = m_utils.callConstFuncForType(ValueType::Number, v); + builder->createVariableWrite(globalVar2.get(), v); - v = m_builder->addConstValue("test"); - m_builder->createVariableWrite(globalVar3.get(), v); + v = builder->addConstValue("test"); + builder->createVariableWrite(globalVar3.get(), v); - v = m_builder->addConstValue("abc"); - m_builder->createVariableWrite(localVar1.get(), v); + v = builder->addConstValue("abc"); + builder->createVariableWrite(localVar1.get(), v); - v = m_builder->addConstValue("hello world"); - v = callConstFuncForType(ValueType::String, v); - m_builder->createVariableWrite(localVar1.get(), v); + v = builder->addConstValue("hello world"); + v = m_utils.callConstFuncForType(ValueType::String, v); + builder->createVariableWrite(localVar1.get(), v); - v = m_builder->addConstValue(false); - m_builder->createVariableWrite(localVar2.get(), v); + v = builder->addConstValue(false); + builder->createVariableWrite(localVar2.get(), v); - v = m_builder->addConstValue(true); - v = callConstFuncForType(ValueType::Bool, v); - m_builder->createVariableWrite(localVar3.get(), v); + v = builder->addConstValue(true); + v = m_utils.callConstFuncForType(ValueType::Bool, v); + builder->createVariableWrite(localVar3.get(), v); - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -1963,63 +415,63 @@ TEST_F(LLVMCodeBuilderTest, Select) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); // Number - CompilerValue *v = m_builder->addConstValue(true); - v = m_builder->createSelect(v, m_builder->addConstValue(5.8), m_builder->addConstValue(-17.42), Compiler::StaticType::Number); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + CompilerValue *v = builder->addConstValue(true); + v = builder->createSelect(v, builder->addConstValue(5.8), builder->addConstValue(-17.42), Compiler::StaticType::Number); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addConstValue(false); - v = m_builder->createSelect(v, m_builder->addConstValue(5.8), m_builder->addConstValue(-17.42), Compiler::StaticType::Number); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue(false); + v = builder->createSelect(v, builder->addConstValue(5.8), builder->addConstValue(-17.42), Compiler::StaticType::Number); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); // Bool - v = m_builder->addConstValue(true); - v = m_builder->createSelect(v, m_builder->addConstValue(true), m_builder->addConstValue(false), Compiler::StaticType::Bool); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue(true); + v = builder->createSelect(v, builder->addConstValue(true), builder->addConstValue(false), Compiler::StaticType::Bool); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - v = m_builder->addConstValue(false); - v = m_builder->createSelect(v, m_builder->addConstValue(true), m_builder->addConstValue(false), Compiler::StaticType::Bool); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue(false); + v = builder->createSelect(v, builder->addConstValue(true), builder->addConstValue(false), Compiler::StaticType::Bool); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); // String - v = m_builder->addConstValue(true); - v = m_builder->createSelect(v, m_builder->addConstValue("hello"), m_builder->addConstValue("world"), Compiler::StaticType::String); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(true); + v = builder->createSelect(v, builder->addConstValue("hello"), builder->addConstValue("world"), Compiler::StaticType::String); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(false); - v = m_builder->createSelect(v, m_builder->addConstValue("hello"), m_builder->addConstValue("world"), Compiler::StaticType::String); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(false); + v = builder->createSelect(v, builder->addConstValue("hello"), builder->addConstValue("world"), Compiler::StaticType::String); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); // Different types - v = m_builder->addConstValue(true); - v = m_builder->createSelect(v, m_builder->addConstValue("543"), m_builder->addConstValue("true"), Compiler::StaticType::Number); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue(true); + v = builder->createSelect(v, builder->addConstValue("543"), builder->addConstValue("true"), Compiler::StaticType::Number); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addConstValue(false); - v = m_builder->createSelect(v, m_builder->addConstValue("543"), m_builder->addConstValue("true"), Compiler::StaticType::Number); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue(false); + v = builder->createSelect(v, builder->addConstValue("543"), builder->addConstValue("true"), Compiler::StaticType::Number); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addConstValue(true); - v = m_builder->createSelect(v, m_builder->addConstValue(1), m_builder->addConstValue("false"), Compiler::StaticType::Bool); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue(true); + v = builder->createSelect(v, builder->addConstValue(1), builder->addConstValue("false"), Compiler::StaticType::Bool); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - v = m_builder->addConstValue(false); - v = m_builder->createSelect(v, m_builder->addConstValue(1), m_builder->addConstValue("false"), Compiler::StaticType::Bool); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue(false); + v = builder->createSelect(v, builder->addConstValue(1), builder->addConstValue("false"), Compiler::StaticType::Bool); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); // Unknown types - v = m_builder->addConstValue(true); - v = m_builder->createSelect(v, m_builder->addConstValue("test"), m_builder->addConstValue(-456.2), Compiler::StaticType::Unknown); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(true); + v = builder->createSelect(v, builder->addConstValue("test"), builder->addConstValue(-456.2), Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(false); - v = m_builder->createSelect(v, m_builder->addConstValue("abc"), m_builder->addConstValue(true), Compiler::StaticType::Unknown); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(false); + v = builder->createSelect(v, builder->addConstValue("abc"), builder->addConstValue(true), Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); static const std::string expected = "5.8\n" @@ -2035,7 +487,7 @@ TEST_F(LLVMCodeBuilderTest, Select) "test\n" "true\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -2050,8 +502,8 @@ TEST_F(LLVMCodeBuilderTest, ReadVariable) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalVar1 = std::make_shared("", "", 87); auto globalVar2 = std::make_shared("", "", 6.4); @@ -2067,25 +519,25 @@ TEST_F(LLVMCodeBuilderTest, ReadVariable) sprite.addVariable(localVar2); sprite.addVariable(localVar3); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addVariableValue(globalVar1.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + CompilerValue *v = builder->addVariableValue(globalVar1.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addVariableValue(globalVar2.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(globalVar2.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addVariableValue(globalVar3.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(globalVar3.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addVariableValue(localVar1.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(localVar1.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addVariableValue(localVar2.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(localVar2.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addVariableValue(localVar3.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(localVar3.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); std::string expected; expected += globalVar1->value().toString() + '\n'; @@ -2095,7 +547,7 @@ TEST_F(LLVMCodeBuilderTest, ReadVariable) expected += localVar2->value().toString() + '\n'; expected += localVar3->value().toString() + '\n'; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -2109,25 +561,25 @@ TEST_F(LLVMCodeBuilderTest, ReadVariable) TEST_F(LLVMCodeBuilderTest, SyncVariablesBeforeCallingFunction) { Sprite sprite; - sprite.setEngine(&m_engine); + sprite.setEngine(&m_utils.engine()); auto var = std::make_shared("", ""); sprite.addVariable(var); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addConstValue("abc"); - m_builder->createVariableWrite(var.get(), v); + CompilerValue *v = builder->addConstValue("abc"); + builder->createVariableWrite(var.get(), v); - v = m_builder->addConstValue(123); - m_builder->createVariableWrite(var.get(), v); + v = builder->addConstValue(123); + builder->createVariableWrite(var.get(), v); - m_builder->addTargetFunctionCall("test_print_first_local_variable", Compiler::StaticType::Void, {}, {}); + builder->addTargetFunctionCall("test_print_first_local_variable", Compiler::StaticType::Void, {}, {}); - v = m_builder->addConstValue(456); - m_builder->createVariableWrite(var.get(), v); + v = builder->addConstValue(456); + builder->createVariableWrite(var.get(), v); - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); @@ -2143,35 +595,35 @@ TEST_F(LLVMCodeBuilderTest, CastNonRawValueToUnknownType) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto var = std::make_shared("", "", 87); stage.addVariable(var); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); // Unknown type - CompilerValue *v = m_builder->addVariableValue(var.get()); - m_builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + CompilerValue *v = builder->addVariableValue(var.get()); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); // Number - v = m_builder->addConstValue(23.5); - m_builder->createVariableWrite(var.get(), v); - v = m_builder->addVariableValue(var.get()); - m_builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + v = builder->addConstValue(23.5); + builder->createVariableWrite(var.get(), v); + v = builder->addVariableValue(var.get()); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); // String - v = m_builder->addConstValue("Hello world"); - m_builder->createVariableWrite(var.get(), v); - v = m_builder->addVariableValue(var.get()); - m_builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + v = builder->addConstValue("Hello world"); + builder->createVariableWrite(var.get(), v); + v = builder->addVariableValue(var.get()); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); // Bool - v = m_builder->addConstValue(true); - m_builder->createVariableWrite(var.get(), v); - v = m_builder->addVariableValue(var.get()); - m_builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + v = builder->addConstValue(true); + builder->createVariableWrite(var.get(), v); + v = builder->addVariableValue(var.get()); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); std::string expected; expected += var->value().toString() + '\n'; @@ -2179,7 +631,7 @@ TEST_F(LLVMCodeBuilderTest, CastNonRawValueToUnknownType) expected += "Hello world\n"; expected += "true\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); Thread thread(&sprite, nullptr, &script); @@ -2193,8 +645,8 @@ TEST_F(LLVMCodeBuilderTest, ClearList) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -2242,14 +694,14 @@ TEST_F(LLVMCodeBuilderTest, ClearList) localList3->append(false); strings[localList3.get()] = localList3->toString(); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - m_builder->createListClear(globalList1.get()); - m_builder->createListClear(globalList3.get()); - m_builder->createListClear(localList1.get()); - m_builder->createListClear(localList2.get()); + builder->createListClear(globalList1.get()); + builder->createListClear(globalList3.get()); + builder->createListClear(localList1.get()); + builder->createListClear(localList2.get()); - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -2270,8 +722,8 @@ TEST_F(LLVMCodeBuilderTest, RemoveFromList) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -2291,27 +743,27 @@ TEST_F(LLVMCodeBuilderTest, RemoveFromList) localList->append("sit"); strings[localList.get()] = localList->toString(); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addConstValue(1); - m_builder->createListRemove(globalList.get(), v); + CompilerValue *v = builder->addConstValue(1); + builder->createListRemove(globalList.get(), v); - v = m_builder->addConstValue(-1); - m_builder->createListRemove(globalList.get(), v); + v = builder->addConstValue(-1); + builder->createListRemove(globalList.get(), v); - v = m_builder->addConstValue(3); - m_builder->createListRemove(globalList.get(), v); + v = builder->addConstValue(3); + builder->createListRemove(globalList.get(), v); - v = m_builder->addConstValue(3); - m_builder->createListRemove(localList.get(), v); + v = builder->addConstValue(3); + builder->createListRemove(localList.get(), v); - v = m_builder->addConstValue(-1); - m_builder->createListRemove(localList.get(), v); + v = builder->addConstValue(-1); + builder->createListRemove(localList.get(), v); - v = m_builder->addConstValue(4); - m_builder->createListRemove(localList.get(), v); + v = builder->addConstValue(4); + builder->createListRemove(localList.get(), v); - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -2327,8 +779,8 @@ TEST_F(LLVMCodeBuilderTest, AppendToList) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -2348,29 +800,29 @@ TEST_F(LLVMCodeBuilderTest, AppendToList) localList->append("sit"); strings[localList.get()] = localList->toString(); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addConstValue(1); - m_builder->createListAppend(globalList.get(), v); + CompilerValue *v = builder->addConstValue(1); + builder->createListAppend(globalList.get(), v); - v = m_builder->addConstValue("test"); - m_builder->createListAppend(globalList.get(), v); + v = builder->addConstValue("test"); + builder->createListAppend(globalList.get(), v); - v = m_builder->addConstValue(3); - m_builder->createListAppend(localList.get(), v); + v = builder->addConstValue(3); + builder->createListAppend(localList.get(), v); - m_builder->createListClear(localList.get()); + builder->createListClear(localList.get()); - v = m_builder->addConstValue(true); - m_builder->createListAppend(localList.get(), v); + v = builder->addConstValue(true); + builder->createListAppend(localList.get(), v); - v = m_builder->addConstValue(false); - m_builder->createListAppend(localList.get(), v); + v = builder->addConstValue(false); + builder->createListAppend(localList.get(), v); - v = m_builder->addConstValue("hello world"); - m_builder->createListAppend(localList.get(), v); + v = builder->addConstValue("hello world"); + builder->createListAppend(localList.get(), v); - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -2386,8 +838,8 @@ TEST_F(LLVMCodeBuilderTest, InsertToList) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -2407,47 +859,47 @@ TEST_F(LLVMCodeBuilderTest, InsertToList) localList->append("sit"); strings[localList.get()] = localList->toString(); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v1 = m_builder->addConstValue(2); - CompilerValue *v2 = m_builder->addConstValue(1); - m_builder->createListInsert(globalList.get(), v1, v2); + CompilerValue *v1 = builder->addConstValue(2); + CompilerValue *v2 = builder->addConstValue(1); + builder->createListInsert(globalList.get(), v1, v2); - v1 = m_builder->addConstValue(3); - v2 = m_builder->addConstValue("test"); - m_builder->createListInsert(globalList.get(), v1, v2); + v1 = builder->addConstValue(3); + v2 = builder->addConstValue("test"); + builder->createListInsert(globalList.get(), v1, v2); - v1 = m_builder->addConstValue(0); - v2 = m_builder->addConstValue(3); - m_builder->createListInsert(localList.get(), v1, v2); + v1 = builder->addConstValue(0); + v2 = builder->addConstValue(3); + builder->createListInsert(localList.get(), v1, v2); - m_builder->createListClear(localList.get()); + builder->createListClear(localList.get()); - v1 = m_builder->addConstValue(0); - v2 = m_builder->addConstValue(true); - m_builder->createListInsert(localList.get(), v1, v2); + v1 = builder->addConstValue(0); + v2 = builder->addConstValue(true); + builder->createListInsert(localList.get(), v1, v2); - v1 = m_builder->addConstValue(0); - v2 = m_builder->addConstValue(false); - m_builder->createListInsert(localList.get(), v1, v2); + v1 = builder->addConstValue(0); + v2 = builder->addConstValue(false); + builder->createListInsert(localList.get(), v1, v2); - v1 = m_builder->addConstValue(1); - v2 = m_builder->addConstValue("hello world"); - m_builder->createListInsert(localList.get(), v1, v2); + v1 = builder->addConstValue(1); + v2 = builder->addConstValue("hello world"); + builder->createListInsert(localList.get(), v1, v2); - v1 = m_builder->addConstValue(3); - v2 = m_builder->addConstValue("test"); - m_builder->createListInsert(localList.get(), v1, v2); + v1 = builder->addConstValue(3); + v2 = builder->addConstValue("test"); + builder->createListInsert(localList.get(), v1, v2); - v1 = m_builder->addConstValue(-1); - v2 = m_builder->addConstValue(123); - m_builder->createListInsert(localList.get(), v1, v2); + v1 = builder->addConstValue(-1); + v2 = builder->addConstValue(123); + builder->createListInsert(localList.get(), v1, v2); - v1 = m_builder->addConstValue(6); - v2 = m_builder->addConstValue(123); - m_builder->createListInsert(localList.get(), v1, v2); + v1 = builder->addConstValue(6); + v2 = builder->addConstValue(123); + builder->createListInsert(localList.get(), v1, v2); - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -2463,8 +915,8 @@ TEST_F(LLVMCodeBuilderTest, ListReplace) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -2484,37 +936,37 @@ TEST_F(LLVMCodeBuilderTest, ListReplace) localList->append("sit"); strings[localList.get()] = localList->toString(); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v1 = m_builder->addConstValue(2); - CompilerValue *v2 = m_builder->addConstValue(1); - m_builder->createListReplace(globalList.get(), v1, v2); + CompilerValue *v1 = builder->addConstValue(2); + CompilerValue *v2 = builder->addConstValue(1); + builder->createListReplace(globalList.get(), v1, v2); - v1 = m_builder->addConstValue(1); - v2 = m_builder->addConstValue("test"); - m_builder->createListReplace(globalList.get(), v1, v2); + v1 = builder->addConstValue(1); + v2 = builder->addConstValue("test"); + builder->createListReplace(globalList.get(), v1, v2); - v1 = m_builder->addConstValue(0); - v2 = m_builder->addConstValue(3); - m_builder->createListReplace(localList.get(), v1, v2); + v1 = builder->addConstValue(0); + v2 = builder->addConstValue(3); + builder->createListReplace(localList.get(), v1, v2); - v1 = m_builder->addConstValue(2); - v2 = m_builder->addConstValue(true); - m_builder->createListReplace(localList.get(), v1, v2); + v1 = builder->addConstValue(2); + v2 = builder->addConstValue(true); + builder->createListReplace(localList.get(), v1, v2); - v1 = m_builder->addConstValue(3); - v2 = m_builder->addConstValue("hello world"); - m_builder->createListReplace(localList.get(), v1, v2); + v1 = builder->addConstValue(3); + v2 = builder->addConstValue("hello world"); + builder->createListReplace(localList.get(), v1, v2); - v1 = m_builder->addConstValue(-1); - v2 = m_builder->addConstValue(123); - m_builder->createListReplace(localList.get(), v1, v2); + v1 = builder->addConstValue(-1); + v2 = builder->addConstValue(123); + builder->createListReplace(localList.get(), v1, v2); - v1 = m_builder->addConstValue(5); - v2 = m_builder->addConstValue(123); - m_builder->createListReplace(localList.get(), v1, v2); + v1 = builder->addConstValue(5); + v2 = builder->addConstValue(123); + builder->createListReplace(localList.get(), v1, v2); - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -2530,8 +982,8 @@ TEST_F(LLVMCodeBuilderTest, GetListContents) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -2551,19 +1003,19 @@ TEST_F(LLVMCodeBuilderTest, GetListContents) localList->append("sit"); strings[localList.get()] = localList->toString(); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addListContents(globalList.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + CompilerValue *v = builder->addListContents(globalList.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addListContents(localList.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addListContents(localList.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); static const std::string expected = "123\n" "Lorem ipsum dolor sit\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -2581,8 +1033,8 @@ TEST_F(LLVMCodeBuilderTest, GetListItem) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalList = std::make_shared("", ""); stage.addList(globalList); @@ -2611,67 +1063,67 @@ TEST_F(LLVMCodeBuilderTest, GetListItem) localList3->append(true); localList3->append(false); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); // Global - CompilerValue *v = m_builder->addConstValue(2); - v = m_builder->addListItem(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + CompilerValue *v = builder->addConstValue(2); + v = builder->addListItem(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - CompilerValue *v1 = m_builder->addConstValue(1); - CompilerValue *v2 = m_builder->addConstValue("test"); - m_builder->createListReplace(globalList.get(), v1, v2); + CompilerValue *v1 = builder->addConstValue(1); + CompilerValue *v2 = builder->addConstValue("test"); + builder->createListReplace(globalList.get(), v1, v2); - v = m_builder->addConstValue(0); - v = m_builder->addListItem(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(0); + v = builder->addListItem(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(-1); - v = m_builder->addListItem(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(-1); + v = builder->addListItem(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(3); - v = m_builder->addListItem(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(3); + v = builder->addListItem(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); // Local 1 - v = m_builder->addConstValue(0); - v = m_builder->addListItem(localList1.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(0); + v = builder->addListItem(localList1.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(2); - v = m_builder->addListItem(localList1.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(2); + v = builder->addListItem(localList1.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(3); - v = m_builder->addListItem(localList1.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(3); + v = builder->addListItem(localList1.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(-1); - v = m_builder->addListItem(localList1.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(-1); + v = builder->addListItem(localList1.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(4); - v = m_builder->addListItem(localList1.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(4); + v = builder->addListItem(localList1.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); // Local 2 - v = m_builder->addConstValue(-1); - v = m_builder->addListItem(localList2.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue(-1); + v = builder->addListItem(localList2.get(), v); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addConstValue(2); - v = m_builder->addListItem(localList2.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue(2); + v = builder->addListItem(localList2.get(), v); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); // Local 3 - v = m_builder->addConstValue(-1); - v = m_builder->addListItem(localList3.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue(-1); + v = builder->addListItem(localList3.get(), v); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addConstValue(2); - v = m_builder->addListItem(localList3.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue(2); + v = builder->addListItem(localList3.get(), v); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); static const std::string expected = "3\n" @@ -2688,7 +1140,7 @@ TEST_F(LLVMCodeBuilderTest, GetListItem) "0\n" "0\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -2708,8 +1160,8 @@ TEST_F(LLVMCodeBuilderTest, GetListSize) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalList = std::make_shared("", ""); stage.addList(globalList); @@ -2726,19 +1178,19 @@ TEST_F(LLVMCodeBuilderTest, GetListSize) localList->append("dolor"); localList->append("sit"); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addListSize(globalList.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + CompilerValue *v = builder->addListSize(globalList.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addListSize(localList.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addListSize(localList.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); static const std::string expected = "3\n" "4\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -2756,8 +1208,8 @@ TEST_F(LLVMCodeBuilderTest, GetListItemIndex) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalList = std::make_shared("", ""); stage.addList(globalList); @@ -2774,51 +1226,51 @@ TEST_F(LLVMCodeBuilderTest, GetListItemIndex) localList->append("dolor"); localList->append("sit"); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addConstValue(2); - v = m_builder->addListItemIndex(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + CompilerValue *v = builder->addConstValue(2); + v = builder->addListItemIndex(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(1); - v = m_builder->addListItemIndex(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(1); + v = builder->addListItemIndex(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(0); - v = m_builder->addListItemIndex(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(0); + v = builder->addListItemIndex(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - CompilerValue *v1 = m_builder->addConstValue(1); - CompilerValue *v2 = m_builder->addConstValue("test"); - m_builder->createListReplace(globalList.get(), v1, v2); + CompilerValue *v1 = builder->addConstValue(1); + CompilerValue *v2 = builder->addConstValue("test"); + builder->createListReplace(globalList.get(), v1, v2); - v = m_builder->addConstValue(2); - v = m_builder->addListItemIndex(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(2); + v = builder->addListItemIndex(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(1); - v = m_builder->addListItemIndex(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(1); + v = builder->addListItemIndex(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue("test"); - v = m_builder->addListItemIndex(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue("test"); + v = builder->addListItemIndex(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue("abc"); - v = m_builder->addListItemIndex(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue("abc"); + v = builder->addListItemIndex(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue("doLor"); - v = m_builder->addListItemIndex(localList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue("doLor"); + v = builder->addListItemIndex(localList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(true); - v = m_builder->addListItemIndex(localList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(true); + v = builder->addListItemIndex(localList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue("site"); - v = m_builder->addListItemIndex(localList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue("site"); + v = builder->addListItemIndex(localList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); static const std::string expected = "1\n" @@ -2832,7 +1284,7 @@ TEST_F(LLVMCodeBuilderTest, GetListItemIndex) "-1\n" "-1\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -2850,8 +1302,8 @@ TEST_F(LLVMCodeBuilderTest, ListContainsItem) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalList = std::make_shared("", ""); stage.addList(globalList); @@ -2868,51 +1320,51 @@ TEST_F(LLVMCodeBuilderTest, ListContainsItem) localList->append("dolor"); localList->append("sit"); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addConstValue(2); - v = m_builder->addListContains(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + CompilerValue *v = builder->addConstValue(2); + v = builder->addListContains(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(1); - v = m_builder->addListContains(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(1); + v = builder->addListContains(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(0); - v = m_builder->addListContains(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(0); + v = builder->addListContains(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - CompilerValue *v1 = m_builder->addConstValue(1); - CompilerValue *v2 = m_builder->addConstValue("test"); - m_builder->createListReplace(globalList.get(), v1, v2); + CompilerValue *v1 = builder->addConstValue(1); + CompilerValue *v2 = builder->addConstValue("test"); + builder->createListReplace(globalList.get(), v1, v2); - v = m_builder->addConstValue(2); - v = m_builder->addListContains(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(2); + v = builder->addListContains(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(1); - v = m_builder->addListContains(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(1); + v = builder->addListContains(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue("test"); - v = m_builder->addListContains(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue("test"); + v = builder->addListContains(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue("abc"); - v = m_builder->addListContains(globalList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue("abc"); + v = builder->addListContains(globalList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue("doLor"); - v = m_builder->addListContains(localList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue("doLor"); + v = builder->addListContains(localList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(true); - v = m_builder->addListContains(localList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(true); + v = builder->addListContains(localList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue("site"); - v = m_builder->addListContains(localList.get(), v); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue("site"); + v = builder->addListContains(localList.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); static const std::string expected = "true\n" @@ -2926,7 +1378,7 @@ TEST_F(LLVMCodeBuilderTest, ListContainsItem) "false\n" "false\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -2942,40 +1394,41 @@ TEST_F(LLVMCodeBuilderTest, ListContainsItem) TEST_F(LLVMCodeBuilderTest, Yield) { - auto build = [this]() { - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + LLVMCodeBuilder *builder; + + auto build = [this, &builder]() { + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - CompilerValue *v = m_builder->addTargetFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {}, {}); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + CompilerValue *v = builder->addTargetFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {}, {}); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->yield(); + builder->yield(); - v = m_builder->addConstValue("1"); - v = m_builder->addTargetFunctionCall("test_function_1_arg_ret", Compiler::StaticType::String, { Compiler::StaticType::String }, { v }); - CompilerValue *v1 = m_builder->addConstValue("2"); - CompilerValue *v2 = m_builder->addConstValue(3); - m_builder - ->addTargetFunctionCall("test_function_3_args", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String }, { v, v1, v2 }); + v = builder->addConstValue("1"); + v = builder->addTargetFunctionCall("test_function_1_arg_ret", Compiler::StaticType::String, { Compiler::StaticType::String }, { v }); + CompilerValue *v1 = builder->addConstValue("2"); + CompilerValue *v2 = builder->addConstValue(3); + builder->addTargetFunctionCall("test_function_3_args", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String }, { v, v1, v2 }); - v = m_builder->addConstValue("test"); - v1 = m_builder->addConstValue("4"); - v2 = m_builder->addConstValue("5"); - v = m_builder->addTargetFunctionCall( + v = builder->addConstValue("test"); + v1 = builder->addConstValue("4"); + v2 = builder->addConstValue("5"); + v = builder->addTargetFunctionCall( "test_function_3_args_ret", Compiler::StaticType::String, { Compiler::StaticType::String, Compiler::StaticType::String, Compiler::StaticType::String }, { v, v1, v2 }); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); }; // Without warp - createBuilder(false); + builder = m_utils.createBuilder(false); build(); - auto code = m_builder->finalize(); - Script script(&m_target, nullptr, nullptr); + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); script.setCode(code); - Thread thread(&m_target, nullptr, &script); + Thread thread(&m_utils.target(), nullptr, &script); auto ctx = code->createExecutionContext(&thread); static const std::string expected1 = @@ -2983,7 +1436,7 @@ TEST_F(LLVMCodeBuilderTest, Yield) "no_args_ret\n" "1_arg no_args_output\n"; - EXPECT_CALL(m_target, isStage()).Times(3); + EXPECT_CALL(m_utils.target(), isStage()).Times(3); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected1); @@ -2995,14 +1448,14 @@ TEST_F(LLVMCodeBuilderTest, Yield) "3_args test 4 5\n" "1_arg 3_args_output\n"; - EXPECT_CALL(m_target, isStage()).Times(4); + EXPECT_CALL(m_utils.target(), isStage()).Times(4); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected2); ASSERT_TRUE(code->isFinished(ctx.get())); // Terminate unfinished coroutine - EXPECT_CALL(m_target, isStage()).Times(3); + EXPECT_CALL(m_utils.target(), isStage()).Times(3); testing::internal::CaptureStdout(); code->reset(ctx.get()); code->run(ctx.get()); @@ -3013,7 +1466,7 @@ TEST_F(LLVMCodeBuilderTest, Yield) ASSERT_TRUE(code->isFinished(ctx.get())); // Reset unfinished coroutine - EXPECT_CALL(m_target, isStage()).Times(3); + EXPECT_CALL(m_utils.target(), isStage()).Times(3); testing::internal::CaptureStdout(); code->reset(ctx.get()); code->run(ctx.get()); @@ -3022,7 +1475,7 @@ TEST_F(LLVMCodeBuilderTest, Yield) code->reset(ctx.get()); - EXPECT_CALL(m_target, isStage()).Times(3); + EXPECT_CALL(m_utils.target(), isStage()).Times(3); testing::internal::CaptureStdout(); code->reset(ctx.get()); code->run(ctx.get()); @@ -3030,9 +1483,9 @@ TEST_F(LLVMCodeBuilderTest, Yield) ASSERT_FALSE(code->isFinished(ctx.get())); // leave unfinished coroutine // With warp - createBuilder(true); + builder = m_utils.createBuilder(true); build(); - code = m_builder->finalize(); + code = builder->build(); ctx = code->createExecutionContext(&thread); static const std::string expected = @@ -3044,7 +1497,7 @@ TEST_F(LLVMCodeBuilderTest, Yield) "3_args test 4 5\n" "1_arg 3_args_output\n"; - EXPECT_CALL(m_target, isStage()).Times(7); + EXPECT_CALL(m_utils.target(), isStage()).Times(7); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -3054,8 +1507,8 @@ TEST_F(LLVMCodeBuilderTest, VariablesAfterSuspend) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalVar = std::make_shared("", "", 87); stage.addVariable(globalVar); @@ -3063,60 +1516,60 @@ TEST_F(LLVMCodeBuilderTest, VariablesAfterSuspend) auto localVar = std::make_shared("", "", "test"); sprite.addVariable(localVar); - createBuilder(&sprite, false); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, false); - CompilerValue *v = m_builder->addConstValue(12.5); - m_builder->createVariableWrite(globalVar.get(), v); + CompilerValue *v = builder->addConstValue(12.5); + builder->createVariableWrite(globalVar.get(), v); - v = m_builder->addConstValue(true); - m_builder->createVariableWrite(localVar.get(), v); + v = builder->addConstValue(true); + builder->createVariableWrite(localVar.get(), v); - m_builder->yield(); + builder->yield(); - v = m_builder->addVariableValue(globalVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(globalVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(localVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); + v = builder->addConstValue(2); + builder->beginRepeatLoop(v); { - v = m_builder->addConstValue(12.5); - m_builder->createVariableWrite(globalVar.get(), v); + v = builder->addConstValue(12.5); + builder->createVariableWrite(globalVar.get(), v); } - m_builder->endLoop(); + builder->endLoop(); - v = m_builder->addVariableValue(globalVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(globalVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(0); - m_builder->createVariableWrite(localVar.get(), v); + v = builder->addConstValue(0); + builder->createVariableWrite(localVar.get(), v); - m_builder->beginLoopCondition(); - v = m_builder->createCmpLT(m_builder->addVariableValue(localVar.get()), m_builder->addConstValue(3)); - m_builder->beginWhileLoop(v); - m_builder->endLoop(); + builder->beginLoopCondition(); + v = builder->createCmpLT(builder->addVariableValue(localVar.get()), builder->addConstValue(3)); + builder->beginWhileLoop(v); + builder->endLoop(); - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(localVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(0); - m_builder->createVariableWrite(localVar.get(), v); + v = builder->addConstValue(0); + builder->createVariableWrite(localVar.get(), v); - m_builder->beginLoopCondition(); - v = m_builder->createCmpEQ(m_builder->addVariableValue(localVar.get()), m_builder->addConstValue(2)); - m_builder->beginRepeatUntilLoop(v); - m_builder->endLoop(); + builder->beginLoopCondition(); + v = builder->createCmpEQ(builder->addVariableValue(localVar.get()), builder->addConstValue(2)); + builder->beginRepeatUntilLoop(v); + builder->endLoop(); - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(localVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); std::string expected = "hello world\n" "-4.8\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -3187,8 +1640,8 @@ TEST_F(LLVMCodeBuilderTest, ListsAfterSuspend) Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalList1 = std::make_shared("", ""); stage.addList(globalList1); @@ -3202,51 +1655,51 @@ TEST_F(LLVMCodeBuilderTest, ListsAfterSuspend) auto localList2 = std::make_shared("", ""); sprite.addList(localList2); - createBuilder(&sprite, false); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, false); - m_builder->createListClear(globalList1.get()); - m_builder->createListClear(globalList2.get()); - m_builder->createListClear(localList1.get()); - m_builder->createListClear(localList2.get()); + builder->createListClear(globalList1.get()); + builder->createListClear(globalList2.get()); + builder->createListClear(localList1.get()); + builder->createListClear(localList2.get()); - m_builder->createListAppend(globalList1.get(), m_builder->addConstValue(1)); - m_builder->createListAppend(globalList1.get(), m_builder->addConstValue(2)); - m_builder->createListAppend(globalList1.get(), m_builder->addConstValue(3)); + builder->createListAppend(globalList1.get(), builder->addConstValue(1)); + builder->createListAppend(globalList1.get(), builder->addConstValue(2)); + builder->createListAppend(globalList1.get(), builder->addConstValue(3)); - m_builder->createListAppend(globalList2.get(), m_builder->addConstValue(1)); - m_builder->createListAppend(globalList2.get(), m_builder->addConstValue(2)); - m_builder->createListAppend(globalList2.get(), m_builder->addConstValue(3)); + builder->createListAppend(globalList2.get(), builder->addConstValue(1)); + builder->createListAppend(globalList2.get(), builder->addConstValue(2)); + builder->createListAppend(globalList2.get(), builder->addConstValue(3)); - m_builder->createListReplace(globalList2.get(), m_builder->addConstValue(1), m_builder->addConstValue(12.5)); + builder->createListReplace(globalList2.get(), builder->addConstValue(1), builder->addConstValue(12.5)); - m_builder->createListInsert(localList1.get(), m_builder->addConstValue(0), m_builder->addConstValue("Lorem")); - m_builder->createListInsert(localList1.get(), m_builder->addConstValue(0), m_builder->addConstValue("ipsum")); + builder->createListInsert(localList1.get(), builder->addConstValue(0), builder->addConstValue("Lorem")); + builder->createListInsert(localList1.get(), builder->addConstValue(0), builder->addConstValue("ipsum")); - m_builder->createListInsert(localList2.get(), m_builder->addConstValue(0), m_builder->addConstValue(true)); - m_builder->createListInsert(localList2.get(), m_builder->addConstValue(0), m_builder->addConstValue(false)); + builder->createListInsert(localList2.get(), builder->addConstValue(0), builder->addConstValue(true)); + builder->createListInsert(localList2.get(), builder->addConstValue(0), builder->addConstValue(false)); - m_builder->yield(); + builder->yield(); - CompilerValue *v = m_builder->addListItem(globalList1.get(), m_builder->addConstValue(1)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + CompilerValue *v = builder->addListItem(globalList1.get(), builder->addConstValue(1)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addListItem(globalList1.get(), m_builder->addConstValue(2)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addListItem(globalList1.get(), builder->addConstValue(2)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addListItem(globalList2.get(), m_builder->addConstValue(1)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addListItem(globalList2.get(), builder->addConstValue(1)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addListItem(globalList2.get(), m_builder->addConstValue(0)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addListItem(globalList2.get(), builder->addConstValue(0)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addListItem(localList1.get(), m_builder->addConstValue(0)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addListItem(localList1.get(), builder->addConstValue(0)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addListItem(localList1.get(), m_builder->addConstValue(1)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addListItem(localList1.get(), builder->addConstValue(1)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addListItem(localList2.get(), m_builder->addConstValue(0)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addListItem(localList2.get(), builder->addConstValue(0)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); std::string expected = "2\n" @@ -3257,7 +1710,7 @@ TEST_F(LLVMCodeBuilderTest, ListsAfterSuspend) "-5.48\n" "hello\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -3278,170 +1731,170 @@ TEST_F(LLVMCodeBuilderTest, ListsAfterSuspend) TEST_F(LLVMCodeBuilderTest, IfStatement) { - createBuilder(true); + LLVMCodeBuilder *builder = m_utils.createBuilder(true); // Without else branch (const condition) - CompilerValue *v = m_builder->addConstValue("true"); - m_builder->beginIfStatement(v); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - m_builder->endIf(); + CompilerValue *v = builder->addConstValue("true"); + builder->beginIfStatement(v); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->endIf(); - v = m_builder->addConstValue("false"); - m_builder->beginIfStatement(v); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - m_builder->endIf(); + v = builder->addConstValue("false"); + builder->beginIfStatement(v); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->endIf(); // Without else branch (condition returned by function) - CompilerValue *v1 = m_builder->addTargetFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {}, {}); - CompilerValue *v2 = m_builder->addConstValue("no_args_output"); - v = m_builder->addFunctionCall("test_equals", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String }, { v1, v2 }); - m_builder->beginIfStatement(v); - v = m_builder->addConstValue(0); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->endIf(); - - v1 = m_builder->addTargetFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {}, {}); - v2 = m_builder->addConstValue(""); - v = m_builder->addFunctionCall("test_equals", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String }, { v1, v2 }); - m_builder->beginIfStatement(v); - v = m_builder->addConstValue(1); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->endIf(); + CompilerValue *v1 = builder->addTargetFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {}, {}); + CompilerValue *v2 = builder->addConstValue("no_args_output"); + v = builder->addFunctionCall("test_equals", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String }, { v1, v2 }); + builder->beginIfStatement(v); + v = builder->addConstValue(0); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->endIf(); + + v1 = builder->addTargetFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {}, {}); + v2 = builder->addConstValue(""); + v = builder->addFunctionCall("test_equals", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String }, { v1, v2 }); + builder->beginIfStatement(v); + v = builder->addConstValue(1); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->endIf(); // With else branch (const condition) - v = m_builder->addConstValue("true"); - m_builder->beginIfStatement(v); - v = m_builder->addConstValue(2); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->beginElseBranch(); - v = m_builder->addConstValue(3); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->endIf(); - - v = m_builder->addConstValue("false"); - m_builder->beginIfStatement(v); - v = m_builder->addConstValue(4); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->beginElseBranch(); - v = m_builder->addConstValue(5); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->endIf(); + v = builder->addConstValue("true"); + builder->beginIfStatement(v); + v = builder->addConstValue(2); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->beginElseBranch(); + v = builder->addConstValue(3); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->endIf(); + + v = builder->addConstValue("false"); + builder->beginIfStatement(v); + v = builder->addConstValue(4); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->beginElseBranch(); + v = builder->addConstValue(5); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->endIf(); // With else branch (condition returned by function) - v1 = m_builder->addTargetFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {}, {}); - v2 = m_builder->addConstValue("no_args_output"); - v = m_builder->addFunctionCall("test_equals", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String }, { v1, v2 }); - m_builder->beginIfStatement(v); - v = m_builder->addConstValue(6); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->beginElseBranch(); - v = m_builder->addConstValue(7); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->endIf(); - - v1 = m_builder->addTargetFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {}, {}); - v2 = m_builder->addConstValue(""); - v = m_builder->addFunctionCall("test_equals", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String }, { v1, v2 }); - m_builder->beginIfStatement(v); - v = m_builder->addConstValue(8); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->beginElseBranch(); - v = m_builder->addConstValue(9); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->endIf(); + v1 = builder->addTargetFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {}, {}); + v2 = builder->addConstValue("no_args_output"); + v = builder->addFunctionCall("test_equals", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String }, { v1, v2 }); + builder->beginIfStatement(v); + v = builder->addConstValue(6); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->beginElseBranch(); + v = builder->addConstValue(7); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->endIf(); + + v1 = builder->addTargetFunctionCall("test_function_no_args_ret", Compiler::StaticType::String, {}, {}); + v2 = builder->addConstValue(""); + v = builder->addFunctionCall("test_equals", Compiler::StaticType::Bool, { Compiler::StaticType::String, Compiler::StaticType::String }, { v1, v2 }); + builder->beginIfStatement(v); + v = builder->addConstValue(8); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->beginElseBranch(); + v = builder->addConstValue(9); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->endIf(); // Nested 1 - CompilerValue *str = m_builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String }, { m_builder->addConstValue("test") }); - v = m_builder->addConstValue(true); - m_builder->beginIfStatement(v); + CompilerValue *str = builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String }, { builder->addConstValue("test") }); + v = builder->addConstValue(true); + builder->beginIfStatement(v); { - v = m_builder->addConstValue(false); - m_builder->beginIfStatement(v); + v = builder->addConstValue(false); + builder->beginIfStatement(v); { - v = m_builder->addConstValue(0); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(0); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); } - m_builder->beginElseBranch(); + builder->beginElseBranch(); { - v = m_builder->addConstValue(1); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(1); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); // str should still be allocated - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { str }); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { str }); - v = m_builder->addConstValue(false); - m_builder->beginIfStatement(v); - m_builder->beginElseBranch(); + v = builder->addConstValue(false); + builder->beginIfStatement(v); + builder->beginElseBranch(); { - v = m_builder->addConstValue(2); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(2); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); } - m_builder->endIf(); + builder->endIf(); // str should still be allocated - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { str }); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { str }); } - m_builder->endIf(); + builder->endIf(); } - m_builder->beginElseBranch(); + builder->beginElseBranch(); { - v = m_builder->addConstValue(true); - m_builder->beginIfStatement(v); + v = builder->addConstValue(true); + builder->beginIfStatement(v); { - v = m_builder->addConstValue(3); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(3); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); } - m_builder->beginElseBranch(); + builder->beginElseBranch(); { - v = m_builder->addConstValue(4); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(4); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); } - m_builder->endIf(); + builder->endIf(); } - m_builder->endIf(); + builder->endIf(); // str should still be allocated - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { str }); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { str }); // Nested 2 - v = m_builder->addConstValue(false); - m_builder->beginIfStatement(v); + v = builder->addConstValue(false); + builder->beginIfStatement(v); { - v = m_builder->addConstValue(false); - m_builder->beginIfStatement(v); + v = builder->addConstValue(false); + builder->beginIfStatement(v); { - v = m_builder->addConstValue(5); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(5); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); } - m_builder->beginElseBranch(); + builder->beginElseBranch(); { - v = m_builder->addConstValue(6); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(6); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); } - m_builder->endIf(); + builder->endIf(); } - m_builder->beginElseBranch(); + builder->beginElseBranch(); { - str = m_builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String }, { m_builder->addConstValue("test") }); + str = builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String }, { builder->addConstValue("test") }); - v = m_builder->addConstValue(true); - m_builder->beginIfStatement(v); + v = builder->addConstValue(true); + builder->beginIfStatement(v); { - v = m_builder->addConstValue(7); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(7); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); } - m_builder->beginElseBranch(); - m_builder->endIf(); + builder->beginElseBranch(); + builder->endIf(); // str should still be allocated - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { str }); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { str }); } - m_builder->endIf(); + builder->endIf(); - auto code = m_builder->finalize(); - Script script(&m_target, nullptr, nullptr); + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); script.setCode(code); - Thread thread(&m_target, nullptr, &script); + Thread thread(&m_utils.target(), nullptr, &script); auto ctx = code->createExecutionContext(&thread); static const std::string expected = @@ -3463,7 +1916,7 @@ TEST_F(LLVMCodeBuilderTest, IfStatement) "1_arg 7\n" "test\n"; - EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false)); + EXPECT_CALL(m_utils.target(), isStage).WillRepeatedly(Return(false)); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -3473,8 +1926,8 @@ TEST_F(LLVMCodeBuilderTest, IfStatementVariables) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalVar = std::make_shared("", "", "test"); stage.addVariable(globalVar); @@ -3482,71 +1935,71 @@ TEST_F(LLVMCodeBuilderTest, IfStatementVariables) auto localVar = std::make_shared("", "", 87); sprite.addVariable(localVar); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addConstValue(12.5); - m_builder->createVariableWrite(globalVar.get(), v); + CompilerValue *v = builder->addConstValue(12.5); + builder->createVariableWrite(globalVar.get(), v); - v = m_builder->addConstValue(true); - m_builder->createVariableWrite(localVar.get(), v); + v = builder->addConstValue(true); + builder->createVariableWrite(localVar.get(), v); - v = m_builder->addConstValue(true); - m_builder->beginIfStatement(v); + v = builder->addConstValue(true); + builder->beginIfStatement(v); { - v = m_builder->addConstValue("hello world"); - m_builder->createVariableWrite(globalVar.get(), v); + v = builder->addConstValue("hello world"); + builder->createVariableWrite(globalVar.get(), v); } - m_builder->endIf(); + builder->endIf(); - v = m_builder->addVariableValue(globalVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(globalVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(12.5); - m_builder->createVariableWrite(globalVar.get(), v); + v = builder->addConstValue(12.5); + builder->createVariableWrite(globalVar.get(), v); - v = m_builder->addConstValue(false); - m_builder->beginIfStatement(v); + v = builder->addConstValue(false); + builder->beginIfStatement(v); { - v = m_builder->addConstValue("hello world"); - m_builder->createVariableWrite(globalVar.get(), v); + v = builder->addConstValue("hello world"); + builder->createVariableWrite(globalVar.get(), v); - v = m_builder->addConstValue(0); - m_builder->createVariableWrite(localVar.get(), v); + v = builder->addConstValue(0); + builder->createVariableWrite(localVar.get(), v); } - m_builder->endIf(); + builder->endIf(); - v = m_builder->addVariableValue(globalVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(globalVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(localVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(true); - m_builder->beginIfStatement(v); + v = builder->addConstValue(true); + builder->beginIfStatement(v); { - v = m_builder->addConstValue(true); - m_builder->beginIfStatement(v); + v = builder->addConstValue(true); + builder->beginIfStatement(v); { - v = m_builder->addConstValue(true); - m_builder->createVariableWrite(globalVar.get(), v); + v = builder->addConstValue(true); + builder->createVariableWrite(globalVar.get(), v); } - m_builder->endIf(); + builder->endIf(); - v = m_builder->addConstValue(false); - m_builder->beginIfStatement(v); + v = builder->addConstValue(false); + builder->beginIfStatement(v); { - v = m_builder->addConstValue(-8.2); - m_builder->createVariableWrite(localVar.get(), v); + v = builder->addConstValue(-8.2); + builder->createVariableWrite(localVar.get(), v); } - m_builder->endIf(); + builder->endIf(); } - m_builder->endIf(); + builder->endIf(); - v = m_builder->addVariableValue(globalVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(globalVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(localVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); std::string expected = "hello world\n" @@ -3555,7 +2008,7 @@ TEST_F(LLVMCodeBuilderTest, IfStatementVariables) "true\n" "true\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -3570,8 +2023,8 @@ TEST_F(LLVMCodeBuilderTest, IfStatementLists) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalList1 = std::make_shared("", ""); stage.addList(globalList1); @@ -3582,105 +2035,105 @@ TEST_F(LLVMCodeBuilderTest, IfStatementLists) auto localList = std::make_shared("", ""); sprite.addList(localList); - createBuilder(&sprite, false); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, false); - auto resetLists = [this, globalList1, globalList2, localList]() { - m_builder->createListClear(globalList1.get()); - m_builder->createListClear(globalList2.get()); - m_builder->createListClear(localList.get()); + auto resetLists = [this, builder, globalList1, globalList2, localList]() { + builder->createListClear(globalList1.get()); + builder->createListClear(globalList2.get()); + builder->createListClear(localList.get()); - m_builder->createListAppend(globalList1.get(), m_builder->addConstValue(1)); - m_builder->createListAppend(globalList1.get(), m_builder->addConstValue(2)); + builder->createListAppend(globalList1.get(), builder->addConstValue(1)); + builder->createListAppend(globalList1.get(), builder->addConstValue(2)); - m_builder->createListAppend(globalList2.get(), m_builder->addConstValue("hello")); - m_builder->createListAppend(globalList2.get(), m_builder->addConstValue("world")); + builder->createListAppend(globalList2.get(), builder->addConstValue("hello")); + builder->createListAppend(globalList2.get(), builder->addConstValue("world")); - m_builder->createListAppend(localList.get(), m_builder->addConstValue(false)); - m_builder->createListAppend(localList.get(), m_builder->addConstValue(true)); + builder->createListAppend(localList.get(), builder->addConstValue(false)); + builder->createListAppend(localList.get(), builder->addConstValue(true)); }; - auto checkLists = [this, globalList1, globalList2, localList]() { - CompilerValue *v = m_builder->addListItem(globalList1.get(), m_builder->addConstValue(0)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + auto checkLists = [this, builder, globalList1, globalList2, localList]() { + CompilerValue *v = builder->addListItem(globalList1.get(), builder->addConstValue(0)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addListItem(globalList2.get(), m_builder->addConstValue(1)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addListItem(globalList2.get(), builder->addConstValue(1)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addListItem(localList.get(), m_builder->addConstValue(0)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addListItem(localList.get(), builder->addConstValue(0)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); }; // if (true) resetLists(); CompilerValue *v, *v1, *v2; - v = m_builder->addConstValue(true); - m_builder->beginIfStatement(v); + v = builder->addConstValue(true); + builder->beginIfStatement(v); { - v = m_builder->addConstValue("hello world"); - m_builder->createListAppend(globalList1.get(), v); + v = builder->addConstValue("hello world"); + builder->createListAppend(globalList1.get(), v); - v1 = m_builder->addConstValue(0); - v2 = m_builder->addConstValue(8.5); - m_builder->createListInsert(globalList2.get(), v1, v2); + v1 = builder->addConstValue(0); + v2 = builder->addConstValue(8.5); + builder->createListInsert(globalList2.get(), v1, v2); - v1 = m_builder->addConstValue(1); - v2 = m_builder->addConstValue(-4.25); - m_builder->createListReplace(localList.get(), v1, v2); + v1 = builder->addConstValue(1); + v2 = builder->addConstValue(-4.25); + builder->createListReplace(localList.get(), v1, v2); } - m_builder->endIf(); + builder->endIf(); checkLists(); // if (false) resetLists(); - v = m_builder->addConstValue(false); - m_builder->beginIfStatement(v); + v = builder->addConstValue(false); + builder->beginIfStatement(v); { - v = m_builder->addConstValue("hello world"); - m_builder->createListAppend(globalList1.get(), v); + v = builder->addConstValue("hello world"); + builder->createListAppend(globalList1.get(), v); - v1 = m_builder->addConstValue(0); - v2 = m_builder->addConstValue(8.5); - m_builder->createListInsert(globalList2.get(), v1, v2); + v1 = builder->addConstValue(0); + v2 = builder->addConstValue(8.5); + builder->createListInsert(globalList2.get(), v1, v2); - v1 = m_builder->addConstValue(1); - v2 = m_builder->addConstValue(-4.25); - m_builder->createListReplace(localList.get(), v1, v2); + v1 = builder->addConstValue(1); + v2 = builder->addConstValue(-4.25); + builder->createListReplace(localList.get(), v1, v2); } - m_builder->endIf(); + builder->endIf(); checkLists(); // if (true) { if (true) { ... }; if (false) { ... } } resetLists(); - v = m_builder->addConstValue(true); - m_builder->beginIfStatement(v); + v = builder->addConstValue(true); + builder->beginIfStatement(v); { - v = m_builder->addConstValue(true); - m_builder->beginIfStatement(v); + v = builder->addConstValue(true); + builder->beginIfStatement(v); { - v = m_builder->addConstValue("hello world"); - m_builder->createListAppend(globalList1.get(), v); + v = builder->addConstValue("hello world"); + builder->createListAppend(globalList1.get(), v); - v1 = m_builder->addConstValue(0); - v2 = m_builder->addConstValue(8.5); - m_builder->createListInsert(globalList2.get(), v1, v2); + v1 = builder->addConstValue(0); + v2 = builder->addConstValue(8.5); + builder->createListInsert(globalList2.get(), v1, v2); } - m_builder->endIf(); + builder->endIf(); - v = m_builder->addConstValue(false); - m_builder->beginIfStatement(v); + v = builder->addConstValue(false); + builder->beginIfStatement(v); { - v1 = m_builder->addConstValue(1); - v2 = m_builder->addConstValue(-4.25); - m_builder->createListReplace(localList.get(), v1, v2); + v1 = builder->addConstValue(1); + v2 = builder->addConstValue(-4.25); + builder->createListReplace(localList.get(), v1, v2); } - m_builder->endIf(); + builder->endIf(); } - m_builder->endIf(); + builder->endIf(); checkLists(); @@ -3695,7 +2148,7 @@ TEST_F(LLVMCodeBuilderTest, IfStatementLists) "hello\n" "false\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -3708,80 +2161,80 @@ TEST_F(LLVMCodeBuilderTest, IfStatementLists) TEST_F(LLVMCodeBuilderTest, RepeatLoop) { - createBuilder(true); + LLVMCodeBuilder *builder = m_utils.createBuilder(true); // Const count - CompilerValue *v = m_builder->addConstValue("-5"); - m_builder->beginRepeatLoop(v); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); - - v = m_builder->addConstValue(0); - m_builder->beginRepeatLoop(v); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); - - v = m_builder->addConstValue(3); - m_builder->beginRepeatLoop(v); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); - - v = m_builder->addConstValue("2.4"); - m_builder->beginRepeatLoop(v); - v = m_builder->addConstValue(0); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->endLoop(); - - v = m_builder->addConstValue("2.5"); - m_builder->beginRepeatLoop(v); - v = m_builder->addConstValue(1); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->endLoop(); + CompilerValue *v = builder->addConstValue("-5"); + builder->beginRepeatLoop(v); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); + + v = builder->addConstValue(0); + builder->beginRepeatLoop(v); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); + + v = builder->addConstValue(3); + builder->beginRepeatLoop(v); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); + + v = builder->addConstValue("2.4"); + builder->beginRepeatLoop(v); + v = builder->addConstValue(0); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->endLoop(); + + v = builder->addConstValue("2.5"); + builder->beginRepeatLoop(v); + v = builder->addConstValue(1); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->endLoop(); // Count returned by function - v = m_builder->addConstValue(2); - v = callConstFuncForType(ValueType::Number, v); - m_builder->beginRepeatLoop(v); - CompilerValue *index = m_builder->addLoopIndex(); - m_builder->addTargetFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { index }); - m_builder->endLoop(); + v = builder->addConstValue(2); + v = m_utils.callConstFuncForType(ValueType::Number, v); + builder->beginRepeatLoop(v); + CompilerValue *index = builder->addLoopIndex(); + builder->addTargetFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { index }); + builder->endLoop(); // Nested - CompilerValue *str = m_builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String }, { m_builder->addConstValue("test") }); - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); + CompilerValue *str = builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String }, { builder->addConstValue("test") }); + v = builder->addConstValue(2); + builder->beginRepeatLoop(v); { - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); + v = builder->addConstValue(2); + builder->beginRepeatLoop(v); { - v = m_builder->addConstValue(1); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(1); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); // str should still be allocated - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { str }); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { str }); } - m_builder->endLoop(); + builder->endLoop(); - v = m_builder->addConstValue(2); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(2); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(3); - m_builder->beginRepeatLoop(v); + v = builder->addConstValue(3); + builder->beginRepeatLoop(v); { - index = m_builder->addLoopIndex(); - m_builder->addTargetFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { index }); + index = builder->addLoopIndex(); + builder->addTargetFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { index }); } - m_builder->endLoop(); + builder->endLoop(); } - m_builder->endLoop(); + builder->endLoop(); // str should still be allocated - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { str }); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { str }); - auto code = m_builder->finalize(); - Script script(&m_target, nullptr, nullptr); + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); script.setCode(code); - Thread thread(&m_target, nullptr, &script); + Thread thread(&m_utils.target(), nullptr, &script); auto ctx = code->createExecutionContext(&thread); static const std::string expected = @@ -3813,25 +2266,25 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop) "2\n" "test\n"; - EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false)); + EXPECT_CALL(m_utils.target(), isStage).WillRepeatedly(Return(false)); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); // Yield - createBuilder(false); + builder = m_utils.createBuilder(false); - v = m_builder->addConstValue(3); - m_builder->beginRepeatLoop(v); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); + v = builder->addConstValue(3); + builder->beginRepeatLoop(v); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); - code = m_builder->finalize(); + code = builder->build(); ctx = code->createExecutionContext(&thread); static const std::string expected1 = "no_args\n"; - EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false)); + EXPECT_CALL(m_utils.target(), isStage).WillRepeatedly(Return(false)); for (int i = 0; i < 3; i++) { testing::internal::CaptureStdout(); @@ -3846,26 +2299,26 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop) ASSERT_TRUE(code->isFinished(ctx.get())); // No warp no-op loop - createBuilder(false); + builder = m_utils.createBuilder(false); - v = m_builder->addConstValue(0); // don't yield - m_builder->beginRepeatLoop(v); - m_builder->endLoop(); + v = builder->addConstValue(0); // don't yield + builder->beginRepeatLoop(v); + builder->endLoop(); - code = m_builder->finalize(); + code = builder->build(); ctx = code->createExecutionContext(&thread); code->run(ctx.get()); ASSERT_TRUE(code->isFinished(ctx.get())); // Infinite no warp loop - createBuilder(false); + builder = m_utils.createBuilder(false); - v = m_builder->addConstValue("Infinity"); - m_builder->beginRepeatLoop(v); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); + v = builder->addConstValue("Infinity"); + builder->beginRepeatLoop(v); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); - code = m_builder->finalize(); + code = builder->build(); ctx = code->createExecutionContext(&thread); for (int i = 0; i < 10; i++) { @@ -3878,70 +2331,70 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop) TEST_F(LLVMCodeBuilderTest, WhileLoop) { - createBuilder(true); + LLVMCodeBuilder *builder = m_utils.createBuilder(true); // Const condition - m_builder->beginLoopCondition(); - CompilerValue *v = m_builder->addConstValue("false"); - m_builder->beginWhileLoop(v); - m_builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); - - m_builder->beginLoopCondition(); - v = m_builder->addConstValue(false); - m_builder->beginWhileLoop(v); - m_builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); + builder->beginLoopCondition(); + CompilerValue *v = builder->addConstValue("false"); + builder->beginWhileLoop(v); + builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); + + builder->beginLoopCondition(); + v = builder->addConstValue(false); + builder->beginWhileLoop(v); + builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); // Condition returned by function - m_builder->addFunctionCall("test_reset_counter", Compiler::StaticType::Void, {}, {}); - m_builder->beginLoopCondition(); - CompilerValue *v1 = m_builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {}, {}); - CompilerValue *v2 = m_builder->addConstValue(2); - v = m_builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { v1, v2 }); - m_builder->beginWhileLoop(v); - v = m_builder->addConstValue(0); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->addFunctionCall("test_increment_counter", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); + builder->addFunctionCall("test_reset_counter", Compiler::StaticType::Void, {}, {}); + builder->beginLoopCondition(); + CompilerValue *v1 = builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {}, {}); + CompilerValue *v2 = builder->addConstValue(2); + v = builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { v1, v2 }); + builder->beginWhileLoop(v); + v = builder->addConstValue(0); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->addFunctionCall("test_increment_counter", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); // Nested - m_builder->addFunctionCall("test_reset_counter", Compiler::StaticType::Void, {}, {}); - m_builder->beginLoopCondition(); - v1 = m_builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {}, {}); - v2 = m_builder->addConstValue(3); - v = m_builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { v1, v2 }); - m_builder->beginWhileLoop(v); + builder->addFunctionCall("test_reset_counter", Compiler::StaticType::Void, {}, {}); + builder->beginLoopCondition(); + v1 = builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {}, {}); + v2 = builder->addConstValue(3); + v = builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { v1, v2 }); + builder->beginWhileLoop(v); { - m_builder->beginLoopCondition(); - v1 = m_builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {}, {}); - v2 = m_builder->addConstValue(3); - v = m_builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { v1, v2 }); - m_builder->beginWhileLoop(v); + builder->beginLoopCondition(); + v1 = builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {}, {}); + v2 = builder->addConstValue(3); + v = builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { v1, v2 }); + builder->beginWhileLoop(v); { - v = m_builder->addConstValue(1); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->addFunctionCall("test_increment_counter", Compiler::StaticType::Void, {}, {}); + v = builder->addConstValue(1); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->addFunctionCall("test_increment_counter", Compiler::StaticType::Void, {}, {}); } - m_builder->endLoop(); + builder->endLoop(); - v = m_builder->addConstValue(2); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(2); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->beginLoopCondition(); - v = m_builder->addConstValue(false); - m_builder->beginWhileLoop(v); + builder->beginLoopCondition(); + v = builder->addConstValue(false); + builder->beginWhileLoop(v); { - m_builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {}, {}); + builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {}, {}); } - m_builder->endLoop(); + builder->endLoop(); } - m_builder->endLoop(); + builder->endLoop(); - auto code = m_builder->finalize(); - Script script(&m_target, nullptr, nullptr); + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); script.setCode(code); - Thread thread(&m_target, nullptr, &script); + Thread thread(&m_utils.target(), nullptr, &script); auto ctx = code->createExecutionContext(&thread); static const std::string expected = @@ -3952,26 +2405,26 @@ TEST_F(LLVMCodeBuilderTest, WhileLoop) "1_arg 1\n" "1_arg 2\n"; - EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false)); + EXPECT_CALL(m_utils.target(), isStage).WillRepeatedly(Return(false)); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); // Yield - createBuilder(false); + builder = m_utils.createBuilder(false); - m_builder->beginLoopCondition(); - v = m_builder->addConstValue(true); - m_builder->beginWhileLoop(v); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); + builder->beginLoopCondition(); + v = builder->addConstValue(true); + builder->beginWhileLoop(v); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); - code = m_builder->finalize(); + code = builder->build(); ctx = code->createExecutionContext(&thread); static const std::string expected1 = "no_args\n"; - EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false)); + EXPECT_CALL(m_utils.target(), isStage).WillRepeatedly(Return(false)); for (int i = 0; i < 10; i++) { testing::internal::CaptureStdout(); @@ -3983,73 +2436,73 @@ TEST_F(LLVMCodeBuilderTest, WhileLoop) TEST_F(LLVMCodeBuilderTest, RepeatUntilLoop) { - createBuilder(true); + LLVMCodeBuilder *builder = m_utils.createBuilder(true); // Const condition - m_builder->beginLoopCondition(); - CompilerValue *v = m_builder->addConstValue("true"); - m_builder->beginRepeatUntilLoop(v); - m_builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); - - m_builder->beginLoopCondition(); - v = m_builder->addConstValue(true); - m_builder->beginRepeatUntilLoop(v); - m_builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); + builder->beginLoopCondition(); + CompilerValue *v = builder->addConstValue("true"); + builder->beginRepeatUntilLoop(v); + builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); + + builder->beginLoopCondition(); + v = builder->addConstValue(true); + builder->beginRepeatUntilLoop(v); + builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); // Condition returned by function - m_builder->addFunctionCall("test_reset_counter", Compiler::StaticType::Void, {}, {}); - m_builder->beginLoopCondition(); - CompilerValue *v1 = m_builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {}, {}); - CompilerValue *v2 = m_builder->addConstValue(2); - v = m_builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { v1, v2 }); - v = m_builder->addFunctionCall("test_not", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { v }); - m_builder->beginRepeatUntilLoop(v); - v = m_builder->addConstValue(0); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->addFunctionCall("test_increment_counter", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); + builder->addFunctionCall("test_reset_counter", Compiler::StaticType::Void, {}, {}); + builder->beginLoopCondition(); + CompilerValue *v1 = builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {}, {}); + CompilerValue *v2 = builder->addConstValue(2); + v = builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { v1, v2 }); + v = builder->addFunctionCall("test_not", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { v }); + builder->beginRepeatUntilLoop(v); + v = builder->addConstValue(0); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->addFunctionCall("test_increment_counter", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); // Nested - m_builder->addFunctionCall("test_reset_counter", Compiler::StaticType::Void, {}, {}); - m_builder->beginLoopCondition(); - v1 = m_builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {}, {}); - v2 = m_builder->addConstValue(3); - v = m_builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { v1, v2 }); - v = m_builder->addFunctionCall("test_not", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { v }); - m_builder->beginRepeatUntilLoop(v); + builder->addFunctionCall("test_reset_counter", Compiler::StaticType::Void, {}, {}); + builder->beginLoopCondition(); + v1 = builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {}, {}); + v2 = builder->addConstValue(3); + v = builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { v1, v2 }); + v = builder->addFunctionCall("test_not", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { v }); + builder->beginRepeatUntilLoop(v); { - m_builder->beginLoopCondition(); - v1 = m_builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {}, {}); - v2 = m_builder->addConstValue(3); - v = m_builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { v1, v2 }); - v = m_builder->addFunctionCall("test_not", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { v }); - m_builder->beginRepeatUntilLoop(v); + builder->beginLoopCondition(); + v1 = builder->addFunctionCall("test_get_counter", Compiler::StaticType::Number, {}, {}); + v2 = builder->addConstValue(3); + v = builder->addFunctionCall("test_lower_than", Compiler::StaticType::Bool, { Compiler::StaticType::Number, Compiler::StaticType::Number }, { v1, v2 }); + v = builder->addFunctionCall("test_not", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { v }); + builder->beginRepeatUntilLoop(v); { - v = m_builder->addConstValue(1); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->addFunctionCall("test_increment_counter", Compiler::StaticType::Void, {}, {}); + v = builder->addConstValue(1); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->addFunctionCall("test_increment_counter", Compiler::StaticType::Void, {}, {}); } - m_builder->endLoop(); + builder->endLoop(); - v = m_builder->addConstValue(2); - m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(2); + builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->beginLoopCondition(); - v = m_builder->addConstValue(true); - m_builder->beginRepeatUntilLoop(v); + builder->beginLoopCondition(); + v = builder->addConstValue(true); + builder->beginRepeatUntilLoop(v); { - m_builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {}, {}); + builder->addFunctionCall("test_unreachable", Compiler::StaticType::Void, {}, {}); } - m_builder->endLoop(); + builder->endLoop(); } - m_builder->endLoop(); + builder->endLoop(); - auto code = m_builder->finalize(); - Script script(&m_target, nullptr, nullptr); + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); script.setCode(code); - Thread thread(&m_target, nullptr, &script); + Thread thread(&m_utils.target(), nullptr, &script); auto ctx = code->createExecutionContext(&thread); static const std::string expected = @@ -4060,26 +2513,26 @@ TEST_F(LLVMCodeBuilderTest, RepeatUntilLoop) "1_arg 1\n" "1_arg 2\n"; - EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false)); + EXPECT_CALL(m_utils.target(), isStage).WillRepeatedly(Return(false)); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); // Yield - createBuilder(false); + builder = m_utils.createBuilder(false); - m_builder->beginLoopCondition(); - v = m_builder->addConstValue(false); - m_builder->beginRepeatUntilLoop(v); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); + builder->beginLoopCondition(); + v = builder->addConstValue(false); + builder->beginRepeatUntilLoop(v); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); - code = m_builder->finalize(); + code = builder->build(); ctx = code->createExecutionContext(&thread); static const std::string expected1 = "no_args\n"; - EXPECT_CALL(m_target, isStage).WillRepeatedly(Return(false)); + EXPECT_CALL(m_utils.target(), isStage).WillRepeatedly(Return(false)); for (int i = 0; i < 10; i++) { testing::internal::CaptureStdout(); @@ -4093,8 +2546,8 @@ TEST_F(LLVMCodeBuilderTest, LoopVariables) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalVar = std::make_shared("", "", "test"); stage.addVariable(globalVar); @@ -4106,103 +2559,103 @@ TEST_F(LLVMCodeBuilderTest, LoopVariables) sprite.addVariable(counter1); sprite.addVariable(counter2); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addConstValue(12.5); - m_builder->createVariableWrite(globalVar.get(), v); + CompilerValue *v = builder->addConstValue(12.5); + builder->createVariableWrite(globalVar.get(), v); - v = m_builder->addConstValue(true); - m_builder->createVariableWrite(localVar.get(), v); + v = builder->addConstValue(true); + builder->createVariableWrite(localVar.get(), v); - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); + v = builder->addConstValue(2); + builder->beginRepeatLoop(v); { - v = m_builder->addConstValue("hello world"); - m_builder->createVariableWrite(globalVar.get(), v); + v = builder->addConstValue("hello world"); + builder->createVariableWrite(globalVar.get(), v); } - m_builder->endLoop(); + builder->endLoop(); - v = m_builder->addVariableValue(globalVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(globalVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(12.5); - m_builder->createVariableWrite(globalVar.get(), v); + v = builder->addConstValue(12.5); + builder->createVariableWrite(globalVar.get(), v); - v = m_builder->addConstValue(0); - m_builder->beginRepeatLoop(v); + v = builder->addConstValue(0); + builder->beginRepeatLoop(v); { - v = m_builder->addConstValue("hello world"); - m_builder->createVariableWrite(globalVar.get(), v); + v = builder->addConstValue("hello world"); + builder->createVariableWrite(globalVar.get(), v); - v = m_builder->addConstValue(0); - m_builder->createVariableWrite(localVar.get(), v); + v = builder->addConstValue(0); + builder->createVariableWrite(localVar.get(), v); } - m_builder->endLoop(); + builder->endLoop(); - v = m_builder->addVariableValue(globalVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(globalVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(localVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(0); - m_builder->createVariableWrite(counter1.get(), v); + v = builder->addConstValue(0); + builder->createVariableWrite(counter1.get(), v); - m_builder->beginLoopCondition(); - CompilerValue *v1 = m_builder->addVariableValue(counter1.get()); - CompilerValue *v2 = m_builder->addConstValue(5); - v = m_builder->createCmpLT(v1, v2); - m_builder->beginWhileLoop(v); + builder->beginLoopCondition(); + CompilerValue *v1 = builder->addVariableValue(counter1.get()); + CompilerValue *v2 = builder->addConstValue(5); + v = builder->createCmpLT(v1, v2); + builder->beginWhileLoop(v); { - v = m_builder->addConstValue(0); - m_builder->createVariableWrite(counter2.get(), v); - - m_builder->beginLoopCondition(); - v1 = m_builder->addVariableValue(counter2.get()); - v2 = m_builder->addConstValue(3); - v = m_builder->createCmpEQ(v1, v2); - m_builder->beginRepeatUntilLoop(v); + v = builder->addConstValue(0); + builder->createVariableWrite(counter2.get(), v); + + builder->beginLoopCondition(); + v1 = builder->addVariableValue(counter2.get()); + v2 = builder->addConstValue(3); + v = builder->createCmpEQ(v1, v2); + builder->beginRepeatUntilLoop(v); { - v = m_builder->addConstValue(true); - m_builder->createVariableWrite(globalVar.get(), v); + v = builder->addConstValue(true); + builder->createVariableWrite(globalVar.get(), v); - v1 = m_builder->addVariableValue(counter2.get()); - v2 = m_builder->addConstValue(1); - v = m_builder->createAdd(v1, v2); - m_builder->createVariableWrite(counter2.get(), v); + v1 = builder->addVariableValue(counter2.get()); + v2 = builder->addConstValue(1); + v = builder->createAdd(v1, v2); + builder->createVariableWrite(counter2.get(), v); } - m_builder->endLoop(); + builder->endLoop(); - m_builder->beginLoopCondition(); - v = m_builder->addConstValue(false); - m_builder->beginWhileLoop(v); + builder->beginLoopCondition(); + v = builder->addConstValue(false); + builder->beginWhileLoop(v); { - v = m_builder->addConstValue(-8.2); - m_builder->createVariableWrite(localVar.get(), v); + v = builder->addConstValue(-8.2); + builder->createVariableWrite(localVar.get(), v); } - m_builder->endLoop(); + builder->endLoop(); - v1 = m_builder->addVariableValue(counter1.get()); - v2 = m_builder->addConstValue(1); - v = m_builder->createAdd(v1, v2); - m_builder->createVariableWrite(counter1.get(), v); + v1 = builder->addVariableValue(counter1.get()); + v2 = builder->addConstValue(1); + v = builder->createAdd(v1, v2); + builder->createVariableWrite(counter1.get(), v); } - m_builder->endLoop(); + builder->endLoop(); - m_builder->beginLoopCondition(); - v = m_builder->addConstValue(true); - m_builder->beginRepeatUntilLoop(v); + builder->beginLoopCondition(); + v = builder->addConstValue(true); + builder->beginRepeatUntilLoop(v); { - v = m_builder->addConstValue(-8.2); - m_builder->createVariableWrite(localVar.get(), v); + v = builder->addConstValue(-8.2); + builder->createVariableWrite(localVar.get(), v); } - m_builder->endLoop(); + builder->endLoop(); - v = m_builder->addVariableValue(globalVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(globalVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(localVar.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); std::string expected = "hello world\n" @@ -4211,7 +2664,7 @@ TEST_F(LLVMCodeBuilderTest, LoopVariables) "true\n" "true\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); ; @@ -4222,12 +2675,13 @@ TEST_F(LLVMCodeBuilderTest, LoopVariables) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } -TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis1) +TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis) { + // This just makes sure the type analyzer is used correctly Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalVar = std::make_shared("", ""); stage.addVariable(globalVar); @@ -4235,28 +2689,28 @@ TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis1) auto localVar = std::make_shared("", ""); sprite.addVariable(localVar); - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addConstValue(5.25); - m_builder->createVariableWrite(localVar.get(), v); + CompilerValue *v = builder->addConstValue(5.25); + builder->createVariableWrite(localVar.get(), v); - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); + v = builder->addConstValue(2); + builder->beginRepeatLoop(v); { // Type is unknown here because a string is assigned later - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addVariableValue(localVar.get()); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addConstValue("test"); - m_builder->createVariableWrite(localVar.get(), v); + v = builder->addConstValue("test"); + builder->createVariableWrite(localVar.get(), v); } - m_builder->endLoop(); + builder->endLoop(); std::string expected = "5.25\n" "0\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); Thread thread(&sprite, nullptr, &script); @@ -4266,1549 +2720,192 @@ TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis1) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } -TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis2) +TEST_F(LLVMCodeBuilderTest, LoopLists) { Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalVar = std::make_shared("", ""); - stage.addVariable(globalVar); - - auto localVar = std::make_shared("", ""); - sprite.addVariable(localVar); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); - createBuilder(&sprite, true); + auto globalList1 = std::make_shared("", ""); + stage.addList(globalList1); - CompilerValue *v = m_builder->addConstValue(5.25); - m_builder->createVariableWrite(localVar.get(), v); + auto globalList2 = std::make_shared("", ""); + stage.addList(globalList2); - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - // Type is known here because a number is assigned later - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + auto localList = std::make_shared("", ""); + sprite.addList(localList); - v = m_builder->addConstValue(12.5); - m_builder->createVariableWrite(localVar.get(), v); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "12.5\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis3) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalVar = std::make_shared("", ""); - stage.addVariable(globalVar); - - auto localVar = std::make_shared("", ""); - sprite.addVariable(localVar); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue(0); - m_builder->createVariableWrite(globalVar.get(), v); - - v = m_builder->addConstValue(5.25); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because a variable with unknown type is assigned (the variable has unknown type because a string is assigned later) - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addVariableValue(globalVar.get()); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue("test"); - m_builder->createVariableWrite(globalVar.get(), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "0\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis4) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalVar = std::make_shared("", ""); - stage.addVariable(globalVar); - - auto localVar = std::make_shared("", ""); - sprite.addVariable(localVar); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue(0); - m_builder->createVariableWrite(globalVar.get(), v); - - v = m_builder->addConstValue(5.25); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - // Type is known here because a variable is assigned which has a number assigned later - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addVariableValue(globalVar.get()); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(12.5); - m_builder->createVariableWrite(globalVar.get(), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "12.5\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis5) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalVar = std::make_shared("", ""); - stage.addVariable(globalVar); - - auto localVar = std::make_shared("", ""); - sprite.addVariable(localVar); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue("test"); - m_builder->createVariableWrite(globalVar.get(), v); - - v = m_builder->addConstValue(5.25); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(3); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because a string variable is assigned later which has a number assigned as well - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addVariableValue(globalVar.get()); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(12.5); - m_builder->createVariableWrite(globalVar.get(), v); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "0\n" - "12.5\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis6) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalVar = std::make_shared("", ""); - stage.addVariable(globalVar); - - auto localVar = std::make_shared("", ""); - sprite.addVariable(localVar); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue(5.25); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - // Type is known here because a variable with known type is assigned later (even though the variable has a string assigned later, there's a number assigned before the read operation) - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue(10); - m_builder->createVariableWrite(globalVar.get(), v); - - v = m_builder->addVariableValue(globalVar.get()); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue("test"); - m_builder->createVariableWrite(globalVar.get(), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "10\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis7) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalVar = std::make_shared("", ""); - stage.addVariable(globalVar); - - auto localVar = std::make_shared("", ""); - sprite.addVariable(localVar); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue(5.25); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because a variable with a known, but different type is assigned later - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue("test"); - m_builder->createVariableWrite(globalVar.get(), v); - - v = m_builder->addVariableValue(globalVar.get()); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(10); - m_builder->createVariableWrite(globalVar.get(), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "0\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis8) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalVar = std::make_shared("", ""); - stage.addVariable(globalVar); - - auto localVar = std::make_shared("", ""); - sprite.addVariable(localVar); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue(5.25); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because a variable with a known, but different type is assigned later - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue("test"); - m_builder->createVariableWrite(globalVar.get(), v); - - v = m_builder->addVariableValue(globalVar.get()); - m_builder->createVariableWrite(localVar.get(), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "0\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis9) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalVar = std::make_shared("", "", "123"); - stage.addVariable(globalVar); - - auto localVar = std::make_shared("", ""); - sprite.addVariable(localVar); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue(5.25); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addVariableValue(globalVar.get()); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(5); - m_builder->createVariableWrite(globalVar.get(), v); - } - m_builder->endLoop(); - - // Type is unknown here because a variable of unknown type is assigned before the read operation - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue(10); - m_builder->createVariableWrite(globalVar.get(), v); - - v = m_builder->addVariableValue(globalVar.get()); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue("test"); - m_builder->createVariableWrite(globalVar.get(), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "5\n" - "5\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis10) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalVar = std::make_shared("", ""); - stage.addVariable(globalVar); - - auto localVar = std::make_shared("", "", "123"); - sprite.addVariable(localVar); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue("test"); - m_builder->createVariableWrite(globalVar.get(), v); - - v = m_builder->addConstValue(3); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because a variable of unknown type is assigned later - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addVariableValue(globalVar.get()); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(12.5); - m_builder->createVariableWrite(globalVar.get(), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "123\n" - "0\n" - "12.5\n" - "12.5\n" - "0\n" - "12.5\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis11) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalVar = std::make_shared("", ""); - stage.addVariable(globalVar); - - auto localVar = std::make_shared("", "", "123"); - sprite.addVariable(localVar); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue("test"); - m_builder->createVariableWrite(globalVar.get(), v); - - v = m_builder->addConstValue(3); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because the variable is assigned to itself later - // This case is not checked because the code isn't considered valid - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addVariableValue(localVar.get()); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addConstValue(12.5); - m_builder->createVariableWrite(globalVar.get(), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "123\n" - "123\n" - "123\n" - "123\n" - "123\n" - "123\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, VariableLoopTypeAnalysis12) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalVar = std::make_shared("", ""); - stage.addVariable(globalVar); - - auto localVar = std::make_shared("", "", "123"); - sprite.addVariable(localVar); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue("test"); - m_builder->createVariableWrite(globalVar.get(), v); - - v = m_builder->addConstValue(3); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because another variable is assigned later, but it has this variable assigned - // This case is not checked because the code isn't considered valid - v = m_builder->addVariableValue(localVar.get()); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addVariableValue(globalVar.get()); - m_builder->createVariableWrite(localVar.get(), v); - - v = m_builder->addVariableValue(localVar.get()); - m_builder->createVariableWrite(globalVar.get(), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "123\n" - "0\n" - "0\n" - "0\n" - "0\n" - "0\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, LoopLists) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalList1 = std::make_shared("", ""); - stage.addList(globalList1); - - auto globalList2 = std::make_shared("", ""); - stage.addList(globalList2); - - auto localList = std::make_shared("", ""); - sprite.addList(localList); - - auto counter1 = std::make_shared("", ""); - auto counter2 = std::make_shared("", ""); - sprite.addVariable(counter1); - sprite.addVariable(counter2); - - createBuilder(&sprite, true); - - auto resetLists = [this, globalList1, globalList2, localList]() { - m_builder->createListClear(globalList1.get()); - m_builder->createListClear(globalList2.get()); - m_builder->createListClear(localList.get()); - - m_builder->createListAppend(globalList1.get(), m_builder->addConstValue(1)); - m_builder->createListAppend(globalList1.get(), m_builder->addConstValue(2)); - - m_builder->createListAppend(globalList2.get(), m_builder->addConstValue("hello")); - m_builder->createListAppend(globalList2.get(), m_builder->addConstValue("world")); - - m_builder->createListAppend(localList.get(), m_builder->addConstValue(false)); - m_builder->createListAppend(localList.get(), m_builder->addConstValue(true)); - }; - - auto checkLists = [this, globalList1, globalList2, localList]() { - CompilerValue *v = m_builder->addListItem(globalList1.get(), m_builder->addConstValue(0)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - - v = m_builder->addListItem(globalList2.get(), m_builder->addConstValue(1)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - - v = m_builder->addListItem(localList.get(), m_builder->addConstValue(0)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - }; - - // repeat (2) - resetLists(); - - CompilerValue *v, *v1, *v2; - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue("hello world"); - m_builder->createListAppend(globalList1.get(), v); - - v1 = m_builder->addConstValue(0); - v2 = m_builder->addConstValue(8.5); - m_builder->createListInsert(globalList2.get(), v1, v2); - - v1 = m_builder->addConstValue(1); - v2 = m_builder->addConstValue(-4.25); - m_builder->createListReplace(localList.get(), v1, v2); - } - m_builder->endLoop(); - - checkLists(); - - // repeat(0) - resetLists(); - - v = m_builder->addConstValue(0); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue("hello world"); - m_builder->createListAppend(globalList1.get(), v); - - v1 = m_builder->addConstValue(0); - v2 = m_builder->addConstValue(8.5); - m_builder->createListInsert(globalList2.get(), v1, v2); - - v1 = m_builder->addConstValue(1); - v2 = m_builder->addConstValue(-4.25); - m_builder->createListReplace(localList.get(), v1, v2); - } - m_builder->endLoop(); - - checkLists(); - - // while (counter1 < 5) { ... until (counter2 == 3) {... counter2++ }; while (false) { ... }; counter1++ } - resetLists(); - - v = m_builder->addConstValue(0); - m_builder->createVariableWrite(counter1.get(), v); - - m_builder->beginLoopCondition(); - v1 = m_builder->addVariableValue(counter1.get()); - v2 = m_builder->addConstValue(5); - v = m_builder->createCmpLT(v1, v2); - m_builder->beginWhileLoop(v); - { - v = m_builder->addConstValue(0); - m_builder->createVariableWrite(counter2.get(), v); - - m_builder->beginLoopCondition(); - v1 = m_builder->addVariableValue(counter2.get()); - v2 = m_builder->addConstValue(3); - v = m_builder->createCmpEQ(v1, v2); - m_builder->beginRepeatUntilLoop(v); - { - v1 = m_builder->addConstValue(0); - v2 = m_builder->addConstValue(8.5); - m_builder->createListInsert(globalList2.get(), v1, v2); - - v1 = m_builder->addConstValue(1); - v2 = m_builder->addConstValue(-4.25); - m_builder->createListReplace(localList.get(), v1, v2); - - v1 = m_builder->addVariableValue(counter2.get()); - v2 = m_builder->addConstValue(1); - v = m_builder->createAdd(v1, v2); - m_builder->createVariableWrite(counter2.get(), v); - } - m_builder->endLoop(); - - m_builder->beginLoopCondition(); - v = m_builder->addConstValue(false); - m_builder->beginWhileLoop(v); - { - v = m_builder->addConstValue("hello world"); - m_builder->createListAppend(globalList1.get(), v); - } - m_builder->endLoop(); - - v1 = m_builder->addVariableValue(counter1.get()); - v2 = m_builder->addConstValue(1); - v = m_builder->createAdd(v1, v2); - m_builder->createVariableWrite(counter1.get(), v); - } - m_builder->endLoop(); - - checkLists(); - - // until (true) - resetLists(); - - m_builder->beginLoopCondition(); - v = m_builder->addConstValue(true); - m_builder->beginRepeatUntilLoop(v); - { - v = m_builder->addConstValue("hello world"); - m_builder->createListAppend(globalList1.get(), v); - - v1 = m_builder->addConstValue(0); - v2 = m_builder->addConstValue(8.5); - m_builder->createListInsert(globalList2.get(), v1, v2); - - v1 = m_builder->addConstValue(1); - v2 = m_builder->addConstValue(-4.25); - m_builder->createListReplace(localList.get(), v1, v2); - } - m_builder->endLoop(); - - checkLists(); - - std::string expected = - "1\n" - "8.5\n" - "false\n" - "1\n" - "world\n" - "false\n" - "1\n" - "8.5\n" - "false\n" - "1\n" - "world\n" - "false\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - ; - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis1) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalList = std::make_shared("", ""); - stage.addList(globalList); - - auto localList = std::make_shared("", ""); - sprite.addList(localList); - - createBuilder(&sprite, true); - - m_builder->createListClear(localList.get()); - - CompilerValue *v = m_builder->addConstValue(5.25); - m_builder->createListAppend(localList.get(), v); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because a string is added later - v = m_builder->createSub(m_builder->addListSize(localList.get()), m_builder->addConstValue(1)); - v = m_builder->addListItem(localList.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - - v = m_builder->addConstValue("test"); - m_builder->createListAppend(localList.get(), v); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "0\n" - "1\n" - "0\n" - "0\n" - "1\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis2) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalList = std::make_shared("", ""); - stage.addList(globalList); - - auto localList = std::make_shared("", ""); - sprite.addList(localList); - - createBuilder(&sprite, true); - - m_builder->createListClear(localList.get()); - - CompilerValue *v = m_builder->addConstValue(5.25); - m_builder->createListAppend(localList.get(), v); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - // Type is known here because a number is assigned later - v = m_builder->addConstValue(0); - v = m_builder->addListItem(localList.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - - v = m_builder->addConstValue(12.5); - m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "0\n" - "1\n" - "12.5\n" - "-1\n" - "0\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis3) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalList = std::make_shared("", ""); - stage.addList(globalList); - - auto localList = std::make_shared("", ""); - sprite.addList(localList); - - createBuilder(&sprite, true); - - m_builder->createListClear(globalList.get()); - m_builder->createListClear(localList.get()); - - CompilerValue *v = m_builder->addConstValue(0); - m_builder->createListAppend(globalList.get(), v); - - v = m_builder->addConstValue(5.25); - m_builder->createListAppend(localList.get(), v); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because a list item with unknown type is inserted (the item has unknown type because a string is assigned later) - v = m_builder->addConstValue(0); - v = m_builder->addListItem(localList.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); - m_builder->createListInsert(localList.get(), m_builder->addConstValue(0), v); - - v = m_builder->addConstValue("test"); - m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "0\n" - "1\n" - "0\n" - "2\n" - "1\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis4) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalList = std::make_shared("", ""); - stage.addList(globalList); - - auto localList = std::make_shared("", ""); - sprite.addList(localList); - - createBuilder(&sprite, true); - - m_builder->createListClear(globalList.get()); - m_builder->createListClear(localList.get()); - - CompilerValue *v = m_builder->addConstValue(0); - m_builder->createListAppend(globalList.get(), v); - - v = m_builder->addConstValue(5.25); - m_builder->createListAppend(localList.get(), v); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - // Type is known here because a number list item is added later - v = m_builder->createSub(m_builder->addListSize(localList.get()), m_builder->addConstValue(1)); - v = m_builder->addListItem(localList.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); - m_builder->createListAppend(localList.get(), v); - - v = m_builder->addConstValue(12.5); - m_builder->createListInsert(globalList.get(), m_builder->addConstValue(0), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "0\n" - "1\n" - "12.5\n" - "0\n" - "1\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis5) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalList = std::make_shared("", ""); - stage.addList(globalList); - - auto localList = std::make_shared("", ""); - sprite.addList(localList); - - createBuilder(&sprite, true); - - m_builder->createListClear(globalList.get()); - m_builder->createListClear(localList.get()); - - CompilerValue *v = m_builder->addConstValue("test"); - m_builder->createListAppend(globalList.get(), v); - - v = m_builder->addConstValue(5.25); - m_builder->createListAppend(localList.get(), v); - - v = m_builder->addConstValue(3); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because a string list item is assigned later which becomes a number later - v = m_builder->addConstValue(0); - v = m_builder->addListItem(localList.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - - v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); - m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); - - v = m_builder->addConstValue(12.5); - m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "0\n" - "1\n" - "0\n" - "-1\n" - "0\n" - "12.5\n" - "-1\n" - "0\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis6) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalList = std::make_shared("", ""); - stage.addList(globalList); - - auto localList = std::make_shared("", ""); - sprite.addList(localList); - - createBuilder(&sprite, true); - - m_builder->createListClear(globalList.get()); - m_builder->createListClear(localList.get()); - - CompilerValue *v = m_builder->addConstValue(-156.07); - m_builder->createListAppend(globalList.get(), v); - - v = m_builder->addConstValue(5.25); - m_builder->createListAppend(localList.get(), v); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because a list item with unknown type is added later - v = m_builder->createSub(m_builder->addListSize(localList.get()), m_builder->addConstValue(1)); - v = m_builder->addListItem(localList.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue(10); - m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); - - v = m_builder->createSub(m_builder->addListSize(globalList.get()), m_builder->addConstValue(1)); - v = m_builder->addListItem(globalList.get(), v); - m_builder->createListAppend(localList.get(), v); - - v = m_builder->addConstValue("test"); - m_builder->createListAppend(globalList.get(), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "0\n" - "1\n" - "0\n" - "0\n" - "1\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} + auto counter1 = std::make_shared("", ""); + auto counter2 = std::make_shared("", ""); + sprite.addVariable(counter1); + sprite.addVariable(counter2); -TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis7) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - auto globalList = std::make_shared("", ""); - stage.addList(globalList); + auto resetLists = [this, builder, globalList1, globalList2, localList]() { + builder->createListClear(globalList1.get()); + builder->createListClear(globalList2.get()); + builder->createListClear(localList.get()); - auto localList = std::make_shared("", ""); - sprite.addList(localList); + builder->createListAppend(globalList1.get(), builder->addConstValue(1)); + builder->createListAppend(globalList1.get(), builder->addConstValue(2)); - createBuilder(&sprite, true); + builder->createListAppend(globalList2.get(), builder->addConstValue("hello")); + builder->createListAppend(globalList2.get(), builder->addConstValue("world")); - m_builder->createListClear(localList.get()); + builder->createListAppend(localList.get(), builder->addConstValue(false)); + builder->createListAppend(localList.get(), builder->addConstValue(true)); + }; - CompilerValue *v = m_builder->addConstValue(5.25); - m_builder->createListAppend(localList.get(), v); + auto checkLists = [this, builder, globalList1, globalList2, localList]() { + CompilerValue *v = builder->addListItem(globalList1.get(), builder->addConstValue(0)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because a list item with unknown type is assigned later - v = m_builder->addConstValue(0); - v = m_builder->addListItem(localList.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addListItem(globalList2.get(), builder->addConstValue(1)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addListItem(localList.get(), builder->addConstValue(0)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + }; - v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + // repeat (2) + resetLists(); - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue("test"); - m_builder->createListInsert(globalList.get(), m_builder->addConstValue(0), v); + CompilerValue *v, *v1, *v2; + v = builder->addConstValue(2); + builder->beginRepeatLoop(v); + { + v = builder->addConstValue("hello world"); + builder->createListAppend(globalList1.get(), v); - v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); - m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); + v1 = builder->addConstValue(0); + v2 = builder->addConstValue(8.5); + builder->createListInsert(globalList2.get(), v1, v2); - v = m_builder->addConstValue(10); - m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); - } - m_builder->endLoop(); + v1 = builder->addConstValue(1); + v2 = builder->addConstValue(-4.25); + builder->createListReplace(localList.get(), v1, v2); } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "0\n" - "1\n" - "0\n" - "-1\n" - "0\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis8) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalList = std::make_shared("", ""); - stage.addList(globalList); - - auto localList = std::make_shared("", ""); - sprite.addList(localList); + builder->endLoop(); - createBuilder(&sprite, true); - - m_builder->createListClear(localList.get()); + checkLists(); - CompilerValue *v = m_builder->addConstValue(5.25); - m_builder->createListAppend(localList.get(), v); + // repeat(0) + resetLists(); - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); + v = builder->addConstValue(0); + builder->beginRepeatLoop(v); { - // Type is unknown here because a list item with a unknown type is assigned later - v = m_builder->addConstValue(0); - v = m_builder->addListItem(localList.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue("hello world"); + builder->createListAppend(globalList1.get(), v); - v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v1 = builder->addConstValue(0); + v2 = builder->addConstValue(8.5); + builder->createListInsert(globalList2.get(), v1, v2); - v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue("test"); - m_builder->createListInsert(globalList.get(), m_builder->addConstValue(0), v); - - v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); - m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); - } - m_builder->endLoop(); + v1 = builder->addConstValue(1); + v2 = builder->addConstValue(-4.25); + builder->createListReplace(localList.get(), v1, v2); } - m_builder->endLoop(); - - std::string expected = - "5.25\n" - "0\n" - "1\n" - "0\n" - "-1\n" - "0\n"; + builder->endLoop(); - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis9) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalList = std::make_shared("", ""); - stage.addList(globalList); - - auto localList = std::make_shared("", ""); - sprite.addList(localList); - - createBuilder(&sprite, true); + checkLists(); - m_builder->createListClear(localList.get()); + // while (counter1 < 5) { ... until (counter2 == 3) {... counter2++ }; while (false) { ... }; counter1++ } + resetLists(); - CompilerValue *v = m_builder->addConstValue(5.25); - m_builder->createListAppend(localList.get(), v); + v = builder->addConstValue(0); + builder->createVariableWrite(counter1.get(), v); - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); + builder->beginLoopCondition(); + v1 = builder->addVariableValue(counter1.get()); + v2 = builder->addConstValue(5); + v = builder->createCmpLT(v1, v2); + builder->beginWhileLoop(v); { - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); + v = builder->addConstValue(0); + builder->createVariableWrite(counter2.get(), v); + + builder->beginLoopCondition(); + v1 = builder->addVariableValue(counter2.get()); + v2 = builder->addConstValue(3); + v = builder->createCmpEQ(v1, v2); + builder->beginRepeatUntilLoop(v); { - v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); - m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); - - v = m_builder->addConstValue(5); - m_builder->createListInsert(globalList.get(), m_builder->addConstValue(0), v); + v1 = builder->addConstValue(0); + v2 = builder->addConstValue(8.5); + builder->createListInsert(globalList2.get(), v1, v2); + + v1 = builder->addConstValue(1); + v2 = builder->addConstValue(-4.25); + builder->createListReplace(localList.get(), v1, v2); + + v1 = builder->addVariableValue(counter2.get()); + v2 = builder->addConstValue(1); + v = builder->createAdd(v1, v2); + builder->createVariableWrite(counter2.get(), v); } - m_builder->endLoop(); - - // Type is unknown here because a list item with unknown type is assigned before the read operation - v = m_builder->addListItem(localList.get(), m_builder->addConstValue(0)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + builder->endLoop(); - v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(5)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListContains(localList.get(), m_builder->addConstValue(5)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); + builder->beginLoopCondition(); + v = builder->addConstValue(false); + builder->beginWhileLoop(v); { - v = m_builder->addConstValue(10); - m_builder->createListInsert(globalList.get(), m_builder->addConstValue(0), v); - - v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); - m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); - - v = m_builder->addConstValue("test"); - m_builder->createListInsert(globalList.get(), m_builder->addConstValue(0), v); + v = builder->addConstValue("hello world"); + builder->createListAppend(globalList1.get(), v); } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "5\n" - "0\n" - "1\n" - "5\n" - "0\n" - "1\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis10) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalList = std::make_shared("", ""); - stage.addList(globalList); - - auto localList = std::make_shared("", ""); - sprite.addList(localList); - localList->append(123); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue("test"); - m_builder->createListAppend(globalList.get(), v); - - v = m_builder->addConstValue(3); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because a list item with unknown type is assigned later - v = m_builder->addListItem(localList.get(), m_builder->addConstValue(0)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(123)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListContains(localList.get(), m_builder->addConstValue(123)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - - v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); - m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); + builder->endLoop(); - v = m_builder->addConstValue(12.5); - m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); - } - m_builder->endLoop(); + v1 = builder->addVariableValue(counter1.get()); + v2 = builder->addConstValue(1); + v = builder->createAdd(v1, v2); + builder->createVariableWrite(counter1.get(), v); } - m_builder->endLoop(); - - std::string expected = - "123\n" - "0\n" - "1\n" - "0\n" - "-1\n" - "0\n" - "12.5\n" - "-1\n" - "0\n" - "12.5\n" - "-1\n" - "0\n" - "12.5\n" - "-1\n" - "0\n" - "12.5\n" - "-1\n" - "0\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis11) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto globalList = std::make_shared("", ""); - stage.addList(globalList); + builder->endLoop(); - auto localList = std::make_shared("", ""); - sprite.addList(localList); - localList->append(123); - localList->append("10"); + checkLists(); - createBuilder(&sprite, true); + // until (true) + resetLists(); - CompilerValue *v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); + builder->beginLoopCondition(); + v = builder->addConstValue(true); + builder->beginRepeatUntilLoop(v); { - v = m_builder->addConstValue("test"); - m_builder->createListAppend(globalList.get(), v); - - v = m_builder->addConstValue(3); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because an item from the same list is assigned later, but the list has unknown type - v = m_builder->addListItem(localList.get(), m_builder->addConstValue(0)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addConstValue("hello world"); + builder->createListAppend(globalList1.get(), v); - v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(123)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v1 = builder->addConstValue(0); + v2 = builder->addConstValue(8.5); + builder->createListInsert(globalList2.get(), v1, v2); - v = m_builder->addListContains(localList.get(), m_builder->addConstValue(123)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - - v = m_builder->addListItem(localList.get(), m_builder->addConstValue(1)); - m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); - - v = m_builder->addConstValue(12.5); - m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); - } - m_builder->endLoop(); + v1 = builder->addConstValue(1); + v2 = builder->addConstValue(-4.25); + builder->createListReplace(localList.get(), v1, v2); } - m_builder->endLoop(); + builder->endLoop(); + + checkLists(); std::string expected = - "123\n" - "0\n" "1\n" - "10\n" - "-1\n" - "0\n" - "10\n" - "-1\n" - "0\n" - "10\n" - "-1\n" - "0\n" - "10\n" - "-1\n" - "0\n" - "10\n" - "-1\n" - "0\n"; + "8.5\n" + "false\n" + "1\n" + "world\n" + "false\n" + "1\n" + "8.5\n" + "false\n" + "1\n" + "world\n" + "false\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); + ; Thread thread(&sprite, nullptr, &script); auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); @@ -5816,139 +2913,55 @@ TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis11) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } -TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis12) +TEST_F(LLVMCodeBuilderTest, ListLoopTypeAnalysis) { + // This just makes sure the type analyzer is used correctly Stage stage; Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_utils.engine()); + EXPECT_CALL(m_utils.engine(), stage()).WillRepeatedly(Return(&stage)); auto globalList = std::make_shared("", ""); stage.addList(globalList); auto localList = std::make_shared("", ""); sprite.addList(localList); - localList->append(123); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - { - v = m_builder->addConstValue("test"); - m_builder->createListAppend(globalList.get(), v); - - v = m_builder->addConstValue(3); - m_builder->beginRepeatLoop(v); - { - // Type is unknown here because an item from another list is assigned later, but it has an item from this list assigned - // This case is not checked because the code isn't considered valid - v = m_builder->addListItem(localList.get(), m_builder->addConstValue(0)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListItemIndex(localList.get(), m_builder->addConstValue(123)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListContains(localList.get(), m_builder->addConstValue(123)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - - v = m_builder->addListItem(globalList.get(), m_builder->addConstValue(0)); - m_builder->createListReplace(localList.get(), m_builder->addConstValue(0), v); - - v = m_builder->addListItem(localList.get(), m_builder->addConstValue(0)); - m_builder->createListReplace(globalList.get(), m_builder->addConstValue(0), v); - } - m_builder->endLoop(); - } - m_builder->endLoop(); - - std::string expected = - "123\n" - "0\n" - "1\n" - "0\n" - "-1\n" - "0\n" - "0\n" - "-1\n" - "0\n" - "0\n" - "-1\n" - "0\n" - "0\n" - "-1\n" - "0\n" - "0\n" - "-1\n" - "0\n"; - - auto code = m_builder->finalize(); - Script script(&sprite, nullptr, nullptr); - script.setCode(code); - Thread thread(&sprite, nullptr, &script); - auto ctx = code->createExecutionContext(&thread); - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); -} - -TEST_F(LLVMCodeBuilderTest, VarAndListLoopTypeAnalysis) -{ - Stage stage; - Sprite sprite; - sprite.setEngine(&m_engine); - EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); - - auto var = std::make_shared("", ""); - sprite.addVariable(var); - auto list = std::make_shared("", ""); - sprite.addList(list); - - createBuilder(&sprite, true); - - CompilerValue *v = m_builder->addConstValue(-2); - m_builder->createVariableWrite(var.get(), v); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - m_builder->createListClear(list.get()); + builder->createListClear(localList.get()); - v = m_builder->addConstValue(5.25); - m_builder->createListAppend(list.get(), v); + CompilerValue *v = builder->addConstValue(5.25); + builder->createListAppend(localList.get(), v); - v = m_builder->addConstValue(3); - m_builder->beginRepeatLoop(v); + v = builder->addConstValue(2); + builder->beginRepeatLoop(v); { - // Type is unknown here because a variable value with unknown type is added later - v = m_builder->createSub(m_builder->addListSize(list.get()), m_builder->addConstValue(1)); - v = m_builder->addListItem(list.get(), v); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - - v = m_builder->addListItemIndex(list.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + // Type is unknown here because a string is added later + v = builder->createSub(builder->addListSize(localList.get()), builder->addConstValue(1)); + v = builder->addListItem(localList.get(), v); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addListContains(list.get(), m_builder->addConstValue(5.25)); - m_builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + v = builder->addListItemIndex(localList.get(), builder->addConstValue(5.25)); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addVariableValue(var.get()); - m_builder->createListAppend(list.get(), v); + v = builder->addListContains(localList.get(), builder->addConstValue(5.25)); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); - v = m_builder->addConstValue("test"); - m_builder->createVariableWrite(var.get(), v); + v = builder->addConstValue("test"); + builder->createListAppend(localList.get(), v); } - m_builder->endLoop(); + builder->endLoop(); std::string expected = "5.25\n" "0\n" "1\n" - "-2\n" - "0\n" - "1\n" "0\n" "0\n" "1\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); Thread thread(&sprite, nullptr, &script); @@ -5961,19 +2974,19 @@ TEST_F(LLVMCodeBuilderTest, VarAndListLoopTypeAnalysis) TEST_F(LLVMCodeBuilderTest, StopNoWarp) { Sprite sprite; - createBuilder(&sprite, false); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, false); - m_builder->beginLoopCondition(); - CompilerValue *v = m_builder->addConstValue(true); - m_builder->beginWhileLoop(v); - m_builder->createStop(); - m_builder->endLoop(); + builder->beginLoopCondition(); + CompilerValue *v = builder->addConstValue(true); + builder->beginWhileLoop(v); + builder->createStop(); + builder->endLoop(); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); std::string expected = ""; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); Thread thread(&sprite, nullptr, &script); @@ -5986,18 +2999,18 @@ TEST_F(LLVMCodeBuilderTest, StopNoWarp) TEST_F(LLVMCodeBuilderTest, StopWarp) { Sprite sprite; - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - CompilerValue *v = m_builder->addConstValue(true); - m_builder->beginIfStatement(v); - m_builder->createStop(); - m_builder->endIf(); + CompilerValue *v = builder->addConstValue(true); + builder->beginIfStatement(v); + builder->createStop(); + builder->endIf(); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); std::string expected = ""; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); Thread thread(&sprite, nullptr, &script); @@ -6010,14 +3023,14 @@ TEST_F(LLVMCodeBuilderTest, StopWarp) TEST_F(LLVMCodeBuilderTest, StopAndReturn) { Sprite sprite; - createBuilder(&sprite, true); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - m_builder->createStop(); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->createStop(); std::string expected = "no_args\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); Thread thread(&sprite, nullptr, &script); @@ -6032,20 +3045,20 @@ TEST_F(LLVMCodeBuilderTest, MultipleScripts) Sprite sprite; // Script 1 - createBuilder(&sprite, nullptr); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, nullptr); - CompilerValue *v = m_builder->addConstValue("script1"); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + CompilerValue *v = builder->addConstValue("script1"); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - auto code1 = m_builder->finalize(); + auto code1 = builder->build(); // Script 2 - createBuilder(&sprite, nullptr); + builder = m_utils.createBuilder(&sprite, nullptr); - v = m_builder->addConstValue("script2"); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue("script2"); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - auto code2 = m_builder->finalize(); + auto code2 = builder->build(); Script script1(&sprite, nullptr, nullptr); script1.setCode(code1); @@ -6080,52 +3093,52 @@ TEST_F(LLVMCodeBuilderTest, Procedures) prototype1.setArgumentNames({ "any type 1", "any type 2", "bool" }); prototype1.setArgumentIds({ "a", "b", "c" }); prototype1.setWarp(false); - createBuilder(&sprite, &prototype1); + LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, &prototype1); - CompilerValue *v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - m_builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); - m_builder->endLoop(); + CompilerValue *v = builder->addConstValue(2); + builder->beginRepeatLoop(v); + builder->addTargetFunctionCall("test_function_no_args", Compiler::StaticType::Void, {}, {}); + builder->endLoop(); - m_builder->createVariableWrite(var.get(), m_builder->addProcedureArgument("any type 1")); - m_builder->createListClear(list.get()); - m_builder->createListAppend(list.get(), m_builder->addProcedureArgument("any type 2")); + builder->createVariableWrite(var.get(), builder->addProcedureArgument("any type 1")); + builder->createListClear(list.get()); + builder->createListAppend(list.get(), builder->addProcedureArgument("any type 2")); - v = m_builder->addVariableValue(var.get()); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addVariableValue(var.get()); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addListItem(list.get(), m_builder->addConstValue(0)); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addListItem(list.get(), builder->addConstValue(0)); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - v = m_builder->addProcedureArgument("bool"); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + v = builder->addProcedureArgument("bool"); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); - v = m_builder->addProcedureArgument("invalid"); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addProcedureArgument("invalid"); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); - m_builder->finalize(); + builder->build(); // Procedure 2 BlockPrototype prototype2; prototype2.setProcCode("procedure 2"); prototype2.setWarp(true); - createBuilder(&sprite, &prototype2); + builder = m_utils.createBuilder(&sprite, &prototype2); - v = m_builder->addConstValue(2); - m_builder->beginRepeatLoop(v); - m_builder->createProcedureCall(&prototype1, { m_builder->addConstValue(-652.3), m_builder->addConstValue(false), m_builder->addConstValue(true) }); - m_builder->endLoop(); + v = builder->addConstValue(2); + builder->beginRepeatLoop(v); + builder->createProcedureCall(&prototype1, { builder->addConstValue(-652.3), builder->addConstValue(false), builder->addConstValue(true) }); + builder->endLoop(); - m_builder->finalize(); + builder->build(); // Script - createBuilder(&sprite, false); - m_builder->createProcedureCall(&prototype1, { m_builder->addConstValue("test"), m_builder->addConstValue(true), m_builder->addConstValue(false) }); - m_builder->createProcedureCall(&prototype2, {}); + builder = m_utils.createBuilder(&sprite, false); + builder->createProcedureCall(&prototype1, { builder->addConstValue("test"), builder->addConstValue(true), builder->addConstValue(false) }); + builder->createProcedureCall(&prototype2, {}); - v = m_builder->addProcedureArgument("test"); - m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addProcedureArgument("test"); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); std::string expected1 = "no_args\n"; @@ -6153,7 +3166,7 @@ TEST_F(LLVMCodeBuilderTest, Procedures) "0\n" "0\n"; - auto code = m_builder->finalize(); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); Thread thread(&sprite, nullptr, &script); @@ -6180,19 +3193,19 @@ TEST_F(LLVMCodeBuilderTest, HatPredicates) Sprite sprite; // Predicate 1 - createPredicateBuilder(&sprite); + LLVMCodeBuilder *builder = m_utils.createPredicateBuilder(&sprite); - m_builder->addConstValue(true); + builder->addConstValue(true); - auto code1 = m_builder->finalize(); + auto code1 = builder->build(); // Predicate 2 - createPredicateBuilder(&sprite); + builder = m_utils.createPredicateBuilder(&sprite); - CompilerValue *v = m_builder->addConstValue(false); - m_builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { v }); + CompilerValue *v = builder->addConstValue(false); + builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }, { v }); - auto code2 = m_builder->finalize(); + auto code2 = builder->build(); Script script1(&sprite, nullptr, nullptr); script1.setCode(code1); @@ -6217,39 +3230,39 @@ TEST_F(LLVMCodeBuilderTest, Reporters) sprite.addVariable(var); // Reporter 1 - createReporterBuilder(&sprite); + LLVMCodeBuilder *builder = m_utils.createReporterBuilder(&sprite); - m_builder->addConstValue(-45.23); + builder->addConstValue(-45.23); - auto code1 = m_builder->finalize(); + auto code1 = builder->build(); // Reporter 2 - createReporterBuilder(&sprite); + builder = m_utils.createReporterBuilder(&sprite); - CompilerValue *v = m_builder->addConstValue("test"); - m_builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String }, { v }); + CompilerValue *v = builder->addConstValue("test"); + builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String }, { v }); - auto code2 = m_builder->finalize(); + auto code2 = builder->build(); // Reporter 3 - createReporterBuilder(&sprite); - m_builder->addVariableValue(var.get()); + builder = m_utils.createReporterBuilder(&sprite); + builder->addVariableValue(var.get()); - auto code3 = m_builder->finalize(); + auto code3 = builder->build(); // Reporter 4 - createReporterBuilder(&sprite); + builder = m_utils.createReporterBuilder(&sprite); int pointee; - m_builder->addConstValue(&pointee); + builder->addConstValue(&pointee); - auto code4 = m_builder->finalize(); + auto code4 = builder->build(); // Reporter 5 - createReporterBuilder(&sprite); - v = m_builder->addConstValue(&pointee); - m_builder->addFunctionCall("test_const_pointer", Compiler::StaticType::Pointer, { Compiler::StaticType::Pointer }, { v }); + builder = m_utils.createReporterBuilder(&sprite); + v = builder->addConstValue(&pointee); + builder->addFunctionCall("test_const_pointer", Compiler::StaticType::Pointer, { Compiler::StaticType::Pointer }, { v }); - auto code5 = m_builder->finalize(); + auto code5 = builder->build(); auto runReporter = [&sprite](std::shared_ptr code) { Script script(&sprite, nullptr, nullptr); diff --git a/test/llvm/llvmexecutablecode_test.cpp b/test/llvm/llvmexecutablecode_test.cpp index d792cfdea..055efa483 100644 --- a/test/llvm/llvmexecutablecode_test.cpp +++ b/test/llvm/llvmexecutablecode_test.cpp @@ -30,7 +30,7 @@ class LLVMExecutableCodeTest : public testing::Test m_module = m_ctx->module(); m_llvmCtx = m_ctx->llvmCtx(); m_builder = std::make_unique>(*m_llvmCtx); - m_valueDataType = LLVMTypes::createValueDataType(m_builder.get()); + m_valueDataType = m_ctx->valueDataType(); test_function(nullptr, nullptr, nullptr, nullptr, nullptr); // force dependency m_script = std::make_unique