| author | |
| committer | |
| log | b5a36f676b1fd69f195d9f1eb6e35f3eb2f15946 |
| tree | 92ce1ffa64bc2d32d5ef73dce66daf3c8e893ee6 |
| parent | d6d05fc84d33c71434a1f8bae51ca5956e08cdf0 |
| parent | f2d374e8465042fa5cb6bf2be7b9b086948f3a94 |
Conflicts:
cmake/Findllvm.cmake
The llvm11 branch changed 10's to 11's and master branch added the
"using LLVM_CONFIG_EXE" help message, so the resolution was to merge
these changes together.
I also added a check to make sure LLVM is built with AVR enabled, which
is no longer an experimental target.78 files changed, 5719 insertions(+), 3111 deletions(-)
CMakeLists.txt+24-18| ... | @@ -27,23 +27,26 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) | ... | @@ -27,23 +27,26 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) |
| 27 | set(ZIG_VERSION_MAJOR 0) | 27 | set(ZIG_VERSION_MAJOR 0) |
| 28 | set(ZIG_VERSION_MINOR 6) | 28 | set(ZIG_VERSION_MINOR 6) |
| 29 | set(ZIG_VERSION_PATCH 0) | 29 | set(ZIG_VERSION_PATCH 0) |
| 30 | set(ZIG_VERSION "${ZIG_VERSION_MAJOR}.${ZIG_VERSION_MINOR}.${ZIG_VERSION_PATCH}") | 30 | set(ZIG_VERSION "" CACHE STRING "Override Zig version string. Default is to find out with git.") |
| 31 | 31 | ||
| 32 | find_program(GIT_EXE NAMES git) | 32 | if("${ZIG_VERSION}" STREQUAL "") |
| 33 | if(GIT_EXE) | 33 | set(ZIG_VERSION "${ZIG_VERSION_MAJOR}.${ZIG_VERSION_MINOR}.${ZIG_VERSION_PATCH}") |
| 34 | execute_process( | 34 | find_program(GIT_EXE NAMES git) |
| 35 | COMMAND ${GIT_EXE} -C ${CMAKE_SOURCE_DIR} name-rev HEAD --tags --name-only --no-undefined --always | 35 | if(GIT_EXE) |
| 36 | RESULT_VARIABLE EXIT_STATUS | 36 | execute_process( |
| 37 | OUTPUT_VARIABLE ZIG_GIT_REV | 37 | COMMAND ${GIT_EXE} -C ${CMAKE_SOURCE_DIR} name-rev HEAD --tags --name-only --no-undefined --always |
| 38 | OUTPUT_STRIP_TRAILING_WHITESPACE | 38 | RESULT_VARIABLE EXIT_STATUS |
| 39 | ERROR_QUIET) | 39 | OUTPUT_VARIABLE ZIG_GIT_REV |
| 40 | if(EXIT_STATUS EQUAL "0") | 40 | OUTPUT_STRIP_TRAILING_WHITESPACE |
| 41 | if(ZIG_GIT_REV MATCHES "\\^0$") | 41 | ERROR_QUIET) |
| 42 | if(NOT("${ZIG_GIT_REV}" STREQUAL "${ZIG_VERSION}^0")) | 42 | if(EXIT_STATUS EQUAL "0") |
| 43 | message("WARNING: Tag does not match configured Zig version") | 43 | if(ZIG_GIT_REV MATCHES "\\^0$") |
| 44 | if(NOT("${ZIG_GIT_REV}" STREQUAL "${ZIG_VERSION}^0")) | ||
| 45 | message("WARNING: Tag does not match configured Zig version") | ||
| 46 | endif() | ||
| 47 | else() | ||
| 48 | set(ZIG_VERSION "${ZIG_VERSION}+${ZIG_GIT_REV}") | ||
| 44 | endif() | 49 | endif() |
| 45 | else() | ||
| 46 | set(ZIG_VERSION "${ZIG_VERSION}+${ZIG_GIT_REV}") | ||
| 47 | endif() | 50 | endif() |
| 48 | endif() | 51 | endif() |
| 49 | endif() | 52 | endif() |
| ... | @@ -63,6 +66,9 @@ endif() | ... | @@ -63,6 +66,9 @@ endif() |
| 63 | 66 | ||
| 64 | if(ZIG_STATIC) | 67 | if(ZIG_STATIC) |
| 65 | set(ZIG_STATIC_LLVM "on") | 68 | set(ZIG_STATIC_LLVM "on") |
| 69 | set(ZIG_LINK_MODE "Static") | ||
| 70 | else() | ||
| 71 | set(ZIG_LINK_MODE "Dynamic") | ||
| 66 | endif() | 72 | endif() |
| 67 | 73 | ||
| 68 | string(REGEX REPLACE "\\\\" "\\\\\\\\" ZIG_LIBC_LIB_DIR_ESCAPED "${ZIG_LIBC_LIB_DIR}") | 74 | string(REGEX REPLACE "\\\\" "\\\\\\\\" ZIG_LIBC_LIB_DIR_ESCAPED "${ZIG_LIBC_LIB_DIR}") |
| ... | @@ -74,6 +80,7 @@ option(ZIG_TEST_COVERAGE "Build Zig with test coverage instrumentation" OFF) | ... | @@ -74,6 +80,7 @@ option(ZIG_TEST_COVERAGE "Build Zig with test coverage instrumentation" OFF) |
| 74 | set(ZIG_TARGET_TRIPLE "native" CACHE STRING "arch-os-abi to output binaries for") | 80 | set(ZIG_TARGET_TRIPLE "native" CACHE STRING "arch-os-abi to output binaries for") |
| 75 | set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries for") | 81 | set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries for") |
| 76 | set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary") | 82 | set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary") |
| 83 | set(ZIG_PREFER_LLVM_CONFIG off CACHE BOOL "(when cross compiling) use llvm-config to find target llvm dependencies if needed") | ||
| 77 | 84 | ||
| 78 | find_package(llvm) | 85 | find_package(llvm) |
| 79 | find_package(clang) | 86 | find_package(clang) |
| ... | @@ -257,7 +264,6 @@ target_include_directories(embedded_softfloat PUBLIC | ... | @@ -257,7 +264,6 @@ target_include_directories(embedded_softfloat PUBLIC |
| 257 | ) | 264 | ) |
| 258 | include_directories("${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/include") | 265 | include_directories("${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/include") |
| 259 | set(SOFTFLOAT_LIBRARIES embedded_softfloat) | 266 | set(SOFTFLOAT_LIBRARIES embedded_softfloat) |
| 260 | include_directories("${CMAKE_SOURCE_DIR}/deps/dbg-macro") | ||
| 261 | 267 | ||
| 262 | find_package(Threads) | 268 | find_package(Threads) |
| 263 | 269 | ||
| ... | @@ -487,7 +493,7 @@ if("${ZIG_TARGET_TRIPLE}" STREQUAL "native") | ... | @@ -487,7 +493,7 @@ if("${ZIG_TARGET_TRIPLE}" STREQUAL "native") |
| 487 | endif() | 493 | endif() |
| 488 | else() | 494 | else() |
| 489 | add_custom_target(zig_build_zig1 ALL | 495 | add_custom_target(zig_build_zig1 ALL |
| 490 | COMMAND "${ZIG_EXECUTABLE}" ${BUILD_ZIG1_ARGS} | 496 | COMMAND "${ZIG_EXECUTABLE}" "build-obj" ${BUILD_ZIG1_ARGS} |
| 491 | BYPRODUCTS "${ZIG1_OBJECT}" | 497 | BYPRODUCTS "${ZIG1_OBJECT}" |
| 492 | COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}" | 498 | COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}" |
| 493 | WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" | 499 | WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" |
README.md-1| ... | @@ -37,7 +37,6 @@ This step must be repeated when you make changes to any of the C++ source code. | ... | @@ -37,7 +37,6 @@ This step must be repeated when you make changes to any of the C++ source code. |
| 37 | 37 | ||
| 38 | * cmake >= 3.15.3 | 38 | * cmake >= 3.15.3 |
| 39 | * Microsoft Visual Studio. Supported versions: | 39 | * Microsoft Visual Studio. Supported versions: |
| 40 | - 2015 (version 14) | ||
| 41 | - 2017 (version 15.8) | 40 | - 2017 (version 15.8) |
| 42 | - 2019 (version 16) | 41 | - 2019 (version 16) |
| 43 | * LLVM, Clang, LLD development libraries == 11.x | 42 | * LLVM, Clang, LLD development libraries == 11.x |
ci/azure/pipelines.yml+2-12| ... | @@ -28,20 +28,10 @@ jobs: | ... | @@ -28,20 +28,10 @@ jobs: |
| 28 | - job: BuildWindows | 28 | - job: BuildWindows |
| 29 | pool: | 29 | pool: |
| 30 | vmImage: 'windows-2019' | 30 | vmImage: 'windows-2019' |
| 31 | strategy: | ||
| 32 | matrix: | ||
| 33 | mingw64: | ||
| 34 | CHERE_INVOKING: yes | ||
| 35 | MSYSTEM: MINGW64 | ||
| 36 | SCRIPT: '%CD:~0,2%\msys64\usr\bin\bash -lc "bash ci/azure/windows_mingw_script"' | ||
| 37 | msvc: | ||
| 38 | SCRIPT: ci/azure/windows_msvc_script.bat | ||
| 39 | |||
| 40 | timeoutInMinutes: 360 | 31 | timeoutInMinutes: 360 |
| 41 | |||
| 42 | steps: | 32 | steps: |
| 43 | - powershell: | | 33 | - powershell: | |
| 44 | (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2020-07-20/msys2-base-x86_64-20200720.sfx.exe", "sfx.exe") | 34 | (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2020-09-03/msys2-base-x86_64-20200903.sfx.exe", "sfx.exe") |
| 45 | .\sfx.exe -y -o\ | 35 | .\sfx.exe -y -o\ |
| 46 | del sfx.exe | 36 | del sfx.exe |
| 47 | displayName: Download/Extract/Install MSYS2 | 37 | displayName: Download/Extract/Install MSYS2 |
| ... | @@ -57,7 +47,7 @@ jobs: | ... | @@ -57,7 +47,7 @@ jobs: |
| 57 | - task: DownloadSecureFile@1 | 47 | - task: DownloadSecureFile@1 |
| 58 | inputs: | 48 | inputs: |
| 59 | secureFile: s3cfg | 49 | secureFile: s3cfg |
| 60 | - script: $(SCRIPT) | 50 | - script: ci/azure/windows_msvc_script.bat |
| 61 | name: main | 51 | name: main |
| 62 | displayName: 'Build and test' | 52 | displayName: 'Build and test' |
| 63 | - job: OnMasterSuccess | 53 | - job: OnMasterSuccess |
ci/azure/windows_mingw_script deleted-28| ... | @@ -1,28 +0,0 @@ | ||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | set -x | ||
| 4 | set -e | ||
| 5 | |||
| 6 | pacman --noconfirm --needed -S git base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-clang mingw-w64-x86_64-lld mingw-w64-x86_64-llvm | ||
| 7 | |||
| 8 | git config core.abbrev 9 | ||
| 9 | |||
| 10 | # Git is wrong for autocrlf being enabled by default on Windows. | ||
| 11 | # git is mangling files on Windows by default. | ||
| 12 | # This is the second bug I've tracked down to being caused by autocrlf. | ||
| 13 | git config core.autocrlf false | ||
| 14 | # Too late; the files are already mangled. | ||
| 15 | git checkout . | ||
| 16 | |||
| 17 | ZIGBUILDDIR="$(pwd)/build" | ||
| 18 | PREFIX="$ZIGBUILDDIR/dist" | ||
| 19 | CMAKEFLAGS="-DCMAKE_COLOR_MAKEFILE=OFF -DCMAKE_INSTALL_PREFIX=$PREFIX -DZIG_STATIC=ON" | ||
| 20 | |||
| 21 | mkdir $ZIGBUILDDIR | ||
| 22 | cd $ZIGBUILDDIR | ||
| 23 | |||
| 24 | cmake .. -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=RelWithDebInfo $CMAKEFLAGS -DCMAKE_EXE_LINKER_FLAGS='-fuse-ld=lld -Wl,/debug,/pdb:zig.pdb' | ||
| 25 | |||
| 26 | make -j$(nproc) install | ||
| 27 | |||
| 28 | ./zig build test-behavior -Dskip-non-native -Dskip-release | ||
ci/azure/windows_msvc_install+1-6| ... | @@ -4,12 +4,7 @@ set -x | ... | @@ -4,12 +4,7 @@ set -x |
| 4 | set -e | 4 | set -e |
| 5 | 5 | ||
| 6 | pacman -Su --needed --noconfirm | 6 | pacman -Su --needed --noconfirm |
| 7 | 7 | pacman -S --needed --noconfirm wget p7zip python3-pip tar xz | |
| 8 | # Uncomment when https://github.com/msys2/MSYS2-packages/issues/2050 is fixed | ||
| 9 | #pacman -S --needed --noconfirm wget p7zip python3-pip tar xz | ||
| 10 | pacman -S --needed --noconfirm wget p7zip tar xz | ||
| 11 | pacman -U --noconfirm http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-python-3.8.4-1-any.pkg.tar.zst | ||
| 12 | pacman -U --noconfirm http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-python-pip-20.0.2-1-any.pkg.tar.xz | ||
| 13 | 8 | ||
| 14 | pip install s3cmd | 9 | pip install s3cmd |
| 15 | wget -nv "https://ziglang.org/deps/llvm%2bclang%2blld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz" | 10 | wget -nv "https://ziglang.org/deps/llvm%2bclang%2blld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz" |
cmake/Findllvm.cmake+7-7| ... | @@ -32,7 +32,7 @@ if(ZIG_PREFER_CLANG_CPP_DYLIB) | ... | @@ -32,7 +32,7 @@ if(ZIG_PREFER_CLANG_CPP_DYLIB) |
| 32 | /usr/local/llvm11/lib | 32 | /usr/local/llvm11/lib |
| 33 | /usr/local/llvm110/lib | 33 | /usr/local/llvm110/lib |
| 34 | ) | 34 | ) |
| 35 | elseif("${ZIG_TARGET_TRIPLE}" STREQUAL "native") | 35 | elseif(("${ZIG_TARGET_TRIPLE}" STREQUAL "native") OR ZIG_PREFER_LLVM_CONFIG) |
| 36 | find_program(LLVM_CONFIG_EXE | 36 | find_program(LLVM_CONFIG_EXE |
| 37 | NAMES llvm-config-11 llvm-config-11.0 llvm-config110 llvm-config11 llvm-config | 37 | NAMES llvm-config-11 llvm-config-11.0 llvm-config110 llvm-config11 llvm-config |
| 38 | PATHS | 38 | PATHS |
| ... | @@ -55,13 +55,13 @@ elseif("${ZIG_TARGET_TRIPLE}" STREQUAL "native") | ... | @@ -55,13 +55,13 @@ elseif("${ZIG_TARGET_TRIPLE}" STREQUAL "native") |
| 55 | OUTPUT_STRIP_TRAILING_WHITESPACE) | 55 | OUTPUT_STRIP_TRAILING_WHITESPACE) |
| 56 | 56 | ||
| 57 | if("${LLVM_CONFIG_VERSION}" VERSION_LESS 11) | 57 | if("${LLVM_CONFIG_VERSION}" VERSION_LESS 11) |
| 58 | message(FATAL_ERROR "expected LLVM 11.x but found ${LLVM_CONFIG_VERSION}") | 58 | message(FATAL_ERROR "expected LLVM 11.x but found ${LLVM_CONFIG_VERSION} using ${LLVM_CONFIG_EXE}") |
| 59 | endif() | 59 | endif() |
| 60 | if("${LLVM_CONFIG_VERSION}" VERSION_EQUAL 12) | 60 | if("${LLVM_CONFIG_VERSION}" VERSION_EQUAL 12) |
| 61 | message(FATAL_ERROR "expected LLVM 11.x but found ${LLVM_CONFIG_VERSION}") | 61 | message(FATAL_ERROR "expected LLVM 11.x but found ${LLVM_CONFIG_VERSION} using ${LLVM_CONFIG_EXE}") |
| 62 | endif() | 62 | endif() |
| 63 | if("${LLVM_CONFIG_VERSION}" VERSION_GREATER 11) | 63 | if("${LLVM_CONFIG_VERSION}" VERSION_GREATER 11) |
| 64 | message(FATAL_ERROR "expected LLVM 11.x but found ${LLVM_CONFIG_VERSION}") | 64 | message(FATAL_ERROR "expected LLVM 11.x but found ${LLVM_CONFIG_VERSION} using ${LLVM_CONFIG_EXE}") |
| 65 | endif() | 65 | endif() |
| 66 | 66 | ||
| 67 | execute_process( | 67 | execute_process( |
| ... | @@ -72,12 +72,13 @@ elseif("${ZIG_TARGET_TRIPLE}" STREQUAL "native") | ... | @@ -72,12 +72,13 @@ elseif("${ZIG_TARGET_TRIPLE}" STREQUAL "native") |
| 72 | function(NEED_TARGET TARGET_NAME) | 72 | function(NEED_TARGET TARGET_NAME) |
| 73 | list (FIND LLVM_TARGETS_BUILT "${TARGET_NAME}" _index) | 73 | list (FIND LLVM_TARGETS_BUILT "${TARGET_NAME}" _index) |
| 74 | if (${_index} EQUAL -1) | 74 | if (${_index} EQUAL -1) |
| 75 | message(FATAL_ERROR "LLVM is missing target ${TARGET_NAME}. Zig requires LLVM to be built with all default targets enabled.") | 75 | message(FATAL_ERROR "LLVM (according to ${LLVM_CONFIG_EXE}) is missing target ${TARGET_NAME}. Zig requires LLVM to be built with all default targets enabled.") |
| 76 | endif() | 76 | endif() |
| 77 | endfunction(NEED_TARGET) | 77 | endfunction(NEED_TARGET) |
| 78 | NEED_TARGET("AArch64") | 78 | NEED_TARGET("AArch64") |
| 79 | NEED_TARGET("AMDGPU") | 79 | NEED_TARGET("AMDGPU") |
| 80 | NEED_TARGET("ARM") | 80 | NEED_TARGET("ARM") |
| 81 | NEED_TARGET("AVR") | ||
| 81 | NEED_TARGET("BPF") | 82 | NEED_TARGET("BPF") |
| 82 | NEED_TARGET("Hexagon") | 83 | NEED_TARGET("Hexagon") |
| 83 | NEED_TARGET("Lanai") | 84 | NEED_TARGET("Lanai") |
| ... | @@ -141,8 +142,7 @@ elseif("${ZIG_TARGET_TRIPLE}" STREQUAL "native") | ... | @@ -141,8 +142,7 @@ elseif("${ZIG_TARGET_TRIPLE}" STREQUAL "native") |
| 141 | link_directories("${LLVM_LIBDIRS}") | 142 | link_directories("${LLVM_LIBDIRS}") |
| 142 | else() | 143 | else() |
| 143 | # Here we assume that we're cross compiling with Zig, of course. No reason | 144 | # Here we assume that we're cross compiling with Zig, of course. No reason |
| 144 | # to support more complicated setups. We also assume the experimental target | 145 | # to support more complicated setups. |
| 145 | # AVR is enabled. | ||
| 146 | 146 | ||
| 147 | macro(FIND_AND_ADD_LLVM_LIB _libname_) | 147 | macro(FIND_AND_ADD_LLVM_LIB _libname_) |
| 148 | string(TOUPPER ${_libname_} _prettylibname_) | 148 | string(TOUPPER ${_libname_} _prettylibname_) |
deps/dbg-macro/LICENSE deleted-21| ... | @@ -1,21 +0,0 @@ | ||
| 1 | MIT License | ||
| 2 | |||
| 3 | Copyright (c) 2019 David Peter <mail@david-peter.de> | ||
| 4 | |||
| 5 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 6 | of this software and associated documentation files (the "Software"), to deal | ||
| 7 | in the Software without restriction, including without limitation the rights | ||
| 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 9 | copies of the Software, and to permit persons to whom the Software is | ||
| 10 | furnished to do so, subject to the following conditions: | ||
| 11 | |||
| 12 | The above copyright notice and this permission notice shall be included in all | ||
| 13 | copies or substantial portions of the Software. | ||
| 14 | |||
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 21 | SOFTWARE. | ||
deps/dbg-macro/README.md deleted-172| ... | @@ -1,172 +0,0 @@ | ||
| 1 | # `dbg(…)` | ||
| 2 | |||
| 3 | [](https://travis-ci.org/sharkdp/dbg-macro) [](https://ci.appveyor.com/project/sharkdp/dbg-macro) [](https://repl.it/@sharkdp/dbg-macro-demo) [](dbg.h) | ||
| 4 | |||
| 5 | *A macro for `printf`-style debugging fans.* | ||
| 6 | |||
| 7 | Debuggers are great. But sometimes you just don't have the time or patience to set | ||
| 8 | up everything correctly and just want a quick way to inspect some values at runtime. | ||
| 9 | |||
| 10 | This projects provides a [single header file](dbg.h) with a `dbg(…)` | ||
| 11 | macro that can be used in all circumstances where you would typically write | ||
| 12 | `printf("…", …)` or `std::cout << …`. But it comes with a few extras. | ||
| 13 | |||
| 14 | ## Examples | ||
| 15 | |||
| 16 | ``` c++ | ||
| 17 | #include <vector> | ||
| 18 | #include <dbg.h> | ||
| 19 | |||
| 20 | // You can use "dbg(..)" in expressions: | ||
| 21 | int factorial(int n) { | ||
| 22 | if (dbg(n <= 1)) { | ||
| 23 | return dbg(1); | ||
| 24 | } else { | ||
| 25 | return dbg(n * factorial(n - 1)); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | int main() { | ||
| 30 | std::string message = "hello"; | ||
| 31 | dbg(message); // [example.cpp:15 (main)] message = "hello" (std::string) | ||
| 32 | |||
| 33 | const int a = 2; | ||
| 34 | const int b = dbg(3 * a) + 1; // [example.cpp:18 (main)] 3 * a = 6 (int) | ||
| 35 | |||
| 36 | std::vector<int> numbers{b, 13, 42}; | ||
| 37 | dbg(numbers); // [example.cpp:21 (main)] numbers = {7, 13, 42} (size: 3) (std::vector<int>) | ||
| 38 | |||
| 39 | dbg("this line is executed"); // [example.cpp:23 (main)] this line is executed | ||
| 40 | |||
| 41 | factorial(4); | ||
| 42 | |||
| 43 | return 0; | ||
| 44 | } | ||
| 45 | ``` | ||
| 46 | |||
| 47 | The code above produces this output ([try it yourself](https://repl.it/@sharkdp/dbg-macro-demo)): | ||
| 48 | |||
| 49 |  | ||
| 50 | |||
| 51 | ## Features | ||
| 52 | |||
| 53 | * Easy to read, colorized output (colors auto-disable when the output is not an interactive terminal) | ||
| 54 | * Prints file name, line number, function name and the original expression | ||
| 55 | * Adds type information for the printed-out value | ||
| 56 | * Specialized pretty-printers for containers, pointers, string literals, enums, `std::optional`, etc. | ||
| 57 | * Can be used inside expressions (passing through the original value) | ||
| 58 | * The `dbg.h` header issues a compiler warning when included (so you don't forget to remove it). | ||
| 59 | * Compatible and tested with C++11, C++14 and C++17. | ||
| 60 | |||
| 61 | ## Installation | ||
| 62 | |||
| 63 | To make this practical, the `dbg.h` header should to be readily available from all kinds of different | ||
| 64 | places and in all kinds of environments. The quick & dirty way is to actually copy the header file | ||
| 65 | to `/usr/include` or to clone the repository and symlink `dbg.h` to `/usr/include/dbg.h`. | ||
| 66 | ``` bash | ||
| 67 | git clone https://github.com/sharkdp/dbg-macro | ||
| 68 | sudo ln -s $(readlink -f dbg-macro/dbg.h) /usr/include/dbg.h | ||
| 69 | ``` | ||
| 70 | If you don't want to make untracked changes to your filesystem, check below if there is a package for | ||
| 71 | your operating system or package manager. | ||
| 72 | |||
| 73 | ### On Arch Linux | ||
| 74 | |||
| 75 | You can install [`dbg-macro` from the AUR](https://aur.archlinux.org/packages/dbg-macro/): | ||
| 76 | ``` bash | ||
| 77 | yay -S dbg-macro | ||
| 78 | ``` | ||
| 79 | |||
| 80 | ### With vcpkg | ||
| 81 | |||
| 82 | You can install the [`dbg-macro` port](https://github.com/microsoft/vcpkg/tree/master/ports/dbg-macro) via: | ||
| 83 | ``` bash | ||
| 84 | vcpkg install dbg-macro | ||
| 85 | ``` | ||
| 86 | |||
| 87 | ## Configuration | ||
| 88 | |||
| 89 | * Set the `DBG_MACRO_DISABLE` flag to disable the `dbg(…)` macro (i.e. to make it a no-op). | ||
| 90 | * Set the `DBG_MACRO_NO_WARNING` flag to disable the *"'dbg.h' header is included in your code base"* warnings. | ||
| 91 | |||
| 92 | ## Advanced features | ||
| 93 | |||
| 94 | ### Hexadecimal, octal and binary format | ||
| 95 | |||
| 96 | If you want to format integers in hexadecimal, octal or binary representation, you can | ||
| 97 | simply wrap them in `dbg::hex(…)`, `dbg::oct(…)` or `dbg::bin(…)`: | ||
| 98 | ```c++ | ||
| 99 | const uint32_t secret = 12648430; | ||
| 100 | dbg(dbg::hex(secret)); | ||
| 101 | ``` | ||
| 102 | |||
| 103 | ### Printing type names | ||
| 104 | |||
| 105 | `dbg(…)` already prints the type for each value in parenthesis (see screenshot above). But | ||
| 106 | sometimes you just want to print a type (maybe because you don't have a value for that type). | ||
| 107 | In this case, you can use the `dbg::type<T>()` helper to pretty-print a given type `T`. | ||
| 108 | For example: | ||
| 109 | ```c++ | ||
| 110 | template <typename T> | ||
| 111 | void my_function_template() { | ||
| 112 | using MyDependentType = typename std::remove_reference<T>::type&&; | ||
| 113 | dbg(dbg::type<MyDependentType>()); | ||
| 114 | } | ||
| 115 | ``` | ||
| 116 | |||
| 117 | ### Print the current time | ||
| 118 | |||
| 119 | To print a timestamp, you can use the `dbg::time()` helper: | ||
| 120 | ```c++ | ||
| 121 | dbg(dbg::time()); | ||
| 122 | ``` | ||
| 123 | |||
| 124 | ### Customization | ||
| 125 | |||
| 126 | If you want `dbg(…)` to work for your custom datatype, you can simply overload `operator<<` for | ||
| 127 | `std::ostream&`: | ||
| 128 | ```c++ | ||
| 129 | std::ostream& operator<<(std::ostream& out, const user_defined_type& v) { | ||
| 130 | out << "…"; | ||
| 131 | return out; | ||
| 132 | } | ||
| 133 | ``` | ||
| 134 | |||
| 135 | If you want to modify the type name that is printed by `dbg(…)`, you can add a custom | ||
| 136 | `get_type_name` overload: | ||
| 137 | ```c++ | ||
| 138 | // Customization point for type information | ||
| 139 | namespace dbg { | ||
| 140 | std::string get_type_name(type_tag<bool>) { | ||
| 141 | return "truth value"; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | ``` | ||
| 145 | |||
| 146 | ## Development | ||
| 147 | |||
| 148 | If you want to contribute to `dbg-macro`, here is how you can build the tests and demos: | ||
| 149 | |||
| 150 | Make sure that the submodule(s) are up to date: | ||
| 151 | ```bash | ||
| 152 | git submodule update --init | ||
| 153 | ``` | ||
| 154 | |||
| 155 | Then, use the typical `cmake` workflow. Usage of `-DCMAKE_CXX_STANDARD=17` is optional, | ||
| 156 | but recommended in order to have the largest set of features enabled: | ||
| 157 | ```bash | ||
| 158 | mkdir build | ||
| 159 | cd build | ||
| 160 | cmake .. -DCMAKE_CXX_STANDARD=17 | ||
| 161 | make | ||
| 162 | ``` | ||
| 163 | |||
| 164 | To run the tests, simply call: | ||
| 165 | ```bash | ||
| 166 | make test | ||
| 167 | ``` | ||
| 168 | You can find the unit tests in `tests/basic.cpp`. | ||
| 169 | |||
| 170 | ## Acknowledgement | ||
| 171 | |||
| 172 | This project is inspired by Rusts [`dbg!(…)` macro](https://doc.rust-lang.org/std/macro.dbg.html). | ||
deps/dbg-macro/dbg.h deleted-711| ... | @@ -1,711 +0,0 @@ | ||
| 1 | /***************************************************************************** | ||
| 2 | |||
| 3 | dbg(...) macro | ||
| 4 | |||
| 5 | License (MIT): | ||
| 6 | |||
| 7 | Copyright (c) 2019 David Peter <mail@david-peter.de> | ||
| 8 | |||
| 9 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 10 | of this software and associated documentation files (the "Software"), to | ||
| 11 | deal in the Software without restriction, including without limitation the | ||
| 12 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| 13 | sell copies of the Software, and to permit persons to whom the Software is | ||
| 14 | furnished to do so, subject to the following conditions: | ||
| 15 | |||
| 16 | The above copyright notice and this permission notice shall be included in | ||
| 17 | all copies or substantial portions of the Software. | ||
| 18 | |||
| 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
| 22 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 25 | SOFTWARE. | ||
| 26 | |||
| 27 | *****************************************************************************/ | ||
| 28 | |||
| 29 | #ifndef DBG_MACRO_DBG_H | ||
| 30 | #define DBG_MACRO_DBG_H | ||
| 31 | |||
| 32 | #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) | ||
| 33 | #define DBG_MACRO_UNIX | ||
| 34 | #elif defined(_MSC_VER) | ||
| 35 | #define DBG_MACRO_WINDOWS | ||
| 36 | #endif | ||
| 37 | |||
| 38 | #ifndef DBG_MACRO_NO_WARNING | ||
| 39 | #pragma message("WARNING: the 'dbg.h' header is included in your code base") | ||
| 40 | #endif // DBG_MACRO_NO_WARNING | ||
| 41 | |||
| 42 | #include <algorithm> | ||
| 43 | #include <chrono> | ||
| 44 | #include <ctime> | ||
| 45 | #include <iomanip> | ||
| 46 | #include <ios> | ||
| 47 | #include <iostream> | ||
| 48 | #include <memory> | ||
| 49 | #include <sstream> | ||
| 50 | #include <string> | ||
| 51 | #include <tuple> | ||
| 52 | #include <type_traits> | ||
| 53 | #include <vector> | ||
| 54 | |||
| 55 | #ifdef DBG_MACRO_UNIX | ||
| 56 | #include <unistd.h> | ||
| 57 | #endif | ||
| 58 | |||
| 59 | #if __cplusplus >= 201703L || defined(_MSC_VER) | ||
| 60 | #define DBG_MACRO_CXX_STANDARD 17 | ||
| 61 | #elif __cplusplus >= 201402L | ||
| 62 | #define DBG_MACRO_CXX_STANDARD 14 | ||
| 63 | #else | ||
| 64 | #define DBG_MACRO_CXX_STANDARD 11 | ||
| 65 | #endif | ||
| 66 | |||
| 67 | #if DBG_MACRO_CXX_STANDARD >= 17 | ||
| 68 | #include <optional> | ||
| 69 | #include <variant> | ||
| 70 | #endif | ||
| 71 | |||
| 72 | namespace dbg { | ||
| 73 | |||
| 74 | #ifdef DBG_MACRO_UNIX | ||
| 75 | inline bool isColorizedOutputEnabled() { | ||
| 76 | return isatty(fileno(stderr)); | ||
| 77 | } | ||
| 78 | #else | ||
| 79 | inline bool isColorizedOutputEnabled() { | ||
| 80 | return true; | ||
| 81 | } | ||
| 82 | #endif | ||
| 83 | |||
| 84 | struct time {}; | ||
| 85 | |||
| 86 | namespace pretty_function { | ||
| 87 | |||
| 88 | // Compiler-agnostic version of __PRETTY_FUNCTION__ and constants to | ||
| 89 | // extract the template argument in `type_name_impl` | ||
| 90 | |||
| 91 | #if defined(__clang__) | ||
| 92 | #define DBG_MACRO_PRETTY_FUNCTION __PRETTY_FUNCTION__ | ||
| 93 | static constexpr size_t PREFIX_LENGTH = | ||
| 94 | sizeof("const char *dbg::type_name_impl() [T = ") - 1; | ||
| 95 | static constexpr size_t SUFFIX_LENGTH = sizeof("]") - 1; | ||
| 96 | #elif defined(__GNUC__) && !defined(__clang__) | ||
| 97 | #define DBG_MACRO_PRETTY_FUNCTION __PRETTY_FUNCTION__ | ||
| 98 | static constexpr size_t PREFIX_LENGTH = | ||
| 99 | sizeof("const char* dbg::type_name_impl() [with T = ") - 1; | ||
| 100 | static constexpr size_t SUFFIX_LENGTH = sizeof("]") - 1; | ||
| 101 | #elif defined(_MSC_VER) | ||
| 102 | #define DBG_MACRO_PRETTY_FUNCTION __FUNCSIG__ | ||
| 103 | static constexpr size_t PREFIX_LENGTH = | ||
| 104 | sizeof("const char *__cdecl dbg::type_name_impl<") - 1; | ||
| 105 | static constexpr size_t SUFFIX_LENGTH = sizeof(">(void)") - 1; | ||
| 106 | #else | ||
| 107 | #error "This compiler is currently not supported by dbg_macro." | ||
| 108 | #endif | ||
| 109 | |||
| 110 | } // namespace pretty_function | ||
| 111 | |||
| 112 | // Formatting helpers | ||
| 113 | |||
| 114 | template <typename T> | ||
| 115 | struct print_formatted { | ||
| 116 | static_assert(std::is_integral<T>::value, | ||
| 117 | "Only integral types are supported."); | ||
| 118 | |||
| 119 | print_formatted(T value, int numeric_base) | ||
| 120 | : inner(value), base(numeric_base) {} | ||
| 121 | |||
| 122 | operator T() const { return inner; } | ||
| 123 | |||
| 124 | const char* prefix() const { | ||
| 125 | switch (base) { | ||
| 126 | case 8: | ||
| 127 | return "0o"; | ||
| 128 | case 16: | ||
| 129 | return "0x"; | ||
| 130 | case 2: | ||
| 131 | return "0b"; | ||
| 132 | default: | ||
| 133 | return ""; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | T inner; | ||
| 138 | int base; | ||
| 139 | }; | ||
| 140 | |||
| 141 | template <typename T> | ||
| 142 | print_formatted<T> hex(T value) { | ||
| 143 | return print_formatted<T>{value, 16}; | ||
| 144 | } | ||
| 145 | |||
| 146 | template <typename T> | ||
| 147 | print_formatted<T> oct(T value) { | ||
| 148 | return print_formatted<T>{value, 8}; | ||
| 149 | } | ||
| 150 | |||
| 151 | template <typename T> | ||
| 152 | print_formatted<T> bin(T value) { | ||
| 153 | return print_formatted<T>{value, 2}; | ||
| 154 | } | ||
| 155 | |||
| 156 | // Implementation of 'type_name<T>()' | ||
| 157 | |||
| 158 | template <typename T> | ||
| 159 | const char* type_name_impl() { | ||
| 160 | return DBG_MACRO_PRETTY_FUNCTION; | ||
| 161 | } | ||
| 162 | |||
| 163 | template <typename T> | ||
| 164 | struct type_tag {}; | ||
| 165 | |||
| 166 | template <int&... ExplicitArgumentBarrier, typename T> | ||
| 167 | std::string get_type_name(type_tag<T>) { | ||
| 168 | namespace pf = pretty_function; | ||
| 169 | |||
| 170 | std::string type = type_name_impl<T>(); | ||
| 171 | return type.substr(pf::PREFIX_LENGTH, | ||
| 172 | type.size() - pf::PREFIX_LENGTH - pf::SUFFIX_LENGTH); | ||
| 173 | } | ||
| 174 | |||
| 175 | template <typename T> | ||
| 176 | std::string type_name() { | ||
| 177 | if (std::is_volatile<T>::value) { | ||
| 178 | if (std::is_pointer<T>::value) { | ||
| 179 | return type_name<typename std::remove_volatile<T>::type>() + " volatile"; | ||
| 180 | } else { | ||
| 181 | return "volatile " + type_name<typename std::remove_volatile<T>::type>(); | ||
| 182 | } | ||
| 183 | } | ||
| 184 | if (std::is_const<T>::value) { | ||
| 185 | if (std::is_pointer<T>::value) { | ||
| 186 | return type_name<typename std::remove_const<T>::type>() + " const"; | ||
| 187 | } else { | ||
| 188 | return "const " + type_name<typename std::remove_const<T>::type>(); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | if (std::is_pointer<T>::value) { | ||
| 192 | return type_name<typename std::remove_pointer<T>::type>() + "*"; | ||
| 193 | } | ||
| 194 | if (std::is_lvalue_reference<T>::value) { | ||
| 195 | return type_name<typename std::remove_reference<T>::type>() + "&"; | ||
| 196 | } | ||
| 197 | if (std::is_rvalue_reference<T>::value) { | ||
| 198 | return type_name<typename std::remove_reference<T>::type>() + "&&"; | ||
| 199 | } | ||
| 200 | return get_type_name(type_tag<T>{}); | ||
| 201 | } | ||
| 202 | |||
| 203 | inline std::string get_type_name(type_tag<short>) { | ||
| 204 | return "short"; | ||
| 205 | } | ||
| 206 | |||
| 207 | inline std::string get_type_name(type_tag<unsigned short>) { | ||
| 208 | return "unsigned short"; | ||
| 209 | } | ||
| 210 | |||
| 211 | inline std::string get_type_name(type_tag<long>) { | ||
| 212 | return "long"; | ||
| 213 | } | ||
| 214 | |||
| 215 | inline std::string get_type_name(type_tag<unsigned long>) { | ||
| 216 | return "unsigned long"; | ||
| 217 | } | ||
| 218 | |||
| 219 | inline std::string get_type_name(type_tag<std::string>) { | ||
| 220 | return "std::string"; | ||
| 221 | } | ||
| 222 | |||
| 223 | template <typename T> | ||
| 224 | std::string get_type_name(type_tag<std::vector<T, std::allocator<T>>>) { | ||
| 225 | return "std::vector<" + type_name<T>() + ">"; | ||
| 226 | } | ||
| 227 | |||
| 228 | template <typename T1, typename T2> | ||
| 229 | std::string get_type_name(type_tag<std::pair<T1, T2>>) { | ||
| 230 | return "std::pair<" + type_name<T1>() + ", " + type_name<T2>() + ">"; | ||
| 231 | } | ||
| 232 | |||
| 233 | template <typename... T> | ||
| 234 | std::string type_list_to_string() { | ||
| 235 | std::string result; | ||
| 236 | auto unused = {(result += type_name<T>() + ", ", 0)..., 0}; | ||
| 237 | static_cast<void>(unused); | ||
| 238 | |||
| 239 | if (sizeof...(T) > 0) { | ||
| 240 | result.pop_back(); | ||
| 241 | result.pop_back(); | ||
| 242 | } | ||
| 243 | return result; | ||
| 244 | } | ||
| 245 | |||
| 246 | template <typename... T> | ||
| 247 | std::string get_type_name(type_tag<std::tuple<T...>>) { | ||
| 248 | return "std::tuple<" + type_list_to_string<T...>() + ">"; | ||
| 249 | } | ||
| 250 | |||
| 251 | template <typename T> | ||
| 252 | inline std::string get_type_name(type_tag<print_formatted<T>>) { | ||
| 253 | return type_name<T>(); | ||
| 254 | } | ||
| 255 | |||
| 256 | // Implementation of 'is_detected' to specialize for container-like types | ||
| 257 | |||
| 258 | namespace detail_detector { | ||
| 259 | |||
| 260 | struct nonesuch { | ||
| 261 | nonesuch() = delete; | ||
| 262 | ~nonesuch() = delete; | ||
| 263 | nonesuch(nonesuch const&) = delete; | ||
| 264 | void operator=(nonesuch const&) = delete; | ||
| 265 | }; | ||
| 266 | |||
| 267 | template <typename...> | ||
| 268 | using void_t = void; | ||
| 269 | |||
| 270 | template <class Default, | ||
| 271 | class AlwaysVoid, | ||
| 272 | template <class...> | ||
| 273 | class Op, | ||
| 274 | class... Args> | ||
| 275 | struct detector { | ||
| 276 | using value_t = std::false_type; | ||
| 277 | using type = Default; | ||
| 278 | }; | ||
| 279 | |||
| 280 | template <class Default, template <class...> class Op, class... Args> | ||
| 281 | struct detector<Default, void_t<Op<Args...>>, Op, Args...> { | ||
| 282 | using value_t = std::true_type; | ||
| 283 | using type = Op<Args...>; | ||
| 284 | }; | ||
| 285 | |||
| 286 | } // namespace detail_detector | ||
| 287 | |||
| 288 | template <template <class...> class Op, class... Args> | ||
| 289 | using is_detected = typename detail_detector:: | ||
| 290 | detector<detail_detector::nonesuch, void, Op, Args...>::value_t; | ||
| 291 | |||
| 292 | namespace detail { | ||
| 293 | |||
| 294 | namespace { | ||
| 295 | using std::begin; | ||
| 296 | using std::end; | ||
| 297 | #if DBG_MACRO_CXX_STANDARD < 17 | ||
| 298 | template <typename T> | ||
| 299 | constexpr auto size(const T& c) -> decltype(c.size()) { | ||
| 300 | return c.size(); | ||
| 301 | } | ||
| 302 | template <typename T, std::size_t N> | ||
| 303 | constexpr std::size_t size(const T (&)[N]) { | ||
| 304 | return N; | ||
| 305 | } | ||
| 306 | #else | ||
| 307 | using std::size; | ||
| 308 | #endif | ||
| 309 | } // namespace | ||
| 310 | |||
| 311 | template <typename T> | ||
| 312 | using detect_begin_t = decltype(detail::begin(std::declval<T>())); | ||
| 313 | |||
| 314 | template <typename T> | ||
| 315 | using detect_end_t = decltype(detail::end(std::declval<T>())); | ||
| 316 | |||
| 317 | template <typename T> | ||
| 318 | using detect_size_t = decltype(detail::size(std::declval<T>())); | ||
| 319 | |||
| 320 | template <typename T> | ||
| 321 | struct is_container { | ||
| 322 | static constexpr bool value = | ||
| 323 | is_detected<detect_begin_t, T>::value && | ||
| 324 | is_detected<detect_end_t, T>::value && | ||
| 325 | is_detected<detect_size_t, T>::value && | ||
| 326 | !std::is_same<std::string, | ||
| 327 | typename std::remove_cv< | ||
| 328 | typename std::remove_reference<T>::type>::type>::value; | ||
| 329 | }; | ||
| 330 | |||
| 331 | template <typename T> | ||
| 332 | using ostream_operator_t = | ||
| 333 | decltype(std::declval<std::ostream&>() << std::declval<T>()); | ||
| 334 | |||
| 335 | template <typename T> | ||
| 336 | struct has_ostream_operator : is_detected<ostream_operator_t, T> {}; | ||
| 337 | |||
| 338 | } // namespace detail | ||
| 339 | |||
| 340 | // Helper to dbg(…)-print types | ||
| 341 | template <typename T> | ||
| 342 | struct print_type {}; | ||
| 343 | |||
| 344 | template <typename T> | ||
| 345 | print_type<T> type() { | ||
| 346 | return print_type<T>{}; | ||
| 347 | } | ||
| 348 | |||
| 349 | // Specializations of "pretty_print" | ||
| 350 | |||
| 351 | template <typename T> | ||
| 352 | inline void pretty_print(std::ostream& stream, const T& value, std::true_type) { | ||
| 353 | stream << value; | ||
| 354 | } | ||
| 355 | |||
| 356 | template <typename T> | ||
| 357 | inline void pretty_print(std::ostream&, const T&, std::false_type) { | ||
| 358 | static_assert(detail::has_ostream_operator<const T&>::value, | ||
| 359 | "Type does not support the << ostream operator"); | ||
| 360 | } | ||
| 361 | |||
| 362 | template <typename T> | ||
| 363 | inline typename std::enable_if<!detail::is_container<const T&>::value && | ||
| 364 | !std::is_enum<T>::value, | ||
| 365 | bool>::type | ||
| 366 | pretty_print(std::ostream& stream, const T& value) { | ||
| 367 | pretty_print(stream, value, | ||
| 368 | typename detail::has_ostream_operator<const T&>::type{}); | ||
| 369 | return true; | ||
| 370 | } | ||
| 371 | |||
| 372 | inline bool pretty_print(std::ostream& stream, const bool& value) { | ||
| 373 | stream << std::boolalpha << value; | ||
| 374 | return true; | ||
| 375 | } | ||
| 376 | |||
| 377 | inline bool pretty_print(std::ostream& stream, const char& value) { | ||
| 378 | const bool printable = value >= 0x20 && value <= 0x7E; | ||
| 379 | |||
| 380 | if (printable) { | ||
| 381 | stream << "'" << value << "'"; | ||
| 382 | } else { | ||
| 383 | stream << "'\\x" << std::setw(2) << std::setfill('0') << std::hex | ||
| 384 | << std::uppercase << (0xFF & value) << "'"; | ||
| 385 | } | ||
| 386 | return true; | ||
| 387 | } | ||
| 388 | |||
| 389 | template <typename P> | ||
| 390 | inline bool pretty_print(std::ostream& stream, P* const& value) { | ||
| 391 | if (value == nullptr) { | ||
| 392 | stream << "nullptr"; | ||
| 393 | } else { | ||
| 394 | stream << value; | ||
| 395 | } | ||
| 396 | return true; | ||
| 397 | } | ||
| 398 | |||
| 399 | template <typename T, typename Deleter> | ||
| 400 | inline bool pretty_print(std::ostream& stream, | ||
| 401 | std::unique_ptr<T, Deleter>& value) { | ||
| 402 | pretty_print(stream, value.get()); | ||
| 403 | return true; | ||
| 404 | } | ||
| 405 | |||
| 406 | template <typename T> | ||
| 407 | inline bool pretty_print(std::ostream& stream, std::shared_ptr<T>& value) { | ||
| 408 | pretty_print(stream, value.get()); | ||
| 409 | stream << " (use_count = " << value.use_count() << ")"; | ||
| 410 | |||
| 411 | return true; | ||
| 412 | } | ||
| 413 | |||
| 414 | template <size_t N> | ||
| 415 | inline bool pretty_print(std::ostream& stream, const char (&value)[N]) { | ||
| 416 | stream << value; | ||
| 417 | return false; | ||
| 418 | } | ||
| 419 | |||
| 420 | template <> | ||
| 421 | inline bool pretty_print(std::ostream& stream, const char* const& value) { | ||
| 422 | stream << '"' << value << '"'; | ||
| 423 | return true; | ||
| 424 | } | ||
| 425 | |||
| 426 | template <size_t Idx> | ||
| 427 | struct pretty_print_tuple { | ||
| 428 | template <typename... Ts> | ||
| 429 | static void print(std::ostream& stream, const std::tuple<Ts...>& tuple) { | ||
| 430 | pretty_print_tuple<Idx - 1>::print(stream, tuple); | ||
| 431 | stream << ", "; | ||
| 432 | pretty_print(stream, std::get<Idx>(tuple)); | ||
| 433 | } | ||
| 434 | }; | ||
| 435 | |||
| 436 | template <> | ||
| 437 | struct pretty_print_tuple<0> { | ||
| 438 | template <typename... Ts> | ||
| 439 | static void print(std::ostream& stream, const std::tuple<Ts...>& tuple) { | ||
| 440 | pretty_print(stream, std::get<0>(tuple)); | ||
| 441 | } | ||
| 442 | }; | ||
| 443 | |||
| 444 | template <typename... Ts> | ||
| 445 | inline bool pretty_print(std::ostream& stream, const std::tuple<Ts...>& value) { | ||
| 446 | stream << "{"; | ||
| 447 | pretty_print_tuple<sizeof...(Ts) - 1>::print(stream, value); | ||
| 448 | stream << "}"; | ||
| 449 | |||
| 450 | return true; | ||
| 451 | } | ||
| 452 | |||
| 453 | template <> | ||
| 454 | inline bool pretty_print(std::ostream& stream, const std::tuple<>&) { | ||
| 455 | stream << "{}"; | ||
| 456 | |||
| 457 | return true; | ||
| 458 | } | ||
| 459 | |||
| 460 | template <> | ||
| 461 | inline bool pretty_print(std::ostream& stream, const time&) { | ||
| 462 | using namespace std::chrono; | ||
| 463 | |||
| 464 | const auto now = system_clock::now(); | ||
| 465 | const auto us = | ||
| 466 | duration_cast<microseconds>(now.time_since_epoch()).count() % 1000000; | ||
| 467 | const auto hms = system_clock::to_time_t(now); | ||
| 468 | const std::tm* tm = std::localtime(&hms); | ||
| 469 | stream << "current time = " << std::put_time(tm, "%H:%M:%S") << '.' | ||
| 470 | << std::setw(6) << std::setfill('0') << us; | ||
| 471 | |||
| 472 | return false; | ||
| 473 | } | ||
| 474 | |||
| 475 | // Converts decimal integer to binary string | ||
| 476 | template <typename T> | ||
| 477 | std::string decimalToBinary(T n) { | ||
| 478 | const size_t length = 8 * sizeof(T); | ||
| 479 | std::string toRet; | ||
| 480 | toRet.resize(length); | ||
| 481 | |||
| 482 | for (size_t i = 0; i < length; ++i) { | ||
| 483 | const auto bit_at_index_i = (n >> i) & 1; | ||
| 484 | toRet[length - 1 - i] = bit_at_index_i + '0'; | ||
| 485 | } | ||
| 486 | |||
| 487 | return toRet; | ||
| 488 | } | ||
| 489 | |||
| 490 | template <typename T> | ||
| 491 | inline bool pretty_print(std::ostream& stream, | ||
| 492 | const print_formatted<T>& value) { | ||
| 493 | if (value.inner < 0) { | ||
| 494 | stream << "-"; | ||
| 495 | } | ||
| 496 | stream << value.prefix(); | ||
| 497 | |||
| 498 | // Print using setbase | ||
| 499 | if (value.base != 2) { | ||
| 500 | stream << std::setw(sizeof(T)) << std::setfill('0') | ||
| 501 | << std::setbase(value.base) << std::uppercase; | ||
| 502 | |||
| 503 | if (value.inner >= 0) { | ||
| 504 | // The '+' sign makes sure that a uint_8 is printed as a number | ||
| 505 | stream << +value.inner; | ||
| 506 | } else { | ||
| 507 | using unsigned_type = typename std::make_unsigned<T>::type; | ||
| 508 | stream << +(static_cast<unsigned_type>(-(value.inner + 1)) + 1); | ||
| 509 | } | ||
| 510 | } else { | ||
| 511 | // Print for binary | ||
| 512 | if (value.inner >= 0) { | ||
| 513 | stream << decimalToBinary(value.inner); | ||
| 514 | } else { | ||
| 515 | using unsigned_type = typename std::make_unsigned<T>::type; | ||
| 516 | stream << decimalToBinary<unsigned_type>( | ||
| 517 | static_cast<unsigned_type>(-(value.inner + 1)) + 1); | ||
| 518 | } | ||
| 519 | } | ||
| 520 | |||
| 521 | return true; | ||
| 522 | } | ||
| 523 | |||
| 524 | template <typename T> | ||
| 525 | inline bool pretty_print(std::ostream& stream, const print_type<T>&) { | ||
| 526 | stream << type_name<T>(); | ||
| 527 | |||
| 528 | stream << " [sizeof: " << sizeof(T) << " byte, "; | ||
| 529 | |||
| 530 | stream << "trivial: "; | ||
| 531 | if (std::is_trivial<T>::value) { | ||
| 532 | stream << "yes"; | ||
| 533 | } else { | ||
| 534 | stream << "no"; | ||
| 535 | } | ||
| 536 | |||
| 537 | stream << ", standard layout: "; | ||
| 538 | if (std::is_standard_layout<T>::value) { | ||
| 539 | stream << "yes"; | ||
| 540 | } else { | ||
| 541 | stream << "no"; | ||
| 542 | } | ||
| 543 | stream << "]"; | ||
| 544 | |||
| 545 | return false; | ||
| 546 | } | ||
| 547 | |||
| 548 | template <typename Container> | ||
| 549 | inline typename std::enable_if<detail::is_container<const Container&>::value, | ||
| 550 | bool>::type | ||
| 551 | pretty_print(std::ostream& stream, const Container& value) { | ||
| 552 | stream << "{"; | ||
| 553 | const size_t size = detail::size(value); | ||
| 554 | const size_t n = std::min(size_t{10}, size); | ||
| 555 | size_t i = 0; | ||
| 556 | using std::begin; | ||
| 557 | using std::end; | ||
| 558 | for (auto it = begin(value); it != end(value) && i < n; ++it, ++i) { | ||
| 559 | pretty_print(stream, *it); | ||
| 560 | if (i != n - 1) { | ||
| 561 | stream << ", "; | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | if (size > n) { | ||
| 566 | stream << ", ..."; | ||
| 567 | stream << " size:" << size; | ||
| 568 | } | ||
| 569 | |||
| 570 | stream << "}"; | ||
| 571 | return true; | ||
| 572 | } | ||
| 573 | |||
| 574 | template <typename Enum> | ||
| 575 | inline typename std::enable_if<std::is_enum<Enum>::value, bool>::type | ||
| 576 | pretty_print(std::ostream& stream, Enum const& value) { | ||
| 577 | using UnderlyingType = typename std::underlying_type<Enum>::type; | ||
| 578 | stream << static_cast<UnderlyingType>(value); | ||
| 579 | |||
| 580 | return true; | ||
| 581 | } | ||
| 582 | |||
| 583 | inline bool pretty_print(std::ostream& stream, const std::string& value) { | ||
| 584 | stream << '"' << value << '"'; | ||
| 585 | return true; | ||
| 586 | } | ||
| 587 | |||
| 588 | template <typename T1, typename T2> | ||
| 589 | inline bool pretty_print(std::ostream& stream, const std::pair<T1, T2>& value) { | ||
| 590 | stream << "{"; | ||
| 591 | pretty_print(stream, value.first); | ||
| 592 | stream << ", "; | ||
| 593 | pretty_print(stream, value.second); | ||
| 594 | stream << "}"; | ||
| 595 | return true; | ||
| 596 | } | ||
| 597 | |||
| 598 | #if DBG_MACRO_CXX_STANDARD >= 17 | ||
| 599 | |||
| 600 | template <typename T> | ||
| 601 | inline bool pretty_print(std::ostream& stream, const std::optional<T>& value) { | ||
| 602 | if (value) { | ||
| 603 | stream << '{'; | ||
| 604 | pretty_print(stream, *value); | ||
| 605 | stream << '}'; | ||
| 606 | } else { | ||
| 607 | stream << "nullopt"; | ||
| 608 | } | ||
| 609 | |||
| 610 | return true; | ||
| 611 | } | ||
| 612 | |||
| 613 | template <typename... Ts> | ||
| 614 | inline bool pretty_print(std::ostream& stream, | ||
| 615 | const std::variant<Ts...>& value) { | ||
| 616 | stream << "{"; | ||
| 617 | std::visit([&stream](auto&& arg) { pretty_print(stream, arg); }, value); | ||
| 618 | stream << "}"; | ||
| 619 | |||
| 620 | return true; | ||
| 621 | } | ||
| 622 | |||
| 623 | #endif | ||
| 624 | |||
| 625 | class DebugOutput { | ||
| 626 | public: | ||
| 627 | DebugOutput(const char* filepath, | ||
| 628 | int line, | ||
| 629 | const char* function_name, | ||
| 630 | const char* expression) | ||
| 631 | : m_use_colorized_output(isColorizedOutputEnabled()), | ||
| 632 | m_filepath(filepath), | ||
| 633 | m_line(line), | ||
| 634 | m_function_name(function_name), | ||
| 635 | m_expression(expression) { | ||
| 636 | const std::size_t path_length = m_filepath.length(); | ||
| 637 | if (path_length > MAX_PATH_LENGTH) { | ||
| 638 | m_filepath = ".." + m_filepath.substr(path_length - MAX_PATH_LENGTH, | ||
| 639 | MAX_PATH_LENGTH); | ||
| 640 | } | ||
| 641 | } | ||
| 642 | |||
| 643 | template <typename T> | ||
| 644 | T&& print(const std::string& type, T&& value) const { | ||
| 645 | const T& ref = value; | ||
| 646 | std::stringstream stream_value; | ||
| 647 | const bool print_expr_and_type = pretty_print(stream_value, ref); | ||
| 648 | |||
| 649 | std::stringstream output; | ||
| 650 | output << ansi(ANSI_DEBUG) << "[" << m_filepath << ":" << m_line << " (" | ||
| 651 | << m_function_name << ")] " << ansi(ANSI_RESET); | ||
| 652 | if (print_expr_and_type) { | ||
| 653 | output << ansi(ANSI_EXPRESSION) << m_expression << ansi(ANSI_RESET) | ||
| 654 | << " = "; | ||
| 655 | } | ||
| 656 | output << ansi(ANSI_VALUE) << stream_value.str() << ansi(ANSI_RESET); | ||
| 657 | if (print_expr_and_type) { | ||
| 658 | output << " (" << ansi(ANSI_TYPE) << type << ansi(ANSI_RESET) << ")"; | ||
| 659 | } | ||
| 660 | output << std::endl; | ||
| 661 | std::cerr << output.str(); | ||
| 662 | |||
| 663 | return std::forward<T>(value); | ||
| 664 | } | ||
| 665 | |||
| 666 | private: | ||
| 667 | const char* ansi(const char* code) const { | ||
| 668 | if (m_use_colorized_output) { | ||
| 669 | return code; | ||
| 670 | } else { | ||
| 671 | return ANSI_EMPTY; | ||
| 672 | } | ||
| 673 | } | ||
| 674 | |||
| 675 | const bool m_use_colorized_output; | ||
| 676 | |||
| 677 | std::string m_filepath; | ||
| 678 | const int m_line; | ||
| 679 | const std::string m_function_name; | ||
| 680 | const std::string m_expression; | ||
| 681 | |||
| 682 | static constexpr std::size_t MAX_PATH_LENGTH = 20; | ||
| 683 | |||
| 684 | static constexpr const char* const ANSI_EMPTY = ""; | ||
| 685 | static constexpr const char* const ANSI_DEBUG = "\x1b[02m"; | ||
| 686 | static constexpr const char* const ANSI_EXPRESSION = "\x1b[36m"; | ||
| 687 | static constexpr const char* const ANSI_VALUE = "\x1b[01m"; | ||
| 688 | static constexpr const char* const ANSI_TYPE = "\x1b[32m"; | ||
| 689 | static constexpr const char* const ANSI_RESET = "\x1b[0m"; | ||
| 690 | }; | ||
| 691 | |||
| 692 | // Identity function to suppress "-Wunused-value" warnings in DBG_MACRO_DISABLE | ||
| 693 | // mode | ||
| 694 | template <typename T> | ||
| 695 | T&& identity(T&& t) { | ||
| 696 | return std::forward<T>(t); | ||
| 697 | } | ||
| 698 | |||
| 699 | } // namespace dbg | ||
| 700 | |||
| 701 | #ifndef DBG_MACRO_DISABLE | ||
| 702 | // We use a variadic macro to support commas inside expressions (e.g. | ||
| 703 | // initializer lists): | ||
| 704 | #define dbg(...) \ | ||
| 705 | dbg::DebugOutput(__FILE__, __LINE__, __func__, #__VA_ARGS__) \ | ||
| 706 | .print(dbg::type_name<decltype(__VA_ARGS__)>(), (__VA_ARGS__)) | ||
| 707 | #else | ||
| 708 | #define dbg(...) dbg::identity(__VA_ARGS__) | ||
| 709 | #endif // DBG_MACRO_DISABLE | ||
| 710 | |||
| 711 | #endif // DBG_MACRO_DBG_H | ||
lib/libc/mingw/lib32/user32.def created+998| ... | @@ -0,0 +1,998 @@ | ||
| 1 | LIBRARY USER32.dll | ||
| 2 | EXPORTS | ||
| 3 | ;ord_1500@16 @1500 | ||
| 4 | ;ord_1501@4 @1501 | ||
| 5 | ;ord_1502@12 @1502 | ||
| 6 | ActivateKeyboardLayout@8 | ||
| 7 | AddClipboardFormatListener@4 | ||
| 8 | AdjustWindowRect@12 | ||
| 9 | AdjustWindowRectEx@16 | ||
| 10 | AlignRects@16 | ||
| 11 | AllowForegroundActivation@0 | ||
| 12 | AllowSetForegroundWindow@4 | ||
| 13 | AnimateWindow@12 | ||
| 14 | AnyPopup@0 | ||
| 15 | AppendMenuA@16 | ||
| 16 | AppendMenuW@16 | ||
| 17 | ArrangeIconicWindows@4 | ||
| 18 | AttachThreadInput@12 | ||
| 19 | BeginDeferWindowPos@4 | ||
| 20 | BeginPaint@8 | ||
| 21 | BlockInput@4 | ||
| 22 | BringWindowToTop@4 | ||
| 23 | BroadcastSystemMessage@20 | ||
| 24 | BroadcastSystemMessageA@20 | ||
| 25 | BroadcastSystemMessageExA@24 | ||
| 26 | BroadcastSystemMessageExW@24 | ||
| 27 | BroadcastSystemMessageW@20 | ||
| 28 | BuildReasonArray@12 | ||
| 29 | CalcChildScroll@8 | ||
| 30 | CalcMenuBar@20 | ||
| 31 | CalculatePopupWindowPosition@20 | ||
| 32 | CallMsgFilter@8 | ||
| 33 | CallMsgFilterA@8 | ||
| 34 | CallMsgFilterW@8 | ||
| 35 | CallNextHookEx@16 | ||
| 36 | CallWindowProcA@20 | ||
| 37 | CallWindowProcW@20 | ||
| 38 | CancelShutdown@0 | ||
| 39 | CascadeChildWindows@8 | ||
| 40 | CascadeWindows@20 | ||
| 41 | ChangeClipboardChain@8 | ||
| 42 | ChangeDisplaySettingsA@8 | ||
| 43 | ChangeDisplaySettingsExA@20 | ||
| 44 | ChangeDisplaySettingsExW@20 | ||
| 45 | ChangeDisplaySettingsW@8 | ||
| 46 | ChangeMenuA@20 | ||
| 47 | ChangeMenuW@20 | ||
| 48 | ChangeWindowMessageFilter@8 | ||
| 49 | ChangeWindowMessageFilterEx@16 | ||
| 50 | CharLowerA@4 | ||
| 51 | CharLowerBuffA@8 | ||
| 52 | CharLowerBuffW@8 | ||
| 53 | CharLowerW@4 | ||
| 54 | CharNextA@4 | ||
| 55 | ;ord_1550@12 @1550 | ||
| 56 | ;ord_1551@8 @1551 | ||
| 57 | ;ord_1552@8 @1552 | ||
| 58 | ;ord_1553@12 @1553 | ||
| 59 | ;ord_1554@8 @1554 | ||
| 60 | ;ord_1555@16 @1555 | ||
| 61 | ;ord_1556@4 @1556 | ||
| 62 | CharNextExA@12 | ||
| 63 | CharNextW@4 | ||
| 64 | CharPrevA@8 | ||
| 65 | CharPrevExA@16 | ||
| 66 | CharPrevW@8 | ||
| 67 | CharToOemA@8 | ||
| 68 | CharToOemBuffA@12 | ||
| 69 | CharToOemBuffW@12 | ||
| 70 | CharToOemW@8 | ||
| 71 | CharUpperA@4 | ||
| 72 | CharUpperBuffA@8 | ||
| 73 | CharUpperBuffW@8 | ||
| 74 | CharUpperW@4 | ||
| 75 | CheckDesktopByThreadId@4 | ||
| 76 | CheckDBCSEnabledExt@0 | ||
| 77 | CheckDlgButton@12 | ||
| 78 | CheckMenuItem@12 | ||
| 79 | CheckMenuRadioItem@20 | ||
| 80 | CheckProcessForClipboardAccess@8 | ||
| 81 | CheckProcessSession@4 | ||
| 82 | CheckRadioButton@16 | ||
| 83 | CheckWindowThreadDesktop@8 | ||
| 84 | ChildWindowFromPoint@12 | ||
| 85 | ChildWindowFromPointEx@16 | ||
| 86 | CliImmSetHotKey@16 | ||
| 87 | ClientThreadSetup@0 | ||
| 88 | ClientToScreen@8 | ||
| 89 | ClipCursor@4 | ||
| 90 | CloseClipboard@0 | ||
| 91 | CloseDesktop@4 | ||
| 92 | CloseGestureInfoHandle@4 | ||
| 93 | CloseTouchInputHandle@4 | ||
| 94 | CloseWindow@4 | ||
| 95 | CloseWindowStation@4 | ||
| 96 | ConsoleControl@12 | ||
| 97 | ControlMagnification@8 | ||
| 98 | CopyAcceleratorTableA@12 | ||
| 99 | CopyAcceleratorTableW@12 | ||
| 100 | CopyIcon@4 | ||
| 101 | CopyImage@20 | ||
| 102 | CopyRect@8 | ||
| 103 | CountClipboardFormats@0 | ||
| 104 | CreateAcceleratorTableA@8 | ||
| 105 | CreateAcceleratorTableW@8 | ||
| 106 | CreateCaret@16 | ||
| 107 | CreateCursor@28 | ||
| 108 | CreateDCompositionHwndTarget@12 | ||
| 109 | CreateDesktopA@24 | ||
| 110 | CreateDesktopExA@32 | ||
| 111 | CreateDesktopExW@32 | ||
| 112 | CreateDesktopW@24 | ||
| 113 | CreateDialogIndirectParamA@20 | ||
| 114 | CreateDialogIndirectParamAorW@24 | ||
| 115 | CreateDialogIndirectParamW@20 | ||
| 116 | CreateDialogParamA@20 | ||
| 117 | CreateDialogParamW@20 | ||
| 118 | CreateIcon@28 | ||
| 119 | CreateIconFromResource@16 | ||
| 120 | CreateIconFromResourceEx@28 | ||
| 121 | CreateIconIndirect@4 | ||
| 122 | CreateMDIWindowA@40 | ||
| 123 | CreateMDIWindowW@40 | ||
| 124 | CreateMenu@0 | ||
| 125 | CreatePopupMenu@0 | ||
| 126 | CreateSystemThreads@16 ; ReactOS has the @8 variant | ||
| 127 | CreateWindowExA@48 | ||
| 128 | CreateWindowExW@48 | ||
| 129 | CreateWindowInBand@52 | ||
| 130 | CreateWindowIndirect@4 | ||
| 131 | CreateWindowStationA@16 | ||
| 132 | CreateWindowStationW@16 | ||
| 133 | CsrBroadcastSystemMessageExW@24 | ||
| 134 | CtxInitUser32@0 | ||
| 135 | DdeAbandonTransaction@12 | ||
| 136 | DdeAccessData@8 | ||
| 137 | DdeAddData@16 | ||
| 138 | DdeClientTransaction@32 | ||
| 139 | DdeCmpStringHandles@8 | ||
| 140 | DdeConnect@16 | ||
| 141 | DdeConnectList@20 | ||
| 142 | DdeCreateDataHandle@28 | ||
| 143 | DdeCreateStringHandleA@12 | ||
| 144 | DdeCreateStringHandleW@12 | ||
| 145 | DdeDisconnect@4 | ||
| 146 | DdeDisconnectList@4 | ||
| 147 | DdeEnableCallback@12 | ||
| 148 | DdeFreeDataHandle@4 | ||
| 149 | DdeFreeStringHandle@8 | ||
| 150 | DdeGetData@16 | ||
| 151 | DdeGetLastError@4 | ||
| 152 | DdeGetQualityOfService@12 | ||
| 153 | DdeImpersonateClient@4 | ||
| 154 | DdeInitializeA@16 | ||
| 155 | DdeInitializeW@16 | ||
| 156 | DdeKeepStringHandle@8 | ||
| 157 | DdeNameService@16 | ||
| 158 | DdePostAdvise@12 | ||
| 159 | DdeQueryConvInfo@12 | ||
| 160 | DdeQueryNextServer@8 | ||
| 161 | DdeQueryStringA@20 | ||
| 162 | DdeQueryStringW@20 | ||
| 163 | DdeReconnect@4 | ||
| 164 | DdeSetQualityOfService@12 | ||
| 165 | DdeSetUserHandle@12 | ||
| 166 | DdeUnaccessData@4 | ||
| 167 | DdeUninitialize@4 | ||
| 168 | DefDlgProcA@16 | ||
| 169 | DefDlgProcW@16 | ||
| 170 | DefFrameProcA@20 | ||
| 171 | DefFrameProcW@20 | ||
| 172 | DefMDIChildProcA@16 | ||
| 173 | DefMDIChildProcW@16 | ||
| 174 | DefRawInputProc@12 | ||
| 175 | DefWindowProcA@16 | ||
| 176 | DefWindowProcW@16 | ||
| 177 | DeferWindowPos@32 | ||
| 178 | DeferWindowPosAndBand@36 | ||
| 179 | DeleteMenu@12 | ||
| 180 | DeregisterShellHookWindow@4 | ||
| 181 | DestroyAcceleratorTable@4 | ||
| 182 | DestroyCaret@0 | ||
| 183 | DestroyCursor@4 | ||
| 184 | DestroyDCompositionHwndTarget@8 | ||
| 185 | DestroyIcon@4 | ||
| 186 | DestroyMenu@4 | ||
| 187 | DestroyReasons@4 | ||
| 188 | DestroyWindow@4 | ||
| 189 | DeviceEventWorker@24 ; No documentation whatsoever, ReactOS has a stub with @20 - https://www.reactos.org/archives/public/ros-diffs/2011-February/040308.html | ||
| 190 | DialogBoxIndirectParamA@20 | ||
| 191 | DialogBoxIndirectParamAorW@24 | ||
| 192 | DialogBoxIndirectParamW@20 | ||
| 193 | DialogBoxParamA@20 | ||
| 194 | DialogBoxParamW@20 | ||
| 195 | DisableProcessWindowsGhosting@0 | ||
| 196 | DispatchMessageA@4 | ||
| 197 | DispatchMessageW@4 | ||
| 198 | DisplayConfigGetDeviceInfo@4 | ||
| 199 | DisplayConfigSetDeviceInfo@4 | ||
| 200 | DisplayExitWindowsWarnings@4 | ||
| 201 | DlgDirListA@20 | ||
| 202 | DlgDirListComboBoxA@20 | ||
| 203 | DlgDirListComboBoxW@20 | ||
| 204 | DlgDirListW@20 | ||
| 205 | DlgDirSelectComboBoxExA@16 | ||
| 206 | DlgDirSelectComboBoxExW@16 | ||
| 207 | DlgDirSelectExA@16 | ||
| 208 | DlgDirSelectExW@16 | ||
| 209 | DoSoundConnect@0 | ||
| 210 | DoSoundDisconnect@0 | ||
| 211 | DragDetect@12 | ||
| 212 | DragObject@20 | ||
| 213 | DrawAnimatedRects@16 | ||
| 214 | DrawCaption@16 | ||
| 215 | DrawCaptionTempA@28 | ||
| 216 | DrawCaptionTempW@28 | ||
| 217 | DrawEdge@16 | ||
| 218 | DrawFocusRect@8 | ||
| 219 | DrawFrame@16 | ||
| 220 | DrawFrameControl@16 | ||
| 221 | DrawIcon@16 | ||
| 222 | DrawIconEx@36 | ||
| 223 | DrawMenuBar@4 | ||
| 224 | DrawMenuBarTemp@20 | ||
| 225 | DrawStateA@40 | ||
| 226 | DrawStateW@40 | ||
| 227 | DrawTextA@20 | ||
| 228 | DrawTextExA@24 | ||
| 229 | DrawTextExW@24 | ||
| 230 | DrawTextW@20 | ||
| 231 | DwmGetDxSharedSurface@24 | ||
| 232 | DwmGetRemoteSessionOcclusionEvent@0 | ||
| 233 | DwmGetRemoteSessionOcclusionState@0 | ||
| 234 | DwmLockScreenUpdates@4 | ||
| 235 | DwmStartRedirection@8 ; Mentioned on http://habrahabr.ru/post/145174/ , enables GDI virtualization (for security purposes) | ||
| 236 | DwmStopRedirection@0 | ||
| 237 | DwmValidateWindow@8 | ||
| 238 | EditWndProc@16 | ||
| 239 | EmptyClipboard@0 | ||
| 240 | EnableMenuItem@12 | ||
| 241 | EnableMouseInPointer@4 | ||
| 242 | EnableScrollBar@12 | ||
| 243 | EnableSessionForMMCSS@4 | ||
| 244 | EnableWindow@8 | ||
| 245 | EndDeferWindowPos@4 | ||
| 246 | EndDeferWindowPosEx@8 | ||
| 247 | EndDialog@8 | ||
| 248 | EndMenu@0 | ||
| 249 | EndPaint@8 | ||
| 250 | EndTask@12 | ||
| 251 | EnterReaderModeHelper@4 | ||
| 252 | EnumChildWindows@12 | ||
| 253 | EnumClipboardFormats@4 | ||
| 254 | EnumDesktopWindows@12 | ||
| 255 | EnumDesktopsA@12 | ||
| 256 | EnumDesktopsW@12 | ||
| 257 | EnumDisplayDevicesA@16 | ||
| 258 | EnumDisplayDevicesW@16 | ||
| 259 | EnumDisplayMonitors@16 | ||
| 260 | EnumDisplaySettingsA@12 | ||
| 261 | EnumDisplaySettingsExA@16 | ||
| 262 | EnumDisplaySettingsExW@16 | ||
| 263 | EnumDisplaySettingsW@12 | ||
| 264 | EnumPropsA@8 | ||
| 265 | EnumPropsExA@12 | ||
| 266 | EnumPropsExW@12 | ||
| 267 | EnumPropsW@8 | ||
| 268 | EnumThreadWindows@12 | ||
| 269 | EnumWindowStationsA@8 | ||
| 270 | EnumWindowStationsW@8 | ||
| 271 | EnumWindows@8 | ||
| 272 | EqualRect@8 | ||
| 273 | EvaluateProximityToPolygon@16 | ||
| 274 | EvaluateProximityToRect@12 | ||
| 275 | ExcludeUpdateRgn@8 | ||
| 276 | ExitWindowsEx@8 | ||
| 277 | FillRect@12 | ||
| 278 | FindWindowA@8 | ||
| 279 | FindWindowExA@16 | ||
| 280 | FindWindowExW@16 | ||
| 281 | FindWindowW@8 | ||
| 282 | FlashWindow@8 | ||
| 283 | FlashWindowEx@4 | ||
| 284 | FrameRect@12 | ||
| 285 | FreeDDElParam@8 | ||
| 286 | FrostCrashedWindow@8 | ||
| 287 | GetActiveWindow@0 | ||
| 288 | GetAltTabInfo@20 | ||
| 289 | GetAltTabInfoA@20 | ||
| 290 | GetAltTabInfoW@20 | ||
| 291 | GetAncestor@8 | ||
| 292 | GetAppCompatFlags2@4 | ||
| 293 | GetAppCompatFlags@8 ; ReactOS has @4 version http://doxygen.reactos.org/d9/d71/undocuser_8h_a9b76cdc68c523a061c86a40367049ed2.html | ||
| 294 | GetAsyncKeyState@4 | ||
| 295 | GetAutoRotationState@4 | ||
| 296 | GetCIMSSM@4 | ||
| 297 | GetCapture@0 | ||
| 298 | GetCaretBlinkTime@0 | ||
| 299 | GetCaretPos@4 | ||
| 300 | GetClassInfoA@12 | ||
| 301 | GetClassInfoExA@12 | ||
| 302 | GetClassInfoExW@12 | ||
| 303 | GetClassInfoW@12 | ||
| 304 | GetClassLongA@8 | ||
| 305 | GetClassLongW@8 | ||
| 306 | GetClassNameA@12 | ||
| 307 | GetClassNameW@12 | ||
| 308 | GetClassWord@8 | ||
| 309 | GetClientRect@8 | ||
| 310 | GetClipCursor@4 | ||
| 311 | GetClipboardAccessToken@8 | ||
| 312 | GetClipboardData@4 | ||
| 313 | GetClipboardFormatNameA@12 | ||
| 314 | GetClipboardFormatNameW@12 | ||
| 315 | GetClipboardOwner@0 | ||
| 316 | GetClipboardSequenceNumber@0 | ||
| 317 | GetClipboardViewer@0 | ||
| 318 | GetComboBoxInfo@8 | ||
| 319 | GetCurrentInputMessageSource@4 | ||
| 320 | GetCursor@0 | ||
| 321 | GetCursorFrameInfo@20 | ||
| 322 | GetCursorInfo@4 | ||
| 323 | GetCursorPos@4 | ||
| 324 | GetDC@4 | ||
| 325 | GetDCEx@12 | ||
| 326 | GetDesktopID@8 | ||
| 327 | GetDesktopWindow@0 | ||
| 328 | GetDialogBaseUnits@0 | ||
| 329 | GetDisplayAutoRotationPreferences@4 | ||
| 330 | GetDisplayConfigBufferSizes@12 | ||
| 331 | GetDlgCtrlID@4 | ||
| 332 | GetDlgItem@8 | ||
| 333 | GetDlgItemInt@16 | ||
| 334 | GetDlgItemTextA@16 | ||
| 335 | GetDlgItemTextW@16 | ||
| 336 | GetDoubleClickTime@0 | ||
| 337 | GetDpiForMonitorInternal@16 | ||
| 338 | GetFocus@0 | ||
| 339 | GetForegroundWindow@0 | ||
| 340 | GetGUIThreadInfo@8 | ||
| 341 | GetGestureConfig@24 | ||
| 342 | GetGestureExtraArgs@12 | ||
| 343 | GetGestureInfo@8 | ||
| 344 | GetGuiResources@8 | ||
| 345 | GetIconInfo@8 | ||
| 346 | GetIconInfoExA@8 | ||
| 347 | GetIconInfoExW@8 | ||
| 348 | GetInputDesktop@0 | ||
| 349 | GetInputLocaleInfo@8 | ||
| 350 | GetInputState@0 | ||
| 351 | GetInternalWindowPos@12 | ||
| 352 | GetKBCodePage@0 | ||
| 353 | GetKeyNameTextA@12 | ||
| 354 | GetKeyNameTextW@12 | ||
| 355 | GetKeyState@4 | ||
| 356 | GetKeyboardLayout@4 | ||
| 357 | GetKeyboardLayoutList@8 | ||
| 358 | GetKeyboardLayoutNameA@4 | ||
| 359 | GetKeyboardLayoutNameW@4 | ||
| 360 | GetKeyboardState@4 | ||
| 361 | GetKeyboardType@4 | ||
| 362 | GetLastActivePopup@4 | ||
| 363 | GetLastInputInfo@4 | ||
| 364 | GetLayeredWindowAttributes@16 | ||
| 365 | GetListBoxInfo@4 | ||
| 366 | GetMagnificationDesktopColorEffect@4 | ||
| 367 | GetMagnificationDesktopMagnification@12 | ||
| 368 | GetMagnificationLensCtxInformation@16 | ||
| 369 | GetMenu@4 | ||
| 370 | GetMenuBarInfo@16 | ||
| 371 | GetMenuCheckMarkDimensions@0 | ||
| 372 | GetMenuContextHelpId@4 | ||
| 373 | GetMenuDefaultItem@12 | ||
| 374 | GetMenuInfo@8 | ||
| 375 | GetMenuItemCount@4 | ||
| 376 | GetMenuItemID@8 | ||
| 377 | GetMenuItemInfoA@16 | ||
| 378 | GetMenuItemInfoW@16 | ||
| 379 | GetMenuItemRect@16 | ||
| 380 | GetMenuState@12 | ||
| 381 | GetMenuStringA@20 | ||
| 382 | GetMenuStringW@20 | ||
| 383 | GetMessageA@16 | ||
| 384 | GetMessageExtraInfo@0 | ||
| 385 | GetMessagePos@0 | ||
| 386 | GetMessageTime@0 | ||
| 387 | GetMessageW@16 | ||
| 388 | GetMonitorInfoA@8 | ||
| 389 | GetMonitorInfoW@8 | ||
| 390 | GetMouseMovePointsEx@20 | ||
| 391 | GetNextDlgGroupItem@12 | ||
| 392 | GetNextDlgTabItem@12 | ||
| 393 | GetOpenClipboardWindow@0 | ||
| 394 | GetParent@4 | ||
| 395 | GetPhysicalCursorPos@4 | ||
| 396 | GetPointerCursorId@8 | ||
| 397 | GetPointerDevice@8 | ||
| 398 | GetPointerDeviceCursors@12 | ||
| 399 | GetPointerDeviceProperties@12 | ||
| 400 | GetPointerDeviceRects@12 | ||
| 401 | GetPointerDevices@8 | ||
| 402 | GetPointerFrameInfo@12 | ||
| 403 | GetPointerFrameInfoHistory@16 | ||
| 404 | GetPointerFramePenInfo@12 | ||
| 405 | GetPointerFramePenInfoHistory@16 | ||
| 406 | GetPointerFrameTouchInfo@12 | ||
| 407 | GetPointerFrameTouchInfoHistory@16 | ||
| 408 | GetPointerInfo@8 | ||
| 409 | GetPointerInfoHistory@12 | ||
| 410 | GetPointerInputTransform@12 | ||
| 411 | GetPointerPenInfo@8 | ||
| 412 | GetPointerPenInfoHistory@12 | ||
| 413 | GetPointerTouchInfo@8 | ||
| 414 | GetPointerTouchInfoHistory@12 | ||
| 415 | GetPointerType@8 | ||
| 416 | GetPriorityClipboardFormat@8 | ||
| 417 | GetProcessDefaultLayout@4 | ||
| 418 | GetProcessDpiAwarenessInternal@8 | ||
| 419 | GetProcessWindowStation@0 | ||
| 420 | GetProgmanWindow@0 | ||
| 421 | GetPropA@8 | ||
| 422 | GetPropW@8 | ||
| 423 | GetQueueStatus@4 | ||
| 424 | GetRawInputBuffer@12 | ||
| 425 | GetRawInputData@20 | ||
| 426 | GetRawInputDeviceInfoA@16 | ||
| 427 | GetRawInputDeviceInfoW@16 | ||
| 428 | GetRawInputDeviceList@12 | ||
| 429 | GetRawPointerDeviceData@20 | ||
| 430 | GetReasonTitleFromReasonCode@12 | ||
| 431 | GetRegisteredRawInputDevices@12 | ||
| 432 | GetQueueStatus@4 | ||
| 433 | GetScrollBarInfo@12 | ||
| 434 | GetScrollInfo@12 | ||
| 435 | GetScrollPos@8 | ||
| 436 | GetScrollRange@16 | ||
| 437 | GetSendMessageReceiver@4 | ||
| 438 | GetShellWindow@0 | ||
| 439 | GetSubMenu@8 | ||
| 440 | GetSysColor@4 | ||
| 441 | GetSysColorBrush@4 | ||
| 442 | GetSystemMenu@8 | ||
| 443 | GetSystemMetrics@4 | ||
| 444 | GetTabbedTextExtentA@20 | ||
| 445 | GetTabbedTextExtentW@20 | ||
| 446 | GetTaskmanWindow@0 | ||
| 447 | GetThreadDesktop@4 | ||
| 448 | GetTitleBarInfo@8 | ||
| 449 | GetTopLevelWindow@4 | ||
| 450 | GetTopWindow@4 | ||
| 451 | GetTouchInputInfo@16 | ||
| 452 | GetUnpredictedMessagePos@0 | ||
| 453 | GetUpdateRect@12 | ||
| 454 | GetUpdateRgn@12 | ||
| 455 | GetUpdatedClipboardFormats@12 | ||
| 456 | GetUserObjectInformationA@20 | ||
| 457 | GetUserObjectInformationW@20 | ||
| 458 | GetUserObjectSecurity@20 | ||
| 459 | GetWinStationInfo@4 | ||
| 460 | GetWindow@8 | ||
| 461 | GetWindowBand@8 | ||
| 462 | GetWindowCompositionAttribute@8 | ||
| 463 | GetWindowCompositionInfo@8 | ||
| 464 | GetWindowContextHelpId@4 | ||
| 465 | GetWindowDC@4 | ||
| 466 | GetWindowDisplayAffinity@8 | ||
| 467 | GetWindowFeedbackSetting@20 | ||
| 468 | GetWindowInfo@8 | ||
| 469 | GetWindowLongA@8 | ||
| 470 | GetWindowLongW@8 | ||
| 471 | GetWindowMinimizeRect@8 | ||
| 472 | GetWindowModuleFileName@12 | ||
| 473 | GetWindowModuleFileNameA@12 | ||
| 474 | GetWindowModuleFileNameW@12 | ||
| 475 | GetWindowPlacement@8 | ||
| 476 | GetWindowRect@8 | ||
| 477 | GetWindowRgn@8 | ||
| 478 | GetWindowRgnBox@8 | ||
| 479 | GetWindowRgnEx@12 | ||
| 480 | GetWindowTextA@12 | ||
| 481 | GetWindowTextLengthA@4 | ||
| 482 | GetWindowTextLengthW@4 | ||
| 483 | GetWindowTextW@12 | ||
| 484 | GetWindowThreadProcessId@8 | ||
| 485 | GetWindowWord@8 | ||
| 486 | GhostWindowFromHungWindow@4 | ||
| 487 | GrayStringA@36 | ||
| 488 | GrayStringW@36 | ||
| 489 | HideCaret@4 | ||
| 490 | HiliteMenuItem@16 | ||
| 491 | HungWindowFromGhostWindow@4 | ||
| 492 | IMPGetIMEA@8 | ||
| 493 | IMPGetIMEW@8 | ||
| 494 | IMPQueryIMEA@4 | ||
| 495 | IMPQueryIMEW@4 | ||
| 496 | IMPSetIMEA@8 | ||
| 497 | IMPSetIMEW@8 | ||
| 498 | ImpersonateDdeClientWindow@8 | ||
| 499 | InSendMessage@0 | ||
| 500 | InSendMessageEx@4 | ||
| 501 | InflateRect@12 | ||
| 502 | InitializeLpkHooks@4 | ||
| 503 | InitializeWin32EntryTable@4 | ||
| 504 | InitializeTouchInjection@8 | ||
| 505 | InjectTouchInput@8 | ||
| 506 | InsertMenuA@20 | ||
| 507 | InsertMenuItemA@16 | ||
| 508 | InsertMenuItemW@16 | ||
| 509 | InsertMenuW@20 | ||
| 510 | InternalGetWindowIcon@8 | ||
| 511 | ;ord_2001@4 @2001 | ||
| 512 | ;ord_2002@4 @2002 | ||
| 513 | InternalGetWindowText@12 | ||
| 514 | IntersectRect@12 | ||
| 515 | ;ord_2005@4 @2005 | ||
| 516 | InvalidateRect@12 | ||
| 517 | InvalidateRgn@12 | ||
| 518 | InvertRect@8 | ||
| 519 | IsCharAlphaA@4 | ||
| 520 | ;ord_2010@16 @2010 | ||
| 521 | IsCharAlphaNumericA@4 | ||
| 522 | IsCharAlphaNumericW@4 | ||
| 523 | IsCharAlphaW@4 | ||
| 524 | IsCharLowerA@4 | ||
| 525 | IsCharLowerW@4 | ||
| 526 | IsCharUpperA@4 | ||
| 527 | IsCharUpperW@4 | ||
| 528 | IsChild@8 | ||
| 529 | IsClipboardFormatAvailable@4 | ||
| 530 | IsDialogMessage@8 | ||
| 531 | IsDialogMessageA@8 | ||
| 532 | IsDialogMessageW@8 | ||
| 533 | IsDlgButtonChecked@8 | ||
| 534 | IsGUIThread@4 | ||
| 535 | IsHungAppWindow@4 | ||
| 536 | IsIconic@4 | ||
| 537 | IsImmersiveProcess@4 | ||
| 538 | IsInDesktopWindowBand@4 | ||
| 539 | IsMenu@4 | ||
| 540 | IsProcess16Bit@0 | ||
| 541 | IsMouseInPointerEnabled@0 | ||
| 542 | IsProcessDPIAware@0 | ||
| 543 | IsQueueAttached@0 | ||
| 544 | IsRectEmpty@4 | ||
| 545 | IsSETEnabled@0 | ||
| 546 | IsServerSideWindow@4 | ||
| 547 | IsThreadDesktopComposited@0 | ||
| 548 | IsTopLevelWindow@4 | ||
| 549 | IsTouchWindow@8 | ||
| 550 | IsWinEventHookInstalled@4 | ||
| 551 | IsWindow@4 | ||
| 552 | IsWindowEnabled@4 | ||
| 553 | IsWindowInDestroy@4 | ||
| 554 | IsWindowRedirectedForPrint@4 | ||
| 555 | IsWindowUnicode@4 | ||
| 556 | IsWindowVisible@4 | ||
| 557 | IsWow64Message@0 | ||
| 558 | IsZoomed@4 | ||
| 559 | KillSystemTimer@8 | ||
| 560 | KillTimer@8 | ||
| 561 | LoadAcceleratorsA@8 | ||
| 562 | LoadAcceleratorsW@8 | ||
| 563 | LoadBitmapA@8 | ||
| 564 | LoadBitmapW@8 | ||
| 565 | LoadCursorA@8 | ||
| 566 | LoadCursorFromFileA@4 | ||
| 567 | LoadCursorFromFileW@4 | ||
| 568 | ;ord_2000@0 @2000 | ||
| 569 | ;ord_2001@4 @2001 | ||
| 570 | ;ord_2002@4 @2002 | ||
| 571 | LoadCursorW@8 | ||
| 572 | LoadIconA@8 | ||
| 573 | ;ord_2005@4 @2005 | ||
| 574 | LoadIconW@8 | ||
| 575 | LoadImageA@24 | ||
| 576 | LoadImageW@24 | ||
| 577 | LoadKeyboardLayoutA@8 | ||
| 578 | LoadKeyboardLayoutEx@12 | ||
| 579 | LoadKeyboardLayoutW@8 | ||
| 580 | LoadLocalFonts@0 | ||
| 581 | LoadMenuA@8 | ||
| 582 | LoadMenuIndirectA@4 | ||
| 583 | LoadMenuIndirectW@4 | ||
| 584 | LoadMenuW@8 | ||
| 585 | LoadRemoteFonts@0 | ||
| 586 | LoadStringA@16 | ||
| 587 | LoadStringW@16 | ||
| 588 | LockSetForegroundWindow@4 | ||
| 589 | LockWindowStation@4 | ||
| 590 | LockWindowUpdate@4 | ||
| 591 | LockWorkStation@0 | ||
| 592 | LogicalToPhysicalPoint@8 | ||
| 593 | LogicalToPhysicalPointForPerMonitorDPI@8 | ||
| 594 | LookupIconIdFromDirectory@8 | ||
| 595 | LookupIconIdFromDirectoryEx@20 | ||
| 596 | MBToWCSEx@24 | ||
| 597 | MBToWCSExt@20 | ||
| 598 | MB_GetString@4 | ||
| 599 | MapDialogRect@8 | ||
| 600 | MapVirtualKeyA@8 | ||
| 601 | MapVirtualKeyExA@12 | ||
| 602 | MapVirtualKeyExW@12 | ||
| 603 | MapVirtualKeyW@8 | ||
| 604 | MapWindowPoints@16 | ||
| 605 | MenuItemFromPoint@16 | ||
| 606 | MenuWindowProcA@20 | ||
| 607 | MenuWindowProcW@20 | ||
| 608 | MessageBeep@4 | ||
| 609 | MessageBoxA@16 | ||
| 610 | MessageBoxExA@20 | ||
| 611 | MessageBoxExW@20 | ||
| 612 | MessageBoxIndirectA@4 | ||
| 613 | MessageBoxIndirectW@4 | ||
| 614 | MessageBoxTimeoutA@24 | ||
| 615 | MessageBoxTimeoutW@24 | ||
| 616 | MessageBoxW@16 | ||
| 617 | ModifyMenuA@20 | ||
| 618 | ModifyMenuW@20 | ||
| 619 | MonitorFromPoint@12 | ||
| 620 | MonitorFromRect@8 | ||
| 621 | MonitorFromWindow@8 | ||
| 622 | MoveWindow@24 | ||
| 623 | MsgWaitForMultipleObjects@20 | ||
| 624 | MsgWaitForMultipleObjectsEx@20 | ||
| 625 | NotifyOverlayWindow@8 | ||
| 626 | NotifyWinEvent@16 | ||
| 627 | OemKeyScan@4 | ||
| 628 | OemToCharA@8 | ||
| 629 | OemToCharBuffA@12 | ||
| 630 | OemToCharBuffW@12 | ||
| 631 | OemToCharW@8 | ||
| 632 | OffsetRect@12 | ||
| 633 | OpenClipboard@4 | ||
| 634 | OpenDesktopA@16 | ||
| 635 | OpenDesktopW@16 | ||
| 636 | OpenIcon@4 | ||
| 637 | OpenInputDesktop@12 | ||
| 638 | OpenThreadDesktop@16 | ||
| 639 | OpenWindowStationA@12 | ||
| 640 | OpenWindowStationW@12 | ||
| 641 | PackDDElParam@12 | ||
| 642 | PackTouchHitTestingProximityEvaluation@8 | ||
| 643 | PaintDesktop@4 | ||
| 644 | PaintMenuBar@24 | ||
| 645 | PaintMonitor@12 | ||
| 646 | PeekMessageA@20 | ||
| 647 | PeekMessageW@20 | ||
| 648 | PhysicalToLogicalPoint@8 | ||
| 649 | PhysicalToLogicalPointForPerMonitorDPI@8 | ||
| 650 | PostMessageA@16 | ||
| 651 | PostMessageW@16 | ||
| 652 | PostQuitMessage@4 | ||
| 653 | PostThreadMessageA@16 | ||
| 654 | PostThreadMessageW@16 | ||
| 655 | PrintWindow@12 | ||
| 656 | PrivateExtractIconExA@20 | ||
| 657 | PrivateExtractIconExW@20 | ||
| 658 | PrivateExtractIconsA@32 | ||
| 659 | PrivateExtractIconsW@32 | ||
| 660 | PrivateSetDbgTag@8 | ||
| 661 | PrivateSetRipFlags@8 | ||
| 662 | PrivateRegisterICSProc@4 | ||
| 663 | PtInRect@12 | ||
| 664 | QueryBSDRWindow@0 | ||
| 665 | QueryDisplayConfig@24 | ||
| 666 | QuerySendMessage@4 | ||
| 667 | QueryUserCounters@20 | ||
| 668 | RealChildWindowFromPoint@12 | ||
| 669 | RealGetWindowClass@12 | ||
| 670 | RealGetWindowClassA@12 | ||
| 671 | RealGetWindowClassW@12 | ||
| 672 | ReasonCodeNeedsBugID@4 | ||
| 673 | ReasonCodeNeedsComment@4 | ||
| 674 | RecordShutdownReason@4 | ||
| 675 | RedrawWindow@16 | ||
| 676 | RegisterBSDRWindow@8 | ||
| 677 | RegisterClassA@4 | ||
| 678 | RegisterClassExA@4 | ||
| 679 | RegisterClassExW@4 | ||
| 680 | RegisterClassW@4 | ||
| 681 | RegisterClipboardFormatA@4 | ||
| 682 | RegisterClipboardFormatW@4 | ||
| 683 | RegisterDeviceNotificationA@12 | ||
| 684 | RegisterDeviceNotificationW@12 | ||
| 685 | RegisterErrorReportingDialog@8 | ||
| 686 | RegisterFrostWindow@8 | ||
| 687 | RegisterGhostWindow@8 | ||
| 688 | RegisterHotKey@16 | ||
| 689 | RegisterPowerSettingNotification@12 | ||
| 690 | RegisterLogonProcess@8 | ||
| 691 | RegisterMessagePumpHook@4 | ||
| 692 | RegisterPointerDeviceNotifications@8 | ||
| 693 | RegisterPointerInputTarget@8 | ||
| 694 | RegisterPowerSettingNotification@12 | ||
| 695 | RegisterRawInputDevices@12 | ||
| 696 | RegisterServicesProcess@4 | ||
| 697 | RegisterSessionPort@4 ; Undocumented, rumored to be related to ALPC - http://blogs.msdn.com/b/ntdebugging/archive/2007/07/26/lpc-local-procedure-calls-part-1-architecture.aspx | ||
| 698 | RegisterShellHookWindow@4 | ||
| 699 | RegisterSuspendResumeNotification@8 | ||
| 700 | RegisterSystemThread@8 | ||
| 701 | RegisterTasklist@4 | ||
| 702 | RegisterTouchHitTestingWindow@8 | ||
| 703 | RegisterTouchWindow@8 | ||
| 704 | RegisterUserApiHook@4 ; Prototype changed in 2003 - https://www.reactos.org/wiki/Techwiki:RegisterUserApiHook | ||
| 705 | RegisterWindowMessageA@4 | ||
| 706 | RegisterWindowMessageW@4 | ||
| 707 | ReleaseCapture@0 | ||
| 708 | ReleaseDC@8 | ||
| 709 | RemoveClipboardFormatListener@4 | ||
| 710 | RemoveMenu@12 | ||
| 711 | RemovePropA@8 | ||
| 712 | RemovePropW@8 | ||
| 713 | ReplyMessage@4 | ||
| 714 | ResolveDesktopForWOW@4 | ||
| 715 | ReuseDDElParam@20 | ||
| 716 | ScreenToClient@8 | ||
| 717 | ScrollChildren@12 | ||
| 718 | ScrollDC@28 | ||
| 719 | ScrollWindow@20 | ||
| 720 | ScrollWindowEx@32 | ||
| 721 | SendDlgItemMessageA@20 | ||
| 722 | SendDlgItemMessageW@20 | ||
| 723 | SendIMEMessageExA@8 | ||
| 724 | SendIMEMessageExW@8 | ||
| 725 | SendInput@12 | ||
| 726 | SendMessageA@16 | ||
| 727 | SendMessageCallbackA@24 | ||
| 728 | SendMessageCallbackW@24 | ||
| 729 | SendMessageTimeoutA@28 | ||
| 730 | SendMessageTimeoutW@28 | ||
| 731 | SendMessageW@16 | ||
| 732 | SendNotifyMessageA@16 | ||
| 733 | SendNotifyMessageW@16 | ||
| 734 | SetActiveWindow@4 | ||
| 735 | SetCapture@4 | ||
| 736 | SetCaretBlinkTime@4 | ||
| 737 | SetCaretPos@8 | ||
| 738 | SetClassLongA@12 | ||
| 739 | SetClassLongW@12 | ||
| 740 | SetClassWord@12 | ||
| 741 | SetClipboardData@8 | ||
| 742 | SetClipboardViewer@4 | ||
| 743 | SetConsoleReserveKeys@8 | ||
| 744 | SetCoalescableTimer@20 | ||
| 745 | SetCursor@4 | ||
| 746 | SetCursorContents@8 | ||
| 747 | SetCursorPos@8 | ||
| 748 | SetDebugErrorLevel@4 | ||
| 749 | SetDeskWallpaper@4 | ||
| 750 | SetDisplayAutoRotationPreferences@4 | ||
| 751 | SetDisplayConfig@20 | ||
| 752 | SetDlgItemInt@16 | ||
| 753 | SetDlgItemTextA@12 | ||
| 754 | SetDlgItemTextW@12 | ||
| 755 | SetDoubleClickTime@4 | ||
| 756 | SetFocus@4 | ||
| 757 | SetForegroundWindow@4 | ||
| 758 | SetGestureConfig@20 | ||
| 759 | SetImmersiveBackgroundWindow@4 | ||
| 760 | SetInternalWindowPos@16 | ||
| 761 | SetKeyboardState@4 | ||
| 762 | SetLastErrorEx@8 | ||
| 763 | SetLayeredWindowAttributes@16 | ||
| 764 | SetLogonNotifyWindow@4 | ||
| 765 | SetMagnificationDesktopColorEffect@4 | ||
| 766 | SetMagnificationDesktopMagnification@16 | ||
| 767 | SetMagnificationLensCtxInformation@16 | ||
| 768 | SetMenu@8 | ||
| 769 | SetMenuContextHelpId@8 | ||
| 770 | SetMenuDefaultItem@12 | ||
| 771 | SetMenuInfo@8 | ||
| 772 | SetMenuItemBitmaps@20 | ||
| 773 | SetMenuItemInfoA@16 | ||
| 774 | SetMenuItemInfoW@16 | ||
| 775 | SetMessageExtraInfo@4 | ||
| 776 | SetMessageQueue@4 | ||
| 777 | SetMirrorRendering@8 | ||
| 778 | SetParent@8 | ||
| 779 | SetPhysicalCursorPos@8 | ||
| 780 | SetProcessDPIAware@0 | ||
| 781 | SetProcessDefaultLayout@4 | ||
| 782 | SetProcessDpiAwarenessInternal@4 | ||
| 783 | SetProcessRestrictionExemption@4 | ||
| 784 | SetProcessWindowStation@4 | ||
| 785 | SetProgmanWindow@4 | ||
| 786 | SetPropA@12 | ||
| 787 | SetPropW@12 | ||
| 788 | SetRect@20 | ||
| 789 | SetRectEmpty@4 | ||
| 790 | SetScrollInfo@16 | ||
| 791 | SetScrollPos@16 | ||
| 792 | SetScrollRange@20 | ||
| 793 | SetShellWindow@4 | ||
| 794 | SetShellWindowEx@8 | ||
| 795 | SetSysColors@12 | ||
| 796 | SetSysColorsTemp@12 | ||
| 797 | SetSystemCursor@8 | ||
| 798 | SetSystemMenu@8 | ||
| 799 | SetSystemTimer@16 | ||
| 800 | SetTaskmanWindow@4 | ||
| 801 | SetThreadDesktop@4 | ||
| 802 | SetThreadInputBlocked@8 | ||
| 803 | SetTimer@16 | ||
| 804 | SetUserObjectInformationA@16 | ||
| 805 | SetUserObjectInformationW@16 | ||
| 806 | SetUserObjectSecurity@12 | ||
| 807 | SetWinEventHook@28 | ||
| 808 | SetWindowBand@12 | ||
| 809 | SetWindowCompositionAttribute@8 | ||
| 810 | SetWindowCompositionTransition@28 | ||
| 811 | SetWindowContextHelpId@8 | ||
| 812 | SetWindowDisplayAffinity@8 | ||
| 813 | SetWindowFeedbackSetting@20 | ||
| 814 | SetWindowLongA@12 | ||
| 815 | SetWindowLongW@12 | ||
| 816 | SetWindowPlacement@8 | ||
| 817 | SetWindowPos@28 | ||
| 818 | SetWindowRgn@12 | ||
| 819 | SetWindowRgnEx@12 | ||
| 820 | SetWindowStationUser@16 | ||
| 821 | SetWindowTextA@8 | ||
| 822 | SetWindowTextW@8 | ||
| 823 | SetWindowWord@12 | ||
| 824 | SetWindowsHookA@8 | ||
| 825 | SetWindowsHookExA@16 | ||
| 826 | SetWindowsHookExW@16 | ||
| 827 | SetWindowsHookW@8 | ||
| 828 | SfmDxBindSwapChain@12 | ||
| 829 | SfmDxGetSwapChainStats@8 | ||
| 830 | SfmDxOpenSwapChain@16 | ||
| 831 | SfmDxQuerySwapChainBindingStatus@12 | ||
| 832 | SfmDxReleaseSwapChain@8 | ||
| 833 | SfmDxReportPendingBindingsToDwm@0 | ||
| 834 | SfmDxSetSwapChainBindingStatus@8 | ||
| 835 | SfmDxSetSwapChainStats@8 | ||
| 836 | ShowCaret@4 | ||
| 837 | ShowCursor@4 | ||
| 838 | ShowOwnedPopups@8 | ||
| 839 | ShowScrollBar@12 | ||
| 840 | ShowStartGlass@4 | ||
| 841 | ShowSystemCursor@4 | ||
| 842 | ShowWindow@8 | ||
| 843 | ShowWindowAsync@8 | ||
| 844 | ShutdownBlockReasonCreate@8 | ||
| 845 | ShutdownBlockReasonDestroy@4 | ||
| 846 | ShutdownBlockReasonQuery@12 | ||
| 847 | SignalRedirectionStartComplete@0 | ||
| 848 | SkipPointerFrameMessages@4 | ||
| 849 | SoftModalMessageBox@4 | ||
| 850 | SoundSentry@0 | ||
| 851 | SubtractRect@12 | ||
| 852 | SwapMouseButton@4 | ||
| 853 | SwitchDesktop@4 | ||
| 854 | SwitchDesktopWithFade@12 ; Same as SwithDesktop(), only with fade (done at log-in), only usable by winlogon - http://blog.airesoft.co.uk/2010/08/things-microsoft-can-do-that-you-cant/ | ||
| 855 | SwitchToThisWindow@8 | ||
| 856 | SystemParametersInfoA@16 | ||
| 857 | SystemParametersInfoW@16 | ||
| 858 | TabbedTextOutA@32 | ||
| 859 | TabbedTextOutW@32 | ||
| 860 | TileChildWindows@8 | ||
| 861 | TileWindows@20 | ||
| 862 | ToAscii@20 | ||
| 863 | ToAsciiEx@24 | ||
| 864 | ToUnicode@24 | ||
| 865 | ToUnicodeEx@28 | ||
| 866 | TrackMouseEvent@4 | ||
| 867 | TrackPopupMenu@28 | ||
| 868 | TrackPopupMenuEx@24 | ||
| 869 | TranslateAccelerator@12 | ||
| 870 | TranslateAcceleratorA@12 | ||
| 871 | TranslateAcceleratorW@12 | ||
| 872 | TranslateMDISysAccel@8 | ||
| 873 | TranslateMessage@4 | ||
| 874 | TranslateMessageEx@8 | ||
| 875 | UnhookWinEvent@4 | ||
| 876 | UnhookWindowsHook@8 | ||
| 877 | UnhookWindowsHookEx@4 | ||
| 878 | UnionRect@12 | ||
| 879 | UnloadKeyboardLayout@4 | ||
| 880 | UnlockWindowStation@4 | ||
| 881 | UnpackDDElParam@16 | ||
| 882 | UnregisterClassA@8 | ||
| 883 | UnregisterClassW@8 | ||
| 884 | UnregisterDeviceNotification@4 | ||
| 885 | UnregisterHotKey@8 | ||
| 886 | UnregisterMessagePumpHook@0 | ||
| 887 | UnregisterPointerInputTarget@8 | ||
| 888 | UnregisterPowerSettingNotification@4 | ||
| 889 | UnregisterSessionPort@0 | ||
| 890 | UnregisterSuspendResumeNotification@4 | ||
| 891 | UnregisterTouchWindow@4 | ||
| 892 | UnregisterUserApiHook@0 | ||
| 893 | UpdateDefaultDesktopThumbnail@20 | ||
| 894 | UpdateLayeredWindow@36 | ||
| 895 | UpdateLayeredWindowIndirect@8 | ||
| 896 | UpdatePerUserSystemParameters@4 ; Undocumented, seems to apply certain registry settings to desktop, etc. ReactOS has @8 version - http://doxygen.reactos.org/d0/d92/win32ss_2user_2user32_2misc_2misc_8c_a1ff565f0af6bac6dce604f9f4473fe79.html ; @4 is rumored to be without the first DWORD | ||
| 897 | UpdateWindow@4 | ||
| 898 | UpdateWindowInputSinkHints@8 | ||
| 899 | UpdateWindowTransform@12 | ||
| 900 | User32InitializeImmEntryTable@4 | ||
| 901 | UserClientDllInitialize@12 | ||
| 902 | UserHandleGrantAccess@12 | ||
| 903 | UserLpkPSMTextOut@24 | ||
| 904 | UserLpkTabbedTextOut@48 | ||
| 905 | UserRealizePalette@4 | ||
| 906 | UserRegisterWowHandlers@8 | ||
| 907 | VRipOutput@0 | ||
| 908 | VTagOutput@0 | ||
| 909 | ValidateRect@8 | ||
| 910 | ValidateRgn@8 | ||
| 911 | VkKeyScanA@4 | ||
| 912 | VkKeyScanExA@8 | ||
| 913 | VkKeyScanExW@8 | ||
| 914 | VkKeyScanW@4 | ||
| 915 | WCSToMBEx@24 | ||
| 916 | WINNLSEnableIME@8 | ||
| 917 | WINNLSGetEnableStatus@4 | ||
| 918 | WINNLSGetIMEHotkey@4 | ||
| 919 | WaitForInputIdle@8 | ||
| 920 | WaitForRedirectionStartComplete@0 | ||
| 921 | WaitMessage@0 | ||
| 922 | Win32PoolAllocationStats@24 | ||
| 923 | WinHelpA@16 | ||
| 924 | WinHelpW@16 | ||
| 925 | WindowFromDC@4 | ||
| 926 | WindowFromPhysicalPoint@8 | ||
| 927 | WindowFromPoint@8 | ||
| 928 | _UserTestTokenForInteractive@8 | ||
| 929 | gSharedInfo DATA | ||
| 930 | gapfnScSendMessage DATA | ||
| 931 | keybd_event@16 | ||
| 932 | mouse_event@20 | ||
| 933 | wsprintfA | ||
| 934 | wsprintfW | ||
| 935 | wvsprintfA@12 | ||
| 936 | wvsprintfW@12 | ||
| 937 | ;ord_2500@16 @2500 | ||
| 938 | ;ord_2501@12 @2501 | ||
| 939 | ;ord_2502@8 @2502 | ||
| 940 | ;ord_2503@24 @2503 | ||
| 941 | ;ord_2504@8 @2504 | ||
| 942 | ;ord_2505@8 @2505 | ||
| 943 | ;ord_2506@12 @2506 | ||
| 944 | ;ord_2507@4 @2507 | ||
| 945 | ;ord_2508@8 @2508 | ||
| 946 | ;ord_2509@4 @2509 | ||
| 947 | ;ord_2510@12 @2510 | ||
| 948 | ;ord_2511@8 @2511 | ||
| 949 | ;ord_2512@12 @2512 | ||
| 950 | ;ord_2513@4 @2513 | ||
| 951 | ;ord_2514@8 @2514 | ||
| 952 | ;ord_2515@8 @2515 | ||
| 953 | ;ord_2516@12 @2516 | ||
| 954 | ;ord_2517@4 @2517 | ||
| 955 | ;ord_2518@0 @2518 | ||
| 956 | ;ord_2519@4 @2519 | ||
| 957 | ;ord_2520@0 @2520 | ||
| 958 | ;ord_2521@8 @2521 | ||
| 959 | ;ord_2522@4 @2522 | ||
| 960 | ;ord_2523@8 @2523 | ||
| 961 | ;ord_2524@8 @2524 | ||
| 962 | ;ord_2525@12 @2525 | ||
| 963 | ;ord_2526@12 @2526 | ||
| 964 | ;ord_2527@12 @2527 | ||
| 965 | IsThreadMessageQueueAttached@4 | ||
| 966 | ;ord_2529@4 @2529 | ||
| 967 | ;ord_2530@8 @2530 | ||
| 968 | ;ord_2531@16 @2531 | ||
| 969 | ;ord_2532@8 @2532 | ||
| 970 | ;ord_2533@4 @2533 | ||
| 971 | ;ord_2534@8 @2534 | ||
| 972 | ;ord_2535@0 @2535 | ||
| 973 | ;ord_2536@8 @2536 | ||
| 974 | ;ord_2537@16 @2537 | ||
| 975 | ;ord_2538@4 @2538 | ||
| 976 | ;ord_2539@4 @2539 | ||
| 977 | ;ord_2540@4 @2540 | ||
| 978 | ;ord_2541@0 @2541 | ||
| 979 | ;ord_2544@4 @2544 | ||
| 980 | ;ord_2545@8 @2545 | ||
| 981 | ;ord_2546@4 @2546 | ||
| 982 | ;ord_2547@4 @2547 | ||
| 983 | ;ord_2548@4 @2548 | ||
| 984 | ;ord_2549@4 @2549 | ||
| 985 | ;ord_2550@8 @2550 | ||
| 986 | ;ord_2551@20 @2551 | ||
| 987 | ;ord_2552@8 @2552 | ||
| 988 | ;ord_2553@32 @2553 | ||
| 989 | ;ord_2554@12 @2554 | ||
| 990 | ;ord_2555@16 @2555 | ||
| 991 | ;ord_2556@8 @2556 | ||
| 992 | ;ord_2557@12 @2557 | ||
| 993 | ;ord_2558@12 @2558 | ||
| 994 | ;ord_2559@16 @2559 | ||
| 995 | ;ord_2560@20 @2560 | ||
| 996 | ;ord_2561@0 @2561 | ||
| 997 | ;ord_2562@0 @2562 | ||
| 998 | ;ord_2563@0 @2563 | ||
lib/std/array_list.zig+407-167| ... | @@ -371,7 +371,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ | ... | @@ -371,7 +371,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 371 | pub fn initCapacity(allocator: *Allocator, num: usize) !Self { | 371 | pub fn initCapacity(allocator: *Allocator, num: usize) !Self { |
| 372 | var self = Self{}; | 372 | var self = Self{}; |
| 373 | 373 | ||
| 374 | const new_memory = try self.allocator.allocAdvanced(T, alignment, num, .at_least); | 374 | const new_memory = try allocator.allocAdvanced(T, alignment, num, .at_least); |
| 375 | self.items.ptr = new_memory.ptr; | 375 | self.items.ptr = new_memory.ptr; |
| 376 | self.capacity = new_memory.len; | 376 | self.capacity = new_memory.len; |
| 377 | 377 | ||
| ... | @@ -419,7 +419,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ | ... | @@ -419,7 +419,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 419 | /// Replace range of elements `list[start..start+len]` with `new_items` | 419 | /// Replace range of elements `list[start..start+len]` with `new_items` |
| 420 | /// grows list if `len < new_items.len`. may allocate | 420 | /// grows list if `len < new_items.len`. may allocate |
| 421 | /// shrinks list if `len > new_items.len` | 421 | /// shrinks list if `len > new_items.len` |
| 422 | pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: SliceConst) !void { | 422 | pub fn replaceRange(self: *Self, allocator: *Allocator, start: usize, len: usize, new_items: SliceConst) !void { |
| 423 | var managed = self.toManaged(allocator); | 423 | var managed = self.toManaged(allocator); |
| 424 | try managed.replaceRange(start, len, new_items); | 424 | try managed.replaceRange(start, len, new_items); |
| 425 | self.* = managed.toUnmanaged(); | 425 | self.* = managed.toUnmanaged(); |
| ... | @@ -617,201 +617,414 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ | ... | @@ -617,201 +617,414 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 617 | }; | 617 | }; |
| 618 | } | 618 | } |
| 619 | 619 | ||
| 620 | test "std.ArrayList.init" { | 620 | test "std.ArrayList/ArrayListUnmanaged.init" { |
| 621 | var list = ArrayList(i32).init(testing.allocator); | 621 | { |
| 622 | defer list.deinit(); | 622 | var list = ArrayList(i32).init(testing.allocator); |
| 623 | defer list.deinit(); | ||
| 623 | 624 | ||
| 624 | testing.expect(list.items.len == 0); | 625 | testing.expect(list.items.len == 0); |
| 625 | testing.expect(list.capacity == 0); | 626 | testing.expect(list.capacity == 0); |
| 626 | } | 627 | } |
| 627 | 628 | ||
| 628 | test "std.ArrayList.initCapacity" { | 629 | { |
| 629 | var list = try ArrayList(i8).initCapacity(testing.allocator, 200); | 630 | var list = ArrayListUnmanaged(i32){}; |
| 630 | defer list.deinit(); | ||
| 631 | testing.expect(list.items.len == 0); | ||
| 632 | testing.expect(list.capacity >= 200); | ||
| 633 | } | ||
| 634 | 631 | ||
| 635 | test "std.ArrayList.basic" { | 632 | testing.expect(list.items.len == 0); |
| 636 | var list = ArrayList(i32).init(testing.allocator); | 633 | testing.expect(list.capacity == 0); |
| 637 | defer list.deinit(); | 634 | } |
| 635 | } | ||
| 638 | 636 | ||
| 637 | test "std.ArrayList/ArrayListUnmanaged.initCapacity" { | ||
| 638 | const a = testing.allocator; | ||
| 639 | { | 639 | { |
| 640 | var i: usize = 0; | 640 | var list = try ArrayList(i8).initCapacity(a, 200); |
| 641 | while (i < 10) : (i += 1) { | 641 | defer list.deinit(); |
| 642 | list.append(@intCast(i32, i + 1)) catch unreachable; | 642 | testing.expect(list.items.len == 0); |
| 643 | } | 643 | testing.expect(list.capacity >= 200); |
| 644 | } | ||
| 645 | { | ||
| 646 | var list = try ArrayListUnmanaged(i8).initCapacity(a, 200); | ||
| 647 | defer list.deinit(a); | ||
| 648 | testing.expect(list.items.len == 0); | ||
| 649 | testing.expect(list.capacity >= 200); | ||
| 644 | } | 650 | } |
| 651 | } | ||
| 645 | 652 | ||
| 653 | test "std.ArrayList/ArrayListUnmanaged.basic" { | ||
| 654 | const a = testing.allocator; | ||
| 646 | { | 655 | { |
| 647 | var i: usize = 0; | 656 | var list = ArrayList(i32).init(a); |
| 648 | while (i < 10) : (i += 1) { | 657 | defer list.deinit(); |
| 649 | testing.expect(list.items[i] == @intCast(i32, i + 1)); | 658 | |
| 659 | { | ||
| 660 | var i: usize = 0; | ||
| 661 | while (i < 10) : (i += 1) { | ||
| 662 | list.append(@intCast(i32, i + 1)) catch unreachable; | ||
| 663 | } | ||
| 664 | } | ||
| 665 | |||
| 666 | { | ||
| 667 | var i: usize = 0; | ||
| 668 | while (i < 10) : (i += 1) { | ||
| 669 | testing.expect(list.items[i] == @intCast(i32, i + 1)); | ||
| 670 | } | ||
| 671 | } | ||
| 672 | |||
| 673 | for (list.items) |v, i| { | ||
| 674 | testing.expect(v == @intCast(i32, i + 1)); | ||
| 650 | } | 675 | } |
| 651 | } | ||
| 652 | 676 | ||
| 653 | for (list.items) |v, i| { | 677 | testing.expect(list.pop() == 10); |
| 654 | testing.expect(v == @intCast(i32, i + 1)); | 678 | testing.expect(list.items.len == 9); |
| 679 | |||
| 680 | list.appendSlice(&[_]i32{ 1, 2, 3 }) catch unreachable; | ||
| 681 | testing.expect(list.items.len == 12); | ||
| 682 | testing.expect(list.pop() == 3); | ||
| 683 | testing.expect(list.pop() == 2); | ||
| 684 | testing.expect(list.pop() == 1); | ||
| 685 | testing.expect(list.items.len == 9); | ||
| 686 | |||
| 687 | list.appendSlice(&[_]i32{}) catch unreachable; | ||
| 688 | testing.expect(list.items.len == 9); | ||
| 689 | |||
| 690 | // can only set on indices < self.items.len | ||
| 691 | list.items[7] = 33; | ||
| 692 | list.items[8] = 42; | ||
| 693 | |||
| 694 | testing.expect(list.pop() == 42); | ||
| 695 | testing.expect(list.pop() == 33); | ||
| 655 | } | 696 | } |
| 697 | { | ||
| 698 | var list = ArrayListUnmanaged(i32){}; | ||
| 699 | defer list.deinit(a); | ||
| 700 | |||
| 701 | { | ||
| 702 | var i: usize = 0; | ||
| 703 | while (i < 10) : (i += 1) { | ||
| 704 | list.append(a, @intCast(i32, i + 1)) catch unreachable; | ||
| 705 | } | ||
| 706 | } | ||
| 707 | |||
| 708 | { | ||
| 709 | var i: usize = 0; | ||
| 710 | while (i < 10) : (i += 1) { | ||
| 711 | testing.expect(list.items[i] == @intCast(i32, i + 1)); | ||
| 712 | } | ||
| 713 | } | ||
| 714 | |||
| 715 | for (list.items) |v, i| { | ||
| 716 | testing.expect(v == @intCast(i32, i + 1)); | ||
| 717 | } | ||
| 656 | 718 | ||
| 657 | testing.expect(list.pop() == 10); | 719 | testing.expect(list.pop() == 10); |
| 658 | testing.expect(list.items.len == 9); | 720 | testing.expect(list.items.len == 9); |
| 659 | 721 | ||
| 660 | list.appendSlice(&[_]i32{ 1, 2, 3 }) catch unreachable; | 722 | list.appendSlice(a, &[_]i32{ 1, 2, 3 }) catch unreachable; |
| 661 | testing.expect(list.items.len == 12); | 723 | testing.expect(list.items.len == 12); |
| 662 | testing.expect(list.pop() == 3); | 724 | testing.expect(list.pop() == 3); |
| 663 | testing.expect(list.pop() == 2); | 725 | testing.expect(list.pop() == 2); |
| 664 | testing.expect(list.pop() == 1); | 726 | testing.expect(list.pop() == 1); |
| 665 | testing.expect(list.items.len == 9); | 727 | testing.expect(list.items.len == 9); |
| 666 | 728 | ||
| 667 | list.appendSlice(&[_]i32{}) catch unreachable; | 729 | list.appendSlice(a, &[_]i32{}) catch unreachable; |
| 668 | testing.expect(list.items.len == 9); | 730 | testing.expect(list.items.len == 9); |
| 669 | 731 | ||
| 670 | // can only set on indices < self.items.len | 732 | // can only set on indices < self.items.len |
| 671 | list.items[7] = 33; | 733 | list.items[7] = 33; |
| 672 | list.items[8] = 42; | 734 | list.items[8] = 42; |
| 673 | 735 | ||
| 674 | testing.expect(list.pop() == 42); | 736 | testing.expect(list.pop() == 42); |
| 675 | testing.expect(list.pop() == 33); | 737 | testing.expect(list.pop() == 33); |
| 738 | } | ||
| 676 | } | 739 | } |
| 677 | 740 | ||
| 678 | test "std.ArrayList.appendNTimes" { | 741 | test "std.ArrayList/ArrayListUnmanaged.appendNTimes" { |
| 679 | var list = ArrayList(i32).init(testing.allocator); | 742 | const a = testing.allocator; |
| 680 | defer list.deinit(); | 743 | { |
| 744 | var list = ArrayList(i32).init(a); | ||
| 745 | defer list.deinit(); | ||
| 746 | |||
| 747 | try list.appendNTimes(2, 10); | ||
| 748 | testing.expectEqual(@as(usize, 10), list.items.len); | ||
| 749 | for (list.items) |element| { | ||
| 750 | testing.expectEqual(@as(i32, 2), element); | ||
| 751 | } | ||
| 752 | } | ||
| 753 | { | ||
| 754 | var list = ArrayListUnmanaged(i32){}; | ||
| 755 | defer list.deinit(a); | ||
| 681 | 756 | ||
| 682 | try list.appendNTimes(2, 10); | 757 | try list.appendNTimes(a, 2, 10); |
| 683 | testing.expectEqual(@as(usize, 10), list.items.len); | 758 | testing.expectEqual(@as(usize, 10), list.items.len); |
| 684 | for (list.items) |element| { | 759 | for (list.items) |element| { |
| 685 | testing.expectEqual(@as(i32, 2), element); | 760 | testing.expectEqual(@as(i32, 2), element); |
| 761 | } | ||
| 686 | } | 762 | } |
| 687 | } | 763 | } |
| 688 | 764 | ||
| 689 | test "std.ArrayList.appendNTimes with failing allocator" { | 765 | test "std.ArrayList/ArrayListUnmanaged.appendNTimes with failing allocator" { |
| 690 | var list = ArrayList(i32).init(testing.failing_allocator); | 766 | const a = testing.failing_allocator; |
| 691 | defer list.deinit(); | 767 | { |
| 692 | testing.expectError(error.OutOfMemory, list.appendNTimes(2, 10)); | 768 | var list = ArrayList(i32).init(a); |
| 769 | defer list.deinit(); | ||
| 770 | testing.expectError(error.OutOfMemory, list.appendNTimes(2, 10)); | ||
| 771 | } | ||
| 772 | { | ||
| 773 | var list = ArrayListUnmanaged(i32){}; | ||
| 774 | defer list.deinit(a); | ||
| 775 | testing.expectError(error.OutOfMemory, list.appendNTimes(a, 2, 10)); | ||
| 776 | } | ||
| 693 | } | 777 | } |
| 694 | 778 | ||
| 695 | test "std.ArrayList.orderedRemove" { | 779 | test "std.ArrayList/ArrayListUnmanaged.orderedRemove" { |
| 696 | var list = ArrayList(i32).init(testing.allocator); | 780 | const a = testing.allocator; |
| 697 | defer list.deinit(); | 781 | { |
| 782 | var list = ArrayList(i32).init(a); | ||
| 783 | defer list.deinit(); | ||
| 784 | |||
| 785 | try list.append(1); | ||
| 786 | try list.append(2); | ||
| 787 | try list.append(3); | ||
| 788 | try list.append(4); | ||
| 789 | try list.append(5); | ||
| 790 | try list.append(6); | ||
| 791 | try list.append(7); | ||
| 792 | |||
| 793 | //remove from middle | ||
| 794 | testing.expectEqual(@as(i32, 4), list.orderedRemove(3)); | ||
| 795 | testing.expectEqual(@as(i32, 5), list.items[3]); | ||
| 796 | testing.expectEqual(@as(usize, 6), list.items.len); | ||
| 797 | |||
| 798 | //remove from end | ||
| 799 | testing.expectEqual(@as(i32, 7), list.orderedRemove(5)); | ||
| 800 | testing.expectEqual(@as(usize, 5), list.items.len); | ||
| 801 | |||
| 802 | //remove from front | ||
| 803 | testing.expectEqual(@as(i32, 1), list.orderedRemove(0)); | ||
| 804 | testing.expectEqual(@as(i32, 2), list.items[0]); | ||
| 805 | testing.expectEqual(@as(usize, 4), list.items.len); | ||
| 806 | } | ||
| 807 | { | ||
| 808 | var list = ArrayListUnmanaged(i32){}; | ||
| 809 | defer list.deinit(a); | ||
| 698 | 810 | ||
| 699 | try list.append(1); | 811 | try list.append(a, 1); |
| 700 | try list.append(2); | 812 | try list.append(a, 2); |
| 701 | try list.append(3); | 813 | try list.append(a, 3); |
| 702 | try list.append(4); | 814 | try list.append(a, 4); |
| 703 | try list.append(5); | 815 | try list.append(a, 5); |
| 704 | try list.append(6); | 816 | try list.append(a, 6); |
| 705 | try list.append(7); | 817 | try list.append(a, 7); |
| 706 | 818 | ||
| 707 | //remove from middle | 819 | //remove from middle |
| 708 | testing.expectEqual(@as(i32, 4), list.orderedRemove(3)); | 820 | testing.expectEqual(@as(i32, 4), list.orderedRemove(3)); |
| 709 | testing.expectEqual(@as(i32, 5), list.items[3]); | 821 | testing.expectEqual(@as(i32, 5), list.items[3]); |
| 710 | testing.expectEqual(@as(usize, 6), list.items.len); | 822 | testing.expectEqual(@as(usize, 6), list.items.len); |
| 711 | 823 | ||
| 712 | //remove from end | 824 | //remove from end |
| 713 | testing.expectEqual(@as(i32, 7), list.orderedRemove(5)); | 825 | testing.expectEqual(@as(i32, 7), list.orderedRemove(5)); |
| 714 | testing.expectEqual(@as(usize, 5), list.items.len); | 826 | testing.expectEqual(@as(usize, 5), list.items.len); |
| 715 | 827 | ||
| 716 | //remove from front | 828 | //remove from front |
| 717 | testing.expectEqual(@as(i32, 1), list.orderedRemove(0)); | 829 | testing.expectEqual(@as(i32, 1), list.orderedRemove(0)); |
| 718 | testing.expectEqual(@as(i32, 2), list.items[0]); | 830 | testing.expectEqual(@as(i32, 2), list.items[0]); |
| 719 | testing.expectEqual(@as(usize, 4), list.items.len); | 831 | testing.expectEqual(@as(usize, 4), list.items.len); |
| 832 | } | ||
| 720 | } | 833 | } |
| 721 | 834 | ||
| 722 | test "std.ArrayList.swapRemove" { | 835 | test "std.ArrayList/ArrayListUnmanaged.swapRemove" { |
| 723 | var list = ArrayList(i32).init(testing.allocator); | 836 | const a = testing.allocator; |
| 724 | defer list.deinit(); | 837 | { |
| 838 | var list = ArrayList(i32).init(a); | ||
| 839 | defer list.deinit(); | ||
| 725 | 840 | ||
| 726 | try list.append(1); | 841 | try list.append(1); |
| 727 | try list.append(2); | 842 | try list.append(2); |
| 728 | try list.append(3); | 843 | try list.append(3); |
| 729 | try list.append(4); | 844 | try list.append(4); |
| 730 | try list.append(5); | 845 | try list.append(5); |
| 731 | try list.append(6); | 846 | try list.append(6); |
| 732 | try list.append(7); | 847 | try list.append(7); |
| 733 | 848 | ||
| 734 | //remove from middle | 849 | //remove from middle |
| 735 | testing.expect(list.swapRemove(3) == 4); | 850 | testing.expect(list.swapRemove(3) == 4); |
| 736 | testing.expect(list.items[3] == 7); | 851 | testing.expect(list.items[3] == 7); |
| 737 | testing.expect(list.items.len == 6); | 852 | testing.expect(list.items.len == 6); |
| 738 | 853 | ||
| 739 | //remove from end | 854 | //remove from end |
| 740 | testing.expect(list.swapRemove(5) == 6); | 855 | testing.expect(list.swapRemove(5) == 6); |
| 741 | testing.expect(list.items.len == 5); | 856 | testing.expect(list.items.len == 5); |
| 742 | 857 | ||
| 743 | //remove from front | 858 | //remove from front |
| 744 | testing.expect(list.swapRemove(0) == 1); | 859 | testing.expect(list.swapRemove(0) == 1); |
| 745 | testing.expect(list.items[0] == 5); | 860 | testing.expect(list.items[0] == 5); |
| 746 | testing.expect(list.items.len == 4); | 861 | testing.expect(list.items.len == 4); |
| 862 | } | ||
| 863 | { | ||
| 864 | var list = ArrayListUnmanaged(i32){}; | ||
| 865 | defer list.deinit(a); | ||
| 866 | |||
| 867 | try list.append(a, 1); | ||
| 868 | try list.append(a, 2); | ||
| 869 | try list.append(a, 3); | ||
| 870 | try list.append(a, 4); | ||
| 871 | try list.append(a, 5); | ||
| 872 | try list.append(a, 6); | ||
| 873 | try list.append(a, 7); | ||
| 874 | |||
| 875 | //remove from middle | ||
| 876 | testing.expect(list.swapRemove(3) == 4); | ||
| 877 | testing.expect(list.items[3] == 7); | ||
| 878 | testing.expect(list.items.len == 6); | ||
| 879 | |||
| 880 | //remove from end | ||
| 881 | testing.expect(list.swapRemove(5) == 6); | ||
| 882 | testing.expect(list.items.len == 5); | ||
| 883 | |||
| 884 | //remove from front | ||
| 885 | testing.expect(list.swapRemove(0) == 1); | ||
| 886 | testing.expect(list.items[0] == 5); | ||
| 887 | testing.expect(list.items.len == 4); | ||
| 888 | } | ||
| 747 | } | 889 | } |
| 748 | 890 | ||
| 749 | test "std.ArrayList.insert" { | 891 | test "std.ArrayList/ArrayListUnmanaged.insert" { |
| 750 | var list = ArrayList(i32).init(testing.allocator); | 892 | const a = testing.allocator; |
| 751 | defer list.deinit(); | 893 | { |
| 894 | var list = ArrayList(i32).init(a); | ||
| 895 | defer list.deinit(); | ||
| 752 | 896 | ||
| 753 | try list.append(1); | 897 | try list.append(1); |
| 754 | try list.append(2); | 898 | try list.append(2); |
| 755 | try list.append(3); | 899 | try list.append(3); |
| 756 | try list.insert(0, 5); | 900 | try list.insert(0, 5); |
| 757 | testing.expect(list.items[0] == 5); | 901 | testing.expect(list.items[0] == 5); |
| 758 | testing.expect(list.items[1] == 1); | 902 | testing.expect(list.items[1] == 1); |
| 759 | testing.expect(list.items[2] == 2); | 903 | testing.expect(list.items[2] == 2); |
| 760 | testing.expect(list.items[3] == 3); | 904 | testing.expect(list.items[3] == 3); |
| 905 | } | ||
| 906 | { | ||
| 907 | var list = ArrayListUnmanaged(i32){}; | ||
| 908 | defer list.deinit(a); | ||
| 909 | |||
| 910 | try list.append(a, 1); | ||
| 911 | try list.append(a, 2); | ||
| 912 | try list.append(a, 3); | ||
| 913 | try list.insert(a, 0, 5); | ||
| 914 | testing.expect(list.items[0] == 5); | ||
| 915 | testing.expect(list.items[1] == 1); | ||
| 916 | testing.expect(list.items[2] == 2); | ||
| 917 | testing.expect(list.items[3] == 3); | ||
| 918 | } | ||
| 761 | } | 919 | } |
| 762 | 920 | ||
| 763 | test "std.ArrayList.insertSlice" { | 921 | test "std.ArrayList/ArrayListUnmanaged.insertSlice" { |
| 764 | var list = ArrayList(i32).init(testing.allocator); | 922 | const a = testing.allocator; |
| 765 | defer list.deinit(); | 923 | { |
| 924 | var list = ArrayList(i32).init(a); | ||
| 925 | defer list.deinit(); | ||
| 766 | 926 | ||
| 767 | try list.append(1); | 927 | try list.append(1); |
| 768 | try list.append(2); | 928 | try list.append(2); |
| 769 | try list.append(3); | 929 | try list.append(3); |
| 770 | try list.append(4); | 930 | try list.append(4); |
| 771 | try list.insertSlice(1, &[_]i32{ 9, 8 }); | 931 | try list.insertSlice(1, &[_]i32{ 9, 8 }); |
| 772 | testing.expect(list.items[0] == 1); | 932 | testing.expect(list.items[0] == 1); |
| 773 | testing.expect(list.items[1] == 9); | 933 | testing.expect(list.items[1] == 9); |
| 774 | testing.expect(list.items[2] == 8); | 934 | testing.expect(list.items[2] == 8); |
| 775 | testing.expect(list.items[3] == 2); | 935 | testing.expect(list.items[3] == 2); |
| 776 | testing.expect(list.items[4] == 3); | 936 | testing.expect(list.items[4] == 3); |
| 777 | testing.expect(list.items[5] == 4); | 937 | testing.expect(list.items[5] == 4); |
| 778 | 938 | ||
| 779 | const items = [_]i32{1}; | 939 | const items = [_]i32{1}; |
| 780 | try list.insertSlice(0, items[0..0]); | 940 | try list.insertSlice(0, items[0..0]); |
| 781 | testing.expect(list.items.len == 6); | 941 | testing.expect(list.items.len == 6); |
| 782 | testing.expect(list.items[0] == 1); | 942 | testing.expect(list.items[0] == 1); |
| 943 | } | ||
| 944 | { | ||
| 945 | var list = ArrayListUnmanaged(i32){}; | ||
| 946 | defer list.deinit(a); | ||
| 947 | |||
| 948 | try list.append(a, 1); | ||
| 949 | try list.append(a, 2); | ||
| 950 | try list.append(a, 3); | ||
| 951 | try list.append(a, 4); | ||
| 952 | try list.insertSlice(a, 1, &[_]i32{ 9, 8 }); | ||
| 953 | testing.expect(list.items[0] == 1); | ||
| 954 | testing.expect(list.items[1] == 9); | ||
| 955 | testing.expect(list.items[2] == 8); | ||
| 956 | testing.expect(list.items[3] == 2); | ||
| 957 | testing.expect(list.items[4] == 3); | ||
| 958 | testing.expect(list.items[5] == 4); | ||
| 959 | |||
| 960 | const items = [_]i32{1}; | ||
| 961 | try list.insertSlice(a, 0, items[0..0]); | ||
| 962 | testing.expect(list.items.len == 6); | ||
| 963 | testing.expect(list.items[0] == 1); | ||
| 964 | } | ||
| 783 | } | 965 | } |
| 784 | 966 | ||
| 785 | test "std.ArrayList.replaceRange" { | 967 | test "std.ArrayList/ArrayListUnmanaged.replaceRange" { |
| 786 | var arena = std.heap.ArenaAllocator.init(testing.allocator); | 968 | var arena = std.heap.ArenaAllocator.init(testing.allocator); |
| 787 | defer arena.deinit(); | 969 | defer arena.deinit(); |
| 970 | const a = &arena.allocator; | ||
| 788 | 971 | ||
| 789 | const alloc = &arena.allocator; | ||
| 790 | const init = [_]i32{ 1, 2, 3, 4, 5 }; | 972 | const init = [_]i32{ 1, 2, 3, 4, 5 }; |
| 791 | const new = [_]i32{ 0, 0, 0 }; | 973 | const new = [_]i32{ 0, 0, 0 }; |
| 792 | 974 | ||
| 793 | var list_zero = ArrayList(i32).init(alloc); | 975 | const result_zero = [_]i32{ 1, 0, 0, 0, 2, 3, 4, 5 }; |
| 794 | var list_eq = ArrayList(i32).init(alloc); | 976 | const result_eq = [_]i32{ 1, 0, 0, 0, 5 }; |
| 795 | var list_lt = ArrayList(i32).init(alloc); | 977 | const result_le = [_]i32{ 1, 0, 0, 0, 4, 5 }; |
| 796 | var list_gt = ArrayList(i32).init(alloc); | 978 | const result_gt = [_]i32{ 1, 0, 0, 0 }; |
| 797 | 979 | ||
| 798 | try list_zero.appendSlice(&init); | 980 | { |
| 799 | try list_eq.appendSlice(&init); | 981 | var list_zero = ArrayList(i32).init(a); |
| 800 | try list_lt.appendSlice(&init); | 982 | var list_eq = ArrayList(i32).init(a); |
| 801 | try list_gt.appendSlice(&init); | 983 | var list_lt = ArrayList(i32).init(a); |
| 802 | 984 | var list_gt = ArrayList(i32).init(a); | |
| 803 | try list_zero.replaceRange(1, 0, &new); | 985 | |
| 804 | try list_eq.replaceRange(1, 3, &new); | 986 | try list_zero.appendSlice(&init); |
| 805 | try list_lt.replaceRange(1, 2, &new); | 987 | try list_eq.appendSlice(&init); |
| 806 | 988 | try list_lt.appendSlice(&init); | |
| 807 | // after_range > new_items.len in function body | 989 | try list_gt.appendSlice(&init); |
| 808 | testing.expect(1 + 4 > new.len); | 990 | |
| 809 | try list_gt.replaceRange(1, 4, &new); | 991 | try list_zero.replaceRange(1, 0, &new); |
| 810 | 992 | try list_eq.replaceRange(1, 3, &new); | |
| 811 | testing.expectEqualSlices(i32, list_zero.items, &[_]i32{ 1, 0, 0, 0, 2, 3, 4, 5 }); | 993 | try list_lt.replaceRange(1, 2, &new); |
| 812 | testing.expectEqualSlices(i32, list_eq.items, &[_]i32{ 1, 0, 0, 0, 5 }); | 994 | |
| 813 | testing.expectEqualSlices(i32, list_lt.items, &[_]i32{ 1, 0, 0, 0, 4, 5 }); | 995 | // after_range > new_items.len in function body |
| 814 | testing.expectEqualSlices(i32, list_gt.items, &[_]i32{ 1, 0, 0, 0 }); | 996 | testing.expect(1 + 4 > new.len); |
| 997 | try list_gt.replaceRange(1, 4, &new); | ||
| 998 | |||
| 999 | testing.expectEqualSlices(i32, list_zero.items, &result_zero); | ||
| 1000 | testing.expectEqualSlices(i32, list_eq.items, &result_eq); | ||
| 1001 | testing.expectEqualSlices(i32, list_lt.items, &result_le); | ||
| 1002 | testing.expectEqualSlices(i32, list_gt.items, &result_gt); | ||
| 1003 | } | ||
| 1004 | { | ||
| 1005 | var list_zero = ArrayListUnmanaged(i32){}; | ||
| 1006 | var list_eq = ArrayListUnmanaged(i32){}; | ||
| 1007 | var list_lt = ArrayListUnmanaged(i32){}; | ||
| 1008 | var list_gt = ArrayListUnmanaged(i32){}; | ||
| 1009 | |||
| 1010 | try list_zero.appendSlice(a, &init); | ||
| 1011 | try list_eq.appendSlice(a, &init); | ||
| 1012 | try list_lt.appendSlice(a, &init); | ||
| 1013 | try list_gt.appendSlice(a, &init); | ||
| 1014 | |||
| 1015 | try list_zero.replaceRange(a, 1, 0, &new); | ||
| 1016 | try list_eq.replaceRange(a, 1, 3, &new); | ||
| 1017 | try list_lt.replaceRange(a, 1, 2, &new); | ||
| 1018 | |||
| 1019 | // after_range > new_items.len in function body | ||
| 1020 | testing.expect(1 + 4 > new.len); | ||
| 1021 | try list_gt.replaceRange(a, 1, 4, &new); | ||
| 1022 | |||
| 1023 | testing.expectEqualSlices(i32, list_zero.items, &result_zero); | ||
| 1024 | testing.expectEqualSlices(i32, list_eq.items, &result_eq); | ||
| 1025 | testing.expectEqualSlices(i32, list_lt.items, &result_le); | ||
| 1026 | testing.expectEqualSlices(i32, list_gt.items, &result_gt); | ||
| 1027 | } | ||
| 815 | } | 1028 | } |
| 816 | 1029 | ||
| 817 | const Item = struct { | 1030 | const Item = struct { |
| ... | @@ -819,11 +1032,25 @@ const Item = struct { | ... | @@ -819,11 +1032,25 @@ const Item = struct { |
| 819 | sub_items: ArrayList(Item), | 1032 | sub_items: ArrayList(Item), |
| 820 | }; | 1033 | }; |
| 821 | 1034 | ||
| 822 | test "std.ArrayList: ArrayList(T) of struct T" { | 1035 | const ItemUnmanaged = struct { |
| 823 | var root = Item{ .integer = 1, .sub_items = ArrayList(Item).init(testing.allocator) }; | 1036 | integer: i32, |
| 824 | defer root.sub_items.deinit(); | 1037 | sub_items: ArrayListUnmanaged(ItemUnmanaged), |
| 825 | try root.sub_items.append(Item{ .integer = 42, .sub_items = ArrayList(Item).init(testing.allocator) }); | 1038 | }; |
| 826 | testing.expect(root.sub_items.items[0].integer == 42); | 1039 | |
| 1040 | test "std.ArrayList/ArrayListUnmanaged: ArrayList(T) of struct T" { | ||
| 1041 | const a = std.testing.allocator; | ||
| 1042 | { | ||
| 1043 | var root = Item{ .integer = 1, .sub_items = ArrayList(Item).init(a) }; | ||
| 1044 | defer root.sub_items.deinit(); | ||
| 1045 | try root.sub_items.append(Item{ .integer = 42, .sub_items = ArrayList(Item).init(a) }); | ||
| 1046 | testing.expect(root.sub_items.items[0].integer == 42); | ||
| 1047 | } | ||
| 1048 | { | ||
| 1049 | var root = ItemUnmanaged{ .integer = 1, .sub_items = ArrayListUnmanaged(ItemUnmanaged){} }; | ||
| 1050 | defer root.sub_items.deinit(a); | ||
| 1051 | try root.sub_items.append(a, ItemUnmanaged{ .integer = 42, .sub_items = ArrayListUnmanaged(ItemUnmanaged){} }); | ||
| 1052 | testing.expect(root.sub_items.items[0].integer == 42); | ||
| 1053 | } | ||
| 827 | } | 1054 | } |
| 828 | 1055 | ||
| 829 | test "std.ArrayList(u8) implements outStream" { | 1056 | test "std.ArrayList(u8) implements outStream" { |
| ... | @@ -837,19 +1064,32 @@ test "std.ArrayList(u8) implements outStream" { | ... | @@ -837,19 +1064,32 @@ test "std.ArrayList(u8) implements outStream" { |
| 837 | testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.span()); | 1064 | testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.span()); |
| 838 | } | 1065 | } |
| 839 | 1066 | ||
| 840 | test "std.ArrayList.shrink still sets length on error.OutOfMemory" { | 1067 | test "std.ArrayList/ArrayListUnmanaged.shrink still sets length on error.OutOfMemory" { |
| 841 | // use an arena allocator to make sure realloc returns error.OutOfMemory | 1068 | // use an arena allocator to make sure realloc returns error.OutOfMemory |
| 842 | var arena = std.heap.ArenaAllocator.init(testing.allocator); | 1069 | var arena = std.heap.ArenaAllocator.init(testing.allocator); |
| 843 | defer arena.deinit(); | 1070 | defer arena.deinit(); |
| 1071 | const a = &arena.allocator; | ||
| 844 | 1072 | ||
| 845 | var list = ArrayList(i32).init(&arena.allocator); | 1073 | { |
| 1074 | var list = ArrayList(i32).init(a); | ||
| 846 | 1075 | ||
| 847 | try list.append(1); | 1076 | try list.append(1); |
| 848 | try list.append(2); | 1077 | try list.append(2); |
| 849 | try list.append(3); | 1078 | try list.append(3); |
| 850 | 1079 | ||
| 851 | list.shrink(1); | 1080 | list.shrink(1); |
| 852 | testing.expect(list.items.len == 1); | 1081 | testing.expect(list.items.len == 1); |
| 1082 | } | ||
| 1083 | { | ||
| 1084 | var list = ArrayListUnmanaged(i32){}; | ||
| 1085 | |||
| 1086 | try list.append(a, 1); | ||
| 1087 | try list.append(a, 2); | ||
| 1088 | try list.append(a, 3); | ||
| 1089 | |||
| 1090 | list.shrink(a, 1); | ||
| 1091 | testing.expect(list.items.len == 1); | ||
| 1092 | } | ||
| 853 | } | 1093 | } |
| 854 | 1094 | ||
| 855 | test "std.ArrayList.writer" { | 1095 | test "std.ArrayList.writer" { |
| ... | @@ -864,7 +1104,7 @@ test "std.ArrayList.writer" { | ... | @@ -864,7 +1104,7 @@ test "std.ArrayList.writer" { |
| 864 | testing.expectEqualSlices(u8, list.items, "abcdefg"); | 1104 | testing.expectEqualSlices(u8, list.items, "abcdefg"); |
| 865 | } | 1105 | } |
| 866 | 1106 | ||
| 867 | test "addManyAsArray" { | 1107 | test "std.ArrayList/ArrayListUnmanaged.addManyAsArray" { |
| 868 | const a = std.testing.allocator; | 1108 | const a = std.testing.allocator; |
| 869 | { | 1109 | { |
| 870 | var list = ArrayList(u8).init(a); | 1110 | var list = ArrayList(u8).init(a); |
lib/std/build.zig+60-56| ... | @@ -1232,6 +1232,9 @@ pub const LibExeObjStep = struct { | ... | @@ -1232,6 +1232,9 @@ pub const LibExeObjStep = struct { |
| 1232 | installed_path: ?[]const u8, | 1232 | installed_path: ?[]const u8, |
| 1233 | install_step: ?*InstallArtifactStep, | 1233 | install_step: ?*InstallArtifactStep, |
| 1234 | 1234 | ||
| 1235 | /// Base address for an executable image. | ||
| 1236 | image_base: ?u64 = null, | ||
| 1237 | |||
| 1235 | libc_file: ?[]const u8 = null, | 1238 | libc_file: ?[]const u8 = null, |
| 1236 | 1239 | ||
| 1237 | valgrind_support: ?bool = null, | 1240 | valgrind_support: ?bool = null, |
| ... | @@ -1239,6 +1242,7 @@ pub const LibExeObjStep = struct { | ... | @@ -1239,6 +1242,7 @@ pub const LibExeObjStep = struct { |
| 1239 | /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF | 1242 | /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF |
| 1240 | /// file. | 1243 | /// file. |
| 1241 | link_eh_frame_hdr: bool = false, | 1244 | link_eh_frame_hdr: bool = false, |
| 1245 | link_emit_relocs: bool = false, | ||
| 1242 | 1246 | ||
| 1243 | /// Place every function in its own section so that unused ones may be | 1247 | /// Place every function in its own section so that unused ones may be |
| 1244 | /// safely garbage-collected during the linking phase. | 1248 | /// safely garbage-collected during the linking phase. |
| ... | @@ -1384,66 +1388,50 @@ pub const LibExeObjStep = struct { | ... | @@ -1384,66 +1388,50 @@ pub const LibExeObjStep = struct { |
| 1384 | } | 1388 | } |
| 1385 | 1389 | ||
| 1386 | fn computeOutFileNames(self: *LibExeObjStep) void { | 1390 | fn computeOutFileNames(self: *LibExeObjStep) void { |
| 1387 | // TODO make this call std.zig.binNameAlloc | 1391 | const target_info = std.zig.system.NativeTargetInfo.detect( |
| 1388 | switch (self.kind) { | 1392 | self.builder.allocator, |
| 1389 | .Obj => { | 1393 | self.target, |
| 1390 | self.out_filename = self.builder.fmt("{}{}", .{ self.name, self.target.oFileExt() }); | 1394 | ) catch unreachable; |
| 1391 | }, | 1395 | const target = target_info.target; |
| 1392 | .Exe => { | 1396 | self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{ |
| 1393 | self.out_filename = self.builder.fmt("{}{}", .{ self.name, self.target.exeFileExt() }); | 1397 | .root_name = self.name, |
| 1394 | }, | 1398 | .target = target, |
| 1395 | .Test => { | 1399 | .output_mode = switch (self.kind) { |
| 1396 | self.out_filename = self.builder.fmt("test{}", .{self.target.exeFileExt()}); | 1400 | .Lib => .Lib, |
| 1401 | .Obj => .Obj, | ||
| 1402 | .Exe, .Test => .Exe, | ||
| 1397 | }, | 1403 | }, |
| 1398 | .Lib => { | 1404 | .link_mode = if (self.is_dynamic) .Dynamic else .Static, |
| 1399 | if (!self.is_dynamic) { | 1405 | .version = self.version, |
| 1400 | self.out_filename = self.builder.fmt("{}{}{}", .{ | 1406 | }) catch unreachable; |
| 1401 | self.target.libPrefix(), | 1407 | |
| 1408 | if (self.kind == .Lib) { | ||
| 1409 | if (!self.is_dynamic) { | ||
| 1410 | self.out_lib_filename = self.out_filename; | ||
| 1411 | } else if (self.version) |version| { | ||
| 1412 | if (target.isDarwin()) { | ||
| 1413 | self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", .{ | ||
| 1402 | self.name, | 1414 | self.name, |
| 1403 | self.target.staticLibSuffix(), | 1415 | version.major, |
| 1404 | }); | 1416 | }); |
| 1417 | self.name_only_filename = self.builder.fmt("lib{}.dylib", .{self.name}); | ||
| 1405 | self.out_lib_filename = self.out_filename; | 1418 | self.out_lib_filename = self.out_filename; |
| 1406 | } else if (self.version) |version| { | 1419 | } else if (target.os.tag == .windows) { |
| 1407 | if (self.target.isDarwin()) { | 1420 | self.out_lib_filename = self.builder.fmt("{}.lib", .{self.name}); |
| 1408 | self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", .{ | ||
| 1409 | self.name, | ||
| 1410 | version.major, | ||
| 1411 | version.minor, | ||
| 1412 | version.patch, | ||
| 1413 | }); | ||
| 1414 | self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", .{ | ||
| 1415 | self.name, | ||
| 1416 | version.major, | ||
| 1417 | }); | ||
| 1418 | self.name_only_filename = self.builder.fmt("lib{}.dylib", .{self.name}); | ||
| 1419 | self.out_lib_filename = self.out_filename; | ||
| 1420 | } else if (self.target.isWindows()) { | ||
| 1421 | self.out_filename = self.builder.fmt("{}.dll", .{self.name}); | ||
| 1422 | self.out_lib_filename = self.builder.fmt("{}.lib", .{self.name}); | ||
| 1423 | } else { | ||
| 1424 | self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", .{ | ||
| 1425 | self.name, | ||
| 1426 | version.major, | ||
| 1427 | version.minor, | ||
| 1428 | version.patch, | ||
| 1429 | }); | ||
| 1430 | self.major_only_filename = self.builder.fmt("lib{}.so.{d}", .{ self.name, version.major }); | ||
| 1431 | self.name_only_filename = self.builder.fmt("lib{}.so", .{self.name}); | ||
| 1432 | self.out_lib_filename = self.out_filename; | ||
| 1433 | } | ||
| 1434 | } else { | 1421 | } else { |
| 1435 | if (self.target.isDarwin()) { | 1422 | self.major_only_filename = self.builder.fmt("lib{}.so.{d}", .{ self.name, version.major }); |
| 1436 | self.out_filename = self.builder.fmt("lib{}.dylib", .{self.name}); | 1423 | self.name_only_filename = self.builder.fmt("lib{}.so", .{self.name}); |
| 1437 | self.out_lib_filename = self.out_filename; | 1424 | self.out_lib_filename = self.out_filename; |
| 1438 | } else if (self.target.isWindows()) { | ||
| 1439 | self.out_filename = self.builder.fmt("{}.dll", .{self.name}); | ||
| 1440 | self.out_lib_filename = self.builder.fmt("{}.lib", .{self.name}); | ||
| 1441 | } else { | ||
| 1442 | self.out_filename = self.builder.fmt("lib{}.so", .{self.name}); | ||
| 1443 | self.out_lib_filename = self.out_filename; | ||
| 1444 | } | ||
| 1445 | } | 1425 | } |
| 1446 | }, | 1426 | } else { |
| 1427 | if (target.isDarwin()) { | ||
| 1428 | self.out_lib_filename = self.out_filename; | ||
| 1429 | } else if (target.os.tag == .windows) { | ||
| 1430 | self.out_lib_filename = self.builder.fmt("{}.lib", .{self.name}); | ||
| 1431 | } else { | ||
| 1432 | self.out_lib_filename = self.out_filename; | ||
| 1433 | } | ||
| 1434 | } | ||
| 1447 | } | 1435 | } |
| 1448 | } | 1436 | } |
| 1449 | 1437 | ||
| ... | @@ -2040,6 +2028,11 @@ pub const LibExeObjStep = struct { | ... | @@ -2040,6 +2028,11 @@ pub const LibExeObjStep = struct { |
| 2040 | try zig_args.append("--pkg-end"); | 2028 | try zig_args.append("--pkg-end"); |
| 2041 | } | 2029 | } |
| 2042 | 2030 | ||
| 2031 | if (self.image_base) |image_base| { | ||
| 2032 | try zig_args.append("--image-base"); | ||
| 2033 | try zig_args.append(builder.fmt("0x{x}", .{image_base})); | ||
| 2034 | } | ||
| 2035 | |||
| 2043 | if (self.filter) |filter| { | 2036 | if (self.filter) |filter| { |
| 2044 | try zig_args.append("--test-filter"); | 2037 | try zig_args.append("--test-filter"); |
| 2045 | try zig_args.append(filter); | 2038 | try zig_args.append(filter); |
| ... | @@ -2075,6 +2068,9 @@ pub const LibExeObjStep = struct { | ... | @@ -2075,6 +2068,9 @@ pub const LibExeObjStep = struct { |
| 2075 | if (self.link_eh_frame_hdr) { | 2068 | if (self.link_eh_frame_hdr) { |
| 2076 | try zig_args.append("--eh-frame-hdr"); | 2069 | try zig_args.append("--eh-frame-hdr"); |
| 2077 | } | 2070 | } |
| 2071 | if (self.link_emit_relocs) { | ||
| 2072 | try zig_args.append("--emit-relocs"); | ||
| 2073 | } | ||
| 2078 | if (self.link_function_sections) { | 2074 | if (self.link_function_sections) { |
| 2079 | try zig_args.append("-ffunction-sections"); | 2075 | try zig_args.append("-ffunction-sections"); |
| 2080 | } | 2076 | } |
| ... | @@ -2168,8 +2164,8 @@ pub const LibExeObjStep = struct { | ... | @@ -2168,8 +2164,8 @@ pub const LibExeObjStep = struct { |
| 2168 | } | 2164 | } |
| 2169 | 2165 | ||
| 2170 | if (self.linker_script) |linker_script| { | 2166 | if (self.linker_script) |linker_script| { |
| 2171 | zig_args.append("--linker-script") catch unreachable; | 2167 | try zig_args.append("--script"); |
| 2172 | zig_args.append(builder.pathFromRoot(linker_script)) catch unreachable; | 2168 | try zig_args.append(builder.pathFromRoot(linker_script)); |
| 2173 | } | 2169 | } |
| 2174 | 2170 | ||
| 2175 | if (self.version_script) |version_script| { | 2171 | if (self.version_script) |version_script| { |
| ... | @@ -2335,6 +2331,14 @@ pub const LibExeObjStep = struct { | ... | @@ -2335,6 +2331,14 @@ pub const LibExeObjStep = struct { |
| 2335 | 2331 | ||
| 2336 | var it = src_dir.iterate(); | 2332 | var it = src_dir.iterate(); |
| 2337 | while (try it.next()) |entry| { | 2333 | while (try it.next()) |entry| { |
| 2334 | // The compiler can put these files into the same directory, but we don't | ||
| 2335 | // want to copy them over. | ||
| 2336 | if (mem.eql(u8, entry.name, "stage1.id") or | ||
| 2337 | mem.eql(u8, entry.name, "llvm-ar.id") or | ||
| 2338 | mem.eql(u8, entry.name, "libs.txt") or | ||
| 2339 | mem.eql(u8, entry.name, "builtin.zig") or | ||
| 2340 | mem.eql(u8, entry.name, "lld.id")) continue; | ||
| 2341 | |||
| 2338 | _ = try src_dir.updateFile(entry.name, dest_dir, entry.name, .{}); | 2342 | _ = try src_dir.updateFile(entry.name, dest_dir, entry.name, .{}); |
| 2339 | } | 2343 | } |
| 2340 | } else { | 2344 | } else { |
lib/std/builtin.zig+13| ... | @@ -98,6 +98,16 @@ pub const AtomicOrder = enum { | ... | @@ -98,6 +98,16 @@ pub const AtomicOrder = enum { |
| 98 | SeqCst, | 98 | SeqCst, |
| 99 | }; | 99 | }; |
| 100 | 100 | ||
| 101 | /// This data structure is used by the Zig language code generation and | ||
| 102 | /// therefore must be kept in sync with the compiler implementation. | ||
| 103 | pub const ReduceOp = enum { | ||
| 104 | And, | ||
| 105 | Or, | ||
| 106 | Xor, | ||
| 107 | Min, | ||
| 108 | Max, | ||
| 109 | }; | ||
| 110 | |||
| 101 | /// This data structure is used by the Zig language code generation and | 111 | /// This data structure is used by the Zig language code generation and |
| 102 | /// therefore must be kept in sync with the compiler implementation. | 112 | /// therefore must be kept in sync with the compiler implementation. |
| 103 | pub const AtomicRmwOp = enum { | 113 | pub const AtomicRmwOp = enum { |
| ... | @@ -262,6 +272,7 @@ pub const TypeInfo = union(enum) { | ... | @@ -262,6 +272,7 @@ pub const TypeInfo = union(enum) { |
| 262 | field_type: type, | 272 | field_type: type, |
| 263 | default_value: anytype, | 273 | default_value: anytype, |
| 264 | is_comptime: bool, | 274 | is_comptime: bool, |
| 275 | alignment: comptime_int, | ||
| 265 | }; | 276 | }; |
| 266 | 277 | ||
| 267 | /// This data structure is used by the Zig language code generation and | 278 | /// This data structure is used by the Zig language code generation and |
| ... | @@ -318,6 +329,7 @@ pub const TypeInfo = union(enum) { | ... | @@ -318,6 +329,7 @@ pub const TypeInfo = union(enum) { |
| 318 | pub const UnionField = struct { | 329 | pub const UnionField = struct { |
| 319 | name: []const u8, | 330 | name: []const u8, |
| 320 | field_type: type, | 331 | field_type: type, |
| 332 | alignment: comptime_int, | ||
| 321 | }; | 333 | }; |
| 322 | 334 | ||
| 323 | /// This data structure is used by the Zig language code generation and | 335 | /// This data structure is used by the Zig language code generation and |
| ... | @@ -341,6 +353,7 @@ pub const TypeInfo = union(enum) { | ... | @@ -341,6 +353,7 @@ pub const TypeInfo = union(enum) { |
| 341 | /// therefore must be kept in sync with the compiler implementation. | 353 | /// therefore must be kept in sync with the compiler implementation. |
| 342 | pub const Fn = struct { | 354 | pub const Fn = struct { |
| 343 | calling_convention: CallingConvention, | 355 | calling_convention: CallingConvention, |
| 356 | alignment: comptime_int, | ||
| 344 | is_generic: bool, | 357 | is_generic: bool, |
| 345 | is_var_args: bool, | 358 | is_var_args: bool, |
| 346 | return_type: ?type, | 359 | return_type: ?type, |
lib/std/c.zig+3| ... | @@ -342,3 +342,6 @@ pub extern "c" fn fsync(fd: c_int) c_int; | ... | @@ -342,3 +342,6 @@ pub extern "c" fn fsync(fd: c_int) c_int; |
| 342 | pub extern "c" fn fdatasync(fd: c_int) c_int; | 342 | pub extern "c" fn fdatasync(fd: c_int) c_int; |
| 343 | 343 | ||
| 344 | pub extern "c" fn prctl(option: c_int, ...) c_int; | 344 | pub extern "c" fn prctl(option: c_int, ...) c_int; |
| 345 | |||
| 346 | pub extern "c" fn getrlimit(resource: rlimit_resource, rlim: *rlimit) c_int; | ||
| 347 | pub extern "c" fn setrlimit(resource: rlimit_resource, rlim: *const rlimit) c_int; |
lib/std/c/darwin.zig+1-1| ... | @@ -12,7 +12,7 @@ usingnamespace @import("../os/bits.zig"); | ... | @@ -12,7 +12,7 @@ usingnamespace @import("../os/bits.zig"); |
| 12 | 12 | ||
| 13 | extern "c" fn __error() *c_int; | 13 | extern "c" fn __error() *c_int; |
| 14 | pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32; | 14 | pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32; |
| 15 | pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int; | 15 | pub extern "c" fn _NSGetExecutablePath(buf: [*:0]u8, bufsize: *u32) c_int; |
| 16 | pub extern "c" fn _dyld_image_count() u32; | 16 | pub extern "c" fn _dyld_image_count() u32; |
| 17 | pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header; | 17 | pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header; |
| 18 | pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize; | 18 | pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize; |
lib/std/c/linux.zig+2| ... | @@ -100,6 +100,8 @@ pub extern "c" fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_ | ... | @@ -100,6 +100,8 @@ pub extern "c" fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_ |
| 100 | 100 | ||
| 101 | pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: c_uint) c_int; | 101 | pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: c_uint) c_int; |
| 102 | 102 | ||
| 103 | pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *const rlimit, old_limit: *rlimit) c_int; | ||
| 104 | |||
| 103 | pub const pthread_attr_t = extern struct { | 105 | pub const pthread_attr_t = extern struct { |
| 104 | __size: [56]u8, | 106 | __size: [56]u8, |
| 105 | __align: c_long, | 107 | __align: c_long, |
lib/std/child_process.zig+2-2| ... | @@ -105,8 +105,8 @@ pub const ChildProcess = struct { | ... | @@ -105,8 +105,8 @@ pub const ChildProcess = struct { |
| 105 | .term = null, | 105 | .term = null, |
| 106 | .env_map = null, | 106 | .env_map = null, |
| 107 | .cwd = null, | 107 | .cwd = null, |
| 108 | .uid = if (builtin.os.tag == .windows) {} else null, | 108 | .uid = if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {} else null, |
| 109 | .gid = if (builtin.os.tag == .windows) {} else null, | 109 | .gid = if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {} else null, |
| 110 | .stdin = null, | 110 | .stdin = null, |
| 111 | .stdout = null, | 111 | .stdout = null, |
| 112 | .stderr = null, | 112 | .stderr = null, |
lib/std/crypto.zig+48-40| ... | @@ -4,6 +4,50 @@ | ... | @@ -4,6 +4,50 @@ |
| 4 | // The MIT license requires this copyright notice to be included in all copies | 4 | // The MIT license requires this copyright notice to be included in all copies |
| 5 | // and substantial portions of the software. | 5 | // and substantial portions of the software. |
| 6 | 6 | ||
| 7 | /// Authenticated Encryption with Associated Data | ||
| 8 | pub const aead = struct { | ||
| 9 | const chacha20 = @import("crypto/chacha20.zig"); | ||
| 10 | |||
| 11 | pub const Gimli = @import("crypto/gimli.zig").Aead; | ||
| 12 | pub const ChaCha20Poly1305 = chacha20.Chacha20Poly1305; | ||
| 13 | pub const XChaCha20Poly1305 = chacha20.XChacha20Poly1305; | ||
| 14 | pub const AEGIS128L = @import("crypto/aegis.zig").AEGIS128L; | ||
| 15 | pub const AEGIS256 = @import("crypto/aegis.zig").AEGIS256; | ||
| 16 | pub const AES128GCM = @import("crypto/aes_gcm.zig").AES128GCM; | ||
| 17 | pub const AES256GCM = @import("crypto/aes_gcm.zig").AES256GCM; | ||
| 18 | }; | ||
| 19 | |||
| 20 | /// Authentication (MAC) functions. | ||
| 21 | pub const auth = struct { | ||
| 22 | pub const hmac = @import("crypto/hmac.zig"); | ||
| 23 | pub const siphash = @import("crypto/siphash.zig"); | ||
| 24 | }; | ||
| 25 | |||
| 26 | /// Core functions, that should rarely be used directly by applications. | ||
| 27 | pub const core = struct { | ||
| 28 | pub const aes = @import("crypto/aes.zig"); | ||
| 29 | pub const Gimli = @import("crypto/gimli.zig").State; | ||
| 30 | |||
| 31 | /// Modes are generic compositions to construct encryption/decryption functions from block ciphers and permutations. | ||
| 32 | /// | ||
| 33 | /// These modes are designed to be building blocks for higher-level constructions, and should generally not be used directly by applications, as they may not provide the expected properties and security guarantees. | ||
| 34 | /// | ||
| 35 | /// Most applications may want to use AEADs instead. | ||
| 36 | pub const modes = @import("crypto/modes.zig"); | ||
| 37 | }; | ||
| 38 | |||
| 39 | /// Diffie-Hellman key exchange functions. | ||
| 40 | pub const dh = struct { | ||
| 41 | pub const X25519 = @import("crypto/25519/x25519.zig").X25519; | ||
| 42 | }; | ||
| 43 | |||
| 44 | /// Elliptic-curve arithmetic. | ||
| 45 | pub const ecc = struct { | ||
| 46 | pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519; | ||
| 47 | pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519; | ||
| 48 | pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255; | ||
| 49 | }; | ||
| 50 | |||
| 7 | /// Hash functions. | 51 | /// Hash functions. |
| 8 | pub const hash = struct { | 52 | pub const hash = struct { |
| 9 | pub const Md5 = @import("crypto/md5.zig").Md5; | 53 | pub const Md5 = @import("crypto/md5.zig").Md5; |
| ... | @@ -15,26 +59,15 @@ pub const hash = struct { | ... | @@ -15,26 +59,15 @@ pub const hash = struct { |
| 15 | pub const Gimli = @import("crypto/gimli.zig").Hash; | 59 | pub const Gimli = @import("crypto/gimli.zig").Hash; |
| 16 | }; | 60 | }; |
| 17 | 61 | ||
| 18 | /// Authentication (MAC) functions. | 62 | /// Key derivation functions. |
| 19 | pub const auth = struct { | 63 | pub const kdf = struct { |
| 20 | pub const hmac = @import("crypto/hmac.zig"); | 64 | pub const hkdf = @import("crypto/hkdf.zig"); |
| 21 | pub const siphash = @import("crypto/siphash.zig"); | ||
| 22 | }; | ||
| 23 | |||
| 24 | /// Authenticated Encryption with Associated Data | ||
| 25 | pub const aead = struct { | ||
| 26 | const chacha20 = @import("crypto/chacha20.zig"); | ||
| 27 | |||
| 28 | pub const Gimli = @import("crypto/gimli.zig").Aead; | ||
| 29 | pub const ChaCha20Poly1305 = chacha20.Chacha20Poly1305; | ||
| 30 | pub const XChaCha20Poly1305 = chacha20.XChacha20Poly1305; | ||
| 31 | pub const AEGIS128L = @import("crypto/aegis.zig").AEGIS128L; | ||
| 32 | pub const AEGIS256 = @import("crypto/aegis.zig").AEGIS256; | ||
| 33 | }; | 65 | }; |
| 34 | 66 | ||
| 35 | /// MAC functions requiring single-use secret keys. | 67 | /// MAC functions requiring single-use secret keys. |
| 36 | pub const onetimeauth = struct { | 68 | pub const onetimeauth = struct { |
| 37 | pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305; | 69 | pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305; |
| 70 | pub const Ghash = @import("crypto/ghash.zig").Ghash; | ||
| 38 | }; | 71 | }; |
| 39 | 72 | ||
| 40 | /// A password hashing function derives a uniform key from low-entropy input material such as passwords. | 73 | /// A password hashing function derives a uniform key from low-entropy input material such as passwords. |
| ... | @@ -57,31 +90,6 @@ pub const pwhash = struct { | ... | @@ -57,31 +90,6 @@ pub const pwhash = struct { |
| 57 | pub const pbkdf2 = @import("crypto/pbkdf2.zig").pbkdf2; | 90 | pub const pbkdf2 = @import("crypto/pbkdf2.zig").pbkdf2; |
| 58 | }; | 91 | }; |
| 59 | 92 | ||
| 60 | /// Core functions, that should rarely be used directly by applications. | ||
| 61 | pub const core = struct { | ||
| 62 | pub const aes = @import("crypto/aes.zig"); | ||
| 63 | pub const Gimli = @import("crypto/gimli.zig").State; | ||
| 64 | |||
| 65 | /// Modes are generic compositions to construct encryption/decryption functions from block ciphers and permutations. | ||
| 66 | /// | ||
| 67 | /// These modes are designed to be building blocks for higher-level constructions, and should generally not be used directly by applications, as they may not provide the expected properties and security guarantees. | ||
| 68 | /// | ||
| 69 | /// Most applications may want to use AEADs instead. | ||
| 70 | pub const modes = @import("crypto/modes.zig"); | ||
| 71 | }; | ||
| 72 | |||
| 73 | /// Elliptic-curve arithmetic. | ||
| 74 | pub const ecc = struct { | ||
| 75 | pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519; | ||
| 76 | pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519; | ||
| 77 | pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255; | ||
| 78 | }; | ||
| 79 | |||
| 80 | /// Diffie-Hellman key exchange functions. | ||
| 81 | pub const dh = struct { | ||
| 82 | pub const X25519 = @import("crypto/25519/x25519.zig").X25519; | ||
| 83 | }; | ||
| 84 | |||
| 85 | /// Digital signature functions. | 93 | /// Digital signature functions. |
| 86 | pub const sign = struct { | 94 | pub const sign = struct { |
| 87 | pub const Ed25519 = @import("crypto/25519/ed25519.zig").Ed25519; | 95 | pub const Ed25519 = @import("crypto/25519/ed25519.zig").Ed25519; |
lib/std/crypto/25519/field.zig+8-6| ... | @@ -307,12 +307,14 @@ pub const Fe = struct { | ... | @@ -307,12 +307,14 @@ pub const Fe = struct { |
| 307 | } | 307 | } |
| 308 | 308 | ||
| 309 | pub fn pow2523(a: Fe) Fe { | 309 | pub fn pow2523(a: Fe) Fe { |
| 310 | var c = a; | 310 | var t0 = a.mul(a.sq()); |
| 311 | var i: usize = 0; | 311 | var t1 = t0.mul(t0.sqn(2)).sq().mul(a); |
| 312 | while (i < 249) : (i += 1) { | 312 | t0 = t1.sqn(5).mul(t1); |
| 313 | c = c.sq().mul(a); | 313 | var t2 = t0.sqn(5).mul(t1); |
| 314 | } | 314 | t1 = t2.sqn(15).mul(t2); |
| 315 | return c.sq().sq().mul(a); | 315 | t2 = t1.sqn(30).mul(t1); |
| 316 | t1 = t2.sqn(60).mul(t2); | ||
| 317 | return t1.sqn(120).mul(t1).sqn(10).mul(t0).sqn(2).mul(a); | ||
| 316 | } | 318 | } |
| 317 | 319 | ||
| 318 | pub fn abs(a: Fe) Fe { | 320 | pub fn abs(a: Fe) Fe { |
lib/std/crypto/aes_gcm.zig created+161| ... | @@ -0,0 +1,161 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const builtin = std.builtin; | ||
| 4 | const crypto = std.crypto; | ||
| 5 | const debug = std.debug; | ||
| 6 | const Ghash = std.crypto.onetimeauth.Ghash; | ||
| 7 | const mem = std.mem; | ||
| 8 | const modes = crypto.core.modes; | ||
| 9 | |||
| 10 | pub const AES128GCM = AESGCM(crypto.core.aes.AES128); | ||
| 11 | pub const AES256GCM = AESGCM(crypto.core.aes.AES256); | ||
| 12 | |||
| 13 | fn AESGCM(comptime AES: anytype) type { | ||
| 14 | debug.assert(AES.block.block_size == 16); | ||
| 15 | |||
| 16 | return struct { | ||
| 17 | pub const tag_length = 16; | ||
| 18 | pub const nonce_length = 12; | ||
| 19 | pub const key_length = AES.key_bits / 8; | ||
| 20 | |||
| 21 | const zeros = [_]u8{0} ** 16; | ||
| 22 | |||
| 23 | pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void { | ||
| 24 | debug.assert(c.len == m.len); | ||
| 25 | debug.assert(m.len <= 16 * ((1 << 32) - 2)); | ||
| 26 | |||
| 27 | const aes = AES.initEnc(key); | ||
| 28 | var h: [16]u8 = undefined; | ||
| 29 | aes.encrypt(&h, &zeros); | ||
| 30 | |||
| 31 | var t: [16]u8 = undefined; | ||
| 32 | var j: [16]u8 = undefined; | ||
| 33 | mem.copy(u8, j[0..nonce_length], npub[0..]); | ||
| 34 | mem.writeIntBig(u32, j[nonce_length..][0..4], 1); | ||
| 35 | aes.encrypt(&t, &j); | ||
| 36 | |||
| 37 | var mac = Ghash.init(&h); | ||
| 38 | mac.update(ad); | ||
| 39 | mac.pad(); | ||
| 40 | |||
| 41 | mem.writeIntBig(u32, j[nonce_length..][0..4], 2); | ||
| 42 | modes.ctr(@TypeOf(aes), aes, c, m, j, builtin.Endian.Big); | ||
| 43 | mac.update(c[0..m.len][0..]); | ||
| 44 | mac.pad(); | ||
| 45 | |||
| 46 | var final_block = h; | ||
| 47 | mem.writeIntBig(u64, final_block[0..8], ad.len * 8); | ||
| 48 | mem.writeIntBig(u64, final_block[8..16], m.len * 8); | ||
| 49 | mac.update(&final_block); | ||
| 50 | mac.final(tag); | ||
| 51 | for (t) |x, i| { | ||
| 52 | tag[i] ^= x; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) !void { | ||
| 57 | assert(c.len == m.len); | ||
| 58 | |||
| 59 | const aes = AES.initEnc(key); | ||
| 60 | var h: [16]u8 = undefined; | ||
| 61 | aes.encrypt(&h, &zeros); | ||
| 62 | |||
| 63 | var t: [16]u8 = undefined; | ||
| 64 | var j: [16]u8 = undefined; | ||
| 65 | mem.copy(u8, j[0..nonce_length], npub[0..]); | ||
| 66 | mem.writeIntBig(u32, j[nonce_length..][0..4], 1); | ||
| 67 | aes.encrypt(&t, &j); | ||
| 68 | |||
| 69 | var mac = Ghash.init(&h); | ||
| 70 | mac.update(ad); | ||
| 71 | mac.pad(); | ||
| 72 | |||
| 73 | mac.update(c); | ||
| 74 | mac.pad(); | ||
| 75 | |||
| 76 | var final_block = h; | ||
| 77 | mem.writeIntBig(u64, final_block[0..8], ad.len * 8); | ||
| 78 | mem.writeIntBig(u64, final_block[8..16], m.len * 8); | ||
| 79 | mac.update(&final_block); | ||
| 80 | var computed_tag: [Ghash.mac_length]u8 = undefined; | ||
| 81 | mac.final(&computed_tag); | ||
| 82 | for (t) |x, i| { | ||
| 83 | computed_tag[i] ^= x; | ||
| 84 | } | ||
| 85 | |||
| 86 | var acc: u8 = 0; | ||
| 87 | for (computed_tag) |_, p| { | ||
| 88 | acc |= (computed_tag[p] ^ tag[p]); | ||
| 89 | } | ||
| 90 | if (acc != 0) { | ||
| 91 | mem.set(u8, m, 0xaa); | ||
| 92 | return error.AuthenticationFailed; | ||
| 93 | } | ||
| 94 | |||
| 95 | mem.writeIntBig(u32, j[nonce_length..][0..4], 2); | ||
| 96 | modes.ctr(@TypeOf(aes), aes, m, c, j, builtin.Endian.Big); | ||
| 97 | } | ||
| 98 | }; | ||
| 99 | } | ||
| 100 | |||
| 101 | const htest = @import("test.zig"); | ||
| 102 | const testing = std.testing; | ||
| 103 | |||
| 104 | test "AES256GCM - Empty message and no associated data" { | ||
| 105 | const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length; | ||
| 106 | const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length; | ||
| 107 | const ad = ""; | ||
| 108 | const m = ""; | ||
| 109 | var c: [m.len]u8 = undefined; | ||
| 110 | var m2: [m.len]u8 = undefined; | ||
| 111 | var tag: [AES256GCM.tag_length]u8 = undefined; | ||
| 112 | |||
| 113 | AES256GCM.encrypt(&c, &tag, m, ad, nonce, key); | ||
| 114 | htest.assertEqual("6b6ff610a16fa4cd59f1fb7903154e92", &tag); | ||
| 115 | } | ||
| 116 | |||
| 117 | test "AES256GCM - Associated data only" { | ||
| 118 | const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length; | ||
| 119 | const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length; | ||
| 120 | const m = ""; | ||
| 121 | const ad = "Test with associated data"; | ||
| 122 | var c: [m.len]u8 = undefined; | ||
| 123 | var tag: [AES256GCM.tag_length]u8 = undefined; | ||
| 124 | |||
| 125 | AES256GCM.encrypt(&c, &tag, m, ad, nonce, key); | ||
| 126 | htest.assertEqual("262ed164c2dfb26e080a9d108dd9dd4c", &tag); | ||
| 127 | } | ||
| 128 | |||
| 129 | test "AES256GCM - Message only" { | ||
| 130 | const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length; | ||
| 131 | const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length; | ||
| 132 | const m = "Test with message only"; | ||
| 133 | const ad = ""; | ||
| 134 | var c: [m.len]u8 = undefined; | ||
| 135 | var m2: [m.len]u8 = undefined; | ||
| 136 | var tag: [AES256GCM.tag_length]u8 = undefined; | ||
| 137 | |||
| 138 | AES256GCM.encrypt(&c, &tag, m, ad, nonce, key); | ||
| 139 | try AES256GCM.decrypt(&m2, &c, tag, ad, nonce, key); | ||
| 140 | testing.expectEqualSlices(u8, m[0..], m2[0..]); | ||
| 141 | |||
| 142 | htest.assertEqual("5ca1642d90009fea33d01f78cf6eefaf01d539472f7c", &c); | ||
| 143 | htest.assertEqual("07cd7fc9103e2f9e9bf2dfaa319caff4", &tag); | ||
| 144 | } | ||
| 145 | |||
| 146 | test "AES256GCM - Message and associated data" { | ||
| 147 | const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length; | ||
| 148 | const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length; | ||
| 149 | const m = "Test with message"; | ||
| 150 | const ad = "Test with associated data"; | ||
| 151 | var c: [m.len]u8 = undefined; | ||
| 152 | var m2: [m.len]u8 = undefined; | ||
| 153 | var tag: [AES256GCM.tag_length]u8 = undefined; | ||
| 154 | |||
| 155 | AES256GCM.encrypt(&c, &tag, m, ad, nonce, key); | ||
| 156 | try AES256GCM.decrypt(&m2, &c, tag, ad, nonce, key); | ||
| 157 | testing.expectEqualSlices(u8, m[0..], m2[0..]); | ||
| 158 | |||
| 159 | htest.assertEqual("5ca1642d90009fea33d01f78cf6eefaf01", &c); | ||
| 160 | htest.assertEqual("64accec679d444e2373bd9f6796c0d2c", &tag); | ||
| 161 | } | ||
lib/std/crypto/benchmark.zig+3| ... | @@ -57,6 +57,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 | ... | @@ -57,6 +57,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | const macs = [_]Crypto{ | 59 | const macs = [_]Crypto{ |
| 60 | Crypto{ .ty = crypto.onetimeauth.Ghash, .name = "ghash" }, | ||
| 60 | Crypto{ .ty = crypto.onetimeauth.Poly1305, .name = "poly1305" }, | 61 | Crypto{ .ty = crypto.onetimeauth.Poly1305, .name = "poly1305" }, |
| 61 | Crypto{ .ty = crypto.auth.hmac.HmacMd5, .name = "hmac-md5" }, | 62 | Crypto{ .ty = crypto.auth.hmac.HmacMd5, .name = "hmac-md5" }, |
| 62 | Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" }, | 63 | Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" }, |
| ... | @@ -151,6 +152,8 @@ const aeads = [_]Crypto{ | ... | @@ -151,6 +152,8 @@ const aeads = [_]Crypto{ |
| 151 | Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" }, | 152 | Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" }, |
| 152 | Crypto{ .ty = crypto.aead.AEGIS128L, .name = "aegis-128l" }, | 153 | Crypto{ .ty = crypto.aead.AEGIS128L, .name = "aegis-128l" }, |
| 153 | Crypto{ .ty = crypto.aead.AEGIS256, .name = "aegis-256" }, | 154 | Crypto{ .ty = crypto.aead.AEGIS256, .name = "aegis-256" }, |
| 155 | Crypto{ .ty = crypto.aead.AES128GCM, .name = "aes128-gcm" }, | ||
| 156 | Crypto{ .ty = crypto.aead.AES256GCM, .name = "aes256-gcm" }, | ||
| 154 | }; | 157 | }; |
| 155 | 158 | ||
| 156 | pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 { | 159 | pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 { |
lib/std/crypto/ghash.zig created+317| ... | @@ -0,0 +1,317 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2020 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | // | ||
| 7 | // Adapted from BearSSL's ctmul64 implementation originally written by Thomas Pornin <pornin@bolet.org> | ||
| 8 | |||
| 9 | const std = @import("../std.zig"); | ||
| 10 | const assert = std.debug.assert; | ||
| 11 | const math = std.math; | ||
| 12 | const mem = std.mem; | ||
| 13 | |||
| 14 | /// GHASH is a universal hash function that features multiplication | ||
| 15 | /// by a fixed parameter within a Galois field. | ||
| 16 | /// | ||
| 17 | /// It is not a general purpose hash function - The key must be secret, unpredictable and never reused. | ||
| 18 | /// | ||
| 19 | /// GHASH is typically used to compute the authentication tag in the AES-GCM construction. | ||
| 20 | pub const Ghash = struct { | ||
| 21 | pub const block_size: usize = 16; | ||
| 22 | pub const mac_length = 16; | ||
| 23 | pub const minimum_key_length = 16; | ||
| 24 | |||
| 25 | y0: u64 = 0, | ||
| 26 | y1: u64 = 0, | ||
| 27 | h0: u64, | ||
| 28 | h1: u64, | ||
| 29 | h2: u64, | ||
| 30 | h0r: u64, | ||
| 31 | h1r: u64, | ||
| 32 | h2r: u64, | ||
| 33 | |||
| 34 | hh0: u64 = undefined, | ||
| 35 | hh1: u64 = undefined, | ||
| 36 | hh2: u64 = undefined, | ||
| 37 | hh0r: u64 = undefined, | ||
| 38 | hh1r: u64 = undefined, | ||
| 39 | hh2r: u64 = undefined, | ||
| 40 | |||
| 41 | leftover: usize = 0, | ||
| 42 | buf: [block_size]u8 align(16) = undefined, | ||
| 43 | |||
| 44 | pub fn init(key: *const [minimum_key_length]u8) Ghash { | ||
| 45 | const h1 = mem.readIntBig(u64, key[0..8]); | ||
| 46 | const h0 = mem.readIntBig(u64, key[8..16]); | ||
| 47 | const h1r = @bitReverse(u64, h1); | ||
| 48 | const h0r = @bitReverse(u64, h0); | ||
| 49 | const h2 = h0 ^ h1; | ||
| 50 | const h2r = h0r ^ h1r; | ||
| 51 | |||
| 52 | if (std.builtin.mode == .ReleaseSmall) { | ||
| 53 | return Ghash{ | ||
| 54 | .h0 = h0, | ||
| 55 | .h1 = h1, | ||
| 56 | .h2 = h2, | ||
| 57 | .h0r = h0r, | ||
| 58 | .h1r = h1r, | ||
| 59 | .h2r = h2r, | ||
| 60 | }; | ||
| 61 | } else { | ||
| 62 | // Precompute H^2 | ||
| 63 | var hh = Ghash{ | ||
| 64 | .h0 = h0, | ||
| 65 | .h1 = h1, | ||
| 66 | .h2 = h2, | ||
| 67 | .h0r = h0r, | ||
| 68 | .h1r = h1r, | ||
| 69 | .h2r = h2r, | ||
| 70 | }; | ||
| 71 | hh.update(key); | ||
| 72 | const hh1 = hh.y1; | ||
| 73 | const hh0 = hh.y0; | ||
| 74 | const hh1r = @bitReverse(u64, hh1); | ||
| 75 | const hh0r = @bitReverse(u64, hh0); | ||
| 76 | const hh2 = hh0 ^ hh1; | ||
| 77 | const hh2r = hh0r ^ hh1r; | ||
| 78 | |||
| 79 | return Ghash{ | ||
| 80 | .h0 = h0, | ||
| 81 | .h1 = h1, | ||
| 82 | .h2 = h2, | ||
| 83 | .h0r = h0r, | ||
| 84 | .h1r = h1r, | ||
| 85 | .h2r = h2r, | ||
| 86 | |||
| 87 | .hh0 = hh0, | ||
| 88 | .hh1 = hh1, | ||
| 89 | .hh2 = hh2, | ||
| 90 | .hh0r = hh0r, | ||
| 91 | .hh1r = hh1r, | ||
| 92 | .hh2r = hh2r, | ||
| 93 | }; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | inline fn clmul_pclmul(x: u64, y: u64) u64 { | ||
| 98 | const Vector = std.meta.Vector; | ||
| 99 | const product = asm ( | ||
| 100 | \\ vpclmulqdq $0x00, %[x], %[y], %[out] | ||
| 101 | : [out] "=x" (-> Vector(2, u64)) | ||
| 102 | : [x] "x" (@bitCast(Vector(2, u64), @as(u128, x))), | ||
| 103 | [y] "x" (@bitCast(Vector(2, u64), @as(u128, y))) | ||
| 104 | ); | ||
| 105 | return product[0]; | ||
| 106 | } | ||
| 107 | |||
| 108 | fn clmul_soft(x: u64, y: u64) u64 { | ||
| 109 | const x0 = x & 0x1111111111111111; | ||
| 110 | const x1 = x & 0x2222222222222222; | ||
| 111 | const x2 = x & 0x4444444444444444; | ||
| 112 | const x3 = x & 0x8888888888888888; | ||
| 113 | const y0 = y & 0x1111111111111111; | ||
| 114 | const y1 = y & 0x2222222222222222; | ||
| 115 | const y2 = y & 0x4444444444444444; | ||
| 116 | const y3 = y & 0x8888888888888888; | ||
| 117 | var z0 = (x0 *% y0) ^ (x1 *% y3) ^ (x2 *% y2) ^ (x3 *% y1); | ||
| 118 | var z1 = (x0 *% y1) ^ (x1 *% y0) ^ (x2 *% y3) ^ (x3 *% y2); | ||
| 119 | var z2 = (x0 *% y2) ^ (x1 *% y1) ^ (x2 *% y0) ^ (x3 *% y3); | ||
| 120 | var z3 = (x0 *% y3) ^ (x1 *% y2) ^ (x2 *% y1) ^ (x3 *% y0); | ||
| 121 | z0 &= 0x1111111111111111; | ||
| 122 | z1 &= 0x2222222222222222; | ||
| 123 | z2 &= 0x4444444444444444; | ||
| 124 | z3 &= 0x8888888888888888; | ||
| 125 | return z0 | z1 | z2 | z3; | ||
| 126 | } | ||
| 127 | |||
| 128 | const has_pclmul = comptime std.Target.x86.featureSetHas(std.Target.current.cpu.features, .pclmul); | ||
| 129 | const has_avx = comptime std.Target.x86.featureSetHas(std.Target.current.cpu.features, .avx); | ||
| 130 | const clmul = if (std.Target.current.cpu.arch == .x86_64 and has_pclmul and has_avx) clmul_pclmul else clmul_soft; | ||
| 131 | |||
| 132 | fn blocks(st: *Ghash, msg: []const u8) void { | ||
| 133 | assert(msg.len % 16 == 0); // GHASH blocks() expects full blocks | ||
| 134 | var y1 = st.y1; | ||
| 135 | var y0 = st.y0; | ||
| 136 | |||
| 137 | var i: usize = 0; | ||
| 138 | |||
| 139 | // 2-blocks aggregated reduction | ||
| 140 | if (std.builtin.mode != .ReleaseSmall) { | ||
| 141 | while (i + 32 <= msg.len) : (i += 32) { | ||
| 142 | // B0 * H^2 unreduced | ||
| 143 | y1 ^= mem.readIntBig(u64, msg[i..][0..8]); | ||
| 144 | y0 ^= mem.readIntBig(u64, msg[i..][8..16]); | ||
| 145 | |||
| 146 | const y1r = @bitReverse(u64, y1); | ||
| 147 | const y0r = @bitReverse(u64, y0); | ||
| 148 | const y2 = y0 ^ y1; | ||
| 149 | const y2r = y0r ^ y1r; | ||
| 150 | |||
| 151 | var z0 = clmul(y0, st.hh0); | ||
| 152 | var z1 = clmul(y1, st.hh1); | ||
| 153 | var z2 = clmul(y2, st.hh2) ^ z0 ^ z1; | ||
| 154 | var z0h = clmul(y0r, st.hh0r); | ||
| 155 | var z1h = clmul(y1r, st.hh1r); | ||
| 156 | var z2h = clmul(y2r, st.hh2r) ^ z0h ^ z1h; | ||
| 157 | |||
| 158 | // B1 * H unreduced | ||
| 159 | const sy1 = mem.readIntBig(u64, msg[i..][16..24]); | ||
| 160 | const sy0 = mem.readIntBig(u64, msg[i..][24..32]); | ||
| 161 | |||
| 162 | const sy1r = @bitReverse(u64, sy1); | ||
| 163 | const sy0r = @bitReverse(u64, sy0); | ||
| 164 | const sy2 = sy0 ^ sy1; | ||
| 165 | const sy2r = sy0r ^ sy1r; | ||
| 166 | |||
| 167 | const sz0 = clmul(sy0, st.h0); | ||
| 168 | const sz1 = clmul(sy1, st.h1); | ||
| 169 | const sz2 = clmul(sy2, st.h2) ^ sz0 ^ sz1; | ||
| 170 | const sz0h = clmul(sy0r, st.h0r); | ||
| 171 | const sz1h = clmul(sy1r, st.h1r); | ||
| 172 | const sz2h = clmul(sy2r, st.h2r) ^ sz0h ^ sz1h; | ||
| 173 | |||
| 174 | // ((B0 * H^2) + B1 * H) (mod M) | ||
| 175 | z0 ^= sz0; | ||
| 176 | z1 ^= sz1; | ||
| 177 | z2 ^= sz2; | ||
| 178 | z0h ^= sz0h; | ||
| 179 | z1h ^= sz1h; | ||
| 180 | z2h ^= sz2h; | ||
| 181 | z0h = @bitReverse(u64, z0h) >> 1; | ||
| 182 | z1h = @bitReverse(u64, z1h) >> 1; | ||
| 183 | z2h = @bitReverse(u64, z2h) >> 1; | ||
| 184 | |||
| 185 | var v3 = z1h; | ||
| 186 | var v2 = z1 ^ z2h; | ||
| 187 | var v1 = z0h ^ z2; | ||
| 188 | var v0 = z0; | ||
| 189 | |||
| 190 | v3 = (v3 << 1) | (v2 >> 63); | ||
| 191 | v2 = (v2 << 1) | (v1 >> 63); | ||
| 192 | v1 = (v1 << 1) | (v0 >> 63); | ||
| 193 | v0 = (v0 << 1); | ||
| 194 | |||
| 195 | v2 ^= v0 ^ (v0 >> 1) ^ (v0 >> 2) ^ (v0 >> 7); | ||
| 196 | v1 ^= (v0 << 63) ^ (v0 << 62) ^ (v0 << 57); | ||
| 197 | y1 = v3 ^ v1 ^ (v1 >> 1) ^ (v1 >> 2) ^ (v1 >> 7); | ||
| 198 | y0 = v2 ^ (v1 << 63) ^ (v1 << 62) ^ (v1 << 57); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | // single block | ||
| 203 | while (i + 16 <= msg.len) : (i += 16) { | ||
| 204 | y1 ^= mem.readIntBig(u64, msg[i..][0..8]); | ||
| 205 | y0 ^= mem.readIntBig(u64, msg[i..][8..16]); | ||
| 206 | |||
| 207 | const y1r = @bitReverse(u64, y1); | ||
| 208 | const y0r = @bitReverse(u64, y0); | ||
| 209 | const y2 = y0 ^ y1; | ||
| 210 | const y2r = y0r ^ y1r; | ||
| 211 | |||
| 212 | const z0 = clmul(y0, st.h0); | ||
| 213 | const z1 = clmul(y1, st.h1); | ||
| 214 | var z2 = clmul(y2, st.h2) ^ z0 ^ z1; | ||
| 215 | var z0h = clmul(y0r, st.h0r); | ||
| 216 | var z1h = clmul(y1r, st.h1r); | ||
| 217 | var z2h = clmul(y2r, st.h2r) ^ z0h ^ z1h; | ||
| 218 | z0h = @bitReverse(u64, z0h) >> 1; | ||
| 219 | z1h = @bitReverse(u64, z1h) >> 1; | ||
| 220 | z2h = @bitReverse(u64, z2h) >> 1; | ||
| 221 | |||
| 222 | // shift & reduce | ||
| 223 | var v3 = z1h; | ||
| 224 | var v2 = z1 ^ z2h; | ||
| 225 | var v1 = z0h ^ z2; | ||
| 226 | var v0 = z0; | ||
| 227 | |||
| 228 | v3 = (v3 << 1) | (v2 >> 63); | ||
| 229 | v2 = (v2 << 1) | (v1 >> 63); | ||
| 230 | v1 = (v1 << 1) | (v0 >> 63); | ||
| 231 | v0 = (v0 << 1); | ||
| 232 | |||
| 233 | v2 ^= v0 ^ (v0 >> 1) ^ (v0 >> 2) ^ (v0 >> 7); | ||
| 234 | v1 ^= (v0 << 63) ^ (v0 << 62) ^ (v0 << 57); | ||
| 235 | y1 = v3 ^ v1 ^ (v1 >> 1) ^ (v1 >> 2) ^ (v1 >> 7); | ||
| 236 | y0 = v2 ^ (v1 << 63) ^ (v1 << 62) ^ (v1 << 57); | ||
| 237 | } | ||
| 238 | st.y1 = y1; | ||
| 239 | st.y0 = y0; | ||
| 240 | } | ||
| 241 | |||
| 242 | pub fn update(st: *Ghash, m: []const u8) void { | ||
| 243 | var mb = m; | ||
| 244 | |||
| 245 | if (st.leftover > 0) { | ||
| 246 | const want = math.min(block_size - st.leftover, mb.len); | ||
| 247 | const mc = mb[0..want]; | ||
| 248 | for (mc) |x, i| { | ||
| 249 | st.buf[st.leftover + i] = x; | ||
| 250 | } | ||
| 251 | mb = mb[want..]; | ||
| 252 | st.leftover += want; | ||
| 253 | if (st.leftover < block_size) { | ||
| 254 | return; | ||
| 255 | } | ||
| 256 | st.blocks(&st.buf); | ||
| 257 | st.leftover = 0; | ||
| 258 | } | ||
| 259 | if (mb.len >= block_size) { | ||
| 260 | const want = mb.len & ~(block_size - 1); | ||
| 261 | st.blocks(mb[0..want]); | ||
| 262 | mb = mb[want..]; | ||
| 263 | } | ||
| 264 | if (mb.len > 0) { | ||
| 265 | for (mb) |x, i| { | ||
| 266 | st.buf[st.leftover + i] = x; | ||
| 267 | } | ||
| 268 | st.leftover += mb.len; | ||
| 269 | } | ||
| 270 | } | ||
| 271 | |||
| 272 | /// Zero-pad to align the next input to the first byte of a block | ||
| 273 | pub fn pad(st: *Ghash) void { | ||
| 274 | if (st.leftover == 0) { | ||
| 275 | return; | ||
| 276 | } | ||
| 277 | var i = st.leftover; | ||
| 278 | while (i < block_size) : (i += 1) { | ||
| 279 | st.buf[i] = 0; | ||
| 280 | } | ||
| 281 | st.blocks(&st.buf); | ||
| 282 | st.leftover = 0; | ||
| 283 | } | ||
| 284 | |||
| 285 | pub fn final(st: *Ghash, out: *[mac_length]u8) void { | ||
| 286 | st.pad(); | ||
| 287 | mem.writeIntBig(u64, out[0..8], st.y1); | ||
| 288 | mem.writeIntBig(u64, out[8..16], st.y0); | ||
| 289 | |||
| 290 | mem.secureZero(u8, @ptrCast([*]u8, st)[0..@sizeOf(Ghash)]); | ||
| 291 | } | ||
| 292 | |||
| 293 | pub fn create(out: *[mac_length]u8, msg: []const u8, key: *const [minimum_key_length]u8) void { | ||
| 294 | var st = Ghash.init(key); | ||
| 295 | st.update(msg); | ||
| 296 | st.final(out); | ||
| 297 | } | ||
| 298 | }; | ||
| 299 | |||
| 300 | const htest = @import("test.zig"); | ||
| 301 | |||
| 302 | test "ghash" { | ||
| 303 | const key = [_]u8{0x42} ** 16; | ||
| 304 | const m = [_]u8{0x69} ** 256; | ||
| 305 | |||
| 306 | var st = Ghash.init(&key); | ||
| 307 | st.update(&m); | ||
| 308 | var out: [16]u8 = undefined; | ||
| 309 | st.final(&out); | ||
| 310 | htest.assertEqual("889295fa746e8b174bf4ec80a65dea41", &out); | ||
| 311 | |||
| 312 | st = Ghash.init(&key); | ||
| 313 | st.update(m[0..100]); | ||
| 314 | st.update(m[100..]); | ||
| 315 | st.final(&out); | ||
| 316 | htest.assertEqual("889295fa746e8b174bf4ec80a65dea41", &out); | ||
| 317 | } | ||
lib/std/crypto/hkdf.zig created+66| ... | @@ -0,0 +1,66 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const hmac = std.crypto.auth.hmac; | ||
| 4 | const mem = std.mem; | ||
| 5 | |||
| 6 | /// HKDF-SHA256 | ||
| 7 | pub const HkdfSha256 = Hkdf(hmac.sha2.HmacSha256); | ||
| 8 | |||
| 9 | /// HKDF-SHA512 | ||
| 10 | pub const HkdfSha512 = Hkdf(hmac.sha2.HmacSha512); | ||
| 11 | |||
| 12 | /// The Hkdf construction takes some source of initial keying material and | ||
| 13 | /// derives one or more uniform keys from it. | ||
| 14 | pub fn Hkdf(comptime Hmac: type) type { | ||
| 15 | return struct { | ||
| 16 | /// Return a master key from a salt and initial keying material. | ||
| 17 | fn extract(salt: []const u8, ikm: []const u8) [Hmac.mac_length]u8 { | ||
| 18 | var prk: [Hmac.mac_length]u8 = undefined; | ||
| 19 | Hmac.create(&prk, ikm, salt); | ||
| 20 | return prk; | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Derive a subkey from a master key `prk` and a subkey description `ctx`. | ||
| 24 | fn expand(out: []u8, ctx: []const u8, prk: [Hmac.mac_length]u8) void { | ||
| 25 | assert(out.len < Hmac.mac_length * 255); // output size is too large for the Hkdf construction | ||
| 26 | var i: usize = 0; | ||
| 27 | var counter = [1]u8{1}; | ||
| 28 | while (i + Hmac.mac_length <= out.len) : (i += Hmac.mac_length) { | ||
| 29 | var st = Hmac.init(&prk); | ||
| 30 | if (i != 0) { | ||
| 31 | st.update(out[i - Hmac.mac_length ..][0..Hmac.mac_length]); | ||
| 32 | } | ||
| 33 | st.update(ctx); | ||
| 34 | st.update(&counter); | ||
| 35 | st.final(out[i..][0..Hmac.mac_length]); | ||
| 36 | counter[0] += 1; | ||
| 37 | } | ||
| 38 | const left = out.len % Hmac.mac_length; | ||
| 39 | if (left > 0) { | ||
| 40 | var st = Hmac.init(&prk); | ||
| 41 | if (i != 0) { | ||
| 42 | st.update(out[i - Hmac.mac_length ..][0..Hmac.mac_length]); | ||
| 43 | } | ||
| 44 | st.update(ctx); | ||
| 45 | st.update(&counter); | ||
| 46 | var tmp: [Hmac.mac_length]u8 = undefined; | ||
| 47 | st.final(tmp[0..Hmac.mac_length]); | ||
| 48 | mem.copy(u8, out[i..][0..left], tmp[0..left]); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | }; | ||
| 52 | } | ||
| 53 | |||
| 54 | const htest = @import("test.zig"); | ||
| 55 | |||
| 56 | test "Hkdf" { | ||
| 57 | const ikm = [_]u8{0x0b} ** 22; | ||
| 58 | const salt = [_]u8{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c }; | ||
| 59 | const context = [_]u8{ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 }; | ||
| 60 | const kdf = HkdfSha256; | ||
| 61 | const prk = kdf.extract(&salt, &ikm); | ||
| 62 | htest.assertEqual("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5", &prk); | ||
| 63 | var out: [42]u8 = undefined; | ||
| 64 | kdf.expand(&out, &context, prk); | ||
| 65 | htest.assertEqual("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865", &out); | ||
| 66 | } | ||
lib/std/crypto/poly1305.zig+17-9| ... | @@ -22,8 +22,7 @@ pub const Poly1305 = struct { | ... | @@ -22,8 +22,7 @@ pub const Poly1305 = struct { |
| 22 | // partial block buffer | 22 | // partial block buffer |
| 23 | buf: [block_size]u8 align(16) = undefined, | 23 | buf: [block_size]u8 align(16) = undefined, |
| 24 | 24 | ||
| 25 | pub fn init(key: []const u8) Poly1305 { | 25 | pub fn init(key: *const [minimum_key_length]u8) Poly1305 { |
| 26 | std.debug.assert(key.len >= minimum_key_length); | ||
| 27 | const t0 = mem.readIntLittle(u64, key[0..8]); | 26 | const t0 = mem.readIntLittle(u64, key[0..8]); |
| 28 | const t1 = mem.readIntLittle(u64, key[8..16]); | 27 | const t1 = mem.readIntLittle(u64, key[8..16]); |
| 29 | return Poly1305{ | 28 | return Poly1305{ |
| ... | @@ -92,7 +91,7 @@ pub const Poly1305 = struct { | ... | @@ -92,7 +91,7 @@ pub const Poly1305 = struct { |
| 92 | } | 91 | } |
| 93 | mb = mb[want..]; | 92 | mb = mb[want..]; |
| 94 | st.leftover += want; | 93 | st.leftover += want; |
| 95 | if (st.leftover > block_size) { | 94 | if (st.leftover < block_size) { |
| 96 | return; | 95 | return; |
| 97 | } | 96 | } |
| 98 | st.blocks(&st.buf, false); | 97 | st.blocks(&st.buf, false); |
| ... | @@ -115,8 +114,20 @@ pub const Poly1305 = struct { | ... | @@ -115,8 +114,20 @@ pub const Poly1305 = struct { |
| 115 | } | 114 | } |
| 116 | } | 115 | } |
| 117 | 116 | ||
| 118 | pub fn final(st: *Poly1305, out: []u8) void { | 117 | /// Zero-pad to align the next input to the first byte of a block |
| 119 | std.debug.assert(out.len >= mac_length); | 118 | pub fn pad(st: *Poly1305) void { |
| 119 | if (st.leftover == 0) { | ||
| 120 | return; | ||
| 121 | } | ||
| 122 | var i = st.leftover; | ||
| 123 | while (i < block_size) : (i += 1) { | ||
| 124 | st.buf[i] = 0; | ||
| 125 | } | ||
| 126 | st.blocks(&st.buf); | ||
| 127 | st.leftover = 0; | ||
| 128 | } | ||
| 129 | |||
| 130 | pub fn final(st: *Poly1305, out: *[mac_length]u8) void { | ||
| 120 | if (st.leftover > 0) { | 131 | if (st.leftover > 0) { |
| 121 | var i = st.leftover; | 132 | var i = st.leftover; |
| 122 | st.buf[i] = 1; | 133 | st.buf[i] = 1; |
| ... | @@ -187,10 +198,7 @@ pub const Poly1305 = struct { | ... | @@ -187,10 +198,7 @@ pub const Poly1305 = struct { |
| 187 | std.mem.secureZero(u8, @ptrCast([*]u8, st)[0..@sizeOf(Poly1305)]); | 198 | std.mem.secureZero(u8, @ptrCast([*]u8, st)[0..@sizeOf(Poly1305)]); |
| 188 | } | 199 | } |
| 189 | 200 | ||
| 190 | pub fn create(out: []u8, msg: []const u8, key: []const u8) void { | 201 | pub fn create(out: *[mac_length]u8, msg: []const u8, key: *const [minimum_key_length]u8) void { |
| 191 | std.debug.assert(out.len >= mac_length); | ||
| 192 | std.debug.assert(key.len >= minimum_key_length); | ||
| 193 | |||
| 194 | var st = Poly1305.init(key); | 202 | var st = Poly1305.init(key); |
| 195 | st.update(msg); | 203 | st.update(msg); |
| 196 | st.final(out); | 204 | st.final(out); |
lib/std/event/loop.zig+55| ... | @@ -647,6 +647,31 @@ pub const Loop = struct { | ... | @@ -647,6 +647,31 @@ pub const Loop = struct { |
| 647 | } | 647 | } |
| 648 | } | 648 | } |
| 649 | 649 | ||
| 650 | /// Runs the provided function asynchronously. The function's frame is allocated | ||
| 651 | /// with `allocator` and freed when the function returns. | ||
| 652 | /// `func` must return void and it can be an async function. | ||
| 653 | /// Yields to the event loop, running the function on the next tick. | ||
| 654 | pub fn runDetached(self: *Loop, alloc: *mem.Allocator, comptime func: anytype, args: anytype) error{OutOfMemory}!void { | ||
| 655 | if (!std.io.is_async) @compileError("Can't use runDetached in non-async mode!"); | ||
| 656 | if (@TypeOf(@call(.{}, func, args)) != void) { | ||
| 657 | @compileError("`func` must not have a return value"); | ||
| 658 | } | ||
| 659 | |||
| 660 | const Wrapper = struct { | ||
| 661 | const Args = @TypeOf(args); | ||
| 662 | fn run(func_args: Args, loop: *Loop, allocator: *mem.Allocator) void { | ||
| 663 | loop.yield(); | ||
| 664 | const result = @call(.{}, func, func_args); | ||
| 665 | suspend { | ||
| 666 | allocator.destroy(@frame()); | ||
| 667 | } | ||
| 668 | } | ||
| 669 | }; | ||
| 670 | |||
| 671 | var run_frame = try alloc.create(@Frame(Wrapper.run)); | ||
| 672 | run_frame.* = async Wrapper.run(args, self, alloc); | ||
| 673 | } | ||
| 674 | |||
| 650 | /// Yielding lets the event loop run, starting any unstarted async operations. | 675 | /// Yielding lets the event loop run, starting any unstarted async operations. |
| 651 | /// Note that async operations automatically start when a function yields for any other reason, | 676 | /// Note that async operations automatically start when a function yields for any other reason, |
| 652 | /// for example, when async I/O is performed. This function is intended to be used only when | 677 | /// for example, when async I/O is performed. This function is intended to be used only when |
| ... | @@ -1493,3 +1518,33 @@ fn testEventLoop2(h: anyframe->i32, did_it: *bool) void { | ... | @@ -1493,3 +1518,33 @@ fn testEventLoop2(h: anyframe->i32, did_it: *bool) void { |
| 1493 | testing.expect(value == 1234); | 1518 | testing.expect(value == 1234); |
| 1494 | did_it.* = true; | 1519 | did_it.* = true; |
| 1495 | } | 1520 | } |
| 1521 | |||
| 1522 | var testRunDetachedData: usize = 0; | ||
| 1523 | test "std.event.Loop - runDetached" { | ||
| 1524 | // https://github.com/ziglang/zig/issues/1908 | ||
| 1525 | if (builtin.single_threaded) return error.SkipZigTest; | ||
| 1526 | if (!std.io.is_async) return error.SkipZigTest; | ||
| 1527 | if (true) { | ||
| 1528 | // https://github.com/ziglang/zig/issues/4922 | ||
| 1529 | return error.SkipZigTest; | ||
| 1530 | } | ||
| 1531 | |||
| 1532 | var loop: Loop = undefined; | ||
| 1533 | try loop.initMultiThreaded(); | ||
| 1534 | defer loop.deinit(); | ||
| 1535 | |||
| 1536 | // Schedule the execution, won't actually start until we start the | ||
| 1537 | // event loop. | ||
| 1538 | try loop.runDetached(std.testing.allocator, testRunDetached, .{}); | ||
| 1539 | |||
| 1540 | // Now we can start the event loop. The function will return only | ||
| 1541 | // after all tasks have been completed, allowing us to synchonize | ||
| 1542 | // with the previous runDetached. | ||
| 1543 | loop.run(); | ||
| 1544 | |||
| 1545 | testing.expect(testRunDetachedData == 1); | ||
| 1546 | } | ||
| 1547 | |||
| 1548 | fn testRunDetached() void { | ||
| 1549 | testRunDetachedData += 1; | ||
| 1550 | } |
lib/std/fmt.zig+10| ... | @@ -1181,6 +1181,16 @@ fn bufPrintIntToSlice(buf: []u8, value: anytype, base: u8, uppercase: bool, opti | ... | @@ -1181,6 +1181,16 @@ fn bufPrintIntToSlice(buf: []u8, value: anytype, base: u8, uppercase: bool, opti |
| 1181 | return buf[0..formatIntBuf(buf, value, base, uppercase, options)]; | 1181 | return buf[0..formatIntBuf(buf, value, base, uppercase, options)]; |
| 1182 | } | 1182 | } |
| 1183 | 1183 | ||
| 1184 | pub fn comptimePrint(comptime fmt: []const u8, args: anytype) *const [count(fmt, args)]u8 { | ||
| 1185 | comptime var buf: [count(fmt, args)]u8 = undefined; | ||
| 1186 | _ = bufPrint(&buf, fmt, args) catch unreachable; | ||
| 1187 | return &buf; | ||
| 1188 | } | ||
| 1189 | |||
| 1190 | test "comptimePrint" { | ||
| 1191 | std.testing.expectEqualSlices(u8, "100", comptime comptimePrint("{}", .{100})); | ||
| 1192 | } | ||
| 1193 | |||
| 1184 | test "parse u64 digit too big" { | 1194 | test "parse u64 digit too big" { |
| 1185 | _ = parseUnsigned(u64, "123a", 10) catch |err| { | 1195 | _ = parseUnsigned(u64, "123a", 10) catch |err| { |
| 1186 | if (err == error.InvalidCharacter) return; | 1196 | if (err == error.InvalidCharacter) return; |
lib/std/fs.zig+13-5| ... | @@ -1856,7 +1856,7 @@ pub const Dir = struct { | ... | @@ -1856,7 +1856,7 @@ pub const Dir = struct { |
| 1856 | } | 1856 | } |
| 1857 | }; | 1857 | }; |
| 1858 | 1858 | ||
| 1859 | /// Returns an handle to the current working directory. It is not opened with iteration capability. | 1859 | /// Returns a handle to the current working directory. It is not opened with iteration capability. |
| 1860 | /// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior. | 1860 | /// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior. |
| 1861 | /// On POSIX targets, this function is comptime-callable. | 1861 | /// On POSIX targets, this function is comptime-callable. |
| 1862 | pub fn cwd() Dir { | 1862 | pub fn cwd() Dir { |
| ... | @@ -2162,7 +2162,7 @@ pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File { | ... | @@ -2162,7 +2162,7 @@ pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File { |
| 2162 | return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, flags); | 2162 | return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, flags); |
| 2163 | } | 2163 | } |
| 2164 | 2164 | ||
| 2165 | pub const SelfExePathError = os.ReadLinkError || os.SysCtlError; | 2165 | pub const SelfExePathError = os.ReadLinkError || os.SysCtlError || os.RealPathError; |
| 2166 | 2166 | ||
| 2167 | /// `selfExePath` except allocates the result on the heap. | 2167 | /// `selfExePath` except allocates the result on the heap. |
| 2168 | /// Caller owns returned memory. | 2168 | /// Caller owns returned memory. |
| ... | @@ -2190,10 +2190,18 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 { | ... | @@ -2190,10 +2190,18 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 { |
| 2190 | /// TODO make the return type of this a null terminated pointer | 2190 | /// TODO make the return type of this a null terminated pointer |
| 2191 | pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { | 2191 | pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { |
| 2192 | if (is_darwin) { | 2192 | if (is_darwin) { |
| 2193 | var u32_len: u32 = @intCast(u32, math.min(out_buffer.len, math.maxInt(u32))); | 2193 | // Note that _NSGetExecutablePath() will return "a path" to |
| 2194 | const rc = std.c._NSGetExecutablePath(out_buffer.ptr, &u32_len); | 2194 | // the executable not a "real path" to the executable. |
| 2195 | var symlink_path_buf: [MAX_PATH_BYTES:0]u8 = undefined; | ||
| 2196 | var u32_len: u32 = MAX_PATH_BYTES + 1; // include the sentinel | ||
| 2197 | const rc = std.c._NSGetExecutablePath(&symlink_path_buf, &u32_len); | ||
| 2195 | if (rc != 0) return error.NameTooLong; | 2198 | if (rc != 0) return error.NameTooLong; |
| 2196 | return mem.spanZ(@ptrCast([*:0]u8, out_buffer)); | 2199 | |
| 2200 | var real_path_buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 2201 | const real_path = try std.os.realpathZ(&symlink_path_buf, &real_path_buf); | ||
| 2202 | if (real_path.len > out_buffer.len) return error.NameTooLong; | ||
| 2203 | std.mem.copy(u8, out_buffer, real_path); | ||
| 2204 | return out_buffer[0..real_path.len]; | ||
| 2197 | } | 2205 | } |
| 2198 | switch (builtin.os.tag) { | 2206 | switch (builtin.os.tag) { |
| 2199 | .linux => return os.readlinkZ("/proc/self/exe", out_buffer), | 2207 | .linux => return os.readlinkZ("/proc/self/exe", out_buffer), |
lib/std/fs/file.zig+9-7| ... | @@ -615,7 +615,7 @@ pub const File = struct { | ... | @@ -615,7 +615,7 @@ pub const File = struct { |
| 615 | } | 615 | } |
| 616 | } | 616 | } |
| 617 | 617 | ||
| 618 | pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!usize { | 618 | pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize { |
| 619 | if (is_windows) { | 619 | if (is_windows) { |
| 620 | // TODO improve this to use WriteFileScatter | 620 | // TODO improve this to use WriteFileScatter |
| 621 | if (iovecs.len == 0) return @as(usize, 0); | 621 | if (iovecs.len == 0) return @as(usize, 0); |
| ... | @@ -632,11 +632,11 @@ pub const File = struct { | ... | @@ -632,11 +632,11 @@ pub const File = struct { |
| 632 | 632 | ||
| 633 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 633 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 634 | /// order to handle partial writes from the underlying OS layer. | 634 | /// order to handle partial writes from the underlying OS layer. |
| 635 | pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!void { | 635 | pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void { |
| 636 | if (iovecs.len == 0) return; | 636 | if (iovecs.len == 0) return; |
| 637 | 637 | ||
| 638 | var i: usize = 0; | 638 | var i: usize = 0; |
| 639 | var off: usize = 0; | 639 | var off: u64 = 0; |
| 640 | while (true) { | 640 | while (true) { |
| 641 | var amt = try self.pwritev(iovecs[i..], offset + off); | 641 | var amt = try self.pwritev(iovecs[i..], offset + off); |
| 642 | off += amt; | 642 | off += amt; |
| ... | @@ -652,14 +652,16 @@ pub const File = struct { | ... | @@ -652,14 +652,16 @@ pub const File = struct { |
| 652 | 652 | ||
| 653 | pub const CopyRangeError = os.CopyFileRangeError; | 653 | pub const CopyRangeError = os.CopyFileRangeError; |
| 654 | 654 | ||
| 655 | pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) CopyRangeError!usize { | 655 | pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: u64) CopyRangeError!u64 { |
| 656 | return os.copy_file_range(in.handle, in_offset, out.handle, out_offset, len, 0); | 656 | const adjusted_len = math.cast(usize, len) catch math.maxInt(usize); |
| 657 | const result = try os.copy_file_range(in.handle, in_offset, out.handle, out_offset, adjusted_len, 0); | ||
| 658 | return result; | ||
| 657 | } | 659 | } |
| 658 | 660 | ||
| 659 | /// Returns the number of bytes copied. If the number read is smaller than `buffer.len`, it | 661 | /// Returns the number of bytes copied. If the number read is smaller than `buffer.len`, it |
| 660 | /// means the in file reached the end. Reaching the end of a file is not an error condition. | 662 | /// means the in file reached the end. Reaching the end of a file is not an error condition. |
| 661 | pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) CopyRangeError!usize { | 663 | pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: u64) CopyRangeError!u64 { |
| 662 | var total_bytes_copied: usize = 0; | 664 | var total_bytes_copied: u64 = 0; |
| 663 | var in_off = in_offset; | 665 | var in_off = in_offset; |
| 664 | var out_off = out_offset; | 666 | var out_off = out_offset; |
| 665 | while (total_bytes_copied < len) { | 667 | while (total_bytes_copied < len) { |
lib/std/macho.zig+44| ... | @@ -1257,3 +1257,47 @@ pub const reloc_type_x86_64 = packed enum(u4) { | ... | @@ -1257,3 +1257,47 @@ pub const reloc_type_x86_64 = packed enum(u4) { |
| 1257 | /// for thread local variables | 1257 | /// for thread local variables |
| 1258 | X86_64_RELOC_TLV, | 1258 | X86_64_RELOC_TLV, |
| 1259 | }; | 1259 | }; |
| 1260 | |||
| 1261 | /// This symbol is a reference to an external non-lazy (data) symbol. | ||
| 1262 | pub const REFERENCE_FLAG_UNDEFINED_NON_LAZY: u16 = 0x0; | ||
| 1263 | |||
| 1264 | /// This symbol is a reference to an external lazy symbol—that is, to a function call. | ||
| 1265 | pub const REFERENCE_FLAG_UNDEFINED_LAZY: u16 = 0x1; | ||
| 1266 | |||
| 1267 | /// This symbol is defined in this module. | ||
| 1268 | pub const REFERENCE_FLAG_DEFINED: u16 = 0x2; | ||
| 1269 | |||
| 1270 | /// This symbol is defined in this module and is visible only to modules within this shared library. | ||
| 1271 | pub const REFERENCE_FLAG_PRIVATE_DEFINED: u16 = 3; | ||
| 1272 | |||
| 1273 | /// This symbol is defined in another module in this file, is a non-lazy (data) symbol, and is visible | ||
| 1274 | /// only to modules within this shared library. | ||
| 1275 | pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY: u16 = 4; | ||
| 1276 | |||
| 1277 | /// This symbol is defined in another module in this file, is a lazy (function) symbol, and is visible | ||
| 1278 | /// only to modules within this shared library. | ||
| 1279 | pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY: u16 = 5; | ||
| 1280 | |||
| 1281 | /// Must be set for any defined symbol that is referenced by dynamic-loader APIs (such as dlsym and | ||
| 1282 | /// NSLookupSymbolInImage) and not ordinary undefined symbol references. The strip tool uses this bit | ||
| 1283 | /// to avoid removing symbols that must exist: If the symbol has this bit set, strip does not strip it. | ||
| 1284 | pub const REFERENCED_DYNAMICALLY: u16 = 0x10; | ||
| 1285 | |||
| 1286 | /// Used by the dynamic linker at runtime. Do not set this bit. | ||
| 1287 | pub const N_DESC_DISCARDED: u16 = 0x20; | ||
| 1288 | |||
| 1289 | /// Indicates that this symbol is a weak reference. If the dynamic linker cannot find a definition | ||
| 1290 | /// for this symbol, it sets the address of this symbol to 0. The static linker sets this symbol given | ||
| 1291 | /// the appropriate weak-linking flags. | ||
| 1292 | pub const N_WEAK_REF: u16 = 0x40; | ||
| 1293 | |||
| 1294 | /// Indicates that this symbol is a weak definition. If the static linker or the dynamic linker finds | ||
| 1295 | /// another (non-weak) definition for this symbol, the weak definition is ignored. Only symbols in a | ||
| 1296 | /// coalesced section (page 23) can be marked as a weak definition. | ||
| 1297 | pub const N_WEAK_DEF: u16 = 0x80; | ||
| 1298 | |||
| 1299 | /// The N_SYMBOL_RESOLVER bit of the n_desc field indicates that the | ||
| 1300 | /// that the function is actually a resolver function and should | ||
| 1301 | /// be called to get the address of the real function to use. | ||
| 1302 | /// This bit is only available in .o files (MH_OBJECT filetype) | ||
| 1303 | pub const N_SYMBOL_RESOLVER: u16 = 0x100; |
lib/std/math/big/int.zig+124| ... | @@ -58,6 +58,11 @@ pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize { | ... | @@ -58,6 +58,11 @@ pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize { |
| 58 | return (string_len + (limb_bits / base - 1)) / (limb_bits / base); | 58 | return (string_len + (limb_bits / base - 1)) / (limb_bits / base); |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | pub fn calcPowLimbsBufferLen(a_bit_count: usize, y: usize) usize { | ||
| 62 | // The 2 accounts for the minimum space requirement for llmulacc | ||
| 63 | return 2 + (a_bit_count * y + (limb_bits - 1)) / limb_bits; | ||
| 64 | } | ||
| 65 | |||
| 61 | /// a + b * c + *carry, sets carry to the overflow bits | 66 | /// a + b * c + *carry, sets carry to the overflow bits |
| 62 | pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { | 67 | pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { |
| 63 | @setRuntimeSafety(debug_safety); | 68 | @setRuntimeSafety(debug_safety); |
| ... | @@ -597,6 +602,52 @@ pub const Mutable = struct { | ... | @@ -597,6 +602,52 @@ pub const Mutable = struct { |
| 597 | return gcdLehmer(rma, x_copy, y_copy, limbs_buffer); | 602 | return gcdLehmer(rma, x_copy, y_copy, limbs_buffer); |
| 598 | } | 603 | } |
| 599 | 604 | ||
| 605 | /// q = a ^ b | ||
| 606 | /// | ||
| 607 | /// r may not alias a. | ||
| 608 | /// | ||
| 609 | /// Asserts that `r` has enough limbs to store the result. Upper bound is | ||
| 610 | /// `calcPowLimbsBufferLen(a.bitCountAbs(), b)`. | ||
| 611 | /// | ||
| 612 | /// `limbs_buffer` is used for temporary storage. | ||
| 613 | /// The amount required is given by `calcPowLimbsBufferLen`. | ||
| 614 | pub fn pow(r: *Mutable, a: Const, b: u32, limbs_buffer: []Limb) !void { | ||
| 615 | assert(r.limbs.ptr != a.limbs.ptr); // illegal aliasing | ||
| 616 | |||
| 617 | // Handle all the trivial cases first | ||
| 618 | switch (b) { | ||
| 619 | 0 => { | ||
| 620 | // a^0 = 1 | ||
| 621 | return r.set(1); | ||
| 622 | }, | ||
| 623 | 1 => { | ||
| 624 | // a^1 = a | ||
| 625 | return r.copy(a); | ||
| 626 | }, | ||
| 627 | else => {}, | ||
| 628 | } | ||
| 629 | |||
| 630 | if (a.eqZero()) { | ||
| 631 | // 0^b = 0 | ||
| 632 | return r.set(0); | ||
| 633 | } else if (a.limbs.len == 1 and a.limbs[0] == 1) { | ||
| 634 | // 1^b = 1 and -1^b = ±1 | ||
| 635 | r.set(1); | ||
| 636 | r.positive = a.positive or (b & 1) == 0; | ||
| 637 | return; | ||
| 638 | } | ||
| 639 | |||
| 640 | // Here a>1 and b>1 | ||
| 641 | const needed_limbs = calcPowLimbsBufferLen(a.bitCountAbs(), b); | ||
| 642 | assert(r.limbs.len >= needed_limbs); | ||
| 643 | assert(limbs_buffer.len >= needed_limbs); | ||
| 644 | |||
| 645 | llpow(r.limbs, a.limbs, b, limbs_buffer); | ||
| 646 | |||
| 647 | r.normalize(needed_limbs); | ||
| 648 | r.positive = a.positive or (b & 1) == 0; | ||
| 649 | } | ||
| 650 | |||
| 600 | /// rma may not alias x or y. | 651 | /// rma may not alias x or y. |
| 601 | /// x and y may alias each other. | 652 | /// x and y may alias each other. |
| 602 | /// Asserts that `rma` has enough limbs to store the result. Upper bound is given by `calcGcdNoAliasLimbLen`. | 653 | /// Asserts that `rma` has enough limbs to store the result. Upper bound is given by `calcGcdNoAliasLimbLen`. |
| ... | @@ -1775,6 +1826,29 @@ pub const Managed = struct { | ... | @@ -1775,6 +1826,29 @@ pub const Managed = struct { |
| 1775 | try m.gcd(x.toConst(), y.toConst(), &limbs_buffer); | 1826 | try m.gcd(x.toConst(), y.toConst(), &limbs_buffer); |
| 1776 | rma.setMetadata(m.positive, m.len); | 1827 | rma.setMetadata(m.positive, m.len); |
| 1777 | } | 1828 | } |
| 1829 | |||
| 1830 | pub fn pow(rma: *Managed, a: Managed, b: u32) !void { | ||
| 1831 | const needed_limbs = calcPowLimbsBufferLen(a.bitCountAbs(), b); | ||
| 1832 | |||
| 1833 | const limbs_buffer = try rma.allocator.alloc(Limb, needed_limbs); | ||
| 1834 | defer rma.allocator.free(limbs_buffer); | ||
| 1835 | |||
| 1836 | if (rma.limbs.ptr == a.limbs.ptr) { | ||
| 1837 | var m = try Managed.initCapacity(rma.allocator, needed_limbs); | ||
| 1838 | errdefer m.deinit(); | ||
| 1839 | var m_mut = m.toMutable(); | ||
| 1840 | try m_mut.pow(a.toConst(), b, limbs_buffer); | ||
| 1841 | m.setMetadata(m_mut.positive, m_mut.len); | ||
| 1842 | |||
| 1843 | rma.deinit(); | ||
| 1844 | rma.swap(&m); | ||
| 1845 | } else { | ||
| 1846 | try rma.ensureCapacity(needed_limbs); | ||
| 1847 | var rma_mut = rma.toMutable(); | ||
| 1848 | try rma_mut.pow(a.toConst(), b, limbs_buffer); | ||
| 1849 | rma.setMetadata(rma_mut.positive, rma_mut.len); | ||
| 1850 | } | ||
| 1851 | } | ||
| 1778 | }; | 1852 | }; |
| 1779 | 1853 | ||
| 1780 | /// Knuth 4.3.1, Algorithm M. | 1854 | /// Knuth 4.3.1, Algorithm M. |
| ... | @@ -2129,6 +2203,56 @@ fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void { | ... | @@ -2129,6 +2203,56 @@ fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void { |
| 2129 | } | 2203 | } |
| 2130 | } | 2204 | } |
| 2131 | 2205 | ||
| 2206 | /// Knuth 4.6.3 | ||
| 2207 | fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void { | ||
| 2208 | var tmp1: []Limb = undefined; | ||
| 2209 | var tmp2: []Limb = undefined; | ||
| 2210 | |||
| 2211 | // Multiplication requires no aliasing between the operand and the result | ||
| 2212 | // variable, use the output limbs and another temporary set to overcome this | ||
| 2213 | // limitation. | ||
| 2214 | // The initial assignment makes the result end in `r` so an extra memory | ||
| 2215 | // copy is saved, each 1 flips the index twice so it's a no-op so count the | ||
| 2216 | // 0. | ||
| 2217 | const b_leading_zeros = @intCast(u5, @clz(u32, b)); | ||
| 2218 | const exp_zeros = @popCount(u32, ~b) - b_leading_zeros; | ||
| 2219 | if (exp_zeros & 1 != 0) { | ||
| 2220 | tmp1 = tmp_limbs; | ||
| 2221 | tmp2 = r; | ||
| 2222 | } else { | ||
| 2223 | tmp1 = r; | ||
| 2224 | tmp2 = tmp_limbs; | ||
| 2225 | } | ||
| 2226 | |||
| 2227 | const a_norm = a[0..llnormalize(a)]; | ||
| 2228 | |||
| 2229 | mem.copy(Limb, tmp1, a_norm); | ||
| 2230 | mem.set(Limb, tmp1[a_norm.len..], 0); | ||
| 2231 | |||
| 2232 | // Scan the exponent as a binary number, from left to right, dropping the | ||
| 2233 | // most significant bit set. | ||
| 2234 | const exp_bits = @intCast(u5, 31 - b_leading_zeros); | ||
| 2235 | var exp = @bitReverse(u32, b) >> 1 + b_leading_zeros; | ||
| 2236 | |||
| 2237 | var i: u5 = 0; | ||
| 2238 | while (i < exp_bits) : (i += 1) { | ||
| 2239 | // Square | ||
| 2240 | { | ||
| 2241 | mem.set(Limb, tmp2, 0); | ||
| 2242 | const op = tmp1[0..llnormalize(tmp1)]; | ||
| 2243 | llmulacc(null, tmp2, op, op); | ||
| 2244 | mem.swap([]Limb, &tmp1, &tmp2); | ||
| 2245 | } | ||
| 2246 | // Multiply by a | ||
| 2247 | if (exp & 1 != 0) { | ||
| 2248 | mem.set(Limb, tmp2, 0); | ||
| 2249 | llmulacc(null, tmp2, tmp1[0..llnormalize(tmp1)], a_norm); | ||
| 2250 | mem.swap([]Limb, &tmp1, &tmp2); | ||
| 2251 | } | ||
| 2252 | exp >>= 1; | ||
| 2253 | } | ||
| 2254 | } | ||
| 2255 | |||
| 2132 | // Storage must live for the lifetime of the returned value | 2256 | // Storage must live for the lifetime of the returned value |
| 2133 | fn fixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Mutable { | 2257 | fn fixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Mutable { |
| 2134 | assert(storage.len >= 2); | 2258 | assert(storage.len >= 2); |
lib/std/math/big/int_test.zig+52| ... | @@ -1480,3 +1480,55 @@ test "big.int const to managed" { | ... | @@ -1480,3 +1480,55 @@ test "big.int const to managed" { |
| 1480 | 1480 | ||
| 1481 | testing.expect(a.toConst().eq(b.toConst())); | 1481 | testing.expect(a.toConst().eq(b.toConst())); |
| 1482 | } | 1482 | } |
| 1483 | |||
| 1484 | test "big.int pow" { | ||
| 1485 | { | ||
| 1486 | var a = try Managed.initSet(testing.allocator, 10); | ||
| 1487 | defer a.deinit(); | ||
| 1488 | |||
| 1489 | try a.pow(a, 8); | ||
| 1490 | testing.expectEqual(@as(u32, 100000000), try a.to(u32)); | ||
| 1491 | } | ||
| 1492 | { | ||
| 1493 | var a = try Managed.initSet(testing.allocator, 10); | ||
| 1494 | defer a.deinit(); | ||
| 1495 | |||
| 1496 | var y = try Managed.init(testing.allocator); | ||
| 1497 | defer y.deinit(); | ||
| 1498 | |||
| 1499 | // y and a are not aliased | ||
| 1500 | try y.pow(a, 123); | ||
| 1501 | // y and a are aliased | ||
| 1502 | try a.pow(a, 123); | ||
| 1503 | |||
| 1504 | testing.expect(a.eq(y)); | ||
| 1505 | |||
| 1506 | const ys = try y.toString(testing.allocator, 16, false); | ||
| 1507 | defer testing.allocator.free(ys); | ||
| 1508 | testing.expectEqualSlices( | ||
| 1509 | u8, | ||
| 1510 | "183425a5f872f126e00a5ad62c839075cd6846c6fb0230887c7ad7a9dc530fcb" ++ | ||
| 1511 | "4933f60e8000000000000000000000000000000", | ||
| 1512 | ys, | ||
| 1513 | ); | ||
| 1514 | } | ||
| 1515 | // Special cases | ||
| 1516 | { | ||
| 1517 | var a = try Managed.initSet(testing.allocator, 0); | ||
| 1518 | defer a.deinit(); | ||
| 1519 | |||
| 1520 | try a.pow(a, 100); | ||
| 1521 | testing.expectEqual(@as(i32, 0), try a.to(i32)); | ||
| 1522 | |||
| 1523 | try a.set(1); | ||
| 1524 | try a.pow(a, 0); | ||
| 1525 | testing.expectEqual(@as(i32, 1), try a.to(i32)); | ||
| 1526 | try a.pow(a, 100); | ||
| 1527 | testing.expectEqual(@as(i32, 1), try a.to(i32)); | ||
| 1528 | try a.set(-1); | ||
| 1529 | try a.pow(a, 15); | ||
| 1530 | testing.expectEqual(@as(i32, -1), try a.to(i32)); | ||
| 1531 | try a.pow(a, 16); | ||
| 1532 | testing.expectEqual(@as(i32, 1), try a.to(i32)); | ||
| 1533 | } | ||
| 1534 | } |
lib/std/meta.zig+2| ... | @@ -854,6 +854,7 @@ pub fn ArgsTuple(comptime Function: type) type { | ... | @@ -854,6 +854,7 @@ pub fn ArgsTuple(comptime Function: type) type { |
| 854 | .field_type = arg.arg_type.?, | 854 | .field_type = arg.arg_type.?, |
| 855 | .default_value = @as(?(arg.arg_type.?), null), | 855 | .default_value = @as(?(arg.arg_type.?), null), |
| 856 | .is_comptime = false, | 856 | .is_comptime = false, |
| 857 | .alignment = @alignOf(arg.arg_type.?), | ||
| 857 | }; | 858 | }; |
| 858 | } | 859 | } |
| 859 | 860 | ||
| ... | @@ -884,6 +885,7 @@ pub fn Tuple(comptime types: []const type) type { | ... | @@ -884,6 +885,7 @@ pub fn Tuple(comptime types: []const type) type { |
| 884 | .field_type = T, | 885 | .field_type = T, |
| 885 | .default_value = @as(?T, null), | 886 | .default_value = @as(?T, null), |
| 886 | .is_comptime = false, | 887 | .is_comptime = false, |
| 888 | .alignment = @alignOf(T), | ||
| 887 | }; | 889 | }; |
| 888 | } | 890 | } |
| 889 | 891 |
lib/std/meta/trailer_flags.zig+1| ... | @@ -47,6 +47,7 @@ pub fn TrailerFlags(comptime Fields: type) type { | ... | @@ -47,6 +47,7 @@ pub fn TrailerFlags(comptime Fields: type) type { |
| 47 | @as(?struct_field.field_type, null), | 47 | @as(?struct_field.field_type, null), |
| 48 | ), | 48 | ), |
| 49 | .is_comptime = false, | 49 | .is_comptime = false, |
| 50 | .alignment = @alignOf(?struct_field.field_type), | ||
| 50 | }; | 51 | }; |
| 51 | } | 52 | } |
| 52 | break :blk @Type(.{ | 53 | break :blk @Type(.{ |
lib/std/os.zig+31-1| ... | @@ -3993,7 +3993,7 @@ pub const RealPathError = error{ | ... | @@ -3993,7 +3993,7 @@ pub const RealPathError = error{ |
| 3993 | /// Expands all symbolic links and resolves references to `.`, `..`, and | 3993 | /// Expands all symbolic links and resolves references to `.`, `..`, and |
| 3994 | /// extra `/` characters in `pathname`. | 3994 | /// extra `/` characters in `pathname`. |
| 3995 | /// The return value is a slice of `out_buffer`, but not necessarily from the beginning. | 3995 | /// The return value is a slice of `out_buffer`, but not necessarily from the beginning. |
| 3996 | /// See also `realpathC` and `realpathW`. | 3996 | /// See also `realpathZ` and `realpathW`. |
| 3997 | pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { | 3997 | pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { |
| 3998 | if (builtin.os.tag == .windows) { | 3998 | if (builtin.os.tag == .windows) { |
| 3999 | const pathname_w = try windows.sliceToPrefixedFileW(pathname); | 3999 | const pathname_w = try windows.sliceToPrefixedFileW(pathname); |
| ... | @@ -5410,3 +5410,33 @@ pub fn prctl(option: i32, args: anytype) PrctlError!u31 { | ... | @@ -5410,3 +5410,33 @@ pub fn prctl(option: i32, args: anytype) PrctlError!u31 { |
| 5410 | else => |err| return std.os.unexpectedErrno(err), | 5410 | else => |err| return std.os.unexpectedErrno(err), |
| 5411 | } | 5411 | } |
| 5412 | } | 5412 | } |
| 5413 | |||
| 5414 | pub const GetrlimitError = UnexpectedError; | ||
| 5415 | |||
| 5416 | pub fn getrlimit(resource: rlimit_resource) GetrlimitError!rlimit { | ||
| 5417 | // TODO implement for systems other than linux and enable test | ||
| 5418 | var limits: rlimit = undefined; | ||
| 5419 | const rc = system.getrlimit(resource, &limits); | ||
| 5420 | switch (errno(rc)) { | ||
| 5421 | 0 => return limits, | ||
| 5422 | EFAULT => unreachable, // bogus pointer | ||
| 5423 | EINVAL => unreachable, | ||
| 5424 | else => |err| return std.os.unexpectedErrno(err), | ||
| 5425 | } | ||
| 5426 | } | ||
| 5427 | |||
| 5428 | pub const SetrlimitError = error{ | ||
| 5429 | PermissionDenied, | ||
| 5430 | } || UnexpectedError; | ||
| 5431 | |||
| 5432 | pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void { | ||
| 5433 | // TODO implement for systems other than linux and enable test | ||
| 5434 | const rc = system.setrlimit(resource, &limits); | ||
| 5435 | switch (errno(rc)) { | ||
| 5436 | 0 => return, | ||
| 5437 | EFAULT => unreachable, // bogus pointer | ||
| 5438 | EINVAL => unreachable, | ||
| 5439 | EPERM => return error.PermissionDenied, | ||
| 5440 | else => |err| return std.os.unexpectedErrno(err), | ||
| 5441 | } | ||
| 5442 | } |
lib/std/os/bits/linux.zig+76| ... | @@ -1890,3 +1890,79 @@ pub const ifreq = extern struct { | ... | @@ -1890,3 +1890,79 @@ pub const ifreq = extern struct { |
| 1890 | data: ?[*]u8, | 1890 | data: ?[*]u8, |
| 1891 | }, | 1891 | }, |
| 1892 | }; | 1892 | }; |
| 1893 | |||
| 1894 | // doc comments copied from musl | ||
| 1895 | pub const rlimit_resource = extern enum(c_int) { | ||
| 1896 | /// Per-process CPU limit, in seconds. | ||
| 1897 | CPU, | ||
| 1898 | |||
| 1899 | /// Largest file that can be created, in bytes. | ||
| 1900 | FSIZE, | ||
| 1901 | |||
| 1902 | /// Maximum size of data segment, in bytes. | ||
| 1903 | DATA, | ||
| 1904 | |||
| 1905 | /// Maximum size of stack segment, in bytes. | ||
| 1906 | STACK, | ||
| 1907 | |||
| 1908 | /// Largest core file that can be created, in bytes. | ||
| 1909 | CORE, | ||
| 1910 | |||
| 1911 | /// Largest resident set size, in bytes. | ||
| 1912 | /// This affects swapping; processes that are exceeding their | ||
| 1913 | /// resident set size will be more likely to have physical memory | ||
| 1914 | /// taken from them. | ||
| 1915 | RSS, | ||
| 1916 | |||
| 1917 | /// Number of processes. | ||
| 1918 | NPROC, | ||
| 1919 | |||
| 1920 | /// Number of open files. | ||
| 1921 | NOFILE, | ||
| 1922 | |||
| 1923 | /// Locked-in-memory address space. | ||
| 1924 | MEMLOCK, | ||
| 1925 | |||
| 1926 | /// Address space limit. | ||
| 1927 | AS, | ||
| 1928 | |||
| 1929 | /// Maximum number of file locks. | ||
| 1930 | LOCKS, | ||
| 1931 | |||
| 1932 | /// Maximum number of pending signals. | ||
| 1933 | SIGPENDING, | ||
| 1934 | |||
| 1935 | /// Maximum bytes in POSIX message queues. | ||
| 1936 | MSGQUEUE, | ||
| 1937 | |||
| 1938 | /// Maximum nice priority allowed to raise to. | ||
| 1939 | /// Nice levels 19 .. -20 correspond to 0 .. 39 | ||
| 1940 | /// values of this resource limit. | ||
| 1941 | NICE, | ||
| 1942 | |||
| 1943 | /// Maximum realtime priority allowed for non-priviledged | ||
| 1944 | /// processes. | ||
| 1945 | RTPRIO, | ||
| 1946 | |||
| 1947 | /// Maximum CPU time in µs that a process scheduled under a real-time | ||
| 1948 | /// scheduling policy may consume without making a blocking system | ||
| 1949 | /// call before being forcibly descheduled. | ||
| 1950 | RTTIME, | ||
| 1951 | |||
| 1952 | _, | ||
| 1953 | }; | ||
| 1954 | |||
| 1955 | pub const rlim_t = u64; | ||
| 1956 | |||
| 1957 | /// No limit | ||
| 1958 | pub const RLIM_INFINITY = ~@as(rlim_t, 0); | ||
| 1959 | |||
| 1960 | pub const RLIM_SAVED_MAX = RLIM_INFINITY; | ||
| 1961 | pub const RLIM_SAVED_CUR = RLIM_INFINITY; | ||
| 1962 | |||
| 1963 | pub const rlimit = extern struct { | ||
| 1964 | /// Soft limit | ||
| 1965 | cur: rlim_t, | ||
| 1966 | /// Hard limit | ||
| 1967 | max: rlim_t, | ||
| 1968 | }; |
lib/std/os/linux.zig+20| ... | @@ -1263,6 +1263,26 @@ pub fn prctl(option: i32, arg2: usize, arg3: usize, arg4: usize, arg5: usize) us | ... | @@ -1263,6 +1263,26 @@ pub fn prctl(option: i32, arg2: usize, arg3: usize, arg4: usize, arg5: usize) us |
| 1263 | return syscall5(.prctl, @bitCast(usize, @as(isize, option)), arg2, arg3, arg4, arg5); | 1263 | return syscall5(.prctl, @bitCast(usize, @as(isize, option)), arg2, arg3, arg4, arg5); |
| 1264 | } | 1264 | } |
| 1265 | 1265 | ||
| 1266 | pub fn getrlimit(resource: rlimit_resource, rlim: *rlimit) usize { | ||
| 1267 | // use prlimit64 to have 64 bit limits on 32 bit platforms | ||
| 1268 | return prlimit(0, resource, null, rlim); | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | pub fn setrlimit(resource: rlimit_resource, rlim: *const rlimit) usize { | ||
| 1272 | // use prlimit64 to have 64 bit limits on 32 bit platforms | ||
| 1273 | return prlimit(0, resource, rlim, null); | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | pub fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: ?*const rlimit, old_limit: ?*rlimit) usize { | ||
| 1277 | return syscall4( | ||
| 1278 | .prlimit64, | ||
| 1279 | @bitCast(usize, @as(isize, pid)), | ||
| 1280 | @bitCast(usize, @as(isize, @enumToInt(resource))), | ||
| 1281 | @ptrToInt(new_limit), | ||
| 1282 | @ptrToInt(old_limit) | ||
| 1283 | ); | ||
| 1284 | } | ||
| 1285 | |||
| 1266 | test "" { | 1286 | test "" { |
| 1267 | if (builtin.os.tag == .linux) { | 1287 | if (builtin.os.tag == .linux) { |
| 1268 | _ = @import("linux/test.zig"); | 1288 | _ = @import("linux/test.zig"); |
lib/std/os/test.zig+10| ... | @@ -591,3 +591,13 @@ test "fsync" { | ... | @@ -591,3 +591,13 @@ test "fsync" { |
| 591 | try os.fsync(file.handle); | 591 | try os.fsync(file.handle); |
| 592 | try os.fdatasync(file.handle); | 592 | try os.fdatasync(file.handle); |
| 593 | } | 593 | } |
| 594 | |||
| 595 | test "getrlimit and setrlimit" { | ||
| 596 | // TODO enable for other systems when implemented | ||
| 597 | if(builtin.os.tag != .linux){ | ||
| 598 | return error.SkipZigTest; | ||
| 599 | } | ||
| 600 | |||
| 601 | const cpuLimit = try os.getrlimit(.CPU); | ||
| 602 | try os.setrlimit(.CPU, cpuLimit); | ||
| 603 | } |
lib/std/packed_int_array.zig+15| ... | @@ -318,9 +318,12 @@ pub fn PackedIntSliceEndian(comptime Int: type, comptime endian: builtin.Endian) | ... | @@ -318,9 +318,12 @@ pub fn PackedIntSliceEndian(comptime Int: type, comptime endian: builtin.Endian) |
| 318 | }; | 318 | }; |
| 319 | } | 319 | } |
| 320 | 320 | ||
| 321 | const we_are_testing_this_with_stage1_which_leaks_comptime_memory = true; | ||
| 322 | |||
| 321 | test "PackedIntArray" { | 323 | test "PackedIntArray" { |
| 322 | // TODO @setEvalBranchQuota generates panics in wasm32. Investigate. | 324 | // TODO @setEvalBranchQuota generates panics in wasm32. Investigate. |
| 323 | if (builtin.arch == .wasm32) return error.SkipZigTest; | 325 | if (builtin.arch == .wasm32) return error.SkipZigTest; |
| 326 | if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest; | ||
| 324 | 327 | ||
| 325 | @setEvalBranchQuota(10000); | 328 | @setEvalBranchQuota(10000); |
| 326 | const max_bits = 256; | 329 | const max_bits = 256; |
| ... | @@ -358,6 +361,7 @@ test "PackedIntArray" { | ... | @@ -358,6 +361,7 @@ test "PackedIntArray" { |
| 358 | } | 361 | } |
| 359 | 362 | ||
| 360 | test "PackedIntArray init" { | 363 | test "PackedIntArray init" { |
| 364 | if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest; | ||
| 361 | const PackedArray = PackedIntArray(u3, 8); | 365 | const PackedArray = PackedIntArray(u3, 8); |
| 362 | var packed_array = PackedArray.init([_]u3{ 0, 1, 2, 3, 4, 5, 6, 7 }); | 366 | var packed_array = PackedArray.init([_]u3{ 0, 1, 2, 3, 4, 5, 6, 7 }); |
| 363 | var i = @as(usize, 0); | 367 | var i = @as(usize, 0); |
| ... | @@ -367,6 +371,7 @@ test "PackedIntArray init" { | ... | @@ -367,6 +371,7 @@ test "PackedIntArray init" { |
| 367 | test "PackedIntSlice" { | 371 | test "PackedIntSlice" { |
| 368 | // TODO @setEvalBranchQuota generates panics in wasm32. Investigate. | 372 | // TODO @setEvalBranchQuota generates panics in wasm32. Investigate. |
| 369 | if (builtin.arch == .wasm32) return error.SkipZigTest; | 373 | if (builtin.arch == .wasm32) return error.SkipZigTest; |
| 374 | if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest; | ||
| 370 | 375 | ||
| 371 | @setEvalBranchQuota(10000); | 376 | @setEvalBranchQuota(10000); |
| 372 | const max_bits = 256; | 377 | const max_bits = 256; |
| ... | @@ -405,6 +410,7 @@ test "PackedIntSlice" { | ... | @@ -405,6 +410,7 @@ test "PackedIntSlice" { |
| 405 | } | 410 | } |
| 406 | 411 | ||
| 407 | test "PackedIntSlice of PackedInt(Array/Slice)" { | 412 | test "PackedIntSlice of PackedInt(Array/Slice)" { |
| 413 | if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest; | ||
| 408 | const max_bits = 16; | 414 | const max_bits = 16; |
| 409 | const int_count = 19; | 415 | const int_count = 19; |
| 410 | 416 | ||
| ... | @@ -470,6 +476,7 @@ test "PackedIntSlice of PackedInt(Array/Slice)" { | ... | @@ -470,6 +476,7 @@ test "PackedIntSlice of PackedInt(Array/Slice)" { |
| 470 | } | 476 | } |
| 471 | 477 | ||
| 472 | test "PackedIntSlice accumulating bit offsets" { | 478 | test "PackedIntSlice accumulating bit offsets" { |
| 479 | if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest; | ||
| 473 | //bit_offset is u3, so standard debugging asserts should catch | 480 | //bit_offset is u3, so standard debugging asserts should catch |
| 474 | // anything | 481 | // anything |
| 475 | { | 482 | { |
| ... | @@ -497,6 +504,8 @@ test "PackedIntSlice accumulating bit offsets" { | ... | @@ -497,6 +504,8 @@ test "PackedIntSlice accumulating bit offsets" { |
| 497 | //@NOTE: As I do not have a big endian system to test this on, | 504 | //@NOTE: As I do not have a big endian system to test this on, |
| 498 | // big endian values were not tested | 505 | // big endian values were not tested |
| 499 | test "PackedInt(Array/Slice) sliceCast" { | 506 | test "PackedInt(Array/Slice) sliceCast" { |
| 507 | if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest; | ||
| 508 | |||
| 500 | const PackedArray = PackedIntArray(u1, 16); | 509 | const PackedArray = PackedIntArray(u1, 16); |
| 501 | var packed_array = PackedArray.init([_]u1{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 }); | 510 | var packed_array = PackedArray.init([_]u1{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 }); |
| 502 | const packed_slice_cast_2 = packed_array.sliceCast(u2); | 511 | const packed_slice_cast_2 = packed_array.sliceCast(u2); |
| ... | @@ -537,6 +546,8 @@ test "PackedInt(Array/Slice) sliceCast" { | ... | @@ -537,6 +546,8 @@ test "PackedInt(Array/Slice) sliceCast" { |
| 537 | } | 546 | } |
| 538 | 547 | ||
| 539 | test "PackedInt(Array/Slice)Endian" { | 548 | test "PackedInt(Array/Slice)Endian" { |
| 549 | if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest; | ||
| 550 | |||
| 540 | { | 551 | { |
| 541 | const PackedArrayBe = PackedIntArrayEndian(u4, .Big, 8); | 552 | const PackedArrayBe = PackedIntArrayEndian(u4, .Big, 8); |
| 542 | var packed_array_be = PackedArrayBe.init([_]u4{ 0, 1, 2, 3, 4, 5, 6, 7 }); | 553 | var packed_array_be = PackedArrayBe.init([_]u4{ 0, 1, 2, 3, 4, 5, 6, 7 }); |
| ... | @@ -604,6 +615,8 @@ test "PackedInt(Array/Slice)Endian" { | ... | @@ -604,6 +615,8 @@ test "PackedInt(Array/Slice)Endian" { |
| 604 | // after this one is not mapped and will cause a segfault if we | 615 | // after this one is not mapped and will cause a segfault if we |
| 605 | // don't account for the bounds. | 616 | // don't account for the bounds. |
| 606 | test "PackedIntArray at end of available memory" { | 617 | test "PackedIntArray at end of available memory" { |
| 618 | if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest; | ||
| 619 | |||
| 607 | switch (builtin.os.tag) { | 620 | switch (builtin.os.tag) { |
| 608 | .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {}, | 621 | .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {}, |
| 609 | else => return, | 622 | else => return, |
| ... | @@ -623,6 +636,8 @@ test "PackedIntArray at end of available memory" { | ... | @@ -623,6 +636,8 @@ test "PackedIntArray at end of available memory" { |
| 623 | } | 636 | } |
| 624 | 637 | ||
| 625 | test "PackedIntSlice at end of available memory" { | 638 | test "PackedIntSlice at end of available memory" { |
| 639 | if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest; | ||
| 640 | |||
| 626 | switch (builtin.os.tag) { | 641 | switch (builtin.os.tag) { |
| 627 | .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {}, | 642 | .linux, .macosx, .ios, .freebsd, .netbsd, .windows => {}, |
| 628 | else => return, | 643 | else => return, |
lib/std/zig/system.zig+2| ... | @@ -213,6 +213,8 @@ pub const NativeTargetInfo = struct { | ... | @@ -213,6 +213,8 @@ pub const NativeTargetInfo = struct { |
| 213 | // kernel version | 213 | // kernel version |
| 214 | const kernel_version = if (mem.indexOfScalar(u8, release, '-')) |pos| | 214 | const kernel_version = if (mem.indexOfScalar(u8, release, '-')) |pos| |
| 215 | release[0..pos] | 215 | release[0..pos] |
| 216 | else if (mem.indexOfScalar(u8, release, '_')) |pos| | ||
| 217 | release[0..pos] | ||
| 216 | else | 218 | else |
| 217 | release; | 219 | release; |
| 218 | 220 |
src/Compilation.zig+51-13| ... | @@ -8,6 +8,7 @@ const log = std.log.scoped(.compilation); | ... | @@ -8,6 +8,7 @@ const log = std.log.scoped(.compilation); |
| 8 | const Target = std.Target; | 8 | const Target = std.Target; |
| 9 | 9 | ||
| 10 | const Value = @import("value.zig").Value; | 10 | const Value = @import("value.zig").Value; |
| 11 | const Type = @import("type.zig").Type; | ||
| 11 | const target_util = @import("target.zig"); | 12 | const target_util = @import("target.zig"); |
| 12 | const Package = @import("Package.zig"); | 13 | const Package = @import("Package.zig"); |
| 13 | const link = @import("link.zig"); | 14 | const link = @import("link.zig"); |
| ... | @@ -352,6 +353,7 @@ pub const InitOptions = struct { | ... | @@ -352,6 +353,7 @@ pub const InitOptions = struct { |
| 352 | time_report: bool = false, | 353 | time_report: bool = false, |
| 353 | stack_report: bool = false, | 354 | stack_report: bool = false, |
| 354 | link_eh_frame_hdr: bool = false, | 355 | link_eh_frame_hdr: bool = false, |
| 356 | link_emit_relocs: bool = false, | ||
| 355 | linker_script: ?[]const u8 = null, | 357 | linker_script: ?[]const u8 = null, |
| 356 | version_script: ?[]const u8 = null, | 358 | version_script: ?[]const u8 = null, |
| 357 | override_soname: ?[]const u8 = null, | 359 | override_soname: ?[]const u8 = null, |
| ... | @@ -376,6 +378,7 @@ pub const InitOptions = struct { | ... | @@ -376,6 +378,7 @@ pub const InitOptions = struct { |
| 376 | is_compiler_rt_or_libc: bool = false, | 378 | is_compiler_rt_or_libc: bool = false, |
| 377 | parent_compilation_link_libc: bool = false, | 379 | parent_compilation_link_libc: bool = false, |
| 378 | stack_size_override: ?u64 = null, | 380 | stack_size_override: ?u64 = null, |
| 381 | image_base_override: ?u64 = null, | ||
| 379 | self_exe_path: ?[]const u8 = null, | 382 | self_exe_path: ?[]const u8 = null, |
| 380 | version: ?std.builtin.Version = null, | 383 | version: ?std.builtin.Version = null, |
| 381 | libc_installation: ?*const LibCInstallation = null, | 384 | libc_installation: ?*const LibCInstallation = null, |
| ... | @@ -447,8 +450,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -447,8 +450,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 447 | options.system_libs.len != 0 or | 450 | options.system_libs.len != 0 or |
| 448 | options.link_libc or options.link_libcpp or | 451 | options.link_libc or options.link_libcpp or |
| 449 | options.link_eh_frame_hdr or | 452 | options.link_eh_frame_hdr or |
| 453 | options.link_emit_relocs or | ||
| 450 | options.output_mode == .Lib or | 454 | options.output_mode == .Lib or |
| 451 | options.lld_argv.len != 0 or | 455 | options.lld_argv.len != 0 or |
| 456 | options.image_base_override != null or | ||
| 452 | options.linker_script != null or options.version_script != null) | 457 | options.linker_script != null or options.version_script != null) |
| 453 | { | 458 | { |
| 454 | break :blk true; | 459 | break :blk true; |
| ... | @@ -473,8 +478,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -473,8 +478,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 473 | { | 478 | { |
| 474 | break :dl true; | 479 | break :dl true; |
| 475 | } | 480 | } |
| 476 | if (options.system_libs.len != 0) | 481 | if (options.system_libs.len != 0) { |
| 477 | break :dl true; | 482 | // when creating a executable that links to system libraries, |
| 483 | // we require dynamic linking, but we must not link static libraries | ||
| 484 | // or object files dynamically! | ||
| 485 | break :dl (options.output_mode == .Exe); | ||
| 486 | } | ||
| 478 | 487 | ||
| 479 | break :dl false; | 488 | break :dl false; |
| 480 | }; | 489 | }; |
| ... | @@ -638,15 +647,19 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -638,15 +647,19 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 638 | 647 | ||
| 639 | const root_scope = rs: { | 648 | const root_scope = rs: { |
| 640 | if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) { | 649 | if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) { |
| 650 | const struct_payload = try gpa.create(Type.Payload.EmptyStruct); | ||
| 641 | const root_scope = try gpa.create(Module.Scope.File); | 651 | const root_scope = try gpa.create(Module.Scope.File); |
| 652 | struct_payload.* = .{ .scope = &root_scope.root_container }; | ||
| 642 | root_scope.* = .{ | 653 | root_scope.* = .{ |
| 643 | .sub_file_path = root_pkg.root_src_path, | 654 | // TODO this is duped so it can be freed in Container.deinit |
| 655 | .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path), | ||
| 644 | .source = .{ .unloaded = {} }, | 656 | .source = .{ .unloaded = {} }, |
| 645 | .contents = .{ .not_available = {} }, | 657 | .contents = .{ .not_available = {} }, |
| 646 | .status = .never_loaded, | 658 | .status = .never_loaded, |
| 647 | .root_container = .{ | 659 | .root_container = .{ |
| 648 | .file_scope = root_scope, | 660 | .file_scope = root_scope, |
| 649 | .decls = .{}, | 661 | .decls = .{}, |
| 662 | .ty = Type.initPayload(&struct_payload.base), | ||
| 650 | }, | 663 | }, |
| 651 | }; | 664 | }; |
| 652 | break :rs &root_scope.base; | 665 | break :rs &root_scope.base; |
| ... | @@ -765,10 +778,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -765,10 +778,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 765 | .z_nodelete = options.linker_z_nodelete, | 778 | .z_nodelete = options.linker_z_nodelete, |
| 766 | .z_defs = options.linker_z_defs, | 779 | .z_defs = options.linker_z_defs, |
| 767 | .stack_size_override = options.stack_size_override, | 780 | .stack_size_override = options.stack_size_override, |
| 781 | .image_base_override = options.image_base_override, | ||
| 768 | .linker_script = options.linker_script, | 782 | .linker_script = options.linker_script, |
| 769 | .version_script = options.version_script, | 783 | .version_script = options.version_script, |
| 770 | .gc_sections = options.linker_gc_sections, | 784 | .gc_sections = options.linker_gc_sections, |
| 771 | .eh_frame_hdr = options.link_eh_frame_hdr, | 785 | .eh_frame_hdr = options.link_eh_frame_hdr, |
| 786 | .emit_relocs = options.link_emit_relocs, | ||
| 772 | .rdynamic = options.rdynamic, | 787 | .rdynamic = options.rdynamic, |
| 773 | .extra_lld_args = options.lld_argv, | 788 | .extra_lld_args = options.lld_argv, |
| 774 | .override_soname = options.override_soname, | 789 | .override_soname = options.override_soname, |
| ... | @@ -785,7 +800,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -785,7 +800,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 785 | .llvm_cpu_features = llvm_cpu_features, | 800 | .llvm_cpu_features = llvm_cpu_features, |
| 786 | .is_compiler_rt_or_libc = options.is_compiler_rt_or_libc, | 801 | .is_compiler_rt_or_libc = options.is_compiler_rt_or_libc, |
| 787 | .parent_compilation_link_libc = options.parent_compilation_link_libc, | 802 | .parent_compilation_link_libc = options.parent_compilation_link_libc, |
| 788 | .each_lib_rpath = options.each_lib_rpath orelse false, | 803 | .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os, |
| 789 | .disable_lld_caching = options.disable_lld_caching, | 804 | .disable_lld_caching = options.disable_lld_caching, |
| 790 | .subsystem = options.subsystem, | 805 | .subsystem = options.subsystem, |
| 791 | .is_test = options.is_test, | 806 | .is_test = options.is_test, |
| ... | @@ -1022,6 +1037,17 @@ pub fn update(self: *Compilation) !void { | ... | @@ -1022,6 +1037,17 @@ pub fn update(self: *Compilation) !void { |
| 1022 | else => |e| return e, | 1037 | else => |e| return e, |
| 1023 | }; | 1038 | }; |
| 1024 | } | 1039 | } |
| 1040 | |||
| 1041 | // TODO only analyze imports if they are still referenced | ||
| 1042 | for (module.import_table.items()) |entry| { | ||
| 1043 | entry.value.unload(module.gpa); | ||
| 1044 | module.analyzeContainer(&entry.value.root_container) catch |err| switch (err) { | ||
| 1045 | error.AnalysisFail => { | ||
| 1046 | assert(self.totalErrorCount() != 0); | ||
| 1047 | }, | ||
| 1048 | else => |e| return e, | ||
| 1049 | }; | ||
| 1050 | } | ||
| 1025 | } | 1051 | } |
| 1026 | } | 1052 | } |
| 1027 | 1053 | ||
| ... | @@ -1146,7 +1172,15 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | ... | @@ -1146,7 +1172,15 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 1146 | }; | 1172 | }; |
| 1147 | } | 1173 | } |
| 1148 | 1174 | ||
| 1149 | pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { | 1175 | pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void { |
| 1176 | var progress: std.Progress = .{}; | ||
| 1177 | var main_progress_node = try progress.start("", null); | ||
| 1178 | defer main_progress_node.end(); | ||
| 1179 | if (self.color == .Off) progress.terminal = null; | ||
| 1180 | |||
| 1181 | var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len); | ||
| 1182 | defer c_comp_progress_node.end(); | ||
| 1183 | |||
| 1150 | while (self.work_queue.readItem()) |work_item| switch (work_item) { | 1184 | while (self.work_queue.readItem()) |work_item| switch (work_item) { |
| 1151 | .codegen_decl => |decl| switch (decl.analysis) { | 1185 | .codegen_decl => |decl| switch (decl.analysis) { |
| 1152 | .unreferenced => unreachable, | 1186 | .unreferenced => unreachable, |
| ... | @@ -1223,7 +1257,7 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { | ... | @@ -1223,7 +1257,7 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { |
| 1223 | }; | 1257 | }; |
| 1224 | }, | 1258 | }, |
| 1225 | .c_object => |c_object| { | 1259 | .c_object => |c_object| { |
| 1226 | self.updateCObject(c_object) catch |err| switch (err) { | 1260 | self.updateCObject(c_object, &c_comp_progress_node) catch |err| switch (err) { |
| 1227 | error.AnalysisFail => continue, | 1261 | error.AnalysisFail => continue, |
| 1228 | else => { | 1262 | else => { |
| 1229 | try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1); | 1263 | try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1); |
| ... | @@ -1309,7 +1343,7 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { | ... | @@ -1309,7 +1343,7 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { |
| 1309 | if (!build_options.is_stage1) | 1343 | if (!build_options.is_stage1) |
| 1310 | unreachable; | 1344 | unreachable; |
| 1311 | 1345 | ||
| 1312 | self.updateStage1Module() catch |err| { | 1346 | self.updateStage1Module(main_progress_node) catch |err| { |
| 1313 | fatal("unable to build stage1 zig object: {}", .{@errorName(err)}); | 1347 | fatal("unable to build stage1 zig object: {}", .{@errorName(err)}); |
| 1314 | }; | 1348 | }; |
| 1315 | }, | 1349 | }, |
| ... | @@ -1470,7 +1504,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { | ... | @@ -1470,7 +1504,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { |
| 1470 | }; | 1504 | }; |
| 1471 | } | 1505 | } |
| 1472 | 1506 | ||
| 1473 | fn updateCObject(comp: *Compilation, c_object: *CObject) !void { | 1507 | fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *std.Progress.Node) !void { |
| 1474 | if (!build_options.have_llvm) { | 1508 | if (!build_options.have_llvm) { |
| 1475 | return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{}); | 1509 | return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{}); |
| 1476 | } | 1510 | } |
| ... | @@ -1513,6 +1547,12 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void { | ... | @@ -1513,6 +1547,12 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void { |
| 1513 | const arena = &arena_allocator.allocator; | 1547 | const arena = &arena_allocator.allocator; |
| 1514 | 1548 | ||
| 1515 | const c_source_basename = std.fs.path.basename(c_object.src.src_path); | 1549 | const c_source_basename = std.fs.path.basename(c_object.src.src_path); |
| 1550 | |||
| 1551 | c_comp_progress_node.activate(); | ||
| 1552 | var child_progress_node = c_comp_progress_node.start(c_source_basename, null); | ||
| 1553 | child_progress_node.activate(); | ||
| 1554 | defer child_progress_node.end(); | ||
| 1555 | |||
| 1516 | // Special case when doing build-obj for just one C file. When there are more than one object | 1556 | // Special case when doing build-obj for just one C file. When there are more than one object |
| 1517 | // file and building an object we need to link them together, but with just one it should go | 1557 | // file and building an object we need to link them together, but with just one it should go |
| 1518 | // directly to the output file. | 1558 | // directly to the output file. |
| ... | @@ -2506,7 +2546,7 @@ fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CR | ... | @@ -2506,7 +2546,7 @@ fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CR |
| 2506 | }; | 2546 | }; |
| 2507 | } | 2547 | } |
| 2508 | 2548 | ||
| 2509 | fn updateStage1Module(comp: *Compilation) !void { | 2549 | fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node) !void { |
| 2510 | const tracy = trace(@src()); | 2550 | const tracy = trace(@src()); |
| 2511 | defer tracy.end(); | 2551 | defer tracy.end(); |
| 2512 | 2552 | ||
| ... | @@ -2550,6 +2590,8 @@ fn updateStage1Module(comp: *Compilation) !void { | ... | @@ -2550,6 +2590,8 @@ fn updateStage1Module(comp: *Compilation) !void { |
| 2550 | man.hash.add(comp.emit_llvm_ir != null); | 2590 | man.hash.add(comp.emit_llvm_ir != null); |
| 2551 | man.hash.add(comp.emit_analysis != null); | 2591 | man.hash.add(comp.emit_analysis != null); |
| 2552 | man.hash.add(comp.emit_docs != null); | 2592 | man.hash.add(comp.emit_docs != null); |
| 2593 | man.hash.addOptionalBytes(comp.test_filter); | ||
| 2594 | man.hash.addOptionalBytes(comp.test_name_prefix); | ||
| 2553 | 2595 | ||
| 2554 | // Capture the state in case we come back from this branch where the hash doesn't match. | 2596 | // Capture the state in case we come back from this branch where the hash doesn't match. |
| 2555 | const prev_hash_state = man.hash.peekBin(); | 2597 | const prev_hash_state = man.hash.peekBin(); |
| ... | @@ -2612,10 +2654,6 @@ fn updateStage1Module(comp: *Compilation) !void { | ... | @@ -2612,10 +2654,6 @@ fn updateStage1Module(comp: *Compilation) !void { |
| 2612 | .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null, | 2654 | .llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null, |
| 2613 | .llvm_cpu_features = comp.bin_file.options.llvm_cpu_features.?, | 2655 | .llvm_cpu_features = comp.bin_file.options.llvm_cpu_features.?, |
| 2614 | }; | 2656 | }; |
| 2615 | var progress: std.Progress = .{}; | ||
| 2616 | var main_progress_node = try progress.start("", null); | ||
| 2617 | defer main_progress_node.end(); | ||
| 2618 | if (comp.color == .Off) progress.terminal = null; | ||
| 2619 | 2657 | ||
| 2620 | comp.stage1_cache_manifest = &man; | 2658 | comp.stage1_cache_manifest = &man; |
| 2621 | 2659 |
src/Module.zig+52-4| ... | @@ -70,6 +70,9 @@ deletion_set: ArrayListUnmanaged(*Decl) = .{}, | ... | @@ -70,6 +70,9 @@ deletion_set: ArrayListUnmanaged(*Decl) = .{}, |
| 70 | /// Error tags and their values, tag names are duped with mod.gpa. | 70 | /// Error tags and their values, tag names are duped with mod.gpa. |
| 71 | global_error_set: std.StringHashMapUnmanaged(u16) = .{}, | 71 | global_error_set: std.StringHashMapUnmanaged(u16) = .{}, |
| 72 | 72 | ||
| 73 | /// Keys are fully qualified paths | ||
| 74 | import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, | ||
| 75 | |||
| 73 | /// Incrementing integer used to compare against the corresponding Decl | 76 | /// Incrementing integer used to compare against the corresponding Decl |
| 74 | /// field to determine whether a Decl's status applies to an ongoing update, or a | 77 | /// field to determine whether a Decl's status applies to an ongoing update, or a |
| 75 | /// previous analysis. | 78 | /// previous analysis. |
| ... | @@ -208,7 +211,7 @@ pub const Decl = struct { | ... | @@ -208,7 +211,7 @@ pub const Decl = struct { |
| 208 | .container => { | 211 | .container => { |
| 209 | const container = @fieldParentPtr(Scope.Container, "base", self.scope); | 212 | const container = @fieldParentPtr(Scope.Container, "base", self.scope); |
| 210 | const tree = container.file_scope.contents.tree; | 213 | const tree = container.file_scope.contents.tree; |
| 211 | // TODO Container should have it's own decls() | 214 | // TODO Container should have its own decls() |
| 212 | const decl_node = tree.root_node.decls()[self.src_index]; | 215 | const decl_node = tree.root_node.decls()[self.src_index]; |
| 213 | return tree.token_locs[decl_node.firstToken()].start; | 216 | return tree.token_locs[decl_node.firstToken()].start; |
| 214 | }, | 217 | }, |
| ... | @@ -532,12 +535,13 @@ pub const Scope = struct { | ... | @@ -532,12 +535,13 @@ pub const Scope = struct { |
| 532 | 535 | ||
| 533 | /// Direct children of the file. | 536 | /// Direct children of the file. |
| 534 | decls: std.AutoArrayHashMapUnmanaged(*Decl, void), | 537 | decls: std.AutoArrayHashMapUnmanaged(*Decl, void), |
| 535 | 538 | ty: Type, | |
| 536 | // TODO implement container types and put this in a status union | ||
| 537 | // ty: Type | ||
| 538 | 539 | ||
| 539 | pub fn deinit(self: *Container, gpa: *Allocator) void { | 540 | pub fn deinit(self: *Container, gpa: *Allocator) void { |
| 540 | self.decls.deinit(gpa); | 541 | self.decls.deinit(gpa); |
| 542 | // TODO either Container of File should have an arena for sub_file_path and ty | ||
| 543 | gpa.destroy(self.ty.cast(Type.Payload.EmptyStruct).?); | ||
| 544 | gpa.free(self.file_scope.sub_file_path); | ||
| 541 | self.* = undefined; | 545 | self.* = undefined; |
| 542 | } | 546 | } |
| 543 | 547 | ||
| ... | @@ -854,6 +858,11 @@ pub fn deinit(self: *Module) void { | ... | @@ -854,6 +858,11 @@ pub fn deinit(self: *Module) void { |
| 854 | gpa.free(entry.key); | 858 | gpa.free(entry.key); |
| 855 | } | 859 | } |
| 856 | self.global_error_set.deinit(gpa); | 860 | self.global_error_set.deinit(gpa); |
| 861 | |||
| 862 | for (self.import_table.items()) |entry| { | ||
| 863 | entry.value.base.destroy(gpa); | ||
| 864 | } | ||
| 865 | self.import_table.deinit(gpa); | ||
| 857 | } | 866 | } |
| 858 | 867 | ||
| 859 | fn freeExportList(gpa: *Allocator, export_list: []*Export) void { | 868 | fn freeExportList(gpa: *Allocator, export_list: []*Export) void { |
| ... | @@ -2381,6 +2390,45 @@ pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, | ... | @@ -2381,6 +2390,45 @@ pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, |
| 2381 | return self.fail(scope, src, "TODO implement analysis of slice", .{}); | 2390 | return self.fail(scope, src, "TODO implement analysis of slice", .{}); |
| 2382 | } | 2391 | } |
| 2383 | 2392 | ||
| 2393 | pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: []const u8) !*Scope.File { | ||
| 2394 | // TODO if (package_table.get(target_string)) |pkg| | ||
| 2395 | if (self.import_table.get(target_string)) |some| { | ||
| 2396 | return some; | ||
| 2397 | } | ||
| 2398 | |||
| 2399 | // TODO check for imports outside of pkg path | ||
| 2400 | if (false) return error.ImportOutsidePkgPath; | ||
| 2401 | |||
| 2402 | // TODO Scope.Container arena for ty and sub_file_path | ||
| 2403 | const struct_payload = try self.gpa.create(Type.Payload.EmptyStruct); | ||
| 2404 | errdefer self.gpa.destroy(struct_payload); | ||
| 2405 | const file_scope = try self.gpa.create(Scope.File); | ||
| 2406 | errdefer self.gpa.destroy(file_scope); | ||
| 2407 | const file_path = try self.gpa.dupe(u8, target_string); | ||
| 2408 | errdefer self.gpa.free(file_path); | ||
| 2409 | |||
| 2410 | struct_payload.* = .{ .scope = &file_scope.root_container }; | ||
| 2411 | file_scope.* = .{ | ||
| 2412 | .sub_file_path = file_path, | ||
| 2413 | .source = .{ .unloaded = {} }, | ||
| 2414 | .contents = .{ .not_available = {} }, | ||
| 2415 | .status = .never_loaded, | ||
| 2416 | .root_container = .{ | ||
| 2417 | .file_scope = file_scope, | ||
| 2418 | .decls = .{}, | ||
| 2419 | .ty = Type.initPayload(&struct_payload.base), | ||
| 2420 | }, | ||
| 2421 | }; | ||
| 2422 | self.analyzeContainer(&file_scope.root_container) catch |err| switch (err) { | ||
| 2423 | error.AnalysisFail => { | ||
| 2424 | assert(self.comp.totalErrorCount() != 0); | ||
| 2425 | }, | ||
| 2426 | else => |e| return e, | ||
| 2427 | }; | ||
| 2428 | try self.import_table.put(self.gpa, file_scope.sub_file_path, file_scope); | ||
| 2429 | return file_scope; | ||
| 2430 | } | ||
| 2431 | |||
| 2384 | /// Asserts that lhs and rhs types are both numeric. | 2432 | /// Asserts that lhs and rhs types are both numeric. |
| 2385 | pub fn cmpNumeric( | 2433 | pub fn cmpNumeric( |
| 2386 | self: *Module, | 2434 | self: *Module, |
src/astgen.zig+11| ... | @@ -1973,6 +1973,15 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa | ... | @@ -1973,6 +1973,15 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa |
| 1973 | } | 1973 | } |
| 1974 | } | 1974 | } |
| 1975 | 1975 | ||
| 1976 | fn import(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | ||
| 1977 | try ensureBuiltinParamCount(mod, scope, call, 1); | ||
| 1978 | const tree = scope.tree(); | ||
| 1979 | const src = tree.token_locs[call.builtin_token].start; | ||
| 1980 | const params = call.params(); | ||
| 1981 | const target = try expr(mod, scope, .none, params[0]); | ||
| 1982 | return addZIRUnOp(mod, scope, src, .import, target); | ||
| 1983 | } | ||
| 1984 | |||
| 1976 | fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | 1985 | fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { |
| 1977 | const tree = scope.tree(); | 1986 | const tree = scope.tree(); |
| 1978 | const builtin_name = tree.tokenSlice(call.builtin_token); | 1987 | const builtin_name = tree.tokenSlice(call.builtin_token); |
| ... | @@ -1995,6 +2004,8 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built | ... | @@ -1995,6 +2004,8 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built |
| 1995 | } else if (mem.eql(u8, builtin_name, "@breakpoint")) { | 2004 | } else if (mem.eql(u8, builtin_name, "@breakpoint")) { |
| 1996 | const src = tree.token_locs[call.builtin_token].start; | 2005 | const src = tree.token_locs[call.builtin_token].start; |
| 1997 | return rlWrap(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint)); | 2006 | return rlWrap(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint)); |
| 2007 | } else if (mem.eql(u8, builtin_name, "@import")) { | ||
| 2008 | return rlWrap(mod, scope, rl, try import(mod, scope, call)); | ||
| 1998 | } else { | 2009 | } else { |
| 1999 | return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name}); | 2010 | return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name}); |
| 2000 | } | 2011 | } |
src/codegen.zig+175-42| ... | @@ -17,9 +17,6 @@ const DW = std.dwarf; | ... | @@ -17,9 +17,6 @@ const DW = std.dwarf; |
| 17 | const leb128 = std.debug.leb; | 17 | const leb128 = std.debug.leb; |
| 18 | const log = std.log.scoped(.codegen); | 18 | const log = std.log.scoped(.codegen); |
| 19 | 19 | ||
| 20 | // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. | ||
| 21 | // zig fmt: off | ||
| 22 | |||
| 23 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. | 20 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. |
| 24 | pub const BlockData = struct { | 21 | pub const BlockData = struct { |
| 25 | relocs: std.ArrayListUnmanaged(Reloc) = undefined, | 22 | relocs: std.ArrayListUnmanaged(Reloc) = undefined, |
| ... | @@ -35,7 +32,7 @@ pub const BlockData = struct { | ... | @@ -35,7 +32,7 @@ pub const BlockData = struct { |
| 35 | /// comptime assert that makes sure we guessed correctly about the size. This only | 32 | /// comptime assert that makes sure we guessed correctly about the size. This only |
| 36 | /// exists so that we can bitcast an arch-independent field to and from the real MCValue. | 33 | /// exists so that we can bitcast an arch-independent field to and from the real MCValue. |
| 37 | pub const AnyMCValue = extern struct { | 34 | pub const AnyMCValue = extern struct { |
| 38 | a: u64, | 35 | a: usize, |
| 39 | b: u64, | 36 | b: u64, |
| 40 | }; | 37 | }; |
| 41 | 38 | ||
| ... | @@ -170,7 +167,6 @@ pub fn generateSymbol( | ... | @@ -170,7 +167,6 @@ pub fn generateSymbol( |
| 170 | }, | 167 | }, |
| 171 | .Pointer => { | 168 | .Pointer => { |
| 172 | // TODO populate .debug_info for the pointer | 169 | // TODO populate .debug_info for the pointer |
| 173 | |||
| 174 | if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| { | 170 | if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| { |
| 175 | const decl = payload.decl; | 171 | const decl = payload.decl; |
| 176 | if (decl.analysis != .complete) return error.AnalysisFail; | 172 | if (decl.analysis != .complete) return error.AnalysisFail; |
| ... | @@ -206,7 +202,6 @@ pub fn generateSymbol( | ... | @@ -206,7 +202,6 @@ pub fn generateSymbol( |
| 206 | }, | 202 | }, |
| 207 | .Int => { | 203 | .Int => { |
| 208 | // TODO populate .debug_info for the integer | 204 | // TODO populate .debug_info for the integer |
| 209 | |||
| 210 | const info = typed_value.ty.intInfo(bin_file.options.target); | 205 | const info = typed_value.ty.intInfo(bin_file.options.target); |
| 211 | if (info.bits == 8 and !info.signed) { | 206 | if (info.bits == 8 and !info.signed) { |
| 212 | const x = typed_value.val.toUnsignedInt(); | 207 | const x = typed_value.val.toUnsignedInt(); |
| ... | @@ -399,7 +394,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -399,7 +394,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 399 | self.free_registers &= ~(@as(FreeRegInt, 1) << free_index); | 394 | self.free_registers &= ~(@as(FreeRegInt, 1) << free_index); |
| 400 | const reg = callee_preserved_regs[free_index]; | 395 | const reg = callee_preserved_regs[free_index]; |
| 401 | self.registers.putAssumeCapacityNoClobber(reg, inst); | 396 | self.registers.putAssumeCapacityNoClobber(reg, inst); |
| 402 | log.debug("alloc {} => {*}", .{reg, inst}); | 397 | log.debug("alloc {} => {*}", .{ reg, inst }); |
| 403 | return reg; | 398 | return reg; |
| 404 | } | 399 | } |
| 405 | 400 | ||
| ... | @@ -439,7 +434,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -439,7 +434,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 439 | } | 434 | } |
| 440 | try branch_stack.append(.{}); | 435 | try branch_stack.append(.{}); |
| 441 | 436 | ||
| 442 | const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: { | 437 | const src_data: struct { lbrace_src: usize, rbrace_src: usize, source: []const u8 } = blk: { |
| 443 | if (module_fn.owner_decl.scope.cast(Module.Scope.Container)) |container_scope| { | 438 | if (module_fn.owner_decl.scope.cast(Module.Scope.Container)) |container_scope| { |
| 444 | const tree = container_scope.file_scope.contents.tree; | 439 | const tree = container_scope.file_scope.contents.tree; |
| 445 | const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?; | 440 | const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?; |
| ... | @@ -570,6 +565,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -570,6 +565,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 570 | try self.dbgSetEpilogueBegin(); | 565 | try self.dbgSetEpilogueBegin(); |
| 571 | } | 566 | } |
| 572 | }, | 567 | }, |
| 568 | .arm => { | ||
| 569 | const cc = self.fn_type.fnCallingConvention(); | ||
| 570 | if (cc != .Naked) { | ||
| 571 | // push {fp, lr} | ||
| 572 | // mov fp, sp | ||
| 573 | // sub sp, sp, #reloc | ||
| 574 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.push(.al, .{ .fp, .lr }).toU32()); | ||
| 575 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .fp, Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none)).toU32()); | ||
| 576 | // TODO: prepare stack for local variables | ||
| 577 | // const backpatch_reloc = try self.code.addManyAsArray(4); | ||
| 578 | |||
| 579 | try self.dbgSetPrologueEnd(); | ||
| 580 | |||
| 581 | try self.genBody(self.mod_fn.analysis.success); | ||
| 582 | |||
| 583 | // Backpatch stack offset | ||
| 584 | // const stack_end = self.max_end_stack; | ||
| 585 | // const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); | ||
| 586 | // mem.writeIntLittle(u32, backpatch_reloc, Instruction.sub(.al, .sp, .sp, Instruction.Operand.imm())); | ||
| 587 | |||
| 588 | try self.dbgSetEpilogueBegin(); | ||
| 589 | |||
| 590 | // mov sp, fp | ||
| 591 | // pop {fp, pc} | ||
| 592 | // TODO: return by jumping to this code, use relocations | ||
| 593 | // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32()); | ||
| 594 | // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32()); | ||
| 595 | } else { | ||
| 596 | try self.dbgSetPrologueEnd(); | ||
| 597 | try self.genBody(self.mod_fn.analysis.success); | ||
| 598 | try self.dbgSetEpilogueBegin(); | ||
| 599 | } | ||
| 600 | }, | ||
| 573 | else => { | 601 | else => { |
| 574 | try self.dbgSetPrologueEnd(); | 602 | try self.dbgSetPrologueEnd(); |
| 575 | try self.genBody(self.mod_fn.analysis.success); | 603 | try self.genBody(self.mod_fn.analysis.success); |
| ... | @@ -586,7 +614,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -586,7 +614,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 586 | 614 | ||
| 587 | const mcv = try self.genFuncInst(inst); | 615 | const mcv = try self.genFuncInst(inst); |
| 588 | if (!inst.isUnused()) { | 616 | if (!inst.isUnused()) { |
| 589 | log.debug("{*} => {}", .{inst, mcv}); | 617 | log.debug("{*} => {}", .{ inst, mcv }); |
| 590 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 618 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 591 | try branch.inst_table.putNoClobber(self.gpa, inst, mcv); | 619 | try branch.inst_table.putNoClobber(self.gpa, inst, mcv); |
| 592 | } | 620 | } |
| ... | @@ -851,7 +879,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -851,7 +879,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 851 | // No side effects, so if it's unreferenced, do nothing. | 879 | // No side effects, so if it's unreferenced, do nothing. |
| 852 | if (inst.base.isUnused()) | 880 | if (inst.base.isUnused()) |
| 853 | return MCValue.dead; | 881 | return MCValue.dead; |
| 854 | 882 | ||
| 855 | const operand = try self.resolveInst(inst.operand); | 883 | const operand = try self.resolveInst(inst.operand); |
| 856 | const info_a = inst.operand.ty.intInfo(self.target.*); | 884 | const info_a = inst.operand.ty.intInfo(self.target.*); |
| 857 | const info_b = inst.base.ty.intInfo(self.target.*); | 885 | const info_b = inst.base.ty.intInfo(self.target.*); |
| ... | @@ -972,10 +1000,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -972,10 +1000,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 972 | if (self.registers.getEntry(toCanonicalReg(reg))) |entry| { | 1000 | if (self.registers.getEntry(toCanonicalReg(reg))) |entry| { |
| 973 | entry.value = inst; | 1001 | entry.value = inst; |
| 974 | } | 1002 | } |
| 975 | log.debug("reusing {} => {*}", .{reg, inst}); | 1003 | log.debug("reusing {} => {*}", .{ reg, inst }); |
| 976 | }, | 1004 | }, |
| 977 | .stack_offset => |off| { | 1005 | .stack_offset => |off| { |
| 978 | log.debug("reusing stack offset {} => {*}", .{off, inst}); | 1006 | log.debug("reusing stack offset {} => {*}", .{ off, inst }); |
| 979 | return true; | 1007 | return true; |
| 980 | }, | 1008 | }, |
| 981 | else => return false, | 1009 | else => return false, |
| ... | @@ -1274,7 +1302,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1274,7 +1302,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1274 | const result = self.args[self.arg_index]; | 1302 | const result = self.args[self.arg_index]; |
| 1275 | self.arg_index += 1; | 1303 | self.arg_index += 1; |
| 1276 | 1304 | ||
| 1277 | const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1]; | 1305 | const name_with_null = inst.name[0 .. mem.lenZ(inst.name) + 1]; |
| 1278 | switch (result) { | 1306 | switch (result) { |
| 1279 | .register => |reg| { | 1307 | .register => |reg| { |
| 1280 | self.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), &inst.base); | 1308 | self.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), &inst.base); |
| ... | @@ -1461,7 +1489,35 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1461,7 +1489,35 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1461 | } | 1489 | } |
| 1462 | }, | 1490 | }, |
| 1463 | .arm => { | 1491 | .arm => { |
| 1464 | if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch}); | 1492 | for (info.args) |mc_arg, arg_i| { |
| 1493 | const arg = inst.args[arg_i]; | ||
| 1494 | const arg_mcv = try self.resolveInst(inst.args[arg_i]); | ||
| 1495 | |||
| 1496 | switch (mc_arg) { | ||
| 1497 | .none => continue, | ||
| 1498 | .undef => unreachable, | ||
| 1499 | .immediate => unreachable, | ||
| 1500 | .unreach => unreachable, | ||
| 1501 | .dead => unreachable, | ||
| 1502 | .embedded_in_code => unreachable, | ||
| 1503 | .memory => unreachable, | ||
| 1504 | .compare_flags_signed => unreachable, | ||
| 1505 | .compare_flags_unsigned => unreachable, | ||
| 1506 | .register => |reg| { | ||
| 1507 | try self.genSetReg(arg.src, reg, arg_mcv); | ||
| 1508 | // TODO interact with the register allocator to mark the instruction as moved. | ||
| 1509 | }, | ||
| 1510 | .stack_offset => { | ||
| 1511 | return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{}); | ||
| 1512 | }, | ||
| 1513 | .ptr_stack_offset => { | ||
| 1514 | return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{}); | ||
| 1515 | }, | ||
| 1516 | .ptr_embedded_in_code => { | ||
| 1517 | return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); | ||
| 1518 | }, | ||
| 1519 | } | ||
| 1520 | } | ||
| 1465 | 1521 | ||
| 1466 | if (inst.func.cast(ir.Inst.Constant)) |func_inst| { | 1522 | if (inst.func.cast(ir.Inst.Constant)) |func_inst| { |
| 1467 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { | 1523 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { |
| ... | @@ -1476,13 +1532,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1476,13 +1532,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1476 | else | 1532 | else |
| 1477 | unreachable; | 1533 | unreachable; |
| 1478 | 1534 | ||
| 1479 | // TODO only works with leaf functions | ||
| 1480 | // at the moment, which works fine for | ||
| 1481 | // Hello World, but not for real code | ||
| 1482 | // of course. Add pushing lr to stack | ||
| 1483 | // and popping after call | ||
| 1484 | try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr }); | 1535 | try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr }); |
| 1485 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32()); | 1536 | |
| 1537 | // TODO: add Instruction.supportedOn | ||
| 1538 | // function for ARM | ||
| 1539 | if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) { | ||
| 1540 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32()); | ||
| 1541 | } else { | ||
| 1542 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .lr, Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none)).toU32()); | ||
| 1543 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32()); | ||
| 1544 | } | ||
| 1486 | } else { | 1545 | } else { |
| 1487 | return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{}); | 1546 | return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{}); |
| 1488 | } | 1547 | } |
| ... | @@ -1532,12 +1591,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1532,12 +1591,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1532 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { | 1591 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { |
| 1533 | const func = func_val.func; | 1592 | const func = func_val.func; |
| 1534 | const got = &macho_file.sections.items[macho_file.got_section_index.?]; | 1593 | const got = &macho_file.sections.items[macho_file.got_section_index.?]; |
| 1535 | const ptr_bytes = 8; | 1594 | const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64); |
| 1536 | const got_addr = @intCast(u32, got.addr + func.owner_decl.link.macho.offset_table_index.? * ptr_bytes); | 1595 | // Here, we store the got address in %rax, and then call %rax |
| 1537 | // ff 14 25 xx xx xx xx call [addr] | 1596 | // movabsq [addr], %rax |
| 1538 | try self.code.ensureCapacity(self.code.items.len + 7); | 1597 | try self.genSetReg(inst.base.src, .rax, .{ .memory = got_addr }); |
| 1539 | self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 }); | 1598 | // callq *%rax |
| 1540 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr); | 1599 | try self.code.ensureCapacity(self.code.items.len + 2); |
| 1600 | self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 }); | ||
| 1541 | } else { | 1601 | } else { |
| 1542 | return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{}); | 1602 | return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{}); |
| 1543 | } | 1603 | } |
| ... | @@ -1601,7 +1661,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1601,7 +1661,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1601 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32()); | 1661 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32()); |
| 1602 | }, | 1662 | }, |
| 1603 | .arm => { | 1663 | .arm => { |
| 1604 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32()); | 1664 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32()); |
| 1665 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32()); | ||
| 1666 | // TODO: jump to the end with relocation | ||
| 1667 | // // Just add space for an instruction, patch this later | ||
| 1668 | // try self.code.resize(self.code.items.len + 4); | ||
| 1669 | // try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4); | ||
| 1605 | }, | 1670 | }, |
| 1606 | else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), | 1671 | else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), |
| 1607 | } | 1672 | } |
| ... | @@ -1709,7 +1774,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1709,7 +1774,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1709 | self.code.items.len += 4; | 1774 | self.code.items.len += 4; |
| 1710 | break :reloc reloc; | 1775 | break :reloc reloc; |
| 1711 | }, | 1776 | }, |
| 1712 | else => return self.fail(inst.base.src, "TODO implement condbr {}", .{ self.target.cpu.arch }), | 1777 | else => return self.fail(inst.base.src, "TODO implement condbr {}", .{self.target.cpu.arch}), |
| 1713 | }; | 1778 | }; |
| 1714 | 1779 | ||
| 1715 | // Capture the state of register and stack allocation state so that we can revert to it. | 1780 | // Capture the state of register and stack allocation state so that we can revert to it. |
| ... | @@ -1789,7 +1854,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1789,7 +1854,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1789 | } | 1854 | } |
| 1790 | } | 1855 | } |
| 1791 | }; | 1856 | }; |
| 1792 | log.debug("consolidating else_entry {*} {}=>{}", .{else_entry.key, else_entry.value, canon_mcv}); | 1857 | log.debug("consolidating else_entry {*} {}=>{}", .{ else_entry.key, else_entry.value, canon_mcv }); |
| 1793 | // TODO make sure the destination stack offset / register does not already have something | 1858 | // TODO make sure the destination stack offset / register does not already have something |
| 1794 | // going on there. | 1859 | // going on there. |
| 1795 | try self.setRegOrMem(inst.base.src, else_entry.key.ty, canon_mcv, else_entry.value); | 1860 | try self.setRegOrMem(inst.base.src, else_entry.key.ty, canon_mcv, else_entry.value); |
| ... | @@ -1813,7 +1878,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1813,7 +1878,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1813 | } | 1878 | } |
| 1814 | } | 1879 | } |
| 1815 | }; | 1880 | }; |
| 1816 | log.debug("consolidating then_entry {*} {}=>{}", .{then_entry.key, parent_mcv, then_entry.value}); | 1881 | log.debug("consolidating then_entry {*} {}=>{}", .{ then_entry.key, parent_mcv, then_entry.value }); |
| 1817 | // TODO make sure the destination stack offset / register does not already have something | 1882 | // TODO make sure the destination stack offset / register does not already have something |
| 1818 | // going on there. | 1883 | // going on there. |
| 1819 | try self.setRegOrMem(inst.base.src, then_entry.key.ty, parent_mcv, then_entry.value); | 1884 | try self.setRegOrMem(inst.base.src, then_entry.key.ty, parent_mcv, then_entry.value); |
| ... | @@ -1880,7 +1945,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1880,7 +1945,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1880 | // break instruction will choose a MCValue for the block result and overwrite | 1945 | // break instruction will choose a MCValue for the block result and overwrite |
| 1881 | // this field. Following break instructions will use that MCValue to put their | 1946 | // this field. Following break instructions will use that MCValue to put their |
| 1882 | // block results. | 1947 | // block results. |
| 1883 | .mcv = @bitCast(AnyMCValue, MCValue { .none = {} }), | 1948 | .mcv = @bitCast(AnyMCValue, MCValue{ .none = {} }), |
| 1884 | }; | 1949 | }; |
| 1885 | defer inst.codegen.relocs.deinit(self.gpa); | 1950 | defer inst.codegen.relocs.deinit(self.gpa); |
| 1886 | 1951 | ||
| ... | @@ -2162,7 +2227,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2162,7 +2227,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2162 | mem.writeIntLittle(u64, &buf, x_big); | 2227 | mem.writeIntLittle(u64, &buf, x_big); |
| 2163 | 2228 | ||
| 2164 | // mov DWORD PTR [rbp+offset+4], immediate | 2229 | // mov DWORD PTR [rbp+offset+4], immediate |
| 2165 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp + 4}); | 2230 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp + 4 }); |
| 2166 | self.code.appendSliceAssumeCapacity(buf[4..8]); | 2231 | self.code.appendSliceAssumeCapacity(buf[4..8]); |
| 2167 | 2232 | ||
| 2168 | // mov DWORD PTR [rbp+offset], immediate | 2233 | // mov DWORD PTR [rbp+offset], immediate |
| ... | @@ -2213,14 +2278,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2213,14 +2278,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2213 | // least amount of necessary instructions (use | 2278 | // least amount of necessary instructions (use |
| 2214 | // more intelligent rotating) | 2279 | // more intelligent rotating) |
| 2215 | if (x <= math.maxInt(u8)) { | 2280 | if (x <= math.maxInt(u8)) { |
| 2216 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); | 2281 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); |
| 2217 | return; | 2282 | return; |
| 2218 | } else if (x <= math.maxInt(u16)) { | 2283 | } else if (x <= math.maxInt(u16)) { |
| 2219 | // TODO Use movw Note: Not supported on | 2284 | // TODO Use movw Note: Not supported on |
| 2220 | // all ARM targets! | 2285 | // all ARM targets! |
| 2221 | 2286 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); | |
| 2222 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); | 2287 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); |
| 2223 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); | ||
| 2224 | } else if (x <= math.maxInt(u32)) { | 2288 | } else if (x <= math.maxInt(u32)) { |
| 2225 | // TODO Use movw and movt Note: Not | 2289 | // TODO Use movw and movt Note: Not |
| 2226 | // supported on all ARM targets! Also TODO | 2290 | // supported on all ARM targets! Also TODO |
| ... | @@ -2232,20 +2296,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2232,20 +2296,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2232 | // orr reg, reg, #0xbb, 24 | 2296 | // orr reg, reg, #0xbb, 24 |
| 2233 | // orr reg, reg, #0xcc, 16 | 2297 | // orr reg, reg, #0xcc, 16 |
| 2234 | // orr reg, reg, #0xdd, 8 | 2298 | // orr reg, reg, #0xdd, 8 |
| 2235 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); | 2299 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); |
| 2236 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); | 2300 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); |
| 2237 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32()); | 2301 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32()); |
| 2238 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32()); | 2302 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32()); |
| 2239 | return; | 2303 | return; |
| 2240 | } else { | 2304 | } else { |
| 2241 | return self.fail(src, "ARM registers are 32-bit wide", .{}); | 2305 | return self.fail(src, "ARM registers are 32-bit wide", .{}); |
| 2242 | } | 2306 | } |
| 2243 | }, | 2307 | }, |
| 2308 | .register => |src_reg| { | ||
| 2309 | // If the registers are the same, nothing to do. | ||
| 2310 | if (src_reg.id() == reg.id()) | ||
| 2311 | return; | ||
| 2312 | |||
| 2313 | // mov reg, src_reg | ||
| 2314 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none)).toU32()); | ||
| 2315 | }, | ||
| 2244 | .memory => |addr| { | 2316 | .memory => |addr| { |
| 2245 | // The value is in memory at a hard-coded address. | 2317 | // The value is in memory at a hard-coded address. |
| 2246 | // If the type is a pointer, it means the pointer address is at this memory location. | 2318 | // If the type is a pointer, it means the pointer address is at this memory location. |
| 2247 | try self.genSetReg(src, reg, .{ .immediate = addr }); | 2319 | try self.genSetReg(src, reg, .{ .immediate = addr }); |
| 2248 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, Instruction.Offset.none).toU32()); | 2320 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32()); |
| 2249 | }, | 2321 | }, |
| 2250 | else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}), | 2322 | else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}), |
| 2251 | }, | 2323 | }, |
| ... | @@ -2590,7 +2662,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2590,7 +2662,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2590 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | 2662 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 2591 | const decl = payload.decl; | 2663 | const decl = payload.decl; |
| 2592 | const got = &macho_file.sections.items[macho_file.got_section_index.?]; | 2664 | const got = &macho_file.sections.items[macho_file.got_section_index.?]; |
| 2593 | const got_addr = got.addr + decl.link.macho.offset_table_index.? * ptr_bytes; | 2665 | const got_addr = got.addr + decl.link.macho.offset_table_index * ptr_bytes; |
| 2594 | return MCValue{ .memory = got_addr }; | 2666 | return MCValue{ .memory = got_addr }; |
| 2595 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | 2667 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 2596 | const decl = payload.decl; | 2668 | const decl = payload.decl; |
| ... | @@ -2701,6 +2773,55 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2701,6 +2773,55 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2701 | else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}), | 2773 | else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}), |
| 2702 | } | 2774 | } |
| 2703 | }, | 2775 | }, |
| 2776 | .arm => { | ||
| 2777 | switch (cc) { | ||
| 2778 | .Naked => { | ||
| 2779 | assert(result.args.len == 0); | ||
| 2780 | result.return_value = .{ .unreach = {} }; | ||
| 2781 | result.stack_byte_count = 0; | ||
| 2782 | result.stack_align = 1; | ||
| 2783 | return result; | ||
| 2784 | }, | ||
| 2785 | .Unspecified, .C => { | ||
| 2786 | // ARM Procedure Call Standard, Chapter 6.5 | ||
| 2787 | var ncrn: usize = 0; // Next Core Register Number | ||
| 2788 | var nsaa: u32 = 0; // Next stacked argument address | ||
| 2789 | |||
| 2790 | for (param_types) |ty, i| { | ||
| 2791 | if (ty.abiAlignment(self.target.*) == 8) { | ||
| 2792 | // Round up NCRN to the next even number | ||
| 2793 | ncrn += ncrn % 2; | ||
| 2794 | } | ||
| 2795 | |||
| 2796 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); | ||
| 2797 | if (std.math.divCeil(u32, param_size, 4) catch unreachable <= 4 - ncrn) { | ||
| 2798 | if (param_size <= 4) { | ||
| 2799 | result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] }; | ||
| 2800 | ncrn += 1; | ||
| 2801 | } else { | ||
| 2802 | return self.fail(src, "TODO MCValues with multiple registers", .{}); | ||
| 2803 | } | ||
| 2804 | } else if (ncrn < 4 and nsaa == 0) { | ||
| 2805 | return self.fail(src, "TODO MCValues split between registers and stack", .{}); | ||
| 2806 | } else { | ||
| 2807 | ncrn = 4; | ||
| 2808 | if (ty.abiAlignment(self.target.*) == 8) { | ||
| 2809 | if (nsaa % 8 != 0) { | ||
| 2810 | nsaa += 8 - (nsaa % 8); | ||
| 2811 | } | ||
| 2812 | } | ||
| 2813 | |||
| 2814 | result.args[i] = .{ .stack_offset = nsaa }; | ||
| 2815 | nsaa += param_size; | ||
| 2816 | } | ||
| 2817 | } | ||
| 2818 | |||
| 2819 | result.stack_byte_count = nsaa; | ||
| 2820 | result.stack_align = 4; | ||
| 2821 | }, | ||
| 2822 | else => return self.fail(src, "TODO implement function parameters for {} on arm", .{cc}), | ||
| 2823 | } | ||
| 2824 | }, | ||
| 2704 | else => if (param_types.len != 0) | 2825 | else => if (param_types.len != 0) |
| 2705 | return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}), | 2826 | return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}), |
| 2706 | } | 2827 | } |
| ... | @@ -2719,6 +2840,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2719,6 +2840,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2719 | }, | 2840 | }, |
| 2720 | else => return self.fail(src, "TODO implement function return values for {}", .{cc}), | 2841 | else => return self.fail(src, "TODO implement function return values for {}", .{cc}), |
| 2721 | }, | 2842 | }, |
| 2843 | .arm => switch (cc) { | ||
| 2844 | .Naked => unreachable, | ||
| 2845 | .Unspecified, .C => { | ||
| 2846 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | ||
| 2847 | if (ret_ty_size <= 4) { | ||
| 2848 | result.return_value = .{ .register = c_abi_int_return_regs[0] }; | ||
| 2849 | } else { | ||
| 2850 | return self.fail(src, "TODO support more return types for ARM backend", .{}); | ||
| 2851 | } | ||
| 2852 | }, | ||
| 2853 | else => return self.fail(src, "TODO implement function return values for {}", .{cc}), | ||
| 2854 | }, | ||
| 2722 | else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}), | 2855 | else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}), |
| 2723 | } | 2856 | } |
| 2724 | return result; | 2857 | return result; |
src/codegen/arm.zig+342-55| ... | @@ -113,6 +113,13 @@ test "Register.id" { | ... | @@ -113,6 +113,13 @@ test "Register.id" { |
| 113 | testing.expectEqual(@as(u4, 15), Register.pc.id()); | 113 | testing.expectEqual(@as(u4, 15), Register.pc.id()); |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | /// Program status registers containing flags, mode bits and other | ||
| 117 | /// vital information | ||
| 118 | pub const Psr = enum { | ||
| 119 | cpsr, | ||
| 120 | spsr, | ||
| 121 | }; | ||
| 122 | |||
| 116 | pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 }; | 123 | pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 }; |
| 117 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; | 124 | pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; |
| 118 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; | 125 | pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; |
| ... | @@ -135,15 +142,26 @@ pub const Instruction = union(enum) { | ... | @@ -135,15 +142,26 @@ pub const Instruction = union(enum) { |
| 135 | offset: u12, | 142 | offset: u12, |
| 136 | rd: u4, | 143 | rd: u4, |
| 137 | rn: u4, | 144 | rn: u4, |
| 138 | l: u1, | 145 | load_store: u1, |
| 139 | w: u1, | 146 | write_back: u1, |
| 140 | b: u1, | 147 | byte_word: u1, |
| 141 | u: u1, | 148 | up_down: u1, |
| 142 | p: u1, | 149 | pre_post: u1, |
| 143 | i: u1, | 150 | imm: u1, |
| 144 | fixed: u2 = 0b01, | 151 | fixed: u2 = 0b01, |
| 145 | cond: u4, | 152 | cond: u4, |
| 146 | }, | 153 | }, |
| 154 | BlockDataTransfer: packed struct { | ||
| 155 | register_list: u16, | ||
| 156 | rn: u4, | ||
| 157 | load_store: u1, | ||
| 158 | write_back: u1, | ||
| 159 | psr_or_user: u1, | ||
| 160 | up_down: u1, | ||
| 161 | pre_post: u1, | ||
| 162 | fixed: u3 = 0b100, | ||
| 163 | cond: u4, | ||
| 164 | }, | ||
| 147 | Branch: packed struct { | 165 | Branch: packed struct { |
| 148 | offset: u24, | 166 | offset: u24, |
| 149 | link: u1, | 167 | link: u1, |
| ... | @@ -235,14 +253,14 @@ pub const Instruction = union(enum) { | ... | @@ -235,14 +253,14 @@ pub const Instruction = union(enum) { |
| 235 | rs: u4, | 253 | rs: u4, |
| 236 | }, | 254 | }, |
| 237 | 255 | ||
| 238 | const Type = enum(u2) { | 256 | pub const Type = enum(u2) { |
| 239 | LogicalLeft, | 257 | logical_left, |
| 240 | LogicalRight, | 258 | logical_right, |
| 241 | ArithmeticRight, | 259 | arithmetic_right, |
| 242 | RotateRight, | 260 | rotate_right, |
| 243 | }; | 261 | }; |
| 244 | 262 | ||
| 245 | const none = Shift{ | 263 | pub const none = Shift{ |
| 246 | .Immediate = .{ | 264 | .Immediate = .{ |
| 247 | .amount = 0, | 265 | .amount = 0, |
| 248 | .typ = 0, | 266 | .typ = 0, |
| ... | @@ -338,10 +356,32 @@ pub const Instruction = union(enum) { | ... | @@ -338,10 +356,32 @@ pub const Instruction = union(enum) { |
| 338 | } | 356 | } |
| 339 | }; | 357 | }; |
| 340 | 358 | ||
| 359 | /// Represents the register list operand to a block data transfer | ||
| 360 | /// instruction | ||
| 361 | pub const RegisterList = packed struct { | ||
| 362 | r0: bool = false, | ||
| 363 | r1: bool = false, | ||
| 364 | r2: bool = false, | ||
| 365 | r3: bool = false, | ||
| 366 | r4: bool = false, | ||
| 367 | r5: bool = false, | ||
| 368 | r6: bool = false, | ||
| 369 | r7: bool = false, | ||
| 370 | r8: bool = false, | ||
| 371 | r9: bool = false, | ||
| 372 | r10: bool = false, | ||
| 373 | r11: bool = false, | ||
| 374 | r12: bool = false, | ||
| 375 | r13: bool = false, | ||
| 376 | r14: bool = false, | ||
| 377 | r15: bool = false, | ||
| 378 | }; | ||
| 379 | |||
| 341 | pub fn toU32(self: Instruction) u32 { | 380 | pub fn toU32(self: Instruction) u32 { |
| 342 | return switch (self) { | 381 | return switch (self) { |
| 343 | .DataProcessing => |v| @bitCast(u32, v), | 382 | .DataProcessing => |v| @bitCast(u32, v), |
| 344 | .SingleDataTransfer => |v| @bitCast(u32, v), | 383 | .SingleDataTransfer => |v| @bitCast(u32, v), |
| 384 | .BlockDataTransfer => |v| @bitCast(u32, v), | ||
| 345 | .Branch => |v| @bitCast(u32, v), | 385 | .Branch => |v| @bitCast(u32, v), |
| 346 | .BranchExchange => |v| @bitCast(u32, v), | 386 | .BranchExchange => |v| @bitCast(u32, v), |
| 347 | .SupervisorCall => |v| @bitCast(u32, v), | 387 | .SupervisorCall => |v| @bitCast(u32, v), |
| ... | @@ -362,7 +402,7 @@ pub const Instruction = union(enum) { | ... | @@ -362,7 +402,7 @@ pub const Instruction = union(enum) { |
| 362 | return Instruction{ | 402 | return Instruction{ |
| 363 | .DataProcessing = .{ | 403 | .DataProcessing = .{ |
| 364 | .cond = @enumToInt(cond), | 404 | .cond = @enumToInt(cond), |
| 365 | .i = if (op2 == .Immediate) 1 else 0, | 405 | .i = @boolToInt(op2 == .Immediate), |
| 366 | .opcode = @enumToInt(opcode), | 406 | .opcode = @enumToInt(opcode), |
| 367 | .s = s, | 407 | .s = s, |
| 368 | .rn = rn.id(), | 408 | .rn = rn.id(), |
| ... | @@ -377,10 +417,10 @@ pub const Instruction = union(enum) { | ... | @@ -377,10 +417,10 @@ pub const Instruction = union(enum) { |
| 377 | rd: Register, | 417 | rd: Register, |
| 378 | rn: Register, | 418 | rn: Register, |
| 379 | offset: Offset, | 419 | offset: Offset, |
| 380 | pre_post: u1, | 420 | pre_index: bool, |
| 381 | up_down: u1, | 421 | positive: bool, |
| 382 | byte_word: u1, | 422 | byte_word: u1, |
| 383 | writeback: u1, | 423 | write_back: bool, |
| 384 | load_store: u1, | 424 | load_store: u1, |
| 385 | ) Instruction { | 425 | ) Instruction { |
| 386 | return Instruction{ | 426 | return Instruction{ |
| ... | @@ -389,12 +429,36 @@ pub const Instruction = union(enum) { | ... | @@ -389,12 +429,36 @@ pub const Instruction = union(enum) { |
| 389 | .rn = rn.id(), | 429 | .rn = rn.id(), |
| 390 | .rd = rd.id(), | 430 | .rd = rd.id(), |
| 391 | .offset = offset.toU12(), | 431 | .offset = offset.toU12(), |
| 392 | .l = load_store, | 432 | .load_store = load_store, |
| 393 | .w = writeback, | 433 | .write_back = @boolToInt(write_back), |
| 394 | .b = byte_word, | 434 | .byte_word = byte_word, |
| 395 | .u = up_down, | 435 | .up_down = @boolToInt(positive), |
| 396 | .p = pre_post, | 436 | .pre_post = @boolToInt(pre_index), |
| 397 | .i = if (offset == .Immediate) 0 else 1, | 437 | .imm = @boolToInt(offset != .Immediate), |
| 438 | }, | ||
| 439 | }; | ||
| 440 | } | ||
| 441 | |||
| 442 | fn blockDataTransfer( | ||
| 443 | cond: Condition, | ||
| 444 | rn: Register, | ||
| 445 | reg_list: RegisterList, | ||
| 446 | pre_post: u1, | ||
| 447 | up_down: u1, | ||
| 448 | psr_or_user: u1, | ||
| 449 | write_back: bool, | ||
| 450 | load_store: u1, | ||
| 451 | ) Instruction { | ||
| 452 | return Instruction{ | ||
| 453 | .BlockDataTransfer = .{ | ||
| 454 | .register_list = @bitCast(u16, reg_list), | ||
| 455 | .rn = rn.id(), | ||
| 456 | .load_store = load_store, | ||
| 457 | .write_back = @boolToInt(write_back), | ||
| 458 | .psr_or_user = psr_or_user, | ||
| 459 | .up_down = up_down, | ||
| 460 | .pre_post = pre_post, | ||
| 461 | .cond = @enumToInt(cond), | ||
| 398 | }, | 462 | }, |
| 399 | }; | 463 | }; |
| 400 | } | 464 | } |
| ... | @@ -442,36 +506,68 @@ pub const Instruction = union(enum) { | ... | @@ -442,36 +506,68 @@ pub const Instruction = union(enum) { |
| 442 | 506 | ||
| 443 | // Data processing | 507 | // Data processing |
| 444 | 508 | ||
| 445 | pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | 509 | pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 446 | return dataProcessing(cond, .@"and", s, rd, rn, op2); | 510 | return dataProcessing(cond, .@"and", 0, rd, rn, op2); |
| 511 | } | ||
| 512 | |||
| 513 | pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 514 | return dataProcessing(cond, .@"and", 1, rd, rn, op2); | ||
| 515 | } | ||
| 516 | |||
| 517 | pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 518 | return dataProcessing(cond, .eor, 0, rd, rn, op2); | ||
| 519 | } | ||
| 520 | |||
| 521 | pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 522 | return dataProcessing(cond, .eor, 1, rd, rn, op2); | ||
| 523 | } | ||
| 524 | |||
| 525 | pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 526 | return dataProcessing(cond, .sub, 0, rd, rn, op2); | ||
| 527 | } | ||
| 528 | |||
| 529 | pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 530 | return dataProcessing(cond, .sub, 1, rd, rn, op2); | ||
| 531 | } | ||
| 532 | |||
| 533 | pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 534 | return dataProcessing(cond, .rsb, 0, rd, rn, op2); | ||
| 535 | } | ||
| 536 | |||
| 537 | pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 538 | return dataProcessing(cond, .rsb, 1, rd, rn, op2); | ||
| 539 | } | ||
| 540 | |||
| 541 | pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 542 | return dataProcessing(cond, .add, 0, rd, rn, op2); | ||
| 447 | } | 543 | } |
| 448 | 544 | ||
| 449 | pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | 545 | pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 450 | return dataProcessing(cond, .eor, s, rd, rn, op2); | 546 | return dataProcessing(cond, .add, 1, rd, rn, op2); |
| 451 | } | 547 | } |
| 452 | 548 | ||
| 453 | pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | 549 | pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 454 | return dataProcessing(cond, .sub, s, rd, rn, op2); | 550 | return dataProcessing(cond, .adc, 0, rd, rn, op2); |
| 455 | } | 551 | } |
| 456 | 552 | ||
| 457 | pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | 553 | pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 458 | return dataProcessing(cond, .rsb, s, rd, rn, op2); | 554 | return dataProcessing(cond, .adc, 1, rd, rn, op2); |
| 459 | } | 555 | } |
| 460 | 556 | ||
| 461 | pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | 557 | pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 462 | return dataProcessing(cond, .add, s, rd, rn, op2); | 558 | return dataProcessing(cond, .sbc, 0, rd, rn, op2); |
| 463 | } | 559 | } |
| 464 | 560 | ||
| 465 | pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | 561 | pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 466 | return dataProcessing(cond, .adc, s, rd, rn, op2); | 562 | return dataProcessing(cond, .sbc, 1, rd, rn, op2); |
| 467 | } | 563 | } |
| 468 | 564 | ||
| 469 | pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | 565 | pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 470 | return dataProcessing(cond, .sbc, s, rd, rn, op2); | 566 | return dataProcessing(cond, .rsc, 0, rd, rn, op2); |
| 471 | } | 567 | } |
| 472 | 568 | ||
| 473 | pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | 569 | pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 474 | return dataProcessing(cond, .rsc, s, rd, rn, op2); | 570 | return dataProcessing(cond, .rsc, 1, rd, rn, op2); |
| 475 | } | 571 | } |
| 476 | 572 | ||
| 477 | pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { | 573 | pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { |
| ... | @@ -490,32 +586,115 @@ pub const Instruction = union(enum) { | ... | @@ -490,32 +586,115 @@ pub const Instruction = union(enum) { |
| 490 | return dataProcessing(cond, .cmn, 1, .r0, rn, op2); | 586 | return dataProcessing(cond, .cmn, 1, .r0, rn, op2); |
| 491 | } | 587 | } |
| 492 | 588 | ||
| 493 | pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { | 589 | pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { |
| 494 | return dataProcessing(cond, .orr, s, rd, rn, op2); | 590 | return dataProcessing(cond, .orr, 0, rd, rn, op2); |
| 591 | } | ||
| 592 | |||
| 593 | pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction { | ||
| 594 | return dataProcessing(cond, .orr, 1, rd, rn, op2); | ||
| 595 | } | ||
| 596 | |||
| 597 | pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction { | ||
| 598 | return dataProcessing(cond, .mov, 0, rd, .r0, op2); | ||
| 495 | } | 599 | } |
| 496 | 600 | ||
| 497 | pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { | 601 | pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction { |
| 498 | return dataProcessing(cond, .mov, s, rd, .r0, op2); | 602 | return dataProcessing(cond, .mov, 1, rd, .r0, op2); |
| 499 | } | 603 | } |
| 500 | 604 | ||
| 501 | pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { | 605 | pub fn bic(cond: Condition, rd: Register, op2: Operand) Instruction { |
| 502 | return dataProcessing(cond, .bic, s, rd, rn, op2); | 606 | return dataProcessing(cond, .bic, 0, rd, rn, op2); |
| 503 | } | 607 | } |
| 504 | 608 | ||
| 505 | pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { | 609 | pub fn bics(cond: Condition, rd: Register, op2: Operand) Instruction { |
| 506 | return dataProcessing(cond, .mvn, s, rd, .r0, op2); | 610 | return dataProcessing(cond, .bic, 1, rd, rn, op2); |
| 611 | } | ||
| 612 | |||
| 613 | pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction { | ||
| 614 | return dataProcessing(cond, .mvn, 0, rd, .r0, op2); | ||
| 615 | } | ||
| 616 | |||
| 617 | pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction { | ||
| 618 | return dataProcessing(cond, .mvn, 1, rd, .r0, op2); | ||
| 619 | } | ||
| 620 | |||
| 621 | // PSR transfer | ||
| 622 | |||
| 623 | pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction { | ||
| 624 | return dataProcessing(cond, if (psr == .cpsr) .tst else .cmp, 0, rd, .r15, Operand.reg(.r0, Operand.Shift.none)); | ||
| 507 | } | 625 | } |
| 508 | 626 | ||
| 509 | // Single data transfer | 627 | // Single data transfer |
| 510 | 628 | ||
| 511 | pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { | 629 | pub const OffsetArgs = struct { |
| 512 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1); | 630 | pre_index: bool = true, |
| 631 | positive: bool = true, | ||
| 632 | offset: Offset, | ||
| 633 | write_back: bool = false, | ||
| 634 | }; | ||
| 635 | |||
| 636 | pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | ||
| 637 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1); | ||
| 638 | } | ||
| 639 | |||
| 640 | pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | ||
| 641 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1); | ||
| 642 | } | ||
| 643 | |||
| 644 | pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | ||
| 645 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0); | ||
| 646 | } | ||
| 647 | |||
| 648 | pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { | ||
| 649 | return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0); | ||
| 650 | } | ||
| 651 | |||
| 652 | // Block data transfer | ||
| 653 | |||
| 654 | pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 655 | return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1); | ||
| 656 | } | ||
| 657 | |||
| 658 | pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 659 | return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1); | ||
| 513 | } | 660 | } |
| 514 | 661 | ||
| 515 | pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { | 662 | pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { |
| 516 | return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0); | 663 | return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1); |
| 517 | } | 664 | } |
| 518 | 665 | ||
| 666 | pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 667 | return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1); | ||
| 668 | } | ||
| 669 | |||
| 670 | pub const ldmfa = ldmda; | ||
| 671 | pub const ldmea = ldmdb; | ||
| 672 | pub const ldmed = ldmib; | ||
| 673 | pub const ldmfd = ldmia; | ||
| 674 | pub const ldm = ldmia; | ||
| 675 | |||
| 676 | pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 677 | return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0); | ||
| 678 | } | ||
| 679 | |||
| 680 | pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 681 | return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0); | ||
| 682 | } | ||
| 683 | |||
| 684 | pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 685 | return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0); | ||
| 686 | } | ||
| 687 | |||
| 688 | pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction { | ||
| 689 | return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0); | ||
| 690 | } | ||
| 691 | |||
| 692 | pub const stmed = stmda; | ||
| 693 | pub const stmfd = stmdb; | ||
| 694 | pub const stmfa = stmib; | ||
| 695 | pub const stmea = stmia; | ||
| 696 | pub const stm = stmia; | ||
| 697 | |||
| 519 | // Branch | 698 | // Branch |
| 520 | 699 | ||
| 521 | pub fn b(cond: Condition, offset: i24) Instruction { | 700 | pub fn b(cond: Condition, offset: i24) Instruction { |
| ... | @@ -549,6 +728,58 @@ pub const Instruction = union(enum) { | ... | @@ -549,6 +728,58 @@ pub const Instruction = union(enum) { |
| 549 | pub fn bkpt(imm: u16) Instruction { | 728 | pub fn bkpt(imm: u16) Instruction { |
| 550 | return breakpoint(imm); | 729 | return breakpoint(imm); |
| 551 | } | 730 | } |
| 731 | |||
| 732 | // Aliases | ||
| 733 | |||
| 734 | pub fn pop(cond: Condition, args: anytype) Instruction { | ||
| 735 | if (@typeInfo(@TypeOf(args)) != .Struct) { | ||
| 736 | @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); | ||
| 737 | } | ||
| 738 | |||
| 739 | if (args.len < 1) { | ||
| 740 | @compileError("Expected at least one register"); | ||
| 741 | } else if (args.len == 1) { | ||
| 742 | const reg = args[0]; | ||
| 743 | return ldr(cond, reg, .sp, .{ | ||
| 744 | .pre_index = false, | ||
| 745 | .positive = true, | ||
| 746 | .offset = Offset.imm(4), | ||
| 747 | .write_back = false, | ||
| 748 | }); | ||
| 749 | } else { | ||
| 750 | var register_list: u16 = 0; | ||
| 751 | inline for (args) |arg| { | ||
| 752 | const reg = @as(Register, arg); | ||
| 753 | register_list |= @as(u16, 1) << reg.id(); | ||
| 754 | } | ||
| 755 | return ldm(cond, .sp, true, @bitCast(RegisterList, register_list)); | ||
| 756 | } | ||
| 757 | } | ||
| 758 | |||
| 759 | pub fn push(cond: Condition, args: anytype) Instruction { | ||
| 760 | if (@typeInfo(@TypeOf(args)) != .Struct) { | ||
| 761 | @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args))); | ||
| 762 | } | ||
| 763 | |||
| 764 | if (args.len < 1) { | ||
| 765 | @compileError("Expected at least one register"); | ||
| 766 | } else if (args.len == 1) { | ||
| 767 | const reg = args[0]; | ||
| 768 | return str(cond, reg, .sp, .{ | ||
| 769 | .pre_index = true, | ||
| 770 | .positive = false, | ||
| 771 | .offset = Offset.imm(4), | ||
| 772 | .write_back = true, | ||
| 773 | }); | ||
| 774 | } else { | ||
| 775 | var register_list: u16 = 0; | ||
| 776 | inline for (args) |arg| { | ||
| 777 | const reg = @as(Register, arg); | ||
| 778 | register_list |= @as(u16, 1) << reg.id(); | ||
| 779 | } | ||
| 780 | return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list)); | ||
| 781 | } | ||
| 782 | } | ||
| 552 | }; | 783 | }; |
| 553 | 784 | ||
| 554 | test "serialize instructions" { | 785 | test "serialize instructions" { |
| ... | @@ -559,23 +790,31 @@ test "serialize instructions" { | ... | @@ -559,23 +790,31 @@ test "serialize instructions" { |
| 559 | 790 | ||
| 560 | const testcases = [_]Testcase{ | 791 | const testcases = [_]Testcase{ |
| 561 | .{ // add r0, r0, r0 | 792 | .{ // add r0, r0, r0 |
| 562 | .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), | 793 | .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), |
| 563 | .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, | 794 | .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, |
| 564 | }, | 795 | }, |
| 565 | .{ // mov r4, r2 | 796 | .{ // mov r4, r2 |
| 566 | .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), | 797 | .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), |
| 567 | .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, | 798 | .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, |
| 568 | }, | 799 | }, |
| 569 | .{ // mov r0, #42 | 800 | .{ // mov r0, #42 |
| 570 | .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)), | 801 | .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)), |
| 571 | .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010, | 802 | .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010, |
| 572 | }, | 803 | }, |
| 804 | .{ // mrs r5, cpsr | ||
| 805 | .inst = Instruction.mrs(.al, .r5, .cpsr), | ||
| 806 | .expected = 0b1110_00010_0_001111_0101_000000000000, | ||
| 807 | }, | ||
| 573 | .{ // ldr r0, [r2, #42] | 808 | .{ // ldr r0, [r2, #42] |
| 574 | .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)), | 809 | .inst = Instruction.ldr(.al, .r0, .r2, .{ |
| 810 | .offset = Instruction.Offset.imm(42), | ||
| 811 | }), | ||
| 575 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, | 812 | .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, |
| 576 | }, | 813 | }, |
| 577 | .{ // str r0, [r3] | 814 | .{ // str r0, [r3] |
| 578 | .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none), | 815 | .inst = Instruction.str(.al, .r0, .r3, .{ |
| 816 | .offset = Instruction.Offset.none, | ||
| 817 | }), | ||
| 579 | .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, | 818 | .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, |
| 580 | }, | 819 | }, |
| 581 | .{ // b #12 | 820 | .{ // b #12 |
| ... | @@ -598,6 +837,14 @@ test "serialize instructions" { | ... | @@ -598,6 +837,14 @@ test "serialize instructions" { |
| 598 | .inst = Instruction.bkpt(42), | 837 | .inst = Instruction.bkpt(42), |
| 599 | .expected = 0b1110_0001_0010_000000000010_0111_1010, | 838 | .expected = 0b1110_0001_0010_000000000010_0111_1010, |
| 600 | }, | 839 | }, |
| 840 | .{ // stmdb r9, {r0} | ||
| 841 | .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }), | ||
| 842 | .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001, | ||
| 843 | }, | ||
| 844 | .{ // ldmea r4!, {r2, r5} | ||
| 845 | .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }), | ||
| 846 | .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100, | ||
| 847 | }, | ||
| 601 | }; | 848 | }; |
| 602 | 849 | ||
| 603 | for (testcases) |case| { | 850 | for (testcases) |case| { |
| ... | @@ -605,3 +852,43 @@ test "serialize instructions" { | ... | @@ -605,3 +852,43 @@ test "serialize instructions" { |
| 605 | testing.expectEqual(case.expected, actual); | 852 | testing.expectEqual(case.expected, actual); |
| 606 | } | 853 | } |
| 607 | } | 854 | } |
| 855 | |||
| 856 | test "aliases" { | ||
| 857 | const Testcase = struct { | ||
| 858 | expected: Instruction, | ||
| 859 | actual: Instruction, | ||
| 860 | }; | ||
| 861 | |||
| 862 | const testcases = [_]Testcase{ | ||
| 863 | .{ // pop { r6 } | ||
| 864 | .actual = Instruction.pop(.al, .{.r6}), | ||
| 865 | .expected = Instruction.ldr(.al, .r6, .sp, .{ | ||
| 866 | .pre_index = false, | ||
| 867 | .positive = true, | ||
| 868 | .offset = Instruction.Offset.imm(4), | ||
| 869 | .write_back = false, | ||
| 870 | }), | ||
| 871 | }, | ||
| 872 | .{ // pop { r1, r5 } | ||
| 873 | .actual = Instruction.pop(.al, .{ .r1, .r5 }), | ||
| 874 | .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }), | ||
| 875 | }, | ||
| 876 | .{ // push { r3 } | ||
| 877 | .actual = Instruction.push(.al, .{.r3}), | ||
| 878 | .expected = Instruction.str(.al, .r3, .sp, .{ | ||
| 879 | .pre_index = true, | ||
| 880 | .positive = false, | ||
| 881 | .offset = Instruction.Offset.imm(4), | ||
| 882 | .write_back = true, | ||
| 883 | }), | ||
| 884 | }, | ||
| 885 | .{ // push { r0, r2 } | ||
| 886 | .actual = Instruction.push(.al, .{ .r0, .r2 }), | ||
| 887 | .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }), | ||
| 888 | }, | ||
| 889 | }; | ||
| 890 | |||
| 891 | for (testcases) |case| { | ||
| 892 | testing.expectEqual(case.expected.toU32(), case.actual.toU32()); | ||
| 893 | } | ||
| 894 | } |
src/glibc.zig+18-9| ... | @@ -689,9 +689,6 @@ pub const BuiltSharedObjects = struct { | ... | @@ -689,9 +689,6 @@ pub const BuiltSharedObjects = struct { |
| 689 | 689 | ||
| 690 | const all_map_basename = "all.map"; | 690 | const all_map_basename = "all.map"; |
| 691 | 691 | ||
| 692 | // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. | ||
| 693 | // zig fmt: off | ||
| 694 | |||
| 695 | pub fn buildSharedObjects(comp: *Compilation) !void { | 692 | pub fn buildSharedObjects(comp: *Compilation) !void { |
| 696 | const tracy = trace(@src()); | 693 | const tracy = trace(@src()); |
| 697 | defer tracy.end(); | 694 | defer tracy.end(); |
| ... | @@ -827,8 +824,9 @@ pub fn buildSharedObjects(comp: *Compilation) !void { | ... | @@ -827,8 +824,9 @@ pub fn buildSharedObjects(comp: *Compilation) !void { |
| 827 | 824 | ||
| 828 | if (ver.patch == 0) { | 825 | if (ver.patch == 0) { |
| 829 | const sym_plus_ver = try std.fmt.allocPrint( | 826 | const sym_plus_ver = try std.fmt.allocPrint( |
| 830 | arena, "{s}_{d}_{d}", | 827 | arena, |
| 831 | .{sym_name, ver.major, ver.minor}, | 828 | "{s}_{d}_{d}", |
| 829 | .{ sym_name, ver.major, ver.minor }, | ||
| 832 | ); | 830 | ); |
| 833 | try zig_body.writer().print( | 831 | try zig_body.writer().print( |
| 834 | \\.globl {s} | 832 | \\.globl {s} |
| ... | @@ -840,13 +838,19 @@ pub fn buildSharedObjects(comp: *Compilation) !void { | ... | @@ -840,13 +838,19 @@ pub fn buildSharedObjects(comp: *Compilation) !void { |
| 840 | , .{ | 838 | , .{ |
| 841 | sym_plus_ver, | 839 | sym_plus_ver, |
| 842 | sym_plus_ver, | 840 | sym_plus_ver, |
| 843 | sym_plus_ver, sym_name, at_sign_str, ver.major, ver.minor, | 841 | sym_plus_ver, |
| 842 | sym_name, | ||
| 843 | at_sign_str, | ||
| 844 | ver.major, | ||
| 845 | ver.minor, | ||
| 844 | sym_plus_ver, | 846 | sym_plus_ver, |
| 845 | sym_plus_ver, | 847 | sym_plus_ver, |
| 846 | }); | 848 | }); |
| 847 | } else { | 849 | } else { |
| 848 | const sym_plus_ver = try std.fmt.allocPrint(arena, "{s}_{d}_{d}_{d}", | 850 | const sym_plus_ver = try std.fmt.allocPrint( |
| 849 | .{sym_name, ver.major, ver.minor, ver.patch}, | 851 | arena, |
| 852 | "{s}_{d}_{d}_{d}", | ||
| 853 | .{ sym_name, ver.major, ver.minor, ver.patch }, | ||
| 850 | ); | 854 | ); |
| 851 | try zig_body.writer().print( | 855 | try zig_body.writer().print( |
| 852 | \\.globl {s} | 856 | \\.globl {s} |
| ... | @@ -858,7 +862,12 @@ pub fn buildSharedObjects(comp: *Compilation) !void { | ... | @@ -858,7 +862,12 @@ pub fn buildSharedObjects(comp: *Compilation) !void { |
| 858 | , .{ | 862 | , .{ |
| 859 | sym_plus_ver, | 863 | sym_plus_ver, |
| 860 | sym_plus_ver, | 864 | sym_plus_ver, |
| 861 | sym_plus_ver, sym_name, at_sign_str, ver.major, ver.minor, ver.patch, | 865 | sym_plus_ver, |
| 866 | sym_name, | ||
| 867 | at_sign_str, | ||
| 868 | ver.major, | ||
| 869 | ver.minor, | ||
| 870 | ver.patch, | ||
| 862 | sym_plus_ver, | 871 | sym_plus_ver, |
| 863 | sym_plus_ver, | 872 | sym_plus_ver, |
| 864 | }); | 873 | }); |
src/link.zig+2| ... | @@ -45,6 +45,7 @@ pub const Options = struct { | ... | @@ -45,6 +45,7 @@ pub const Options = struct { |
| 45 | program_code_size_hint: u64 = 256 * 1024, | 45 | program_code_size_hint: u64 = 256 * 1024, |
| 46 | entry_addr: ?u64 = null, | 46 | entry_addr: ?u64 = null, |
| 47 | stack_size_override: ?u64, | 47 | stack_size_override: ?u64, |
| 48 | image_base_override: ?u64, | ||
| 48 | /// Set to `true` to omit debug info. | 49 | /// Set to `true` to omit debug info. |
| 49 | strip: bool, | 50 | strip: bool, |
| 50 | /// If this is true then this link code is responsible for outputting an object | 51 | /// If this is true then this link code is responsible for outputting an object |
| ... | @@ -60,6 +61,7 @@ pub const Options = struct { | ... | @@ -60,6 +61,7 @@ pub const Options = struct { |
| 60 | link_libcpp: bool, | 61 | link_libcpp: bool, |
| 61 | function_sections: bool, | 62 | function_sections: bool, |
| 62 | eh_frame_hdr: bool, | 63 | eh_frame_hdr: bool, |
| 64 | emit_relocs: bool, | ||
| 63 | rdynamic: bool, | 65 | rdynamic: bool, |
| 64 | z_nodelete: bool, | 66 | z_nodelete: bool, |
| 65 | z_defs: bool, | 67 | z_defs: bool, |
src/link/Coff.zig+17-13| ... | @@ -22,10 +22,10 @@ const minimum_text_block_size = 64 * allocation_padding; | ... | @@ -22,10 +22,10 @@ const minimum_text_block_size = 64 * allocation_padding; |
| 22 | 22 | ||
| 23 | const section_alignment = 4096; | 23 | const section_alignment = 4096; |
| 24 | const file_alignment = 512; | 24 | const file_alignment = 512; |
| 25 | const image_base = 0x400_000; | 25 | const default_image_base = 0x400_000; |
| 26 | const section_table_size = 2 * 40; | 26 | const section_table_size = 2 * 40; |
| 27 | comptime { | 27 | comptime { |
| 28 | assert(mem.isAligned(image_base, section_alignment)); | 28 | assert(mem.isAligned(default_image_base, section_alignment)); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | pub const base_tag: link.File.Tag = .coff; | 31 | pub const base_tag: link.File.Tag = .coff; |
| ... | @@ -55,7 +55,7 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, | ... | @@ -55,7 +55,7 @@ offset_table: std.ArrayListUnmanaged(u64) = .{}, |
| 55 | /// Free list of offset table indices | 55 | /// Free list of offset table indices |
| 56 | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, | 56 | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 57 | 57 | ||
| 58 | /// Virtual address of the entry point procedure relative to `image_base` | 58 | /// Virtual address of the entry point procedure relative to image base. |
| 59 | entry_addr: ?u32 = null, | 59 | entry_addr: ?u32 = null, |
| 60 | 60 | ||
| 61 | /// Absolute virtual address of the text section when the executable is loaded in memory. | 61 | /// Absolute virtual address of the text section when the executable is loaded in memory. |
| ... | @@ -183,14 +183,14 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio | ... | @@ -183,14 +183,14 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 183 | 183 | ||
| 184 | self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment); | 184 | self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment); |
| 185 | const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment); | 185 | const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment); |
| 186 | self.offset_table_virtual_address = image_base + section_data_relative_virtual_address; | 186 | self.offset_table_virtual_address = default_image_base + section_data_relative_virtual_address; |
| 187 | self.offset_table_size = default_offset_table_size; | 187 | self.offset_table_size = default_offset_table_size; |
| 188 | self.section_table_offset = section_table_offset; | 188 | self.section_table_offset = section_table_offset; |
| 189 | self.text_section_virtual_address = image_base + section_data_relative_virtual_address + section_alignment; | 189 | self.text_section_virtual_address = default_image_base + section_data_relative_virtual_address + section_alignment; |
| 190 | self.text_section_size = default_size_of_code; | 190 | self.text_section_size = default_size_of_code; |
| 191 | 191 | ||
| 192 | // Size of file when loaded in memory | 192 | // Size of file when loaded in memory |
| 193 | const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment); | 193 | const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + default_size_of_code, section_alignment); |
| 194 | 194 | ||
| 195 | mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size); | 195 | mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size); |
| 196 | index += 2; | 196 | index += 2; |
| ... | @@ -234,11 +234,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio | ... | @@ -234,11 +234,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 234 | index += 4; | 234 | index += 4; |
| 235 | 235 | ||
| 236 | // Image base address | 236 | // Image base address |
| 237 | mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base); | 237 | mem.writeIntLittle(u32, hdr_data[index..][0..4], default_image_base); |
| 238 | index += 4; | 238 | index += 4; |
| 239 | } else { | 239 | } else { |
| 240 | // Image base address | 240 | // Image base address |
| 241 | mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base); | 241 | mem.writeIntLittle(u64, hdr_data[index..][0..8], default_image_base); |
| 242 | index += 8; | 242 | index += 8; |
| 243 | } | 243 | } |
| 244 | 244 | ||
| ... | @@ -328,7 +328,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio | ... | @@ -328,7 +328,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 328 | mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); | 328 | mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); |
| 329 | index += 4; | 329 | index += 4; |
| 330 | // Virtual address (u32) | 330 | // Virtual address (u32) |
| 331 | mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base); | 331 | mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - default_image_base); |
| 332 | index += 4; | 332 | index += 4; |
| 333 | } else { | 333 | } else { |
| 334 | mem.set(u8, hdr_data[index..][0..8], 0); | 334 | mem.set(u8, hdr_data[index..][0..8], 0); |
| ... | @@ -354,7 +354,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio | ... | @@ -354,7 +354,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 354 | mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); | 354 | mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); |
| 355 | index += 4; | 355 | index += 4; |
| 356 | // Virtual address (u32) | 356 | // Virtual address (u32) |
| 357 | mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base); | 357 | mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - default_image_base); |
| 358 | index += 4; | 358 | index += 4; |
| 359 | } else { | 359 | } else { |
| 360 | mem.set(u8, hdr_data[index..][0..8], 0); | 360 | mem.set(u8, hdr_data[index..][0..8], 0); |
| ... | @@ -601,7 +601,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void { | ... | @@ -601,7 +601,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void { |
| 601 | 601 | ||
| 602 | // Write .text new virtual address | 602 | // Write .text new virtual address |
| 603 | self.text_section_virtual_address = self.text_section_virtual_address + va_offset; | 603 | self.text_section_virtual_address = self.text_section_virtual_address + va_offset; |
| 604 | mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base); | 604 | mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - default_image_base); |
| 605 | try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12); | 605 | try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12); |
| 606 | 606 | ||
| 607 | // Fix the VAs in the offset table | 607 | // Fix the VAs in the offset table |
| ... | @@ -716,7 +716,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, | ... | @@ -716,7 +716,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, |
| 716 | } | 716 | } |
| 717 | } | 717 | } |
| 718 | if (mem.eql(u8, exp.options.name, "_start")) { | 718 | if (mem.eql(u8, exp.options.name, "_start")) { |
| 719 | self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base; | 719 | self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base; |
| 720 | } else { | 720 | } else { |
| 721 | try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); | 721 | try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); |
| 722 | module.failed_exports.putAssumeCapacityNoClobber( | 722 | module.failed_exports.putAssumeCapacityNoClobber( |
| ... | @@ -754,7 +754,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void { | ... | @@ -754,7 +754,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void { |
| 754 | } | 754 | } |
| 755 | 755 | ||
| 756 | if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) { | 756 | if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) { |
| 757 | const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment); | 757 | const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + self.text_section_size, section_alignment); |
| 758 | var buf: [4]u8 = undefined; | 758 | var buf: [4]u8 = undefined; |
| 759 | mem.writeIntLittle(u32, &buf, new_size_of_image); | 759 | mem.writeIntLittle(u32, &buf, new_size_of_image); |
| 760 | try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56); | 760 | try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56); |
| ... | @@ -832,6 +832,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -832,6 +832,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 832 | } | 832 | } |
| 833 | try man.addOptionalFile(module_obj_path); | 833 | try man.addOptionalFile(module_obj_path); |
| 834 | man.hash.addOptional(self.base.options.stack_size_override); | 834 | man.hash.addOptional(self.base.options.stack_size_override); |
| 835 | man.hash.addOptional(self.base.options.image_base_override); | ||
| 835 | man.hash.addListOfBytes(self.base.options.extra_lld_args); | 836 | man.hash.addListOfBytes(self.base.options.extra_lld_args); |
| 836 | man.hash.addListOfBytes(self.base.options.lib_dirs); | 837 | man.hash.addListOfBytes(self.base.options.lib_dirs); |
| 837 | man.hash.add(self.base.options.is_compiler_rt_or_libc); | 838 | man.hash.add(self.base.options.is_compiler_rt_or_libc); |
| ... | @@ -914,6 +915,9 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { | ... | @@ -914,6 +915,9 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 914 | const stack_size = self.base.options.stack_size_override orelse 16777216; | 915 | const stack_size = self.base.options.stack_size_override orelse 16777216; |
| 915 | try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size})); | 916 | try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size})); |
| 916 | } | 917 | } |
| 918 | if (self.base.options.image_base_override) |image_base| { | ||
| 919 | try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base})); | ||
| 920 | } | ||
| 917 | 921 | ||
| 918 | if (target.cpu.arch == .i386) { | 922 | if (target.cpu.arch == .i386) { |
| 919 | try argv.append("-MACHINE:X86"); | 923 | try argv.append("-MACHINE:X86"); |
src/link/Elf.zig+70-47| ... | @@ -27,9 +27,6 @@ const Cache = @import("../Cache.zig"); | ... | @@ -27,9 +27,6 @@ const Cache = @import("../Cache.zig"); |
| 27 | 27 | ||
| 28 | const default_entry_addr = 0x8000000; | 28 | const default_entry_addr = 0x8000000; |
| 29 | 29 | ||
| 30 | // TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. | ||
| 31 | // zig fmt: off | ||
| 32 | |||
| 33 | pub const base_tag: File.Tag = .elf; | 30 | pub const base_tag: File.Tag = .elf; |
| 34 | 31 | ||
| 35 | base: File, | 32 | base: File, |
| ... | @@ -273,8 +270,8 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio | ... | @@ -273,8 +270,8 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 273 | 270 | ||
| 274 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf { | 271 | pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf { |
| 275 | const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { | 272 | const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { |
| 276 | 0 ... 32 => .p32, | 273 | 0...32 => .p32, |
| 277 | 33 ... 64 => .p64, | 274 | 33...64 => .p64, |
| 278 | else => return error.UnsupportedELFArchitecture, | 275 | else => return error.UnsupportedELFArchitecture, |
| 279 | }; | 276 | }; |
| 280 | const self = try gpa.create(Elf); | 277 | const self = try gpa.create(Elf); |
| ... | @@ -752,40 +749,52 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { | ... | @@ -752,40 +749,52 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 752 | // These are LEB encoded but since the values are all less than 127 | 749 | // These are LEB encoded but since the values are all less than 127 |
| 753 | // we can simply append these bytes. | 750 | // we can simply append these bytes. |
| 754 | const abbrev_buf = [_]u8{ | 751 | const abbrev_buf = [_]u8{ |
| 755 | abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header | 752 | abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header |
| 756 | DW.AT_stmt_list, DW.FORM_sec_offset, DW.AT_low_pc, | 753 | DW.AT_stmt_list, DW.FORM_sec_offset, DW.AT_low_pc, |
| 757 | DW.FORM_addr, DW.AT_high_pc, DW.FORM_addr, | 754 | DW.FORM_addr, DW.AT_high_pc, DW.FORM_addr, |
| 758 | DW.AT_name, DW.FORM_strp, DW.AT_comp_dir, | 755 | DW.AT_name, DW.FORM_strp, DW.AT_comp_dir, |
| 759 | DW.FORM_strp, DW.AT_producer, DW.FORM_strp, | 756 | DW.FORM_strp, DW.AT_producer, DW.FORM_strp, |
| 760 | DW.AT_language, DW.FORM_data2, 0, | 757 | DW.AT_language, DW.FORM_data2, 0, |
| 761 | 0, // table sentinel | 758 | 0, // table sentinel |
| 762 | abbrev_subprogram, DW.TAG_subprogram, | 759 | abbrev_subprogram, |
| 760 | DW.TAG_subprogram, | ||
| 763 | DW.CHILDREN_yes, // header | 761 | DW.CHILDREN_yes, // header |
| 764 | DW.AT_low_pc, DW.FORM_addr, | 762 | DW.AT_low_pc, |
| 765 | DW.AT_high_pc, DW.FORM_data4, DW.AT_type, | 763 | DW.FORM_addr, |
| 766 | DW.FORM_ref4, DW.AT_name, DW.FORM_string, | 764 | DW.AT_high_pc, |
| 767 | 0, 0, // table sentinel | 765 | DW.FORM_data4, |
| 768 | abbrev_subprogram_retvoid, | 766 | DW.AT_type, |
| 769 | DW.TAG_subprogram, DW.CHILDREN_yes, // header | 767 | DW.FORM_ref4, |
| 770 | DW.AT_low_pc, | 768 | DW.AT_name, |
| 771 | DW.FORM_addr, DW.AT_high_pc, DW.FORM_data4, | 769 | DW.FORM_string, |
| 772 | DW.AT_name, DW.FORM_string, 0, | 770 | 0, 0, // table sentinel |
| 771 | abbrev_subprogram_retvoid, | ||
| 772 | DW.TAG_subprogram, DW.CHILDREN_yes, // header | ||
| 773 | DW.AT_low_pc, DW.FORM_addr, | ||
| 774 | DW.AT_high_pc, DW.FORM_data4, | ||
| 775 | DW.AT_name, DW.FORM_string, | ||
| 776 | 0, | ||
| 773 | 0, // table sentinel | 777 | 0, // table sentinel |
| 774 | abbrev_base_type, DW.TAG_base_type, | 778 | abbrev_base_type, |
| 779 | DW.TAG_base_type, | ||
| 775 | DW.CHILDREN_no, // header | 780 | DW.CHILDREN_no, // header |
| 776 | DW.AT_encoding, DW.FORM_data1, | 781 | DW.AT_encoding, |
| 777 | DW.AT_byte_size, DW.FORM_data1, DW.AT_name, | 782 | DW.FORM_data1, |
| 778 | DW.FORM_string, 0, 0, // table sentinel | 783 | DW.AT_byte_size, |
| 779 | 784 | DW.FORM_data1, | |
| 780 | abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header | 785 | DW.AT_name, |
| 781 | 0, 0, // table sentinel | 786 | DW.FORM_string, 0, 0, // table sentinel |
| 782 | abbrev_parameter, | 787 | abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header |
| 788 | 0, 0, // table sentinel | ||
| 789 | abbrev_parameter, | ||
| 783 | DW.TAG_formal_parameter, DW.CHILDREN_no, // header | 790 | DW.TAG_formal_parameter, DW.CHILDREN_no, // header |
| 784 | DW.AT_location, | 791 | DW.AT_location, DW.FORM_exprloc, |
| 785 | DW.FORM_exprloc, DW.AT_type, DW.FORM_ref4, | 792 | DW.AT_type, DW.FORM_ref4, |
| 786 | DW.AT_name, DW.FORM_string, 0, | 793 | DW.AT_name, DW.FORM_string, |
| 794 | 0, | ||
| 787 | 0, // table sentinel | 795 | 0, // table sentinel |
| 788 | 0, 0, | 796 | 0, |
| 797 | 0, | ||
| 789 | 0, // section sentinel | 798 | 0, // section sentinel |
| 790 | }; | 799 | }; |
| 791 | 800 | ||
| ... | @@ -1021,7 +1030,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { | ... | @@ -1021,7 +1030,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void { |
| 1021 | 0, // `DW.LNS_set_prologue_end` | 1030 | 0, // `DW.LNS_set_prologue_end` |
| 1022 | 0, // `DW.LNS_set_epilogue_begin` | 1031 | 0, // `DW.LNS_set_epilogue_begin` |
| 1023 | 1, // `DW.LNS_set_isa` | 1032 | 1, // `DW.LNS_set_isa` |
| 1024 | |||
| 1025 | 0, // include_directories (none except the compilation unit cwd) | 1033 | 0, // include_directories (none except the compilation unit cwd) |
| 1026 | }); | 1034 | }); |
| 1027 | // file_names[0] | 1035 | // file_names[0] |
| ... | @@ -1284,8 +1292,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1284,8 +1292,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1284 | // We can skip hashing libc and libc++ components that we are in charge of building from Zig | 1292 | // We can skip hashing libc and libc++ components that we are in charge of building from Zig |
| 1285 | // installation sources because they are always a product of the compiler version + target information. | 1293 | // installation sources because they are always a product of the compiler version + target information. |
| 1286 | man.hash.add(stack_size); | 1294 | man.hash.add(stack_size); |
| 1295 | man.hash.addOptional(self.base.options.image_base_override); | ||
| 1287 | man.hash.add(gc_sections); | 1296 | man.hash.add(gc_sections); |
| 1288 | man.hash.add(self.base.options.eh_frame_hdr); | 1297 | man.hash.add(self.base.options.eh_frame_hdr); |
| 1298 | man.hash.add(self.base.options.emit_relocs); | ||
| 1289 | man.hash.add(self.base.options.rdynamic); | 1299 | man.hash.add(self.base.options.rdynamic); |
| 1290 | man.hash.addListOfBytes(self.base.options.extra_lld_args); | 1300 | man.hash.addListOfBytes(self.base.options.extra_lld_args); |
| 1291 | man.hash.addListOfBytes(self.base.options.lib_dirs); | 1301 | man.hash.addListOfBytes(self.base.options.lib_dirs); |
| ... | @@ -1317,7 +1327,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1317,7 +1327,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1317 | 1327 | ||
| 1318 | var prev_digest_buf: [digest.len]u8 = undefined; | 1328 | var prev_digest_buf: [digest.len]u8 = undefined; |
| 1319 | const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { | 1329 | const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { |
| 1320 | log.debug("ELF LLD new_digest={} readlink error: {}", .{digest, @errorName(err)}); | 1330 | log.debug("ELF LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) }); |
| 1321 | // Handle this as a cache miss. | 1331 | // Handle this as a cache miss. |
| 1322 | break :blk prev_digest_buf[0..0]; | 1332 | break :blk prev_digest_buf[0..0]; |
| 1323 | }; | 1333 | }; |
| ... | @@ -1327,7 +1337,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1327,7 +1337,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1327 | self.base.lock = man.toOwnedLock(); | 1337 | self.base.lock = man.toOwnedLock(); |
| 1328 | return; | 1338 | return; |
| 1329 | } | 1339 | } |
| 1330 | log.debug("ELF LLD prev_digest={} new_digest={}", .{prev_digest, digest}); | 1340 | log.debug("ELF LLD prev_digest={} new_digest={}", .{ prev_digest, digest }); |
| 1331 | 1341 | ||
| 1332 | // We are about to change the output file to be different, so we invalidate the build hash now. | 1342 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 1333 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { | 1343 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
| ... | @@ -1352,6 +1362,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1352,6 +1362,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1352 | try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size})); | 1362 | try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size})); |
| 1353 | } | 1363 | } |
| 1354 | 1364 | ||
| 1365 | if (self.base.options.image_base_override) |image_base| { | ||
| 1366 | try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base})); | ||
| 1367 | } | ||
| 1368 | |||
| 1355 | if (self.base.options.linker_script) |linker_script| { | 1369 | if (self.base.options.linker_script) |linker_script| { |
| 1356 | try argv.append("-T"); | 1370 | try argv.append("-T"); |
| 1357 | try argv.append(linker_script); | 1371 | try argv.append(linker_script); |
| ... | @@ -1365,6 +1379,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1365,6 +1379,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1365 | try argv.append("--eh-frame-hdr"); | 1379 | try argv.append("--eh-frame-hdr"); |
| 1366 | } | 1380 | } |
| 1367 | 1381 | ||
| 1382 | if (self.base.options.emit_relocs) { | ||
| 1383 | try argv.append("--emit-relocs"); | ||
| 1384 | } | ||
| 1385 | |||
| 1368 | if (self.base.options.rdynamic) { | 1386 | if (self.base.options.rdynamic) { |
| 1369 | try argv.append("--export-dynamic"); | 1387 | try argv.append("--export-dynamic"); |
| 1370 | } | 1388 | } |
| ... | @@ -1443,10 +1461,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1443,10 +1461,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1443 | var test_path = std.ArrayList(u8).init(self.base.allocator); | 1461 | var test_path = std.ArrayList(u8).init(self.base.allocator); |
| 1444 | defer test_path.deinit(); | 1462 | defer test_path.deinit(); |
| 1445 | for (self.base.options.lib_dirs) |lib_dir_path| { | 1463 | for (self.base.options.lib_dirs) |lib_dir_path| { |
| 1446 | for (self.base.options.system_libs.items()) |link_lib| { | 1464 | for (self.base.options.system_libs.items()) |entry| { |
| 1465 | const link_lib = entry.key; | ||
| 1447 | test_path.shrinkRetainingCapacity(0); | 1466 | test_path.shrinkRetainingCapacity(0); |
| 1448 | const sep = fs.path.sep_str; | 1467 | const sep = fs.path.sep_str; |
| 1449 | try test_path.writer().print("{}" ++ sep ++ "lib{}.so", .{ lib_dir_path, link_lib }); | 1468 | try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, link_lib }); |
| 1450 | fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { | 1469 | fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { |
| 1451 | error.FileNotFound => continue, | 1470 | error.FileNotFound => continue, |
| 1452 | else => |e| return e, | 1471 | else => |e| return e, |
| ... | @@ -1480,9 +1499,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1480,9 +1499,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1480 | 1499 | ||
| 1481 | if (is_dyn_lib) { | 1500 | if (is_dyn_lib) { |
| 1482 | const soname = self.base.options.override_soname orelse if (self.base.options.version) |ver| | 1501 | const soname = self.base.options.override_soname orelse if (self.base.options.version) |ver| |
| 1483 | try std.fmt.allocPrint(arena, "lib{}.so.{}", .{self.base.options.root_name, ver.major}) | 1502 | try std.fmt.allocPrint(arena, "lib{}.so.{}", .{ self.base.options.root_name, ver.major }) |
| 1484 | else | 1503 | else |
| 1485 | try std.fmt.allocPrint(arena, "lib{}.so", .{self.base.options.root_name}); | 1504 | try std.fmt.allocPrint(arena, "lib{}.so", .{self.base.options.root_name}); |
| 1486 | try argv.append("-soname"); | 1505 | try argv.append("-soname"); |
| 1487 | try argv.append(soname); | 1506 | try argv.append(soname); |
| 1488 | 1507 | ||
| ... | @@ -1605,7 +1624,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1605,7 +1624,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1605 | }; | 1624 | }; |
| 1606 | defer stdout_context.data.deinit(); | 1625 | defer stdout_context.data.deinit(); |
| 1607 | const llvm = @import("../llvm.zig"); | 1626 | const llvm = @import("../llvm.zig"); |
| 1608 | const ok = llvm.Link(.ELF, new_argv.ptr, new_argv.len, append_diagnostic, | 1627 | const ok = llvm.Link( |
| 1628 | .ELF, | ||
| 1629 | new_argv.ptr, | ||
| 1630 | new_argv.len, | ||
| 1631 | append_diagnostic, | ||
| 1609 | @ptrToInt(&stdout_context), | 1632 | @ptrToInt(&stdout_context), |
| 1610 | @ptrToInt(&stderr_context), | 1633 | @ptrToInt(&stderr_context), |
| 1611 | ); | 1634 | ); |
| ... | @@ -1631,7 +1654,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { | ... | @@ -1631,7 +1654,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { |
| 1631 | }; | 1654 | }; |
| 1632 | // Again failure here only means an unnecessary cache miss. | 1655 | // Again failure here only means an unnecessary cache miss. |
| 1633 | man.writeManifest() catch |err| { | 1656 | man.writeManifest() catch |err| { |
| 1634 | std.log.warn("failed to write cache manifest when linking: {}", .{ @errorName(err) }); | 1657 | std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); |
| 1635 | }; | 1658 | }; |
| 1636 | // We hang on to this lock so that the output file path can be used without | 1659 | // We hang on to this lock so that the output file path can be used without |
| 1637 | // other processes clobbering it. | 1660 | // other processes clobbering it. |
| ... | @@ -2866,8 +2889,8 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 { | ... | @@ -2866,8 +2889,8 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 { |
| 2866 | const root_src_dir_path_len = if (self.base.options.module.?.root_pkg.root_src_directory.path) |p| p.len else 1; // "." | 2889 | const root_src_dir_path_len = if (self.base.options.module.?.root_pkg.root_src_directory.path) |p| p.len else 1; // "." |
| 2867 | return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + | 2890 | return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + |
| 2868 | directory_count * 8 + file_name_count * 8 + | 2891 | directory_count * 8 + file_name_count * 8 + |
| 2869 | // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like | 2892 | // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like |
| 2870 | // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. | 2893 | // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. |
| 2871 | root_src_dir_path_len + | 2894 | root_src_dir_path_len + |
| 2872 | self.base.options.module.?.root_pkg.root_src_path.len); | 2895 | self.base.options.module.?.root_pkg.root_src_path.len); |
| 2873 | } | 2896 | } |
| ... | @@ -2888,7 +2911,7 @@ fn pwriteDbgLineNops( | ... | @@ -2888,7 +2911,7 @@ fn pwriteDbgLineNops( |
| 2888 | prev_padding_size: usize, | 2911 | prev_padding_size: usize, |
| 2889 | buf: []const u8, | 2912 | buf: []const u8, |
| 2890 | next_padding_size: usize, | 2913 | next_padding_size: usize, |
| 2891 | offset: usize, | 2914 | offset: u64, |
| 2892 | ) !void { | 2915 | ) !void { |
| 2893 | const tracy = trace(@src()); | 2916 | const tracy = trace(@src()); |
| 2894 | defer tracy.end(); | 2917 | defer tracy.end(); |
| ... | @@ -2967,7 +2990,7 @@ fn pwriteDbgInfoNops( | ... | @@ -2967,7 +2990,7 @@ fn pwriteDbgInfoNops( |
| 2967 | buf: []const u8, | 2990 | buf: []const u8, |
| 2968 | next_padding_size: usize, | 2991 | next_padding_size: usize, |
| 2969 | trailing_zero: bool, | 2992 | trailing_zero: bool, |
| 2970 | offset: usize, | 2993 | offset: u64, |
| 2971 | ) !void { | 2994 | ) !void { |
| 2972 | const tracy = trace(@src()); | 2995 | const tracy = trace(@src()); |
| 2973 | defer tracy.end(); | 2996 | defer tracy.end(); |
src/link/MachO.zig+613-165| ... | @@ -27,6 +27,10 @@ const LoadCommand = union(enum) { | ... | @@ -27,6 +27,10 @@ const LoadCommand = union(enum) { |
| 27 | LinkeditData: macho.linkedit_data_command, | 27 | LinkeditData: macho.linkedit_data_command, |
| 28 | Symtab: macho.symtab_command, | 28 | Symtab: macho.symtab_command, |
| 29 | Dysymtab: macho.dysymtab_command, | 29 | Dysymtab: macho.dysymtab_command, |
| 30 | DyldInfo: macho.dyld_info_command, | ||
| 31 | Dylinker: macho.dylinker_command, | ||
| 32 | Dylib: macho.dylib_command, | ||
| 33 | EntryPoint: macho.entry_point_command, | ||
| 30 | 34 | ||
| 31 | pub fn cmdsize(self: LoadCommand) u32 { | 35 | pub fn cmdsize(self: LoadCommand) u32 { |
| 32 | return switch (self) { | 36 | return switch (self) { |
| ... | @@ -34,6 +38,10 @@ const LoadCommand = union(enum) { | ... | @@ -34,6 +38,10 @@ const LoadCommand = union(enum) { |
| 34 | .LinkeditData => |x| x.cmdsize, | 38 | .LinkeditData => |x| x.cmdsize, |
| 35 | .Symtab => |x| x.cmdsize, | 39 | .Symtab => |x| x.cmdsize, |
| 36 | .Dysymtab => |x| x.cmdsize, | 40 | .Dysymtab => |x| x.cmdsize, |
| 41 | .DyldInfo => |x| x.cmdsize, | ||
| 42 | .Dylinker => |x| x.cmdsize, | ||
| 43 | .Dylib => |x| x.cmdsize, | ||
| 44 | .EntryPoint => |x| x.cmdsize, | ||
| 37 | }; | 45 | }; |
| 38 | } | 46 | } |
| 39 | 47 | ||
| ... | @@ -43,6 +51,10 @@ const LoadCommand = union(enum) { | ... | @@ -43,6 +51,10 @@ const LoadCommand = union(enum) { |
| 43 | .LinkeditData => |cmd| writeGeneric(cmd, file, offset), | 51 | .LinkeditData => |cmd| writeGeneric(cmd, file, offset), |
| 44 | .Symtab => |cmd| writeGeneric(cmd, file, offset), | 52 | .Symtab => |cmd| writeGeneric(cmd, file, offset), |
| 45 | .Dysymtab => |cmd| writeGeneric(cmd, file, offset), | 53 | .Dysymtab => |cmd| writeGeneric(cmd, file, offset), |
| 54 | .DyldInfo => |cmd| writeGeneric(cmd, file, offset), | ||
| 55 | .Dylinker => |cmd| writeGeneric(cmd, file, offset), | ||
| 56 | .Dylib => |cmd| writeGeneric(cmd, file, offset), | ||
| 57 | .EntryPoint => |cmd| writeGeneric(cmd, file, offset), | ||
| 46 | }; | 58 | }; |
| 47 | } | 59 | } |
| 48 | 60 | ||
| ... | @@ -56,30 +68,52 @@ base: File, | ... | @@ -56,30 +68,52 @@ base: File, |
| 56 | 68 | ||
| 57 | /// Table of all load commands | 69 | /// Table of all load commands |
| 58 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | 70 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 59 | segment_cmd_index: ?u16 = null, | 71 | /// __PAGEZERO segment |
| 72 | pagezero_segment_cmd_index: ?u16 = null, | ||
| 73 | /// __TEXT segment | ||
| 74 | text_segment_cmd_index: ?u16 = null, | ||
| 75 | /// __DATA segment | ||
| 76 | data_segment_cmd_index: ?u16 = null, | ||
| 77 | /// __LINKEDIT segment | ||
| 78 | linkedit_segment_cmd_index: ?u16 = null, | ||
| 79 | /// Dyld info | ||
| 80 | dyld_info_cmd_index: ?u16 = null, | ||
| 81 | /// Symbol table | ||
| 60 | symtab_cmd_index: ?u16 = null, | 82 | symtab_cmd_index: ?u16 = null, |
| 83 | /// Dynamic symbol table | ||
| 61 | dysymtab_cmd_index: ?u16 = null, | 84 | dysymtab_cmd_index: ?u16 = null, |
| 85 | /// Path to dyld linker | ||
| 86 | dylinker_cmd_index: ?u16 = null, | ||
| 87 | /// Path to libSystem | ||
| 88 | libsystem_cmd_index: ?u16 = null, | ||
| 89 | /// Data-in-code section of __LINKEDIT segment | ||
| 62 | data_in_code_cmd_index: ?u16 = null, | 90 | data_in_code_cmd_index: ?u16 = null, |
| 91 | /// Address to entry point function | ||
| 92 | function_starts_cmd_index: ?u16 = null, | ||
| 93 | /// Main/entry point | ||
| 94 | /// Specifies offset wrt __TEXT segment start address to the main entry point | ||
| 95 | /// of the binary. | ||
| 96 | main_cmd_index: ?u16 = null, | ||
| 63 | 97 | ||
| 64 | /// Table of all sections | 98 | /// Table of all sections |
| 65 | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, | 99 | sections: std.ArrayListUnmanaged(macho.section_64) = .{}, |
| 66 | 100 | ||
| 67 | /// __TEXT segment sections | 101 | /// __TEXT,__text section |
| 68 | text_section_index: ?u16 = null, | 102 | text_section_index: ?u16 = null, |
| 69 | cstring_section_index: ?u16 = null, | ||
| 70 | const_text_section_index: ?u16 = null, | ||
| 71 | stubs_section_index: ?u16 = null, | ||
| 72 | stub_helper_section_index: ?u16 = null, | ||
| 73 | 103 | ||
| 74 | /// __DATA segment sections | 104 | /// __DATA,__got section |
| 75 | got_section_index: ?u16 = null, | 105 | got_section_index: ?u16 = null, |
| 76 | const_data_section_index: ?u16 = null, | ||
| 77 | 106 | ||
| 78 | entry_addr: ?u64 = null, | 107 | entry_addr: ?u64 = null, |
| 79 | 108 | ||
| 80 | /// Table of all symbols used. | 109 | /// Table of all local symbols |
| 81 | /// Internally references string table for names (which are optional). | 110 | /// Internally references string table for names (which are optional). |
| 82 | symbol_table: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | 111 | local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, |
| 112 | /// Table of all defined global symbols | ||
| 113 | global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | ||
| 114 | /// Table of all undefined symbols | ||
| 115 | undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{}, | ||
| 116 | dyld_stub_binder_index: ?u16 = null, | ||
| 83 | 117 | ||
| 84 | /// Table of symbol names aka the string table. | 118 | /// Table of symbol names aka the string table. |
| 85 | string_table: std.ArrayListUnmanaged(u8) = .{}, | 119 | string_table: std.ArrayListUnmanaged(u8) = .{}, |
| ... | @@ -115,19 +149,27 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System"; | ... | @@ -115,19 +149,27 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System"; |
| 115 | const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib"; | 149 | const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib"; |
| 116 | 150 | ||
| 117 | pub const TextBlock = struct { | 151 | pub const TextBlock = struct { |
| 118 | /// Index into the symbol table | 152 | /// Each decl always gets a local symbol with the fully qualified name. |
| 119 | symbol_table_index: ?u32, | 153 | /// The vaddr and size are found here directly. |
| 154 | /// The file offset is found by computing the vaddr offset from the section vaddr | ||
| 155 | /// the symbol references, and adding that to the file offset of the section. | ||
| 156 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or | ||
| 157 | /// offset table entry. | ||
| 158 | local_sym_index: u32, | ||
| 120 | /// Index into offset table | 159 | /// Index into offset table |
| 121 | offset_table_index: ?u32, | 160 | /// This field is undefined for symbols with size = 0. |
| 161 | offset_table_index: u32, | ||
| 122 | /// Size of this text block | 162 | /// Size of this text block |
| 163 | /// Unlike in Elf, we need to store the size of this symbol as part of | ||
| 164 | /// the TextBlock since macho.nlist_64 lacks this information. | ||
| 123 | size: u64, | 165 | size: u64, |
| 124 | /// Points to the previous and next neighbours | 166 | /// Points to the previous and next neighbours |
| 125 | prev: ?*TextBlock, | 167 | prev: ?*TextBlock, |
| 126 | next: ?*TextBlock, | 168 | next: ?*TextBlock, |
| 127 | 169 | ||
| 128 | pub const empty = TextBlock{ | 170 | pub const empty = TextBlock{ |
| 129 | .symbol_table_index = null, | 171 | .local_sym_index = 0, |
| 130 | .offset_table_index = null, | 172 | .offset_table_index = undefined, |
| 131 | .size = 0, | 173 | .size = 0, |
| 132 | .prev = null, | 174 | .prev = null, |
| 133 | .next = null, | 175 | .next = null, |
| ... | @@ -156,6 +198,15 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio | ... | @@ -156,6 +198,15 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 156 | 198 | ||
| 157 | self.base.file = file; | 199 | self.base.file = file; |
| 158 | 200 | ||
| 201 | // Index 0 is always a null symbol. | ||
| 202 | try self.local_symbols.append(allocator, .{ | ||
| 203 | .n_strx = 0, | ||
| 204 | .n_type = 0, | ||
| 205 | .n_sect = 0, | ||
| 206 | .n_desc = 0, | ||
| 207 | .n_value = 0, | ||
| 208 | }); | ||
| 209 | |||
| 159 | switch (options.output_mode) { | 210 | switch (options.output_mode) { |
| 160 | .Exe => {}, | 211 | .Exe => {}, |
| 161 | .Obj => {}, | 212 | .Obj => {}, |
| ... | @@ -196,88 +247,83 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { | ... | @@ -196,88 +247,83 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 196 | const tracy = trace(@src()); | 247 | const tracy = trace(@src()); |
| 197 | defer tracy.end(); | 248 | defer tracy.end(); |
| 198 | 249 | ||
| 250 | // Unfortunately these have to be buffered and done at the end because MachO does not allow | ||
| 251 | // mixing local, global and undefined symbols within a symbol table. | ||
| 252 | try self.writeAllGlobalSymbols(); | ||
| 253 | try self.writeAllUndefSymbols(); | ||
| 254 | |||
| 255 | try self.writeStringTable(); | ||
| 256 | |||
| 199 | switch (self.base.options.output_mode) { | 257 | switch (self.base.options.output_mode) { |
| 200 | .Exe => { | 258 | .Exe => { |
| 201 | var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); | 259 | if (self.entry_addr) |addr| { |
| 202 | { | 260 | // Write export trie. |
| 203 | // Specify path to dynamic linker dyld | 261 | try self.writeExportTrie(); |
| 204 | const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH)); | 262 | |
| 205 | const load_dylinker = [1]macho.dylinker_command{ | 263 | // Update LC_MAIN with entry offset |
| 206 | .{ | 264 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 207 | .cmd = macho.LC_LOAD_DYLINKER, | 265 | const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint; |
| 208 | .cmdsize = cmdsize, | 266 | main_cmd.entryoff = addr - text_segment.vmaddr; |
| 209 | .name = @sizeOf(macho.dylinker_command), | ||
| 210 | }, | ||
| 211 | }; | ||
| 212 | |||
| 213 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), last_cmd_offset); | ||
| 214 | |||
| 215 | const file_offset = last_cmd_offset + @sizeOf(macho.dylinker_command); | ||
| 216 | try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset); | ||
| 217 | |||
| 218 | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset); | ||
| 219 | last_cmd_offset += cmdsize; | ||
| 220 | } | 267 | } |
| 221 | 268 | ||
| 222 | { | 269 | { |
| 223 | // Link against libSystem | 270 | // Update dynamic symbol table. |
| 224 | const cmdsize = commandSize(@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH)); | 271 | const nlocals = @intCast(u32, self.local_symbols.items.len); |
| 225 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. | 272 | const nglobals = @intCast(u32, self.global_symbols.items.len); |
| 226 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0. | 273 | const nundefs = @intCast(u32, self.undef_symbols.items.len); |
| 227 | const min_version = 0x10000; | 274 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; |
| 228 | const dylib = .{ | 275 | dysymtab.nlocalsym = nlocals; |
| 229 | .name = @sizeOf(macho.dylib_command), | 276 | dysymtab.iextdefsym = nlocals; |
| 230 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files | 277 | dysymtab.nextdefsym = nglobals; |
| 231 | .current_version = min_version, | 278 | dysymtab.iundefsym = nlocals + nglobals; |
| 232 | .compatibility_version = min_version, | 279 | dysymtab.nundefsym = nundefs; |
| 233 | }; | ||
| 234 | const load_dylib = [1]macho.dylib_command{ | ||
| 235 | .{ | ||
| 236 | .cmd = macho.LC_LOAD_DYLIB, | ||
| 237 | .cmdsize = cmdsize, | ||
| 238 | .dylib = dylib, | ||
| 239 | }, | ||
| 240 | }; | ||
| 241 | |||
| 242 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), last_cmd_offset); | ||
| 243 | |||
| 244 | const file_offset = last_cmd_offset + @sizeOf(macho.dylib_command); | ||
| 245 | try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset); | ||
| 246 | |||
| 247 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset); | ||
| 248 | last_cmd_offset += cmdsize; | ||
| 249 | } | 280 | } |
| 250 | }, | ||
| 251 | .Obj => { | ||
| 252 | { | 281 | { |
| 253 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 282 | // Write path to dyld loader. |
| 254 | symtab.nsyms = @intCast(u32, self.symbol_table.items.len); | 283 | var off: usize = @sizeOf(macho.mach_header_64); |
| 255 | const allocated_size = self.allocatedSize(symtab.stroff); | 284 | for (self.load_commands.items) |cmd| { |
| 256 | const needed_size = self.string_table.items.len; | 285 | if (cmd == .Dylinker) break; |
| 257 | log.debug("allocated_size = 0x{x}, needed_size = 0x{x}\n", .{ allocated_size, needed_size }); | 286 | off += cmd.cmdsize(); |
| 258 | |||
| 259 | if (needed_size > allocated_size) { | ||
| 260 | symtab.strsize = 0; | ||
| 261 | symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1)); | ||
| 262 | } | 287 | } |
| 263 | symtab.strsize = @intCast(u32, needed_size); | 288 | const cmd = &self.load_commands.items[self.dylinker_cmd_index.?].Dylinker; |
| 264 | 289 | off += cmd.name; | |
| 265 | log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize }); | 290 | const padding = cmd.cmdsize - @sizeOf(macho.dylinker_command); |
| 266 | 291 | log.debug("writing LC_LOAD_DYLINKER padding of size {} at 0x{x}\n", .{ padding, off }); | |
| 267 | try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff); | 292 | try self.addPadding(padding, off); |
| 293 | log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off}); | ||
| 294 | try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off); | ||
| 268 | } | 295 | } |
| 269 | 296 | { | |
| 270 | var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); | 297 | // Write path to libSystem. |
| 271 | for (self.load_commands.items) |cmd| { | 298 | var off: usize = @sizeOf(macho.mach_header_64); |
| 272 | try cmd.write(&self.base.file.?, last_cmd_offset); | 299 | for (self.load_commands.items) |cmd| { |
| 273 | last_cmd_offset += cmd.cmdsize(); | 300 | if (cmd == .Dylib) break; |
| 301 | off += cmd.cmdsize(); | ||
| 302 | } | ||
| 303 | const cmd = &self.load_commands.items[self.libsystem_cmd_index.?].Dylib; | ||
| 304 | off += cmd.dylib.name; | ||
| 305 | const padding = cmd.cmdsize - @sizeOf(macho.dylib_command); | ||
| 306 | log.debug("writing LC_LOAD_DYLIB padding of size {} at 0x{x}\n", .{ padding, off }); | ||
| 307 | try self.addPadding(padding, off); | ||
| 308 | log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off}); | ||
| 309 | try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off); | ||
| 274 | } | 310 | } |
| 275 | const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); | ||
| 276 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items), off); | ||
| 277 | }, | 311 | }, |
| 312 | .Obj => {}, | ||
| 278 | .Lib => return error.TODOImplementWritingLibFiles, | 313 | .Lib => return error.TODOImplementWritingLibFiles, |
| 279 | } | 314 | } |
| 280 | 315 | ||
| 316 | if (self.cmd_table_dirty) try self.writeCmdHeaders(); | ||
| 317 | |||
| 318 | { | ||
| 319 | // Update symbol table. | ||
| 320 | const nlocals = @intCast(u32, self.local_symbols.items.len); | ||
| 321 | const nglobals = @intCast(u32, self.global_symbols.items.len); | ||
| 322 | const nundefs = @intCast(u32, self.undef_symbols.items.len); | ||
| 323 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | ||
| 324 | symtab.nsyms = nlocals + nglobals + nundefs; | ||
| 325 | } | ||
| 326 | |||
| 281 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { | 327 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { |
| 282 | log.debug("flushing. no_entry_point_found = true\n", .{}); | 328 | log.debug("flushing. no_entry_point_found = true\n", .{}); |
| 283 | self.error_flags.no_entry_point_found = true; | 329 | self.error_flags.no_entry_point_found = true; |
| ... | @@ -669,32 +715,34 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { | ... | @@ -669,32 +715,34 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { |
| 669 | pub fn deinit(self: *MachO) void { | 715 | pub fn deinit(self: *MachO) void { |
| 670 | self.offset_table.deinit(self.base.allocator); | 716 | self.offset_table.deinit(self.base.allocator); |
| 671 | self.string_table.deinit(self.base.allocator); | 717 | self.string_table.deinit(self.base.allocator); |
| 672 | self.symbol_table.deinit(self.base.allocator); | 718 | self.undef_symbols.deinit(self.base.allocator); |
| 719 | self.global_symbols.deinit(self.base.allocator); | ||
| 720 | self.local_symbols.deinit(self.base.allocator); | ||
| 673 | self.sections.deinit(self.base.allocator); | 721 | self.sections.deinit(self.base.allocator); |
| 674 | self.load_commands.deinit(self.base.allocator); | 722 | self.load_commands.deinit(self.base.allocator); |
| 675 | } | 723 | } |
| 676 | 724 | ||
| 677 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { | 725 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { |
| 678 | if (decl.link.macho.symbol_table_index) |_| return; | 726 | if (decl.link.macho.local_sym_index != 0) return; |
| 679 | 727 | ||
| 680 | try self.symbol_table.ensureCapacity(self.base.allocator, self.symbol_table.items.len + 1); | 728 | try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1); |
| 681 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); | 729 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); |
| 682 | 730 | ||
| 683 | log.debug("allocating symbol index {} for {}\n", .{ self.symbol_table.items.len, decl.name }); | 731 | log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name }); |
| 684 | decl.link.macho.symbol_table_index = @intCast(u32, self.symbol_table.items.len); | 732 | decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len); |
| 685 | _ = self.symbol_table.addOneAssumeCapacity(); | 733 | _ = self.local_symbols.addOneAssumeCapacity(); |
| 686 | 734 | ||
| 687 | decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len); | 735 | decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len); |
| 688 | _ = self.offset_table.addOneAssumeCapacity(); | 736 | _ = self.offset_table.addOneAssumeCapacity(); |
| 689 | 737 | ||
| 690 | self.symbol_table.items[decl.link.macho.symbol_table_index.?] = .{ | 738 | self.local_symbols.items[decl.link.macho.local_sym_index] = .{ |
| 691 | .n_strx = 0, | 739 | .n_strx = 0, |
| 692 | .n_type = 0, | 740 | .n_type = 0, |
| 693 | .n_sect = 0, | 741 | .n_sect = 0, |
| 694 | .n_desc = 0, | 742 | .n_desc = 0, |
| 695 | .n_value = 0, | 743 | .n_value = 0, |
| 696 | }; | 744 | }; |
| 697 | self.offset_table.items[decl.link.macho.offset_table_index.?] = 0; | 745 | self.offset_table.items[decl.link.macho.offset_table_index] = 0; |
| 698 | } | 746 | } |
| 699 | 747 | ||
| 700 | pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | 748 | pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| ... | @@ -716,16 +764,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -716,16 +764,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 716 | return; | 764 | return; |
| 717 | }, | 765 | }, |
| 718 | }; | 766 | }; |
| 719 | log.debug("generated code {}\n", .{code}); | ||
| 720 | 767 | ||
| 721 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); | 768 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); |
| 722 | const symbol = &self.symbol_table.items[decl.link.macho.symbol_table_index.?]; | 769 | const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index]; |
| 723 | 770 | ||
| 724 | const decl_name = mem.spanZ(decl.name); | 771 | const decl_name = mem.spanZ(decl.name); |
| 725 | const name_str_index = try self.makeString(decl_name); | 772 | const name_str_index = try self.makeString(decl_name); |
| 726 | const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment); | 773 | const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment); |
| 727 | log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr }); | 774 | log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr }); |
| 728 | log.debug("updated text section {}\n", .{self.sections.items[self.text_section_index.?]}); | ||
| 729 | 775 | ||
| 730 | symbol.* = .{ | 776 | symbol.* = .{ |
| 731 | .n_strx = name_str_index, | 777 | .n_strx = name_str_index, |
| ... | @@ -734,18 +780,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { | ... | @@ -734,18 +780,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 734 | .n_desc = 0, | 780 | .n_desc = 0, |
| 735 | .n_value = addr, | 781 | .n_value = addr, |
| 736 | }; | 782 | }; |
| 783 | self.offset_table.items[decl.link.macho.offset_table_index] = addr; | ||
| 737 | 784 | ||
| 738 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | 785 | try self.writeSymbol(decl.link.macho.local_sym_index); |
| 739 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | 786 | try self.writeOffsetTableEntry(decl.link.macho.offset_table_index); |
| 740 | try self.updateDeclExports(module, decl, decl_exports); | ||
| 741 | try self.writeSymbol(decl.link.macho.symbol_table_index.?); | ||
| 742 | 787 | ||
| 743 | const text_section = self.sections.items[self.text_section_index.?]; | 788 | const text_section = self.sections.items[self.text_section_index.?]; |
| 744 | const section_offset = symbol.n_value - text_section.addr; | 789 | const section_offset = symbol.n_value - text_section.addr; |
| 745 | const file_offset = text_section.offset + section_offset; | 790 | const file_offset = text_section.offset + section_offset; |
| 746 | log.debug("file_offset 0x{x}\n", .{file_offset}); | ||
| 747 | 791 | ||
| 748 | try self.base.file.?.pwriteAll(code, file_offset); | 792 | try self.base.file.?.pwriteAll(code, file_offset); |
| 793 | |||
| 794 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | ||
| 795 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | ||
| 796 | try self.updateDeclExports(module, decl, decl_exports); | ||
| 749 | } | 797 | } |
| 750 | 798 | ||
| 751 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {} | 799 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {} |
| ... | @@ -759,34 +807,89 @@ pub fn updateDeclExports( | ... | @@ -759,34 +807,89 @@ pub fn updateDeclExports( |
| 759 | const tracy = trace(@src()); | 807 | const tracy = trace(@src()); |
| 760 | defer tracy.end(); | 808 | defer tracy.end(); |
| 761 | 809 | ||
| 762 | if (decl.link.macho.symbol_table_index == null) return; | 810 | try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len); |
| 763 | 811 | if (decl.link.macho.local_sym_index == 0) return; | |
| 764 | const decl_sym = &self.symbol_table.items[decl.link.macho.symbol_table_index.?]; | 812 | const decl_sym = &self.local_symbols.items[decl.link.macho.local_sym_index]; |
| 765 | // TODO implement | 813 | |
| 766 | if (exports.len == 0) return; | 814 | for (exports) |exp| { |
| 767 | 815 | if (exp.options.section) |section_name| { | |
| 768 | const exp = exports[0]; | 816 | if (!mem.eql(u8, section_name, "__text")) { |
| 769 | self.entry_addr = decl_sym.n_value; | 817 | try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); |
| 770 | decl_sym.n_type |= macho.N_EXT; | 818 | module.failed_exports.putAssumeCapacityNoClobber( |
| 771 | exp.link.sym_index = 0; | 819 | exp, |
| 820 | try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}), | ||
| 821 | ); | ||
| 822 | continue; | ||
| 823 | } | ||
| 824 | } | ||
| 825 | const n_desc = switch (exp.options.linkage) { | ||
| 826 | .Internal => macho.REFERENCE_FLAG_PRIVATE_DEFINED, | ||
| 827 | .Strong => blk: { | ||
| 828 | if (mem.eql(u8, exp.options.name, "_start")) { | ||
| 829 | self.entry_addr = decl_sym.n_value; | ||
| 830 | } | ||
| 831 | break :blk macho.REFERENCE_FLAG_DEFINED; | ||
| 832 | }, | ||
| 833 | .Weak => macho.N_WEAK_REF, | ||
| 834 | .LinkOnce => { | ||
| 835 | try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); | ||
| 836 | module.failed_exports.putAssumeCapacityNoClobber( | ||
| 837 | exp, | ||
| 838 | try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}), | ||
| 839 | ); | ||
| 840 | continue; | ||
| 841 | }, | ||
| 842 | }; | ||
| 843 | const n_type = decl_sym.n_type | macho.N_EXT; | ||
| 844 | if (exp.link.sym_index) |i| { | ||
| 845 | const sym = &self.global_symbols.items[i]; | ||
| 846 | sym.* = .{ | ||
| 847 | .n_strx = try self.updateString(sym.n_strx, exp.options.name), | ||
| 848 | .n_type = n_type, | ||
| 849 | .n_sect = @intCast(u8, self.text_section_index.?) + 1, | ||
| 850 | .n_desc = n_desc, | ||
| 851 | .n_value = decl_sym.n_value, | ||
| 852 | }; | ||
| 853 | } else { | ||
| 854 | const name_str_index = try self.makeString(exp.options.name); | ||
| 855 | _ = self.global_symbols.addOneAssumeCapacity(); | ||
| 856 | const i = self.global_symbols.items.len - 1; | ||
| 857 | self.global_symbols.items[i] = .{ | ||
| 858 | .n_strx = name_str_index, | ||
| 859 | .n_type = n_type, | ||
| 860 | .n_sect = @intCast(u8, self.text_section_index.?) + 1, | ||
| 861 | .n_desc = n_desc, | ||
| 862 | .n_value = decl_sym.n_value, | ||
| 863 | }; | ||
| 864 | |||
| 865 | exp.link.sym_index = @intCast(u32, i); | ||
| 866 | } | ||
| 867 | } | ||
| 772 | } | 868 | } |
| 773 | 869 | ||
| 774 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {} | 870 | pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {} |
| 775 | 871 | ||
| 776 | pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { | 872 | pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 { |
| 777 | return self.symbol_table.items[decl.link.macho.symbol_table_index.?].n_value; | 873 | assert(decl.link.macho.local_sym_index != 0); |
| 874 | return self.local_symbols.items[decl.link.macho.local_sym_index].n_value; | ||
| 778 | } | 875 | } |
| 779 | 876 | ||
| 780 | pub fn populateMissingMetadata(self: *MachO) !void { | 877 | pub fn populateMissingMetadata(self: *MachO) !void { |
| 781 | if (self.segment_cmd_index == null) { | 878 | switch (self.base.options.output_mode) { |
| 782 | self.segment_cmd_index = @intCast(u16, self.load_commands.items.len); | 879 | .Exe => {}, |
| 880 | .Obj => return error.TODOImplementWritingObjFiles, | ||
| 881 | .Lib => return error.TODOImplementWritingLibFiles, | ||
| 882 | } | ||
| 883 | |||
| 884 | if (self.pagezero_segment_cmd_index == null) { | ||
| 885 | self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | ||
| 783 | try self.load_commands.append(self.base.allocator, .{ | 886 | try self.load_commands.append(self.base.allocator, .{ |
| 784 | .Segment = .{ | 887 | .Segment = .{ |
| 785 | .cmd = macho.LC_SEGMENT_64, | 888 | .cmd = macho.LC_SEGMENT_64, |
| 786 | .cmdsize = @sizeOf(macho.segment_command_64), | 889 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 787 | .segname = makeStaticString(""), | 890 | .segname = makeStaticString("__PAGEZERO"), |
| 788 | .vmaddr = 0, | 891 | .vmaddr = 0, |
| 789 | .vmsize = 0, | 892 | .vmsize = 0x100000000, // size always set to 4GB |
| 790 | .fileoff = 0, | 893 | .fileoff = 0, |
| 791 | .filesize = 0, | 894 | .filesize = 0, |
| 792 | .maxprot = 0, | 895 | .maxprot = 0, |
| ... | @@ -797,28 +900,34 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -797,28 +900,34 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 797 | }); | 900 | }); |
| 798 | self.cmd_table_dirty = true; | 901 | self.cmd_table_dirty = true; |
| 799 | } | 902 | } |
| 800 | if (self.symtab_cmd_index == null) { | 903 | if (self.text_segment_cmd_index == null) { |
| 801 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); | 904 | self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len); |
| 905 | const prot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE; | ||
| 802 | try self.load_commands.append(self.base.allocator, .{ | 906 | try self.load_commands.append(self.base.allocator, .{ |
| 803 | .Symtab = .{ | 907 | .Segment = .{ |
| 804 | .cmd = macho.LC_SYMTAB, | 908 | .cmd = macho.LC_SEGMENT_64, |
| 805 | .cmdsize = @sizeOf(macho.symtab_command), | 909 | .cmdsize = @sizeOf(macho.segment_command_64), |
| 806 | .symoff = 0, | 910 | .segname = makeStaticString("__TEXT"), |
| 807 | .nsyms = 0, | 911 | .vmaddr = 0x100000000, // always starts at 4GB |
| 808 | .stroff = 0, | 912 | .vmsize = 0, |
| 809 | .strsize = 0, | 913 | .fileoff = 0, |
| 914 | .filesize = 0, | ||
| 915 | .maxprot = prot, | ||
| 916 | .initprot = prot, | ||
| 917 | .nsects = 0, | ||
| 918 | .flags = 0, | ||
| 810 | }, | 919 | }, |
| 811 | }); | 920 | }); |
| 812 | self.cmd_table_dirty = true; | 921 | self.cmd_table_dirty = true; |
| 813 | } | 922 | } |
| 814 | if (self.text_section_index == null) { | 923 | if (self.text_section_index == null) { |
| 815 | self.text_section_index = @intCast(u16, self.sections.items.len); | 924 | self.text_section_index = @intCast(u16, self.sections.items.len); |
| 816 | const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment; | 925 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; |
| 817 | segment.cmdsize += @sizeOf(macho.section_64); | 926 | text_segment.cmdsize += @sizeOf(macho.section_64); |
| 818 | segment.nsects += 1; | 927 | text_segment.nsects += 1; |
| 819 | 928 | ||
| 820 | const file_size = self.base.options.program_code_size_hint; | 929 | const file_size = mem.alignForwardGeneric(u64, self.base.options.program_code_size_hint, 0x1000); |
| 821 | const off = @intCast(u32, self.findFreeSpace(file_size, 1)); | 930 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); // TODO maybe findFreeSpace should return u32 directly? |
| 822 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; | 931 | const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS; |
| 823 | 932 | ||
| 824 | log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 933 | log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| ... | @@ -826,10 +935,10 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -826,10 +935,10 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 826 | try self.sections.append(self.base.allocator, .{ | 935 | try self.sections.append(self.base.allocator, .{ |
| 827 | .sectname = makeStaticString("__text"), | 936 | .sectname = makeStaticString("__text"), |
| 828 | .segname = makeStaticString("__TEXT"), | 937 | .segname = makeStaticString("__TEXT"), |
| 829 | .addr = 0, | 938 | .addr = text_segment.vmaddr + off, |
| 830 | .size = file_size, | 939 | .size = file_size, |
| 831 | .offset = off, | 940 | .offset = off, |
| 832 | .@"align" = 0x1000, | 941 | .@"align" = 12, // 2^12 = 4096 |
| 833 | .reloff = 0, | 942 | .reloff = 0, |
| 834 | .nreloc = 0, | 943 | .nreloc = 0, |
| 835 | .flags = flags, | 944 | .flags = flags, |
| ... | @@ -838,43 +947,256 @@ pub fn populateMissingMetadata(self: *MachO) !void { | ... | @@ -838,43 +947,256 @@ pub fn populateMissingMetadata(self: *MachO) !void { |
| 838 | .reserved3 = 0, | 947 | .reserved3 = 0, |
| 839 | }); | 948 | }); |
| 840 | 949 | ||
| 841 | segment.vmsize += file_size; | 950 | text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section. |
| 842 | segment.filesize += file_size; | 951 | text_segment.filesize = file_size + off; |
| 843 | segment.fileoff = off; | 952 | } |
| 953 | if (self.data_segment_cmd_index == null) { | ||
| 954 | self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | ||
| 955 | const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; | ||
| 956 | const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE; | ||
| 957 | try self.load_commands.append(self.base.allocator, .{ | ||
| 958 | .Segment = .{ | ||
| 959 | .cmd = macho.LC_SEGMENT_64, | ||
| 960 | .cmdsize = @sizeOf(macho.segment_command_64), | ||
| 961 | .segname = makeStaticString("__DATA"), | ||
| 962 | .vmaddr = text_segment.vmaddr + text_segment.vmsize, | ||
| 963 | .vmsize = 0, | ||
| 964 | .fileoff = 0, | ||
| 965 | .filesize = 0, | ||
| 966 | .maxprot = prot, | ||
| 967 | .initprot = prot, | ||
| 968 | .nsects = 0, | ||
| 969 | .flags = 0, | ||
| 970 | }, | ||
| 971 | }); | ||
| 972 | self.cmd_table_dirty = true; | ||
| 973 | } | ||
| 974 | if (self.got_section_index == null) { | ||
| 975 | self.got_section_index = @intCast(u16, self.sections.items.len); | ||
| 976 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | ||
| 977 | data_segment.cmdsize += @sizeOf(macho.section_64); | ||
| 978 | data_segment.nsects += 1; | ||
| 979 | |||
| 980 | const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint; | ||
| 981 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); | ||
| 982 | |||
| 983 | log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | ||
| 984 | |||
| 985 | try self.sections.append(self.base.allocator, .{ | ||
| 986 | .sectname = makeStaticString("__got"), | ||
| 987 | .segname = makeStaticString("__DATA"), | ||
| 988 | .addr = data_segment.vmaddr, | ||
| 989 | .size = file_size, | ||
| 990 | .offset = off, | ||
| 991 | .@"align" = 3, // 2^3 = 8 | ||
| 992 | .reloff = 0, | ||
| 993 | .nreloc = 0, | ||
| 994 | .flags = macho.S_REGULAR, | ||
| 995 | .reserved1 = 0, | ||
| 996 | .reserved2 = 0, | ||
| 997 | .reserved3 = 0, | ||
| 998 | }); | ||
| 844 | 999 | ||
| 845 | log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]}); | 1000 | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); |
| 1001 | data_segment.vmsize = segment_size; | ||
| 1002 | data_segment.filesize = segment_size; | ||
| 1003 | data_segment.fileoff = off; | ||
| 1004 | } | ||
| 1005 | if (self.linkedit_segment_cmd_index == null) { | ||
| 1006 | self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len); | ||
| 1007 | const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; | ||
| 1008 | const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE; | ||
| 1009 | try self.load_commands.append(self.base.allocator, .{ | ||
| 1010 | .Segment = .{ | ||
| 1011 | .cmd = macho.LC_SEGMENT_64, | ||
| 1012 | .cmdsize = @sizeOf(macho.segment_command_64), | ||
| 1013 | .segname = makeStaticString("__LINKEDIT"), | ||
| 1014 | .vmaddr = data_segment.vmaddr + data_segment.vmsize, | ||
| 1015 | .vmsize = 0, | ||
| 1016 | .fileoff = 0, | ||
| 1017 | .filesize = 0, | ||
| 1018 | .maxprot = prot, | ||
| 1019 | .initprot = prot, | ||
| 1020 | .nsects = 0, | ||
| 1021 | .flags = 0, | ||
| 1022 | }, | ||
| 1023 | }); | ||
| 1024 | self.cmd_table_dirty = true; | ||
| 1025 | } | ||
| 1026 | if (self.dyld_info_cmd_index == null) { | ||
| 1027 | self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); | ||
| 1028 | try self.load_commands.append(self.base.allocator, .{ | ||
| 1029 | .DyldInfo = .{ | ||
| 1030 | .cmd = macho.LC_DYLD_INFO_ONLY, | ||
| 1031 | .cmdsize = @sizeOf(macho.dyld_info_command), | ||
| 1032 | .rebase_off = 0, | ||
| 1033 | .rebase_size = 0, | ||
| 1034 | .bind_off = 0, | ||
| 1035 | .bind_size = 0, | ||
| 1036 | .weak_bind_off = 0, | ||
| 1037 | .weak_bind_size = 0, | ||
| 1038 | .lazy_bind_off = 0, | ||
| 1039 | .lazy_bind_size = 0, | ||
| 1040 | .export_off = 0, | ||
| 1041 | .export_size = 0, | ||
| 1042 | }, | ||
| 1043 | }); | ||
| 1044 | self.cmd_table_dirty = true; | ||
| 1045 | } | ||
| 1046 | if (self.symtab_cmd_index == null) { | ||
| 1047 | self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); | ||
| 1048 | try self.load_commands.append(self.base.allocator, .{ | ||
| 1049 | .Symtab = .{ | ||
| 1050 | .cmd = macho.LC_SYMTAB, | ||
| 1051 | .cmdsize = @sizeOf(macho.symtab_command), | ||
| 1052 | .symoff = 0, | ||
| 1053 | .nsyms = 0, | ||
| 1054 | .stroff = 0, | ||
| 1055 | .strsize = 0, | ||
| 1056 | }, | ||
| 1057 | }); | ||
| 1058 | self.cmd_table_dirty = true; | ||
| 1059 | } | ||
| 1060 | if (self.dysymtab_cmd_index == null) { | ||
| 1061 | self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); | ||
| 1062 | try self.load_commands.append(self.base.allocator, .{ | ||
| 1063 | .Dysymtab = .{ | ||
| 1064 | .cmd = macho.LC_DYSYMTAB, | ||
| 1065 | .cmdsize = @sizeOf(macho.dysymtab_command), | ||
| 1066 | .ilocalsym = 0, | ||
| 1067 | .nlocalsym = 0, | ||
| 1068 | .iextdefsym = 0, | ||
| 1069 | .nextdefsym = 0, | ||
| 1070 | .iundefsym = 0, | ||
| 1071 | .nundefsym = 0, | ||
| 1072 | .tocoff = 0, | ||
| 1073 | .ntoc = 0, | ||
| 1074 | .modtaboff = 0, | ||
| 1075 | .nmodtab = 0, | ||
| 1076 | .extrefsymoff = 0, | ||
| 1077 | .nextrefsyms = 0, | ||
| 1078 | .indirectsymoff = 0, | ||
| 1079 | .nindirectsyms = 0, | ||
| 1080 | .extreloff = 0, | ||
| 1081 | .nextrel = 0, | ||
| 1082 | .locreloff = 0, | ||
| 1083 | .nlocrel = 0, | ||
| 1084 | }, | ||
| 1085 | }); | ||
| 1086 | self.cmd_table_dirty = true; | ||
| 1087 | } | ||
| 1088 | if (self.dylinker_cmd_index == null) { | ||
| 1089 | self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len); | ||
| 1090 | const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), @sizeOf(u64)); | ||
| 1091 | try self.load_commands.append(self.base.allocator, .{ | ||
| 1092 | .Dylinker = .{ | ||
| 1093 | .cmd = macho.LC_LOAD_DYLINKER, | ||
| 1094 | .cmdsize = @intCast(u32, cmdsize), | ||
| 1095 | .name = @sizeOf(macho.dylinker_command), | ||
| 1096 | }, | ||
| 1097 | }); | ||
| 1098 | self.cmd_table_dirty = true; | ||
| 1099 | } | ||
| 1100 | if (self.libsystem_cmd_index == null) { | ||
| 1101 | self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len); | ||
| 1102 | const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), @sizeOf(u64)); | ||
| 1103 | // TODO Find a way to work out runtime version from the OS version triple stored in std.Target. | ||
| 1104 | // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0. | ||
| 1105 | const min_version = 0x10000; | ||
| 1106 | const dylib = .{ | ||
| 1107 | .name = @sizeOf(macho.dylib_command), | ||
| 1108 | .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files | ||
| 1109 | .current_version = min_version, | ||
| 1110 | .compatibility_version = min_version, | ||
| 1111 | }; | ||
| 1112 | try self.load_commands.append(self.base.allocator, .{ | ||
| 1113 | .Dylib = .{ | ||
| 1114 | .cmd = macho.LC_LOAD_DYLIB, | ||
| 1115 | .cmdsize = @intCast(u32, cmdsize), | ||
| 1116 | .dylib = dylib, | ||
| 1117 | }, | ||
| 1118 | }); | ||
| 1119 | self.cmd_table_dirty = true; | ||
| 1120 | } | ||
| 1121 | if (self.main_cmd_index == null) { | ||
| 1122 | self.main_cmd_index = @intCast(u16, self.load_commands.items.len); | ||
| 1123 | try self.load_commands.append(self.base.allocator, .{ | ||
| 1124 | .EntryPoint = .{ | ||
| 1125 | .cmd = macho.LC_MAIN, | ||
| 1126 | .cmdsize = @sizeOf(macho.entry_point_command), | ||
| 1127 | .entryoff = 0x0, | ||
| 1128 | .stacksize = 0, | ||
| 1129 | }, | ||
| 1130 | }); | ||
| 1131 | self.cmd_table_dirty = true; | ||
| 1132 | } | ||
| 1133 | { | ||
| 1134 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | ||
| 1135 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; | ||
| 1136 | if (dyld_info.export_off == 0) { | ||
| 1137 | const nsyms = self.base.options.symbol_count_hint; | ||
| 1138 | const file_size = @sizeOf(u64) * nsyms; | ||
| 1139 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); | ||
| 1140 | log.debug("found export trie free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | ||
| 1141 | dyld_info.export_off = off; | ||
| 1142 | dyld_info.export_size = @intCast(u32, file_size); | ||
| 1143 | |||
| 1144 | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); | ||
| 1145 | linkedit.vmsize += segment_size; | ||
| 1146 | linkedit.fileoff = off; | ||
| 1147 | } | ||
| 846 | } | 1148 | } |
| 847 | { | 1149 | { |
| 1150 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | ||
| 848 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 1151 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 849 | if (symtab.symoff == 0) { | 1152 | if (symtab.symoff == 0) { |
| 850 | const p_align = @sizeOf(macho.nlist_64); | ||
| 851 | const nsyms = self.base.options.symbol_count_hint; | 1153 | const nsyms = self.base.options.symbol_count_hint; |
| 852 | const file_size = p_align * nsyms; | 1154 | const file_size = @sizeOf(macho.nlist_64) * nsyms; |
| 853 | const off = @intCast(u32, self.findFreeSpace(file_size, p_align)); | 1155 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); |
| 854 | log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 1156 | log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 855 | symtab.symoff = off; | 1157 | symtab.symoff = off; |
| 856 | symtab.nsyms = @intCast(u32, nsyms); | 1158 | symtab.nsyms = @intCast(u32, nsyms); |
| 1159 | |||
| 1160 | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); | ||
| 1161 | linkedit.vmsize += segment_size; | ||
| 857 | } | 1162 | } |
| 858 | if (symtab.stroff == 0) { | 1163 | if (symtab.stroff == 0) { |
| 859 | try self.string_table.append(self.base.allocator, 0); | 1164 | try self.string_table.append(self.base.allocator, 0); |
| 860 | const file_size = @intCast(u32, self.string_table.items.len); | 1165 | const file_size = @intCast(u32, self.string_table.items.len); |
| 861 | const off = @intCast(u32, self.findFreeSpace(file_size, 1)); | 1166 | const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); |
| 862 | log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | 1167 | log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 863 | symtab.stroff = off; | 1168 | symtab.stroff = off; |
| 864 | symtab.strsize = file_size; | 1169 | symtab.strsize = file_size; |
| 1170 | |||
| 1171 | const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000); | ||
| 1172 | linkedit.vmsize += segment_size; | ||
| 865 | } | 1173 | } |
| 866 | } | 1174 | } |
| 1175 | if (self.dyld_stub_binder_index == null) { | ||
| 1176 | self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len); | ||
| 1177 | const name = try self.makeString("dyld_stub_binder"); | ||
| 1178 | try self.undef_symbols.append(self.base.allocator, .{ | ||
| 1179 | .n_strx = name, | ||
| 1180 | .n_type = macho.N_UNDF | macho.N_EXT, | ||
| 1181 | .n_sect = 0, | ||
| 1182 | .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER, | ||
| 1183 | .n_value = 0, | ||
| 1184 | }); | ||
| 1185 | } | ||
| 867 | } | 1186 | } |
| 868 | 1187 | ||
| 869 | fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { | 1188 | fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { |
| 870 | const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment; | ||
| 871 | const text_section = &self.sections.items[self.text_section_index.?]; | 1189 | const text_section = &self.sections.items[self.text_section_index.?]; |
| 872 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; | 1190 | const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den; |
| 873 | 1191 | ||
| 874 | var block_placement: ?*TextBlock = null; | 1192 | var block_placement: ?*TextBlock = null; |
| 875 | const addr = blk: { | 1193 | const addr = blk: { |
| 876 | if (self.last_text_block) |last| { | 1194 | if (self.last_text_block) |last| { |
| 877 | const last_symbol = self.symbol_table.items[last.symbol_table_index.?]; | 1195 | const last_symbol = self.local_symbols.items[last.local_sym_index]; |
| 1196 | // TODO pad out with NOPs and reenable | ||
| 1197 | // const ideal_capacity = last.size * alloc_num / alloc_den; | ||
| 1198 | // const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity; | ||
| 1199 | // const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment); | ||
| 878 | const end_addr = last_symbol.n_value + last.size; | 1200 | const end_addr = last_symbol.n_value + last.size; |
| 879 | const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment); | 1201 | const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment); |
| 880 | block_placement = last; | 1202 | block_placement = last; |
| ... | @@ -883,22 +1205,15 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, | ... | @@ -883,22 +1205,15 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, |
| 883 | break :blk text_section.addr; | 1205 | break :blk text_section.addr; |
| 884 | } | 1206 | } |
| 885 | }; | 1207 | }; |
| 886 | log.debug("computed symbol address 0x{x}\n", .{addr}); | ||
| 887 | 1208 | ||
| 888 | const expand_text_section = block_placement == null or block_placement.?.next == null; | 1209 | const expand_text_section = block_placement == null or block_placement.?.next == null; |
| 889 | if (expand_text_section) { | 1210 | if (expand_text_section) { |
| 890 | const text_capacity = self.allocatedSize(text_section.offset); | 1211 | const text_capacity = self.allocatedSize(text_section.offset); |
| 891 | const needed_size = (addr + new_block_size) - text_section.addr; | 1212 | const needed_size = (addr + new_block_size) - text_section.addr; |
| 892 | log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size }); | ||
| 893 | assert(needed_size <= text_capacity); // TODO handle growth | 1213 | assert(needed_size <= text_capacity); // TODO handle growth |
| 894 | 1214 | ||
| 895 | self.last_text_block = text_block; | 1215 | self.last_text_block = text_block; |
| 896 | text_section.size = needed_size; | 1216 | text_section.size = needed_size; // TODO temp until we pad out with NOPs |
| 897 | segment.vmsize = needed_size; | ||
| 898 | segment.filesize = needed_size; | ||
| 899 | if (alignment < text_section.@"align") { | ||
| 900 | text_section.@"align" = @intCast(u32, alignment); | ||
| 901 | } | ||
| 902 | } | 1217 | } |
| 903 | text_block.size = new_block_size; | 1218 | text_block.size = new_block_size; |
| 904 | 1219 | ||
| ... | @@ -936,22 +1251,27 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 { | ... | @@ -936,22 +1251,27 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 { |
| 936 | return @intCast(u32, result); | 1251 | return @intCast(u32, result); |
| 937 | } | 1252 | } |
| 938 | 1253 | ||
| 939 | fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int { | 1254 | fn getString(self: *MachO, str_off: u32) []const u8 { |
| 940 | const size = @intCast(Int, min_size); | 1255 | assert(str_off < self.string_table.items.len); |
| 941 | if (size % alignment == 0) return size; | 1256 | return mem.spanZ(@ptrCast([*:0]const u8, self.string_table.items.ptr + str_off)); |
| 942 | |||
| 943 | const div = size / alignment; | ||
| 944 | return (div + 1) * alignment; | ||
| 945 | } | 1257 | } |
| 946 | 1258 | ||
| 947 | fn commandSize(min_size: anytype) u32 { | 1259 | fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 { |
| 948 | return alignSize(u32, min_size, @sizeOf(u64)); | 1260 | const existing_name = self.getString(old_str_off); |
| 1261 | if (mem.eql(u8, existing_name, new_name)) { | ||
| 1262 | return old_str_off; | ||
| 1263 | } | ||
| 1264 | return self.makeString(new_name); | ||
| 949 | } | 1265 | } |
| 950 | 1266 | ||
| 1267 | /// TODO This should not heap allocate, instead it should utilize a fixed size, statically allocated | ||
| 1268 | /// global const array. You could even use pwritev to write the same buffer multiple times with only | ||
| 1269 | /// 1 syscall if you needed to, for example, write 8192 bytes using a buffer of only 4096 bytes. | ||
| 1270 | /// This size parameter should probably be a usize not u64. | ||
| 951 | fn addPadding(self: *MachO, size: u64, file_offset: u64) !void { | 1271 | fn addPadding(self: *MachO, size: u64, file_offset: u64) !void { |
| 952 | if (size == 0) return; | 1272 | if (size == 0) return; |
| 953 | 1273 | ||
| 954 | const buf = try self.base.allocator.alloc(u8, size); | 1274 | const buf = try self.base.allocator.alloc(u8, @intCast(usize, size)); |
| 955 | defer self.base.allocator.free(buf); | 1275 | defer self.base.allocator.free(buf); |
| 956 | 1276 | ||
| 957 | mem.set(u8, buf[0..], 0); | 1277 | mem.set(u8, buf[0..], 0); |
| ... | @@ -961,11 +1281,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void { | ... | @@ -961,11 +1281,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void { |
| 961 | 1281 | ||
| 962 | fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { | 1282 | fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 963 | const hdr_size: u64 = @sizeOf(macho.mach_header_64); | 1283 | const hdr_size: u64 = @sizeOf(macho.mach_header_64); |
| 964 | if (start < hdr_size) | 1284 | if (start < hdr_size) return hdr_size; |
| 965 | return hdr_size; | ||
| 966 | |||
| 967 | const end = start + satMul(size, alloc_num) / alloc_den; | 1285 | const end = start + satMul(size, alloc_num) / alloc_den; |
| 968 | |||
| 969 | { | 1286 | { |
| 970 | const off = @sizeOf(macho.mach_header_64); | 1287 | const off = @sizeOf(macho.mach_header_64); |
| 971 | var tight_size: u64 = 0; | 1288 | var tight_size: u64 = 0; |
| ... | @@ -978,7 +1295,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { | ... | @@ -978,7 +1295,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 978 | return test_end; | 1295 | return test_end; |
| 979 | } | 1296 | } |
| 980 | } | 1297 | } |
| 981 | |||
| 982 | for (self.sections.items) |section| { | 1298 | for (self.sections.items) |section| { |
| 983 | const increased_size = satMul(section.size, alloc_num) / alloc_den; | 1299 | const increased_size = satMul(section.size, alloc_num) / alloc_den; |
| 984 | const test_end = section.offset + increased_size; | 1300 | const test_end = section.offset + increased_size; |
| ... | @@ -986,7 +1302,15 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { | ... | @@ -986,7 +1302,15 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 986 | return test_end; | 1302 | return test_end; |
| 987 | } | 1303 | } |
| 988 | } | 1304 | } |
| 989 | 1305 | if (self.dyld_info_cmd_index) |dyld_info_index| { | |
| 1306 | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo; | ||
| 1307 | const tight_size = dyld_info.export_size; | ||
| 1308 | const increased_size = satMul(tight_size, alloc_num) / alloc_den; | ||
| 1309 | const test_end = dyld_info.export_off + increased_size; | ||
| 1310 | if (end > dyld_info.export_off and start < test_end) { | ||
| 1311 | return test_end; | ||
| 1312 | } | ||
| 1313 | } | ||
| 990 | if (self.symtab_cmd_index) |symtab_index| { | 1314 | if (self.symtab_cmd_index) |symtab_index| { |
| 991 | const symtab = self.load_commands.items[symtab_index].Symtab; | 1315 | const symtab = self.load_commands.items[symtab_index].Symtab; |
| 992 | { | 1316 | { |
| ... | @@ -1005,7 +1329,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { | ... | @@ -1005,7 +1329,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 { |
| 1005 | } | 1329 | } |
| 1006 | } | 1330 | } |
| 1007 | } | 1331 | } |
| 1008 | |||
| 1009 | return null; | 1332 | return null; |
| 1010 | } | 1333 | } |
| 1011 | 1334 | ||
| ... | @@ -1021,6 +1344,10 @@ fn allocatedSize(self: *MachO, start: u64) u64 { | ... | @@ -1021,6 +1344,10 @@ fn allocatedSize(self: *MachO, start: u64) u64 { |
| 1021 | if (section.offset <= start) continue; | 1344 | if (section.offset <= start) continue; |
| 1022 | if (section.offset < min_pos) min_pos = section.offset; | 1345 | if (section.offset < min_pos) min_pos = section.offset; |
| 1023 | } | 1346 | } |
| 1347 | if (self.dyld_info_cmd_index) |dyld_info_index| { | ||
| 1348 | const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo; | ||
| 1349 | if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off; | ||
| 1350 | } | ||
| 1024 | if (self.symtab_cmd_index) |symtab_index| { | 1351 | if (self.symtab_cmd_index) |symtab_index| { |
| 1025 | const symtab = self.load_commands.items[symtab_index].Symtab; | 1352 | const symtab = self.load_commands.items[symtab_index].Symtab; |
| 1026 | if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff; | 1353 | if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff; |
| ... | @@ -1042,12 +1369,133 @@ fn writeSymbol(self: *MachO, index: usize) !void { | ... | @@ -1042,12 +1369,133 @@ fn writeSymbol(self: *MachO, index: usize) !void { |
| 1042 | defer tracy.end(); | 1369 | defer tracy.end(); |
| 1043 | 1370 | ||
| 1044 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | 1371 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; |
| 1045 | const sym = [1]macho.nlist_64{self.symbol_table.items[index]}; | 1372 | const sym = [1]macho.nlist_64{self.local_symbols.items[index]}; |
| 1046 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; | 1373 | const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; |
| 1047 | log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off }); | 1374 | log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off }); |
| 1048 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); | 1375 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off); |
| 1049 | } | 1376 | } |
| 1050 | 1377 | ||
| 1378 | fn writeOffsetTableEntry(self: *MachO, index: usize) !void { | ||
| 1379 | const sect = &self.sections.items[self.got_section_index.?]; | ||
| 1380 | const endian = self.base.options.target.cpu.arch.endian(); | ||
| 1381 | var buf: [@sizeOf(u64)]u8 = undefined; | ||
| 1382 | mem.writeInt(u64, &buf, self.offset_table.items[index], endian); | ||
| 1383 | const off = sect.offset + @sizeOf(u64) * index; | ||
| 1384 | log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off }); | ||
| 1385 | try self.base.file.?.pwriteAll(&buf, off); | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | fn writeAllGlobalSymbols(self: *MachO) !void { | ||
| 1389 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | ||
| 1390 | const off = symtab.symoff + self.local_symbols.items.len * @sizeOf(macho.nlist_64); | ||
| 1391 | const file_size = self.global_symbols.items.len * @sizeOf(macho.nlist_64); | ||
| 1392 | log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off }); | ||
| 1393 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), off); | ||
| 1394 | } | ||
| 1395 | |||
| 1396 | fn writeAllUndefSymbols(self: *MachO) !void { | ||
| 1397 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | ||
| 1398 | const nlocals = self.local_symbols.items.len; | ||
| 1399 | const nglobals = self.global_symbols.items.len; | ||
| 1400 | const off = symtab.symoff + (nlocals + nglobals) * @sizeOf(macho.nlist_64); | ||
| 1401 | const file_size = self.undef_symbols.items.len * @sizeOf(macho.nlist_64); | ||
| 1402 | log.debug("writing undef symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off }); | ||
| 1403 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), off); | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | fn writeExportTrie(self: *MachO) !void { | ||
| 1407 | assert(self.entry_addr != null); | ||
| 1408 | |||
| 1409 | // TODO implement mechanism for generating a prefix tree of the exported symbols | ||
| 1410 | // single branch export trie | ||
| 1411 | var buf = [_]u8{0} ** 24; | ||
| 1412 | buf[0] = 0; // root node | ||
| 1413 | buf[1] = 1; // 1 branch from root | ||
| 1414 | mem.copy(u8, buf[2..], "_start"); | ||
| 1415 | buf[8] = 0; | ||
| 1416 | buf[9] = 9 + 1; | ||
| 1417 | |||
| 1418 | const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; | ||
| 1419 | const addr = self.entry_addr.? - text_segment.vmaddr; | ||
| 1420 | const written = try std.debug.leb.writeULEB128Mem(buf[12..], addr); | ||
| 1421 | buf[10] = @intCast(u8, written) + 1; | ||
| 1422 | buf[11] = 0; | ||
| 1423 | |||
| 1424 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo; | ||
| 1425 | try self.base.file.?.pwriteAll(buf[0..], dyld_info.export_off); | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | fn writeStringTable(self: *MachO) !void { | ||
| 1429 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; | ||
| 1430 | const allocated_size = self.allocatedSize(symtab.stroff); | ||
| 1431 | const needed_size = self.string_table.items.len; | ||
| 1432 | |||
| 1433 | if (needed_size > allocated_size) { | ||
| 1434 | symtab.strsize = 0; | ||
| 1435 | symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1)); | ||
| 1436 | } | ||
| 1437 | symtab.strsize = @intCast(u32, needed_size); | ||
| 1438 | |||
| 1439 | log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize }); | ||
| 1440 | |||
| 1441 | try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff); | ||
| 1442 | |||
| 1443 | // TODO rework how we preallocate space for the entire __LINKEDIT segment instead of | ||
| 1444 | // doing dynamic updates like this. | ||
| 1445 | const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; | ||
| 1446 | linkedit.filesize = symtab.stroff + symtab.strsize - linkedit.fileoff; | ||
| 1447 | } | ||
| 1448 | |||
| 1449 | fn writeCmdHeaders(self: *MachO) !void { | ||
| 1450 | assert(self.cmd_table_dirty); | ||
| 1451 | |||
| 1452 | // Write all load command headers first. | ||
| 1453 | // Since command sizes are up-to-date and accurate, we will correctly | ||
| 1454 | // leave space for any section headers that any of the segment load | ||
| 1455 | // commands might consist of. | ||
| 1456 | var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); | ||
| 1457 | for (self.load_commands.items) |cmd| { | ||
| 1458 | try cmd.write(&self.base.file.?, last_cmd_offset); | ||
| 1459 | last_cmd_offset += cmd.cmdsize(); | ||
| 1460 | } | ||
| 1461 | { | ||
| 1462 | // write __text section header | ||
| 1463 | const off = if (self.text_segment_cmd_index) |text_segment_index| blk: { | ||
| 1464 | var i: usize = 0; | ||
| 1465 | var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); | ||
| 1466 | while (i < text_segment_index) : (i += 1) { | ||
| 1467 | cmdsize += self.load_commands.items[i].cmdsize(); | ||
| 1468 | } | ||
| 1469 | break :blk cmdsize; | ||
| 1470 | } else { | ||
| 1471 | // If we've landed in here, we are building a MachO object file, so we have | ||
| 1472 | // only one, noname segment to append this section header to. | ||
| 1473 | return error.TODOImplementWritingObjFiles; | ||
| 1474 | }; | ||
| 1475 | const idx = self.text_section_index.?; | ||
| 1476 | log.debug("writing text section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off }); | ||
| 1477 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); | ||
| 1478 | } | ||
| 1479 | { | ||
| 1480 | // write __got section header | ||
| 1481 | const off = if (self.data_segment_cmd_index) |data_segment_index| blk: { | ||
| 1482 | var i: usize = 0; | ||
| 1483 | var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64); | ||
| 1484 | while (i < data_segment_index) : (i += 1) { | ||
| 1485 | cmdsize += self.load_commands.items[i].cmdsize(); | ||
| 1486 | } | ||
| 1487 | break :blk cmdsize; | ||
| 1488 | } else { | ||
| 1489 | // If we've landed in here, we are building a MachO object file, so we have | ||
| 1490 | // only one, noname segment to append this section header to. | ||
| 1491 | return error.TODOImplementWritingObjFiles; | ||
| 1492 | }; | ||
| 1493 | const idx = self.got_section_index.?; | ||
| 1494 | log.debug("writing got section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off }); | ||
| 1495 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off); | ||
| 1496 | } | ||
| 1497 | } | ||
| 1498 | |||
| 1051 | /// Writes Mach-O file header. | 1499 | /// Writes Mach-O file header. |
| 1052 | /// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping | 1500 | /// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping |
| 1053 | /// variables. | 1501 | /// variables. |
src/main.zig+56-12| ... | @@ -268,16 +268,19 @@ const usage_build_generic = | ... | @@ -268,16 +268,19 @@ const usage_build_generic = |
| 268 | \\ -T[script], --script [script] Use a custom linker script | 268 | \\ -T[script], --script [script] Use a custom linker script |
| 269 | \\ --version-script [path] Provide a version .map file | 269 | \\ --version-script [path] Provide a version .map file |
| 270 | \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) | 270 | \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) |
| 271 | \\ --each-lib-rpath Add rpath for each used dynamic library | ||
| 272 | \\ --version [ver] Dynamic library semver | 271 | \\ --version [ver] Dynamic library semver |
| 273 | \\ -rdynamic Add all symbols to the dynamic symbol table | 272 | \\ -rdynamic Add all symbols to the dynamic symbol table |
| 274 | \\ -rpath [path] Add directory to the runtime library search path | 273 | \\ -rpath [path] Add directory to the runtime library search path |
| 274 | \\ -feach-lib-rpath Ensure adding rpath for each used dynamic library | ||
| 275 | \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library | ||
| 275 | \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker | 276 | \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker |
| 277 | \\ --emit-relocs Enable output of relocation sections for post build tools | ||
| 276 | \\ -dynamic Force output to be dynamically linked | 278 | \\ -dynamic Force output to be dynamically linked |
| 277 | \\ -static Force output to be statically linked | 279 | \\ -static Force output to be statically linked |
| 278 | \\ -Bsymbolic Bind global references locally | 280 | \\ -Bsymbolic Bind global references locally |
| 279 | \\ --subsystem [subsystem] (windows) /SUBSYSTEM:<subsystem> to the linker\n" | 281 | \\ --subsystem [subsystem] (windows) /SUBSYSTEM:<subsystem> to the linker\n" |
| 280 | \\ --stack [size] Override default stack size | 282 | \\ --stack [size] Override default stack size |
| 283 | \\ --image-base [addr] Set base address for executable image | ||
| 281 | \\ -framework [name] (darwin) link against framework | 284 | \\ -framework [name] (darwin) link against framework |
| 282 | \\ -F[dir] (darwin) add search path for frameworks | 285 | \\ -F[dir] (darwin) add search path for frameworks |
| 283 | \\ | 286 | \\ |
| ... | @@ -434,11 +437,13 @@ fn buildOutputType( | ... | @@ -434,11 +437,13 @@ fn buildOutputType( |
| 434 | var linker_z_defs = false; | 437 | var linker_z_defs = false; |
| 435 | var test_evented_io = false; | 438 | var test_evented_io = false; |
| 436 | var stack_size_override: ?u64 = null; | 439 | var stack_size_override: ?u64 = null; |
| 440 | var image_base_override: ?u64 = null; | ||
| 437 | var use_llvm: ?bool = null; | 441 | var use_llvm: ?bool = null; |
| 438 | var use_lld: ?bool = null; | 442 | var use_lld: ?bool = null; |
| 439 | var use_clang: ?bool = null; | 443 | var use_clang: ?bool = null; |
| 440 | var link_eh_frame_hdr = false; | 444 | var link_eh_frame_hdr = false; |
| 441 | var each_lib_rpath = false; | 445 | var link_emit_relocs = false; |
| 446 | var each_lib_rpath: ?bool = null; | ||
| 442 | var libc_paths_file: ?[]const u8 = null; | 447 | var libc_paths_file: ?[]const u8 = null; |
| 443 | var machine_code_model: std.builtin.CodeModel = .default; | 448 | var machine_code_model: std.builtin.CodeModel = .default; |
| 444 | var runtime_args_start: ?usize = null; | 449 | var runtime_args_start: ?usize = null; |
| ... | @@ -520,7 +525,7 @@ fn buildOutputType( | ... | @@ -520,7 +525,7 @@ fn buildOutputType( |
| 520 | //} | 525 | //} |
| 521 | const args = all_args[2..]; | 526 | const args = all_args[2..]; |
| 522 | var i: usize = 0; | 527 | var i: usize = 0; |
| 523 | while (i < args.len) : (i += 1) { | 528 | args_loop: while (i < args.len) : (i += 1) { |
| 524 | const arg = args[i]; | 529 | const arg = args[i]; |
| 525 | if (mem.startsWith(u8, arg, "-")) { | 530 | if (mem.startsWith(u8, arg, "-")) { |
| 526 | if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { | 531 | if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { |
| ... | @@ -528,7 +533,10 @@ fn buildOutputType( | ... | @@ -528,7 +533,10 @@ fn buildOutputType( |
| 528 | return cleanExit(); | 533 | return cleanExit(); |
| 529 | } else if (mem.eql(u8, arg, "--")) { | 534 | } else if (mem.eql(u8, arg, "--")) { |
| 530 | if (arg_mode == .run) { | 535 | if (arg_mode == .run) { |
| 531 | runtime_args_start = i + 1; | 536 | // The index refers to all_args so skip `zig` `run` |
| 537 | // and `--` | ||
| 538 | runtime_args_start = i + 3; | ||
| 539 | break :args_loop; | ||
| 532 | } else { | 540 | } else { |
| 533 | fatal("unexpected end-of-parameter mark: --", .{}); | 541 | fatal("unexpected end-of-parameter mark: --", .{}); |
| 534 | } | 542 | } |
| ... | @@ -626,9 +634,11 @@ fn buildOutputType( | ... | @@ -626,9 +634,11 @@ fn buildOutputType( |
| 626 | } else if (mem.eql(u8, arg, "--stack")) { | 634 | } else if (mem.eql(u8, arg, "--stack")) { |
| 627 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); | 635 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 628 | i += 1; | 636 | i += 1; |
| 629 | stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| { | 637 | stack_size_override = parseAnyBaseInt(args[i]); |
| 630 | fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); | 638 | } else if (mem.eql(u8, arg, "--image-base")) { |
| 631 | }; | 639 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 640 | i += 1; | ||
| 641 | image_base_override = parseAnyBaseInt(args[i]); | ||
| 632 | } else if (mem.eql(u8, arg, "--name")) { | 642 | } else if (mem.eql(u8, arg, "--name")) { |
| 633 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); | 643 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 634 | i += 1; | 644 | i += 1; |
| ... | @@ -733,8 +743,10 @@ fn buildOutputType( | ... | @@ -733,8 +743,10 @@ fn buildOutputType( |
| 733 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); | 743 | if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); |
| 734 | i += 1; | 744 | i += 1; |
| 735 | override_lib_dir = args[i]; | 745 | override_lib_dir = args[i]; |
| 736 | } else if (mem.eql(u8, arg, "--each-lib-rpath")) { | 746 | } else if (mem.eql(u8, arg, "-feach-lib-rpath")) { |
| 737 | each_lib_rpath = true; | 747 | each_lib_rpath = true; |
| 748 | } else if (mem.eql(u8, arg, "-fno-each-lib-rpath")) { | ||
| 749 | each_lib_rpath = false; | ||
| 738 | } else if (mem.eql(u8, arg, "--enable-cache")) { | 750 | } else if (mem.eql(u8, arg, "--enable-cache")) { |
| 739 | enable_cache = true; | 751 | enable_cache = true; |
| 740 | } else if (mem.eql(u8, arg, "--test-cmd-bin")) { | 752 | } else if (mem.eql(u8, arg, "--test-cmd-bin")) { |
| ... | @@ -838,6 +850,8 @@ fn buildOutputType( | ... | @@ -838,6 +850,8 @@ fn buildOutputType( |
| 838 | function_sections = true; | 850 | function_sections = true; |
| 839 | } else if (mem.eql(u8, arg, "--eh-frame-hdr")) { | 851 | } else if (mem.eql(u8, arg, "--eh-frame-hdr")) { |
| 840 | link_eh_frame_hdr = true; | 852 | link_eh_frame_hdr = true; |
| 853 | } else if (mem.eql(u8, arg, "--emit-relocs")) { | ||
| 854 | link_emit_relocs = true; | ||
| 841 | } else if (mem.eql(u8, arg, "-Bsymbolic")) { | 855 | } else if (mem.eql(u8, arg, "-Bsymbolic")) { |
| 842 | linker_bind_global_refs_locally = true; | 856 | linker_bind_global_refs_locally = true; |
| 843 | } else if (mem.eql(u8, arg, "--verbose-link")) { | 857 | } else if (mem.eql(u8, arg, "--verbose-link")) { |
| ... | @@ -1143,9 +1157,13 @@ fn buildOutputType( | ... | @@ -1143,9 +1157,13 @@ fn buildOutputType( |
| 1143 | if (i >= linker_args.items.len) { | 1157 | if (i >= linker_args.items.len) { |
| 1144 | fatal("expected linker arg after '{}'", .{arg}); | 1158 | fatal("expected linker arg after '{}'", .{arg}); |
| 1145 | } | 1159 | } |
| 1146 | stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| { | 1160 | stack_size_override = parseAnyBaseInt(linker_args.items[i]); |
| 1147 | fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); | 1161 | } else if (mem.eql(u8, arg, "--image-base")) { |
| 1148 | }; | 1162 | i += 1; |
| 1163 | if (i >= linker_args.items.len) { | ||
| 1164 | fatal("expected linker arg after '{}'", .{arg}); | ||
| 1165 | } | ||
| 1166 | image_base_override = parseAnyBaseInt(linker_args.items[i]); | ||
| 1149 | } else { | 1167 | } else { |
| 1150 | warn("unsupported linker arg: {}", .{arg}); | 1168 | warn("unsupported linker arg: {}", .{arg}); |
| 1151 | } | 1169 | } |
| ... | @@ -1206,6 +1224,10 @@ fn buildOutputType( | ... | @@ -1206,6 +1224,10 @@ fn buildOutputType( |
| 1206 | fatal("translate-c expects exactly 1 source file (found {})", .{c_source_files.items.len}); | 1224 | fatal("translate-c expects exactly 1 source file (found {})", .{c_source_files.items.len}); |
| 1207 | } | 1225 | } |
| 1208 | 1226 | ||
| 1227 | if (root_src_file == null and arg_mode == .zig_test) { | ||
| 1228 | fatal("one zig source file is required to run `zig test`", .{}); | ||
| 1229 | } | ||
| 1230 | |||
| 1209 | const root_name = if (provided_name) |n| n else blk: { | 1231 | const root_name = if (provided_name) |n| n else blk: { |
| 1210 | if (arg_mode == .zig_test) { | 1232 | if (arg_mode == .zig_test) { |
| 1211 | break :blk "test"; | 1233 | break :blk "test"; |
| ... | @@ -1446,6 +1468,11 @@ fn buildOutputType( | ... | @@ -1446,6 +1468,11 @@ fn buildOutputType( |
| 1446 | cleanup_root_dir = dir; | 1468 | cleanup_root_dir = dir; |
| 1447 | root_pkg_memory.root_src_directory = .{ .path = p, .handle = dir }; | 1469 | root_pkg_memory.root_src_directory = .{ .path = p, .handle = dir }; |
| 1448 | root_pkg_memory.root_src_path = try fs.path.relative(arena, p, src_path); | 1470 | root_pkg_memory.root_src_path = try fs.path.relative(arena, p, src_path); |
| 1471 | } else if (fs.path.dirname(src_path)) |p| { | ||
| 1472 | const dir = try fs.cwd().openDir(p, .{}); | ||
| 1473 | cleanup_root_dir = dir; | ||
| 1474 | root_pkg_memory.root_src_directory = .{ .path = p, .handle = dir }; | ||
| 1475 | root_pkg_memory.root_src_path = fs.path.basename(src_path); | ||
| 1449 | } else { | 1476 | } else { |
| 1450 | root_pkg_memory.root_src_directory = .{ .path = null, .handle = fs.cwd() }; | 1477 | root_pkg_memory.root_src_directory = .{ .path = null, .handle = fs.cwd() }; |
| 1451 | root_pkg_memory.root_src_path = src_path; | 1478 | root_pkg_memory.root_src_path = src_path; |
| ... | @@ -1580,7 +1607,9 @@ fn buildOutputType( | ... | @@ -1580,7 +1607,9 @@ fn buildOutputType( |
| 1580 | .linker_z_nodelete = linker_z_nodelete, | 1607 | .linker_z_nodelete = linker_z_nodelete, |
| 1581 | .linker_z_defs = linker_z_defs, | 1608 | .linker_z_defs = linker_z_defs, |
| 1582 | .link_eh_frame_hdr = link_eh_frame_hdr, | 1609 | .link_eh_frame_hdr = link_eh_frame_hdr, |
| 1610 | .link_emit_relocs = link_emit_relocs, | ||
| 1583 | .stack_size_override = stack_size_override, | 1611 | .stack_size_override = stack_size_override, |
| 1612 | .image_base_override = image_base_override, | ||
| 1584 | .strip = strip, | 1613 | .strip = strip, |
| 1585 | .single_threaded = single_threaded, | 1614 | .single_threaded = single_threaded, |
| 1586 | .function_sections = function_sections, | 1615 | .function_sections = function_sections, |
| ... | @@ -2525,7 +2554,7 @@ fn fmtPathFile( | ... | @@ -2525,7 +2554,7 @@ fn fmtPathFile( |
| 2525 | const source_code = source_file.readToEndAllocOptions( | 2554 | const source_code = source_file.readToEndAllocOptions( |
| 2526 | fmt.gpa, | 2555 | fmt.gpa, |
| 2527 | max_src_size, | 2556 | max_src_size, |
| 2528 | stat.size, | 2557 | std.math.cast(usize, stat.size) catch return error.FileTooBig, |
| 2529 | @alignOf(u8), | 2558 | @alignOf(u8), |
| 2530 | null, | 2559 | null, |
| 2531 | ) catch |err| switch (err) { | 2560 | ) catch |err| switch (err) { |
| ... | @@ -3037,3 +3066,18 @@ pub fn cleanExit() void { | ... | @@ -3037,3 +3066,18 @@ pub fn cleanExit() void { |
| 3037 | process.exit(0); | 3066 | process.exit(0); |
| 3038 | } | 3067 | } |
| 3039 | } | 3068 | } |
| 3069 | |||
| 3070 | fn parseAnyBaseInt(prefixed_bytes: []const u8) u64 { | ||
| 3071 | const base: u8 = if (mem.startsWith(u8, prefixed_bytes, "0x")) | ||
| 3072 | 16 | ||
| 3073 | else if (mem.startsWith(u8, prefixed_bytes, "0o")) | ||
| 3074 | 8 | ||
| 3075 | else if (mem.startsWith(u8, prefixed_bytes, "0b")) | ||
| 3076 | 2 | ||
| 3077 | else | ||
| 3078 | @as(u8, 10); | ||
| 3079 | const bytes = if (base == 10) prefixed_bytes else prefixed_bytes[2..]; | ||
| 3080 | return std.fmt.parseInt(u64, bytes, base) catch |err| { | ||
| 3081 | fatal("unable to parse '{}': {}", .{ prefixed_bytes, @errorName(err) }); | ||
| 3082 | }; | ||
| 3083 | } |
src/mingw.zig+6-5| ... | @@ -32,6 +32,8 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -32,6 +32,8 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 32 | var args = std.ArrayList([]const u8).init(arena); | 32 | var args = std.ArrayList([]const u8).init(arena); |
| 33 | try add_cc_args(comp, arena, &args); | 33 | try add_cc_args(comp, arena, &args); |
| 34 | try args.appendSlice(&[_][]const u8{ | 34 | try args.appendSlice(&[_][]const u8{ |
| 35 | "-D_SYSCRT=1", | ||
| 36 | "-DCRTDLL=1", | ||
| 35 | "-U__CRTDLL__", | 37 | "-U__CRTDLL__", |
| 36 | "-D__MSVCRT__", | 38 | "-D__MSVCRT__", |
| 37 | // Uncomment these 3 things for crtu | 39 | // Uncomment these 3 things for crtu |
| ... | @@ -53,6 +55,8 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -53,6 +55,8 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 53 | var args = std.ArrayList([]const u8).init(arena); | 55 | var args = std.ArrayList([]const u8).init(arena); |
| 54 | try add_cc_args(comp, arena, &args); | 56 | try add_cc_args(comp, arena, &args); |
| 55 | try args.appendSlice(&[_][]const u8{ | 57 | try args.appendSlice(&[_][]const u8{ |
| 58 | "-D_SYSCRT=1", | ||
| 59 | "-DCRTDLL=1", | ||
| 56 | "-U__CRTDLL__", | 60 | "-U__CRTDLL__", |
| 57 | "-D__MSVCRT__", | 61 | "-D__MSVCRT__", |
| 58 | }); | 62 | }); |
| ... | @@ -437,11 +441,8 @@ fn findDef(comp: *Compilation, allocator: *Allocator, lib_name: []const u8) ![]u | ... | @@ -437,11 +441,8 @@ fn findDef(comp: *Compilation, allocator: *Allocator, lib_name: []const u8) ![]u |
| 437 | const lib_path = switch (target.cpu.arch) { | 441 | const lib_path = switch (target.cpu.arch) { |
| 438 | .i386 => "lib32", | 442 | .i386 => "lib32", |
| 439 | .x86_64 => "lib64", | 443 | .x86_64 => "lib64", |
| 440 | .arm, .armeb => switch (target.cpu.arch.ptrBitWidth()) { | 444 | .arm, .armeb, .thumb, .thumbeb, .aarch64_32 => "libarm32", |
| 441 | 32 => "libarm32", | 445 | .aarch64, .aarch64_be => "libarm64", |
| 442 | 64 => "libarm64", | ||
| 443 | else => unreachable, | ||
| 444 | }, | ||
| 445 | else => unreachable, | 446 | else => unreachable, |
| 446 | }; | 447 | }; |
| 447 | 448 |
src/stage1.zig+5-1| ... | @@ -39,7 +39,11 @@ pub export fn main(argc: c_int, argv: [*]const [*:0]const u8) c_int { | ... | @@ -39,7 +39,11 @@ pub export fn main(argc: c_int, argv: [*]const [*:0]const u8) c_int { |
| 39 | for (args) |*arg, i| { | 39 | for (args) |*arg, i| { |
| 40 | arg.* = mem.spanZ(argv[i]); | 40 | arg.* = mem.spanZ(argv[i]); |
| 41 | } | 41 | } |
| 42 | stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{@errorName(err)}); | 42 | if (std.builtin.mode == .Debug) { |
| 43 | stage2.mainArgs(gpa, arena, args) catch unreachable; | ||
| 44 | } else { | ||
| 45 | stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{@errorName(err)}); | ||
| 46 | } | ||
| 43 | return 0; | 47 | return 0; |
| 44 | } | 48 | } |
| 45 | 49 |
src/stage1/all_types.hpp+27-5| ... | @@ -18,11 +18,6 @@ | ... | @@ -18,11 +18,6 @@ |
| 18 | #include "target.hpp" | 18 | #include "target.hpp" |
| 19 | #include "tokenizer.hpp" | 19 | #include "tokenizer.hpp" |
| 20 | 20 | ||
| 21 | #ifndef NDEBUG | ||
| 22 | #define DBG_MACRO_NO_WARNING | ||
| 23 | #include <dbg.h> | ||
| 24 | #endif | ||
| 25 | |||
| 26 | struct AstNode; | 21 | struct AstNode; |
| 27 | struct ZigFn; | 22 | struct ZigFn; |
| 28 | struct Scope; | 23 | struct Scope; |
| ... | @@ -1456,6 +1451,7 @@ struct ZigTypeEnum { | ... | @@ -1456,6 +1451,7 @@ struct ZigTypeEnum { |
| 1456 | ContainerLayout layout; | 1451 | ContainerLayout layout; |
| 1457 | ResolveStatus resolve_status; | 1452 | ResolveStatus resolve_status; |
| 1458 | 1453 | ||
| 1454 | bool has_explicit_tag_type; | ||
| 1459 | bool non_exhaustive; | 1455 | bool non_exhaustive; |
| 1460 | bool resolve_loop_flag; | 1456 | bool resolve_loop_flag; |
| 1461 | }; | 1457 | }; |
| ... | @@ -1825,6 +1821,7 @@ enum BuiltinFnId { | ... | @@ -1825,6 +1821,7 @@ enum BuiltinFnId { |
| 1825 | BuiltinFnIdWasmMemorySize, | 1821 | BuiltinFnIdWasmMemorySize, |
| 1826 | BuiltinFnIdWasmMemoryGrow, | 1822 | BuiltinFnIdWasmMemoryGrow, |
| 1827 | BuiltinFnIdSrc, | 1823 | BuiltinFnIdSrc, |
| 1824 | BuiltinFnIdReduce, | ||
| 1828 | }; | 1825 | }; |
| 1829 | 1826 | ||
| 1830 | struct BuiltinFnEntry { | 1827 | struct BuiltinFnEntry { |
| ... | @@ -2440,6 +2437,15 @@ enum AtomicOrder { | ... | @@ -2440,6 +2437,15 @@ enum AtomicOrder { |
| 2440 | AtomicOrderSeqCst, | 2437 | AtomicOrderSeqCst, |
| 2441 | }; | 2438 | }; |
| 2442 | 2439 | ||
| 2440 | // synchronized with code in define_builtin_compile_vars | ||
| 2441 | enum ReduceOp { | ||
| 2442 | ReduceOp_and, | ||
| 2443 | ReduceOp_or, | ||
| 2444 | ReduceOp_xor, | ||
| 2445 | ReduceOp_min, | ||
| 2446 | ReduceOp_max, | ||
| 2447 | }; | ||
| 2448 | |||
| 2443 | // synchronized with the code in define_builtin_compile_vars | 2449 | // synchronized with the code in define_builtin_compile_vars |
| 2444 | enum AtomicRmwOp { | 2450 | enum AtomicRmwOp { |
| 2445 | AtomicRmwOp_xchg, | 2451 | AtomicRmwOp_xchg, |
| ... | @@ -2549,6 +2555,7 @@ enum IrInstSrcId { | ... | @@ -2549,6 +2555,7 @@ enum IrInstSrcId { |
| 2549 | IrInstSrcIdEmbedFile, | 2555 | IrInstSrcIdEmbedFile, |
| 2550 | IrInstSrcIdCmpxchg, | 2556 | IrInstSrcIdCmpxchg, |
| 2551 | IrInstSrcIdFence, | 2557 | IrInstSrcIdFence, |
| 2558 | IrInstSrcIdReduce, | ||
| 2552 | IrInstSrcIdTruncate, | 2559 | IrInstSrcIdTruncate, |
| 2553 | IrInstSrcIdIntCast, | 2560 | IrInstSrcIdIntCast, |
| 2554 | IrInstSrcIdFloatCast, | 2561 | IrInstSrcIdFloatCast, |
| ... | @@ -2671,6 +2678,7 @@ enum IrInstGenId { | ... | @@ -2671,6 +2678,7 @@ enum IrInstGenId { |
| 2671 | IrInstGenIdErrName, | 2678 | IrInstGenIdErrName, |
| 2672 | IrInstGenIdCmpxchg, | 2679 | IrInstGenIdCmpxchg, |
| 2673 | IrInstGenIdFence, | 2680 | IrInstGenIdFence, |
| 2681 | IrInstGenIdReduce, | ||
| 2674 | IrInstGenIdTruncate, | 2682 | IrInstGenIdTruncate, |
| 2675 | IrInstGenIdShuffleVector, | 2683 | IrInstGenIdShuffleVector, |
| 2676 | IrInstGenIdSplat, | 2684 | IrInstGenIdSplat, |
| ... | @@ -3520,6 +3528,20 @@ struct IrInstGenFence { | ... | @@ -3520,6 +3528,20 @@ struct IrInstGenFence { |
| 3520 | AtomicOrder order; | 3528 | AtomicOrder order; |
| 3521 | }; | 3529 | }; |
| 3522 | 3530 | ||
| 3531 | struct IrInstSrcReduce { | ||
| 3532 | IrInstSrc base; | ||
| 3533 | |||
| 3534 | IrInstSrc *op; | ||
| 3535 | IrInstSrc *value; | ||
| 3536 | }; | ||
| 3537 | |||
| 3538 | struct IrInstGenReduce { | ||
| 3539 | IrInstGen base; | ||
| 3540 | |||
| 3541 | ReduceOp op; | ||
| 3542 | IrInstGen *value; | ||
| 3543 | }; | ||
| 3544 | |||
| 3523 | struct IrInstSrcTruncate { | 3545 | struct IrInstSrcTruncate { |
| 3524 | IrInstSrc base; | 3546 | IrInstSrc base; |
| 3525 | 3547 |
src/stage1/analyze.cpp+16-8| ... | @@ -1802,10 +1802,18 @@ Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) { | ... | @@ -1802,10 +1802,18 @@ Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) { |
| 1802 | } | 1802 | } |
| 1803 | return type_allowed_in_extern(g, child_type, result); | 1803 | return type_allowed_in_extern(g, child_type, result); |
| 1804 | } | 1804 | } |
| 1805 | case ZigTypeIdEnum: | 1805 | case ZigTypeIdEnum: { |
| 1806 | *result = type_entry->data.enumeration.layout == ContainerLayoutExtern || | 1806 | if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown))) |
| 1807 | type_entry->data.enumeration.layout == ContainerLayoutPacked; | 1807 | return err; |
| 1808 | return ErrorNone; | 1808 | ZigType *tag_int_type = type_entry->data.enumeration.tag_int_type; |
| 1809 | if (type_entry->data.enumeration.has_explicit_tag_type) { | ||
| 1810 | return type_allowed_in_extern(g, tag_int_type, result); | ||
| 1811 | } else { | ||
| 1812 | *result = type_entry->data.enumeration.layout == ContainerLayoutExtern || | ||
| 1813 | type_entry->data.enumeration.layout == ContainerLayoutPacked; | ||
| 1814 | return ErrorNone; | ||
| 1815 | } | ||
| 1816 | } | ||
| 1809 | case ZigTypeIdUnion: | 1817 | case ZigTypeIdUnion: |
| 1810 | *result = type_entry->data.unionation.layout == ContainerLayoutExtern || | 1818 | *result = type_entry->data.unionation.layout == ContainerLayoutExtern || |
| 1811 | type_entry->data.unionation.layout == ContainerLayoutPacked; | 1819 | type_entry->data.unionation.layout == ContainerLayoutPacked; |
| ... | @@ -2639,9 +2647,11 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { | ... | @@ -2639,9 +2647,11 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { |
| 2639 | if (decl_node->type == NodeTypeContainerDecl) { | 2647 | if (decl_node->type == NodeTypeContainerDecl) { |
| 2640 | if (decl_node->data.container_decl.init_arg_expr != nullptr) { | 2648 | if (decl_node->data.container_decl.init_arg_expr != nullptr) { |
| 2641 | wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); | 2649 | wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); |
| 2650 | enum_type->data.enumeration.has_explicit_tag_type = true; | ||
| 2642 | } | 2651 | } |
| 2643 | } else { | 2652 | } else { |
| 2644 | wanted_tag_int_type = enum_type->data.enumeration.tag_int_type; | 2653 | wanted_tag_int_type = enum_type->data.enumeration.tag_int_type; |
| 2654 | enum_type->data.enumeration.has_explicit_tag_type = true; | ||
| 2645 | } | 2655 | } |
| 2646 | 2656 | ||
| 2647 | if (wanted_tag_int_type != nullptr) { | 2657 | if (wanted_tag_int_type != nullptr) { |
| ... | @@ -3120,12 +3130,9 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { | ... | @@ -3120,12 +3130,9 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { |
| 3120 | bool create_enum_type = is_auto_enum || (!is_explicit_enum && want_safety); | 3130 | bool create_enum_type = is_auto_enum || (!is_explicit_enum && want_safety); |
| 3121 | bool *covered_enum_fields; | 3131 | bool *covered_enum_fields; |
| 3122 | bool *is_zero_bits = heap::c_allocator.allocate<bool>(field_count); | 3132 | bool *is_zero_bits = heap::c_allocator.allocate<bool>(field_count); |
| 3123 | ZigLLVMDIEnumerator **di_enumerators; | ||
| 3124 | if (create_enum_type) { | 3133 | if (create_enum_type) { |
| 3125 | occupied_tag_values.init(field_count); | 3134 | occupied_tag_values.init(field_count); |
| 3126 | 3135 | ||
| 3127 | di_enumerators = heap::c_allocator.allocate<ZigLLVMDIEnumerator*>(field_count); | ||
| 3128 | |||
| 3129 | ZigType *tag_int_type; | 3136 | ZigType *tag_int_type; |
| 3130 | if (enum_type_node != nullptr) { | 3137 | if (enum_type_node != nullptr) { |
| 3131 | tag_int_type = analyze_type_expr(g, scope, enum_type_node); | 3138 | tag_int_type = analyze_type_expr(g, scope, enum_type_node); |
| ... | @@ -3269,7 +3276,6 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { | ... | @@ -3269,7 +3276,6 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { |
| 3269 | } | 3276 | } |
| 3270 | 3277 | ||
| 3271 | if (create_enum_type) { | 3278 | if (create_enum_type) { |
| 3272 | di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(union_field->name), i); | ||
| 3273 | union_field->enum_field = &tag_type->data.enumeration.fields[i]; | 3279 | union_field->enum_field = &tag_type->data.enumeration.fields[i]; |
| 3274 | union_field->enum_field->name = union_field->name; | 3280 | union_field->enum_field->name = union_field->name; |
| 3275 | union_field->enum_field->decl_index = i; | 3281 | union_field->enum_field->decl_index = i; |
| ... | @@ -3336,6 +3342,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { | ... | @@ -3336,6 +3342,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { |
| 3336 | gen_field_index += 1; | 3342 | gen_field_index += 1; |
| 3337 | } | 3343 | } |
| 3338 | } | 3344 | } |
| 3345 | heap::c_allocator.deallocate(is_zero_bits, field_count); | ||
| 3339 | 3346 | ||
| 3340 | bool src_have_tag = is_auto_enum || is_explicit_enum; | 3347 | bool src_have_tag = is_auto_enum || is_explicit_enum; |
| 3341 | 3348 | ||
| ... | @@ -3403,6 +3410,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { | ... | @@ -3403,6 +3410,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { |
| 3403 | union_type->data.unionation.resolve_status = ResolveStatusInvalid; | 3410 | union_type->data.unionation.resolve_status = ResolveStatusInvalid; |
| 3404 | } | 3411 | } |
| 3405 | } | 3412 | } |
| 3413 | heap::c_allocator.deallocate(covered_enum_fields, tag_type->data.enumeration.src_field_count); | ||
| 3406 | } | 3414 | } |
| 3407 | 3415 | ||
| 3408 | if (union_type->data.unionation.resolve_status == ResolveStatusInvalid) { | 3416 | if (union_type->data.unionation.resolve_status == ResolveStatusInvalid) { |
src/stage1/codegen.cpp+86-47| ... | @@ -2584,36 +2584,6 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutableGen *executable, Ir | ... | @@ -2584,36 +2584,6 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutableGen *executable, Ir |
| 2584 | return nullptr; | 2584 | return nullptr; |
| 2585 | } | 2585 | } |
| 2586 | 2586 | ||
| 2587 | enum class ScalarizePredicate { | ||
| 2588 | // Returns true iff all the elements in the vector are 1. | ||
| 2589 | // Equivalent to folding all the bits with `and`. | ||
| 2590 | All, | ||
| 2591 | // Returns true iff there's at least one element in the vector that is 1. | ||
| 2592 | // Equivalent to folding all the bits with `or`. | ||
| 2593 | Any, | ||
| 2594 | }; | ||
| 2595 | |||
| 2596 | // Collapses a <N x i1> vector into a single i1 according to the given predicate | ||
| 2597 | static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val, ScalarizePredicate predicate) { | ||
| 2598 | assert(LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMVectorTypeKind); | ||
| 2599 | LLVMTypeRef scalar_type = LLVMIntType(LLVMGetVectorSize(LLVMTypeOf(val))); | ||
| 2600 | LLVMValueRef casted = LLVMBuildBitCast(g->builder, val, scalar_type, ""); | ||
| 2601 | |||
| 2602 | switch (predicate) { | ||
| 2603 | case ScalarizePredicate::Any: { | ||
| 2604 | LLVMValueRef all_zeros = LLVMConstNull(scalar_type); | ||
| 2605 | return LLVMBuildICmp(g->builder, LLVMIntNE, casted, all_zeros, ""); | ||
| 2606 | } | ||
| 2607 | case ScalarizePredicate::All: { | ||
| 2608 | LLVMValueRef all_ones = LLVMConstAllOnes(scalar_type); | ||
| 2609 | return LLVMBuildICmp(g->builder, LLVMIntEQ, casted, all_ones, ""); | ||
| 2610 | } | ||
| 2611 | } | ||
| 2612 | |||
| 2613 | zig_unreachable(); | ||
| 2614 | } | ||
| 2615 | |||
| 2616 | |||
| 2617 | static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, | 2587 | static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, |
| 2618 | LLVMValueRef val1, LLVMValueRef val2) | 2588 | LLVMValueRef val1, LLVMValueRef val2) |
| 2619 | { | 2589 | { |
| ... | @@ -2638,7 +2608,7 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, | ... | @@ -2638,7 +2608,7 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, |
| 2638 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); | 2608 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); |
| 2639 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); | 2609 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); |
| 2640 | if (operand_type->id == ZigTypeIdVector) { | 2610 | if (operand_type->id == ZigTypeIdVector) { |
| 2641 | ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); | 2611 | ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit); |
| 2642 | } | 2612 | } |
| 2643 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | 2613 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2644 | 2614 | ||
| ... | @@ -2669,7 +2639,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type, | ... | @@ -2669,7 +2639,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type, |
| 2669 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); | 2639 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); |
| 2670 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); | 2640 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); |
| 2671 | if (operand_type->id == ZigTypeIdVector) { | 2641 | if (operand_type->id == ZigTypeIdVector) { |
| 2672 | ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); | 2642 | ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit); |
| 2673 | } | 2643 | } |
| 2674 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | 2644 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2675 | 2645 | ||
| ... | @@ -2746,7 +2716,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2746,7 +2716,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2746 | } | 2716 | } |
| 2747 | 2717 | ||
| 2748 | if (operand_type->id == ZigTypeIdVector) { | 2718 | if (operand_type->id == ZigTypeIdVector) { |
| 2749 | is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any); | 2719 | is_zero_bit = ZigLLVMBuildOrReduce(g->builder, is_zero_bit); |
| 2750 | } | 2720 | } |
| 2751 | 2721 | ||
| 2752 | LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); | 2722 | LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); |
| ... | @@ -2771,7 +2741,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2771,7 +2741,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2771 | LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); | 2741 | LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); |
| 2772 | LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); | 2742 | LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); |
| 2773 | if (operand_type->id == ZigTypeIdVector) { | 2743 | if (operand_type->id == ZigTypeIdVector) { |
| 2774 | overflow_fail_bit = scalarize_cmp_result(g, overflow_fail_bit, ScalarizePredicate::Any); | 2744 | overflow_fail_bit = ZigLLVMBuildOrReduce(g->builder, overflow_fail_bit); |
| 2775 | } | 2745 | } |
| 2776 | LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); | 2746 | LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); |
| 2777 | 2747 | ||
| ... | @@ -2796,7 +2766,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2796,7 +2766,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2796 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); | 2766 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2797 | LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); | 2767 | LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); |
| 2798 | if (operand_type->id == ZigTypeIdVector) { | 2768 | if (operand_type->id == ZigTypeIdVector) { |
| 2799 | ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); | 2769 | ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit); |
| 2800 | } | 2770 | } |
| 2801 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | 2771 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2802 | 2772 | ||
| ... | @@ -2813,7 +2783,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2813,7 +2783,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2813 | LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd"); | 2783 | LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd"); |
| 2814 | LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); | 2784 | LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); |
| 2815 | if (operand_type->id == ZigTypeIdVector) { | 2785 | if (operand_type->id == ZigTypeIdVector) { |
| 2816 | ltz = scalarize_cmp_result(g, ltz, ScalarizePredicate::Any); | 2786 | ltz = ZigLLVMBuildOrReduce(g->builder, ltz); |
| 2817 | } | 2787 | } |
| 2818 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); | 2788 | LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); |
| 2819 | 2789 | ||
| ... | @@ -2865,7 +2835,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2865,7 +2835,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2865 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); | 2835 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); |
| 2866 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); | 2836 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); |
| 2867 | if (operand_type->id == ZigTypeIdVector) { | 2837 | if (operand_type->id == ZigTypeIdVector) { |
| 2868 | ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); | 2838 | ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit); |
| 2869 | } | 2839 | } |
| 2870 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | 2840 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 2871 | 2841 | ||
| ... | @@ -2929,7 +2899,7 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast | ... | @@ -2929,7 +2899,7 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast |
| 2929 | } | 2899 | } |
| 2930 | 2900 | ||
| 2931 | if (operand_type->id == ZigTypeIdVector) { | 2901 | if (operand_type->id == ZigTypeIdVector) { |
| 2932 | is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any); | 2902 | is_zero_bit = ZigLLVMBuildOrReduce(g->builder, is_zero_bit); |
| 2933 | } | 2903 | } |
| 2934 | 2904 | ||
| 2935 | LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); | 2905 | LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); |
| ... | @@ -2986,7 +2956,7 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type | ... | @@ -2986,7 +2956,7 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type |
| 2986 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk"); | 2956 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk"); |
| 2987 | LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); | 2957 | LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); |
| 2988 | if (rhs_type->id == ZigTypeIdVector) { | 2958 | if (rhs_type->id == ZigTypeIdVector) { |
| 2989 | less_than_bit = scalarize_cmp_result(g, less_than_bit, ScalarizePredicate::Any); | 2959 | less_than_bit = ZigLLVMBuildOrReduce(g->builder, less_than_bit); |
| 2990 | } | 2960 | } |
| 2991 | LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block); | 2961 | LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block); |
| 2992 | 2962 | ||
| ... | @@ -4415,6 +4385,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutableGen *executable, IrIn | ... | @@ -4415,6 +4385,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutableGen *executable, IrIn |
| 4415 | } | 4385 | } |
| 4416 | } | 4386 | } |
| 4417 | LLVMTypeRef frame_with_args_type = LLVMStructType(field_types, field_count, false); | 4387 | LLVMTypeRef frame_with_args_type = LLVMStructType(field_types, field_count, false); |
| 4388 | heap::c_allocator.deallocate(field_types, field_count); | ||
| 4418 | LLVMTypeRef ptr_frame_with_args_type = LLVMPointerType(frame_with_args_type, 0); | 4389 | LLVMTypeRef ptr_frame_with_args_type = LLVMPointerType(frame_with_args_type, 0); |
| 4419 | 4390 | ||
| 4420 | casted_frame = LLVMBuildBitCast(g->builder, frame_result_loc, ptr_frame_with_args_type, ""); | 4391 | casted_frame = LLVMBuildBitCast(g->builder, frame_result_loc, ptr_frame_with_args_type, ""); |
| ... | @@ -4429,6 +4400,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutableGen *executable, IrIn | ... | @@ -4429,6 +4400,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutableGen *executable, IrIn |
| 4429 | gen_assign_raw(g, arg_ptr, get_pointer_to_type(g, gen_param_types.at(arg_i), true), | 4400 | gen_assign_raw(g, arg_ptr, get_pointer_to_type(g, gen_param_types.at(arg_i), true), |
| 4430 | gen_param_values.at(arg_i)); | 4401 | gen_param_values.at(arg_i)); |
| 4431 | } | 4402 | } |
| 4403 | gen_param_types.deinit(); | ||
| 4432 | 4404 | ||
| 4433 | if (instruction->modifier == CallModifierAsync) { | 4405 | if (instruction->modifier == CallModifierAsync) { |
| 4434 | gen_resume(g, fn_val, frame_result_loc, ResumeIdCall); | 4406 | gen_resume(g, fn_val, frame_result_loc, ResumeIdCall); |
| ... | @@ -4506,6 +4478,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutableGen *executable, IrIn | ... | @@ -4506,6 +4478,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutableGen *executable, IrIn |
| 4506 | LLVMValueRef result_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); | 4478 | LLVMValueRef result_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); |
| 4507 | return LLVMBuildLoad(g->builder, result_ptr, ""); | 4479 | return LLVMBuildLoad(g->builder, result_ptr, ""); |
| 4508 | } | 4480 | } |
| 4481 | } else { | ||
| 4482 | gen_param_types.deinit(); | ||
| 4509 | } | 4483 | } |
| 4510 | 4484 | ||
| 4511 | if (instruction->new_stack == nullptr || instruction->is_async_call_builtin) { | 4485 | if (instruction->new_stack == nullptr || instruction->is_async_call_builtin) { |
| ... | @@ -4823,12 +4797,15 @@ static LLVMValueRef ir_render_asm_gen(CodeGen *g, IrExecutableGen *executable, I | ... | @@ -4823,12 +4797,15 @@ static LLVMValueRef ir_render_asm_gen(CodeGen *g, IrExecutableGen *executable, I |
| 4823 | ret_type = get_llvm_type(g, instruction->base.value->type); | 4797 | ret_type = get_llvm_type(g, instruction->base.value->type); |
| 4824 | } | 4798 | } |
| 4825 | LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, (unsigned)input_and_output_count, false); | 4799 | LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, (unsigned)input_and_output_count, false); |
| 4800 | heap::c_allocator.deallocate(param_types, input_and_output_count); | ||
| 4826 | 4801 | ||
| 4827 | bool is_volatile = instruction->has_side_effects || (asm_expr->output_list.length == 0); | 4802 | bool is_volatile = instruction->has_side_effects || (asm_expr->output_list.length == 0); |
| 4828 | LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(&llvm_template), buf_len(&llvm_template), | 4803 | LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(&llvm_template), buf_len(&llvm_template), |
| 4829 | buf_ptr(&constraint_buf), buf_len(&constraint_buf), is_volatile, false, LLVMInlineAsmDialectATT); | 4804 | buf_ptr(&constraint_buf), buf_len(&constraint_buf), is_volatile, false, LLVMInlineAsmDialectATT); |
| 4830 | 4805 | ||
| 4831 | return LLVMBuildCall(g->builder, asm_fn, param_values, (unsigned)input_and_output_count, ""); | 4806 | LLVMValueRef built_call = LLVMBuildCall(g->builder, asm_fn, param_values, (unsigned)input_and_output_count, ""); |
| 4807 | heap::c_allocator.deallocate(param_values, input_and_output_count); | ||
| 4808 | return built_call; | ||
| 4832 | } | 4809 | } |
| 4833 | 4810 | ||
| 4834 | static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueRef maybe_handle) { | 4811 | static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueRef maybe_handle) { |
| ... | @@ -5081,6 +5058,8 @@ static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutableGen *executable, IrIns | ... | @@ -5081,6 +5058,8 @@ static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutableGen *executable, IrIns |
| 5081 | incoming_blocks[i] = instruction->incoming_blocks[i]->llvm_exit_block; | 5058 | incoming_blocks[i] = instruction->incoming_blocks[i]->llvm_exit_block; |
| 5082 | } | 5059 | } |
| 5083 | LLVMAddIncoming(phi, incoming_values, incoming_blocks, (unsigned)instruction->incoming_count); | 5060 | LLVMAddIncoming(phi, incoming_values, incoming_blocks, (unsigned)instruction->incoming_count); |
| 5061 | heap::c_allocator.deallocate(incoming_values, instruction->incoming_count); | ||
| 5062 | heap::c_allocator.deallocate(incoming_blocks, instruction->incoming_count); | ||
| 5084 | return phi; | 5063 | return phi; |
| 5085 | } | 5064 | } |
| 5086 | 5065 | ||
| ... | @@ -5471,6 +5450,50 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutableGen *executable, I | ... | @@ -5471,6 +5450,50 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutableGen *executable, I |
| 5471 | return result_loc; | 5450 | return result_loc; |
| 5472 | } | 5451 | } |
| 5473 | 5452 | ||
| 5453 | static LLVMValueRef ir_render_reduce(CodeGen *g, IrExecutableGen *executable, IrInstGenReduce *instruction) { | ||
| 5454 | LLVMValueRef value = ir_llvm_value(g, instruction->value); | ||
| 5455 | |||
| 5456 | ZigType *value_type = instruction->value->value->type; | ||
| 5457 | assert(value_type->id == ZigTypeIdVector); | ||
| 5458 | ZigType *scalar_type = value_type->data.vector.elem_type; | ||
| 5459 | |||
| 5460 | LLVMValueRef result_val; | ||
| 5461 | switch (instruction->op) { | ||
| 5462 | case ReduceOp_and: | ||
| 5463 | assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool); | ||
| 5464 | result_val = ZigLLVMBuildAndReduce(g->builder, value); | ||
| 5465 | break; | ||
| 5466 | case ReduceOp_or: | ||
| 5467 | assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool); | ||
| 5468 | result_val = ZigLLVMBuildOrReduce(g->builder, value); | ||
| 5469 | break; | ||
| 5470 | case ReduceOp_xor: | ||
| 5471 | assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool); | ||
| 5472 | result_val = ZigLLVMBuildXorReduce(g->builder, value); | ||
| 5473 | break; | ||
| 5474 | case ReduceOp_min: { | ||
| 5475 | if (scalar_type->id == ZigTypeIdInt) { | ||
| 5476 | const bool is_signed = scalar_type->data.integral.is_signed; | ||
| 5477 | result_val = ZigLLVMBuildIntMinReduce(g->builder, value, is_signed); | ||
| 5478 | } else if (scalar_type->id == ZigTypeIdFloat) { | ||
| 5479 | result_val = ZigLLVMBuildFPMinReduce(g->builder, value); | ||
| 5480 | } else zig_unreachable(); | ||
| 5481 | } break; | ||
| 5482 | case ReduceOp_max: { | ||
| 5483 | if (scalar_type->id == ZigTypeIdInt) { | ||
| 5484 | const bool is_signed = scalar_type->data.integral.is_signed; | ||
| 5485 | result_val = ZigLLVMBuildIntMaxReduce(g->builder, value, is_signed); | ||
| 5486 | } else if (scalar_type->id == ZigTypeIdFloat) { | ||
| 5487 | result_val = ZigLLVMBuildFPMaxReduce(g->builder, value); | ||
| 5488 | } else zig_unreachable(); | ||
| 5489 | } break; | ||
| 5490 | default: | ||
| 5491 | zig_unreachable(); | ||
| 5492 | } | ||
| 5493 | |||
| 5494 | return result_val; | ||
| 5495 | } | ||
| 5496 | |||
| 5474 | static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutableGen *executable, IrInstGenFence *instruction) { | 5497 | static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutableGen *executable, IrInstGenFence *instruction) { |
| 5475 | LLVMAtomicOrdering atomic_order = to_LLVMAtomicOrdering(instruction->order); | 5498 | LLVMAtomicOrdering atomic_order = to_LLVMAtomicOrdering(instruction->order); |
| 5476 | LLVMBuildFence(g->builder, atomic_order, false, ""); | 5499 | LLVMBuildFence(g->builder, atomic_order, false, ""); |
| ... | @@ -6675,6 +6698,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executabl | ... | @@ -6675,6 +6698,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executabl |
| 6675 | return ir_render_cmpxchg(g, executable, (IrInstGenCmpxchg *)instruction); | 6698 | return ir_render_cmpxchg(g, executable, (IrInstGenCmpxchg *)instruction); |
| 6676 | case IrInstGenIdFence: | 6699 | case IrInstGenIdFence: |
| 6677 | return ir_render_fence(g, executable, (IrInstGenFence *)instruction); | 6700 | return ir_render_fence(g, executable, (IrInstGenFence *)instruction); |
| 6701 | case IrInstGenIdReduce: | ||
| 6702 | return ir_render_reduce(g, executable, (IrInstGenReduce *)instruction); | ||
| 6678 | case IrInstGenIdTruncate: | 6703 | case IrInstGenIdTruncate: |
| 6679 | return ir_render_truncate(g, executable, (IrInstGenTruncate *)instruction); | 6704 | return ir_render_truncate(g, executable, (IrInstGenTruncate *)instruction); |
| 6680 | case IrInstGenIdBoolNot: | 6705 | case IrInstGenIdBoolNot: |
| ... | @@ -7457,10 +7482,14 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n | ... | @@ -7457,10 +7482,14 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n |
| 7457 | } | 7482 | } |
| 7458 | } | 7483 | } |
| 7459 | if (make_unnamed_struct) { | 7484 | if (make_unnamed_struct) { |
| 7460 | return LLVMConstStruct(fields, type_entry->data.structure.gen_field_count, | 7485 | LLVMValueRef unnamed_struct = LLVMConstStruct(fields, type_entry->data.structure.gen_field_count, |
| 7461 | type_entry->data.structure.layout == ContainerLayoutPacked); | 7486 | type_entry->data.structure.layout == ContainerLayoutPacked); |
| 7487 | heap::c_allocator.deallocate(fields, type_entry->data.structure.gen_field_count); | ||
| 7488 | return unnamed_struct; | ||
| 7462 | } else { | 7489 | } else { |
| 7463 | return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, type_entry->data.structure.gen_field_count); | 7490 | LLVMValueRef named_struct = LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, type_entry->data.structure.gen_field_count); |
| 7491 | heap::c_allocator.deallocate(fields, type_entry->data.structure.gen_field_count); | ||
| 7492 | return named_struct; | ||
| 7464 | } | 7493 | } |
| 7465 | } | 7494 | } |
| 7466 | case ZigTypeIdArray: | 7495 | case ZigTypeIdArray: |
| ... | @@ -7485,9 +7514,13 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n | ... | @@ -7485,9 +7514,13 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n |
| 7485 | values[len] = gen_const_val(g, type_entry->data.array.sentinel, ""); | 7514 | values[len] = gen_const_val(g, type_entry->data.array.sentinel, ""); |
| 7486 | } | 7515 | } |
| 7487 | if (make_unnamed_struct) { | 7516 | if (make_unnamed_struct) { |
| 7488 | return LLVMConstStruct(values, full_len, true); | 7517 | LLVMValueRef unnamed_struct = LLVMConstStruct(values, full_len, true); |
| 7518 | heap::c_allocator.deallocate(values, full_len); | ||
| 7519 | return unnamed_struct; | ||
| 7489 | } else { | 7520 | } else { |
| 7490 | return LLVMConstArray(element_type_ref, values, (unsigned)full_len); | 7521 | LLVMValueRef array = LLVMConstArray(element_type_ref, values, (unsigned)full_len); |
| 7522 | heap::c_allocator.deallocate(values, full_len); | ||
| 7523 | return array; | ||
| 7491 | } | 7524 | } |
| 7492 | } | 7525 | } |
| 7493 | case ConstArraySpecialBuf: { | 7526 | case ConstArraySpecialBuf: { |
| ... | @@ -7509,7 +7542,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n | ... | @@ -7509,7 +7542,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n |
| 7509 | ZigValue *elem_value = &const_val->data.x_array.data.s_none.elements[i]; | 7542 | ZigValue *elem_value = &const_val->data.x_array.data.s_none.elements[i]; |
| 7510 | values[i] = gen_const_val(g, elem_value, ""); | 7543 | values[i] = gen_const_val(g, elem_value, ""); |
| 7511 | } | 7544 | } |
| 7512 | return LLVMConstVector(values, len); | 7545 | LLVMValueRef vector = LLVMConstVector(values, len); |
| 7546 | heap::c_allocator.deallocate(values, len); | ||
| 7547 | return vector; | ||
| 7513 | } | 7548 | } |
| 7514 | case ConstArraySpecialBuf: { | 7549 | case ConstArraySpecialBuf: { |
| 7515 | Buf *buf = const_val->data.x_array.data.s_buf; | 7550 | Buf *buf = const_val->data.x_array.data.s_buf; |
| ... | @@ -7518,7 +7553,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n | ... | @@ -7518,7 +7553,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n |
| 7518 | for (uint64_t i = 0; i < len; i += 1) { | 7553 | for (uint64_t i = 0; i < len; i += 1) { |
| 7519 | values[i] = LLVMConstInt(g->builtin_types.entry_u8->llvm_type, buf_ptr(buf)[i], false); | 7554 | values[i] = LLVMConstInt(g->builtin_types.entry_u8->llvm_type, buf_ptr(buf)[i], false); |
| 7520 | } | 7555 | } |
| 7521 | return LLVMConstVector(values, len); | 7556 | LLVMValueRef vector = LLVMConstVector(values, len); |
| 7557 | heap::c_allocator.deallocate(values, len); | ||
| 7558 | return vector; | ||
| 7522 | } | 7559 | } |
| 7523 | } | 7560 | } |
| 7524 | zig_unreachable(); | 7561 | zig_unreachable(); |
| ... | @@ -7740,6 +7777,7 @@ static void generate_error_name_table(CodeGen *g) { | ... | @@ -7740,6 +7777,7 @@ static void generate_error_name_table(CodeGen *g) { |
| 7740 | } | 7777 | } |
| 7741 | 7778 | ||
| 7742 | LLVMValueRef err_name_table_init = LLVMConstArray(get_llvm_type(g, str_type), values, (unsigned)g->errors_by_index.length); | 7779 | LLVMValueRef err_name_table_init = LLVMConstArray(get_llvm_type(g, str_type), values, (unsigned)g->errors_by_index.length); |
| 7780 | heap::c_allocator.deallocate(values, g->errors_by_index.length); | ||
| 7743 | 7781 | ||
| 7744 | g->err_name_table = LLVMAddGlobal(g->module, LLVMTypeOf(err_name_table_init), | 7782 | g->err_name_table = LLVMAddGlobal(g->module, LLVMTypeOf(err_name_table_init), |
| 7745 | get_mangled_name(g, buf_ptr(buf_create_from_str("__zig_err_name_table")))); | 7783 | get_mangled_name(g, buf_ptr(buf_create_from_str("__zig_err_name_table")))); |
| ... | @@ -8631,6 +8669,7 @@ static void define_builtin_fns(CodeGen *g) { | ... | @@ -8631,6 +8669,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 8631 | create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1); | 8669 | create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1); |
| 8632 | create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2); | 8670 | create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2); |
| 8633 | create_builtin_fn(g, BuiltinFnIdSrc, "src", 0); | 8671 | create_builtin_fn(g, BuiltinFnIdSrc, "src", 0); |
| 8672 | create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2); | ||
| 8634 | } | 8673 | } |
| 8635 | 8674 | ||
| 8636 | static const char *bool_to_str(bool b) { | 8675 | static const char *bool_to_str(bool b) { |
| ... | @@ -8816,7 +8855,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { | ... | @@ -8816,7 +8855,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { |
| 8816 | buf_append_str(contents, "/// Deprecated: use `std.Target.current.cpu.arch.endian()`\n"); | 8855 | buf_append_str(contents, "/// Deprecated: use `std.Target.current.cpu.arch.endian()`\n"); |
| 8817 | buf_append_str(contents, "pub const endian = Target.current.cpu.arch.endian();\n"); | 8856 | buf_append_str(contents, "pub const endian = Target.current.cpu.arch.endian();\n"); |
| 8818 | buf_appendf(contents, "pub const output_mode = OutputMode.Obj;\n"); | 8857 | buf_appendf(contents, "pub const output_mode = OutputMode.Obj;\n"); |
| 8819 | buf_appendf(contents, "pub const link_mode = LinkMode.Static;\n"); | 8858 | buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", ZIG_LINK_MODE); |
| 8820 | buf_appendf(contents, "pub const is_test = false;\n"); | 8859 | buf_appendf(contents, "pub const is_test = false;\n"); |
| 8821 | buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); | 8860 | buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); |
| 8822 | buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi); | 8861 | buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi); |
src/stage1/config.h.in+1| ... | @@ -21,5 +21,6 @@ | ... | @@ -21,5 +21,6 @@ |
| 21 | #define ZIG_CLANG_LIBRARIES "@CLANG_LIBRARIES@" | 21 | #define ZIG_CLANG_LIBRARIES "@CLANG_LIBRARIES@" |
| 22 | #define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@" | 22 | #define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@" |
| 23 | #define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@" | 23 | #define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@" |
| 24 | #define ZIG_LINK_MODE "@ZIG_LINK_MODE@" | ||
| 24 | 25 | ||
| 25 | #endif | 26 | #endif |
src/stage1/ir.cpp+392-28| ... | @@ -403,6 +403,8 @@ static void destroy_instruction_src(IrInstSrc *inst) { | ... | @@ -403,6 +403,8 @@ static void destroy_instruction_src(IrInstSrc *inst) { |
| 403 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCmpxchg *>(inst)); | 403 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCmpxchg *>(inst)); |
| 404 | case IrInstSrcIdFence: | 404 | case IrInstSrcIdFence: |
| 405 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFence *>(inst)); | 405 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFence *>(inst)); |
| 406 | case IrInstSrcIdReduce: | ||
| 407 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReduce *>(inst)); | ||
| 406 | case IrInstSrcIdTruncate: | 408 | case IrInstSrcIdTruncate: |
| 407 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTruncate *>(inst)); | 409 | return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTruncate *>(inst)); |
| 408 | case IrInstSrcIdIntCast: | 410 | case IrInstSrcIdIntCast: |
| ... | @@ -637,6 +639,8 @@ void destroy_instruction_gen(IrInstGen *inst) { | ... | @@ -637,6 +639,8 @@ void destroy_instruction_gen(IrInstGen *inst) { |
| 637 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenCmpxchg *>(inst)); | 639 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenCmpxchg *>(inst)); |
| 638 | case IrInstGenIdFence: | 640 | case IrInstGenIdFence: |
| 639 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenFence *>(inst)); | 641 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenFence *>(inst)); |
| 642 | case IrInstGenIdReduce: | ||
| 643 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenReduce *>(inst)); | ||
| 640 | case IrInstGenIdTruncate: | 644 | case IrInstGenIdTruncate: |
| 641 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenTruncate *>(inst)); | 645 | return heap::c_allocator.destroy(reinterpret_cast<IrInstGenTruncate *>(inst)); |
| 642 | case IrInstGenIdShuffleVector: | 646 | case IrInstGenIdShuffleVector: |
| ... | @@ -1312,6 +1316,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcFence *) { | ... | @@ -1312,6 +1316,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcFence *) { |
| 1312 | return IrInstSrcIdFence; | 1316 | return IrInstSrcIdFence; |
| 1313 | } | 1317 | } |
| 1314 | 1318 | ||
| 1319 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcReduce *) { | ||
| 1320 | return IrInstSrcIdReduce; | ||
| 1321 | } | ||
| 1322 | |||
| 1315 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcTruncate *) { | 1323 | static constexpr IrInstSrcId ir_inst_id(IrInstSrcTruncate *) { |
| 1316 | return IrInstSrcIdTruncate; | 1324 | return IrInstSrcIdTruncate; |
| 1317 | } | 1325 | } |
| ... | @@ -1776,6 +1784,10 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenFence *) { | ... | @@ -1776,6 +1784,10 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenFence *) { |
| 1776 | return IrInstGenIdFence; | 1784 | return IrInstGenIdFence; |
| 1777 | } | 1785 | } |
| 1778 | 1786 | ||
| 1787 | static constexpr IrInstGenId ir_inst_id(IrInstGenReduce *) { | ||
| 1788 | return IrInstGenIdReduce; | ||
| 1789 | } | ||
| 1790 | |||
| 1779 | static constexpr IrInstGenId ir_inst_id(IrInstGenTruncate *) { | 1791 | static constexpr IrInstGenId ir_inst_id(IrInstGenTruncate *) { |
| 1780 | return IrInstGenIdTruncate; | 1792 | return IrInstGenIdTruncate; |
| 1781 | } | 1793 | } |
| ... | @@ -3503,6 +3515,29 @@ static IrInstGen *ir_build_fence_gen(IrAnalyze *ira, IrInst *source_instr, Atomi | ... | @@ -3503,6 +3515,29 @@ static IrInstGen *ir_build_fence_gen(IrAnalyze *ira, IrInst *source_instr, Atomi |
| 3503 | return &instruction->base; | 3515 | return &instruction->base; |
| 3504 | } | 3516 | } |
| 3505 | 3517 | ||
| 3518 | static IrInstSrc *ir_build_reduce(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *op, IrInstSrc *value) { | ||
| 3519 | IrInstSrcReduce *instruction = ir_build_instruction<IrInstSrcReduce>(irb, scope, source_node); | ||
| 3520 | instruction->op = op; | ||
| 3521 | instruction->value = value; | ||
| 3522 | |||
| 3523 | ir_ref_instruction(op, irb->current_basic_block); | ||
| 3524 | ir_ref_instruction(value, irb->current_basic_block); | ||
| 3525 | |||
| 3526 | return &instruction->base; | ||
| 3527 | } | ||
| 3528 | |||
| 3529 | static IrInstGen *ir_build_reduce_gen(IrAnalyze *ira, IrInst *source_instruction, ReduceOp op, IrInstGen *value, ZigType *result_type) { | ||
| 3530 | IrInstGenReduce *instruction = ir_build_inst_gen<IrInstGenReduce>(&ira->new_irb, | ||
| 3531 | source_instruction->scope, source_instruction->source_node); | ||
| 3532 | instruction->base.value->type = result_type; | ||
| 3533 | instruction->op = op; | ||
| 3534 | instruction->value = value; | ||
| 3535 | |||
| 3536 | ir_ref_inst_gen(value); | ||
| 3537 | |||
| 3538 | return &instruction->base; | ||
| 3539 | } | ||
| 3540 | |||
| 3506 | static IrInstSrc *ir_build_truncate(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, | 3541 | static IrInstSrc *ir_build_truncate(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, |
| 3507 | IrInstSrc *dest_type, IrInstSrc *target) | 3542 | IrInstSrc *dest_type, IrInstSrc *target) |
| 3508 | { | 3543 | { |
| ... | @@ -6581,6 +6616,21 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod | ... | @@ -6581,6 +6616,21 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod |
| 6581 | IrInstSrc *fence = ir_build_fence(irb, scope, node, arg0_value); | 6616 | IrInstSrc *fence = ir_build_fence(irb, scope, node, arg0_value); |
| 6582 | return ir_lval_wrap(irb, scope, fence, lval, result_loc); | 6617 | return ir_lval_wrap(irb, scope, fence, lval, result_loc); |
| 6583 | } | 6618 | } |
| 6619 | case BuiltinFnIdReduce: | ||
| 6620 | { | ||
| 6621 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | ||
| 6622 | IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); | ||
| 6623 | if (arg0_value == irb->codegen->invalid_inst_src) | ||
| 6624 | return arg0_value; | ||
| 6625 | |||
| 6626 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | ||
| 6627 | IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); | ||
| 6628 | if (arg1_value == irb->codegen->invalid_inst_src) | ||
| 6629 | return arg1_value; | ||
| 6630 | |||
| 6631 | IrInstSrc *reduce = ir_build_reduce(irb, scope, node, arg0_value, arg1_value); | ||
| 6632 | return ir_lval_wrap(irb, scope, reduce, lval, result_loc); | ||
| 6633 | } | ||
| 6584 | case BuiltinFnIdDivExact: | 6634 | case BuiltinFnIdDivExact: |
| 6585 | { | 6635 | { |
| 6586 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | 6636 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| ... | @@ -9607,6 +9657,7 @@ static IrInstSrc *ir_gen_continue(IrBuilderSrc *irb, Scope *continue_scope, AstN | ... | @@ -9607,6 +9657,7 @@ static IrInstSrc *ir_gen_continue(IrBuilderSrc *irb, Scope *continue_scope, AstN |
| 9607 | ScopeRuntime *scope_runtime = runtime_scopes.at(i); | 9657 | ScopeRuntime *scope_runtime = runtime_scopes.at(i); |
| 9608 | ir_mark_gen(ir_build_check_runtime_scope(irb, continue_scope, node, scope_runtime->is_comptime, is_comptime)); | 9658 | ir_mark_gen(ir_build_check_runtime_scope(irb, continue_scope, node, scope_runtime->is_comptime, is_comptime)); |
| 9609 | } | 9659 | } |
| 9660 | runtime_scopes.deinit(); | ||
| 9610 | 9661 | ||
| 9611 | IrBasicBlockSrc *dest_block = loop_scope->continue_block; | 9662 | IrBasicBlockSrc *dest_block = loop_scope->continue_block; |
| 9612 | if (!ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, nullptr, nullptr)) | 9663 | if (!ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, nullptr, nullptr)) |
| ... | @@ -15933,6 +15984,24 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstGen *value, bool *out) { | ... | @@ -15933,6 +15984,24 @@ static bool ir_resolve_comptime(IrAnalyze *ira, IrInstGen *value, bool *out) { |
| 15933 | return ir_resolve_bool(ira, value, out); | 15984 | return ir_resolve_bool(ira, value, out); |
| 15934 | } | 15985 | } |
| 15935 | 15986 | ||
| 15987 | static bool ir_resolve_reduce_op(IrAnalyze *ira, IrInstGen *value, ReduceOp *out) { | ||
| 15988 | if (type_is_invalid(value->value->type)) | ||
| 15989 | return false; | ||
| 15990 | |||
| 15991 | ZigType *reduce_op_type = get_builtin_type(ira->codegen, "ReduceOp"); | ||
| 15992 | |||
| 15993 | IrInstGen *casted_value = ir_implicit_cast(ira, value, reduce_op_type); | ||
| 15994 | if (type_is_invalid(casted_value->value->type)) | ||
| 15995 | return false; | ||
| 15996 | |||
| 15997 | ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); | ||
| 15998 | if (!const_val) | ||
| 15999 | return false; | ||
| 16000 | |||
| 16001 | *out = (ReduceOp)bigint_as_u32(&const_val->data.x_enum_tag); | ||
| 16002 | return true; | ||
| 16003 | } | ||
| 16004 | |||
| 15936 | static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstGen *value, AtomicOrder *out) { | 16005 | static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstGen *value, AtomicOrder *out) { |
| 15937 | if (type_is_invalid(value->value->type)) | 16006 | if (type_is_invalid(value->value->type)) |
| 15938 | return false; | 16007 | return false; |
| ... | @@ -21527,6 +21596,7 @@ static IrInstGen *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstSrcPhi *phi_i | ... | @@ -21527,6 +21596,7 @@ static IrInstGen *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstSrcPhi *phi_i |
| 21527 | predecessor->instruction_list.append(instrs_to_move.pop()); | 21596 | predecessor->instruction_list.append(instrs_to_move.pop()); |
| 21528 | } | 21597 | } |
| 21529 | predecessor->instruction_list.append(branch_instruction); | 21598 | predecessor->instruction_list.append(branch_instruction); |
| 21599 | instrs_to_move.deinit(); | ||
| 21530 | } | 21600 | } |
| 21531 | } | 21601 | } |
| 21532 | 21602 | ||
| ... | @@ -21577,7 +21647,10 @@ static IrInstGen *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstSrcPhi *phi_i | ... | @@ -21577,7 +21647,10 @@ static IrInstGen *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstSrcPhi *phi_i |
| 21577 | } | 21647 | } |
| 21578 | 21648 | ||
| 21579 | if (new_incoming_blocks.length == 1) { | 21649 | if (new_incoming_blocks.length == 1) { |
| 21580 | return new_incoming_values.at(0); | 21650 | IrInstGen *incoming_value = new_incoming_values.at(0); |
| 21651 | new_incoming_blocks.deinit(); | ||
| 21652 | new_incoming_values.deinit(); | ||
| 21653 | return incoming_value; | ||
| 21581 | } | 21654 | } |
| 21582 | 21655 | ||
| 21583 | ZigType *resolved_type = nullptr; | 21656 | ZigType *resolved_type = nullptr; |
| ... | @@ -24140,6 +24213,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc | ... | @@ -24140,6 +24213,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc |
| 24140 | first_non_const_instruction = result_loc; | 24213 | first_non_const_instruction = result_loc; |
| 24141 | } | 24214 | } |
| 24142 | } | 24215 | } |
| 24216 | heap::c_allocator.deallocate(field_assign_nodes, actual_field_count); | ||
| 24143 | if (any_missing) | 24217 | if (any_missing) |
| 24144 | return ira->codegen->invalid_inst_gen; | 24218 | return ira->codegen->invalid_inst_gen; |
| 24145 | 24219 | ||
| ... | @@ -24155,6 +24229,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc | ... | @@ -24155,6 +24229,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc |
| 24155 | } | 24229 | } |
| 24156 | } | 24230 | } |
| 24157 | 24231 | ||
| 24232 | const_ptrs.deinit(); | ||
| 24158 | IrInstGen *result = ir_get_deref(ira, source_instr, result_loc, nullptr); | 24233 | IrInstGen *result = ir_get_deref(ira, source_instr, result_loc, nullptr); |
| 24159 | 24234 | ||
| 24160 | if (is_comptime && !instr_is_comptime(result)) { | 24235 | if (is_comptime && !instr_is_comptime(result)) { |
| ... | @@ -25028,7 +25103,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, IrInst *source_instr, | ... | @@ -25028,7 +25103,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, IrInst *source_instr, |
| 25028 | fields[2]->special = ConstValSpecialStatic; | 25103 | fields[2]->special = ConstValSpecialStatic; |
| 25029 | fields[2]->type = ira->codegen->builtin_types.entry_bool; | 25104 | fields[2]->type = ira->codegen->builtin_types.entry_bool; |
| 25030 | fields[2]->data.x_bool = attrs_type->data.pointer.is_volatile; | 25105 | fields[2]->data.x_bool = attrs_type->data.pointer.is_volatile; |
| 25031 | // alignment: u32 | 25106 | // alignment: comptime_int |
| 25032 | ensure_field_index(result->type, "alignment", 3); | 25107 | ensure_field_index(result->type, "alignment", 3); |
| 25033 | fields[3]->type = ira->codegen->builtin_types.entry_num_lit_int; | 25108 | fields[3]->type = ira->codegen->builtin_types.entry_num_lit_int; |
| 25034 | if (attrs_type->data.pointer.explicit_alignment != 0) { | 25109 | if (attrs_type->data.pointer.explicit_alignment != 0) { |
| ... | @@ -25432,11 +25507,17 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy | ... | @@ -25432,11 +25507,17 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy |
| 25432 | union_field_val->special = ConstValSpecialStatic; | 25507 | union_field_val->special = ConstValSpecialStatic; |
| 25433 | union_field_val->type = type_info_union_field_type; | 25508 | union_field_val->type = type_info_union_field_type; |
| 25434 | 25509 | ||
| 25435 | ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2); | 25510 | ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 3); |
| 25511 | // field_type: type | ||
| 25436 | inner_fields[1]->special = ConstValSpecialStatic; | 25512 | inner_fields[1]->special = ConstValSpecialStatic; |
| 25437 | inner_fields[1]->type = ira->codegen->builtin_types.entry_type; | 25513 | inner_fields[1]->type = ira->codegen->builtin_types.entry_type; |
| 25438 | inner_fields[1]->data.x_type = union_field->type_entry; | 25514 | inner_fields[1]->data.x_type = union_field->type_entry; |
| 25439 | 25515 | ||
| 25516 | // alignment: comptime_int | ||
| 25517 | inner_fields[2]->special = ConstValSpecialStatic; | ||
| 25518 | inner_fields[2]->type = ira->codegen->builtin_types.entry_num_lit_int; | ||
| 25519 | bigint_init_unsigned(&inner_fields[2]->data.x_bigint, union_field->align); | ||
| 25520 | |||
| 25440 | ZigValue *name = create_const_str_lit(ira->codegen, union_field->name)->data.x_ptr.data.ref.pointee; | 25521 | ZigValue *name = create_const_str_lit(ira->codegen, union_field->name)->data.x_ptr.data.ref.pointee; |
| 25441 | init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(union_field->name), true); | 25522 | init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(union_field->name), true); |
| 25442 | 25523 | ||
| ... | @@ -25503,7 +25584,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy | ... | @@ -25503,7 +25584,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy |
| 25503 | struct_field_val->special = ConstValSpecialStatic; | 25584 | struct_field_val->special = ConstValSpecialStatic; |
| 25504 | struct_field_val->type = type_info_struct_field_type; | 25585 | struct_field_val->type = type_info_struct_field_type; |
| 25505 | 25586 | ||
| 25506 | ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 4); | 25587 | ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 5); |
| 25507 | 25588 | ||
| 25508 | inner_fields[1]->special = ConstValSpecialStatic; | 25589 | inner_fields[1]->special = ConstValSpecialStatic; |
| 25509 | inner_fields[1]->type = ira->codegen->builtin_types.entry_type; | 25590 | inner_fields[1]->type = ira->codegen->builtin_types.entry_type; |
| ... | @@ -25519,10 +25600,16 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy | ... | @@ -25519,10 +25600,16 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy |
| 25519 | } | 25600 | } |
| 25520 | set_optional_payload(inner_fields[2], struct_field->init_val); | 25601 | set_optional_payload(inner_fields[2], struct_field->init_val); |
| 25521 | 25602 | ||
| 25603 | // is_comptime: bool | ||
| 25522 | inner_fields[3]->special = ConstValSpecialStatic; | 25604 | inner_fields[3]->special = ConstValSpecialStatic; |
| 25523 | inner_fields[3]->type = ira->codegen->builtin_types.entry_bool; | 25605 | inner_fields[3]->type = ira->codegen->builtin_types.entry_bool; |
| 25524 | inner_fields[3]->data.x_bool = struct_field->is_comptime; | 25606 | inner_fields[3]->data.x_bool = struct_field->is_comptime; |
| 25525 | 25607 | ||
| 25608 | // alignment: comptime_int | ||
| 25609 | inner_fields[4]->special = ConstValSpecialStatic; | ||
| 25610 | inner_fields[4]->type = ira->codegen->builtin_types.entry_num_lit_int; | ||
| 25611 | bigint_init_unsigned(&inner_fields[4]->data.x_bigint, struct_field->align); | ||
| 25612 | |||
| 25526 | ZigValue *name = create_const_str_lit(ira->codegen, struct_field->name)->data.x_ptr.data.ref.pointee; | 25613 | ZigValue *name = create_const_str_lit(ira->codegen, struct_field->name)->data.x_ptr.data.ref.pointee; |
| 25527 | init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(struct_field->name), true); | 25614 | init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(struct_field->name), true); |
| 25528 | 25615 | ||
| ... | @@ -25553,7 +25640,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy | ... | @@ -25553,7 +25640,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy |
| 25553 | result->special = ConstValSpecialStatic; | 25640 | result->special = ConstValSpecialStatic; |
| 25554 | result->type = ir_type_info_get_type(ira, "Fn", nullptr); | 25641 | result->type = ir_type_info_get_type(ira, "Fn", nullptr); |
| 25555 | 25642 | ||
| 25556 | ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 5); | 25643 | ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 6); |
| 25557 | result->data.x_struct.fields = fields; | 25644 | result->data.x_struct.fields = fields; |
| 25558 | 25645 | ||
| 25559 | // calling_convention: TypeInfo.CallingConvention | 25646 | // calling_convention: TypeInfo.CallingConvention |
| ... | @@ -25561,38 +25648,42 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy | ... | @@ -25561,38 +25648,42 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy |
| 25561 | fields[0]->special = ConstValSpecialStatic; | 25648 | fields[0]->special = ConstValSpecialStatic; |
| 25562 | fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention"); | 25649 | fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention"); |
| 25563 | bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); | 25650 | bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); |
| 25651 | // alignment: u29 | ||
| 25652 | ensure_field_index(result->type, "alignment", 1); | ||
| 25653 | fields[1]->special = ConstValSpecialStatic; | ||
| 25654 | fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; | ||
| 25655 | bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.fn.fn_type_id.alignment); | ||
| 25564 | // is_generic: bool | 25656 | // is_generic: bool |
| 25565 | ensure_field_index(result->type, "is_generic", 1); | 25657 | ensure_field_index(result->type, "is_generic", 2); |
| 25566 | bool is_generic = type_entry->data.fn.is_generic; | 25658 | bool is_generic = type_entry->data.fn.is_generic; |
| 25567 | fields[1]->special = ConstValSpecialStatic; | ||
| 25568 | fields[1]->type = ira->codegen->builtin_types.entry_bool; | ||
| 25569 | fields[1]->data.x_bool = is_generic; | ||
| 25570 | // is_varargs: bool | ||
| 25571 | ensure_field_index(result->type, "is_var_args", 2); | ||
| 25572 | bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args; | ||
| 25573 | fields[2]->special = ConstValSpecialStatic; | 25659 | fields[2]->special = ConstValSpecialStatic; |
| 25574 | fields[2]->type = ira->codegen->builtin_types.entry_bool; | 25660 | fields[2]->type = ira->codegen->builtin_types.entry_bool; |
| 25575 | fields[2]->data.x_bool = type_entry->data.fn.fn_type_id.is_var_args; | 25661 | fields[2]->data.x_bool = is_generic; |
| 25576 | // return_type: ?type | 25662 | // is_varargs: bool |
| 25577 | ensure_field_index(result->type, "return_type", 3); | 25663 | ensure_field_index(result->type, "is_var_args", 3); |
| 25664 | bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args; | ||
| 25578 | fields[3]->special = ConstValSpecialStatic; | 25665 | fields[3]->special = ConstValSpecialStatic; |
| 25579 | fields[3]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); | 25666 | fields[3]->type = ira->codegen->builtin_types.entry_bool; |
| 25667 | fields[3]->data.x_bool = is_varargs; | ||
| 25668 | // return_type: ?type | ||
| 25669 | ensure_field_index(result->type, "return_type", 4); | ||
| 25670 | fields[4]->special = ConstValSpecialStatic; | ||
| 25671 | fields[4]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); | ||
| 25580 | if (type_entry->data.fn.fn_type_id.return_type == nullptr) | 25672 | if (type_entry->data.fn.fn_type_id.return_type == nullptr) |
| 25581 | fields[3]->data.x_optional = nullptr; | 25673 | fields[4]->data.x_optional = nullptr; |
| 25582 | else { | 25674 | else { |
| 25583 | ZigValue *return_type = ira->codegen->pass1_arena->create<ZigValue>(); | 25675 | ZigValue *return_type = ira->codegen->pass1_arena->create<ZigValue>(); |
| 25584 | return_type->special = ConstValSpecialStatic; | 25676 | return_type->special = ConstValSpecialStatic; |
| 25585 | return_type->type = ira->codegen->builtin_types.entry_type; | 25677 | return_type->type = ira->codegen->builtin_types.entry_type; |
| 25586 | return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type; | 25678 | return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type; |
| 25587 | fields[3]->data.x_optional = return_type; | 25679 | fields[4]->data.x_optional = return_type; |
| 25588 | } | 25680 | } |
| 25589 | // args: []TypeInfo.FnArg | 25681 | // args: []TypeInfo.FnArg |
| 25590 | ZigType *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr); | 25682 | ZigType *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr); |
| 25591 | if ((err = type_resolve(ira->codegen, type_info_fn_arg_type, ResolveStatusSizeKnown))) { | 25683 | if ((err = type_resolve(ira->codegen, type_info_fn_arg_type, ResolveStatusSizeKnown))) { |
| 25592 | zig_unreachable(); | 25684 | zig_unreachable(); |
| 25593 | } | 25685 | } |
| 25594 | size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count - | 25686 | size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count; |
| 25595 | (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC); | ||
| 25596 | 25687 | ||
| 25597 | ZigValue *fn_arg_array = ira->codegen->pass1_arena->create<ZigValue>(); | 25688 | ZigValue *fn_arg_array = ira->codegen->pass1_arena->create<ZigValue>(); |
| 25598 | fn_arg_array->special = ConstValSpecialStatic; | 25689 | fn_arg_array->special = ConstValSpecialStatic; |
| ... | @@ -25600,7 +25691,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy | ... | @@ -25600,7 +25691,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy |
| 25600 | fn_arg_array->data.x_array.special = ConstArraySpecialNone; | 25691 | fn_arg_array->data.x_array.special = ConstArraySpecialNone; |
| 25601 | fn_arg_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(fn_arg_count); | 25692 | fn_arg_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(fn_arg_count); |
| 25602 | 25693 | ||
| 25603 | init_const_slice(ira->codegen, fields[4], fn_arg_array, 0, fn_arg_count, false); | 25694 | init_const_slice(ira->codegen, fields[5], fn_arg_array, 0, fn_arg_count, false); |
| 25604 | 25695 | ||
| 25605 | for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { | 25696 | for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { |
| 25606 | FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index]; | 25697 | FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index]; |
| ... | @@ -25869,8 +25960,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -25869,8 +25960,9 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 25869 | buf_sprintf("sentinels are only allowed on slices and unknown-length pointers")); | 25960 | buf_sprintf("sentinels are only allowed on slices and unknown-length pointers")); |
| 25870 | return ira->codegen->invalid_inst_gen->value->type; | 25961 | return ira->codegen->invalid_inst_gen->value->type; |
| 25871 | } | 25962 | } |
| 25872 | BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 3); | 25963 | |
| 25873 | if (bi == nullptr) | 25964 | BigInt *alignment = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 3); |
| 25965 | if (alignment == nullptr) | ||
| 25874 | return ira->codegen->invalid_inst_gen->value->type; | 25966 | return ira->codegen->invalid_inst_gen->value->type; |
| 25875 | 25967 | ||
| 25876 | bool is_const; | 25968 | bool is_const; |
| ... | @@ -25897,7 +25989,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -25897,7 +25989,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 25897 | is_const, | 25989 | is_const, |
| 25898 | is_volatile, | 25990 | is_volatile, |
| 25899 | ptr_len, | 25991 | ptr_len, |
| 25900 | bigint_as_u32(bi), | 25992 | bigint_as_u32(alignment), |
| 25901 | 0, // bit_offset_in_host | 25993 | 0, // bit_offset_in_host |
| 25902 | 0, // host_int_bytes | 25994 | 0, // host_int_bytes |
| 25903 | is_allowzero, | 25995 | is_allowzero, |
| ... | @@ -26134,6 +26226,10 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26134,6 +26226,10 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26134 | } | 26226 | } |
| 26135 | if ((err = get_const_field_bool(ira, source_instr->source_node, field_value, "is_comptime", 3, &field->is_comptime))) | 26227 | if ((err = get_const_field_bool(ira, source_instr->source_node, field_value, "is_comptime", 3, &field->is_comptime))) |
| 26136 | return ira->codegen->invalid_inst_gen->value->type; | 26228 | return ira->codegen->invalid_inst_gen->value->type; |
| 26229 | BigInt *alignment = get_const_field_lit_int(ira, source_instr->source_node, field_value, "alignment", 4); | ||
| 26230 | if (alignment == nullptr) | ||
| 26231 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26232 | field->align = bigint_as_u32(alignment); | ||
| 26137 | } | 26233 | } |
| 26138 | 26234 | ||
| 26139 | return entry; | 26235 | return entry; |
| ... | @@ -26151,6 +26247,13 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26151,6 +26247,13 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26151 | ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); | 26247 | ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); |
| 26152 | 26248 | ||
| 26153 | ZigType *tag_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "tag_type", 1); | 26249 | ZigType *tag_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "tag_type", 1); |
| 26250 | if (type_is_invalid(tag_type)) | ||
| 26251 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26252 | if (tag_type->id != ZigTypeIdInt) { | ||
| 26253 | ir_add_error(ira, source_instr, buf_sprintf( | ||
| 26254 | "TypeInfo.Enum.tag_type must be an integer type, not '%s'", buf_ptr(&tag_type->name))); | ||
| 26255 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26256 | } | ||
| 26154 | 26257 | ||
| 26155 | ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 2); | 26258 | ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 2); |
| 26156 | if (fields_value == nullptr) | 26259 | if (fields_value == nullptr) |
| ... | @@ -26296,14 +26399,113 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI | ... | @@ -26296,14 +26399,113 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI |
| 26296 | return ira->codegen->invalid_inst_gen->value->type; | 26399 | return ira->codegen->invalid_inst_gen->value->type; |
| 26297 | field->type_val = type_value; | 26400 | field->type_val = type_value; |
| 26298 | field->type_entry = type_value->data.x_type; | 26401 | field->type_entry = type_value->data.x_type; |
| 26402 | BigInt *alignment = get_const_field_lit_int(ira, source_instr->source_node, field_value, "alignment", 2); | ||
| 26403 | if (alignment == nullptr) | ||
| 26404 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26405 | field->align = bigint_as_u32(alignment); | ||
| 26299 | } | 26406 | } |
| 26300 | return entry; | 26407 | return entry; |
| 26301 | } | 26408 | } |
| 26302 | case ZigTypeIdFn: | 26409 | case ZigTypeIdFn: |
| 26303 | case ZigTypeIdBoundFn: | 26410 | case ZigTypeIdBoundFn: { |
| 26304 | ir_add_error(ira, source_instr, buf_sprintf( | 26411 | assert(payload->special == ConstValSpecialStatic); |
| 26305 | "@Type not available for 'TypeInfo.%s'", type_id_name(tagTypeId))); | 26412 | assert(payload->type == ir_type_info_get_type(ira, "Fn", nullptr)); |
| 26306 | return ira->codegen->invalid_inst_gen->value->type; | 26413 | |
| 26414 | ZigValue *cc_value = get_const_field(ira, source_instr->source_node, payload, "calling_convention", 0); | ||
| 26415 | if (cc_value == nullptr) | ||
| 26416 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26417 | assert(cc_value->special == ConstValSpecialStatic); | ||
| 26418 | assert(cc_value->type == get_builtin_type(ira->codegen, "CallingConvention")); | ||
| 26419 | CallingConvention cc = (CallingConvention)bigint_as_u32(&cc_value->data.x_enum_tag); | ||
| 26420 | |||
| 26421 | BigInt *alignment = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 1); | ||
| 26422 | if (alignment == nullptr) | ||
| 26423 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26424 | |||
| 26425 | Error err; | ||
| 26426 | bool is_generic; | ||
| 26427 | if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_generic", 2, &is_generic))) | ||
| 26428 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26429 | if (is_generic) { | ||
| 26430 | ir_add_error(ira, source_instr, buf_sprintf("TypeInfo.Fn.is_generic must be false for @Type")); | ||
| 26431 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26432 | } | ||
| 26433 | |||
| 26434 | bool is_var_args; | ||
| 26435 | if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_var_args", 3, &is_var_args))) | ||
| 26436 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26437 | if (is_var_args && cc != CallingConventionC) { | ||
| 26438 | ir_add_error(ira, source_instr, buf_sprintf("varargs functions must have C calling convention")); | ||
| 26439 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26440 | } | ||
| 26441 | |||
| 26442 | ZigType *return_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "return_type", 4); | ||
| 26443 | if (return_type == nullptr) { | ||
| 26444 | ir_add_error(ira, source_instr, buf_sprintf("TypeInfo.Fn.return_type must be non-null for @Type")); | ||
| 26445 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26446 | } | ||
| 26447 | |||
| 26448 | ZigValue *args_value = get_const_field(ira, source_instr->source_node, payload, "args", 5); | ||
| 26449 | if (args_value == nullptr) | ||
| 26450 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26451 | assert(args_value->special == ConstValSpecialStatic); | ||
| 26452 | assert(is_slice(args_value->type)); | ||
| 26453 | ZigValue *args_ptr = args_value->data.x_struct.fields[slice_ptr_index]; | ||
| 26454 | ZigValue *args_len_value = args_value->data.x_struct.fields[slice_len_index]; | ||
| 26455 | size_t args_len = bigint_as_usize(&args_len_value->data.x_bigint); | ||
| 26456 | |||
| 26457 | FnTypeId fn_type_id = {}; | ||
| 26458 | fn_type_id.return_type = return_type; | ||
| 26459 | fn_type_id.param_info = heap::c_allocator.allocate<FnTypeParamInfo>(args_len); | ||
| 26460 | fn_type_id.param_count = args_len; | ||
| 26461 | fn_type_id.next_param_index = args_len; | ||
| 26462 | fn_type_id.is_var_args = is_var_args; | ||
| 26463 | fn_type_id.cc = cc; | ||
| 26464 | fn_type_id.alignment = bigint_as_u32(alignment); | ||
| 26465 | |||
| 26466 | assert(args_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); | ||
| 26467 | assert(args_ptr->data.x_ptr.data.base_array.elem_index == 0); | ||
| 26468 | ZigValue *args_arr = args_ptr->data.x_ptr.data.base_array.array_val; | ||
| 26469 | assert(args_arr->special == ConstValSpecialStatic); | ||
| 26470 | assert(args_arr->data.x_array.special == ConstArraySpecialNone); | ||
| 26471 | for (size_t i = 0; i < args_len; i++) { | ||
| 26472 | ZigValue *arg_value = &args_arr->data.x_array.data.s_none.elements[i]; | ||
| 26473 | assert(arg_value->type == ir_type_info_get_type(ira, "FnArg", nullptr)); | ||
| 26474 | FnTypeParamInfo *info = &fn_type_id.param_info[i]; | ||
| 26475 | Error err; | ||
| 26476 | bool is_generic; | ||
| 26477 | if ((err = get_const_field_bool(ira, source_instr->source_node, arg_value, "is_generic", 0, &is_generic))) | ||
| 26478 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26479 | if (is_generic) { | ||
| 26480 | ir_add_error(ira, source_instr, buf_sprintf("TypeInfo.FnArg.is_generic must be false for @Type")); | ||
| 26481 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26482 | } | ||
| 26483 | if ((err = get_const_field_bool(ira, source_instr->source_node, arg_value, "is_noalias", 1, &info->is_noalias))) | ||
| 26484 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26485 | ZigType *type = get_const_field_meta_type_optional( | ||
| 26486 | ira, source_instr->source_node, arg_value, "arg_type", 2); | ||
| 26487 | if (type == nullptr) { | ||
| 26488 | ir_add_error(ira, source_instr, buf_sprintf("TypeInfo.FnArg.arg_type must be non-null for @Type")); | ||
| 26489 | return ira->codegen->invalid_inst_gen->value->type; | ||
| 26490 | } | ||
| 26491 | info->type = type; | ||
| 26492 | } | ||
| 26493 | |||
| 26494 | ZigType *entry = get_fn_type(ira->codegen, &fn_type_id); | ||
| 26495 | |||
| 26496 | switch (tagTypeId) { | ||
| 26497 | case ZigTypeIdFn: | ||
| 26498 | return entry; | ||
| 26499 | case ZigTypeIdBoundFn: { | ||
| 26500 | ZigType *bound_fn_entry = new_type_table_entry(ZigTypeIdBoundFn); | ||
| 26501 | bound_fn_entry->name = *buf_sprintf("(bound %s)", buf_ptr(&entry->name)); | ||
| 26502 | bound_fn_entry->data.bound_fn.fn_type = entry; | ||
| 26503 | return bound_fn_entry; | ||
| 26504 | } | ||
| 26505 | default: | ||
| 26506 | zig_unreachable(); | ||
| 26507 | } | ||
| 26508 | } | ||
| 26307 | } | 26509 | } |
| 26308 | zig_unreachable(); | 26510 | zig_unreachable(); |
| 26309 | } | 26511 | } |
| ... | @@ -26676,6 +26878,161 @@ static IrInstGen *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstSrcCmpxch | ... | @@ -26676,6 +26878,161 @@ static IrInstGen *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstSrcCmpxch |
| 26676 | success_order, failure_order, instruction->is_weak, result_loc); | 26878 | success_order, failure_order, instruction->is_weak, result_loc); |
| 26677 | } | 26879 | } |
| 26678 | 26880 | ||
| 26881 | static ErrorMsg *ir_eval_reduce(IrAnalyze *ira, IrInst *source_instr, ReduceOp op, ZigValue *value, ZigValue *out_value) { | ||
| 26882 | assert(value->type->id == ZigTypeIdVector); | ||
| 26883 | ZigType *scalar_type = value->type->data.vector.elem_type; | ||
| 26884 | const size_t len = value->type->data.vector.len; | ||
| 26885 | assert(len > 0); | ||
| 26886 | |||
| 26887 | out_value->type = scalar_type; | ||
| 26888 | out_value->special = ConstValSpecialStatic; | ||
| 26889 | |||
| 26890 | if (scalar_type->id == ZigTypeIdBool) { | ||
| 26891 | ZigValue *first_elem_val = &value->data.x_array.data.s_none.elements[0]; | ||
| 26892 | |||
| 26893 | bool result = first_elem_val->data.x_bool; | ||
| 26894 | for (size_t i = 1; i < len; i++) { | ||
| 26895 | ZigValue *elem_val = &value->data.x_array.data.s_none.elements[i]; | ||
| 26896 | |||
| 26897 | switch (op) { | ||
| 26898 | case ReduceOp_and: | ||
| 26899 | result = result && elem_val->data.x_bool; | ||
| 26900 | if (!result) break; // Short circuit | ||
| 26901 | break; | ||
| 26902 | case ReduceOp_or: | ||
| 26903 | result = result || elem_val->data.x_bool; | ||
| 26904 | if (result) break; // Short circuit | ||
| 26905 | break; | ||
| 26906 | case ReduceOp_xor: | ||
| 26907 | result = result != elem_val->data.x_bool; | ||
| 26908 | break; | ||
| 26909 | default: | ||
| 26910 | zig_unreachable(); | ||
| 26911 | } | ||
| 26912 | } | ||
| 26913 | |||
| 26914 | out_value->data.x_bool = result; | ||
| 26915 | return nullptr; | ||
| 26916 | } | ||
| 26917 | |||
| 26918 | if (op != ReduceOp_min && op != ReduceOp_max) { | ||
| 26919 | ZigValue *first_elem_val = &value->data.x_array.data.s_none.elements[0]; | ||
| 26920 | |||
| 26921 | copy_const_val(ira->codegen, out_value, first_elem_val); | ||
| 26922 | |||
| 26923 | for (size_t i = 1; i < len; i++) { | ||
| 26924 | ZigValue *elem_val = &value->data.x_array.data.s_none.elements[i]; | ||
| 26925 | |||
| 26926 | IrBinOp bin_op; | ||
| 26927 | switch (op) { | ||
| 26928 | case ReduceOp_and: bin_op = IrBinOpBinAnd; break; | ||
| 26929 | case ReduceOp_or: bin_op = IrBinOpBinOr; break; | ||
| 26930 | case ReduceOp_xor: bin_op = IrBinOpBinXor; break; | ||
| 26931 | default: zig_unreachable(); | ||
| 26932 | } | ||
| 26933 | |||
| 26934 | ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, | ||
| 26935 | out_value, bin_op, elem_val, out_value); | ||
| 26936 | if (msg != nullptr) | ||
| 26937 | return msg; | ||
| 26938 | } | ||
| 26939 | |||
| 26940 | return nullptr; | ||
| 26941 | } | ||
| 26942 | |||
| 26943 | ZigValue *candidate_elem_val = &value->data.x_array.data.s_none.elements[0]; | ||
| 26944 | |||
| 26945 | ZigValue *dummy_cmp_value = ira->codegen->pass1_arena->create<ZigValue>(); | ||
| 26946 | for (size_t i = 1; i < len; i++) { | ||
| 26947 | ZigValue *elem_val = &value->data.x_array.data.s_none.elements[i]; | ||
| 26948 | |||
| 26949 | IrBinOp bin_op; | ||
| 26950 | switch (op) { | ||
| 26951 | case ReduceOp_min: bin_op = IrBinOpCmpLessThan; break; | ||
| 26952 | case ReduceOp_max: bin_op = IrBinOpCmpGreaterThan; break; | ||
| 26953 | default: zig_unreachable(); | ||
| 26954 | } | ||
| 26955 | |||
| 26956 | ErrorMsg *msg = ir_eval_bin_op_cmp_scalar(ira, source_instr, | ||
| 26957 | elem_val, bin_op, candidate_elem_val, dummy_cmp_value); | ||
| 26958 | if (msg != nullptr) | ||
| 26959 | return msg; | ||
| 26960 | |||
| 26961 | if (dummy_cmp_value->data.x_bool) | ||
| 26962 | candidate_elem_val = elem_val; | ||
| 26963 | } | ||
| 26964 | |||
| 26965 | ira->codegen->pass1_arena->destroy(dummy_cmp_value); | ||
| 26966 | copy_const_val(ira->codegen, out_value, candidate_elem_val); | ||
| 26967 | |||
| 26968 | return nullptr; | ||
| 26969 | } | ||
| 26970 | |||
| 26971 | static IrInstGen *ir_analyze_instruction_reduce(IrAnalyze *ira, IrInstSrcReduce *instruction) { | ||
| 26972 | IrInstGen *op_inst = instruction->op->child; | ||
| 26973 | if (type_is_invalid(op_inst->value->type)) | ||
| 26974 | return ira->codegen->invalid_inst_gen; | ||
| 26975 | |||
| 26976 | IrInstGen *value_inst = instruction->value->child; | ||
| 26977 | if (type_is_invalid(value_inst->value->type)) | ||
| 26978 | return ira->codegen->invalid_inst_gen; | ||
| 26979 | |||
| 26980 | ZigType *value_type = value_inst->value->type; | ||
| 26981 | if (value_type->id != ZigTypeIdVector) { | ||
| 26982 | ir_add_error(ira, &value_inst->base, | ||
| 26983 | buf_sprintf("expected vector type, found '%s'", | ||
| 26984 | buf_ptr(&value_type->name))); | ||
| 26985 | return ira->codegen->invalid_inst_gen; | ||
| 26986 | } | ||
| 26987 | |||
| 26988 | ReduceOp op; | ||
| 26989 | if (!ir_resolve_reduce_op(ira, op_inst, &op)) | ||
| 26990 | return ira->codegen->invalid_inst_gen; | ||
| 26991 | |||
| 26992 | ZigType *elem_type = value_type->data.vector.elem_type; | ||
| 26993 | switch (elem_type->id) { | ||
| 26994 | case ZigTypeIdInt: | ||
| 26995 | break; | ||
| 26996 | case ZigTypeIdBool: | ||
| 26997 | if (op > ReduceOp_xor) { | ||
| 26998 | ir_add_error(ira, &op_inst->base, | ||
| 26999 | buf_sprintf("invalid operation for '%s' type", | ||
| 27000 | buf_ptr(&elem_type->name))); | ||
| 27001 | return ira->codegen->invalid_inst_gen; | ||
| 27002 | } break; | ||
| 27003 | case ZigTypeIdFloat: | ||
| 27004 | if (op < ReduceOp_min) { | ||
| 27005 | ir_add_error(ira, &op_inst->base, | ||
| 27006 | buf_sprintf("invalid operation for '%s' type", | ||
| 27007 | buf_ptr(&elem_type->name))); | ||
| 27008 | return ira->codegen->invalid_inst_gen; | ||
| 27009 | } break; | ||
| 27010 | default: | ||
| 27011 | // Vectors cannot have child types other than those listed above | ||
| 27012 | zig_unreachable(); | ||
| 27013 | } | ||
| 27014 | |||
| 27015 | // special case zero bit types | ||
| 27016 | switch (type_has_one_possible_value(ira->codegen, elem_type)) { | ||
| 27017 | case OnePossibleValueInvalid: | ||
| 27018 | return ira->codegen->invalid_inst_gen; | ||
| 27019 | case OnePossibleValueYes: | ||
| 27020 | return ir_const_move(ira, &instruction->base.base, | ||
| 27021 | get_the_one_possible_value(ira->codegen, elem_type)); | ||
| 27022 | case OnePossibleValueNo: | ||
| 27023 | break; | ||
| 27024 | } | ||
| 27025 | |||
| 27026 | if (instr_is_comptime(value_inst)) { | ||
| 27027 | IrInstGen *result = ir_const(ira, &instruction->base.base, elem_type); | ||
| 27028 | if (ir_eval_reduce(ira, &instruction->base.base, op, value_inst->value, result->value)) | ||
| 27029 | return ira->codegen->invalid_inst_gen; | ||
| 27030 | return result; | ||
| 27031 | } | ||
| 27032 | |||
| 27033 | return ir_build_reduce_gen(ira, &instruction->base.base, op, value_inst, elem_type); | ||
| 27034 | } | ||
| 27035 | |||
| 26679 | static IrInstGen *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstSrcFence *instruction) { | 27036 | static IrInstGen *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstSrcFence *instruction) { |
| 26680 | IrInstGen *order_inst = instruction->order->child; | 27037 | IrInstGen *order_inst = instruction->order->child; |
| 26681 | if (type_is_invalid(order_inst->value->type)) | 27038 | if (type_is_invalid(order_inst->value->type)) |
| ... | @@ -29835,6 +30192,7 @@ static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrIn | ... | @@ -29835,6 +30192,7 @@ static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrIn |
| 29835 | buf_write_value_bytes(ira->codegen, buf, val); | 30192 | buf_write_value_bytes(ira->codegen, buf, val); |
| 29836 | if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, result->value))) | 30193 | if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, result->value))) |
| 29837 | return ira->codegen->invalid_inst_gen; | 30194 | return ira->codegen->invalid_inst_gen; |
| 30195 | heap::c_allocator.deallocate(buf, src_size_bytes); | ||
| 29838 | return result; | 30196 | return result; |
| 29839 | } | 30197 | } |
| 29840 | 30198 | ||
| ... | @@ -30872,6 +31230,8 @@ static IrInstGen *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstSrcBi | ... | @@ -30872,6 +31230,8 @@ static IrInstGen *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstSrcBi |
| 30872 | ira->codegen->is_big_endian, | 31230 | ira->codegen->is_big_endian, |
| 30873 | int_type->data.integral.is_signed); | 31231 | int_type->data.integral.is_signed); |
| 30874 | 31232 | ||
| 31233 | heap::c_allocator.deallocate(comptime_buf, buf_size); | ||
| 31234 | heap::c_allocator.deallocate(result_buf, buf_size); | ||
| 30875 | return result; | 31235 | return result; |
| 30876 | } | 31236 | } |
| 30877 | 31237 | ||
| ... | @@ -31424,6 +31784,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc | ... | @@ -31424,6 +31784,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc |
| 31424 | return ir_analyze_instruction_cmpxchg(ira, (IrInstSrcCmpxchg *)instruction); | 31784 | return ir_analyze_instruction_cmpxchg(ira, (IrInstSrcCmpxchg *)instruction); |
| 31425 | case IrInstSrcIdFence: | 31785 | case IrInstSrcIdFence: |
| 31426 | return ir_analyze_instruction_fence(ira, (IrInstSrcFence *)instruction); | 31786 | return ir_analyze_instruction_fence(ira, (IrInstSrcFence *)instruction); |
| 31787 | case IrInstSrcIdReduce: | ||
| 31788 | return ir_analyze_instruction_reduce(ira, (IrInstSrcReduce *)instruction); | ||
| 31427 | case IrInstSrcIdTruncate: | 31789 | case IrInstSrcIdTruncate: |
| 31428 | return ir_analyze_instruction_truncate(ira, (IrInstSrcTruncate *)instruction); | 31790 | return ir_analyze_instruction_truncate(ira, (IrInstSrcTruncate *)instruction); |
| 31429 | case IrInstSrcIdIntCast: | 31791 | case IrInstSrcIdIntCast: |
| ... | @@ -31811,6 +32173,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) { | ... | @@ -31811,6 +32173,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) { |
| 31811 | case IrInstGenIdNegation: | 32173 | case IrInstGenIdNegation: |
| 31812 | case IrInstGenIdNegationWrapping: | 32174 | case IrInstGenIdNegationWrapping: |
| 31813 | case IrInstGenIdWasmMemorySize: | 32175 | case IrInstGenIdWasmMemorySize: |
| 32176 | case IrInstGenIdReduce: | ||
| 31814 | return false; | 32177 | return false; |
| 31815 | 32178 | ||
| 31816 | case IrInstGenIdAsm: | 32179 | case IrInstGenIdAsm: |
| ... | @@ -31980,6 +32343,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { | ... | @@ -31980,6 +32343,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { |
| 31980 | case IrInstSrcIdSpillEnd: | 32343 | case IrInstSrcIdSpillEnd: |
| 31981 | case IrInstSrcIdWasmMemorySize: | 32344 | case IrInstSrcIdWasmMemorySize: |
| 31982 | case IrInstSrcIdSrc: | 32345 | case IrInstSrcIdSrc: |
| 32346 | case IrInstSrcIdReduce: | ||
| 31983 | return false; | 32347 | return false; |
| 31984 | 32348 | ||
| 31985 | case IrInstSrcIdAsm: | 32349 | case IrInstSrcIdAsm: |
src/stage1/ir_print.cpp+35| ... | @@ -200,6 +200,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) { | ... | @@ -200,6 +200,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) { |
| 200 | return "SrcCmpxchg"; | 200 | return "SrcCmpxchg"; |
| 201 | case IrInstSrcIdFence: | 201 | case IrInstSrcIdFence: |
| 202 | return "SrcFence"; | 202 | return "SrcFence"; |
| 203 | case IrInstSrcIdReduce: | ||
| 204 | return "SrcReduce"; | ||
| 203 | case IrInstSrcIdTruncate: | 205 | case IrInstSrcIdTruncate: |
| 204 | return "SrcTruncate"; | 206 | return "SrcTruncate"; |
| 205 | case IrInstSrcIdIntCast: | 207 | case IrInstSrcIdIntCast: |
| ... | @@ -436,6 +438,8 @@ const char* ir_inst_gen_type_str(IrInstGenId id) { | ... | @@ -436,6 +438,8 @@ const char* ir_inst_gen_type_str(IrInstGenId id) { |
| 436 | return "GenCmpxchg"; | 438 | return "GenCmpxchg"; |
| 437 | case IrInstGenIdFence: | 439 | case IrInstGenIdFence: |
| 438 | return "GenFence"; | 440 | return "GenFence"; |
| 441 | case IrInstGenIdReduce: | ||
| 442 | return "GenReduce"; | ||
| 439 | case IrInstGenIdTruncate: | 443 | case IrInstGenIdTruncate: |
| 440 | return "GenTruncate"; | 444 | return "GenTruncate"; |
| 441 | case IrInstGenIdBoolNot: | 445 | case IrInstGenIdBoolNot: |
| ... | @@ -1584,6 +1588,14 @@ static void ir_print_fence(IrPrintSrc *irp, IrInstSrcFence *instruction) { | ... | @@ -1584,6 +1588,14 @@ static void ir_print_fence(IrPrintSrc *irp, IrInstSrcFence *instruction) { |
| 1584 | fprintf(irp->f, ")"); | 1588 | fprintf(irp->f, ")"); |
| 1585 | } | 1589 | } |
| 1586 | 1590 | ||
| 1591 | static void ir_print_reduce(IrPrintSrc *irp, IrInstSrcReduce *instruction) { | ||
| 1592 | fprintf(irp->f, "@reduce("); | ||
| 1593 | ir_print_other_inst_src(irp, instruction->op); | ||
| 1594 | fprintf(irp->f, ", "); | ||
| 1595 | ir_print_other_inst_src(irp, instruction->value); | ||
| 1596 | fprintf(irp->f, ")"); | ||
| 1597 | } | ||
| 1598 | |||
| 1587 | static const char *atomic_order_str(AtomicOrder order) { | 1599 | static const char *atomic_order_str(AtomicOrder order) { |
| 1588 | switch (order) { | 1600 | switch (order) { |
| 1589 | case AtomicOrderUnordered: return "Unordered"; | 1601 | case AtomicOrderUnordered: return "Unordered"; |
| ... | @@ -1600,6 +1612,23 @@ static void ir_print_fence(IrPrintGen *irp, IrInstGenFence *instruction) { | ... | @@ -1600,6 +1612,23 @@ static void ir_print_fence(IrPrintGen *irp, IrInstGenFence *instruction) { |
| 1600 | fprintf(irp->f, "fence %s", atomic_order_str(instruction->order)); | 1612 | fprintf(irp->f, "fence %s", atomic_order_str(instruction->order)); |
| 1601 | } | 1613 | } |
| 1602 | 1614 | ||
| 1615 | static const char *reduce_op_str(ReduceOp op) { | ||
| 1616 | switch (op) { | ||
| 1617 | case ReduceOp_and: return "And"; | ||
| 1618 | case ReduceOp_or: return "Or"; | ||
| 1619 | case ReduceOp_xor: return "Xor"; | ||
| 1620 | case ReduceOp_min: return "Min"; | ||
| 1621 | case ReduceOp_max: return "Max"; | ||
| 1622 | } | ||
| 1623 | zig_unreachable(); | ||
| 1624 | } | ||
| 1625 | |||
| 1626 | static void ir_print_reduce(IrPrintGen *irp, IrInstGenReduce *instruction) { | ||
| 1627 | fprintf(irp->f, "@reduce(.%s, ", reduce_op_str(instruction->op)); | ||
| 1628 | ir_print_other_inst_gen(irp, instruction->value); | ||
| 1629 | fprintf(irp->f, ")"); | ||
| 1630 | } | ||
| 1631 | |||
| 1603 | static void ir_print_truncate(IrPrintSrc *irp, IrInstSrcTruncate *instruction) { | 1632 | static void ir_print_truncate(IrPrintSrc *irp, IrInstSrcTruncate *instruction) { |
| 1604 | fprintf(irp->f, "@truncate("); | 1633 | fprintf(irp->f, "@truncate("); |
| 1605 | ir_print_other_inst_src(irp, instruction->dest_type); | 1634 | ir_print_other_inst_src(irp, instruction->dest_type); |
| ... | @@ -2749,6 +2778,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai | ... | @@ -2749,6 +2778,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trai |
| 2749 | case IrInstSrcIdFence: | 2778 | case IrInstSrcIdFence: |
| 2750 | ir_print_fence(irp, (IrInstSrcFence *)instruction); | 2779 | ir_print_fence(irp, (IrInstSrcFence *)instruction); |
| 2751 | break; | 2780 | break; |
| 2781 | case IrInstSrcIdReduce: | ||
| 2782 | ir_print_reduce(irp, (IrInstSrcReduce *)instruction); | ||
| 2783 | break; | ||
| 2752 | case IrInstSrcIdTruncate: | 2784 | case IrInstSrcIdTruncate: |
| 2753 | ir_print_truncate(irp, (IrInstSrcTruncate *)instruction); | 2785 | ir_print_truncate(irp, (IrInstSrcTruncate *)instruction); |
| 2754 | break; | 2786 | break; |
| ... | @@ -3097,6 +3129,9 @@ static void ir_print_inst_gen(IrPrintGen *irp, IrInstGen *instruction, bool trai | ... | @@ -3097,6 +3129,9 @@ static void ir_print_inst_gen(IrPrintGen *irp, IrInstGen *instruction, bool trai |
| 3097 | case IrInstGenIdFence: | 3129 | case IrInstGenIdFence: |
| 3098 | ir_print_fence(irp, (IrInstGenFence *)instruction); | 3130 | ir_print_fence(irp, (IrInstGenFence *)instruction); |
| 3099 | break; | 3131 | break; |
| 3132 | case IrInstGenIdReduce: | ||
| 3133 | ir_print_reduce(irp, (IrInstGenReduce *)instruction); | ||
| 3134 | break; | ||
| 3100 | case IrInstGenIdTruncate: | 3135 | case IrInstGenIdTruncate: |
| 3101 | ir_print_truncate(irp, (IrInstGenTruncate *)instruction); | 3136 | ir_print_truncate(irp, (IrInstGenTruncate *)instruction); |
| 3102 | break; | 3137 | break; |
src/stage1/os.cpp+22-1052| ... | @@ -94,105 +94,6 @@ static clock_serv_t macos_monotonic_clock; | ... | @@ -94,105 +94,6 @@ static clock_serv_t macos_monotonic_clock; |
| 94 | extern char **environ; | 94 | extern char **environ; |
| 95 | #endif | 95 | #endif |
| 96 | 96 | ||
| 97 | #if defined(ZIG_OS_POSIX) | ||
| 98 | static void populate_termination(Termination *term, int status) { | ||
| 99 | if (WIFEXITED(status)) { | ||
| 100 | term->how = TerminationIdClean; | ||
| 101 | term->code = WEXITSTATUS(status); | ||
| 102 | } else if (WIFSIGNALED(status)) { | ||
| 103 | term->how = TerminationIdSignaled; | ||
| 104 | term->code = WTERMSIG(status); | ||
| 105 | } else if (WIFSTOPPED(status)) { | ||
| 106 | term->how = TerminationIdStopped; | ||
| 107 | term->code = WSTOPSIG(status); | ||
| 108 | } else { | ||
| 109 | term->how = TerminationIdUnknown; | ||
| 110 | term->code = status; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | static void os_spawn_process_posix(ZigList<const char *> &args, Termination *term) { | ||
| 115 | const char **argv = heap::c_allocator.allocate<const char *>(args.length + 1); | ||
| 116 | for (size_t i = 0; i < args.length; i += 1) { | ||
| 117 | argv[i] = args.at(i); | ||
| 118 | } | ||
| 119 | argv[args.length] = nullptr; | ||
| 120 | |||
| 121 | pid_t pid; | ||
| 122 | int rc = posix_spawnp(&pid, args.at(0), nullptr, nullptr, const_cast<char *const*>(argv), environ); | ||
| 123 | if (rc != 0) { | ||
| 124 | zig_panic("unable to spawn %s: %s", args.at(0), strerror(rc)); | ||
| 125 | } | ||
| 126 | |||
| 127 | int status; | ||
| 128 | waitpid(pid, &status, 0); | ||
| 129 | populate_termination(term, status); | ||
| 130 | } | ||
| 131 | #endif | ||
| 132 | |||
| 133 | #if defined(ZIG_OS_WINDOWS) | ||
| 134 | |||
| 135 | static void os_windows_create_command_line(Buf *command_line, ZigList<const char *> &args) { | ||
| 136 | buf_resize(command_line, 0); | ||
| 137 | const char *prefix = "\""; | ||
| 138 | for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) { | ||
| 139 | const char *arg = args.at(arg_i); | ||
| 140 | buf_append_str(command_line, prefix); | ||
| 141 | prefix = " \""; | ||
| 142 | size_t arg_len = strlen(arg); | ||
| 143 | for (size_t c_i = 0; c_i < arg_len; c_i += 1) { | ||
| 144 | if (arg[c_i] == '\"') { | ||
| 145 | zig_panic("TODO"); | ||
| 146 | } | ||
| 147 | buf_append_char(command_line, arg[c_i]); | ||
| 148 | } | ||
| 149 | buf_append_char(command_line, '\"'); | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | static void os_spawn_process_windows(ZigList<const char *> &args, Termination *term) { | ||
| 154 | Buf command_line = BUF_INIT; | ||
| 155 | os_windows_create_command_line(&command_line, args); | ||
| 156 | |||
| 157 | PROCESS_INFORMATION piProcInfo = {0}; | ||
| 158 | STARTUPINFOW siStartInfo = {0}; | ||
| 159 | siStartInfo.cb = sizeof(STARTUPINFOW); | ||
| 160 | |||
| 161 | Slice<uint8_t> exe_slice = str(args.at(0)); | ||
| 162 | auto exe_utf16_slice = Slice<WCHAR>::alloc(exe_slice.len + 1); | ||
| 163 | exe_utf16_slice.ptr[utf8_to_utf16le(exe_utf16_slice.ptr, exe_slice)] = 0; | ||
| 164 | |||
| 165 | auto command_line_utf16 = Slice<WCHAR>::alloc(buf_len(&command_line) + 1); | ||
| 166 | command_line_utf16.ptr[utf8_to_utf16le(command_line_utf16.ptr, buf_to_slice(&command_line))] = 0; | ||
| 167 | |||
| 168 | BOOL success = CreateProcessW(exe_utf16_slice.ptr, command_line_utf16.ptr, nullptr, nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr, | ||
| 169 | &siStartInfo, &piProcInfo); | ||
| 170 | |||
| 171 | if (!success) { | ||
| 172 | zig_panic("CreateProcess failed. exe: %s command_line: %s", args.at(0), buf_ptr(&command_line)); | ||
| 173 | } | ||
| 174 | |||
| 175 | WaitForSingleObject(piProcInfo.hProcess, INFINITE); | ||
| 176 | |||
| 177 | DWORD exit_code; | ||
| 178 | if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) { | ||
| 179 | zig_panic("GetExitCodeProcess failed"); | ||
| 180 | } | ||
| 181 | term->how = TerminationIdClean; | ||
| 182 | term->code = exit_code; | ||
| 183 | } | ||
| 184 | #endif | ||
| 185 | |||
| 186 | void os_spawn_process(ZigList<const char *> &args, Termination *term) { | ||
| 187 | #if defined(ZIG_OS_WINDOWS) | ||
| 188 | os_spawn_process_windows(args, term); | ||
| 189 | #elif defined(ZIG_OS_POSIX) | ||
| 190 | os_spawn_process_posix(args, term); | ||
| 191 | #else | ||
| 192 | #error "missing os_spawn_process implementation" | ||
| 193 | #endif | ||
| 194 | } | ||
| 195 | |||
| 196 | void os_path_dirname(Buf *full_path, Buf *out_dirname) { | 97 | void os_path_dirname(Buf *full_path, Buf *out_dirname) { |
| 197 | return os_path_split(full_path, out_dirname, nullptr); | 98 | return os_path_split(full_path, out_dirname, nullptr); |
| 198 | } | 99 | } |
| ... | @@ -280,71 +181,27 @@ void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { | ... | @@ -280,71 +181,27 @@ void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { |
| 280 | buf_append_buf(out_full_path, basename); | 181 | buf_append_buf(out_full_path, basename); |
| 281 | } | 182 | } |
| 282 | 183 | ||
| 283 | Error os_path_real(Buf *rel_path, Buf *out_abs_path) { | ||
| 284 | #if defined(ZIG_OS_WINDOWS) | ||
| 285 | PathSpace rel_path_space = slice_to_prefixed_file_w(buf_to_slice(rel_path)); | ||
| 286 | PathSpace out_abs_path_space; | ||
| 287 | |||
| 288 | if (_wfullpath(&out_abs_path_space.data.items[0], &rel_path_space.data.items[0], PATH_MAX_WIDE) == nullptr) { | ||
| 289 | zig_panic("_wfullpath failed"); | ||
| 290 | } | ||
| 291 | utf16le_ptr_to_utf8(out_abs_path, &out_abs_path_space.data.items[0]); | ||
| 292 | return ErrorNone; | ||
| 293 | #elif defined(ZIG_OS_POSIX) | ||
| 294 | buf_resize(out_abs_path, PATH_MAX + 1); | ||
| 295 | char *result = realpath(buf_ptr(rel_path), buf_ptr(out_abs_path)); | ||
| 296 | if (!result) { | ||
| 297 | int err = errno; | ||
| 298 | if (err == EACCES) { | ||
| 299 | return ErrorAccess; | ||
| 300 | } else if (err == ENOENT) { | ||
| 301 | return ErrorFileNotFound; | ||
| 302 | } else if (err == ENOMEM) { | ||
| 303 | return ErrorNoMem; | ||
| 304 | } else { | ||
| 305 | return ErrorFileSystem; | ||
| 306 | } | ||
| 307 | } | ||
| 308 | buf_resize(out_abs_path, strlen(buf_ptr(out_abs_path))); | ||
| 309 | return ErrorNone; | ||
| 310 | #else | ||
| 311 | #error "missing os_path_real implementation" | ||
| 312 | #endif | ||
| 313 | } | ||
| 314 | |||
| 315 | #if defined(ZIG_OS_WINDOWS) | ||
| 316 | // Ported from std/os/path.zig | ||
| 317 | static bool isAbsoluteWindows(Slice<uint8_t> path) { | ||
| 318 | if (path.ptr[0] == '/') | ||
| 319 | return true; | ||
| 320 | |||
| 321 | if (path.ptr[0] == '\\') { | ||
| 322 | return true; | ||
| 323 | } | ||
| 324 | if (path.len < 3) { | ||
| 325 | return false; | ||
| 326 | } | ||
| 327 | if (path.ptr[1] == ':') { | ||
| 328 | if (path.ptr[2] == '/') | ||
| 329 | return true; | ||
| 330 | if (path.ptr[2] == '\\') | ||
| 331 | return true; | ||
| 332 | } | ||
| 333 | return false; | ||
| 334 | } | ||
| 335 | #endif | ||
| 336 | |||
| 337 | bool os_path_is_absolute(Buf *path) { | ||
| 338 | #if defined(ZIG_OS_WINDOWS) | ||
| 339 | return isAbsoluteWindows(buf_to_slice(path)); | ||
| 340 | #elif defined(ZIG_OS_POSIX) | ||
| 341 | return buf_ptr(path)[0] == '/'; | ||
| 342 | #else | ||
| 343 | #error "missing os_path_is_absolute implementation" | ||
| 344 | #endif | ||
| 345 | } | ||
| 346 | 184 | ||
| 347 | #if defined(ZIG_OS_WINDOWS) | 185 | #if defined(ZIG_OS_WINDOWS)		 |
| 186 | // Ported from std/os/path.zig		 | ||
| 187 | static bool isAbsoluteWindows(Slice<uint8_t> path) {		 | ||
| 188 | if (path.ptr[0] == '/')		 | ||
| 189 | return true;		 | ||
| 190 | |||
| 191 | if (path.ptr[0] == '\\') {		 | ||
| 192 | return true;		 | ||
| 193 | }		 | ||
| 194 | if (path.len < 3) {		 | ||
| 195 | return false;		 | ||
| 196 | }		 | ||
| 197 | if (path.ptr[1] == ':') {		 | ||
| 198 | if (path.ptr[2] == '/')		 | ||
| 199 | return true;		 | ||
| 200 | if (path.ptr[2] == '\\')		 | ||
| 201 | return true;		 | ||
| 202 | }		 | ||
| 203 | return false;		 | ||
| 204 | }		 | ||
| 348 | 205 | ||
| 349 | enum WindowsPathKind { | 206 | enum WindowsPathKind { |
| 350 | WindowsPathKindNone, | 207 | WindowsPathKindNone, |
| ... | @@ -687,7 +544,7 @@ static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) { | ... | @@ -687,7 +544,7 @@ static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) { |
| 687 | size_t max_size = 0; | 544 | size_t max_size = 0; |
| 688 | for (size_t i = 0; i < paths_len; i += 1) { | 545 | for (size_t i = 0; i < paths_len; i += 1) { |
| 689 | Buf *p = paths_ptr[i]; | 546 | Buf *p = paths_ptr[i]; |
| 690 | if (os_path_is_absolute(p)) { | 547 | if (buf_ptr(p)[0] == '/') { |
| 691 | first_index = i; | 548 | first_index = i; |
| 692 | have_abs = true; | 549 | have_abs = true; |
| 693 | max_size = 0; | 550 | max_size = 0; |
| ... | @@ -748,6 +605,7 @@ static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) { | ... | @@ -748,6 +605,7 @@ static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) { |
| 748 | 605 | ||
| 749 | Buf return_value = BUF_INIT; | 606 | Buf return_value = BUF_INIT; |
| 750 | buf_init_from_mem(&return_value, (char *)result_ptr, result_index); | 607 | buf_init_from_mem(&return_value, (char *)result_ptr, result_index); |
| 608 | heap::c_allocator.deallocate(result_ptr, result_len); | ||
| 751 | return return_value; | 609 | return return_value; |
| 752 | } | 610 | } |
| 753 | #endif | 611 | #endif |
| ... | @@ -786,256 +644,6 @@ Error os_fetch_file(FILE *f, Buf *out_buf) { | ... | @@ -786,256 +644,6 @@ Error os_fetch_file(FILE *f, Buf *out_buf) { |
| 786 | zig_unreachable(); | 644 | zig_unreachable(); |
| 787 | } | 645 | } |
| 788 | 646 | ||
| 789 | Error os_file_exists(Buf *full_path, bool *result) { | ||
| 790 | #if defined(ZIG_OS_WINDOWS) | ||
| 791 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); | ||
| 792 | *result = GetFileAttributesW(&path_space.data.items[0]) != INVALID_FILE_ATTRIBUTES; | ||
| 793 | return ErrorNone; | ||
| 794 | #else | ||
| 795 | *result = access(buf_ptr(full_path), F_OK) != -1; | ||
| 796 | return ErrorNone; | ||
| 797 | #endif | ||
| 798 | } | ||
| 799 | |||
| 800 | #if defined(ZIG_OS_POSIX) | ||
| 801 | static Error os_exec_process_posix(ZigList<const char *> &args, | ||
| 802 | Termination *term, Buf *out_stderr, Buf *out_stdout) | ||
| 803 | { | ||
| 804 | int stdin_pipe[2]; | ||
| 805 | int stdout_pipe[2]; | ||
| 806 | int stderr_pipe[2]; | ||
| 807 | int err_pipe[2]; | ||
| 808 | |||
| 809 | int err; | ||
| 810 | if ((err = pipe(stdin_pipe))) | ||
| 811 | zig_panic("pipe failed"); | ||
| 812 | if ((err = pipe(stdout_pipe))) | ||
| 813 | zig_panic("pipe failed"); | ||
| 814 | if ((err = pipe(stderr_pipe))) | ||
| 815 | zig_panic("pipe failed"); | ||
| 816 | if ((err = pipe(err_pipe))) | ||
| 817 | zig_panic("pipe failed"); | ||
| 818 | |||
| 819 | pid_t pid = fork(); | ||
| 820 | if (pid == -1) | ||
| 821 | zig_panic("fork failed: %s", strerror(errno)); | ||
| 822 | if (pid == 0) { | ||
| 823 | // child | ||
| 824 | if (dup2(stdin_pipe[0], STDIN_FILENO) == -1) | ||
| 825 | zig_panic("dup2 failed"); | ||
| 826 | |||
| 827 | if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1) | ||
| 828 | zig_panic("dup2 failed"); | ||
| 829 | |||
| 830 | if (dup2(stderr_pipe[1], STDERR_FILENO) == -1) | ||
| 831 | zig_panic("dup2 failed"); | ||
| 832 | |||
| 833 | const char **argv = heap::c_allocator.allocate<const char *>(args.length + 1); | ||
| 834 | argv[args.length] = nullptr; | ||
| 835 | for (size_t i = 0; i < args.length; i += 1) { | ||
| 836 | argv[i] = args.at(i); | ||
| 837 | } | ||
| 838 | execvp(argv[0], const_cast<char * const *>(argv)); | ||
| 839 | Error report_err = ErrorUnexpected; | ||
| 840 | if (errno == ENOENT) { | ||
| 841 | report_err = ErrorFileNotFound; | ||
| 842 | } | ||
| 843 | if (write(err_pipe[1], &report_err, sizeof(Error)) == -1) { | ||
| 844 | zig_panic("write failed"); | ||
| 845 | } | ||
| 846 | exit(1); | ||
| 847 | } else { | ||
| 848 | // parent | ||
| 849 | close(stdin_pipe[0]); | ||
| 850 | close(stdin_pipe[1]); | ||
| 851 | close(stdout_pipe[1]); | ||
| 852 | close(stderr_pipe[1]); | ||
| 853 | |||
| 854 | int status; | ||
| 855 | waitpid(pid, &status, 0); | ||
| 856 | populate_termination(term, status); | ||
| 857 | |||
| 858 | FILE *stdout_f = fdopen(stdout_pipe[0], "rb"); | ||
| 859 | FILE *stderr_f = fdopen(stderr_pipe[0], "rb"); | ||
| 860 | Error err1 = os_fetch_file(stdout_f, out_stdout); | ||
| 861 | Error err2 = os_fetch_file(stderr_f, out_stderr); | ||
| 862 | |||
| 863 | fclose(stdout_f); | ||
| 864 | fclose(stderr_f); | ||
| 865 | |||
| 866 | if (err1) return err1; | ||
| 867 | if (err2) return err2; | ||
| 868 | |||
| 869 | Error child_err = ErrorNone; | ||
| 870 | if (write(err_pipe[1], &child_err, sizeof(Error)) == -1) { | ||
| 871 | zig_panic("write failed"); | ||
| 872 | } | ||
| 873 | close(err_pipe[1]); | ||
| 874 | if (read(err_pipe[0], &child_err, sizeof(Error)) == -1) { | ||
| 875 | zig_panic("write failed"); | ||
| 876 | } | ||
| 877 | close(err_pipe[0]); | ||
| 878 | return child_err; | ||
| 879 | } | ||
| 880 | } | ||
| 881 | #endif | ||
| 882 | |||
| 883 | #if defined(ZIG_OS_WINDOWS) | ||
| 884 | |||
| 885 | //static void win32_panic(const char *str) { | ||
| 886 | // DWORD err = GetLastError(); | ||
| 887 | // LPSTR messageBuffer = nullptr; | ||
| 888 | // FormatMessageA( | ||
| 889 | // FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | ||
| 890 | // NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); | ||
| 891 | // zig_panic(str, messageBuffer); | ||
| 892 | // LocalFree(messageBuffer); | ||
| 893 | //} | ||
| 894 | |||
| 895 | static Error os_exec_process_windows(ZigList<const char *> &args, | ||
| 896 | Termination *term, Buf *out_stderr, Buf *out_stdout) | ||
| 897 | { | ||
| 898 | Buf command_line = BUF_INIT; | ||
| 899 | os_windows_create_command_line(&command_line, args); | ||
| 900 | |||
| 901 | HANDLE g_hChildStd_IN_Rd = NULL; | ||
| 902 | HANDLE g_hChildStd_IN_Wr = NULL; | ||
| 903 | HANDLE g_hChildStd_OUT_Rd = NULL; | ||
| 904 | HANDLE g_hChildStd_OUT_Wr = NULL; | ||
| 905 | HANDLE g_hChildStd_ERR_Rd = NULL; | ||
| 906 | HANDLE g_hChildStd_ERR_Wr = NULL; | ||
| 907 | |||
| 908 | SECURITY_ATTRIBUTES saAttr; | ||
| 909 | saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); | ||
| 910 | saAttr.bInheritHandle = TRUE; | ||
| 911 | saAttr.lpSecurityDescriptor = NULL; | ||
| 912 | |||
| 913 | if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) { | ||
| 914 | zig_panic("StdoutRd CreatePipe"); | ||
| 915 | } | ||
| 916 | |||
| 917 | if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { | ||
| 918 | zig_panic("Stdout SetHandleInformation"); | ||
| 919 | } | ||
| 920 | |||
| 921 | if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) { | ||
| 922 | zig_panic("stderr CreatePipe"); | ||
| 923 | } | ||
| 924 | |||
| 925 | if (!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) { | ||
| 926 | zig_panic("stderr SetHandleInformation"); | ||
| 927 | } | ||
| 928 | |||
| 929 | if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) { | ||
| 930 | zig_panic("Stdin CreatePipe"); | ||
| 931 | } | ||
| 932 | |||
| 933 | if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) { | ||
| 934 | zig_panic("Stdin SetHandleInformation"); | ||
| 935 | } | ||
| 936 | |||
| 937 | |||
| 938 | PROCESS_INFORMATION piProcInfo = {0}; | ||
| 939 | STARTUPINFO siStartInfo = {0}; | ||
| 940 | siStartInfo.cb = sizeof(STARTUPINFO); | ||
| 941 | siStartInfo.hStdError = g_hChildStd_ERR_Wr; | ||
| 942 | siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; | ||
| 943 | siStartInfo.hStdInput = g_hChildStd_IN_Rd; | ||
| 944 | siStartInfo.dwFlags |= STARTF_USESTDHANDLES; | ||
| 945 | |||
| 946 | const char *exe = args.at(0); | ||
| 947 | BOOL success = CreateProcess(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr, | ||
| 948 | &siStartInfo, &piProcInfo); | ||
| 949 | |||
| 950 | if (!success) { | ||
| 951 | if (GetLastError() == ERROR_FILE_NOT_FOUND) { | ||
| 952 | CloseHandle(piProcInfo.hProcess); | ||
| 953 | CloseHandle(piProcInfo.hThread); | ||
| 954 | return ErrorFileNotFound; | ||
| 955 | } | ||
| 956 | zig_panic("CreateProcess failed. exe: %s command_line: %s", exe, buf_ptr(&command_line)); | ||
| 957 | } | ||
| 958 | |||
| 959 | if (!CloseHandle(g_hChildStd_IN_Wr)) { | ||
| 960 | zig_panic("stdinwr closehandle"); | ||
| 961 | } | ||
| 962 | |||
| 963 | CloseHandle(g_hChildStd_IN_Rd); | ||
| 964 | CloseHandle(g_hChildStd_ERR_Wr); | ||
| 965 | CloseHandle(g_hChildStd_OUT_Wr); | ||
| 966 | |||
| 967 | static const size_t BUF_SIZE = 4 * 1024; | ||
| 968 | { | ||
| 969 | DWORD dwRead; | ||
| 970 | char chBuf[BUF_SIZE]; | ||
| 971 | |||
| 972 | buf_resize(out_stdout, 0); | ||
| 973 | for (;;) { | ||
| 974 | success = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUF_SIZE, &dwRead, NULL); | ||
| 975 | if (!success || dwRead == 0) break; | ||
| 976 | |||
| 977 | buf_append_mem(out_stdout, chBuf, dwRead); | ||
| 978 | } | ||
| 979 | CloseHandle(g_hChildStd_OUT_Rd); | ||
| 980 | } | ||
| 981 | { | ||
| 982 | DWORD dwRead; | ||
| 983 | char chBuf[BUF_SIZE]; | ||
| 984 | |||
| 985 | buf_resize(out_stderr, 0); | ||
| 986 | for (;;) { | ||
| 987 | success = ReadFile( g_hChildStd_ERR_Rd, chBuf, BUF_SIZE, &dwRead, NULL); | ||
| 988 | if (!success || dwRead == 0) break; | ||
| 989 | |||
| 990 | buf_append_mem(out_stderr, chBuf, dwRead); | ||
| 991 | } | ||
| 992 | CloseHandle(g_hChildStd_ERR_Rd); | ||
| 993 | } | ||
| 994 | |||
| 995 | WaitForSingleObject(piProcInfo.hProcess, INFINITE); | ||
| 996 | |||
| 997 | DWORD exit_code; | ||
| 998 | if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) { | ||
| 999 | zig_panic("GetExitCodeProcess failed"); | ||
| 1000 | } | ||
| 1001 | term->how = TerminationIdClean; | ||
| 1002 | term->code = exit_code; | ||
| 1003 | |||
| 1004 | CloseHandle(piProcInfo.hProcess); | ||
| 1005 | CloseHandle(piProcInfo.hThread); | ||
| 1006 | |||
| 1007 | return ErrorNone; | ||
| 1008 | } | ||
| 1009 | #endif | ||
| 1010 | |||
| 1011 | Error os_execv(const char *exe, const char **argv) { | ||
| 1012 | #if defined(ZIG_OS_WINDOWS) | ||
| 1013 | return ErrorUnsupportedOperatingSystem; | ||
| 1014 | #else | ||
| 1015 | execv(exe, (char *const *)argv); | ||
| 1016 | switch (errno) { | ||
| 1017 | case ENOMEM: | ||
| 1018 | return ErrorSystemResources; | ||
| 1019 | case EIO: | ||
| 1020 | return ErrorFileSystem; | ||
| 1021 | default: | ||
| 1022 | return ErrorUnexpected; | ||
| 1023 | } | ||
| 1024 | #endif | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | Error os_exec_process(ZigList<const char *> &args, | ||
| 1028 | Termination *term, Buf *out_stderr, Buf *out_stdout) | ||
| 1029 | { | ||
| 1030 | #if defined(ZIG_OS_WINDOWS) | ||
| 1031 | return os_exec_process_windows(args, term, out_stderr, out_stdout); | ||
| 1032 | #elif defined(ZIG_OS_POSIX) | ||
| 1033 | return os_exec_process_posix(args, term, out_stderr, out_stdout); | ||
| 1034 | #else | ||
| 1035 | #error "missing os_exec_process implementation" | ||
| 1036 | #endif | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | Error os_write_file(Buf *full_path, Buf *contents) { | 647 | Error os_write_file(Buf *full_path, Buf *contents) { |
| 1040 | #if defined(ZIG_OS_WINDOWS) | 648 | #if defined(ZIG_OS_WINDOWS) |
| 1041 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); | 649 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); |
| ... | @@ -1074,35 +682,6 @@ static Error copy_open_files(FILE *src_f, FILE *dest_f) { | ... | @@ -1074,35 +682,6 @@ static Error copy_open_files(FILE *src_f, FILE *dest_f) { |
| 1074 | } | 682 | } |
| 1075 | } | 683 | } |
| 1076 | 684 | ||
| 1077 | Error os_dump_file(Buf *src_path, FILE *dest_file) { | ||
| 1078 | Error err; | ||
| 1079 | |||
| 1080 | #if defined(ZIG_OS_WINDOWS) | ||
| 1081 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); | ||
| 1082 | FILE *src_f = _wfopen(&path_space.data.items[0], L"rb"); | ||
| 1083 | #else | ||
| 1084 | FILE *src_f = fopen(buf_ptr(src_path), "rb"); | ||
| 1085 | #endif | ||
| 1086 | if (!src_f) { | ||
| 1087 | int err = errno; | ||
| 1088 | if (err == ENOENT) { | ||
| 1089 | return ErrorFileNotFound; | ||
| 1090 | } else if (err == EACCES || err == EPERM) { | ||
| 1091 | return ErrorAccess; | ||
| 1092 | } else { | ||
| 1093 | return ErrorFileSystem; | ||
| 1094 | } | ||
| 1095 | } | ||
| 1096 | copy_open_files(src_f, dest_file); | ||
| 1097 | if ((err = copy_open_files(src_f, dest_file))) { | ||
| 1098 | fclose(src_f); | ||
| 1099 | return err; | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | fclose(src_f); | ||
| 1103 | return ErrorNone; | ||
| 1104 | } | ||
| 1105 | |||
| 1106 | #if defined(ZIG_OS_WINDOWS) | 685 | #if defined(ZIG_OS_WINDOWS) |
| 1107 | static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) { | 686 | static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) { |
| 1108 | mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime; | 687 | mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime; |
| ... | @@ -1116,88 +695,6 @@ static FILETIME windows_os_timestamp_to_filetime(OsTimeStamp mtime) { | ... | @@ -1116,88 +695,6 @@ static FILETIME windows_os_timestamp_to_filetime(OsTimeStamp mtime) { |
| 1116 | } | 695 | } |
| 1117 | #endif | 696 | #endif |
| 1118 | 697 | ||
| 1119 | static Error set_file_times(OsFile file, OsTimeStamp ts) { | ||
| 1120 | #if defined(ZIG_OS_WINDOWS) | ||
| 1121 | FILETIME ft = windows_os_timestamp_to_filetime(ts); | ||
| 1122 | if (SetFileTime(file, nullptr, &ft, &ft) == 0) { | ||
| 1123 | return ErrorUnexpected; | ||
| 1124 | } | ||
| 1125 | return ErrorNone; | ||
| 1126 | #else | ||
| 1127 | struct timespec times[2] = { | ||
| 1128 | { (time_t)ts.sec, (long)ts.nsec }, | ||
| 1129 | { (time_t)ts.sec, (long)ts.nsec }, | ||
| 1130 | }; | ||
| 1131 | if (futimens(file, times) == -1) { | ||
| 1132 | switch (errno) { | ||
| 1133 | case EBADF: | ||
| 1134 | zig_panic("futimens EBADF"); | ||
| 1135 | default: | ||
| 1136 | return ErrorUnexpected; | ||
| 1137 | } | ||
| 1138 | } | ||
| 1139 | return ErrorNone; | ||
| 1140 | #endif | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | Error os_update_file(Buf *src_path, Buf *dst_path) { | ||
| 1144 | Error err; | ||
| 1145 | |||
| 1146 | OsFile src_file; | ||
| 1147 | OsFileAttr src_attr; | ||
| 1148 | if ((err = os_file_open_r(src_path, &src_file, &src_attr))) { | ||
| 1149 | return err; | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | OsFile dst_file; | ||
| 1153 | OsFileAttr dst_attr; | ||
| 1154 | if ((err = os_file_open_w(dst_path, &dst_file, &dst_attr, src_attr.mode))) { | ||
| 1155 | os_file_close(&src_file); | ||
| 1156 | return err; | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | if (src_attr.size == dst_attr.size && | ||
| 1160 | src_attr.mode == dst_attr.mode && | ||
| 1161 | src_attr.mtime.sec == dst_attr.mtime.sec && | ||
| 1162 | src_attr.mtime.nsec == dst_attr.mtime.nsec) | ||
| 1163 | { | ||
| 1164 | os_file_close(&src_file); | ||
| 1165 | os_file_close(&dst_file); | ||
| 1166 | return ErrorNone; | ||
| 1167 | } | ||
| 1168 | #if defined(ZIG_OS_WINDOWS) | ||
| 1169 | if (SetEndOfFile(dst_file) == 0) { | ||
| 1170 | return ErrorUnexpected; | ||
| 1171 | } | ||
| 1172 | #else | ||
| 1173 | if (ftruncate(dst_file, 0) == -1) { | ||
| 1174 | return ErrorUnexpected; | ||
| 1175 | } | ||
| 1176 | #endif | ||
| 1177 | #if defined(ZIG_OS_WINDOWS) | ||
| 1178 | FILE *src_libc_file = _fdopen(_open_osfhandle((intptr_t)src_file, _O_RDONLY), "rb"); | ||
| 1179 | FILE *dst_libc_file = _fdopen(_open_osfhandle((intptr_t)dst_file, 0), "wb"); | ||
| 1180 | #else | ||
| 1181 | FILE *src_libc_file = fdopen(src_file, "rb"); | ||
| 1182 | FILE *dst_libc_file = fdopen(dst_file, "wb"); | ||
| 1183 | #endif | ||
| 1184 | assert(src_libc_file); | ||
| 1185 | assert(dst_libc_file); | ||
| 1186 | |||
| 1187 | if ((err = copy_open_files(src_libc_file, dst_libc_file))) { | ||
| 1188 | fclose(src_libc_file); | ||
| 1189 | fclose(dst_libc_file); | ||
| 1190 | return err; | ||
| 1191 | } | ||
| 1192 | if (fflush(dst_libc_file) == -1) { | ||
| 1193 | return ErrorUnexpected; | ||
| 1194 | } | ||
| 1195 | err = set_file_times(dst_file, src_attr.mtime); | ||
| 1196 | fclose(src_libc_file); | ||
| 1197 | fclose(dst_libc_file); | ||
| 1198 | return err; | ||
| 1199 | } | ||
| 1200 | |||
| 1201 | Error os_copy_file(Buf *src_path, Buf *dest_path) { | 698 | Error os_copy_file(Buf *src_path, Buf *dest_path) { |
| 1202 | #if defined(ZIG_OS_WINDOWS) | 699 | #if defined(ZIG_OS_WINDOWS) |
| 1203 | PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); | 700 | PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); |
| ... | @@ -1358,14 +855,6 @@ bool os_stderr_tty(void) { | ... | @@ -1358,14 +855,6 @@ bool os_stderr_tty(void) { |
| 1358 | #endif | 855 | #endif |
| 1359 | } | 856 | } |
| 1360 | 857 | ||
| 1361 | Error os_delete_file(Buf *path) { | ||
| 1362 | if (remove(buf_ptr(path))) { | ||
| 1363 | return ErrorFileSystem; | ||
| 1364 | } else { | ||
| 1365 | return ErrorNone; | ||
| 1366 | } | ||
| 1367 | } | ||
| 1368 | |||
| 1369 | Error os_rename(Buf *src_path, Buf *dest_path) { | 858 | Error os_rename(Buf *src_path, Buf *dest_path) { |
| 1370 | if (buf_eql_buf(src_path, dest_path)) { | 859 | if (buf_eql_buf(src_path, dest_path)) { |
| 1371 | return ErrorNone; | 860 | return ErrorNone; |
| ... | @@ -1384,30 +873,6 @@ Error os_rename(Buf *src_path, Buf *dest_path) { | ... | @@ -1384,30 +873,6 @@ Error os_rename(Buf *src_path, Buf *dest_path) { |
| 1384 | return ErrorNone; | 873 | return ErrorNone; |
| 1385 | } | 874 | } |
| 1386 | 875 | ||
| 1387 | OsTimeStamp os_timestamp_calendar(void) { | ||
| 1388 | OsTimeStamp result; | ||
| 1389 | #if defined(ZIG_OS_WINDOWS) | ||
| 1390 | FILETIME ft; | ||
| 1391 | GetSystemTimeAsFileTime(&ft); | ||
| 1392 | windows_filetime_to_os_timestamp(&ft, &result); | ||
| 1393 | #elif defined(__MACH__) | ||
| 1394 | mach_timespec_t mts; | ||
| 1395 | |||
| 1396 | kern_return_t err = clock_get_time(macos_calendar_clock, &mts); | ||
| 1397 | assert(!err); | ||
| 1398 | |||
| 1399 | result.sec = mts.tv_sec; | ||
| 1400 | result.nsec = mts.tv_nsec; | ||
| 1401 | #else | ||
| 1402 | struct timespec tms; | ||
| 1403 | clock_gettime(CLOCK_REALTIME, &tms); | ||
| 1404 | |||
| 1405 | result.sec = tms.tv_sec; | ||
| 1406 | result.nsec = tms.tv_nsec; | ||
| 1407 | #endif | ||
| 1408 | return result; | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | OsTimeStamp os_timestamp_monotonic(void) { | 876 | OsTimeStamp os_timestamp_monotonic(void) { |
| 1412 | OsTimeStamp result; | 877 | OsTimeStamp result; |
| 1413 | #if defined(ZIG_OS_WINDOWS) | 878 | #if defined(ZIG_OS_WINDOWS) |
| ... | @@ -1501,49 +966,8 @@ Error os_make_dir(Buf *path) { | ... | @@ -1501,49 +966,8 @@ Error os_make_dir(Buf *path) { |
| 1501 | #endif | 966 | #endif |
| 1502 | } | 967 | } |
| 1503 | 968 | ||
| 1504 | static void init_rand() { | ||
| 1505 | #if defined(ZIG_OS_WINDOWS) | ||
| 1506 | char bytes[sizeof(unsigned)]; | ||
| 1507 | unsigned seed; | ||
| 1508 | RtlGenRandom(bytes, sizeof(unsigned)); | ||
| 1509 | memcpy(&seed, bytes, sizeof(unsigned)); | ||
| 1510 | srand(seed); | ||
| 1511 | #elif defined(ZIG_OS_LINUX) | ||
| 1512 | unsigned char *ptr_random = (unsigned char*)getauxval(AT_RANDOM); | ||
| 1513 | unsigned seed; | ||
| 1514 | memcpy(&seed, ptr_random, sizeof(seed)); | ||
| 1515 | srand(seed); | ||
| 1516 | #elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) | ||
| 1517 | unsigned seed; | ||
| 1518 | size_t len = sizeof(seed); | ||
| 1519 | int mib[2] = { CTL_KERN, KERN_ARND }; | ||
| 1520 | if (sysctl(mib, 2, &seed, &len, NULL, 0) != 0) { | ||
| 1521 | zig_panic("unable to query random data from sysctl"); | ||
| 1522 | } | ||
| 1523 | srand(seed); | ||
| 1524 | #else | ||
| 1525 | int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC); | ||
| 1526 | if (fd == -1) { | ||
| 1527 | zig_panic("unable to open /dev/urandom"); | ||
| 1528 | } | ||
| 1529 | char bytes[sizeof(unsigned)]; | ||
| 1530 | ssize_t amt_read; | ||
| 1531 | while ((amt_read = read(fd, bytes, sizeof(unsigned))) == -1) { | ||
| 1532 | if (errno == EINTR) continue; | ||
| 1533 | zig_panic("unable to read /dev/urandom"); | ||
| 1534 | } | ||
| 1535 | if (amt_read != sizeof(unsigned)) { | ||
| 1536 | zig_panic("unable to read enough bytes from /dev/urandom"); | ||
| 1537 | } | ||
| 1538 | close(fd); | ||
| 1539 | unsigned seed; | ||
| 1540 | memcpy(&seed, bytes, sizeof(unsigned)); | ||
| 1541 | srand(seed); | ||
| 1542 | #endif | ||
| 1543 | } | ||
| 1544 | 969 | ||
| 1545 | int os_init(void) { | 970 | int os_init(void) { |
| 1546 | init_rand(); | ||
| 1547 | #if defined(ZIG_OS_WINDOWS) | 971 | #if defined(ZIG_OS_WINDOWS) |
| 1548 | _setmode(fileno(stdout), _O_BINARY); | 972 | _setmode(fileno(stdout), _O_BINARY); |
| 1549 | _setmode(fileno(stderr), _O_BINARY); | 973 | _setmode(fileno(stderr), _O_BINARY); |
| ... | @@ -1580,71 +1004,6 @@ int os_init(void) { | ... | @@ -1580,71 +1004,6 @@ int os_init(void) { |
| 1580 | return 0; | 1004 | return 0; |
| 1581 | } | 1005 | } |
| 1582 | 1006 | ||
| 1583 | Error os_self_exe_path(Buf *out_path) { | ||
| 1584 | #if defined(ZIG_OS_WINDOWS) | ||
| 1585 | PathSpace path_space; | ||
| 1586 | DWORD copied_amt = GetModuleFileNameW(nullptr, &path_space.data.items[0], PATH_MAX_WIDE); | ||
| 1587 | if (copied_amt <= 0) { | ||
| 1588 | return ErrorFileNotFound; | ||
| 1589 | } | ||
| 1590 | utf16le_ptr_to_utf8(out_path, &path_space.data.items[0]); | ||
| 1591 | return ErrorNone; | ||
| 1592 | |||
| 1593 | #elif defined(ZIG_OS_DARWIN) | ||
| 1594 | // How long is the executable's path? | ||
| 1595 | uint32_t u32_len = 0; | ||
| 1596 | int ret1 = _NSGetExecutablePath(nullptr, &u32_len); | ||
| 1597 | assert(ret1 != 0); | ||
| 1598 | |||
| 1599 | Buf *tmp = buf_alloc_fixed(u32_len); | ||
| 1600 | |||
| 1601 | // Fill the executable path. | ||
| 1602 | int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len); | ||
| 1603 | assert(ret2 == 0); | ||
| 1604 | |||
| 1605 | // According to libuv project, PATH_MAX*2 works around a libc bug where | ||
| 1606 | // the resolved path is sometimes bigger than PATH_MAX. | ||
| 1607 | buf_resize(out_path, PATH_MAX*2); | ||
| 1608 | char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); | ||
| 1609 | if (!real_path) { | ||
| 1610 | buf_init_from_buf(out_path, tmp); | ||
| 1611 | return ErrorNone; | ||
| 1612 | } | ||
| 1613 | |||
| 1614 | // Resize out_path for the correct length. | ||
| 1615 | buf_resize(out_path, strlen(buf_ptr(out_path))); | ||
| 1616 | |||
| 1617 | return ErrorNone; | ||
| 1618 | #elif defined(ZIG_OS_LINUX) | ||
| 1619 | buf_resize(out_path, PATH_MAX); | ||
| 1620 | ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path)); | ||
| 1621 | if (amt == -1) { | ||
| 1622 | return ErrorUnexpected; | ||
| 1623 | } | ||
| 1624 | buf_resize(out_path, amt); | ||
| 1625 | return ErrorNone; | ||
| 1626 | #elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_DRAGONFLY) | ||
| 1627 | buf_resize(out_path, PATH_MAX); | ||
| 1628 | int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; | ||
| 1629 | size_t cb = PATH_MAX; | ||
| 1630 | if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) { | ||
| 1631 | return ErrorUnexpected; | ||
| 1632 | } | ||
| 1633 | buf_resize(out_path, cb - 1); | ||
| 1634 | return ErrorNone; | ||
| 1635 | #elif defined(ZIG_OS_NETBSD) | ||
| 1636 | buf_resize(out_path, PATH_MAX); | ||
| 1637 | int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; | ||
| 1638 | size_t cb = PATH_MAX; | ||
| 1639 | if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) { | ||
| 1640 | return ErrorUnexpected; | ||
| 1641 | } | ||
| 1642 | buf_resize(out_path, cb - 1); | ||
| 1643 | return ErrorNone; | ||
| 1644 | #endif | ||
| 1645 | return ErrorFileNotFound; | ||
| 1646 | } | ||
| 1647 | |||
| 1648 | #define VT_RED "\x1b[31;1m" | 1007 | #define VT_RED "\x1b[31;1m" |
| 1649 | #define VT_GREEN "\x1b[32;1m" | 1008 | #define VT_GREEN "\x1b[32;1m" |
| 1650 | #define VT_CYAN "\x1b[36;1m" | 1009 | #define VT_CYAN "\x1b[36;1m" |
| ... | @@ -1954,392 +1313,3 @@ PathSpace slice_to_prefixed_file_w(Slice<uint8_t> path) { | ... | @@ -1954,392 +1313,3 @@ PathSpace slice_to_prefixed_file_w(Slice<uint8_t> path) { |
| 1954 | return path_space; | 1313 | return path_space; |
| 1955 | } | 1314 | } |
| 1956 | #endif | 1315 | #endif |
| 1957 | |||
| 1958 | // Ported from std.os.getAppDataDir | ||
| 1959 | Error os_get_app_data_dir(Buf *out_path, const char *appname) { | ||
| 1960 | #if defined(ZIG_OS_WINDOWS) | ||
| 1961 | WCHAR *dir_path_ptr; | ||
| 1962 | switch (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &dir_path_ptr)) { | ||
| 1963 | case S_OK: | ||
| 1964 | // defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); | ||
| 1965 | utf16le_ptr_to_utf8(out_path, dir_path_ptr); | ||
| 1966 | CoTaskMemFree(dir_path_ptr); | ||
| 1967 | buf_appendf(out_path, "\\%s", appname); | ||
| 1968 | return ErrorNone; | ||
| 1969 | case E_OUTOFMEMORY: | ||
| 1970 | return ErrorNoMem; | ||
| 1971 | default: | ||
| 1972 | return ErrorUnexpected; | ||
| 1973 | } | ||
| 1974 | zig_unreachable(); | ||
| 1975 | #elif defined(ZIG_OS_DARWIN) | ||
| 1976 | const char *home_dir = getenv("HOME"); | ||
| 1977 | if (home_dir == nullptr) { | ||
| 1978 | // TODO use /etc/passwd | ||
| 1979 | return ErrorFileNotFound; | ||
| 1980 | } | ||
| 1981 | buf_resize(out_path, 0); | ||
| 1982 | buf_appendf(out_path, "%s/Library/Application Support/%s", home_dir, appname); | ||
| 1983 | return ErrorNone; | ||
| 1984 | #elif defined(ZIG_OS_POSIX) | ||
| 1985 | const char *cache_dir = getenv("XDG_CACHE_HOME"); | ||
| 1986 | if (cache_dir == nullptr) { | ||
| 1987 | cache_dir = getenv("HOME"); | ||
| 1988 | if (cache_dir == nullptr) { | ||
| 1989 | // TODO use /etc/passwd | ||
| 1990 | return ErrorFileNotFound; | ||
| 1991 | } | ||
| 1992 | if (cache_dir[0] == 0) { | ||
| 1993 | return ErrorFileNotFound; | ||
| 1994 | } | ||
| 1995 | buf_init_from_str(out_path, cache_dir); | ||
| 1996 | if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { | ||
| 1997 | buf_append_char(out_path, '/'); | ||
| 1998 | } | ||
| 1999 | buf_appendf(out_path, ".cache/%s", appname); | ||
| 2000 | } else { | ||
| 2001 | if (cache_dir[0] == 0) { | ||
| 2002 | return ErrorFileNotFound; | ||
| 2003 | } | ||
| 2004 | buf_init_from_str(out_path, cache_dir); | ||
| 2005 | if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { | ||
| 2006 | buf_append_char(out_path, '/'); | ||
| 2007 | } | ||
| 2008 | buf_appendf(out_path, "%s", appname); | ||
| 2009 | } | ||
| 2010 | return ErrorNone; | ||
| 2011 | #endif | ||
| 2012 | } | ||
| 2013 | |||
| 2014 | #if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) | ||
| 2015 | static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, void *data) { | ||
| 2016 | ZigList<Buf *> *libs = reinterpret_cast< ZigList<Buf *> *>(data); | ||
| 2017 | if (info->dlpi_name[0] == '/') { | ||
| 2018 | libs->append(buf_create_from_str(info->dlpi_name)); | ||
| 2019 | } | ||
| 2020 | return 0; | ||
| 2021 | } | ||
| 2022 | #endif | ||
| 2023 | |||
| 2024 | Error os_self_exe_shared_libs(ZigList<Buf *> &paths) { | ||
| 2025 | #if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) | ||
| 2026 | paths.resize(0); | ||
| 2027 | dl_iterate_phdr(self_exe_shared_libs_callback, &paths); | ||
| 2028 | return ErrorNone; | ||
| 2029 | #elif defined(ZIG_OS_DARWIN) | ||
| 2030 | paths.resize(0); | ||
| 2031 | uint32_t img_count = _dyld_image_count(); | ||
| 2032 | for (uint32_t i = 0; i != img_count; i += 1) { | ||
| 2033 | const char *name = _dyld_get_image_name(i); | ||
| 2034 | paths.append(buf_create_from_str(name)); | ||
| 2035 | } | ||
| 2036 | return ErrorNone; | ||
| 2037 | #elif defined(ZIG_OS_WINDOWS) | ||
| 2038 | // zig is built statically on windows, so we can return an empty list | ||
| 2039 | paths.resize(0); | ||
| 2040 | return ErrorNone; | ||
| 2041 | #else | ||
| 2042 | #error unimplemented | ||
| 2043 | #endif | ||
| 2044 | } | ||
| 2045 | |||
| 2046 | Error os_file_open_rw(Buf *full_path, OsFile *out_file, OsFileAttr *attr, bool need_write, uint32_t mode) { | ||
| 2047 | #if defined(ZIG_OS_WINDOWS) | ||
| 2048 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); | ||
| 2049 | HANDLE result = CreateFileW(&path_space.data.items[0], | ||
| 2050 | need_write ? (GENERIC_READ|GENERIC_WRITE) : GENERIC_READ, | ||
| 2051 | need_write ? 0 : FILE_SHARE_READ, | ||
| 2052 | nullptr, | ||
| 2053 | need_write ? OPEN_ALWAYS : OPEN_EXISTING, | ||
| 2054 | FILE_ATTRIBUTE_NORMAL, nullptr); | ||
| 2055 | |||
| 2056 | if (result == INVALID_HANDLE_VALUE) { | ||
| 2057 | DWORD err = GetLastError(); | ||
| 2058 | switch (err) { | ||
| 2059 | case ERROR_SHARING_VIOLATION: | ||
| 2060 | return ErrorSharingViolation; | ||
| 2061 | case ERROR_ALREADY_EXISTS: | ||
| 2062 | return ErrorPathAlreadyExists; | ||
| 2063 | case ERROR_FILE_EXISTS: | ||
| 2064 | return ErrorPathAlreadyExists; | ||
| 2065 | case ERROR_FILE_NOT_FOUND: | ||
| 2066 | return ErrorFileNotFound; | ||
| 2067 | case ERROR_PATH_NOT_FOUND: | ||
| 2068 | return ErrorFileNotFound; | ||
| 2069 | case ERROR_ACCESS_DENIED: | ||
| 2070 | return ErrorAccess; | ||
| 2071 | case ERROR_PIPE_BUSY: | ||
| 2072 | return ErrorPipeBusy; | ||
| 2073 | default: | ||
| 2074 | return ErrorUnexpected; | ||
| 2075 | } | ||
| 2076 | } | ||
| 2077 | *out_file = result; | ||
| 2078 | |||
| 2079 | if (attr != nullptr) { | ||
| 2080 | BY_HANDLE_FILE_INFORMATION file_info; | ||
| 2081 | if (!GetFileInformationByHandle(result, &file_info)) { | ||
| 2082 | CloseHandle(result); | ||
| 2083 | return ErrorUnexpected; | ||
| 2084 | } | ||
| 2085 | windows_filetime_to_os_timestamp(&file_info.ftLastWriteTime, &attr->mtime); | ||
| 2086 | attr->inode = (((uint64_t)file_info.nFileIndexHigh) << 32) | file_info.nFileIndexLow; | ||
| 2087 | attr->mode = 0; | ||
| 2088 | attr->size = (((uint64_t)file_info.nFileSizeHigh) << 32) | file_info.nFileSizeLow; | ||
| 2089 | } | ||
| 2090 | |||
| 2091 | return ErrorNone; | ||
| 2092 | #else | ||
| 2093 | for (;;) { | ||
| 2094 | int fd = open(buf_ptr(full_path), | ||
| 2095 | need_write ? (O_RDWR|O_CLOEXEC|O_CREAT) : (O_RDONLY|O_CLOEXEC), mode); | ||
| 2096 | if (fd == -1) { | ||
| 2097 | switch (errno) { | ||
| 2098 | case EINTR: | ||
| 2099 | continue; | ||
| 2100 | case EINVAL: | ||
| 2101 | zig_unreachable(); | ||
| 2102 | case EFAULT: | ||
| 2103 | zig_unreachable(); | ||
| 2104 | case EACCES: | ||
| 2105 | case EPERM: | ||
| 2106 | return ErrorAccess; | ||
| 2107 | case EISDIR: | ||
| 2108 | return ErrorIsDir; | ||
| 2109 | case ENOENT: | ||
| 2110 | return ErrorFileNotFound; | ||
| 2111 | default: | ||
| 2112 | return ErrorFileSystem; | ||
| 2113 | } | ||
| 2114 | } | ||
| 2115 | struct stat statbuf; | ||
| 2116 | if (fstat(fd, &statbuf) == -1) { | ||
| 2117 | close(fd); | ||
| 2118 | return ErrorFileSystem; | ||
| 2119 | } | ||
| 2120 | if (S_ISDIR(statbuf.st_mode)) { | ||
| 2121 | close(fd); | ||
| 2122 | return ErrorIsDir; | ||
| 2123 | } | ||
| 2124 | *out_file = fd; | ||
| 2125 | |||
| 2126 | if (attr != nullptr) { | ||
| 2127 | attr->inode = statbuf.st_ino; | ||
| 2128 | #if defined(ZIG_OS_DARWIN) | ||
| 2129 | attr->mtime.sec = statbuf.st_mtimespec.tv_sec; | ||
| 2130 | attr->mtime.nsec = statbuf.st_mtimespec.tv_nsec; | ||
| 2131 | #else | ||
| 2132 | attr->mtime.sec = statbuf.st_mtim.tv_sec; | ||
| 2133 | attr->mtime.nsec = statbuf.st_mtim.tv_nsec; | ||
| 2134 | #endif | ||
| 2135 | attr->mode = statbuf.st_mode; | ||
| 2136 | attr->size = statbuf.st_size; | ||
| 2137 | } | ||
| 2138 | return ErrorNone; | ||
| 2139 | } | ||
| 2140 | #endif | ||
| 2141 | } | ||
| 2142 | |||
| 2143 | Error os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr) { | ||
| 2144 | return os_file_open_rw(full_path, out_file, attr, false, 0); | ||
| 2145 | } | ||
| 2146 | |||
| 2147 | Error os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode) { | ||
| 2148 | return os_file_open_rw(full_path, out_file, attr, true, mode); | ||
| 2149 | } | ||
| 2150 | |||
| 2151 | Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) { | ||
| 2152 | #if defined(ZIG_OS_WINDOWS) | ||
| 2153 | PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); | ||
| 2154 | for (;;) { | ||
| 2155 | HANDLE result = CreateFileW(&path_space.data.items[0], GENERIC_READ | GENERIC_WRITE, | ||
| 2156 | 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); | ||
| 2157 | |||
| 2158 | if (result == INVALID_HANDLE_VALUE) { | ||
| 2159 | DWORD err = GetLastError(); | ||
| 2160 | switch (err) { | ||
| 2161 | case ERROR_SHARING_VIOLATION: | ||
| 2162 | // TODO wait for the lock instead of sleeping | ||
| 2163 | Sleep(10); | ||
| 2164 | continue; | ||
| 2165 | case ERROR_ALREADY_EXISTS: | ||
| 2166 | return ErrorPathAlreadyExists; | ||
| 2167 | case ERROR_FILE_EXISTS: | ||
| 2168 | return ErrorPathAlreadyExists; | ||
| 2169 | case ERROR_FILE_NOT_FOUND: | ||
| 2170 | return ErrorFileNotFound; | ||
| 2171 | case ERROR_PATH_NOT_FOUND: | ||
| 2172 | return ErrorFileNotFound; | ||
| 2173 | case ERROR_ACCESS_DENIED: | ||
| 2174 | return ErrorAccess; | ||
| 2175 | case ERROR_PIPE_BUSY: | ||
| 2176 | return ErrorPipeBusy; | ||
| 2177 | default: | ||
| 2178 | return ErrorUnexpected; | ||
| 2179 | } | ||
| 2180 | } | ||
| 2181 | *out_file = result; | ||
| 2182 | return ErrorNone; | ||
| 2183 | } | ||
| 2184 | #else | ||
| 2185 | int fd; | ||
| 2186 | for (;;) { | ||
| 2187 | fd = open(buf_ptr(full_path), O_RDWR|O_CLOEXEC|O_CREAT, 0666); | ||
| 2188 | if (fd == -1) { | ||
| 2189 | switch (errno) { | ||
| 2190 | case EINTR: | ||
| 2191 | continue; | ||
| 2192 | case EINVAL: | ||
| 2193 | zig_unreachable(); | ||
| 2194 | case EFAULT: | ||
| 2195 | zig_unreachable(); | ||
| 2196 | case EACCES: | ||
| 2197 | case EPERM: | ||
| 2198 | return ErrorAccess; | ||
| 2199 | case EISDIR: | ||
| 2200 | return ErrorIsDir; | ||
| 2201 | case ENOENT: | ||
| 2202 | return ErrorFileNotFound; | ||
| 2203 | case ENOTDIR: | ||
| 2204 | return ErrorNotDir; | ||
| 2205 | default: | ||
| 2206 | return ErrorFileSystem; | ||
| 2207 | } | ||
| 2208 | } | ||
| 2209 | break; | ||
| 2210 | } | ||
| 2211 | for (;;) { | ||
| 2212 | struct flock lock; | ||
| 2213 | lock.l_type = F_WRLCK; | ||
| 2214 | lock.l_whence = SEEK_SET; | ||
| 2215 | lock.l_start = 0; | ||
| 2216 | lock.l_len = 0; | ||
| 2217 | if (fcntl(fd, F_SETLKW, &lock) == -1) { | ||
| 2218 | switch (errno) { | ||
| 2219 | case EINTR: | ||
| 2220 | continue; | ||
| 2221 | case EBADF: | ||
| 2222 | zig_unreachable(); | ||
| 2223 | case EFAULT: | ||
| 2224 | zig_unreachable(); | ||
| 2225 | case EINVAL: | ||
| 2226 | zig_unreachable(); | ||
| 2227 | default: | ||
| 2228 | close(fd); | ||
| 2229 | return ErrorFileSystem; | ||
| 2230 | } | ||
| 2231 | } | ||
| 2232 | break; | ||
| 2233 | } | ||
| 2234 | *out_file = fd; | ||
| 2235 | return ErrorNone; | ||
| 2236 | #endif | ||
| 2237 | } | ||
| 2238 | |||
| 2239 | Error os_file_read(OsFile file, void *ptr, size_t *len) { | ||
| 2240 | #if defined(ZIG_OS_WINDOWS) | ||
| 2241 | DWORD amt_read; | ||
| 2242 | if (ReadFile(file, ptr, *len, &amt_read, nullptr) == 0) | ||
| 2243 | return ErrorUnexpected; | ||
| 2244 | *len = amt_read; | ||
| 2245 | return ErrorNone; | ||
| 2246 | #else | ||
| 2247 | for (;;) { | ||
| 2248 | ssize_t rc = read(file, ptr, *len); | ||
| 2249 | if (rc == -1) { | ||
| 2250 | switch (errno) { | ||
| 2251 | case EINTR: | ||
| 2252 | continue; | ||
| 2253 | case EBADF: | ||
| 2254 | zig_unreachable(); | ||
| 2255 | case EFAULT: | ||
| 2256 | zig_unreachable(); | ||
| 2257 | case EISDIR: | ||
| 2258 | return ErrorIsDir; | ||
| 2259 | default: | ||
| 2260 | return ErrorFileSystem; | ||
| 2261 | } | ||
| 2262 | } | ||
| 2263 | *len = rc; | ||
| 2264 | return ErrorNone; | ||
| 2265 | } | ||
| 2266 | #endif | ||
| 2267 | } | ||
| 2268 | |||
| 2269 | Error os_file_read_all(OsFile file, Buf *contents) { | ||
| 2270 | Error err; | ||
| 2271 | size_t index = 0; | ||
| 2272 | for (;;) { | ||
| 2273 | size_t amt = buf_len(contents) - index; | ||
| 2274 | |||
| 2275 | if (amt < 4096) { | ||
| 2276 | buf_resize(contents, buf_len(contents) + (4096 - amt)); | ||
| 2277 | amt = buf_len(contents) - index; | ||
| 2278 | } | ||
| 2279 | |||
| 2280 | if ((err = os_file_read(file, buf_ptr(contents) + index, &amt))) | ||
| 2281 | return err; | ||
| 2282 | |||
| 2283 | if (amt == 0) { | ||
| 2284 | buf_resize(contents, index); | ||
| 2285 | return ErrorNone; | ||
| 2286 | } | ||
| 2287 | |||
| 2288 | index += amt; | ||
| 2289 | } | ||
| 2290 | } | ||
| 2291 | |||
| 2292 | Error os_file_overwrite(OsFile file, Buf *contents) { | ||
| 2293 | #if defined(ZIG_OS_WINDOWS) | ||
| 2294 | if (SetFilePointer(file, 0, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) | ||
| 2295 | return ErrorFileSystem; | ||
| 2296 | if (!SetEndOfFile(file)) | ||
| 2297 | return ErrorFileSystem; | ||
| 2298 | DWORD bytes_written; | ||
| 2299 | if (!WriteFile(file, buf_ptr(contents), buf_len(contents), &bytes_written, nullptr)) | ||
| 2300 | return ErrorFileSystem; | ||
| 2301 | return ErrorNone; | ||
| 2302 | #else | ||
| 2303 | if (lseek(file, 0, SEEK_SET) == -1) | ||
| 2304 | return ErrorUnexpectedSeekFailure; | ||
| 2305 | if (ftruncate(file, 0) == -1) | ||
| 2306 | return ErrorUnexpectedFileTruncationFailure; | ||
| 2307 | for (;;) { | ||
| 2308 | if (write(file, buf_ptr(contents), buf_len(contents)) == -1) { | ||
| 2309 | switch (errno) { | ||
| 2310 | case EINTR: | ||
| 2311 | continue; | ||
| 2312 | case EINVAL: | ||
| 2313 | zig_unreachable(); | ||
| 2314 | case EBADF: | ||
| 2315 | zig_unreachable(); | ||
| 2316 | case EFAULT: | ||
| 2317 | zig_unreachable(); | ||
| 2318 | case EDQUOT: | ||
| 2319 | return ErrorDiskQuota; | ||
| 2320 | case ENOSPC: | ||
| 2321 | return ErrorDiskSpace; | ||
| 2322 | case EFBIG: | ||
| 2323 | return ErrorFileTooBig; | ||
| 2324 | case EIO: | ||
| 2325 | return ErrorFileSystem; | ||
| 2326 | case EPERM: | ||
| 2327 | return ErrorAccess; | ||
| 2328 | default: | ||
| 2329 | return ErrorUnexpectedWriteFailure; | ||
| 2330 | } | ||
| 2331 | } | ||
| 2332 | return ErrorNone; | ||
| 2333 | } | ||
| 2334 | #endif | ||
| 2335 | } | ||
| 2336 | |||
| 2337 | void os_file_close(OsFile *file) { | ||
| 2338 | #if defined(ZIG_OS_WINDOWS) | ||
| 2339 | CloseHandle(*file); | ||
| 2340 | *file = NULL; | ||
| 2341 | #else | ||
| 2342 | close(*file); | ||
| 2343 | *file = -1; | ||
| 2344 | #endif | ||
| 2345 | } |
src/stage1/os.hpp-52| ... | @@ -70,66 +70,25 @@ enum TermColor { | ... | @@ -70,66 +70,25 @@ enum TermColor { |
| 70 | TermColorReset, | 70 | TermColorReset, |
| 71 | }; | 71 | }; |
| 72 | 72 | ||
| 73 | enum TerminationId { | ||
| 74 | TerminationIdClean, | ||
| 75 | TerminationIdSignaled, | ||
| 76 | TerminationIdStopped, | ||
| 77 | TerminationIdUnknown, | ||
| 78 | }; | ||
| 79 | |||
| 80 | struct Termination { | ||
| 81 | TerminationId how; | ||
| 82 | int code; | ||
| 83 | }; | ||
| 84 | |||
| 85 | #if defined(ZIG_OS_WINDOWS) | ||
| 86 | #define OsFile void * | ||
| 87 | #else | ||
| 88 | #define OsFile int | ||
| 89 | #endif | ||
| 90 | |||
| 91 | struct OsTimeStamp { | 73 | struct OsTimeStamp { |
| 92 | int64_t sec; | 74 | int64_t sec; |
| 93 | int64_t nsec; | 75 | int64_t nsec; |
| 94 | }; | 76 | }; |
| 95 | 77 | ||
| 96 | struct OsFileAttr { | ||
| 97 | OsTimeStamp mtime; | ||
| 98 | uint64_t size; | ||
| 99 | uint64_t inode; | ||
| 100 | uint32_t mode; | ||
| 101 | }; | ||
| 102 | |||
| 103 | int os_init(void); | 78 | int os_init(void); |
| 104 | 79 | ||
| 105 | void os_spawn_process(ZigList<const char *> &args, Termination *term); | ||
| 106 | Error os_exec_process(ZigList<const char *> &args, | ||
| 107 | Termination *term, Buf *out_stderr, Buf *out_stdout); | ||
| 108 | Error os_execv(const char *exe, const char **argv); | ||
| 109 | |||
| 110 | void os_path_dirname(Buf *full_path, Buf *out_dirname); | 80 | void os_path_dirname(Buf *full_path, Buf *out_dirname); |
| 111 | void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename); | 81 | void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename); |
| 112 | void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname); | 82 | void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname); |
| 113 | void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path); | 83 | void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path); |
| 114 | Error os_path_real(Buf *rel_path, Buf *out_abs_path); | ||
| 115 | Buf os_path_resolve(Buf **paths_ptr, size_t paths_len); | 84 | Buf os_path_resolve(Buf **paths_ptr, size_t paths_len); |
| 116 | bool os_path_is_absolute(Buf *path); | 85 | bool os_path_is_absolute(Buf *path); |
| 117 | 86 | ||
| 118 | Error ATTRIBUTE_MUST_USE os_make_path(Buf *path); | 87 | Error ATTRIBUTE_MUST_USE os_make_path(Buf *path); |
| 119 | Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path); | 88 | Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path); |
| 120 | 89 | ||
| 121 | Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr); | ||
| 122 | Error ATTRIBUTE_MUST_USE os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode); | ||
| 123 | Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file); | ||
| 124 | Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len); | ||
| 125 | Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents); | ||
| 126 | Error ATTRIBUTE_MUST_USE os_file_overwrite(OsFile file, Buf *contents); | ||
| 127 | void os_file_close(OsFile *file); | ||
| 128 | |||
| 129 | Error ATTRIBUTE_MUST_USE os_write_file(Buf *full_path, Buf *contents); | 90 | Error ATTRIBUTE_MUST_USE os_write_file(Buf *full_path, Buf *contents); |
| 130 | Error ATTRIBUTE_MUST_USE os_copy_file(Buf *src_path, Buf *dest_path); | 91 | Error ATTRIBUTE_MUST_USE os_copy_file(Buf *src_path, Buf *dest_path); |
| 131 | Error ATTRIBUTE_MUST_USE os_update_file(Buf *src_path, Buf *dest_path); | ||
| 132 | Error ATTRIBUTE_MUST_USE os_dump_file(Buf *src_path, FILE *dest_file); | ||
| 133 | 92 | ||
| 134 | Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents); | 93 | Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents); |
| 135 | Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents); | 94 | Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents); |
| ... | @@ -139,22 +98,11 @@ Error ATTRIBUTE_MUST_USE os_get_cwd(Buf *out_cwd); | ... | @@ -139,22 +98,11 @@ Error ATTRIBUTE_MUST_USE os_get_cwd(Buf *out_cwd); |
| 139 | bool os_stderr_tty(void); | 98 | bool os_stderr_tty(void); |
| 140 | void os_stderr_set_color(TermColor color); | 99 | void os_stderr_set_color(TermColor color); |
| 141 | 100 | ||
| 142 | Error os_delete_file(Buf *path); | ||
| 143 | |||
| 144 | Error ATTRIBUTE_MUST_USE os_file_exists(Buf *full_path, bool *result); | ||
| 145 | |||
| 146 | Error os_rename(Buf *src_path, Buf *dest_path); | 101 | Error os_rename(Buf *src_path, Buf *dest_path); |
| 147 | OsTimeStamp os_timestamp_monotonic(void); | 102 | OsTimeStamp os_timestamp_monotonic(void); |
| 148 | OsTimeStamp os_timestamp_calendar(void); | ||
| 149 | 103 | ||
| 150 | bool os_is_sep(uint8_t c); | 104 | bool os_is_sep(uint8_t c); |
| 151 | 105 | ||
| 152 | Error ATTRIBUTE_MUST_USE os_self_exe_path(Buf *out_path); | ||
| 153 | |||
| 154 | Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname); | ||
| 155 | |||
| 156 | Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths); | ||
| 157 | |||
| 158 | const size_t PATH_MAX_WIDE = 32767; | 106 | const size_t PATH_MAX_WIDE = 32767; |
| 159 | 107 | ||
| 160 | struct PathSpace { | 108 | struct PathSpace { |
src/stage1/zig0.cpp+10| ... | @@ -430,6 +430,16 @@ int main(int argc, char **argv) { | ... | @@ -430,6 +430,16 @@ int main(int argc, char **argv) { |
| 430 | return print_error_usage(arg0); | 430 | return print_error_usage(arg0); |
| 431 | } | 431 | } |
| 432 | 432 | ||
| 433 | if (override_lib_dir == nullptr) { | ||
| 434 | fprintf(stderr, "missing --override-lib-dir\n"); | ||
| 435 | return print_error_usage(arg0); | ||
| 436 | } | ||
| 437 | |||
| 438 | if (emit_bin_path == nullptr) { | ||
| 439 | fprintf(stderr, "missing -femit-bin=\n"); | ||
| 440 | return print_error_usage(arg0); | ||
| 441 | } | ||
| 442 | |||
| 433 | ZigStage1 *stage1 = zig_stage1_create(optimize_mode, | 443 | ZigStage1 *stage1 = zig_stage1_create(optimize_mode, |
| 434 | nullptr, 0, | 444 | nullptr, 0, |
| 435 | in_file, strlen(in_file), | 445 | in_file, strlen(in_file), |
src/target.zig+1-1| ... | @@ -130,7 +130,6 @@ pub fn osRequiresLibC(target: std.Target) bool { | ... | @@ -130,7 +130,6 @@ pub fn osRequiresLibC(target: std.Target) bool { |
| 130 | 130 | ||
| 131 | pub fn libcNeedsLibUnwind(target: std.Target) bool { | 131 | pub fn libcNeedsLibUnwind(target: std.Target) bool { |
| 132 | return switch (target.os.tag) { | 132 | return switch (target.os.tag) { |
| 133 | .windows, | ||
| 134 | .macosx, | 133 | .macosx, |
| 135 | .ios, | 134 | .ios, |
| 136 | .watchos, | 135 | .watchos, |
| ... | @@ -138,6 +137,7 @@ pub fn libcNeedsLibUnwind(target: std.Target) bool { | ... | @@ -138,6 +137,7 @@ pub fn libcNeedsLibUnwind(target: std.Target) bool { |
| 138 | .freestanding, | 137 | .freestanding, |
| 139 | => false, | 138 | => false, |
| 140 | 139 | ||
| 140 | .windows => target.abi != .msvc, | ||
| 141 | else => true, | 141 | else => true, |
| 142 | }; | 142 | }; |
| 143 | } | 143 | } |
src/test.zig+15| ... | @@ -56,6 +56,12 @@ pub const TestContext = struct { | ... | @@ -56,6 +56,12 @@ pub const TestContext = struct { |
| 56 | }, | 56 | }, |
| 57 | }; | 57 | }; |
| 58 | 58 | ||
| 59 | pub const File = struct { | ||
| 60 | /// Contents of the importable file. Doesn't yet support incremental updates. | ||
| 61 | src: [:0]const u8, | ||
| 62 | path: []const u8, | ||
| 63 | }; | ||
| 64 | |||
| 59 | pub const TestType = enum { | 65 | pub const TestType = enum { |
| 60 | Zig, | 66 | Zig, |
| 61 | ZIR, | 67 | ZIR, |
| ... | @@ -78,6 +84,8 @@ pub const TestContext = struct { | ... | @@ -78,6 +84,8 @@ pub const TestContext = struct { |
| 78 | extension: TestType, | 84 | extension: TestType, |
| 79 | cbe: bool = false, | 85 | cbe: bool = false, |
| 80 | 86 | ||
| 87 | files: std.ArrayList(File), | ||
| 88 | |||
| 81 | /// Adds a subcase in which the module is updated with `src`, and the | 89 | /// Adds a subcase in which the module is updated with `src`, and the |
| 82 | /// resulting ZIR is validated against `result`. | 90 | /// resulting ZIR is validated against `result`. |
| 83 | pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void { | 91 | pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void { |
| ... | @@ -156,6 +164,7 @@ pub const TestContext = struct { | ... | @@ -156,6 +164,7 @@ pub const TestContext = struct { |
| 156 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | 164 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 157 | .output_mode = .Exe, | 165 | .output_mode = .Exe, |
| 158 | .extension = T, | 166 | .extension = T, |
| 167 | .files = std.ArrayList(File).init(ctx.cases.allocator), | ||
| 159 | }) catch unreachable; | 168 | }) catch unreachable; |
| 160 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 169 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 161 | } | 170 | } |
| ... | @@ -182,6 +191,7 @@ pub const TestContext = struct { | ... | @@ -182,6 +191,7 @@ pub const TestContext = struct { |
| 182 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), | 191 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 183 | .output_mode = .Obj, | 192 | .output_mode = .Obj, |
| 184 | .extension = T, | 193 | .extension = T, |
| 194 | .files = std.ArrayList(File).init(ctx.cases.allocator), | ||
| 185 | }) catch unreachable; | 195 | }) catch unreachable; |
| 186 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 196 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 187 | } | 197 | } |
| ... | @@ -204,6 +214,7 @@ pub const TestContext = struct { | ... | @@ -204,6 +214,7 @@ pub const TestContext = struct { |
| 204 | .output_mode = .Obj, | 214 | .output_mode = .Obj, |
| 205 | .extension = T, | 215 | .extension = T, |
| 206 | .cbe = true, | 216 | .cbe = true, |
| 217 | .files = std.ArrayList(File).init(ctx.cases.allocator), | ||
| 207 | }) catch unreachable; | 218 | }) catch unreachable; |
| 208 | return &ctx.cases.items[ctx.cases.items.len - 1]; | 219 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 209 | } | 220 | } |
| ... | @@ -505,6 +516,10 @@ pub const TestContext = struct { | ... | @@ -505,6 +516,10 @@ pub const TestContext = struct { |
| 505 | }); | 516 | }); |
| 506 | defer comp.destroy(); | 517 | defer comp.destroy(); |
| 507 | 518 | ||
| 519 | for (case.files.items) |file| { | ||
| 520 | try tmp.dir.writeFile(file.path, file.src); | ||
| 521 | } | ||
| 522 | |||
| 508 | for (case.updates.items) |update, update_index| { | 523 | for (case.updates.items) |update, update_index| { |
| 509 | var update_node = root_node.start("update", 3); | 524 | var update_node = root_node.start("update", 3); |
| 510 | update_node.activate(); | 525 | update_node.activate(); |
src/translate_c.zig+380-165| ... | @@ -5527,58 +5527,41 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { | ... | @@ -5527,58 +5527,41 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { |
| 5527 | const ParseError = Error || error{ParseError}; | 5527 | const ParseError = Error || error{ParseError}; |
| 5528 | 5528 | ||
| 5529 | fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 5529 | fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { |
| 5530 | const node = try parseCPrefixOpExpr(c, m, scope); | 5530 | // TODO parseCAssignExpr here |
| 5531 | switch (m.next().?) { | 5531 | const node = try parseCCondExpr(c, m, scope); |
| 5532 | .QuestionMark => { | 5532 | if (m.next().? != .Comma) { |
| 5533 | // must come immediately after expr | 5533 | m.i -= 1; |
| 5534 | _ = try appendToken(c, .RParen, ")"); | 5534 | return node; |
| 5535 | const if_node = try transCreateNodeIf(c); | 5535 | } |
| 5536 | if_node.condition = node; | 5536 | _ = try appendToken(c, .Semicolon, ";"); |
| 5537 | if_node.body = try parseCPrimaryExpr(c, m, scope); | 5537 | var block_scope = try Scope.Block.init(c, scope, true); |
| 5538 | if (m.next().? != .Colon) { | 5538 | defer block_scope.deinit(); |
| 5539 | try m.fail(c, "unable to translate C expr: expected ':'", .{}); | ||
| 5540 | return error.ParseError; | ||
| 5541 | } | ||
| 5542 | if_node.@"else" = try transCreateNodeElse(c); | ||
| 5543 | if_node.@"else".?.body = try parseCPrimaryExpr(c, m, scope); | ||
| 5544 | return &if_node.base; | ||
| 5545 | }, | ||
| 5546 | .Comma => { | ||
| 5547 | _ = try appendToken(c, .Semicolon, ";"); | ||
| 5548 | var block_scope = try Scope.Block.init(c, scope, true); | ||
| 5549 | defer block_scope.deinit(); | ||
| 5550 | |||
| 5551 | var last = node; | ||
| 5552 | while (true) { | ||
| 5553 | // suppress result | ||
| 5554 | const lhs = try transCreateNodeIdentifier(c, "_"); | ||
| 5555 | const op_token = try appendToken(c, .Equal, "="); | ||
| 5556 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 5557 | op_node.* = .{ | ||
| 5558 | .base = .{ .tag = .Assign }, | ||
| 5559 | .op_token = op_token, | ||
| 5560 | .lhs = lhs, | ||
| 5561 | .rhs = last, | ||
| 5562 | }; | ||
| 5563 | try block_scope.statements.append(&op_node.base); | ||
| 5564 | 5539 | ||
| 5565 | last = try parseCPrefixOpExpr(c, m, scope); | 5540 | var last = node; |
| 5566 | _ = try appendToken(c, .Semicolon, ";"); | 5541 | while (true) { |
| 5567 | if (m.next().? != .Comma) { | 5542 | // suppress result |
| 5568 | m.i -= 1; | 5543 | const lhs = try transCreateNodeIdentifier(c, "_"); |
| 5569 | break; | 5544 | const op_token = try appendToken(c, .Equal, "="); |
| 5570 | } | 5545 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); |
| 5571 | } | 5546 | op_node.* = .{ |
| 5547 | .base = .{ .tag = .Assign }, | ||
| 5548 | .op_token = op_token, | ||
| 5549 | .lhs = lhs, | ||
| 5550 | .rhs = last, | ||
| 5551 | }; | ||
| 5552 | try block_scope.statements.append(&op_node.base); | ||
| 5572 | 5553 | ||
| 5573 | const break_node = try transCreateNodeBreak(c, block_scope.label, last); | 5554 | last = try parseCCondExpr(c, m, scope); |
| 5574 | try block_scope.statements.append(&break_node.base); | 5555 | _ = try appendToken(c, .Semicolon, ";"); |
| 5575 | return try block_scope.complete(c); | 5556 | if (m.next().? != .Comma) { |
| 5576 | }, | ||
| 5577 | else => { | ||
| 5578 | m.i -= 1; | 5557 | m.i -= 1; |
| 5579 | return node; | 5558 | break; |
| 5580 | }, | 5559 | } |
| 5581 | } | 5560 | } |
| 5561 | |||
| 5562 | const break_node = try transCreateNodeBreak(c, block_scope.label, last); | ||
| 5563 | try block_scope.statements.append(&break_node.base); | ||
| 5564 | return try block_scope.complete(c); | ||
| 5582 | } | 5565 | } |
| 5583 | 5566 | ||
| 5584 | fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { | 5567 | fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { |
| ... | @@ -5805,7 +5788,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { | ... | @@ -5805,7 +5788,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 { |
| 5805 | return bytes[0..i]; | 5788 | return bytes[0..i]; |
| 5806 | } | 5789 | } |
| 5807 | 5790 | ||
| 5808 | fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 5791 | fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { |
| 5809 | const tok = m.next().?; | 5792 | const tok = m.next().?; |
| 5810 | const slice = m.slice(); | 5793 | const slice = m.slice(); |
| 5811 | switch (tok) { | 5794 | switch (tok) { |
| ... | @@ -5952,6 +5935,30 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N | ... | @@ -5952,6 +5935,30 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N |
| 5952 | } | 5935 | } |
| 5953 | } | 5936 | } |
| 5954 | 5937 | ||
| 5938 | fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 5939 | var node = try parseCPrimaryExprInner(c, m, scope); | ||
| 5940 | // In C the preprocessor would handle concatting strings while expanding macros. | ||
| 5941 | // This should do approximately the same by concatting any strings and identifiers | ||
| 5942 | // after a primary expression. | ||
| 5943 | while (true) { | ||
| 5944 | var op_token: ast.TokenIndex = undefined; | ||
| 5945 | var op_id: ast.Node.Tag = undefined; | ||
| 5946 | switch (m.peek().?) { | ||
| 5947 | .StringLiteral, .Identifier => {}, | ||
| 5948 | else => break, | ||
| 5949 | } | ||
| 5950 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 5951 | op_node.* = .{ | ||
| 5952 | .base = .{ .tag = .ArrayCat }, | ||
| 5953 | .op_token = try appendToken(c, .PlusPlus, "++"), | ||
| 5954 | .lhs = node, | ||
| 5955 | .rhs = try parseCPrimaryExprInner(c, m, scope), | ||
| 5956 | }; | ||
| 5957 | node = &op_node.base; | ||
| 5958 | } | ||
| 5959 | return node; | ||
| 5960 | } | ||
| 5961 | |||
| 5955 | fn nodeIsInfixOp(tag: ast.Node.Tag) bool { | 5962 | fn nodeIsInfixOp(tag: ast.Node.Tag) bool { |
| 5956 | return switch (tag) { | 5963 | return switch (tag) { |
| 5957 | .Add, | 5964 | .Add, |
| ... | @@ -6053,31 +6060,268 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node { | ... | @@ -6053,31 +6060,268 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node { |
| 6053 | return &group_node.base; | 6060 | return &group_node.base; |
| 6054 | } | 6061 | } |
| 6055 | 6062 | ||
| 6056 | fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 6063 | fn macroGroup(c: *Context, node: *ast.Node) !*ast.Node { |
| 6057 | var node = try parseCPrimaryExpr(c, m, scope); | 6064 | if (!nodeIsInfixOp(node.tag)) return node; |
| 6065 | |||
| 6066 | const group_node = try c.arena.create(ast.Node.GroupedExpression); | ||
| 6067 | group_node.* = .{ | ||
| 6068 | .lparen = try appendToken(c, .LParen, "("), | ||
| 6069 | .expr = node, | ||
| 6070 | .rparen = try appendToken(c, .RParen, ")"), | ||
| 6071 | }; | ||
| 6072 | return &group_node.base; | ||
| 6073 | } | ||
| 6074 | |||
| 6075 | fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 6076 | const node = try parseCOrExpr(c, m, scope); | ||
| 6077 | if (m.peek().? != .QuestionMark) { | ||
| 6078 | return node; | ||
| 6079 | } | ||
| 6080 | _ = m.next(); | ||
| 6081 | |||
| 6082 | // must come immediately after expr | ||
| 6083 | _ = try appendToken(c, .RParen, ")"); | ||
| 6084 | const if_node = try transCreateNodeIf(c); | ||
| 6085 | if_node.condition = node; | ||
| 6086 | if_node.body = try parseCOrExpr(c, m, scope); | ||
| 6087 | if (m.next().? != .Colon) { | ||
| 6088 | try m.fail(c, "unable to translate C expr: expected ':'", .{}); | ||
| 6089 | return error.ParseError; | ||
| 6090 | } | ||
| 6091 | if_node.@"else" = try transCreateNodeElse(c); | ||
| 6092 | if_node.@"else".?.body = try parseCCondExpr(c, m, scope); | ||
| 6093 | return &if_node.base; | ||
| 6094 | } | ||
| 6095 | |||
| 6096 | fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 6097 | var node = try parseCAndExpr(c, m, scope); | ||
| 6098 | while (m.next().? == .PipePipe) { | ||
| 6099 | const lhs_node = try macroIntToBool(c, node); | ||
| 6100 | const op_token = try appendToken(c, .Keyword_or, "or"); | ||
| 6101 | const rhs_node = try parseCAndExpr(c, m, scope); | ||
| 6102 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6103 | op_node.* = .{ | ||
| 6104 | .base = .{ .tag = .BoolOr }, | ||
| 6105 | .op_token = op_token, | ||
| 6106 | .lhs = lhs_node, | ||
| 6107 | .rhs = try macroIntToBool(c, rhs_node), | ||
| 6108 | }; | ||
| 6109 | node = &op_node.base; | ||
| 6110 | } | ||
| 6111 | m.i -= 1; | ||
| 6112 | return node; | ||
| 6113 | } | ||
| 6114 | |||
| 6115 | fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 6116 | var node = try parseCBitOrExpr(c, m, scope); | ||
| 6117 | while (m.next().? == .AmpersandAmpersand) { | ||
| 6118 | const lhs_node = try macroIntToBool(c, node); | ||
| 6119 | const op_token = try appendToken(c, .Keyword_and, "and"); | ||
| 6120 | const rhs_node = try parseCBitOrExpr(c, m, scope); | ||
| 6121 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6122 | op_node.* = .{ | ||
| 6123 | .base = .{ .tag = .BoolAnd }, | ||
| 6124 | .op_token = op_token, | ||
| 6125 | .lhs = lhs_node, | ||
| 6126 | .rhs = try macroIntToBool(c, rhs_node), | ||
| 6127 | }; | ||
| 6128 | node = &op_node.base; | ||
| 6129 | } | ||
| 6130 | m.i -= 1; | ||
| 6131 | return node; | ||
| 6132 | } | ||
| 6133 | |||
| 6134 | fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 6135 | var node = try parseCBitXorExpr(c, m, scope); | ||
| 6136 | while (m.next().? == .Pipe) { | ||
| 6137 | const lhs_node = try macroBoolToInt(c, node); | ||
| 6138 | const op_token = try appendToken(c, .Pipe, "|"); | ||
| 6139 | const rhs_node = try parseCBitXorExpr(c, m, scope); | ||
| 6140 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6141 | op_node.* = .{ | ||
| 6142 | .base = .{ .tag = .BitOr }, | ||
| 6143 | .op_token = op_token, | ||
| 6144 | .lhs = lhs_node, | ||
| 6145 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6146 | }; | ||
| 6147 | node = &op_node.base; | ||
| 6148 | } | ||
| 6149 | m.i -= 1; | ||
| 6150 | return node; | ||
| 6151 | } | ||
| 6152 | |||
| 6153 | fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 6154 | var node = try parseCBitAndExpr(c, m, scope); | ||
| 6155 | while (m.next().? == .Caret) { | ||
| 6156 | const lhs_node = try macroBoolToInt(c, node); | ||
| 6157 | const op_token = try appendToken(c, .Caret, "^"); | ||
| 6158 | const rhs_node = try parseCBitAndExpr(c, m, scope); | ||
| 6159 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6160 | op_node.* = .{ | ||
| 6161 | .base = .{ .tag = .BitXor }, | ||
| 6162 | .op_token = op_token, | ||
| 6163 | .lhs = lhs_node, | ||
| 6164 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6165 | }; | ||
| 6166 | node = &op_node.base; | ||
| 6167 | } | ||
| 6168 | m.i -= 1; | ||
| 6169 | return node; | ||
| 6170 | } | ||
| 6171 | |||
| 6172 | fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 6173 | var node = try parseCEqExpr(c, m, scope); | ||
| 6174 | while (m.next().? == .Ampersand) { | ||
| 6175 | const lhs_node = try macroBoolToInt(c, node); | ||
| 6176 | const op_token = try appendToken(c, .Ampersand, "&"); | ||
| 6177 | const rhs_node = try parseCEqExpr(c, m, scope); | ||
| 6178 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6179 | op_node.* = .{ | ||
| 6180 | .base = .{ .tag = .BitAnd }, | ||
| 6181 | .op_token = op_token, | ||
| 6182 | .lhs = lhs_node, | ||
| 6183 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6184 | }; | ||
| 6185 | node = &op_node.base; | ||
| 6186 | } | ||
| 6187 | m.i -= 1; | ||
| 6188 | return node; | ||
| 6189 | } | ||
| 6190 | |||
| 6191 | fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 6192 | var node = try parseCRelExpr(c, m, scope); | ||
| 6058 | while (true) { | 6193 | while (true) { |
| 6059 | var op_token: ast.TokenIndex = undefined; | 6194 | var op_token: ast.TokenIndex = undefined; |
| 6060 | var op_id: ast.Node.Tag = undefined; | 6195 | var op_id: ast.Node.Tag = undefined; |
| 6061 | var bool_op = false; | 6196 | switch (m.peek().?) { |
| 6062 | switch (m.next().?) { | 6197 | .BangEqual => { |
| 6063 | .Period => { | 6198 | op_token = try appendToken(c, .BangEqual, "!="); |
| 6064 | if (m.next().? != .Identifier) { | 6199 | op_id = .BangEqual; |
| 6065 | try m.fail(c, "unable to translate C expr: expected identifier", .{}); | 6200 | }, |
| 6066 | return error.ParseError; | 6201 | .EqualEqual => { |
| 6067 | } | 6202 | op_token = try appendToken(c, .EqualEqual, "=="); |
| 6203 | op_id = .EqualEqual; | ||
| 6204 | }, | ||
| 6205 | else => return node, | ||
| 6206 | } | ||
| 6207 | _ = m.next(); | ||
| 6208 | const lhs_node = try macroBoolToInt(c, node); | ||
| 6209 | const rhs_node = try parseCRelExpr(c, m, scope); | ||
| 6210 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6211 | op_node.* = .{ | ||
| 6212 | .base = .{ .tag = op_id }, | ||
| 6213 | .op_token = op_token, | ||
| 6214 | .lhs = lhs_node, | ||
| 6215 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6216 | }; | ||
| 6217 | node = &op_node.base; | ||
| 6218 | } | ||
| 6219 | } | ||
| 6068 | 6220 | ||
| 6069 | node = try transCreateNodeFieldAccess(c, node, m.slice()); | 6221 | fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { |
| 6070 | continue; | 6222 | var node = try parseCShiftExpr(c, m, scope); |
| 6223 | while (true) { | ||
| 6224 | var op_token: ast.TokenIndex = undefined; | ||
| 6225 | var op_id: ast.Node.Tag = undefined; | ||
| 6226 | switch (m.peek().?) { | ||
| 6227 | .AngleBracketRight => { | ||
| 6228 | op_token = try appendToken(c, .AngleBracketRight, ">"); | ||
| 6229 | op_id = .GreaterThan; | ||
| 6071 | }, | 6230 | }, |
| 6072 | .Arrow => { | 6231 | .AngleBracketRightEqual => { |
| 6073 | if (m.next().? != .Identifier) { | 6232 | op_token = try appendToken(c, .AngleBracketRightEqual, ">="); |
| 6074 | try m.fail(c, "unable to translate C expr: expected identifier", .{}); | 6233 | op_id = .GreaterOrEqual; |
| 6075 | return error.ParseError; | 6234 | }, |
| 6076 | } | 6235 | .AngleBracketLeft => { |
| 6077 | const deref = try transCreateNodePtrDeref(c, node); | 6236 | op_token = try appendToken(c, .AngleBracketLeft, "<"); |
| 6078 | node = try transCreateNodeFieldAccess(c, deref, m.slice()); | 6237 | op_id = .LessThan; |
| 6079 | continue; | ||
| 6080 | }, | 6238 | }, |
| 6239 | .AngleBracketLeftEqual => { | ||
| 6240 | op_token = try appendToken(c, .AngleBracketLeftEqual, "<="); | ||
| 6241 | op_id = .LessOrEqual; | ||
| 6242 | }, | ||
| 6243 | else => return node, | ||
| 6244 | } | ||
| 6245 | _ = m.next(); | ||
| 6246 | const lhs_node = try macroBoolToInt(c, node); | ||
| 6247 | const rhs_node = try parseCShiftExpr(c, m, scope); | ||
| 6248 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6249 | op_node.* = .{ | ||
| 6250 | .base = .{ .tag = op_id }, | ||
| 6251 | .op_token = op_token, | ||
| 6252 | .lhs = lhs_node, | ||
| 6253 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6254 | }; | ||
| 6255 | node = &op_node.base; | ||
| 6256 | } | ||
| 6257 | } | ||
| 6258 | |||
| 6259 | fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 6260 | var node = try parseCAddSubExpr(c, m, scope); | ||
| 6261 | while (true) { | ||
| 6262 | var op_token: ast.TokenIndex = undefined; | ||
| 6263 | var op_id: ast.Node.Tag = undefined; | ||
| 6264 | switch (m.peek().?) { | ||
| 6265 | .AngleBracketAngleBracketLeft => { | ||
| 6266 | op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<"); | ||
| 6267 | op_id = .BitShiftLeft; | ||
| 6268 | }, | ||
| 6269 | .AngleBracketAngleBracketRight => { | ||
| 6270 | op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>"); | ||
| 6271 | op_id = .BitShiftRight; | ||
| 6272 | }, | ||
| 6273 | else => return node, | ||
| 6274 | } | ||
| 6275 | _ = m.next(); | ||
| 6276 | const lhs_node = try macroBoolToInt(c, node); | ||
| 6277 | const rhs_node = try parseCAddSubExpr(c, m, scope); | ||
| 6278 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6279 | op_node.* = .{ | ||
| 6280 | .base = .{ .tag = op_id }, | ||
| 6281 | .op_token = op_token, | ||
| 6282 | .lhs = lhs_node, | ||
| 6283 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6284 | }; | ||
| 6285 | node = &op_node.base; | ||
| 6286 | } | ||
| 6287 | } | ||
| 6288 | |||
| 6289 | fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 6290 | var node = try parseCMulExpr(c, m, scope); | ||
| 6291 | while (true) { | ||
| 6292 | var op_token: ast.TokenIndex = undefined; | ||
| 6293 | var op_id: ast.Node.Tag = undefined; | ||
| 6294 | switch (m.peek().?) { | ||
| 6295 | .Plus => { | ||
| 6296 | op_token = try appendToken(c, .Plus, "+"); | ||
| 6297 | op_id = .Add; | ||
| 6298 | }, | ||
| 6299 | .Minus => { | ||
| 6300 | op_token = try appendToken(c, .Minus, "-"); | ||
| 6301 | op_id = .Sub; | ||
| 6302 | }, | ||
| 6303 | else => return node, | ||
| 6304 | } | ||
| 6305 | _ = m.next(); | ||
| 6306 | const lhs_node = try macroBoolToInt(c, node); | ||
| 6307 | const rhs_node = try parseCMulExpr(c, m, scope); | ||
| 6308 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6309 | op_node.* = .{ | ||
| 6310 | .base = .{ .tag = op_id }, | ||
| 6311 | .op_token = op_token, | ||
| 6312 | .lhs = lhs_node, | ||
| 6313 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6314 | }; | ||
| 6315 | node = &op_node.base; | ||
| 6316 | } | ||
| 6317 | } | ||
| 6318 | |||
| 6319 | fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 6320 | var node = try parseCUnaryExpr(c, m, scope); | ||
| 6321 | while (true) { | ||
| 6322 | var op_token: ast.TokenIndex = undefined; | ||
| 6323 | var op_id: ast.Node.Tag = undefined; | ||
| 6324 | switch (m.next().?) { | ||
| 6081 | .Asterisk => { | 6325 | .Asterisk => { |
| 6082 | if (m.peek().? == .RParen) { | 6326 | if (m.peek().? == .RParen) { |
| 6083 | // type *) | 6327 | // type *) |
| ... | @@ -6105,59 +6349,57 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. | ... | @@ -6105,59 +6349,57 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. |
| 6105 | op_id = .BitShiftLeft; | 6349 | op_id = .BitShiftLeft; |
| 6106 | } | 6350 | } |
| 6107 | }, | 6351 | }, |
| 6108 | .AngleBracketAngleBracketLeft => { | 6352 | .Slash => { |
| 6109 | op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<"); | 6353 | op_id = .Div; |
| 6110 | op_id = .BitShiftLeft; | 6354 | op_token = try appendToken(c, .Slash, "/"); |
| 6111 | }, | ||
| 6112 | .AngleBracketAngleBracketRight => { | ||
| 6113 | op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>"); | ||
| 6114 | op_id = .BitShiftRight; | ||
| 6115 | }, | ||
| 6116 | .Pipe => { | ||
| 6117 | op_token = try appendToken(c, .Pipe, "|"); | ||
| 6118 | op_id = .BitOr; | ||
| 6119 | }, | ||
| 6120 | .Ampersand => { | ||
| 6121 | op_token = try appendToken(c, .Ampersand, "&"); | ||
| 6122 | op_id = .BitAnd; | ||
| 6123 | }, | ||
| 6124 | .Plus => { | ||
| 6125 | op_token = try appendToken(c, .Plus, "+"); | ||
| 6126 | op_id = .Add; | ||
| 6127 | }, | ||
| 6128 | .Minus => { | ||
| 6129 | op_token = try appendToken(c, .Minus, "-"); | ||
| 6130 | op_id = .Sub; | ||
| 6131 | }, | ||
| 6132 | .AmpersandAmpersand => { | ||
| 6133 | op_token = try appendToken(c, .Keyword_and, "and"); | ||
| 6134 | op_id = .BoolAnd; | ||
| 6135 | bool_op = true; | ||
| 6136 | }, | ||
| 6137 | .PipePipe => { | ||
| 6138 | op_token = try appendToken(c, .Keyword_or, "or"); | ||
| 6139 | op_id = .BoolOr; | ||
| 6140 | bool_op = true; | ||
| 6141 | }, | 6355 | }, |
| 6142 | .AngleBracketRight => { | 6356 | .Percent => { |
| 6143 | op_token = try appendToken(c, .AngleBracketRight, ">"); | 6357 | op_id = .Mod; |
| 6144 | op_id = .GreaterThan; | 6358 | op_token = try appendToken(c, .Percent, "%"); |
| 6145 | }, | 6359 | }, |
| 6146 | .AngleBracketRightEqual => { | 6360 | else => { |
| 6147 | op_token = try appendToken(c, .AngleBracketRightEqual, ">="); | 6361 | m.i -= 1; |
| 6148 | op_id = .GreaterOrEqual; | 6362 | return node; |
| 6149 | }, | 6363 | }, |
| 6150 | .AngleBracketLeft => { | 6364 | } |
| 6151 | op_token = try appendToken(c, .AngleBracketLeft, "<"); | 6365 | const lhs_node = try macroBoolToInt(c, node); |
| 6152 | op_id = .LessThan; | 6366 | const rhs_node = try parseCUnaryExpr(c, m, scope); |
| 6367 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6368 | op_node.* = .{ | ||
| 6369 | .base = .{ .tag = op_id }, | ||
| 6370 | .op_token = op_token, | ||
| 6371 | .lhs = lhs_node, | ||
| 6372 | .rhs = try macroBoolToInt(c, rhs_node), | ||
| 6373 | }; | ||
| 6374 | node = &op_node.base; | ||
| 6375 | } | ||
| 6376 | } | ||
| 6377 | |||
| 6378 | fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | ||
| 6379 | var node = try parseCPrimaryExpr(c, m, scope); | ||
| 6380 | while (true) { | ||
| 6381 | switch (m.next().?) { | ||
| 6382 | .Period => { | ||
| 6383 | if (m.next().? != .Identifier) { | ||
| 6384 | try m.fail(c, "unable to translate C expr: expected identifier", .{}); | ||
| 6385 | return error.ParseError; | ||
| 6386 | } | ||
| 6387 | |||
| 6388 | node = try transCreateNodeFieldAccess(c, node, m.slice()); | ||
| 6389 | continue; | ||
| 6153 | }, | 6390 | }, |
| 6154 | .AngleBracketLeftEqual => { | 6391 | .Arrow => { |
| 6155 | op_token = try appendToken(c, .AngleBracketLeftEqual, "<="); | 6392 | if (m.next().? != .Identifier) { |
| 6156 | op_id = .LessOrEqual; | 6393 | try m.fail(c, "unable to translate C expr: expected identifier", .{}); |
| 6394 | return error.ParseError; | ||
| 6395 | } | ||
| 6396 | const deref = try transCreateNodePtrDeref(c, node); | ||
| 6397 | node = try transCreateNodeFieldAccess(c, deref, m.slice()); | ||
| 6398 | continue; | ||
| 6157 | }, | 6399 | }, |
| 6158 | .LBracket => { | 6400 | .LBracket => { |
| 6159 | const arr_node = try transCreateNodeArrayAccess(c, node); | 6401 | const arr_node = try transCreateNodeArrayAccess(c, node); |
| 6160 | arr_node.index_expr = try parseCPrefixOpExpr(c, m, scope); | 6402 | arr_node.index_expr = try parseCExpr(c, m, scope); |
| 6161 | arr_node.rtoken = try appendToken(c, .RBracket, "]"); | 6403 | arr_node.rtoken = try appendToken(c, .RBracket, "]"); |
| 6162 | node = &arr_node.base; | 6404 | node = &arr_node.base; |
| 6163 | if (m.next().? != .RBracket) { | 6405 | if (m.next().? != .RBracket) { |
| ... | @@ -6171,7 +6413,7 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. | ... | @@ -6171,7 +6413,7 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. |
| 6171 | var call_params = std.ArrayList(*ast.Node).init(c.gpa); | 6413 | var call_params = std.ArrayList(*ast.Node).init(c.gpa); |
| 6172 | defer call_params.deinit(); | 6414 | defer call_params.deinit(); |
| 6173 | while (true) { | 6415 | while (true) { |
| 6174 | const arg = try parseCPrefixOpExpr(c, m, scope); | 6416 | const arg = try parseCCondExpr(c, m, scope); |
| 6175 | try call_params.append(arg); | 6417 | try call_params.append(arg); |
| 6176 | switch (m.next().?) { | 6418 | switch (m.next().?) { |
| 6177 | .Comma => _ = try appendToken(c, .Comma, ","), | 6419 | .Comma => _ = try appendToken(c, .Comma, ","), |
| ... | @@ -6204,7 +6446,7 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. | ... | @@ -6204,7 +6446,7 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. |
| 6204 | defer init_vals.deinit(); | 6446 | defer init_vals.deinit(); |
| 6205 | 6447 | ||
| 6206 | while (true) { | 6448 | while (true) { |
| 6207 | const val = try parseCPrefixOpExpr(c, m, scope); | 6449 | const val = try parseCCondExpr(c, m, scope); |
| 6208 | try init_vals.append(val); | 6450 | try init_vals.append(val); |
| 6209 | switch (m.next().?) { | 6451 | switch (m.next().?) { |
| 6210 | .Comma => _ = try appendToken(c, .Comma, ","), | 6452 | .Comma => _ = try appendToken(c, .Comma, ","), |
| ... | @@ -6239,90 +6481,57 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. | ... | @@ -6239,90 +6481,57 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. |
| 6239 | node = &zero_init_call.base; | 6481 | node = &zero_init_call.base; |
| 6240 | continue; | 6482 | continue; |
| 6241 | }, | 6483 | }, |
| 6242 | .BangEqual => { | 6484 | .PlusPlus, .MinusMinus => { |
| 6243 | op_token = try appendToken(c, .BangEqual, "!="); | 6485 | try m.fail(c, "TODO postfix inc/dec expr", .{}); |
| 6244 | op_id = .BangEqual; | 6486 | return error.ParseError; |
| 6245 | }, | ||
| 6246 | .EqualEqual => { | ||
| 6247 | op_token = try appendToken(c, .EqualEqual, "=="); | ||
| 6248 | op_id = .EqualEqual; | ||
| 6249 | }, | ||
| 6250 | .Slash => { | ||
| 6251 | op_id = .Div; | ||
| 6252 | op_token = try appendToken(c, .Slash, "/"); | ||
| 6253 | }, | ||
| 6254 | .Percent => { | ||
| 6255 | op_id = .Mod; | ||
| 6256 | op_token = try appendToken(c, .Percent, "%"); | ||
| 6257 | }, | ||
| 6258 | .StringLiteral => { | ||
| 6259 | op_id = .ArrayCat; | ||
| 6260 | op_token = try appendToken(c, .PlusPlus, "++"); | ||
| 6261 | |||
| 6262 | m.i -= 1; | ||
| 6263 | }, | ||
| 6264 | .Identifier => { | ||
| 6265 | op_id = .ArrayCat; | ||
| 6266 | op_token = try appendToken(c, .PlusPlus, "++"); | ||
| 6267 | |||
| 6268 | m.i -= 1; | ||
| 6269 | }, | 6487 | }, |
| 6270 | else => { | 6488 | else => { |
| 6271 | m.i -= 1; | 6489 | m.i -= 1; |
| 6272 | return node; | 6490 | return node; |
| 6273 | }, | 6491 | }, |
| 6274 | } | 6492 | } |
| 6275 | const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt; | ||
| 6276 | const lhs_node = try cast_fn(c, node); | ||
| 6277 | const rhs_node = try parseCPrefixOpExpr(c, m, scope); | ||
| 6278 | const op_node = try c.arena.create(ast.Node.SimpleInfixOp); | ||
| 6279 | op_node.* = .{ | ||
| 6280 | .base = .{ .tag = op_id }, | ||
| 6281 | .op_token = op_token, | ||
| 6282 | .lhs = lhs_node, | ||
| 6283 | .rhs = try cast_fn(c, rhs_node), | ||
| 6284 | }; | ||
| 6285 | node = &op_node.base; | ||
| 6286 | } | 6493 | } |
| 6287 | } | 6494 | } |
| 6288 | 6495 | ||
| 6289 | fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { | 6496 | fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { |
| 6290 | switch (m.next().?) { | 6497 | switch (m.next().?) { |
| 6291 | .Bang => { | 6498 | .Bang => { |
| 6292 | const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!"); | 6499 | const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!"); |
| 6293 | node.rhs = try parseCPrefixOpExpr(c, m, scope); | 6500 | node.rhs = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope)); |
| 6294 | return &node.base; | 6501 | return &node.base; |
| 6295 | }, | 6502 | }, |
| 6296 | .Minus => { | 6503 | .Minus => { |
| 6297 | const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-"); | 6504 | const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-"); |
| 6298 | node.rhs = try parseCPrefixOpExpr(c, m, scope); | 6505 | node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); |
| 6299 | return &node.base; | 6506 | return &node.base; |
| 6300 | }, | 6507 | }, |
| 6301 | .Plus => return try parseCPrefixOpExpr(c, m, scope), | 6508 | .Plus => return try parseCUnaryExpr(c, m, scope), |
| 6302 | .Tilde => { | 6509 | .Tilde => { |
| 6303 | const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~"); | 6510 | const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~"); |
| 6304 | node.rhs = try parseCPrefixOpExpr(c, m, scope); | 6511 | node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope)); |
| 6305 | return &node.base; | 6512 | return &node.base; |
| 6306 | }, | 6513 | }, |
| 6307 | .Asterisk => { | 6514 | .Asterisk => { |
| 6308 | const node = try parseCPrefixOpExpr(c, m, scope); | 6515 | const node = try macroGroup(c, try parseCUnaryExpr(c, m, scope)); |
| 6309 | return try transCreateNodePtrDeref(c, node); | 6516 | return try transCreateNodePtrDeref(c, node); |
| 6310 | }, | 6517 | }, |
| 6311 | .Ampersand => { | 6518 | .Ampersand => { |
| 6312 | const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&"); | 6519 | const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&"); |
| 6313 | node.rhs = try parseCPrefixOpExpr(c, m, scope); | 6520 | node.rhs = try macroGroup(c, try parseCUnaryExpr(c, m, scope)); |
| 6314 | return &node.base; | 6521 | return &node.base; |
| 6315 | }, | 6522 | }, |
| 6316 | .Keyword_sizeof => { | 6523 | .Keyword_sizeof => { |
| 6317 | const inner = if (m.peek().? == .LParen) blk: { | 6524 | const inner = if (m.peek().? == .LParen) blk: { |
| 6318 | _ = m.next(); | 6525 | _ = m.next(); |
| 6319 | const inner = try parseCExpr(c, m, scope); | 6526 | // C grammar says this should be 'type-name' but we have to |
| 6527 | // use parseCMulExpr to correctly handle pointer types. | ||
| 6528 | const inner = try parseCMulExpr(c, m, scope); | ||
| 6320 | if (m.next().? != .RParen) { | 6529 | if (m.next().? != .RParen) { |
| 6321 | try m.fail(c, "unable to translate C expr: expected ')'", .{}); | 6530 | try m.fail(c, "unable to translate C expr: expected ')'", .{}); |
| 6322 | return error.ParseError; | 6531 | return error.ParseError; |
| 6323 | } | 6532 | } |
| 6324 | break :blk inner; | 6533 | break :blk inner; |
| 6325 | } else try parseCPrefixOpExpr(c, m, scope); | 6534 | } else try parseCUnaryExpr(c, m, scope); |
| 6326 | 6535 | ||
| 6327 | //(@import("std").meta.sizeof(dest, x)) | 6536 | //(@import("std").meta.sizeof(dest, x)) |
| 6328 | const import_fn_call = try c.createBuiltinCall("@import", 1); | 6537 | const import_fn_call = try c.createBuiltinCall("@import", 1); |
| ... | @@ -6344,7 +6553,9 @@ fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. | ... | @@ -6344,7 +6553,9 @@ fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. |
| 6344 | try m.fail(c, "unable to translate C expr: expected '('", .{}); | 6553 | try m.fail(c, "unable to translate C expr: expected '('", .{}); |
| 6345 | return error.ParseError; | 6554 | return error.ParseError; |
| 6346 | } | 6555 | } |
| 6347 | const inner = try parseCExpr(c, m, scope); | 6556 | // C grammar says this should be 'type-name' but we have to |
| 6557 | // use parseCMulExpr to correctly handle pointer types. | ||
| 6558 | const inner = try parseCMulExpr(c, m, scope); | ||
| 6348 | if (m.next().? != .RParen) { | 6559 | if (m.next().? != .RParen) { |
| 6349 | try m.fail(c, "unable to translate C expr: expected ')'", .{}); | 6560 | try m.fail(c, "unable to translate C expr: expected ')'", .{}); |
| 6350 | return error.ParseError; | 6561 | return error.ParseError; |
| ... | @@ -6355,9 +6566,13 @@ fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. | ... | @@ -6355,9 +6566,13 @@ fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast. |
| 6355 | builtin_call.rparen_token = try appendToken(c, .RParen, ")"); | 6566 | builtin_call.rparen_token = try appendToken(c, .RParen, ")"); |
| 6356 | return &builtin_call.base; | 6567 | return &builtin_call.base; |
| 6357 | }, | 6568 | }, |
| 6569 | .PlusPlus, .MinusMinus => { | ||
| 6570 | try m.fail(c, "TODO unary inc/dec expr", .{}); | ||
| 6571 | return error.ParseError; | ||
| 6572 | }, | ||
| 6358 | else => { | 6573 | else => { |
| 6359 | m.i -= 1; | 6574 | m.i -= 1; |
| 6360 | return try parseCSuffixOpExpr(c, m, scope); | 6575 | return try parseCPostfixExpr(c, m, scope); |
| 6361 | }, | 6576 | }, |
| 6362 | } | 6577 | } |
| 6363 | } | 6578 | } |
src/type.zig+112| ... | @@ -89,6 +89,8 @@ pub const Type = extern union { | ... | @@ -89,6 +89,8 @@ pub const Type = extern union { |
| 89 | .anyerror_void_error_union, .error_union => return .ErrorUnion, | 89 | .anyerror_void_error_union, .error_union => return .ErrorUnion, |
| 90 | 90 | ||
| 91 | .anyframe_T, .@"anyframe" => return .AnyFrame, | 91 | .anyframe_T, .@"anyframe" => return .AnyFrame, |
| 92 | |||
| 93 | .empty_struct => return .Struct, | ||
| 92 | } | 94 | } |
| 93 | } | 95 | } |
| 94 | 96 | ||
| ... | @@ -439,6 +441,7 @@ pub const Type = extern union { | ... | @@ -439,6 +441,7 @@ pub const Type = extern union { |
| 439 | }, | 441 | }, |
| 440 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), | 442 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), |
| 441 | .error_set_single => return self.copyPayloadShallow(allocator, Payload.ErrorSetSingle), | 443 | .error_set_single => return self.copyPayloadShallow(allocator, Payload.ErrorSetSingle), |
| 444 | .empty_struct => return self.copyPayloadShallow(allocator, Payload.EmptyStruct), | ||
| 442 | } | 445 | } |
| 443 | } | 446 | } |
| 444 | 447 | ||
| ... | @@ -505,6 +508,8 @@ pub const Type = extern union { | ... | @@ -505,6 +508,8 @@ pub const Type = extern union { |
| 505 | .@"null" => return out_stream.writeAll("@Type(.Null)"), | 508 | .@"null" => return out_stream.writeAll("@Type(.Null)"), |
| 506 | .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"), | 509 | .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"), |
| 507 | 510 | ||
| 511 | // TODO this should print the structs name | ||
| 512 | .empty_struct => return out_stream.writeAll("struct {}"), | ||
| 508 | .@"anyframe" => return out_stream.writeAll("anyframe"), | 513 | .@"anyframe" => return out_stream.writeAll("anyframe"), |
| 509 | .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"), | 514 | .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"), |
| 510 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), | 515 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| ... | @@ -788,6 +793,7 @@ pub const Type = extern union { | ... | @@ -788,6 +793,7 @@ pub const Type = extern union { |
| 788 | .@"null", | 793 | .@"null", |
| 789 | .@"undefined", | 794 | .@"undefined", |
| 790 | .enum_literal, | 795 | .enum_literal, |
| 796 | .empty_struct, | ||
| 791 | => false, | 797 | => false, |
| 792 | }; | 798 | }; |
| 793 | } | 799 | } |
| ... | @@ -910,6 +916,7 @@ pub const Type = extern union { | ... | @@ -910,6 +916,7 @@ pub const Type = extern union { |
| 910 | .@"null", | 916 | .@"null", |
| 911 | .@"undefined", | 917 | .@"undefined", |
| 912 | .enum_literal, | 918 | .enum_literal, |
| 919 | .empty_struct, | ||
| 913 | => unreachable, | 920 | => unreachable, |
| 914 | }; | 921 | }; |
| 915 | } | 922 | } |
| ... | @@ -932,6 +939,7 @@ pub const Type = extern union { | ... | @@ -932,6 +939,7 @@ pub const Type = extern union { |
| 932 | .@"undefined" => unreachable, | 939 | .@"undefined" => unreachable, |
| 933 | .enum_literal => unreachable, | 940 | .enum_literal => unreachable, |
| 934 | .single_const_pointer_to_comptime_int => unreachable, | 941 | .single_const_pointer_to_comptime_int => unreachable, |
| 942 | .empty_struct => unreachable, | ||
| 935 | 943 | ||
| 936 | .u8, | 944 | .u8, |
| 937 | .i8, | 945 | .i8, |
| ... | @@ -1107,6 +1115,7 @@ pub const Type = extern union { | ... | @@ -1107,6 +1115,7 @@ pub const Type = extern union { |
| 1107 | .anyerror_void_error_union, | 1115 | .anyerror_void_error_union, |
| 1108 | .error_set, | 1116 | .error_set, |
| 1109 | .error_set_single, | 1117 | .error_set_single, |
| 1118 | .empty_struct, | ||
| 1110 | => false, | 1119 | => false, |
| 1111 | 1120 | ||
| 1112 | .single_const_pointer, | 1121 | .single_const_pointer, |
| ... | @@ -1181,6 +1190,7 @@ pub const Type = extern union { | ... | @@ -1181,6 +1190,7 @@ pub const Type = extern union { |
| 1181 | .anyerror_void_error_union, | 1190 | .anyerror_void_error_union, |
| 1182 | .error_set, | 1191 | .error_set, |
| 1183 | .error_set_single, | 1192 | .error_set_single, |
| 1193 | .empty_struct, | ||
| 1184 | => false, | 1194 | => false, |
| 1185 | 1195 | ||
| 1186 | .const_slice, | 1196 | .const_slice, |
| ... | @@ -1252,6 +1262,7 @@ pub const Type = extern union { | ... | @@ -1252,6 +1262,7 @@ pub const Type = extern union { |
| 1252 | .anyerror_void_error_union, | 1262 | .anyerror_void_error_union, |
| 1253 | .error_set, | 1263 | .error_set, |
| 1254 | .error_set_single, | 1264 | .error_set_single, |
| 1265 | .empty_struct, | ||
| 1255 | => false, | 1266 | => false, |
| 1256 | 1267 | ||
| 1257 | .single_const_pointer, | 1268 | .single_const_pointer, |
| ... | @@ -1332,6 +1343,7 @@ pub const Type = extern union { | ... | @@ -1332,6 +1343,7 @@ pub const Type = extern union { |
| 1332 | .anyerror_void_error_union, | 1343 | .anyerror_void_error_union, |
| 1333 | .error_set, | 1344 | .error_set, |
| 1334 | .error_set_single, | 1345 | .error_set_single, |
| 1346 | .empty_struct, | ||
| 1335 | => false, | 1347 | => false, |
| 1336 | 1348 | ||
| 1337 | .pointer => { | 1349 | .pointer => { |
| ... | @@ -1407,6 +1419,7 @@ pub const Type = extern union { | ... | @@ -1407,6 +1419,7 @@ pub const Type = extern union { |
| 1407 | .anyerror_void_error_union, | 1419 | .anyerror_void_error_union, |
| 1408 | .error_set, | 1420 | .error_set, |
| 1409 | .error_set_single, | 1421 | .error_set_single, |
| 1422 | .empty_struct, | ||
| 1410 | => false, | 1423 | => false, |
| 1411 | 1424 | ||
| 1412 | .pointer => { | 1425 | .pointer => { |
| ... | @@ -1524,6 +1537,7 @@ pub const Type = extern union { | ... | @@ -1524,6 +1537,7 @@ pub const Type = extern union { |
| 1524 | .anyerror_void_error_union, | 1537 | .anyerror_void_error_union, |
| 1525 | .error_set, | 1538 | .error_set, |
| 1526 | .error_set_single, | 1539 | .error_set_single, |
| 1540 | .empty_struct, | ||
| 1527 | => unreachable, | 1541 | => unreachable, |
| 1528 | 1542 | ||
| 1529 | .array => self.cast(Payload.Array).?.elem_type, | 1543 | .array => self.cast(Payload.Array).?.elem_type, |
| ... | @@ -1651,6 +1665,7 @@ pub const Type = extern union { | ... | @@ -1651,6 +1665,7 @@ pub const Type = extern union { |
| 1651 | .anyerror_void_error_union, | 1665 | .anyerror_void_error_union, |
| 1652 | .error_set, | 1666 | .error_set, |
| 1653 | .error_set_single, | 1667 | .error_set_single, |
| 1668 | .empty_struct, | ||
| 1654 | => unreachable, | 1669 | => unreachable, |
| 1655 | 1670 | ||
| 1656 | .array => self.cast(Payload.Array).?.len, | 1671 | .array => self.cast(Payload.Array).?.len, |
| ... | @@ -1716,6 +1731,7 @@ pub const Type = extern union { | ... | @@ -1716,6 +1731,7 @@ pub const Type = extern union { |
| 1716 | .anyerror_void_error_union, | 1731 | .anyerror_void_error_union, |
| 1717 | .error_set, | 1732 | .error_set, |
| 1718 | .error_set_single, | 1733 | .error_set_single, |
| 1734 | .empty_struct, | ||
| 1719 | => unreachable, | 1735 | => unreachable, |
| 1720 | 1736 | ||
| 1721 | .single_const_pointer, | 1737 | .single_const_pointer, |
| ... | @@ -1798,6 +1814,7 @@ pub const Type = extern union { | ... | @@ -1798,6 +1814,7 @@ pub const Type = extern union { |
| 1798 | .anyerror_void_error_union, | 1814 | .anyerror_void_error_union, |
| 1799 | .error_set, | 1815 | .error_set, |
| 1800 | .error_set_single, | 1816 | .error_set_single, |
| 1817 | .empty_struct, | ||
| 1801 | => false, | 1818 | => false, |
| 1802 | 1819 | ||
| 1803 | .int_signed, | 1820 | .int_signed, |
| ... | @@ -1872,6 +1889,7 @@ pub const Type = extern union { | ... | @@ -1872,6 +1889,7 @@ pub const Type = extern union { |
| 1872 | .anyerror_void_error_union, | 1889 | .anyerror_void_error_union, |
| 1873 | .error_set, | 1890 | .error_set, |
| 1874 | .error_set_single, | 1891 | .error_set_single, |
| 1892 | .empty_struct, | ||
| 1875 | => false, | 1893 | => false, |
| 1876 | 1894 | ||
| 1877 | .int_unsigned, | 1895 | .int_unsigned, |
| ... | @@ -1936,6 +1954,7 @@ pub const Type = extern union { | ... | @@ -1936,6 +1954,7 @@ pub const Type = extern union { |
| 1936 | .anyerror_void_error_union, | 1954 | .anyerror_void_error_union, |
| 1937 | .error_set, | 1955 | .error_set, |
| 1938 | .error_set_single, | 1956 | .error_set_single, |
| 1957 | .empty_struct, | ||
| 1939 | => unreachable, | 1958 | => unreachable, |
| 1940 | 1959 | ||
| 1941 | .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, | 1960 | .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, |
| ... | @@ -2018,6 +2037,7 @@ pub const Type = extern union { | ... | @@ -2018,6 +2037,7 @@ pub const Type = extern union { |
| 2018 | .anyerror_void_error_union, | 2037 | .anyerror_void_error_union, |
| 2019 | .error_set, | 2038 | .error_set, |
| 2020 | .error_set_single, | 2039 | .error_set_single, |
| 2040 | .empty_struct, | ||
| 2021 | => false, | 2041 | => false, |
| 2022 | 2042 | ||
| 2023 | .usize, | 2043 | .usize, |
| ... | @@ -2129,6 +2149,7 @@ pub const Type = extern union { | ... | @@ -2129,6 +2149,7 @@ pub const Type = extern union { |
| 2129 | .anyerror_void_error_union, | 2149 | .anyerror_void_error_union, |
| 2130 | .error_set, | 2150 | .error_set, |
| 2131 | .error_set_single, | 2151 | .error_set_single, |
| 2152 | .empty_struct, | ||
| 2132 | => unreachable, | 2153 | => unreachable, |
| 2133 | }; | 2154 | }; |
| 2134 | } | 2155 | } |
| ... | @@ -2206,6 +2227,7 @@ pub const Type = extern union { | ... | @@ -2206,6 +2227,7 @@ pub const Type = extern union { |
| 2206 | .anyerror_void_error_union, | 2227 | .anyerror_void_error_union, |
| 2207 | .error_set, | 2228 | .error_set, |
| 2208 | .error_set_single, | 2229 | .error_set_single, |
| 2230 | .empty_struct, | ||
| 2209 | => unreachable, | 2231 | => unreachable, |
| 2210 | } | 2232 | } |
| 2211 | } | 2233 | } |
| ... | @@ -2282,6 +2304,7 @@ pub const Type = extern union { | ... | @@ -2282,6 +2304,7 @@ pub const Type = extern union { |
| 2282 | .anyerror_void_error_union, | 2304 | .anyerror_void_error_union, |
| 2283 | .error_set, | 2305 | .error_set, |
| 2284 | .error_set_single, | 2306 | .error_set_single, |
| 2307 | .empty_struct, | ||
| 2285 | => unreachable, | 2308 | => unreachable, |
| 2286 | } | 2309 | } |
| 2287 | } | 2310 | } |
| ... | @@ -2358,6 +2381,7 @@ pub const Type = extern union { | ... | @@ -2358,6 +2381,7 @@ pub const Type = extern union { |
| 2358 | .anyerror_void_error_union, | 2381 | .anyerror_void_error_union, |
| 2359 | .error_set, | 2382 | .error_set, |
| 2360 | .error_set_single, | 2383 | .error_set_single, |
| 2384 | .empty_struct, | ||
| 2361 | => unreachable, | 2385 | => unreachable, |
| 2362 | }; | 2386 | }; |
| 2363 | } | 2387 | } |
| ... | @@ -2431,6 +2455,7 @@ pub const Type = extern union { | ... | @@ -2431,6 +2455,7 @@ pub const Type = extern union { |
| 2431 | .anyerror_void_error_union, | 2455 | .anyerror_void_error_union, |
| 2432 | .error_set, | 2456 | .error_set, |
| 2433 | .error_set_single, | 2457 | .error_set_single, |
| 2458 | .empty_struct, | ||
| 2434 | => unreachable, | 2459 | => unreachable, |
| 2435 | }; | 2460 | }; |
| 2436 | } | 2461 | } |
| ... | @@ -2504,6 +2529,7 @@ pub const Type = extern union { | ... | @@ -2504,6 +2529,7 @@ pub const Type = extern union { |
| 2504 | .anyerror_void_error_union, | 2529 | .anyerror_void_error_union, |
| 2505 | .error_set, | 2530 | .error_set, |
| 2506 | .error_set_single, | 2531 | .error_set_single, |
| 2532 | .empty_struct, | ||
| 2507 | => unreachable, | 2533 | => unreachable, |
| 2508 | }; | 2534 | }; |
| 2509 | } | 2535 | } |
| ... | @@ -2577,6 +2603,7 @@ pub const Type = extern union { | ... | @@ -2577,6 +2603,7 @@ pub const Type = extern union { |
| 2577 | .anyerror_void_error_union, | 2603 | .anyerror_void_error_union, |
| 2578 | .error_set, | 2604 | .error_set, |
| 2579 | .error_set_single, | 2605 | .error_set_single, |
| 2606 | .empty_struct, | ||
| 2580 | => false, | 2607 | => false, |
| 2581 | }; | 2608 | }; |
| 2582 | } | 2609 | } |
| ... | @@ -2636,6 +2663,7 @@ pub const Type = extern union { | ... | @@ -2636,6 +2663,7 @@ pub const Type = extern union { |
| 2636 | .error_set_single, | 2663 | .error_set_single, |
| 2637 | => return null, | 2664 | => return null, |
| 2638 | 2665 | ||
| 2666 | .empty_struct => return Value.initTag(.empty_struct_value), | ||
| 2639 | .void => return Value.initTag(.void_value), | 2667 | .void => return Value.initTag(.void_value), |
| 2640 | .noreturn => return Value.initTag(.unreachable_value), | 2668 | .noreturn => return Value.initTag(.unreachable_value), |
| 2641 | .@"null" => return Value.initTag(.null_value), | 2669 | .@"null" => return Value.initTag(.null_value), |
| ... | @@ -2743,6 +2771,7 @@ pub const Type = extern union { | ... | @@ -2743,6 +2771,7 @@ pub const Type = extern union { |
| 2743 | .anyerror_void_error_union, | 2771 | .anyerror_void_error_union, |
| 2744 | .error_set, | 2772 | .error_set, |
| 2745 | .error_set_single, | 2773 | .error_set_single, |
| 2774 | .empty_struct, | ||
| 2746 | => return false, | 2775 | => return false, |
| 2747 | 2776 | ||
| 2748 | .c_const_pointer, | 2777 | .c_const_pointer, |
| ... | @@ -2760,6 +2789,80 @@ pub const Type = extern union { | ... | @@ -2760,6 +2789,80 @@ pub const Type = extern union { |
| 2760 | (self.isSinglePointer() and self.elemType().zigTypeTag() == .Array); | 2789 | (self.isSinglePointer() and self.elemType().zigTypeTag() == .Array); |
| 2761 | } | 2790 | } |
| 2762 | 2791 | ||
| 2792 | /// Asserts that the type is a container. (note: ErrorSet is not a container). | ||
| 2793 | pub fn getContainerScope(self: Type) *Module.Scope.Container { | ||
| 2794 | return switch (self.tag()) { | ||
| 2795 | .f16, | ||
| 2796 | .f32, | ||
| 2797 | .f64, | ||
| 2798 | .f128, | ||
| 2799 | .c_longdouble, | ||
| 2800 | .comptime_int, | ||
| 2801 | .comptime_float, | ||
| 2802 | .u8, | ||
| 2803 | .i8, | ||
| 2804 | .u16, | ||
| 2805 | .i16, | ||
| 2806 | .u32, | ||
| 2807 | .i32, | ||
| 2808 | .u64, | ||
| 2809 | .i64, | ||
| 2810 | .usize, | ||
| 2811 | .isize, | ||
| 2812 | .c_short, | ||
| 2813 | .c_ushort, | ||
| 2814 | .c_int, | ||
| 2815 | .c_uint, | ||
| 2816 | .c_long, | ||
| 2817 | .c_ulong, | ||
| 2818 | .c_longlong, | ||
| 2819 | .c_ulonglong, | ||
| 2820 | .bool, | ||
| 2821 | .type, | ||
| 2822 | .anyerror, | ||
| 2823 | .fn_noreturn_no_args, | ||
| 2824 | .fn_void_no_args, | ||
| 2825 | .fn_naked_noreturn_no_args, | ||
| 2826 | .fn_ccc_void_no_args, | ||
| 2827 | .function, | ||
| 2828 | .single_const_pointer_to_comptime_int, | ||
| 2829 | .const_slice_u8, | ||
| 2830 | .c_void, | ||
| 2831 | .void, | ||
| 2832 | .noreturn, | ||
| 2833 | .@"null", | ||
| 2834 | .@"undefined", | ||
| 2835 | .int_unsigned, | ||
| 2836 | .int_signed, | ||
| 2837 | .array, | ||
| 2838 | .array_sentinel, | ||
| 2839 | .array_u8, | ||
| 2840 | .array_u8_sentinel_0, | ||
| 2841 | .single_const_pointer, | ||
| 2842 | .single_mut_pointer, | ||
| 2843 | .many_const_pointer, | ||
| 2844 | .many_mut_pointer, | ||
| 2845 | .const_slice, | ||
| 2846 | .mut_slice, | ||
| 2847 | .optional, | ||
| 2848 | .optional_single_mut_pointer, | ||
| 2849 | .optional_single_const_pointer, | ||
| 2850 | .enum_literal, | ||
| 2851 | .error_union, | ||
| 2852 | .@"anyframe", | ||
| 2853 | .anyframe_T, | ||
| 2854 | .anyerror_void_error_union, | ||
| 2855 | .error_set, | ||
| 2856 | .error_set_single, | ||
| 2857 | .c_const_pointer, | ||
| 2858 | .c_mut_pointer, | ||
| 2859 | .pointer, | ||
| 2860 | => unreachable, | ||
| 2861 | |||
| 2862 | .empty_struct => self.cast(Type.Payload.EmptyStruct).?.scope, | ||
| 2863 | }; | ||
| 2864 | } | ||
| 2865 | |||
| 2763 | /// This enum does not directly correspond to `std.builtin.TypeId` because | 2866 | /// This enum does not directly correspond to `std.builtin.TypeId` because |
| 2764 | /// it has extra enum tags in it, as a way of using less memory. For example, | 2867 | /// it has extra enum tags in it, as a way of using less memory. For example, |
| 2765 | /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types | 2868 | /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types |
| ... | @@ -2835,6 +2938,7 @@ pub const Type = extern union { | ... | @@ -2835,6 +2938,7 @@ pub const Type = extern union { |
| 2835 | anyframe_T, | 2938 | anyframe_T, |
| 2836 | error_set, | 2939 | error_set, |
| 2837 | error_set_single, | 2940 | error_set_single, |
| 2941 | empty_struct, | ||
| 2838 | 2942 | ||
| 2839 | pub const last_no_payload_tag = Tag.const_slice_u8; | 2943 | pub const last_no_payload_tag = Tag.const_slice_u8; |
| 2840 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | 2944 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | @@ -2942,6 +3046,14 @@ pub const Type = extern union { | ... | @@ -2942,6 +3046,14 @@ pub const Type = extern union { |
| 2942 | /// memory is owned by `Module` | 3046 | /// memory is owned by `Module` |
| 2943 | name: []const u8, | 3047 | name: []const u8, |
| 2944 | }; | 3048 | }; |
| 3049 | |||
| 3050 | /// Mostly used for namespace like structs with zero fields. | ||
| 3051 | /// Most commonly used for files. | ||
| 3052 | pub const EmptyStruct = struct { | ||
| 3053 | base: Payload = .{ .tag = .empty_struct }, | ||
| 3054 | |||
| 3055 | scope: *Module.Scope.Container, | ||
| 3056 | }; | ||
| 2945 | }; | 3057 | }; |
| 2946 | }; | 3058 | }; |
| 2947 | 3059 |
src/value.zig+16-1| ... | @@ -68,6 +68,7 @@ pub const Value = extern union { | ... | @@ -68,6 +68,7 @@ pub const Value = extern union { |
| 68 | one, | 68 | one, |
| 69 | void_value, | 69 | void_value, |
| 70 | unreachable_value, | 70 | unreachable_value, |
| 71 | empty_struct_value, | ||
| 71 | empty_array, | 72 | empty_array, |
| 72 | null_value, | 73 | null_value, |
| 73 | bool_true, | 74 | bool_true, |
| ... | @@ -182,6 +183,7 @@ pub const Value = extern union { | ... | @@ -182,6 +183,7 @@ pub const Value = extern union { |
| 182 | .null_value, | 183 | .null_value, |
| 183 | .bool_true, | 184 | .bool_true, |
| 184 | .bool_false, | 185 | .bool_false, |
| 186 | .empty_struct_value, | ||
| 185 | => unreachable, | 187 | => unreachable, |
| 186 | 188 | ||
| 187 | .ty => { | 189 | .ty => { |
| ... | @@ -312,6 +314,8 @@ pub const Value = extern union { | ... | @@ -312,6 +314,8 @@ pub const Value = extern union { |
| 312 | .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"), | 314 | .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"), |
| 313 | .anyframe_type => return out_stream.writeAll("anyframe"), | 315 | .anyframe_type => return out_stream.writeAll("anyframe"), |
| 314 | 316 | ||
| 317 | // TODO this should print `NAME{}` | ||
| 318 | .empty_struct_value => return out_stream.writeAll("struct {}{}"), | ||
| 315 | .null_value => return out_stream.writeAll("null"), | 319 | .null_value => return out_stream.writeAll("null"), |
| 316 | .undef => return out_stream.writeAll("undefined"), | 320 | .undef => return out_stream.writeAll("undefined"), |
| 317 | .zero => return out_stream.writeAll("0"), | 321 | .zero => return out_stream.writeAll("0"), |
| ... | @@ -475,6 +479,7 @@ pub const Value = extern union { | ... | @@ -475,6 +479,7 @@ pub const Value = extern union { |
| 475 | .float_128, | 479 | .float_128, |
| 476 | .enum_literal, | 480 | .enum_literal, |
| 477 | .@"error", | 481 | .@"error", |
| 482 | .empty_struct_value, | ||
| 478 | => unreachable, | 483 | => unreachable, |
| 479 | }; | 484 | }; |
| 480 | } | 485 | } |
| ... | @@ -543,6 +548,7 @@ pub const Value = extern union { | ... | @@ -543,6 +548,7 @@ pub const Value = extern union { |
| 543 | .enum_literal, | 548 | .enum_literal, |
| 544 | .error_set, | 549 | .error_set, |
| 545 | .@"error", | 550 | .@"error", |
| 551 | .empty_struct_value, | ||
| 546 | => unreachable, | 552 | => unreachable, |
| 547 | 553 | ||
| 548 | .undef => unreachable, | 554 | .undef => unreachable, |
| ... | @@ -626,6 +632,7 @@ pub const Value = extern union { | ... | @@ -626,6 +632,7 @@ pub const Value = extern union { |
| 626 | .enum_literal, | 632 | .enum_literal, |
| 627 | .error_set, | 633 | .error_set, |
| 628 | .@"error", | 634 | .@"error", |
| 635 | .empty_struct_value, | ||
| 629 | => unreachable, | 636 | => unreachable, |
| 630 | 637 | ||
| 631 | .undef => unreachable, | 638 | .undef => unreachable, |
| ... | @@ -709,6 +716,7 @@ pub const Value = extern union { | ... | @@ -709,6 +716,7 @@ pub const Value = extern union { |
| 709 | .enum_literal, | 716 | .enum_literal, |
| 710 | .error_set, | 717 | .error_set, |
| 711 | .@"error", | 718 | .@"error", |
| 719 | .empty_struct_value, | ||
| 712 | => unreachable, | 720 | => unreachable, |
| 713 | 721 | ||
| 714 | .undef => unreachable, | 722 | .undef => unreachable, |
| ... | @@ -820,6 +828,7 @@ pub const Value = extern union { | ... | @@ -820,6 +828,7 @@ pub const Value = extern union { |
| 820 | .enum_literal, | 828 | .enum_literal, |
| 821 | .error_set, | 829 | .error_set, |
| 822 | .@"error", | 830 | .@"error", |
| 831 | .empty_struct_value, | ||
| 823 | => unreachable, | 832 | => unreachable, |
| 824 | 833 | ||
| 825 | .zero, | 834 | .zero, |
| ... | @@ -833,7 +842,7 @@ pub const Value = extern union { | ... | @@ -833,7 +842,7 @@ pub const Value = extern union { |
| 833 | .int_u64 => { | 842 | .int_u64 => { |
| 834 | const x = self.cast(Payload.Int_u64).?.int; | 843 | const x = self.cast(Payload.Int_u64).?.int; |
| 835 | if (x == 0) return 0; | 844 | if (x == 0) return 0; |
| 836 | return std.math.log2(x) + 1; | 845 | return @intCast(usize, std.math.log2(x) + 1); |
| 837 | }, | 846 | }, |
| 838 | .int_i64 => { | 847 | .int_i64 => { |
| 839 | @panic("TODO implement i64 intBitCountTwosComp"); | 848 | @panic("TODO implement i64 intBitCountTwosComp"); |
| ... | @@ -907,6 +916,7 @@ pub const Value = extern union { | ... | @@ -907,6 +916,7 @@ pub const Value = extern union { |
| 907 | .enum_literal, | 916 | .enum_literal, |
| 908 | .error_set, | 917 | .error_set, |
| 909 | .@"error", | 918 | .@"error", |
| 919 | .empty_struct_value, | ||
| 910 | => unreachable, | 920 | => unreachable, |
| 911 | 921 | ||
| 912 | .zero, | 922 | .zero, |
| ... | @@ -1078,6 +1088,7 @@ pub const Value = extern union { | ... | @@ -1078,6 +1088,7 @@ pub const Value = extern union { |
| 1078 | .enum_literal, | 1088 | .enum_literal, |
| 1079 | .error_set, | 1089 | .error_set, |
| 1080 | .@"error", | 1090 | .@"error", |
| 1091 | .empty_struct_value, | ||
| 1081 | => unreachable, | 1092 | => unreachable, |
| 1082 | 1093 | ||
| 1083 | .zero, | 1094 | .zero, |
| ... | @@ -1152,6 +1163,7 @@ pub const Value = extern union { | ... | @@ -1152,6 +1163,7 @@ pub const Value = extern union { |
| 1152 | .enum_literal, | 1163 | .enum_literal, |
| 1153 | .error_set, | 1164 | .error_set, |
| 1154 | .@"error", | 1165 | .@"error", |
| 1166 | .empty_struct_value, | ||
| 1155 | => unreachable, | 1167 | => unreachable, |
| 1156 | 1168 | ||
| 1157 | .zero, | 1169 | .zero, |
| ... | @@ -1300,6 +1312,7 @@ pub const Value = extern union { | ... | @@ -1300,6 +1312,7 @@ pub const Value = extern union { |
| 1300 | .enum_literal, | 1312 | .enum_literal, |
| 1301 | .error_set, | 1313 | .error_set, |
| 1302 | .@"error", | 1314 | .@"error", |
| 1315 | .empty_struct_value, | ||
| 1303 | => unreachable, | 1316 | => unreachable, |
| 1304 | 1317 | ||
| 1305 | .ref_val => self.cast(Payload.RefVal).?.val, | 1318 | .ref_val => self.cast(Payload.RefVal).?.val, |
| ... | @@ -1383,6 +1396,7 @@ pub const Value = extern union { | ... | @@ -1383,6 +1396,7 @@ pub const Value = extern union { |
| 1383 | .enum_literal, | 1396 | .enum_literal, |
| 1384 | .error_set, | 1397 | .error_set, |
| 1385 | .@"error", | 1398 | .@"error", |
| 1399 | .empty_struct_value, | ||
| 1386 | => unreachable, | 1400 | => unreachable, |
| 1387 | 1401 | ||
| 1388 | .empty_array => unreachable, // out of bounds array index | 1402 | .empty_array => unreachable, // out of bounds array index |
| ... | @@ -1483,6 +1497,7 @@ pub const Value = extern union { | ... | @@ -1483,6 +1497,7 @@ pub const Value = extern union { |
| 1483 | .enum_literal, | 1497 | .enum_literal, |
| 1484 | .error_set, | 1498 | .error_set, |
| 1485 | .@"error", | 1499 | .@"error", |
| 1500 | .empty_struct_value, | ||
| 1486 | => false, | 1501 | => false, |
| 1487 | 1502 | ||
| 1488 | .undef => unreachable, | 1503 | .undef => unreachable, |
src/zig_llvm.cpp+28| ... | @@ -1126,6 +1126,34 @@ LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp | ... | @@ -1126,6 +1126,34 @@ LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp |
| 1126 | singleThread ? SyncScope::SingleThread : SyncScope::System)); | 1126 | singleThread ? SyncScope::SingleThread : SyncScope::System)); |
| 1127 | } | 1127 | } |
| 1128 | 1128 | ||
| 1129 | LLVMValueRef ZigLLVMBuildAndReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ||
| 1130 | return wrap(unwrap(B)->CreateAndReduce(unwrap(Val))); | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | LLVMValueRef ZigLLVMBuildOrReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ||
| 1134 | return wrap(unwrap(B)->CreateOrReduce(unwrap(Val))); | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | LLVMValueRef ZigLLVMBuildXorReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ||
| 1138 | return wrap(unwrap(B)->CreateXorReduce(unwrap(Val))); | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | LLVMValueRef ZigLLVMBuildIntMaxReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed) { | ||
| 1142 | return wrap(unwrap(B)->CreateIntMaxReduce(unwrap(Val), is_signed)); | ||
| 1143 | } | ||
| 1144 | |||
| 1145 | LLVMValueRef ZigLLVMBuildIntMinReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed) { | ||
| 1146 | return wrap(unwrap(B)->CreateIntMinReduce(unwrap(Val), is_signed)); | ||
| 1147 | } | ||
| 1148 | |||
| 1149 | LLVMValueRef ZigLLVMBuildFPMaxReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ||
| 1150 | return wrap(unwrap(B)->CreateFPMaxReduce(unwrap(Val))); | ||
| 1151 | } | ||
| 1152 | |||
| 1153 | LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val) { | ||
| 1154 | return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Val))); | ||
| 1155 | } | ||
| 1156 | |||
| 1129 | static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); | 1157 | static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, ""); |
| 1130 | static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); | 1158 | static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, ""); |
| 1131 | static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); | 1159 | static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, ""); |
src/zig_llvm.h+8| ... | @@ -455,6 +455,14 @@ LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp | ... | @@ -455,6 +455,14 @@ LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp |
| 455 | LLVMValueRef PTR, LLVMValueRef Val, | 455 | LLVMValueRef PTR, LLVMValueRef Val, |
| 456 | LLVMAtomicOrdering ordering, LLVMBool singleThread); | 456 | LLVMAtomicOrdering ordering, LLVMBool singleThread); |
| 457 | 457 | ||
| 458 | LLVMValueRef ZigLLVMBuildAndReduce(LLVMBuilderRef B, LLVMValueRef Val); | ||
| 459 | LLVMValueRef ZigLLVMBuildOrReduce(LLVMBuilderRef B, LLVMValueRef Val); | ||
| 460 | LLVMValueRef ZigLLVMBuildXorReduce(LLVMBuilderRef B, LLVMValueRef Val); | ||
| 461 | LLVMValueRef ZigLLVMBuildIntMaxReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed); | ||
| 462 | LLVMValueRef ZigLLVMBuildIntMinReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed); | ||
| 463 | LLVMValueRef ZigLLVMBuildFPMaxReduce(LLVMBuilderRef B, LLVMValueRef Val); | ||
| 464 | LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val); | ||
| 465 | |||
| 458 | #define ZigLLVM_DIFlags_Zero 0U | 466 | #define ZigLLVM_DIFlags_Zero 0U |
| 459 | #define ZigLLVM_DIFlags_Private 1U | 467 | #define ZigLLVM_DIFlags_Private 1U |
| 460 | #define ZigLLVM_DIFlags_Protected 2U | 468 | #define ZigLLVM_DIFlags_Protected 2U |
src/zir.zig+4| ... | @@ -161,6 +161,8 @@ pub const Inst = struct { | ... | @@ -161,6 +161,8 @@ pub const Inst = struct { |
| 161 | @"fn", | 161 | @"fn", |
| 162 | /// Returns a function type. | 162 | /// Returns a function type. |
| 163 | fntype, | 163 | fntype, |
| 164 | /// @import(operand) | ||
| 165 | import, | ||
| 164 | /// Integer literal. | 166 | /// Integer literal. |
| 165 | int, | 167 | int, |
| 166 | /// Convert an integer value to another integer type, asserting that the destination type | 168 | /// Convert an integer value to another integer type, asserting that the destination type |
| ... | @@ -315,6 +317,7 @@ pub const Inst = struct { | ... | @@ -315,6 +317,7 @@ pub const Inst = struct { |
| 315 | .ensure_err_payload_void, | 317 | .ensure_err_payload_void, |
| 316 | .anyframe_type, | 318 | .anyframe_type, |
| 317 | .bitnot, | 319 | .bitnot, |
| 320 | .import, | ||
| 318 | => UnOp, | 321 | => UnOp, |
| 319 | 322 | ||
| 320 | .add, | 323 | .add, |
| ... | @@ -489,6 +492,7 @@ pub const Inst = struct { | ... | @@ -489,6 +492,7 @@ pub const Inst = struct { |
| 489 | .error_set, | 492 | .error_set, |
| 490 | .slice, | 493 | .slice, |
| 491 | .slice_start, | 494 | .slice_start, |
| 495 | .import, | ||
| 492 | => false, | 496 | => false, |
| 493 | 497 | ||
| 494 | .@"break", | 498 | .@"break", |
src/zir_sema.zig+32| ... | @@ -134,6 +134,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! | ... | @@ -134,6 +134,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 134 | .error_set => return analyzeInstErrorSet(mod, scope, old_inst.castTag(.error_set).?), | 134 | .error_set => return analyzeInstErrorSet(mod, scope, old_inst.castTag(.error_set).?), |
| 135 | .slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?), | 135 | .slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?), |
| 136 | .slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?), | 136 | .slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?), |
| 137 | .import => return analyzeInstImport(mod, scope, old_inst.castTag(.import).?), | ||
| 137 | } | 138 | } |
| 138 | } | 139 | } |
| 139 | 140 | ||
| ... | @@ -1047,6 +1048,19 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr | ... | @@ -1047,6 +1048,19 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr |
| 1047 | .val = Value.initPayload(&ref_payload.base), | 1048 | .val = Value.initPayload(&ref_payload.base), |
| 1048 | }); | 1049 | }); |
| 1049 | }, | 1050 | }, |
| 1051 | .Struct => { | ||
| 1052 | const container_scope = child_type.getContainerScope(); | ||
| 1053 | if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| { | ||
| 1054 | // TODO if !decl.is_pub and inDifferentFiles() "{} is private" | ||
| 1055 | return mod.analyzeDeclRef(scope, fieldptr.base.src, decl); | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | if (&container_scope.file_scope.base == mod.root_scope) { | ||
| 1059 | return mod.fail(scope, fieldptr.base.src, "root source file has no member called '{}'", .{field_name}); | ||
| 1060 | } else { | ||
| 1061 | return mod.fail(scope, fieldptr.base.src, "container '{}' has no member called '{}'", .{ child_type, field_name }); | ||
| 1062 | } | ||
| 1063 | }, | ||
| 1050 | else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{child_type}), | 1064 | else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{child_type}), |
| 1051 | } | 1065 | } |
| 1052 | }, | 1066 | }, |
| ... | @@ -1190,6 +1204,24 @@ fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn | ... | @@ -1190,6 +1204,24 @@ fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn |
| 1190 | return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null); | 1204 | return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null); |
| 1191 | } | 1205 | } |
| 1192 | 1206 | ||
| 1207 | fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | ||
| 1208 | const operand = try resolveConstString(mod, scope, inst.positionals.operand); | ||
| 1209 | |||
| 1210 | const file_scope = mod.analyzeImport(scope, inst.base.src, operand) catch |err| switch (err) { | ||
| 1211 | // error.ImportOutsidePkgPath => { | ||
| 1212 | // return mod.fail(scope, inst.base.src, "import of file outside package path: '{}'", .{operand}); | ||
| 1213 | // }, | ||
| 1214 | error.FileNotFound => { | ||
| 1215 | return mod.fail(scope, inst.base.src, "unable to find '{}'", .{operand}); | ||
| 1216 | }, | ||
| 1217 | else => { | ||
| 1218 | // TODO user friendly error to string | ||
| 1219 | return mod.fail(scope, inst.base.src, "unable to open '{}': {}", .{ operand, @errorName(err) }); | ||
| 1220 | }, | ||
| 1221 | }; | ||
| 1222 | return mod.constType(scope, inst.base.src, file_scope.root_container.ty); | ||
| 1223 | } | ||
| 1224 | |||
| 1193 | fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { | 1225 | fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { |
| 1194 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{}); | 1226 | return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{}); |
| 1195 | } | 1227 | } |
test/compile_errors.zig+182-47| ... | @@ -2,20 +2,175 @@ const tests = @import("tests.zig"); | ... | @@ -2,20 +2,175 @@ const tests = @import("tests.zig"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add("slice sentinel mismatch", | 5 | cases.add("@Type for exhaustive enum with undefined tag type", |
| 6 | \\const TypeInfo = @import("builtin").TypeInfo; | ||
| 7 | \\const Tag = @Type(.{ | ||
| 8 | \\ .Enum = .{ | ||
| 9 | \\ .layout = .Auto, | ||
| 10 | \\ .tag_type = undefined, | ||
| 11 | \\ .fields = &[_]TypeInfo.EnumField{}, | ||
| 12 | \\ .decls = &[_]TypeInfo.Declaration{}, | ||
| 13 | \\ .is_exhaustive = false, | ||
| 14 | \\ }, | ||
| 15 | \\}); | ||
| 6 | \\export fn entry() void { | 16 | \\export fn entry() void { |
| 7 | \\ const x = @import("std").meta.Vector(3, f32){ 25, 75, 5, 0 }; | 17 | \\ _ = @intToEnum(Tag, 0); |
| 8 | \\} | 18 | \\} |
| 9 | , &[_][]const u8{ | 19 | , &[_][]const u8{ |
| 10 | "tmp.zig:2:62: error: index 3 outside vector of size 3", | 20 | "tmp.zig:2:20: error: use of undefined value here causes undefined behavior", |
| 11 | }); | 21 | }); |
| 12 | 22 | ||
| 13 | cases.add("slice sentinel mismatch", | 23 | cases.add("extern struct with non-extern-compatible integer tag type", |
| 24 | \\pub const E = enum(u31) { A, B, C }; | ||
| 25 | \\pub const S = extern struct { | ||
| 26 | \\ e: E, | ||
| 27 | \\}; | ||
| 14 | \\export fn entry() void { | 28 | \\export fn entry() void { |
| 15 | \\ const y: [:1]const u8 = &[_:2]u8{ 1, 2 }; | 29 | \\ const s: S = undefined; |
| 16 | \\} | 30 | \\} |
| 17 | , &[_][]const u8{ | 31 | , &[_][]const u8{ |
| 18 | "tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8'", | 32 | "tmp.zig:3:5: error: extern structs cannot contain fields of type 'E'", |
| 33 | }); | ||
| 34 | |||
| 35 | cases.add("@Type for exhaustive enum with non-integer tag type", | ||
| 36 | \\const TypeInfo = @import("builtin").TypeInfo; | ||
| 37 | \\const Tag = @Type(.{ | ||
| 38 | \\ .Enum = .{ | ||
| 39 | \\ .layout = .Auto, | ||
| 40 | \\ .tag_type = bool, | ||
| 41 | \\ .fields = &[_]TypeInfo.EnumField{}, | ||
| 42 | \\ .decls = &[_]TypeInfo.Declaration{}, | ||
| 43 | \\ .is_exhaustive = false, | ||
| 44 | \\ }, | ||
| 45 | \\}); | ||
| 46 | \\export fn entry() void { | ||
| 47 | \\ _ = @intToEnum(Tag, 0); | ||
| 48 | \\} | ||
| 49 | , &[_][]const u8{ | ||
| 50 | "tmp.zig:2:20: error: TypeInfo.Enum.tag_type must be an integer type, not 'bool'", | ||
| 51 | }); | ||
| 52 | |||
| 53 | cases.add("extern struct with extern-compatible but inferred integer tag type", | ||
| 54 | \\pub const E = enum { | ||
| 55 | \\@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12", | ||
| 56 | \\@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20",@"21",@"22",@"23", | ||
| 57 | \\@"24",@"25",@"26",@"27",@"28",@"29",@"30",@"31",@"32",@"33",@"34", | ||
| 58 | \\@"35",@"36",@"37",@"38",@"39",@"40",@"41",@"42",@"43",@"44",@"45", | ||
| 59 | \\@"46",@"47",@"48",@"49",@"50",@"51",@"52",@"53",@"54",@"55",@"56", | ||
| 60 | \\@"57",@"58",@"59",@"60",@"61",@"62",@"63",@"64",@"65",@"66",@"67", | ||
| 61 | \\@"68",@"69",@"70",@"71",@"72",@"73",@"74",@"75",@"76",@"77",@"78", | ||
| 62 | \\@"79",@"80",@"81",@"82",@"83",@"84",@"85",@"86",@"87",@"88",@"89", | ||
| 63 | \\@"90",@"91",@"92",@"93",@"94",@"95",@"96",@"97",@"98",@"99",@"100", | ||
| 64 | \\@"101",@"102",@"103",@"104",@"105",@"106",@"107",@"108",@"109", | ||
| 65 | \\@"110",@"111",@"112",@"113",@"114",@"115",@"116",@"117",@"118", | ||
| 66 | \\@"119",@"120",@"121",@"122",@"123",@"124",@"125",@"126",@"127", | ||
| 67 | \\@"128",@"129",@"130",@"131",@"132",@"133",@"134",@"135",@"136", | ||
| 68 | \\@"137",@"138",@"139",@"140",@"141",@"142",@"143",@"144",@"145", | ||
| 69 | \\@"146",@"147",@"148",@"149",@"150",@"151",@"152",@"153",@"154", | ||
| 70 | \\@"155",@"156",@"157",@"158",@"159",@"160",@"161",@"162",@"163", | ||
| 71 | \\@"164",@"165",@"166",@"167",@"168",@"169",@"170",@"171",@"172", | ||
| 72 | \\@"173",@"174",@"175",@"176",@"177",@"178",@"179",@"180",@"181", | ||
| 73 | \\@"182",@"183",@"184",@"185",@"186",@"187",@"188",@"189",@"190", | ||
| 74 | \\@"191",@"192",@"193",@"194",@"195",@"196",@"197",@"198",@"199", | ||
| 75 | \\@"200",@"201",@"202",@"203",@"204",@"205",@"206",@"207",@"208", | ||
| 76 | \\@"209",@"210",@"211",@"212",@"213",@"214",@"215",@"216",@"217", | ||
| 77 | \\@"218",@"219",@"220",@"221",@"222",@"223",@"224",@"225",@"226", | ||
| 78 | \\@"227",@"228",@"229",@"230",@"231",@"232",@"233",@"234",@"235", | ||
| 79 | \\@"236",@"237",@"238",@"239",@"240",@"241",@"242",@"243",@"244", | ||
| 80 | \\@"245",@"246",@"247",@"248",@"249",@"250",@"251",@"252",@"253", | ||
| 81 | \\@"254",@"255" | ||
| 82 | \\}; | ||
| 83 | \\pub const S = extern struct { | ||
| 84 | \\ e: E, | ||
| 85 | \\}; | ||
| 86 | \\export fn entry() void { | ||
| 87 | \\ if (@TagType(E) != u8) @compileError("did not infer u8 tag type"); | ||
| 88 | \\ const s: S = undefined; | ||
| 89 | \\} | ||
| 90 | , &[_][]const u8{ | ||
| 91 | "tmp.zig:31:5: error: extern structs cannot contain fields of type 'E'", | ||
| 92 | }); | ||
| 93 | |||
| 94 | cases.add("@Type for tagged union with extra enum field", | ||
| 95 | \\const TypeInfo = @import("builtin").TypeInfo; | ||
| 96 | \\const Tag = @Type(.{ | ||
| 97 | \\ .Enum = .{ | ||
| 98 | \\ .layout = .Auto, | ||
| 99 | \\ .tag_type = u2, | ||
| 100 | \\ .fields = &[_]TypeInfo.EnumField{ | ||
| 101 | \\ .{ .name = "signed", .value = 0 }, | ||
| 102 | \\ .{ .name = "unsigned", .value = 1 }, | ||
| 103 | \\ .{ .name = "arst", .value = 2 }, | ||
| 104 | \\ }, | ||
| 105 | \\ .decls = &[_]TypeInfo.Declaration{}, | ||
| 106 | \\ .is_exhaustive = true, | ||
| 107 | \\ }, | ||
| 108 | \\}); | ||
| 109 | \\const Tagged = @Type(.{ | ||
| 110 | \\ .Union = .{ | ||
| 111 | \\ .layout = .Auto, | ||
| 112 | \\ .tag_type = Tag, | ||
| 113 | \\ .fields = &[_]TypeInfo.UnionField{ | ||
| 114 | \\ .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, | ||
| 115 | \\ .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, | ||
| 116 | \\ }, | ||
| 117 | \\ .decls = &[_]TypeInfo.Declaration{}, | ||
| 118 | \\ }, | ||
| 119 | \\}); | ||
| 120 | \\export fn entry() void { | ||
| 121 | \\ var tagged = Tagged{ .signed = -1 }; | ||
| 122 | \\ tagged = .{ .unsigned = 1 }; | ||
| 123 | \\} | ||
| 124 | , &[_][]const u8{ | ||
| 125 | "tmp.zig:15:23: error: enum field missing: 'arst'", | ||
| 126 | "tmp.zig:27:24: note: referenced here", | ||
| 127 | }); | ||
| 128 | cases.add("@Type(.Fn) with is_generic = true", | ||
| 129 | \\const Foo = @Type(.{ | ||
| 130 | \\ .Fn = .{ | ||
| 131 | \\ .calling_convention = .Unspecified, | ||
| 132 | \\ .alignment = 0, | ||
| 133 | \\ .is_generic = true, | ||
| 134 | \\ .is_var_args = false, | ||
| 135 | \\ .return_type = u0, | ||
| 136 | \\ .args = &[_]@import("builtin").TypeInfo.FnArg{}, | ||
| 137 | \\ }, | ||
| 138 | \\}); | ||
| 139 | \\comptime { _ = Foo; } | ||
| 140 | , &[_][]const u8{ | ||
| 141 | "tmp.zig:1:20: error: TypeInfo.Fn.is_generic must be false for @Type", | ||
| 142 | }); | ||
| 143 | |||
| 144 | cases.add("@Type(.Fn) with is_var_args = true and non-C callconv", | ||
| 145 | \\const Foo = @Type(.{ | ||
| 146 | \\ .Fn = .{ | ||
| 147 | \\ .calling_convention = .Unspecified, | ||
| 148 | \\ .alignment = 0, | ||
| 149 | \\ .is_generic = false, | ||
| 150 | \\ .is_var_args = true, | ||
| 151 | \\ .return_type = u0, | ||
| 152 | \\ .args = &[_]@import("builtin").TypeInfo.FnArg{}, | ||
| 153 | \\ }, | ||
| 154 | \\}); | ||
| 155 | \\comptime { _ = Foo; } | ||
| 156 | , &[_][]const u8{ | ||
| 157 | "tmp.zig:1:20: error: varargs functions must have C calling convention", | ||
| 158 | }); | ||
| 159 | |||
| 160 | cases.add("@Type(.Fn) with return_type = null", | ||
| 161 | \\const Foo = @Type(.{ | ||
| 162 | \\ .Fn = .{ | ||
| 163 | \\ .calling_convention = .Unspecified, | ||
| 164 | \\ .alignment = 0, | ||
| 165 | \\ .is_generic = false, | ||
| 166 | \\ .is_var_args = false, | ||
| 167 | \\ .return_type = null, | ||
| 168 | \\ .args = &[_]@import("builtin").TypeInfo.FnArg{}, | ||
| 169 | \\ }, | ||
| 170 | \\}); | ||
| 171 | \\comptime { _ = Foo; } | ||
| 172 | , &[_][]const u8{ | ||
| 173 | "tmp.zig:1:20: error: TypeInfo.Fn.return_type must be non-null for @Type", | ||
| 19 | }); | 174 | }); |
| 20 | 175 | ||
| 21 | cases.add("@Type for union with opaque field", | 176 | cases.add("@Type for union with opaque field", |
| ... | @@ -25,7 +180,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -25,7 +180,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 25 | \\ .layout = .Auto, | 180 | \\ .layout = .Auto, |
| 26 | \\ .tag_type = null, | 181 | \\ .tag_type = null, |
| 27 | \\ .fields = &[_]TypeInfo.UnionField{ | 182 | \\ .fields = &[_]TypeInfo.UnionField{ |
| 28 | \\ .{ .name = "foo", .field_type = @Type(.Opaque) }, | 183 | \\ .{ .name = "foo", .field_type = @Type(.Opaque), .alignment = 1 }, |
| 29 | \\ }, | 184 | \\ }, |
| 30 | \\ .decls = &[_]TypeInfo.Declaration{}, | 185 | \\ .decls = &[_]TypeInfo.Declaration{}, |
| 31 | \\ }, | 186 | \\ }, |
| ... | @@ -38,6 +193,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -38,6 +193,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 38 | "tmp.zig:13:17: note: referenced here", | 193 | "tmp.zig:13:17: note: referenced here", |
| 39 | }); | 194 | }); |
| 40 | 195 | ||
| 196 | cases.add("slice sentinel mismatch", | ||
| 197 | \\export fn entry() void { | ||
| 198 | \\ const x = @import("std").meta.Vector(3, f32){ 25, 75, 5, 0 }; | ||
| 199 | \\} | ||
| 200 | , &[_][]const u8{ | ||
| 201 | "tmp.zig:2:62: error: index 3 outside vector of size 3", | ||
| 202 | }); | ||
| 203 | |||
| 204 | cases.add("slice sentinel mismatch", | ||
| 205 | \\export fn entry() void { | ||
| 206 | \\ const y: [:1]const u8 = &[_:2]u8{ 1, 2 }; | ||
| 207 | \\} | ||
| 208 | , &[_][]const u8{ | ||
| 209 | "tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8'", | ||
| 210 | }); | ||
| 211 | |||
| 41 | cases.add("@Type for union with zero fields", | 212 | cases.add("@Type for union with zero fields", |
| 42 | \\const TypeInfo = @import("builtin").TypeInfo; | 213 | \\const TypeInfo = @import("builtin").TypeInfo; |
| 43 | \\const Untagged = @Type(.{ | 214 | \\const Untagged = @Type(.{ |
| ... | @@ -94,9 +265,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -94,9 +265,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 94 | \\ .layout = .Auto, | 265 | \\ .layout = .Auto, |
| 95 | \\ .tag_type = Tag, | 266 | \\ .tag_type = Tag, |
| 96 | \\ .fields = &[_]TypeInfo.UnionField{ | 267 | \\ .fields = &[_]TypeInfo.UnionField{ |
| 97 | \\ .{ .name = "signed", .field_type = i32 }, | 268 | \\ .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, |
| 98 | \\ .{ .name = "unsigned", .field_type = u32 }, | 269 | \\ .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, |
| 99 | \\ .{ .name = "arst", .field_type = f32 }, | 270 | \\ .{ .name = "arst", .field_type = f32, .alignment = @alignOf(f32) }, |
| 100 | \\ }, | 271 | \\ }, |
| 101 | \\ .decls = &[_]TypeInfo.Declaration{}, | 272 | \\ .decls = &[_]TypeInfo.Declaration{}, |
| 102 | \\ }, | 273 | \\ }, |
| ... | @@ -111,42 +282,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -111,42 +282,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 111 | "tmp.zig:27:24: note: referenced here", | 282 | "tmp.zig:27:24: note: referenced here", |
| 112 | }); | 283 | }); |
| 113 | 284 | ||
| 114 | cases.add("@Type for tagged union with extra enum field", | ||
| 115 | \\const TypeInfo = @import("builtin").TypeInfo; | ||
| 116 | \\const Tag = @Type(.{ | ||
| 117 | \\ .Enum = .{ | ||
| 118 | \\ .layout = .Auto, | ||
| 119 | \\ .tag_type = u2, | ||
| 120 | \\ .fields = &[_]TypeInfo.EnumField{ | ||
| 121 | \\ .{ .name = "signed", .value = 0 }, | ||
| 122 | \\ .{ .name = "unsigned", .value = 1 }, | ||
| 123 | \\ .{ .name = "arst", .field_type = 2 }, | ||
| 124 | \\ }, | ||
| 125 | \\ .decls = &[_]TypeInfo.Declaration{}, | ||
| 126 | \\ .is_exhaustive = true, | ||
| 127 | \\ }, | ||
| 128 | \\}); | ||
| 129 | \\const Tagged = @Type(.{ | ||
| 130 | \\ .Union = .{ | ||
| 131 | \\ .layout = .Auto, | ||
| 132 | \\ .tag_type = Tag, | ||
| 133 | \\ .fields = &[_]TypeInfo.UnionField{ | ||
| 134 | \\ .{ .name = "signed", .field_type = i32 }, | ||
| 135 | \\ .{ .name = "unsigned", .field_type = u32 }, | ||
| 136 | \\ }, | ||
| 137 | \\ .decls = &[_]TypeInfo.Declaration{}, | ||
| 138 | \\ }, | ||
| 139 | \\}); | ||
| 140 | \\export fn entry() void { | ||
| 141 | \\ var tagged = Tagged{ .signed = -1 }; | ||
| 142 | \\ tagged = .{ .unsigned = 1 }; | ||
| 143 | \\} | ||
| 144 | , &[_][]const u8{ | ||
| 145 | "tmp.zig:9:32: error: no member named 'field_type' in struct 'std.builtin.EnumField'", | ||
| 146 | "tmp.zig:18:21: note: referenced here", | ||
| 147 | "tmp.zig:27:18: note: referenced here", | ||
| 148 | }); | ||
| 149 | |||
| 150 | cases.add("@Type with undefined", | 285 | cases.add("@Type with undefined", |
| 151 | \\comptime { | 286 | \\comptime { |
| 152 | \\ _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } }); | 287 | \\ _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } }); |
| ... | @@ -7556,7 +7691,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -7556,7 +7691,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 7556 | }); | 7691 | }); |
| 7557 | 7692 | ||
| 7558 | cases.add( // fixed bug #2032 | 7693 | cases.add( // fixed bug #2032 |
| 7559 | "compile diagnostic string for top level decl type", | 7694 | "compile diagnostic string for top level decl type", |
| 7560 | \\export fn entry() void { | 7695 | \\export fn entry() void { |
| 7561 | \\ var foo: u32 = @This(){}; | 7696 | \\ var foo: u32 = @This(){}; |
| 7562 | \\} | 7697 | \\} |
test/stage1/behavior/bugs/1467.zig created+7| ... | @@ -0,0 +1,7 @@ | ||
| 1 | pub const E = enum(u32) { A, B, C }; | ||
| 2 | pub const S = extern struct { | ||
| 3 | e: E, | ||
| 4 | }; | ||
| 5 | test "bug 1467" { | ||
| 6 | const s: S = undefined; | ||
| 7 | } | ||
test/stage1/behavior/type.zig+35-8| ... | @@ -320,8 +320,8 @@ test "Type.Union" { | ... | @@ -320,8 +320,8 @@ test "Type.Union" { |
| 320 | .layout = .Auto, | 320 | .layout = .Auto, |
| 321 | .tag_type = null, | 321 | .tag_type = null, |
| 322 | .fields = &[_]TypeInfo.UnionField{ | 322 | .fields = &[_]TypeInfo.UnionField{ |
| 323 | .{ .name = "int", .field_type = i32 }, | 323 | .{ .name = "int", .field_type = i32, .alignment = @alignOf(f32) }, |
| 324 | .{ .name = "float", .field_type = f32 }, | 324 | .{ .name = "float", .field_type = f32, .alignment = @alignOf(f32) }, |
| 325 | }, | 325 | }, |
| 326 | .decls = &[_]TypeInfo.Declaration{}, | 326 | .decls = &[_]TypeInfo.Declaration{}, |
| 327 | }, | 327 | }, |
| ... | @@ -336,8 +336,8 @@ test "Type.Union" { | ... | @@ -336,8 +336,8 @@ test "Type.Union" { |
| 336 | .layout = .Packed, | 336 | .layout = .Packed, |
| 337 | .tag_type = null, | 337 | .tag_type = null, |
| 338 | .fields = &[_]TypeInfo.UnionField{ | 338 | .fields = &[_]TypeInfo.UnionField{ |
| 339 | .{ .name = "signed", .field_type = i32 }, | 339 | .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, |
| 340 | .{ .name = "unsigned", .field_type = u32 }, | 340 | .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, |
| 341 | }, | 341 | }, |
| 342 | .decls = &[_]TypeInfo.Declaration{}, | 342 | .decls = &[_]TypeInfo.Declaration{}, |
| 343 | }, | 343 | }, |
| ... | @@ -363,8 +363,8 @@ test "Type.Union" { | ... | @@ -363,8 +363,8 @@ test "Type.Union" { |
| 363 | .layout = .Auto, | 363 | .layout = .Auto, |
| 364 | .tag_type = Tag, | 364 | .tag_type = Tag, |
| 365 | .fields = &[_]TypeInfo.UnionField{ | 365 | .fields = &[_]TypeInfo.UnionField{ |
| 366 | .{ .name = "signed", .field_type = i32 }, | 366 | .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) }, |
| 367 | .{ .name = "unsigned", .field_type = u32 }, | 367 | .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) }, |
| 368 | }, | 368 | }, |
| 369 | .decls = &[_]TypeInfo.Declaration{}, | 369 | .decls = &[_]TypeInfo.Declaration{}, |
| 370 | }, | 370 | }, |
| ... | @@ -392,7 +392,7 @@ test "Type.Union from Type.Enum" { | ... | @@ -392,7 +392,7 @@ test "Type.Union from Type.Enum" { |
| 392 | .layout = .Auto, | 392 | .layout = .Auto, |
| 393 | .tag_type = Tag, | 393 | .tag_type = Tag, |
| 394 | .fields = &[_]TypeInfo.UnionField{ | 394 | .fields = &[_]TypeInfo.UnionField{ |
| 395 | .{ .name = "working_as_expected", .field_type = u32 }, | 395 | .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) }, |
| 396 | }, | 396 | }, |
| 397 | .decls = &[_]TypeInfo.Declaration{}, | 397 | .decls = &[_]TypeInfo.Declaration{}, |
| 398 | }, | 398 | }, |
| ... | @@ -408,7 +408,7 @@ test "Type.Union from regular enum" { | ... | @@ -408,7 +408,7 @@ test "Type.Union from regular enum" { |
| 408 | .layout = .Auto, | 408 | .layout = .Auto, |
| 409 | .tag_type = E, | 409 | .tag_type = E, |
| 410 | .fields = &[_]TypeInfo.UnionField{ | 410 | .fields = &[_]TypeInfo.UnionField{ |
| 411 | .{ .name = "working_as_expected", .field_type = u32 }, | 411 | .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) }, |
| 412 | }, | 412 | }, |
| 413 | .decls = &[_]TypeInfo.Declaration{}, | 413 | .decls = &[_]TypeInfo.Declaration{}, |
| 414 | }, | 414 | }, |
| ... | @@ -416,3 +416,30 @@ test "Type.Union from regular enum" { | ... | @@ -416,3 +416,30 @@ test "Type.Union from regular enum" { |
| 416 | _ = T; | 416 | _ = T; |
| 417 | _ = @typeInfo(T).Union; | 417 | _ = @typeInfo(T).Union; |
| 418 | } | 418 | } |
| 419 | |||
| 420 | test "Type.Fn" { | ||
| 421 | // wasm doesn't support align attributes on functions | ||
| 422 | if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest; | ||
| 423 | |||
| 424 | const foo = struct { | ||
| 425 | fn func(a: usize, b: bool) align(4) callconv(.C) usize { | ||
| 426 | return 0; | ||
| 427 | } | ||
| 428 | }.func; | ||
| 429 | const Foo = @Type(@typeInfo(@TypeOf(foo))); | ||
| 430 | const foo_2: Foo = foo; | ||
| 431 | } | ||
| 432 | |||
| 433 | test "Type.BoundFn" { | ||
| 434 | // wasm doesn't support align attributes on functions | ||
| 435 | if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest; | ||
| 436 | |||
| 437 | const TestStruct = packed struct { | ||
| 438 | pub fn foo(self: *const @This()) align(4) callconv(.Unspecified) void {} | ||
| 439 | }; | ||
| 440 | const test_instance: TestStruct = undefined; | ||
| 441 | testing.expect(std.meta.eql( | ||
| 442 | @typeName(@TypeOf(test_instance.foo)), | ||
| 443 | @typeName(@Type(@typeInfo(@TypeOf(test_instance.foo)))), | ||
| 444 | )); | ||
| 445 | } |
test/stage1/behavior/type_info.zig+19-2| ... | @@ -211,7 +211,9 @@ fn testUnion() void { | ... | @@ -211,7 +211,9 @@ fn testUnion() void { |
| 211 | expect(notag_union_info.Union.tag_type == null); | 211 | expect(notag_union_info.Union.tag_type == null); |
| 212 | expect(notag_union_info.Union.layout == .Auto); | 212 | expect(notag_union_info.Union.layout == .Auto); |
| 213 | expect(notag_union_info.Union.fields.len == 2); | 213 | expect(notag_union_info.Union.fields.len == 2); |
| 214 | expect(notag_union_info.Union.fields[0].alignment == @alignOf(void)); | ||
| 214 | expect(notag_union_info.Union.fields[1].field_type == u32); | 215 | expect(notag_union_info.Union.fields[1].field_type == u32); |
| 216 | expect(notag_union_info.Union.fields[1].alignment == @alignOf(u32)); | ||
| 215 | 217 | ||
| 216 | const TestExternUnion = extern union { | 218 | const TestExternUnion = extern union { |
| 217 | foo: *c_void, | 219 | foo: *c_void, |
| ... | @@ -229,13 +231,18 @@ test "type info: struct info" { | ... | @@ -229,13 +231,18 @@ test "type info: struct info" { |
| 229 | } | 231 | } |
| 230 | 232 | ||
| 231 | fn testStruct() void { | 233 | fn testStruct() void { |
| 234 | const unpacked_struct_info = @typeInfo(TestUnpackedStruct); | ||
| 235 | expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32)); | ||
| 236 | |||
| 232 | const struct_info = @typeInfo(TestStruct); | 237 | const struct_info = @typeInfo(TestStruct); |
| 233 | expect(struct_info == .Struct); | 238 | expect(struct_info == .Struct); |
| 234 | expect(struct_info.Struct.layout == .Packed); | 239 | expect(struct_info.Struct.layout == .Packed); |
| 235 | expect(struct_info.Struct.fields.len == 4); | 240 | expect(struct_info.Struct.fields.len == 4); |
| 241 | expect(struct_info.Struct.fields[0].alignment == 2 * @alignOf(usize)); | ||
| 236 | expect(struct_info.Struct.fields[2].field_type == *TestStruct); | 242 | expect(struct_info.Struct.fields[2].field_type == *TestStruct); |
| 237 | expect(struct_info.Struct.fields[2].default_value == null); | 243 | expect(struct_info.Struct.fields[2].default_value == null); |
| 238 | expect(struct_info.Struct.fields[3].default_value.? == 4); | 244 | expect(struct_info.Struct.fields[3].default_value.? == 4); |
| 245 | expect(struct_info.Struct.fields[3].alignment == 1); | ||
| 239 | expect(struct_info.Struct.decls.len == 2); | 246 | expect(struct_info.Struct.decls.len == 2); |
| 240 | expect(struct_info.Struct.decls[0].is_pub); | 247 | expect(struct_info.Struct.decls[0].is_pub); |
| 241 | expect(!struct_info.Struct.decls[0].data.Fn.is_extern); | 248 | expect(!struct_info.Struct.decls[0].data.Fn.is_extern); |
| ... | @@ -244,8 +251,12 @@ fn testStruct() void { | ... | @@ -244,8 +251,12 @@ fn testStruct() void { |
| 244 | expect(struct_info.Struct.decls[0].data.Fn.fn_type == fn (*const TestStruct) void); | 251 | expect(struct_info.Struct.decls[0].data.Fn.fn_type == fn (*const TestStruct) void); |
| 245 | } | 252 | } |
| 246 | 253 | ||
| 254 | const TestUnpackedStruct = struct { | ||
| 255 | fieldA: u32 = 4, | ||
| 256 | }; | ||
| 257 | |||
| 247 | const TestStruct = packed struct { | 258 | const TestStruct = packed struct { |
| 248 | fieldA: usize, | 259 | fieldA: usize align(2 * @alignOf(usize)), |
| 249 | fieldB: void, | 260 | fieldB: void, |
| 250 | fieldC: *Self, | 261 | fieldC: *Self, |
| 251 | fieldD: u32 = 4, | 262 | fieldD: u32 = 4, |
| ... | @@ -255,6 +266,8 @@ const TestStruct = packed struct { | ... | @@ -255,6 +266,8 @@ const TestStruct = packed struct { |
| 255 | }; | 266 | }; |
| 256 | 267 | ||
| 257 | test "type info: function type info" { | 268 | test "type info: function type info" { |
| 269 | // wasm doesn't support align attributes on functions | ||
| 270 | if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest; | ||
| 258 | testFunction(); | 271 | testFunction(); |
| 259 | comptime testFunction(); | 272 | comptime testFunction(); |
| 260 | } | 273 | } |
| ... | @@ -262,11 +275,14 @@ test "type info: function type info" { | ... | @@ -262,11 +275,14 @@ test "type info: function type info" { |
| 262 | fn testFunction() void { | 275 | fn testFunction() void { |
| 263 | const fn_info = @typeInfo(@TypeOf(foo)); | 276 | const fn_info = @typeInfo(@TypeOf(foo)); |
| 264 | expect(fn_info == .Fn); | 277 | expect(fn_info == .Fn); |
| 278 | expect(fn_info.Fn.alignment == 0); | ||
| 265 | expect(fn_info.Fn.calling_convention == .C); | 279 | expect(fn_info.Fn.calling_convention == .C); |
| 266 | expect(!fn_info.Fn.is_generic); | 280 | expect(!fn_info.Fn.is_generic); |
| 267 | expect(fn_info.Fn.args.len == 2); | 281 | expect(fn_info.Fn.args.len == 2); |
| 268 | expect(fn_info.Fn.is_var_args); | 282 | expect(fn_info.Fn.is_var_args); |
| 269 | expect(fn_info.Fn.return_type.? == usize); | 283 | expect(fn_info.Fn.return_type.? == usize); |
| 284 | const fn_aligned_info = @typeInfo(@TypeOf(fooAligned)); | ||
| 285 | expect(fn_aligned_info.Fn.alignment == 4); | ||
| 270 | 286 | ||
| 271 | const test_instance: TestStruct = undefined; | 287 | const test_instance: TestStruct = undefined; |
| 272 | const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo)); | 288 | const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo)); |
| ... | @@ -274,7 +290,8 @@ fn testFunction() void { | ... | @@ -274,7 +290,8 @@ fn testFunction() void { |
| 274 | expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); | 290 | expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct); |
| 275 | } | 291 | } |
| 276 | 292 | ||
| 277 | extern fn foo(a: usize, b: bool, ...) usize; | 293 | extern fn foo(a: usize, b: bool, ...) callconv(.C) usize; |
| 294 | extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize; | ||
| 278 | 295 | ||
| 279 | test "typeInfo with comptime parameter in struct fn def" { | 296 | test "typeInfo with comptime parameter in struct fn def" { |
| 280 | const S = struct { | 297 | const S = struct { |
test/stage1/behavior/vector.zig+40| ... | @@ -484,3 +484,43 @@ test "vector shift operators" { | ... | @@ -484,3 +484,43 @@ test "vector shift operators" { |
| 484 | S.doTheTest(); | 484 | S.doTheTest(); |
| 485 | comptime S.doTheTest(); | 485 | comptime S.doTheTest(); |
| 486 | } | 486 | } |
| 487 | |||
| 488 | test "vector reduce operation" { | ||
| 489 | const S = struct { | ||
| 490 | fn doTheTestReduce(comptime op: builtin.ReduceOp, x: anytype, expected: anytype) void { | ||
| 491 | const N = @typeInfo(@TypeOf(x)).Array.len; | ||
| 492 | const TX = @typeInfo(@TypeOf(x)).Array.child; | ||
| 493 | |||
| 494 | var r = @reduce(op, @as(Vector(N, TX), x)); | ||
| 495 | expectEqual(expected, r); | ||
| 496 | } | ||
| 497 | fn doTheTest() void { | ||
| 498 | doTheTestReduce(.And, [4]bool{ true, false, true, true }, @as(bool, false)); | ||
| 499 | doTheTestReduce(.Or, [4]bool{ false, true, false, false }, @as(bool, true)); | ||
| 500 | doTheTestReduce(.Xor, [4]bool{ true, true, true, false }, @as(bool, true)); | ||
| 501 | |||
| 502 | doTheTestReduce(.And, [4]u1{ 1, 0, 1, 1 }, @as(u1, 0)); | ||
| 503 | doTheTestReduce(.Or, [4]u1{ 0, 1, 0, 0 }, @as(u1, 1)); | ||
| 504 | doTheTestReduce(.Xor, [4]u1{ 1, 1, 1, 0 }, @as(u1, 1)); | ||
| 505 | |||
| 506 | doTheTestReduce(.And, [4]u32{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u32, 0x1010)); | ||
| 507 | doTheTestReduce(.Or, [4]u32{ 0xffff0000, 0xff00, 0xf0, 0xf }, ~@as(u32, 0)); | ||
| 508 | doTheTestReduce(.Xor, [4]u32{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, ~@as(u32, 0)); | ||
| 509 | |||
| 510 | doTheTestReduce(.Min, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, -386)); | ||
| 511 | doTheTestReduce(.Max, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, 1234567)); | ||
| 512 | |||
| 513 | doTheTestReduce(.Min, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 9)); | ||
| 514 | doTheTestReduce(.Max, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 99999)); | ||
| 515 | |||
| 516 | doTheTestReduce(.Min, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, -100.0)); | ||
| 517 | doTheTestReduce(.Max, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, 10.0e9)); | ||
| 518 | |||
| 519 | doTheTestReduce(.Min, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, -100.0)); | ||
| 520 | doTheTestReduce(.Max, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, 10.0e9)); | ||
| 521 | } | ||
| 522 | }; | ||
| 523 | |||
| 524 | S.doTheTest(); | ||
| 525 | comptime S.doTheTest(); | ||
| 526 | } |
test/stage2/arm.zig created+116| ... | @@ -0,0 +1,116 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const TestContext = @import("../../src/test.zig").TestContext; | ||
| 3 | |||
| 4 | const linux_arm = std.zig.CrossTarget{ | ||
| 5 | .cpu_arch = .arm, | ||
| 6 | .os_tag = .linux, | ||
| 7 | }; | ||
| 8 | |||
| 9 | pub fn addCases(ctx: *TestContext) !void { | ||
| 10 | { | ||
| 11 | var case = ctx.exe("hello world", linux_arm); | ||
| 12 | // Regular old hello world | ||
| 13 | case.addCompareOutput( | ||
| 14 | \\export fn _start() noreturn { | ||
| 15 | \\ print(); | ||
| 16 | \\ exit(); | ||
| 17 | \\} | ||
| 18 | \\ | ||
| 19 | \\fn print() void { | ||
| 20 | \\ asm volatile ("svc #0" | ||
| 21 | \\ : | ||
| 22 | \\ : [number] "{r7}" (4), | ||
| 23 | \\ [arg1] "{r0}" (1), | ||
| 24 | \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), | ||
| 25 | \\ [arg3] "{r2}" (14) | ||
| 26 | \\ : "memory" | ||
| 27 | \\ ); | ||
| 28 | \\ return; | ||
| 29 | \\} | ||
| 30 | \\ | ||
| 31 | \\fn exit() noreturn { | ||
| 32 | \\ asm volatile ("svc #0" | ||
| 33 | \\ : | ||
| 34 | \\ : [number] "{r7}" (1), | ||
| 35 | \\ [arg1] "{r0}" (0) | ||
| 36 | \\ : "memory" | ||
| 37 | \\ ); | ||
| 38 | \\ unreachable; | ||
| 39 | \\} | ||
| 40 | , | ||
| 41 | "Hello, World!\n", | ||
| 42 | ); | ||
| 43 | } | ||
| 44 | |||
| 45 | { | ||
| 46 | var case = ctx.exe("parameters and return values", linux_arm); | ||
| 47 | // Testing simple parameters and return values | ||
| 48 | // | ||
| 49 | // TODO: The parameters to the asm statement in print() had to | ||
| 50 | // be in a specific order because otherwise the write to r0 | ||
| 51 | // would overwrite the len parameter which resides in r0 | ||
| 52 | case.addCompareOutput( | ||
| 53 | \\export fn _start() noreturn { | ||
| 54 | \\ print(id(14)); | ||
| 55 | \\ exit(); | ||
| 56 | \\} | ||
| 57 | \\ | ||
| 58 | \\fn id(x: u32) u32 { | ||
| 59 | \\ return x; | ||
| 60 | \\} | ||
| 61 | \\ | ||
| 62 | \\fn print(len: u32) void { | ||
| 63 | \\ asm volatile ("svc #0" | ||
| 64 | \\ : | ||
| 65 | \\ : [number] "{r7}" (4), | ||
| 66 | \\ [arg3] "{r2}" (len), | ||
| 67 | \\ [arg1] "{r0}" (1), | ||
| 68 | \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")) | ||
| 69 | \\ : "memory" | ||
| 70 | \\ ); | ||
| 71 | \\ return; | ||
| 72 | \\} | ||
| 73 | \\ | ||
| 74 | \\fn exit() noreturn { | ||
| 75 | \\ asm volatile ("svc #0" | ||
| 76 | \\ : | ||
| 77 | \\ : [number] "{r7}" (1), | ||
| 78 | \\ [arg1] "{r0}" (0) | ||
| 79 | \\ : "memory" | ||
| 80 | \\ ); | ||
| 81 | \\ unreachable; | ||
| 82 | \\} | ||
| 83 | , | ||
| 84 | "Hello, World!\n", | ||
| 85 | ); | ||
| 86 | } | ||
| 87 | |||
| 88 | { | ||
| 89 | var case = ctx.exe("non-leaf functions", linux_arm); | ||
| 90 | // Testing non-leaf functions | ||
| 91 | case.addCompareOutput( | ||
| 92 | \\export fn _start() noreturn { | ||
| 93 | \\ foo(); | ||
| 94 | \\ exit(); | ||
| 95 | \\} | ||
| 96 | \\ | ||
| 97 | \\fn foo() void { | ||
| 98 | \\ bar(); | ||
| 99 | \\} | ||
| 100 | \\ | ||
| 101 | \\fn bar() void {} | ||
| 102 | \\ | ||
| 103 | \\fn exit() noreturn { | ||
| 104 | \\ asm volatile ("svc #0" | ||
| 105 | \\ : | ||
| 106 | \\ : [number] "{r7}" (1), | ||
| 107 | \\ [arg1] "{r0}" (0) | ||
| 108 | \\ : "memory" | ||
| 109 | \\ ); | ||
| 110 | \\ unreachable; | ||
| 111 | \\} | ||
| 112 | , | ||
| 113 | "", | ||
| 114 | ); | ||
| 115 | } | ||
| 116 | } | ||
test/stage2/test.zig+96-58| ... | @@ -21,11 +21,6 @@ const linux_riscv64 = std.zig.CrossTarget{ | ... | @@ -21,11 +21,6 @@ const linux_riscv64 = std.zig.CrossTarget{ |
| 21 | .os_tag = .linux, | 21 | .os_tag = .linux, |
| 22 | }; | 22 | }; |
| 23 | 23 | ||
| 24 | const linux_arm = std.zig.CrossTarget{ | ||
| 25 | .cpu_arch = .arm, | ||
| 26 | .os_tag = .linux, | ||
| 27 | }; | ||
| 28 | |||
| 29 | const wasi = std.zig.CrossTarget{ | 24 | const wasi = std.zig.CrossTarget{ |
| 30 | .cpu_arch = .wasm32, | 25 | .cpu_arch = .wasm32, |
| 31 | .os_tag = .wasi, | 26 | .os_tag = .wasi, |
| ... | @@ -35,6 +30,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -35,6 +30,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 35 | try @import("zir.zig").addCases(ctx); | 30 | try @import("zir.zig").addCases(ctx); |
| 36 | try @import("cbe.zig").addCases(ctx); | 31 | try @import("cbe.zig").addCases(ctx); |
| 37 | try @import("spu-ii.zig").addCases(ctx); | 32 | try @import("spu-ii.zig").addCases(ctx); |
| 33 | try @import("arm.zig").addCases(ctx); | ||
| 38 | 34 | ||
| 39 | { | 35 | { |
| 40 | var case = ctx.exe("hello world with updates", linux_x64); | 36 | var case = ctx.exe("hello world with updates", linux_x64); |
| ... | @@ -76,7 +72,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -76,7 +72,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 76 | \\ ); | 72 | \\ ); |
| 77 | \\ unreachable; | 73 | \\ unreachable; |
| 78 | \\} | 74 | \\} |
| 79 | , | 75 | , |
| 80 | "Hello, World!\n", | 76 | "Hello, World!\n", |
| 81 | ); | 77 | ); |
| 82 | // Now change the message only | 78 | // Now change the message only |
| ... | @@ -108,7 +104,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -108,7 +104,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 108 | \\ ); | 104 | \\ ); |
| 109 | \\ unreachable; | 105 | \\ unreachable; |
| 110 | \\} | 106 | \\} |
| 111 | , | 107 | , |
| 112 | "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", | 108 | "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", |
| 113 | ); | 109 | ); |
| 114 | // Now we print it twice. | 110 | // Now we print it twice. |
| ... | @@ -151,10 +147,13 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -151,10 +147,13 @@ pub fn addCases(ctx: *TestContext) !void { |
| 151 | { | 147 | { |
| 152 | var case = ctx.exe("hello world", macosx_x64); | 148 | var case = ctx.exe("hello world", macosx_x64); |
| 153 | case.addError("", &[_][]const u8{":1:1: error: no entry point found"}); | 149 | case.addError("", &[_][]const u8{":1:1: error: no entry point found"}); |
| 154 | } | ||
| 155 | 150 | ||
| 156 | { | 151 | // Incorrect return type |
| 157 | var case = ctx.exe("hello world", linux_riscv64); | 152 | case.addError( |
| 153 | \\export fn _start() noreturn { | ||
| 154 | \\} | ||
| 155 | , &[_][]const u8{":2:1: error: expected noreturn, found void"}); | ||
| 156 | |||
| 158 | // Regular old hello world | 157 | // Regular old hello world |
| 159 | case.addCompareOutput( | 158 | case.addCompareOutput( |
| 160 | \\export fn _start() noreturn { | 159 | \\export fn _start() noreturn { |
| ... | @@ -164,23 +163,23 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -164,23 +163,23 @@ pub fn addCases(ctx: *TestContext) !void { |
| 164 | \\} | 163 | \\} |
| 165 | \\ | 164 | \\ |
| 166 | \\fn print() void { | 165 | \\fn print() void { |
| 167 | \\ asm volatile ("ecall" | 166 | \\ asm volatile ("syscall" |
| 168 | \\ : | 167 | \\ : |
| 169 | \\ : [number] "{a7}" (64), | 168 | \\ : [number] "{rax}" (0x2000004), |
| 170 | \\ [arg1] "{a0}" (1), | 169 | \\ [arg1] "{rdi}" (1), |
| 171 | \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")), | 170 | \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), |
| 172 | \\ [arg3] "{a2}" ("Hello, World!\n".len) | 171 | \\ [arg3] "{rdx}" (14) |
| 173 | \\ : "rcx", "r11", "memory" | 172 | \\ : "memory" |
| 174 | \\ ); | 173 | \\ ); |
| 175 | \\ return; | 174 | \\ return; |
| 176 | \\} | 175 | \\} |
| 177 | \\ | 176 | \\ |
| 178 | \\fn exit() noreturn { | 177 | \\fn exit() noreturn { |
| 179 | \\ asm volatile ("ecall" | 178 | \\ asm volatile ("syscall" |
| 180 | \\ : | 179 | \\ : |
| 181 | \\ : [number] "{a7}" (94), | 180 | \\ : [number] "{rax}" (0x2000001), |
| 182 | \\ [arg1] "{a0}" (0) | 181 | \\ [arg1] "{rdi}" (0) |
| 183 | \\ : "rcx", "r11", "memory" | 182 | \\ : "memory" |
| 184 | \\ ); | 183 | \\ ); |
| 185 | \\ unreachable; | 184 | \\ unreachable; |
| 186 | \\} | 185 | \\} |
| ... | @@ -190,36 +189,37 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -190,36 +189,37 @@ pub fn addCases(ctx: *TestContext) !void { |
| 190 | } | 189 | } |
| 191 | 190 | ||
| 192 | { | 191 | { |
| 193 | var case = ctx.exe("hello world", linux_arm); | 192 | var case = ctx.exe("hello world", linux_riscv64); |
| 194 | // Regular old hello world | 193 | // Regular old hello world |
| 195 | case.addCompareOutput( | 194 | case.addCompareOutput( |
| 196 | \\export fn _start() noreturn { | 195 | \\export fn _start() noreturn { |
| 197 | \\ print(); | 196 | \\ print(); |
| 197 | \\ | ||
| 198 | \\ exit(); | 198 | \\ exit(); |
| 199 | \\} | 199 | \\} |
| 200 | \\ | 200 | \\ |
| 201 | \\fn print() void { | 201 | \\fn print() void { |
| 202 | \\ asm volatile ("svc #0" | 202 | \\ asm volatile ("ecall" |
| 203 | \\ : | 203 | \\ : |
| 204 | \\ : [number] "{r7}" (4), | 204 | \\ : [number] "{a7}" (64), |
| 205 | \\ [arg1] "{r0}" (1), | 205 | \\ [arg1] "{a0}" (1), |
| 206 | \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), | 206 | \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")), |
| 207 | \\ [arg3] "{r2}" (14) | 207 | \\ [arg3] "{a2}" ("Hello, World!\n".len) |
| 208 | \\ : "memory" | 208 | \\ : "rcx", "r11", "memory" |
| 209 | \\ ); | 209 | \\ ); |
| 210 | \\ return; | 210 | \\ return; |
| 211 | \\} | 211 | \\} |
| 212 | \\ | 212 | \\ |
| 213 | \\fn exit() noreturn { | 213 | \\fn exit() noreturn { |
| 214 | \\ asm volatile ("svc #0" | 214 | \\ asm volatile ("ecall" |
| 215 | \\ : | 215 | \\ : |
| 216 | \\ : [number] "{r7}" (1), | 216 | \\ : [number] "{a7}" (94), |
| 217 | \\ [arg1] "{r0}" (0) | 217 | \\ [arg1] "{a0}" (0) |
| 218 | \\ : "memory" | 218 | \\ : "rcx", "r11", "memory" |
| 219 | \\ ); | 219 | \\ ); |
| 220 | \\ unreachable; | 220 | \\ unreachable; |
| 221 | \\} | 221 | \\} |
| 222 | , | 222 | , |
| 223 | "Hello, World!\n", | 223 | "Hello, World!\n", |
| 224 | ); | 224 | ); |
| 225 | } | 225 | } |
| ... | @@ -244,7 +244,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -244,7 +244,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 244 | \\ ); | 244 | \\ ); |
| 245 | \\ unreachable; | 245 | \\ unreachable; |
| 246 | \\} | 246 | \\} |
| 247 | , | 247 | , |
| 248 | "Hello, World!\n", | 248 | "Hello, World!\n", |
| 249 | ); | 249 | ); |
| 250 | } | 250 | } |
| ... | @@ -271,7 +271,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -271,7 +271,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 271 | \\ ); | 271 | \\ ); |
| 272 | \\ unreachable; | 272 | \\ unreachable; |
| 273 | \\} | 273 | \\} |
| 274 | , | 274 | , |
| 275 | "", | 275 | "", |
| 276 | ); | 276 | ); |
| 277 | } | 277 | } |
| ... | @@ -298,7 +298,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -298,7 +298,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 298 | \\ ); | 298 | \\ ); |
| 299 | \\ unreachable; | 299 | \\ unreachable; |
| 300 | \\} | 300 | \\} |
| 301 | , | 301 | , |
| 302 | "", | 302 | "", |
| 303 | ); | 303 | ); |
| 304 | } | 304 | } |
| ... | @@ -329,7 +329,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -329,7 +329,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 329 | \\ ); | 329 | \\ ); |
| 330 | \\ unreachable; | 330 | \\ unreachable; |
| 331 | \\} | 331 | \\} |
| 332 | , | 332 | , |
| 333 | "", | 333 | "", |
| 334 | ); | 334 | ); |
| 335 | 335 | ||
| ... | @@ -362,7 +362,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -362,7 +362,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 362 | \\ ); | 362 | \\ ); |
| 363 | \\ unreachable; | 363 | \\ unreachable; |
| 364 | \\} | 364 | \\} |
| 365 | , | 365 | , |
| 366 | "", | 366 | "", |
| 367 | ); | 367 | ); |
| 368 | 368 | ||
| ... | @@ -398,7 +398,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -398,7 +398,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 398 | \\ ); | 398 | \\ ); |
| 399 | \\ unreachable; | 399 | \\ unreachable; |
| 400 | \\} | 400 | \\} |
| 401 | , | 401 | , |
| 402 | "", | 402 | "", |
| 403 | ); | 403 | ); |
| 404 | 404 | ||
| ... | @@ -435,7 +435,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -435,7 +435,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 435 | \\ ); | 435 | \\ ); |
| 436 | \\ unreachable; | 436 | \\ unreachable; |
| 437 | \\} | 437 | \\} |
| 438 | , | 438 | , |
| 439 | "", | 439 | "", |
| 440 | ); | 440 | ); |
| 441 | 441 | ||
| ... | @@ -465,7 +465,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -465,7 +465,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 465 | \\ ); | 465 | \\ ); |
| 466 | \\ unreachable; | 466 | \\ unreachable; |
| 467 | \\} | 467 | \\} |
| 468 | , | 468 | , |
| 469 | "", | 469 | "", |
| 470 | ); | 470 | ); |
| 471 | 471 | ||
| ... | @@ -499,7 +499,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -499,7 +499,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 499 | \\ ); | 499 | \\ ); |
| 500 | \\ unreachable; | 500 | \\ unreachable; |
| 501 | \\} | 501 | \\} |
| 502 | , | 502 | , |
| 503 | "", | 503 | "", |
| 504 | ); | 504 | ); |
| 505 | 505 | ||
| ... | @@ -523,7 +523,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -523,7 +523,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 523 | \\ ); | 523 | \\ ); |
| 524 | \\ unreachable; | 524 | \\ unreachable; |
| 525 | \\} | 525 | \\} |
| 526 | , | 526 | , |
| 527 | "", | 527 | "", |
| 528 | ); | 528 | ); |
| 529 | 529 | ||
| ... | @@ -562,7 +562,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -562,7 +562,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 562 | \\ ); | 562 | \\ ); |
| 563 | \\ unreachable; | 563 | \\ unreachable; |
| 564 | \\} | 564 | \\} |
| 565 | , | 565 | , |
| 566 | "hello\nhello\nhello\nhello\n", | 566 | "hello\nhello\nhello\nhello\n", |
| 567 | ); | 567 | ); |
| 568 | 568 | ||
| ... | @@ -599,7 +599,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -599,7 +599,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 599 | \\ ); | 599 | \\ ); |
| 600 | \\ unreachable; | 600 | \\ unreachable; |
| 601 | \\} | 601 | \\} |
| 602 | , | 602 | , |
| 603 | "", | 603 | "", |
| 604 | ); | 604 | ); |
| 605 | 605 | ||
| ... | @@ -641,7 +641,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -641,7 +641,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 641 | \\ ); | 641 | \\ ); |
| 642 | \\ unreachable; | 642 | \\ unreachable; |
| 643 | \\} | 643 | \\} |
| 644 | , | 644 | , |
| 645 | "", | 645 | "", |
| 646 | ); | 646 | ); |
| 647 | 647 | ||
| ... | @@ -693,7 +693,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -693,7 +693,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 693 | \\ ); | 693 | \\ ); |
| 694 | \\ unreachable; | 694 | \\ unreachable; |
| 695 | \\} | 695 | \\} |
| 696 | , | 696 | , |
| 697 | "", | 697 | "", |
| 698 | ); | 698 | ); |
| 699 | 699 | ||
| ... | @@ -755,7 +755,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -755,7 +755,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 755 | \\ ); | 755 | \\ ); |
| 756 | \\ unreachable; | 756 | \\ unreachable; |
| 757 | \\} | 757 | \\} |
| 758 | , | 758 | , |
| 759 | "", | 759 | "", |
| 760 | ); | 760 | ); |
| 761 | 761 | ||
| ... | @@ -788,7 +788,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -788,7 +788,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 788 | \\ ); | 788 | \\ ); |
| 789 | \\ unreachable; | 789 | \\ unreachable; |
| 790 | \\} | 790 | \\} |
| 791 | , | 791 | , |
| 792 | "", | 792 | "", |
| 793 | ); | 793 | ); |
| 794 | 794 | ||
| ... | @@ -820,7 +820,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -820,7 +820,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 820 | \\ ); | 820 | \\ ); |
| 821 | \\ unreachable; | 821 | \\ unreachable; |
| 822 | \\} | 822 | \\} |
| 823 | , | 823 | , |
| 824 | "", | 824 | "", |
| 825 | ); | 825 | ); |
| 826 | 826 | ||
| ... | @@ -845,7 +845,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -845,7 +845,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 845 | \\ ); | 845 | \\ ); |
| 846 | \\ unreachable; | 846 | \\ unreachable; |
| 847 | \\} | 847 | \\} |
| 848 | , | 848 | , |
| 849 | "", | 849 | "", |
| 850 | ); | 850 | ); |
| 851 | 851 | ||
| ... | @@ -871,7 +871,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -871,7 +871,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 871 | \\ ); | 871 | \\ ); |
| 872 | \\ unreachable; | 872 | \\ unreachable; |
| 873 | \\} | 873 | \\} |
| 874 | , | 874 | , |
| 875 | "", | 875 | "", |
| 876 | ); | 876 | ); |
| 877 | 877 | ||
| ... | @@ -904,11 +904,49 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -904,11 +904,49 @@ pub fn addCases(ctx: *TestContext) !void { |
| 904 | \\ ); | 904 | \\ ); |
| 905 | \\ unreachable; | 905 | \\ unreachable; |
| 906 | \\} | 906 | \\} |
| 907 | , | 907 | , |
| 908 | "hello\nhello\nhello\nhello\nhello\n", | 908 | "hello\nhello\nhello\nhello\nhello\n", |
| 909 | ); | 909 | ); |
| 910 | } | 910 | } |
| 911 | 911 | ||
| 912 | { | ||
| 913 | var case = ctx.exe("basic import", linux_x64); | ||
| 914 | case.addCompareOutput( | ||
| 915 | \\export fn _start() noreturn { | ||
| 916 | \\ @import("print.zig").print(); | ||
| 917 | \\ exit(); | ||
| 918 | \\} | ||
| 919 | \\ | ||
| 920 | \\fn exit() noreturn { | ||
| 921 | \\ asm volatile ("syscall" | ||
| 922 | \\ : | ||
| 923 | \\ : [number] "{rax}" (231), | ||
| 924 | \\ [arg1] "{rdi}" (@as(usize, 0)) | ||
| 925 | \\ : "rcx", "r11", "memory" | ||
| 926 | \\ ); | ||
| 927 | \\ unreachable; | ||
| 928 | \\} | ||
| 929 | , | ||
| 930 | "Hello, World!\n", | ||
| 931 | ); | ||
| 932 | try case.files.append(.{ | ||
| 933 | .src = | ||
| 934 | \\pub fn print() void { | ||
| 935 | \\ asm volatile ("syscall" | ||
| 936 | \\ : | ||
| 937 | \\ : [number] "{rax}" (@as(usize, 1)), | ||
| 938 | \\ [arg1] "{rdi}" (@as(usize, 1)), | ||
| 939 | \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), | ||
| 940 | \\ [arg3] "{rdx}" (@as(usize, 14)) | ||
| 941 | \\ : "rcx", "r11", "memory" | ||
| 942 | \\ ); | ||
| 943 | \\ return; | ||
| 944 | \\} | ||
| 945 | , | ||
| 946 | .path = "print.zig", | ||
| 947 | }); | ||
| 948 | } | ||
| 949 | |||
| 912 | { | 950 | { |
| 913 | var case = ctx.exe("wasm function calls", wasi); | 951 | var case = ctx.exe("wasm function calls", wasi); |
| 914 | 952 | ||
| ... | @@ -923,7 +961,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -923,7 +961,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 923 | \\ bar(); | 961 | \\ bar(); |
| 924 | \\} | 962 | \\} |
| 925 | \\fn bar() void {} | 963 | \\fn bar() void {} |
| 926 | , | 964 | , |
| 927 | "42\n", | 965 | "42\n", |
| 928 | ); | 966 | ); |
| 929 | 967 | ||
| ... | @@ -941,7 +979,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -941,7 +979,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 941 | \\ bar(); | 979 | \\ bar(); |
| 942 | \\} | 980 | \\} |
| 943 | \\fn bar() void {} | 981 | \\fn bar() void {} |
| 944 | , | 982 | , |
| 945 | "42\n", | 983 | "42\n", |
| 946 | ); | 984 | ); |
| 947 | 985 | ||
| ... | @@ -957,10 +995,10 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -957,10 +995,10 @@ pub fn addCases(ctx: *TestContext) !void { |
| 957 | \\ bar(); | 995 | \\ bar(); |
| 958 | \\} | 996 | \\} |
| 959 | \\fn bar() void {} | 997 | \\fn bar() void {} |
| 960 | , | 998 | , |
| 961 | // This is what you get when you take the bits of the IEE-754 | 999 | // This is what you get when you take the bits of the IEE-754 |
| 962 | // representation of 42.0 and reinterpret them as an unsigned | 1000 | // representation of 42.0 and reinterpret them as an unsigned |
| 963 | // integer. Guess that's a bug in wasmtime. | 1001 | // integer. Guess that's a bug in wasmtime. |
| 964 | "1109917696\n", | 1002 | "1109917696\n", |
| 965 | ); | 1003 | ); |
| 966 | } | 1004 | } |
test/translate_c.zig+17-1| ... | @@ -3,6 +3,22 @@ const std = @import("std"); | ... | @@ -3,6 +3,22 @@ const std = @import("std"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | ||
| 5 | pub fn addCases(cases: *tests.TranslateCContext) void { | 5 | pub fn addCases(cases: *tests.TranslateCContext) void { |
| 6 | cases.add("macro expressions respect C operator precedence", | ||
| 7 | \\#define FOO *((foo) + 2) | ||
| 8 | \\#define VALUE (1 + 2 * 3 + 4 * 5 + 6 << 7 | 8 == 9) | ||
| 9 | \\#define _AL_READ3BYTES(p) ((*(unsigned char *)(p)) \ | ||
| 10 | \\ | (*((unsigned char *)(p) + 1) << 8) \ | ||
| 11 | \\ | (*((unsigned char *)(p) + 2) << 16)) | ||
| 12 | , &[_][]const u8{ | ||
| 13 | \\pub const FOO = (foo + 2).*; | ||
| 14 | , | ||
| 15 | \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9); | ||
| 16 | , | ||
| 17 | \\pub inline fn _AL_READ3BYTES(p: anytype) @TypeOf(((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16)) { | ||
| 18 | \\ return ((@import("std").meta.cast([*c]u8, p)).* | (((@import("std").meta.cast([*c]u8, p)) + 1).* << 8)) | (((@import("std").meta.cast([*c]u8, p)) + 2).* << 16); | ||
| 19 | \\} | ||
| 20 | }); | ||
| 21 | |||
| 6 | cases.add("extern variable in block scope", | 22 | cases.add("extern variable in block scope", |
| 7 | \\float bar; | 23 | \\float bar; |
| 8 | \\int foo() { | 24 | \\int foo() { |
| ... | @@ -2978,7 +2994,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -2978,7 +2994,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2978 | cases.add("string concatenation in macros: three strings", | 2994 | cases.add("string concatenation in macros: three strings", |
| 2979 | \\#define FOO "a" "b" "c" | 2995 | \\#define FOO "a" "b" "c" |
| 2980 | , &[_][]const u8{ | 2996 | , &[_][]const u8{ |
| 2981 | \\pub const FOO = "a" ++ ("b" ++ "c"); | 2997 | \\pub const FOO = "a" ++ "b" ++ "c"; |
| 2982 | }); | 2998 | }); |
| 2983 | 2999 | ||
| 2984 | cases.add("multibyte character literals", | 3000 | cases.add("multibyte character literals", |