Merge branch 'master' into zen_stdlib
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1 +1,2 @@
|
||||
*.zig text eol=lf
|
||||
langref.html.in text eol=lf
|
||||
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -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/
|
||||
|
||||
22
.travis.yml
22
.travis.yml
@@ -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=
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
83
README.md
83
README.md
@@ -1,9 +1,9 @@
|
||||

|
||||

|
||||
|
||||
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
|
||||
|
||||
[](https://travis-ci.org/zig-lang/zig)
|
||||
[](https://travis-ci.org/ziglang/zig)
|
||||
[](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
241
build.zig
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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'
|
||||
|
||||
3
deps/lld/COFF/Driver.cpp
vendored
3
deps/lld/COFF/Driver.cpp
vendored
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
275
doc/docgen.zig
275
doc/docgen.zig
@@ -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;
|
||||
}
|
||||
|
||||
2817
doc/langref.html.in
2817
doc/langref.html.in
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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
68
src-self-hosted/c_int.zig
Normal 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
450
src-self-hosted/codegen.zig
Normal 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,
|
||||
};
|
||||
}
|
||||
1303
src-self-hosted/compilation.zig
Normal file
1303
src-self-hosted/compilation.zig
Normal file
File diff suppressed because it is too large
Load Diff
98
src-self-hosted/decl.zig
Normal file
98
src-self-hosted/decl.zig
Normal 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
237
src-self-hosted/errmsg.zig
Normal 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);
|
||||
}
|
||||
};
|
||||
@@ -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
462
src-self-hosted/libc_installation.zig
Normal file
462
src-self-hosted/libc_installation.zig
Normal 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
737
src-self-hosted/link.zig
Normal 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;
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
29
src-self-hosted/package.zig
Normal file
29
src-self-hosted/package.zig
Normal 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);
|
||||
}
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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
243
src-self-hosted/test.zig
Normal 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
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
581
src-self-hosted/value.zig
Normal 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);
|
||||
}
|
||||
};
|
||||
};
|
||||
4
src-self-hosted/visib.zig
Normal file
4
src-self-hosted/visib.zig
Normal file
@@ -0,0 +1,4 @@
|
||||
pub const Visib = enum {
|
||||
Private,
|
||||
Pub,
|
||||
};
|
||||
@@ -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"
|
||||
|
||||
969
src/analyze.cpp
969
src/analyze.cpp
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
1331
src/codegen.cpp
1331
src/codegen.cpp
File diff suppressed because it is too large
Load Diff
@@ -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
6215
src/ir.cpp
File diff suppressed because it is too large
Load Diff
217
src/ir_print.cpp
217
src/ir_print.cpp
@@ -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");
|
||||
}
|
||||
|
||||
35
src/link.cpp
35
src/link.cpp
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
37
src/main.cpp
37
src/main.cpp
@@ -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);
|
||||
|
||||
|
||||
275
src/os.cpp
275
src/os.cpp
@@ -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\\");
|
||||
|
||||
10
src/os.hpp
10
src/os.hpp
@@ -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);
|
||||
|
||||
131
src/parser.cpp
131
src/parser.cpp
@@ -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("<" SuffixOpExpression ">") 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;
|
||||
}
|
||||
|
||||
121
src/target.cpp
121
src/target.cpp
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 "-%=";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
48
src/util.hpp
48
src/util.hpp
@@ -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
352
src/windows_sdk.cpp
Normal 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
47
src/windows_sdk.h
Normal 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
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
9
std/atomic/index.zig
Normal 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
33
std/atomic/int.zig
Normal 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
240
std/atomic/queue.zig
Normal 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
150
std/atomic/stack.zig
Normal 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;
|
||||
}
|
||||
}
|
||||
136
std/base64.zig
136
std/base64.zig
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
593
std/build.zig
593
std/build.zig
File diff suppressed because it is too large
Load Diff
105
std/c/darwin.zig
105
std/c/darwin.zig
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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: ?×pec) 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();
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -1 +1 @@
|
||||
pub extern "c" fn _errno() &c_int;
|
||||
pub extern "c" fn _errno() *c_int;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
32
std/cstr.zig
32
std/cstr.zig
@@ -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]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
156
std/dynamic_library.zig
Normal 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));
|
||||
}
|
||||
666
std/elf.zig
666
std/elf.zig
@@ -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"),
|
||||
};
|
||||
|
||||
248
std/event.zig
248
std/event.zig
@@ -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
235
std/event/channel.zig
Normal 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
130
std/event/future.zig
Normal 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
170
std/event/group.zig
Normal 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
190
std/event/lock.zig
Normal 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
43
std/event/locked.zig
Normal 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
Reference in New Issue
Block a user