commit b5a36f676b1fd69f195d9f1eb6e35f3eb2f15946 (tree)
parent d6d05fc84d33c71434a1f8bae51ca5956e08cdf0
Author: Andrew Kelley <andrew@ziglang.org>
Date: Wed, 7 Oct 2020 00:46:05 -0700
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.
Diffstat:
78 files changed, 5719 insertions(+), 3111 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -27,23 +27,26 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
set(ZIG_VERSION_MAJOR 0)
set(ZIG_VERSION_MINOR 6)
set(ZIG_VERSION_PATCH 0)
-set(ZIG_VERSION "${ZIG_VERSION_MAJOR}.${ZIG_VERSION_MINOR}.${ZIG_VERSION_PATCH}")
-
-find_program(GIT_EXE NAMES git)
-if(GIT_EXE)
- execute_process(
- COMMAND ${GIT_EXE} -C ${CMAKE_SOURCE_DIR} name-rev HEAD --tags --name-only --no-undefined --always
- RESULT_VARIABLE EXIT_STATUS
- OUTPUT_VARIABLE ZIG_GIT_REV
- OUTPUT_STRIP_TRAILING_WHITESPACE
- ERROR_QUIET)
- if(EXIT_STATUS EQUAL "0")
- if(ZIG_GIT_REV MATCHES "\\^0$")
- if(NOT("${ZIG_GIT_REV}" STREQUAL "${ZIG_VERSION}^0"))
- message("WARNING: Tag does not match configured Zig version")
+set(ZIG_VERSION "" CACHE STRING "Override Zig version string. Default is to find out with git.")
+
+if("${ZIG_VERSION}" STREQUAL "")
+ set(ZIG_VERSION "${ZIG_VERSION_MAJOR}.${ZIG_VERSION_MINOR}.${ZIG_VERSION_PATCH}")
+ find_program(GIT_EXE NAMES git)
+ if(GIT_EXE)
+ execute_process(
+ COMMAND ${GIT_EXE} -C ${CMAKE_SOURCE_DIR} name-rev HEAD --tags --name-only --no-undefined --always
+ RESULT_VARIABLE EXIT_STATUS
+ OUTPUT_VARIABLE ZIG_GIT_REV
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_QUIET)
+ if(EXIT_STATUS EQUAL "0")
+ if(ZIG_GIT_REV MATCHES "\\^0$")
+ if(NOT("${ZIG_GIT_REV}" STREQUAL "${ZIG_VERSION}^0"))
+ message("WARNING: Tag does not match configured Zig version")
+ endif()
+ else()
+ set(ZIG_VERSION "${ZIG_VERSION}+${ZIG_GIT_REV}")
endif()
- else()
- set(ZIG_VERSION "${ZIG_VERSION}+${ZIG_GIT_REV}")
endif()
endif()
endif()
@@ -63,6 +66,9 @@ endif()
if(ZIG_STATIC)
set(ZIG_STATIC_LLVM "on")
+ set(ZIG_LINK_MODE "Static")
+else()
+ set(ZIG_LINK_MODE "Dynamic")
endif()
string(REGEX REPLACE "\\\\" "\\\\\\\\" ZIG_LIBC_LIB_DIR_ESCAPED "${ZIG_LIBC_LIB_DIR}")
@@ -74,6 +80,7 @@ option(ZIG_TEST_COVERAGE "Build Zig with test coverage instrumentation" OFF)
set(ZIG_TARGET_TRIPLE "native" CACHE STRING "arch-os-abi to output binaries for")
set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries for")
set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary")
+set(ZIG_PREFER_LLVM_CONFIG off CACHE BOOL "(when cross compiling) use llvm-config to find target llvm dependencies if needed")
find_package(llvm)
find_package(clang)
@@ -257,7 +264,6 @@ target_include_directories(embedded_softfloat PUBLIC
)
include_directories("${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/include")
set(SOFTFLOAT_LIBRARIES embedded_softfloat)
-include_directories("${CMAKE_SOURCE_DIR}/deps/dbg-macro")
find_package(Threads)
@@ -487,7 +493,7 @@ if("${ZIG_TARGET_TRIPLE}" STREQUAL "native")
endif()
else()
add_custom_target(zig_build_zig1 ALL
- COMMAND "${ZIG_EXECUTABLE}" ${BUILD_ZIG1_ARGS}
+ COMMAND "${ZIG_EXECUTABLE}" "build-obj" ${BUILD_ZIG1_ARGS}
BYPRODUCTS "${ZIG1_OBJECT}"
COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
diff --git a/README.md b/README.md
@@ -37,7 +37,6 @@ This step must be repeated when you make changes to any of the C++ source code.
* cmake >= 3.15.3
* Microsoft Visual Studio. Supported versions:
- - 2015 (version 14)
- 2017 (version 15.8)
- 2019 (version 16)
* LLVM, Clang, LLD development libraries == 11.x
diff --git a/ci/azure/pipelines.yml b/ci/azure/pipelines.yml
@@ -28,20 +28,10 @@ jobs:
- job: BuildWindows
pool:
vmImage: 'windows-2019'
- strategy:
- matrix:
- mingw64:
- CHERE_INVOKING: yes
- MSYSTEM: MINGW64
- SCRIPT: '%CD:~0,2%\msys64\usr\bin\bash -lc "bash ci/azure/windows_mingw_script"'
- msvc:
- SCRIPT: ci/azure/windows_msvc_script.bat
-
timeoutInMinutes: 360
-
steps:
- powershell: |
- (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")
+ (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")
.\sfx.exe -y -o\
del sfx.exe
displayName: Download/Extract/Install MSYS2
@@ -57,7 +47,7 @@ jobs:
- task: DownloadSecureFile@1
inputs:
secureFile: s3cfg
- - script: $(SCRIPT)
+ - script: ci/azure/windows_msvc_script.bat
name: main
displayName: 'Build and test'
- job: OnMasterSuccess
diff --git a/ci/azure/windows_mingw_script b/ci/azure/windows_mingw_script
@@ -1,28 +0,0 @@
-#!/bin/sh
-
-set -x
-set -e
-
-pacman --noconfirm --needed -S git base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-clang mingw-w64-x86_64-lld mingw-w64-x86_64-llvm
-
-git config core.abbrev 9
-
-# Git is wrong for autocrlf being enabled by default on Windows.
-# git is mangling files on Windows by default.
-# This is the second bug I've tracked down to being caused by autocrlf.
-git config core.autocrlf false
-# Too late; the files are already mangled.
-git checkout .
-
-ZIGBUILDDIR="$(pwd)/build"
-PREFIX="$ZIGBUILDDIR/dist"
-CMAKEFLAGS="-DCMAKE_COLOR_MAKEFILE=OFF -DCMAKE_INSTALL_PREFIX=$PREFIX -DZIG_STATIC=ON"
-
-mkdir $ZIGBUILDDIR
-cd $ZIGBUILDDIR
-
-cmake .. -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=RelWithDebInfo $CMAKEFLAGS -DCMAKE_EXE_LINKER_FLAGS='-fuse-ld=lld -Wl,/debug,/pdb:zig.pdb'
-
-make -j$(nproc) install
-
-./zig build test-behavior -Dskip-non-native -Dskip-release
diff --git a/ci/azure/windows_msvc_install b/ci/azure/windows_msvc_install
@@ -4,12 +4,7 @@ set -x
set -e
pacman -Su --needed --noconfirm
-
-# Uncomment when https://github.com/msys2/MSYS2-packages/issues/2050 is fixed
-#pacman -S --needed --noconfirm wget p7zip python3-pip tar xz
-pacman -S --needed --noconfirm wget p7zip tar xz
-pacman -U --noconfirm http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-python-3.8.4-1-any.pkg.tar.zst
-pacman -U --noconfirm http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-python-pip-20.0.2-1-any.pkg.tar.xz
+pacman -S --needed --noconfirm wget p7zip python3-pip tar xz
pip install s3cmd
wget -nv "https://ziglang.org/deps/llvm%2bclang%2blld-10.0.0-x86_64-windows-msvc-release-mt.tar.xz"
diff --git a/cmake/Findllvm.cmake b/cmake/Findllvm.cmake
@@ -32,7 +32,7 @@ if(ZIG_PREFER_CLANG_CPP_DYLIB)
/usr/local/llvm11/lib
/usr/local/llvm110/lib
)
-elseif("${ZIG_TARGET_TRIPLE}" STREQUAL "native")
+elseif(("${ZIG_TARGET_TRIPLE}" STREQUAL "native") OR ZIG_PREFER_LLVM_CONFIG)
find_program(LLVM_CONFIG_EXE
NAMES llvm-config-11 llvm-config-11.0 llvm-config110 llvm-config11 llvm-config
PATHS
@@ -55,13 +55,13 @@ elseif("${ZIG_TARGET_TRIPLE}" STREQUAL "native")
OUTPUT_STRIP_TRAILING_WHITESPACE)
if("${LLVM_CONFIG_VERSION}" VERSION_LESS 11)
- message(FATAL_ERROR "expected LLVM 11.x but found ${LLVM_CONFIG_VERSION}")
+ message(FATAL_ERROR "expected LLVM 11.x but found ${LLVM_CONFIG_VERSION} using ${LLVM_CONFIG_EXE}")
endif()
if("${LLVM_CONFIG_VERSION}" VERSION_EQUAL 12)
- message(FATAL_ERROR "expected LLVM 11.x but found ${LLVM_CONFIG_VERSION}")
+ message(FATAL_ERROR "expected LLVM 11.x but found ${LLVM_CONFIG_VERSION} using ${LLVM_CONFIG_EXE}")
endif()
if("${LLVM_CONFIG_VERSION}" VERSION_GREATER 11)
- message(FATAL_ERROR "expected LLVM 11.x but found ${LLVM_CONFIG_VERSION}")
+ message(FATAL_ERROR "expected LLVM 11.x but found ${LLVM_CONFIG_VERSION} using ${LLVM_CONFIG_EXE}")
endif()
execute_process(
@@ -72,12 +72,13 @@ elseif("${ZIG_TARGET_TRIPLE}" STREQUAL "native")
function(NEED_TARGET TARGET_NAME)
list (FIND LLVM_TARGETS_BUILT "${TARGET_NAME}" _index)
if (${_index} EQUAL -1)
- message(FATAL_ERROR "LLVM is missing target ${TARGET_NAME}. Zig requires LLVM to be built with all default targets enabled.")
+ 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.")
endif()
endfunction(NEED_TARGET)
NEED_TARGET("AArch64")
NEED_TARGET("AMDGPU")
NEED_TARGET("ARM")
+ NEED_TARGET("AVR")
NEED_TARGET("BPF")
NEED_TARGET("Hexagon")
NEED_TARGET("Lanai")
@@ -141,8 +142,7 @@ elseif("${ZIG_TARGET_TRIPLE}" STREQUAL "native")
link_directories("${LLVM_LIBDIRS}")
else()
# Here we assume that we're cross compiling with Zig, of course. No reason
- # to support more complicated setups. We also assume the experimental target
- # AVR is enabled.
+ # to support more complicated setups.
macro(FIND_AND_ADD_LLVM_LIB _libname_)
string(TOUPPER ${_libname_} _prettylibname_)
diff --git a/deps/dbg-macro/LICENSE b/deps/dbg-macro/LICENSE
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2019 David Peter <mail@david-peter.de>
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/deps/dbg-macro/README.md b/deps/dbg-macro/README.md
@@ -1,172 +0,0 @@
-# `dbg(…)`
-
-[](https://travis-ci.org/sharkdp/dbg-macro) [](https://ci.appveyor.com/project/sharkdp/dbg-macro) [](https://repl.it/@sharkdp/dbg-macro-demo) [](dbg.h)
-
-*A macro for `printf`-style debugging fans.*
-
-Debuggers are great. But sometimes you just don't have the time or patience to set
-up everything correctly and just want a quick way to inspect some values at runtime.
-
-This projects provides a [single header file](dbg.h) with a `dbg(…)`
-macro that can be used in all circumstances where you would typically write
-`printf("…", …)` or `std::cout << …`. But it comes with a few extras.
-
-## Examples
-
-``` c++
-#include <vector>
-#include <dbg.h>
-
-// You can use "dbg(..)" in expressions:
-int factorial(int n) {
- if (dbg(n <= 1)) {
- return dbg(1);
- } else {
- return dbg(n * factorial(n - 1));
- }
-}
-
-int main() {
- std::string message = "hello";
- dbg(message); // [example.cpp:15 (main)] message = "hello" (std::string)
-
- const int a = 2;
- const int b = dbg(3 * a) + 1; // [example.cpp:18 (main)] 3 * a = 6 (int)
-
- std::vector<int> numbers{b, 13, 42};
- dbg(numbers); // [example.cpp:21 (main)] numbers = {7, 13, 42} (size: 3) (std::vector<int>)
-
- dbg("this line is executed"); // [example.cpp:23 (main)] this line is executed
-
- factorial(4);
-
- return 0;
-}
-```
-
-The code above produces this output ([try it yourself](https://repl.it/@sharkdp/dbg-macro-demo)):
-
-
-
-## Features
-
- * Easy to read, colorized output (colors auto-disable when the output is not an interactive terminal)
- * Prints file name, line number, function name and the original expression
- * Adds type information for the printed-out value
- * Specialized pretty-printers for containers, pointers, string literals, enums, `std::optional`, etc.
- * Can be used inside expressions (passing through the original value)
- * The `dbg.h` header issues a compiler warning when included (so you don't forget to remove it).
- * Compatible and tested with C++11, C++14 and C++17.
-
-## Installation
-
-To make this practical, the `dbg.h` header should to be readily available from all kinds of different
-places and in all kinds of environments. The quick & dirty way is to actually copy the header file
-to `/usr/include` or to clone the repository and symlink `dbg.h` to `/usr/include/dbg.h`.
-``` bash
-git clone https://github.com/sharkdp/dbg-macro
-sudo ln -s $(readlink -f dbg-macro/dbg.h) /usr/include/dbg.h
-```
-If you don't want to make untracked changes to your filesystem, check below if there is a package for
-your operating system or package manager.
-
-### On Arch Linux
-
-You can install [`dbg-macro` from the AUR](https://aur.archlinux.org/packages/dbg-macro/):
-``` bash
-yay -S dbg-macro
-```
-
-### With vcpkg
-
-You can install the [`dbg-macro` port](https://github.com/microsoft/vcpkg/tree/master/ports/dbg-macro) via:
-``` bash
-vcpkg install dbg-macro
-```
-
-## Configuration
-
-* Set the `DBG_MACRO_DISABLE` flag to disable the `dbg(…)` macro (i.e. to make it a no-op).
-* Set the `DBG_MACRO_NO_WARNING` flag to disable the *"'dbg.h' header is included in your code base"* warnings.
-
-## Advanced features
-
-### Hexadecimal, octal and binary format
-
-If you want to format integers in hexadecimal, octal or binary representation, you can
-simply wrap them in `dbg::hex(…)`, `dbg::oct(…)` or `dbg::bin(…)`:
-```c++
-const uint32_t secret = 12648430;
-dbg(dbg::hex(secret));
-```
-
-### Printing type names
-
-`dbg(…)` already prints the type for each value in parenthesis (see screenshot above). But
-sometimes you just want to print a type (maybe because you don't have a value for that type).
-In this case, you can use the `dbg::type<T>()` helper to pretty-print a given type `T`.
-For example:
-```c++
-template <typename T>
-void my_function_template() {
- using MyDependentType = typename std::remove_reference<T>::type&&;
- dbg(dbg::type<MyDependentType>());
-}
-```
-
-### Print the current time
-
-To print a timestamp, you can use the `dbg::time()` helper:
-```c++
-dbg(dbg::time());
-```
-
-### Customization
-
-If you want `dbg(…)` to work for your custom datatype, you can simply overload `operator<<` for
-`std::ostream&`:
-```c++
-std::ostream& operator<<(std::ostream& out, const user_defined_type& v) {
- out << "…";
- return out;
-}
-```
-
-If you want to modify the type name that is printed by `dbg(…)`, you can add a custom
-`get_type_name` overload:
-```c++
-// Customization point for type information
-namespace dbg {
- std::string get_type_name(type_tag<bool>) {
- return "truth value";
- }
-}
-```
-
-## Development
-
-If you want to contribute to `dbg-macro`, here is how you can build the tests and demos:
-
-Make sure that the submodule(s) are up to date:
-```bash
-git submodule update --init
-```
-
-Then, use the typical `cmake` workflow. Usage of `-DCMAKE_CXX_STANDARD=17` is optional,
-but recommended in order to have the largest set of features enabled:
-```bash
-mkdir build
-cd build
-cmake .. -DCMAKE_CXX_STANDARD=17
-make
-```
-
-To run the tests, simply call:
-```bash
-make test
-```
-You can find the unit tests in `tests/basic.cpp`.
-
-## Acknowledgement
-
-This project is inspired by Rusts [`dbg!(…)` macro](https://doc.rust-lang.org/std/macro.dbg.html).
diff --git a/deps/dbg-macro/dbg.h b/deps/dbg-macro/dbg.h
@@ -1,711 +0,0 @@
-/*****************************************************************************
-
- dbg(...) macro
-
-License (MIT):
-
- Copyright (c) 2019 David Peter <mail@david-peter.de>
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to
- deal in the Software without restriction, including without limitation the
- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- sell copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
-
-*****************************************************************************/
-
-#ifndef DBG_MACRO_DBG_H
-#define DBG_MACRO_DBG_H
-
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-#define DBG_MACRO_UNIX
-#elif defined(_MSC_VER)
-#define DBG_MACRO_WINDOWS
-#endif
-
-#ifndef DBG_MACRO_NO_WARNING
-#pragma message("WARNING: the 'dbg.h' header is included in your code base")
-#endif // DBG_MACRO_NO_WARNING
-
-#include <algorithm>
-#include <chrono>
-#include <ctime>
-#include <iomanip>
-#include <ios>
-#include <iostream>
-#include <memory>
-#include <sstream>
-#include <string>
-#include <tuple>
-#include <type_traits>
-#include <vector>
-
-#ifdef DBG_MACRO_UNIX
-#include <unistd.h>
-#endif
-
-#if __cplusplus >= 201703L || defined(_MSC_VER)
-#define DBG_MACRO_CXX_STANDARD 17
-#elif __cplusplus >= 201402L
-#define DBG_MACRO_CXX_STANDARD 14
-#else
-#define DBG_MACRO_CXX_STANDARD 11
-#endif
-
-#if DBG_MACRO_CXX_STANDARD >= 17
-#include <optional>
-#include <variant>
-#endif
-
-namespace dbg {
-
-#ifdef DBG_MACRO_UNIX
-inline bool isColorizedOutputEnabled() {
- return isatty(fileno(stderr));
-}
-#else
-inline bool isColorizedOutputEnabled() {
- return true;
-}
-#endif
-
-struct time {};
-
-namespace pretty_function {
-
-// Compiler-agnostic version of __PRETTY_FUNCTION__ and constants to
-// extract the template argument in `type_name_impl`
-
-#if defined(__clang__)
-#define DBG_MACRO_PRETTY_FUNCTION __PRETTY_FUNCTION__
-static constexpr size_t PREFIX_LENGTH =
- sizeof("const char *dbg::type_name_impl() [T = ") - 1;
-static constexpr size_t SUFFIX_LENGTH = sizeof("]") - 1;
-#elif defined(__GNUC__) && !defined(__clang__)
-#define DBG_MACRO_PRETTY_FUNCTION __PRETTY_FUNCTION__
-static constexpr size_t PREFIX_LENGTH =
- sizeof("const char* dbg::type_name_impl() [with T = ") - 1;
-static constexpr size_t SUFFIX_LENGTH = sizeof("]") - 1;
-#elif defined(_MSC_VER)
-#define DBG_MACRO_PRETTY_FUNCTION __FUNCSIG__
-static constexpr size_t PREFIX_LENGTH =
- sizeof("const char *__cdecl dbg::type_name_impl<") - 1;
-static constexpr size_t SUFFIX_LENGTH = sizeof(">(void)") - 1;
-#else
-#error "This compiler is currently not supported by dbg_macro."
-#endif
-
-} // namespace pretty_function
-
-// Formatting helpers
-
-template <typename T>
-struct print_formatted {
- static_assert(std::is_integral<T>::value,
- "Only integral types are supported.");
-
- print_formatted(T value, int numeric_base)
- : inner(value), base(numeric_base) {}
-
- operator T() const { return inner; }
-
- const char* prefix() const {
- switch (base) {
- case 8:
- return "0o";
- case 16:
- return "0x";
- case 2:
- return "0b";
- default:
- return "";
- }
- }
-
- T inner;
- int base;
-};
-
-template <typename T>
-print_formatted<T> hex(T value) {
- return print_formatted<T>{value, 16};
-}
-
-template <typename T>
-print_formatted<T> oct(T value) {
- return print_formatted<T>{value, 8};
-}
-
-template <typename T>
-print_formatted<T> bin(T value) {
- return print_formatted<T>{value, 2};
-}
-
-// Implementation of 'type_name<T>()'
-
-template <typename T>
-const char* type_name_impl() {
- return DBG_MACRO_PRETTY_FUNCTION;
-}
-
-template <typename T>
-struct type_tag {};
-
-template <int&... ExplicitArgumentBarrier, typename T>
-std::string get_type_name(type_tag<T>) {
- namespace pf = pretty_function;
-
- std::string type = type_name_impl<T>();
- return type.substr(pf::PREFIX_LENGTH,
- type.size() - pf::PREFIX_LENGTH - pf::SUFFIX_LENGTH);
-}
-
-template <typename T>
-std::string type_name() {
- if (std::is_volatile<T>::value) {
- if (std::is_pointer<T>::value) {
- return type_name<typename std::remove_volatile<T>::type>() + " volatile";
- } else {
- return "volatile " + type_name<typename std::remove_volatile<T>::type>();
- }
- }
- if (std::is_const<T>::value) {
- if (std::is_pointer<T>::value) {
- return type_name<typename std::remove_const<T>::type>() + " const";
- } else {
- return "const " + type_name<typename std::remove_const<T>::type>();
- }
- }
- if (std::is_pointer<T>::value) {
- return type_name<typename std::remove_pointer<T>::type>() + "*";
- }
- if (std::is_lvalue_reference<T>::value) {
- return type_name<typename std::remove_reference<T>::type>() + "&";
- }
- if (std::is_rvalue_reference<T>::value) {
- return type_name<typename std::remove_reference<T>::type>() + "&&";
- }
- return get_type_name(type_tag<T>{});
-}
-
-inline std::string get_type_name(type_tag<short>) {
- return "short";
-}
-
-inline std::string get_type_name(type_tag<unsigned short>) {
- return "unsigned short";
-}
-
-inline std::string get_type_name(type_tag<long>) {
- return "long";
-}
-
-inline std::string get_type_name(type_tag<unsigned long>) {
- return "unsigned long";
-}
-
-inline std::string get_type_name(type_tag<std::string>) {
- return "std::string";
-}
-
-template <typename T>
-std::string get_type_name(type_tag<std::vector<T, std::allocator<T>>>) {
- return "std::vector<" + type_name<T>() + ">";
-}
-
-template <typename T1, typename T2>
-std::string get_type_name(type_tag<std::pair<T1, T2>>) {
- return "std::pair<" + type_name<T1>() + ", " + type_name<T2>() + ">";
-}
-
-template <typename... T>
-std::string type_list_to_string() {
- std::string result;
- auto unused = {(result += type_name<T>() + ", ", 0)..., 0};
- static_cast<void>(unused);
-
- if (sizeof...(T) > 0) {
- result.pop_back();
- result.pop_back();
- }
- return result;
-}
-
-template <typename... T>
-std::string get_type_name(type_tag<std::tuple<T...>>) {
- return "std::tuple<" + type_list_to_string<T...>() + ">";
-}
-
-template <typename T>
-inline std::string get_type_name(type_tag<print_formatted<T>>) {
- return type_name<T>();
-}
-
-// Implementation of 'is_detected' to specialize for container-like types
-
-namespace detail_detector {
-
-struct nonesuch {
- nonesuch() = delete;
- ~nonesuch() = delete;
- nonesuch(nonesuch const&) = delete;
- void operator=(nonesuch const&) = delete;
-};
-
-template <typename...>
-using void_t = void;
-
-template <class Default,
- class AlwaysVoid,
- template <class...>
- class Op,
- class... Args>
-struct detector {
- using value_t = std::false_type;
- using type = Default;
-};
-
-template <class Default, template <class...> class Op, class... Args>
-struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
- using value_t = std::true_type;
- using type = Op<Args...>;
-};
-
-} // namespace detail_detector
-
-template <template <class...> class Op, class... Args>
-using is_detected = typename detail_detector::
- detector<detail_detector::nonesuch, void, Op, Args...>::value_t;
-
-namespace detail {
-
-namespace {
-using std::begin;
-using std::end;
-#if DBG_MACRO_CXX_STANDARD < 17
-template <typename T>
-constexpr auto size(const T& c) -> decltype(c.size()) {
- return c.size();
-}
-template <typename T, std::size_t N>
-constexpr std::size_t size(const T (&)[N]) {
- return N;
-}
-#else
-using std::size;
-#endif
-} // namespace
-
-template <typename T>
-using detect_begin_t = decltype(detail::begin(std::declval<T>()));
-
-template <typename T>
-using detect_end_t = decltype(detail::end(std::declval<T>()));
-
-template <typename T>
-using detect_size_t = decltype(detail::size(std::declval<T>()));
-
-template <typename T>
-struct is_container {
- static constexpr bool value =
- is_detected<detect_begin_t, T>::value &&
- is_detected<detect_end_t, T>::value &&
- is_detected<detect_size_t, T>::value &&
- !std::is_same<std::string,
- typename std::remove_cv<
- typename std::remove_reference<T>::type>::type>::value;
-};
-
-template <typename T>
-using ostream_operator_t =
- decltype(std::declval<std::ostream&>() << std::declval<T>());
-
-template <typename T>
-struct has_ostream_operator : is_detected<ostream_operator_t, T> {};
-
-} // namespace detail
-
-// Helper to dbg(…)-print types
-template <typename T>
-struct print_type {};
-
-template <typename T>
-print_type<T> type() {
- return print_type<T>{};
-}
-
-// Specializations of "pretty_print"
-
-template <typename T>
-inline void pretty_print(std::ostream& stream, const T& value, std::true_type) {
- stream << value;
-}
-
-template <typename T>
-inline void pretty_print(std::ostream&, const T&, std::false_type) {
- static_assert(detail::has_ostream_operator<const T&>::value,
- "Type does not support the << ostream operator");
-}
-
-template <typename T>
-inline typename std::enable_if<!detail::is_container<const T&>::value &&
- !std::is_enum<T>::value,
- bool>::type
-pretty_print(std::ostream& stream, const T& value) {
- pretty_print(stream, value,
- typename detail::has_ostream_operator<const T&>::type{});
- return true;
-}
-
-inline bool pretty_print(std::ostream& stream, const bool& value) {
- stream << std::boolalpha << value;
- return true;
-}
-
-inline bool pretty_print(std::ostream& stream, const char& value) {
- const bool printable = value >= 0x20 && value <= 0x7E;
-
- if (printable) {
- stream << "'" << value << "'";
- } else {
- stream << "'\\x" << std::setw(2) << std::setfill('0') << std::hex
- << std::uppercase << (0xFF & value) << "'";
- }
- return true;
-}
-
-template <typename P>
-inline bool pretty_print(std::ostream& stream, P* const& value) {
- if (value == nullptr) {
- stream << "nullptr";
- } else {
- stream << value;
- }
- return true;
-}
-
-template <typename T, typename Deleter>
-inline bool pretty_print(std::ostream& stream,
- std::unique_ptr<T, Deleter>& value) {
- pretty_print(stream, value.get());
- return true;
-}
-
-template <typename T>
-inline bool pretty_print(std::ostream& stream, std::shared_ptr<T>& value) {
- pretty_print(stream, value.get());
- stream << " (use_count = " << value.use_count() << ")";
-
- return true;
-}
-
-template <size_t N>
-inline bool pretty_print(std::ostream& stream, const char (&value)[N]) {
- stream << value;
- return false;
-}
-
-template <>
-inline bool pretty_print(std::ostream& stream, const char* const& value) {
- stream << '"' << value << '"';
- return true;
-}
-
-template <size_t Idx>
-struct pretty_print_tuple {
- template <typename... Ts>
- static void print(std::ostream& stream, const std::tuple<Ts...>& tuple) {
- pretty_print_tuple<Idx - 1>::print(stream, tuple);
- stream << ", ";
- pretty_print(stream, std::get<Idx>(tuple));
- }
-};
-
-template <>
-struct pretty_print_tuple<0> {
- template <typename... Ts>
- static void print(std::ostream& stream, const std::tuple<Ts...>& tuple) {
- pretty_print(stream, std::get<0>(tuple));
- }
-};
-
-template <typename... Ts>
-inline bool pretty_print(std::ostream& stream, const std::tuple<Ts...>& value) {
- stream << "{";
- pretty_print_tuple<sizeof...(Ts) - 1>::print(stream, value);
- stream << "}";
-
- return true;
-}
-
-template <>
-inline bool pretty_print(std::ostream& stream, const std::tuple<>&) {
- stream << "{}";
-
- return true;
-}
-
-template <>
-inline bool pretty_print(std::ostream& stream, const time&) {
- using namespace std::chrono;
-
- const auto now = system_clock::now();
- const auto us =
- duration_cast<microseconds>(now.time_since_epoch()).count() % 1000000;
- const auto hms = system_clock::to_time_t(now);
- const std::tm* tm = std::localtime(&hms);
- stream << "current time = " << std::put_time(tm, "%H:%M:%S") << '.'
- << std::setw(6) << std::setfill('0') << us;
-
- return false;
-}
-
-// Converts decimal integer to binary string
-template <typename T>
-std::string decimalToBinary(T n) {
- const size_t length = 8 * sizeof(T);
- std::string toRet;
- toRet.resize(length);
-
- for (size_t i = 0; i < length; ++i) {
- const auto bit_at_index_i = (n >> i) & 1;
- toRet[length - 1 - i] = bit_at_index_i + '0';
- }
-
- return toRet;
-}
-
-template <typename T>
-inline bool pretty_print(std::ostream& stream,
- const print_formatted<T>& value) {
- if (value.inner < 0) {
- stream << "-";
- }
- stream << value.prefix();
-
- // Print using setbase
- if (value.base != 2) {
- stream << std::setw(sizeof(T)) << std::setfill('0')
- << std::setbase(value.base) << std::uppercase;
-
- if (value.inner >= 0) {
- // The '+' sign makes sure that a uint_8 is printed as a number
- stream << +value.inner;
- } else {
- using unsigned_type = typename std::make_unsigned<T>::type;
- stream << +(static_cast<unsigned_type>(-(value.inner + 1)) + 1);
- }
- } else {
- // Print for binary
- if (value.inner >= 0) {
- stream << decimalToBinary(value.inner);
- } else {
- using unsigned_type = typename std::make_unsigned<T>::type;
- stream << decimalToBinary<unsigned_type>(
- static_cast<unsigned_type>(-(value.inner + 1)) + 1);
- }
- }
-
- return true;
-}
-
-template <typename T>
-inline bool pretty_print(std::ostream& stream, const print_type<T>&) {
- stream << type_name<T>();
-
- stream << " [sizeof: " << sizeof(T) << " byte, ";
-
- stream << "trivial: ";
- if (std::is_trivial<T>::value) {
- stream << "yes";
- } else {
- stream << "no";
- }
-
- stream << ", standard layout: ";
- if (std::is_standard_layout<T>::value) {
- stream << "yes";
- } else {
- stream << "no";
- }
- stream << "]";
-
- return false;
-}
-
-template <typename Container>
-inline typename std::enable_if<detail::is_container<const Container&>::value,
- bool>::type
-pretty_print(std::ostream& stream, const Container& value) {
- stream << "{";
- const size_t size = detail::size(value);
- const size_t n = std::min(size_t{10}, size);
- size_t i = 0;
- using std::begin;
- using std::end;
- for (auto it = begin(value); it != end(value) && i < n; ++it, ++i) {
- pretty_print(stream, *it);
- if (i != n - 1) {
- stream << ", ";
- }
- }
-
- if (size > n) {
- stream << ", ...";
- stream << " size:" << size;
- }
-
- stream << "}";
- return true;
-}
-
-template <typename Enum>
-inline typename std::enable_if<std::is_enum<Enum>::value, bool>::type
-pretty_print(std::ostream& stream, Enum const& value) {
- using UnderlyingType = typename std::underlying_type<Enum>::type;
- stream << static_cast<UnderlyingType>(value);
-
- return true;
-}
-
-inline bool pretty_print(std::ostream& stream, const std::string& value) {
- stream << '"' << value << '"';
- return true;
-}
-
-template <typename T1, typename T2>
-inline bool pretty_print(std::ostream& stream, const std::pair<T1, T2>& value) {
- stream << "{";
- pretty_print(stream, value.first);
- stream << ", ";
- pretty_print(stream, value.second);
- stream << "}";
- return true;
-}
-
-#if DBG_MACRO_CXX_STANDARD >= 17
-
-template <typename T>
-inline bool pretty_print(std::ostream& stream, const std::optional<T>& value) {
- if (value) {
- stream << '{';
- pretty_print(stream, *value);
- stream << '}';
- } else {
- stream << "nullopt";
- }
-
- return true;
-}
-
-template <typename... Ts>
-inline bool pretty_print(std::ostream& stream,
- const std::variant<Ts...>& value) {
- stream << "{";
- std::visit([&stream](auto&& arg) { pretty_print(stream, arg); }, value);
- stream << "}";
-
- return true;
-}
-
-#endif
-
-class DebugOutput {
- public:
- DebugOutput(const char* filepath,
- int line,
- const char* function_name,
- const char* expression)
- : m_use_colorized_output(isColorizedOutputEnabled()),
- m_filepath(filepath),
- m_line(line),
- m_function_name(function_name),
- m_expression(expression) {
- const std::size_t path_length = m_filepath.length();
- if (path_length > MAX_PATH_LENGTH) {
- m_filepath = ".." + m_filepath.substr(path_length - MAX_PATH_LENGTH,
- MAX_PATH_LENGTH);
- }
- }
-
- template <typename T>
- T&& print(const std::string& type, T&& value) const {
- const T& ref = value;
- std::stringstream stream_value;
- const bool print_expr_and_type = pretty_print(stream_value, ref);
-
- std::stringstream output;
- output << ansi(ANSI_DEBUG) << "[" << m_filepath << ":" << m_line << " ("
- << m_function_name << ")] " << ansi(ANSI_RESET);
- if (print_expr_and_type) {
- output << ansi(ANSI_EXPRESSION) << m_expression << ansi(ANSI_RESET)
- << " = ";
- }
- output << ansi(ANSI_VALUE) << stream_value.str() << ansi(ANSI_RESET);
- if (print_expr_and_type) {
- output << " (" << ansi(ANSI_TYPE) << type << ansi(ANSI_RESET) << ")";
- }
- output << std::endl;
- std::cerr << output.str();
-
- return std::forward<T>(value);
- }
-
- private:
- const char* ansi(const char* code) const {
- if (m_use_colorized_output) {
- return code;
- } else {
- return ANSI_EMPTY;
- }
- }
-
- const bool m_use_colorized_output;
-
- std::string m_filepath;
- const int m_line;
- const std::string m_function_name;
- const std::string m_expression;
-
- static constexpr std::size_t MAX_PATH_LENGTH = 20;
-
- static constexpr const char* const ANSI_EMPTY = "";
- static constexpr const char* const ANSI_DEBUG = "\x1b[02m";
- static constexpr const char* const ANSI_EXPRESSION = "\x1b[36m";
- static constexpr const char* const ANSI_VALUE = "\x1b[01m";
- static constexpr const char* const ANSI_TYPE = "\x1b[32m";
- static constexpr const char* const ANSI_RESET = "\x1b[0m";
-};
-
-// Identity function to suppress "-Wunused-value" warnings in DBG_MACRO_DISABLE
-// mode
-template <typename T>
-T&& identity(T&& t) {
- return std::forward<T>(t);
-}
-
-} // namespace dbg
-
-#ifndef DBG_MACRO_DISABLE
-// We use a variadic macro to support commas inside expressions (e.g.
-// initializer lists):
-#define dbg(...) \
- dbg::DebugOutput(__FILE__, __LINE__, __func__, #__VA_ARGS__) \
- .print(dbg::type_name<decltype(__VA_ARGS__)>(), (__VA_ARGS__))
-#else
-#define dbg(...) dbg::identity(__VA_ARGS__)
-#endif // DBG_MACRO_DISABLE
-
-#endif // DBG_MACRO_DBG_H
diff --git a/lib/libc/mingw/lib32/user32.def b/lib/libc/mingw/lib32/user32.def
@@ -0,0 +1,998 @@
+LIBRARY USER32.dll
+EXPORTS
+;ord_1500@16 @1500
+;ord_1501@4 @1501
+;ord_1502@12 @1502
+ActivateKeyboardLayout@8
+AddClipboardFormatListener@4
+AdjustWindowRect@12
+AdjustWindowRectEx@16
+AlignRects@16
+AllowForegroundActivation@0
+AllowSetForegroundWindow@4
+AnimateWindow@12
+AnyPopup@0
+AppendMenuA@16
+AppendMenuW@16
+ArrangeIconicWindows@4
+AttachThreadInput@12
+BeginDeferWindowPos@4
+BeginPaint@8
+BlockInput@4
+BringWindowToTop@4
+BroadcastSystemMessage@20
+BroadcastSystemMessageA@20
+BroadcastSystemMessageExA@24
+BroadcastSystemMessageExW@24
+BroadcastSystemMessageW@20
+BuildReasonArray@12
+CalcChildScroll@8
+CalcMenuBar@20
+CalculatePopupWindowPosition@20
+CallMsgFilter@8
+CallMsgFilterA@8
+CallMsgFilterW@8
+CallNextHookEx@16
+CallWindowProcA@20
+CallWindowProcW@20
+CancelShutdown@0
+CascadeChildWindows@8
+CascadeWindows@20
+ChangeClipboardChain@8
+ChangeDisplaySettingsA@8
+ChangeDisplaySettingsExA@20
+ChangeDisplaySettingsExW@20
+ChangeDisplaySettingsW@8
+ChangeMenuA@20
+ChangeMenuW@20
+ChangeWindowMessageFilter@8
+ChangeWindowMessageFilterEx@16
+CharLowerA@4
+CharLowerBuffA@8
+CharLowerBuffW@8
+CharLowerW@4
+CharNextA@4
+;ord_1550@12 @1550
+;ord_1551@8 @1551
+;ord_1552@8 @1552
+;ord_1553@12 @1553
+;ord_1554@8 @1554
+;ord_1555@16 @1555
+;ord_1556@4 @1556
+CharNextExA@12
+CharNextW@4
+CharPrevA@8
+CharPrevExA@16
+CharPrevW@8
+CharToOemA@8
+CharToOemBuffA@12
+CharToOemBuffW@12
+CharToOemW@8
+CharUpperA@4
+CharUpperBuffA@8
+CharUpperBuffW@8
+CharUpperW@4
+CheckDesktopByThreadId@4
+CheckDBCSEnabledExt@0
+CheckDlgButton@12
+CheckMenuItem@12
+CheckMenuRadioItem@20
+CheckProcessForClipboardAccess@8
+CheckProcessSession@4
+CheckRadioButton@16
+CheckWindowThreadDesktop@8
+ChildWindowFromPoint@12
+ChildWindowFromPointEx@16
+CliImmSetHotKey@16
+ClientThreadSetup@0
+ClientToScreen@8
+ClipCursor@4
+CloseClipboard@0
+CloseDesktop@4
+CloseGestureInfoHandle@4
+CloseTouchInputHandle@4
+CloseWindow@4
+CloseWindowStation@4
+ConsoleControl@12
+ControlMagnification@8
+CopyAcceleratorTableA@12
+CopyAcceleratorTableW@12
+CopyIcon@4
+CopyImage@20
+CopyRect@8
+CountClipboardFormats@0
+CreateAcceleratorTableA@8
+CreateAcceleratorTableW@8
+CreateCaret@16
+CreateCursor@28
+CreateDCompositionHwndTarget@12
+CreateDesktopA@24
+CreateDesktopExA@32
+CreateDesktopExW@32
+CreateDesktopW@24
+CreateDialogIndirectParamA@20
+CreateDialogIndirectParamAorW@24
+CreateDialogIndirectParamW@20
+CreateDialogParamA@20
+CreateDialogParamW@20
+CreateIcon@28
+CreateIconFromResource@16
+CreateIconFromResourceEx@28
+CreateIconIndirect@4
+CreateMDIWindowA@40
+CreateMDIWindowW@40
+CreateMenu@0
+CreatePopupMenu@0
+CreateSystemThreads@16 ; ReactOS has the @8 variant
+CreateWindowExA@48
+CreateWindowExW@48
+CreateWindowInBand@52
+CreateWindowIndirect@4
+CreateWindowStationA@16
+CreateWindowStationW@16
+CsrBroadcastSystemMessageExW@24
+CtxInitUser32@0
+DdeAbandonTransaction@12
+DdeAccessData@8
+DdeAddData@16
+DdeClientTransaction@32
+DdeCmpStringHandles@8
+DdeConnect@16
+DdeConnectList@20
+DdeCreateDataHandle@28
+DdeCreateStringHandleA@12
+DdeCreateStringHandleW@12
+DdeDisconnect@4
+DdeDisconnectList@4
+DdeEnableCallback@12
+DdeFreeDataHandle@4
+DdeFreeStringHandle@8
+DdeGetData@16
+DdeGetLastError@4
+DdeGetQualityOfService@12
+DdeImpersonateClient@4
+DdeInitializeA@16
+DdeInitializeW@16
+DdeKeepStringHandle@8
+DdeNameService@16
+DdePostAdvise@12
+DdeQueryConvInfo@12
+DdeQueryNextServer@8
+DdeQueryStringA@20
+DdeQueryStringW@20
+DdeReconnect@4
+DdeSetQualityOfService@12
+DdeSetUserHandle@12
+DdeUnaccessData@4
+DdeUninitialize@4
+DefDlgProcA@16
+DefDlgProcW@16
+DefFrameProcA@20
+DefFrameProcW@20
+DefMDIChildProcA@16
+DefMDIChildProcW@16
+DefRawInputProc@12
+DefWindowProcA@16
+DefWindowProcW@16
+DeferWindowPos@32
+DeferWindowPosAndBand@36
+DeleteMenu@12
+DeregisterShellHookWindow@4
+DestroyAcceleratorTable@4
+DestroyCaret@0
+DestroyCursor@4
+DestroyDCompositionHwndTarget@8
+DestroyIcon@4
+DestroyMenu@4
+DestroyReasons@4
+DestroyWindow@4
+DeviceEventWorker@24 ; No documentation whatsoever, ReactOS has a stub with @20 - https://www.reactos.org/archives/public/ros-diffs/2011-February/040308.html
+DialogBoxIndirectParamA@20
+DialogBoxIndirectParamAorW@24
+DialogBoxIndirectParamW@20
+DialogBoxParamA@20
+DialogBoxParamW@20
+DisableProcessWindowsGhosting@0
+DispatchMessageA@4
+DispatchMessageW@4
+DisplayConfigGetDeviceInfo@4
+DisplayConfigSetDeviceInfo@4
+DisplayExitWindowsWarnings@4
+DlgDirListA@20
+DlgDirListComboBoxA@20
+DlgDirListComboBoxW@20
+DlgDirListW@20
+DlgDirSelectComboBoxExA@16
+DlgDirSelectComboBoxExW@16
+DlgDirSelectExA@16
+DlgDirSelectExW@16
+DoSoundConnect@0
+DoSoundDisconnect@0
+DragDetect@12
+DragObject@20
+DrawAnimatedRects@16
+DrawCaption@16
+DrawCaptionTempA@28
+DrawCaptionTempW@28
+DrawEdge@16
+DrawFocusRect@8
+DrawFrame@16
+DrawFrameControl@16
+DrawIcon@16
+DrawIconEx@36
+DrawMenuBar@4
+DrawMenuBarTemp@20
+DrawStateA@40
+DrawStateW@40
+DrawTextA@20
+DrawTextExA@24
+DrawTextExW@24
+DrawTextW@20
+DwmGetDxSharedSurface@24
+DwmGetRemoteSessionOcclusionEvent@0
+DwmGetRemoteSessionOcclusionState@0
+DwmLockScreenUpdates@4
+DwmStartRedirection@8 ; Mentioned on http://habrahabr.ru/post/145174/ , enables GDI virtualization (for security purposes)
+DwmStopRedirection@0
+DwmValidateWindow@8
+EditWndProc@16
+EmptyClipboard@0
+EnableMenuItem@12
+EnableMouseInPointer@4
+EnableScrollBar@12
+EnableSessionForMMCSS@4
+EnableWindow@8
+EndDeferWindowPos@4
+EndDeferWindowPosEx@8
+EndDialog@8
+EndMenu@0
+EndPaint@8
+EndTask@12
+EnterReaderModeHelper@4
+EnumChildWindows@12
+EnumClipboardFormats@4
+EnumDesktopWindows@12
+EnumDesktopsA@12
+EnumDesktopsW@12
+EnumDisplayDevicesA@16
+EnumDisplayDevicesW@16
+EnumDisplayMonitors@16
+EnumDisplaySettingsA@12
+EnumDisplaySettingsExA@16
+EnumDisplaySettingsExW@16
+EnumDisplaySettingsW@12
+EnumPropsA@8
+EnumPropsExA@12
+EnumPropsExW@12
+EnumPropsW@8
+EnumThreadWindows@12
+EnumWindowStationsA@8
+EnumWindowStationsW@8
+EnumWindows@8
+EqualRect@8
+EvaluateProximityToPolygon@16
+EvaluateProximityToRect@12
+ExcludeUpdateRgn@8
+ExitWindowsEx@8
+FillRect@12
+FindWindowA@8
+FindWindowExA@16
+FindWindowExW@16
+FindWindowW@8
+FlashWindow@8
+FlashWindowEx@4
+FrameRect@12
+FreeDDElParam@8
+FrostCrashedWindow@8
+GetActiveWindow@0
+GetAltTabInfo@20
+GetAltTabInfoA@20
+GetAltTabInfoW@20
+GetAncestor@8
+GetAppCompatFlags2@4
+GetAppCompatFlags@8 ; ReactOS has @4 version http://doxygen.reactos.org/d9/d71/undocuser_8h_a9b76cdc68c523a061c86a40367049ed2.html
+GetAsyncKeyState@4
+GetAutoRotationState@4
+GetCIMSSM@4
+GetCapture@0
+GetCaretBlinkTime@0
+GetCaretPos@4
+GetClassInfoA@12
+GetClassInfoExA@12
+GetClassInfoExW@12
+GetClassInfoW@12
+GetClassLongA@8
+GetClassLongW@8
+GetClassNameA@12
+GetClassNameW@12
+GetClassWord@8
+GetClientRect@8
+GetClipCursor@4
+GetClipboardAccessToken@8
+GetClipboardData@4
+GetClipboardFormatNameA@12
+GetClipboardFormatNameW@12
+GetClipboardOwner@0
+GetClipboardSequenceNumber@0
+GetClipboardViewer@0
+GetComboBoxInfo@8
+GetCurrentInputMessageSource@4
+GetCursor@0
+GetCursorFrameInfo@20
+GetCursorInfo@4
+GetCursorPos@4
+GetDC@4
+GetDCEx@12
+GetDesktopID@8
+GetDesktopWindow@0
+GetDialogBaseUnits@0
+GetDisplayAutoRotationPreferences@4
+GetDisplayConfigBufferSizes@12
+GetDlgCtrlID@4
+GetDlgItem@8
+GetDlgItemInt@16
+GetDlgItemTextA@16
+GetDlgItemTextW@16
+GetDoubleClickTime@0
+GetDpiForMonitorInternal@16
+GetFocus@0
+GetForegroundWindow@0
+GetGUIThreadInfo@8
+GetGestureConfig@24
+GetGestureExtraArgs@12
+GetGestureInfo@8
+GetGuiResources@8
+GetIconInfo@8
+GetIconInfoExA@8
+GetIconInfoExW@8
+GetInputDesktop@0
+GetInputLocaleInfo@8
+GetInputState@0
+GetInternalWindowPos@12
+GetKBCodePage@0
+GetKeyNameTextA@12
+GetKeyNameTextW@12
+GetKeyState@4
+GetKeyboardLayout@4
+GetKeyboardLayoutList@8
+GetKeyboardLayoutNameA@4
+GetKeyboardLayoutNameW@4
+GetKeyboardState@4
+GetKeyboardType@4
+GetLastActivePopup@4
+GetLastInputInfo@4
+GetLayeredWindowAttributes@16
+GetListBoxInfo@4
+GetMagnificationDesktopColorEffect@4
+GetMagnificationDesktopMagnification@12
+GetMagnificationLensCtxInformation@16
+GetMenu@4
+GetMenuBarInfo@16
+GetMenuCheckMarkDimensions@0
+GetMenuContextHelpId@4
+GetMenuDefaultItem@12
+GetMenuInfo@8
+GetMenuItemCount@4
+GetMenuItemID@8
+GetMenuItemInfoA@16
+GetMenuItemInfoW@16
+GetMenuItemRect@16
+GetMenuState@12
+GetMenuStringA@20
+GetMenuStringW@20
+GetMessageA@16
+GetMessageExtraInfo@0
+GetMessagePos@0
+GetMessageTime@0
+GetMessageW@16
+GetMonitorInfoA@8
+GetMonitorInfoW@8
+GetMouseMovePointsEx@20
+GetNextDlgGroupItem@12
+GetNextDlgTabItem@12
+GetOpenClipboardWindow@0
+GetParent@4
+GetPhysicalCursorPos@4
+GetPointerCursorId@8
+GetPointerDevice@8
+GetPointerDeviceCursors@12
+GetPointerDeviceProperties@12
+GetPointerDeviceRects@12
+GetPointerDevices@8
+GetPointerFrameInfo@12
+GetPointerFrameInfoHistory@16
+GetPointerFramePenInfo@12
+GetPointerFramePenInfoHistory@16
+GetPointerFrameTouchInfo@12
+GetPointerFrameTouchInfoHistory@16
+GetPointerInfo@8
+GetPointerInfoHistory@12
+GetPointerInputTransform@12
+GetPointerPenInfo@8
+GetPointerPenInfoHistory@12
+GetPointerTouchInfo@8
+GetPointerTouchInfoHistory@12
+GetPointerType@8
+GetPriorityClipboardFormat@8
+GetProcessDefaultLayout@4
+GetProcessDpiAwarenessInternal@8
+GetProcessWindowStation@0
+GetProgmanWindow@0
+GetPropA@8
+GetPropW@8
+GetQueueStatus@4
+GetRawInputBuffer@12
+GetRawInputData@20
+GetRawInputDeviceInfoA@16
+GetRawInputDeviceInfoW@16
+GetRawInputDeviceList@12
+GetRawPointerDeviceData@20
+GetReasonTitleFromReasonCode@12
+GetRegisteredRawInputDevices@12
+GetQueueStatus@4
+GetScrollBarInfo@12
+GetScrollInfo@12
+GetScrollPos@8
+GetScrollRange@16
+GetSendMessageReceiver@4
+GetShellWindow@0
+GetSubMenu@8
+GetSysColor@4
+GetSysColorBrush@4
+GetSystemMenu@8
+GetSystemMetrics@4
+GetTabbedTextExtentA@20
+GetTabbedTextExtentW@20
+GetTaskmanWindow@0
+GetThreadDesktop@4
+GetTitleBarInfo@8
+GetTopLevelWindow@4
+GetTopWindow@4
+GetTouchInputInfo@16
+GetUnpredictedMessagePos@0
+GetUpdateRect@12
+GetUpdateRgn@12
+GetUpdatedClipboardFormats@12
+GetUserObjectInformationA@20
+GetUserObjectInformationW@20
+GetUserObjectSecurity@20
+GetWinStationInfo@4
+GetWindow@8
+GetWindowBand@8
+GetWindowCompositionAttribute@8
+GetWindowCompositionInfo@8
+GetWindowContextHelpId@4
+GetWindowDC@4
+GetWindowDisplayAffinity@8
+GetWindowFeedbackSetting@20
+GetWindowInfo@8
+GetWindowLongA@8
+GetWindowLongW@8
+GetWindowMinimizeRect@8
+GetWindowModuleFileName@12
+GetWindowModuleFileNameA@12
+GetWindowModuleFileNameW@12
+GetWindowPlacement@8
+GetWindowRect@8
+GetWindowRgn@8
+GetWindowRgnBox@8
+GetWindowRgnEx@12
+GetWindowTextA@12
+GetWindowTextLengthA@4
+GetWindowTextLengthW@4
+GetWindowTextW@12
+GetWindowThreadProcessId@8
+GetWindowWord@8
+GhostWindowFromHungWindow@4
+GrayStringA@36
+GrayStringW@36
+HideCaret@4
+HiliteMenuItem@16
+HungWindowFromGhostWindow@4
+IMPGetIMEA@8
+IMPGetIMEW@8
+IMPQueryIMEA@4
+IMPQueryIMEW@4
+IMPSetIMEA@8
+IMPSetIMEW@8
+ImpersonateDdeClientWindow@8
+InSendMessage@0
+InSendMessageEx@4
+InflateRect@12
+InitializeLpkHooks@4
+InitializeWin32EntryTable@4
+InitializeTouchInjection@8
+InjectTouchInput@8
+InsertMenuA@20
+InsertMenuItemA@16
+InsertMenuItemW@16
+InsertMenuW@20
+InternalGetWindowIcon@8
+;ord_2001@4 @2001
+;ord_2002@4 @2002
+InternalGetWindowText@12
+IntersectRect@12
+;ord_2005@4 @2005
+InvalidateRect@12
+InvalidateRgn@12
+InvertRect@8
+IsCharAlphaA@4
+;ord_2010@16 @2010
+IsCharAlphaNumericA@4
+IsCharAlphaNumericW@4
+IsCharAlphaW@4
+IsCharLowerA@4
+IsCharLowerW@4
+IsCharUpperA@4
+IsCharUpperW@4
+IsChild@8
+IsClipboardFormatAvailable@4
+IsDialogMessage@8
+IsDialogMessageA@8
+IsDialogMessageW@8
+IsDlgButtonChecked@8
+IsGUIThread@4
+IsHungAppWindow@4
+IsIconic@4
+IsImmersiveProcess@4
+IsInDesktopWindowBand@4
+IsMenu@4
+IsProcess16Bit@0
+IsMouseInPointerEnabled@0
+IsProcessDPIAware@0
+IsQueueAttached@0
+IsRectEmpty@4
+IsSETEnabled@0
+IsServerSideWindow@4
+IsThreadDesktopComposited@0
+IsTopLevelWindow@4
+IsTouchWindow@8
+IsWinEventHookInstalled@4
+IsWindow@4
+IsWindowEnabled@4
+IsWindowInDestroy@4
+IsWindowRedirectedForPrint@4
+IsWindowUnicode@4
+IsWindowVisible@4
+IsWow64Message@0
+IsZoomed@4
+KillSystemTimer@8
+KillTimer@8
+LoadAcceleratorsA@8
+LoadAcceleratorsW@8
+LoadBitmapA@8
+LoadBitmapW@8
+LoadCursorA@8
+LoadCursorFromFileA@4
+LoadCursorFromFileW@4
+;ord_2000@0 @2000
+;ord_2001@4 @2001
+;ord_2002@4 @2002
+LoadCursorW@8
+LoadIconA@8
+;ord_2005@4 @2005
+LoadIconW@8
+LoadImageA@24
+LoadImageW@24
+LoadKeyboardLayoutA@8
+LoadKeyboardLayoutEx@12
+LoadKeyboardLayoutW@8
+LoadLocalFonts@0
+LoadMenuA@8
+LoadMenuIndirectA@4
+LoadMenuIndirectW@4
+LoadMenuW@8
+LoadRemoteFonts@0
+LoadStringA@16
+LoadStringW@16
+LockSetForegroundWindow@4
+LockWindowStation@4
+LockWindowUpdate@4
+LockWorkStation@0
+LogicalToPhysicalPoint@8
+LogicalToPhysicalPointForPerMonitorDPI@8
+LookupIconIdFromDirectory@8
+LookupIconIdFromDirectoryEx@20
+MBToWCSEx@24
+MBToWCSExt@20
+MB_GetString@4
+MapDialogRect@8
+MapVirtualKeyA@8
+MapVirtualKeyExA@12
+MapVirtualKeyExW@12
+MapVirtualKeyW@8
+MapWindowPoints@16
+MenuItemFromPoint@16
+MenuWindowProcA@20
+MenuWindowProcW@20
+MessageBeep@4
+MessageBoxA@16
+MessageBoxExA@20
+MessageBoxExW@20
+MessageBoxIndirectA@4
+MessageBoxIndirectW@4
+MessageBoxTimeoutA@24
+MessageBoxTimeoutW@24
+MessageBoxW@16
+ModifyMenuA@20
+ModifyMenuW@20
+MonitorFromPoint@12
+MonitorFromRect@8
+MonitorFromWindow@8
+MoveWindow@24
+MsgWaitForMultipleObjects@20
+MsgWaitForMultipleObjectsEx@20
+NotifyOverlayWindow@8
+NotifyWinEvent@16
+OemKeyScan@4
+OemToCharA@8
+OemToCharBuffA@12
+OemToCharBuffW@12
+OemToCharW@8
+OffsetRect@12
+OpenClipboard@4
+OpenDesktopA@16
+OpenDesktopW@16
+OpenIcon@4
+OpenInputDesktop@12
+OpenThreadDesktop@16
+OpenWindowStationA@12
+OpenWindowStationW@12
+PackDDElParam@12
+PackTouchHitTestingProximityEvaluation@8
+PaintDesktop@4
+PaintMenuBar@24
+PaintMonitor@12
+PeekMessageA@20
+PeekMessageW@20
+PhysicalToLogicalPoint@8
+PhysicalToLogicalPointForPerMonitorDPI@8
+PostMessageA@16
+PostMessageW@16
+PostQuitMessage@4
+PostThreadMessageA@16
+PostThreadMessageW@16
+PrintWindow@12
+PrivateExtractIconExA@20
+PrivateExtractIconExW@20
+PrivateExtractIconsA@32
+PrivateExtractIconsW@32
+PrivateSetDbgTag@8
+PrivateSetRipFlags@8
+PrivateRegisterICSProc@4
+PtInRect@12
+QueryBSDRWindow@0
+QueryDisplayConfig@24
+QuerySendMessage@4
+QueryUserCounters@20
+RealChildWindowFromPoint@12
+RealGetWindowClass@12
+RealGetWindowClassA@12
+RealGetWindowClassW@12
+ReasonCodeNeedsBugID@4
+ReasonCodeNeedsComment@4
+RecordShutdownReason@4
+RedrawWindow@16
+RegisterBSDRWindow@8
+RegisterClassA@4
+RegisterClassExA@4
+RegisterClassExW@4
+RegisterClassW@4
+RegisterClipboardFormatA@4
+RegisterClipboardFormatW@4
+RegisterDeviceNotificationA@12
+RegisterDeviceNotificationW@12
+RegisterErrorReportingDialog@8
+RegisterFrostWindow@8
+RegisterGhostWindow@8
+RegisterHotKey@16
+RegisterPowerSettingNotification@12
+RegisterLogonProcess@8
+RegisterMessagePumpHook@4
+RegisterPointerDeviceNotifications@8
+RegisterPointerInputTarget@8
+RegisterPowerSettingNotification@12
+RegisterRawInputDevices@12
+RegisterServicesProcess@4
+RegisterSessionPort@4 ; Undocumented, rumored to be related to ALPC - http://blogs.msdn.com/b/ntdebugging/archive/2007/07/26/lpc-local-procedure-calls-part-1-architecture.aspx
+RegisterShellHookWindow@4
+RegisterSuspendResumeNotification@8
+RegisterSystemThread@8
+RegisterTasklist@4
+RegisterTouchHitTestingWindow@8
+RegisterTouchWindow@8
+RegisterUserApiHook@4 ; Prototype changed in 2003 - https://www.reactos.org/wiki/Techwiki:RegisterUserApiHook
+RegisterWindowMessageA@4
+RegisterWindowMessageW@4
+ReleaseCapture@0
+ReleaseDC@8
+RemoveClipboardFormatListener@4
+RemoveMenu@12
+RemovePropA@8
+RemovePropW@8
+ReplyMessage@4
+ResolveDesktopForWOW@4
+ReuseDDElParam@20
+ScreenToClient@8
+ScrollChildren@12
+ScrollDC@28
+ScrollWindow@20
+ScrollWindowEx@32
+SendDlgItemMessageA@20
+SendDlgItemMessageW@20
+SendIMEMessageExA@8
+SendIMEMessageExW@8
+SendInput@12
+SendMessageA@16
+SendMessageCallbackA@24
+SendMessageCallbackW@24
+SendMessageTimeoutA@28
+SendMessageTimeoutW@28
+SendMessageW@16
+SendNotifyMessageA@16
+SendNotifyMessageW@16
+SetActiveWindow@4
+SetCapture@4
+SetCaretBlinkTime@4
+SetCaretPos@8
+SetClassLongA@12
+SetClassLongW@12
+SetClassWord@12
+SetClipboardData@8
+SetClipboardViewer@4
+SetConsoleReserveKeys@8
+SetCoalescableTimer@20
+SetCursor@4
+SetCursorContents@8
+SetCursorPos@8
+SetDebugErrorLevel@4
+SetDeskWallpaper@4
+SetDisplayAutoRotationPreferences@4
+SetDisplayConfig@20
+SetDlgItemInt@16
+SetDlgItemTextA@12
+SetDlgItemTextW@12
+SetDoubleClickTime@4
+SetFocus@4
+SetForegroundWindow@4
+SetGestureConfig@20
+SetImmersiveBackgroundWindow@4
+SetInternalWindowPos@16
+SetKeyboardState@4
+SetLastErrorEx@8
+SetLayeredWindowAttributes@16
+SetLogonNotifyWindow@4
+SetMagnificationDesktopColorEffect@4
+SetMagnificationDesktopMagnification@16
+SetMagnificationLensCtxInformation@16
+SetMenu@8
+SetMenuContextHelpId@8
+SetMenuDefaultItem@12
+SetMenuInfo@8
+SetMenuItemBitmaps@20
+SetMenuItemInfoA@16
+SetMenuItemInfoW@16
+SetMessageExtraInfo@4
+SetMessageQueue@4
+SetMirrorRendering@8
+SetParent@8
+SetPhysicalCursorPos@8
+SetProcessDPIAware@0
+SetProcessDefaultLayout@4
+SetProcessDpiAwarenessInternal@4
+SetProcessRestrictionExemption@4
+SetProcessWindowStation@4
+SetProgmanWindow@4
+SetPropA@12
+SetPropW@12
+SetRect@20
+SetRectEmpty@4
+SetScrollInfo@16
+SetScrollPos@16
+SetScrollRange@20
+SetShellWindow@4
+SetShellWindowEx@8
+SetSysColors@12
+SetSysColorsTemp@12
+SetSystemCursor@8
+SetSystemMenu@8
+SetSystemTimer@16
+SetTaskmanWindow@4
+SetThreadDesktop@4
+SetThreadInputBlocked@8
+SetTimer@16
+SetUserObjectInformationA@16
+SetUserObjectInformationW@16
+SetUserObjectSecurity@12
+SetWinEventHook@28
+SetWindowBand@12
+SetWindowCompositionAttribute@8
+SetWindowCompositionTransition@28
+SetWindowContextHelpId@8
+SetWindowDisplayAffinity@8
+SetWindowFeedbackSetting@20
+SetWindowLongA@12
+SetWindowLongW@12
+SetWindowPlacement@8
+SetWindowPos@28
+SetWindowRgn@12
+SetWindowRgnEx@12
+SetWindowStationUser@16
+SetWindowTextA@8
+SetWindowTextW@8
+SetWindowWord@12
+SetWindowsHookA@8
+SetWindowsHookExA@16
+SetWindowsHookExW@16
+SetWindowsHookW@8
+SfmDxBindSwapChain@12
+SfmDxGetSwapChainStats@8
+SfmDxOpenSwapChain@16
+SfmDxQuerySwapChainBindingStatus@12
+SfmDxReleaseSwapChain@8
+SfmDxReportPendingBindingsToDwm@0
+SfmDxSetSwapChainBindingStatus@8
+SfmDxSetSwapChainStats@8
+ShowCaret@4
+ShowCursor@4
+ShowOwnedPopups@8
+ShowScrollBar@12
+ShowStartGlass@4
+ShowSystemCursor@4
+ShowWindow@8
+ShowWindowAsync@8
+ShutdownBlockReasonCreate@8
+ShutdownBlockReasonDestroy@4
+ShutdownBlockReasonQuery@12
+SignalRedirectionStartComplete@0
+SkipPointerFrameMessages@4
+SoftModalMessageBox@4
+SoundSentry@0
+SubtractRect@12
+SwapMouseButton@4
+SwitchDesktop@4
+SwitchDesktopWithFade@12 ; Same as SwithDesktop(), only with fade (done at log-in), only usable by winlogon - http://blog.airesoft.co.uk/2010/08/things-microsoft-can-do-that-you-cant/
+SwitchToThisWindow@8
+SystemParametersInfoA@16
+SystemParametersInfoW@16
+TabbedTextOutA@32
+TabbedTextOutW@32
+TileChildWindows@8
+TileWindows@20
+ToAscii@20
+ToAsciiEx@24
+ToUnicode@24
+ToUnicodeEx@28
+TrackMouseEvent@4
+TrackPopupMenu@28
+TrackPopupMenuEx@24
+TranslateAccelerator@12
+TranslateAcceleratorA@12
+TranslateAcceleratorW@12
+TranslateMDISysAccel@8
+TranslateMessage@4
+TranslateMessageEx@8
+UnhookWinEvent@4
+UnhookWindowsHook@8
+UnhookWindowsHookEx@4
+UnionRect@12
+UnloadKeyboardLayout@4
+UnlockWindowStation@4
+UnpackDDElParam@16
+UnregisterClassA@8
+UnregisterClassW@8
+UnregisterDeviceNotification@4
+UnregisterHotKey@8
+UnregisterMessagePumpHook@0
+UnregisterPointerInputTarget@8
+UnregisterPowerSettingNotification@4
+UnregisterSessionPort@0
+UnregisterSuspendResumeNotification@4
+UnregisterTouchWindow@4
+UnregisterUserApiHook@0
+UpdateDefaultDesktopThumbnail@20
+UpdateLayeredWindow@36
+UpdateLayeredWindowIndirect@8
+UpdatePerUserSystemParameters@4 ; Undocumented, seems to apply certain registry settings to desktop, etc. ReactOS has @8 version - http://doxygen.reactos.org/d0/d92/win32ss_2user_2user32_2misc_2misc_8c_a1ff565f0af6bac6dce604f9f4473fe79.html ; @4 is rumored to be without the first DWORD
+UpdateWindow@4
+UpdateWindowInputSinkHints@8
+UpdateWindowTransform@12
+User32InitializeImmEntryTable@4
+UserClientDllInitialize@12
+UserHandleGrantAccess@12
+UserLpkPSMTextOut@24
+UserLpkTabbedTextOut@48
+UserRealizePalette@4
+UserRegisterWowHandlers@8
+VRipOutput@0
+VTagOutput@0
+ValidateRect@8
+ValidateRgn@8
+VkKeyScanA@4
+VkKeyScanExA@8
+VkKeyScanExW@8
+VkKeyScanW@4
+WCSToMBEx@24
+WINNLSEnableIME@8
+WINNLSGetEnableStatus@4
+WINNLSGetIMEHotkey@4
+WaitForInputIdle@8
+WaitForRedirectionStartComplete@0
+WaitMessage@0
+Win32PoolAllocationStats@24
+WinHelpA@16
+WinHelpW@16
+WindowFromDC@4
+WindowFromPhysicalPoint@8
+WindowFromPoint@8
+_UserTestTokenForInteractive@8
+gSharedInfo DATA
+gapfnScSendMessage DATA
+keybd_event@16
+mouse_event@20
+wsprintfA
+wsprintfW
+wvsprintfA@12
+wvsprintfW@12
+;ord_2500@16 @2500
+;ord_2501@12 @2501
+;ord_2502@8 @2502
+;ord_2503@24 @2503
+;ord_2504@8 @2504
+;ord_2505@8 @2505
+;ord_2506@12 @2506
+;ord_2507@4 @2507
+;ord_2508@8 @2508
+;ord_2509@4 @2509
+;ord_2510@12 @2510
+;ord_2511@8 @2511
+;ord_2512@12 @2512
+;ord_2513@4 @2513
+;ord_2514@8 @2514
+;ord_2515@8 @2515
+;ord_2516@12 @2516
+;ord_2517@4 @2517
+;ord_2518@0 @2518
+;ord_2519@4 @2519
+;ord_2520@0 @2520
+;ord_2521@8 @2521
+;ord_2522@4 @2522
+;ord_2523@8 @2523
+;ord_2524@8 @2524
+;ord_2525@12 @2525
+;ord_2526@12 @2526
+;ord_2527@12 @2527
+IsThreadMessageQueueAttached@4
+;ord_2529@4 @2529
+;ord_2530@8 @2530
+;ord_2531@16 @2531
+;ord_2532@8 @2532
+;ord_2533@4 @2533
+;ord_2534@8 @2534
+;ord_2535@0 @2535
+;ord_2536@8 @2536
+;ord_2537@16 @2537
+;ord_2538@4 @2538
+;ord_2539@4 @2539
+;ord_2540@4 @2540
+;ord_2541@0 @2541
+;ord_2544@4 @2544
+;ord_2545@8 @2545
+;ord_2546@4 @2546
+;ord_2547@4 @2547
+;ord_2548@4 @2548
+;ord_2549@4 @2549
+;ord_2550@8 @2550
+;ord_2551@20 @2551
+;ord_2552@8 @2552
+;ord_2553@32 @2553
+;ord_2554@12 @2554
+;ord_2555@16 @2555
+;ord_2556@8 @2556
+;ord_2557@12 @2557
+;ord_2558@12 @2558
+;ord_2559@16 @2559
+;ord_2560@20 @2560
+;ord_2561@0 @2561
+;ord_2562@0 @2562
+;ord_2563@0 @2563
diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig
@@ -371,7 +371,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
var self = Self{};
- const new_memory = try self.allocator.allocAdvanced(T, alignment, num, .at_least);
+ const new_memory = try allocator.allocAdvanced(T, alignment, num, .at_least);
self.items.ptr = new_memory.ptr;
self.capacity = new_memory.len;
@@ -419,7 +419,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
/// Replace range of elements `list[start..start+len]` with `new_items`
/// grows list if `len < new_items.len`. may allocate
/// shrinks list if `len > new_items.len`
- pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: SliceConst) !void {
+ pub fn replaceRange(self: *Self, allocator: *Allocator, start: usize, len: usize, new_items: SliceConst) !void {
var managed = self.toManaged(allocator);
try managed.replaceRange(start, len, new_items);
self.* = managed.toUnmanaged();
@@ -617,201 +617,414 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
};
}
-test "std.ArrayList.init" {
- var list = ArrayList(i32).init(testing.allocator);
- defer list.deinit();
+test "std.ArrayList/ArrayListUnmanaged.init" {
+ {
+ var list = ArrayList(i32).init(testing.allocator);
+ defer list.deinit();
- testing.expect(list.items.len == 0);
- testing.expect(list.capacity == 0);
-}
+ testing.expect(list.items.len == 0);
+ testing.expect(list.capacity == 0);
+ }
-test "std.ArrayList.initCapacity" {
- var list = try ArrayList(i8).initCapacity(testing.allocator, 200);
- defer list.deinit();
- testing.expect(list.items.len == 0);
- testing.expect(list.capacity >= 200);
-}
+ {
+ var list = ArrayListUnmanaged(i32){};
-test "std.ArrayList.basic" {
- var list = ArrayList(i32).init(testing.allocator);
- defer list.deinit();
+ testing.expect(list.items.len == 0);
+ testing.expect(list.capacity == 0);
+ }
+}
+test "std.ArrayList/ArrayListUnmanaged.initCapacity" {
+ const a = testing.allocator;
{
- var i: usize = 0;
- while (i < 10) : (i += 1) {
- list.append(@intCast(i32, i + 1)) catch unreachable;
- }
+ var list = try ArrayList(i8).initCapacity(a, 200);
+ defer list.deinit();
+ testing.expect(list.items.len == 0);
+ testing.expect(list.capacity >= 200);
+ }
+ {
+ var list = try ArrayListUnmanaged(i8).initCapacity(a, 200);
+ defer list.deinit(a);
+ testing.expect(list.items.len == 0);
+ testing.expect(list.capacity >= 200);
}
+}
+test "std.ArrayList/ArrayListUnmanaged.basic" {
+ const a = testing.allocator;
{
- var i: usize = 0;
- while (i < 10) : (i += 1) {
- testing.expect(list.items[i] == @intCast(i32, i + 1));
+ var list = ArrayList(i32).init(a);
+ defer list.deinit();
+
+ {
+ var i: usize = 0;
+ while (i < 10) : (i += 1) {
+ list.append(@intCast(i32, i + 1)) catch unreachable;
+ }
+ }
+
+ {
+ var i: usize = 0;
+ while (i < 10) : (i += 1) {
+ testing.expect(list.items[i] == @intCast(i32, i + 1));
+ }
+ }
+
+ for (list.items) |v, i| {
+ testing.expect(v == @intCast(i32, i + 1));
}
- }
- for (list.items) |v, i| {
- testing.expect(v == @intCast(i32, i + 1));
+ testing.expect(list.pop() == 10);
+ testing.expect(list.items.len == 9);
+
+ list.appendSlice(&[_]i32{ 1, 2, 3 }) catch unreachable;
+ testing.expect(list.items.len == 12);
+ testing.expect(list.pop() == 3);
+ testing.expect(list.pop() == 2);
+ testing.expect(list.pop() == 1);
+ testing.expect(list.items.len == 9);
+
+ list.appendSlice(&[_]i32{}) catch unreachable;
+ testing.expect(list.items.len == 9);
+
+ // can only set on indices < self.items.len
+ list.items[7] = 33;
+ list.items[8] = 42;
+
+ testing.expect(list.pop() == 42);
+ testing.expect(list.pop() == 33);
}
+ {
+ var list = ArrayListUnmanaged(i32){};
+ defer list.deinit(a);
+
+ {
+ var i: usize = 0;
+ while (i < 10) : (i += 1) {
+ list.append(a, @intCast(i32, i + 1)) catch unreachable;
+ }
+ }
+
+ {
+ var i: usize = 0;
+ while (i < 10) : (i += 1) {
+ testing.expect(list.items[i] == @intCast(i32, i + 1));
+ }
+ }
+
+ for (list.items) |v, i| {
+ testing.expect(v == @intCast(i32, i + 1));
+ }
- testing.expect(list.pop() == 10);
- testing.expect(list.items.len == 9);
+ testing.expect(list.pop() == 10);
+ testing.expect(list.items.len == 9);
- list.appendSlice(&[_]i32{ 1, 2, 3 }) catch unreachable;
- testing.expect(list.items.len == 12);
- testing.expect(list.pop() == 3);
- testing.expect(list.pop() == 2);
- testing.expect(list.pop() == 1);
- testing.expect(list.items.len == 9);
+ list.appendSlice(a, &[_]i32{ 1, 2, 3 }) catch unreachable;
+ testing.expect(list.items.len == 12);
+ testing.expect(list.pop() == 3);
+ testing.expect(list.pop() == 2);
+ testing.expect(list.pop() == 1);
+ testing.expect(list.items.len == 9);
- list.appendSlice(&[_]i32{}) catch unreachable;
- testing.expect(list.items.len == 9);
+ list.appendSlice(a, &[_]i32{}) catch unreachable;
+ testing.expect(list.items.len == 9);
- // can only set on indices < self.items.len
- list.items[7] = 33;
- list.items[8] = 42;
+ // can only set on indices < self.items.len
+ list.items[7] = 33;
+ list.items[8] = 42;
- testing.expect(list.pop() == 42);
- testing.expect(list.pop() == 33);
+ testing.expect(list.pop() == 42);
+ testing.expect(list.pop() == 33);
+ }
}
-test "std.ArrayList.appendNTimes" {
- var list = ArrayList(i32).init(testing.allocator);
- defer list.deinit();
+test "std.ArrayList/ArrayListUnmanaged.appendNTimes" {
+ const a = testing.allocator;
+ {
+ var list = ArrayList(i32).init(a);
+ defer list.deinit();
+
+ try list.appendNTimes(2, 10);
+ testing.expectEqual(@as(usize, 10), list.items.len);
+ for (list.items) |element| {
+ testing.expectEqual(@as(i32, 2), element);
+ }
+ }
+ {
+ var list = ArrayListUnmanaged(i32){};
+ defer list.deinit(a);
- try list.appendNTimes(2, 10);
- testing.expectEqual(@as(usize, 10), list.items.len);
- for (list.items) |element| {
- testing.expectEqual(@as(i32, 2), element);
+ try list.appendNTimes(a, 2, 10);
+ testing.expectEqual(@as(usize, 10), list.items.len);
+ for (list.items) |element| {
+ testing.expectEqual(@as(i32, 2), element);
+ }
}
}
-test "std.ArrayList.appendNTimes with failing allocator" {
- var list = ArrayList(i32).init(testing.failing_allocator);
- defer list.deinit();
- testing.expectError(error.OutOfMemory, list.appendNTimes(2, 10));
+test "std.ArrayList/ArrayListUnmanaged.appendNTimes with failing allocator" {
+ const a = testing.failing_allocator;
+ {
+ var list = ArrayList(i32).init(a);
+ defer list.deinit();
+ testing.expectError(error.OutOfMemory, list.appendNTimes(2, 10));
+ }
+ {
+ var list = ArrayListUnmanaged(i32){};
+ defer list.deinit(a);
+ testing.expectError(error.OutOfMemory, list.appendNTimes(a, 2, 10));
+ }
}
-test "std.ArrayList.orderedRemove" {
- var list = ArrayList(i32).init(testing.allocator);
- defer list.deinit();
+test "std.ArrayList/ArrayListUnmanaged.orderedRemove" {
+ const a = testing.allocator;
+ {
+ var list = ArrayList(i32).init(a);
+ defer list.deinit();
+
+ try list.append(1);
+ try list.append(2);
+ try list.append(3);
+ try list.append(4);
+ try list.append(5);
+ try list.append(6);
+ try list.append(7);
+
+ //remove from middle
+ testing.expectEqual(@as(i32, 4), list.orderedRemove(3));
+ testing.expectEqual(@as(i32, 5), list.items[3]);
+ testing.expectEqual(@as(usize, 6), list.items.len);
+
+ //remove from end
+ testing.expectEqual(@as(i32, 7), list.orderedRemove(5));
+ testing.expectEqual(@as(usize, 5), list.items.len);
+
+ //remove from front
+ testing.expectEqual(@as(i32, 1), list.orderedRemove(0));
+ testing.expectEqual(@as(i32, 2), list.items[0]);
+ testing.expectEqual(@as(usize, 4), list.items.len);
+ }
+ {
+ var list = ArrayListUnmanaged(i32){};
+ defer list.deinit(a);
- try list.append(1);
- try list.append(2);
- try list.append(3);
- try list.append(4);
- try list.append(5);
- try list.append(6);
- try list.append(7);
-
- //remove from middle
- testing.expectEqual(@as(i32, 4), list.orderedRemove(3));
- testing.expectEqual(@as(i32, 5), list.items[3]);
- testing.expectEqual(@as(usize, 6), list.items.len);
-
- //remove from end
- testing.expectEqual(@as(i32, 7), list.orderedRemove(5));
- testing.expectEqual(@as(usize, 5), list.items.len);
-
- //remove from front
- testing.expectEqual(@as(i32, 1), list.orderedRemove(0));
- testing.expectEqual(@as(i32, 2), list.items[0]);
- testing.expectEqual(@as(usize, 4), list.items.len);
+ try list.append(a, 1);
+ try list.append(a, 2);
+ try list.append(a, 3);
+ try list.append(a, 4);
+ try list.append(a, 5);
+ try list.append(a, 6);
+ try list.append(a, 7);
+
+ //remove from middle
+ testing.expectEqual(@as(i32, 4), list.orderedRemove(3));
+ testing.expectEqual(@as(i32, 5), list.items[3]);
+ testing.expectEqual(@as(usize, 6), list.items.len);
+
+ //remove from end
+ testing.expectEqual(@as(i32, 7), list.orderedRemove(5));
+ testing.expectEqual(@as(usize, 5), list.items.len);
+
+ //remove from front
+ testing.expectEqual(@as(i32, 1), list.orderedRemove(0));
+ testing.expectEqual(@as(i32, 2), list.items[0]);
+ testing.expectEqual(@as(usize, 4), list.items.len);
+ }
}
-test "std.ArrayList.swapRemove" {
- var list = ArrayList(i32).init(testing.allocator);
- defer list.deinit();
+test "std.ArrayList/ArrayListUnmanaged.swapRemove" {
+ const a = testing.allocator;
+ {
+ var list = ArrayList(i32).init(a);
+ defer list.deinit();
- try list.append(1);
- try list.append(2);
- try list.append(3);
- try list.append(4);
- try list.append(5);
- try list.append(6);
- try list.append(7);
-
- //remove from middle
- testing.expect(list.swapRemove(3) == 4);
- testing.expect(list.items[3] == 7);
- testing.expect(list.items.len == 6);
-
- //remove from end
- testing.expect(list.swapRemove(5) == 6);
- testing.expect(list.items.len == 5);
-
- //remove from front
- testing.expect(list.swapRemove(0) == 1);
- testing.expect(list.items[0] == 5);
- testing.expect(list.items.len == 4);
+ try list.append(1);
+ try list.append(2);
+ try list.append(3);
+ try list.append(4);
+ try list.append(5);
+ try list.append(6);
+ try list.append(7);
+
+ //remove from middle
+ testing.expect(list.swapRemove(3) == 4);
+ testing.expect(list.items[3] == 7);
+ testing.expect(list.items.len == 6);
+
+ //remove from end
+ testing.expect(list.swapRemove(5) == 6);
+ testing.expect(list.items.len == 5);
+
+ //remove from front
+ testing.expect(list.swapRemove(0) == 1);
+ testing.expect(list.items[0] == 5);
+ testing.expect(list.items.len == 4);
+ }
+ {
+ var list = ArrayListUnmanaged(i32){};
+ defer list.deinit(a);
+
+ try list.append(a, 1);
+ try list.append(a, 2);
+ try list.append(a, 3);
+ try list.append(a, 4);
+ try list.append(a, 5);
+ try list.append(a, 6);
+ try list.append(a, 7);
+
+ //remove from middle
+ testing.expect(list.swapRemove(3) == 4);
+ testing.expect(list.items[3] == 7);
+ testing.expect(list.items.len == 6);
+
+ //remove from end
+ testing.expect(list.swapRemove(5) == 6);
+ testing.expect(list.items.len == 5);
+
+ //remove from front
+ testing.expect(list.swapRemove(0) == 1);
+ testing.expect(list.items[0] == 5);
+ testing.expect(list.items.len == 4);
+ }
}
-test "std.ArrayList.insert" {
- var list = ArrayList(i32).init(testing.allocator);
- defer list.deinit();
+test "std.ArrayList/ArrayListUnmanaged.insert" {
+ const a = testing.allocator;
+ {
+ var list = ArrayList(i32).init(a);
+ defer list.deinit();
- try list.append(1);
- try list.append(2);
- try list.append(3);
- try list.insert(0, 5);
- testing.expect(list.items[0] == 5);
- testing.expect(list.items[1] == 1);
- testing.expect(list.items[2] == 2);
- testing.expect(list.items[3] == 3);
+ try list.append(1);
+ try list.append(2);
+ try list.append(3);
+ try list.insert(0, 5);
+ testing.expect(list.items[0] == 5);
+ testing.expect(list.items[1] == 1);
+ testing.expect(list.items[2] == 2);
+ testing.expect(list.items[3] == 3);
+ }
+ {
+ var list = ArrayListUnmanaged(i32){};
+ defer list.deinit(a);
+
+ try list.append(a, 1);
+ try list.append(a, 2);
+ try list.append(a, 3);
+ try list.insert(a, 0, 5);
+ testing.expect(list.items[0] == 5);
+ testing.expect(list.items[1] == 1);
+ testing.expect(list.items[2] == 2);
+ testing.expect(list.items[3] == 3);
+ }
}
-test "std.ArrayList.insertSlice" {
- var list = ArrayList(i32).init(testing.allocator);
- defer list.deinit();
+test "std.ArrayList/ArrayListUnmanaged.insertSlice" {
+ const a = testing.allocator;
+ {
+ var list = ArrayList(i32).init(a);
+ defer list.deinit();
- try list.append(1);
- try list.append(2);
- try list.append(3);
- try list.append(4);
- try list.insertSlice(1, &[_]i32{ 9, 8 });
- testing.expect(list.items[0] == 1);
- testing.expect(list.items[1] == 9);
- testing.expect(list.items[2] == 8);
- testing.expect(list.items[3] == 2);
- testing.expect(list.items[4] == 3);
- testing.expect(list.items[5] == 4);
-
- const items = [_]i32{1};
- try list.insertSlice(0, items[0..0]);
- testing.expect(list.items.len == 6);
- testing.expect(list.items[0] == 1);
+ try list.append(1);
+ try list.append(2);
+ try list.append(3);
+ try list.append(4);
+ try list.insertSlice(1, &[_]i32{ 9, 8 });
+ testing.expect(list.items[0] == 1);
+ testing.expect(list.items[1] == 9);
+ testing.expect(list.items[2] == 8);
+ testing.expect(list.items[3] == 2);
+ testing.expect(list.items[4] == 3);
+ testing.expect(list.items[5] == 4);
+
+ const items = [_]i32{1};
+ try list.insertSlice(0, items[0..0]);
+ testing.expect(list.items.len == 6);
+ testing.expect(list.items[0] == 1);
+ }
+ {
+ var list = ArrayListUnmanaged(i32){};
+ defer list.deinit(a);
+
+ try list.append(a, 1);
+ try list.append(a, 2);
+ try list.append(a, 3);
+ try list.append(a, 4);
+ try list.insertSlice(a, 1, &[_]i32{ 9, 8 });
+ testing.expect(list.items[0] == 1);
+ testing.expect(list.items[1] == 9);
+ testing.expect(list.items[2] == 8);
+ testing.expect(list.items[3] == 2);
+ testing.expect(list.items[4] == 3);
+ testing.expect(list.items[5] == 4);
+
+ const items = [_]i32{1};
+ try list.insertSlice(a, 0, items[0..0]);
+ testing.expect(list.items.len == 6);
+ testing.expect(list.items[0] == 1);
+ }
}
-test "std.ArrayList.replaceRange" {
+test "std.ArrayList/ArrayListUnmanaged.replaceRange" {
var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit();
+ const a = &arena.allocator;
- const alloc = &arena.allocator;
const init = [_]i32{ 1, 2, 3, 4, 5 };
const new = [_]i32{ 0, 0, 0 };
- var list_zero = ArrayList(i32).init(alloc);
- var list_eq = ArrayList(i32).init(alloc);
- var list_lt = ArrayList(i32).init(alloc);
- var list_gt = ArrayList(i32).init(alloc);
+ const result_zero = [_]i32{ 1, 0, 0, 0, 2, 3, 4, 5 };
+ const result_eq = [_]i32{ 1, 0, 0, 0, 5 };
+ const result_le = [_]i32{ 1, 0, 0, 0, 4, 5 };
+ const result_gt = [_]i32{ 1, 0, 0, 0 };
- try list_zero.appendSlice(&init);
- try list_eq.appendSlice(&init);
- try list_lt.appendSlice(&init);
- try list_gt.appendSlice(&init);
-
- try list_zero.replaceRange(1, 0, &new);
- try list_eq.replaceRange(1, 3, &new);
- try list_lt.replaceRange(1, 2, &new);
-
- // after_range > new_items.len in function body
- testing.expect(1 + 4 > new.len);
- try list_gt.replaceRange(1, 4, &new);
-
- testing.expectEqualSlices(i32, list_zero.items, &[_]i32{ 1, 0, 0, 0, 2, 3, 4, 5 });
- testing.expectEqualSlices(i32, list_eq.items, &[_]i32{ 1, 0, 0, 0, 5 });
- testing.expectEqualSlices(i32, list_lt.items, &[_]i32{ 1, 0, 0, 0, 4, 5 });
- testing.expectEqualSlices(i32, list_gt.items, &[_]i32{ 1, 0, 0, 0 });
+ {
+ var list_zero = ArrayList(i32).init(a);
+ var list_eq = ArrayList(i32).init(a);
+ var list_lt = ArrayList(i32).init(a);
+ var list_gt = ArrayList(i32).init(a);
+
+ try list_zero.appendSlice(&init);
+ try list_eq.appendSlice(&init);
+ try list_lt.appendSlice(&init);
+ try list_gt.appendSlice(&init);
+
+ try list_zero.replaceRange(1, 0, &new);
+ try list_eq.replaceRange(1, 3, &new);
+ try list_lt.replaceRange(1, 2, &new);
+
+ // after_range > new_items.len in function body
+ testing.expect(1 + 4 > new.len);
+ try list_gt.replaceRange(1, 4, &new);
+
+ testing.expectEqualSlices(i32, list_zero.items, &result_zero);
+ testing.expectEqualSlices(i32, list_eq.items, &result_eq);
+ testing.expectEqualSlices(i32, list_lt.items, &result_le);
+ testing.expectEqualSlices(i32, list_gt.items, &result_gt);
+ }
+ {
+ var list_zero = ArrayListUnmanaged(i32){};
+ var list_eq = ArrayListUnmanaged(i32){};
+ var list_lt = ArrayListUnmanaged(i32){};
+ var list_gt = ArrayListUnmanaged(i32){};
+
+ try list_zero.appendSlice(a, &init);
+ try list_eq.appendSlice(a, &init);
+ try list_lt.appendSlice(a, &init);
+ try list_gt.appendSlice(a, &init);
+
+ try list_zero.replaceRange(a, 1, 0, &new);
+ try list_eq.replaceRange(a, 1, 3, &new);
+ try list_lt.replaceRange(a, 1, 2, &new);
+
+ // after_range > new_items.len in function body
+ testing.expect(1 + 4 > new.len);
+ try list_gt.replaceRange(a, 1, 4, &new);
+
+ testing.expectEqualSlices(i32, list_zero.items, &result_zero);
+ testing.expectEqualSlices(i32, list_eq.items, &result_eq);
+ testing.expectEqualSlices(i32, list_lt.items, &result_le);
+ testing.expectEqualSlices(i32, list_gt.items, &result_gt);
+ }
}
const Item = struct {
@@ -819,11 +1032,25 @@ const Item = struct {
sub_items: ArrayList(Item),
};
-test "std.ArrayList: ArrayList(T) of struct T" {
- var root = Item{ .integer = 1, .sub_items = ArrayList(Item).init(testing.allocator) };
- defer root.sub_items.deinit();
- try root.sub_items.append(Item{ .integer = 42, .sub_items = ArrayList(Item).init(testing.allocator) });
- testing.expect(root.sub_items.items[0].integer == 42);
+const ItemUnmanaged = struct {
+ integer: i32,
+ sub_items: ArrayListUnmanaged(ItemUnmanaged),
+};
+
+test "std.ArrayList/ArrayListUnmanaged: ArrayList(T) of struct T" {
+ const a = std.testing.allocator;
+ {
+ var root = Item{ .integer = 1, .sub_items = ArrayList(Item).init(a) };
+ defer root.sub_items.deinit();
+ try root.sub_items.append(Item{ .integer = 42, .sub_items = ArrayList(Item).init(a) });
+ testing.expect(root.sub_items.items[0].integer == 42);
+ }
+ {
+ var root = ItemUnmanaged{ .integer = 1, .sub_items = ArrayListUnmanaged(ItemUnmanaged){} };
+ defer root.sub_items.deinit(a);
+ try root.sub_items.append(a, ItemUnmanaged{ .integer = 42, .sub_items = ArrayListUnmanaged(ItemUnmanaged){} });
+ testing.expect(root.sub_items.items[0].integer == 42);
+ }
}
test "std.ArrayList(u8) implements outStream" {
@@ -837,19 +1064,32 @@ test "std.ArrayList(u8) implements outStream" {
testing.expectEqualSlices(u8, "x: 42\ny: 1234\n", buffer.span());
}
-test "std.ArrayList.shrink still sets length on error.OutOfMemory" {
+test "std.ArrayList/ArrayListUnmanaged.shrink still sets length on error.OutOfMemory" {
// use an arena allocator to make sure realloc returns error.OutOfMemory
var arena = std.heap.ArenaAllocator.init(testing.allocator);
defer arena.deinit();
+ const a = &arena.allocator;
- var list = ArrayList(i32).init(&arena.allocator);
+ {
+ var list = ArrayList(i32).init(a);
- try list.append(1);
- try list.append(2);
- try list.append(3);
+ try list.append(1);
+ try list.append(2);
+ try list.append(3);
- list.shrink(1);
- testing.expect(list.items.len == 1);
+ list.shrink(1);
+ testing.expect(list.items.len == 1);
+ }
+ {
+ var list = ArrayListUnmanaged(i32){};
+
+ try list.append(a, 1);
+ try list.append(a, 2);
+ try list.append(a, 3);
+
+ list.shrink(a, 1);
+ testing.expect(list.items.len == 1);
+ }
}
test "std.ArrayList.writer" {
@@ -864,7 +1104,7 @@ test "std.ArrayList.writer" {
testing.expectEqualSlices(u8, list.items, "abcdefg");
}
-test "addManyAsArray" {
+test "std.ArrayList/ArrayListUnmanaged.addManyAsArray" {
const a = std.testing.allocator;
{
var list = ArrayList(u8).init(a);
diff --git a/lib/std/build.zig b/lib/std/build.zig
@@ -1232,6 +1232,9 @@ pub const LibExeObjStep = struct {
installed_path: ?[]const u8,
install_step: ?*InstallArtifactStep,
+ /// Base address for an executable image.
+ image_base: ?u64 = null,
+
libc_file: ?[]const u8 = null,
valgrind_support: ?bool = null,
@@ -1239,6 +1242,7 @@ pub const LibExeObjStep = struct {
/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
/// file.
link_eh_frame_hdr: bool = false,
+ link_emit_relocs: bool = false,
/// Place every function in its own section so that unused ones may be
/// safely garbage-collected during the linking phase.
@@ -1384,66 +1388,50 @@ pub const LibExeObjStep = struct {
}
fn computeOutFileNames(self: *LibExeObjStep) void {
- // TODO make this call std.zig.binNameAlloc
- switch (self.kind) {
- .Obj => {
- self.out_filename = self.builder.fmt("{}{}", .{ self.name, self.target.oFileExt() });
- },
- .Exe => {
- self.out_filename = self.builder.fmt("{}{}", .{ self.name, self.target.exeFileExt() });
- },
- .Test => {
- self.out_filename = self.builder.fmt("test{}", .{self.target.exeFileExt()});
+ const target_info = std.zig.system.NativeTargetInfo.detect(
+ self.builder.allocator,
+ self.target,
+ ) catch unreachable;
+ const target = target_info.target;
+ self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
+ .root_name = self.name,
+ .target = target,
+ .output_mode = switch (self.kind) {
+ .Lib => .Lib,
+ .Obj => .Obj,
+ .Exe, .Test => .Exe,
},
- .Lib => {
- if (!self.is_dynamic) {
- self.out_filename = self.builder.fmt("{}{}{}", .{
- self.target.libPrefix(),
+ .link_mode = if (self.is_dynamic) .Dynamic else .Static,
+ .version = self.version,
+ }) catch unreachable;
+
+ if (self.kind == .Lib) {
+ if (!self.is_dynamic) {
+ self.out_lib_filename = self.out_filename;
+ } else if (self.version) |version| {
+ if (target.isDarwin()) {
+ self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", .{
self.name,
- self.target.staticLibSuffix(),
+ version.major,
});
+ self.name_only_filename = self.builder.fmt("lib{}.dylib", .{self.name});
self.out_lib_filename = self.out_filename;
- } else if (self.version) |version| {
- if (self.target.isDarwin()) {
- self.out_filename = self.builder.fmt("lib{}.{d}.{d}.{d}.dylib", .{
- self.name,
- version.major,
- version.minor,
- version.patch,
- });
- self.major_only_filename = self.builder.fmt("lib{}.{d}.dylib", .{
- self.name,
- version.major,
- });
- self.name_only_filename = self.builder.fmt("lib{}.dylib", .{self.name});
- self.out_lib_filename = self.out_filename;
- } else if (self.target.isWindows()) {
- self.out_filename = self.builder.fmt("{}.dll", .{self.name});
- self.out_lib_filename = self.builder.fmt("{}.lib", .{self.name});
- } else {
- self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", .{
- self.name,
- version.major,
- version.minor,
- version.patch,
- });
- self.major_only_filename = self.builder.fmt("lib{}.so.{d}", .{ self.name, version.major });
- self.name_only_filename = self.builder.fmt("lib{}.so", .{self.name});
- self.out_lib_filename = self.out_filename;
- }
+ } else if (target.os.tag == .windows) {
+ self.out_lib_filename = self.builder.fmt("{}.lib", .{self.name});
} else {
- if (self.target.isDarwin()) {
- self.out_filename = self.builder.fmt("lib{}.dylib", .{self.name});
- self.out_lib_filename = self.out_filename;
- } else if (self.target.isWindows()) {
- self.out_filename = self.builder.fmt("{}.dll", .{self.name});
- self.out_lib_filename = self.builder.fmt("{}.lib", .{self.name});
- } else {
- self.out_filename = self.builder.fmt("lib{}.so", .{self.name});
- self.out_lib_filename = self.out_filename;
- }
+ self.major_only_filename = self.builder.fmt("lib{}.so.{d}", .{ self.name, version.major });
+ self.name_only_filename = self.builder.fmt("lib{}.so", .{self.name});
+ self.out_lib_filename = self.out_filename;
}
- },
+ } else {
+ if (target.isDarwin()) {
+ self.out_lib_filename = self.out_filename;
+ } else if (target.os.tag == .windows) {
+ self.out_lib_filename = self.builder.fmt("{}.lib", .{self.name});
+ } else {
+ self.out_lib_filename = self.out_filename;
+ }
+ }
}
}
@@ -2040,6 +2028,11 @@ pub const LibExeObjStep = struct {
try zig_args.append("--pkg-end");
}
+ if (self.image_base) |image_base| {
+ try zig_args.append("--image-base");
+ try zig_args.append(builder.fmt("0x{x}", .{image_base}));
+ }
+
if (self.filter) |filter| {
try zig_args.append("--test-filter");
try zig_args.append(filter);
@@ -2075,6 +2068,9 @@ pub const LibExeObjStep = struct {
if (self.link_eh_frame_hdr) {
try zig_args.append("--eh-frame-hdr");
}
+ if (self.link_emit_relocs) {
+ try zig_args.append("--emit-relocs");
+ }
if (self.link_function_sections) {
try zig_args.append("-ffunction-sections");
}
@@ -2168,8 +2164,8 @@ pub const LibExeObjStep = struct {
}
if (self.linker_script) |linker_script| {
- zig_args.append("--linker-script") catch unreachable;
- zig_args.append(builder.pathFromRoot(linker_script)) catch unreachable;
+ try zig_args.append("--script");
+ try zig_args.append(builder.pathFromRoot(linker_script));
}
if (self.version_script) |version_script| {
@@ -2335,6 +2331,14 @@ pub const LibExeObjStep = struct {
var it = src_dir.iterate();
while (try it.next()) |entry| {
+ // The compiler can put these files into the same directory, but we don't
+ // want to copy them over.
+ if (mem.eql(u8, entry.name, "stage1.id") or
+ mem.eql(u8, entry.name, "llvm-ar.id") or
+ mem.eql(u8, entry.name, "libs.txt") or
+ mem.eql(u8, entry.name, "builtin.zig") or
+ mem.eql(u8, entry.name, "lld.id")) continue;
+
_ = try src_dir.updateFile(entry.name, dest_dir, entry.name, .{});
}
} else {
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
@@ -100,6 +100,16 @@ pub const AtomicOrder = enum {
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
+pub const ReduceOp = enum {
+ And,
+ Or,
+ Xor,
+ Min,
+ Max,
+};
+
+/// This data structure is used by the Zig language code generation and
+/// therefore must be kept in sync with the compiler implementation.
pub const AtomicRmwOp = enum {
Xchg,
Add,
@@ -262,6 +272,7 @@ pub const TypeInfo = union(enum) {
field_type: type,
default_value: anytype,
is_comptime: bool,
+ alignment: comptime_int,
};
/// This data structure is used by the Zig language code generation and
@@ -318,6 +329,7 @@ pub const TypeInfo = union(enum) {
pub const UnionField = struct {
name: []const u8,
field_type: type,
+ alignment: comptime_int,
};
/// This data structure is used by the Zig language code generation and
@@ -341,6 +353,7 @@ pub const TypeInfo = union(enum) {
/// therefore must be kept in sync with the compiler implementation.
pub const Fn = struct {
calling_convention: CallingConvention,
+ alignment: comptime_int,
is_generic: bool,
is_var_args: bool,
return_type: ?type,
diff --git a/lib/std/c.zig b/lib/std/c.zig
@@ -342,3 +342,6 @@ pub extern "c" fn fsync(fd: c_int) c_int;
pub extern "c" fn fdatasync(fd: c_int) c_int;
pub extern "c" fn prctl(option: c_int, ...) c_int;
+
+pub extern "c" fn getrlimit(resource: rlimit_resource, rlim: *rlimit) c_int;
+pub extern "c" fn setrlimit(resource: rlimit_resource, rlim: *const rlimit) c_int;
diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig
@@ -12,7 +12,7 @@ usingnamespace @import("../os/bits.zig");
extern "c" fn __error() *c_int;
pub extern "c" fn NSVersionOfRunTimeLibrary(library_name: [*:0]const u8) u32;
-pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int;
+pub extern "c" fn _NSGetExecutablePath(buf: [*:0]u8, bufsize: *u32) c_int;
pub extern "c" fn _dyld_image_count() u32;
pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header;
pub extern "c" fn _dyld_get_image_vmaddr_slide(image_index: u32) usize;
diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig
@@ -100,6 +100,8 @@ pub extern "c" fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_
pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: c_uint) c_int;
+pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *const rlimit, old_limit: *rlimit) c_int;
+
pub const pthread_attr_t = extern struct {
__size: [56]u8,
__align: c_long,
diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig
@@ -105,8 +105,8 @@ pub const ChildProcess = struct {
.term = null,
.env_map = null,
.cwd = null,
- .uid = if (builtin.os.tag == .windows) {} else null,
- .gid = if (builtin.os.tag == .windows) {} else null,
+ .uid = if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {} else null,
+ .gid = if (builtin.os.tag == .windows or builtin.os.tag == .wasi) {} else null,
.stdin = null,
.stdout = null,
.stderr = null,
diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig
@@ -4,6 +4,50 @@
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
+/// Authenticated Encryption with Associated Data
+pub const aead = struct {
+ const chacha20 = @import("crypto/chacha20.zig");
+
+ pub const Gimli = @import("crypto/gimli.zig").Aead;
+ pub const ChaCha20Poly1305 = chacha20.Chacha20Poly1305;
+ pub const XChaCha20Poly1305 = chacha20.XChacha20Poly1305;
+ pub const AEGIS128L = @import("crypto/aegis.zig").AEGIS128L;
+ pub const AEGIS256 = @import("crypto/aegis.zig").AEGIS256;
+ pub const AES128GCM = @import("crypto/aes_gcm.zig").AES128GCM;
+ pub const AES256GCM = @import("crypto/aes_gcm.zig").AES256GCM;
+};
+
+/// Authentication (MAC) functions.
+pub const auth = struct {
+ pub const hmac = @import("crypto/hmac.zig");
+ pub const siphash = @import("crypto/siphash.zig");
+};
+
+/// Core functions, that should rarely be used directly by applications.
+pub const core = struct {
+ pub const aes = @import("crypto/aes.zig");
+ pub const Gimli = @import("crypto/gimli.zig").State;
+
+ /// Modes are generic compositions to construct encryption/decryption functions from block ciphers and permutations.
+ ///
+ /// 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.
+ ///
+ /// Most applications may want to use AEADs instead.
+ pub const modes = @import("crypto/modes.zig");
+};
+
+/// Diffie-Hellman key exchange functions.
+pub const dh = struct {
+ pub const X25519 = @import("crypto/25519/x25519.zig").X25519;
+};
+
+/// Elliptic-curve arithmetic.
+pub const ecc = struct {
+ pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519;
+ pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519;
+ pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255;
+};
+
/// Hash functions.
pub const hash = struct {
pub const Md5 = @import("crypto/md5.zig").Md5;
@@ -15,26 +59,15 @@ pub const hash = struct {
pub const Gimli = @import("crypto/gimli.zig").Hash;
};
-/// Authentication (MAC) functions.
-pub const auth = struct {
- pub const hmac = @import("crypto/hmac.zig");
- pub const siphash = @import("crypto/siphash.zig");
-};
-
-/// Authenticated Encryption with Associated Data
-pub const aead = struct {
- const chacha20 = @import("crypto/chacha20.zig");
-
- pub const Gimli = @import("crypto/gimli.zig").Aead;
- pub const ChaCha20Poly1305 = chacha20.Chacha20Poly1305;
- pub const XChaCha20Poly1305 = chacha20.XChacha20Poly1305;
- pub const AEGIS128L = @import("crypto/aegis.zig").AEGIS128L;
- pub const AEGIS256 = @import("crypto/aegis.zig").AEGIS256;
+/// Key derivation functions.
+pub const kdf = struct {
+ pub const hkdf = @import("crypto/hkdf.zig");
};
/// MAC functions requiring single-use secret keys.
pub const onetimeauth = struct {
pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;
+ pub const Ghash = @import("crypto/ghash.zig").Ghash;
};
/// A password hashing function derives a uniform key from low-entropy input material such as passwords.
@@ -57,31 +90,6 @@ pub const pwhash = struct {
pub const pbkdf2 = @import("crypto/pbkdf2.zig").pbkdf2;
};
-/// Core functions, that should rarely be used directly by applications.
-pub const core = struct {
- pub const aes = @import("crypto/aes.zig");
- pub const Gimli = @import("crypto/gimli.zig").State;
-
- /// Modes are generic compositions to construct encryption/decryption functions from block ciphers and permutations.
- ///
- /// 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.
- ///
- /// Most applications may want to use AEADs instead.
- pub const modes = @import("crypto/modes.zig");
-};
-
-/// Elliptic-curve arithmetic.
-pub const ecc = struct {
- pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519;
- pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519;
- pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255;
-};
-
-/// Diffie-Hellman key exchange functions.
-pub const dh = struct {
- pub const X25519 = @import("crypto/25519/x25519.zig").X25519;
-};
-
/// Digital signature functions.
pub const sign = struct {
pub const Ed25519 = @import("crypto/25519/ed25519.zig").Ed25519;
diff --git a/lib/std/crypto/25519/field.zig b/lib/std/crypto/25519/field.zig
@@ -307,12 +307,14 @@ pub const Fe = struct {
}
pub fn pow2523(a: Fe) Fe {
- var c = a;
- var i: usize = 0;
- while (i < 249) : (i += 1) {
- c = c.sq().mul(a);
- }
- return c.sq().sq().mul(a);
+ var t0 = a.mul(a.sq());
+ var t1 = t0.mul(t0.sqn(2)).sq().mul(a);
+ t0 = t1.sqn(5).mul(t1);
+ var t2 = t0.sqn(5).mul(t1);
+ t1 = t2.sqn(15).mul(t2);
+ t2 = t1.sqn(30).mul(t1);
+ t1 = t2.sqn(60).mul(t2);
+ return t1.sqn(120).mul(t1).sqn(10).mul(t0).sqn(2).mul(a);
}
pub fn abs(a: Fe) Fe {
diff --git a/lib/std/crypto/aes_gcm.zig b/lib/std/crypto/aes_gcm.zig
@@ -0,0 +1,161 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const builtin = std.builtin;
+const crypto = std.crypto;
+const debug = std.debug;
+const Ghash = std.crypto.onetimeauth.Ghash;
+const mem = std.mem;
+const modes = crypto.core.modes;
+
+pub const AES128GCM = AESGCM(crypto.core.aes.AES128);
+pub const AES256GCM = AESGCM(crypto.core.aes.AES256);
+
+fn AESGCM(comptime AES: anytype) type {
+ debug.assert(AES.block.block_size == 16);
+
+ return struct {
+ pub const tag_length = 16;
+ pub const nonce_length = 12;
+ pub const key_length = AES.key_bits / 8;
+
+ const zeros = [_]u8{0} ** 16;
+
+ pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
+ debug.assert(c.len == m.len);
+ debug.assert(m.len <= 16 * ((1 << 32) - 2));
+
+ const aes = AES.initEnc(key);
+ var h: [16]u8 = undefined;
+ aes.encrypt(&h, &zeros);
+
+ var t: [16]u8 = undefined;
+ var j: [16]u8 = undefined;
+ mem.copy(u8, j[0..nonce_length], npub[0..]);
+ mem.writeIntBig(u32, j[nonce_length..][0..4], 1);
+ aes.encrypt(&t, &j);
+
+ var mac = Ghash.init(&h);
+ mac.update(ad);
+ mac.pad();
+
+ mem.writeIntBig(u32, j[nonce_length..][0..4], 2);
+ modes.ctr(@TypeOf(aes), aes, c, m, j, builtin.Endian.Big);
+ mac.update(c[0..m.len][0..]);
+ mac.pad();
+
+ var final_block = h;
+ mem.writeIntBig(u64, final_block[0..8], ad.len * 8);
+ mem.writeIntBig(u64, final_block[8..16], m.len * 8);
+ mac.update(&final_block);
+ mac.final(tag);
+ for (t) |x, i| {
+ tag[i] ^= x;
+ }
+ }
+
+ pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) !void {
+ assert(c.len == m.len);
+
+ const aes = AES.initEnc(key);
+ var h: [16]u8 = undefined;
+ aes.encrypt(&h, &zeros);
+
+ var t: [16]u8 = undefined;
+ var j: [16]u8 = undefined;
+ mem.copy(u8, j[0..nonce_length], npub[0..]);
+ mem.writeIntBig(u32, j[nonce_length..][0..4], 1);
+ aes.encrypt(&t, &j);
+
+ var mac = Ghash.init(&h);
+ mac.update(ad);
+ mac.pad();
+
+ mac.update(c);
+ mac.pad();
+
+ var final_block = h;
+ mem.writeIntBig(u64, final_block[0..8], ad.len * 8);
+ mem.writeIntBig(u64, final_block[8..16], m.len * 8);
+ mac.update(&final_block);
+ var computed_tag: [Ghash.mac_length]u8 = undefined;
+ mac.final(&computed_tag);
+ for (t) |x, i| {
+ computed_tag[i] ^= x;
+ }
+
+ var acc: u8 = 0;
+ for (computed_tag) |_, p| {
+ acc |= (computed_tag[p] ^ tag[p]);
+ }
+ if (acc != 0) {
+ mem.set(u8, m, 0xaa);
+ return error.AuthenticationFailed;
+ }
+
+ mem.writeIntBig(u32, j[nonce_length..][0..4], 2);
+ modes.ctr(@TypeOf(aes), aes, m, c, j, builtin.Endian.Big);
+ }
+ };
+}
+
+const htest = @import("test.zig");
+const testing = std.testing;
+
+test "AES256GCM - Empty message and no associated data" {
+ const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length;
+ const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length;
+ const ad = "";
+ const m = "";
+ var c: [m.len]u8 = undefined;
+ var m2: [m.len]u8 = undefined;
+ var tag: [AES256GCM.tag_length]u8 = undefined;
+
+ AES256GCM.encrypt(&c, &tag, m, ad, nonce, key);
+ htest.assertEqual("6b6ff610a16fa4cd59f1fb7903154e92", &tag);
+}
+
+test "AES256GCM - Associated data only" {
+ const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length;
+ const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length;
+ const m = "";
+ const ad = "Test with associated data";
+ var c: [m.len]u8 = undefined;
+ var tag: [AES256GCM.tag_length]u8 = undefined;
+
+ AES256GCM.encrypt(&c, &tag, m, ad, nonce, key);
+ htest.assertEqual("262ed164c2dfb26e080a9d108dd9dd4c", &tag);
+}
+
+test "AES256GCM - Message only" {
+ const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length;
+ const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length;
+ const m = "Test with message only";
+ const ad = "";
+ var c: [m.len]u8 = undefined;
+ var m2: [m.len]u8 = undefined;
+ var tag: [AES256GCM.tag_length]u8 = undefined;
+
+ AES256GCM.encrypt(&c, &tag, m, ad, nonce, key);
+ try AES256GCM.decrypt(&m2, &c, tag, ad, nonce, key);
+ testing.expectEqualSlices(u8, m[0..], m2[0..]);
+
+ htest.assertEqual("5ca1642d90009fea33d01f78cf6eefaf01d539472f7c", &c);
+ htest.assertEqual("07cd7fc9103e2f9e9bf2dfaa319caff4", &tag);
+}
+
+test "AES256GCM - Message and associated data" {
+ const key: [AES256GCM.key_length]u8 = [_]u8{0x69} ** AES256GCM.key_length;
+ const nonce: [AES256GCM.nonce_length]u8 = [_]u8{0x42} ** AES256GCM.nonce_length;
+ const m = "Test with message";
+ const ad = "Test with associated data";
+ var c: [m.len]u8 = undefined;
+ var m2: [m.len]u8 = undefined;
+ var tag: [AES256GCM.tag_length]u8 = undefined;
+
+ AES256GCM.encrypt(&c, &tag, m, ad, nonce, key);
+ try AES256GCM.decrypt(&m2, &c, tag, ad, nonce, key);
+ testing.expectEqualSlices(u8, m[0..], m2[0..]);
+
+ htest.assertEqual("5ca1642d90009fea33d01f78cf6eefaf01", &c);
+ htest.assertEqual("64accec679d444e2373bd9f6796c0d2c", &tag);
+}
diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig
@@ -57,6 +57,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64
}
const macs = [_]Crypto{
+ Crypto{ .ty = crypto.onetimeauth.Ghash, .name = "ghash" },
Crypto{ .ty = crypto.onetimeauth.Poly1305, .name = "poly1305" },
Crypto{ .ty = crypto.auth.hmac.HmacMd5, .name = "hmac-md5" },
Crypto{ .ty = crypto.auth.hmac.HmacSha1, .name = "hmac-sha1" },
@@ -151,6 +152,8 @@ const aeads = [_]Crypto{
Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
Crypto{ .ty = crypto.aead.AEGIS128L, .name = "aegis-128l" },
Crypto{ .ty = crypto.aead.AEGIS256, .name = "aegis-256" },
+ Crypto{ .ty = crypto.aead.AES128GCM, .name = "aes128-gcm" },
+ Crypto{ .ty = crypto.aead.AES256GCM, .name = "aes256-gcm" },
};
pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 {
diff --git a/lib/std/crypto/ghash.zig b/lib/std/crypto/ghash.zig
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+//
+// Adapted from BearSSL's ctmul64 implementation originally written by Thomas Pornin <pornin@bolet.org>
+
+const std = @import("../std.zig");
+const assert = std.debug.assert;
+const math = std.math;
+const mem = std.mem;
+
+/// GHASH is a universal hash function that features multiplication
+/// by a fixed parameter within a Galois field.
+///
+/// It is not a general purpose hash function - The key must be secret, unpredictable and never reused.
+///
+/// GHASH is typically used to compute the authentication tag in the AES-GCM construction.
+pub const Ghash = struct {
+ pub const block_size: usize = 16;
+ pub const mac_length = 16;
+ pub const minimum_key_length = 16;
+
+ y0: u64 = 0,
+ y1: u64 = 0,
+ h0: u64,
+ h1: u64,
+ h2: u64,
+ h0r: u64,
+ h1r: u64,
+ h2r: u64,
+
+ hh0: u64 = undefined,
+ hh1: u64 = undefined,
+ hh2: u64 = undefined,
+ hh0r: u64 = undefined,
+ hh1r: u64 = undefined,
+ hh2r: u64 = undefined,
+
+ leftover: usize = 0,
+ buf: [block_size]u8 align(16) = undefined,
+
+ pub fn init(key: *const [minimum_key_length]u8) Ghash {
+ const h1 = mem.readIntBig(u64, key[0..8]);
+ const h0 = mem.readIntBig(u64, key[8..16]);
+ const h1r = @bitReverse(u64, h1);
+ const h0r = @bitReverse(u64, h0);
+ const h2 = h0 ^ h1;
+ const h2r = h0r ^ h1r;
+
+ if (std.builtin.mode == .ReleaseSmall) {
+ return Ghash{
+ .h0 = h0,
+ .h1 = h1,
+ .h2 = h2,
+ .h0r = h0r,
+ .h1r = h1r,
+ .h2r = h2r,
+ };
+ } else {
+ // Precompute H^2
+ var hh = Ghash{
+ .h0 = h0,
+ .h1 = h1,
+ .h2 = h2,
+ .h0r = h0r,
+ .h1r = h1r,
+ .h2r = h2r,
+ };
+ hh.update(key);
+ const hh1 = hh.y1;
+ const hh0 = hh.y0;
+ const hh1r = @bitReverse(u64, hh1);
+ const hh0r = @bitReverse(u64, hh0);
+ const hh2 = hh0 ^ hh1;
+ const hh2r = hh0r ^ hh1r;
+
+ return Ghash{
+ .h0 = h0,
+ .h1 = h1,
+ .h2 = h2,
+ .h0r = h0r,
+ .h1r = h1r,
+ .h2r = h2r,
+
+ .hh0 = hh0,
+ .hh1 = hh1,
+ .hh2 = hh2,
+ .hh0r = hh0r,
+ .hh1r = hh1r,
+ .hh2r = hh2r,
+ };
+ }
+ }
+
+ inline fn clmul_pclmul(x: u64, y: u64) u64 {
+ const Vector = std.meta.Vector;
+ const product = asm (
+ \\ vpclmulqdq $0x00, %[x], %[y], %[out]
+ : [out] "=x" (-> Vector(2, u64))
+ : [x] "x" (@bitCast(Vector(2, u64), @as(u128, x))),
+ [y] "x" (@bitCast(Vector(2, u64), @as(u128, y)))
+ );
+ return product[0];
+ }
+
+ fn clmul_soft(x: u64, y: u64) u64 {
+ const x0 = x & 0x1111111111111111;
+ const x1 = x & 0x2222222222222222;
+ const x2 = x & 0x4444444444444444;
+ const x3 = x & 0x8888888888888888;
+ const y0 = y & 0x1111111111111111;
+ const y1 = y & 0x2222222222222222;
+ const y2 = y & 0x4444444444444444;
+ const y3 = y & 0x8888888888888888;
+ var z0 = (x0 *% y0) ^ (x1 *% y3) ^ (x2 *% y2) ^ (x3 *% y1);
+ var z1 = (x0 *% y1) ^ (x1 *% y0) ^ (x2 *% y3) ^ (x3 *% y2);
+ var z2 = (x0 *% y2) ^ (x1 *% y1) ^ (x2 *% y0) ^ (x3 *% y3);
+ var z3 = (x0 *% y3) ^ (x1 *% y2) ^ (x2 *% y1) ^ (x3 *% y0);
+ z0 &= 0x1111111111111111;
+ z1 &= 0x2222222222222222;
+ z2 &= 0x4444444444444444;
+ z3 &= 0x8888888888888888;
+ return z0 | z1 | z2 | z3;
+ }
+
+ const has_pclmul = comptime std.Target.x86.featureSetHas(std.Target.current.cpu.features, .pclmul);
+ const has_avx = comptime std.Target.x86.featureSetHas(std.Target.current.cpu.features, .avx);
+ const clmul = if (std.Target.current.cpu.arch == .x86_64 and has_pclmul and has_avx) clmul_pclmul else clmul_soft;
+
+ fn blocks(st: *Ghash, msg: []const u8) void {
+ assert(msg.len % 16 == 0); // GHASH blocks() expects full blocks
+ var y1 = st.y1;
+ var y0 = st.y0;
+
+ var i: usize = 0;
+
+ // 2-blocks aggregated reduction
+ if (std.builtin.mode != .ReleaseSmall) {
+ while (i + 32 <= msg.len) : (i += 32) {
+ // B0 * H^2 unreduced
+ y1 ^= mem.readIntBig(u64, msg[i..][0..8]);
+ y0 ^= mem.readIntBig(u64, msg[i..][8..16]);
+
+ const y1r = @bitReverse(u64, y1);
+ const y0r = @bitReverse(u64, y0);
+ const y2 = y0 ^ y1;
+ const y2r = y0r ^ y1r;
+
+ var z0 = clmul(y0, st.hh0);
+ var z1 = clmul(y1, st.hh1);
+ var z2 = clmul(y2, st.hh2) ^ z0 ^ z1;
+ var z0h = clmul(y0r, st.hh0r);
+ var z1h = clmul(y1r, st.hh1r);
+ var z2h = clmul(y2r, st.hh2r) ^ z0h ^ z1h;
+
+ // B1 * H unreduced
+ const sy1 = mem.readIntBig(u64, msg[i..][16..24]);
+ const sy0 = mem.readIntBig(u64, msg[i..][24..32]);
+
+ const sy1r = @bitReverse(u64, sy1);
+ const sy0r = @bitReverse(u64, sy0);
+ const sy2 = sy0 ^ sy1;
+ const sy2r = sy0r ^ sy1r;
+
+ const sz0 = clmul(sy0, st.h0);
+ const sz1 = clmul(sy1, st.h1);
+ const sz2 = clmul(sy2, st.h2) ^ sz0 ^ sz1;
+ const sz0h = clmul(sy0r, st.h0r);
+ const sz1h = clmul(sy1r, st.h1r);
+ const sz2h = clmul(sy2r, st.h2r) ^ sz0h ^ sz1h;
+
+ // ((B0 * H^2) + B1 * H) (mod M)
+ z0 ^= sz0;
+ z1 ^= sz1;
+ z2 ^= sz2;
+ z0h ^= sz0h;
+ z1h ^= sz1h;
+ z2h ^= sz2h;
+ z0h = @bitReverse(u64, z0h) >> 1;
+ z1h = @bitReverse(u64, z1h) >> 1;
+ z2h = @bitReverse(u64, z2h) >> 1;
+
+ var v3 = z1h;
+ var v2 = z1 ^ z2h;
+ var v1 = z0h ^ z2;
+ var v0 = z0;
+
+ v3 = (v3 << 1) | (v2 >> 63);
+ v2 = (v2 << 1) | (v1 >> 63);
+ v1 = (v1 << 1) | (v0 >> 63);
+ v0 = (v0 << 1);
+
+ v2 ^= v0 ^ (v0 >> 1) ^ (v0 >> 2) ^ (v0 >> 7);
+ v1 ^= (v0 << 63) ^ (v0 << 62) ^ (v0 << 57);
+ y1 = v3 ^ v1 ^ (v1 >> 1) ^ (v1 >> 2) ^ (v1 >> 7);
+ y0 = v2 ^ (v1 << 63) ^ (v1 << 62) ^ (v1 << 57);
+ }
+ }
+
+ // single block
+ while (i + 16 <= msg.len) : (i += 16) {
+ y1 ^= mem.readIntBig(u64, msg[i..][0..8]);
+ y0 ^= mem.readIntBig(u64, msg[i..][8..16]);
+
+ const y1r = @bitReverse(u64, y1);
+ const y0r = @bitReverse(u64, y0);
+ const y2 = y0 ^ y1;
+ const y2r = y0r ^ y1r;
+
+ const z0 = clmul(y0, st.h0);
+ const z1 = clmul(y1, st.h1);
+ var z2 = clmul(y2, st.h2) ^ z0 ^ z1;
+ var z0h = clmul(y0r, st.h0r);
+ var z1h = clmul(y1r, st.h1r);
+ var z2h = clmul(y2r, st.h2r) ^ z0h ^ z1h;
+ z0h = @bitReverse(u64, z0h) >> 1;
+ z1h = @bitReverse(u64, z1h) >> 1;
+ z2h = @bitReverse(u64, z2h) >> 1;
+
+ // shift & reduce
+ var v3 = z1h;
+ var v2 = z1 ^ z2h;
+ var v1 = z0h ^ z2;
+ var v0 = z0;
+
+ v3 = (v3 << 1) | (v2 >> 63);
+ v2 = (v2 << 1) | (v1 >> 63);
+ v1 = (v1 << 1) | (v0 >> 63);
+ v0 = (v0 << 1);
+
+ v2 ^= v0 ^ (v0 >> 1) ^ (v0 >> 2) ^ (v0 >> 7);
+ v1 ^= (v0 << 63) ^ (v0 << 62) ^ (v0 << 57);
+ y1 = v3 ^ v1 ^ (v1 >> 1) ^ (v1 >> 2) ^ (v1 >> 7);
+ y0 = v2 ^ (v1 << 63) ^ (v1 << 62) ^ (v1 << 57);
+ }
+ st.y1 = y1;
+ st.y0 = y0;
+ }
+
+ pub fn update(st: *Ghash, m: []const u8) void {
+ var mb = m;
+
+ if (st.leftover > 0) {
+ const want = math.min(block_size - st.leftover, mb.len);
+ const mc = mb[0..want];
+ for (mc) |x, i| {
+ st.buf[st.leftover + i] = x;
+ }
+ mb = mb[want..];
+ st.leftover += want;
+ if (st.leftover < block_size) {
+ return;
+ }
+ st.blocks(&st.buf);
+ st.leftover = 0;
+ }
+ if (mb.len >= block_size) {
+ const want = mb.len & ~(block_size - 1);
+ st.blocks(mb[0..want]);
+ mb = mb[want..];
+ }
+ if (mb.len > 0) {
+ for (mb) |x, i| {
+ st.buf[st.leftover + i] = x;
+ }
+ st.leftover += mb.len;
+ }
+ }
+
+ /// Zero-pad to align the next input to the first byte of a block
+ pub fn pad(st: *Ghash) void {
+ if (st.leftover == 0) {
+ return;
+ }
+ var i = st.leftover;
+ while (i < block_size) : (i += 1) {
+ st.buf[i] = 0;
+ }
+ st.blocks(&st.buf);
+ st.leftover = 0;
+ }
+
+ pub fn final(st: *Ghash, out: *[mac_length]u8) void {
+ st.pad();
+ mem.writeIntBig(u64, out[0..8], st.y1);
+ mem.writeIntBig(u64, out[8..16], st.y0);
+
+ mem.secureZero(u8, @ptrCast([*]u8, st)[0..@sizeOf(Ghash)]);
+ }
+
+ pub fn create(out: *[mac_length]u8, msg: []const u8, key: *const [minimum_key_length]u8) void {
+ var st = Ghash.init(key);
+ st.update(msg);
+ st.final(out);
+ }
+};
+
+const htest = @import("test.zig");
+
+test "ghash" {
+ const key = [_]u8{0x42} ** 16;
+ const m = [_]u8{0x69} ** 256;
+
+ var st = Ghash.init(&key);
+ st.update(&m);
+ var out: [16]u8 = undefined;
+ st.final(&out);
+ htest.assertEqual("889295fa746e8b174bf4ec80a65dea41", &out);
+
+ st = Ghash.init(&key);
+ st.update(m[0..100]);
+ st.update(m[100..]);
+ st.final(&out);
+ htest.assertEqual("889295fa746e8b174bf4ec80a65dea41", &out);
+}
diff --git a/lib/std/crypto/hkdf.zig b/lib/std/crypto/hkdf.zig
@@ -0,0 +1,66 @@
+const std = @import("../std.zig");
+const assert = std.debug.assert;
+const hmac = std.crypto.auth.hmac;
+const mem = std.mem;
+
+/// HKDF-SHA256
+pub const HkdfSha256 = Hkdf(hmac.sha2.HmacSha256);
+
+/// HKDF-SHA512
+pub const HkdfSha512 = Hkdf(hmac.sha2.HmacSha512);
+
+/// The Hkdf construction takes some source of initial keying material and
+/// derives one or more uniform keys from it.
+pub fn Hkdf(comptime Hmac: type) type {
+ return struct {
+ /// Return a master key from a salt and initial keying material.
+ fn extract(salt: []const u8, ikm: []const u8) [Hmac.mac_length]u8 {
+ var prk: [Hmac.mac_length]u8 = undefined;
+ Hmac.create(&prk, ikm, salt);
+ return prk;
+ }
+
+ /// Derive a subkey from a master key `prk` and a subkey description `ctx`.
+ fn expand(out: []u8, ctx: []const u8, prk: [Hmac.mac_length]u8) void {
+ assert(out.len < Hmac.mac_length * 255); // output size is too large for the Hkdf construction
+ var i: usize = 0;
+ var counter = [1]u8{1};
+ while (i + Hmac.mac_length <= out.len) : (i += Hmac.mac_length) {
+ var st = Hmac.init(&prk);
+ if (i != 0) {
+ st.update(out[i - Hmac.mac_length ..][0..Hmac.mac_length]);
+ }
+ st.update(ctx);
+ st.update(&counter);
+ st.final(out[i..][0..Hmac.mac_length]);
+ counter[0] += 1;
+ }
+ const left = out.len % Hmac.mac_length;
+ if (left > 0) {
+ var st = Hmac.init(&prk);
+ if (i != 0) {
+ st.update(out[i - Hmac.mac_length ..][0..Hmac.mac_length]);
+ }
+ st.update(ctx);
+ st.update(&counter);
+ var tmp: [Hmac.mac_length]u8 = undefined;
+ st.final(tmp[0..Hmac.mac_length]);
+ mem.copy(u8, out[i..][0..left], tmp[0..left]);
+ }
+ }
+ };
+}
+
+const htest = @import("test.zig");
+
+test "Hkdf" {
+ const ikm = [_]u8{0x0b} ** 22;
+ const salt = [_]u8{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c };
+ const context = [_]u8{ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 };
+ const kdf = HkdfSha256;
+ const prk = kdf.extract(&salt, &ikm);
+ htest.assertEqual("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5", &prk);
+ var out: [42]u8 = undefined;
+ kdf.expand(&out, &context, prk);
+ htest.assertEqual("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865", &out);
+}
diff --git a/lib/std/crypto/poly1305.zig b/lib/std/crypto/poly1305.zig
@@ -22,8 +22,7 @@ pub const Poly1305 = struct {
// partial block buffer
buf: [block_size]u8 align(16) = undefined,
- pub fn init(key: []const u8) Poly1305 {
- std.debug.assert(key.len >= minimum_key_length);
+ pub fn init(key: *const [minimum_key_length]u8) Poly1305 {
const t0 = mem.readIntLittle(u64, key[0..8]);
const t1 = mem.readIntLittle(u64, key[8..16]);
return Poly1305{
@@ -92,7 +91,7 @@ pub const Poly1305 = struct {
}
mb = mb[want..];
st.leftover += want;
- if (st.leftover > block_size) {
+ if (st.leftover < block_size) {
return;
}
st.blocks(&st.buf, false);
@@ -115,8 +114,20 @@ pub const Poly1305 = struct {
}
}
- pub fn final(st: *Poly1305, out: []u8) void {
- std.debug.assert(out.len >= mac_length);
+ /// Zero-pad to align the next input to the first byte of a block
+ pub fn pad(st: *Poly1305) void {
+ if (st.leftover == 0) {
+ return;
+ }
+ var i = st.leftover;
+ while (i < block_size) : (i += 1) {
+ st.buf[i] = 0;
+ }
+ st.blocks(&st.buf);
+ st.leftover = 0;
+ }
+
+ pub fn final(st: *Poly1305, out: *[mac_length]u8) void {
if (st.leftover > 0) {
var i = st.leftover;
st.buf[i] = 1;
@@ -187,10 +198,7 @@ pub const Poly1305 = struct {
std.mem.secureZero(u8, @ptrCast([*]u8, st)[0..@sizeOf(Poly1305)]);
}
- pub fn create(out: []u8, msg: []const u8, key: []const u8) void {
- std.debug.assert(out.len >= mac_length);
- std.debug.assert(key.len >= minimum_key_length);
-
+ pub fn create(out: *[mac_length]u8, msg: []const u8, key: *const [minimum_key_length]u8) void {
var st = Poly1305.init(key);
st.update(msg);
st.final(out);
diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig
@@ -647,6 +647,31 @@ pub const Loop = struct {
}
}
+ /// Runs the provided function asynchronously. The function's frame is allocated
+ /// with `allocator` and freed when the function returns.
+ /// `func` must return void and it can be an async function.
+ /// Yields to the event loop, running the function on the next tick.
+ pub fn runDetached(self: *Loop, alloc: *mem.Allocator, comptime func: anytype, args: anytype) error{OutOfMemory}!void {
+ if (!std.io.is_async) @compileError("Can't use runDetached in non-async mode!");
+ if (@TypeOf(@call(.{}, func, args)) != void) {
+ @compileError("`func` must not have a return value");
+ }
+
+ const Wrapper = struct {
+ const Args = @TypeOf(args);
+ fn run(func_args: Args, loop: *Loop, allocator: *mem.Allocator) void {
+ loop.yield();
+ const result = @call(.{}, func, func_args);
+ suspend {
+ allocator.destroy(@frame());
+ }
+ }
+ };
+
+ var run_frame = try alloc.create(@Frame(Wrapper.run));
+ run_frame.* = async Wrapper.run(args, self, alloc);
+ }
+
/// Yielding lets the event loop run, starting any unstarted async operations.
/// Note that async operations automatically start when a function yields for any other reason,
/// 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 {
testing.expect(value == 1234);
did_it.* = true;
}
+
+var testRunDetachedData: usize = 0;
+test "std.event.Loop - runDetached" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+ if (!std.io.is_async) return error.SkipZigTest;
+ if (true) {
+ // https://github.com/ziglang/zig/issues/4922
+ return error.SkipZigTest;
+ }
+
+ var loop: Loop = undefined;
+ try loop.initMultiThreaded();
+ defer loop.deinit();
+
+ // Schedule the execution, won't actually start until we start the
+ // event loop.
+ try loop.runDetached(std.testing.allocator, testRunDetached, .{});
+
+ // Now we can start the event loop. The function will return only
+ // after all tasks have been completed, allowing us to synchonize
+ // with the previous runDetached.
+ loop.run();
+
+ testing.expect(testRunDetachedData == 1);
+}
+
+fn testRunDetached() void {
+ testRunDetachedData += 1;
+}
diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig
@@ -1181,6 +1181,16 @@ fn bufPrintIntToSlice(buf: []u8, value: anytype, base: u8, uppercase: bool, opti
return buf[0..formatIntBuf(buf, value, base, uppercase, options)];
}
+pub fn comptimePrint(comptime fmt: []const u8, args: anytype) *const [count(fmt, args)]u8 {
+ comptime var buf: [count(fmt, args)]u8 = undefined;
+ _ = bufPrint(&buf, fmt, args) catch unreachable;
+ return &buf;
+}
+
+test "comptimePrint" {
+ std.testing.expectEqualSlices(u8, "100", comptime comptimePrint("{}", .{100}));
+}
+
test "parse u64 digit too big" {
_ = parseUnsigned(u64, "123a", 10) catch |err| {
if (err == error.InvalidCharacter) return;
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
@@ -1856,7 +1856,7 @@ pub const Dir = struct {
}
};
-/// Returns an handle to the current working directory. It is not opened with iteration capability.
+/// Returns a handle to the current working directory. It is not opened with iteration capability.
/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
/// On POSIX targets, this function is comptime-callable.
pub fn cwd() Dir {
@@ -2162,7 +2162,7 @@ pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, flags);
}
-pub const SelfExePathError = os.ReadLinkError || os.SysCtlError;
+pub const SelfExePathError = os.ReadLinkError || os.SysCtlError || os.RealPathError;
/// `selfExePath` except allocates the result on the heap.
/// Caller owns returned memory.
@@ -2190,10 +2190,18 @@ pub fn selfExePathAlloc(allocator: *Allocator) ![]u8 {
/// TODO make the return type of this a null terminated pointer
pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
if (is_darwin) {
- var u32_len: u32 = @intCast(u32, math.min(out_buffer.len, math.maxInt(u32)));
- const rc = std.c._NSGetExecutablePath(out_buffer.ptr, &u32_len);
+ // Note that _NSGetExecutablePath() will return "a path" to
+ // the executable not a "real path" to the executable.
+ var symlink_path_buf: [MAX_PATH_BYTES:0]u8 = undefined;
+ var u32_len: u32 = MAX_PATH_BYTES + 1; // include the sentinel
+ const rc = std.c._NSGetExecutablePath(&symlink_path_buf, &u32_len);
if (rc != 0) return error.NameTooLong;
- return mem.spanZ(@ptrCast([*:0]u8, out_buffer));
+
+ var real_path_buf: [MAX_PATH_BYTES]u8 = undefined;
+ const real_path = try std.os.realpathZ(&symlink_path_buf, &real_path_buf);
+ if (real_path.len > out_buffer.len) return error.NameTooLong;
+ std.mem.copy(u8, out_buffer, real_path);
+ return out_buffer[0..real_path.len];
}
switch (builtin.os.tag) {
.linux => return os.readlinkZ("/proc/self/exe", out_buffer),
diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig
@@ -615,7 +615,7 @@ pub const File = struct {
}
}
- pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!usize {
+ pub fn pwritev(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!usize {
if (is_windows) {
// TODO improve this to use WriteFileScatter
if (iovecs.len == 0) return @as(usize, 0);
@@ -632,11 +632,11 @@ pub const File = struct {
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
/// order to handle partial writes from the underlying OS layer.
- pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!void {
+ pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: u64) PWriteError!void {
if (iovecs.len == 0) return;
var i: usize = 0;
- var off: usize = 0;
+ var off: u64 = 0;
while (true) {
var amt = try self.pwritev(iovecs[i..], offset + off);
off += amt;
@@ -652,14 +652,16 @@ pub const File = struct {
pub const CopyRangeError = os.CopyFileRangeError;
- pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) CopyRangeError!usize {
- return os.copy_file_range(in.handle, in_offset, out.handle, out_offset, len, 0);
+ pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: u64) CopyRangeError!u64 {
+ const adjusted_len = math.cast(usize, len) catch math.maxInt(usize);
+ const result = try os.copy_file_range(in.handle, in_offset, out.handle, out_offset, adjusted_len, 0);
+ return result;
}
/// Returns the number of bytes copied. If the number read is smaller than `buffer.len`, it
/// means the in file reached the end. Reaching the end of a file is not an error condition.
- pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) CopyRangeError!usize {
- var total_bytes_copied: usize = 0;
+ pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: u64) CopyRangeError!u64 {
+ var total_bytes_copied: u64 = 0;
var in_off = in_offset;
var out_off = out_offset;
while (total_bytes_copied < len) {
diff --git a/lib/std/macho.zig b/lib/std/macho.zig
@@ -1257,3 +1257,47 @@ pub const reloc_type_x86_64 = packed enum(u4) {
/// for thread local variables
X86_64_RELOC_TLV,
};
+
+/// This symbol is a reference to an external non-lazy (data) symbol.
+pub const REFERENCE_FLAG_UNDEFINED_NON_LAZY: u16 = 0x0;
+
+/// This symbol is a reference to an external lazy symbol—that is, to a function call.
+pub const REFERENCE_FLAG_UNDEFINED_LAZY: u16 = 0x1;
+
+/// This symbol is defined in this module.
+pub const REFERENCE_FLAG_DEFINED: u16 = 0x2;
+
+/// This symbol is defined in this module and is visible only to modules within this shared library.
+pub const REFERENCE_FLAG_PRIVATE_DEFINED: u16 = 3;
+
+/// This symbol is defined in another module in this file, is a non-lazy (data) symbol, and is visible
+/// only to modules within this shared library.
+pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY: u16 = 4;
+
+/// This symbol is defined in another module in this file, is a lazy (function) symbol, and is visible
+/// only to modules within this shared library.
+pub const REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY: u16 = 5;
+
+/// Must be set for any defined symbol that is referenced by dynamic-loader APIs (such as dlsym and
+/// NSLookupSymbolInImage) and not ordinary undefined symbol references. The strip tool uses this bit
+/// to avoid removing symbols that must exist: If the symbol has this bit set, strip does not strip it.
+pub const REFERENCED_DYNAMICALLY: u16 = 0x10;
+
+/// Used by the dynamic linker at runtime. Do not set this bit.
+pub const N_DESC_DISCARDED: u16 = 0x20;
+
+/// Indicates that this symbol is a weak reference. If the dynamic linker cannot find a definition
+/// for this symbol, it sets the address of this symbol to 0. The static linker sets this symbol given
+/// the appropriate weak-linking flags.
+pub const N_WEAK_REF: u16 = 0x40;
+
+/// Indicates that this symbol is a weak definition. If the static linker or the dynamic linker finds
+/// another (non-weak) definition for this symbol, the weak definition is ignored. Only symbols in a
+/// coalesced section (page 23) can be marked as a weak definition.
+pub const N_WEAK_DEF: u16 = 0x80;
+
+/// The N_SYMBOL_RESOLVER bit of the n_desc field indicates that the
+/// that the function is actually a resolver function and should
+/// be called to get the address of the real function to use.
+/// This bit is only available in .o files (MH_OBJECT filetype)
+pub const N_SYMBOL_RESOLVER: u16 = 0x100;
diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig
@@ -58,6 +58,11 @@ pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize {
return (string_len + (limb_bits / base - 1)) / (limb_bits / base);
}
+pub fn calcPowLimbsBufferLen(a_bit_count: usize, y: usize) usize {
+ // The 2 accounts for the minimum space requirement for llmulacc
+ return 2 + (a_bit_count * y + (limb_bits - 1)) / limb_bits;
+}
+
/// a + b * c + *carry, sets carry to the overflow bits
pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
@setRuntimeSafety(debug_safety);
@@ -597,6 +602,52 @@ pub const Mutable = struct {
return gcdLehmer(rma, x_copy, y_copy, limbs_buffer);
}
+ /// q = a ^ b
+ ///
+ /// r may not alias a.
+ ///
+ /// Asserts that `r` has enough limbs to store the result. Upper bound is
+ /// `calcPowLimbsBufferLen(a.bitCountAbs(), b)`.
+ ///
+ /// `limbs_buffer` is used for temporary storage.
+ /// The amount required is given by `calcPowLimbsBufferLen`.
+ pub fn pow(r: *Mutable, a: Const, b: u32, limbs_buffer: []Limb) !void {
+ assert(r.limbs.ptr != a.limbs.ptr); // illegal aliasing
+
+ // Handle all the trivial cases first
+ switch (b) {
+ 0 => {
+ // a^0 = 1
+ return r.set(1);
+ },
+ 1 => {
+ // a^1 = a
+ return r.copy(a);
+ },
+ else => {},
+ }
+
+ if (a.eqZero()) {
+ // 0^b = 0
+ return r.set(0);
+ } else if (a.limbs.len == 1 and a.limbs[0] == 1) {
+ // 1^b = 1 and -1^b = ±1
+ r.set(1);
+ r.positive = a.positive or (b & 1) == 0;
+ return;
+ }
+
+ // Here a>1 and b>1
+ const needed_limbs = calcPowLimbsBufferLen(a.bitCountAbs(), b);
+ assert(r.limbs.len >= needed_limbs);
+ assert(limbs_buffer.len >= needed_limbs);
+
+ llpow(r.limbs, a.limbs, b, limbs_buffer);
+
+ r.normalize(needed_limbs);
+ r.positive = a.positive or (b & 1) == 0;
+ }
+
/// rma may not alias x or y.
/// x and y may alias each other.
/// Asserts that `rma` has enough limbs to store the result. Upper bound is given by `calcGcdNoAliasLimbLen`.
@@ -1775,6 +1826,29 @@ pub const Managed = struct {
try m.gcd(x.toConst(), y.toConst(), &limbs_buffer);
rma.setMetadata(m.positive, m.len);
}
+
+ pub fn pow(rma: *Managed, a: Managed, b: u32) !void {
+ const needed_limbs = calcPowLimbsBufferLen(a.bitCountAbs(), b);
+
+ const limbs_buffer = try rma.allocator.alloc(Limb, needed_limbs);
+ defer rma.allocator.free(limbs_buffer);
+
+ if (rma.limbs.ptr == a.limbs.ptr) {
+ var m = try Managed.initCapacity(rma.allocator, needed_limbs);
+ errdefer m.deinit();
+ var m_mut = m.toMutable();
+ try m_mut.pow(a.toConst(), b, limbs_buffer);
+ m.setMetadata(m_mut.positive, m_mut.len);
+
+ rma.deinit();
+ rma.swap(&m);
+ } else {
+ try rma.ensureCapacity(needed_limbs);
+ var rma_mut = rma.toMutable();
+ try rma_mut.pow(a.toConst(), b, limbs_buffer);
+ rma.setMetadata(rma_mut.positive, rma_mut.len);
+ }
+ }
};
/// Knuth 4.3.1, Algorithm M.
@@ -2129,6 +2203,56 @@ fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {
}
}
+/// Knuth 4.6.3
+fn llpow(r: []Limb, a: []const Limb, b: u32, tmp_limbs: []Limb) void {
+ var tmp1: []Limb = undefined;
+ var tmp2: []Limb = undefined;
+
+ // Multiplication requires no aliasing between the operand and the result
+ // variable, use the output limbs and another temporary set to overcome this
+ // limitation.
+ // The initial assignment makes the result end in `r` so an extra memory
+ // copy is saved, each 1 flips the index twice so it's a no-op so count the
+ // 0.
+ const b_leading_zeros = @intCast(u5, @clz(u32, b));
+ const exp_zeros = @popCount(u32, ~b) - b_leading_zeros;
+ if (exp_zeros & 1 != 0) {
+ tmp1 = tmp_limbs;
+ tmp2 = r;
+ } else {
+ tmp1 = r;
+ tmp2 = tmp_limbs;
+ }
+
+ const a_norm = a[0..llnormalize(a)];
+
+ mem.copy(Limb, tmp1, a_norm);
+ mem.set(Limb, tmp1[a_norm.len..], 0);
+
+ // Scan the exponent as a binary number, from left to right, dropping the
+ // most significant bit set.
+ const exp_bits = @intCast(u5, 31 - b_leading_zeros);
+ var exp = @bitReverse(u32, b) >> 1 + b_leading_zeros;
+
+ var i: u5 = 0;
+ while (i < exp_bits) : (i += 1) {
+ // Square
+ {
+ mem.set(Limb, tmp2, 0);
+ const op = tmp1[0..llnormalize(tmp1)];
+ llmulacc(null, tmp2, op, op);
+ mem.swap([]Limb, &tmp1, &tmp2);
+ }
+ // Multiply by a
+ if (exp & 1 != 0) {
+ mem.set(Limb, tmp2, 0);
+ llmulacc(null, tmp2, tmp1[0..llnormalize(tmp1)], a_norm);
+ mem.swap([]Limb, &tmp1, &tmp2);
+ }
+ exp >>= 1;
+ }
+}
+
// Storage must live for the lifetime of the returned value
fn fixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Mutable {
assert(storage.len >= 2);
diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig
@@ -1480,3 +1480,55 @@ test "big.int const to managed" {
testing.expect(a.toConst().eq(b.toConst()));
}
+
+test "big.int pow" {
+ {
+ var a = try Managed.initSet(testing.allocator, 10);
+ defer a.deinit();
+
+ try a.pow(a, 8);
+ testing.expectEqual(@as(u32, 100000000), try a.to(u32));
+ }
+ {
+ var a = try Managed.initSet(testing.allocator, 10);
+ defer a.deinit();
+
+ var y = try Managed.init(testing.allocator);
+ defer y.deinit();
+
+ // y and a are not aliased
+ try y.pow(a, 123);
+ // y and a are aliased
+ try a.pow(a, 123);
+
+ testing.expect(a.eq(y));
+
+ const ys = try y.toString(testing.allocator, 16, false);
+ defer testing.allocator.free(ys);
+ testing.expectEqualSlices(
+ u8,
+ "183425a5f872f126e00a5ad62c839075cd6846c6fb0230887c7ad7a9dc530fcb" ++
+ "4933f60e8000000000000000000000000000000",
+ ys,
+ );
+ }
+ // Special cases
+ {
+ var a = try Managed.initSet(testing.allocator, 0);
+ defer a.deinit();
+
+ try a.pow(a, 100);
+ testing.expectEqual(@as(i32, 0), try a.to(i32));
+
+ try a.set(1);
+ try a.pow(a, 0);
+ testing.expectEqual(@as(i32, 1), try a.to(i32));
+ try a.pow(a, 100);
+ testing.expectEqual(@as(i32, 1), try a.to(i32));
+ try a.set(-1);
+ try a.pow(a, 15);
+ testing.expectEqual(@as(i32, -1), try a.to(i32));
+ try a.pow(a, 16);
+ testing.expectEqual(@as(i32, 1), try a.to(i32));
+ }
+}
diff --git a/lib/std/meta.zig b/lib/std/meta.zig
@@ -854,6 +854,7 @@ pub fn ArgsTuple(comptime Function: type) type {
.field_type = arg.arg_type.?,
.default_value = @as(?(arg.arg_type.?), null),
.is_comptime = false,
+ .alignment = @alignOf(arg.arg_type.?),
};
}
@@ -884,6 +885,7 @@ pub fn Tuple(comptime types: []const type) type {
.field_type = T,
.default_value = @as(?T, null),
.is_comptime = false,
+ .alignment = @alignOf(T),
};
}
diff --git a/lib/std/meta/trailer_flags.zig b/lib/std/meta/trailer_flags.zig
@@ -47,6 +47,7 @@ pub fn TrailerFlags(comptime Fields: type) type {
@as(?struct_field.field_type, null),
),
.is_comptime = false,
+ .alignment = @alignOf(?struct_field.field_type),
};
}
break :blk @Type(.{
diff --git a/lib/std/os.zig b/lib/std/os.zig
@@ -3993,7 +3993,7 @@ pub const RealPathError = error{
/// Expands all symbolic links and resolves references to `.`, `..`, and
/// extra `/` characters in `pathname`.
/// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
-/// See also `realpathC` and `realpathW`.
+/// See also `realpathZ` and `realpathW`.
pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
if (builtin.os.tag == .windows) {
const pathname_w = try windows.sliceToPrefixedFileW(pathname);
@@ -5410,3 +5410,33 @@ pub fn prctl(option: i32, args: anytype) PrctlError!u31 {
else => |err| return std.os.unexpectedErrno(err),
}
}
+
+pub const GetrlimitError = UnexpectedError;
+
+pub fn getrlimit(resource: rlimit_resource) GetrlimitError!rlimit {
+ // TODO implement for systems other than linux and enable test
+ var limits: rlimit = undefined;
+ const rc = system.getrlimit(resource, &limits);
+ switch (errno(rc)) {
+ 0 => return limits,
+ EFAULT => unreachable, // bogus pointer
+ EINVAL => unreachable,
+ else => |err| return std.os.unexpectedErrno(err),
+ }
+}
+
+pub const SetrlimitError = error{
+ PermissionDenied,
+} || UnexpectedError;
+
+pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void {
+ // TODO implement for systems other than linux and enable test
+ const rc = system.setrlimit(resource, &limits);
+ switch (errno(rc)) {
+ 0 => return,
+ EFAULT => unreachable, // bogus pointer
+ EINVAL => unreachable,
+ EPERM => return error.PermissionDenied,
+ else => |err| return std.os.unexpectedErrno(err),
+ }
+}
diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig
@@ -1890,3 +1890,79 @@ pub const ifreq = extern struct {
data: ?[*]u8,
},
};
+
+// doc comments copied from musl
+pub const rlimit_resource = extern enum(c_int) {
+ /// Per-process CPU limit, in seconds.
+ CPU,
+
+ /// Largest file that can be created, in bytes.
+ FSIZE,
+
+ /// Maximum size of data segment, in bytes.
+ DATA,
+
+ /// Maximum size of stack segment, in bytes.
+ STACK,
+
+ /// Largest core file that can be created, in bytes.
+ CORE,
+
+ /// Largest resident set size, in bytes.
+ /// This affects swapping; processes that are exceeding their
+ /// resident set size will be more likely to have physical memory
+ /// taken from them.
+ RSS,
+
+ /// Number of processes.
+ NPROC,
+
+ /// Number of open files.
+ NOFILE,
+
+ /// Locked-in-memory address space.
+ MEMLOCK,
+
+ /// Address space limit.
+ AS,
+
+ /// Maximum number of file locks.
+ LOCKS,
+
+ /// Maximum number of pending signals.
+ SIGPENDING,
+
+ /// Maximum bytes in POSIX message queues.
+ MSGQUEUE,
+
+ /// Maximum nice priority allowed to raise to.
+ /// Nice levels 19 .. -20 correspond to 0 .. 39
+ /// values of this resource limit.
+ NICE,
+
+ /// Maximum realtime priority allowed for non-priviledged
+ /// processes.
+ RTPRIO,
+
+ /// Maximum CPU time in µs that a process scheduled under a real-time
+ /// scheduling policy may consume without making a blocking system
+ /// call before being forcibly descheduled.
+ RTTIME,
+
+ _,
+};
+
+pub const rlim_t = u64;
+
+/// No limit
+pub const RLIM_INFINITY = ~@as(rlim_t, 0);
+
+pub const RLIM_SAVED_MAX = RLIM_INFINITY;
+pub const RLIM_SAVED_CUR = RLIM_INFINITY;
+
+pub const rlimit = extern struct {
+ /// Soft limit
+ cur: rlim_t,
+ /// Hard limit
+ max: rlim_t,
+};
diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig
@@ -1263,6 +1263,26 @@ pub fn prctl(option: i32, arg2: usize, arg3: usize, arg4: usize, arg5: usize) us
return syscall5(.prctl, @bitCast(usize, @as(isize, option)), arg2, arg3, arg4, arg5);
}
+pub fn getrlimit(resource: rlimit_resource, rlim: *rlimit) usize {
+ // use prlimit64 to have 64 bit limits on 32 bit platforms
+ return prlimit(0, resource, null, rlim);
+}
+
+pub fn setrlimit(resource: rlimit_resource, rlim: *const rlimit) usize {
+ // use prlimit64 to have 64 bit limits on 32 bit platforms
+ return prlimit(0, resource, rlim, null);
+}
+
+pub fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: ?*const rlimit, old_limit: ?*rlimit) usize {
+ return syscall4(
+ .prlimit64,
+ @bitCast(usize, @as(isize, pid)),
+ @bitCast(usize, @as(isize, @enumToInt(resource))),
+ @ptrToInt(new_limit),
+ @ptrToInt(old_limit)
+ );
+}
+
test "" {
if (builtin.os.tag == .linux) {
_ = @import("linux/test.zig");
diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig
@@ -591,3 +591,13 @@ test "fsync" {
try os.fsync(file.handle);
try os.fdatasync(file.handle);
}
+
+test "getrlimit and setrlimit" {
+ // TODO enable for other systems when implemented
+ if(builtin.os.tag != .linux){
+ return error.SkipZigTest;
+ }
+
+ const cpuLimit = try os.getrlimit(.CPU);
+ try os.setrlimit(.CPU, cpuLimit);
+}
diff --git a/lib/std/packed_int_array.zig b/lib/std/packed_int_array.zig
@@ -318,9 +318,12 @@ pub fn PackedIntSliceEndian(comptime Int: type, comptime endian: builtin.Endian)
};
}
+const we_are_testing_this_with_stage1_which_leaks_comptime_memory = true;
+
test "PackedIntArray" {
// TODO @setEvalBranchQuota generates panics in wasm32. Investigate.
if (builtin.arch == .wasm32) return error.SkipZigTest;
+ if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
@setEvalBranchQuota(10000);
const max_bits = 256;
@@ -358,6 +361,7 @@ test "PackedIntArray" {
}
test "PackedIntArray init" {
+ if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
const PackedArray = PackedIntArray(u3, 8);
var packed_array = PackedArray.init([_]u3{ 0, 1, 2, 3, 4, 5, 6, 7 });
var i = @as(usize, 0);
@@ -367,6 +371,7 @@ test "PackedIntArray init" {
test "PackedIntSlice" {
// TODO @setEvalBranchQuota generates panics in wasm32. Investigate.
if (builtin.arch == .wasm32) return error.SkipZigTest;
+ if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
@setEvalBranchQuota(10000);
const max_bits = 256;
@@ -405,6 +410,7 @@ test "PackedIntSlice" {
}
test "PackedIntSlice of PackedInt(Array/Slice)" {
+ if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
const max_bits = 16;
const int_count = 19;
@@ -470,6 +476,7 @@ test "PackedIntSlice of PackedInt(Array/Slice)" {
}
test "PackedIntSlice accumulating bit offsets" {
+ if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
//bit_offset is u3, so standard debugging asserts should catch
// anything
{
@@ -497,6 +504,8 @@ test "PackedIntSlice accumulating bit offsets" {
//@NOTE: As I do not have a big endian system to test this on,
// big endian values were not tested
test "PackedInt(Array/Slice) sliceCast" {
+ if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
+
const PackedArray = PackedIntArray(u1, 16);
var packed_array = PackedArray.init([_]u1{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 });
const packed_slice_cast_2 = packed_array.sliceCast(u2);
@@ -537,6 +546,8 @@ test "PackedInt(Array/Slice) sliceCast" {
}
test "PackedInt(Array/Slice)Endian" {
+ if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
+
{
const PackedArrayBe = PackedIntArrayEndian(u4, .Big, 8);
var packed_array_be = PackedArrayBe.init([_]u4{ 0, 1, 2, 3, 4, 5, 6, 7 });
@@ -604,6 +615,8 @@ test "PackedInt(Array/Slice)Endian" {
// after this one is not mapped and will cause a segfault if we
// don't account for the bounds.
test "PackedIntArray at end of available memory" {
+ if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
+
switch (builtin.os.tag) {
.linux, .macosx, .ios, .freebsd, .netbsd, .windows => {},
else => return,
@@ -623,6 +636,8 @@ test "PackedIntArray at end of available memory" {
}
test "PackedIntSlice at end of available memory" {
+ if (we_are_testing_this_with_stage1_which_leaks_comptime_memory) return error.SkipZigTest;
+
switch (builtin.os.tag) {
.linux, .macosx, .ios, .freebsd, .netbsd, .windows => {},
else => return,
diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig
@@ -213,6 +213,8 @@ pub const NativeTargetInfo = struct {
// kernel version
const kernel_version = if (mem.indexOfScalar(u8, release, '-')) |pos|
release[0..pos]
+ else if (mem.indexOfScalar(u8, release, '_')) |pos|
+ release[0..pos]
else
release;
diff --git a/src/Compilation.zig b/src/Compilation.zig
@@ -8,6 +8,7 @@ const log = std.log.scoped(.compilation);
const Target = std.Target;
const Value = @import("value.zig").Value;
+const Type = @import("type.zig").Type;
const target_util = @import("target.zig");
const Package = @import("Package.zig");
const link = @import("link.zig");
@@ -352,6 +353,7 @@ pub const InitOptions = struct {
time_report: bool = false,
stack_report: bool = false,
link_eh_frame_hdr: bool = false,
+ link_emit_relocs: bool = false,
linker_script: ?[]const u8 = null,
version_script: ?[]const u8 = null,
override_soname: ?[]const u8 = null,
@@ -376,6 +378,7 @@ pub const InitOptions = struct {
is_compiler_rt_or_libc: bool = false,
parent_compilation_link_libc: bool = false,
stack_size_override: ?u64 = null,
+ image_base_override: ?u64 = null,
self_exe_path: ?[]const u8 = null,
version: ?std.builtin.Version = null,
libc_installation: ?*const LibCInstallation = null,
@@ -447,8 +450,10 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
options.system_libs.len != 0 or
options.link_libc or options.link_libcpp or
options.link_eh_frame_hdr or
+ options.link_emit_relocs or
options.output_mode == .Lib or
options.lld_argv.len != 0 or
+ options.image_base_override != null or
options.linker_script != null or options.version_script != null)
{
break :blk true;
@@ -473,8 +478,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
{
break :dl true;
}
- if (options.system_libs.len != 0)
- break :dl true;
+ if (options.system_libs.len != 0) {
+ // when creating a executable that links to system libraries,
+ // we require dynamic linking, but we must not link static libraries
+ // or object files dynamically!
+ break :dl (options.output_mode == .Exe);
+ }
break :dl false;
};
@@ -638,15 +647,19 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
const root_scope = rs: {
if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) {
+ const struct_payload = try gpa.create(Type.Payload.EmptyStruct);
const root_scope = try gpa.create(Module.Scope.File);
+ struct_payload.* = .{ .scope = &root_scope.root_container };
root_scope.* = .{
- .sub_file_path = root_pkg.root_src_path,
+ // TODO this is duped so it can be freed in Container.deinit
+ .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path),
.source = .{ .unloaded = {} },
.contents = .{ .not_available = {} },
.status = .never_loaded,
.root_container = .{
.file_scope = root_scope,
.decls = .{},
+ .ty = Type.initPayload(&struct_payload.base),
},
};
break :rs &root_scope.base;
@@ -765,10 +778,12 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.z_nodelete = options.linker_z_nodelete,
.z_defs = options.linker_z_defs,
.stack_size_override = options.stack_size_override,
+ .image_base_override = options.image_base_override,
.linker_script = options.linker_script,
.version_script = options.version_script,
.gc_sections = options.linker_gc_sections,
.eh_frame_hdr = options.link_eh_frame_hdr,
+ .emit_relocs = options.link_emit_relocs,
.rdynamic = options.rdynamic,
.extra_lld_args = options.lld_argv,
.override_soname = options.override_soname,
@@ -785,7 +800,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.llvm_cpu_features = llvm_cpu_features,
.is_compiler_rt_or_libc = options.is_compiler_rt_or_libc,
.parent_compilation_link_libc = options.parent_compilation_link_libc,
- .each_lib_rpath = options.each_lib_rpath orelse false,
+ .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os,
.disable_lld_caching = options.disable_lld_caching,
.subsystem = options.subsystem,
.is_test = options.is_test,
@@ -1022,6 +1037,17 @@ pub fn update(self: *Compilation) !void {
else => |e| return e,
};
}
+
+ // TODO only analyze imports if they are still referenced
+ for (module.import_table.items()) |entry| {
+ entry.value.unload(module.gpa);
+ module.analyzeContainer(&entry.value.root_container) catch |err| switch (err) {
+ error.AnalysisFail => {
+ assert(self.totalErrorCount() != 0);
+ },
+ else => |e| return e,
+ };
+ }
}
}
@@ -1146,7 +1172,15 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
};
}
-pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void {
+pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void {
+ var progress: std.Progress = .{};
+ var main_progress_node = try progress.start("", null);
+ defer main_progress_node.end();
+ if (self.color == .Off) progress.terminal = null;
+
+ var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len);
+ defer c_comp_progress_node.end();
+
while (self.work_queue.readItem()) |work_item| switch (work_item) {
.codegen_decl => |decl| switch (decl.analysis) {
.unreferenced => unreachable,
@@ -1223,7 +1257,7 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void {
};
},
.c_object => |c_object| {
- self.updateCObject(c_object) catch |err| switch (err) {
+ self.updateCObject(c_object, &c_comp_progress_node) catch |err| switch (err) {
error.AnalysisFail => continue,
else => {
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 {
if (!build_options.is_stage1)
unreachable;
- self.updateStage1Module() catch |err| {
+ self.updateStage1Module(main_progress_node) catch |err| {
fatal("unable to build stage1 zig object: {}", .{@errorName(err)});
};
},
@@ -1470,7 +1504,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
};
}
-fn updateCObject(comp: *Compilation, c_object: *CObject) !void {
+fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *std.Progress.Node) !void {
if (!build_options.have_llvm) {
return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{});
}
@@ -1513,6 +1547,12 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void {
const arena = &arena_allocator.allocator;
const c_source_basename = std.fs.path.basename(c_object.src.src_path);
+
+ c_comp_progress_node.activate();
+ var child_progress_node = c_comp_progress_node.start(c_source_basename, null);
+ child_progress_node.activate();
+ defer child_progress_node.end();
+
// Special case when doing build-obj for just one C file. When there are more than one object
// file and building an object we need to link them together, but with just one it should go
// directly to the output file.
@@ -2506,7 +2546,7 @@ fn buildStaticLibFromZig(comp: *Compilation, src_basename: []const u8, out: *?CR
};
}
-fn updateStage1Module(comp: *Compilation) !void {
+fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -2550,6 +2590,8 @@ fn updateStage1Module(comp: *Compilation) !void {
man.hash.add(comp.emit_llvm_ir != null);
man.hash.add(comp.emit_analysis != null);
man.hash.add(comp.emit_docs != null);
+ man.hash.addOptionalBytes(comp.test_filter);
+ man.hash.addOptionalBytes(comp.test_name_prefix);
// Capture the state in case we come back from this branch where the hash doesn't match.
const prev_hash_state = man.hash.peekBin();
@@ -2612,10 +2654,6 @@ fn updateStage1Module(comp: *Compilation) !void {
.llvm_cpu_name = if (target.cpu.model.llvm_name) |s| s.ptr else null,
.llvm_cpu_features = comp.bin_file.options.llvm_cpu_features.?,
};
- var progress: std.Progress = .{};
- var main_progress_node = try progress.start("", null);
- defer main_progress_node.end();
- if (comp.color == .Off) progress.terminal = null;
comp.stage1_cache_manifest = &man;
diff --git a/src/Module.zig b/src/Module.zig
@@ -70,6 +70,9 @@ deletion_set: ArrayListUnmanaged(*Decl) = .{},
/// Error tags and their values, tag names are duped with mod.gpa.
global_error_set: std.StringHashMapUnmanaged(u16) = .{},
+/// Keys are fully qualified paths
+import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},
+
/// Incrementing integer used to compare against the corresponding Decl
/// field to determine whether a Decl's status applies to an ongoing update, or a
/// previous analysis.
@@ -208,7 +211,7 @@ pub const Decl = struct {
.container => {
const container = @fieldParentPtr(Scope.Container, "base", self.scope);
const tree = container.file_scope.contents.tree;
- // TODO Container should have it's own decls()
+ // TODO Container should have its own decls()
const decl_node = tree.root_node.decls()[self.src_index];
return tree.token_locs[decl_node.firstToken()].start;
},
@@ -532,12 +535,13 @@ pub const Scope = struct {
/// Direct children of the file.
decls: std.AutoArrayHashMapUnmanaged(*Decl, void),
-
- // TODO implement container types and put this in a status union
- // ty: Type
+ ty: Type,
pub fn deinit(self: *Container, gpa: *Allocator) void {
self.decls.deinit(gpa);
+ // TODO either Container of File should have an arena for sub_file_path and ty
+ gpa.destroy(self.ty.cast(Type.Payload.EmptyStruct).?);
+ gpa.free(self.file_scope.sub_file_path);
self.* = undefined;
}
@@ -854,6 +858,11 @@ pub fn deinit(self: *Module) void {
gpa.free(entry.key);
}
self.global_error_set.deinit(gpa);
+
+ for (self.import_table.items()) |entry| {
+ entry.value.base.destroy(gpa);
+ }
+ self.import_table.deinit(gpa);
}
fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
@@ -2381,6 +2390,45 @@ pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst,
return self.fail(scope, src, "TODO implement analysis of slice", .{});
}
+pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: []const u8) !*Scope.File {
+ // TODO if (package_table.get(target_string)) |pkg|
+ if (self.import_table.get(target_string)) |some| {
+ return some;
+ }
+
+ // TODO check for imports outside of pkg path
+ if (false) return error.ImportOutsidePkgPath;
+
+ // TODO Scope.Container arena for ty and sub_file_path
+ const struct_payload = try self.gpa.create(Type.Payload.EmptyStruct);
+ errdefer self.gpa.destroy(struct_payload);
+ const file_scope = try self.gpa.create(Scope.File);
+ errdefer self.gpa.destroy(file_scope);
+ const file_path = try self.gpa.dupe(u8, target_string);
+ errdefer self.gpa.free(file_path);
+
+ struct_payload.* = .{ .scope = &file_scope.root_container };
+ file_scope.* = .{
+ .sub_file_path = file_path,
+ .source = .{ .unloaded = {} },
+ .contents = .{ .not_available = {} },
+ .status = .never_loaded,
+ .root_container = .{
+ .file_scope = file_scope,
+ .decls = .{},
+ .ty = Type.initPayload(&struct_payload.base),
+ },
+ };
+ self.analyzeContainer(&file_scope.root_container) catch |err| switch (err) {
+ error.AnalysisFail => {
+ assert(self.comp.totalErrorCount() != 0);
+ },
+ else => |e| return e,
+ };
+ try self.import_table.put(self.gpa, file_scope.sub_file_path, file_scope);
+ return file_scope;
+}
+
/// Asserts that lhs and rhs types are both numeric.
pub fn cmpNumeric(
self: *Module,
diff --git a/src/astgen.zig b/src/astgen.zig
@@ -1973,6 +1973,15 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa
}
}
+fn import(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 1);
+ const tree = scope.tree();
+ const src = tree.token_locs[call.builtin_token].start;
+ const params = call.params();
+ const target = try expr(mod, scope, .none, params[0]);
+ return addZIRUnOp(mod, scope, src, .import, target);
+}
+
fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
const tree = scope.tree();
const builtin_name = tree.tokenSlice(call.builtin_token);
@@ -1995,6 +2004,8 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built
} else if (mem.eql(u8, builtin_name, "@breakpoint")) {
const src = tree.token_locs[call.builtin_token].start;
return rlWrap(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint));
+ } else if (mem.eql(u8, builtin_name, "@import")) {
+ return rlWrap(mod, scope, rl, try import(mod, scope, call));
} else {
return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});
}
diff --git a/src/codegen.zig b/src/codegen.zig
@@ -17,9 +17,6 @@ const DW = std.dwarf;
const leb128 = std.debug.leb;
const log = std.log.scoped(.codegen);
-// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
-// zig fmt: off
-
/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
pub const BlockData = struct {
relocs: std.ArrayListUnmanaged(Reloc) = undefined,
@@ -35,7 +32,7 @@ pub const BlockData = struct {
/// comptime assert that makes sure we guessed correctly about the size. This only
/// exists so that we can bitcast an arch-independent field to and from the real MCValue.
pub const AnyMCValue = extern struct {
- a: u64,
+ a: usize,
b: u64,
};
@@ -170,7 +167,6 @@ pub fn generateSymbol(
},
.Pointer => {
// TODO populate .debug_info for the pointer
-
if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
const decl = payload.decl;
if (decl.analysis != .complete) return error.AnalysisFail;
@@ -206,7 +202,6 @@ pub fn generateSymbol(
},
.Int => {
// TODO populate .debug_info for the integer
-
const info = typed_value.ty.intInfo(bin_file.options.target);
if (info.bits == 8 and !info.signed) {
const x = typed_value.val.toUnsignedInt();
@@ -399,7 +394,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
self.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
const reg = callee_preserved_regs[free_index];
self.registers.putAssumeCapacityNoClobber(reg, inst);
- log.debug("alloc {} => {*}", .{reg, inst});
+ log.debug("alloc {} => {*}", .{ reg, inst });
return reg;
}
@@ -439,7 +434,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
try branch_stack.append(.{});
- const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: {
+ const src_data: struct { lbrace_src: usize, rbrace_src: usize, source: []const u8 } = blk: {
if (module_fn.owner_decl.scope.cast(Module.Scope.Container)) |container_scope| {
const tree = container_scope.file_scope.contents.tree;
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 {
try self.dbgSetEpilogueBegin();
}
},
+ .arm => {
+ const cc = self.fn_type.fnCallingConvention();
+ if (cc != .Naked) {
+ // push {fp, lr}
+ // mov fp, sp
+ // sub sp, sp, #reloc
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.push(.al, .{ .fp, .lr }).toU32());
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .fp, Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none)).toU32());
+ // TODO: prepare stack for local variables
+ // const backpatch_reloc = try self.code.addManyAsArray(4);
+
+ try self.dbgSetPrologueEnd();
+
+ try self.genBody(self.mod_fn.analysis.success);
+
+ // Backpatch stack offset
+ // const stack_end = self.max_end_stack;
+ // const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
+ // mem.writeIntLittle(u32, backpatch_reloc, Instruction.sub(.al, .sp, .sp, Instruction.Operand.imm()));
+
+ try self.dbgSetEpilogueBegin();
+
+ // mov sp, fp
+ // pop {fp, pc}
+ // TODO: return by jumping to this code, use relocations
+ // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32());
+ // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
+ } else {
+ try self.dbgSetPrologueEnd();
+ try self.genBody(self.mod_fn.analysis.success);
+ try self.dbgSetEpilogueBegin();
+ }
+ },
else => {
try self.dbgSetPrologueEnd();
try self.genBody(self.mod_fn.analysis.success);
@@ -586,7 +614,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const mcv = try self.genFuncInst(inst);
if (!inst.isUnused()) {
- log.debug("{*} => {}", .{inst, mcv});
+ log.debug("{*} => {}", .{ inst, mcv });
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
try branch.inst_table.putNoClobber(self.gpa, inst, mcv);
}
@@ -851,7 +879,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
// No side effects, so if it's unreferenced, do nothing.
if (inst.base.isUnused())
return MCValue.dead;
-
+
const operand = try self.resolveInst(inst.operand);
const info_a = inst.operand.ty.intInfo(self.target.*);
const info_b = inst.base.ty.intInfo(self.target.*);
@@ -972,10 +1000,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
if (self.registers.getEntry(toCanonicalReg(reg))) |entry| {
entry.value = inst;
}
- log.debug("reusing {} => {*}", .{reg, inst});
+ log.debug("reusing {} => {*}", .{ reg, inst });
},
.stack_offset => |off| {
- log.debug("reusing stack offset {} => {*}", .{off, inst});
+ log.debug("reusing stack offset {} => {*}", .{ off, inst });
return true;
},
else => return false,
@@ -1274,7 +1302,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const result = self.args[self.arg_index];
self.arg_index += 1;
- const name_with_null = inst.name[0..mem.lenZ(inst.name) + 1];
+ const name_with_null = inst.name[0 .. mem.lenZ(inst.name) + 1];
switch (result) {
.register => |reg| {
self.registers.putAssumeCapacityNoClobber(toCanonicalReg(reg), &inst.base);
@@ -1461,7 +1489,35 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
},
.arm => {
- if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});
+ for (info.args) |mc_arg, arg_i| {
+ const arg = inst.args[arg_i];
+ const arg_mcv = try self.resolveInst(inst.args[arg_i]);
+
+ switch (mc_arg) {
+ .none => continue,
+ .undef => unreachable,
+ .immediate => unreachable,
+ .unreach => unreachable,
+ .dead => unreachable,
+ .embedded_in_code => unreachable,
+ .memory => unreachable,
+ .compare_flags_signed => unreachable,
+ .compare_flags_unsigned => unreachable,
+ .register => |reg| {
+ try self.genSetReg(arg.src, reg, arg_mcv);
+ // TODO interact with the register allocator to mark the instruction as moved.
+ },
+ .stack_offset => {
+ return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
+ },
+ .ptr_stack_offset => {
+ return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
+ },
+ .ptr_embedded_in_code => {
+ return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
+ },
+ }
+ }
if (inst.func.cast(ir.Inst.Constant)) |func_inst| {
if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
@@ -1476,13 +1532,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
else
unreachable;
- // TODO only works with leaf functions
- // at the moment, which works fine for
- // Hello World, but not for real code
- // of course. Add pushing lr to stack
- // and popping after call
try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr });
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());
+
+ // TODO: add Instruction.supportedOn
+ // function for ARM
+ if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32());
+ } else {
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .lr, Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none)).toU32());
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
+ }
} else {
return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
}
@@ -1532,12 +1591,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
const func = func_val.func;
const got = &macho_file.sections.items[macho_file.got_section_index.?];
- const ptr_bytes = 8;
- const got_addr = @intCast(u32, got.addr + func.owner_decl.link.macho.offset_table_index.? * ptr_bytes);
- // ff 14 25 xx xx xx xx call [addr]
- try self.code.ensureCapacity(self.code.items.len + 7);
- self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
- mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
+ const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64);
+ // Here, we store the got address in %rax, and then call %rax
+ // movabsq [addr], %rax
+ try self.genSetReg(inst.base.src, .rax, .{ .memory = got_addr });
+ // callq *%rax
+ try self.code.ensureCapacity(self.code.items.len + 2);
+ self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
} else {
return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
}
@@ -1601,7 +1661,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
},
.arm => {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32());
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32());
+ // TODO: jump to the end with relocation
+ // // Just add space for an instruction, patch this later
+ // try self.code.resize(self.code.items.len + 4);
+ // try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
},
else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
}
@@ -1709,7 +1774,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
self.code.items.len += 4;
break :reloc reloc;
},
- else => return self.fail(inst.base.src, "TODO implement condbr {}", .{ self.target.cpu.arch }),
+ else => return self.fail(inst.base.src, "TODO implement condbr {}", .{self.target.cpu.arch}),
};
// 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 {
}
}
};
- log.debug("consolidating else_entry {*} {}=>{}", .{else_entry.key, else_entry.value, canon_mcv});
+ log.debug("consolidating else_entry {*} {}=>{}", .{ else_entry.key, else_entry.value, canon_mcv });
// TODO make sure the destination stack offset / register does not already have something
// going on there.
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 {
}
}
};
- log.debug("consolidating then_entry {*} {}=>{}", .{then_entry.key, parent_mcv, then_entry.value});
+ log.debug("consolidating then_entry {*} {}=>{}", .{ then_entry.key, parent_mcv, then_entry.value });
// TODO make sure the destination stack offset / register does not already have something
// going on there.
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 {
// break instruction will choose a MCValue for the block result and overwrite
// this field. Following break instructions will use that MCValue to put their
// block results.
- .mcv = @bitCast(AnyMCValue, MCValue { .none = {} }),
+ .mcv = @bitCast(AnyMCValue, MCValue{ .none = {} }),
};
defer inst.codegen.relocs.deinit(self.gpa);
@@ -2162,7 +2227,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
mem.writeIntLittle(u64, &buf, x_big);
// mov DWORD PTR [rbp+offset+4], immediate
- self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp + 4});
+ self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp + 4 });
self.code.appendSliceAssumeCapacity(buf[4..8]);
// mov DWORD PTR [rbp+offset], immediate
@@ -2213,14 +2278,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
// least amount of necessary instructions (use
// more intelligent rotating)
if (x <= math.maxInt(u8)) {
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
return;
} else if (x <= math.maxInt(u16)) {
// TODO Use movw Note: Not supported on
// all ARM targets!
-
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
} else if (x <= math.maxInt(u32)) {
// TODO Use movw and movt Note: Not
// supported on all ARM targets! Also TODO
@@ -2232,20 +2296,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
// orr reg, reg, #0xbb, 24
// orr reg, reg, #0xcc, 16
// orr reg, reg, #0xdd, 8
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, 0, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, 0, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32());
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32());
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32());
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32());
return;
} else {
return self.fail(src, "ARM registers are 32-bit wide", .{});
}
},
+ .register => |src_reg| {
+ // If the registers are the same, nothing to do.
+ if (src_reg.id() == reg.id())
+ return;
+
+ // mov reg, src_reg
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none)).toU32());
+ },
.memory => |addr| {
// The value is in memory at a hard-coded address.
// If the type is a pointer, it means the pointer address is at this memory location.
try self.genSetReg(src, reg, .{ .immediate = addr });
- mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, Instruction.Offset.none).toU32());
+ mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());
},
else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),
},
@@ -2590,7 +2662,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
} else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
const decl = payload.decl;
const got = &macho_file.sections.items[macho_file.got_section_index.?];
- const got_addr = got.addr + decl.link.macho.offset_table_index.? * ptr_bytes;
+ const got_addr = got.addr + decl.link.macho.offset_table_index * ptr_bytes;
return MCValue{ .memory = got_addr };
} else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
const decl = payload.decl;
@@ -2701,6 +2773,55 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),
}
},
+ .arm => {
+ switch (cc) {
+ .Naked => {
+ assert(result.args.len == 0);
+ result.return_value = .{ .unreach = {} };
+ result.stack_byte_count = 0;
+ result.stack_align = 1;
+ return result;
+ },
+ .Unspecified, .C => {
+ // ARM Procedure Call Standard, Chapter 6.5
+ var ncrn: usize = 0; // Next Core Register Number
+ var nsaa: u32 = 0; // Next stacked argument address
+
+ for (param_types) |ty, i| {
+ if (ty.abiAlignment(self.target.*) == 8) {
+ // Round up NCRN to the next even number
+ ncrn += ncrn % 2;
+ }
+
+ const param_size = @intCast(u32, ty.abiSize(self.target.*));
+ if (std.math.divCeil(u32, param_size, 4) catch unreachable <= 4 - ncrn) {
+ if (param_size <= 4) {
+ result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
+ ncrn += 1;
+ } else {
+ return self.fail(src, "TODO MCValues with multiple registers", .{});
+ }
+ } else if (ncrn < 4 and nsaa == 0) {
+ return self.fail(src, "TODO MCValues split between registers and stack", .{});
+ } else {
+ ncrn = 4;
+ if (ty.abiAlignment(self.target.*) == 8) {
+ if (nsaa % 8 != 0) {
+ nsaa += 8 - (nsaa % 8);
+ }
+ }
+
+ result.args[i] = .{ .stack_offset = nsaa };
+ nsaa += param_size;
+ }
+ }
+
+ result.stack_byte_count = nsaa;
+ result.stack_align = 4;
+ },
+ else => return self.fail(src, "TODO implement function parameters for {} on arm", .{cc}),
+ }
+ },
else => if (param_types.len != 0)
return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
}
@@ -2719,6 +2840,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
},
else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
},
+ .arm => switch (cc) {
+ .Naked => unreachable,
+ .Unspecified, .C => {
+ const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
+ if (ret_ty_size <= 4) {
+ result.return_value = .{ .register = c_abi_int_return_regs[0] };
+ } else {
+ return self.fail(src, "TODO support more return types for ARM backend", .{});
+ }
+ },
+ else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
+ },
else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),
}
return result;
diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig
@@ -113,6 +113,13 @@ test "Register.id" {
testing.expectEqual(@as(u4, 15), Register.pc.id());
}
+/// Program status registers containing flags, mode bits and other
+/// vital information
+pub const Psr = enum {
+ cpsr,
+ spsr,
+};
+
pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 };
pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
@@ -135,15 +142,26 @@ pub const Instruction = union(enum) {
offset: u12,
rd: u4,
rn: u4,
- l: u1,
- w: u1,
- b: u1,
- u: u1,
- p: u1,
- i: u1,
+ load_store: u1,
+ write_back: u1,
+ byte_word: u1,
+ up_down: u1,
+ pre_post: u1,
+ imm: u1,
fixed: u2 = 0b01,
cond: u4,
},
+ BlockDataTransfer: packed struct {
+ register_list: u16,
+ rn: u4,
+ load_store: u1,
+ write_back: u1,
+ psr_or_user: u1,
+ up_down: u1,
+ pre_post: u1,
+ fixed: u3 = 0b100,
+ cond: u4,
+ },
Branch: packed struct {
offset: u24,
link: u1,
@@ -235,14 +253,14 @@ pub const Instruction = union(enum) {
rs: u4,
},
- const Type = enum(u2) {
- LogicalLeft,
- LogicalRight,
- ArithmeticRight,
- RotateRight,
+ pub const Type = enum(u2) {
+ logical_left,
+ logical_right,
+ arithmetic_right,
+ rotate_right,
};
- const none = Shift{
+ pub const none = Shift{
.Immediate = .{
.amount = 0,
.typ = 0,
@@ -338,10 +356,32 @@ pub const Instruction = union(enum) {
}
};
+ /// Represents the register list operand to a block data transfer
+ /// instruction
+ pub const RegisterList = packed struct {
+ r0: bool = false,
+ r1: bool = false,
+ r2: bool = false,
+ r3: bool = false,
+ r4: bool = false,
+ r5: bool = false,
+ r6: bool = false,
+ r7: bool = false,
+ r8: bool = false,
+ r9: bool = false,
+ r10: bool = false,
+ r11: bool = false,
+ r12: bool = false,
+ r13: bool = false,
+ r14: bool = false,
+ r15: bool = false,
+ };
+
pub fn toU32(self: Instruction) u32 {
return switch (self) {
.DataProcessing => |v| @bitCast(u32, v),
.SingleDataTransfer => |v| @bitCast(u32, v),
+ .BlockDataTransfer => |v| @bitCast(u32, v),
.Branch => |v| @bitCast(u32, v),
.BranchExchange => |v| @bitCast(u32, v),
.SupervisorCall => |v| @bitCast(u32, v),
@@ -362,7 +402,7 @@ pub const Instruction = union(enum) {
return Instruction{
.DataProcessing = .{
.cond = @enumToInt(cond),
- .i = if (op2 == .Immediate) 1 else 0,
+ .i = @boolToInt(op2 == .Immediate),
.opcode = @enumToInt(opcode),
.s = s,
.rn = rn.id(),
@@ -377,10 +417,10 @@ pub const Instruction = union(enum) {
rd: Register,
rn: Register,
offset: Offset,
- pre_post: u1,
- up_down: u1,
+ pre_index: bool,
+ positive: bool,
byte_word: u1,
- writeback: u1,
+ write_back: bool,
load_store: u1,
) Instruction {
return Instruction{
@@ -389,12 +429,36 @@ pub const Instruction = union(enum) {
.rn = rn.id(),
.rd = rd.id(),
.offset = offset.toU12(),
- .l = load_store,
- .w = writeback,
- .b = byte_word,
- .u = up_down,
- .p = pre_post,
- .i = if (offset == .Immediate) 0 else 1,
+ .load_store = load_store,
+ .write_back = @boolToInt(write_back),
+ .byte_word = byte_word,
+ .up_down = @boolToInt(positive),
+ .pre_post = @boolToInt(pre_index),
+ .imm = @boolToInt(offset != .Immediate),
+ },
+ };
+ }
+
+ fn blockDataTransfer(
+ cond: Condition,
+ rn: Register,
+ reg_list: RegisterList,
+ pre_post: u1,
+ up_down: u1,
+ psr_or_user: u1,
+ write_back: bool,
+ load_store: u1,
+ ) Instruction {
+ return Instruction{
+ .BlockDataTransfer = .{
+ .register_list = @bitCast(u16, reg_list),
+ .rn = rn.id(),
+ .load_store = load_store,
+ .write_back = @boolToInt(write_back),
+ .psr_or_user = psr_or_user,
+ .up_down = up_down,
+ .pre_post = pre_post,
+ .cond = @enumToInt(cond),
},
};
}
@@ -442,36 +506,68 @@ pub const Instruction = union(enum) {
// Data processing
- pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .@"and", s, rd, rn, op2);
+ pub fn @"and"(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .@"and", 0, rd, rn, op2);
+ }
+
+ pub fn ands(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .@"and", 1, rd, rn, op2);
+ }
+
+ pub fn eor(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .eor, 0, rd, rn, op2);
+ }
+
+ pub fn eors(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .eor, 1, rd, rn, op2);
+ }
+
+ pub fn sub(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .sub, 0, rd, rn, op2);
+ }
+
+ pub fn subs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .sub, 1, rd, rn, op2);
+ }
+
+ pub fn rsb(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .rsb, 0, rd, rn, op2);
+ }
+
+ pub fn rsbs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .rsb, 1, rd, rn, op2);
+ }
+
+ pub fn add(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .add, 0, rd, rn, op2);
}
- pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .eor, s, rd, rn, op2);
+ pub fn adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .add, 1, rd, rn, op2);
}
- pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .sub, s, rd, rn, op2);
+ pub fn adc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .adc, 0, rd, rn, op2);
}
- pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .rsb, s, rd, rn, op2);
+ pub fn adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .adc, 1, rd, rn, op2);
}
- pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .add, s, rd, rn, op2);
+ pub fn sbc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .sbc, 0, rd, rn, op2);
}
- pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .adc, s, rd, rn, op2);
+ pub fn sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .sbc, 1, rd, rn, op2);
}
- pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .sbc, s, rd, rn, op2);
+ pub fn rsc(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .rsc, 0, rd, rn, op2);
}
- pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .rsc, s, rd, rn, op2);
+ pub fn rscs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .rsc, 1, rd, rn, op2);
}
pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction {
@@ -490,32 +586,115 @@ pub const Instruction = union(enum) {
return dataProcessing(cond, .cmn, 1, .r0, rn, op2);
}
- pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .orr, s, rd, rn, op2);
+ pub fn orr(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .orr, 0, rd, rn, op2);
+ }
+
+ pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .orr, 1, rd, rn, op2);
+ }
+
+ pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .mov, 0, rd, .r0, op2);
}
- pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .mov, s, rd, .r0, op2);
+ pub fn movs(cond: Condition, rd: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .mov, 1, rd, .r0, op2);
}
- pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .bic, s, rd, rn, op2);
+ pub fn bic(cond: Condition, rd: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .bic, 0, rd, rn, op2);
}
- pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
- return dataProcessing(cond, .mvn, s, rd, .r0, op2);
+ pub fn bics(cond: Condition, rd: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .bic, 1, rd, rn, op2);
+ }
+
+ pub fn mvn(cond: Condition, rd: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .mvn, 0, rd, .r0, op2);
+ }
+
+ pub fn mvns(cond: Condition, rd: Register, op2: Operand) Instruction {
+ return dataProcessing(cond, .mvn, 1, rd, .r0, op2);
+ }
+
+ // PSR transfer
+
+ pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction {
+ return dataProcessing(cond, if (psr == .cpsr) .tst else .cmp, 0, rd, .r15, Operand.reg(.r0, Operand.Shift.none));
}
// Single data transfer
- pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
- return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1);
+ pub const OffsetArgs = struct {
+ pre_index: bool = true,
+ positive: bool = true,
+ offset: Offset,
+ write_back: bool = false,
+ };
+
+ pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
+ return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1);
+ }
+
+ pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
+ return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1);
+ }
+
+ pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
+ return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0);
+ }
+
+ pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction {
+ return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0);
+ }
+
+ // Block data transfer
+
+ pub fn ldmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
+ return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 1);
+ }
+
+ pub fn ldmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
+ return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 1);
}
- pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
- return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0);
+ pub fn ldmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
+ return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 1);
}
+ pub fn ldmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
+ return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 1);
+ }
+
+ pub const ldmfa = ldmda;
+ pub const ldmea = ldmdb;
+ pub const ldmed = ldmib;
+ pub const ldmfd = ldmia;
+ pub const ldm = ldmia;
+
+ pub fn stmda(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
+ return blockDataTransfer(cond, rn, reg_list, 0, 0, 0, write_back, 0);
+ }
+
+ pub fn stmdb(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
+ return blockDataTransfer(cond, rn, reg_list, 1, 0, 0, write_back, 0);
+ }
+
+ pub fn stmib(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
+ return blockDataTransfer(cond, rn, reg_list, 1, 1, 0, write_back, 0);
+ }
+
+ pub fn stmia(cond: Condition, rn: Register, write_back: bool, reg_list: RegisterList) Instruction {
+ return blockDataTransfer(cond, rn, reg_list, 0, 1, 0, write_back, 0);
+ }
+
+ pub const stmed = stmda;
+ pub const stmfd = stmdb;
+ pub const stmfa = stmib;
+ pub const stmea = stmia;
+ pub const stm = stmia;
+
// Branch
pub fn b(cond: Condition, offset: i24) Instruction {
@@ -549,6 +728,58 @@ pub const Instruction = union(enum) {
pub fn bkpt(imm: u16) Instruction {
return breakpoint(imm);
}
+
+ // Aliases
+
+ pub fn pop(cond: Condition, args: anytype) Instruction {
+ if (@typeInfo(@TypeOf(args)) != .Struct) {
+ @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
+ }
+
+ if (args.len < 1) {
+ @compileError("Expected at least one register");
+ } else if (args.len == 1) {
+ const reg = args[0];
+ return ldr(cond, reg, .sp, .{
+ .pre_index = false,
+ .positive = true,
+ .offset = Offset.imm(4),
+ .write_back = false,
+ });
+ } else {
+ var register_list: u16 = 0;
+ inline for (args) |arg| {
+ const reg = @as(Register, arg);
+ register_list |= @as(u16, 1) << reg.id();
+ }
+ return ldm(cond, .sp, true, @bitCast(RegisterList, register_list));
+ }
+ }
+
+ pub fn push(cond: Condition, args: anytype) Instruction {
+ if (@typeInfo(@TypeOf(args)) != .Struct) {
+ @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
+ }
+
+ if (args.len < 1) {
+ @compileError("Expected at least one register");
+ } else if (args.len == 1) {
+ const reg = args[0];
+ return str(cond, reg, .sp, .{
+ .pre_index = true,
+ .positive = false,
+ .offset = Offset.imm(4),
+ .write_back = true,
+ });
+ } else {
+ var register_list: u16 = 0;
+ inline for (args) |arg| {
+ const reg = @as(Register, arg);
+ register_list |= @as(u16, 1) << reg.id();
+ }
+ return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list));
+ }
+ }
};
test "serialize instructions" {
@@ -559,23 +790,31 @@ test "serialize instructions" {
const testcases = [_]Testcase{
.{ // add r0, r0, r0
- .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
+ .inst = Instruction.add(.al, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)),
.expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000,
},
.{ // mov r4, r2
- .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
+ .inst = Instruction.mov(.al, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)),
.expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010,
},
.{ // mov r0, #42
- .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)),
+ .inst = Instruction.mov(.al, .r0, Instruction.Operand.imm(42, 0)),
.expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010,
},
+ .{ // mrs r5, cpsr
+ .inst = Instruction.mrs(.al, .r5, .cpsr),
+ .expected = 0b1110_00010_0_001111_0101_000000000000,
+ },
.{ // ldr r0, [r2, #42]
- .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)),
+ .inst = Instruction.ldr(.al, .r0, .r2, .{
+ .offset = Instruction.Offset.imm(42),
+ }),
.expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010,
},
.{ // str r0, [r3]
- .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none),
+ .inst = Instruction.str(.al, .r0, .r3, .{
+ .offset = Instruction.Offset.none,
+ }),
.expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000,
},
.{ // b #12
@@ -598,6 +837,14 @@ test "serialize instructions" {
.inst = Instruction.bkpt(42),
.expected = 0b1110_0001_0010_000000000010_0111_1010,
},
+ .{ // stmdb r9, {r0}
+ .inst = Instruction.stmdb(.al, .r9, false, .{ .r0 = true }),
+ .expected = 0b1110_100_1_0_0_0_0_1001_0000000000000001,
+ },
+ .{ // ldmea r4!, {r2, r5}
+ .inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }),
+ .expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100,
+ },
};
for (testcases) |case| {
@@ -605,3 +852,43 @@ test "serialize instructions" {
testing.expectEqual(case.expected, actual);
}
}
+
+test "aliases" {
+ const Testcase = struct {
+ expected: Instruction,
+ actual: Instruction,
+ };
+
+ const testcases = [_]Testcase{
+ .{ // pop { r6 }
+ .actual = Instruction.pop(.al, .{.r6}),
+ .expected = Instruction.ldr(.al, .r6, .sp, .{
+ .pre_index = false,
+ .positive = true,
+ .offset = Instruction.Offset.imm(4),
+ .write_back = false,
+ }),
+ },
+ .{ // pop { r1, r5 }
+ .actual = Instruction.pop(.al, .{ .r1, .r5 }),
+ .expected = Instruction.ldm(.al, .sp, true, .{ .r1 = true, .r5 = true }),
+ },
+ .{ // push { r3 }
+ .actual = Instruction.push(.al, .{.r3}),
+ .expected = Instruction.str(.al, .r3, .sp, .{
+ .pre_index = true,
+ .positive = false,
+ .offset = Instruction.Offset.imm(4),
+ .write_back = true,
+ }),
+ },
+ .{ // push { r0, r2 }
+ .actual = Instruction.push(.al, .{ .r0, .r2 }),
+ .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }),
+ },
+ };
+
+ for (testcases) |case| {
+ testing.expectEqual(case.expected.toU32(), case.actual.toU32());
+ }
+}
diff --git a/src/glibc.zig b/src/glibc.zig
@@ -689,9 +689,6 @@ pub const BuiltSharedObjects = struct {
const all_map_basename = "all.map";
-// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
-// zig fmt: off
-
pub fn buildSharedObjects(comp: *Compilation) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -827,8 +824,9 @@ pub fn buildSharedObjects(comp: *Compilation) !void {
if (ver.patch == 0) {
const sym_plus_ver = try std.fmt.allocPrint(
- arena, "{s}_{d}_{d}",
- .{sym_name, ver.major, ver.minor},
+ arena,
+ "{s}_{d}_{d}",
+ .{ sym_name, ver.major, ver.minor },
);
try zig_body.writer().print(
\\.globl {s}
@@ -840,13 +838,19 @@ pub fn buildSharedObjects(comp: *Compilation) !void {
, .{
sym_plus_ver,
sym_plus_ver,
- sym_plus_ver, sym_name, at_sign_str, ver.major, ver.minor,
+ sym_plus_ver,
+ sym_name,
+ at_sign_str,
+ ver.major,
+ ver.minor,
sym_plus_ver,
sym_plus_ver,
});
} else {
- const sym_plus_ver = try std.fmt.allocPrint(arena, "{s}_{d}_{d}_{d}",
- .{sym_name, ver.major, ver.minor, ver.patch},
+ const sym_plus_ver = try std.fmt.allocPrint(
+ arena,
+ "{s}_{d}_{d}_{d}",
+ .{ sym_name, ver.major, ver.minor, ver.patch },
);
try zig_body.writer().print(
\\.globl {s}
@@ -858,7 +862,12 @@ pub fn buildSharedObjects(comp: *Compilation) !void {
, .{
sym_plus_ver,
sym_plus_ver,
- sym_plus_ver, sym_name, at_sign_str, ver.major, ver.minor, ver.patch,
+ sym_plus_ver,
+ sym_name,
+ at_sign_str,
+ ver.major,
+ ver.minor,
+ ver.patch,
sym_plus_ver,
sym_plus_ver,
});
diff --git a/src/link.zig b/src/link.zig
@@ -45,6 +45,7 @@ pub const Options = struct {
program_code_size_hint: u64 = 256 * 1024,
entry_addr: ?u64 = null,
stack_size_override: ?u64,
+ image_base_override: ?u64,
/// Set to `true` to omit debug info.
strip: bool,
/// If this is true then this link code is responsible for outputting an object
@@ -60,6 +61,7 @@ pub const Options = struct {
link_libcpp: bool,
function_sections: bool,
eh_frame_hdr: bool,
+ emit_relocs: bool,
rdynamic: bool,
z_nodelete: bool,
z_defs: bool,
diff --git a/src/link/Coff.zig b/src/link/Coff.zig
@@ -22,10 +22,10 @@ const minimum_text_block_size = 64 * allocation_padding;
const section_alignment = 4096;
const file_alignment = 512;
-const image_base = 0x400_000;
+const default_image_base = 0x400_000;
const section_table_size = 2 * 40;
comptime {
- assert(mem.isAligned(image_base, section_alignment));
+ assert(mem.isAligned(default_image_base, section_alignment));
}
pub const base_tag: link.File.Tag = .coff;
@@ -55,7 +55,7 @@ offset_table: std.ArrayListUnmanaged(u64) = .{},
/// Free list of offset table indices
offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
-/// Virtual address of the entry point procedure relative to `image_base`
+/// Virtual address of the entry point procedure relative to image base.
entry_addr: ?u32 = null,
/// 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
self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
- self.offset_table_virtual_address = image_base + section_data_relative_virtual_address;
+ self.offset_table_virtual_address = default_image_base + section_data_relative_virtual_address;
self.offset_table_size = default_offset_table_size;
self.section_table_offset = section_table_offset;
- self.text_section_virtual_address = image_base + section_data_relative_virtual_address + section_alignment;
+ self.text_section_virtual_address = default_image_base + section_data_relative_virtual_address + section_alignment;
self.text_section_size = default_size_of_code;
// Size of file when loaded in memory
- const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment);
+ const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + default_size_of_code, section_alignment);
mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
index += 2;
@@ -234,11 +234,11 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
index += 4;
// Image base address
- mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], default_image_base);
index += 4;
} else {
// Image base address
- mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base);
+ mem.writeIntLittle(u64, hdr_data[index..][0..8], default_image_base);
index += 8;
}
@@ -328,7 +328,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
index += 4;
// Virtual address (u32)
- mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - default_image_base);
index += 4;
} else {
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
mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
index += 4;
// Virtual address (u32)
- mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base);
+ mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - default_image_base);
index += 4;
} else {
mem.set(u8, hdr_data[index..][0..8], 0);
@@ -601,7 +601,7 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
// Write .text new virtual address
self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
- mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base);
+ mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - default_image_base);
try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
// Fix the VAs in the offset table
@@ -716,7 +716,7 @@ pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl,
}
}
if (mem.eql(u8, exp.options.name, "_start")) {
- self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base;
+ self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base;
} else {
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
module.failed_exports.putAssumeCapacityNoClobber(
@@ -754,7 +754,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
}
if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
- const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment);
+ const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + self.text_section_size, section_alignment);
var buf: [4]u8 = undefined;
mem.writeIntLittle(u32, &buf, new_size_of_image);
try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
@@ -832,6 +832,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
}
try man.addOptionalFile(module_obj_path);
man.hash.addOptional(self.base.options.stack_size_override);
+ man.hash.addOptional(self.base.options.image_base_override);
man.hash.addListOfBytes(self.base.options.extra_lld_args);
man.hash.addListOfBytes(self.base.options.lib_dirs);
man.hash.add(self.base.options.is_compiler_rt_or_libc);
@@ -914,6 +915,9 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
const stack_size = self.base.options.stack_size_override orelse 16777216;
try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
}
+ if (self.base.options.image_base_override) |image_base| {
+ try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base}));
+ }
if (target.cpu.arch == .i386) {
try argv.append("-MACHINE:X86");
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
@@ -27,9 +27,6 @@ const Cache = @import("../Cache.zig");
const default_entry_addr = 0x8000000;
-// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
-// zig fmt: off
-
pub const base_tag: File.Tag = .elf;
base: File,
@@ -273,8 +270,8 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
- 0 ... 32 => .p32,
- 33 ... 64 => .p64,
+ 0...32 => .p32,
+ 33...64 => .p64,
else => return error.UnsupportedELFArchitecture,
};
const self = try gpa.create(Elf);
@@ -752,40 +749,52 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
// These are LEB encoded but since the values are all less than 127
// we can simply append these bytes.
const abbrev_buf = [_]u8{
- abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header
- DW.AT_stmt_list, DW.FORM_sec_offset, DW.AT_low_pc,
- DW.FORM_addr, DW.AT_high_pc, DW.FORM_addr,
- DW.AT_name, DW.FORM_strp, DW.AT_comp_dir,
- DW.FORM_strp, DW.AT_producer, DW.FORM_strp,
- DW.AT_language, DW.FORM_data2, 0,
+ abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header
+ DW.AT_stmt_list, DW.FORM_sec_offset, DW.AT_low_pc,
+ DW.FORM_addr, DW.AT_high_pc, DW.FORM_addr,
+ DW.AT_name, DW.FORM_strp, DW.AT_comp_dir,
+ DW.FORM_strp, DW.AT_producer, DW.FORM_strp,
+ DW.AT_language, DW.FORM_data2, 0,
0, // table sentinel
- abbrev_subprogram, DW.TAG_subprogram,
+ abbrev_subprogram,
+ DW.TAG_subprogram,
DW.CHILDREN_yes, // header
- DW.AT_low_pc, DW.FORM_addr,
- DW.AT_high_pc, DW.FORM_data4, DW.AT_type,
- DW.FORM_ref4, DW.AT_name, DW.FORM_string,
- 0, 0, // table sentinel
- abbrev_subprogram_retvoid,
- DW.TAG_subprogram, DW.CHILDREN_yes, // header
- DW.AT_low_pc,
- DW.FORM_addr, DW.AT_high_pc, DW.FORM_data4,
- DW.AT_name, DW.FORM_string, 0,
+ DW.AT_low_pc,
+ DW.FORM_addr,
+ DW.AT_high_pc,
+ DW.FORM_data4,
+ DW.AT_type,
+ DW.FORM_ref4,
+ DW.AT_name,
+ DW.FORM_string,
+ 0, 0, // table sentinel
+ abbrev_subprogram_retvoid,
+ DW.TAG_subprogram, DW.CHILDREN_yes, // header
+ DW.AT_low_pc, DW.FORM_addr,
+ DW.AT_high_pc, DW.FORM_data4,
+ DW.AT_name, DW.FORM_string,
+ 0,
0, // table sentinel
- abbrev_base_type, DW.TAG_base_type,
+ abbrev_base_type,
+ DW.TAG_base_type,
DW.CHILDREN_no, // header
- DW.AT_encoding, DW.FORM_data1,
- DW.AT_byte_size, DW.FORM_data1, DW.AT_name,
- DW.FORM_string, 0, 0, // table sentinel
-
- abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header
- 0, 0, // table sentinel
- abbrev_parameter,
+ DW.AT_encoding,
+ DW.FORM_data1,
+ DW.AT_byte_size,
+ DW.FORM_data1,
+ DW.AT_name,
+ DW.FORM_string, 0, 0, // table sentinel
+ abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header
+ 0, 0, // table sentinel
+ abbrev_parameter,
DW.TAG_formal_parameter, DW.CHILDREN_no, // header
- DW.AT_location,
- DW.FORM_exprloc, DW.AT_type, DW.FORM_ref4,
- DW.AT_name, DW.FORM_string, 0,
+ DW.AT_location, DW.FORM_exprloc,
+ DW.AT_type, DW.FORM_ref4,
+ DW.AT_name, DW.FORM_string,
+ 0,
0, // table sentinel
- 0, 0,
+ 0,
+ 0,
0, // section sentinel
};
@@ -1021,7 +1030,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
0, // `DW.LNS_set_prologue_end`
0, // `DW.LNS_set_epilogue_begin`
1, // `DW.LNS_set_isa`
-
0, // include_directories (none except the compilation unit cwd)
});
// file_names[0]
@@ -1284,8 +1292,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
// We can skip hashing libc and libc++ components that we are in charge of building from Zig
// installation sources because they are always a product of the compiler version + target information.
man.hash.add(stack_size);
+ man.hash.addOptional(self.base.options.image_base_override);
man.hash.add(gc_sections);
man.hash.add(self.base.options.eh_frame_hdr);
+ man.hash.add(self.base.options.emit_relocs);
man.hash.add(self.base.options.rdynamic);
man.hash.addListOfBytes(self.base.options.extra_lld_args);
man.hash.addListOfBytes(self.base.options.lib_dirs);
@@ -1317,7 +1327,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
var prev_digest_buf: [digest.len]u8 = undefined;
const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: {
- log.debug("ELF LLD new_digest={} readlink error: {}", .{digest, @errorName(err)});
+ log.debug("ELF LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) });
// Handle this as a cache miss.
break :blk prev_digest_buf[0..0];
};
@@ -1327,7 +1337,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
self.base.lock = man.toOwnedLock();
return;
}
- log.debug("ELF LLD prev_digest={} new_digest={}", .{prev_digest, digest});
+ log.debug("ELF LLD prev_digest={} new_digest={}", .{ prev_digest, digest });
// We are about to change the output file to be different, so we invalidate the build hash now.
directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
@@ -1352,6 +1362,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size}));
}
+ if (self.base.options.image_base_override) |image_base| {
+ try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base}));
+ }
+
if (self.base.options.linker_script) |linker_script| {
try argv.append("-T");
try argv.append(linker_script);
@@ -1365,6 +1379,10 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
try argv.append("--eh-frame-hdr");
}
+ if (self.base.options.emit_relocs) {
+ try argv.append("--emit-relocs");
+ }
+
if (self.base.options.rdynamic) {
try argv.append("--export-dynamic");
}
@@ -1443,10 +1461,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
var test_path = std.ArrayList(u8).init(self.base.allocator);
defer test_path.deinit();
for (self.base.options.lib_dirs) |lib_dir_path| {
- for (self.base.options.system_libs.items()) |link_lib| {
+ for (self.base.options.system_libs.items()) |entry| {
+ const link_lib = entry.key;
test_path.shrinkRetainingCapacity(0);
const sep = fs.path.sep_str;
- try test_path.writer().print("{}" ++ sep ++ "lib{}.so", .{ lib_dir_path, link_lib });
+ try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{ lib_dir_path, link_lib });
fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
error.FileNotFound => continue,
else => |e| return e,
@@ -1480,9 +1499,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
if (is_dyn_lib) {
const soname = self.base.options.override_soname orelse if (self.base.options.version) |ver|
- try std.fmt.allocPrint(arena, "lib{}.so.{}", .{self.base.options.root_name, ver.major})
- else
- try std.fmt.allocPrint(arena, "lib{}.so", .{self.base.options.root_name});
+ try std.fmt.allocPrint(arena, "lib{}.so.{}", .{ self.base.options.root_name, ver.major })
+ else
+ try std.fmt.allocPrint(arena, "lib{}.so", .{self.base.options.root_name});
try argv.append("-soname");
try argv.append(soname);
@@ -1605,7 +1624,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
};
defer stdout_context.data.deinit();
const llvm = @import("../llvm.zig");
- const ok = llvm.Link(.ELF, new_argv.ptr, new_argv.len, append_diagnostic,
+ const ok = llvm.Link(
+ .ELF,
+ new_argv.ptr,
+ new_argv.len,
+ append_diagnostic,
@ptrToInt(&stdout_context),
@ptrToInt(&stderr_context),
);
@@ -1631,7 +1654,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
};
// Again failure here only means an unnecessary cache miss.
man.writeManifest() catch |err| {
- std.log.warn("failed to write cache manifest when linking: {}", .{ @errorName(err) });
+ std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)});
};
// We hang on to this lock so that the output file path can be used without
// other processes clobbering it.
@@ -2866,8 +2889,8 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 {
const root_src_dir_path_len = if (self.base.options.module.?.root_pkg.root_src_directory.path) |p| p.len else 1; // "."
return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
directory_count * 8 + file_name_count * 8 +
- // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
- // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
+ // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
+ // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
root_src_dir_path_len +
self.base.options.module.?.root_pkg.root_src_path.len);
}
@@ -2888,7 +2911,7 @@ fn pwriteDbgLineNops(
prev_padding_size: usize,
buf: []const u8,
next_padding_size: usize,
- offset: usize,
+ offset: u64,
) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -2967,7 +2990,7 @@ fn pwriteDbgInfoNops(
buf: []const u8,
next_padding_size: usize,
trailing_zero: bool,
- offset: usize,
+ offset: u64,
) !void {
const tracy = trace(@src());
defer tracy.end();
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
@@ -27,6 +27,10 @@ const LoadCommand = union(enum) {
LinkeditData: macho.linkedit_data_command,
Symtab: macho.symtab_command,
Dysymtab: macho.dysymtab_command,
+ DyldInfo: macho.dyld_info_command,
+ Dylinker: macho.dylinker_command,
+ Dylib: macho.dylib_command,
+ EntryPoint: macho.entry_point_command,
pub fn cmdsize(self: LoadCommand) u32 {
return switch (self) {
@@ -34,6 +38,10 @@ const LoadCommand = union(enum) {
.LinkeditData => |x| x.cmdsize,
.Symtab => |x| x.cmdsize,
.Dysymtab => |x| x.cmdsize,
+ .DyldInfo => |x| x.cmdsize,
+ .Dylinker => |x| x.cmdsize,
+ .Dylib => |x| x.cmdsize,
+ .EntryPoint => |x| x.cmdsize,
};
}
@@ -43,6 +51,10 @@ const LoadCommand = union(enum) {
.LinkeditData => |cmd| writeGeneric(cmd, file, offset),
.Symtab => |cmd| writeGeneric(cmd, file, offset),
.Dysymtab => |cmd| writeGeneric(cmd, file, offset),
+ .DyldInfo => |cmd| writeGeneric(cmd, file, offset),
+ .Dylinker => |cmd| writeGeneric(cmd, file, offset),
+ .Dylib => |cmd| writeGeneric(cmd, file, offset),
+ .EntryPoint => |cmd| writeGeneric(cmd, file, offset),
};
}
@@ -56,30 +68,52 @@ base: File,
/// Table of all load commands
load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
-segment_cmd_index: ?u16 = null,
+/// __PAGEZERO segment
+pagezero_segment_cmd_index: ?u16 = null,
+/// __TEXT segment
+text_segment_cmd_index: ?u16 = null,
+/// __DATA segment
+data_segment_cmd_index: ?u16 = null,
+/// __LINKEDIT segment
+linkedit_segment_cmd_index: ?u16 = null,
+/// Dyld info
+dyld_info_cmd_index: ?u16 = null,
+/// Symbol table
symtab_cmd_index: ?u16 = null,
+/// Dynamic symbol table
dysymtab_cmd_index: ?u16 = null,
+/// Path to dyld linker
+dylinker_cmd_index: ?u16 = null,
+/// Path to libSystem
+libsystem_cmd_index: ?u16 = null,
+/// Data-in-code section of __LINKEDIT segment
data_in_code_cmd_index: ?u16 = null,
+/// Address to entry point function
+function_starts_cmd_index: ?u16 = null,
+/// Main/entry point
+/// Specifies offset wrt __TEXT segment start address to the main entry point
+/// of the binary.
+main_cmd_index: ?u16 = null,
/// Table of all sections
sections: std.ArrayListUnmanaged(macho.section_64) = .{},
-/// __TEXT segment sections
+/// __TEXT,__text section
text_section_index: ?u16 = null,
-cstring_section_index: ?u16 = null,
-const_text_section_index: ?u16 = null,
-stubs_section_index: ?u16 = null,
-stub_helper_section_index: ?u16 = null,
-/// __DATA segment sections
+/// __DATA,__got section
got_section_index: ?u16 = null,
-const_data_section_index: ?u16 = null,
entry_addr: ?u64 = null,
-/// Table of all symbols used.
+/// Table of all local symbols
/// Internally references string table for names (which are optional).
-symbol_table: std.ArrayListUnmanaged(macho.nlist_64) = .{},
+local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
+/// Table of all defined global symbols
+global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
+/// Table of all undefined symbols
+undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
+dyld_stub_binder_index: ?u16 = null,
/// Table of symbol names aka the string table.
string_table: std.ArrayListUnmanaged(u8) = .{},
@@ -115,19 +149,27 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
pub const TextBlock = struct {
- /// Index into the symbol table
- symbol_table_index: ?u32,
+ /// Each decl always gets a local symbol with the fully qualified name.
+ /// The vaddr and size are found here directly.
+ /// The file offset is found by computing the vaddr offset from the section vaddr
+ /// the symbol references, and adding that to the file offset of the section.
+ /// If this field is 0, it means the codegen size = 0 and there is no symbol or
+ /// offset table entry.
+ local_sym_index: u32,
/// Index into offset table
- offset_table_index: ?u32,
+ /// This field is undefined for symbols with size = 0.
+ offset_table_index: u32,
/// Size of this text block
+ /// Unlike in Elf, we need to store the size of this symbol as part of
+ /// the TextBlock since macho.nlist_64 lacks this information.
size: u64,
/// Points to the previous and next neighbours
prev: ?*TextBlock,
next: ?*TextBlock,
pub const empty = TextBlock{
- .symbol_table_index = null,
- .offset_table_index = null,
+ .local_sym_index = 0,
+ .offset_table_index = undefined,
.size = 0,
.prev = null,
.next = null,
@@ -156,6 +198,15 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
self.base.file = file;
+ // Index 0 is always a null symbol.
+ try self.local_symbols.append(allocator, .{
+ .n_strx = 0,
+ .n_type = 0,
+ .n_sect = 0,
+ .n_desc = 0,
+ .n_value = 0,
+ });
+
switch (options.output_mode) {
.Exe => {},
.Obj => {},
@@ -196,88 +247,83 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
const tracy = trace(@src());
defer tracy.end();
+ // Unfortunately these have to be buffered and done at the end because MachO does not allow
+ // mixing local, global and undefined symbols within a symbol table.
+ try self.writeAllGlobalSymbols();
+ try self.writeAllUndefSymbols();
+
+ try self.writeStringTable();
+
switch (self.base.options.output_mode) {
.Exe => {
- var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
- {
- // Specify path to dynamic linker dyld
- const cmdsize = commandSize(@sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH));
- const load_dylinker = [1]macho.dylinker_command{
- .{
- .cmd = macho.LC_LOAD_DYLINKER,
- .cmdsize = cmdsize,
- .name = @sizeOf(macho.dylinker_command),
- },
- };
-
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylinker[0..1]), last_cmd_offset);
-
- const file_offset = last_cmd_offset + @sizeOf(macho.dylinker_command);
- try self.addPadding(cmdsize - @sizeOf(macho.dylinker_command), file_offset);
-
- try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), file_offset);
- last_cmd_offset += cmdsize;
+ if (self.entry_addr) |addr| {
+ // Write export trie.
+ try self.writeExportTrie();
+
+ // Update LC_MAIN with entry offset
+ const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+ const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint;
+ main_cmd.entryoff = addr - text_segment.vmaddr;
}
{
- // Link against libSystem
- const cmdsize = commandSize(@sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH));
- // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
- // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.
- const min_version = 0x10000;
- const dylib = .{
- .name = @sizeOf(macho.dylib_command),
- .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
- .current_version = min_version,
- .compatibility_version = min_version,
- };
- const load_dylib = [1]macho.dylib_command{
- .{
- .cmd = macho.LC_LOAD_DYLIB,
- .cmdsize = cmdsize,
- .dylib = dylib,
- },
- };
-
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(load_dylib[0..1]), last_cmd_offset);
-
- const file_offset = last_cmd_offset + @sizeOf(macho.dylib_command);
- try self.addPadding(cmdsize - @sizeOf(macho.dylib_command), file_offset);
-
- try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), file_offset);
- last_cmd_offset += cmdsize;
+ // Update dynamic symbol table.
+ const nlocals = @intCast(u32, self.local_symbols.items.len);
+ const nglobals = @intCast(u32, self.global_symbols.items.len);
+ const nundefs = @intCast(u32, self.undef_symbols.items.len);
+ const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
+ dysymtab.nlocalsym = nlocals;
+ dysymtab.iextdefsym = nlocals;
+ dysymtab.nextdefsym = nglobals;
+ dysymtab.iundefsym = nlocals + nglobals;
+ dysymtab.nundefsym = nundefs;
}
- },
- .Obj => {
{
- const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
- symtab.nsyms = @intCast(u32, self.symbol_table.items.len);
- const allocated_size = self.allocatedSize(symtab.stroff);
- const needed_size = self.string_table.items.len;
- log.debug("allocated_size = 0x{x}, needed_size = 0x{x}\n", .{ allocated_size, needed_size });
-
- if (needed_size > allocated_size) {
- symtab.strsize = 0;
- symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1));
+ // Write path to dyld loader.
+ var off: usize = @sizeOf(macho.mach_header_64);
+ for (self.load_commands.items) |cmd| {
+ if (cmd == .Dylinker) break;
+ off += cmd.cmdsize();
}
- symtab.strsize = @intCast(u32, needed_size);
-
- log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize });
-
- try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);
+ const cmd = &self.load_commands.items[self.dylinker_cmd_index.?].Dylinker;
+ off += cmd.name;
+ const padding = cmd.cmdsize - @sizeOf(macho.dylinker_command);
+ log.debug("writing LC_LOAD_DYLINKER padding of size {} at 0x{x}\n", .{ padding, off });
+ try self.addPadding(padding, off);
+ log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off});
+ try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off);
}
-
- var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
- for (self.load_commands.items) |cmd| {
- try cmd.write(&self.base.file.?, last_cmd_offset);
- last_cmd_offset += cmd.cmdsize();
+ {
+ // Write path to libSystem.
+ var off: usize = @sizeOf(macho.mach_header_64);
+ for (self.load_commands.items) |cmd| {
+ if (cmd == .Dylib) break;
+ off += cmd.cmdsize();
+ }
+ const cmd = &self.load_commands.items[self.libsystem_cmd_index.?].Dylib;
+ off += cmd.dylib.name;
+ const padding = cmd.cmdsize - @sizeOf(macho.dylib_command);
+ log.debug("writing LC_LOAD_DYLIB padding of size {} at 0x{x}\n", .{ padding, off });
+ try self.addPadding(padding, off);
+ log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off});
+ try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off);
}
- const off = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
- try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items), off);
},
+ .Obj => {},
.Lib => return error.TODOImplementWritingLibFiles,
}
+ if (self.cmd_table_dirty) try self.writeCmdHeaders();
+
+ {
+ // Update symbol table.
+ const nlocals = @intCast(u32, self.local_symbols.items.len);
+ const nglobals = @intCast(u32, self.global_symbols.items.len);
+ const nundefs = @intCast(u32, self.undef_symbols.items.len);
+ const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
+ symtab.nsyms = nlocals + nglobals + nundefs;
+ }
+
if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
log.debug("flushing. no_entry_point_found = true\n", .{});
self.error_flags.no_entry_point_found = true;
@@ -669,32 +715,34 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
pub fn deinit(self: *MachO) void {
self.offset_table.deinit(self.base.allocator);
self.string_table.deinit(self.base.allocator);
- self.symbol_table.deinit(self.base.allocator);
+ self.undef_symbols.deinit(self.base.allocator);
+ self.global_symbols.deinit(self.base.allocator);
+ self.local_symbols.deinit(self.base.allocator);
self.sections.deinit(self.base.allocator);
self.load_commands.deinit(self.base.allocator);
}
pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
- if (decl.link.macho.symbol_table_index) |_| return;
+ if (decl.link.macho.local_sym_index != 0) return;
- try self.symbol_table.ensureCapacity(self.base.allocator, self.symbol_table.items.len + 1);
+ try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
- log.debug("allocating symbol index {} for {}\n", .{ self.symbol_table.items.len, decl.name });
- decl.link.macho.symbol_table_index = @intCast(u32, self.symbol_table.items.len);
- _ = self.symbol_table.addOneAssumeCapacity();
+ log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
+ decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len);
+ _ = self.local_symbols.addOneAssumeCapacity();
decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
_ = self.offset_table.addOneAssumeCapacity();
- self.symbol_table.items[decl.link.macho.symbol_table_index.?] = .{
+ self.local_symbols.items[decl.link.macho.local_sym_index] = .{
.n_strx = 0,
.n_type = 0,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
};
- self.offset_table.items[decl.link.macho.offset_table_index.?] = 0;
+ self.offset_table.items[decl.link.macho.offset_table_index] = 0;
}
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 {
return;
},
};
- log.debug("generated code {}\n", .{code});
const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
- const symbol = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];
+ const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index];
const decl_name = mem.spanZ(decl.name);
const name_str_index = try self.makeString(decl_name);
const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr });
- log.debug("updated text section {}\n", .{self.sections.items[self.text_section_index.?]});
symbol.* = .{
.n_strx = name_str_index,
@@ -734,18 +780,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
.n_desc = 0,
.n_value = addr,
};
+ self.offset_table.items[decl.link.macho.offset_table_index] = addr;
- // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
- const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
- try self.updateDeclExports(module, decl, decl_exports);
- try self.writeSymbol(decl.link.macho.symbol_table_index.?);
+ try self.writeSymbol(decl.link.macho.local_sym_index);
+ try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
const text_section = self.sections.items[self.text_section_index.?];
const section_offset = symbol.n_value - text_section.addr;
const file_offset = text_section.offset + section_offset;
- log.debug("file_offset 0x{x}\n", .{file_offset});
try self.base.file.?.pwriteAll(code, file_offset);
+
+ // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
+ const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
+ try self.updateDeclExports(module, decl, decl_exports);
}
pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {}
@@ -759,34 +807,89 @@ pub fn updateDeclExports(
const tracy = trace(@src());
defer tracy.end();
- if (decl.link.macho.symbol_table_index == null) return;
-
- const decl_sym = &self.symbol_table.items[decl.link.macho.symbol_table_index.?];
- // TODO implement
- if (exports.len == 0) return;
-
- const exp = exports[0];
- self.entry_addr = decl_sym.n_value;
- decl_sym.n_type |= macho.N_EXT;
- exp.link.sym_index = 0;
+ try self.global_symbols.ensureCapacity(self.base.allocator, self.global_symbols.items.len + exports.len);
+ if (decl.link.macho.local_sym_index == 0) return;
+ const decl_sym = &self.local_symbols.items[decl.link.macho.local_sym_index];
+
+ for (exports) |exp| {
+ if (exp.options.section) |section_name| {
+ if (!mem.eql(u8, section_name, "__text")) {
+ try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
+ module.failed_exports.putAssumeCapacityNoClobber(
+ exp,
+ try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}),
+ );
+ continue;
+ }
+ }
+ const n_desc = switch (exp.options.linkage) {
+ .Internal => macho.REFERENCE_FLAG_PRIVATE_DEFINED,
+ .Strong => blk: {
+ if (mem.eql(u8, exp.options.name, "_start")) {
+ self.entry_addr = decl_sym.n_value;
+ }
+ break :blk macho.REFERENCE_FLAG_DEFINED;
+ },
+ .Weak => macho.N_WEAK_REF,
+ .LinkOnce => {
+ try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
+ module.failed_exports.putAssumeCapacityNoClobber(
+ exp,
+ try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}),
+ );
+ continue;
+ },
+ };
+ const n_type = decl_sym.n_type | macho.N_EXT;
+ if (exp.link.sym_index) |i| {
+ const sym = &self.global_symbols.items[i];
+ sym.* = .{
+ .n_strx = try self.updateString(sym.n_strx, exp.options.name),
+ .n_type = n_type,
+ .n_sect = @intCast(u8, self.text_section_index.?) + 1,
+ .n_desc = n_desc,
+ .n_value = decl_sym.n_value,
+ };
+ } else {
+ const name_str_index = try self.makeString(exp.options.name);
+ _ = self.global_symbols.addOneAssumeCapacity();
+ const i = self.global_symbols.items.len - 1;
+ self.global_symbols.items[i] = .{
+ .n_strx = name_str_index,
+ .n_type = n_type,
+ .n_sect = @intCast(u8, self.text_section_index.?) + 1,
+ .n_desc = n_desc,
+ .n_value = decl_sym.n_value,
+ };
+
+ exp.link.sym_index = @intCast(u32, i);
+ }
+ }
}
pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
- return self.symbol_table.items[decl.link.macho.symbol_table_index.?].n_value;
+ assert(decl.link.macho.local_sym_index != 0);
+ return self.local_symbols.items[decl.link.macho.local_sym_index].n_value;
}
pub fn populateMissingMetadata(self: *MachO) !void {
- if (self.segment_cmd_index == null) {
- self.segment_cmd_index = @intCast(u16, self.load_commands.items.len);
+ switch (self.base.options.output_mode) {
+ .Exe => {},
+ .Obj => return error.TODOImplementWritingObjFiles,
+ .Lib => return error.TODOImplementWritingLibFiles,
+ }
+
+ if (self.pagezero_segment_cmd_index == null) {
+ self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
.Segment = .{
.cmd = macho.LC_SEGMENT_64,
.cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString(""),
+ .segname = makeStaticString("__PAGEZERO"),
.vmaddr = 0,
- .vmsize = 0,
+ .vmsize = 0x100000000, // size always set to 4GB
.fileoff = 0,
.filesize = 0,
.maxprot = 0,
@@ -797,28 +900,34 @@ pub fn populateMissingMetadata(self: *MachO) !void {
});
self.cmd_table_dirty = true;
}
- if (self.symtab_cmd_index == null) {
- self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
+ if (self.text_segment_cmd_index == null) {
+ self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
+ const prot = macho.VM_PROT_READ | macho.VM_PROT_EXECUTE;
try self.load_commands.append(self.base.allocator, .{
- .Symtab = .{
- .cmd = macho.LC_SYMTAB,
- .cmdsize = @sizeOf(macho.symtab_command),
- .symoff = 0,
- .nsyms = 0,
- .stroff = 0,
- .strsize = 0,
+ .Segment = .{
+ .cmd = macho.LC_SEGMENT_64,
+ .cmdsize = @sizeOf(macho.segment_command_64),
+ .segname = makeStaticString("__TEXT"),
+ .vmaddr = 0x100000000, // always starts at 4GB
+ .vmsize = 0,
+ .fileoff = 0,
+ .filesize = 0,
+ .maxprot = prot,
+ .initprot = prot,
+ .nsects = 0,
+ .flags = 0,
},
});
self.cmd_table_dirty = true;
}
if (self.text_section_index == null) {
self.text_section_index = @intCast(u16, self.sections.items.len);
- const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
- segment.cmdsize += @sizeOf(macho.section_64);
- segment.nsects += 1;
+ const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+ text_segment.cmdsize += @sizeOf(macho.section_64);
+ text_segment.nsects += 1;
- const file_size = self.base.options.program_code_size_hint;
- const off = @intCast(u32, self.findFreeSpace(file_size, 1));
+ const file_size = mem.alignForwardGeneric(u64, self.base.options.program_code_size_hint, 0x1000);
+ const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000)); // TODO maybe findFreeSpace should return u32 directly?
const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
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 {
try self.sections.append(self.base.allocator, .{
.sectname = makeStaticString("__text"),
.segname = makeStaticString("__TEXT"),
- .addr = 0,
+ .addr = text_segment.vmaddr + off,
.size = file_size,
.offset = off,
- .@"align" = 0x1000,
+ .@"align" = 12, // 2^12 = 4096
.reloff = 0,
.nreloc = 0,
.flags = flags,
@@ -838,43 +947,256 @@ pub fn populateMissingMetadata(self: *MachO) !void {
.reserved3 = 0,
});
- segment.vmsize += file_size;
- segment.filesize += file_size;
- segment.fileoff = off;
+ text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section.
+ text_segment.filesize = file_size + off;
+ }
+ if (self.data_segment_cmd_index == null) {
+ self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
+ const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+ const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE;
+ try self.load_commands.append(self.base.allocator, .{
+ .Segment = .{
+ .cmd = macho.LC_SEGMENT_64,
+ .cmdsize = @sizeOf(macho.segment_command_64),
+ .segname = makeStaticString("__DATA"),
+ .vmaddr = text_segment.vmaddr + text_segment.vmsize,
+ .vmsize = 0,
+ .fileoff = 0,
+ .filesize = 0,
+ .maxprot = prot,
+ .initprot = prot,
+ .nsects = 0,
+ .flags = 0,
+ },
+ });
+ self.cmd_table_dirty = true;
+ }
+ if (self.got_section_index == null) {
+ self.got_section_index = @intCast(u16, self.sections.items.len);
+ const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+ data_segment.cmdsize += @sizeOf(macho.section_64);
+ data_segment.nsects += 1;
+
+ const file_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
+ const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
+
+ log.debug("found __got section free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
+
+ try self.sections.append(self.base.allocator, .{
+ .sectname = makeStaticString("__got"),
+ .segname = makeStaticString("__DATA"),
+ .addr = data_segment.vmaddr,
+ .size = file_size,
+ .offset = off,
+ .@"align" = 3, // 2^3 = 8
+ .reloff = 0,
+ .nreloc = 0,
+ .flags = macho.S_REGULAR,
+ .reserved1 = 0,
+ .reserved2 = 0,
+ .reserved3 = 0,
+ });
- log.debug("initial text section {}\n", .{self.sections.items[self.text_section_index.?]});
+ const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
+ data_segment.vmsize = segment_size;
+ data_segment.filesize = segment_size;
+ data_segment.fileoff = off;
+ }
+ if (self.linkedit_segment_cmd_index == null) {
+ self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
+ const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
+ const prot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
+ try self.load_commands.append(self.base.allocator, .{
+ .Segment = .{
+ .cmd = macho.LC_SEGMENT_64,
+ .cmdsize = @sizeOf(macho.segment_command_64),
+ .segname = makeStaticString("__LINKEDIT"),
+ .vmaddr = data_segment.vmaddr + data_segment.vmsize,
+ .vmsize = 0,
+ .fileoff = 0,
+ .filesize = 0,
+ .maxprot = prot,
+ .initprot = prot,
+ .nsects = 0,
+ .flags = 0,
+ },
+ });
+ self.cmd_table_dirty = true;
+ }
+ if (self.dyld_info_cmd_index == null) {
+ self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
+ try self.load_commands.append(self.base.allocator, .{
+ .DyldInfo = .{
+ .cmd = macho.LC_DYLD_INFO_ONLY,
+ .cmdsize = @sizeOf(macho.dyld_info_command),
+ .rebase_off = 0,
+ .rebase_size = 0,
+ .bind_off = 0,
+ .bind_size = 0,
+ .weak_bind_off = 0,
+ .weak_bind_size = 0,
+ .lazy_bind_off = 0,
+ .lazy_bind_size = 0,
+ .export_off = 0,
+ .export_size = 0,
+ },
+ });
+ self.cmd_table_dirty = true;
+ }
+ if (self.symtab_cmd_index == null) {
+ self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
+ try self.load_commands.append(self.base.allocator, .{
+ .Symtab = .{
+ .cmd = macho.LC_SYMTAB,
+ .cmdsize = @sizeOf(macho.symtab_command),
+ .symoff = 0,
+ .nsyms = 0,
+ .stroff = 0,
+ .strsize = 0,
+ },
+ });
+ self.cmd_table_dirty = true;
+ }
+ if (self.dysymtab_cmd_index == null) {
+ self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
+ try self.load_commands.append(self.base.allocator, .{
+ .Dysymtab = .{
+ .cmd = macho.LC_DYSYMTAB,
+ .cmdsize = @sizeOf(macho.dysymtab_command),
+ .ilocalsym = 0,
+ .nlocalsym = 0,
+ .iextdefsym = 0,
+ .nextdefsym = 0,
+ .iundefsym = 0,
+ .nundefsym = 0,
+ .tocoff = 0,
+ .ntoc = 0,
+ .modtaboff = 0,
+ .nmodtab = 0,
+ .extrefsymoff = 0,
+ .nextrefsyms = 0,
+ .indirectsymoff = 0,
+ .nindirectsyms = 0,
+ .extreloff = 0,
+ .nextrel = 0,
+ .locreloff = 0,
+ .nlocrel = 0,
+ },
+ });
+ self.cmd_table_dirty = true;
+ }
+ if (self.dylinker_cmd_index == null) {
+ self.dylinker_cmd_index = @intCast(u16, self.load_commands.items.len);
+ const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylinker_command) + mem.lenZ(DEFAULT_DYLD_PATH), @sizeOf(u64));
+ try self.load_commands.append(self.base.allocator, .{
+ .Dylinker = .{
+ .cmd = macho.LC_LOAD_DYLINKER,
+ .cmdsize = @intCast(u32, cmdsize),
+ .name = @sizeOf(macho.dylinker_command),
+ },
+ });
+ self.cmd_table_dirty = true;
+ }
+ if (self.libsystem_cmd_index == null) {
+ self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
+ const cmdsize = mem.alignForwardGeneric(u64, @sizeOf(macho.dylib_command) + mem.lenZ(LIB_SYSTEM_PATH), @sizeOf(u64));
+ // TODO Find a way to work out runtime version from the OS version triple stored in std.Target.
+ // In the meantime, we're gonna hardcode to the minimum compatibility version of 1.0.0.
+ const min_version = 0x10000;
+ const dylib = .{
+ .name = @sizeOf(macho.dylib_command),
+ .timestamp = 2, // not sure why not simply 0; this is reverse engineered from Mach-O files
+ .current_version = min_version,
+ .compatibility_version = min_version,
+ };
+ try self.load_commands.append(self.base.allocator, .{
+ .Dylib = .{
+ .cmd = macho.LC_LOAD_DYLIB,
+ .cmdsize = @intCast(u32, cmdsize),
+ .dylib = dylib,
+ },
+ });
+ self.cmd_table_dirty = true;
+ }
+ if (self.main_cmd_index == null) {
+ self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
+ try self.load_commands.append(self.base.allocator, .{
+ .EntryPoint = .{
+ .cmd = macho.LC_MAIN,
+ .cmdsize = @sizeOf(macho.entry_point_command),
+ .entryoff = 0x0,
+ .stacksize = 0,
+ },
+ });
+ self.cmd_table_dirty = true;
+ }
+ {
+ const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
+ const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo;
+ if (dyld_info.export_off == 0) {
+ const nsyms = self.base.options.symbol_count_hint;
+ const file_size = @sizeOf(u64) * nsyms;
+ const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
+ log.debug("found export trie free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
+ dyld_info.export_off = off;
+ dyld_info.export_size = @intCast(u32, file_size);
+
+ const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
+ linkedit.vmsize += segment_size;
+ linkedit.fileoff = off;
+ }
}
{
+ const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
if (symtab.symoff == 0) {
- const p_align = @sizeOf(macho.nlist_64);
const nsyms = self.base.options.symbol_count_hint;
- const file_size = p_align * nsyms;
- const off = @intCast(u32, self.findFreeSpace(file_size, p_align));
+ const file_size = @sizeOf(macho.nlist_64) * nsyms;
+ const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
log.debug("found symbol table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
symtab.symoff = off;
symtab.nsyms = @intCast(u32, nsyms);
+
+ const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
+ linkedit.vmsize += segment_size;
}
if (symtab.stroff == 0) {
try self.string_table.append(self.base.allocator, 0);
const file_size = @intCast(u32, self.string_table.items.len);
- const off = @intCast(u32, self.findFreeSpace(file_size, 1));
+ const off = @intCast(u32, self.findFreeSpace(file_size, 0x1000));
log.debug("found string table free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
symtab.stroff = off;
symtab.strsize = file_size;
+
+ const segment_size = mem.alignForwardGeneric(u64, file_size, 0x1000);
+ linkedit.vmsize += segment_size;
}
}
+ if (self.dyld_stub_binder_index == null) {
+ self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len);
+ const name = try self.makeString("dyld_stub_binder");
+ try self.undef_symbols.append(self.base.allocator, .{
+ .n_strx = name,
+ .n_type = macho.N_UNDF | macho.N_EXT,
+ .n_sect = 0,
+ .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,
+ .n_value = 0,
+ });
+ }
}
fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
- const segment = &self.load_commands.items[self.segment_cmd_index.?].Segment;
const text_section = &self.sections.items[self.text_section_index.?];
const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
var block_placement: ?*TextBlock = null;
const addr = blk: {
if (self.last_text_block) |last| {
- const last_symbol = self.symbol_table.items[last.symbol_table_index.?];
+ const last_symbol = self.local_symbols.items[last.local_sym_index];
+ // TODO pad out with NOPs and reenable
+ // const ideal_capacity = last.size * alloc_num / alloc_den;
+ // const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity;
+ // const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment);
const end_addr = last_symbol.n_value + last.size;
const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);
block_placement = last;
@@ -883,22 +1205,15 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
break :blk text_section.addr;
}
};
- log.debug("computed symbol address 0x{x}\n", .{addr});
const expand_text_section = block_placement == null or block_placement.?.next == null;
if (expand_text_section) {
const text_capacity = self.allocatedSize(text_section.offset);
const needed_size = (addr + new_block_size) - text_section.addr;
- log.debug("text capacity 0x{x}, needed size 0x{x}\n", .{ text_capacity, needed_size });
assert(needed_size <= text_capacity); // TODO handle growth
self.last_text_block = text_block;
- text_section.size = needed_size;
- segment.vmsize = needed_size;
- segment.filesize = needed_size;
- if (alignment < text_section.@"align") {
- text_section.@"align" = @intCast(u32, alignment);
- }
+ text_section.size = needed_size; // TODO temp until we pad out with NOPs
}
text_block.size = new_block_size;
@@ -936,22 +1251,27 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 {
return @intCast(u32, result);
}
-fn alignSize(comptime Int: type, min_size: anytype, alignment: Int) Int {
- const size = @intCast(Int, min_size);
- if (size % alignment == 0) return size;
-
- const div = size / alignment;
- return (div + 1) * alignment;
+fn getString(self: *MachO, str_off: u32) []const u8 {
+ assert(str_off < self.string_table.items.len);
+ return mem.spanZ(@ptrCast([*:0]const u8, self.string_table.items.ptr + str_off));
}
-fn commandSize(min_size: anytype) u32 {
- return alignSize(u32, min_size, @sizeOf(u64));
+fn updateString(self: *MachO, old_str_off: u32, new_name: []const u8) !u32 {
+ const existing_name = self.getString(old_str_off);
+ if (mem.eql(u8, existing_name, new_name)) {
+ return old_str_off;
+ }
+ return self.makeString(new_name);
}
+/// TODO This should not heap allocate, instead it should utilize a fixed size, statically allocated
+/// global const array. You could even use pwritev to write the same buffer multiple times with only
+/// 1 syscall if you needed to, for example, write 8192 bytes using a buffer of only 4096 bytes.
+/// This size parameter should probably be a usize not u64.
fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
if (size == 0) return;
- const buf = try self.base.allocator.alloc(u8, size);
+ const buf = try self.base.allocator.alloc(u8, @intCast(usize, size));
defer self.base.allocator.free(buf);
mem.set(u8, buf[0..], 0);
@@ -961,11 +1281,8 @@ fn addPadding(self: *MachO, size: u64, file_offset: u64) !void {
fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
const hdr_size: u64 = @sizeOf(macho.mach_header_64);
- if (start < hdr_size)
- return hdr_size;
-
+ if (start < hdr_size) return hdr_size;
const end = start + satMul(size, alloc_num) / alloc_den;
-
{
const off = @sizeOf(macho.mach_header_64);
var tight_size: u64 = 0;
@@ -978,7 +1295,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
return test_end;
}
}
-
for (self.sections.items) |section| {
const increased_size = satMul(section.size, alloc_num) / alloc_den;
const test_end = section.offset + increased_size;
@@ -986,7 +1302,15 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
return test_end;
}
}
-
+ if (self.dyld_info_cmd_index) |dyld_info_index| {
+ const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo;
+ const tight_size = dyld_info.export_size;
+ const increased_size = satMul(tight_size, alloc_num) / alloc_den;
+ const test_end = dyld_info.export_off + increased_size;
+ if (end > dyld_info.export_off and start < test_end) {
+ return test_end;
+ }
+ }
if (self.symtab_cmd_index) |symtab_index| {
const symtab = self.load_commands.items[symtab_index].Symtab;
{
@@ -1005,7 +1329,6 @@ fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
}
}
}
-
return null;
}
@@ -1021,6 +1344,10 @@ fn allocatedSize(self: *MachO, start: u64) u64 {
if (section.offset <= start) continue;
if (section.offset < min_pos) min_pos = section.offset;
}
+ if (self.dyld_info_cmd_index) |dyld_info_index| {
+ const dyld_info = self.load_commands.items[dyld_info_index].DyldInfo;
+ if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off;
+ }
if (self.symtab_cmd_index) |symtab_index| {
const symtab = self.load_commands.items[symtab_index].Symtab;
if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff;
@@ -1042,12 +1369,133 @@ fn writeSymbol(self: *MachO, index: usize) !void {
defer tracy.end();
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
- const sym = [1]macho.nlist_64{self.symbol_table.items[index]};
+ const sym = [1]macho.nlist_64{self.local_symbols.items[index]};
const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
log.debug("writing symbol {} at 0x{x}\n", .{ sym[0], off });
try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
}
+fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
+ const sect = &self.sections.items[self.got_section_index.?];
+ const endian = self.base.options.target.cpu.arch.endian();
+ var buf: [@sizeOf(u64)]u8 = undefined;
+ mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
+ const off = sect.offset + @sizeOf(u64) * index;
+ log.debug("writing offset table entry 0x{x} at 0x{x}\n", .{ self.offset_table.items[index], off });
+ try self.base.file.?.pwriteAll(&buf, off);
+}
+
+fn writeAllGlobalSymbols(self: *MachO) !void {
+ const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
+ const off = symtab.symoff + self.local_symbols.items.len * @sizeOf(macho.nlist_64);
+ const file_size = self.global_symbols.items.len * @sizeOf(macho.nlist_64);
+ log.debug("writing global symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off });
+ try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.global_symbols.items), off);
+}
+
+fn writeAllUndefSymbols(self: *MachO) !void {
+ const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
+ const nlocals = self.local_symbols.items.len;
+ const nglobals = self.global_symbols.items.len;
+ const off = symtab.symoff + (nlocals + nglobals) * @sizeOf(macho.nlist_64);
+ const file_size = self.undef_symbols.items.len * @sizeOf(macho.nlist_64);
+ log.debug("writing undef symbols from 0x{x} to 0x{x}\n", .{ off, file_size + off });
+ try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), off);
+}
+
+fn writeExportTrie(self: *MachO) !void {
+ assert(self.entry_addr != null);
+
+ // TODO implement mechanism for generating a prefix tree of the exported symbols
+ // single branch export trie
+ var buf = [_]u8{0} ** 24;
+ buf[0] = 0; // root node
+ buf[1] = 1; // 1 branch from root
+ mem.copy(u8, buf[2..], "_start");
+ buf[8] = 0;
+ buf[9] = 9 + 1;
+
+ const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
+ const addr = self.entry_addr.? - text_segment.vmaddr;
+ const written = try std.debug.leb.writeULEB128Mem(buf[12..], addr);
+ buf[10] = @intCast(u8, written) + 1;
+ buf[11] = 0;
+
+ const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfo;
+ try self.base.file.?.pwriteAll(buf[0..], dyld_info.export_off);
+}
+
+fn writeStringTable(self: *MachO) !void {
+ const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
+ const allocated_size = self.allocatedSize(symtab.stroff);
+ const needed_size = self.string_table.items.len;
+
+ if (needed_size > allocated_size) {
+ symtab.strsize = 0;
+ symtab.stroff = @intCast(u32, self.findFreeSpace(needed_size, 1));
+ }
+ symtab.strsize = @intCast(u32, needed_size);
+
+ log.debug("writing string table from 0x{x} to 0x{x}\n", .{ symtab.stroff, symtab.stroff + symtab.strsize });
+
+ try self.base.file.?.pwriteAll(self.string_table.items, symtab.stroff);
+
+ // TODO rework how we preallocate space for the entire __LINKEDIT segment instead of
+ // doing dynamic updates like this.
+ const linkedit = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
+ linkedit.filesize = symtab.stroff + symtab.strsize - linkedit.fileoff;
+}
+
+fn writeCmdHeaders(self: *MachO) !void {
+ assert(self.cmd_table_dirty);
+
+ // Write all load command headers first.
+ // Since command sizes are up-to-date and accurate, we will correctly
+ // leave space for any section headers that any of the segment load
+ // commands might consist of.
+ var last_cmd_offset: usize = @sizeOf(macho.mach_header_64);
+ for (self.load_commands.items) |cmd| {
+ try cmd.write(&self.base.file.?, last_cmd_offset);
+ last_cmd_offset += cmd.cmdsize();
+ }
+ {
+ // write __text section header
+ const off = if (self.text_segment_cmd_index) |text_segment_index| blk: {
+ var i: usize = 0;
+ var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
+ while (i < text_segment_index) : (i += 1) {
+ cmdsize += self.load_commands.items[i].cmdsize();
+ }
+ break :blk cmdsize;
+ } else {
+ // If we've landed in here, we are building a MachO object file, so we have
+ // only one, noname segment to append this section header to.
+ return error.TODOImplementWritingObjFiles;
+ };
+ const idx = self.text_section_index.?;
+ log.debug("writing text section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off });
+ try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off);
+ }
+ {
+ // write __got section header
+ const off = if (self.data_segment_cmd_index) |data_segment_index| blk: {
+ var i: usize = 0;
+ var cmdsize: usize = @sizeOf(macho.mach_header_64) + @sizeOf(macho.segment_command_64);
+ while (i < data_segment_index) : (i += 1) {
+ cmdsize += self.load_commands.items[i].cmdsize();
+ }
+ break :blk cmdsize;
+ } else {
+ // If we've landed in here, we are building a MachO object file, so we have
+ // only one, noname segment to append this section header to.
+ return error.TODOImplementWritingObjFiles;
+ };
+ const idx = self.got_section_index.?;
+ log.debug("writing got section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off });
+ try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off);
+ }
+}
+
/// Writes Mach-O file header.
/// Should be invoked last as it needs up-to-date values of ncmds and sizeof_cmds bookkeeping
/// variables.
diff --git a/src/main.zig b/src/main.zig
@@ -268,16 +268,19 @@ const usage_build_generic =
\\ -T[script], --script [script] Use a custom linker script
\\ --version-script [path] Provide a version .map file
\\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so)
- \\ --each-lib-rpath Add rpath for each used dynamic library
\\ --version [ver] Dynamic library semver
\\ -rdynamic Add all symbols to the dynamic symbol table
\\ -rpath [path] Add directory to the runtime library search path
+ \\ -feach-lib-rpath Ensure adding rpath for each used dynamic library
+ \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library
\\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker
+ \\ --emit-relocs Enable output of relocation sections for post build tools
\\ -dynamic Force output to be dynamically linked
\\ -static Force output to be statically linked
\\ -Bsymbolic Bind global references locally
\\ --subsystem [subsystem] (windows) /SUBSYSTEM:<subsystem> to the linker\n"
\\ --stack [size] Override default stack size
+ \\ --image-base [addr] Set base address for executable image
\\ -framework [name] (darwin) link against framework
\\ -F[dir] (darwin) add search path for frameworks
\\
@@ -434,11 +437,13 @@ fn buildOutputType(
var linker_z_defs = false;
var test_evented_io = false;
var stack_size_override: ?u64 = null;
+ var image_base_override: ?u64 = null;
var use_llvm: ?bool = null;
var use_lld: ?bool = null;
var use_clang: ?bool = null;
var link_eh_frame_hdr = false;
- var each_lib_rpath = false;
+ var link_emit_relocs = false;
+ var each_lib_rpath: ?bool = null;
var libc_paths_file: ?[]const u8 = null;
var machine_code_model: std.builtin.CodeModel = .default;
var runtime_args_start: ?usize = null;
@@ -520,7 +525,7 @@ fn buildOutputType(
//}
const args = all_args[2..];
var i: usize = 0;
- while (i < args.len) : (i += 1) {
+ args_loop: while (i < args.len) : (i += 1) {
const arg = args[i];
if (mem.startsWith(u8, arg, "-")) {
if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
@@ -528,7 +533,10 @@ fn buildOutputType(
return cleanExit();
} else if (mem.eql(u8, arg, "--")) {
if (arg_mode == .run) {
- runtime_args_start = i + 1;
+ // The index refers to all_args so skip `zig` `run`
+ // and `--`
+ runtime_args_start = i + 3;
+ break :args_loop;
} else {
fatal("unexpected end-of-parameter mark: --", .{});
}
@@ -626,9 +634,11 @@ fn buildOutputType(
} else if (mem.eql(u8, arg, "--stack")) {
if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
i += 1;
- stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| {
- fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
- };
+ stack_size_override = parseAnyBaseInt(args[i]);
+ } else if (mem.eql(u8, arg, "--image-base")) {
+ if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
+ i += 1;
+ image_base_override = parseAnyBaseInt(args[i]);
} else if (mem.eql(u8, arg, "--name")) {
if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
i += 1;
@@ -733,8 +743,10 @@ fn buildOutputType(
if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg});
i += 1;
override_lib_dir = args[i];
- } else if (mem.eql(u8, arg, "--each-lib-rpath")) {
+ } else if (mem.eql(u8, arg, "-feach-lib-rpath")) {
each_lib_rpath = true;
+ } else if (mem.eql(u8, arg, "-fno-each-lib-rpath")) {
+ each_lib_rpath = false;
} else if (mem.eql(u8, arg, "--enable-cache")) {
enable_cache = true;
} else if (mem.eql(u8, arg, "--test-cmd-bin")) {
@@ -838,6 +850,8 @@ fn buildOutputType(
function_sections = true;
} else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
link_eh_frame_hdr = true;
+ } else if (mem.eql(u8, arg, "--emit-relocs")) {
+ link_emit_relocs = true;
} else if (mem.eql(u8, arg, "-Bsymbolic")) {
linker_bind_global_refs_locally = true;
} else if (mem.eql(u8, arg, "--verbose-link")) {
@@ -1143,9 +1157,13 @@ fn buildOutputType(
if (i >= linker_args.items.len) {
fatal("expected linker arg after '{}'", .{arg});
}
- stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| {
- fatal("unable to parse '{}': {}", .{ arg, @errorName(err) });
- };
+ stack_size_override = parseAnyBaseInt(linker_args.items[i]);
+ } else if (mem.eql(u8, arg, "--image-base")) {
+ i += 1;
+ if (i >= linker_args.items.len) {
+ fatal("expected linker arg after '{}'", .{arg});
+ }
+ image_base_override = parseAnyBaseInt(linker_args.items[i]);
} else {
warn("unsupported linker arg: {}", .{arg});
}
@@ -1206,6 +1224,10 @@ fn buildOutputType(
fatal("translate-c expects exactly 1 source file (found {})", .{c_source_files.items.len});
}
+ if (root_src_file == null and arg_mode == .zig_test) {
+ fatal("one zig source file is required to run `zig test`", .{});
+ }
+
const root_name = if (provided_name) |n| n else blk: {
if (arg_mode == .zig_test) {
break :blk "test";
@@ -1446,6 +1468,11 @@ fn buildOutputType(
cleanup_root_dir = dir;
root_pkg_memory.root_src_directory = .{ .path = p, .handle = dir };
root_pkg_memory.root_src_path = try fs.path.relative(arena, p, src_path);
+ } else if (fs.path.dirname(src_path)) |p| {
+ const dir = try fs.cwd().openDir(p, .{});
+ cleanup_root_dir = dir;
+ root_pkg_memory.root_src_directory = .{ .path = p, .handle = dir };
+ root_pkg_memory.root_src_path = fs.path.basename(src_path);
} else {
root_pkg_memory.root_src_directory = .{ .path = null, .handle = fs.cwd() };
root_pkg_memory.root_src_path = src_path;
@@ -1580,7 +1607,9 @@ fn buildOutputType(
.linker_z_nodelete = linker_z_nodelete,
.linker_z_defs = linker_z_defs,
.link_eh_frame_hdr = link_eh_frame_hdr,
+ .link_emit_relocs = link_emit_relocs,
.stack_size_override = stack_size_override,
+ .image_base_override = image_base_override,
.strip = strip,
.single_threaded = single_threaded,
.function_sections = function_sections,
@@ -2525,7 +2554,7 @@ fn fmtPathFile(
const source_code = source_file.readToEndAllocOptions(
fmt.gpa,
max_src_size,
- stat.size,
+ std.math.cast(usize, stat.size) catch return error.FileTooBig,
@alignOf(u8),
null,
) catch |err| switch (err) {
@@ -3037,3 +3066,18 @@ pub fn cleanExit() void {
process.exit(0);
}
}
+
+fn parseAnyBaseInt(prefixed_bytes: []const u8) u64 {
+ const base: u8 = if (mem.startsWith(u8, prefixed_bytes, "0x"))
+ 16
+ else if (mem.startsWith(u8, prefixed_bytes, "0o"))
+ 8
+ else if (mem.startsWith(u8, prefixed_bytes, "0b"))
+ 2
+ else
+ @as(u8, 10);
+ const bytes = if (base == 10) prefixed_bytes else prefixed_bytes[2..];
+ return std.fmt.parseInt(u64, bytes, base) catch |err| {
+ fatal("unable to parse '{}': {}", .{ prefixed_bytes, @errorName(err) });
+ };
+}
diff --git a/src/mingw.zig b/src/mingw.zig
@@ -32,6 +32,8 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
var args = std.ArrayList([]const u8).init(arena);
try add_cc_args(comp, arena, &args);
try args.appendSlice(&[_][]const u8{
+ "-D_SYSCRT=1",
+ "-DCRTDLL=1",
"-U__CRTDLL__",
"-D__MSVCRT__",
// Uncomment these 3 things for crtu
@@ -53,6 +55,8 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
var args = std.ArrayList([]const u8).init(arena);
try add_cc_args(comp, arena, &args);
try args.appendSlice(&[_][]const u8{
+ "-D_SYSCRT=1",
+ "-DCRTDLL=1",
"-U__CRTDLL__",
"-D__MSVCRT__",
});
@@ -437,11 +441,8 @@ fn findDef(comp: *Compilation, allocator: *Allocator, lib_name: []const u8) ![]u
const lib_path = switch (target.cpu.arch) {
.i386 => "lib32",
.x86_64 => "lib64",
- .arm, .armeb => switch (target.cpu.arch.ptrBitWidth()) {
- 32 => "libarm32",
- 64 => "libarm64",
- else => unreachable,
- },
+ .arm, .armeb, .thumb, .thumbeb, .aarch64_32 => "libarm32",
+ .aarch64, .aarch64_be => "libarm64",
else => unreachable,
};
diff --git a/src/stage1.zig b/src/stage1.zig
@@ -39,7 +39,11 @@ pub export fn main(argc: c_int, argv: [*]const [*:0]const u8) c_int {
for (args) |*arg, i| {
arg.* = mem.spanZ(argv[i]);
}
- stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{@errorName(err)});
+ if (std.builtin.mode == .Debug) {
+ stage2.mainArgs(gpa, arena, args) catch unreachable;
+ } else {
+ stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{@errorName(err)});
+ }
return 0;
}
diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp
@@ -18,11 +18,6 @@
#include "target.hpp"
#include "tokenizer.hpp"
-#ifndef NDEBUG
-#define DBG_MACRO_NO_WARNING
-#include <dbg.h>
-#endif
-
struct AstNode;
struct ZigFn;
struct Scope;
@@ -1456,6 +1451,7 @@ struct ZigTypeEnum {
ContainerLayout layout;
ResolveStatus resolve_status;
+ bool has_explicit_tag_type;
bool non_exhaustive;
bool resolve_loop_flag;
};
@@ -1825,6 +1821,7 @@ enum BuiltinFnId {
BuiltinFnIdWasmMemorySize,
BuiltinFnIdWasmMemoryGrow,
BuiltinFnIdSrc,
+ BuiltinFnIdReduce,
};
struct BuiltinFnEntry {
@@ -2440,6 +2437,15 @@ enum AtomicOrder {
AtomicOrderSeqCst,
};
+// synchronized with code in define_builtin_compile_vars
+enum ReduceOp {
+ ReduceOp_and,
+ ReduceOp_or,
+ ReduceOp_xor,
+ ReduceOp_min,
+ ReduceOp_max,
+};
+
// synchronized with the code in define_builtin_compile_vars
enum AtomicRmwOp {
AtomicRmwOp_xchg,
@@ -2549,6 +2555,7 @@ enum IrInstSrcId {
IrInstSrcIdEmbedFile,
IrInstSrcIdCmpxchg,
IrInstSrcIdFence,
+ IrInstSrcIdReduce,
IrInstSrcIdTruncate,
IrInstSrcIdIntCast,
IrInstSrcIdFloatCast,
@@ -2671,6 +2678,7 @@ enum IrInstGenId {
IrInstGenIdErrName,
IrInstGenIdCmpxchg,
IrInstGenIdFence,
+ IrInstGenIdReduce,
IrInstGenIdTruncate,
IrInstGenIdShuffleVector,
IrInstGenIdSplat,
@@ -3520,6 +3528,20 @@ struct IrInstGenFence {
AtomicOrder order;
};
+struct IrInstSrcReduce {
+ IrInstSrc base;
+
+ IrInstSrc *op;
+ IrInstSrc *value;
+};
+
+struct IrInstGenReduce {
+ IrInstGen base;
+
+ ReduceOp op;
+ IrInstGen *value;
+};
+
struct IrInstSrcTruncate {
IrInstSrc base;
diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp
@@ -1802,10 +1802,18 @@ Error type_allowed_in_extern(CodeGen *g, ZigType *type_entry, bool *result) {
}
return type_allowed_in_extern(g, child_type, result);
}
- case ZigTypeIdEnum:
- *result = type_entry->data.enumeration.layout == ContainerLayoutExtern ||
- type_entry->data.enumeration.layout == ContainerLayoutPacked;
- return ErrorNone;
+ case ZigTypeIdEnum: {
+ if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
+ return err;
+ ZigType *tag_int_type = type_entry->data.enumeration.tag_int_type;
+ if (type_entry->data.enumeration.has_explicit_tag_type) {
+ return type_allowed_in_extern(g, tag_int_type, result);
+ } else {
+ *result = type_entry->data.enumeration.layout == ContainerLayoutExtern ||
+ type_entry->data.enumeration.layout == ContainerLayoutPacked;
+ return ErrorNone;
+ }
+ }
case ZigTypeIdUnion:
*result = type_entry->data.unionation.layout == ContainerLayoutExtern ||
type_entry->data.unionation.layout == ContainerLayoutPacked;
@@ -2639,9 +2647,11 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {
if (decl_node->type == NodeTypeContainerDecl) {
if (decl_node->data.container_decl.init_arg_expr != nullptr) {
wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
+ enum_type->data.enumeration.has_explicit_tag_type = true;
}
} else {
wanted_tag_int_type = enum_type->data.enumeration.tag_int_type;
+ enum_type->data.enumeration.has_explicit_tag_type = true;
}
if (wanted_tag_int_type != nullptr) {
@@ -3120,12 +3130,9 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
bool create_enum_type = is_auto_enum || (!is_explicit_enum && want_safety);
bool *covered_enum_fields;
bool *is_zero_bits = heap::c_allocator.allocate<bool>(field_count);
- ZigLLVMDIEnumerator **di_enumerators;
if (create_enum_type) {
occupied_tag_values.init(field_count);
- di_enumerators = heap::c_allocator.allocate<ZigLLVMDIEnumerator*>(field_count);
-
ZigType *tag_int_type;
if (enum_type_node != nullptr) {
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) {
}
if (create_enum_type) {
- di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(union_field->name), i);
union_field->enum_field = &tag_type->data.enumeration.fields[i];
union_field->enum_field->name = union_field->name;
union_field->enum_field->decl_index = i;
@@ -3336,6 +3342,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
gen_field_index += 1;
}
}
+ heap::c_allocator.deallocate(is_zero_bits, field_count);
bool src_have_tag = is_auto_enum || is_explicit_enum;
@@ -3403,6 +3410,7 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) {
union_type->data.unionation.resolve_status = ResolveStatusInvalid;
}
}
+ heap::c_allocator.deallocate(covered_enum_fields, tag_type->data.enumeration.src_field_count);
}
if (union_type->data.unionation.resolve_status == ResolveStatusInvalid) {
diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp
@@ -2584,36 +2584,6 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutableGen *executable, Ir
return nullptr;
}
-enum class ScalarizePredicate {
- // Returns true iff all the elements in the vector are 1.
- // Equivalent to folding all the bits with `and`.
- All,
- // Returns true iff there's at least one element in the vector that is 1.
- // Equivalent to folding all the bits with `or`.
- Any,
-};
-
-// Collapses a <N x i1> vector into a single i1 according to the given predicate
-static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val, ScalarizePredicate predicate) {
- assert(LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMVectorTypeKind);
- LLVMTypeRef scalar_type = LLVMIntType(LLVMGetVectorSize(LLVMTypeOf(val)));
- LLVMValueRef casted = LLVMBuildBitCast(g->builder, val, scalar_type, "");
-
- switch (predicate) {
- case ScalarizePredicate::Any: {
- LLVMValueRef all_zeros = LLVMConstNull(scalar_type);
- return LLVMBuildICmp(g->builder, LLVMIntNE, casted, all_zeros, "");
- }
- case ScalarizePredicate::All: {
- LLVMValueRef all_ones = LLVMConstAllOnes(scalar_type);
- return LLVMBuildICmp(g->builder, LLVMIntEQ, casted, all_ones, "");
- }
- }
-
- zig_unreachable();
-}
-
-
static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type,
LLVMValueRef val1, LLVMValueRef val2)
{
@@ -2638,7 +2608,7 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type,
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk");
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail");
if (operand_type->id == ZigTypeIdVector) {
- ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All);
+ ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit);
}
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
@@ -2669,7 +2639,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type,
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk");
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail");
if (operand_type->id == ZigTypeIdVector) {
- ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All);
+ ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit);
}
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
@@ -2746,7 +2716,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
}
if (operand_type->id == ZigTypeIdVector) {
- is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any);
+ is_zero_bit = ZigLLVMBuildOrReduce(g->builder, is_zero_bit);
}
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
LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, "");
LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, "");
if (operand_type->id == ZigTypeIdVector) {
- overflow_fail_bit = scalarize_cmp_result(g, overflow_fail_bit, ScalarizePredicate::Any);
+ overflow_fail_bit = ZigLLVMBuildOrReduce(g->builder, overflow_fail_bit);
}
LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block);
@@ -2796,7 +2766,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, "");
if (operand_type->id == ZigTypeIdVector) {
- ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All);
+ ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit);
}
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
@@ -2813,7 +2783,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd");
LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, "");
if (operand_type->id == ZigTypeIdVector) {
- ltz = scalarize_cmp_result(g, ltz, ScalarizePredicate::Any);
+ ltz = ZigLLVMBuildOrReduce(g->builder, ltz);
}
LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block);
@@ -2865,7 +2835,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, "");
if (operand_type->id == ZigTypeIdVector) {
- ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All);
+ ok_bit = ZigLLVMBuildAndReduce(g->builder, ok_bit);
}
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
@@ -2929,7 +2899,7 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
}
if (operand_type->id == ZigTypeIdVector) {
- is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any);
+ is_zero_bit = ZigLLVMBuildOrReduce(g->builder, is_zero_bit);
}
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
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk");
LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, "");
if (rhs_type->id == ZigTypeIdVector) {
- less_than_bit = scalarize_cmp_result(g, less_than_bit, ScalarizePredicate::Any);
+ less_than_bit = ZigLLVMBuildOrReduce(g->builder, less_than_bit);
}
LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block);
@@ -4415,6 +4385,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutableGen *executable, IrIn
}
}
LLVMTypeRef frame_with_args_type = LLVMStructType(field_types, field_count, false);
+ heap::c_allocator.deallocate(field_types, field_count);
LLVMTypeRef ptr_frame_with_args_type = LLVMPointerType(frame_with_args_type, 0);
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
gen_assign_raw(g, arg_ptr, get_pointer_to_type(g, gen_param_types.at(arg_i), true),
gen_param_values.at(arg_i));
}
+ gen_param_types.deinit();
if (instruction->modifier == CallModifierAsync) {
gen_resume(g, fn_val, frame_result_loc, ResumeIdCall);
@@ -4506,6 +4478,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutableGen *executable, IrIn
LLVMValueRef result_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, "");
return LLVMBuildLoad(g->builder, result_ptr, "");
}
+ } else {
+ gen_param_types.deinit();
}
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
ret_type = get_llvm_type(g, instruction->base.value->type);
}
LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, (unsigned)input_and_output_count, false);
+ heap::c_allocator.deallocate(param_types, input_and_output_count);
bool is_volatile = instruction->has_side_effects || (asm_expr->output_list.length == 0);
LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(&llvm_template), buf_len(&llvm_template),
buf_ptr(&constraint_buf), buf_len(&constraint_buf), is_volatile, false, LLVMInlineAsmDialectATT);
- return LLVMBuildCall(g->builder, asm_fn, param_values, (unsigned)input_and_output_count, "");
+ LLVMValueRef built_call = LLVMBuildCall(g->builder, asm_fn, param_values, (unsigned)input_and_output_count, "");
+ heap::c_allocator.deallocate(param_values, input_and_output_count);
+ return built_call;
}
static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueRef maybe_handle) {
@@ -5081,6 +5058,8 @@ static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutableGen *executable, IrIns
incoming_blocks[i] = instruction->incoming_blocks[i]->llvm_exit_block;
}
LLVMAddIncoming(phi, incoming_values, incoming_blocks, (unsigned)instruction->incoming_count);
+ heap::c_allocator.deallocate(incoming_values, instruction->incoming_count);
+ heap::c_allocator.deallocate(incoming_blocks, instruction->incoming_count);
return phi;
}
@@ -5471,6 +5450,50 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutableGen *executable, I
return result_loc;
}
+static LLVMValueRef ir_render_reduce(CodeGen *g, IrExecutableGen *executable, IrInstGenReduce *instruction) {
+ LLVMValueRef value = ir_llvm_value(g, instruction->value);
+
+ ZigType *value_type = instruction->value->value->type;
+ assert(value_type->id == ZigTypeIdVector);
+ ZigType *scalar_type = value_type->data.vector.elem_type;
+
+ LLVMValueRef result_val;
+ switch (instruction->op) {
+ case ReduceOp_and:
+ assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);
+ result_val = ZigLLVMBuildAndReduce(g->builder, value);
+ break;
+ case ReduceOp_or:
+ assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);
+ result_val = ZigLLVMBuildOrReduce(g->builder, value);
+ break;
+ case ReduceOp_xor:
+ assert(scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdBool);
+ result_val = ZigLLVMBuildXorReduce(g->builder, value);
+ break;
+ case ReduceOp_min: {
+ if (scalar_type->id == ZigTypeIdInt) {
+ const bool is_signed = scalar_type->data.integral.is_signed;
+ result_val = ZigLLVMBuildIntMinReduce(g->builder, value, is_signed);
+ } else if (scalar_type->id == ZigTypeIdFloat) {
+ result_val = ZigLLVMBuildFPMinReduce(g->builder, value);
+ } else zig_unreachable();
+ } break;
+ case ReduceOp_max: {
+ if (scalar_type->id == ZigTypeIdInt) {
+ const bool is_signed = scalar_type->data.integral.is_signed;
+ result_val = ZigLLVMBuildIntMaxReduce(g->builder, value, is_signed);
+ } else if (scalar_type->id == ZigTypeIdFloat) {
+ result_val = ZigLLVMBuildFPMaxReduce(g->builder, value);
+ } else zig_unreachable();
+ } break;
+ default:
+ zig_unreachable();
+ }
+
+ return result_val;
+}
+
static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutableGen *executable, IrInstGenFence *instruction) {
LLVMAtomicOrdering atomic_order = to_LLVMAtomicOrdering(instruction->order);
LLVMBuildFence(g->builder, atomic_order, false, "");
@@ -6675,6 +6698,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executabl
return ir_render_cmpxchg(g, executable, (IrInstGenCmpxchg *)instruction);
case IrInstGenIdFence:
return ir_render_fence(g, executable, (IrInstGenFence *)instruction);
+ case IrInstGenIdReduce:
+ return ir_render_reduce(g, executable, (IrInstGenReduce *)instruction);
case IrInstGenIdTruncate:
return ir_render_truncate(g, executable, (IrInstGenTruncate *)instruction);
case IrInstGenIdBoolNot:
@@ -7457,10 +7482,14 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
}
}
if (make_unnamed_struct) {
- return LLVMConstStruct(fields, type_entry->data.structure.gen_field_count,
+ LLVMValueRef unnamed_struct = LLVMConstStruct(fields, type_entry->data.structure.gen_field_count,
type_entry->data.structure.layout == ContainerLayoutPacked);
+ heap::c_allocator.deallocate(fields, type_entry->data.structure.gen_field_count);
+ return unnamed_struct;
} else {
- return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, type_entry->data.structure.gen_field_count);
+ LLVMValueRef named_struct = LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, type_entry->data.structure.gen_field_count);
+ heap::c_allocator.deallocate(fields, type_entry->data.structure.gen_field_count);
+ return named_struct;
}
}
case ZigTypeIdArray:
@@ -7485,9 +7514,13 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
values[len] = gen_const_val(g, type_entry->data.array.sentinel, "");
}
if (make_unnamed_struct) {
- return LLVMConstStruct(values, full_len, true);
+ LLVMValueRef unnamed_struct = LLVMConstStruct(values, full_len, true);
+ heap::c_allocator.deallocate(values, full_len);
+ return unnamed_struct;
} else {
- return LLVMConstArray(element_type_ref, values, (unsigned)full_len);
+ LLVMValueRef array = LLVMConstArray(element_type_ref, values, (unsigned)full_len);
+ heap::c_allocator.deallocate(values, full_len);
+ return array;
}
}
case ConstArraySpecialBuf: {
@@ -7509,7 +7542,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
ZigValue *elem_value = &const_val->data.x_array.data.s_none.elements[i];
values[i] = gen_const_val(g, elem_value, "");
}
- return LLVMConstVector(values, len);
+ LLVMValueRef vector = LLVMConstVector(values, len);
+ heap::c_allocator.deallocate(values, len);
+ return vector;
}
case ConstArraySpecialBuf: {
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
for (uint64_t i = 0; i < len; i += 1) {
values[i] = LLVMConstInt(g->builtin_types.entry_u8->llvm_type, buf_ptr(buf)[i], false);
}
- return LLVMConstVector(values, len);
+ LLVMValueRef vector = LLVMConstVector(values, len);
+ heap::c_allocator.deallocate(values, len);
+ return vector;
}
}
zig_unreachable();
@@ -7740,6 +7777,7 @@ static void generate_error_name_table(CodeGen *g) {
}
LLVMValueRef err_name_table_init = LLVMConstArray(get_llvm_type(g, str_type), values, (unsigned)g->errors_by_index.length);
+ heap::c_allocator.deallocate(values, g->errors_by_index.length);
g->err_name_table = LLVMAddGlobal(g->module, LLVMTypeOf(err_name_table_init),
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) {
create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1);
create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2);
create_builtin_fn(g, BuiltinFnIdSrc, "src", 0);
+ create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);
}
static const char *bool_to_str(bool b) {
@@ -8816,7 +8855,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
buf_append_str(contents, "/// Deprecated: use `std.Target.current.cpu.arch.endian()`\n");
buf_append_str(contents, "pub const endian = Target.current.cpu.arch.endian();\n");
buf_appendf(contents, "pub const output_mode = OutputMode.Obj;\n");
- buf_appendf(contents, "pub const link_mode = LinkMode.Static;\n");
+ buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", ZIG_LINK_MODE);
buf_appendf(contents, "pub const is_test = false;\n");
buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded));
buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi);
diff --git a/src/stage1/config.h.in b/src/stage1/config.h.in
@@ -21,5 +21,6 @@
#define ZIG_CLANG_LIBRARIES "@CLANG_LIBRARIES@"
#define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@"
#define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@"
+#define ZIG_LINK_MODE "@ZIG_LINK_MODE@"
#endif
diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp
@@ -403,6 +403,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCmpxchg *>(inst));
case IrInstSrcIdFence:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFence *>(inst));
+ case IrInstSrcIdReduce:
+ return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReduce *>(inst));
case IrInstSrcIdTruncate:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTruncate *>(inst));
case IrInstSrcIdIntCast:
@@ -637,6 +639,8 @@ void destroy_instruction_gen(IrInstGen *inst) {
return heap::c_allocator.destroy(reinterpret_cast<IrInstGenCmpxchg *>(inst));
case IrInstGenIdFence:
return heap::c_allocator.destroy(reinterpret_cast<IrInstGenFence *>(inst));
+ case IrInstGenIdReduce:
+ return heap::c_allocator.destroy(reinterpret_cast<IrInstGenReduce *>(inst));
case IrInstGenIdTruncate:
return heap::c_allocator.destroy(reinterpret_cast<IrInstGenTruncate *>(inst));
case IrInstGenIdShuffleVector:
@@ -1312,6 +1316,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcFence *) {
return IrInstSrcIdFence;
}
+static constexpr IrInstSrcId ir_inst_id(IrInstSrcReduce *) {
+ return IrInstSrcIdReduce;
+}
+
static constexpr IrInstSrcId ir_inst_id(IrInstSrcTruncate *) {
return IrInstSrcIdTruncate;
}
@@ -1776,6 +1784,10 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenFence *) {
return IrInstGenIdFence;
}
+static constexpr IrInstGenId ir_inst_id(IrInstGenReduce *) {
+ return IrInstGenIdReduce;
+}
+
static constexpr IrInstGenId ir_inst_id(IrInstGenTruncate *) {
return IrInstGenIdTruncate;
}
@@ -3503,6 +3515,29 @@ static IrInstGen *ir_build_fence_gen(IrAnalyze *ira, IrInst *source_instr, Atomi
return &instruction->base;
}
+static IrInstSrc *ir_build_reduce(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *op, IrInstSrc *value) {
+ IrInstSrcReduce *instruction = ir_build_instruction<IrInstSrcReduce>(irb, scope, source_node);
+ instruction->op = op;
+ instruction->value = value;
+
+ ir_ref_instruction(op, irb->current_basic_block);
+ ir_ref_instruction(value, irb->current_basic_block);
+
+ return &instruction->base;
+}
+
+static IrInstGen *ir_build_reduce_gen(IrAnalyze *ira, IrInst *source_instruction, ReduceOp op, IrInstGen *value, ZigType *result_type) {
+ IrInstGenReduce *instruction = ir_build_inst_gen<IrInstGenReduce>(&ira->new_irb,
+ source_instruction->scope, source_instruction->source_node);
+ instruction->base.value->type = result_type;
+ instruction->op = op;
+ instruction->value = value;
+
+ ir_ref_inst_gen(value);
+
+ return &instruction->base;
+}
+
static IrInstSrc *ir_build_truncate(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *dest_type, IrInstSrc *target)
{
@@ -6581,6 +6616,21 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod
IrInstSrc *fence = ir_build_fence(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, fence, lval, result_loc);
}
+ case BuiltinFnIdReduce:
+ {
+ AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+ IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
+ if (arg0_value == irb->codegen->invalid_inst_src)
+ return arg0_value;
+
+ AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+ IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
+ if (arg1_value == irb->codegen->invalid_inst_src)
+ return arg1_value;
+
+ IrInstSrc *reduce = ir_build_reduce(irb, scope, node, arg0_value, arg1_value);
+ return ir_lval_wrap(irb, scope, reduce, lval, result_loc);
+ }
case BuiltinFnIdDivExact:
{
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
ScopeRuntime *scope_runtime = runtime_scopes.at(i);
ir_mark_gen(ir_build_check_runtime_scope(irb, continue_scope, node, scope_runtime->is_comptime, is_comptime));
}
+ runtime_scopes.deinit();
IrBasicBlockSrc *dest_block = loop_scope->continue_block;
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) {
return ir_resolve_bool(ira, value, out);
}
+static bool ir_resolve_reduce_op(IrAnalyze *ira, IrInstGen *value, ReduceOp *out) {
+ if (type_is_invalid(value->value->type))
+ return false;
+
+ ZigType *reduce_op_type = get_builtin_type(ira->codegen, "ReduceOp");
+
+ IrInstGen *casted_value = ir_implicit_cast(ira, value, reduce_op_type);
+ if (type_is_invalid(casted_value->value->type))
+ return false;
+
+ ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
+ if (!const_val)
+ return false;
+
+ *out = (ReduceOp)bigint_as_u32(&const_val->data.x_enum_tag);
+ return true;
+}
+
static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstGen *value, AtomicOrder *out) {
if (type_is_invalid(value->value->type))
return false;
@@ -21527,6 +21596,7 @@ static IrInstGen *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstSrcPhi *phi_i
predecessor->instruction_list.append(instrs_to_move.pop());
}
predecessor->instruction_list.append(branch_instruction);
+ instrs_to_move.deinit();
}
}
@@ -21577,7 +21647,10 @@ static IrInstGen *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstSrcPhi *phi_i
}
if (new_incoming_blocks.length == 1) {
- return new_incoming_values.at(0);
+ IrInstGen *incoming_value = new_incoming_values.at(0);
+ new_incoming_blocks.deinit();
+ new_incoming_values.deinit();
+ return incoming_value;
}
ZigType *resolved_type = nullptr;
@@ -24140,6 +24213,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc
first_non_const_instruction = result_loc;
}
}
+ heap::c_allocator.deallocate(field_assign_nodes, actual_field_count);
if (any_missing)
return ira->codegen->invalid_inst_gen;
@@ -24155,6 +24229,7 @@ static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *sourc
}
}
+ const_ptrs.deinit();
IrInstGen *result = ir_get_deref(ira, source_instr, result_loc, nullptr);
if (is_comptime && !instr_is_comptime(result)) {
@@ -25028,7 +25103,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, IrInst *source_instr,
fields[2]->special = ConstValSpecialStatic;
fields[2]->type = ira->codegen->builtin_types.entry_bool;
fields[2]->data.x_bool = attrs_type->data.pointer.is_volatile;
- // alignment: u32
+ // alignment: comptime_int
ensure_field_index(result->type, "alignment", 3);
fields[3]->type = ira->codegen->builtin_types.entry_num_lit_int;
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
union_field_val->special = ConstValSpecialStatic;
union_field_val->type = type_info_union_field_type;
- ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2);
+ ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 3);
+ // field_type: type
inner_fields[1]->special = ConstValSpecialStatic;
inner_fields[1]->type = ira->codegen->builtin_types.entry_type;
inner_fields[1]->data.x_type = union_field->type_entry;
+ // alignment: comptime_int
+ inner_fields[2]->special = ConstValSpecialStatic;
+ inner_fields[2]->type = ira->codegen->builtin_types.entry_num_lit_int;
+ bigint_init_unsigned(&inner_fields[2]->data.x_bigint, union_field->align);
+
ZigValue *name = create_const_str_lit(ira->codegen, union_field->name)->data.x_ptr.data.ref.pointee;
init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(union_field->name), true);
@@ -25503,7 +25584,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
struct_field_val->special = ConstValSpecialStatic;
struct_field_val->type = type_info_struct_field_type;
- ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 4);
+ ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 5);
inner_fields[1]->special = ConstValSpecialStatic;
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
}
set_optional_payload(inner_fields[2], struct_field->init_val);
+ // is_comptime: bool
inner_fields[3]->special = ConstValSpecialStatic;
inner_fields[3]->type = ira->codegen->builtin_types.entry_bool;
inner_fields[3]->data.x_bool = struct_field->is_comptime;
+ // alignment: comptime_int
+ inner_fields[4]->special = ConstValSpecialStatic;
+ inner_fields[4]->type = ira->codegen->builtin_types.entry_num_lit_int;
+ bigint_init_unsigned(&inner_fields[4]->data.x_bigint, struct_field->align);
+
ZigValue *name = create_const_str_lit(ira->codegen, struct_field->name)->data.x_ptr.data.ref.pointee;
init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(struct_field->name), true);
@@ -25553,7 +25640,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Fn", nullptr);
- ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 5);
+ ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 6);
result->data.x_struct.fields = fields;
// calling_convention: TypeInfo.CallingConvention
@@ -25561,38 +25648,42 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
fields[0]->special = ConstValSpecialStatic;
fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention");
bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
+ // alignment: u29
+ ensure_field_index(result->type, "alignment", 1);
+ fields[1]->special = ConstValSpecialStatic;
+ fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;
+ bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.fn.fn_type_id.alignment);
// is_generic: bool
- ensure_field_index(result->type, "is_generic", 1);
+ ensure_field_index(result->type, "is_generic", 2);
bool is_generic = type_entry->data.fn.is_generic;
- fields[1]->special = ConstValSpecialStatic;
- fields[1]->type = ira->codegen->builtin_types.entry_bool;
- fields[1]->data.x_bool = is_generic;
- // is_varargs: bool
- ensure_field_index(result->type, "is_var_args", 2);
- bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args;
fields[2]->special = ConstValSpecialStatic;
fields[2]->type = ira->codegen->builtin_types.entry_bool;
- fields[2]->data.x_bool = type_entry->data.fn.fn_type_id.is_var_args;
- // return_type: ?type
- ensure_field_index(result->type, "return_type", 3);
+ fields[2]->data.x_bool = is_generic;
+ // is_varargs: bool
+ ensure_field_index(result->type, "is_var_args", 3);
+ bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args;
fields[3]->special = ConstValSpecialStatic;
- fields[3]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
+ fields[3]->type = ira->codegen->builtin_types.entry_bool;
+ fields[3]->data.x_bool = is_varargs;
+ // return_type: ?type
+ ensure_field_index(result->type, "return_type", 4);
+ fields[4]->special = ConstValSpecialStatic;
+ fields[4]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
if (type_entry->data.fn.fn_type_id.return_type == nullptr)
- fields[3]->data.x_optional = nullptr;
+ fields[4]->data.x_optional = nullptr;
else {
ZigValue *return_type = ira->codegen->pass1_arena->create<ZigValue>();
return_type->special = ConstValSpecialStatic;
return_type->type = ira->codegen->builtin_types.entry_type;
return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type;
- fields[3]->data.x_optional = return_type;
+ fields[4]->data.x_optional = return_type;
}
// args: []TypeInfo.FnArg
ZigType *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr);
if ((err = type_resolve(ira->codegen, type_info_fn_arg_type, ResolveStatusSizeKnown))) {
zig_unreachable();
}
- size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count -
- (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC);
+ size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count;
ZigValue *fn_arg_array = ira->codegen->pass1_arena->create<ZigValue>();
fn_arg_array->special = ConstValSpecialStatic;
@@ -25600,7 +25691,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
fn_arg_array->data.x_array.special = ConstArraySpecialNone;
fn_arg_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate<ZigValue>(fn_arg_count);
- init_const_slice(ira->codegen, fields[4], fn_arg_array, 0, fn_arg_count, false);
+ init_const_slice(ira->codegen, fields[5], fn_arg_array, 0, fn_arg_count, false);
for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) {
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
buf_sprintf("sentinels are only allowed on slices and unknown-length pointers"));
return ira->codegen->invalid_inst_gen->value->type;
}
- BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 3);
- if (bi == nullptr)
+
+ BigInt *alignment = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 3);
+ if (alignment == nullptr)
return ira->codegen->invalid_inst_gen->value->type;
bool is_const;
@@ -25897,7 +25989,7 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
is_const,
is_volatile,
ptr_len,
- bigint_as_u32(bi),
+ bigint_as_u32(alignment),
0, // bit_offset_in_host
0, // host_int_bytes
is_allowzero,
@@ -26134,6 +26226,10 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
}
if ((err = get_const_field_bool(ira, source_instr->source_node, field_value, "is_comptime", 3, &field->is_comptime)))
return ira->codegen->invalid_inst_gen->value->type;
+ BigInt *alignment = get_const_field_lit_int(ira, source_instr->source_node, field_value, "alignment", 4);
+ if (alignment == nullptr)
+ return ira->codegen->invalid_inst_gen->value->type;
+ field->align = bigint_as_u32(alignment);
}
return entry;
@@ -26151,6 +26247,13 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag);
ZigType *tag_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "tag_type", 1);
+ if (type_is_invalid(tag_type))
+ return ira->codegen->invalid_inst_gen->value->type;
+ if (tag_type->id != ZigTypeIdInt) {
+ ir_add_error(ira, source_instr, buf_sprintf(
+ "TypeInfo.Enum.tag_type must be an integer type, not '%s'", buf_ptr(&tag_type->name)));
+ return ira->codegen->invalid_inst_gen->value->type;
+ }
ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 2);
if (fields_value == nullptr)
@@ -26296,14 +26399,113 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
return ira->codegen->invalid_inst_gen->value->type;
field->type_val = type_value;
field->type_entry = type_value->data.x_type;
+ BigInt *alignment = get_const_field_lit_int(ira, source_instr->source_node, field_value, "alignment", 2);
+ if (alignment == nullptr)
+ return ira->codegen->invalid_inst_gen->value->type;
+ field->align = bigint_as_u32(alignment);
}
return entry;
}
case ZigTypeIdFn:
- case ZigTypeIdBoundFn:
- ir_add_error(ira, source_instr, buf_sprintf(
- "@Type not available for 'TypeInfo.%s'", type_id_name(tagTypeId)));
- return ira->codegen->invalid_inst_gen->value->type;
+ case ZigTypeIdBoundFn: {
+ assert(payload->special == ConstValSpecialStatic);
+ assert(payload->type == ir_type_info_get_type(ira, "Fn", nullptr));
+
+ ZigValue *cc_value = get_const_field(ira, source_instr->source_node, payload, "calling_convention", 0);
+ if (cc_value == nullptr)
+ return ira->codegen->invalid_inst_gen->value->type;
+ assert(cc_value->special == ConstValSpecialStatic);
+ assert(cc_value->type == get_builtin_type(ira->codegen, "CallingConvention"));
+ CallingConvention cc = (CallingConvention)bigint_as_u32(&cc_value->data.x_enum_tag);
+
+ BigInt *alignment = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 1);
+ if (alignment == nullptr)
+ return ira->codegen->invalid_inst_gen->value->type;
+
+ Error err;
+ bool is_generic;
+ if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_generic", 2, &is_generic)))
+ return ira->codegen->invalid_inst_gen->value->type;
+ if (is_generic) {
+ ir_add_error(ira, source_instr, buf_sprintf("TypeInfo.Fn.is_generic must be false for @Type"));
+ return ira->codegen->invalid_inst_gen->value->type;
+ }
+
+ bool is_var_args;
+ if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_var_args", 3, &is_var_args)))
+ return ira->codegen->invalid_inst_gen->value->type;
+ if (is_var_args && cc != CallingConventionC) {
+ ir_add_error(ira, source_instr, buf_sprintf("varargs functions must have C calling convention"));
+ return ira->codegen->invalid_inst_gen->value->type;
+ }
+
+ ZigType *return_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "return_type", 4);
+ if (return_type == nullptr) {
+ ir_add_error(ira, source_instr, buf_sprintf("TypeInfo.Fn.return_type must be non-null for @Type"));
+ return ira->codegen->invalid_inst_gen->value->type;
+ }
+
+ ZigValue *args_value = get_const_field(ira, source_instr->source_node, payload, "args", 5);
+ if (args_value == nullptr)
+ return ira->codegen->invalid_inst_gen->value->type;
+ assert(args_value->special == ConstValSpecialStatic);
+ assert(is_slice(args_value->type));
+ ZigValue *args_ptr = args_value->data.x_struct.fields[slice_ptr_index];
+ ZigValue *args_len_value = args_value->data.x_struct.fields[slice_len_index];
+ size_t args_len = bigint_as_usize(&args_len_value->data.x_bigint);
+
+ FnTypeId fn_type_id = {};
+ fn_type_id.return_type = return_type;
+ fn_type_id.param_info = heap::c_allocator.allocate<FnTypeParamInfo>(args_len);
+ fn_type_id.param_count = args_len;
+ fn_type_id.next_param_index = args_len;
+ fn_type_id.is_var_args = is_var_args;
+ fn_type_id.cc = cc;
+ fn_type_id.alignment = bigint_as_u32(alignment);
+
+ assert(args_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);
+ assert(args_ptr->data.x_ptr.data.base_array.elem_index == 0);
+ ZigValue *args_arr = args_ptr->data.x_ptr.data.base_array.array_val;
+ assert(args_arr->special == ConstValSpecialStatic);
+ assert(args_arr->data.x_array.special == ConstArraySpecialNone);
+ for (size_t i = 0; i < args_len; i++) {
+ ZigValue *arg_value = &args_arr->data.x_array.data.s_none.elements[i];
+ assert(arg_value->type == ir_type_info_get_type(ira, "FnArg", nullptr));
+ FnTypeParamInfo *info = &fn_type_id.param_info[i];
+ Error err;
+ bool is_generic;
+ if ((err = get_const_field_bool(ira, source_instr->source_node, arg_value, "is_generic", 0, &is_generic)))
+ return ira->codegen->invalid_inst_gen->value->type;
+ if (is_generic) {
+ ir_add_error(ira, source_instr, buf_sprintf("TypeInfo.FnArg.is_generic must be false for @Type"));
+ return ira->codegen->invalid_inst_gen->value->type;
+ }
+ if ((err = get_const_field_bool(ira, source_instr->source_node, arg_value, "is_noalias", 1, &info->is_noalias)))
+ return ira->codegen->invalid_inst_gen->value->type;
+ ZigType *type = get_const_field_meta_type_optional(
+ ira, source_instr->source_node, arg_value, "arg_type", 2);
+ if (type == nullptr) {
+ ir_add_error(ira, source_instr, buf_sprintf("TypeInfo.FnArg.arg_type must be non-null for @Type"));
+ return ira->codegen->invalid_inst_gen->value->type;
+ }
+ info->type = type;
+ }
+
+ ZigType *entry = get_fn_type(ira->codegen, &fn_type_id);
+
+ switch (tagTypeId) {
+ case ZigTypeIdFn:
+ return entry;
+ case ZigTypeIdBoundFn: {
+ ZigType *bound_fn_entry = new_type_table_entry(ZigTypeIdBoundFn);
+ bound_fn_entry->name = *buf_sprintf("(bound %s)", buf_ptr(&entry->name));
+ bound_fn_entry->data.bound_fn.fn_type = entry;
+ return bound_fn_entry;
+ }
+ default:
+ zig_unreachable();
+ }
+ }
}
zig_unreachable();
}
@@ -26676,6 +26878,161 @@ static IrInstGen *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstSrcCmpxch
success_order, failure_order, instruction->is_weak, result_loc);
}
+static ErrorMsg *ir_eval_reduce(IrAnalyze *ira, IrInst *source_instr, ReduceOp op, ZigValue *value, ZigValue *out_value) {
+ assert(value->type->id == ZigTypeIdVector);
+ ZigType *scalar_type = value->type->data.vector.elem_type;
+ const size_t len = value->type->data.vector.len;
+ assert(len > 0);
+
+ out_value->type = scalar_type;
+ out_value->special = ConstValSpecialStatic;
+
+ if (scalar_type->id == ZigTypeIdBool) {
+ ZigValue *first_elem_val = &value->data.x_array.data.s_none.elements[0];
+
+ bool result = first_elem_val->data.x_bool;
+ for (size_t i = 1; i < len; i++) {
+ ZigValue *elem_val = &value->data.x_array.data.s_none.elements[i];
+
+ switch (op) {
+ case ReduceOp_and:
+ result = result && elem_val->data.x_bool;
+ if (!result) break; // Short circuit
+ break;
+ case ReduceOp_or:
+ result = result || elem_val->data.x_bool;
+ if (result) break; // Short circuit
+ break;
+ case ReduceOp_xor:
+ result = result != elem_val->data.x_bool;
+ break;
+ default:
+ zig_unreachable();
+ }
+ }
+
+ out_value->data.x_bool = result;
+ return nullptr;
+ }
+
+ if (op != ReduceOp_min && op != ReduceOp_max) {
+ ZigValue *first_elem_val = &value->data.x_array.data.s_none.elements[0];
+
+ copy_const_val(ira->codegen, out_value, first_elem_val);
+
+ for (size_t i = 1; i < len; i++) {
+ ZigValue *elem_val = &value->data.x_array.data.s_none.elements[i];
+
+ IrBinOp bin_op;
+ switch (op) {
+ case ReduceOp_and: bin_op = IrBinOpBinAnd; break;
+ case ReduceOp_or: bin_op = IrBinOpBinOr; break;
+ case ReduceOp_xor: bin_op = IrBinOpBinXor; break;
+ default: zig_unreachable();
+ }
+
+ ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type,
+ out_value, bin_op, elem_val, out_value);
+ if (msg != nullptr)
+ return msg;
+ }
+
+ return nullptr;
+ }
+
+ ZigValue *candidate_elem_val = &value->data.x_array.data.s_none.elements[0];
+
+ ZigValue *dummy_cmp_value = ira->codegen->pass1_arena->create<ZigValue>();
+ for (size_t i = 1; i < len; i++) {
+ ZigValue *elem_val = &value->data.x_array.data.s_none.elements[i];
+
+ IrBinOp bin_op;
+ switch (op) {
+ case ReduceOp_min: bin_op = IrBinOpCmpLessThan; break;
+ case ReduceOp_max: bin_op = IrBinOpCmpGreaterThan; break;
+ default: zig_unreachable();
+ }
+
+ ErrorMsg *msg = ir_eval_bin_op_cmp_scalar(ira, source_instr,
+ elem_val, bin_op, candidate_elem_val, dummy_cmp_value);
+ if (msg != nullptr)
+ return msg;
+
+ if (dummy_cmp_value->data.x_bool)
+ candidate_elem_val = elem_val;
+ }
+
+ ira->codegen->pass1_arena->destroy(dummy_cmp_value);
+ copy_const_val(ira->codegen, out_value, candidate_elem_val);
+
+ return nullptr;
+}
+
+static IrInstGen *ir_analyze_instruction_reduce(IrAnalyze *ira, IrInstSrcReduce *instruction) {
+ IrInstGen *op_inst = instruction->op->child;
+ if (type_is_invalid(op_inst->value->type))
+ return ira->codegen->invalid_inst_gen;
+
+ IrInstGen *value_inst = instruction->value->child;
+ if (type_is_invalid(value_inst->value->type))
+ return ira->codegen->invalid_inst_gen;
+
+ ZigType *value_type = value_inst->value->type;
+ if (value_type->id != ZigTypeIdVector) {
+ ir_add_error(ira, &value_inst->base,
+ buf_sprintf("expected vector type, found '%s'",
+ buf_ptr(&value_type->name)));
+ return ira->codegen->invalid_inst_gen;
+ }
+
+ ReduceOp op;
+ if (!ir_resolve_reduce_op(ira, op_inst, &op))
+ return ira->codegen->invalid_inst_gen;
+
+ ZigType *elem_type = value_type->data.vector.elem_type;
+ switch (elem_type->id) {
+ case ZigTypeIdInt:
+ break;
+ case ZigTypeIdBool:
+ if (op > ReduceOp_xor) {
+ ir_add_error(ira, &op_inst->base,
+ buf_sprintf("invalid operation for '%s' type",
+ buf_ptr(&elem_type->name)));
+ return ira->codegen->invalid_inst_gen;
+ } break;
+ case ZigTypeIdFloat:
+ if (op < ReduceOp_min) {
+ ir_add_error(ira, &op_inst->base,
+ buf_sprintf("invalid operation for '%s' type",
+ buf_ptr(&elem_type->name)));
+ return ira->codegen->invalid_inst_gen;
+ } break;
+ default:
+ // Vectors cannot have child types other than those listed above
+ zig_unreachable();
+ }
+
+ // special case zero bit types
+ switch (type_has_one_possible_value(ira->codegen, elem_type)) {
+ case OnePossibleValueInvalid:
+ return ira->codegen->invalid_inst_gen;
+ case OnePossibleValueYes:
+ return ir_const_move(ira, &instruction->base.base,
+ get_the_one_possible_value(ira->codegen, elem_type));
+ case OnePossibleValueNo:
+ break;
+ }
+
+ if (instr_is_comptime(value_inst)) {
+ IrInstGen *result = ir_const(ira, &instruction->base.base, elem_type);
+ if (ir_eval_reduce(ira, &instruction->base.base, op, value_inst->value, result->value))
+ return ira->codegen->invalid_inst_gen;
+ return result;
+ }
+
+ return ir_build_reduce_gen(ira, &instruction->base.base, op, value_inst, elem_type);
+}
+
static IrInstGen *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstSrcFence *instruction) {
IrInstGen *order_inst = instruction->order->child;
if (type_is_invalid(order_inst->value->type))
@@ -29835,6 +30192,7 @@ static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrIn
buf_write_value_bytes(ira->codegen, buf, val);
if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, result->value)))
return ira->codegen->invalid_inst_gen;
+ heap::c_allocator.deallocate(buf, src_size_bytes);
return result;
}
@@ -30872,6 +31230,8 @@ static IrInstGen *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstSrcBi
ira->codegen->is_big_endian,
int_type->data.integral.is_signed);
+ heap::c_allocator.deallocate(comptime_buf, buf_size);
+ heap::c_allocator.deallocate(result_buf, buf_size);
return result;
}
@@ -31424,6 +31784,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
return ir_analyze_instruction_cmpxchg(ira, (IrInstSrcCmpxchg *)instruction);
case IrInstSrcIdFence:
return ir_analyze_instruction_fence(ira, (IrInstSrcFence *)instruction);
+ case IrInstSrcIdReduce:
+ return ir_analyze_instruction_reduce(ira, (IrInstSrcReduce *)instruction);
case IrInstSrcIdTruncate:
return ir_analyze_instruction_truncate(ira, (IrInstSrcTruncate *)instruction);
case IrInstSrcIdIntCast:
@@ -31811,6 +32173,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) {
case IrInstGenIdNegation:
case IrInstGenIdNegationWrapping:
case IrInstGenIdWasmMemorySize:
+ case IrInstGenIdReduce:
return false;
case IrInstGenIdAsm:
@@ -31980,6 +32343,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
case IrInstSrcIdSpillEnd:
case IrInstSrcIdWasmMemorySize:
case IrInstSrcIdSrc:
+ case IrInstSrcIdReduce:
return false;
case IrInstSrcIdAsm:
diff --git a/src/stage1/ir_print.cpp b/src/stage1/ir_print.cpp
@@ -200,6 +200,8 @@ const char* ir_inst_src_type_str(IrInstSrcId id) {
return "SrcCmpxchg";
case IrInstSrcIdFence:
return "SrcFence";
+ case IrInstSrcIdReduce:
+ return "SrcReduce";
case IrInstSrcIdTruncate:
return "SrcTruncate";
case IrInstSrcIdIntCast:
@@ -436,6 +438,8 @@ const char* ir_inst_gen_type_str(IrInstGenId id) {
return "GenCmpxchg";
case IrInstGenIdFence:
return "GenFence";
+ case IrInstGenIdReduce:
+ return "GenReduce";
case IrInstGenIdTruncate:
return "GenTruncate";
case IrInstGenIdBoolNot:
@@ -1584,6 +1588,14 @@ static void ir_print_fence(IrPrintSrc *irp, IrInstSrcFence *instruction) {
fprintf(irp->f, ")");
}
+static void ir_print_reduce(IrPrintSrc *irp, IrInstSrcReduce *instruction) {
+ fprintf(irp->f, "@reduce(");
+ ir_print_other_inst_src(irp, instruction->op);
+ fprintf(irp->f, ", ");
+ ir_print_other_inst_src(irp, instruction->value);
+ fprintf(irp->f, ")");
+}
+
static const char *atomic_order_str(AtomicOrder order) {
switch (order) {
case AtomicOrderUnordered: return "Unordered";
@@ -1600,6 +1612,23 @@ static void ir_print_fence(IrPrintGen *irp, IrInstGenFence *instruction) {
fprintf(irp->f, "fence %s", atomic_order_str(instruction->order));
}
+static const char *reduce_op_str(ReduceOp op) {
+ switch (op) {
+ case ReduceOp_and: return "And";
+ case ReduceOp_or: return "Or";
+ case ReduceOp_xor: return "Xor";
+ case ReduceOp_min: return "Min";
+ case ReduceOp_max: return "Max";
+ }
+ zig_unreachable();
+}
+
+static void ir_print_reduce(IrPrintGen *irp, IrInstGenReduce *instruction) {
+ fprintf(irp->f, "@reduce(.%s, ", reduce_op_str(instruction->op));
+ ir_print_other_inst_gen(irp, instruction->value);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_truncate(IrPrintSrc *irp, IrInstSrcTruncate *instruction) {
fprintf(irp->f, "@truncate(");
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
case IrInstSrcIdFence:
ir_print_fence(irp, (IrInstSrcFence *)instruction);
break;
+ case IrInstSrcIdReduce:
+ ir_print_reduce(irp, (IrInstSrcReduce *)instruction);
+ break;
case IrInstSrcIdTruncate:
ir_print_truncate(irp, (IrInstSrcTruncate *)instruction);
break;
@@ -3097,6 +3129,9 @@ static void ir_print_inst_gen(IrPrintGen *irp, IrInstGen *instruction, bool trai
case IrInstGenIdFence:
ir_print_fence(irp, (IrInstGenFence *)instruction);
break;
+ case IrInstGenIdReduce:
+ ir_print_reduce(irp, (IrInstGenReduce *)instruction);
+ break;
case IrInstGenIdTruncate:
ir_print_truncate(irp, (IrInstGenTruncate *)instruction);
break;
diff --git a/src/stage1/os.cpp b/src/stage1/os.cpp
@@ -94,105 +94,6 @@ static clock_serv_t macos_monotonic_clock;
extern char **environ;
#endif
-#if defined(ZIG_OS_POSIX)
-static void populate_termination(Termination *term, int status) {
- if (WIFEXITED(status)) {
- term->how = TerminationIdClean;
- term->code = WEXITSTATUS(status);
- } else if (WIFSIGNALED(status)) {
- term->how = TerminationIdSignaled;
- term->code = WTERMSIG(status);
- } else if (WIFSTOPPED(status)) {
- term->how = TerminationIdStopped;
- term->code = WSTOPSIG(status);
- } else {
- term->how = TerminationIdUnknown;
- term->code = status;
- }
-}
-
-static void os_spawn_process_posix(ZigList<const char *> &args, Termination *term) {
- const char **argv = heap::c_allocator.allocate<const char *>(args.length + 1);
- for (size_t i = 0; i < args.length; i += 1) {
- argv[i] = args.at(i);
- }
- argv[args.length] = nullptr;
-
- pid_t pid;
- int rc = posix_spawnp(&pid, args.at(0), nullptr, nullptr, const_cast<char *const*>(argv), environ);
- if (rc != 0) {
- zig_panic("unable to spawn %s: %s", args.at(0), strerror(rc));
- }
-
- int status;
- waitpid(pid, &status, 0);
- populate_termination(term, status);
-}
-#endif
-
-#if defined(ZIG_OS_WINDOWS)
-
-static void os_windows_create_command_line(Buf *command_line, ZigList<const char *> &args) {
- buf_resize(command_line, 0);
- const char *prefix = "\"";
- for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) {
- const char *arg = args.at(arg_i);
- buf_append_str(command_line, prefix);
- prefix = " \"";
- size_t arg_len = strlen(arg);
- for (size_t c_i = 0; c_i < arg_len; c_i += 1) {
- if (arg[c_i] == '\"') {
- zig_panic("TODO");
- }
- buf_append_char(command_line, arg[c_i]);
- }
- buf_append_char(command_line, '\"');
- }
-}
-
-static void os_spawn_process_windows(ZigList<const char *> &args, Termination *term) {
- Buf command_line = BUF_INIT;
- os_windows_create_command_line(&command_line, args);
-
- PROCESS_INFORMATION piProcInfo = {0};
- STARTUPINFOW siStartInfo = {0};
- siStartInfo.cb = sizeof(STARTUPINFOW);
-
- Slice<uint8_t> exe_slice = str(args.at(0));
- auto exe_utf16_slice = Slice<WCHAR>::alloc(exe_slice.len + 1);
- exe_utf16_slice.ptr[utf8_to_utf16le(exe_utf16_slice.ptr, exe_slice)] = 0;
-
- auto command_line_utf16 = Slice<WCHAR>::alloc(buf_len(&command_line) + 1);
- command_line_utf16.ptr[utf8_to_utf16le(command_line_utf16.ptr, buf_to_slice(&command_line))] = 0;
-
- BOOL success = CreateProcessW(exe_utf16_slice.ptr, command_line_utf16.ptr, nullptr, nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr,
- &siStartInfo, &piProcInfo);
-
- if (!success) {
- zig_panic("CreateProcess failed. exe: %s command_line: %s", args.at(0), buf_ptr(&command_line));
- }
-
- WaitForSingleObject(piProcInfo.hProcess, INFINITE);
-
- DWORD exit_code;
- if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) {
- zig_panic("GetExitCodeProcess failed");
- }
- term->how = TerminationIdClean;
- term->code = exit_code;
-}
-#endif
-
-void os_spawn_process(ZigList<const char *> &args, Termination *term) {
-#if defined(ZIG_OS_WINDOWS)
- os_spawn_process_windows(args, term);
-#elif defined(ZIG_OS_POSIX)
- os_spawn_process_posix(args, term);
-#else
-#error "missing os_spawn_process implementation"
-#endif
-}
-
void os_path_dirname(Buf *full_path, Buf *out_dirname) {
return os_path_split(full_path, out_dirname, nullptr);
}
@@ -280,71 +181,27 @@ void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) {
buf_append_buf(out_full_path, basename);
}
-Error os_path_real(Buf *rel_path, Buf *out_abs_path) {
-#if defined(ZIG_OS_WINDOWS)
- PathSpace rel_path_space = slice_to_prefixed_file_w(buf_to_slice(rel_path));
- PathSpace out_abs_path_space;
-
- if (_wfullpath(&out_abs_path_space.data.items[0], &rel_path_space.data.items[0], PATH_MAX_WIDE) == nullptr) {
- zig_panic("_wfullpath failed");
- }
- utf16le_ptr_to_utf8(out_abs_path, &out_abs_path_space.data.items[0]);
- return ErrorNone;
-#elif defined(ZIG_OS_POSIX)
- buf_resize(out_abs_path, PATH_MAX + 1);
- char *result = realpath(buf_ptr(rel_path), buf_ptr(out_abs_path));
- if (!result) {
- int err = errno;
- if (err == EACCES) {
- return ErrorAccess;
- } else if (err == ENOENT) {
- return ErrorFileNotFound;
- } else if (err == ENOMEM) {
- return ErrorNoMem;
- } else {
- return ErrorFileSystem;
- }
- }
- buf_resize(out_abs_path, strlen(buf_ptr(out_abs_path)));
- return ErrorNone;
-#else
-#error "missing os_path_real implementation"
-#endif
-}
-
-#if defined(ZIG_OS_WINDOWS)
-// Ported from std/os/path.zig
-static bool isAbsoluteWindows(Slice<uint8_t> path) {
- if (path.ptr[0] == '/')
- return true;
-
- if (path.ptr[0] == '\\') {
- return true;
- }
- if (path.len < 3) {
- return false;
- }
- if (path.ptr[1] == ':') {
- if (path.ptr[2] == '/')
- return true;
- if (path.ptr[2] == '\\')
- return true;
- }
- return false;
-}
-#endif
-
-bool os_path_is_absolute(Buf *path) {
-#if defined(ZIG_OS_WINDOWS)
- return isAbsoluteWindows(buf_to_slice(path));
-#elif defined(ZIG_OS_POSIX)
- return buf_ptr(path)[0] == '/';
-#else
-#error "missing os_path_is_absolute implementation"
-#endif
-}
-#if defined(ZIG_OS_WINDOWS)
+#if defined(ZIG_OS_WINDOWS)
+// Ported from std/os/path.zig
+static bool isAbsoluteWindows(Slice<uint8_t> path) {
+ if (path.ptr[0] == '/')
+ return true;
+
+ if (path.ptr[0] == '\\') {
+ return true;
+ }
+ if (path.len < 3) {
+ return false;
+ }
+ if (path.ptr[1] == ':') {
+ if (path.ptr[2] == '/')
+ return true;
+ if (path.ptr[2] == '\\')
+ return true;
+ }
+ return false;
+}
enum WindowsPathKind {
WindowsPathKindNone,
@@ -687,7 +544,7 @@ static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) {
size_t max_size = 0;
for (size_t i = 0; i < paths_len; i += 1) {
Buf *p = paths_ptr[i];
- if (os_path_is_absolute(p)) {
+ if (buf_ptr(p)[0] == '/') {
first_index = i;
have_abs = true;
max_size = 0;
@@ -748,6 +605,7 @@ static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) {
Buf return_value = BUF_INIT;
buf_init_from_mem(&return_value, (char *)result_ptr, result_index);
+ heap::c_allocator.deallocate(result_ptr, result_len);
return return_value;
}
#endif
@@ -786,256 +644,6 @@ Error os_fetch_file(FILE *f, Buf *out_buf) {
zig_unreachable();
}
-Error os_file_exists(Buf *full_path, bool *result) {
-#if defined(ZIG_OS_WINDOWS)
- PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
- *result = GetFileAttributesW(&path_space.data.items[0]) != INVALID_FILE_ATTRIBUTES;
- return ErrorNone;
-#else
- *result = access(buf_ptr(full_path), F_OK) != -1;
- return ErrorNone;
-#endif
-}
-
-#if defined(ZIG_OS_POSIX)
-static Error os_exec_process_posix(ZigList<const char *> &args,
- Termination *term, Buf *out_stderr, Buf *out_stdout)
-{
- int stdin_pipe[2];
- int stdout_pipe[2];
- int stderr_pipe[2];
- int err_pipe[2];
-
- int err;
- if ((err = pipe(stdin_pipe)))
- zig_panic("pipe failed");
- if ((err = pipe(stdout_pipe)))
- zig_panic("pipe failed");
- if ((err = pipe(stderr_pipe)))
- zig_panic("pipe failed");
- if ((err = pipe(err_pipe)))
- zig_panic("pipe failed");
-
- pid_t pid = fork();
- if (pid == -1)
- zig_panic("fork failed: %s", strerror(errno));
- if (pid == 0) {
- // child
- if (dup2(stdin_pipe[0], STDIN_FILENO) == -1)
- zig_panic("dup2 failed");
-
- if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1)
- zig_panic("dup2 failed");
-
- if (dup2(stderr_pipe[1], STDERR_FILENO) == -1)
- zig_panic("dup2 failed");
-
- const char **argv = heap::c_allocator.allocate<const char *>(args.length + 1);
- argv[args.length] = nullptr;
- for (size_t i = 0; i < args.length; i += 1) {
- argv[i] = args.at(i);
- }
- execvp(argv[0], const_cast<char * const *>(argv));
- Error report_err = ErrorUnexpected;
- if (errno == ENOENT) {
- report_err = ErrorFileNotFound;
- }
- if (write(err_pipe[1], &report_err, sizeof(Error)) == -1) {
- zig_panic("write failed");
- }
- exit(1);
- } else {
- // parent
- close(stdin_pipe[0]);
- close(stdin_pipe[1]);
- close(stdout_pipe[1]);
- close(stderr_pipe[1]);
-
- int status;
- waitpid(pid, &status, 0);
- populate_termination(term, status);
-
- FILE *stdout_f = fdopen(stdout_pipe[0], "rb");
- FILE *stderr_f = fdopen(stderr_pipe[0], "rb");
- Error err1 = os_fetch_file(stdout_f, out_stdout);
- Error err2 = os_fetch_file(stderr_f, out_stderr);
-
- fclose(stdout_f);
- fclose(stderr_f);
-
- if (err1) return err1;
- if (err2) return err2;
-
- Error child_err = ErrorNone;
- if (write(err_pipe[1], &child_err, sizeof(Error)) == -1) {
- zig_panic("write failed");
- }
- close(err_pipe[1]);
- if (read(err_pipe[0], &child_err, sizeof(Error)) == -1) {
- zig_panic("write failed");
- }
- close(err_pipe[0]);
- return child_err;
- }
-}
-#endif
-
-#if defined(ZIG_OS_WINDOWS)
-
-//static void win32_panic(const char *str) {
-// DWORD err = GetLastError();
-// LPSTR messageBuffer = nullptr;
-// FormatMessageA(
-// FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
-// NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
-// zig_panic(str, messageBuffer);
-// LocalFree(messageBuffer);
-//}
-
-static Error os_exec_process_windows(ZigList<const char *> &args,
- Termination *term, Buf *out_stderr, Buf *out_stdout)
-{
- Buf command_line = BUF_INIT;
- os_windows_create_command_line(&command_line, args);
-
- HANDLE g_hChildStd_IN_Rd = NULL;
- HANDLE g_hChildStd_IN_Wr = NULL;
- HANDLE g_hChildStd_OUT_Rd = NULL;
- HANDLE g_hChildStd_OUT_Wr = NULL;
- HANDLE g_hChildStd_ERR_Rd = NULL;
- HANDLE g_hChildStd_ERR_Wr = NULL;
-
- SECURITY_ATTRIBUTES saAttr;
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
- saAttr.bInheritHandle = TRUE;
- saAttr.lpSecurityDescriptor = NULL;
-
- if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) {
- zig_panic("StdoutRd CreatePipe");
- }
-
- if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
- zig_panic("Stdout SetHandleInformation");
- }
-
- if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) {
- zig_panic("stderr CreatePipe");
- }
-
- if (!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) {
- zig_panic("stderr SetHandleInformation");
- }
-
- if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) {
- zig_panic("Stdin CreatePipe");
- }
-
- if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) {
- zig_panic("Stdin SetHandleInformation");
- }
-
-
- PROCESS_INFORMATION piProcInfo = {0};
- STARTUPINFO siStartInfo = {0};
- siStartInfo.cb = sizeof(STARTUPINFO);
- siStartInfo.hStdError = g_hChildStd_ERR_Wr;
- siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
- siStartInfo.hStdInput = g_hChildStd_IN_Rd;
- siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
-
- const char *exe = args.at(0);
- BOOL success = CreateProcess(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr,
- &siStartInfo, &piProcInfo);
-
- if (!success) {
- if (GetLastError() == ERROR_FILE_NOT_FOUND) {
- CloseHandle(piProcInfo.hProcess);
- CloseHandle(piProcInfo.hThread);
- return ErrorFileNotFound;
- }
- zig_panic("CreateProcess failed. exe: %s command_line: %s", exe, buf_ptr(&command_line));
- }
-
- if (!CloseHandle(g_hChildStd_IN_Wr)) {
- zig_panic("stdinwr closehandle");
- }
-
- CloseHandle(g_hChildStd_IN_Rd);
- CloseHandle(g_hChildStd_ERR_Wr);
- CloseHandle(g_hChildStd_OUT_Wr);
-
- static const size_t BUF_SIZE = 4 * 1024;
- {
- DWORD dwRead;
- char chBuf[BUF_SIZE];
-
- buf_resize(out_stdout, 0);
- for (;;) {
- success = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUF_SIZE, &dwRead, NULL);
- if (!success || dwRead == 0) break;
-
- buf_append_mem(out_stdout, chBuf, dwRead);
- }
- CloseHandle(g_hChildStd_OUT_Rd);
- }
- {
- DWORD dwRead;
- char chBuf[BUF_SIZE];
-
- buf_resize(out_stderr, 0);
- for (;;) {
- success = ReadFile( g_hChildStd_ERR_Rd, chBuf, BUF_SIZE, &dwRead, NULL);
- if (!success || dwRead == 0) break;
-
- buf_append_mem(out_stderr, chBuf, dwRead);
- }
- CloseHandle(g_hChildStd_ERR_Rd);
- }
-
- WaitForSingleObject(piProcInfo.hProcess, INFINITE);
-
- DWORD exit_code;
- if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) {
- zig_panic("GetExitCodeProcess failed");
- }
- term->how = TerminationIdClean;
- term->code = exit_code;
-
- CloseHandle(piProcInfo.hProcess);
- CloseHandle(piProcInfo.hThread);
-
- return ErrorNone;
-}
-#endif
-
-Error os_execv(const char *exe, const char **argv) {
-#if defined(ZIG_OS_WINDOWS)
- return ErrorUnsupportedOperatingSystem;
-#else
- execv(exe, (char *const *)argv);
- switch (errno) {
- case ENOMEM:
- return ErrorSystemResources;
- case EIO:
- return ErrorFileSystem;
- default:
- return ErrorUnexpected;
- }
-#endif
-}
-
-Error os_exec_process(ZigList<const char *> &args,
- Termination *term, Buf *out_stderr, Buf *out_stdout)
-{
-#if defined(ZIG_OS_WINDOWS)
- return os_exec_process_windows(args, term, out_stderr, out_stdout);
-#elif defined(ZIG_OS_POSIX)
- return os_exec_process_posix(args, term, out_stderr, out_stdout);
-#else
-#error "missing os_exec_process implementation"
-#endif
-}
-
Error os_write_file(Buf *full_path, Buf *contents) {
#if defined(ZIG_OS_WINDOWS)
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) {
}
}
-Error os_dump_file(Buf *src_path, FILE *dest_file) {
- Error err;
-
-#if defined(ZIG_OS_WINDOWS)
- PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(src_path));
- FILE *src_f = _wfopen(&path_space.data.items[0], L"rb");
-#else
- FILE *src_f = fopen(buf_ptr(src_path), "rb");
-#endif
- if (!src_f) {
- int err = errno;
- if (err == ENOENT) {
- return ErrorFileNotFound;
- } else if (err == EACCES || err == EPERM) {
- return ErrorAccess;
- } else {
- return ErrorFileSystem;
- }
- }
- copy_open_files(src_f, dest_file);
- if ((err = copy_open_files(src_f, dest_file))) {
- fclose(src_f);
- return err;
- }
-
- fclose(src_f);
- return ErrorNone;
-}
-
#if defined(ZIG_OS_WINDOWS)
static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) {
mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime;
@@ -1116,88 +695,6 @@ static FILETIME windows_os_timestamp_to_filetime(OsTimeStamp mtime) {
}
#endif
-static Error set_file_times(OsFile file, OsTimeStamp ts) {
-#if defined(ZIG_OS_WINDOWS)
- FILETIME ft = windows_os_timestamp_to_filetime(ts);
- if (SetFileTime(file, nullptr, &ft, &ft) == 0) {
- return ErrorUnexpected;
- }
- return ErrorNone;
-#else
- struct timespec times[2] = {
- { (time_t)ts.sec, (long)ts.nsec },
- { (time_t)ts.sec, (long)ts.nsec },
- };
- if (futimens(file, times) == -1) {
- switch (errno) {
- case EBADF:
- zig_panic("futimens EBADF");
- default:
- return ErrorUnexpected;
- }
- }
- return ErrorNone;
-#endif
-}
-
-Error os_update_file(Buf *src_path, Buf *dst_path) {
- Error err;
-
- OsFile src_file;
- OsFileAttr src_attr;
- if ((err = os_file_open_r(src_path, &src_file, &src_attr))) {
- return err;
- }
-
- OsFile dst_file;
- OsFileAttr dst_attr;
- if ((err = os_file_open_w(dst_path, &dst_file, &dst_attr, src_attr.mode))) {
- os_file_close(&src_file);
- return err;
- }
-
- if (src_attr.size == dst_attr.size &&
- src_attr.mode == dst_attr.mode &&
- src_attr.mtime.sec == dst_attr.mtime.sec &&
- src_attr.mtime.nsec == dst_attr.mtime.nsec)
- {
- os_file_close(&src_file);
- os_file_close(&dst_file);
- return ErrorNone;
- }
-#if defined(ZIG_OS_WINDOWS)
- if (SetEndOfFile(dst_file) == 0) {
- return ErrorUnexpected;
- }
-#else
- if (ftruncate(dst_file, 0) == -1) {
- return ErrorUnexpected;
- }
-#endif
-#if defined(ZIG_OS_WINDOWS)
- FILE *src_libc_file = _fdopen(_open_osfhandle((intptr_t)src_file, _O_RDONLY), "rb");
- FILE *dst_libc_file = _fdopen(_open_osfhandle((intptr_t)dst_file, 0), "wb");
-#else
- FILE *src_libc_file = fdopen(src_file, "rb");
- FILE *dst_libc_file = fdopen(dst_file, "wb");
-#endif
- assert(src_libc_file);
- assert(dst_libc_file);
-
- if ((err = copy_open_files(src_libc_file, dst_libc_file))) {
- fclose(src_libc_file);
- fclose(dst_libc_file);
- return err;
- }
- if (fflush(dst_libc_file) == -1) {
- return ErrorUnexpected;
- }
- err = set_file_times(dst_file, src_attr.mtime);
- fclose(src_libc_file);
- fclose(dst_libc_file);
- return err;
-}
-
Error os_copy_file(Buf *src_path, Buf *dest_path) {
#if defined(ZIG_OS_WINDOWS)
PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path));
@@ -1358,14 +855,6 @@ bool os_stderr_tty(void) {
#endif
}
-Error os_delete_file(Buf *path) {
- if (remove(buf_ptr(path))) {
- return ErrorFileSystem;
- } else {
- return ErrorNone;
- }
-}
-
Error os_rename(Buf *src_path, Buf *dest_path) {
if (buf_eql_buf(src_path, dest_path)) {
return ErrorNone;
@@ -1384,30 +873,6 @@ Error os_rename(Buf *src_path, Buf *dest_path) {
return ErrorNone;
}
-OsTimeStamp os_timestamp_calendar(void) {
- OsTimeStamp result;
-#if defined(ZIG_OS_WINDOWS)
- FILETIME ft;
- GetSystemTimeAsFileTime(&ft);
- windows_filetime_to_os_timestamp(&ft, &result);
-#elif defined(__MACH__)
- mach_timespec_t mts;
-
- kern_return_t err = clock_get_time(macos_calendar_clock, &mts);
- assert(!err);
-
- result.sec = mts.tv_sec;
- result.nsec = mts.tv_nsec;
-#else
- struct timespec tms;
- clock_gettime(CLOCK_REALTIME, &tms);
-
- result.sec = tms.tv_sec;
- result.nsec = tms.tv_nsec;
-#endif
- return result;
-}
-
OsTimeStamp os_timestamp_monotonic(void) {
OsTimeStamp result;
#if defined(ZIG_OS_WINDOWS)
@@ -1501,49 +966,8 @@ Error os_make_dir(Buf *path) {
#endif
}
-static void init_rand() {
-#if defined(ZIG_OS_WINDOWS)
- char bytes[sizeof(unsigned)];
- unsigned seed;
- RtlGenRandom(bytes, sizeof(unsigned));
- memcpy(&seed, bytes, sizeof(unsigned));
- srand(seed);
-#elif defined(ZIG_OS_LINUX)
- unsigned char *ptr_random = (unsigned char*)getauxval(AT_RANDOM);
- unsigned seed;
- memcpy(&seed, ptr_random, sizeof(seed));
- srand(seed);
-#elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD)
- unsigned seed;
- size_t len = sizeof(seed);
- int mib[2] = { CTL_KERN, KERN_ARND };
- if (sysctl(mib, 2, &seed, &len, NULL, 0) != 0) {
- zig_panic("unable to query random data from sysctl");
- }
- srand(seed);
-#else
- int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC);
- if (fd == -1) {
- zig_panic("unable to open /dev/urandom");
- }
- char bytes[sizeof(unsigned)];
- ssize_t amt_read;
- while ((amt_read = read(fd, bytes, sizeof(unsigned))) == -1) {
- if (errno == EINTR) continue;
- zig_panic("unable to read /dev/urandom");
- }
- if (amt_read != sizeof(unsigned)) {
- zig_panic("unable to read enough bytes from /dev/urandom");
- }
- close(fd);
- unsigned seed;
- memcpy(&seed, bytes, sizeof(unsigned));
- srand(seed);
-#endif
-}
int os_init(void) {
- init_rand();
#if defined(ZIG_OS_WINDOWS)
_setmode(fileno(stdout), _O_BINARY);
_setmode(fileno(stderr), _O_BINARY);
@@ -1580,71 +1004,6 @@ int os_init(void) {
return 0;
}
-Error os_self_exe_path(Buf *out_path) {
-#if defined(ZIG_OS_WINDOWS)
- PathSpace path_space;
- DWORD copied_amt = GetModuleFileNameW(nullptr, &path_space.data.items[0], PATH_MAX_WIDE);
- if (copied_amt <= 0) {
- return ErrorFileNotFound;
- }
- utf16le_ptr_to_utf8(out_path, &path_space.data.items[0]);
- return ErrorNone;
-
-#elif defined(ZIG_OS_DARWIN)
- // How long is the executable's path?
- uint32_t u32_len = 0;
- int ret1 = _NSGetExecutablePath(nullptr, &u32_len);
- assert(ret1 != 0);
-
- Buf *tmp = buf_alloc_fixed(u32_len);
-
- // Fill the executable path.
- int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len);
- assert(ret2 == 0);
-
- // According to libuv project, PATH_MAX*2 works around a libc bug where
- // the resolved path is sometimes bigger than PATH_MAX.
- buf_resize(out_path, PATH_MAX*2);
- char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path));
- if (!real_path) {
- buf_init_from_buf(out_path, tmp);
- return ErrorNone;
- }
-
- // Resize out_path for the correct length.
- buf_resize(out_path, strlen(buf_ptr(out_path)));
-
- return ErrorNone;
-#elif defined(ZIG_OS_LINUX)
- buf_resize(out_path, PATH_MAX);
- ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path));
- if (amt == -1) {
- return ErrorUnexpected;
- }
- buf_resize(out_path, amt);
- return ErrorNone;
-#elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_DRAGONFLY)
- buf_resize(out_path, PATH_MAX);
- int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
- size_t cb = PATH_MAX;
- if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) {
- return ErrorUnexpected;
- }
- buf_resize(out_path, cb - 1);
- return ErrorNone;
-#elif defined(ZIG_OS_NETBSD)
- buf_resize(out_path, PATH_MAX);
- int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
- size_t cb = PATH_MAX;
- if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) {
- return ErrorUnexpected;
- }
- buf_resize(out_path, cb - 1);
- return ErrorNone;
-#endif
- return ErrorFileNotFound;
-}
-
#define VT_RED "\x1b[31;1m"
#define VT_GREEN "\x1b[32;1m"
#define VT_CYAN "\x1b[36;1m"
@@ -1954,392 +1313,3 @@ PathSpace slice_to_prefixed_file_w(Slice<uint8_t> path) {
return path_space;
}
#endif
-
-// Ported from std.os.getAppDataDir
-Error os_get_app_data_dir(Buf *out_path, const char *appname) {
-#if defined(ZIG_OS_WINDOWS)
- WCHAR *dir_path_ptr;
- switch (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &dir_path_ptr)) {
- case S_OK:
- // defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));
- utf16le_ptr_to_utf8(out_path, dir_path_ptr);
- CoTaskMemFree(dir_path_ptr);
- buf_appendf(out_path, "\\%s", appname);
- return ErrorNone;
- case E_OUTOFMEMORY:
- return ErrorNoMem;
- default:
- return ErrorUnexpected;
- }
- zig_unreachable();
-#elif defined(ZIG_OS_DARWIN)
- const char *home_dir = getenv("HOME");
- if (home_dir == nullptr) {
- // TODO use /etc/passwd
- return ErrorFileNotFound;
- }
- buf_resize(out_path, 0);
- buf_appendf(out_path, "%s/Library/Application Support/%s", home_dir, appname);
- return ErrorNone;
-#elif defined(ZIG_OS_POSIX)
- const char *cache_dir = getenv("XDG_CACHE_HOME");
- if (cache_dir == nullptr) {
- cache_dir = getenv("HOME");
- if (cache_dir == nullptr) {
- // TODO use /etc/passwd
- return ErrorFileNotFound;
- }
- if (cache_dir[0] == 0) {
- return ErrorFileNotFound;
- }
- buf_init_from_str(out_path, cache_dir);
- if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') {
- buf_append_char(out_path, '/');
- }
- buf_appendf(out_path, ".cache/%s", appname);
- } else {
- if (cache_dir[0] == 0) {
- return ErrorFileNotFound;
- }
- buf_init_from_str(out_path, cache_dir);
- if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') {
- buf_append_char(out_path, '/');
- }
- buf_appendf(out_path, "%s", appname);
- }
- return ErrorNone;
-#endif
-}
-
-#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY)
-static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, void *data) {
- ZigList<Buf *> *libs = reinterpret_cast< ZigList<Buf *> *>(data);
- if (info->dlpi_name[0] == '/') {
- libs->append(buf_create_from_str(info->dlpi_name));
- }
- return 0;
-}
-#endif
-
-Error os_self_exe_shared_libs(ZigList<Buf *> &paths) {
-#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY)
- paths.resize(0);
- dl_iterate_phdr(self_exe_shared_libs_callback, &paths);
- return ErrorNone;
-#elif defined(ZIG_OS_DARWIN)
- paths.resize(0);
- uint32_t img_count = _dyld_image_count();
- for (uint32_t i = 0; i != img_count; i += 1) {
- const char *name = _dyld_get_image_name(i);
- paths.append(buf_create_from_str(name));
- }
- return ErrorNone;
-#elif defined(ZIG_OS_WINDOWS)
- // zig is built statically on windows, so we can return an empty list
- paths.resize(0);
- return ErrorNone;
-#else
-#error unimplemented
-#endif
-}
-
-Error os_file_open_rw(Buf *full_path, OsFile *out_file, OsFileAttr *attr, bool need_write, uint32_t mode) {
-#if defined(ZIG_OS_WINDOWS)
- PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
- HANDLE result = CreateFileW(&path_space.data.items[0],
- need_write ? (GENERIC_READ|GENERIC_WRITE) : GENERIC_READ,
- need_write ? 0 : FILE_SHARE_READ,
- nullptr,
- need_write ? OPEN_ALWAYS : OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, nullptr);
-
- if (result == INVALID_HANDLE_VALUE) {
- DWORD err = GetLastError();
- switch (err) {
- case ERROR_SHARING_VIOLATION:
- return ErrorSharingViolation;
- case ERROR_ALREADY_EXISTS:
- return ErrorPathAlreadyExists;
- case ERROR_FILE_EXISTS:
- return ErrorPathAlreadyExists;
- case ERROR_FILE_NOT_FOUND:
- return ErrorFileNotFound;
- case ERROR_PATH_NOT_FOUND:
- return ErrorFileNotFound;
- case ERROR_ACCESS_DENIED:
- return ErrorAccess;
- case ERROR_PIPE_BUSY:
- return ErrorPipeBusy;
- default:
- return ErrorUnexpected;
- }
- }
- *out_file = result;
-
- if (attr != nullptr) {
- BY_HANDLE_FILE_INFORMATION file_info;
- if (!GetFileInformationByHandle(result, &file_info)) {
- CloseHandle(result);
- return ErrorUnexpected;
- }
- windows_filetime_to_os_timestamp(&file_info.ftLastWriteTime, &attr->mtime);
- attr->inode = (((uint64_t)file_info.nFileIndexHigh) << 32) | file_info.nFileIndexLow;
- attr->mode = 0;
- attr->size = (((uint64_t)file_info.nFileSizeHigh) << 32) | file_info.nFileSizeLow;
- }
-
- return ErrorNone;
-#else
- for (;;) {
- int fd = open(buf_ptr(full_path),
- need_write ? (O_RDWR|O_CLOEXEC|O_CREAT) : (O_RDONLY|O_CLOEXEC), mode);
- if (fd == -1) {
- switch (errno) {
- case EINTR:
- continue;
- case EINVAL:
- zig_unreachable();
- case EFAULT:
- zig_unreachable();
- case EACCES:
- case EPERM:
- return ErrorAccess;
- case EISDIR:
- return ErrorIsDir;
- case ENOENT:
- return ErrorFileNotFound;
- default:
- return ErrorFileSystem;
- }
- }
- struct stat statbuf;
- if (fstat(fd, &statbuf) == -1) {
- close(fd);
- return ErrorFileSystem;
- }
- if (S_ISDIR(statbuf.st_mode)) {
- close(fd);
- return ErrorIsDir;
- }
- *out_file = fd;
-
- if (attr != nullptr) {
- attr->inode = statbuf.st_ino;
-#if defined(ZIG_OS_DARWIN)
- attr->mtime.sec = statbuf.st_mtimespec.tv_sec;
- attr->mtime.nsec = statbuf.st_mtimespec.tv_nsec;
-#else
- attr->mtime.sec = statbuf.st_mtim.tv_sec;
- attr->mtime.nsec = statbuf.st_mtim.tv_nsec;
-#endif
- attr->mode = statbuf.st_mode;
- attr->size = statbuf.st_size;
- }
- return ErrorNone;
- }
-#endif
-}
-
-Error os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr) {
- return os_file_open_rw(full_path, out_file, attr, false, 0);
-}
-
-Error os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode) {
- return os_file_open_rw(full_path, out_file, attr, true, mode);
-}
-
-Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) {
-#if defined(ZIG_OS_WINDOWS)
- PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path));
- for (;;) {
- HANDLE result = CreateFileW(&path_space.data.items[0], GENERIC_READ | GENERIC_WRITE,
- 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
-
- if (result == INVALID_HANDLE_VALUE) {
- DWORD err = GetLastError();
- switch (err) {
- case ERROR_SHARING_VIOLATION:
- // TODO wait for the lock instead of sleeping
- Sleep(10);
- continue;
- case ERROR_ALREADY_EXISTS:
- return ErrorPathAlreadyExists;
- case ERROR_FILE_EXISTS:
- return ErrorPathAlreadyExists;
- case ERROR_FILE_NOT_FOUND:
- return ErrorFileNotFound;
- case ERROR_PATH_NOT_FOUND:
- return ErrorFileNotFound;
- case ERROR_ACCESS_DENIED:
- return ErrorAccess;
- case ERROR_PIPE_BUSY:
- return ErrorPipeBusy;
- default:
- return ErrorUnexpected;
- }
- }
- *out_file = result;
- return ErrorNone;
- }
-#else
- int fd;
- for (;;) {
- fd = open(buf_ptr(full_path), O_RDWR|O_CLOEXEC|O_CREAT, 0666);
- if (fd == -1) {
- switch (errno) {
- case EINTR:
- continue;
- case EINVAL:
- zig_unreachable();
- case EFAULT:
- zig_unreachable();
- case EACCES:
- case EPERM:
- return ErrorAccess;
- case EISDIR:
- return ErrorIsDir;
- case ENOENT:
- return ErrorFileNotFound;
- case ENOTDIR:
- return ErrorNotDir;
- default:
- return ErrorFileSystem;
- }
- }
- break;
- }
- for (;;) {
- struct flock lock;
- lock.l_type = F_WRLCK;
- lock.l_whence = SEEK_SET;
- lock.l_start = 0;
- lock.l_len = 0;
- if (fcntl(fd, F_SETLKW, &lock) == -1) {
- switch (errno) {
- case EINTR:
- continue;
- case EBADF:
- zig_unreachable();
- case EFAULT:
- zig_unreachable();
- case EINVAL:
- zig_unreachable();
- default:
- close(fd);
- return ErrorFileSystem;
- }
- }
- break;
- }
- *out_file = fd;
- return ErrorNone;
-#endif
-}
-
-Error os_file_read(OsFile file, void *ptr, size_t *len) {
-#if defined(ZIG_OS_WINDOWS)
- DWORD amt_read;
- if (ReadFile(file, ptr, *len, &amt_read, nullptr) == 0)
- return ErrorUnexpected;
- *len = amt_read;
- return ErrorNone;
-#else
- for (;;) {
- ssize_t rc = read(file, ptr, *len);
- if (rc == -1) {
- switch (errno) {
- case EINTR:
- continue;
- case EBADF:
- zig_unreachable();
- case EFAULT:
- zig_unreachable();
- case EISDIR:
- return ErrorIsDir;
- default:
- return ErrorFileSystem;
- }
- }
- *len = rc;
- return ErrorNone;
- }
-#endif
-}
-
-Error os_file_read_all(OsFile file, Buf *contents) {
- Error err;
- size_t index = 0;
- for (;;) {
- size_t amt = buf_len(contents) - index;
-
- if (amt < 4096) {
- buf_resize(contents, buf_len(contents) + (4096 - amt));
- amt = buf_len(contents) - index;
- }
-
- if ((err = os_file_read(file, buf_ptr(contents) + index, &amt)))
- return err;
-
- if (amt == 0) {
- buf_resize(contents, index);
- return ErrorNone;
- }
-
- index += amt;
- }
-}
-
-Error os_file_overwrite(OsFile file, Buf *contents) {
-#if defined(ZIG_OS_WINDOWS)
- if (SetFilePointer(file, 0, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
- return ErrorFileSystem;
- if (!SetEndOfFile(file))
- return ErrorFileSystem;
- DWORD bytes_written;
- if (!WriteFile(file, buf_ptr(contents), buf_len(contents), &bytes_written, nullptr))
- return ErrorFileSystem;
- return ErrorNone;
-#else
- if (lseek(file, 0, SEEK_SET) == -1)
- return ErrorUnexpectedSeekFailure;
- if (ftruncate(file, 0) == -1)
- return ErrorUnexpectedFileTruncationFailure;
- for (;;) {
- if (write(file, buf_ptr(contents), buf_len(contents)) == -1) {
- switch (errno) {
- case EINTR:
- continue;
- case EINVAL:
- zig_unreachable();
- case EBADF:
- zig_unreachable();
- case EFAULT:
- zig_unreachable();
- case EDQUOT:
- return ErrorDiskQuota;
- case ENOSPC:
- return ErrorDiskSpace;
- case EFBIG:
- return ErrorFileTooBig;
- case EIO:
- return ErrorFileSystem;
- case EPERM:
- return ErrorAccess;
- default:
- return ErrorUnexpectedWriteFailure;
- }
- }
- return ErrorNone;
- }
-#endif
-}
-
-void os_file_close(OsFile *file) {
-#if defined(ZIG_OS_WINDOWS)
- CloseHandle(*file);
- *file = NULL;
-#else
- close(*file);
- *file = -1;
-#endif
-}
diff --git a/src/stage1/os.hpp b/src/stage1/os.hpp
@@ -70,66 +70,25 @@ enum TermColor {
TermColorReset,
};
-enum TerminationId {
- TerminationIdClean,
- TerminationIdSignaled,
- TerminationIdStopped,
- TerminationIdUnknown,
-};
-
-struct Termination {
- TerminationId how;
- int code;
-};
-
-#if defined(ZIG_OS_WINDOWS)
-#define OsFile void *
-#else
-#define OsFile int
-#endif
-
struct OsTimeStamp {
int64_t sec;
int64_t nsec;
};
-struct OsFileAttr {
- OsTimeStamp mtime;
- uint64_t size;
- uint64_t inode;
- uint32_t mode;
-};
-
int os_init(void);
-void os_spawn_process(ZigList<const char *> &args, Termination *term);
-Error os_exec_process(ZigList<const char *> &args,
- Termination *term, Buf *out_stderr, Buf *out_stdout);
-Error os_execv(const char *exe, const char **argv);
-
void os_path_dirname(Buf *full_path, Buf *out_dirname);
void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename);
void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname);
void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path);
-Error os_path_real(Buf *rel_path, Buf *out_abs_path);
Buf os_path_resolve(Buf **paths_ptr, size_t paths_len);
bool os_path_is_absolute(Buf *path);
Error ATTRIBUTE_MUST_USE os_make_path(Buf *path);
Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path);
-Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr);
-Error ATTRIBUTE_MUST_USE os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode);
-Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file);
-Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len);
-Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents);
-Error ATTRIBUTE_MUST_USE os_file_overwrite(OsFile file, Buf *contents);
-void os_file_close(OsFile *file);
-
Error ATTRIBUTE_MUST_USE os_write_file(Buf *full_path, Buf *contents);
Error ATTRIBUTE_MUST_USE os_copy_file(Buf *src_path, Buf *dest_path);
-Error ATTRIBUTE_MUST_USE os_update_file(Buf *src_path, Buf *dest_path);
-Error ATTRIBUTE_MUST_USE os_dump_file(Buf *src_path, FILE *dest_file);
Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents);
Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents);
@@ -139,22 +98,11 @@ Error ATTRIBUTE_MUST_USE os_get_cwd(Buf *out_cwd);
bool os_stderr_tty(void);
void os_stderr_set_color(TermColor color);
-Error os_delete_file(Buf *path);
-
-Error ATTRIBUTE_MUST_USE os_file_exists(Buf *full_path, bool *result);
-
Error os_rename(Buf *src_path, Buf *dest_path);
OsTimeStamp os_timestamp_monotonic(void);
-OsTimeStamp os_timestamp_calendar(void);
bool os_is_sep(uint8_t c);
-Error ATTRIBUTE_MUST_USE os_self_exe_path(Buf *out_path);
-
-Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname);
-
-Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths);
-
const size_t PATH_MAX_WIDE = 32767;
struct PathSpace {
diff --git a/src/stage1/zig0.cpp b/src/stage1/zig0.cpp
@@ -430,6 +430,16 @@ int main(int argc, char **argv) {
return print_error_usage(arg0);
}
+ if (override_lib_dir == nullptr) {
+ fprintf(stderr, "missing --override-lib-dir\n");
+ return print_error_usage(arg0);
+ }
+
+ if (emit_bin_path == nullptr) {
+ fprintf(stderr, "missing -femit-bin=\n");
+ return print_error_usage(arg0);
+ }
+
ZigStage1 *stage1 = zig_stage1_create(optimize_mode,
nullptr, 0,
in_file, strlen(in_file),
diff --git a/src/target.zig b/src/target.zig
@@ -130,7 +130,6 @@ pub fn osRequiresLibC(target: std.Target) bool {
pub fn libcNeedsLibUnwind(target: std.Target) bool {
return switch (target.os.tag) {
- .windows,
.macosx,
.ios,
.watchos,
@@ -138,6 +137,7 @@ pub fn libcNeedsLibUnwind(target: std.Target) bool {
.freestanding,
=> false,
+ .windows => target.abi != .msvc,
else => true,
};
}
diff --git a/src/test.zig b/src/test.zig
@@ -56,6 +56,12 @@ pub const TestContext = struct {
},
};
+ pub const File = struct {
+ /// Contents of the importable file. Doesn't yet support incremental updates.
+ src: [:0]const u8,
+ path: []const u8,
+ };
+
pub const TestType = enum {
Zig,
ZIR,
@@ -78,6 +84,8 @@ pub const TestContext = struct {
extension: TestType,
cbe: bool = false,
+ files: std.ArrayList(File),
+
/// Adds a subcase in which the module is updated with `src`, and the
/// resulting ZIR is validated against `result`.
pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
@@ -156,6 +164,7 @@ pub const TestContext = struct {
.updates = std.ArrayList(Update).init(ctx.cases.allocator),
.output_mode = .Exe,
.extension = T,
+ .files = std.ArrayList(File).init(ctx.cases.allocator),
}) catch unreachable;
return &ctx.cases.items[ctx.cases.items.len - 1];
}
@@ -182,6 +191,7 @@ pub const TestContext = struct {
.updates = std.ArrayList(Update).init(ctx.cases.allocator),
.output_mode = .Obj,
.extension = T,
+ .files = std.ArrayList(File).init(ctx.cases.allocator),
}) catch unreachable;
return &ctx.cases.items[ctx.cases.items.len - 1];
}
@@ -204,6 +214,7 @@ pub const TestContext = struct {
.output_mode = .Obj,
.extension = T,
.cbe = true,
+ .files = std.ArrayList(File).init(ctx.cases.allocator),
}) catch unreachable;
return &ctx.cases.items[ctx.cases.items.len - 1];
}
@@ -505,6 +516,10 @@ pub const TestContext = struct {
});
defer comp.destroy();
+ for (case.files.items) |file| {
+ try tmp.dir.writeFile(file.path, file.src);
+ }
+
for (case.updates.items) |update, update_index| {
var update_node = root_node.start("update", 3);
update_node.activate();
diff --git a/src/translate_c.zig b/src/translate_c.zig
@@ -5527,58 +5527,41 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
const ParseError = Error || error{ParseError};
fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
- const node = try parseCPrefixOpExpr(c, m, scope);
- switch (m.next().?) {
- .QuestionMark => {
- // must come immediately after expr
- _ = try appendToken(c, .RParen, ")");
- const if_node = try transCreateNodeIf(c);
- if_node.condition = node;
- if_node.body = try parseCPrimaryExpr(c, m, scope);
- if (m.next().? != .Colon) {
- try m.fail(c, "unable to translate C expr: expected ':'", .{});
- return error.ParseError;
- }
- if_node.@"else" = try transCreateNodeElse(c);
- if_node.@"else".?.body = try parseCPrimaryExpr(c, m, scope);
- return &if_node.base;
- },
- .Comma => {
- _ = try appendToken(c, .Semicolon, ";");
- var block_scope = try Scope.Block.init(c, scope, true);
- defer block_scope.deinit();
-
- var last = node;
- while (true) {
- // suppress result
- const lhs = try transCreateNodeIdentifier(c, "_");
- const op_token = try appendToken(c, .Equal, "=");
- const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
- op_node.* = .{
- .base = .{ .tag = .Assign },
- .op_token = op_token,
- .lhs = lhs,
- .rhs = last,
- };
- try block_scope.statements.append(&op_node.base);
+ // TODO parseCAssignExpr here
+ const node = try parseCCondExpr(c, m, scope);
+ if (m.next().? != .Comma) {
+ m.i -= 1;
+ return node;
+ }
+ _ = try appendToken(c, .Semicolon, ";");
+ var block_scope = try Scope.Block.init(c, scope, true);
+ defer block_scope.deinit();
- last = try parseCPrefixOpExpr(c, m, scope);
- _ = try appendToken(c, .Semicolon, ";");
- if (m.next().? != .Comma) {
- m.i -= 1;
- break;
- }
- }
+ var last = node;
+ while (true) {
+ // suppress result
+ const lhs = try transCreateNodeIdentifier(c, "_");
+ const op_token = try appendToken(c, .Equal, "=");
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
+ op_node.* = .{
+ .base = .{ .tag = .Assign },
+ .op_token = op_token,
+ .lhs = lhs,
+ .rhs = last,
+ };
+ try block_scope.statements.append(&op_node.base);
- const break_node = try transCreateNodeBreak(c, block_scope.label, last);
- try block_scope.statements.append(&break_node.base);
- return try block_scope.complete(c);
- },
- else => {
+ last = try parseCCondExpr(c, m, scope);
+ _ = try appendToken(c, .Semicolon, ";");
+ if (m.next().? != .Comma) {
m.i -= 1;
- return node;
- },
+ break;
+ }
}
+
+ const break_node = try transCreateNodeBreak(c, block_scope.label, last);
+ try block_scope.statements.append(&break_node.base);
+ return try block_scope.complete(c);
}
fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node {
@@ -5805,7 +5788,7 @@ fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
return bytes[0..i];
}
-fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
const tok = m.next().?;
const slice = m.slice();
switch (tok) {
@@ -5952,6 +5935,30 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.N
}
}
+fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ var node = try parseCPrimaryExprInner(c, m, scope);
+ // In C the preprocessor would handle concatting strings while expanding macros.
+ // This should do approximately the same by concatting any strings and identifiers
+ // after a primary expression.
+ while (true) {
+ var op_token: ast.TokenIndex = undefined;
+ var op_id: ast.Node.Tag = undefined;
+ switch (m.peek().?) {
+ .StringLiteral, .Identifier => {},
+ else => break,
+ }
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
+ op_node.* = .{
+ .base = .{ .tag = .ArrayCat },
+ .op_token = try appendToken(c, .PlusPlus, "++"),
+ .lhs = node,
+ .rhs = try parseCPrimaryExprInner(c, m, scope),
+ };
+ node = &op_node.base;
+ }
+ return node;
+}
+
fn nodeIsInfixOp(tag: ast.Node.Tag) bool {
return switch (tag) {
.Add,
@@ -6053,31 +6060,268 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
return &group_node.base;
}
-fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
- var node = try parseCPrimaryExpr(c, m, scope);
+fn macroGroup(c: *Context, node: *ast.Node) !*ast.Node {
+ if (!nodeIsInfixOp(node.tag)) return node;
+
+ const group_node = try c.arena.create(ast.Node.GroupedExpression);
+ group_node.* = .{
+ .lparen = try appendToken(c, .LParen, "("),
+ .expr = node,
+ .rparen = try appendToken(c, .RParen, ")"),
+ };
+ return &group_node.base;
+}
+
+fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ const node = try parseCOrExpr(c, m, scope);
+ if (m.peek().? != .QuestionMark) {
+ return node;
+ }
+ _ = m.next();
+
+ // must come immediately after expr
+ _ = try appendToken(c, .RParen, ")");
+ const if_node = try transCreateNodeIf(c);
+ if_node.condition = node;
+ if_node.body = try parseCOrExpr(c, m, scope);
+ if (m.next().? != .Colon) {
+ try m.fail(c, "unable to translate C expr: expected ':'", .{});
+ return error.ParseError;
+ }
+ if_node.@"else" = try transCreateNodeElse(c);
+ if_node.@"else".?.body = try parseCCondExpr(c, m, scope);
+ return &if_node.base;
+}
+
+fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ var node = try parseCAndExpr(c, m, scope);
+ while (m.next().? == .PipePipe) {
+ const lhs_node = try macroIntToBool(c, node);
+ const op_token = try appendToken(c, .Keyword_or, "or");
+ const rhs_node = try parseCAndExpr(c, m, scope);
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
+ op_node.* = .{
+ .base = .{ .tag = .BoolOr },
+ .op_token = op_token,
+ .lhs = lhs_node,
+ .rhs = try macroIntToBool(c, rhs_node),
+ };
+ node = &op_node.base;
+ }
+ m.i -= 1;
+ return node;
+}
+
+fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ var node = try parseCBitOrExpr(c, m, scope);
+ while (m.next().? == .AmpersandAmpersand) {
+ const lhs_node = try macroIntToBool(c, node);
+ const op_token = try appendToken(c, .Keyword_and, "and");
+ const rhs_node = try parseCBitOrExpr(c, m, scope);
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
+ op_node.* = .{
+ .base = .{ .tag = .BoolAnd },
+ .op_token = op_token,
+ .lhs = lhs_node,
+ .rhs = try macroIntToBool(c, rhs_node),
+ };
+ node = &op_node.base;
+ }
+ m.i -= 1;
+ return node;
+}
+
+fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ var node = try parseCBitXorExpr(c, m, scope);
+ while (m.next().? == .Pipe) {
+ const lhs_node = try macroBoolToInt(c, node);
+ const op_token = try appendToken(c, .Pipe, "|");
+ const rhs_node = try parseCBitXorExpr(c, m, scope);
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
+ op_node.* = .{
+ .base = .{ .tag = .BitOr },
+ .op_token = op_token,
+ .lhs = lhs_node,
+ .rhs = try macroBoolToInt(c, rhs_node),
+ };
+ node = &op_node.base;
+ }
+ m.i -= 1;
+ return node;
+}
+
+fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ var node = try parseCBitAndExpr(c, m, scope);
+ while (m.next().? == .Caret) {
+ const lhs_node = try macroBoolToInt(c, node);
+ const op_token = try appendToken(c, .Caret, "^");
+ const rhs_node = try parseCBitAndExpr(c, m, scope);
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
+ op_node.* = .{
+ .base = .{ .tag = .BitXor },
+ .op_token = op_token,
+ .lhs = lhs_node,
+ .rhs = try macroBoolToInt(c, rhs_node),
+ };
+ node = &op_node.base;
+ }
+ m.i -= 1;
+ return node;
+}
+
+fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ var node = try parseCEqExpr(c, m, scope);
+ while (m.next().? == .Ampersand) {
+ const lhs_node = try macroBoolToInt(c, node);
+ const op_token = try appendToken(c, .Ampersand, "&");
+ const rhs_node = try parseCEqExpr(c, m, scope);
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
+ op_node.* = .{
+ .base = .{ .tag = .BitAnd },
+ .op_token = op_token,
+ .lhs = lhs_node,
+ .rhs = try macroBoolToInt(c, rhs_node),
+ };
+ node = &op_node.base;
+ }
+ m.i -= 1;
+ return node;
+}
+
+fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ var node = try parseCRelExpr(c, m, scope);
while (true) {
var op_token: ast.TokenIndex = undefined;
var op_id: ast.Node.Tag = undefined;
- var bool_op = false;
- switch (m.next().?) {
- .Period => {
- if (m.next().? != .Identifier) {
- try m.fail(c, "unable to translate C expr: expected identifier", .{});
- return error.ParseError;
- }
+ switch (m.peek().?) {
+ .BangEqual => {
+ op_token = try appendToken(c, .BangEqual, "!=");
+ op_id = .BangEqual;
+ },
+ .EqualEqual => {
+ op_token = try appendToken(c, .EqualEqual, "==");
+ op_id = .EqualEqual;
+ },
+ else => return node,
+ }
+ _ = m.next();
+ const lhs_node = try macroBoolToInt(c, node);
+ const rhs_node = try parseCRelExpr(c, m, scope);
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
+ op_node.* = .{
+ .base = .{ .tag = op_id },
+ .op_token = op_token,
+ .lhs = lhs_node,
+ .rhs = try macroBoolToInt(c, rhs_node),
+ };
+ node = &op_node.base;
+ }
+}
- node = try transCreateNodeFieldAccess(c, node, m.slice());
- continue;
+fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ var node = try parseCShiftExpr(c, m, scope);
+ while (true) {
+ var op_token: ast.TokenIndex = undefined;
+ var op_id: ast.Node.Tag = undefined;
+ switch (m.peek().?) {
+ .AngleBracketRight => {
+ op_token = try appendToken(c, .AngleBracketRight, ">");
+ op_id = .GreaterThan;
},
- .Arrow => {
- if (m.next().? != .Identifier) {
- try m.fail(c, "unable to translate C expr: expected identifier", .{});
- return error.ParseError;
- }
- const deref = try transCreateNodePtrDeref(c, node);
- node = try transCreateNodeFieldAccess(c, deref, m.slice());
- continue;
+ .AngleBracketRightEqual => {
+ op_token = try appendToken(c, .AngleBracketRightEqual, ">=");
+ op_id = .GreaterOrEqual;
+ },
+ .AngleBracketLeft => {
+ op_token = try appendToken(c, .AngleBracketLeft, "<");
+ op_id = .LessThan;
},
+ .AngleBracketLeftEqual => {
+ op_token = try appendToken(c, .AngleBracketLeftEqual, "<=");
+ op_id = .LessOrEqual;
+ },
+ else => return node,
+ }
+ _ = m.next();
+ const lhs_node = try macroBoolToInt(c, node);
+ const rhs_node = try parseCShiftExpr(c, m, scope);
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
+ op_node.* = .{
+ .base = .{ .tag = op_id },
+ .op_token = op_token,
+ .lhs = lhs_node,
+ .rhs = try macroBoolToInt(c, rhs_node),
+ };
+ node = &op_node.base;
+ }
+}
+
+fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ var node = try parseCAddSubExpr(c, m, scope);
+ while (true) {
+ var op_token: ast.TokenIndex = undefined;
+ var op_id: ast.Node.Tag = undefined;
+ switch (m.peek().?) {
+ .AngleBracketAngleBracketLeft => {
+ op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<");
+ op_id = .BitShiftLeft;
+ },
+ .AngleBracketAngleBracketRight => {
+ op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>");
+ op_id = .BitShiftRight;
+ },
+ else => return node,
+ }
+ _ = m.next();
+ const lhs_node = try macroBoolToInt(c, node);
+ const rhs_node = try parseCAddSubExpr(c, m, scope);
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
+ op_node.* = .{
+ .base = .{ .tag = op_id },
+ .op_token = op_token,
+ .lhs = lhs_node,
+ .rhs = try macroBoolToInt(c, rhs_node),
+ };
+ node = &op_node.base;
+ }
+}
+
+fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ var node = try parseCMulExpr(c, m, scope);
+ while (true) {
+ var op_token: ast.TokenIndex = undefined;
+ var op_id: ast.Node.Tag = undefined;
+ switch (m.peek().?) {
+ .Plus => {
+ op_token = try appendToken(c, .Plus, "+");
+ op_id = .Add;
+ },
+ .Minus => {
+ op_token = try appendToken(c, .Minus, "-");
+ op_id = .Sub;
+ },
+ else => return node,
+ }
+ _ = m.next();
+ const lhs_node = try macroBoolToInt(c, node);
+ const rhs_node = try parseCMulExpr(c, m, scope);
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
+ op_node.* = .{
+ .base = .{ .tag = op_id },
+ .op_token = op_token,
+ .lhs = lhs_node,
+ .rhs = try macroBoolToInt(c, rhs_node),
+ };
+ node = &op_node.base;
+ }
+}
+
+fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ var node = try parseCUnaryExpr(c, m, scope);
+ while (true) {
+ var op_token: ast.TokenIndex = undefined;
+ var op_id: ast.Node.Tag = undefined;
+ switch (m.next().?) {
.Asterisk => {
if (m.peek().? == .RParen) {
// type *)
@@ -6105,59 +6349,57 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.
op_id = .BitShiftLeft;
}
},
- .AngleBracketAngleBracketLeft => {
- op_token = try appendToken(c, .AngleBracketAngleBracketLeft, "<<");
- op_id = .BitShiftLeft;
- },
- .AngleBracketAngleBracketRight => {
- op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>");
- op_id = .BitShiftRight;
- },
- .Pipe => {
- op_token = try appendToken(c, .Pipe, "|");
- op_id = .BitOr;
- },
- .Ampersand => {
- op_token = try appendToken(c, .Ampersand, "&");
- op_id = .BitAnd;
- },
- .Plus => {
- op_token = try appendToken(c, .Plus, "+");
- op_id = .Add;
- },
- .Minus => {
- op_token = try appendToken(c, .Minus, "-");
- op_id = .Sub;
- },
- .AmpersandAmpersand => {
- op_token = try appendToken(c, .Keyword_and, "and");
- op_id = .BoolAnd;
- bool_op = true;
- },
- .PipePipe => {
- op_token = try appendToken(c, .Keyword_or, "or");
- op_id = .BoolOr;
- bool_op = true;
+ .Slash => {
+ op_id = .Div;
+ op_token = try appendToken(c, .Slash, "/");
},
- .AngleBracketRight => {
- op_token = try appendToken(c, .AngleBracketRight, ">");
- op_id = .GreaterThan;
+ .Percent => {
+ op_id = .Mod;
+ op_token = try appendToken(c, .Percent, "%");
},
- .AngleBracketRightEqual => {
- op_token = try appendToken(c, .AngleBracketRightEqual, ">=");
- op_id = .GreaterOrEqual;
+ else => {
+ m.i -= 1;
+ return node;
},
- .AngleBracketLeft => {
- op_token = try appendToken(c, .AngleBracketLeft, "<");
- op_id = .LessThan;
+ }
+ const lhs_node = try macroBoolToInt(c, node);
+ const rhs_node = try parseCUnaryExpr(c, m, scope);
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
+ op_node.* = .{
+ .base = .{ .tag = op_id },
+ .op_token = op_token,
+ .lhs = lhs_node,
+ .rhs = try macroBoolToInt(c, rhs_node),
+ };
+ node = &op_node.base;
+ }
+}
+
+fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+ var node = try parseCPrimaryExpr(c, m, scope);
+ while (true) {
+ switch (m.next().?) {
+ .Period => {
+ if (m.next().? != .Identifier) {
+ try m.fail(c, "unable to translate C expr: expected identifier", .{});
+ return error.ParseError;
+ }
+
+ node = try transCreateNodeFieldAccess(c, node, m.slice());
+ continue;
},
- .AngleBracketLeftEqual => {
- op_token = try appendToken(c, .AngleBracketLeftEqual, "<=");
- op_id = .LessOrEqual;
+ .Arrow => {
+ if (m.next().? != .Identifier) {
+ try m.fail(c, "unable to translate C expr: expected identifier", .{});
+ return error.ParseError;
+ }
+ const deref = try transCreateNodePtrDeref(c, node);
+ node = try transCreateNodeFieldAccess(c, deref, m.slice());
+ continue;
},
.LBracket => {
const arr_node = try transCreateNodeArrayAccess(c, node);
- arr_node.index_expr = try parseCPrefixOpExpr(c, m, scope);
+ arr_node.index_expr = try parseCExpr(c, m, scope);
arr_node.rtoken = try appendToken(c, .RBracket, "]");
node = &arr_node.base;
if (m.next().? != .RBracket) {
@@ -6171,7 +6413,7 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.
var call_params = std.ArrayList(*ast.Node).init(c.gpa);
defer call_params.deinit();
while (true) {
- const arg = try parseCPrefixOpExpr(c, m, scope);
+ const arg = try parseCCondExpr(c, m, scope);
try call_params.append(arg);
switch (m.next().?) {
.Comma => _ = try appendToken(c, .Comma, ","),
@@ -6204,7 +6446,7 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.
defer init_vals.deinit();
while (true) {
- const val = try parseCPrefixOpExpr(c, m, scope);
+ const val = try parseCCondExpr(c, m, scope);
try init_vals.append(val);
switch (m.next().?) {
.Comma => _ = try appendToken(c, .Comma, ","),
@@ -6239,90 +6481,57 @@ fn parseCSuffixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.
node = &zero_init_call.base;
continue;
},
- .BangEqual => {
- op_token = try appendToken(c, .BangEqual, "!=");
- op_id = .BangEqual;
- },
- .EqualEqual => {
- op_token = try appendToken(c, .EqualEqual, "==");
- op_id = .EqualEqual;
- },
- .Slash => {
- op_id = .Div;
- op_token = try appendToken(c, .Slash, "/");
- },
- .Percent => {
- op_id = .Mod;
- op_token = try appendToken(c, .Percent, "%");
- },
- .StringLiteral => {
- op_id = .ArrayCat;
- op_token = try appendToken(c, .PlusPlus, "++");
-
- m.i -= 1;
- },
- .Identifier => {
- op_id = .ArrayCat;
- op_token = try appendToken(c, .PlusPlus, "++");
-
- m.i -= 1;
+ .PlusPlus, .MinusMinus => {
+ try m.fail(c, "TODO postfix inc/dec expr", .{});
+ return error.ParseError;
},
else => {
m.i -= 1;
return node;
},
}
- const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;
- const lhs_node = try cast_fn(c, node);
- const rhs_node = try parseCPrefixOpExpr(c, m, scope);
- const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
- op_node.* = .{
- .base = .{ .tag = op_id },
- .op_token = op_token,
- .lhs = lhs_node,
- .rhs = try cast_fn(c, rhs_node),
- };
- node = &op_node.base;
}
}
-fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
+fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node {
switch (m.next().?) {
.Bang => {
const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");
- node.rhs = try parseCPrefixOpExpr(c, m, scope);
+ node.rhs = try macroIntToBool(c, try parseCUnaryExpr(c, m, scope));
return &node.base;
},
.Minus => {
const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");
- node.rhs = try parseCPrefixOpExpr(c, m, scope);
+ node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
return &node.base;
},
- .Plus => return try parseCPrefixOpExpr(c, m, scope),
+ .Plus => return try parseCUnaryExpr(c, m, scope),
.Tilde => {
const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");
- node.rhs = try parseCPrefixOpExpr(c, m, scope);
+ node.rhs = try macroBoolToInt(c, try parseCUnaryExpr(c, m, scope));
return &node.base;
},
.Asterisk => {
- const node = try parseCPrefixOpExpr(c, m, scope);
+ const node = try macroGroup(c, try parseCUnaryExpr(c, m, scope));
return try transCreateNodePtrDeref(c, node);
},
.Ampersand => {
const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");
- node.rhs = try parseCPrefixOpExpr(c, m, scope);
+ node.rhs = try macroGroup(c, try parseCUnaryExpr(c, m, scope));
return &node.base;
},
.Keyword_sizeof => {
const inner = if (m.peek().? == .LParen) blk: {
_ = m.next();
- const inner = try parseCExpr(c, m, scope);
+ // C grammar says this should be 'type-name' but we have to
+ // use parseCMulExpr to correctly handle pointer types.
+ const inner = try parseCMulExpr(c, m, scope);
if (m.next().? != .RParen) {
try m.fail(c, "unable to translate C expr: expected ')'", .{});
return error.ParseError;
}
break :blk inner;
- } else try parseCPrefixOpExpr(c, m, scope);
+ } else try parseCUnaryExpr(c, m, scope);
//(@import("std").meta.sizeof(dest, x))
const import_fn_call = try c.createBuiltinCall("@import", 1);
@@ -6344,7 +6553,9 @@ fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.
try m.fail(c, "unable to translate C expr: expected '('", .{});
return error.ParseError;
}
- const inner = try parseCExpr(c, m, scope);
+ // C grammar says this should be 'type-name' but we have to
+ // use parseCMulExpr to correctly handle pointer types.
+ const inner = try parseCMulExpr(c, m, scope);
if (m.next().? != .RParen) {
try m.fail(c, "unable to translate C expr: expected ')'", .{});
return error.ParseError;
@@ -6355,9 +6566,13 @@ fn parseCPrefixOpExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.
builtin_call.rparen_token = try appendToken(c, .RParen, ")");
return &builtin_call.base;
},
+ .PlusPlus, .MinusMinus => {
+ try m.fail(c, "TODO unary inc/dec expr", .{});
+ return error.ParseError;
+ },
else => {
m.i -= 1;
- return try parseCSuffixOpExpr(c, m, scope);
+ return try parseCPostfixExpr(c, m, scope);
},
}
}
diff --git a/src/type.zig b/src/type.zig
@@ -89,6 +89,8 @@ pub const Type = extern union {
.anyerror_void_error_union, .error_union => return .ErrorUnion,
.anyframe_T, .@"anyframe" => return .AnyFrame,
+
+ .empty_struct => return .Struct,
}
}
@@ -439,6 +441,7 @@ pub const Type = extern union {
},
.error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
.error_set_single => return self.copyPayloadShallow(allocator, Payload.ErrorSetSingle),
+ .empty_struct => return self.copyPayloadShallow(allocator, Payload.EmptyStruct),
}
}
@@ -505,6 +508,8 @@ pub const Type = extern union {
.@"null" => return out_stream.writeAll("@Type(.Null)"),
.@"undefined" => return out_stream.writeAll("@Type(.Undefined)"),
+ // TODO this should print the structs name
+ .empty_struct => return out_stream.writeAll("struct {}"),
.@"anyframe" => return out_stream.writeAll("anyframe"),
.anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
.const_slice_u8 => return out_stream.writeAll("[]const u8"),
@@ -788,6 +793,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.enum_literal,
+ .empty_struct,
=> false,
};
}
@@ -910,6 +916,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.enum_literal,
+ .empty_struct,
=> unreachable,
};
}
@@ -932,6 +939,7 @@ pub const Type = extern union {
.@"undefined" => unreachable,
.enum_literal => unreachable,
.single_const_pointer_to_comptime_int => unreachable,
+ .empty_struct => unreachable,
.u8,
.i8,
@@ -1107,6 +1115,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> false,
.single_const_pointer,
@@ -1181,6 +1190,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> false,
.const_slice,
@@ -1252,6 +1262,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> false,
.single_const_pointer,
@@ -1332,6 +1343,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> false,
.pointer => {
@@ -1407,6 +1419,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> false,
.pointer => {
@@ -1524,6 +1537,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> unreachable,
.array => self.cast(Payload.Array).?.elem_type,
@@ -1651,6 +1665,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> unreachable,
.array => self.cast(Payload.Array).?.len,
@@ -1716,6 +1731,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> unreachable,
.single_const_pointer,
@@ -1798,6 +1814,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> false,
.int_signed,
@@ -1872,6 +1889,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> false,
.int_unsigned,
@@ -1936,6 +1954,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> unreachable,
.int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits },
@@ -2018,6 +2037,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> false,
.usize,
@@ -2129,6 +2149,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> unreachable,
};
}
@@ -2206,6 +2227,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> unreachable,
}
}
@@ -2282,6 +2304,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> unreachable,
}
}
@@ -2358,6 +2381,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> unreachable,
};
}
@@ -2431,6 +2455,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> unreachable,
};
}
@@ -2504,6 +2529,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> unreachable,
};
}
@@ -2577,6 +2603,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> false,
};
}
@@ -2636,6 +2663,7 @@ pub const Type = extern union {
.error_set_single,
=> return null,
+ .empty_struct => return Value.initTag(.empty_struct_value),
.void => return Value.initTag(.void_value),
.noreturn => return Value.initTag(.unreachable_value),
.@"null" => return Value.initTag(.null_value),
@@ -2743,6 +2771,7 @@ pub const Type = extern union {
.anyerror_void_error_union,
.error_set,
.error_set_single,
+ .empty_struct,
=> return false,
.c_const_pointer,
@@ -2760,6 +2789,80 @@ pub const Type = extern union {
(self.isSinglePointer() and self.elemType().zigTypeTag() == .Array);
}
+ /// Asserts that the type is a container. (note: ErrorSet is not a container).
+ pub fn getContainerScope(self: Type) *Module.Scope.Container {
+ return switch (self.tag()) {
+ .f16,
+ .f32,
+ .f64,
+ .f128,
+ .c_longdouble,
+ .comptime_int,
+ .comptime_float,
+ .u8,
+ .i8,
+ .u16,
+ .i16,
+ .u32,
+ .i32,
+ .u64,
+ .i64,
+ .usize,
+ .isize,
+ .c_short,
+ .c_ushort,
+ .c_int,
+ .c_uint,
+ .c_long,
+ .c_ulong,
+ .c_longlong,
+ .c_ulonglong,
+ .bool,
+ .type,
+ .anyerror,
+ .fn_noreturn_no_args,
+ .fn_void_no_args,
+ .fn_naked_noreturn_no_args,
+ .fn_ccc_void_no_args,
+ .function,
+ .single_const_pointer_to_comptime_int,
+ .const_slice_u8,
+ .c_void,
+ .void,
+ .noreturn,
+ .@"null",
+ .@"undefined",
+ .int_unsigned,
+ .int_signed,
+ .array,
+ .array_sentinel,
+ .array_u8,
+ .array_u8_sentinel_0,
+ .single_const_pointer,
+ .single_mut_pointer,
+ .many_const_pointer,
+ .many_mut_pointer,
+ .const_slice,
+ .mut_slice,
+ .optional,
+ .optional_single_mut_pointer,
+ .optional_single_const_pointer,
+ .enum_literal,
+ .error_union,
+ .@"anyframe",
+ .anyframe_T,
+ .anyerror_void_error_union,
+ .error_set,
+ .error_set_single,
+ .c_const_pointer,
+ .c_mut_pointer,
+ .pointer,
+ => unreachable,
+
+ .empty_struct => self.cast(Type.Payload.EmptyStruct).?.scope,
+ };
+ }
+
/// This enum does not directly correspond to `std.builtin.TypeId` because
/// it has extra enum tags in it, as a way of using less memory. For example,
/// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
@@ -2835,6 +2938,7 @@ pub const Type = extern union {
anyframe_T,
error_set,
error_set_single,
+ empty_struct,
pub const last_no_payload_tag = Tag.const_slice_u8;
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
@@ -2942,6 +3046,14 @@ pub const Type = extern union {
/// memory is owned by `Module`
name: []const u8,
};
+
+ /// Mostly used for namespace like structs with zero fields.
+ /// Most commonly used for files.
+ pub const EmptyStruct = struct {
+ base: Payload = .{ .tag = .empty_struct },
+
+ scope: *Module.Scope.Container,
+ };
};
};
diff --git a/src/value.zig b/src/value.zig
@@ -68,6 +68,7 @@ pub const Value = extern union {
one,
void_value,
unreachable_value,
+ empty_struct_value,
empty_array,
null_value,
bool_true,
@@ -182,6 +183,7 @@ pub const Value = extern union {
.null_value,
.bool_true,
.bool_false,
+ .empty_struct_value,
=> unreachable,
.ty => {
@@ -312,6 +314,8 @@ pub const Value = extern union {
.enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"),
.anyframe_type => return out_stream.writeAll("anyframe"),
+ // TODO this should print `NAME{}`
+ .empty_struct_value => return out_stream.writeAll("struct {}{}"),
.null_value => return out_stream.writeAll("null"),
.undef => return out_stream.writeAll("undefined"),
.zero => return out_stream.writeAll("0"),
@@ -475,6 +479,7 @@ pub const Value = extern union {
.float_128,
.enum_literal,
.@"error",
+ .empty_struct_value,
=> unreachable,
};
}
@@ -543,6 +548,7 @@ pub const Value = extern union {
.enum_literal,
.error_set,
.@"error",
+ .empty_struct_value,
=> unreachable,
.undef => unreachable,
@@ -626,6 +632,7 @@ pub const Value = extern union {
.enum_literal,
.error_set,
.@"error",
+ .empty_struct_value,
=> unreachable,
.undef => unreachable,
@@ -709,6 +716,7 @@ pub const Value = extern union {
.enum_literal,
.error_set,
.@"error",
+ .empty_struct_value,
=> unreachable,
.undef => unreachable,
@@ -820,6 +828,7 @@ pub const Value = extern union {
.enum_literal,
.error_set,
.@"error",
+ .empty_struct_value,
=> unreachable,
.zero,
@@ -833,7 +842,7 @@ pub const Value = extern union {
.int_u64 => {
const x = self.cast(Payload.Int_u64).?.int;
if (x == 0) return 0;
- return std.math.log2(x) + 1;
+ return @intCast(usize, std.math.log2(x) + 1);
},
.int_i64 => {
@panic("TODO implement i64 intBitCountTwosComp");
@@ -907,6 +916,7 @@ pub const Value = extern union {
.enum_literal,
.error_set,
.@"error",
+ .empty_struct_value,
=> unreachable,
.zero,
@@ -1078,6 +1088,7 @@ pub const Value = extern union {
.enum_literal,
.error_set,
.@"error",
+ .empty_struct_value,
=> unreachable,
.zero,
@@ -1152,6 +1163,7 @@ pub const Value = extern union {
.enum_literal,
.error_set,
.@"error",
+ .empty_struct_value,
=> unreachable,
.zero,
@@ -1300,6 +1312,7 @@ pub const Value = extern union {
.enum_literal,
.error_set,
.@"error",
+ .empty_struct_value,
=> unreachable,
.ref_val => self.cast(Payload.RefVal).?.val,
@@ -1383,6 +1396,7 @@ pub const Value = extern union {
.enum_literal,
.error_set,
.@"error",
+ .empty_struct_value,
=> unreachable,
.empty_array => unreachable, // out of bounds array index
@@ -1483,6 +1497,7 @@ pub const Value = extern union {
.enum_literal,
.error_set,
.@"error",
+ .empty_struct_value,
=> false,
.undef => unreachable,
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
@@ -1126,6 +1126,34 @@ LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp
singleThread ? SyncScope::SingleThread : SyncScope::System));
}
+LLVMValueRef ZigLLVMBuildAndReduce(LLVMBuilderRef B, LLVMValueRef Val) {
+ return wrap(unwrap(B)->CreateAndReduce(unwrap(Val)));
+}
+
+LLVMValueRef ZigLLVMBuildOrReduce(LLVMBuilderRef B, LLVMValueRef Val) {
+ return wrap(unwrap(B)->CreateOrReduce(unwrap(Val)));
+}
+
+LLVMValueRef ZigLLVMBuildXorReduce(LLVMBuilderRef B, LLVMValueRef Val) {
+ return wrap(unwrap(B)->CreateXorReduce(unwrap(Val)));
+}
+
+LLVMValueRef ZigLLVMBuildIntMaxReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed) {
+ return wrap(unwrap(B)->CreateIntMaxReduce(unwrap(Val), is_signed));
+}
+
+LLVMValueRef ZigLLVMBuildIntMinReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed) {
+ return wrap(unwrap(B)->CreateIntMinReduce(unwrap(Val), is_signed));
+}
+
+LLVMValueRef ZigLLVMBuildFPMaxReduce(LLVMBuilderRef B, LLVMValueRef Val) {
+ return wrap(unwrap(B)->CreateFPMaxReduce(unwrap(Val)));
+}
+
+LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val) {
+ return wrap(unwrap(B)->CreateFPMinReduce(unwrap(Val)));
+}
+
static_assert((Triple::ArchType)ZigLLVM_UnknownArch == Triple::UnknownArch, "");
static_assert((Triple::ArchType)ZigLLVM_arm == Triple::arm, "");
static_assert((Triple::ArchType)ZigLLVM_armeb == Triple::armeb, "");
diff --git a/src/zig_llvm.h b/src/zig_llvm.h
@@ -455,6 +455,14 @@ LLVMValueRef ZigLLVMBuildAtomicRMW(LLVMBuilderRef B, enum ZigLLVM_AtomicRMWBinOp
LLVMValueRef PTR, LLVMValueRef Val,
LLVMAtomicOrdering ordering, LLVMBool singleThread);
+LLVMValueRef ZigLLVMBuildAndReduce(LLVMBuilderRef B, LLVMValueRef Val);
+LLVMValueRef ZigLLVMBuildOrReduce(LLVMBuilderRef B, LLVMValueRef Val);
+LLVMValueRef ZigLLVMBuildXorReduce(LLVMBuilderRef B, LLVMValueRef Val);
+LLVMValueRef ZigLLVMBuildIntMaxReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed);
+LLVMValueRef ZigLLVMBuildIntMinReduce(LLVMBuilderRef B, LLVMValueRef Val, bool is_signed);
+LLVMValueRef ZigLLVMBuildFPMaxReduce(LLVMBuilderRef B, LLVMValueRef Val);
+LLVMValueRef ZigLLVMBuildFPMinReduce(LLVMBuilderRef B, LLVMValueRef Val);
+
#define ZigLLVM_DIFlags_Zero 0U
#define ZigLLVM_DIFlags_Private 1U
#define ZigLLVM_DIFlags_Protected 2U
diff --git a/src/zir.zig b/src/zir.zig
@@ -161,6 +161,8 @@ pub const Inst = struct {
@"fn",
/// Returns a function type.
fntype,
+ /// @import(operand)
+ import,
/// Integer literal.
int,
/// Convert an integer value to another integer type, asserting that the destination type
@@ -315,6 +317,7 @@ pub const Inst = struct {
.ensure_err_payload_void,
.anyframe_type,
.bitnot,
+ .import,
=> UnOp,
.add,
@@ -489,6 +492,7 @@ pub const Inst = struct {
.error_set,
.slice,
.slice_start,
+ .import,
=> false,
.@"break",
diff --git a/src/zir_sema.zig b/src/zir_sema.zig
@@ -134,6 +134,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
.error_set => return analyzeInstErrorSet(mod, scope, old_inst.castTag(.error_set).?),
.slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?),
.slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?),
+ .import => return analyzeInstImport(mod, scope, old_inst.castTag(.import).?),
}
}
@@ -1047,6 +1048,19 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr
.val = Value.initPayload(&ref_payload.base),
});
},
+ .Struct => {
+ const container_scope = child_type.getContainerScope();
+ if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
+ // TODO if !decl.is_pub and inDifferentFiles() "{} is private"
+ return mod.analyzeDeclRef(scope, fieldptr.base.src, decl);
+ }
+
+ if (&container_scope.file_scope.base == mod.root_scope) {
+ return mod.fail(scope, fieldptr.base.src, "root source file has no member called '{}'", .{field_name});
+ } else {
+ return mod.fail(scope, fieldptr.base.src, "container '{}' has no member called '{}'", .{ child_type, field_name });
+ }
+ },
else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{child_type}),
}
},
@@ -1190,6 +1204,24 @@ fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn
return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null);
}
+fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
+ const operand = try resolveConstString(mod, scope, inst.positionals.operand);
+
+ const file_scope = mod.analyzeImport(scope, inst.base.src, operand) catch |err| switch (err) {
+ // error.ImportOutsidePkgPath => {
+ // return mod.fail(scope, inst.base.src, "import of file outside package path: '{}'", .{operand});
+ // },
+ error.FileNotFound => {
+ return mod.fail(scope, inst.base.src, "unable to find '{}'", .{operand});
+ },
+ else => {
+ // TODO user friendly error to string
+ return mod.fail(scope, inst.base.src, "unable to open '{}': {}", .{ operand, @errorName(err) });
+ },
+ };
+ return mod.constType(scope, inst.base.src, file_scope.root_container.ty);
+}
+
fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{});
}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
@@ -2,20 +2,175 @@ const tests = @import("tests.zig");
const std = @import("std");
pub fn addCases(cases: *tests.CompileErrorContext) void {
- cases.add("slice sentinel mismatch",
+ cases.add("@Type for exhaustive enum with undefined tag type",
+ \\const TypeInfo = @import("builtin").TypeInfo;
+ \\const Tag = @Type(.{
+ \\ .Enum = .{
+ \\ .layout = .Auto,
+ \\ .tag_type = undefined,
+ \\ .fields = &[_]TypeInfo.EnumField{},
+ \\ .decls = &[_]TypeInfo.Declaration{},
+ \\ .is_exhaustive = false,
+ \\ },
+ \\});
\\export fn entry() void {
- \\ const x = @import("std").meta.Vector(3, f32){ 25, 75, 5, 0 };
+ \\ _ = @intToEnum(Tag, 0);
\\}
, &[_][]const u8{
- "tmp.zig:2:62: error: index 3 outside vector of size 3",
+ "tmp.zig:2:20: error: use of undefined value here causes undefined behavior",
});
- cases.add("slice sentinel mismatch",
+ cases.add("extern struct with non-extern-compatible integer tag type",
+ \\pub const E = enum(u31) { A, B, C };
+ \\pub const S = extern struct {
+ \\ e: E,
+ \\};
\\export fn entry() void {
- \\ const y: [:1]const u8 = &[_:2]u8{ 1, 2 };
+ \\ const s: S = undefined;
\\}
, &[_][]const u8{
- "tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8'",
+ "tmp.zig:3:5: error: extern structs cannot contain fields of type 'E'",
+ });
+
+ cases.add("@Type for exhaustive enum with non-integer tag type",
+ \\const TypeInfo = @import("builtin").TypeInfo;
+ \\const Tag = @Type(.{
+ \\ .Enum = .{
+ \\ .layout = .Auto,
+ \\ .tag_type = bool,
+ \\ .fields = &[_]TypeInfo.EnumField{},
+ \\ .decls = &[_]TypeInfo.Declaration{},
+ \\ .is_exhaustive = false,
+ \\ },
+ \\});
+ \\export fn entry() void {
+ \\ _ = @intToEnum(Tag, 0);
+ \\}
+ , &[_][]const u8{
+ "tmp.zig:2:20: error: TypeInfo.Enum.tag_type must be an integer type, not 'bool'",
+ });
+
+ cases.add("extern struct with extern-compatible but inferred integer tag type",
+ \\pub const E = enum {
+ \\@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12",
+ \\@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20",@"21",@"22",@"23",
+ \\@"24",@"25",@"26",@"27",@"28",@"29",@"30",@"31",@"32",@"33",@"34",
+ \\@"35",@"36",@"37",@"38",@"39",@"40",@"41",@"42",@"43",@"44",@"45",
+ \\@"46",@"47",@"48",@"49",@"50",@"51",@"52",@"53",@"54",@"55",@"56",
+ \\@"57",@"58",@"59",@"60",@"61",@"62",@"63",@"64",@"65",@"66",@"67",
+ \\@"68",@"69",@"70",@"71",@"72",@"73",@"74",@"75",@"76",@"77",@"78",
+ \\@"79",@"80",@"81",@"82",@"83",@"84",@"85",@"86",@"87",@"88",@"89",
+ \\@"90",@"91",@"92",@"93",@"94",@"95",@"96",@"97",@"98",@"99",@"100",
+ \\@"101",@"102",@"103",@"104",@"105",@"106",@"107",@"108",@"109",
+ \\@"110",@"111",@"112",@"113",@"114",@"115",@"116",@"117",@"118",
+ \\@"119",@"120",@"121",@"122",@"123",@"124",@"125",@"126",@"127",
+ \\@"128",@"129",@"130",@"131",@"132",@"133",@"134",@"135",@"136",
+ \\@"137",@"138",@"139",@"140",@"141",@"142",@"143",@"144",@"145",
+ \\@"146",@"147",@"148",@"149",@"150",@"151",@"152",@"153",@"154",
+ \\@"155",@"156",@"157",@"158",@"159",@"160",@"161",@"162",@"163",
+ \\@"164",@"165",@"166",@"167",@"168",@"169",@"170",@"171",@"172",
+ \\@"173",@"174",@"175",@"176",@"177",@"178",@"179",@"180",@"181",
+ \\@"182",@"183",@"184",@"185",@"186",@"187",@"188",@"189",@"190",
+ \\@"191",@"192",@"193",@"194",@"195",@"196",@"197",@"198",@"199",
+ \\@"200",@"201",@"202",@"203",@"204",@"205",@"206",@"207",@"208",
+ \\@"209",@"210",@"211",@"212",@"213",@"214",@"215",@"216",@"217",
+ \\@"218",@"219",@"220",@"221",@"222",@"223",@"224",@"225",@"226",
+ \\@"227",@"228",@"229",@"230",@"231",@"232",@"233",@"234",@"235",
+ \\@"236",@"237",@"238",@"239",@"240",@"241",@"242",@"243",@"244",
+ \\@"245",@"246",@"247",@"248",@"249",@"250",@"251",@"252",@"253",
+ \\@"254",@"255"
+ \\};
+ \\pub const S = extern struct {
+ \\ e: E,
+ \\};
+ \\export fn entry() void {
+ \\ if (@TagType(E) != u8) @compileError("did not infer u8 tag type");
+ \\ const s: S = undefined;
+ \\}
+ , &[_][]const u8{
+ "tmp.zig:31:5: error: extern structs cannot contain fields of type 'E'",
+ });
+
+ cases.add("@Type for tagged union with extra enum field",
+ \\const TypeInfo = @import("builtin").TypeInfo;
+ \\const Tag = @Type(.{
+ \\ .Enum = .{
+ \\ .layout = .Auto,
+ \\ .tag_type = u2,
+ \\ .fields = &[_]TypeInfo.EnumField{
+ \\ .{ .name = "signed", .value = 0 },
+ \\ .{ .name = "unsigned", .value = 1 },
+ \\ .{ .name = "arst", .value = 2 },
+ \\ },
+ \\ .decls = &[_]TypeInfo.Declaration{},
+ \\ .is_exhaustive = true,
+ \\ },
+ \\});
+ \\const Tagged = @Type(.{
+ \\ .Union = .{
+ \\ .layout = .Auto,
+ \\ .tag_type = Tag,
+ \\ .fields = &[_]TypeInfo.UnionField{
+ \\ .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) },
+ \\ .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) },
+ \\ },
+ \\ .decls = &[_]TypeInfo.Declaration{},
+ \\ },
+ \\});
+ \\export fn entry() void {
+ \\ var tagged = Tagged{ .signed = -1 };
+ \\ tagged = .{ .unsigned = 1 };
+ \\}
+ , &[_][]const u8{
+ "tmp.zig:15:23: error: enum field missing: 'arst'",
+ "tmp.zig:27:24: note: referenced here",
+ });
+ cases.add("@Type(.Fn) with is_generic = true",
+ \\const Foo = @Type(.{
+ \\ .Fn = .{
+ \\ .calling_convention = .Unspecified,
+ \\ .alignment = 0,
+ \\ .is_generic = true,
+ \\ .is_var_args = false,
+ \\ .return_type = u0,
+ \\ .args = &[_]@import("builtin").TypeInfo.FnArg{},
+ \\ },
+ \\});
+ \\comptime { _ = Foo; }
+ , &[_][]const u8{
+ "tmp.zig:1:20: error: TypeInfo.Fn.is_generic must be false for @Type",
+ });
+
+ cases.add("@Type(.Fn) with is_var_args = true and non-C callconv",
+ \\const Foo = @Type(.{
+ \\ .Fn = .{
+ \\ .calling_convention = .Unspecified,
+ \\ .alignment = 0,
+ \\ .is_generic = false,
+ \\ .is_var_args = true,
+ \\ .return_type = u0,
+ \\ .args = &[_]@import("builtin").TypeInfo.FnArg{},
+ \\ },
+ \\});
+ \\comptime { _ = Foo; }
+ , &[_][]const u8{
+ "tmp.zig:1:20: error: varargs functions must have C calling convention",
+ });
+
+ cases.add("@Type(.Fn) with return_type = null",
+ \\const Foo = @Type(.{
+ \\ .Fn = .{
+ \\ .calling_convention = .Unspecified,
+ \\ .alignment = 0,
+ \\ .is_generic = false,
+ \\ .is_var_args = false,
+ \\ .return_type = null,
+ \\ .args = &[_]@import("builtin").TypeInfo.FnArg{},
+ \\ },
+ \\});
+ \\comptime { _ = Foo; }
+ , &[_][]const u8{
+ "tmp.zig:1:20: error: TypeInfo.Fn.return_type must be non-null for @Type",
});
cases.add("@Type for union with opaque field",
@@ -25,7 +180,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ .layout = .Auto,
\\ .tag_type = null,
\\ .fields = &[_]TypeInfo.UnionField{
- \\ .{ .name = "foo", .field_type = @Type(.Opaque) },
+ \\ .{ .name = "foo", .field_type = @Type(.Opaque), .alignment = 1 },
\\ },
\\ .decls = &[_]TypeInfo.Declaration{},
\\ },
@@ -38,6 +193,22 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
"tmp.zig:13:17: note: referenced here",
});
+ cases.add("slice sentinel mismatch",
+ \\export fn entry() void {
+ \\ const x = @import("std").meta.Vector(3, f32){ 25, 75, 5, 0 };
+ \\}
+ , &[_][]const u8{
+ "tmp.zig:2:62: error: index 3 outside vector of size 3",
+ });
+
+ cases.add("slice sentinel mismatch",
+ \\export fn entry() void {
+ \\ const y: [:1]const u8 = &[_:2]u8{ 1, 2 };
+ \\}
+ , &[_][]const u8{
+ "tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8'",
+ });
+
cases.add("@Type for union with zero fields",
\\const TypeInfo = @import("builtin").TypeInfo;
\\const Untagged = @Type(.{
@@ -94,9 +265,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ .layout = .Auto,
\\ .tag_type = Tag,
\\ .fields = &[_]TypeInfo.UnionField{
- \\ .{ .name = "signed", .field_type = i32 },
- \\ .{ .name = "unsigned", .field_type = u32 },
- \\ .{ .name = "arst", .field_type = f32 },
+ \\ .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) },
+ \\ .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) },
+ \\ .{ .name = "arst", .field_type = f32, .alignment = @alignOf(f32) },
\\ },
\\ .decls = &[_]TypeInfo.Declaration{},
\\ },
@@ -111,42 +282,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
"tmp.zig:27:24: note: referenced here",
});
- cases.add("@Type for tagged union with extra enum field",
- \\const TypeInfo = @import("builtin").TypeInfo;
- \\const Tag = @Type(.{
- \\ .Enum = .{
- \\ .layout = .Auto,
- \\ .tag_type = u2,
- \\ .fields = &[_]TypeInfo.EnumField{
- \\ .{ .name = "signed", .value = 0 },
- \\ .{ .name = "unsigned", .value = 1 },
- \\ .{ .name = "arst", .field_type = 2 },
- \\ },
- \\ .decls = &[_]TypeInfo.Declaration{},
- \\ .is_exhaustive = true,
- \\ },
- \\});
- \\const Tagged = @Type(.{
- \\ .Union = .{
- \\ .layout = .Auto,
- \\ .tag_type = Tag,
- \\ .fields = &[_]TypeInfo.UnionField{
- \\ .{ .name = "signed", .field_type = i32 },
- \\ .{ .name = "unsigned", .field_type = u32 },
- \\ },
- \\ .decls = &[_]TypeInfo.Declaration{},
- \\ },
- \\});
- \\export fn entry() void {
- \\ var tagged = Tagged{ .signed = -1 };
- \\ tagged = .{ .unsigned = 1 };
- \\}
- , &[_][]const u8{
- "tmp.zig:9:32: error: no member named 'field_type' in struct 'std.builtin.EnumField'",
- "tmp.zig:18:21: note: referenced here",
- "tmp.zig:27:18: note: referenced here",
- });
-
cases.add("@Type with undefined",
\\comptime {
\\ _ = @Type(.{ .Array = .{ .len = 0, .child = u8, .sentinel = undefined } });
@@ -7556,7 +7691,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
});
cases.add( // fixed bug #2032
- "compile diagnostic string for top level decl type",
+ "compile diagnostic string for top level decl type",
\\export fn entry() void {
\\ var foo: u32 = @This(){};
\\}
diff --git a/test/stage1/behavior/bugs/1467.zig b/test/stage1/behavior/bugs/1467.zig
@@ -0,0 +1,7 @@
+pub const E = enum(u32) { A, B, C };
+pub const S = extern struct {
+ e: E,
+};
+test "bug 1467" {
+ const s: S = undefined;
+}
diff --git a/test/stage1/behavior/type.zig b/test/stage1/behavior/type.zig
@@ -320,8 +320,8 @@ test "Type.Union" {
.layout = .Auto,
.tag_type = null,
.fields = &[_]TypeInfo.UnionField{
- .{ .name = "int", .field_type = i32 },
- .{ .name = "float", .field_type = f32 },
+ .{ .name = "int", .field_type = i32, .alignment = @alignOf(f32) },
+ .{ .name = "float", .field_type = f32, .alignment = @alignOf(f32) },
},
.decls = &[_]TypeInfo.Declaration{},
},
@@ -336,8 +336,8 @@ test "Type.Union" {
.layout = .Packed,
.tag_type = null,
.fields = &[_]TypeInfo.UnionField{
- .{ .name = "signed", .field_type = i32 },
- .{ .name = "unsigned", .field_type = u32 },
+ .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) },
+ .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) },
},
.decls = &[_]TypeInfo.Declaration{},
},
@@ -363,8 +363,8 @@ test "Type.Union" {
.layout = .Auto,
.tag_type = Tag,
.fields = &[_]TypeInfo.UnionField{
- .{ .name = "signed", .field_type = i32 },
- .{ .name = "unsigned", .field_type = u32 },
+ .{ .name = "signed", .field_type = i32, .alignment = @alignOf(i32) },
+ .{ .name = "unsigned", .field_type = u32, .alignment = @alignOf(u32) },
},
.decls = &[_]TypeInfo.Declaration{},
},
@@ -392,7 +392,7 @@ test "Type.Union from Type.Enum" {
.layout = .Auto,
.tag_type = Tag,
.fields = &[_]TypeInfo.UnionField{
- .{ .name = "working_as_expected", .field_type = u32 },
+ .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) },
},
.decls = &[_]TypeInfo.Declaration{},
},
@@ -408,7 +408,7 @@ test "Type.Union from regular enum" {
.layout = .Auto,
.tag_type = E,
.fields = &[_]TypeInfo.UnionField{
- .{ .name = "working_as_expected", .field_type = u32 },
+ .{ .name = "working_as_expected", .field_type = u32, .alignment = @alignOf(u32) },
},
.decls = &[_]TypeInfo.Declaration{},
},
@@ -416,3 +416,30 @@ test "Type.Union from regular enum" {
_ = T;
_ = @typeInfo(T).Union;
}
+
+test "Type.Fn" {
+ // wasm doesn't support align attributes on functions
+ if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest;
+
+ const foo = struct {
+ fn func(a: usize, b: bool) align(4) callconv(.C) usize {
+ return 0;
+ }
+ }.func;
+ const Foo = @Type(@typeInfo(@TypeOf(foo)));
+ const foo_2: Foo = foo;
+}
+
+test "Type.BoundFn" {
+ // wasm doesn't support align attributes on functions
+ if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest;
+
+ const TestStruct = packed struct {
+ pub fn foo(self: *const @This()) align(4) callconv(.Unspecified) void {}
+ };
+ const test_instance: TestStruct = undefined;
+ testing.expect(std.meta.eql(
+ @typeName(@TypeOf(test_instance.foo)),
+ @typeName(@Type(@typeInfo(@TypeOf(test_instance.foo)))),
+ ));
+}
diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig
@@ -211,7 +211,9 @@ fn testUnion() void {
expect(notag_union_info.Union.tag_type == null);
expect(notag_union_info.Union.layout == .Auto);
expect(notag_union_info.Union.fields.len == 2);
+ expect(notag_union_info.Union.fields[0].alignment == @alignOf(void));
expect(notag_union_info.Union.fields[1].field_type == u32);
+ expect(notag_union_info.Union.fields[1].alignment == @alignOf(u32));
const TestExternUnion = extern union {
foo: *c_void,
@@ -229,13 +231,18 @@ test "type info: struct info" {
}
fn testStruct() void {
+ const unpacked_struct_info = @typeInfo(TestUnpackedStruct);
+ expect(unpacked_struct_info.Struct.fields[0].alignment == @alignOf(u32));
+
const struct_info = @typeInfo(TestStruct);
expect(struct_info == .Struct);
expect(struct_info.Struct.layout == .Packed);
expect(struct_info.Struct.fields.len == 4);
+ expect(struct_info.Struct.fields[0].alignment == 2 * @alignOf(usize));
expect(struct_info.Struct.fields[2].field_type == *TestStruct);
expect(struct_info.Struct.fields[2].default_value == null);
expect(struct_info.Struct.fields[3].default_value.? == 4);
+ expect(struct_info.Struct.fields[3].alignment == 1);
expect(struct_info.Struct.decls.len == 2);
expect(struct_info.Struct.decls[0].is_pub);
expect(!struct_info.Struct.decls[0].data.Fn.is_extern);
@@ -244,8 +251,12 @@ fn testStruct() void {
expect(struct_info.Struct.decls[0].data.Fn.fn_type == fn (*const TestStruct) void);
}
+const TestUnpackedStruct = struct {
+ fieldA: u32 = 4,
+};
+
const TestStruct = packed struct {
- fieldA: usize,
+ fieldA: usize align(2 * @alignOf(usize)),
fieldB: void,
fieldC: *Self,
fieldD: u32 = 4,
@@ -255,6 +266,8 @@ const TestStruct = packed struct {
};
test "type info: function type info" {
+ // wasm doesn't support align attributes on functions
+ if (builtin.arch == .wasm32 or builtin.arch == .wasm64) return error.SkipZigTest;
testFunction();
comptime testFunction();
}
@@ -262,11 +275,14 @@ test "type info: function type info" {
fn testFunction() void {
const fn_info = @typeInfo(@TypeOf(foo));
expect(fn_info == .Fn);
+ expect(fn_info.Fn.alignment == 0);
expect(fn_info.Fn.calling_convention == .C);
expect(!fn_info.Fn.is_generic);
expect(fn_info.Fn.args.len == 2);
expect(fn_info.Fn.is_var_args);
expect(fn_info.Fn.return_type.? == usize);
+ const fn_aligned_info = @typeInfo(@TypeOf(fooAligned));
+ expect(fn_aligned_info.Fn.alignment == 4);
const test_instance: TestStruct = undefined;
const bound_fn_info = @typeInfo(@TypeOf(test_instance.foo));
@@ -274,7 +290,8 @@ fn testFunction() void {
expect(bound_fn_info.BoundFn.args[0].arg_type.? == *const TestStruct);
}
-extern fn foo(a: usize, b: bool, ...) usize;
+extern fn foo(a: usize, b: bool, ...) callconv(.C) usize;
+extern fn fooAligned(a: usize, b: bool, ...) align(4) callconv(.C) usize;
test "typeInfo with comptime parameter in struct fn def" {
const S = struct {
diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig
@@ -484,3 +484,43 @@ test "vector shift operators" {
S.doTheTest();
comptime S.doTheTest();
}
+
+test "vector reduce operation" {
+ const S = struct {
+ fn doTheTestReduce(comptime op: builtin.ReduceOp, x: anytype, expected: anytype) void {
+ const N = @typeInfo(@TypeOf(x)).Array.len;
+ const TX = @typeInfo(@TypeOf(x)).Array.child;
+
+ var r = @reduce(op, @as(Vector(N, TX), x));
+ expectEqual(expected, r);
+ }
+ fn doTheTest() void {
+ doTheTestReduce(.And, [4]bool{ true, false, true, true }, @as(bool, false));
+ doTheTestReduce(.Or, [4]bool{ false, true, false, false }, @as(bool, true));
+ doTheTestReduce(.Xor, [4]bool{ true, true, true, false }, @as(bool, true));
+
+ doTheTestReduce(.And, [4]u1{ 1, 0, 1, 1 }, @as(u1, 0));
+ doTheTestReduce(.Or, [4]u1{ 0, 1, 0, 0 }, @as(u1, 1));
+ doTheTestReduce(.Xor, [4]u1{ 1, 1, 1, 0 }, @as(u1, 1));
+
+ doTheTestReduce(.And, [4]u32{ 0xffffffff, 0xffff5555, 0xaaaaffff, 0x10101010 }, @as(u32, 0x1010));
+ doTheTestReduce(.Or, [4]u32{ 0xffff0000, 0xff00, 0xf0, 0xf }, ~@as(u32, 0));
+ doTheTestReduce(.Xor, [4]u32{ 0x00000000, 0x33333333, 0x88888888, 0x44444444 }, ~@as(u32, 0));
+
+ doTheTestReduce(.Min, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, -386));
+ doTheTestReduce(.Max, [4]i32{ 1234567, -386, 0, 3 }, @as(i32, 1234567));
+
+ doTheTestReduce(.Min, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 9));
+ doTheTestReduce(.Max, [4]u32{ 99, 9999, 9, 99999 }, @as(u32, 99999));
+
+ doTheTestReduce(.Min, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, -100.0));
+ doTheTestReduce(.Max, [4]f32{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f32, 10.0e9));
+
+ doTheTestReduce(.Min, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, -100.0));
+ doTheTestReduce(.Max, [4]f64{ -10.3, 10.0e9, 13.0, -100.0 }, @as(f64, 10.0e9));
+ }
+ };
+
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/stage2/arm.zig b/test/stage2/arm.zig
@@ -0,0 +1,116 @@
+const std = @import("std");
+const TestContext = @import("../../src/test.zig").TestContext;
+
+const linux_arm = std.zig.CrossTarget{
+ .cpu_arch = .arm,
+ .os_tag = .linux,
+};
+
+pub fn addCases(ctx: *TestContext) !void {
+ {
+ var case = ctx.exe("hello world", linux_arm);
+ // Regular old hello world
+ case.addCompareOutput(
+ \\export fn _start() noreturn {
+ \\ print();
+ \\ exit();
+ \\}
+ \\
+ \\fn print() void {
+ \\ asm volatile ("svc #0"
+ \\ :
+ \\ : [number] "{r7}" (4),
+ \\ [arg1] "{r0}" (1),
+ \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
+ \\ [arg3] "{r2}" (14)
+ \\ : "memory"
+ \\ );
+ \\ return;
+ \\}
+ \\
+ \\fn exit() noreturn {
+ \\ asm volatile ("svc #0"
+ \\ :
+ \\ : [number] "{r7}" (1),
+ \\ [arg1] "{r0}" (0)
+ \\ : "memory"
+ \\ );
+ \\ unreachable;
+ \\}
+ ,
+ "Hello, World!\n",
+ );
+ }
+
+ {
+ var case = ctx.exe("parameters and return values", linux_arm);
+ // Testing simple parameters and return values
+ //
+ // TODO: The parameters to the asm statement in print() had to
+ // be in a specific order because otherwise the write to r0
+ // would overwrite the len parameter which resides in r0
+ case.addCompareOutput(
+ \\export fn _start() noreturn {
+ \\ print(id(14));
+ \\ exit();
+ \\}
+ \\
+ \\fn id(x: u32) u32 {
+ \\ return x;
+ \\}
+ \\
+ \\fn print(len: u32) void {
+ \\ asm volatile ("svc #0"
+ \\ :
+ \\ : [number] "{r7}" (4),
+ \\ [arg3] "{r2}" (len),
+ \\ [arg1] "{r0}" (1),
+ \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n"))
+ \\ : "memory"
+ \\ );
+ \\ return;
+ \\}
+ \\
+ \\fn exit() noreturn {
+ \\ asm volatile ("svc #0"
+ \\ :
+ \\ : [number] "{r7}" (1),
+ \\ [arg1] "{r0}" (0)
+ \\ : "memory"
+ \\ );
+ \\ unreachable;
+ \\}
+ ,
+ "Hello, World!\n",
+ );
+ }
+
+ {
+ var case = ctx.exe("non-leaf functions", linux_arm);
+ // Testing non-leaf functions
+ case.addCompareOutput(
+ \\export fn _start() noreturn {
+ \\ foo();
+ \\ exit();
+ \\}
+ \\
+ \\fn foo() void {
+ \\ bar();
+ \\}
+ \\
+ \\fn bar() void {}
+ \\
+ \\fn exit() noreturn {
+ \\ asm volatile ("svc #0"
+ \\ :
+ \\ : [number] "{r7}" (1),
+ \\ [arg1] "{r0}" (0)
+ \\ : "memory"
+ \\ );
+ \\ unreachable;
+ \\}
+ ,
+ "",
+ );
+ }
+}
diff --git a/test/stage2/test.zig b/test/stage2/test.zig
@@ -21,11 +21,6 @@ const linux_riscv64 = std.zig.CrossTarget{
.os_tag = .linux,
};
-const linux_arm = std.zig.CrossTarget{
- .cpu_arch = .arm,
- .os_tag = .linux,
-};
-
const wasi = std.zig.CrossTarget{
.cpu_arch = .wasm32,
.os_tag = .wasi,
@@ -35,6 +30,7 @@ pub fn addCases(ctx: *TestContext) !void {
try @import("zir.zig").addCases(ctx);
try @import("cbe.zig").addCases(ctx);
try @import("spu-ii.zig").addCases(ctx);
+ try @import("arm.zig").addCases(ctx);
{
var case = ctx.exe("hello world with updates", linux_x64);
@@ -76,7 +72,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"Hello, World!\n",
);
// Now change the message only
@@ -108,7 +104,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
);
// Now we print it twice.
@@ -151,10 +147,13 @@ pub fn addCases(ctx: *TestContext) !void {
{
var case = ctx.exe("hello world", macosx_x64);
case.addError("", &[_][]const u8{":1:1: error: no entry point found"});
- }
- {
- var case = ctx.exe("hello world", linux_riscv64);
+ // Incorrect return type
+ case.addError(
+ \\export fn _start() noreturn {
+ \\}
+ , &[_][]const u8{":2:1: error: expected noreturn, found void"});
+
// Regular old hello world
case.addCompareOutput(
\\export fn _start() noreturn {
@@ -164,23 +163,23 @@ pub fn addCases(ctx: *TestContext) !void {
\\}
\\
\\fn print() void {
- \\ asm volatile ("ecall"
+ \\ asm volatile ("syscall"
\\ :
- \\ : [number] "{a7}" (64),
- \\ [arg1] "{a0}" (1),
- \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
- \\ [arg3] "{a2}" ("Hello, World!\n".len)
- \\ : "rcx", "r11", "memory"
+ \\ : [number] "{rax}" (0x2000004),
+ \\ [arg1] "{rdi}" (1),
+ \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
+ \\ [arg3] "{rdx}" (14)
+ \\ : "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
- \\ asm volatile ("ecall"
+ \\ asm volatile ("syscall"
\\ :
- \\ : [number] "{a7}" (94),
- \\ [arg1] "{a0}" (0)
- \\ : "rcx", "r11", "memory"
+ \\ : [number] "{rax}" (0x2000001),
+ \\ [arg1] "{rdi}" (0)
+ \\ : "memory"
\\ );
\\ unreachable;
\\}
@@ -190,36 +189,37 @@ pub fn addCases(ctx: *TestContext) !void {
}
{
- var case = ctx.exe("hello world", linux_arm);
+ var case = ctx.exe("hello world", linux_riscv64);
// Regular old hello world
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print();
+ \\
\\ exit();
\\}
\\
\\fn print() void {
- \\ asm volatile ("svc #0"
+ \\ asm volatile ("ecall"
\\ :
- \\ : [number] "{r7}" (4),
- \\ [arg1] "{r0}" (1),
- \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")),
- \\ [arg3] "{r2}" (14)
- \\ : "memory"
+ \\ : [number] "{a7}" (64),
+ \\ [arg1] "{a0}" (1),
+ \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
+ \\ [arg3] "{a2}" ("Hello, World!\n".len)
+ \\ : "rcx", "r11", "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
- \\ asm volatile ("svc #0"
+ \\ asm volatile ("ecall"
\\ :
- \\ : [number] "{r7}" (1),
- \\ [arg1] "{r0}" (0)
- \\ : "memory"
+ \\ : [number] "{a7}" (94),
+ \\ [arg1] "{a0}" (0)
+ \\ : "rcx", "r11", "memory"
\\ );
\\ unreachable;
\\}
- ,
+ ,
"Hello, World!\n",
);
}
@@ -244,7 +244,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"Hello, World!\n",
);
}
@@ -271,7 +271,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
}
@@ -298,7 +298,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
}
@@ -329,7 +329,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -362,7 +362,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -398,7 +398,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -435,7 +435,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -465,7 +465,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -499,7 +499,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -523,7 +523,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -562,7 +562,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"hello\nhello\nhello\nhello\n",
);
@@ -599,7 +599,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -641,7 +641,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -693,7 +693,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -755,7 +755,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -788,7 +788,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -820,7 +820,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -845,7 +845,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -871,7 +871,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"",
);
@@ -904,12 +904,50 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
- ,
+ ,
"hello\nhello\nhello\nhello\nhello\n",
);
}
{
+ var case = ctx.exe("basic import", linux_x64);
+ case.addCompareOutput(
+ \\export fn _start() noreturn {
+ \\ @import("print.zig").print();
+ \\ exit();
+ \\}
+ \\
+ \\fn exit() noreturn {
+ \\ asm volatile ("syscall"
+ \\ :
+ \\ : [number] "{rax}" (231),
+ \\ [arg1] "{rdi}" (@as(usize, 0))
+ \\ : "rcx", "r11", "memory"
+ \\ );
+ \\ unreachable;
+ \\}
+ ,
+ "Hello, World!\n",
+ );
+ try case.files.append(.{
+ .src =
+ \\pub fn print() void {
+ \\ asm volatile ("syscall"
+ \\ :
+ \\ : [number] "{rax}" (@as(usize, 1)),
+ \\ [arg1] "{rdi}" (@as(usize, 1)),
+ \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
+ \\ [arg3] "{rdx}" (@as(usize, 14))
+ \\ : "rcx", "r11", "memory"
+ \\ );
+ \\ return;
+ \\}
+ ,
+ .path = "print.zig",
+ });
+ }
+
+ {
var case = ctx.exe("wasm function calls", wasi);
case.addCompareOutput(
@@ -923,7 +961,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ bar();
\\}
\\fn bar() void {}
- ,
+ ,
"42\n",
);
@@ -941,7 +979,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ bar();
\\}
\\fn bar() void {}
- ,
+ ,
"42\n",
);
@@ -957,10 +995,10 @@ pub fn addCases(ctx: *TestContext) !void {
\\ bar();
\\}
\\fn bar() void {}
- ,
- // This is what you get when you take the bits of the IEE-754
- // representation of 42.0 and reinterpret them as an unsigned
- // integer. Guess that's a bug in wasmtime.
+ ,
+ // This is what you get when you take the bits of the IEE-754
+ // representation of 42.0 and reinterpret them as an unsigned
+ // integer. Guess that's a bug in wasmtime.
"1109917696\n",
);
}
diff --git a/test/translate_c.zig b/test/translate_c.zig
@@ -3,6 +3,22 @@ const std = @import("std");
const CrossTarget = std.zig.CrossTarget;
pub fn addCases(cases: *tests.TranslateCContext) void {
+ cases.add("macro expressions respect C operator precedence",
+ \\#define FOO *((foo) + 2)
+ \\#define VALUE (1 + 2 * 3 + 4 * 5 + 6 << 7 | 8 == 9)
+ \\#define _AL_READ3BYTES(p) ((*(unsigned char *)(p)) \
+ \\ | (*((unsigned char *)(p) + 1) << 8) \
+ \\ | (*((unsigned char *)(p) + 2) << 16))
+ , &[_][]const u8{
+ \\pub const FOO = (foo + 2).*;
+ ,
+ \\pub const VALUE = ((((1 + (2 * 3)) + (4 * 5)) + 6) << 7) | @boolToInt(8 == 9);
+ ,
+ \\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)) {
+ \\ 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);
+ \\}
+ });
+
cases.add("extern variable in block scope",
\\float bar;
\\int foo() {
@@ -2978,7 +2994,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("string concatenation in macros: three strings",
\\#define FOO "a" "b" "c"
, &[_][]const u8{
- \\pub const FOO = "a" ++ ("b" ++ "c");
+ \\pub const FOO = "a" ++ "b" ++ "c";
});
cases.add("multibyte character literals",