├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── src └── mpsl │ ├── mpast.cpp │ ├── mpast_p.h │ ├── mpastoptimizer.cpp │ ├── mpastoptimizer_p.h │ ├── mpatomic_p.h │ ├── mpcodegen.cpp │ ├── mpcodegen_p.h │ ├── mpfold.cpp │ ├── mpfold_p.h │ ├── mpformatutils.cpp │ ├── mpformatutils_p.h │ ├── mphash.cpp │ ├── mphash_p.h │ ├── mpir.cpp │ ├── mpir_p.h │ ├── mpirpass.cpp │ ├── mpirpass_p.h │ ├── mpirtox86.cpp │ ├── mpirtox86_p.h │ ├── mplang.cpp │ ├── mplang_p.h │ ├── mpmath.cpp │ ├── mpmath_p.h │ ├── mpparser.cpp │ ├── mpparser_p.h │ ├── mpsl.cpp │ ├── mpsl.h │ ├── mpsl_apibegin.h │ ├── mpsl_apiend.h │ ├── mpsl_build.h │ ├── mpsl_p.h │ ├── mpstrtod_p.h │ ├── mptokenizer.cpp │ └── mptokenizer_p.h ├── test ├── mp_dsp.cpp ├── mp_test.cpp ├── mp_tutorial.cpp ├── mp_utils.h └── mpsl.h └── tools ├── configure-ninja.sh ├── configure-vs-x64.bat ├── configure-vs-x86.bat └── configure-xcode.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .kdev4 2 | *.kdev4 3 | build 4 | build_* 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | git: 4 | depth: false 5 | 6 | env: 7 | global: 8 | - BUILD_TOOLCHAIN="Unix Makefiles" 9 | 10 | dist: xenial 11 | 12 | matrix: 13 | include: 14 | - name: "Linux GCC 4.8 [32-bit] [DBG]" 15 | os: linux 16 | addons: 17 | apt: 18 | sources: [ubuntu-toolchain-r-test] 19 | packages: [g++-4.8, g++-4.8-multilib, "linux-libc-dev:i386"] 20 | env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-4.8 && CXX=g++-4.8" CXXFLAGS=-m32 LDFLAGS=-m32 21 | 22 | - name: "Linux GCC 4.8 [64-bit] [DBG]" 23 | os: linux 24 | addons: 25 | apt: 26 | sources: [ubuntu-toolchain-r-test] 27 | packages: [g++-4.8] 28 | env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-4.8 && CXX=g++-4.8" 29 | 30 | - name: "Linux GCC 4.9 [32-bit] [DBG]" 31 | os: linux 32 | addons: 33 | apt: 34 | sources: [ubuntu-toolchain-r-test] 35 | packages: [g++-4.9, g++-4.9-multilib, "linux-libc-dev:i386"] 36 | env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-4.9 && CXX=g++-4.9" CXXFLAGS=-m32 LDFLAGS=-m32 37 | 38 | - name: "Linux GCC 4.9 [64-bit] [DBG]" 39 | os: linux 40 | addons: 41 | apt: 42 | sources: [ubuntu-toolchain-r-test] 43 | packages: [g++-4.9] 44 | env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-4.9 && CXX=g++-4.9" 45 | 46 | - name: "Linux GCC 5.X [32-bit] [DBG]" 47 | os: linux 48 | addons: 49 | apt: 50 | sources: [ubuntu-toolchain-r-test] 51 | packages: [g++-5, g++-5-multilib, "linux-libc-dev:i386"] 52 | env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-5 && CXX=g++-5" CXXFLAGS=-m32 LDFLAGS=-m32 53 | 54 | - name: "Linux GCC 5.X [64-bit] [DBG]" 55 | os: linux 56 | addons: 57 | apt: 58 | sources: [ubuntu-toolchain-r-test] 59 | packages: [g++-5] 60 | env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-5 && CXX=g++-5" 61 | 62 | - name: "Linux GCC 6.X [32-bit] [DBG]" 63 | os: linux 64 | addons: 65 | apt: 66 | sources: [ubuntu-toolchain-r-test] 67 | packages: [g++-6, g++-6-multilib, "linux-libc-dev:i386"] 68 | env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-6 && CXX=g++-6" CXXFLAGS=-m32 LDFLAGS=-m32 69 | 70 | - name: "Linux GCC 6.X [64-bit] [DBG]" 71 | os: linux 72 | addons: 73 | apt: 74 | sources: [ubuntu-toolchain-r-test] 75 | packages: [g++-6] 76 | env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-6 && CXX=g++-6" 77 | 78 | - name: "Linux GCC 7.X [32-bit] [DBG]" 79 | os: linux 80 | addons: 81 | apt: 82 | sources: [ubuntu-toolchain-r-test] 83 | packages: [g++-7, g++-7-multilib, "linux-libc-dev:i386"] 84 | env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-7 && CXX=g++-7" CXXFLAGS=-m32 LDFLAGS=-m32 85 | 86 | - name: "Linux GCC 7.X [64-bit] [DBG]" 87 | os: linux 88 | addons: 89 | apt: 90 | sources: [ubuntu-toolchain-r-test] 91 | packages: [g++-7] 92 | env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-7 && CXX=g++-7" 93 | 94 | - name: "Linux GCC 8.X [32-bit] [DBG]" 95 | os: linux 96 | addons: 97 | apt: 98 | sources: [ubuntu-toolchain-r-test] 99 | packages: [g++-8, g++-8-multilib, "linux-libc-dev:i386"] 100 | env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-8 && CXX=g++-8" CXXFLAGS=-m32 LDFLAGS=-m32 101 | 102 | - name: "Linux GCC 8.X [32-bit] [REL]" 103 | os: linux 104 | addons: 105 | apt: 106 | sources: [ubuntu-toolchain-r-test] 107 | packages: [g++-8, g++-8-multilib, "linux-libc-dev:i386"] 108 | env: BUILD_MATRIX="BUILD_TYPE=Release && CC=gcc-8 && CXX=g++-8" CXXFLAGS=-m32 LDFLAGS=-m32 109 | 110 | - name: "Linux GCC 8.X [64-bit] [DBG]" 111 | os: linux 112 | addons: 113 | apt: 114 | sources: [ubuntu-toolchain-r-test] 115 | packages: [g++-8] 116 | env: BUILD_MATRIX="BUILD_TYPE=Debug && CC=gcc-8 && CXX=g++-8" 117 | 118 | - name: "Linux GCC 8.X [64-bit] [REL]" 119 | os: linux 120 | addons: 121 | apt: 122 | sources: [ubuntu-toolchain-r-test] 123 | packages: [g++-8] 124 | env: BUILD_MATRIX="BUILD_TYPE=Release && CC=gcc-8 && CXX=g++-8" 125 | 126 | - name: "Linux GCC Default [64-bit] [DBG + Valgrind]" 127 | os: linux 128 | addons: 129 | apt: 130 | sources: [ubuntu-toolchain-r-test] 131 | packages: [valgrind] 132 | env: BUILD_MATRIX="BUILD_TYPE=Debug" USE_VALGRIND=1 133 | 134 | - name: "Linux GCC Default [64-bit] [REL + Valgrind]" 135 | os: linux 136 | addons: 137 | apt: 138 | sources: [ubuntu-toolchain-r-test] 139 | packages: [valgrind] 140 | env: BUILD_MATRIX="BUILD_TYPE=Release" USE_VALGRIND=1 141 | 142 | - name: "OSX Clang XCode 9.4 [32-bit] [DBG]" 143 | os: osx 144 | osx_image: xcode9.4 145 | env: BUILD_MATRIX="BUILD_TYPE=Debug" CXXFLAGS=-m32 && LDFLAGS=-m32 146 | 147 | - name: "OSX Clang XCode 9.4 [32-bit] [REL]" 148 | os: osx 149 | osx_image: xcode9.4 150 | env: BUILD_MATRIX="BUILD_TYPE=Release" CXXFLAGS=-m32 && LDFLAGS=-m32 151 | 152 | - name: "OSX Clang XCode 9.4 [64-bit] [DBG]" 153 | os: osx 154 | osx_image: xcode9.4 155 | env: BUILD_MATRIX="BUILD_TYPE=Debug" 156 | 157 | - name: "OSX Clang XCode 9.4 [64-bit] [REL]" 158 | os: osx 159 | osx_image: xcode9.4 160 | env: BUILD_MATRIX="BUILD_TYPE=Release" 161 | 162 | - name: "Windows VS2017 [32-bit] [DBG]" 163 | os: windows 164 | env: BUILD_MATRIX="BUILD_TYPE=Debug" BUILD_TOOLCHAIN="Visual Studio 15 2017" 165 | 166 | - name: "Windows VS2017 [32-bit] [REL]" 167 | os: windows 168 | env: BUILD_MATRIX="BUILD_TYPE=Release" BUILD_TOOLCHAIN="Visual Studio 15 2017" 169 | 170 | - name: "Windows VS2017 [64-bit] [DBG]" 171 | os: windows 172 | env: BUILD_MATRIX="BUILD_TYPE=Debug" BUILD_TOOLCHAIN="Visual Studio 15 2017 Win64" 173 | 174 | - name: "Windows VS2017 [64-bit] [REL]" 175 | os: windows 176 | env: BUILD_MATRIX="BUILD_TYPE=Release" BUILD_TOOLCHAIN="Visual Studio 15 2017 Win64" 177 | 178 | before_install: 179 | - eval "$BUILD_MATRIX" 180 | 181 | install: 182 | - | 183 | mkdir -p deps/asmjit 184 | git clone --depth=1 https://github.com/asmjit/asmjit.git deps/asmjit 185 | 186 | before_script: 187 | - mkdir build 188 | - cd build 189 | - | 190 | if [[ "$BUILD_TOOLCHAIN" =~ ^Visual\ Studio ]]; then 191 | cmake .. -G"${BUILD_TOOLCHAIN}" -DASMJIT_DIR="../deps/asmjit" -DMPSL_TEST=1 192 | else 193 | cmake .. -G"${BUILD_TOOLCHAIN}" -DASMJIT_DIR="../deps/asmjit" -DMPSL_TEST=1 -DCMAKE_PREFIX_PATH="$MINGW_PATH" -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" 194 | fi 195 | - cd .. 196 | 197 | script: 198 | - cd build 199 | - | 200 | if [[ "$BUILD_TOOLCHAIN" =~ ^Visual\ Studio ]]; then 201 | cmake --build . --config ${BUILD_TYPE} -- -nologo -v:quiet 202 | cd ${BUILD_TYPE} 203 | else 204 | cmake --build . 205 | fi 206 | 207 | - | 208 | if [ "$USE_VALGRIND" = "1" ]; then 209 | RUN_CMD="valgrind --leak-check=full --show-reachable=yes" 210 | fi 211 | 212 | - eval "$RUN_CMD ./mp_test --verbose" 213 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | 3 | # Don't create a project if it was already created by another CMakeLists.txt. 4 | # This allows one library to embed another library without a project collision. 5 | if(NOT CMAKE_PROJECT_NAME OR "${CMAKE_PROJECT_NAME}" STREQUAL "mpsl") 6 | project(mpsl CXX) 7 | endif() 8 | 9 | # ============================================================================= 10 | # [MPSL - Configuration] 11 | # ============================================================================= 12 | 13 | if (NOT DEFINED MPSL_DIR) 14 | set(MPSL_DIR "${CMAKE_CURRENT_LIST_DIR}") 15 | endif() 16 | 17 | if (NOT DEFINED ASMJIT_DIR) 18 | set(ASMJIT_DIR "${MPSL_DIR}/../asmjit") 19 | endif() 20 | 21 | set(MPSL_DIR "${MPSL_DIR}" CACHE PATH "Location of 'mpsl'") 22 | set(ASMJIT_DIR "${ASMJIT_DIR}" CACHE PATH "Location of 'asmjit'") 23 | 24 | set(MPSL_TEST FALSE CACHE BOOL "Build 'mpsl' test applications") 25 | set(MPSL_EMBED FALSE CACHE BOOL "Embed 'mpsl' library (no targets)") 26 | set(MPSL_STATIC ${MPSL_EMBED} CACHE BOOL "Build 'mpsl' library as static") 27 | 28 | # ============================================================================= 29 | # [MPSL - Project] 30 | # ============================================================================= 31 | 32 | set(MPSL_INCLUDE_DIRS ${MPSL_DIR}/src) 33 | set(MPSL_DEPS "") 34 | set(MPSL_LIBS "") 35 | set(MPSL_CFLAGS "") 36 | set(MPSL_PRIVATE_CFLAGS "") 37 | set(MPSL_PRIVATE_CFLAGS_DBG "") 38 | set(MPSL_PRIVATE_CFLAGS_REL "") 39 | 40 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" OR "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC") 41 | set(MPSL_PRIVATE_LFLAGS "/OPT:REF /OPT:ICF") 42 | 43 | list(APPEND MPSL_PRIVATE_CFLAGS -MP) # [+] Multi-process compilation 44 | list(APPEND MPSL_PRIVATE_CFLAGS -GF) # [+] Eliminate duplicate strings. 45 | list(APPEND MPSL_PRIVATE_CFLAGS -GR-) # [-] Runtime type information. 46 | list(APPEND MPSL_PRIVATE_CFLAGS_DBG -GS) # [+] Buffer security-check. 47 | list(APPEND MPSL_PRIVATE_CFLAGS_REL -GS-) # [-] Buffer security-check. 48 | list(APPEND MPSL_PRIVATE_CFLAGS_REL -O2) # [+] Favor speed over size. 49 | endif() 50 | 51 | if("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(GNU|Clang|AppleClang)$") 52 | list(APPEND MPSL_PRIVATE_CFLAGS -Wall -Wextra) 53 | endif() 54 | 55 | if(NOT DEFINED ASMJIT_EMBED) 56 | set(ASMJIT_EMBED TRUE) 57 | endif() 58 | include("${ASMJIT_DIR}/CMakeLists.txt") 59 | 60 | list(APPEND MPSL_DEPS ${ASMJIT_LIBS}) 61 | list(APPEND MPSL_PRIVATE_CFLAGS "${ASMJIT_CFLAGS}") 62 | 63 | list(APPEND MPSL_LIBS ${MPSL_DEPS}) 64 | if (NOT MPSL_EMBED) 65 | list(INSERT MPSL_LIBS 0 mpsl) 66 | endif() 67 | 68 | if (MPSL_EMBED) 69 | set(MPSL_TARGET_TYPE "EMBED") 70 | elseif (MPSL_STATIC) 71 | set(MPSL_TARGET_TYPE "STATIC") 72 | else() 73 | set(MPSL_TARGET_TYPE "SHARED") 74 | endif() 75 | 76 | if (MPSL_EMBED OR MPSL_STATIC) 77 | list(APPEND MPSL_CFLAGS -DMPSL_STATIC) 78 | endif() 79 | 80 | list(REMOVE_DUPLICATES MPSL_DEPS) 81 | list(REMOVE_DUPLICATES MPSL_PRIVATE_CFLAGS) 82 | 83 | # ============================================================================= 84 | # [MPSL - Source] 85 | # ============================================================================= 86 | 87 | set(MPSL_SRC_LIST 88 | mpsl/mpsl_apibegin.h 89 | mpsl/mpsl_apiend.h 90 | mpsl/mpsl_build.h 91 | 92 | mpsl/mpsl.cpp 93 | mpsl/mpsl.h 94 | mpsl/mpsl_p.h 95 | 96 | mpsl/mpast.cpp 97 | mpsl/mpast_p.h 98 | mpsl/mpastoptimizer.cpp 99 | mpsl/mpastoptimizer_p.h 100 | mpsl/mpatomic_p.h 101 | mpsl/mpcodegen.cpp 102 | mpsl/mpcodegen_p.h 103 | mpsl/mpfold.cpp 104 | mpsl/mpfold_p.h 105 | mpsl/mpformatutils.cpp 106 | mpsl/mpformatutils_p.h 107 | mpsl/mphash.cpp 108 | mpsl/mphash_p.h 109 | mpsl/mpir.cpp 110 | mpsl/mpir_p.h 111 | mpsl/mpirpass.cpp 112 | mpsl/mpirpass_p.h 113 | mpsl/mpirtox86.cpp 114 | mpsl/mpirtox86_p.h 115 | mpsl/mplang.cpp 116 | mpsl/mplang_p.h 117 | mpsl/mpmath.cpp 118 | mpsl/mpmath_p.h 119 | mpsl/mpparser.cpp 120 | mpsl/mpparser_p.h 121 | mpsl/mpstrtod_p.h 122 | mpsl/mptokenizer.cpp 123 | mpsl/mptokenizer_p.h 124 | ) 125 | 126 | set(MPSL_SRC) 127 | foreach(_src_file ${MPSL_SRC_LIST}) 128 | list(APPEND MPSL_SRC src/${_src_file}) 129 | endforeach() 130 | source_group(TREE "${MPSL_DIR}" FILES ${MPSL_SRC}) 131 | 132 | # ============================================================================= 133 | # [MPSL - Targets] 134 | # ============================================================================= 135 | 136 | if (NOT MPSL_EMBED) 137 | add_library(mpsl ${MPSL_TARGET_TYPE} ${MPSL_SRC} ${ASMJIT_SRC}) 138 | target_link_libraries(mpsl PRIVATE ${MPSL_DEPS}) 139 | target_compile_options(mpsl PUBLIC ${MPSL_CFLAGS}) 140 | target_compile_options(mpsl PRIVATE ${MPSL_PRIVATE_CFLAGS} 141 | $<$:${MPSL_PRIVATE_CFLAGS_DBG}> 142 | $<$>:${MPSL_PRIVATE_CFLAGS_REL}>) 143 | target_compile_features(mpsl PUBLIC cxx_std_11) 144 | set_property(TARGET mpsl PROPERTY CXX_VISIBILITY_PRESET hidden) 145 | 146 | target_include_directories(mpsl PRIVATE BEFORE ${ASMJIT_INCLUDE_DIRS}) 147 | target_include_directories(mpsl PUBLIC BEFORE ${MPSL_INCLUDE_DIRS}) 148 | 149 | foreach(_src_file ${MPSL_SRC_LIST}) 150 | if ("${_src_file}" MATCHES "\\.h$" AND NOT "${_src_file}" MATCHES "_p\\.h$") 151 | get_filename_component(_src_dir ${_src_file} PATH) 152 | install(FILES "${MPSL_DIR}/src/${_src_file}" DESTINATION "include/${_src_dir}") 153 | endif() 154 | endforeach() 155 | endif() 156 | 157 | if (MPSL_TEST) 158 | foreach(_target mp_dsp mp_test mp_tutorial) 159 | add_executable(${_target} test/${_target}.cpp) 160 | target_link_libraries(${_target} ${MPSL_LIBS}) 161 | target_compile_options(${_target} PRIVATE ${MPSL_PRIVATE_CFLAGS} 162 | $<$:${MPSL_PRIVATE_CFLAGS_DBG}> 163 | $<$>:${MPSL_PRIVATE_CFLAGS_REL}>) 164 | target_compile_features(${_target} PUBLIC cxx_std_11) 165 | set_property(TARGET ${_target} PROPERTY CXX_VISIBILITY_PRESET hidden) 166 | endforeach() 167 | endif() 168 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MPSL - MathPresso's Shading Language with JIT Engine for C++ 2 | Copyright (c) 2016, Petr Kobalicek 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 3. This notice may not be removed or altered from any source distribution. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MPSL 2 | ==== 3 | 4 | MathPresso's Shading Language with JIT Engine for C++. 5 | 6 | * [Official Repository (kobalicek/mpsl)](https://github.com/kobalicek/mpsl) 7 | * [Official Blog (asmbits)](https://asmbits.blogspot.com/ncr) 8 | * [Official Chat (gitter)](https://gitter.im/kobalicek/mpsl) 9 | * [Permissive ZLIB license](./LICENSE.md) 10 | 11 | 12 | Disclaimer 13 | ---------- 14 | 15 | This is a WORK-IN-PROGRESS that is far from being complete. MPSL is a challenging and difficult project and I work on it mostly when I'm tired of other projects. Contact me if you found MPSL interesting and want to collaborate on its development - people joining this project are very welcome. 16 | 17 | 18 | Project Status 19 | -------------- 20 | 21 | What is implemented: 22 | * [x] MPSL APIs (public) for embedding 23 | * [x] AST definition and source code to AST conversion (parser) 24 | * [x] AST-based semantic code analysis (happens after parsing) 25 | * [x] AST-based optimizations (constant folding and dead code elimination) 26 | * [x] IR concept and initial support for AST to IR mapping 27 | 28 | What is a work-in-progress: 29 | * [ ] AST-To-IR translation is only basic for now (doesn't implement control-flow and many operators) 30 | * [ ] IR-based optimizations are not implemented yet 31 | * [ ] IR-To-ASM translation is very basic and buggy 32 | * [ ] IR is not in SSA form yet, this is to-be-researched subject atm 33 | 34 | 35 | Introduction 36 | ------------ 37 | 38 | MPSL is a lightweight shader-like programming language written in C++. Its name is based on a sister project called [MathPresso](https://github.com/kobalicek/mathpresso), which provided the basic idea and some building blocks. MPSL has been designed to be a safe programming language that can access CPU's SIMD capabilities through a shader-like programs compiled at runtime. The language is statically typed and allows to use up to 256-bit wide variables that map directly to CPU's SIMD registers (SSE, AVX, NEON, ...). 39 | 40 | MPSL has been designed to be lightweight and embeddable - it doesn't depend on huge libraries like LLVM, it only uses a very lightweight library called [AsmJit](https://github.com/kobalicek/asmjit) as a JIT backend. It implements its own abstract syntax tree (AST) and intermediate representation (IR) of the input program, and then uses an IRToAsm translator to convert IR to machine code. 41 | 42 | Check out a working [mp_tutorial.cpp](./test/mp_tutorial.cpp) to see how MPSL APIs are designed and how MPSL engine is embedded and used within an application. 43 | 44 | 45 | MPSL Types 46 | ---------- 47 | 48 | MPSL is a statically typed language where variables have to be declared before they are used like in any other C-like language. Variables can be declared anywhere and can shadow other variables and/or function arguments. 49 | 50 | Available types: 51 | 52 | * `int` - 32-bit signed integer 53 | * `float` - 32-bit (single-precision) floating point 54 | * `double` - 64-bit (double-precision) floating point 55 | * `bool` - 32-bit boolean type (implicitly castable to any type) 56 | * `__qbool` - 64-bit boolean type (implicitly castable to any type) 57 | * All types can form a vector up to 4 elements, like `int2`, `float3`, and `double4` 58 | * 32-bit types can additionally form a vector of 8 elements, like `bool8`, `int8`, and `float8` 59 | 60 | Constants are parsed in the following way: 61 | 62 | * If the number doesn't have any of fraction and exponent parts and it's in range [-2147483648, 2147483647] it's parsed as a 32-bit integer, otherwise it's parsed as `double` 63 | * If the number contains "f" suffix it's parsed as `float` 64 | * If the number contains "d" suffix it's parsed as `double` 65 | * TODO: MSPL should allow to customize which floating type is preferred (if `float` or `double`). 66 | 67 | 68 | MPSL Features 69 | ------------- 70 | 71 | * Variables have to be declared before they are used: 72 | * `[const] type var [= value];` 73 | * Variables declared as `const` have to be assigned immediately and cannot be changed 74 | * Expressions like `int x = x;` are not allowed (unlike C), unless `x` shadows another `x` from outer scope 75 | * TODO: Reading a variable that was not assigned yet is undefined, should be defined 76 | 77 | * Typedef (aka type alias) 78 | * `typedef type newtype` 79 | 80 | * If-Else: 81 | * `if (cond) { taken-body; } [else { not-taken-body; }]` 82 | 83 | * Implicit and explicit casts: 84 | * `(type)expression` is an explicit cast 85 | * Integer and float can implicitly cast to double 86 | * Boolean types can implicitly or explicitly cast to any 32-bit or 64-bit type, converting `true` value to `1` and `false` value to `0` 87 | * Scalar is implicitly promoted to vector if used with a vector type 88 | 89 | * Loops: 90 | * `for (init; cond; iter) { body; }` 91 | * `do { body } while(cond);` 92 | * `while(cond) { body; }` 93 | 94 | * Functions: 95 | * `ret-type func([arg-type arg-name [, ...]]) { body; }` 96 | * Functions can call other functions, but they can't recurse 97 | * Each MPSL program contains a `main() { ... }` entry-point 98 | 99 | * Arithmetic operators: 100 | * `-(x)` - negate 101 | * `x + y` - add 102 | * `x - y` - subtract 103 | * `x * y` - multiply 104 | * `x / y` - divide 105 | * `x % y` - modulo 106 | 107 | * Bitwise and shift operators and intrinsics: 108 | * `~(x)` - bitwise NOT 109 | * `x & y` - bitwise AND 110 | * `x | y` - bitwise OR 111 | * `x ^ y` - bitwise XOR 112 | * `x >> y` - shift arithmetic right 113 | * `x >>> y` - shift logical right 114 | * `x << y` - shift logical left 115 | * `ror(x, y)` - rotate right 116 | * `rol(x, y)` - rotate left 117 | * `lzcnt(x)` - count leading zeros 118 | * `popcnt(x)` - population count (count of bits set to `1`) 119 | 120 | * Logical operators: 121 | * `!(x)` - logical NOT 122 | * `x && y` - logical AND 123 | * `x || y` - logical OR 124 | 125 | * Comparison operators: 126 | * `x == y` - check if equal 127 | * `x != y` - check if not equal 128 | * `x > y` - check if greater than 129 | * `x >= y` - check if greater than or equal 130 | * `x < y` - check if lesser than 131 | * `x <= y` - check if lesser than or equal 132 | 133 | * Assignment operators: 134 | * `++x` - pre-increment 135 | * `--x` - pre-decrement 136 | * `x++` - post-increment 137 | * `x--` - post-decrement 138 | * `x = y` - assignment 139 | * `x += y` - add with assignment 140 | * `x -= y` - subtract with assignment 141 | * `x *= y` - multiply with assignment 142 | * `x /= y` - divide with assignment 143 | * `x %= y` - modulo with assignment 144 | * `x &= y` - bitwise AND with assignment 145 | * `x |= y` - bitwise OR with assignment 146 | * `x ^= y` - bitwise XOR with assignment 147 | * `x >>= y` - shift arithmetic right with assignment 148 | * `x >>>= y` - shift logical right with assignment 149 | * `x <<= y` - shift logical left with assignment 150 | 151 | * Built-in intrinsics for special number handling: 152 | * `isnan(x)` - check for NaN 153 | * `isinf(x)` - check for infinity 154 | * `isfinite(x)` - check for finite number 155 | * `signbit(x)` - get a sign bit 156 | * `copysign(x, y)` - copy sign 157 | 158 | * Built-in intrinsics for floating-point rounding: 159 | * `round(x)` - round to nearest integer 160 | * `roundeven(x)` - round to even integer 161 | * `trunc(x)` - round towards zero (truncate) 162 | * `floor(x)` - round down (floor) 163 | * `ceil(x)` - round up (ceil) 164 | 165 | * Built-in intrinsics that map easily to CPU instructions: 166 | * `abs(x)` - absolute value 167 | * `frac(x)` - extract fraction 168 | * `sqrt(x)` - square root 169 | * `min(x, y)` - minimum value 170 | * `max(x, y)` - maximum value 171 | 172 | * Other built-in intrinsics: 173 | * `exp(x)` - exponential 174 | * `log(x)` - logarithm of base E 175 | * `log2(x)` - logarithm of base 2 176 | * `log10(x)` - logarithm of base 10 177 | * `pow(x, y)` - power 178 | * `sin(x)` - sine 179 | * `cos(x)` - cosine 180 | * `tan(x)` - tangent 181 | * `asin(x)` - arcsine 182 | * `acos(x)` - arccosine 183 | * `atan(x)` and `atan2(x, y)` - arctangent 184 | 185 | * Built-in DSP intrinsics (`int` and `int2..8` only): 186 | * `vabsb(x)` - absolute value of packed bytes 187 | * `vabsw(x)` - absolute value of packed words 188 | * `vabsd(x)` - absolute value of packed dwords 189 | * `vaddb(x, y)` - add packed bytes 190 | * `vaddw(x, y)` - add packed words 191 | * `vaddd(x, y)` - add packed dwords 192 | * `vaddq(x, y)` - add packed qwords 193 | * `vaddssb(x, y)` - add packed bytes with signed saturation 194 | * `vaddusb(x, y)` - add packed bytes with unsigned saturation 195 | * `vaddssw(x, y)` - add packed words with signed saturation 196 | * `vaddusw(x, y)` - add packed words with unsigned saturation 197 | * `vsubb(x, y)` - subtract packed bytes 198 | * `vsubw(x, y)` - subtract packed words 199 | * `vsubd(x, y)` - subtract packed dwords 200 | * `vsubq(x, y)` - subtract packed qwords 201 | * `vsubssb(x, y)` - subtract packed bytes with signed saturation 202 | * `vsubusb(x, y)` - subtract packed bytes with unsigned saturation 203 | * `vsubssw(x, y)` - Subtract packed words with signed saturation 204 | * `vsubusw(x, y)` - subtract packed words with unsigned saturation 205 | * `vmulw(x, y)` - multiply packed words (signed or unsigned) 206 | * `vmulhsw(x, y)` - multiply packed words and store high word of a signed result 207 | * `vmulhuw(x, y)` - multiply packed words and store high word of an unsigned result 208 | * `vmuld(x, y)` - multiply packed words (signed or unsigned) 209 | * `vminsb(x, y)` - minimum of packed bytes (signed) 210 | * `vminub(x, y)` - minimum of packed bytes (unsigned) 211 | * `vminsw(x, y)` - minimum of packed words (signed) 212 | * `vminuw(x, y)` - minimum of packed words (unsigned) 213 | * `vminsd(x, y)` - minimum of packed dwords (signed) 214 | * `vminud(x, y)` - minimum of packed dwords (unsigned) 215 | * `vmaxsb(x, y)` - maximum of packed bytes (signed) 216 | * `vmaxub(x, y)` - maximum of packed bytes (unsigned) 217 | * `vmaxsw(x, y)` - maximum of packed words (signed) 218 | * `vmaxuw(x, y)` - maximum of packed words (unsigned) 219 | * `vmaxsd(x, y)` - maximum of packed dwords (signed) 220 | * `vmaxud(x, y)` - maximum of packed dwords (unsigned) 221 | * `vsllw(x, y)` - shift left logical of packed words by scalar `y` 222 | * `vsrlw(x, y)` - shift right logical of packed words by scalar `y` 223 | * `vsraw(x, y)` - shift right arithmetic of packed words by scalar `y` 224 | * `vslld(x, y)` - shift left logical of packed dwords by scalar `y` 225 | * `vsrld(x, y)` - shift right logical of packed dwords by scalar `y` 226 | * `vsrad(x, y)` - shift right arithmetic of packed dwords by scalar `y` 227 | * `vsllq(x, y)` - shift left logical of packed qwords by scalar `y` 228 | * `vsrlq(x, y)` - shift right logical of packed qwords by scalar `y` 229 | * `vcmpeqb(x, y)` - compare packed bytes (signed) if equal 230 | * `vcmpeqw(x, y)` - compare packed words (signed) if equal 231 | * `vcmpeqd(x, y)` - compare packed dwords (signed) if equal 232 | * `vcmpgtb(x, y)` - compare packed bytes (signed) if greater than 233 | * `vcmpgtw(x, y)` - compare packed words (signed) if greater than 234 | * `vcmpgtd(x, y)` - compare packed dwords (signed) if greater than 235 | 236 | * Built-in special constants: 237 | * `INF` - infinity 238 | * `NaN` - not a number 239 | 240 | * Built-in math constants from C's math.h: 241 | * `M_E = 2.71828182845904523536 ` - Euler's number 242 | * `M_LOG2E = 1.44269504088896340736 ` - log2(e) 243 | * `M_LOG10E = 0.434294481903251827651` - log10(e) 244 | * `M_LN2 = 0.693147180559945309417` - ln(2) 245 | * `M_LN10 = 2.30258509299404568402 ` - ln(10) 246 | * `M_PI = 3.14159265358979323846 ` - PI 247 | * `M_PI_2 = 1.57079632679489661923 ` - PI/2 248 | * `M_PI_4 = 0.785398163397448309616` - PI/4 249 | * `M_1_PI = 0.318309886183790671538` - 1/PI 250 | * `M_2_PI = 0.636619772367581343076` - 2/PI 251 | * `M_2_SQRTPI = 1.1283791670955125739 ` - 2/sqrt(pi) 252 | * `M_SQRT2 = 1.4142135623730950488 ` - sqrt(2) 253 | * `M_SQRT1_2 = 0.707106781186547524401` - 1/sqrt(2) 254 | 255 | 256 | MPSL Program 257 | ------------ 258 | 259 | A typical MPSL program has an entry-point called `main()` and uses input and output variables provided by the embedder. If `a` and `b` variables of type `float4` are provided then we can write a simple shader that returns their sum: 260 | 261 | ```c++ 262 | // Shader's entry-point - can have return value (if embedded defines it), but has no arguments. 263 | float4 main() { 264 | // In case the embedder provides two arguments `a` and `b` of `float4` type. 265 | return a + b; 266 | } 267 | ``` 268 | 269 | Where `a`, `b`, and a hidden return variable are provided by the embedder (including their types). This means that the same MPSL program is able to use different data layouts and different number of data arguments passed to the compiled shader. 270 | 271 | 272 | MPSL C++ API 273 | ------------ 274 | 275 | MPSL is written in C++ and provides C++ APIs for embedders. Here is a summary of MPSL's design choices: 276 | 277 | * MPSL is written in C++ and exposes a simple C++ interface for embedders. It's possible to wrap it in a pure C interface, but it's not planned to be part of a MPSL project at the moment. 278 | * MPSL uses error codes, not exceptions, and guarantees that every failure is propagated to the embedder as an error code. MPSL never aborts on out-of-memory condition, never throws, and clean ups all resources in case of error properly. 279 | * MPSL has its own pooled memory allocator, which uses the OS allocator to allocate larger blocks of memory, which are then split into smaller chunks and pools. It's very fast and prevents memory fragmentation. 280 | * MPSL allows embedder to specify a data-layout of his own structures, which means that embedders generally don't have to change their data structures to use MPSL. 281 | 282 | To use MPSL from your C++ code you must first include `mpsl/mpsl.h` to make all public APIs available within `mpsl` namespace. The following concepts are provided: 283 | 284 | * `mpsl::Context` - This is an expression's environment. A program cannot be created without having a `Context` associated. `Context` also manages virtual memory that is used by shaders. 285 | * `mpsl::Program[1-4]<>` - Program represents a compiled MPSL shader. The `[1-4]` is a number of data arguments passed to the shader. Here the data argument doesn't represent variables used in a shader, it represents number of "pointers" passed to the shader, where each pointer can contain variables the shader has access to. 286 | * `mpsl::Layout` - A layout of data arguments passed to the shader. Each data argument (or sometimes called slot) has its own `Layout` definition. 287 | * `mpsl::LayoutTmp` - A layout that uses `N` bytes of stack before it allocates dynamic memory. Since `Layout` instances are short-lived it's beneficial to allocate them on stack. 288 | * `mpsl::OutputLog` - An interface that can be used to catch messages produced by MPSL parser, analyzer, optimizer, and JIT compiler. 289 | * `mpsl::StringRef` - A string reference, which may be used to specify string and its length. You can pass non-NULL terminated strings to all MPSL APIs. 290 | 291 | * `mpsl::Int[2-4]` - `int` vectors, which map to MPSL's `int[2-4]` types. 292 | * `mpsl::Float[2-4]` - `float` vectors, which map to MPSL's `float[2-4]` types. 293 | * `mpsl::Double[2-4]` - `double` vectors, which map to MPSL's `double[2-4]` types. 294 | 295 | * `mpsl::Error` - An error type (typedef to `uint32_t`) that is returned from MPSL APIs to report the result status. 296 | 297 | Check out [mpsl.h](./src/mpsl/mpsl.h) for more details about class member functions and public enumerations that can be used by embedders. 298 | 299 | Below is a minimal code that creates a simple data layout and compiles a very simple shader: 300 | 301 | ```c++ 302 | #include 303 | 304 | // Data structure that will be passed to the program. 305 | struct Data { 306 | double a, b; 307 | float c; 308 | double result; 309 | }; 310 | 311 | int main(int argc, char* argv[]) { 312 | // Create the shader environment. 313 | mpsl::Context context = mpsl::Context::create(); 314 | 315 | // Create the `Data` layout and register all variables that the shader has 316 | // access to. Special variables like `@ret` have always `@` prefix to prevent 317 | // a collision with valid identifiers. 318 | mpsl::LayoutTmp<> layout; 319 | layout.addMember("a" , mpsl::kTypeDouble | mpsl::kTypeRO, MPSL_OFFSET_OF(Data, a)); 320 | layout.addMember("b" , mpsl::kTypeDouble | mpsl::kTypeRO, MPSL_OFFSET_OF(Data, b)); 321 | layout.addMember("c" , mpsl::kTypeFloat | mpsl::kTypeRO, MPSL_OFFSET_OF(Data, c)); 322 | layout.addMember("@ret", mpsl::kTypeDouble | mpsl::kTypeWO, MPSL_OFFSET_OF(Data, result)); 323 | 324 | // This is our shader-program that will be compiled by MPSL. 325 | const char body[] = "double main() { return sqrt(a * b) * c; }"; 326 | 327 | // Create the program object and try to compile it. The `Program1` means 328 | // that the program accepts one data argument of type `Data`. You can just use 329 | // `Program1<>` if you want the argument untyped (void). 330 | mpsl::Program1 program; 331 | mpsl::Error err = program.compile(context, body, mpsl::kNoOptions, layout); 332 | 333 | if (err) { 334 | printf("Compilation failed: ERROR 0x%08X\n", static_cast(err)); 335 | } 336 | else { 337 | Data data; 338 | data.a = 4.0; 339 | data.b = 16.0; 340 | data.c = 0.5f; 341 | 342 | err = program.run(&data); 343 | if (err) 344 | printf("Execution failed: ERROR 0x%08X\n", static_cast(err)); 345 | else 346 | printf("Return=%g\n", data.result); 347 | } 348 | 349 | // RAII - `Context` and `Program` are ref-counted and will 350 | // be automatically destroyed when they go out of scope. 351 | return 0; 352 | } 353 | ``` 354 | 355 | More documentation will come in the future. 356 | 357 | Dependencies 358 | ------------ 359 | 360 | * AsmJit - 1.0 or later. 361 | 362 | Support 363 | ------- 364 | 365 | Please consider a donation if you use the project and would like to keep it active in the future. 366 | 367 | * [Donate by PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=QDRM6SRNG7378&lc=EN;&item_name=mpsl¤cy_code=EUR) 368 | 369 | Authors & Maintainers 370 | --------------------- 371 | 372 | * Petr Kobalicek 373 | -------------------------------------------------------------------------------- /src/mpsl/mpastoptimizer.cpp: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Export] 8 | #define MPSL_EXPORTS 9 | 10 | // [Dependencies - MPSL] 11 | #include "./mpastoptimizer_p.h" 12 | #include "./mpfold_p.h" 13 | #include "./mpmath_p.h" 14 | 15 | // [Api-Begin] 16 | #include "./mpsl_apibegin.h" 17 | 18 | namespace mpsl { 19 | 20 | // ============================================================================ 21 | // [mpsl::AstOptimizer - Construction / Destruction] 22 | // ============================================================================ 23 | 24 | AstOptimizer::AstOptimizer(AstBuilder* ast, ErrorReporter* errorReporter) noexcept 25 | : AstVisitor(ast), 26 | _errorReporter(errorReporter), 27 | _currentRet(nullptr), 28 | _unreachable(false), 29 | _isConditional(false), 30 | _isLocalScope(false) {} 31 | AstOptimizer::~AstOptimizer() noexcept {} 32 | 33 | // ============================================================================ 34 | // [mpsl::AstOptimizer - Utilities] 35 | // ============================================================================ 36 | 37 | static MPSL_INLINE bool mpGetBooleanValue(const Value* src, uint32_t typeId) noexcept { 38 | return mpTypeInfo[typeId].size() == 4 ? src->u[0] != 0 : src->q[0] != 0; 39 | } 40 | 41 | static bool mpValueAsScalarDouble(double* dst, const Value* src, uint32_t typeInfo) noexcept { 42 | uint32_t typeId = typeInfo & kTypeIdMask; 43 | uint32_t count = TypeInfo::elementsOf(typeInfo); 44 | 45 | // Check whether all vectors are equivalent before converting to scalar. 46 | switch (mpTypeInfo[typeId].size()) { 47 | case 4: { 48 | uint32_t v = src->u[0]; 49 | for (uint32_t i = 1; i < count; i++) 50 | if (src->u[i] != v) 51 | return false; 52 | break; 53 | } 54 | 55 | case 8: { 56 | uint64_t v = src->q[0]; 57 | for (uint32_t i = 1; i < count; i++) 58 | if (src->q[i] != v) 59 | return false; 60 | break; 61 | } 62 | } 63 | 64 | double v; 65 | switch (typeId) { 66 | case kTypeBool : v = src->u[0] ? 1.0 : 0.0; break; 67 | case kTypeQBool : v = src->q[0] ? 1.0 : 0.0; break; 68 | case kTypeInt : v = static_cast(src->i[0]); break; 69 | case kTypeFloat : v = static_cast(src->f[0]); break; 70 | case kTypeDouble : v = src->d[0]; break; 71 | 72 | default: 73 | return false; 74 | } 75 | 76 | *dst = v; 77 | return true; 78 | } 79 | 80 | // ============================================================================ 81 | // [mpsl::AstOptimizer - OnNode] 82 | // ============================================================================ 83 | 84 | Error AstOptimizer::onProgram(AstProgram* node) noexcept { 85 | return onBlock(node); 86 | } 87 | 88 | Error AstOptimizer::onFunction(AstFunction* node) noexcept { 89 | AstSymbol* ret = node->ret(); 90 | 91 | _currentRet = ret; 92 | _isLocalScope = true; 93 | 94 | Error err = node->body() ? onNode(node->body()) : static_cast(kErrorOk); 95 | 96 | _currentRet = nullptr; 97 | _unreachable = false; 98 | 99 | _isConditional = false; 100 | _isLocalScope = false; 101 | 102 | return err; 103 | } 104 | 105 | Error AstOptimizer::onBlock(AstBlock* node) noexcept { 106 | // Prevent removing nodes that are not stored in pure `AstBlock`. For example 107 | // function call inherits from `AstBlock`, but it needs each expression passed. 108 | bool alterable = node->nodeType() == AstNode::kTypeBlock; 109 | 110 | uint32_t i = 0; 111 | uint32_t curCount = node->size(); 112 | uint32_t oldCount; 113 | 114 | while (i < curCount) { 115 | if (isUnreachable() && alterable) { 116 | _DeleteNode: 117 | _ast->deleteNode(node->removeAt(i)); 118 | curCount--; 119 | continue; 120 | } 121 | 122 | bool wasUnreachable = isUnreachable(); 123 | MPSL_PROPAGATE(onNode(node->childAt(i))); 124 | 125 | oldCount = curCount; 126 | curCount = node->size(); 127 | 128 | if (curCount < oldCount) { 129 | if (!alterable) 130 | return MPSL_TRACE_ERROR(kErrorInvalidState); 131 | continue; 132 | } 133 | 134 | if (alterable && (wasUnreachable || node->childAt(i)->isImm())) 135 | goto _DeleteNode; 136 | 137 | i++; 138 | } 139 | 140 | return kErrorOk; 141 | } 142 | 143 | Error AstOptimizer::onBranch(AstBranch* node) noexcept { 144 | if (node->condition()) { 145 | MPSL_PROPAGATE(onNode(node->condition())); 146 | AstNode* condition = node->condition(); 147 | 148 | if (condition->isImm()) { 149 | uint32_t typeId = condition->typeInfo() & kTypeIdMask; 150 | 151 | AstImm* imm = condition->as(); 152 | AstNode* alwaysTaken = nullptr; 153 | AstNode* neverTaken = nullptr; 154 | 155 | // Simplify if (condition) {a} else {b} to either {a} or {b}. 156 | if (mpGetBooleanValue(&imm->_value, typeId)) { 157 | if (node->thenBody()) 158 | alwaysTaken = node->unlinkThenBody(); 159 | if (node->elseBody()) 160 | neverTaken = node->unlinkElseBody(); 161 | } 162 | else { 163 | if (node->thenBody()) 164 | neverTaken = node->unlinkThenBody(); 165 | if (node->elseBody()) 166 | alwaysTaken = node->unlinkElseBody(); 167 | } 168 | 169 | if (neverTaken) 170 | _ast->deleteNode(neverTaken); 171 | 172 | _ast->deleteNode( 173 | node->parent()->replaceNode( 174 | node, alwaysTaken)); 175 | 176 | if (alwaysTaken) { 177 | bool prevUnreachable = _unreachable; 178 | MPSL_PROPAGATE(onNode(alwaysTaken)); 179 | _unreachable = prevUnreachable; 180 | } 181 | 182 | return kErrorOk; 183 | } 184 | } 185 | 186 | if (node->thenBody()) { 187 | bool prevUnreachable = _unreachable; 188 | MPSL_PROPAGATE(onNode(node->thenBody())); 189 | _unreachable = prevUnreachable; 190 | } 191 | 192 | if (node->elseBody()) { 193 | bool prevUnreachable = _unreachable; 194 | MPSL_PROPAGATE(onNode(node->elseBody())); 195 | _unreachable = prevUnreachable; 196 | } 197 | 198 | return kErrorOk; 199 | } 200 | 201 | Error AstOptimizer::onLoop(AstLoop* node) noexcept { 202 | if (node->forInit()) 203 | MPSL_PROPAGATE(onNode(node->forInit())); 204 | 205 | if (node->forIter()) 206 | MPSL_PROPAGATE(onNode(node->forIter())); 207 | 208 | if (node->condition()) 209 | MPSL_PROPAGATE(onNode(node->condition())); 210 | 211 | if (node->body()) { 212 | bool prevUnreachable = _unreachable; 213 | MPSL_PROPAGATE(onNode(node->body())); 214 | _unreachable = prevUnreachable; 215 | } 216 | 217 | return kErrorOk; 218 | } 219 | 220 | Error AstOptimizer::onBreak(AstBreak* node) noexcept { 221 | _unreachable = true; 222 | return kErrorOk; 223 | } 224 | 225 | Error AstOptimizer::onContinue(AstContinue* node) noexcept { 226 | _unreachable = true; 227 | return kErrorOk; 228 | } 229 | 230 | Error AstOptimizer::onReturn(AstReturn* node) noexcept { 231 | if (node->child()) 232 | MPSL_PROPAGATE(onNode(node->child())); 233 | 234 | _unreachable = true; 235 | return kErrorOk; 236 | } 237 | 238 | Error AstOptimizer::onVarDecl(AstVarDecl* node) noexcept { 239 | AstSymbol* sym = node->symbol(); 240 | 241 | if (node->child()) { 242 | MPSL_PROPAGATE(onNode(node->child())); 243 | AstNode* child = node->child(); 244 | 245 | if (child->isImm()) 246 | sym->setValue(static_cast(child)->value()); 247 | } 248 | 249 | return kErrorOk; 250 | } 251 | 252 | Error AstOptimizer::onVarMemb(AstVarMemb* node) noexcept { 253 | if (!node->child()) 254 | return MPSL_TRACE_ERROR(kErrorInvalidState); 255 | 256 | MPSL_PROPAGATE(onNode(node->child())); 257 | AstNode* child = node->child(); 258 | 259 | uint32_t typeInfo = child->typeInfo(); 260 | uint32_t typeId = typeInfo & kTypeIdMask; 261 | 262 | if (TypeInfo::isPtrId(typeId)) { 263 | if (child->nodeType() != AstNode::kTypeVar) 264 | return MPSL_TRACE_ERROR(kErrorInvalidState); 265 | 266 | // The field accesses an object's member. 267 | const AstSymbol* symbol = static_cast(child)->symbol(); 268 | const Layout* layout = symbol->layout(); 269 | 270 | if (layout == nullptr) 271 | return MPSL_TRACE_ERROR(kErrorInvalidState); 272 | 273 | const Layout::Member* m = layout->member(node->field()); 274 | if (m == nullptr) 275 | return _errorReporter->onError(kErrorInvalidProgram, node->position(), 276 | "Object '%s' doesn't have member '%s'", symbol->name(), node->field().data()); 277 | 278 | node->setTypeInfo(m->typeInfo | kTypeRef | (typeInfo & kTypeRW)); 279 | node->setOffset(m->offset); 280 | } 281 | else { 282 | // The field should access/shuffle a vector. 283 | if ((typeInfo & kTypeVecMask) == 0) 284 | return _errorReporter->onError(kErrorInvalidProgram, node->position(), 285 | "Type '%{Type}' doesn't have member '%s'", typeInfo, node->field().data()); 286 | 287 | // TODO: Field 288 | } 289 | 290 | return kErrorOk; 291 | } 292 | 293 | Error AstOptimizer::onVar(AstVar* node) noexcept { 294 | AstSymbol* sym = node->symbol(); 295 | uint32_t typeInfo = node->typeInfo(); 296 | 297 | if (!isUnreachable() && 298 | !isConditional() && 299 | sym->isAssigned() && 300 | !node->hasNodeFlag(AstNode::kFlagSideEffect)) { 301 | typeInfo |= kTypeRead; 302 | typeInfo &= ~(kTypeWrite | kTypeRef); 303 | 304 | AstImm* imm = _ast->newNode(sym->value(), typeInfo); 305 | _ast->deleteNode(node->parent()->replaceNode(node, imm)); 306 | } 307 | else { 308 | typeInfo |= kTypeRef; 309 | node->setTypeInfo(typeInfo); 310 | } 311 | 312 | return kErrorOk; 313 | } 314 | 315 | Error AstOptimizer::onImm(AstImm* node) noexcept { 316 | return kErrorOk; 317 | } 318 | 319 | Error AstOptimizer::onUnaryOp(AstUnaryOp* node) noexcept { 320 | const OpInfo& op = OpInfo::get(node->opType()); 321 | 322 | MPSL_PROPAGATE(onNode(node->child())); 323 | AstNode* child = node->child(); 324 | 325 | if (!isUnreachable()) { 326 | if (child->isImm()) { 327 | AstImm* child = static_cast(node->child()); 328 | Value& value = child->_value; 329 | 330 | uint32_t dTypeInfo = node->typeInfo(); 331 | uint32_t sTypeInfo = child->typeInfo(); 332 | 333 | if (op.isCast()) { 334 | MPSL_PROPAGATE( 335 | Fold::foldCast(value, dTypeInfo, value, sTypeInfo)); 336 | 337 | child->setTypeInfo(dTypeInfo); 338 | node->unlinkChild(); 339 | node->parent()->replaceNode(node, child); 340 | _ast->deleteNode(node); 341 | } 342 | else if (op.isSwizzle()) { 343 | MPSL_PROPAGATE( 344 | Fold::foldSwizzle(node->swizzleArray(), value, value, dTypeInfo)); 345 | 346 | child->setTypeInfo(dTypeInfo); 347 | node->unlinkChild(); 348 | node->parent()->replaceNode(node, child); 349 | _ast->deleteNode(node); 350 | } 351 | else { 352 | MPSL_PROPAGATE( 353 | Fold::foldUnaryOp(op.type(), value, value, sTypeInfo)); 354 | 355 | child->setTypeInfo(dTypeInfo); 356 | node->unlinkChild(); 357 | node->parent()->replaceNode(node, child); 358 | _ast->deleteNode(node); 359 | } 360 | } 361 | // Evaluate an assignment. 362 | else if (!isConditional() && op.isAssignment() && child->isVar()) { 363 | AstSymbol* sym = static_cast(child)->symbol(); 364 | if (sym->isAssigned()) { 365 | Value newValue = sym->value(); 366 | uint32_t typeInfo = child->typeInfo() & ~(kTypeRef | kTypeWrite); 367 | 368 | MPSL_PROPAGATE( 369 | Fold::foldUnaryOp(op.type(), sym->_value, sym->_value, typeInfo)); 370 | 371 | // Only `(.)++` and `(.)--` operators keep the previous value. 372 | if (!(op.flags() & kOpFlagAssignPost)) 373 | newValue = sym->value(); 374 | 375 | AstImm* newNode = _ast->newNode(newValue, typeInfo); 376 | MPSL_NULLCHECK(newNode); 377 | 378 | AstNode* oldNode = node->parent()->replaceNode(node, newNode); 379 | _ast->deleteNode(oldNode); 380 | } 381 | } 382 | // Simplify `-(-(x))` -> `x` and `~(~(x))` -> `x`. 383 | else if (child->nodeType() == AstNode::kTypeUnaryOp && node->opType() == child->opType()) { 384 | if (node->opType() == kOpNeg || node->opType() == kOpBitNeg) { 385 | AstNode* childOfChild = static_cast(child)->unlinkChild(); 386 | node->parent()->replaceNode(node, childOfChild); 387 | _ast->deleteNode(node); 388 | } 389 | } 390 | 391 | } 392 | 393 | return kErrorOk; 394 | } 395 | 396 | Error AstOptimizer::onBinaryOp(AstBinaryOp* node) noexcept { 397 | const OpInfo& op = OpInfo::get(node->opType()); 398 | 399 | AstNode* left = node->left(); 400 | AstNode* right = node->right(); 401 | 402 | if (op.isAssignment()) 403 | left->addNodeFlags(AstNode::kFlagSideEffect); 404 | 405 | MPSL_PROPAGATE(onNode(left)); 406 | left = node->left(); 407 | 408 | // Support minimal evaluation of `&&` and `||` operators. 409 | if (left->isImm() && op.isLogical()) { 410 | AstImm* lVal = static_cast(left); 411 | AstNode* result = nullptr; 412 | 413 | bool b = mpGetBooleanValue(&lVal->_value, lVal->typeInfo() & kTypeIdMask); 414 | if ((op.type() == kOpLogAnd && b) || // Fold `true && x` -> `x`. 415 | (op.type() == kOpLogOr && !b)) { // Fold `false || x` -> `x`. 416 | result = node->unlinkRight(); 417 | } 418 | else if ( 419 | (op.type() == kOpLogAnd && b) || // Fold `false && x` -> `false`. 420 | (op.type() == kOpLogOr && !b)) { // Fold `true || x` -> `true`. 421 | result = node->unlinkLeft(); 422 | } 423 | 424 | if (result == nullptr) 425 | return MPSL_TRACE_ERROR(kErrorInvalidState); 426 | 427 | _ast->deleteNode(node->parent()->replaceNode(node, result)); 428 | return onNode(result); 429 | } 430 | 431 | MPSL_PROPAGATE(onNode(right)); 432 | right = node->right(); 433 | 434 | if (!isUnreachable()) { 435 | uint32_t typeInfo = node->typeInfo(); 436 | 437 | bool lIsImm = left->isImm(); 438 | bool rIsImm = right->isImm(); 439 | 440 | // If both nodes are values it's easy, just fold them into a single one. 441 | if (lIsImm && rIsImm) { 442 | AstImm* lNode = static_cast(left); 443 | AstImm* rNode = static_cast(right); 444 | 445 | MPSL_PROPAGATE(Fold::foldBinaryOp(op.type(), 446 | lNode->_value, 447 | lNode->_value, lNode->typeInfo(), 448 | rNode->_value, rNode->typeInfo())); 449 | lNode->setTypeInfo(typeInfo | kTypeRead); 450 | 451 | node->unlinkLeft(); 452 | node->parent()->replaceNode(node, lNode); 453 | 454 | _ast->deleteNode(node); 455 | } 456 | // There is still a little optimization opportunity. 457 | else if (lIsImm) { 458 | AstImm* lNode = static_cast(left); 459 | double val; 460 | 461 | if (TypeInfo::isIntOrFPType(typeInfo) && mpValueAsScalarDouble(&val, &lNode->_value, left->typeInfo())) { 462 | if ((val == 0.0 && (op.flags() & kOpFlagNopIfL0)) || 463 | (val == 1.0 && (op.flags() & kOpFlagNopIfL1))) { 464 | node->unlinkRight(); 465 | node->parent()->replaceNode(node, right); 466 | 467 | _ast->deleteNode(node); 468 | } 469 | } 470 | } 471 | else if (rIsImm) { 472 | AstImm* rNode = static_cast(right); 473 | 474 | // Evaluate an assignment. 475 | if (!isConditional() && op.isAssignment() && left->isVar()) { 476 | AstSymbol* sym = static_cast(left)->symbol(); 477 | if (op.type() == kOpAssign || sym->isAssigned()) { 478 | MPSL_PROPAGATE( 479 | Fold::foldBinaryOp(op.type(), 480 | sym->_value, 481 | sym->_value, left->typeInfo(), 482 | rNode->_value, rNode->typeInfo())); 483 | 484 | typeInfo = (left->typeInfo() & ~(kTypeRef | kTypeWrite)) | kTypeRead; 485 | sym->setAssigned(); 486 | 487 | AstImm* newNode = _ast->newNode(sym->value(), typeInfo); 488 | MPSL_NULLCHECK(newNode); 489 | 490 | AstNode* oldNode = node->parent()->replaceNode(node, newNode); 491 | _ast->deleteNode(oldNode); 492 | } 493 | } 494 | else { 495 | double val; 496 | if (TypeInfo::isIntOrFPType(typeInfo) && mpValueAsScalarDouble(&val, &rNode->_value, right->typeInfo())) { 497 | if ((val == 0.0 && (op.flags() & kOpFlagNopIfR0)) || 498 | (val == 1.0 && (op.flags() & kOpFlagNopIfR1))) { 499 | node->unlinkLeft(); 500 | node->parent()->replaceNode(node, left); 501 | 502 | _ast->deleteNode(node); 503 | } 504 | } 505 | } 506 | } 507 | } 508 | 509 | return kErrorOk; 510 | } 511 | 512 | Error AstOptimizer::onCall(AstCall* node) noexcept { 513 | AstSymbol* sym = node->symbol(); 514 | uint32_t i, count = node->size(); 515 | 516 | for (i = 0; i < count; i++) 517 | MPSL_PROPAGATE(onNode(node->childAt(i))); 518 | 519 | // TODO: 520 | 521 | return kErrorOk; 522 | } 523 | 524 | } // mpsl namespace 525 | 526 | // [Api-End] 527 | #include "./mpsl_apiend.h" 528 | -------------------------------------------------------------------------------- /src/mpsl/mpastoptimizer_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPASTOPTIMIZER_P_H 9 | #define _MPSL_MPASTOPTIMIZER_P_H 10 | 11 | // [Dependencies - MPSL] 12 | #include "./mpast_p.h" 13 | 14 | // [Api-Begin] 15 | #include "./mpsl_apibegin.h" 16 | 17 | namespace mpsl { 18 | 19 | // ============================================================================ 20 | // [mpsl::AstOptimizer] 21 | // ============================================================================ 22 | 23 | //! \internal 24 | class AstOptimizer : public AstVisitor { 25 | public: 26 | MPSL_NONCOPYABLE(AstOptimizer) 27 | 28 | // -------------------------------------------------------------------------- 29 | // [Construction / Destruction] 30 | // -------------------------------------------------------------------------- 31 | 32 | AstOptimizer(AstBuilder* ast, ErrorReporter* errorReporter) noexcept; 33 | ~AstOptimizer() noexcept; 34 | 35 | // -------------------------------------------------------------------------- 36 | // [OnNode] 37 | // -------------------------------------------------------------------------- 38 | 39 | Error onProgram(AstProgram* node) noexcept; 40 | Error onFunction(AstFunction* node) noexcept; 41 | Error onBlock(AstBlock* node) noexcept; 42 | Error onBranch(AstBranch* node) noexcept; 43 | Error onLoop(AstLoop* node) noexcept; 44 | Error onBreak(AstBreak* node) noexcept; 45 | Error onContinue(AstContinue* node) noexcept; 46 | Error onReturn(AstReturn* node) noexcept; 47 | Error onVarDecl(AstVarDecl* node) noexcept; 48 | Error onVarMemb(AstVarMemb* node) noexcept; 49 | Error onVar(AstVar* node) noexcept; 50 | Error onImm(AstImm* node) noexcept; 51 | Error onUnaryOp(AstUnaryOp* node) noexcept; 52 | Error onBinaryOp(AstBinaryOp* node) noexcept; 53 | Error onCall(AstCall* node) noexcept; 54 | 55 | // -------------------------------------------------------------------------- 56 | // [Accessors] 57 | // -------------------------------------------------------------------------- 58 | 59 | MPSL_INLINE AstSymbol* currentRet() const noexcept { return _currentRet; } 60 | MPSL_INLINE bool isUnreachable() const noexcept { return _unreachable; } 61 | MPSL_INLINE bool isConditional() const noexcept { return _isConditional; } 62 | MPSL_INLINE bool isLocalScope() const noexcept { return _isLocalScope; } 63 | 64 | // -------------------------------------------------------------------------- 65 | // [Members] 66 | // -------------------------------------------------------------------------- 67 | 68 | ErrorReporter* _errorReporter; 69 | AstSymbol* _currentRet; 70 | 71 | bool _unreachable; 72 | bool _isConditional; 73 | bool _isLocalScope; 74 | }; 75 | 76 | } // mpsl namespace 77 | 78 | // [Api-End] 79 | #include "./mpsl_apiend.h" 80 | 81 | // [Guard] 82 | #endif // _MPSL_MPASTOPTIMIZER_P_H 83 | -------------------------------------------------------------------------------- /src/mpsl/mpatomic_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPATOMIC_P_H 9 | #define _MPSL_MPATOMIC_P_H 10 | 11 | // [Dependencies - MPSL] 12 | #include "./mpsl_p.h" 13 | 14 | // [Dependencies - MSC] 15 | #if defined(_MSC_VER) && _MSC_VER >= 1400 16 | #include 17 | #endif // _MSC_VER >= 1400 18 | 19 | // [Api-Begin] 20 | #include "./mpsl_apibegin.h" 21 | 22 | namespace mpsl { 23 | 24 | // ============================================================================ 25 | // [mpsl::mpAtomic] 26 | // ============================================================================ 27 | 28 | //! \internal 29 | static MPSL_INLINE uintptr_t mpAtomicGet(const uintptr_t* atomic) noexcept { 30 | return *(const uintptr_t volatile *)atomic; 31 | } 32 | 33 | //! \internal 34 | static MPSL_INLINE void mpAtomicSet(uintptr_t* atomic, size_t value) noexcept { 35 | *(uintptr_t volatile *)atomic = value; 36 | } 37 | 38 | #if defined(_MSC_VER) 39 | # if defined(__x86_64__) || defined(_WIN64) || defined(_M_IA64) || defined(_M_X64) 40 | # pragma intrinsic (_InterlockedIncrement64) 41 | # pragma intrinsic (_InterlockedDecrement64) 42 | # pragma intrinsic (_InterlockedExchange64) 43 | //! \internal 44 | static MPSL_INLINE uintptr_t mpAtomicSetXchg(uintptr_t* atomic, uintptr_t value) noexcept { 45 | return _InterlockedExchange64((__int64 volatile *)atomic, static_cast<__int64>(value)); 46 | }; 47 | //! \internal 48 | static MPSL_INLINE uintptr_t mpAtomicInc(uintptr_t* atomic) noexcept { 49 | return _InterlockedIncrement64((__int64 volatile *)atomic); 50 | }; 51 | //! \internal 52 | static MPSL_INLINE uintptr_t mpAtomicDec(uintptr_t* atomic) noexcept { 53 | return _InterlockedDecrement64((__int64 volatile *)atomic); 54 | } 55 | # else 56 | # pragma intrinsic (_InterlockedIncrement) 57 | # pragma intrinsic (_InterlockedDecrement) 58 | # pragma intrinsic (_InterlockedExchange) 59 | //! \internal 60 | static MPSL_INLINE uintptr_t mpAtomicSetXchg(uintptr_t* atomic, uintptr_t value) noexcept { 61 | return _InterlockedExchange((long volatile *)atomic, static_cast(value)); 62 | }; 63 | //! \internal 64 | static MPSL_INLINE uintptr_t mpAtomicInc(uintptr_t* atomic) noexcept { 65 | return _InterlockedIncrement((long volatile *)atomic); 66 | } 67 | //! \internal 68 | static MPSL_INLINE uintptr_t mpAtomicDec(uintptr_t* atomic) noexcept { 69 | return _InterlockedDecrement((long volatile *)atomic); 70 | } 71 | # endif // _64BIT 72 | #endif // _MSC_VER 73 | 74 | #if defined(__GNUC__) || defined(__clang__) 75 | //! \internal 76 | static MPSL_INLINE uintptr_t mpAtomicSetXchg(uintptr_t* atomic, uintptr_t value) noexcept { 77 | return __sync_lock_test_and_set(atomic, value); 78 | }; 79 | //! \internal 80 | static MPSL_INLINE uintptr_t mpAtomicInc(uintptr_t* atomic) noexcept { 81 | return __sync_add_and_fetch(atomic, 1); 82 | } 83 | //! \internal 84 | static MPSL_INLINE uintptr_t mpAtomicDec(uintptr_t* atomic) noexcept { 85 | return __sync_sub_and_fetch(atomic, 1); 86 | } 87 | #endif // __GNUC__ 88 | 89 | template 90 | MPSL_INLINE T mpAtomicSetXchgT(T* atomic, T value) noexcept { 91 | return (T)mpAtomicSetXchg((uintptr_t *)atomic, (uintptr_t)value); 92 | } 93 | 94 | } // mpsl namespace 95 | 96 | // [Api-End] 97 | #include "./mpsl_apiend.h" 98 | 99 | // [Guard] 100 | #endif // _MPSL_MPATOMIC_P_H 101 | -------------------------------------------------------------------------------- /src/mpsl/mpcodegen_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPCODEGEN_P_H 9 | #define _MPSL_MPCODEGEN_P_H 10 | 11 | // [Dependencies - MPSL] 12 | #include "./mpast_p.h" 13 | #include "./mpir_p.h" 14 | 15 | // [Api-Begin] 16 | #include "./mpsl_apibegin.h" 17 | 18 | namespace mpsl { 19 | 20 | // ============================================================================ 21 | // [mpsl::CodeGen] 22 | // ============================================================================ 23 | 24 | class CodeGenResult { 25 | public: 26 | MPSL_INLINE CodeGenResult(bool dependsOnResult = true) noexcept { 27 | this->result.reset(); 28 | this->dependsOnResult = dependsOnResult; 29 | } 30 | 31 | MPSL_INLINE bool hasValue() const noexcept { 32 | return result.lo != nullptr; 33 | } 34 | 35 | IRPair result; 36 | bool dependsOnResult; 37 | }; 38 | 39 | //! \internal 40 | class CodeGen : public AstVisitorWithArgs { 41 | public: 42 | MPSL_NONCOPYABLE(CodeGen) 43 | 44 | typedef CodeGenResult Result; 45 | 46 | typedef Set< AstFunction* > FunctionSet; 47 | typedef Map< AstSymbol*, IRPair > VarMap; 48 | typedef Map< AstSymbol*, IRMem* > MemMap; 49 | 50 | struct DataSlot { 51 | MPSL_INLINE DataSlot(uint32_t slot, int32_t offset) noexcept 52 | : slot(slot), 53 | offset(offset) {} 54 | 55 | uint32_t slot; 56 | int32_t offset; 57 | }; 58 | 59 | // -------------------------------------------------------------------------- 60 | // [Construction / Destruction] 61 | // -------------------------------------------------------------------------- 62 | 63 | CodeGen(AstBuilder* ast, IRBuilder* ir) noexcept; 64 | ~CodeGen() noexcept; 65 | 66 | // -------------------------------------------------------------------------- 67 | // [OnNode] 68 | // -------------------------------------------------------------------------- 69 | 70 | Error onProgram(AstProgram* node, Result& out) noexcept; 71 | Error onFunction(AstFunction* node, Result& out) noexcept; 72 | Error onBlock(AstBlock* node, Result& out) noexcept; 73 | Error onBranch(AstBranch* node, Result& out) noexcept; 74 | Error onLoop(AstLoop* node, Result& out) noexcept; 75 | Error onBreak(AstBreak* node, Result& out) noexcept; 76 | Error onContinue(AstContinue* node, Result& out) noexcept; 77 | Error onReturn(AstReturn* node, Result& out) noexcept; 78 | Error onVarDecl(AstVarDecl* node, Result& out) noexcept; 79 | Error onVarMemb(AstVarMemb* node, Result& out) noexcept; 80 | Error onVar(AstVar* node, Result& out) noexcept; 81 | Error onImm(AstImm* node, Result& out) noexcept; 82 | Error onUnaryOp(AstUnaryOp* node, Result& out) noexcept; 83 | Error onBinaryOp(AstBinaryOp* node, Result& out) noexcept; 84 | Error onCall(AstCall* node, Result& out) noexcept; 85 | 86 | // -------------------------------------------------------------------------- 87 | // [Accessors] 88 | // -------------------------------------------------------------------------- 89 | 90 | MPSL_INLINE IRBuilder* ir() const noexcept { return _ir; } 91 | MPSL_INLINE IRBlock* block() const noexcept { return _block; } 92 | MPSL_INLINE ZoneAllocator* allocator() const noexcept { return _ir->allocator(); } 93 | 94 | MPSL_INLINE bool hasV256() const noexcept { return _hasV256; } 95 | MPSL_INLINE bool needSplit(uint32_t width) const { return width > 16 && !_hasV256; } 96 | 97 | // -------------------------------------------------------------------------- 98 | // [Utilities] 99 | // -------------------------------------------------------------------------- 100 | 101 | Error mapVarToAst(AstSymbol* sym, IRPair var) noexcept; 102 | 103 | Error newVar(IRPair& dst, uint32_t typeInfo) noexcept; 104 | Error newImm(IRPair& dst, const Value& value, uint32_t typeInfo) noexcept; 105 | Error addrOfData(IRPair& dst, DataSlot data, uint32_t width) noexcept; 106 | 107 | Error asVar(IRPair& out, IRPair in, uint32_t typeInfo) noexcept; 108 | 109 | Error emitMove(IRPair dst, IRPair src, uint32_t typeInfo) noexcept; 110 | Error emitStore(IRPair dst, IRPair src, uint32_t typeInfo) noexcept; 111 | Error emitInst2(uint32_t instCode, 112 | IRPair o0, 113 | IRPair o1, uint32_t typeInfo) noexcept; 114 | 115 | Error emitInst3(uint32_t instCode, 116 | IRPair o0, 117 | IRPair o1, 118 | IRPair o2, uint32_t typeInfo) noexcept; 119 | 120 | // TODO: Rename after API is completed. 121 | Error emitFetchX(IRReg* dst, IRMem* src, uint32_t typeInfo) noexcept; 122 | Error emitStoreX(IRMem* dst, IRReg* src, uint32_t typeInfo) noexcept; 123 | 124 | // -------------------------------------------------------------------------- 125 | // [Members] 126 | // -------------------------------------------------------------------------- 127 | 128 | IRBuilder* _ir; //!< IR builder. 129 | IRBlock* _block; //!< Current block. 130 | 131 | int _functionLevel; //!< Current function level (0 if main). 132 | bool _hasV256; //!< Use 256-bit SIMD instructions. 133 | 134 | AstSymbol* _hiddenRet; //!< A hidden return variable internally named `@ret`. 135 | IRPair _currentRet; //!< Current return, required by \ref onReturn(). 136 | 137 | FunctionSet _nestedFunctions; //!< Hash of all nested functions. 138 | VarMap _varMap; //!< Mapping of `AstVar` to `IRPair`. 139 | MemMap _memMap; //!< Mapping of `AstVarMemb` to `IRMem`. 140 | }; 141 | 142 | } // mpsl namespace 143 | 144 | // [Api-End] 145 | #include "./mpsl_apiend.h" 146 | 147 | // [Guard] 148 | #endif // _MPSL_MPCODEGEN_P_H 149 | -------------------------------------------------------------------------------- /src/mpsl/mpfold_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPFOLD_P_H 9 | #define _MPSL_MPFOLD_P_H 10 | 11 | // [Dependencies - MPSL] 12 | #include "./mpsl_p.h" 13 | 14 | // [Api-Begin] 15 | #include "./mpsl_apibegin.h" 16 | 17 | namespace mpsl { 18 | 19 | // ============================================================================ 20 | // [mpsl::Fold] 21 | // ============================================================================ 22 | 23 | //! \internal 24 | //! 25 | //! Contains an implementation of all operations (both operations and IR 26 | //! instructions) that are used in MPSL. Functions in this namespace are 27 | //! used by both AST-based and IR-based constant folding. 28 | namespace Fold { 29 | 30 | // ============================================================================ 31 | // [mpsl::Fold - AST-Based Constant Folding] 32 | // ============================================================================ 33 | 34 | Error foldCast(Value& dVal, uint32_t dTypeInfo, const Value& sVal, uint32_t sTypeInfo) noexcept; 35 | Error foldSwizzle(const uint8_t* swizzleArray, Value& dVal, const Value& sVal, uint32_t sTypeInfo) noexcept; 36 | Error foldUnaryOp(uint32_t op, Value& dVal, const Value& sVal, uint32_t sTypeInfo) noexcept; 37 | 38 | Error foldBinaryOp(uint32_t op, Value& dVal, 39 | const Value& lVal, uint32_t lTypeInfo, 40 | const Value& rVal, uint32_t rTypeInfo) noexcept; 41 | 42 | // ============================================================================ 43 | // [mpsl::Fold - IR-Based Constant Folding] 44 | // ============================================================================ 45 | 46 | Error foldInst(uint32_t inst, Value& dVal, const Value& sVal) noexcept; 47 | Error foldInst(uint32_t inst, Value& dVal, const Value& lVal, const Value& rVal) noexcept; 48 | 49 | } // Fold namespace 50 | } // mpsl namespace 51 | 52 | // [Api-End] 53 | #include "./mpsl_apiend.h" 54 | 55 | // [Guard] 56 | #endif // _MPSL_MPFOLD_P_H 57 | -------------------------------------------------------------------------------- /src/mpsl/mpformatutils.cpp: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Export] 8 | #define MPSL_EXPORTS 9 | 10 | // [Dependencies - MPSL] 11 | #include "./mpformatutils_p.h" 12 | #include "./mplang_p.h" 13 | 14 | // [Api-Begin] 15 | #include "./mpsl_apibegin.h" 16 | 17 | namespace mpsl { 18 | 19 | struct EnumString { 20 | const char name[16]; 21 | }; 22 | 23 | static const EnumString mpAstSymbolType[] = { 24 | { "" }, 25 | { "typename" }, 26 | { "operator" }, 27 | { "variable" }, 28 | { "function" } 29 | }; 30 | 31 | // ============================================================================ 32 | // [mpsl::Utils] 33 | // ============================================================================ 34 | 35 | String& FormatUtils::sformat(String& sb, const char* fmt, ...) noexcept { 36 | va_list ap; 37 | va_start(ap, fmt); 38 | 39 | vformat(sb, fmt, ap); 40 | 41 | va_end(ap); 42 | return sb; 43 | } 44 | 45 | String& FormatUtils::vformat(String& sb, const char* fmt, va_list ap) noexcept { 46 | const char kFormatChar = '%'; 47 | 48 | const char* p = fmt; 49 | const char* mark = fmt; 50 | 51 | for (;;) { 52 | uint32_t c = static_cast(*p); 53 | if (c == '\0') 54 | break; 55 | p++; 56 | 57 | // Don't continue if the character is not a formatting mark. In most cases 58 | // this branch should be taken as most of the string should be just a text. 59 | if (c != kFormatChar) 60 | continue; 61 | 62 | // NULL terminator after a formatting mark. It's safe to just break here. 63 | // The trailing mark will be appended to the string as well. However, this 64 | // is basically handling of an invalid `fmt` string. 65 | c = static_cast(*p); 66 | if (c == '\0') 67 | break; 68 | 69 | // Handle a double-escaped formatting mark "%%", which produces "%". 70 | bool isEscape = (c == kFormatChar); 71 | sb.appendString(mark, (size_t)(p - mark) - 1 + isEscape); 72 | 73 | // Guess a new mark position, which is exactly two characters after the 74 | // initial mark when simple or a double-escaped mark formatting has been 75 | // used. MPSL formatting extensions will adjust the mark again. 76 | mark = ++p; 77 | if (isEscape) 78 | continue; 79 | 80 | if (c == '{') { 81 | // ---------------------------------------------------------------------- 82 | // [MPSL Formatting Extensions - '%{...}'] 83 | // ---------------------------------------------------------------------- 84 | 85 | do { 86 | c = static_cast(*p); 87 | // Invalid formatting, bail out. 88 | if (c == '\0') { 89 | mark -= 2; 90 | goto _Done; 91 | } 92 | p++; 93 | } while (c != '}'); 94 | 95 | StringRef ext(mark, (size_t)(p - mark) - 1); 96 | mark = p; 97 | 98 | // Handle '%{StringRef}' passed as 'const StringRef*'. 99 | if (ext.eq("StringRef", 9)) { 100 | const StringRef* value = va_arg(ap, StringRef*); 101 | 102 | sb.appendString(value->data(), value->size()); 103 | continue; 104 | } 105 | 106 | // Handle '%{Type}' passed as 'unsigned int'. 107 | if (ext.eq("Type", 4)) { 108 | unsigned int type = va_arg(ap, unsigned int); 109 | 110 | formatType(sb, type); 111 | continue; 112 | } 113 | 114 | // Handle '%{Value}' passed as 'unsigned int, const Value*'. 115 | if (ext.eq("Value", 5)) { 116 | unsigned int type = va_arg(ap, unsigned int); 117 | const Value* value = va_arg(ap, const Value*); 118 | 119 | formatValue(sb, type, value); 120 | continue; 121 | } 122 | 123 | // Handle '%{SymbolType}' passed as 'unsigned int'. 124 | if (ext.eq("SymbolType", 10)) { 125 | unsigned int value = va_arg(ap, unsigned int); 126 | 127 | sb.appendString(mpAstSymbolType[value].name); 128 | continue; 129 | } 130 | 131 | // Unhandled, revert to '%'. 132 | mark = ext.data() - 2; 133 | } 134 | else { 135 | // ---------------------------------------------------------------------- 136 | // [C-Like Formatting Extensions] 137 | // ---------------------------------------------------------------------- 138 | 139 | // Handle '%d' - C[int32_t], MPSL[int]. 140 | if (c == 'd') { 141 | sb.appendInt(static_cast(va_arg(ap, int)), 10); 142 | continue; 143 | } 144 | 145 | // Handle '%u' - C[uint32_t], MPSL[uint]. 146 | if (c == 'u') { 147 | sb.appendUInt(static_cast(va_arg(ap, unsigned int)), 10); 148 | continue; 149 | } 150 | 151 | // Handle '%q' - C[int64_t] / MPSL[long]. 152 | if (c == 'q') { 153 | sb.appendInt(va_arg(ap, int64_t), 10); 154 | continue; 155 | } 156 | 157 | // Handle '%s' - NULL terminated string. 158 | if (c == 's') { 159 | const char* s = va_arg(ap, const char*); 160 | if (s == nullptr) s = ""; 161 | 162 | sb.appendString(s); 163 | continue; 164 | } 165 | 166 | // Unhandled, revert to '%'. 167 | mark = p - 2; 168 | } 169 | } 170 | 171 | _Done: 172 | if (mark != p) 173 | sb.appendString(mark, (size_t)(p - mark)); 174 | 175 | return sb; 176 | } 177 | 178 | String& FormatUtils::formatType(String& sb, uint32_t typeInfo) noexcept { 179 | uint32_t typeId = typeInfo & kTypeIdMask; 180 | const char* typeName = ""; 181 | 182 | switch (typeInfo & kTypeRW) { 183 | case kTypeRO: sb.appendString("const ", 6); break; 184 | case kTypeWO: sb.appendString("out ", 6); break; 185 | } 186 | 187 | if (typeId < kTypeCount) 188 | typeName = TypeInfo::get(typeId).name(); 189 | 190 | sb.appendString(typeName); 191 | 192 | uint32_t count = TypeInfo::elementsOf(typeInfo); 193 | if (count > 0) 194 | sb.appendInt(count); 195 | 196 | if (typeInfo & kTypeRef) 197 | sb.appendString(" &", 2); 198 | 199 | return sb; 200 | } 201 | 202 | String& FormatUtils::formatValue(String& sb, uint32_t typeInfo, const Value* value_) noexcept { 203 | uint32_t id = typeInfo & kTypeIdMask; 204 | uint32_t count = TypeInfo::elementsOf(typeInfo); 205 | 206 | uint32_t i = 0; 207 | const uint8_t* value = reinterpret_cast(value_); 208 | 209 | if (count > 1) 210 | sb.appendChar('{'); 211 | 212 | for (;;) { 213 | switch (id) { 214 | case kTypeVoid: { 215 | sb.appendString("(void)"); 216 | break; 217 | } 218 | 219 | case kTypeBool: { 220 | uint32_t mask = reinterpret_cast(value)[0]; 221 | if (mask <= 1) { 222 | sb.appendString(mask == 0 ? "false" : "true"); 223 | } 224 | else { 225 | sb.appendString("0x"); 226 | sb.appendUInt(mask, 16, 8); 227 | } 228 | value += 4; 229 | break; 230 | } 231 | 232 | case kTypeQBool: { 233 | uint64_t mask = reinterpret_cast(value)[0]; 234 | if (mask <= 1) { 235 | sb.appendString(mask == 0 ? "false" : "true"); 236 | } 237 | else { 238 | sb.appendString("0x"); 239 | sb.appendUInt(mask, 16, 16); 240 | } 241 | value += 8; 242 | break; 243 | } 244 | 245 | case kTypeInt: { 246 | sb.appendInt(reinterpret_cast(value)[0]); 247 | value += 4; 248 | break; 249 | } 250 | 251 | case kTypeFloat: 252 | case kTypeDouble: { 253 | double dVal; 254 | 255 | if (id == kTypeFloat) { 256 | dVal = reinterpret_cast(value)[0]; 257 | value += 4; 258 | } 259 | else { 260 | dVal = reinterpret_cast(value)[0]; 261 | value += 8; 262 | } 263 | 264 | sb.appendFormat("%g", dVal); 265 | break; 266 | } 267 | 268 | case kTypePtr: { 269 | sb.appendString("__ptr"); 270 | break; 271 | } 272 | } 273 | 274 | if (++i >= count) 275 | break; 276 | 277 | sb.appendString(", "); 278 | } 279 | 280 | if (count > 1) 281 | sb.appendChar('}'); 282 | 283 | return sb; 284 | } 285 | 286 | void FormatUtils::formatSwizzleArray(char* dst, const uint8_t* swizzleArray, uint32_t count) noexcept { 287 | const char* letters = mpVectorIdentifiers[0].letters(); 288 | uint32_t i, max = 8; 289 | 290 | for (i = 0; i < count; i++) { 291 | uint32_t pos = swizzleArray[i]; 292 | if (pos < max) 293 | dst[i] = letters[pos]; 294 | else 295 | dst[i] = (pos == 0xF) ? '0' : '?'; 296 | } 297 | 298 | dst[i] = '\0'; 299 | } 300 | 301 | } // mpsl namespace 302 | 303 | // [Api-End] 304 | #include "./mpsl_apiend.h" 305 | -------------------------------------------------------------------------------- /src/mpsl/mpformatutils_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPFORMATUTILS_P_H 9 | #define _MPSL_MPFORMATUTILS_P_H 10 | 11 | // [Dependencies - MPSL] 12 | #include "./mpsl_p.h" 13 | 14 | // [Api-Begin] 15 | #include "./mpsl_apibegin.h" 16 | 17 | namespace mpsl { 18 | 19 | // ============================================================================ 20 | // [mpsl::FormatUtils] 21 | // ============================================================================ 22 | 23 | //! \internal 24 | //! 25 | //! String format helpers. 26 | namespace FormatUtils { 27 | 28 | //! Append MPSL specific formatted string `fmt` into `sb`. 29 | String& sformat(String& sb, const char* fmt, ...) noexcept; 30 | //! Append MPSL specific formatted string `fmt` into `sb` (`va_list` version). 31 | String& vformat(String& sb, const char* fmt, va_list ap) noexcept; 32 | 33 | //! Append a formatted type name and related information of `type`. 34 | //! 35 | //! Provided also by `sformat`'s `"%{Type}"` extension. 36 | String& formatType(String& sb, uint32_t typeInfo) noexcept; 37 | 38 | //! Append a formatted `value` (scalar or vector) of type `type`. 39 | //! 40 | //! Provided also by `sformat`'s `"%{Value}"` extension. 41 | String& formatValue(String& sb, uint32_t typeInfo, const Value* value) noexcept; 42 | 43 | //! Append a formatted swizzle letters. 44 | void formatSwizzleArray(char* dst, const uint8_t* swizzleArray, uint32_t count) noexcept; 45 | 46 | } // FormatUtils namespace 47 | } // mpsl namespace 48 | 49 | // [Api-End] 50 | #include "./mpsl_apiend.h" 51 | 52 | // [Guard] 53 | #endif // _MPSL_MPFORMATUTILS_P_H 54 | -------------------------------------------------------------------------------- /src/mpsl/mphash.cpp: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Export] 8 | #define MPSL_EXPORTS 9 | 10 | // [Dependencies - MPSL] 11 | #include "./mphash_p.h" 12 | 13 | // [Api-Begin] 14 | #include "./mpsl_apibegin.h" 15 | 16 | namespace mpsl { 17 | 18 | // ============================================================================ 19 | // [Helpers] 20 | // ============================================================================ 21 | 22 | static const uint32_t mpPrimeTable[] = { 23 | 19, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593 24 | }; 25 | 26 | // ============================================================================ 27 | // [mpsl::HashUtils] 28 | // ============================================================================ 29 | 30 | uint32_t HashUtils::hashString(const char* data, size_t size) noexcept { 31 | if (size == 0) 32 | return 0; 33 | 34 | uint32_t hashCode = *data++; 35 | if (--size == 0) 36 | return hashCode; 37 | 38 | do { 39 | hashCode = hashChar(hashCode, static_cast(*data++)); 40 | } while (--size); 41 | 42 | return hashCode; 43 | } 44 | 45 | uint32_t HashUtils::closestPrime(uint32_t x) noexcept { 46 | uint32_t p, i = 0; 47 | 48 | do { 49 | if ((p = mpPrimeTable[i]) > x) 50 | break; 51 | } while (++i < MPSL_ARRAY_SIZE(mpPrimeTable)); 52 | 53 | return p; 54 | } 55 | 56 | // ============================================================================ 57 | // [mpsl::HashBase - Reset / Rehash] 58 | // ============================================================================ 59 | 60 | void HashBase::_rehash(uint32_t newCount) noexcept { 61 | ZoneAllocator* allocator = _allocator; 62 | 63 | HashNode** oldData = _data; 64 | HashNode** newData = static_cast( 65 | allocator->allocZeroed( 66 | static_cast(newCount + kExtraCount) * sizeof(void*))); 67 | 68 | if (newData == nullptr) 69 | return; 70 | 71 | uint32_t i; 72 | uint32_t oldCount = _bucketsCount; 73 | 74 | for (i = 0; i < oldCount; i++) { 75 | HashNode* node = oldData[i]; 76 | while (node) { 77 | HashNode* next = node->_next; 78 | uint32_t hMod = node->_hashCode % newCount; 79 | 80 | node->_next = newData[hMod]; 81 | newData[hMod] = node; 82 | 83 | node = next; 84 | } 85 | } 86 | 87 | // Move extra entries. 88 | for (i = 0; i < kExtraCount; i++) { 89 | newData[i + newCount] = oldData[i + oldCount]; 90 | } 91 | 92 | // 90% is the maximum occupancy, can't overflow since the maximum capacity 93 | // is limited to the last prime number stored in the `mpPrimeTable[]` array. 94 | _bucketsCount = newCount; 95 | _bucketsGrow = newCount * 9 / 10; 96 | 97 | _data = newData; 98 | if (oldData != _embedded) 99 | allocator->release(oldData, 100 | static_cast(oldCount + kExtraCount) * sizeof(void*)); 101 | } 102 | 103 | void HashBase::_mergeToInvisibleSlot(HashBase& other) noexcept { 104 | uint32_t i, count = other._bucketsCount + kExtraCount; 105 | HashNode** data = other._data; 106 | 107 | HashNode* first; 108 | HashNode* last; 109 | 110 | // Find the `first` node. 111 | for (i = 0; i < count; i++) { 112 | first = data[i]; 113 | if (first) break; 114 | } 115 | 116 | if (first) { 117 | // Initialize `first` and `last`. 118 | last = first; 119 | while (last->_next) 120 | last = last->_next; 121 | data[i] = nullptr; 122 | 123 | // Iterate over the rest and append so `first` stay the same and `last` 124 | // is updated to the last node added. 125 | while (++i < count) { 126 | HashNode* node = data[i]; 127 | if (node) { 128 | last->_next = node; 129 | last = node; 130 | while (last->_next) 131 | last = last->_next; 132 | data[i] = nullptr; 133 | } 134 | } 135 | 136 | // Link with ours. 137 | if (last) { 138 | i = _bucketsCount + kExtraFirst; 139 | last->_next = _data[i]; 140 | _data[i] = first; 141 | } 142 | } 143 | } 144 | 145 | // ============================================================================ 146 | // [mpsl::HashBase - Ops] 147 | // ============================================================================ 148 | 149 | HashNode* HashBase::_put(HashNode* node) noexcept { 150 | uint32_t hMod = node->_hashCode % _bucketsCount; 151 | HashNode* next = _data[hMod]; 152 | 153 | node->_next = next; 154 | _data[hMod] = node; 155 | 156 | if (++_size >= _bucketsGrow && next != nullptr) { 157 | uint32_t newCapacity = HashUtils::closestPrime(_bucketsCount + kExtraCount); 158 | if (newCapacity != _bucketsCount) 159 | _rehash(newCapacity); 160 | } 161 | 162 | return node; 163 | } 164 | 165 | HashNode* HashBase::_del(HashNode* node) noexcept { 166 | uint32_t hMod = node->_hashCode % _bucketsCount; 167 | 168 | HashNode** pPrev = &_data[hMod]; 169 | HashNode* p = *pPrev; 170 | 171 | while (p) { 172 | if (p == node) { 173 | *pPrev = p->_next; 174 | return node; 175 | } 176 | 177 | pPrev = &p->_next; 178 | p = *pPrev; 179 | } 180 | 181 | return nullptr; 182 | } 183 | 184 | } // mpsl namespace 185 | 186 | // [Api-End] 187 | #include "./mpsl_apiend.h" 188 | -------------------------------------------------------------------------------- /src/mpsl/mphash_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPHASH_P_H 9 | #define _MPSL_MPHASH_P_H 10 | 11 | // [Dependencies - MPSL] 12 | #include "./mpsl_p.h" 13 | 14 | // [Api-Begin] 15 | #include "./mpsl_apibegin.h" 16 | 17 | namespace mpsl { 18 | 19 | // ============================================================================ 20 | // [mpsl::HashUtils] 21 | // ============================================================================ 22 | 23 | namespace HashUtils { 24 | // \internal 25 | static MPSL_INLINE uint32_t hashPointer(const void* kPtr) noexcept { 26 | uintptr_t p = (uintptr_t)kPtr; 27 | return static_cast( 28 | ((p >> 3) ^ (p >> 7) ^ (p >> 12) ^ (p >> 20) ^ (p >> 27)) & 0xFFFFFFFFu); 29 | } 30 | 31 | // \internal 32 | static MPSL_INLINE uint32_t hashChar(uint32_t hash, uint32_t c) noexcept { 33 | return hash * 65599 + c; 34 | } 35 | 36 | // \internal 37 | // 38 | // Get a hash of the given string `data` of size `size`. This function doesn't 39 | // require `data` to be NULL terminated. 40 | uint32_t hashString(const char* data, size_t size) noexcept; 41 | //! \overload 42 | static MPSL_INLINE uint32_t hashString(const StringRef& str) noexcept { 43 | return hashString(str.data(), str.size()); 44 | } 45 | 46 | // \internal 47 | // 48 | // Get a prime number that is close to `x`, but always greater than or equal to `x`. 49 | uint32_t closestPrime(uint32_t x) noexcept; 50 | }; 51 | 52 | // ============================================================================ 53 | // [mpsl::HashNode] 54 | // ============================================================================ 55 | 56 | class HashNode { 57 | public: 58 | MPSL_INLINE HashNode(uint32_t hashCode = 0) noexcept 59 | : _next(nullptr), _hashCode(hashCode) {} 60 | 61 | //! Next node in the chain, null if last node. 62 | HashNode* _next; 63 | //! Hash code. 64 | uint32_t _hashCode; 65 | }; 66 | 67 | // ============================================================================ 68 | // [mpsl::HashBase] 69 | // ============================================================================ 70 | 71 | class HashBase { 72 | public: 73 | MPSL_NONCOPYABLE(HashBase) 74 | 75 | enum { 76 | kExtraFirst = 0, 77 | kExtraCount = 1 78 | }; 79 | 80 | // -------------------------------------------------------------------------- 81 | // [Construction / Destruction] 82 | // -------------------------------------------------------------------------- 83 | 84 | MPSL_INLINE HashBase(ZoneAllocator* allocator) noexcept { 85 | _allocator = allocator; 86 | _size = 0; 87 | 88 | _bucketsCount = 1; 89 | _bucketsGrow = 1; 90 | 91 | _data = _embedded; 92 | for (uint32_t i = 0; i <= kExtraCount; i++) 93 | _embedded[i] = nullptr; 94 | } 95 | 96 | MPSL_INLINE ~HashBase() noexcept { 97 | if (_data != _embedded) 98 | _allocator->release(_data, static_cast(_bucketsCount + kExtraCount) * sizeof(void*)); 99 | } 100 | 101 | // -------------------------------------------------------------------------- 102 | // [Accessors] 103 | // -------------------------------------------------------------------------- 104 | 105 | MPSL_INLINE ZoneAllocator* allocator() const noexcept { return _allocator; } 106 | 107 | // -------------------------------------------------------------------------- 108 | // [Ops] 109 | // -------------------------------------------------------------------------- 110 | 111 | void _rehash(uint32_t newCount) noexcept; 112 | void _mergeToInvisibleSlot(HashBase& other) noexcept; 113 | 114 | HashNode* _put(HashNode* node) noexcept; 115 | HashNode* _del(HashNode* node) noexcept; 116 | 117 | // -------------------------------------------------------------------------- 118 | // [Members] 119 | // -------------------------------------------------------------------------- 120 | 121 | ZoneAllocator* _allocator; 122 | 123 | uint32_t _size; 124 | uint32_t _bucketsCount; 125 | uint32_t _bucketsGrow; 126 | 127 | HashNode** _data; 128 | HashNode* _embedded[1 + kExtraCount]; 129 | }; 130 | 131 | // ============================================================================ 132 | // [mpsl::Hash] 133 | // ============================================================================ 134 | 135 | //! \internal 136 | //! 137 | //! Low level hash table container used by MPSL, with some "special" features. 138 | //! 139 | //! Notes: 140 | //! 141 | //! 1. This hash table allows duplicates to be inserted (the API is so low 142 | //! level that it's up to you if you allow it or not, as you should first 143 | //! `get()` the node and then modify it or insert a new node by using `put()`, 144 | //! depending on the intention). 145 | //! 146 | //! 2. This hash table also contains "invisible" nodes that are not used by 147 | //! the basic hash functions, but can be used by functions having "invisible" 148 | //! in their name. These are used by the parser to merge symbols from the 149 | //! current scope that is being closed into the root local scope, but without 150 | //! making these symbols visible to the symbol resolver. This feature can be 151 | //! completely ignored if not needed. 152 | //! 153 | //! Hash is currently used by AST to keep references of global and local 154 | //! symbols and by AST to IR translator to associate IR specific data with AST. 155 | template 156 | class Hash : public HashBase { 157 | public: 158 | // -------------------------------------------------------------------------- 159 | // [Construction / Destruction] 160 | // -------------------------------------------------------------------------- 161 | 162 | MPSL_INLINE Hash(ZoneAllocator* allocator) noexcept 163 | : HashBase(allocator) {} 164 | 165 | // -------------------------------------------------------------------------- 166 | // [Ops] 167 | // -------------------------------------------------------------------------- 168 | 169 | template 170 | void reset(ReleaseHandler& handler) noexcept { 171 | HashNode** data = _data; 172 | uint32_t bucketsCount = _bucketsCount + kExtraCount; 173 | 174 | for (uint32_t i = 0; i < bucketsCount; i++) { 175 | HashNode* node = data[i]; 176 | 177 | while (node) { 178 | HashNode* next = node->_next; 179 | handler.release(static_cast(node)); 180 | node = next; 181 | } 182 | } 183 | 184 | if (data != _embedded) 185 | _allocator->release(data, static_cast(bucketsCount + kExtraCount) * sizeof(void*)); 186 | 187 | _bucketsCount = 1; 188 | _bucketsGrow = 1; 189 | 190 | _size = 0; 191 | _data = _embedded; 192 | 193 | for (uint32_t i = 0; i <= kExtraCount; i++) 194 | _embedded[i] = nullptr; 195 | } 196 | 197 | MPSL_INLINE void mergeToInvisibleSlot(Hash& other) noexcept { 198 | _mergeToInvisibleSlot(other); 199 | } 200 | 201 | MPSL_INLINE Node* get(const Key& key, uint32_t hashCode) const noexcept { 202 | uint32_t hMod = hashCode % _bucketsCount; 203 | Node* node = static_cast(_data[hMod]); 204 | 205 | while (node) { 206 | if (node->eq(key)) 207 | return node; 208 | node = static_cast(node->_next); 209 | } 210 | 211 | return nullptr; 212 | } 213 | 214 | MPSL_INLINE Node* put(Node* node) noexcept { return static_cast(_put(node)); } 215 | MPSL_INLINE Node* del(Node* node) noexcept { return static_cast(_del(node)); } 216 | }; 217 | 218 | // ============================================================================ 219 | // [mpsl::Map] 220 | // ============================================================================ 221 | 222 | //! \internal 223 | template 224 | class Map { 225 | public: 226 | MPSL_NONCOPYABLE(Map) 227 | 228 | struct Node : public HashNode { 229 | MPSL_INLINE Node(const Key& key, const Value& value, uint32_t hashCode) noexcept 230 | : HashNode(hashCode), 231 | _key(key), 232 | _value(value) {} 233 | MPSL_INLINE bool eq(const Key& key) noexcept { return _key == key; } 234 | 235 | Key _key; 236 | Value _value; 237 | }; 238 | 239 | struct ReleaseHandler { 240 | MPSL_INLINE ReleaseHandler(ZoneAllocator* allocator) noexcept 241 | : _allocator(allocator) {} 242 | 243 | MPSL_INLINE void release(Node* node) noexcept { 244 | _allocator->release(node, sizeof(Node)); 245 | } 246 | 247 | ZoneAllocator* _allocator; 248 | }; 249 | 250 | // -------------------------------------------------------------------------- 251 | // [Construction / Destruction] 252 | // -------------------------------------------------------------------------- 253 | 254 | MPSL_INLINE Map(ZoneAllocator* allocator) noexcept 255 | : _hash(allocator) {} 256 | 257 | MPSL_INLINE ~Map() noexcept { 258 | ReleaseHandler releaseHandler(_hash.allocator()); 259 | _hash.reset(releaseHandler); 260 | } 261 | 262 | // -------------------------------------------------------------------------- 263 | // [Ops] 264 | // -------------------------------------------------------------------------- 265 | 266 | MPSL_INLINE bool has(const Key& key) const noexcept { 267 | uint32_t hashCode = HashUtils::hashPointer(key); 268 | uint32_t hMod = hashCode % _hash._bucketsCount; 269 | Node* node = static_cast(_hash._data[hMod]); 270 | 271 | while (node) { 272 | if (node->eq(key)) 273 | return true; 274 | node = static_cast(node->_next); 275 | } 276 | return false; 277 | } 278 | 279 | MPSL_INLINE Value get(const Key& key) const noexcept { 280 | uint32_t hashCode = HashUtils::hashPointer(key); 281 | uint32_t hMod = hashCode % _hash._bucketsCount; 282 | Node* node = static_cast(_hash._data[hMod]); 283 | 284 | while (node) { 285 | if (node->eq(key)) 286 | return node->_value; 287 | node = static_cast(node->_next); 288 | } 289 | 290 | return Value(); 291 | } 292 | 293 | MPSL_INLINE bool get(const Key& key, Value** pValue) const noexcept { 294 | uint32_t hashCode = HashUtils::hashPointer(key); 295 | uint32_t hMod = hashCode % _hash._bucketsCount; 296 | Node* node = static_cast(_hash._data[hMod]); 297 | 298 | while (node) { 299 | if (node->eq(key)) { 300 | *pValue = &node->_value; 301 | return true; 302 | } 303 | node = static_cast(node->_next); 304 | } 305 | return false; 306 | } 307 | 308 | MPSL_INLINE Error put(const Key& key, const Value& value) noexcept { 309 | Node* node = static_cast(_hash._allocator->alloc(sizeof(Node))); 310 | if (node == nullptr) 311 | return MPSL_TRACE_ERROR(kErrorNoMemory); 312 | 313 | uint32_t hashCode = HashUtils::hashPointer(key); 314 | _hash.put(new(node) Node(key, value, hashCode)); 315 | 316 | return kErrorOk; 317 | } 318 | 319 | Hash _hash; 320 | }; 321 | 322 | // ============================================================================ 323 | // [mpsl::Set] 324 | // ============================================================================ 325 | 326 | //! \internal 327 | template 328 | class Set { 329 | public: 330 | MPSL_NONCOPYABLE(Set) 331 | 332 | struct Node : public HashNode { 333 | MPSL_INLINE Node(const Key& key, uint32_t hashCode) noexcept 334 | : HashNode(hashCode), 335 | _key(key) {} 336 | MPSL_INLINE bool eq(const Key& key) noexcept { return _key == key; } 337 | 338 | Key _key; 339 | }; 340 | 341 | struct ReleaseHandler { 342 | MPSL_INLINE ReleaseHandler(ZoneAllocator* allocator) noexcept 343 | : _allocator(allocator) {} 344 | 345 | MPSL_INLINE void release(Node* node) noexcept { 346 | _allocator->release(node, sizeof(Node)); 347 | } 348 | 349 | ZoneAllocator* _allocator; 350 | }; 351 | 352 | // -------------------------------------------------------------------------- 353 | // [Construction / Destruction] 354 | // -------------------------------------------------------------------------- 355 | 356 | MPSL_INLINE Set(ZoneAllocator* allocator) noexcept 357 | : _hash(allocator) {} 358 | 359 | MPSL_INLINE ~Set() noexcept { 360 | ReleaseHandler releaseHandler(_hash.allocator()); 361 | _hash.reset(releaseHandler); 362 | } 363 | 364 | // -------------------------------------------------------------------------- 365 | // [Ops] 366 | // -------------------------------------------------------------------------- 367 | 368 | MPSL_INLINE bool has(const Key& key) const noexcept { 369 | uint32_t hashCode = HashUtils::hashPointer(key); 370 | uint32_t hMod = hashCode % _hash._bucketsCount; 371 | Node* node = static_cast(_hash._data[hMod]); 372 | 373 | while (node) { 374 | if (node->eq(key)) 375 | return true; 376 | node = static_cast(node->_next); 377 | } 378 | return false; 379 | } 380 | 381 | MPSL_INLINE Error put(const Key& key) noexcept { 382 | MPSL_ASSERT(!has(key)); 383 | 384 | Node* node = static_cast(_hash._allocator->alloc(sizeof(Node))); 385 | if (node == nullptr) 386 | return MPSL_TRACE_ERROR(kErrorNoMemory); 387 | 388 | uint32_t hashCode = HashUtils::hashPointer(key); 389 | _hash.put(new(node) Node(key, hashCode)); 390 | 391 | return kErrorOk; 392 | } 393 | 394 | MPSL_INLINE bool del(const Key& key) noexcept { 395 | uint32_t hashCode = HashUtils::hashPointer(key); 396 | uint32_t hMod = hashCode % _hash._bucketsCount; 397 | Node* node = static_cast(_hash._data[hMod]); 398 | 399 | while (node) { 400 | if (node->eq(key)) { 401 | _hash._allocator->release(_hash.del(node), sizeof(Node)); 402 | return true; 403 | } 404 | node = static_cast(node->_next); 405 | } 406 | return false; 407 | } 408 | 409 | Hash _hash; 410 | }; 411 | 412 | } // mpsl namespace 413 | 414 | // [Api-End] 415 | #include "./mpsl_apiend.h" 416 | 417 | // [Guard] 418 | #endif // _MPSL_MPHASH_P_H 419 | -------------------------------------------------------------------------------- /src/mpsl/mpir.cpp: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Export] 8 | #define MPSL_EXPORTS 9 | 10 | // [Dependencies - MPSL] 11 | #include "./mpformatutils_p.h" 12 | #include "./mpir_p.h" 13 | #include "./mpmath_p.h" 14 | 15 | // [Api-Begin] 16 | #include "./mpsl_apibegin.h" 17 | 18 | namespace mpsl { 19 | 20 | // ============================================================================ 21 | // [mpsl::IRBlock - Utilities] 22 | // ============================================================================ 23 | 24 | void IRBlock::_fixupAfterNeutering() noexcept { 25 | size_t srcIndex = 0; 26 | size_t dstIndex = 0; 27 | 28 | IRInst** data = _body.data(); 29 | size_t size = static_cast(_body.size()); 30 | 31 | while (srcIndex < size) { 32 | IRInst* inst = data[srcIndex++]; 33 | if (!inst) continue; 34 | data[dstIndex++] = inst; 35 | } 36 | 37 | _body.truncate(dstIndex); 38 | _requiresFixup = false; 39 | } 40 | 41 | // ============================================================================ 42 | // [mpsl::IRBuilder - Construction / Destruction] 43 | // ============================================================================ 44 | 45 | IRBuilder::IRBuilder(ZoneAllocator* allocator, uint32_t numSlots) noexcept 46 | : _allocator(allocator), 47 | _numSlots(numSlots), 48 | _blockIdGen(0), 49 | _varIdGen(0) { 50 | 51 | for (uint32_t i = 0; i < Globals::kMaxArgumentsCount; i++) { 52 | if (i < numSlots) 53 | _dataSlots[i] = newVar(IRReg::kKindGp, kPointerWidth); 54 | else 55 | _dataSlots[i] = nullptr; 56 | } 57 | } 58 | IRBuilder::~IRBuilder() noexcept { 59 | _blocks.release(_allocator); 60 | } 61 | 62 | // ============================================================================ 63 | // [mpsl::IRBuilder - Factory] 64 | // ============================================================================ 65 | 66 | MPSL_INLINE void mpExpandTypeInfo(uint32_t typeInfo, uint32_t& reg, uint32_t& width) noexcept { 67 | uint32_t typeId = typeInfo & kTypeIdMask; 68 | uint32_t vecLen = TypeInfo::elementsOf(typeInfo); 69 | 70 | // Scalar integers are allocated in GP registers. 71 | if (typeId == kTypeInt && vecLen <= 1) { 72 | reg = IRReg::kKindGp; 73 | width = 4; 74 | } 75 | // Everything else is allocated in SIMD registers. 76 | else { 77 | reg = IRReg::kKindVec; 78 | width = TypeInfo::sizeOf(typeId) * vecLen; 79 | } 80 | } 81 | 82 | IRReg* IRBuilder::newVar(uint32_t reg, uint32_t width) noexcept { 83 | IRReg* var = newObject(reg, width); 84 | if (var == nullptr) return nullptr; 85 | 86 | // Assign a variable ID. 87 | var->_id = ++_varIdGen; 88 | 89 | return var; 90 | } 91 | 92 | IRReg* IRBuilder::newVarByTypeInfo(uint32_t typeInfo) noexcept { 93 | uint32_t reg; 94 | uint32_t width; 95 | mpExpandTypeInfo(typeInfo, reg, width); 96 | 97 | return newVar(reg, width); 98 | } 99 | 100 | IRMem* IRBuilder::newMem(IRReg* base, IRReg* index, int32_t offset) noexcept { 101 | IRMem* mem = newObject(base, index, offset); 102 | return mem; 103 | } 104 | 105 | IRImm* IRBuilder::newImm(const Value& value, uint32_t reg, uint32_t width) noexcept { 106 | return newObject(value, reg, width); 107 | } 108 | 109 | IRImm* IRBuilder::newImmByTypeInfo(const Value& value, uint32_t typeInfo) noexcept { 110 | uint32_t reg; 111 | uint32_t width; 112 | mpExpandTypeInfo(typeInfo, reg, width); 113 | 114 | IRImm* imm = newImm(value, reg, width); 115 | if (imm == nullptr) return nullptr; 116 | 117 | imm->setTypeInfo(typeInfo); 118 | return imm; 119 | } 120 | 121 | IRBlock* IRBuilder::newBlock() noexcept { 122 | if (MPSL_UNLIKELY(_blocks.willGrow(_allocator, 1) != kErrorOk)) 123 | return nullptr; 124 | 125 | IRBlock* block = newObject(); 126 | if (MPSL_UNLIKELY(block == nullptr)) 127 | return nullptr; 128 | 129 | // Assign block ID. 130 | block->_id = ++_blockIdGen; 131 | 132 | _blocks.appendUnsafe(block); 133 | return block; 134 | } 135 | 136 | Error IRBuilder::connectBlocks(IRBlock* predecessor, IRBlock* successor) noexcept { 137 | // First check if these blocks are not already connected. 138 | MPSL_ASSERT(!predecessor->_successors.contains(successor)); 139 | MPSL_ASSERT(!successor->_predecessors.contains(predecessor)); 140 | 141 | MPSL_PROPAGATE(predecessor->_successors.willGrow(_allocator, 1)); 142 | MPSL_PROPAGATE(successor->_predecessors.willGrow(_allocator, 1)); 143 | 144 | predecessor->_successors.appendUnsafe(successor); 145 | successor->_predecessors.appendUnsafe(predecessor); 146 | 147 | return kErrorOk; 148 | } 149 | 150 | void IRBuilder::deleteInst(IRInst* inst) noexcept { 151 | IRObject** opArray = inst->operands(); 152 | uint32_t count = inst->opCount(); 153 | 154 | for (uint32_t i = 0; i < count; i++) 155 | derefObject(opArray[i]); 156 | 157 | _allocator->release(inst, sizeof(IRInst)); 158 | } 159 | 160 | void IRBuilder::deleteObject(IRObject* obj) noexcept { 161 | size_t objectSize = 0; 162 | 163 | switch (obj->objectType()) { 164 | case IRObject::kTypeReg: { 165 | objectSize = sizeof(IRReg); 166 | break; 167 | } 168 | 169 | case IRObject::kTypeMem: { 170 | IRMem* mem = obj->as(); 171 | 172 | if (mem->hasBase()) derefObject(mem->base()); 173 | if (mem->hasIndex()) derefObject(mem->index()); 174 | 175 | objectSize = sizeof(IRMem); 176 | break; 177 | } 178 | 179 | case IRObject::kTypeImm: { 180 | objectSize = sizeof(IRImm); 181 | break; 182 | } 183 | 184 | case IRObject::kTypeBlock: { 185 | IRBlock* block = obj->as(); 186 | for (IRInst* inst : block->body()) 187 | if (inst) deleteInst(inst); 188 | 189 | // TODO: Blocks should not be deleted, just marked as removed. 190 | block->_body.release(_allocator); 191 | _blocks[block->id()] = nullptr; 192 | 193 | objectSize = sizeof(IRBlock); 194 | break; 195 | } 196 | 197 | default: 198 | MPSL_ASSERT(!"Reached"); 199 | } 200 | 201 | _allocator->release(obj, objectSize); 202 | } 203 | 204 | // ============================================================================ 205 | // [mpsl::IRBuilder - Init] 206 | // ============================================================================ 207 | 208 | Error IRBuilder::initEntry() noexcept { 209 | MPSL_ASSERT(_blocks.empty()); 210 | 211 | IRBlock* entry = newBlock(); 212 | MPSL_NULLCHECK(entry); 213 | 214 | entry->_blockData._blockType = IRBlock::kKindEntry; 215 | entry->_refCount = 1; 216 | 217 | return kErrorOk; 218 | } 219 | 220 | // ============================================================================ 221 | // [mpsl::IRBuilder - Emit] 222 | // ============================================================================ 223 | 224 | Error IRBuilder::emitInst(IRBlock* block, uint32_t instCode, IRObject* o0) noexcept { 225 | IRInst* node = newInst(instCode, o0); 226 | MPSL_NULLCHECK(node); 227 | return block->append(node); 228 | } 229 | 230 | Error IRBuilder::emitInst(IRBlock* block, uint32_t instCode, IRObject* o0, IRObject* o1) noexcept { 231 | IRInst* node = newInst(instCode, o0, o1); 232 | MPSL_NULLCHECK(node); 233 | return block->append(node); 234 | } 235 | 236 | Error IRBuilder::emitInst(IRBlock* block, uint32_t instCode, IRObject* o0, IRObject* o1, IRObject* o2) noexcept { 237 | IRInst* node = newInst(instCode, o0, o1, o2); 238 | MPSL_NULLCHECK(node); 239 | return block->append(node); 240 | } 241 | 242 | Error IRBuilder::emitMove(IRBlock* block, IRReg* dst, IRReg* src) noexcept { 243 | uint32_t inst = kInstCodeNone; 244 | 245 | switch (mpMin(dst->width(), src->width())) { 246 | case 4: inst = kInstCodeMov32 ; break; 247 | case 8: inst = kInstCodeMov64 ; break; 248 | case 16: inst = kInstCodeMov128; break; 249 | case 32: inst = kInstCodeMov256; break; 250 | 251 | default: 252 | return MPSL_TRACE_ERROR(kErrorInvalidState); 253 | } 254 | 255 | return emitInst(block, inst, dst, src); 256 | } 257 | 258 | Error IRBuilder::emitFetch(IRBlock* block, IRReg* dst, IRObject* src) noexcept { 259 | uint32_t inst = kInstCodeNone; 260 | 261 | if (src->isImm() || src->isMem()) { 262 | switch (dst->width()) { 263 | case 4: inst = kInstCodeFetch32 ; break; 264 | case 8: inst = kInstCodeFetch64 ; break; 265 | case 16: inst = kInstCodeFetch128; break; 266 | case 32: inst = kInstCodeFetch256; break; 267 | } 268 | } 269 | 270 | if (inst == kInstCodeNone) 271 | return MPSL_TRACE_ERROR(kErrorInvalidState); 272 | 273 | return emitInst(block, inst, dst, src); 274 | } 275 | 276 | // ============================================================================ 277 | // [mpsl::IRBuilder - Dump] 278 | // ============================================================================ 279 | 280 | Error IRBuilder::dump(String& sb) noexcept { 281 | for (IRBlock* block : blocks()) { 282 | sb.appendFormat(".B%u\n", block->id()); 283 | for (IRInst* inst : block->body()) { 284 | uint32_t code = inst->instCode() & kInstCodeMask; 285 | uint32_t vec = inst->instCode() & kInstVecMask; 286 | 287 | sb.appendFormat(" %s", mpInstInfo[code].name()); 288 | 289 | if (vec == kInstVec128) sb.appendString("@128"); 290 | if (vec == kInstVec256) sb.appendString("@256"); 291 | 292 | IRObject** opArray = inst->operands(); 293 | size_t opIndex, count = inst->opCount(); 294 | 295 | for (opIndex = 0; opIndex < count; opIndex++) { 296 | IRObject* op = opArray[opIndex]; 297 | 298 | if (opIndex == 0) 299 | sb.appendString(" "); 300 | else 301 | sb.appendString(", "); 302 | 303 | switch (op->objectType()) { 304 | case IRObject::kTypeReg: { 305 | IRReg* var = static_cast(op); 306 | sb.appendFormat("%%%u", var->id()); 307 | break; 308 | } 309 | 310 | case IRObject::kTypeMem: { 311 | IRMem* mem = static_cast(op); 312 | sb.appendFormat("[%%%u + %d]", 313 | mem->base()->id(), 314 | static_cast(mem->offset())); 315 | break; 316 | } 317 | 318 | case IRObject::kTypeImm: { 319 | IRImm* imm = static_cast(op); 320 | FormatUtils::formatValue(sb, imm->typeInfo(), &imm->_value); 321 | break; 322 | } 323 | 324 | case IRObject::kTypeBlock: { 325 | IRBlock* block = static_cast(op); 326 | sb.appendFormat("B%u", block->id()); 327 | break; 328 | } 329 | } 330 | } 331 | 332 | sb.appendString("\n"); 333 | } 334 | } 335 | 336 | return kErrorOk; 337 | } 338 | 339 | } // mpsl namespace 340 | 341 | // [Api-End] 342 | #include "./mpsl_apiend.h" 343 | -------------------------------------------------------------------------------- /src/mpsl/mpirpass.cpp: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Export] 8 | #define MPSL_EXPORTS 9 | 10 | // [Dependencies - MPSL] 11 | #include "./mpirpass_p.h" 12 | 13 | // [Api-Begin] 14 | #include "./mpsl_apibegin.h" 15 | 16 | namespace mpsl { 17 | 18 | static Error mpIRPassBlock(IRBuilder* ir, IRBlock* block) noexcept { 19 | IRBody& body = block->body(); 20 | size_t i = body.size(); 21 | 22 | while (i != 0) { 23 | IRInst* inst = body[--i]; 24 | if (inst) { 25 | // TODO: Just testing some concepts. 26 | const InstInfo& info = mpInstInfo[inst->instCode() & kInstCodeMask]; 27 | if (!info.isStore() && !info.isCall() && !info.isRet()) { 28 | IRObject** opArray = inst->operands(); 29 | uint32_t opCount = inst->opCount(); 30 | 31 | IRObject* dst = opArray[0]; 32 | if (dst->isReg() && dst->refCount() == 1) { 33 | block->neuterAt(i); 34 | ir->deleteInst(inst); 35 | } 36 | } 37 | } 38 | } 39 | 40 | block->fixupAfterNeutering(); 41 | return kErrorOk; 42 | } 43 | 44 | Error mpIRPass(IRBuilder* ir) noexcept { 45 | for (IRBlock* block : ir->blocks()) 46 | MPSL_PROPAGATE(mpIRPassBlock(ir, block)); 47 | return kErrorOk; 48 | } 49 | 50 | } // mpsl namespace 51 | 52 | // [Api-End] 53 | #include "./mpsl_apiend.h" 54 | -------------------------------------------------------------------------------- /src/mpsl/mpirpass_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPIRPASS_P_H 9 | #define _MPSL_MPIRPASS_P_H 10 | 11 | // [Dependencies - MPSL] 12 | #include "./mpir_p.h" 13 | #include "./mplang_p.h" 14 | 15 | // [Api-Begin] 16 | #include "./mpsl_apibegin.h" 17 | 18 | namespace mpsl { 19 | 20 | Error mpIRPass(IRBuilder* ir) noexcept; 21 | 22 | } // mpsl namespace 23 | 24 | // [Api-End] 25 | #include "./mpsl_apiend.h" 26 | 27 | // [Guard] 28 | #endif // _MPSL_MPIRPASS_P_H 29 | -------------------------------------------------------------------------------- /src/mpsl/mpirtox86_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPIRTOX86_P_H 9 | #define _MPSL_MPIRTOX86_P_H 10 | 11 | // [Dependencies - MPSL] 12 | #include "./mpast_p.h" 13 | #include "./mphash_p.h" 14 | #include "./mpir_p.h" 15 | 16 | // [Api-Begin] 17 | #include "./mpsl_apibegin.h" 18 | 19 | namespace mpsl { 20 | 21 | namespace x86 = asmjit::x86; 22 | 23 | using asmjit::BaseNode; 24 | using asmjit::ConstPool; 25 | using asmjit::Label; 26 | using asmjit::Operand; 27 | 28 | // ============================================================================ 29 | // [mpsl::IRToX86] 30 | // ============================================================================ 31 | 32 | class IRToX86 { 33 | public: 34 | MPSL_NONCOPYABLE(IRToX86) 35 | 36 | // -------------------------------------------------------------------------- 37 | // [Construction / Destruction] 38 | // -------------------------------------------------------------------------- 39 | 40 | IRToX86(ZoneAllocator* allocator, x86::Compiler* cc); 41 | ~IRToX86(); 42 | 43 | // -------------------------------------------------------------------------- 44 | // [Const Pool] 45 | // -------------------------------------------------------------------------- 46 | 47 | void prepareConstPool(); 48 | 49 | x86::Mem getConstantU64(uint64_t value); 50 | x86::Mem getConstantU64AsPD(uint64_t value); 51 | x86::Mem getConstantD64(double value); 52 | x86::Mem getConstantD64AsPD(double value); 53 | x86::Mem getConstantByValue(const Value& value, uint32_t width); 54 | 55 | // -------------------------------------------------------------------------- 56 | // [Compile] 57 | // -------------------------------------------------------------------------- 58 | 59 | Error compileIRAsFunc(IRBuilder* ir); 60 | Error compileIRAsPart(IRBuilder* ir); 61 | Error compileConsecutiveBlocks(IRBlock* block); 62 | Error compileBasicBlock(IRBlock* block, IRBlock* next); 63 | 64 | MPSL_INLINE void emit2x(uint32_t instId, const Operand& o0, const Operand& o1) { _cc->emit(instId, o0, o1); } 65 | void emit3i(uint32_t instId, const Operand& o0, const Operand& o1, const Operand& o2); 66 | void emit3f(uint32_t instId, const Operand& o0, const Operand& o1, const Operand& o2); 67 | void emit3f(uint32_t instId, const Operand& o0, const Operand& o1, const Operand& o2, int imm); 68 | void emit3d(uint32_t instId, const Operand& o0, const Operand& o1, const Operand& o2); 69 | void emit3d(uint32_t instId, const Operand& o0, const Operand& o1, const Operand& o2, int imm); 70 | 71 | x86::Gp varAsPtr(IRReg* irVar); 72 | x86::Gp varAsI32(IRReg* irVar); 73 | x86::Xmm varAsXmm(IRReg* irVar); 74 | 75 | // -------------------------------------------------------------------------- 76 | // [Members] 77 | // -------------------------------------------------------------------------- 78 | 79 | ZoneAllocator* _allocator; 80 | x86::Compiler* _cc; 81 | 82 | x86::Gp _data[Globals::kMaxArgumentsCount]; 83 | x86::Gp _ret; 84 | 85 | BaseNode* _functionBody; 86 | ConstPool _constPool; 87 | Label _constLabel; 88 | x86::Gp _constPtr; 89 | 90 | x86::Xmm _tmpXmm0; 91 | x86::Xmm _tmpXmm1; 92 | 93 | bool _enableSSE4_1; 94 | }; 95 | 96 | } // mpsl namespace 97 | 98 | // [Api-End] 99 | #include "./mpsl_apiend.h" 100 | 101 | // [Guard] 102 | #endif // _MPSL_MPIRTOX86_P_H 103 | -------------------------------------------------------------------------------- /src/mpsl/mpmath.cpp: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Export] 8 | #define MPSL_EXPORTS 9 | 10 | // [Dependencies - MPSL] 11 | #include "./mpmath_p.h" 12 | 13 | // [Api-Begin] 14 | #include "./mpsl_apibegin.h" 15 | 16 | namespace mpsl { 17 | 18 | 19 | 20 | } // mpsl namespace 21 | 22 | // [Api-End] 23 | #include "./mpsl_apiend.h" 24 | -------------------------------------------------------------------------------- /src/mpsl/mpmath_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPMATH_P_H 9 | #define _MPSL_MPMATH_P_H 10 | 11 | // [Dependencies - MPSL] 12 | #include "./mpsl_p.h" 13 | 14 | // [Dependencies - C] 15 | #include 16 | 17 | // [Api-Begin] 18 | #include "./mpsl_apibegin.h" 19 | 20 | namespace mpsl { 21 | 22 | // ============================================================================ 23 | // [mpsl::FloatBits] 24 | // ============================================================================ 25 | 26 | //! Single-precision floating point and its binary representation. 27 | union FloatBits { 28 | // -------------------------------------------------------------------------- 29 | // [Construction / Destruction] 30 | // -------------------------------------------------------------------------- 31 | 32 | static MPSL_INLINE FloatBits fromFloat(float val) noexcept { FloatBits u; u.f = val; return u; } 33 | static MPSL_INLINE FloatBits fromUInt(uint32_t val) noexcept { FloatBits u; u.u = val; return u; } 34 | 35 | // -------------------------------------------------------------------------- 36 | // [Utilities] 37 | // -------------------------------------------------------------------------- 38 | 39 | MPSL_INLINE bool isNan() const noexcept { return (u & 0x7FFFFFFFu) > 0x7F800000u; } 40 | MPSL_INLINE bool isInf() const noexcept { return (u & 0x7FFFFFFFu) == 0x7F800000u; } 41 | MPSL_INLINE bool isFinite() const noexcept { return (u & 0x7F800000u) != 0x7F800000u; } 42 | 43 | MPSL_INLINE FloatBits& setNan() noexcept { u = 0x7FC00000u; return *this; } 44 | MPSL_INLINE FloatBits& setInf() noexcept { u = 0x7F800000u; return *this; } 45 | 46 | MPSL_INLINE FloatBits& invSign() noexcept { u ^= 0x80000000u; return *this; } 47 | MPSL_INLINE FloatBits& setSign() noexcept { u |= 0x80000000u; return *this; } 48 | MPSL_INLINE FloatBits& clearSign() noexcept { u &= ~0x80000000u; return *this; } 49 | 50 | // -------------------------------------------------------------------------- 51 | // [Members] 52 | // -------------------------------------------------------------------------- 53 | 54 | //! Value as `uint32_t`. 55 | uint32_t u; 56 | //! Value as `float`. 57 | float f; 58 | }; 59 | 60 | // ============================================================================ 61 | // [mpsl::DoubleBits] 62 | // ============================================================================ 63 | 64 | //! Double-precision floating point and its binary representation. 65 | union DoubleBits { 66 | // -------------------------------------------------------------------------- 67 | // [Construction / Destruction] 68 | // -------------------------------------------------------------------------- 69 | 70 | static MPSL_INLINE DoubleBits fromDouble(double val) noexcept { DoubleBits u; u.d = val; return u; } 71 | static MPSL_INLINE DoubleBits fromUInt64(uint64_t val) noexcept { DoubleBits u; u.u = val; return u; } 72 | 73 | // -------------------------------------------------------------------------- 74 | // [Utilities] 75 | // -------------------------------------------------------------------------- 76 | 77 | MPSL_INLINE bool isNan() const noexcept { 78 | if (MPSL_ARCH_64BIT) 79 | return (u & 0x7FFFFFFFFFFFFFFFu) > 0x7F80000000000000u; 80 | else 81 | return ((hi & 0x7FF00000u)) == 0x7FF00000u && ((hi & 0x000FFFFFu) | lo) != 0x00000000u; 82 | } 83 | 84 | MPSL_INLINE bool isInf() const noexcept { 85 | if (MPSL_ARCH_64BIT) 86 | return (u & 0x7FFFFFFFFFFFFFFFu) == 0x7F80000000000000u; 87 | else 88 | return (hi & 0x7FFFFFFFu) == 0x7FF00000u && lo == 0x00000000u; 89 | } 90 | 91 | MPSL_INLINE bool isFinite() const noexcept { 92 | if (MPSL_ARCH_64BIT) 93 | return (u & 0x7FF0000000000000u) != 0x7FF0000000000000u; 94 | else 95 | return (hi & 0x7FF00000u) != 0x7FF00000u; 96 | } 97 | 98 | MPSL_INLINE DoubleBits& setNan() noexcept { u = 0x7FF8000000000000u; return *this; } 99 | MPSL_INLINE DoubleBits& setInf() noexcept { u = 0x7FF0000000000000u; return *this; } 100 | 101 | MPSL_INLINE DoubleBits& invSign() noexcept { hi ^= 0x80000000u; return *this; } 102 | MPSL_INLINE DoubleBits& setSign() noexcept { hi |= 0x80000000u; return *this; } 103 | MPSL_INLINE DoubleBits& clearSign() noexcept { hi&= ~0x80000000u; return *this; } 104 | 105 | // -------------------------------------------------------------------------- 106 | // [Members] 107 | // -------------------------------------------------------------------------- 108 | 109 | //! Value as `uint64_t`. 110 | uint64_t u; 111 | //! Value as `double`. 112 | double d; 113 | 114 | #if MPSL_ARCH_LE 115 | struct { uint32_t lo; uint32_t hi; }; 116 | #else 117 | struct { uint32_t hi; uint32_t lo; }; 118 | #endif 119 | }; 120 | 121 | // ============================================================================ 122 | // [mpsl::Math - Min / Max] 123 | // ============================================================================ 124 | 125 | // The `a != a` condition is used to handle NaN values properly. If one of `a` 126 | // and `b` is NaN the result should be NaN. When `T` isn't a floating point the 127 | // condition should be removed by C++ compiler. 128 | template MPSL_INLINE T mpMin(T a, T b) noexcept { return (a != a) | (a < b) ? a : b; } 129 | template MPSL_INLINE T mpMax(T a, T b) noexcept { return (a != a) | (a > b) ? a : b; } 130 | 131 | template MPSL_INLINE T mpBound(T x, T xMin, T xMax) noexcept { 132 | return x < xMin ? xMin : 133 | x > xMax ? xMax : x; 134 | } 135 | 136 | // ============================================================================ 137 | // [mpsl::Math - NaN / Inf] 138 | // ============================================================================ 139 | 140 | static MPSL_INLINE float mpGetNanF() noexcept { static const FloatBits value = { 0x7FC00000u }; return value.f; } 141 | static MPSL_INLINE float mpGetInfF() noexcept { static const FloatBits value = { 0x7F800000u }; return value.f; } 142 | 143 | static MPSL_INLINE double mpGetNanD() noexcept { static const DoubleBits value = { 0x7FF8000000000000u }; return value.d; } 144 | static MPSL_INLINE double mpGetInfD() noexcept { static const DoubleBits value = { 0x7FF0000000000000u }; return value.d; } 145 | 146 | static MPSL_INLINE bool mpIsNanF(float x) noexcept { return FloatBits::fromFloat(x).isNan(); } 147 | static MPSL_INLINE bool mpIsInfF(float x) noexcept { return FloatBits::fromFloat(x).isInf(); } 148 | static MPSL_INLINE bool mpIsFiniteF(float x) noexcept { return FloatBits::fromFloat(x).isFinite(); } 149 | 150 | static MPSL_INLINE bool mpIsNanD(double x) noexcept { return DoubleBits::fromDouble(x).isNan(); } 151 | static MPSL_INLINE bool mpIsInfD(double x) noexcept { return DoubleBits::fromDouble(x).isInf(); } 152 | static MPSL_INLINE bool mpIsFiniteD(double x) noexcept { return DoubleBits::fromDouble(x).isFinite(); } 153 | 154 | // ============================================================================ 155 | // [mpsl::Math - Normalize] 156 | // ============================================================================ 157 | 158 | static MPSL_INLINE float mpNormalizeF(float x) noexcept { return x + static_cast(0); } 159 | static MPSL_INLINE double mpNormalizeD(double x) noexcept { return x + static_cast(0); } 160 | 161 | // ============================================================================ 162 | // [mpsl::Math - Abs] 163 | // ============================================================================ 164 | 165 | static MPSL_INLINE int32_t mpAbsI(int32_t x) noexcept { 166 | // To support `mpAbsI(-2147483648)` returning `-2147483648` we have 167 | // to cast to an unsigned integer to prevent "undefined behavior". 168 | uint32_t mask = static_cast(-static_cast(x < 0)); 169 | return static_cast((static_cast(x) ^ mask) - mask); 170 | } 171 | 172 | static MPSL_INLINE float mpAbsF(float x) noexcept { return FloatBits::fromFloat(x).clearSign().f; } 173 | static MPSL_INLINE double mpAbsD(double x) noexcept { return DoubleBits::fromDouble(x).clearSign().d; } 174 | 175 | // ============================================================================ 176 | // [mpsl::Math - Neg] 177 | // ============================================================================ 178 | 179 | static MPSL_INLINE float mpNegF(float x) noexcept { return FloatBits::fromFloat(x).invSign().f; } 180 | static MPSL_INLINE double mpNegD(double x) noexcept { return DoubleBits::fromDouble(x).invSign().d; } 181 | 182 | // ============================================================================ 183 | // [mpsl::Math - Round] 184 | // ============================================================================ 185 | 186 | static MPSL_INLINE float mpTruncF(float x) noexcept { return ::truncf(x); } 187 | static MPSL_INLINE float mpFloorF(float x) noexcept { return ::floorf(x); } 188 | static MPSL_INLINE float mpCeilF(float x) noexcept { return ::ceilf(x); } 189 | 190 | static MPSL_INLINE double mpTruncD(double x) noexcept { return ::trunc(x); } 191 | static MPSL_INLINE double mpFloorD(double x) noexcept { return ::floor(x); } 192 | static MPSL_INLINE double mpCeilD(double x) noexcept { return ::ceil(x); } 193 | 194 | static MPSL_INLINE float mpRoundF(float x) noexcept { 195 | float y = mpFloorF(x); 196 | return y + (x - y >= 0.5f ? 1.0f : 0.0f); 197 | } 198 | 199 | static MPSL_INLINE double mpRoundD(double x) noexcept { 200 | double y = mpFloorD(x); 201 | return y + (x - y >= 0.5 ? 1.0 : 0.0); 202 | } 203 | 204 | static MPSL_INLINE float mpRoundEvenF(float x) noexcept { return ::rintf(x); } 205 | static MPSL_INLINE double mpRoundEvenD(double x) noexcept { return ::rint(x); } 206 | 207 | // ============================================================================ 208 | // [mpsl::Math - SignBit] 209 | // ============================================================================ 210 | 211 | static MPSL_INLINE bool mpSignBitF(float x) noexcept { 212 | return FloatBits::fromFloat(x).u >= 0x80000000u; 213 | } 214 | 215 | static MPSL_INLINE bool mpSignBitD(double x) noexcept { 216 | return DoubleBits::fromDouble(x).hi >= 0x80000000u; 217 | } 218 | 219 | static MPSL_INLINE float mpCopySignF(float x, float y) noexcept { 220 | FloatBits bits = FloatBits::fromFloat(x); 221 | bits.u &= 0x7FFFFFFFu; 222 | bits.u |= FloatBits::fromFloat(y).u & 0x80000000u; 223 | return bits.f; 224 | } 225 | 226 | static MPSL_INLINE double mpCopySignD(double x, double y) noexcept { 227 | DoubleBits bits = DoubleBits::fromDouble(x); 228 | bits.hi &= 0x7FFFFFFFu; 229 | bits.hi |= DoubleBits::fromDouble(y).hi & 0x80000000u; 230 | return bits.d; 231 | } 232 | 233 | // ============================================================================ 234 | // [mpsl::Math - Fraction] 235 | // ============================================================================ 236 | 237 | static MPSL_INLINE float mpFracF(float x) noexcept { return x - mpFloorF(x); } 238 | static MPSL_INLINE double mpFracD(double x) noexcept { return x - mpFloorD(x); } 239 | 240 | // ============================================================================ 241 | // [mpsl::Math - Modulo / Remainder] 242 | // ============================================================================ 243 | 244 | static MPSL_INLINE float mpModF(float x, float y) noexcept { return fmodf(x, y); } 245 | static MPSL_INLINE double mpModD(double x, double y) noexcept { return fmod(x, y); } 246 | 247 | // ============================================================================ 248 | // [mpsl::Math - Sqrt] 249 | // ============================================================================ 250 | 251 | static MPSL_INLINE float mpSqrtF(float x) noexcept { return ::sqrtf(x); } 252 | static MPSL_INLINE double mpSqrtD(double x) noexcept { return ::sqrt(x); } 253 | 254 | // ============================================================================ 255 | // [mpsl::Math - Power] 256 | // ============================================================================ 257 | 258 | static MPSL_INLINE float mpPowF(float x, float y) noexcept { return ::powf(x, y); } 259 | static MPSL_INLINE double mpPowD(double x, double y) noexcept { return ::pow(x, y); } 260 | 261 | // ============================================================================ 262 | // [mpsl::Math - Exponential] 263 | // ============================================================================ 264 | 265 | static MPSL_INLINE float mpExpF(float x) noexcept { return ::expf(x); } 266 | static MPSL_INLINE double mpExpD(double x) noexcept { return ::exp(x); } 267 | 268 | // ============================================================================ 269 | // [mpsl::Math - Logarithm] 270 | // ============================================================================ 271 | 272 | static MPSL_INLINE float mpLogF(float x) noexcept { return ::logf(x); } 273 | static MPSL_INLINE double mpLogD(double x) noexcept { return ::log(x); } 274 | 275 | static MPSL_INLINE float mpLog2F(float x) noexcept { return ::log2f(x); } 276 | static MPSL_INLINE double mpLog2D(double x) noexcept { return ::log2(x); } 277 | 278 | static MPSL_INLINE float mpLog10F(float x) noexcept { return ::log10f(x); } 279 | static MPSL_INLINE double mpLog10D(double x) noexcept { return ::log10(x); } 280 | 281 | // ============================================================================ 282 | // [mpsl::Math - Trigonometric] 283 | // ============================================================================ 284 | 285 | static MPSL_INLINE float mpSinF(float x) noexcept { return ::sinf(x); } 286 | static MPSL_INLINE double mpSinD(double x) noexcept { return ::sin(x); } 287 | 288 | static MPSL_INLINE float mpCosF(float x) noexcept { return ::cosf(x); } 289 | static MPSL_INLINE double mpCosD(double x) noexcept { return ::cos(x); } 290 | 291 | static MPSL_INLINE float mpTanF(float x) noexcept { return ::tanf(x); } 292 | static MPSL_INLINE double mpTanD(double x) noexcept { return ::tan(x); } 293 | 294 | static MPSL_INLINE float mpAsinF(float x) noexcept { return ::asinf(x); } 295 | static MPSL_INLINE double mpAsinD(double x) noexcept { return ::asin(x); } 296 | 297 | static MPSL_INLINE float mpAcosF(float x) noexcept { return ::acosf(x); } 298 | static MPSL_INLINE double mpAcosD(double x) noexcept { return ::acos(x); } 299 | 300 | static MPSL_INLINE float mpAtanF(float x) noexcept { return ::atanf(x); } 301 | static MPSL_INLINE double mpAtanD(double x) noexcept { return ::atan(x); } 302 | 303 | static MPSL_INLINE float mpAtan2F(float x, float y) noexcept { return ::atan2f(x, y); } 304 | static MPSL_INLINE double mpAtan2D(double x, double y) noexcept { return ::atan2(x, y); } 305 | 306 | } // mpsl namespace 307 | 308 | // [Api-End] 309 | #include "./mpsl_apiend.h" 310 | 311 | // [Guard] 312 | #endif // _MPSL_MPMATH_P_H 313 | -------------------------------------------------------------------------------- /src/mpsl/mpparser_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPPARSER_P_H 9 | #define _MPSL_MPPARSER_P_H 10 | 11 | // [Dependencies - MPSL] 12 | #include "./mpast_p.h" 13 | #include "./mptokenizer_p.h" 14 | 15 | // [Api-Begin] 16 | #include "./mpsl_apibegin.h" 17 | 18 | namespace mpsl { 19 | 20 | // ============================================================================ 21 | // [Forward Declaration] 22 | // ============================================================================ 23 | 24 | class AstBuilder; 25 | class AstBlock; 26 | class AstNode; 27 | class AstProgram; 28 | class AstVar; 29 | 30 | // ============================================================================ 31 | // [mpsl::Parser] 32 | // ============================================================================ 33 | 34 | class Parser { 35 | public: 36 | MPSL_NONCOPYABLE(Parser) 37 | 38 | enum Flags { 39 | kNoFlags = 0x00, 40 | kEnableNewSymbols = 0x01, 41 | kEnableNestedBlock = 0x02 42 | }; 43 | 44 | // -------------------------------------------------------------------------- 45 | // [Construction / Destruction] 46 | // -------------------------------------------------------------------------- 47 | 48 | MPSL_INLINE Parser(AstBuilder* ast, ErrorReporter* errorReporter, const char* body, size_t size) noexcept 49 | : _ast(ast), 50 | _errorReporter(errorReporter), 51 | _currentScope(ast->globalScope()), 52 | _tokenizer(body, size) {} 53 | MPSL_INLINE ~Parser() noexcept {} 54 | 55 | // -------------------------------------------------------------------------- 56 | // [Accessors] 57 | // -------------------------------------------------------------------------- 58 | 59 | MPSL_INLINE AstScope* currentScope() const noexcept { return _currentScope; } 60 | 61 | // -------------------------------------------------------------------------- 62 | // [Parse] 63 | // -------------------------------------------------------------------------- 64 | 65 | Error parseProgram(AstProgram* block) noexcept; 66 | Error parseFunction(AstProgram* block) noexcept; 67 | 68 | Error parseStatement(AstBlock* block, uint32_t flags) noexcept; 69 | Error parseBlockOrStatement(AstBlock* block) noexcept; 70 | 71 | Error parseTypedef(AstBlock* block) noexcept; 72 | Error parseVarDecl(AstBlock* block) noexcept; 73 | Error parseIfElse(AstBlock* block) noexcept; 74 | 75 | Error parseFor(AstBlock* block) noexcept; 76 | Error parseWhile(AstBlock* block) noexcept; 77 | Error parseDoWhile(AstBlock* block) noexcept; 78 | 79 | Error parseBreak(AstBlock* block) noexcept; 80 | Error parseContinue(AstBlock* block) noexcept; 81 | Error parseReturn(AstBlock* block) noexcept; 82 | 83 | Error parseExpression(AstNode** pNodeOut) noexcept; 84 | Error parseVariable(AstVar** pNodeOut) noexcept; 85 | Error parseCall(AstCall** pNodeOut) noexcept; 86 | 87 | // -------------------------------------------------------------------------- 88 | // [Members] 89 | // -------------------------------------------------------------------------- 90 | 91 | AstBuilder* _ast; 92 | ErrorReporter* _errorReporter; 93 | 94 | AstScope* _currentScope; 95 | Tokenizer _tokenizer; 96 | }; 97 | 98 | } // mpsl namespace 99 | 100 | // [Api-End] 101 | #include "./mpsl_apiend.h" 102 | 103 | // [Guard] 104 | #endif // _MPSL_MPPARSER_P_H 105 | -------------------------------------------------------------------------------- /src/mpsl/mpsl.cpp: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Export] 8 | #define MPSL_EXPORTS 9 | 10 | // [Dependencies - MPSL] 11 | #include "./mpast_p.h" 12 | #include "./mpastoptimizer_p.h" 13 | #include "./mpcodegen_p.h" 14 | #include "./mpatomic_p.h" 15 | #include "./mpformatutils_p.h" 16 | #include "./mpir_p.h" 17 | #include "./mpirpass_p.h" 18 | #include "./mpirtox86_p.h" 19 | #include "./mplang_p.h" 20 | #include "./mpparser_p.h" 21 | 22 | // [Api-Begin] 23 | #include "./mpsl_apibegin.h" 24 | 25 | namespace mpsl { 26 | 27 | // ============================================================================ 28 | // [mpsl::mpAssertionFailed] 29 | // ============================================================================ 30 | 31 | void mpAssertionFailed(const char* exp, const char* file, int line) noexcept { 32 | ::fprintf(stderr, "Assertion failed: %s\n, file %s, line %d\n", exp, file, line); 33 | ::abort(); 34 | } 35 | 36 | // ============================================================================ 37 | // [mpsl::mpTraceError] 38 | // ============================================================================ 39 | 40 | Error mpTraceError(Error error) noexcept { 41 | return error; 42 | } 43 | 44 | // ============================================================================ 45 | // [mpsl::Helpers] 46 | // ============================================================================ 47 | 48 | template 49 | MPSL_INLINE T* mpObjectAddRef(T* self) noexcept { 50 | if (self->_refCount == 0) 51 | return self; 52 | 53 | mpAtomicInc(&self->_refCount); 54 | return self; 55 | } 56 | 57 | template 58 | MPSL_INLINE void mpObjectRelease(T* self) noexcept { 59 | if (self->_refCount != 0 && !mpAtomicDec(&self->_refCount)) 60 | self->destroy(); 61 | } 62 | 63 | // Declared in public "mpsl.h" header. 64 | MPSL_INLINE void Context::Impl::destroy() noexcept { 65 | RuntimeData* rt = static_cast(_runtimeData); 66 | 67 | mpObjectRelease(rt); 68 | ::free(this); 69 | } 70 | 71 | MPSL_INLINE void Program::Impl::destroy() noexcept { 72 | RuntimeData* rt = static_cast(_runtimeData); 73 | rt->_runtime.release(_main); 74 | 75 | mpObjectRelease(rt); 76 | ::free(this); 77 | } 78 | 79 | // ============================================================================ 80 | // [mpsl::Layout - Internal] 81 | // ============================================================================ 82 | 83 | static MPSL_INLINE bool mpLayoutIsEmbedded(Layout* self, const uint8_t* data) noexcept { 84 | return self->_embeddedData == data; 85 | } 86 | 87 | static MPSL_INLINE uint32_t mpLayoutGetRemainingSize(const Layout* self) noexcept { 88 | return self->_dataIndex - static_cast(self->_membersCount * sizeof(Layout::Member)); 89 | } 90 | 91 | static MPSL_INLINE Layout::Member* mpLayoutFind( 92 | const Layout* self, const char* name, size_t size) noexcept { 93 | 94 | Layout::Member* m = self->_members; 95 | uint32_t count = self->_membersCount; 96 | 97 | for (uint32_t i = 0; i < count; i++) 98 | if (m[i].nameSize == size && (size == 0 || ::memcmp(m[i].name, name, size) == 0)) 99 | return &m[i]; 100 | 101 | return nullptr; 102 | } 103 | 104 | static MPSL_NOINLINE Error mpLayoutResize(Layout* self, uint32_t dataSize) noexcept { 105 | uint8_t* oldData = self->_data; 106 | uint8_t* newData = static_cast(::malloc(dataSize)); 107 | 108 | if (newData == nullptr) 109 | return MPSL_TRACE_ERROR(kErrorNoMemory); 110 | 111 | uint32_t dataIndex = dataSize; 112 | uint32_t count = self->_membersCount; 113 | 114 | Layout::Member* oldMember = reinterpret_cast(oldData); 115 | Layout::Member* newMember = reinterpret_cast(newData); 116 | 117 | uint32_t size = self->_nameSize; 118 | if (size != 0) { 119 | dataIndex -= ++size; 120 | 121 | ::memcpy(newData + dataIndex, self->_name, size); 122 | self->_name = reinterpret_cast(newData + dataIndex); 123 | } 124 | 125 | for (uint32_t i = 0; i < count; i++, oldMember++, newMember++) { 126 | size = oldMember->nameSize; 127 | newMember->nameSize = size; 128 | MPSL_ASSERT(size < dataIndex); 129 | 130 | dataIndex -= ++size; 131 | MPSL_ASSERT(dataIndex >= (i + 1) * sizeof(Layout::Member)); 132 | 133 | ::memcpy(newData + dataIndex, oldMember->name, size); 134 | newMember->name = reinterpret_cast(newData + dataIndex); 135 | newMember->typeInfo = oldMember->typeInfo; 136 | newMember->offset = oldMember->offset; 137 | } 138 | 139 | self->_data = newData; 140 | self->_dataSize = dataSize; 141 | self->_dataIndex = dataIndex; 142 | 143 | if (oldData && !mpLayoutIsEmbedded(self, oldData)) 144 | ::free(oldData); 145 | 146 | return kErrorOk; 147 | } 148 | 149 | static MPSL_INLINE Error mpLayoutPrepareAdd(Layout* self, size_t n) noexcept { 150 | size_t remainingSize = mpLayoutGetRemainingSize(self); 151 | if (remainingSize < n) { 152 | uint32_t newSize = self->_dataSize; 153 | // Make sure we reserve much more than the current size if it wasn't enough. 154 | if (newSize <= 128) 155 | newSize = 512; 156 | else 157 | newSize *= 2; 158 | MPSL_PROPAGATE(mpLayoutResize(self, newSize)); 159 | } 160 | return kErrorOk; 161 | } 162 | 163 | // ============================================================================ 164 | // [mpsl::Layout - Construction / Destruction] 165 | // ============================================================================ 166 | 167 | Layout::Layout() noexcept 168 | : _data(nullptr), 169 | _name(nullptr), 170 | _nameSize(0), 171 | _membersCount(0), 172 | _dataSize(0), 173 | _dataIndex(0) {} 174 | 175 | Layout::Layout(uint8_t* data, uint32_t dataSize) noexcept 176 | : _data(data), 177 | _name(nullptr), 178 | _nameSize(0), 179 | _membersCount(0), 180 | _dataSize(dataSize), 181 | _dataIndex(dataSize) {} 182 | 183 | Layout::~Layout() noexcept { 184 | uint8_t* data = _data; 185 | 186 | if (data && !mpLayoutIsEmbedded(this, data)) 187 | ::free(data); 188 | } 189 | 190 | // ============================================================================ 191 | // [mpsl::Layout - Interface] 192 | // ============================================================================ 193 | 194 | Error Layout::_configure(const char* name, size_t size) noexcept { 195 | if (size == Globals::kInvalidIndex) 196 | size = name ? ::strlen(name) : (size_t)0; 197 | 198 | if (size > Globals::kMaxIdentifierLength) 199 | return MPSL_TRACE_ERROR(kErrorInvalidArgument); 200 | 201 | if (_name) 202 | return MPSL_TRACE_ERROR(kErrorAlreadyConfigured); 203 | 204 | size++; // Include NULL terminator. 205 | MPSL_PROPAGATE(mpLayoutPrepareAdd(this, size)); 206 | 207 | uint32_t dataIndex = _dataIndex - static_cast(size); 208 | ::memcpy(_data + dataIndex, name, size); 209 | 210 | _name = reinterpret_cast(_data + dataIndex); 211 | // Casting to `uint32_t` is safe, see `kMaxIdentifierLength`, store size 212 | // without a NULL terminator (hence `-1`). 213 | _nameSize = static_cast(size - 1); 214 | 215 | _dataIndex = dataIndex; 216 | return kErrorOk; 217 | } 218 | 219 | const Layout::Member* Layout::_get(const char* name, size_t size) const noexcept { 220 | if (name == nullptr) 221 | return nullptr; 222 | 223 | if (size == Globals::kInvalidIndex) 224 | size = ::strlen(name); 225 | 226 | return mpLayoutFind(this, name, size); 227 | } 228 | 229 | Error Layout::_add(const char* name, size_t size, uint32_t typeInfo, int32_t offset) noexcept { 230 | if (name == nullptr) 231 | return MPSL_TRACE_ERROR(kErrorInvalidArgument); 232 | 233 | if (size == Globals::kInvalidIndex) 234 | size = ::strlen(name); 235 | 236 | if (size > Globals::kMaxIdentifierLength) 237 | return MPSL_TRACE_ERROR(kErrorInvalidArgument); 238 | 239 | uint32_t count = _membersCount; 240 | if (count >= Globals::kMaxMembersCount) 241 | return MPSL_TRACE_ERROR(kErrorTooManyMembers); 242 | 243 | Member* member = mpLayoutFind(this, name, size); 244 | if (member) 245 | return MPSL_TRACE_ERROR(kErrorAlreadyExists); 246 | 247 | MPSL_PROPAGATE(mpLayoutPrepareAdd(this, size + 1 + static_cast(sizeof(Member)))); 248 | member = _members + count; 249 | uint32_t dataIndex = _dataIndex - (static_cast(size) + 1); 250 | 251 | ::memcpy(_data + dataIndex, name, size + 1); 252 | member->name = reinterpret_cast(_data + dataIndex); 253 | member->nameSize = static_cast(size); 254 | member->typeInfo = typeInfo; 255 | member->offset = offset; 256 | 257 | _membersCount++; 258 | _dataIndex = dataIndex; 259 | return kErrorOk; 260 | } 261 | 262 | // ============================================================================ 263 | // [mpsl::Context - Construction / Destruction] 264 | // ============================================================================ 265 | 266 | static const Context::Impl mpContextNull = { 0, nullptr }; 267 | 268 | Context::Context() noexcept 269 | : _d(const_cast(&mpContextNull)) {} 270 | 271 | Context::Context(const Context& other) noexcept 272 | : _d(mpObjectAddRef(other._d)) {} 273 | 274 | Context::~Context() noexcept { 275 | mpObjectRelease(_d); 276 | } 277 | 278 | Context Context::create() noexcept { 279 | Impl* d = static_cast(::malloc(sizeof(Impl))); 280 | 281 | if (d == nullptr) { 282 | // Allocation failure. 283 | d = const_cast(&mpContextNull); 284 | } 285 | else { 286 | RuntimeData* rt = static_cast(::malloc(sizeof(RuntimeData))); 287 | if (rt == nullptr) { 288 | // Allocation failure. 289 | ::free(d); 290 | d = const_cast(&mpContextNull); 291 | } 292 | else { 293 | d->_refCount = 1; 294 | d->_runtimeData = new(rt) RuntimeData(); 295 | } 296 | } 297 | 298 | return Context(d); 299 | } 300 | 301 | // ============================================================================ 302 | // [mpsl::Context - Reset] 303 | // ============================================================================ 304 | 305 | Error Context::reset() noexcept { 306 | mpObjectRelease(mpAtomicSetXchgT( 307 | &_d, const_cast(&mpContextNull))); 308 | 309 | return kErrorOk; 310 | } 311 | 312 | // ============================================================================ 313 | // [mpsl::Context - Clone / Freeze] 314 | // ============================================================================ 315 | 316 | Error Context::clone() noexcept { 317 | 318 | // TODO: 319 | return kErrorOk; 320 | } 321 | 322 | Error Context::freeze() noexcept { 323 | 324 | // TODO: 325 | return kErrorOk; 326 | } 327 | 328 | // ============================================================================ 329 | // [mpsl::Context - Compile] 330 | // ============================================================================ 331 | 332 | #define MPSL_PROPAGATE_AND_HANDLE_COLLISION(...) \ 333 | do { \ 334 | AstSymbol* collidedSymbol = nullptr; \ 335 | Error _errorValue = __VA_ARGS__; \ 336 | \ 337 | if (MPSL_UNLIKELY(_errorValue)) { \ 338 | if (_errorValue == kErrorSymbolCollision && log) { \ 339 | sbTmp.assignFormat("Built-in symbol collision: '%s' already defined", \ 340 | collidedSymbol->name()); \ 341 | log->log(OutputLog::Message( \ 342 | OutputLog::kMessageError, 0, 0, \ 343 | StringRef("ERROR", 5), \ 344 | StringRef(sbTmp.data(), sbTmp.size()))); \ 345 | } \ 346 | return _errorValue; \ 347 | } \ 348 | } while (0) 349 | 350 | Error Context::_compile(Program& program, const CompileArgs& ca, OutputLog* log) noexcept { 351 | const char* body = ca.body.data(); 352 | size_t size = ca.body.size(); 353 | 354 | uint32_t options = ca.options; 355 | uint32_t numArgs = ca.numArgs; 356 | 357 | if (numArgs == 0 || numArgs > Globals::kMaxArgumentsCount) 358 | return MPSL_TRACE_ERROR(kErrorInvalidArgument); 359 | 360 | // -------------------------------------------------------------------------- 361 | // [Debug Strings] 362 | // -------------------------------------------------------------------------- 363 | 364 | static const char kDebugHeadingAST[] = "AST"; 365 | static const char kDebugHeadingIR[] = "IR"; 366 | static const char kDebugHeadingASM[] = "ASM"; 367 | 368 | // -------------------------------------------------------------------------- 369 | // [Init] 370 | // -------------------------------------------------------------------------- 371 | 372 | // Init options first. 373 | options &= _kOptionsMask; 374 | 375 | if (log) 376 | options |= kInternalOptionLog; 377 | else 378 | options &= ~(kOptionVerbose | kOptionDebugAst | kOptionDebugIR | kOptionDebugASM); 379 | 380 | if (size == Globals::kInvalidIndex) 381 | size = ::strlen(body); 382 | 383 | Zone zone(32768 - Zone::kBlockOverhead); 384 | ZoneAllocator allocator(&zone); 385 | StringTmp<512> sbTmp; 386 | 387 | AstBuilder ast(&allocator); 388 | IRBuilder ir(&allocator, numArgs); 389 | 390 | MPSL_PROPAGATE(ast.addProgramScope()); 391 | MPSL_PROPAGATE(ast.addBuiltInTypes(mpTypeInfo, kTypeCount)); 392 | MPSL_PROPAGATE(ast.addBuiltInConstants(mpConstInfo, MPSL_ARRAY_SIZE(mpConstInfo))); 393 | MPSL_PROPAGATE(ast.addBuiltInIntrinsics()); 394 | 395 | for (uint32_t slot = 0; slot < numArgs; slot++) { 396 | MPSL_PROPAGATE_AND_HANDLE_COLLISION(ast.addBuiltInObject(slot, ca.layout[slot], &collidedSymbol)); 397 | } 398 | 399 | // Setup basic data structures used during parsing and compilation. 400 | ErrorReporter errorReporter(body, size, options, log); 401 | 402 | // -------------------------------------------------------------------------- 403 | // [AST] 404 | // -------------------------------------------------------------------------- 405 | 406 | // Parse the source code into AST. 407 | { MPSL_PROPAGATE(Parser(&ast, &errorReporter, body, size).parseProgram(ast.programNode())); } 408 | 409 | // Perform a semantic analysis of the parsed AST. 410 | // 411 | // It can add some nodes required by implicit casts and fail if the code is 412 | // semantically incorrect - for example invalid implicit cast, explicit-cast, 413 | // or function call. This pass doesn't do constant folding or optimizations. 414 | { MPSL_PROPAGATE(AstAnalysis(&ast, &errorReporter).onProgram(ast.programNode())); } 415 | 416 | if (options & kOptionDebugAst) { 417 | ast.dump(sbTmp); 418 | log->log( 419 | OutputLog::Message( 420 | OutputLog::kMessageDump, 0, 0, 421 | StringRef(kDebugHeadingAST, MPSL_ARRAY_SIZE(kDebugHeadingAST) - 1), 422 | StringRef(sbTmp.data(), sbTmp.size()))); 423 | sbTmp.clear(); 424 | } 425 | 426 | // Perform basic optimizations at AST level (dead code removal and constant 427 | // folding). This pass shouldn't do any unsafe optimizations and it's a bit 428 | // limited, but it's faster to do them now than doing these optimizations at 429 | // IR level. 430 | { MPSL_PROPAGATE(AstOptimizer(&ast, &errorReporter).onProgram(ast.programNode())); } 431 | 432 | if (options & kOptionDebugAst) { 433 | ast.dump(sbTmp); 434 | log->log( 435 | OutputLog::Message( 436 | OutputLog::kMessageDump, 0, 0, 437 | StringRef(kDebugHeadingAST, MPSL_ARRAY_SIZE(kDebugHeadingAST) - 1), 438 | StringRef(sbTmp.data(), sbTmp.size()))); 439 | sbTmp.clear(); 440 | } 441 | 442 | // -------------------------------------------------------------------------- 443 | // [IR] 444 | // -------------------------------------------------------------------------- 445 | 446 | // Translate AST to IR. 447 | { 448 | CodeGen::Result unused(false); 449 | MPSL_PROPAGATE(CodeGen(&ast, &ir).onProgram(ast.programNode(), unused)); 450 | } 451 | 452 | if (options & kOptionDebugIR) { 453 | ir.dump(sbTmp); 454 | log->log( 455 | OutputLog::Message( 456 | OutputLog::kMessageDump, 0, 0, 457 | StringRef(kDebugHeadingIR, MPSL_ARRAY_SIZE(kDebugHeadingIR) - 1), 458 | StringRef(sbTmp.data(), sbTmp.size()))); 459 | sbTmp.clear(); 460 | } 461 | 462 | MPSL_PROPAGATE(mpIRPass(&ir)); 463 | 464 | if (options & kOptionDebugIR) { 465 | ir.dump(sbTmp); 466 | log->log( 467 | OutputLog::Message( 468 | OutputLog::kMessageDump, 0, 0, 469 | StringRef(kDebugHeadingIR, MPSL_ARRAY_SIZE(kDebugHeadingIR) - 1), 470 | StringRef(sbTmp.data(), sbTmp.size()))); 471 | sbTmp.clear(); 472 | } 473 | 474 | // -------------------------------------------------------------------------- 475 | // [ASM] 476 | // -------------------------------------------------------------------------- 477 | 478 | // Compile and store the reference to the `main()` function. 479 | RuntimeData* rt = static_cast(_d->_runtimeData); 480 | Program::Impl* programD = program._d; 481 | 482 | void* func = nullptr; 483 | { 484 | asmjit::StringLogger asmlog; 485 | asmjit::CodeHolder code; 486 | 487 | code.init(rt->_runtime.codeInfo()); 488 | asmjit::x86::Compiler c(&code); 489 | 490 | if (options & kOptionDebugASM) 491 | code.setLogger(&asmlog); 492 | 493 | IRToX86 compiler(&allocator, &c); 494 | if (options & kOptionDisableSSE4_1) 495 | compiler._enableSSE4_1 = false; 496 | MPSL_PROPAGATE(compiler.compileIRAsFunc(&ir)); 497 | 498 | asmjit::Error err = c.finalize(); 499 | if (err) return MPSL_TRACE_ERROR(kErrorJITFailed); 500 | 501 | err = rt->_runtime.add(&func, &code); 502 | if (err) return MPSL_TRACE_ERROR(kErrorJITFailed); 503 | 504 | if (options & kOptionDebugASM) 505 | log->log( 506 | OutputLog::Message( 507 | OutputLog::kMessageDump, 0, 0, 508 | StringRef(kDebugHeadingASM, MPSL_ARRAY_SIZE(kDebugHeadingASM) - 1), 509 | StringRef(asmlog.data(), asmlog.dataSize()))); 510 | } 511 | 512 | if (programD->_refCount == 1 && static_cast(programD->_runtimeData) == rt) { 513 | rt->_runtime.release(programD->_main); 514 | programD->_main = func; 515 | } 516 | else { 517 | programD = static_cast(::malloc(sizeof(Program::Impl))); 518 | if (programD == nullptr) { 519 | rt->_runtime.release(func); 520 | return MPSL_TRACE_ERROR(kErrorNoMemory); 521 | } 522 | 523 | programD->_refCount = 1; 524 | programD->_runtimeData = mpObjectAddRef(rt); 525 | programD->_main = func; 526 | 527 | mpObjectRelease( 528 | mpAtomicSetXchgT( 529 | &program._d, programD)); 530 | } 531 | 532 | return kErrorOk; 533 | } 534 | 535 | // ============================================================================ 536 | // [mpsl::Context - Operator Overload] 537 | // ============================================================================ 538 | 539 | Context& Context::operator=(const Context& other) noexcept { 540 | mpObjectRelease( 541 | mpAtomicSetXchgT( 542 | &_d, mpObjectAddRef(other._d))); 543 | 544 | return *this; 545 | } 546 | 547 | // ============================================================================ 548 | // [mpsl::Program - Construction / Destruction] 549 | // ============================================================================ 550 | 551 | static const Program::Impl mpProgramNull = { 0, nullptr, nullptr }; 552 | 553 | Program::Program() noexcept 554 | : _d(const_cast(&mpProgramNull)) {} 555 | 556 | Program::Program(const Program& other) noexcept 557 | : _d(mpObjectAddRef(other._d)) {} 558 | 559 | Program::~Program() noexcept { 560 | mpObjectRelease(_d); 561 | } 562 | 563 | // ============================================================================ 564 | // [mpsl::Program - Reset] 565 | // ============================================================================ 566 | 567 | Error Program::reset() noexcept { 568 | mpObjectRelease(mpAtomicSetXchgT( 569 | &_d, const_cast(&mpProgramNull))); 570 | return kErrorOk; 571 | } 572 | 573 | // ============================================================================ 574 | // [mpsl::Program - Operator Overload] 575 | // ============================================================================ 576 | 577 | Program& Program::operator=(const Program& other) noexcept { 578 | mpObjectRelease( 579 | mpAtomicSetXchgT( 580 | &_d, mpObjectAddRef(other._d))); 581 | 582 | return *this; 583 | } 584 | 585 | // ============================================================================ 586 | // [mpsl::OutputLog - Construction / Destruction] 587 | // ============================================================================ 588 | 589 | OutputLog::OutputLog() noexcept {} 590 | OutputLog::~OutputLog() noexcept {} 591 | 592 | // ============================================================================ 593 | // [mpsl::ErrorReporter - Interface] 594 | // ============================================================================ 595 | 596 | void ErrorReporter::getLineAndColumn(uint32_t position, uint32_t& line, uint32_t& column) noexcept { 597 | // Should't happen, but be defensive. 598 | if (static_cast(position) >= _size) { 599 | line = 0; 600 | column = 0; 601 | return; 602 | } 603 | 604 | const char* pStart = _body; 605 | const char* p = pStart + position; 606 | 607 | uint32_t x = 0; 608 | uint32_t y = 1; 609 | 610 | // Find column. 611 | while (p[0] != '\n') { 612 | x++; 613 | if (p == pStart) 614 | break; 615 | p--; 616 | } 617 | 618 | // Find line. 619 | while (p != pStart) { 620 | y += p[0] == '\n'; 621 | p--; 622 | } 623 | 624 | line = y; 625 | column = x; 626 | } 627 | 628 | void ErrorReporter::onWarning(uint32_t position, const char* fmt, ...) noexcept { 629 | if (reportsWarnings()) { 630 | StringTmp<256> sb; 631 | 632 | va_list ap; 633 | va_start(ap, fmt); 634 | FormatUtils::vformat(sb, fmt, ap); 635 | va_end(ap); 636 | 637 | onWarning(position, sb); 638 | } 639 | } 640 | 641 | void ErrorReporter::onWarning(uint32_t position, const String& msg) noexcept { 642 | if (reportsWarnings()) { 643 | uint32_t line, column; 644 | getLineAndColumn(position, line, column); 645 | _log->log( 646 | OutputLog::Message( 647 | OutputLog::kMessageWarning, line, column, 648 | StringRef("WARNING", 7), 649 | StringRef(msg.data(), msg.size()))); 650 | } 651 | } 652 | 653 | Error ErrorReporter::onError(Error error, uint32_t position, const char* fmt, ...) noexcept { 654 | if (reportsErrors()) { 655 | StringTmp<256> sb; 656 | 657 | va_list ap; 658 | va_start(ap, fmt); 659 | FormatUtils::vformat(sb, fmt, ap); 660 | va_end(ap); 661 | 662 | return onError(error, position, sb); 663 | } 664 | else { 665 | return MPSL_TRACE_ERROR(error); 666 | } 667 | } 668 | 669 | Error ErrorReporter::onError(Error error, uint32_t position, const String& msg) noexcept { 670 | if (reportsErrors()) { 671 | OutputLog::Message logMsg( 672 | OutputLog::kMessageError, 0, 0, 673 | StringRef("ERROR", 5), 674 | StringRef(msg.data(), msg.size())); 675 | 676 | getLineAndColumn(position, logMsg._line, logMsg._column); 677 | _log->log(logMsg); 678 | } 679 | 680 | return MPSL_TRACE_ERROR(error); 681 | } 682 | 683 | } // mpsl namespace 684 | 685 | // [Api-End] 686 | #include "./mpsl_apiend.h" 687 | -------------------------------------------------------------------------------- /src/mpsl/mpsl_apibegin.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #if !defined(_MPSL_MPSL_H) 9 | # error "MPSL - Invalid use of 'apibegin.h' - Include 'mpsl.h' first." 10 | #endif // !_MPSL_MPSL_H 11 | 12 | #if !defined(MPSL_API_SCOPE) 13 | # define MPSL_API_SCOPE 14 | #else 15 | # error "MPSL - api-scope is already active, previous scope not closed by mpsl_apiend.h?" 16 | #endif // MPSL_API_SCOPE 17 | 18 | // ============================================================================ 19 | // [Compiler Support] 20 | // ============================================================================ 21 | 22 | // [MSC] 23 | #if defined(_MSC_VER) 24 | 25 | # pragma warning(push) 26 | # pragma warning(disable: 4127) // conditional expression is constant 27 | # pragma warning(disable: 4201) // nameless struct/union 28 | # pragma warning(disable: 4244) // '+=' : conversion from 'int' to 'x', possible 29 | // loss of data 30 | # pragma warning(disable: 4251) // struct needs to have dll-interface to be used 31 | // by clients of struct ... 32 | # pragma warning(disable: 4275) // non dll-interface struct ... used as base for 33 | // dll-interface struct 34 | # pragma warning(disable: 4355) // this used in base member initializer list 35 | # pragma warning(disable: 4480) // specifying underlying type for enum 36 | # pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' 37 | 38 | # if _MSC_VER < 1900 39 | # if !defined(vsnprintf) 40 | # define MPSP_UNDEF_VSNPRINTF 41 | # define vsnprintf _vsnprintf 42 | # endif // !vsnprintf 43 | # if !defined(snprintf) 44 | # define MPSP_UNDEF_SNPRINTF 45 | # define snprintf _snprintf 46 | # endif // !snprintf 47 | # endif 48 | 49 | #endif // _MSC_VER 50 | 51 | // [Clang] 52 | #if defined(__clang__) 53 | # pragma clang diagnostic push 54 | # pragma clang diagnostic ignored "-Wc++11-extensions" 55 | # pragma clang diagnostic ignored "-Wunnamed-type-template-args" 56 | #endif // __clang__ 57 | 58 | // ============================================================================ 59 | // [Custom Macros] 60 | // ============================================================================ 61 | 62 | // [NONCOPYABLE] 63 | #define MPSL_NONCOPYABLE(...) \ 64 | private: \ 65 | MPSL_INLINE __VA_ARGS__(const __VA_ARGS__& other) noexcept = delete; \ 66 | MPSL_INLINE __VA_ARGS__& operator=(const __VA_ARGS__& other) noexcept = delete; \ 67 | public: 68 | -------------------------------------------------------------------------------- /src/mpsl/mpsl_apiend.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #if defined(MPSL_API_SCOPE) 9 | # undef MPSL_API_SCOPE 10 | #else 11 | # error "MPSL - api-scope not active, forgot to include mpsl_apibegin.h?" 12 | #endif // MPSL_API_SCOPE 13 | 14 | // ============================================================================ 15 | // [Compiler Support] 16 | // ============================================================================ 17 | 18 | // [MSC] 19 | #if defined(_MSC_VER) 20 | # pragma warning(pop) 21 | # if _MSC_VER < 1900 22 | # if defined(MPSL_UNDEF_VSNPRINTF) 23 | # undef vsnprintf 24 | # undef MPSL_UNDEF_VSNPRINTF 25 | # endif // MPSL_UNDEF_VSNPRINTF 26 | # if defined(MPSL_UNDEF_SNPRINTF) 27 | # undef snprintf 28 | # undef MPSL_UNDEF_SNPRINTF 29 | # endif // MPSL_UNDEF_SNPRINTF 30 | # endif 31 | #endif // _MSC_VER 32 | 33 | // [Clang] 34 | #if defined(__clang__) 35 | # pragma clang diagnostic pop 36 | #endif // __clang__ 37 | 38 | // ============================================================================ 39 | // [Custom Macros] 40 | // ============================================================================ 41 | 42 | // [NONCOPYABLE] 43 | #undef MPSL_NONCOPYABLE 44 | -------------------------------------------------------------------------------- /src/mpsl/mpsl_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPSL_P_H 9 | #define _MPSL_MPSL_P_H 10 | 11 | // [Dependencies - MPSL] 12 | #include "./mpsl.h" 13 | 14 | // [Dependencies - C] 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | // [Dependencies - C++] 22 | #include 23 | 24 | // [Dependencies - AsmJit] 25 | #include 26 | 27 | // [Dependencies - SSE2] 28 | #if MPSL_ARCH_X64 || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) || defined(__SSE2__) 29 | # define MPSL_USE_SSE2 1 30 | #else 31 | # define MPSL_USE_SSE2 0 32 | #endif 33 | 34 | #if MPSL_USE_SSE2 35 | # include 36 | # include 37 | #endif 38 | 39 | // [Api-Begin] 40 | #include "./mpsl_apibegin.h" 41 | 42 | namespace mpsl { 43 | 44 | // ============================================================================ 45 | // [MPSL_ASSERT] 46 | // // ============================================================================ 47 | 48 | //! \internal 49 | //! \def MPSL_ASSERT 50 | 51 | #if defined(MPSL_DEBUG) 52 | # define MPSL_ASSERT(exp) \ 53 | do { \ 54 | if (MPSL_UNLIKELY(!(exp))) \ 55 | ::mpsl::mpAssertionFailed(#exp, __FILE__, __LINE__); \ 56 | } while (0) 57 | #else 58 | # define MPSL_ASSERT(exp) ((void)0) 59 | #endif 60 | 61 | // ============================================================================ 62 | // [MPSL_TRACE_ERROR] 63 | // ============================================================================ 64 | 65 | #if defined(MPSL_DEBUG) 66 | # define MPSL_TRACE_ERROR(err) ::mpsl::mpTraceError(err) 67 | #else 68 | # define MPSL_TRACE_ERROR(err) static_cast< ::mpsl::Error >(err) 69 | #endif 70 | 71 | // ============================================================================ 72 | // [MPSL_PROPAGATE] 73 | // ============================================================================ 74 | 75 | //! \internal 76 | #define MPSL_PROPAGATE(...) \ 77 | do { \ 78 | ::mpsl::Error _errorValue = __VA_ARGS__; \ 79 | if (MPSL_UNLIKELY(_errorValue != ::mpsl::kErrorOk)) \ 80 | return _errorValue; \ 81 | } while (0) 82 | 83 | //! \internal 84 | #define MPSL_PROPAGATE_(exp, cleanup) \ 85 | do { \ 86 | ::mpsl::Error _errorCode = (exp); \ 87 | if (MPSL_UNLIKELY(_errorCode != ::mpsl::kErrorOk)) { \ 88 | cleanup \ 89 | return _errorCode; \ 90 | } \ 91 | } while (0) 92 | 93 | //! \internal 94 | #define MPSL_NULLCHECK(ptr) \ 95 | do { \ 96 | if (MPSL_UNLIKELY(!(ptr))) \ 97 | return MPSL_TRACE_ERROR(::mpsl::kErrorNoMemory); \ 98 | } while (0) 99 | 100 | //! \internal 101 | #define MPSL_NULLCHECK_(ptr, cleanup) \ 102 | do { \ 103 | if (MPSL_UNLIKELY(!(ptr))) { \ 104 | cleanup \ 105 | return MPSL_TRACE_ERROR(::mpsl::kErrorNoMemory); \ 106 | } \ 107 | } while (0) 108 | 109 | // ============================================================================ 110 | // [Reuse] 111 | // ============================================================================ 112 | 113 | // Reuse these classes for internal use as we depend on asmjit anyway. 114 | using asmjit::Lock; 115 | using asmjit::ScopedLock; 116 | 117 | using asmjit::String; 118 | using asmjit::StringTmp; 119 | 120 | using asmjit::Zone; 121 | using asmjit::ZoneVector; 122 | using asmjit::ZoneAllocator; 123 | 124 | // ============================================================================ 125 | // [mpsl::Miscellaneous] 126 | // ============================================================================ 127 | 128 | enum { kInvalidDataSlot = 0xFF }; 129 | enum { kInvalidRegId = asmjit::BaseReg::kIdBad }; 130 | enum { kPointerWidth = static_cast(sizeof(void*)) }; 131 | 132 | static const uint32_t kB32_0 = 0x00000000u; 133 | static const uint32_t kB32_1 = 0xFFFFFFFFu; 134 | 135 | static const uint64_t kB64_0 = 0x0000000000000000u; 136 | static const uint64_t kB64_1 = 0xFFFFFFFFFFFFFFFFu; 137 | 138 | // ============================================================================ 139 | // [mpsl::InternalOptions] 140 | // ============================================================================ 141 | 142 | //! \internal 143 | //! 144 | //! Compilation options MPSL uses internally. 145 | enum InternalOptions { 146 | //! Set if `OutputLog` is present. MPSL then checks only this flag to use it. 147 | kInternalOptionLog = 0x10000000 148 | }; 149 | 150 | // ============================================================================ 151 | // [mpsl::mpAssertionFailed] 152 | // ============================================================================ 153 | 154 | //! \internal 155 | void mpAssertionFailed(const char* exp, const char* file, int line) noexcept; 156 | 157 | // ============================================================================ 158 | // [mpsl::mpTraceError] 159 | // ============================================================================ 160 | 161 | //! \internal 162 | //! 163 | //! Error tracking available in debug-mode. 164 | //! 165 | //! The mpTraceError function can be used to track any error reported by MPSL. 166 | //! Put a breakpoint inside mpTraceError and the program execution will be 167 | //! stopped immediately after the error is created. 168 | Error mpTraceError(Error error) noexcept; 169 | 170 | // ============================================================================ 171 | // [mpsl::RuntimeData] 172 | // ============================================================================ 173 | 174 | class RuntimeData { 175 | public: 176 | MPSL_NONCOPYABLE(RuntimeData) 177 | 178 | // -------------------------------------------------------------------------- 179 | // [Construction / Destruction] 180 | // -------------------------------------------------------------------------- 181 | 182 | MPSL_INLINE RuntimeData() noexcept 183 | : _refCount(1), 184 | _runtime() {} 185 | MPSL_INLINE ~RuntimeData() noexcept {} 186 | 187 | // -------------------------------------------------------------------------- 188 | // [Internal] 189 | // -------------------------------------------------------------------------- 190 | 191 | MPSL_INLINE void destroy() noexcept { 192 | this->~RuntimeData(); 193 | ::free(this); 194 | } 195 | 196 | // -------------------------------------------------------------------------- 197 | // [Accessors] 198 | // -------------------------------------------------------------------------- 199 | 200 | MPSL_INLINE asmjit::JitRuntime* runtime() const noexcept { 201 | return const_cast(&_runtime); 202 | } 203 | 204 | // -------------------------------------------------------------------------- 205 | // [Members] 206 | // -------------------------------------------------------------------------- 207 | 208 | uintptr_t _refCount; //!< Reference count. 209 | asmjit::JitRuntime _runtime; //!< JIT runtime. 210 | }; 211 | 212 | // ============================================================================ 213 | // [mpsl::ErrorReporter] 214 | // ============================================================================ 215 | 216 | //! Error reporter. 217 | class ErrorReporter { 218 | public: 219 | MPSL_NONCOPYABLE(ErrorReporter) 220 | 221 | MPSL_INLINE ErrorReporter(const char* body, size_t size, uint32_t options, OutputLog* log) noexcept 222 | : _body(body), 223 | _size(size), 224 | _options(options), 225 | _log(log) { 226 | 227 | // These should be handled by MPSL before the `ErrorReporter` is created. 228 | MPSL_ASSERT((log == nullptr && (_options & kInternalOptionLog) == 0) || 229 | (log != nullptr && (_options & kInternalOptionLog) != 0) ); 230 | } 231 | 232 | // -------------------------------------------------------------------------- 233 | // [Error Handling] 234 | // -------------------------------------------------------------------------- 235 | 236 | MPSL_INLINE bool reportsErrors() const noexcept { return (_options & kInternalOptionLog) != 0; } 237 | MPSL_INLINE bool reportsWarnings() const noexcept { return (_options & kOptionVerbose) != 0; } 238 | 239 | void getLineAndColumn(uint32_t position, uint32_t& line, uint32_t& column) noexcept; 240 | 241 | void onWarning(uint32_t position, const char* fmt, ...) noexcept; 242 | void onWarning(uint32_t position, const String& msg) noexcept; 243 | 244 | Error onError(Error error, uint32_t position, const char* fmt, ...) noexcept; 245 | Error onError(Error error, uint32_t position, const String& msg) noexcept; 246 | 247 | // -------------------------------------------------------------------------- 248 | // [Members] 249 | // -------------------------------------------------------------------------- 250 | 251 | const char* _body; 252 | size_t _size; 253 | 254 | uint32_t _options; 255 | OutputLog* _log; 256 | }; 257 | 258 | } // mpsl namespace 259 | 260 | // [Api-End] 261 | #include "./mpsl_apiend.h" 262 | 263 | // [Guard] 264 | #endif // _MPSL_MPSL_P_H 265 | -------------------------------------------------------------------------------- /src/mpsl/mpstrtod_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPSTRTOD_P_H 9 | #define _MPSL_MPSTRTOD_P_H 10 | 11 | // [Dependencies] 12 | #include "./mpsl_p.h" 13 | 14 | #if defined(_WIN32) 15 | #define MPSL_STRTOD_MSLOCALE 16 | #include 17 | #include 18 | #else 19 | #define MPSL_STRTOD_XLOCALE 20 | #include 21 | #include 22 | // xlocale.h is not available on Linux anymore, it uses . 23 | #if defined(__APPLE__ ) || \ 24 | defined(__bsdi__ ) || \ 25 | defined(__DragonFly__) || \ 26 | defined(__FreeBSD__ ) || \ 27 | defined(__NetBSD__ ) || \ 28 | defined(__OpenBSD__ ) 29 | #include 30 | #endif 31 | #endif 32 | 33 | // [Api-Begin] 34 | #include "./mpsl_apibegin.h" 35 | 36 | namespace mpsl { 37 | 38 | // ============================================================================ 39 | // [mpsl::StrToD] 40 | // ============================================================================ 41 | 42 | struct StrToD { 43 | MPSL_NONCOPYABLE(StrToD) 44 | 45 | #if defined(MPSL_STRTOD_MSLOCALE) 46 | MPSL_INLINE StrToD() noexcept { 47 | handle = _create_locale(LC_ALL, "C"); 48 | } 49 | MPSL_INLINE ~StrToD() noexcept { 50 | _free_locale(handle); 51 | } 52 | 53 | MPSL_INLINE bool isOk() const noexcept { 54 | return handle != nullptr; 55 | } 56 | MPSL_INLINE double conv(const char* s, char** end) const noexcept { 57 | return _strtod_l(s, end, handle); 58 | } 59 | 60 | _locale_t handle; 61 | #elif defined(MPSL_STRTOD_XLOCALE) 62 | MPSL_INLINE StrToD() noexcept { 63 | handle = newlocale(LC_ALL_MASK, "C", nullptr); 64 | } 65 | MPSL_INLINE ~StrToD() noexcept { 66 | freelocale(handle); 67 | } 68 | 69 | MPSL_INLINE bool isOk() const noexcept { 70 | return handle != nullptr; 71 | } 72 | MPSL_INLINE double conv(const char* s, char** end) const noexcept { 73 | return strtod_l(s, end, handle); 74 | } 75 | 76 | locale_t handle; 77 | #else 78 | // Time bomb! 79 | MPSL_INLINE bool isOk() const noexcept { 80 | return true; 81 | } 82 | MPSL_INLINE double conv(const char* s, char** end) const noexcept { 83 | return strtod(s, end); 84 | } 85 | #endif 86 | }; 87 | 88 | } // mpsl namespace 89 | 90 | // [Api-End] 91 | #include "./mpsl_apiend.h" 92 | 93 | // [Guard] 94 | #endif // _MPSL_MPSTRTOD_P_H 95 | -------------------------------------------------------------------------------- /src/mpsl/mptokenizer.cpp: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Export] 8 | #define MPSL_EXPORTS 9 | 10 | // [Dependencies - MPSL] 11 | #include "./mphash_p.h" 12 | #include "./mptokenizer_p.h" 13 | 14 | // [Api-Begin] 15 | #include "./mpsl_apibegin.h" 16 | 17 | namespace mpsl { 18 | 19 | // ============================================================================ 20 | // [mpsl::Tokenizer] 21 | // ============================================================================ 22 | 23 | //! \internal 24 | //! 25 | //! Character classes used by tokenizer. 26 | enum TokenChar { 27 | // Digit. 28 | kTokenChar0x0, kTokenChar0x1, kTokenChar0x2, kTokenChar0x3, 29 | kTokenChar0x4, kTokenChar0x5, kTokenChar0x6, kTokenChar0x7, 30 | kTokenChar0x8, kTokenChar0x9, 31 | 32 | // Digit-Hex. 33 | kTokenChar0xA, kTokenChar0xB, kTokenChar0xC, kTokenChar0xD, 34 | kTokenChar0xE, kTokenChar0xF, 35 | 36 | // Non-Hex ASCII [A-Z] Letter and Underscore [_]. 37 | kTokenCharSym, 38 | 39 | // Punctuation. 40 | kTokenCharDot = kTokenDot, // . 41 | kTokenCharCom = kTokenComma, // , 42 | kTokenCharSem = kTokenSemicolon, // ; 43 | kTokenCharQue = kTokenQMark, // ? 44 | kTokenCharCol = kTokenColon, // : 45 | kTokenCharLCu = kTokenLCurl, // { 46 | kTokenCharRCu = kTokenRCurl, // } 47 | kTokenCharLBr = kTokenLBracket, // [ 48 | kTokenCharRBr = kTokenRBracket, // ] 49 | kTokenCharLPa = kTokenLParen, // ( 50 | kTokenCharRPa = kTokenRParen, // ) 51 | 52 | kTokenCharAdd = kTokenAdd, // + 53 | kTokenCharSub = kTokenSub, // - 54 | kTokenCharMul = kTokenMul, // * 55 | kTokenCharDiv = kTokenDiv, // / 56 | kTokenCharMod = kTokenMod, // % 57 | kTokenCharNot = kTokenNot, // ! 58 | kTokenCharAnd = kTokenAnd, // & 59 | kTokenCharOr = kTokenOr, // | 60 | kTokenCharXor = kTokenXor, // ^ 61 | kTokenCharNeg = kTokenBitNeg, // ~ 62 | kTokenCharEq = kTokenAssign, // = 63 | kTokenCharLt = kTokenLt, // < 64 | kTokenCharGt = kTokenGt, // > 65 | 66 | // Space. 67 | kTokenCharSpc = 63, 68 | 69 | // Extended ASCII character (0x80 and above), acts as non-recognized. 70 | kTokenCharExt, 71 | // Invalid (non-recognized) character. 72 | kTokenCharInv, 73 | 74 | kTokenCharSingleCharTokenEnd = kTokenCharRPa 75 | }; 76 | 77 | #define C(id) kTokenChar##id 78 | static const uint8_t mpCharClass[] = { 79 | C(Inv), C(Inv), C(Inv), C(Inv), C(Inv), C(Inv), C(Inv), C(Inv), // 000-007 ........ | All invalid. 80 | C(Inv), C(Spc), C(Spc), C(Spc), C(Spc), C(Spc), C(Inv), C(Inv), // 008-015 . .. | Spaces: 0x9-0xD. 81 | C(Inv), C(Inv), C(Inv), C(Inv), C(Inv), C(Inv), C(Inv), C(Inv), // 016-023 ........ | All invalid. 82 | C(Inv), C(Inv), C(Inv), C(Inv), C(Inv), C(Inv), C(Inv), C(Inv), // 024-031 ........ | All invalid. 83 | C(Spc), C(Not), C(Inv), C(Inv), C(Inv), C(Mod), C(And), C(Inv), // 032-039 !"#$%&' | Unassigned: "#$'. 84 | C(LPa), C(RPa), C(Mul), C(Add), C(Com), C(Sub), C(Dot), C(Div), // 040-047 ()*+,-./ | 85 | C(0x0), C(0x1), C(0x2), C(0x3), C(0x4), C(0x5), C(0x6), C(0x7), // 048-055 01234567 | 86 | C(0x8), C(0x9), C(Col), C(Sem), C(Lt ), C(Eq ), C(Gt ), C(Que), // 056-063 89:;<=>? | 87 | C(Inv), C(0xA), C(0xB), C(0xC), C(0xD), C(0xE), C(0xF), C(Sym), // 064-071 @ABCDEFG | Unassigned: @. 88 | C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), // 072-079 HIJKLMNO | 89 | C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), // 080-087 PQRSTUVW | 90 | C(Sym), C(Sym), C(Sym), C(LBr), C(Inv), C(RBr), C(Xor), C(Sym), // 088-095 XYZ[\]^_ | Unassigned: \. 91 | C(Inv), C(0xA), C(0xB), C(0xC), C(0xD), C(0xE), C(0xF), C(Sym), // 096-103 `abcdefg | Unassigned: `. 92 | C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), // 104-111 hijklmno | 93 | C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), C(Sym), // 112-119 pqrstuvw | 94 | C(Sym), C(Sym), C(Sym), C(LCu), C(Or ), C(RCu), C(Neg), C(Inv), // 120-127 xyz{|}~ | 95 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 128-135 ........ | Extended. 96 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 136-143 ........ | 97 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 144-151 ........ | 98 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 152-159 ........ | 99 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 160-167 ........ | 100 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 168-175 ........ | 101 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 176-183 ........ | 102 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 184-191 ........ | 103 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 192-199 ........ | 104 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 200-207 ........ | 105 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 208-215 ........ | 106 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 216-223 ........ | 107 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 224-231 ........ | 108 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 232-239 ........ | 109 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), // 240-247 ........ | 110 | C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext), C(Ext) // 248-255 ........ | 111 | }; 112 | #undef C 113 | 114 | //! \internal 115 | //! 116 | //! RAW lowercase conversion. 117 | //! 118 | //! This method exploits how ASCII table has been designed. It expects ASCII 119 | //! character on the input that will be lowercased by setting the 0x20 bit on. 120 | static MPSL_INLINE uint32_t mpGetLower(uint32_t c) noexcept { return c | 0x20; } 121 | 122 | //! \internal 123 | static const double mpPow10Table[] = { 124 | 1e+0 , 1e+1 , 1e+2 , 1e+3 , 1e+4 , 1e+5 , 1e+6 , 1e+7 , 125 | 1e+8 , 1e+9 , 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15 126 | }; 127 | 128 | //! \internal 129 | enum { 130 | kSafeDigits = 15, 131 | kPow10TableSize = static_cast(MPSL_ARRAY_SIZE(mpPow10Table)) 132 | }; 133 | 134 | #define CHAR4X(C0, C1, C2, C3) \ 135 | ( (static_cast(C0) ) + \ 136 | (static_cast(C1) << 8) + \ 137 | (static_cast(C2) << 16) + \ 138 | (static_cast(C3) << 24) ) 139 | 140 | //! \internal 141 | //! 142 | //! Converts a given symbol `s` of `sLen` to a keyword token. 143 | static uint32_t mpGetKeyword(const uint8_t* s, size_t sLen) noexcept { 144 | if (sLen < 2) 145 | return kTokenSymbol; 146 | 147 | uint32_t s0 = (static_cast(s[0]) ) + 148 | (static_cast(s[1]) << 8) ; 149 | 150 | if (sLen == 2) { 151 | if (s0 == CHAR4X('d', 'o', 0 , 0 )) return kTokenDo; 152 | if (s0 == CHAR4X('i', 'f', 0 , 0 )) return kTokenIf; 153 | return kTokenSymbol; 154 | } 155 | 156 | s0 += (static_cast(s[2]) << 16); 157 | if (sLen == 3) { 158 | if (s0 == CHAR4X('f', 'o', 'r', 0 )) return kTokenFor; 159 | return kTokenSymbol; 160 | } 161 | 162 | s0 += (static_cast(s[3]) << 24); 163 | if (sLen == 4) { 164 | if (s0 == CHAR4X('e', 'l', 's', 'e')) return kTokenElse; 165 | if (s0 == CHAR4X('v', 'o', 'i', 'd')) return kTokenVoid; 166 | return kTokenSymbol; 167 | } 168 | 169 | uint32_t s1 = static_cast(s[4]); 170 | 171 | if (sLen == 5) { 172 | if (s0 == CHAR4X('b', 'r', 'e', 'a') && s1 == CHAR4X('k', 0 , 0 , 0 )) return kTokenBreak; 173 | if (s0 == CHAR4X('c', 'o', 'n', 's') && s1 == CHAR4X('t', 0 , 0 , 0 )) return kTokenConst; 174 | if (s0 == CHAR4X('w', 'h', 'i', 'l') && s1 == CHAR4X('e', 0 , 0 , 0 )) return kTokenWhile; 175 | return kTokenSymbol; 176 | } 177 | 178 | s1 += (static_cast(s[5]) << 8); 179 | if (sLen == 6) { 180 | if (s0 == CHAR4X('r', 'e', 't', 'u') && s1 == CHAR4X('r', 'n', 0 , 0 )) return kTokenReturn; 181 | if (s0 == CHAR4X('s', 't', 'r', 'u') && s1 == CHAR4X('c', 't', 0 , 0 )) return kTokenReserved; 182 | return kTokenSymbol; 183 | } 184 | 185 | s1 += (static_cast(s[6]) << 16); 186 | if (sLen == 7) { 187 | if (s0 == CHAR4X('t', 'y', 'p', 'e') && s1 == CHAR4X('d', 'e', 'f', 0 )) return kTokenTypeDef; 188 | return kTokenSymbol; 189 | } 190 | 191 | s1 += (static_cast(s[7]) << 24); 192 | if (sLen == 8) { 193 | if (s0 == CHAR4X('c', 'o', 'n', 't') && s1 == CHAR4X('i', 'n', 'u', 'e')) return kTokenContinue; 194 | return kTokenSymbol; 195 | } 196 | 197 | return kTokenSymbol; 198 | } 199 | 200 | uint32_t Tokenizer::peek(Token* token) noexcept { 201 | uint32_t uToken = _token.tokenType(); 202 | if (uToken != kTokenInvalid || (uToken = next(&_token)) != kTokenInvalid) 203 | *token = _token; 204 | return uToken; 205 | } 206 | 207 | uint32_t Tokenizer::next(Token* token) noexcept { 208 | // Skip parsing if the next token is already done, caused by `peek()`. 209 | uint32_t c = _token.tokenType(); 210 | uint32_t hVal; 211 | 212 | if (c != kTokenInvalid) { 213 | *token = _token; 214 | _token._tokenType = kTokenInvalid; 215 | return c; 216 | } 217 | 218 | // Input string. 219 | const uint8_t* p = reinterpret_cast(_p); 220 | const uint8_t* pToken = p; 221 | 222 | const uint8_t* pStart = reinterpret_cast(_start); 223 | const uint8_t* pEnd = reinterpret_cast(_end); 224 | 225 | // -------------------------------------------------------------------------- 226 | // [Spaces] 227 | // -------------------------------------------------------------------------- 228 | 229 | _Repeat: 230 | for (;;) { 231 | if (p == pEnd) 232 | goto _EndOfInput; 233 | 234 | hVal = p[0]; 235 | c = mpCharClass[hVal]; 236 | 237 | if (c != kTokenCharSpc) 238 | break; 239 | p++; 240 | } 241 | 242 | // Save the first character of the token. 243 | pToken = p; 244 | 245 | // -------------------------------------------------------------------------- 246 | // [Number | Dot] 247 | // -------------------------------------------------------------------------- 248 | 249 | if (c <= kTokenChar0x9 || c == kTokenCharDot) { 250 | if (c == kTokenChar0x0 && (size_t)(pEnd - p) >= 2 && mpGetLower(p[1]) == 'x') { 251 | // Hexadecimal number starts with "0x" or "0X". 252 | uint32_t hexVal = 0; 253 | uint32_t hexLen = 0; 254 | 255 | if ((p += 2) == pEnd) 256 | goto _Invalid; 257 | 258 | do { 259 | c = mpCharClass[static_cast(p[0])]; 260 | if (c != kTokenChar0x0) 261 | break; 262 | } while (++p != pEnd); 263 | 264 | // Parse digits. 265 | for (;;) { 266 | if (c > kTokenChar0xF) 267 | break; 268 | hexVal = (hexVal << 4) + c; 269 | 270 | // A 32-bit hexadecimal number can have at most 8 digits (without 271 | // leading zeros). 272 | if (++hexLen >= 9) 273 | goto _Invalid; 274 | 275 | if (++p == pEnd) 276 | break; 277 | c = mpCharClass[static_cast(p[0])]; 278 | } 279 | 280 | // There has to be at least one digit after the '0x' prefix. 281 | if ((size_t)(p - pToken) == 2) 282 | goto _Invalid; 283 | 284 | // Error if there is an alpha-numeric character after the number. 285 | if (p != pEnd && mpCharClass[static_cast(p[0])] <= kTokenCharSym) 286 | goto _Invalid; 287 | 288 | token->setData((size_t)(pToken - pStart), (size_t)(p - pToken), kTypeInt, kTokenNumber); 289 | token->_value = static_cast(static_cast(hexVal)); 290 | } 291 | else { 292 | // Integer or double precision floating point. 293 | uint32_t nType = kTypeVoid; 294 | 295 | // Parsing floating point is not that simple as it looks. To simplify the 296 | // most common cases we parse floating points up to `kSafeDigits` and then 297 | // use libc `strtod()` function to parse numbers that are more complicated. 298 | // 299 | // http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ 300 | double val = 0.0; 301 | size_t digits = 0; 302 | 303 | // Skip leading zeros. 304 | while (p[0] == '0') { 305 | if (++p == pEnd) 306 | break; 307 | } 308 | 309 | // Parse significand or integer part. 310 | size_t scale = 0; 311 | while (p != pEnd) { 312 | c = static_cast(p[0]) - static_cast('0'); 313 | if (c > 9) 314 | break; 315 | scale++; 316 | 317 | if (c != 0) { 318 | if (scale < kPow10TableSize) 319 | val = val * mpPow10Table[scale] + static_cast(static_cast(c)); 320 | digits += scale; 321 | scale = 0; 322 | } 323 | 324 | p++; 325 | } 326 | size_t significantDigits = digits + scale; 327 | 328 | // Parse fraction. 329 | if (p != pEnd && p[0] == '.') { 330 | // Fraction, even if it's just '.' promotes the number to `double`. 331 | nType = kTypeDouble; 332 | 333 | while (++p != pEnd) { 334 | c = static_cast(p[0]) - static_cast('0'); 335 | if (c > 9) 336 | break; 337 | scale++; 338 | 339 | if (c != 0) { 340 | if (scale < kPow10TableSize) 341 | val = val * mpPow10Table[scale] + static_cast(static_cast(c)); 342 | digits += scale; 343 | scale = 0; 344 | } 345 | } 346 | 347 | // Token is '.'. 348 | if ((size_t)(p - pToken) == 1) { 349 | _p = reinterpret_cast(p); 350 | return token->setData((size_t)(pToken - pStart), (size_t)(p - pToken), 0, kTokenDot); 351 | } 352 | } 353 | 354 | bool safe = digits <= kSafeDigits && significantDigits < 999999; 355 | int exponent = safe ? static_cast(significantDigits) - static_cast(digits) : 0; 356 | 357 | // Parse an optional exponent. 358 | if (p != pEnd && mpGetLower(p[0]) == 'e') { 359 | // Exponent promotes the number to `double`. 360 | nType = kTypeDouble; 361 | 362 | if (++p == pEnd) 363 | goto _Invalid; 364 | 365 | c = p[0]; 366 | bool negative = c == '-'; 367 | if (negative || c == '+') 368 | if (++p == pEnd) 369 | goto _Invalid; 370 | 371 | uint32_t e = 0; 372 | size_t eLen = 0; 373 | 374 | do { 375 | c = static_cast(p[0]) - static_cast('0'); 376 | if (c > 9) 377 | break; 378 | 379 | e = e * 10 + c; 380 | eLen++; 381 | } while (++p != pEnd); 382 | 383 | // Error if there is no number after the 'e' token. 384 | if (eLen == 0) 385 | goto _Invalid; 386 | 387 | // If less than 10 digits it's safe to assume the exponent is zero if 388 | // `e` is zero. Otherwise it could have overflown the 32-bit integer. 389 | if (e == 0 && eLen < 10) 390 | eLen = 0; // No exponent. 391 | 392 | if (eLen <= 6) 393 | exponent += negative ? -static_cast(e) : static_cast(e); 394 | else 395 | safe = false; 396 | } 397 | 398 | // Parse an optional "f" or "d" suffix. 399 | if (p != pEnd) { 400 | c = mpGetLower(p[0]); 401 | 402 | if (c == 'f') { 403 | nType = kTypeFloat; 404 | p++; 405 | } 406 | else if (c == 'd') { 407 | nType = kTypeDouble; 408 | p++; 409 | } 410 | } 411 | 412 | // Error if there is an alpha-numeric character right next to the number. 413 | if (p != pEnd && mpCharClass[p[0]] <= kTokenCharSym) { 414 | goto _Invalid; 415 | } 416 | 417 | // Limit a range of safe values from Xe-15 to Xe15. 418 | safe = safe && exponent >= -kPow10TableSize && exponent <= kPow10TableSize; 419 | size_t size = (size_t)(p - pToken); 420 | 421 | if (safe) { 422 | // Now decide whether to report `int` or `double` if there was no type specifier. 423 | if (nType == kTypeVoid) { 424 | if (val >= -2147483648.0 && val <= 2147483647.0) 425 | nType = kTypeInt; 426 | else 427 | nType = kTypeDouble; 428 | } 429 | 430 | if (exponent != 0) 431 | val = exponent < 0 ? val / mpPow10Table[-exponent] : val * mpPow10Table[exponent]; 432 | } 433 | else { 434 | // If the number is not safe and there was no specifier then it's `double`. 435 | if (nType == kTypeVoid) 436 | nType = kTypeDouble; 437 | 438 | // Using libc's strtod is not optimal, but it's precise for complex cases. 439 | char tmp[512]; 440 | char* buf = tmp; 441 | 442 | if (size >= MPSL_ARRAY_SIZE(tmp) && (buf = static_cast(::malloc(size + 1))) == nullptr) 443 | return kTokenInvalid; 444 | 445 | memcpy(buf, pToken, size); 446 | buf[size] = '\0'; 447 | 448 | val = _strtod.conv(buf, nullptr); 449 | 450 | if (buf != tmp) 451 | ::free(buf); 452 | } 453 | 454 | token->_value = val; 455 | token->setData((size_t)(pToken - pStart), size, nType, kTokenNumber); 456 | } 457 | 458 | _p = reinterpret_cast(p); 459 | return kTokenNumber; 460 | } 461 | 462 | // -------------------------------------------------------------------------- 463 | // [Symbol | Keyword] 464 | // -------------------------------------------------------------------------- 465 | 466 | else if (c <= kTokenCharSym) { 467 | // We always calculate hashCode during tokenization to improve performance. 468 | while (++p != pEnd) { 469 | uint32_t ord = static_cast(p[0]); 470 | c = mpCharClass[ord]; 471 | if (c > kTokenCharSym) 472 | break; 473 | hVal = HashUtils::hashChar(hVal, ord); 474 | } 475 | 476 | size_t size = (size_t)(p - pToken); 477 | _p = reinterpret_cast(p); 478 | return token->setData((size_t)(pToken - pStart), size, hVal, mpGetKeyword(pToken, size)); 479 | } 480 | 481 | // -------------------------------------------------------------------------- 482 | // [Single-Char] 483 | // -------------------------------------------------------------------------- 484 | 485 | else if (c <= kTokenCharSingleCharTokenEnd) { 486 | _p = reinterpret_cast(++p); 487 | return token->setData((size_t)(pToken - pStart), (size_t)(p - pToken), 0, c); 488 | } 489 | 490 | // -------------------------------------------------------------------------- 491 | // [Single-Char | Multi-Char] 492 | // -------------------------------------------------------------------------- 493 | 494 | else if (c < kTokenCharSpc) { 495 | uint32_t c1 = (++p != pEnd) ? static_cast(p[0]) : static_cast(0); 496 | 497 | switch (c) { 498 | case kTokenCharAdd: // `+=`, `++`, `+`. 499 | if (c1 == '=') { c = kTokenAssignAdd; p++; break; } 500 | if (c1 == '+') { c = kTokenPlusPlus; p++; break; } 501 | break; 502 | 503 | case kTokenCharSub: // `-=`, `--`, `-`. 504 | if (c1 == '=') { c = kTokenAssignSub; p++; break; } 505 | if (c1 == '-') { c = kTokenMinusMinus; p++; break; } 506 | break; 507 | 508 | case kTokenCharMul: // `*=`, `*`. 509 | if (c1 == '=') { c = kTokenAssignMul; p++; break; } 510 | break; 511 | 512 | case kTokenCharDiv: // `//`, `/=`, `/`. 513 | if (c1 == '/') 514 | goto _Comment; 515 | if (c1 == '=') { c = kTokenAssignDiv; p++; break; } 516 | break; 517 | 518 | case kTokenCharMod: // `%=`, `%`. 519 | if (c1 == '=') { c = kTokenAssignMod; p++; break; } 520 | break; 521 | 522 | case kTokenCharAnd: // `&=`, `&&`, `&`. 523 | if (c1 == '=') { c = kTokenAssignAnd; p++; break; } 524 | if (c1 == '&') { c = kTokenLogAnd; p++; break; } 525 | break; 526 | 527 | case kTokenCharOr: // `|=`, `||`, `|`. 528 | if (c1 == '=') { c = kTokenAssignOr; p++; break; } 529 | if (c1 == '|') { c = kTokenLogOr; p++; break; } 530 | break; 531 | 532 | case kTokenCharXor: // `^=`, `^`. 533 | if (c1 == '=') { c = kTokenAssignXor; p++; break; } 534 | break; 535 | 536 | case kTokenCharNeg: // `~`. 537 | break; 538 | 539 | case kTokenCharNot: // `!=`, `!`. 540 | if (c1 == '=') { c = kTokenNe; p++; break; } 541 | break; 542 | 543 | case kTokenCharEq: // `==`, `=`. 544 | if (c1 == '=') { c = kTokenEq; p++; break; } 545 | break; 546 | 547 | case kTokenCharLt: // `<<=`, `<<`, `<=`, `<`. 548 | if (c1 == '<') { 549 | if (++p != pEnd && p[0] == '=') { 550 | c = kTokenAssignSll; p++; 551 | } 552 | else { 553 | c = kTokenSll; 554 | } 555 | break; 556 | } 557 | if (c1 == '=') { c = kTokenLe; p++; break; } 558 | break; 559 | 560 | case kTokenCharGt: // `>>>=`, `>>>`, `>>=`, `>>`, `>=`, `>`. 561 | if (c1 == '>') { 562 | if (++p != pEnd) { 563 | uint32_t c2 = static_cast(p[0]); 564 | if (c2 == '>') { 565 | if (++p != pEnd && p[0] == '=') { 566 | c = kTokenAssignSrl; p++; 567 | } 568 | else { 569 | c = kTokenSrl; 570 | } 571 | break; 572 | } 573 | else if (c2 == '=') { 574 | c = kTokenAssignSra; 575 | break; 576 | } 577 | } 578 | c = kTokenSra; 579 | break; 580 | } 581 | if (c1 == '=') { c = kTokenGe; p++; break; } 582 | break; 583 | } 584 | 585 | _p = reinterpret_cast(p); 586 | return token->setData((size_t)(pToken - pStart), (size_t)(p - pToken), 0, c); 587 | } 588 | 589 | _Invalid: 590 | _p = reinterpret_cast(pToken); 591 | return token->setData((size_t)(pToken - pStart), (size_t)(p - pToken), 0, kTokenInvalid); 592 | 593 | _Comment: 594 | for (;;) { 595 | if (p == pEnd) 596 | goto _EndOfInput; 597 | 598 | c = static_cast(*p++); 599 | if (c == '\n') 600 | goto _Repeat; 601 | } 602 | 603 | _EndOfInput: 604 | _p = _end; 605 | return token->setData((size_t)(pToken - pStart), (size_t)(p - pToken), 0, kTokenEnd); 606 | } 607 | 608 | } // mpsl namespace 609 | 610 | // [Api-End] 611 | #include "./mpsl_apiend.h" 612 | -------------------------------------------------------------------------------- /src/mpsl/mptokenizer_p.h: -------------------------------------------------------------------------------- 1 | // [MPSL] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Guard] 8 | #ifndef _MPSL_MPTOKENIZER_P_H 9 | #define _MPSL_MPTOKENIZER_P_H 10 | 11 | // [Dependencies - MPSL] 12 | #include "./mpsl_p.h" 13 | #include "./mpstrtod_p.h" 14 | 15 | // [Api-Begin] 16 | #include "./mpsl_apibegin.h" 17 | 18 | namespace mpsl { 19 | 20 | // ============================================================================ 21 | // [mpsl::Token] 22 | // ============================================================================ 23 | 24 | //! \internal 25 | //! 26 | //! Token type. 27 | enum TokenType { 28 | kTokenInvalid = 0, // 29 | 30 | kTokenSymbol, // 31 | kTokenNumber, // 32 | 33 | kTokenBreak, // break 34 | kTokenConst, // const 35 | kTokenContinue, // continue 36 | kTokenDo, // do 37 | kTokenElse, // else 38 | kTokenFor, // for 39 | kTokenIf, // if 40 | kTokenReturn, // return 41 | kTokenTypeDef, // typedef 42 | kTokenVoid, // void 43 | kTokenWhile, // while 44 | 45 | kTokenReserved, // reserved keyword 46 | 47 | kTokenDot = 36, // . 48 | kTokenComma, // , 49 | kTokenSemicolon, // ; 50 | 51 | kTokenQMark, // ? 52 | kTokenColon, // : 53 | 54 | kTokenLCurl, // { 55 | kTokenRCurl, // } 56 | 57 | kTokenLBracket, // [ 58 | kTokenRBracket, // ] 59 | 60 | kTokenLParen, // ( 61 | kTokenRParen, // ) 62 | 63 | kTokenAdd, // + 64 | kTokenSub, // - 65 | kTokenMul, // * 66 | kTokenDiv, // / 67 | kTokenMod, // % 68 | kTokenNot, // ! 69 | 70 | kTokenAnd, // & 71 | kTokenOr, // | 72 | kTokenXor, // ^ 73 | kTokenBitNeg, // ~ 74 | 75 | kTokenAssign, // = 76 | kTokenLt, // < 77 | kTokenGt, // > 78 | 79 | kTokenPlusPlus, // ++ 80 | kTokenMinusMinus, // -- 81 | 82 | kTokenEq, // == 83 | kTokenNe, // != 84 | kTokenLe, // <= 85 | kTokenGe, // >= 86 | 87 | kTokenLogAnd, // && 88 | kTokenLogOr, // || 89 | 90 | kTokenSll, // << 91 | kTokenSrl, // >>> 92 | kTokenSra, // >> 93 | 94 | kTokenAssignAdd, // += 95 | kTokenAssignSub, // -= 96 | kTokenAssignMul, // *= 97 | kTokenAssignDiv, // /= 98 | kTokenAssignMod, // %= 99 | 100 | kTokenAssignAnd, // &= 101 | kTokenAssignOr, // |= 102 | kTokenAssignXor, // ^= 103 | kTokenAssignSll, // <<= 104 | kTokenAssignSrl, // >>>= 105 | kTokenAssignSra, // >>= 106 | 107 | kTokenEnd // 108 | }; 109 | 110 | // ============================================================================ 111 | // [mpsl::Token] 112 | // ============================================================================ 113 | 114 | //! \internal 115 | //! 116 | //! Token. 117 | struct Token { 118 | // -------------------------------------------------------------------------- 119 | // [Reset] 120 | // -------------------------------------------------------------------------- 121 | 122 | MPSL_INLINE void reset() noexcept { 123 | _position = 0; 124 | _size = 0; 125 | _value = 0.0; 126 | _tokenType = kTokenInvalid; 127 | } 128 | 129 | // -------------------------------------------------------------------------- 130 | // [Accessors] 131 | // -------------------------------------------------------------------------- 132 | 133 | MPSL_INLINE uint32_t setData(size_t position, size_t size, uint32_t hashCodeOrNumberType, uint32_t tokenType) noexcept { 134 | _position = position; 135 | _size = size; 136 | _hashCode = hashCodeOrNumberType; 137 | _tokenType = tokenType; 138 | 139 | return tokenType; 140 | } 141 | 142 | MPSL_INLINE uint32_t tokenType() const noexcept { return _tokenType; } 143 | MPSL_INLINE uint32_t hashCode() const noexcept { return _hashCode; } 144 | MPSL_INLINE uint32_t numberType() const noexcept { return _numberType; } 145 | 146 | MPSL_INLINE size_t position() const noexcept { return _position; } 147 | MPSL_INLINE uint32_t positionAsUInt() const noexcept { 148 | MPSL_ASSERT(_position < ~static_cast(0)); 149 | return static_cast(_position); 150 | } 151 | 152 | MPSL_INLINE size_t size() const noexcept { return _size; } 153 | MPSL_INLINE double value() const noexcept { return _value; } 154 | 155 | // -------------------------------------------------------------------------- 156 | // [Members] 157 | // -------------------------------------------------------------------------- 158 | 159 | uint32_t _tokenType; //!< Token type. 160 | union { 161 | uint32_t _hashCode; //!< Token hash (only if the token is symbol or keyword). 162 | uint32_t _numberType; //!< Number type if the token is `kTokenNumber`. 163 | }; 164 | size_t _position; //!< Token position from the beginning of the input. 165 | size_t _size; //!< Token string size. 166 | double _value; //!< Token value (if the token is a number). 167 | }; 168 | 169 | // ============================================================================ 170 | // [mpsl::Tokenizer] 171 | // ============================================================================ 172 | 173 | struct Tokenizer { 174 | MPSL_NONCOPYABLE(Tokenizer) 175 | 176 | // -------------------------------------------------------------------------- 177 | // [Construction / Destruction] 178 | // -------------------------------------------------------------------------- 179 | 180 | MPSL_INLINE Tokenizer(const char* s, size_t sLen) noexcept 181 | : _p(s), 182 | _start(s), 183 | _end(s + sLen), 184 | _strtod() { 185 | _token.reset(); 186 | } 187 | 188 | // -------------------------------------------------------------------------- 189 | // [Ops] 190 | // -------------------------------------------------------------------------- 191 | 192 | //! Get the current token. 193 | uint32_t peek(Token* token) noexcept; 194 | //! Get the current token and advance. 195 | uint32_t next(Token* token) noexcept; 196 | 197 | //! Set the token that will be returned by `next()` and `peek()` functions. 198 | MPSL_INLINE void set(Token* token) noexcept { 199 | // We have to update also _p in case that multiple tokens were put back. 200 | _p = _start + token->position() + token->size(); 201 | _token = *token; 202 | } 203 | 204 | //! Consume a token got by using peek(). 205 | MPSL_INLINE void consume() noexcept { 206 | _token._tokenType = kTokenInvalid; 207 | } 208 | 209 | //! Consume a token got by using peek() and call `peek()`. 210 | //! 211 | //! \note Can be called only immediately after peek(). 212 | MPSL_INLINE uint32_t consumeAndPeek(Token* token) noexcept { 213 | consume(); 214 | return peek(token); 215 | } 216 | 217 | //! Consume a token got by using peek() and call `next()`. 218 | //! 219 | //! \note Can be called only immediately after peek(). 220 | MPSL_INLINE uint32_t consumeAndNext(Token* token) noexcept { 221 | consume(); 222 | return next(token); 223 | } 224 | 225 | // -------------------------------------------------------------------------- 226 | // [Members] 227 | // -------------------------------------------------------------------------- 228 | 229 | const char* _p; 230 | const char* _start; 231 | const char* _end; 232 | 233 | StrToD _strtod; 234 | Token _token; 235 | }; 236 | 237 | } // mpsl namespace 238 | 239 | // [Api-End] 240 | #include "./mpsl_apiend.h" 241 | 242 | // [Guard] 243 | #endif // _MPSL_MPTOKENIZER_P_H 244 | -------------------------------------------------------------------------------- /test/mp_dsp.cpp: -------------------------------------------------------------------------------- 1 | // [MPSL-Test] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Dependencies] 8 | #include "./mpsl.h" 9 | #include "./mp_utils.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | struct Args { 16 | mpsl::Int4 bg, fg; 17 | mpsl::Int4 alpha; 18 | mpsl::Int4 result; 19 | }; 20 | 21 | int main(int argc, char* argv[]) { 22 | mpsl::Context ctx = mpsl::Context::create(); 23 | mpsl::LayoutTmp<> args; 24 | 25 | args.addMember("bg" , mpsl::kTypeInt4 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, bg)); 26 | args.addMember("fg" , mpsl::kTypeInt4 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, fg)); 27 | args.addMember("alpha", mpsl::kTypeInt4 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, alpha)); 28 | args.addMember("@ret" , mpsl::kTypeInt4 | mpsl::kTypeWO, MPSL_OFFSET_OF(Args, result)); 29 | 30 | const char body[] = 31 | "int4 main() {\n" 32 | " const int inv = 0x01000100;\n" 33 | " int4 x = pmulw(bg, psubw(inv, alpha));\n" 34 | " int4 y = pmulw(fg, alpha);\n" 35 | " return psrlw(paddw(x, y), 8);\n" 36 | "}"; 37 | 38 | printf("[Program]\n%s\n", body); 39 | uint32_t options = 40 | mpsl::kOptionVerbose | 41 | mpsl::kOptionDebugAst | 42 | mpsl::kOptionDebugIR | 43 | mpsl::kOptionDebugASM ; 44 | TestLog log; 45 | 46 | mpsl::Program1<> program; 47 | mpsl::Error err = program.compile(ctx, body, options, args, &log); 48 | 49 | if (err) { 50 | printf("Compilation failed: ERROR: 0x%08X\n", static_cast(err)); 51 | } 52 | else { 53 | Args args; 54 | args.bg.set(0x00200030, 0x00400050, 0x00600070, 0x008000FF); 55 | args.fg.set(0x00000000); 56 | args.alpha.set(0x00800080); 57 | 58 | err = program.run(&args); 59 | if (err) 60 | printf("Execution failed: ERROR %08X\n", static_cast(err)); 61 | else 62 | printf("Return=%08X %08X %08X %08X\n", args.result[0], args.result[1], args.result[2], args.result[3]); 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /test/mp_test.cpp: -------------------------------------------------------------------------------- 1 | // [MPSL-Test] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Dependencies] 8 | #include "./mpsl.h" 9 | #include "./mp_utils.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | // ============================================================================ 16 | // [CmdLine] 17 | // ============================================================================ 18 | 19 | class CmdLine { 20 | public: 21 | CmdLine(int argc, const char* const* argv) 22 | : argc(argc), 23 | argv(argv) {} 24 | 25 | bool hasKey(const char* key) const { 26 | for (int i = 0; i < argc; i++) 27 | if (::strcmp(argv[i], key) == 0) 28 | return true; 29 | return false; 30 | } 31 | 32 | int argc; 33 | const char* const* argv; 34 | }; 35 | 36 | // ============================================================================ 37 | // [TestUtils] 38 | // ============================================================================ 39 | 40 | struct TestUtils { 41 | static void printCode(const char* prefix, const char* body) { 42 | size_t prefixLen = ::strlen(prefix); 43 | 44 | size_t bodyStart = 0; 45 | size_t bodyLen = ::strlen(body); 46 | 47 | bool needsIndent = false; 48 | char indent[2] = " "; 49 | 50 | size_t i = 0; 51 | printf("%s", prefix); 52 | 53 | if (bodyLen && body[bodyLen - 1] == '\n') 54 | bodyLen--; 55 | 56 | for (;;) { 57 | if (i == bodyLen || body[i] == '\n') { 58 | if (needsIndent) { 59 | for (size_t j = 0; j < prefixLen; j++) 60 | printf(indent); 61 | } 62 | 63 | printf("%.*s\n", int(i - bodyStart), body + bodyStart); 64 | if (i == bodyLen) return; 65 | 66 | bodyStart = ++i; 67 | needsIndent = true; 68 | } 69 | else { 70 | i++; 71 | } 72 | } 73 | } 74 | }; 75 | 76 | // ============================================================================ 77 | // [Helpers] 78 | // ============================================================================ 79 | 80 | static mpsl::Value makeIVal(int x, int y = 0, int z = 0, int w = 0) { 81 | mpsl::Value v; v.i.set(x, y, z, w, 0, 0, 0, 0); return v; 82 | } 83 | 84 | static mpsl::Value makeFVal(float x, float y = 0.0f, float z = 0.0f, float w = 0.0f) { 85 | mpsl::Value v; v.f.set(x, y, z, w, 0, 0, 0, 0); return v; 86 | } 87 | 88 | static mpsl::Value makeDVal(double x, double y = 0.0, double z = 0.0, double w = 0.0) { 89 | mpsl::Value v; v.d.set(x, y, z, w); return v; 90 | } 91 | 92 | // ============================================================================ 93 | // [Test] 94 | // ============================================================================ 95 | 96 | class Test { 97 | public: 98 | struct Args { 99 | int ia, ib, ic; 100 | mpsl::Int2 i2a, i2b, i2c; 101 | mpsl::Int3 i3a, i3b, i3c; 102 | mpsl::Int4 i4a, i4b, i4c; 103 | 104 | float fa, fb, fc; 105 | mpsl::Float2 f2a, f2b, f2c; 106 | mpsl::Float3 f3a, f3b, f3c; 107 | mpsl::Float4 f4a, f4b, f4c; 108 | 109 | double da, db, dc; 110 | mpsl::Double2 d2a, d2b, d2c; 111 | mpsl::Double3 d3a, d3b, d3c; 112 | mpsl::Double4 d4a, d4b, d4c; 113 | 114 | mpsl::Value ret; 115 | }; 116 | 117 | Test(uint32_t options); 118 | 119 | inline bool isVerbose() { 120 | const uint32_t kVerboseMask = 121 | mpsl::kOptionVerbose | 122 | mpsl::kOptionDebugAst | 123 | mpsl::kOptionDebugIR | 124 | mpsl::kOptionDebugASM ; 125 | return (_options & kVerboseMask) != 0; 126 | } 127 | 128 | void initLayout(mpsl::Layout& layout, uint32_t retType); 129 | void initArgs(Args& args); 130 | 131 | void printTest(const char* body); 132 | void printPass(const char* body); 133 | void printFail(const char* body, const char* fmt, ...); 134 | 135 | bool basicTest(const char* body, uint32_t retType, const mpsl::Value& retValue); 136 | bool failureTest(const char* body); 137 | 138 | mpsl::Context _ctx; 139 | uint32_t _options; 140 | 141 | int a[4]; 142 | int b[4]; 143 | int c[4]; 144 | bool _succeeded; 145 | }; 146 | 147 | Test::Test(uint32_t options) 148 | : _ctx(mpsl::Context::create()), 149 | _options(options), 150 | _succeeded(true) { 151 | a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; 152 | b[0] = 9; b[1] = 8; b[2] = 7; b[3] = 6; 153 | c[0] =-2; c[1] =-3; c[2] = 4; c[3] = 5; 154 | } 155 | 156 | void Test::initLayout(mpsl::Layout& layout, uint32_t retType) { 157 | layout.addMember("ia" , mpsl::kTypeInt | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, ia)); 158 | layout.addMember("ib" , mpsl::kTypeInt | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, ib)); 159 | layout.addMember("ic" , mpsl::kTypeInt | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, ic)); 160 | 161 | layout.addMember("i2a", mpsl::kTypeInt2 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, i2a)); 162 | layout.addMember("i2b", mpsl::kTypeInt2 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, i2b)); 163 | layout.addMember("i2c", mpsl::kTypeInt2 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, i2c)); 164 | 165 | layout.addMember("i3a", mpsl::kTypeInt3 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, i3a)); 166 | layout.addMember("i3b", mpsl::kTypeInt3 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, i3b)); 167 | layout.addMember("i3c", mpsl::kTypeInt3 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, i3c)); 168 | 169 | layout.addMember("i4a", mpsl::kTypeInt4 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, i4a)); 170 | layout.addMember("i4b", mpsl::kTypeInt4 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, i4b)); 171 | layout.addMember("i4c", mpsl::kTypeInt4 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, i4c)); 172 | 173 | layout.addMember("fa" , mpsl::kTypeFloat | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, fa)); 174 | layout.addMember("fb" , mpsl::kTypeFloat | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, fb)); 175 | layout.addMember("fc" , mpsl::kTypeFloat | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, fc)); 176 | 177 | layout.addMember("f2a", mpsl::kTypeFloat2 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, f2a)); 178 | layout.addMember("f2b", mpsl::kTypeFloat2 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, f2b)); 179 | layout.addMember("f2c", mpsl::kTypeFloat2 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, f2c)); 180 | 181 | layout.addMember("f3a", mpsl::kTypeFloat3 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, f3a)); 182 | layout.addMember("f3b", mpsl::kTypeFloat3 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, f3b)); 183 | layout.addMember("f3c", mpsl::kTypeFloat3 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, f3c)); 184 | 185 | layout.addMember("f4a", mpsl::kTypeFloat4 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, f4a)); 186 | layout.addMember("f4b", mpsl::kTypeFloat4 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, f4b)); 187 | layout.addMember("f4c", mpsl::kTypeFloat4 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, f4c)); 188 | 189 | layout.addMember("da" , mpsl::kTypeDouble | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, da)); 190 | layout.addMember("db" , mpsl::kTypeDouble | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, db)); 191 | layout.addMember("dc" , mpsl::kTypeDouble | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, dc)); 192 | 193 | layout.addMember("d2a", mpsl::kTypeDouble2 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, d2a)); 194 | layout.addMember("d2b", mpsl::kTypeDouble2 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, d2b)); 195 | layout.addMember("d2c", mpsl::kTypeDouble2 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, d2c)); 196 | 197 | layout.addMember("d3a", mpsl::kTypeDouble3 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, d3a)); 198 | layout.addMember("d3b", mpsl::kTypeDouble3 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, d3b)); 199 | layout.addMember("d3c", mpsl::kTypeDouble3 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, d3c)); 200 | 201 | layout.addMember("d4a", mpsl::kTypeDouble4 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, d4a)); 202 | layout.addMember("d4b", mpsl::kTypeDouble4 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, d4b)); 203 | layout.addMember("d4c", mpsl::kTypeDouble4 | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, d4c)); 204 | 205 | layout.addMember("@ret", retType, MPSL_OFFSET_OF(Args, ret)); 206 | } 207 | 208 | void Test::initArgs(Args& args) { 209 | args.ia = a[0]; 210 | args.ib = b[0]; 211 | args.ic = c[0]; 212 | args.i2a.set(a[0], a[1]); 213 | args.i2b.set(b[0], b[1]); 214 | args.i2c.set(c[0], c[1]); 215 | args.i3a.set(a[0], a[1], a[2]); 216 | args.i3b.set(b[0], b[1], b[2]); 217 | args.i3c.set(c[0], c[1], c[2]); 218 | args.i4a.set(a[0], a[1], a[2], a[3]); 219 | args.i4b.set(b[0], b[1], b[2], b[3]); 220 | args.i4c.set(c[0], c[1], c[2], c[3]); 221 | 222 | args.fa = float(a[0]); 223 | args.fb = float(b[0]); 224 | args.fc = float(c[0]); 225 | args.f2a.set(float(a[0]), float(a[1])); 226 | args.f2b.set(float(b[0]), float(b[1])); 227 | args.f2c.set(float(c[0]), float(c[1])); 228 | args.f3a.set(float(a[0]), float(a[1]), float(a[2])); 229 | args.f3b.set(float(b[0]), float(b[1]), float(b[2])); 230 | args.f3c.set(float(c[0]), float(c[1]), float(c[2])); 231 | args.f4a.set(float(a[0]), float(a[1]), float(a[2]), float(a[3])); 232 | args.f4b.set(float(b[0]), float(b[1]), float(b[2]), float(b[3])); 233 | args.f4c.set(float(c[0]), float(c[1]), float(c[2]), float(c[3])); 234 | 235 | args.da = double(a[0]); 236 | args.db = double(b[0]); 237 | args.dc = double(c[0]); 238 | args.d2a.set(double(a[0]), double(a[1])); 239 | args.d2b.set(double(b[0]), double(b[1])); 240 | args.d2c.set(double(c[0]), double(c[1])); 241 | args.d3a.set(double(a[0]), double(a[1]), double(a[2])); 242 | args.d3b.set(double(b[0]), double(b[1]), double(b[2])); 243 | args.d3c.set(double(c[0]), double(c[1]), double(c[2])); 244 | args.d4a.set(double(a[0]), double(a[1]), double(a[2]), double(a[3])); 245 | args.d4b.set(double(b[0]), double(b[1]), double(b[2]), double(b[3])); 246 | args.d4c.set(double(c[0]), double(c[1]), double(c[2]), double(c[3])); 247 | } 248 | 249 | void Test::printTest(const char* body) { 250 | TestUtils::printCode("[TEST] ", body); 251 | } 252 | 253 | void Test::printPass(const char* body) { 254 | if (isVerbose()) 255 | printf("[PASS]\n"); 256 | } 257 | 258 | void Test::printFail(const char* body, const char* fmt, ...) { 259 | printf("[FAIL] "); 260 | 261 | va_list ap; 262 | va_start(ap, fmt); 263 | vprintf(fmt, ap); 264 | va_end(ap); 265 | } 266 | 267 | bool Test::basicTest(const char* body, uint32_t retType, const mpsl::Value& retValue) { 268 | mpsl::LayoutTmp<1024> layout; 269 | Args args; 270 | 271 | initLayout(layout, retType); 272 | initArgs(args); 273 | printTest(body); 274 | 275 | TestLog log; 276 | mpsl::Program1 program; 277 | mpsl::Error err = program.compile(_ctx, body, _options, layout, &log); 278 | 279 | if (err != mpsl::kErrorOk) { 280 | printFail(body, "COMPILATION ERROR 0x%08X.\n", static_cast(err)); 281 | return false; 282 | } 283 | 284 | err = program.run(&args); 285 | if (err != mpsl::kErrorOk) { 286 | printFail(body, "EXECUTION ERROR 0x%08X.\n", static_cast(err)); 287 | return false; 288 | } 289 | 290 | bool isOk = true; 291 | unsigned int i, n; 292 | 293 | switch (retType) { 294 | case mpsl::kTypeInt : n = 1; goto checkInt; 295 | case mpsl::kTypeInt2: n = 2; goto checkInt; 296 | case mpsl::kTypeInt3: n = 3; goto checkInt; 297 | case mpsl::kTypeInt4: n = 4; goto checkInt; 298 | checkInt: 299 | for (i = 0; i < n; i++) { 300 | int x = args.ret.i[i]; 301 | int y = retValue.i[i]; 302 | 303 | if (x != y) { 304 | printf("[FAIL] ic[%u] %d != Expected(%d)\n", i, x, y); 305 | isOk = false; 306 | } 307 | } 308 | break; 309 | 310 | case mpsl::kTypeFloat : n = 1; goto checkFloat; 311 | case mpsl::kTypeFloat2: n = 2; goto checkFloat; 312 | case mpsl::kTypeFloat3: n = 3; goto checkFloat; 313 | case mpsl::kTypeFloat4: n = 4; goto checkFloat; 314 | checkFloat: 315 | for (i = 0; i < n; i++) { 316 | float x = args.ret.f[i]; 317 | float y = retValue.f[i]; 318 | 319 | if (x != y) { 320 | printf("[FAIL] fc[%u] %g != Expected(%g)\n", i, x, y); 321 | isOk = false; 322 | } 323 | } 324 | break; 325 | 326 | case mpsl::kTypeDouble : n = 1; goto checkDouble; 327 | case mpsl::kTypeDouble2: n = 2; goto checkDouble; 328 | case mpsl::kTypeDouble3: n = 3; goto checkDouble; 329 | case mpsl::kTypeDouble4: n = 4; goto checkDouble; 330 | checkDouble: 331 | for (i = 0; i < n; i++) { 332 | double x = args.ret.d[i]; 333 | double y = retValue.d[i]; 334 | 335 | if (x != y) { 336 | printf("[FAIL] dc[%u] %g != Expected(%g)\n", i, x, y); 337 | isOk = false; 338 | } 339 | } 340 | break; 341 | } 342 | 343 | if (isOk) 344 | printPass(body); 345 | else 346 | _succeeded = false; 347 | return isOk; 348 | } 349 | 350 | bool Test::failureTest(const char* body) { 351 | return true; 352 | } 353 | 354 | // ============================================================================ 355 | // [Main] 356 | // ============================================================================ 357 | 358 | int main(int argc, char* argv[]) { 359 | CmdLine cmd(argc, argv); 360 | uint32_t options = 0; 361 | 362 | if (cmd.hasKey("--verbose")) options |= mpsl::kOptionVerbose; 363 | if (cmd.hasKey("--ast" )) options |= mpsl::kOptionDebugAst; 364 | if (cmd.hasKey("--ir" )) options |= mpsl::kOptionDebugIR; 365 | if (cmd.hasKey("--asm" )) options |= mpsl::kOptionDebugASM; 366 | 367 | // Variables are initialized to these: 368 | // a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; 369 | // b[0] = 9; b[1] = 8; b[2] = 7; b[3] = 6; 370 | // c[0] =-2; c[1] =-3; c[2] =-4; c[3] =-5; 371 | Test test(options); 372 | 373 | // Test MPSL basics. 374 | test.basicTest("int main() { return ia + ib; }", mpsl::kTypeInt , makeIVal(10 )); 375 | test.basicTest("float main() { return fa + fb; }", mpsl::kTypeFloat , makeFVal(10.0f)); 376 | test.basicTest("double main() { return da + db; }", mpsl::kTypeDouble , makeDVal(10.0 )); 377 | 378 | test.basicTest("int2 main() { return i2a + i2b; }", mpsl::kTypeInt2 , makeIVal(10 , 10 )); 379 | test.basicTest("float2 main() { return f2a + f2b; }", mpsl::kTypeFloat2 , makeFVal(10.0f, 10.0f)); 380 | test.basicTest("double2 main() { return d2a + d2b; }", mpsl::kTypeDouble2, makeDVal(10.0 , 10.0 )); 381 | 382 | test.basicTest("int3 main() { return i3a + i3b; }", mpsl::kTypeInt3 , makeIVal(10 , 10 , 10 )); 383 | test.basicTest("float3 main() { return f3a + f3b; }", mpsl::kTypeFloat3 , makeFVal(10.0f, 10.0f, 10.0f)); 384 | test.basicTest("double3 main() { return d3a + d3b; }", mpsl::kTypeDouble3, makeDVal(10.0 , 10.0 , 10.0 )); 385 | 386 | test.basicTest("int4 main() { return i4a + i4b; }", mpsl::kTypeInt4 , makeIVal(10 , 10 , 10 , 10 )); 387 | test.basicTest("float4 main() { return f4a + f4b; }", mpsl::kTypeFloat4 , makeFVal(10.0f, 10.0f, 10.0f, 10.0f)); 388 | test.basicTest("double4 main() { return d4a + d4b; }", mpsl::kTypeDouble4, makeDVal(10.0 , 10.0 , 10.0 , 10.0 )); 389 | 390 | test.basicTest("int main() { return (ia + ib) * ic - ia; }", mpsl::kTypeInt , makeIVal(-21 )); 391 | test.basicTest("float main() { return (fa + fb) * fc - fa; }", mpsl::kTypeFloat , makeFVal(-21.0f)); 392 | test.basicTest("double main() { return (da + db) * dc - da; }", mpsl::kTypeDouble , makeDVal(-21.0 )); 393 | 394 | test.basicTest("int2 main() { return (i2a + i2b) * i2c - i2a; }", mpsl::kTypeInt2 , makeIVal(-21 , -32 )); 395 | test.basicTest("float2 main() { return (f2a + f2b) * f2c - f2a; }", mpsl::kTypeFloat2 , makeFVal(-21.0f, -32.0f)); 396 | test.basicTest("double2 main() { return (d2a + d2b) * d2c - d2a; }", mpsl::kTypeDouble2, makeDVal(-21.0 , -32.0 )); 397 | 398 | test.basicTest("int3 main() { return (i3a + i3b) * i3c - i3a; }", mpsl::kTypeInt3 , makeIVal(-21 , -32 , 37 )); 399 | test.basicTest("float3 main() { return (f3a + f3b) * f3c - f3a; }", mpsl::kTypeFloat3 , makeFVal(-21.0f, -32.0f, 37.0f)); 400 | test.basicTest("double3 main() { return (d3a + d3b) * d3c - d3a; }", mpsl::kTypeDouble3, makeDVal(-21.0 , -32.0 , 37.0 )); 401 | 402 | test.basicTest("int4 main() { return (i4a + i4b) * i4c - i4a; }", mpsl::kTypeInt4 , makeIVal(-21 , -32 , 37 , 46 )); 403 | test.basicTest("float4 main() { return (f4a + f4b) * f4c - f4a; }", mpsl::kTypeFloat4 , makeFVal(-21.0f, -32.0f, 37.0f, 46.0f)); 404 | test.basicTest("double4 main() { return (d4a + d4b) * d4c - d4a; }", mpsl::kTypeDouble4, makeDVal(-21.0 , -32.0 , 37.0 , 46.0 )); 405 | 406 | // Test vector swizzling. 407 | test.basicTest("int4 main() { return i4a.xxxx; }", mpsl::kTypeInt4 , makeIVal(1, 1, 1, 1)); 408 | test.basicTest("int4 main() { return i4a.xyxy; }", mpsl::kTypeInt4 , makeIVal(1, 2, 1, 2)); 409 | test.basicTest("float4 main() { return f4a.xxxx; }", mpsl::kTypeFloat4 , makeFVal(1, 1, 1, 1)); 410 | test.basicTest("float4 main() { return f4a.xyxy; }", mpsl::kTypeFloat4 , makeFVal(1, 2, 1, 2)); 411 | test.basicTest("double4 main() { return d4a.xxxx; }", mpsl::kTypeDouble4, makeDVal(1, 1, 1, 1)); 412 | test.basicTest("double4 main() { return d4a.xyxy; }", mpsl::kTypeDouble4, makeDVal(1, 2, 1, 2)); 413 | 414 | // Test control flow - branches. 415 | test.basicTest("int main() { if (ia == 1) return ib; else return ic; }", mpsl::kTypeInt, makeIVal( 9)); 416 | test.basicTest("int main() { if (ia != 1) return ib; else return ic; }", mpsl::kTypeInt, makeIVal(-2)); 417 | test.basicTest("int main() { if (ia >= 1) return ib; else return ic; }", mpsl::kTypeInt, makeIVal( 9)); 418 | test.basicTest("int main() { if (ia > 1) return ib; else return ic; }", mpsl::kTypeInt, makeIVal(-2)); 419 | test.basicTest("int main() { if (ia <= 1) return ib; else return ic; }", mpsl::kTypeInt, makeIVal( 9)); 420 | test.basicTest("int main() { if (ia < 1) return ib; else return ic; }", mpsl::kTypeInt, makeIVal(-2)); 421 | 422 | /* 423 | // Test creating and calling functions inside the shader. 424 | test.basicTest("int dummy(int a, int b) { return a + b; }\n" 425 | "int main() { return dummy(1, 2); }\n", 426 | mpsl::kTypeInt, makeIVal(3)); 427 | 428 | test.basicTest("int dummy(int a, int b) { return a + b; }\n" 429 | "int main() { return dummy(ia, ib); }\n", 430 | mpsl::kTypeInt, makeIVal(10)); 431 | 432 | test.basicTest("int xFunc(int a, int b) { return a + b; }\n" 433 | "int yFunc(int a, int b) { return xFunc(a, b); }\n" 434 | "int main() { return yFunc(ia, ib); }\n", 435 | mpsl::kTypeInt, makeIVal(10)); 436 | 437 | test.basicTest("int xFunc(int a, int b) { return a + b; }\n" 438 | "int yFunc(int a, int b) { return xFunc(a, b); }\n" 439 | "int main() { return xFunc(ia, ib) + yFunc(ia, ic) + yFunc(ib, ic); }\n", 440 | mpsl::kTypeInt, makeIVal(16)); 441 | */ 442 | return test._succeeded ? 0 : 1; 443 | } 444 | -------------------------------------------------------------------------------- /test/mp_tutorial.cpp: -------------------------------------------------------------------------------- 1 | // [MPSL-Test] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | // [Dependencies] 8 | #include "./mpsl.h" 9 | #include "./mp_utils.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | struct Args { 16 | double a, b; 17 | float c; 18 | double result; 19 | }; 20 | 21 | int main(int argc, char* argv[]) { 22 | mpsl::Context ctx = mpsl::Context::create(); 23 | mpsl::LayoutTmp<> args; 24 | 25 | args.addMember("a" , mpsl::kTypeDouble | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, a)); 26 | args.addMember("b" , mpsl::kTypeDouble | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, b)); 27 | args.addMember("c" , mpsl::kTypeFloat | mpsl::kTypeRO, MPSL_OFFSET_OF(Args, c)); 28 | args.addMember("@ret", mpsl::kTypeDouble | mpsl::kTypeWO, MPSL_OFFSET_OF(Args, result)); 29 | 30 | const char body[] = 31 | "double main() {\n" 32 | " double x = sqrt(a * b) * c;\n" 33 | " return ++x;\n" 34 | "}\n"; 35 | printf("[Program]\n%s\n", body); 36 | 37 | uint32_t options = 38 | mpsl::kOptionVerbose | 39 | mpsl::kOptionDebugAst | 40 | mpsl::kOptionDebugIR | 41 | mpsl::kOptionDebugASM ; 42 | TestLog log; 43 | 44 | mpsl::Program1<> program; 45 | mpsl::Error err = program.compile(ctx, body, options, args, &log); 46 | if (err) { 47 | printf("Compilation failed: ERROR 0x%08X\n", err); 48 | } 49 | else { 50 | Args args; 51 | args.a = 4.0; 52 | args.b = 16.0; 53 | args.c = 0.5f; 54 | 55 | err = program.run(&args); 56 | if (err == mpsl::kErrorOk) 57 | printf("Return=%.17g\n", args.result); 58 | else 59 | printf("Execution failed: ERROR %08X\n", static_cast(err)); 60 | } 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /test/mp_utils.h: -------------------------------------------------------------------------------- 1 | // [MPSL-Test] 2 | // MathPresso's Shading Language with JIT Engine for C++. 3 | // 4 | // [License] 5 | // Zlib - See LICENSE.md file in the package. 6 | 7 | #ifndef _TEST_MP_UTILS_H 8 | #define _TEST_MP_UTILS_H 9 | 10 | #include "./mpsl.h" 11 | 12 | struct TestLog : public mpsl::OutputLog { 13 | virtual void log(const Message& msg) noexcept { 14 | // TYPE - Message type - Error, warning, debug, or dump. 15 | // LINE - Line number of the code related to the message (if known). 16 | // COLUMN - Column of the code related to the message (if known). 17 | // HEADER - Message or dump header, can be understood as a message type 18 | // with a bit more information than just the type. If the message 19 | // is a dump then the header is a dump type. 20 | // CONTENT - Message or dump content. If it's just a message it always 21 | // contains just a single line. Dumps are typically multiline. 22 | printf("%s", msg.header().data()); 23 | 24 | if (!msg.isDump()) { 25 | printf(" %s", msg.content().data()); 26 | if (msg.hasPosition()) 27 | printf(" at [%u:%u]", msg.line(), msg.column()); 28 | printf("\n"); 29 | } 30 | else { 31 | printf("\n%s\n", msg.content().data()); 32 | } 33 | } 34 | }; 35 | 36 | #endif // _TEST_MP_UTILS_H 37 | -------------------------------------------------------------------------------- /test/mpsl.h: -------------------------------------------------------------------------------- 1 | #include "../src/mpsl/mpsl.h" 2 | -------------------------------------------------------------------------------- /tools/configure-ninja.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ -z "${ASMJIT_DIR}" ]; then 4 | ASMJIT_DIR="../../asmjit" 5 | fi 6 | 7 | CURRENT_DIR=`pwd` 8 | BUILD_DIR="build" 9 | BUILD_OPTIONS="-DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DASMJIT_DIR=\"${ASMJIT_DIR}\" -DMPSL_TEST=1" 10 | 11 | mkdir -p ../${BUILD_DIR}_dbg 12 | mkdir -p ../${BUILD_DIR}_rel 13 | 14 | cd ../${BUILD_DIR}_dbg 15 | eval cmake .. -G"Ninja" -DCMAKE_BUILD_TYPE=Debug ${BUILD_OPTIONS} 16 | cd ${CURRENT_DIR} 17 | 18 | cd ../${BUILD_DIR}_rel 19 | eval cmake .. -G"Ninja" -DCMAKE_BUILD_TYPE=Release ${BUILD_OPTIONS} 20 | cd ${CURRENT_DIR} 21 | -------------------------------------------------------------------------------- /tools/configure-vs-x64.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set MPSL_CURRENT_DIR=%CD% 4 | set MPSL_BUILD_DIR="build_vs_x64" 5 | 6 | mkdir ..\%MPSL_BUILD_DIR% 7 | cd ..\%MPSL_BUILD_DIR% 8 | cmake .. -G"Visual Studio 16" -A x64 -DMPSL_TEST=1 9 | cd %MPSL_CURRENT_DIR% 10 | -------------------------------------------------------------------------------- /tools/configure-vs-x86.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set MPSL_CURRENT_DIR=%CD% 4 | set MPSL_BUILD_DIR="build_vs_x86" 5 | 6 | mkdir ..\%MPSL_BUILD_DIR% 7 | cd ..\%MPSL_BUILD_DIR% 8 | cmake .. -G"Visual Studio 16" -A Win32 -DMPSL_TEST=1 9 | cd %MPSL_CURRENT_DIR% 10 | -------------------------------------------------------------------------------- /tools/configure-xcode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CURRENT_DIR=`pwd` 4 | BUILD_DIR="build_xcode" 5 | ASMJIT_DIR="../../asmjit" 6 | 7 | mkdir -p ../${BUILD_DIR} 8 | cd ../${BUILD_DIR} 9 | cmake .. -G"Xcode" -DASMJIT_DIR="${ASMJIT_DIR}" -DMPSL_TEST=1 10 | cd ${CURRENT_DIR} 11 | --------------------------------------------------------------------------------