diff --git a/CMakeLists.txt b/CMakeLists.txt index 95f74700..8ca8c1aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,7 @@ if (LIBSCRATCHCPP_USE_LLVM) target_sources(scratchcpp PUBLIC include/scratchcpp/dev/compiler.h + include/scratchcpp/dev/compilercontext.h include/scratchcpp/dev/compilervalue.h include/scratchcpp/dev/compilerconstant.h include/scratchcpp/dev/compilerlocalvariable.h diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h index 4486099e..b362afaa 100644 --- a/include/scratchcpp/dev/compiler.h +++ b/include/scratchcpp/dev/compiler.h @@ -11,6 +11,7 @@ namespace libscratchcpp { +class CompilerContext; class IEngine; class Target; class ExecutableCode; @@ -21,6 +22,7 @@ class Variable; class List; class Input; class Field; +class BlockPrototype; class CompilerPrivate; /*! \brief The Compiler class provides API for compiling Scratch scripts. */ @@ -39,6 +41,7 @@ class LIBSCRATCHCPP_EXPORT Compiler using ArgTypes = std::vector; using Args = std::vector; + Compiler(CompilerContext *ctx); Compiler(IEngine *engine, Target *target); Compiler(const Compiler &) = delete; @@ -60,7 +63,10 @@ class LIBSCRATCHCPP_EXPORT Compiler CompilerValue *addListItemIndex(List *list, CompilerValue *item); CompilerValue *addListContains(List *list, CompilerValue *item); CompilerValue *addListSize(List *list); + CompilerValue *addProcedureArgument(const std::string &name); + CompilerValue *addInput(const std::string &name); + CompilerValue *addInput(Input *input); CompilerValue *createAdd(CompilerValue *operand1, CompilerValue *operand2); CompilerValue *createSub(CompilerValue *operand1, CompilerValue *operand2); @@ -127,14 +133,16 @@ class LIBSCRATCHCPP_EXPORT Compiler void createYield(); void createStop(); + void createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args); + Input *input(const std::string &name) const; Field *field(const std::string &name) const; const std::unordered_set &unsupportedBlocks() const; - private: - CompilerValue *addInput(Input *input); + static std::shared_ptr createContext(IEngine *engine, Target *target); + private: spimpl::unique_impl_ptr impl; }; diff --git a/include/scratchcpp/dev/compilercontext.h b/include/scratchcpp/dev/compilercontext.h new file mode 100644 index 00000000..2a4c2420 --- /dev/null +++ b/include/scratchcpp/dev/compilercontext.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "../global.h" +#include "../spimpl.h" + +namespace libscratchcpp +{ + +class IEngine; +class Target; +class CompilerContextPrivate; + +/*! \brief The CompilerContext represents a context for a specific target which is used with the Compiler class. */ +class LIBSCRATCHCPP_EXPORT CompilerContext +{ + public: + CompilerContext(IEngine *engine, Target *target); + CompilerContext(const CompilerContext &) = delete; + virtual ~CompilerContext() { } + + IEngine *engine() const; + Target *target() const; + + private: + spimpl::unique_impl_ptr impl; +}; + +} // namespace libscratchcpp diff --git a/src/dev/engine/CMakeLists.txt b/src/dev/engine/CMakeLists.txt index 9320b664..be71b580 100644 --- a/src/dev/engine/CMakeLists.txt +++ b/src/dev/engine/CMakeLists.txt @@ -3,6 +3,9 @@ target_sources(scratchcpp compiler.cpp compiler_p.cpp compiler_p.h + compilercontext.cpp + compilercontext_p.cpp + compilercontext_p.h compilervalue.cpp compilervalue_p.cpp compilervalue_p.h diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp index bcbc8a35..2ce143e8 100644 --- a/src/dev/engine/compiler.cpp +++ b/src/dev/engine/compiler.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include #include #include @@ -12,7 +13,13 @@ using namespace libscratchcpp; -/*! Constructs Compiler. */ +/*! Constructs Compiler using the given context. */ +Compiler::Compiler(CompilerContext *ctx) : + impl(spimpl::make_unique_impl(ctx)) +{ +} + +/*! Constructs Compiler using a new context for the given target. */ Compiler::Compiler(IEngine *engine, Target *target) : impl(spimpl::make_unique_impl(engine, target)) { @@ -21,13 +28,13 @@ Compiler::Compiler(IEngine *engine, Target *target) : /*! Returns the Engine of the project. */ IEngine *Compiler::engine() const { - return impl->engine; + return impl->ctx->engine(); } /*! Returns the Target of this compiler. */ Target *Compiler::target() const { - return impl->target; + return impl->ctx->target(); } /*! Returns currently compiled block. */ @@ -39,7 +46,21 @@ std::shared_ptr Compiler::block() const /*! Compiles the script starting with the given block. */ std::shared_ptr Compiler::compile(std::shared_ptr startBlock) { - impl->builder = impl->builderFactory->create(impl->target, startBlock->id(), false); + BlockPrototype *procedurePrototype = nullptr; + + if (startBlock) { + // TODO: Move procedure definition logic to the custom blocks extension + auto input = startBlock->inputAt(0); + + if (input && input->valueBlock()) { + procedurePrototype = input->valueBlock()->mutationPrototype(); + + if (procedurePrototype && procedurePrototype->procCode().empty()) + procedurePrototype = nullptr; + } + } + + impl->builder = impl->builderFactory->create(impl->ctx, procedurePrototype); impl->substackTree.clear(); impl->substackHit = false; impl->emptySubstack = false; @@ -169,12 +190,74 @@ CompilerValue *Compiler::addListSize(List *list) return impl->builder->addListSize(list); } +/*! Adds the procedure argument with the given name to the code. */ +CompilerValue *Compiler::addProcedureArgument(const std::string &name) +{ + return impl->builder->addProcedureArgument(name); +} + /*! Compiles the given input (resolved by name) and adds it to the compiled code. */ CompilerValue *Compiler::addInput(const std::string &name) { return addInput(impl->block->inputAt(impl->block->findInput(name)).get()); } +/*! Compiles the given input and adds it to the compiled code. */ +CompilerValue *Compiler::addInput(Input *input) +{ + if (!input) + return addConstValue(Value()); + + switch (input->type()) { + case Input::Type::Shadow: + case Input::Type::NoShadow: { + if (input->pointsToDropdownMenu()) + return addConstValue(input->selectedMenuItem()); + else { + CompilerValue *ret = nullptr; + auto previousBlock = impl->block; + impl->block = input->valueBlock(); + + if (impl->block) { + if (impl->block->compileFunction()) + ret = impl->block->compile(this); + else { + std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl; + impl->unsupportedBlocks.insert(impl->block->opcode()); + ret = addConstValue(Value()); + } + } else + ret = addConstValue(input->primaryValue()->value()); + + impl->block = previousBlock; + return ret; + } + } + + case Input::Type::ObscuredShadow: { + CompilerValue *ret = nullptr; + auto previousBlock = impl->block; + impl->block = input->valueBlock(); + + if (impl->block) { + if (impl->block->compileFunction()) + ret = impl->block->compile(this); + else { + std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl; + impl->unsupportedBlocks.insert(impl->block->opcode()); + ret = addConstValue(Value()); + } + } else + ret = input->primaryValue()->compile(this); + + impl->block = previousBlock; + return ret; + } + } + + return nullptr; +} + /*! Creates an add instruction. */ CompilerValue *Compiler::createAdd(CompilerValue *operand1, CompilerValue *operand2) { @@ -552,6 +635,12 @@ void Compiler::createStop() impl->builder->createStop(); } +/*! Creates a call to the procedure with the given prototype. */ +void Compiler::createProcedureCall(BlockPrototype *prototype, const libscratchcpp::Compiler::Args &args) +{ + impl->builder->createProcedureCall(prototype, args); +} + /*! Convenience method which returns the field with the given name. */ Input *Compiler::input(const std::string &name) const { @@ -570,57 +659,9 @@ const std::unordered_set &Compiler::unsupportedBlocks() const return impl->unsupportedBlocks; } -CompilerValue *Compiler::addInput(Input *input) +/*! Creates a compiler context for the given target. */ +std::shared_ptr Compiler::createContext(IEngine *engine, Target *target) { - if (!input) - return addConstValue(Value()); - - switch (input->type()) { - case Input::Type::Shadow: - case Input::Type::NoShadow: { - if (input->pointsToDropdownMenu()) - return addConstValue(input->selectedMenuItem()); - else { - CompilerValue *ret = nullptr; - auto previousBlock = impl->block; - impl->block = input->valueBlock(); - - if (impl->block) { - if (impl->block->compileFunction()) - ret = impl->block->compile(this); - else { - std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl; - impl->unsupportedBlocks.insert(impl->block->opcode()); - ret = addConstValue(Value()); - } - } else - ret = addConstValue(input->primaryValue()->value()); - - impl->block = previousBlock; - return ret; - } - } - - case Input::Type::ObscuredShadow: { - CompilerValue *ret = nullptr; - auto previousBlock = impl->block; - impl->block = input->valueBlock(); - - if (impl->block) { - if (impl->block->compileFunction()) - ret = impl->block->compile(this); - else { - std::cout << "warning: unsupported reporter block: " << impl->block->opcode() << std::endl; - impl->unsupportedBlocks.insert(impl->block->opcode()); - ret = addConstValue(Value()); - } - } else - ret = input->primaryValue()->compile(this); - - impl->block = previousBlock; - return ret; - } - } - - return nullptr; + CompilerPrivate::initBuilderFactory(); + return CompilerPrivate::builderFactory->createCtx(engine, target); } diff --git a/src/dev/engine/compiler_p.cpp b/src/dev/engine/compiler_p.cpp index cff14fe9..963c0d3e 100644 --- a/src/dev/engine/compiler_p.cpp +++ b/src/dev/engine/compiler_p.cpp @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 +#include #include #include "compiler_p.h" @@ -9,9 +10,21 @@ using namespace libscratchcpp; +CompilerPrivate::CompilerPrivate(CompilerContext *ctx) : + ctx(ctx) +{ + assert(ctx); + initBuilderFactory(); +} + CompilerPrivate::CompilerPrivate(IEngine *engine, Target *target) : - engine(engine), - target(target) + ctxPtr(Compiler::createContext(engine, target)), + ctx(ctxPtr.get()) +{ + initBuilderFactory(); +} + +void CompilerPrivate::initBuilderFactory() { if (!builderFactory) builderFactory = CodeBuilderFactory::instance().get(); diff --git a/src/dev/engine/compiler_p.h b/src/dev/engine/compiler_p.h index add1021c..593accbf 100644 --- a/src/dev/engine/compiler_p.h +++ b/src/dev/engine/compiler_p.h @@ -9,6 +9,7 @@ namespace libscratchcpp { +class CompilerContext; class IEngine; class Target; class Block; @@ -24,12 +25,15 @@ struct CompilerPrivate IfStatement }; + CompilerPrivate(CompilerContext *ctx); CompilerPrivate(IEngine *engine, Target *target); + static void initBuilderFactory(); + void substackEnd(); - IEngine *engine = nullptr; - Target *target = nullptr; + std::shared_ptr ctxPtr; // for self-managed contexts + CompilerContext *ctx = nullptr; std::shared_ptr block; int customIfStatementCount = 0; diff --git a/src/dev/engine/compilercontext.cpp b/src/dev/engine/compilercontext.cpp new file mode 100644 index 00000000..12bc613e --- /dev/null +++ b/src/dev/engine/compilercontext.cpp @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "compilercontext_p.h" + +using namespace libscratchcpp; + +/*! Constructs CompilerContext. */ +CompilerContext::CompilerContext(IEngine *engine, Target *target) : + impl(spimpl::make_unique_impl(engine, target)) +{ +} + +/*! Returns the engine of the project. */ +IEngine *CompilerContext::engine() const +{ + return impl->engine; +} + +/*! Returns the target of this context. */ +Target *CompilerContext::target() const +{ + return impl->target; +} diff --git a/src/dev/engine/compilercontext_p.cpp b/src/dev/engine/compilercontext_p.cpp new file mode 100644 index 00000000..5f4c42f2 --- /dev/null +++ b/src/dev/engine/compilercontext_p.cpp @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "compilercontext_p.h" + +using namespace libscratchcpp; + +CompilerContextPrivate::CompilerContextPrivate(IEngine *engine, Target *target) : + engine(engine), + target(target) +{ +} diff --git a/src/dev/engine/compilercontext_p.h b/src/dev/engine/compilercontext_p.h new file mode 100644 index 00000000..5a97a56a --- /dev/null +++ b/src/dev/engine/compilercontext_p.h @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +namespace libscratchcpp +{ + +class IEngine; +class Target; + +struct CompilerContextPrivate +{ + CompilerContextPrivate(IEngine *engine, Target *target); + + IEngine *engine = nullptr; + Target *target = nullptr; +}; + +} // namespace libscratchcpp diff --git a/src/dev/engine/internal/codebuilderfactory.cpp b/src/dev/engine/internal/codebuilderfactory.cpp index 999a740a..bb3cb1e9 100644 --- a/src/dev/engine/internal/codebuilderfactory.cpp +++ b/src/dev/engine/internal/codebuilderfactory.cpp @@ -2,6 +2,7 @@ #include "codebuilderfactory.h" #include "llvm/llvmcodebuilder.h" +#include "llvm/llvmcompilercontext.h" using namespace libscratchcpp; @@ -12,7 +13,14 @@ std::shared_ptr CodeBuilderFactory::instance() return m_instance; } -std::shared_ptr CodeBuilderFactory::create(Target *target, const std::string &id, bool warp) const +std::shared_ptr CodeBuilderFactory::create(CompilerContext *ctx, BlockPrototype *procedurePrototype) const { - return std::make_shared(target, id, warp); + assert(dynamic_cast(ctx)); + return std::make_shared(static_cast(ctx), procedurePrototype); +} + +std::shared_ptr CodeBuilderFactory::createCtx(IEngine *engine, Target *target) const +{ + auto ptr = std::make_shared(engine, target); + return ptr; } diff --git a/src/dev/engine/internal/codebuilderfactory.h b/src/dev/engine/internal/codebuilderfactory.h index 9a498717..942a9a1e 100644 --- a/src/dev/engine/internal/codebuilderfactory.h +++ b/src/dev/engine/internal/codebuilderfactory.h @@ -11,7 +11,8 @@ class CodeBuilderFactory : public ICodeBuilderFactory { public: static std::shared_ptr instance(); - std::shared_ptr create(Target *target, const std::string &id, bool warp) const override; + std::shared_ptr create(CompilerContext *ctx, BlockPrototype *procedurePrototype) const override; + std::shared_ptr createCtx(IEngine *engine, Target *target) const override; private: static std::shared_ptr m_instance; diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h index a3d38629..9f6cb567 100644 --- a/src/dev/engine/internal/icodebuilder.h +++ b/src/dev/engine/internal/icodebuilder.h @@ -11,6 +11,7 @@ class Value; class Variable; class List; class ExecutableCode; +class BlockPrototype; class ICodeBuilder { @@ -31,6 +32,7 @@ class ICodeBuilder virtual CompilerValue *addListItemIndex(List *list, CompilerValue *item) = 0; virtual CompilerValue *addListContains(List *list, CompilerValue *item) = 0; virtual CompilerValue *addListSize(List *list) = 0; + virtual CompilerValue *addProcedureArgument(const std::string &name) = 0; virtual CompilerValue *createAdd(CompilerValue *operand1, CompilerValue *operand2) = 0; virtual CompilerValue *createSub(CompilerValue *operand1, CompilerValue *operand2) = 0; @@ -91,6 +93,8 @@ class ICodeBuilder virtual void yield() = 0; virtual void createStop() = 0; + + virtual void createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args) = 0; }; } // namespace libscratchcpp diff --git a/src/dev/engine/internal/icodebuilderfactory.h b/src/dev/engine/internal/icodebuilderfactory.h index 07265463..6e031b2d 100644 --- a/src/dev/engine/internal/icodebuilderfactory.h +++ b/src/dev/engine/internal/icodebuilderfactory.h @@ -8,14 +8,18 @@ namespace libscratchcpp { class ICodeBuilder; +class CompilerContext; +class BlockPrototype; class Target; +class IEngine; class ICodeBuilderFactory { public: virtual ~ICodeBuilderFactory() { } - virtual std::shared_ptr create(Target *target, const std::string &id, bool warp) const = 0; + virtual std::shared_ptr create(CompilerContext *ctx, BlockPrototype *procedurePrototype = nullptr) const = 0; + virtual std::shared_ptr createCtx(IEngine *engine, Target *target) const = 0; }; } // namespace libscratchcpp diff --git a/src/dev/engine/internal/llvm/CMakeLists.txt b/src/dev/engine/internal/llvm/CMakeLists.txt index 315be55a..e1bb3ef5 100644 --- a/src/dev/engine/internal/llvm/CMakeLists.txt +++ b/src/dev/engine/internal/llvm/CMakeLists.txt @@ -16,6 +16,8 @@ target_sources(scratchcpp llvmtypes.cpp llvmtypes.h llvmfunctions.cpp + llvmcompilercontext.cpp + llvmcompilercontext.h llvmexecutablecode.cpp llvmexecutablecode.h llvmexecutioncontext.cpp diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp index 98d4190b..c62322cb 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.cpp @@ -1,9 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -#include #include -#include -#include #include #include @@ -13,6 +10,7 @@ #include #include "llvmcodebuilder.h" +#include "llvmcompilercontext.h" #include "llvmexecutablecode.h" #include "llvmconstantregister.h" #include "llvmifstatement.h" @@ -24,28 +22,31 @@ using namespace libscratchcpp; static std::unordered_map TYPE_MAP = { { ValueType::Number, Compiler::StaticType::Number }, { ValueType::Bool, Compiler::StaticType::Bool }, { ValueType::String, Compiler::StaticType::String } }; -LLVMCodeBuilder::LLVMCodeBuilder(Target *target, const std::string &id, bool warp) : - m_target(target), - m_id(id), - m_module(std::make_unique(id, m_ctx)), - m_builder(m_ctx), - m_defaultWarp(warp), - m_warp(warp) +LLVMCodeBuilder::LLVMCodeBuilder(LLVMCompilerContext *ctx, BlockPrototype *procedurePrototype) : + m_ctx(ctx), + m_target(ctx->target()), + m_llvmCtx(*ctx->llvmCtx()), + m_module(ctx->module()), + m_builder(m_llvmCtx), + m_procedurePrototype(procedurePrototype), + m_defaultWarp(procedurePrototype ? procedurePrototype->warp() : false), + m_warp(m_defaultWarp) { - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); - llvm::InitializeNativeTargetAsmParser(); - initTypes(); createVariableMap(); createListMap(); + + llvm::FunctionType *funcType = getMainFunctionType(nullptr); + m_defaultArgCount = funcType->getNumParams(); } std::shared_ptr LLVMCodeBuilder::finalize() { - // Do not create coroutine if there are no yield instructions if (!m_warp) { - auto it = std::find_if(m_instructions.begin(), m_instructions.end(), [](const LLVMInstruction &step) { return step.type == LLVMInstruction::Type::Yield; }); + // Do not create coroutine if there are no yield instructions nor non-warp procedure calls + auto it = std::find_if(m_instructions.begin(), m_instructions.end(), [](const LLVMInstruction &step) { + return step.type == LLVMInstruction::Type::Yield || (step.type == LLVMInstruction::Type::CallProcedure && step.procedurePrototype && !step.procedurePrototype->warp()); + }); if (it == m_instructions.end()) m_warp = true; @@ -60,24 +61,35 @@ std::shared_ptr LLVMCodeBuilder::finalize() m_builder.setFastMathFlags(fmf); // Create function - // void *f(ExecutionContext *, Target *, ValueData **, List **) - llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); - llvm::FunctionType *funcType = llvm::FunctionType::get(pointerType, { pointerType, pointerType, pointerType, pointerType }, false); - llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "f", m_module.get()); + std::string funcName = getMainFunctionName(m_procedurePrototype); + llvm::FunctionType *funcType = getMainFunctionType(m_procedurePrototype); + llvm::Function *func; + + if (m_procedurePrototype) + func = getOrCreateFunction(funcName, funcType); + else + func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, funcName, m_module); + llvm::Value *executionContextPtr = func->getArg(0); llvm::Value *targetPtr = func->getArg(1); llvm::Value *targetVariables = func->getArg(2); llvm::Value *targetLists = func->getArg(3); + llvm::Value *warpArg = nullptr; - llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_ctx, "entry", func); - llvm::BasicBlock *endBranch = llvm::BasicBlock::Create(m_ctx, "end", func); + if (m_procedurePrototype) { + func->addFnAttr(llvm::Attribute::AlwaysInline); + warpArg = func->getArg(4); + } + + llvm::BasicBlock *entry = llvm::BasicBlock::Create(m_llvmCtx, "entry", func); + llvm::BasicBlock *endBranch = llvm::BasicBlock::Create(m_llvmCtx, "end", func); m_builder.SetInsertPoint(entry); // Init coroutine std::unique_ptr coro; if (!m_warp) - coro = std::make_unique(m_module.get(), &m_builder, func); + coro = std::make_unique(m_module, &m_builder, func); std::vector ifStatements; std::vector loops; @@ -123,13 +135,13 @@ std::shared_ptr LLVMCodeBuilder::finalize() // Add execution context arg if (step.functionCtxArg) { - types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0)); + 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_ctx), 0)); + types.push_back(llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0)); args.push_back(targetPtr); } @@ -291,7 +303,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() 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_ctx, llvm::APFloat(0.0)); + 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) @@ -304,15 +316,15 @@ std::shared_ptr LLVMCodeBuilder::finalize() 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_ctx, llvm::APFloat(0.0)); - llvm::Constant *negativeZero = llvm::ConstantFP::get(m_ctx, llvm::APFloat(-0.0)); - llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::round, m_builder.getDoubleTy()); - llvm::Function *floorFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::floor, m_builder.getDoubleTy()); + 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_ctx, llvm::APFloat(-0.5))); // num >= -0.5 - llvm::Value *negativeRound = m_builder.CreateCall(floorFunc, m_builder.CreateFAdd(num, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.5)))); // floor(x + 0.5) + 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; } @@ -320,7 +332,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() case LLVMInstruction::Type::Abs: { assert(step.args.size() == 1); const auto &arg = step.args[0]; - llvm::Function *absFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::fabs, m_builder.getDoubleTy()); + 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; @@ -329,7 +341,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() case LLVMInstruction::Type::Floor: { assert(step.args.size() == 1); const auto &arg = step.args[0]; - llvm::Function *floorFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::floor, 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)); step.functionReturnReg->value = m_builder.CreateCall(floorFunc, num); break; @@ -338,7 +350,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() case LLVMInstruction::Type::Ceil: { assert(step.args.size() == 1); const auto &arg = step.args[0]; - llvm::Function *ceilFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::ceil, m_builder.getDoubleTy()); + 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; @@ -349,8 +361,8 @@ std::shared_ptr LLVMCodeBuilder::finalize() const auto &arg = step.args[0]; // sqrt(x) + 0.0 // This avoids negative zero - llvm::Constant *zero = llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)); - llvm::Function *sqrtFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::sqrt, m_builder.getDoubleTy()); + 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; @@ -361,12 +373,12 @@ std::shared_ptr LLVMCodeBuilder::finalize() 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_ctx, llvm::APFloat(0.0)); - llvm::Constant *pi = llvm::ConstantFP::get(m_ctx, llvm::APFloat(std::acos(-1.0))); - llvm::Constant *piDeg = llvm::ConstantFP::get(m_ctx, llvm::APFloat(180.0)); - llvm::Constant *factor = llvm::ConstantFP::get(m_ctx, llvm::APFloat(1e10)); - llvm::Function *sinFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::sin, m_builder.getDoubleTy()); - llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::round, m_builder.getDoubleTy()); + 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) @@ -378,11 +390,11 @@ std::shared_ptr LLVMCodeBuilder::finalize() 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_ctx, llvm::APFloat(std::acos(-1.0))); - llvm::Constant *piDeg = llvm::ConstantFP::get(m_ctx, llvm::APFloat(180.0)); - llvm::Constant *factor = llvm::ConstantFP::get(m_ctx, llvm::APFloat(1e10)); - llvm::Function *cosFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::cos, m_builder.getDoubleTy()); - llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::round, m_builder.getDoubleTy()); + 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) @@ -395,19 +407,19 @@ std::shared_ptr LLVMCodeBuilder::finalize() 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_ctx, llvm::APFloat(0.0)); - llvm::Constant *full = llvm::ConstantFP::get(m_ctx, llvm::APFloat(360.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_ctx, llvm::APFloat(-270.0)); - llvm::Constant *undefined2 = llvm::ConstantFP::get(m_ctx, llvm::APFloat(90.0)); - llvm::Constant *undefined3 = llvm::ConstantFP::get(m_ctx, llvm::APFloat(-90.0)); - llvm::Constant *undefined4 = llvm::ConstantFP::get(m_ctx, llvm::APFloat(270.0)); - llvm::Constant *pi = llvm::ConstantFP::get(m_ctx, llvm::APFloat(std::acos(-1.0))); - llvm::Constant *piDeg = llvm::ConstantFP::get(m_ctx, llvm::APFloat(180.0)); - llvm::Constant *factor = llvm::ConstantFP::get(m_ctx, llvm::APFloat(1e10)); - llvm::Function *tanFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::tan, m_builder.getDoubleTy()); - llvm::Function *roundFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::round, m_builder.getDoubleTy()); + 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 @@ -427,10 +439,10 @@ std::shared_ptr LLVMCodeBuilder::finalize() 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_ctx, llvm::APFloat(0.0)); - llvm::Constant *pi = llvm::ConstantFP::get(m_ctx, llvm::APFloat(std::acos(-1.0))); - llvm::Constant *piDeg = llvm::ConstantFP::get(m_ctx, llvm::APFloat(180.0)); - llvm::Function *asinFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::asin, m_builder.getDoubleTy()); + 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; @@ -440,9 +452,9 @@ std::shared_ptr LLVMCodeBuilder::finalize() assert(step.args.size() == 1); const auto &arg = step.args[0]; // acos(x) * 180.0 / pi - llvm::Constant *pi = llvm::ConstantFP::get(m_ctx, llvm::APFloat(std::acos(-1.0))); - llvm::Constant *piDeg = llvm::ConstantFP::get(m_ctx, llvm::APFloat(180.0)); - llvm::Function *acosFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::acos, m_builder.getDoubleTy()); + 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; @@ -452,9 +464,9 @@ std::shared_ptr LLVMCodeBuilder::finalize() assert(step.args.size() == 1); const auto &arg = step.args[0]; // atan(x) * 180.0 / pi - llvm::Constant *pi = llvm::ConstantFP::get(m_ctx, llvm::APFloat(std::acos(-1.0))); - llvm::Constant *piDeg = llvm::ConstantFP::get(m_ctx, llvm::APFloat(180.0)); - llvm::Function *atanFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::atan, m_builder.getDoubleTy()); + 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; @@ -464,7 +476,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() assert(step.args.size() == 1); const auto &arg = step.args[0]; // log(x) - llvm::Function *logFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::log, m_builder.getDoubleTy()); + 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; @@ -474,7 +486,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() assert(step.args.size() == 1); const auto &arg = step.args[0]; // log10(x) - llvm::Function *log10Func = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::log10, m_builder.getDoubleTy()); + 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; @@ -484,7 +496,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() assert(step.args.size() == 1); const auto &arg = step.args[0]; // exp(x) - llvm::Function *expFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::exp, m_builder.getDoubleTy()); + 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; @@ -494,7 +506,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() assert(step.args.size() == 1); const auto &arg = step.args[0]; // exp10(x) - llvm::Function *expFunc = llvm::Intrinsic::getDeclaration(m_module.get(), llvm::Intrinsic::exp10, m_builder.getDoubleTy()); + 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; @@ -658,9 +670,9 @@ std::shared_ptr LLVMCodeBuilder::finalize() 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_ctx, "", func); - llvm::BasicBlock *elseBlock = llvm::BasicBlock::Create(m_ctx, "", func); - llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_ctx, "", func); + llvm::BasicBlock *ifBlock = llvm::BasicBlock::Create(m_llvmCtx, "", func); + llvm::BasicBlock *elseBlock = llvm::BasicBlock::Create(m_llvmCtx, "", func); + llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_llvmCtx, "", func); m_builder.CreateCondBr(isAllocated, ifBlock, elseBlock); // If there's enough space, use the allocated memory @@ -790,21 +802,14 @@ std::shared_ptr LLVMCodeBuilder::finalize() } case LLVMInstruction::Type::Yield: - if (!m_warp) { - // TODO: Do not allow use after suspend (use after free) - freeScopeHeap(); - syncVariables(targetVariables); - coro->createSuspend(); - reloadVariables(targetVariables); - reloadLists(); - } - + // TODO: Do not allow use after suspend (use after free) + createSuspend(coro.get(), func, warpArg, targetVariables); break; case LLVMInstruction::Type::BeginIf: { LLVMIfStatement statement; statement.beforeIf = m_builder.GetInsertBlock(); - statement.body = llvm::BasicBlock::Create(m_ctx, "", func); + statement.body = llvm::BasicBlock::Create(m_llvmCtx, "", func); // Use last reg assert(step.args.size() == 1); @@ -835,13 +840,13 @@ std::shared_ptr LLVMCodeBuilder::finalize() // Jump to the branch after the if statement assert(!statement.afterIf); - statement.afterIf = llvm::BasicBlock::Create(m_ctx, "", func); + statement.afterIf = llvm::BasicBlock::Create(m_llvmCtx, "", func); freeScopeHeap(); m_builder.CreateBr(statement.afterIf); // Create else branch assert(!statement.elseBranch); - statement.elseBranch = llvm::BasicBlock::Create(m_ctx, "", func); + statement.elseBranch = llvm::BasicBlock::Create(m_llvmCtx, "", func); // Since there's an else branch, the conditional instruction should jump to it m_builder.SetInsertPoint(statement.beforeIf); @@ -859,7 +864,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() // Jump to the branch after the if statement if (!statement.afterIf) - statement.afterIf = llvm::BasicBlock::Create(m_ctx, "", func); + statement.afterIf = llvm::BasicBlock::Create(m_llvmCtx, "", func); m_builder.CreateBr(statement.afterIf); @@ -888,9 +893,9 @@ std::shared_ptr LLVMCodeBuilder::finalize() m_builder.CreateStore(zero, loop.index); // Create branches - llvm::BasicBlock *roundBranch = llvm::BasicBlock::Create(m_ctx, "", func); - loop.conditionBranch = llvm::BasicBlock::Create(m_ctx, "", func); - loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", func); + llvm::BasicBlock *roundBranch = llvm::BasicBlock::Create(m_llvmCtx, "", func); + loop.conditionBranch = llvm::BasicBlock::Create(m_llvmCtx, "", func); + loop.afterLoop = llvm::BasicBlock::Create(m_llvmCtx, "", func); // Use last reg for count assert(step.args.size() == 1); @@ -900,12 +905,12 @@ std::shared_ptr LLVMCodeBuilder::finalize() 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_ctx, llvm::APFloat(0.0))); + 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.get(), llvm::Intrinsic::round, { count->getType() }); + 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); @@ -916,10 +921,10 @@ std::shared_ptr LLVMCodeBuilder::finalize() // Check index m_builder.SetInsertPoint(loop.conditionBranch); - llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", func); + llvm::BasicBlock *body = llvm::BasicBlock::Create(m_llvmCtx, "", func); if (!loop.afterLoop) - loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", func); + loop.afterLoop = llvm::BasicBlock::Create(m_llvmCtx, "", func); llvm::Value *currentIndex = m_builder.CreateLoad(m_builder.getInt64Ty(), loop.index); comparison = m_builder.CreateOr(isInf, m_builder.CreateICmpULT(currentIndex, count)); @@ -946,8 +951,8 @@ std::shared_ptr LLVMCodeBuilder::finalize() LLVMLoop &loop = loops.back(); // Create branches - llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", func); - loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", func); + llvm::BasicBlock *body = llvm::BasicBlock::Create(m_llvmCtx, "", func); + loop.afterLoop = llvm::BasicBlock::Create(m_llvmCtx, "", func); // Use last reg assert(step.args.size() == 1); @@ -967,8 +972,8 @@ std::shared_ptr LLVMCodeBuilder::finalize() LLVMLoop &loop = loops.back(); // Create branches - llvm::BasicBlock *body = llvm::BasicBlock::Create(m_ctx, "", func); - loop.afterLoop = llvm::BasicBlock::Create(m_ctx, "", func); + llvm::BasicBlock *body = llvm::BasicBlock::Create(m_llvmCtx, "", func); + loop.afterLoop = llvm::BasicBlock::Create(m_llvmCtx, "", func); // Use last reg assert(step.args.size() == 1); @@ -986,7 +991,7 @@ std::shared_ptr LLVMCodeBuilder::finalize() case LLVMInstruction::Type::BeginLoopCondition: { LLVMLoop loop; loop.isRepeatLoop = false; - loop.conditionBranch = llvm::BasicBlock::Create(m_ctx, "", func); + loop.conditionBranch = llvm::BasicBlock::Create(m_llvmCtx, "", func); m_builder.CreateBr(loop.conditionBranch); m_builder.SetInsertPoint(loop.conditionBranch); loops.push_back(loop); @@ -1018,10 +1023,65 @@ std::shared_ptr LLVMCodeBuilder::finalize() case LLVMInstruction::Type::Stop: { m_builder.CreateBr(endBranch); - llvm::BasicBlock *nextBranch = llvm::BasicBlock::Create(m_ctx, "", func); + llvm::BasicBlock *nextBranch = llvm::BasicBlock::Create(m_llvmCtx, "", func); 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(func->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, "", func); + llvm::BasicBlock *nextBranch = llvm::BasicBlock::Create(m_llvmCtx, "", func); + m_builder.CreateCondBr(m_builder.CreateIsNull(handle), nextBranch, suspendBranch); + + m_builder.SetInsertPoint(suspendBranch); + createSuspend(coro.get(), func, 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 = func->getArg(m_defaultArgCount + 1 + step.procedureArgIndex); // omit warp arg + step.functionReturnReg->value = arg; + break; + } } } @@ -1033,6 +1093,8 @@ std::shared_ptr LLVMCodeBuilder::finalize() syncVariables(targetVariables); // End and verify the function + llvm::PointerType *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); + if (m_warp) m_builder.CreateRet(llvm::ConstantPointerNull::get(pointerType)); else @@ -1042,29 +1104,20 @@ std::shared_ptr LLVMCodeBuilder::finalize() // Create resume function // bool resume(void *) - funcType = llvm::FunctionType::get(m_builder.getInt1Ty(), pointerType, false); - func = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "resume", m_module.get()); + funcName = getResumeFunctionName(m_procedurePrototype); + llvm::Function *resumeFunc = getOrCreateFunction(funcName, m_resumeFuncType); - entry = llvm::BasicBlock::Create(m_ctx, "entry", func); + 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(func->getArg(0))); - - verifyFunction(func); - -#ifdef PRINT_LLVM_IR - std::cout << std::endl << "=== LLVM IR (" << m_module->getName().str() << ") ===" << std::endl; - m_module->print(llvm::outs(), nullptr); - std::cout << "==============" << std::endl << std::endl; -#endif + m_builder.CreateRet(coro->createResume(resumeFunc->getArg(0))); - // Optimize - optimize(); + verifyFunction(resumeFunc); - return std::make_shared(std::move(m_module)); + return std::make_shared(m_ctx, func->getName().str(), resumeFunc->getName().str()); } CompilerValue *LLVMCodeBuilder::addFunctionCall(const std::string &functionName, Compiler::StaticType returnType, const Compiler::ArgTypes &argTypes, const Compiler::Args &args) @@ -1182,6 +1235,31 @@ CompilerValue *LLVMCodeBuilder::addListSize(List *list) return createOp(ins, Compiler::StaticType::Number); } +CompilerValue *LLVMCodeBuilder::addProcedureArgument(const std::string &name) +{ + if (!m_procedurePrototype) + return addConstValue(Value()); + + const auto &argNames = m_procedurePrototype->argumentNames(); + auto it = std::find(argNames.begin(), argNames.end(), name); + + if (it == argNames.end()) { + std::cout << "warning: could not find argument '" << name << "' in custom block '" << m_procedurePrototype->procCode() << "'" << std::endl; + return addConstValue(Value()); + } + + const auto index = it - argNames.begin(); + const Compiler::StaticType type = getProcedureArgType(m_procedurePrototype->argumentTypes()[index]); + LLVMInstruction ins(LLVMInstruction::Type::ProcedureArg); + auto ret = std::make_shared(type); + ret->isRawValue = (type != Compiler::StaticType::Unknown); + ins.functionReturnReg = ret.get(); + ins.procedureArgIndex = index; + + m_instructions.push_back(ins); + return addReg(ret); +} + CompilerValue *LLVMCodeBuilder::createAdd(CompilerValue *operand1, CompilerValue *operand2) { return createOp(LLVMInstruction::Type::Add, Compiler::StaticType::Number, Compiler::StaticType::Number, { operand1, operand2 }); @@ -1454,9 +1532,26 @@ void LLVMCodeBuilder::createStop() m_instructions.push_back({ LLVMInstruction::Type::Stop }); } +void LLVMCodeBuilder::createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args) +{ + assert(prototype); + assert(prototype->argumentTypes().size() == args.size()); + const auto &procedureArgs = prototype->argumentTypes(); + Compiler::ArgTypes types; + + for (BlockPrototype::ArgType type : procedureArgs) + types.push_back(getProcedureArgType(type)); + + LLVMInstruction ins(LLVMInstruction::Type::CallProcedure); + 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_resumeFuncType = llvm::FunctionType::get(m_builder.getInt1Ty(), pointerType, false); } void LLVMCodeBuilder::createVariableMap() @@ -1560,30 +1655,53 @@ void LLVMCodeBuilder::popScopeLevel() m_heap.pop_back(); } -void LLVMCodeBuilder::verifyFunction(llvm::Function *func) +std::string LLVMCodeBuilder::getMainFunctionName(BlockPrototype *procedurePrototype) { - if (llvm::verifyFunction(*func, &llvm::errs())) { - llvm::errs() << "error: LLVM function verficiation failed!\n"; - llvm::errs() << "script hat ID: " << m_id << "\n"; + return procedurePrototype ? "f." + procedurePrototype->procCode() : "f"; +} + +std::string LLVMCodeBuilder::getResumeFunctionName(BlockPrototype *procedurePrototype) +{ + return procedurePrototype ? "resume." + procedurePrototype->procCode() : "resume"; +} + +llvm::FunctionType *LLVMCodeBuilder::getMainFunctionType(BlockPrototype *procedurePrototype) +{ + // void *f(ExecutionContext *, Target *, ValueData **, List **, (warp arg), (procedure args...)) + llvm::PointerType *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()); + } } + + return llvm::FunctionType::get(pointerType, argTypes, false); } -void LLVMCodeBuilder::optimize() +llvm::Function *LLVMCodeBuilder::getOrCreateFunction(const std::string &name, llvm::FunctionType *type) { - llvm::PassBuilder passBuilder; - llvm::LoopAnalysisManager loopAnalysisManager; - llvm::FunctionAnalysisManager functionAnalysisManager; - llvm::CGSCCAnalysisManager cGSCCAnalysisManager; - llvm::ModuleAnalysisManager moduleAnalysisManager; + llvm::Function *func = m_module->getFunction(name); - passBuilder.registerModuleAnalyses(moduleAnalysisManager); - passBuilder.registerCGSCCAnalyses(cGSCCAnalysisManager); - passBuilder.registerFunctionAnalyses(functionAnalysisManager); - passBuilder.registerLoopAnalyses(loopAnalysisManager); - passBuilder.crossRegisterProxies(loopAnalysisManager, functionAnalysisManager, cGSCCAnalysisManager, moduleAnalysisManager); + if (func) + return func; + else + return llvm::Function::Create(type, llvm::Function::ExternalLinkage, name, m_module); +} - llvm::ModulePassManager modulePassManager = passBuilder.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O3); - modulePassManager.run(*m_module, moduleAnalysisManager); +void LLVMCodeBuilder::verifyFunction(llvm::Function *func) +{ + if (llvm::verifyFunction(*func, &llvm::errs())) { + llvm::errs() << "error: LLVM function verficiation failed!\n"; + llvm::errs() << "module name: " << m_module->getName() << "\n"; + } } LLVMRegister *LLVMCodeBuilder::addReg(std::shared_ptr reg) @@ -1659,7 +1777,7 @@ llvm::Value *LLVMCodeBuilder::castValue(LLVMRegister *reg, Compiler::StaticType // 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_ctx, llvm::APFloat(0.0))); + return m_builder.CreateFCmpONE(numberValue, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); } case Compiler::StaticType::Bool: { @@ -1692,7 +1810,7 @@ llvm::Value *LLVMCodeBuilder::castValue(LLVMRegister *reg, Compiler::StaticType case Compiler::StaticType::String: { // Read string pointer directly llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - return m_builder.CreateLoad(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), ptr); + return m_builder.CreateLoad(llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0), ptr); } default: @@ -1732,7 +1850,7 @@ llvm::Value *LLVMCodeBuilder::castRawValue(LLVMRegister *reg, Compiler::StaticTy switch (reg->type()) { case Compiler::StaticType::Number: // Cast double to bool (true if != 0) - return m_builder.CreateFCmpONE(reg->value, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0))); + return m_builder.CreateFCmpONE(reg->value, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); case Compiler::StaticType::String: // Convert string to bool @@ -1775,7 +1893,7 @@ llvm::Constant *LLVMCodeBuilder::castConstValue(const Value &value, Compiler::St switch (targetType) { case Compiler::StaticType::Number: { const double nan = std::numeric_limits::quiet_NaN(); - return llvm::ConstantFP::get(m_ctx, llvm::APFloat(value.isNaN() ? nan : value.toDouble())); + return llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(value.isNaN() ? nan : value.toDouble())); } case Compiler::StaticType::Bool: @@ -1814,7 +1932,7 @@ llvm::Type *LLVMCodeBuilder::getType(Compiler::StaticType type) return m_builder.getInt1Ty(); case Compiler::StaticType::String: - return llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); + return llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); default: assert(false); @@ -1822,6 +1940,11 @@ llvm::Type *LLVMCodeBuilder::getType(Compiler::StaticType type) } } +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); @@ -1830,7 +1953,7 @@ llvm::Value *LLVMCodeBuilder::isNaN(llvm::Value *num) llvm::Value *LLVMCodeBuilder::removeNaN(llvm::Value *num) { // Replace NaN with zero - return m_builder.CreateSelect(isNaN(num), llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)), num); + 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) @@ -1854,7 +1977,7 @@ llvm::Value *LLVMCodeBuilder::getListPtr(llvm::Value *targetLists, List *list) // 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_ctx), 0); + 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); } @@ -2067,12 +2190,12 @@ llvm::Value *LLVMCodeBuilder::getListItem(const LLVMListPtr &listPtr, llvm::Valu llvm::Value *LLVMCodeBuilder::getListItemIndex(const LLVMListPtr &listPtr, LLVMRegister *item, llvm::Function *func) { llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); - llvm::BasicBlock *condBlock = llvm::BasicBlock::Create(m_ctx, "", func); - llvm::BasicBlock *bodyBlock = llvm::BasicBlock::Create(m_ctx, "", func); - llvm::BasicBlock *cmpIfBlock = llvm::BasicBlock::Create(m_ctx, "", func); - llvm::BasicBlock *cmpElseBlock = llvm::BasicBlock::Create(m_ctx, "", func); - llvm::BasicBlock *notFoundBlock = llvm::BasicBlock::Create(m_ctx, "", func); - llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_ctx, "", func); + llvm::BasicBlock *condBlock = llvm::BasicBlock::Create(m_llvmCtx, "", func); + llvm::BasicBlock *bodyBlock = llvm::BasicBlock::Create(m_llvmCtx, "", func); + llvm::BasicBlock *cmpIfBlock = llvm::BasicBlock::Create(m_llvmCtx, "", func); + llvm::BasicBlock *cmpElseBlock = llvm::BasicBlock::Create(m_llvmCtx, "", func); + llvm::BasicBlock *notFoundBlock = llvm::BasicBlock::Create(m_llvmCtx, "", func); + llvm::BasicBlock *nextBlock = llvm::BasicBlock::Create(m_llvmCtx, "", func); // index = 0 llvm::Value *index = m_builder.CreateAlloca(m_builder.getInt64Ty()); @@ -2105,7 +2228,7 @@ llvm::Value *LLVMCodeBuilder::getListItemIndex(const LLVMListPtr &listPtr, LLVMR // index = -1 // goto nextBlock m_builder.SetInsertPoint(notFoundBlock); - m_builder.CreateStore(llvm::ConstantInt::get(llvm::Type::getInt64Ty(m_ctx), -1, true), index); + m_builder.CreateStore(llvm::ConstantInt::get(llvm::Type::getInt64Ty(m_llvmCtx), -1, true), index); m_builder.CreateBr(nextBlock); // nextBlock: @@ -2349,6 +2472,31 @@ llvm::Value *LLVMCodeBuilder::createComparison(LLVMRegister *arg1, LLVMRegister } } +void LLVMCodeBuilder::createSuspend(LLVMCoroutine *coro, llvm::Function *func, llvm::Value *warpArg, llvm::Value *targetVariables) +{ + if (!m_warp) { + llvm::BasicBlock *suspendBranch, *nextBranch; + + if (warpArg) { + suspendBranch = llvm::BasicBlock::Create(m_llvmCtx, "", func); + nextBranch = llvm::BasicBlock::Create(m_llvmCtx, "", func); + m_builder.CreateCondBr(warpArg, nextBranch, suspendBranch); + m_builder.SetInsertPoint(suspendBranch); + } + + freeScopeHeap(); + 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); @@ -2381,7 +2529,9 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_bool() llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_cstring() { - return resolveFunction("value_assign_cstring", llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0) }, false)); + return resolveFunction( + "value_assign_cstring", + llvm::FunctionType::get(m_builder.getVoidTy(), { m_valueDataType->getPointerTo(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0) }, false)); } llvm::FunctionCallee LLVMCodeBuilder::resolve_value_assign_special() @@ -2406,27 +2556,27 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toBool() llvm::FunctionCallee LLVMCodeBuilder::resolve_value_toCString() { - return resolveFunction("value_toCString", llvm::FunctionType::get(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), m_valueDataType->getPointerTo(), false)); + return resolveFunction("value_toCString", llvm::FunctionType::get(llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0), m_valueDataType->getPointerTo(), false)); } llvm::FunctionCallee LLVMCodeBuilder::resolve_value_doubleToCString() { - return resolveFunction("value_doubleToCString", llvm::FunctionType::get(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), m_builder.getDoubleTy(), false)); + return resolveFunction("value_doubleToCString", llvm::FunctionType::get(llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0), m_builder.getDoubleTy(), false)); } llvm::FunctionCallee LLVMCodeBuilder::resolve_value_boolToCString() { - return resolveFunction("value_boolToCString", llvm::FunctionType::get(llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), m_builder.getInt1Ty(), false)); + return resolveFunction("value_boolToCString", llvm::FunctionType::get(llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0), m_builder.getInt1Ty(), false)); } llvm::FunctionCallee LLVMCodeBuilder::resolve_value_stringToDouble() { - return resolveFunction("value_stringToDouble", llvm::FunctionType::get(m_builder.getDoubleTy(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false)); + return resolveFunction("value_stringToDouble", llvm::FunctionType::get(m_builder.getDoubleTy(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0), false)); } llvm::FunctionCallee LLVMCodeBuilder::resolve_value_stringToBool() { - return resolveFunction("value_stringToBool", llvm::FunctionType::get(m_builder.getInt1Ty(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false)); + return resolveFunction("value_stringToBool", llvm::FunctionType::get(m_builder.getInt1Ty(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0), false)); } llvm::FunctionCallee LLVMCodeBuilder::resolve_value_equals() @@ -2449,79 +2599,79 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_lower() llvm::FunctionCallee LLVMCodeBuilder::resolve_list_clear() { - llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); + 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_ctx), 0); + 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_ctx), 0); + 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_ctx), 0); + 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_ctx), 0); + llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); return resolveFunction("list_data", llvm::FunctionType::get(m_valueDataType->getPointerTo(), { listPtr }, false)); } llvm::FunctionCallee LLVMCodeBuilder::resolve_list_size_ptr() { - llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); + llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); return resolveFunction("list_size_ptr", llvm::FunctionType::get(m_builder.getInt64Ty()->getPointerTo()->getPointerTo(), { listPtr }, false)); } llvm::FunctionCallee LLVMCodeBuilder::resolve_list_alloc_size_ptr() { - llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); + llvm::Type *listPtr = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); return resolveFunction("list_alloc_size_ptr", llvm::FunctionType::get(m_builder.getInt64Ty()->getPointerTo()->getPointerTo(), { listPtr }, false)); } llvm::FunctionCallee LLVMCodeBuilder::resolve_list_to_string() { - llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); + llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); return resolveFunction("list_to_string", llvm::FunctionType::get(pointerType, { pointerType }, false)); } llvm::FunctionCallee LLVMCodeBuilder::resolve_llvm_random() { - llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); + 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_ctx), 0); + 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_ctx), 0); + 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_ctx), 0); + 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_strcasecmp() { - llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); + llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_llvmCtx), 0); return resolveFunction("strcasecmp", llvm::FunctionType::get(m_builder.getInt32Ty(), { pointerType, pointerType }, false)); } diff --git a/src/dev/engine/internal/llvm/llvmcodebuilder.h b/src/dev/engine/internal/llvm/llvmcodebuilder.h index 91f42e80..85f0beba 100644 --- a/src/dev/engine/internal/llvm/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvm/llvmcodebuilder.h @@ -3,6 +3,8 @@ #pragma once #include +#include + #include #include #include @@ -16,13 +18,13 @@ namespace libscratchcpp { -class Target; +class LLVMCompilerContext; class LLVMConstantRegister; class LLVMCodeBuilder : public ICodeBuilder { public: - LLVMCodeBuilder(Target *target, const std::string &id, bool warp); + LLVMCodeBuilder(LLVMCompilerContext *ctx, BlockPrototype *procedurePrototype = nullptr); std::shared_ptr finalize() override; @@ -38,6 +40,7 @@ class LLVMCodeBuilder : public ICodeBuilder CompilerValue *addListItemIndex(List *list, CompilerValue *item) override; CompilerValue *addListContains(List *list, CompilerValue *item) override; CompilerValue *addListSize(List *list) override; + CompilerValue *addProcedureArgument(const std::string &name) override; CompilerValue *createAdd(CompilerValue *operand1, CompilerValue *operand2) override; CompilerValue *createSub(CompilerValue *operand1, CompilerValue *operand2) override; @@ -99,6 +102,8 @@ class LLVMCodeBuilder : public ICodeBuilder void createStop() override; + void createProcedureCall(BlockPrototype *prototype, const Compiler::Args &args) override; + private: enum class Comparison { @@ -113,8 +118,11 @@ class LLVMCodeBuilder : public ICodeBuilder void pushScopeLevel(); void popScopeLevel(); + 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); - void optimize(); LLVMRegister *addReg(std::shared_ptr reg); @@ -125,6 +133,7 @@ class LLVMCodeBuilder : public ICodeBuilder llvm::Constant *castConstValue(const Value &value, Compiler::StaticType targetType); Compiler::StaticType optimizeRegisterType(LLVMRegister *reg); llvm::Type *getType(Compiler::StaticType type); + Compiler::StaticType getProcedureArgType(BlockPrototype::ArgType type); llvm::Value *isNaN(llvm::Value *num); llvm::Value *removeNaN(llvm::Value *num); @@ -147,6 +156,8 @@ class LLVMCodeBuilder : public ICodeBuilder llvm::Value *createValue(LLVMRegister *reg); llvm::Value *createComparison(LLVMRegister *arg1, LLVMRegister *arg2, Comparison type); + void createSuspend(LLVMCoroutine *coro, llvm::Function *func, 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(); @@ -190,18 +201,21 @@ class LLVMCodeBuilder : public ICodeBuilder std::unordered_map m_listPtrs; std::vector> m_scopeLists; - std::string m_id; - llvm::LLVMContext m_ctx; - std::unique_ptr m_module; + LLVMCompilerContext *m_ctx; + llvm::LLVMContext &m_llvmCtx; + llvm::Module *m_module = nullptr; llvm::IRBuilder<> m_builder; llvm::StructType *m_valueDataType = nullptr; + llvm::FunctionType *m_resumeFuncType = nullptr; std::vector m_instructions; std::vector> m_regs; std::vector> m_localVars; + BlockPrototype *m_procedurePrototype = nullptr; bool m_defaultWarp = false; bool m_warp = false; + int m_defaultArgCount = 0; std::vector> m_heap; // scopes diff --git a/src/dev/engine/internal/llvm/llvmcompilercontext.cpp b/src/dev/engine/internal/llvm/llvmcompilercontext.cpp new file mode 100644 index 00000000..ccefb872 --- /dev/null +++ b/src/dev/engine/internal/llvm/llvmcompilercontext.cpp @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include +#include + +#include "llvmcompilercontext.h" + +using namespace libscratchcpp; + +LLVMCompilerContext::LLVMCompilerContext(IEngine *engine, Target *target) : + CompilerContext(engine, target), + m_llvmCtx(std::make_unique()), + 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())) +{ + if (!m_jit) { + llvm::errs() << "error: failed to create JIT: " << toString(m_jit.takeError()) << "\n"; + return; + } +} + +llvm::LLVMContext *LLVMCompilerContext::llvmCtx() +{ + return m_llvmCtxPtr; +} + +llvm::Module *LLVMCompilerContext::module() +{ + return m_modulePtr; +} + +void LLVMCompilerContext::initJit() +{ + if (m_jitInitialized) { + std::cout << "warning: JIT compiler is already initialized" << std::endl; + return; + } + + m_jitInitialized = true; + assert(m_llvmCtx); + assert(m_module); + +#ifdef PRINT_LLVM_IR + std::cout << std::endl << "=== LLVM IR (" << m_module->getName().str() << ") ===" << std::endl; + m_module->print(llvm::outs(), nullptr); + std::cout << "==============" << std::endl << std::endl; +#endif + + // Optimize + llvm::PassBuilder passBuilder; + llvm::LoopAnalysisManager loopAnalysisManager; + llvm::FunctionAnalysisManager functionAnalysisManager; + llvm::CGSCCAnalysisManager cGSCCAnalysisManager; + llvm::ModuleAnalysisManager moduleAnalysisManager; + + passBuilder.registerModuleAnalyses(moduleAnalysisManager); + passBuilder.registerCGSCCAnalyses(cGSCCAnalysisManager); + passBuilder.registerFunctionAnalyses(functionAnalysisManager); + passBuilder.registerLoopAnalyses(loopAnalysisManager); + passBuilder.crossRegisterProxies(loopAnalysisManager, functionAnalysisManager, cGSCCAnalysisManager, moduleAnalysisManager); + + llvm::ModulePassManager modulePassManager = passBuilder.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O3); + modulePassManager.run(*m_module, moduleAnalysisManager); + + // Init JIT compiler + std::string name = m_module->getName().str(); + auto err = m_jit->get()->addIRModule(llvm::orc::ThreadSafeModule(std::move(m_module), std::move(m_llvmCtx))); + + if (err) { + llvm::errs() << "error: failed to add module '" << name << "' to JIT: " << toString(std::move(err)) << "\n"; + return; + } +} + +bool LLVMCompilerContext::jitInitialized() const +{ + return m_jitInitialized; +} + +void LLVMCompilerContext::initTarget() +{ + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); +} diff --git a/src/dev/engine/internal/llvm/llvmcompilercontext.h b/src/dev/engine/internal/llvm/llvmcompilercontext.h new file mode 100644 index 00000000..f549ff46 --- /dev/null +++ b/src/dev/engine/internal/llvm/llvmcompilercontext.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include + +namespace libscratchcpp +{ + +class ExecutionContext; +class ValueData; +class List; +class LLVMExecutableCode; + +class LLVMCompilerContext : public CompilerContext +{ + public: + LLVMCompilerContext(IEngine *engine, Target *target); + + llvm::LLVMContext *llvmCtx(); + llvm::Module *module(); + + void initJit(); + bool jitInitialized() const; + + template + T lookupFunction(const std::string &name) + { + auto func = m_jit->get()->lookup(name); + + if (func) + return (T)func->getValue(); + else { + llvm::errs() << "error: failed to lookup LLVM function: " << toString(func.takeError()) << "\n"; + return nullptr; + } + } + + private: + void initTarget(); + + 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; +}; + +} // namespace libscratchcpp diff --git a/src/dev/engine/internal/llvm/llvmcoroutine.cpp b/src/dev/engine/internal/llvm/llvmcoroutine.cpp index 6a4ad6e1..17b834d9 100644 --- a/src/dev/engine/internal/llvm/llvmcoroutine.cpp +++ b/src/dev/engine/internal/llvm/llvmcoroutine.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 +#include + #include "llvmcoroutine.h" using namespace libscratchcpp; @@ -28,7 +30,7 @@ LLVMCoroutine::LLVMCoroutine(llvm::Module *module, llvm::IRBuilder<> *builder, l // Allocate memory llvm::Value *coroSizeRet = builder->CreateCall(coroSize, std::nullopt, "size"); - llvm::Function *mallocFunc = llvm::Function::Create(llvm::FunctionType::get(pointerType, { builder->getInt64Ty() }, false), llvm::Function::ExternalLinkage, "malloc", module); + llvm::FunctionCallee mallocFunc = module->getOrInsertFunction("malloc", llvm::FunctionType::get(pointerType, { builder->getInt64Ty() }, false)); llvm::Value *alloc = builder->CreateCall(mallocFunc, coroSizeRet, "mem"); // Begin diff --git a/src/dev/engine/internal/llvm/llvmexecutablecode.cpp b/src/dev/engine/internal/llvm/llvmexecutablecode.cpp index 20aa9dfc..ed88ac60 100644 --- a/src/dev/engine/internal/llvm/llvmexecutablecode.cpp +++ b/src/dev/engine/internal/llvm/llvmexecutablecode.cpp @@ -6,43 +6,33 @@ #include #include #include -#include #include +#include + #include "llvmexecutablecode.h" +#include "llvmcompilercontext.h" #include "llvmexecutioncontext.h" using namespace libscratchcpp; -LLVMExecutableCode::LLVMExecutableCode(std::unique_ptr module) : - m_ctx(std::make_unique()), - m_jit(llvm::orc::LLJITBuilder().create()) +LLVMExecutableCode::LLVMExecutableCode(LLVMCompilerContext *ctx, const std::string &mainFunctionName, const std::string &resumeFunctionName) : + m_ctx(ctx), + m_mainFunctionName(mainFunctionName), + m_resumeFunctionName(resumeFunctionName) { - if (!m_jit) { - llvm::errs() << "error: failed to create JIT: " << toString(m_jit.takeError()) << "\n"; - return; - } + assert(m_ctx); - if (!module) - return; - - std::string name = module->getName().str(); - auto err = m_jit->get()->addIRModule(llvm::orc::ThreadSafeModule(std::move(module), std::move(m_ctx))); - - if (err) { - llvm::errs() << "error: failed to add module '" << name << "' to JIT: " << toString(std::move(err)) << "\n"; - return; + if (m_ctx->jitInitialized()) { + std::cerr << "error: cannot create LLVM code after JIT compiler had been initialized" << std::endl; + assert(false); } - - // Lookup functions - m_mainFunction = (MainFunctionType)lookupFunction("f"); - assert(m_mainFunction); - m_resumeFunction = (ResumeFunctionType)lookupFunction("resume"); - assert(m_resumeFunction); } void LLVMExecutableCode::run(ExecutionContext *context) { + assert(m_mainFunction); + assert(m_resumeFunction); LLVMExecutionContext *ctx = getContext(context); if (ctx->finished()) @@ -98,19 +88,12 @@ bool LLVMExecutableCode::isFinished(ExecutionContext *context) const std::shared_ptr LLVMExecutableCode::createExecutionContext(Thread *thread) const { - return std::make_shared(thread); -} - -uint64_t LLVMExecutableCode::lookupFunction(const std::string &name) -{ - auto func = m_jit->get()->lookup(name); + if (!m_ctx->jitInitialized()) + m_ctx->initJit(); - if (func) - return func->getValue(); - else { - llvm::errs() << "error: failed to lookup LLVM function: " << toString(func.takeError()) << "\n"; - return 0; - } + m_mainFunction = m_ctx->lookupFunction(m_mainFunctionName); + m_resumeFunction = m_ctx->lookupFunction(m_resumeFunctionName); + return std::make_shared(thread); } LLVMExecutionContext *LLVMExecutableCode::getContext(ExecutionContext *context) diff --git a/src/dev/engine/internal/llvm/llvmexecutablecode.h b/src/dev/engine/internal/llvm/llvmexecutablecode.h index 6d293b3d..aaa9c9c1 100644 --- a/src/dev/engine/internal/llvm/llvmexecutablecode.h +++ b/src/dev/engine/internal/llvm/llvmexecutablecode.h @@ -5,19 +5,18 @@ #include #include #include -#include + +#include "llvmcompilercontext.h" namespace libscratchcpp { -class Target; -class List; class LLVMExecutionContext; class LLVMExecutableCode : public ExecutableCode { public: - LLVMExecutableCode(std::unique_ptr module); + LLVMExecutableCode(LLVMCompilerContext *ctx, const std::string &mainFunctionName, const std::string &resumeFunctionName); void run(ExecutionContext *context) override; void kill(libscratchcpp::ExecutionContext *context) override; @@ -28,18 +27,16 @@ class LLVMExecutableCode : public ExecutableCode std::shared_ptr createExecutionContext(Thread *thread) const override; private: - uint64_t lookupFunction(const std::string &name); - using MainFunctionType = void *(*)(ExecutionContext *, Target *, ValueData **, List **); using ResumeFunctionType = bool (*)(void *); static LLVMExecutionContext *getContext(ExecutionContext *context); - std::unique_ptr m_ctx; - llvm::Expected> m_jit; - - MainFunctionType m_mainFunction; - ResumeFunctionType m_resumeFunction; + LLVMCompilerContext *m_ctx = nullptr; + std::string m_mainFunctionName; + std::string m_resumeFunctionName; + mutable MainFunctionType m_mainFunction = nullptr; + mutable ResumeFunctionType m_resumeFunction = nullptr; }; } // namespace libscratchcpp diff --git a/src/dev/engine/internal/llvm/llvminstruction.h b/src/dev/engine/internal/llvm/llvminstruction.h index f4518142..cb8b7ce5 100644 --- a/src/dev/engine/internal/llvm/llvminstruction.h +++ b/src/dev/engine/internal/llvm/llvminstruction.h @@ -9,6 +9,8 @@ namespace libscratchcpp { +class BlockPrototype; + struct LLVMInstruction { enum class Type @@ -68,7 +70,9 @@ struct LLVMInstruction BeginRepeatUntilLoop, BeginLoopCondition, EndLoop, - Stop + Stop, + CallProcedure, + ProcedureArg }; LLVMInstruction(Type type) : @@ -84,6 +88,8 @@ struct LLVMInstruction bool functionCtxArg = false; // whether to add execution context ptr to function parameters Variable *workVariable = nullptr; // for variables List *workList = nullptr; // for lists + BlockPrototype *procedurePrototype = nullptr; + size_t procedureArgIndex = 0; }; } // namespace libscratchcpp diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp index 1be628aa..ee9708a8 100644 --- a/src/engine/internal/engine.cpp +++ b/src/engine/internal/engine.cpp @@ -271,10 +271,14 @@ void Engine::compile() // Compile scripts to bytecode for (auto target : m_targets) { std::cout << "Compiling scripts in target " << target->name() << "..." << std::endl; -#ifndef USE_LLVM +#ifdef USE_LLVM + auto ctx = Compiler::createContext(this, target.get()); + m_compilerContexts[target.get()] = ctx; + Compiler compiler(ctx.get()); +#else std::unordered_map procedureBytecodeMap; -#endif Compiler compiler(this, target.get()); +#endif const auto &blocks = target->blocks(); for (auto block : blocks) { if (block->topLevel() && !block->isTopLevelReporter() && !block->shadow()) { diff --git a/src/engine/internal/engine.h b/src/engine/internal/engine.h index 69d64835..ed4b562d 100644 --- a/src/engine/internal/engine.h +++ b/src/engine/internal/engine.h @@ -19,6 +19,9 @@ class Entity; class IClock; class IAudioEngine; class Thread; +#ifdef USE_LLVM +class CompilerContext; +#endif class Engine : public IEngine { @@ -238,6 +241,9 @@ class Engine : public IEngine static const std::unordered_map m_hatEdgeActivated; // used to check whether a hat is edge-activated (runs when a predicate becomes true) std::vector> m_targets; +#ifdef USE_LLVM + std::unordered_map> m_compilerContexts; +#endif std::vector> m_broadcasts; std::unordered_map> m_broadcastMap; std::unordered_map> m_backdropBroadcastMap; diff --git a/test/dev/compiler/CMakeLists.txt b/test/dev/compiler/CMakeLists.txt index cdc9696c..639bbbb0 100644 --- a/test/dev/compiler/CMakeLists.txt +++ b/test/dev/compiler/CMakeLists.txt @@ -1,6 +1,7 @@ add_executable( compiler_test compiler_test.cpp + compilercontext_test.cpp compilervalue_test.cpp compilerconstant_test.cpp compilerlocalvariable_test.cpp diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp index 945e2d0d..ceb2d115 100644 --- a/test/dev/compiler/compiler_test.cpp +++ b/test/dev/compiler/compiler_test.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -26,7 +27,12 @@ class CompilerTest : public testing::Test public: void SetUp() override { + m_ctx = std::make_shared(&m_engine, &m_target); CompilerPrivate::builderFactory = &m_builderFactory; + + EXPECT_CALL(m_builderFactory, createCtx(&m_engine, &m_target)).WillOnce(Return(m_ctx)); + m_compiler = std::make_unique(&m_engine, &m_target); + m_builder = std::make_shared(); m_code = std::make_shared(); } @@ -39,18 +45,19 @@ class CompilerTest : public testing::Test m_testVar.reset(); } - void compile(Compiler &compiler, std::shared_ptr block) + void compile(Compiler *compiler, std::shared_ptr block, BlockPrototype *procedurePrototype = nullptr) { - ASSERT_EQ(compiler.block(), nullptr); - // TODO: Test warp - EXPECT_CALL(m_builderFactory, create(compiler.target(), block->id(), false)).WillOnce(Return(m_builder)); + ASSERT_EQ(compiler->block(), nullptr); + EXPECT_CALL(m_builderFactory, create(m_ctx.get(), procedurePrototype)).WillOnce(Return(m_builder)); EXPECT_CALL(*m_builder, finalize()).WillOnce(Return(m_code)); - ASSERT_EQ(compiler.compile(block), m_code); - ASSERT_EQ(compiler.block(), nullptr); + ASSERT_EQ(compiler->compile(block), m_code); + ASSERT_EQ(compiler->block(), nullptr); } EngineMock m_engine; TargetMock m_target; + std::unique_ptr m_compiler; + std::shared_ptr m_ctx; CodeBuilderFactoryMock m_builderFactory; static inline std::shared_ptr m_builder; std::shared_ptr m_code; @@ -58,16 +65,19 @@ class CompilerTest : public testing::Test static inline std::shared_ptr m_testVar; }; -TEST_F(CompilerTest, Constructors) +TEST_F(CompilerTest, Engine) +{ + ASSERT_EQ(m_compiler->engine(), &m_engine); +} + +TEST_F(CompilerTest, Target) { - Compiler compiler(&m_engine, &m_target); - ASSERT_EQ(compiler.engine(), &m_engine); - ASSERT_EQ(compiler.target(), &m_target); + ASSERT_EQ(m_compiler->target(), &m_target); } TEST_F(CompilerTest, AddFunctionCall) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); m_compareBlock = block; block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -87,12 +97,12 @@ TEST_F(CompilerTest, AddFunctionCall) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, AddTargetFunctionCall) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); m_compareBlock = block; block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -112,12 +122,12 @@ TEST_F(CompilerTest, AddTargetFunctionCall) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, AddFunctionCallWithCtx) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); m_compareBlock = block; block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -137,12 +147,12 @@ TEST_F(CompilerTest, AddFunctionCallWithCtx) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, AddConstValue) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { CompilerConstant ret(Compiler::StaticType::Unknown, Value()); @@ -159,12 +169,12 @@ TEST_F(CompilerTest, AddConstValue) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, AddLoopIndex) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { CompilerValue ret(Compiler::StaticType::Unknown); @@ -178,12 +188,12 @@ TEST_F(CompilerTest, AddLoopIndex) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, AddLocalVariableValue) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { CompilerValue ret(Compiler::StaticType::Number); @@ -201,12 +211,12 @@ TEST_F(CompilerTest, AddLocalVariableValue) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, AddVariableValue) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { CompilerValue ret(Compiler::StaticType::Unknown); @@ -221,12 +231,12 @@ TEST_F(CompilerTest, AddVariableValue) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, AddListContents) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { CompilerValue ret(Compiler::StaticType::Unknown); @@ -241,12 +251,12 @@ TEST_F(CompilerTest, AddListContents) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, AddListItem) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { CompilerValue ret(Compiler::StaticType::Unknown); @@ -262,12 +272,12 @@ TEST_F(CompilerTest, AddListItem) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, AddListItemIndex) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { CompilerValue ret(Compiler::StaticType::Unknown); @@ -283,12 +293,12 @@ TEST_F(CompilerTest, AddListItemIndex) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, AddListSize) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { CompilerValue ret(Compiler::StaticType::Unknown); @@ -303,12 +313,12 @@ TEST_F(CompilerTest, AddListSize) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, AddListContains) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { CompilerValue ret(Compiler::StaticType::Unknown); @@ -324,12 +334,31 @@ TEST_F(CompilerTest, AddListContains) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); +} + +TEST_F(CompilerTest, AddProcedureArgument) +{ + + auto block = std::make_shared("a", ""); + block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { + CompilerValue ret(Compiler::StaticType::Unknown); + + EXPECT_CALL(*m_builder, addProcedureArgument("arg 1")).WillOnce(Return(&ret)); + EXPECT_EQ(compiler->addProcedureArgument("arg 1"), &ret); + + EXPECT_CALL(*m_builder, addProcedureArgument("arg 2")).WillOnce(Return(nullptr)); + EXPECT_EQ(compiler->addProcedureArgument("arg 2"), nullptr); + + return nullptr; + }); + + compile(m_compiler.get(), block); } TEST_F(CompilerTest, AddInput) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); auto valueBlock = std::make_shared("b", ""); m_compareBlock = valueBlock; @@ -407,20 +436,20 @@ TEST_F(CompilerTest, AddInput) EXPECT_EQ(compiler->addInput("OBSCURED_SHADOW"), &constRet); EXPECT_CALL(*m_builder, addVariableValue(m_testVar.get())).WillOnce(Return(&ret)); - EXPECT_EQ(compiler->addInput("OBSCURED_SHADOW_VARIABLE"), &ret); + EXPECT_EQ(compiler->addInput(compiler->input("OBSCURED_SHADOW_VARIABLE")), &ret); EXPECT_CALL(*m_builder, addConstValue(Value("value block"))).WillOnce(Return(&constRet)); - EXPECT_EQ(compiler->addInput("OBSCURED_SHADOW_BLOCK"), &constRet); + EXPECT_EQ(compiler->addInput(compiler->input("OBSCURED_SHADOW_BLOCK")), &constRet); return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateAdd) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -433,12 +462,12 @@ TEST_F(CompilerTest, CreateAdd) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateSub) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -451,12 +480,12 @@ TEST_F(CompilerTest, CreateSub) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateMul) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -469,12 +498,12 @@ TEST_F(CompilerTest, CreateMul) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateDiv) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -487,12 +516,12 @@ TEST_F(CompilerTest, CreateDiv) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateRandom) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -505,12 +534,12 @@ TEST_F(CompilerTest, CreateRandom) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateRandomInt) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -523,12 +552,12 @@ TEST_F(CompilerTest, CreateRandomInt) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateCmpEQ) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -541,12 +570,12 @@ TEST_F(CompilerTest, CreateCmpEQ) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateCmpGT) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -559,12 +588,12 @@ TEST_F(CompilerTest, CreateCmpGT) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateCmpLT) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -577,12 +606,12 @@ TEST_F(CompilerTest, CreateCmpLT) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateAnd) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -595,12 +624,12 @@ TEST_F(CompilerTest, CreateAnd) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateOr) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -613,12 +642,12 @@ TEST_F(CompilerTest, CreateOr) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateNot) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -630,12 +659,12 @@ TEST_F(CompilerTest, CreateNot) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateMod) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -648,12 +677,12 @@ TEST_F(CompilerTest, CreateMod) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateRound) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -665,12 +694,12 @@ TEST_F(CompilerTest, CreateRound) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateAbs) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -682,12 +711,12 @@ TEST_F(CompilerTest, CreateAbs) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateFloor) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -699,12 +728,12 @@ TEST_F(CompilerTest, CreateFloor) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateCeil) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -716,12 +745,12 @@ TEST_F(CompilerTest, CreateCeil) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateSqrt) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -733,12 +762,12 @@ TEST_F(CompilerTest, CreateSqrt) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateSin) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -750,12 +779,12 @@ TEST_F(CompilerTest, CreateSin) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateCos) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -767,12 +796,12 @@ TEST_F(CompilerTest, CreateCos) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateTan) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -784,12 +813,12 @@ TEST_F(CompilerTest, CreateTan) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateAsin) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -801,12 +830,12 @@ TEST_F(CompilerTest, CreateAsin) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateAcos) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -818,12 +847,12 @@ TEST_F(CompilerTest, CreateAcos) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateAtan) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -835,12 +864,12 @@ TEST_F(CompilerTest, CreateAtan) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateLn) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -852,12 +881,12 @@ TEST_F(CompilerTest, CreateLn) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateLog10) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -869,12 +898,12 @@ TEST_F(CompilerTest, CreateLog10) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateExp) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -886,12 +915,12 @@ TEST_F(CompilerTest, CreateExp) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateExp10) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -903,12 +932,12 @@ TEST_F(CompilerTest, CreateExp10) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateSelect) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -926,12 +955,12 @@ TEST_F(CompilerTest, CreateSelect) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateLocalVariable) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -948,12 +977,12 @@ TEST_F(CompilerTest, CreateLocalVariable) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateLocalVariableWrite) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -966,12 +995,12 @@ TEST_F(CompilerTest, CreateLocalVariableWrite) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateVariableWrite) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -983,12 +1012,12 @@ TEST_F(CompilerTest, CreateVariableWrite) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CustomIfStatement) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -1008,12 +1037,12 @@ TEST_F(CompilerTest, CustomIfStatement) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CustomWhileLoop) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -1026,12 +1055,12 @@ TEST_F(CompilerTest, CustomWhileLoop) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CustomRepeatUntilLoop) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -1044,12 +1073,12 @@ TEST_F(CompilerTest, CustomRepeatUntilLoop) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, BeginLoopCondition) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { EXPECT_CALL(*m_builder, beginLoopCondition()); @@ -1057,12 +1086,12 @@ TEST_F(CompilerTest, BeginLoopCondition) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, MoveToIf) { - Compiler compiler(&m_engine, &m_target); + EXPECT_CALL(*m_builder, beginElseBranch).Times(0); auto if1 = std::make_shared("", "if"); @@ -1094,12 +1123,11 @@ TEST_F(CompilerTest, MoveToIf) input->setValueBlock(substack1); if2->addInput(input); - compile(compiler, if1); + compile(m_compiler.get(), if1); } TEST_F(CompilerTest, MoveToIfElse) { - Compiler compiler(&m_engine, &m_target); auto if1 = std::make_shared("", "if"); if1->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -1264,12 +1292,11 @@ TEST_F(CompilerTest, MoveToIfElse) block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { return compiler->addConstValue("after"); }); EXPECT_CALL(*m_builder, addConstValue(Value("after"))); - compile(compiler, if1); + compile(m_compiler.get(), if1); } TEST_F(CompilerTest, MoveToRepeatLoop) { - Compiler compiler(&m_engine, &m_target); auto l1 = std::make_shared("", "loop"); l1->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -1354,12 +1381,11 @@ TEST_F(CompilerTest, MoveToRepeatLoop) block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { return compiler->addConstValue("after"); }); EXPECT_CALL(*m_builder, addConstValue(Value("after"))); - compile(compiler, l1); + compile(m_compiler.get(), l1); } TEST_F(CompilerTest, MoveToWhileLoop) { - Compiler compiler(&m_engine, &m_target); auto l1 = std::make_shared("", "loop"); l1->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -1444,12 +1470,11 @@ TEST_F(CompilerTest, MoveToWhileLoop) block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { return compiler->addConstValue("after"); }); EXPECT_CALL(*m_builder, addConstValue(Value("after"))); - compile(compiler, l1); + compile(m_compiler.get(), l1); } TEST_F(CompilerTest, MoveToRepeatUntilLoop) { - Compiler compiler(&m_engine, &m_target); auto l1 = std::make_shared("", "loop"); l1->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -1534,12 +1559,12 @@ TEST_F(CompilerTest, MoveToRepeatUntilLoop) block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { return compiler->addConstValue("after"); }); EXPECT_CALL(*m_builder, addConstValue(Value("after"))); - compile(compiler, l1); + compile(m_compiler.get(), l1); } TEST_F(CompilerTest, CreateYield) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -1548,12 +1573,12 @@ TEST_F(CompilerTest, CreateYield) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, CreateStop) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -1562,12 +1587,31 @@ TEST_F(CompilerTest, CreateStop) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); +} + +TEST_F(CompilerTest, CreateProcedureCall) +{ + + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { + BlockPrototype prototype; + CompilerValue arg1(Compiler::StaticType::Unknown); + CompilerValue arg2(Compiler::StaticType::Unknown); + Compiler::Args args = { &arg1, &arg2 }; + + EXPECT_CALL(*m_builder, createProcedureCall(&prototype, args)); + compiler->createProcedureCall(&prototype, args); + return nullptr; + }); + + compile(m_compiler.get(), block); } TEST_F(CompilerTest, Input) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); block->addInput(std::make_shared("TEST", Input::Type::Shadow)); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -1576,12 +1620,12 @@ TEST_F(CompilerTest, Input) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, Field) { - Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("a", ""); block->addField(std::make_shared("TEST", "test")); block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { @@ -1590,7 +1634,7 @@ TEST_F(CompilerTest, Field) return nullptr; }); - compile(compiler, block); + compile(m_compiler.get(), block); } TEST_F(CompilerTest, UnsupportedBlocks) @@ -1648,9 +1692,35 @@ TEST_F(CompilerTest, UnsupportedBlocks) block4->setParent(block3); block3->setNext(block4); - Compiler compiler(&m_engine, &m_target); EXPECT_CALL(*m_builder, addConstValue).WillRepeatedly(Return(nullptr)); - compile(compiler, block1); + compile(m_compiler.get(), block1); + + ASSERT_EQ(m_compiler->unsupportedBlocks(), std::unordered_set({ "block1", "block2", "value_block1", "value_block3", "value_block5", "block4" })); +} + +TEST_F(CompilerTest, Procedure) +{ + { + auto block = std::make_shared("", ""); + auto customBlock = std::make_shared("", ""); + customBlock->mutationPrototype()->setProcCode(""); + + auto input = std::make_shared("custom_block", Input::Type::ObscuredShadow); + input->setValueBlock(customBlock); + block->addInput(input); + + compile(m_compiler.get(), block, nullptr); + } + + { + auto block = std::make_shared("", ""); + auto customBlock = std::make_shared("", ""); + customBlock->mutationPrototype()->setProcCode("test"); + + auto input = std::make_shared("custom_block", Input::Type::ObscuredShadow); + input->setValueBlock(customBlock); + block->addInput(input); - ASSERT_EQ(compiler.unsupportedBlocks(), std::unordered_set({ "block1", "block2", "value_block1", "value_block3", "value_block5", "block4" })); + compile(m_compiler.get(), block, customBlock->mutationPrototype()); + } } diff --git a/test/dev/compiler/compilercontext_test.cpp b/test/dev/compiler/compilercontext_test.cpp new file mode 100644 index 00000000..0cc611b5 --- /dev/null +++ b/test/dev/compiler/compilercontext_test.cpp @@ -0,0 +1,14 @@ +#include +#include +#include + +using namespace libscratchcpp; + +TEST(CompilerContextTest, Constructors) +{ + EngineMock engine; + TargetMock target; + CompilerContext ctx(&engine, &target); + ASSERT_EQ(ctx.engine(), &engine); + ASSERT_EQ(ctx.target(), &target); +} diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 9a7f2f1c..ec0aa414 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -60,7 +62,21 @@ class LLVMCodeBuilderTest : public testing::Test test_function(nullptr, nullptr, nullptr, nullptr, nullptr); // force dependency } - void createBuilder(Target *target, bool warp) { m_builder = std::make_unique(target, "test", warp); } + void createBuilder(Target *target, BlockPrototype *procedurePrototype) + { + if (m_contexts.find(target) == m_contexts.cend() || !target) + m_contexts[target] = std::make_unique(&m_engine, target); + + m_builder = std::make_unique(m_contexts[target].get(), procedurePrototype); + } + + void createBuilder(Target *target, bool warp) + { + m_procedurePrototype = std::make_shared("test"); + m_procedurePrototype->setWarp(warp); + createBuilder(target, m_procedurePrototype.get()); + } + void createBuilder(bool warp) { createBuilder(nullptr, warp); } CompilerValue *callConstFuncForType(ValueType type, CompilerValue *arg) @@ -353,7 +369,10 @@ class LLVMCodeBuilderTest : public testing::Test ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes << v.toString() << quotes; }; + std::unordered_map> m_contexts; 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; }; @@ -1521,11 +1540,10 @@ TEST_F(LLVMCodeBuilderTest, Exp10) TEST_F(LLVMCodeBuilderTest, LocalVariables) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); createBuilder(&sprite, true); @@ -1578,11 +1596,10 @@ TEST_F(LLVMCodeBuilderTest, LocalVariables) TEST_F(LLVMCodeBuilderTest, WriteVariable) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); auto globalVar1 = std::make_shared("", ""); auto globalVar2 = std::make_shared("", ""); @@ -1642,11 +1659,10 @@ TEST_F(LLVMCodeBuilderTest, WriteVariable) TEST_F(LLVMCodeBuilderTest, Select) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); createBuilder(&sprite, true); @@ -1718,23 +1734,22 @@ TEST_F(LLVMCodeBuilderTest, Select) "true\n"; auto code = m_builder->finalize(); - testing::internal::CaptureStdout(); 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, ReadVariable) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); auto globalVar1 = std::make_shared("", "", 87); auto globalVar2 = std::make_shared("", "", 6.4); @@ -1791,11 +1806,10 @@ TEST_F(LLVMCodeBuilderTest, ReadVariable) TEST_F(LLVMCodeBuilderTest, ClearList) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -1869,11 +1883,10 @@ TEST_F(LLVMCodeBuilderTest, ClearList) TEST_F(LLVMCodeBuilderTest, RemoveFromList) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -1915,11 +1928,10 @@ TEST_F(LLVMCodeBuilderTest, RemoveFromList) TEST_F(LLVMCodeBuilderTest, AppendToList) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -1975,11 +1987,10 @@ TEST_F(LLVMCodeBuilderTest, AppendToList) TEST_F(LLVMCodeBuilderTest, InsertToList) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -2041,11 +2052,10 @@ TEST_F(LLVMCodeBuilderTest, InsertToList) TEST_F(LLVMCodeBuilderTest, ListReplace) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -2101,11 +2111,10 @@ TEST_F(LLVMCodeBuilderTest, ListReplace) TEST_F(LLVMCodeBuilderTest, GetListContents) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -2153,11 +2162,10 @@ TEST_F(LLVMCodeBuilderTest, GetListContents) TEST_F(LLVMCodeBuilderTest, GetListItem) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); std::unordered_map strings; @@ -2226,11 +2234,10 @@ TEST_F(LLVMCodeBuilderTest, GetListItem) TEST_F(LLVMCodeBuilderTest, GetListSize) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); auto globalList = std::make_shared("", ""); stage.addList(globalList); @@ -2275,11 +2282,10 @@ TEST_F(LLVMCodeBuilderTest, GetListSize) TEST_F(LLVMCodeBuilderTest, GetListItemIndex) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); auto globalList = std::make_shared("", ""); stage.addList(globalList); @@ -2370,11 +2376,10 @@ TEST_F(LLVMCodeBuilderTest, GetListItemIndex) TEST_F(LLVMCodeBuilderTest, ListContainsItem) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); auto globalList = std::make_shared("", ""); stage.addList(globalList); @@ -2547,11 +2552,10 @@ TEST_F(LLVMCodeBuilderTest, Yield) TEST_F(LLVMCodeBuilderTest, VariablesAfterSuspend) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); auto globalVar = std::make_shared("", "", 87); stage.addVariable(globalVar); @@ -2598,11 +2602,11 @@ TEST_F(LLVMCodeBuilderTest, VariablesAfterSuspend) TEST_F(LLVMCodeBuilderTest, ListsAfterSuspend) { - EngineMock engine; + Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); auto globalList1 = std::make_shared("", ""); stage.addList(globalList1); @@ -2885,11 +2889,10 @@ TEST_F(LLVMCodeBuilderTest, IfStatement) TEST_F(LLVMCodeBuilderTest, IfStatementVariables) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); auto globalVar = std::make_shared("", "", "test"); stage.addVariable(globalVar); @@ -2983,11 +2986,10 @@ TEST_F(LLVMCodeBuilderTest, IfStatementVariables) TEST_F(LLVMCodeBuilderTest, IfStatementLists) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); auto globalList1 = std::make_shared("", ""); stage.addList(globalList1); @@ -3507,11 +3509,10 @@ TEST_F(LLVMCodeBuilderTest, RepeatUntilLoop) TEST_F(LLVMCodeBuilderTest, LoopVariables) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); auto globalVar = std::make_shared("", "", "test"); stage.addVariable(globalVar); @@ -3641,11 +3642,10 @@ TEST_F(LLVMCodeBuilderTest, LoopVariables) TEST_F(LLVMCodeBuilderTest, LoopLists) { - EngineMock engine; Stage stage; Sprite sprite; - sprite.setEngine(&engine); - EXPECT_CALL(engine, stage()).WillRepeatedly(Return(&stage)); + sprite.setEngine(&m_engine); + EXPECT_CALL(m_engine, stage()).WillRepeatedly(Return(&stage)); auto globalList1 = std::make_shared("", ""); stage.addList(globalList1); @@ -3901,3 +3901,151 @@ TEST_F(LLVMCodeBuilderTest, StopAndReturn) code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } + +TEST_F(LLVMCodeBuilderTest, MultipleScripts) +{ + Sprite sprite; + + // Script 1 + createBuilder(&sprite, nullptr); + + CompilerValue *v = m_builder->addConstValue("script1"); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + auto code1 = m_builder->finalize(); + + // Script 2 + createBuilder(&sprite, nullptr); + + v = m_builder->addConstValue("script2"); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + auto code2 = m_builder->finalize(); + + Script script1(&sprite, nullptr, nullptr); + script1.setCode(code1); + Thread thread1(&sprite, nullptr, &script1); + auto ctx = code1->createExecutionContext(&thread1); + + testing::internal::CaptureStdout(); + code1->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), "script1\n"); + + Script script2(&sprite, nullptr, nullptr); + script2.setCode(code2); + Thread thread2(&sprite, nullptr, &script2); + ctx = code2->createExecutionContext(&thread2); + + testing::internal::CaptureStdout(); + code2->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), "script2\n"); +} + +TEST_F(LLVMCodeBuilderTest, Procedures) +{ + Sprite sprite; + auto var = std::make_shared("", ""); + auto list = std::make_shared("", ""); + sprite.addVariable(var); + sprite.addList(list); + + // Procedure 1 + BlockPrototype prototype1; + prototype1.setProcCode("procedure 1 %s %s %b"); + prototype1.setArgumentNames({ "any type 1", "any type 2", "bool" }); + prototype1.setArgumentIds({ "a", "b", "c" }); + prototype1.setWarp(false); + 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(); + + 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")); + + v = m_builder->addVariableValue(var.get()); + m_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 = 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 = m_builder->addProcedureArgument("invalid"); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + m_builder->finalize(); + + // Procedure 2 + BlockPrototype prototype2; + prototype2.setProcCode("procedure 2"); + prototype2.setWarp(true); + 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(); + + m_builder->finalize(); + + // Script + createBuilder(&sprite, false); + m_builder->createProcedureCall(&prototype1, { m_builder->addConstValue("test"), m_builder->addConstValue(true), m_builder->addConstValue(false) }); + m_builder->createProcedureCall(&prototype2, {}); + + v = m_builder->addProcedureArgument("test"); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + std::string expected1 = "no_args\n"; + + std::string expected2 = + "test\n" + "true\n" + "false\n" + "0\n" + "0\n"; + + std::string expected3 = + "no_args\n" + "no_args\n" + "-652.3\n" + "false\n" + "true\n" + "1\n" + "0\n" + "no_args\n" + "no_args\n" + "-652.3\n" + "false\n" + "true\n" + "1\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(), expected1); + ASSERT_FALSE(code->isFinished(ctx.get())); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected1); + ASSERT_FALSE(code->isFinished(ctx.get())); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected2 + expected3); + ASSERT_TRUE(code->isFinished(ctx.get())); +} diff --git a/test/dev/llvm/llvmexecutablecode_test.cpp b/test/dev/llvm/llvmexecutablecode_test.cpp index 581a3a58..08427a32 100644 --- a/test/dev/llvm/llvmexecutablecode_test.cpp +++ b/test/dev/llvm/llvmexecutablecode_test.cpp @@ -6,8 +6,9 @@ #include #include #include +#include #include -#include +#include #include #include @@ -21,27 +22,26 @@ class LLVMExecutableCodeTest : public testing::Test public: void SetUp() override { - m_module = std::make_unique("test", m_ctx); - m_builder = std::make_unique>(m_ctx); + m_target.setName("test"); + m_ctx = std::make_unique(&m_engine, &m_target); + m_module = m_ctx->module(); + m_llvmCtx = m_ctx->llvmCtx(); + m_builder = std::make_unique>(*m_llvmCtx); test_function(nullptr, nullptr, nullptr, nullptr, nullptr); // force dependency m_script = std::make_unique