From 622e463aa60a5d90684b04c453e2567251f7eda6 Mon Sep 17 00:00:00 2001 From: "Crunch (Chaz9)" Date: Sat, 2 Mar 2024 17:20:17 +0000 Subject: [PATCH] s3air --- .../source/lemon/runtime/RuntimeFunction.h | 75 ++ .../source/lemon/runtime/RuntimeOpcode.h | 76 ++ .../lemon/runtime/RuntimeOpcodeContext.h | 64 ++ .../source/lemon/runtime/StandardLibrary.cpp | 668 ++++++++++++++++ .../source/lemon/runtime/StandardLibrary.h | 23 + .../provider/DefaultOpcodeProvider.cpp | 718 ++++++++++++++++++ .../runtime/provider/DefaultOpcodeProvider.h | 24 + .../provider/NativizedOpcodeProvider.cpp | 126 +++ .../provider/NativizedOpcodeProvider.h | 33 + 9 files changed, 1807 insertions(+) create mode 100644 sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/RuntimeFunction.h create mode 100644 sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/RuntimeOpcode.h create mode 100644 sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/RuntimeOpcodeContext.h create mode 100644 sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/StandardLibrary.cpp create mode 100644 sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/StandardLibrary.h create mode 100644 sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/DefaultOpcodeProvider.cpp create mode 100644 sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/DefaultOpcodeProvider.h create mode 100644 sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/NativizedOpcodeProvider.cpp create mode 100644 sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/NativizedOpcodeProvider.h diff --git a/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/RuntimeFunction.h b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/RuntimeFunction.h new file mode 100644 index 00000000..42ac91bf --- /dev/null +++ b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/RuntimeFunction.h @@ -0,0 +1,75 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#pragma once + +#include "lemon/runtime/RuntimeOpcode.h" + +// Experimental change of conditional jump to being executed as a runtime opcode function +// -> It works, but has a negative impact on performance +//#define USE_JUMP_CONDITIONAL_RUNTIME_EXEC + + +namespace lemon +{ + class Program; + class ScriptFunction; + + struct RuntimeOpcodeBuffer + { + public: + ~RuntimeOpcodeBuffer(); + + inline bool empty() const { return mSize == 0; } + inline size_t size() const { return mSize; } + inline const uint8* getStart() const { return mBuffer; } + inline const uint8* getEnd() const { return mBuffer + mSize; } + + inline const uint8& operator[](size_t offset) const { return mBuffer[offset]; } + + inline const std::vector& getOpcodePointers() const { return mOpcodePointers; } + + void clear(); + void reserveForOpcodes(size_t numOpcodes); + RuntimeOpcode& addOpcode(size_t parameterSize); + + void copyFrom(const RuntimeOpcodeBuffer& other, rmx::OneTimeAllocPool& memoryPool); + + public: + std::vector mOpcodePointers; // Direct pointers to runtime opcodes + + private: + uint8* mBuffer = nullptr; + bool mSelfManagedBuffer = false; + size_t mSize = 0; // In bytes + size_t mReserved = 0; // In bytes + }; + + + class API_EXPORT RuntimeFunction + { + public: + void build(Runtime& runtime); + + const uint8* getFirstRuntimeOpcode() const { return mRuntimeOpcodeBuffer.getStart(); } + + size_t translateFromRuntimeProgramCounter(const uint8* runtimeProgramCounter) const; + int translateFromRuntimeProgramCounterOptional(const uint8* runtimeProgramCounter) const; + const uint8* translateToRuntimeProgramCounter(size_t originalProgramCounter) const; + + private: + void createRuntimeOpcode(RuntimeOpcodeBuffer& buffer, const Opcode* opcodes, int numOpcodesAvailable, int firstOpcodeIndex, int& outNumOpcodesConsumed, const Runtime& runtime); + const uint8* translateJumpTarget(uint32 targetOpcodeIndex) const; + + public: + const ScriptFunction* mFunction = nullptr; + RuntimeOpcodeBuffer mRuntimeOpcodeBuffer; + std::vector mProgramCounterByOpcodeIndex; // Program counter (= byte index inside "mRuntimeOpcodeData") where runtime opcode for given original opcode index starts + }; + +} diff --git a/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/RuntimeOpcode.h b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/RuntimeOpcode.h new file mode 100644 index 00000000..5bad856c --- /dev/null +++ b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/RuntimeOpcode.h @@ -0,0 +1,76 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#pragma once + +#include "lemon/program/Opcode.h" +#include "lemon/runtime/Runtime.h" + + +namespace lemon +{ + class Runtime; + struct RuntimeOpcode; + struct RuntimeOpcodeBuffer; + struct RuntimeOpcodeContext; + + + typedef void(*ExecFunc)(const RuntimeOpcodeContext context); + + class API_EXPORT RuntimeOpcodeProvider + { + public: + virtual bool buildRuntimeOpcode(RuntimeOpcodeBuffer& buffer, const Opcode* opcodes, int numOpcodesAvailable, int firstOpcodeIndex, int& outNumOpcodesConsumed, const Runtime& runtime) = 0; + }; + + + struct API_EXPORT RuntimeOpcodeBase + { + public: + enum class Flag : uint8 + { + CALL_IS_BASE_CALL = 0x20, // For CALL opcodes only: It is a base call + CALL_TARGET_RESOLVED = 0x40, // For CALL opcodes only: Call target is already resolved and can be found in the parameter (as pointer) + CALL_TARGET_RUNTIME_FUNC = 0x80 // For CALL opcodes only: Call target is resolved and is a RuntimeFunction, not a Function + }; + + ExecFunc mExecFunc; + RuntimeOpcode* mNext = nullptr; + Opcode::Type mOpcodeType = Opcode::Type::NOP; + uint8 mSize = 0; + BitFlagSet mFlags; + uint8 mSuccessiveHandledOpcodes = 0; // Number of internally handled opcodes (i.e. not manipulating control flow) in a row from this one -- including this one, so if this is 0, the opcode is not handled + }; + + struct API_EXPORT RuntimeOpcode : public RuntimeOpcodeBase + { + public: + static const constexpr size_t PARAMETER_OFFSET = sizeof(RuntimeOpcodeBase); + + template FORCE_INLINE T getParameter() const + { + return *reinterpret_cast((uint8*)this + PARAMETER_OFFSET); + } + + template FORCE_INLINE T getParameter(size_t offset) const + { + return *reinterpret_cast((uint8*)this + PARAMETER_OFFSET + offset); + } + + template void setParameter(T value) + { + *reinterpret_cast((uint8*)this + PARAMETER_OFFSET) = value; + } + + template void setParameter(T value, size_t offset) + { + *reinterpret_cast((uint8*)this + PARAMETER_OFFSET + offset) = value; + } + }; + +} diff --git a/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/RuntimeOpcodeContext.h b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/RuntimeOpcodeContext.h new file mode 100644 index 00000000..cf9c90df --- /dev/null +++ b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/RuntimeOpcodeContext.h @@ -0,0 +1,64 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#pragma once + +#include "lemon/runtime/Runtime.h" +#include "lemon/runtime/RuntimeOpcode.h" +#include "lemon/utility/AnyBaseValue.h" + + +namespace lemon +{ + struct API_EXPORT RuntimeOpcodeContext + { + ControlFlow* mControlFlow = nullptr; + const RuntimeOpcode* mOpcode = nullptr; + + template + FORCE_INLINE T getParameter() const + { + return mOpcode->getParameter(); + } + + template + FORCE_INLINE T getParameter(size_t offset) const + { + return mOpcode->getParameter(offset); + } + + template + FORCE_INLINE T readValueStack(int offset) const + { + return mControlFlow->readValueStack(offset); + } + + template + FORCE_INLINE void writeValueStack(int offset, T value) const + { + mControlFlow->writeValueStack(offset, value); + } + + FORCE_INLINE void moveValueStack(int change) const + { + mControlFlow->moveValueStack(change); + } + + template + FORCE_INLINE T readLocalVariable(size_t index) const + { + return BaseTypeConversion::convert(mControlFlow->mCurrentLocalVariables[index]); + } + + template + FORCE_INLINE void writeLocalVariable(size_t index, T value) const + { + mControlFlow->mCurrentLocalVariables[index] = BaseTypeConversion::convert(value); + } + }; +} diff --git a/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/StandardLibrary.cpp b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/StandardLibrary.cpp new file mode 100644 index 00000000..e50a3e60 --- /dev/null +++ b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/StandardLibrary.cpp @@ -0,0 +1,668 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#include "lemon/pch.h" +#include "lemon/runtime/StandardLibrary.h" +#include "lemon/runtime/BuiltInFunctions.h" +#include "lemon/runtime/FastStringStream.h" +#include "lemon/program/FunctionWrapper.h" +#include "lemon/program/Module.h" +#include "lemon/program/Program.h" + + +namespace lemon +{ + namespace functions + { + template + T minimum(T a, T b) + { + return std::min(a, b); + } + + template + T maximum(T a, T b) + { + return std::max(a, b); + } + + template + T clamp(T a, T b, T c) + { + return std::min(std::max(a, b), c); + } + + template + R absolute(T a) + { + return (R)std::abs(a); + } + + uint32 sqrt_u32(uint32 a) + { + return (uint32)std::sqrt((float)a); + } + + int16 sin_s16(int16 x) + { + return (int16)roundToInt(std::sin((float)x / (float)0x100) * (float)0x100); + } + + int32 sin_s32(int32 x) + { + return (int32)roundToInt(std::sin((float)x / (float)0x10000) * (float)0x10000); + } + + int16 cos_s16(int16 x) + { + return (int16)roundToInt(std::cos((float)x / (float)0x100) * (float)0x100); + } + + int32 cos_s32(int32 x) + { + return (int32)roundToInt(std::cos((float)x / (float)0x10000) * (float)0x10000); + } + + template T Math_PI() { return PI_DOUBLE; } + template<> float Math_PI() { return PI_FLOAT; } + + template T Math_sqr(T value) { return value * value; } + template T Math_sqrt(T value) { return std::sqrt(value); } + template T Math_pow(T base, T exponent) { return std::pow(base, exponent); } + template T Math_exp(T value) { return std::exp(value); } + template T Math_log(T value) { return std::log(value); } + + template T Math_sin(T value) { return std::sin(value); } + template T Math_cos(T value) { return std::cos(value); } + template T Math_tan(T value) { return std::tan(value); } + template T Math_asin(T value) { return std::asin(value); } + template T Math_acos(T value) { return std::acos(value); } + template T Math_atan(T value) { return std::atan(value); } + template T Math_atan2(T y, T x) { return std::atan2(y, x); } + + template T Math_degreesToRadians(T degrees) { return degrees * (Math_PI() / (T)180); } + template T Math_radiansToDegrees(T radians) { return radians * ((T)180 / Math_PI()); } + template T Math_u8ToDegrees(uint8 angle) { return (T)angle * ((T)360 / (T)256); } + template T Math_u8ToRadians(uint8 angle) { return (T)angle * (Math_PI() / (T)128); } + template uint8 Math_u8FromDegrees(T degrees) { return (uint8)std::round(degrees * ((T)256 / (T)360)); } + template uint8 Math_u8FromRadians(T radians) { return (uint8)std::round(radians * ((T)128 / Math_PI())); } + + template T Math_floor(T value) { return std::floor(value); } + template int64 Math_floorToInt(T value) { return (int64)std::floor(value); } + template T Math_ceil(T value) { return std::ceil(value); } + template int64 Math_ceilToInt(T value) { return (int64)std::ceil(value); } + template T Math_round(T value) { return std::round(value); } + template int64 Math_roundToInt(T value) { return (int64)std::round(value); } + template T Math_frac(T value) { return value - std::floor(value); } + + template bool Math_isNumber(T value) { return std::isnormal(value) || (value == (T)0); } + template bool Math_isNaN(T value) { return std::isnan(value); } + template bool Math_isInfinite(T value) { return std::isinf(value); } + + template T Math_lerp(T a, T b, T factor) { return a + (b - a) * factor; } + template T Math_lerp_int(T a, T b, float factor) { return a + roundToInt((float)(signed)(b - a) * factor); } + template T Math_invlerp(T a, T b, T value) { return (a == b) ? 0.0f : (value - a) / (b - a); } + template float Math_invlerp_int(T a, T b, T value) { return (a == b) ? 0.0f : (float)(value - a) / (float)(b - a); } + + StringRef stringformat(StringRef format, int argv, uint64* args) + { + Runtime* runtime = Runtime::getActiveRuntime(); + RMX_ASSERT(nullptr != runtime, "No lemon script runtime active"); + RMX_CHECK(format.isValid(), "Unable to resolve format string", return StringRef()); + + std::string_view formatString = format.getString(); + const int length = (int)formatString.length(); + const char* fmtPtr = formatString.data(); + const char* fmtEnd = fmtPtr + length; + + static detail::FastStringStream result; + result.clear(); + + for (; fmtPtr < fmtEnd; ++fmtPtr) + { + if (argv <= 0) + { + // Warning: This means that additional '%' characters won't be processed at all, which also means that escaped ones won't be reduces to a single one + // -> There's scripts that rely on this exact behavior, so don't ever change that! + result.addString(fmtPtr, (int)(fmtEnd - fmtPtr)); + break; + } + + // Continue until getting a '%' character + { + const char* fmtStart = fmtPtr; + while (fmtPtr != fmtEnd && *fmtPtr != '%') + { + ++fmtPtr; + } + if (fmtPtr != fmtStart) + { + result.addString(fmtStart, (int)(fmtPtr - fmtStart)); + } + if (fmtPtr == fmtEnd) + break; + } + + const int remaining = (int)(fmtEnd - fmtPtr); + if (remaining >= 2) + { + char numberOutputCharacter = 0; + int minDigits = 0; + int charsRead = 0; + + if (fmtPtr[1] == '%') + { + result.addChar('%'); + charsRead = 1; + } + else if (fmtPtr[1] == 's') + { + // String argument + const FlyweightString* argStoredString = runtime->resolveStringByKey(args[0]); + if (nullptr == argStoredString) + result.addString("", 3); + else + result.addString(argStoredString->getString()); + ++args; + --argv; + charsRead = 1; + } + else if (fmtPtr[1] == 'd' || fmtPtr[1] == 'b' || fmtPtr[1] == 'x') + { + // Integer argument + numberOutputCharacter = fmtPtr[1]; + charsRead = 1; + } + else if (remaining >= 4 && fmtPtr[1] == '0' && (fmtPtr[2] >= '1' && fmtPtr[2] <= '9') && (fmtPtr[3] == 'd' || fmtPtr[3] == 'b' || fmtPtr[3] == 'x')) + { + // Integer argument with minimum number of digits (9 or less) + numberOutputCharacter = fmtPtr[3]; + minDigits = (int)(fmtPtr[2] - '0'); + charsRead = 3; + } + else if (remaining >= 5 && fmtPtr[1] == '0' && (fmtPtr[2] >= '1' && fmtPtr[2] <= '9') && (fmtPtr[3] >= '0' && fmtPtr[3] <= '9') && (fmtPtr[4] == 'd' || fmtPtr[4] == 'b' || fmtPtr[4] == 'x')) + { + // Integer argument with minimum number of digits (10 or more) + numberOutputCharacter = fmtPtr[4]; + minDigits = (int)(fmtPtr[2] - '0') * 10 + (int)(fmtPtr[3] - '0'); + charsRead = 4; + } + else + { + result.addChar('%'); + } + + if (numberOutputCharacter != 0) + { + if (numberOutputCharacter == 'd') + { + result.addDecimal(args[0], minDigits); + } + else if (numberOutputCharacter == 'b') + { + result.addBinary(args[0], minDigits); + } + else if (numberOutputCharacter == 'x') + { + result.addHex(args[0], minDigits); + } + ++args; + --argv; + } + + fmtPtr += charsRead; + } + else + { + result.addChar('%'); + } + } + + return StringRef(runtime->addString(std::string_view(result.mBuffer, result.mLength))); + } + + StringRef stringformat1(StringRef format, uint64 arg1) + { + return stringformat(format, 1, &arg1); + } + + StringRef stringformat2(StringRef format, uint64 arg1, uint64 arg2) + { + uint64 args[] = { arg1, arg2 }; + return stringformat(format, 2, args); + } + + StringRef stringformat3(StringRef format, uint64 arg1, uint64 arg2, uint64 arg3) + { + uint64 args[] = { arg1, arg2, arg3 }; + return stringformat(format, 3, args); + } + + StringRef stringformat4(StringRef format, uint64 arg1, uint64 arg2, uint64 arg3, uint64 arg4) + { + uint64 args[] = { arg1, arg2, arg3, arg4 }; + return stringformat(format, 4, args); + } + + StringRef stringformat5(StringRef format, uint64 arg1, uint64 arg2, uint64 arg3, uint64 arg4, uint64 arg5) + { + uint64 args[] = { arg1, arg2, arg3, arg4, arg5 }; + return stringformat(format, 5, args); + } + + StringRef stringformat6(StringRef format, uint64 arg1, uint64 arg2, uint64 arg3, uint64 arg4, uint64 arg5, uint64 arg6) + { + uint64 args[] = { arg1, arg2, arg3, arg4, arg5, arg6 }; + return stringformat(format, 6, args); + } + + StringRef stringformat7(StringRef format, uint64 arg1, uint64 arg2, uint64 arg3, uint64 arg4, uint64 arg5, uint64 arg6, uint64 arg7) + { + uint64 args[] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7 }; + return stringformat(format, 7, args); + } + + StringRef stringformat8(StringRef format, uint64 arg1, uint64 arg2, uint64 arg3, uint64 arg4, uint64 arg5, uint64 arg6, uint64 arg7, uint64 arg8) + { + uint64 args[] = { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 }; + return stringformat(format, 8, args); + } + + uint32 string_length(StringRef str) + { + RMX_CHECK(str.isValid(), "Unable to resolve string", return 0); + return (uint32)str.getString().length(); + } + + bool string_isEmpty(StringRef str) + { + RMX_CHECK(str.isValid(), "Unable to resolve string", return 0); + return str.getString().empty(); + } + + uint8 string_getCharacter(StringRef string, uint32 index) + { + if (!string.isValid()) + return 0; + if (index >= string.getString().length()) + return 0; + return string.getString()[index]; + } + + StringRef string_getSubString(StringRef string, uint32 index, uint32 length) + { + Runtime* runtime = Runtime::getActiveRuntime(); + RMX_ASSERT(nullptr != runtime, "No lemon script runtime active"); + if (!string.isValid()) + return StringRef(); + + const std::string_view part = string.getString().substr(index, length); + return StringRef(runtime->addString(part)); + } + + bool string_startsWith(StringRef string, StringRef substring) + { + if (!string.isValid() || !substring.isValid()) + return false; + return rmx::startsWith(string.getString(), substring.getString()); + } + + bool string_endsWith(StringRef string, StringRef substring) + { + if (!string.isValid() || !substring.isValid()) + return false; + return rmx::endsWith(string.getString(), substring.getString()); + } + + int16 string_find(StringRef string, StringRef substring) + { + if (!string.isValid() || !substring.isValid()) + return false; + const size_t position = string.getString().find(substring.getString()); + return (position == std::string_view::npos) ? -1 : (int16)position; + } + + StringRef getStringFromCharacter(uint8 character) + { + Runtime* runtime = Runtime::getActiveRuntime(); + RMX_ASSERT(nullptr != runtime, "No lemon script runtime active"); + const char str[2] = { (char)character, '\0' }; + return StringRef(runtime->addString(str)); + } + + StringRef getStringFromHash(uint64 hash) + { + Runtime* runtime = Runtime::getActiveRuntime(); + RMX_ASSERT(nullptr != runtime, "No lemon script runtime active"); + const FlyweightString* str = runtime->resolveStringByKey(hash); + return (nullptr == str) ? StringRef() : StringRef(*str); + } + } + + + void StandardLibrary::registerBindings(lemon::Module& module) + { + // Register built-in functions + BuiltInFunctions::registerBuiltInFunctions(module); + + // Constants + module.addConstant("PI_FLOAT", &PredefinedDataTypes::FLOAT, AnyBaseValue(PI_FLOAT)); + module.addConstant("PI_DOUBLE", &PredefinedDataTypes::DOUBLE, AnyBaseValue(PI_DOUBLE)); + + // Functions + const BitFlagSet defaultFlags(Function::Flag::ALLOW_INLINE_EXECUTION); + const BitFlagSet compileTimeConstant(Function::Flag::ALLOW_INLINE_EXECUTION, Function::Flag::COMPILE_TIME_CONSTANT); + + module.addNativeFunction("min", lemon::wrap(&functions::minimum), compileTimeConstant); + module.addNativeFunction("min", lemon::wrap(&functions::minimum), compileTimeConstant); + module.addNativeFunction("min", lemon::wrap(&functions::minimum), compileTimeConstant); + module.addNativeFunction("min", lemon::wrap(&functions::minimum), compileTimeConstant); + module.addNativeFunction("min", lemon::wrap(&functions::minimum), compileTimeConstant); + module.addNativeFunction("min", lemon::wrap(&functions::minimum), compileTimeConstant); + module.addNativeFunction("min", lemon::wrap(&functions::minimum), compileTimeConstant); + module.addNativeFunction("min", lemon::wrap(&functions::minimum), compileTimeConstant); + module.addNativeFunction("min", lemon::wrap(&functions::minimum), compileTimeConstant); + module.addNativeFunction("min", lemon::wrap(&functions::minimum), compileTimeConstant); + + module.addNativeFunction("max", lemon::wrap(&functions::maximum), compileTimeConstant); + module.addNativeFunction("max", lemon::wrap(&functions::maximum), compileTimeConstant); + module.addNativeFunction("max", lemon::wrap(&functions::maximum), compileTimeConstant); + module.addNativeFunction("max", lemon::wrap(&functions::maximum), compileTimeConstant); + module.addNativeFunction("max", lemon::wrap(&functions::maximum), compileTimeConstant); + module.addNativeFunction("max", lemon::wrap(&functions::maximum), compileTimeConstant); + module.addNativeFunction("max", lemon::wrap(&functions::maximum), compileTimeConstant); + module.addNativeFunction("max", lemon::wrap(&functions::maximum), compileTimeConstant); + module.addNativeFunction("max", lemon::wrap(&functions::maximum), compileTimeConstant); + module.addNativeFunction("max", lemon::wrap(&functions::maximum), compileTimeConstant); + + module.addNativeFunction("clamp", lemon::wrap(&functions::clamp), compileTimeConstant); + module.addNativeFunction("clamp", lemon::wrap(&functions::clamp), compileTimeConstant); + module.addNativeFunction("clamp", lemon::wrap(&functions::clamp), compileTimeConstant); + module.addNativeFunction("clamp", lemon::wrap(&functions::clamp), compileTimeConstant); + module.addNativeFunction("clamp", lemon::wrap(&functions::clamp), compileTimeConstant); + module.addNativeFunction("clamp", lemon::wrap(&functions::clamp), compileTimeConstant); + module.addNativeFunction("clamp", lemon::wrap(&functions::clamp), compileTimeConstant); + module.addNativeFunction("clamp", lemon::wrap(&functions::clamp), compileTimeConstant); + module.addNativeFunction("clamp", lemon::wrap(&functions::clamp), compileTimeConstant); + module.addNativeFunction("clamp", lemon::wrap(&functions::clamp), compileTimeConstant); + + module.addNativeFunction("abs", lemon::wrap(&functions::absolute), compileTimeConstant); + module.addNativeFunction("abs", lemon::wrap(&functions::absolute), compileTimeConstant); + module.addNativeFunction("abs", lemon::wrap(&functions::absolute), compileTimeConstant); + module.addNativeFunction("abs", lemon::wrap(&functions::absolute), compileTimeConstant); + module.addNativeFunction("abs", lemon::wrap(&functions::absolute), compileTimeConstant); + module.addNativeFunction("abs", lemon::wrap(&functions::absolute), compileTimeConstant); + + module.addNativeFunction("sqrt", lemon::wrap(&functions::sqrt_u32), compileTimeConstant); + + module.addNativeFunction("sin_s16", lemon::wrap(&functions::sin_s16), compileTimeConstant); + module.addNativeFunction("sin_s32", lemon::wrap(&functions::sin_s32), compileTimeConstant); + module.addNativeFunction("cos_s16", lemon::wrap(&functions::cos_s16), compileTimeConstant); + module.addNativeFunction("cos_s32", lemon::wrap(&functions::cos_s32), compileTimeConstant); + + // Math + { + module.addNativeFunction("Math.sqr", lemon::wrap(&functions::Math_sqr), compileTimeConstant); + module.addNativeFunction("Math.sqr", lemon::wrap(&functions::Math_sqr), compileTimeConstant); + module.addNativeFunction("Math.sqrt", lemon::wrap(&functions::Math_sqrt), compileTimeConstant); + module.addNativeFunction("Math.sqrt", lemon::wrap(&functions::Math_sqrt), compileTimeConstant); + module.addNativeFunction("Math.pow", lemon::wrap(&functions::Math_pow), compileTimeConstant); + module.addNativeFunction("Math.pow", lemon::wrap(&functions::Math_pow), compileTimeConstant); + module.addNativeFunction("Math.exp", lemon::wrap(&functions::Math_exp), compileTimeConstant); + module.addNativeFunction("Math.exp", lemon::wrap(&functions::Math_exp), compileTimeConstant); + module.addNativeFunction("Math.log", lemon::wrap(&functions::Math_log), compileTimeConstant); + module.addNativeFunction("Math.log", lemon::wrap(&functions::Math_log), compileTimeConstant); + + module.addNativeFunction("Math.sin", lemon::wrap(&functions::Math_sin), compileTimeConstant); + module.addNativeFunction("Math.sin", lemon::wrap(&functions::Math_sin), compileTimeConstant); + module.addNativeFunction("Math.cos", lemon::wrap(&functions::Math_cos), compileTimeConstant); + module.addNativeFunction("Math.cos", lemon::wrap(&functions::Math_cos), compileTimeConstant); + module.addNativeFunction("Math.tan", lemon::wrap(&functions::Math_tan), compileTimeConstant); + module.addNativeFunction("Math.tan", lemon::wrap(&functions::Math_tan), compileTimeConstant); + module.addNativeFunction("Math.asin", lemon::wrap(&functions::Math_asin), compileTimeConstant); + module.addNativeFunction("Math.asin", lemon::wrap(&functions::Math_asin), compileTimeConstant); + module.addNativeFunction("Math.acos", lemon::wrap(&functions::Math_acos), compileTimeConstant); + module.addNativeFunction("Math.acos", lemon::wrap(&functions::Math_acos), compileTimeConstant); + module.addNativeFunction("Math.atan", lemon::wrap(&functions::Math_atan), compileTimeConstant); + module.addNativeFunction("Math.atan", lemon::wrap(&functions::Math_atan), compileTimeConstant); + module.addNativeFunction("Math.atan2", lemon::wrap(&functions::Math_atan2), compileTimeConstant); + module.addNativeFunction("Math.atan2", lemon::wrap(&functions::Math_atan2), compileTimeConstant); + + module.addNativeFunction("Math.degreesToRadians", lemon::wrap(&functions::Math_degreesToRadians), compileTimeConstant); + module.addNativeFunction("Math.degreesToRadians", lemon::wrap(&functions::Math_degreesToRadians), compileTimeConstant); + module.addNativeFunction("Math.radiansToDegrees", lemon::wrap(&functions::Math_radiansToDegrees), compileTimeConstant); + module.addNativeFunction("Math.radiansToDegrees", lemon::wrap(&functions::Math_radiansToDegrees), compileTimeConstant); + module.addNativeFunction("Math.u8ToDegrees", lemon::wrap(&functions::Math_u8ToDegrees), compileTimeConstant); + module.addNativeFunction("Math.u8ToRadians", lemon::wrap(&functions::Math_u8ToRadians), compileTimeConstant); + module.addNativeFunction("Math.u8FromDegrees", lemon::wrap(&functions::Math_u8FromDegrees), compileTimeConstant); + module.addNativeFunction("Math.u8FromRadians", lemon::wrap(&functions::Math_u8FromRadians), compileTimeConstant); + + module.addNativeFunction("Math.floor", lemon::wrap(&functions::Math_floor), compileTimeConstant); + module.addNativeFunction("Math.floor", lemon::wrap(&functions::Math_floor), compileTimeConstant); + module.addNativeFunction("Math.floorToInt", lemon::wrap(&functions::Math_floorToInt), compileTimeConstant); + module.addNativeFunction("Math.floorToInt", lemon::wrap(&functions::Math_floorToInt), compileTimeConstant); + module.addNativeFunction("Math.ceil", lemon::wrap(&functions::Math_ceil), compileTimeConstant); + module.addNativeFunction("Math.ceil", lemon::wrap(&functions::Math_ceil), compileTimeConstant); + module.addNativeFunction("Math.ceilToInt", lemon::wrap(&functions::Math_ceilToInt), compileTimeConstant); + module.addNativeFunction("Math.ceilToInt", lemon::wrap(&functions::Math_ceilToInt), compileTimeConstant); + module.addNativeFunction("Math.round", lemon::wrap(&functions::Math_round), compileTimeConstant); + module.addNativeFunction("Math.round", lemon::wrap(&functions::Math_round), compileTimeConstant); + module.addNativeFunction("Math.roundToInt", lemon::wrap(&functions::Math_roundToInt), compileTimeConstant); + module.addNativeFunction("Math.roundToInt", lemon::wrap(&functions::Math_roundToInt), compileTimeConstant); + module.addNativeFunction("Math.frac", lemon::wrap(&functions::Math_frac), compileTimeConstant); + module.addNativeFunction("Math.frac", lemon::wrap(&functions::Math_frac), compileTimeConstant); + + module.addNativeFunction("Math.isNumber", lemon::wrap(&functions::Math_isNumber), compileTimeConstant); + module.addNativeFunction("Math.isNumber", lemon::wrap(&functions::Math_isNumber), compileTimeConstant); + module.addNativeFunction("Math.isNaN", lemon::wrap(&functions::Math_isNaN), compileTimeConstant); + module.addNativeFunction("Math.isNaN", lemon::wrap(&functions::Math_isNaN), compileTimeConstant); + module.addNativeFunction("Math.isInfinite", lemon::wrap(&functions::Math_isInfinite), compileTimeConstant); + module.addNativeFunction("Math.isInfinite", lemon::wrap(&functions::Math_isInfinite), compileTimeConstant); + + module.addNativeFunction("Math.lerp", lemon::wrap(&functions::Math_lerp), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "factor"); + + module.addNativeFunction("Math.lerp", lemon::wrap(&functions::Math_lerp), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "factor"); + + module.addNativeFunction("Math.lerp", lemon::wrap(&functions::Math_lerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "factor"); + + module.addNativeFunction("Math.lerp", lemon::wrap(&functions::Math_lerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "factor"); + + module.addNativeFunction("Math.lerp", lemon::wrap(&functions::Math_lerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "factor"); + + module.addNativeFunction("Math.lerp", lemon::wrap(&functions::Math_lerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "factor"); + + module.addNativeFunction("Math.lerp", lemon::wrap(&functions::Math_lerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "factor"); + + module.addNativeFunction("Math.lerp", lemon::wrap(&functions::Math_lerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "factor"); + + module.addNativeFunction("Math.lerp", lemon::wrap(&functions::Math_lerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "factor"); + + module.addNativeFunction("Math.lerp", lemon::wrap(&functions::Math_lerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "factor"); + + module.addNativeFunction("Math.invlerp", lemon::wrap(&functions::Math_invlerp), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "value"); + + module.addNativeFunction("Math.invlerp", lemon::wrap(&functions::Math_invlerp), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "value"); + + module.addNativeFunction("Math.invlerp", lemon::wrap(&functions::Math_invlerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "value"); + + module.addNativeFunction("Math.invlerp", lemon::wrap(&functions::Math_invlerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "value"); + + module.addNativeFunction("Math.invlerp", lemon::wrap(&functions::Math_invlerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "value"); + + module.addNativeFunction("Math.invlerp", lemon::wrap(&functions::Math_invlerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "value"); + + module.addNativeFunction("Math.invlerp", lemon::wrap(&functions::Math_invlerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "value"); + + module.addNativeFunction("Math.invlerp", lemon::wrap(&functions::Math_invlerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "value"); + + module.addNativeFunction("Math.invlerp", lemon::wrap(&functions::Math_invlerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "value"); + + module.addNativeFunction("Math.invlerp", lemon::wrap(&functions::Math_invlerp_int), compileTimeConstant) + .setParameterInfo(0, "a") + .setParameterInfo(1, "b") + .setParameterInfo(2, "value"); + } + + module.addNativeFunction("stringformat", lemon::wrap(&functions::stringformat1), defaultFlags) + .setParameterInfo(0, "format") + .setParameterInfo(1, "arg1"); + + module.addNativeFunction("stringformat", lemon::wrap(&functions::stringformat2), defaultFlags) + .setParameterInfo(0, "format") + .setParameterInfo(1, "arg1") + .setParameterInfo(2, "arg2"); + + module.addNativeFunction("stringformat", lemon::wrap(&functions::stringformat3), defaultFlags) + .setParameterInfo(0, "format") + .setParameterInfo(1, "arg1") + .setParameterInfo(2, "arg2") + .setParameterInfo(3, "arg3"); + + module.addNativeFunction("stringformat", lemon::wrap(&functions::stringformat4), defaultFlags) + .setParameterInfo(0, "format") + .setParameterInfo(1, "arg1") + .setParameterInfo(2, "arg2") + .setParameterInfo(3, "arg3") + .setParameterInfo(4, "arg4"); + + module.addNativeFunction("stringformat", lemon::wrap(&functions::stringformat5), defaultFlags) + .setParameterInfo(0, "format") + .setParameterInfo(1, "arg1") + .setParameterInfo(2, "arg2") + .setParameterInfo(3, "arg3") + .setParameterInfo(4, "arg4") + .setParameterInfo(5, "arg5"); + + module.addNativeFunction("stringformat", lemon::wrap(&functions::stringformat6), defaultFlags) + .setParameterInfo(0, "format") + .setParameterInfo(1, "arg1") + .setParameterInfo(2, "arg2") + .setParameterInfo(3, "arg3") + .setParameterInfo(4, "arg4") + .setParameterInfo(5, "arg5") + .setParameterInfo(6, "arg6"); + + module.addNativeFunction("stringformat", lemon::wrap(&functions::stringformat7), defaultFlags) + .setParameterInfo(0, "format") + .setParameterInfo(1, "arg1") + .setParameterInfo(2, "arg2") + .setParameterInfo(3, "arg3") + .setParameterInfo(4, "arg4") + .setParameterInfo(5, "arg5") + .setParameterInfo(6, "arg6") + .setParameterInfo(7, "arg7"); + + module.addNativeFunction("stringformat", lemon::wrap(&functions::stringformat8), defaultFlags) + .setParameterInfo(0, "format") + .setParameterInfo(1, "arg1") + .setParameterInfo(2, "arg2") + .setParameterInfo(3, "arg3") + .setParameterInfo(4, "arg4") + .setParameterInfo(5, "arg5") + .setParameterInfo(6, "arg6") + .setParameterInfo(7, "arg7") + .setParameterInfo(8, "arg8"); + + module.addNativeFunction("strlen", lemon::wrap(&functions::string_length), defaultFlags) + .setParameterInfo(0, "str"); + + module.addNativeFunction("getchar", lemon::wrap(&functions::string_getCharacter), defaultFlags) + .setParameterInfo(0, "str") + .setParameterInfo(1, "index"); + + module.addNativeFunction("substring", lemon::wrap(&functions::string_getSubString), defaultFlags) + .setParameterInfo(0, "str") + .setParameterInfo(1, "index") + .setParameterInfo(2, "length"); + + module.addNativeMethod("string", "length", lemon::wrap(&functions::string_length), defaultFlags); + + module.addNativeMethod("string", "isEmpty", lemon::wrap(&functions::string_isEmpty), defaultFlags); + + module.addNativeMethod("string", "getCharacter", lemon::wrap(&functions::string_getCharacter), defaultFlags) + .setParameterInfo(0, "str") + .setParameterInfo(1, "index"); + + module.addNativeMethod("string", "getSubString", lemon::wrap(&functions::string_getSubString), defaultFlags) + .setParameterInfo(0, "str") + .setParameterInfo(1, "index") + .setParameterInfo(2, "length"); + + module.addNativeMethod("string", "startsWith", lemon::wrap(&functions::string_startsWith), defaultFlags) + .setParameterInfo(0, "substring"); + + module.addNativeMethod("string", "endsWith", lemon::wrap(&functions::string_endsWith), defaultFlags) + .setParameterInfo(0, "substring"); + + module.addNativeMethod("string", "find", lemon::wrap(&functions::string_find), defaultFlags) + .setParameterInfo(0, "substring"); + + module.addNativeFunction("getStringFromCharacter", lemon::wrap(&functions::getStringFromCharacter), defaultFlags) + .setParameterInfo(0, "character"); + + module.addNativeFunction("getStringFromHash", lemon::wrap(&functions::getStringFromHash), defaultFlags) + .setParameterInfo(0, "hash"); + } +} diff --git a/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/StandardLibrary.h b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/StandardLibrary.h new file mode 100644 index 00000000..bf85343e --- /dev/null +++ b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/StandardLibrary.h @@ -0,0 +1,23 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#pragma once + +#include "lemon/utility/FlyweightString.h" + + +namespace lemon +{ + class Module; + + class API_EXPORT StandardLibrary + { + public: + static void registerBindings(Module& module); + }; +} diff --git a/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/DefaultOpcodeProvider.cpp b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/DefaultOpcodeProvider.cpp new file mode 100644 index 00000000..5fa68e1f --- /dev/null +++ b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/DefaultOpcodeProvider.cpp @@ -0,0 +1,718 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#include "lemon/pch.h" +#include "lemon/runtime/provider/DefaultOpcodeProvider.h" +#include "lemon/runtime/RuntimeFunction.h" +#include "lemon/runtime/RuntimeOpcodeContext.h" +#include "lemon/runtime/OpcodeExecUtils.h" +#include "lemon/program/OpcodeHelper.h" +#include "lemon/program/Program.h" + + +namespace lemon +{ + #define SELECT_EXEC_FUNC_BY_DATATYPE(_function_) \ + { \ + switch (opcode.mDataType) \ + { \ + case BaseType::INT_8: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::INT_16: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::INT_32: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::INT_64: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::UINT_8: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::UINT_16: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::UINT_32: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::UINT_64: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::INT_CONST: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::FLOAT: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::DOUBLE: runtimeOpcode.mExecFunc = &_function_; break; \ + default: \ + throw std::runtime_error("Invalid opcode data type"); \ + } \ + } + + #define SELECT_EXEC_FUNC_BY_DATATYPE_INT(_function_) \ + { \ + switch (opcode.mDataType) \ + { \ + case BaseType::INT_8: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::INT_16: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::INT_32: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::INT_64: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::UINT_8: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::UINT_16: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::UINT_32: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::UINT_64: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::INT_CONST: runtimeOpcode.mExecFunc = &_function_; break; \ + default: \ + throw std::runtime_error("Invalid opcode data type"); \ + } \ + } + + #define SELECT_EXEC_FUNC_BY_DATATYPE_SIGNED(_function_) \ + { \ + const BaseType baseType = BaseTypeHelper::isIntegerType(opcode.mDataType) ? BaseTypeHelper::makeIntegerSigned(opcode.mDataType) : opcode.mDataType; \ + switch (baseType) \ + { \ + case BaseType::INT_8: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::INT_16: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::INT_32: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::INT_64: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::INT_CONST: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::FLOAT: runtimeOpcode.mExecFunc = &_function_; break; \ + case BaseType::DOUBLE: runtimeOpcode.mExecFunc = &_function_; break; \ + default: \ + throw std::runtime_error("Invalid opcode data type"); \ + } \ + } + + + class OpcodeExec + { + public: + static void exec_NOP(const RuntimeOpcodeContext context) + { + } + + static void exec_MOVE_STACK_positive(const RuntimeOpcodeContext context) + { + const int count = (int)context.getParameter(); + for (int i = 0; i < count; ++i) + context.writeValueStack(i, 0); + context.moveValueStack(count); + } + + static void exec_MOVE_STACK_negative(const RuntimeOpcodeContext context) + { + context.moveValueStack(context.getParameter()); + } + + static void exec_MOVE_STACK_m1(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + } + + static void exec_MOVE_VAR_STACK_positive(const RuntimeOpcodeContext context) + { + const int count = (int)context.getParameter(); + int64* variables = &context.mControlFlow->mLocalVariablesBuffer[context.mControlFlow->mLocalVariablesSize]; + memset(variables, 0, count * sizeof(int64)); + context.mControlFlow->mLocalVariablesSize += count; + RMX_CHECK(context.mControlFlow->mLocalVariablesSize <= ControlFlow::VAR_STACK_LIMIT, "Reached var stack limit, probably due to recursive function calls", RMX_REACT_THROW); + } + + static void exec_MOVE_VAR_STACK_negative(const RuntimeOpcodeContext context) + { + const int count = (int)context.getParameter(); + context.mControlFlow->mLocalVariablesSize += count; + } + + static void exec_PUSH_CONSTANT(const RuntimeOpcodeContext context) + { + *context.mControlFlow->mValueStackPtr = context.getParameter(); + ++context.mControlFlow->mValueStackPtr; + } + + static void exec_GET_VARIABLE_VALUE_LOCAL(const RuntimeOpcodeContext context) + { + const uint32 variableId = context.getParameter(); + *context.mControlFlow->mValueStackPtr = context.readLocalVariable(variableId); + ++context.mControlFlow->mValueStackPtr; + } + + static void exec_GET_VARIABLE_VALUE_USER(const RuntimeOpcodeContext context) + { + const uint32 variableId = context.getParameter(); + const GlobalVariable& variable = static_cast(context.mControlFlow->getProgram().getGlobalVariableByID(variableId)); + *context.mControlFlow->mValueStackPtr = variable.getValue(); + ++context.mControlFlow->mValueStackPtr; + } + + template + static void exec_GET_VARIABLE_VALUE_EXTERNAL(const RuntimeOpcodeContext context) + { + *context.mControlFlow->mValueStackPtr = *context.getParameter(); + ++context.mControlFlow->mValueStackPtr; + } + + static void exec_SET_VARIABLE_VALUE_LOCAL(const RuntimeOpcodeContext context) + { + const int64 value = *(context.mControlFlow->mValueStackPtr-1); + const uint32 variableId = context.getParameter(); + context.writeLocalVariable(variableId, value); + } + + static void exec_SET_VARIABLE_VALUE_USER(const RuntimeOpcodeContext context) + { + const int64 value = *(context.mControlFlow->mValueStackPtr-1); + const uint32 variableId = context.getParameter(); + GlobalVariable& variable = static_cast(context.mControlFlow->getProgram().getGlobalVariableByID(variableId)); + variable.setValue(value); + } + + template + static void exec_SET_VARIABLE_VALUE_EXTERNAL(const RuntimeOpcodeContext context) + { + const int64 value = *(context.mControlFlow->mValueStackPtr-1); + *context.getParameter() = (T)value; + } + + template + static void exec_READ_MEMORY(const RuntimeOpcodeContext context) + { + const uint64 address = *(context.mControlFlow->mValueStackPtr-1); + *(context.mControlFlow->mValueStackPtr-1) = OpcodeExecUtils::readMemory(*context.mControlFlow, address); + } + + template + static void exec_READ_MEMORY_NOCONSUME(const RuntimeOpcodeContext context) + { + const uint64 address = *(context.mControlFlow->mValueStackPtr-1); + *context.mControlFlow->mValueStackPtr = OpcodeExecUtils::readMemory(*context.mControlFlow, address); + ++context.mControlFlow->mValueStackPtr; + } + + template + static void exec_WRITE_MEMORY(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + const uint64 address = *context.mControlFlow->mValueStackPtr; + OpcodeExecUtils::writeMemory(*context.mControlFlow, address, (T)(*(context.mControlFlow->mValueStackPtr-1))); + } + + template + static void exec_WRITE_MEMORY_EXCHANGED(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + const uint64 address = *(context.mControlFlow->mValueStackPtr - 1); + const T value = (T)(*context.mControlFlow->mValueStackPtr); + OpcodeExecUtils::writeMemory(*context.mControlFlow, address, value); + *(context.mControlFlow->mValueStackPtr - 1) = value; // Replace top-of-stack (still the address) with the value + } + + template + static void exec_CAST_VALUE(const RuntimeOpcodeContext context) + { + const S value = context.readValueStack(-1); + context.writeValueStack(-1, static_cast(value)); + } + + static void exec_MAKE_BOOL(const RuntimeOpcodeContext context) + { + *(context.mControlFlow->mValueStackPtr-1) = (*(context.mControlFlow->mValueStackPtr-1) != 0) ? 1 : 0; + } + + template + static void exec_ARITHM_BINARY_ADD(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, context.readValueStack(-1) + context.readValueStack(0)); + } + + template + static void exec_ARITHM_BINARY_SUB(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, context.readValueStack(-1) - context.readValueStack(0)); + } + + template + static void exec_ARITHM_BINARY_MUL(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, context.readValueStack(-1) * context.readValueStack(0)); + } + + template + static void exec_ARITHM_BINARY_DIV(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, OpcodeExecUtils::safeDivide(context.readValueStack(-1), context.readValueStack(0))); + } + + template + static void exec_ARITHM_BINARY_MOD(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, OpcodeExecUtils::safeModulo(context.readValueStack(-1), context.readValueStack(0))); + } + + template + static void exec_ARITHM_BINARY_AND(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, context.readValueStack(-1) & context.readValueStack(0)); + } + + template + static void exec_ARITHM_BINARY_OR(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, context.readValueStack(-1) | context.readValueStack(0)); + } + + template + static void exec_ARITHM_BINARY_XOR(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, context.readValueStack(-1) ^ context.readValueStack(0)); + } + + template + static void exec_ARITHM_BINARY_SHL(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, context.readValueStack(-1) << (context.readValueStack(0) & (sizeof(T) * 8 - 1))); + } + + template + static void exec_ARITHM_BINARY_SHR(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, context.readValueStack(-1) >> (context.readValueStack(0) & (sizeof(T) * 8 - 1))); + } + + template + static void exec_ARITHM_BINARY_CMP_EQ(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, (context.readValueStack(-1) == context.readValueStack(0)) ? 1 : 0); + } + + template + static void exec_ARITHM_BINARY_CMP_NEQ(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, (context.readValueStack(-1) != context.readValueStack(0)) ? 1 : 0); + } + + template + static void exec_ARITHM_BINARY_CMP_LT(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, (context.readValueStack(-1) < context.readValueStack(0)) ? 1 : 0); + } + + template + static void exec_ARITHM_BINARY_CMP_LE(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, (context.readValueStack(-1) <= context.readValueStack(0)) ? 1 : 0); + } + + template + static void exec_ARITHM_BINARY_CMP_GT(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, (context.readValueStack(-1) > context.readValueStack(0)) ? 1 : 0); + } + + template + static void exec_ARITHM_BINARY_CMP_GE(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + context.writeValueStack(-1, (context.readValueStack(-1) >= context.readValueStack(0)) ? 1 : 0); + } + + template + static void exec_ARITHM_UNARY_NEG(const RuntimeOpcodeContext context) + { + context.writeValueStack(-1, -context.readValueStack(-1)); + } + + template + static void exec_ARITHM_UNARY_NOT(const RuntimeOpcodeContext context) + { + context.writeValueStack(-1, (context.readValueStack(-1) == 0) ? 1 : 0); + } + + template + static void exec_ARITHM_UNARY_BITNOT(const RuntimeOpcodeContext context) + { + context.writeValueStack(-1, ~context.readValueStack(-1)); + } + + #ifdef USE_JUMP_CONDITIONAL_RUNTIME_EXEC + static void exec_JUMP_CONDITIONAL(const RuntimeOpcodeContext context) + { + --context.mControlFlow->mValueStackPtr; + const size_t index = (*context.mControlFlow->mValueStackPtr == 0) ? 0 : 8; // Parameter index 0 if condition is true (i.e. value stack is zero), otherwise index 8 + const_cast(context.mOpcode)->mNext = context.mOpcode->getParameter(index); + } + #endif + + static void exec_INLINE_NATIVE_CALL(const RuntimeOpcodeContext context) + { + const NativeFunction& func = *context.mOpcode->getParameter(); + func.execute(NativeFunction::Context(*context.mControlFlow)); + } + + static void exec_NOT_HANDLED(const RuntimeOpcodeContext context) + { + throw std::runtime_error("Unhandled opcode"); + } + }; + + + + void DefaultOpcodeProvider::buildRuntimeOpcodeStatic(RuntimeOpcodeBuffer& buffer, const Opcode* opcodes, int numOpcodesAvailable, int firstOpcodeIndex, int& outNumOpcodesConsumed, const Runtime& runtime) + { + const Opcode& opcode = opcodes[0]; + outNumOpcodesConsumed = 1; + + // Get parameter size + // -> It's usually 8 bytes = 1 parameter, but not all opcodes will actually use parameter + size_t parameterSize = 8; + switch (opcode.mType) + { + case Opcode::Type::MOVE_STACK: + parameterSize = (opcode.mParameter == -1) ? 0 : 8; + break; + case Opcode::Type::NOP: + case Opcode::Type::READ_MEMORY: + case Opcode::Type::WRITE_MEMORY: + case Opcode::Type::MAKE_BOOL: + case Opcode::Type::ARITHM_ADD: + case Opcode::Type::ARITHM_SUB: + case Opcode::Type::ARITHM_MUL: + case Opcode::Type::ARITHM_DIV: + case Opcode::Type::ARITHM_MOD: + case Opcode::Type::ARITHM_AND: + case Opcode::Type::ARITHM_OR: + case Opcode::Type::ARITHM_XOR: + case Opcode::Type::ARITHM_SHL: + case Opcode::Type::ARITHM_SHR: + case Opcode::Type::COMPARE_EQ: + case Opcode::Type::COMPARE_NEQ: + case Opcode::Type::COMPARE_LT: + case Opcode::Type::COMPARE_LE: + case Opcode::Type::COMPARE_GT: + case Opcode::Type::COMPARE_GE: + case Opcode::Type::ARITHM_NEG: + case Opcode::Type::ARITHM_NOT: + case Opcode::Type::ARITHM_BITNOT: + case Opcode::Type::RETURN: + case Opcode::Type::EXTERNAL_CALL: + case Opcode::Type::EXTERNAL_JUMP: + parameterSize = 0; + break; + #ifdef USE_JUMP_CONDITIONAL_RUNTIME_EXEC + case Opcode::Type::JUMP_CONDITIONAL: + parameterSize = 16; + break; + #endif + default: + parameterSize = 8; + break; + } + + RuntimeOpcode& runtimeOpcode = buffer.addOpcode(parameterSize); + if (parameterSize >= 8) + runtimeOpcode.setParameter(opcode.mParameter); // Default usage, parameter might be used differently depending on the opcode type + runtimeOpcode.mExecFunc = &OpcodeExec::exec_NOT_HANDLED; + runtimeOpcode.mOpcodeType = opcode.mType; + + switch (opcode.mType) + { + case Opcode::Type::NOP: + runtimeOpcode.mExecFunc = &OpcodeExec::exec_NOP; + break; + + case Opcode::Type::MOVE_STACK: + runtimeOpcode.mExecFunc = (opcode.mParameter >= 0) ? OpcodeExec::exec_MOVE_STACK_positive : + (opcode.mParameter == -1) ? &OpcodeExec::exec_MOVE_STACK_m1 : &OpcodeExec::exec_MOVE_STACK_negative; + break; + + case Opcode::Type::MOVE_VAR_STACK: + runtimeOpcode.mExecFunc = (opcode.mParameter >= 0) ? &OpcodeExec::exec_MOVE_VAR_STACK_positive : &OpcodeExec::exec_MOVE_VAR_STACK_negative; + break; + + case Opcode::Type::PUSH_CONSTANT: + runtimeOpcode.mExecFunc = &OpcodeExec::exec_PUSH_CONSTANT; + break; + + case Opcode::Type::GET_VARIABLE_VALUE: + { + const uint32 variableId = (uint32)opcode.mParameter; + const Variable::Type type = (Variable::Type)(variableId >> 28); + switch (type) + { + case Variable::Type::LOCAL: runtimeOpcode.mExecFunc = &OpcodeExec::exec_GET_VARIABLE_VALUE_LOCAL; break; + case Variable::Type::USER: runtimeOpcode.mExecFunc = &OpcodeExec::exec_GET_VARIABLE_VALUE_USER; break; + + case Variable::Type::GLOBAL: + { + int64* value = const_cast(runtime).accessGlobalVariableValue(runtime.getProgram().getGlobalVariableByID(variableId)); + runtimeOpcode.setParameter(value); + + switch (DataTypeHelper::getSizeOfBaseType(opcode.mDataType)) + { + case 1: runtimeOpcode.mExecFunc = &OpcodeExec::exec_GET_VARIABLE_VALUE_EXTERNAL; break; + case 2: runtimeOpcode.mExecFunc = &OpcodeExec::exec_GET_VARIABLE_VALUE_EXTERNAL; break; + case 4: runtimeOpcode.mExecFunc = &OpcodeExec::exec_GET_VARIABLE_VALUE_EXTERNAL; break; + case 8: runtimeOpcode.mExecFunc = &OpcodeExec::exec_GET_VARIABLE_VALUE_EXTERNAL; break; + } + break; + } + + case Variable::Type::EXTERNAL: + { + const ExternalVariable& variable = static_cast(runtime.getProgram().getGlobalVariableByID(variableId)); + runtimeOpcode.setParameter(variable.mAccessor()); + + switch (variable.getDataType()->getBytes()) + { + case 1: runtimeOpcode.mExecFunc = &OpcodeExec::exec_GET_VARIABLE_VALUE_EXTERNAL; break; + case 2: runtimeOpcode.mExecFunc = &OpcodeExec::exec_GET_VARIABLE_VALUE_EXTERNAL; break; + case 4: runtimeOpcode.mExecFunc = &OpcodeExec::exec_GET_VARIABLE_VALUE_EXTERNAL; break; + case 8: runtimeOpcode.mExecFunc = &OpcodeExec::exec_GET_VARIABLE_VALUE_EXTERNAL; break; + } + break; + } + } + break; + } + + case Opcode::Type::SET_VARIABLE_VALUE: + { + const uint32 variableId = (uint32)opcodes[0].mParameter; + const Variable::Type type = (Variable::Type)(variableId >> 28); + switch (type) + { + case Variable::Type::LOCAL: runtimeOpcode.mExecFunc = &OpcodeExec::exec_SET_VARIABLE_VALUE_LOCAL; break; + case Variable::Type::USER: runtimeOpcode.mExecFunc = &OpcodeExec::exec_SET_VARIABLE_VALUE_USER; break; + + case Variable::Type::GLOBAL: + { + int64* value = const_cast(runtime).accessGlobalVariableValue(runtime.getProgram().getGlobalVariableByID(variableId)); + runtimeOpcode.setParameter(value); + + switch (DataTypeHelper::getSizeOfBaseType(opcode.mDataType)) + { + case 1: runtimeOpcode.mExecFunc = &OpcodeExec::exec_SET_VARIABLE_VALUE_EXTERNAL; break; + case 2: runtimeOpcode.mExecFunc = &OpcodeExec::exec_SET_VARIABLE_VALUE_EXTERNAL; break; + case 4: runtimeOpcode.mExecFunc = &OpcodeExec::exec_SET_VARIABLE_VALUE_EXTERNAL; break; + case 8: runtimeOpcode.mExecFunc = &OpcodeExec::exec_SET_VARIABLE_VALUE_EXTERNAL; break; + } + break; + } + + case Variable::Type::EXTERNAL: + { + const ExternalVariable& variable = static_cast(runtime.getProgram().getGlobalVariableByID(variableId)); + runtimeOpcode.setParameter(variable.mAccessor()); + + switch (variable.getDataType()->getBytes()) + { + case 1: runtimeOpcode.mExecFunc = &OpcodeExec::exec_SET_VARIABLE_VALUE_EXTERNAL; break; + case 2: runtimeOpcode.mExecFunc = &OpcodeExec::exec_SET_VARIABLE_VALUE_EXTERNAL; break; + case 4: runtimeOpcode.mExecFunc = &OpcodeExec::exec_SET_VARIABLE_VALUE_EXTERNAL; break; + case 8: runtimeOpcode.mExecFunc = &OpcodeExec::exec_SET_VARIABLE_VALUE_EXTERNAL; break; + } + break; + } + } + break; + } + + case Opcode::Type::READ_MEMORY: + { + if (opcode.mParameter == 0) + { + SELECT_EXEC_FUNC_BY_DATATYPE_INT(OpcodeExec::exec_READ_MEMORY); + } + else + { + SELECT_EXEC_FUNC_BY_DATATYPE_INT(OpcodeExec::exec_READ_MEMORY_NOCONSUME); + } + break; + } + + case Opcode::Type::WRITE_MEMORY: + { + if (opcode.mParameter == 0) + { + SELECT_EXEC_FUNC_BY_DATATYPE_INT(OpcodeExec::exec_WRITE_MEMORY); + } + else + { + SELECT_EXEC_FUNC_BY_DATATYPE_INT(OpcodeExec::exec_WRITE_MEMORY_EXCHANGED); + } + break; + } + + case Opcode::Type::CAST_VALUE: + { + const BaseCastType baseCastType = static_cast(opcode.mParameter); + switch (baseCastType) + { + // Cast down (signed or unsigned makes no difference here) + case BaseCastType::INT_16_TO_8: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::INT_32_TO_8: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::INT_64_TO_8: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::INT_32_TO_16: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::INT_64_TO_16: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::INT_64_TO_32: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + + // Cast up (value is unsigned -> adding zeroes) + case BaseCastType::UINT_8_TO_16: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::UINT_8_TO_32: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::UINT_8_TO_64: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::UINT_16_TO_32: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::UINT_16_TO_64: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::UINT_32_TO_64: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + + // Cast up (value is signed -> adding highest bit) + case BaseCastType::SINT_8_TO_16: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_8_TO_32: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_8_TO_64: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_16_TO_32: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_16_TO_64: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_32_TO_64: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + + // Integer cast to float + case BaseCastType::UINT_8_TO_FLOAT: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::UINT_16_TO_FLOAT: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::UINT_32_TO_FLOAT: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::UINT_64_TO_FLOAT: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_8_TO_FLOAT: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_16_TO_FLOAT: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_32_TO_FLOAT: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_64_TO_FLOAT: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + + case BaseCastType::UINT_8_TO_DOUBLE: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::UINT_16_TO_DOUBLE: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::UINT_32_TO_DOUBLE: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::UINT_64_TO_DOUBLE: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_8_TO_DOUBLE: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_16_TO_DOUBLE: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_32_TO_DOUBLE: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::SINT_64_TO_DOUBLE: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + + // Float cast to integer + case BaseCastType::FLOAT_TO_UINT_8: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::FLOAT_TO_UINT_16: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::FLOAT_TO_UINT_32: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::FLOAT_TO_UINT_64: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::FLOAT_TO_SINT_8: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::FLOAT_TO_SINT_16: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::FLOAT_TO_SINT_32: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::FLOAT_TO_SINT_64: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + + case BaseCastType::DOUBLE_TO_UINT_8: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::DOUBLE_TO_UINT_16: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::DOUBLE_TO_UINT_32: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::DOUBLE_TO_UINT_64: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::DOUBLE_TO_SINT_8: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::DOUBLE_TO_SINT_16: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::DOUBLE_TO_SINT_32: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::DOUBLE_TO_SINT_64: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + + // Float cast + case BaseCastType::FLOAT_TO_DOUBLE: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + case BaseCastType::DOUBLE_TO_FLOAT: runtimeOpcode.mExecFunc = &OpcodeExec::exec_CAST_VALUE; break; + + default: + throw std::runtime_error("Unrecognized cast type"); + } + break; + } + + case Opcode::Type::MAKE_BOOL: + { + runtimeOpcode.mExecFunc = &OpcodeExec::exec_MAKE_BOOL; + break; + } + + case Opcode::Type::ARITHM_ADD: SELECT_EXEC_FUNC_BY_DATATYPE(OpcodeExec::exec_ARITHM_BINARY_ADD); break; + case Opcode::Type::ARITHM_SUB: SELECT_EXEC_FUNC_BY_DATATYPE(OpcodeExec::exec_ARITHM_BINARY_SUB); break; + case Opcode::Type::ARITHM_MUL: SELECT_EXEC_FUNC_BY_DATATYPE(OpcodeExec::exec_ARITHM_BINARY_MUL); break; + case Opcode::Type::ARITHM_DIV: SELECT_EXEC_FUNC_BY_DATATYPE(OpcodeExec::exec_ARITHM_BINARY_DIV); break; + case Opcode::Type::ARITHM_MOD: SELECT_EXEC_FUNC_BY_DATATYPE(OpcodeExec::exec_ARITHM_BINARY_MOD); break; + + case Opcode::Type::ARITHM_AND: SELECT_EXEC_FUNC_BY_DATATYPE_INT(OpcodeExec::exec_ARITHM_BINARY_AND); break; + case Opcode::Type::ARITHM_OR: SELECT_EXEC_FUNC_BY_DATATYPE_INT(OpcodeExec::exec_ARITHM_BINARY_OR); break; + case Opcode::Type::ARITHM_XOR: SELECT_EXEC_FUNC_BY_DATATYPE_INT(OpcodeExec::exec_ARITHM_BINARY_XOR); break; + case Opcode::Type::ARITHM_SHL: SELECT_EXEC_FUNC_BY_DATATYPE_INT(OpcodeExec::exec_ARITHM_BINARY_SHL); break; + case Opcode::Type::ARITHM_SHR: SELECT_EXEC_FUNC_BY_DATATYPE_INT(OpcodeExec::exec_ARITHM_BINARY_SHR); break; + + case Opcode::Type::COMPARE_EQ: SELECT_EXEC_FUNC_BY_DATATYPE(OpcodeExec::exec_ARITHM_BINARY_CMP_EQ); break; + case Opcode::Type::COMPARE_NEQ: SELECT_EXEC_FUNC_BY_DATATYPE(OpcodeExec::exec_ARITHM_BINARY_CMP_NEQ); break; + case Opcode::Type::COMPARE_LT: SELECT_EXEC_FUNC_BY_DATATYPE(OpcodeExec::exec_ARITHM_BINARY_CMP_LT); break; + case Opcode::Type::COMPARE_LE: SELECT_EXEC_FUNC_BY_DATATYPE(OpcodeExec::exec_ARITHM_BINARY_CMP_LE); break; + case Opcode::Type::COMPARE_GT: SELECT_EXEC_FUNC_BY_DATATYPE(OpcodeExec::exec_ARITHM_BINARY_CMP_GT); break; + case Opcode::Type::COMPARE_GE: SELECT_EXEC_FUNC_BY_DATATYPE(OpcodeExec::exec_ARITHM_BINARY_CMP_GE); break; + + case Opcode::Type::ARITHM_NEG: SELECT_EXEC_FUNC_BY_DATATYPE_SIGNED(OpcodeExec::exec_ARITHM_UNARY_NEG); break; + case Opcode::Type::ARITHM_NOT: SELECT_EXEC_FUNC_BY_DATATYPE(OpcodeExec::exec_ARITHM_UNARY_NOT); break; + case Opcode::Type::ARITHM_BITNOT: SELECT_EXEC_FUNC_BY_DATATYPE_INT(OpcodeExec::exec_ARITHM_UNARY_BITNOT); break; + + case Opcode::Type::JUMP: + case Opcode::Type::RETURN: + case Opcode::Type::EXTERNAL_CALL: + case Opcode::Type::EXTERNAL_JUMP: + { + runtimeOpcode.mSuccessiveHandledOpcodes = 0; + return; + } + + case Opcode::Type::JUMP_CONDITIONAL: + { + #ifdef USE_JUMP_CONDITIONAL_RUNTIME_EXEC + runtimeOpcode.mExecFunc = &OpcodeExec::exec_JUMP_CONDITIONAL; + runtimeOpcode.setParameter((uint32)opcode.mParameter, 0); // Jump target if condition is true + runtimeOpcode.setParameter((uint32)firstOpcodeIndex + 1, 8); // Pointer to next opcode + runtimeOpcode.mSuccessiveHandledOpcodes = 1; + #else + runtimeOpcode.mSuccessiveHandledOpcodes = 0; + #endif + return; + } + + case Opcode::Type::CALL: + { + const bool isBaseCall = ((uint32)opcode.mDataType != 0); + if (isBaseCall) + { + runtimeOpcode.mFlags.set(RuntimeOpcode::Flag::CALL_IS_BASE_CALL); + } + else + { + // If this is a native function, replace with a runtime opcode that just executes the function without the usual overheads + const Function* function = runtime.getProgram().getFunctionBySignature((uint64)opcode.mParameter); + if (nullptr != function && function->getType() == Function::Type::NATIVE && function->hasFlag(Function::Flag::ALLOW_INLINE_EXECUTION)) + { + runtimeOpcode.mExecFunc = &OpcodeExec::exec_INLINE_NATIVE_CALL; + runtimeOpcode.setParameter((uint64)function); + return; + } + } + + runtimeOpcode.mSuccessiveHandledOpcodes = 0; + return; + } + + default: + // Other opcode types are handled outside already + break; + } + + runtimeOpcode.mSuccessiveHandledOpcodes = (runtimeOpcode.mExecFunc == &OpcodeExec::exec_NOT_HANDLED) ? 0 : 1; + } + + bool DefaultOpcodeProvider::buildRuntimeOpcode(RuntimeOpcodeBuffer& buffer, const Opcode* opcodes, int numOpcodesAvailable, int firstOpcodeIndex, int& outNumOpcodesConsumed, const Runtime& runtime) + { + buildRuntimeOpcodeStatic(buffer, opcodes, numOpcodesAvailable, firstOpcodeIndex, outNumOpcodesConsumed, runtime); + return true; + } + + #undef SELECT_EXEC_FUNC_BY_DATATYPE + #undef SELECT_EXEC_FUNC_BY_DATATYPE_INT + #undef SELECT_EXEC_FUNC_BY_DATATYPE_SIGNED +} diff --git a/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/DefaultOpcodeProvider.h b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/DefaultOpcodeProvider.h new file mode 100644 index 00000000..e216f79c --- /dev/null +++ b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/DefaultOpcodeProvider.h @@ -0,0 +1,24 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#pragma once + +#include "lemon/runtime/RuntimeOpcode.h" + + +namespace lemon +{ + class DefaultOpcodeProvider final : public RuntimeOpcodeProvider + { + public: + static void buildRuntimeOpcodeStatic(RuntimeOpcodeBuffer& buffer, const Opcode* opcodes, int numOpcodesAvailable, int firstOpcodeIndex, int& outNumOpcodesConsumed, const Runtime& runtime); + + public: + bool buildRuntimeOpcode(RuntimeOpcodeBuffer& buffer, const Opcode* opcodes, int numOpcodesAvailable, int firstOpcodeIndex, int& outNumOpcodesConsumed, const Runtime& runtime) override; + }; +} diff --git a/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/NativizedOpcodeProvider.cpp b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/NativizedOpcodeProvider.cpp new file mode 100644 index 00000000..d9256df3 --- /dev/null +++ b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/NativizedOpcodeProvider.cpp @@ -0,0 +1,126 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#include "lemon/pch.h" +#include "lemon/runtime/provider/NativizedOpcodeProvider.h" +#include "lemon/runtime/RuntimeFunction.h" +#include "lemon/program/Program.h" +#include "lemon/program/Variable.h" + + +namespace lemon +{ + + void NativizedOpcodeProvider::buildLookup(BuildFunction buildFunction) + { + mLookupDictionary.mEntries.clear(); + (*buildFunction)(mLookupDictionary); + } + + bool NativizedOpcodeProvider::buildRuntimeOpcode(RuntimeOpcodeBuffer& buffer, const Opcode* opcodes, int numOpcodesAvailable, int firstOpcodeIndex, int& outNumOpcodesConsumed, const Runtime& runtime) + { + if (mLookupDictionary.mEntries.empty() || numOpcodesAvailable < (int)Nativizer::MIN_OPCODES) + return false; + + Nativizer::LookupEntry* bestEntry = nullptr; + uint64 bestEntryHash = 0; // Variable exists only for debugging + { + uint64 hash = Nativizer::getStartHash(); + for (size_t index = 0; index < (size_t)numOpcodesAvailable; ) + { + Nativizer::OpcodeSubtypeInfo info; + Nativizer::getOpcodeSubtypeInfo(info, &opcodes[index], numOpcodesAvailable, *runtime.getMemoryAccessHandler()); + hash = Nativizer::addOpcodeSubtypeInfoToHash(hash, info); + index += info.mConsumedOpcodes; + + if (index >= Nativizer::MIN_OPCODES) + { + const auto it = mLookupDictionary.mEntries.find(hash); + if (it == mLookupDictionary.mEntries.end()) + return false; + + if (nullptr != it->second.mExecFunc) + { + bestEntry = &it->second; + bestEntryHash = hash; + outNumOpcodesConsumed = (int)index; + } + } + } + } + + if (nullptr != bestEntry) + { + // Find out number of parameters and total parameter size first + const Nativizer::LookupEntry& entry = *bestEntry; + int numParameters = 0; + size_t parameterSize = 0; + { + const Nativizer::LookupEntry::ParameterInfo* parameterPtr = &mLookupDictionary.mParameterData[entry.mParameterStart]; + while (parameterPtr->mOpcodeIndex != 0xff) + { + ++parameterPtr; + ++numParameters; + } + parameterSize = (size_t)parameterPtr->mOffset; + } + + // Create runtime opcode + RuntimeOpcode& runtimeOpcode = buffer.addOpcode(parameterSize); + runtimeOpcode.mExecFunc = entry.mExecFunc; + { + // Go through all parameters (again), now adding them to the runtime opcode + const Nativizer::LookupEntry::ParameterInfo* parameterPtr = &mLookupDictionary.mParameterData[entry.mParameterStart]; + for (int k = 0; k < numParameters; ++k) + { + const Nativizer::LookupEntry::ParameterInfo& parameter = parameterPtr[k]; + const Opcode& opcode = opcodes[parameter.mOpcodeIndex]; + const int64 value = opcode.mParameter; + switch (parameter.mSemantics) + { + case Nativizer::LookupEntry::ParameterInfo::Semantics::INTEGER: + { + runtimeOpcode.setParameter(value, parameter.mOffset); + break; + } + + case Nativizer::LookupEntry::ParameterInfo::Semantics::GLOBAL_VARIABLE: + { + const uint32 variableId = (uint32)opcode.mParameter; + int64* valuePointer = const_cast(runtime).accessGlobalVariableValue(runtime.getProgram().getGlobalVariableByID(variableId)); + runtimeOpcode.setParameter(valuePointer, parameter.mOffset); + break; + } + + case Nativizer::LookupEntry::ParameterInfo::Semantics::EXTERNAL_VARIABLE: + { + const uint32 variableId = (uint32)opcode.mParameter; + const ExternalVariable& variable = static_cast(runtime.getProgram().getGlobalVariableByID(variableId)); + runtimeOpcode.setParameter(variable.mAccessor(), parameter.mOffset); + break; + } + + case Nativizer::LookupEntry::ParameterInfo::Semantics::FIXED_MEMORY_ADDRESS: + { + // TODO: "opcode.mDataType" refers to the PUSH_CONSTANT opcode, so it actually does not tell us the correct data type; however, this shouldn't be much of a problem for now + const uint64 address = opcode.mParameter; + MemoryAccessHandler::SpecializationResult result; + runtime.getMemoryAccessHandler()->getDirectAccessSpecialization(result, address, DataTypeHelper::getSizeOfBaseType(opcode.mDataType), false); // No support for write access here + RMX_ASSERT(result.mResult == MemoryAccessHandler::SpecializationResult::Result::HAS_SPECIALIZATION, "No memory access specialization found even though this was previously checked"); + runtimeOpcode.setParameter(result.mDirectAccessPointer, parameter.mOffset); + break; + } + } + } + } + return true; + } + return false; + } + +} diff --git a/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/NativizedOpcodeProvider.h b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/NativizedOpcodeProvider.h new file mode 100644 index 00000000..ddb20c78 --- /dev/null +++ b/sonic3air-main/Oxygen/lemonscript/source/lemon/runtime/provider/NativizedOpcodeProvider.h @@ -0,0 +1,33 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#pragma once + +#include "lemon/translator/Nativizer.h" + + +namespace lemon +{ + class API_EXPORT NativizedOpcodeProvider : public RuntimeOpcodeProvider + { + public: + typedef void (*BuildFunction)(Nativizer::LookupDictionary& dict); + + public: + inline NativizedOpcodeProvider() {} + inline NativizedOpcodeProvider(BuildFunction buildFunction) { buildLookup(buildFunction); } + + inline bool isValid() const { return !mLookupDictionary.mEntries.empty(); } + void buildLookup(BuildFunction buildFunction); + + bool buildRuntimeOpcode(RuntimeOpcodeBuffer& buffer, const Opcode* opcodes, int numOpcodesAvailable, int firstOpcodeIndex, int& outNumOpcodesConsumed, const Runtime& runtime) override; + + protected: + Nativizer::LookupDictionary mLookupDictionary; // This needs to be filled by either a sub-class implementation or a call to the buildLookup method + }; +}