Merge branch 'master' into zen_stdlib

This commit is contained in:
Andrea Orru
2018-08-06 01:43:19 -04:00
369 changed files with 60808 additions and 21012 deletions

1
.gitattributes vendored
View File

@@ -1 +1,2 @@
*.zig text eol=lf
langref.html.in text eol=lf

12
.gitignore vendored
View File

@@ -1,3 +1,15 @@
# This file is for zig-specific build artifacts.
# If you have OS-specific or editor-specific files to ignore,
# such as *.swp or .DS_Store, put those in your global
# ~/.gitignore and put this in your ~/.gitconfig:
#
# [core]
# excludesfile = ~/.gitignore
#
# Cheers!
# -andrewrk
zig-cache/
build/
build-*/
docgen_tmp/

View File

@@ -1,18 +1,22 @@
sudo: required
services:
- docker
- docker
os:
- linux
- osx
- linux
- osx
dist: trusty
osx_image: xcode8.3
language: cpp
before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi
install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi
script:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi
env:
global:
- secure: QmJ+eLOxj3Irl5SHxt6lQvrj7++1AIz8bYri6RScAQGHQPIztkmbpBjAkpFgYaWPkZ04ROtamFXdS7oHtJHSECesgPoqM/CHIychQkgpDq30+TsFyYbBpDGHY+N6r2WnQTvg+9EuAp6P365us6qFS0D5zQ3P40c56uMbazFu3J4W1HZP+pLWlLjEXaN88ePhHWqNZyvwGMkLpYl3ghcrE9H4vGZQ7jenRW4UmskLEkuhUPJbQiow3Td8arJiRmLVISzWqneqNraLUpGyUVr4F3Rbjzacfoo3r9ZZynhY0mFsEye82x6TMGgH2xsNGkd91zpQuckWUT+pQv/G6FXpnEnjIJSO2Z5WAxXrx6xB1k2HZ17/4NWLF3fJVhdQJm3mS6odeGzUjgGrl1A42evxU+7VbcofEJq1aMiLgU1jUT2pt+pefCwmKJYLpEsSzuyrVxgvskQz0QpC053TAYSNf2Jj6Qhg9YDWyOeemYmDgffTqErF7AYhc6NKH0s0XKkIiNFSxorkEsfG/Ck1o+15slHNmWZXlmXToxDqFkLDoPvfGKg7koU5YTGvci/F9ZKb1juhGLxZbwap/18zN40BqA+Ip2yDBJAKxsIiwSjSIguy6g/Z1I50s0xNGOr36urfRRQX5H+rqr/xCZ63B6WSe6qBcZboWAQMDn8HLS9Xiwc=
- secure: dnb7r5guUeMOX9e7XlPUSZzmga8VW3G9Q1aa7LxEKiTjSnWhu5KpPDe8o1X3Rj6nc5iXDqmBH/C/7eNXPDyXJJWPvpE2YRpGymyUkRaakul0QBKJEaMvwy2SuAfS69CWC+TSzfGRvtSYkdpBhhLvs0h5S819S5jYbCNSCmOKfFucaP5NsHNIZ/I19oIeTPTa0/UnVm7DLFZXZjvbS+czkdyH1DhbT85sLj+XqNTzLePImE68efrjaHnlSy/CzBVJzj55UgD5i9fxNCQWzGWim/SD5xZ0zKtLycSOf6wQN2lCo0lkjw9rDlYz69mM5L9ikfYL9oHDPZnh84oXKglQ5miOHCgqs/qs4439I05lIu8i/EfbFA55YG4NyO3rL9YVOOt5gwiwvJYhDcnkVVzSl0o5bsoZgQfYvPWaIQKNkl3C53zfDQjgqS54CeDzlZpFrQTDQ1RrH8oeVC1gfYAeMabMDadox5rfZmLIN5JTf/F8iD/QdxGcoUvkEENcQgfP9PnubExtexgHGsEmqbm6ORSZ1MkEh2m3fo0f8KE6TbN1UigmcQ8nTkWBHsSmfHnB8HwJQp8mwQmDamXA+Hl3e3w4LOdYkJVlNW1/TTyJJOOvjMQCjF8SJmPHuh+QpqKbSaT9XM/vBhxbIZEufH8kawJKCBBcCNspGMNjhXfNjM0=

View File

@@ -22,7 +22,7 @@ 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} name-rev HEAD --tags --name-only --no-undefined --always
COMMAND ${GIT_EXE} -C ${CMAKE_SOURCE_DIR} name-rev HEAD --tags --name-only --no-undefined --always
OUTPUT_VARIABLE ZIG_GIT_REV
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(ZIG_GIT_REV MATCHES "\\^0$")
@@ -30,7 +30,7 @@ if(GIT_EXE)
message("WARNING: Tag does not match configured Zig version")
endif()
else()
set(ZIG_VERSION "${ZIG_VERSION_MAJOR}.${ZIG_VERSION_MINOR}.${ZIG_VERSION_PATCH}.${ZIG_GIT_REV}")
set(ZIG_VERSION "${ZIG_VERSION}+${ZIG_GIT_REV}")
endif()
endif()
message("Configuring zig version ${ZIG_VERSION}")
@@ -196,7 +196,7 @@ else()
if(MSVC)
set(ZIG_LLD_COMPILE_FLAGS "-std=c++11 -D_CRT_SECURE_NO_WARNINGS /w")
else()
set(ZIG_LLD_COMPILE_FLAGS "-std=c++11 -fno-exceptions -fno-rtti -Wno-comment")
set(ZIG_LLD_COMPILE_FLAGS "-std=c++11 -fno-exceptions -fno-rtti -Wno-comment -Wno-class-memaccess -Wno-unknown-warning-option")
endif()
set_target_properties(embedded_lld_lib PROPERTIES
COMPILE_FLAGS ${ZIG_LLD_COMPILE_FLAGS}
@@ -261,12 +261,15 @@ endif()
set(EMBEDDED_SOFTFLOAT_SOURCES
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/f128M_isSignalingNaN.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF128M.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF16UI.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF32UI.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_commonNaNToF64UI.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f128MToCommonNaN.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f16UIToCommonNaN.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f32UIToCommonNaN.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_f64UIToCommonNaN.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNF128M.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/s_propagateNaNF16UI.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/8086/softfloat_raiseFlags.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_add.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_div.c"
@@ -293,8 +296,20 @@ set(EMBEDDED_SOFTFLOAT_SOURCES
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui32_r_minMag.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui64.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f128M_to_ui64_r_minMag.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_add.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_div.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_eq.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_lt.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_mul.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_rem.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_roundToInt.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_sqrt.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_sub.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_f128M.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f16_to_f64.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f32_to_f128M.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_f128M.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/f64_to_f16.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_add256M.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addCarryM.c"
"${CMAKE_SOURCE_DIR}/deps/SoftFloat-3e/source/s_addComplCarryM.c"
@@ -411,10 +426,15 @@ set(ZIG_SOURCES
)
set(ZIG_CPP_SOURCES
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
"${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp"
)
set(ZIG_STD_FILES
"array_list.zig"
"atomic/index.zig"
"atomic/int.zig"
"atomic/queue.zig"
"atomic/stack.zig"
"base64.zig"
"buf_map.zig"
"buf_set.zig"
@@ -424,33 +444,43 @@ set(ZIG_STD_FILES
"c/index.zig"
"c/linux.zig"
"c/windows.zig"
"crypto/blake2.zig"
"crypto/hmac.zig"
"crypto/index.zig"
"crypto/md5.zig"
"crypto/sha1.zig"
"crypto/sha2.zig"
"crypto/sha3.zig"
"crypto/blake2.zig"
"crypto/hmac.zig"
"cstr.zig"
"debug/failing_allocator.zig"
"debug/index.zig"
"dwarf.zig"
"dynamic_library.zig"
"elf.zig"
"empty.zig"
"event.zig"
"event/channel.zig"
"event/future.zig"
"event/group.zig"
"event/lock.zig"
"event/locked.zig"
"event/loop.zig"
"event/tcp.zig"
"fmt/errol/enum3.zig"
"fmt/errol/index.zig"
"fmt/errol/lookup.zig"
"fmt/index.zig"
"hash_map.zig"
"hash/index.zig"
"hash/adler.zig"
"hash/crc.zig"
"hash/fnv.zig"
"hash/index.zig"
"hash/siphash.zig"
"hash_map.zig"
"heap.zig"
"index.zig"
"io.zig"
"json.zig"
"lazy_init.zig"
"linked_list.zig"
"macho.zig"
"math/acos.zig"
@@ -460,8 +490,32 @@ set(ZIG_STD_FILES
"math/atan.zig"
"math/atan2.zig"
"math/atanh.zig"
"math/big/index.zig"
"math/big/int.zig"
"math/cbrt.zig"
"math/ceil.zig"
"math/complex/abs.zig"
"math/complex/acos.zig"
"math/complex/acosh.zig"
"math/complex/arg.zig"
"math/complex/asin.zig"
"math/complex/asinh.zig"
"math/complex/atan.zig"
"math/complex/atanh.zig"
"math/complex/conj.zig"
"math/complex/cos.zig"
"math/complex/cosh.zig"
"math/complex/exp.zig"
"math/complex/index.zig"
"math/complex/ldexp.zig"
"math/complex/log.zig"
"math/complex/pow.zig"
"math/complex/proj.zig"
"math/complex/sin.zig"
"math/complex/sinh.zig"
"math/complex/sqrt.zig"
"math/complex/tan.zig"
"math/complex/tanh.zig"
"math/copysign.zig"
"math/cos.zig"
"math/cosh.zig"
@@ -498,24 +552,35 @@ set(ZIG_STD_FILES
"math/tan.zig"
"math/tanh.zig"
"math/trunc.zig"
"math/x86_64/sqrt.zig"
"mem.zig"
"net.zig"
"os/child_process.zig"
"os/darwin.zig"
"os/darwin_errno.zig"
"os/darwin/errno.zig"
"os/epoch.zig"
"os/file.zig"
"os/get_app_data_dir.zig"
"os/get_user_id.zig"
"os/index.zig"
"os/linux/errno.zig"
"os/linux/index.zig"
"os/linux/vdso.zig"
"os/linux/x86_64.zig"
"os/path.zig"
"os/time.zig"
"os/windows/advapi32.zig"
"os/windows/error.zig"
"os/windows/index.zig"
"os/windows/kernel32.zig"
"os/windows/ole32.zig"
"os/windows/shell32.zig"
"os/windows/shlwapi.zig"
"os/windows/user32.zig"
"os/windows/util.zig"
"os/zen.zig"
"rand/index.zig"
"rand/ziggurat.zig"
"segmented_list.zig"
"sort.zig"
"special/bootstrap.zig"
"special/bootstrap_lib.zig"
@@ -525,6 +590,8 @@ set(ZIG_STD_FILES
"special/compiler_rt/aulldiv.zig"
"special/compiler_rt/aullrem.zig"
"special/compiler_rt/comparetf2.zig"
"special/compiler_rt/divti3.zig"
"special/compiler_rt/extendXfYf2.zig"
"special/compiler_rt/fixuint.zig"
"special/compiler_rt/fixunsdfdi.zig"
"special/compiler_rt/fixunsdfsi.zig"
@@ -535,7 +602,17 @@ set(ZIG_STD_FILES
"special/compiler_rt/fixunstfdi.zig"
"special/compiler_rt/fixunstfsi.zig"
"special/compiler_rt/fixunstfti.zig"
"special/compiler_rt/floatunditf.zig"
"special/compiler_rt/floatunsitf.zig"
"special/compiler_rt/floatuntidf.zig"
"special/compiler_rt/floatuntisf.zig"
"special/compiler_rt/floatuntitf.zig"
"special/compiler_rt/floattidf.zig"
"special/compiler_rt/floattisf.zig"
"special/compiler_rt/floattitf.zig"
"special/compiler_rt/muloti4.zig"
"special/compiler_rt/index.zig"
"special/compiler_rt/truncXfYf2.zig"
"special/compiler_rt/udivmod.zig"
"special/compiler_rt/udivmoddi4.zig"
"special/compiler_rt/udivmodti4.zig"
@@ -546,7 +623,9 @@ set(ZIG_STD_FILES
"unicode.zig"
"zig/ast.zig"
"zig/index.zig"
"zig/parser.zig"
"zig/parse.zig"
"zig/parse_string_literal.zig"
"zig/render.zig"
"zig/tokenizer.zig"
)

View File

@@ -1,9 +1,9 @@
![ZIG](http://ziglang.org/zig-logo.svg)
![ZIG](https://ziglang.org/zig-logo.svg)
A programming language designed for robustness, optimality, and
clarity.
[ziglang.org](http://ziglang.org)
[ziglang.org](https://ziglang.org)
## Feature Highlights
@@ -21,19 +21,19 @@ clarity.
* Compatible with C libraries with no wrapper necessary. Directly include
C .h files and get access to the functions and symbols therein.
* Provides standard library which competes with the C standard library and is
always compiled against statically in source form. Compile units do not
always compiled against statically in source form. Zig binaries do not
depend on libc unless explicitly linked.
* Nullable type instead of null pointers.
* Optional type instead of null pointers.
* Safe unions, tagged unions, and C ABI compatible unions.
* Generics so that one can write efficient data structures that work for any
data type.
* No header files required. Top level declarations are entirely
order-independent.
* Compile-time code execution. Compile-time reflection.
* Partial compile-time function evaluation with eliminates the need for
* Partial compile-time function evaluation which eliminates the need for
a preprocessor or macros.
* The binaries produced by Zig have complete debugging information so you can,
for example, use GDB or MSVC to debug your software.
for example, use GDB, MSVC, or LLDB to debug your software.
* Built-in unit tests with `zig test`.
* Friendly toward package maintainers. Reproducible build, bootstrapping
process carefully documented. Issues filed by package maintainers are
@@ -55,66 +55,28 @@ that counts as "freestanding" for the purposes of this table.
|i386 | OK | planned | OK | planned | planned |
|x86_64 | OK | OK | OK | OK | planned |
|arm | OK | planned | planned | N/A | planned |
|aarch64 | OK | planned | planned | planned | planned |
|bpf | OK | planned | planned | N/A | planned |
|hexagon | OK | planned | planned | N/A | planned |
|mips | OK | planned | planned | N/A | planned |
|powerpc | OK | planned | planned | N/A | planned |
|r600 | OK | planned | planned | N/A | planned |
|amdgcn | OK | planned | planned | N/A | planned |
|sparc | OK | planned | planned | N/A | planned |
|s390x | OK | planned | planned | N/A | planned |
|thumb | OK | planned | planned | N/A | planned |
|spir | OK | planned | planned | N/A | planned |
|lanai | OK | planned | planned | N/A | planned |
|aarch64 | OK | planned | N/A | planned | planned |
|bpf | OK | planned | N/A | N/A | planned |
|hexagon | OK | planned | N/A | N/A | planned |
|mips | OK | planned | N/A | N/A | planned |
|powerpc | OK | planned | N/A | N/A | planned |
|r600 | OK | planned | N/A | N/A | planned |
|amdgcn | OK | planned | N/A | N/A | planned |
|sparc | OK | planned | N/A | N/A | planned |
|s390x | OK | planned | N/A | N/A | planned |
|thumb | OK | planned | N/A | N/A | planned |
|spir | OK | planned | N/A | N/A | planned |
|lanai | OK | planned | N/A | N/A | planned |
## Community
* IRC: `#zig` on Freenode.
* IRC: `#zig` on Freenode ([Channel Logs](https://irclog.whitequark.org/zig/)).
* Reddit: [/r/zig](https://www.reddit.com/r/zig)
* Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang)
### Wanted: Windows Developers
Flesh out the standard library for Windows, streamline Zig installation and
distribution for Windows. Work with LLVM and LLD teams to improve
PDB/CodeView/MSVC debugging. Implement stack traces for Windows in the MinGW
environment and the MSVC environment.
### Wanted: MacOS and iOS Developers
Flesh out the standard library for MacOS. Improve the MACH-O linker. Implement
stack traces for MacOS. Streamline the process of using Zig to build for
iOS.
### Wanted: Android Developers
Flesh out the standard library for Android. Streamline the process of using
Zig to build for Android and for depending on Zig code on Android.
### Wanted: Web Developers
Figure out what are the use cases for compiling Zig to WebAssembly. Create demo
projects with it and streamline experience for users trying to output
WebAssembly. Work on the documentation generator outputting useful searchable html
documentation. Create Zig modules for common web tasks such as WebSockets and gzip.
### Wanted: Embedded Developers
Flesh out the standard library for uncommon CPU architectures and OS targets.
Drive issue discussion for cross compiling and using Zig in constrained
or unusual environments.
### Wanted: Game Developers
Create cross platform Zig modules to compete with SDL and GLFW. Create an
OpenGL library that does not depend on libc. Drive the usability of Zig
for video games. Create a general purpose allocator that does not depend on
libc. Create demo games using Zig.
## Building
[![Build Status](https://travis-ci.org/zig-lang/zig.svg?branch=master)](https://travis-ci.org/zig-lang/zig)
[![Build Status](https://travis-ci.org/ziglang/zig.svg?branch=master)](https://travis-ci.org/ziglang/zig)
[![Build status](https://ci.appveyor.com/api/projects/status/4t80mk2dmucrc38i/branch/master?svg=true)](https://ci.appveyor.com/project/andrewrk/zig-d3l86/branch/master)
### Stage 1: Build Zig from C++ Source Code
@@ -161,7 +123,7 @@ bin/zig build --build-file ../build.zig test
##### Windows
See https://github.com/zig-lang/zig/wiki/Building-Zig-on-Windows
See https://github.com/ziglang/zig/wiki/Building-Zig-on-Windows
### Stage 2: Build Self-Hosted Zig from Zig Source Code
@@ -182,6 +144,9 @@ binary.
This is the actual compiler binary that we will install to the system.
*Note: Stage 2 compiler is not yet able to build Stage 3. Building Stage 3 is
not yet supported.*
#### Debug / Development Build
```

241
build.zig
View File

@@ -10,13 +10,13 @@ const ArrayList = std.ArrayList;
const Buffer = std.Buffer;
const io = std.io;
pub fn build(b: &Builder) !void {
pub fn build(b: *Builder) !void {
const mode = b.standardReleaseOptions();
var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
const rel_zig_exe = try os.path.relative(b.allocator, b.build_root, b.zig_exe);
var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8 {
var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{
docgen_exe.getOutputPath(),
rel_zig_exe,
"doc/langref.html.in",
@@ -30,109 +30,79 @@ pub fn build(b: &Builder) !void {
const test_step = b.step("test", "Run all the tests");
// find the stage0 build artifacts because we're going to re-use config.h and zig_cpp library
const build_info = try b.exec([][]const u8{b.zig_exe, "BUILD_INFO"});
const build_info = try b.exec([][]const u8{
b.zig_exe,
"BUILD_INFO",
});
var index: usize = 0;
const cmake_binary_dir = nextValue(&index, build_info);
const cxx_compiler = nextValue(&index, build_info);
const llvm_config_exe = nextValue(&index, build_info);
const lld_include_dir = nextValue(&index, build_info);
const lld_libraries = nextValue(&index, build_info);
const std_files = nextValue(&index, build_info);
const c_header_files = nextValue(&index, build_info);
const dia_guids_lib = nextValue(&index, build_info);
var ctx = Context{
.cmake_binary_dir = nextValue(&index, build_info),
.cxx_compiler = nextValue(&index, build_info),
.llvm_config_exe = nextValue(&index, build_info),
.lld_include_dir = nextValue(&index, build_info),
.lld_libraries = nextValue(&index, build_info),
.std_files = nextValue(&index, build_info),
.c_header_files = nextValue(&index, build_info),
.dia_guids_lib = nextValue(&index, build_info),
.llvm = undefined,
.no_rosegment = b.option(bool, "no-rosegment", "Workaround to enable valgrind builds") orelse false,
};
ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
const llvm = findLLVM(b, llvm_config_exe) catch unreachable;
var test_stage2 = b.addTest("src-self-hosted/test.zig");
test_stage2.setBuildMode(builtin.Mode.Debug);
var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
exe.setBuildMode(mode);
// This is for finding /lib/libz.a on alpine linux.
// TODO turn this into -Dextra-lib-path=/lib option
exe.addLibPath("/lib");
exe.addIncludeDir("src");
exe.addIncludeDir(cmake_binary_dir);
addCppLib(b, exe, cmake_binary_dir, "zig_cpp");
if (lld_include_dir.len != 0) {
exe.addIncludeDir(lld_include_dir);
var it = mem.split(lld_libraries, ";");
while (it.next()) |lib| {
exe.addObjectFile(lib);
}
} else {
addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf");
addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff");
addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib");
}
dependOnLib(exe, llvm);
if (exe.target.getOs() == builtin.Os.linux) {
const libstdcxx_path_padded = try b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"});
const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next();
if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) {
warn(
\\Unable to determine path to libstdc++.a
\\On Fedora, install libstdc++-static and try again.
\\
);
return error.RequiredLibraryNotFound;
}
exe.addObjectFile(libstdcxx_path);
exe.linkSystemLibrary("pthread");
} else if (exe.target.isDarwin()) {
exe.linkSystemLibrary("c++");
}
if (dia_guids_lib.len != 0) {
exe.addObjectFile(dia_guids_lib);
}
if (exe.target.getOs() != builtin.Os.windows) {
exe.linkSystemLibrary("xml2");
}
exe.linkSystemLibrary("c");
try configureStage2(b, test_stage2, ctx);
try configureStage2(b, exe, ctx);
b.default_step.dependOn(&exe.step);
const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") ?? false;
const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false;
if (!skip_self_hosted) {
test_step.dependOn(&exe.step);
}
const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") ?? false;
const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") orelse false;
exe.setVerboseLink(verbose_link_exe);
b.installArtifact(exe);
installStdLib(b, std_files);
installCHeaders(b, c_header_files);
installStdLib(b, ctx.std_files);
installCHeaders(b, ctx.c_header_files);
const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") ?? false;
test_step.dependOn(docs_step);
const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
test_stage2_step.dependOn(&test_stage2.step);
test_step.dependOn(test_stage2_step);
test_step.dependOn(tests.addPkgTests(b, test_filter,
"test/behavior.zig", "behavior", "Run the behavior tests",
with_lldb));
const all_modes = []builtin.Mode{
builtin.Mode.Debug,
builtin.Mode.ReleaseSafe,
builtin.Mode.ReleaseFast,
builtin.Mode.ReleaseSmall,
};
const modes = if (skip_release) []builtin.Mode{builtin.Mode.Debug} else all_modes;
test_step.dependOn(tests.addPkgTests(b, test_filter,
"std/index.zig", "std", "Run the standard library tests",
with_lldb));
test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", modes));
test_step.dependOn(tests.addPkgTests(b, test_filter,
"std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests",
with_lldb));
test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", modes));
test_step.dependOn(tests.addCompareOutputTests(b, test_filter));
test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter));
test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes));
test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
test_step.dependOn(tests.addBuildExampleTests(b, test_filter, modes));
test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
test_step.dependOn(tests.addTranslateCTests(b, test_filter));
test_step.dependOn(tests.addGenHTests(b, test_filter));
test_step.dependOn(docs_step);
}
fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) void {
fn dependOnLib(lib_exe_obj: var, dep: *const LibraryDep) void {
for (dep.libdirs.toSliceConst()) |lib_dir| {
lib_exe_obj.addLibPath(lib_dir);
}
@@ -147,10 +117,9 @@ fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) vo
}
}
fn addCppLib(b: &Builder, lib_exe_obj: &std.build.LibExeObjStep, cmake_binary_dir: []const u8, lib_name: []const u8) void {
fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void {
const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib";
lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp",
b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable);
lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable);
}
const LibraryDep = struct {
@@ -160,12 +129,22 @@ const LibraryDep = struct {
includes: ArrayList([]const u8),
};
fn findLLVM(b: &Builder, llvm_config_exe: []const u8) !LibraryDep {
const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"});
const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"});
const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"});
fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
const libs_output = try b.exec([][]const u8{
llvm_config_exe,
"--libs",
"--system-libs",
});
const includes_output = try b.exec([][]const u8{
llvm_config_exe,
"--includedir",
});
const libdir_output = try b.exec([][]const u8{
llvm_config_exe,
"--libdir",
});
var result = LibraryDep {
var result = LibraryDep{
.libs = ArrayList([]const u8).init(b.allocator),
.system_libs = ArrayList([]const u8).init(b.allocator),
.includes = ArrayList([]const u8).init(b.allocator),
@@ -208,7 +187,7 @@ fn findLLVM(b: &Builder, llvm_config_exe: []const u8) !LibraryDep {
return result;
}
pub fn installStdLib(b: &Builder, stdlib_files: []const u8) void {
pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {
var it = mem.split(stdlib_files, ";");
while (it.next()) |stdlib_file| {
const src_path = os.path.join(b.allocator, "std", stdlib_file) catch unreachable;
@@ -217,7 +196,7 @@ pub fn installStdLib(b: &Builder, stdlib_files: []const u8) void {
}
}
pub fn installCHeaders(b: &Builder, c_header_files: []const u8) void {
pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void {
var it = mem.split(c_header_files, ";");
while (it.next()) |c_header_file| {
const src_path = os.path.join(b.allocator, "c_headers", c_header_file) catch unreachable;
@@ -226,21 +205,89 @@ pub fn installCHeaders(b: &Builder, c_header_files: []const u8) void {
}
}
fn nextValue(index: &usize, build_info: []const u8) []const u8 {
const start = *index;
while (true) : (*index += 1) {
switch (build_info[*index]) {
fn nextValue(index: *usize, build_info: []const u8) []const u8 {
const start = index.*;
while (true) : (index.* += 1) {
switch (build_info[index.*]) {
'\n' => {
const result = build_info[start..*index];
*index += 1;
const result = build_info[start..index.*];
index.* += 1;
return result;
},
'\r' => {
const result = build_info[start..*index];
*index += 2;
const result = build_info[start..index.*];
index.* += 2;
return result;
},
else => continue,
}
}
}
fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
// This is for finding /lib/libz.a on alpine linux.
// TODO turn this into -Dextra-lib-path=/lib option
exe.addLibPath("/lib");
exe.setNoRoSegment(ctx.no_rosegment);
exe.addIncludeDir("src");
exe.addIncludeDir(ctx.cmake_binary_dir);
addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp");
if (ctx.lld_include_dir.len != 0) {
exe.addIncludeDir(ctx.lld_include_dir);
var it = mem.split(ctx.lld_libraries, ";");
while (it.next()) |lib| {
exe.addObjectFile(lib);
}
} else {
addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_wasm");
addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_elf");
addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_coff");
addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_lib");
}
dependOnLib(exe, ctx.llvm);
if (exe.target.getOs() == builtin.Os.linux) {
const libstdcxx_path_padded = try b.exec([][]const u8{
ctx.cxx_compiler,
"-print-file-name=libstdc++.a",
});
const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?;
if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) {
warn(
\\Unable to determine path to libstdc++.a
\\On Fedora, install libstdc++-static and try again.
\\
);
return error.RequiredLibraryNotFound;
}
exe.addObjectFile(libstdcxx_path);
exe.linkSystemLibrary("pthread");
} else if (exe.target.isDarwin()) {
exe.linkSystemLibrary("c++");
}
if (ctx.dia_guids_lib.len != 0) {
exe.addObjectFile(ctx.dia_guids_lib);
}
if (exe.target.getOs() != builtin.Os.windows) {
exe.linkSystemLibrary("xml2");
}
exe.linkSystemLibrary("c");
}
const Context = struct {
cmake_binary_dir: []const u8,
cxx_compiler: []const u8,
llvm_config_exe: []const u8,
lld_include_dir: []const u8,
lld_libraries: []const u8,
std_files: []const u8,
c_header_files: []const u8,
dia_guids_lib: []const u8,
llvm: LibraryDep,
no_rosegment: bool,
};

View File

@@ -6,5 +6,4 @@ build_script:
after_build:
- '%APPVEYOR_BUILD_FOLDER%\ci\appveyor\after_build.bat'
cache:
- 'llvm+clang-5.0.1-win64-msvc-release.tar.xz'
- 'llvm+clang-6.0.0-win64-msvc-release.tar.xz'

View File

@@ -72,6 +72,9 @@ bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {
exitLld(errorCount() ? 1 : 0);
freeArena();
ObjFile::Instances.clear();
ImportFile::Instances.clear();
BitcodeFile::Instances.clear();
return !errorCount();
}

View File

@@ -6,7 +6,7 @@ Every type has a "handle". If a type is a simple primitive type such as i32 or
f64, the handle is "by value", meaning that we pass around the value itself when
we refer to a value of that type.
If a type is a container, error union, maybe type, slice, or array, then its
If a type is a container, error union, optional type, slice, or array, then its
handle is a pointer, and everywhere we refer to a value of this type we refer to
a pointer.
@@ -19,7 +19,7 @@ Error union types are represented as:
payload: T,
}
Maybe types are represented as:
Optional types are represented as:
struct {
payload: T,
@@ -28,6 +28,6 @@ Maybe types are represented as:
## Data Optimizations
Maybe pointer types are special: the 0x0 pointer value is used to represent a
null pointer. Thus, instead of the struct above, maybe pointer types are
Optional pointer types are special: the 0x0 pointer value is used to represent a
null pointer. Thus, instead of the struct above, optional pointer types are
represented as a `usize` in codegen and the handle is by value.

View File

@@ -25,13 +25,13 @@ pub fn main() !void {
if (!args_it.skip()) @panic("expected self arg");
const zig_exe = try (args_it.next(allocator) ?? @panic("expected zig exe arg"));
const zig_exe = try (args_it.next(allocator) orelse @panic("expected zig exe arg"));
defer allocator.free(zig_exe);
const in_file_name = try (args_it.next(allocator) ?? @panic("expected input arg"));
const in_file_name = try (args_it.next(allocator) orelse @panic("expected input arg"));
defer allocator.free(in_file_name);
const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg"));
const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg"));
defer allocator.free(out_file_name);
var in_file = try os.File.openRead(allocator, in_file_name);
@@ -51,14 +51,8 @@ pub fn main() !void {
var toc = try genToc(allocator, &tokenizer);
try os.makePath(allocator, tmp_dir_name);
defer {
// TODO issue #709
// disabled to pass CI tests, but obviously we want to implement this
// and then remove this workaround
if (builtin.os != builtin.Os.windows) {
os.deleteTree(allocator, tmp_dir_name) catch {};
}
}
defer os.deleteTree(allocator, tmp_dir_name) catch {};
try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);
try buffered_out_stream.flush();
}
@@ -95,7 +89,7 @@ const Tokenizer = struct {
};
fn init(source_file_name: []const u8, buffer: []const u8) Tokenizer {
return Tokenizer {
return Tokenizer{
.buffer = buffer,
.index = 0,
.state = State.Start,
@@ -104,8 +98,8 @@ const Tokenizer = struct {
};
}
fn next(self: &Tokenizer) Token {
var result = Token {
fn next(self: *Tokenizer) Token {
var result = Token{
.id = Token.Id.Eof,
.start = self.index,
.end = undefined,
@@ -196,8 +190,8 @@ const Tokenizer = struct {
line_end: usize,
};
fn getTokenLocation(self: &Tokenizer, token: &const Token) Location {
var loc = Location {
fn getTokenLocation(self: *Tokenizer, token: *const Token) Location {
var loc = Location{
.line = 0,
.column = 0,
.line_start = 0,
@@ -221,7 +215,7 @@ const Tokenizer = struct {
}
};
fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const u8, args: ...) error {
fn parseError(tokenizer: *Tokenizer, token: *const Token, comptime fmt: []const u8, args: ...) error {
const loc = tokenizer.getTokenLocation(token);
warn("{}:{}:{}: error: " ++ fmt ++ "\n", tokenizer.source_file_name, loc.line + 1, loc.column + 1, args);
if (loc.line_start <= loc.line_end) {
@@ -244,13 +238,13 @@ fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const
return error.ParseError;
}
fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) !void {
fn assertToken(tokenizer: *Tokenizer, token: *const Token, id: Token.Id) !void {
if (token.id != id) {
return parseError(tokenizer, token, "expected {}, found {}", @tagName(id), @tagName(token.id));
}
}
fn eatToken(tokenizer: &Tokenizer, id: Token.Id) !Token {
fn eatToken(tokenizer: *Tokenizer, id: Token.Id) !Token {
const token = tokenizer.next();
try assertToken(tokenizer, token, id);
return token;
@@ -300,6 +294,7 @@ const Link = struct {
const Node = union(enum) {
Content: []const u8,
Nav,
Builtin,
HeaderOpen: HeaderOpen,
SeeAlso: []const SeeAlsoItem,
Code: Code,
@@ -317,7 +312,7 @@ const Action = enum {
Close,
};
fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
var urls = std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator);
errdefer urls.deinit();
@@ -346,7 +341,7 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
break;
},
Token.Id.Content => {
try nodes.append(Node {.Content = tokenizer.buffer[token.start..token.end] });
try nodes.append(Node{ .Content = tokenizer.buffer[token.start..token.end] });
},
Token.Id.BracketOpen => {
const tag_token = try eatToken(tokenizer, Token.Id.TagContent);
@@ -356,6 +351,9 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
_ = try eatToken(tokenizer, Token.Id.BracketClose);
try nodes.append(Node.Nav);
} else if (mem.eql(u8, tag_name, "builtin")) {
_ = try eatToken(tokenizer, Token.Id.BracketClose);
try nodes.append(Node.Builtin);
} else if (mem.eql(u8, tag_name, "header_open")) {
_ = try eatToken(tokenizer, Token.Id.Separator);
const content_token = try eatToken(tokenizer, Token.Id.TagContent);
@@ -365,11 +363,13 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
header_stack_size += 1;
const urlized = try urlize(allocator, content);
try nodes.append(Node{.HeaderOpen = HeaderOpen {
.name = content,
.url = urlized,
.n = header_stack_size,
}});
try nodes.append(Node{
.HeaderOpen = HeaderOpen{
.name = content,
.url = urlized,
.n = header_stack_size,
},
});
if (try urls.put(urlized, tag_token)) |other_tag_token| {
parseError(tokenizer, tag_token, "duplicate header url: #{}", urlized) catch {};
parseError(tokenizer, other_tag_token, "other tag here") catch {};
@@ -407,14 +407,14 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
switch (see_also_tok.id) {
Token.Id.TagContent => {
const content = tokenizer.buffer[see_also_tok.start..see_also_tok.end];
try list.append(SeeAlsoItem {
try list.append(SeeAlsoItem{
.name = content,
.token = see_also_tok,
});
},
Token.Id.Separator => {},
Token.Id.BracketClose => {
try nodes.append(Node {.SeeAlso = list.toOwnedSlice() } );
try nodes.append(Node{ .SeeAlso = list.toOwnedSlice() });
break;
},
else => return parseError(tokenizer, see_also_tok, "invalid see_also token"),
@@ -438,8 +438,8 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
}
};
try nodes.append(Node {
.Link = Link {
try nodes.append(Node{
.Link = Link{
.url = try urlize(allocator, url_name),
.name = name,
.token = name_tok,
@@ -463,24 +463,24 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
var code_kind_id: Code.Id = undefined;
var is_inline = false;
if (mem.eql(u8, code_kind_str, "exe")) {
code_kind_id = Code.Id { .Exe = ExpectedOutcome.Succeed };
code_kind_id = Code.Id{ .Exe = ExpectedOutcome.Succeed };
} else if (mem.eql(u8, code_kind_str, "exe_err")) {
code_kind_id = Code.Id { .Exe = ExpectedOutcome.Fail };
code_kind_id = Code.Id{ .Exe = ExpectedOutcome.Fail };
} else if (mem.eql(u8, code_kind_str, "test")) {
code_kind_id = Code.Id.Test;
} else if (mem.eql(u8, code_kind_str, "test_err")) {
code_kind_id = Code.Id { .TestError = name};
code_kind_id = Code.Id{ .TestError = name };
name = "test";
} else if (mem.eql(u8, code_kind_str, "test_safety")) {
code_kind_id = Code.Id { .TestSafety = name};
code_kind_id = Code.Id{ .TestSafety = name };
name = "test";
} else if (mem.eql(u8, code_kind_str, "obj")) {
code_kind_id = Code.Id { .Obj = null };
code_kind_id = Code.Id{ .Obj = null };
} else if (mem.eql(u8, code_kind_str, "obj_err")) {
code_kind_id = Code.Id { .Obj = name };
code_kind_id = Code.Id{ .Obj = name };
name = "test";
} else if (mem.eql(u8, code_kind_str, "syntax")) {
code_kind_id = Code.Id { .Obj = null };
code_kind_id = Code.Id{ .Obj = null };
is_inline = true;
} else {
return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {}", code_kind_str);
@@ -514,17 +514,20 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
return parseError(tokenizer, end_code_tag, "invalid token inside code_begin: {}", end_tag_name);
}
_ = try eatToken(tokenizer, Token.Id.BracketClose);
} else unreachable; // TODO issue #707
try nodes.append(Node {.Code = Code {
.id = code_kind_id,
.name = name,
.source_token = source_token,
.is_inline = is_inline,
.mode = mode,
.link_objects = link_objects.toOwnedSlice(),
.target_windows = target_windows,
.link_libc = link_libc,
}});
} else
unreachable; // TODO issue #707
try nodes.append(Node{
.Code = Code{
.id = code_kind_id,
.name = name,
.source_token = source_token,
.is_inline = is_inline,
.mode = mode,
.link_objects = link_objects.toOwnedSlice(),
.target_windows = target_windows,
.link_libc = link_libc,
},
});
tokenizer.code_node_count += 1;
} else {
return parseError(tokenizer, tag_token, "unrecognized tag name: {}", tag_name);
@@ -534,14 +537,14 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc {
}
}
return Toc {
return Toc{
.nodes = nodes.toOwnedSlice(),
.toc = toc_buf.toOwnedSlice(),
.urls = urls,
};
}
fn urlize(allocator: &mem.Allocator, input: []const u8) ![]u8 {
fn urlize(allocator: *mem.Allocator, input: []const u8) ![]u8 {
var buf = try std.Buffer.initSize(allocator, 0);
defer buf.deinit();
@@ -561,7 +564,7 @@ fn urlize(allocator: &mem.Allocator, input: []const u8) ![]u8 {
return buf.toOwnedSlice();
}
fn escapeHtml(allocator: &mem.Allocator, input: []const u8) ![]u8 {
fn escapeHtml(allocator: *mem.Allocator, input: []const u8) ![]u8 {
var buf = try std.Buffer.initSize(allocator, 0);
defer buf.deinit();
@@ -603,7 +606,7 @@ test "term color" {
assert(mem.eql(u8, result, "A<span class=\"t32\">green</span>B"));
}
fn termColor(allocator: &mem.Allocator, input: []const u8) ![]u8 {
fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {
var buf = try std.Buffer.initSize(allocator, 0);
defer buf.deinit();
@@ -683,8 +686,14 @@ fn termColor(allocator: &mem.Allocator, input: []const u8) ![]u8 {
return buf.toOwnedSlice();
}
fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var, zig_exe: []const u8) !void {
fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void {
var code_progress_index: usize = 0;
var env_map = try os.getEnvMap(allocator);
try env_map.set("ZIG_DEBUG_COLOR", "1");
const builtin_code = try escapeHtml(allocator, try getBuiltinCode(allocator, &env_map, zig_exe));
for (toc.nodes) |node| {
switch (node) {
Node.Content => |data| {
@@ -699,6 +708,9 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
Node.Nav => {
try out.write(toc.toc);
},
Node.Builtin => {
try out.print("<pre><code class=\"zig\">{}</code></pre>", builtin_code);
},
Node.HeaderOpen => |info| {
try out.print("<h{} id=\"{}\">{}</h{}>\n", info.n, info.url, info.name, info.n);
},
@@ -727,16 +739,19 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);
try io.writeFile(allocator, tmp_source_file_name, trimmed_raw_source);
switch (code.id) {
Code.Id.Exe => |expected_outcome| {
const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, exe_ext);
const tmp_bin_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_bin_ext);
var build_args = std.ArrayList([]const u8).init(allocator);
defer build_args.deinit();
try build_args.appendSlice([][]const u8 {zig_exe,
"build-exe", tmp_source_file_name,
"--output", tmp_bin_file_name,
try build_args.appendSlice([][]const u8{
zig_exe,
"build-exe",
tmp_source_file_name,
"--output",
tmp_bin_file_name,
});
try out.print("<pre><code class=\"shell\">$ zig build-exe {}.zig", code.name);
switch (code.mode) {
@@ -749,6 +764,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
try build_args.append("--release-fast");
try out.print(" --release-fast");
},
builtin.Mode.ReleaseSmall => {
try build_args.append("--release-small");
try out.print(" --release-small");
},
}
for (code.link_objects) |link_object| {
const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext);
@@ -762,18 +781,20 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
try build_args.append("c");
try out.print(" --library c");
}
_ = exec(allocator, build_args.toSliceConst()) catch return parseError(
tokenizer, code.source_token, "example failed to compile");
_ = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile");
const run_args = [][]const u8 {tmp_bin_file_name};
const run_args = [][]const u8{tmp_bin_file_name};
const result = if (expected_outcome == ExpectedOutcome.Fail) blk: {
const result = try os.ChildProcess.exec(allocator, run_args, null, null, max_doc_file_size);
const result = try os.ChildProcess.exec(allocator, run_args, null, &env_map, max_doc_file_size);
switch (result.term) {
os.ChildProcess.Term.Exited => |exit_code| {
if (exit_code == 0) {
warn("{}\nThe following command incorrectly succeeded:\n", result.stderr);
for (run_args) |arg| warn("{} ", arg) else warn("\n");
for (run_args) |arg|
warn("{} ", arg)
else
warn("\n");
return parseError(tokenizer, code.source_token, "example incorrectly compiled");
}
},
@@ -781,11 +802,9 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
}
break :blk result;
} else blk: {
break :blk exec(allocator, run_args) catch return parseError(
tokenizer, code.source_token, "example crashed");
break :blk exec(allocator, &env_map, run_args) catch return parseError(tokenizer, code.source_token, "example crashed");
};
const escaped_stderr = try escapeHtml(allocator, result.stderr);
const escaped_stdout = try escapeHtml(allocator, result.stdout);
@@ -798,7 +817,11 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
var test_args = std.ArrayList([]const u8).init(allocator);
defer test_args.deinit();
try test_args.appendSlice([][]const u8 {zig_exe, "test", tmp_source_file_name});
try test_args.appendSlice([][]const u8{
zig_exe,
"test",
tmp_source_file_name,
});
try out.print("<pre><code class=\"shell\">$ zig test {}.zig", code.name);
switch (code.mode) {
builtin.Mode.Debug => {},
@@ -810,16 +833,22 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
try test_args.append("--release-fast");
try out.print(" --release-fast");
},
builtin.Mode.ReleaseSmall => {
try test_args.append("--release-small");
try out.print(" --release-small");
},
}
if (code.target_windows) {
try test_args.appendSlice([][]const u8{
"--target-os", "windows",
"--target-arch", "x86_64",
"--target-environ", "msvc",
"--target-os",
"windows",
"--target-arch",
"x86_64",
"--target-environ",
"msvc",
});
}
const result = exec(allocator, test_args.toSliceConst()) catch return parseError(
tokenizer, code.source_token, "test failed");
const result = exec(allocator, &env_map, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed");
const escaped_stderr = try escapeHtml(allocator, result.stderr);
const escaped_stdout = try escapeHtml(allocator, result.stdout);
try out.print("\n{}{}</code></pre>\n", escaped_stderr, escaped_stdout);
@@ -828,7 +857,13 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
var test_args = std.ArrayList([]const u8).init(allocator);
defer test_args.deinit();
try test_args.appendSlice([][]const u8 {zig_exe, "test", "--color", "on", tmp_source_file_name});
try test_args.appendSlice([][]const u8{
zig_exe,
"test",
"--color",
"on",
tmp_source_file_name,
});
try out.print("<pre><code class=\"shell\">$ zig test {}.zig", code.name);
switch (code.mode) {
builtin.Mode.Debug => {},
@@ -840,19 +875,29 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
try test_args.append("--release-fast");
try out.print(" --release-fast");
},
builtin.Mode.ReleaseSmall => {
try test_args.append("--release-small");
try out.print(" --release-small");
},
}
const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);
const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, &env_map, max_doc_file_size);
switch (result.term) {
os.ChildProcess.Term.Exited => |exit_code| {
if (exit_code == 0) {
warn("{}\nThe following command incorrectly succeeded:\n", result.stderr);
for (test_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
for (test_args.toSliceConst()) |arg|
warn("{} ", arg)
else
warn("\n");
return parseError(tokenizer, code.source_token, "example incorrectly compiled");
}
},
else => {
warn("{}\nThe following command crashed:\n", result.stderr);
for (test_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
for (test_args.toSliceConst()) |arg|
warn("{} ", arg)
else
warn("\n");
return parseError(tokenizer, code.source_token, "example compile crashed");
},
}
@@ -869,25 +914,36 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
var test_args = std.ArrayList([]const u8).init(allocator);
defer test_args.deinit();
try test_args.appendSlice([][]const u8 {zig_exe, "test", tmp_source_file_name});
try test_args.appendSlice([][]const u8{
zig_exe,
"test",
tmp_source_file_name,
});
switch (code.mode) {
builtin.Mode.Debug => {},
builtin.Mode.ReleaseSafe => try test_args.append("--release-safe"),
builtin.Mode.ReleaseFast => try test_args.append("--release-fast"),
builtin.Mode.ReleaseSmall => try test_args.append("--release-small"),
}
const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);
const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, &env_map, max_doc_file_size);
switch (result.term) {
os.ChildProcess.Term.Exited => |exit_code| {
if (exit_code == 0) {
warn("{}\nThe following command incorrectly succeeded:\n", result.stderr);
for (test_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
for (test_args.toSliceConst()) |arg|
warn("{} ", arg)
else
warn("\n");
return parseError(tokenizer, code.source_token, "example test incorrectly succeeded");
}
},
else => {
warn("{}\nThe following command crashed:\n", result.stderr);
for (test_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
for (test_args.toSliceConst()) |arg|
warn("{} ", arg)
else
warn("\n");
return parseError(tokenizer, code.source_token, "example compile crashed");
},
}
@@ -905,9 +961,20 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
var build_args = std.ArrayList([]const u8).init(allocator);
defer build_args.deinit();
try build_args.appendSlice([][]const u8 {zig_exe, "build-obj", tmp_source_file_name,
"--color", "on",
"--output", tmp_obj_file_name});
const name_plus_h_ext = try std.fmt.allocPrint(allocator, "{}.h", code.name);
const output_h_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_h_ext);
try build_args.appendSlice([][]const u8{
zig_exe,
"build-obj",
tmp_source_file_name,
"--color",
"on",
"--output",
tmp_obj_file_name,
"--output-h",
output_h_file_name,
});
if (!code.is_inline) {
try out.print("<pre><code class=\"shell\">$ zig build-obj {}.zig", code.name);
@@ -927,21 +994,33 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
try out.print(" --release-fast");
}
},
builtin.Mode.ReleaseSmall => {
try build_args.append("--release-small");
if (!code.is_inline) {
try out.print(" --release-small");
}
},
}
if (maybe_error_match) |error_match| {
const result = try os.ChildProcess.exec(allocator, build_args.toSliceConst(), null, null, max_doc_file_size);
const result = try os.ChildProcess.exec(allocator, build_args.toSliceConst(), null, &env_map, max_doc_file_size);
switch (result.term) {
os.ChildProcess.Term.Exited => |exit_code| {
if (exit_code == 0) {
warn("{}\nThe following command incorrectly succeeded:\n", result.stderr);
for (build_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
for (build_args.toSliceConst()) |arg|
warn("{} ", arg)
else
warn("\n");
return parseError(tokenizer, code.source_token, "example build incorrectly succeeded");
}
},
else => {
warn("{}\nThe following command crashed:\n", result.stderr);
for (build_args.toSliceConst()) |arg| warn("{} ", arg) else warn("\n");
for (build_args.toSliceConst()) |arg|
warn("{} ", arg)
else
warn("\n");
return parseError(tokenizer, code.source_token, "example compile crashed");
},
}
@@ -956,8 +1035,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
try out.print("</code></pre>\n");
}
} else {
_ = exec(allocator, build_args.toSliceConst()) catch return parseError(
tokenizer, code.source_token, "example failed to compile");
_ = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile");
}
if (!code.is_inline) {
try out.print("</code></pre>\n");
@@ -968,24 +1046,37 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
},
}
}
}
fn exec(allocator: &mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult {
const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size);
fn exec(allocator: *mem.Allocator, env_map: *std.BufMap, args: []const []const u8) !os.ChildProcess.ExecResult {
const result = try os.ChildProcess.exec(allocator, args, null, env_map, max_doc_file_size);
switch (result.term) {
os.ChildProcess.Term.Exited => |exit_code| {
if (exit_code != 0) {
warn("{}\nThe following command exited with code {}:\n", result.stderr, exit_code);
for (args) |arg| warn("{} ", arg) else warn("\n");
for (args) |arg|
warn("{} ", arg)
else
warn("\n");
return error.ChildExitError;
}
},
else => {
warn("{}\nThe following command crashed:\n", result.stderr);
for (args) |arg| warn("{} ", arg) else warn("\n");
for (args) |arg|
warn("{} ", arg)
else
warn("\n");
return error.ChildCrashed;
},
}
return result;
}
fn getBuiltinCode(allocator: *mem.Allocator, env_map: *std.BufMap, zig_exe: []const u8) ![]const u8 {
const result = try exec(allocator, env_map, []const []const u8{
zig_exe,
"builtin",
});
return result.stdout;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,74 +0,0 @@
# How Semantic Analysis Works
We start with a set of files. Typically the user only has one entry point file,
which imports the other files they want to use. However, the compiler may
choose to add more files to the compilation, for example bootstrap.zig which
contains the code that calls main.
Our goal now is to treat everything that is marked with the `export` keyword
as a root node, and then parse and semantically analyze as little as possible
in order to fulfill these exports.
So, some parts of the code very well may have uncaught semantic errors, but as
long as the code is not referenced in any way, the compiler will not complain
because the code may as well not exist. This is similar to the fact that code
excluded from compilation with an `#ifdef` in C is not analyzed. Avoiding
analyzing unused code will save compilation time - one of Zig's goals.
So, for each file, we iterate over the top level declarations. The set of top
level declarations are:
* Function Definition
* Global Variable Declaration
* Container Declaration (struct or enum)
* Error Value Declaration
* Use Declaration
Each of these can have `export` attached to them except for error value
declarations and use declarations.
When we see a top level declaration during this iteration, we determine its
unique name identifier within the file. For example, for a function definition,
the unique name identifier is simply its name. Using this name we add the top
level declaration to a map.
If the top level declaration is exported, we add it to a set of exported top
level identifiers.
If the top level declaration is a use declaration, we add it to a set of use
declarations.
If the top level declaration is an error value declaration, we assign it a value
and increment the count of error values.
After this preliminary iteration over the top level declarations, we iterate
over the use declarations and resolve them. To resolve a use declaration, we
analyze the associated expression, verify that its type is the namespace type,
and then add all the items from the namespace into the top level declaration
map for the current file.
To analyze an expression, we recurse the abstract syntax tree of the
expression. Whenever we must look up a symbol, if the symbol exists already,
we can use it. Otherwise, we look it up in the top level declaration map.
If it exists, we can use it. Otherwise, we interrupt resolving this use
declaration to resolve the next one. If a dependency loop is detected, emit
an error. If all use declarations are resolved yet the symbol we need still
does not exist, emit an error.
To analyze an `@import` expression, find the referenced file, parse it, and
add it to the set of files to perform semantic analysis on.
Proceed through the rest of the use declarations the same way.
If we make it through the use declarations without an error, then we have a
complete map of all globals that exist in the current file.
Next we iterate over the set of exported top level declarations.
If it's a function definition, add it to the set of exported function
definitions and resolve the function prototype only. Otherwise, resolve the
top level declaration completely. This may involve recursively resolving other
top level declarations that expressions depend on.
Finally, iterate over the set of exported function definitions and analyze the
bodies.

View File

@@ -7,7 +7,7 @@ const allocator = std.debug.global_allocator;
pub fn main() !void {
var args_it = os.args();
const exe = try unwrapArg(??args_it.next(allocator));
const exe = try unwrapArg(args_it.next(allocator).?);
var catted_anything = false;
var stdout_file = try io.getStdOut();
@@ -41,7 +41,7 @@ fn usage(exe: []const u8) !void {
return error.Invalid;
}
fn cat_file(stdout: &os.File, file: &os.File) !void {
fn cat_file(stdout: *os.File, file: *os.File) !void {
var buf: [1024 * 4]u8 = undefined;
while (true) {

View File

@@ -23,7 +23,7 @@ pub fn main() !void {
while (true) {
try stdout.print("\nGuess a number between 1 and 100: ");
var line_buf : [20]u8 = undefined;
var line_buf: [20]u8 = undefined;
const line_len = io.readLine(line_buf[0..]) catch |err| switch (err) {
error.InputTooLong => {

View File

@@ -1,5 +1,5 @@
const c = @cImport({
// See https://github.com/zig-lang/zig/issues/515
// See https://github.com/ziglang/zig/issues/515
@cDefine("_NO_CRT_STDIO_INLINE", "1");
@cInclude("stdio.h");
@cInclude("string.h");
@@ -7,9 +7,8 @@ const c = @cImport({
const msg = c"Hello, world!\n";
export fn main(argc: c_int, argv: &&u8) c_int {
if (c.printf(msg) != c_int(c.strlen(msg)))
return -1;
export fn main(argc: c_int, argv: **u8) c_int {
if (c.printf(msg) != @intCast(c_int, c.strlen(msg))) return -1;
return 0;
}

View File

@@ -1,6 +1,6 @@
const base64 = @import("std").base64;
export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8, source_len: usize) usize {
export fn decode_base_64(dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize) usize {
const src = source_ptr[0..source_len];
const dest = dest_ptr[0..dest_len];
const base64_decoder = base64.standard_decoder_unsafe;

View File

@@ -1,12 +1,10 @@
const Builder = @import("std").build.Builder;
pub fn build(b: &Builder) void {
pub fn build(b: *Builder) void {
const obj = b.addObject("base64", "base64.zig");
const exe = b.addCExecutable("test");
exe.addCompileFlags([][]const u8 {
"-std=c99",
});
exe.addCompileFlags([][]const u8{"-std=c99"});
exe.addSourceFile("test.c");
exe.addObject(obj);

View File

@@ -1,12 +1,10 @@
const Builder = @import("std").build.Builder;
pub fn build(b: &Builder) void {
pub fn build(b: *Builder) void {
const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
const exe = b.addCExecutable("test");
exe.addCompileFlags([][]const u8 {
"-std=c99",
});
exe.addCompileFlags([][]const u8{"-std=c99"});
exe.addSourceFile("test.c");
exe.linkLibrary(lib);

View File

@@ -30,24 +30,22 @@ fn argInAllowedSet(maybe_set: ?[]const []const u8, arg: []const u8) bool {
}
// Modifies the current argument index during iteration
fn readFlagArguments(allocator: &Allocator, args: []const []const u8, required: usize,
allowed_set: ?[]const []const u8, index: &usize) !FlagArg {
fn readFlagArguments(allocator: *Allocator, args: []const []const u8, required: usize, allowed_set: ?[]const []const u8, index: *usize) !FlagArg {
switch (required) {
0 => return FlagArg { .None = undefined }, // TODO: Required to force non-tag but value?
0 => return FlagArg{ .None = undefined }, // TODO: Required to force non-tag but value?
1 => {
if (*index + 1 >= args.len) {
if (index.* + 1 >= args.len) {
return error.MissingFlagArguments;
}
*index += 1;
const arg = args[*index];
index.* += 1;
const arg = args[index.*];
if (!argInAllowedSet(allowed_set, arg)) {
return error.ArgumentNotInAllowedSet;
}
return FlagArg { .Single = arg };
return FlagArg{ .Single = arg };
},
else => |needed| {
var extra = ArrayList([]const u8).init(allocator);
@@ -55,12 +53,12 @@ fn readFlagArguments(allocator: &Allocator, args: []const []const u8, required:
var j: usize = 0;
while (j < needed) : (j += 1) {
if (*index + 1 >= args.len) {
if (index.* + 1 >= args.len) {
return error.MissingFlagArguments;
}
*index += 1;
const arg = args[*index];
index.* += 1;
const arg = args[index.*];
if (!argInAllowedSet(allowed_set, arg)) {
return error.ArgumentNotInAllowedSet;
@@ -69,7 +67,7 @@ fn readFlagArguments(allocator: &Allocator, args: []const []const u8, required:
try extra.append(arg);
}
return FlagArg { .Many = extra };
return FlagArg{ .Many = extra };
},
}
}
@@ -81,8 +79,8 @@ pub const Args = struct {
flags: HashMapFlags,
positionals: ArrayList([]const u8),
pub fn parse(allocator: &Allocator, comptime spec: []const Flag, args: []const []const u8) !Args {
var parsed = Args {
pub fn parse(allocator: *Allocator, comptime spec: []const Flag, args: []const []const u8) !Args {
var parsed = Args{
.flags = HashMapFlags.init(allocator),
.positionals = ArrayList([]const u8).init(allocator),
};
@@ -101,7 +99,7 @@ pub const Args = struct {
error.ArgumentNotInAllowedSet => {
std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg);
std.debug.warn("allowed options are ");
for (??flag.allowed_set) |possible| {
for (flag.allowed_set.?) |possible| {
std.debug.warn("'{}' ", possible);
}
std.debug.warn("\n");
@@ -116,11 +114,7 @@ pub const Args = struct {
};
if (flag.mergable) {
var prev =
if (parsed.flags.get(flag_name_trimmed)) |entry|
entry.value.Many
else
ArrayList([]const u8).init(allocator);
var prev = if (parsed.flags.get(flag_name_trimmed)) |entry| entry.value.Many else ArrayList([]const u8).init(allocator);
// MergeN creation disallows 0 length flag entry (doesn't make sense)
switch (flag_args) {
@@ -129,7 +123,7 @@ pub const Args = struct {
FlagArg.Many => |inner| try prev.appendSlice(inner.toSliceConst()),
}
_ = try parsed.flags.put(flag_name_trimmed, FlagArg { .Many = prev });
_ = try parsed.flags.put(flag_name_trimmed, FlagArg{ .Many = prev });
} else {
_ = try parsed.flags.put(flag_name_trimmed, flag_args);
}
@@ -149,21 +143,23 @@ pub const Args = struct {
return parsed;
}
pub fn deinit(self: &Args) void {
pub fn deinit(self: *Args) void {
self.flags.deinit();
self.positionals.deinit();
}
// e.g. --help
pub fn present(self: &Args, name: []const u8) bool {
pub fn present(self: *Args, name: []const u8) bool {
return self.flags.contains(name);
}
// e.g. --name value
pub fn single(self: &Args, name: []const u8) ?[]const u8 {
pub fn single(self: *Args, name: []const u8) ?[]const u8 {
if (self.flags.get(name)) |entry| {
switch (entry.value) {
FlagArg.Single => |inner| { return inner; },
FlagArg.Single => |inner| {
return inner;
},
else => @panic("attempted to retrieve flag with wrong type"),
}
} else {
@@ -172,14 +168,16 @@ pub const Args = struct {
}
// e.g. --names value1 value2 value3
pub fn many(self: &Args, name: []const u8) ?[]const []const u8 {
pub fn many(self: *Args, name: []const u8) []const []const u8 {
if (self.flags.get(name)) |entry| {
switch (entry.value) {
FlagArg.Many => |inner| { return inner.toSliceConst(); },
FlagArg.Many => |inner| {
return inner.toSliceConst();
},
else => @panic("attempted to retrieve flag with wrong type"),
}
} else {
return null;
return []const []const u8{};
}
}
};
@@ -207,7 +205,7 @@ pub const Flag = struct {
}
pub fn ArgN(comptime name: []const u8, comptime n: usize) Flag {
return Flag {
return Flag{
.name = name,
.required = n,
.mergable = false,
@@ -220,7 +218,7 @@ pub const Flag = struct {
@compileError("n must be greater than 0");
}
return Flag {
return Flag{
.name = name,
.required = n,
.mergable = true,
@@ -229,7 +227,7 @@ pub const Flag = struct {
}
pub fn Option(comptime name: []const u8, comptime set: []const []const u8) Flag {
return Flag {
return Flag{
.name = name,
.required = 1,
.mergable = false,
@@ -239,26 +237,36 @@ pub const Flag = struct {
};
test "parse arguments" {
const spec1 = comptime []const Flag {
const spec1 = comptime []const Flag{
Flag.Bool("--help"),
Flag.Bool("--init"),
Flag.Arg1("--build-file"),
Flag.Option("--color", []const []const u8 { "on", "off", "auto" }),
Flag.Option("--color", []const []const u8{
"on",
"off",
"auto",
}),
Flag.ArgN("--pkg-begin", 2),
Flag.ArgMergeN("--object", 1),
Flag.ArgN("--library", 1),
};
const cliargs = []const []const u8 {
const cliargs = []const []const u8{
"build",
"--help",
"pos1",
"--build-file", "build.zig",
"--object", "obj1",
"--object", "obj2",
"--library", "lib1",
"--library", "lib2",
"--color", "on",
"--build-file",
"build.zig",
"--object",
"obj1",
"--object",
"obj2",
"--library",
"lib1",
"--library",
"lib2",
"--color",
"on",
"pos2",
};
@@ -268,14 +276,14 @@ test "parse arguments" {
debug.assert(!args.present("help2"));
debug.assert(!args.present("init"));
debug.assert(mem.eql(u8, ??args.single("build-file"), "build.zig"));
debug.assert(mem.eql(u8, ??args.single("color"), "on"));
debug.assert(mem.eql(u8, args.single("build-file").?, "build.zig"));
debug.assert(mem.eql(u8, args.single("color").?, "on"));
const objects = ??args.many("object");
const objects = args.many("object").?;
debug.assert(mem.eql(u8, objects[0], "obj1"));
debug.assert(mem.eql(u8, objects[1], "obj2"));
debug.assert(mem.eql(u8, ??args.single("library"), "lib2"));
debug.assert(mem.eql(u8, args.single("library").?, "lib2"));
const pos = args.positionals.toSliceConst();
debug.assert(mem.eql(u8, pos[0], "build"));

View File

@@ -1,5 +1,8 @@
pub use @cImport({
@cDefine("__STDC_CONSTANT_MACROS", "");
@cDefine("__STDC_LIMIT_MACROS", "");
@cInclude("inttypes.h");
@cInclude("config.h");
@cInclude("zig_llvm.h");
@cInclude("windows_sdk.h");
});

68
src-self-hosted/c_int.zig Normal file
View File

@@ -0,0 +1,68 @@
pub const CInt = struct {
id: Id,
zig_name: []const u8,
c_name: []const u8,
is_signed: bool,
pub const Id = enum {
Short,
UShort,
Int,
UInt,
Long,
ULong,
LongLong,
ULongLong,
};
pub const list = []CInt{
CInt{
.id = Id.Short,
.zig_name = "c_short",
.c_name = "short",
.is_signed = true,
},
CInt{
.id = Id.UShort,
.zig_name = "c_ushort",
.c_name = "unsigned short",
.is_signed = false,
},
CInt{
.id = Id.Int,
.zig_name = "c_int",
.c_name = "int",
.is_signed = true,
},
CInt{
.id = Id.UInt,
.zig_name = "c_uint",
.c_name = "unsigned int",
.is_signed = false,
},
CInt{
.id = Id.Long,
.zig_name = "c_long",
.c_name = "long",
.is_signed = true,
},
CInt{
.id = Id.ULong,
.zig_name = "c_ulong",
.c_name = "unsigned long",
.is_signed = false,
},
CInt{
.id = Id.LongLong,
.zig_name = "c_longlong",
.c_name = "long long",
.is_signed = true,
},
CInt{
.id = Id.ULongLong,
.zig_name = "c_ulonglong",
.c_name = "unsigned long long",
.is_signed = false,
},
};
};

450
src-self-hosted/codegen.zig Normal file
View File

@@ -0,0 +1,450 @@
const std = @import("std");
const builtin = @import("builtin");
const Compilation = @import("compilation.zig").Compilation;
const llvm = @import("llvm.zig");
const c = @import("c.zig");
const ir = @import("ir.zig");
const Value = @import("value.zig").Value;
const Type = @import("type.zig").Type;
const Scope = @import("scope.zig").Scope;
const event = std.event;
const assert = std.debug.assert;
const DW = std.dwarf;
pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void {
fn_val.base.ref();
defer fn_val.base.deref(comp);
defer code.destroy(comp.gpa());
var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
errdefer output_path.deinit();
const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();
defer llvm_handle.release(comp.event_loop_local);
const context = llvm_handle.node.data;
const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory;
defer llvm.DisposeModule(module);
llvm.SetTarget(module, comp.llvm_triple.ptr());
llvm.SetDataLayout(module, comp.target_layout_str);
if (comp.target.getObjectFormat() == builtin.ObjectFormat.coff) {
llvm.AddModuleCodeViewFlag(module);
} else {
llvm.AddModuleDebugInfoFlag(module);
}
const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory;
defer llvm.DisposeBuilder(builder);
const dibuilder = llvm.CreateDIBuilder(module, true) orelse return error.OutOfMemory;
defer llvm.DisposeDIBuilder(dibuilder);
// Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes
// the git revision.
const producer = try std.Buffer.allocPrint(
&code.arena.allocator,
"zig {}.{}.{}",
u32(c.ZIG_VERSION_MAJOR),
u32(c.ZIG_VERSION_MINOR),
u32(c.ZIG_VERSION_PATCH),
);
const flags = c"";
const runtime_version = 0;
const compile_unit_file = llvm.CreateFile(
dibuilder,
comp.name.ptr(),
comp.root_package.root_src_dir.ptr(),
) orelse return error.OutOfMemory;
const is_optimized = comp.build_mode != builtin.Mode.Debug;
const compile_unit = llvm.CreateCompileUnit(
dibuilder,
DW.LANG_C99,
compile_unit_file,
producer.ptr(),
is_optimized,
flags,
runtime_version,
c"",
0,
!comp.strip,
) orelse return error.OutOfMemory;
var ofile = ObjectFile{
.comp = comp,
.module = module,
.builder = builder,
.dibuilder = dibuilder,
.context = context,
.lock = event.Lock.init(comp.loop),
.arena = &code.arena.allocator,
};
try renderToLlvmModule(&ofile, fn_val, code);
// TODO module level assembly
//if (buf_len(&g->global_asm) != 0) {
// LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm));
//}
llvm.DIBuilderFinalize(dibuilder);
if (comp.verbose_llvm_ir) {
std.debug.warn("raw module:\n");
llvm.DumpModule(ofile.module);
}
// verify the llvm module when safety is on
if (std.debug.runtime_safety) {
var error_ptr: ?[*]u8 = null;
_ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr);
}
assert(comp.emit_file_type == Compilation.Emit.Binary); // TODO support other types
const is_small = comp.build_mode == builtin.Mode.ReleaseSmall;
const is_debug = comp.build_mode == builtin.Mode.Debug;
var err_msg: [*]u8 = undefined;
// TODO integrate this with evented I/O
if (llvm.TargetMachineEmitToFile(
comp.target_machine,
module,
output_path.ptr(),
llvm.EmitBinary,
&err_msg,
is_debug,
is_small,
)) {
if (std.debug.runtime_safety) {
std.debug.panic("unable to write object file {}: {s}\n", output_path.toSliceConst(), err_msg);
}
return error.WritingObjectFileFailed;
}
//validate_inline_fns(g); TODO
fn_val.containing_object = output_path;
if (comp.verbose_llvm_ir) {
std.debug.warn("optimized module:\n");
llvm.DumpModule(ofile.module);
}
if (comp.verbose_link) {
std.debug.warn("created {}\n", output_path.toSliceConst());
}
}
pub const ObjectFile = struct {
comp: *Compilation,
module: llvm.ModuleRef,
builder: llvm.BuilderRef,
dibuilder: *llvm.DIBuilder,
context: llvm.ContextRef,
lock: event.Lock,
arena: *std.mem.Allocator,
fn gpa(self: *ObjectFile) *std.mem.Allocator {
return self.comp.gpa();
}
};
pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void {
// TODO audit more of codegen.cpp:fn_llvm_value and port more logic
const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
fn_val.symbol_name.ptr(),
llvm_fn_type,
) orelse return error.OutOfMemory;
const want_fn_safety = fn_val.block_scope.?.safety.get(ofile.comp);
if (want_fn_safety and ofile.comp.haveLibC()) {
try addLLVMFnAttr(ofile, llvm_fn, "sspstrong");
try addLLVMFnAttrStr(ofile, llvm_fn, "stack-protector-buffer-size", "4");
}
// TODO
//if (fn_val.align_stack) |align_stack| {
// try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack);
//}
const fn_type = fn_val.base.typ.cast(Type.Fn).?;
const fn_type_normal = &fn_type.key.data.Normal;
try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
//add_uwtable_attr(g, fn_table_entry->llvm_value);
try addLLVMFnAttr(ofile, llvm_fn, "nobuiltin");
//if (g->build_mode == BuildModeDebug && fn_table_entry->fn_inline != FnInlineAlways) {
// ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim", "true");
// ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim-non-leaf", nullptr);
//}
//if (fn_table_entry->section_name) {
// LLVMSetSection(fn_table_entry->llvm_value, buf_ptr(fn_table_entry->section_name));
//}
//if (fn_table_entry->align_bytes > 0) {
// LLVMSetAlignment(fn_table_entry->llvm_value, (unsigned)fn_table_entry->align_bytes);
//} else {
// // We'd like to set the best alignment for the function here, but on Darwin LLVM gives
// // "Cannot getTypeInfo() on a type that is unsized!" assertion failure when calling
// // any of the functions for getting alignment. Not specifying the alignment should
// // use the ABI alignment, which is fine.
//}
//if (!type_has_bits(return_type)) {
// // nothing to do
//} else if (type_is_codegen_pointer(return_type)) {
// addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");
//} else if (handle_is_ptr(return_type) &&
// calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc))
//{
// addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
// addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull");
//}
// TODO set parameter attributes
// TODO
//uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
//if (err_ret_trace_arg_index != UINT32_MAX) {
// addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
//}
const cur_ret_ptr = if (fn_type_normal.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null;
// build all basic blocks
for (code.basic_block_list.toSlice()) |bb| {
bb.llvm_block = llvm.AppendBasicBlockInContext(
ofile.context,
llvm_fn,
bb.name_hint,
) orelse return error.OutOfMemory;
}
const entry_bb = code.basic_block_list.at(0);
llvm.PositionBuilderAtEnd(ofile.builder, entry_bb.llvm_block);
llvm.ClearCurrentDebugLocation(ofile.builder);
// TODO set up error return tracing
// TODO allocate temporary stack values
const var_list = fn_type.non_key.Normal.variable_list.toSliceConst();
// create debug variable declarations for variables and allocate all local variables
for (var_list) |var_scope, i| {
const var_type = switch (var_scope.data) {
Scope.Var.Data.Const => unreachable,
Scope.Var.Data.Param => |param| param.typ,
};
// if (!type_has_bits(var->value->type)) {
// continue;
// }
// if (ir_get_var_is_comptime(var))
// continue;
// if (type_requires_comptime(var->value->type))
// continue;
// if (var->src_arg_index == SIZE_MAX) {
// var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes);
// var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
// buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1),
// var->value->type->di_type, !g->strip_debug_symbols, 0);
// } else {
// it's a parameter
// assert(var->gen_arg_index != SIZE_MAX);
// TypeTableEntry *gen_type;
// FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index];
if (var_type.handleIsPtr()) {
// if (gen_info->is_byval) {
// gen_type = var->value->type;
// } else {
// gen_type = gen_info->type;
// }
var_scope.data.Param.llvm_value = llvm.GetParam(llvm_fn, @intCast(c_uint, i));
} else {
// gen_type = var->value->type;
var_scope.data.Param.llvm_value = try renderAlloca(ofile, var_type, var_scope.name, Type.Pointer.Align.Abi);
}
// if (var->decl_node) {
// var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
// buf_ptr(&var->name), import->di_file,
// (unsigned)(var->decl_node->line + 1),
// gen_type->di_type, !g->strip_debug_symbols, 0, (unsigned)(var->gen_arg_index + 1));
// }
// }
}
// TODO finishing error return trace setup. we have to do this after all the allocas.
// create debug variable declarations for parameters
// rely on the first variables in the variable_list being parameters.
//size_t next_var_i = 0;
for (fn_type.key.data.Normal.params) |param, i| {
//FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i];
//if (info->gen_index == SIZE_MAX)
// continue;
const scope_var = var_list[i];
//assert(variable->src_arg_index != SIZE_MAX);
//next_var_i += 1;
//assert(variable);
//assert(variable->value_ref);
if (!param.typ.handleIsPtr()) {
//clear_debug_source_node(g);
const llvm_param = llvm.GetParam(llvm_fn, @intCast(c_uint, i));
_ = renderStoreUntyped(
ofile,
llvm_param,
scope_var.data.Param.llvm_value,
Type.Pointer.Align.Abi,
Type.Pointer.Vol.Non,
);
}
//if (variable->decl_node) {
// gen_var_debug_decl(g, variable);
//}
}
for (code.basic_block_list.toSlice()) |current_block| {
llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block);
for (current_block.instruction_list.toSlice()) |instruction| {
if (instruction.ref_count == 0 and !instruction.hasSideEffects()) continue;
instruction.llvm_value = try instruction.render(ofile, fn_val);
}
current_block.llvm_exit_block = llvm.GetInsertBlock(ofile.builder);
}
}
fn addLLVMAttr(
ofile: *ObjectFile,
val: llvm.ValueRef,
attr_index: llvm.AttributeIndex,
attr_name: []const u8,
) !void {
const kind_id = llvm.GetEnumAttributeKindForName(attr_name.ptr, attr_name.len);
assert(kind_id != 0);
const llvm_attr = llvm.CreateEnumAttribute(ofile.context, kind_id, 0) orelse return error.OutOfMemory;
llvm.AddAttributeAtIndex(val, attr_index, llvm_attr);
}
fn addLLVMAttrStr(
ofile: *ObjectFile,
val: llvm.ValueRef,
attr_index: llvm.AttributeIndex,
attr_name: []const u8,
attr_val: []const u8,
) !void {
const llvm_attr = llvm.CreateStringAttribute(
ofile.context,
attr_name.ptr,
@intCast(c_uint, attr_name.len),
attr_val.ptr,
@intCast(c_uint, attr_val.len),
) orelse return error.OutOfMemory;
llvm.AddAttributeAtIndex(val, attr_index, llvm_attr);
}
fn addLLVMAttrInt(
val: llvm.ValueRef,
attr_index: llvm.AttributeIndex,
attr_name: []const u8,
attr_val: u64,
) !void {
const kind_id = llvm.GetEnumAttributeKindForName(attr_name.ptr, attr_name.len);
assert(kind_id != 0);
const llvm_attr = llvm.CreateEnumAttribute(ofile.context, kind_id, attr_val) orelse return error.OutOfMemory;
llvm.AddAttributeAtIndex(val, attr_index, llvm_attr);
}
fn addLLVMFnAttr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8) !void {
return addLLVMAttr(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name);
}
fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: []const u8) !void {
return addLLVMAttrStr(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val);
}
fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void {
return addLLVMAttrInt(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val);
}
fn renderLoadUntyped(
ofile: *ObjectFile,
ptr: llvm.ValueRef,
alignment: Type.Pointer.Align,
vol: Type.Pointer.Vol,
name: [*]const u8,
) !llvm.ValueRef {
const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory;
switch (vol) {
Type.Pointer.Vol.Non => {},
Type.Pointer.Vol.Volatile => llvm.SetVolatile(result, 1),
}
llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.GetElementType(llvm.TypeOf(ptr))));
return result;
}
fn renderLoad(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer, name: [*]const u8) !llvm.ValueRef {
return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name);
}
pub fn getHandleValue(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer) !?llvm.ValueRef {
const child_type = ptr_type.key.child_type;
if (!child_type.hasBits()) {
return null;
}
if (child_type.handleIsPtr()) {
return ptr;
}
return try renderLoad(ofile, ptr, ptr_type, c"");
}
pub fn renderStoreUntyped(
ofile: *ObjectFile,
value: llvm.ValueRef,
ptr: llvm.ValueRef,
alignment: Type.Pointer.Align,
vol: Type.Pointer.Vol,
) !llvm.ValueRef {
const result = llvm.BuildStore(ofile.builder, value, ptr) orelse return error.OutOfMemory;
switch (vol) {
Type.Pointer.Vol.Non => {},
Type.Pointer.Vol.Volatile => llvm.SetVolatile(result, 1),
}
llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.TypeOf(value)));
return result;
}
pub fn renderStore(
ofile: *ObjectFile,
value: llvm.ValueRef,
ptr: llvm.ValueRef,
ptr_type: *Type.Pointer,
) !llvm.ValueRef {
return renderStoreUntyped(ofile, value, ptr, ptr_type.key.alignment, ptr_type.key.vol);
}
pub fn renderAlloca(
ofile: *ObjectFile,
var_type: *Type,
name: []const u8,
alignment: Type.Pointer.Align,
) !llvm.ValueRef {
const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context);
const name_with_null = try std.cstr.addNullByte(ofile.arena, name);
const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, name_with_null.ptr) orelse return error.OutOfMemory;
llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm_var_type));
return result;
}
pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: llvm.TypeRef) u32 {
return switch (alignment) {
Type.Pointer.Align.Abi => return llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, llvm_type),
Type.Pointer.Align.Override => |a| a,
};
}

File diff suppressed because it is too large Load Diff

98
src-self-hosted/decl.zig Normal file
View File

@@ -0,0 +1,98 @@
const std = @import("std");
const Allocator = mem.Allocator;
const mem = std.mem;
const ast = std.zig.ast;
const Visib = @import("visib.zig").Visib;
const event = std.event;
const Value = @import("value.zig").Value;
const Token = std.zig.Token;
const errmsg = @import("errmsg.zig");
const Scope = @import("scope.zig").Scope;
const Compilation = @import("compilation.zig").Compilation;
pub const Decl = struct {
id: Id,
name: []const u8,
visib: Visib,
resolution: event.Future(Compilation.BuildError!void),
parent_scope: *Scope,
pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
switch (base.id) {
Id.Fn => {
const fn_decl = @fieldParentPtr(Fn, "base", base);
return fn_decl.isExported(tree);
},
else => return false,
}
}
pub fn getSpan(base: *const Decl) errmsg.Span {
switch (base.id) {
Id.Fn => {
const fn_decl = @fieldParentPtr(Fn, "base", base);
const fn_proto = fn_decl.fn_proto;
const start = fn_proto.fn_token;
const end = fn_proto.name_token orelse start;
return errmsg.Span{
.first = start,
.last = end + 1,
};
},
else => @panic("TODO"),
}
}
pub fn findRootScope(base: *const Decl) *Scope.Root {
return base.parent_scope.findRoot();
}
pub const Id = enum {
Var,
Fn,
CompTime,
};
pub const Var = struct {
base: Decl,
};
pub const Fn = struct {
base: Decl,
value: Val,
fn_proto: *ast.Node.FnProto,
// TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
pub const Val = union(enum) {
Unresolved: void,
Fn: *Value.Fn,
FnProto: *Value.FnProto,
};
pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {
const token = tree.tokens.at(tok_index);
break :x switch (token.id) {
Token.Id.Extern => tree.tokenSlicePtr(token),
else => null,
};
} else null;
}
pub fn isExported(self: Fn, tree: *ast.Tree) bool {
if (self.fn_proto.extern_export_inline_token) |tok_index| {
const token = tree.tokens.at(tok_index);
return token.id == Token.Id.Keyword_export;
} else {
return false;
}
}
};
pub const CompTime = struct {
base: Decl,
};
};

237
src-self-hosted/errmsg.zig Normal file
View File

@@ -0,0 +1,237 @@
const std = @import("std");
const mem = std.mem;
const os = std.os;
const Token = std.zig.Token;
const ast = std.zig.ast;
const TokenIndex = std.zig.ast.TokenIndex;
const Compilation = @import("compilation.zig").Compilation;
const Scope = @import("scope.zig").Scope;
pub const Color = enum {
Auto,
Off,
On,
};
pub const Span = struct {
first: ast.TokenIndex,
last: ast.TokenIndex,
pub fn token(i: TokenIndex) Span {
return Span{
.first = i,
.last = i,
};
}
pub fn node(n: *ast.Node) Span {
return Span{
.first = n.firstToken(),
.last = n.lastToken(),
};
}
};
pub const Msg = struct {
span: Span,
text: []u8,
data: Data,
const Data = union(enum) {
PathAndTree: PathAndTree,
ScopeAndComp: ScopeAndComp,
};
const PathAndTree = struct {
realpath: []const u8,
tree: *ast.Tree,
allocator: *mem.Allocator,
};
const ScopeAndComp = struct {
root_scope: *Scope.Root,
compilation: *Compilation,
};
pub fn destroy(self: *Msg) void {
switch (self.data) {
Data.PathAndTree => |path_and_tree| {
path_and_tree.allocator.free(self.text);
path_and_tree.allocator.destroy(self);
},
Data.ScopeAndComp => |scope_and_comp| {
scope_and_comp.root_scope.base.deref(scope_and_comp.compilation);
scope_and_comp.compilation.gpa().free(self.text);
scope_and_comp.compilation.gpa().destroy(self);
},
}
}
fn getAllocator(self: *const Msg) *mem.Allocator {
switch (self.data) {
Data.PathAndTree => |path_and_tree| {
return path_and_tree.allocator;
},
Data.ScopeAndComp => |scope_and_comp| {
return scope_and_comp.compilation.gpa();
},
}
}
pub fn getRealPath(self: *const Msg) []const u8 {
switch (self.data) {
Data.PathAndTree => |path_and_tree| {
return path_and_tree.realpath;
},
Data.ScopeAndComp => |scope_and_comp| {
return scope_and_comp.root_scope.realpath;
},
}
}
pub fn getTree(self: *const Msg) *ast.Tree {
switch (self.data) {
Data.PathAndTree => |path_and_tree| {
return path_and_tree.tree;
},
Data.ScopeAndComp => |scope_and_comp| {
return scope_and_comp.root_scope.tree;
},
}
}
/// Takes ownership of text
/// References root_scope, and derefs when the msg is freed
pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {
const msg = try comp.gpa().create(Msg{
.text = text,
.span = span,
.data = Data{
.ScopeAndComp = ScopeAndComp{
.root_scope = root_scope,
.compilation = comp,
},
},
});
root_scope.base.ref();
return msg;
}
pub fn createFromParseErrorAndScope(
comp: *Compilation,
root_scope: *Scope.Root,
parse_error: *const ast.Error,
) !*Msg {
const loc_token = parse_error.loc();
var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
defer text_buf.deinit();
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
try parse_error.render(&root_scope.tree.tokens, out_stream);
const msg = try comp.gpa().create(Msg{
.text = undefined,
.span = Span{
.first = loc_token,
.last = loc_token,
},
.data = Data{
.ScopeAndComp = ScopeAndComp{
.root_scope = root_scope,
.compilation = comp,
},
},
});
root_scope.base.ref();
msg.text = text_buf.toOwnedSlice();
return msg;
}
/// `realpath` must outlive the returned Msg
/// `tree` must outlive the returned Msg
/// Caller owns returned Msg and must free with `allocator`
/// allocator will additionally be used for printing messages later.
pub fn createFromParseError(
allocator: *mem.Allocator,
parse_error: *const ast.Error,
tree: *ast.Tree,
realpath: []const u8,
) !*Msg {
const loc_token = parse_error.loc();
var text_buf = try std.Buffer.initSize(allocator, 0);
defer text_buf.deinit();
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
try parse_error.render(&tree.tokens, out_stream);
const msg = try allocator.create(Msg{
.text = undefined,
.data = Data{
.PathAndTree = PathAndTree{
.allocator = allocator,
.realpath = realpath,
.tree = tree,
},
},
.span = Span{
.first = loc_token,
.last = loc_token,
},
});
msg.text = text_buf.toOwnedSlice();
errdefer allocator.destroy(msg);
return msg;
}
pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
const allocator = msg.getAllocator();
const realpath = msg.getRealPath();
const tree = msg.getTree();
const cwd = try os.getCwd(allocator);
defer allocator.free(cwd);
const relpath = try os.path.relative(allocator, cwd, realpath);
defer allocator.free(relpath);
const path = if (relpath.len < realpath.len) relpath else realpath;
const first_token = tree.tokens.at(msg.span.first);
const last_token = tree.tokens.at(msg.span.last);
const start_loc = tree.tokenLocationPtr(0, first_token);
const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
if (!color_on) {
try stream.print(
"{}:{}:{}: error: {}\n",
path,
start_loc.line + 1,
start_loc.column + 1,
msg.text,
);
return;
}
try stream.print(
"{}:{}:{}: error: {}\n{}\n",
path,
start_loc.line + 1,
start_loc.column + 1,
msg.text,
tree.source[start_loc.line_start..start_loc.line_end],
);
try stream.writeByteNTimes(' ', start_loc.column);
try stream.writeByteNTimes('~', last_token.end - first_token.start);
try stream.write("\n");
}
pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void {
const color_on = switch (color) {
Color.Auto => file.isTty(),
Color.On => true,
Color.Off => false,
};
var stream = &std.io.FileOutStream.init(file).stream;
return msg.printToStream(stream, color_on);
}
};

View File

@@ -7,7 +7,7 @@ const os = std.os;
const warn = std.debug.warn;
/// Caller must free result
pub fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {
pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 {
const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
errdefer allocator.free(test_zig_dir);
@@ -21,13 +21,13 @@ pub fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![
}
/// Caller must free result
pub fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {
const self_exe_path = try os.selfExeDirPath(allocator);
defer allocator.free(self_exe_path);
var cur_path: []const u8 = self_exe_path;
while (true) {
const test_dir = os.path.dirname(cur_path);
const test_dir = os.path.dirname(cur_path) orelse ".";
if (mem.eql(u8, test_dir, cur_path)) {
break;
@@ -42,16 +42,19 @@ pub fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
return error.FileNotFound;
}
pub fn resolveZigLibDir(allocator: &mem.Allocator) ![]u8 {
pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 {
return findZigLibDir(allocator) catch |err| {
warn(
\\Unable to find zig lib directory: {}.
\\Reinstall Zig or use --zig-install-prefix.
\\
,
@errorName(err)
);
, @errorName(err));
return error.ZigLibDirNotFound;
};
}
/// Caller must free result
pub fn resolveZigCacheDir(allocator: *mem.Allocator) ![]u8 {
return std.mem.dupe(allocator, u8, "zig-cache");
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,462 @@
const std = @import("std");
const builtin = @import("builtin");
const event = std.event;
const Target = @import("target.zig").Target;
const c = @import("c.zig");
/// See the render function implementation for documentation of the fields.
pub const LibCInstallation = struct {
include_dir: []const u8,
lib_dir: ?[]const u8,
static_lib_dir: ?[]const u8,
msvc_lib_dir: ?[]const u8,
kernel32_lib_dir: ?[]const u8,
dynamic_linker_path: ?[]const u8,
pub const FindError = error{
OutOfMemory,
FileSystem,
UnableToSpawnCCompiler,
CCompilerExitCode,
CCompilerCrashed,
CCompilerCannotFindHeaders,
LibCRuntimeNotFound,
LibCStdLibHeaderNotFound,
LibCKernel32LibNotFound,
UnsupportedArchitecture,
};
pub fn parse(
self: *LibCInstallation,
allocator: *std.mem.Allocator,
libc_file: []const u8,
stderr: *std.io.OutStream(std.io.FileOutStream.Error),
) !void {
self.initEmpty();
const keys = []const []const u8{
"include_dir",
"lib_dir",
"static_lib_dir",
"msvc_lib_dir",
"kernel32_lib_dir",
"dynamic_linker_path",
};
const FoundKey = struct {
found: bool,
allocated: ?[]u8,
};
var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** keys.len;
errdefer {
self.initEmpty();
for (found_keys) |found_key| {
if (found_key.allocated) |s| allocator.free(s);
}
}
const contents = try std.io.readFileAlloc(allocator, libc_file);
defer allocator.free(contents);
var it = std.mem.split(contents, "\n");
while (it.next()) |line| {
if (line.len == 0 or line[0] == '#') continue;
var line_it = std.mem.split(line, "=");
const name = line_it.next() orelse {
try stderr.print("missing equal sign after field name\n");
return error.ParseError;
};
const value = line_it.rest();
inline for (keys) |key, i| {
if (std.mem.eql(u8, name, key)) {
found_keys[i].found = true;
switch (@typeInfo(@typeOf(@field(self, key)))) {
builtin.TypeId.Optional => {
if (value.len == 0) {
@field(self, key) = null;
} else {
found_keys[i].allocated = try std.mem.dupe(allocator, u8, value);
@field(self, key) = found_keys[i].allocated;
}
},
else => {
if (value.len == 0) {
try stderr.print("field cannot be empty: {}\n", key);
return error.ParseError;
}
const dupe = try std.mem.dupe(allocator, u8, value);
found_keys[i].allocated = dupe;
@field(self, key) = dupe;
},
}
break;
}
}
}
for (found_keys) |found_key, i| {
if (!found_key.found) {
try stderr.print("missing field: {}\n", keys[i]);
return error.ParseError;
}
}
}
pub fn render(self: *const LibCInstallation, out: *std.io.OutStream(std.io.FileOutStream.Error)) !void {
@setEvalBranchQuota(4000);
try out.print(
\\# The directory that contains `stdlib.h`.
\\# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null`
\\include_dir={}
\\
\\# The directory that contains `crt1.o`.
\\# On Linux, can be found with `cc -print-file-name=crt1.o`.
\\# Not needed when targeting MacOS.
\\lib_dir={}
\\
\\# The directory that contains `crtbegin.o`.
\\# On Linux, can be found with `cc -print-file-name=crtbegin.o`.
\\# Not needed when targeting MacOS or Windows.
\\static_lib_dir={}
\\
\\# The directory that contains `vcruntime.lib`.
\\# Only needed when targeting Windows.
\\msvc_lib_dir={}
\\
\\# The directory that contains `kernel32.lib`.
\\# Only needed when targeting Windows.
\\kernel32_lib_dir={}
\\
\\# The full path to the dynamic linker, on the target system.
\\# Only needed when targeting Linux.
\\dynamic_linker_path={}
\\
,
self.include_dir,
self.lib_dir orelse "",
self.static_lib_dir orelse "",
self.msvc_lib_dir orelse "",
self.kernel32_lib_dir orelse "",
self.dynamic_linker_path orelse Target(Target.Native).getDynamicLinkerPath(),
);
}
/// Finds the default, native libc.
pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
self.initEmpty();
var group = event.Group(FindError!void).init(loop);
errdefer group.cancelAll();
var windows_sdk: ?*c.ZigWindowsSDK = null;
errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));
switch (builtin.os) {
builtin.Os.windows => {
var sdk: *c.ZigWindowsSDK = undefined;
switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) {
c.ZigFindWindowsSdkError.None => {
windows_sdk = sdk;
if (sdk.msvc_lib_dir_ptr) |ptr| {
self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]);
}
try group.call(findNativeKernel32LibDir, self, loop, sdk);
try group.call(findNativeIncludeDirWindows, self, loop, sdk);
try group.call(findNativeLibDirWindows, self, loop, sdk);
},
c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory,
c.ZigFindWindowsSdkError.NotFound => return error.NotFound,
c.ZigFindWindowsSdkError.PathTooLong => return error.NotFound,
}
},
builtin.Os.linux => {
try group.call(findNativeIncludeDirLinux, self, loop);
try group.call(findNativeLibDirLinux, self, loop);
try group.call(findNativeStaticLibDir, self, loop);
try group.call(findNativeDynamicLinker, self, loop);
},
builtin.Os.macosx => {
self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include");
},
else => @compileError("unimplemented: find libc for this OS"),
}
return await (async group.wait() catch unreachable);
}
async fn findNativeIncludeDirLinux(self: *LibCInstallation, loop: *event.Loop) !void {
const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
const argv = []const []const u8{
cc_exe,
"-E",
"-Wp,-v",
"-xc",
"/dev/null",
};
// TODO make this use event loop
const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
const exec_result = if (std.debug.runtime_safety) blk: {
break :blk errorable_result catch unreachable;
} else blk: {
break :blk errorable_result catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => return error.UnableToSpawnCCompiler,
};
};
defer {
loop.allocator.free(exec_result.stdout);
loop.allocator.free(exec_result.stderr);
}
switch (exec_result.term) {
std.os.ChildProcess.Term.Exited => |code| {
if (code != 0) return error.CCompilerExitCode;
},
else => {
return error.CCompilerCrashed;
},
}
var it = std.mem.split(exec_result.stderr, "\n\r");
var search_paths = std.ArrayList([]const u8).init(loop.allocator);
defer search_paths.deinit();
while (it.next()) |line| {
if (line.len != 0 and line[0] == ' ') {
try search_paths.append(line);
}
}
if (search_paths.len == 0) {
return error.CCompilerCannotFindHeaders;
}
// search in reverse order
var path_i: usize = 0;
while (path_i < search_paths.len) : (path_i += 1) {
const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");
defer loop.allocator.free(stdlib_path);
if (try fileExists(loop.allocator, stdlib_path)) {
self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path);
return;
}
}
return error.LibCStdLibHeaderNotFound;
}
async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) !void {
var search_buf: [2]Search = undefined;
const searches = fillSearch(&search_buf, sdk);
var result_buf = try std.Buffer.initSize(loop.allocator, 0);
defer result_buf.deinit();
for (searches) |search| {
result_buf.shrink(0);
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version);
const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");
defer loop.allocator.free(stdlib_path);
if (try fileExists(loop.allocator, stdlib_path)) {
self.include_dir = result_buf.toOwnedSlice();
return;
}
}
return error.LibCStdLibHeaderNotFound;
}
async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
var search_buf: [2]Search = undefined;
const searches = fillSearch(&search_buf, sdk);
var result_buf = try std.Buffer.initSize(loop.allocator, 0);
defer result_buf.deinit();
for (searches) |search| {
result_buf.shrink(0);
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
try stream.print("{}\\Lib\\{}\\ucrt\\", search.path, search.version);
switch (builtin.arch) {
builtin.Arch.i386 => try stream.write("x86"),
builtin.Arch.x86_64 => try stream.write("x64"),
builtin.Arch.aarch64 => try stream.write("arm"),
else => return error.UnsupportedArchitecture,
}
const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");
defer loop.allocator.free(ucrt_lib_path);
if (try fileExists(loop.allocator, ucrt_lib_path)) {
self.lib_dir = result_buf.toOwnedSlice();
return;
}
}
return error.LibCRuntimeNotFound;
}
async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) FindError!void {
self.lib_dir = try await (async ccPrintFileName(loop, "crt1.o", true) catch unreachable);
}
async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void {
self.static_lib_dir = try await (async ccPrintFileName(loop, "crtbegin.o", true) catch unreachable);
}
async fn findNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop) FindError!void {
var dyn_tests = []DynTest{
DynTest{
.name = "ld-linux-x86-64.so.2",
.result = null,
},
DynTest{
.name = "ld-musl-x86_64.so.1",
.result = null,
},
};
var group = event.Group(FindError!void).init(loop);
errdefer group.cancelAll();
for (dyn_tests) |*dyn_test| {
try group.call(testNativeDynamicLinker, self, loop, dyn_test);
}
try await (async group.wait() catch unreachable);
for (dyn_tests) |*dyn_test| {
if (dyn_test.result) |result| {
self.dynamic_linker_path = result;
return;
}
}
}
const DynTest = struct {
name: []const u8,
result: ?[]const u8,
};
async fn testNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop, dyn_test: *DynTest) FindError!void {
if (await (async ccPrintFileName(loop, dyn_test.name, false) catch unreachable)) |result| {
dyn_test.result = result;
return;
} else |err| switch (err) {
error.LibCRuntimeNotFound => return,
else => return err,
}
}
async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
var search_buf: [2]Search = undefined;
const searches = fillSearch(&search_buf, sdk);
var result_buf = try std.Buffer.initSize(loop.allocator, 0);
defer result_buf.deinit();
for (searches) |search| {
result_buf.shrink(0);
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
try stream.print("{}\\Lib\\{}\\um\\", search.path, search.version);
switch (builtin.arch) {
builtin.Arch.i386 => try stream.write("x86\\"),
builtin.Arch.x86_64 => try stream.write("x64\\"),
builtin.Arch.aarch64 => try stream.write("arm\\"),
else => return error.UnsupportedArchitecture,
}
const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");
defer loop.allocator.free(kernel32_path);
if (try fileExists(loop.allocator, kernel32_path)) {
self.kernel32_lib_dir = result_buf.toOwnedSlice();
return;
}
}
return error.LibCKernel32LibNotFound;
}
fn initEmpty(self: *LibCInstallation) void {
self.* = LibCInstallation{
.include_dir = ([*]const u8)(undefined)[0..0],
.lib_dir = null,
.static_lib_dir = null,
.msvc_lib_dir = null,
.kernel32_lib_dir = null,
.dynamic_linker_path = null,
};
}
};
/// caller owns returned memory
async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bool) ![]u8 {
const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
const arg1 = try std.fmt.allocPrint(loop.allocator, "-print-file-name={}", o_file);
defer loop.allocator.free(arg1);
const argv = []const []const u8{ cc_exe, arg1 };
// TODO This simulates evented I/O for the child process exec
await (async loop.yield() catch unreachable);
const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
const exec_result = if (std.debug.runtime_safety) blk: {
break :blk errorable_result catch unreachable;
} else blk: {
break :blk errorable_result catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => return error.UnableToSpawnCCompiler,
};
};
defer {
loop.allocator.free(exec_result.stdout);
loop.allocator.free(exec_result.stderr);
}
switch (exec_result.term) {
std.os.ChildProcess.Term.Exited => |code| {
if (code != 0) return error.CCompilerExitCode;
},
else => {
return error.CCompilerCrashed;
},
}
var it = std.mem.split(exec_result.stdout, "\n\r");
const line = it.next() orelse return error.LibCRuntimeNotFound;
const dirname = std.os.path.dirname(line) orelse return error.LibCRuntimeNotFound;
if (want_dirname) {
return std.mem.dupe(loop.allocator, u8, dirname);
} else {
return std.mem.dupe(loop.allocator, u8, line);
}
}
const Search = struct {
path: []const u8,
version: []const u8,
};
fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
var search_end: usize = 0;
if (sdk.path10_ptr) |path10_ptr| {
if (sdk.version10_ptr) |ver10_ptr| {
search_buf[search_end] = Search{
.path = path10_ptr[0..sdk.path10_len],
.version = ver10_ptr[0..sdk.version10_len],
};
search_end += 1;
}
}
if (sdk.path81_ptr) |path81_ptr| {
if (sdk.version81_ptr) |ver81_ptr| {
search_buf[search_end] = Search{
.path = path81_ptr[0..sdk.path81_len],
.version = ver81_ptr[0..sdk.version81_len],
};
search_end += 1;
}
}
return search_buf[0..search_end];
}
fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {
if (std.os.File.access(allocator, path)) |_| {
return true;
} else |err| switch (err) {
error.NotFound, error.PermissionDenied => return false,
error.OutOfMemory => return error.OutOfMemory,
else => return error.FileSystem,
}
}

737
src-self-hosted/link.zig Normal file
View File

@@ -0,0 +1,737 @@
const std = @import("std");
const mem = std.mem;
const c = @import("c.zig");
const builtin = @import("builtin");
const ObjectFormat = builtin.ObjectFormat;
const Compilation = @import("compilation.zig").Compilation;
const Target = @import("target.zig").Target;
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
const assert = std.debug.assert;
const Context = struct {
comp: *Compilation,
arena: std.heap.ArenaAllocator,
args: std.ArrayList([*]const u8),
link_in_crt: bool,
link_err: error{OutOfMemory}!void,
link_msg: std.Buffer,
libc: *LibCInstallation,
out_file_path: std.Buffer,
};
pub async fn link(comp: *Compilation) !void {
var ctx = Context{
.comp = comp,
.arena = std.heap.ArenaAllocator.init(comp.gpa()),
.args = undefined,
.link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe,
.link_err = {},
.link_msg = undefined,
.libc = undefined,
.out_file_path = undefined,
};
defer ctx.arena.deinit();
ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator);
ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
if (comp.link_out_file) |out_file| {
ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, out_file);
} else {
ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, comp.name.toSliceConst());
switch (comp.kind) {
Compilation.Kind.Exe => {
try ctx.out_file_path.append(comp.target.exeFileExt());
},
Compilation.Kind.Lib => {
try ctx.out_file_path.append(comp.target.libFileExt(comp.is_static));
},
Compilation.Kind.Obj => {
try ctx.out_file_path.append(comp.target.objFileExt());
},
}
}
// even though we're calling LLD as a library it thinks the first
// argument is its own exe name
try ctx.args.append(c"lld");
if (comp.haveLibC()) {
ctx.libc = ctx.comp.override_libc orelse blk: {
switch (comp.target) {
Target.Native => {
break :blk (await (async comp.event_loop_local.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
},
else => return error.LibCRequiredButNotProvidedOrFound,
}
};
}
try constructLinkerArgs(&ctx);
if (comp.verbose_link) {
for (ctx.args.toSliceConst()) |arg, i| {
const space = if (i == 0) "" else " ";
std.debug.warn("{}{s}", space, arg);
}
std.debug.warn("\n");
}
const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
const args_slice = ctx.args.toSlice();
{
// LLD is not thread-safe, so we grab a global lock.
const held = await (async comp.event_loop_local.lld_lock.acquire() catch unreachable);
defer held.release();
// Not evented I/O. LLD does its own multithreading internally.
if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
if (!ctx.link_msg.isNull()) {
// TODO capture these messages and pass them through the system, reporting them through the
// event system instead of printing them directly here.
// perhaps try to parse and understand them.
std.debug.warn("{}\n", ctx.link_msg.toSliceConst());
}
return error.LinkFailed;
}
}
}
extern fn ZigLLDLink(
oformat: c.ZigLLVM_ObjectFormatType,
args: [*]const [*]const u8,
arg_count: usize,
append_diagnostic: extern fn (*c_void, [*]const u8, usize) void,
context: *c_void,
) bool;
extern fn linkDiagCallback(context: *c_void, ptr: [*]const u8, len: usize) void {
const ctx = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
ctx.link_err = linkDiagCallbackErrorable(ctx, ptr[0..len]);
}
fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void {
if (ctx.link_msg.isNull()) {
try ctx.link_msg.resize(0);
}
try ctx.link_msg.append(msg);
}
fn toExternObjectFormatType(ofmt: ObjectFormat) c.ZigLLVM_ObjectFormatType {
return switch (ofmt) {
ObjectFormat.unknown => c.ZigLLVM_UnknownObjectFormat,
ObjectFormat.coff => c.ZigLLVM_COFF,
ObjectFormat.elf => c.ZigLLVM_ELF,
ObjectFormat.macho => c.ZigLLVM_MachO,
ObjectFormat.wasm => c.ZigLLVM_Wasm,
};
}
fn constructLinkerArgs(ctx: *Context) !void {
switch (ctx.comp.target.getObjectFormat()) {
ObjectFormat.unknown => unreachable,
ObjectFormat.coff => return constructLinkerArgsCoff(ctx),
ObjectFormat.elf => return constructLinkerArgsElf(ctx),
ObjectFormat.macho => return constructLinkerArgsMachO(ctx),
ObjectFormat.wasm => return constructLinkerArgsWasm(ctx),
}
}
fn constructLinkerArgsElf(ctx: *Context) !void {
// TODO commented out code in this function
//if (g->linker_script) {
// lj->args.append("-T");
// lj->args.append(g->linker_script);
//}
//if (g->no_rosegment_workaround) {
// lj->args.append("--no-rosegment");
//}
try ctx.args.append(c"--gc-sections");
//lj->args.append("-m");
//lj->args.append(getLDMOption(&g->zig_target));
//bool is_lib = g->out_type == OutTypeLib;
//bool shared = !g->is_static && is_lib;
//Buf *soname = nullptr;
if (ctx.comp.is_static) {
if (ctx.comp.target.isArmOrThumb()) {
try ctx.args.append(c"-Bstatic");
} else {
try ctx.args.append(c"-static");
}
}
//} else if (shared) {
// lj->args.append("-shared");
// if (buf_len(&lj->out_file) == 0) {
// buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "",
// buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
// }
// soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major);
//}
try ctx.args.append(c"-o");
try ctx.args.append(ctx.out_file_path.ptr());
if (ctx.link_in_crt) {
const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
const crtbegino = if (ctx.comp.is_static) "crtbeginT.o" else "crtbegin.o";
try addPathJoin(ctx, ctx.libc.lib_dir.?, crt1o);
try addPathJoin(ctx, ctx.libc.lib_dir.?, "crti.o");
try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino);
}
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
// Buf *rpath = g->rpath_list.at(i);
// add_rpath(lj, rpath);
//}
//if (g->each_lib_rpath) {
// for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
// const char *lib_dir = g->lib_dirs.at(i);
// for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
// LinkLib *link_lib = g->link_libs_list.at(i);
// if (buf_eql_str(link_lib->name, "c")) {
// continue;
// }
// bool does_exist;
// Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name));
// if (os_file_exists(test_path, &does_exist) != ErrorNone) {
// zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path));
// }
// if (does_exist) {
// add_rpath(lj, buf_create_from_str(lib_dir));
// break;
// }
// }
// }
//}
//for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
// const char *lib_dir = g->lib_dirs.at(i);
// lj->args.append("-L");
// lj->args.append(lib_dir);
//}
if (ctx.comp.haveLibC()) {
try ctx.args.append(c"-L");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr);
try ctx.args.append(c"-L");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr);
if (!ctx.comp.is_static) {
const dl = blk: {
if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
if (ctx.comp.target.getDynamicLinkerPath()) |dl| break :blk dl;
return error.LibCMissingDynamicLinker;
};
try ctx.args.append(c"-dynamic-linker");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr);
}
}
//if (shared) {
// lj->args.append("-soname");
// lj->args.append(buf_ptr(soname));
//}
// .o files
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
try ctx.args.append(link_obj_with_null.ptr);
}
try addFnObjects(ctx);
//if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
// if (g->libc_link_lib == nullptr) {
// Buf *builtin_o_path = build_o(g, "builtin");
// lj->args.append(buf_ptr(builtin_o_path));
// }
// // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage
// Buf *compiler_rt_o_path = build_compiler_rt(g);
// lj->args.append(buf_ptr(compiler_rt_o_path));
//}
//for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
// LinkLib *link_lib = g->link_libs_list.at(i);
// if (buf_eql_str(link_lib->name, "c")) {
// continue;
// }
// Buf *arg;
// if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") ||
// buf_ends_with_str(link_lib->name, ".so"))
// {
// arg = link_lib->name;
// } else {
// arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
// }
// lj->args.append(buf_ptr(arg));
//}
// libc dep
if (ctx.comp.haveLibC()) {
if (ctx.comp.is_static) {
try ctx.args.append(c"--start-group");
try ctx.args.append(c"-lgcc");
try ctx.args.append(c"-lgcc_eh");
try ctx.args.append(c"-lc");
try ctx.args.append(c"-lm");
try ctx.args.append(c"--end-group");
} else {
try ctx.args.append(c"-lgcc");
try ctx.args.append(c"--as-needed");
try ctx.args.append(c"-lgcc_s");
try ctx.args.append(c"--no-as-needed");
try ctx.args.append(c"-lc");
try ctx.args.append(c"-lm");
try ctx.args.append(c"-lgcc");
try ctx.args.append(c"--as-needed");
try ctx.args.append(c"-lgcc_s");
try ctx.args.append(c"--no-as-needed");
}
}
// crt end
if (ctx.link_in_crt) {
try addPathJoin(ctx, ctx.libc.static_lib_dir.?, "crtend.o");
try addPathJoin(ctx, ctx.libc.lib_dir.?, "crtn.o");
}
if (ctx.comp.target != Target.Native) {
try ctx.args.append(c"--allow-shlib-undefined");
}
if (ctx.comp.target.getOs() == builtin.Os.zen) {
try ctx.args.append(c"-e");
try ctx.args.append(c"_start");
try ctx.args.append(c"--image-base=0x10000000");
}
}
fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename);
const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
try ctx.args.append(full_path_with_null.ptr);
}
fn constructLinkerArgsCoff(ctx: *Context) !void {
try ctx.args.append(c"-NOLOGO");
if (!ctx.comp.strip) {
try ctx.args.append(c"-DEBUG");
}
switch (ctx.comp.target.getArch()) {
builtin.Arch.i386 => try ctx.args.append(c"-MACHINE:X86"),
builtin.Arch.x86_64 => try ctx.args.append(c"-MACHINE:X64"),
builtin.Arch.aarch64 => try ctx.args.append(c"-MACHINE:ARM"),
else => return error.UnsupportedLinkArchitecture,
}
if (ctx.comp.windows_subsystem_windows) {
try ctx.args.append(c"/SUBSYSTEM:windows");
} else if (ctx.comp.windows_subsystem_console) {
try ctx.args.append(c"/SUBSYSTEM:console");
}
const is_library = ctx.comp.kind == Compilation.Kind.Lib;
const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst());
try ctx.args.append(out_arg.ptr);
if (ctx.comp.haveLibC()) {
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr);
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr);
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr);
}
if (ctx.link_in_crt) {
const lib_str = if (ctx.comp.is_static) "lib" else "";
const d_str = if (ctx.comp.build_mode == builtin.Mode.Debug) "d" else "";
if (ctx.comp.is_static) {
const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str);
try ctx.args.append(cmt_lib_name.ptr);
} else {
const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str);
try ctx.args.append(msvcrt_lib_name.ptr);
}
const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str);
try ctx.args.append(vcruntime_lib_name.ptr);
const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str);
try ctx.args.append(crt_lib_name.ptr);
// Visual C++ 2015 Conformance Changes
// https://msdn.microsoft.com/en-us/library/bb531344.aspx
try ctx.args.append(c"legacy_stdio_definitions.lib");
// msvcrt depends on kernel32
try ctx.args.append(c"kernel32.lib");
} else {
try ctx.args.append(c"-NODEFAULTLIB");
if (!is_library) {
try ctx.args.append(c"-ENTRY:WinMainCRTStartup");
// TODO
//if (g->have_winmain) {
// lj->args.append("-ENTRY:WinMain");
//} else {
// lj->args.append("-ENTRY:WinMainCRTStartup");
//}
}
}
if (is_library and !ctx.comp.is_static) {
try ctx.args.append(c"-DLL");
}
//for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
// const char *lib_dir = g->lib_dirs.at(i);
// lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir)));
//}
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
try ctx.args.append(link_obj_with_null.ptr);
}
try addFnObjects(ctx);
switch (ctx.comp.kind) {
Compilation.Kind.Exe, Compilation.Kind.Lib => {
if (!ctx.comp.haveLibC()) {
@panic("TODO");
//Buf *builtin_o_path = build_o(g, "builtin");
//lj->args.append(buf_ptr(builtin_o_path));
}
// msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage
// TODO
//Buf *compiler_rt_o_path = build_compiler_rt(g);
//lj->args.append(buf_ptr(compiler_rt_o_path));
},
Compilation.Kind.Obj => {},
}
//Buf *def_contents = buf_alloc();
//ZigList<const char *> gen_lib_args = {0};
//for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) {
// LinkLib *link_lib = g->link_libs_list.at(lib_i);
// if (buf_eql_str(link_lib->name, "c")) {
// continue;
// }
// if (link_lib->provided_explicitly) {
// if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) {
// Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
// lj->args.append(buf_ptr(arg));
// }
// else {
// lj->args.append(buf_ptr(link_lib->name));
// }
// } else {
// buf_resize(def_contents, 0);
// buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name));
// for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) {
// Buf *symbol_name = link_lib->symbols.at(exp_i);
// buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name));
// }
// buf_appendf(def_contents, "\n");
// Buf *def_path = buf_alloc();
// os_path_join(g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path);
// os_write_file(def_path, def_contents);
// Buf *generated_lib_path = buf_alloc();
// os_path_join(g->cache_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path);
// gen_lib_args.resize(0);
// gen_lib_args.append("link");
// coff_append_machine_arg(g, &gen_lib_args);
// gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path))));
// gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path))));
// Buf diag = BUF_INIT;
// if (!zig_lld_link(g->zig_target.oformat, gen_lib_args.items, gen_lib_args.length, &diag)) {
// fprintf(stderr, "%s\n", buf_ptr(&diag));
// exit(1);
// }
// lj->args.append(buf_ptr(generated_lib_path));
// }
//}
}
fn constructLinkerArgsMachO(ctx: *Context) !void {
try ctx.args.append(c"-demangle");
if (ctx.comp.linker_rdynamic) {
try ctx.args.append(c"-export_dynamic");
}
const is_lib = ctx.comp.kind == Compilation.Kind.Lib;
const shared = !ctx.comp.is_static and is_lib;
if (ctx.comp.is_static) {
try ctx.args.append(c"-static");
} else {
try ctx.args.append(c"-dynamic");
}
//if (is_lib) {
// if (!g->is_static) {
// lj->args.append("-dylib");
// Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major);
// lj->args.append("-compatibility_version");
// lj->args.append(buf_ptr(compat_vers));
// Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize,
// g->version_major, g->version_minor, g->version_patch);
// lj->args.append("-current_version");
// lj->args.append(buf_ptr(cur_vers));
// // TODO getting an error when running an executable when doing this rpath thing
// //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
// // buf_ptr(g->root_out_name), g->version_major);
// //lj->args.append("-install_name");
// //lj->args.append(buf_ptr(dylib_install_name));
// if (buf_len(&lj->out_file) == 0) {
// buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib",
// buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
// }
// }
//}
try ctx.args.append(c"-arch");
const darwin_arch_str = try std.cstr.addNullByte(
&ctx.arena.allocator,
ctx.comp.target.getDarwinArchString(),
);
try ctx.args.append(darwin_arch_str.ptr);
const platform = try DarwinPlatform.get(ctx.comp);
switch (platform.kind) {
DarwinPlatform.Kind.MacOS => try ctx.args.append(c"-macosx_version_min"),
DarwinPlatform.Kind.IPhoneOS => try ctx.args.append(c"-iphoneos_version_min"),
DarwinPlatform.Kind.IPhoneOSSimulator => try ctx.args.append(c"-ios_simulator_version_min"),
}
const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro);
try ctx.args.append(ver_str.ptr);
if (ctx.comp.kind == Compilation.Kind.Exe) {
if (ctx.comp.is_static) {
try ctx.args.append(c"-no_pie");
} else {
try ctx.args.append(c"-pie");
}
}
try ctx.args.append(c"-o");
try ctx.args.append(ctx.out_file_path.ptr());
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
// Buf *rpath = g->rpath_list.at(i);
// add_rpath(lj, rpath);
//}
//add_rpath(lj, &lj->out_file);
if (shared) {
try ctx.args.append(c"-headerpad_max_install_names");
} else if (ctx.comp.is_static) {
try ctx.args.append(c"-lcrt0.o");
} else {
switch (platform.kind) {
DarwinPlatform.Kind.MacOS => {
if (platform.versionLessThan(10, 5)) {
try ctx.args.append(c"-lcrt1.o");
} else if (platform.versionLessThan(10, 6)) {
try ctx.args.append(c"-lcrt1.10.5.o");
} else if (platform.versionLessThan(10, 8)) {
try ctx.args.append(c"-lcrt1.10.6.o");
}
},
DarwinPlatform.Kind.IPhoneOS => {
if (ctx.comp.target.getArch() == builtin.Arch.aarch64) {
// iOS does not need any crt1 files for arm64
} else if (platform.versionLessThan(3, 1)) {
try ctx.args.append(c"-lcrt1.o");
} else if (platform.versionLessThan(6, 0)) {
try ctx.args.append(c"-lcrt1.3.1.o");
}
},
DarwinPlatform.Kind.IPhoneOSSimulator => {}, // no crt1.o needed
}
}
//for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
// const char *lib_dir = g->lib_dirs.at(i);
// lj->args.append("-L");
// lj->args.append(lib_dir);
//}
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
try ctx.args.append(link_obj_with_null.ptr);
}
try addFnObjects(ctx);
//// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
//if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
// Buf *compiler_rt_o_path = build_compiler_rt(g);
// lj->args.append(buf_ptr(compiler_rt_o_path));
//}
if (ctx.comp.target == Target.Native) {
for (ctx.comp.link_libs_list.toSliceConst()) |lib| {
if (mem.eql(u8, lib.name, "c")) {
// on Darwin, libSystem has libc in it, but also you have to use it
// to make syscalls because the syscall numbers are not documented
// and change between versions.
// so we always link against libSystem
try ctx.args.append(c"-lSystem");
} else {
if (mem.indexOfScalar(u8, lib.name, '/') == null) {
const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name);
try ctx.args.append(arg.ptr);
} else {
const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
try ctx.args.append(arg.ptr);
}
}
}
} else {
try ctx.args.append(c"-undefined");
try ctx.args.append(c"dynamic_lookup");
}
if (platform.kind == DarwinPlatform.Kind.MacOS) {
if (platform.versionLessThan(10, 5)) {
try ctx.args.append(c"-lgcc_s.10.4");
} else if (platform.versionLessThan(10, 6)) {
try ctx.args.append(c"-lgcc_s.10.5");
}
} else {
@panic("TODO");
}
//for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) {
// lj->args.append("-framework");
// lj->args.append(buf_ptr(g->darwin_frameworks.at(i)));
//}
}
fn constructLinkerArgsWasm(ctx: *Context) void {
@panic("TODO");
}
fn addFnObjects(ctx: *Context) !void {
// at this point it's guaranteed nobody else has this lock, so we circumvent it
// and avoid having to be a coroutine
const fn_link_set = &ctx.comp.fn_link_set.private_data;
var it = fn_link_set.first;
while (it) |node| {
const fn_val = node.data orelse {
// handle the tombstone. See Value.Fn.destroy.
it = node.next;
fn_link_set.remove(node);
ctx.comp.gpa().destroy(node);
continue;
};
try ctx.args.append(fn_val.containing_object.ptr());
it = node.next;
}
}
const DarwinPlatform = struct {
kind: Kind,
major: u32,
minor: u32,
micro: u32,
const Kind = enum {
MacOS,
IPhoneOS,
IPhoneOSSimulator,
};
fn get(comp: *Compilation) !DarwinPlatform {
var result: DarwinPlatform = undefined;
const ver_str = switch (comp.darwin_version_min) {
Compilation.DarwinVersionMin.MacOS => |ver| blk: {
result.kind = Kind.MacOS;
break :blk ver;
},
Compilation.DarwinVersionMin.Ios => |ver| blk: {
result.kind = Kind.IPhoneOS;
break :blk ver;
},
Compilation.DarwinVersionMin.None => blk: {
assert(comp.target.getOs() == builtin.Os.macosx);
result.kind = Kind.MacOS;
break :blk "10.10";
},
};
var had_extra: bool = undefined;
try darwinGetReleaseVersion(
ver_str,
&result.major,
&result.minor,
&result.micro,
&had_extra,
);
if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) {
return error.InvalidDarwinVersionString;
}
if (result.kind == Kind.IPhoneOS) {
switch (comp.target.getArch()) {
builtin.Arch.i386,
builtin.Arch.x86_64,
=> result.kind = Kind.IPhoneOSSimulator,
else => {},
}
}
return result;
}
fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool {
if (self.major < major)
return true;
if (self.major > major)
return false;
if (self.minor < minor)
return true;
return false;
}
};
/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the
/// grouped values as integers. Numbers which are not provided are set to 0.
/// return true if the entire string was parsed (9.2), or all groups were
/// parsed (10.3.5extrastuff).
fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void {
major.* = 0;
minor.* = 0;
micro.* = 0;
had_extra.* = false;
if (str.len == 0)
return error.InvalidDarwinVersionString;
var start_pos: usize = 0;
for ([]*u32{ major, minor, micro }) |v| {
const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.');
const end_pos = dot_pos orelse str.len;
v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString;
start_pos = (dot_pos orelse return) + 1;
if (start_pos == str.len) return;
}
had_extra.* = true;
}

View File

@@ -2,12 +2,213 @@ const builtin = @import("builtin");
const c = @import("c.zig");
const assert = @import("std").debug.assert;
pub const ValueRef = removeNullability(c.LLVMValueRef);
pub const ModuleRef = removeNullability(c.LLVMModuleRef);
pub const ContextRef = removeNullability(c.LLVMContextRef);
// we wrap the c module for 3 reasons:
// 1. to avoid accidentally calling the non-thread-safe functions
// 2. patch up some of the types to remove nullability
// 3. some functions have been augmented by zig_llvm.cpp to be more powerful,
// such as ZigLLVMTargetMachineEmitToFile
pub const AttributeIndex = c_uint;
pub const Bool = c_int;
pub const BuilderRef = removeNullability(c.LLVMBuilderRef);
pub const ContextRef = removeNullability(c.LLVMContextRef);
pub const ModuleRef = removeNullability(c.LLVMModuleRef);
pub const ValueRef = removeNullability(c.LLVMValueRef);
pub const TypeRef = removeNullability(c.LLVMTypeRef);
pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef);
pub const AttributeRef = removeNullability(c.LLVMAttributeRef);
pub const TargetRef = removeNullability(c.LLVMTargetRef);
pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef);
pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef);
pub const DIBuilder = c.ZigLLVMDIBuilder;
pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType;
pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;
pub const AddFunction = c.LLVMAddFunction;
pub const AddGlobal = c.LLVMAddGlobal;
pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
pub const ArrayType = c.LLVMArrayType;
pub const BuildLoad = c.LLVMBuildLoad;
pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
pub const ConstAllOnes = c.LLVMConstAllOnes;
pub const ConstArray = c.LLVMConstArray;
pub const ConstBitCast = c.LLVMConstBitCast;
pub const ConstInt = c.LLVMConstInt;
pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision;
pub const ConstNeg = c.LLVMConstNeg;
pub const ConstNull = c.LLVMConstNull;
pub const ConstStringInContext = c.LLVMConstStringInContext;
pub const ConstStructInContext = c.LLVMConstStructInContext;
pub const CopyStringRepOfTargetData = c.LLVMCopyStringRepOfTargetData;
pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext;
pub const CreateCompileUnit = c.ZigLLVMCreateCompileUnit;
pub const CreateDIBuilder = c.ZigLLVMCreateDIBuilder;
pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute;
pub const CreateFile = c.ZigLLVMCreateFile;
pub const CreateStringAttribute = c.LLVMCreateStringAttribute;
pub const CreateTargetDataLayout = c.LLVMCreateTargetDataLayout;
pub const CreateTargetMachine = c.LLVMCreateTargetMachine;
pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize;
pub const DisposeBuilder = c.LLVMDisposeBuilder;
pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder;
pub const DisposeMessage = c.LLVMDisposeMessage;
pub const DisposeModule = c.LLVMDisposeModule;
pub const DisposeTargetData = c.LLVMDisposeTargetData;
pub const DisposeTargetMachine = c.LLVMDisposeTargetMachine;
pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext;
pub const DumpModule = c.LLVMDumpModule;
pub const FP128TypeInContext = c.LLVMFP128TypeInContext;
pub const FloatTypeInContext = c.LLVMFloatTypeInContext;
pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;
pub const GetHostCPUName = c.ZigLLVMGetHostCPUName;
pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;
pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures;
pub const GetUndef = c.LLVMGetUndef;
pub const HalfTypeInContext = c.LLVMHalfTypeInContext;
pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers;
pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters;
pub const InitializeAllTargetInfos = c.LLVMInitializeAllTargetInfos;
pub const InitializeAllTargetMCs = c.LLVMInitializeAllTargetMCs;
pub const InitializeAllTargets = c.LLVMInitializeAllTargets;
pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext;
pub const Int128TypeInContext = c.LLVMInt128TypeInContext;
pub const Int16TypeInContext = c.LLVMInt16TypeInContext;
pub const Int1TypeInContext = c.LLVMInt1TypeInContext;
pub const Int32TypeInContext = c.LLVMInt32TypeInContext;
pub const Int64TypeInContext = c.LLVMInt64TypeInContext;
pub const Int8TypeInContext = c.LLVMInt8TypeInContext;
pub const IntPtrTypeForASInContext = c.LLVMIntPtrTypeForASInContext;
pub const IntPtrTypeInContext = c.LLVMIntPtrTypeInContext;
pub const IntTypeInContext = c.LLVMIntTypeInContext;
pub const LabelTypeInContext = c.LLVMLabelTypeInContext;
pub const MDNodeInContext = c.LLVMMDNodeInContext;
pub const MDStringInContext = c.LLVMMDStringInContext;
pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;
pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;
pub const PointerType = c.LLVMPointerType;
pub const SetAlignment = c.LLVMSetAlignment;
pub const SetDataLayout = c.LLVMSetDataLayout;
pub const SetGlobalConstant = c.LLVMSetGlobalConstant;
pub const SetInitializer = c.LLVMSetInitializer;
pub const SetLinkage = c.LLVMSetLinkage;
pub const SetTarget = c.LLVMSetTarget;
pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
pub const SetVolatile = c.LLVMSetVolatile;
pub const StructTypeInContext = c.LLVMStructTypeInContext;
pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
pub const GetElementType = LLVMGetElementType;
extern fn LLVMGetElementType(Ty: TypeRef) TypeRef;
pub const TypeOf = LLVMTypeOf;
extern fn LLVMTypeOf(Val: ValueRef) TypeRef;
pub const BuildStore = LLVMBuildStore;
extern fn LLVMBuildStore(arg0: BuilderRef, Val: ValueRef, Ptr: ValueRef) ?ValueRef;
pub const BuildAlloca = LLVMBuildAlloca;
extern fn LLVMBuildAlloca(arg0: BuilderRef, Ty: TypeRef, Name: ?[*]const u8) ?ValueRef;
pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;
pub const GetTargetFromTriple = LLVMGetTargetFromTriple;
extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool;
pub const VerifyModule = LLVMVerifyModule;
extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool;
pub const GetInsertBlock = LLVMGetInsertBlock;
extern fn LLVMGetInsertBlock(Builder: BuilderRef) BasicBlockRef;
pub const FunctionType = LLVMFunctionType;
extern fn LLVMFunctionType(
ReturnType: TypeRef,
ParamTypes: [*]TypeRef,
ParamCount: c_uint,
IsVarArg: Bool,
) ?TypeRef;
pub const GetParam = LLVMGetParam;
extern fn LLVMGetParam(Fn: ValueRef, Index: c_uint) ValueRef;
pub const AppendBasicBlockInContext = LLVMAppendBasicBlockInContext;
extern fn LLVMAppendBasicBlockInContext(C: ContextRef, Fn: ValueRef, Name: [*]const u8) ?BasicBlockRef;
pub const PositionBuilderAtEnd = LLVMPositionBuilderAtEnd;
extern fn LLVMPositionBuilderAtEnd(Builder: BuilderRef, Block: BasicBlockRef) void;
pub const AbortProcessAction = VerifierFailureAction.LLVMAbortProcessAction;
pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction;
pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction;
pub const VerifierFailureAction = c.LLVMVerifierFailureAction;
pub const CodeGenLevelNone = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelNone;
pub const CodeGenLevelLess = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelLess;
pub const CodeGenLevelDefault = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault;
pub const CodeGenLevelAggressive = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive;
pub const CodeGenOptLevel = c.LLVMCodeGenOptLevel;
pub const RelocDefault = c.LLVMRelocMode.LLVMRelocDefault;
pub const RelocStatic = c.LLVMRelocMode.LLVMRelocStatic;
pub const RelocPIC = c.LLVMRelocMode.LLVMRelocPIC;
pub const RelocDynamicNoPic = c.LLVMRelocMode.LLVMRelocDynamicNoPic;
pub const RelocMode = c.LLVMRelocMode;
pub const CodeModelDefault = c.LLVMCodeModel.LLVMCodeModelDefault;
pub const CodeModelJITDefault = c.LLVMCodeModel.LLVMCodeModelJITDefault;
pub const CodeModelSmall = c.LLVMCodeModel.LLVMCodeModelSmall;
pub const CodeModelKernel = c.LLVMCodeModel.LLVMCodeModelKernel;
pub const CodeModelMedium = c.LLVMCodeModel.LLVMCodeModelMedium;
pub const CodeModelLarge = c.LLVMCodeModel.LLVMCodeModelLarge;
pub const CodeModel = c.LLVMCodeModel;
pub const EmitAssembly = EmitOutputType.ZigLLVM_EmitAssembly;
pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary;
pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr;
pub const EmitOutputType = c.ZigLLVM_EmitOutputType;
pub const CCallConv = c.LLVMCCallConv;
pub const FastCallConv = c.LLVMFastCallConv;
pub const ColdCallConv = c.LLVMColdCallConv;
pub const WebKitJSCallConv = c.LLVMWebKitJSCallConv;
pub const AnyRegCallConv = c.LLVMAnyRegCallConv;
pub const X86StdcallCallConv = c.LLVMX86StdcallCallConv;
pub const X86FastcallCallConv = c.LLVMX86FastcallCallConv;
pub const CallConv = c.LLVMCallConv;
pub const FnInline = extern enum {
Auto,
Always,
Never,
};
fn removeNullability(comptime T: type) type {
comptime assert(@typeId(T) == builtin.TypeId.Nullable);
comptime assert(@typeId(T) == builtin.TypeId.Optional);
return T.Child;
}
pub const BuildRet = LLVMBuildRet;
extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef;
pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile;
extern fn ZigLLVMTargetMachineEmitToFile(
targ_machine_ref: TargetMachineRef,
module_ref: ModuleRef,
filename: [*]const u8,
output_type: EmitOutputType,
error_message: *[*]u8,
is_debug: bool,
is_small: bool,
) bool;
pub const BuildCall = ZigLLVMBuildCall;
extern fn ZigLLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: [*]ValueRef, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?ValueRef;
pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage;

File diff suppressed because it is too large Load Diff

View File

@@ -1,326 +0,0 @@
const std = @import("std");
const os = std.os;
const io = std.io;
const mem = std.mem;
const Buffer = std.Buffer;
const llvm = @import("llvm.zig");
const c = @import("c.zig");
const builtin = @import("builtin");
const Target = @import("target.zig").Target;
const warn = std.debug.warn;
const Tokenizer = std.zig.Tokenizer;
const Token = std.zig.Token;
const Parser = std.zig.Parser;
const ArrayList = std.ArrayList;
pub const Module = struct {
allocator: &mem.Allocator,
name: Buffer,
root_src_path: ?[]const u8,
module: llvm.ModuleRef,
context: llvm.ContextRef,
builder: llvm.BuilderRef,
target: Target,
build_mode: builtin.Mode,
zig_lib_dir: []const u8,
version_major: u32,
version_minor: u32,
version_patch: u32,
linker_script: ?[]const u8,
cache_dir: []const u8,
libc_lib_dir: ?[]const u8,
libc_static_lib_dir: ?[]const u8,
libc_include_dir: ?[]const u8,
msvc_lib_dir: ?[]const u8,
kernel32_lib_dir: ?[]const u8,
dynamic_linker: ?[]const u8,
out_h_path: ?[]const u8,
is_test: bool,
each_lib_rpath: bool,
strip: bool,
is_static: bool,
linker_rdynamic: bool,
clang_argv: []const []const u8,
llvm_argv: []const []const u8,
lib_dirs: []const []const u8,
rpath_list: []const []const u8,
assembly_files: []const []const u8,
link_objects: []const []const u8,
windows_subsystem_windows: bool,
windows_subsystem_console: bool,
link_libs_list: ArrayList(&LinkLib),
libc_link_lib: ?&LinkLib,
err_color: ErrColor,
verbose_tokenize: bool,
verbose_ast_tree: bool,
verbose_ast_fmt: bool,
verbose_cimport: bool,
verbose_ir: bool,
verbose_llvm_ir: bool,
verbose_link: bool,
darwin_frameworks: []const []const u8,
darwin_version_min: DarwinVersionMin,
test_filters: []const []const u8,
test_name_prefix: ?[]const u8,
emit_file_type: Emit,
kind: Kind,
pub const DarwinVersionMin = union(enum) {
None,
MacOS: []const u8,
Ios: []const u8,
};
pub const Kind = enum {
Exe,
Lib,
Obj,
};
pub const ErrColor = enum {
Auto,
Off,
On,
};
pub const LinkLib = struct {
name: []const u8,
path: ?[]const u8,
/// the list of symbols we depend on from this lib
symbols: ArrayList([]u8),
provided_explicitly: bool,
};
pub const Emit = enum {
Binary,
Assembly,
LlvmIr,
};
pub const CliPkg = struct {
name: []const u8,
path: []const u8,
children: ArrayList(&CliPkg),
parent: ?&CliPkg,
pub fn init(allocator: &mem.Allocator, name: []const u8, path: []const u8, parent: ?&CliPkg) !&CliPkg {
var pkg = try allocator.create(CliPkg);
pkg.name = name;
pkg.path = path;
pkg.children = ArrayList(&CliPkg).init(allocator);
pkg.parent = parent;
return pkg;
}
pub fn deinit(self: &CliPkg) void {
for (self.children.toSliceConst()) |child| {
child.deinit();
}
self.children.deinit();
}
};
pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,
kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module
{
var name_buffer = try Buffer.init(allocator, name);
errdefer name_buffer.deinit();
const context = c.LLVMContextCreate() ?? return error.OutOfMemory;
errdefer c.LLVMContextDispose(context);
const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) ?? return error.OutOfMemory;
errdefer c.LLVMDisposeModule(module);
const builder = c.LLVMCreateBuilderInContext(context) ?? return error.OutOfMemory;
errdefer c.LLVMDisposeBuilder(builder);
const module_ptr = try allocator.create(Module);
errdefer allocator.destroy(module_ptr);
*module_ptr = Module {
.allocator = allocator,
.name = name_buffer,
.root_src_path = root_src_path,
.module = module,
.context = context,
.builder = builder,
.target = *target,
.kind = kind,
.build_mode = build_mode,
.zig_lib_dir = zig_lib_dir,
.cache_dir = cache_dir,
.version_major = 0,
.version_minor = 0,
.version_patch = 0,
.verbose_tokenize = false,
.verbose_ast_tree = false,
.verbose_ast_fmt = false,
.verbose_cimport = false,
.verbose_ir = false,
.verbose_llvm_ir = false,
.verbose_link = false,
.linker_script = null,
.libc_lib_dir = null,
.libc_static_lib_dir = null,
.libc_include_dir = null,
.msvc_lib_dir = null,
.kernel32_lib_dir = null,
.dynamic_linker = null,
.out_h_path = null,
.is_test = false,
.each_lib_rpath = false,
.strip = false,
.is_static = false,
.linker_rdynamic = false,
.clang_argv = [][]const u8{},
.llvm_argv = [][]const u8{},
.lib_dirs = [][]const u8{},
.rpath_list = [][]const u8{},
.assembly_files = [][]const u8{},
.link_objects = [][]const u8{},
.windows_subsystem_windows = false,
.windows_subsystem_console = false,
.link_libs_list = ArrayList(&LinkLib).init(allocator),
.libc_link_lib = null,
.err_color = ErrColor.Auto,
.darwin_frameworks = [][]const u8{},
.darwin_version_min = DarwinVersionMin.None,
.test_filters = [][]const u8{},
.test_name_prefix = null,
.emit_file_type = Emit.Binary,
};
return module_ptr;
}
fn dump(self: &Module) void {
c.LLVMDumpModule(self.module);
}
pub fn destroy(self: &Module) void {
c.LLVMDisposeBuilder(self.builder);
c.LLVMDisposeModule(self.module);
c.LLVMContextDispose(self.context);
self.name.deinit();
self.allocator.destroy(self);
}
pub fn build(self: &Module) !void {
if (self.llvm_argv.len != 0) {
var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.allocator,
[][]const []const u8 { [][]const u8{"zig (LLVM option parsing)"}, self.llvm_argv, });
defer c_compatible_args.deinit();
c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
}
const root_src_path = self.root_src_path ?? @panic("TODO handle null root src path");
const root_src_real_path = os.path.real(self.allocator, root_src_path) catch |err| {
try printError("unable to get real path '{}': {}", root_src_path, err);
return err;
};
errdefer self.allocator.free(root_src_real_path);
const source_code = io.readFileAlloc(self.allocator, root_src_real_path) catch |err| {
try printError("unable to open '{}': {}", root_src_real_path, err);
return err;
};
errdefer self.allocator.free(source_code);
warn("====input:====\n");
warn("{}", source_code);
warn("====tokenization:====\n");
{
var tokenizer = Tokenizer.init(source_code);
while (true) {
const token = tokenizer.next();
tokenizer.dump(token);
if (token.id == Token.Id.Eof) {
break;
}
}
}
warn("====parse:====\n");
var tokenizer = Tokenizer.init(source_code);
var parser = Parser.init(&tokenizer, self.allocator, root_src_real_path);
defer parser.deinit();
var tree = try parser.parse();
defer tree.deinit();
var stderr_file = try std.io.getStdErr();
var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
const out_stream = &stderr_file_out_stream.stream;
try parser.renderAst(out_stream, tree.root_node);
warn("====fmt:====\n");
try parser.renderSource(out_stream, tree.root_node);
warn("====ir:====\n");
warn("TODO\n\n");
warn("====llvm ir:====\n");
self.dump();
}
pub fn link(self: &Module, out_file: ?[]const u8) !void {
warn("TODO link");
return error.Todo;
}
pub fn addLinkLib(self: &Module, name: []const u8, provided_explicitly: bool) !&LinkLib {
const is_libc = mem.eql(u8, name, "c");
if (is_libc) {
if (self.libc_link_lib) |libc_link_lib| {
return libc_link_lib;
}
}
for (self.link_libs_list.toSliceConst()) |existing_lib| {
if (mem.eql(u8, name, existing_lib.name)) {
return existing_lib;
}
}
const link_lib = try self.allocator.create(LinkLib);
*link_lib = LinkLib {
.name = name,
.path = null,
.provided_explicitly = provided_explicitly,
.symbols = ArrayList([]u8).init(self.allocator),
};
try self.link_libs_list.append(link_lib);
if (is_libc) {
self.libc_link_lib = link_lib;
}
return link_lib;
}
};
fn printError(comptime format: []const u8, args: ...) !void {
var stderr_file = try std.io.getStdErr();
var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
const out_stream = &stderr_file_out_stream.stream;
try out_stream.print(format, args);
}

View File

@@ -0,0 +1,29 @@
const std = @import("std");
const mem = std.mem;
const assert = std.debug.assert;
const Buffer = std.Buffer;
pub const Package = struct {
root_src_dir: Buffer,
root_src_path: Buffer,
/// relative to root_src_dir
table: Table,
pub const Table = std.HashMap([]const u8, *Package, mem.hash_slice_u8, mem.eql_slice_u8);
/// makes internal copies of root_src_dir and root_src_path
/// allocator should be an arena allocator because Package never frees anything
pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package {
return allocator.create(Package{
.root_src_dir = try Buffer.init(allocator, root_src_dir),
.root_src_path = try Buffer.init(allocator, root_src_path),
.table = Table.init(allocator),
});
}
pub fn add(self: *Package, name: []const u8, package: *Package) !void {
const entry = try self.table.put(try mem.dupe(self.table.allocator, u8, name), package);
assert(entry == null);
}
};

View File

@@ -1,16 +1,396 @@
const std = @import("std");
const builtin = @import("builtin");
const Allocator = mem.Allocator;
const Decl = @import("decl.zig").Decl;
const Compilation = @import("compilation.zig").Compilation;
const mem = std.mem;
const ast = std.zig.ast;
const Value = @import("value.zig").Value;
const Type = @import("type.zig").Type;
const ir = @import("ir.zig");
const Span = @import("errmsg.zig").Span;
const assert = std.debug.assert;
const event = std.event;
const llvm = @import("llvm.zig");
pub const Scope = struct {
id: Id,
parent: &Scope,
parent: ?*Scope,
ref_count: std.atomic.Int(usize),
/// Thread-safe
pub fn ref(base: *Scope) void {
_ = base.ref_count.incr();
}
/// Thread-safe
pub fn deref(base: *Scope, comp: *Compilation) void {
if (base.ref_count.decr() == 1) {
if (base.parent) |parent| parent.deref(comp);
switch (base.id) {
Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp),
Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(comp),
Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp),
Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp),
Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),
Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),
Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),
}
}
}
pub fn findRoot(base: *Scope) *Root {
var scope = base;
while (scope.parent) |parent| {
scope = parent;
}
assert(scope.id == Id.Root);
return @fieldParentPtr(Root, "base", scope);
}
pub fn findFnDef(base: *Scope) ?*FnDef {
var scope = base;
while (true) {
switch (scope.id) {
Id.FnDef => return @fieldParentPtr(FnDef, "base", scope),
Id.Root, Id.Decls => return null,
Id.Block,
Id.Defer,
Id.DeferExpr,
Id.CompTime,
Id.Var,
=> scope = scope.parent.?,
}
}
}
pub fn findDeferExpr(base: *Scope) ?*DeferExpr {
var scope = base;
while (true) {
switch (scope.id) {
Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", scope),
Id.FnDef,
Id.Decls,
=> return null,
Id.Block,
Id.Defer,
Id.CompTime,
Id.Root,
Id.Var,
=> scope = scope.parent orelse return null,
}
}
}
fn init(base: *Scope, id: Id, parent: *Scope) void {
base.* = Scope{
.id = id,
.parent = parent,
.ref_count = std.atomic.Int(usize).init(1),
};
parent.ref();
}
pub const Id = enum {
Root,
Decls,
Block,
Defer,
DeferExpr,
VarDecl,
CImport,
Loop,
FnDef,
CompTime,
Defer,
DeferExpr,
Var,
};
pub const Root = struct {
base: Scope,
tree: *ast.Tree,
realpath: []const u8,
/// Creates a Root scope with 1 reference
/// Takes ownership of realpath
/// Takes ownership of tree, will deinit and destroy when done.
pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
const self = try comp.gpa().createOne(Root);
self.* = Root{
.base = Scope{
.id = Id.Root,
.parent = null,
.ref_count = std.atomic.Int(usize).init(1),
},
.tree = tree,
.realpath = realpath,
};
return self;
}
pub fn destroy(self: *Root, comp: *Compilation) void {
comp.gpa().free(self.tree.source);
self.tree.deinit();
comp.gpa().destroy(self.tree);
comp.gpa().free(self.realpath);
comp.gpa().destroy(self);
}
};
pub const Decls = struct {
base: Scope,
/// The lock must be respected for writing. However once name_future resolves,
/// readers can freely access it.
table: event.Locked(Decl.Table),
/// Once this future is resolved, the table is complete and available for unlocked
/// read-only access. It does not mean all the decls are resolved; it means only that
/// the table has all the names. Each decl in the table has its own resolution state.
name_future: event.Future(void),
/// Creates a Decls scope with 1 reference
pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
const self = try comp.gpa().createOne(Decls);
self.* = Decls{
.base = undefined,
.table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
.name_future = event.Future(void).init(comp.loop),
};
self.base.init(Id.Decls, parent);
return self;
}
pub fn destroy(self: *Decls, comp: *Compilation) void {
self.table.deinit();
comp.gpa().destroy(self);
}
pub async fn getTableReadOnly(self: *Decls) *Decl.Table {
_ = await (async self.name_future.get() catch unreachable);
return &self.table.private_data;
}
};
pub const Block = struct {
base: Scope,
incoming_values: std.ArrayList(*ir.Inst),
incoming_blocks: std.ArrayList(*ir.BasicBlock),
end_block: *ir.BasicBlock,
is_comptime: *ir.Inst,
safety: Safety,
const Safety = union(enum) {
Auto,
Manual: Manual,
const Manual = struct {
/// the source span that disabled the safety value
span: Span,
/// whether safety is enabled
enabled: bool,
};
fn get(self: Safety, comp: *Compilation) bool {
return switch (self) {
Safety.Auto => switch (comp.build_mode) {
builtin.Mode.Debug,
builtin.Mode.ReleaseSafe,
=> true,
builtin.Mode.ReleaseFast,
builtin.Mode.ReleaseSmall,
=> false,
},
@TagType(Safety).Manual => |man| man.enabled,
};
}
};
/// Creates a Block scope with 1 reference
pub fn create(comp: *Compilation, parent: *Scope) !*Block {
const self = try comp.gpa().createOne(Block);
self.* = Block{
.base = undefined,
.incoming_values = undefined,
.incoming_blocks = undefined,
.end_block = undefined,
.is_comptime = undefined,
.safety = Safety.Auto,
};
self.base.init(Id.Block, parent);
return self;
}
pub fn destroy(self: *Block, comp: *Compilation) void {
comp.gpa().destroy(self);
}
};
pub const FnDef = struct {
base: Scope,
/// This reference is not counted so that the scope can get destroyed with the function
fn_val: ?*Value.Fn,
/// Creates a FnDef scope with 1 reference
/// Must set the fn_val later
pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {
const self = try comp.gpa().createOne(FnDef);
self.* = FnDef{
.base = undefined,
.fn_val = null,
};
self.base.init(Id.FnDef, parent);
return self;
}
pub fn destroy(self: *FnDef, comp: *Compilation) void {
comp.gpa().destroy(self);
}
};
pub const CompTime = struct {
base: Scope,
/// Creates a CompTime scope with 1 reference
pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {
const self = try comp.gpa().createOne(CompTime);
self.* = CompTime{ .base = undefined };
self.base.init(Id.CompTime, parent);
return self;
}
pub fn destroy(self: *CompTime, comp: *Compilation) void {
comp.gpa().destroy(self);
}
};
pub const Defer = struct {
base: Scope,
defer_expr_scope: *DeferExpr,
kind: Kind,
pub const Kind = enum {
ScopeExit,
ErrorExit,
};
/// Creates a Defer scope with 1 reference
pub fn create(
comp: *Compilation,
parent: *Scope,
kind: Kind,
defer_expr_scope: *DeferExpr,
) !*Defer {
const self = try comp.gpa().createOne(Defer);
self.* = Defer{
.base = undefined,
.defer_expr_scope = defer_expr_scope,
.kind = kind,
};
self.base.init(Id.Defer, parent);
defer_expr_scope.base.ref();
return self;
}
pub fn destroy(self: *Defer, comp: *Compilation) void {
self.defer_expr_scope.base.deref(comp);
comp.gpa().destroy(self);
}
};
pub const DeferExpr = struct {
base: Scope,
expr_node: *ast.Node,
reported_err: bool,
/// Creates a DeferExpr scope with 1 reference
pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {
const self = try comp.gpa().createOne(DeferExpr);
self.* = DeferExpr{
.base = undefined,
.expr_node = expr_node,
.reported_err = false,
};
self.base.init(Id.DeferExpr, parent);
return self;
}
pub fn destroy(self: *DeferExpr, comp: *Compilation) void {
comp.gpa().destroy(self);
}
};
pub const Var = struct {
base: Scope,
name: []const u8,
src_node: *ast.Node,
data: Data,
pub const Data = union(enum) {
Param: Param,
Const: *Value,
};
pub const Param = struct {
index: usize,
typ: *Type,
llvm_value: llvm.ValueRef,
};
pub fn createParam(
comp: *Compilation,
parent: *Scope,
name: []const u8,
src_node: *ast.Node,
param_index: usize,
param_type: *Type,
) !*Var {
const self = try create(comp, parent, name, src_node);
self.data = Data{
.Param = Param{
.index = param_index,
.typ = param_type,
.llvm_value = undefined,
},
};
return self;
}
pub fn createConst(
comp: *Compilation,
parent: *Scope,
name: []const u8,
src_node: *ast.Node,
value: *Value,
) !*Var {
const self = try create(comp, parent, name, src_node);
self.data = Data{ .Const = value };
value.ref();
return self;
}
fn create(comp: *Compilation, parent: *Scope, name: []const u8, src_node: *ast.Node) !*Var {
const self = try comp.gpa().createOne(Var);
self.* = Var{
.base = undefined,
.name = name,
.src_node = src_node,
.data = undefined,
};
self.base.init(Id.Var, parent);
return self;
}
pub fn destroy(self: *Var, comp: *Compilation) void {
switch (self.data) {
Data.Param => {},
Data.Const => |value| value.deref(comp),
}
comp.gpa().destroy(self);
}
};
};

View File

@@ -1,60 +1,562 @@
const std = @import("std");
const builtin = @import("builtin");
const c = @import("c.zig");
const llvm = @import("llvm.zig");
const CInt = @import("c_int.zig").CInt;
pub const CrossTarget = struct {
arch: builtin.Arch,
os: builtin.Os,
environ: builtin.Environ,
pub const FloatAbi = enum {
Hard,
Soft,
SoftFp,
};
pub const Target = union(enum) {
Native,
Cross: CrossTarget,
Cross: Cross,
pub fn oFileExt(self: &const Target) []const u8 {
const environ = switch (*self) {
Target.Native => builtin.environ,
Target.Cross => |t| t.environ,
};
return switch (environ) {
builtin.Environ.msvc => ".obj",
pub const Cross = struct {
arch: builtin.Arch,
os: builtin.Os,
environ: builtin.Environ,
object_format: builtin.ObjectFormat,
};
pub fn objFileExt(self: Target) []const u8 {
return switch (self.getObjectFormat()) {
builtin.ObjectFormat.coff => ".obj",
else => ".o",
};
}
pub fn exeFileExt(self: &const Target) []const u8 {
pub fn exeFileExt(self: Target) []const u8 {
return switch (self.getOs()) {
builtin.Os.windows => ".exe",
else => "",
};
}
pub fn getOs(self: &const Target) builtin.Os {
return switch (*self) {
Target.Native => builtin.os,
Target.Cross => |t| t.os,
pub fn libFileExt(self: Target, is_static: bool) []const u8 {
return switch (self.getOs()) {
builtin.Os.windows => if (is_static) ".lib" else ".dll",
else => if (is_static) ".a" else ".so",
};
}
pub fn isDarwin(self: &const Target) bool {
pub fn getOs(self: Target) builtin.Os {
return switch (self) {
Target.Native => builtin.os,
@TagType(Target).Cross => |t| t.os,
};
}
pub fn getArch(self: Target) builtin.Arch {
return switch (self) {
Target.Native => builtin.arch,
@TagType(Target).Cross => |t| t.arch,
};
}
pub fn getEnviron(self: Target) builtin.Environ {
return switch (self) {
Target.Native => builtin.environ,
@TagType(Target).Cross => |t| t.environ,
};
}
pub fn getObjectFormat(self: Target) builtin.ObjectFormat {
return switch (self) {
Target.Native => builtin.object_format,
@TagType(Target).Cross => |t| t.object_format,
};
}
pub fn isWasm(self: Target) bool {
return switch (self.getArch()) {
builtin.Arch.wasm32, builtin.Arch.wasm64 => true,
else => false,
};
}
pub fn isDarwin(self: Target) bool {
return switch (self.getOs()) {
builtin.Os.ios, builtin.Os.macosx => true,
else => false,
};
}
pub fn isWindows(self: &const Target) bool {
pub fn isWindows(self: Target) bool {
return switch (self.getOs()) {
builtin.Os.windows => true,
else => false,
};
}
};
pub fn initializeAll() void {
c.LLVMInitializeAllTargets();
c.LLVMInitializeAllTargetInfos();
c.LLVMInitializeAllTargetMCs();
c.LLVMInitializeAllAsmPrinters();
c.LLVMInitializeAllAsmParsers();
}
/// TODO expose the arch and subarch separately
pub fn isArmOrThumb(self: Target) bool {
return switch (self.getArch()) {
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
builtin.Arch.armebv8_3a,
builtin.Arch.armebv8_2a,
builtin.Arch.armebv8_1a,
builtin.Arch.armebv8,
builtin.Arch.armebv8r,
builtin.Arch.armebv8m_baseline,
builtin.Arch.armebv8m_mainline,
builtin.Arch.armebv7,
builtin.Arch.armebv7em,
builtin.Arch.armebv7m,
builtin.Arch.armebv7s,
builtin.Arch.armebv7k,
builtin.Arch.armebv7ve,
builtin.Arch.armebv6,
builtin.Arch.armebv6m,
builtin.Arch.armebv6k,
builtin.Arch.armebv6t2,
builtin.Arch.armebv5,
builtin.Arch.armebv5te,
builtin.Arch.armebv4t,
builtin.Arch.thumb,
builtin.Arch.thumbeb,
=> true,
else => false,
};
}
pub fn initializeAll() void {
llvm.InitializeAllTargets();
llvm.InitializeAllTargetInfos();
llvm.InitializeAllTargetMCs();
llvm.InitializeAllAsmPrinters();
llvm.InitializeAllAsmParsers();
}
pub fn getTriple(self: Target, allocator: *std.mem.Allocator) !std.Buffer {
var result = try std.Buffer.initSize(allocator, 0);
errdefer result.deinit();
// LLVM WebAssembly output support requires the target to be activated at
// build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly.
//
// LLVM determines the output format based on the environment suffix,
// defaulting to an object based on the architecture. The default format in
// LLVM 6 sets the wasm arch output incorrectly to ELF. We need to
// explicitly set this ourself in order for it to work.
//
// This is fixed in LLVM 7 and you will be able to get wasm output by
// using the target triple `wasm32-unknown-unknown-unknown`.
const env_name = if (self.isWasm()) "wasm" else @tagName(self.getEnviron());
var out = &std.io.BufferOutStream.init(&result).stream;
try out.print("{}-unknown-{}-{}", @tagName(self.getArch()), @tagName(self.getOs()), env_name);
return result;
}
pub fn is64bit(self: Target) bool {
return self.getArchPtrBitWidth() == 64;
}
pub fn getArchPtrBitWidth(self: Target) u32 {
switch (self.getArch()) {
builtin.Arch.avr,
builtin.Arch.msp430,
=> return 16,
builtin.Arch.arc,
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
builtin.Arch.armebv8_3a,
builtin.Arch.armebv8_2a,
builtin.Arch.armebv8_1a,
builtin.Arch.armebv8,
builtin.Arch.armebv8r,
builtin.Arch.armebv8m_baseline,
builtin.Arch.armebv8m_mainline,
builtin.Arch.armebv7,
builtin.Arch.armebv7em,
builtin.Arch.armebv7m,
builtin.Arch.armebv7s,
builtin.Arch.armebv7k,
builtin.Arch.armebv7ve,
builtin.Arch.armebv6,
builtin.Arch.armebv6m,
builtin.Arch.armebv6k,
builtin.Arch.armebv6t2,
builtin.Arch.armebv5,
builtin.Arch.armebv5te,
builtin.Arch.armebv4t,
builtin.Arch.hexagon,
builtin.Arch.le32,
builtin.Arch.mips,
builtin.Arch.mipsel,
builtin.Arch.nios2,
builtin.Arch.powerpc,
builtin.Arch.r600,
builtin.Arch.riscv32,
builtin.Arch.sparc,
builtin.Arch.sparcel,
builtin.Arch.tce,
builtin.Arch.tcele,
builtin.Arch.thumb,
builtin.Arch.thumbeb,
builtin.Arch.i386,
builtin.Arch.xcore,
builtin.Arch.nvptx,
builtin.Arch.amdil,
builtin.Arch.hsail,
builtin.Arch.spir,
builtin.Arch.kalimbav3,
builtin.Arch.kalimbav4,
builtin.Arch.kalimbav5,
builtin.Arch.shave,
builtin.Arch.lanai,
builtin.Arch.wasm32,
builtin.Arch.renderscript32,
=> return 32,
builtin.Arch.aarch64,
builtin.Arch.aarch64_be,
builtin.Arch.mips64,
builtin.Arch.mips64el,
builtin.Arch.powerpc64,
builtin.Arch.powerpc64le,
builtin.Arch.riscv64,
builtin.Arch.x86_64,
builtin.Arch.nvptx64,
builtin.Arch.le64,
builtin.Arch.amdil64,
builtin.Arch.hsail64,
builtin.Arch.spir64,
builtin.Arch.wasm64,
builtin.Arch.renderscript64,
builtin.Arch.amdgcn,
builtin.Arch.bpfel,
builtin.Arch.bpfeb,
builtin.Arch.sparcv9,
builtin.Arch.s390x,
=> return 64,
}
}
pub fn getFloatAbi(self: Target) FloatAbi {
return switch (self.getEnviron()) {
builtin.Environ.gnueabihf,
builtin.Environ.eabihf,
builtin.Environ.musleabihf,
=> FloatAbi.Hard,
else => FloatAbi.Soft,
};
}
pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
const env = self.getEnviron();
const arch = self.getArch();
switch (env) {
builtin.Environ.android => {
if (self.is64bit()) {
return "/system/bin/linker64";
} else {
return "/system/bin/linker";
}
},
builtin.Environ.gnux32 => {
if (arch == builtin.Arch.x86_64) {
return "/libx32/ld-linux-x32.so.2";
}
},
builtin.Environ.musl,
builtin.Environ.musleabi,
builtin.Environ.musleabihf,
=> {
if (arch == builtin.Arch.x86_64) {
return "/lib/ld-musl-x86_64.so.1";
}
},
else => {},
}
switch (arch) {
builtin.Arch.i386,
builtin.Arch.sparc,
builtin.Arch.sparcel,
=> return "/lib/ld-linux.so.2",
builtin.Arch.aarch64 => return "/lib/ld-linux-aarch64.so.1",
builtin.Arch.aarch64_be => return "/lib/ld-linux-aarch64_be.so.1",
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
builtin.Arch.thumb,
=> return switch (self.getFloatAbi()) {
FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
else => return "/lib/ld-linux.so.3",
},
builtin.Arch.armebv8_3a,
builtin.Arch.armebv8_2a,
builtin.Arch.armebv8_1a,
builtin.Arch.armebv8,
builtin.Arch.armebv8r,
builtin.Arch.armebv8m_baseline,
builtin.Arch.armebv8m_mainline,
builtin.Arch.armebv7,
builtin.Arch.armebv7em,
builtin.Arch.armebv7m,
builtin.Arch.armebv7s,
builtin.Arch.armebv7k,
builtin.Arch.armebv7ve,
builtin.Arch.armebv6,
builtin.Arch.armebv6m,
builtin.Arch.armebv6k,
builtin.Arch.armebv6t2,
builtin.Arch.armebv5,
builtin.Arch.armebv5te,
builtin.Arch.armebv4t,
builtin.Arch.thumbeb,
=> return switch (self.getFloatAbi()) {
FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
else => return "/lib/ld-linux.so.3",
},
builtin.Arch.mips,
builtin.Arch.mipsel,
builtin.Arch.mips64,
builtin.Arch.mips64el,
=> return null,
builtin.Arch.powerpc => return "/lib/ld.so.1",
builtin.Arch.powerpc64 => return "/lib64/ld64.so.2",
builtin.Arch.powerpc64le => return "/lib64/ld64.so.2",
builtin.Arch.s390x => return "/lib64/ld64.so.1",
builtin.Arch.sparcv9 => return "/lib64/ld-linux.so.2",
builtin.Arch.x86_64 => return "/lib64/ld-linux-x86-64.so.2",
builtin.Arch.arc,
builtin.Arch.avr,
builtin.Arch.bpfel,
builtin.Arch.bpfeb,
builtin.Arch.hexagon,
builtin.Arch.msp430,
builtin.Arch.nios2,
builtin.Arch.r600,
builtin.Arch.amdgcn,
builtin.Arch.riscv32,
builtin.Arch.riscv64,
builtin.Arch.tce,
builtin.Arch.tcele,
builtin.Arch.xcore,
builtin.Arch.nvptx,
builtin.Arch.nvptx64,
builtin.Arch.le32,
builtin.Arch.le64,
builtin.Arch.amdil,
builtin.Arch.amdil64,
builtin.Arch.hsail,
builtin.Arch.hsail64,
builtin.Arch.spir,
builtin.Arch.spir64,
builtin.Arch.kalimbav3,
builtin.Arch.kalimbav4,
builtin.Arch.kalimbav5,
builtin.Arch.shave,
builtin.Arch.lanai,
builtin.Arch.wasm32,
builtin.Arch.wasm64,
builtin.Arch.renderscript32,
builtin.Arch.renderscript64,
=> return null,
}
}
pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef {
var result: llvm.TargetRef = undefined;
var err_msg: [*]u8 = undefined;
if (llvm.GetTargetFromTriple(triple.ptr(), &result, &err_msg) != 0) {
std.debug.warn("triple: {s} error: {s}\n", triple.ptr(), err_msg);
return error.UnsupportedTarget;
}
return result;
}
pub fn cIntTypeSizeInBits(self: Target, id: CInt.Id) u32 {
const arch = self.getArch();
switch (self.getOs()) {
builtin.Os.freestanding => switch (self.getArch()) {
builtin.Arch.msp430 => switch (id) {
CInt.Id.Short,
CInt.Id.UShort,
CInt.Id.Int,
CInt.Id.UInt,
=> return 16,
CInt.Id.Long,
CInt.Id.ULong,
=> return 32,
CInt.Id.LongLong,
CInt.Id.ULongLong,
=> return 64,
},
else => switch (id) {
CInt.Id.Short,
CInt.Id.UShort,
=> return 16,
CInt.Id.Int,
CInt.Id.UInt,
=> return 32,
CInt.Id.Long,
CInt.Id.ULong,
=> return self.getArchPtrBitWidth(),
CInt.Id.LongLong,
CInt.Id.ULongLong,
=> return 64,
},
},
builtin.Os.linux,
builtin.Os.macosx,
builtin.Os.openbsd,
builtin.Os.zen,
=> switch (id) {
CInt.Id.Short,
CInt.Id.UShort,
=> return 16,
CInt.Id.Int,
CInt.Id.UInt,
=> return 32,
CInt.Id.Long,
CInt.Id.ULong,
=> return self.getArchPtrBitWidth(),
CInt.Id.LongLong,
CInt.Id.ULongLong,
=> return 64,
},
builtin.Os.windows => switch (id) {
CInt.Id.Short,
CInt.Id.UShort,
=> return 16,
CInt.Id.Int,
CInt.Id.UInt,
=> return 32,
CInt.Id.Long,
CInt.Id.ULong,
CInt.Id.LongLong,
CInt.Id.ULongLong,
=> return 64,
},
builtin.Os.ananas,
builtin.Os.cloudabi,
builtin.Os.dragonfly,
builtin.Os.freebsd,
builtin.Os.fuchsia,
builtin.Os.ios,
builtin.Os.kfreebsd,
builtin.Os.lv2,
builtin.Os.netbsd,
builtin.Os.solaris,
builtin.Os.haiku,
builtin.Os.minix,
builtin.Os.rtems,
builtin.Os.nacl,
builtin.Os.cnk,
builtin.Os.aix,
builtin.Os.cuda,
builtin.Os.nvcl,
builtin.Os.amdhsa,
builtin.Os.ps4,
builtin.Os.elfiamcu,
builtin.Os.tvos,
builtin.Os.watchos,
builtin.Os.mesa3d,
builtin.Os.contiki,
builtin.Os.amdpal,
=> @panic("TODO specify the C integer type sizes for this OS"),
}
}
pub fn getDarwinArchString(self: Target) []const u8 {
const arch = self.getArch();
switch (arch) {
builtin.Arch.aarch64 => return "arm64",
builtin.Arch.thumb,
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
=> return "arm",
builtin.Arch.powerpc => return "ppc",
builtin.Arch.powerpc64 => return "ppc64",
builtin.Arch.powerpc64le => return "ppc64le",
else => return @tagName(arch),
}
}
};

243
src-self-hosted/test.zig Normal file
View File

@@ -0,0 +1,243 @@
const std = @import("std");
const mem = std.mem;
const builtin = @import("builtin");
const Target = @import("target.zig").Target;
const Compilation = @import("compilation.zig").Compilation;
const introspect = @import("introspect.zig");
const assertOrPanic = std.debug.assertOrPanic;
const errmsg = @import("errmsg.zig");
const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
var ctx: TestContext = undefined;
test "stage2" {
try ctx.init();
defer ctx.deinit();
try @import("../test/stage2/compile_errors.zig").addCases(&ctx);
try @import("../test/stage2/compare_output.zig").addCases(&ctx);
try ctx.run();
}
const file1 = "1.zig";
const allocator = std.heap.c_allocator;
pub const TestContext = struct {
loop: std.event.Loop,
event_loop_local: EventLoopLocal,
zig_lib_dir: []u8,
file_index: std.atomic.Int(usize),
group: std.event.Group(error!void),
any_err: error!void,
const tmp_dir_name = "stage2_test_tmp";
fn init(self: *TestContext) !void {
self.* = TestContext{
.any_err = {},
.loop = undefined,
.event_loop_local = undefined,
.zig_lib_dir = undefined,
.group = undefined,
.file_index = std.atomic.Int(usize).init(0),
};
try self.loop.initMultiThreaded(allocator);
errdefer self.loop.deinit();
self.event_loop_local = try EventLoopLocal.init(&self.loop);
errdefer self.event_loop_local.deinit();
self.group = std.event.Group(error!void).init(&self.loop);
errdefer self.group.cancelAll();
self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
errdefer allocator.free(self.zig_lib_dir);
try std.os.makePath(allocator, tmp_dir_name);
errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {};
}
fn deinit(self: *TestContext) void {
std.os.deleteTree(allocator, tmp_dir_name) catch {};
allocator.free(self.zig_lib_dir);
self.event_loop_local.deinit();
self.loop.deinit();
}
fn run(self: *TestContext) !void {
const handle = try self.loop.call(waitForGroup, self);
defer cancel handle;
self.loop.run();
return self.any_err;
}
async fn waitForGroup(self: *TestContext) void {
self.any_err = await (async self.group.wait() catch unreachable);
}
fn testCompileError(
self: *TestContext,
source: []const u8,
path: []const u8,
line: usize,
column: usize,
msg: []const u8,
) !void {
var file_index_buf: [20]u8 = undefined;
const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
if (std.os.path.dirname(file1_path)) |dirname| {
try std.os.makePath(allocator, dirname);
}
// TODO async I/O
try std.io.writeFile(allocator, file1_path, source);
var comp = try Compilation.create(
&self.event_loop_local,
"test",
file1_path,
Target.Native,
Compilation.Kind.Obj,
builtin.Mode.Debug,
true, // is_static
self.zig_lib_dir,
);
errdefer comp.destroy();
try comp.build();
try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
}
fn testCompareOutputLibC(
self: *TestContext,
source: []const u8,
expected_output: []const u8,
) !void {
var file_index_buf: [20]u8 = undefined;
const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt());
if (std.os.path.dirname(file1_path)) |dirname| {
try std.os.makePath(allocator, dirname);
}
// TODO async I/O
try std.io.writeFile(allocator, file1_path, source);
var comp = try Compilation.create(
&self.event_loop_local,
"test",
file1_path,
Target.Native,
Compilation.Kind.Exe,
builtin.Mode.Debug,
false,
self.zig_lib_dir,
);
errdefer comp.destroy();
_ = try comp.addLinkLib("c", true);
comp.link_out_file = output_file;
try comp.build();
try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
}
async fn getModuleEventSuccess(
comp: *Compilation,
exe_file: []const u8,
expected_output: []const u8,
) !void {
// TODO this should not be necessary
const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file);
defer comp.destroy();
const build_event = await (async comp.events.get() catch unreachable);
switch (build_event) {
Compilation.Event.Ok => {
const argv = []const []const u8{exe_file_2};
// TODO use event loop
const child = try std.os.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024);
switch (child.term) {
std.os.ChildProcess.Term.Exited => |code| {
if (code != 0) {
return error.BadReturnCode;
}
},
else => {
return error.Crashed;
},
}
if (!mem.eql(u8, child.stdout, expected_output)) {
return error.OutputMismatch;
}
},
Compilation.Event.Error => |err| return err,
Compilation.Event.Fail => |msgs| {
var stderr = try std.io.getStdErr();
try stderr.write("build incorrectly failed:\n");
for (msgs) |msg| {
defer msg.destroy();
try msg.printToFile(&stderr, errmsg.Color.Auto);
}
},
}
}
async fn getModuleEvent(
comp: *Compilation,
source: []const u8,
path: []const u8,
line: usize,
column: usize,
text: []const u8,
) !void {
defer comp.destroy();
const build_event = await (async comp.events.get() catch unreachable);
switch (build_event) {
Compilation.Event.Ok => {
@panic("build incorrectly succeeded");
},
Compilation.Event.Error => |err| {
@panic("build incorrectly failed");
},
Compilation.Event.Fail => |msgs| {
assertOrPanic(msgs.len != 0);
for (msgs) |msg| {
if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {
const first_token = msg.getTree().tokens.at(msg.span.first);
const last_token = msg.getTree().tokens.at(msg.span.first);
const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
return;
}
}
}
std.debug.warn(
"\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n",
source,
path,
line,
column,
text,
);
std.debug.warn("\n====found:========\n");
var stderr = try std.io.getStdErr();
for (msgs) |msg| {
defer msg.destroy();
try msg.printToFile(&stderr, errmsg.Color.Auto);
}
std.debug.warn("============\n");
return error.TestFailed;
},
}
}
};

1101
src-self-hosted/type.zig Normal file

File diff suppressed because it is too large Load Diff

581
src-self-hosted/value.zig Normal file
View File

@@ -0,0 +1,581 @@
const std = @import("std");
const builtin = @import("builtin");
const Scope = @import("scope.zig").Scope;
const Compilation = @import("compilation.zig").Compilation;
const ObjectFile = @import("codegen.zig").ObjectFile;
const llvm = @import("llvm.zig");
const Buffer = std.Buffer;
const assert = std.debug.assert;
/// Values are ref-counted, heap-allocated, and copy-on-write
/// If there is only 1 ref then write need not copy
pub const Value = struct {
id: Id,
typ: *Type,
ref_count: std.atomic.Int(usize),
/// Thread-safe
pub fn ref(base: *Value) void {
_ = base.ref_count.incr();
}
/// Thread-safe
pub fn deref(base: *Value, comp: *Compilation) void {
if (base.ref_count.decr() == 1) {
base.typ.base.deref(comp);
switch (base.id) {
Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp),
Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
Id.FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp),
Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp),
Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp),
Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp),
Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp),
}
}
}
pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void {
base.typ.base.deref(comp);
new_type.base.ref();
base.typ = new_type;
}
pub fn getRef(base: *Value) *Value {
base.ref();
return base;
}
pub fn cast(base: *Value, comptime T: type) ?*T {
if (base.id != @field(Id, @typeName(T))) return null;
return @fieldParentPtr(T, "base", base);
}
pub fn dump(base: *const Value) void {
std.debug.warn("{}", @tagName(base.id));
}
pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) {
switch (base.id) {
Id.Type => unreachable,
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile),
Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
Id.Void => return null,
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
Id.NoReturn => unreachable,
Id.Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile),
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile),
Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile),
}
}
pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
if (self.ref_count.get() == 1) {
// ( ͡° ͜ʖ ͡°)
return self;
}
assert(self.ref_count.decr() != 1);
return self.copy(comp);
}
pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
switch (base.id) {
Id.Type => unreachable,
Id.Fn => unreachable,
Id.FnProto => unreachable,
Id.Void => unreachable,
Id.Bool => unreachable,
Id.NoReturn => unreachable,
Id.Ptr => unreachable,
Id.Array => unreachable,
Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base,
}
}
pub const Parent = union(enum) {
None,
BaseStruct: BaseStruct,
BaseArray: BaseArray,
BaseUnion: *Value,
BaseScalar: *Value,
pub const BaseStruct = struct {
val: *Value,
field_index: usize,
};
pub const BaseArray = struct {
val: *Value,
elem_index: usize,
};
};
pub const Id = enum {
Type,
Fn,
Void,
Bool,
NoReturn,
Array,
Ptr,
Int,
FnProto,
};
pub const Type = @import("type.zig").Type;
pub const FnProto = struct {
base: Value,
/// The main external name that is used in the .o file.
/// TODO https://github.com/ziglang/zig/issues/265
symbol_name: Buffer,
pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto {
const self = try comp.gpa().create(FnProto{
.base = Value{
.id = Value.Id.FnProto,
.typ = &fn_type.base,
.ref_count = std.atomic.Int(usize).init(1),
},
.symbol_name = symbol_name,
});
fn_type.base.base.ref();
return self;
}
pub fn destroy(self: *FnProto, comp: *Compilation) void {
self.symbol_name.deinit();
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?llvm.ValueRef {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
self.symbol_name.ptr(),
llvm_fn_type,
) orelse return error.OutOfMemory;
// TODO port more logic from codegen.cpp:fn_llvm_value
return llvm_fn;
}
};
pub const Fn = struct {
base: Value,
/// The main external name that is used in the .o file.
/// TODO https://github.com/ziglang/zig/issues/265
symbol_name: Buffer,
/// parent should be the top level decls or container decls
fndef_scope: *Scope.FnDef,
/// parent is scope for last parameter
child_scope: *Scope,
/// parent is child_scope
block_scope: ?*Scope.Block,
/// Path to the object file that contains this function
containing_object: Buffer,
link_set_node: *std.LinkedList(?*Value.Fn).Node,
/// Creates a Fn value with 1 ref
/// Takes ownership of symbol_name
pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn {
const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{
.data = null,
.next = undefined,
.prev = undefined,
});
errdefer comp.gpa().destroy(link_set_node);
const self = try comp.gpa().create(Fn{
.base = Value{
.id = Value.Id.Fn,
.typ = &fn_type.base,
.ref_count = std.atomic.Int(usize).init(1),
},
.fndef_scope = fndef_scope,
.child_scope = &fndef_scope.base,
.block_scope = null,
.symbol_name = symbol_name,
.containing_object = Buffer.initNull(comp.gpa()),
.link_set_node = link_set_node,
});
fn_type.base.base.ref();
fndef_scope.fn_val = self;
fndef_scope.base.ref();
return self;
}
pub fn destroy(self: *Fn, comp: *Compilation) void {
// remove with a tombstone so that we do not have to grab a lock
if (self.link_set_node.data != null) {
// it's now the job of the link step to find this tombstone and
// deallocate it.
self.link_set_node.data = null;
} else {
comp.gpa().destroy(self.link_set_node);
}
self.containing_object.deinit();
self.fndef_scope.base.deref(comp);
self.symbol_name.deinit();
comp.gpa().destroy(self);
}
/// We know that the function definition will end up in an .o file somewhere.
/// Here, all we have to do is generate a global prototype.
/// TODO cache the prototype per ObjectFile
pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?llvm.ValueRef {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
self.symbol_name.ptr(),
llvm_fn_type,
) orelse return error.OutOfMemory;
// TODO port more logic from codegen.cpp:fn_llvm_value
return llvm_fn;
}
};
pub const Void = struct {
base: Value,
pub fn get(comp: *Compilation) *Void {
comp.void_value.base.ref();
return comp.void_value;
}
pub fn destroy(self: *Void, comp: *Compilation) void {
comp.gpa().destroy(self);
}
};
pub const Bool = struct {
base: Value,
x: bool,
pub fn get(comp: *Compilation, x: bool) *Bool {
if (x) {
comp.true_value.base.ref();
return comp.true_value;
} else {
comp.false_value.base.ref();
return comp.false_value;
}
}
pub fn destroy(self: *Bool, comp: *Compilation) void {
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef {
const llvm_type = llvm.Int1TypeInContext(ofile.context);
if (self.x) {
return llvm.ConstAllOnes(llvm_type);
} else {
return llvm.ConstNull(llvm_type);
}
}
};
pub const NoReturn = struct {
base: Value,
pub fn get(comp: *Compilation) *NoReturn {
comp.noreturn_value.base.ref();
return comp.noreturn_value;
}
pub fn destroy(self: *NoReturn, comp: *Compilation) void {
comp.gpa().destroy(self);
}
};
pub const Ptr = struct {
base: Value,
special: Special,
mut: Mut,
pub const Mut = enum {
CompTimeConst,
CompTimeVar,
RunTime,
};
pub const Special = union(enum) {
Scalar: *Value,
BaseArray: BaseArray,
BaseStruct: BaseStruct,
HardCodedAddr: u64,
Discard,
};
pub const BaseArray = struct {
val: *Value,
elem_index: usize,
};
pub const BaseStruct = struct {
val: *Value,
field_index: usize,
};
pub async fn createArrayElemPtr(
comp: *Compilation,
array_val: *Array,
mut: Type.Pointer.Mut,
size: Type.Pointer.Size,
elem_index: usize,
) !*Ptr {
array_val.base.ref();
errdefer array_val.base.deref(comp);
const elem_type = array_val.base.typ.cast(Type.Array).?.key.elem_type;
const ptr_type = try await (async Type.Pointer.get(comp, Type.Pointer.Key{
.child_type = elem_type,
.mut = mut,
.vol = Type.Pointer.Vol.Non,
.size = size,
.alignment = Type.Pointer.Align.Abi,
}) catch unreachable);
var ptr_type_consumed = false;
errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
const self = try comp.gpa().create(Value.Ptr{
.base = Value{
.id = Value.Id.Ptr,
.typ = &ptr_type.base,
.ref_count = std.atomic.Int(usize).init(1),
},
.special = Special{
.BaseArray = BaseArray{
.val = &array_val.base,
.elem_index = 0,
},
},
.mut = Mut.CompTimeConst,
});
ptr_type_consumed = true;
errdefer comp.gpa().destroy(self);
return self;
}
pub fn destroy(self: *Ptr, comp: *Compilation) void {
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?llvm.ValueRef {
const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context);
// TODO carefully port the logic from codegen.cpp:gen_const_val_ptr
switch (self.special) {
Special.Scalar => |scalar| @panic("TODO"),
Special.BaseArray => |base_array| {
// TODO put this in one .o file only, and after that, generate extern references to it
const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
const ptr_bit_count = ofile.comp.target_ptr_bits;
const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
const indices = []llvm.ValueRef{
llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
};
return llvm.ConstInBoundsGEP(
array_llvm_value,
&indices,
@intCast(c_uint, indices.len),
) orelse return error.OutOfMemory;
},
Special.BaseStruct => |base_struct| @panic("TODO"),
Special.HardCodedAddr => |addr| @panic("TODO"),
Special.Discard => unreachable,
}
}
};
pub const Array = struct {
base: Value,
special: Special,
pub const Special = union(enum) {
Undefined,
OwnedBuffer: []u8,
Explicit: Data,
};
pub const Data = struct {
parent: Parent,
elements: []*Value,
};
/// Takes ownership of buffer
pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
const u8_type = Type.Int.get_u8(comp);
defer u8_type.base.base.deref(comp);
const array_type = try await (async Type.Array.get(comp, Type.Array.Key{
.elem_type = &u8_type.base,
.len = buffer.len,
}) catch unreachable);
errdefer array_type.base.base.deref(comp);
const self = try comp.gpa().create(Value.Array{
.base = Value{
.id = Value.Id.Array,
.typ = &array_type.base,
.ref_count = std.atomic.Int(usize).init(1),
},
.special = Special{ .OwnedBuffer = buffer },
});
errdefer comp.gpa().destroy(self);
return self;
}
pub fn destroy(self: *Array, comp: *Compilation) void {
switch (self.special) {
Special.Undefined => {},
Special.OwnedBuffer => |buf| {
comp.gpa().free(buf);
},
Special.Explicit => {},
}
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?llvm.ValueRef {
switch (self.special) {
Special.Undefined => {
const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
return llvm.GetUndef(llvm_type);
},
Special.OwnedBuffer => |buf| {
const dont_null_terminate = 1;
const llvm_str_init = llvm.ConstStringInContext(
ofile.context,
buf.ptr,
@intCast(c_uint, buf.len),
dont_null_terminate,
) orelse return error.OutOfMemory;
const str_init_type = llvm.TypeOf(llvm_str_init);
const global = llvm.AddGlobal(ofile.module, str_init_type, c"") orelse return error.OutOfMemory;
llvm.SetInitializer(global, llvm_str_init);
llvm.SetLinkage(global, llvm.PrivateLinkage);
llvm.SetGlobalConstant(global, 1);
llvm.SetUnnamedAddr(global, 1);
llvm.SetAlignment(global, llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, str_init_type));
return global;
},
Special.Explicit => @panic("TODO"),
}
//{
// uint64_t len = type_entry->data.array.len;
// if (const_val->data.x_array.special == ConstArraySpecialUndef) {
// return LLVMGetUndef(type_entry->type_ref);
// }
// LLVMValueRef *values = allocate<LLVMValueRef>(len);
// LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref;
// bool make_unnamed_struct = false;
// for (uint64_t i = 0; i < len; i += 1) {
// ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
// LLVMValueRef val = gen_const_val(g, elem_value, "");
// values[i] = val;
// make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
// }
// if (make_unnamed_struct) {
// return LLVMConstStruct(values, len, true);
// } else {
// return LLVMConstArray(element_type_ref, values, (unsigned)len);
// }
//}
}
};
pub const Int = struct {
base: Value,
big_int: std.math.big.Int,
pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int {
const self = try comp.gpa().create(Value.Int{
.base = Value{
.id = Value.Id.Int,
.typ = typ,
.ref_count = std.atomic.Int(usize).init(1),
},
.big_int = undefined,
});
typ.base.ref();
errdefer comp.gpa().destroy(self);
self.big_int = try std.math.big.Int.init(comp.gpa());
errdefer self.big_int.deinit();
try self.big_int.setString(base, value);
return self;
}
pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef {
switch (self.base.typ.id) {
Type.Id.Int => {
const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
if (self.big_int.len == 0) {
return llvm.ConstNull(type_ref);
}
const unsigned_val = if (self.big_int.len == 1) blk: {
break :blk llvm.ConstInt(type_ref, self.big_int.limbs[0], @boolToInt(false));
} else if (@sizeOf(std.math.big.Limb) == @sizeOf(u64)) blk: {
break :blk llvm.ConstIntOfArbitraryPrecision(
type_ref,
@intCast(c_uint, self.big_int.len),
@ptrCast([*]u64, self.big_int.limbs.ptr),
);
} else {
@compileError("std.math.Big.Int.Limb size does not match LLVM");
};
return if (self.big_int.positive) unsigned_val else llvm.ConstNeg(unsigned_val);
},
Type.Id.ComptimeInt => unreachable,
else => unreachable,
}
}
pub fn copy(old: *Int, comp: *Compilation) !*Int {
old.base.typ.base.ref();
errdefer old.base.typ.base.deref(comp);
const new = try comp.gpa().create(Value.Int{
.base = Value{
.id = Value.Id.Int,
.typ = old.base.typ,
.ref_count = std.atomic.Int(usize).init(1),
},
.big_int = undefined,
});
errdefer comp.gpa().destroy(new);
new.big_int = try old.big_int.clone();
errdefer new.big_int.deinit();
return new;
}
pub fn destroy(self: *Int, comp: *Compilation) void {
self.big_int.deinit();
comp.gpa().destroy(self);
}
};
};

View File

@@ -0,0 +1,4 @@
pub const Visib = enum {
Private,
Pub,
};

View File

@@ -60,7 +60,7 @@ struct IrExecutable {
ZigList<Tld *> tld_list;
IrInstruction *coro_handle;
IrInstruction *coro_awaiter_field_ptr; // this one is shared and in the promise
IrInstruction *atomic_state_field_ptr; // this one is shared and in the promise
IrInstruction *coro_result_ptr_field_ptr;
IrInstruction *coro_result_field_ptr;
IrInstruction *await_handle_var_ptr; // this one is where we put the one we extracted from the promise
@@ -83,6 +83,7 @@ enum ConstParentId {
ConstParentIdStruct,
ConstParentIdArray,
ConstParentIdUnion,
ConstParentIdScalar,
};
struct ConstParent {
@@ -100,6 +101,9 @@ struct ConstParent {
struct {
ConstExprValue *union_val;
} p_union;
struct {
ConstExprValue *scalar_val;
} p_scalar;
} data;
};
@@ -140,6 +144,9 @@ enum ConstPtrSpecial {
// understand the value of pointee at compile time. However, we will still
// emit a binary with a compile time known address.
// In this case index is the numeric address value.
// We also use this for null pointer. We need the data layout for ConstCastOnly == true
// types to be the same, so all optionals of pointer types use x_ptr
// instead of x_optional
ConstPtrSpecialHardCodedAddr,
// This means that the pointer represents memory of assigning to _.
// That is, storing discards the data, and loading is invalid.
@@ -215,10 +222,10 @@ enum RuntimeHintErrorUnion {
RuntimeHintErrorUnionNonError,
};
enum RuntimeHintMaybe {
RuntimeHintMaybeUnknown,
RuntimeHintMaybeNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known.
RuntimeHintMaybeNonNull,
enum RuntimeHintOptional {
RuntimeHintOptionalUnknown,
RuntimeHintOptionalNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known.
RuntimeHintOptionalNonNull,
};
enum RuntimeHintPtr {
@@ -227,6 +234,16 @@ enum RuntimeHintPtr {
RuntimeHintPtrNonStack,
};
enum RuntimeHintSliceId {
RuntimeHintSliceIdUnknown,
RuntimeHintSliceIdLen,
};
struct RuntimeHintSlice {
enum RuntimeHintSliceId id;
uint64_t len;
};
struct ConstGlobalRefs {
LLVMValueRef llvm_value;
LLVMValueRef llvm_global;
@@ -241,13 +258,14 @@ struct ConstExprValue {
// populated if special == ConstValSpecialStatic
BigInt x_bigint;
BigFloat x_bigfloat;
float16_t x_f16;
float x_f32;
double x_f64;
float128_t x_f128;
bool x_bool;
ConstBoundFnValue x_bound_fn;
TypeTableEntry *x_type;
ConstExprValue *x_maybe;
ConstExprValue *x_optional;
ConstErrValue x_err_union;
ErrorTableEntry *x_err_set;
BigInt x_enum_tag;
@@ -261,8 +279,9 @@ struct ConstExprValue {
// populated if special == ConstValSpecialRuntime
RuntimeHintErrorUnion rh_error_union;
RuntimeHintMaybe rh_maybe;
RuntimeHintOptional rh_maybe;
RuntimeHintPtr rh_ptr;
RuntimeHintSlice rh_slice;
} data;
};
@@ -374,11 +393,13 @@ enum NodeType {
NodeTypeCharLiteral,
NodeTypeSymbol,
NodeTypePrefixOpExpr,
NodeTypeAddrOfExpr,
NodeTypePointerType,
NodeTypeFnCallExpr,
NodeTypeArrayAccessExpr,
NodeTypeSliceExpr,
NodeTypeFieldAccessExpr,
NodeTypePtrDeref,
NodeTypeUnwrapOptional,
NodeTypeUse,
NodeTypeBoolLiteral,
NodeTypeNullLiteral,
@@ -548,7 +569,7 @@ enum BinOpType {
BinOpTypeMultWrap,
BinOpTypeDiv,
BinOpTypeMod,
BinOpTypeUnwrapMaybe,
BinOpTypeUnwrapOptional,
BinOpTypeArrayCat,
BinOpTypeArrayMult,
BinOpTypeErrorUnion,
@@ -567,6 +588,10 @@ struct AstNodeCatchExpr {
AstNode *op2;
};
struct AstNodeUnwrapOptional {
AstNode *expr;
};
enum CastOp {
CastOpNoCast, // signifies the function call expression is not a cast
CastOpNoop, // fn call expr is a cast, but does nothing
@@ -577,6 +602,8 @@ enum CastOp {
CastOpBytesToSlice,
CastOpNumLitToConcrete,
CastOpErrSet,
CastOpBitCast,
CastOpPtrOfArrayToSlice,
};
struct AstNodeFnCallExpr {
@@ -603,15 +630,18 @@ struct AstNodeFieldAccessExpr {
Buf *field_name;
};
struct AstNodePtrDerefExpr {
AstNode *target;
};
enum PrefixOp {
PrefixOpInvalid,
PrefixOpBoolNot,
PrefixOpBinNot,
PrefixOpNegation,
PrefixOpNegationWrap,
PrefixOpDereference,
PrefixOpMaybe,
PrefixOpUnwrapMaybe,
PrefixOpOptional,
PrefixOpAddrOf,
};
struct AstNodePrefixOpExpr {
@@ -619,7 +649,8 @@ struct AstNodePrefixOpExpr {
AstNode *primary_expr;
};
struct AstNodeAddrOfExpr {
struct AstNodePointerType {
Token *star_token;
AstNode *align_expr;
BigInt *bit_offset_start;
BigInt *bit_offset_end;
@@ -868,7 +899,6 @@ struct AstNodeAwaitExpr {
struct AstNodeSuspend {
AstNode *block;
AstNode *promise_symbol;
};
struct AstNodePromiseType {
@@ -893,8 +923,9 @@ struct AstNode {
AstNodeTestDecl test_decl;
AstNodeBinOpExpr bin_op_expr;
AstNodeCatchExpr unwrap_err_expr;
AstNodeUnwrapOptional unwrap_optional;
AstNodePrefixOpExpr prefix_op_expr;
AstNodeAddrOfExpr addr_of_expr;
AstNodePointerType pointer_type;
AstNodeFnCallExpr fn_call_expr;
AstNodeArrayAccessExpr array_access_expr;
AstNodeSliceExpr slice_expr;
@@ -910,6 +941,7 @@ struct AstNode {
AstNodeCompTime comptime_expr;
AstNodeAsmExpr asm_expr;
AstNodeFieldAccessExpr field_access_expr;
AstNodePtrDerefExpr ptr_deref_expr;
AstNodeContainerDecl container_decl;
AstNodeStructField struct_field;
AstNodeStringLiteral string_literal;
@@ -966,8 +998,14 @@ struct FnTypeId {
uint32_t fn_type_id_hash(FnTypeId*);
bool fn_type_id_eql(FnTypeId *a, FnTypeId *b);
enum PtrLen {
PtrLenUnknown,
PtrLenSingle,
};
struct TypeTableEntryPointer {
TypeTableEntry *child_type;
PtrLen ptr_len;
bool is_const;
bool is_volatile;
uint32_t alignment;
@@ -1018,6 +1056,10 @@ struct TypeTableEntryStruct {
// whether we've finished resolving it
bool complete;
// whether any of the fields require comptime
// the value is not valid until zero_bits_known == true
bool requires_comptime;
bool zero_bits_loop_flag;
bool zero_bits_known;
uint32_t abi_alignment; // also figured out with zero_bits pass
@@ -1025,7 +1067,7 @@ struct TypeTableEntryStruct {
HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name;
};
struct TypeTableEntryMaybe {
struct TypeTableEntryOptional {
TypeTableEntry *child_type;
};
@@ -1059,8 +1101,7 @@ struct TypeTableEntryEnum {
bool zero_bits_loop_flag;
bool zero_bits_known;
bool generate_name_table;
LLVMValueRef name_table;
LLVMValueRef name_function;
HashMap<Buf *, TypeEnumField *, buf_hash, buf_eql_buf> fields_by_name;
};
@@ -1086,6 +1127,10 @@ struct TypeTableEntryUnion {
// whether we've finished resolving it
bool complete;
// whether any of the fields require comptime
// the value is not valid until zero_bits_known == true
bool requires_comptime;
bool zero_bits_loop_flag;
bool zero_bits_known;
uint32_t abi_alignment; // also figured out with zero_bits pass
@@ -1140,11 +1185,11 @@ enum TypeTableEntryId {
TypeTableEntryIdPointer,
TypeTableEntryIdArray,
TypeTableEntryIdStruct,
TypeTableEntryIdNumLitFloat,
TypeTableEntryIdNumLitInt,
TypeTableEntryIdUndefLit,
TypeTableEntryIdNullLit,
TypeTableEntryIdMaybe,
TypeTableEntryIdComptimeFloat,
TypeTableEntryIdComptimeInt,
TypeTableEntryIdUndefined,
TypeTableEntryIdNull,
TypeTableEntryIdOptional,
TypeTableEntryIdErrorUnion,
TypeTableEntryIdErrorSet,
TypeTableEntryIdEnum,
@@ -1175,7 +1220,7 @@ struct TypeTableEntry {
TypeTableEntryFloat floating;
TypeTableEntryArray array;
TypeTableEntryStruct structure;
TypeTableEntryMaybe maybe;
TypeTableEntryOptional maybe;
TypeTableEntryErrorUnion error_union;
TypeTableEntryErrorSet error_set;
TypeTableEntryEnum enumeration;
@@ -1187,7 +1232,7 @@ struct TypeTableEntry {
// use these fields to make sure we don't duplicate type table entries for the same type
TypeTableEntry *pointer_parent[2]; // [0 - mut, 1 - const]
TypeTableEntry *maybe_parent;
TypeTableEntry *optional_parent;
TypeTableEntry *promise_parent;
TypeTableEntry *promise_frame_parent;
// If we generate a constant name value for this type, we memoize it here.
@@ -1291,6 +1336,8 @@ enum BuiltinFnId {
BuiltinFnIdMemberCount,
BuiltinFnIdMemberType,
BuiltinFnIdMemberName,
BuiltinFnIdField,
BuiltinFnIdTypeInfo,
BuiltinFnIdTypeof,
BuiltinFnIdAddWithOverflow,
BuiltinFnIdSubWithOverflow,
@@ -1303,27 +1350,42 @@ enum BuiltinFnId {
BuiltinFnIdCompileLog,
BuiltinFnIdCtz,
BuiltinFnIdClz,
BuiltinFnIdPopCount,
BuiltinFnIdImport,
BuiltinFnIdCImport,
BuiltinFnIdErrName,
BuiltinFnIdBreakpoint,
BuiltinFnIdReturnAddress,
BuiltinFnIdFrameAddress,
BuiltinFnIdHandle,
BuiltinFnIdEmbedFile,
BuiltinFnIdCmpExchange,
BuiltinFnIdCmpxchgWeak,
BuiltinFnIdCmpxchgStrong,
BuiltinFnIdFence,
BuiltinFnIdDivExact,
BuiltinFnIdDivTrunc,
BuiltinFnIdDivFloor,
BuiltinFnIdRem,
BuiltinFnIdMod,
BuiltinFnIdSqrt,
BuiltinFnIdTruncate,
BuiltinFnIdIntCast,
BuiltinFnIdFloatCast,
BuiltinFnIdErrSetCast,
BuiltinFnIdToBytes,
BuiltinFnIdFromBytes,
BuiltinFnIdIntToFloat,
BuiltinFnIdFloatToInt,
BuiltinFnIdBoolToInt,
BuiltinFnIdErrToInt,
BuiltinFnIdIntToErr,
BuiltinFnIdEnumToInt,
BuiltinFnIdIntToEnum,
BuiltinFnIdIntType,
BuiltinFnIdSetCold,
BuiltinFnIdSetRuntimeSafety,
BuiltinFnIdSetFloatMode,
BuiltinFnIdTypeName,
BuiltinFnIdCanImplicitCast,
BuiltinFnIdPanic,
BuiltinFnIdPtrCast,
BuiltinFnIdBitCast,
@@ -1335,6 +1397,7 @@ enum BuiltinFnId {
BuiltinFnIdOffsetOf,
BuiltinFnIdInlineCall,
BuiltinFnIdNoInlineCall,
BuiltinFnIdNewStackCall,
BuiltinFnIdTypeId,
BuiltinFnIdShlExact,
BuiltinFnIdShrExact,
@@ -1346,6 +1409,7 @@ enum BuiltinFnId {
BuiltinFnIdExport,
BuiltinFnIdErrorReturnTrace,
BuiltinFnIdAtomicRmw,
BuiltinFnIdAtomicLoad,
};
struct BuiltinFnEntry {
@@ -1366,10 +1430,12 @@ enum PanicMsgId {
PanicMsgIdRemainderDivisionByZero,
PanicMsgIdExactDivisionRemainder,
PanicMsgIdSliceWidenRemainder,
PanicMsgIdUnwrapMaybeFail,
PanicMsgIdUnwrapOptionalFail,
PanicMsgIdInvalidErrorCode,
PanicMsgIdIncorrectAlignment,
PanicMsgIdBadUnionField,
PanicMsgIdBadEnumValue,
PanicMsgIdFloatToInt,
PanicMsgIdCount,
};
@@ -1383,6 +1449,7 @@ struct TypeId {
union {
struct {
TypeTableEntry *child_type;
PtrLen ptr_len;
bool is_const;
bool is_volatile;
uint32_t alignment;
@@ -1410,9 +1477,11 @@ bool type_id_eql(TypeId a, TypeId b);
enum ZigLLVMFnId {
ZigLLVMFnIdCtz,
ZigLLVMFnIdClz,
ZigLLVMFnIdPopCount,
ZigLLVMFnIdOverflowArithmetic,
ZigLLVMFnIdFloor,
ZigLLVMFnIdCeil,
ZigLLVMFnIdSqrt,
};
enum AddSubMul {
@@ -1433,7 +1502,10 @@ struct ZigLLVMFnKey {
} clz;
struct {
uint32_t bit_count;
} floor_ceil;
} pop_count;
struct {
uint32_t bit_count;
} floating;
struct {
AddSubMul add_sub_mul;
uint32_t bit_count;
@@ -1454,6 +1526,7 @@ enum BuildMode {
BuildModeDebug,
BuildModeFastRelease,
BuildModeSafeRelease,
BuildModeSmallRelease,
};
enum EmitFileType {
@@ -1499,6 +1572,7 @@ struct CodeGen {
HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> exported_symbol_names;
HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> external_prototypes;
HashMap<Buf *, ConstExprValue *, buf_hash, buf_eql_buf> string_literals_table;
HashMap<const TypeTableEntry *, ConstExprValue *, type_ptr_hash, type_ptr_eql> type_info_cache;
ZigList<ImportTableEntry *> import_queue;
@@ -1512,7 +1586,6 @@ struct CodeGen {
struct {
TypeTableEntry *entry_bool;
TypeTableEntry *entry_int[2][12]; // [signed,unsigned][2,3,4,5,6,7,8,16,29,32,64,128]
TypeTableEntry *entry_c_int[CIntTypeCount];
TypeTableEntry *entry_c_longdouble;
TypeTableEntry *entry_c_void;
@@ -1521,14 +1594,12 @@ struct CodeGen {
TypeTableEntry *entry_u32;
TypeTableEntry *entry_u29;
TypeTableEntry *entry_u64;
TypeTableEntry *entry_u128;
TypeTableEntry *entry_i8;
TypeTableEntry *entry_i16;
TypeTableEntry *entry_i32;
TypeTableEntry *entry_i64;
TypeTableEntry *entry_i128;
TypeTableEntry *entry_isize;
TypeTableEntry *entry_usize;
TypeTableEntry *entry_f16;
TypeTableEntry *entry_f32;
TypeTableEntry *entry_f64;
TypeTableEntry *entry_f128;
@@ -1645,10 +1716,16 @@ struct CodeGen {
LLVMValueRef coro_save_fn_val;
LLVMValueRef coro_promise_fn_val;
LLVMValueRef coro_alloc_helper_fn_val;
LLVMValueRef coro_frame_fn_val;
LLVMValueRef merge_err_ret_traces_fn_val;
LLVMValueRef add_error_return_trace_addr_fn_val;
LLVMValueRef stacksave_fn_val;
LLVMValueRef stackrestore_fn_val;
LLVMValueRef write_register_fn_val;
bool error_during_imports;
LLVMValueRef sp_md_node;
const char **clang_argv;
size_t clang_argv_len;
ZigList<const char *> lib_dirs;
@@ -1680,8 +1757,6 @@ struct CodeGen {
ZigList<Buf *> link_objects;
ZigList<Buf *> assembly_files;
ZigList<TypeTableEntry *> name_table_enums;
Buf *test_filter;
Buf *test_name_prefix;
@@ -1700,6 +1775,8 @@ struct CodeGen {
ZigList<ZigLLVMDIType **> error_di_types;
ZigList<Buf *> forbidden_libs;
bool no_rosegment_workaround;
};
enum VarLinkage {
@@ -1750,6 +1827,7 @@ enum ScopeId {
ScopeIdVarDecl,
ScopeIdCImport,
ScopeIdLoop,
ScopeIdSuspend,
ScopeIdFnDef,
ScopeIdCompTime,
ScopeIdCoroPrelude,
@@ -1845,6 +1923,16 @@ struct ScopeLoop {
ZigList<IrBasicBlock *> *incoming_blocks;
};
// This scope is created for a suspend block in order to have labeled
// suspend for breaking out of a suspend and for detecting if a suspend
// block is inside a suspend block.
struct ScopeSuspend {
Scope base;
IrBasicBlock *resume_block;
bool reported_err;
};
// This scope is created for a comptime expression.
// NodeTypeCompTime, NodeTypeSwitchExpr
struct ScopeCompTime {
@@ -1912,12 +2000,6 @@ struct IrBasicBlock {
IrInstruction *must_be_comptime_source_instr;
};
struct LVal {
bool is_ptr;
bool is_const;
bool is_volatile;
};
enum IrInstructionId {
IrInstructionIdInvalid,
IrInstructionIdBr,
@@ -1957,11 +2039,12 @@ enum IrInstructionId {
IrInstructionIdAsm,
IrInstructionIdSizeOf,
IrInstructionIdTestNonNull,
IrInstructionIdUnwrapMaybe,
IrInstructionIdMaybeWrap,
IrInstructionIdUnwrapOptional,
IrInstructionIdOptionalWrap,
IrInstructionIdUnionTag,
IrInstructionIdClz,
IrInstructionIdCtz,
IrInstructionIdPopCount,
IrInstructionIdImport,
IrInstructionIdCImport,
IrInstructionIdCInclude,
@@ -1978,6 +2061,11 @@ enum IrInstructionId {
IrInstructionIdCmpxchg,
IrInstructionIdFence,
IrInstructionIdTruncate,
IrInstructionIdIntCast,
IrInstructionIdFloatCast,
IrInstructionIdIntToFloat,
IrInstructionIdFloatToInt,
IrInstructionIdBoolToInt,
IrInstructionIdIntType,
IrInstructionIdBoolNot,
IrInstructionIdMemset,
@@ -1989,6 +2077,7 @@ enum IrInstructionId {
IrInstructionIdBreakpoint,
IrInstructionIdReturnAddress,
IrInstructionIdFrameAddress,
IrInstructionIdHandle,
IrInstructionIdAlignOf,
IrInstructionIdOverflowOp,
IrInstructionIdTestErr,
@@ -2004,21 +2093,22 @@ enum IrInstructionId {
IrInstructionIdIntToPtr,
IrInstructionIdPtrToInt,
IrInstructionIdIntToEnum,
IrInstructionIdEnumToInt,
IrInstructionIdIntToErr,
IrInstructionIdErrToInt,
IrInstructionIdCheckSwitchProngs,
IrInstructionIdCheckStatementIsVoid,
IrInstructionIdTypeName,
IrInstructionIdCanImplicitCast,
IrInstructionIdDeclRef,
IrInstructionIdPanic,
IrInstructionIdTagName,
IrInstructionIdTagType,
IrInstructionIdFieldParentPtr,
IrInstructionIdOffsetOf,
IrInstructionIdTypeInfo,
IrInstructionIdTypeId,
IrInstructionIdSetEvalBranchQuota,
IrInstructionIdPtrTypeOf,
IrInstructionIdPtrType,
IrInstructionIdAlignCast,
IrInstructionIdOpaqueType,
IrInstructionIdSetAlignStack,
@@ -2041,12 +2131,17 @@ enum IrInstructionId {
IrInstructionIdCoroPromise,
IrInstructionIdCoroAllocHelper,
IrInstructionIdAtomicRmw,
IrInstructionIdAtomicLoad,
IrInstructionIdPromiseResultType,
IrInstructionIdAwaitBookkeeping,
IrInstructionIdSaveErrRetAddr,
IrInstructionIdAddImplicitReturnType,
IrInstructionIdMergeErrRetTraces,
IrInstructionIdMarkErrRetTracePtr,
IrInstructionIdSqrt,
IrInstructionIdErrSetCast,
IrInstructionIdToBytes,
IrInstructionIdFromBytes,
};
struct IrInstruction {
@@ -2094,6 +2189,7 @@ struct IrInstructionSwitchBr {
size_t case_count;
IrInstructionSwitchBrCase *cases;
IrInstruction *is_comptime;
IrInstruction *switch_prongs_void;
};
struct IrInstructionSwitchVar {
@@ -2123,7 +2219,7 @@ enum IrUnOp {
IrUnOpNegation,
IrUnOpNegationWrap,
IrUnOpDereference,
IrUnOpMaybe,
IrUnOpOptional,
};
struct IrInstructionUnOp {
@@ -2203,7 +2299,8 @@ struct IrInstructionFieldPtr {
IrInstruction base;
IrInstruction *container_ptr;
Buf *field_name;
Buf *field_name_buffer;
IrInstruction *field_name_expr;
bool is_const;
};
@@ -2228,6 +2325,7 @@ struct IrInstructionElemPtr {
IrInstruction *array_ptr;
IrInstruction *elem_index;
PtrLen ptr_len;
bool is_const;
bool safety_check_on;
};
@@ -2236,8 +2334,6 @@ struct IrInstructionVarPtr {
IrInstruction base;
VariableTableEntry *var;
bool is_const;
bool is_volatile;
};
struct IrInstructionCall {
@@ -2253,6 +2349,7 @@ struct IrInstructionCall {
bool is_async;
IrInstruction *async_allocator;
IrInstruction *new_stack;
};
struct IrInstructionConst {
@@ -2373,6 +2470,18 @@ struct IrInstructionArrayType {
IrInstruction *child_type;
};
struct IrInstructionPtrType {
IrInstruction base;
IrInstruction *align_value;
IrInstruction *child_type;
uint32_t bit_offset_start;
uint32_t bit_offset_end;
PtrLen ptr_len;
bool is_const;
bool is_volatile;
};
struct IrInstructionPromiseType {
IrInstruction base;
@@ -2413,7 +2522,7 @@ struct IrInstructionTestNonNull {
IrInstruction *value;
};
struct IrInstructionUnwrapMaybe {
struct IrInstructionUnwrapOptional {
IrInstruction base;
IrInstruction *value;
@@ -2432,6 +2541,12 @@ struct IrInstructionClz {
IrInstruction *value;
};
struct IrInstructionPopCount {
IrInstruction base;
IrInstruction *value;
};
struct IrInstructionUnionTag {
IrInstruction base;
@@ -2522,6 +2637,7 @@ struct IrInstructionEmbedFile {
struct IrInstructionCmpxchg {
IrInstruction base;
IrInstruction *type_value;
IrInstruction *ptr;
IrInstruction *cmp_value;
IrInstruction *new_value;
@@ -2529,8 +2645,13 @@ struct IrInstructionCmpxchg {
IrInstruction *failure_order_value;
// if this instruction gets to runtime then we know these values:
TypeTableEntry *type;
AtomicOrder success_order;
AtomicOrder failure_order;
bool is_weak;
LLVMValueRef tmp_ptr;
};
struct IrInstructionFence {
@@ -2549,6 +2670,60 @@ struct IrInstructionTruncate {
IrInstruction *target;
};
struct IrInstructionIntCast {
IrInstruction base;
IrInstruction *dest_type;
IrInstruction *target;
};
struct IrInstructionFloatCast {
IrInstruction base;
IrInstruction *dest_type;
IrInstruction *target;
};
struct IrInstructionErrSetCast {
IrInstruction base;
IrInstruction *dest_type;
IrInstruction *target;
};
struct IrInstructionToBytes {
IrInstruction base;
IrInstruction *target;
};
struct IrInstructionFromBytes {
IrInstruction base;
IrInstruction *dest_child_type;
IrInstruction *target;
};
struct IrInstructionIntToFloat {
IrInstruction base;
IrInstruction *dest_type;
IrInstruction *target;
};
struct IrInstructionFloatToInt {
IrInstruction base;
IrInstruction *dest_type;
IrInstruction *target;
};
struct IrInstructionBoolToInt {
IrInstruction base;
IrInstruction *target;
};
struct IrInstructionIntType {
IrInstruction base;
@@ -2620,6 +2795,10 @@ struct IrInstructionFrameAddress {
IrInstruction base;
};
struct IrInstructionHandle {
IrInstruction base;
};
enum IrOverflowOp {
IrOverflowOpAdd,
IrOverflowOpSub,
@@ -2665,7 +2844,7 @@ struct IrInstructionUnwrapErrPayload {
bool safety_check_on;
};
struct IrInstructionMaybeWrap {
struct IrInstructionOptionalWrap {
IrInstruction base;
IrInstruction *value;
@@ -2739,6 +2918,13 @@ struct IrInstructionIntToPtr {
struct IrInstructionIntToEnum {
IrInstruction base;
IrInstruction *dest_type;
IrInstruction *target;
};
struct IrInstructionEnumToInt {
IrInstruction base;
IrInstruction *target;
};
@@ -2780,11 +2966,9 @@ struct IrInstructionTypeName {
IrInstruction *type_value;
};
struct IrInstructionCanImplicitCast {
IrInstruction base;
IrInstruction *type_value;
IrInstruction *target_value;
enum LVal {
LValNone,
LValPtr,
};
struct IrInstructionDeclRef {
@@ -2828,6 +3012,12 @@ struct IrInstructionOffsetOf {
IrInstruction *field_name;
};
struct IrInstructionTypeInfo {
IrInstruction base;
IrInstruction *type_value;
};
struct IrInstructionTypeId {
IrInstruction base;
@@ -2840,17 +3030,6 @@ struct IrInstructionSetEvalBranchQuota {
IrInstruction *new_quota;
};
struct IrInstructionPtrTypeOf {
IrInstruction base;
IrInstruction *align_value;
IrInstruction *child_type;
uint32_t bit_offset_start;
uint32_t bit_offset_end;
bool is_const;
bool is_volatile;
};
struct IrInstructionAlignCast {
IrInstruction base;
@@ -2886,10 +3065,10 @@ struct IrInstructionExport {
struct IrInstructionErrorReturnTrace {
IrInstruction base;
enum Nullable {
enum Optional {
Null,
NonNull,
} nullable;
} optional;
};
struct IrInstructionErrorUnion {
@@ -3000,6 +3179,15 @@ struct IrInstructionAtomicRmw {
AtomicOrder resolved_ordering;
};
struct IrInstructionAtomicLoad {
IrInstruction base;
IrInstruction *operand_type;
IrInstruction *ptr;
IrInstruction *ordering;
AtomicOrder resolved_ordering;
};
struct IrInstructionPromiseResultType {
IrInstruction base;
@@ -3036,6 +3224,13 @@ struct IrInstructionMarkErrRetTracePtr {
IrInstruction *err_ret_trace_ptr;
};
struct IrInstructionSqrt {
IrInstruction base;
IrInstruction *type;
IrInstruction *op;
};
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;
@@ -3054,7 +3249,7 @@ static const size_t stack_trace_ptr_count = 30;
#define RESULT_FIELD_NAME "result"
#define ASYNC_ALLOC_FIELD_NAME "allocFn"
#define ASYNC_FREE_FIELD_NAME "freeFn"
#define AWAITER_HANDLE_FIELD_NAME "awaiter_handle"
#define ATOMIC_STATE_FIELD_NAME "atomic_state"
// these point to data belonging to the awaiter
#define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr"
#define RESULT_PTR_FIELD_NAME "result_ptr"

File diff suppressed because it is too large Load Diff

View File

@@ -16,15 +16,14 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m
TypeTableEntry *new_type_table_entry(TypeTableEntryId id);
TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const);
TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const,
bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count);
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count);
uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry);
uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry);
TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_bits);
TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);
TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type);
TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id);
TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type);
TypeTableEntry *get_optional_type(CodeGen *g, TypeTableEntry *child_type);
TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size);
TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type);
TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind,
@@ -70,6 +69,8 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name);
TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag);
TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag);
bool is_ref(TypeTableEntry *type_entry);
bool is_array_ref(TypeTableEntry *type_entry);
bool is_container_ref(TypeTableEntry *type_entry);
void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
void scan_import(CodeGen *g, ImportTableEntry *import);
@@ -104,6 +105,7 @@ ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent);
Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var);
ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent);
ScopeLoop *create_loop_scope(AstNode *node, Scope *parent);
ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent);
ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry);
ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import);
Scope *create_comptime_scope(AstNode *node, Scope *parent);
@@ -151,8 +153,9 @@ ConstExprValue *create_const_ptr_hard_coded_addr(CodeGen *g, TypeTableEntry *poi
size_t addr, bool is_const);
void init_const_ptr_array(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val,
size_t elem_index, bool is_const);
ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index, bool is_const);
size_t elem_index, bool is_const, PtrLen ptr_len);
ConstExprValue *create_const_ptr_array(CodeGen *g, ConstExprValue *array_val, size_t elem_index,
bool is_const, PtrLen ptr_len);
void init_const_slice(CodeGen *g, ConstExprValue *const_val, ConstExprValue *array_val,
size_t start, size_t len, bool is_const);
@@ -173,7 +176,7 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value);
const char *type_id_name(TypeTableEntryId id);
TypeTableEntryId type_id_at_index(size_t index);
size_t type_id_len();
size_t type_id_index(TypeTableEntryId id);
size_t type_id_index(TypeTableEntry *entry);
TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id);
bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry);
LinkLib *create_link_lib(Buf *name);
@@ -190,7 +193,7 @@ void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, G
ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name);
TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g);
void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry);
bool resolve_inferred_error_set(CodeGen *g, TypeTableEntry *err_set_type, AstNode *source_node);
TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry);
@@ -198,5 +201,8 @@ uint32_t get_coro_frame_align_bytes(CodeGen *g);
bool fn_type_can_fail(FnTypeId *fn_type_id);
bool type_can_fail(TypeTableEntry *type_entry);
bool fn_eval_cacheable(Scope *scope, TypeTableEntry *return_type);
AstNode *type_decl_node(TypeTableEntry *type_entry);
TypeTableEntry *get_primitive_type(CodeGen *g, Buf *name);
#endif

View File

@@ -50,7 +50,7 @@ static const char *bin_op_str(BinOpType bin_op) {
case BinOpTypeAssignBitXor: return "^=";
case BinOpTypeAssignBitOr: return "|=";
case BinOpTypeAssignMergeErrorSets: return "||=";
case BinOpTypeUnwrapMaybe: return "??";
case BinOpTypeUnwrapOptional: return "orelse";
case BinOpTypeArrayCat: return "++";
case BinOpTypeArrayMult: return "**";
case BinOpTypeErrorUnion: return "!";
@@ -66,9 +66,8 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
case PrefixOpNegationWrap: return "-%";
case PrefixOpBoolNot: return "!";
case PrefixOpBinNot: return "~";
case PrefixOpDereference: return "*";
case PrefixOpMaybe: return "?";
case PrefixOpUnwrapMaybe: return "??";
case PrefixOpOptional: return "?";
case PrefixOpAddrOf: return "&";
}
zig_unreachable();
}
@@ -186,8 +185,6 @@ static const char *node_type_str(NodeType node_type) {
return "Symbol";
case NodeTypePrefixOpExpr:
return "PrefixOpExpr";
case NodeTypeAddrOfExpr:
return "AddrOfExpr";
case NodeTypeUse:
return "Use";
case NodeTypeBoolLiteral:
@@ -222,6 +219,10 @@ static const char *node_type_str(NodeType node_type) {
return "AsmExpr";
case NodeTypeFieldAccessExpr:
return "FieldAccessExpr";
case NodeTypePtrDeref:
return "PtrDerefExpr";
case NodeTypeUnwrapOptional:
return "UnwrapOptional";
case NodeTypeContainerDecl:
return "ContainerDecl";
case NodeTypeStructField:
@@ -250,6 +251,8 @@ static const char *node_type_str(NodeType node_type) {
return "Suspend";
case NodeTypePromiseType:
return "PromiseType";
case NodeTypePointerType:
return "PointerType";
}
zig_unreachable();
}
@@ -615,41 +618,47 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
fprintf(ar->f, "%s", prefix_op_str(op));
AstNode *child_node = node->data.prefix_op_expr.primary_expr;
bool new_grouped = child_node->type == NodeTypePrefixOpExpr || child_node->type == NodeTypeAddrOfExpr;
bool new_grouped = child_node->type == NodeTypePrefixOpExpr || child_node->type == NodeTypePointerType;
render_node_extra(ar, child_node, new_grouped);
if (!grouped) fprintf(ar->f, ")");
break;
}
case NodeTypeAddrOfExpr:
case NodeTypePointerType:
{
if (!grouped) fprintf(ar->f, "(");
fprintf(ar->f, "&");
if (node->data.addr_of_expr.align_expr != nullptr) {
const char *star = "[*]";
if (node->data.pointer_type.star_token != nullptr &&
(node->data.pointer_type.star_token->id == TokenIdStar || node->data.pointer_type.star_token->id == TokenIdStarStar))
{
star = "*";
}
fprintf(ar->f, "%s", star);
if (node->data.pointer_type.align_expr != nullptr) {
fprintf(ar->f, "align(");
render_node_grouped(ar, node->data.addr_of_expr.align_expr);
if (node->data.addr_of_expr.bit_offset_start != nullptr) {
assert(node->data.addr_of_expr.bit_offset_end != nullptr);
render_node_grouped(ar, node->data.pointer_type.align_expr);
if (node->data.pointer_type.bit_offset_start != nullptr) {
assert(node->data.pointer_type.bit_offset_end != nullptr);
Buf offset_start_buf = BUF_INIT;
buf_resize(&offset_start_buf, 0);
bigint_append_buf(&offset_start_buf, node->data.addr_of_expr.bit_offset_start, 10);
bigint_append_buf(&offset_start_buf, node->data.pointer_type.bit_offset_start, 10);
Buf offset_end_buf = BUF_INIT;
buf_resize(&offset_end_buf, 0);
bigint_append_buf(&offset_end_buf, node->data.addr_of_expr.bit_offset_end, 10);
bigint_append_buf(&offset_end_buf, node->data.pointer_type.bit_offset_end, 10);
fprintf(ar->f, ":%s:%s ", buf_ptr(&offset_start_buf), buf_ptr(&offset_end_buf));
}
fprintf(ar->f, ") ");
}
if (node->data.addr_of_expr.is_const) {
if (node->data.pointer_type.is_const) {
fprintf(ar->f, "const ");
}
if (node->data.addr_of_expr.is_volatile) {
if (node->data.pointer_type.is_volatile) {
fprintf(ar->f, "volatile ");
}
render_node_ungrouped(ar, node->data.addr_of_expr.op_expr);
render_node_ungrouped(ar, node->data.pointer_type.op_expr);
if (!grouped) fprintf(ar->f, ")");
break;
}
@@ -668,7 +677,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
fprintf(ar->f, " ");
}
AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypeAddrOfExpr);
bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypePointerType);
render_node_extra(ar, fn_ref_node, grouped);
fprintf(ar->f, "(");
for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
@@ -696,6 +705,20 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
print_symbol(ar, rhs);
break;
}
case NodeTypePtrDeref:
{
AstNode *lhs = node->data.ptr_deref_expr.target;
render_node_ungrouped(ar, lhs);
fprintf(ar->f, ".*");
break;
}
case NodeTypeUnwrapOptional:
{
AstNode *lhs = node->data.unwrap_optional.expr;
render_node_ungrouped(ar, lhs);
fprintf(ar->f, ".?");
break;
}
case NodeTypeUndefinedLiteral:
fprintf(ar->f, "undefined");
break;
@@ -728,7 +751,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
render_node_grouped(ar, field_node->data.struct_field.type);
}
if (field_node->data.struct_field.value != nullptr) {
fprintf(ar->f, "= ");
fprintf(ar->f, " = ");
render_node_grouped(ar, field_node->data.struct_field.value);
}
fprintf(ar->f, ",\n");
@@ -1089,9 +1112,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
{
fprintf(ar->f, "suspend");
if (node->data.suspend.block != nullptr) {
fprintf(ar->f, " |");
render_node_grouped(ar, node->data.suspend.promise_symbol);
fprintf(ar->f, "| ");
render_node_grouped(ar, node->data.suspend.block);
}
break;

View File

@@ -18,6 +18,10 @@ void bigfloat_init_128(BigFloat *dest, float128_t x) {
dest->value = x;
}
void bigfloat_init_16(BigFloat *dest, float16_t x) {
f16_to_f128M(x, &dest->value);
}
void bigfloat_init_32(BigFloat *dest, float x) {
float32_t f32_val;
memcpy(&f32_val, &x, sizeof(float));
@@ -146,6 +150,10 @@ Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2) {
}
}
float16_t bigfloat_to_f16(const BigFloat *bigfloat) {
return f128M_to_f16(&bigfloat->value);
}
float bigfloat_to_f32(const BigFloat *bigfloat) {
float32_t f32_value = f128M_to_f32(&bigfloat->value);
float result;
@@ -181,3 +189,7 @@ bool bigfloat_has_fraction(const BigFloat *bigfloat) {
f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored);
return !f128M_eq(&floored, &bigfloat->value);
}
void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
f128M_sqrt(&op->value, &dest->value);
}

View File

@@ -22,6 +22,7 @@ struct BigFloat {
struct Buf;
void bigfloat_init_16(BigFloat *dest, float16_t x);
void bigfloat_init_32(BigFloat *dest, float x);
void bigfloat_init_64(BigFloat *dest, double x);
void bigfloat_init_128(BigFloat *dest, float128_t x);
@@ -29,6 +30,7 @@ void bigfloat_init_bigfloat(BigFloat *dest, const BigFloat *x);
void bigfloat_init_bigint(BigFloat *dest, const BigInt *op);
int bigfloat_init_buf_base10(BigFloat *dest, const uint8_t *buf_ptr, size_t buf_len);
float16_t bigfloat_to_f16(const BigFloat *bigfloat);
float bigfloat_to_f32(const BigFloat *bigfloat);
double bigfloat_to_f64(const BigFloat *bigfloat);
float128_t bigfloat_to_f128(const BigFloat *bigfloat);
@@ -42,6 +44,7 @@ void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2
void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);
void bigfloat_append_buf(Buf *buf, const BigFloat *op);
Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);

View File

@@ -86,6 +86,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)
size_t digits_to_copy = bit_count / 64;
size_t leftover_bits = bit_count % 64;
dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1);
if (dest->digit_count == 1 && leftover_bits == 0) {
dest->data.digit = op_digits[0];
if (dest->data.digit == 0) dest->digit_count = 0;
return;
}
dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
for (size_t i = 0; i < digits_to_copy; i += 1) {
uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0;
@@ -1254,12 +1259,11 @@ void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) {
bigint_normalize(dest);
return;
}
// TODO this code path is untested
uint64_t first_digit = dest->data.digit;
dest->digit_count = max(op1->digit_count, op2->digit_count);
dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
dest->data.digits[0] = first_digit;
size_t i = 1;
size_t i = 0;
for (; i < op1->digit_count && i < op2->digit_count; i += 1) {
dest->data.digits[i] = op1_digits[i] & op2_digits[i];
}
@@ -1407,7 +1411,6 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {
return;
}
// TODO this code path is untested
size_t digit_shift_count = shift_amt / 64;
size_t leftover_shift_count = shift_amt % 64;
@@ -1422,7 +1425,7 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {
uint64_t digit = op1_digits[op_digit_index];
size_t dest_digit_index = op_digit_index - digit_shift_count;
dest->data.digits[dest_digit_index] = carry | (digit >> leftover_shift_count);
carry = (0xffffffffffffffffULL << leftover_shift_count) & digit;
carry = digit << (64 - leftover_shift_count);
if (dest_digit_index == 0) { break; }
op_digit_index -= 1;
@@ -1590,6 +1593,37 @@ void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base) {
}
}
size_t bigint_popcount_unsigned(const BigInt *bi) {
assert(!bi->is_negative);
if (bi->digit_count == 0)
return 0;
size_t count = 0;
size_t bit_count = bi->digit_count * 64;
for (size_t i = 0; i < bit_count; i += 1) {
if (bit_at_index(bi, i))
count += 1;
}
return count;
}
size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) {
if (bit_count == 0)
return 0;
if (bi->digit_count == 0)
return 0;
BigInt twos_comp = {0};
to_twos_complement(&twos_comp, bi, bit_count);
size_t count = 0;
for (size_t i = 0; i < bit_count; i += 1) {
if (bit_at_index(&twos_comp, i))
count += 1;
}
return count;
}
size_t bigint_ctz(const BigInt *bi, size_t bit_count) {
if (bit_count == 0)
return 0;
@@ -1680,10 +1714,15 @@ void bigint_incr(BigInt *x) {
bigint_init_unsigned(x, 1);
return;
}
if (x->digit_count == 1 && x->data.digit != UINT64_MAX) {
x->data.digit += 1;
return;
if (x->digit_count == 1) {
if (x->is_negative && x->data.digit != 0) {
x->data.digit -= 1;
return;
} else if (!x->is_negative && x->data.digit != UINT64_MAX) {
x->data.digit += 1;
return;
}
}
BigInt copy;

View File

@@ -81,6 +81,8 @@ void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base);
size_t bigint_ctz(const BigInt *bi, size_t bit_count);
size_t bigint_clz(const BigInt *bi, size_t bit_count);
size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count);
size_t bigint_popcount_unsigned(const BigInt *bi);
size_t bigint_bits_needed(const BigInt *op);

File diff suppressed because it is too large Load Diff

View File

@@ -59,5 +59,7 @@ void codegen_add_object(CodeGen *g, Buf *object_path);
void codegen_translate_c(CodeGen *g, Buf *path);
Buf *codegen_generate_builtin_source(CodeGen *g);
#endif

6215
src/ir.cpp

File diff suppressed because it is too large Load Diff

View File

@@ -45,6 +45,10 @@ static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) {
}
static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) {
if (instruction == nullptr) {
fprintf(irp->f, "(null)");
return;
}
if (instruction->value.special != ConstValSpecialRuntime) {
ir_print_const_value(irp, &instruction->value);
} else {
@@ -148,7 +152,7 @@ static const char *ir_un_op_id_str(IrUnOp op_id) {
return "-%";
case IrUnOpDereference:
return "*";
case IrUnOpMaybe:
case IrUnOpOptional:
return "?";
}
zig_unreachable();
@@ -358,9 +362,18 @@ static void ir_print_ptr_type_child(IrPrint *irp, IrInstructionPtrTypeChild *ins
}
static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) {
fprintf(irp->f, "fieldptr ");
ir_print_other_instruction(irp, instruction->container_ptr);
fprintf(irp->f, ".%s", buf_ptr(instruction->field_name));
if (instruction->field_name_buffer) {
fprintf(irp->f, "fieldptr ");
ir_print_other_instruction(irp, instruction->container_ptr);
fprintf(irp->f, ".%s", buf_ptr(instruction->field_name_buffer));
} else {
assert(instruction->field_name_expr);
fprintf(irp->f, "@field(");
ir_print_other_instruction(irp, instruction->container_ptr);
fprintf(irp->f, ", ");
ir_print_other_instruction(irp, instruction->field_name_expr);
fprintf(irp->f, ")");
}
}
static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) {
@@ -472,7 +485,7 @@ static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instructi
fprintf(irp->f, " != null");
}
static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapMaybe *instruction) {
static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapOptional *instruction) {
fprintf(irp->f, "&??*");
ir_print_other_instruction(irp, instruction->value);
if (!instruction->safety_check_on) {
@@ -492,6 +505,12 @@ static void ir_print_ctz(IrPrint *irp, IrInstructionCtz *instruction) {
fprintf(irp->f, ")");
}
static void ir_print_pop_count(IrPrint *irp, IrInstructionPopCount *instruction) {
fprintf(irp->f, "@popCount(");
ir_print_other_instruction(irp, instruction->value);
fprintf(irp->f, ")");
}
static void ir_print_switch_br(IrPrint *irp, IrInstructionSwitchBr *instruction) {
fprintf(irp->f, "switch (");
ir_print_other_instruction(irp, instruction->target_value);
@@ -639,6 +658,66 @@ static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction)
fprintf(irp->f, ")");
}
static void ir_print_int_cast(IrPrint *irp, IrInstructionIntCast *instruction) {
fprintf(irp->f, "@intCast(");
ir_print_other_instruction(irp, instruction->dest_type);
fprintf(irp->f, ", ");
ir_print_other_instruction(irp, instruction->target);
fprintf(irp->f, ")");
}
static void ir_print_float_cast(IrPrint *irp, IrInstructionFloatCast *instruction) {
fprintf(irp->f, "@floatCast(");
ir_print_other_instruction(irp, instruction->dest_type);
fprintf(irp->f, ", ");
ir_print_other_instruction(irp, instruction->target);
fprintf(irp->f, ")");
}
static void ir_print_err_set_cast(IrPrint *irp, IrInstructionErrSetCast *instruction) {
fprintf(irp->f, "@errSetCast(");
ir_print_other_instruction(irp, instruction->dest_type);
fprintf(irp->f, ", ");
ir_print_other_instruction(irp, instruction->target);
fprintf(irp->f, ")");
}
static void ir_print_from_bytes(IrPrint *irp, IrInstructionFromBytes *instruction) {
fprintf(irp->f, "@bytesToSlice(");
ir_print_other_instruction(irp, instruction->dest_child_type);
fprintf(irp->f, ", ");
ir_print_other_instruction(irp, instruction->target);
fprintf(irp->f, ")");
}
static void ir_print_to_bytes(IrPrint *irp, IrInstructionToBytes *instruction) {
fprintf(irp->f, "@sliceToBytes(");
ir_print_other_instruction(irp, instruction->target);
fprintf(irp->f, ")");
}
static void ir_print_int_to_float(IrPrint *irp, IrInstructionIntToFloat *instruction) {
fprintf(irp->f, "@intToFloat(");
ir_print_other_instruction(irp, instruction->dest_type);
fprintf(irp->f, ", ");
ir_print_other_instruction(irp, instruction->target);
fprintf(irp->f, ")");
}
static void ir_print_float_to_int(IrPrint *irp, IrInstructionFloatToInt *instruction) {
fprintf(irp->f, "@floatToInt(");
ir_print_other_instruction(irp, instruction->dest_type);
fprintf(irp->f, ", ");
ir_print_other_instruction(irp, instruction->target);
fprintf(irp->f, ")");
}
static void ir_print_bool_to_int(IrPrint *irp, IrInstructionBoolToInt *instruction) {
fprintf(irp->f, "@boolToInt(");
ir_print_other_instruction(irp, instruction->target);
fprintf(irp->f, ")");
}
static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) {
fprintf(irp->f, "@IntType(");
ir_print_other_instruction(irp, instruction->is_signed);
@@ -712,6 +791,10 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst
fprintf(irp->f, "@frameAddress()");
}
static void ir_print_handle(IrPrint *irp, IrInstructionHandle *instruction) {
fprintf(irp->f, "@handle()");
}
static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) {
fprintf(irp->f, "@returnAddress()");
}
@@ -768,7 +851,7 @@ static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayl
}
}
static void ir_print_maybe_wrap(IrPrint *irp, IrInstructionMaybeWrap *instruction) {
static void ir_print_maybe_wrap(IrPrint *irp, IrInstructionOptionalWrap *instruction) {
fprintf(irp->f, "@maybeWrap(");
ir_print_other_instruction(irp, instruction->value);
fprintf(irp->f, ")");
@@ -859,6 +942,17 @@ static void ir_print_int_to_ptr(IrPrint *irp, IrInstructionIntToPtr *instruction
static void ir_print_int_to_enum(IrPrint *irp, IrInstructionIntToEnum *instruction) {
fprintf(irp->f, "@intToEnum(");
if (instruction->dest_type == nullptr) {
fprintf(irp->f, "(null)");
} else {
ir_print_other_instruction(irp, instruction->dest_type);
}
ir_print_other_instruction(irp, instruction->target);
fprintf(irp->f, ")");
}
static void ir_print_enum_to_int(IrPrint *irp, IrInstructionEnumToInt *instruction) {
fprintf(irp->f, "@enumToInt(");
ir_print_other_instruction(irp, instruction->target);
fprintf(irp->f, ")");
}
@@ -904,15 +998,7 @@ static void ir_print_tag_name(IrPrint *irp, IrInstructionTagName *instruction) {
ir_print_other_instruction(irp, instruction->target);
}
static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCast *instruction) {
fprintf(irp->f, "@canImplicitCast(");
ir_print_other_instruction(irp, instruction->type_value);
fprintf(irp->f, ",");
ir_print_other_instruction(irp, instruction->target_value);
fprintf(irp->f, ")");
}
static void ir_print_ptr_type_of(IrPrint *irp, IrInstructionPtrTypeOf *instruction) {
static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) {
fprintf(irp->f, "&");
if (instruction->align_value != nullptr) {
fprintf(irp->f, "align(");
@@ -927,10 +1013,8 @@ static void ir_print_ptr_type_of(IrPrint *irp, IrInstructionPtrTypeOf *instructi
}
static void ir_print_decl_ref(IrPrint *irp, IrInstructionDeclRef *instruction) {
const char *ptr_str = instruction->lval.is_ptr ? "ptr " : "";
const char *const_str = instruction->lval.is_const ? "const " : "";
const char *volatile_str = instruction->lval.is_volatile ? "volatile " : "";
fprintf(irp->f, "declref %s%s%s%s", const_str, volatile_str, ptr_str, buf_ptr(instruction->tld->name));
const char *ptr_str = (instruction->lval == LValPtr) ? "ptr " : "";
fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name));
}
static void ir_print_panic(IrPrint *irp, IrInstructionPanic *instruction) {
@@ -957,6 +1041,12 @@ static void ir_print_offset_of(IrPrint *irp, IrInstructionOffsetOf *instruction)
fprintf(irp->f, ")");
}
static void ir_print_type_info(IrPrint *irp, IrInstructionTypeInfo *instruction) {
fprintf(irp->f, "@typeInfo(");
ir_print_other_instruction(irp, instruction->type_value);
fprintf(irp->f, ")");
}
static void ir_print_type_id(IrPrint *irp, IrInstructionTypeId *instruction) {
fprintf(irp->f, "@typeId(");
ir_print_other_instruction(irp, instruction->type_value);
@@ -1025,7 +1115,7 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {
static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) {
fprintf(irp->f, "@errorReturnTrace(");
switch (instruction->nullable) {
switch (instruction->optional) {
case IrInstructionErrorReturnTrace::Null:
fprintf(irp->f, "Null");
break;
@@ -1172,6 +1262,24 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio
fprintf(irp->f, ")");
}
static void ir_print_atomic_load(IrPrint *irp, IrInstructionAtomicLoad *instruction) {
fprintf(irp->f, "@atomicLoad(");
if (instruction->operand_type != nullptr) {
ir_print_other_instruction(irp, instruction->operand_type);
} else {
fprintf(irp->f, "[TODO print]");
}
fprintf(irp->f, ",");
ir_print_other_instruction(irp, instruction->ptr);
fprintf(irp->f, ",");
if (instruction->ordering != nullptr) {
ir_print_other_instruction(irp, instruction->ordering);
} else {
fprintf(irp->f, "[TODO print]");
}
fprintf(irp->f, ")");
}
static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeeping *instruction) {
fprintf(irp->f, "@awaitBookkeeping(");
ir_print_other_instruction(irp, instruction->promise_result_type);
@@ -1204,6 +1312,18 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe
fprintf(irp->f, ")");
}
static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) {
fprintf(irp->f, "@sqrt(");
if (instruction->type != nullptr) {
ir_print_other_instruction(irp, instruction->type);
} else {
fprintf(irp->f, "null");
}
fprintf(irp->f, ",");
ir_print_other_instruction(irp, instruction->op);
fprintf(irp->f, ")");
}
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
switch (instruction->id) {
@@ -1311,12 +1431,15 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdTestNonNull:
ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction);
break;
case IrInstructionIdUnwrapMaybe:
ir_print_unwrap_maybe(irp, (IrInstructionUnwrapMaybe *)instruction);
case IrInstructionIdUnwrapOptional:
ir_print_unwrap_maybe(irp, (IrInstructionUnwrapOptional *)instruction);
break;
case IrInstructionIdCtz:
ir_print_ctz(irp, (IrInstructionCtz *)instruction);
break;
case IrInstructionIdPopCount:
ir_print_pop_count(irp, (IrInstructionPopCount *)instruction);
break;
case IrInstructionIdClz:
ir_print_clz(irp, (IrInstructionClz *)instruction);
break;
@@ -1380,6 +1503,30 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdTruncate:
ir_print_truncate(irp, (IrInstructionTruncate *)instruction);
break;
case IrInstructionIdIntCast:
ir_print_int_cast(irp, (IrInstructionIntCast *)instruction);
break;
case IrInstructionIdFloatCast:
ir_print_float_cast(irp, (IrInstructionFloatCast *)instruction);
break;
case IrInstructionIdErrSetCast:
ir_print_err_set_cast(irp, (IrInstructionErrSetCast *)instruction);
break;
case IrInstructionIdFromBytes:
ir_print_from_bytes(irp, (IrInstructionFromBytes *)instruction);
break;
case IrInstructionIdToBytes:
ir_print_to_bytes(irp, (IrInstructionToBytes *)instruction);
break;
case IrInstructionIdIntToFloat:
ir_print_int_to_float(irp, (IrInstructionIntToFloat *)instruction);
break;
case IrInstructionIdFloatToInt:
ir_print_float_to_int(irp, (IrInstructionFloatToInt *)instruction);
break;
case IrInstructionIdBoolToInt:
ir_print_bool_to_int(irp, (IrInstructionBoolToInt *)instruction);
break;
case IrInstructionIdIntType:
ir_print_int_type(irp, (IrInstructionIntType *)instruction);
break;
@@ -1413,6 +1560,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdFrameAddress:
ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction);
break;
case IrInstructionIdHandle:
ir_print_handle(irp, (IrInstructionHandle *)instruction);
break;
case IrInstructionIdAlignOf:
ir_print_align_of(irp, (IrInstructionAlignOf *)instruction);
break;
@@ -1428,8 +1578,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdUnwrapErrPayload:
ir_print_unwrap_err_payload(irp, (IrInstructionUnwrapErrPayload *)instruction);
break;
case IrInstructionIdMaybeWrap:
ir_print_maybe_wrap(irp, (IrInstructionMaybeWrap *)instruction);
case IrInstructionIdOptionalWrap:
ir_print_maybe_wrap(irp, (IrInstructionOptionalWrap *)instruction);
break;
case IrInstructionIdErrWrapCode:
ir_print_err_wrap_code(irp, (IrInstructionErrWrapCode *)instruction);
@@ -1479,11 +1629,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdTagName:
ir_print_tag_name(irp, (IrInstructionTagName *)instruction);
break;
case IrInstructionIdCanImplicitCast:
ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction);
break;
case IrInstructionIdPtrTypeOf:
ir_print_ptr_type_of(irp, (IrInstructionPtrTypeOf *)instruction);
case IrInstructionIdPtrType:
ir_print_ptr_type(irp, (IrInstructionPtrType *)instruction);
break;
case IrInstructionIdDeclRef:
ir_print_decl_ref(irp, (IrInstructionDeclRef *)instruction);
@@ -1497,6 +1644,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdOffsetOf:
ir_print_offset_of(irp, (IrInstructionOffsetOf *)instruction);
break;
case IrInstructionIdTypeInfo:
ir_print_type_info(irp, (IrInstructionTypeInfo *)instruction);
break;
case IrInstructionIdTypeId:
ir_print_type_id(irp, (IrInstructionTypeId *)instruction);
break;
@@ -1590,6 +1740,15 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdMarkErrRetTracePtr:
ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);
break;
case IrInstructionIdSqrt:
ir_print_sqrt(irp, (IrInstructionSqrt *)instruction);
break;
case IrInstructionIdAtomicLoad:
ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction);
break;
case IrInstructionIdEnumToInt:
ir_print_enum_to_int(irp, (IrInstructionEnumToInt *)instruction);
break;
}
fprintf(irp->f, "\n");
}

View File

@@ -208,7 +208,7 @@ static Buf *get_dynamic_linker_path(CodeGen *g) {
static void construct_linker_job_elf(LinkJob *lj) {
CodeGen *g = lj->codegen;
if (lj->link_in_crt) {
if (g->libc_link_lib != nullptr) {
find_libc_lib_path(g);
}
@@ -217,6 +217,9 @@ static void construct_linker_job_elf(LinkJob *lj) {
lj->args.append(g->linker_script);
}
if (g->no_rosegment_workaround) {
lj->args.append("--no-rosegment");
}
lj->args.append("--gc-sections");
lj->args.append("-m");
@@ -322,10 +325,13 @@ static void construct_linker_job_elf(LinkJob *lj) {
lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
}
if (g->libc_link_lib == nullptr && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) {
Buf *builtin_o_path = build_o(g, "builtin");
lj->args.append(buf_ptr(builtin_o_path));
if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
if (g->libc_link_lib == nullptr) {
Buf *builtin_o_path = build_o(g, "builtin");
lj->args.append(buf_ptr(builtin_o_path));
}
// sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage
Buf *compiler_rt_o_path = build_compiler_rt(g);
lj->args.append(buf_ptr(compiler_rt_o_path));
}
@@ -388,6 +394,19 @@ static void construct_linker_job_elf(LinkJob *lj) {
}
}
static void construct_linker_job_wasm(LinkJob *lj) {
CodeGen *g = lj->codegen;
lj->args.append("--relocatable"); // So lld doesn't look for _start.
lj->args.append("-o");
lj->args.append(buf_ptr(&lj->out_file));
// .o files
for (size_t i = 0; i < g->link_objects.length; i += 1) {
lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
}
}
//static bool is_target_cyg_mingw(const ZigTarget *target) {
// return (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_Cygnus) ||
// (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU);
@@ -416,7 +435,7 @@ static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, si
static void construct_linker_job_coff(LinkJob *lj) {
CodeGen *g = lj->codegen;
if (lj->link_in_crt) {
if (g->libc_link_lib != nullptr) {
find_libc_lib_path(g);
}
@@ -538,7 +557,7 @@ static void construct_linker_job_coff(LinkJob *lj) {
lj->args.append(buf_ptr(builtin_o_path));
}
// msvc compiler_rt is missing some stuff, so we still build it and rely on LinkOnce
// msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage
Buf *compiler_rt_o_path = build_compiler_rt(g);
lj->args.append(buf_ptr(compiler_rt_o_path));
}
@@ -882,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) {
if (strchr(buf_ptr(link_lib->name), '/') == nullptr) {
Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
lj->args.append(buf_ptr(arg));
} else {
} else {
lj->args.append(buf_ptr(link_lib->name));
}
}
@@ -921,7 +940,7 @@ static void construct_linker_job(LinkJob *lj) {
case ZigLLVM_MachO:
return construct_linker_job_macho(lj);
case ZigLLVM_Wasm:
zig_panic("TODO link wasm");
return construct_linker_job_wasm(lj);
}
}

View File

@@ -23,6 +23,7 @@ static int usage(const char *arg0) {
" build-exe [source] create executable from source or object files\n"
" build-lib [source] create library from source or object files\n"
" build-obj [source] create object from source or assembly\n"
" builtin show the source code of that @import(\"builtin\")\n"
" run [source] create executable and run immediately\n"
" translate-c [source] convert c code to zig code\n"
" targets list available compilation targets\n"
@@ -33,7 +34,7 @@ static int usage(const char *arg0) {
" --assembly [source] add assembly file to build\n"
" --cache-dir [path] override the cache directory\n"
" --color [auto|off|on] enable or disable colored error messages\n"
" --emit [filetype] emit a specific file format as compilation output\n"
" --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n"
" --enable-timing-info print timing diagnostics\n"
" --libc-include-dir [path] directory where libc stdlib.h resides\n"
" --name [name] override output name\n"
@@ -43,6 +44,7 @@ static int usage(const char *arg0) {
" --pkg-end pop current pkg\n"
" --release-fast build with optimizations on and safety off\n"
" --release-safe build with optimizations on and safety on\n"
" --release-small build with size optimizations on and safety off\n"
" --static output will be statically linked\n"
" --strip exclude debug symbols\n"
" --target-arch [name] specify target architecture\n"
@@ -73,6 +75,7 @@ static int usage(const char *arg0) {
" -L[dir] alias for --library-path\n"
" -rdynamic add all symbols to the dynamic symbol table\n"
" -rpath [path] add directory to the runtime library search path\n"
" --no-rosegment compromise security to workaround valgrind bug\n"
" -mconsole (windows) --subsystem console to the linker\n"
" -mwindows (windows) --subsystem windows to the linker\n"
" -framework [name] (darwin) link against framework\n"
@@ -212,6 +215,7 @@ static Buf *resolve_zig_lib_dir(void) {
enum Cmd {
CmdInvalid,
CmdBuild,
CmdBuiltin,
CmdRun,
CmdTest,
CmdVersion,
@@ -323,6 +327,7 @@ int main(int argc, char **argv) {
ZigList<const char *> test_exec_args = {0};
int comptime_args_end = 0;
int runtime_args_start = argc;
bool no_rosegment_workaround = false;
if (argc >= 2 && strcmp(argv[1], "build") == 0) {
const char *zig_exe_path = arg0;
@@ -482,6 +487,8 @@ int main(int argc, char **argv) {
build_mode = BuildModeFastRelease;
} else if (strcmp(arg, "--release-safe") == 0) {
build_mode = BuildModeSafeRelease;
} else if (strcmp(arg, "--release-small") == 0) {
build_mode = BuildModeSmallRelease;
} else if (strcmp(arg, "--strip") == 0) {
strip = true;
} else if (strcmp(arg, "--static") == 0) {
@@ -504,6 +511,8 @@ int main(int argc, char **argv) {
mconsole = true;
} else if (strcmp(arg, "-rdynamic") == 0) {
rdynamic = true;
} else if (strcmp(arg, "--no-rosegment") == 0) {
no_rosegment_workaround = true;
} else if (strcmp(arg, "--each-lib-rpath") == 0) {
each_lib_rpath = true;
} else if (strcmp(arg, "--enable-timing-info") == 0) {
@@ -657,6 +666,8 @@ int main(int argc, char **argv) {
out_type = OutTypeExe;
} else if (strcmp(arg, "targets") == 0) {
cmd = CmdTargets;
} else if (strcmp(arg, "builtin") == 0) {
cmd = CmdBuiltin;
} else {
fprintf(stderr, "Unrecognized command: %s\n", arg);
return usage(arg0);
@@ -674,6 +685,7 @@ int main(int argc, char **argv) {
return usage(arg0);
}
break;
case CmdBuiltin:
case CmdVersion:
case CmdZen:
case CmdTargets:
@@ -720,6 +732,16 @@ int main(int argc, char **argv) {
}
switch (cmd) {
case CmdBuiltin: {
Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, zig_lib_dir_buf);
Buf *builtin_source = codegen_generate_builtin_source(g);
if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
case CmdRun:
case CmdBuild:
case CmdTranslateC:
@@ -841,6 +863,7 @@ int main(int argc, char **argv) {
codegen_set_windows_subsystem(g, mwindows, mconsole);
codegen_set_rdynamic(g, rdynamic);
g->no_rosegment_workaround = no_rosegment_workaround;
if (mmacosx_version_min && mios_version_min) {
fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n");
return EXIT_FAILURE;
@@ -868,15 +891,19 @@ int main(int argc, char **argv) {
add_package(g, cur_pkg, g->root_package);
if (cmd == CmdBuild || cmd == CmdRun) {
codegen_set_emit_file_type(g, emit_file_type);
if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) {
for (size_t i = 0; i < objects.length; i += 1) {
codegen_add_object(g, buf_create_from_str(objects.at(i)));
}
for (size_t i = 0; i < asm_files.length; i += 1) {
codegen_add_assembly(g, buf_create_from_str(asm_files.at(i)));
}
}
if (cmd == CmdBuild || cmd == CmdRun) {
codegen_set_emit_file_type(g, emit_file_type);
codegen_build(g);
codegen_link(g, out_file);
if (timing_info)
@@ -901,6 +928,8 @@ int main(int argc, char **argv) {
codegen_print_timing_report(g, stdout);
return EXIT_SUCCESS;
} else if (cmd == CmdTest) {
codegen_set_emit_file_type(g, emit_file_type);
ZigTarget native;
get_native_target(&native);

View File

@@ -26,7 +26,6 @@
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include "windows_com.hpp"
typedef SSIZE_T ssize_t;
#else
@@ -225,6 +224,11 @@ 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) {
if (buf_len(dirname) == 0) {
buf_init_from_buf(out_full_path, basename);
return;
}
buf_init_from_buf(out_full_path, dirname);
uint8_t c = *(buf_ptr(out_full_path) + buf_len(out_full_path) - 1);
if (!os_is_sep(c))
@@ -989,12 +993,29 @@ int os_self_exe_path(Buf *out_path) {
}
#elif defined(ZIG_OS_DARWIN)
// How long is the executable's path?
uint32_t u32_len = 0;
int ret1 = _NSGetExecutablePath(nullptr, &u32_len);
assert(ret1 != 0);
buf_resize(out_path, u32_len);
int ret2 = _NSGetExecutablePath(buf_ptr(out_path), &u32_len);
Buf *tmp = buf_alloc_fixed(u32_len);
// Fill the executable path.
int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len);
assert(ret2 == 0);
// According to libuv project, PATH_MAX*2 works around a libc bug where
// the resolved path is sometimes bigger than PATH_MAX.
buf_resize(out_path, PATH_MAX*2);
char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path));
if (!real_path) {
buf_init_from_buf(out_path, tmp);
return 0;
}
// Resize out_path for the correct length.
buf_resize(out_path, strlen(buf_ptr(out_path)));
return 0;
#elif defined(ZIG_OS_LINUX)
buf_resize(out_path, 256);
@@ -1007,6 +1028,7 @@ int os_self_exe_path(Buf *out_path) {
buf_resize(out_path, buf_len(out_path) * 2);
continue;
}
buf_resize(out_path, amt);
return 0;
}
#endif
@@ -1092,249 +1114,10 @@ void os_stderr_set_color(TermColor color) {
#endif
}
int os_find_windows_sdk(ZigWindowsSDK **out_sdk) {
#if defined(ZIG_OS_WINDOWS)
ZigWindowsSDK *result_sdk = allocate<ZigWindowsSDK>(1);
buf_resize(&result_sdk->path10, 0);
buf_resize(&result_sdk->path81, 0);
HKEY key;
HRESULT rc;
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key);
if (rc != ERROR_SUCCESS) {
return ErrorFileNotFound;
}
{
DWORD tmp_buf_len = MAX_PATH;
buf_resize(&result_sdk->path10, tmp_buf_len);
rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path10), &tmp_buf_len);
if (rc == ERROR_FILE_NOT_FOUND) {
buf_resize(&result_sdk->path10, 0);
} else {
buf_resize(&result_sdk->path10, tmp_buf_len);
}
}
{
DWORD tmp_buf_len = MAX_PATH;
buf_resize(&result_sdk->path81, tmp_buf_len);
rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path81), &tmp_buf_len);
if (rc == ERROR_FILE_NOT_FOUND) {
buf_resize(&result_sdk->path81, 0);
} else {
buf_resize(&result_sdk->path81, tmp_buf_len);
}
}
if (buf_len(&result_sdk->path10) != 0) {
Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\*", buf_ptr(&result_sdk->path10));
// enumerate files in sdk path looking for latest version
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return ErrorFileNotFound;
}
int v0 = 0, v1 = 0, v2 = 0, v3 = 0;
bool found_version_dir = false;
for (;;) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3);
if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) {
// Microsoft released 26624 as 10240 accidentally.
// https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
c2 = 26624;
}
if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
v0 = c0, v1 = c1, v2 = c2, v3 = c3;
buf_init_from_str(&result_sdk->version10, ffd.cFileName);
found_version_dir = true;
}
}
if (FindNextFile(hFind, &ffd) == 0) {
FindClose(hFind);
break;
}
}
if (!found_version_dir) {
buf_resize(&result_sdk->path10, 0);
}
}
if (buf_len(&result_sdk->path81) != 0) {
Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\winv*", buf_ptr(&result_sdk->path81));
// enumerate files in sdk path looking for latest version
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return ErrorFileNotFound;
}
int v0 = 0, v1 = 0;
bool found_version_dir = false;
for (;;) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
int c0 = 0, c1 = 0;
sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
if ((c0 > v0) || (c1 > v1)) {
v0 = c0, v1 = c1;
buf_init_from_str(&result_sdk->version81, ffd.cFileName);
found_version_dir = true;
}
}
if (FindNextFile(hFind, &ffd) == 0) {
FindClose(hFind);
break;
}
}
if (!found_version_dir) {
buf_resize(&result_sdk->path81, 0);
}
}
*out_sdk = result_sdk;
return 0;
#else
return ErrorFileNotFound;
#endif
}
int os_get_win32_vcruntime_path(Buf* output_buf, ZigLLVM_ArchType platform_type) {
#if defined(ZIG_OS_WINDOWS)
buf_resize(output_buf, 0);
//COM Smart Pointerse requires explicit scope
{
HRESULT rc;
rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (rc != S_OK) {
goto com_done;
}
//This COM class is installed when a VS2017
ISetupConfigurationPtr setup_config;
rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
if (rc != S_OK) {
goto com_done;
}
IEnumSetupInstancesPtr all_instances;
rc = setup_config->EnumInstances(&all_instances);
if (rc != S_OK) {
goto com_done;
}
ISetupInstance* curr_instance;
ULONG found_inst;
while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
BSTR bstr_inst_path;
rc = curr_instance->GetInstallationPath(&bstr_inst_path);
if (rc != S_OK) {
goto com_done;
}
//BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
ULONG tmp_path_len = bstr_path_len / 2 + 1;
char* conv_path = (char*)bstr_inst_path;
char *tmp_path = (char*)alloca(tmp_path_len);
memset(tmp_path, 0, tmp_path_len);
uint32_t c = 0;
for (uint32_t i = 0; i < bstr_path_len; i += 2) {
tmp_path[c] = conv_path[i];
++c;
assert(c != tmp_path_len);
}
buf_append_str(output_buf, tmp_path);
buf_append_char(output_buf, '\\');
Buf* tmp_buf = buf_alloc();
buf_append_buf(tmp_buf, output_buf);
buf_append_str(tmp_buf, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
FILE* tools_file = fopen(buf_ptr(tmp_buf), "r");
if (!tools_file) {
goto com_done;
}
memset(tmp_path, 0, tmp_path_len);
fgets(tmp_path, tmp_path_len, tools_file);
strtok(tmp_path, " \r\n");
fclose(tools_file);
buf_appendf(output_buf, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
switch (platform_type) {
case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\");
break;
case ZigLLVM_x86_64:
buf_append_str(output_buf, "x64\\");
break;
case ZigLLVM_arm:
buf_append_str(output_buf, "arm\\");
break;
default:
zig_panic("Attemped to use vcruntime for non-supported platform.");
}
buf_resize(tmp_buf, 0);
buf_append_buf(tmp_buf, output_buf);
buf_append_str(tmp_buf, "vcruntime.lib");
if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
return 0;
}
}
}
com_done:;
HKEY key;
HRESULT rc;
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
if (rc != ERROR_SUCCESS) {
return ErrorFileNotFound;
}
DWORD dw_type = 0;
DWORD cb_data = 0;
rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
return ErrorFileNotFound;
}
Buf* tmp_buf = buf_alloc_fixed(cb_data);
RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)buf_ptr(tmp_buf), &cb_data);
//RegQueryValueExA returns the length of the string INCLUDING the null terminator
buf_resize(tmp_buf, cb_data-1);
buf_append_str(tmp_buf, "VC\\Lib\\");
switch (platform_type) {
case ZigLLVM_x86:
//x86 is in the root of the Lib folder
break;
case ZigLLVM_x86_64:
buf_append_str(tmp_buf, "amd64\\");
break;
case ZigLLVM_arm:
buf_append_str(tmp_buf, "arm\\");
break;
default:
zig_panic("Attemped to use vcruntime for non-supported platform.");
}
buf_append_buf(output_buf, tmp_buf);
buf_append_str(tmp_buf, "vcruntime.lib");
if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
return 0;
} else {
buf_resize(output_buf, 0);
return ErrorFileNotFound;
}
#else
return ErrorFileNotFound;
#endif
}
int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
#if defined(ZIG_OS_WINDOWS)
buf_resize(output_buf, 0);
buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));
buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr);
switch (platform_type) {
case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\");
@@ -1366,7 +1149,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch
int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) {
#if defined(ZIG_OS_WINDOWS)
buf_resize(output_buf, 0);
buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));
buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr);
if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) {
return 0;
}
@@ -1383,7 +1166,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
#if defined(ZIG_OS_WINDOWS)
{
buf_resize(output_buf, 0);
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr);
switch (platform_type) {
case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\");
@@ -1406,7 +1189,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
}
{
buf_resize(output_buf, 0);
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path81), buf_ptr(&sdk->version81));
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr);
switch (platform_type) {
case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\");

View File

@@ -12,6 +12,7 @@
#include "buffer.hpp"
#include "error.hpp"
#include "zig_llvm.h"
#include "windows_sdk.h"
#include <stdio.h>
#include <inttypes.h>
@@ -79,15 +80,6 @@ bool os_is_sep(uint8_t c);
int os_self_exe_path(Buf *out_path);
struct ZigWindowsSDK {
Buf path10;
Buf version10;
Buf path81;
Buf version81;
};
int os_find_windows_sdk(ZigWindowsSDK **out_sdk);
int os_get_win32_vcruntime_path(Buf *output_buf, ZigLLVM_ArchType platform_type);
int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf);
int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);

View File

@@ -648,12 +648,11 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
}
/*
SuspendExpression(body) = "suspend" "|" Symbol "|" body
SuspendExpression(body) = "suspend" option( body )
*/
static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
size_t orig_token_index = *token_index;
Token *suspend_token = &pc->tokens->at(*token_index);
if (suspend_token->id == TokenIdKeywordSuspend) {
*token_index += 1;
} else if (mandatory) {
@@ -663,23 +662,18 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b
return nullptr;
}
Token *bar_token = &pc->tokens->at(*token_index);
if (bar_token->id == TokenIdBinOr) {
*token_index += 1;
Token *lbrace = &pc->tokens->at(*token_index);
if (lbrace->id == TokenIdLBrace) {
AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
node->data.suspend.block = ast_parse_block(pc, token_index, true);
return node;
} else if (mandatory) {
ast_expect_token(pc, suspend_token, TokenIdBinOr);
ast_expect_token(pc, lbrace, TokenIdLBrace);
zig_unreachable();
} else {
*token_index = orig_token_index;
*token_index -= 1;
return nullptr;
}
AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
ast_eat_token(pc, token_index, TokenIdBinOr);
node->data.suspend.block = ast_parse_block(pc, token_index, true);
return node;
}
/*
@@ -1025,7 +1019,7 @@ static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index
}
/*
SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
SuffixOpExpression = ("async" option("&lt;" SuffixOpExpression "&gt;") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ".*" | ".?")
FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
ArrayAccessExpression : token(LBracket) Expression token(RBracket)
SliceExpression = "[" Expression ".." option(Expression) "]"
@@ -1110,13 +1104,34 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,
} else if (first_token->id == TokenIdDot) {
*token_index += 1;
Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol);
Token *token = &pc->tokens->at(*token_index);
AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token);
node->data.field_access_expr.struct_expr = primary_expr;
node->data.field_access_expr.field_name = token_buf(name_token);
if (token->id == TokenIdSymbol) {
*token_index += 1;
AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token);
node->data.field_access_expr.struct_expr = primary_expr;
node->data.field_access_expr.field_name = token_buf(token);
primary_expr = node;
} else if (token->id == TokenIdStar) {
*token_index += 1;
AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token);
node->data.ptr_deref_expr.target = primary_expr;
primary_expr = node;
} else if (token->id == TokenIdQuestion) {
*token_index += 1;
AstNode *node = ast_create_node(pc, NodeTypeUnwrapOptional, first_token);
node->data.unwrap_optional.expr = primary_expr;
primary_expr = node;
} else {
ast_invalid_token_error(pc, token);
}
primary_expr = node;
} else {
return primary_expr;
}
@@ -1129,24 +1144,21 @@ static PrefixOp tok_to_prefix_op(Token *token) {
case TokenIdDash: return PrefixOpNegation;
case TokenIdMinusPercent: return PrefixOpNegationWrap;
case TokenIdTilde: return PrefixOpBinNot;
case TokenIdStar: return PrefixOpDereference;
case TokenIdMaybe: return PrefixOpMaybe;
case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe;
case TokenIdStarStar: return PrefixOpDereference;
case TokenIdQuestion: return PrefixOpOptional;
case TokenIdAmpersand: return PrefixOpAddrOf;
default: return PrefixOpInvalid;
}
}
static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) {
Token *ampersand_tok = ast_eat_token(pc, token_index, TokenIdAmpersand);
AstNode *node = ast_create_node(pc, NodeTypeAddrOfExpr, ampersand_tok);
static AstNode *ast_parse_pointer_type(ParseContext *pc, size_t *token_index, Token *star_tok) {
AstNode *node = ast_create_node(pc, NodeTypePointerType, star_tok);
node->data.pointer_type.star_token = star_tok;
Token *token = &pc->tokens->at(*token_index);
if (token->id == TokenIdKeywordAlign) {
*token_index += 1;
ast_eat_token(pc, token_index, TokenIdLParen);
node->data.addr_of_expr.align_expr = ast_parse_expression(pc, token_index, true);
node->data.pointer_type.align_expr = ast_parse_expression(pc, token_index, true);
token = &pc->tokens->at(*token_index);
if (token->id == TokenIdColon) {
@@ -1155,35 +1167,45 @@ static AstNode *ast_parse_addr_of(ParseContext *pc, size_t *token_index) {
ast_eat_token(pc, token_index, TokenIdColon);
Token *bit_offset_end_tok = ast_eat_token(pc, token_index, TokenIdIntLiteral);
node->data.addr_of_expr.bit_offset_start = token_bigint(bit_offset_start_tok);
node->data.addr_of_expr.bit_offset_end = token_bigint(bit_offset_end_tok);
node->data.pointer_type.bit_offset_start = token_bigint(bit_offset_start_tok);
node->data.pointer_type.bit_offset_end = token_bigint(bit_offset_end_tok);
}
ast_eat_token(pc, token_index, TokenIdRParen);
token = &pc->tokens->at(*token_index);
}
if (token->id == TokenIdKeywordConst) {
*token_index += 1;
node->data.addr_of_expr.is_const = true;
node->data.pointer_type.is_const = true;
token = &pc->tokens->at(*token_index);
}
if (token->id == TokenIdKeywordVolatile) {
*token_index += 1;
node->data.addr_of_expr.is_volatile = true;
node->data.pointer_type.is_volatile = true;
}
node->data.addr_of_expr.op_expr = ast_parse_prefix_op_expr(pc, token_index, true);
node->data.pointer_type.op_expr = ast_parse_prefix_op_expr(pc, token_index, true);
return node;
}
/*
PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression
PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await"
PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await"
*/
static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index);
if (token->id == TokenIdAmpersand) {
return ast_parse_addr_of(pc, token_index);
if (token->id == TokenIdStar || token->id == TokenIdBracketStarBracket) {
*token_index += 1;
return ast_parse_pointer_type(pc, token_index, token);
}
if (token->id == TokenIdStarStar) {
*token_index += 1;
AstNode *child_node = ast_parse_pointer_type(pc, token_index, token);
child_node->column += 1;
AstNode *parent_node = ast_create_node(pc, NodeTypePointerType, token);
parent_node->data.pointer_type.star_token = token;
parent_node->data.pointer_type.op_expr = child_node;
return parent_node;
}
if (token->id == TokenIdKeywordTry) {
return ast_parse_try_expr(pc, token_index);
@@ -1200,22 +1222,12 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index,
AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token);
AstNode *parent_node = node;
if (token->id == TokenIdStarStar) {
// pretend that we got 2 star tokens
parent_node = ast_create_node(pc, NodeTypePrefixOpExpr, token);
parent_node->data.prefix_op_expr.primary_expr = node;
parent_node->data.prefix_op_expr.prefix_op = PrefixOpDereference;
node->column += 1;
}
AstNode *prefix_op_expr = ast_parse_error_set_expr(pc, token_index, true);
node->data.prefix_op_expr.primary_expr = prefix_op_expr;
node->data.prefix_op_expr.prefix_op = prefix_op;
return parent_node;
return node;
}
@@ -2270,8 +2282,8 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, size_t *token_index, bool ma
}
/*
UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression
UnwrapMaybe : "??" BoolOrExpression
UnwrapExpression : BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression
UnwrapOptional = "orelse" Expression
UnwrapError = "catch" option("|" Symbol "|") Expression
*/
static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
@@ -2281,14 +2293,14 @@ static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, boo
Token *token = &pc->tokens->at(*token_index);
if (token->id == TokenIdDoubleQuestion) {
if (token->id == TokenIdKeywordOrElse) {
*token_index += 1;
AstNode *rhs = ast_parse_expression(pc, token_index, true);
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
node->data.bin_op_expr.op1 = lhs;
node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe;
node->data.bin_op_expr.bin_op = BinOpTypeUnwrapOptional;
node->data.bin_op_expr.op2 = rhs;
return node;
@@ -2991,6 +3003,12 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
case NodeTypeFieldAccessExpr:
visit_field(&node->data.field_access_expr.struct_expr, visit, context);
break;
case NodeTypePtrDeref:
visit_field(&node->data.ptr_deref_expr.target, visit, context);
break;
case NodeTypeUnwrapOptional:
visit_field(&node->data.unwrap_optional.expr, visit, context);
break;
case NodeTypeUse:
visit_field(&node->data.use.expr, visit, context);
break;
@@ -3093,9 +3111,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
case NodeTypeErrorType:
// none
break;
case NodeTypeAddrOfExpr:
visit_field(&node->data.addr_of_expr.align_expr, visit, context);
visit_field(&node->data.addr_of_expr.op_expr, visit, context);
case NodeTypePointerType:
visit_field(&node->data.pointer_type.align_expr, visit, context);
visit_field(&node->data.pointer_type.op_expr, visit, context);
break;
case NodeTypeErrorSetDecl:
visit_node_list(&node->data.err_set_decl.decls, visit, context);
@@ -3110,7 +3128,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
visit_field(&node->data.await_expr.expr, visit, context);
break;
case NodeTypeSuspend:
visit_field(&node->data.suspend.promise_symbol, visit, context);
visit_field(&node->data.suspend.block, visit, context);
break;
}

View File

@@ -597,12 +597,15 @@ void resolve_target_object_format(ZigTarget *target) {
case ZigLLVM_tce:
case ZigLLVM_tcele:
case ZigLLVM_thumbeb:
case ZigLLVM_wasm32:
case ZigLLVM_wasm64:
case ZigLLVM_xcore:
target->oformat= ZigLLVM_ELF;
return;
case ZigLLVM_wasm32:
case ZigLLVM_wasm64:
target->oformat = ZigLLVM_Wasm;
return;
case ZigLLVM_ppc:
case ZigLLVM_ppc64:
if (is_os_darwin(target)) {
@@ -683,25 +686,46 @@ static int get_arch_pointer_bit_width(ZigLLVM_ArchType arch) {
uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
switch (target->os) {
case OsFreestanding:
switch (id) {
case CIntTypeShort:
case CIntTypeUShort:
return 16;
case CIntTypeInt:
case CIntTypeUInt:
return 32;
case CIntTypeLong:
case CIntTypeULong:
return get_arch_pointer_bit_width(target->arch.arch);
case CIntTypeLongLong:
case CIntTypeULongLong:
return 64;
case CIntTypeCount:
zig_unreachable();
switch (target->arch.arch) {
case ZigLLVM_msp430:
switch (id) {
case CIntTypeShort:
case CIntTypeUShort:
return 16;
case CIntTypeInt:
case CIntTypeUInt:
return 16;
case CIntTypeLong:
case CIntTypeULong:
return 32;
case CIntTypeLongLong:
case CIntTypeULongLong:
return 64;
case CIntTypeCount:
zig_unreachable();
}
default:
switch (id) {
case CIntTypeShort:
case CIntTypeUShort:
return 16;
case CIntTypeInt:
case CIntTypeUInt:
return 32;
case CIntTypeLong:
case CIntTypeULong:
return get_arch_pointer_bit_width(target->arch.arch);
case CIntTypeLongLong:
case CIntTypeULongLong:
return 64;
case CIntTypeCount:
zig_unreachable();
}
}
case OsLinux:
case OsMacOSX:
case OsZen:
case OsOpenBSD:
switch (id) {
case CIntTypeShort:
case CIntTypeUShort:
@@ -742,7 +766,6 @@ uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) {
case OsKFreeBSD:
case OsLv2:
case OsNetBSD:
case OsOpenBSD:
case OsSolaris:
case OsHaiku:
case OsMinix:
@@ -896,3 +919,65 @@ bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target
return false;
}
const char *arch_stack_pointer_register_name(const ArchType *arch) {
switch (arch->arch) {
case ZigLLVM_UnknownArch:
zig_unreachable();
case ZigLLVM_x86:
return "sp";
case ZigLLVM_x86_64:
return "rsp";
case ZigLLVM_aarch64:
case ZigLLVM_arm:
case ZigLLVM_thumb:
case ZigLLVM_aarch64_be:
case ZigLLVM_amdgcn:
case ZigLLVM_amdil:
case ZigLLVM_amdil64:
case ZigLLVM_armeb:
case ZigLLVM_arc:
case ZigLLVM_avr:
case ZigLLVM_bpfeb:
case ZigLLVM_bpfel:
case ZigLLVM_hexagon:
case ZigLLVM_lanai:
case ZigLLVM_hsail:
case ZigLLVM_hsail64:
case ZigLLVM_kalimba:
case ZigLLVM_le32:
case ZigLLVM_le64:
case ZigLLVM_mips:
case ZigLLVM_mips64:
case ZigLLVM_mips64el:
case ZigLLVM_mipsel:
case ZigLLVM_msp430:
case ZigLLVM_nios2:
case ZigLLVM_nvptx:
case ZigLLVM_nvptx64:
case ZigLLVM_ppc64le:
case ZigLLVM_r600:
case ZigLLVM_renderscript32:
case ZigLLVM_renderscript64:
case ZigLLVM_riscv32:
case ZigLLVM_riscv64:
case ZigLLVM_shave:
case ZigLLVM_sparc:
case ZigLLVM_sparcel:
case ZigLLVM_sparcv9:
case ZigLLVM_spir:
case ZigLLVM_spir64:
case ZigLLVM_systemz:
case ZigLLVM_tce:
case ZigLLVM_tcele:
case ZigLLVM_thumbeb:
case ZigLLVM_wasm32:
case ZigLLVM_wasm64:
case ZigLLVM_xcore:
case ZigLLVM_ppc:
case ZigLLVM_ppc64:
zig_panic("TODO populate this table with stack pointer register name for this CPU architecture");
}
zig_unreachable();
}

View File

@@ -77,6 +77,8 @@ size_t target_arch_count(void);
const ArchType *get_target_arch(size_t index);
void get_arch_name(char *out_str, const ArchType *arch);
const char *arch_stack_pointer_register_name(const ArchType *arch);
size_t target_vendor_count(void);
ZigLLVM_VendorType get_target_vendor(size_t index);

View File

@@ -134,6 +134,7 @@ static const struct ZigKeyword zig_keywords[] = {
{"noalias", TokenIdKeywordNoAlias},
{"null", TokenIdKeywordNull},
{"or", TokenIdKeywordOr},
{"orelse", TokenIdKeywordOrElse},
{"packed", TokenIdKeywordPacked},
{"promise", TokenIdKeywordPromise},
{"pub", TokenIdKeywordPub},
@@ -215,10 +216,11 @@ enum TokenizeState {
TokenizeStateSawGreaterThanGreaterThan,
TokenizeStateSawDot,
TokenizeStateSawDotDot,
TokenizeStateSawQuestionMark,
TokenizeStateSawAtSign,
TokenizeStateCharCode,
TokenizeStateError,
TokenizeStateLBracket,
TokenizeStateLBracketStar,
};
@@ -355,12 +357,19 @@ static void end_float_token(Tokenize *t) {
// Mask the sign bit to 0 since always non-negative lex
const uint64_t exp_mask = 0xffffull << exp_shift;
if (shift >= 64) {
// must be special-cased to avoid undefined behavior on shift == 64
if (shift == 128) {
f_bits.repr[0] = 0;
f_bits.repr[1] = sig_bits[0];
} else if (shift == 0) {
f_bits.repr[0] = sig_bits[0];
f_bits.repr[1] = sig_bits[1];
} else if (shift >= 64) {
f_bits.repr[0] = 0;
f_bits.repr[1] = sig_bits[0] << (shift - 64);
} else {
f_bits.repr[0] = sig_bits[0] << shift;
f_bits.repr[1] = ((sig_bits[1] << shift) | (sig_bits[0] >> (64 - shift)));
f_bits.repr[1] = (sig_bits[1] << shift) | (sig_bits[0] >> (64 - shift));
}
f_bits.repr[1] &= ~exp_mask;
@@ -451,16 +460,21 @@ static const char* get_escape_shorthand(uint8_t c) {
static void invalid_char_error(Tokenize *t, uint8_t c) {
if (c == '\r') {
tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported");
} else if (isprint(c)) {
tokenize_error(t, "invalid character: '%c'", c);
} else {
const char *sh = get_escape_shorthand(c);
if (sh) {
tokenize_error(t, "invalid character: '%s'", sh);
} else {
tokenize_error(t, "invalid character: '\\x%x'", c);
}
return;
}
const char *sh = get_escape_shorthand(c);
if (sh) {
tokenize_error(t, "invalid character: '%s'", sh);
return;
}
if (isprint(c)) {
tokenize_error(t, "invalid character: '%c'", c);
return;
}
tokenize_error(t, "invalid character: '\\x%02x'", c);
}
void tokenize(Buf *buf, Tokenization *out) {
@@ -530,6 +544,10 @@ void tokenize(Buf *buf, Tokenization *out) {
begin_token(&t, TokenIdComma);
end_token(&t);
break;
case '?':
begin_token(&t, TokenIdQuestion);
end_token(&t);
break;
case '{':
begin_token(&t, TokenIdLBrace);
end_token(&t);
@@ -539,8 +557,8 @@ void tokenize(Buf *buf, Tokenization *out) {
end_token(&t);
break;
case '[':
t.state = TokenizeStateLBracket;
begin_token(&t, TokenIdLBracket);
end_token(&t);
break;
case ']':
begin_token(&t, TokenIdRBracket);
@@ -622,33 +640,10 @@ void tokenize(Buf *buf, Tokenization *out) {
begin_token(&t, TokenIdDot);
t.state = TokenizeStateSawDot;
break;
case '?':
begin_token(&t, TokenIdMaybe);
t.state = TokenizeStateSawQuestionMark;
break;
default:
invalid_char_error(&t, c);
}
break;
case TokenizeStateSawQuestionMark:
switch (c) {
case '?':
set_token_id(&t, t.cur_tok, TokenIdDoubleQuestion);
end_token(&t);
t.state = TokenizeStateStart;
break;
case '=':
set_token_id(&t, t.cur_tok, TokenIdMaybeAssign);
end_token(&t);
t.state = TokenizeStateStart;
break;
default:
t.pos -= 1;
end_token(&t);
t.state = TokenizeStateStart;
continue;
}
break;
case TokenizeStateSawDot:
switch (c) {
case '.':
@@ -852,6 +847,30 @@ void tokenize(Buf *buf, Tokenization *out) {
continue;
}
break;
case TokenizeStateLBracket:
switch (c) {
case '*':
t.state = TokenizeStateLBracketStar;
set_token_id(&t, t.cur_tok, TokenIdBracketStarBracket);
break;
default:
// reinterpret as just an lbracket
t.pos -= 1;
end_token(&t);
t.state = TokenizeStateStart;
continue;
}
break;
case TokenizeStateLBracketStar:
switch (c) {
case ']':
end_token(&t);
t.state = TokenizeStateStart;
break;
default:
invalid_char_error(&t, c);
}
break;
case TokenizeStateSawPlusPercent:
switch (c) {
case '=':
@@ -1459,7 +1478,6 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateSawGreaterThan:
case TokenizeStateSawGreaterThanGreaterThan:
case TokenizeStateSawDot:
case TokenizeStateSawQuestionMark:
case TokenizeStateSawAtSign:
case TokenizeStateSawStarPercent:
case TokenizeStateSawPlusPercent:
@@ -1467,12 +1485,14 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateLineString:
case TokenizeStateLineStringEnd:
case TokenizeStateSawBarBar:
case TokenizeStateLBracket:
end_token(&t);
break;
case TokenizeStateSawDotDot:
case TokenizeStateSawBackslash:
case TokenizeStateLineStringContinue:
case TokenizeStateLineStringContinueC:
case TokenizeStateLBracketStar:
tokenize_error(&t, "unexpected EOF");
break;
case TokenizeStateLineComment:
@@ -1509,6 +1529,7 @@ const char * token_name(TokenId id) {
case TokenIdBitShiftRight: return ">>";
case TokenIdBitShiftRightEq: return ">>=";
case TokenIdBitXorEq: return "^=";
case TokenIdBracketStarBracket: return "[*]";
case TokenIdCharLiteral: return "CharLiteral";
case TokenIdCmpEq: return "==";
case TokenIdCmpGreaterOrEq: return ">=";
@@ -1521,7 +1542,6 @@ const char * token_name(TokenId id) {
case TokenIdDash: return "-";
case TokenIdDivEq: return "/=";
case TokenIdDot: return ".";
case TokenIdDoubleQuestion: return "??";
case TokenIdEllipsis2: return "..";
case TokenIdEllipsis3: return "...";
case TokenIdEof: return "EOF";
@@ -1558,6 +1578,7 @@ const char * token_name(TokenId id) {
case TokenIdKeywordNoAlias: return "noalias";
case TokenIdKeywordNull: return "null";
case TokenIdKeywordOr: return "or";
case TokenIdKeywordOrElse: return "orelse";
case TokenIdKeywordPacked: return "packed";
case TokenIdKeywordPromise: return "promise";
case TokenIdKeywordPub: return "pub";
@@ -1580,8 +1601,7 @@ const char * token_name(TokenId id) {
case TokenIdLBrace: return "{";
case TokenIdLBracket: return "[";
case TokenIdLParen: return "(";
case TokenIdMaybe: return "?";
case TokenIdMaybeAssign: return "?=";
case TokenIdQuestion: return "?";
case TokenIdMinusEq: return "-=";
case TokenIdMinusPercent: return "-%";
case TokenIdMinusPercentEq: return "-%=";

View File

@@ -28,6 +28,7 @@ enum TokenId {
TokenIdBitShiftRight,
TokenIdBitShiftRightEq,
TokenIdBitXorEq,
TokenIdBracketStarBracket,
TokenIdCharLiteral,
TokenIdCmpEq,
TokenIdCmpGreaterOrEq,
@@ -40,7 +41,6 @@ enum TokenId {
TokenIdDash,
TokenIdDivEq,
TokenIdDot,
TokenIdDoubleQuestion,
TokenIdEllipsis2,
TokenIdEllipsis3,
TokenIdEof,
@@ -75,6 +75,7 @@ enum TokenId {
TokenIdKeywordNoAlias,
TokenIdKeywordNull,
TokenIdKeywordOr,
TokenIdKeywordOrElse,
TokenIdKeywordPacked,
TokenIdKeywordPromise,
TokenIdKeywordPub,
@@ -99,8 +100,7 @@ enum TokenId {
TokenIdLBrace,
TokenIdLBracket,
TokenIdLParen,
TokenIdMaybe,
TokenIdMaybeAssign,
TokenIdQuestion,
TokenIdMinusEq,
TokenIdMinusPercent,
TokenIdMinusPercentEq,
@@ -169,6 +169,8 @@ struct Token {
TokenCharLit char_lit;
} data;
};
// work around conflicting name Token which is also found in libclang
typedef Token ZigToken;
struct Tokenization {
ZigList<Token> *tokens;

View File

@@ -247,6 +247,12 @@ static AstNode *trans_create_node_field_access_str(Context *c, AstNode *containe
return trans_create_node_field_access(c, container, buf_create_from_str(field_name));
}
static AstNode *trans_create_node_ptr_deref(Context *c, AstNode *child_node) {
AstNode *node = trans_create_node(c, NodeTypePtrDeref);
node->data.ptr_deref_expr.target = child_node;
return node;
}
static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *child_node) {
AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr);
node->data.prefix_op_expr.prefix_op = op;
@@ -254,6 +260,12 @@ static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *ch
return node;
}
static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child_node) {
AstNode *node = trans_create_node(c, NodeTypeUnwrapOptional);
node->data.unwrap_optional.expr = child_node;
return node;
}
static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpType op, AstNode *rhs_node) {
AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
node->data.bin_op_expr.op1 = lhs_node;
@@ -270,11 +282,21 @@ static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNod
node);
}
static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_volatile, AstNode *child_node) {
AstNode *node = trans_create_node(c, NodeTypeAddrOfExpr);
node->data.addr_of_expr.is_const = is_const;
node->data.addr_of_expr.is_volatile = is_volatile;
node->data.addr_of_expr.op_expr = child_node;
static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node, PtrLen ptr_len) {
AstNode *node = trans_create_node(c, NodeTypePointerType);
node->data.pointer_type.star_token = allocate<ZigToken>(1);
node->data.pointer_type.star_token->id = (ptr_len == PtrLenSingle) ? TokenIdStar: TokenIdBracketStarBracket;
node->data.pointer_type.is_const = is_const;
node->data.pointer_type.is_const = is_const;
node->data.pointer_type.is_volatile = is_volatile;
node->data.pointer_type.op_expr = child_node;
return node;
}
static AstNode *trans_create_node_addr_of(Context *c, AstNode *child_node) {
AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr);
node->data.prefix_op_expr.prefix_op = PrefixOpAddrOf;
node->data.prefix_op_expr.primary_expr = child_node;
return node;
}
@@ -366,7 +388,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r
fn_def->data.fn_def.fn_proto = fn_proto;
fn_proto->data.fn_proto.fn_def_node = fn_def;
AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, ref_node);
AstNode *unwrap_node = trans_create_node_unwrap_null(c, ref_node);
AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr);
fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node;
@@ -393,10 +415,6 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r
return fn_def;
}
static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child) {
return trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, child);
}
static AstNode *get_global(Context *c, Buf *name) {
{
auto entry = c->global_table.maybe_get(name);
@@ -409,7 +427,7 @@ static AstNode *get_global(Context *c, Buf *name) {
if (entry)
return entry->value;
}
if (c->codegen->primitive_type_table.maybe_get(name) != nullptr) {
if (get_primitive_type(c->codegen, name) != nullptr) {
return trans_create_node_symbol(c, name);
}
return nullptr;
@@ -718,6 +736,30 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) {
}
}
static bool type_is_opaque(Context *c, const Type *ty, const SourceLocation &source_loc) {
switch (ty->getTypeClass()) {
case Type::Builtin: {
const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(ty);
return builtin_ty->getKind() == BuiltinType::Void;
}
case Type::Record: {
const RecordType *record_ty = static_cast<const RecordType*>(ty);
return record_ty->getDecl()->getDefinition() == nullptr;
}
case Type::Elaborated: {
const ElaboratedType *elaborated_ty = static_cast<const ElaboratedType*>(ty);
return type_is_opaque(c, elaborated_ty->getNamedType().getTypePtr(), source_loc);
}
case Type::Typedef: {
const TypedefType *typedef_ty = static_cast<const TypedefType*>(ty);
const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
return type_is_opaque(c, typedef_decl->getUnderlyingType().getTypePtr(), source_loc);
}
default:
return false;
}
}
static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) {
switch (ty->getTypeClass()) {
case Type::Builtin:
@@ -839,12 +881,14 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
}
if (qual_type_child_is_fn_proto(child_qt)) {
return trans_create_node_prefix_op(c, PrefixOpMaybe, child_node);
return trans_create_node_prefix_op(c, PrefixOpOptional, child_node);
}
AstNode *pointer_node = trans_create_node_addr_of(c, child_qt.isConstQualified(),
child_qt.isVolatileQualified(), child_node);
return trans_create_node_prefix_op(c, PrefixOpMaybe, pointer_node);
PtrLen ptr_len = type_is_opaque(c, child_qt.getTypePtr(), source_loc) ? PtrLenSingle : PtrLenUnknown;
AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
child_qt.isVolatileQualified(), child_node, ptr_len);
return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node);
}
case Type::Typedef:
{
@@ -1027,8 +1071,8 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
emit_warning(c, source_loc, "unresolved array element type");
return nullptr;
}
AstNode *pointer_node = trans_create_node_addr_of(c, child_qt.isConstQualified(),
child_qt.isVolatileQualified(), child_type_node);
AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
child_qt.isVolatileQualified(), child_type_node, PtrLenUnknown);
return pointer_node;
}
case Type::BlockPointer:
@@ -1396,7 +1440,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result
// const _ref = &lhs;
AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue);
if (lhs == nullptr) return nullptr;
AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs);
AstNode *addr_of_lhs = trans_create_node_addr_of(c, lhs);
// TODO: avoid name collisions with generated variable names
Buf* tmp_var_name = buf_create_from_str("_ref");
AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs);
@@ -1412,8 +1456,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result
AstNode *operation_type_cast = trans_c_cast(c, rhs_location,
stmt->getComputationLHSType(),
stmt->getLHS()->getType(),
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, tmp_var_name)));
trans_create_node_ptr_deref(c, trans_create_node_symbol(c, tmp_var_name)));
// result_type(... >> u5(rhs))
AstNode *result_type_cast = trans_c_cast(c, rhs_location,
@@ -1426,7 +1469,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result
// *_ref = ...
AstNode *assign_statement = trans_create_node_bin_op(c,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_ptr_deref(c,
trans_create_node_symbol(c, tmp_var_name)),
BinOpTypeAssign, result_type_cast);
@@ -1436,7 +1479,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result
// break :x *_ref
child_scope->node->data.block.statements.append(
trans_create_node_break(c, label_name,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_ptr_deref(c,
trans_create_node_symbol(c, tmp_var_name))));
}
@@ -1471,7 +1514,7 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used,
// const _ref = &lhs;
AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue);
if (lhs == nullptr) return nullptr;
AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs);
AstNode *addr_of_lhs = trans_create_node_addr_of(c, lhs);
// TODO: avoid name collisions with generated variable names
Buf* tmp_var_name = buf_create_from_str("_ref");
AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs);
@@ -1483,11 +1526,11 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used,
if (rhs == nullptr) return nullptr;
AstNode *assign_statement = trans_create_node_bin_op(c,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_ptr_deref(c,
trans_create_node_symbol(c, tmp_var_name)),
BinOpTypeAssign,
trans_create_node_bin_op(c,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_ptr_deref(c,
trans_create_node_symbol(c, tmp_var_name)),
bin_op,
rhs));
@@ -1496,7 +1539,7 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used,
// break :x *_ref
child_scope->node->data.block.statements.append(
trans_create_node_break(c, label_name,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_ptr_deref(c,
trans_create_node_symbol(c, tmp_var_name))));
return child_scope->node;
@@ -1808,7 +1851,7 @@ static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, Tr
// const _ref = &expr;
AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue);
if (expr == nullptr) return nullptr;
AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr);
AstNode *addr_of_expr = trans_create_node_addr_of(c, expr);
// TODO: avoid name collisions with generated variable names
Buf* ref_var_name = buf_create_from_str("_ref");
AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr);
@@ -1817,13 +1860,13 @@ static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, Tr
// const _tmp = *_ref;
Buf* tmp_var_name = buf_create_from_str("_tmp");
AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_ptr_deref(c,
trans_create_node_symbol(c, ref_var_name)));
child_scope->node->data.block.statements.append(tmp_var_decl);
// *_ref += 1;
AstNode *assign_statement = trans_create_node_bin_op(c,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_ptr_deref(c,
trans_create_node_symbol(c, ref_var_name)),
assign_op,
trans_create_node_unsigned(c, 1));
@@ -1863,7 +1906,7 @@ static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, Tra
// const _ref = &expr;
AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue);
if (expr == nullptr) return nullptr;
AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr);
AstNode *addr_of_expr = trans_create_node_addr_of(c, expr);
// TODO: avoid name collisions with generated variable names
Buf* ref_var_name = buf_create_from_str("_ref");
AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr);
@@ -1871,14 +1914,14 @@ static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, Tra
// *_ref += 1;
AstNode *assign_statement = trans_create_node_bin_op(c,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_ptr_deref(c,
trans_create_node_symbol(c, ref_var_name)),
assign_op,
trans_create_node_unsigned(c, 1));
child_scope->node->data.block.statements.append(assign_statement);
// break :x *_ref
AstNode *deref_expr = trans_create_node_prefix_op(c, PrefixOpDereference,
AstNode *deref_expr = trans_create_node_ptr_deref(c,
trans_create_node_symbol(c, ref_var_name));
child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, deref_expr));
@@ -1912,7 +1955,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc
AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransLValue);
if (value_node == nullptr)
return value_node;
return trans_create_node_addr_of(c, false, false, value_node);
return trans_create_node_addr_of(c, value_node);
}
case UO_Deref:
{
@@ -1922,8 +1965,8 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc
bool is_fn_ptr = qual_type_is_fn_ptr(stmt->getSubExpr()->getType());
if (is_fn_ptr)
return value_node;
AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node);
return trans_create_node_prefix_op(c, PrefixOpDereference, unwrapped);
AstNode *unwrapped = trans_create_node_unwrap_null(c, value_node);
return trans_create_node_ptr_deref(c, unwrapped);
}
case UO_Plus:
emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Plus");
@@ -2546,7 +2589,7 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *
}
}
if (callee_node == nullptr) {
callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, callee_raw_node);
callee_node = trans_create_node_unwrap_null(c, callee_raw_node);
}
} else {
callee_node = callee_raw_node;
@@ -2664,7 +2707,9 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
AstNode *child_statement;
child_scope = trans_stmt(c, &child_block_scope->base, stmt->getBody(), &child_statement);
if (child_scope == nullptr) return nullptr;
body_node->data.block.statements.append(child_statement);
if (child_statement != nullptr) {
body_node->data.block.statements.append(child_statement);
}
}
// if (!cond) break;
@@ -2674,6 +2719,7 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
terminator_node->data.if_bool_expr.condition = trans_create_node_prefix_op(c, PrefixOpBoolNot, condition_node);
terminator_node->data.if_bool_expr.then_block = trans_create_node(c, NodeTypeBreak);
assert(terminator_node != nullptr);
body_node->data.block.statements.append(terminator_node);
while_scope->node->data.while_expr.body = body_node;
@@ -2737,7 +2783,12 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt
TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(), &body_statement);
if (body_scope == nullptr)
return nullptr;
while_scope->node->data.while_expr.body = body_statement;
if (body_statement == nullptr) {
while_scope->node->data.while_expr.body = trans_create_node(c, NodeTypeBlock);
} else {
while_scope->node->data.while_expr.body = body_statement;
}
return loop_block_node;
}
@@ -2972,9 +3023,14 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt));
case Stmt::DeclStmtClass:
return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope);
case Stmt::WhileStmtClass:
return wrap_stmt(out_node, out_child_scope, scope,
trans_while_loop(c, scope, (const WhileStmt *)stmt));
case Stmt::WhileStmtClass: {
AstNode *while_node = trans_while_loop(c, scope, (const WhileStmt *)stmt);
assert(while_node->type == NodeTypeWhileExpr);
if (while_node->data.while_expr.body == nullptr) {
while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock);
}
return wrap_stmt(out_node, out_child_scope, scope, while_node);
}
case Stmt::IfStmtClass:
return wrap_stmt(out_node, out_child_scope, scope,
trans_if_statement(c, scope, (const IfStmt *)stmt));
@@ -2997,12 +3053,18 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
case Stmt::UnaryExprOrTypeTraitExprClass:
return wrap_stmt(out_node, out_child_scope, scope,
trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt));
case Stmt::DoStmtClass:
return wrap_stmt(out_node, out_child_scope, scope,
trans_do_loop(c, scope, (const DoStmt *)stmt));
case Stmt::ForStmtClass:
return wrap_stmt(out_node, out_child_scope, scope,
trans_for_loop(c, scope, (const ForStmt *)stmt));
case Stmt::DoStmtClass: {
AstNode *while_node = trans_do_loop(c, scope, (const DoStmt *)stmt);
assert(while_node->type == NodeTypeWhileExpr);
if (while_node->data.while_expr.body == nullptr) {
while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock);
}
return wrap_stmt(out_node, out_child_scope, scope, while_node);
}
case Stmt::ForStmtClass: {
AstNode *node = trans_for_loop(c, scope, (const ForStmt *)stmt);
return wrap_stmt(out_node, out_child_scope, scope, node);
}
case Stmt::StringLiteralClass:
return wrap_stmt(out_node, out_child_scope, scope,
trans_string_literal(c, scope, (const StringLiteral *)stmt));
@@ -3667,6 +3729,7 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_
if (existing_entry) {
return existing_entry->value;
}
QualType child_qt = typedef_decl->getUnderlyingType();
Buf *type_name = buf_create_from_str(decl_name(typedef_decl));
@@ -3700,16 +3763,19 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_
// use the name of this typedef
// TODO
// trans_qual_type here might cause us to look at this typedef again so we put the item in the map first
AstNode *symbol_node = trans_create_node_symbol(c, type_name);
c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node);
AstNode *type_node = trans_qual_type(c, child_qt, typedef_decl->getLocation());
if (type_node == nullptr) {
emit_warning(c, typedef_decl->getLocation(), "typedef %s - unresolved child type", buf_ptr(type_name));
c->decl_table.put(typedef_decl, nullptr);
// TODO add global var with type_name equal to @compileError("unable to resolve C type")
return nullptr;
}
add_global_var(c, type_name, type_node);
AstNode *symbol_node = trans_create_node_symbol(c, type_name);
c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node);
return symbol_node;
}
@@ -3744,6 +3810,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
return demote_enum_to_opaque(c, enum_decl, full_type_name, bare_name);
}
bool pure_enum = true;
uint32_t field_count = 0;
for (auto it = enum_def->enumerator_begin(),
@@ -3755,84 +3822,53 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
pure_enum = false;
}
}
AstNode *tag_int_type = trans_qual_type(c, enum_decl->getIntegerType(), enum_decl->getLocation());
assert(tag_int_type);
if (pure_enum) {
AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl);
enum_node->data.container_decl.kind = ContainerKindEnum;
enum_node->data.container_decl.layout = ContainerLayoutExtern;
// TODO only emit this tag type if the enum tag type is not the default.
// I don't know what the default is, need to figure out how clang is deciding.
// it appears to at least be different across gcc/msvc
if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) &&
!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int))
{
enum_node->data.container_decl.init_arg_expr = tag_int_type;
}
enum_node->data.container_decl.fields.resize(field_count);
uint32_t i = 0;
for (auto it = enum_def->enumerator_begin(),
it_end = enum_def->enumerator_end();
it != it_end; ++it, i += 1)
{
const EnumConstantDecl *enum_const = *it;
Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
Buf *field_name;
if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) {
field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name));
} else {
field_name = enum_val_name;
}
AstNode *field_node = trans_create_node(c, NodeTypeStructField);
field_node->data.struct_field.name = field_name;
field_node->data.struct_field.type = nullptr;
enum_node->data.container_decl.fields.items[i] = field_node;
// in C each enum value is in the global namespace. so we put them there too.
// at this point we can rely on the enum emitting successfully
if (is_anonymous) {
AstNode *lit_node = trans_create_node_unsigned(c, i);
add_global_var(c, enum_val_name, lit_node);
} else {
AstNode *field_access_node = trans_create_node_field_access(c,
trans_create_node_symbol(c, full_type_name), field_name);
add_global_var(c, enum_val_name, field_access_node);
}
}
if (is_anonymous) {
c->decl_table.put(enum_decl->getCanonicalDecl(), enum_node);
return enum_node;
} else {
AstNode *symbol_node = trans_create_node_symbol(c, full_type_name);
add_global_weak_alias(c, bare_name, full_type_name);
add_global_var(c, full_type_name, enum_node);
c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);
return enum_node;
}
AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl);
enum_node->data.container_decl.kind = ContainerKindEnum;
enum_node->data.container_decl.layout = ContainerLayoutExtern;
// TODO only emit this tag type if the enum tag type is not the default.
// I don't know what the default is, need to figure out how clang is deciding.
// it appears to at least be different across gcc/msvc
if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) &&
!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int))
{
enum_node->data.container_decl.init_arg_expr = tag_int_type;
}
// TODO after issue #305 is solved, make this be an enum with tag_int_type
// as the integer type and set the custom enum values
AstNode *enum_node = tag_int_type;
// add variables for all the values with enum_node
enum_node->data.container_decl.fields.resize(field_count);
uint32_t i = 0;
for (auto it = enum_def->enumerator_begin(),
it_end = enum_def->enumerator_end();
it != it_end; ++it)
it != it_end; ++it, i += 1)
{
const EnumConstantDecl *enum_const = *it;
Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
AstNode *int_node = trans_create_node_apint(c, enum_const->getInitVal());
AstNode *var_node = add_global_var(c, enum_val_name, int_node);
var_node->data.variable_declaration.type = tag_int_type;
Buf *field_name;
if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) {
field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name));
} else {
field_name = enum_val_name;
}
AstNode *int_node = pure_enum && !is_anonymous ? nullptr : trans_create_node_apint(c, enum_const->getInitVal());
AstNode *field_node = trans_create_node(c, NodeTypeStructField);
field_node->data.struct_field.name = field_name;
field_node->data.struct_field.type = nullptr;
field_node->data.struct_field.value = int_node;
enum_node->data.container_decl.fields.items[i] = field_node;
// in C each enum value is in the global namespace. so we put them there too.
// at this point we can rely on the enum emitting successfully
if (is_anonymous) {
Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
add_global_var(c, enum_val_name, int_node);
} else {
AstNode *field_access_node = trans_create_node_field_access(c,
trans_create_node_symbol(c, full_type_name), field_name);
add_global_var(c, enum_val_name, field_access_node);
}
}
if (is_anonymous) {
@@ -3843,7 +3879,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
add_global_weak_alias(c, bare_name, full_type_name);
add_global_var(c, full_type_name, enum_node);
c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);
return symbol_node;
return enum_node;
}
}
@@ -4286,7 +4322,7 @@ static AstNode *trans_lookup_ast_maybe_fn(Context *c, AstNode *ref_node) {
return nullptr;
if (prefix_node->type != NodeTypePrefixOpExpr)
return nullptr;
if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpMaybe)
if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpOptional)
return nullptr;
AstNode *fn_proto_node = prefix_node->data.prefix_op_expr.primary_expr;
@@ -4462,34 +4498,52 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t
} else if (first_tok->id == CTokIdAsterisk) {
*tok_i += 1;
node = trans_create_node_addr_of(c, false, false, node);
node = trans_create_node_ptr_type(c, false, false, node, PtrLenUnknown);
} else {
return node;
}
}
}
static PrefixOp ctok_to_prefix_op(CTok *token) {
switch (token->id) {
case CTokIdBang: return PrefixOpBoolNot;
case CTokIdMinus: return PrefixOpNegation;
case CTokIdTilde: return PrefixOpBinNot;
case CTokIdAsterisk: return PrefixOpDereference;
default: return PrefixOpInvalid;
}
}
static AstNode *parse_ctok_prefix_op_expr(Context *c, CTokenize *ctok, size_t *tok_i) {
CTok *op_tok = &ctok->tokens.at(*tok_i);
PrefixOp prefix_op = ctok_to_prefix_op(op_tok);
if (prefix_op == PrefixOpInvalid) {
return parse_ctok_suffix_op_expr(c, ctok, tok_i);
}
*tok_i += 1;
AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i);
if (prefix_op_expr == nullptr)
return nullptr;
return trans_create_node_prefix_op(c, prefix_op, prefix_op_expr);
switch (op_tok->id) {
case CTokIdBang:
{
*tok_i += 1;
AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i);
if (prefix_op_expr == nullptr)
return nullptr;
return trans_create_node_prefix_op(c, PrefixOpBoolNot, prefix_op_expr);
}
case CTokIdMinus:
{
*tok_i += 1;
AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i);
if (prefix_op_expr == nullptr)
return nullptr;
return trans_create_node_prefix_op(c, PrefixOpNegation, prefix_op_expr);
}
case CTokIdTilde:
{
*tok_i += 1;
AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i);
if (prefix_op_expr == nullptr)
return nullptr;
return trans_create_node_prefix_op(c, PrefixOpBinNot, prefix_op_expr);
}
case CTokIdAsterisk:
{
*tok_i += 1;
AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i);
if (prefix_op_expr == nullptr)
return nullptr;
return trans_create_node_ptr_deref(c, prefix_op_expr);
}
default:
return parse_ctok_suffix_op_expr(c, ctok, tok_i);
}
}
static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *char_ptr) {

View File

@@ -31,6 +31,8 @@
#endif
#include "softfloat.hpp"
#define BREAKPOINT __asm("int $0x03")
ATTRIBUTE_COLD
@@ -38,11 +40,11 @@ ATTRIBUTE_NORETURN
ATTRIBUTE_PRINTF(1, 2)
void zig_panic(const char *format, ...);
ATTRIBUTE_COLD
ATTRIBUTE_NORETURN
static inline void zig_unreachable(void) {
zig_panic("unreachable");
}
#ifdef WIN32
#define __func__ __FUNCTION__
#endif
#define zig_unreachable() zig_panic("unreachable: %s:%s:%d", __FILE__, __func__, __LINE__)
#if defined(_MSC_VER)
static inline int clzll(unsigned long long mask) {
@@ -65,6 +67,11 @@ static inline int clzll(unsigned long long mask) {
template<typename T>
ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) {
#ifndef NDEBUG
// make behavior when size == 0 portable
if (count == 0)
return nullptr;
#endif
T *ptr = reinterpret_cast<T*>(malloc(count * sizeof(T)));
if (!ptr)
zig_panic("allocation failed");
@@ -73,6 +80,11 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) {
template<typename T>
ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) {
#ifndef NDEBUG
// make behavior when size == 0 portable
if (count == 0)
return nullptr;
#endif
T *ptr = reinterpret_cast<T*>(calloc(count, sizeof(T)));
if (!ptr)
zig_panic("allocation failed");
@@ -93,9 +105,7 @@ static inline void safe_memcpy(T *dest, const T *src, size_t count) {
template<typename T>
static inline T *reallocate(T *old, size_t old_count, size_t new_count) {
T *ptr = reinterpret_cast<T*>(realloc(old, new_count * sizeof(T)));
if (!ptr)
zig_panic("allocation failed");
T *ptr = reallocate_nonzero(old, old_count, new_count);
if (new_count > old_count) {
memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T));
}
@@ -104,6 +114,11 @@ static inline T *reallocate(T *old, size_t old_count, size_t new_count) {
template<typename T>
static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) {
#ifndef NDEBUG
// make behavior when size == 0 portable
if (new_count == 0 && old == nullptr)
return nullptr;
#endif
T *ptr = reinterpret_cast<T*>(realloc(old, new_count * sizeof(T)));
if (!ptr)
zig_panic("allocation failed");
@@ -152,4 +167,21 @@ static inline uint8_t log2_u64(uint64_t x) {
return (63 - clzll(x));
}
static inline float16_t zig_double_to_f16(double x) {
float64_t y;
static_assert(sizeof(x) == sizeof(y), "");
memcpy(&y, &x, sizeof(x));
return f64_to_f16(y);
}
// Return value is safe to coerce to float even when |x| is NaN or Infinity.
static inline double zig_f16_to_double(float16_t x) {
float64_t y = f16_to_f64(x);
double z;
static_assert(sizeof(y) == sizeof(z), "");
memcpy(&z, &y, sizeof(y));
return z;
}
#endif

352
src/windows_sdk.cpp Normal file
View File

@@ -0,0 +1,352 @@
/*
* Copyright (c) 2018 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "windows_sdk.h"
#if defined(_WIN32)
#include "windows_com.hpp"
#include <inttypes.h>
#include <assert.h>
struct ZigWindowsSDKPrivate {
ZigWindowsSDK base;
};
enum NativeArch {
NativeArchArm,
NativeArchi386,
NativeArchx86_64,
};
#if defined(_M_ARM) || defined(__arm_)
static const NativeArch native_arch = NativeArchArm;
#endif
#if defined(_M_IX86) || defined(__i386__)
static const NativeArch native_arch = NativeArchi386;
#endif
#if defined(_M_X64) || defined(__x86_64__)
static const NativeArch native_arch = NativeArchx86_64;
#endif
void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {
if (sdk == nullptr) {
return;
}
free((void*)sdk->path10_ptr);
free((void*)sdk->version10_ptr);
free((void*)sdk->path81_ptr);
free((void*)sdk->version81_ptr);
free((void*)sdk->msvc_lib_dir_ptr);
}
static ZigFindWindowsSdkError find_msvc_lib_dir(ZigWindowsSDKPrivate *priv) {
//COM Smart Pointers requires explicit scope
{
HRESULT rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (rc != S_OK && rc != S_FALSE) {
goto com_done;
}
//This COM class is installed when a VS2017
ISetupConfigurationPtr setup_config;
rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
if (rc != S_OK) {
goto com_done;
}
IEnumSetupInstancesPtr all_instances;
rc = setup_config->EnumInstances(&all_instances);
if (rc != S_OK) {
goto com_done;
}
ISetupInstance* curr_instance;
ULONG found_inst;
while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
BSTR bstr_inst_path;
rc = curr_instance->GetInstallationPath(&bstr_inst_path);
if (rc != S_OK) {
goto com_done;
}
//BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
//TODO call an actual function to do this
UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
ULONG tmp_path_len = bstr_path_len / 2 + 1;
char* conv_path = (char*)bstr_inst_path;
// TODO don't use alloca
char *tmp_path = (char*)alloca(tmp_path_len);
memset(tmp_path, 0, tmp_path_len);
uint32_t c = 0;
for (uint32_t i = 0; i < bstr_path_len; i += 2) {
tmp_path[c] = conv_path[i];
++c;
assert(c != tmp_path_len);
}
char output_path[4096];
output_path[0] = 0;
char *out_append_ptr = output_path;
out_append_ptr += sprintf(out_append_ptr, "%s\\", tmp_path);
char tmp_buf[4096];
sprintf(tmp_buf, "%s%s", output_path, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
FILE* tools_file = fopen(tmp_buf, "rb");
if (!tools_file) {
goto com_done;
}
memset(tmp_path, 0, tmp_path_len);
fgets(tmp_path, tmp_path_len, tools_file);
strtok(tmp_path, " \r\n");
fclose(tools_file);
out_append_ptr += sprintf(out_append_ptr, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
switch (native_arch) {
case NativeArchi386:
out_append_ptr += sprintf(out_append_ptr, "x86\\");
break;
case NativeArchx86_64:
out_append_ptr += sprintf(out_append_ptr, "x64\\");
break;
case NativeArchArm:
out_append_ptr += sprintf(out_append_ptr, "arm\\");
break;
}
sprintf(tmp_buf, "%s%s", output_path, "vcruntime.lib");
if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
priv->base.msvc_lib_dir_ptr = strdup(output_path);
if (priv->base.msvc_lib_dir_ptr == nullptr) {
return ZigFindWindowsSdkErrorOutOfMemory;
}
priv->base.msvc_lib_dir_len = strlen(priv->base.msvc_lib_dir_ptr);
return ZigFindWindowsSdkErrorNone;
}
}
}
com_done:;
HKEY key;
HRESULT rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0,
KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
if (rc != ERROR_SUCCESS) {
return ZigFindWindowsSdkErrorNotFound;
}
DWORD dw_type = 0;
DWORD cb_data = 0;
rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
return ZigFindWindowsSdkErrorNotFound;
}
char tmp_buf[4096];
RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)tmp_buf, &cb_data);
// RegQueryValueExA returns the length of the string INCLUDING the null terminator
char *tmp_buf_append_ptr = tmp_buf + (cb_data - 1);
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "VC\\Lib\\");
switch (native_arch) {
case NativeArchi386:
//x86 is in the root of the Lib folder
break;
case NativeArchx86_64:
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "amd64\\");
break;
case NativeArchArm:
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm\\");
break;
}
char *output_path = strdup(tmp_buf);
if (output_path == nullptr) {
return ZigFindWindowsSdkErrorOutOfMemory;
}
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "vcruntime.lib");
if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
priv->base.msvc_lib_dir_ptr = output_path;
priv->base.msvc_lib_dir_len = strlen(output_path);
return ZigFindWindowsSdkErrorNone;
} else {
free(output_path);
return ZigFindWindowsSdkErrorNotFound;
}
}
static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) {
if (priv->base.path10_ptr == nullptr)
return ZigFindWindowsSdkErrorNone;
char sdk_lib_dir[4096];
int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\*", priv->base.path10_ptr);
if (n < 0 || n >= 4096) {
return ZigFindWindowsSdkErrorPathTooLong;
}
// enumerate files in sdk path looking for latest version
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return ZigFindWindowsSdkErrorNotFound;
}
int v0 = 0, v1 = 0, v2 = 0, v3 = 0;
for (;;) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3);
if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) {
// Microsoft released 26624 as 10240 accidentally.
// https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
c2 = 26624;
}
if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
v0 = c0, v1 = c1, v2 = c2, v3 = c3;
free((void*)priv->base.version10_ptr);
priv->base.version10_ptr = strdup(ffd.cFileName);
if (priv->base.version10_ptr == nullptr) {
FindClose(hFind);
return ZigFindWindowsSdkErrorOutOfMemory;
}
}
}
if (FindNextFile(hFind, &ffd) == 0) {
FindClose(hFind);
break;
}
}
priv->base.version10_len = strlen(priv->base.version10_ptr);
return ZigFindWindowsSdkErrorNone;
}
static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) {
if (priv->base.path81_ptr == nullptr)
return ZigFindWindowsSdkErrorNone;
char sdk_lib_dir[4096];
int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\winv*", priv->base.path81_ptr);
if (n < 0 || n >= 4096) {
return ZigFindWindowsSdkErrorPathTooLong;
}
// enumerate files in sdk path looking for latest version
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return ZigFindWindowsSdkErrorNotFound;
}
int v0 = 0, v1 = 0;
for (;;) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
int c0 = 0, c1 = 0;
sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
if ((c0 > v0) || (c1 > v1)) {
v0 = c0, v1 = c1;
free((void*)priv->base.version81_ptr);
priv->base.version81_ptr = strdup(ffd.cFileName);
if (priv->base.version81_ptr == nullptr) {
FindClose(hFind);
return ZigFindWindowsSdkErrorOutOfMemory;
}
}
}
if (FindNextFile(hFind, &ffd) == 0) {
FindClose(hFind);
break;
}
}
priv->base.version81_len = strlen(priv->base.version81_ptr);
return ZigFindWindowsSdkErrorNone;
}
ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
ZigWindowsSDKPrivate *priv = (ZigWindowsSDKPrivate*)calloc(1, sizeof(ZigWindowsSDKPrivate));
if (priv == nullptr) {
return ZigFindWindowsSdkErrorOutOfMemory;
}
HKEY key;
HRESULT rc;
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0,
KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key);
if (rc != ERROR_SUCCESS) {
zig_free_windows_sdk(&priv->base);
return ZigFindWindowsSdkErrorNotFound;
}
{
DWORD tmp_buf_len = MAX_PATH;
priv->base.path10_ptr = (const char *)calloc(tmp_buf_len, 1);
if (priv->base.path10_ptr == nullptr) {
zig_free_windows_sdk(&priv->base);
return ZigFindWindowsSdkErrorOutOfMemory;
}
rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len);
if (rc == ERROR_SUCCESS) {
priv->base.path10_len = tmp_buf_len - 1;
if (priv->base.path10_ptr[priv->base.path10_len - 1] == '\\') {
priv->base.path10_len -= 1;
}
} else {
free((void*)priv->base.path10_ptr);
priv->base.path10_ptr = nullptr;
}
}
{
DWORD tmp_buf_len = MAX_PATH;
priv->base.path81_ptr = (const char *)calloc(tmp_buf_len, 1);
if (priv->base.path81_ptr == nullptr) {
zig_free_windows_sdk(&priv->base);
return ZigFindWindowsSdkErrorOutOfMemory;
}
rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len);
if (rc == ERROR_SUCCESS) {
priv->base.path81_len = tmp_buf_len - 1;
if (priv->base.path81_ptr[priv->base.path81_len - 1] == '\\') {
priv->base.path81_len -= 1;
}
} else {
free((void*)priv->base.path81_ptr);
priv->base.path81_ptr = nullptr;
}
}
{
ZigFindWindowsSdkError err = find_10_version(priv);
if (err == ZigFindWindowsSdkErrorOutOfMemory) {
zig_free_windows_sdk(&priv->base);
return err;
}
}
{
ZigFindWindowsSdkError err = find_81_version(priv);
if (err == ZigFindWindowsSdkErrorOutOfMemory) {
zig_free_windows_sdk(&priv->base);
return err;
}
}
{
ZigFindWindowsSdkError err = find_msvc_lib_dir(priv);
if (err == ZigFindWindowsSdkErrorOutOfMemory) {
zig_free_windows_sdk(&priv->base);
return err;
}
}
*out_sdk = &priv->base;
return ZigFindWindowsSdkErrorNone;
}
#else
void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {}
ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
return ZigFindWindowsSdkErrorNotFound;
}
#endif

47
src/windows_sdk.h Normal file
View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2018 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef ZIG_WINDOWS_SDK_H
#define ZIG_WINDOWS_SDK_H
#ifdef __cplusplus
#define ZIG_EXTERN_C extern "C"
#else
#define ZIG_EXTERN_C
#endif
#include <stddef.h>
struct ZigWindowsSDK {
const char *path10_ptr;
size_t path10_len;
const char *version10_ptr;
size_t version10_len;
const char *path81_ptr;
size_t path81_len;
const char *version81_ptr;
size_t version81_len;
const char *msvc_lib_dir_ptr;
size_t msvc_lib_dir_len;
};
enum ZigFindWindowsSdkError {
ZigFindWindowsSdkErrorNone,
ZigFindWindowsSdkErrorOutOfMemory,
ZigFindWindowsSdkErrorNotFound,
ZigFindWindowsSdkErrorPathTooLong,
};
ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk);
ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk);
#endif

View File

@@ -81,7 +81,7 @@ static const bool assertions_on = false;
#endif
bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug)
const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small)
{
std::error_code EC;
raw_fd_ostream dest(filename, EC, sys::fs::F_None);
@@ -100,7 +100,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
return true;
}
PMBuilder->OptLevel = target_machine->getOptLevel();
PMBuilder->SizeLevel = 0;
PMBuilder->SizeLevel = is_small ? 2 : 0;
PMBuilder->DisableTailCalls = is_debug;
PMBuilder->DisableUnitAtATime = is_debug;
@@ -440,6 +440,11 @@ ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unreso
return reinterpret_cast<ZigLLVMDIBuilder *>(di_builder);
}
void ZigLLVMDisposeDIBuilder(ZigLLVMDIBuilder *dbuilder) {
DIBuilder *di_builder = reinterpret_cast<DIBuilder *>(dbuilder);
delete di_builder;
}
void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) {
unwrap(builder)->SetCurrentDebugLocation(DebugLoc::get(
line, column, reinterpret_cast<DIScope*>(scope)));
@@ -765,10 +770,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) {
LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
LLVMAtomicOrdering failure_ordering)
LLVMAtomicOrdering failure_ordering, bool is_weak)
{
return wrap(unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), unwrap(new_val),
mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering)));
AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp),
unwrap(new_val), mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering));
inst->setWeak(is_weak);
return wrap(inst);
}
LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
@@ -836,7 +843,7 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_
return lld::mach_o::link(array_ref_args, diag);
case ZigLLVM_Wasm:
assert(false); // TODO ZigLLDLink for Wasm
return lld::wasm::link(array_ref_args, false, diag);
}
assert(false); // unreachable
abort();

View File

@@ -22,6 +22,9 @@
#define ZIG_EXTERN_C
#endif
// ATTENTION: If you modify this file, be sure to update the corresponding
// extern function declarations in the self-hosted compiler.
struct ZigLLVMDIType;
struct ZigLLVMDIBuilder;
struct ZigLLVMDICompileUnit;
@@ -39,7 +42,7 @@ struct ZigLLVMInsertionPoint;
ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R);
ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R);
/// Caller must free memory.
/// Caller must free memory with LLVMDisposeMessage
ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void);
ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
@@ -52,7 +55,7 @@ enum ZigLLVM_EmitOutputType {
};
ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug);
const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small);
ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);
@@ -66,7 +69,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LL
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
LLVMAtomicOrdering failure_ordering);
LLVMAtomicOrdering failure_ordering, bool is_weak);
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
const char *name);
@@ -139,6 +142,7 @@ ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void);
ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void);
ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);
ZIG_EXTERN_C void ZigLLVMDisposeDIBuilder(struct ZigLLVMDIBuilder *dbuilder);
ZIG_EXTERN_C void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module);
ZIG_EXTERN_C void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module);

View File

@@ -1,6 +1,7 @@
const std = @import("index.zig");
const debug = std.debug;
const assert = debug.assert;
const assertError = debug.assertError;
const mem = std.mem;
const Allocator = mem.Allocator;
@@ -8,7 +9,7 @@ pub fn ArrayList(comptime T: type) type {
return AlignedArrayList(T, @alignOf(T));
}
pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
return struct {
const Self = this;
@@ -17,38 +18,55 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
/// you uninitialized memory.
items: []align(A) T,
len: usize,
allocator: &Allocator,
allocator: *Allocator,
/// Deinitialize with `deinit` or use `toOwnedSlice`.
pub fn init(allocator: &Allocator) Self {
return Self {
pub fn init(allocator: *Allocator) Self {
return Self{
.items = []align(A) T{},
.len = 0,
.allocator = allocator,
};
}
pub fn deinit(l: &Self) void {
l.allocator.free(l.items);
pub fn deinit(self: Self) void {
self.allocator.free(self.items);
}
pub fn toSlice(l: &Self) []align(A) T {
return l.items[0..l.len];
pub fn toSlice(self: Self) []align(A) T {
return self.items[0..self.len];
}
pub fn toSliceConst(l: &const Self) []align(A) const T {
return l.items[0..l.len];
pub fn toSliceConst(self: Self) []align(A) const T {
return self.items[0..self.len];
}
pub fn at(l: &const Self, n: usize) T {
return l.toSliceConst()[n];
pub fn at(self: Self, i: usize) T {
return self.toSliceConst()[i];
}
/// Sets the value at index `i`, or returns `error.OutOfBounds` if
/// the index is not in range.
pub fn setOrError(self: Self, i: usize, item: T) !void {
if (i >= self.len) return error.OutOfBounds;
self.items[i] = item;
}
/// Sets the value at index `i`, asserting that the value is in range.
pub fn set(self: *Self, i: usize, item: T) void {
assert(i < self.len);
self.items[i] = item;
}
pub fn count(self: Self) usize {
return self.len;
}
/// ArrayList takes ownership of the passed in slice. The slice must have been
/// allocated with `allocator`.
/// Deinitialize with `deinit` or use `toOwnedSlice`.
pub fn fromOwnedSlice(allocator: &Allocator, slice: []align(A) T) Self {
return Self {
pub fn fromOwnedSlice(allocator: *Allocator, slice: []align(A) T) Self {
return Self{
.items = slice,
.len = slice.len,
.allocator = allocator,
@@ -56,122 +74,302 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
}
/// The caller owns the returned memory. ArrayList becomes empty.
pub fn toOwnedSlice(self: &Self) []align(A) T {
pub fn toOwnedSlice(self: *Self) []align(A) T {
const allocator = self.allocator;
const result = allocator.alignedShrink(T, A, self.items, self.len);
*self = init(allocator);
self.* = init(allocator);
return result;
}
pub fn insert(l: &Self, n: usize, item: &const T) !void {
try l.ensureCapacity(l.len + 1);
l.len += 1;
pub fn insert(self: *Self, n: usize, item: T) !void {
try self.ensureCapacity(self.len + 1);
self.len += 1;
mem.copy(T, l.items[n+1..l.len], l.items[n..l.len-1]);
l.items[n] = *item;
mem.copyBackwards(T, self.items[n + 1 .. self.len], self.items[n .. self.len - 1]);
self.items[n] = item;
}
pub fn insertSlice(l: &Self, n: usize, items: []align(A) const T) !void {
try l.ensureCapacity(l.len + items.len);
l.len += items.len;
pub fn insertSlice(self: *Self, n: usize, items: []align(A) const T) !void {
try self.ensureCapacity(self.len + items.len);
self.len += items.len;
mem.copy(T, l.items[n+items.len..l.len], l.items[n..l.len-items.len]);
mem.copy(T, l.items[n..n+items.len], items);
mem.copyBackwards(T, self.items[n + items.len .. self.len], self.items[n .. self.len - items.len]);
mem.copy(T, self.items[n .. n + items.len], items);
}
pub fn append(l: &Self, item: &const T) !void {
const new_item_ptr = try l.addOne();
*new_item_ptr = *item;
pub fn append(self: *Self, item: T) !void {
const new_item_ptr = try self.addOne();
new_item_ptr.* = item;
}
pub fn appendSlice(l: &Self, items: []align(A) const T) !void {
try l.ensureCapacity(l.len + items.len);
mem.copy(T, l.items[l.len..], items);
l.len += items.len;
/// Removes the element at the specified index and returns it.
/// The empty slot is filled from the end of the list.
pub fn swapRemove(self: *Self, i: usize) T {
if (self.len - 1 == i) return self.pop();
const slice = self.toSlice();
const old_item = slice[i];
slice[i] = self.pop();
return old_item;
}
pub fn resize(l: &Self, new_len: usize) !void {
try l.ensureCapacity(new_len);
l.len = new_len;
/// Removes the element at the specified index and returns it
/// or an error.OutOfBounds is returned. If no error then
/// the empty slot is filled from the end of the list.
pub fn swapRemoveOrError(self: *Self, i: usize) !T {
if (i >= self.len) return error.OutOfBounds;
return self.swapRemove(i);
}
pub fn shrink(l: &Self, new_len: usize) void {
assert(new_len <= l.len);
l.len = new_len;
pub fn appendSlice(self: *Self, items: []align(A) const T) !void {
try self.ensureCapacity(self.len + items.len);
mem.copy(T, self.items[self.len..], items);
self.len += items.len;
}
pub fn ensureCapacity(l: &Self, new_capacity: usize) !void {
var better_capacity = l.items.len;
pub fn resize(self: *Self, new_len: usize) !void {
try self.ensureCapacity(new_len);
self.len = new_len;
}
pub fn shrink(self: *Self, new_len: usize) void {
assert(new_len <= self.len);
self.len = new_len;
}
pub fn ensureCapacity(self: *Self, new_capacity: usize) !void {
var better_capacity = self.items.len;
if (better_capacity >= new_capacity) return;
while (true) {
better_capacity += better_capacity / 2 + 8;
if (better_capacity >= new_capacity) break;
}
l.items = try l.allocator.alignedRealloc(T, A, l.items, better_capacity);
self.items = try self.allocator.alignedRealloc(T, A, self.items, better_capacity);
}
pub fn addOne(l: &Self) !&T {
const new_length = l.len + 1;
try l.ensureCapacity(new_length);
const result = &l.items[l.len];
l.len = new_length;
pub fn addOne(self: *Self) !*T {
const new_length = self.len + 1;
try self.ensureCapacity(new_length);
const result = &self.items[self.len];
self.len = new_length;
return result;
}
pub fn pop(self: &Self) T {
pub fn pop(self: *Self) T {
self.len -= 1;
return self.items[self.len];
}
pub fn popOrNull(self: &Self) ?T {
if (self.len == 0)
return null;
pub fn popOrNull(self: *Self) ?T {
if (self.len == 0) return null;
return self.pop();
}
pub const Iterator = struct {
list: *const Self,
// how many items have we returned
count: usize,
pub fn next(it: *Iterator) ?T {
if (it.count >= it.list.len) return null;
const val = it.list.at(it.count);
it.count += 1;
return val;
}
pub fn reset(it: *Iterator) void {
it.count = 0;
}
};
pub fn iterator(self: *const Self) Iterator {
return Iterator{
.list = self,
.count = 0,
};
}
};
}
test "basic ArrayList test" {
var list = ArrayList(i32).init(debug.global_allocator);
test "std.ArrayList.basic" {
var bytes: [1024]u8 = undefined;
const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
var list = ArrayList(i32).init(allocator);
defer list.deinit();
{var i: usize = 0; while (i < 10) : (i += 1) {
list.append(i32(i + 1)) catch unreachable;
}}
// setting on empty list is out of bounds
assertError(list.setOrError(0, 1), error.OutOfBounds);
{var i: usize = 0; while (i < 10) : (i += 1) {
assert(list.items[i] == i32(i + 1));
}}
{
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) {
assert(list.items[i] == @intCast(i32, i + 1));
}
}
for (list.toSlice()) |v, i| {
assert(v == @intCast(i32, i + 1));
}
for (list.toSliceConst()) |v, i| {
assert(v == @intCast(i32, i + 1));
}
assert(list.pop() == 10);
assert(list.len == 9);
list.appendSlice([]const i32 { 1, 2, 3 }) catch unreachable;
list.appendSlice([]const i32{
1,
2,
3,
}) catch unreachable;
assert(list.len == 12);
assert(list.pop() == 3);
assert(list.pop() == 2);
assert(list.pop() == 1);
assert(list.len == 9);
list.appendSlice([]const i32 {}) catch unreachable;
list.appendSlice([]const i32{}) catch unreachable;
assert(list.len == 9);
// can only set on indices < self.len
list.set(7, 33);
list.set(8, 42);
assertError(list.setOrError(9, 99), error.OutOfBounds);
assertError(list.setOrError(10, 123), error.OutOfBounds);
assert(list.pop() == 42);
assert(list.pop() == 33);
}
test "insert ArrayList test" {
test "std.ArrayList.swapRemove" {
var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit();
try list.append(1);
try list.append(2);
try list.append(3);
try list.append(4);
try list.append(5);
try list.append(6);
try list.append(7);
//remove from middle
assert(list.swapRemove(3) == 4);
assert(list.at(3) == 7);
assert(list.len == 6);
//remove from end
assert(list.swapRemove(5) == 6);
assert(list.len == 5);
//remove from front
assert(list.swapRemove(0) == 1);
assert(list.at(0) == 5);
assert(list.len == 4);
}
test "std.ArrayList.swapRemoveOrError" {
var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit();
// Test just after initialization
assertError(list.swapRemoveOrError(0), error.OutOfBounds);
// Test after adding one item and remote it
try list.append(1);
assert((try list.swapRemoveOrError(0)) == 1);
assertError(list.swapRemoveOrError(0), error.OutOfBounds);
// Test after adding two items and remote both
try list.append(1);
try list.append(2);
assert((try list.swapRemoveOrError(1)) == 2);
assert((try list.swapRemoveOrError(0)) == 1);
assertError(list.swapRemoveOrError(0), error.OutOfBounds);
// Test out of bounds with one item
try list.append(1);
assertError(list.swapRemoveOrError(1), error.OutOfBounds);
// Test out of bounds with two items
try list.append(2);
assertError(list.swapRemoveOrError(2), error.OutOfBounds);
}
test "std.ArrayList.iterator" {
var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit();
try list.append(1);
try list.append(2);
try list.append(3);
var count: i32 = 0;
var it = list.iterator();
while (it.next()) |next| {
assert(next == count + 1);
count += 1;
}
assert(count == 3);
assert(it.next() == null);
it.reset();
count = 0;
while (it.next()) |next| {
assert(next == count + 1);
count += 1;
if (count == 2) break;
}
it.reset();
assert(it.next().? == 1);
}
test "std.ArrayList.insert" {
var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit();
try list.append(1);
try list.append(2);
try list.append(3);
try list.insert(0, 5);
assert(list.items[0] == 5);
assert(list.items[1] == 1);
assert(list.items[2] == 2);
assert(list.items[3] == 3);
}
try list.insertSlice(1, []const i32 { 9, 8 });
assert(list.items[0] == 5);
test "std.ArrayList.insertSlice" {
var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit();
try list.append(1);
try list.append(2);
try list.append(3);
try list.append(4);
try list.insertSlice(1, []const i32{
9,
8,
});
assert(list.items[0] == 1);
assert(list.items[1] == 9);
assert(list.items[2] == 8);
assert(list.items[3] == 2);
assert(list.items[4] == 3);
assert(list.items[5] == 4);
const items = []const i32 { 1 };
const items = []const i32{1};
try list.insertSlice(0, items[0..0]);
assert(list.items[0] == 5);
assert(list.len == 6);
assert(list.items[0] == 1);
}

9
std/atomic/index.zig Normal file
View File

@@ -0,0 +1,9 @@
pub const Stack = @import("stack.zig").Stack;
pub const Queue = @import("queue.zig").Queue;
pub const Int = @import("int.zig").Int;
test "std.atomic" {
_ = @import("stack.zig");
_ = @import("queue.zig");
_ = @import("int.zig");
}

33
std/atomic/int.zig Normal file
View File

@@ -0,0 +1,33 @@
const builtin = @import("builtin");
const AtomicOrder = builtin.AtomicOrder;
/// Thread-safe, lock-free integer
pub fn Int(comptime T: type) type {
return struct {
unprotected_value: T,
pub const Self = this;
pub fn init(init_val: T) Self {
return Self{ .unprotected_value = init_val };
}
/// Returns previous value
pub fn incr(self: *Self) T {
return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
}
/// Returns previous value
pub fn decr(self: *Self) T {
return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
}
pub fn get(self: *Self) T {
return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst);
}
pub fn xchg(self: *Self, new_value: T) T {
return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Xchg, new_value, AtomicOrder.SeqCst);
}
};
}

240
std/atomic/queue.zig Normal file
View File

@@ -0,0 +1,240 @@
const builtin = @import("builtin");
const AtomicOrder = builtin.AtomicOrder;
const AtomicRmwOp = builtin.AtomicRmwOp;
/// Many producer, many consumer, non-allocating, thread-safe.
/// Uses a spinlock to protect get() and put().
pub fn Queue(comptime T: type) type {
return struct {
head: ?*Node,
tail: ?*Node,
lock: u8,
pub const Self = this;
pub const Node = struct {
next: ?*Node,
data: T,
};
pub fn init() Self {
return Self{
.head = null,
.tail = null,
.lock = 0,
};
}
pub fn put(self: *Self, node: *Node) void {
node.next = null;
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
const opt_tail = self.tail;
self.tail = node;
if (opt_tail) |tail| {
tail.next = node;
} else {
assert(self.head == null);
self.head = node;
}
}
pub fn get(self: *Self) ?*Node {
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
const head = self.head orelse return null;
self.head = head.next;
if (head.next == null) self.tail = null;
return head;
}
pub fn unget(self: *Self, node: *Node) void {
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
const opt_head = self.head;
self.head = node;
if (opt_head) |head| {
head.next = node;
} else {
assert(self.tail == null);
self.tail = node;
}
}
pub fn isEmpty(self: *Self) bool {
return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null;
}
pub fn dump(self: *Self) void {
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
std.debug.warn("head: ");
dumpRecursive(self.head, 0);
std.debug.warn("tail: ");
dumpRecursive(self.tail, 0);
}
fn dumpRecursive(optional_node: ?*Node, indent: usize) void {
var stderr_file = std.io.getStdErr() catch return;
const stderr = &std.io.FileOutStream.init(&stderr_file).stream;
stderr.writeByteNTimes(' ', indent) catch return;
if (optional_node) |node| {
std.debug.warn("0x{x}={}\n", @ptrToInt(node), node.data);
dumpRecursive(node.next, indent + 1);
} else {
std.debug.warn("(null)\n");
}
}
};
}
const std = @import("../index.zig");
const assert = std.debug.assert;
const Context = struct {
allocator: *std.mem.Allocator,
queue: *Queue(i32),
put_sum: isize,
get_sum: isize,
get_count: usize,
puts_done: u8, // TODO make this a bool
};
// TODO add lazy evaluated build options and then put puts_per_thread behind
// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor
// CI we would use a less aggressive setting since at 1 core, while we still
// want this test to pass, we need a smaller value since there is so much thrashing
// we would also use a less aggressive setting when running in valgrind
const puts_per_thread = 500;
const put_thread_count = 3;
test "std.atomic.Queue" {
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
defer direct_allocator.allocator.free(plenty_of_memory);
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
var a = &fixed_buffer_allocator.allocator;
var queue = Queue(i32).init();
var context = Context{
.allocator = a,
.queue = &queue,
.put_sum = 0,
.get_sum = 0,
.puts_done = 0,
.get_count = 0,
};
var putters: [put_thread_count]*std.os.Thread = undefined;
for (putters) |*t| {
t.* = try std.os.spawnThread(&context, startPuts);
}
var getters: [put_thread_count]*std.os.Thread = undefined;
for (getters) |*t| {
t.* = try std.os.spawnThread(&context, startGets);
}
for (putters) |t|
t.wait();
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
for (getters) |t|
t.wait();
if (context.put_sum != context.get_sum) {
std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
}
if (context.get_count != puts_per_thread * put_thread_count) {
std.debug.panic(
"failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}",
context.get_count,
u32(puts_per_thread),
u32(put_thread_count),
);
}
}
fn startPuts(ctx: *Context) u8 {
var put_count: usize = puts_per_thread;
var r = std.rand.DefaultPrng.init(0xdeadbeef);
while (put_count != 0) : (put_count -= 1) {
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
const x = @bitCast(i32, r.random.scalar(u32));
const node = ctx.allocator.create(Queue(i32).Node{
.next = undefined,
.data = x,
}) catch unreachable;
ctx.queue.put(node);
_ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
}
return 0;
}
fn startGets(ctx: *Context) u8 {
while (true) {
const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1;
while (ctx.queue.get()) |node| {
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
_ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
_ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
}
if (last) return 0;
}
}
test "std.atomic.Queue single-threaded" {
var queue = Queue(i32).init();
var node_0 = Queue(i32).Node{
.data = 0,
.next = undefined,
};
queue.put(&node_0);
var node_1 = Queue(i32).Node{
.data = 1,
.next = undefined,
};
queue.put(&node_1);
assert(queue.get().?.data == 0);
var node_2 = Queue(i32).Node{
.data = 2,
.next = undefined,
};
queue.put(&node_2);
var node_3 = Queue(i32).Node{
.data = 3,
.next = undefined,
};
queue.put(&node_3);
assert(queue.get().?.data == 1);
assert(queue.get().?.data == 2);
var node_4 = Queue(i32).Node{
.data = 4,
.next = undefined,
};
queue.put(&node_4);
assert(queue.get().?.data == 3);
node_3.next = null;
assert(queue.get().?.data == 4);
assert(queue.get() == null);
}

150
std/atomic/stack.zig Normal file
View File

@@ -0,0 +1,150 @@
const assert = std.debug.assert;
const builtin = @import("builtin");
const AtomicOrder = builtin.AtomicOrder;
/// Many reader, many writer, non-allocating, thread-safe
/// Uses a spinlock to protect push() and pop()
pub fn Stack(comptime T: type) type {
return struct {
root: ?*Node,
lock: u8,
pub const Self = this;
pub const Node = struct {
next: ?*Node,
data: T,
};
pub fn init() Self {
return Self{
.root = null,
.lock = 0,
};
}
/// push operation, but only if you are the first item in the stack. if you did not succeed in
/// being the first item in the stack, returns the other item that was there.
pub fn pushFirst(self: *Self, node: *Node) ?*Node {
node.next = null;
return @cmpxchgStrong(?*Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst);
}
pub fn push(self: *Self, node: *Node) void {
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
node.next = self.root;
self.root = node;
}
pub fn pop(self: *Self) ?*Node {
while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
const root = self.root orelse return null;
self.root = root.next;
return root;
}
pub fn isEmpty(self: *Self) bool {
return @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst) == null;
}
};
}
const std = @import("../index.zig");
const Context = struct {
allocator: *std.mem.Allocator,
stack: *Stack(i32),
put_sum: isize,
get_sum: isize,
get_count: usize,
puts_done: u8, // TODO make this a bool
};
// TODO add lazy evaluated build options and then put puts_per_thread behind
// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor
// CI we would use a less aggressive setting since at 1 core, while we still
// want this test to pass, we need a smaller value since there is so much thrashing
// we would also use a less aggressive setting when running in valgrind
const puts_per_thread = 500;
const put_thread_count = 3;
test "std.atomic.stack" {
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
defer direct_allocator.allocator.free(plenty_of_memory);
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
var a = &fixed_buffer_allocator.allocator;
var stack = Stack(i32).init();
var context = Context{
.allocator = a,
.stack = &stack,
.put_sum = 0,
.get_sum = 0,
.puts_done = 0,
.get_count = 0,
};
var putters: [put_thread_count]*std.os.Thread = undefined;
for (putters) |*t| {
t.* = try std.os.spawnThread(&context, startPuts);
}
var getters: [put_thread_count]*std.os.Thread = undefined;
for (getters) |*t| {
t.* = try std.os.spawnThread(&context, startGets);
}
for (putters) |t|
t.wait();
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
for (getters) |t|
t.wait();
if (context.put_sum != context.get_sum) {
std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
}
if (context.get_count != puts_per_thread * put_thread_count) {
std.debug.panic(
"failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}",
context.get_count,
u32(puts_per_thread),
u32(put_thread_count),
);
}
}
fn startPuts(ctx: *Context) u8 {
var put_count: usize = puts_per_thread;
var r = std.rand.DefaultPrng.init(0xdeadbeef);
while (put_count != 0) : (put_count -= 1) {
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
const x = @bitCast(i32, r.random.scalar(u32));
const node = ctx.allocator.create(Stack(i32).Node{
.next = undefined,
.data = x,
}) catch unreachable;
ctx.stack.push(node);
_ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
}
return 0;
}
fn startGets(ctx: *Context) u8 {
while (true) {
const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1;
while (ctx.stack.pop()) |node| {
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
_ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
_ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
}
if (last) return 0;
}
}

View File

@@ -32,7 +32,7 @@ pub const Base64Encoder = struct {
}
/// dest.len must be what you get from ::calcSize.
pub fn encode(encoder: &const Base64Encoder, dest: []u8, source: []const u8) void {
pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) void {
assert(dest.len == Base64Encoder.calcSize(source.len));
var i: usize = 0;
@@ -41,12 +41,10 @@ pub const Base64Encoder = struct {
dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
out_index += 1;
dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) |
((source[i + 1] & 0xf0) >> 4)];
dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)];
out_index += 1;
dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) |
((source[i + 2] & 0xc0) >> 6)];
dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) | ((source[i + 2] & 0xc0) >> 6)];
out_index += 1;
dest[out_index] = encoder.alphabet_chars[source[i + 2] & 0x3f];
@@ -64,8 +62,7 @@ pub const Base64Encoder = struct {
dest[out_index] = encoder.pad_char;
out_index += 1;
} else {
dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) |
((source[i + 1] & 0xf0) >> 4)];
dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)];
out_index += 1;
dest[out_index] = encoder.alphabet_chars[(source[i + 1] & 0xf) << 2];
@@ -84,6 +81,7 @@ pub const Base64Decoder = struct {
/// e.g. 'A' => 0.
/// undefined for any value not in the 64 alphabet chars.
char_to_index: [256]u8,
/// true only for the 64 chars in the alphabet, not the pad char.
char_in_alphabet: [256]bool,
pad_char: u8,
@@ -101,7 +99,7 @@ pub const Base64Decoder = struct {
assert(!result.char_in_alphabet[c]);
assert(c != pad_char);
result.char_to_index[c] = u8(i);
result.char_to_index[c] = @intCast(u8, i);
result.char_in_alphabet[c] = true;
}
@@ -109,7 +107,7 @@ pub const Base64Decoder = struct {
}
/// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.
pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) !usize {
pub fn calcSize(decoder: *const Base64Decoder, source: []const u8) !usize {
if (source.len % 4 != 0) return error.InvalidPadding;
return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
}
@@ -117,7 +115,7 @@ pub const Base64Decoder = struct {
/// dest.len must be what you get from ::calcSize.
/// invalid characters result in error.InvalidCharacter.
/// invalid padding results in error.InvalidPadding.
pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) !void {
pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) !void {
assert(dest.len == (decoder.calcSize(source) catch unreachable));
assert(source.len % 4 == 0);
@@ -131,26 +129,20 @@ pub const Base64Decoder = struct {
// common case
if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter;
dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
decoder.char_to_index[source[src_cursor + 1]] >> 4;
dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 |
decoder.char_to_index[source[src_cursor + 2]] >> 2;
dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 |
decoder.char_to_index[source[src_cursor + 3]];
dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;
dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2;
dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 | decoder.char_to_index[source[src_cursor + 3]];
dest_cursor += 3;
} else if (source[src_cursor + 2] != decoder.pad_char) {
// one pad char
if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
decoder.char_to_index[source[src_cursor + 1]] >> 4;
dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 |
decoder.char_to_index[source[src_cursor + 2]] >> 2;
dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;
dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2;
if (decoder.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding;
dest_cursor += 2;
} else {
// two pad chars
dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
decoder.char_to_index[source[src_cursor + 1]] >> 4;
dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;
if (decoder.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding;
dest_cursor += 1;
}
@@ -165,7 +157,7 @@ pub const Base64DecoderWithIgnore = struct {
decoder: Base64Decoder,
char_is_ignored: [256]bool,
pub fn init(alphabet_chars: []const u8, pad_char: u8, ignore_chars: []const u8) Base64DecoderWithIgnore {
var result = Base64DecoderWithIgnore {
var result = Base64DecoderWithIgnore{
.decoder = Base64Decoder.init(alphabet_chars, pad_char),
.char_is_ignored = []bool{false} ** 256,
};
@@ -189,7 +181,7 @@ pub const Base64DecoderWithIgnore = struct {
/// Invalid padding results in error.InvalidPadding.
/// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.
/// Returns the number of bytes writen to dest.
pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) !usize {
pub fn decode(decoder_with_ignore: *const Base64DecoderWithIgnore, dest: []u8, source: []const u8) !usize {
const decoder = &decoder_with_ignore.decoder;
var src_cursor: usize = 0;
@@ -223,10 +215,12 @@ pub const Base64DecoderWithIgnore = struct {
} else if (decoder_with_ignore.char_is_ignored[c]) {
// we can even ignore chars during the padding
continue;
} else return error.InvalidCharacter;
} else
return error.InvalidCharacter;
}
break;
} else return error.InvalidCharacter;
} else
return error.InvalidCharacter;
}
switch (available_chars) {
@@ -234,22 +228,17 @@ pub const Base64DecoderWithIgnore = struct {
// common case
if (dest_cursor + 3 > dest.len) return error.OutputTooSmall;
assert(pad_char_count == 0);
dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
decoder.char_to_index[next_4_chars[1]] >> 4;
dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 |
decoder.char_to_index[next_4_chars[2]] >> 2;
dest[dest_cursor + 2] = decoder.char_to_index[next_4_chars[2]] << 6 |
decoder.char_to_index[next_4_chars[3]];
dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;
dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | decoder.char_to_index[next_4_chars[2]] >> 2;
dest[dest_cursor + 2] = decoder.char_to_index[next_4_chars[2]] << 6 | decoder.char_to_index[next_4_chars[3]];
dest_cursor += 3;
continue;
},
3 => {
if (dest_cursor + 2 > dest.len) return error.OutputTooSmall;
if (pad_char_count != 1) return error.InvalidPadding;
dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
decoder.char_to_index[next_4_chars[1]] >> 4;
dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 |
decoder.char_to_index[next_4_chars[2]] >> 2;
dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;
dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | decoder.char_to_index[next_4_chars[2]] >> 2;
if (decoder.char_to_index[next_4_chars[2]] << 6 != 0) return error.InvalidPadding;
dest_cursor += 2;
break;
@@ -257,8 +246,7 @@ pub const Base64DecoderWithIgnore = struct {
2 => {
if (dest_cursor + 1 > dest.len) return error.OutputTooSmall;
if (pad_char_count != 2) return error.InvalidPadding;
dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
decoder.char_to_index[next_4_chars[1]] >> 4;
dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;
if (decoder.char_to_index[next_4_chars[1]] << 4 != 0) return error.InvalidPadding;
dest_cursor += 1;
break;
@@ -280,7 +268,6 @@ pub const Base64DecoderWithIgnore = struct {
}
};
pub const standard_decoder_unsafe = Base64DecoderUnsafe.init(standard_alphabet_chars, standard_pad_char);
pub const Base64DecoderUnsafe = struct {
@@ -291,25 +278,25 @@ pub const Base64DecoderUnsafe = struct {
pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64DecoderUnsafe {
assert(alphabet_chars.len == 64);
var result = Base64DecoderUnsafe {
var result = Base64DecoderUnsafe{
.char_to_index = undefined,
.pad_char = pad_char,
};
for (alphabet_chars) |c, i| {
assert(c != pad_char);
result.char_to_index[c] = u8(i);
result.char_to_index[c] = @intCast(u8, i);
}
return result;
}
/// The source buffer must be valid.
pub fn calcSize(decoder: &const Base64DecoderUnsafe, source: []const u8) usize {
pub fn calcSize(decoder: *const Base64DecoderUnsafe, source: []const u8) usize {
return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
}
/// dest.len must be what you get from ::calcDecodedSizeExactUnsafe.
/// invalid characters or padding will result in undefined values.
pub fn decode(decoder: &const Base64DecoderUnsafe, dest: []u8, source: []const u8) void {
pub fn decode(decoder: *const Base64DecoderUnsafe, dest: []u8, source: []const u8) void {
assert(dest.len == decoder.calcSize(source));
var src_index: usize = 0;
@@ -321,16 +308,13 @@ pub const Base64DecoderUnsafe = struct {
}
while (in_buf_len > 4) {
dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 |
decoder.char_to_index[source[src_index + 1]] >> 4;
dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4;
dest_index += 1;
dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 |
decoder.char_to_index[source[src_index + 2]] >> 2;
dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2;
dest_index += 1;
dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 |
decoder.char_to_index[source[src_index + 3]];
dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]];
dest_index += 1;
src_index += 4;
@@ -338,18 +322,15 @@ pub const Base64DecoderUnsafe = struct {
}
if (in_buf_len > 1) {
dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 |
decoder.char_to_index[source[src_index + 1]] >> 4;
dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4;
dest_index += 1;
}
if (in_buf_len > 2) {
dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 |
decoder.char_to_index[source[src_index + 2]] >> 2;
dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2;
dest_index += 1;
}
if (in_buf_len > 3) {
dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 |
decoder.char_to_index[source[src_index + 3]];
dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]];
dest_index += 1;
}
}
@@ -367,7 +348,6 @@ fn calcDecodedSizeExactUnsafe(source: []const u8, pad_char: u8) usize {
return result;
}
test "base64" {
@setEvalBranchQuota(8000);
testBase64() catch unreachable;
@@ -375,26 +355,26 @@ test "base64" {
}
fn testBase64() !void {
try testAllApis("", "");
try testAllApis("f", "Zg==");
try testAllApis("fo", "Zm8=");
try testAllApis("foo", "Zm9v");
try testAllApis("foob", "Zm9vYg==");
try testAllApis("fooba", "Zm9vYmE=");
try testAllApis("", "");
try testAllApis("f", "Zg==");
try testAllApis("fo", "Zm8=");
try testAllApis("foo", "Zm9v");
try testAllApis("foob", "Zm9vYg==");
try testAllApis("fooba", "Zm9vYmE=");
try testAllApis("foobar", "Zm9vYmFy");
try testDecodeIgnoreSpace("", " ");
try testDecodeIgnoreSpace("f", "Z g= =");
try testDecodeIgnoreSpace("fo", " Zm8=");
try testDecodeIgnoreSpace("foo", "Zm9v ");
try testDecodeIgnoreSpace("foob", "Zm9vYg = = ");
try testDecodeIgnoreSpace("fooba", "Zm9v YmE=");
try testDecodeIgnoreSpace("", " ");
try testDecodeIgnoreSpace("f", "Z g= =");
try testDecodeIgnoreSpace("fo", " Zm8=");
try testDecodeIgnoreSpace("foo", "Zm9v ");
try testDecodeIgnoreSpace("foob", "Zm9vYg = = ");
try testDecodeIgnoreSpace("fooba", "Zm9v YmE=");
try testDecodeIgnoreSpace("foobar", " Z m 9 v Y m F y ");
// test getting some api errors
try testError("A", error.InvalidPadding);
try testError("AA", error.InvalidPadding);
try testError("AAA", error.InvalidPadding);
try testError("A", error.InvalidPadding);
try testError("AA", error.InvalidPadding);
try testError("AAA", error.InvalidPadding);
try testError("A..A", error.InvalidCharacter);
try testError("AA=A", error.InvalidCharacter);
try testError("AA/=", error.InvalidPadding);
@@ -427,8 +407,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void
// Base64DecoderWithIgnore
{
const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(
standard_alphabet_chars, standard_pad_char, "");
const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, "");
var buffer: [0x100]u8 = undefined;
var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];
var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded);
@@ -446,8 +425,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void
}
fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !void {
const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
standard_alphabet_chars, standard_pad_char, " ");
const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");
var buffer: [0x100]u8 = undefined;
var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
var written = try standard_decoder_ignore_space.decode(decoded, encoded);
@@ -455,8 +433,7 @@ fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !voi
}
fn testError(encoded: []const u8, expected_err: error) !void {
const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
standard_alphabet_chars, standard_pad_char, " ");
const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");
var buffer: [0x100]u8 = undefined;
if (standard_decoder.calcSize(encoded)) |decoded_size| {
var decoded = buffer[0..decoded_size];
@@ -471,10 +448,9 @@ fn testError(encoded: []const u8, expected_err: error) !void {
}
fn testOutputTooSmallError(encoded: []const u8) !void {
const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
standard_alphabet_chars, standard_pad_char, " ");
const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");
var buffer: [0x100]u8 = undefined;
var decoded = buffer[0..calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1];
var decoded = buffer[0 .. calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1];
if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| {
return error.ExpectedError;
} else |err| if (err != error.OutputTooSmall) return err;

View File

@@ -11,17 +11,15 @@ pub const BufMap = struct {
const BufMapHashMap = HashMap([]const u8, []const u8, mem.hash_slice_u8, mem.eql_slice_u8);
pub fn init(allocator: &Allocator) BufMap {
var self = BufMap {
.hash_map = BufMapHashMap.init(allocator),
};
pub fn init(allocator: *Allocator) BufMap {
var self = BufMap{ .hash_map = BufMapHashMap.init(allocator) };
return self;
}
pub fn deinit(self: &BufMap) void {
pub fn deinit(self: *const BufMap) void {
var it = self.hash_map.iterator();
while (true) {
const entry = it.next() ?? break;
const entry = it.next() orelse break;
self.free(entry.key);
self.free(entry.value);
}
@@ -29,7 +27,7 @@ pub const BufMap = struct {
self.hash_map.deinit();
}
pub fn set(self: &BufMap, key: []const u8, value: []const u8) !void {
pub fn set(self: *BufMap, key: []const u8, value: []const u8) !void {
self.delete(key);
const key_copy = try self.copy(key);
errdefer self.free(key_copy);
@@ -38,30 +36,30 @@ pub const BufMap = struct {
_ = try self.hash_map.put(key_copy, value_copy);
}
pub fn get(self: &BufMap, key: []const u8) ?[]const u8 {
const entry = self.hash_map.get(key) ?? return null;
pub fn get(self: *const BufMap, key: []const u8) ?[]const u8 {
const entry = self.hash_map.get(key) orelse return null;
return entry.value;
}
pub fn delete(self: &BufMap, key: []const u8) void {
const entry = self.hash_map.remove(key) ?? return;
pub fn delete(self: *BufMap, key: []const u8) void {
const entry = self.hash_map.remove(key) orelse return;
self.free(entry.key);
self.free(entry.value);
}
pub fn count(self: &const BufMap) usize {
return self.hash_map.size;
pub fn count(self: *const BufMap) usize {
return self.hash_map.count();
}
pub fn iterator(self: &const BufMap) BufMapHashMap.Iterator {
pub fn iterator(self: *const BufMap) BufMapHashMap.Iterator {
return self.hash_map.iterator();
}
fn free(self: &BufMap, value: []const u8) void {
fn free(self: *const BufMap, value: []const u8) void {
self.hash_map.allocator.free(value);
}
fn copy(self: &BufMap, value: []const u8) ![]const u8 {
fn copy(self: *const BufMap, value: []const u8) ![]const u8 {
return mem.dupe(self.hash_map.allocator, u8, value);
}
};
@@ -74,15 +72,15 @@ test "BufMap" {
defer bufmap.deinit();
try bufmap.set("x", "1");
assert(mem.eql(u8, ??bufmap.get("x"), "1"));
assert(mem.eql(u8, bufmap.get("x").?, "1"));
assert(1 == bufmap.count());
try bufmap.set("x", "2");
assert(mem.eql(u8, ??bufmap.get("x"), "2"));
assert(mem.eql(u8, bufmap.get("x").?, "2"));
assert(1 == bufmap.count());
try bufmap.set("x", "3");
assert(mem.eql(u8, ??bufmap.get("x"), "3"));
assert(mem.eql(u8, bufmap.get("x").?, "3"));
assert(1 == bufmap.count());
bufmap.delete("x");

View File

@@ -1,30 +1,30 @@
const std = @import("index.zig");
const HashMap = @import("hash_map.zig").HashMap;
const mem = @import("mem.zig");
const Allocator = mem.Allocator;
const assert = std.debug.assert;
pub const BufSet = struct {
hash_map: BufSetHashMap,
const BufSetHashMap = HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
pub fn init(a: &Allocator) BufSet {
var self = BufSet {
.hash_map = BufSetHashMap.init(a),
};
pub fn init(a: *Allocator) BufSet {
var self = BufSet{ .hash_map = BufSetHashMap.init(a) };
return self;
}
pub fn deinit(self: &BufSet) void {
pub fn deinit(self: *const BufSet) void {
var it = self.hash_map.iterator();
while (true) {
const entry = it.next() ?? break;
const entry = it.next() orelse break;
self.free(entry.key);
}
self.hash_map.deinit();
}
pub fn put(self: &BufSet, key: []const u8) !void {
pub fn put(self: *BufSet, key: []const u8) !void {
if (self.hash_map.get(key) == null) {
const key_copy = try self.copy(key);
errdefer self.free(key_copy);
@@ -32,31 +32,47 @@ pub const BufSet = struct {
}
}
pub fn delete(self: &BufSet, key: []const u8) void {
const entry = self.hash_map.remove(key) ?? return;
pub fn delete(self: *BufSet, key: []const u8) void {
const entry = self.hash_map.remove(key) orelse return;
self.free(entry.key);
}
pub fn count(self: &const BufSet) usize {
return self.hash_map.size;
pub fn count(self: *const BufSet) usize {
return self.hash_map.count();
}
pub fn iterator(self: &const BufSet) BufSetHashMap.Iterator {
pub fn iterator(self: *const BufSet) BufSetHashMap.Iterator {
return self.hash_map.iterator();
}
pub fn allocator(self: &const BufSet) &Allocator {
pub fn allocator(self: *const BufSet) *Allocator {
return self.hash_map.allocator;
}
fn free(self: &BufSet, value: []const u8) void {
fn free(self: *const BufSet, value: []const u8) void {
self.hash_map.allocator.free(value);
}
fn copy(self: &BufSet, value: []const u8) ![]const u8 {
fn copy(self: *const BufSet, value: []const u8) ![]const u8 {
const result = try self.hash_map.allocator.alloc(u8, value.len);
mem.copy(u8, result, value);
return result;
}
};
test "BufSet" {
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
var bufset = BufSet.init(&direct_allocator.allocator);
defer bufset.deinit();
try bufset.put("x");
assert(bufset.count() == 1);
bufset.delete("x");
assert(bufset.count() == 0);
try bufset.put("x");
try bufset.put("y");
try bufset.put("z");
}

View File

@@ -5,21 +5,19 @@ const Allocator = mem.Allocator;
const assert = debug.assert;
const ArrayList = std.ArrayList;
const fmt = std.fmt;
/// A buffer that allocates memory and maintains a null byte at the end.
pub const Buffer = struct {
list: ArrayList(u8),
/// Must deinitialize with deinit.
pub fn init(allocator: &Allocator, m: []const u8) !Buffer {
pub fn init(allocator: *Allocator, m: []const u8) !Buffer {
var self = try initSize(allocator, m.len);
mem.copy(u8, self.list.items, m);
return self;
}
/// Must deinitialize with deinit.
pub fn initSize(allocator: &Allocator, size: usize) !Buffer {
pub fn initSize(allocator: *Allocator, size: usize) !Buffer {
var self = initNull(allocator);
try self.resize(size);
return self;
@@ -28,122 +26,113 @@ pub const Buffer = struct {
/// Must deinitialize with deinit.
/// None of the other operations are valid until you do one of these:
/// * ::replaceContents
/// * ::replaceContentsBuffer
/// * ::resize
pub fn initNull(allocator: &Allocator) Buffer {
return Buffer {
.list = ArrayList(u8).init(allocator),
};
pub fn initNull(allocator: *Allocator) Buffer {
return Buffer{ .list = ArrayList(u8).init(allocator) };
}
/// Must deinitialize with deinit.
pub fn initFromBuffer(buffer: &const Buffer) !Buffer {
pub fn initFromBuffer(buffer: *const Buffer) !Buffer {
return Buffer.init(buffer.list.allocator, buffer.toSliceConst());
}
/// Buffer takes ownership of the passed in slice. The slice must have been
/// allocated with `allocator`.
/// Must deinitialize with deinit.
pub fn fromOwnedSlice(allocator: &Allocator, slice: []u8) Buffer {
var self = Buffer {
.list = ArrayList(u8).fromOwnedSlice(allocator, slice),
};
self.list.append(0);
pub fn fromOwnedSlice(allocator: *Allocator, slice: []u8) !Buffer {
var self = Buffer{ .list = ArrayList(u8).fromOwnedSlice(allocator, slice) };
try self.list.append(0);
return self;
}
/// The caller owns the returned memory. The Buffer becomes null and
/// is safe to `deinit`.
pub fn toOwnedSlice(self: &Buffer) []u8 {
pub fn toOwnedSlice(self: *Buffer) []u8 {
const allocator = self.list.allocator;
const result = allocator.shrink(u8, self.list.items, self.len());
*self = initNull(allocator);
self.* = initNull(allocator);
return result;
}
pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: ...) !Buffer {
const countSize = struct {
fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
size.* += bytes.len;
}
}.countSize;
var size: usize = 0;
std.fmt.format(&size, error{}, countSize, format, args) catch |err| switch (err) {};
var self = try Buffer.initSize(allocator, size);
assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size);
return self;
}
pub fn deinit(self: &Buffer) void {
pub fn deinit(self: *Buffer) void {
self.list.deinit();
}
pub fn toSlice(self: &Buffer) []u8 {
pub fn toSlice(self: *const Buffer) []u8 {
return self.list.toSlice()[0..self.len()];
}
pub fn toSliceConst(self: &const Buffer) []const u8 {
pub fn toSliceConst(self: *const Buffer) []const u8 {
return self.list.toSliceConst()[0..self.len()];
}
pub fn shrink(self: &Buffer, new_len: usize) void {
pub fn shrink(self: *Buffer, new_len: usize) void {
assert(new_len <= self.len());
self.list.shrink(new_len + 1);
self.list.items[self.len()] = 0;
}
pub fn resize(self: &Buffer, new_len: usize) !void {
pub fn resize(self: *Buffer, new_len: usize) !void {
try self.list.resize(new_len + 1);
self.list.items[self.len()] = 0;
}
pub fn isNull(self: &const Buffer) bool {
pub fn isNull(self: *const Buffer) bool {
return self.list.len == 0;
}
pub fn len(self: &const Buffer) usize {
pub fn len(self: *const Buffer) usize {
return self.list.len - 1;
}
pub fn append(self: &Buffer, m: []const u8) !void {
pub fn append(self: *Buffer, m: []const u8) !void {
const old_len = self.len();
try self.resize(old_len + m.len);
mem.copy(u8, self.list.toSlice()[old_len..], m);
}
// TODO: remove, use OutStream for this
pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) !void {
return fmt.format(self, append, format, args);
pub fn appendByte(self: *Buffer, byte: u8) !void {
const old_len = self.len();
try self.resize(old_len + 1);
self.list.toSlice()[old_len] = byte;
}
// TODO: remove, use OutStream for this
pub fn appendByte(self: &Buffer, byte: u8) !void {
return self.appendByteNTimes(byte, 1);
}
// TODO: remove, use OutStream for this
pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) !void {
var prev_size: usize = self.len();
const new_size = prev_size + count;
try self.resize(new_size);
var i: usize = prev_size;
while (i < new_size) : (i += 1) {
self.list.items[i] = byte;
}
}
pub fn eql(self: &const Buffer, m: []const u8) bool {
pub fn eql(self: *const Buffer, m: []const u8) bool {
return mem.eql(u8, self.toSliceConst(), m);
}
pub fn startsWith(self: &const Buffer, m: []const u8) bool {
pub fn startsWith(self: *const Buffer, m: []const u8) bool {
if (self.len() < m.len) return false;
return mem.eql(u8, self.list.items[0..m.len], m);
}
pub fn endsWith(self: &const Buffer, m: []const u8) bool {
pub fn endsWith(self: *const Buffer, m: []const u8) bool {
const l = self.len();
if (l < m.len) return false;
const start = l - m.len;
return mem.eql(u8, self.list.items[start..l], m);
}
pub fn replaceContents(self: &const Buffer, m: []const u8) !void {
pub fn replaceContents(self: *Buffer, m: []const u8) !void {
try self.resize(m.len);
mem.copy(u8, self.list.toSlice(), m);
}
/// For passing to C functions.
pub fn ptr(self: &const Buffer) &u8 {
pub fn ptr(self: *const Buffer) [*]u8 {
return self.list.items.ptr;
}
};
@@ -154,7 +143,7 @@ test "simple Buffer" {
var buf = try Buffer.init(debug.global_allocator, "");
assert(buf.len() == 0);
try buf.append("hello");
try buf.appendByte(' ');
try buf.append(" ");
try buf.append("world");
assert(buf.eql("hello world"));
assert(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst()));
@@ -166,5 +155,5 @@ test "simple Buffer" {
assert(buf.endsWith("orld"));
try buf2.resize(4);
assert(buf.startsWith(buf2.toSliceConst()));
assert(buf.startsWith(buf2.toSlice()));
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,54 @@
extern "c" fn __error() &c_int;
pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int;
extern "c" fn __error() *c_int;
pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int;
pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize;
pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize;
pub use @import("../os/darwin_errno.zig");
pub extern "c" fn mach_absolute_time() u64;
pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;
pub extern "c" fn kqueue() c_int;
pub extern "c" fn kevent(
kq: c_int,
changelist: [*]const Kevent,
nchanges: c_int,
eventlist: [*]Kevent,
nevents: c_int,
timeout: ?*const timespec,
) c_int;
pub extern "c" fn kevent64(
kq: c_int,
changelist: [*]const kevent64_s,
nchanges: c_int,
eventlist: [*]kevent64_s,
nevents: c_int,
flags: c_uint,
timeout: ?*const timespec,
) c_int;
pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int;
pub use @import("../os/darwin/errno.zig");
pub const _errno = __error;
pub const timeval = extern struct {
tv_sec: isize,
tv_usec: isize,
};
pub const timezone = extern struct {
tz_minuteswest: i32,
tz_dsttime: i32,
};
pub const mach_timebase_info_data = extern struct {
numer: u32,
denom: u32,
};
/// Renamed to Stat to not conflict with the stat function.
pub const Stat = extern struct {
dev: i32,
@@ -42,7 +84,7 @@ pub const sigset_t = u32;
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name.
pub const Sigaction = extern struct {
handler: extern fn(c_int)void,
handler: extern fn (c_int) void,
sa_mask: sigset_t,
sa_flags: c_int,
};
@@ -63,3 +105,56 @@ pub const sockaddr = extern struct {
};
pub const sa_family_t = u8;
pub const pthread_attr_t = extern struct {
__sig: c_long,
__opaque: [56]u8,
};
/// Renamed from `kevent` to `Kevent` to avoid conflict with function name.
pub const Kevent = extern struct {
ident: usize,
filter: i16,
flags: u16,
fflags: u32,
data: isize,
udata: usize,
};
// sys/types.h on macos uses #pragma pack(4) so these checks are
// to make sure the struct is laid out the same. These values were
// produced from C code using the offsetof macro.
const std = @import("../index.zig");
const assert = std.debug.assert;
comptime {
assert(@offsetOf(Kevent, "ident") == 0);
assert(@offsetOf(Kevent, "filter") == 8);
assert(@offsetOf(Kevent, "flags") == 10);
assert(@offsetOf(Kevent, "fflags") == 12);
assert(@offsetOf(Kevent, "data") == 16);
assert(@offsetOf(Kevent, "udata") == 24);
}
pub const kevent64_s = extern struct {
ident: u64,
filter: i16,
flags: u16,
fflags: u32,
data: i64,
udata: u64,
ext: [2]u64,
};
// sys/types.h on macos uses #pragma pack() so these checks are
// to make sure the struct is laid out the same. These values were
// produced from C code using the offsetof macro.
comptime {
assert(@offsetOf(kevent64_s, "ident") == 0);
assert(@offsetOf(kevent64_s, "filter") == 8);
assert(@offsetOf(kevent64_s, "flags") == 10);
assert(@offsetOf(kevent64_s, "fflags") == 12);
assert(@offsetOf(kevent64_s, "data") == 16);
assert(@offsetOf(kevent64_s, "udata") == 24);
assert(@offsetOf(kevent64_s, "ext") == 32);
}

View File

@@ -1,7 +1,7 @@
const builtin = @import("builtin");
const Os = builtin.Os;
pub use switch(builtin.os) {
pub use switch (builtin.os) {
Os.linux => @import("linux.zig"),
Os.windows => @import("windows.zig"),
Os.macosx, Os.ios => @import("darwin.zig"),
@@ -9,46 +9,55 @@ pub use switch(builtin.os) {
};
const empty_import = @import("../empty.zig");
// TODO https://github.com/ziglang/zig/issues/265 on this whole file
pub extern "c" fn abort() noreturn;
pub extern "c" fn exit(code: c_int) noreturn;
pub extern "c" fn isatty(fd: c_int) c_int;
pub extern "c" fn close(fd: c_int) c_int;
pub extern "c" fn fstat(fd: c_int, buf: &Stat) c_int;
pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: &Stat) c_int;
pub extern "c" fn fstat(fd: c_int, buf: *Stat) c_int;
pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int;
pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize;
pub extern "c" fn open(path: &const u8, oflag: c_int, ...) c_int;
pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int;
pub extern "c" fn raise(sig: c_int) c_int;
pub extern "c" fn read(fd: c_int, buf: &c_void, nbyte: usize) isize;
pub extern "c" fn stat(noalias path: &const u8, noalias buf: &Stat) c_int;
pub extern "c" fn write(fd: c_int, buf: &const c_void, nbyte: usize) isize;
pub extern "c" fn mmap(addr: ?&c_void, len: usize, prot: c_int, flags: c_int,
fd: c_int, offset: isize) ?&c_void;
pub extern "c" fn munmap(addr: &c_void, len: usize) c_int;
pub extern "c" fn unlink(path: &const u8) c_int;
pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8;
pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int;
pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize;
pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;
pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize;
pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void;
pub extern "c" fn munmap(addr: *c_void, len: usize) c_int;
pub extern "c" fn unlink(path: [*]const u8) c_int;
pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;
pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int;
pub extern "c" fn fork() c_int;
pub extern "c" fn access(path: &const u8, mode: c_uint) c_int;
pub extern "c" fn pipe(fds: &c_int) c_int;
pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int;
pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int;
pub extern "c" fn rename(old: &const u8, new: &const u8) c_int;
pub extern "c" fn chdir(path: &const u8) c_int;
pub extern "c" fn execve(path: &const u8, argv: &const ?&const u8,
envp: &const ?&const u8) c_int;
pub extern "c" fn access(path: [*]const u8, mode: c_uint) c_int;
pub extern "c" fn pipe(fds: *[2]c_int) c_int;
pub extern "c" fn mkdir(path: [*]const u8, mode: c_uint) c_int;
pub extern "c" fn symlink(existing: [*]const u8, new: [*]const u8) c_int;
pub extern "c" fn rename(old: [*]const u8, new: [*]const u8) c_int;
pub extern "c" fn chdir(path: [*]const u8) c_int;
pub extern "c" fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) c_int;
pub extern "c" fn dup(fd: c_int) c_int;
pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int;
pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize;
pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8;
pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int;
pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int;
pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int;
pub extern "c" fn readlink(noalias path: [*]const u8, noalias buf: [*]u8, bufsize: usize) isize;
pub extern "c" fn realpath(noalias file_name: [*]const u8, noalias resolved_name: [*]u8) ?[*]u8;
pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int;
pub extern "c" fn gettimeofday(tv: ?*timeval, tz: ?*timezone) c_int;
pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int;
pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int;
pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int;
pub extern "c" fn rmdir(path: &const u8) c_int;
pub extern "c" fn rmdir(path: [*]const u8) c_int;
pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?&c_void;
pub extern "c" fn malloc(usize) ?&c_void;
pub extern "c" fn realloc(&c_void, usize) ?&c_void;
pub extern "c" fn free(&c_void) void;
pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int;
pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void;
pub extern "c" fn malloc(usize) ?*c_void;
pub extern "c" fn realloc(*c_void, usize) ?*c_void;
pub extern "c" fn free(*c_void) void;
pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int;
pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int;
pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int;
pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int;
pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;
pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int;
pub const pthread_t = *@OpaqueType();

View File

@@ -1,5 +1,10 @@
pub use @import("../os/linux/errno.zig");
pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int;
extern "c" fn __errno_location() &c_int;
pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int;
extern "c" fn __errno_location() *c_int;
pub const _errno = __errno_location;
pub const pthread_attr_t = extern struct {
__size: [56]u8,
__align: c_long,
};

View File

@@ -1 +1 @@
pub extern "c" fn _errno() &c_int;
pub extern "c" fn _errno() *c_int;

View File

@@ -6,11 +6,23 @@ const builtin = @import("builtin");
const htest = @import("test.zig");
const RoundParam = struct {
a: usize, b: usize, c: usize, d: usize, x: usize, y: usize,
a: usize,
b: usize,
c: usize,
d: usize,
x: usize,
y: usize,
};
fn Rp(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) RoundParam {
return RoundParam { .a = a, .b = b, .c = c, .d = d, .x = x, .y = y, };
return RoundParam{
.a = a,
.b = b,
.c = c,
.d = d,
.x = x,
.y = y,
};
}
/////////////////////
@@ -19,145 +31,153 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) RoundParam {
pub const Blake2s224 = Blake2s(224);
pub const Blake2s256 = Blake2s(256);
fn Blake2s(comptime out_len: usize) type { return struct {
const Self = this;
const block_size = 64;
const digest_size = out_len / 8;
fn Blake2s(comptime out_len: usize) type {
return struct {
const Self = this;
const block_size = 64;
const digest_size = out_len / 8;
const iv = [8]u32 {
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
};
const iv = [8]u32{
0x6A09E667,
0xBB67AE85,
0x3C6EF372,
0xA54FF53A,
0x510E527F,
0x9B05688C,
0x1F83D9AB,
0x5BE0CD19,
};
const sigma = [10][16]u8 {
[]const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
[]const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
[]const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
[]const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
[]const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
[]const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
[]const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
[]const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
[]const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
[]const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
};
const sigma = [10][16]u8{
[]const u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
[]const u8{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
[]const u8{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
[]const u8{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
[]const u8{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
[]const u8{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
[]const u8{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
[]const u8{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
[]const u8{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
[]const u8{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
};
h: [8]u32,
t: u64,
// Streaming cache
buf: [64]u8,
buf_len: u8,
h: [8]u32,
t: u64,
// Streaming cache
buf: [64]u8,
buf_len: u8,
pub fn init() Self {
debug.assert(8 <= out_len and out_len <= 512);
pub fn init() Self {
debug.assert(8 <= out_len and out_len <= 512);
var s: Self = undefined;
s.reset();
return s;
}
var s: Self = undefined;
s.reset();
return s;
}
pub fn reset(d: &Self) void {
mem.copy(u32, d.h[0..], iv[0..]);
pub fn reset(d: *Self) void {
mem.copy(u32, d.h[0..], iv[0..]);
// No key plus default parameters
d.h[0] ^= 0x01010000 ^ u32(out_len >> 3);
d.t = 0;
d.buf_len = 0;
}
pub fn hash(b: []const u8, out: []u8) void {
var d = Self.init();
d.update(b);
d.final(out);
}
pub fn update(d: &Self, b: []const u8) void {
var off: usize = 0;
// Partial buffer exists from previous update. Copy into buffer then hash.
if (d.buf_len != 0 and d.buf_len + b.len > 64) {
off += 64 - d.buf_len;
mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
d.t += 64;
d.round(d.buf[0..], false);
// No key plus default parameters
d.h[0] ^= 0x01010000 ^ @intCast(u32, out_len >> 3);
d.t = 0;
d.buf_len = 0;
}
// Full middle blocks.
while (off + 64 <= b.len) : (off += 64) {
d.t += 64;
d.round(b[off..off + 64], false);
pub fn hash(b: []const u8, out: []u8) void {
var d = Self.init();
d.update(b);
d.final(out);
}
// Copy any remainder for next pass.
mem.copy(u8, d.buf[d.buf_len..], b[off..]);
d.buf_len += u8(b[off..].len);
}
pub fn update(d: *Self, b: []const u8) void {
var off: usize = 0;
pub fn final(d: &Self, out: []u8) void {
debug.assert(out.len >= out_len / 8);
// Partial buffer exists from previous update. Copy into buffer then hash.
if (d.buf_len != 0 and d.buf_len + b.len > 64) {
off += 64 - d.buf_len;
mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
d.t += 64;
d.round(d.buf[0..], false);
d.buf_len = 0;
}
mem.set(u8, d.buf[d.buf_len..], 0);
d.t += d.buf_len;
d.round(d.buf[0..], true);
// Full middle blocks.
while (off + 64 <= b.len) : (off += 64) {
d.t += 64;
d.round(b[off .. off + 64], false);
}
const rr = d.h[0 .. out_len / 32];
for (rr) |s, j| {
mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Little);
}
}
fn round(d: &Self, b: []const u8, last: bool) void {
debug.assert(b.len == 64);
var m: [16]u32 = undefined;
var v: [16]u32 = undefined;
for (m) |*r, i| {
*r = mem.readIntLE(u32, b[4*i .. 4*i + 4]);
// Copy any remainder for next pass.
mem.copy(u8, d.buf[d.buf_len..], b[off..]);
d.buf_len += @intCast(u8, b[off..].len);
}
var k: usize = 0;
while (k < 8) : (k += 1) {
v[k] = d.h[k];
v[k+8] = iv[k];
}
pub fn final(d: *Self, out: []u8) void {
debug.assert(out.len >= out_len / 8);
v[12] ^= @truncate(u32, d.t);
v[13] ^= u32(d.t >> 32);
if (last) v[14] = ~v[14];
mem.set(u8, d.buf[d.buf_len..], 0);
d.t += d.buf_len;
d.round(d.buf[0..], true);
const rounds = comptime []RoundParam {
Rp(0, 4, 8, 12, 0, 1),
Rp(1, 5, 9, 13, 2, 3),
Rp(2, 6, 10, 14, 4, 5),
Rp(3, 7, 11, 15, 6, 7),
Rp(0, 5, 10, 15, 8, 9),
Rp(1, 6, 11, 12, 10, 11),
Rp(2, 7, 8, 13, 12, 13),
Rp(3, 4, 9, 14, 14, 15),
};
const rr = d.h[0 .. out_len / 32];
comptime var j: usize = 0;
inline while (j < 10) : (j += 1) {
inline for (rounds) |r| {
v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]];
v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(16));
v[r.c] = v[r.c] +% v[r.d];
v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(12));
v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]];
v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(8));
v[r.c] = v[r.c] +% v[r.d];
v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(7));
for (rr) |s, j| {
mem.writeInt(out[4 * j .. 4 * j + 4], s, builtin.Endian.Little);
}
}
for (d.h) |*r, i| {
*r ^= v[i] ^ v[i + 8];
fn round(d: *Self, b: []const u8, last: bool) void {
debug.assert(b.len == 64);
var m: [16]u32 = undefined;
var v: [16]u32 = undefined;
for (m) |*r, i| {
r.* = mem.readIntLE(u32, b[4 * i .. 4 * i + 4]);
}
var k: usize = 0;
while (k < 8) : (k += 1) {
v[k] = d.h[k];
v[k + 8] = iv[k];
}
v[12] ^= @truncate(u32, d.t);
v[13] ^= @intCast(u32, d.t >> 32);
if (last) v[14] = ~v[14];
const rounds = comptime []RoundParam{
Rp(0, 4, 8, 12, 0, 1),
Rp(1, 5, 9, 13, 2, 3),
Rp(2, 6, 10, 14, 4, 5),
Rp(3, 7, 11, 15, 6, 7),
Rp(0, 5, 10, 15, 8, 9),
Rp(1, 6, 11, 12, 10, 11),
Rp(2, 7, 8, 13, 12, 13),
Rp(3, 4, 9, 14, 14, 15),
};
comptime var j: usize = 0;
inline while (j < 10) : (j += 1) {
inline for (rounds) |r| {
v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]];
v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(16));
v[r.c] = v[r.c] +% v[r.d];
v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(12));
v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]];
v[r.d] = math.rotr(u32, v[r.d] ^ v[r.a], usize(8));
v[r.c] = v[r.c] +% v[r.d];
v[r.b] = math.rotr(u32, v[r.b] ^ v[r.c], usize(7));
}
}
for (d.h) |*r, i| {
r.* ^= v[i] ^ v[i + 8];
}
}
}
};}
};
}
test "blake2s224 single" {
const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4";
@@ -230,7 +250,7 @@ test "blake2s256 streaming" {
}
test "blake2s256 aligned final" {
var block = []u8 {0} ** Blake2s256.block_size;
var block = []u8{0} ** Blake2s256.block_size;
var out: [Blake2s256.digest_size]u8 = undefined;
var h = Blake2s256.init();
@@ -238,154 +258,159 @@ test "blake2s256 aligned final" {
h.final(out[0..]);
}
/////////////////////
// Blake2b
pub const Blake2b384 = Blake2b(384);
pub const Blake2b512 = Blake2b(512);
fn Blake2b(comptime out_len: usize) type { return struct {
const Self = this;
const block_size = 128;
const digest_size = out_len / 8;
fn Blake2b(comptime out_len: usize) type {
return struct {
const Self = this;
const block_size = 128;
const digest_size = out_len / 8;
const iv = [8]u64 {
0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
0x510e527fade682d1, 0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
};
const iv = [8]u64{
0x6a09e667f3bcc908,
0xbb67ae8584caa73b,
0x3c6ef372fe94f82b,
0xa54ff53a5f1d36f1,
0x510e527fade682d1,
0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b,
0x5be0cd19137e2179,
};
const sigma = [12][16]u8 {
[]const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
[]const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
[]const u8 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
[]const u8 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
[]const u8 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
[]const u8 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
[]const u8 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
[]const u8 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
[]const u8 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
[]const u8 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 },
[]const u8 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
[]const u8 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
};
const sigma = [12][16]u8{
[]const u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
[]const u8{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
[]const u8{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
[]const u8{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
[]const u8{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
[]const u8{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
[]const u8{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
[]const u8{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
[]const u8{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
[]const u8{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
[]const u8{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
[]const u8{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
};
h: [8]u64,
t: u128,
// Streaming cache
buf: [128]u8,
buf_len: u8,
h: [8]u64,
t: u128,
// Streaming cache
buf: [128]u8,
buf_len: u8,
pub fn init() Self {
debug.assert(8 <= out_len and out_len <= 512);
pub fn init() Self {
debug.assert(8 <= out_len and out_len <= 512);
var s: Self = undefined;
s.reset();
return s;
}
var s: Self = undefined;
s.reset();
return s;
}
pub fn reset(d: &Self) void {
mem.copy(u64, d.h[0..], iv[0..]);
pub fn reset(d: *Self) void {
mem.copy(u64, d.h[0..], iv[0..]);
// No key plus default parameters
d.h[0] ^= 0x01010000 ^ (out_len >> 3);
d.t = 0;
d.buf_len = 0;
}
pub fn hash(b: []const u8, out: []u8) void {
var d = Self.init();
d.update(b);
d.final(out);
}
pub fn update(d: &Self, b: []const u8) void {
var off: usize = 0;
// Partial buffer exists from previous update. Copy into buffer then hash.
if (d.buf_len != 0 and d.buf_len + b.len > 128) {
off += 128 - d.buf_len;
mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
d.t += 128;
d.round(d.buf[0..], false);
// No key plus default parameters
d.h[0] ^= 0x01010000 ^ (out_len >> 3);
d.t = 0;
d.buf_len = 0;
}
// Full middle blocks.
while (off + 128 <= b.len) : (off += 128) {
d.t += 128;
d.round(b[off..off + 128], false);
pub fn hash(b: []const u8, out: []u8) void {
var d = Self.init();
d.update(b);
d.final(out);
}
// Copy any remainder for next pass.
mem.copy(u8, d.buf[d.buf_len..], b[off..]);
d.buf_len += u8(b[off..].len);
}
pub fn update(d: *Self, b: []const u8) void {
var off: usize = 0;
pub fn final(d: &Self, out: []u8) void {
mem.set(u8, d.buf[d.buf_len..], 0);
d.t += d.buf_len;
d.round(d.buf[0..], true);
// Partial buffer exists from previous update. Copy into buffer then hash.
if (d.buf_len != 0 and d.buf_len + b.len > 128) {
off += 128 - d.buf_len;
mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
d.t += 128;
d.round(d.buf[0..], false);
d.buf_len = 0;
}
const rr = d.h[0 .. out_len / 64];
// Full middle blocks.
while (off + 128 <= b.len) : (off += 128) {
d.t += 128;
d.round(b[off .. off + 128], false);
}
for (rr) |s, j| {
mem.writeInt(out[8*j .. 8*j + 8], s, builtin.Endian.Little);
}
}
fn round(d: &Self, b: []const u8, last: bool) void {
debug.assert(b.len == 128);
var m: [16]u64 = undefined;
var v: [16]u64 = undefined;
for (m) |*r, i| {
*r = mem.readIntLE(u64, b[8*i .. 8*i + 8]);
// Copy any remainder for next pass.
mem.copy(u8, d.buf[d.buf_len..], b[off..]);
d.buf_len += @intCast(u8, b[off..].len);
}
var k: usize = 0;
while (k < 8) : (k += 1) {
v[k] = d.h[k];
v[k+8] = iv[k];
}
pub fn final(d: *Self, out: []u8) void {
mem.set(u8, d.buf[d.buf_len..], 0);
d.t += d.buf_len;
d.round(d.buf[0..], true);
v[12] ^= @truncate(u64, d.t);
v[13] ^= u64(d.t >> 64);
if (last) v[14] = ~v[14];
const rr = d.h[0 .. out_len / 64];
const rounds = comptime []RoundParam {
Rp(0, 4, 8, 12, 0, 1),
Rp(1, 5, 9, 13, 2, 3),
Rp(2, 6, 10, 14, 4, 5),
Rp(3, 7, 11, 15, 6, 7),
Rp(0, 5, 10, 15, 8, 9),
Rp(1, 6, 11, 12, 10, 11),
Rp(2, 7, 8, 13, 12, 13),
Rp(3, 4, 9, 14, 14, 15),
};
comptime var j: usize = 0;
inline while (j < 12) : (j += 1) {
inline for (rounds) |r| {
v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]];
v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(32));
v[r.c] = v[r.c] +% v[r.d];
v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(24));
v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]];
v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(16));
v[r.c] = v[r.c] +% v[r.d];
v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(63));
for (rr) |s, j| {
mem.writeInt(out[8 * j .. 8 * j + 8], s, builtin.Endian.Little);
}
}
for (d.h) |*r, i| {
*r ^= v[i] ^ v[i + 8];
fn round(d: *Self, b: []const u8, last: bool) void {
debug.assert(b.len == 128);
var m: [16]u64 = undefined;
var v: [16]u64 = undefined;
for (m) |*r, i| {
r.* = mem.readIntLE(u64, b[8 * i .. 8 * i + 8]);
}
var k: usize = 0;
while (k < 8) : (k += 1) {
v[k] = d.h[k];
v[k + 8] = iv[k];
}
v[12] ^= @truncate(u64, d.t);
v[13] ^= @intCast(u64, d.t >> 64);
if (last) v[14] = ~v[14];
const rounds = comptime []RoundParam{
Rp(0, 4, 8, 12, 0, 1),
Rp(1, 5, 9, 13, 2, 3),
Rp(2, 6, 10, 14, 4, 5),
Rp(3, 7, 11, 15, 6, 7),
Rp(0, 5, 10, 15, 8, 9),
Rp(1, 6, 11, 12, 10, 11),
Rp(2, 7, 8, 13, 12, 13),
Rp(3, 4, 9, 14, 14, 15),
};
comptime var j: usize = 0;
inline while (j < 12) : (j += 1) {
inline for (rounds) |r| {
v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.x]];
v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(32));
v[r.c] = v[r.c] +% v[r.d];
v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(24));
v[r.a] = v[r.a] +% v[r.b] +% m[sigma[j][r.y]];
v[r.d] = math.rotr(u64, v[r.d] ^ v[r.a], usize(16));
v[r.c] = v[r.c] +% v[r.d];
v[r.b] = math.rotr(u64, v[r.b] ^ v[r.c], usize(63));
}
}
for (d.h) |*r, i| {
r.* ^= v[i] ^ v[i + 8];
}
}
}
};}
};
}
test "blake2b384 single" {
const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100";
@@ -458,7 +483,7 @@ test "blake2b512 streaming" {
}
test "blake2b512 aligned final" {
var block = []u8 {0} ** Blake2b512.block_size;
var block = []u8{0} ** Blake2b512.block_size;
var out: [Blake2b512.digest_size]u8 = undefined;
var h = Blake2b512.init();

View File

@@ -29,12 +29,12 @@ pub fn Hmac(comptime H: type) type {
var o_key_pad: [H.block_size]u8 = undefined;
for (o_key_pad) |*b, i| {
*b = scratch[i] ^ 0x5c;
b.* = scratch[i] ^ 0x5c;
}
var i_key_pad: [H.block_size]u8 = undefined;
for (i_key_pad) |*b, i| {
*b = scratch[i] ^ 0x36;
b.* = scratch[i] ^ 0x36;
}
// HMAC(k, m) = H(o_key_pad | H(i_key_pad | message)) where | is concatenation

View File

@@ -6,12 +6,25 @@ const debug = @import("../debug/index.zig");
const fmt = @import("../fmt/index.zig");
const RoundParam = struct {
a: usize, b: usize, c: usize, d: usize,
k: usize, s: u32, t: u32
a: usize,
b: usize,
c: usize,
d: usize,
k: usize,
s: u32,
t: u32,
};
fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) RoundParam {
return RoundParam { .a = a, .b = b, .c = c, .d = d, .k = k, .s = s, .t = t };
return RoundParam{
.a = a,
.b = b,
.c = c,
.d = d,
.k = k,
.s = s,
.t = t,
};
}
pub const Md5 = struct {
@@ -31,7 +44,7 @@ pub const Md5 = struct {
return d;
}
pub fn reset(d: &Self) void {
pub fn reset(d: *Self) void {
d.s[0] = 0x67452301;
d.s[1] = 0xEFCDAB89;
d.s[2] = 0x98BADCFE;
@@ -46,7 +59,7 @@ pub const Md5 = struct {
d.final(out);
}
pub fn update(d: &Self, b: []const u8) void {
pub fn update(d: *Self, b: []const u8) void {
var off: usize = 0;
// Partial buffer exists from previous update. Copy into buffer then hash.
@@ -60,18 +73,18 @@ pub const Md5 = struct {
// Full middle blocks.
while (off + 64 <= b.len) : (off += 64) {
d.round(b[off..off + 64]);
d.round(b[off .. off + 64]);
}
// Copy any remainder for next pass.
mem.copy(u8, d.buf[d.buf_len..], b[off..]);
d.buf_len += u8(b[off..].len);
d.buf_len += @intCast(u8, b[off..].len);
// Md5 uses the bottom 64-bits for length padding
d.total_len +%= b.len;
}
pub fn final(d: &Self, out: []u8) void {
pub fn final(d: *Self, out: []u8) void {
debug.assert(out.len >= 16);
// The buffer here will never be completely full.
@@ -90,20 +103,20 @@ pub const Md5 = struct {
// Append message length.
var i: usize = 1;
var len = d.total_len >> 5;
d.buf[56] = u8(d.total_len & 0x1f) << 3;
d.buf[56] = @intCast(u8, d.total_len & 0x1f) << 3;
while (i < 8) : (i += 1) {
d.buf[56 + i] = u8(len & 0xff);
d.buf[56 + i] = @intCast(u8, len & 0xff);
len >>= 8;
}
d.round(d.buf[0..]);
for (d.s) |s, j| {
mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Little);
mem.writeInt(out[4 * j .. 4 * j + 4], s, builtin.Endian.Little);
}
}
fn round(d: &Self, b: []const u8) void {
fn round(d: *Self, b: []const u8) void {
debug.assert(b.len == 64);
var s: [16]u32 = undefined;
@@ -112,30 +125,33 @@ pub const Md5 = struct {
while (i < 16) : (i += 1) {
// NOTE: Performing or's separately improves perf by ~10%
s[i] = 0;
s[i] |= u32(b[i*4+0]);
s[i] |= u32(b[i*4+1]) << 8;
s[i] |= u32(b[i*4+2]) << 16;
s[i] |= u32(b[i*4+3]) << 24;
s[i] |= u32(b[i * 4 + 0]);
s[i] |= u32(b[i * 4 + 1]) << 8;
s[i] |= u32(b[i * 4 + 2]) << 16;
s[i] |= u32(b[i * 4 + 3]) << 24;
}
var v: [4]u32 = []u32 {
d.s[0], d.s[1], d.s[2], d.s[3],
var v: [4]u32 = []u32{
d.s[0],
d.s[1],
d.s[2],
d.s[3],
};
const round0 = comptime []RoundParam {
Rp(0, 1, 2, 3, 0, 7, 0xD76AA478),
Rp(3, 0, 1, 2, 1, 12, 0xE8C7B756),
Rp(2, 3, 0, 1, 2, 17, 0x242070DB),
Rp(1, 2, 3, 0, 3, 22, 0xC1BDCEEE),
Rp(0, 1, 2, 3, 4, 7, 0xF57C0FAF),
Rp(3, 0, 1, 2, 5, 12, 0x4787C62A),
Rp(2, 3, 0, 1, 6, 17, 0xA8304613),
Rp(1, 2, 3, 0, 7, 22, 0xFD469501),
Rp(0, 1, 2, 3, 8, 7, 0x698098D8),
Rp(3, 0, 1, 2, 9, 12, 0x8B44F7AF),
const round0 = comptime []RoundParam{
Rp(0, 1, 2, 3, 0, 7, 0xD76AA478),
Rp(3, 0, 1, 2, 1, 12, 0xE8C7B756),
Rp(2, 3, 0, 1, 2, 17, 0x242070DB),
Rp(1, 2, 3, 0, 3, 22, 0xC1BDCEEE),
Rp(0, 1, 2, 3, 4, 7, 0xF57C0FAF),
Rp(3, 0, 1, 2, 5, 12, 0x4787C62A),
Rp(2, 3, 0, 1, 6, 17, 0xA8304613),
Rp(1, 2, 3, 0, 7, 22, 0xFD469501),
Rp(0, 1, 2, 3, 8, 7, 0x698098D8),
Rp(3, 0, 1, 2, 9, 12, 0x8B44F7AF),
Rp(2, 3, 0, 1, 10, 17, 0xFFFF5BB1),
Rp(1, 2, 3, 0, 11, 22, 0x895CD7BE),
Rp(0, 1, 2, 3, 12, 7, 0x6B901122),
Rp(0, 1, 2, 3, 12, 7, 0x6B901122),
Rp(3, 0, 1, 2, 13, 12, 0xFD987193),
Rp(2, 3, 0, 1, 14, 17, 0xA679438E),
Rp(1, 2, 3, 0, 15, 22, 0x49B40821),
@@ -145,22 +161,22 @@ pub const Md5 = struct {
v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s);
}
const round1 = comptime []RoundParam {
Rp(0, 1, 2, 3, 1, 5, 0xF61E2562),
Rp(3, 0, 1, 2, 6, 9, 0xC040B340),
const round1 = comptime []RoundParam{
Rp(0, 1, 2, 3, 1, 5, 0xF61E2562),
Rp(3, 0, 1, 2, 6, 9, 0xC040B340),
Rp(2, 3, 0, 1, 11, 14, 0x265E5A51),
Rp(1, 2, 3, 0, 0, 20, 0xE9B6C7AA),
Rp(0, 1, 2, 3, 5, 5, 0xD62F105D),
Rp(3, 0, 1, 2, 10, 9, 0x02441453),
Rp(1, 2, 3, 0, 0, 20, 0xE9B6C7AA),
Rp(0, 1, 2, 3, 5, 5, 0xD62F105D),
Rp(3, 0, 1, 2, 10, 9, 0x02441453),
Rp(2, 3, 0, 1, 15, 14, 0xD8A1E681),
Rp(1, 2, 3, 0, 4, 20, 0xE7D3FBC8),
Rp(0, 1, 2, 3, 9, 5, 0x21E1CDE6),
Rp(3, 0, 1, 2, 14, 9, 0xC33707D6),
Rp(2, 3, 0, 1, 3, 14, 0xF4D50D87),
Rp(1, 2, 3, 0, 8, 20, 0x455A14ED),
Rp(0, 1, 2, 3, 13, 5, 0xA9E3E905),
Rp(3, 0, 1, 2, 2, 9, 0xFCEFA3F8),
Rp(2, 3, 0, 1, 7, 14, 0x676F02D9),
Rp(1, 2, 3, 0, 4, 20, 0xE7D3FBC8),
Rp(0, 1, 2, 3, 9, 5, 0x21E1CDE6),
Rp(3, 0, 1, 2, 14, 9, 0xC33707D6),
Rp(2, 3, 0, 1, 3, 14, 0xF4D50D87),
Rp(1, 2, 3, 0, 8, 20, 0x455A14ED),
Rp(0, 1, 2, 3, 13, 5, 0xA9E3E905),
Rp(3, 0, 1, 2, 2, 9, 0xFCEFA3F8),
Rp(2, 3, 0, 1, 7, 14, 0x676F02D9),
Rp(1, 2, 3, 0, 12, 20, 0x8D2A4C8A),
};
inline for (round1) |r| {
@@ -168,46 +184,46 @@ pub const Md5 = struct {
v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s);
}
const round2 = comptime []RoundParam {
Rp(0, 1, 2, 3, 5, 4, 0xFFFA3942),
Rp(3, 0, 1, 2, 8, 11, 0x8771F681),
const round2 = comptime []RoundParam{
Rp(0, 1, 2, 3, 5, 4, 0xFFFA3942),
Rp(3, 0, 1, 2, 8, 11, 0x8771F681),
Rp(2, 3, 0, 1, 11, 16, 0x6D9D6122),
Rp(1, 2, 3, 0, 14, 23, 0xFDE5380C),
Rp(0, 1, 2, 3, 1, 4, 0xA4BEEA44),
Rp(3, 0, 1, 2, 4, 11, 0x4BDECFA9),
Rp(2, 3, 0, 1, 7, 16, 0xF6BB4B60),
Rp(0, 1, 2, 3, 1, 4, 0xA4BEEA44),
Rp(3, 0, 1, 2, 4, 11, 0x4BDECFA9),
Rp(2, 3, 0, 1, 7, 16, 0xF6BB4B60),
Rp(1, 2, 3, 0, 10, 23, 0xBEBFBC70),
Rp(0, 1, 2, 3, 13, 4, 0x289B7EC6),
Rp(3, 0, 1, 2, 0, 11, 0xEAA127FA),
Rp(2, 3, 0, 1, 3, 16, 0xD4EF3085),
Rp(1, 2, 3, 0, 6, 23, 0x04881D05),
Rp(0, 1, 2, 3, 9, 4, 0xD9D4D039),
Rp(0, 1, 2, 3, 13, 4, 0x289B7EC6),
Rp(3, 0, 1, 2, 0, 11, 0xEAA127FA),
Rp(2, 3, 0, 1, 3, 16, 0xD4EF3085),
Rp(1, 2, 3, 0, 6, 23, 0x04881D05),
Rp(0, 1, 2, 3, 9, 4, 0xD9D4D039),
Rp(3, 0, 1, 2, 12, 11, 0xE6DB99E5),
Rp(2, 3, 0, 1, 15, 16, 0x1FA27CF8),
Rp(1, 2, 3, 0, 2, 23, 0xC4AC5665),
Rp(1, 2, 3, 0, 2, 23, 0xC4AC5665),
};
inline for (round2) |r| {
v[r.a] = v[r.a] +% (v[r.b] ^ v[r.c] ^ v[r.d]) +% r.t +% s[r.k];
v[r.a] = v[r.b] +% math.rotl(u32, v[r.a], r.s);
}
const round3 = comptime []RoundParam {
Rp(0, 1, 2, 3, 0, 6, 0xF4292244),
Rp(3, 0, 1, 2, 7, 10, 0x432AFF97),
const round3 = comptime []RoundParam{
Rp(0, 1, 2, 3, 0, 6, 0xF4292244),
Rp(3, 0, 1, 2, 7, 10, 0x432AFF97),
Rp(2, 3, 0, 1, 14, 15, 0xAB9423A7),
Rp(1, 2, 3, 0, 5, 21, 0xFC93A039),
Rp(0, 1, 2, 3, 12, 6, 0x655B59C3),
Rp(3, 0, 1, 2, 3, 10, 0x8F0CCC92),
Rp(1, 2, 3, 0, 5, 21, 0xFC93A039),
Rp(0, 1, 2, 3, 12, 6, 0x655B59C3),
Rp(3, 0, 1, 2, 3, 10, 0x8F0CCC92),
Rp(2, 3, 0, 1, 10, 15, 0xFFEFF47D),
Rp(1, 2, 3, 0, 1, 21, 0x85845DD1),
Rp(0, 1, 2, 3, 8, 6, 0x6FA87E4F),
Rp(1, 2, 3, 0, 1, 21, 0x85845DD1),
Rp(0, 1, 2, 3, 8, 6, 0x6FA87E4F),
Rp(3, 0, 1, 2, 15, 10, 0xFE2CE6E0),
Rp(2, 3, 0, 1, 6, 15, 0xA3014314),
Rp(2, 3, 0, 1, 6, 15, 0xA3014314),
Rp(1, 2, 3, 0, 13, 21, 0x4E0811A1),
Rp(0, 1, 2, 3, 4, 6, 0xF7537E82),
Rp(0, 1, 2, 3, 4, 6, 0xF7537E82),
Rp(3, 0, 1, 2, 11, 10, 0xBD3AF235),
Rp(2, 3, 0, 1, 2, 15, 0x2AD7D2BB),
Rp(1, 2, 3, 0, 9, 21, 0xEB86D391),
Rp(2, 3, 0, 1, 2, 15, 0x2AD7D2BB),
Rp(1, 2, 3, 0, 9, 21, 0xEB86D391),
};
inline for (round3) |r| {
v[r.a] = v[r.a] +% (v[r.c] ^ (v[r.b] | ~v[r.d])) +% r.t +% s[r.k];
@@ -255,7 +271,7 @@ test "md5 streaming" {
}
test "md5 aligned final" {
var block = []u8 {0} ** Md5.block_size;
var block = []u8{0} ** Md5.block_size;
var out: [Md5.digest_size]u8 = undefined;
var h = Md5.init();

View File

@@ -4,14 +4,24 @@ const endian = @import("../endian.zig");
const debug = @import("../debug/index.zig");
const builtin = @import("builtin");
pub const u160 = @IntType(false, 160);
const RoundParam = struct {
a: usize, b: usize, c: usize, d: usize, e: usize, i: u32,
a: usize,
b: usize,
c: usize,
d: usize,
e: usize,
i: u32,
};
fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam {
return RoundParam { .a = a, .b = b, .c = c, .d = d, .e = e, .i = i };
return RoundParam{
.a = a,
.b = b,
.c = c,
.d = d,
.e = e,
.i = i,
};
}
pub const Sha1 = struct {
@@ -31,7 +41,7 @@ pub const Sha1 = struct {
return d;
}
pub fn reset(d: &Self) void {
pub fn reset(d: *Self) void {
d.s[0] = 0x67452301;
d.s[1] = 0xEFCDAB89;
d.s[2] = 0x98BADCFE;
@@ -47,7 +57,7 @@ pub const Sha1 = struct {
d.final(out);
}
pub fn update(d: &Self, b: []const u8) void {
pub fn update(d: *Self, b: []const u8) void {
var off: usize = 0;
// Partial buffer exists from previous update. Copy into buffer then hash.
@@ -61,17 +71,17 @@ pub const Sha1 = struct {
// Full middle blocks.
while (off + 64 <= b.len) : (off += 64) {
d.round(b[off..off + 64]);
d.round(b[off .. off + 64]);
}
// Copy any remainder for next pass.
mem.copy(u8, d.buf[d.buf_len..], b[off..]);
d.buf_len += u8(b[off..].len);
d.buf_len += @intCast(u8, b[off..].len);
d.total_len += b.len;
}
pub fn final(d: &Self, out: []u8) void {
pub fn final(d: *Self, out: []u8) void {
debug.assert(out.len >= 20);
// The buffer here will never be completely full.
@@ -90,39 +100,43 @@ pub const Sha1 = struct {
// Append message length.
var i: usize = 1;
var len = d.total_len >> 5;
d.buf[63] = u8(d.total_len & 0x1f) << 3;
d.buf[63] = @intCast(u8, d.total_len & 0x1f) << 3;
while (i < 8) : (i += 1) {
d.buf[63 - i] = u8(len & 0xff);
d.buf[63 - i] = @intCast(u8, len & 0xff);
len >>= 8;
}
d.round(d.buf[0..]);
for (d.s) |s, j| {
mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Big);
mem.writeInt(out[4 * j .. 4 * j + 4], s, builtin.Endian.Big);
}
}
fn round(d: &Self, b: []const u8) void {
fn round(d: *Self, b: []const u8) void {
debug.assert(b.len == 64);
var s: [16]u32 = undefined;
var v: [5]u32 = []u32 {
d.s[0], d.s[1], d.s[2], d.s[3], d.s[4],
var v: [5]u32 = []u32{
d.s[0],
d.s[1],
d.s[2],
d.s[3],
d.s[4],
};
const round0a = comptime []RoundParam {
Rp(0, 1, 2, 3, 4, 0),
Rp(4, 0, 1, 2, 3, 1),
Rp(3, 4, 0, 1, 2, 2),
Rp(2, 3, 4, 0, 1, 3),
Rp(1, 2, 3, 4, 0, 4),
Rp(0, 1, 2, 3, 4, 5),
Rp(4, 0, 1, 2, 3, 6),
Rp(3, 4, 0, 1, 2, 7),
Rp(2, 3, 4, 0, 1, 8),
Rp(1, 2, 3, 4, 0, 9),
const round0a = comptime []RoundParam{
Rp(0, 1, 2, 3, 4, 0),
Rp(4, 0, 1, 2, 3, 1),
Rp(3, 4, 0, 1, 2, 2),
Rp(2, 3, 4, 0, 1, 3),
Rp(1, 2, 3, 4, 0, 4),
Rp(0, 1, 2, 3, 4, 5),
Rp(4, 0, 1, 2, 3, 6),
Rp(3, 4, 0, 1, 2, 7),
Rp(2, 3, 4, 0, 1, 8),
Rp(1, 2, 3, 4, 0, 9),
Rp(0, 1, 2, 3, 4, 10),
Rp(4, 0, 1, 2, 3, 11),
Rp(3, 4, 0, 1, 2, 12),
@@ -131,32 +145,27 @@ pub const Sha1 = struct {
Rp(0, 1, 2, 3, 4, 15),
};
inline for (round0a) |r| {
s[r.i] = (u32(b[r.i * 4 + 0]) << 24) |
(u32(b[r.i * 4 + 1]) << 16) |
(u32(b[r.i * 4 + 2]) << 8) |
(u32(b[r.i * 4 + 3]) << 0);
s[r.i] = (u32(b[r.i * 4 + 0]) << 24) | (u32(b[r.i * 4 + 1]) << 16) | (u32(b[r.i * 4 + 2]) << 8) | (u32(b[r.i * 4 + 3]) << 0);
v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf]
+% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d]));
v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d]));
v[r.b] = math.rotl(u32, v[r.b], u32(30));
}
const round0b = comptime []RoundParam {
const round0b = comptime []RoundParam{
Rp(4, 0, 1, 2, 3, 16),
Rp(3, 4, 0, 1, 2, 17),
Rp(2, 3, 4, 0, 1, 18),
Rp(1, 2, 3, 4, 0, 19),
};
inline for (round0b) |r| {
const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf];
const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
s[r.i & 0xf] = math.rotl(u32, t, u32(1));
v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf]
+% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d]));
v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x5A827999 +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) | (~v[r.b] & v[r.d]));
v[r.b] = math.rotl(u32, v[r.b], u32(30));
}
const round1 = comptime []RoundParam {
const round1 = comptime []RoundParam{
Rp(0, 1, 2, 3, 4, 20),
Rp(4, 0, 1, 2, 3, 21),
Rp(3, 4, 0, 1, 2, 22),
@@ -179,15 +188,14 @@ pub const Sha1 = struct {
Rp(1, 2, 3, 4, 0, 39),
};
inline for (round1) |r| {
const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf];
const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
s[r.i & 0xf] = math.rotl(u32, t, u32(1));
v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x6ED9EBA1 +% s[r.i & 0xf]
+% (v[r.b] ^ v[r.c] ^ v[r.d]);
v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x6ED9EBA1 +% s[r.i & 0xf] +% (v[r.b] ^ v[r.c] ^ v[r.d]);
v[r.b] = math.rotl(u32, v[r.b], u32(30));
}
const round2 = comptime []RoundParam {
const round2 = comptime []RoundParam{
Rp(0, 1, 2, 3, 4, 40),
Rp(4, 0, 1, 2, 3, 41),
Rp(3, 4, 0, 1, 2, 42),
@@ -210,15 +218,14 @@ pub const Sha1 = struct {
Rp(1, 2, 3, 4, 0, 59),
};
inline for (round2) |r| {
const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf];
const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
s[r.i & 0xf] = math.rotl(u32, t, u32(1));
v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x8F1BBCDC +% s[r.i & 0xf]
+% ((v[r.b] & v[r.c]) ^ (v[r.b] & v[r.d]) ^ (v[r.c] & v[r.d]));
v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0x8F1BBCDC +% s[r.i & 0xf] +% ((v[r.b] & v[r.c]) ^ (v[r.b] & v[r.d]) ^ (v[r.c] & v[r.d]));
v[r.b] = math.rotl(u32, v[r.b], u32(30));
}
const round3 = comptime []RoundParam {
const round3 = comptime []RoundParam{
Rp(0, 1, 2, 3, 4, 60),
Rp(4, 0, 1, 2, 3, 61),
Rp(3, 4, 0, 1, 2, 62),
@@ -241,11 +248,10 @@ pub const Sha1 = struct {
Rp(1, 2, 3, 4, 0, 79),
};
inline for (round3) |r| {
const t = s[(r.i-3) & 0xf] ^ s[(r.i-8) & 0xf] ^ s[(r.i-14) & 0xf] ^ s[(r.i-16) & 0xf];
const t = s[(r.i - 3) & 0xf] ^ s[(r.i - 8) & 0xf] ^ s[(r.i - 14) & 0xf] ^ s[(r.i - 16) & 0xf];
s[r.i & 0xf] = math.rotl(u32, t, u32(1));
v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0xCA62C1D6 +% s[r.i & 0xf]
+% (v[r.b] ^ v[r.c] ^ v[r.d]);
v[r.e] = v[r.e] +% math.rotl(u32, v[r.a], u32(5)) +% 0xCA62C1D6 +% s[r.i & 0xf] +% (v[r.b] ^ v[r.c] ^ v[r.d]);
v[r.b] = math.rotl(u32, v[r.b], u32(30));
}
@@ -286,7 +292,7 @@ test "sha1 streaming" {
}
test "sha1 aligned final" {
var block = []u8 {0} ** Sha1.block_size;
var block = []u8{0} ** Sha1.block_size;
var out: [Sha1.digest_size]u8 = undefined;
var h = Sha1.init();

View File

@@ -9,12 +9,31 @@ const htest = @import("test.zig");
// Sha224 + Sha256
const RoundParam256 = struct {
a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize,
i: usize, k: u32,
a: usize,
b: usize,
c: usize,
d: usize,
e: usize,
f: usize,
g: usize,
h: usize,
i: usize,
k: u32,
};
fn Rp256(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u32) RoundParam256 {
return RoundParam256 { .a = a, .b = b, .c = c, .d = d, .e = e, .f = f, .g = g, .h = h, .i = i, .k = k };
return RoundParam256{
.a = a,
.b = b,
.c = c,
.d = d,
.e = e,
.f = f,
.g = g,
.h = h,
.i = i,
.k = k,
};
}
const Sha2Params32 = struct {
@@ -29,7 +48,7 @@ const Sha2Params32 = struct {
out_len: usize,
};
const Sha224Params = Sha2Params32 {
const Sha224Params = Sha2Params32{
.iv0 = 0xC1059ED8,
.iv1 = 0x367CD507,
.iv2 = 0x3070DD17,
@@ -41,7 +60,7 @@ const Sha224Params = Sha2Params32 {
.out_len = 224,
};
const Sha256Params = Sha2Params32 {
const Sha256Params = Sha2Params32{
.iv0 = 0x6A09E667,
.iv1 = 0xBB67AE85,
.iv2 = 0x3C6EF372,
@@ -56,216 +75,215 @@ const Sha256Params = Sha2Params32 {
pub const Sha224 = Sha2_32(Sha224Params);
pub const Sha256 = Sha2_32(Sha256Params);
fn Sha2_32(comptime params: Sha2Params32) type { return struct {
const Self = this;
const block_size = 64;
const digest_size = params.out_len / 8;
fn Sha2_32(comptime params: Sha2Params32) type {
return struct {
const Self = this;
const block_size = 64;
const digest_size = params.out_len / 8;
s: [8]u32,
// Streaming Cache
buf: [64]u8,
buf_len: u8,
total_len: u64,
s: [8]u32,
// Streaming Cache
buf: [64]u8,
buf_len: u8,
total_len: u64,
pub fn init() Self {
var d: Self = undefined;
d.reset();
return d;
}
pub fn init() Self {
var d: Self = undefined;
d.reset();
return d;
}
pub fn reset(d: &Self) void {
d.s[0] = params.iv0;
d.s[1] = params.iv1;
d.s[2] = params.iv2;
d.s[3] = params.iv3;
d.s[4] = params.iv4;
d.s[5] = params.iv5;
d.s[6] = params.iv6;
d.s[7] = params.iv7;
d.buf_len = 0;
d.total_len = 0;
}
pub fn hash(b: []const u8, out: []u8) void {
var d = Self.init();
d.update(b);
d.final(out);
}
pub fn update(d: &Self, b: []const u8) void {
var off: usize = 0;
// Partial buffer exists from previous update. Copy into buffer then hash.
if (d.buf_len != 0 and d.buf_len + b.len > 64) {
off += 64 - d.buf_len;
mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
d.round(d.buf[0..]);
pub fn reset(d: *Self) void {
d.s[0] = params.iv0;
d.s[1] = params.iv1;
d.s[2] = params.iv2;
d.s[3] = params.iv3;
d.s[4] = params.iv4;
d.s[5] = params.iv5;
d.s[6] = params.iv6;
d.s[7] = params.iv7;
d.buf_len = 0;
d.total_len = 0;
}
// Full middle blocks.
while (off + 64 <= b.len) : (off += 64) {
d.round(b[off..off + 64]);
pub fn hash(b: []const u8, out: []u8) void {
var d = Self.init();
d.update(b);
d.final(out);
}
// Copy any remainder for next pass.
mem.copy(u8, d.buf[d.buf_len..], b[off..]);
d.buf_len += u8(b[off..].len);
pub fn update(d: *Self, b: []const u8) void {
var off: usize = 0;
d.total_len += b.len;
}
// Partial buffer exists from previous update. Copy into buffer then hash.
if (d.buf_len != 0 and d.buf_len + b.len > 64) {
off += 64 - d.buf_len;
mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
pub fn final(d: &Self, out: []u8) void {
debug.assert(out.len >= params.out_len / 8);
d.round(d.buf[0..]);
d.buf_len = 0;
}
// The buffer here will never be completely full.
mem.set(u8, d.buf[d.buf_len..], 0);
// Full middle blocks.
while (off + 64 <= b.len) : (off += 64) {
d.round(b[off .. off + 64]);
}
// Append padding bits.
d.buf[d.buf_len] = 0x80;
d.buf_len += 1;
// Copy any remainder for next pass.
mem.copy(u8, d.buf[d.buf_len..], b[off..]);
d.buf_len += @intCast(u8, b[off..].len);
d.total_len += b.len;
}
pub fn final(d: *Self, out: []u8) void {
debug.assert(out.len >= params.out_len / 8);
// The buffer here will never be completely full.
mem.set(u8, d.buf[d.buf_len..], 0);
// Append padding bits.
d.buf[d.buf_len] = 0x80;
d.buf_len += 1;
// > 448 mod 512 so need to add an extra round to wrap around.
if (64 - d.buf_len < 8) {
d.round(d.buf[0..]);
mem.set(u8, d.buf[0..], 0);
}
// Append message length.
var i: usize = 1;
var len = d.total_len >> 5;
d.buf[63] = @intCast(u8, d.total_len & 0x1f) << 3;
while (i < 8) : (i += 1) {
d.buf[63 - i] = @intCast(u8, len & 0xff);
len >>= 8;
}
// > 448 mod 512 so need to add an extra round to wrap around.
if (64 - d.buf_len < 8) {
d.round(d.buf[0..]);
mem.set(u8, d.buf[0..], 0);
// May truncate for possible 224 output
const rr = d.s[0 .. params.out_len / 32];
for (rr) |s, j| {
mem.writeInt(out[4 * j .. 4 * j + 4], s, builtin.Endian.Big);
}
}
// Append message length.
var i: usize = 1;
var len = d.total_len >> 5;
d.buf[63] = u8(d.total_len & 0x1f) << 3;
while (i < 8) : (i += 1) {
d.buf[63 - i] = u8(len & 0xff);
len >>= 8;
fn round(d: *Self, b: []const u8) void {
debug.assert(b.len == 64);
var s: [64]u32 = undefined;
var i: usize = 0;
while (i < 16) : (i += 1) {
s[i] = 0;
s[i] |= u32(b[i * 4 + 0]) << 24;
s[i] |= u32(b[i * 4 + 1]) << 16;
s[i] |= u32(b[i * 4 + 2]) << 8;
s[i] |= u32(b[i * 4 + 3]) << 0;
}
while (i < 64) : (i += 1) {
s[i] = s[i - 16] +% s[i - 7] +% (math.rotr(u32, s[i - 15], u32(7)) ^ math.rotr(u32, s[i - 15], u32(18)) ^ (s[i - 15] >> 3)) +% (math.rotr(u32, s[i - 2], u32(17)) ^ math.rotr(u32, s[i - 2], u32(19)) ^ (s[i - 2] >> 10));
}
var v: [8]u32 = []u32{
d.s[0],
d.s[1],
d.s[2],
d.s[3],
d.s[4],
d.s[5],
d.s[6],
d.s[7],
};
const round0 = comptime []RoundParam256{
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x71374491),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCF),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA5),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25B),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B01),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A7),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C1),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC6),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DC),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C8),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF3),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x14292967),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A85),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B2138),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D13),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A7354),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C85),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A1),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664B),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A3),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD6990624),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E3585),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA070),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C08),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774C),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4A),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC70208),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEB),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2),
};
inline for (round0) |r| {
v[r.h] = v[r.h] +% (math.rotr(u32, v[r.e], u32(6)) ^ math.rotr(u32, v[r.e], u32(11)) ^ math.rotr(u32, v[r.e], u32(25))) +% (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% r.k +% s[r.i];
v[r.d] = v[r.d] +% v[r.h];
v[r.h] = v[r.h] +% (math.rotr(u32, v[r.a], u32(2)) ^ math.rotr(u32, v[r.a], u32(13)) ^ math.rotr(u32, v[r.a], u32(22))) +% ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c]));
}
d.s[0] +%= v[0];
d.s[1] +%= v[1];
d.s[2] +%= v[2];
d.s[3] +%= v[3];
d.s[4] +%= v[4];
d.s[5] +%= v[5];
d.s[6] +%= v[6];
d.s[7] +%= v[7];
}
d.round(d.buf[0..]);
// May truncate for possible 224 output
const rr = d.s[0 .. params.out_len / 32];
for (rr) |s, j| {
mem.writeInt(out[4*j .. 4*j + 4], s, builtin.Endian.Big);
}
}
fn round(d: &Self, b: []const u8) void {
debug.assert(b.len == 64);
var s: [64]u32 = undefined;
var i: usize = 0;
while (i < 16) : (i += 1) {
s[i] = 0;
s[i] |= u32(b[i*4+0]) << 24;
s[i] |= u32(b[i*4+1]) << 16;
s[i] |= u32(b[i*4+2]) << 8;
s[i] |= u32(b[i*4+3]) << 0;
}
while (i < 64) : (i += 1) {
s[i] =
s[i-16] +% s[i-7] +%
(math.rotr(u32, s[i-15], u32(7)) ^ math.rotr(u32, s[i-15], u32(18)) ^ (s[i-15] >> 3)) +%
(math.rotr(u32, s[i-2], u32(17)) ^ math.rotr(u32, s[i-2], u32(19)) ^ (s[i-2] >> 10));
}
var v: [8]u32 = []u32 {
d.s[0], d.s[1], d.s[2], d.s[3], d.s[4], d.s[5], d.s[6], d.s[7],
};
const round0 = comptime []RoundParam256 {
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x71374491),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCF),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA5),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25B),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B01),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A7),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C1),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC6),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DC),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C8),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF3),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x14292967),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A85),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B2138),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D13),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A7354),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C85),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A1),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664B),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A3),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD6990624),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E3585),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA070),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C08),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774C),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4A),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3),
Rp256(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE),
Rp256(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F),
Rp256(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814),
Rp256(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC70208),
Rp256(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA),
Rp256(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEB),
Rp256(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7),
Rp256(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2),
};
inline for (round0) |r| {
v[r.h] =
v[r.h] +%
(math.rotr(u32, v[r.e], u32(6)) ^ math.rotr(u32, v[r.e], u32(11)) ^ math.rotr(u32, v[r.e], u32(25))) +%
(v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +%
r.k +% s[r.i];
v[r.d] = v[r.d] +% v[r.h];
v[r.h] =
v[r.h] +%
(math.rotr(u32, v[r.a], u32(2)) ^ math.rotr(u32, v[r.a], u32(13)) ^ math.rotr(u32, v[r.a], u32(22))) +%
((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c]));
}
d.s[0] +%= v[0];
d.s[1] +%= v[1];
d.s[2] +%= v[2];
d.s[3] +%= v[3];
d.s[4] +%= v[4];
d.s[5] +%= v[5];
d.s[6] +%= v[6];
d.s[7] +%= v[7];
}
};}
};
}
test "sha224 single" {
htest.assertEqualHash(Sha224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", "");
@@ -320,7 +338,7 @@ test "sha256 streaming" {
}
test "sha256 aligned final" {
var block = []u8 {0} ** Sha256.block_size;
var block = []u8{0} ** Sha256.block_size;
var out: [Sha256.digest_size]u8 = undefined;
var h = Sha256.init();
@@ -328,17 +346,35 @@ test "sha256 aligned final" {
h.final(out[0..]);
}
/////////////////////
// Sha384 + Sha512
const RoundParam512 = struct {
a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize,
i: usize, k: u64,
a: usize,
b: usize,
c: usize,
d: usize,
e: usize,
f: usize,
g: usize,
h: usize,
i: usize,
k: u64,
};
fn Rp512(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, g: usize, h: usize, i: usize, k: u64) RoundParam512 {
return RoundParam512 { .a = a, .b = b, .c = c, .d = d, .e = e, .f = f, .g = g, .h = h, .i = i, .k = k };
return RoundParam512{
.a = a,
.b = b,
.c = c,
.d = d,
.e = e,
.f = f,
.g = g,
.h = h,
.i = i,
.k = k,
};
}
const Sha2Params64 = struct {
@@ -353,7 +389,7 @@ const Sha2Params64 = struct {
out_len: usize,
};
const Sha384Params = Sha2Params64 {
const Sha384Params = Sha2Params64{
.iv0 = 0xCBBB9D5DC1059ED8,
.iv1 = 0x629A292A367CD507,
.iv2 = 0x9159015A3070DD17,
@@ -365,7 +401,7 @@ const Sha384Params = Sha2Params64 {
.out_len = 384,
};
const Sha512Params = Sha2Params64 {
const Sha512Params = Sha2Params64{
.iv0 = 0x6A09E667F3BCC908,
.iv1 = 0xBB67AE8584CAA73B,
.iv2 = 0x3C6EF372FE94F82B,
@@ -374,242 +410,241 @@ const Sha512Params = Sha2Params64 {
.iv5 = 0x9B05688C2B3E6C1F,
.iv6 = 0x1F83D9ABFB41BD6B,
.iv7 = 0x5BE0CD19137E2179,
.out_len = 512
.out_len = 512,
};
pub const Sha384 = Sha2_64(Sha384Params);
pub const Sha512 = Sha2_64(Sha512Params);
fn Sha2_64(comptime params: Sha2Params64) type { return struct {
const Self = this;
const block_size = 128;
const digest_size = params.out_len / 8;
fn Sha2_64(comptime params: Sha2Params64) type {
return struct {
const Self = this;
const block_size = 128;
const digest_size = params.out_len / 8;
s: [8]u64,
// Streaming Cache
buf: [128]u8,
buf_len: u8,
total_len: u128,
s: [8]u64,
// Streaming Cache
buf: [128]u8,
buf_len: u8,
total_len: u128,
pub fn init() Self {
var d: Self = undefined;
d.reset();
return d;
}
pub fn init() Self {
var d: Self = undefined;
d.reset();
return d;
}
pub fn reset(d: &Self) void {
d.s[0] = params.iv0;
d.s[1] = params.iv1;
d.s[2] = params.iv2;
d.s[3] = params.iv3;
d.s[4] = params.iv4;
d.s[5] = params.iv5;
d.s[6] = params.iv6;
d.s[7] = params.iv7;
d.buf_len = 0;
d.total_len = 0;
}
pub fn hash(b: []const u8, out: []u8) void {
var d = Self.init();
d.update(b);
d.final(out);
}
pub fn update(d: &Self, b: []const u8) void {
var off: usize = 0;
// Partial buffer exists from previous update. Copy into buffer then hash.
if (d.buf_len != 0 and d.buf_len + b.len > 128) {
off += 128 - d.buf_len;
mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
d.round(d.buf[0..]);
pub fn reset(d: *Self) void {
d.s[0] = params.iv0;
d.s[1] = params.iv1;
d.s[2] = params.iv2;
d.s[3] = params.iv3;
d.s[4] = params.iv4;
d.s[5] = params.iv5;
d.s[6] = params.iv6;
d.s[7] = params.iv7;
d.buf_len = 0;
d.total_len = 0;
}
// Full middle blocks.
while (off + 128 <= b.len) : (off += 128) {
d.round(b[off..off + 128]);
pub fn hash(b: []const u8, out: []u8) void {
var d = Self.init();
d.update(b);
d.final(out);
}
// Copy any remainder for next pass.
mem.copy(u8, d.buf[d.buf_len..], b[off..]);
d.buf_len += u8(b[off..].len);
pub fn update(d: *Self, b: []const u8) void {
var off: usize = 0;
d.total_len += b.len;
}
// Partial buffer exists from previous update. Copy into buffer then hash.
if (d.buf_len != 0 and d.buf_len + b.len > 128) {
off += 128 - d.buf_len;
mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
pub fn final(d: &Self, out: []u8) void {
debug.assert(out.len >= params.out_len / 8);
d.round(d.buf[0..]);
d.buf_len = 0;
}
// The buffer here will never be completely full.
mem.set(u8, d.buf[d.buf_len..], 0);
// Full middle blocks.
while (off + 128 <= b.len) : (off += 128) {
d.round(b[off .. off + 128]);
}
// Append padding bits.
d.buf[d.buf_len] = 0x80;
d.buf_len += 1;
// Copy any remainder for next pass.
mem.copy(u8, d.buf[d.buf_len..], b[off..]);
d.buf_len += @intCast(u8, b[off..].len);
d.total_len += b.len;
}
pub fn final(d: *Self, out: []u8) void {
debug.assert(out.len >= params.out_len / 8);
// The buffer here will never be completely full.
mem.set(u8, d.buf[d.buf_len..], 0);
// Append padding bits.
d.buf[d.buf_len] = 0x80;
d.buf_len += 1;
// > 896 mod 1024 so need to add an extra round to wrap around.
if (128 - d.buf_len < 16) {
d.round(d.buf[0..]);
mem.set(u8, d.buf[0..], 0);
}
// Append message length.
var i: usize = 1;
var len = d.total_len >> 5;
d.buf[127] = @intCast(u8, d.total_len & 0x1f) << 3;
while (i < 16) : (i += 1) {
d.buf[127 - i] = @intCast(u8, len & 0xff);
len >>= 8;
}
// > 896 mod 1024 so need to add an extra round to wrap around.
if (128 - d.buf_len < 16) {
d.round(d.buf[0..]);
mem.set(u8, d.buf[0..], 0);
// May truncate for possible 384 output
const rr = d.s[0 .. params.out_len / 64];
for (rr) |s, j| {
mem.writeInt(out[8 * j .. 8 * j + 8], s, builtin.Endian.Big);
}
}
// Append message length.
var i: usize = 1;
var len = d.total_len >> 5;
d.buf[127] = u8(d.total_len & 0x1f) << 3;
while (i < 16) : (i += 1) {
d.buf[127 - i] = u8(len & 0xff);
len >>= 8;
fn round(d: *Self, b: []const u8) void {
debug.assert(b.len == 128);
var s: [80]u64 = undefined;
var i: usize = 0;
while (i < 16) : (i += 1) {
s[i] = 0;
s[i] |= u64(b[i * 8 + 0]) << 56;
s[i] |= u64(b[i * 8 + 1]) << 48;
s[i] |= u64(b[i * 8 + 2]) << 40;
s[i] |= u64(b[i * 8 + 3]) << 32;
s[i] |= u64(b[i * 8 + 4]) << 24;
s[i] |= u64(b[i * 8 + 5]) << 16;
s[i] |= u64(b[i * 8 + 6]) << 8;
s[i] |= u64(b[i * 8 + 7]) << 0;
}
while (i < 80) : (i += 1) {
s[i] = s[i - 16] +% s[i - 7] +% (math.rotr(u64, s[i - 15], u64(1)) ^ math.rotr(u64, s[i - 15], u64(8)) ^ (s[i - 15] >> 7)) +% (math.rotr(u64, s[i - 2], u64(19)) ^ math.rotr(u64, s[i - 2], u64(61)) ^ (s[i - 2] >> 6));
}
var v: [8]u64 = []u64{
d.s[0],
d.s[1],
d.s[2],
d.s[3],
d.s[4],
d.s[5],
d.s[6],
d.s[7],
};
const round0 = comptime []RoundParam512{
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98D728AE22),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x7137449123EF65CD),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCFEC4D3B2F),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA58189DBBC),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25BF348B538),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1B605D019),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4AF194F9B),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5DA6D8118),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98A3030242),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B0145706FBE),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE4EE4B28C),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3D5FFB4E2),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74F27B896F),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE3B1696B1),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A725C71235),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174CF692694),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C19EF14AD2),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786384F25E3),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC68B8CD5B5),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC77AC9C65),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F592B0275),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA6EA6E483),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DCBD41FBD4),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA831153B5),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152EE66DFAB),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D2DB43210),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C898FB213F),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7BEEF0EE4),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF33DA88FC2),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147930AA725),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351E003826F),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x142929670A0E6E70),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A8546D22FFC),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B21385C26C926),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC5AC42AED),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D139D95B3DF),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A73548BAF63DE),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB3C77B2A8),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E47EDAEE6),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C851482353B),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A14CF10364),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664BBC423001),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70D0F89791),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A30654BE30),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819D6EF5218),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD69906245565A910),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E35855771202A),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA07032BBD1B8),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116B8D2D0C8),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C085141AB53),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774CDF8EEB99),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5E19B48A8),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3C5C95A63),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4AE3418ACB),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F7763E373),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3D6B2B8A3),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE5DEFB2FC),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F43172F60),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814A1F0AB72),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC702081A6439EC),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA23631E28),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEBDE82BDE9),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7B2C67915),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2E372532B),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 64, 0xCA273ECEEA26619C),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 65, 0xD186B8C721C0C207),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 66, 0xEADA7DD6CDE0EB1E),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 67, 0xF57D4F7FEE6ED178),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 68, 0x06F067AA72176FBA),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 69, 0x0A637DC5A2C898A6),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 70, 0x113F9804BEF90DAE),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 71, 0x1B710B35131C471B),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 72, 0x28DB77F523047D84),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 73, 0x32CAAB7B40C72493),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 74, 0x3C9EBE0A15C9BEBC),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 75, 0x431D67C49C100D4C),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 76, 0x4CC5D4BECB3E42B6),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 77, 0x597F299CFC657E2A),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 78, 0x5FCB6FAB3AD6FAEC),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 79, 0x6C44198C4A475817),
};
inline for (round0) |r| {
v[r.h] = v[r.h] +% (math.rotr(u64, v[r.e], u64(14)) ^ math.rotr(u64, v[r.e], u64(18)) ^ math.rotr(u64, v[r.e], u64(41))) +% (v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +% r.k +% s[r.i];
v[r.d] = v[r.d] +% v[r.h];
v[r.h] = v[r.h] +% (math.rotr(u64, v[r.a], u64(28)) ^ math.rotr(u64, v[r.a], u64(34)) ^ math.rotr(u64, v[r.a], u64(39))) +% ((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c]));
}
d.s[0] +%= v[0];
d.s[1] +%= v[1];
d.s[2] +%= v[2];
d.s[3] +%= v[3];
d.s[4] +%= v[4];
d.s[5] +%= v[5];
d.s[6] +%= v[6];
d.s[7] +%= v[7];
}
d.round(d.buf[0..]);
// May truncate for possible 384 output
const rr = d.s[0 .. params.out_len / 64];
for (rr) |s, j| {
mem.writeInt(out[8*j .. 8*j + 8], s, builtin.Endian.Big);
}
}
fn round(d: &Self, b: []const u8) void {
debug.assert(b.len == 128);
var s: [80]u64 = undefined;
var i: usize = 0;
while (i < 16) : (i += 1) {
s[i] = 0;
s[i] |= u64(b[i*8+0]) << 56;
s[i] |= u64(b[i*8+1]) << 48;
s[i] |= u64(b[i*8+2]) << 40;
s[i] |= u64(b[i*8+3]) << 32;
s[i] |= u64(b[i*8+4]) << 24;
s[i] |= u64(b[i*8+5]) << 16;
s[i] |= u64(b[i*8+6]) << 8;
s[i] |= u64(b[i*8+7]) << 0;
}
while (i < 80) : (i += 1) {
s[i] =
s[i-16] +% s[i-7] +%
(math.rotr(u64, s[i-15], u64(1)) ^ math.rotr(u64, s[i-15], u64(8)) ^ (s[i-15] >> 7)) +%
(math.rotr(u64, s[i-2], u64(19)) ^ math.rotr(u64, s[i-2], u64(61)) ^ (s[i-2] >> 6));
}
var v: [8]u64 = []u64 {
d.s[0], d.s[1], d.s[2], d.s[3], d.s[4], d.s[5], d.s[6], d.s[7],
};
const round0 = comptime []RoundParam512 {
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 0, 0x428A2F98D728AE22),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 1, 0x7137449123EF65CD),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 2, 0xB5C0FBCFEC4D3B2F),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 3, 0xE9B5DBA58189DBBC),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 4, 0x3956C25BF348B538),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 5, 0x59F111F1B605D019),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 6, 0x923F82A4AF194F9B),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 7, 0xAB1C5ED5DA6D8118),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 8, 0xD807AA98A3030242),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 9, 0x12835B0145706FBE),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 10, 0x243185BE4EE4B28C),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 11, 0x550C7DC3D5FFB4E2),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 12, 0x72BE5D74F27B896F),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 13, 0x80DEB1FE3B1696B1),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 14, 0x9BDC06A725C71235),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 15, 0xC19BF174CF692694),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 16, 0xE49B69C19EF14AD2),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 17, 0xEFBE4786384F25E3),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 18, 0x0FC19DC68B8CD5B5),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 19, 0x240CA1CC77AC9C65),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 20, 0x2DE92C6F592B0275),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 21, 0x4A7484AA6EA6E483),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 22, 0x5CB0A9DCBD41FBD4),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 23, 0x76F988DA831153B5),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 24, 0x983E5152EE66DFAB),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 25, 0xA831C66D2DB43210),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 26, 0xB00327C898FB213F),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 27, 0xBF597FC7BEEF0EE4),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 28, 0xC6E00BF33DA88FC2),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 29, 0xD5A79147930AA725),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 30, 0x06CA6351E003826F),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 31, 0x142929670A0E6E70),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 32, 0x27B70A8546D22FFC),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 33, 0x2E1B21385C26C926),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 34, 0x4D2C6DFC5AC42AED),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 35, 0x53380D139D95B3DF),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 36, 0x650A73548BAF63DE),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 37, 0x766A0ABB3C77B2A8),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 38, 0x81C2C92E47EDAEE6),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 39, 0x92722C851482353B),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 40, 0xA2BFE8A14CF10364),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 41, 0xA81A664BBC423001),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 42, 0xC24B8B70D0F89791),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 43, 0xC76C51A30654BE30),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 44, 0xD192E819D6EF5218),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 45, 0xD69906245565A910),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 46, 0xF40E35855771202A),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 47, 0x106AA07032BBD1B8),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 48, 0x19A4C116B8D2D0C8),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 49, 0x1E376C085141AB53),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 50, 0x2748774CDF8EEB99),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 51, 0x34B0BCB5E19B48A8),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 52, 0x391C0CB3C5C95A63),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 53, 0x4ED8AA4AE3418ACB),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 54, 0x5B9CCA4F7763E373),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 55, 0x682E6FF3D6B2B8A3),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 56, 0x748F82EE5DEFB2FC),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 57, 0x78A5636F43172F60),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 58, 0x84C87814A1F0AB72),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 59, 0x8CC702081A6439EC),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 60, 0x90BEFFFA23631E28),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 61, 0xA4506CEBDE82BDE9),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 62, 0xBEF9A3F7B2C67915),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 63, 0xC67178F2E372532B),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 64, 0xCA273ECEEA26619C),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 65, 0xD186B8C721C0C207),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 66, 0xEADA7DD6CDE0EB1E),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 67, 0xF57D4F7FEE6ED178),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 68, 0x06F067AA72176FBA),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 69, 0x0A637DC5A2C898A6),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 70, 0x113F9804BEF90DAE),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 71, 0x1B710B35131C471B),
Rp512(0, 1, 2, 3, 4, 5, 6, 7, 72, 0x28DB77F523047D84),
Rp512(7, 0, 1, 2, 3, 4, 5, 6, 73, 0x32CAAB7B40C72493),
Rp512(6, 7, 0, 1, 2, 3, 4, 5, 74, 0x3C9EBE0A15C9BEBC),
Rp512(5, 6, 7, 0, 1, 2, 3, 4, 75, 0x431D67C49C100D4C),
Rp512(4, 5, 6, 7, 0, 1, 2, 3, 76, 0x4CC5D4BECB3E42B6),
Rp512(3, 4, 5, 6, 7, 0, 1, 2, 77, 0x597F299CFC657E2A),
Rp512(2, 3, 4, 5, 6, 7, 0, 1, 78, 0x5FCB6FAB3AD6FAEC),
Rp512(1, 2, 3, 4, 5, 6, 7, 0, 79, 0x6C44198C4A475817),
};
inline for (round0) |r| {
v[r.h] =
v[r.h] +%
(math.rotr(u64, v[r.e], u64(14)) ^ math.rotr(u64, v[r.e], u64(18)) ^ math.rotr(u64, v[r.e], u64(41))) +%
(v[r.g] ^ (v[r.e] & (v[r.f] ^ v[r.g]))) +%
r.k +% s[r.i];
v[r.d] = v[r.d] +% v[r.h];
v[r.h] =
v[r.h] +%
(math.rotr(u64, v[r.a], u64(28)) ^ math.rotr(u64, v[r.a], u64(34)) ^ math.rotr(u64, v[r.a], u64(39))) +%
((v[r.a] & (v[r.b] | v[r.c])) | (v[r.b] & v[r.c]));
}
d.s[0] +%= v[0];
d.s[1] +%= v[1];
d.s[2] +%= v[2];
d.s[3] +%= v[3];
d.s[4] +%= v[4];
d.s[5] +%= v[5];
d.s[6] +%= v[6];
d.s[7] +%= v[7];
}
};}
};
}
test "sha384 single" {
const h1 = "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b";
@@ -680,7 +715,7 @@ test "sha512 streaming" {
}
test "sha512 aligned final" {
var block = []u8 {0} ** Sha512.block_size;
var block = []u8{0} ** Sha512.block_size;
var out: [Sha512.digest_size]u8 = undefined;
var h = Sha512.init();

View File

@@ -10,148 +10,228 @@ pub const Sha3_256 = Keccak(256, 0x06);
pub const Sha3_384 = Keccak(384, 0x06);
pub const Sha3_512 = Keccak(512, 0x06);
fn Keccak(comptime bits: usize, comptime delim: u8) type { return struct {
const Self = this;
const block_size = 200;
const digest_size = bits / 8;
fn Keccak(comptime bits: usize, comptime delim: u8) type {
return struct {
const Self = this;
const block_size = 200;
const digest_size = bits / 8;
s: [200]u8,
offset: usize,
rate: usize,
s: [200]u8,
offset: usize,
rate: usize,
pub fn init() Self {
var d: Self = undefined;
d.reset();
return d;
}
pub fn init() Self {
var d: Self = undefined;
d.reset();
return d;
}
pub fn reset(d: &Self) void {
mem.set(u8, d.s[0..], 0);
d.offset = 0;
d.rate = 200 - (bits / 4);
}
pub fn reset(d: *Self) void {
mem.set(u8, d.s[0..], 0);
d.offset = 0;
d.rate = 200 - (bits / 4);
}
pub fn hash(b: []const u8, out: []u8) void {
var d = Self.init();
d.update(b);
d.final(out);
}
pub fn hash(b: []const u8, out: []u8) void {
var d = Self.init();
d.update(b);
d.final(out);
}
pub fn update(d: &Self, b: []const u8) void {
var ip: usize = 0;
var len = b.len;
var rate = d.rate - d.offset;
var offset = d.offset;
pub fn update(d: *Self, b: []const u8) void {
var ip: usize = 0;
var len = b.len;
var rate = d.rate - d.offset;
var offset = d.offset;
// absorb
while (len >= rate) {
for (d.s[offset .. offset + rate]) |*r, i|
*r ^= b[ip..][i];
// absorb
while (len >= rate) {
for (d.s[offset .. offset + rate]) |*r, i|
r.* ^= b[ip..][i];
keccak_f(1600, d.s[0..]);
ip += rate;
len -= rate;
rate = d.rate;
offset = 0;
}
for (d.s[offset .. offset + len]) |*r, i|
r.* ^= b[ip..][i];
d.offset = offset + len;
}
pub fn final(d: *Self, out: []u8) void {
// padding
d.s[d.offset] ^= delim;
d.s[d.rate - 1] ^= 0x80;
keccak_f(1600, d.s[0..]);
ip += rate;
len -= rate;
rate = d.rate;
offset = 0;
// squeeze
var op: usize = 0;
var len: usize = bits / 8;
while (len >= d.rate) {
mem.copy(u8, out[op..], d.s[0..d.rate]);
keccak_f(1600, d.s[0..]);
op += d.rate;
len -= d.rate;
}
mem.copy(u8, out[op..], d.s[0..len]);
}
};
}
for (d.s[offset .. offset + len]) |*r, i|
*r ^= b[ip..][i];
d.offset = offset + len;
}
pub fn final(d: &Self, out: []u8) void {
// padding
d.s[d.offset] ^= delim;
d.s[d.rate - 1] ^= 0x80;
keccak_f(1600, d.s[0..]);
// squeeze
var op: usize = 0;
var len: usize = bits / 8;
while (len >= d.rate) {
mem.copy(u8, out[op..], d.s[0..d.rate]);
keccak_f(1600, d.s[0..]);
op += d.rate;
len -= d.rate;
}
mem.copy(u8, out[op..], d.s[0..len]);
}
};}
const RC = []const u64 {
0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
const RC = []const u64{
0x0000000000000001,
0x0000000000008082,
0x800000000000808a,
0x8000000080008000,
0x000000000000808b,
0x0000000080000001,
0x8000000080008081,
0x8000000000008009,
0x000000000000008a,
0x0000000000000088,
0x0000000080008009,
0x000000008000000a,
0x000000008000808b,
0x800000000000008b,
0x8000000000008089,
0x8000000000008003,
0x8000000000008002,
0x8000000000000080,
0x000000000000800a,
0x800000008000000a,
0x8000000080008081,
0x8000000000008080,
0x0000000080000001,
0x8000000080008008,
};
const ROTC = []const usize {
1, 3, 6, 10, 15, 21, 28, 36,
45, 55, 2, 14, 27, 41, 56, 8,
25, 43, 62, 18, 39, 61, 20, 44
const ROTC = []const usize{
1,
3,
6,
10,
15,
21,
28,
36,
45,
55,
2,
14,
27,
41,
56,
8,
25,
43,
62,
18,
39,
61,
20,
44,
};
const PIL = []const usize {
10, 7, 11, 17, 18, 3, 5, 16,
8, 21, 24, 4, 15, 23, 19, 13,
12, 2, 20, 14, 22, 9, 6, 1
const PIL = []const usize{
10,
7,
11,
17,
18,
3,
5,
16,
8,
21,
24,
4,
15,
23,
19,
13,
12,
2,
20,
14,
22,
9,
6,
1,
};
const M5 = []const usize {
0, 1, 2, 3, 4, 0, 1, 2, 3, 4
const M5 = []const usize{
0,
1,
2,
3,
4,
0,
1,
2,
3,
4,
};
fn keccak_f(comptime F: usize, d: []u8) void {
debug.assert(d.len == F / 8);
const B = F / 25;
const no_rounds = comptime x: { break :x 12 + 2 * math.log2(B); };
const no_rounds = comptime x: {
break :x 12 + 2 * math.log2(B);
};
var s = []const u64 {0} ** 25;
var t = []const u64 {0} ** 1;
var c = []const u64 {0} ** 5;
var s = []const u64{0} ** 25;
var t = []const u64{0} ** 1;
var c = []const u64{0} ** 5;
for (s) |*r, i| {
*r = mem.readIntLE(u64, d[8*i .. 8*i + 8]);
r.* = mem.readIntLE(u64, d[8 * i .. 8 * i + 8]);
}
comptime var x: usize = 0;
comptime var y: usize = 0;
for (RC[0..no_rounds]) |round| {
// theta
x = 0; inline while (x < 5) : (x += 1) {
c[x] = s[x] ^ s[x+5] ^ s[x+10] ^ s[x+15] ^ s[x+20];
x = 0;
inline while (x < 5) : (x += 1) {
c[x] = s[x] ^ s[x + 5] ^ s[x + 10] ^ s[x + 15] ^ s[x + 20];
}
x = 0; inline while (x < 5) : (x += 1) {
t[0] = c[M5[x+4]] ^ math.rotl(u64, c[M5[x+1]], usize(1));
y = 0; inline while (y < 5) : (y += 1) {
s[x + y*5] ^= t[0];
x = 0;
inline while (x < 5) : (x += 1) {
t[0] = c[M5[x + 4]] ^ math.rotl(u64, c[M5[x + 1]], usize(1));
y = 0;
inline while (y < 5) : (y += 1) {
s[x + y * 5] ^= t[0];
}
}
// rho+pi
t[0] = s[1];
x = 0; inline while (x < 24) : (x += 1) {
x = 0;
inline while (x < 24) : (x += 1) {
c[0] = s[PIL[x]];
s[PIL[x]] = math.rotl(u64, t[0], ROTC[x]);
t[0] = c[0];
}
// chi
y = 0; inline while (y < 5) : (y += 1) {
x = 0; inline while (x < 5) : (x += 1) {
c[x] = s[x + y*5];
y = 0;
inline while (y < 5) : (y += 1) {
x = 0;
inline while (x < 5) : (x += 1) {
c[x] = s[x + y * 5];
}
x = 0; inline while (x < 5) : (x += 1) {
s[x + y*5] = c[x] ^ (~c[M5[x+1]] & c[M5[x+2]]);
x = 0;
inline while (x < 5) : (x += 1) {
s[x + y * 5] = c[x] ^ (~c[M5[x + 1]] & c[M5[x + 2]]);
}
}
@@ -160,11 +240,10 @@ fn keccak_f(comptime F: usize, d: []u8) void {
}
for (s) |r, i| {
mem.writeInt(d[8*i .. 8*i + 8], r, builtin.Endian.Little);
mem.writeInt(d[8 * i .. 8 * i + 8], r, builtin.Endian.Little);
}
}
test "sha3-224 single" {
htest.assertEqualHash(Sha3_224, "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", "");
htest.assertEqualHash(Sha3_224, "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc");
@@ -192,7 +271,7 @@ test "sha3-224 streaming" {
}
test "sha3-256 single" {
htest.assertEqualHash(Sha3_256, "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a" , "");
htest.assertEqualHash(Sha3_256, "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", "");
htest.assertEqualHash(Sha3_256, "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", "abc");
htest.assertEqualHash(Sha3_256, "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
}
@@ -218,7 +297,7 @@ test "sha3-256 streaming" {
}
test "sha3-256 aligned final" {
var block = []u8 {0} ** Sha3_256.block_size;
var block = []u8{0} ** Sha3_256.block_size;
var out: [Sha3_256.digest_size]u8 = undefined;
var h = Sha3_256.init();
@@ -228,7 +307,7 @@ test "sha3-256 aligned final" {
test "sha3-384 single" {
const h1 = "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004";
htest.assertEqualHash(Sha3_384, h1 , "");
htest.assertEqualHash(Sha3_384, h1, "");
const h2 = "ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25";
htest.assertEqualHash(Sha3_384, h2, "abc");
const h3 = "79407d3b5916b59c3e30b09822974791c313fb9ecc849e406f23592d04f625dc8c709b98b43b3852b337216179aa7fc7";
@@ -259,7 +338,7 @@ test "sha3-384 streaming" {
test "sha3-512 single" {
const h1 = "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26";
htest.assertEqualHash(Sha3_512, h1 , "");
htest.assertEqualHash(Sha3_512, h1, "");
const h2 = "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0";
htest.assertEqualHash(Sha3_512, h2, "abc");
const h3 = "afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185";
@@ -289,7 +368,7 @@ test "sha3-512 streaming" {
}
test "sha3-512 aligned final" {
var block = []u8 {0} ** Sha3_512.block_size;
var block = []u8{0} ** Sha3_512.block_size;
var out: [Sha3_512.digest_size]u8 = undefined;
var h = Sha3_512.init();

View File

@@ -14,9 +14,8 @@ pub fn assertEqualHash(comptime Hasher: var, comptime expected: []const u8, inpu
pub fn assertEqual(comptime expected: []const u8, input: []const u8) void {
var expected_bytes: [expected.len / 2]u8 = undefined;
for (expected_bytes) |*r, i| {
*r = fmt.parseInt(u8, expected[2*i .. 2*i+2], 16) catch unreachable;
r.* = fmt.parseInt(u8, expected[2 * i .. 2 * i + 2], 16) catch unreachable;
}
debug.assert(mem.eql(u8, expected_bytes, input));
}

View File

@@ -1,22 +1,17 @@
// Modify the HashFunction variable to the one wanted to test.
//
// NOTE: The throughput measurement may be slightly lower than other measurements since we run
// through our block alignment functions as well. Be aware when comparing against other tests.
//
// ```
// zig build-exe --release-fast --library c throughput_test.zig
// zig build-exe --release-fast throughput_test.zig
// ./throughput_test
// ```
const HashFunction = @import("md5.zig").Md5;
const BytesToHash = 1024 * Mb;
const std = @import("std");
const time = std.os.time;
const Timer = time.Timer;
const HashFunction = @import("md5.zig").Md5;
const c = @cImport({
@cInclude("time.h");
});
const Mb = 1024 * 1024;
const MiB = 1024 * 1024;
const BytesToHash = 1024 * MiB;
pub fn main() !void {
var stdout_file = try std.io.getStdOut();
@@ -29,15 +24,15 @@ pub fn main() !void {
var h = HashFunction.init();
var offset: usize = 0;
const start = c.clock();
var timer = try Timer.start();
const start = timer.lap();
while (offset < BytesToHash) : (offset += block.len) {
h.update(block[0..]);
}
const end = c.clock();
const end = timer.read();
const elapsed_s = f64((end - start) * c.CLOCKS_PER_SEC) / 1000000;
const throughput = u64(BytesToHash / elapsed_s);
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
const throughput = @floatToInt(u64, BytesToHash / elapsed_s);
try stdout.print("{}: ", @typeName(HashFunction));
try stdout.print("{} Mb/s\n", throughput);
try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB));
}

View File

@@ -9,14 +9,13 @@ pub const line_sep = switch (builtin.os) {
else => "\n",
};
pub fn len(ptr: &const u8) usize {
pub fn len(ptr: [*]const u8) usize {
var count: usize = 0;
while (ptr[count] != 0) : (count += 1) {}
return count;
}
pub fn cmp(a: &const u8, b: &const u8) i8 {
pub fn cmp(a: [*]const u8, b: [*]const u8) i8 {
var index: usize = 0;
while (a[index] == b[index] and a[index] != 0) : (index += 1) {}
if (a[index] > b[index]) {
@@ -28,11 +27,11 @@ pub fn cmp(a: &const u8, b: &const u8) i8 {
}
}
pub fn toSliceConst(str: &const u8) []const u8 {
pub fn toSliceConst(str: [*]const u8) []const u8 {
return str[0..len(str)];
}
pub fn toSlice(str: &u8) []u8 {
pub fn toSlice(str: [*]u8) []u8 {
return str[0..len(str)];
}
@@ -48,7 +47,7 @@ fn testCStrFnsImpl() void {
/// Returns a mutable slice with 1 more byte of length which is a null byte.
/// Caller owns the returned memory.
pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) ![]u8 {
pub fn addNullByte(allocator: *mem.Allocator, slice: []const u8) ![]u8 {
const result = try allocator.alloc(u8, slice.len + 1);
mem.copy(u8, result, slice);
result[slice.len] = 0;
@@ -56,13 +55,13 @@ pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) ![]u8 {
}
pub const NullTerminated2DArray = struct {
allocator: &mem.Allocator,
allocator: *mem.Allocator,
byte_count: usize,
ptr: ?&?&u8,
ptr: ?[*]?[*]u8,
/// Takes N lists of strings, concatenates the lists together, and adds a null terminator
/// Caller must deinit result
pub fn fromSlices(allocator: &mem.Allocator, slices: []const []const []const u8) !NullTerminated2DArray {
pub fn fromSlices(allocator: *mem.Allocator, slices: []const []const []const u8) !NullTerminated2DArray {
var new_len: usize = 1; // 1 for the list null
var byte_count: usize = 0;
for (slices) |slice| {
@@ -76,16 +75,16 @@ pub const NullTerminated2DArray = struct {
const index_size = @sizeOf(usize) * new_len; // size of the ptrs
byte_count += index_size;
const buf = try allocator.alignedAlloc(u8, @alignOf(?&u8), byte_count);
const buf = try allocator.alignedAlloc(u8, @alignOf(?*u8), byte_count);
errdefer allocator.free(buf);
var write_index = index_size;
const index_buf = ([]?&u8)(buf);
const index_buf = @bytesToSlice(?[*]u8, buf);
var i: usize = 0;
for (slices) |slice| {
for (slice) |inner| {
index_buf[i] = &buf[write_index];
index_buf[i] = buf.ptr + write_index;
i += 1;
mem.copy(u8, buf[write_index..], inner);
write_index += inner.len;
@@ -95,16 +94,15 @@ pub const NullTerminated2DArray = struct {
}
index_buf[i] = null;
return NullTerminated2DArray {
return NullTerminated2DArray{
.allocator = allocator,
.byte_count = byte_count,
.ptr = @ptrCast(?&?&u8, buf.ptr),
.ptr = @ptrCast(?[*]?[*]u8, buf.ptr),
};
}
pub fn deinit(self: &NullTerminated2DArray) void {
const buf = @ptrCast(&u8, self.ptr);
pub fn deinit(self: *NullTerminated2DArray) void {
const buf = @ptrCast([*]u8, self.ptr);
self.allocator.free(buf[0..self.byte_count]);
}
};

View File

@@ -7,20 +7,20 @@ pub const FailingAllocator = struct {
allocator: mem.Allocator,
index: usize,
fail_index: usize,
internal_allocator: &mem.Allocator,
internal_allocator: *mem.Allocator,
allocated_bytes: usize,
freed_bytes: usize,
deallocations: usize,
pub fn init(allocator: &mem.Allocator, fail_index: usize) FailingAllocator {
return FailingAllocator {
pub fn init(allocator: *mem.Allocator, fail_index: usize) FailingAllocator {
return FailingAllocator{
.internal_allocator = allocator,
.fail_index = fail_index,
.index = 0,
.allocated_bytes = 0,
.freed_bytes = 0,
.deallocations = 0,
.allocator = mem.Allocator {
.allocator = mem.Allocator{
.allocFn = alloc,
.reallocFn = realloc,
.freeFn = free,
@@ -28,7 +28,7 @@ pub const FailingAllocator = struct {
};
}
fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) ![]u8 {
fn alloc(allocator: *mem.Allocator, n: usize, alignment: u29) ![]u8 {
const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
if (self.index == self.fail_index) {
return error.OutOfMemory;
@@ -39,7 +39,7 @@ pub const FailingAllocator = struct {
return result;
}
fn realloc(allocator: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
fn realloc(allocator: *mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
if (new_size <= old_mem.len) {
self.freed_bytes += old_mem.len - new_size;
@@ -55,7 +55,7 @@ pub const FailingAllocator = struct {
return result;
}
fn free(allocator: &mem.Allocator, bytes: []u8) void {
fn free(allocator: *mem.Allocator, bytes: []u8) void {
const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
self.freed_bytes += bytes.len;
self.deallocations += 1;

View File

@@ -10,18 +10,24 @@ const ArrayList = std.ArrayList;
const builtin = @import("builtin");
pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
pub const failing_allocator = FailingAllocator.init(global_allocator, 0);
pub const runtime_safety = switch (builtin.mode) {
builtin.Mode.Debug, builtin.Mode.ReleaseSafe => true,
builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false,
};
/// Tries to write to stderr, unbuffered, and ignores any error returned.
/// Does not append a newline.
/// TODO atomic/multithread support
var stderr_file: os.File = undefined;
var stderr_file_out_stream: io.FileOutStream = undefined;
var stderr_stream: ?&io.OutStream(io.FileOutStream.Error) = null;
var stderr_stream: ?*io.OutStream(io.FileOutStream.Error) = null;
pub fn warn(comptime fmt: []const u8, args: ...) void {
const stderr = getStderrStream() catch return;
stderr.print(fmt, args) catch return;
}
fn getStderrStream() !&io.OutStream(io.FileOutStream.Error) {
pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
if (stderr_stream) |st| {
return st;
} else {
@@ -33,17 +39,23 @@ fn getStderrStream() !&io.OutStream(io.FileOutStream.Error) {
}
}
var self_debug_info: ?&ElfStackTrace = null;
pub fn getSelfDebugInfo() !&ElfStackTrace {
var self_debug_info: ?*ElfStackTrace = null;
pub fn getSelfDebugInfo() !*ElfStackTrace {
if (self_debug_info) |info| {
return info;
} else {
const info = try openSelfDebugInfo(global_allocator);
const info = try openSelfDebugInfo(getDebugInfoAllocator());
self_debug_info = info;
return info;
}
}
fn wantTtyColor() bool {
var bytes: [128]u8 = undefined;
const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
return if (std.os.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty();
}
/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
const stderr = getStderrStream() catch return;
@@ -51,20 +63,20 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
return;
};
writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), start_addr) catch |err| {
writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, wantTtyColor(), start_addr) catch |err| {
stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
return;
};
}
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void {
pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void {
const stderr = getStderrStream() catch return;
const debug_info = getSelfDebugInfo() catch |err| {
stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
return;
};
writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| {
writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, wantTtyColor()) catch |err| {
stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
return;
};
@@ -88,6 +100,16 @@ pub fn assert(ok: bool) void {
}
}
/// TODO: add `==` operator for `error_union == error_set`, and then
/// remove this function
pub fn assertError(value: var, expected_error: error) void {
if (value) {
@panic("expected error");
} else |actual_error| {
assert(actual_error == expected_error);
}
}
/// Call this function when you want to panic if the condition is not true.
/// If `ok` is `false`, this function will panic in every release mode.
pub fn assertOrPanic(ok: bool) void {
@@ -104,9 +126,7 @@ pub fn panic(comptime format: []const u8, args: ...) noreturn {
var panicking: u8 = 0; // TODO make this a bool
pub fn panicExtra(trace: ?&const builtin.StackTrace, first_trace_addr: ?usize,
comptime format: []const u8, args: ...) noreturn
{
pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn {
@setCold(true);
if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) {
@@ -132,9 +152,7 @@ const WHITE = "\x1b[37;1m";
const DIM = "\x1b[2m";
const RESET = "\x1b[0m";
pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, allocator: &mem.Allocator,
debug_info: &ElfStackTrace, tty_color: bool) !void
{
pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool) !void {
var frame_index: usize = undefined;
var frames_left: usize = undefined;
if (stack_trace.index < stack_trace.instruction_addresses.len) {
@@ -150,13 +168,21 @@ pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var,
frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
}) {
const return_address = stack_trace.instruction_addresses[frame_index];
try printSourceAtAddress(debug_info, out_stream, return_address);
try printSourceAtAddress(debug_info, out_stream, return_address, tty_color);
}
}
pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
debug_info: &ElfStackTrace, tty_color: bool, start_addr: ?usize) !void
{
pub inline fn getReturnAddress(frame_count: usize) usize {
var fp = @ptrToInt(@frameAddress());
var i: usize = 0;
while (fp != 0 and i < frame_count) {
fp = @intToPtr(*const usize, fp).*;
i += 1;
}
return @intToPtr(*const usize, fp + @sizeOf(usize)).*;
}
pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void {
const AddressState = union(enum) {
NotLookingForStartAddress,
LookingForStartAddress: usize,
@@ -166,14 +192,14 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
// else AddressState.NotLookingForStartAddress;
var addr_state: AddressState = undefined;
if (start_addr) |addr| {
addr_state = AddressState { .LookingForStartAddress = addr };
addr_state = AddressState{ .LookingForStartAddress = addr };
} else {
addr_state = AddressState.NotLookingForStartAddress;
}
var fp = @ptrToInt(@frameAddress());
while (fp != 0) : (fp = *@intToPtr(&const usize, fp)) {
const return_address = *@intToPtr(&const usize, fp + @sizeOf(usize));
while (fp != 0) : (fp = @intToPtr(*const usize, fp).*) {
const return_address = @intToPtr(*const usize, fp + @sizeOf(usize)).*;
switch (addr_state) {
AddressState.NotLookingForStartAddress => {},
@@ -185,13 +211,11 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
}
},
}
try printSourceAtAddress(debug_info, out_stream, return_address);
try printSourceAtAddress(debug_info, out_stream, return_address, tty_color);
}
}
fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void {
const ptr_hex = "0x{x}";
pub fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void {
switch (builtin.os) {
builtin.Os.windows => return error.UnsupportedDebugInfo,
builtin.Os.macosx => {
@@ -200,41 +224,63 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: us
// in practice because the compiler dumps everything in a single
// object file. Future improvement: use external dSYM data when
// available.
const unknown = macho.Symbol { .name = "???", .address = address };
const symbol = debug_info.symbol_table.search(address) ?? &unknown;
try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++
DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n",
symbol.name, address);
const unknown = macho.Symbol{
.name = "???",
.address = address,
};
const symbol = debug_info.symbol_table.search(address) orelse &unknown;
try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ "0x{x}" ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address);
},
else => {
const compile_unit = findCompileUnit(debug_info, address) catch {
try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
address);
if (tty_color) {
try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n ???\n\n", address);
} else {
try out_stream.print("???:?:?: 0x{x} in ??? (???)\n ???\n\n", address);
}
return;
};
const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
defer line_info.deinit();
try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
line_info.file_name, line_info.line, line_info.column,
address, compile_unit_name);
if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
if (line_info.column == 0) {
try out_stream.write("\n");
} else {
{var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
try out_stream.writeByte(' ');
}}
try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
if (tty_color) {
try out_stream.print(
WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n",
line_info.file_name,
line_info.line,
line_info.column,
address,
compile_unit_name,
);
if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
if (line_info.column == 0) {
try out_stream.write("\n");
} else {
{
var col_i: usize = 1;
while (col_i < line_info.column) : (col_i += 1) {
try out_stream.writeByte(' ');
}
}
try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
}
} else |err| switch (err) {
error.EndOfFile => {},
else => return err,
}
} else |err| switch (err) {
error.EndOfFile => {},
else => return err,
} else {
try out_stream.print(
"{}:{}:{}: 0x{x} in ??? ({})\n",
line_info.file_name,
line_info.line,
line_info.column,
address,
compile_unit_name,
);
}
} else |err| switch (err) {
error.MissingDebugInfo, error.InvalidDebugInfo => {
try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
try out_stream.print("0x{x} in ??? ({})\n", address, compile_unit_name);
},
else => return err,
}
@@ -242,12 +288,10 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: us
}
}
pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace {
switch (builtin.object_format) {
builtin.ObjectFormat.elf => {
const st = try allocator.create(ElfStackTrace);
errdefer allocator.destroy(st);
*st = ElfStackTrace {
const st = try allocator.create(ElfStackTrace{
.self_exe_file = undefined,
.elf = undefined,
.debug_info = undefined,
@@ -257,17 +301,18 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
.debug_ranges = null,
.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
.compile_unit_list = ArrayList(CompileUnit).init(allocator),
};
});
errdefer allocator.destroy(st);
st.self_exe_file = try os.openSelfExe();
errdefer st.self_exe_file.close();
try st.elf.openFile(allocator, &st.self_exe_file);
errdefer st.elf.close();
st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo;
st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
st.debug_info = (try st.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo;
st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) orelse return error.MissingDebugInfo;
st.debug_str = (try st.elf.findSection(".debug_str")) orelse return error.MissingDebugInfo;
st.debug_line = (try st.elf.findSection(".debug_line")) orelse return error.MissingDebugInfo;
st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
try scanAllCompileUnits(st);
return st;
@@ -276,13 +321,8 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
var exe_file = try os.openSelfExe();
defer exe_file.close();
const st = try allocator.create(ElfStackTrace);
const st = try allocator.create(ElfStackTrace{ .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)) });
errdefer allocator.destroy(st);
*st = ElfStackTrace {
.symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)),
};
return st;
},
builtin.ObjectFormat.coff => {
@@ -297,7 +337,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
}
}
fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &const LineInfo) !void {
fn printLineFromFile(allocator: *mem.Allocator, out_stream: var, line_info: *const LineInfo) !void {
var f = try os.File.openRead(allocator, line_info.file_name);
defer f.close();
// TODO fstat and make sure that the file has the correct size
@@ -325,8 +365,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con
}
}
if (amt_read < buf.len)
return error.EndOfFile;
if (amt_read < buf.len) return error.EndOfFile;
}
}
@@ -334,32 +373,32 @@ pub const ElfStackTrace = switch (builtin.os) {
builtin.Os.macosx => struct {
symbol_table: macho.SymbolTable,
pub fn close(self: &ElfStackTrace) void {
pub fn close(self: *ElfStackTrace) void {
self.symbol_table.deinit();
}
},
else => struct {
self_exe_file: os.File,
elf: elf.Elf,
debug_info: &elf.SectionHeader,
debug_abbrev: &elf.SectionHeader,
debug_str: &elf.SectionHeader,
debug_line: &elf.SectionHeader,
debug_ranges: ?&elf.SectionHeader,
debug_info: *elf.SectionHeader,
debug_abbrev: *elf.SectionHeader,
debug_str: *elf.SectionHeader,
debug_line: *elf.SectionHeader,
debug_ranges: ?*elf.SectionHeader,
abbrev_table_list: ArrayList(AbbrevTableHeader),
compile_unit_list: ArrayList(CompileUnit),
pub fn allocator(self: &const ElfStackTrace) &mem.Allocator {
pub fn allocator(self: *const ElfStackTrace) *mem.Allocator {
return self.abbrev_table_list.allocator;
}
pub fn readString(self: &ElfStackTrace) ![]u8 {
pub fn readString(self: *ElfStackTrace) ![]u8 {
var in_file_stream = io.FileInStream.init(&self.self_exe_file);
const in_stream = &in_file_stream.stream;
return readStringRaw(self.allocator(), in_stream);
}
pub fn close(self: &ElfStackTrace) void {
pub fn close(self: *ElfStackTrace) void {
self.self_exe_file.close();
self.elf.close();
}
@@ -374,7 +413,7 @@ const PcRange = struct {
const CompileUnit = struct {
version: u16,
is_64: bool,
die: &Die,
die: *Die,
index: usize,
pc_range: ?PcRange,
};
@@ -417,11 +456,9 @@ const Constant = struct {
payload: []u8,
signed: bool,
fn asUnsignedLe(self: &const Constant) !u64 {
if (self.payload.len > @sizeOf(u64))
return error.InvalidDebugInfo;
if (self.signed)
return error.InvalidDebugInfo;
fn asUnsignedLe(self: *const Constant) !u64 {
if (self.payload.len > @sizeOf(u64)) return error.InvalidDebugInfo;
if (self.signed) return error.InvalidDebugInfo;
return mem.readInt(self.payload, u64, builtin.Endian.Little);
}
};
@@ -436,42 +473,41 @@ const Die = struct {
value: FormValue,
};
fn getAttr(self: &const Die, id: u64) ?&const FormValue {
fn getAttr(self: *const Die, id: u64) ?*const FormValue {
for (self.attrs.toSliceConst()) |*attr| {
if (attr.id == id)
return &attr.value;
if (attr.id == id) return &attr.value;
}
return null;
}
fn getAttrAddr(self: &const Die, id: u64) !u64 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
return switch (*form_value) {
fn getAttrAddr(self: *const Die, id: u64) !u64 {
const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
return switch (form_value.*) {
FormValue.Address => |value| value,
else => error.InvalidDebugInfo,
};
}
fn getAttrSecOffset(self: &const Die, id: u64) !u64 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
return switch (*form_value) {
fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
return switch (form_value.*) {
FormValue.Const => |value| value.asUnsignedLe(),
FormValue.SecOffset => |value| value,
else => error.InvalidDebugInfo,
};
}
fn getAttrUnsignedLe(self: &const Die, id: u64) !u64 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
return switch (*form_value) {
fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
return switch (form_value.*) {
FormValue.Const => |value| value.asUnsignedLe(),
else => error.InvalidDebugInfo,
};
}
fn getAttrString(self: &const Die, st: &ElfStackTrace, id: u64) ![]u8 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
return switch (*form_value) {
fn getAttrString(self: *const Die, st: *ElfStackTrace, id: u64) ![]u8 {
const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
return switch (form_value.*) {
FormValue.String => |value| value,
FormValue.StrPtr => |offset| getString(st, offset),
else => error.InvalidDebugInfo,
@@ -490,9 +526,9 @@ const LineInfo = struct {
line: usize,
column: usize,
file_name: []u8,
allocator: &mem.Allocator,
allocator: *mem.Allocator,
fn deinit(self: &const LineInfo) void {
fn deinit(self: *const LineInfo) void {
self.allocator.free(self.file_name);
}
};
@@ -508,7 +544,7 @@ const LineNumberProgram = struct {
target_address: usize,
include_dirs: []const []const u8,
file_entries: &ArrayList(FileEntry),
file_entries: *ArrayList(FileEntry),
prev_address: usize,
prev_file: usize,
@@ -518,10 +554,8 @@ const LineNumberProgram = struct {
prev_basic_block: bool,
prev_end_sequence: bool,
pub fn init(is_stmt: bool, include_dirs: []const []const u8,
file_entries: &ArrayList(FileEntry), target_address: usize) LineNumberProgram
{
return LineNumberProgram {
pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram {
return LineNumberProgram{
.address = 0,
.file = 1,
.line = 1,
@@ -542,21 +576,23 @@ const LineNumberProgram = struct {
};
}
pub fn checkLineMatch(self: &LineNumberProgram) !?LineInfo {
pub fn checkLineMatch(self: *LineNumberProgram) !?LineInfo {
if (self.target_address >= self.prev_address and self.target_address < self.address) {
const file_entry = if (self.prev_file == 0) {
return error.MissingDebugInfo;
} else if (self.prev_file - 1 >= self.file_entries.len) {
return error.InvalidDebugInfo;
} else &self.file_entries.items[self.prev_file - 1];
} else
&self.file_entries.items[self.prev_file - 1];
const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {
return error.InvalidDebugInfo;
} else self.include_dirs[file_entry.dir_index];
} else
self.include_dirs[file_entry.dir_index];
const file_name = try os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name);
errdefer self.file_entries.allocator.free(file_name);
return LineInfo {
.line = if (self.prev_line >= 0) usize(self.prev_line) else 0,
return LineInfo{
.line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0,
.column = self.prev_column,
.file_name = file_name,
.allocator = self.file_entries.allocator,
@@ -574,83 +610,80 @@ const LineNumberProgram = struct {
}
};
fn readStringRaw(allocator: &mem.Allocator, in_stream: var) ![]u8 {
fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 {
var buf = ArrayList(u8).init(allocator);
while (true) {
const byte = try in_stream.readByte();
if (byte == 0)
break;
if (byte == 0) break;
try buf.append(byte);
}
return buf.toSlice();
}
fn getString(st: &ElfStackTrace, offset: u64) ![]u8 {
fn getString(st: *ElfStackTrace, offset: u64) ![]u8 {
const pos = st.debug_str.offset + offset;
try st.self_exe_file.seekTo(pos);
return st.readString();
}
fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 {
const buf = try global_allocator.alloc(u8, size);
errdefer global_allocator.free(buf);
fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {
const buf = try allocator.alloc(u8, size);
errdefer allocator.free(buf);
if ((try in_stream.read(buf)) < size) return error.EndOfFile;
return buf;
}
fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
const buf = try readAllocBytes(allocator, in_stream, size);
return FormValue { .Block = buf };
return FormValue{ .Block = buf };
}
fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
const block_len = try in_stream.readVarInt(builtin.Endian.Little, usize, size);
return parseFormValueBlockLen(allocator, in_stream, block_len);
}
fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue {
return FormValue { .Const = Constant {
.signed = signed,
.payload = try readAllocBytes(allocator, in_stream, size),
}};
fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue {
return FormValue{
.Const = Constant{
.signed = signed,
.payload = try readAllocBytes(allocator, in_stream, size),
},
};
}
fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 {
return if (is_64) try in_stream.readIntLe(u64)
else u64(try in_stream.readIntLe(u32)) ;
return if (is_64) try in_stream.readIntLe(u64) else u64(try in_stream.readIntLe(u32));
}
fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32))
else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64)
else unreachable;
return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64) else unreachable;
}
fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue {
fn parseFormValueRefLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
const buf = try readAllocBytes(allocator, in_stream, size);
return FormValue { .Ref = buf };
return FormValue{ .Ref = buf };
}
fn parseFormValueRef(allocator: &mem.Allocator, in_stream: var, comptime T: type) !FormValue {
fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type) !FormValue {
const block_len = try in_stream.readIntLe(T);
return parseFormValueRefLen(allocator, in_stream, block_len);
}
const ParseFormValueError = error {
const ParseFormValueError = error{
EndOfStream,
Io,
BadFd,
Unexpected,
InvalidDebugInfo,
EndOfFile,
IsDir,
OutOfMemory,
};
fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64: bool)
ParseFormValueError!FormValue
{
fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue {
return switch (form_id) {
DW.FORM_addr => FormValue { .Address = try parseFormValueTargetAddrSize(in_stream) },
DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) },
DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2),
DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4),
@@ -670,11 +703,11 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64
DW.FORM_exprloc => {
const size = try readULeb128(in_stream);
const buf = try readAllocBytes(allocator, in_stream, size);
return FormValue { .ExprLoc = buf };
return FormValue{ .ExprLoc = buf };
},
DW.FORM_flag => FormValue { .Flag = (try in_stream.readByte()) != 0 },
DW.FORM_flag_present => FormValue { .Flag = true },
DW.FORM_sec_offset => FormValue { .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_flag => FormValue{ .Flag = (try in_stream.readByte()) != 0 },
DW.FORM_flag_present => FormValue{ .Flag = true },
DW.FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, u8),
DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, u16),
@@ -685,11 +718,11 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64
return parseFormValueRefLen(allocator, in_stream, ref_len);
},
DW.FORM_ref_addr => FormValue { .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_ref_sig8 => FormValue { .RefSig8 = try in_stream.readIntLe(u64) },
DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_ref_sig8 => FormValue{ .RefSig8 = try in_stream.readIntLe(u64) },
DW.FORM_string => FormValue { .String = try readStringRaw(allocator, in_stream) },
DW.FORM_strp => FormValue { .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) },
DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_indirect => {
const child_form_id = try readULeb128(in_stream);
return parseFormValue(allocator, in_stream, child_form_id, is_64);
@@ -698,16 +731,15 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64
};
}
fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable {
fn parseAbbrevTable(st: *ElfStackTrace) !AbbrevTable {
const in_file = &st.self_exe_file;
var in_file_stream = io.FileInStream.init(in_file);
const in_stream = &in_file_stream.stream;
var result = AbbrevTable.init(st.allocator());
while (true) {
const abbrev_code = try readULeb128(in_stream);
if (abbrev_code == 0)
return result;
try result.append(AbbrevTableEntry {
if (abbrev_code == 0) return result;
try result.append(AbbrevTableEntry{
.abbrev_code = abbrev_code,
.tag_id = try readULeb128(in_stream),
.has_children = (try in_stream.readByte()) == DW.CHILDREN_yes,
@@ -718,9 +750,8 @@ fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable {
while (true) {
const attr_id = try readULeb128(in_stream);
const form_id = try readULeb128(in_stream);
if (attr_id == 0 and form_id == 0)
break;
try attrs.append(AbbrevAttr {
if (attr_id == 0 and form_id == 0) break;
try attrs.append(AbbrevAttr{
.attr_id = attr_id,
.form_id = form_id,
});
@@ -730,43 +761,42 @@ fn parseAbbrevTable(st: &ElfStackTrace) !AbbrevTable {
/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
/// seeks in the stream and parses it.
fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) !&const AbbrevTable {
fn getAbbrevTable(st: *ElfStackTrace, abbrev_offset: u64) !*const AbbrevTable {
for (st.abbrev_table_list.toSlice()) |*header| {
if (header.offset == abbrev_offset) {
return &header.table;
}
}
try st.self_exe_file.seekTo(st.debug_abbrev.offset + abbrev_offset);
try st.abbrev_table_list.append(AbbrevTableHeader {
try st.abbrev_table_list.append(AbbrevTableHeader{
.offset = abbrev_offset,
.table = try parseAbbrevTable(st),
});
return &st.abbrev_table_list.items[st.abbrev_table_list.len - 1].table;
}
fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) ?&const AbbrevTableEntry {
fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry {
for (abbrev_table.toSliceConst()) |*table_entry| {
if (table_entry.abbrev_code == abbrev_code)
return table_entry;
if (table_entry.abbrev_code == abbrev_code) return table_entry;
}
return null;
}
fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) !Die {
fn parseDie(st: *ElfStackTrace, abbrev_table: *const AbbrevTable, is_64: bool) !Die {
const in_file = &st.self_exe_file;
var in_file_stream = io.FileInStream.init(in_file);
const in_stream = &in_file_stream.stream;
const abbrev_code = try readULeb128(in_stream);
const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo;
const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
var result = Die {
var result = Die{
.tag_id = table_entry.tag_id,
.has_children = table_entry.has_children,
.attrs = ArrayList(Die.Attr).init(st.allocator()),
};
try result.attrs.resize(table_entry.attrs.len);
for (table_entry.attrs.toSliceConst()) |attr, i| {
result.attrs.items[i] = Die.Attr {
result.attrs.items[i] = Die.Attr{
.id = attr.attr_id,
.value = try parseFormValue(st.allocator(), in_stream, attr.form_id, is_64),
};
@@ -774,7 +804,7 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) !
return result;
}
fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, target_address: usize) !LineInfo {
fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, target_address: usize) !LineInfo {
const compile_unit_cwd = try compile_unit.die.getAttrString(st, DW.AT_comp_dir);
const in_file = &st.self_exe_file;
@@ -790,8 +820,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
var is_64: bool = undefined;
const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64);
if (unit_length == 0)
return error.MissingDebugInfo;
if (unit_length == 0) return error.MissingDebugInfo;
const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
if (compile_unit.index != this_index) {
@@ -803,8 +832,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
// TODO support 3 and 5
if (version != 2 and version != 4) return error.InvalidDebugInfo;
const prologue_length = if (is_64) try in_stream.readInt(st.elf.endian, u64)
else try in_stream.readInt(st.elf.endian, u32);
const prologue_length = if (is_64) try in_stream.readInt(st.elf.endian, u64) else try in_stream.readInt(st.elf.endian, u32);
const prog_start_offset = (try in_file.getPos()) + prologue_length;
const minimum_instruction_length = try in_stream.readByte();
@@ -819,38 +847,37 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
const line_base = try in_stream.readByteSigned();
const line_range = try in_stream.readByte();
if (line_range == 0)
return error.InvalidDebugInfo;
if (line_range == 0) return error.InvalidDebugInfo;
const opcode_base = try in_stream.readByte();
const standard_opcode_lengths = try st.allocator().alloc(u8, opcode_base - 1);
{var i: usize = 0; while (i < opcode_base - 1) : (i += 1) {
standard_opcode_lengths[i] = try in_stream.readByte();
}}
{
var i: usize = 0;
while (i < opcode_base - 1) : (i += 1) {
standard_opcode_lengths[i] = try in_stream.readByte();
}
}
var include_directories = ArrayList([]u8).init(st.allocator());
try include_directories.append(compile_unit_cwd);
while (true) {
const dir = try st.readString();
if (dir.len == 0)
break;
if (dir.len == 0) break;
try include_directories.append(dir);
}
var file_entries = ArrayList(FileEntry).init(st.allocator());
var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(),
&file_entries, target_address);
var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
while (true) {
const file_name = try st.readString();
if (file_name.len == 0)
break;
if (file_name.len == 0) break;
const dir_index = try readULeb128(in_stream);
const mtime = try readULeb128(in_stream);
const len_bytes = try readULeb128(in_stream);
try file_entries.append(FileEntry {
try file_entries.append(FileEntry{
.file_name = file_name,
.dir_index = dir_index,
.mtime = mtime,
@@ -866,8 +893,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
var sub_op: u8 = undefined; // TODO move this to the correct scope and fix the compiler crash
if (opcode == DW.LNS_extended_op) {
const op_size = try readULeb128(in_stream);
if (op_size < 1)
return error.InvalidDebugInfo;
if (op_size < 1) return error.InvalidDebugInfo;
sub_op = try in_stream.readByte();
switch (sub_op) {
DW.LNE_end_sequence => {
@@ -884,7 +910,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
const dir_index = try readULeb128(in_stream);
const mtime = try readULeb128(in_stream);
const len_bytes = try readULeb128(in_stream);
try file_entries.append(FileEntry {
try file_entries.append(FileEntry{
.file_name = file_name,
.dir_index = dir_index,
.mtime = mtime,
@@ -941,11 +967,9 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
const arg = try in_stream.readInt(st.elf.endian, u16);
prog.address += arg;
},
DW.LNS_set_prologue_end => {
},
DW.LNS_set_prologue_end => {},
else => {
if (opcode - 1 >= standard_opcode_lengths.len)
return error.InvalidDebugInfo;
if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
const len_bytes = standard_opcode_lengths[opcode - 1];
try in_file.seekForward(len_bytes);
},
@@ -959,7 +983,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
return error.MissingDebugInfo;
}
fn scanAllCompileUnits(st: &ElfStackTrace) !void {
fn scanAllCompileUnits(st: *ElfStackTrace) !void {
const debug_info_end = st.debug_info.offset + st.debug_info.size;
var this_unit_offset = st.debug_info.offset;
var cu_index: usize = 0;
@@ -972,16 +996,13 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void {
var is_64: bool = undefined;
const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64);
if (unit_length == 0)
return;
if (unit_length == 0) return;
const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
const version = try in_stream.readInt(st.elf.endian, u16);
if (version < 2 or version > 5) return error.InvalidDebugInfo;
const debug_abbrev_offset =
if (is_64) try in_stream.readInt(st.elf.endian, u64)
else try in_stream.readInt(st.elf.endian, u32);
const debug_abbrev_offset = if (is_64) try in_stream.readInt(st.elf.endian, u64) else try in_stream.readInt(st.elf.endian, u32);
const address_size = try in_stream.readByte();
if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
@@ -991,16 +1012,14 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void {
try st.self_exe_file.seekTo(compile_unit_pos);
const compile_unit_die = try st.allocator().create(Die);
*compile_unit_die = try parseDie(st, abbrev_table, is_64);
const compile_unit_die = try st.allocator().create(try parseDie(st, abbrev_table, is_64));
if (compile_unit_die.tag_id != DW.TAG_compile_unit)
return error.InvalidDebugInfo;
if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo;
const pc_range = x: {
if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| {
if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| {
const pc_end = switch (*high_pc_value) {
const pc_end = switch (high_pc_value.*) {
FormValue.Address => |value| value,
FormValue.Const => |value| b: {
const offset = try value.asUnsignedLe();
@@ -1008,7 +1027,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void {
},
else => return error.InvalidDebugInfo,
};
break :x PcRange {
break :x PcRange{
.start = low_pc,
.end = pc_end,
};
@@ -1016,13 +1035,12 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void {
break :x null;
}
} else |err| {
if (err != error.MissingDebugInfo)
return err;
if (err != error.MissingDebugInfo) return err;
break :x null;
}
};
try st.compile_unit_list.append(CompileUnit {
try st.compile_unit_list.append(CompileUnit{
.version = version,
.is_64 = is_64,
.pc_range = pc_range,
@@ -1035,13 +1053,12 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void {
}
}
fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit {
fn findCompileUnit(st: *ElfStackTrace, target_address: u64) !*const CompileUnit {
var in_file_stream = io.FileInStream.init(&st.self_exe_file);
const in_stream = &in_file_stream.stream;
for (st.compile_unit_list.toSlice()) |*compile_unit| {
if (compile_unit.pc_range) |range| {
if (target_address >= range.start and target_address < range.end)
return compile_unit;
if (target_address >= range.start and target_address < range.end) return compile_unit;
}
if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| {
var base_address: usize = 0;
@@ -1063,18 +1080,17 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit
}
}
} else |err| {
if (err != error.MissingDebugInfo)
return err;
if (err != error.MissingDebugInfo) return err;
continue;
}
}
return error.MissingDebugInfo;
}
fn readInitialLength(comptime E: type, in_stream: &io.InStream(E), is_64: &bool) !u64 {
fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
const first_32_bits = try in_stream.readIntLe(u32);
*is_64 = (first_32_bits == 0xffffffff);
if (*is_64) {
is_64.* = (first_32_bits == 0xffffffff);
if (is_64.*) {
return in_stream.readIntLe(u64);
} else {
if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
@@ -1091,13 +1107,11 @@ fn readULeb128(in_stream: var) !u64 {
var operand: u64 = undefined;
if (@shlWithOverflow(u64, byte & 0b01111111, u6(shift), &operand))
return error.InvalidDebugInfo;
if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
result |= operand;
if ((byte & 0b10000000) == 0)
return result;
if ((byte & 0b10000000) == 0) return result;
shift += 7;
}
@@ -1112,20 +1126,32 @@ fn readILeb128(in_stream: var) !i64 {
var operand: i64 = undefined;
if (@shlWithOverflow(i64, byte & 0b01111111, u6(shift), &operand))
return error.InvalidDebugInfo;
if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
result |= operand;
shift += 7;
if ((byte & 0b10000000) == 0) {
if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0)
result |= -(i64(1) << u6(shift));
if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift));
return result;
}
}
}
/// This should only be used in temporary test programs.
pub const global_allocator = &global_fixed_allocator.allocator;
var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]);
var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]);
var global_allocator_mem: [100 * 1024]u8 = undefined;
// TODO make thread safe
var debug_info_allocator: ?*mem.Allocator = null;
var debug_info_direct_allocator: std.heap.DirectAllocator = undefined;
var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
fn getDebugInfoAllocator() *mem.Allocator {
if (debug_info_allocator) |a| return a;
debug_info_direct_allocator = std.heap.DirectAllocator.init();
debug_info_arena_allocator = std.heap.ArenaAllocator.init(&debug_info_direct_allocator.allocator);
debug_info_allocator = &debug_info_arena_allocator.allocator;
return &debug_info_arena_allocator.allocator;
}

View File

@@ -337,7 +337,6 @@ pub const AT_PGI_lbase = 0x3a00;
pub const AT_PGI_soffset = 0x3a01;
pub const AT_PGI_lstride = 0x3a02;
pub const OP_addr = 0x03;
pub const OP_deref = 0x06;
pub const OP_const1u = 0x08;
@@ -577,7 +576,6 @@ pub const ATE_HP_unsigned_fixed = 0x8e; // Cobol.
pub const ATE_HP_VAX_complex_float = 0x8f; // F or G floating complex.
pub const ATE_HP_VAX_complex_float_d = 0x90; // D floating complex.
pub const CFA_advance_loc = 0x40;
pub const CFA_offset = 0x80;
pub const CFA_restore = 0xc0;
@@ -641,3 +639,40 @@ pub const LNE_define_file = 0x03;
pub const LNE_set_discriminator = 0x04;
pub const LNE_lo_user = 0x80;
pub const LNE_hi_user = 0xff;
pub const LANG_C89 = 0x0001;
pub const LANG_C = 0x0002;
pub const LANG_Ada83 = 0x0003;
pub const LANG_C_plus_plus = 0x0004;
pub const LANG_Cobol74 = 0x0005;
pub const LANG_Cobol85 = 0x0006;
pub const LANG_Fortran77 = 0x0007;
pub const LANG_Fortran90 = 0x0008;
pub const LANG_Pascal83 = 0x0009;
pub const LANG_Modula2 = 0x000a;
pub const LANG_Java = 0x000b;
pub const LANG_C99 = 0x000c;
pub const LANG_Ada95 = 0x000d;
pub const LANG_Fortran95 = 0x000e;
pub const LANG_PLI = 0x000f;
pub const LANG_ObjC = 0x0010;
pub const LANG_ObjC_plus_plus = 0x0011;
pub const LANG_UPC = 0x0012;
pub const LANG_D = 0x0013;
pub const LANG_Python = 0x0014;
pub const LANG_Go = 0x0016;
pub const LANG_C_plus_plus_11 = 0x001a;
pub const LANG_Rust = 0x001c;
pub const LANG_C11 = 0x001d;
pub const LANG_C_plus_plus_14 = 0x0021;
pub const LANG_Fortran03 = 0x0022;
pub const LANG_Fortran08 = 0x0023;
pub const LANG_lo_user = 0x8000;
pub const LANG_hi_user = 0xffff;
pub const LANG_Mips_Assembler = 0x8001;
pub const LANG_Upc = 0x8765;
pub const LANG_HP_Bliss = 0x8003;
pub const LANG_HP_Basic91 = 0x8004;
pub const LANG_HP_Pascal91 = 0x8005;
pub const LANG_HP_IMacro = 0x8006;
pub const LANG_HP_Assembler = 0x8007;

156
std/dynamic_library.zig Normal file
View File

@@ -0,0 +1,156 @@
const std = @import("index.zig");
const mem = std.mem;
const elf = std.elf;
const cstr = std.cstr;
const linux = std.os.linux;
pub const DynLib = struct {
allocator: *mem.Allocator,
elf_lib: ElfLib,
fd: i32,
map_addr: usize,
map_size: usize,
/// Trusts the file
pub fn open(allocator: *mem.Allocator, path: []const u8) !DynLib {
const fd = try std.os.posixOpen(allocator, path, 0, linux.O_RDONLY | linux.O_CLOEXEC);
errdefer std.os.close(fd);
const size = @intCast(usize, (try std.os.posixFStat(fd)).size);
const addr = linux.mmap(
null,
size,
linux.PROT_READ | linux.PROT_EXEC,
linux.MAP_PRIVATE | linux.MAP_LOCKED,
fd,
0,
);
errdefer _ = linux.munmap(addr, size);
const bytes = @intToPtr([*]align(std.os.page_size) u8, addr)[0..size];
return DynLib{
.allocator = allocator,
.elf_lib = try ElfLib.init(bytes),
.fd = fd,
.map_addr = addr,
.map_size = size,
};
}
pub fn close(self: *DynLib) void {
_ = linux.munmap(self.map_addr, self.map_size);
std.os.close(self.fd);
self.* = undefined;
}
pub fn lookup(self: *DynLib, name: []const u8) ?usize {
return self.elf_lib.lookup("", name);
}
};
pub const ElfLib = struct {
strings: [*]u8,
syms: [*]elf.Sym,
hashtab: [*]linux.Elf_Symndx,
versym: ?[*]u16,
verdef: ?*elf.Verdef,
base: usize,
// Trusts the memory
pub fn init(bytes: []align(@alignOf(elf.Ehdr)) u8) !ElfLib {
const eh = @ptrCast(*elf.Ehdr, bytes.ptr);
if (!mem.eql(u8, eh.e_ident[0..4], "\x7fELF")) return error.NotElfFile;
if (eh.e_type != elf.ET_DYN) return error.NotDynamicLibrary;
const elf_addr = @ptrToInt(bytes.ptr);
var ph_addr: usize = elf_addr + eh.e_phoff;
var base: usize = @maxValue(usize);
var maybe_dynv: ?[*]usize = null;
{
var i: usize = 0;
while (i < eh.e_phnum) : ({
i += 1;
ph_addr += eh.e_phentsize;
}) {
const ph = @intToPtr(*elf.Phdr, ph_addr);
switch (ph.p_type) {
elf.PT_LOAD => base = elf_addr + ph.p_offset - ph.p_vaddr,
elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, elf_addr + ph.p_offset),
else => {},
}
}
}
const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation;
if (base == @maxValue(usize)) return error.BaseNotFound;
var maybe_strings: ?[*]u8 = null;
var maybe_syms: ?[*]elf.Sym = null;
var maybe_hashtab: ?[*]linux.Elf_Symndx = null;
var maybe_versym: ?[*]u16 = null;
var maybe_verdef: ?*elf.Verdef = null;
{
var i: usize = 0;
while (dynv[i] != 0) : (i += 2) {
const p = base + dynv[i + 1];
switch (dynv[i]) {
elf.DT_STRTAB => maybe_strings = @intToPtr([*]u8, p),
elf.DT_SYMTAB => maybe_syms = @intToPtr([*]elf.Sym, p),
elf.DT_HASH => maybe_hashtab = @intToPtr([*]linux.Elf_Symndx, p),
elf.DT_VERSYM => maybe_versym = @intToPtr([*]u16, p),
elf.DT_VERDEF => maybe_verdef = @intToPtr(*elf.Verdef, p),
else => {},
}
}
}
return ElfLib{
.base = base,
.strings = maybe_strings orelse return error.ElfStringSectionNotFound,
.syms = maybe_syms orelse return error.ElfSymSectionNotFound,
.hashtab = maybe_hashtab orelse return error.ElfHashTableNotFound,
.versym = maybe_versym,
.verdef = maybe_verdef,
};
}
/// Returns the address of the symbol
pub fn lookup(self: *const ElfLib, vername: []const u8, name: []const u8) ?usize {
const maybe_versym = if (self.verdef == null) null else self.versym;
const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON);
const OK_BINDS = (1 << elf.STB_GLOBAL | 1 << elf.STB_WEAK | 1 << elf.STB_GNU_UNIQUE);
var i: usize = 0;
while (i < self.hashtab[1]) : (i += 1) {
if (0 == (u32(1) << @intCast(u5, self.syms[i].st_info & 0xf) & OK_TYPES)) continue;
if (0 == (u32(1) << @intCast(u5, self.syms[i].st_info >> 4) & OK_BINDS)) continue;
if (0 == self.syms[i].st_shndx) continue;
if (!mem.eql(u8, name, cstr.toSliceConst(self.strings + self.syms[i].st_name))) continue;
if (maybe_versym) |versym| {
if (!checkver(self.verdef.?, versym[i], vername, self.strings))
continue;
}
return self.base + self.syms[i].st_value;
}
return null;
}
};
fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*]u8) bool {
var def = def_arg;
const vsym = @bitCast(u32, vsym_arg) & 0x7fff;
while (true) {
if (0 == (def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym)
break;
if (def.vd_next == 0)
return false;
def = @intToPtr(*elf.Verdef, @ptrToInt(def) + def.vd_next);
}
const aux = @intToPtr(*elf.Verdaux, @ptrToInt(def) + def.vd_aux);
return mem.eql(u8, vername, cstr.toSliceConst(strings + aux.vda_name));
}

View File

@@ -7,6 +7,241 @@ const mem = std.mem;
const debug = std.debug;
const InStream = std.stream.InStream;
pub const AT_NULL = 0;
pub const AT_IGNORE = 1;
pub const AT_EXECFD = 2;
pub const AT_PHDR = 3;
pub const AT_PHENT = 4;
pub const AT_PHNUM = 5;
pub const AT_PAGESZ = 6;
pub const AT_BASE = 7;
pub const AT_FLAGS = 8;
pub const AT_ENTRY = 9;
pub const AT_NOTELF = 10;
pub const AT_UID = 11;
pub const AT_EUID = 12;
pub const AT_GID = 13;
pub const AT_EGID = 14;
pub const AT_CLKTCK = 17;
pub const AT_PLATFORM = 15;
pub const AT_HWCAP = 16;
pub const AT_FPUCW = 18;
pub const AT_DCACHEBSIZE = 19;
pub const AT_ICACHEBSIZE = 20;
pub const AT_UCACHEBSIZE = 21;
pub const AT_IGNOREPPC = 22;
pub const AT_SECURE = 23;
pub const AT_BASE_PLATFORM = 24;
pub const AT_RANDOM = 25;
pub const AT_HWCAP2 = 26;
pub const AT_EXECFN = 31;
pub const AT_SYSINFO = 32;
pub const AT_SYSINFO_EHDR = 33;
pub const AT_L1I_CACHESHAPE = 34;
pub const AT_L1D_CACHESHAPE = 35;
pub const AT_L2_CACHESHAPE = 36;
pub const AT_L3_CACHESHAPE = 37;
pub const AT_L1I_CACHESIZE = 40;
pub const AT_L1I_CACHEGEOMETRY = 41;
pub const AT_L1D_CACHESIZE = 42;
pub const AT_L1D_CACHEGEOMETRY = 43;
pub const AT_L2_CACHESIZE = 44;
pub const AT_L2_CACHEGEOMETRY = 45;
pub const AT_L3_CACHESIZE = 46;
pub const AT_L3_CACHEGEOMETRY = 47;
pub const DT_NULL = 0;
pub const DT_NEEDED = 1;
pub const DT_PLTRELSZ = 2;
pub const DT_PLTGOT = 3;
pub const DT_HASH = 4;
pub const DT_STRTAB = 5;
pub const DT_SYMTAB = 6;
pub const DT_RELA = 7;
pub const DT_RELASZ = 8;
pub const DT_RELAENT = 9;
pub const DT_STRSZ = 10;
pub const DT_SYMENT = 11;
pub const DT_INIT = 12;
pub const DT_FINI = 13;
pub const DT_SONAME = 14;
pub const DT_RPATH = 15;
pub const DT_SYMBOLIC = 16;
pub const DT_REL = 17;
pub const DT_RELSZ = 18;
pub const DT_RELENT = 19;
pub const DT_PLTREL = 20;
pub const DT_DEBUG = 21;
pub const DT_TEXTREL = 22;
pub const DT_JMPREL = 23;
pub const DT_BIND_NOW = 24;
pub const DT_INIT_ARRAY = 25;
pub const DT_FINI_ARRAY = 26;
pub const DT_INIT_ARRAYSZ = 27;
pub const DT_FINI_ARRAYSZ = 28;
pub const DT_RUNPATH = 29;
pub const DT_FLAGS = 30;
pub const DT_ENCODING = 32;
pub const DT_PREINIT_ARRAY = 32;
pub const DT_PREINIT_ARRAYSZ = 33;
pub const DT_SYMTAB_SHNDX = 34;
pub const DT_NUM = 35;
pub const DT_LOOS = 0x6000000d;
pub const DT_HIOS = 0x6ffff000;
pub const DT_LOPROC = 0x70000000;
pub const DT_HIPROC = 0x7fffffff;
pub const DT_PROCNUM = DT_MIPS_NUM;
pub const DT_VALRNGLO = 0x6ffffd00;
pub const DT_GNU_PRELINKED = 0x6ffffdf5;
pub const DT_GNU_CONFLICTSZ = 0x6ffffdf6;
pub const DT_GNU_LIBLISTSZ = 0x6ffffdf7;
pub const DT_CHECKSUM = 0x6ffffdf8;
pub const DT_PLTPADSZ = 0x6ffffdf9;
pub const DT_MOVEENT = 0x6ffffdfa;
pub const DT_MOVESZ = 0x6ffffdfb;
pub const DT_FEATURE_1 = 0x6ffffdfc;
pub const DT_POSFLAG_1 = 0x6ffffdfd;
pub const DT_SYMINSZ = 0x6ffffdfe;
pub const DT_SYMINENT = 0x6ffffdff;
pub const DT_VALRNGHI = 0x6ffffdff;
pub const DT_VALNUM = 12;
pub const DT_ADDRRNGLO = 0x6ffffe00;
pub const DT_GNU_HASH = 0x6ffffef5;
pub const DT_TLSDESC_PLT = 0x6ffffef6;
pub const DT_TLSDESC_GOT = 0x6ffffef7;
pub const DT_GNU_CONFLICT = 0x6ffffef8;
pub const DT_GNU_LIBLIST = 0x6ffffef9;
pub const DT_CONFIG = 0x6ffffefa;
pub const DT_DEPAUDIT = 0x6ffffefb;
pub const DT_AUDIT = 0x6ffffefc;
pub const DT_PLTPAD = 0x6ffffefd;
pub const DT_MOVETAB = 0x6ffffefe;
pub const DT_SYMINFO = 0x6ffffeff;
pub const DT_ADDRRNGHI = 0x6ffffeff;
pub const DT_ADDRNUM = 11;
pub const DT_VERSYM = 0x6ffffff0;
pub const DT_RELACOUNT = 0x6ffffff9;
pub const DT_RELCOUNT = 0x6ffffffa;
pub const DT_FLAGS_1 = 0x6ffffffb;
pub const DT_VERDEF = 0x6ffffffc;
pub const DT_VERDEFNUM = 0x6ffffffd;
pub const DT_VERNEED = 0x6ffffffe;
pub const DT_VERNEEDNUM = 0x6fffffff;
pub const DT_VERSIONTAGNUM = 16;
pub const DT_AUXILIARY = 0x7ffffffd;
pub const DT_FILTER = 0x7fffffff;
pub const DT_EXTRANUM = 3;
pub const DT_SPARC_REGISTER = 0x70000001;
pub const DT_SPARC_NUM = 2;
pub const DT_MIPS_RLD_VERSION = 0x70000001;
pub const DT_MIPS_TIME_STAMP = 0x70000002;
pub const DT_MIPS_ICHECKSUM = 0x70000003;
pub const DT_MIPS_IVERSION = 0x70000004;
pub const DT_MIPS_FLAGS = 0x70000005;
pub const DT_MIPS_BASE_ADDRESS = 0x70000006;
pub const DT_MIPS_MSYM = 0x70000007;
pub const DT_MIPS_CONFLICT = 0x70000008;
pub const DT_MIPS_LIBLIST = 0x70000009;
pub const DT_MIPS_LOCAL_GOTNO = 0x7000000a;
pub const DT_MIPS_CONFLICTNO = 0x7000000b;
pub const DT_MIPS_LIBLISTNO = 0x70000010;
pub const DT_MIPS_SYMTABNO = 0x70000011;
pub const DT_MIPS_UNREFEXTNO = 0x70000012;
pub const DT_MIPS_GOTSYM = 0x70000013;
pub const DT_MIPS_HIPAGENO = 0x70000014;
pub const DT_MIPS_RLD_MAP = 0x70000016;
pub const DT_MIPS_DELTA_CLASS = 0x70000017;
pub const DT_MIPS_DELTA_CLASS_NO = 0x70000018;
pub const DT_MIPS_DELTA_INSTANCE = 0x70000019;
pub const DT_MIPS_DELTA_INSTANCE_NO = 0x7000001a;
pub const DT_MIPS_DELTA_RELOC = 0x7000001b;
pub const DT_MIPS_DELTA_RELOC_NO = 0x7000001c;
pub const DT_MIPS_DELTA_SYM = 0x7000001d;
pub const DT_MIPS_DELTA_SYM_NO = 0x7000001e;
pub const DT_MIPS_DELTA_CLASSSYM = 0x70000020;
pub const DT_MIPS_DELTA_CLASSSYM_NO = 0x70000021;
pub const DT_MIPS_CXX_FLAGS = 0x70000022;
pub const DT_MIPS_PIXIE_INIT = 0x70000023;
pub const DT_MIPS_SYMBOL_LIB = 0x70000024;
pub const DT_MIPS_LOCALPAGE_GOTIDX = 0x70000025;
pub const DT_MIPS_LOCAL_GOTIDX = 0x70000026;
pub const DT_MIPS_HIDDEN_GOTIDX = 0x70000027;
pub const DT_MIPS_PROTECTED_GOTIDX = 0x70000028;
pub const DT_MIPS_OPTIONS = 0x70000029;
pub const DT_MIPS_INTERFACE = 0x7000002a;
pub const DT_MIPS_DYNSTR_ALIGN = 0x7000002b;
pub const DT_MIPS_INTERFACE_SIZE = 0x7000002c;
pub const DT_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002d;
pub const DT_MIPS_PERF_SUFFIX = 0x7000002e;
pub const DT_MIPS_COMPACT_SIZE = 0x7000002f;
pub const DT_MIPS_GP_VALUE = 0x70000030;
pub const DT_MIPS_AUX_DYNAMIC = 0x70000031;
pub const DT_MIPS_PLTGOT = 0x70000032;
pub const DT_MIPS_RWPLT = 0x70000034;
pub const DT_MIPS_RLD_MAP_REL = 0x70000035;
pub const DT_MIPS_NUM = 0x36;
pub const DT_ALPHA_PLTRO = (DT_LOPROC + 0);
pub const DT_ALPHA_NUM = 1;
pub const DT_PPC_GOT = (DT_LOPROC + 0);
pub const DT_PPC_OPT = (DT_LOPROC + 1);
pub const DT_PPC_NUM = 2;
pub const DT_PPC64_GLINK = (DT_LOPROC + 0);
pub const DT_PPC64_OPD = (DT_LOPROC + 1);
pub const DT_PPC64_OPDSZ = (DT_LOPROC + 2);
pub const DT_PPC64_OPT = (DT_LOPROC + 3);
pub const DT_PPC64_NUM = 4;
pub const DT_IA_64_PLT_RESERVE = (DT_LOPROC + 0);
pub const DT_IA_64_NUM = 1;
pub const DT_NIOS2_GP = 0x70000002;
pub const PT_NULL = 0;
pub const PT_LOAD = 1;
pub const PT_DYNAMIC = 2;
pub const PT_INTERP = 3;
pub const PT_NOTE = 4;
pub const PT_SHLIB = 5;
pub const PT_PHDR = 6;
pub const PT_TLS = 7;
pub const PT_NUM = 8;
pub const PT_LOOS = 0x60000000;
pub const PT_GNU_EH_FRAME = 0x6474e550;
pub const PT_GNU_STACK = 0x6474e551;
pub const PT_GNU_RELRO = 0x6474e552;
pub const PT_LOSUNW = 0x6ffffffa;
pub const PT_SUNWBSS = 0x6ffffffa;
pub const PT_SUNWSTACK = 0x6ffffffb;
pub const PT_HISUNW = 0x6fffffff;
pub const PT_HIOS = 0x6fffffff;
pub const PT_LOPROC = 0x70000000;
pub const PT_HIPROC = 0x7fffffff;
pub const SHT_NULL = 0;
pub const SHT_PROGBITS = 1;
pub const SHT_SYMTAB = 2;
@@ -31,6 +266,60 @@ pub const SHT_HIPROC = 0x7fffffff;
pub const SHT_LOUSER = 0x80000000;
pub const SHT_HIUSER = 0xffffffff;
pub const STB_LOCAL = 0;
pub const STB_GLOBAL = 1;
pub const STB_WEAK = 2;
pub const STB_NUM = 3;
pub const STB_LOOS = 10;
pub const STB_GNU_UNIQUE = 10;
pub const STB_HIOS = 12;
pub const STB_LOPROC = 13;
pub const STB_HIPROC = 15;
pub const STB_MIPS_SPLIT_COMMON = 13;
pub const STT_NOTYPE = 0;
pub const STT_OBJECT = 1;
pub const STT_FUNC = 2;
pub const STT_SECTION = 3;
pub const STT_FILE = 4;
pub const STT_COMMON = 5;
pub const STT_TLS = 6;
pub const STT_NUM = 7;
pub const STT_LOOS = 10;
pub const STT_GNU_IFUNC = 10;
pub const STT_HIOS = 12;
pub const STT_LOPROC = 13;
pub const STT_HIPROC = 15;
pub const STT_SPARC_REGISTER = 13;
pub const STT_PARISC_MILLICODE = 13;
pub const STT_HP_OPAQUE = (STT_LOOS + 0x1);
pub const STT_HP_STUB = (STT_LOOS + 0x2);
pub const STT_ARM_TFUNC = STT_LOPROC;
pub const STT_ARM_16BIT = STT_HIPROC;
pub const VER_FLG_BASE = 0x1;
pub const VER_FLG_WEAK = 0x2;
/// An unknown type.
pub const ET_NONE = 0;
/// A relocatable file.
pub const ET_REL = 1;
/// An executable file.
pub const ET_EXEC = 2;
/// A shared object.
pub const ET_DYN = 3;
/// A core file.
pub const ET_CORE = 4;
pub const FileType = enum {
Relocatable,
Executable,
@@ -64,7 +353,7 @@ pub const SectionHeader = struct {
};
pub const Elf = struct {
in_file: &os.File,
in_file: *os.File,
auto_close_stream: bool,
is_64: bool,
endian: builtin.Endian,
@@ -74,20 +363,20 @@ pub const Elf = struct {
program_header_offset: u64,
section_header_offset: u64,
string_section_index: u64,
string_section: &SectionHeader,
string_section: *SectionHeader,
section_headers: []SectionHeader,
allocator: &mem.Allocator,
allocator: *mem.Allocator,
prealloc_file: os.File,
/// Call close when done.
pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) !void {
pub fn openPath(elf: *Elf, allocator: *mem.Allocator, path: []const u8) !void {
try elf.prealloc_file.open(path);
try elf.openFile(allocator, &elf.prealloc_file);
try elf.openFile(allocator, *elf.prealloc_file);
elf.auto_close_stream = true;
}
/// Call close when done.
pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &os.File) !void {
pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: *os.File) !void {
elf.allocator = allocator;
elf.in_file = file;
elf.auto_close_stream = false;
@@ -155,9 +444,7 @@ pub const Elf = struct {
try elf.in_file.seekForward(4);
const header_size = try in.readInt(elf.endian, u16);
if ((elf.is_64 and header_size != 64) or
(!elf.is_64 and header_size != 52))
{
if ((elf.is_64 and header_size != 64) or (!elf.is_64 and header_size != 52)) {
return error.InvalidFormat;
}
@@ -188,16 +475,16 @@ pub const Elf = struct {
if (sh_entry_size != 64) return error.InvalidFormat;
for (elf.section_headers) |*elf_section| {
elf_section.name = try in.readInt(elf.endian, u32);
elf_section.sh_type = try in.readInt(elf.endian, u32);
elf_section.flags = try in.readInt(elf.endian, u64);
elf_section.addr = try in.readInt(elf.endian, u64);
elf_section.offset = try in.readInt(elf.endian, u64);
elf_section.size = try in.readInt(elf.endian, u64);
elf_section.link = try in.readInt(elf.endian, u32);
elf_section.info = try in.readInt(elf.endian, u32);
elf_section.addr_align = try in.readInt(elf.endian, u64);
elf_section.ent_size = try in.readInt(elf.endian, u64);
elf_section.name = try in.readInt(elf.endian, u32);
elf_section.sh_type = try in.readInt(elf.endian, u32);
elf_section.flags = try in.readInt(elf.endian, u64);
elf_section.addr = try in.readInt(elf.endian, u64);
elf_section.offset = try in.readInt(elf.endian, u64);
elf_section.size = try in.readInt(elf.endian, u64);
elf_section.link = try in.readInt(elf.endian, u32);
elf_section.info = try in.readInt(elf.endian, u32);
elf_section.addr_align = try in.readInt(elf.endian, u64);
elf_section.ent_size = try in.readInt(elf.endian, u64);
}
} else {
if (sh_entry_size != 40) return error.InvalidFormat;
@@ -231,14 +518,13 @@ pub const Elf = struct {
}
}
pub fn close(elf: &Elf) void {
pub fn close(elf: *Elf) void {
elf.allocator.free(elf.section_headers);
if (elf.auto_close_stream)
elf.in_file.close();
if (elf.auto_close_stream) elf.in_file.close();
}
pub fn findSection(elf: &Elf, name: []const u8) !?&SectionHeader {
pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader {
var file_stream = io.FileInStream.init(elf.in_file);
const in = &file_stream.stream;
@@ -262,7 +548,339 @@ pub const Elf = struct {
return null;
}
pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) !void {
pub fn seekToSection(elf: *Elf, elf_section: *SectionHeader) !void {
try elf.in_file.seekTo(elf_section.offset);
}
};
pub const EI_NIDENT = 16;
pub const Elf32_Half = u16;
pub const Elf64_Half = u16;
pub const Elf32_Word = u32;
pub const Elf32_Sword = i32;
pub const Elf64_Word = u32;
pub const Elf64_Sword = i32;
pub const Elf32_Xword = u64;
pub const Elf32_Sxword = i64;
pub const Elf64_Xword = u64;
pub const Elf64_Sxword = i64;
pub const Elf32_Addr = u32;
pub const Elf64_Addr = u64;
pub const Elf32_Off = u32;
pub const Elf64_Off = u64;
pub const Elf32_Section = u16;
pub const Elf64_Section = u16;
pub const Elf32_Versym = Elf32_Half;
pub const Elf64_Versym = Elf64_Half;
pub const Elf32_Ehdr = extern struct {
e_ident: [EI_NIDENT]u8,
e_type: Elf32_Half,
e_machine: Elf32_Half,
e_version: Elf32_Word,
e_entry: Elf32_Addr,
e_phoff: Elf32_Off,
e_shoff: Elf32_Off,
e_flags: Elf32_Word,
e_ehsize: Elf32_Half,
e_phentsize: Elf32_Half,
e_phnum: Elf32_Half,
e_shentsize: Elf32_Half,
e_shnum: Elf32_Half,
e_shstrndx: Elf32_Half,
};
pub const Elf64_Ehdr = extern struct {
e_ident: [EI_NIDENT]u8,
e_type: Elf64_Half,
e_machine: Elf64_Half,
e_version: Elf64_Word,
e_entry: Elf64_Addr,
e_phoff: Elf64_Off,
e_shoff: Elf64_Off,
e_flags: Elf64_Word,
e_ehsize: Elf64_Half,
e_phentsize: Elf64_Half,
e_phnum: Elf64_Half,
e_shentsize: Elf64_Half,
e_shnum: Elf64_Half,
e_shstrndx: Elf64_Half,
};
pub const Elf32_Shdr = extern struct {
sh_name: Elf32_Word,
sh_type: Elf32_Word,
sh_flags: Elf32_Word,
sh_addr: Elf32_Addr,
sh_offset: Elf32_Off,
sh_size: Elf32_Word,
sh_link: Elf32_Word,
sh_info: Elf32_Word,
sh_addralign: Elf32_Word,
sh_entsize: Elf32_Word,
};
pub const Elf64_Shdr = extern struct {
sh_name: Elf64_Word,
sh_type: Elf64_Word,
sh_flags: Elf64_Xword,
sh_addr: Elf64_Addr,
sh_offset: Elf64_Off,
sh_size: Elf64_Xword,
sh_link: Elf64_Word,
sh_info: Elf64_Word,
sh_addralign: Elf64_Xword,
sh_entsize: Elf64_Xword,
};
pub const Elf32_Chdr = extern struct {
ch_type: Elf32_Word,
ch_size: Elf32_Word,
ch_addralign: Elf32_Word,
};
pub const Elf64_Chdr = extern struct {
ch_type: Elf64_Word,
ch_reserved: Elf64_Word,
ch_size: Elf64_Xword,
ch_addralign: Elf64_Xword,
};
pub const Elf32_Sym = extern struct {
st_name: Elf32_Word,
st_value: Elf32_Addr,
st_size: Elf32_Word,
st_info: u8,
st_other: u8,
st_shndx: Elf32_Section,
};
pub const Elf64_Sym = extern struct {
st_name: Elf64_Word,
st_info: u8,
st_other: u8,
st_shndx: Elf64_Section,
st_value: Elf64_Addr,
st_size: Elf64_Xword,
};
pub const Elf32_Syminfo = extern struct {
si_boundto: Elf32_Half,
si_flags: Elf32_Half,
};
pub const Elf64_Syminfo = extern struct {
si_boundto: Elf64_Half,
si_flags: Elf64_Half,
};
pub const Elf32_Rel = extern struct {
r_offset: Elf32_Addr,
r_info: Elf32_Word,
};
pub const Elf64_Rel = extern struct {
r_offset: Elf64_Addr,
r_info: Elf64_Xword,
};
pub const Elf32_Rela = extern struct {
r_offset: Elf32_Addr,
r_info: Elf32_Word,
r_addend: Elf32_Sword,
};
pub const Elf64_Rela = extern struct {
r_offset: Elf64_Addr,
r_info: Elf64_Xword,
r_addend: Elf64_Sxword,
};
pub const Elf32_Phdr = extern struct {
p_type: Elf32_Word,
p_offset: Elf32_Off,
p_vaddr: Elf32_Addr,
p_paddr: Elf32_Addr,
p_filesz: Elf32_Word,
p_memsz: Elf32_Word,
p_flags: Elf32_Word,
p_align: Elf32_Word,
};
pub const Elf64_Phdr = extern struct {
p_type: Elf64_Word,
p_flags: Elf64_Word,
p_offset: Elf64_Off,
p_vaddr: Elf64_Addr,
p_paddr: Elf64_Addr,
p_filesz: Elf64_Xword,
p_memsz: Elf64_Xword,
p_align: Elf64_Xword,
};
pub const Elf32_Dyn = extern struct {
d_tag: Elf32_Sword,
d_un: extern union {
d_val: Elf32_Word,
d_ptr: Elf32_Addr,
},
};
pub const Elf64_Dyn = extern struct {
d_tag: Elf64_Sxword,
d_un: extern union {
d_val: Elf64_Xword,
d_ptr: Elf64_Addr,
},
};
pub const Elf32_Verdef = extern struct {
vd_version: Elf32_Half,
vd_flags: Elf32_Half,
vd_ndx: Elf32_Half,
vd_cnt: Elf32_Half,
vd_hash: Elf32_Word,
vd_aux: Elf32_Word,
vd_next: Elf32_Word,
};
pub const Elf64_Verdef = extern struct {
vd_version: Elf64_Half,
vd_flags: Elf64_Half,
vd_ndx: Elf64_Half,
vd_cnt: Elf64_Half,
vd_hash: Elf64_Word,
vd_aux: Elf64_Word,
vd_next: Elf64_Word,
};
pub const Elf32_Verdaux = extern struct {
vda_name: Elf32_Word,
vda_next: Elf32_Word,
};
pub const Elf64_Verdaux = extern struct {
vda_name: Elf64_Word,
vda_next: Elf64_Word,
};
pub const Elf32_Verneed = extern struct {
vn_version: Elf32_Half,
vn_cnt: Elf32_Half,
vn_file: Elf32_Word,
vn_aux: Elf32_Word,
vn_next: Elf32_Word,
};
pub const Elf64_Verneed = extern struct {
vn_version: Elf64_Half,
vn_cnt: Elf64_Half,
vn_file: Elf64_Word,
vn_aux: Elf64_Word,
vn_next: Elf64_Word,
};
pub const Elf32_Vernaux = extern struct {
vna_hash: Elf32_Word,
vna_flags: Elf32_Half,
vna_other: Elf32_Half,
vna_name: Elf32_Word,
vna_next: Elf32_Word,
};
pub const Elf64_Vernaux = extern struct {
vna_hash: Elf64_Word,
vna_flags: Elf64_Half,
vna_other: Elf64_Half,
vna_name: Elf64_Word,
vna_next: Elf64_Word,
};
pub const Elf32_auxv_t = extern struct {
a_type: u32,
a_un: extern union {
a_val: u32,
},
};
pub const Elf64_auxv_t = extern struct {
a_type: u64,
a_un: extern union {
a_val: u64,
},
};
pub const Elf32_Nhdr = extern struct {
n_namesz: Elf32_Word,
n_descsz: Elf32_Word,
n_type: Elf32_Word,
};
pub const Elf64_Nhdr = extern struct {
n_namesz: Elf64_Word,
n_descsz: Elf64_Word,
n_type: Elf64_Word,
};
pub const Elf32_Move = extern struct {
m_value: Elf32_Xword,
m_info: Elf32_Word,
m_poffset: Elf32_Word,
m_repeat: Elf32_Half,
m_stride: Elf32_Half,
};
pub const Elf64_Move = extern struct {
m_value: Elf64_Xword,
m_info: Elf64_Xword,
m_poffset: Elf64_Xword,
m_repeat: Elf64_Half,
m_stride: Elf64_Half,
};
pub const Elf32_gptab = extern union {
gt_header: extern struct {
gt_current_g_value: Elf32_Word,
gt_unused: Elf32_Word,
},
gt_entry: extern struct {
gt_g_value: Elf32_Word,
gt_bytes: Elf32_Word,
},
};
pub const Elf32_RegInfo = extern struct {
ri_gprmask: Elf32_Word,
ri_cprmask: [4]Elf32_Word,
ri_gp_value: Elf32_Sword,
};
pub const Elf_Options = extern struct {
kind: u8,
size: u8,
@"section": Elf32_Section,
info: Elf32_Word,
};
pub const Elf_Options_Hw = extern struct {
hwp_flags1: Elf32_Word,
hwp_flags2: Elf32_Word,
};
pub const Elf32_Lib = extern struct {
l_name: Elf32_Word,
l_time_stamp: Elf32_Word,
l_checksum: Elf32_Word,
l_version: Elf32_Word,
l_flags: Elf32_Word,
};
pub const Elf64_Lib = extern struct {
l_name: Elf64_Word,
l_time_stamp: Elf64_Word,
l_checksum: Elf64_Word,
l_version: Elf64_Word,
l_flags: Elf64_Word,
};
pub const Elf32_Conflict = Elf32_Addr;
pub const Elf_MIPS_ABIFlags_v0 = extern struct {
version: Elf32_Half,
isa_level: u8,
isa_rev: u8,
gpr_size: u8,
cpr1_size: u8,
cpr2_size: u8,
fp_abi: u8,
isa_ext: Elf32_Word,
ases: Elf32_Word,
flags1: Elf32_Word,
flags2: Elf32_Word,
};
pub const Ehdr = switch (@sizeOf(usize)) {
4 => Elf32_Ehdr,
8 => Elf64_Ehdr,
else => @compileError("expected pointer size of 32 or 64"),
};
pub const Phdr = switch (@sizeOf(usize)) {
4 => Elf32_Phdr,
8 => Elf64_Phdr,
else => @compileError("expected pointer size of 32 or 64"),
};
pub const Sym = switch (@sizeOf(usize)) {
4 => Elf32_Sym,
8 => Elf64_Sym,
else => @compileError("expected pointer size of 32 or 64"),
};
pub const Verdef = switch (@sizeOf(usize)) {
4 => Elf32_Verdef,
8 => Elf64_Verdef,
else => @compileError("expected pointer size of 32 or 64"),
};
pub const Verdaux = switch (@sizeOf(usize)) {
4 => Elf32_Verdaux,
8 => Elf64_Verdaux,
else => @compileError("expected pointer size of 32 or 64"),
};

View File

@@ -1,235 +1,17 @@
const std = @import("index.zig");
const builtin = @import("builtin");
const assert = std.debug.assert;
const event = this;
const mem = std.mem;
const posix = std.os.posix;
pub const Locked = @import("event/locked.zig").Locked;
pub const Loop = @import("event/loop.zig").Loop;
pub const Lock = @import("event/lock.zig").Lock;
pub const tcp = @import("event/tcp.zig");
pub const Channel = @import("event/channel.zig").Channel;
pub const Group = @import("event/group.zig").Group;
pub const Future = @import("event/future.zig").Future;
pub const TcpServer = struct {
handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void,
loop: &Loop,
sockfd: i32,
accept_coro: ?promise,
listen_address: std.net.Address,
waiting_for_emfile_node: PromiseNode,
const PromiseNode = std.LinkedList(promise).Node;
pub fn init(loop: &Loop) !TcpServer {
const sockfd = try std.os.posixSocket(posix.AF_INET,
posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK,
posix.PROTO_tcp);
errdefer std.os.close(sockfd);
// TODO can't initialize handler coroutine here because we need well defined copy elision
return TcpServer {
.loop = loop,
.sockfd = sockfd,
.accept_coro = null,
.handleRequestFn = undefined,
.waiting_for_emfile_node = undefined,
.listen_address = undefined,
};
}
pub fn listen(self: &TcpServer, address: &const std.net.Address,
handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File)void) !void
{
self.handleRequestFn = handleRequestFn;
try std.os.posixBind(self.sockfd, &address.os_addr);
try std.os.posixListen(self.sockfd, posix.SOMAXCONN);
self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd));
self.accept_coro = try async<self.loop.allocator> TcpServer.handler(self);
errdefer cancel ??self.accept_coro;
try self.loop.addFd(self.sockfd, ??self.accept_coro);
errdefer self.loop.removeFd(self.sockfd);
}
pub fn deinit(self: &TcpServer) void {
self.loop.removeFd(self.sockfd);
if (self.accept_coro) |accept_coro| cancel accept_coro;
std.os.close(self.sockfd);
}
pub async fn handler(self: &TcpServer) void {
while (true) {
var accepted_addr: std.net.Address = undefined;
if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr,
posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd|
{
var socket = std.os.File.openHandle(accepted_fd);
_ = async<self.loop.allocator> self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) {
error.OutOfMemory => {
socket.close();
continue;
},
};
} else |err| switch (err) {
error.WouldBlock => {
suspend; // we will get resumed by epoll_wait in the event loop
continue;
},
error.ProcessFdQuotaExceeded => {
errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
suspend |p| {
self.waiting_for_emfile_node = PromiseNode.init(p);
std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
}
continue;
},
error.ConnectionAborted,
error.FileDescriptorClosed => continue,
error.PageFault => unreachable,
error.InvalidSyscall => unreachable,
error.FileDescriptorNotASocket => unreachable,
error.OperationNotSupported => unreachable,
error.SystemFdQuotaExceeded,
error.SystemResources,
error.ProtocolFailure,
error.BlockedByFirewall,
error.Unexpected => {
@panic("TODO handle this error");
},
}
}
}
};
pub const Loop = struct {
allocator: &mem.Allocator,
epollfd: i32,
keep_running: bool,
fn init(allocator: &mem.Allocator) !Loop {
const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC);
return Loop {
.keep_running = true,
.allocator = allocator,
.epollfd = epollfd,
};
}
pub fn addFd(self: &Loop, fd: i32, prom: promise) !void {
var ev = std.os.linux.epoll_event {
.events = std.os.linux.EPOLLIN|std.os.linux.EPOLLOUT|std.os.linux.EPOLLET,
.data = std.os.linux.epoll_data {
.ptr = @ptrToInt(prom),
},
};
try std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev);
}
pub fn removeFd(self: &Loop, fd: i32) void {
std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
}
async fn waitFd(self: &Loop, fd: i32) !void {
defer self.removeFd(fd);
suspend |p| {
try self.addFd(fd, p);
}
}
pub fn stop(self: &Loop) void {
// TODO make atomic
self.keep_running = false;
// TODO activate an fd in the epoll set
}
pub fn run(self: &Loop) void {
while (self.keep_running) {
var events: [16]std.os.linux.epoll_event = undefined;
const count = std.os.linuxEpollWait(self.epollfd, events[0..], -1);
for (events[0..count]) |ev| {
const p = @intToPtr(promise, ev.data.ptr);
resume p;
}
}
}
};
pub async fn connect(loop: &Loop, _address: &const std.net.Address) !std.os.File {
var address = *_address; // TODO https://github.com/zig-lang/zig/issues/733
const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK, posix.PROTO_tcp);
errdefer std.os.close(sockfd);
try std.os.posixConnectAsync(sockfd, &address.os_addr);
try await try async loop.waitFd(sockfd);
try std.os.posixGetSockOptConnectError(sockfd);
return std.os.File.openHandle(sockfd);
}
test "listen on a port, send bytes, receive bytes" {
if (builtin.os != builtin.Os.linux) {
// TODO build abstractions for other operating systems
return;
}
const MyServer = struct {
tcp_server: TcpServer,
const Self = this;
async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address,
_socket: &const std.os.File) void
{
const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
defer socket.close();
const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) {
error.OutOfMemory => @panic("unable to handle connection: out of memory"),
};
(await next_handler) catch |err| {
std.debug.panic("unable to handle connection: {}\n", err);
};
suspend |p| { cancel p; }
}
async fn errorableHandler(self: &Self, _addr: &const std.net.Address,
_socket: &const std.os.File) !void
{
const addr = *_addr; // TODO https://github.com/zig-lang/zig/issues/733
var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
var adapter = std.io.FileOutStream.init(&socket);
var stream = &adapter.stream;
try stream.print("hello from server\n");
}
};
const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable;
const addr = std.net.Address.initIp4(ip4addr, 0);
var loop = try Loop.init(std.debug.global_allocator);
var server = MyServer {
.tcp_server = try TcpServer.init(&loop),
};
defer server.tcp_server.deinit();
try server.tcp_server.listen(addr, MyServer.handler);
const p = try async<std.debug.global_allocator> doAsyncTest(&loop, server.tcp_server.listen_address);
defer cancel p;
loop.run();
}
async fn doAsyncTest(loop: &Loop, address: &const std.net.Address) void {
errdefer @panic("test failure");
var socket_file = try await try async event.connect(loop, address);
defer socket_file.close();
var buf: [512]u8 = undefined;
const amt_read = try socket_file.read(buf[0..]);
const msg = buf[0..amt_read];
assert(mem.eql(u8, msg, "hello from server\n"));
loop.stop();
test "import event tests" {
_ = @import("event/locked.zig");
_ = @import("event/loop.zig");
_ = @import("event/lock.zig");
_ = @import("event/tcp.zig");
_ = @import("event/channel.zig");
_ = @import("event/group.zig");
_ = @import("event/future.zig");
}

235
std/event/channel.zig Normal file
View File

@@ -0,0 +1,235 @@
const std = @import("../index.zig");
const builtin = @import("builtin");
const assert = std.debug.assert;
const AtomicRmwOp = builtin.AtomicRmwOp;
const AtomicOrder = builtin.AtomicOrder;
const Loop = std.event.Loop;
/// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size
/// when buffer is empty, consumers suspend and are resumed by producers
/// when buffer is full, producers suspend and are resumed by consumers
pub fn Channel(comptime T: type) type {
return struct {
loop: *Loop,
getters: std.atomic.Queue(GetNode),
putters: std.atomic.Queue(PutNode),
get_count: usize,
put_count: usize,
dispatch_lock: u8, // TODO make this a bool
need_dispatch: u8, // TODO make this a bool
// simple fixed size ring buffer
buffer_nodes: []T,
buffer_index: usize,
buffer_len: usize,
const SelfChannel = this;
const GetNode = struct {
ptr: *T,
tick_node: *Loop.NextTickNode,
};
const PutNode = struct {
data: T,
tick_node: *Loop.NextTickNode,
};
/// call destroy when done
pub fn create(loop: *Loop, capacity: usize) !*SelfChannel {
const buffer_nodes = try loop.allocator.alloc(T, capacity);
errdefer loop.allocator.free(buffer_nodes);
const self = try loop.allocator.create(SelfChannel{
.loop = loop,
.buffer_len = 0,
.buffer_nodes = buffer_nodes,
.buffer_index = 0,
.dispatch_lock = 0,
.need_dispatch = 0,
.getters = std.atomic.Queue(GetNode).init(),
.putters = std.atomic.Queue(PutNode).init(),
.get_count = 0,
.put_count = 0,
});
errdefer loop.allocator.destroy(self);
return self;
}
/// must be called when all calls to put and get have suspended and no more calls occur
pub fn destroy(self: *SelfChannel) void {
while (self.getters.get()) |get_node| {
cancel get_node.data.tick_node.data;
}
while (self.putters.get()) |put_node| {
cancel put_node.data.tick_node.data;
}
self.loop.allocator.free(self.buffer_nodes);
self.loop.allocator.destroy(self);
}
/// puts a data item in the channel. The promise completes when the value has been added to the
/// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
pub async fn put(self: *SelfChannel, data: T) void {
suspend {
var my_tick_node = Loop.NextTickNode{
.next = undefined,
.data = @handle(),
};
var queue_node = std.atomic.Queue(PutNode).Node{
.data = PutNode{
.tick_node = &my_tick_node,
.data = data,
},
.next = undefined,
};
self.putters.put(&queue_node);
_ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
self.dispatch();
}
}
/// await this function to get an item from the channel. If the buffer is empty, the promise will
/// complete when the next item is put in the channel.
pub async fn get(self: *SelfChannel) T {
// TODO integrate this function with named return values
// so we can get rid of this extra result copy
var result: T = undefined;
suspend {
var my_tick_node = Loop.NextTickNode{
.next = undefined,
.data = @handle(),
};
var queue_node = std.atomic.Queue(GetNode).Node{
.data = GetNode{
.ptr = &result,
.tick_node = &my_tick_node,
},
.next = undefined,
};
self.getters.put(&queue_node);
_ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
self.dispatch();
}
return result;
}
fn dispatch(self: *SelfChannel) void {
// set the "need dispatch" flag
_ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
lock: while (true) {
// set the lock flag
const prev_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
if (prev_lock != 0) return;
// clear the need_dispatch flag since we're about to do it
_ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
while (true) {
one_dispatch: {
// later we correct these extra subtractions
var get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
var put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
// transfer self.buffer to self.getters
while (self.buffer_len != 0) {
if (get_count == 0) break :one_dispatch;
const get_node = &self.getters.get().?.data;
get_node.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
self.loop.onNextTick(get_node.tick_node);
self.buffer_len -= 1;
get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
}
// direct transfer self.putters to self.getters
while (get_count != 0 and put_count != 0) {
const get_node = &self.getters.get().?.data;
const put_node = &self.putters.get().?.data;
get_node.ptr.* = put_node.data;
self.loop.onNextTick(get_node.tick_node);
self.loop.onNextTick(put_node.tick_node);
get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
}
// transfer self.putters to self.buffer
while (self.buffer_len != self.buffer_nodes.len and put_count != 0) {
const put_node = &self.putters.get().?.data;
self.buffer_nodes[self.buffer_index] = put_node.data;
self.loop.onNextTick(put_node.tick_node);
self.buffer_index +%= 1;
self.buffer_len += 1;
put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
}
}
// undo the extra subtractions
_ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
_ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
// clear need-dispatch flag
const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
if (need_dispatch != 0) continue;
const my_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
assert(my_lock != 0);
// we have to check again now that we unlocked
if (@atomicLoad(u8, &self.need_dispatch, AtomicOrder.SeqCst) != 0) continue :lock;
return;
}
}
}
};
}
test "std.event.Channel" {
var da = std.heap.DirectAllocator.init();
defer da.deinit();
const allocator = &da.allocator;
var loop: Loop = undefined;
// TODO make a multi threaded test
try loop.initSingleThreaded(allocator);
defer loop.deinit();
const channel = try Channel(i32).create(&loop, 0);
defer channel.destroy();
const handle = try async<allocator> testChannelGetter(&loop, channel);
defer cancel handle;
const putter = try async<allocator> testChannelPutter(channel);
defer cancel putter;
loop.run();
}
async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {
errdefer @panic("test failed");
const value1_promise = try async channel.get();
const value1 = await value1_promise;
assert(value1 == 1234);
const value2_promise = try async channel.get();
const value2 = await value2_promise;
assert(value2 == 4567);
}
async fn testChannelPutter(channel: *Channel(i32)) void {
await (async channel.put(1234) catch @panic("out of memory"));
await (async channel.put(4567) catch @panic("out of memory"));
}

130
std/event/future.zig Normal file
View File

@@ -0,0 +1,130 @@
const std = @import("../index.zig");
const assert = std.debug.assert;
const builtin = @import("builtin");
const AtomicRmwOp = builtin.AtomicRmwOp;
const AtomicOrder = builtin.AtomicOrder;
const Lock = std.event.Lock;
const Loop = std.event.Loop;
/// This is a value that starts out unavailable, until resolve() is called
/// While it is unavailable, coroutines suspend when they try to get() it,
/// and then are resumed when resolve() is called.
/// At this point the value remains forever available, and another resolve() is not allowed.
pub fn Future(comptime T: type) type {
return struct {
lock: Lock,
data: T,
/// TODO make this an enum
/// 0 - not started
/// 1 - started
/// 2 - finished
available: u8,
const Self = this;
const Queue = std.atomic.Queue(promise);
pub fn init(loop: *Loop) Self {
return Self{
.lock = Lock.initLocked(loop),
.available = 0,
.data = undefined,
};
}
/// Obtain the value. If it's not available, wait until it becomes
/// available.
/// Thread-safe.
pub async fn get(self: *Self) *T {
if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {
return &self.data;
}
const held = await (async self.lock.acquire() catch unreachable);
held.release();
return &self.data;
}
/// Gets the data without waiting for it. If it's available, a pointer is
/// returned. Otherwise, null is returned.
pub fn getOrNull(self: *Self) ?*T {
if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) {
return &self.data;
} else {
return null;
}
}
/// If someone else has started working on the data, wait for them to complete
/// and return a pointer to the data. Otherwise, return null, and the caller
/// should start working on the data.
/// It's not required to call start() before resolve() but it can be useful since
/// this method is thread-safe.
pub async fn start(self: *Self) ?*T {
const state = @cmpxchgStrong(u8, &self.available, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null;
switch (state) {
1 => {
const held = await (async self.lock.acquire() catch unreachable);
held.release();
return &self.data;
},
2 => return &self.data,
else => unreachable,
}
}
/// Make the data become available. May be called only once.
/// Before calling this, modify the `data` property.
pub fn resolve(self: *Self) void {
const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst);
assert(prev == 0 or prev == 1); // resolve() called twice
Lock.Held.release(Lock.Held{ .lock = &self.lock });
}
};
}
test "std.event.Future" {
var da = std.heap.DirectAllocator.init();
defer da.deinit();
const allocator = &da.allocator;
var loop: Loop = undefined;
try loop.initMultiThreaded(allocator);
defer loop.deinit();
const handle = try async<allocator> testFuture(&loop);
defer cancel handle;
loop.run();
}
async fn testFuture(loop: *Loop) void {
suspend {
resume @handle();
}
var future = Future(i32).init(loop);
const a = async waitOnFuture(&future) catch @panic("memory");
const b = async waitOnFuture(&future) catch @panic("memory");
const c = async resolveFuture(&future) catch @panic("memory");
const result = (await a) + (await b);
cancel c;
assert(result == 12);
}
async fn waitOnFuture(future: *Future(i32)) i32 {
suspend {
resume @handle();
}
return (await (async future.get() catch @panic("memory"))).*;
}
async fn resolveFuture(future: *Future(i32)) void {
suspend {
resume @handle();
}
future.data = 6;
future.resolve();
}

170
std/event/group.zig Normal file
View File

@@ -0,0 +1,170 @@
const std = @import("../index.zig");
const builtin = @import("builtin");
const Lock = std.event.Lock;
const Loop = std.event.Loop;
const AtomicRmwOp = builtin.AtomicRmwOp;
const AtomicOrder = builtin.AtomicOrder;
const assert = std.debug.assert;
/// ReturnType must be `void` or `E!void`
pub fn Group(comptime ReturnType: type) type {
return struct {
coro_stack: Stack,
alloc_stack: Stack,
lock: Lock,
const Self = this;
const Error = switch (@typeInfo(ReturnType)) {
builtin.TypeId.ErrorUnion => |payload| payload.error_set,
else => void,
};
const Stack = std.atomic.Stack(promise->ReturnType);
pub fn init(loop: *Loop) Self {
return Self{
.coro_stack = Stack.init(),
.alloc_stack = Stack.init(),
.lock = Lock.init(loop),
};
}
/// Add a promise to the group. Thread-safe.
pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) {
const node = try self.lock.loop.allocator.create(Stack.Node{
.next = undefined,
.data = handle,
});
self.alloc_stack.push(node);
}
/// Add a node to the group. Thread-safe. Cannot fail.
/// `node.data` should be the promise handle to add to the group.
/// The node's memory should be in the coroutine frame of
/// the handle that is in the node, or somewhere guaranteed to live
/// at least as long.
pub fn addNode(self: *Self, node: *Stack.Node) void {
self.coro_stack.push(node);
}
/// This is equivalent to an async call, but the async function is added to the group, instead
/// of returning a promise. func must be async and have return type ReturnType.
/// Thread-safe.
pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) {
const S = struct {
async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType {
// TODO this is a hack to make the memory following be inside the coro frame
suspend {
var my_node: Stack.Node = undefined;
node.* = &my_node;
resume @handle();
}
// TODO this allocation elision should be guaranteed because we await it in
// this coro frame
return await (async func(args2) catch unreachable);
}
};
var node: *Stack.Node = undefined;
const handle = try async<self.lock.loop.allocator> S.asyncFunc(&node, args);
node.* = Stack.Node{
.next = undefined,
.data = handle,
};
self.coro_stack.push(node);
}
/// Wait for all the calls and promises of the group to complete.
/// Thread-safe.
/// Safe to call any number of times.
pub async fn wait(self: *Self) ReturnType {
// TODO catch unreachable because the allocation can be grouped with
// the coro frame allocation
const held = await (async self.lock.acquire() catch unreachable);
defer held.release();
while (self.coro_stack.pop()) |node| {
if (Error == void) {
await node.data;
} else {
(await node.data) catch |err| {
self.cancelAll();
return err;
};
}
}
while (self.alloc_stack.pop()) |node| {
const handle = node.data;
self.lock.loop.allocator.destroy(node);
if (Error == void) {
await handle;
} else {
(await handle) catch |err| {
self.cancelAll();
return err;
};
}
}
}
/// Cancel all the outstanding promises. May only be called if wait was never called.
/// TODO These should be `cancelasync` not `cancel`.
/// See https://github.com/ziglang/zig/issues/1261
pub fn cancelAll(self: *Self) void {
while (self.coro_stack.pop()) |node| {
cancel node.data;
}
while (self.alloc_stack.pop()) |node| {
cancel node.data;
self.lock.loop.allocator.destroy(node);
}
}
};
}
test "std.event.Group" {
var da = std.heap.DirectAllocator.init();
defer da.deinit();
const allocator = &da.allocator;
var loop: Loop = undefined;
try loop.initMultiThreaded(allocator);
defer loop.deinit();
const handle = try async<allocator> testGroup(&loop);
defer cancel handle;
loop.run();
}
async fn testGroup(loop: *Loop) void {
var count: usize = 0;
var group = Group(void).init(loop);
group.add(async sleepALittle(&count) catch @panic("memory")) catch @panic("memory");
group.call(increaseByTen, &count) catch @panic("memory");
await (async group.wait() catch @panic("memory"));
assert(count == 11);
var another = Group(error!void).init(loop);
another.add(async somethingElse() catch @panic("memory")) catch @panic("memory");
another.call(doSomethingThatFails) catch @panic("memory");
std.debug.assertError(await (async another.wait() catch @panic("memory")), error.ItBroke);
}
async fn sleepALittle(count: *usize) void {
std.os.time.sleep(0, 1000000);
_ = @atomicRmw(usize, count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
}
async fn increaseByTen(count: *usize) void {
var i: usize = 0;
while (i < 10) : (i += 1) {
_ = @atomicRmw(usize, count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
}
}
async fn doSomethingThatFails() error!void {}
async fn somethingElse() error!void {
return error.ItBroke;
}

190
std/event/lock.zig Normal file
View File

@@ -0,0 +1,190 @@
const std = @import("../index.zig");
const builtin = @import("builtin");
const assert = std.debug.assert;
const mem = std.mem;
const AtomicRmwOp = builtin.AtomicRmwOp;
const AtomicOrder = builtin.AtomicOrder;
const Loop = std.event.Loop;
/// Thread-safe async/await lock.
/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
/// are resumed when the lock is released, in order.
pub const Lock = struct {
loop: *Loop,
shared_bit: u8, // TODO make this a bool
queue: Queue,
queue_empty_bit: u8, // TODO make this a bool
const Queue = std.atomic.Queue(promise);
pub const Held = struct {
lock: *Lock,
pub fn release(self: Held) void {
// Resume the next item from the queue.
if (self.lock.queue.get()) |node| {
self.lock.loop.onNextTick(node);
return;
}
// We need to release the lock.
_ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
_ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
// There might be a queue item. If we know the queue is empty, we can be done,
// because the other actor will try to obtain the lock.
// But if there's a queue item, we are the actor which must loop and attempt
// to grab the lock again.
if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) {
return;
}
while (true) {
const old_bit = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
if (old_bit != 0) {
// We did not obtain the lock. Great, the queue is someone else's problem.
return;
}
// Resume the next item from the queue.
if (self.lock.queue.get()) |node| {
self.lock.loop.onNextTick(node);
return;
}
// Release the lock again.
_ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
_ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
// Find out if we can be done.
if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) {
return;
}
}
}
};
pub fn init(loop: *Loop) Lock {
return Lock{
.loop = loop,
.shared_bit = 0,
.queue = Queue.init(),
.queue_empty_bit = 1,
};
}
pub fn initLocked(loop: *Loop) Lock {
return Lock{
.loop = loop,
.shared_bit = 1,
.queue = Queue.init(),
.queue_empty_bit = 1,
};
}
/// Must be called when not locked. Not thread safe.
/// All calls to acquire() and release() must complete before calling deinit().
pub fn deinit(self: *Lock) void {
assert(self.shared_bit == 0);
while (self.queue.get()) |node| cancel node.data;
}
pub async fn acquire(self: *Lock) Held {
suspend {
// TODO explicitly put this memory in the coroutine frame #1194
var my_tick_node = Loop.NextTickNode{
.data = @handle(),
.next = undefined,
};
self.queue.put(&my_tick_node);
// At this point, we are in the queue, so we might have already been resumed and this coroutine
// frame might be destroyed. For the rest of the suspend block we cannot access the coroutine frame.
// We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor
// will attempt to grab the lock.
_ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
if (old_bit == 0) {
if (self.queue.get()) |node| {
// Whether this node is us or someone else, we tail resume it.
resume node.data;
}
}
}
return Held{ .lock = self };
}
};
test "std.event.Lock" {
var da = std.heap.DirectAllocator.init();
defer da.deinit();
const allocator = &da.allocator;
var loop: Loop = undefined;
try loop.initMultiThreaded(allocator);
defer loop.deinit();
var lock = Lock.init(&loop);
defer lock.deinit();
const handle = try async<allocator> testLock(&loop, &lock);
defer cancel handle;
loop.run();
assert(mem.eql(i32, shared_test_data, [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len));
}
async fn testLock(loop: *Loop, lock: *Lock) void {
// TODO explicitly put next tick node memory in the coroutine frame #1194
suspend {
resume @handle();
}
const handle1 = async lockRunner(lock) catch @panic("out of memory");
var tick_node1 = Loop.NextTickNode{
.next = undefined,
.data = handle1,
};
loop.onNextTick(&tick_node1);
const handle2 = async lockRunner(lock) catch @panic("out of memory");
var tick_node2 = Loop.NextTickNode{
.next = undefined,
.data = handle2,
};
loop.onNextTick(&tick_node2);
const handle3 = async lockRunner(lock) catch @panic("out of memory");
var tick_node3 = Loop.NextTickNode{
.next = undefined,
.data = handle3,
};
loop.onNextTick(&tick_node3);
await handle1;
await handle2;
await handle3;
}
var shared_test_data = [1]i32{0} ** 10;
var shared_test_index: usize = 0;
async fn lockRunner(lock: *Lock) void {
suspend; // resumed by onNextTick
var i: usize = 0;
while (i < shared_test_data.len) : (i += 1) {
const lock_promise = async lock.acquire() catch @panic("out of memory");
const handle = await lock_promise;
defer handle.release();
shared_test_index = 0;
while (shared_test_index < shared_test_data.len) : (shared_test_index += 1) {
shared_test_data[shared_test_index] = shared_test_data[shared_test_index] + 1;
}
}
}

43
std/event/locked.zig Normal file
View File

@@ -0,0 +1,43 @@
const std = @import("../index.zig");
const Lock = std.event.Lock;
const Loop = std.event.Loop;
/// Thread-safe async/await lock that protects one piece of data.
/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
/// are resumed when the lock is released, in order.
pub fn Locked(comptime T: type) type {
return struct {
lock: Lock,
private_data: T,
const Self = this;
pub const HeldLock = struct {
value: *T,
held: Lock.Held,
pub fn release(self: HeldLock) void {
self.held.release();
}
};
pub fn init(loop: *Loop, data: T) Self {
return Self{
.lock = Lock.init(loop),
.private_data = data,
};
}
pub fn deinit(self: *Self) void {
self.lock.deinit();
}
pub async fn acquire(self: *Self) HeldLock {
return HeldLock{
// TODO guaranteed allocation elision
.held = await (async self.lock.acquire() catch unreachable),
.value = &self.private_data,
};
}
};
}

Some files were not shown because too many files have changed in this diff Show More