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.master
commit
b5a36f676b
|
@ -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}")
|
||||
set(ZIG_VERSION "" CACHE STRING "Override Zig version string. Default is to find out with git.")
|
||||
|
||||
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")
|
||||
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}"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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_)
|
||||
|
|
|
@ -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.
|
|
@ -1,172 +0,0 @@
|
|||
# `dbg(…)`
|
||||
|
||||
[![Build Status](https://travis-ci.org/sharkdp/dbg-macro.svg?branch=master)](https://travis-ci.org/sharkdp/dbg-macro) [![Build status](https://ci.appveyor.com/api/projects/status/vmo9rw4te2wifkul/branch/master?svg=true)](https://ci.appveyor.com/project/sharkdp/dbg-macro) [![Try it online](https://img.shields.io/badge/try-online-f34b7d.svg)](https://repl.it/@sharkdp/dbg-macro-demo) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](dbg.h)
|
||||
|
||||
*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)):
|
||||
|
||||
![dbg(…) macro output](https://i.imgur.com/NHEYk9A.png)
|
||||
|
||||
## 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).
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
test "std.ArrayList.basic" {
|
||||
var list = ArrayList(i32).init(testing.allocator);
|
||||
defer list.deinit();
|
||||
|
||||
test "std.ArrayList/ArrayListUnmanaged.init" {
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < 10) : (i += 1) {
|
||||
list.append(@intCast(i32, i + 1)) catch unreachable;
|
||||
}
|
||||
var list = ArrayList(i32).init(testing.allocator);
|
||||
defer list.deinit();
|
||||
|
||||
testing.expect(list.items.len == 0);
|
||||
testing.expect(list.capacity == 0);
|
||||
}
|
||||
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < 10) : (i += 1) {
|
||||
testing.expect(list.items[i] == @intCast(i32, i + 1));
|
||||
var list = ArrayListUnmanaged(i32){};
|
||||
|
||||
testing.expect(list.items.len == 0);
|
||||
testing.expect(list.capacity == 0);
|
||||
}
|
||||
}
|
||||
|
||||
test "std.ArrayList/ArrayListUnmanaged.initCapacity" {
|
||||
const a = testing.allocator;
|
||||
{
|
||||
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 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));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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(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;
|
||||
|
||||
testing.expect(list.pop() == 42);
|
||||
testing.expect(list.pop() == 33);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
test "std.ArrayList.appendNTimes" {
|
||||
var list = ArrayList(i32).init(testing.allocator);
|
||||
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);
|
||||
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);
|
||||
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 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 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);
|
||||
//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(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);
|
||||
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 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 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);
|
||||
//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);
|
||||
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);
|
||||
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);
|
||||
{
|
||||
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.replaceRange(1, 0, &new);
|
||||
try list_eq.replaceRange(1, 3, &new);
|
||||
try list_lt.replaceRange(1, 2, &new);
|
||||
try list_zero.appendSlice(&init);
|
||||
try list_eq.appendSlice(&init);
|
||||
try list_lt.appendSlice(&init);
|
||||
try list_gt.appendSlice(&init);
|
||||
|
||||
// after_range > new_items.len in function body
|
||||
testing.expect(1 + 4 > new.len);
|
||||
try list_gt.replaceRange(1, 4, &new);
|
||||
try list_zero.replaceRange(1, 0, &new);
|
||||
try list_eq.replaceRange(1, 3, &new);
|
||||
try list_lt.replaceRange(1, 2, &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 });
|
||||
// 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);
|
||||
|
|
|
@ -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() });
|
||||
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,
|
||||
},
|
||||
.Exe => {
|
||||
self.out_filename = self.builder.fmt("{}{}", .{ self.name, self.target.exeFileExt() });
|
||||
},
|
||||
.Test => {
|
||||
self.out_filename = self.builder.fmt("test{}", .{self.target.exeFileExt()});
|
||||
},
|
||||
.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 {
|
||||
|
|
|
@ -98,6 +98,16 @@ pub const AtomicOrder = enum {
|
|||
SeqCst,
|
||||
};
|
||||
|
||||
/// 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 {
|
||||
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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(.{
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
|
|
217
src/codegen.zig
217
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;
|
||||
|
|
|
@ -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 eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .eor, s, 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 sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .sub, s, 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 rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .rsb, s, 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 add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .add, s, 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 adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .adc, s, 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 sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .sbc, s, 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 rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .rsc, s, 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 adds(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .add, 1, 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 adcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .adc, 1, 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 sbcs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .sbc, 1, 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 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 mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .mov, s, rd, .r0, op2);
|
||||
pub fn orrs(cond: Condition, rd: Register, rn: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .orr, 1, rd, rn, op2);
|
||||
}
|
||||
|
||||
pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .bic, s, rd, rn, op2);
|
||||
pub fn mov(cond: Condition, rd: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .mov, 0, rd, .r0, op2);
|
||||
}
|
||||
|
||||
pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .mvn, 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, rd: Register, op2: Operand) Instruction {
|
||||
return dataProcessing(cond, .bic, 0, rd, rn, 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 str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction {
|
||||
return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0);
|
||||
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 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
|
|
117
src/link/Elf.zig
117
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();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
68
src/main.zig
68
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) });
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
// is_generic: bool
|
||||
ensure_field_index(result->type, "is_generic", 1);
|
||||
bool is_generic = type_entry->data.fn.is_generic;
|
||||
// alignment: u29
|
||||
ensure_field_index(result->type, "alignment", 1);
|
||||
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[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", 2);
|
||||
bool is_generic = type_entry->data.fn.is_generic;
|
||||
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:
|
||||
|
|
|
@ -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;
|
||||
|
|
1072
src/stage1/os.cpp
1072
src/stage1/os.cpp
File diff suppressed because it is too large
Load Diff
|
@ -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 {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
15
src/test.zig
15
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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
last = try parseCPrefixOpExpr(c, m, scope);
|
||||
_ = try appendToken(c, .Semicolon, ";");
|
||||
if (m.next().? != .Comma) {
|
||||
m.i -= 1;
|
||||
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);
|
||||
},
|
||||
else => {
|
||||
m.i -= 1;
|
||||
return node;
|
||||
},
|
||||
// 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();
|
||||
|
||||
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);
|
||||
|
||||
last = try parseCCondExpr(c, m, scope);
|
||||
_ = try appendToken(c, .Semicolon, ";");
|
||||
if (m.next().? != .Comma) {
|
||||
m.i -= 1;
|
||||
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;
|
||||
.Slash => {
|
||||
op_id = .Div;
|
||||
op_token = try appendToken(c, .Slash, "/");
|
||||
},
|
||||
.AngleBracketAngleBracketRight => {
|
||||
op_token = try appendToken(c, .AngleBracketAngleBracketRight, ">>");
|
||||
op_id = .BitShiftRight;
|
||||
.Percent => {
|
||||
op_id = .Mod;
|
||||
op_token = try appendToken(c, .Percent, "%");
|
||||
},
|
||||
.Pipe => {
|
||||
op_token = try appendToken(c, .Pipe, "|");
|
||||
op_id = .BitOr;
|
||||
else => {
|
||||
m.i -= 1;
|
||||
return node;
|
||||
},
|
||||
.Ampersand => {
|
||||
op_token = try appendToken(c, .Ampersand, "&");
|
||||
op_id = .BitAnd;
|
||||
}
|
||||
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;
|
||||
},
|
||||
.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;
|
||||
},
|
||||
.AngleBracketRight => {
|
||||
op_token = try appendToken(c, .AngleBracketRight, ">");
|
||||
op_id = .GreaterThan;
|
||||
},
|
||||
.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;
|
||||
.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);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
112
src/type.zig
112
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,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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, "");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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", .{});
|
||||
}
|
||||
|
|
|
@ -2,6 +2,197 @@ const tests = @import("tests.zig");
|
|||
const std = @import("std");
|
||||
|
||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
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 {
|
||||
\\ _ = @intToEnum(Tag, 0);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:20: error: use of undefined value here causes undefined behavior",
|
||||
});
|
||||
|
||||
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 s: S = undefined;
|
||||
\\}
|
||||
, &[_][]const 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",
|
||||
\\const TypeInfo = @import("builtin").TypeInfo;
|
||||
\\const Untagged = @Type(.{
|
||||
\\ .Union = .{
|
||||
\\ .layout = .Auto,
|
||||
\\ .tag_type = null,
|
||||
\\ .fields = &[_]TypeInfo.UnionField{
|
||||
\\ .{ .name = "foo", .field_type = @Type(.Opaque), .alignment = 1 },
|
||||
\\ },
|
||||
\\ .decls = &[_]TypeInfo.Declaration{},
|
||||
\\ },
|
||||
\\});
|
||||
\\export fn entry() void {
|
||||
\\ _ = Untagged{};
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:25: error: opaque types have unknown size and therefore cannot be directly embedded in unions",
|
||||
"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 };
|
||||
|
@ -18,26 +209,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
|||
"tmp.zig:2:37: error: expected type '[:1]const u8', found '*const [2:2]u8'",
|
||||
});
|
||||
|
||||
cases.add("@Type for union with opaque field",
|
||||
\\const TypeInfo = @import("builtin").TypeInfo;
|
||||
\\const Untagged = @Type(.{
|
||||
\\ .Union = .{
|
||||
\\ .layout = .Auto,
|
||||
\\ .tag_type = null,
|
||||
\\ .fields = &[_]TypeInfo.UnionField{
|
||||
\\ .{ .name = "foo", .field_type = @Type(.Opaque) },
|
||||
\\ },
|
||||
\\ .decls = &[_]TypeInfo.Declaration{},
|
||||
\\ },
|
||||
\\});
|
||||
\\export fn entry() void {
|
||||
\\ _ = Untagged{};
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:25: error: opaque types have unknown size and therefore cannot be directly embedded in unions",
|
||||
"tmp.zig:13:17: note: referenced here",
|
||||
});
|
||||
|
||||
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(){};
|
||||
\\}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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)))),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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,6 +147,45 @@ 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"});
|
||||
|
||||
// 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 {
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (0x2000004),
|
||||
\\ [arg1] "{rdi}" (1),
|
||||
\\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
|
||||
\\ [arg3] "{rdx}" (14)
|
||||
\\ : "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (0x2000001),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -184,42 +219,7 @@ pub fn addCases(ctx: *TestContext) !void {
|
|||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
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",
|
||||
);
|
||||
}
|
||||
|
@ -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,11 +904,49 @@ 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);
|
||||
|
||||
|
@ -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",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue