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
Andrew Kelley 2020-10-07 00:46:05 -07:00
commit b5a36f676b
78 changed files with 5755 additions and 3147 deletions

View File

@ -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}"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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_)

View File

@ -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.

View File

@ -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).

711
deps/dbg-macro/dbg.h vendored
View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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 {

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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 {

161
lib/std/crypto/aes_gcm.zig Normal file
View File

@ -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);
}

View File

@ -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 {

317
lib/std/crypto/ghash.zig Normal file
View File

@ -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);
}

66
lib/std/crypto/hkdf.zig Normal file
View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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),

View File

@ -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) {

View File

@ -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 symbolthat 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;

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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),
};
}

View File

@ -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(.{

View File

@ -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),
}
}

View File

@ -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,
};

View File

@ -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");

View File

@ -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);
}

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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});
}

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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,
});

View File

@ -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,

View File

@ -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");

View File

@ -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

View File

@ -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) });
};
}

View File

@ -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,
};

View File

@ -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;
}

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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

View File

@ -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:

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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 {

View File

@ -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),

View 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,
};
}

View File

@ -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();

View File

@ -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);
},
}
}

View File

@ -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,
};
};
};

View File

@ -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,

View File

@ -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, "");

View File

@ -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

View File

@ -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",

View File

@ -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", .{});
}

View File

@ -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(){};
\\}

View File

@ -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;
}

View File

@ -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)))),
));
}

View File

@ -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 {

View File

@ -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();
}

116
test/stage2/arm.zig Normal file
View File

@ -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;
\\}
,
"",
);
}
}

View File

@ -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",
);
}

View File

@ -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",