authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-07 00:46:05-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-07 00:46:05-07:00
logb5a36f676b1fd69f195d9f1eb6e35f3eb2f15946
tree92ce1ffa64bc2d32d5ef73dce66daf3c8e893ee6
parentd6d05fc84d33c71434a1f8bae51ca5956e08cdf0
parentf2d374e8465042fa5cb6bf2be7b9b086948f3a94

Merge remote-tracking branch 'origin/master' into llvm11

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})
27set(ZIG_VERSION_MAJOR 0)27set(ZIG_VERSION_MAJOR 0)
28set(ZIG_VERSION_MINOR 6)28set(ZIG_VERSION_MINOR 6)
29set(ZIG_VERSION_PATCH 0)29set(ZIG_VERSION_PATCH 0)
30set(ZIG_VERSION "${ZIG_VERSION_MAJOR}.${ZIG_VERSION_MINOR}.${ZIG_VERSION_PATCH}")30set(ZIG_VERSION "" CACHE STRING "Override Zig version string. Default is to find out with git.")
3131
32find_program(GIT_EXE NAMES git)32if("${ZIG_VERSION}" STREQUAL "")
33if(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 --always35 if(GIT_EXE)
36 RESULT_VARIABLE EXIT_STATUS36 execute_process(
37 OUTPUT_VARIABLE ZIG_GIT_REV37 COMMAND ${GIT_EXE} -C ${CMAKE_SOURCE_DIR} name-rev HEAD --tags --name-only --no-undefined --always
38 OUTPUT_STRIP_TRAILING_WHITESPACE38 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()
49endif()52endif()
...@@ -63,6 +66,9 @@ endif()...@@ -63,6 +66,9 @@ endif()
6366
64if(ZIG_STATIC)67if(ZIG_STATIC)
65 set(ZIG_STATIC_LLVM "on")68 set(ZIG_STATIC_LLVM "on")
69 set(ZIG_LINK_MODE "Static")
70else()
71 set(ZIG_LINK_MODE "Dynamic")
66endif()72endif()
6773
68string(REGEX REPLACE "\\\\" "\\\\\\\\" ZIG_LIBC_LIB_DIR_ESCAPED "${ZIG_LIBC_LIB_DIR}")74string(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)
74set(ZIG_TARGET_TRIPLE "native" CACHE STRING "arch-os-abi to output binaries for")80set(ZIG_TARGET_TRIPLE "native" CACHE STRING "arch-os-abi to output binaries for")
75set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries for")81set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries for")
76set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary")82set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary")
83set(ZIG_PREFER_LLVM_CONFIG off CACHE BOOL "(when cross compiling) use llvm-config to find target llvm dependencies if needed")
7784
78find_package(llvm)85find_package(llvm)
79find_package(clang)86find_package(clang)
...@@ -257,7 +264,6 @@ target_include_directories(embedded_softfloat PUBLIC...@@ -257,7 +264,6 @@ target_include_directories(embedded_softfloat PUBLIC
257)264)
258include_directories("${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/include")265include_directories("${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/include")
259set(SOFTFLOAT_LIBRARIES embedded_softfloat)266set(SOFTFLOAT_LIBRARIES embedded_softfloat)
260include_directories("${CMAKE_SOURCE_DIR}/deps/dbg-macro")
261267
262find_package(Threads)268find_package(Threads)
263269
...@@ -487,7 +493,7 @@ if("${ZIG_TARGET_TRIPLE}" STREQUAL "native")...@@ -487,7 +493,7 @@ if("${ZIG_TARGET_TRIPLE}" STREQUAL "native")
487 endif()493 endif()
488else()494else()
489 add_custom_target(zig_build_zig1 ALL495 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.
3737
38 * cmake >= 3.15.338 * 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.x42 * 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: BuildWindows28- 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: 36031 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.exe36 del sfx.exe
47 displayName: Download/Extract/Install MSYS237 displayName: Download/Extract/Install MSYS2
...@@ -57,7 +47,7 @@ jobs:...@@ -57,7 +47,7 @@ jobs:
57 - task: DownloadSecureFile@147 - task: DownloadSecureFile@1
58 inputs:48 inputs:
59 secureFile: s3cfg49 secureFile: s3cfg
60 - script: $(SCRIPT)50 - script: ci/azure/windows_msvc_script.bat
61 name: main51 name: main
62 displayName: 'Build and test'52 displayName: 'Build and test'
63- job: OnMasterSuccess53- job: OnMasterSuccess
ci/azure/windows_mingw_script deleted-28
...@@ -1,28 +0,0 @@
1#!/bin/sh
2
3set -x
4set -e
5
6pacman --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
8git 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.
13git config core.autocrlf false
14# Too late; the files are already mangled.
15git checkout .
16
17ZIGBUILDDIR="$(pwd)/build"
18PREFIX="$ZIGBUILDDIR/dist"
19CMAKEFLAGS="-DCMAKE_COLOR_MAKEFILE=OFF -DCMAKE_INSTALL_PREFIX=$PREFIX -DZIG_STATIC=ON"
20
21mkdir $ZIGBUILDDIR
22cd $ZIGBUILDDIR
23
24cmake .. -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=RelWithDebInfo $CMAKEFLAGS -DCMAKE_EXE_LINKER_FLAGS='-fuse-ld=lld -Wl,/debug,/pdb:zig.pdb'
25
26make -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
4set -e4set -e
55
6pacman -Su --needed --noconfirm6pacman -Su --needed --noconfirm
77pacman -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
10pacman -S --needed --noconfirm wget p7zip tar xz
11pacman -U --noconfirm http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-python-3.8.4-1-any.pkg.tar.zst
12pacman -U --noconfirm http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-python-pip-20.0.2-1-any.pkg.tar.xz
138
14pip install s3cmd9pip install s3cmd
15wget -nv "https://ziglang.org/deps/llvm%2bclang%2blld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz"10wget -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/lib32 /usr/local/llvm11/lib
33 /usr/local/llvm110/lib33 /usr/local/llvm110/lib
34 )34 )
35elseif("${ZIG_TARGET_TRIPLE}" STREQUAL "native")35elseif(("${ZIG_TARGET_TRIPLE}" STREQUAL "native") OR ZIG_PREFER_LLVM_CONFIG)
36 find_program(LLVM_CONFIG_EXE36 find_program(LLVM_CONFIG_EXE
37 NAMES llvm-config-11 llvm-config-11.0 llvm-config110 llvm-config11 llvm-config37 NAMES llvm-config-11 llvm-config-11.0 llvm-config110 llvm-config11 llvm-config
38 PATHS38 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)
5656
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()
6666
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}")
142else()143else()
143 # Here we assume that we're cross compiling with Zig, of course. No reason144 # 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 target145 # to support more complicated setups.
145 # AVR is enabled.
146146
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 @@
1MIT License
2
3Copyright (c) 2019 David Peter <mail@david-peter.de>
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
deps/dbg-macro/README.md deleted-172
...@@ -1,172 +0,0 @@
1# `dbg(…)`
2
3[![Build Status](https://travis-ci.org/sharkdp/dbg-macro.svg?branch=master)](https://travis-ci.org/sharkdp/dbg-macro) [![Build status](https://ci.appveyor.com/api/projects/status/vmo9rw4te2wifkul/branch/master?svg=true)](https://ci.appveyor.com/project/sharkdp/dbg-macro) [![Try it online](https://img.shields.io/badge/try-online-f34b7d.svg)](https://repl.it/@sharkdp/dbg-macro-demo) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](dbg.h)
4
5*A macro for `printf`-style debugging fans.*
6
7Debuggers are great. But sometimes you just don't have the time or patience to set
8up everything correctly and just want a quick way to inspect some values at runtime.
9
10This projects provides a [single header file](dbg.h) with a `dbg(…)`
11macro 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:
21int factorial(int n) {
22 if (dbg(n <= 1)) {
23 return dbg(1);
24 } else {
25 return dbg(n * factorial(n - 1));
26 }
27}
28
29int 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
47The code above produces this output ([try it yourself](https://repl.it/@sharkdp/dbg-macro-demo)):
48
49![dbg(…) macro output](https://i.imgur.com/NHEYk9A.png)
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
63To make this practical, the `dbg.h` header should to be readily available from all kinds of different
64places and in all kinds of environments. The quick & dirty way is to actually copy the header file
65to `/usr/include` or to clone the repository and symlink `dbg.h` to `/usr/include/dbg.h`.
66``` bash
67git clone https://github.com/sharkdp/dbg-macro
68sudo ln -s $(readlink -f dbg-macro/dbg.h) /usr/include/dbg.h
69```
70If you don't want to make untracked changes to your filesystem, check below if there is a package for
71your operating system or package manager.
72
73### On Arch Linux
74
75You can install [`dbg-macro` from the AUR](https://aur.archlinux.org/packages/dbg-macro/):
76``` bash
77yay -S dbg-macro
78```
79
80### With vcpkg
81
82You can install the [`dbg-macro` port](https://github.com/microsoft/vcpkg/tree/master/ports/dbg-macro) via:
83``` bash
84vcpkg 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
96If you want to format integers in hexadecimal, octal or binary representation, you can
97simply wrap them in `dbg::hex(…)`, `dbg::oct(…)` or `dbg::bin(…)`:
98```c++
99const uint32_t secret = 12648430;
100dbg(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
106sometimes you just want to print a type (maybe because you don't have a value for that type).
107In this case, you can use the `dbg::type<T>()` helper to pretty-print a given type `T`.
108For example:
109```c++
110template <typename T>
111void 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
119To print a timestamp, you can use the `dbg::time()` helper:
120```c++
121dbg(dbg::time());
122```
123
124### Customization
125
126If you want `dbg(…)` to work for your custom datatype, you can simply overload `operator<<` for
127`std::ostream&`:
128```c++
129std::ostream& operator<<(std::ostream& out, const user_defined_type& v) {
130 out << "…";
131 return out;
132}
133```
134
135If 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
139namespace dbg {
140 std::string get_type_name(type_tag<bool>) {
141 return "truth value";
142 }
143}
144```
145
146## Development
147
148If you want to contribute to `dbg-macro`, here is how you can build the tests and demos:
149
150Make sure that the submodule(s) are up to date:
151```bash
152git submodule update --init
153```
154
155Then, use the typical `cmake` workflow. Usage of `-DCMAKE_CXX_STANDARD=17` is optional,
156but recommended in order to have the largest set of features enabled:
157```bash
158mkdir build
159cd build
160cmake .. -DCMAKE_CXX_STANDARD=17
161make
162```
163
164To run the tests, simply call:
165```bash
166make test
167```
168You can find the unit tests in `tests/basic.cpp`.
169
170## Acknowledgement
171
172This 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
5License (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
72namespace dbg {
73
74#ifdef DBG_MACRO_UNIX
75inline bool isColorizedOutputEnabled() {
76 return isatty(fileno(stderr));
77}
78#else
79inline bool isColorizedOutputEnabled() {
80 return true;
81}
82#endif
83
84struct time {};
85
86namespace 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__
93static constexpr size_t PREFIX_LENGTH =
94 sizeof("const char *dbg::type_name_impl() [T = ") - 1;
95static constexpr size_t SUFFIX_LENGTH = sizeof("]") - 1;
96#elif defined(__GNUC__) && !defined(__clang__)
97#define DBG_MACRO_PRETTY_FUNCTION __PRETTY_FUNCTION__
98static constexpr size_t PREFIX_LENGTH =
99 sizeof("const char* dbg::type_name_impl() [with T = ") - 1;
100static constexpr size_t SUFFIX_LENGTH = sizeof("]") - 1;
101#elif defined(_MSC_VER)
102#define DBG_MACRO_PRETTY_FUNCTION __FUNCSIG__
103static constexpr size_t PREFIX_LENGTH =
104 sizeof("const char *__cdecl dbg::type_name_impl<") - 1;
105static 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
114template <typename T>
115struct 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
141template <typename T>
142print_formatted<T> hex(T value) {
143 return print_formatted<T>{value, 16};
144}
145
146template <typename T>
147print_formatted<T> oct(T value) {
148 return print_formatted<T>{value, 8};
149}
150
151template <typename T>
152print_formatted<T> bin(T value) {
153 return print_formatted<T>{value, 2};
154}
155
156// Implementation of 'type_name<T>()'
157
158template <typename T>
159const char* type_name_impl() {
160 return DBG_MACRO_PRETTY_FUNCTION;
161}
162
163template <typename T>
164struct type_tag {};
165
166template <int&... ExplicitArgumentBarrier, typename T>
167std::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
175template <typename T>
176std::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
203inline std::string get_type_name(type_tag<short>) {
204 return "short";
205}
206
207inline std::string get_type_name(type_tag<unsigned short>) {
208 return "unsigned short";
209}
210
211inline std::string get_type_name(type_tag<long>) {
212 return "long";
213}
214
215inline std::string get_type_name(type_tag<unsigned long>) {
216 return "unsigned long";
217}
218
219inline std::string get_type_name(type_tag<std::string>) {
220 return "std::string";
221}
222
223template <typename T>
224std::string get_type_name(type_tag<std::vector<T, std::allocator<T>>>) {
225 return "std::vector<" + type_name<T>() + ">";
226}
227
228template <typename T1, typename T2>
229std::string get_type_name(type_tag<std::pair<T1, T2>>) {
230 return "std::pair<" + type_name<T1>() + ", " + type_name<T2>() + ">";
231}
232
233template <typename... T>
234std::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
246template <typename... T>
247std::string get_type_name(type_tag<std::tuple<T...>>) {
248 return "std::tuple<" + type_list_to_string<T...>() + ">";
249}
250
251template <typename T>
252inline 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
258namespace detail_detector {
259
260struct nonesuch {
261 nonesuch() = delete;
262 ~nonesuch() = delete;
263 nonesuch(nonesuch const&) = delete;
264 void operator=(nonesuch const&) = delete;
265};
266
267template <typename...>
268using void_t = void;
269
270template <class Default,
271 class AlwaysVoid,
272 template <class...>
273 class Op,
274 class... Args>
275struct detector {
276 using value_t = std::false_type;
277 using type = Default;
278};
279
280template <class Default, template <class...> class Op, class... Args>
281struct 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
288template <template <class...> class Op, class... Args>
289using is_detected = typename detail_detector::
290 detector<detail_detector::nonesuch, void, Op, Args...>::value_t;
291
292namespace detail {
293
294namespace {
295using std::begin;
296using std::end;
297#if DBG_MACRO_CXX_STANDARD < 17
298template <typename T>
299constexpr auto size(const T& c) -> decltype(c.size()) {
300 return c.size();
301}
302template <typename T, std::size_t N>
303constexpr std::size_t size(const T (&)[N]) {
304 return N;
305}
306#else
307using std::size;
308#endif
309} // namespace
310
311template <typename T>
312using detect_begin_t = decltype(detail::begin(std::declval<T>()));
313
314template <typename T>
315using detect_end_t = decltype(detail::end(std::declval<T>()));
316
317template <typename T>
318using detect_size_t = decltype(detail::size(std::declval<T>()));
319
320template <typename T>
321struct 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
331template <typename T>
332using ostream_operator_t =
333 decltype(std::declval<std::ostream&>() << std::declval<T>());
334
335template <typename T>
336struct has_ostream_operator : is_detected<ostream_operator_t, T> {};
337
338} // namespace detail
339
340// Helper to dbg(…)-print types
341template <typename T>
342struct print_type {};
343
344template <typename T>
345print_type<T> type() {
346 return print_type<T>{};
347}
348
349// Specializations of "pretty_print"
350
351template <typename T>
352inline void pretty_print(std::ostream& stream, const T& value, std::true_type) {
353 stream << value;
354}
355
356template <typename T>
357inline 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
362template <typename T>
363inline typename std::enable_if<!detail::is_container<const T&>::value &&
364 !std::is_enum<T>::value,
365 bool>::type
366pretty_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
372inline bool pretty_print(std::ostream& stream, const bool& value) {
373 stream << std::boolalpha << value;
374 return true;
375}
376
377inline 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
389template <typename P>
390inline 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
399template <typename T, typename Deleter>
400inline bool pretty_print(std::ostream& stream,
401 std::unique_ptr<T, Deleter>& value) {
402 pretty_print(stream, value.get());
403 return true;
404}
405
406template <typename T>
407inline 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
414template <size_t N>
415inline bool pretty_print(std::ostream& stream, const char (&value)[N]) {
416 stream << value;
417 return false;
418}
419
420template <>
421inline bool pretty_print(std::ostream& stream, const char* const& value) {
422 stream << '"' << value << '"';
423 return true;
424}
425
426template <size_t Idx>
427struct 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
436template <>
437struct 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
444template <typename... Ts>
445inline 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
453template <>
454inline bool pretty_print(std::ostream& stream, const std::tuple<>&) {
455 stream << "{}";
456
457 return true;
458}
459
460template <>
461inline 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
476template <typename T>
477std::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
490template <typename T>
491inline 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
524template <typename T>
525inline 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
548template <typename Container>
549inline typename std::enable_if<detail::is_container<const Container&>::value,
550 bool>::type
551pretty_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
574template <typename Enum>
575inline typename std::enable_if<std::is_enum<Enum>::value, bool>::type
576pretty_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
583inline bool pretty_print(std::ostream& stream, const std::string& value) {
584 stream << '"' << value << '"';
585 return true;
586}
587
588template <typename T1, typename T2>
589inline 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
600template <typename T>
601inline 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
613template <typename... Ts>
614inline 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
625class 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
694template <typename T>
695T&& 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 @@
1LIBRARY USER32.dll
2EXPORTS
3;ord_1500@16 @1500
4;ord_1501@4 @1501
5;ord_1502@12 @1502
6ActivateKeyboardLayout@8
7AddClipboardFormatListener@4
8AdjustWindowRect@12
9AdjustWindowRectEx@16
10AlignRects@16
11AllowForegroundActivation@0
12AllowSetForegroundWindow@4
13AnimateWindow@12
14AnyPopup@0
15AppendMenuA@16
16AppendMenuW@16
17ArrangeIconicWindows@4
18AttachThreadInput@12
19BeginDeferWindowPos@4
20BeginPaint@8
21BlockInput@4
22BringWindowToTop@4
23BroadcastSystemMessage@20
24BroadcastSystemMessageA@20
25BroadcastSystemMessageExA@24
26BroadcastSystemMessageExW@24
27BroadcastSystemMessageW@20
28BuildReasonArray@12
29CalcChildScroll@8
30CalcMenuBar@20
31CalculatePopupWindowPosition@20
32CallMsgFilter@8
33CallMsgFilterA@8
34CallMsgFilterW@8
35CallNextHookEx@16
36CallWindowProcA@20
37CallWindowProcW@20
38CancelShutdown@0
39CascadeChildWindows@8
40CascadeWindows@20
41ChangeClipboardChain@8
42ChangeDisplaySettingsA@8
43ChangeDisplaySettingsExA@20
44ChangeDisplaySettingsExW@20
45ChangeDisplaySettingsW@8
46ChangeMenuA@20
47ChangeMenuW@20
48ChangeWindowMessageFilter@8
49ChangeWindowMessageFilterEx@16
50CharLowerA@4
51CharLowerBuffA@8
52CharLowerBuffW@8
53CharLowerW@4
54CharNextA@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
62CharNextExA@12
63CharNextW@4
64CharPrevA@8
65CharPrevExA@16
66CharPrevW@8
67CharToOemA@8
68CharToOemBuffA@12
69CharToOemBuffW@12
70CharToOemW@8
71CharUpperA@4
72CharUpperBuffA@8
73CharUpperBuffW@8
74CharUpperW@4
75CheckDesktopByThreadId@4
76CheckDBCSEnabledExt@0
77CheckDlgButton@12
78CheckMenuItem@12
79CheckMenuRadioItem@20
80CheckProcessForClipboardAccess@8
81CheckProcessSession@4
82CheckRadioButton@16
83CheckWindowThreadDesktop@8
84ChildWindowFromPoint@12
85ChildWindowFromPointEx@16
86CliImmSetHotKey@16
87ClientThreadSetup@0
88ClientToScreen@8
89ClipCursor@4
90CloseClipboard@0
91CloseDesktop@4
92CloseGestureInfoHandle@4
93CloseTouchInputHandle@4
94CloseWindow@4
95CloseWindowStation@4
96ConsoleControl@12
97ControlMagnification@8
98CopyAcceleratorTableA@12
99CopyAcceleratorTableW@12
100CopyIcon@4
101CopyImage@20
102CopyRect@8
103CountClipboardFormats@0
104CreateAcceleratorTableA@8
105CreateAcceleratorTableW@8
106CreateCaret@16
107CreateCursor@28
108CreateDCompositionHwndTarget@12
109CreateDesktopA@24
110CreateDesktopExA@32
111CreateDesktopExW@32
112CreateDesktopW@24
113CreateDialogIndirectParamA@20
114CreateDialogIndirectParamAorW@24
115CreateDialogIndirectParamW@20
116CreateDialogParamA@20
117CreateDialogParamW@20
118CreateIcon@28
119CreateIconFromResource@16
120CreateIconFromResourceEx@28
121CreateIconIndirect@4
122CreateMDIWindowA@40
123CreateMDIWindowW@40
124CreateMenu@0
125CreatePopupMenu@0
126CreateSystemThreads@16 ; ReactOS has the @8 variant
127CreateWindowExA@48
128CreateWindowExW@48
129CreateWindowInBand@52
130CreateWindowIndirect@4
131CreateWindowStationA@16
132CreateWindowStationW@16
133CsrBroadcastSystemMessageExW@24
134CtxInitUser32@0
135DdeAbandonTransaction@12
136DdeAccessData@8
137DdeAddData@16
138DdeClientTransaction@32
139DdeCmpStringHandles@8
140DdeConnect@16
141DdeConnectList@20
142DdeCreateDataHandle@28
143DdeCreateStringHandleA@12
144DdeCreateStringHandleW@12
145DdeDisconnect@4
146DdeDisconnectList@4
147DdeEnableCallback@12
148DdeFreeDataHandle@4
149DdeFreeStringHandle@8
150DdeGetData@16
151DdeGetLastError@4
152DdeGetQualityOfService@12
153DdeImpersonateClient@4
154DdeInitializeA@16
155DdeInitializeW@16
156DdeKeepStringHandle@8
157DdeNameService@16
158DdePostAdvise@12
159DdeQueryConvInfo@12
160DdeQueryNextServer@8
161DdeQueryStringA@20
162DdeQueryStringW@20
163DdeReconnect@4
164DdeSetQualityOfService@12
165DdeSetUserHandle@12
166DdeUnaccessData@4
167DdeUninitialize@4
168DefDlgProcA@16
169DefDlgProcW@16
170DefFrameProcA@20
171DefFrameProcW@20
172DefMDIChildProcA@16
173DefMDIChildProcW@16
174DefRawInputProc@12
175DefWindowProcA@16
176DefWindowProcW@16
177DeferWindowPos@32
178DeferWindowPosAndBand@36
179DeleteMenu@12
180DeregisterShellHookWindow@4
181DestroyAcceleratorTable@4
182DestroyCaret@0
183DestroyCursor@4
184DestroyDCompositionHwndTarget@8
185DestroyIcon@4
186DestroyMenu@4
187DestroyReasons@4
188DestroyWindow@4
189DeviceEventWorker@24 ; No documentation whatsoever, ReactOS has a stub with @20 - https://www.reactos.org/archives/public/ros-diffs/2011-February/040308.html
190DialogBoxIndirectParamA@20
191DialogBoxIndirectParamAorW@24
192DialogBoxIndirectParamW@20
193DialogBoxParamA@20
194DialogBoxParamW@20
195DisableProcessWindowsGhosting@0
196DispatchMessageA@4
197DispatchMessageW@4
198DisplayConfigGetDeviceInfo@4
199DisplayConfigSetDeviceInfo@4
200DisplayExitWindowsWarnings@4
201DlgDirListA@20
202DlgDirListComboBoxA@20
203DlgDirListComboBoxW@20
204DlgDirListW@20
205DlgDirSelectComboBoxExA@16
206DlgDirSelectComboBoxExW@16
207DlgDirSelectExA@16
208DlgDirSelectExW@16
209DoSoundConnect@0
210DoSoundDisconnect@0
211DragDetect@12
212DragObject@20
213DrawAnimatedRects@16
214DrawCaption@16
215DrawCaptionTempA@28
216DrawCaptionTempW@28
217DrawEdge@16
218DrawFocusRect@8
219DrawFrame@16
220DrawFrameControl@16
221DrawIcon@16
222DrawIconEx@36
223DrawMenuBar@4
224DrawMenuBarTemp@20
225DrawStateA@40
226DrawStateW@40
227DrawTextA@20
228DrawTextExA@24
229DrawTextExW@24
230DrawTextW@20
231DwmGetDxSharedSurface@24
232DwmGetRemoteSessionOcclusionEvent@0
233DwmGetRemoteSessionOcclusionState@0
234DwmLockScreenUpdates@4
235DwmStartRedirection@8 ; Mentioned on http://habrahabr.ru/post/145174/ , enables GDI virtualization (for security purposes)
236DwmStopRedirection@0
237DwmValidateWindow@8
238EditWndProc@16
239EmptyClipboard@0
240EnableMenuItem@12
241EnableMouseInPointer@4
242EnableScrollBar@12
243EnableSessionForMMCSS@4
244EnableWindow@8
245EndDeferWindowPos@4
246EndDeferWindowPosEx@8
247EndDialog@8
248EndMenu@0
249EndPaint@8
250EndTask@12
251EnterReaderModeHelper@4
252EnumChildWindows@12
253EnumClipboardFormats@4
254EnumDesktopWindows@12
255EnumDesktopsA@12
256EnumDesktopsW@12
257EnumDisplayDevicesA@16
258EnumDisplayDevicesW@16
259EnumDisplayMonitors@16
260EnumDisplaySettingsA@12
261EnumDisplaySettingsExA@16
262EnumDisplaySettingsExW@16
263EnumDisplaySettingsW@12
264EnumPropsA@8
265EnumPropsExA@12
266EnumPropsExW@12
267EnumPropsW@8
268EnumThreadWindows@12
269EnumWindowStationsA@8
270EnumWindowStationsW@8
271EnumWindows@8
272EqualRect@8
273EvaluateProximityToPolygon@16
274EvaluateProximityToRect@12
275ExcludeUpdateRgn@8
276ExitWindowsEx@8
277FillRect@12
278FindWindowA@8
279FindWindowExA@16
280FindWindowExW@16
281FindWindowW@8
282FlashWindow@8
283FlashWindowEx@4
284FrameRect@12
285FreeDDElParam@8
286FrostCrashedWindow@8
287GetActiveWindow@0
288GetAltTabInfo@20
289GetAltTabInfoA@20
290GetAltTabInfoW@20
291GetAncestor@8
292GetAppCompatFlags2@4
293GetAppCompatFlags@8 ; ReactOS has @4 version http://doxygen.reactos.org/d9/d71/undocuser_8h_a9b76cdc68c523a061c86a40367049ed2.html
294GetAsyncKeyState@4
295GetAutoRotationState@4
296GetCIMSSM@4
297GetCapture@0
298GetCaretBlinkTime@0
299GetCaretPos@4
300GetClassInfoA@12
301GetClassInfoExA@12
302GetClassInfoExW@12
303GetClassInfoW@12
304GetClassLongA@8
305GetClassLongW@8
306GetClassNameA@12
307GetClassNameW@12
308GetClassWord@8
309GetClientRect@8
310GetClipCursor@4
311GetClipboardAccessToken@8
312GetClipboardData@4
313GetClipboardFormatNameA@12
314GetClipboardFormatNameW@12
315GetClipboardOwner@0
316GetClipboardSequenceNumber@0
317GetClipboardViewer@0
318GetComboBoxInfo@8
319GetCurrentInputMessageSource@4
320GetCursor@0
321GetCursorFrameInfo@20
322GetCursorInfo@4
323GetCursorPos@4
324GetDC@4
325GetDCEx@12
326GetDesktopID@8
327GetDesktopWindow@0
328GetDialogBaseUnits@0
329GetDisplayAutoRotationPreferences@4
330GetDisplayConfigBufferSizes@12
331GetDlgCtrlID@4
332GetDlgItem@8
333GetDlgItemInt@16
334GetDlgItemTextA@16
335GetDlgItemTextW@16
336GetDoubleClickTime@0
337GetDpiForMonitorInternal@16
338GetFocus@0
339GetForegroundWindow@0
340GetGUIThreadInfo@8
341GetGestureConfig@24
342GetGestureExtraArgs@12
343GetGestureInfo@8
344GetGuiResources@8
345GetIconInfo@8
346GetIconInfoExA@8
347GetIconInfoExW@8
348GetInputDesktop@0
349GetInputLocaleInfo@8
350GetInputState@0
351GetInternalWindowPos@12
352GetKBCodePage@0
353GetKeyNameTextA@12
354GetKeyNameTextW@12
355GetKeyState@4
356GetKeyboardLayout@4
357GetKeyboardLayoutList@8
358GetKeyboardLayoutNameA@4
359GetKeyboardLayoutNameW@4
360GetKeyboardState@4
361GetKeyboardType@4
362GetLastActivePopup@4
363GetLastInputInfo@4
364GetLayeredWindowAttributes@16
365GetListBoxInfo@4
366GetMagnificationDesktopColorEffect@4
367GetMagnificationDesktopMagnification@12
368GetMagnificationLensCtxInformation@16
369GetMenu@4
370GetMenuBarInfo@16
371GetMenuCheckMarkDimensions@0
372GetMenuContextHelpId@4
373GetMenuDefaultItem@12
374GetMenuInfo@8
375GetMenuItemCount@4
376GetMenuItemID@8
377GetMenuItemInfoA@16
378GetMenuItemInfoW@16
379GetMenuItemRect@16
380GetMenuState@12
381GetMenuStringA@20
382GetMenuStringW@20
383GetMessageA@16
384GetMessageExtraInfo@0
385GetMessagePos@0
386GetMessageTime@0
387GetMessageW@16
388GetMonitorInfoA@8
389GetMonitorInfoW@8
390GetMouseMovePointsEx@20
391GetNextDlgGroupItem@12
392GetNextDlgTabItem@12
393GetOpenClipboardWindow@0
394GetParent@4
395GetPhysicalCursorPos@4
396GetPointerCursorId@8
397GetPointerDevice@8
398GetPointerDeviceCursors@12
399GetPointerDeviceProperties@12
400GetPointerDeviceRects@12
401GetPointerDevices@8
402GetPointerFrameInfo@12
403GetPointerFrameInfoHistory@16
404GetPointerFramePenInfo@12
405GetPointerFramePenInfoHistory@16
406GetPointerFrameTouchInfo@12
407GetPointerFrameTouchInfoHistory@16
408GetPointerInfo@8
409GetPointerInfoHistory@12
410GetPointerInputTransform@12
411GetPointerPenInfo@8
412GetPointerPenInfoHistory@12
413GetPointerTouchInfo@8
414GetPointerTouchInfoHistory@12
415GetPointerType@8
416GetPriorityClipboardFormat@8
417GetProcessDefaultLayout@4
418GetProcessDpiAwarenessInternal@8
419GetProcessWindowStation@0
420GetProgmanWindow@0
421GetPropA@8
422GetPropW@8
423GetQueueStatus@4
424GetRawInputBuffer@12
425GetRawInputData@20
426GetRawInputDeviceInfoA@16
427GetRawInputDeviceInfoW@16
428GetRawInputDeviceList@12
429GetRawPointerDeviceData@20
430GetReasonTitleFromReasonCode@12
431GetRegisteredRawInputDevices@12
432GetQueueStatus@4
433GetScrollBarInfo@12
434GetScrollInfo@12
435GetScrollPos@8
436GetScrollRange@16
437GetSendMessageReceiver@4
438GetShellWindow@0
439GetSubMenu@8
440GetSysColor@4
441GetSysColorBrush@4
442GetSystemMenu@8
443GetSystemMetrics@4
444GetTabbedTextExtentA@20
445GetTabbedTextExtentW@20
446GetTaskmanWindow@0
447GetThreadDesktop@4
448GetTitleBarInfo@8
449GetTopLevelWindow@4
450GetTopWindow@4
451GetTouchInputInfo@16
452GetUnpredictedMessagePos@0
453GetUpdateRect@12
454GetUpdateRgn@12
455GetUpdatedClipboardFormats@12
456GetUserObjectInformationA@20
457GetUserObjectInformationW@20
458GetUserObjectSecurity@20
459GetWinStationInfo@4
460GetWindow@8
461GetWindowBand@8
462GetWindowCompositionAttribute@8
463GetWindowCompositionInfo@8
464GetWindowContextHelpId@4
465GetWindowDC@4
466GetWindowDisplayAffinity@8
467GetWindowFeedbackSetting@20
468GetWindowInfo@8
469GetWindowLongA@8
470GetWindowLongW@8
471GetWindowMinimizeRect@8
472GetWindowModuleFileName@12
473GetWindowModuleFileNameA@12
474GetWindowModuleFileNameW@12
475GetWindowPlacement@8
476GetWindowRect@8
477GetWindowRgn@8
478GetWindowRgnBox@8
479GetWindowRgnEx@12
480GetWindowTextA@12
481GetWindowTextLengthA@4
482GetWindowTextLengthW@4
483GetWindowTextW@12
484GetWindowThreadProcessId@8
485GetWindowWord@8
486GhostWindowFromHungWindow@4
487GrayStringA@36
488GrayStringW@36
489HideCaret@4
490HiliteMenuItem@16
491HungWindowFromGhostWindow@4
492IMPGetIMEA@8
493IMPGetIMEW@8
494IMPQueryIMEA@4
495IMPQueryIMEW@4
496IMPSetIMEA@8
497IMPSetIMEW@8
498ImpersonateDdeClientWindow@8
499InSendMessage@0
500InSendMessageEx@4
501InflateRect@12
502InitializeLpkHooks@4
503InitializeWin32EntryTable@4
504InitializeTouchInjection@8
505InjectTouchInput@8
506InsertMenuA@20
507InsertMenuItemA@16
508InsertMenuItemW@16
509InsertMenuW@20
510InternalGetWindowIcon@8
511;ord_2001@4 @2001
512;ord_2002@4 @2002
513InternalGetWindowText@12
514IntersectRect@12
515;ord_2005@4 @2005
516InvalidateRect@12
517InvalidateRgn@12
518InvertRect@8
519IsCharAlphaA@4
520;ord_2010@16 @2010
521IsCharAlphaNumericA@4
522IsCharAlphaNumericW@4
523IsCharAlphaW@4
524IsCharLowerA@4
525IsCharLowerW@4
526IsCharUpperA@4
527IsCharUpperW@4
528IsChild@8
529IsClipboardFormatAvailable@4
530IsDialogMessage@8
531IsDialogMessageA@8
532IsDialogMessageW@8
533IsDlgButtonChecked@8
534IsGUIThread@4
535IsHungAppWindow@4
536IsIconic@4
537IsImmersiveProcess@4
538IsInDesktopWindowBand@4
539IsMenu@4
540IsProcess16Bit@0
541IsMouseInPointerEnabled@0
542IsProcessDPIAware@0
543IsQueueAttached@0
544IsRectEmpty@4
545IsSETEnabled@0
546IsServerSideWindow@4
547IsThreadDesktopComposited@0
548IsTopLevelWindow@4
549IsTouchWindow@8
550IsWinEventHookInstalled@4
551IsWindow@4
552IsWindowEnabled@4
553IsWindowInDestroy@4
554IsWindowRedirectedForPrint@4
555IsWindowUnicode@4
556IsWindowVisible@4
557IsWow64Message@0
558IsZoomed@4
559KillSystemTimer@8
560KillTimer@8
561LoadAcceleratorsA@8
562LoadAcceleratorsW@8
563LoadBitmapA@8
564LoadBitmapW@8
565LoadCursorA@8
566LoadCursorFromFileA@4
567LoadCursorFromFileW@4
568;ord_2000@0 @2000
569;ord_2001@4 @2001
570;ord_2002@4 @2002
571LoadCursorW@8
572LoadIconA@8
573;ord_2005@4 @2005
574LoadIconW@8
575LoadImageA@24
576LoadImageW@24
577LoadKeyboardLayoutA@8
578LoadKeyboardLayoutEx@12
579LoadKeyboardLayoutW@8
580LoadLocalFonts@0
581LoadMenuA@8
582LoadMenuIndirectA@4
583LoadMenuIndirectW@4
584LoadMenuW@8
585LoadRemoteFonts@0
586LoadStringA@16
587LoadStringW@16
588LockSetForegroundWindow@4
589LockWindowStation@4
590LockWindowUpdate@4
591LockWorkStation@0
592LogicalToPhysicalPoint@8
593LogicalToPhysicalPointForPerMonitorDPI@8
594LookupIconIdFromDirectory@8
595LookupIconIdFromDirectoryEx@20
596MBToWCSEx@24
597MBToWCSExt@20
598MB_GetString@4
599MapDialogRect@8
600MapVirtualKeyA@8
601MapVirtualKeyExA@12
602MapVirtualKeyExW@12
603MapVirtualKeyW@8
604MapWindowPoints@16
605MenuItemFromPoint@16
606MenuWindowProcA@20
607MenuWindowProcW@20
608MessageBeep@4
609MessageBoxA@16
610MessageBoxExA@20
611MessageBoxExW@20
612MessageBoxIndirectA@4
613MessageBoxIndirectW@4
614MessageBoxTimeoutA@24
615MessageBoxTimeoutW@24
616MessageBoxW@16
617ModifyMenuA@20
618ModifyMenuW@20
619MonitorFromPoint@12
620MonitorFromRect@8
621MonitorFromWindow@8
622MoveWindow@24
623MsgWaitForMultipleObjects@20
624MsgWaitForMultipleObjectsEx@20
625NotifyOverlayWindow@8
626NotifyWinEvent@16
627OemKeyScan@4
628OemToCharA@8
629OemToCharBuffA@12
630OemToCharBuffW@12
631OemToCharW@8
632OffsetRect@12
633OpenClipboard@4
634OpenDesktopA@16
635OpenDesktopW@16
636OpenIcon@4
637OpenInputDesktop@12
638OpenThreadDesktop@16
639OpenWindowStationA@12
640OpenWindowStationW@12
641PackDDElParam@12
642PackTouchHitTestingProximityEvaluation@8
643PaintDesktop@4
644PaintMenuBar@24
645PaintMonitor@12
646PeekMessageA@20
647PeekMessageW@20
648PhysicalToLogicalPoint@8
649PhysicalToLogicalPointForPerMonitorDPI@8
650PostMessageA@16
651PostMessageW@16
652PostQuitMessage@4
653PostThreadMessageA@16
654PostThreadMessageW@16
655PrintWindow@12
656PrivateExtractIconExA@20
657PrivateExtractIconExW@20
658PrivateExtractIconsA@32
659PrivateExtractIconsW@32
660PrivateSetDbgTag@8
661PrivateSetRipFlags@8
662PrivateRegisterICSProc@4
663PtInRect@12
664QueryBSDRWindow@0
665QueryDisplayConfig@24
666QuerySendMessage@4
667QueryUserCounters@20
668RealChildWindowFromPoint@12
669RealGetWindowClass@12
670RealGetWindowClassA@12
671RealGetWindowClassW@12
672ReasonCodeNeedsBugID@4
673ReasonCodeNeedsComment@4
674RecordShutdownReason@4
675RedrawWindow@16
676RegisterBSDRWindow@8
677RegisterClassA@4
678RegisterClassExA@4
679RegisterClassExW@4
680RegisterClassW@4
681RegisterClipboardFormatA@4
682RegisterClipboardFormatW@4
683RegisterDeviceNotificationA@12
684RegisterDeviceNotificationW@12
685RegisterErrorReportingDialog@8
686RegisterFrostWindow@8
687RegisterGhostWindow@8
688RegisterHotKey@16
689RegisterPowerSettingNotification@12
690RegisterLogonProcess@8
691RegisterMessagePumpHook@4
692RegisterPointerDeviceNotifications@8
693RegisterPointerInputTarget@8
694RegisterPowerSettingNotification@12
695RegisterRawInputDevices@12
696RegisterServicesProcess@4
697RegisterSessionPort@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
698RegisterShellHookWindow@4
699RegisterSuspendResumeNotification@8
700RegisterSystemThread@8
701RegisterTasklist@4
702RegisterTouchHitTestingWindow@8
703RegisterTouchWindow@8
704RegisterUserApiHook@4 ; Prototype changed in 2003 - https://www.reactos.org/wiki/Techwiki:RegisterUserApiHook
705RegisterWindowMessageA@4
706RegisterWindowMessageW@4
707ReleaseCapture@0
708ReleaseDC@8
709RemoveClipboardFormatListener@4
710RemoveMenu@12
711RemovePropA@8
712RemovePropW@8
713ReplyMessage@4
714ResolveDesktopForWOW@4
715ReuseDDElParam@20
716ScreenToClient@8
717ScrollChildren@12
718ScrollDC@28
719ScrollWindow@20
720ScrollWindowEx@32
721SendDlgItemMessageA@20
722SendDlgItemMessageW@20
723SendIMEMessageExA@8
724SendIMEMessageExW@8
725SendInput@12
726SendMessageA@16
727SendMessageCallbackA@24
728SendMessageCallbackW@24
729SendMessageTimeoutA@28
730SendMessageTimeoutW@28
731SendMessageW@16
732SendNotifyMessageA@16
733SendNotifyMessageW@16
734SetActiveWindow@4
735SetCapture@4
736SetCaretBlinkTime@4
737SetCaretPos@8
738SetClassLongA@12
739SetClassLongW@12
740SetClassWord@12
741SetClipboardData@8
742SetClipboardViewer@4
743SetConsoleReserveKeys@8
744SetCoalescableTimer@20
745SetCursor@4
746SetCursorContents@8
747SetCursorPos@8
748SetDebugErrorLevel@4
749SetDeskWallpaper@4
750SetDisplayAutoRotationPreferences@4
751SetDisplayConfig@20
752SetDlgItemInt@16
753SetDlgItemTextA@12
754SetDlgItemTextW@12
755SetDoubleClickTime@4
756SetFocus@4
757SetForegroundWindow@4
758SetGestureConfig@20
759SetImmersiveBackgroundWindow@4
760SetInternalWindowPos@16
761SetKeyboardState@4
762SetLastErrorEx@8
763SetLayeredWindowAttributes@16
764SetLogonNotifyWindow@4
765SetMagnificationDesktopColorEffect@4
766SetMagnificationDesktopMagnification@16
767SetMagnificationLensCtxInformation@16
768SetMenu@8
769SetMenuContextHelpId@8
770SetMenuDefaultItem@12
771SetMenuInfo@8
772SetMenuItemBitmaps@20
773SetMenuItemInfoA@16
774SetMenuItemInfoW@16
775SetMessageExtraInfo@4
776SetMessageQueue@4
777SetMirrorRendering@8
778SetParent@8
779SetPhysicalCursorPos@8
780SetProcessDPIAware@0
781SetProcessDefaultLayout@4
782SetProcessDpiAwarenessInternal@4
783SetProcessRestrictionExemption@4
784SetProcessWindowStation@4
785SetProgmanWindow@4
786SetPropA@12
787SetPropW@12
788SetRect@20
789SetRectEmpty@4
790SetScrollInfo@16
791SetScrollPos@16
792SetScrollRange@20
793SetShellWindow@4
794SetShellWindowEx@8
795SetSysColors@12
796SetSysColorsTemp@12
797SetSystemCursor@8
798SetSystemMenu@8
799SetSystemTimer@16
800SetTaskmanWindow@4
801SetThreadDesktop@4
802SetThreadInputBlocked@8
803SetTimer@16
804SetUserObjectInformationA@16
805SetUserObjectInformationW@16
806SetUserObjectSecurity@12
807SetWinEventHook@28
808SetWindowBand@12
809SetWindowCompositionAttribute@8
810SetWindowCompositionTransition@28
811SetWindowContextHelpId@8
812SetWindowDisplayAffinity@8
813SetWindowFeedbackSetting@20
814SetWindowLongA@12
815SetWindowLongW@12
816SetWindowPlacement@8
817SetWindowPos@28
818SetWindowRgn@12
819SetWindowRgnEx@12
820SetWindowStationUser@16
821SetWindowTextA@8
822SetWindowTextW@8
823SetWindowWord@12
824SetWindowsHookA@8
825SetWindowsHookExA@16
826SetWindowsHookExW@16
827SetWindowsHookW@8
828SfmDxBindSwapChain@12
829SfmDxGetSwapChainStats@8
830SfmDxOpenSwapChain@16
831SfmDxQuerySwapChainBindingStatus@12
832SfmDxReleaseSwapChain@8
833SfmDxReportPendingBindingsToDwm@0
834SfmDxSetSwapChainBindingStatus@8
835SfmDxSetSwapChainStats@8
836ShowCaret@4
837ShowCursor@4
838ShowOwnedPopups@8
839ShowScrollBar@12
840ShowStartGlass@4
841ShowSystemCursor@4
842ShowWindow@8
843ShowWindowAsync@8
844ShutdownBlockReasonCreate@8
845ShutdownBlockReasonDestroy@4
846ShutdownBlockReasonQuery@12
847SignalRedirectionStartComplete@0
848SkipPointerFrameMessages@4
849SoftModalMessageBox@4
850SoundSentry@0
851SubtractRect@12
852SwapMouseButton@4
853SwitchDesktop@4
854SwitchDesktopWithFade@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/
855SwitchToThisWindow@8
856SystemParametersInfoA@16
857SystemParametersInfoW@16
858TabbedTextOutA@32
859TabbedTextOutW@32
860TileChildWindows@8
861TileWindows@20
862ToAscii@20
863ToAsciiEx@24
864ToUnicode@24
865ToUnicodeEx@28
866TrackMouseEvent@4
867TrackPopupMenu@28
868TrackPopupMenuEx@24
869TranslateAccelerator@12
870TranslateAcceleratorA@12
871TranslateAcceleratorW@12
872TranslateMDISysAccel@8
873TranslateMessage@4
874TranslateMessageEx@8
875UnhookWinEvent@4
876UnhookWindowsHook@8
877UnhookWindowsHookEx@4
878UnionRect@12
879UnloadKeyboardLayout@4
880UnlockWindowStation@4
881UnpackDDElParam@16
882UnregisterClassA@8
883UnregisterClassW@8
884UnregisterDeviceNotification@4
885UnregisterHotKey@8
886UnregisterMessagePumpHook@0
887UnregisterPointerInputTarget@8
888UnregisterPowerSettingNotification@4
889UnregisterSessionPort@0
890UnregisterSuspendResumeNotification@4
891UnregisterTouchWindow@4
892UnregisterUserApiHook@0
893UpdateDefaultDesktopThumbnail@20
894UpdateLayeredWindow@36
895UpdateLayeredWindowIndirect@8
896UpdatePerUserSystemParameters@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
897UpdateWindow@4
898UpdateWindowInputSinkHints@8
899UpdateWindowTransform@12
900User32InitializeImmEntryTable@4
901UserClientDllInitialize@12
902UserHandleGrantAccess@12
903UserLpkPSMTextOut@24
904UserLpkTabbedTextOut@48
905UserRealizePalette@4
906UserRegisterWowHandlers@8
907VRipOutput@0
908VTagOutput@0
909ValidateRect@8
910ValidateRgn@8
911VkKeyScanA@4
912VkKeyScanExA@8
913VkKeyScanExW@8
914VkKeyScanW@4
915WCSToMBEx@24
916WINNLSEnableIME@8
917WINNLSGetEnableStatus@4
918WINNLSGetIMEHotkey@4
919WaitForInputIdle@8
920WaitForRedirectionStartComplete@0
921WaitMessage@0
922Win32PoolAllocationStats@24
923WinHelpA@16
924WinHelpW@16
925WindowFromDC@4
926WindowFromPhysicalPoint@8
927WindowFromPoint@8
928_UserTestTokenForInteractive@8
929gSharedInfo DATA
930gapfnScSendMessage DATA
931keybd_event@16
932mouse_event@20
933wsprintfA
934wsprintfW
935wvsprintfA@12
936wvsprintfW@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
965IsThreadMessageQueueAttached@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{};
373373
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;
377377
...@@ -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 allocate420 /// 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}
619619
620test "std.ArrayList.init" {620test "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();
623624
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 }
627628
628test "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}
634631
635test "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}
638636
637test "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}
645652
653test "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 }
652676
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 }
656718
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);
659721
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);
666728
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);
669731
670 // can only set on indices < self.items.len732 // 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;
673735
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}
677740
678test "std.ArrayList.appendNTimes" {741test "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);
681756
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}
688764
689test "std.ArrayList.appendNTimes with failing allocator" {765test "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}
694778
695test "std.ArrayList.orderedRemove" {779test "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);
698810
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);
706818
707 //remove from middle819 //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);
711823
712 //remove from end824 //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);
715827
716 //remove from front828 //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}
721834
722test "std.ArrayList.swapRemove" {835test "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();
725840
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);
733848
734 //remove from middle849 //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);
738853
739 //remove from end854 //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);
742857
743 //remove from front858 //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}
748890
749test "std.ArrayList.insert" {891test "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();
752896
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}
762920
763test "std.ArrayList.insertSlice" {921test "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();
766926
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);
778938
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}
784966
785test "std.ArrayList.replaceRange" {967test "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;
788971
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 };
792974
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 };
797979
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);
802984 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);
806988 try list_lt.appendSlice(&init);
807 // after_range > new_items.len in function body989 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);
810992 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}
8161029
817const Item = struct {1030const 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};
8211034
822test "std.ArrayList: ArrayList(T) of struct T" {1035const 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
1040test "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}
8281055
829test "std.ArrayList(u8) implements outStream" {1056test "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}
8391066
840test "std.ArrayList.shrink still sets length on error.OutOfMemory" {1067test "std.ArrayList/ArrayListUnmanaged.shrink still sets length on error.OutOfMemory" {
841 // use an arena allocator to make sure realloc returns error.OutOfMemory1068 // 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;
8441072
845 var list = ArrayList(i32).init(&arena.allocator);1073 {
1074 var list = ArrayList(i32).init(a);
8461075
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);
8501079
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}
8541094
855test "std.ArrayList.writer" {1095test "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}
8661106
867test "addManyAsArray" {1107test "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,
12341234
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,
12361239
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 ELF1242 /// 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,
12421246
1243 /// Place every function in its own section so that unused ones may be1247 /// 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 }
13851389
1386 fn computeOutFileNames(self: *LibExeObjStep) void {1390 fn computeOutFileNames(self: *LibExeObjStep) void {
1387 // TODO make this call std.zig.binNameAlloc1391 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 }
14491437
...@@ -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 }
20422030
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 }
21692165
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 }
21742170
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 {
23352331
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};
100100
101/// This data structure is used by the Zig language code generation and
102/// therefore must be kept in sync with the compiler implementation.
103pub 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 and111/// 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.
103pub const AtomicRmwOp = enum {113pub 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 };
266277
267 /// This data structure is used by the Zig language code generation and278 /// 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 };
322334
323 /// This data structure is used by the Zig language code generation and335 /// 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;
342pub extern "c" fn fdatasync(fd: c_int) c_int;342pub extern "c" fn fdatasync(fd: c_int) c_int;
343343
344pub extern "c" fn prctl(option: c_int, ...) c_int;344pub extern "c" fn prctl(option: c_int, ...) c_int;
345
346pub extern "c" fn getrlimit(resource: rlimit_resource, rlim: *rlimit) c_int;
347pub 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");
1212
13extern "c" fn __error() *c_int;13extern "c" fn __error() *c_int;
14pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32;14pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32;
15pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int;15pub extern "c" fn _NSGetExecutablePath(buf: [*:0]u8, bufsize: *u32) c_int;
16pub extern "c" fn _dyld_image_count() u32;16pub extern "c" fn _dyld_image_count() u32;
17pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header;17pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header;
18pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize;18pub 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_
100100
101pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: c_uint) c_int;101pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: c_uint) c_int;
102102
103pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *const rlimit, old_limit: *rlimit) c_int;
104
103pub const pthread_attr_t = extern struct {105pub 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 copies4// 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.
66
7/// Authenticated Encryption with Associated Data
8pub 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.
21pub 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.
27pub 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.
40pub const dh = struct {
41 pub const X25519 = @import("crypto/25519/x25519.zig").X25519;
42};
43
44/// Elliptic-curve arithmetic.
45pub 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.
8pub const hash = struct {52pub 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};
1761
18/// Authentication (MAC) functions.62/// Key derivation functions.
19pub const auth = struct {63pub 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
25pub 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};
3466
35/// MAC functions requiring single-use secret keys.67/// MAC functions requiring single-use secret keys.
36pub const onetimeauth = struct {68pub 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};
3972
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};
5992
60/// Core functions, that should rarely be used directly by applications.
61pub 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.
74pub 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.
81pub const dh = struct {
82 pub const X25519 = @import("crypto/25519/x25519.zig").X25519;
83};
84
85/// Digital signature functions.93/// Digital signature functions.
86pub const sign = struct {94pub 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 }
308308
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 }
317319
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 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const builtin = std.builtin;
4const crypto = std.crypto;
5const debug = std.debug;
6const Ghash = std.crypto.onetimeauth.Ghash;
7const mem = std.mem;
8const modes = crypto.core.modes;
9
10pub const AES128GCM = AESGCM(crypto.core.aes.AES128);
11pub const AES256GCM = AESGCM(crypto.core.aes.AES256);
12
13fn 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
101const htest = @import("test.zig");
102const testing = std.testing;
103
104test "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
117test "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
129test "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
146test "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}
5858
59const macs = [_]Crypto{59const 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};
155158
156pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 {159pub 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
9const std = @import("../std.zig");
10const assert = std.debug.assert;
11const math = std.math;
12const 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.
20pub 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
300const htest = @import("test.zig");
301
302test "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 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const hmac = std.crypto.auth.hmac;
4const mem = std.mem;
5
6/// HKDF-SHA256
7pub const HkdfSha256 = Hkdf(hmac.sha2.HmacSha256);
8
9/// HKDF-SHA512
10pub 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.
14pub 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
54const htest = @import("test.zig");
55
56test "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 buffer22 // partial block buffer
23 buf: [block_size]u8 align(16) = undefined,23 buf: [block_size]u8 align(16) = undefined,
2424
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 }
117116
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 }
189200
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 }
649649
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 when677 /// 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
1522var testRunDetachedData: usize = 0;
1523test "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
1548fn 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}
11831183
1184pub 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
1190test "comptimePrint" {
1191 std.testing.expectEqualSlices(u8, "100", comptime comptimePrint("{}", .{100}));
1192}
1193
1184test "parse u64 digit too big" {1194test "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};
18581858
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.
1862pub fn cwd() Dir {1862pub 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}
21642164
2165pub const SelfExePathError = os.ReadLinkError || os.SysCtlError;2165pub const SelfExePathError = os.ReadLinkError || os.SysCtlError || os.RealPathError;
21662166
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 pointer2190/// TODO make the return type of this a null terminated pointer
2191pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {2191pub 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 }
617617
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 WriteFileScatter620 // 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 {
632632
633 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in633 /// 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;
637637
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 {
652652
653 pub const CopyRangeError = os.CopyFileRangeError;653 pub const CopyRangeError = os.CopyFileRangeError;
654654
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 }
658660
659 /// Returns the number of bytes copied. If the number read is smaller than `buffer.len`, it661 /// 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 variables1257 /// 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.
1262pub 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.
1265pub const REFERENCE_FLAG_UNDEFINED_LAZY: u16 = 0x1;
1266
1267/// This symbol is defined in this module.
1268pub 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.
1271pub 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.
1275pub 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.
1279pub 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.
1284pub const REFERENCED_DYNAMICALLY: u16 = 0x10;
1285
1286/// Used by the dynamic linker at runtime. Do not set this bit.
1287pub 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.
1292pub 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.
1297pub 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)
1303pub 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}
6060
61pub 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 bits66/// a + b * c + *carry, sets carry to the overflow bits
62pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {67pub 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 }
599604
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};
17791853
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}
21312205
2206/// Knuth 4.6.3
2207fn 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 value2256// Storage must live for the lifetime of the returned value
2133fn fixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Mutable {2257fn 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" {
14801480
1481 testing.expect(a.toConst().eq(b.toConst()));1481 testing.expect(a.toConst().eq(b.toConst()));
1482}1482}
1483
1484test "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 }
859860
...@@ -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 }
889891
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 `.`, `..`, and3993/// 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`.
3997pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {3997pub 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
5414pub const GetrlimitError = UnexpectedError;
5415
5416pub 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
5428pub const SetrlimitError = error{
5429 PermissionDenied,
5430} || UnexpectedError;
5431
5432pub 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
1895pub 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
1955pub const rlim_t = u64;
1956
1957/// No limit
1958pub const RLIM_INFINITY = ~@as(rlim_t, 0);
1959
1960pub const RLIM_SAVED_MAX = RLIM_INFINITY;
1961pub const RLIM_SAVED_CUR = RLIM_INFINITY;
1962
1963pub 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}
12651265
1266pub 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
1271pub 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
1276pub 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
1266test "" {1286test "" {
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
595test "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}
320320
321const we_are_testing_this_with_stage1_which_leaks_comptime_memory = true;
322
321test "PackedIntArray" {323test "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;
324327
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}
359362
360test "PackedIntArray init" {363test "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" {
367test "PackedIntSlice" {371test "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;
370375
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}
406411
407test "PackedIntSlice of PackedInt(Array/Slice)" {412test "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;
410416
...@@ -470,6 +476,7 @@ test "PackedIntSlice of PackedInt(Array/Slice)" {...@@ -470,6 +476,7 @@ test "PackedIntSlice of PackedInt(Array/Slice)" {
470}476}
471477
472test "PackedIntSlice accumulating bit offsets" {478test "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 catch480 //bit_offset is u3, so standard debugging asserts should catch
474 // anything481 // 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 tested505// big endian values were not tested
499test "PackedInt(Array/Slice) sliceCast" {506test "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}
538547
539test "PackedInt(Array/Slice)Endian" {548test "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 we615// 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.
606test "PackedIntArray at end of available memory" {617test "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}
624637
625test "PackedIntSlice at end of available memory" {638test "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 version213 // 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 else218 else
217 release;219 release;
218220
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);
8const Target = std.Target;8const Target = std.Target;
99
10const Value = @import("value.zig").Value;10const Value = @import("value.zig").Value;
11const Type = @import("type.zig").Type;
11const target_util = @import("target.zig");12const target_util = @import("target.zig");
12const Package = @import("Package.zig");13const Package = @import("Package.zig");
13const link = @import("link.zig");14const 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 or450 options.system_libs.len != 0 or
448 options.link_libc or options.link_libcpp or451 options.link_libc or options.link_libcpp or
449 options.link_eh_frame_hdr or452 options.link_eh_frame_hdr or
453 options.link_emit_relocs or
450 options.output_mode == .Lib or454 options.output_mode == .Lib or
451 options.lld_argv.len != 0 or455 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 }
478487
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 {
638647
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 }
10271053
...@@ -1146,7 +1172,15 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {...@@ -1146,7 +1172,15 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
1146 };1172 };
1147}1173}
11481174
1149pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void {1175pub 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;
13111345
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}
14721506
1473fn updateCObject(comp: *Compilation, c_object: *CObject) !void {1507fn 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;
15141548
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 object1556 // 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 go1557 // 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}
25082548
2509fn updateStage1Module(comp: *Compilation) !void {2549fn 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();
25122552
...@@ -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);
25532595
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;
26192657
2620 comp.stage1_cache_manifest = &man;2658 comp.stage1_cache_manifest = &man;
26212659
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.
71global_error_set: std.StringHashMapUnmanaged(u16) = .{},71global_error_set: std.StringHashMapUnmanaged(u16) = .{},
7272
73/// Keys are fully qualified paths
74import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},
75
73/// Incrementing integer used to compare against the corresponding Decl76/// Incrementing integer used to compare against the corresponding Decl
74/// field to determine whether a Decl's status applies to an ongoing update, or a77/// 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 {
532535
533 /// Direct children of the file.536 /// Direct children of the file.
534 decls: std.AutoArrayHashMapUnmanaged(*Decl, void),537 decls: std.AutoArrayHashMapUnmanaged(*Decl, void),
535538 ty: Type,
536 // TODO implement container types and put this in a status union
537 // ty: Type
538539
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 }
543547
...@@ -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}
858867
859fn freeExportList(gpa: *Allocator, export_list: []*Export) void {868fn 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}
23832392
2393pub 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.
2385pub fn cmpNumeric(2433pub 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}
19751975
1976fn 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
1976fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {1985fn 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;
17const leb128 = std.debug.leb;17const leb128 = std.debug.leb;
18const log = std.log.scoped(.codegen);18const log = std.log.scoped(.codegen);
1919
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.
24pub const BlockData = struct {21pub 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 only32/// 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.
37pub const AnyMCValue = extern struct {34pub const AnyMCValue = extern struct {
38 a: u64,35 a: usize,
39 b: u64,36 b: u64,
40};37};
4138
...@@ -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 pointer169 // 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 integer204 // 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 }
405400
...@@ -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(.{});
441436
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 {
586614
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;
12761304
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 }
14651521
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 else1532 else
1477 unreachable;1533 unreachable;
14781534
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 };
17141779
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 something1858 // 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 something1882 // 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 overwrite1945 // break instruction will choose a MCValue for the block result and overwrite
1881 // this field. Following break instructions will use that MCValue to put their1946 // 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);
18861951
...@@ -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);
21632228
2164 // mov DWORD PTR [rbp+offset+4], immediate2229 // 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]);
21672232
2168 // mov DWORD PTR [rbp+offset], immediate2233 // 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 (use2278 // 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 on2284 // TODO Use movw Note: Not supported on
2220 // all ARM targets!2285 // all ARM targets!
22212286 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: Not2289 // TODO Use movw and movt Note: Not
2226 // supported on all ARM targets! Also TODO2290 // 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, 242296 // orr reg, reg, #0xbb, 24
2233 // orr reg, reg, #0xcc, 162297 // orr reg, reg, #0xcc, 16
2234 // orr reg, reg, #0xdd, 82298 // 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}
115115
116/// Program status registers containing flags, mode bits and other
117/// vital information
118pub const Psr = enum {
119 cpsr,
120 spsr,
121};
122
116pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };123pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };
117pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };124pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
118pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };125pub 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 },
237255
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 };
244262
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 };
340358
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) {
442506
443 // Data processing507 // Data processing
444508
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 }
448544
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 }
452548
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 }
456552
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 }
460556
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 }
464560
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 }
468564
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 }
472568
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 }
476572
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 }
492588
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 }
496600
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 }
500604
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 }
504608
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 }
508626
509 // Single data transfer627 // Single data transfer
510628
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 }
514661
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 }
518665
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 // Branch698 // Branch
520699
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};
553784
554test "serialize instructions" {785test "serialize instructions" {
...@@ -559,23 +790,31 @@ test "serialize instructions" {...@@ -559,23 +790,31 @@ test "serialize instructions" {
559790
560 const testcases = [_]Testcase{791 const testcases = [_]Testcase{
561 .{ // add r0, r0, r0792 .{ // 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, r2796 .{ // 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, #42800 .{ // 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 #12820 .{ // 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 };
602849
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
856test "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 {
689689
690const all_map_basename = "all.map";690const all_map_basename = "all.map";
691691
692// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
693// zig fmt: off
694
695pub fn buildSharedObjects(comp: *Compilation) !void {692pub 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 {
827824
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 object51 /// 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;
2222
23const section_alignment = 4096;23const section_alignment = 4096;
24const file_alignment = 512;24const file_alignment = 512;
25const image_base = 0x400_000;25const default_image_base = 0x400_000;
26const section_table_size = 2 * 40;26const section_table_size = 2 * 40;
27comptime {27comptime {
28 assert(mem.isAligned(image_base, section_alignment));28 assert(mem.isAligned(default_image_base, section_alignment));
29}29}
3030
31pub const base_tag: link.File.Tag = .coff;31pub 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 indices55/// Free list of offset table indices
56offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},56offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
5757
58/// Virtual address of the entry point procedure relative to `image_base`58/// Virtual address of the entry point procedure relative to image base.
59entry_addr: ?u32 = null,59entry_addr: ?u32 = null,
6060
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
183183
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;
191191
192 // Size of file when loaded in memory192 // 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);
194194
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;
235235
236 // Image base address236 // 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 address240 // 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 }
244244
...@@ -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 {
601601
602 // Write .text new virtual address602 // 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);
606606
607 // Fix the VAs in the offset table607 // 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 }
755755
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 }
917921
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");
2727
28const default_entry_addr = 0x8000000;28const default_entry_addr = 0x8000000;
2929
30// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
31// zig fmt: off
32
33pub const base_tag: File.Tag = .elf;30pub const base_tag: File.Tag = .elf;
3431
35base: File,32base: 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
273270
274pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {271pub 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 127749 // 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, // header752 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 sentinel758 0, // table sentinel
762 abbrev_subprogram, DW.TAG_subprogram,759 abbrev_subprogram,
760 DW.TAG_subprogram,
763 DW.CHILDREN_yes, // header761 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 sentinel765 DW.FORM_data4,
768 abbrev_subprogram_retvoid,766 DW.AT_type,
769 DW.TAG_subprogram, DW.CHILDREN_yes, // header767 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 sentinel777 0, // table sentinel
774 abbrev_base_type, DW.TAG_base_type,778 abbrev_base_type,
779 DW.TAG_base_type,
775 DW.CHILDREN_no, // header780 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 sentinel783 DW.AT_byte_size,
779784 DW.FORM_data1,
780 abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header785 DW.AT_name,
781 0, 0, // table sentinel786 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, // header790 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 sentinel795 0, // table sentinel
788 0, 0,796 0,
797 0,
789 0, // section sentinel798 0, // section sentinel
790 };799 };
791800
...@@ -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 Zig1292 // 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 {
13171327
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 });
13311341
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 }
13541364
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 }
13671381
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 {
14801499
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 else1503 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);
14881507
...@@ -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 without1659 // 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 like2892 // 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,
3034
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 }
3947
...@@ -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 }
4860
...@@ -56,30 +68,52 @@ base: File,...@@ -56,30 +68,52 @@ base: File,
5668
57/// Table of all load commands69/// Table of all load commands
58load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},70load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
59segment_cmd_index: ?u16 = null,71/// __PAGEZERO segment
72pagezero_segment_cmd_index: ?u16 = null,
73/// __TEXT segment
74text_segment_cmd_index: ?u16 = null,
75/// __DATA segment
76data_segment_cmd_index: ?u16 = null,
77/// __LINKEDIT segment
78linkedit_segment_cmd_index: ?u16 = null,
79/// Dyld info
80dyld_info_cmd_index: ?u16 = null,
81/// Symbol table
60symtab_cmd_index: ?u16 = null,82symtab_cmd_index: ?u16 = null,
83/// Dynamic symbol table
61dysymtab_cmd_index: ?u16 = null,84dysymtab_cmd_index: ?u16 = null,
85/// Path to dyld linker
86dylinker_cmd_index: ?u16 = null,
87/// Path to libSystem
88libsystem_cmd_index: ?u16 = null,
89/// Data-in-code section of __LINKEDIT segment
62data_in_code_cmd_index: ?u16 = null,90data_in_code_cmd_index: ?u16 = null,
91/// Address to entry point function
92function_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.
96main_cmd_index: ?u16 = null,
6397
64/// Table of all sections98/// Table of all sections
65sections: std.ArrayListUnmanaged(macho.section_64) = .{},99sections: std.ArrayListUnmanaged(macho.section_64) = .{},
66100
67/// __TEXT segment sections101/// __TEXT,__text section
68text_section_index: ?u16 = null,102text_section_index: ?u16 = null,
69cstring_section_index: ?u16 = null,
70const_text_section_index: ?u16 = null,
71stubs_section_index: ?u16 = null,
72stub_helper_section_index: ?u16 = null,
73103
74/// __DATA segment sections104/// __DATA,__got section
75got_section_index: ?u16 = null,105got_section_index: ?u16 = null,
76const_data_section_index: ?u16 = null,
77106
78entry_addr: ?u64 = null,107entry_addr: ?u64 = null,
79108
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).
82symbol_table: std.ArrayListUnmanaged(macho.nlist_64) = .{},111local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
112/// Table of all defined global symbols
113global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
114/// Table of all undefined symbols
115undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
116dyld_stub_binder_index: ?u16 = null,
83117
84/// Table of symbol names aka the string table.118/// Table of symbol names aka the string table.
85string_table: std.ArrayListUnmanaged(u8) = .{},119string_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";
115const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";149const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
116150
117pub const TextBlock = struct {151pub const TextBlock = struct {
118 /// Index into the symbol table152 /// 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 table159 /// 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 block162 /// 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 neighbours166 /// Points to the previous and next neighbours
125 prev: ?*TextBlock,167 prev: ?*TextBlock,
126 next: ?*TextBlock,168 next: ?*TextBlock,
127169
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
156198
157 self.base.file = file;199 self.base.file = file;
158200
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();
198249
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 dyld261 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 }
221268
222 {269 {
223 // Link against libSystem270 // 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 files277 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;
264289 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);
266291 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 }
269296 {
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 }
280315
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 {
669pub fn deinit(self: *MachO) void {715pub 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}
676724
677pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {725pub 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;
679727
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);
682730
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();
686734
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();
689737
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}
699747
700pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {748pub 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});
720767
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];
723770
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.?]});
729775
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;
737784
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.?);
742787
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});
747791
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}
750798
751pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}799pub 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();
761809
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);
763811 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 implement813
766 if (exports.len == 0) return;814 for (exports) |exp| {
767815 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}
773869
774pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}870pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
775871
776pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {872pub 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}
779876
780pub fn populateMissingMetadata(self: *MachO) !void {877pub 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;
819928
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;
823932
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 });
840949
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 });
844999
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}
8681187
869fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {1188fn 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;
8731191
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});
8871208
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 growth1213 assert(needed_size <= text_capacity); // TODO handle growth
8941214
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;
9041219
...@@ -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}
9381253
939fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int {1254fn 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}
9461258
947fn commandSize(min_size: anytype) u32 {1259fn 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}
9501266
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.
951fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {1271fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
952 if (size == 0) return;1272 if (size == 0) return;
9531273
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);
9561276
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 {
9611281
962fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {1282fn 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 }
9891305 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}
10111334
...@@ -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();
10431370
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}
10501377
1378fn 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
1388fn 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
1396fn 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
1406fn 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
1428fn 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
1449fn 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 bookkeeping1500/// 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 script268 \\ -T[script], --script [script] Use a custom linker script
269 \\ --version-script [path] Provide a version .map file269 \\ --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 semver271 \\ --version [ver] Dynamic library semver
273 \\ -rdynamic Add all symbols to the dynamic symbol table272 \\ -rdynamic Add all symbols to the dynamic symbol table
274 \\ -rpath [path] Add directory to the runtime library search path273 \\ -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 linker276 \\ --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 linked278 \\ -dynamic Force output to be dynamically linked
277 \\ -static Force output to be statically linked279 \\ -static Force output to be statically linked
278 \\ -Bsymbolic Bind global references locally280 \\ -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 size282 \\ --stack [size] Override default stack size
283 \\ --image-base [addr] Set base address for executable image
281 \\ -framework [name] (darwin) link against framework284 \\ -framework [name] (darwin) link against framework
282 \\ -F[dir] (darwin) add search path for frameworks285 \\ -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 }
12081226
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
3070fn 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 crtu39 // 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 };
447448
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}
4549
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"
2020
21#ifndef NDEBUG
22#define DBG_MACRO_NO_WARNING
23#include <dbg.h>
24#endif
25
26struct AstNode;21struct AstNode;
27struct ZigFn;22struct ZigFn;
28struct Scope;23struct 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;
14581453
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};
18291826
1830struct BuiltinFnEntry {1827struct BuiltinFnEntry {
...@@ -2440,6 +2437,15 @@ enum AtomicOrder {...@@ -2440,6 +2437,15 @@ enum AtomicOrder {
2440 AtomicOrderSeqCst,2437 AtomicOrderSeqCst,
2441};2438};
24422439
2440// synchronized with code in define_builtin_compile_vars
2441enum 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_vars2449// synchronized with the code in define_builtin_compile_vars
2444enum AtomicRmwOp {2450enum 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};
35223530
3531struct IrInstSrcReduce {
3532 IrInstSrc base;
3533
3534 IrInstSrc *op;
3535 IrInstSrc *value;
3536};
3537
3538struct IrInstGenReduce {
3539 IrInstGen base;
3540
3541 ReduceOp op;
3542 IrInstGen *value;
3543};
3544
3523struct IrInstSrcTruncate {3545struct IrInstSrcTruncate {
3524 IrInstSrc base;3546 IrInstSrc base;
35253547
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 }
26462656
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);
31263135
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 }
32703277
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);
33393346
3340 bool src_have_tag = is_auto_enum || is_explicit_enum;3347 bool src_have_tag = is_auto_enum || is_explicit_enum;
33413348
...@@ -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 }
34073415
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}
25862586
2587enum 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
2597static 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
2617static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type,2587static 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);
26442614
...@@ -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);
26752645
...@@ -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 }
27472717
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 }
27512721
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);
27772747
...@@ -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);
28022772
...@@ -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);
28192789
...@@ -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);
28712841
...@@ -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 }
29302900
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 }
29342904
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);
29922962
...@@ -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);
44194390
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();
44324404
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 }
45104484
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);
48264801
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);
48304805
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}
48334810
4834static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueRef maybe_handle) {4811static 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}
50865065
...@@ -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}
54735452
5453static 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
5474static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutableGen *executable, IrInstGenFence *instruction) {5497static 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 }
77417778
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);
77437781
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}
86358674
8636static const char *bool_to_str(bool b) {8675static 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@"
2425
25#endif26#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}
13141318
1319static constexpr IrInstSrcId ir_inst_id(IrInstSrcReduce *) {
1320 return IrInstSrcIdReduce;
1321}
1322
1315static constexpr IrInstSrcId ir_inst_id(IrInstSrcTruncate *) {1323static 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}
17781786
1787static constexpr IrInstGenId ir_inst_id(IrInstGenReduce *) {
1788 return IrInstGenIdReduce;
1789}
1790
1779static constexpr IrInstGenId ir_inst_id(IrInstGenTruncate *) {1791static 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}
35053517
3518static 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
3529static 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
3506static IrInstSrc *ir_build_truncate(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,3541static 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();
96109661
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}
1593515986
15987static 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
15936static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstGen *value, AtomicOrder *out) {16005static 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 }
2153221602
...@@ -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 }
2157821648
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 }
2158221655
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;
2414524219
...@@ -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 }
2415724231
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);
2415924234
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: u3225106 // 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;
2543425509
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;
2543925515
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);
2544225523
...@@ -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;
2550525586
25506 ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 4);25587 ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 5);
2550725588
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);
2552125602
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;
2552525607
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);
2552825615
...@@ -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);
2555525642
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;
2555825645
25559 // calling_convention: TypeInfo.CallingConvention25646 // 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: bool25656 // 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: ?type25662 // 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.FnArg25681 // 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);
2559625687
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);
2560225693
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);
2560425695
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;
2587525967
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_host25993 0, // bit_offset_in_host
25902 0, // host_int_bytes25994 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 }
2613826234
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);
2615226248
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 }
2615426257
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}
2667826880
26881static 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
26971static 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
26679static IrInstGen *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstSrcFence *instruction) {27036static 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 }
2984030198
...@@ -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);
3087431232
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 }
3087731237
...@@ -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;
3181532178
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;
3198432348
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}
15861590
1591static 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
1587static const char *atomic_order_str(AtomicOrder order) {1599static 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}
16021614
1615static 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
1626static 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
1603static void ir_print_truncate(IrPrintSrc *irp, IrInstSrcTruncate *instruction) {1632static 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;
94extern char **environ;94extern char **environ;
95#endif95#endif
9696
97#if defined(ZIG_OS_POSIX)
98static 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
114static 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
135static 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
153static 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
186void 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
196void os_path_dirname(Buf *full_path, Buf *out_dirname) {97void 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}
282183
283Error 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
317static 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
337bool 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}
346184
347#if defined(ZIG_OS_WINDOWS)185#if defined(ZIG_OS_WINDOWS)
186// Ported from std/os/path.zig
187static 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}
348205
349enum WindowsPathKind {206enum 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) {
748605
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#endif611#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}
788646
789Error 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)
801static 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
895static 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
1011Error 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
1027Error 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
1039Error os_write_file(Buf *full_path, Buf *contents) {647Error 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}
1076684
1077Error 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)
1107static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) {686static 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#endif696#endif
1118697
1119static 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
1143Error 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
1201Error os_copy_file(Buf *src_path, Buf *dest_path) {698Error 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#endif855#endif
1359}856}
1360857
1361Error os_delete_file(Buf *path) {
1362 if (remove(buf_ptr(path))) {
1363 return ErrorFileSystem;
1364 } else {
1365 return ErrorNone;
1366 }
1367}
1368
1369Error os_rename(Buf *src_path, Buf *dest_path) {858Error 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}
1386875
1387OsTimeStamp 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
1411OsTimeStamp os_timestamp_monotonic(void) {876OsTimeStamp 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#endif966#endif
1502}967}
1503968
1504static 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}
1544969
1545int os_init(void) {970int 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}
15821006
1583Error 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#endif1315#endif
1957
1958// Ported from std.os.getAppDataDir
1959Error 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)
2015static 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
2024Error 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
2046Error 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
2143Error 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
2147Error 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
2151Error 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
2239Error 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
2269Error 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
2292Error 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
2337void 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};
7272
73enum TerminationId {
74 TerminationIdClean,
75 TerminationIdSignaled,
76 TerminationIdStopped,
77 TerminationIdUnknown,
78};
79
80struct 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
91struct OsTimeStamp {73struct OsTimeStamp {
92 int64_t sec;74 int64_t sec;
93 int64_t nsec;75 int64_t nsec;
94};76};
9577
96struct OsFileAttr {
97 OsTimeStamp mtime;
98 uint64_t size;
99 uint64_t inode;
100 uint32_t mode;
101};
102
103int os_init(void);78int os_init(void);
10479
105void os_spawn_process(ZigList<const char *> &args, Termination *term);
106Error os_exec_process(ZigList<const char *> &args,
107 Termination *term, Buf *out_stderr, Buf *out_stdout);
108Error os_execv(const char *exe, const char **argv);
109
110void os_path_dirname(Buf *full_path, Buf *out_dirname);80void os_path_dirname(Buf *full_path, Buf *out_dirname);
111void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename);81void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename);
112void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname);82void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname);
113void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path);83void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path);
114Error os_path_real(Buf *rel_path, Buf *out_abs_path);
115Buf os_path_resolve(Buf **paths_ptr, size_t paths_len);84Buf os_path_resolve(Buf **paths_ptr, size_t paths_len);
116bool os_path_is_absolute(Buf *path);85bool os_path_is_absolute(Buf *path);
11786
118Error ATTRIBUTE_MUST_USE os_make_path(Buf *path);87Error ATTRIBUTE_MUST_USE os_make_path(Buf *path);
119Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path);88Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path);
12089
121Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr);
122Error ATTRIBUTE_MUST_USE os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode);
123Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file);
124Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len);
125Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents);
126Error ATTRIBUTE_MUST_USE os_file_overwrite(OsFile file, Buf *contents);
127void os_file_close(OsFile *file);
128
129Error ATTRIBUTE_MUST_USE os_write_file(Buf *full_path, Buf *contents);90Error ATTRIBUTE_MUST_USE os_write_file(Buf *full_path, Buf *contents);
130Error ATTRIBUTE_MUST_USE os_copy_file(Buf *src_path, Buf *dest_path);91Error ATTRIBUTE_MUST_USE os_copy_file(Buf *src_path, Buf *dest_path);
131Error ATTRIBUTE_MUST_USE os_update_file(Buf *src_path, Buf *dest_path);
132Error ATTRIBUTE_MUST_USE os_dump_file(Buf *src_path, FILE *dest_file);
13392
134Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents);93Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents);
135Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents);94Error 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);
139bool os_stderr_tty(void);98bool os_stderr_tty(void);
140void os_stderr_set_color(TermColor color);99void os_stderr_set_color(TermColor color);
141100
142Error os_delete_file(Buf *path);
143
144Error ATTRIBUTE_MUST_USE os_file_exists(Buf *full_path, bool *result);
145
146Error os_rename(Buf *src_path, Buf *dest_path);101Error os_rename(Buf *src_path, Buf *dest_path);
147OsTimeStamp os_timestamp_monotonic(void);102OsTimeStamp os_timestamp_monotonic(void);
148OsTimeStamp os_timestamp_calendar(void);
149103
150bool os_is_sep(uint8_t c);104bool os_is_sep(uint8_t c);
151105
152Error ATTRIBUTE_MUST_USE os_self_exe_path(Buf *out_path);
153
154Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname);
155
156Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths);
157
158const size_t PATH_MAX_WIDE = 32767;106const size_t PATH_MAX_WIDE = 32767;
159107
160struct PathSpace {108struct 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 }
432432
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 {
130130
131pub fn libcNeedsLibUnwind(target: std.Target) bool {131pub 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,
140139
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 };
5858
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,
8086
87 files: std.ArrayList(File),
88
81 /// Adds a subcase in which the module is updated with `src`, and the89 /// 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();
507518
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 {
5527const ParseError = Error || error{ParseError};5527const ParseError = Error || error{ParseError};
55285528
5529fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {5529fn 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 expr5533 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);
55645539
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);
55725553
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}
55835566
5584fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {5567fn 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}
58075790
5808fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {5791fn 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}
59545937
5938fn 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
5955fn nodeIsInfixOp(tag: ast.Node.Tag) bool {5962fn 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}
60556062
6056fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {6063fn 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
6075fn 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
6096fn 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
6115fn 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
6134fn 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
6153fn 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
6172fn 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
6191fn 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}
60686220
6069 node = try transCreateNodeFieldAccess(c, node, m.slice());6221fn 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
6259fn 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
6289fn 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
6319fn 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
6378fn 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();
62056447
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}
62886495
6289fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {6496fn 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);
63266535
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,
9090
91 .anyframe_T, .@"anyframe" => return .AnyFrame,91 .anyframe_T, .@"anyframe" => return .AnyFrame,
92
93 .empty_struct => return .Struct,
92 }94 }
93 }95 }
9496
...@@ -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 }
444447
...@@ -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)"),
507510
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,
935943
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,
11111120
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,
11851195
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,
12561267
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,
13361348
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,
14111424
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,
15281542
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,
16551670
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,
17201736
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,
18021819
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,
18761894
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,
19401959
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,
20222042
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,
26382665
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,
27472776
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 }
27622791
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` because2866 /// 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 types2868 /// 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,
28382942
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};
29473059
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,
186188
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"),
314316
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,
547553
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,
630637
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,
713721
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,
824833
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,
911921
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,
10821093
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,
11561168
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,
13041317
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,
13871401
1388 .empty_array => unreachable, // out of bounds array index1402 .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,
14871502
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}
11281128
1129LLVMValueRef ZigLLVMBuildAndReduce(LLVMBuilderRef B, LLVMValueRef Val) {
1130 return wrap(unwrap(B)->CreateAndReduce(unwrap(Val)));
1131}
1132
1133LLVMValueRef ZigLLVMBuildOrReduce(LLVMBuilderRef B, LLVMValueRef Val) {
1134 return wrap(unwrap(B)->CreateOrReduce(unwrap(Val)));
1135}
1136
1137LLVMValueRef ZigLLVMBuildXorReduce(LLVMBuilderRef B, LLVMValueRef Val) {
1138 return wrap(unwrap(B)->CreateXorReduce(unwrap(Val)));
1139}
1140
1141LLVMValueRef ZigLLVMBuildIntMaxReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed) {
1142 return wrap(unwrap(B)->CreateIntMaxReduce(unwrap(Val), is_signed));
1143}
1144
1145LLVMValueRef ZigLLVMBuildIntMinReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed) {
1146 return wrap(unwrap(B)->CreateIntMinReduce(unwrap(Val), is_signed));
1147}
1148
1149LLVMValueRef ZigLLVMBuildFPMaxReduce(LLVMBuilderRef B, LLVMValueRef Val) {
1150 return wrap(unwrap(B)->CreateFPMaxReduce(unwrap(Val)));
1151}
1152
1153LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val) {
1154 return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Val)));
1155}
1156
1129static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, "");1157static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, "");
1130static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, "");1158static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, "");
1131static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, "");1159static_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);
457457
458LLVMValueRef ZigLLVMBuildAndReduce(LLVMBuilderRef B, LLVMValueRef Val);
459LLVMValueRef ZigLLVMBuildOrReduce(LLVMBuilderRef B, LLVMValueRef Val);
460LLVMValueRef ZigLLVMBuildXorReduce(LLVMBuilderRef B, LLVMValueRef Val);
461LLVMValueRef ZigLLVMBuildIntMaxReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed);
462LLVMValueRef ZigLLVMBuildIntMinReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed);
463LLVMValueRef ZigLLVMBuildFPMaxReduce(LLVMBuilderRef B, LLVMValueRef Val);
464LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val);
465
458#define ZigLLVM_DIFlags_Zero 0U466#define ZigLLVM_DIFlags_Zero 0U
459#define ZigLLVM_DIFlags_Private 1U467#define ZigLLVM_DIFlags_Private 1U
460#define ZigLLVM_DIFlags_Protected 2U468#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 type168 /// 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,
319322
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,
493497
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}
139140
...@@ -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}
11921206
1207fn 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
1193fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {1225fn 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");
2const std = @import("std");2const std = @import("std");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub 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 });
1222
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 });
20175
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 });
40195
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 });
113284
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 });
75577692
7558 cases.add( // fixed bug #20327693 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 @@
1pub const E = enum(u32) { A, B, C };
2pub const S = extern struct {
3 e: E,
4};
5test "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
420test "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
433test "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));
215217
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}
230232
231fn testStruct() void {233fn 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}
246253
254const TestUnpackedStruct = struct {
255 fieldA: u32 = 4,
256};
257
247const TestStruct = packed struct {258const 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};
256267
257test "type info: function type info" {268test "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" {
262fn testFunction() void {275fn 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);
270286
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}
276292
277extern fn foo(a: usize, b: bool, ...) usize;293extern fn foo(a: usize, b: bool, ...) callconv(.C) usize;
294extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize;
278295
279test "typeInfo with comptime parameter in struct fn def" {296test "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
488test "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 @@
1const std = @import("std");
2const TestContext = @import("../../src/test.zig").TestContext;
3
4const linux_arm = std.zig.CrossTarget{
5 .cpu_arch = .arm,
6 .os_tag = .linux,
7};
8
9pub 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};
2323
24const linux_arm = std.zig.CrossTarget{
25 .cpu_arch = .arm,
26 .os_tag = .linux,
27};
28
29const wasi = std.zig.CrossTarget{24const 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);
3834
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 only78 // 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 }
155150
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 world157 // 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 }
191190
192 {191 {
193 var case = ctx.exe("hello world", linux_arm);192 var case = ctx.exe("hello world", linux_riscv64);
194 // Regular old hello world193 // 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 );
335335
...@@ -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 );
368368
...@@ -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 );
404404
...@@ -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 );
441441
...@@ -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 );
471471
...@@ -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 );
505505
...@@ -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 );
529529
...@@ -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 );
568568
...@@ -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 );
605605
...@@ -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 );
647647
...@@ -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 );
699699
...@@ -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 );
761761
...@@ -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 );
794794
...@@ -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 );
826826
...@@ -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 );
851851
...@@ -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 );
877877
...@@ -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 }
911911
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);
914952
...@@ -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 );
929967
...@@ -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 );
947985
...@@ -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-754999 // This is what you get when you take the bits of the IEE-754
962 // representation of 42.0 and reinterpret them as an unsigned1000 // 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");
3const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
44
5pub fn addCases(cases: *tests.TranslateCContext) void {5pub 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 });
29832999
2984 cases.add("multibyte character literals",3000 cases.add("multibyte character literals",