mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
Compare commits
173 Commits
overload_t
...
v0.3.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6f2eb22a9 | ||
|
|
ef7365224f | ||
|
|
f122d290f1 | ||
|
|
90f5c24d4c | ||
|
|
bcbcc3100f | ||
|
|
f6e798c283 | ||
|
|
f622b3bd4c | ||
|
|
fe54b84aed | ||
|
|
f46697bc54 | ||
|
|
e2b4a19c81 | ||
|
|
9e77fe7787 | ||
|
|
7223435df6 | ||
|
|
fe9ace713b | ||
|
|
03cdc8e3b1 | ||
|
|
352e09970c | ||
|
|
e8a8ac8bc1 | ||
|
|
334c004f1e | ||
|
|
aa534e2b3f | ||
|
|
2ee2bc3129 | ||
|
|
b88e5a8079 | ||
|
|
c339261d1e | ||
|
|
8008fb2c18 | ||
|
|
10219eccb2 | ||
|
|
1a510207e8 | ||
|
|
a21236d661 | ||
|
|
a749a4d265 | ||
|
|
b5afa98507 | ||
|
|
abf0f64ac0 | ||
|
|
dcf0b4c580 | ||
|
|
a9ed514fe5 | ||
|
|
8b0d409695 | ||
|
|
50d2a04c48 | ||
|
|
1864c69f31 | ||
|
|
af0174f360 | ||
|
|
2a4d43d7c7 | ||
|
|
13cb637cb4 | ||
|
|
de4bfe470e | ||
|
|
eaaa5362a5 | ||
|
|
1ea5625183 | ||
|
|
f7659776fc | ||
|
|
ed99e09c4e | ||
|
|
e9181a75e6 | ||
|
|
963b8f28ef | ||
|
|
5721fcc224 | ||
|
|
287de8f499 | ||
|
|
44dfeb621d | ||
|
|
eb87eb1987 | ||
|
|
c15fb7460c | ||
|
|
927ad2001f | ||
|
|
7647378e7c | ||
|
|
10b0b5f9c7 | ||
|
|
299ec1814b | ||
|
|
07700ed2c5 | ||
|
|
0ae586585a | ||
|
|
285299dcd5 | ||
|
|
2fefed5bda | ||
|
|
da1a45f718 | ||
|
|
bbef94b0fd | ||
|
|
c093f16fd0 | ||
|
|
5ff726d8d1 | ||
|
|
49eacb8824 | ||
|
|
450113d161 | ||
|
|
998c56533b | ||
|
|
73619817ba | ||
|
|
70f6ad1b27 | ||
|
|
e070bf22ee | ||
|
|
b086c85d9f | ||
|
|
66d87b25a3 | ||
|
|
d9e81c6035 | ||
|
|
98ae13c03d | ||
|
|
d9ea953b5c | ||
|
|
4cdfac092b | ||
|
|
1ef43cbc13 | ||
|
|
093bc2089a | ||
|
|
9b5e2b8578 | ||
|
|
2d377ada45 | ||
|
|
02374c6aab | ||
|
|
155c8862c9 | ||
|
|
f73b507ccb | ||
|
|
26e4662c3b | ||
|
|
ede224662c | ||
|
|
bd0e8f1ef1 | ||
|
|
e6a5f98606 | ||
|
|
e15dbd4907 | ||
|
|
ae7aa65f35 | ||
|
|
d13b7ac96a | ||
|
|
03fe2b575d | ||
|
|
f86ef8a743 | ||
|
|
7d58ce0dcb | ||
|
|
f8f249ee2c | ||
|
|
0adb15139f | ||
|
|
76ee384a4c | ||
|
|
b1ed066e55 | ||
|
|
160659c4e3 | ||
|
|
dfe3128b16 | ||
|
|
8f269a4f13 | ||
|
|
1ae251478b | ||
|
|
effec3a1f6 | ||
|
|
4d08fee30e | ||
|
|
5d9a7ab0a6 | ||
|
|
5e184f04e7 | ||
|
|
b2b1a3489a | ||
|
|
fc41179636 | ||
|
|
959c418e8b | ||
|
|
9424bba49f | ||
|
|
314369d069 | ||
|
|
ec3d77f4bd | ||
|
|
ab78663f3c | ||
|
|
348495b4c8 | ||
|
|
6523982f14 | ||
|
|
df8595cd64 | ||
|
|
febd11fa95 | ||
|
|
feba7b8ed2 | ||
|
|
3624c2a72c | ||
|
|
d5f965e137 | ||
|
|
70a429f832 | ||
|
|
7fa129932d | ||
|
|
73ac0b8ea0 | ||
|
|
407ed5a63d | ||
|
|
fa064276bc | ||
|
|
c84f82559c | ||
|
|
bb20a38cdb | ||
|
|
f010f6a926 | ||
|
|
ec1a5d97c9 | ||
|
|
870e716f59 | ||
|
|
d33ff212a7 | ||
|
|
ee533c5500 | ||
|
|
a281dbe812 | ||
|
|
1d39fc475f | ||
|
|
f5a1894876 | ||
|
|
db06f99445 | ||
|
|
05d4ec55f6 | ||
|
|
dcfcf460a5 | ||
|
|
eb86b83bd7 | ||
|
|
3d844b8722 | ||
|
|
6bd72c2ec4 | ||
|
|
4783946476 | ||
|
|
9f7ed00f04 | ||
|
|
f05ffc84d8 | ||
|
|
55f7046da8 | ||
|
|
c5b9b6c761 | ||
|
|
5029dc703e | ||
|
|
cbb731b42b | ||
|
|
258a6ba97a | ||
|
|
e1b5b0b60c | ||
|
|
58647043f4 | ||
|
|
379f9e60bf | ||
|
|
a4d4c27ca6 | ||
|
|
81bea9bad6 | ||
|
|
52f3948026 | ||
|
|
46c182f3d1 | ||
|
|
cc71b96c38 | ||
|
|
ad18d9ba48 | ||
|
|
4d5821408d | ||
|
|
48ee567f81 | ||
|
|
321c713687 | ||
|
|
be5c82cfa6 | ||
|
|
4fa4b2a631 | ||
|
|
9b14340a57 | ||
|
|
e7fad16d0f | ||
|
|
62e3b8063e | ||
|
|
c7f0f58e82 | ||
|
|
35549c21bc | ||
|
|
6220bda4a3 | ||
|
|
81a2474f75 | ||
|
|
28f2247602 | ||
|
|
1541354587 | ||
|
|
a66c0942f8 | ||
|
|
fd9d300b06 | ||
|
|
b60862ec7a | ||
|
|
c8166f6fdb | ||
|
|
4d27150952 | ||
|
|
d4aec525f5 |
39
.github/workflows/main.yml
vendored
39
.github/workflows/main.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
branches: [ master ]
|
||||
|
||||
env:
|
||||
LLVM_RELEASE_VERSION: 14
|
||||
LLVM_RELEASE_VERSION: 15
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -52,6 +52,11 @@ jobs:
|
||||
cd test
|
||||
python3.exe src/tester.py ..\build\${{ matrix.build_type }}\c3c.exe test_suite/
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -g1 --safe
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -79,8 +84,8 @@ jobs:
|
||||
install: git binutils mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-python
|
||||
- shell: msys2 {0}
|
||||
run: |
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-13.0.1-2-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-13.0.1-2-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-15.0.3-1-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-15.0.3-1-any.pkg.tar.zst
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
@@ -106,7 +111,7 @@ jobs:
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c.exe test_suite/
|
||||
python3 src/tester.py ../build/c3c.exe test_suite2/
|
||||
|
||||
|
||||
build-msys2-clang:
|
||||
@@ -164,7 +169,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [12, 13, 14, 15, 16]
|
||||
llvm_version: [13, 14, 15, 16]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -182,10 +187,7 @@ jobs:
|
||||
fi
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
|
||||
|
||||
if [[ "${{matrix.llvm_version}}" > 12 ]]; then
|
||||
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
|
||||
fi
|
||||
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
@@ -208,6 +210,11 @@ jobs:
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit -g1 --safe
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
@@ -251,11 +258,12 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [12, 13, 14]
|
||||
llvm_version: [13, 14, 15]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download LLVM
|
||||
run: |
|
||||
brew update && brew install --overwrite python && brew install python-tk
|
||||
brew install llvm@${{ matrix.llvm_version }} botan ninja
|
||||
echo "/usr/local/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
@@ -273,6 +281,11 @@ jobs:
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit -g1 --safe
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
@@ -291,7 +304,11 @@ jobs:
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
if [[ "${{matrix.llvm_version}}" < 15 ]]; then
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
else
|
||||
python3 src/tester.py ../build/c3c test_suite2/
|
||||
fi
|
||||
|
||||
- name: bundle_output
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
|
||||
|
||||
@@ -44,7 +44,7 @@ if(C3_USE_MIMALLOC)
|
||||
endif()
|
||||
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
if (${C3_LLVM_VERSION} VERSION_LESS 12 OR ${C3_LLVM_VERSION} VERSION_GREATER 16)
|
||||
if (${C3_LLVM_VERSION} VERSION_LESS 13 OR ${C3_LLVM_VERSION} VERSION_GREATER 16)
|
||||
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
|
||||
endif()
|
||||
endif()
|
||||
@@ -84,6 +84,7 @@ message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
link_directories(${LLVM_LIBRARY_DIRS})
|
||||
add_definitions(${LLVM_DEFINITIONS})
|
||||
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
@@ -129,22 +130,22 @@ message(STATUS "using find_library")
|
||||
if(C3_USE_TB)
|
||||
find_library(TB_LIB NAMES tildebackend.a tildebackend.lib PATHS ${CMAKE_SOURCE_DIR}/tb/)
|
||||
endif()
|
||||
find_library(LLD_COFF NAMES lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_COMMON NAMES lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_ELF NAMES lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_COFF NAMES lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
if (${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
|
||||
find_library(LLD_MACHO NAMES lldMachO2.lib lldMachO2.a liblldMachO2.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_MACHO NAMES lldMachO2.lib lldMachO2.a liblldMachO2.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
else ()
|
||||
find_library(LLD_MACHO NAMES lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_MACHO NAMES lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
endif ()
|
||||
find_library(LLD_MINGW NAMES lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_WASM NAMES lldWasm.lib lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_MINGW NAMES lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES lldWasm.lib lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
|
||||
if (${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
|
||||
find_library(LLD_CORE NAMES lldCore.lib lldCore.a liblldCore.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_DRIVER NAMES lldDriver.lib lldDriver.a liblldDriver.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_READER_WRITER NAMES lldReaderWriter.lib lldReaderWriter.a liblldReaderWriter.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_YAML NAMES lldYAML.lib lldYAML.a liblldYAML.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_CORE NAMES lldCore.lib lldCore.a liblldCore.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_DRIVER NAMES lldDriver.lib lldDriver.a liblldDriver.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_READER_WRITER NAMES lldReaderWriter.lib lldReaderWriter.a liblldReaderWriter.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_YAML NAMES lldYAML.lib lldYAML.a liblldYAML.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
endif ()
|
||||
|
||||
set(lld_libs
|
||||
@@ -216,6 +217,9 @@ add_executable(c3c
|
||||
src/compiler/sema_expr.c
|
||||
src/compiler/sema_internal.h
|
||||
src/compiler/sema_name_resolution.c
|
||||
src/compiler/sema_errors.c
|
||||
src/compiler/sema_builtins.c
|
||||
src/compiler/sema_initializers.c
|
||||
src/compiler/semantic_analyser.c
|
||||
src/compiler/sema_passes.c
|
||||
src/compiler/sema_stmts.c
|
||||
@@ -245,6 +249,7 @@ add_executable(c3c
|
||||
src/utils/vmem.c
|
||||
src/utils/vmem.h
|
||||
src/utils/whereami.c
|
||||
src/utils/cpus.c
|
||||
src/compiler/decltable.c
|
||||
src/compiler/mac_support.c
|
||||
src/compiler/tilde_codegen_storeload.c
|
||||
@@ -254,7 +259,9 @@ add_executable(c3c
|
||||
src/compiler/tilde_codegen_type.c
|
||||
src/compiler/windows_support.c
|
||||
src/compiler/codegen_asm.c
|
||||
src/compiler/asm_target.c)
|
||||
src/compiler/asm_target.c
|
||||
src/compiler/llvm_codegen_builtins.c
|
||||
src/compiler/expr.c src/utils/time.c)
|
||||
|
||||
|
||||
target_include_directories(c3c PRIVATE
|
||||
@@ -295,10 +302,10 @@ if(MSVC)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "using gcc/clang warning switches")
|
||||
target_compile_options(c3c PRIVATE -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
|
||||
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
||||
if (WIN32)
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
target_compile_definitions(c3c PRIVATE USE_PTHREAD=1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
10
README.md
10
README.md
@@ -32,8 +32,8 @@ module stack <Type>;
|
||||
|
||||
struct Stack
|
||||
{
|
||||
usize capacity;
|
||||
usize size;
|
||||
usz capacity;
|
||||
usz size;
|
||||
Type* elems;
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ fn void test()
|
||||
|
||||
### Current status
|
||||
|
||||
The current version of the compiler is alpha release 0.2.
|
||||
The current version of the compiler is alpha release 0.3.
|
||||
|
||||
It's possible to try out the current C3 compiler in the browser: https://ide.judge0.com/ – this is courtesy of the
|
||||
developer of Judge0.
|
||||
@@ -261,7 +261,7 @@ You can try it out by running some sample code: `c3c.exe compile ../resources/ex
|
||||
|
||||
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
|
||||
2. Install CMake: `sudo apt install cmake`
|
||||
3. Install LLVM 12 (or greater: C3C supports LLVM 12-16): `sudo apt-get install clang-12 zlib1g zlib1g-dev libllvm12 llvm-12 llvm-12-dev llvm-12-runtime liblld-12-dev liblld-12`
|
||||
3. Install LLVM 13 (or greater: C3C supports LLVM 13-16): `sudo apt-get install clang-13 zlib1g zlib1g-dev libllvm13 llvm-13 llvm-13-dev llvm-13-runtime liblld-13-dev liblld-13`
|
||||
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
5. Enter the C3C directory `cd c3c`.
|
||||
6. Create a build directory `mkdir build`
|
||||
@@ -277,7 +277,7 @@ You can try it out by running some sample code: `./c3c compile ../resources/exam
|
||||
#### Compiling on other Linux / Unix variants
|
||||
|
||||
1. Install CMake.
|
||||
2. Install or compile LLVM and LLD *libraries* (version 12+ or higher)
|
||||
2. Install or compile LLVM and LLD *libraries* (version 13+ or higher)
|
||||
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
4. Enter the C3C directory `cd c3c`.
|
||||
5. Create a build directory `mkdir build`
|
||||
|
||||
@@ -29,17 +29,14 @@ else
|
||||
fi
|
||||
|
||||
TAG="$1"
|
||||
if [ "$1" = 20 ]; then
|
||||
UBUNTU_VERSION="20.04"
|
||||
LLVM_VERSION="12"
|
||||
elif [ "$1" = 21 ]; then
|
||||
if [ "$1" = 21 ]; then
|
||||
UBUNTU_VERSION="21.10"
|
||||
LLVM_VERSION="13"
|
||||
elif [ "$1" = 22 ]; then
|
||||
UBUNTU_VERSION="22.04"
|
||||
LLVM_VERSION="14"
|
||||
else
|
||||
echo "ERROR: expected 20, 21 or 22 as Ubuntu version argument" 1>&2
|
||||
echo "ERROR: expected 21 or 22 as Ubuntu version argument" 1>&2
|
||||
exit 2
|
||||
fi
|
||||
IMAGE="$IMAGE:$TAG"
|
||||
|
||||
73
lib/std/ascii.c3
Normal file
73
lib/std/ascii.c3
Normal file
@@ -0,0 +1,73 @@
|
||||
module std::ascii;
|
||||
|
||||
macro bool in_range_m(c, start, len) => (uint)(c - start) < len;
|
||||
macro bool is_lower_m(c) => in_range_m(c, 0x61, 26);
|
||||
macro bool is_upper_m(c) => in_range_m(c, 0x41, 26);
|
||||
macro bool is_digit_m(c) => in_range_m(c, 0x30, 10);
|
||||
macro bool is_bdigit_m(c) => in_range_m(c, 0x30, 2);
|
||||
macro bool is_odigit_m(c) => in_range_m(c, 0x30, 8);
|
||||
macro bool is_xdigit_m(c) => in_range_m(c | 32, 0x61, 6) || is_digit_m(c);
|
||||
macro bool is_alpha_m(c) => in_range_m(c | 32, 0x61, 26);
|
||||
macro bool is_print_m(c) => in_range_m(c, 0x20, 95);
|
||||
macro bool is_graph_m(c) => in_range_m(c, 0x21, 94);
|
||||
macro bool is_space_m(c) => in_range_m(c, 0x9, 5) || c == 0x20;
|
||||
macro bool is_alnum_m(c) => is_alpha_m(c) || is_digit_m(c);
|
||||
macro bool is_punct_m(c) => !is_alnum_m(c) && is_graph_m(c);
|
||||
macro bool is_blank_m(c) => c == 0x20 || c == 0x9;
|
||||
macro bool is_cntrl_m(c) => c < 0x20 || c == 0x7f;
|
||||
macro to_lower_m(c) => is_upper_m(c) ? c + 0x20 : c;
|
||||
macro to_upper_m(c) => is_lower_m(c) ? c - 0x20 : c;
|
||||
|
||||
fn bool in_range(char c, char start, char len) => in_range_m(c, start, len);
|
||||
fn bool is_lower(char c) => is_lower_m(c);
|
||||
fn bool is_upper(char c) => is_upper_m(c);
|
||||
fn bool is_digit(char c) => is_digit_m(c);
|
||||
fn bool is_bdigit(char c) => is_bdigit_m(c);
|
||||
fn bool is_odigit(char c) => is_odigit_m(c);
|
||||
fn bool is_xdigit(char c) => is_xdigit_m(c);
|
||||
fn bool is_alpha(char c) => is_alpha_m(c);
|
||||
fn bool is_print(char c) => is_print_m(c);
|
||||
fn bool is_graph(char c) => is_graph_m(c);
|
||||
fn bool is_space(char c) => is_space_m(c);
|
||||
fn bool is_alnum(char c) => is_alnum_m(c);
|
||||
fn bool is_punct(char c) => is_punct_m(c);
|
||||
fn bool is_blank(char c) => is_blank_m(c);
|
||||
fn bool is_cntrl(char c) => is_cntrl_m(c);
|
||||
fn char to_lower(char c) => (char)to_lower_m(c);
|
||||
fn char to_upper(char c) => (char)to_upper_m(c);
|
||||
|
||||
fn bool char.in_range(char c, char start, char len) => in_range_m(c, start, len);
|
||||
fn bool char.is_lower(char c) => is_lower_m(c);
|
||||
fn bool char.is_upper(char c) => is_upper_m(c);
|
||||
fn bool char.is_digit(char c) => is_digit_m(c);
|
||||
fn bool char.is_bdigit(char c) => is_bdigit_m(c);
|
||||
fn bool char.is_odigit(char c) => is_odigit_m(c);
|
||||
fn bool char.is_xdigit(char c) => is_xdigit_m(c);
|
||||
fn bool char.is_alpha(char c) => is_alpha_m(c);
|
||||
fn bool char.is_print(char c) => is_print_m(c);
|
||||
fn bool char.is_graph(char c) => is_graph_m(c);
|
||||
fn bool char.is_space(char c) => is_space_m(c);
|
||||
fn bool char.is_alnum(char c) => is_alnum_m(c);
|
||||
fn bool char.is_punct(char c) => is_punct_m(c);
|
||||
fn bool char.is_blank(char c) => is_blank_m(c);
|
||||
fn bool char.is_cntrl(char c) => is_cntrl_m(c);
|
||||
fn char char.to_lower(char c) => (char)to_lower_m(c);
|
||||
fn char char.to_upper(char c) => (char)to_upper_m(c);
|
||||
|
||||
fn bool uint.in_range(uint c, uint start, uint len) => in_range_m(c, start, len);
|
||||
fn bool uint.is_lower(uint c) => is_lower_m(c);
|
||||
fn bool uint.is_upper(uint c) => is_upper_m(c);
|
||||
fn bool uint.is_digit(uint c) => is_digit_m(c);
|
||||
fn bool uint.is_bdigit(uint c) => is_bdigit_m(c);
|
||||
fn bool uint.is_odigit(uint c) => is_odigit_m(c);
|
||||
fn bool uint.is_xdigit(uint c) => is_xdigit_m(c);
|
||||
fn bool uint.is_alpha(uint c) => is_alpha_m(c);
|
||||
fn bool uint.is_print(uint c) => is_print_m(c);
|
||||
fn bool uint.is_graph(uint c) => is_graph_m(c);
|
||||
fn bool uint.is_space(uint c) => is_space_m(c);
|
||||
fn bool uint.is_alnum(uint c) => is_alnum_m(c);
|
||||
fn bool uint.is_punct(uint c) => is_punct_m(c);
|
||||
fn bool uint.is_blank(uint c) => is_blank_m(c);
|
||||
fn bool uint.is_cntrl(uint c) => is_cntrl_m(c);
|
||||
fn uint uint.to_lower(uint c) => (uint)to_lower_m(c);
|
||||
fn uint uint.to_upper(uint c) => (uint)to_upper_m(c);
|
||||
217
lib/std/bits.c3
217
lib/std/bits.c3
@@ -1,78 +1,173 @@
|
||||
module std::bits;
|
||||
|
||||
/**
|
||||
* @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
|
||||
**/
|
||||
macro popcount(i) @operator(intvec)
|
||||
{
|
||||
return $$popcount(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
|
||||
**/
|
||||
macro reverse(i)
|
||||
{
|
||||
return $$bitreverse(i);
|
||||
}
|
||||
macro reverse(i) = $$bitreverse(i);
|
||||
|
||||
/**
|
||||
* @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
|
||||
**/
|
||||
macro bswap(i) @builtin
|
||||
{
|
||||
return $$bswap(i);
|
||||
}
|
||||
macro bswap(i) @builtin = $$bswap(i);
|
||||
|
||||
/**
|
||||
* @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
|
||||
**/
|
||||
macro ctz(i) @operator(intvec) @builtin
|
||||
{
|
||||
return $$ctz(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
|
||||
**/
|
||||
macro clz(i) @operator(intvec) @builtin
|
||||
{
|
||||
return $$clz(i);
|
||||
}
|
||||
macro uint[<*>].popcount(uint[<*>] i) = $$popcount(i);
|
||||
macro uint[<*>].ctz(uint[<*>] i) = $$ctz(i);
|
||||
macro uint[<*>].clz(uint[<*>] i) = $$clz(i);
|
||||
macro uint[<*>] uint[<*>].fshl(uint[<*>] hi, uint[<*>] lo, uint[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro uint[<*>] uint[<*>].fshr(uint[<*>] hi, uint[<*>] lo, uint[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro uint[<*>] uint[<*>].rotl(uint[<*>] i, uint[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro uint[<*>] uint[<*>].rotr(uint[<*>] i, uint[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
/**
|
||||
* @require types::is_intlike($typeof(hi)) && types::is_intlike($typeof(lo)) && types::is_intlike($typeof(shift)) `The input must be an integer or integer vector`
|
||||
* @require types::@has_same(hi, lo, shift) `Hi, low and shift arguments must have the same type`
|
||||
**/
|
||||
macro fshl(hi, lo, shift) @builtin
|
||||
{
|
||||
return $$fshl(hi, lo, ($typeof(hi))shift);
|
||||
}
|
||||
macro int[<*>].popcount(int[<*>] i) = $$popcount(i);
|
||||
macro int[<*>].ctz(int[<*>] i) = $$ctz(i);
|
||||
macro int[<*>].clz(int[<*>] i) = $$clz(i);
|
||||
macro int[<*>] int[<*>].fshl(int[<*>] hi, int[<*>] lo, int[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro int[<*>] int[<*>].fshr(int[<*>] hi, int[<*>] lo, int[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro int[<*>] int[<*>].rotl(int[<*>] i, int[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro int[<*>] int[<*>].rotr(int[<*>] i, int[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
/**
|
||||
* @require types::is_intlike($typeof(hi)) && types::is_intlike($typeof(lo)) && types::is_intlike($typeof(shift)) `The input must be an integer or integer vector`
|
||||
* @require types::@has_same(hi, lo, shift) `Hi, low and shift arguments must have the same type`
|
||||
**/
|
||||
macro fshr(hi, lo, shift) @builtin
|
||||
{
|
||||
return $$fshr(hi, lo, ($typeof(hi))shift);
|
||||
}
|
||||
macro ushort[<*>].popcount(ushort[<*>] i) = $$popcount(i);
|
||||
macro ushort[<*>].ctz(ushort[<*>] i) = $$ctz(i);
|
||||
macro ushort[<*>].clz(ushort[<*>] i) = $$clz(i);
|
||||
macro ushort[<*>] ushort[<*>].fshl(ushort[<*>] hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ushort[<*>] ushort[<*>].fshr(ushort[<*>] hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ushort[<*>] ushort[<*>].rotl(ushort[<*>] i, ushort[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro ushort[<*>] ushort[<*>].rotr(ushort[<*>] i, ushort[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
/**
|
||||
* @require types::is_intlike($typeof(i)) && types::is_intlike($typeof(shift)) `The input must be an integer or integer vector`
|
||||
* @require types::@has_same(i, shift) `The shift value must have the same type as shifted types`
|
||||
**/
|
||||
macro rotl(i, shift) @operator(intvec) @builtin
|
||||
{
|
||||
return $$fshl(i, i, shift);
|
||||
}
|
||||
macro short[<*>].popcount(short[<*>] i) = $$popcount(i);
|
||||
macro short[<*>].ctz(short[<*>] i) = $$ctz(i);
|
||||
macro short[<*>].clz(short[<*>] i) = $$clz(i);
|
||||
macro short[<*>] short[<*>].fshl(short[<*>] hi, short[<*>] lo, short[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro short[<*>] short[<*>].fshr(short[<*>] hi, short[<*>] lo, short[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro short[<*>] short[<*>].rotl(short[<*>] i, short[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro short[<*>] short[<*>].rotr(short[<*>] i, short[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
/**
|
||||
* @require types::is_intlike($typeof(i)) && types::is_intlike($typeof(shift)) `The input must be an integer or integer vector`
|
||||
* @require types::@has_same(i, shift) `The shift value must have the same type as shifted types`
|
||||
**/
|
||||
macro rotr(i, shift) @operator(intvec) @builtin
|
||||
{
|
||||
return $$fshr(i, i, shift);
|
||||
}
|
||||
macro char[<*>].popcount(char[<*>] i) = $$popcount(i);
|
||||
macro char[<*>].ctz(char[<*>] i) = $$ctz(i);
|
||||
macro char[<*>].clz(char[<*>] i) = $$clz(i);
|
||||
macro char[<*>] char[<*>].fshl(char[<*>] hi, char[<*>] lo, char[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro char[<*>] char[<*>].fshr(char[<*>] hi, char[<*>] lo, char[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro char[<*>] char[<*>].rotl(char[<*>] i, char[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro char[<*>] char[<*>].rotr(char[<*>] i, char[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ichar[<*>].popcount(ichar[<*>] i) = $$popcount(i);
|
||||
macro ichar[<*>].ctz(ichar[<*>] i) = $$ctz(i);
|
||||
macro ichar[<*>].clz(ichar[<*>] i) = $$clz(i);
|
||||
macro ichar[<*>] ichar[<*>].fshl(ichar[<*>] hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ichar[<*>] ichar[<*>].fshr(ichar[<*>] hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ichar[<*>] ichar[<*>].rotl(ichar[<*>] i, ichar[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro ichar[<*>] ichar[<*>].rotr(ichar[<*>] i, ichar[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ulong[<*>].popcount(ulong[<*>] i) = $$popcount(i);
|
||||
macro ulong[<*>].ctz(ulong[<*>] i) = $$ctz(i);
|
||||
macro ulong[<*>].clz(ulong[<*>] i) = $$clz(i);
|
||||
macro ulong[<*>] ulong[<*>].fshl(ulong[<*>] hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ulong[<*>] ulong[<*>].fshr(ulong[<*>] hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ulong[<*>] ulong[<*>].rotl(ulong[<*>] i, ulong[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro ulong[<*>] ulong[<*>].rotr(ulong[<*>] i, ulong[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro long[<*>].popcount(long[<*>] i) = $$popcount(i);
|
||||
macro long[<*>].ctz(long[<*>] i) = $$ctz(i);
|
||||
macro long[<*>].clz(long[<*>] i) = $$clz(i);
|
||||
macro long[<*>] long[<*>].fshl(long[<*>] hi, long[<*>] lo, long[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro long[<*>] long[<*>].fshr(long[<*>] hi, long[<*>] lo, long[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro long[<*>] long[<*>].rotl(long[<*>] i, long[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro long[<*>] long[<*>].rotr(long[<*>] i, long[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro uint128[<*>].popcount(uint128[<*>] i) = $$popcount(i);
|
||||
macro uint128[<*>].ctz(uint128[<*>] i) = $$ctz(i);
|
||||
macro uint128[<*>].clz(uint128[<*>] i) = $$clz(i);
|
||||
macro uint128[<*>] uint128[<*>].fshl(uint128[<*>] hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro uint128[<*>] uint128[<*>].fshr(uint128[<*>] hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro uint128[<*>] uint128[<*>].rotl(uint128[<*>] i, uint128[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro uint128[<*>] uint128[<*>].rotr(uint128[<*>] i, uint128[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro int128[<*>].popcount(int128[<*>] i) = $$popcount(i);
|
||||
macro int128[<*>].ctz(int128[<*>] i) = $$ctz(i);
|
||||
macro int128[<*>].clz(int128[<*>] i) = $$clz(i);
|
||||
macro int128[<*>] int128[<*>].fshl(int128[<*>] hi, int128[<*>] lo, int128[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro int128[<*>] int128[<*>].fshr(int128[<*>] hi, int128[<*>] lo, int128[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro int128[<*>] int128[<*>].rotl(int128[<*>] i, int128[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro int128[<*>] int128[<*>].rotr(int128[<*>] i, int128[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro uint.popcount(uint i) = $$popcount(i);
|
||||
macro uint.ctz(uint i) = $$ctz(i);
|
||||
macro uint.clz(uint i) = $$clz(i);
|
||||
macro uint uint.fshl(uint hi, uint lo, uint shift) => $$fshl(hi, lo, shift);
|
||||
macro uint uint.fshr(uint hi, uint lo, uint shift) => $$fshr(hi, lo, shift);
|
||||
macro uint uint.rotl(uint i, uint shift) => $$fshl(i, i, shift);
|
||||
macro uint uint.rotr(uint i, uint shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro int.popcount(int i) = $$popcount(i);
|
||||
macro int.ctz(int i) = $$ctz(i);
|
||||
macro int.clz(int i) = $$clz(i);
|
||||
macro int int.fshl(int hi, int lo, int shift) => $$fshl(hi, lo, shift);
|
||||
macro int int.fshr(int hi, int lo, int shift) => $$fshr(hi, lo, shift);
|
||||
macro int int.rotl(int i, int shift) => $$fshl(i, i, shift);
|
||||
macro int int.rotr(int i, int shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ushort.popcount(ushort i) = $$popcount(i);
|
||||
macro ushort.ctz(ushort i) = $$ctz(i);
|
||||
macro ushort.clz(ushort i) = $$clz(i);
|
||||
macro ushort ushort.fshl(ushort hi, ushort lo, ushort shift) => $$fshl(hi, lo, shift);
|
||||
macro ushort ushort.fshr(ushort hi, ushort lo, ushort shift) => $$fshr(hi, lo, shift);
|
||||
macro ushort ushort.rotl(ushort i, ushort shift) => $$fshl(i, i, shift);
|
||||
macro ushort ushort.rotr(ushort i, ushort shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro short.popcount(short i) = $$popcount(i);
|
||||
macro short.ctz(short i) = $$ctz(i);
|
||||
macro short.clz(short i) = $$clz(i);
|
||||
macro short short.fshl(short hi, short lo, short shift) => $$fshl(hi, lo, shift);
|
||||
macro short short.fshr(short hi, short lo, short shift) => $$fshr(hi, lo, shift);
|
||||
macro short short.rotl(short i, short shift) => $$fshl(i, i, shift);
|
||||
macro short short.rotr(short i, short shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro char.popcount(char i) = $$popcount(i);
|
||||
macro char.ctz(char i) = $$ctz(i);
|
||||
macro char.clz(char i) = $$clz(i);
|
||||
macro char char.fshl(char hi, char lo, char shift) => $$fshl(hi, lo, shift);
|
||||
macro char char.fshr(char hi, char lo, char shift) => $$fshr(hi, lo, shift);
|
||||
macro char char.rotl(char i, char shift) => $$fshl(i, i, shift);
|
||||
macro char char.rotr(char i, char shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ichar.popcount(ichar i) = $$popcount(i);
|
||||
macro ichar.ctz(ichar i) = $$ctz(i);
|
||||
macro ichar.clz(ichar i) = $$clz(i);
|
||||
macro ichar ichar.fshl(ichar hi, ichar lo, ichar shift) => $$fshl(hi, lo, shift);
|
||||
macro ichar ichar.fshr(ichar hi, ichar lo, ichar shift) => $$fshr(hi, lo, shift);
|
||||
macro ichar ichar.rotl(ichar i, ichar shift) => $$fshl(i, i, shift);
|
||||
macro ichar ichar.rotr(ichar i, ichar shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ulong.popcount(ulong i) = $$popcount(i);
|
||||
macro ulong.ctz(ulong i) = $$ctz(i);
|
||||
macro ulong.clz(ulong i) = $$clz(i);
|
||||
macro ulong ulong.fshl(ulong hi, ulong lo, ulong shift) => $$fshl(hi, lo, shift);
|
||||
macro ulong ulong.fshr(ulong hi, ulong lo, ulong shift) => $$fshr(hi, lo, shift);
|
||||
macro ulong ulong.rotl(ulong i, ulong shift) => $$fshl(i, i, shift);
|
||||
macro ulong ulong.rotr(ulong i, ulong shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro long.popcount(long i) = $$popcount(i);
|
||||
macro long.ctz(long i) = $$ctz(i);
|
||||
macro long.clz(long i) = $$clz(i);
|
||||
macro long long.fshl(long hi, long lo, long shift) => $$fshl(hi, lo, shift);
|
||||
macro long long.fshr(long hi, long lo, long shift) => $$fshr(hi, lo, shift);
|
||||
macro long long.rotl(long i, long shift) => $$fshl(i, i, shift);
|
||||
macro long long.rotr(long i, long shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro uint128.popcount(uint128 i) = $$popcount(i);
|
||||
macro uint128.ctz(uint128 i) = $$ctz(i);
|
||||
macro uint128.clz(uint128 i) = $$clz(i);
|
||||
macro uint128 uint128.fshl(uint128 hi, uint128 lo, uint128 shift) => $$fshl(hi, lo, shift);
|
||||
macro uint128 uint128.fshr(uint128 hi, uint128 lo, uint128 shift) => $$fshr(hi, lo, shift);
|
||||
macro uint128 uint128.rotl(uint128 i, uint128 shift) => $$fshl(i, i, shift);
|
||||
macro uint128 uint128.rotr(uint128 i, uint128 shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro int128.popcount(int128 i) = $$popcount(i);
|
||||
macro int128.ctz(int128 i) = $$ctz(i);
|
||||
macro int128.clz(int128 i) = $$clz(i);
|
||||
macro int128 int128.fshl(int128 hi, int128 lo, int128 shift) => $$fshl(hi, lo, shift);
|
||||
macro int128 int128.fshr(int128 hi, int128 lo, int128 shift) => $$fshr(hi, lo, shift);
|
||||
macro int128 int128.rotl(int128 i, int128 shift) => $$fshl(i, i, shift);
|
||||
macro int128 int128.rotr(int128 i, int128 shift) => $$fshr(i, i, shift);
|
||||
|
||||
@@ -2,14 +2,14 @@ module std::core::mem::allocator;
|
||||
|
||||
struct ArenaAllocatorHeader
|
||||
{
|
||||
usize size;
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require data `unexpectedly missing the allocator`
|
||||
*/
|
||||
private fn void*! arena_allocator_function(Allocator* data, usize size, usize alignment, usize offset, void* old_pointer, AllocationKind kind)
|
||||
private fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
|
||||
{
|
||||
ArenaAllocator* arena = (ArenaAllocator*)data;
|
||||
bool clear = false;
|
||||
@@ -60,17 +60,17 @@ private fn void*! arena_allocator_function(Allocator* data, usize size, usize al
|
||||
* @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require offset <= MAX_MEMORY_ALIGNMENT `offset too big`
|
||||
* @require offset <= size && offset >= 0
|
||||
* @require mem::aligned_offset(offset, $alignof(ArenaAllocatorHeader)) == offset
|
||||
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
|
||||
* @require this != null
|
||||
**/
|
||||
private fn void*! ArenaAllocator._alloc(ArenaAllocator* this, usize size, usize alignment, usize offset)
|
||||
private fn void*! ArenaAllocator._alloc(ArenaAllocator* this, usz size, usz alignment, usz offset)
|
||||
{
|
||||
usize total_len = this.data.len;
|
||||
usz total_len = this.data.len;
|
||||
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE!;
|
||||
void* start_mem = this.data.ptr;
|
||||
void* unaligned_pointer_to_offset = start_mem + this.used + ArenaAllocatorHeader.sizeof + offset;
|
||||
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
||||
usize end = (usize)(aligned_pointer_to_offset - this.data.ptr) + size - offset;
|
||||
usz end = (usz)(aligned_pointer_to_offset - this.data.ptr) + size - offset;
|
||||
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
this.used = end;
|
||||
void *mem = aligned_pointer_to_offset - offset;
|
||||
@@ -86,16 +86,16 @@ private fn void*! ArenaAllocator._alloc(ArenaAllocator* this, usize size, usize
|
||||
* @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require offset <= MAX_MEMORY_ALIGNMENT `offset too big`
|
||||
* @require offset <= size && offset >= 0
|
||||
* @require mem::aligned_offset(offset, $alignof(ArenaAllocatorHeader)) == offset
|
||||
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
|
||||
* @require this != null
|
||||
**/
|
||||
private fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointer, usize size, usize alignment, usize offset)
|
||||
private fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointer, usz size, usz alignment, usz offset)
|
||||
{
|
||||
assert(old_pointer >= this.data.ptr, "Pointer originates from a different allocator.");
|
||||
usize total_len = this.data.len;
|
||||
usz total_len = this.data.len;
|
||||
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE!;
|
||||
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
|
||||
usize old_size = header.size;
|
||||
usz old_size = header.size;
|
||||
// Do last allocation and alignment match?
|
||||
if (&this.data[this.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer + offset, alignment))
|
||||
{
|
||||
@@ -105,7 +105,7 @@ private fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointe
|
||||
}
|
||||
else
|
||||
{
|
||||
usize new_used = this.used + size - old_size;
|
||||
usz new_used = this.used + size - old_size;
|
||||
if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
this.used = new_used;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ private struct DynamicArenaPage
|
||||
{
|
||||
void* memory;
|
||||
void* prev_arena;
|
||||
usize total;
|
||||
usize used;
|
||||
usz total;
|
||||
usz used;
|
||||
void* last_ptr;
|
||||
}
|
||||
|
||||
private struct DynamicArenaChunk
|
||||
{
|
||||
usize size;
|
||||
usz size;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,7 @@ private fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this, void* pt
|
||||
DynamicArenaPage* current_page = this.page;
|
||||
if (ptr == current_page.last_ptr)
|
||||
{
|
||||
current_page.used = (usize)((ptr - DEFAULT_SIZE_PREFIX) - current_page.memory);
|
||||
current_page.used = (usz)((ptr - DEFAULT_SIZE_PREFIX) - current_page.memory);
|
||||
}
|
||||
current_page.last_ptr = null;
|
||||
}
|
||||
@@ -32,26 +32,26 @@ private fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this, void* pt
|
||||
* @require old_pointer && size > 0
|
||||
* @require this.page `tried to realloc pointer on invalid allocator`
|
||||
*/
|
||||
private fn void*! DynamicArenaAllocator._realloc(DynamicArenaAllocator* this, void* old_pointer, usize size, usize alignment, usize offset)
|
||||
private fn void*! DynamicArenaAllocator._realloc(DynamicArenaAllocator* this, void* old_pointer, usz size, usz alignment, usz offset)
|
||||
{
|
||||
DynamicArenaPage* current_page = this.page;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
usize* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
|
||||
usize old_size = *old_size_ptr;
|
||||
usz* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
|
||||
usz old_size = *old_size_ptr;
|
||||
// We have the old pointer and it's correctly aligned.
|
||||
if (old_size >= size && mem::ptr_is_aligned(old_pointer, alignment))
|
||||
{
|
||||
*old_size_ptr = size;
|
||||
if (current_page.last_ptr == old_pointer)
|
||||
{
|
||||
current_page.used = (usize)((old_pointer - DEFAULT_SIZE_PREFIX) - current_page.memory);
|
||||
current_page.used = (usz)((old_pointer - DEFAULT_SIZE_PREFIX) - current_page.memory);
|
||||
}
|
||||
return old_pointer;
|
||||
}
|
||||
if REUSE: (current_page.last_ptr == old_pointer && mem::ptr_is_aligned(old_pointer, alignment))
|
||||
{
|
||||
assert(size > old_size);
|
||||
usize add_size = size - old_size;
|
||||
usz add_size = size - old_size;
|
||||
if (add_size + current_page.used > current_page.total) break REUSE;
|
||||
*old_size_ptr = size;
|
||||
current_page.used += add_size;
|
||||
@@ -82,10 +82,10 @@ private fn void DynamicArenaAllocator.reset(DynamicArenaAllocator* this)
|
||||
* @require math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
*/
|
||||
private fn void*! DynamicArenaAllocator._alloc_new(DynamicArenaAllocator* this, usize size, usize alignment, usize offset)
|
||||
private fn void*! DynamicArenaAllocator._alloc_new(DynamicArenaAllocator* this, usz size, usz alignment, usz offset)
|
||||
{
|
||||
// First, make sure that we can align it, extending the page size if needed.
|
||||
usize page_size = max(this.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + offset, alignment) - offset);
|
||||
usz page_size = max(this.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + offset, alignment) - offset);
|
||||
|
||||
// Grab the page without alignment (we do it ourselves)
|
||||
void* mem = this.backing_allocator.alloc(page_size)?;
|
||||
@@ -113,7 +113,7 @@ private fn void*! DynamicArenaAllocator._alloc_new(DynamicArenaAllocator* this,
|
||||
* @require size > 0
|
||||
* @require this
|
||||
*/
|
||||
private fn void*! DynamicArenaAllocator._alloc(DynamicArenaAllocator* this, usize size, usize alignment, usize offset)
|
||||
private fn void*! DynamicArenaAllocator._alloc(DynamicArenaAllocator* this, usz size, usz alignment, usz offset)
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
DynamicArenaPage* page = this.page;
|
||||
@@ -125,7 +125,7 @@ private fn void*! DynamicArenaAllocator._alloc(DynamicArenaAllocator* this, usiz
|
||||
}
|
||||
if (!page) return this._alloc_new(size, alignment, offset);
|
||||
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
|
||||
usize new_used = start - page.memory + size;
|
||||
usz new_used = start - page.memory + size;
|
||||
if ALLOCATE_NEW: (new_used > page.total)
|
||||
{
|
||||
if ((page = this.unused_page))
|
||||
@@ -155,7 +155,7 @@ private fn void*! DynamicArenaAllocator._alloc(DynamicArenaAllocator* this, usiz
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require data `unexpectedly missing the allocator`
|
||||
*/
|
||||
private fn void*! dynamic_arena_allocator_function(Allocator* data, usize size, usize alignment, usize offset, void* old_pointer, AllocationKind kind)
|
||||
private fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
|
||||
{
|
||||
DynamicArenaAllocator* allocator = (DynamicArenaAllocator*)data;
|
||||
switch (kind)
|
||||
|
||||
@@ -4,7 +4,7 @@ import libc;
|
||||
private const Allocator _NULL_ALLOCATOR = { &null_allocator_fn };
|
||||
private const Allocator _SYSTEM_ALLOCATOR = { &libc_allocator_fn };
|
||||
|
||||
private fn void*! null_allocator_fn(Allocator* this, usize bytes, usize alignment, usize offset, void* old_pointer, AllocationKind kind)
|
||||
private fn void*! null_allocator_fn(Allocator* this, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
@@ -22,7 +22,7 @@ private fn void*! null_allocator_fn(Allocator* this, usize bytes, usize alignmen
|
||||
|
||||
private struct AlignedBlock
|
||||
{
|
||||
usize len;
|
||||
usz len;
|
||||
void* start;
|
||||
}
|
||||
|
||||
@@ -31,11 +31,11 @@ private struct AlignedBlock
|
||||
* @require bytes > 0
|
||||
* @require alignment > 0
|
||||
**/
|
||||
private fn void* _libc_aligned_alloc(usize bytes, usize alignment, usize offset) @inline
|
||||
private fn void* _libc_aligned_alloc(usz bytes, usz alignment, usz offset) @inline
|
||||
{
|
||||
usize header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
|
||||
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
|
||||
void* data = libc::malloc(header + bytes);
|
||||
void* mem = mem::aligned_pointer(data + offset, alignment) - offset;
|
||||
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
|
||||
assert(mem > data);
|
||||
AlignedBlock* desc = (AlignedBlock*)mem - 1;
|
||||
*desc = { bytes, data };
|
||||
@@ -46,11 +46,11 @@ private fn void* _libc_aligned_alloc(usize bytes, usize alignment, usize offset)
|
||||
* @require bytes > 0
|
||||
* @require alignment > 0
|
||||
**/
|
||||
private fn void* _libc_aligned_calloc(usize bytes, usize alignment, usize offset) @inline
|
||||
private fn void* _libc_aligned_calloc(usz bytes, usz alignment, usz offset) @inline
|
||||
{
|
||||
usize header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
|
||||
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
|
||||
void* data = libc::calloc(header + bytes, 1);
|
||||
void* mem = mem::aligned_pointer(data + offset, alignment) - offset;
|
||||
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
|
||||
AlignedBlock* desc = (AlignedBlock*)mem - 1;
|
||||
assert(mem > data);
|
||||
*desc = { bytes, data };
|
||||
@@ -61,10 +61,10 @@ private fn void* _libc_aligned_calloc(usize bytes, usize alignment, usize offset
|
||||
* @require bytes > 0
|
||||
* @require alignment > 0
|
||||
**/
|
||||
private fn void* _libc_aligned_realloc(void* old_pointer, usize bytes, usize alignment, usize offset) @inline
|
||||
private fn void* _libc_aligned_realloc(void* old_pointer, usz bytes, usz alignment, usz offset) @inline
|
||||
{
|
||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||
void* data_start = desc.start;
|
||||
void* data_start = desc.start;
|
||||
void* new_data = _libc_aligned_calloc(bytes, alignment, offset);
|
||||
mem::copy(new_data, old_pointer, desc.len > bytes ? desc.len : bytes, DEFAULT_MEM_ALIGNMENT, DEFAULT_MEM_ALIGNMENT);
|
||||
libc::free(data_start);
|
||||
@@ -77,7 +77,7 @@ private fn void _libc_aligned_free(void* old_pointer) @inline
|
||||
libc::free(desc.start);
|
||||
}
|
||||
|
||||
fn void*! libc_allocator_fn(Allocator* unused, usize bytes, usize alignment, usize offset, void* old_pointer, AllocationKind kind) @inline
|
||||
fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @inline
|
||||
{
|
||||
if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT;
|
||||
assert(math::is_power_of_2(alignment), "Alignment was not a power of 2");
|
||||
@@ -86,12 +86,10 @@ fn void*! libc_allocator_fn(Allocator* unused, usize bytes, usize alignment, usi
|
||||
switch (kind)
|
||||
{
|
||||
case ALIGNED_ALLOC:
|
||||
if (alignment <= DEFAULT_MEM_ALIGNMENT) nextcase ALLOC;
|
||||
data = _libc_aligned_alloc(bytes, alignment, offset);
|
||||
case ALLOC:
|
||||
data = libc::malloc(bytes);
|
||||
case ALIGNED_CALLOC:
|
||||
if (alignment <= DEFAULT_MEM_ALIGNMENT) nextcase CALLOC;
|
||||
data = _libc_aligned_calloc(bytes, alignment, offset);
|
||||
case CALLOC:
|
||||
data = libc::calloc(bytes, 1);
|
||||
|
||||
@@ -3,7 +3,7 @@ import std::io;
|
||||
|
||||
private struct TempAllocatorChunk
|
||||
{
|
||||
usize size;
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
@@ -12,32 +12,32 @@ struct TempAllocator
|
||||
inline Allocator allocator;
|
||||
Allocator* backing_allocator;
|
||||
TempAllocatorPage* last_page;
|
||||
usize used;
|
||||
usize capacity;
|
||||
usz used;
|
||||
usz capacity;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
|
||||
private const usize PAGE_IS_ALIGNED = (usize)isize.max + 1;
|
||||
private const usz PAGE_IS_ALIGNED = (usz)isz.max + 1u;
|
||||
|
||||
|
||||
struct TempAllocatorPage
|
||||
{
|
||||
TempAllocatorPage* prev_page;
|
||||
void* start;
|
||||
usize mark;
|
||||
usize size;
|
||||
usize ident;
|
||||
usz mark;
|
||||
usz size;
|
||||
usz ident;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
macro usize TempAllocatorPage.pagesize(TempAllocatorPage* page) { return page.size & ~PAGE_IS_ALIGNED; }
|
||||
macro usz TempAllocatorPage.pagesize(TempAllocatorPage* page) { return page.size & ~PAGE_IS_ALIGNED; }
|
||||
macro bool TempAllocatorPage.is_aligned(TempAllocatorPage* page) { return page.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED; }
|
||||
|
||||
/**
|
||||
* @require size >= 16
|
||||
**/
|
||||
fn TempAllocator*! new_temp(usize size, Allocator* backing_allocator)
|
||||
fn TempAllocator*! new_temp(usz size, Allocator* backing_allocator)
|
||||
{
|
||||
TempAllocator* allocator = backing_allocator.alloc(size + TempAllocator.sizeof)?;
|
||||
allocator.last_page = null;
|
||||
@@ -52,7 +52,7 @@ fn TempAllocator*! new_temp(usize size, Allocator* backing_allocator)
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require data `unexpectedly missing the allocator`
|
||||
*/
|
||||
private fn void*! temp_allocator_function(Allocator* data, usize size, usize alignment, usize offset, void* old_pointer, AllocationKind kind)
|
||||
private fn void*! temp_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
|
||||
{
|
||||
TempAllocator* arena = (TempAllocator*)data;
|
||||
switch (kind)
|
||||
@@ -61,7 +61,7 @@ private fn void*! temp_allocator_function(Allocator* data, usize size, usize ali
|
||||
case ALIGNED_CALLOC:
|
||||
assert(!old_pointer, "Unexpected old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
return arena._alloc(size, alignment, offset, true);
|
||||
return arena._alloc(size, alignment_for_allocation(alignment), offset, true);
|
||||
case ALLOC:
|
||||
case ALIGNED_ALLOC:
|
||||
assert(!old_pointer, "Unexpected old pointer for alloc.");
|
||||
@@ -75,7 +75,6 @@ private fn void*! temp_allocator_function(Allocator* data, usize size, usize ali
|
||||
case FREE:
|
||||
case ALIGNED_FREE:
|
||||
if (!old_pointer) return null;
|
||||
io::println("Freeing stuff\n");
|
||||
arena._free(old_pointer)?;
|
||||
return null;
|
||||
case MARK:
|
||||
@@ -89,16 +88,15 @@ private fn void*! temp_allocator_function(Allocator* data, usize size, usize ali
|
||||
|
||||
private fn void! TempAllocator._free(TempAllocator* this, void* old_pointer)
|
||||
{
|
||||
|
||||
// TODO fix free
|
||||
assert((uptr)old_pointer >= (uptr)&this.data, "Pointer originates from a different allocator.");
|
||||
usize old_size = *(usize*)(old_pointer - DEFAULT_SIZE_PREFIX);
|
||||
usz old_size = *(usz*)(old_pointer - DEFAULT_SIZE_PREFIX);
|
||||
if (old_pointer + old_size == &this.data[this.used])
|
||||
{
|
||||
this.used -= old_size;
|
||||
}
|
||||
}
|
||||
private fn void! TempAllocator._reset(TempAllocator* this, usize mark)
|
||||
private fn void! TempAllocator._reset(TempAllocator* this, usz mark)
|
||||
{
|
||||
TempAllocatorPage *last_page = this.last_page;
|
||||
while (last_page && last_page.mark > mark)
|
||||
@@ -118,7 +116,7 @@ private fn void! TempAllocator._free_page(TempAllocator* this, TempAllocatorPage
|
||||
return this.backing_allocator.free(mem);
|
||||
}
|
||||
|
||||
private fn void*! TempAllocator._realloc_page(TempAllocator* this, TempAllocatorPage* page, usize size, usize alignment, usize offset) @inline
|
||||
private fn void*! TempAllocator._realloc_page(TempAllocator* this, TempAllocatorPage* page, usz size, usz alignment, usz offset) @inline
|
||||
{
|
||||
// Then the actual start pointer:
|
||||
void* real_pointer = page.start;
|
||||
@@ -131,7 +129,7 @@ private fn void*! TempAllocator._realloc_page(TempAllocator* this, TempAllocator
|
||||
pointer_to_prev = &((*pointer_to_prev).prev_page);
|
||||
}
|
||||
*pointer_to_prev = page.prev_page;
|
||||
usize page_size = page.pagesize();
|
||||
usz page_size = page.pagesize();
|
||||
// Clear on size > original size.
|
||||
void* data = this._alloc(size, alignment, offset, false)?;
|
||||
mem::copy(data, &page.data[0], page_size, DEFAULT_MEM_ALIGNMENT, DEFAULT_MEM_ALIGNMENT);
|
||||
@@ -146,10 +144,10 @@ private fn void*! TempAllocator._realloc_page(TempAllocator* this, TempAllocator
|
||||
return data;
|
||||
}
|
||||
|
||||
private fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usize size, usize alignment, usize offset) @inline
|
||||
private fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usz size, usz alignment, usz offset) @inline
|
||||
{
|
||||
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
|
||||
if (chunk.size == (usize)-1)
|
||||
if (chunk.size == (usz)-1)
|
||||
{
|
||||
assert(this.last_page, "Realloc of non temp pointer");
|
||||
// First grab the page
|
||||
@@ -172,17 +170,17 @@ private fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usi
|
||||
* @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require this != null
|
||||
**/
|
||||
private fn void*! TempAllocator._alloc(TempAllocator* this, usize size, usize alignment, usize offset, bool clear)
|
||||
private fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignment, usz offset, bool clear)
|
||||
{
|
||||
void* start_mem = &this.data;
|
||||
void* starting_ptr = start_mem + this.used;
|
||||
void* aligned_header_start = mem::aligned_pointer(starting_ptr, $alignof(TempAllocatorChunk));
|
||||
void* aligned_header_start = mem::aligned_pointer(starting_ptr, TempAllocatorChunk.alignof);
|
||||
void* mem = aligned_header_start + TempAllocatorChunk.sizeof;
|
||||
if (alignment > $alignof(TempAllocatorChunk))
|
||||
if (alignment > TempAllocatorChunk.alignof)
|
||||
{
|
||||
mem = mem::aligned_pointer(mem + offset, alignment) - offset;
|
||||
}
|
||||
usize new_usage = (usize)(mem - start_mem) + size;
|
||||
usz new_usage = (usz)(mem - start_mem) + size;
|
||||
|
||||
// Arena alignment, simple!
|
||||
if (new_usage <= this.capacity)
|
||||
@@ -201,7 +199,7 @@ private fn void*! TempAllocator._alloc(TempAllocator* this, usize size, usize al
|
||||
if (alignment > DEFAULT_MEM_ALIGNMENT || offset)
|
||||
{
|
||||
// This is actually simpler, since it will create the offset for us.
|
||||
usize total_alloc_size = TempAllocatorPage.sizeof + size;
|
||||
usz total_alloc_size = TempAllocatorPage.sizeof + size;
|
||||
if (clear)
|
||||
{
|
||||
page = this.backing_allocator.calloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)?;
|
||||
@@ -216,20 +214,20 @@ private fn void*! TempAllocator._alloc(TempAllocator* this, usize size, usize al
|
||||
else
|
||||
{
|
||||
// Here we might need to pad
|
||||
usize padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, DEFAULT_MEM_ALIGNMENT);
|
||||
usize total_alloc_size = padded_header_size + size;
|
||||
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, DEFAULT_MEM_ALIGNMENT);
|
||||
usz total_alloc_size = padded_header_size + size;
|
||||
void* alloc = (clear ? this.backing_allocator.calloc(total_alloc_size) : this.backing_allocator.alloc(total_alloc_size))?;
|
||||
|
||||
// Find the page.
|
||||
page = alloc + padded_header_size - TempAllocatorPage.sizeof;
|
||||
assert(mem::ptr_is_aligned(page, $alignof(TempAllocator)));
|
||||
assert(mem::ptr_is_aligned(page, TempAllocator.alignof));
|
||||
assert(mem::ptr_is_aligned(&page.data[0], DEFAULT_MEM_ALIGNMENT));
|
||||
page.start = alloc;
|
||||
page.size = size;
|
||||
}
|
||||
|
||||
// Mark it as a page
|
||||
page.ident = ~(usize)0;
|
||||
page.ident = ~(usz)0;
|
||||
// Store when it was created
|
||||
page.mark = ++this.used;
|
||||
// Hook up the page.
|
||||
|
||||
@@ -6,26 +6,35 @@ macro tconcat(arr1, arr2)
|
||||
$Type[] result = array::talloc($Type, arr1.len + arr2.len);
|
||||
if (arr1.len > 0)
|
||||
{
|
||||
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $alignof($Type), $alignof($Type));
|
||||
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
}
|
||||
if (arr2.len > 0)
|
||||
{
|
||||
mem::copy(&result[arr1.len], &arr2[0], arr2.len * $Type.sizeof, $alignof($Type), $alignof($Type));
|
||||
mem::copy(&result[arr1.len], &arr2[0], arr2.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
macro index_of(array, element)
|
||||
{
|
||||
foreach (i, &e : array)
|
||||
{
|
||||
if (*e == element) return i;
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
}
|
||||
|
||||
macro concat(arr1, arr2)
|
||||
{
|
||||
var $Type = $typeof(arr1[0]);
|
||||
$Type[] result = array::alloc($Type, arr1.len + arr2.len);
|
||||
if (arr1.len > 0)
|
||||
{
|
||||
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $alignof($Type), $alignof($Type));
|
||||
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
}
|
||||
if (arr2.len > 0)
|
||||
{
|
||||
mem::copy(&result[arr1.len], &arr2[0], arr2.len * $Type.sizeof, $alignof($Type), $alignof($Type));
|
||||
mem::copy(&result[arr1.len], &arr2[0], arr2.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -30,6 +30,16 @@ bitstruct ULongBE : ulong @bigendian
|
||||
ulong val : 0..63;
|
||||
}
|
||||
|
||||
bitstruct Int128BE : int128 @bigendian
|
||||
{
|
||||
int128 val : 0..127;
|
||||
}
|
||||
|
||||
bitstruct UInt128BE : uint128 @bigendian
|
||||
{
|
||||
uint128 val : 0..127;
|
||||
}
|
||||
|
||||
bitstruct ShortLE : short @littleendian
|
||||
{
|
||||
short val : 0..15;
|
||||
@@ -59,3 +69,14 @@ bitstruct ULongLE : ulong @littleendian
|
||||
{
|
||||
ulong val : 0..63;
|
||||
}
|
||||
|
||||
bitstruct Int128LE : int128 @littleendian
|
||||
{
|
||||
int128 val : 0..127;
|
||||
}
|
||||
|
||||
bitstruct UInt128LE : uint128 @littleendian
|
||||
{
|
||||
uint128 val : 0..127;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::builtin;
|
||||
import libc;
|
||||
import std::hash;
|
||||
|
||||
fault IteratorResult
|
||||
{
|
||||
@@ -27,16 +28,16 @@ fault VarCastResult
|
||||
**/
|
||||
macro void @scope(&variable; @body) @builtin
|
||||
{
|
||||
$typeof(variable) temp = variable;
|
||||
var temp = variable;
|
||||
defer variable = temp;
|
||||
@body();
|
||||
}
|
||||
|
||||
macro void @swap(&a, &b) @builtin
|
||||
{
|
||||
$typeof(a) temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
var temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,12 +56,12 @@ macro varcast(variant v, $Type) @builtin
|
||||
struct CallstackElement
|
||||
{
|
||||
CallstackElement* prev;
|
||||
char* function;
|
||||
char* file;
|
||||
char[] function;
|
||||
char[] file;
|
||||
uint line;
|
||||
}
|
||||
|
||||
fn void panic(char* message, char *file, char *function, uint line) @builtin
|
||||
fn void default_panic(char[] message, char[] file, char[] function, uint line)
|
||||
{
|
||||
CallstackElement* stack = $$stacktrace();
|
||||
$if ($defined(libc::stderr) && $defined(libc::fprintf)):
|
||||
@@ -68,24 +69,29 @@ fn void panic(char* message, char *file, char *function, uint line) @builtin
|
||||
if (stack) stack = stack.prev;
|
||||
if (stack)
|
||||
{
|
||||
libc::fprintf(libc::stderr(), "\nERROR: '%s'\n", message);
|
||||
libc::fprintf(libc::stderr(), "\nERROR: '%.*s'\n", (int)message.len, message.ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
libc::fprintf(libc::stderr(), "\nERROR: '%s', function %s (%s:%d)\n", message, function, file, line);
|
||||
libc::fprintf(libc::stderr(), "\nERROR: '%.*s', function %.*s (%.*s:%d)\n",
|
||||
(int)message.len, message.ptr, (int)function.len, function.ptr, (int)file.len, file.ptr, line);
|
||||
}
|
||||
while (stack)
|
||||
{
|
||||
libc::fprintf(libc::stderr(), " at function %s (%s:%u)\n", stack.function, stack.file, stack.line);
|
||||
libc::fprintf(libc::stderr(), " at function %.*s (%.*s:%u)\n", (int)stack.function.len, stack.function.ptr,
|
||||
(int)stack.file.len, stack.file.ptr, stack.line);
|
||||
if (stack == stack.prev) break;
|
||||
stack = stack.prev;
|
||||
}
|
||||
|
||||
$endif;
|
||||
|
||||
$$trap();
|
||||
}
|
||||
|
||||
define PanicFn = fn void(char[] message, char[] file, char[] function, uint line);
|
||||
|
||||
PanicFn panic = &default_panic;
|
||||
|
||||
macro void unreachable($string = "Unreachable statement reached.") @builtin @noreturn
|
||||
{
|
||||
panic($string, $$FILE, $$FUNC, $$LINE);
|
||||
@@ -94,15 +100,15 @@ macro void unreachable($string = "Unreachable statement reached.") @builtin @nor
|
||||
|
||||
macro bitcast(expr, $Type) @builtin
|
||||
{
|
||||
var $size = (usize)($sizeof(expr));
|
||||
$assert($size == $Type.sizeof, "Cannot bitcast between types of different size.");
|
||||
$Type x = void;
|
||||
mem::copy(&x, &expr, $size, $alignof($Type), $alignof(expr));
|
||||
return x;
|
||||
var $size = (usz)($sizeof(expr));
|
||||
$assert($size == $Type.sizeof, "Cannot bitcast between types of different size.");
|
||||
$Type x = void;
|
||||
mem::copy(&x, &expr, $size, $Type.alignof, $alignof(expr));
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $Type.kind == TypeKind.ENUM `Only enums may be used`
|
||||
* @require $Type.kindof == TypeKind.ENUM `Only enums may be used`
|
||||
**/
|
||||
macro enum_by_name($Type, char[] enum_name) @builtin
|
||||
{
|
||||
@@ -112,4 +118,50 @@ macro enum_by_name($Type, char[] enum_name) @builtin
|
||||
if (str::compare(name, enum_name)) return ($Type)i;
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locality for prefetch, levels 0 - 3, corresponding
|
||||
* to "extremely local" to "no locality"
|
||||
**/
|
||||
enum PrefetchLocality
|
||||
{
|
||||
NO_LOCALITY,
|
||||
FAR,
|
||||
NEAR,
|
||||
VERY_NEAR,
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefetch a pointer.
|
||||
|
||||
* @param [in] ptr `Pointer to prefetch`
|
||||
* @param $locality `Locality ranging from none to extremely local`
|
||||
* @param $write `Prefetch for write, otherwise prefetch for read.`
|
||||
**/
|
||||
macro prefetch(void* ptr, PrefetchLocality $locality = VERY_NEAR, bool $write = false) @builtin
|
||||
{
|
||||
$$prefetch(ptr, $write ? 1 : 0, $locality.ordinal);
|
||||
}
|
||||
|
||||
macro bool @castable(#expr, $To) @builtin
|
||||
{
|
||||
return $checks(($To)#expr);
|
||||
}
|
||||
|
||||
macro bool @convertible(#expr, $To) @builtin
|
||||
{
|
||||
return $checks($To x = #expr);
|
||||
}
|
||||
|
||||
macro uint int.hash(int i) = i;
|
||||
macro uint uint.hash(uint i) = i;
|
||||
macro uint short.hash(short s) = s;
|
||||
macro uint ushort.hash(ushort s) = s;
|
||||
macro uint char.hash(char c) = c;
|
||||
macro uint ichar.hash(ichar c) = c;
|
||||
macro uint long.hash(long i) = (uint)((i >> 32) ^ i);
|
||||
macro uint ulong.hash(ulong i) = (uint)((i >> 32) ^ i);
|
||||
macro uint bool.hash(bool b) = (uint)b;
|
||||
macro uint typeid.hash(typeid t) = (uint)(((uptr)t >> 32) ^ (uptr)t);
|
||||
macro uint char[].hash(char[] c) = (uint)fnv32a::encode(c);
|
||||
@@ -74,3 +74,36 @@ macro bool equals(a, b) @builtin
|
||||
return a == b;
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro min(x, ...) @builtin
|
||||
{
|
||||
$if ($vacount == 1):
|
||||
return less(x, $vaarg(0)) ? x : $vaarg(0);
|
||||
$else:
|
||||
var result = x;
|
||||
$for (var $i = 0; $i < $vacount; $i++):
|
||||
if (less($vaarg($i), result))
|
||||
{
|
||||
result = $vaarg($i);
|
||||
}
|
||||
$endfor;
|
||||
return result;
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro max(x, ...) @builtin
|
||||
{
|
||||
$if ($vacount == 1):
|
||||
return greater(x, $vaarg(0)) ? x : $vaarg(0);
|
||||
$else:
|
||||
var result = x;
|
||||
$for (var $i = 0; $i < $vacount; $i++):
|
||||
if (greater($vaarg($i), result))
|
||||
{
|
||||
result = $vaarg($i);
|
||||
}
|
||||
$endfor;
|
||||
return result;
|
||||
$endif;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,32 +14,35 @@ private const uint UTF16_SURROGATE_HIGH_VALUE = 0xD800;
|
||||
* @param [out] output `the resulting buffer`
|
||||
* @param available `the size available`
|
||||
**/
|
||||
fn usize! char32_to_utf8(Char32 c, char* output, usize available)
|
||||
fn usz! char32_to_utf8(Char32 c, char* output, usz available)
|
||||
{
|
||||
if (!available) return UnicodeResult.CONVERSION_FAILED!;
|
||||
switch (true)
|
||||
{
|
||||
case c < 0x7f:
|
||||
case c <= 0x7f:
|
||||
output[0] = (char)c;
|
||||
return 1;
|
||||
case c < 0x7ff:
|
||||
case c <= 0x7ff:
|
||||
if (available < 2) return UnicodeResult.CONVERSION_FAILED!;
|
||||
output[0] = (char)(0xC0 | c >> 6);
|
||||
output[1] = (char)(0x80 | (c & 0x3F));
|
||||
return 2;
|
||||
case c < 0xffff:
|
||||
case c <= 0xffff:
|
||||
if (available < 3) return UnicodeResult.CONVERSION_FAILED!;
|
||||
output[0] = (char)(0xE0 | c >> 12);
|
||||
output[1] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
output[2] = (char)(0x80 | (c & 0x3F));
|
||||
return 3;
|
||||
default:
|
||||
case c <= 0x10ffff:
|
||||
if (available < 4) return UnicodeResult.CONVERSION_FAILED!;
|
||||
output[0] = (char)(0xF0 | c >> 18);
|
||||
output[1] = (char)(0x80 | (c >> 12 & 0x3F));
|
||||
output[2] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
output[3] = (char)(0x80 | (c & 0x3F));
|
||||
return 4;
|
||||
default:
|
||||
// 0x10FFFF and above is not defined.
|
||||
return UnicodeResult.CONVERSION_FAILED!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +74,7 @@ fn void char32_to_utf16_unsafe(Char32 c, Char16** output)
|
||||
* @param [inout] available `amount of UTF16 data available.`
|
||||
* @param [inout] output `the resulting utf8 buffer to write to.`
|
||||
**/
|
||||
fn void! char16_to_utf8_unsafe(Char16 *ptr, usize *available, char** output)
|
||||
fn void! char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
|
||||
{
|
||||
Char16 high = *ptr;
|
||||
if (high & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE)
|
||||
@@ -128,9 +131,9 @@ fn void char32_to_utf8_unsafe(Char32 c, char** output)
|
||||
* @param [inout] size `Set to max characters to read, set to characters read`
|
||||
* @return `the parsed 32 bit codepoint`
|
||||
**/
|
||||
fn Char32! utf8_to_char32(char* ptr, usize* size)
|
||||
fn Char32! utf8_to_char32(char* ptr, usz* size)
|
||||
{
|
||||
usize max_size = *size;
|
||||
usz max_size = *size;
|
||||
if (max_size < 1) return UnicodeResult.INVALID_UTF8!;
|
||||
char c = (ptr++)[0];
|
||||
|
||||
@@ -144,8 +147,9 @@ fn Char32! utf8_to_char32(char* ptr, usize* size)
|
||||
if (max_size < 2) return UnicodeResult.INVALID_UTF8!;
|
||||
*size = 2;
|
||||
Char32 uc = (c & 0x1F) << 6;
|
||||
c = *ptr;
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
c = *ptr;
|
||||
// Overlong sequence or invalid second.
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
if ((c & 0xF0) == 0xE0)
|
||||
@@ -157,10 +161,12 @@ fn Char32! utf8_to_char32(char* ptr, usize* size)
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
uc += (c & 0x3F) << 6;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
// Overlong sequence or invalid last
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
if (max_size < 4) return UnicodeResult.INVALID_UTF8!;
|
||||
if ((c & 0xF8) != 0xF0) return UnicodeResult.INVALID_UTF8!;
|
||||
*size = 4;
|
||||
Char32 uc = (c & 0x07) << 18;
|
||||
c = ptr++[0];
|
||||
@@ -170,7 +176,8 @@ fn Char32! utf8_to_char32(char* ptr, usize* size)
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
uc += (c & 0x3F) << 6;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
// Overlong sequence or invalid last
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
|
||||
@@ -178,9 +185,9 @@ fn Char32! utf8_to_char32(char* ptr, usize* size)
|
||||
* @param utf8 `An UTF-8 encoded slice of bytes`
|
||||
* @return `the number of encoded code points`
|
||||
**/
|
||||
fn usize utf8_codepoints(char[] utf8)
|
||||
fn usz utf8_codepoints(char[] utf8)
|
||||
{
|
||||
usize len = 0;
|
||||
usz len = 0;
|
||||
foreach (char c : utf8)
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
@@ -193,9 +200,9 @@ fn usize utf8_codepoints(char[] utf8)
|
||||
* @param [in] utf32 `the utf32 data to calculate from`
|
||||
* @return `the length of the resulting UTF8 array`
|
||||
**/
|
||||
fn usize utf8len_for_utf32(Char32[] utf32)
|
||||
fn usz utf8len_for_utf32(Char32[] utf32)
|
||||
{
|
||||
usize len = 0;
|
||||
usz len = 0;
|
||||
foreach (Char32 uc : utf32)
|
||||
{
|
||||
switch (true)
|
||||
@@ -218,11 +225,11 @@ fn usize utf8len_for_utf32(Char32[] utf32)
|
||||
* @param [in] utf16 `the utf16 data to calculate from`
|
||||
* @return `the length of the resulting UTF8 array`
|
||||
**/
|
||||
fn usize utf8len_for_utf16(Char16[] utf16)
|
||||
fn usz utf8len_for_utf16(Char16[] utf16)
|
||||
{
|
||||
usize len = 0;
|
||||
usize len16 = utf16.len;
|
||||
for (usize i = 0; i < len16; i++)
|
||||
usz len = 0;
|
||||
usz len16 = utf16.len;
|
||||
for (usz i = 0; i < len16; i++)
|
||||
{
|
||||
Char16 c = utf16[i];
|
||||
if (c & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE)
|
||||
@@ -250,11 +257,11 @@ fn usize utf8len_for_utf16(Char16[] utf16)
|
||||
* @param utf8 `the utf8 data to calculate from`
|
||||
* @return `the length of the resulting UTF16 array`
|
||||
**/
|
||||
fn usize utf16len_for_utf8(char[] utf8)
|
||||
fn usz utf16len_for_utf8(char[] utf8)
|
||||
{
|
||||
usize len = utf8.len;
|
||||
usize len16 = 0;
|
||||
for (usize i = 0; i < len; i++)
|
||||
usz len = utf8.len;
|
||||
usz len16 = 0;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
len16++;
|
||||
char c = utf8[i];
|
||||
@@ -273,9 +280,9 @@ fn usize utf16len_for_utf8(char[] utf8)
|
||||
* @param [in] utf32 `the UTF32 array to check the length for`
|
||||
* @return `the required length of an UTF16 array to hold the UTF32 data.`
|
||||
**/
|
||||
fn usize utf16len_for_utf32(Char32[] utf32)
|
||||
fn usz utf16len_for_utf32(Char32[] utf32)
|
||||
{
|
||||
usize len = utf32.len;
|
||||
usz len = utf32.len;
|
||||
foreach (Char32 uc : utf32)
|
||||
{
|
||||
if (uc >= UTF16_SURROGATE_OFFSET) len++;
|
||||
@@ -290,13 +297,13 @@ fn usize utf16len_for_utf32(Char32[] utf32)
|
||||
* @param [out] utf8_buffer
|
||||
* @return `the number of bytes written.`
|
||||
**/
|
||||
fn usize! utf32to8(Char32[] utf32, char[] utf8_buffer)
|
||||
fn usz! utf32to8(Char32[] utf32, char[] utf8_buffer)
|
||||
{
|
||||
usize len = utf8_buffer.len;
|
||||
usz len = utf8_buffer.len;
|
||||
char* ptr = utf8_buffer.ptr;
|
||||
foreach (Char32 uc : utf32)
|
||||
{
|
||||
usize used = char32_to_utf8(uc, ptr, len) @inline?;
|
||||
usz used = char32_to_utf8(uc, ptr, len) @inline?;
|
||||
len -= used;
|
||||
ptr += used;
|
||||
}
|
||||
@@ -310,16 +317,16 @@ fn usize! utf32to8(Char32[] utf32, char[] utf8_buffer)
|
||||
* @param [out] utf32_buffer
|
||||
* @return `the number of Char32s written.`
|
||||
**/
|
||||
fn usize! utf8to32(char[] utf8, Char32[] utf32_buffer)
|
||||
fn usz! utf8to32(char[] utf8, Char32[] utf32_buffer)
|
||||
{
|
||||
usize len = utf8.len;
|
||||
usz len = utf8.len;
|
||||
Char32* ptr = utf32_buffer.ptr;
|
||||
usize len32 = 0;
|
||||
usize buf_len = utf32_buffer.len;
|
||||
for (usize i = 0; i < len;)
|
||||
usz len32 = 0;
|
||||
usz buf_len = utf32_buffer.len;
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED!;
|
||||
usize width = len - i;
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
|
||||
i += width;
|
||||
ptr[len32++] = uc;
|
||||
@@ -337,10 +344,10 @@ fn usize! utf8to32(char[] utf8, Char32[] utf32_buffer)
|
||||
**/
|
||||
fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
|
||||
{
|
||||
usize len16 = utf16.len;
|
||||
for (usize i = 0; i < len16;)
|
||||
usz len16 = utf16.len;
|
||||
for (usz i = 0; i < len16;)
|
||||
{
|
||||
usize available = len16 - i;
|
||||
usz available = len16 - i;
|
||||
char16_to_utf8_unsafe(&utf16[i], &available, &utf8_buffer) @inline?;
|
||||
i += available;
|
||||
}
|
||||
@@ -356,10 +363,10 @@ fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
|
||||
**/
|
||||
fn void! utf8to32_unsafe(char[] utf8, Char32* utf32_buffer)
|
||||
{
|
||||
usize len = utf8.len;
|
||||
for (usize i = 0; i < len;)
|
||||
usz len = utf8.len;
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
usize width = len - i;
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
|
||||
i += width;
|
||||
(utf32_buffer++)[0] = uc;
|
||||
@@ -376,10 +383,10 @@ fn void! utf8to32_unsafe(char[] utf8, Char32* utf32_buffer)
|
||||
**/
|
||||
fn void! utf8to16_unsafe(char[] utf8, Char16* utf16_buffer)
|
||||
{
|
||||
usize len = utf8.len;
|
||||
for (usize i = 0; i < len;)
|
||||
usz len = utf8.len;
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
usize width = len - i;
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
|
||||
char32_to_utf16_unsafe(uc, &utf16_buffer) @inline;
|
||||
i += width;
|
||||
|
||||
@@ -52,10 +52,38 @@ enum OsType
|
||||
}
|
||||
|
||||
const OsType OS_TYPE = (OsType)($$OS_TYPE);
|
||||
const bool COMPILER_LIBC_AVAILABLE = $$COMPILER_LIBC_AVAILABLE;
|
||||
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)($$COMPILER_OPT_LEVEL);
|
||||
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
|
||||
const bool I128_SUPPORT = $$PLATFORM_I128_SUPPORTED;
|
||||
const bool F128_SUPPORT = $$PLATFORM_F128_SUPPORTED;
|
||||
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
|
||||
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
|
||||
const bool F128_SUPPORT = $$PLATFORM_F128_SUPPORTED;
|
||||
const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE;
|
||||
const usize TEMP_ALLOCATOR_SIZE = 128 * 1024;
|
||||
const usz LLVM_VERSION = $$LLVM_VERSION;
|
||||
const bool BENCHMARKING = $$BENCHMARKING;
|
||||
const bool TESTING = $$TESTING;
|
||||
const usz TEMP_ALLOCATOR_SIZE = 128 * 1024;
|
||||
|
||||
macro bool os_is_posix()
|
||||
{
|
||||
$switch (OS_TYPE):
|
||||
$case IOS:
|
||||
$case MACOSX:
|
||||
$case NETBSD:
|
||||
$case LINUX:
|
||||
$case KFREEBSD:
|
||||
$case FREEBSD:
|
||||
$case OPENBSD:
|
||||
$case SOLARIS:
|
||||
$case TVOS:
|
||||
$case WATCHOS:
|
||||
return true;
|
||||
$case WIN32:
|
||||
$case WASI:
|
||||
$case EMSCRIPTEN:
|
||||
return false;
|
||||
$default:
|
||||
$echo("Assuming non-Posix environment");
|
||||
return false;
|
||||
$endswitch;
|
||||
}
|
||||
@@ -16,55 +16,71 @@ macro @volatile_store(&x, y)
|
||||
/**
|
||||
* @require math::is_power_of_2(alignment)
|
||||
**/
|
||||
fn usize aligned_offset(usize offset, usize alignment)
|
||||
fn usz aligned_offset(usz offset, usz alignment)
|
||||
{
|
||||
return alignment * ((offset + alignment - 1) / alignment);
|
||||
}
|
||||
|
||||
macro void* aligned_pointer(void* ptr, usize alignment)
|
||||
macro void* aligned_pointer(void* ptr, usz alignment)
|
||||
{
|
||||
return (void*)(uptr)aligned_offset((uptr)ptr, alignment);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require math::is_power_of_2(alignment)
|
||||
**/
|
||||
fn bool ptr_is_aligned(void* ptr, usize alignment) @inline
|
||||
fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
|
||||
{
|
||||
return (uptr)ptr & ((uptr)alignment - 1) == 0;
|
||||
}
|
||||
|
||||
macro void copy(void* dst, void* src, usize len, usize $dst_align = 0, usize $src_align = 0, bool $is_volatile = false)
|
||||
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
{
|
||||
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||
$if ($inlined):
|
||||
$$memset_inline(dst, (char)0, len, $is_volatile, $dst_align);
|
||||
$else:
|
||||
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro void set(void* dst, char val, usize len, usize $dst_align = 0, bool $is_volatile = false)
|
||||
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
{
|
||||
$$memset(dst, val, len, $is_volatile, $dst_align);
|
||||
$if ($inlined):
|
||||
$$memcpy_inline(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||
$else:
|
||||
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro void clear(void* dst, usize len, usize $dst_align = 0, bool $is_volatile = false)
|
||||
macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
|
||||
{
|
||||
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
|
||||
$$memmove(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||
}
|
||||
|
||||
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
{
|
||||
$if ($inlined):
|
||||
$$memset_inline(dst, val, len, $is_volatile, $dst_align);
|
||||
$else:
|
||||
$$memset(dst, val, len, $is_volatile, $dst_align);
|
||||
$endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $typeof(a).kind == TypeKind.SUBARRAY || $typeof(a).kind == TypeKind.POINTER
|
||||
* @require $typeof(b).kind == TypeKind.SUBARRAY || $typeof(b).kind == TypeKind.POINTER
|
||||
* @require $typeof(a).kind != TypeKind.SUBARRAY || len == -1
|
||||
* @require $typeof(a).kind != TypeKind.POINTER || len > -1
|
||||
* @require $typeof(a).kindof == TypeKind.SUBARRAY || $typeof(a).kindof == TypeKind.POINTER
|
||||
* @require $typeof(b).kindof == TypeKind.SUBARRAY || $typeof(b).kindof == TypeKind.POINTER
|
||||
* @require $typeof(a).kindof != TypeKind.SUBARRAY || len == -1
|
||||
* @require $typeof(a).kindof != TypeKind.POINTER || len > -1
|
||||
* @checked (a = b), (b = a)
|
||||
**/
|
||||
macro bool equals(a, b, isize len = -1, usize $align = 0)
|
||||
macro bool equals(a, b, isz len = -1, usz $align = 0)
|
||||
{
|
||||
$if (!$align):
|
||||
$align = $alignof($typeof(a[0]));
|
||||
$align = $typeof(a[0]).alignof;
|
||||
$endif;
|
||||
void* x = void;
|
||||
void* y = void;
|
||||
$if ($typeof(a).kind == TypeKind.SUBARRAY):
|
||||
$if ($typeof(a).kindof == TypeKind.SUBARRAY):
|
||||
len = a.len;
|
||||
if (len != b.len) return false;
|
||||
x = a.ptr;
|
||||
@@ -76,25 +92,26 @@ macro bool equals(a, b, isize len = -1, usize $align = 0)
|
||||
$endif;
|
||||
|
||||
if (!len) return true;
|
||||
var $Type;
|
||||
$switch ($align):
|
||||
$case 1:
|
||||
var $Type = char;
|
||||
$Type = char;
|
||||
$case 2:
|
||||
var $Type = ushort;
|
||||
$Type = ushort;
|
||||
$case 4:
|
||||
var $Type = uint;
|
||||
$Type = uint;
|
||||
$case 8:
|
||||
$default:
|
||||
var $Type = ulong;
|
||||
$Type = ulong;
|
||||
$endswitch;
|
||||
var $step = $Type.sizeof;
|
||||
usize end = len / $step;
|
||||
for (usize i = 0; i < end; i++)
|
||||
usz end = len / $step;
|
||||
for (usz i = 0; i < end; i++)
|
||||
{
|
||||
if ((($Type*)x)[i] != (($Type*)y)[i]) return false;
|
||||
}
|
||||
usize last = len % $align;
|
||||
for (usize i = len - last; i < len; i++)
|
||||
usz last = len % $align;
|
||||
for (usz i = len - last; i < len; i++)
|
||||
{
|
||||
if (((char*)x)[i] != ((char*)y)[i]) return false;
|
||||
}
|
||||
@@ -115,12 +132,12 @@ macro @tclone(&value) @builtin
|
||||
return x;
|
||||
}
|
||||
|
||||
fn void* malloc(usize size) @builtin @inline
|
||||
fn void* malloc(usz size) @builtin @inline
|
||||
{
|
||||
return thread_allocator.alloc(size)!!;
|
||||
}
|
||||
|
||||
fn void*! malloc_checked(usize size) @builtin @inline
|
||||
fn void*! malloc_checked(usz size) @builtin @inline
|
||||
{
|
||||
return thread_allocator.alloc(size);
|
||||
}
|
||||
@@ -128,12 +145,12 @@ fn void*! malloc_checked(usize size) @builtin @inline
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! malloc_aligned(usize size, usize alignment) @builtin @inline
|
||||
fn void*! malloc_aligned(usz size, usz alignment) @builtin @inline
|
||||
{
|
||||
return thread_allocator.alloc_aligned(size, alignment);
|
||||
}
|
||||
|
||||
fn char[] alloc_bytes(usize bytes) @inline
|
||||
fn char[] alloc_bytes(usz bytes) @inline
|
||||
{
|
||||
return ((char*)thread_allocator.alloc(bytes))[:bytes]!!;
|
||||
}
|
||||
@@ -144,12 +161,12 @@ macro alloc($Type)
|
||||
}
|
||||
|
||||
|
||||
fn void* calloc(usize size) @builtin @inline
|
||||
fn void* calloc(usz size) @builtin @inline
|
||||
{
|
||||
return thread_allocator.calloc(size)!!;
|
||||
}
|
||||
|
||||
fn void*! calloc_checked(usize size) @builtin @inline
|
||||
fn void*! calloc_checked(usz size) @builtin @inline
|
||||
{
|
||||
return thread_allocator.calloc(size);
|
||||
}
|
||||
@@ -157,17 +174,17 @@ fn void*! calloc_checked(usize size) @builtin @inline
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! calloc_aligned(usize size, usize alignment) @builtin @inline
|
||||
fn void*! calloc_aligned(usz size, usz alignment) @builtin @inline
|
||||
{
|
||||
return thread_allocator.calloc_aligned(size, alignment);
|
||||
}
|
||||
|
||||
fn void* realloc(void *ptr, usize new_size) @builtin @inline
|
||||
fn void* realloc(void *ptr, usz new_size) @builtin @inline
|
||||
{
|
||||
return thread_allocator.realloc(ptr, new_size)!!;
|
||||
}
|
||||
|
||||
fn void*! realloc_checked(void *ptr, usize new_size) @builtin @inline
|
||||
fn void*! realloc_checked(void *ptr, usz new_size) @builtin @inline
|
||||
{
|
||||
return thread_allocator.realloc(ptr, new_size);
|
||||
}
|
||||
@@ -175,7 +192,7 @@ fn void*! realloc_checked(void *ptr, usize new_size) @builtin @inline
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! realloc_aligned(void *ptr, usize new_size, usize alignment) @builtin @inline
|
||||
fn void*! realloc_aligned(void *ptr, usz new_size, usz alignment) @builtin @inline
|
||||
{
|
||||
return thread_allocator.realloc_aligned(ptr, new_size, alignment);
|
||||
}
|
||||
@@ -201,33 +218,23 @@ macro void @scoped(Allocator* allocator; @body())
|
||||
@body();
|
||||
}
|
||||
|
||||
macro void @tscoped(;@body())
|
||||
{
|
||||
Allocator* old_allocator = thread_allocator;
|
||||
TempAllocator* temp = temp_allocator();
|
||||
usize mark = temp.mark()!!;
|
||||
thread_allocator = temp;
|
||||
defer temp.reset(mark);
|
||||
defer thread_allocator = old_allocator;
|
||||
@body();
|
||||
}
|
||||
|
||||
macro talloc($Type) @builtin
|
||||
{
|
||||
return temp_allocator().alloc_aligned($Type.sizeof, $alignof($Type))!!;
|
||||
return temp_allocator().alloc_aligned($Type.sizeof, $Type.alignof)!!;
|
||||
}
|
||||
|
||||
fn void* tmalloc(usize size, usize alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
fn void* tmalloc(usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
{
|
||||
return temp_allocator().alloc_aligned(size, alignment)!!;
|
||||
}
|
||||
|
||||
fn void* tcalloc(usize size, usize alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
fn void* tcalloc(usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
{
|
||||
return temp_allocator().calloc_aligned(size, alignment)!!;
|
||||
}
|
||||
|
||||
fn void* trealloc(void* ptr, usize size, usize alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
fn void* trealloc(void* ptr, usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
{
|
||||
return temp_allocator().realloc_aligned(ptr, size, alignment)!!;
|
||||
}
|
||||
@@ -235,7 +242,7 @@ fn void* trealloc(void* ptr, usize size, usize alignment = allocator::DEFAULT_ME
|
||||
macro void @pool(;@body) @builtin
|
||||
{
|
||||
TempAllocator* temp = temp_allocator();
|
||||
usize mark = temp.used;
|
||||
usz mark = temp.used;
|
||||
defer temp.reset(mark);
|
||||
@body();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
module std::core::mem::allocator;
|
||||
|
||||
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
|
||||
const DEFAULT_MEM_ALIGNMENT = $alignof(void*) * 2;
|
||||
const DEFAULT_SIZE_PREFIX = usize.sizeof;
|
||||
const DEFAULT_SIZE_PREFIX_ALIGNMENT = $alignof(usize);
|
||||
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
|
||||
const DEFAULT_SIZE_PREFIX = usz.sizeof;
|
||||
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
|
||||
|
||||
const Allocator* NULL_ALLOCATOR = &_NULL_ALLOCATOR;
|
||||
const Allocator* LIBC_ALLOCATOR = &_SYSTEM_ALLOCATOR;
|
||||
|
||||
define AllocatorFunction = fn void*!(Allocator* allocator, usize new_size, usize alignment, usize offset, void* old_pointer, AllocationKind kind);
|
||||
define AllocatorFunction = fn void*!(Allocator* allocator, usz new_size, usz alignment, usz offset, void* old_pointer, AllocationKind kind);
|
||||
|
||||
struct Allocator
|
||||
{
|
||||
@@ -38,7 +38,7 @@ fault AllocationFailure
|
||||
|
||||
|
||||
|
||||
fn void*! Allocator.alloc(Allocator* allocator, usize size) @inline
|
||||
fn void*! Allocator.alloc(Allocator* allocator, usz size) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, 0, 0, null, ALLOC);
|
||||
}
|
||||
@@ -46,12 +46,12 @@ fn void*! Allocator.alloc(Allocator* allocator, usize size) @inline
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! Allocator.alloc_aligned(Allocator* allocator, usize size, usize alignment, usize offset = 0) @inline
|
||||
fn void*! Allocator.alloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, alignment, offset, null, ALIGNED_ALLOC);
|
||||
}
|
||||
|
||||
fn void*! Allocator.realloc(Allocator* allocator, void* old_pointer, usize size) @inline
|
||||
fn void*! Allocator.realloc(Allocator* allocator, void* old_pointer, usz size) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, 0, 0, old_pointer, REALLOC);
|
||||
}
|
||||
@@ -59,18 +59,18 @@ fn void*! Allocator.realloc(Allocator* allocator, void* old_pointer, usize size)
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! Allocator.realloc_aligned(Allocator* allocator, void* old_pointer, usize size, usize alignment, usize offset = 0) @inline
|
||||
fn void*! Allocator.realloc_aligned(Allocator* allocator, void* old_pointer, usz size, usz alignment, usz offset = 0) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, alignment, offset, old_pointer, ALIGNED_REALLOC);
|
||||
}
|
||||
|
||||
fn usize! Allocator.mark(Allocator* allocator) @inline
|
||||
fn usz! Allocator.mark(Allocator* allocator) @inline
|
||||
{
|
||||
return (usize)(uptr)allocator.function(allocator, 0, 0, 0, null, MARK);
|
||||
return (usz)(uptr)allocator.function(allocator, 0, 0, 0, null, MARK);
|
||||
}
|
||||
|
||||
|
||||
fn void*! Allocator.calloc(Allocator* allocator, usize size) @inline
|
||||
fn void*! Allocator.calloc(Allocator* allocator, usz size) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, 0, 0, null, CALLOC);
|
||||
}
|
||||
@@ -78,7 +78,7 @@ fn void*! Allocator.calloc(Allocator* allocator, usize size) @inline
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! Allocator.calloc_aligned(Allocator* allocator, usize size, usize alignment, usize offset = 0) @inline
|
||||
fn void*! Allocator.calloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, alignment, offset, null, ALIGNED_CALLOC);
|
||||
}
|
||||
@@ -93,12 +93,12 @@ fn void! Allocator.free_aligned(Allocator* allocator, void* old_pointer) @inline
|
||||
allocator.function(allocator, 0, 0, 0, old_pointer, ALIGNED_FREE)?;
|
||||
}
|
||||
|
||||
fn void Allocator.reset(Allocator* allocator, usize mark = 0)
|
||||
fn void Allocator.reset(Allocator* allocator, usz mark = 0)
|
||||
{
|
||||
allocator.function(allocator, mark, 0, 0, null, RESET)!!;
|
||||
}
|
||||
|
||||
private fn usize alignment_for_allocation(usize alignment) @inline
|
||||
private fn usz alignment_for_allocation(usz alignment) @inline
|
||||
{
|
||||
if (alignment < DEFAULT_MEM_ALIGNMENT)
|
||||
{
|
||||
@@ -113,14 +113,14 @@ struct DynamicArenaAllocator
|
||||
Allocator* backing_allocator;
|
||||
DynamicArenaPage* page;
|
||||
DynamicArenaPage* unused_page;
|
||||
usize page_size;
|
||||
usz page_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require page_size >= 128
|
||||
* @require this != null
|
||||
**/
|
||||
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usize page_size, Allocator* backing_allocator = mem::current_allocator())
|
||||
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usz page_size, Allocator* backing_allocator = mem::current_allocator())
|
||||
{
|
||||
this.function = &dynamic_arena_allocator_function;
|
||||
this.page = null;
|
||||
@@ -157,7 +157,7 @@ struct ArenaAllocator
|
||||
{
|
||||
inline Allocator allocator;
|
||||
char[] data;
|
||||
usize used;
|
||||
usz used;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,37 +4,37 @@
|
||||
module std::core::mem::array;
|
||||
|
||||
/**
|
||||
* @require usize.max / elements > $Type.sizeof
|
||||
* @require usz.max / elements > $Type.sizeof
|
||||
**/
|
||||
macro alloc($Type, usize elements)
|
||||
macro alloc($Type, usz elements)
|
||||
{
|
||||
$Type* ptr = malloc($Type.sizeof * elements);
|
||||
return ptr[:elements];
|
||||
}
|
||||
|
||||
/**
|
||||
* @require usize.max / elements > $Type.sizeof
|
||||
* @require usz.max / elements > $Type.sizeof
|
||||
**/
|
||||
macro talloc($Type, usize elements)
|
||||
macro talloc($Type, usz elements)
|
||||
{
|
||||
$Type* ptr = tmalloc($Type.sizeof * elements, $alignof($Type[1]));
|
||||
$Type* ptr = tmalloc($Type.sizeof * elements, $Type[1].alignof);
|
||||
return ptr[:elements];
|
||||
}
|
||||
|
||||
/**
|
||||
* @require (usize.max / elements > $Type.sizeof)
|
||||
* @require (usz.max / elements > $Type.sizeof)
|
||||
**/
|
||||
macro make($Type, usize elements)
|
||||
macro make($Type, usz elements, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
$Type* ptr = calloc($sizeof($Type) * elements);
|
||||
$Type* ptr = allocator.calloc($Type.sizeof * elements)!!;
|
||||
return ptr[:elements];
|
||||
}
|
||||
|
||||
/**
|
||||
* @require (usize.max / elements > $Type.sizeof)
|
||||
* @require (usz.max / elements > $Type.sizeof)
|
||||
**/
|
||||
macro tmake($Type, usize elements)
|
||||
macro tmake($Type, usz elements)
|
||||
{
|
||||
$Type* ptr = tcalloc($sizeof($Type) * elements, $alignof($Type[1]));
|
||||
$Type* ptr = tcalloc($Type.sizeof * elements, $Type[1].alignof);
|
||||
return ptr[:elements];
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
module std::core::os::linux;
|
||||
|
||||
$if (env::OS_TYPE == OsType.LINUX):
|
||||
|
||||
extern fn int* __errno_location();
|
||||
|
||||
fn int errno() @inline
|
||||
{
|
||||
return *__errno_location();
|
||||
}
|
||||
|
||||
fn void errno_set(int err)
|
||||
{
|
||||
*(__errno_location()) = err;
|
||||
}
|
||||
|
||||
$endif;
|
||||
@@ -1,14 +0,0 @@
|
||||
module std::core::os::macos;
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
|
||||
extern fn int* __error();
|
||||
|
||||
fn int errno() @inline
|
||||
{
|
||||
return *__error();
|
||||
}
|
||||
fn void errno_set(int err)
|
||||
{
|
||||
*(__error()) = err;
|
||||
}
|
||||
$endif;
|
||||
@@ -1,10 +0,0 @@
|
||||
module std::core::os::windows;
|
||||
|
||||
$if (env::OS_TYPE == OsType.WIN32):
|
||||
|
||||
extern fn int getLastError() @stdcall @extname("GetLastError");
|
||||
fn int errno() @inline
|
||||
{
|
||||
return getLastError();
|
||||
}
|
||||
$endif;
|
||||
@@ -11,10 +11,17 @@ private const uint SURROGATE_BITS = 10;
|
||||
private const uint SURROGATE_LOW_VALUE = 0xDC00;
|
||||
private const uint SURROGATE_HIGH_VALUE = 0xD800;
|
||||
|
||||
fault NumberConversion
|
||||
{
|
||||
EMPTY_STRING,
|
||||
NEGATIVE_VALUE,
|
||||
MALFORMED_INTEGER,
|
||||
INTEGER_OVERFLOW,
|
||||
}
|
||||
fn String join(char[][] s, char[] joiner)
|
||||
{
|
||||
if (!s.len) return (String)null;
|
||||
usize total_size = joiner.len * s.len;
|
||||
usz total_size = joiner.len * s.len;
|
||||
foreach (char[]* &str : s)
|
||||
{
|
||||
total_size += str.len;
|
||||
@@ -29,20 +36,163 @@ fn String join(char[][] s, char[] joiner)
|
||||
return res;
|
||||
}
|
||||
|
||||
fn usize! str_index_of(char[] s, char[] needle)
|
||||
macro bool char_in_set(char c, char[] set)
|
||||
{
|
||||
usize match = 0;
|
||||
usize needed = needle.len;
|
||||
foreach (ch : set)
|
||||
{
|
||||
if (ch == c) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private macro char_is_space_tab(char c)
|
||||
{
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
||||
private macro to_signed_integer($Type, char[] string)
|
||||
{
|
||||
usz len = string.len;
|
||||
usz index = 0;
|
||||
char* ptr = string.ptr;
|
||||
while (index < len && char_is_space_tab(ptr[index])) index++;
|
||||
if (len == index) return NumberConversion.EMPTY_STRING!;
|
||||
bool is_negative;
|
||||
switch (string[index])
|
||||
{
|
||||
case '-':
|
||||
if ($Type.min == 0) return NumberConversion.NEGATIVE_VALUE!;
|
||||
is_negative = true;
|
||||
index++;
|
||||
case '+':
|
||||
index++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (len == index) return NumberConversion.MALFORMED_INTEGER!;
|
||||
$Type base = 10;
|
||||
if (string[index] == '0')
|
||||
{
|
||||
index++;
|
||||
if (index == len) return ($Type)0;
|
||||
switch (string[index])
|
||||
{
|
||||
case 'x':
|
||||
case 'X':
|
||||
base = 16;
|
||||
index++;
|
||||
case 'b':
|
||||
case 'B':
|
||||
base = 2;
|
||||
index++;
|
||||
case 'o':
|
||||
case 'O':
|
||||
base = 8;
|
||||
index++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (len == index) return NumberConversion.MALFORMED_INTEGER!;
|
||||
}
|
||||
$Type value = 0;
|
||||
while (index != len)
|
||||
{
|
||||
char c = {|
|
||||
char ch = string[index++];
|
||||
if (base != 16 || ch < 'A') return (char)(ch - '0');
|
||||
if (ch <= 'F') return (char)(ch - 'A');
|
||||
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER!;
|
||||
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER!;
|
||||
return (char)(ch - 'a');
|
||||
|}?;
|
||||
if (c >= base) return NumberConversion.MALFORMED_INTEGER!;
|
||||
value = {|
|
||||
if (is_negative)
|
||||
{
|
||||
$Type new_value = value * base - c;
|
||||
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW!;
|
||||
return new_value;
|
||||
}
|
||||
$Type new_value = value * base + c;
|
||||
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW!;
|
||||
return new_value;
|
||||
|}?;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
fn int128! to_int128(char[] string) = to_signed_integer(int128, string);
|
||||
fn long! to_long(char[] string) = to_signed_integer(long, string);
|
||||
fn int! to_int(char[] string) = to_signed_integer(int, string);
|
||||
fn short! to_short(char[] string) = to_signed_integer(short, string);
|
||||
fn ichar! to_ichar(char[] string) = to_signed_integer(ichar, string);
|
||||
|
||||
fn char[] trim(char[] string, char[] to_trim = "\t\n\r ")
|
||||
{
|
||||
usz start = 0;
|
||||
usz len = string.len;
|
||||
while (start < len && char_in_set(string[start], to_trim)) start++;
|
||||
if (start == len) return string[:0];
|
||||
usz end = len - 1;
|
||||
while (end > start && char_in_set(string[end], to_trim)) end--;
|
||||
return string[start..end];
|
||||
}
|
||||
|
||||
fn bool starts_with(char[] s, char[] needle)
|
||||
{
|
||||
if (needle.len > s.len) return false;
|
||||
foreach (i, c : needle)
|
||||
{
|
||||
if (c != s[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn char[][] tsplit(char[] s, char[] needle) = split(s, needle, mem::temp_allocator()) @inline;
|
||||
|
||||
fn char[][] split(char[] s, char[] needle, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
usz capacity = 16;
|
||||
usz i = 0;
|
||||
char[]* holder = allocator.alloc(char[].sizeof * capacity)!!;
|
||||
while (s.len)
|
||||
{
|
||||
usz! index = index_of(s, needle);
|
||||
char[] res = void;
|
||||
if (try index)
|
||||
{
|
||||
res = s[:index];
|
||||
s = s[index + needle.len..];
|
||||
}
|
||||
else
|
||||
{
|
||||
res = s;
|
||||
s = s[:0];
|
||||
}
|
||||
if (i == capacity)
|
||||
{
|
||||
capacity *= 2;
|
||||
holder = allocator.realloc(holder, char[].sizeof * capacity)!!;
|
||||
}
|
||||
holder[i++] = res;
|
||||
}
|
||||
return holder[:i];
|
||||
}
|
||||
|
||||
fn usz! index_of(char[] s, char[] needle)
|
||||
{
|
||||
usz match = 0;
|
||||
usz needed = needle.len;
|
||||
if (!needed) return SearchResult.MISSING!;
|
||||
usize index_start = 0;
|
||||
usz index_start = 0;
|
||||
char search = needle[0];
|
||||
foreach (usize i, char c : s)
|
||||
foreach (usz i, char c : s)
|
||||
{
|
||||
if (c == search)
|
||||
{
|
||||
if (!match) index_start = i;
|
||||
match++;
|
||||
if (match == needed) return i;
|
||||
if (match == needed) return index_start;
|
||||
search = needle[match];
|
||||
continue;
|
||||
}
|
||||
@@ -55,22 +205,27 @@ fn usize! str_index_of(char[] s, char[] needle)
|
||||
return SearchResult.MISSING!;
|
||||
}
|
||||
|
||||
fn ZString copy_zstring(char[] s)
|
||||
fn ZString copy_zstring(char[] s, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
usize len = s.len;
|
||||
char* str = malloc(len + 1);
|
||||
usz len = s.len;
|
||||
char* str = allocator.alloc(len + 1)!!;
|
||||
mem::copy(str, s.ptr, len);
|
||||
str[len] = 0;
|
||||
return (ZString)str;
|
||||
}
|
||||
|
||||
fn ZString tcopy_zstring(char[] s)
|
||||
fn char[] copyz(char[] s, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
usize len = s.len;
|
||||
char* str = tmalloc(len + 1);
|
||||
usz len = s.len;
|
||||
char* str = allocator.alloc(len + 1)!!;
|
||||
mem::copy(str, s.ptr, len);
|
||||
str[len] = 0;
|
||||
return (ZString)str;
|
||||
return str[:len];
|
||||
}
|
||||
|
||||
fn ZString tcopy_zstring(char[] s)
|
||||
{
|
||||
return copy_zstring(s, mem::temp_allocator());
|
||||
}
|
||||
|
||||
fn bool compare(char[] a, char[] b)
|
||||
@@ -89,11 +244,9 @@ fault UnicodeResult
|
||||
CONVERSION_FAILED,
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn usize utf8_codepoints(char[] utf8)
|
||||
fn usz utf8_codepoints(char[] utf8)
|
||||
{
|
||||
usize len = 0;
|
||||
usz len = 0;
|
||||
foreach (char c : utf8)
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
@@ -103,83 +256,89 @@ fn usize utf8_codepoints(char[] utf8)
|
||||
|
||||
fn Char32[]! utf8to32(char[] utf8, Allocator* allocator = mem::current_allocator)
|
||||
{
|
||||
usize codepoints = conv::utf8_codepoints(utf8);
|
||||
usz codepoints = conv::utf8_codepoints(utf8);
|
||||
Char32* data = allocator.alloc(Char32.sizeof * (codepoints + 1))?;
|
||||
conv::utf8to32_unsafe(utf8, data)?;
|
||||
data[codepoints] = 0;
|
||||
return data[0..codepoints - 1];
|
||||
return data[:codepoints];
|
||||
}
|
||||
|
||||
fn char[] utf32to8(Char32[] utf32, Allocator* allocator = mem::current_allocator)
|
||||
{
|
||||
usize len = conv::utf8len_for_utf32(utf32);
|
||||
usz len = conv::utf8len_for_utf32(utf32);
|
||||
char* data = allocator.alloc(len + 1)!!;
|
||||
conv::utf32to8_unsafe(utf32, data);
|
||||
data[len] = 0;
|
||||
return data[0..len - 1];
|
||||
return data[:len];
|
||||
}
|
||||
|
||||
fn Char16[]! utf8to16(char[] utf8, Allocator* allocator = mem::current_allocator)
|
||||
{
|
||||
usize len16 = conv::utf16len_for_utf8(utf8);
|
||||
usz len16 = conv::utf16len_for_utf8(utf8);
|
||||
Char16* data = allocator.alloc((len16 + 1) * Char16.sizeof)?;
|
||||
conv::utf8to16_unsafe(utf8, data)?;
|
||||
data[len16] = 0;
|
||||
return data[0..len16 - 1];
|
||||
return data[:len16];
|
||||
}
|
||||
|
||||
|
||||
fn char[]! utf16to8(Char16[] utf16, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
usize len = conv::utf8len_for_utf16(utf16);
|
||||
usz len = conv::utf8len_for_utf16(utf16);
|
||||
char* data = allocator.alloc(len + 1)?;
|
||||
conv::utf16to8_unsafe(utf16, data)?;
|
||||
return data[0 .. len - 1];
|
||||
return data[:len];
|
||||
}
|
||||
|
||||
fn char[] copy(char[] s)
|
||||
fn char[] copy(char[] s, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
usize len = s.len;
|
||||
ZString str_copy = copy_zstring(s) @inline;
|
||||
return str_copy[..len];
|
||||
usz len = s.len;
|
||||
ZString str_copy = copy_zstring(s, allocator) @inline;
|
||||
return str_copy[:len];
|
||||
}
|
||||
|
||||
fn char[] tcopy(char[] s)
|
||||
{
|
||||
usize len = s.len;
|
||||
usz len = s.len;
|
||||
ZString str_copy = tcopy_zstring(s) @inline;
|
||||
return str_copy[..len];
|
||||
return str_copy[:len];
|
||||
}
|
||||
|
||||
fn char[] tconcat(char[] s1, char[] s2)
|
||||
{
|
||||
usize full_len = s1.len + s2.len;
|
||||
usz full_len = s1.len + s2.len;
|
||||
char* str = tmalloc(full_len + 1);
|
||||
usize s1_len = s1.len;
|
||||
usz s1_len = s1.len;
|
||||
mem::copy(str, s1.ptr, s1_len);
|
||||
mem::copy(str + s1_len, s2.ptr, s2.len);
|
||||
str[full_len] = 0;
|
||||
return str[..full_len];
|
||||
return str[:full_len];
|
||||
}
|
||||
|
||||
fn char[] concat(char[] s1, char[] s2)
|
||||
{
|
||||
usize full_len = s1.len + s2.len;
|
||||
usz full_len = s1.len + s2.len;
|
||||
char* str = malloc(full_len + 1);
|
||||
usize s1_len = s1.len;
|
||||
usz s1_len = s1.len;
|
||||
mem::copy(str, s1.ptr, s1_len);
|
||||
mem::copy(str + s1_len, s2.ptr, s2.len);
|
||||
str[full_len] = 0;
|
||||
return str[..full_len];
|
||||
return str[:full_len];
|
||||
}
|
||||
|
||||
fn usize ZString.len(ZString *str)
|
||||
fn char[] ZString.as_str(ZString str)
|
||||
{
|
||||
usize len = 0;
|
||||
char* ptr = (char*)*str;
|
||||
return ((char*)str)[:str.len()];
|
||||
}
|
||||
|
||||
fn usz ZString.len(ZString str)
|
||||
{
|
||||
usz len = 0;
|
||||
char* ptr = (char*)str;
|
||||
while (char c = ptr++[0])
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@ define String = distinct void*;
|
||||
private struct StringData
|
||||
{
|
||||
Allocator* allocator;
|
||||
usize len;
|
||||
usize capacity;
|
||||
usz len;
|
||||
usz capacity;
|
||||
char[*] chars;
|
||||
}
|
||||
|
||||
const usize MIN_CAPACITY = 16;
|
||||
const usz MIN_CAPACITY = 16;
|
||||
|
||||
fn String new_with_capacity(usize capacity, Allocator* allocator = mem::current_allocator())
|
||||
fn String new_with_capacity(usz capacity, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
|
||||
StringData* data = allocator.alloc(StringData.sizeof + capacity)!!;
|
||||
@@ -25,7 +25,7 @@ fn String new_with_capacity(usize capacity, Allocator* allocator = mem::current_
|
||||
|
||||
fn String new(char[] c)
|
||||
{
|
||||
usize len = c.len;
|
||||
usz len = c.len;
|
||||
String str = new_with_capacity(len);
|
||||
StringData* data = str.data();
|
||||
if (len)
|
||||
@@ -52,7 +52,7 @@ fn ZString String.zstr(String str)
|
||||
return (ZString)&data.chars[0];
|
||||
}
|
||||
|
||||
fn usize String.len(String this)
|
||||
fn usz String.len(String this)
|
||||
{
|
||||
if (!this) return 0;
|
||||
return this.data().len;
|
||||
@@ -61,7 +61,7 @@ fn usize String.len(String this)
|
||||
/**
|
||||
* @require new_size <= this.len()
|
||||
*/
|
||||
fn void String.chop(String this, usize new_size)
|
||||
fn void String.chop(String this, usz new_size)
|
||||
{
|
||||
if (!this) return;
|
||||
this.data().len = new_size;
|
||||
@@ -86,17 +86,17 @@ fn void String.append_utf32(String* str, Char32[] chars)
|
||||
/**
|
||||
* @require index < str.len()
|
||||
**/
|
||||
fn void String.set(String str, usize index, char c)
|
||||
fn void String.set(String str, usz index, char c)
|
||||
{
|
||||
str.data().chars[index] = c;
|
||||
}
|
||||
|
||||
fn void String.append_repeat(String* str, char c, usize times)
|
||||
fn void String.append_repeat(String* str, char c, usz times)
|
||||
{
|
||||
if (times == 0) return;
|
||||
str.reserve(times);
|
||||
StringData* data = str.data();
|
||||
for (usize i = 0; i < times; i++)
|
||||
for (usz i = 0; i < times; i++)
|
||||
{
|
||||
data.chars[data.len++] = c;
|
||||
}
|
||||
@@ -139,6 +139,8 @@ fn void String.append_char32(String* str, Char32 c)
|
||||
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
|
||||
}
|
||||
|
||||
fn String String.tcopy(String* str) = str.copy(mem::temp_allocator());
|
||||
|
||||
fn String String.copy(String* str, Allocator* allocator = null)
|
||||
{
|
||||
if (!str)
|
||||
@@ -155,7 +157,7 @@ fn String String.copy(String* str, Allocator* allocator = null)
|
||||
|
||||
fn ZString String.copy_zstr(String* str, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
usize str_len = str.len();
|
||||
usz str_len = str.len();
|
||||
if (!str_len)
|
||||
{
|
||||
return (ZString)allocator.calloc(1)!!;
|
||||
@@ -172,6 +174,8 @@ fn char[] String.copy_str(String* str, Allocator* allocator = mem::current_alloc
|
||||
return str.copy_zstr(allocator)[:str.len()];
|
||||
}
|
||||
|
||||
fn char[] String.tcopy_str(String* str) = str.copy_str(mem::temp_allocator()) @inline;
|
||||
|
||||
fn bool String.equals(String str, String other_string)
|
||||
{
|
||||
StringData *str1 = str.data();
|
||||
@@ -179,7 +183,7 @@ fn bool String.equals(String str, String other_string)
|
||||
if (str1 == str2) return true;
|
||||
if (!str1) return str2.len == 0;
|
||||
if (!str2) return str1.len == 0;
|
||||
usize str1_len = str1.len;
|
||||
usz str1_len = str1.len;
|
||||
if (str1_len != str2.len) return false;
|
||||
for (int i = 0; i < str1_len; i++)
|
||||
{
|
||||
@@ -204,8 +208,8 @@ fn bool String.less(String str, String other_string)
|
||||
if (str1 == str2) return false;
|
||||
if (!str1) return str2.len != 0;
|
||||
if (!str2) return str1.len == 0;
|
||||
usize str1_len = str1.len;
|
||||
usize str2_len = str2.len;
|
||||
usz str1_len = str1.len;
|
||||
usz str2_len = str2.len;
|
||||
if (str1_len != str2_len) return str1_len < str2_len;
|
||||
for (int i = 0; i < str1_len; i++)
|
||||
{
|
||||
@@ -216,7 +220,7 @@ fn bool String.less(String str, String other_string)
|
||||
|
||||
fn void String.append_chars(String* this, char[] str)
|
||||
{
|
||||
usize other_len = str.len;
|
||||
usz other_len = str.len;
|
||||
if (!other_len) return;
|
||||
if (!*this)
|
||||
{
|
||||
@@ -267,9 +271,9 @@ macro void String.append(String* str, value)
|
||||
$case Char32:
|
||||
str.append_char32(value);
|
||||
$default:
|
||||
$if ($convertible($Type, Char32)):
|
||||
$if (@convertible($Type, Char32)):
|
||||
str.append_char32(value);
|
||||
$elif ($convertible($Type, char[])):
|
||||
$elif (@convertible($Type, char[])):
|
||||
str.append_chars(value);
|
||||
$else:
|
||||
$assert("Unsupported type for appending");
|
||||
@@ -283,7 +287,7 @@ private fn StringData* String.data(String str) @inline
|
||||
return (StringData*)str;
|
||||
}
|
||||
|
||||
private fn void String.reserve(String* str, usize addition)
|
||||
private fn void String.reserve(String* str, usz addition)
|
||||
{
|
||||
StringData* data = str.data();
|
||||
if (!data)
|
||||
@@ -291,9 +295,17 @@ private fn void String.reserve(String* str, usize addition)
|
||||
*str = string::new_with_capacity(addition);
|
||||
return;
|
||||
}
|
||||
usize len = data.len + addition;
|
||||
usz len = data.len + addition;
|
||||
if (data.capacity >= len) return;
|
||||
usize new_capacity = data.capacity * 2;
|
||||
usz new_capacity = data.capacity *= 2;
|
||||
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
|
||||
*str = (String)data.allocator.realloc(data, StringData.sizeof + new_capacity)!!;
|
||||
}
|
||||
|
||||
fn String String.new_concat(String a, String b, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
String string = new_with_capacity(a.len() + b.len(), allocator);
|
||||
string.append(a);
|
||||
string.append(b);
|
||||
return string;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
module std::core::string::iterator;
|
||||
|
||||
|
||||
|
||||
struct StringIterator
|
||||
{
|
||||
char[] utf8;
|
||||
usize current;
|
||||
usz current;
|
||||
}
|
||||
|
||||
fn void StringIterator.reset(StringIterator* this)
|
||||
@@ -15,10 +13,10 @@ fn void StringIterator.reset(StringIterator* this)
|
||||
|
||||
fn Char32! StringIterator.next(StringIterator* this)
|
||||
{
|
||||
usize len = this.utf8.len;
|
||||
usize current = this.current;
|
||||
usz len = this.utf8.len;
|
||||
usz current = this.current;
|
||||
if (current >= len) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
usize read = (len - current < 4 ? len - current : 4);
|
||||
usz read = (len - current < 4 ? len - current : 4);
|
||||
Char32 res = conv::utf8_to_char32(&this.utf8[current], &read)?;
|
||||
this.current += read;
|
||||
return res;
|
||||
|
||||
@@ -8,18 +8,18 @@ fault ConversionResult
|
||||
VALUE_OUT_OF_UNSIGNED_RANGE,
|
||||
}
|
||||
/**
|
||||
* @require $Type.kind.is_int() || $Type.kind == TypeKind.ENUM "Argument was not an integer"
|
||||
* @require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
|
||||
**/
|
||||
macro variant_to_int(variant v, $Type)
|
||||
{
|
||||
typeid variant_type = v.type;
|
||||
TypeKind kind = variant_type.kind;
|
||||
TypeKind kind = variant_type.kindof;
|
||||
if (kind == TypeKind.ENUM)
|
||||
{
|
||||
variant_type = variant_type.inner;
|
||||
kind = variant_type.kind;
|
||||
kind = variant_type.kindof;
|
||||
}
|
||||
bool is_mixed_signed = $Type.kind != variant_type.kind;
|
||||
bool is_mixed_signed = $Type.kindof != variant_type.kindof;
|
||||
$Type max = $Type.max;
|
||||
$Type min = $Type.min;
|
||||
switch (variant_type)
|
||||
@@ -44,14 +44,10 @@ macro variant_to_int(variant v, $Type)
|
||||
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
return ($Type)l;
|
||||
case int128:
|
||||
$if (env::I128_SUPPORT):
|
||||
int128 i = *(int128*)v.ptr;
|
||||
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
return ($Type)i;
|
||||
$else:
|
||||
unreachable();
|
||||
$endif;
|
||||
int128 i = *(int128*)v.ptr;
|
||||
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
return ($Type)i;
|
||||
case char:
|
||||
char c = *(char*)v.ptr;
|
||||
if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
@@ -69,13 +65,9 @@ macro variant_to_int(variant v, $Type)
|
||||
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
return ($Type)l;
|
||||
case uint128:
|
||||
$if (env::I128_SUPPORT):
|
||||
uint128 i = *(uint128*)v.ptr;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
return ($Type)i;
|
||||
$else:
|
||||
unreachable();
|
||||
$endif;
|
||||
uint128 i = *(uint128*)v.ptr;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
return ($Type)i;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
@@ -83,7 +75,7 @@ macro variant_to_int(variant v, $Type)
|
||||
|
||||
macro bool is_numerical($Type)
|
||||
{
|
||||
var $kind = $Type.kind;
|
||||
var $kind = $Type.kindof;
|
||||
$if ($kind == TypeKind.DISTINCT):
|
||||
return is_numerical($Type.inner);
|
||||
$else:
|
||||
@@ -97,9 +89,14 @@ fn bool TypeKind.is_int(TypeKind kind) @inline
|
||||
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
|
||||
}
|
||||
|
||||
macro bool is_indexable($Type)
|
||||
{
|
||||
return $checks($Type t, int i, t[i]);
|
||||
}
|
||||
|
||||
macro bool is_comparable($Type)
|
||||
{
|
||||
var $kind = $Type.kind;
|
||||
var $kind = $Type.kindof;
|
||||
$if ($kind == TypeKind.DISTINCT):
|
||||
return is_comparable($Type.inner);
|
||||
$else:
|
||||
@@ -109,38 +106,48 @@ macro bool is_comparable($Type)
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro bool is_equatable($Type)
|
||||
{
|
||||
return $checks($Type a, a == a);
|
||||
}
|
||||
|
||||
macro bool is_subarray_convertable($Type)
|
||||
{
|
||||
$switch ($Type.kind):
|
||||
$switch ($Type.kindof):
|
||||
$case SUBARRAY:
|
||||
return true;
|
||||
$case POINTER:
|
||||
return $Type.inner.kind == TypeKind.ARRAY;
|
||||
return $Type.inner.kindof == TypeKind.ARRAY;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch;
|
||||
}
|
||||
|
||||
macro bool is_int($Type) = $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
|
||||
|
||||
macro bool is_intlike($Type)
|
||||
{
|
||||
$switch ($Type.kind):
|
||||
$switch ($Type.kindof):
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
return true;
|
||||
$case VECTOR:
|
||||
return $Type.inner.kind == TypeKind.SIGNED_INT || $Type.inner.kind == TypeKind.UNSIGNED_INT;
|
||||
return $Type.inner.kindof == TypeKind.SIGNED_INT || $Type.inner.kindof == TypeKind.UNSIGNED_INT;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch;
|
||||
}
|
||||
|
||||
|
||||
macro bool is_float($Type) = $Type.kindof == TypeKind.FLOAT;
|
||||
|
||||
macro bool is_floatlike($Type)
|
||||
{
|
||||
$switch ($Type.kind):
|
||||
$switch ($Type.kindof):
|
||||
$case FLOAT:
|
||||
return true;
|
||||
$case VECTOR:
|
||||
return $Type.inner.kind == TypeKind.FLOAT;
|
||||
return $Type.inner.kindof == TypeKind.FLOAT;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch;
|
||||
@@ -148,7 +155,12 @@ macro bool is_floatlike($Type)
|
||||
|
||||
macro bool is_vector($Type)
|
||||
{
|
||||
return $Type.kind == TypeKind.VECTOR;
|
||||
return $Type.kindof == TypeKind.VECTOR;
|
||||
}
|
||||
|
||||
macro bool @convertable(#a, $TypeB)
|
||||
{
|
||||
return $checks($TypeB x = #a);
|
||||
}
|
||||
|
||||
macro bool is_same($TypeA, $TypeB)
|
||||
@@ -175,7 +187,7 @@ macro bool is_equatable_value(value)
|
||||
$if ($defined(value.less) || $defined(value.compare_to) || $defined(value.equals)):
|
||||
return true;
|
||||
$else:
|
||||
return is_comparable($typeof(value));
|
||||
return is_equatable($typeof(value));
|
||||
$endif;
|
||||
}
|
||||
|
||||
@@ -204,7 +216,7 @@ enum TypeKind : char
|
||||
UNION,
|
||||
BITSTRUCT,
|
||||
FUNC,
|
||||
FAILABLE,
|
||||
OPTIONAL,
|
||||
ARRAY,
|
||||
SUBARRAY,
|
||||
VECTOR,
|
||||
@@ -216,5 +228,6 @@ enum TypeKind : char
|
||||
struct TypeEnum
|
||||
{
|
||||
TypeKind type;
|
||||
usize elements;
|
||||
usz elements;
|
||||
}
|
||||
|
||||
|
||||
6
lib/std/core/values.c3
Normal file
6
lib/std/core/values.c3
Normal file
@@ -0,0 +1,6 @@
|
||||
module std::core::values;
|
||||
|
||||
macro bool @is_int(#value) = types::is_int($typeof(#value));
|
||||
macro bool @convertable_to(#a, #b) = $checks($typeof(#b) x = #a);
|
||||
macro bool @is_floatlike(#value) = types::is_floatlike($typeof(#value));
|
||||
macro bool @is_float(#value) = types::is_float($typeof(#value));
|
||||
41
lib/std/hash/fnv32a.c3
Normal file
41
lib/std/hash/fnv32a.c3
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::hash::fnv32a;
|
||||
|
||||
define Fnv32a = distinct uint;
|
||||
|
||||
private const FNV32A_START = 0x811c9dc5;
|
||||
private const FNV32A_MUL = 0x01000193;
|
||||
|
||||
private macro void @update(uint &h, char x) => h = (h * FNV32A_MUL) ^ x;
|
||||
|
||||
fn void Fnv32a.init(Fnv32a* this)
|
||||
{
|
||||
*this = FNV32A_START;
|
||||
}
|
||||
|
||||
fn void Fnv32a.update(Fnv32a* this, char[] data)
|
||||
{
|
||||
uint h = (uint)*this;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
}
|
||||
*this = (Fnv32a)h;
|
||||
}
|
||||
|
||||
macro void Fnv32a.update_char(Fnv32a* this, char c)
|
||||
{
|
||||
@update(*this, x);
|
||||
}
|
||||
|
||||
fn uint encode(char[] data)
|
||||
{
|
||||
uint h = FNV32A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
183
lib/std/io/dir.c3
Normal file
183
lib/std/io/dir.c3
Normal file
@@ -0,0 +1,183 @@
|
||||
module std::io::dir;
|
||||
import std::io::os;
|
||||
// In progress.
|
||||
define Path = distinct char[];
|
||||
|
||||
const PREFERRED_SEPARATOR = USE_WIN32_FILESYSTEM ? '\\' : '/';
|
||||
private const USE_WIN32_FILESYSTEM = env::OS_TYPE != OsType.WIN32;
|
||||
|
||||
fault PathResult
|
||||
{
|
||||
INVALID_PATH
|
||||
}
|
||||
|
||||
fn char[]! getcwd(Allocator* allocator = mem::default_allocator())
|
||||
{
|
||||
return os::getcwd(allocator);
|
||||
}
|
||||
|
||||
fn char[]! tgetcwd()
|
||||
{
|
||||
return getcwd(mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
macro bool is_separator(char c)
|
||||
{
|
||||
$if (USE_WIN32_FILESYSTEM):
|
||||
return c == '/' || c == '\\';
|
||||
$else:
|
||||
return c == '/';
|
||||
$endif;
|
||||
}
|
||||
|
||||
const bool[256] RESERVED_PATH_CHAR_POSIX = {
|
||||
[0] = true,
|
||||
['/'] = true,
|
||||
};
|
||||
const bool[256] RESERVED_PATH_CHAR_WIN32 = {
|
||||
[0..31] = true,
|
||||
['>'] = true,
|
||||
['<'] = true,
|
||||
[':'] = true,
|
||||
['\"'] = true,
|
||||
['/'] = true,
|
||||
['\\'] = true,
|
||||
['|'] = true,
|
||||
['?'] = true,
|
||||
['*'] = true,
|
||||
};
|
||||
|
||||
macro bool is_reserved_path_char(char c)
|
||||
{
|
||||
$if (USE_WIN32_FILESYSTEM):
|
||||
return RESERVED_PATH_CHAR_WIN32[c];
|
||||
$else:
|
||||
return RESERVED_PATH_CHAR_POSIX[c];
|
||||
$endif;
|
||||
}
|
||||
|
||||
private fn usz! root_name_len(char[] path)
|
||||
{
|
||||
usz len = path.len;
|
||||
if (!len) return 0;
|
||||
$if (USE_WIN32_FILESYSTEM):
|
||||
switch (path[0])
|
||||
{
|
||||
case '\\':
|
||||
if (len < 2 || path[1] != '\\') break;
|
||||
if (len == 2 || is_separator(path[2])) return PathResult.INVALID_PATH!;
|
||||
for (usz i = 2; i < len; i++)
|
||||
{
|
||||
char c = path[i];
|
||||
if (is_separator(c)) return i;
|
||||
if (is_reserved_path_char(c)) return PathResult.INVALID_PATH!;
|
||||
}
|
||||
return len;
|
||||
case 'A'..'Z':
|
||||
case 'a'..'z':
|
||||
if (len < 2 || path[1] != ':') break;
|
||||
if (len < 3 || !is_separator(path[2])) return PathResult.INVALID_PATH!;
|
||||
return 2;
|
||||
}
|
||||
$endif;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private fn void! normalize_path(char[]* path_ref)
|
||||
{
|
||||
char[] path = *path_ref;
|
||||
if (!path.len) return;
|
||||
usz path_start = root_name_len(path)?;
|
||||
usz len = path_start;
|
||||
bool previous_was_separator = false;
|
||||
usz path_len = path.len;
|
||||
for (usz i = path_start; i < path_len; i++)
|
||||
{
|
||||
char c = path[i];
|
||||
// Fold foo///bar into foo/bar
|
||||
if (is_separator(c))
|
||||
{
|
||||
if (previous_was_separator)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
path.ptr[len++] = PREFERRED_SEPARATOR;
|
||||
previous_was_separator = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we get . we have different things that might happen:
|
||||
if (c == '.' && i < path_len - 1)
|
||||
{
|
||||
// Is this ./ or /./ ?
|
||||
if ((previous_was_separator || i == path_start) && is_separator(path[i + 1]))
|
||||
{
|
||||
// Then we skip this
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
// Is this /../ in that case we must walk back and erase(!)
|
||||
if (i < path_len - 2 && previous_was_separator && path[i + 1] == '.' && is_separator(path[i + 2]))
|
||||
{
|
||||
assert(len > path_start);
|
||||
len--;
|
||||
while (len > path_start && !is_separator(path[len - 1]))
|
||||
{
|
||||
len--;
|
||||
}
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (i != len) path[len] = c;
|
||||
previous_was_separator = false;
|
||||
len++;
|
||||
}
|
||||
path.ptr[len] = 0;
|
||||
*path_ref = path[:len];
|
||||
}
|
||||
|
||||
fn Path new_path(char[] path)
|
||||
{
|
||||
char[] copy = str::copy(path);
|
||||
normalize_path(©)!!;
|
||||
return (Path)copy;
|
||||
}
|
||||
|
||||
fn Path Path.root_name(Path path)
|
||||
{
|
||||
char[] path_str = (char[])path;
|
||||
usz len = root_name_len(path_str)!!;
|
||||
if (!len) return (Path)"";
|
||||
return (Path)path_str[0:len];
|
||||
}
|
||||
|
||||
fn Path Path.root_directory(Path path)
|
||||
{
|
||||
char[] path_str = (char[])path;
|
||||
usz len = path_str.len;
|
||||
if (!len) return (Path)"";
|
||||
$if (USE_WIN32_FILESYSTEM):
|
||||
usz root_len = root_name_len(path_str)!!;
|
||||
if (root_len == len || !is_separator(path_str[root_len])) return (Path)"";
|
||||
return (Path)path_str[root_len..root_len];
|
||||
$else:
|
||||
if (!is_separator(path_str[0])) return (Path)"";
|
||||
for (usz i = 1; i < len; i++)
|
||||
{
|
||||
if (is_separator(path_str[i]))
|
||||
{
|
||||
return (Path)path_str[:i];
|
||||
}
|
||||
}
|
||||
return path;
|
||||
$endif;
|
||||
}
|
||||
|
||||
|
||||
fn void Path.destroy(Path path)
|
||||
{
|
||||
free(path.ptr);
|
||||
}
|
||||
|
||||
|
||||
111
lib/std/io/io.c3
Normal file
111
lib/std/io/io.c3
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright (c) 2021-2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::io;
|
||||
import libc;
|
||||
|
||||
struct File
|
||||
{
|
||||
CFile file;
|
||||
}
|
||||
|
||||
enum Seek
|
||||
{
|
||||
SET,
|
||||
CURSOR,
|
||||
END
|
||||
}
|
||||
|
||||
fault IoError
|
||||
{
|
||||
FILE_NOT_FOUND,
|
||||
FILE_NOT_SEEKABLE,
|
||||
FILE_NOT_VALID,
|
||||
FILE_INVALID_POSITION,
|
||||
FILE_OVERFLOW,
|
||||
FILE_IS_PIPE,
|
||||
FILE_EOF,
|
||||
FILE_INCOMPLETE_WRITE,
|
||||
FILE_NOT_DIR,
|
||||
NO_PERMISSION,
|
||||
NAME_TOO_LONG,
|
||||
INTERRUPTED,
|
||||
GENERAL_ERROR,
|
||||
UNKNOWN_ERROR,
|
||||
}
|
||||
|
||||
fn int putchar(char c) @inline
|
||||
{
|
||||
return libc::putchar(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] message
|
||||
* @return `number of bytes printed.`
|
||||
*/
|
||||
fn int print(char* message)
|
||||
{
|
||||
char* pointer = message;
|
||||
while (*pointer != '\0')
|
||||
{
|
||||
if (!putchar(*pointer)) return 0;
|
||||
pointer++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] message
|
||||
* @return `number of bytes printed.`
|
||||
*/
|
||||
fn int println(char *message = "") @inline
|
||||
{
|
||||
return libc::puts(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
fn File stdout()
|
||||
{
|
||||
return { libc::stdout() };
|
||||
}
|
||||
|
||||
fn File stderr()
|
||||
{
|
||||
return { libc::stderr() };
|
||||
}
|
||||
|
||||
fn File stdin()
|
||||
{
|
||||
return { libc::stdin() };
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
|
||||
error FileError
|
||||
{
|
||||
ulong errno;
|
||||
}
|
||||
|
||||
fn FileError errorFromErrno()
|
||||
{
|
||||
return FileError { };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
pubic fn void! File.clearerr(File *file) @inline
|
||||
{
|
||||
clearerr(file->file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn void File.error(File *file) @inline
|
||||
{
|
||||
int err = ferror
|
||||
}
|
||||
*/
|
||||
@@ -1,44 +1,6 @@
|
||||
// Copyright (c) 2021-2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::io;
|
||||
import libc;
|
||||
|
||||
struct File
|
||||
{
|
||||
CFile file;
|
||||
}
|
||||
|
||||
|
||||
fn int putchar(char c) @inline
|
||||
{
|
||||
return libc::putchar(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] message
|
||||
* @return `number of bytes printed.`
|
||||
*/
|
||||
fn int print(char* message)
|
||||
{
|
||||
char* pointer = message;
|
||||
while (*pointer != '\0')
|
||||
{
|
||||
if (!putchar(*pointer)) return 0;
|
||||
pointer++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] message
|
||||
* @return `number of bytes printed.`
|
||||
*/
|
||||
fn int println(char *message = "") @inline
|
||||
{
|
||||
return libc::puts(message);
|
||||
}
|
||||
|
||||
fn void! File.open(File* file, char[] filename, char[] mode)
|
||||
{
|
||||
@pool()
|
||||
@@ -55,27 +17,6 @@ fn void! File.open(File* file, char[] filename, char[] mode)
|
||||
};
|
||||
}
|
||||
|
||||
enum Seek
|
||||
{
|
||||
SET,
|
||||
CURSOR,
|
||||
END
|
||||
}
|
||||
|
||||
fault IoError
|
||||
{
|
||||
FILE_NOT_FOUND,
|
||||
FILE_NOT_SEEKABLE,
|
||||
FILE_NOT_VALID,
|
||||
FILE_INVALID_POSITION,
|
||||
FILE_OVERFLOW,
|
||||
FILE_IS_PIPE,
|
||||
FILE_EOF,
|
||||
FILE_INCOMPLETE_WRITE,
|
||||
INTERRUPTED,
|
||||
UNKNOWN_ERROR,
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file.file != null
|
||||
**/
|
||||
@@ -139,7 +80,7 @@ fn bool File.eof(File* file) @inline
|
||||
/**
|
||||
* @require file && file.file
|
||||
*/
|
||||
fn usize File.read(File* file, void* buffer, usize items, usize element_size = 1)
|
||||
fn usz File.read(File* file, void* buffer, usz items, usz element_size = 1)
|
||||
{
|
||||
return libc::fread(buffer, element_size, items, file.file);
|
||||
}
|
||||
@@ -152,7 +93,7 @@ fn usize File.read(File* file, void* buffer, usize items, usize element_size = 1
|
||||
* @require file.file `File must be initialized`
|
||||
* @require element_size > 1
|
||||
*/
|
||||
fn usize File.write(File* file, void* buffer, usize items, usize element_size = 1)
|
||||
fn usz File.write(File* file, void* buffer, usz items, usz element_size = 1)
|
||||
{
|
||||
return libc::fwrite(buffer, element_size, items, file.file);
|
||||
}
|
||||
@@ -161,9 +102,9 @@ fn usize File.write(File* file, void* buffer, usize items, usize element_size =
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
*/
|
||||
fn usize! File.println(File* file, char[] string)
|
||||
fn usz! File.println(File* file, char[] string)
|
||||
{
|
||||
usize len = string.len;
|
||||
usz len = string.len;
|
||||
if (len != libc::fwrite(string.ptr, 1, len, file.file)) return IoError.UNKNOWN_ERROR!;
|
||||
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR!;
|
||||
return len + 1;
|
||||
@@ -179,12 +120,33 @@ fn String File.getline(File* file, Allocator* allocator = mem::current_allocator
|
||||
while (!file.eof())
|
||||
{
|
||||
int c = libc::fgetc(file.file);
|
||||
if (c == -1) break;
|
||||
if (c == '\n') break;
|
||||
s.append_char((char)c);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
* @return "a zero terminated char[] (the pointer may be safely cast into a ZString)"
|
||||
*/
|
||||
fn char[] File.tgetline(File* file)
|
||||
{
|
||||
|
||||
String s = file.getline(mem::temp_allocator());
|
||||
ZString z = s.zstr();
|
||||
return z[:s.len()];
|
||||
}
|
||||
|
||||
fn char! File.getc(File* file)
|
||||
{
|
||||
int c = libc::fgetc(file.file);
|
||||
if (c == -1) return IoError.FILE_EOF!;
|
||||
return (char)c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
@@ -193,47 +155,3 @@ fn void File.flush(File* file)
|
||||
{
|
||||
libc::fflush(file.file);
|
||||
}
|
||||
|
||||
fn File stdout()
|
||||
{
|
||||
return { libc::stdout() };
|
||||
}
|
||||
|
||||
fn File stderr()
|
||||
{
|
||||
return { libc::stderr() };
|
||||
}
|
||||
|
||||
fn File stdin()
|
||||
{
|
||||
return { libc::stdin() };
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
|
||||
error FileError
|
||||
{
|
||||
ulong errno;
|
||||
}
|
||||
|
||||
fn FileError errorFromErrno()
|
||||
{
|
||||
return FileError { };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
pubic fn void! File.clearerr(File *file) @inline
|
||||
{
|
||||
clearerr(file->file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn void File.error(File *file) @inline
|
||||
{
|
||||
int err = ferror
|
||||
}
|
||||
*/
|
||||
81
lib/std/io/io_fileinfo.c3
Normal file
81
lib/std/io/io_fileinfo.c3
Normal file
@@ -0,0 +1,81 @@
|
||||
module std::io::file;
|
||||
import libc;
|
||||
|
||||
struct FileInfo
|
||||
{
|
||||
ulong size;
|
||||
}
|
||||
|
||||
$switch (env::OS_TYPE):
|
||||
$case MACOSX:
|
||||
$case IOS:
|
||||
$case WATCHOS:
|
||||
$case TVOS:
|
||||
private struct DarwinTimespec
|
||||
{
|
||||
long tv_sec;
|
||||
long tv_nsec;
|
||||
}
|
||||
private struct Darwin64Stat
|
||||
{
|
||||
int st_dev;
|
||||
ushort st_mode;
|
||||
ushort st_nlink;
|
||||
ulong st_ino;
|
||||
uint st_uid;
|
||||
uint st_gid;
|
||||
int st_rdev;
|
||||
DarwinTimespec st_atimespec; // time of last access
|
||||
DarwinTimespec st_mtimespec; // time of last data modification
|
||||
DarwinTimespec st_ctimespec; // time of last status change
|
||||
DarwinTimespec st_birthtimespec; // time of file creation(birth)
|
||||
long st_size;
|
||||
long st_blocks;
|
||||
int st_blocksize;
|
||||
uint st_flags;
|
||||
uint st_gen;
|
||||
int st_lspare;
|
||||
long[2] st_qspare;
|
||||
}
|
||||
extern fn int _stat(ZString str, Darwin64Stat* stat) @extname("stat64");
|
||||
fn void! FileInfo.read(FileInfo* info, Path path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
Darwin64Stat stat;
|
||||
int res = _stat(str::tcopy_zstring((char[])path), &stat);
|
||||
if (res != 0)
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EBADF:
|
||||
return IoError.FILE_NOT_VALID!;
|
||||
case errno::EFAULT:
|
||||
unreachable("Invalid stat");
|
||||
case errno::EIO:
|
||||
return IoError.GENERAL_ERROR!;
|
||||
case errno::EACCES:
|
||||
return IoError.NO_PERMISSION!;
|
||||
case errno::ELOOP:
|
||||
return IoError.NO_PERMISSION!;
|
||||
case errno::ENAMETOOLONG:
|
||||
return IoError.NAME_TOO_LONG!;
|
||||
case errno::ENOENT:
|
||||
return IoError.FILE_NOT_FOUND!;
|
||||
case errno::ENOTDIR:
|
||||
return IoError.FILE_NOT_DIR!;
|
||||
case errno::EOVERFLOW:
|
||||
return IoError.GENERAL_ERROR!;
|
||||
default:
|
||||
return IoError.UNKNOWN_ERROR!;
|
||||
}
|
||||
}
|
||||
info.size = stat.st_size;
|
||||
};
|
||||
}
|
||||
$default:
|
||||
macro void! FileInfo.read(FileInfo* info, Path path)
|
||||
{
|
||||
$assert("Unsupported function");
|
||||
}
|
||||
$endswitch;
|
||||
556
lib/std/io/io_formatter_private.c3
Normal file
556
lib/std/io/io_formatter_private.c3
Normal file
@@ -0,0 +1,556 @@
|
||||
module std::io;
|
||||
|
||||
private fn void! Formatter.left_adjust(Formatter* this, usz len)
|
||||
{
|
||||
if (!this.flags.left) return;
|
||||
for (usz l = len; l < this.width; l++) this.out(' ')?;
|
||||
}
|
||||
|
||||
private fn void! Formatter.right_adjust(Formatter* this, usz len)
|
||||
{
|
||||
if (this.flags.left) return;
|
||||
for (usz l = len; l < this.width; l++) this.out(' ')?;
|
||||
}
|
||||
|
||||
|
||||
private fn uint128 int_from_variant(variant arg, bool *is_neg)
|
||||
{
|
||||
*is_neg = false;
|
||||
if (arg.type.kindof == TypeKind.POINTER)
|
||||
{
|
||||
return (uint128)(uptr)*(void**)arg.ptr;
|
||||
}
|
||||
switch (arg)
|
||||
{
|
||||
case bool:
|
||||
return (uint128)*arg;
|
||||
case ichar:
|
||||
int val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case short:
|
||||
int val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case int:
|
||||
int val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case long:
|
||||
long val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case int128:
|
||||
int128 val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case char:
|
||||
return *arg;
|
||||
case ushort:
|
||||
return *arg;
|
||||
case uint:
|
||||
return *arg;
|
||||
case ulong:
|
||||
return *arg;
|
||||
case uint128:
|
||||
return *arg;
|
||||
case float:
|
||||
float f = *arg;
|
||||
return (uint128)((*is_neg = f < 0) ? -f : f);
|
||||
case double:
|
||||
double d = *arg;
|
||||
return (uint128)((*is_neg = d < 0) ? -d : d);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private fn FloatType float_from_variant(variant arg)
|
||||
{
|
||||
$if (env::F128_SUPPORT):
|
||||
if (arg.type == float128.typeid) return *((float128*)arg.ptr);
|
||||
$endif;
|
||||
$if (env::F16_SUPPORT):
|
||||
if (arg.type == float16.typeid) return *((float16*)arg.ptr);
|
||||
$endif;
|
||||
|
||||
if (arg.type.kindof == TypeKind.POINTER)
|
||||
{
|
||||
return (FloatType)(uptr)(void*)arg.ptr;
|
||||
}
|
||||
switch (arg)
|
||||
{
|
||||
case bool:
|
||||
return (FloatType)*arg;
|
||||
case ichar:
|
||||
return *arg;
|
||||
case short:
|
||||
return *arg;
|
||||
case int:
|
||||
return *arg;
|
||||
case long:
|
||||
return *arg;
|
||||
case int128:
|
||||
return *arg;
|
||||
case char:
|
||||
return *arg;
|
||||
case ushort:
|
||||
return *arg;
|
||||
case uint:
|
||||
return *arg;
|
||||
case ulong:
|
||||
return *arg;
|
||||
case uint128:
|
||||
return *arg;
|
||||
case float:
|
||||
return (FloatType)*arg;
|
||||
case double:
|
||||
return (FloatType)*arg;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a simple integer value, typically for formatting.
|
||||
*
|
||||
* @param [inout] len_ptr "the length remaining."
|
||||
* @param [in] buf "the buf to read from."
|
||||
* @param maxlen "the maximum len that can be read."
|
||||
* @return "The result of the atoi."
|
||||
**/
|
||||
private fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline
|
||||
{
|
||||
uint i = 0;
|
||||
usz len = *len_ptr;
|
||||
while (len < maxlen)
|
||||
{
|
||||
char c = buf[len];
|
||||
if (c < '0' || c > '9') break;
|
||||
i = i * 10 + c - '0';
|
||||
len++;
|
||||
}
|
||||
*len_ptr = len;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
private fn void! Formatter.out_substr(Formatter *this, char[] str)
|
||||
{
|
||||
usz l = conv::utf8_codepoints(str);
|
||||
uint prec = this.prec;
|
||||
if (this.flags.precision && l < prec) l = prec;
|
||||
this.right_adjust(' ')?;
|
||||
usz index = 0;
|
||||
usz chars = str.len;
|
||||
char* ptr = str.ptr;
|
||||
while (index < chars)
|
||||
{
|
||||
char c = ptr[index];
|
||||
// Break if we have precision set and we ran out...
|
||||
if (c & 0xC0 != 0x80 && this.flags.precision && !prec--) break;
|
||||
this.out(c)?;
|
||||
index++;
|
||||
}
|
||||
return this.left_adjust(l);
|
||||
}
|
||||
|
||||
|
||||
union ConvUnion
|
||||
{
|
||||
ulong u;
|
||||
double f;
|
||||
}
|
||||
|
||||
private fn void! Formatter.etoa(Formatter* this, FloatType value)
|
||||
{
|
||||
// check for NaN and special values
|
||||
if (value != value || value < -FloatType.max || value > FloatType.max)
|
||||
{
|
||||
return this.ftoa(value);
|
||||
}
|
||||
|
||||
// determine the sign
|
||||
bool negative = value < 0;
|
||||
if (negative) value = -value;
|
||||
|
||||
// default precision
|
||||
if (!this.flags.precision)
|
||||
{
|
||||
this.prec = PRINTF_DEFAULT_FLOAT_PRECISION;
|
||||
}
|
||||
|
||||
// determine the decimal exponent
|
||||
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
|
||||
ConvUnion conv;
|
||||
|
||||
conv.f = (double)value;
|
||||
int exp2 = (int)(conv.u >> 52 & 0x7FF) - 1023; // effectively log2
|
||||
conv.u = (conv.u & (1u64 << 52 - 1)) | (1023u64 << 52); // drop the exponent so conv.F is now in [1,2)
|
||||
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
|
||||
int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.f - 1.5) * 0.289529654602168);
|
||||
// now we want to compute 10^expval but we want to be sure it won't overflow
|
||||
exp2 = (int)(expval * 3.321928094887362 + 0.5);
|
||||
double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
|
||||
double z2 = z * z;
|
||||
conv.u = (ulong)(exp2 + 1023) << 52;
|
||||
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
|
||||
conv.f *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
|
||||
// correct for rounding errors
|
||||
if (value < conv.f)
|
||||
{
|
||||
expval--;
|
||||
conv.f /= 10;
|
||||
}
|
||||
|
||||
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
|
||||
uint minwidth = ((expval < 100) && (expval > -100)) ? 4 : 5;
|
||||
|
||||
// in "%g" mode, "prec" is the number of *significant figures* not decimals
|
||||
if (this.flags.adapt_exp)
|
||||
{
|
||||
// do we want to fall-back to "%f" mode?
|
||||
if (value >= 1e-4 && value < 1e6)
|
||||
{
|
||||
this.prec = this.prec > expval ? this.prec - expval - 1 : 0;
|
||||
this.flags.precision = true; // make sure ftoa respects precision
|
||||
// no characters in exponent
|
||||
minwidth = 0;
|
||||
expval = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we use one sigfig for the whole part
|
||||
if (this.prec > 0 && this.flags.precision) this.prec--;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust width
|
||||
uint fwidth = this.width > minwidth ? this.width - minwidth : 0;
|
||||
|
||||
// if we're padding on the right, DON'T pad the floating part
|
||||
if (this.flags.left && minwidth) fwidth = 0;
|
||||
|
||||
// rescale the float value
|
||||
if (expval) value /= conv.f;
|
||||
|
||||
// output the floating part
|
||||
usz start_idx = this.idx;
|
||||
PrintFlags old = this.flags;
|
||||
this.flags.adapt_exp = false;
|
||||
this.width = fwidth;
|
||||
this.ftoa(negative ? -value : value)?;
|
||||
this.flags = old;
|
||||
|
||||
// output the exponent part
|
||||
if (minwidth)
|
||||
{
|
||||
// output the exponential symbol
|
||||
this.out(this.flags.uppercase ? 'E' : 'e')?;
|
||||
// output the exponent value
|
||||
this.flags = { .zeropad = true, .plus = true };
|
||||
this.width = minwidth - 1;
|
||||
this.prec = 0;
|
||||
this.ntoa((uint128)(expval < 0 ? -expval : expval), expval < 0, 10)?;
|
||||
this.flags = old;
|
||||
// might need to right-pad spaces
|
||||
this.left_adjust(this.idx - start_idx)?;
|
||||
}
|
||||
}
|
||||
|
||||
// internal ftoa for fixed decimal floating point
|
||||
private fn void! Formatter.ftoa(Formatter* this, FloatType value)
|
||||
{
|
||||
char[PRINTF_FTOA_BUFFER_SIZE] buf = void;
|
||||
usz len = 0;
|
||||
const FloatType[] POW10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
|
||||
FloatType diff = 0.0;
|
||||
|
||||
// powers of 10
|
||||
|
||||
// test for special values
|
||||
if (value != value)
|
||||
{
|
||||
return this.out_reverse("nan");
|
||||
}
|
||||
if (value < -FloatType.max)
|
||||
{
|
||||
return this.out_reverse("fni-");
|
||||
}
|
||||
if (value > FloatType.max)
|
||||
{
|
||||
if (this.flags.plus)
|
||||
{
|
||||
return this.out_reverse("fni+");
|
||||
}
|
||||
return this.out_reverse("fni");
|
||||
}
|
||||
|
||||
// test for very large values
|
||||
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
|
||||
if (value > PRINTF_MAX_FLOAT || value < -PRINTF_MAX_FLOAT)
|
||||
{
|
||||
return this.etoa(value);
|
||||
}
|
||||
|
||||
// test for negative
|
||||
bool negative = value < 0;
|
||||
if (negative) value = 0 - value;
|
||||
|
||||
// set default precision, if not set explicitly
|
||||
if (!this.flags.precision) this.prec = PRINTF_DEFAULT_FLOAT_PRECISION;
|
||||
|
||||
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
|
||||
while (this.prec > 9)
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
this.prec--;
|
||||
}
|
||||
|
||||
// Safe due to 1e9 limit.
|
||||
int whole = (int)value;
|
||||
FloatType tmp = (value - whole) * POW10[this.prec];
|
||||
ulong frac = (ulong)tmp;
|
||||
diff = tmp - frac;
|
||||
|
||||
switch (true)
|
||||
{
|
||||
case diff > 0.5:
|
||||
++frac;
|
||||
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
|
||||
if (frac >= POW10[this.prec])
|
||||
{
|
||||
frac = 0;
|
||||
++whole;
|
||||
}
|
||||
case diff < 0.5:
|
||||
break;
|
||||
case !frac && (frac & 1):
|
||||
// if halfway, round up if odd OR if last digit is 0
|
||||
++frac;
|
||||
}
|
||||
if (!this.prec)
|
||||
{
|
||||
diff = value - (FloatType)whole;
|
||||
if ((!(diff < 0.5) || diff > 0.5) && (whole & 1))
|
||||
{
|
||||
// exactly 0.5 and ODD, then round up
|
||||
// 1.5 -> 2, but 2.5 -> 2
|
||||
++whole;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint count = this.prec;
|
||||
// now do fractional part, as an unsigned number
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
--count;
|
||||
buf[len++] = (char)(48 + (frac % 10));
|
||||
}
|
||||
while (frac /= 10);
|
||||
// add extra 0s
|
||||
while (count-- > 0)
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
// add decimal
|
||||
buf[len++] = '.';
|
||||
}
|
||||
|
||||
// do whole part, number is reversed
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = (char)(48 + (whole % 10));
|
||||
}
|
||||
while (whole /= 10);
|
||||
|
||||
// pad leading zeros
|
||||
if (!this.flags.left && this.flags.zeropad)
|
||||
{
|
||||
if (this.width && (negative || this.flags.plus || this.flags.space)) this.width--;
|
||||
while (len < this.width)
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
char next = {|
|
||||
if (negative) return '-';
|
||||
if (this.flags.plus) return '+';
|
||||
if (this.flags.space) return ' ';
|
||||
return 0;
|
||||
|};
|
||||
if (next)
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = next;
|
||||
}
|
||||
return this.out_reverse(buf[:len]);
|
||||
}
|
||||
|
||||
private fn void! Formatter.ntoa(Formatter* this, uint128 value, bool negative, uint base)
|
||||
{
|
||||
char[PRINTF_NTOA_BUFFER_SIZE] buf = void;
|
||||
usz len = 0;
|
||||
|
||||
// no hash for 0 values
|
||||
if (!value) this.flags.hash = false;
|
||||
|
||||
// write if precision != 0 or value is != 0
|
||||
if (!this.flags.precision || value)
|
||||
{
|
||||
char past_10 = (this.flags.uppercase ? 'A' : 'a') - 10;
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
char digit = (char)(value % base);
|
||||
buf[len++] = digit + (digit < 10 ? '0' : past_10);
|
||||
value /= base;
|
||||
}
|
||||
while (value);
|
||||
}
|
||||
return this.ntoa_format(buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
|
||||
}
|
||||
|
||||
private fn void! Formatter.ntoa_format(Formatter* this, char[] buf, usz len, bool negative, uint base)
|
||||
{
|
||||
// pad leading zeros
|
||||
if (!this.flags.left)
|
||||
{
|
||||
if (this.width && this.flags.zeropad && (negative || this.flags.plus || this.flags.space)) this.width--;
|
||||
while (len < this.prec)
|
||||
{
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
while (this.flags.zeropad && len < this.width)
|
||||
{
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
// handle hash
|
||||
if (this.flags.hash && base != 10)
|
||||
{
|
||||
if (!this.flags.precision && len && len == this.prec && len == this.width)
|
||||
{
|
||||
len--;
|
||||
if (len) len--;
|
||||
}
|
||||
if (base != 10)
|
||||
{
|
||||
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
switch (base)
|
||||
{
|
||||
case 16:
|
||||
buf[len++] = this.flags.uppercase ? 'X' : 'x';
|
||||
case 8:
|
||||
buf[len++] = this.flags.uppercase ? 'O' : 'o';
|
||||
case 2:
|
||||
buf[len++] = this.flags.uppercase ? 'B' : 'b';
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
switch (true)
|
||||
{
|
||||
case negative:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '-';
|
||||
case this.flags.plus:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '+';
|
||||
case this.flags.space:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
if (!len) return;
|
||||
return this.out_reverse(buf[:len]);
|
||||
}
|
||||
|
||||
|
||||
private fn void! Formatter.ntoa_variant(Formatter* this, variant arg, uint base)
|
||||
{
|
||||
bool is_neg;
|
||||
uint128 val = int_from_variant(arg, &is_neg);
|
||||
return this.ntoa(val, is_neg, base) @inline;
|
||||
}
|
||||
|
||||
private fn void! Formatter.out_char(Formatter* this, variant arg)
|
||||
{
|
||||
uint l = 1;
|
||||
// pre padding
|
||||
this.right_adjust(l)?;
|
||||
// char output
|
||||
Char32 c = types::variant_to_int(arg, uint) ?? 0xFFFD;
|
||||
switch (true)
|
||||
{
|
||||
case c < 0x7f:
|
||||
this.out((char)c)?;
|
||||
case c < 0x7ff:
|
||||
this.out((char)(0xC0 | c >> 6))?;
|
||||
this.out((char)(0x80 | (c & 0x3F)))?;
|
||||
case c < 0xffff:
|
||||
this.out((char)(0xE0 | c >> 12))?;
|
||||
this.out((char)(0x80 | (c >> 6 & 0x3F)))?;
|
||||
this.out((char)(0x80 | (c & 0x3F)))?;
|
||||
default:
|
||||
this.out((char)(0xF0 | c >> 18))?;
|
||||
this.out((char)(0x80 | (c >> 12 & 0x3F)))?;
|
||||
this.out((char)(0x80 | (c >> 6 & 0x3F)))?;
|
||||
this.out((char)(0x80 | (c & 0x3F)))?;
|
||||
}
|
||||
return this.left_adjust(l);
|
||||
}
|
||||
|
||||
|
||||
private fn void! Formatter.out_reverse(Formatter* this, char[] buf)
|
||||
{
|
||||
usz buffer_start_idx = this.idx;
|
||||
usz len = buf.len;
|
||||
// pad spaces up to given width
|
||||
if (!this.flags.left && !this.flags.zeropad)
|
||||
{
|
||||
for (usz i = len; i < this.width; i++)
|
||||
{
|
||||
this.out(' ')?;
|
||||
}
|
||||
}
|
||||
// reverse string
|
||||
while (len) this.out(buf[--len])?;
|
||||
|
||||
// append pad spaces up to given width
|
||||
return this.left_adjust(this.idx - buffer_start_idx);
|
||||
}
|
||||
|
||||
private fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline
|
||||
{
|
||||
usz val = ++(*index_ptr);
|
||||
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT!;
|
||||
}
|
||||
|
||||
private fn variant! next_variant(variant* args_ptr, usz args_len, usz* arg_index_ptr) @inline
|
||||
{
|
||||
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG!;
|
||||
return args_ptr[(*arg_index_ptr)++];
|
||||
}
|
||||
|
||||
private fn int! printf_parse_format_field(variant* args_ptr, usz args_len, usz* args_index_ptr, char* format_ptr, usz format_len, usz* index_ptr) @inline
|
||||
{
|
||||
char c = format_ptr[*index_ptr];
|
||||
if (c >= '0' && c <= '9') return simple_atoi(format_ptr, format_len, index_ptr);
|
||||
if (c != '*') return 0;
|
||||
printf_advance_format(format_len, index_ptr)?;
|
||||
variant val = next_variant(args_ptr, args_len, args_index_ptr)?;
|
||||
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG!;
|
||||
uint! intval = types::variant_to_int(val, int);
|
||||
if (catch intval) return FormattingFault.INVALID_WIDTH_ARG!;
|
||||
return intval;
|
||||
}
|
||||
497
lib/std/io/io_printf.c3
Normal file
497
lib/std/io/io_printf.c3
Normal file
@@ -0,0 +1,497 @@
|
||||
module std::io;
|
||||
import std::map;
|
||||
import libc;
|
||||
|
||||
const int PRINTF_NTOA_BUFFER_SIZE = 256;
|
||||
const int PRINTF_FTOA_BUFFER_SIZE = 256;
|
||||
const float PRINTF_MAX_FLOAT = 1e9;
|
||||
const uint PRINTF_DEFAULT_FLOAT_PRECISION = 6;
|
||||
|
||||
fault PrintFault
|
||||
{
|
||||
BUFFER_EXCEEDED,
|
||||
INTERNAL_BUFFER_EXCEEDED,
|
||||
INVALID_FORMAT_STRING,
|
||||
MISSING_ARG,
|
||||
}
|
||||
|
||||
|
||||
define OutputFn = fn void!(char c, void* buffer);
|
||||
define ToStringFunction = fn char[](void* value, Allocator *allocator);
|
||||
define ToFormatFunction = fn void!(void* value, Formatter* formatter);
|
||||
define FloatType = double;
|
||||
|
||||
private define StringFunctionMap = std::map::HashMap<typeid, ToStringFunction>;
|
||||
private define FormatterFunctionMap = std::map::HashMap<typeid, ToFormatFunction>;
|
||||
private FormatterFunctionMap toformat_functions;
|
||||
private StringFunctionMap tostring_functions;
|
||||
|
||||
struct Formatter
|
||||
{
|
||||
void *data;
|
||||
OutputFn out_fn;
|
||||
struct
|
||||
{
|
||||
PrintFlags flags;
|
||||
uint width;
|
||||
uint prec;
|
||||
usz idx;
|
||||
}
|
||||
}
|
||||
|
||||
bitstruct PrintFlags : uint
|
||||
{
|
||||
bool zeropad : 0;
|
||||
bool left : 1;
|
||||
bool plus : 2;
|
||||
bool space : 3;
|
||||
bool hash : 4;
|
||||
bool uppercase : 5;
|
||||
bool precision : 6;
|
||||
bool adapt_exp : 7;
|
||||
}
|
||||
|
||||
fn void Formatter.init(Formatter* this, OutputFn out_fn, void* data = null)
|
||||
{
|
||||
*this = { .data = data, .out_fn = out_fn};
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $checks($Type a, a.to_string()) || $checks($Type a, a.to_format(&&Formatter{})) "Expected a type with 'to_string' or 'to_format' defined"
|
||||
* @require !$checks($Type a, a.to_string()) || $checks($Type a, a.to_string(&&Allocator{})) "Expected 'to_string' to take an allocator as argument."
|
||||
* @require !$checks($Type a, a.to_format(&&Formatter{})) || $checks($Type a, Formatter b, a.to_format(&b)) "Expected 'to_format' to take a Formatter as argument."
|
||||
*/
|
||||
macro void formatter_register_type($Type)
|
||||
{
|
||||
$if ($checks($Type a, a.to_format(&&Formatter {}))):
|
||||
if (!toformat_functions.table.len)
|
||||
{
|
||||
toformat_functions.init(512);
|
||||
}
|
||||
toformat_functions.set($Type.typeid, (ToFormatFunction)&$Type.to_format);
|
||||
$else:
|
||||
if (!tostring_functions.table.len)
|
||||
{
|
||||
tostring_functions.init(512);
|
||||
}
|
||||
tostring_functions.set($Type.typeid, (ToStringFunction)&$Type.to_string);
|
||||
$endif;
|
||||
}
|
||||
|
||||
|
||||
static initialize @priority(101)
|
||||
{
|
||||
if (!toformat_functions.table.len)
|
||||
{
|
||||
toformat_functions.init(512);
|
||||
}
|
||||
if (!tostring_functions.table.len)
|
||||
{
|
||||
tostring_functions.init(512);
|
||||
}
|
||||
}
|
||||
|
||||
private fn void! Formatter.out(Formatter* this, char c)
|
||||
{
|
||||
this.out_fn(c, this.data)?;
|
||||
}
|
||||
|
||||
macro bool! Formatter.print_with_function(Formatter* this, variant arg)
|
||||
{
|
||||
if (try to_format = toformat_functions.get(arg.type))
|
||||
{
|
||||
PrintFlags old = this.flags;
|
||||
uint old_width = this.width;
|
||||
uint old_prec = this.prec;
|
||||
defer
|
||||
{
|
||||
this.flags = old;
|
||||
this.width = old_width;
|
||||
this.prec = old_prec;
|
||||
}
|
||||
to_format(arg.ptr, this)?;
|
||||
return true;
|
||||
}
|
||||
if (try to_string = tostring_functions.get(arg.type))
|
||||
{
|
||||
PrintFlags old = this.flags;
|
||||
uint old_width = this.width;
|
||||
uint old_prec = this.prec;
|
||||
defer
|
||||
{
|
||||
this.flags = old;
|
||||
this.width = old_width;
|
||||
this.prec = old_prec;
|
||||
}
|
||||
@pool()
|
||||
{
|
||||
this.out_substr(to_string(arg.ptr, mem::temp_allocator()))?;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private fn void! Formatter.out_str(Formatter* this, variant arg)
|
||||
{
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case TYPEID:
|
||||
return this.out_substr("<typeid>");
|
||||
case VOID:
|
||||
return this.out_substr("void");
|
||||
case ANYERR:
|
||||
case FAULT:
|
||||
return this.out_substr((*(anyerr*)arg.ptr).nameof);
|
||||
case VARIANT:
|
||||
return this.out_substr("<variant>");
|
||||
case ENUM:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr(arg.type.names[types::variant_to_int(arg, usz)!!]);
|
||||
case STRUCT:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr("<struct>");
|
||||
case UNION:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr("<union>");
|
||||
case BITSTRUCT:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr("<bitstruct>");
|
||||
case FUNC:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr("<function>");
|
||||
case OPTIONAL:
|
||||
unreachable();
|
||||
case DISTINCT:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
if (arg.type == String.typeid)
|
||||
{
|
||||
return this.out_substr(((String*)arg).str());
|
||||
}
|
||||
return this.out_str(variant { arg.ptr, arg.type.inner });
|
||||
case POINTER:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner.kindof == TypeKind.ARRAY && inner.inner == char.typeid)
|
||||
{
|
||||
char *ptr = *(char**)arg.ptr;
|
||||
return this.out_substr(ptr[:inner.len]);
|
||||
}
|
||||
return this.ntoa_variant(arg, 16);
|
||||
case SIGNED_INT:
|
||||
case UNSIGNED_INT:
|
||||
return this.ntoa_variant(arg, 10);
|
||||
case FLOAT:
|
||||
return this.ftoa(float_from_variant(arg));
|
||||
case ARRAY:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz len = arg.type.len;
|
||||
// Pretend this is a char[]
|
||||
void* ptr = (void*)arg.ptr;
|
||||
this.out('[')?;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) this.out_substr(", ")?;
|
||||
this.out_str(variant { ptr, inner })?;
|
||||
ptr += size;
|
||||
}
|
||||
return this.out(']');
|
||||
case VECTOR:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz len = arg.type.len;
|
||||
// Pretend this is a char[]
|
||||
void* ptr = (void*)arg.ptr;
|
||||
this.out_substr("[<")?;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) this.out_substr(", ")?;
|
||||
this.out_str(variant { ptr, inner })?;
|
||||
ptr += size;
|
||||
}
|
||||
return this.out_substr(">]");
|
||||
case SUBARRAY:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
// this is SomeType[] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner == char.typeid)
|
||||
{
|
||||
return this.out_substr(*(char[]*)arg);
|
||||
}
|
||||
usz size = inner.sizeof;
|
||||
// Pretend this is a char[]
|
||||
char[]* temp = (void*)arg.ptr;
|
||||
void* ptr = (void*)temp.ptr;
|
||||
usz len = temp.len;
|
||||
this.out('[')?;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) this.out_substr(", ")?;
|
||||
this.out_str(variant { ptr, inner })?;
|
||||
ptr += size;
|
||||
}
|
||||
this.out(']')?;
|
||||
case BOOL:
|
||||
if (*(bool*)arg.ptr)
|
||||
{
|
||||
return this.out_substr("true");
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.out_substr("false");
|
||||
}
|
||||
default:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr("Invalid type");
|
||||
}
|
||||
}
|
||||
|
||||
fault FormattingFault
|
||||
{
|
||||
UNTERMINATED_FORMAT,
|
||||
MISSING_ARG,
|
||||
INVALID_WIDTH_ARG,
|
||||
INVALID_FORMAT_TYPE,
|
||||
}
|
||||
|
||||
|
||||
private fn void! out_buffer_fn(char c, void *data)
|
||||
{
|
||||
BufferData *buffer_data = data;
|
||||
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED!;
|
||||
buffer_data.buffer[buffer_data.written++] = c;
|
||||
}
|
||||
|
||||
private fn void! out_null_fn(char c @unused, void* data @unused)
|
||||
{
|
||||
}
|
||||
|
||||
private fn void! out_putchar_fn(char c, void* data @unused)
|
||||
{
|
||||
libc::putchar(c);
|
||||
}
|
||||
|
||||
private fn void! out_fputchar_fn(char c, void* data)
|
||||
{
|
||||
File* f = data;
|
||||
f.putc(c)?;
|
||||
}
|
||||
|
||||
private fn void! out_string_append_fn(char c, void* data)
|
||||
{
|
||||
String* s = data;
|
||||
s.append_char(c);
|
||||
}
|
||||
|
||||
fn usz! printf(char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn);
|
||||
return formatter.vprintf(format, args);
|
||||
}
|
||||
|
||||
fn usz! printfln(char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn);
|
||||
usz len = formatter.vprintf(format, args)?;
|
||||
putchar('\n');
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
fn usz! String.printf(String* str, char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_string_append_fn, str);
|
||||
return formatter.vprintf(format, args);
|
||||
}
|
||||
|
||||
fn usz! String.printfln(String* str, char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_string_append_fn, str);
|
||||
usz len = formatter.vprintf(format, args)?;
|
||||
str.append('\n');
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
private struct BufferData
|
||||
{
|
||||
char[] buffer;
|
||||
usz written;
|
||||
}
|
||||
|
||||
fn char[]! bprintf(char[] buffer, char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
BufferData data = { .buffer = buffer };
|
||||
formatter.init(&out_buffer_fn, &data);
|
||||
usz size = formatter.vprintf(format, args)?;
|
||||
return buffer[:size];
|
||||
}
|
||||
|
||||
fn usz! File.printf(File file, char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn, &file);
|
||||
return formatter.vprintf(format, args)?;
|
||||
}
|
||||
|
||||
fn usz! File.printfln(File file, char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn, &file);
|
||||
usz len = formatter.vprintf(format, args)?;
|
||||
file.putc('\n')?;
|
||||
file.flush();
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
fn usz! Formatter.printf(Formatter* this, char[] format, args...)
|
||||
{
|
||||
return this.vprintf(format, args) @inline;
|
||||
}
|
||||
|
||||
fn usz! Formatter.vprintf(Formatter* this, char[] format, variant[] variants)
|
||||
{
|
||||
if (!this.out_fn)
|
||||
{
|
||||
// use null output function
|
||||
this.out_fn = &out_null_fn;
|
||||
}
|
||||
usz format_len = format.len;
|
||||
usz variant_index = 0;
|
||||
for (usz i = 0; i < format_len; i++)
|
||||
{
|
||||
// format specifier? %[flags][width][.precision][length]
|
||||
char c = format[i];
|
||||
if (c != '%')
|
||||
{
|
||||
// no
|
||||
this.out(c)?;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
|
||||
c = format[i];
|
||||
if (c == '%')
|
||||
{
|
||||
this.out(c)?;
|
||||
continue;
|
||||
}
|
||||
// evaluate flags
|
||||
this.flags = {};
|
||||
while FLAG_EVAL: (true)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '0': this.flags.zeropad = true;
|
||||
case '-': this.flags.left = true;
|
||||
case '+': this.flags.plus = true;
|
||||
case ' ': this.flags.space = true;
|
||||
case '#': this.flags.hash = true;
|
||||
default: break FLAG_EVAL;
|
||||
}
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
|
||||
c = format[i];
|
||||
}
|
||||
// evaluate width field
|
||||
int w = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
|
||||
c = format[i];
|
||||
if (w < 0)
|
||||
{
|
||||
this.flags.left = true;
|
||||
w = -w;
|
||||
}
|
||||
this.width = w;
|
||||
// evaluate precision field
|
||||
this.prec = 0;
|
||||
if (c == '.')
|
||||
{
|
||||
this.flags.precision = true;
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
|
||||
int prec = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
|
||||
this.prec = prec < 0 ? 0 : prec;
|
||||
c = format[i];
|
||||
}
|
||||
|
||||
// evaluate specifier
|
||||
uint base = 0;
|
||||
if (variant_index >= variants.len) return PrintFault.MISSING_ARG!;
|
||||
variant current = variants[variant_index++];
|
||||
switch (c)
|
||||
{
|
||||
case 'd':
|
||||
base = 10;
|
||||
this.flags.hash = false;
|
||||
case 'X' :
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'x' :
|
||||
base = 16;
|
||||
case 'O':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'o' :
|
||||
base = 8;
|
||||
case 'B':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'b' :
|
||||
base = 2;
|
||||
case 'F' :
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'f':
|
||||
this.ftoa(float_from_variant(current))?;
|
||||
continue;
|
||||
case 'E':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'e':
|
||||
this.etoa(float_from_variant(current))?;
|
||||
continue;
|
||||
case 'G':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'g':
|
||||
this.flags.adapt_exp = true;
|
||||
this.etoa(float_from_variant(current))?;
|
||||
continue;
|
||||
case 'c':
|
||||
this.out_char(current)?;
|
||||
continue;
|
||||
case 's':
|
||||
this.out_str(current)?;
|
||||
continue;
|
||||
case 'p':
|
||||
this.flags.zeropad = true;
|
||||
this.flags.hash = true;
|
||||
base = 16;
|
||||
default:
|
||||
return PrintFault.INVALID_FORMAT_STRING!;
|
||||
}
|
||||
if (base != 10)
|
||||
{
|
||||
this.flags.plus = false;
|
||||
this.flags.space = false;
|
||||
}
|
||||
// ignore '0' flag when precision is given
|
||||
if (this.flags.precision) this.flags.zeropad = false;
|
||||
|
||||
bool is_neg;
|
||||
uint128 v = int_from_variant(current, &is_neg);
|
||||
|
||||
this.ntoa(v, is_neg, base)?;
|
||||
}
|
||||
// termination
|
||||
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
||||
|
||||
// return written chars without terminating \0
|
||||
return this.idx;
|
||||
}
|
||||
|
||||
47
lib/std/io/os/getcwd.c3
Normal file
47
lib/std/io/os/getcwd.c3
Normal file
@@ -0,0 +1,47 @@
|
||||
module std::io::os;
|
||||
import libc;
|
||||
|
||||
$if (env::OS_TYPE == OsType.WIN32):
|
||||
|
||||
extern fn Char16* _wgetcwd(Char16* buffer, int maxlen);
|
||||
extern fn usz wcslen(Char16* str);
|
||||
|
||||
macro char[]! getcwd(Allocator* allocator = mem::default_allocator())
|
||||
{
|
||||
const DEFAULT_BUFFER = 256;
|
||||
Char16[DEFAULT_BUFFER] buffer;
|
||||
Char16 *res = _wgetcwd(&buffer, DEFAULT_BUFFER);
|
||||
bool free = false;
|
||||
defer if (free) libc::free(res);
|
||||
if (!res)
|
||||
{
|
||||
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR!;
|
||||
res = _wgetcwd(null, 0);
|
||||
free = true;
|
||||
}
|
||||
Char16[] str16 = res[:wcslen(res)];
|
||||
return str::utf16to8(str16, allocator);
|
||||
}
|
||||
|
||||
$else:
|
||||
|
||||
extern fn ZString _getcwd(char* pwd, usz len) @extname("getcwd");
|
||||
macro char[]! getcwd(Allocator* allocator = mem::default_allocator())
|
||||
{
|
||||
const usz DEFAULT_BUFFER = 256;
|
||||
char[DEFAULT_BUFFER] buffer;
|
||||
ZString res = _getcwd(&buffer, DEFAULT_BUFFER);
|
||||
bool free = false;
|
||||
if (!res)
|
||||
{
|
||||
// Improve error
|
||||
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR!;
|
||||
res = _getcwd(null, 0);
|
||||
free = true;
|
||||
}
|
||||
defer if (free) libc::free((void*)res);
|
||||
char[] copy = str::copyz(res.as_str(), allocator);
|
||||
return copy;
|
||||
}
|
||||
|
||||
$endif;
|
||||
@@ -1,929 +0,0 @@
|
||||
module std::io;
|
||||
import libc;
|
||||
|
||||
const int PRINTF_NTOA_BUFFER_SIZE = 256;
|
||||
const int PRINTF_FTOA_BUFFER_SIZE = 256;
|
||||
const float PRINTF_MAX_FLOAT = 1e9;
|
||||
const uint PRINTF_DEFAULT_FLOAT_PRECISION = 6;
|
||||
|
||||
fault PrintFault
|
||||
{
|
||||
BUFFER_EXCEEDED,
|
||||
INTERNAL_BUFFER_EXCEEDED,
|
||||
INVALID_FORMAT_STRING,
|
||||
MISSING_ARG,
|
||||
}
|
||||
bitstruct PrintFlags : uint
|
||||
{
|
||||
bool zeropad : 0;
|
||||
bool left : 1;
|
||||
bool plus : 2;
|
||||
bool space : 3;
|
||||
bool hash : 4;
|
||||
bool uppercase : 5;
|
||||
bool precision : 6;
|
||||
bool adapt_exp : 7;
|
||||
}
|
||||
|
||||
struct PrintParam
|
||||
{
|
||||
OutputFn outfn;
|
||||
void* buffer;
|
||||
PrintFlags flags;
|
||||
uint width;
|
||||
uint prec;
|
||||
usize idx;
|
||||
}
|
||||
|
||||
define OutputFn = fn void!(char c, void* buffer, usize buffer_idx);
|
||||
|
||||
fn void! PrintParam.out(PrintParam* param, char c)
|
||||
{
|
||||
|
||||
param.outfn(c, param.buffer, param.idx++)?;
|
||||
}
|
||||
|
||||
private fn void! out_str(PrintParam* param, variant arg)
|
||||
{
|
||||
switch (arg.type.kind)
|
||||
{
|
||||
case TYPEID:
|
||||
return out_substr(param, "<typeid>");
|
||||
case VOID:
|
||||
return out_substr(param, "void");
|
||||
case ANYERR:
|
||||
case FAULT:
|
||||
return out_substr(param, (*(anyerr*)arg.ptr).nameof);
|
||||
case VARIANT:
|
||||
return out_substr(param, "<variant>");
|
||||
case ENUM:
|
||||
return out_substr(param, arg.type.names[types::variant_to_int(arg, usize)!!]);
|
||||
case STRUCT:
|
||||
return out_substr(param, "<struct>");
|
||||
case UNION:
|
||||
return out_substr(param, "<union>");
|
||||
case BITSTRUCT:
|
||||
return out_substr(param, "<bitstruct>");
|
||||
case FUNC:
|
||||
return out_substr(param, "<func>");
|
||||
case FAILABLE:
|
||||
unreachable();
|
||||
case DISTINCT:
|
||||
if (arg.type == String.typeid)
|
||||
{
|
||||
return out_substr(param, ((String*)arg).str());
|
||||
}
|
||||
return out_str(param, variant { arg.ptr, arg.type.inner });
|
||||
case POINTER:
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner.kind == TypeKind.ARRAY && inner.inner == char.typeid)
|
||||
{
|
||||
char *ptr = *(char**)arg.ptr;
|
||||
return out_substr(param, ptr[:inner.len]);
|
||||
}
|
||||
return ntoa_variant(param, arg, 16);
|
||||
case SIGNED_INT:
|
||||
case UNSIGNED_INT:
|
||||
return ntoa_variant(param, arg, 10);
|
||||
case FLOAT:
|
||||
return ftoa(param, float_from_variant(arg));
|
||||
case ARRAY:
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usize size = inner.sizeof;
|
||||
usize len = arg.type.len;
|
||||
// Pretend this is a char[]
|
||||
void* ptr = (void*)arg.ptr;
|
||||
param.out('[')?;
|
||||
for (usize i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) out_substr(param, ", ")?;
|
||||
out_str(param, variant { ptr, inner })?;
|
||||
ptr += size;
|
||||
}
|
||||
return param.out(']');
|
||||
case VECTOR:
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usize size = inner.sizeof;
|
||||
usize len = arg.type.len;
|
||||
// Pretend this is a char[]
|
||||
void* ptr = (void*)arg.ptr;
|
||||
out_substr(param, "[<")?;
|
||||
for (usize i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) out_substr(param, ", ")?;
|
||||
out_str(param, variant { ptr, inner })?;
|
||||
ptr += size;
|
||||
}
|
||||
return out_substr(param, ">]");
|
||||
case SUBARRAY:
|
||||
// this is SomeType[] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner == char.typeid)
|
||||
{
|
||||
return out_substr(param, *(char[]*)arg);
|
||||
}
|
||||
usize size = inner.sizeof;
|
||||
// Pretend this is a char[]
|
||||
char[]* temp = (void*)arg.ptr;
|
||||
void* ptr = (void*)temp.ptr;
|
||||
usize len = temp.len;
|
||||
param.out('[')?;
|
||||
for (usize i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) out_substr(param, ", ")?;
|
||||
out_str(param, variant { ptr, inner })?;
|
||||
ptr += size;
|
||||
}
|
||||
param.out(']')?;
|
||||
case BOOL:
|
||||
if (*(bool*)arg.ptr)
|
||||
{
|
||||
return out_substr(param, "true");
|
||||
}
|
||||
else
|
||||
{
|
||||
return out_substr(param, "false");
|
||||
}
|
||||
default:
|
||||
return out_substr(param, "Invalid type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fn uint simple_atoi(char* buf, usize maxlen, usize* len_ptr) @inline
|
||||
{
|
||||
uint i = 0;
|
||||
usize len = *len_ptr;
|
||||
while (len < maxlen)
|
||||
{
|
||||
char c = buf[len];
|
||||
if (c < '0' || c > '9') break;
|
||||
i = i * 10 + c - '0';
|
||||
len++;
|
||||
}
|
||||
*len_ptr = len;
|
||||
return i;
|
||||
}
|
||||
|
||||
fault FormattingFault
|
||||
{
|
||||
UNTERMINATED_FORMAT,
|
||||
MISSING_ARG,
|
||||
INVALID_WIDTH_ARG,
|
||||
INVALID_FORMAT_TYPE,
|
||||
}
|
||||
|
||||
private fn void! printf_advance_format(usize format_len, usize *index_ptr) @inline
|
||||
{
|
||||
usize val = ++(*index_ptr);
|
||||
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT!;
|
||||
}
|
||||
|
||||
private fn variant! next_variant(variant* args_ptr, usize args_len, usize* arg_index_ptr) @inline
|
||||
{
|
||||
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG!;
|
||||
return args_ptr[(*arg_index_ptr)++];
|
||||
}
|
||||
|
||||
private fn int! printf_parse_format_field(variant* args_ptr, usize args_len, usize* args_index_ptr, char* format_ptr, usize format_len, usize* index_ptr) @inline
|
||||
{
|
||||
char c = format_ptr[*index_ptr];
|
||||
if (c >= '0' && c <= '9') return simple_atoi(format_ptr, format_len, index_ptr);
|
||||
if (c != '*') return 0;
|
||||
printf_advance_format(format_len, index_ptr)?;
|
||||
variant val = next_variant(args_ptr, args_len, args_index_ptr)?;
|
||||
if (!val.type.kind.is_int()) return FormattingFault.INVALID_WIDTH_ARG!;
|
||||
uint! intval = types::variant_to_int(val, int);
|
||||
if (catch intval) return FormattingFault.INVALID_WIDTH_ARG!;
|
||||
return intval;
|
||||
}
|
||||
|
||||
|
||||
private fn void! out_buffer_fn(char c, char[] buffer, usize buffer_idx)
|
||||
{
|
||||
if (buffer_idx >= buffer.len) return PrintFault.BUFFER_EXCEEDED!;
|
||||
buffer[buffer_idx] = c;
|
||||
}
|
||||
|
||||
private fn void! out_null_fn(char c @unused, void* data @unused, usize idx @unused)
|
||||
{
|
||||
}
|
||||
|
||||
private fn void! out_putchar_fn(char c, void* data @unused, usize idx @unused)
|
||||
{
|
||||
libc::putchar(c);
|
||||
}
|
||||
|
||||
private fn void! out_fputchar_fn(char c, void* data, usize idx @unused)
|
||||
{
|
||||
File* f = data;
|
||||
f.putc(c)?;
|
||||
}
|
||||
|
||||
private fn void! out_string_append_fn(char c, void* data, usize idx @unused)
|
||||
{
|
||||
String* s = data;
|
||||
s.append_char(c);
|
||||
}
|
||||
|
||||
private fn void! PrintParam.out_reverse(PrintParam* param, char[] buf)
|
||||
{
|
||||
usize buffer_start_idx = param.idx;
|
||||
usize len = buf.len;
|
||||
// pad spaces up to given width
|
||||
if (!param.flags.left && !param.flags.zeropad)
|
||||
{
|
||||
for (usize i = len; i < param.width; i++)
|
||||
{
|
||||
param.out(' ')?;
|
||||
}
|
||||
}
|
||||
// reverse string
|
||||
while (len) param.out(buf[--len])?;
|
||||
|
||||
// append pad spaces up to given width
|
||||
return param.left_adjust(param.idx - buffer_start_idx);
|
||||
}
|
||||
|
||||
private fn void! out_char(PrintParam* param, variant arg)
|
||||
{
|
||||
uint l = 1;
|
||||
// pre padding
|
||||
param.right_adjust(l)?;
|
||||
// char output
|
||||
Char32 c = types::variant_to_int(arg, uint) ?? 0xFFFD;
|
||||
switch (true)
|
||||
{
|
||||
case c < 0x7f:
|
||||
param.out((char)c)?;
|
||||
case c < 0x7ff:
|
||||
param.out((char)(0xC0 | c >> 6))?;
|
||||
param.out((char)(0x80 | (c & 0x3F)))?;
|
||||
case c < 0xffff:
|
||||
param.out((char)(0xE0 | c >> 12))?;
|
||||
param.out((char)(0x80 | (c >> 6 & 0x3F)))?;
|
||||
param.out((char)(0x80 | (c & 0x3F)))?;
|
||||
default:
|
||||
param.out((char)(0xF0 | c >> 18))?;
|
||||
param.out((char)(0x80 | (c >> 12 & 0x3F)))?;
|
||||
param.out((char)(0x80 | (c >> 6 & 0x3F)))?;
|
||||
param.out((char)(0x80 | (c & 0x3F)))?;
|
||||
}
|
||||
return param.left_adjust(l);
|
||||
}
|
||||
|
||||
private fn void! ntoa_format(PrintParam* param, char[] buf, usize len, bool negative, uint base)
|
||||
{
|
||||
// pad leading zeros
|
||||
if (!param.flags.left)
|
||||
{
|
||||
if (param.width && param.flags.zeropad && (negative || param.flags.plus || param.flags.space)) param.width--;
|
||||
while (len < param.prec)
|
||||
{
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
while (param.flags.zeropad && len < param.width)
|
||||
{
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
// handle hash
|
||||
if (param.flags.hash && base != 10)
|
||||
{
|
||||
if (!param.flags.precision && len && len == param.prec && len == param.width)
|
||||
{
|
||||
len--;
|
||||
if (len) len--;
|
||||
}
|
||||
if (base != 10)
|
||||
{
|
||||
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
switch (base)
|
||||
{
|
||||
case 16:
|
||||
buf[len++] = param.flags.uppercase ? 'X' : 'x';
|
||||
case 8:
|
||||
buf[len++] = param.flags.uppercase ? 'O' : 'o';
|
||||
case 2:
|
||||
buf[len++] = param.flags.uppercase ? 'B' : 'b';
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
switch (true)
|
||||
{
|
||||
case negative:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '-';
|
||||
case param.flags.plus:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '+';
|
||||
case param.flags.space:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
if (!len) return;
|
||||
return param.out_reverse(buf[:len]);
|
||||
}
|
||||
|
||||
$if (env::I128_SUPPORT):
|
||||
define NtoaType = uint128;
|
||||
$else:
|
||||
define NtoaType = ulong;
|
||||
$endif;
|
||||
|
||||
private fn void! ntoa_variant(PrintParam* param, variant arg, uint base)
|
||||
{
|
||||
bool is_neg;
|
||||
NtoaType val = int_from_variant(arg, &is_neg);
|
||||
return ntoa(param, val, is_neg, base) @inline;
|
||||
}
|
||||
|
||||
private fn void! ntoa(PrintParam* param, NtoaType value, bool negative, uint base)
|
||||
{
|
||||
char[PRINTF_NTOA_BUFFER_SIZE] buf = void;
|
||||
usize len = 0;
|
||||
|
||||
// no hash for 0 values
|
||||
if (!value) param.flags.hash = false;
|
||||
|
||||
// write if precision != 0 or value is != 0
|
||||
if (!param.flags.precision || value)
|
||||
{
|
||||
char past_10 = (param.flags.uppercase ? 'A' : 'a') - 10;
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
char digit = (char)(value % base);
|
||||
buf[len++] = digit + (digit < 10 ? '0' : past_10);
|
||||
value /= base;
|
||||
}
|
||||
while (value);
|
||||
}
|
||||
return ntoa_format(param, buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
|
||||
}
|
||||
|
||||
|
||||
define FloatType = double;
|
||||
|
||||
// internal ftoa for fixed decimal floating point
|
||||
private fn void! ftoa(PrintParam* param, FloatType value)
|
||||
{
|
||||
char[PRINTF_FTOA_BUFFER_SIZE] buf = void;
|
||||
usize len = 0;
|
||||
const FloatType[] POW10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
|
||||
FloatType diff = 0.0;
|
||||
|
||||
// powers of 10
|
||||
|
||||
// test for special values
|
||||
if (value != value)
|
||||
{
|
||||
return param.out_reverse("nan");
|
||||
}
|
||||
if (value < -FloatType.max)
|
||||
{
|
||||
return param.out_reverse("fni-");
|
||||
}
|
||||
if (value > FloatType.max)
|
||||
{
|
||||
if (param.flags.plus)
|
||||
{
|
||||
return param.out_reverse("fni+");
|
||||
}
|
||||
return param.out_reverse("fni");
|
||||
}
|
||||
|
||||
// test for very large values
|
||||
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
|
||||
if (value > PRINTF_MAX_FLOAT || value < -PRINTF_MAX_FLOAT)
|
||||
{
|
||||
return etoa(param, value);
|
||||
}
|
||||
|
||||
// test for negative
|
||||
bool negative = value < 0;
|
||||
if (negative) value = 0 - value;
|
||||
|
||||
// set default precision, if not set explicitly
|
||||
if (!param.flags.precision) param.prec = PRINTF_DEFAULT_FLOAT_PRECISION;
|
||||
|
||||
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
|
||||
while (param.prec > 9)
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
param.prec--;
|
||||
}
|
||||
|
||||
// Safe due to 1e9 limit.
|
||||
int whole = (int)value;
|
||||
FloatType tmp = (value - whole) * POW10[param.prec];
|
||||
ulong frac = (ulong)tmp;
|
||||
diff = tmp - frac;
|
||||
|
||||
switch (true)
|
||||
{
|
||||
case diff > 0.5:
|
||||
++frac;
|
||||
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
|
||||
if (frac >= POW10[param.prec])
|
||||
{
|
||||
frac = 0;
|
||||
++whole;
|
||||
}
|
||||
case diff < 0.5:
|
||||
break;
|
||||
case !frac && (frac & 1):
|
||||
// if halfway, round up if odd OR if last digit is 0
|
||||
++frac;
|
||||
}
|
||||
if (!param.prec)
|
||||
{
|
||||
diff = value - (FloatType)whole;
|
||||
if ((!(diff < 0.5) || diff > 0.5) && (whole & 1))
|
||||
{
|
||||
// exactly 0.5 and ODD, then round up
|
||||
// 1.5 -> 2, but 2.5 -> 2
|
||||
++whole;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint count = param.prec;
|
||||
// now do fractional part, as an unsigned number
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
--count;
|
||||
buf[len++] = (char)(48 + (frac % 10));
|
||||
}
|
||||
while (frac /= 10);
|
||||
// add extra 0s
|
||||
while (count-- > 0)
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
// add decimal
|
||||
buf[len++] = '.';
|
||||
}
|
||||
|
||||
// do whole part, number is reversed
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = (char)(48 + (whole % 10));
|
||||
}
|
||||
while (whole /= 10);
|
||||
|
||||
// pad leading zeros
|
||||
if (!param.flags.left && param.flags.zeropad)
|
||||
{
|
||||
if (param.width && (negative || param.flags.plus || param.flags.space)) param.width--;
|
||||
while (len < param.width)
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
char next = {|
|
||||
if (negative) return '-';
|
||||
if (param.flags.plus) return '+';
|
||||
if (param.flags.space) return ' ';
|
||||
return 0;
|
||||
|};
|
||||
if (next)
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = next;
|
||||
}
|
||||
return param.out_reverse(buf[:len]);
|
||||
}
|
||||
|
||||
union ConvUnion
|
||||
{
|
||||
ulong u;
|
||||
double f;
|
||||
}
|
||||
|
||||
private fn void! etoa(PrintParam* param, FloatType value)
|
||||
{
|
||||
// check for NaN and special values
|
||||
if (value != value || value < FloatType.min || value > FloatType.max)
|
||||
{
|
||||
return ftoa(param, value);
|
||||
}
|
||||
|
||||
// determine the sign
|
||||
bool negative = value < 0;
|
||||
if (negative) value = -value;
|
||||
|
||||
// default precision
|
||||
if (!param.flags.precision)
|
||||
{
|
||||
param.prec = PRINTF_DEFAULT_FLOAT_PRECISION;
|
||||
}
|
||||
|
||||
// determine the decimal exponent
|
||||
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
|
||||
ConvUnion conv;
|
||||
|
||||
conv.f = (double)value;
|
||||
int exp2 = (int)(conv.u >> 52 & 0x7FF) - 1023; // effectively log2
|
||||
conv.u = (conv.u & (1u64 << 52 - 1)) | (1023u64 << 52); // drop the exponent so conv.F is now in [1,2)
|
||||
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
|
||||
int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.f - 1.5) * 0.289529654602168);
|
||||
// now we want to compute 10^expval but we want to be sure it won't overflow
|
||||
exp2 = (int)(expval * 3.321928094887362 + 0.5);
|
||||
double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
|
||||
double z2 = z * z;
|
||||
conv.u = (ulong)(exp2 + 1023) << 52;
|
||||
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
|
||||
conv.f *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
|
||||
// correct for rounding errors
|
||||
if (value < conv.f)
|
||||
{
|
||||
expval--;
|
||||
conv.f /= 10;
|
||||
}
|
||||
|
||||
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
|
||||
uint minwidth = ((expval < 100) && (expval > -100)) ? 4 : 5;
|
||||
|
||||
// in "%g" mode, "prec" is the number of *significant figures* not decimals
|
||||
if (param.flags.adapt_exp)
|
||||
{
|
||||
// do we want to fall-back to "%f" mode?
|
||||
if (value >= 1e-4 && value < 1e6)
|
||||
{
|
||||
param.prec = param.prec > expval ? param.prec - expval - 1 : 0;
|
||||
param.flags.precision = true; // make sure ftoa respects precision
|
||||
// no characters in exponent
|
||||
minwidth = 0;
|
||||
expval = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we use one sigfig for the whole part
|
||||
if (param.prec > 0 && param.flags.precision) param.prec--;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust width
|
||||
uint fwidth = param.width > minwidth ? param.width - minwidth : 0;
|
||||
|
||||
// if we're padding on the right, DON'T pad the floating part
|
||||
if (param.flags.left && minwidth) fwidth = 0;
|
||||
|
||||
// rescale the float value
|
||||
if (expval) value /= conv.f;
|
||||
|
||||
// output the floating part
|
||||
usize start_idx = param.idx;
|
||||
PrintFlags old = param.flags;
|
||||
param.flags.adapt_exp = false;
|
||||
param.width = fwidth;
|
||||
ftoa(param, negative ? -value : value)?;
|
||||
param.flags = old;
|
||||
|
||||
// output the exponent part
|
||||
if (minwidth)
|
||||
{
|
||||
// output the exponential symbol
|
||||
param.out(param.flags.uppercase ? 'E' : 'e')?;
|
||||
// output the exponent value
|
||||
param.flags = { .zeropad = true, .plus = true };
|
||||
param.width = minwidth - 1;
|
||||
param.prec = 0;
|
||||
ntoa(param, (NtoaType)(expval < 0 ? -expval : expval), expval < 0, 10)?;
|
||||
param.flags = old;
|
||||
// might need to right-pad spaces
|
||||
param.left_adjust(param.idx - start_idx)?;
|
||||
}
|
||||
}
|
||||
|
||||
private fn FloatType float_from_variant(variant arg)
|
||||
{
|
||||
$if (env::I128_SUPPORT):
|
||||
switch (arg)
|
||||
{
|
||||
case int128:
|
||||
return *arg;
|
||||
case uint128:
|
||||
return *arg;
|
||||
}
|
||||
$endif;
|
||||
$if (env::F128_SUPPORT):
|
||||
if (arg.type == float128.typeid) return *((float128*)arg.ptr);
|
||||
$endif;
|
||||
$if (env::F16_SUPPORT):
|
||||
if (arg.type == float16.typeid) return *((float16*)arg.ptr);
|
||||
$endif;
|
||||
|
||||
if (arg.type.kind == TypeKind.POINTER)
|
||||
{
|
||||
return (FloatType)(uptr)(void*)arg.ptr;
|
||||
}
|
||||
switch (arg)
|
||||
{
|
||||
case bool:
|
||||
return (FloatType)*arg;
|
||||
case ichar:
|
||||
return *arg;
|
||||
case short:
|
||||
return *arg;
|
||||
case int:
|
||||
return *arg;
|
||||
case long:
|
||||
return *arg;
|
||||
case char:
|
||||
return *arg;
|
||||
case ushort:
|
||||
return *arg;
|
||||
case uint:
|
||||
return *arg;
|
||||
case ulong:
|
||||
return *arg;
|
||||
case float:
|
||||
return (FloatType)*arg;
|
||||
case double:
|
||||
return (FloatType)*arg;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private fn NtoaType int_from_variant(variant arg, bool *is_neg)
|
||||
{
|
||||
*is_neg = false;
|
||||
$if (NtoaType.typeid == uint128.typeid):
|
||||
switch (arg)
|
||||
{
|
||||
case int128:
|
||||
int128 val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : val;
|
||||
case uint128:
|
||||
return *arg;
|
||||
}
|
||||
$endif;
|
||||
|
||||
if (arg.type.kind == TypeKind.POINTER)
|
||||
{
|
||||
return (NtoaType)(uptr)*(void**)arg.ptr;
|
||||
}
|
||||
switch (arg)
|
||||
{
|
||||
case bool:
|
||||
return (NtoaType)*arg;
|
||||
case ichar:
|
||||
int val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val;
|
||||
case short:
|
||||
int val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val;
|
||||
case int:
|
||||
int val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val;
|
||||
case long:
|
||||
long val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(NtoaType)val) + 1 : (NtoaType)val;
|
||||
case char:
|
||||
return *arg;
|
||||
case ushort:
|
||||
return *arg;
|
||||
case uint:
|
||||
return *arg;
|
||||
case ulong:
|
||||
return *arg;
|
||||
case float:
|
||||
float f = *arg;
|
||||
return (NtoaType)((*is_neg = f < 0) ? -f : f);
|
||||
case double:
|
||||
double d = *arg;
|
||||
return (NtoaType)((*is_neg = d < 0) ? -d : d);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn usize! printf(char[] format, args...) @maydiscard
|
||||
{
|
||||
return vsnprintf(&out_putchar_fn, null, format, args);
|
||||
}
|
||||
|
||||
fn usize! printfln(char[] format, args...) @maydiscard
|
||||
{
|
||||
usize size = vsnprintf(&out_putchar_fn, null, format, args)?;
|
||||
putchar('\n');
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
fn usize! String.printf(String* str, char[] format, args...) @maydiscard
|
||||
{
|
||||
return vsnprintf(&out_string_append_fn, str, format, args);
|
||||
}
|
||||
|
||||
fn usize! String.printfln(String* str, char[] format, args...) @maydiscard
|
||||
{
|
||||
usize size = vsnprintf(&out_string_append_fn, str, format, args)?;
|
||||
str.append('\n');
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
fn usize! File.printf(File file, char[] format, args...) @maydiscard
|
||||
{
|
||||
return vsnprintf(&out_putchar_fn, &file, format, args);
|
||||
}
|
||||
|
||||
fn usize! File.printfln(File file, char[] format, args...) @maydiscard
|
||||
{
|
||||
usize size = vsnprintf(&out_putchar_fn, &file, format, args)?;
|
||||
file.putc('\n')?;
|
||||
file.flush();
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
private fn void! PrintParam.left_adjust(PrintParam* param, usize len)
|
||||
{
|
||||
if (!param.flags.left) return;
|
||||
for (usize l = len; l < param.width; l++) param.out(' ')?;
|
||||
}
|
||||
|
||||
private fn void! PrintParam.right_adjust(PrintParam* param, usize len)
|
||||
{
|
||||
if (param.flags.left) return;
|
||||
for (usize l = len; l < param.width; l++) param.out(' ')?;
|
||||
}
|
||||
|
||||
private fn void! out_substr(PrintParam* param, char[] str)
|
||||
{
|
||||
usize l = conv::utf8_codepoints(str);
|
||||
uint prec = param.prec;
|
||||
if (param.flags.precision && l < prec) l = prec;
|
||||
param.right_adjust(' ')?;
|
||||
usize index = 0;
|
||||
usize chars = str.len;
|
||||
char* ptr = str.ptr;
|
||||
while (index < chars)
|
||||
{
|
||||
char c = ptr[index];
|
||||
// Break if we have precision set and we ran out...
|
||||
if (c & 0xC0 != 0x80 && param.flags.precision && !prec--) break;
|
||||
param.out(c)?;
|
||||
index++;
|
||||
}
|
||||
return param.left_adjust(l);
|
||||
}
|
||||
|
||||
private fn usize! vsnprintf(OutputFn out, void* data, char[] format, variant[] variants)
|
||||
{
|
||||
if (!out)
|
||||
{
|
||||
// use null output function
|
||||
out = &out_null_fn;
|
||||
}
|
||||
PrintParam param = { .outfn = out, .buffer = data };
|
||||
usize format_len = format.len;
|
||||
usize variant_index = 0;
|
||||
for (usize i = 0; i < format_len; i++)
|
||||
{
|
||||
// format specifier? %[flags][width][.precision][length]
|
||||
char c = format[i];
|
||||
if (c != '%')
|
||||
{
|
||||
// no
|
||||
param.out(c)?;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
|
||||
c = format[i];
|
||||
if (c == '%')
|
||||
{
|
||||
param.out(c)?;
|
||||
continue;
|
||||
}
|
||||
// evaluate flags
|
||||
param.flags = {};
|
||||
while FLAG_EVAL: (true)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '0': param.flags.zeropad = true;
|
||||
case '-': param.flags.left = true;
|
||||
case '+': param.flags.plus = true;
|
||||
case ' ': param.flags.space = true;
|
||||
case '#': param.flags.hash = true;
|
||||
default: break FLAG_EVAL;
|
||||
}
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
|
||||
c = format[i];
|
||||
}
|
||||
// evaluate width field
|
||||
int w = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
|
||||
c = format[i];
|
||||
if (w < 0)
|
||||
{
|
||||
param.flags.left = true;
|
||||
w = -w;
|
||||
}
|
||||
param.width = w;
|
||||
// evaluate precision field
|
||||
param.prec = 0;
|
||||
if (c == '.')
|
||||
{
|
||||
param.flags.precision = true;
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
|
||||
int prec = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
|
||||
param.prec = prec < 0 ? 0 : prec;
|
||||
c = format[i];
|
||||
}
|
||||
|
||||
// evaluate specifier
|
||||
uint base = 0;
|
||||
if (variant_index >= variants.len) return PrintFault.MISSING_ARG!;
|
||||
variant current = variants[variant_index++];
|
||||
switch (c)
|
||||
{
|
||||
case 'd':
|
||||
base = 10;
|
||||
param.flags.hash = false;
|
||||
case 'X' :
|
||||
param.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'x' :
|
||||
base = 16;
|
||||
case 'O':
|
||||
param.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'o' :
|
||||
base = 8;
|
||||
case 'B':
|
||||
param.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'b' :
|
||||
base = 2;
|
||||
case 'F' :
|
||||
param.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'f':
|
||||
ftoa(¶m, float_from_variant(current))?;
|
||||
continue;
|
||||
case 'E':
|
||||
param.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'e':
|
||||
etoa(¶m, float_from_variant(current))?;
|
||||
continue;
|
||||
case 'G':
|
||||
param.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'g':
|
||||
param.flags.adapt_exp = true;
|
||||
etoa(¶m, float_from_variant(current))?;
|
||||
continue;
|
||||
case 'c':
|
||||
out_char(¶m, current)?;
|
||||
continue;
|
||||
case 's':
|
||||
out_str(¶m, current)?;
|
||||
continue;
|
||||
case 'p':
|
||||
param.flags.zeropad = true;
|
||||
param.flags.hash = true;
|
||||
base = 16;
|
||||
default:
|
||||
return PrintFault.INVALID_FORMAT_STRING!;
|
||||
}
|
||||
if (base != 10)
|
||||
{
|
||||
param.flags.plus = false;
|
||||
param.flags.space = false;
|
||||
}
|
||||
// ignore '0' flag when precision is given
|
||||
if (param.flags.precision) param.flags.zeropad = false;
|
||||
|
||||
bool is_neg;
|
||||
NtoaType v = int_from_variant(current, &is_neg);
|
||||
|
||||
ntoa(¶m, v, is_neg, base)?;
|
||||
}
|
||||
// termination
|
||||
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
||||
|
||||
// return written chars without terminating \0
|
||||
return param.idx;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module libc;
|
||||
|
||||
|
||||
// stdlib
|
||||
|
||||
|
||||
@@ -26,17 +28,13 @@ struct LongDivResult
|
||||
|
||||
fn Errno errno()
|
||||
{
|
||||
$if (env::OS_TYPE == OsType.WIN32):
|
||||
return (Errno)windows::errno();
|
||||
$elif (env::OS_TYPE == OsType.MACOSX):
|
||||
return (Errno)macos::errno();
|
||||
$elif (env::OS_TYPE == OsType.LINUX):
|
||||
return (Errno)linux::errno();
|
||||
$else:
|
||||
return errno::ENOTRECOVERABLE;
|
||||
$endif;
|
||||
return (Errno)os::errno();
|
||||
}
|
||||
|
||||
fn void errno_set(Errno e)
|
||||
{
|
||||
os::errno_set((int)e);
|
||||
}
|
||||
|
||||
define TerminateFunction = fn void();
|
||||
define CompareFunction = fn int(void*, void*);
|
||||
@@ -51,8 +49,8 @@ extern fn void atexit(TerminateFunction f);
|
||||
extern fn void exit(int status);
|
||||
extern fn char* getenv(char* name);
|
||||
extern fn int system(char* str);
|
||||
extern fn void bsearch(void* key, void *base, usize items, usize size, CompareFunction compare);
|
||||
extern fn void qsort(void* base, usize items, usize size, CompareFunction compare);
|
||||
extern fn void bsearch(void* key, void *base, usz items, usz size, CompareFunction compare);
|
||||
extern fn void qsort(void* base, usz items, usz size, CompareFunction compare);
|
||||
extern fn int abs(int x);
|
||||
extern fn DivResult div(int numer, int denom);
|
||||
extern fn long labs(long x);
|
||||
@@ -60,36 +58,46 @@ extern fn LongDivResult ldiv(long number, long denom);
|
||||
extern fn int rand();
|
||||
extern fn void srand(uint seed);
|
||||
|
||||
define JmpBuf = CInt[$$JMP_BUF_SIZE];
|
||||
|
||||
extern fn void longjmp(JmpBuf* buffer, CInt value);
|
||||
$if (env::OS_TYPE == OsType.WIN32):
|
||||
// TODO win32 aarch64
|
||||
extern fn CInt _setjmp(void* frameptr, JmpBuf* buffer);
|
||||
macro CInt setjmp(JmpBuf* buffer) = _setjmp($$frameaddress(), buffer);
|
||||
$else:
|
||||
extern fn CInt setjmp(JmpBuf* buffer);
|
||||
$endif;
|
||||
// MB functions omitted
|
||||
|
||||
// string
|
||||
extern fn void* memchr(void* str, int c, usize n);
|
||||
extern fn int memcmp(void* str1, void* str2, usize n);
|
||||
extern fn void* memcpy(void* dest, void* src, usize n);
|
||||
extern fn void* memmove(void* dest, void* src, usize n);
|
||||
extern fn void* memset(void* dest, usize n);
|
||||
extern fn void* memchr(void* str, int c, usz n);
|
||||
extern fn int memcmp(void* str1, void* str2, usz n);
|
||||
extern fn void* memcpy(void* dest, void* src, usz n);
|
||||
extern fn void* memmove(void* dest, void* src, usz n);
|
||||
extern fn void* memset(void* dest, usz n);
|
||||
extern fn char* strcat(char* dest, char* src);
|
||||
extern fn char* strncat(char* dest, char* src, usize n);
|
||||
extern fn char* strncat(char* dest, char* src, usz n);
|
||||
extern fn char* strchr(char* str, int c);
|
||||
extern fn int strcmp(char* str1, char* str2);
|
||||
extern fn int strncmp(char* str1, char* str2, usize n);
|
||||
extern fn int strncmp(char* str1, char* str2, usz n);
|
||||
extern fn int strcoll(char* str1, char* str2);
|
||||
extern fn char* strcpy(char* dst, char* src);
|
||||
extern fn char* strncpy(char* dst, char* src, usize n);
|
||||
extern fn usize strcspn(char* str1, char* str2);
|
||||
extern fn char* strncpy(char* dst, char* src, usz n);
|
||||
extern fn usz strcspn(char* str1, char* str2);
|
||||
extern fn char* strerror(int errn);
|
||||
extern fn usize strlen(char* str);
|
||||
extern fn usz strlen(char* str);
|
||||
extern fn char* strpbrk(char* str1, char* str2);
|
||||
extern fn usize strspn(char* str1, char* str2);
|
||||
extern fn usz strspn(char* str1, char* str2);
|
||||
extern fn char* strstr(char* haystack, char* needle);
|
||||
extern fn char* strtok(char* str, char* delim);
|
||||
extern fn usize strxfrm(char* dest, char* src, usize n);
|
||||
extern fn usz strxfrm(char* dest, char* src, usz n);
|
||||
|
||||
// malloc
|
||||
extern fn void* malloc(usize size);
|
||||
extern fn void* calloc(usize count, usize size);
|
||||
extern fn void* malloc(usz size);
|
||||
extern fn void* calloc(usz count, usz size);
|
||||
extern fn void* free(void*);
|
||||
extern fn void* realloc(void* ptr, usize size);
|
||||
extern fn void* realloc(void* ptr, usz size);
|
||||
|
||||
// stdio
|
||||
|
||||
@@ -101,9 +109,9 @@ $case OsType.LINUX:
|
||||
extern CFile __stdin @extname("stdin");
|
||||
extern CFile __stdout @extname("stdout");
|
||||
extern CFile __stderr @extname("stderr");
|
||||
extern fn usize malloc_usable_size(void* ptr);
|
||||
macro usize malloc_size(void* ptr) { return malloc_usable_size(ptr); }
|
||||
extern fn void* aligned_alloc(usize align, usize size);
|
||||
extern fn usz malloc_usable_size(void* ptr);
|
||||
macro usz malloc_size(void* ptr) { return malloc_usable_size(ptr); }
|
||||
extern fn void* aligned_alloc(usz align, usz size);
|
||||
macro CFile stdin() { return __stdin; }
|
||||
macro CFile stdout() { return __stdout; }
|
||||
macro CFile stderr() { return __stderr; }
|
||||
@@ -111,15 +119,15 @@ $case OsType.MACOSX:
|
||||
extern CFile __stdinp;
|
||||
extern CFile __stdoutp;
|
||||
extern CFile __stderrp;
|
||||
extern fn usize malloc_size(void* ptr);
|
||||
extern fn void* aligned_alloc(usize align, usize size);
|
||||
extern fn usz malloc_size(void* ptr);
|
||||
extern fn void* aligned_alloc(usz align, usz size);
|
||||
macro CFile stdin() { return __stdinp; }
|
||||
macro CFile stdout() { return __stdoutp; }
|
||||
macro CFile stderr() { return __stderrp; }
|
||||
$case OsType.WIN32:
|
||||
extern fn CFile __acrt_iob_func(CInt c);
|
||||
extern fn usize _msize(void* ptr);
|
||||
macro usize malloc_size(void* ptr) { return _msize(ptr); }
|
||||
extern fn usz _msize(void* ptr);
|
||||
macro usz malloc_size(void* ptr) { return _msize(ptr); }
|
||||
macro CFile stdin() { return __acrt_iob_func(0); }
|
||||
macro CFile stdout() { return __acrt_iob_func(1); }
|
||||
macro CFile stderr() { return __acrt_iob_func(2); }
|
||||
@@ -129,6 +137,7 @@ $default:
|
||||
macro CFile stderr() { return (CFile*)(uptr)2; }
|
||||
$endswitch;
|
||||
|
||||
|
||||
const HAS_MALLOC_SIZE =
|
||||
env::OS_TYPE == OsType.LINUX
|
||||
|| env::OS_TYPE == OsType.WIN32
|
||||
@@ -157,22 +166,22 @@ extern fn int ferror(CFile stream);
|
||||
extern fn int fflush(CFile stream);
|
||||
extern fn int fgetpos(CFile stream, Fpos* pos);
|
||||
extern fn CFile fopen(char* filename, char* mode);
|
||||
extern fn usize fread(void* ptr, usize size, usize nmemb, CFile stream);
|
||||
extern fn usz fread(void* ptr, usz size, usz nmemb, CFile stream);
|
||||
extern fn CFile freopen(char* filename, char* mode, CFile stream);
|
||||
extern fn int fseek(CFile stream, SeekIndex offset, int whence);
|
||||
extern fn int fsetpos(CFile stream, Fpos* pos);
|
||||
extern fn SeekIndex ftell(CFile stream);
|
||||
extern fn usize fwrite(void* ptr, usize size, usize nmemb, CFile stream);
|
||||
extern fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream);
|
||||
extern fn int remove(char* filename);
|
||||
extern fn int rename(char* old_name, char* new_name);
|
||||
extern fn void rewind(CFile stream);
|
||||
extern fn void setbuf(CFile stream, char* buffer);
|
||||
extern fn void setvbuf(CFile stream, char* buffer, int mode, usize size);
|
||||
extern fn void setvbuf(CFile stream, char* buffer, int mode, usz size);
|
||||
extern fn CFile tmpnam(char* str);
|
||||
extern fn int fprintf(CFile stream, char* format, ...);
|
||||
extern fn int printf(char* format, ...);
|
||||
extern fn int sprintf(char* str, char* format, ...);
|
||||
extern fn int snprintf(char* str, usize size, char* format, ...);
|
||||
extern fn int snprintf(char* str, usz size, char* format, ...);
|
||||
extern fn int fscanf(CFile stream, char* format, ...);
|
||||
extern fn int scanf(char* format, ...);
|
||||
extern fn int sscanf(char* str, char* format, ...);
|
||||
@@ -186,7 +195,7 @@ extern fn int putchar(int c);
|
||||
extern fn int puts(char* str);
|
||||
extern fn int ungetc(int c, CFile stream);
|
||||
extern fn void perror(char* str);
|
||||
extern fn isize getline(char** linep, usize* linecapp, CFile stream);
|
||||
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
|
||||
|
||||
// vsprintf vprintf not supported
|
||||
|
||||
@@ -223,7 +232,7 @@ extern fn double difftime(Time time1, Time time2);
|
||||
extern fn Tm* gmtime(Time *timer);
|
||||
extern fn Tm* localtime(Time *timer);
|
||||
extern fn Time mktime(Tm *timeptr);
|
||||
extern fn usize strftime(char* str, usize maxsize, char* format, Tm *timeptr);
|
||||
extern fn usz strftime(char* str, usz maxsize, char* format, Tm *timeptr);
|
||||
extern fn Time time(Time *timer);
|
||||
|
||||
// signal
|
||||
@@ -231,48 +240,87 @@ define SignalFunction = fn void(int);
|
||||
extern fn SignalFunction signal(int sig, SignalFunction function);
|
||||
// Incomplete
|
||||
|
||||
|
||||
module libc::errno;
|
||||
|
||||
const Errno EPERM = 1; /* Operation not permitted */
|
||||
const Errno ENOENT = 2; /* No such file or directory */
|
||||
const Errno ESRCH = 3; /* No such process */
|
||||
const Errno EINTR = 4; /* Interrupted system call */
|
||||
const Errno EIO = 5; /* I/O error */
|
||||
const Errno ENXIO = 6; /* No such device or address */
|
||||
const Errno E2BIG = 7; /* Argument list too long */
|
||||
const Errno ENOEXEC = 8; /* Exec format error */
|
||||
const Errno EBADF = 9; /* Bad file number */
|
||||
const Errno ECHILD = 10; /* No child processes */
|
||||
const Errno EAGAIN = 11; /* Try again */
|
||||
const Errno ENOMEM = 12; /* Out of memory */
|
||||
const Errno EACCES = 13; /* Permission denied */
|
||||
const Errno EFAULT = 14; /* Bad address */
|
||||
const Errno ENOTBLK = 15; /* Block device required */
|
||||
const Errno EBUSY = 16; /* Device or resource busy */
|
||||
const Errno EEXIST = 17; /* File exists */
|
||||
const Errno EXDEV = 18; /* Cross-device link */
|
||||
const Errno ENODEV = 19; /* No such device */
|
||||
const Errno ENOTDIR = 20; /* Not a directory */
|
||||
const Errno EISDIR = 21; /* Is a directory */
|
||||
const Errno EINVAL = 22; /* Invalid argument */
|
||||
const Errno ENFILE = 23; /* File table overflow */
|
||||
const Errno EMFILE = 24; /* Too many open files */
|
||||
const Errno ENOTTY = 25; /* Not a typewriter */
|
||||
const Errno ETXTBSY = 26; /* Text file busy */
|
||||
const Errno EFBIG = 27; /* File too large */
|
||||
const Errno ENOSPC = 28; /* No space left on device */
|
||||
const Errno ESPIPE = 29; /* Illegal seek */
|
||||
const Errno EROFS = 30; /* Read-only file system */
|
||||
const Errno EMLINK = 31; /* Too many links */
|
||||
const Errno EPIPE = 32; /* Broken pipe */
|
||||
const Errno EDOM = 33; /* Math argument out of domain of func */
|
||||
const Errno ERANGE = 34; /* Math result not representable */
|
||||
const Errno EDEADLK = 35; /* Resource deadlock would occur */
|
||||
const Errno ENAMETOOLONG = 36; /* File name too long */
|
||||
const Errno EPERM = 1; // Operation not permitted
|
||||
const Errno ENOENT = 2; // No such file or directory
|
||||
const Errno ESRCH = 3; // No such process
|
||||
const Errno EINTR = 4; // Interrupted system call
|
||||
const Errno EIO = 5; // I/O error
|
||||
const Errno ENXIO = 6; // No such device or address
|
||||
const Errno E2BIG = 7; // Argument list too long
|
||||
const Errno ENOEXEC = 8; // Exec format error
|
||||
const Errno EBADF = 9; // Bad file number
|
||||
const Errno ECHILD = 10; // No child processes
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
const Errno EAGAIN = 35; // Try again Macos
|
||||
$else:
|
||||
const Errno EAGAIN = 11; // Try again
|
||||
$endif;
|
||||
|
||||
const Errno ENOMEM = 12; // Out of memory
|
||||
const Errno EACCES = 13; // Permission denied
|
||||
const Errno EFAULT = 14; // Bad address
|
||||
const Errno ENOTBLK = 15; // Block device required, not on Win32
|
||||
const Errno EBUSY = 16; // Device or resource busy
|
||||
const Errno EEXIST = 17; // File exists
|
||||
const Errno EXDEV = 18; // Cross-device link
|
||||
const Errno ENODEV = 19; // No such device
|
||||
const Errno ENOTDIR = 20; // Not a directory
|
||||
const Errno EISDIR = 21; // Is a directory
|
||||
const Errno EINVAL = 22; // Invalid argument
|
||||
const Errno ENFILE = 23; // File table overflow
|
||||
const Errno EMFILE = 24; // Too many open files
|
||||
const Errno ENOTTY = 25; // Not a typewriter
|
||||
const Errno ETXTBSY = 26; // Text file busy, not on Win32
|
||||
const Errno EFBIG = 27; // File too large
|
||||
const Errno ENOSPC = 28; // No space left on device
|
||||
const Errno ESPIPE = 29; // Illegal seek
|
||||
const Errno EROFS = 30; // Read-only file system
|
||||
const Errno EMLINK = 31; // Too many links
|
||||
const Errno EPIPE = 32; // Broken pipe
|
||||
const Errno EDOM = 33; // Math argument out of domain of func
|
||||
const Errno ERANGE = 34; // Math result not representable
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
const Errno EDEADLK = 11; // Resource deadlock would occur MacOS
|
||||
const Errno ENAMETOOLONG = 63; // File name too long MacOS
|
||||
const Errno ELOOP = 62; // Too many symbolic links encountered
|
||||
const Errno EOVERFLOW = 84; // Value too large for defined data type Macos
|
||||
const Errno ECONNRESET = 54; // Connection reset by peer Macos
|
||||
const Errno ENETDOWN = 50; // Network is down MacOS
|
||||
const Errno ENETUNREACH = 51; // Network is unreachable MacOS
|
||||
const Errno ENETRESET = 52; // Network dropped connection because of reset MacOS
|
||||
|
||||
$elif (env::OS_TYPE == OsType.WIN32):
|
||||
const Errno EDEADLK = 36; // Resource deadlock would occur Win32
|
||||
const Errno ENAMETOOLONG = 38; // File name too long Win32
|
||||
const Errno ELOOP = 114; // Too many symbolic links encountered
|
||||
const Errno EOVERFLOW = 132; // Value too large for defined data type
|
||||
const Errno ENETDOWN = 116; // Network is down
|
||||
const Errno ECONNRESET = 108; // Connection reset by peer
|
||||
const Errno ENETUNREACH = 118; // Network is unreachable
|
||||
const Errno ENETRESET = 117; // Network dropped connection because of reset
|
||||
|
||||
$else:
|
||||
const Errno EDEADLK = 35; // Resource deadlock would occur Linux (others?)
|
||||
const Errno ENAMETOOLONG = 36; // File name too long Linux (others?)
|
||||
const Errno ELOOP = 40; // Too many symbolic links encountered
|
||||
const Errno EOVERFLOW = 75; // Value too large for defined data type
|
||||
const Errno ENETDOWN = 100; // Network is down
|
||||
const Errno ECONNRESET = 104; // Connection reset by peer
|
||||
const Errno ENETUNREACH = 101; // Network is unreachable
|
||||
const Errno ENETRESET = 102; // Network dropped connection because of reset
|
||||
|
||||
$endif;
|
||||
|
||||
|
||||
/*
|
||||
const Errno ENOLCK = 37; /* No record locks available */
|
||||
const Errno ENOSYS = 38; /* Function not implemented */
|
||||
const Errno ENOTEMPTY = 39; /* Directory not empty */
|
||||
const Errno ELOOP = 40; /* Too many symbolic links encountered */
|
||||
|
||||
const Errno ENOMSG = 42; /* No message of desired type */
|
||||
const Errno EIDRM = 43; /* Identifier removed */
|
||||
@@ -307,7 +355,6 @@ const Errno EPROTO = 71; /* Protocol error */
|
||||
const Errno EMULTIHOP = 72; /* Multihop attempted */
|
||||
const Errno EDOTDOT = 73; /* RFS specific error */
|
||||
const Errno EBADMSG = 74; /* Not a data message */
|
||||
const Errno EOVERFLOW = 75; /* Value too large for defined data type */
|
||||
const Errno ENOTUNIQ = 76; /* Name not unique on network */
|
||||
const Errno EBADFD = 77; /* File descriptor in bad state */
|
||||
const Errno EREMCHG = 78; /* Remote address changed */
|
||||
@@ -332,11 +379,7 @@ const Errno EPFNOSUPPORT = 96; /* Protocol family not supported */
|
||||
const Errno EAFNOSUPPORT = 97; /* Address family not supported by protocol */
|
||||
const Errno EADDRINUSE = 98; /* Address already in use */
|
||||
const Errno EADDRNOTAVAIL = 99; /* Cannot assign requested address */
|
||||
const Errno ENETDOWN = 100; /* Network is down */
|
||||
const Errno ENETUNREACH = 101; /* Network is unreachable */
|
||||
const Errno ENETRESET = 102; /* Network dropped connection because of reset */
|
||||
const Errno ECONNABORTED = 103; /* Software caused connection abort */
|
||||
const Errno ECONNRESET = 104; /* Connection reset by peer */
|
||||
const Errno ENOBUFS = 105; /* No buffer space available */
|
||||
const Errno EISCONN = 106; /* Transport endpoint is already connected */
|
||||
const Errno ENOTCONN = 107; /* Transport endpoint is not connected */
|
||||
@@ -346,15 +389,30 @@ const Errno ETIMEDOUT = 110; /* Connection timed out */
|
||||
const Errno ECONNREFUSED = 111; /* Connection refused */
|
||||
const Errno EHOSTDOWN = 112; /* Host is down */
|
||||
const Errno EHOSTUNREACH = 113; /* No route to host */
|
||||
const Errno EALREADY = 114; /* Operation already in progress */
|
||||
const Errno EINPROGRESS = 115; /* Operation now in progress */
|
||||
*/
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
const Errno EINPROGRESS = 36; // Operation now in progress MacOS
|
||||
const Errno EALREADY = 37; // Operation already in progress MacOS
|
||||
const Errno EDQUOT = 69; // Quota exceeded, MacOS
|
||||
|
||||
$elif (env::OS_TYPE == OsType.WIN32):
|
||||
const Errno EALREADY = 103; // Operation already in progress
|
||||
const Errno EINPROGRESS = 112; // Operation now in progress Win32
|
||||
const Errno EDQUOT = -122; // Quota exceeded, not in Win32
|
||||
|
||||
$else:
|
||||
const Errno EALREADY = 114; // Operation already in progress
|
||||
const Errno EINPROGRESS = 115; // Operation now in progress
|
||||
const Errno EDQUOT = 122; // Quota exceeded
|
||||
$endif;
|
||||
|
||||
/*
|
||||
const Errno ESTALE = 116; /* Stale NFS file handle */
|
||||
const Errno EUCLEAN = 117; /* Structure needs cleaning */
|
||||
const Errno ENOTNAM = 118; /* Not a XENIX named type file */
|
||||
const Errno ENAVAIL = 119; /* No XENIX semaphores available */
|
||||
const Errno EISNAM = 120; /* Is a named type file */
|
||||
const Errno EREMOTEIO = 121; /* Remote I/O error */
|
||||
const Errno EDQUOT = 122; /* Quota exceeded */
|
||||
|
||||
const Errno ENOMEDIUM = 123; /* No medium found */
|
||||
const Errno EMEDIUMTYPE = 124; /* Wrong medium type */
|
||||
@@ -366,3 +424,5 @@ const Errno EKEYREJECTED = 129; /* Key was rejected by service */
|
||||
|
||||
const Errno EOWNERDEAD = 130; /* Owner died */
|
||||
const Errno ENOTRECOVERABLE = 131; /* State not recoverable */
|
||||
*/
|
||||
|
||||
37
lib/std/libc/os/errno.c3
Normal file
37
lib/std/libc/os/errno.c3
Normal file
@@ -0,0 +1,37 @@
|
||||
module libc::os;
|
||||
|
||||
$switch (env::OS_TYPE):
|
||||
|
||||
$case LINUX:
|
||||
|
||||
extern fn int* __errno_location();
|
||||
|
||||
macro int errno() = *__errno_location();
|
||||
macro void errno_set(int err) = *(__errno_location()) = err;
|
||||
|
||||
$case MACOSX:
|
||||
|
||||
extern fn int* __error();
|
||||
macro int errno() = *__error();
|
||||
macro void errno_set(int err) = *(__error()) = err;
|
||||
|
||||
$case WIN32:
|
||||
|
||||
macro int errno()
|
||||
{
|
||||
int holder;
|
||||
_get_errno(&holder);
|
||||
return holder;
|
||||
}
|
||||
|
||||
macro void errno_set(int err) = _set_errno(err);
|
||||
|
||||
extern fn void _get_errno(int* result);
|
||||
extern fn void _set_errno(int err);
|
||||
|
||||
$default:
|
||||
|
||||
macro int errno() = 1;
|
||||
fn void errno_set(int err) {}
|
||||
|
||||
$endswitch;
|
||||
@@ -12,7 +12,7 @@ private struct Node
|
||||
|
||||
struct LinkedList
|
||||
{
|
||||
usize size;
|
||||
usz size;
|
||||
Node *first;
|
||||
Node *last;
|
||||
}
|
||||
@@ -69,12 +69,12 @@ fn void LinkedList.free(LinkedList *list)
|
||||
list.size = 0;
|
||||
}
|
||||
|
||||
fn usize LinkedList.len(LinkedList* list) @inline
|
||||
fn usz LinkedList.len(LinkedList* list) @inline
|
||||
{
|
||||
return list.size;
|
||||
}
|
||||
|
||||
fn Type LinkedList.get(LinkedList* list, usize index)
|
||||
fn Type LinkedList.get(LinkedList* list, usz index)
|
||||
{
|
||||
Node* node = list.first;
|
||||
while (index--)
|
||||
|
||||
@@ -2,25 +2,41 @@
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::array::list<Type>;
|
||||
import std::math;
|
||||
|
||||
struct List
|
||||
{
|
||||
usize size;
|
||||
usize capacity;
|
||||
usz size;
|
||||
usz capacity;
|
||||
Allocator *allocator;
|
||||
Type *entries;
|
||||
}
|
||||
|
||||
private fn void List.ensure_capacity(List *list) @inline
|
||||
/**
|
||||
* @require allocator != null "A valid allocator must be provided"
|
||||
**/
|
||||
fn void List.init(List* list, usz initial_capacity = 16, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
if (list.capacity == list.size)
|
||||
{
|
||||
list.capacity = list.capacity ? 2 * list.capacity : 16;
|
||||
list.entries = realloc(list.entries, Type.sizeof * list.capacity);
|
||||
}
|
||||
list.allocator = allocator;
|
||||
list.size = 0;
|
||||
if (initial_capacity > 0)
|
||||
{
|
||||
initial_capacity = math::next_power_of_2(initial_capacity);
|
||||
list.entries = allocator.alloc_aligned(Type.sizeof * initial_capacity, Type[1].alignof)!!;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.entries = null;
|
||||
}
|
||||
list.capacity = initial_capacity;
|
||||
}
|
||||
|
||||
fn void List.tinit(List* list, usz initial_capacity = 16)
|
||||
{
|
||||
list.init(initial_capacity, mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
fn void List.push(List *list, Type element) @inline
|
||||
fn void List.push(List* list, Type element) @inline
|
||||
{
|
||||
list.append(element);
|
||||
}
|
||||
@@ -39,34 +55,39 @@ fn Type List.pop(List* list)
|
||||
return list.entries[--list.size];
|
||||
}
|
||||
|
||||
fn void List.clear(List* list)
|
||||
{
|
||||
list.size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require list.size > 0
|
||||
*/
|
||||
fn Type List.pop_first(List *list)
|
||||
fn Type List.pop_first(List* list)
|
||||
{
|
||||
Type value = list.entries[0];
|
||||
list.remove_at(0);
|
||||
return value;
|
||||
}
|
||||
|
||||
fn void List.remove_at(List *list, usize index)
|
||||
fn void List.remove_at(List* list, usz index)
|
||||
{
|
||||
for (usize i = index + 1; i < list.size; i++)
|
||||
for (usz i = index + 1; i < list.size; i++)
|
||||
{
|
||||
list.entries[i - 1] = list.entries[i];
|
||||
}
|
||||
list.size--;
|
||||
}
|
||||
|
||||
fn void List.push_front(List *list, Type type) @inline
|
||||
fn void List.push_front(List* list, Type type) @inline
|
||||
{
|
||||
list.insert_at(0, type);
|
||||
}
|
||||
|
||||
fn void List.insert_at(List* list, usize index, Type type)
|
||||
fn void List.insert_at(List* list, usz index, Type type)
|
||||
{
|
||||
list.ensure_capacity();
|
||||
for (usize i = list.size; i > index; i--)
|
||||
for (usz i = list.size; i > index; i--)
|
||||
{
|
||||
list.entries[i] = list.entries[i - 1];
|
||||
}
|
||||
@@ -79,55 +100,78 @@ fn void List.remove_last(List* list)
|
||||
list.size--;
|
||||
}
|
||||
|
||||
fn void List.remove_first(List *list)
|
||||
fn void List.remove_first(List* list)
|
||||
{
|
||||
list.remove_at(0);
|
||||
}
|
||||
|
||||
fn Type* List.first(List *list)
|
||||
fn Type* List.first(List* list)
|
||||
{
|
||||
return list.size ? &list.entries[0] : null;
|
||||
}
|
||||
|
||||
fn Type* List.last(List *list)
|
||||
fn Type* List.last(List* list)
|
||||
{
|
||||
return list.size ? &list.entries[list.size - 1] : null;
|
||||
}
|
||||
|
||||
fn bool List.is_empty(List *list)
|
||||
fn bool List.is_empty(List* list)
|
||||
{
|
||||
return !list.size;
|
||||
}
|
||||
|
||||
fn usize List.len(List *list) @operator(len)
|
||||
fn usz List.len(List* list) @operator(len)
|
||||
{
|
||||
return list.size;
|
||||
}
|
||||
|
||||
fn Type List.get(List *list, usize index)
|
||||
fn Type List.get(List* list, usz index)
|
||||
{
|
||||
return list.entries[index];
|
||||
}
|
||||
|
||||
fn void List.free(List *list)
|
||||
fn void List.free(List* list)
|
||||
{
|
||||
free(list.entries);
|
||||
if (!list.allocator) return;
|
||||
list.allocator.free_aligned(list.entries)!!;
|
||||
list.capacity = 0;
|
||||
list.size = 0;
|
||||
list.entries = null;
|
||||
}
|
||||
|
||||
fn void List.swap(List *list, usize i, usize j)
|
||||
fn void List.swap(List* list, usz i, usz j)
|
||||
{
|
||||
@swap(list.entries[i], list.entries[j]);
|
||||
}
|
||||
|
||||
macro Type List.@item_at(List &list, usize index) @operator(elementat)
|
||||
/**
|
||||
* Reserve at least min_capacity
|
||||
**/
|
||||
fn void List.reserve(List* list, usz min_capacity)
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
if (list.capacity >= min_capacity) return;
|
||||
if (!list.allocator) list.allocator = mem::temp_allocator();
|
||||
min_capacity = math::next_power_of_2(min_capacity);
|
||||
list.entries = list.allocator.realloc_aligned(list.entries, Type.sizeof * min_capacity, Type[1].alignof) ?? null;
|
||||
list.capacity = min_capacity;
|
||||
}
|
||||
|
||||
macro Type List.@item_at(List &list, usz index) @operator([])
|
||||
{
|
||||
return list.entries[index];
|
||||
}
|
||||
|
||||
macro Type* List.@item_ref(List &list, usize index) @operator(elementref)
|
||||
fn Type* List.get_ref(List* list, usz index) @operator(&[]) @inline
|
||||
{
|
||||
return &list.entries[index];
|
||||
}
|
||||
|
||||
|
||||
private fn void List.ensure_capacity(List* list) @inline
|
||||
{
|
||||
if (list.capacity == list.size)
|
||||
{
|
||||
list.reserve(list.capacity ? 2 * list.capacity : 16);
|
||||
}
|
||||
}
|
||||
|
||||
341
lib/std/map.c3
Normal file
341
lib/std/map.c3
Normal file
@@ -0,0 +1,341 @@
|
||||
module std::map<Key, Value>;
|
||||
import std::math;
|
||||
|
||||
const uint DEFAULT_INITIAL_CAPACITY = 16;
|
||||
const uint MAXIMUM_CAPACITY = 1u << 31;
|
||||
const float DEFAULT_LOAD_FACTOR = 0.75;
|
||||
|
||||
private struct Entry
|
||||
{
|
||||
uint hash;
|
||||
Key key;
|
||||
Value value;
|
||||
Entry* next;
|
||||
}
|
||||
struct HashMap
|
||||
{
|
||||
Entry*[] table;
|
||||
Allocator* allocator;
|
||||
uint count; // Number of elements
|
||||
uint threshold; // Resize limit
|
||||
float load_factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require capacity > 0 "The capacity must be 1 or higher"
|
||||
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
* @require !map.allocator "Map was already initialized"
|
||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
* @require allocator != null "The allocator must be non-null"
|
||||
**/
|
||||
fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
capacity = math::next_power_of_2(capacity);
|
||||
map.allocator = allocator;
|
||||
map.load_factor = load_factor;
|
||||
map.threshold = (uint)(capacity * load_factor);
|
||||
map.table = array::make(Entry*, capacity, allocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require capacity > 0 "The capacity must be 1 or higher"
|
||||
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
* @require !map.allocator "Map was already initialized"
|
||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
**/
|
||||
fn void HashMap.tinit(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
map.init(capacity, load_factor, mem::temp_allocator());
|
||||
}
|
||||
|
||||
fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
map.init(other_map.table.len, other_map.load_factor, allocator);
|
||||
map.put_all_for_create(other_map);
|
||||
}
|
||||
|
||||
fn void HashMap.tinit_from_map(HashMap* map, HashMap* other_map)
|
||||
{
|
||||
map.init_from_map(other_map, mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
fn bool HashMap.is_empty(HashMap* map) @inline
|
||||
{
|
||||
return !map.count;
|
||||
}
|
||||
|
||||
fn Value*! HashMap.get_ref(HashMap* map, Key key)
|
||||
{
|
||||
if (!map.count) return SearchResult.MISSING!;
|
||||
uint hash = rehash(key.hash());
|
||||
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return &e.value;
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value or update and
|
||||
**/
|
||||
macro Value HashMap.@get_or_set(HashMap* map, Key key, Value #expr)
|
||||
{
|
||||
if (!map.count)
|
||||
{
|
||||
Value val = #expr;
|
||||
map.set(key, val);
|
||||
return val;
|
||||
}
|
||||
uint hash = rehash(key.hash());
|
||||
uint index = index_for(hash, map.table.len);
|
||||
for (Entry *e = map.table[index]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return e.value;
|
||||
}
|
||||
Value val = #expr;
|
||||
map.add_entry(hash, key, val, index);
|
||||
return val;
|
||||
}
|
||||
|
||||
fn Value! HashMap.get(HashMap* map, Key key) @operator([])
|
||||
{
|
||||
return *map.get_ref(key) @inline;
|
||||
}
|
||||
|
||||
fn bool HashMap.has_key(HashMap* map, Key key)
|
||||
{
|
||||
return try(map.get_ref(key));
|
||||
}
|
||||
|
||||
fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
|
||||
{
|
||||
// If the map isn't initialized, use the defaults to initialize it.
|
||||
if (!map.allocator)
|
||||
{
|
||||
map.init();
|
||||
}
|
||||
uint hash = rehash(key.hash());
|
||||
uint index = index_for(hash, map.table.len);
|
||||
for (Entry *e = map.table[index]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key))
|
||||
{
|
||||
e.value = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
map.add_entry(hash, key, value, index);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void! HashMap.remove(HashMap* map, Key key) @maydiscard
|
||||
{
|
||||
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING!;
|
||||
}
|
||||
|
||||
fn void HashMap.clear(HashMap* map)
|
||||
{
|
||||
if (!map.count) return;
|
||||
foreach (Entry** &entry_ref : map.table)
|
||||
{
|
||||
Entry* entry = *entry_ref;
|
||||
if (!entry) continue;
|
||||
map.free(entry);
|
||||
*entry_ref = null;
|
||||
}
|
||||
map.count = 0;
|
||||
}
|
||||
|
||||
fn void HashMap.destroy(HashMap* map)
|
||||
{
|
||||
if (!map.allocator) return;
|
||||
map.clear();
|
||||
map.free(map.table.ptr);
|
||||
map.table = Entry*[] {};
|
||||
}
|
||||
|
||||
fn Key[] HashMap.key_tlist(HashMap* map)
|
||||
{
|
||||
return map.key_list(mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
fn Key[] HashMap.key_list(HashMap* map, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
if (!map.count) return Key[] {};
|
||||
|
||||
Key[] list = array::make(Key, map.count, allocator);
|
||||
usz index = 0;
|
||||
foreach (Entry* entry : map.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
list[index++] = entry.key;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
fn Value[] HashMap.value_tlist(HashMap* map)
|
||||
{
|
||||
return map.value_list(mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
fn Value[] HashMap.value_list(HashMap* map, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
if (!map.count) return Value[] {};
|
||||
Value[] list = array::make(Value, map.count, allocator);
|
||||
usz index = 0;
|
||||
foreach (Entry* entry : map.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
list[index++] = entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
$if (types::is_equatable(Value)):
|
||||
fn bool HashMap.has_value(HashMap* map, Value v)
|
||||
{
|
||||
if (!map.count) return false;
|
||||
foreach (Entry* entry : map.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
if (equals(v, entry.value)) return true;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$endif;
|
||||
|
||||
// --- private methods
|
||||
|
||||
private fn void HashMap.add_entry(HashMap* map, uint hash, Key key, Value value, uint bucket_index)
|
||||
{
|
||||
Entry* entry = map.allocator.alloc(Entry.sizeof)!!;
|
||||
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
|
||||
map.table[bucket_index] = entry;
|
||||
if (map.count++ >= map.threshold)
|
||||
{
|
||||
map.resize(map.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
private fn void HashMap.resize(HashMap* map, uint new_capacity)
|
||||
{
|
||||
Entry*[] old_table = map.table;
|
||||
uint old_capacity = old_table.len;
|
||||
if (old_capacity == MAXIMUM_CAPACITY)
|
||||
{
|
||||
map.threshold = uint.max;
|
||||
return;
|
||||
}
|
||||
Entry*[] new_table = array::make(Entry*, new_capacity, map.allocator);
|
||||
map.transfer(new_table);
|
||||
map.table = new_table;
|
||||
map.free(old_table.ptr);
|
||||
map.threshold = (uint)(new_capacity * map.load_factor);
|
||||
}
|
||||
|
||||
private fn uint rehash(uint hash) @inline
|
||||
{
|
||||
hash ^= (hash >> 20) ^ (hash >> 12);
|
||||
return hash ^ ((hash >> 7) ^ (hash >> 4));
|
||||
}
|
||||
|
||||
private macro uint index_for(uint hash, uint capacity)
|
||||
{
|
||||
return hash & (capacity - 1);
|
||||
}
|
||||
|
||||
private fn void HashMap.transfer(HashMap* map, Entry*[] new_table)
|
||||
{
|
||||
Entry*[] src = map.table;
|
||||
uint new_capacity = new_table.len;
|
||||
foreach (uint j, Entry *e : src)
|
||||
{
|
||||
if (!e) continue;
|
||||
do
|
||||
{
|
||||
Entry* next = e.next;
|
||||
uint i = index_for(e.hash, new_capacity);
|
||||
e.next = new_table[i];
|
||||
new_table[i] = e;
|
||||
e = next;
|
||||
}
|
||||
while (e);
|
||||
}
|
||||
}
|
||||
|
||||
private fn void HashMap.put_all_for_create(HashMap* map, HashMap* other_map)
|
||||
{
|
||||
if (!other_map.count) return;
|
||||
foreach (Entry *e : other_map.table)
|
||||
{
|
||||
if (!e) continue;
|
||||
map.put_for_create(e.key, e.value);
|
||||
}
|
||||
}
|
||||
|
||||
private fn void HashMap.put_for_create(HashMap* map, Key key, Value value)
|
||||
{
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
for (Entry *e = map.table[i]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key))
|
||||
{
|
||||
e.value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
map.create_entry(hash, key, value, i);
|
||||
}
|
||||
|
||||
private fn void HashMap.free(HashMap* map, void* ptr)
|
||||
{
|
||||
map.allocator.free(ptr)!!;
|
||||
}
|
||||
|
||||
private fn bool HashMap.remove_entry_for_key(HashMap* map, Key key)
|
||||
{
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
Entry* prev = map.table[i];
|
||||
Entry* e = prev;
|
||||
while (e)
|
||||
{
|
||||
Entry *next = e.next;
|
||||
if (e.hash == hash && equals(key, e.key))
|
||||
{
|
||||
map.count--;
|
||||
if (prev == e)
|
||||
{
|
||||
map.table[i] = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev.next = next;
|
||||
}
|
||||
map.free(e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
e = next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private fn void HashMap.create_entry(HashMap* map, uint hash, Key key, Value value, int bucket_index)
|
||||
{
|
||||
Entry *e = map.table[bucket_index];
|
||||
Entry* entry = map.allocator.alloc(Entry.sizeof)!!;
|
||||
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
|
||||
map.table[bucket_index] = entry;
|
||||
map.count++;
|
||||
}
|
||||
407
lib/std/math.c3
407
lib/std/math.c3
@@ -54,13 +54,15 @@ const DOUBLE_MIN_10_EXP = -307;
|
||||
const DOUBLE_MAX_EXP = 1024;
|
||||
const DOUBLE_MIN_EXP = -1021;
|
||||
const DOUBLE_EPSILON = 2.22044604925031308085e-16;
|
||||
|
||||
const QUAD_MANT_DIG = 113;
|
||||
|
||||
/*
|
||||
const QUAD_MAX = 1.18973149535723176508575932662800702e+4932;
|
||||
const QUAD_MIN = 3.36210314311209350626267781732175260e-4932;
|
||||
const QUAD_DENORM_MIN = 6.47517511943802511092443895822764655e-4966;
|
||||
const QUAD_DIG = 33;
|
||||
const QUAD_DEC_DIGITS = 36;
|
||||
const QUAD_MANT_DIG = 113;
|
||||
const QUAD_MAX_10_EXP = 4932;
|
||||
const QUAD_MIN_10_EXP = -4931;
|
||||
const QUAD_MAX_EXP = 16384;
|
||||
@@ -68,91 +70,396 @@ const QUAD_MIN_EXP = -16481;
|
||||
const QUAD_EPSILON = 1.92592994438723585305597794258492732e-34;
|
||||
*/
|
||||
|
||||
enum RoundingMode : int
|
||||
{
|
||||
TOWARD_ZERO,
|
||||
TO_NEAREST,
|
||||
TOWARD_INFINITY,
|
||||
TOWARD_NEG_INFINITY
|
||||
}
|
||||
|
||||
define Complex32 = Complex<float>;
|
||||
define Complex64 = Complex<double>;
|
||||
|
||||
macro max(x, y) @builtin
|
||||
/**
|
||||
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
|
||||
**/
|
||||
macro abs(x) = $$abs(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_int(x) `The input must be an integer`
|
||||
**/
|
||||
macro sign(x)
|
||||
{
|
||||
return x > y ? x : y;
|
||||
if (!x) return ($typeof(x))0;
|
||||
return ($typeof(x))(x < 0 ? -1 : 1);
|
||||
}
|
||||
|
||||
macro min(x, y) @builtin
|
||||
$if (env::COMPILER_LIBC_AVAILABLE):
|
||||
|
||||
extern fn double _atan(double x) @extname("atan");
|
||||
extern fn float _atanf(float x) @extname("atanf");
|
||||
extern fn double _atan2(double x) @extname("atan2");
|
||||
extern fn float _atan2f(float x) @extname("atan2f");
|
||||
|
||||
macro atan(x)
|
||||
{
|
||||
return x < y ? x : y;
|
||||
$if ($typeof(x).typeid == float.typeid):
|
||||
return _atanf(x);
|
||||
$else:
|
||||
return _atan(x);
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro abs(x) @builtin
|
||||
macro atan2(x, y)
|
||||
{
|
||||
return $$abs(x);
|
||||
$if ($typeof(x).typeid == float.typeid && $typeof(y).typeid == float.typeid):
|
||||
return _atan2f(x);
|
||||
$else:
|
||||
return _atan2(x);
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn double log10(double x) @inline
|
||||
{
|
||||
return $$log10(x);
|
||||
}
|
||||
$endif;
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro ceil(x) = $$ceil(x);
|
||||
|
||||
fn double log2(double x) @inline
|
||||
/**
|
||||
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
|
||||
* @require types::@has_same(x, lower, upper) `The input types must be equal`
|
||||
**/
|
||||
macro clamp(x, lower, upper) = $$max(lower, $$min(x, upper));
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(mag) `The input must be a floating point value or float vector`
|
||||
* @require types::is_same($typeof(mag), $typeof(sgn)) `The input types must be equal`
|
||||
**/
|
||||
macro copysign(mag, sgn) = $$copysign(mag, sgn);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro cos(x) = $$cos(x);
|
||||
|
||||
macro cosec(x) = 1 / sin(x);
|
||||
|
||||
macro cosech(x) = 2 / (exp(x) - exp(-x));
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro cosh(x) = (exp(x) + exp(-x)) / 2.0;
|
||||
|
||||
macro cotan(x) = cos(x) / sin(x);
|
||||
|
||||
macro cotanh(x) = (exp(2.0 * x) + 1.0) / (exp(2.0 * x) - 1.0);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro exp(x) = $$exp(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro exp2(x) = $$exp2(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro floor(x) = $$floor(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(a) `The input must be a floating point value or float vector`
|
||||
* @require types::@has_same(a, b, c) `The input types must be equal`
|
||||
**/
|
||||
macro fma(a, b, c) = $$fma(a, b, c);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
* @require values::@is_floatlike(y) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro hypot(x, y) = sqrt(sqr(x) + sqr(y));
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro log(x) = $$log(x);
|
||||
|
||||
/**
|
||||
* @require values::is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro log2(x) = $$log2(x);
|
||||
|
||||
/**
|
||||
* @require values::is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro log10(x) = $$log10(x);
|
||||
|
||||
/**
|
||||
* @require types::is_numerical($typeof(x)) `The input must be a floating point value or float vector`
|
||||
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
|
||||
**/
|
||||
macro max(x, y, ...)
|
||||
{
|
||||
return $$log2(x);
|
||||
$if ($vacount == 0):
|
||||
return $$max(x, y);
|
||||
$else:
|
||||
var m = $$max(x, y);
|
||||
$for (var $i = 0; $i < $vacount; $i++):
|
||||
m = $$max(m, $vaarg($i));
|
||||
$endfor;
|
||||
return m;
|
||||
$endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_floatlike($typeof(f)) `The input must be a floating point value or float vector`
|
||||
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
|
||||
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
|
||||
**/
|
||||
macro log(f)
|
||||
macro min(x, y, ...)
|
||||
{
|
||||
return $$log(f);
|
||||
$if ($vacount == 0):
|
||||
return $$min(x, y);
|
||||
$else:
|
||||
var m = $$min(x, y);
|
||||
$for (var $i = 0; $i < $vacount; $i++):
|
||||
m = $$min(m, $vaarg($i));
|
||||
$endfor;
|
||||
return m;
|
||||
$endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_floatlike($typeof(f)) `The input must be a floating point value or float vector`
|
||||
* @require types::@is_float(a) `The input must be a floating point value`
|
||||
* @require types::@has_same(a, b, c) `The input types must be equal`
|
||||
**/
|
||||
macro cos(f)
|
||||
{
|
||||
return $$cos(f);
|
||||
}
|
||||
|
||||
macro muladd(a, b, c) = $$fmuladd(a, b, c);
|
||||
|
||||
/**
|
||||
* @require types::is_floatlike($typeof(f)) `The input must be a floating point value or float vector`
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro sin(f)
|
||||
macro nearbyint(x) = $$nearbyint(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
* @require values::@convertable_to(exp, x) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
|
||||
**/
|
||||
macro pow(x, exp)
|
||||
{
|
||||
return $$sin(f);
|
||||
$if (types::is_floatlike($typeof(exp))):
|
||||
return $$pow(x, ($typeof(x))exp);
|
||||
$else:
|
||||
return $$pow_int(x, exp);
|
||||
$endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_floatlike($typeof(f)) `The input must be a floating point value or float vector`
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro exp(f)
|
||||
{
|
||||
return $$exp(f);
|
||||
}
|
||||
macro rint(x) = $$rint(x);
|
||||
|
||||
/**
|
||||
* @require types::is_floatlike($typeof(f)) `The input must be a floating point value or float vector`
|
||||
* @require types::@has_same(f, exp) `Parameters must have the same type`
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro pow(f, exp) @operator(floatvec)
|
||||
{
|
||||
return $$pow(f, exp);
|
||||
}
|
||||
macro round(x) = $$round(x);
|
||||
|
||||
/**
|
||||
* @require types::is_floatlike($typeof(f)) `The input must be a floating point value or float vector`
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro trunc(x) @operator(floatvec)
|
||||
{
|
||||
return $$trunc(x);
|
||||
}
|
||||
macro roundeven(x) = $$roundeven(x);
|
||||
|
||||
macro sec(x) = 1 / cos(x);
|
||||
|
||||
macro sech(x) = 2 / (exp(x) + exp(-x));
|
||||
|
||||
/**
|
||||
* @require types::is_floatlike($typeof(f)) `The input must be a floating point value or float vector`
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro ceil(x) @operator(floatvec)
|
||||
{
|
||||
return $$ceil(x);
|
||||
}
|
||||
macro sin(x) = $$sin(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro sinh(x) = (exp(x) - exp(-x)) / 2.0;
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro sqr(x) = x * x;
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro sqrt(x) = $$sqrt(x);
|
||||
|
||||
macro tan(x) = sin(x) / cos(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro tanh(x) = (exp(2.0 * x) - 1.0) / (exp(2.0 * x) + 1.0);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro trunc(x) = $$trunc(x);
|
||||
|
||||
macro float float.ceil(float x) = $$ceil(x);
|
||||
macro float float.clamp(float x, float lower, float upper) = $$max(lower, $$min(x, upper));
|
||||
macro float float.copysign(float mag, float sgn) = $$copysign(mag, sgn);
|
||||
macro float float.floor(float x) = $$floor(x);
|
||||
macro float float.fma(float a, float b, float c) = $$fma(a, b, c);
|
||||
macro float float.muladd(float a, float b, float c) = $$fmuladd(a, b, c);
|
||||
macro float float.nearbyint(float x) = $$nearbyint(x);
|
||||
macro float float.pow(float x, exp) = pow(x, exp);
|
||||
macro float float.rint(float x) = $$rint(x);
|
||||
macro float float.round(float x) = $$round(x);
|
||||
macro float float.roundeven(float x) = $$roundeven(x);
|
||||
macro float float.trunc(float x) = $$trunc(x);
|
||||
|
||||
macro float float[<*>].sum(float[<*>] x, float start = 0.0) = $$reduce_fadd(x, start);
|
||||
macro float float[<*>].product(float[<*>] x, float start = 1.0) = $$reduce_fmul(x, start);
|
||||
macro float float[<*>].max(float[<*>] x) = $$reduce_max(x);
|
||||
macro float float[<*>].min(float[<*>] x) = $$reduce_min(x);
|
||||
macro float[<*>] float[<*>].ceil(float[<*>] x) = $$ceil(x);
|
||||
macro float[<*>] float[<*>].clamp(float[<*>] x, float[<*>] lower, float[<*>] upper) = $$max(lower, $$min(x, upper));
|
||||
macro float[<*>] float[<*>].copysign(float[<*>] mag, float[<*>] sgn) = $$copysign(mag, sgn);
|
||||
macro float[<*>] float[<*>].fma(float[<*>] a, float[<*>] b, float[<*>] c) = $$fma(a, b, c);
|
||||
macro float[<*>] float[<*>].floor(float[<*>] x) = $$floor(x);
|
||||
macro float[<*>] float[<*>].nearbyint(float[<*>] x) = $$nearbyint(x);
|
||||
macro float[<*>] float[<*>].pow(float[<*>] x, exp) = pow(x, exp);
|
||||
macro float[<*>] float[<*>].rint(float[<*>] x) = $$rint(x);
|
||||
macro float[<*>] float[<*>].round(float[<*>] x) = $$round(x);
|
||||
macro float[<*>] float[<*>].roundeven(float[<*>] x) = $$roundeven(x);
|
||||
macro float[<*>] float[<*>].trunc(float[<*>] x) = $$trunc(x);
|
||||
|
||||
macro double double.ceil(double x) = $$ceil(x);
|
||||
macro double double.clamp(double x, double lower, double upper) = $$max(lower, $$min(x, upper));
|
||||
macro double double.copysign(double mag, double sgn) = $$copysign(mag, sgn);
|
||||
macro double double.floor(double x) = $$floor(x);
|
||||
macro double double.fma(double a, double b, double c) = $$fma(a, b, c);
|
||||
macro double double.muladd(double a, double b, double c) = $$fmuladd(a, b, c);
|
||||
macro double double.nearbyint(double x) = $$nearbyint(x);
|
||||
macro double double.pow(double x, exp) = pow(x, exp);
|
||||
macro double double.rint(double x) = $$rint(x);
|
||||
macro double double.round(double x) = $$round(x);
|
||||
macro double double.roundeven(double x) = $$roundeven(x);
|
||||
macro double double.trunc(double x) = $$trunc(x);
|
||||
|
||||
macro double double[<*>].sum(double[<*>] x, double start = 0.0) = $$reduce_add(x, start);
|
||||
macro double double[<*>].product(double[<*>] x, double start = 1.0) = $$reduce_mul(x, start);
|
||||
macro double double[<*>].max(double[<*>] x) = $$reduce_fmax(x);
|
||||
macro double double[<*>].min(double[<*>] x) = $$reduce_fmin(x);
|
||||
macro double[<*>] double[<*>].ceil(double[<*>] x) = $$ceil(x);
|
||||
macro double[<*>] double[<*>].clamp(double[<*>] x, double[<*>] lower, double[<*>] upper) = $$max(lower, $$min(x, upper));
|
||||
macro double[<*>] double[<*>].copysign(double[<*>] mag, double[<*>] sgn) = $$copysign(mag, sgn);
|
||||
macro double[<*>] double[<*>].floor(double[<*>] x) = $$floor(x);
|
||||
macro double[<*>] double[<*>].fma(double[<*>] a, double[<*>] b, double[<*>] c) = $$fma(a, b, c);
|
||||
macro double[<*>] double[<*>].nearbyint(double[<*>] x) = $$nearbyint(x);
|
||||
macro double[<*>] double[<*>].pow(double[<*>] x, exp) = pow(x, exp);
|
||||
macro double[<*>] double[<*>].rint(double[<*>] x) = $$rint(x);
|
||||
macro double[<*>] double[<*>].round(double[<*>] x) = $$round(x);
|
||||
macro double[<*>] double[<*>].roundeven(double[<*>] x) = $$roundeven(x);
|
||||
macro double[<*>] double[<*>].trunc(double[<*>] x) = $$trunc(x);
|
||||
|
||||
macro ichar ichar[<*>].sum(ichar[<*>] x) = $$reduce_add(x);
|
||||
macro ichar ichar[<*>].product(ichar[<*>] x) = $$reduce_mul(x);
|
||||
macro ichar ichar[<*>].and(ichar[<*>] x) = $$reduce_and(x);
|
||||
macro ichar ichar[<*>].or(ichar[<*>] x) = $$reduce_or(x);
|
||||
macro ichar ichar[<*>].xor(ichar[<*>] x) = $$reduce_xor(x);
|
||||
macro ichar ichar[<*>].max(ichar[<*>] x) = $$reduce_max(x);
|
||||
macro ichar ichar[<*>].min(ichar[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro short short[<*>].sum(short[<*>] x) = $$reduce_add(x);
|
||||
macro short short[<*>].product(short[<*>] x) = $$reduce_mul(x);
|
||||
macro short short[<*>].and(short[<*>] x) = $$reduce_and(x);
|
||||
macro short short[<*>].or(short[<*>] x) = $$reduce_or(x);
|
||||
macro short short[<*>].xor(short[<*>] x) = $$reduce_xor(x);
|
||||
macro short short[<*>].max(short[<*>] x) = $$reduce_max(x);
|
||||
macro short short[<*>].min(short[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] int[<*>].comp_lt(int[<*>] x, int[<*>] y) = $$veccomplt(x, y);
|
||||
macro bool[<*>] int[<*>].comp_le(int[<*>] x, int[<*>] y) = $$veccomple(x, y);
|
||||
macro bool[<*>] int[<*>].comp_eq(int[<*>] x, int[<*>] y) = $$veccompeq(x, y);
|
||||
macro bool[<*>] int[<*>].comp_gt(int[<*>] x, int[<*>] y) = $$veccompgt(x, y);
|
||||
macro bool[<*>] int[<*>].comp_ge(int[<*>] x, int[<*>] y) = $$veccompge(x, y);
|
||||
macro bool[<*>] int[<*>].comp_ne(int[<*>] x, int[<*>] y) = $$veccompne(x, y);
|
||||
|
||||
macro int int[<*>].sum(int[<*>] x) = $$reduce_add(x);
|
||||
macro int int[<*>].product(int[<*>] x) = $$reduce_mul(x);
|
||||
macro int int[<*>].and(int[<*>] x) = $$reduce_and(x);
|
||||
macro int int[<*>].or(int[<*>] x) = $$reduce_or(x);
|
||||
macro int int[<*>].xor(int[<*>] x) = $$reduce_xor(x);
|
||||
macro int int[<*>].max(int[<*>] x) = $$reduce_max(x);
|
||||
macro int int[<*>].min(int[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro long long[<*>].sum(long[<*>] x) = $$reduce_add(x);
|
||||
macro long long[<*>].product(long[<*>] x) = $$reduce_mul(x);
|
||||
macro long long[<*>].and(long[<*>] x) = $$reduce_and(x);
|
||||
macro long long[<*>].or(long[<*>] x) = $$reduce_or(x);
|
||||
macro long long[<*>].xor(long[<*>] x) = $$reduce_xor(x);
|
||||
macro long long[<*>].max(long[<*>] x) = $$reduce_max(x);
|
||||
macro long long[<*>].min(long[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro int128 int128[<*>].sum(int128[<*>] x) = $$reduce_add(x);
|
||||
macro int128 int128[<*>].product(int128[<*>] x) = $$reduce_mul(x);
|
||||
macro int128 int128[<*>].and(int128[<*>] x) = $$reduce_and(x);
|
||||
macro int128 int128[<*>].or(int128[<*>] x) = $$reduce_or(x);
|
||||
macro int128 int128[<*>].xor(int128[<*>] x) = $$reduce_xor(x);
|
||||
macro int128 int128[<*>].max(int128[<*>] x) = $$reduce_max(x);
|
||||
macro int128 int128[<*>].min(int128[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro bool bool[<*>].sum(bool[<*>] x) = $$reduce_add(x);
|
||||
macro bool bool[<*>].product(bool[<*>] x) = $$reduce_mul(x);
|
||||
macro bool bool[<*>].and(bool[<*>] x) = $$reduce_and(x);
|
||||
macro bool bool[<*>].or(bool[<*>] x) = $$reduce_or(x);
|
||||
macro bool bool[<*>].xor(bool[<*>] x) = $$reduce_xor(x);
|
||||
macro bool bool[<*>].max(bool[<*>] x) = $$reduce_max(x);
|
||||
macro bool bool[<*>].min(bool[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro char char[<*>].sum(char[<*>] x) = $$reduce_add(x);
|
||||
macro char char[<*>].product(char[<*>] x) = $$reduce_mul(x);
|
||||
macro char char[<*>].and(char[<*>] x) = $$reduce_and(x);
|
||||
macro char char[<*>].or(char[<*>] x) = $$reduce_or(x);
|
||||
macro char char[<*>].xor(char[<*>] x) = $$reduce_xor(x);
|
||||
macro char char[<*>].max(char[<*>] x) = $$reduce_max(x);
|
||||
macro char char[<*>].min(char[<*>] x) = $$reduce_min(x);
|
||||
|
||||
|
||||
macro ushort ushort[<*>].sum(ushort[<*>] x) = $$reduce_add(x);
|
||||
macro ushort ushort[<*>].product(ushort[<*>] x) = $$reduce_mul(x);
|
||||
macro ushort ushort[<*>].and(ushort[<*>] x) = $$reduce_and(x);
|
||||
macro ushort ushort[<*>].or(ushort[<*>] x) = $$reduce_or(x);
|
||||
macro ushort ushort[<*>].xor(ushort[<*>] x) = $$reduce_xor(x);
|
||||
macro ushort ushort[<*>].max(ushort[<*>] x) = $$reduce_max(x);
|
||||
macro ushort ushort[<*>].min(ushort[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro uint uint[<*>].sum(uint[<*>] x) = $$reduce_add(x);
|
||||
macro uint uint[<*>].product(uint[<*>] x) = $$reduce_mul(x);
|
||||
macro uint uint[<*>].and(uint[<*>] x) = $$reduce_and(x);
|
||||
macro uint uint[<*>].or(uint[<*>] x) = $$reduce_or(x);
|
||||
macro uint uint[<*>].xor(uint[<*>] x) = $$reduce_xor(x);
|
||||
macro uint uint[<*>].max(uint[<*>] x) = $$reduce_max(x);
|
||||
macro uint uint[<*>].min(uint[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro ulong ulong[<*>].sum(ulong[<*>] x) = $$reduce_add(x);
|
||||
macro ulong ulong[<*>].product(ulong[<*>] x) = $$reduce_mul(x);
|
||||
macro ulong ulong[<*>].and(ulong[<*>] x) = $$reduce_and(x);
|
||||
macro ulong ulong[<*>].or(ulong[<*>] x) = $$reduce_or(x);
|
||||
macro ulong ulong[<*>].xor(ulong[<*>] x) = $$reduce_xor(x);
|
||||
macro ulong ulong[<*>].max(ulong[<*>] x) = $$reduce_max(x);
|
||||
macro ulong ulong[<*>].min(ulong[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro uint128 uint128[<*>].sum(uint128[<*>] x) = $$reduce_add(x);
|
||||
macro uint128 uint128[<*>].product(uint128[<*>] x) = $$reduce_mul(x);
|
||||
macro uint128 uint128[<*>].and(uint128[<*>] x) = $$reduce_and(x);
|
||||
macro uint128 uint128[<*>].or(uint128[<*>] x) = $$reduce_or(x);
|
||||
macro uint128 uint128[<*>].xor(uint128[<*>] x) = $$reduce_xor(x);
|
||||
macro uint128 uint128[<*>].max(uint128[<*>] x) = $$reduce_max(x);
|
||||
macro uint128 uint128[<*>].min(uint128[<*>] x) = $$reduce_min(x);
|
||||
|
||||
/**
|
||||
* @checked x & 1
|
||||
@@ -162,5 +469,9 @@ macro bool is_power_of_2(x)
|
||||
return x != 0 && (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
macro next_power_of_2(x)
|
||||
{
|
||||
$typeof(x) y = 1;
|
||||
while (y < x) y += y;
|
||||
return y;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ struct SimpleRandom
|
||||
long seed;
|
||||
}
|
||||
|
||||
private const long SIMPLE_RANDOM_MULTIPLIER = 0x5DEECE66Di64;
|
||||
private const long SIMPLE_RANDOM_MULTIPLIER = 0x5DEECE66D;
|
||||
private const long SIMPLE_RANDOM_ADDEND = 0xB;
|
||||
private const long SIMPLE_RANDOM_MASK = (1i64 << 48) - 1;
|
||||
|
||||
|
||||
284
lib/std/math_i128.c3
Normal file
284
lib/std/math_i128.c3
Normal file
@@ -0,0 +1,284 @@
|
||||
module std::math;
|
||||
|
||||
fn int128 __divti3(int128 a, int128 b) @extname("__divti3") @weak
|
||||
{
|
||||
int128 sign_a = a >> 127; // -1 : 0
|
||||
int128 sign_b = b >> 127; // -1 : 0
|
||||
uint128 unsigned_a = (uint128)(a ^ sign_a) + (-sign_a);
|
||||
uint128 unsigned_b = (uint128)(b ^ sign_b) + (-sign_b);
|
||||
sign_a ^= sign_b; // quotient sign
|
||||
return __udivti3(unsigned_a, unsigned_b) @inline ^ sign_a + (-sign_a);
|
||||
}
|
||||
|
||||
fn uint128 __umodti3(uint128 n, uint128 d) @extname("__umodti3") @weak
|
||||
{
|
||||
// Ignore d = 0
|
||||
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
|
||||
// If n < d then sr is wrapping.
|
||||
// which means we can just return n.
|
||||
if (sr > 127) return n;
|
||||
// If d == 1 and n = MAX
|
||||
if (sr == 127) return 0;
|
||||
sr++;
|
||||
uint128 r = n >> sr;
|
||||
// Follow known algorithm:
|
||||
n <<= 128 - sr;
|
||||
for (uint128 carry = 0; sr > 0; sr--)
|
||||
{
|
||||
r = (r << 1) | (n >> 127);
|
||||
n = (n << 1) | carry;
|
||||
int128 sign = (int128)(d - r - 1) >> 127;
|
||||
carry = sign & 1;
|
||||
r -= d & sign;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
fn uint128 __udivti3(uint128 n, uint128 d) @extname("__udivti3") @weak
|
||||
{
|
||||
// Ignore d = 0
|
||||
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
|
||||
// If n < d then sr is wrapping.
|
||||
// which means we can just return 0.
|
||||
if (sr > 127) return 0;
|
||||
// If d == 1 and n = MAX
|
||||
if (sr == 127) return n;
|
||||
sr++;
|
||||
uint128 r = n >> sr;
|
||||
// Follow known algorithm:
|
||||
n <<= 128 - sr;
|
||||
uint128 carry = 0;
|
||||
for (; sr > 0; sr--)
|
||||
{
|
||||
r = (r << 1) | (n >> 127);
|
||||
n = (n << 1) | carry;
|
||||
int128 sign = (int128)(d - r - 1) >> 127;
|
||||
carry = sign & 1;
|
||||
r -= d & sign;
|
||||
}
|
||||
n = (n << 1) | carry;
|
||||
return n;
|
||||
}
|
||||
|
||||
fn int128 __modti3(int128 a, int128 b) @extname("__modti3") @weak
|
||||
{
|
||||
int128 sign = b >> 127;
|
||||
uint128 unsigned_b = (uint128)(b ^ sign) + (-sign);
|
||||
sign = a >> 127;
|
||||
uint128 unsigned_a = (uint128)(a ^ sign) + (-sign);
|
||||
|
||||
return __umodti3(unsigned_a, unsigned_b) ^ sign + (-sign);
|
||||
}
|
||||
|
||||
fn float __floattisf(int128 a) @extname("__floattisf") @weak = float_from_i128(float, a);
|
||||
fn double __floattidf(int128 a) @extname("__floattidf") @weak = float_from_i128(double, a);
|
||||
fn float __floatuntisf(uint128 a) @extname("__floatuntisf") @weak = float_from_u128(float, a);
|
||||
fn double __floatuntidf(uint128 a) @extname("__floatuntidf") @weak = float_from_u128(double, a);
|
||||
fn uint128 __fixunsdfti(double a) @weak @extname("__fixunsdfti") = fixuint(a);
|
||||
fn uint128 __fixunssfti(float a) @weak @extname("__fixunssfti") = fixuint(a);
|
||||
fn int128 __fixdfti(double a) @weak @extname("__fixdfti") = fixint(a);
|
||||
fn int128 __fixsfti(float a) @weak @extname("__fixsfti") = fixint(a);
|
||||
|
||||
|
||||
private macro float_from_i128($Type, a)
|
||||
{
|
||||
var $Rep;
|
||||
$switch ($Type):
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const MANT_DIG = DOUBLE_MANT_DIG;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
const EXP_BIAS = 1023;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
|
||||
const SIGN_BIT = 1u64 << 63;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const MANT_DIG = FLOAT_MANT_DIG;
|
||||
const EXP_BIAS = 127;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
const MANTISSA_MASK = 0x7F_FFFFu32;
|
||||
const SIGN_BIT = 1u32 << 31;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const MANT_DIG = HALF_MANT_DIG;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const MANT_DIG = QUAD_MANT_DIG;
|
||||
$endswitch;
|
||||
if (a == 0) return ($Type)0;
|
||||
// Grab and remove sign.
|
||||
int128 sign = a >> 127;
|
||||
a = (a ^ sign) - sign;
|
||||
int sd = 128 - (int)$$clz(a); // digits
|
||||
int e = sd - 1; // exponent
|
||||
if (sd > MANT_DIG)
|
||||
{
|
||||
switch (sd)
|
||||
{
|
||||
case MANT_DIG + 1:
|
||||
a <<= 1;
|
||||
case MANT_DIG + 2:
|
||||
break;
|
||||
default:
|
||||
a = (a >> (sd - (MANT_DIG + 2)))
|
||||
| (uint128)((a & ((uint128)(-1) >> ((128 + MANT_DIG + 2) - sd))) != 0);
|
||||
}
|
||||
a |= (uint128)((a & 4) != 0);
|
||||
a++;
|
||||
a >>= 2;
|
||||
if (a & (1i128 << MANT_DIG))
|
||||
{
|
||||
a >>= 1;
|
||||
e++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a <<= (MANT_DIG - sd);
|
||||
}
|
||||
return bitcast(((($Rep)sign & SIGN_BIT) | ((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS)) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type);
|
||||
}
|
||||
|
||||
private macro float_from_u128($Type, a)
|
||||
{
|
||||
var $Rep;
|
||||
$switch ($Type):
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const MANT_DIG = DOUBLE_MANT_DIG;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
const EXP_BIAS = 1023;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const MANT_DIG = FLOAT_MANT_DIG;
|
||||
const EXP_BIAS = 127;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
const MANTISSA_MASK = 0x7F_FFFFu32;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const MANT_DIG = HALF_MANT_DIG;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const MANT_DIG = QUAD_MANT_DIG;
|
||||
$endswitch;
|
||||
if (a == 0) return ($Type)0;
|
||||
int sd = 128 - (int)$$clz(a); // digits
|
||||
int e = sd - 1; // exponent
|
||||
if (sd > MANT_DIG)
|
||||
{
|
||||
switch (sd)
|
||||
{
|
||||
case MANT_DIG + 1:
|
||||
a <<= 1;
|
||||
case MANT_DIG + 2:
|
||||
break;
|
||||
default:
|
||||
a = (a >> (sd - (MANT_DIG + 2)))
|
||||
| (uint128)((a & ((uint128)(-1) >> ((128 + MANT_DIG + 2) - sd))) != 0);
|
||||
}
|
||||
a |= (uint128)((a & 4) != 0);
|
||||
a++;
|
||||
a >>= 2;
|
||||
if (a & (1i128 << MANT_DIG))
|
||||
{
|
||||
a >>= 1;
|
||||
e++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a <<= (MANT_DIG - sd);
|
||||
}
|
||||
return bitcast(((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type);
|
||||
}
|
||||
|
||||
|
||||
private macro fixuint(a)
|
||||
{
|
||||
var $Rep;
|
||||
$switch ($typeof(a)):
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const EXPONENT_BITS = 11;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const EXPONENT_BITS = 8;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const EXPONENT_BITS = 5;
|
||||
const SIGNIFICANT_BITS = 10;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const EXPONENT_BITS = 15;
|
||||
const SIGNIFICANT_BITS = 112;
|
||||
$endswitch;
|
||||
const $Rep MAX_EXPONENT = ($Rep)1 << EXPONENT_BITS - 1u;
|
||||
const $Rep EXPONENT_BIAS = MAX_EXPONENT >> 1u;
|
||||
const $Rep ONE_REP =EXPONENT_BIAS << SIGNIFICANT_BITS;
|
||||
const $Rep SIGN_BIT = ($Rep)1 << (EXPONENT_BITS + SIGNIFICANT_BITS);
|
||||
const $Rep ABS_MASK = SIGN_BIT - 1u;
|
||||
const $Rep IMPLICIT_BIT = ($Rep)1 << SIGNIFICANT_BITS;
|
||||
const $Rep SIGNIFICANT_MASK = IMPLICIT_BIT - 1u;
|
||||
const $Rep EXPONENT_MASK = ABS_MASK ^ SIGNIFICANT_MASK;
|
||||
const $Rep QUIET_BIT = IMPLICIT_BIT >> 1;
|
||||
const $Rep QNAN_REP = EXPONENT_MASK | QUIET_BIT;
|
||||
const $Rep INF_REP = EXPONENT_MASK;
|
||||
|
||||
$Rep rep = bitcast(a, $Rep);
|
||||
$Rep abs = rep & ABS_MASK;
|
||||
int sign = rep & SIGN_BIT ? -1 : 1;
|
||||
int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS);
|
||||
$Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT;
|
||||
if (sign == -1 || exponent < 0) return 0u128;
|
||||
if ((uint)exponent >= uint128.sizeof * 8) return ~0u128;
|
||||
if (exponent < SIGNIFICANT_BITS) return (uint128)significand >> (SIGNIFICANT_BITS - exponent);
|
||||
return (uint128)significand << (exponent - SIGNIFICANT_BITS);
|
||||
}
|
||||
|
||||
private macro fixint(a)
|
||||
{
|
||||
var $Rep;
|
||||
$switch ($typeof(a)):
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const EXPONENT_BITS = 11;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const EXPONENT_BITS = 8;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const EXPONENT_BITS = 5;
|
||||
const SIGNIFICANT_BITS = 10;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const EXPONENT_BITS = 15;
|
||||
const SIGNIFICANT_BITS = 112;
|
||||
$endswitch;
|
||||
const $Rep MAX_EXPONENT = ($Rep)1 << EXPONENT_BITS - 1u;
|
||||
const $Rep EXPONENT_BIAS = MAX_EXPONENT >> 1u;
|
||||
const $Rep ONE_REP = EXPONENT_BIAS << SIGNIFICANT_BITS;
|
||||
const $Rep SIGN_BIT = ($Rep)1 << (EXPONENT_BITS + SIGNIFICANT_BITS);
|
||||
const $Rep ABS_MASK = SIGN_BIT - 1u;
|
||||
const $Rep IMPLICIT_BIT = ($Rep)1 << SIGNIFICANT_BITS;
|
||||
const $Rep SIGNIFICANT_MASK = IMPLICIT_BIT - 1u;
|
||||
const $Rep EXPONENT_MASK = ABS_MASK ^ SIGNIFICANT_MASK;
|
||||
const $Rep QUIET_BIT = IMPLICIT_BIT >> 1;
|
||||
const $Rep QNAN_REP = EXPONENT_MASK | QUIET_BIT;
|
||||
const $Rep INF_REP = EXPONENT_MASK;
|
||||
|
||||
$Rep rep = bitcast(a, $Rep);
|
||||
$Rep abs = rep & ABS_MASK;
|
||||
int sign = rep & SIGN_BIT ? -1 : 1;
|
||||
int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS);
|
||||
$Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT;
|
||||
if (exponent < 0) return 0;
|
||||
if ((uint)exponent >= uint128.sizeof * 8) return sign == 1 ? int128.max : int128.min;
|
||||
|
||||
if (exponent < SIGNIFICANT_BITS) return sign * ((int128)significand >> (SIGNIFICANT_BITS - exponent));
|
||||
return sign * ((int128)significand << (exponent - SIGNIFICANT_BITS));
|
||||
}
|
||||
21
lib/std/os/macos/cf_allocator.c3
Normal file
21
lib/std/os/macos/cf_allocator.c3
Normal file
@@ -0,0 +1,21 @@
|
||||
module std::os::macos::cf;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
|
||||
define CFAllocatorRef = distinct void*;
|
||||
define CFAllocatorContextRef = distinct void*;
|
||||
define CFOptionFlags = usz;
|
||||
|
||||
macro CFAllocatorRef default_allocator() = _macos_CFAllocatorGetDefault();
|
||||
macro void CFAllocatorRef.dealloc(CFAllocatorRef allocator, void* ptr) = _macos_CFAllocatorDeallocate(allocator, ptr);
|
||||
macro void* CFAllocatorRef.alloc(CFAllocatorRef allocator, usz size) = _macos_CFAllocatorAllocate(allocator, size, 0);
|
||||
macro usz CFAllocatorRef.get_preferred_size(CFAllocatorRef allocator, usz req_size) = _macos_CFAllocatorGetPreferredSizeForSize(allocator, req_size, 0);
|
||||
macro void CFAllocatorRef.set_default(CFAllocatorRef allocator) = _macos_CFAllocatorSetDefault(allocator);
|
||||
|
||||
extern fn CFAllocatorRef _macos_CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContextRef context) @extname("CFAllocatorCreate");
|
||||
extern fn void _macos_CFAllocatorDeallocate(CFAllocatorRef allocator, void* ptr) @extname("CFAllocatorDeallocate");
|
||||
extern fn CFAllocatorRef _macos_CFAllocatorGetDefault() @extname("CFAllocatorGetDefault");
|
||||
extern fn void _macos_CFAllocatorSetDefault(CFAllocatorRef allocator) @extname("CFAllocatorSetDefault");
|
||||
extern fn void* _macos_CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extname("CFAllocatorAllocate");
|
||||
extern fn CFIndex _macos_CFAllocatorGetPreferredSizeForSize(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extname("CFAllocatorGetPreferredSizeForSize");
|
||||
$endif;
|
||||
14
lib/std/os/macos/cf_array.c3
Normal file
14
lib/std/os/macos/cf_array.c3
Normal file
@@ -0,0 +1,14 @@
|
||||
module std::os::macos::cf;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
|
||||
define CFArrayRef = distinct void*;
|
||||
define CFArrayCallBacksRef = distinct void*;
|
||||
define CFMutableArrayRef = distinct void*;
|
||||
extern fn CFArrayRef _macos_CFArrayCreate(CFAllocatorRef allocator, void** values, CFIndex num_values, CFArrayCallBacksRef callBacks) @extname("CFArrayCreate");
|
||||
extern fn CFArrayRef _macos_CFArrayCopy(CFAllocatorRef allocator, CFArrayRef array) @extname("CFArrayCopy");
|
||||
extern fn CFIndex _macos_CFArrayGetCount(CFArrayRef array) @extname("CFArrayGetCount");
|
||||
extern fn void _macos_CFArrayAppendArray(CFMutableArrayRef theArray, CFArrayRef otherArray, CFRange otherRange) @extname("CFArrayAppendArray");
|
||||
extern fn CFMutableArrayRef _macos_CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, CFArrayCallBacksRef callBacks) @extname("CFArrayCreateMutable");
|
||||
extern fn void _macos_CFArrayAppendValue(CFMutableArrayRef theArray, void *value) @extname("CFArrayAppendValue");
|
||||
$endif;
|
||||
16
lib/std/os/macos/core_foundation.c3
Normal file
16
lib/std/os/macos/core_foundation.c3
Normal file
@@ -0,0 +1,16 @@
|
||||
module std::os::macos::cf;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
|
||||
define CFTypeRef = distinct void*;
|
||||
define CFIndex = isz;
|
||||
struct CFRange
|
||||
{
|
||||
CFIndex location;
|
||||
CFIndex length;
|
||||
}
|
||||
|
||||
extern fn CFTypeRef _macos_CFRetain(CFTypeRef cf) @extname("CFRetain");
|
||||
extern fn void _macos_CFRelease(CFTypeRef cf) @extname("CFRelease");
|
||||
|
||||
$endif;
|
||||
49
lib/std/os/macos/objc.c3
Normal file
49
lib/std/os/macos/objc.c3
Normal file
@@ -0,0 +1,49 @@
|
||||
module std::os::macos::objc;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
|
||||
define Class = distinct void*;
|
||||
define Method = distinct void*;
|
||||
define Ivar = distinct void*;
|
||||
define Selector = distinct void*;
|
||||
|
||||
fault ObjcFailure
|
||||
{
|
||||
CLASS_NOT_FOUND
|
||||
}
|
||||
|
||||
macro char* Class.name(Class cls) = _macos_class_getName(cls);
|
||||
macro Class Class.superclass(Class cls) = _macos_class_getSuperclass(cls);
|
||||
macro bool Class.responds_to(Class cls, Selector sel) = _macos_class_respondsToSelector(cls, sel);
|
||||
macro Method Class.method(Class cls, Selector name) = _macos_class_getClassMethod(cls, name);
|
||||
|
||||
macro bool Selector.equals(Selector a, Selector b) = a == b;
|
||||
macro bool Class.equals(Class a, Class b) = a == b;
|
||||
|
||||
macro Selector selector_register(char* c) = _macos_sel_registerName(c);
|
||||
macro Class! class_by_name(char* c)
|
||||
{
|
||||
Class cls = _macos_objc_lookUpClass(c);
|
||||
if (!cls) return ObjcFailure.CLASS_NOT_FOUND!;
|
||||
return cls;
|
||||
}
|
||||
|
||||
macro Class[] class_get_list(Allocator *allocator = mem::current_allocator())
|
||||
{
|
||||
int num_classes = _macos_objc_getClassList(null, 0);
|
||||
if (!num_classes) return {};
|
||||
Class[] entries = array::make(Class, num_classes, allocator);
|
||||
_macos_objc_getClassList(entries.ptr, entries.len);
|
||||
return entries;
|
||||
}
|
||||
|
||||
extern fn Class _macos_objc_getClass(char* name) @extname("objc_getClass");
|
||||
extern fn int _macos_objc_getClassList(Class* buffer, int buffer_count) @extname("objc_getClassList");
|
||||
extern fn char* _macos_class_getName(Class cls) @extname("class_getName");
|
||||
extern fn Class _macos_class_getSuperclass(Class cls) @extname("class_getSuperclass");
|
||||
extern fn Method _macos_class_getClassMethod(Class cls, Selector name) @extname("class_getClassMethod");
|
||||
extern fn bool _macos_class_respondsToSelector(Class cls, Selector name) @extname("class_respondsToSelector");
|
||||
extern fn Selector _macos_sel_registerName(char* str) @extname("sel_registerName");
|
||||
extern fn Class _macos_objc_lookUpClass(char* name) @extname("objc_lookUpClass");
|
||||
$endif;
|
||||
|
||||
12
lib/std/os/win32/files.c3
Normal file
12
lib/std/os/win32/files.c3
Normal file
@@ -0,0 +1,12 @@
|
||||
module std::os::win32::files;
|
||||
|
||||
$if (env::OS_TYPE == OsType.WIN32):
|
||||
/*
|
||||
private extern ulong _win32_GetCurrentDirectoryW(ulong, Char16* buffer) @extname("GetCurrentDirectoryW");
|
||||
private extern bool _win32_CreateSymbolicLinkW(Char16* symlink_file, Char16* target_file, ulong flags) @extname("CreateSymbolicLinkW");
|
||||
private extern bool _win32_CreateDirectoryW(Char16* path_name, void* security_attributes) @extname("CreateDirectoryW");
|
||||
private extern bool _win32_DeleteFileW(Char16* file) @extname("DeleteFileW");
|
||||
private extern bool _win32_CopyFileW(Char16* from_file, Char16* to_file, bool no_overwrite) @extname("CopyFileW");
|
||||
private extern ulong _win32_GetFullPathNameW(Char16* file_name, ulong buffer_len, Char16* buffer, Char16** file_part) @extname("GetFullPathNameW");
|
||||
*/
|
||||
$endif;
|
||||
@@ -34,10 +34,10 @@ struct PriorityQueue
|
||||
fn void PriorityQueue.push(PriorityQueue* pq, Type element)
|
||||
{
|
||||
pq.heap.push(element);
|
||||
usize i = pq.heap.len() - 1;
|
||||
usz i = pq.heap.len() - 1;
|
||||
while (i > 0)
|
||||
{
|
||||
usize parent = (i - 1) / 2;
|
||||
usz parent = (i - 1) / 2;
|
||||
if ((pq.max && greater(pq.heap.get(i), pq.heap.get(parent))) || (!pq.max && less(pq.heap.get(i), pq.heap.get(parent))))
|
||||
{
|
||||
pq.heap.swap(i, parent);
|
||||
@@ -53,14 +53,14 @@ fn void PriorityQueue.push(PriorityQueue* pq, Type element)
|
||||
*/
|
||||
fn Type! PriorityQueue.pop(PriorityQueue* pq)
|
||||
{
|
||||
usize i = 0;
|
||||
usize len = pq.heap.len() @inline;
|
||||
usz i = 0;
|
||||
usz len = pq.heap.len() @inline;
|
||||
if (!len) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
usize newCount = len - 1;
|
||||
usz newCount = len - 1;
|
||||
pq.heap.swap(0, newCount);
|
||||
while ((2 * i + 1) < newCount)
|
||||
{
|
||||
usize j = 2 * i + 1;
|
||||
usz j = 2 * i + 1;
|
||||
if (((j + 1) < newCount) &&
|
||||
((pq.max && greater(pq.heap.get(j + 1), pq.heap[j]))
|
||||
|| (!pq.max && less(pq.heap.get(j + 1), pq.heap.get(j)))))
|
||||
@@ -99,7 +99,15 @@ fn void PriorityQueue.free(PriorityQueue* pq)
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn usize PriorityQueue.len(PriorityQueue* pq) @operator(len)
|
||||
fn usz PriorityQueue.len(PriorityQueue* pq) @operator(len)
|
||||
{
|
||||
return pq.heap.len();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null, index < pq.len()
|
||||
*/
|
||||
fn Type PriorityQueue.peek_at(PriorityQueue* pq, usz index) @operator([])
|
||||
{
|
||||
return pq.heap[index];
|
||||
}
|
||||
|
||||
@@ -18,13 +18,82 @@ struct VirtualContainer
|
||||
struct SubArrayContainer
|
||||
{
|
||||
void* ptr;
|
||||
usize len;
|
||||
usz len;
|
||||
}
|
||||
|
||||
struct VarArrayHeader
|
||||
{
|
||||
usize size;
|
||||
usize capacity;
|
||||
usz size;
|
||||
usz capacity;
|
||||
void *allocator;
|
||||
}
|
||||
|
||||
define TestFn = fn void!();
|
||||
|
||||
struct TestRunner
|
||||
{
|
||||
char[][] test_names;
|
||||
TestFn[] test_fns;
|
||||
JmpBuf buf;
|
||||
}
|
||||
|
||||
fn TestRunner test_runner_create()
|
||||
{
|
||||
return TestRunner {
|
||||
.test_fns = $$TEST_FNS,
|
||||
.test_names = $$TEST_NAMES,
|
||||
};
|
||||
}
|
||||
|
||||
import libc;
|
||||
|
||||
private TestRunner* current_runner;
|
||||
fn void test_panic(char[] message, char[] file, char[] function, uint line)
|
||||
{
|
||||
io::println("[error]");
|
||||
io::printfln("\n Error: %s", message);
|
||||
io::printfln(" - in %s %s:%s.\n", function, file, line);
|
||||
libc::longjmp(¤t_runner.buf, 1);
|
||||
}
|
||||
|
||||
fn bool TestRunner.run(TestRunner* runner)
|
||||
{
|
||||
current_runner = runner;
|
||||
PanicFn old_panic = builtin::panic;
|
||||
defer builtin::panic = old_panic;
|
||||
builtin::panic = &test_panic;
|
||||
int tests_passed = 0;
|
||||
int tests = runner.test_names.len;
|
||||
io::println("----- TESTS -----");
|
||||
foreach(i, char[] name : runner.test_names)
|
||||
{
|
||||
io::printf("Testing %s ... ", name);
|
||||
if (libc::setjmp(&runner.buf) == 0)
|
||||
{
|
||||
if (catch err = runner.test_fns[i]())
|
||||
{
|
||||
io::println("[failed]");
|
||||
continue;
|
||||
}
|
||||
io::println("[ok]");
|
||||
tests_passed++;
|
||||
}
|
||||
}
|
||||
io::printfln("\n%d test(s) run.\n", tests);
|
||||
io::print("Test Result: ");
|
||||
if (tests_passed < tests)
|
||||
{
|
||||
io::print("FAILED");
|
||||
}
|
||||
else
|
||||
{
|
||||
io::print("ok");
|
||||
}
|
||||
io::printfln(". %d passed, %d failed.", tests_passed, tests - tests_passed);
|
||||
return tests == tests_passed;
|
||||
}
|
||||
|
||||
fn bool __run_default_test_runner()
|
||||
{
|
||||
return test_runner_create().run();
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Script based on https://gist.github.com/mmozeiko/7f3162ec2988e81e56d5c4e22cde9977
|
||||
# by Mārtiņš Možeiko.
|
||||
# Changes and additions to the gist are licensed under the CC0 License.
|
||||
|
||||
import platform
|
||||
import io
|
||||
import os
|
||||
|
||||
42
releasenotes.md
Normal file
42
releasenotes.md
Normal file
@@ -0,0 +1,42 @@
|
||||
Release Notes
|
||||
|
||||
0.4.0 Change List
|
||||
|
||||
- Compatibility with LLVM 16.
|
||||
- Dropped LLVM 12 support.
|
||||
- Updated memory allocators. Added @scoped and @pool macros.
|
||||
- Various bug fixes.
|
||||
- Constant pointers may be compile time evaluated.
|
||||
- Added many new builtins.
|
||||
- Emit asm using --emit-asm.
|
||||
- Added --nostdlib and --nolibc.
|
||||
- Fixes to adding libraries at link time.
|
||||
- Various improved error messages.
|
||||
- Windows debug info fixes.
|
||||
- Add of `foreach_r`.
|
||||
- Script downloading the MSVC SDK to cross compile to windows.
|
||||
- Many standard library additions.
|
||||
- Extension methods may be added for built-in types.
|
||||
- Macros may take vector and array arguments generic over length.
|
||||
- Macro varargs with $vaarg, $vacount etc.
|
||||
- Many vector builtins added as dot methods.
|
||||
- in / out / inout doc parameters checked.
|
||||
- Initial inline asm support for aarch64 and x64.
|
||||
- Single line short function declaration.
|
||||
- Added `$checks` builtin.
|
||||
- Optional single module compilation.
|
||||
- Static initialization / finalization to have code running at start/end.
|
||||
- C3 custom printf function in the stdlib.
|
||||
- `[]=` overload now works correctly.
|
||||
- More compile time reflection added and general cleanup done.
|
||||
- usize/isize/iptrdiff/uptrdiff replaced by usz/isz.
|
||||
- Add `var` to allow type inference on regular variables.
|
||||
- LLVM codegen optimizations.
|
||||
- `??` now allows chaining another optional.
|
||||
- int128 support on all platforms.
|
||||
- `import` is now allowed anywhere at the top level.
|
||||
- `project.c3p` renamed `project.json`
|
||||
- Update to project properties, e.g. "libs" -> "dependencies" etc.
|
||||
- $$TIME, $$DATE and $$FUNCTION builtin defines added.
|
||||
- Improvements to untyped lists.
|
||||
- Various builtins added: $$prefetch, $$reverse, $$shufflevector etc.
|
||||
@@ -61,7 +61,7 @@ fn void encode(char[] in, char *out)
|
||||
}
|
||||
|
||||
// move back
|
||||
usize last = in.len - 1;
|
||||
usz last = in.len - 1;
|
||||
// check the last and add padding
|
||||
switch (last % 3)
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ fn String bin(int x)
|
||||
str.append_repeat('0', bits);
|
||||
for (int i = 0; i < bits; i++)
|
||||
{
|
||||
str.set((usize)(bits - i - 1), x & 1 ? '1' : '0');
|
||||
str.set((usz)(bits - i - 1), x & 1 ? '1' : '0');
|
||||
x >>= 1;
|
||||
}
|
||||
return str;
|
||||
|
||||
86
resources/examples/brainfk.c3
Normal file
86
resources/examples/brainfk.c3
Normal file
@@ -0,0 +1,86 @@
|
||||
// #target: macos-x64
|
||||
module brainfk;
|
||||
import std::io;
|
||||
|
||||
const BF_MEM = 30000;
|
||||
char[BF_MEM] memory;
|
||||
|
||||
fault InterpretError
|
||||
{
|
||||
INTEPRET_FAILED
|
||||
}
|
||||
|
||||
fn void! print_error(usz pos, char[] err)
|
||||
{
|
||||
io::printfln("Error at %s: %s", pos, err);
|
||||
return InterpretError.INTEPRET_FAILED!;
|
||||
}
|
||||
|
||||
fn void! brainf(char[] program)
|
||||
{
|
||||
usz sp = 0;
|
||||
usz mem = 0;
|
||||
while (sp < program.len)
|
||||
{
|
||||
char c = program[sp++];
|
||||
switch (c)
|
||||
{
|
||||
case '<':
|
||||
if (!mem) return print_error(sp, "Memory underflow");
|
||||
mem--;
|
||||
case '>':
|
||||
if (mem == BF_MEM - 1) return print_error(sp, "Memory overflow");
|
||||
mem++;
|
||||
case '+':
|
||||
memory[mem]++;
|
||||
case '-':
|
||||
memory[mem]--;
|
||||
case '.':
|
||||
io::putchar(memory[mem]);
|
||||
io::stdout().flush();
|
||||
case ',':
|
||||
memory[mem] = io::stdin().getc()!!;
|
||||
case '[':
|
||||
usz indent = 1;
|
||||
if (memory[mem]) continue;
|
||||
usz start = sp - 1;
|
||||
while (indent)
|
||||
{
|
||||
if (sp == program.len) return print_error(start, "No matching ']'");
|
||||
switch (program[sp++])
|
||||
{
|
||||
case ']': indent--;
|
||||
case '[': indent++;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
case ']':
|
||||
if (!memory[mem]) continue;
|
||||
usz start = sp--;
|
||||
usz indent = 1;
|
||||
while (indent)
|
||||
{
|
||||
if (!sp) return print_error(start, "No matching '['");
|
||||
switch (program[--sp])
|
||||
{
|
||||
case ']': indent++;
|
||||
case '[': indent--;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
sp++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fn void! main()
|
||||
{
|
||||
char[] program = `
|
||||
++++[>+++++<-]>[<+++++>-]+<+[
|
||||
>[>+>+<<-]++>>[<<+>>-]>>>[-]++>[-]+
|
||||
>>>+[[-]++++++>>>]<<<[[<++++++++<++>>-]+<.<[>----<-]<]
|
||||
<<[>>>>>[>>>[-]+++++++++<[>-<-]+++++++++>[-[<->-]+[<<<]]<[>+<-]>]<<-]<<-
|
||||
]`;
|
||||
brainf(program)?;
|
||||
}
|
||||
@@ -14,8 +14,8 @@ struct Summary
|
||||
private struct StringData
|
||||
{
|
||||
Allocator allocator;
|
||||
usize len;
|
||||
usize capacity;
|
||||
usz len;
|
||||
usz capacity;
|
||||
char[*] chars;
|
||||
}
|
||||
|
||||
@@ -27,12 +27,12 @@ fn void Summary.print(Summary *s, File out)
|
||||
|
||||
fn bool contains(char[] haystack, char[] needle)
|
||||
{
|
||||
usize len = haystack.len;
|
||||
usize needle_len = needle.len;
|
||||
usz len = haystack.len;
|
||||
usz needle_len = needle.len;
|
||||
if (len < needle_len) return false;
|
||||
if (!needle_len) return true;
|
||||
len -= needle_len - 1;
|
||||
for (usize i = 0; i < len; i++)
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (mem::equals(haystack[i..], needle))
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@ module guess_number;
|
||||
import std::io;
|
||||
import libc;
|
||||
|
||||
extern fn isize getline(char** linep, usize* linecapp, CFile stream);
|
||||
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
|
||||
|
||||
struct Game
|
||||
{
|
||||
@@ -33,7 +33,7 @@ fn int! askGuess(int high)
|
||||
fn char[]! readLine()
|
||||
{
|
||||
char* chars = tmalloc(1024)?;
|
||||
isize loaded = getline(&chars, &&(usize)1023, libc::stdin());
|
||||
isz loaded = getline(&chars, &&(usz)1023, libc::stdin());
|
||||
if (loaded < 0) return InputResult.FAILED_TO_READ!;
|
||||
chars[loaded] = 0;
|
||||
return chars[0..(loaded - 1)];
|
||||
|
||||
@@ -56,7 +56,7 @@ const LINELEN = 60;
|
||||
// slowest character-at-a-time output
|
||||
fn void repeat_fasta(char[] seq, int n)
|
||||
{
|
||||
usize len = seq.len;
|
||||
usz len = seq.len;
|
||||
int i = void;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module hash;
|
||||
module std::hash;
|
||||
import libc;
|
||||
|
||||
// Code adapted from Odin's hash.odin
|
||||
|
||||
97
resources/examples/map.c3
Normal file
97
resources/examples/map.c3
Normal file
@@ -0,0 +1,97 @@
|
||||
module std::container::map <Key, Type>;
|
||||
import std::core::builtin;
|
||||
import std::io;
|
||||
fault MapResult
|
||||
{
|
||||
KEY_NOT_FOUND
|
||||
}
|
||||
|
||||
struct Entry
|
||||
{
|
||||
Key key;
|
||||
Type value;
|
||||
usz hash;
|
||||
Entry* next;
|
||||
bool used;
|
||||
}
|
||||
|
||||
struct Map
|
||||
{
|
||||
usz size;
|
||||
Entry* map;
|
||||
uint mod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require map != null
|
||||
**/
|
||||
fn void Map.init(Map *map, uint capacity = 128)
|
||||
{
|
||||
if (capacity < 16) capacity = 4;
|
||||
capacity = math::next_power_of_2(capacity);
|
||||
map.map = calloc(Entry.sizeof * capacity);
|
||||
map.mod = capacity - 1;
|
||||
}
|
||||
|
||||
fn Type! Map.valueForKey(Map *map, Key key)
|
||||
{
|
||||
if (!map.map) return MapResult.KEY_NOT_FOUND!;
|
||||
uint hash = key.hash();
|
||||
usz pos = hash & map.mod;
|
||||
Entry* entry = &map.map[pos];
|
||||
if (!entry) return MapResult.KEY_NOT_FOUND!;
|
||||
while (entry)
|
||||
{
|
||||
if (entry.hash == hash && entry.key == key) return entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
return MapResult.KEY_NOT_FOUND!;
|
||||
}
|
||||
|
||||
fn Type! Map.set(Map *map, Key key, Type value) @maydiscard
|
||||
{
|
||||
if (!map.map)
|
||||
{
|
||||
map.map = calloc(Entry.sizeof * 16);
|
||||
map.mod = 0x0F;
|
||||
}
|
||||
|
||||
uint hash = key.hash();
|
||||
uint pos = hash & map.mod;
|
||||
Entry *entry = &map.map[pos];
|
||||
while (1)
|
||||
{
|
||||
if (!entry.used)
|
||||
{
|
||||
entry.used = true;
|
||||
entry.value = value;
|
||||
entry.hash = hash;
|
||||
entry.key = key;
|
||||
return MapResult.KEY_NOT_FOUND!;
|
||||
}
|
||||
if (entry.hash == hash && entry.key == key)
|
||||
{
|
||||
Type old = entry.value;
|
||||
entry.value = value;
|
||||
return old;
|
||||
}
|
||||
if (entry.next)
|
||||
{
|
||||
entry = entry.next;
|
||||
continue;
|
||||
}
|
||||
Entry* new = mem::calloc(Entry.sizeof);
|
||||
new.hash = hash;
|
||||
new.key = key;
|
||||
new.value = value;
|
||||
new.next = null;
|
||||
new.used = true;
|
||||
entry.next = new;
|
||||
return MapResult.KEY_NOT_FOUND!;
|
||||
}
|
||||
}
|
||||
|
||||
fn usz Map.size(Map* map)
|
||||
{
|
||||
return map.size;
|
||||
}
|
||||
@@ -15,10 +15,10 @@ struct Planet
|
||||
|
||||
fn void advance(Planet[] bodies) @noinline
|
||||
{
|
||||
usize nbodies = bodies.len;
|
||||
usz nbodies = bodies.len;
|
||||
foreach (i, Planet* &b : bodies)
|
||||
{
|
||||
for (usize j = i + 1; j < nbodies; j++)
|
||||
for (usz j = i + 1; j < nbodies; j++)
|
||||
{
|
||||
Planet* b2 = &bodies[j];
|
||||
double dx = b.x - b2.x;
|
||||
@@ -45,12 +45,12 @@ fn void advance(Planet[] bodies) @noinline
|
||||
fn double energy(Planet[] bodies)
|
||||
{
|
||||
double e;
|
||||
usize nbodies = bodies.len;
|
||||
usz nbodies = bodies.len;
|
||||
|
||||
foreach (i, Planet* &b : bodies)
|
||||
{
|
||||
e += 0.5 * b.mass * (b.vx * b.vx + b.vy * b.vy + b.vz * b.vz);
|
||||
for (usize j = i + 1; j < nbodies; j++)
|
||||
for (usz j = i + 1; j < nbodies; j++)
|
||||
{
|
||||
Planet* b2 = &bodies[j];
|
||||
double dx = b.x - b2.x;
|
||||
|
||||
@@ -16,14 +16,14 @@ macro assert_exp($c, $e)
|
||||
*/
|
||||
|
||||
/** A signed integer, whose size matches Value */
|
||||
typedef isize Aint;
|
||||
typedef isz Aint;
|
||||
/** An unsigned integer, whose size matches Value */
|
||||
typedef usize Auint;
|
||||
typedef usz Auint;
|
||||
|
||||
|
||||
/** A float, whose size matches Value (see avm_env.h) */
|
||||
$assert(usize.size == 8 || usize.size == 4)
|
||||
$if (usize.size == 8)
|
||||
$assert(usz.size == 8 || usz.size == 4)
|
||||
$if (usz.size == 8)
|
||||
{
|
||||
typedef double as Afloat;
|
||||
}
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
module std::container::map <Key, Type>;
|
||||
|
||||
fault MapResult
|
||||
{
|
||||
KEY_NOT_FOUND
|
||||
}
|
||||
|
||||
public struct Entry
|
||||
{
|
||||
Key key;
|
||||
Type value;
|
||||
usize hash;
|
||||
Entry* next;
|
||||
}
|
||||
|
||||
public struct Map
|
||||
{
|
||||
usize size;
|
||||
void* map;
|
||||
uint mod;
|
||||
Allocator allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require map != null
|
||||
**/
|
||||
public fn void Map.init(Map *map, Allocator allocator)
|
||||
{
|
||||
map.allocator = allocator;
|
||||
}
|
||||
|
||||
public fn Type! Map.valueForKey(Map *map, Key key)
|
||||
{
|
||||
if (!map.map) return null;
|
||||
usize hash = key.hash();
|
||||
usize pos = hash & map.mod;
|
||||
Entry* entry = &map.map[pop];
|
||||
if (!entry) return MapResult.KEY_NOT_FOUND!;
|
||||
while (entry)
|
||||
{
|
||||
if (entry.hash == hash && entry.key == key) return entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
return MapResult.KEY_NOT_FOUND!;
|
||||
}
|
||||
|
||||
public fn Type *Map.set(Map *map, Key key, Type value)
|
||||
{
|
||||
if (!map.map)
|
||||
{
|
||||
map.map = allocator.calloc(Entry, 16);
|
||||
map.mod = 0x0F;
|
||||
}
|
||||
|
||||
usize hash = key.hash();
|
||||
usize pos = hash & map.mod;
|
||||
Entry *entry = &map.map[pop];
|
||||
while (1)
|
||||
{
|
||||
if (!entry.value)
|
||||
{
|
||||
entry.value = value;
|
||||
entry.hash = hash;
|
||||
entry.key = key;
|
||||
return nil;
|
||||
}
|
||||
if (entry.hash == hash && entry.key == key)
|
||||
{
|
||||
Type *old = entry.value;
|
||||
entry.value = value;
|
||||
return old;
|
||||
}
|
||||
if (entry.next)
|
||||
{
|
||||
entry = entry.next;
|
||||
}
|
||||
entry.next = allocator.alloc(Entry);
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
|
||||
public fn usize Map.size(Map* map)
|
||||
{
|
||||
return map.size;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ fn char[]! read_next(char[]* remaining)
|
||||
|
||||
// Store the beginning of the parse
|
||||
char* ptr_start = remaining.ptr;
|
||||
usize len = 0;
|
||||
usz len = 0;
|
||||
while (remaining.len > 0 && (*remaining)[0] != ' ')
|
||||
{
|
||||
// Increase length
|
||||
|
||||
@@ -238,8 +238,8 @@ macro_argument_list
|
||||
;
|
||||
|
||||
declaration
|
||||
: failable_type IDENT '=' initializer
|
||||
| failable_type IDENT
|
||||
: optional_type IDENT '=' initializer
|
||||
| optional_type IDENT
|
||||
;
|
||||
|
||||
param_declaration
|
||||
@@ -291,7 +291,7 @@ type
|
||||
| type '[' '+' ']'
|
||||
;
|
||||
|
||||
failable_type
|
||||
optional_type
|
||||
: type
|
||||
| type '!'
|
||||
;
|
||||
@@ -419,8 +419,8 @@ expression_statement
|
||||
|
||||
|
||||
if_expr
|
||||
: failable_type IDENT '=' initializer
|
||||
| failable_type IDENT NOFAIL_ASSIGN expression
|
||||
: optional_type IDENT '=' initializer
|
||||
| optional_type IDENT NOFAIL_ASSIGN expression
|
||||
| expression
|
||||
;
|
||||
|
||||
@@ -513,7 +513,7 @@ func_name
|
||||
;
|
||||
|
||||
func_declaration
|
||||
: FUNC failable_type func_name opt_parameter_type_list opt_attributes
|
||||
: FUNC optional_type func_name opt_parameter_type_list opt_attributes
|
||||
;
|
||||
|
||||
func_definition
|
||||
@@ -594,7 +594,7 @@ const_declaration
|
||||
;
|
||||
|
||||
func_typedef
|
||||
: FUNC failable_type opt_parameter_type_list
|
||||
: FUNC optional_type opt_parameter_type_list
|
||||
;
|
||||
|
||||
typedef_declaration
|
||||
|
||||
@@ -5,8 +5,8 @@ import std::io;
|
||||
struct String
|
||||
{
|
||||
Allocator allocator;
|
||||
usize len;
|
||||
usize capacity;
|
||||
usz len;
|
||||
usz capacity;
|
||||
char* ptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,26 +12,27 @@
|
||||
"./**"
|
||||
],
|
||||
// libraries to use
|
||||
"libs": [],
|
||||
"dependencies": [],
|
||||
|
||||
// c compiler
|
||||
"cc": "cc",
|
||||
// c sources
|
||||
"targets": {
|
||||
"hello_world": {
|
||||
"type": "executable",
|
||||
"csources": [
|
||||
"c-sources-override": [
|
||||
"./csource/**"
|
||||
]
|
||||
},
|
||||
"hello_world_win32": {
|
||||
"type": "executable",
|
||||
"csources": [
|
||||
"c-sources-override": [
|
||||
"./csource/**"
|
||||
]
|
||||
},
|
||||
"hello_world_lib": {
|
||||
"type": "static-lib",
|
||||
"csources": [
|
||||
"c-sources-override": [
|
||||
"./csource/**"
|
||||
]
|
||||
},
|
||||
@@ -63,78 +63,94 @@ static void usage(void)
|
||||
OUTPUT(" compile <file1> [<file2> ...] Compile files without a project into an executable.");
|
||||
OUTPUT(" init <project name> Initialize a new project structure.");
|
||||
OUTPUT(" build [<target>] Build the target in the current project.");
|
||||
OUTPUT(" test Run the unit tests in the current project.");
|
||||
OUTPUT(" clean Clean all build files.");
|
||||
OUTPUT(" run [<target>] Run (and build if needed) the target in the current project.");
|
||||
OUTPUT(" dist [<target>] Clean and build a target for distribution.");
|
||||
OUTPUT(" directives [<target>] Generate documentation for the target.");
|
||||
OUTPUT(" directives [<target>] Generate documentation for the target.");
|
||||
OUTPUT(" bench [<target>] Benchmark a target.");
|
||||
OUTPUT(" clean-run [<target>] Clean, then run the target.");
|
||||
OUTPUT(" compile-run <file1> [<file2> ...] Compile files then immediately run the result.");
|
||||
OUTPUT(" compile-only <file1> [<file2> ...] Compile files but do not perform linking.");
|
||||
OUTPUT(" compile-test <file1> [<file2> ...] Compile files into an executable and run unit tests.");
|
||||
OUTPUT(" static-lib <file1> [<file2> ...] Compile files without a project into a static library.");
|
||||
OUTPUT(" dynamic-lib <file1> [<file2> ...] Compile files without a project into a dynamic library.");
|
||||
OUTPUT(" headers <file1> [<file2> ...] Analyse files and generate C headers for public methods.");
|
||||
OUTPUT("");
|
||||
OUTPUT("Options:");
|
||||
OUTPUT(" --tinybackend - Use the TinyBackend for compilation.");
|
||||
OUTPUT(" --stdlib <dir> - Use this directory as the C3 standard library path.");
|
||||
OUTPUT(" --nostdlib - Do not include the standard library.");
|
||||
OUTPUT(" --libdir <dir> - Add this directory to the C3 library search paths.");
|
||||
OUTPUT(" --lib <name> - Add this library to the compilation.");
|
||||
OUTPUT(" --path <dir> - Use this as the base directory for the current command.");
|
||||
OUTPUT(" --template <template> - Use a different template: \"lib\", \"static-lib\" or a path.");
|
||||
OUTPUT(" --about - Prints a short description of C3.");
|
||||
OUTPUT(" --symtab <value> - Sets the preferred symtab size.");
|
||||
OUTPUT(" -V --version - Print version information.");
|
||||
OUTPUT(" -E - Lex only.");
|
||||
OUTPUT(" -P - Only parse and output the AST as S-expressions.");
|
||||
OUTPUT(" -C - Only lex, parse and check.");
|
||||
OUTPUT(" -o <file> - Write output to <file>.");
|
||||
OUTPUT(" -O0 - Optimizations off.");
|
||||
OUTPUT(" -O1 - Simple optimizations only.");
|
||||
OUTPUT(" -O2 - Default optimization level.");
|
||||
OUTPUT(" -Os - Optimize for size.");
|
||||
OUTPUT(" -O3 - Aggressive optimization.");
|
||||
OUTPUT(" --build-dir <dir> - Override build output directory.");
|
||||
OUTPUT(" --obj-out <dir> - Override object file output directory.");
|
||||
OUTPUT(" --llvm-out <dir> - Override llvm output directory for '--emit-llvm'.");
|
||||
OUTPUT(" --emit-llvm - Emit LLVM IR as a .ll file per module.");
|
||||
OUTPUT(" --asm-out <dir> - Override asm output directory for '--emit-asm'.");
|
||||
OUTPUT(" --emit-asm - Emit asm as a .s file per module.");
|
||||
OUTPUT(" --target <target> - Compile for a particular architecture + OS target.");
|
||||
OUTPUT(" --threads <number> - Set the number of threads to use for compilation.");
|
||||
OUTPUT(" --safe - Set mode to 'safe', generating runtime traps on overflows and contract violations.");
|
||||
OUTPUT(" --fast - Set mode to 'fast', removes runtime traps.");
|
||||
OUTPUT(" --tinybackend - Use the TinyBackend for compilation.");
|
||||
OUTPUT(" --stdlib <dir> - Use this directory as the C3 standard library path.");
|
||||
OUTPUT(" --nostdlib - Do not include the standard library.");
|
||||
OUTPUT(" --nolibc - Do not implicitly link libc nor any associated files.");
|
||||
OUTPUT(" --libdir <dir> - Add this directory to the C3 library search paths.");
|
||||
OUTPUT(" --lib <name> - Add this library to the compilation.");
|
||||
OUTPUT(" --path <dir> - Use this as the base directory for the current command.");
|
||||
OUTPUT(" --template <template> - Select template for 'init': \"exe\", \"static-lib\", \"dynamic-lib\" or a path.");
|
||||
OUTPUT(" --about - Prints a short description of C3.");
|
||||
OUTPUT(" --symtab <value> - Sets the preferred symtab size.");
|
||||
OUTPUT(" -V --version - Print version information.");
|
||||
OUTPUT(" -E - Lex only.");
|
||||
OUTPUT(" -P - Only parse and output the AST as S-expressions.");
|
||||
OUTPUT(" -C - Only lex, parse and check.");
|
||||
OUTPUT(" - - Read code from standard in.");
|
||||
OUTPUT(" -o <file> - Write output to <file>.");
|
||||
OUTPUT(" -O0 - Optimizations off.");
|
||||
OUTPUT(" -O1 - Simple optimizations only.");
|
||||
OUTPUT(" -O2 - Default optimization level.");
|
||||
OUTPUT(" -O3 - Aggressive optimization.");
|
||||
OUTPUT(" -Os - Optimize for size.");
|
||||
OUTPUT(" -Oz - Optimize for tiny size.");
|
||||
OUTPUT(" -O0+ - No optimization, single module");
|
||||
OUTPUT(" -O1+ - Simple optimizations, single module.");
|
||||
OUTPUT(" -O2+ - Default optimization level, single module");
|
||||
OUTPUT(" -O3+ - Aggressive optimization, single module.");
|
||||
OUTPUT(" -Os+ - Optimize for size, single module.");
|
||||
OUTPUT(" -Oz+ - Optimize for tiny size, single module.");
|
||||
OUTPUT(" --build-dir <dir> - Override build output directory.");
|
||||
OUTPUT(" --obj-out <dir> - Override object file output directory.");
|
||||
OUTPUT(" --llvm-out <dir> - Override llvm output directory for '--emit-llvm'.");
|
||||
OUTPUT(" --emit-llvm - Emit LLVM IR as a .ll file per module.");
|
||||
OUTPUT(" --asm-out <dir> - Override asm output directory for '--emit-asm'.");
|
||||
OUTPUT(" --emit-asm - Emit asm as a .s file per module.");
|
||||
OUTPUT(" --target <target> - Compile for a particular architecture + OS target.");
|
||||
OUTPUT(" --threads <number> - Set the number of threads to use for compilation.");
|
||||
OUTPUT(" --safe - Set mode to 'safe', generating runtime traps on overflows and contract violations.");
|
||||
OUTPUT(" --fast - Set mode to 'fast', removes runtime traps.");
|
||||
OUTPUT("");
|
||||
OUTPUT(" -g - Emit full debug info.");
|
||||
OUTPUT(" -g0 - Emit no debug info.");
|
||||
OUTPUT(" -gline-tables-only - Only emit line tables for debugging.");
|
||||
OUTPUT(" -g - Emit full debug info.");
|
||||
OUTPUT(" -g0 - Emit no debug info.");
|
||||
OUTPUT(" -gline-tables-only - Only emit line tables for debugging.");
|
||||
OUTPUT("");
|
||||
OUTPUT("");
|
||||
OUTPUT(" -l <library> - Link with the library provided.");
|
||||
OUTPUT(" -L <library dir> - Append the directory to the linker search paths.");
|
||||
OUTPUT(" -z <argument> - Send the <argument> as a parameter to the linker.");
|
||||
OUTPUT(" --forcelinker - Force built in linker usage when doing non-cross linking.");
|
||||
OUTPUT(" --newoptimizer - Use new optimizer pipeline.");
|
||||
OUTPUT(" -l <library> - Link with the library provided.");
|
||||
OUTPUT(" -L <library dir> - Append the directory to the linker search paths.");
|
||||
OUTPUT(" -z <argument> - Send the <argument> as a parameter to the linker.");
|
||||
OUTPUT(" --forcelinker - Force built in linker usage when doing non-cross linking.");
|
||||
OUTPUT("");
|
||||
OUTPUT(" --reloc=<option> - Relocation model: none, pic, PIC, pie, PIE");
|
||||
OUTPUT(" --x86vec=<option> - Set max level of vector instructions: none, mmx, sse, avx, avx512.");
|
||||
OUTPUT(" --reloc=<option> - Relocation model: none, pic, PIC, pie, PIE");
|
||||
OUTPUT(" --x86vec=<option> - Set max level of vector instructions: none, mmx, sse, avx, avx512.");
|
||||
OUTPUT("");
|
||||
OUTPUT(" --debug-stats - Print debug statistics.");
|
||||
OUTPUT(" --debug-stats - Print debug statistics.");
|
||||
#ifndef NDEBUG
|
||||
OUTPUT(" --debug-log - Print debug logging to stdout.");
|
||||
OUTPUT(" --debug-log - Print debug logging to stdout.");
|
||||
#endif
|
||||
OUTPUT("");
|
||||
OUTPUT(" --list-attributes - List all attributes.");
|
||||
OUTPUT(" --list-builtins - List all builtins.");
|
||||
OUTPUT(" --list-keywords - List all keywords.");
|
||||
OUTPUT(" --list-operators - List all operators.");
|
||||
OUTPUT(" --list-precedence - List operator precedence order.");
|
||||
OUTPUT(" --list-targets - List all architectures the compiler supports.");
|
||||
OUTPUT(" --benchmarking - Run builtin benchmarks.");
|
||||
OUTPUT(" --testing - Run built-in tests.");
|
||||
OUTPUT("");
|
||||
OUTPUT(" --winsdk <dir> - Set the directory for Windows system library files for cross compilation.");
|
||||
OUTPUT(" --wincrt=<option> - Windows CRT linking: none, static, dynamic (default).");
|
||||
OUTPUT(" --list-attributes - List all attributes.");
|
||||
OUTPUT(" --list-builtins - List all builtins.");
|
||||
OUTPUT(" --list-keywords - List all keywords.");
|
||||
OUTPUT(" --list-operators - List all operators.");
|
||||
OUTPUT(" --list-precedence - List operator precedence order.");
|
||||
OUTPUT(" --list-project-properties - List all available keys used in project.json files.");
|
||||
OUTPUT(" --list-targets - List all architectures the compiler supports.");
|
||||
OUTPUT(" --list-type-properties - List all type properties.");
|
||||
OUTPUT("");
|
||||
OUTPUT(" --macossdk <dir> - Set the directory for the MacOS SDK for cross compilation.");
|
||||
OUTPUT(" --winsdk <dir> - Set the directory for Windows system library files for cross compilation.");
|
||||
OUTPUT(" --wincrt=<option> - Windows CRT linking: none, static, dynamic (default).");
|
||||
OUTPUT("");
|
||||
OUTPUT(" --macossdk <dir> - Set the directory for the MacOS SDK for cross compilation.");
|
||||
|
||||
}
|
||||
|
||||
@@ -230,6 +246,12 @@ static void parse_command(BuildOptions *options)
|
||||
options->command = COMMAND_UNIT_TEST;
|
||||
return;
|
||||
}
|
||||
if (arg_match("compile-test"))
|
||||
{
|
||||
options->command = COMMAND_COMPILE_TEST;
|
||||
options->testing = true;
|
||||
return;
|
||||
}
|
||||
if (arg_match("compile"))
|
||||
{
|
||||
options->command = COMMAND_COMPILE;
|
||||
@@ -261,6 +283,12 @@ static void parse_command(BuildOptions *options)
|
||||
parse_optional_target(options);
|
||||
return;
|
||||
}
|
||||
if (arg_match("test"))
|
||||
{
|
||||
options->command = COMMAND_TEST;
|
||||
options->testing = true;
|
||||
return;
|
||||
}
|
||||
if (arg_match("run"))
|
||||
{
|
||||
options->command = COMMAND_RUN;
|
||||
@@ -342,6 +370,9 @@ static void parse_option(BuildOptions *options)
|
||||
const char *argopt;
|
||||
switch (current_arg[1])
|
||||
{
|
||||
case '\0':
|
||||
options->read_stdin = true;
|
||||
return;
|
||||
case '?':
|
||||
if (match_shortopt("?"))
|
||||
{
|
||||
@@ -388,26 +419,50 @@ static void parse_option(BuildOptions *options)
|
||||
{
|
||||
FAIL_WITH_ERR("Multiple optimization levels were set.");
|
||||
}
|
||||
if (match_shortopt("O0"))
|
||||
if (match_shortopt("O0+"))
|
||||
{
|
||||
options->optimization_setting_override = OPT_SETTING_O0_PLUS;
|
||||
}
|
||||
else if (match_shortopt("O0"))
|
||||
{
|
||||
options->optimization_setting_override = OPT_SETTING_O0;
|
||||
}
|
||||
else if (match_shortopt("O1+"))
|
||||
{
|
||||
options->optimization_setting_override = OPT_SETTING_O1_PLUS;
|
||||
}
|
||||
else if (match_shortopt("O1"))
|
||||
{
|
||||
options->optimization_setting_override = OPT_SETTING_O1;
|
||||
}
|
||||
else if (match_shortopt("O2+"))
|
||||
{
|
||||
options->optimization_setting_override = OPT_SETTING_O2_PLUS;
|
||||
}
|
||||
else if (match_shortopt("O2"))
|
||||
{
|
||||
options->optimization_setting_override = OPT_SETTING_O2;
|
||||
}
|
||||
else if (match_shortopt("O3+"))
|
||||
{
|
||||
options->optimization_setting_override = OPT_SETTING_O3_PLUS;
|
||||
}
|
||||
else if (match_shortopt("O3"))
|
||||
{
|
||||
options->optimization_setting_override = OPT_SETTING_O3;
|
||||
}
|
||||
else if (match_shortopt("Os+"))
|
||||
{
|
||||
options->optimization_setting_override = OPT_SETTING_OSMALL_PLUS;
|
||||
}
|
||||
else if (match_shortopt("Os"))
|
||||
{
|
||||
options->optimization_setting_override = OPT_SETTING_OSMALL;
|
||||
}
|
||||
else if (match_shortopt("Oz+"))
|
||||
{
|
||||
options->optimization_setting_override = OPT_SETTING_OTINY_PLUS;
|
||||
}
|
||||
else if (match_shortopt("Oz"))
|
||||
{
|
||||
options->optimization_setting_override = OPT_SETTING_OTINY;
|
||||
@@ -465,21 +520,9 @@ static void parse_option(BuildOptions *options)
|
||||
options->symtab_size = next_highest_power_of_2(symtab);
|
||||
return;
|
||||
}
|
||||
if (match_longopt("newoptimizer"))
|
||||
{
|
||||
options->use_new_optimizer = true;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("forcelinker"))
|
||||
{
|
||||
if (llvm_version_major > 12)
|
||||
{
|
||||
options->force_linker = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Force linking ignored on LLVM 12 and earlier.\n");
|
||||
}
|
||||
options->force_linker = true;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("version"))
|
||||
@@ -489,7 +532,7 @@ static void parse_option(BuildOptions *options)
|
||||
}
|
||||
if ((argopt = match_argopt("x86vec")))
|
||||
{
|
||||
options->x86_vector_capability = (X86VectorCapability)parse_multi_option(argopt, 5, vector_capability);
|
||||
options->x86_vector_capability = (X86VectorCapability)parse_multi_option(argopt, 6, vector_capability);
|
||||
return;
|
||||
}
|
||||
if ((argopt = match_argopt("reloc")))
|
||||
@@ -532,6 +575,18 @@ static void parse_option(BuildOptions *options)
|
||||
options->command = COMMAND_PRINT_SYNTAX;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("list-type-properties"))
|
||||
{
|
||||
options->print_type_properties = true;
|
||||
options->command = COMMAND_PRINT_SYNTAX;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("list-project-properties"))
|
||||
{
|
||||
options->print_project_properties = true;
|
||||
options->command = COMMAND_PRINT_SYNTAX;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("list-operators"))
|
||||
{
|
||||
options->print_operators = true;
|
||||
@@ -598,6 +653,7 @@ static void parse_option(BuildOptions *options)
|
||||
{
|
||||
if (at_end() || next_is_opt()) error_exit("error: --stdlib needs a directory.");
|
||||
options->std_lib_dir = check_dir(next_arg());
|
||||
options->no_stdlib = false;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("nostdlib"))
|
||||
@@ -605,6 +661,11 @@ static void parse_option(BuildOptions *options)
|
||||
options->no_stdlib = true;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("nolibc"))
|
||||
{
|
||||
options->no_libc = true;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("panicfn"))
|
||||
{
|
||||
if (at_end() || next_is_opt()) error_exit("error: --panicfn needs a function name.");
|
||||
@@ -628,6 +689,18 @@ static void parse_option(BuildOptions *options)
|
||||
options->win.crt_linking = (WinCrtLinking)parse_multi_option(argopt, 3, wincrt_linking);
|
||||
return;
|
||||
}
|
||||
if (match_longopt("macos-sdk-version"))
|
||||
{
|
||||
if (at_end() || next_is_opt()) error_exit("error: --macos-sdk-version needs a version.");
|
||||
options->macos.sdk_version = next_arg();
|
||||
return;
|
||||
}
|
||||
if (match_longopt("macos-min-version"))
|
||||
{
|
||||
if (at_end() || next_is_opt()) error_exit("error: --macos-min-version needs a version.");
|
||||
options->macos.min_version = next_arg();
|
||||
return;
|
||||
}
|
||||
if (match_longopt("build-dir"))
|
||||
{
|
||||
if (at_end() || next_is_opt()) error_exit("error: --build-dir needs a directory.");
|
||||
@@ -677,6 +750,12 @@ static void parse_option(BuildOptions *options)
|
||||
options->test_mode = true;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("template"))
|
||||
{
|
||||
if (at_end() || next_is_opt()) error_exit("error: --template needs an argument.");
|
||||
options->template = next_arg();
|
||||
return;
|
||||
}
|
||||
if (match_longopt("path"))
|
||||
{
|
||||
if (at_end() || next_is_opt()) error_exit("error: --path needs a directory.");
|
||||
@@ -693,6 +772,16 @@ static void parse_option(BuildOptions *options)
|
||||
options->safe_mode = 0;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("benchmarking"))
|
||||
{
|
||||
options->benchmarking = true;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("testing"))
|
||||
{
|
||||
options->testing = true;
|
||||
return;
|
||||
}
|
||||
if (match_longopt("help"))
|
||||
{
|
||||
usage();
|
||||
@@ -725,7 +814,7 @@ BuildOptions parse_arguments(int argc, const char *argv[])
|
||||
.optimization_setting_override = OPT_SETTING_NOT_SET,
|
||||
.debug_info_override = DEBUG_INFO_NOT_SET,
|
||||
.safe_mode = -1,
|
||||
.build_threads = 16,
|
||||
.build_threads = cpus(),
|
||||
.command = COMMAND_MISSING,
|
||||
.reloc_model = RELOC_DEFAULT,
|
||||
.backend = BACKEND_LLVM,
|
||||
|
||||
@@ -23,6 +23,7 @@ typedef enum
|
||||
COMMAND_MISSING = 0,
|
||||
COMMAND_COMPILE,
|
||||
COMMAND_COMPILE_ONLY,
|
||||
COMMAND_COMPILE_TEST,
|
||||
COMMAND_GENERATE_HEADERS,
|
||||
COMMAND_INIT,
|
||||
COMMAND_BUILD,
|
||||
@@ -35,6 +36,7 @@ typedef enum
|
||||
COMMAND_DIST,
|
||||
COMMAND_DOCS,
|
||||
COMMAND_BENCH,
|
||||
COMMAND_TEST,
|
||||
COMMAND_UNIT_TEST,
|
||||
COMMAND_PRINT_SYNTAX,
|
||||
} CompilerCommand;
|
||||
@@ -89,11 +91,17 @@ typedef enum
|
||||
{
|
||||
OPT_SETTING_NOT_SET = -1,
|
||||
OPT_SETTING_O0 = 0,
|
||||
OPT_SETTING_O1 = 1,
|
||||
OPT_SETTING_O2 = 2,
|
||||
OPT_SETTING_O3 = 3,
|
||||
OPT_SETTING_OSMALL = 4,
|
||||
OPT_SETTING_OTINY = 5,
|
||||
OPT_SETTING_O0_PLUS,
|
||||
OPT_SETTING_O1,
|
||||
OPT_SETTING_O1_PLUS,
|
||||
OPT_SETTING_O2,
|
||||
OPT_SETTING_O2_PLUS,
|
||||
OPT_SETTING_O3,
|
||||
OPT_SETTING_O3_PLUS,
|
||||
OPT_SETTING_OSMALL,
|
||||
OPT_SETTING_OSMALL_PLUS,
|
||||
OPT_SETTING_OTINY,
|
||||
OPT_SETTING_OTINY_PLUS
|
||||
} OptimizationSetting;
|
||||
|
||||
typedef enum
|
||||
@@ -135,14 +143,16 @@ typedef enum
|
||||
X86VECTOR_SSE = 2,
|
||||
X86VECTOR_AVX = 3,
|
||||
X86VECTOR_AVX512 = 4,
|
||||
X86VECTOR_NATIVE = 5,
|
||||
} X86VectorCapability;
|
||||
|
||||
static const char *vector_capability[5] = {
|
||||
static const char *vector_capability[6] = {
|
||||
[X86VECTOR_NONE] = "none",
|
||||
[X86VECTOR_MMX] = "mmx",
|
||||
[X86VECTOR_SSE] = "sse",
|
||||
[X86VECTOR_AVX] = "avx",
|
||||
[X86VECTOR_AVX512] = "avx512",
|
||||
[X86VECTOR_NATIVE] = "native"
|
||||
};
|
||||
|
||||
typedef enum
|
||||
@@ -234,6 +244,8 @@ typedef struct BuildOptions_
|
||||
} win;
|
||||
struct {
|
||||
const char *sdk;
|
||||
const char *min_version;
|
||||
const char *sdk_version;
|
||||
} macos;
|
||||
int build_threads;
|
||||
const char** files;
|
||||
@@ -241,6 +253,7 @@ typedef struct BuildOptions_
|
||||
const char* project_name;
|
||||
const char* target_select;
|
||||
const char* path;
|
||||
const char *template;
|
||||
uint32_t symtab_size;
|
||||
unsigned version;
|
||||
CompilerBackend backend;
|
||||
@@ -256,8 +269,9 @@ typedef struct BuildOptions_
|
||||
bool emit_bitcode;
|
||||
bool test_mode;
|
||||
bool no_stdlib;
|
||||
bool no_libc;
|
||||
bool force_linker;
|
||||
bool use_new_optimizer;
|
||||
bool read_stdin;
|
||||
const char *panicfn;
|
||||
const char *cc;
|
||||
const char *build_dir;
|
||||
@@ -270,8 +284,12 @@ typedef struct BuildOptions_
|
||||
bool print_attributes;
|
||||
bool print_builtins;
|
||||
bool print_operators;
|
||||
bool print_type_properties;
|
||||
bool print_project_properties;
|
||||
bool print_precedence;
|
||||
bool print_build_settings;
|
||||
bool benchmarking;
|
||||
bool testing;
|
||||
} BuildOptions;
|
||||
|
||||
|
||||
@@ -323,6 +341,7 @@ typedef struct
|
||||
const char *llvm_file_dir;
|
||||
const char *asm_file_dir;
|
||||
bool run_after_compile;
|
||||
bool generate_test_runner;
|
||||
bool test_output;
|
||||
bool output_headers;
|
||||
bool output_ast;
|
||||
@@ -332,11 +351,16 @@ typedef struct
|
||||
bool emit_llvm;
|
||||
bool emit_asm;
|
||||
bool no_stdlib;
|
||||
bool no_libc;
|
||||
bool emit_object_files;
|
||||
bool force_linker;
|
||||
bool use_new_optimizer;
|
||||
bool benchmarking;
|
||||
bool testing;
|
||||
bool read_stdin;
|
||||
int build_threads;
|
||||
OptimizationLevel optimization_level;
|
||||
SizeOptimizationLevel size_optimization_level;
|
||||
bool single_module;
|
||||
DebugInfo debug_info;
|
||||
RelocModel reloc_model;
|
||||
ArchOsTarget arch_os_target;
|
||||
@@ -359,6 +383,8 @@ typedef struct
|
||||
struct
|
||||
{
|
||||
const char *sdk;
|
||||
const char *min_version;
|
||||
const char *sdk_version;
|
||||
} macos;
|
||||
struct
|
||||
{
|
||||
|
||||
@@ -69,6 +69,8 @@ bool command_is_projectless(CompilerCommand command)
|
||||
case COMMAND_COMPILE_RUN:
|
||||
case COMMAND_DYNAMIC_LIB:
|
||||
case COMMAND_STATIC_LIB:
|
||||
case COMMAND_COMPILE_TEST:
|
||||
case COMMAND_UNIT_TEST:
|
||||
return true;
|
||||
case COMMAND_MISSING:
|
||||
case COMMAND_GENERATE_HEADERS:
|
||||
@@ -80,8 +82,8 @@ bool command_is_projectless(CompilerCommand command)
|
||||
case COMMAND_DIST:
|
||||
case COMMAND_DOCS:
|
||||
case COMMAND_BENCH:
|
||||
case COMMAND_UNIT_TEST:
|
||||
case COMMAND_PRINT_SYNTAX:
|
||||
case COMMAND_TEST:
|
||||
return false;
|
||||
}
|
||||
UNREACHABLE
|
||||
@@ -90,6 +92,11 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
|
||||
{
|
||||
switch (options->command)
|
||||
{
|
||||
case COMMAND_COMPILE_TEST:
|
||||
case COMMAND_TEST:
|
||||
target->run_after_compile = true;
|
||||
target->type = TARGET_TYPE_TEST;
|
||||
break;
|
||||
case COMMAND_RUN:
|
||||
case COMMAND_COMPILE_RUN:
|
||||
case COMMAND_CLEAN_RUN:
|
||||
@@ -124,35 +131,54 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
|
||||
}
|
||||
|
||||
target->backend = options->backend;
|
||||
target->single_module = false;
|
||||
|
||||
// Copy optimization levels.
|
||||
switch (options->optimization_setting_override)
|
||||
{
|
||||
case OPT_SETTING_O0_PLUS:
|
||||
target->single_module = true;
|
||||
FALLTHROUGH;
|
||||
case OPT_SETTING_O0:
|
||||
target->optimization_level = OPTIMIZATION_NONE;
|
||||
target->size_optimization_level = SIZE_OPTIMIZATION_NONE;
|
||||
target->feature.safe_mode = true;
|
||||
break;
|
||||
case OPT_SETTING_O1_PLUS:
|
||||
target->single_module = true;
|
||||
FALLTHROUGH;
|
||||
case OPT_SETTING_O1:
|
||||
target->optimization_level = OPTIMIZATION_LESS;
|
||||
target->size_optimization_level = SIZE_OPTIMIZATION_NONE;
|
||||
target->feature.safe_mode = false;
|
||||
break;
|
||||
case OPT_SETTING_O2_PLUS:
|
||||
target->single_module = true;
|
||||
FALLTHROUGH;
|
||||
case OPT_SETTING_O2:
|
||||
target->optimization_level = OPTIMIZATION_DEFAULT;
|
||||
target->size_optimization_level = SIZE_OPTIMIZATION_NONE;
|
||||
target->feature.safe_mode = false;
|
||||
break;
|
||||
case OPT_SETTING_O3_PLUS:
|
||||
target->single_module = true;
|
||||
FALLTHROUGH;
|
||||
case OPT_SETTING_O3:
|
||||
target->optimization_level = OPTIMIZATION_AGGRESSIVE;
|
||||
target->size_optimization_level = SIZE_OPTIMIZATION_NONE;
|
||||
target->feature.safe_mode = false;
|
||||
break;
|
||||
case OPT_SETTING_OSMALL_PLUS:
|
||||
target->single_module = true;
|
||||
FALLTHROUGH;
|
||||
case OPT_SETTING_OSMALL:
|
||||
target->optimization_level = OPTIMIZATION_DEFAULT;
|
||||
target->size_optimization_level = SIZE_OPTIMIZATION_SMALL;
|
||||
target->feature.safe_mode = false;
|
||||
break;
|
||||
case OPT_SETTING_OTINY_PLUS:
|
||||
target->single_module = true;
|
||||
FALLTHROUGH;
|
||||
case OPT_SETTING_OTINY:
|
||||
target->optimization_level = OPTIMIZATION_DEFAULT;
|
||||
target->size_optimization_level = SIZE_OPTIMIZATION_TINY;
|
||||
@@ -191,14 +217,19 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
|
||||
{
|
||||
vec_add(target->linker_libs, options->linker_libs[i]);
|
||||
}
|
||||
target->no_stdlib = options->no_stdlib;
|
||||
if (options->no_stdlib) target->no_stdlib = true;
|
||||
if (options->no_libc) target->no_libc = true;
|
||||
target->emit_llvm = options->emit_llvm;
|
||||
target->build_threads = options->build_threads;
|
||||
target->emit_asm = options->emit_asm;
|
||||
target->force_linker = options->force_linker;
|
||||
target->use_new_optimizer = options->use_new_optimizer;
|
||||
target->panicfn = options->panicfn;
|
||||
target->benchmarking = options->benchmarking;
|
||||
target->testing = options->testing;
|
||||
if (options->macos.sdk) target->macos.sdk = options->macos.sdk;
|
||||
if (options->win.sdk) target->win.sdk = options->win.sdk;
|
||||
if (options->macos.min_version) target->macos.min_version = options->macos.min_version;
|
||||
if (options->macos.sdk_version) target->macos.sdk_version = options->macos.sdk_version;
|
||||
if (options->win.crt_linking != WIN_CRT_DEFAULT) target->win.crt_linking = options->win.crt_linking;
|
||||
if (options->x86_vector_capability != X86VECTOR_DEFAULT)
|
||||
{
|
||||
@@ -262,6 +293,7 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
|
||||
void init_default_build_target(BuildTarget *target, BuildOptions *options)
|
||||
{
|
||||
*target = (BuildTarget) {
|
||||
.read_stdin = options->read_stdin,
|
||||
.type = TARGET_TYPE_EXECUTABLE,
|
||||
.source_dirs = options->files,
|
||||
.name = options->output_name,
|
||||
@@ -281,7 +313,7 @@ void init_default_build_target(BuildTarget *target, BuildOptions *options)
|
||||
void init_build_target(BuildTarget *target, BuildOptions *options)
|
||||
{
|
||||
*target = (BuildTarget) { 0 };
|
||||
// Locate the project.c3p
|
||||
// Locate the project.json
|
||||
file_find_top_dir();
|
||||
// Parse it
|
||||
Project *project = project_load();
|
||||
|
||||
@@ -7,6 +7,75 @@
|
||||
#define MAX_SYMTAB_SIZE (1024 * 1024)
|
||||
|
||||
|
||||
const char *project_default_keys[] = {
|
||||
"authors",
|
||||
"cc",
|
||||
"cflags",
|
||||
"cpu",
|
||||
"c-sources",
|
||||
"debug-info",
|
||||
"langrev",
|
||||
"dependency-search-paths",
|
||||
"dependencies",
|
||||
"linked-libraries",
|
||||
"macossdk",
|
||||
"nolibc",
|
||||
"nostdlib",
|
||||
"panicfn",
|
||||
"reloc",
|
||||
"soft-float",
|
||||
"sources",
|
||||
"symtab",
|
||||
"target",
|
||||
"targets",
|
||||
"trap-on-wrap",
|
||||
"version",
|
||||
"warnings",
|
||||
"wincrt",
|
||||
"winsdk",
|
||||
"x86-stack-struct-return",
|
||||
"x86vec",
|
||||
"output",
|
||||
};
|
||||
|
||||
const int project_default_keys_count = sizeof(project_default_keys) / sizeof(char*);
|
||||
|
||||
const char* project_target_keys[] = {
|
||||
"output"
|
||||
"cc",
|
||||
"cflags-add",
|
||||
"cflags-override",
|
||||
"cpu",
|
||||
"c-sources-add",
|
||||
"c-sources-override",
|
||||
"debug-info",
|
||||
"langrev",
|
||||
"dependency-search-paths-add",
|
||||
"dependency-search-paths-override",
|
||||
"dependencies-add",
|
||||
"dependencies-override",
|
||||
"linked-libraries",
|
||||
"macossdk",
|
||||
"nolibc",
|
||||
"nostdlib",
|
||||
"panicfn",
|
||||
"reloc",
|
||||
"soft-float",
|
||||
"sources-add",
|
||||
"sources-override",
|
||||
"symtab",
|
||||
"target",
|
||||
"trap-on-wrap",
|
||||
"type",
|
||||
"version",
|
||||
"warnings",
|
||||
"wincrt",
|
||||
"winsdk",
|
||||
"x86-stack-struct-return",
|
||||
"x86vec",
|
||||
};
|
||||
|
||||
const int project_target_keys_count = sizeof(project_target_keys) / sizeof(char*);
|
||||
|
||||
const char *get_valid_string(JSONObject *table, const char *key, const char *category, bool mandatory)
|
||||
{
|
||||
@@ -100,66 +169,120 @@ static const char **get_valid_array(JSONObject *table, const char *key, const ch
|
||||
return values;
|
||||
}
|
||||
|
||||
static void load_into_build_target(JSONObject *json, const char *type, BuildTarget *target)
|
||||
static void check_json_keys(const char** valid_keys, size_t key_count, JSONObject *json, const char *type)
|
||||
{
|
||||
const char *cc = get_valid_string(json, "cc", type, false);
|
||||
const char *cflags = get_valid_string(json, "cflags", type, false);
|
||||
const char **csource_dirs = get_valid_array(json, "csources", type, false);
|
||||
const char *version = get_valid_string(json, "version", type, false);
|
||||
const char *langrev = get_valid_string(json, "langrev", type, false);
|
||||
const char **source_dirs = get_valid_array(json, "sources", type, target->source_dirs == NULL);
|
||||
const char **libraries = get_valid_array(json, "libs", type, false);
|
||||
const char **linker_libs = get_valid_array(json, "linker-libs", type, false);
|
||||
VECEACH(libraries, i)
|
||||
static bool failed_shown = false;
|
||||
bool failed = false;
|
||||
for (size_t i = 0; i < json->member_len; i++)
|
||||
{
|
||||
if (!str_is_valid_lowercase_name(libraries[i]))
|
||||
const char *key = json->keys[i];
|
||||
for (size_t j = 0; j < key_count; j++)
|
||||
{
|
||||
char *name = strdup(libraries[i]);
|
||||
str_ellide_in_place(name, 32);
|
||||
error_exit("Error reading %s: invalid library target '%s'.", PROJECT_JSON, name);
|
||||
if (strcmp(key, valid_keys[j]) == 0) goto OK;
|
||||
}
|
||||
eprintf("WARNING: Unknown parameter '%s' in '%s'.\n", key, type);
|
||||
failed = true;
|
||||
OK:;
|
||||
}
|
||||
if (failed && !failed_shown)
|
||||
{
|
||||
eprintf("You can use '--list-project-properties' to list all valid properties.\n");
|
||||
failed_shown = true;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void append_strings_to_strings(const char*** list_of_strings_ptr, const char **strings_to_append)
|
||||
{
|
||||
FOREACH_BEGIN(const char *string, strings_to_append)
|
||||
vec_add(*list_of_strings_ptr, string);
|
||||
FOREACH_END();
|
||||
}
|
||||
|
||||
static void target_append_strings(JSONObject *json, const char *type, const char ***list_ptr, const char *base, const char *override, const char *add, bool is_default)
|
||||
{
|
||||
const char **value = get_valid_array(json, is_default ? base : override, type, false);
|
||||
const char **add_value = is_default ? NULL : get_valid_array(json, add, type, false);
|
||||
if (value && add_value)
|
||||
{
|
||||
error_exit("'%s' is combining both '%s' and '%s', only one may be used.", type, override, add);
|
||||
}
|
||||
if (value) *list_ptr = value;
|
||||
if (add_value)
|
||||
{
|
||||
append_strings_to_strings(&add_value, *list_ptr);
|
||||
*list_ptr = add_value;
|
||||
}
|
||||
}
|
||||
static void load_into_build_target(JSONObject *json, const char *type, BuildTarget *target, bool is_default)
|
||||
{
|
||||
if (is_default)
|
||||
{
|
||||
check_json_keys(project_default_keys, sizeof(project_default_keys) / sizeof(char*), json, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
check_json_keys(project_target_keys, sizeof(project_target_keys) / sizeof(char*), json, type);
|
||||
}
|
||||
const char *cc = get_valid_string(json, "cc", type, false);
|
||||
if (cc) target->cc = cc;
|
||||
|
||||
// CFlags
|
||||
const char *cflags = get_valid_string(json, is_default ? "cflags" : "cflags-override" , type, false);
|
||||
const char *cflags_add = is_default ? NULL : get_valid_string(json, "cflags-add" , type, false);
|
||||
if (cflags && cflags_add)
|
||||
{
|
||||
error_exit("'%s' is combining both 'cflags-add' and 'cflags-override', only one may be used.", type);
|
||||
}
|
||||
if (cflags) target->cflags = cflags;
|
||||
if (cflags_add)
|
||||
{
|
||||
if (target->cflags)
|
||||
{
|
||||
target->cflags = str_printf("%s %s", target->cflags, cflags_add);
|
||||
}
|
||||
else
|
||||
{
|
||||
target->cflags = cflags_add;
|
||||
}
|
||||
}
|
||||
const char **libdirs = get_valid_array(json, "libdir", type, false);
|
||||
const char **linker_libdirs = get_valid_array(json, "linker-libdir", type, false);
|
||||
|
||||
// C source dirs.
|
||||
target_append_strings(json, type, &target->csource_dirs, "c-sources", "c-sources-override", "c-sources-add", is_default);
|
||||
|
||||
// Sources
|
||||
target_append_strings(json, type, &target->source_dirs, "sources", "sources-override", "sources-add", is_default);
|
||||
|
||||
// Linked-libraries - libraries to add at link time
|
||||
target_append_strings(json, type, &target->linker_libs, "linked-libraries", "linked-libraries-override", "linked-libraries-add", is_default);
|
||||
|
||||
// linker-search-paths libs dir - libraries to add at link time
|
||||
target_append_strings(json, type, &target->linker_libdirs, "linker-search-paths", "linker-search-paths-override", "linker-search-paths-add", is_default);
|
||||
|
||||
// dependency-search-paths - path to search for libraries
|
||||
target_append_strings(json, type, &target->libdirs, "dependency-search-paths", "dependency-search-paths-override", "dependency-search-paths-add", is_default);
|
||||
|
||||
// Dependencies
|
||||
target_append_strings(json, type, &target->libs, "dependencies", "dependencies-override", "dependencies-add", is_default);
|
||||
FOREACH_BEGIN(const char *name, target->libs)
|
||||
if (!str_is_valid_lowercase_name(name))
|
||||
{
|
||||
char *name_copy = strdup(name);
|
||||
str_ellide_in_place(name_copy, 32);
|
||||
error_exit("Error reading %s: invalid library target '%s'.", PROJECT_JSON, name_copy);
|
||||
}
|
||||
FOREACH_END();
|
||||
|
||||
// debug-info
|
||||
static const char *debug_infos[3] = {
|
||||
[DEBUG_INFO_FULL] = "full",
|
||||
[DEBUG_INFO_NONE] = "none",
|
||||
[DEBUG_INFO_LINE_TABLES] = "line-tables"
|
||||
};
|
||||
DebugInfo info = get_valid_string_setting(json, "debug-info", type, debug_infos, 0, 3, "one of 'full' 'line-table' or 'none'.");
|
||||
const char *arch_os_string = get_valid_string(json, "target", type, false);
|
||||
long symtab_size = get_valid_integer(json, "symtab", type, false);
|
||||
const char *cpu = get_valid_string(json, "cpu", type, false);
|
||||
int reloc = get_valid_string_setting(json, "reloc", type, reloc_models, 0, 5, "'none', 'pic', 'PIC', 'pie' or 'PIE'.");
|
||||
int wincrt = get_valid_string_setting(json, "wincrt", type, wincrt_linking, 0, 5, "'none', 'static' or 'dynamic'.");
|
||||
int x86vec = get_valid_string_setting(json, "x86vec", type, vector_capability, 0, 5, "none, mmx, sse, avx or avx512");
|
||||
const char *panicfn = get_valid_string(json, "panicfn", type, false);
|
||||
target->win.sdk = get_valid_string(json, "winsdk", type, false);
|
||||
target->macos.sdk = get_valid_string(json, "macossdk", type, false);
|
||||
target->panicfn = panicfn;
|
||||
if (cc) target->cc = cc;
|
||||
if (cflags) target->cflags = cflags;
|
||||
if (csource_dirs) target->csource_dirs = csource_dirs;
|
||||
if (version) target->version = version;
|
||||
if (langrev) target->langrev = langrev;
|
||||
if (source_dirs) target->source_dirs = source_dirs;
|
||||
if (libdirs) target->libdirs = libdirs;
|
||||
if (linker_libdirs) target->linker_libdirs = linker_libdirs;
|
||||
if (linker_libs) target->linker_libs = linker_libs;
|
||||
if (libraries) target->libs = libraries;
|
||||
if (info > -1) target->debug_info = info;
|
||||
if (cpu) target->cpu = cpu;
|
||||
if (wincrt > -1) target->win.crt_linking = (WinCrtLinking)wincrt;
|
||||
if (reloc > -1) target->reloc_model = (RelocModel)reloc;
|
||||
if (x86vec > -1) target->feature.x86_vector_capability = x86vec;
|
||||
|
||||
if (arch_os_string)
|
||||
{
|
||||
ArchOsTarget arch_os = arch_os_target_from_string(arch_os_string);
|
||||
if (arch_os == ARCH_OS_TARGET_DEFAULT) error_exit("Error reading %s: %s target was not valid.", PROJECT_JSON, type);
|
||||
target->arch_os_target = arch_os;
|
||||
}
|
||||
// Symtab
|
||||
long symtab_size = get_valid_integer(json, "symtab", type, false);
|
||||
if (symtab_size > 0)
|
||||
{
|
||||
if (symtab_size < 1024)
|
||||
@@ -173,11 +296,67 @@ static void load_into_build_target(JSONObject *json, const char *type, BuildTarg
|
||||
target->symtab_size = (uint32_t)symtab_size;
|
||||
}
|
||||
|
||||
// Target
|
||||
const char *arch_os_string = get_valid_string(json, "target", type, false);
|
||||
if (arch_os_string)
|
||||
{
|
||||
ArchOsTarget arch_os = arch_os_target_from_string(arch_os_string);
|
||||
if (arch_os == ARCH_OS_TARGET_DEFAULT) error_exit("Error reading %s: %s target was not valid.", PROJECT_JSON, type);
|
||||
target->arch_os_target = arch_os;
|
||||
}
|
||||
|
||||
// Reloc
|
||||
int reloc = get_valid_string_setting(json, "reloc", type, reloc_models, 0, 5, "'none', 'pic', 'PIC', 'pie' or 'PIE'.");
|
||||
if (reloc > -1) target->reloc_model = (RelocModel)reloc;
|
||||
|
||||
// Cpu
|
||||
const char *cpu = get_valid_string(json, "cpu", type, false);
|
||||
if (cpu) target->cpu = cpu;
|
||||
|
||||
// WinCRT
|
||||
int wincrt = get_valid_string_setting(json, "wincrt", type, wincrt_linking, 0, 5, "'none', 'static' or 'dynamic'.");
|
||||
if (wincrt > -1) target->win.crt_linking = (WinCrtLinking)wincrt;
|
||||
|
||||
// x86vec
|
||||
int x86vec = get_valid_string_setting(json, "x86vec", type, vector_capability, 0, 6, "none, native, mmx, sse, avx or avx512");
|
||||
if (x86vec > -1) target->feature.x86_vector_capability = x86vec;
|
||||
|
||||
// winsdk
|
||||
target->win.sdk = get_valid_string(json, "winsdk", type, false);
|
||||
|
||||
// macossdk
|
||||
target->macos.sdk = get_valid_string(json, "macossdk", type, false);
|
||||
|
||||
// macos-min-version
|
||||
target->macos.min_version = get_valid_string(json, "macos-min-version", type, false);
|
||||
|
||||
// macos-sdk-version
|
||||
target->macos.sdk_version = get_valid_string(json, "macos-sdk-version", type, false);
|
||||
|
||||
// version
|
||||
const char *version = get_valid_string(json, "version", type, false);
|
||||
if (version) target->version = version;
|
||||
|
||||
// langrev
|
||||
const char *langrev = get_valid_string(json, "langrev", type, false);
|
||||
if (langrev) target->langrev = langrev;
|
||||
|
||||
// panicfn
|
||||
const char *panicfn = get_valid_string(json, "panicfn", type, false);
|
||||
target->panicfn = panicfn;
|
||||
|
||||
// nolibc
|
||||
target->no_libc = get_valid_bool(json, "nolibc", type, target->no_libc);
|
||||
|
||||
// nostdlib
|
||||
target->no_stdlib = get_valid_bool(json, "nostdlib", type, target->no_stdlib);
|
||||
|
||||
// Trap on wrap
|
||||
target->feature.trap_on_wrap = get_valid_bool(json, "trap-on-wrap", type, target->feature.trap_on_wrap);
|
||||
|
||||
// Use the fact that they correspond to 0, 1, -1
|
||||
target->feature.x86_struct_return = get_valid_bool(json, "x86-stack-struct-return", type, target->feature.x86_struct_return);
|
||||
target->feature.soft_float = get_valid_bool(json, "soft-float", type, target->feature.soft_float);
|
||||
target->no_stdlib = get_valid_bool(json, "nostdlib", type, false);
|
||||
|
||||
}
|
||||
static void project_add_target(Project *project, BuildTarget *default_target, JSONObject *json, const char *name, const char *type, TargetType target_type)
|
||||
@@ -198,7 +377,7 @@ static void project_add_target(Project *project, BuildTarget *default_target, J
|
||||
}
|
||||
}
|
||||
type = str_printf("%s %s", type, target->name);
|
||||
load_into_build_target(json, type, target);
|
||||
load_into_build_target(json, type, target, false);
|
||||
}
|
||||
|
||||
static void project_add_targets(Project *project, JSONObject *project_data)
|
||||
@@ -233,7 +412,7 @@ static void project_add_targets(Project *project, JSONObject *project_data)
|
||||
.feature.safe_mode = true,
|
||||
.win.crt_linking = WIN_CRT_DEFAULT,
|
||||
};
|
||||
load_into_build_target(project_data, "default target", &default_target);
|
||||
load_into_build_target(project_data, "default target", &default_target, true);
|
||||
JSONObject *targets_json = json_obj_get(project_data, "targets");
|
||||
if (!targets_json)
|
||||
{
|
||||
|
||||
@@ -11,14 +11,16 @@
|
||||
#include "build_options.h"
|
||||
#include "../utils/lib.h"
|
||||
|
||||
const char* JSON =
|
||||
const char* JSON_EXE =
|
||||
"{\n"
|
||||
" // language version of C3\n"
|
||||
" \"langrev\": \"1\",\n"
|
||||
" // warnings used for all targets\n"
|
||||
" \"warnings\": [ \"no-unused\" ],\n"
|
||||
" // directories where C3 library files may be found\n"
|
||||
" \"dependency-search-paths\": [ \"lib\" ],\n"
|
||||
" // libraries to use for all targets\n"
|
||||
" \"libs\": [ ],\n"
|
||||
" \"dependencies\": [ ],\n"
|
||||
" // authors, optionally with email\n"
|
||||
" \"authors\": [ \"John Doe <john.doe@example.com>\" ],\n"
|
||||
" // Version using semantic versioning\n"
|
||||
@@ -61,14 +63,158 @@ const char* JSON =
|
||||
" \"cc\": \"cc\",\n"
|
||||
" // c sources if the project also compiles c sources\n"
|
||||
" // relative to the project file\n"
|
||||
" \"csources\": [\n"
|
||||
" \"c-sources\": [\n"
|
||||
" \"csource/**\"\n"
|
||||
" ]\n"
|
||||
" */\n"
|
||||
"}";
|
||||
|
||||
const char* JSON_STATIC =
|
||||
"{\n"
|
||||
" // language version of C3\n"
|
||||
" \"langrev\": \"1\",\n"
|
||||
" // warnings used for all targets\n"
|
||||
" \"warnings\": [ \"no-unused\" ],\n"
|
||||
" // directories where C3 library files may be found\n"
|
||||
" \"dependency-search-paths\": [ \"lib\" ],\n"
|
||||
" // libraries to use for all targets\n"
|
||||
" \"dependencies\": [ ],\n"
|
||||
" // authors, optionally with email\n"
|
||||
" \"authors\": [ \"John Doe <john.doe@example.com>\" ],\n"
|
||||
" // Version using semantic versioning\n"
|
||||
" \"version\": \"0.1.0\",\n"
|
||||
" // sources compiled for all targets\n"
|
||||
" \"sources\": [ \"src/**\" ],\n"
|
||||
" // Targets\n"
|
||||
" \"targets\": {\n"
|
||||
" \"%s\": {\n"
|
||||
" // executable or library\n"
|
||||
" \"type\": \"static-lib\"\n"
|
||||
" // additional libraries, sources\n"
|
||||
" // and overrides of global settings here\n"
|
||||
" },\n"
|
||||
" }\n"
|
||||
" /*\n"
|
||||
" // Debug information, may be 'none', 'full' and 'line-tables'\n"
|
||||
" \"debug-info\": \"full\",\n"
|
||||
" // Architecture and OS target:\n"
|
||||
" \"target\": \"windows-x64\",\n"
|
||||
" // The size of the symtab, which limits the amount\n"
|
||||
" // of symbols that can be used. Should usually not\n"
|
||||
" // be changed.\n"
|
||||
" \"symtab\": 4194304,\n"
|
||||
" // \"none\", \"pic\", \"PIC\", \"pie\", \"PIE\"\n"
|
||||
" \"reloc\": \"none\",\n"
|
||||
" // Trap on signed and unsigned integer wrapping\n"
|
||||
" // for testing\n"
|
||||
" \"trap-on-wrap\": false,\n"
|
||||
" // Use / don't use soft float, value is otherwise target default\n"
|
||||
" \"soft-float\": false,\n"
|
||||
" // Vector settings on x86: none/mmx/sse/avx/avx512\n"
|
||||
" \"x86vec\": \"sse\",\n"
|
||||
" // CPU name, used for optimizations in the LLVM backend\n"
|
||||
" \"cpu\": \"generic\",\n"
|
||||
" // Output location, relative to project file\n"
|
||||
" \"output\": \"../build\",\n"
|
||||
" // C compiler if the project also compiles c sources\n"
|
||||
" // defaults to 'cc'\n"
|
||||
" \"cc\": \"cc\",\n"
|
||||
" // c sources if the project also compiles c sources\n"
|
||||
" // relative to the project file\n"
|
||||
" \"c-sources\": [\n"
|
||||
" \"csource/**\"\n"
|
||||
" ]\n"
|
||||
" */\n"
|
||||
"}";
|
||||
|
||||
const char* JSON_DYNAMIC =
|
||||
"{\n"
|
||||
" // language version of C3\n"
|
||||
" \"langrev\": \"1\",\n"
|
||||
" // warnings used for all targets\n"
|
||||
" \"warnings\": [ \"no-unused\" ],\n"
|
||||
" // directories where C3 library files may be found\n"
|
||||
" \"dependency-search-paths\": [ \"lib\" ],\n"
|
||||
" // libraries to use for all targets\n"
|
||||
" \"dependencies\": [ ],\n"
|
||||
" // authors, optionally with email\n"
|
||||
" \"authors\": [ \"John Doe <john.doe@example.com>\" ],\n"
|
||||
" // Version using semantic versioning\n"
|
||||
" \"version\": \"0.1.0\",\n"
|
||||
" // sources compiled for all targets\n"
|
||||
" \"sources\": [ \"src/**\" ],\n"
|
||||
" // Targets\n"
|
||||
" \"targets\": {\n"
|
||||
" \"%s\": {\n"
|
||||
" // executable or library\n"
|
||||
" \"type\": \"dynamic-lib\"\n"
|
||||
" // additional libraries, sources\n"
|
||||
" // and overrides of global settings here\n"
|
||||
" },\n"
|
||||
" }\n"
|
||||
" /*\n"
|
||||
" // Debug information, may be 'none', 'full' and 'line-tables'\n"
|
||||
" \"debug-info\": \"full\",\n"
|
||||
" // Architecture and OS target:\n"
|
||||
" \"target\": \"windows-x64\",\n"
|
||||
" // The size of the symtab, which limits the amount\n"
|
||||
" // of symbols that can be used. Should usually not\n"
|
||||
" // be changed.\n"
|
||||
" \"symtab\": 4194304,\n"
|
||||
" // \"none\", \"pic\", \"PIC\", \"pie\", \"PIE\"\n"
|
||||
" \"reloc\": \"none\",\n"
|
||||
" // Trap on signed and unsigned integer wrapping\n"
|
||||
" // for testing\n"
|
||||
" \"trap-on-wrap\": false,\n"
|
||||
" // Use / don't use soft float, value is otherwise target default\n"
|
||||
" \"soft-float\": false,\n"
|
||||
" // Vector settings on x86: none/mmx/sse/avx/avx512\n"
|
||||
" \"x86vec\": \"sse\",\n"
|
||||
" // CPU name, used for optimizations in the LLVM backend\n"
|
||||
" \"cpu\": \"generic\",\n"
|
||||
" // Output location, relative to project file\n"
|
||||
" \"output\": \"../build\",\n"
|
||||
" // C compiler if the project also compiles c sources\n"
|
||||
" // defaults to 'cc'\n"
|
||||
" \"cc\": \"cc\",\n"
|
||||
" // c sources if the project also compiles c sources\n"
|
||||
" // relative to the project file\n"
|
||||
" \"c-sources\": [\n"
|
||||
" \"csource/**\"\n"
|
||||
" ]\n"
|
||||
" */\n"
|
||||
"}";
|
||||
|
||||
const char* MAIN_TEMPLATE =
|
||||
"module %s;\n"
|
||||
"import std::io;\n"
|
||||
"\n"
|
||||
"fn int main(char[][] args)\n"
|
||||
"{\n"
|
||||
"\tio::println(\"Hello, World!\");\n"
|
||||
"\treturn 0;\n"
|
||||
"}\n";
|
||||
|
||||
void create_project(BuildOptions *build_options)
|
||||
{
|
||||
const char *template;
|
||||
if (!build_options->template || strcmp(build_options->template, "exe") == 0)
|
||||
{
|
||||
template = JSON_EXE;
|
||||
}
|
||||
else if (strcmp(build_options->template, "static-lib") == 0)
|
||||
{
|
||||
template = JSON_STATIC;
|
||||
}
|
||||
else if (strcmp(build_options->template, "dynamic-lib") == 0)
|
||||
{
|
||||
template = JSON_DYNAMIC;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t len;
|
||||
template = file_read_all(build_options->template, &len);
|
||||
}
|
||||
for (int i = 0; ; i++)
|
||||
{
|
||||
char c = build_options->project_name[i];
|
||||
@@ -98,50 +244,32 @@ void create_project(BuildOptions *build_options)
|
||||
|
||||
if (!file_touch("README.md")) goto ERROR;
|
||||
|
||||
FILE *file = fopen("project.c3p", "a");
|
||||
if (!file) goto ERROR;
|
||||
(void) fprintf(file, JSON, build_options->project_name);
|
||||
if (fclose(file)) goto ERROR;
|
||||
|
||||
if (!dir_make("lib")) goto ERROR;
|
||||
FILE *file = fopen("project.json", "a");
|
||||
if (!file) goto ERROR;
|
||||
(void) fprintf(file, template, build_options->project_name);
|
||||
if (fclose(file)) goto ERROR;
|
||||
|
||||
if (!dir_make("build")) goto ERROR;
|
||||
|
||||
if (!dir_make("docs")) goto ERROR;
|
||||
|
||||
if (!dir_make("lib")) goto ERROR;
|
||||
|
||||
if (!dir_make("resources")) goto ERROR;
|
||||
|
||||
if (!dir_make("test")) goto ERROR;
|
||||
if (!dir_make("src")) goto ERROR;
|
||||
|
||||
if (!dir_change("test")) goto ERROR;
|
||||
if (!dir_change("src")) goto ERROR;
|
||||
|
||||
if (!dir_make(build_options->project_name)) goto ERROR;
|
||||
file = fopen("main.c3", "w");
|
||||
if (!file) goto ERROR;
|
||||
(void) fprintf(file, MAIN_TEMPLATE, build_options->project_name);
|
||||
if (fclose(file)) goto ERROR;
|
||||
|
||||
if (!dir_change("..")) goto ERROR;
|
||||
|
||||
if (!dir_make("directives")) goto ERROR;
|
||||
|
||||
if (!dir_change("directives")) goto ERROR;
|
||||
|
||||
if (!file_touch("about.md")) goto ERROR;
|
||||
|
||||
if (!dir_make("src")) goto ERROR;
|
||||
|
||||
if (!dir_change("src")) goto ERROR;
|
||||
|
||||
if (!file_touch("index.html")) goto ERROR;
|
||||
|
||||
if (!dir_change("../..")) goto ERROR;
|
||||
|
||||
if (!dir_make("src")) goto ERROR;
|
||||
|
||||
if (!dir_change("src")) goto ERROR;
|
||||
|
||||
if (!dir_make(build_options->project_name)) goto ERROR;
|
||||
|
||||
if (!dir_change(build_options->project_name)) goto ERROR;
|
||||
|
||||
if (!file_touch("main.c3")) goto ERROR;
|
||||
|
||||
if (!dir_change("../..")) goto ERROR;
|
||||
if (!dir_make("test")) goto ERROR;
|
||||
|
||||
(void) printf("Project '%s' created.\n", build_options->project_name);
|
||||
exit_compiler(COMPILER_SUCCESS_EXIT);
|
||||
|
||||
@@ -35,38 +35,6 @@ typedef enum
|
||||
X86_XMM13,
|
||||
X86_XMM14,
|
||||
X86_XMM15,
|
||||
X86_YMM0,
|
||||
X86_YMM1,
|
||||
X86_YMM2,
|
||||
X86_YMM3,
|
||||
X86_YMM4,
|
||||
X86_YMM5,
|
||||
X86_YMM6,
|
||||
X86_YMM7,
|
||||
X86_YMM8,
|
||||
X86_YMM9,
|
||||
X86_YMM10,
|
||||
X86_YMM11,
|
||||
X86_YMM12,
|
||||
X86_YMM13,
|
||||
X86_YMM14,
|
||||
X86_YMM15,
|
||||
X86_ZMM0,
|
||||
X86_ZMM1,
|
||||
X86_ZMM2,
|
||||
X86_ZMM3,
|
||||
X86_ZMM4,
|
||||
X86_ZMM5,
|
||||
X86_ZMM6,
|
||||
X86_ZMM7,
|
||||
X86_ZMM8,
|
||||
X86_ZMM9,
|
||||
X86_ZMM10,
|
||||
X86_ZMM11,
|
||||
X86_ZMM12,
|
||||
X86_ZMM13,
|
||||
X86_ZMM14,
|
||||
X86_ZMM15,
|
||||
X86_K0,
|
||||
X86_K1,
|
||||
X86_K2,
|
||||
@@ -136,38 +104,6 @@ static const char *X86ClobberNames[] = {
|
||||
[X86_XMM13] = "xmm13",
|
||||
[X86_XMM14] = "xmm14",
|
||||
[X86_XMM15] = "xmm15",
|
||||
[X86_YMM0] = "ymm0",
|
||||
[X86_YMM1] = "ymm1",
|
||||
[X86_YMM2] = "ymm2",
|
||||
[X86_YMM3] = "ymm3",
|
||||
[X86_YMM4] = "ymm4",
|
||||
[X86_YMM5] = "ymm5",
|
||||
[X86_YMM6] = "ymm6",
|
||||
[X86_YMM7] = "ymm7",
|
||||
[X86_YMM8] = "ymm8",
|
||||
[X86_YMM9] = "ymm9",
|
||||
[X86_YMM10] = "ymm10",
|
||||
[X86_YMM11] = "ymm11",
|
||||
[X86_YMM12] = "ymm12",
|
||||
[X86_YMM13] = "ymm13",
|
||||
[X86_YMM14] = "ymm14",
|
||||
[X86_YMM15] = "ymm15",
|
||||
[X86_ZMM0] = "zmm0",
|
||||
[X86_ZMM1] = "zmm1",
|
||||
[X86_ZMM2] = "zmm2",
|
||||
[X86_ZMM3] = "zmm3",
|
||||
[X86_ZMM4] = "zmm4",
|
||||
[X86_ZMM5] = "zmm5",
|
||||
[X86_ZMM6] = "zmm6",
|
||||
[X86_ZMM7] = "zmm7",
|
||||
[X86_ZMM8] = "zmm8",
|
||||
[X86_ZMM9] = "zmm9",
|
||||
[X86_ZMM10] = "zmm10",
|
||||
[X86_ZMM11] = "zmm11",
|
||||
[X86_ZMM12] = "zmm12",
|
||||
[X86_ZMM13] = "zmm13",
|
||||
[X86_ZMM14] = "zmm14",
|
||||
[X86_ZMM15] = "zmm15",
|
||||
[X86_K0] = "k0",
|
||||
[X86_K1] = "k1",
|
||||
[X86_K2] = "k2",
|
||||
@@ -224,3 +160,5 @@ static const char *x86_xmm_regs[] = { "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4
|
||||
static const char *x86_ymm_regs[] = { "$ymm0", "$ymm1", "$ymm2", "$ymm3", "$ymm4", "$ymm5", "$ymm6", "$ymm7",
|
||||
"$ymm8", "$ymm9", "$ymm10", "$ymm11", "$ymm12", "$ymm13", "$ymm14", "$ymm15" };
|
||||
|
||||
static const char *x86_zmm_regs[] = { "$zmm0", "$zmm1", "$zmm2", "$zmm3", "$zmm4", "$zmm5", "$zmm6", "$zmm7",
|
||||
"$zmm8", "$zmm9", "$zmm10", "$zmm11", "$zmm12", "$zmm13", "$zmm14", "$zmm15" };
|
||||
|
||||
@@ -58,9 +58,19 @@ INLINE AsmArgBits parse_bits(const char **desc)
|
||||
}
|
||||
if (memcmp("128", *desc, 3) == 0)
|
||||
{
|
||||
*desc += 2;
|
||||
*desc += 3;
|
||||
return ARG_BITS_128;
|
||||
}
|
||||
if (memcmp("256", *desc, 3) == 0)
|
||||
{
|
||||
*desc += 3;
|
||||
return ARG_BITS_256;
|
||||
}
|
||||
if (memcmp("512", *desc, 3) == 0)
|
||||
{
|
||||
*desc += 3;
|
||||
return ARG_BITS_512;
|
||||
}
|
||||
error_exit("Invalid bits: %s.", *desc);
|
||||
}
|
||||
|
||||
@@ -95,7 +105,10 @@ INLINE AsmArgType decode_arg_type(const char **desc)
|
||||
*desc += 2;
|
||||
goto NEXT;
|
||||
}
|
||||
error_exit("Unexpected string %s", &desc[-1]);
|
||||
error_exit("Unexpected string %s", &(*desc)[-1]);
|
||||
case 'v':
|
||||
arg_type.vec_bits |= parse_bits(desc);
|
||||
goto NEXT;
|
||||
case 'i':
|
||||
if (memcmp("mm", *desc, 2) == 0)
|
||||
{
|
||||
@@ -119,7 +132,7 @@ INLINE AsmArgType decode_arg_type(const char **desc)
|
||||
goto NEXT;
|
||||
}
|
||||
default:
|
||||
error_exit("Unexpected string '%s'.", &desc[-1]);
|
||||
error_exit("Unexpected string '%s'.", &(*desc)[-1]);
|
||||
}
|
||||
NEXT:
|
||||
switch (**desc)
|
||||
@@ -236,22 +249,22 @@ static void init_asm_aarch64(void)
|
||||
reg_instr("strh", "r32/r64, w:mem");
|
||||
reg_instr("stp", "r32/r64, r32/r64, w:mem");
|
||||
reg_instr("mov", "w:r32/r64, mem");
|
||||
reg_register_list(aarch64_quad_regs, 32, ASM_REG_INT, 64, AARCH64_R0);
|
||||
reg_register_list(aarch64_long_regs, 32, ASM_REG_INT, 32, AARCH64_R0);
|
||||
reg_register_list(aarch64_f128_regs, 32, ASM_REG_FLOAT, 128, AARCH64_Q0);
|
||||
reg_register_list(aarch64_double_regs, 32, ASM_REG_FLOAT, 64, AARCH64_Q0);
|
||||
reg_register_list(aarch64_float_regs, 32, ASM_REG_FLOAT, 32, AARCH64_Q0);
|
||||
reg_register_list(aarch64_f16_regs, 32, ASM_REG_FLOAT, 16, AARCH64_Q0);
|
||||
reg_register_list(aarch64_f8_regs, 32, ASM_REG_FLOAT, 8, AARCH64_Q0);
|
||||
reg_register_list(aarch64_v8b_regs, 32, ASM_REG_IVEC, 64, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v16b_regs, 32, ASM_REG_IVEC, 128,AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v4h_regs, 32, ASM_REG_IVEC, 64, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v8h_regs, 32, ASM_REG_IVEC, 128, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v2s_regs, 32, ASM_REG_IVEC, 64, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v4s_regs, 32, ASM_REG_IVEC, 128, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v1d_regs, 32, ASM_REG_IVEC, 64, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v2d_regs, 32, ASM_REG_IVEC, 128, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register("$sp", ASM_REG_INT, 64, AARCH64_R31);
|
||||
reg_register_list(aarch64_quad_regs, 32, ASM_REG_INT, ARG_BITS_64, AARCH64_R0);
|
||||
reg_register_list(aarch64_long_regs, 32, ASM_REG_INT, ARG_BITS_32, AARCH64_R0);
|
||||
reg_register_list(aarch64_f128_regs, 32, ASM_REG_FLOAT, ARG_BITS_128, AARCH64_Q0);
|
||||
reg_register_list(aarch64_double_regs, 32, ASM_REG_FLOAT, ARG_BITS_64, AARCH64_Q0);
|
||||
reg_register_list(aarch64_float_regs, 32, ASM_REG_FLOAT, ARG_BITS_32, AARCH64_Q0);
|
||||
reg_register_list(aarch64_f16_regs, 32, ASM_REG_FLOAT, ARG_BITS_16, AARCH64_Q0);
|
||||
reg_register_list(aarch64_f8_regs, 32, ASM_REG_FLOAT, ARG_BITS_8, AARCH64_Q0);
|
||||
reg_register_list(aarch64_v8b_regs, 32, ASM_REG_IVEC, ARG_BITS_64, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v16b_regs, 32, ASM_REG_IVEC, ARG_BITS_128, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v4h_regs, 32, ASM_REG_IVEC, ARG_BITS_64, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v8h_regs, 32, ASM_REG_IVEC, ARG_BITS_128, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v2s_regs, 32, ASM_REG_IVEC, ARG_BITS_64, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v4s_regs, 32, ASM_REG_IVEC, ARG_BITS_128, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v1d_regs, 32, ASM_REG_IVEC, ARG_BITS_64, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register_list(aarch64_v2d_regs, 32, ASM_REG_IVEC, ARG_BITS_128, AARCH64_FIRST_RV_CLOBBER);
|
||||
reg_register("$sp", ASM_REG_INT, ARG_BITS_64, AARCH64_R31);
|
||||
}
|
||||
|
||||
static void init_asm_wasm(void)
|
||||
@@ -294,11 +307,24 @@ static void init_asm_x86(void)
|
||||
reg_instr_clob("adcw", cc_flag_mask, "rw:r16/mem, r16/mem/imm16/immi8");
|
||||
reg_instr_clob("adcl", cc_flag_mask, "rw:r32/mem, r32/mem/imm32/immi8");
|
||||
reg_instr_clob("adcq", cc_flag_mask, "rw:r64/mem, r64/mem/immi32/immi8");
|
||||
|
||||
reg_instr_clob("adcxl", cc_flag_mask, "r32, rw:r32/mem");
|
||||
reg_instr_clob("adcxq", cc_flag_mask, "r64, rw:r64/mem");
|
||||
|
||||
reg_instr_clob("addb", cc_flag_mask, "rw:r8/mem, r8/mem/imm8");
|
||||
reg_instr_clob("addw", cc_flag_mask, "rw:r16/mem, r16/mem/imm16/immi8");
|
||||
reg_instr_clob("addl", cc_flag_mask, "rw:r32/mem, r32/mem/imm32/immi8");
|
||||
reg_instr_clob("addq", cc_flag_mask, "rw:r64/mem, r64/mem/immi32/immi8");
|
||||
|
||||
reg_instr("addpd", "rw:v128, v128/mem");
|
||||
reg_instr("addps", "rw:v128, v128/mem");
|
||||
reg_instr("addsd", "rw:v128, v128/mem");
|
||||
reg_instr("addss", "rw:v128, v128/mem");
|
||||
reg_instr("vaddpd", "w:v128/v256/v512, v128/v256/v512, v128/v256/v512/mem");
|
||||
reg_instr("vaddps", "w:v128/v256/v512, v128/v256/v512, v128/v256/v512/mem");
|
||||
reg_instr("vaddsd", "w:v128, v128, v128/mem");
|
||||
reg_instr("vaddss", "w:v128, v128, v128/mem");
|
||||
|
||||
reg_instr_clob("cbtw", rax_mask, NULL);
|
||||
reg_instr_clob("cwtl", rax_mask, NULL);
|
||||
reg_instr_clob("cltq", rax_mask, NULL);
|
||||
@@ -354,6 +380,7 @@ static void init_asm_x86(void)
|
||||
reg_instr_clob("subw", rax_cc_mask, "rw:r16/mem, r16/mem/imm16");
|
||||
reg_instr_clob("subl", rax_cc_mask, "rw:r32/mem, r32/mem/imm32");
|
||||
reg_instr_clob("subq", rax_cc_mask, "rw:r64/mem, r64/mem/immi32/imm64");
|
||||
reg_instr("cpuid", NULL);
|
||||
reg_instr("hlt", NULL);
|
||||
reg_instr("in", "w:r8/r16/r32, r16/imm8"); // Actually ensure reg_al_ax and dx
|
||||
reg_instr_clob("incb", cc_flag_mask, "rw:r8/mem");
|
||||
@@ -374,27 +401,30 @@ static void init_asm_x86(void)
|
||||
reg_instr("iretl", NULL);
|
||||
reg_instr("iretw", NULL);
|
||||
reg_instr("iretq", NULL);
|
||||
reg_instr("rdtsc", NULL);
|
||||
reg_instr("rdtscp", NULL);
|
||||
reg_instr("ret", NULL);
|
||||
|
||||
asm_target.clobber_name_list = X86ClobberNames;
|
||||
asm_target.extra_clobbers = "~{flags},~{dirflag},~{fspr}";
|
||||
if (platform_target.arch == ARCH_TYPE_X86)
|
||||
{
|
||||
reg_register_list(x86_long_regs, 8, ASM_REG_INT, 32, X86_RAX);
|
||||
reg_register_list(x86_word_regs, 8, ASM_REG_INT, 16, X86_RAX);
|
||||
reg_register_list(x86_low_byte_regs, 8, ASM_REG_INT, 8, X86_RAX);
|
||||
reg_register_list(x86_float_regs, 8, ASM_REG_FLOAT, 80, X86_ST0);
|
||||
reg_register_list(x86_xmm_regs, 8, ASM_REF_MMX, 128, X86_MM0);
|
||||
reg_register_list(x86_long_regs, 8, ASM_REG_INT, ARG_BITS_32, X86_RAX);
|
||||
reg_register_list(x86_word_regs, 8, ASM_REG_INT, ARG_BITS_16, X86_RAX);
|
||||
reg_register_list(x86_low_byte_regs, 8, ASM_REG_INT, ARG_BITS_8, X86_RAX);
|
||||
reg_register_list(x86_float_regs, 8, ASM_REG_FLOAT, ARG_BITS_80, X86_ST0);
|
||||
reg_register_list(x86_xmm_regs, 8, ASM_REF_FVEC, ARG_BITS_128, X86_MM0);
|
||||
}
|
||||
else
|
||||
{
|
||||
reg_register_list(x64_quad_regs, 15, ASM_REG_INT, 64, X86_RAX);
|
||||
reg_register_list(x86_long_regs, 15, ASM_REG_INT, 32, X86_RAX);
|
||||
reg_register_list(x86_word_regs, 15, ASM_REG_INT, 16, X86_RAX);
|
||||
reg_register_list(x86_low_byte_regs, 15, ASM_REG_INT, 8, X86_RAX);
|
||||
reg_register_list(x86_high_byte_regs, 4, ASM_REG_INT, 8, X86_RAX);
|
||||
reg_register_list(x86_xmm_regs, 16, ASM_REF_MMX, 128, X86_XMM0);
|
||||
reg_register_list(x86_ymm_regs, 16, ASM_REF_MMX, 128, X86_YMM0);
|
||||
reg_register_list(x64_quad_regs, 15, ASM_REG_INT, ARG_BITS_64, X86_RAX);
|
||||
reg_register_list(x86_long_regs, 15, ASM_REG_INT, ARG_BITS_32, X86_RAX);
|
||||
reg_register_list(x86_word_regs, 15, ASM_REG_INT, ARG_BITS_16, X86_RAX);
|
||||
reg_register_list(x86_low_byte_regs, 15, ASM_REG_INT, ARG_BITS_8, X86_RAX);
|
||||
reg_register_list(x86_high_byte_regs, 4, ASM_REG_INT, ARG_BITS_8, X86_RAX);
|
||||
reg_register_list(x86_xmm_regs, 16, ASM_REF_FVEC, ARG_BITS_128, X86_XMM0);
|
||||
reg_register_list(x86_ymm_regs, 16, ASM_REF_FVEC, ARG_BITS_256, X86_XMM0);
|
||||
reg_register_list(x86_zmm_regs, 16, ASM_REF_FVEC, ARG_BITS_512, X86_XMM0);
|
||||
}
|
||||
}
|
||||
void init_asm(void)
|
||||
|
||||
@@ -35,6 +35,13 @@ Decl *decl_new(DeclKind decl_kind, const char *name, SourceSpan span, Visibility
|
||||
return decl;
|
||||
}
|
||||
|
||||
bool decl_is_ct_var(Decl *decl)
|
||||
{
|
||||
if (decl->decl_kind != DECL_VAR) return false;
|
||||
return decl_var_kind_is_ct(decl->var.kind);
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
Decl *decl_new_with_type(const char *name, SourceSpan loc, DeclKind decl_type, Visibility visibility)
|
||||
{
|
||||
Decl *decl = decl_calloc();
|
||||
@@ -87,6 +94,10 @@ Decl *decl_new_with_type(const char *name, SourceSpan loc, DeclKind decl_type, V
|
||||
case DECL_CT_ASSERT:
|
||||
case DECL_DECLARRAY:
|
||||
case DECL_BODYPARAM:
|
||||
case DECL_INITIALIZE:
|
||||
case DECL_FINALIZE:
|
||||
case DECL_CT_ECHO:
|
||||
case DECL_CT_INCLUDE:
|
||||
UNREACHABLE
|
||||
}
|
||||
Type *type = type_new(kind, name ? name : "$anon");
|
||||
@@ -117,6 +128,8 @@ const char *decl_to_a_name(Decl *decl)
|
||||
return "a poisoned decl";
|
||||
case DECL_CT_ASSERT:
|
||||
return "a compile time assert";
|
||||
case DECL_CT_ECHO:
|
||||
return "a compile time echo";
|
||||
case DECL_CT_CASE:
|
||||
return "a compile time case";
|
||||
case DECL_CT_ELIF:
|
||||
@@ -156,6 +169,12 @@ const char *decl_to_a_name(Decl *decl)
|
||||
return "a struct";
|
||||
case DECL_UNION:
|
||||
return "a union";
|
||||
case DECL_INITIALIZE:
|
||||
return "a static initializer";
|
||||
case DECL_FINALIZE:
|
||||
return "a static finalizer";
|
||||
case DECL_CT_INCLUDE:
|
||||
return "an include";
|
||||
case DECL_VAR:
|
||||
switch (decl->var.kind)
|
||||
{
|
||||
@@ -261,220 +280,6 @@ Decl *decl_new_generated_var(Type *type, VarDeclKind kind, SourceSpan span)
|
||||
return decl;
|
||||
}
|
||||
|
||||
// Determine if the expression has side effects
|
||||
// Note! This is not the same as it being const.
|
||||
bool expr_is_pure(Expr *expr)
|
||||
{
|
||||
if (!expr) return true;
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_BUILTIN:
|
||||
return false;
|
||||
case EXPR_BUILTIN_ACCESS:
|
||||
return exprid_is_pure(expr->builtin_access_expr.inner);
|
||||
case EXPR_VARIANT:
|
||||
return exprid_is_pure(expr->variant_expr.type_id) && exprid_is_pure(expr->variant_expr.ptr);
|
||||
case EXPR_POINTER_OFFSET:
|
||||
return exprid_is_pure(expr->pointer_offset_expr.ptr) && exprid_is_pure(expr->pointer_offset_expr.offset);
|
||||
case EXPR_COMPILER_CONST:
|
||||
case EXPR_CONST:
|
||||
case EXPR_IDENTIFIER:
|
||||
case EXPR_NOP:
|
||||
case EXPR_STRINGIFY:
|
||||
case EXPR_RETVAL:
|
||||
case EXPR_CT_CONV:
|
||||
case EXPR_TYPEINFO:
|
||||
case EXPR_CT_EVAL:
|
||||
case EXPR_CT_IDENT:
|
||||
case EXPR_CT_CALL:
|
||||
case EXPR_TYPEID:
|
||||
case EXPR_CT_ARG:
|
||||
return true;
|
||||
case EXPR_VASPLAT:
|
||||
return true;
|
||||
case EXPR_ARGV_TO_SUBARRAY:
|
||||
case EXPR_BITASSIGN:
|
||||
return false;
|
||||
case EXPR_VARIANTSWITCH:
|
||||
return false;
|
||||
case EXPR_BINARY:
|
||||
// Anything with assignment is impure, otherwise true if sub expr are pure.
|
||||
if (expr->binary_expr.operator >= BINARYOP_ASSIGN) return false;
|
||||
return exprid_is_pure(expr->binary_expr.right) && exprid_is_pure(expr->binary_expr.left);
|
||||
case EXPR_UNARY:
|
||||
switch (expr->unary_expr.operator)
|
||||
{
|
||||
case UNARYOP_INC:
|
||||
case UNARYOP_DEC:
|
||||
case UNARYOP_TADDR:
|
||||
// ++ -- &&1
|
||||
return false;
|
||||
case UNARYOP_ERROR:
|
||||
case UNARYOP_DEREF:
|
||||
case UNARYOP_ADDR:
|
||||
case UNARYOP_NEG:
|
||||
case UNARYOP_BITNEG:
|
||||
case UNARYOP_NOT:
|
||||
return expr_is_pure(expr->unary_expr.expr);
|
||||
}
|
||||
UNREACHABLE
|
||||
case EXPR_BITACCESS:
|
||||
case EXPR_ACCESS:
|
||||
// All access is pure if the parent is pure.
|
||||
return expr_is_pure(expr->access_expr.parent);
|
||||
case EXPR_POISONED:
|
||||
UNREACHABLE
|
||||
case EXPR_MACRO_BODY_EXPANSION:
|
||||
case EXPR_CALL:
|
||||
case EXPR_CATCH_UNWRAP:
|
||||
case EXPR_COMPOUND_LITERAL:
|
||||
case EXPR_COND:
|
||||
case EXPR_DESIGNATOR:
|
||||
case EXPR_DECL:
|
||||
case EXPR_EXPR_BLOCK:
|
||||
case EXPR_FAILABLE:
|
||||
case EXPR_RETHROW:
|
||||
case EXPR_HASH_IDENT:
|
||||
case EXPR_MACRO_BLOCK:
|
||||
case EXPR_FLATPATH:
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
case EXPR_DESIGNATED_INITIALIZER_LIST:
|
||||
case EXPR_POST_UNARY:
|
||||
case EXPR_SLICE_ASSIGN:
|
||||
case EXPR_TRY_UNWRAP:
|
||||
case EXPR_TRY_UNWRAP_CHAIN:
|
||||
case EXPR_FORCE_UNWRAP:
|
||||
return false;
|
||||
case EXPR_CAST:
|
||||
return exprid_is_pure(expr->cast_expr.expr);
|
||||
case EXPR_EXPRESSION_LIST:
|
||||
VECEACH(expr->expression_list, i)
|
||||
{
|
||||
if (!expr_is_pure(expr->expression_list[i])) return false;
|
||||
}
|
||||
return true;
|
||||
case EXPR_TYPEID_INFO:
|
||||
return exprid_is_pure(expr->typeid_info_expr.parent);
|
||||
case EXPR_SLICE:
|
||||
return exprid_is_pure(expr->subscript_expr.expr)
|
||||
&& exprid_is_pure(expr->subscript_expr.range.start)
|
||||
&& exprid_is_pure(expr->subscript_expr.range.end);
|
||||
case EXPR_SUBSCRIPT:
|
||||
case EXPR_SUBSCRIPT_ADDR:
|
||||
return exprid_is_pure(expr->subscript_expr.expr)
|
||||
&& exprid_is_pure(expr->subscript_expr.range.start);
|
||||
case EXPR_TERNARY:
|
||||
return exprid_is_pure(expr->ternary_expr.cond)
|
||||
&& exprid_is_pure(expr->ternary_expr.else_expr)
|
||||
&& exprid_is_pure(expr->ternary_expr.then_expr);
|
||||
case EXPR_ASM:
|
||||
return false;
|
||||
case EXPR_TRY:
|
||||
case EXPR_GROUP:
|
||||
case EXPR_CATCH:
|
||||
return expr_is_pure(expr->inner_expr);
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
|
||||
bool expr_is_simple(Expr *expr)
|
||||
{
|
||||
RETRY:
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_GROUP:
|
||||
expr = expr->inner_expr;
|
||||
goto RETRY;
|
||||
case EXPR_TERNARY:
|
||||
return expr_is_simple(exprptr(expr->ternary_expr.else_expr)) && expr_is_simple(exprptr(expr->ternary_expr.then_expr));
|
||||
case EXPR_RETHROW:
|
||||
expr = expr->rethrow_expr.inner;
|
||||
goto RETRY;
|
||||
default:
|
||||
return true;
|
||||
case EXPR_BINARY:
|
||||
switch (expr->binary_expr.operator)
|
||||
{
|
||||
case BINARYOP_AND:
|
||||
case BINARYOP_OR:
|
||||
case BINARYOP_GT:
|
||||
case BINARYOP_GE:
|
||||
case BINARYOP_LT:
|
||||
case BINARYOP_LE:
|
||||
case BINARYOP_NE:
|
||||
case BINARYOP_EQ:
|
||||
case BINARYOP_ASSIGN:
|
||||
case BINARYOP_ADD_ASSIGN:
|
||||
case BINARYOP_BIT_AND_ASSIGN:
|
||||
case BINARYOP_BIT_OR_ASSIGN:
|
||||
case BINARYOP_BIT_XOR_ASSIGN:
|
||||
case BINARYOP_DIV_ASSIGN:
|
||||
case BINARYOP_MOD_ASSIGN:
|
||||
case BINARYOP_MULT_ASSIGN:
|
||||
case BINARYOP_SHR_ASSIGN:
|
||||
case BINARYOP_SHL_ASSIGN:
|
||||
case BINARYOP_SUB_ASSIGN:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
UNREACHABLE
|
||||
case EXPR_UNARY:
|
||||
switch (expr->unary_expr.operator)
|
||||
{
|
||||
case UNARYOP_NEG:
|
||||
case UNARYOP_BITNEG:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
|
||||
Expr *expr_new(ExprKind kind, SourceSpan start)
|
||||
{
|
||||
Expr *expr = expr_calloc();
|
||||
expr->expr_kind = kind;
|
||||
expr->span = start;
|
||||
return expr;
|
||||
}
|
||||
|
||||
Expr *expr_new_const_int(SourceSpan span, Type *type, uint64_t v, bool narrowable)
|
||||
{
|
||||
Expr *expr = expr_calloc();
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr->span = span;
|
||||
expr->type = type;
|
||||
TypeKind kind = type_flatten(type)->type_kind;
|
||||
expr->const_expr.ixx.i.high = 0;
|
||||
if (type_kind_is_signed(kind))
|
||||
{
|
||||
if (v > (uint64_t)INT64_MAX) expr->const_expr.ixx.i.high = UINT64_MAX;
|
||||
}
|
||||
expr->const_expr.ixx.i.low = v;
|
||||
expr->const_expr.ixx.type = kind;
|
||||
expr->const_expr.const_kind = CONST_INTEGER;
|
||||
expr->const_expr.narrowable = narrowable;
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
return expr;
|
||||
}
|
||||
|
||||
Expr *expr_new_const_bool(SourceSpan span, Type *type, bool value)
|
||||
{
|
||||
Expr *expr = expr_calloc();
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr->span = span;
|
||||
expr->type = type;
|
||||
assert(type_flatten(type)->type_kind == TYPE_BOOL);
|
||||
expr->const_expr.b = value;
|
||||
expr->const_expr.const_kind = CONST_BOOL;
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
return expr;
|
||||
}
|
||||
|
||||
|
||||
BinaryOp binary_op[TOKEN_LAST + 1] = {
|
||||
@@ -582,110 +387,104 @@ AttributeType attribute_by_name(const char *name)
|
||||
return ATTRIBUTE_NONE;
|
||||
}
|
||||
|
||||
static inline ConstInitializer *initializer_for_index(ConstInitializer *initializer, uint32_t index)
|
||||
|
||||
|
||||
int decl_count_elements(Decl *structlike)
|
||||
{
|
||||
switch (initializer->kind)
|
||||
int elements = 0;
|
||||
Decl **members = structlike->strukt.members;
|
||||
unsigned member_size = vec_size(members);
|
||||
if (member_size == 0) return 0;
|
||||
if (structlike->decl_kind == DECL_UNION) member_size = 1;
|
||||
for (unsigned i = 0; i < member_size; i++)
|
||||
{
|
||||
case CONST_INIT_ZERO:
|
||||
case CONST_INIT_STRUCT:
|
||||
case CONST_INIT_UNION:
|
||||
case CONST_INIT_VALUE:
|
||||
return initializer;
|
||||
case CONST_INIT_ARRAY_FULL:
|
||||
return initializer->init_array_full[index];
|
||||
case CONST_INIT_ARRAY:
|
||||
Decl *member = members[i];
|
||||
if (member->decl_kind != DECL_VAR && !member->name)
|
||||
{
|
||||
ConstInitializer **sub_values = initializer->init_array.elements;
|
||||
VECEACH(sub_values, i)
|
||||
elements += decl_count_elements(member);
|
||||
continue;
|
||||
}
|
||||
elements++;
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
bool ast_is_compile_time(Ast *ast)
|
||||
{
|
||||
switch (ast->ast_kind)
|
||||
{
|
||||
case AST_NOP_STMT:
|
||||
return true;
|
||||
case AST_RETURN_STMT:
|
||||
case AST_BLOCK_EXIT_STMT:
|
||||
if (!ast->return_stmt.expr) return true;
|
||||
return expr_is_constant_eval(ast->return_stmt.expr, CONSTANT_EVAL_CONSTANT_VALUE);
|
||||
case AST_EXPR_STMT:
|
||||
return expr_is_compile_time(ast->expr_stmt);
|
||||
case AST_COMPOUND_STMT:
|
||||
{
|
||||
AstId current = ast->compound_stmt.first_stmt;
|
||||
while (current)
|
||||
{
|
||||
ConstInitializer *init = sub_values[i];
|
||||
assert(init->kind == CONST_INIT_ARRAY_VALUE);
|
||||
if (init->init_array_value.index == index) return init->init_array_value.element;
|
||||
if (!ast_is_compile_time(ast_next(¤t))) return false;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
case CONST_INIT_ARRAY_VALUE:
|
||||
UNREACHABLE
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
void expr_rewrite_to_const_zero(Expr *expr, Type *type)
|
||||
{
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr->const_expr.narrowable = true;
|
||||
switch (type->canonical->type_kind)
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
case TYPE_VOID:
|
||||
UNREACHABLE
|
||||
case ALL_INTS:
|
||||
expr_rewrite_const_int(expr, type, 0, true);
|
||||
return;
|
||||
case ALL_FLOATS:
|
||||
expr_rewrite_const_float(expr, type, 0);
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
expr_rewrite_const_bool(expr, type, false);
|
||||
return;
|
||||
case TYPE_POINTER:
|
||||
case TYPE_FAULTTYPE:
|
||||
case TYPE_ANY:
|
||||
case TYPE_ANYERR:
|
||||
case TYPE_TYPEID:
|
||||
expr_rewrite_const_null(expr, type);
|
||||
return;
|
||||
case TYPE_ENUM:
|
||||
expr->const_expr.const_kind = CONST_ENUM;
|
||||
expr->const_expr.enum_val = type->decl->enums.values[0];
|
||||
break;
|
||||
case TYPE_FUNC:
|
||||
case TYPE_TYPEDEF:
|
||||
case TYPE_FAILABLE_ANY:
|
||||
case TYPE_FAILABLE:
|
||||
case TYPE_TYPEINFO:
|
||||
UNREACHABLE
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
case TYPE_BITSTRUCT:
|
||||
case TYPE_ARRAY:
|
||||
case TYPE_SUBARRAY:
|
||||
case TYPE_INFERRED_ARRAY:
|
||||
case TYPE_FLEXIBLE_ARRAY:
|
||||
case TYPE_UNTYPED_LIST:
|
||||
case TYPE_VECTOR:
|
||||
{
|
||||
ConstInitializer *init = CALLOCS(ConstInitializer);
|
||||
init->kind = CONST_INIT_ZERO;
|
||||
init->type = type;
|
||||
expr_rewrite_const_list(expr, type, init);
|
||||
return;
|
||||
}
|
||||
case TYPE_DISTINCT:
|
||||
expr_rewrite_to_const_zero(expr, type->decl->distinct_decl.base_type);
|
||||
break;
|
||||
}
|
||||
expr->type = type;
|
||||
}
|
||||
|
||||
bool expr_rewrite_to_const_initializer_index(Type *list_type, ConstInitializer *list, Expr *result, unsigned index)
|
||||
{
|
||||
ConstInitializer *initializer = initializer_for_index(list, index);
|
||||
ConstInitType kind = initializer ? initializer->kind : CONST_INIT_ZERO;
|
||||
switch (kind)
|
||||
{
|
||||
case CONST_INIT_ZERO:
|
||||
expr_rewrite_to_const_zero(result, type_get_indexed_type(list_type));
|
||||
return true;
|
||||
case CONST_INIT_STRUCT:
|
||||
case CONST_INIT_UNION:
|
||||
case CONST_INIT_ARRAY:
|
||||
case CONST_INIT_ARRAY_FULL:
|
||||
case CONST_INIT_ARRAY_VALUE:
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
case CONST_INIT_VALUE:
|
||||
expr_replace(result, initializer->init_value);
|
||||
return true;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
Decl *decl_find_enum_constant(Decl *decl, const char *name)
|
||||
{
|
||||
VECEACH(decl->enums.values, i)
|
||||
{
|
||||
Decl *enum_constant = decl->enums.values[i];
|
||||
if (enum_constant->name == name)
|
||||
{
|
||||
return enum_constant;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AlignSize decl_find_member_offset(Decl *decl, Decl *member)
|
||||
{
|
||||
static const AlignSize NO_MATCH = ~(AlignSize)0;
|
||||
while (decl->decl_kind == DECL_DISTINCT) decl = decl->distinct_decl.base_type->decl;
|
||||
Decl **members = NULL;
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_BITSTRUCT:
|
||||
members = decl->bitstruct.members;
|
||||
break;
|
||||
case DECL_STRUCT:
|
||||
case DECL_UNION:
|
||||
members = decl->strukt.members;
|
||||
break;
|
||||
default:
|
||||
return NO_MATCH;
|
||||
}
|
||||
assert(members);
|
||||
unsigned list = vec_size(members);
|
||||
for (unsigned i = 0; i < list; i++)
|
||||
{
|
||||
Decl *m = members[i];
|
||||
if (m == member)
|
||||
{
|
||||
return member->offset;
|
||||
}
|
||||
if (m->decl_kind != DECL_VAR)
|
||||
{
|
||||
AlignSize possible_offset = decl_find_member_offset(m, member);
|
||||
if (possible_offset != NO_MATCH) return possible_offset + m->offset;
|
||||
}
|
||||
}
|
||||
return NO_MATCH;
|
||||
}
|
||||
|
||||
bool ast_supports_continue(Ast *stmt)
|
||||
{
|
||||
if (stmt->ast_kind != AST_FOR_STMT) return false;
|
||||
return stmt->for_stmt.cond || !stmt->flow.skip_first;
|
||||
}
|
||||
|
||||
@@ -126,14 +126,10 @@ static inline char *codegen_create_x86_att_asm(AsmInlineBlock *block)
|
||||
Expr** args = ast->asm_stmt.args;
|
||||
unsigned arg_count = vec_size(args);
|
||||
scratch_buffer_append_char(' ');
|
||||
if (arg_count > 1)
|
||||
for (unsigned i = arg_count; i > 0; i--)
|
||||
{
|
||||
codegen_create_x86att_arg(block, input_arg_offset, args[1]);
|
||||
scratch_buffer_append(", ");
|
||||
}
|
||||
if (arg_count)
|
||||
{
|
||||
codegen_create_x86att_arg(block, input_arg_offset, args[0]);
|
||||
if (i != arg_count) scratch_buffer_append(", ");
|
||||
codegen_create_x86att_arg(block, input_arg_offset, args[i - 1]);
|
||||
}
|
||||
scratch_buffer_append_char('\n');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "codegen_internal.h"
|
||||
|
||||
const char *test_fns_var_name = "__$C3_TEST_FN_LIST";
|
||||
const char *test_count_var_name = "__$C3_TEST_COUNT";
|
||||
const char *test_names_var_name = "__$C3_TEST_NAMES_LIST";
|
||||
/**
|
||||
* Based on isSingleElementStruct in Clang
|
||||
*/
|
||||
@@ -166,8 +169,8 @@ bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements)
|
||||
RETRY:
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_FAILABLE:
|
||||
type = type->failable;
|
||||
case TYPE_OPTIONAL:
|
||||
type = type->optional;
|
||||
goto RETRY;
|
||||
case TYPE_DISTINCT:
|
||||
type = type->decl->distinct_decl.base_type;
|
||||
@@ -180,7 +183,7 @@ bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements)
|
||||
case TYPE_FUNC:
|
||||
case TYPE_SUBARRAY:
|
||||
case CT_TYPES:
|
||||
case TYPE_FAILABLE_ANY:
|
||||
case TYPE_OPTIONAL_ANY:
|
||||
return false;
|
||||
case TYPE_ANY:
|
||||
*base = type_iptr->canonical;
|
||||
@@ -234,6 +237,7 @@ bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements)
|
||||
}
|
||||
goto TYPECHECK;
|
||||
case TYPE_FLEXIBLE_ARRAY:
|
||||
case TYPE_SCALED_VECTOR:
|
||||
return false;
|
||||
case TYPE_ARRAY:
|
||||
// Empty arrays? Not homogenous.
|
||||
|
||||
@@ -56,4 +56,8 @@ static inline bool abi_type_is_promotable_integer_or_bool(AbiType type)
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *codegen_create_asm(Ast *ast);
|
||||
const char *codegen_create_asm(Ast *ast);
|
||||
|
||||
extern const char *test_fns_var_name;
|
||||
extern const char *test_count_var_name;
|
||||
extern const char *test_names_var_name;
|
||||
@@ -38,7 +38,7 @@ void compiler_init(const char *std_lib_dir)
|
||||
compiler_codegen_time = -1;
|
||||
compiler_link_time = -1;
|
||||
|
||||
DEBUG_LOG("Version: %s", COMPILER_VERSION);
|
||||
INFO_LOG("Version: %s", COMPILER_VERSION);
|
||||
|
||||
global_context = (GlobalContext ){ .in_panic_mode = false };
|
||||
// Skip library detection.
|
||||
@@ -51,6 +51,7 @@ void compiler_init(const char *std_lib_dir)
|
||||
htable_init(&global_context.compiler_defines, 16 * 1024);
|
||||
global_context.module_list = NULL;
|
||||
global_context.generic_module_list = NULL;
|
||||
global_context.method_extensions = NULL;
|
||||
vmem_init(&ast_arena, 512);
|
||||
ast_calloc();
|
||||
vmem_init(&expr_arena, 512);
|
||||
@@ -75,7 +76,9 @@ static void compiler_lex(void)
|
||||
VECEACH(global_context.sources, i)
|
||||
{
|
||||
bool loaded = false;
|
||||
File *file = source_file_load(global_context.sources[i], &loaded);
|
||||
const char *error;
|
||||
File *file = source_file_load(global_context.sources[i], &loaded, &error);
|
||||
if (!file) error_exit(error);
|
||||
if (loaded) continue;
|
||||
Lexer lexer = { .file = file };
|
||||
lexer_init(&lexer);
|
||||
@@ -96,12 +99,19 @@ void compiler_parse(void)
|
||||
VECEACH(global_context.sources, i)
|
||||
{
|
||||
bool loaded = false;
|
||||
File *file = source_file_load(global_context.sources[i], &loaded);
|
||||
const char *error;
|
||||
File *file = source_file_load(global_context.sources[i], &loaded, &error);
|
||||
if (!file) error_exit(error);
|
||||
if (loaded) continue;
|
||||
|
||||
global_context_clear_errors();
|
||||
parse_file(file);
|
||||
}
|
||||
if (active_target.read_stdin)
|
||||
{
|
||||
global_context_clear_errors();
|
||||
parse_stdin();
|
||||
}
|
||||
exit_compiler(COMPILER_SUCCESS_EXIT);
|
||||
}
|
||||
|
||||
@@ -247,7 +257,6 @@ void compiler_compile(void)
|
||||
|
||||
Module **modules = global_context.module_list;
|
||||
unsigned module_count = vec_size(modules);
|
||||
|
||||
if (module_count > MAX_MODULES)
|
||||
{
|
||||
error_exit("Too many modules.");
|
||||
@@ -306,12 +315,7 @@ void compiler_compile(void)
|
||||
switch (active_target.backend)
|
||||
{
|
||||
case BACKEND_LLVM:
|
||||
llvm_codegen_setup();
|
||||
for (unsigned i = 0; i < module_count; i++)
|
||||
{
|
||||
void *result = llvm_gen(modules[i]);
|
||||
if (result) vec_add(gen_contexts, result);
|
||||
}
|
||||
gen_contexts = llvm_gen(modules, module_count);
|
||||
task = &thread_compile_task_llvm;
|
||||
break;
|
||||
case BACKEND_TB:
|
||||
@@ -335,8 +339,11 @@ void compiler_compile(void)
|
||||
{
|
||||
switch (active_target.type)
|
||||
{
|
||||
case TARGET_TYPE_EXECUTABLE:
|
||||
case TARGET_TYPE_TEST:
|
||||
active_target.name = "testrun";
|
||||
output_exe = exe_name();
|
||||
break;
|
||||
case TARGET_TYPE_EXECUTABLE:
|
||||
if (!global_context.main)
|
||||
{
|
||||
puts("No main function was found, compilation only object files are generated.");
|
||||
@@ -389,22 +396,22 @@ void compiler_compile(void)
|
||||
filename[len - 1] = 'o';
|
||||
obj_files[output_file_count + i] = filename;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
TaskQueueRef queue = taskqueue_create(16);
|
||||
|
||||
|
||||
Task **tasks = NULL;
|
||||
for (unsigned i = 0; i < output_file_count; i++)
|
||||
{
|
||||
compile_data[i] = (CompileData) { .context = gen_contexts[i] };
|
||||
compile_data[i].task = (Task) { task, &compile_data[i] };
|
||||
taskqueue_add(queue, &compile_data[i].task);
|
||||
vec_add(tasks, &compile_data[i].task);
|
||||
}
|
||||
|
||||
#if USE_PTHREAD
|
||||
INFO_LOG("Will use %d thread(s).", active_target.build_threads);
|
||||
#endif
|
||||
|
||||
TaskQueueRef queue = taskqueue_create(active_target.build_threads, tasks);
|
||||
taskqueue_wait_for_completion(queue);
|
||||
taskqueue_destroy(queue);
|
||||
|
||||
for (unsigned i = 0; i < output_file_count; i++)
|
||||
{
|
||||
@@ -412,15 +419,13 @@ void compiler_compile(void)
|
||||
assert(obj_files[i] || !output_exe);
|
||||
}
|
||||
|
||||
|
||||
|
||||
output_file_count += cfiles;
|
||||
free(compile_data);
|
||||
compiler_codegen_time = bench_mark();
|
||||
|
||||
if (output_exe)
|
||||
{
|
||||
if (platform_target.os != OS_TYPE_WIN32 && active_target.arch_os_target == default_target && !active_target.force_linker)
|
||||
if (!active_target.no_libc && platform_target.os != OS_TYPE_WIN32 && active_target.arch_os_target == default_target && !active_target.force_linker)
|
||||
{
|
||||
platform_linker(output_exe, obj_files, output_file_count);
|
||||
compiler_link_time = bench_mark();
|
||||
@@ -442,7 +447,7 @@ void compiler_compile(void)
|
||||
DEBUG_LOG("Will run");
|
||||
printf("Launching %s...\n", output_exe);
|
||||
int ret = system(platform_target.os == OS_TYPE_WIN32 ? output_exe : str_printf("./%s", output_exe));
|
||||
printf("Program finished with exit code %d.", ret);
|
||||
printf("Program finished with exit code %d.\n", ret);
|
||||
}
|
||||
}
|
||||
if (output_static)
|
||||
@@ -470,6 +475,7 @@ static const char **target_expand_source_names(const char** dirs, const char **s
|
||||
VECEACH(dirs, i)
|
||||
{
|
||||
const char *name = dirs[i];
|
||||
DEBUG_LOG("Searching for sources in %s", name);
|
||||
size_t name_len = strlen(name);
|
||||
if (name_len < 1) goto INVALID_NAME;
|
||||
if (name[name_len - 1] == '*')
|
||||
@@ -481,9 +487,11 @@ static const char **target_expand_source_names(const char** dirs, const char **s
|
||||
continue;
|
||||
}
|
||||
if (name[name_len - 2] != '*') goto INVALID_NAME;
|
||||
DEBUG_LOG("Searching for wildcard sources in %s", name);
|
||||
if (name_len == 2 || name[name_len - 3] == '/')
|
||||
{
|
||||
char *path = str_copy(name, name_len - 2);
|
||||
DEBUG_LOG("Reduced path %s", path);
|
||||
file_add_wildcard_files(&files, path, true, suffix_list, suffix_count);
|
||||
continue;
|
||||
}
|
||||
@@ -494,6 +502,11 @@ static const char **target_expand_source_names(const char** dirs, const char **s
|
||||
vec_add(files, name);
|
||||
continue;
|
||||
INVALID_NAME:
|
||||
if (file_is_dir(name))
|
||||
{
|
||||
file_add_wildcard_files(&files, name, true, suffix_list, suffix_count);
|
||||
continue;
|
||||
}
|
||||
if (!error_on_mismatch) continue;
|
||||
error_exit("File names must end with %s or they cannot be compiled: '%s' is invalid.", suffix_list[0], name);
|
||||
}
|
||||
@@ -588,7 +601,7 @@ void print_syntax(BuildOptions *options)
|
||||
{
|
||||
for (int i = 0; i < NUMBER_OF_ATTRIBUTES; i++)
|
||||
{
|
||||
printf("%2d @%s\n", i + 1, attribute_list[i]);
|
||||
printf("%2d %s\n", i + 1, attribute_list[i]);
|
||||
}
|
||||
}
|
||||
if (options->print_builtins)
|
||||
@@ -597,6 +610,35 @@ void print_syntax(BuildOptions *options)
|
||||
{
|
||||
printf("%2d $$%s\n", i + 1, builtin_list[i]);
|
||||
}
|
||||
puts("---");
|
||||
for (int i = 0; i < NUMBER_OF_BUILTIN_DEFINES; i++)
|
||||
{
|
||||
printf("%2d $$%s\n", i + 1, builtin_defines[i]);
|
||||
}
|
||||
}
|
||||
if (options->print_type_properties)
|
||||
{
|
||||
for (int i = 0; i < NUMBER_OF_TYPE_PROPERTIES; i++)
|
||||
{
|
||||
printf("%2d .%s\n", i + 1, type_property_list[i]);
|
||||
}
|
||||
}
|
||||
if (options->print_project_properties)
|
||||
{
|
||||
puts("Project properties");
|
||||
puts("------------------");
|
||||
for (int i = 0; i < project_default_keys_count; i++)
|
||||
{
|
||||
printf("%2d %s\n", i + 1, project_default_keys[i]);
|
||||
}
|
||||
puts("");
|
||||
puts("Target properties");
|
||||
puts("-----------------");
|
||||
for (int i = 0; i < project_target_keys_count; i++)
|
||||
{
|
||||
printf("%2d %s\n", i + 1, project_target_keys[i]);
|
||||
}
|
||||
puts("");
|
||||
}
|
||||
if (options->print_precedence)
|
||||
{
|
||||
@@ -620,6 +662,48 @@ void print_syntax(BuildOptions *options)
|
||||
|
||||
void resolve_libraries(void);
|
||||
|
||||
static int jump_buffer_size()
|
||||
{
|
||||
switch (active_target.arch_os_target)
|
||||
{
|
||||
case ARCH_OS_TARGET_DEFAULT:
|
||||
return 512;
|
||||
case ELF_RISCV32:
|
||||
case ELF_RISCV64:
|
||||
case LINUX_RISCV32:
|
||||
case LINUX_RISCV64:
|
||||
REMINDER("RISCV jmpbuf size is unknown");
|
||||
return 512;
|
||||
case ELF_X64:
|
||||
case FREEBSD_X64:
|
||||
case LINUX_X64:
|
||||
case MACOS_X64:
|
||||
case WINDOWS_X64:
|
||||
case MINGW_X64:
|
||||
case NETBSD_X64:
|
||||
case OPENBSD_X64:
|
||||
// Based on MacOS headers
|
||||
return ((9 * 2) + 3 + 16);
|
||||
case LINUX_AARCH64:
|
||||
case ELF_AARCH64:
|
||||
case MACOS_AARCH64:
|
||||
// Based on MacOS headers
|
||||
return ((14 + 8 + 2) * 2);
|
||||
case LINUX_X86:
|
||||
case MCU_X86:
|
||||
case NETBSD_X86:
|
||||
case OPENBSD_X86:
|
||||
case WINDOWS_X86:
|
||||
case ELF_X86:
|
||||
case FREEBSD_X86:
|
||||
return 18;
|
||||
case WASM32:
|
||||
case WASM64:
|
||||
REMINDER("WASM setjmp size is unknown");
|
||||
return 512;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
void compile()
|
||||
{
|
||||
symtab_init(active_target.symtab_size);
|
||||
@@ -644,17 +728,21 @@ void compile()
|
||||
setup_bool_define("PLATFORM_I128_SUPPORTED", platform_target.int128);
|
||||
setup_bool_define("PLATFORM_F128_SUPPORTED", platform_target.float128);
|
||||
setup_bool_define("PLATFORM_F16_SUPPORTED", platform_target.float16);
|
||||
setup_bool_define("COMPILER_LIBC_AVAILABLE", !active_target.no_libc);
|
||||
setup_int_define("COMPILER_OPT_LEVEL", (uint64_t)active_target.optimization_level, type_int);
|
||||
setup_int_define("OS_TYPE", (uint64_t)platform_target.os, type_int);
|
||||
setup_int_define("COMPILER_SIZE_OPT_LEVEL", (uint64_t)active_target.size_optimization_level, type_int);
|
||||
setup_bool_define("COMPILER_SAFE_MODE", active_target.feature.safe_mode);
|
||||
setup_int_define("LLVM_VERSION", llvm_version_major, type_int);
|
||||
setup_bool_define("BENCHMARKING", active_target.benchmarking);
|
||||
setup_int_define("JMP_BUF_SIZE", jump_buffer_size(), type_int);
|
||||
setup_bool_define("TESTING", active_target.testing);
|
||||
|
||||
type_init_cint();
|
||||
|
||||
compiler_init_time = bench_mark();
|
||||
|
||||
if (!vec_size(active_target.sources)) error_exit("No files to compile.");
|
||||
if (!vec_size(active_target.sources) && !active_target.read_stdin) error_exit("No files to compile.");
|
||||
if (active_target.lex_only)
|
||||
{
|
||||
compiler_lex();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
#include "compiler_internal.h"
|
||||
|
||||
|
||||
CompilationUnit * unit_create(File *file)
|
||||
CompilationUnit *unit_create(File *file)
|
||||
{
|
||||
CompilationUnit *unit = CALLOCS(CompilationUnit);
|
||||
unit->file = file;
|
||||
@@ -132,6 +132,8 @@ void decl_register(Decl *decl)
|
||||
if (decl->visibility != VISIBLE_PUBLIC && decl->visibility != VISIBLE_EXTERN) return;
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_INITIALIZE:
|
||||
case DECL_FINALIZE:
|
||||
case DECL_POISONED:
|
||||
case DECL_CT_CASE:
|
||||
case DECL_CT_ELIF:
|
||||
@@ -139,12 +141,14 @@ void decl_register(Decl *decl)
|
||||
case DECL_CT_IF:
|
||||
case DECL_CT_SWITCH:
|
||||
case DECL_CT_ASSERT:
|
||||
case DECL_CT_ECHO:
|
||||
case DECL_ENUM_CONSTANT:
|
||||
case DECL_FAULTVALUE:
|
||||
case DECL_IMPORT:
|
||||
case DECL_LABEL:
|
||||
case DECL_DECLARRAY:
|
||||
case DECL_BODYPARAM:
|
||||
case DECL_CT_INCLUDE:
|
||||
UNREACHABLE
|
||||
case DECL_ATTRIBUTE:
|
||||
case DECL_BITSTRUCT:
|
||||
@@ -259,11 +263,19 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl)
|
||||
UNREACHABLE
|
||||
case DECL_CT_IF:
|
||||
case DECL_CT_SWITCH:
|
||||
case DECL_CT_INCLUDE:
|
||||
vec_add(unit->ct_ifs, decl);
|
||||
return;
|
||||
case DECL_CT_ECHO:
|
||||
vec_add(unit->ct_echos, decl);
|
||||
return;
|
||||
case DECL_CT_ASSERT:
|
||||
vec_add(unit->ct_asserts, decl);
|
||||
return;
|
||||
case DECL_INITIALIZE:
|
||||
case DECL_FINALIZE:
|
||||
vec_add(unit->xxlizers, decl);
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG("Registering symbol '%s' in %s.", decl->name, unit->module->name->module);
|
||||
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
#define SCOPE_FIXUP_START do { CopyFixup *current = c->current_fixup;
|
||||
#define SCOPE_FIXUP_END c->current_fixup = current; } while (0)
|
||||
|
||||
static inline void copy_const_initializer(CopyStruct *c, ConstInitializer **initializer_ref);
|
||||
static inline void copy_reg_ref(CopyStruct *c, void *original, void *result);
|
||||
static inline void *fixup(CopyStruct *c, void *original);
|
||||
INLINE void fixup_decl(CopyStruct *c, Decl **decl_ref);
|
||||
INLINE void fixup_declid(CopyStruct *c, DeclId *declid_ref);
|
||||
INLINE ConstInitializer **copy_const_initializer_list(CopyStruct *c, ConstInitializer **initializer_list);
|
||||
INLINE ConstInitializer **copy_const_initializer_array(CopyStruct *c, ConstInitializer **initializer_list, unsigned len);
|
||||
|
||||
static Expr **copy_expr_list(CopyStruct *c, Expr **expr_list);
|
||||
static Expr *copy_expr(CopyStruct *c, Expr *source_expr);
|
||||
static Ast *ast_copy_deep(CopyStruct *c, Ast *source);
|
||||
static Ast **copy_ast_list(CopyStruct *c, Ast **to_copy);
|
||||
static Decl *copy_decl(CopyStruct *c, Decl *decl);
|
||||
static Decl **copy_decl_list(CopyStruct *c, Decl **decl_list);
|
||||
static TypeInfo *copy_type_info(CopyStruct *c, TypeInfo *source);
|
||||
|
||||
static inline void copy_reg_ref(CopyStruct *c, void *original, void *result)
|
||||
{
|
||||
c->current_fixup->new_ptr = result;
|
||||
@@ -141,32 +157,35 @@ static DesignatorElement **macro_copy_designator_list(CopyStruct *c, DesignatorE
|
||||
|
||||
static CopyStruct copy_struct;
|
||||
|
||||
Ast *ast_macro_copy(Ast *source_ast)
|
||||
Ast *copy_ast_single(Ast *source_ast)
|
||||
{
|
||||
copy_struct.current_fixup = copy_struct.fixups;
|
||||
copy_struct.single_static = false;
|
||||
copy_begin();
|
||||
Ast *ast = ast_copy_deep(©_struct, source_ast);
|
||||
copy_end();
|
||||
return ast;
|
||||
}
|
||||
|
||||
Ast *copy_ast_macro(Ast *source_ast)
|
||||
{
|
||||
assert(copy_struct.copy_in_use);
|
||||
return ast_copy_deep(©_struct, source_ast);
|
||||
}
|
||||
|
||||
Decl *decl_macro_copy(Decl *source_decl)
|
||||
Ast *copy_ast_defer(Ast *source_ast)
|
||||
{
|
||||
copy_struct.current_fixup = copy_struct.fixups;
|
||||
copy_struct.single_static = false;
|
||||
return copy_decl(©_struct, source_decl);
|
||||
}
|
||||
|
||||
Ast *ast_defer_copy(Ast *source_ast)
|
||||
{
|
||||
copy_struct.current_fixup = copy_struct.fixups;
|
||||
copy_begin();
|
||||
copy_struct.single_static = true;
|
||||
return ast_copy_deep(©_struct, source_ast);
|
||||
Ast *ast = copy_ast_macro(source_ast);
|
||||
copy_end();
|
||||
return ast;
|
||||
}
|
||||
|
||||
Expr *expr_macro_copy(Expr *source_expr)
|
||||
Expr *copy_expr_single(Expr *source_expr)
|
||||
{
|
||||
copy_struct.current_fixup = copy_struct.fixups;
|
||||
copy_struct.single_static = false;
|
||||
return copy_expr(©_struct, source_expr);
|
||||
copy_begin();
|
||||
Expr *expr = copy_expr(©_struct, source_expr);
|
||||
copy_end();
|
||||
return expr;
|
||||
}
|
||||
|
||||
void copy_range(CopyStruct *c, Range *range)
|
||||
@@ -175,6 +194,91 @@ void copy_range(CopyStruct *c, Range *range)
|
||||
MACRO_COPY_EXPRID(range->end);
|
||||
}
|
||||
|
||||
INLINE ConstInitializer **copy_const_initializer_list(CopyStruct *c, ConstInitializer **initializer_list)
|
||||
{
|
||||
ConstInitializer **initializer = NULL;
|
||||
FOREACH_BEGIN(ConstInitializer *element, initializer_list)
|
||||
copy_const_initializer(c, &element);
|
||||
vec_add(initializer, element);
|
||||
FOREACH_END();
|
||||
return initializer;
|
||||
}
|
||||
|
||||
INLINE ConstInitializer **copy_const_initializer_array(CopyStruct *c, ConstInitializer **initializer_list, unsigned len)
|
||||
{
|
||||
ConstInitializer **initializer = MALLOC(sizeof(ConstInitializer*) * len);
|
||||
for (unsigned i = 0; i < len; i++)
|
||||
{
|
||||
ConstInitializer *element = initializer_list[i];
|
||||
copy_const_initializer(c, &element);
|
||||
initializer[i] = element;
|
||||
}
|
||||
return initializer;
|
||||
}
|
||||
|
||||
static inline void copy_const_initializer(CopyStruct *c, ConstInitializer **initializer_ref)
|
||||
{
|
||||
ConstInitializer *copy = MALLOCS(ConstInitializer);
|
||||
*copy = **initializer_ref;
|
||||
*initializer_ref = copy;
|
||||
|
||||
switch (copy->kind)
|
||||
{
|
||||
case CONST_INIT_ZERO:
|
||||
return;
|
||||
case CONST_INIT_STRUCT:
|
||||
copy->init_struct = copy_const_initializer_array(c, copy->init_struct, vec_size(type_flatten(copy->type)->decl->strukt.members));
|
||||
return;
|
||||
case CONST_INIT_UNION:
|
||||
copy_const_initializer(c, ©->init_union.element);
|
||||
return;
|
||||
case CONST_INIT_VALUE:
|
||||
copy->init_value = copy_expr(c, copy->init_value);
|
||||
return;
|
||||
case CONST_INIT_ARRAY:
|
||||
copy->init_array.elements = copy_const_initializer_list(c, copy->init_array.elements);
|
||||
return;
|
||||
case CONST_INIT_ARRAY_FULL:
|
||||
copy->init_array_full = copy_const_initializer_list(c, copy->init_array_full);
|
||||
return;
|
||||
case CONST_INIT_ARRAY_VALUE:
|
||||
copy_const_initializer(c, ©->init_array_value.element);
|
||||
return;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
INLINE Expr *copy_const_expr(CopyStruct *c, Expr *expr)
|
||||
{
|
||||
switch (expr->const_expr.const_kind)
|
||||
{
|
||||
case CONST_FLOAT:
|
||||
case CONST_INTEGER:
|
||||
case CONST_BOOL:
|
||||
break;
|
||||
case CONST_ENUM:
|
||||
case CONST_ERR:
|
||||
fixup_decl(c, &expr->const_expr.enum_err_val);
|
||||
break;
|
||||
case CONST_BYTES:
|
||||
case CONST_STRING:
|
||||
// Assume this is never modified.
|
||||
break;
|
||||
case CONST_POINTER:
|
||||
case CONST_TYPEID:
|
||||
break;
|
||||
case CONST_INITIALIZER:
|
||||
copy_const_initializer(c, &expr->const_expr.initializer);
|
||||
break;
|
||||
case CONST_UNTYPED_LIST:
|
||||
expr->const_expr.untyped_list = copy_expr_list(c, expr->const_expr.untyped_list);
|
||||
break;
|
||||
case CONST_MEMBER:
|
||||
fixup_decl(c, &expr->const_expr.member.decl);
|
||||
break;
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
Expr *copy_expr(CopyStruct *c, Expr *source_expr)
|
||||
{
|
||||
if (!source_expr) return NULL;
|
||||
@@ -193,6 +297,7 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
|
||||
case EXPR_NOP:
|
||||
case EXPR_BUILTIN:
|
||||
case EXPR_RETVAL:
|
||||
case EXPR_OPERATOR_CHARS:
|
||||
return expr;
|
||||
case EXPR_VASPLAT:
|
||||
copy_range(c, &expr->vasplat_expr);
|
||||
@@ -207,10 +312,6 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
|
||||
case EXPR_BUILTIN_ACCESS:
|
||||
MACRO_COPY_EXPRID(expr->builtin_access_expr.inner);
|
||||
return expr;
|
||||
case EXPR_CT_CONV:
|
||||
MACRO_COPY_TYPEID(expr->ct_call_expr.type_from);
|
||||
MACRO_COPY_TYPEID(expr->ct_call_expr.type_to);
|
||||
return expr;
|
||||
case EXPR_DECL:
|
||||
MACRO_COPY_DECL(expr->decl_expr);
|
||||
return expr;
|
||||
@@ -242,6 +343,7 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
|
||||
case EXPR_HASH_IDENT:
|
||||
assert(expr->resolve_status != RESOLVE_DONE);
|
||||
return expr;
|
||||
case EXPR_TEST_HOOK:
|
||||
case EXPR_COMPILER_CONST:
|
||||
return expr;
|
||||
case EXPR_DESIGNATOR:
|
||||
@@ -252,9 +354,12 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
|
||||
MACRO_COPY_TYPE(expr->type_expr);
|
||||
return expr;
|
||||
case EXPR_SLICE_ASSIGN:
|
||||
case EXPR_SLICE_COPY:
|
||||
MACRO_COPY_EXPRID(expr->slice_assign_expr.left);
|
||||
MACRO_COPY_EXPRID(expr->slice_assign_expr.right);
|
||||
return expr;
|
||||
case EXPR_SUBSCRIPT_ASSIGN:
|
||||
UNREACHABLE
|
||||
case EXPR_SLICE:
|
||||
case EXPR_SUBSCRIPT:
|
||||
case EXPR_SUBSCRIPT_ADDR:
|
||||
@@ -279,10 +384,11 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
|
||||
case EXPR_FORCE_UNWRAP:
|
||||
case EXPR_TRY:
|
||||
case EXPR_CATCH:
|
||||
case EXPR_FAILABLE:
|
||||
case EXPR_OPTIONAL:
|
||||
case EXPR_GROUP:
|
||||
case EXPR_STRINGIFY:
|
||||
case EXPR_CT_EVAL:
|
||||
case EXPR_CT_CHECKS:
|
||||
MACRO_COPY_EXPR(expr->inner_expr);
|
||||
return expr;
|
||||
case EXPR_TYPEID_INFO:
|
||||
@@ -308,7 +414,7 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
|
||||
MACRO_COPY_EXPR(expr->rethrow_expr.inner);
|
||||
return expr;
|
||||
case EXPR_CONST:
|
||||
return expr;
|
||||
return copy_const_expr(c, expr);
|
||||
case EXPR_BINARY:
|
||||
case EXPR_BITASSIGN:
|
||||
MACRO_COPY_EXPRID(expr->binary_expr.left);
|
||||
@@ -455,7 +561,7 @@ RETRY:
|
||||
break;
|
||||
case AST_CT_IF_STMT:
|
||||
MACRO_COPY_EXPR(ast->ct_if_stmt.expr);
|
||||
MACRO_COPY_AST(ast->ct_if_stmt.elif);
|
||||
MACRO_COPY_ASTID(ast->ct_if_stmt.elif);
|
||||
MACRO_COPY_ASTID(ast->ct_if_stmt.then);
|
||||
break;
|
||||
case AST_CT_ELSE_STMT:
|
||||
@@ -466,7 +572,7 @@ RETRY:
|
||||
MACRO_COPY_EXPRID(ast->ct_foreach_stmt.expr);
|
||||
break;
|
||||
case AST_CT_SWITCH_STMT:
|
||||
MACRO_COPY_EXPR(ast->ct_switch_stmt.cond);
|
||||
MACRO_COPY_EXPRID(ast->ct_switch_stmt.cond);
|
||||
MACRO_COPY_AST_LIST(ast->ct_switch_stmt.body);
|
||||
break;
|
||||
case AST_DECLARE_STMT:
|
||||
@@ -480,6 +586,7 @@ RETRY:
|
||||
copy_reg_ref(c, source, ast);
|
||||
fixup_astid(c, &ast->defer_stmt.prev_defer);
|
||||
break;
|
||||
case AST_CT_ECHO_STMT:
|
||||
case AST_EXPR_STMT:
|
||||
MACRO_COPY_EXPR(ast->expr_stmt);
|
||||
break;
|
||||
@@ -549,14 +656,31 @@ Ast **copy_ast_list(CopyStruct *c, Ast **to_copy)
|
||||
return result;
|
||||
}
|
||||
|
||||
Decl **decl_copy_list(Decl **decl_list)
|
||||
void copy_begin(void)
|
||||
{
|
||||
copy_struct.current_fixup = copy_struct.fixups;
|
||||
Decl **result = NULL;
|
||||
VECEACH(decl_list, i)
|
||||
{
|
||||
vec_add(result, copy_decl(©_struct, decl_list[i]));
|
||||
}
|
||||
assert(!copy_struct.copy_in_use);
|
||||
copy_struct.copy_in_use = true;
|
||||
copy_struct.single_static = false;
|
||||
}
|
||||
|
||||
void copy_end(void)
|
||||
{
|
||||
assert(copy_struct.copy_in_use);
|
||||
copy_struct.copy_in_use = false;
|
||||
}
|
||||
|
||||
Decl **copy_decl_list_macro(Decl **decl_list)
|
||||
{
|
||||
assert(copy_struct.copy_in_use);
|
||||
return copy_decl_list(©_struct, decl_list);
|
||||
}
|
||||
|
||||
Decl **copy_decl_list_single(Decl **decl_list)
|
||||
{
|
||||
copy_begin();
|
||||
Decl **result = copy_decl_list_macro(decl_list);
|
||||
copy_end();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -582,8 +706,9 @@ TypeInfo *copy_type_info(CopyStruct *c, TypeInfo *source)
|
||||
case TYPE_INFO_CT_IDENTIFIER:
|
||||
case TYPE_INFO_IDENTIFIER:
|
||||
return copy;
|
||||
case TYPE_INFO_TYPEFROM:
|
||||
case TYPE_INFO_EVALTYPE:
|
||||
case TYPE_INFO_EXPRESSION:
|
||||
case TYPE_INFO_TYPEOF:
|
||||
case TYPE_INFO_VATYPE:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
copy->unresolved_type_expr = copy_expr(c, source->unresolved_type_expr);
|
||||
@@ -596,6 +721,8 @@ TypeInfo *copy_type_info(CopyStruct *c, TypeInfo *source)
|
||||
return copy;
|
||||
case TYPE_INFO_INFERRED_ARRAY:
|
||||
case TYPE_INFO_SUBARRAY:
|
||||
case TYPE_INFO_INFERRED_VECTOR:
|
||||
case TYPE_INFO_SCALED_VECTOR:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
copy->array.base = copy_type_info(c, source->array.base);
|
||||
return copy;
|
||||
@@ -657,6 +784,12 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
|
||||
{
|
||||
case DECL_POISONED:
|
||||
break;
|
||||
case DECL_CT_INCLUDE:
|
||||
break;
|
||||
case DECL_INITIALIZE:
|
||||
case DECL_FINALIZE:
|
||||
MACRO_COPY_ASTID(copy->xxlizer.init);
|
||||
break;
|
||||
case DECL_BODYPARAM:
|
||||
MACRO_COPY_DECL_LIST(copy->body_params);
|
||||
break;
|
||||
@@ -733,6 +866,9 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
|
||||
MACRO_COPY_DECL(decl->ct_if_decl.elif);
|
||||
MACRO_COPY_DECL_LIST(decl->ct_if_decl.then);
|
||||
break;
|
||||
case DECL_CT_ECHO:
|
||||
MACRO_COPY_AST(decl->ct_echo_decl);
|
||||
break;
|
||||
case DECL_CT_ASSERT:
|
||||
MACRO_COPY_AST(decl->ct_assert_decl);
|
||||
break;
|
||||
@@ -770,7 +906,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
|
||||
{
|
||||
case DEFINE_TYPE_GENERIC:
|
||||
case DEFINE_IDENT_GENERIC:
|
||||
MACRO_COPY_TYPE_LIST(decl->define_decl.generic_params);
|
||||
MACRO_COPY_EXPR_LIST(decl->define_decl.generic_params);
|
||||
break;
|
||||
case DEFINE_IDENT_ALIAS:
|
||||
break;
|
||||
|
||||
@@ -53,10 +53,11 @@ typedef enum
|
||||
AST_COMPOUND_STMT,
|
||||
AST_CONTINUE_STMT,
|
||||
AST_CT_ASSERT,
|
||||
AST_CT_IF_STMT,
|
||||
AST_CT_ECHO_STMT,
|
||||
AST_CT_ELSE_STMT,
|
||||
AST_CT_FOR_STMT,
|
||||
AST_CT_FOREACH_STMT,
|
||||
AST_CT_FOR_STMT,
|
||||
AST_CT_IF_STMT,
|
||||
AST_CT_SWITCH_STMT,
|
||||
AST_DECLARE_STMT,
|
||||
AST_DEFAULT_STMT,
|
||||
@@ -94,6 +95,7 @@ typedef enum
|
||||
CAST_PTRBOOL,
|
||||
CAST_BOOLINT,
|
||||
CAST_BOOLFP,
|
||||
CAST_BOOLVECINT,
|
||||
CAST_BOOLBOOL,
|
||||
CAST_FPBOOL,
|
||||
CAST_INTBOOL,
|
||||
@@ -111,6 +113,7 @@ typedef enum
|
||||
CAST_ENUMLOW,
|
||||
CAST_APTSA,
|
||||
CAST_SAPTR,
|
||||
CAST_SASA,
|
||||
CAST_SABOOL,
|
||||
CAST_STST,
|
||||
CAST_PTRANY,
|
||||
@@ -118,6 +121,18 @@ typedef enum
|
||||
CAST_VECARR,
|
||||
} CastKind;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CAST_OPTION_NONE = 0x00,
|
||||
CAST_OPTION_SIMPLE_EXPR = 0x01,
|
||||
|
||||
} CastOption;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CAST_TYPE_NO_ERROR_REPORT = 0x08,
|
||||
CAST_TYPE_NO_OPTIONAL = 0x02,
|
||||
} CastType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@@ -130,6 +145,7 @@ typedef enum
|
||||
DECL_CT_IF,
|
||||
DECL_CT_SWITCH,
|
||||
DECL_CT_ASSERT,
|
||||
DECL_CT_ECHO,
|
||||
DECL_DEFINE,
|
||||
DECL_DISTINCT,
|
||||
DECL_ENUM,
|
||||
@@ -137,8 +153,11 @@ typedef enum
|
||||
DECL_FAULT,
|
||||
DECL_FAULTVALUE,
|
||||
DECL_FUNC,
|
||||
DECL_INITIALIZE,
|
||||
DECL_FINALIZE,
|
||||
DECL_GENERIC,
|
||||
DECL_IMPORT,
|
||||
DECL_CT_INCLUDE,
|
||||
DECL_LABEL,
|
||||
DECL_MACRO,
|
||||
DECL_STRUCT,
|
||||
@@ -152,7 +171,8 @@ typedef enum
|
||||
#define NON_TYPE_DECLS DECL_IMPORT: case DECL_MACRO: \
|
||||
case DECL_DECLARRAY: case DECL_CT_IF: case DECL_CT_ELSE: case DECL_CT_ELIF: \
|
||||
case DECL_CT_SWITCH: case DECL_CT_CASE: case DECL_ATTRIBUTE: case DECL_LABEL: \
|
||||
case DECL_DEFINE: case DECL_CT_ASSERT: case DECL_GENERIC
|
||||
case DECL_DEFINE: case DECL_CT_ASSERT: case DECL_GENERIC: case DECL_INITIALIZE: \
|
||||
case DECL_FINALIZE: case DECL_CT_ECHO: case DECL_CT_INCLUDE
|
||||
|
||||
#define NON_RUNTIME_EXPR EXPR_DESIGNATOR: case EXPR_POISONED: \
|
||||
case EXPR_TYPEINFO: case EXPR_CT_IDENT: case EXPR_HASH_IDENT: \
|
||||
@@ -186,7 +206,7 @@ typedef enum
|
||||
INTROSPECT_TYPE_UNION = 11,
|
||||
INTROSPECT_TYPE_BITSTRUCT = 12,
|
||||
INTROSPECT_TYPE_FUNC = 13,
|
||||
INTROSPECT_TYPE_FAILABLE = 14,
|
||||
INTROSPECT_TYPE_OPTIONAL = 14,
|
||||
INTROSPECT_TYPE_ARRAY = 15,
|
||||
INTROSPECT_TYPE_SUBARRAY = 16,
|
||||
INTROSPECT_TYPE_VECTOR = 17,
|
||||
@@ -199,61 +219,65 @@ typedef enum
|
||||
{
|
||||
EXPR_POISONED,
|
||||
EXPR_ACCESS,
|
||||
EXPR_ARGV_TO_SUBARRAY,
|
||||
EXPR_ASM,
|
||||
EXPR_BINARY,
|
||||
EXPR_BITACCESS,
|
||||
EXPR_BITASSIGN,
|
||||
EXPR_BINARY,
|
||||
EXPR_BUILTIN,
|
||||
EXPR_COMPILER_CONST,
|
||||
EXPR_MACRO_BODY_EXPANSION,
|
||||
EXPR_BUILTIN_ACCESS,
|
||||
EXPR_CALL,
|
||||
EXPR_CAST,
|
||||
EXPR_CATCH,
|
||||
EXPR_CATCH_UNWRAP,
|
||||
EXPR_COMPILER_CONST,
|
||||
EXPR_COMPOUND_LITERAL,
|
||||
EXPR_CONST,
|
||||
EXPR_CT_CALL,
|
||||
EXPR_CT_CONV,
|
||||
EXPR_CT_IDENT,
|
||||
EXPR_CT_EVAL,
|
||||
EXPR_CT_ARG,
|
||||
EXPR_COND,
|
||||
EXPR_CONST,
|
||||
EXPR_CT_ARG,
|
||||
EXPR_CT_CALL,
|
||||
EXPR_CT_CHECKS,
|
||||
EXPR_CT_EVAL,
|
||||
EXPR_CT_IDENT,
|
||||
EXPR_DECL,
|
||||
EXPR_DESIGNATOR,
|
||||
EXPR_EXPR_BLOCK,
|
||||
EXPR_EXPRESSION_LIST,
|
||||
EXPR_FAILABLE,
|
||||
EXPR_GROUP,
|
||||
EXPR_RETHROW,
|
||||
EXPR_FORCE_UNWRAP,
|
||||
EXPR_HASH_IDENT,
|
||||
EXPR_MACRO_BLOCK,
|
||||
EXPR_IDENTIFIER,
|
||||
EXPR_RETVAL,
|
||||
EXPR_FLATPATH,
|
||||
EXPR_INITIALIZER_LIST,
|
||||
EXPR_DESIGNATED_INITIALIZER_LIST,
|
||||
EXPR_DESIGNATOR,
|
||||
EXPR_EXPRESSION_LIST,
|
||||
EXPR_EXPR_BLOCK,
|
||||
EXPR_OPTIONAL,
|
||||
EXPR_FLATPATH,
|
||||
EXPR_FORCE_UNWRAP,
|
||||
EXPR_GROUP,
|
||||
EXPR_HASH_IDENT,
|
||||
EXPR_IDENTIFIER,
|
||||
EXPR_INITIALIZER_LIST,
|
||||
EXPR_MACRO_BLOCK,
|
||||
EXPR_MACRO_BODY_EXPANSION,
|
||||
EXPR_NOP,
|
||||
EXPR_OPERATOR_CHARS,
|
||||
EXPR_POINTER_OFFSET,
|
||||
EXPR_POST_UNARY,
|
||||
EXPR_RETHROW,
|
||||
EXPR_RETVAL,
|
||||
EXPR_SLICE,
|
||||
EXPR_SLICE_ASSIGN,
|
||||
EXPR_SLICE_COPY,
|
||||
EXPR_STRINGIFY,
|
||||
EXPR_SUBSCRIPT,
|
||||
EXPR_SUBSCRIPT_ADDR,
|
||||
EXPR_POINTER_OFFSET,
|
||||
EXPR_STRINGIFY,
|
||||
EXPR_ARGV_TO_SUBARRAY,
|
||||
EXPR_SUBSCRIPT_ASSIGN,
|
||||
EXPR_TERNARY,
|
||||
EXPR_TEST_HOOK,
|
||||
EXPR_TRY,
|
||||
EXPR_TRY_UNWRAP,
|
||||
EXPR_TRY_UNWRAP_CHAIN,
|
||||
EXPR_TYPEID,
|
||||
EXPR_TYPEID_INFO,
|
||||
EXPR_TYPEINFO,
|
||||
EXPR_UNARY,
|
||||
EXPR_VARIANT,
|
||||
EXPR_VARIANTSWITCH,
|
||||
EXPR_VASPLAT,
|
||||
EXPR_NOP,
|
||||
EXPR_TYPEID_INFO,
|
||||
EXPR_VARIANT,
|
||||
EXPR_BUILTIN_ACCESS,
|
||||
EXPR_ASM,
|
||||
} ExprKind;
|
||||
|
||||
typedef enum
|
||||
@@ -273,8 +297,6 @@ typedef enum
|
||||
ASM_REG_FLOAT,
|
||||
ASM_REG_IVEC,
|
||||
ASM_REF_FVEC,
|
||||
ASM_REF_SSE,
|
||||
ASM_REF_MMX,
|
||||
} AsmRegisterType;
|
||||
|
||||
typedef enum
|
||||
@@ -284,7 +306,9 @@ typedef enum
|
||||
ARG_BITS_32 = 1 << 2,
|
||||
ARG_BITS_64 = 1 << 3,
|
||||
ARG_BITS_128 = 1 << 4,
|
||||
ARG_BITS_80 = 1 << 5,
|
||||
ARG_BITS_256 = 1 << 5,
|
||||
ARG_BITS_512 = 1 << 6,
|
||||
ARG_BITS_80 = 1 << 7,
|
||||
} AsmArgBits;
|
||||
|
||||
typedef enum
|
||||
@@ -309,7 +333,9 @@ typedef enum
|
||||
CONST_STRING,
|
||||
CONST_POINTER,
|
||||
CONST_TYPEID,
|
||||
CONST_LIST,
|
||||
CONST_INITIALIZER,
|
||||
CONST_UNTYPED_LIST,
|
||||
CONST_MEMBER,
|
||||
} ConstKind;
|
||||
|
||||
typedef enum
|
||||
@@ -342,6 +368,7 @@ typedef enum
|
||||
typedef enum
|
||||
{
|
||||
SCOPE_NONE = 0,
|
||||
SCOPE_CHECKS = 1 << 0,
|
||||
SCOPE_EXPR_BLOCK = 1 << 5,
|
||||
SCOPE_MACRO = 1 << 6,
|
||||
} ScopeFlags;
|
||||
@@ -358,12 +385,15 @@ typedef enum
|
||||
TYPE_INFO_POISON,
|
||||
TYPE_INFO_IDENTIFIER,
|
||||
TYPE_INFO_CT_IDENTIFIER,
|
||||
TYPE_INFO_EXPRESSION,
|
||||
TYPE_INFO_TYPEOF,
|
||||
TYPE_INFO_VATYPE,
|
||||
TYPE_INFO_EVALTYPE,
|
||||
TYPE_INFO_TYPEFROM,
|
||||
TYPE_INFO_ARRAY,
|
||||
TYPE_INFO_VECTOR,
|
||||
TYPE_INFO_INFERRED_ARRAY,
|
||||
TYPE_INFO_INFERRED_VECTOR,
|
||||
TYPE_INFO_SCALED_VECTOR,
|
||||
TYPE_INFO_SUBARRAY,
|
||||
TYPE_INFO_POINTER,
|
||||
} TypeInfoKind;
|
||||
@@ -450,17 +480,17 @@ typedef enum
|
||||
TOKEN_ICHAR,
|
||||
TOKEN_INT,
|
||||
TOKEN_IPTR,
|
||||
TOKEN_IPTRDIFF,
|
||||
TOKEN_ISIZE,
|
||||
TOKEN_ISZ,
|
||||
TOKEN_LONG,
|
||||
TOKEN_SHORT,
|
||||
TOKEN_UINT128,
|
||||
TOKEN_UINT,
|
||||
TOKEN_ULONG,
|
||||
TOKEN_UPTR,
|
||||
TOKEN_UPTRDIFF,
|
||||
TOKEN_USHORT,
|
||||
TOKEN_USIZE,
|
||||
TOKEN_USZ,
|
||||
TOKEN_FLOAT128,
|
||||
TOKEN_VARIANT,
|
||||
TOKEN_ANYERR,
|
||||
@@ -540,29 +570,31 @@ typedef enum
|
||||
TOKEN_CT_ALIGNOF, // $alignof
|
||||
TOKEN_CT_ASSERT, // $assert
|
||||
TOKEN_CT_CASE, // $case
|
||||
TOKEN_CT_CHECKS, // $checks
|
||||
TOKEN_CT_DEFAULT, // $default
|
||||
TOKEN_CT_DEFINED, // $defined
|
||||
TOKEN_CT_FOR, // $for
|
||||
TOKEN_CT_FOREACH, // $foreach
|
||||
TOKEN_CT_ECHO, // $echo
|
||||
TOKEN_CT_ELIF, // $elif
|
||||
TOKEN_CT_ELSE, // $else
|
||||
TOKEN_CT_EVAL, // $eval
|
||||
TOKEN_CT_EVALTYPE, // $evaltype
|
||||
TOKEN_CT_ENDIF, // $endif
|
||||
TOKEN_CT_ENDSWITCH, // $endswitch
|
||||
TOKEN_CT_ENDFOR, // $endfor
|
||||
TOKEN_CT_ENDFOREACH, // $endforeach
|
||||
TOKEN_CT_ENDIF, // $endif
|
||||
TOKEN_CT_ENDSWITCH, // $endswitch
|
||||
TOKEN_CT_EVAL, // $eval
|
||||
TOKEN_CT_EVALTYPE, // $evaltype
|
||||
TOKEN_CT_EXTNAMEOF, // $extnameof
|
||||
TOKEN_CT_FOR, // $for
|
||||
TOKEN_CT_FOREACH, // $foreach
|
||||
TOKEN_CT_IF, // $if
|
||||
TOKEN_CT_INCLUDE, // $include
|
||||
TOKEN_CT_NAMEOF, // $nameof
|
||||
TOKEN_CT_OFFSETOF, // $offsetof
|
||||
TOKEN_CT_QNAMEOF, // $qnameof
|
||||
TOKEN_CT_SIZEOF, // $sizeof
|
||||
TOKEN_CT_STRINGIFY, // $stringify
|
||||
TOKEN_CT_SWITCH, // $switch
|
||||
TOKEN_CT_TYPEFROM, // $typefrom
|
||||
TOKEN_CT_TYPEOF, // $typeof
|
||||
TOKEN_CT_CONVERTIBLE, // $convertible
|
||||
TOKEN_CT_CASTABLE, // $castable
|
||||
TOKEN_CT_VACOUNT, // $vacount
|
||||
TOKEN_CT_VATYPE, // $vatype
|
||||
TOKEN_CT_VACONST, // $vaconst,
|
||||
@@ -582,14 +614,14 @@ typedef enum
|
||||
#define NON_VOID_TYPE_TOKENS \
|
||||
TOKEN_BOOL: case TOKEN_CHAR: case TOKEN_DOUBLE: case TOKEN_FLOAT: \
|
||||
case TOKEN_FLOAT16: case TOKEN_INT128: case TOKEN_ICHAR: case TOKEN_INT: \
|
||||
case TOKEN_IPTR: case TOKEN_IPTRDIFF: case TOKEN_ISIZE: case TOKEN_LONG: \
|
||||
case TOKEN_IPTR: case TOKEN_ISIZE: case TOKEN_LONG: \
|
||||
case TOKEN_SHORT: case TOKEN_UINT128: case TOKEN_UINT: case TOKEN_ULONG: \
|
||||
case TOKEN_UPTR: case TOKEN_UPTRDIFF: case TOKEN_USHORT: case TOKEN_USIZE: \
|
||||
case TOKEN_FLOAT128: case TOKEN_TYPEID: case TOKEN_ANYERR: case TOKEN_VARIANT
|
||||
case TOKEN_UPTR: case TOKEN_USHORT: case TOKEN_USIZE:\
|
||||
case TOKEN_USZ: case TOKEN_ISZ: case TOKEN_FLOAT128: case TOKEN_TYPEID: case TOKEN_ANYERR: case TOKEN_VARIANT
|
||||
#define TYPE_TOKENS NON_VOID_TYPE_TOKENS: case TOKEN_VOID
|
||||
#define TYPELIKE_TOKENS TYPE_TOKENS: case TOKEN_TYPE_IDENT: \
|
||||
case TOKEN_CT_TYPE_IDENT: case TOKEN_CT_TYPEOF: case TOKEN_CT_EVALTYPE: \
|
||||
case TOKEN_CT_VATYPE
|
||||
#define CT_TYPE_TOKENS TOKEN_CT_TYPE_IDENT: case TOKEN_CT_TYPEOF: case TOKEN_CT_EVALTYPE: \
|
||||
case TOKEN_CT_VATYPE: case TOKEN_CT_TYPEFROM
|
||||
#define TYPELIKE_TOKENS TYPE_TOKENS: case TOKEN_TYPE_IDENT: case CT_TYPE_TOKENS
|
||||
|
||||
// Note that ordering matters here. If ordering is changed,
|
||||
// So must type_find_max_type and friends.
|
||||
@@ -641,14 +673,18 @@ typedef enum
|
||||
TYPE_INFERRED_ARRAY,
|
||||
TYPE_FLEXIBLE_ARRAY,
|
||||
TYPE_UNTYPED_LIST,
|
||||
TYPE_FAILABLE,
|
||||
TYPE_FAILABLE_ANY,
|
||||
TYPE_OPTIONAL,
|
||||
TYPE_OPTIONAL_ANY,
|
||||
TYPE_TYPEINFO,
|
||||
TYPE_MEMBER,
|
||||
TYPE_INFERRED_VECTOR,
|
||||
TYPE_SCALED_VECTOR,
|
||||
TYPE_VECTOR,
|
||||
TYPE_LAST = TYPE_ANY
|
||||
} TypeKind;
|
||||
|
||||
#define CT_TYPES TYPE_TYPEINFO: case TYPE_INFERRED_ARRAY: case TYPE_UNTYPED_LIST: case TYPE_POISONED
|
||||
#define CT_TYPES TYPE_TYPEINFO: case TYPE_INFERRED_ARRAY: case TYPE_INFERRED_VECTOR: case TYPE_UNTYPED_LIST: \
|
||||
case TYPE_POISONED: case TYPE_MEMBER
|
||||
#define ALL_INTS TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64: case TYPE_I128: \
|
||||
case TYPE_U8: case TYPE_U16: case TYPE_U32: case TYPE_U64: case TYPE_U128
|
||||
#define ALL_SIGNED_INTS TYPE_I8: case TYPE_I16: case TYPE_I32: case TYPE_I64: case TYPE_I128
|
||||
@@ -678,15 +714,17 @@ typedef enum
|
||||
VARDECL_PARAM,
|
||||
VARDECL_MEMBER,
|
||||
VARDECL_BITMEMBER,
|
||||
VARDECL_PARAM_CT,
|
||||
VARDECL_PARAM_CT_TYPE,
|
||||
VARDECL_PARAM_REF,
|
||||
VARDECL_PARAM_EXPR,
|
||||
VARDECL_LOCAL_CT,
|
||||
VARDECL_LOCAL_CT_TYPE,
|
||||
VARDECL_UNWRAPPED,
|
||||
VARDECL_ERASE,
|
||||
VARDECL_REWRAPPED,
|
||||
VARDECL_PARAM_CT,
|
||||
VARDECL_PARAM_CT_TYPE,
|
||||
VARDECL_LOCAL_CT,
|
||||
VARDECL_LOCAL_CT_TYPE,
|
||||
VARDECL_FIRST_CT = VARDECL_PARAM_CT,
|
||||
VARDECL_LAST_CT = VARDECL_LOCAL_CT_TYPE,
|
||||
} VarDeclKind;
|
||||
|
||||
typedef enum
|
||||
@@ -714,6 +752,9 @@ typedef enum
|
||||
ATTR_CALL = 1 << 11,
|
||||
ATTR_BITSTRUCT = 1 << 12,
|
||||
ATTR_MACRO = 1 << 13,
|
||||
ATTR_INITIALIZER = 1 << 14,
|
||||
ATTR_FINALIZER = 1 << 15,
|
||||
ATTR_XXLIZER = ATTR_INITIALIZER | ATTR_FINALIZER
|
||||
} AttributeDomain;
|
||||
|
||||
typedef enum
|
||||
@@ -722,12 +763,6 @@ typedef enum
|
||||
OVERLOAD_ELEMENT_REF,
|
||||
OVERLOAD_ELEMENT_SET,
|
||||
OVERLOAD_LEN,
|
||||
OVERLOAD_ADD,
|
||||
OVERLOAD_SUB,
|
||||
OVERLOAD_MULT,
|
||||
OVERLOAD_DIV,
|
||||
OVERLOAD_REM,
|
||||
OVERLOAD_NEG,
|
||||
} OperatorOverload;
|
||||
|
||||
typedef enum
|
||||
@@ -749,11 +784,13 @@ typedef enum
|
||||
ATTRIBUTE_OPERATOR,
|
||||
ATTRIBUTE_OVERLAP,
|
||||
ATTRIBUTE_PACKED,
|
||||
ATTRIBUTE_PRIORITY,
|
||||
ATTRIBUTE_PURE,
|
||||
ATTRIBUTE_REFLECT,
|
||||
ATTRIBUTE_REGCALL,
|
||||
ATTRIBUTE_SECTION,
|
||||
ATTRIBUTE_STDCALL,
|
||||
ATTRIBUTE_TEST,
|
||||
ATTRIBUTE_UNUSED,
|
||||
ATTRIBUTE_USED,
|
||||
ATTRIBUTE_VECCALL,
|
||||
@@ -784,6 +821,7 @@ typedef enum
|
||||
ANALYSIS_REGISTER_GLOBALS,
|
||||
ANALYSIS_CONDITIONAL_COMPILATION,
|
||||
ANALYSIS_DECLS,
|
||||
ANALYSIS_CT_ECHO,
|
||||
ANALYSIS_CT_ASSERT,
|
||||
ANALYSIS_FUNCTIONS,
|
||||
ANALYSIS_LAST = ANALYSIS_FUNCTIONS
|
||||
@@ -791,33 +829,66 @@ typedef enum
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
||||
BUILTIN_ABS,
|
||||
BUILTIN_BITREVERSE,
|
||||
BUILTIN_BSWAP,
|
||||
BUILTIN_CEIL,
|
||||
BUILTIN_COS,
|
||||
BUILTIN_COPYSIGN,
|
||||
BUILTIN_COS,
|
||||
BUILTIN_CTLZ,
|
||||
BUILTIN_CTTZ,
|
||||
BUILTIN_EXACT_ADD,
|
||||
BUILTIN_EXACT_DIV,
|
||||
BUILTIN_EXACT_MOD,
|
||||
BUILTIN_EXACT_MUL,
|
||||
BUILTIN_EXACT_NEG,
|
||||
BUILTIN_EXACT_SUB,
|
||||
BUILTIN_EXP,
|
||||
BUILTIN_EXP2,
|
||||
BUILTIN_FLOOR,
|
||||
BUILTIN_FMA,
|
||||
BUILTIN_FMULADD,
|
||||
BUILTIN_FRAMEADDRESS,
|
||||
BUILTIN_FSHL,
|
||||
BUILTIN_FSHR,
|
||||
BUILTIN_GET_ROUNDING_MODE,
|
||||
BUILTIN_LOG,
|
||||
BUILTIN_LOG2,
|
||||
BUILTIN_LOG10,
|
||||
BUILTIN_LOG2,
|
||||
BUILTIN_MAX,
|
||||
BUILTIN_MEMCOPY,
|
||||
BUILTIN_MEMCOPY_INLINE,
|
||||
BUILTIN_MEMMOVE,
|
||||
BUILTIN_MEMSET,
|
||||
BUILTIN_MEMSET_INLINE,
|
||||
BUILTIN_MIN,
|
||||
BUILTIN_NEARBYINT,
|
||||
BUILTIN_CTPOP, // should be here for keeping sorting order
|
||||
BUILTIN_OVERFLOW_ADD,
|
||||
BUILTIN_OVERFLOW_MUL,
|
||||
BUILTIN_OVERFLOW_SUB,
|
||||
BUILTIN_POPCOUNT,
|
||||
BUILTIN_POW,
|
||||
BUILTIN_POW_INT,
|
||||
BUILTIN_PREFETCH,
|
||||
BUILTIN_REDUCE_ADD,
|
||||
BUILTIN_REDUCE_AND,
|
||||
BUILTIN_REDUCE_FADD,
|
||||
BUILTIN_REDUCE_FMUL,
|
||||
BUILTIN_REDUCE_MAX,
|
||||
BUILTIN_REDUCE_MIN,
|
||||
BUILTIN_REDUCE_MUL,
|
||||
BUILTIN_REDUCE_OR,
|
||||
BUILTIN_REDUCE_XOR,
|
||||
BUILTIN_REVERSE,
|
||||
BUILTIN_RINT,
|
||||
BUILTIN_ROUND,
|
||||
BUILTIN_ROUNDEVEN,
|
||||
BUILTIN_SAT_ADD,
|
||||
BUILTIN_SAT_SHL,
|
||||
BUILTIN_SAT_SUB,
|
||||
BUILTIN_SET_ROUNDING_MODE,
|
||||
BUILTIN_SHUFFLEVECTOR,
|
||||
BUILTIN_SIN,
|
||||
BUILTIN_SQRT,
|
||||
BUILTIN_STACKTRACE,
|
||||
@@ -826,8 +897,15 @@ typedef enum
|
||||
BUILTIN_TRAP,
|
||||
BUILTIN_TRUNC,
|
||||
BUILTIN_UNREACHABLE,
|
||||
BUILTIN_VECCOMPLT,
|
||||
BUILTIN_VECCOMPLE,
|
||||
BUILTIN_VECCOMPGT,
|
||||
BUILTIN_VECCOMPGE,
|
||||
BUILTIN_VECCOMPEQ,
|
||||
BUILTIN_VECCOMPNE,
|
||||
BUILTIN_VOLATILE_LOAD,
|
||||
BUILTIN_VOLATILE_STORE,
|
||||
|
||||
BUILTIN_NONE,
|
||||
NUMBER_OF_BUILTINS = BUILTIN_NONE,
|
||||
|
||||
@@ -838,6 +916,45 @@ typedef enum
|
||||
BUILTIN_LROUND,
|
||||
} BuiltinFunction;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BUILTIN_DEF_DATE,
|
||||
BUILTIN_DEF_FILE,
|
||||
BUILTIN_DEF_FUNC,
|
||||
BUILTIN_DEF_FUNCTION,
|
||||
BUILTIN_DEF_LINE,
|
||||
BUILTIN_DEF_LINE_RAW,
|
||||
BUILTIN_DEF_MODULE,
|
||||
BUILTIN_DEF_TEST_NAMES,
|
||||
BUILTIN_DEF_TEST_FNS,
|
||||
BUILTIN_DEF_TIME,
|
||||
BUILTIN_DEF_NONE,
|
||||
NUMBER_OF_BUILTIN_DEFINES = BUILTIN_DEF_NONE
|
||||
} BuiltinDefine;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
TYPE_PROPERTY_ALIGNOF,
|
||||
TYPE_PROPERTY_ELEMENTS,
|
||||
TYPE_PROPERTY_EXTNAMEOF,
|
||||
TYPE_PROPERTY_INF,
|
||||
TYPE_PROPERTY_LEN,
|
||||
TYPE_PROPERTY_MAX,
|
||||
TYPE_PROPERTY_MEMBERSOF,
|
||||
TYPE_PROPERTY_MIN,
|
||||
TYPE_PROPERTY_NAN,
|
||||
TYPE_PROPERTY_INNER,
|
||||
TYPE_PROPERTY_KINDOF,
|
||||
TYPE_PROPERTY_NAMES,
|
||||
TYPE_PROPERTY_NAMEOF,
|
||||
TYPE_PROPERTY_PARAMS,
|
||||
TYPE_PROPERTY_QNAMEOF,
|
||||
TYPE_PROPERTY_RETURNS,
|
||||
TYPE_PROPERTY_SIZEOF,
|
||||
TYPE_PROPERTY_VALUES,
|
||||
TYPE_PROPERTY_NONE,
|
||||
NUMBER_OF_TYPE_PROPERTIES = TYPE_PROPERTY_NONE
|
||||
} TypeProperty;
|
||||
typedef enum
|
||||
{
|
||||
ATOMIC_NONE,
|
||||
@@ -871,3 +988,10 @@ typedef enum
|
||||
CONSTANT_EVAL_LOCAL_INIT,
|
||||
CONSTANT_EVAL_CONSTANT_VALUE,
|
||||
} ConstantEvalKind;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
COND_TYPE_UNWRAP_BOOL,
|
||||
COND_TYPE_UNWRAP,
|
||||
COND_TYPE_EVALTYPE_VALUE,
|
||||
} CondType;
|
||||
|
||||
944
src/compiler/expr.c
Normal file
944
src/compiler/expr.c
Normal file
@@ -0,0 +1,944 @@
|
||||
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
static inline bool expr_binary_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind);
|
||||
static inline bool expr_cast_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind);
|
||||
static inline bool expr_list_is_constant_eval(Expr **exprs, ConstantEvalKind eval_kind);
|
||||
static inline bool expr_unary_addr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind);
|
||||
static inline ConstInitializer *initializer_for_index(ConstInitializer *initializer, uint32_t index);
|
||||
|
||||
Expr *expr_negate_expr(Expr *expr)
|
||||
{
|
||||
if (expr->expr_kind == EXPR_UNARY && expr->unary_expr.operator == UNARYOP_NEG)
|
||||
{
|
||||
return expr->inner_expr;
|
||||
}
|
||||
Expr *neg = expr_new_expr(EXPR_UNARY, expr);
|
||||
neg->unary_expr = (ExprUnary) { .operator = UNARYOP_NEG, .expr = expr };
|
||||
return neg;
|
||||
}
|
||||
|
||||
bool expr_in_int_range(Expr *expr, int64_t low, int64_t high)
|
||||
{
|
||||
assert(expr_is_const(expr) && expr->const_expr.const_kind == CONST_INTEGER);
|
||||
Int val = expr->const_expr.ixx;
|
||||
if (!int_fits(val, TYPE_I64)) return false;
|
||||
int64_t value = int_to_i64(val);
|
||||
return value >= low && value <= high;
|
||||
}
|
||||
|
||||
bool expr_is_unwrapped_ident(Expr *expr)
|
||||
{
|
||||
if (expr->expr_kind != EXPR_IDENTIFIER) return false;
|
||||
Decl *decl = expr->identifier_expr.decl;
|
||||
if (decl->decl_kind != DECL_VAR) return false;
|
||||
return decl->var.kind == VARDECL_UNWRAPPED && IS_OPTIONAL(decl->var.alias);
|
||||
}
|
||||
|
||||
bool expr_may_addr(Expr *expr)
|
||||
{
|
||||
if (IS_OPTIONAL(expr)) return false;
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_IDENTIFIER:
|
||||
{
|
||||
Decl *decl = expr->identifier_expr.decl;
|
||||
if (decl->decl_kind != DECL_VAR) return false;
|
||||
decl = decl_raw(decl);
|
||||
switch (decl->var.kind)
|
||||
{
|
||||
case VARDECL_LOCAL_CT:
|
||||
case VARDECL_LOCAL_CT_TYPE:
|
||||
case VARDECL_LOCAL:
|
||||
case VARDECL_GLOBAL:
|
||||
case VARDECL_PARAM:
|
||||
case VARDECL_PARAM_REF:
|
||||
case VARDECL_CONST:
|
||||
return true;
|
||||
case VARDECL_MEMBER:
|
||||
case VARDECL_BITMEMBER:
|
||||
case VARDECL_PARAM_CT:
|
||||
case VARDECL_PARAM_CT_TYPE:
|
||||
case VARDECL_PARAM_EXPR:
|
||||
return false;
|
||||
case VARDECL_UNWRAPPED:
|
||||
case VARDECL_ERASE:
|
||||
case VARDECL_REWRAPPED:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
case EXPR_UNARY:
|
||||
return expr->unary_expr.operator == UNARYOP_DEREF;
|
||||
case EXPR_BITACCESS:
|
||||
case EXPR_ACCESS:
|
||||
return expr_may_addr(expr->access_expr.parent);
|
||||
case EXPR_GROUP:
|
||||
return expr_may_addr(expr->inner_expr);
|
||||
case EXPR_SUBSCRIPT:
|
||||
case EXPR_SLICE:
|
||||
return true;
|
||||
case EXPR_TEST_HOOK:
|
||||
return false;
|
||||
case EXPR_ARGV_TO_SUBARRAY:
|
||||
case EXPR_ASM:
|
||||
case EXPR_BINARY:
|
||||
case EXPR_BITASSIGN:
|
||||
case EXPR_BUILTIN:
|
||||
case EXPR_BUILTIN_ACCESS:
|
||||
case EXPR_CALL:
|
||||
case EXPR_CAST:
|
||||
case EXPR_CATCH:
|
||||
case EXPR_CATCH_UNWRAP:
|
||||
case EXPR_COMPILER_CONST:
|
||||
case EXPR_COMPOUND_LITERAL:
|
||||
case EXPR_COND:
|
||||
case EXPR_CONST:
|
||||
case EXPR_CT_ARG:
|
||||
case EXPR_CT_CALL:
|
||||
case EXPR_CT_CHECKS:
|
||||
case EXPR_CT_EVAL:
|
||||
case EXPR_CT_IDENT:
|
||||
case EXPR_DECL:
|
||||
case EXPR_DESIGNATED_INITIALIZER_LIST:
|
||||
case EXPR_DESIGNATOR:
|
||||
case EXPR_EXPRESSION_LIST:
|
||||
case EXPR_EXPR_BLOCK:
|
||||
case EXPR_OPTIONAL:
|
||||
case EXPR_FLATPATH:
|
||||
case EXPR_FORCE_UNWRAP:
|
||||
case EXPR_HASH_IDENT:
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
case EXPR_MACRO_BLOCK:
|
||||
case EXPR_MACRO_BODY_EXPANSION:
|
||||
case EXPR_NOP:
|
||||
case EXPR_OPERATOR_CHARS:
|
||||
case EXPR_POINTER_OFFSET:
|
||||
case EXPR_POISONED:
|
||||
case EXPR_POST_UNARY:
|
||||
case EXPR_RETHROW:
|
||||
case EXPR_RETVAL:
|
||||
case EXPR_SLICE_ASSIGN:
|
||||
case EXPR_SLICE_COPY:
|
||||
case EXPR_STRINGIFY:
|
||||
case EXPR_SUBSCRIPT_ADDR:
|
||||
case EXPR_SUBSCRIPT_ASSIGN:
|
||||
case EXPR_TERNARY:
|
||||
case EXPR_TRY:
|
||||
case EXPR_TRY_UNWRAP:
|
||||
case EXPR_TRY_UNWRAP_CHAIN:
|
||||
case EXPR_TYPEID:
|
||||
case EXPR_TYPEID_INFO:
|
||||
case EXPR_TYPEINFO:
|
||||
case EXPR_VARIANT:
|
||||
case EXPR_VARIANTSWITCH:
|
||||
case EXPR_VASPLAT:
|
||||
return false;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static inline bool expr_binary_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind)
|
||||
{
|
||||
if (expr->binary_expr.operator >= BINARYOP_ASSIGN) return false;
|
||||
// Pointer add is already handled.
|
||||
if (eval_kind == CONSTANT_EVAL_GLOBAL_INIT) return false;
|
||||
Expr *left = exprptr(expr->binary_expr.left);
|
||||
Expr *right = exprptr(expr->binary_expr.right);
|
||||
if (!expr_is_constant_eval(left, eval_kind)) return false;
|
||||
if (!expr_is_constant_eval(right, eval_kind)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind)
|
||||
{
|
||||
assert(expr->resolve_status == RESOLVE_DONE);
|
||||
RETRY:
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_POINTER_OFFSET:
|
||||
return exprid_is_constant_eval(expr->pointer_offset_expr.ptr, eval_kind) && exprid_is_constant_eval(expr->pointer_offset_expr.offset, eval_kind);
|
||||
case EXPR_RETVAL:
|
||||
return false;
|
||||
case EXPR_BUILTIN:
|
||||
case EXPR_CT_EVAL:
|
||||
case EXPR_VASPLAT:
|
||||
case EXPR_TEST_HOOK:
|
||||
return false;
|
||||
case EXPR_BITACCESS:
|
||||
case EXPR_ACCESS:
|
||||
expr = expr->access_expr.parent;
|
||||
goto RETRY;
|
||||
case EXPR_VARIANTSWITCH:
|
||||
return false;
|
||||
case EXPR_BITASSIGN:
|
||||
return false;
|
||||
case EXPR_BUILTIN_ACCESS:
|
||||
switch (expr->builtin_access_expr.kind)
|
||||
{
|
||||
case ACCESS_ENUMNAME:
|
||||
case ACCESS_FAULTNAME:
|
||||
case ACCESS_LEN:
|
||||
case ACCESS_PTR:
|
||||
break;
|
||||
case ACCESS_TYPEOFANY:
|
||||
if (eval_kind != CONSTANT_EVAL_NO_SIDE_EFFECTS) return false;
|
||||
break;
|
||||
}
|
||||
return exprid_is_constant_eval(expr->builtin_access_expr.inner, eval_kind);
|
||||
case EXPR_VARIANT:
|
||||
return exprid_is_constant_eval(expr->variant_expr.type_id, eval_kind) && exprid_is_constant_eval(expr->variant_expr.ptr, eval_kind);
|
||||
case EXPR_BINARY:
|
||||
return expr_binary_is_constant_eval(expr, eval_kind);
|
||||
case EXPR_CAST:
|
||||
return expr_cast_is_constant_eval(expr, eval_kind);
|
||||
case EXPR_CONST:
|
||||
case EXPR_OPERATOR_CHARS:
|
||||
case EXPR_STRINGIFY:
|
||||
case EXPR_CT_CHECKS:
|
||||
return true;
|
||||
case EXPR_COND:
|
||||
return expr_list_is_constant_eval(expr->cond_expr, eval_kind);
|
||||
case EXPR_DESIGNATOR:
|
||||
expr = expr->designator_expr.value;
|
||||
goto RETRY;
|
||||
case EXPR_EXPR_BLOCK:
|
||||
case EXPR_DECL:
|
||||
case EXPR_CALL:
|
||||
case EXPR_CATCH_UNWRAP:
|
||||
case EXPR_MACRO_BODY_EXPANSION:
|
||||
case EXPR_TRY_UNWRAP:
|
||||
case EXPR_TRY_UNWRAP_CHAIN:
|
||||
case EXPR_POST_UNARY:
|
||||
case EXPR_SLICE_ASSIGN:
|
||||
case EXPR_SLICE_COPY:
|
||||
case EXPR_MACRO_BLOCK:
|
||||
case EXPR_RETHROW:
|
||||
return false;
|
||||
case EXPR_IDENTIFIER:
|
||||
if (expr->identifier_expr.decl->decl_kind != DECL_VAR) return true;
|
||||
if (expr->identifier_expr.decl->var.kind == VARDECL_CONST)
|
||||
{
|
||||
expr = expr->identifier_expr.decl->var.init_expr;
|
||||
goto RETRY;
|
||||
}
|
||||
switch (expr->identifier_expr.decl->var.kind)
|
||||
{
|
||||
case VARDECL_CONST:
|
||||
case VARDECL_PARAM_CT_TYPE:
|
||||
case VARDECL_LOCAL_CT_TYPE:
|
||||
case VARDECL_LOCAL_CT:
|
||||
case VARDECL_PARAM_CT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case EXPR_EXPRESSION_LIST:
|
||||
return expr_list_is_constant_eval(expr->expression_list, eval_kind);
|
||||
case EXPR_TYPEID_INFO:
|
||||
expr = exprptr(expr->typeid_info_expr.parent);
|
||||
goto RETRY;
|
||||
case EXPR_OPTIONAL:
|
||||
case EXPR_GROUP:
|
||||
expr = expr->inner_expr;
|
||||
goto RETRY;
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
return expr_list_is_constant_eval(expr->initializer_list, eval_kind);
|
||||
case EXPR_DESIGNATED_INITIALIZER_LIST:
|
||||
return expr_list_is_constant_eval(expr->designated_init_list, eval_kind);
|
||||
case EXPR_SLICE:
|
||||
return false;
|
||||
/*
|
||||
if (expr->slice_expr.start && !exprid_is_constant_eval(expr->slice_expr.start, eval_kind)) return false;
|
||||
if (expr->slice_expr.end && !exprid_is_constant_eval(expr->slice_expr.end, CONSTANT_EVAL_FOLDABLE)) return false;
|
||||
return exprid_is_constant_eval(expr->slice_expr.expr, eval_kind);*/
|
||||
case EXPR_SUBSCRIPT:
|
||||
if (!exprid_is_constant_eval(expr->subscript_expr.range.start, eval_kind)) return false;
|
||||
expr = exprptr(expr->subscript_expr.expr);
|
||||
goto RETRY;
|
||||
case EXPR_SUBSCRIPT_ADDR:
|
||||
if (!exprid_is_constant_eval(expr->subscript_expr.range.start, eval_kind)) return false;
|
||||
expr = exprptr(expr->subscript_expr.expr);
|
||||
if (expr->expr_kind == EXPR_IDENTIFIER)
|
||||
{
|
||||
Decl *decl = expr->identifier_expr.decl;
|
||||
if (decl->decl_kind == DECL_VAR)
|
||||
{
|
||||
switch (decl->var.kind)
|
||||
{
|
||||
case VARDECL_CONST:
|
||||
case VARDECL_GLOBAL:
|
||||
break;
|
||||
case VARDECL_LOCAL:
|
||||
if (decl->var.is_static) break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
goto RETRY;
|
||||
case EXPR_TERNARY:
|
||||
assert(!exprid_is_constant_eval(expr->ternary_expr.cond, eval_kind));
|
||||
return false;
|
||||
case EXPR_FORCE_UNWRAP:
|
||||
case EXPR_TRY:
|
||||
case EXPR_CATCH:
|
||||
expr = expr->inner_expr;
|
||||
goto RETRY;
|
||||
case EXPR_TYPEID:
|
||||
return eval_kind != CONSTANT_EVAL_CONSTANT_VALUE;
|
||||
case EXPR_UNARY:
|
||||
switch (expr->unary_expr.operator)
|
||||
{
|
||||
case UNARYOP_DEREF:
|
||||
case UNARYOP_ERROR:
|
||||
return false;
|
||||
case UNARYOP_ADDR:
|
||||
return expr_unary_addr_is_constant_eval(expr, eval_kind);
|
||||
case UNARYOP_TADDR:
|
||||
if (eval_kind == CONSTANT_EVAL_CONSTANT_VALUE || eval_kind == CONSTANT_EVAL_LOCAL_INIT) return false;
|
||||
expr = expr->unary_expr.expr;
|
||||
goto RETRY;
|
||||
case UNARYOP_NEG:
|
||||
case UNARYOP_BITNEG:
|
||||
case UNARYOP_NOT:
|
||||
expr = expr->unary_expr.expr;
|
||||
goto RETRY;
|
||||
case UNARYOP_INC:
|
||||
case UNARYOP_DEC:
|
||||
return false;
|
||||
}
|
||||
UNREACHABLE
|
||||
case EXPR_COMPILER_CONST:
|
||||
// Not foldable
|
||||
return false;
|
||||
case EXPR_CT_CALL:
|
||||
case EXPR_TYPEINFO:
|
||||
case EXPR_HASH_IDENT:
|
||||
case EXPR_CT_IDENT:
|
||||
case EXPR_FLATPATH:
|
||||
case EXPR_COMPOUND_LITERAL:
|
||||
case EXPR_POISONED:
|
||||
case EXPR_ARGV_TO_SUBARRAY:
|
||||
case EXPR_CT_ARG:
|
||||
case EXPR_ASM:
|
||||
case EXPR_SUBSCRIPT_ASSIGN:
|
||||
UNREACHABLE
|
||||
case EXPR_NOP:
|
||||
return true;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static inline bool expr_cast_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind)
|
||||
{
|
||||
switch (expr->cast_expr.kind)
|
||||
{
|
||||
case CAST_ERROR:
|
||||
UNREACHABLE
|
||||
case CAST_BSINT:
|
||||
case CAST_BSARRY:
|
||||
return true;
|
||||
case CAST_INTENUM:
|
||||
case CAST_ANYPTR:
|
||||
case CAST_ERBOOL:
|
||||
case CAST_EUBOOL:
|
||||
case CAST_EUER:
|
||||
case CAST_EREU:
|
||||
case CAST_XIERR:
|
||||
case CAST_STRPTR:
|
||||
case CAST_PTRBOOL:
|
||||
case CAST_BOOLINT:
|
||||
case CAST_BOOLFP:
|
||||
case CAST_BOOLBOOL:
|
||||
case CAST_FPBOOL:
|
||||
case CAST_INTBOOL:
|
||||
case CAST_FPFP:
|
||||
case CAST_FPSI:
|
||||
case CAST_FPUI:
|
||||
case CAST_SISI:
|
||||
case CAST_SIUI:
|
||||
case CAST_SIFP:
|
||||
case CAST_UISI:
|
||||
case CAST_UIUI:
|
||||
case CAST_UIFP:
|
||||
case CAST_SABOOL:
|
||||
case CAST_STST:
|
||||
case CAST_VECARR:
|
||||
case CAST_ARRVEC:
|
||||
case CAST_BOOLVECINT:
|
||||
if (eval_kind != CONSTANT_EVAL_NO_SIDE_EFFECTS) return false;
|
||||
return exprid_is_constant_eval(expr->cast_expr.expr, eval_kind);
|
||||
case CAST_XIPTR:
|
||||
case CAST_PTRPTR:
|
||||
case CAST_APTSA:
|
||||
case CAST_SAPTR:
|
||||
case CAST_SASA:
|
||||
case CAST_ENUMLOW:
|
||||
return exprid_is_constant_eval(expr->cast_expr.expr, eval_kind);
|
||||
case CAST_PTRANY:
|
||||
if (eval_kind == CONSTANT_EVAL_LOCAL_INIT || eval_kind == CONSTANT_EVAL_CONSTANT_VALUE) return false;
|
||||
return exprid_is_constant_eval(expr->cast_expr.expr, eval_kind);
|
||||
case CAST_EUINT:
|
||||
case CAST_ERINT:
|
||||
case CAST_PTRXI:
|
||||
if (eval_kind == CONSTANT_EVAL_CONSTANT_VALUE) return false;
|
||||
return exprid_is_constant_eval(expr->cast_expr.expr, eval_kind);
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static inline bool expr_list_is_constant_eval(Expr **exprs, ConstantEvalKind eval_kind)
|
||||
{
|
||||
VECEACH(exprs, i)
|
||||
{
|
||||
if (!expr_is_constant_eval(exprs[i], eval_kind)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool expr_unary_addr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind)
|
||||
{
|
||||
// An address is never a constant value.
|
||||
if (eval_kind == CONSTANT_EVAL_CONSTANT_VALUE) return false;
|
||||
Expr *inner = expr->unary_expr.expr;
|
||||
switch (inner->expr_kind)
|
||||
{
|
||||
case EXPR_CONST:
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
case EXPR_DESIGNATED_INITIALIZER_LIST:
|
||||
// We can't create temporaries as const locally or making them into compile time constants.
|
||||
if (eval_kind == CONSTANT_EVAL_LOCAL_INIT) return false;
|
||||
return expr_is_constant_eval(inner, eval_kind);
|
||||
case EXPR_IDENTIFIER:
|
||||
{
|
||||
// The address of an identifier is side effect free.
|
||||
if (eval_kind == CONSTANT_EVAL_NO_SIDE_EFFECTS) return true;
|
||||
Decl *decl = inner->identifier_expr.decl;
|
||||
if (decl->decl_kind == DECL_FUNC) return true;
|
||||
if (decl->decl_kind != DECL_VAR) return false;
|
||||
assert(eval_kind == CONSTANT_EVAL_LOCAL_INIT || eval_kind == CONSTANT_EVAL_GLOBAL_INIT);
|
||||
switch (decl->var.kind)
|
||||
{
|
||||
case VARDECL_CONST:
|
||||
case VARDECL_GLOBAL:
|
||||
// Fine for both local and global init.
|
||||
return true;
|
||||
case VARDECL_LOCAL:
|
||||
// Getting the address of a local can never be constant init unless it is static.
|
||||
return decl->var.is_static;
|
||||
case VARDECL_PARAM:
|
||||
case VARDECL_MEMBER:
|
||||
case VARDECL_BITMEMBER:
|
||||
case VARDECL_PARAM_CT:
|
||||
case VARDECL_PARAM_CT_TYPE:
|
||||
case VARDECL_PARAM_REF:
|
||||
case VARDECL_PARAM_EXPR:
|
||||
case VARDECL_LOCAL_CT:
|
||||
case VARDECL_LOCAL_CT_TYPE:
|
||||
case VARDECL_UNWRAPPED:
|
||||
case VARDECL_ERASE:
|
||||
case VARDECL_REWRAPPED:
|
||||
// None of these are constant.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void expr_insert_addr(Expr *original)
|
||||
{
|
||||
assert(original->resolve_status == RESOLVE_DONE);
|
||||
if (original->expr_kind == EXPR_UNARY && original->unary_expr.operator == UNARYOP_DEREF)
|
||||
{
|
||||
*original = *original->unary_expr.expr;
|
||||
return;
|
||||
}
|
||||
Expr *inner = expr_copy(original);
|
||||
original->expr_kind = EXPR_UNARY;
|
||||
Type *inner_type = inner->type;
|
||||
bool optional = type_is_optional(inner->type);
|
||||
original->type = type_add_optional(type_get_ptr(type_no_optional(inner_type)), optional);
|
||||
original->unary_expr.operator = UNARYOP_ADDR;
|
||||
original->unary_expr.expr = inner;
|
||||
}
|
||||
|
||||
Expr *expr_generate_decl(Decl *decl, Expr *assign)
|
||||
{
|
||||
assert(decl->decl_kind == DECL_VAR);
|
||||
assert(decl->var.init_expr == NULL);
|
||||
Expr *expr_decl = expr_new(EXPR_DECL, decl->span);
|
||||
expr_decl->decl_expr = decl;
|
||||
if (!assign) decl->var.no_init = true;
|
||||
decl->var.init_expr = assign;
|
||||
return expr_decl;
|
||||
}
|
||||
|
||||
bool expr_may_splat_as_vararg(Expr *expr, Type *variadic_base_type)
|
||||
{
|
||||
Type *base_type = variadic_base_type->canonical;
|
||||
Type *canonical = expr->type->canonical;
|
||||
switch (canonical->type_kind)
|
||||
{
|
||||
case TYPE_ARRAY:
|
||||
case TYPE_SUBARRAY:
|
||||
return canonical->array.base == base_type;
|
||||
case TYPE_POINTER:
|
||||
if (canonical->pointer->type_kind == TYPE_ARRAY) return canonical->pointer->array.base == base_type;
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool expr_is_compile_time(Expr *expr)
|
||||
{
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_CONST:
|
||||
return true;
|
||||
case EXPR_MACRO_BLOCK:
|
||||
{
|
||||
AstId current = expr->macro_block.first_stmt;
|
||||
while (current)
|
||||
{
|
||||
if (!ast_is_compile_time(ast_next(¤t))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static inline ConstInitializer *initializer_for_index(ConstInitializer *initializer, uint32_t index)
|
||||
{
|
||||
switch (initializer->kind)
|
||||
{
|
||||
case CONST_INIT_ZERO:
|
||||
case CONST_INIT_STRUCT:
|
||||
case CONST_INIT_UNION:
|
||||
case CONST_INIT_VALUE:
|
||||
return initializer;
|
||||
case CONST_INIT_ARRAY_FULL:
|
||||
return initializer->init_array_full[index];
|
||||
case CONST_INIT_ARRAY:
|
||||
{
|
||||
ConstInitializer **sub_values = initializer->init_array.elements;
|
||||
VECEACH(sub_values, i)
|
||||
{
|
||||
ConstInitializer *init = sub_values[i];
|
||||
assert(init->kind == CONST_INIT_ARRAY_VALUE);
|
||||
if (init->init_array_value.index == index) return init->init_array_value.element;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
case CONST_INIT_ARRAY_VALUE:
|
||||
UNREACHABLE
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
void expr_rewrite_to_const_zero(Expr *expr, Type *type)
|
||||
{
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr->const_expr.narrowable = true;
|
||||
switch (type->canonical->type_kind)
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
case TYPE_VOID:
|
||||
case TYPE_INFERRED_VECTOR:
|
||||
UNREACHABLE
|
||||
case ALL_INTS:
|
||||
expr_rewrite_const_int(expr, type, 0, true);
|
||||
return;
|
||||
case ALL_FLOATS:
|
||||
expr_rewrite_const_float(expr, type, 0);
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
expr_rewrite_const_bool(expr, type, false);
|
||||
return;
|
||||
case TYPE_POINTER:
|
||||
case TYPE_FAULTTYPE:
|
||||
case TYPE_ANY:
|
||||
case TYPE_ANYERR:
|
||||
case TYPE_TYPEID:
|
||||
expr_rewrite_const_null(expr, type);
|
||||
return;
|
||||
case TYPE_ENUM:
|
||||
expr->const_expr.const_kind = CONST_ENUM;
|
||||
expr->const_expr.enum_err_val = type->decl->enums.values[0];
|
||||
break;
|
||||
case TYPE_FUNC:
|
||||
case TYPE_TYPEDEF:
|
||||
case TYPE_OPTIONAL_ANY:
|
||||
case TYPE_OPTIONAL:
|
||||
case TYPE_TYPEINFO:
|
||||
case TYPE_MEMBER:
|
||||
UNREACHABLE
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
case TYPE_BITSTRUCT:
|
||||
case TYPE_ARRAY:
|
||||
case TYPE_SUBARRAY:
|
||||
case TYPE_INFERRED_ARRAY:
|
||||
case TYPE_FLEXIBLE_ARRAY:
|
||||
case TYPE_UNTYPED_LIST:
|
||||
case TYPE_SCALED_VECTOR:
|
||||
case TYPE_VECTOR:
|
||||
{
|
||||
ConstInitializer *init = CALLOCS(ConstInitializer);
|
||||
init->kind = CONST_INIT_ZERO;
|
||||
init->type = type;
|
||||
expr_rewrite_const_initializer(expr, type, init);
|
||||
return;
|
||||
}
|
||||
case TYPE_DISTINCT:
|
||||
expr_rewrite_to_const_zero(expr, type->decl->distinct_decl.base_type);
|
||||
break;
|
||||
}
|
||||
expr->type = type;
|
||||
}
|
||||
|
||||
bool expr_rewrite_to_const_initializer_index(Type *list_type, ConstInitializer *list, Expr *result, unsigned index)
|
||||
{
|
||||
ConstInitializer *initializer = initializer_for_index(list, index);
|
||||
ConstInitType kind = initializer ? initializer->kind : CONST_INIT_ZERO;
|
||||
switch (kind)
|
||||
{
|
||||
case CONST_INIT_ZERO:
|
||||
expr_rewrite_to_const_zero(result, type_get_indexed_type(list_type));
|
||||
return true;
|
||||
case CONST_INIT_STRUCT:
|
||||
case CONST_INIT_UNION:
|
||||
case CONST_INIT_ARRAY:
|
||||
case CONST_INIT_ARRAY_FULL:
|
||||
case CONST_INIT_ARRAY_VALUE:
|
||||
return false;
|
||||
case CONST_INIT_VALUE:
|
||||
expr_replace(result, initializer->init_value);
|
||||
return true;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
// Determine if the expression has side effects
|
||||
// Note! This is not the same as it being const.
|
||||
bool expr_is_pure(Expr *expr)
|
||||
{
|
||||
if (!expr) return true;
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_BUILTIN:
|
||||
case EXPR_TEST_HOOK:
|
||||
return false;
|
||||
case EXPR_BUILTIN_ACCESS:
|
||||
return exprid_is_pure(expr->builtin_access_expr.inner);
|
||||
case EXPR_VARIANT:
|
||||
return exprid_is_pure(expr->variant_expr.type_id) && exprid_is_pure(expr->variant_expr.ptr);
|
||||
case EXPR_POINTER_OFFSET:
|
||||
return exprid_is_pure(expr->pointer_offset_expr.ptr) && exprid_is_pure(expr->pointer_offset_expr.offset);
|
||||
case EXPR_COMPILER_CONST:
|
||||
case EXPR_CONST:
|
||||
case EXPR_IDENTIFIER:
|
||||
case EXPR_NOP:
|
||||
case EXPR_STRINGIFY:
|
||||
case EXPR_RETVAL:
|
||||
case EXPR_TYPEINFO:
|
||||
case EXPR_CT_EVAL:
|
||||
case EXPR_CT_IDENT:
|
||||
case EXPR_CT_CALL:
|
||||
case EXPR_TYPEID:
|
||||
case EXPR_CT_ARG:
|
||||
case EXPR_OPERATOR_CHARS:
|
||||
case EXPR_CT_CHECKS:
|
||||
return true;
|
||||
case EXPR_VASPLAT:
|
||||
return true;
|
||||
case EXPR_ARGV_TO_SUBARRAY:
|
||||
case EXPR_BITASSIGN:
|
||||
return false;
|
||||
case EXPR_VARIANTSWITCH:
|
||||
return false;
|
||||
case EXPR_BINARY:
|
||||
// Anything with assignment is impure, otherwise true if sub expr are pure.
|
||||
if (expr->binary_expr.operator >= BINARYOP_ASSIGN) return false;
|
||||
return exprid_is_pure(expr->binary_expr.right) && exprid_is_pure(expr->binary_expr.left);
|
||||
case EXPR_UNARY:
|
||||
switch (expr->unary_expr.operator)
|
||||
{
|
||||
case UNARYOP_INC:
|
||||
case UNARYOP_DEC:
|
||||
case UNARYOP_TADDR:
|
||||
// ++ -- &&1
|
||||
return false;
|
||||
case UNARYOP_ERROR:
|
||||
case UNARYOP_DEREF:
|
||||
case UNARYOP_ADDR:
|
||||
case UNARYOP_NEG:
|
||||
case UNARYOP_BITNEG:
|
||||
case UNARYOP_NOT:
|
||||
return expr_is_pure(expr->unary_expr.expr);
|
||||
}
|
||||
UNREACHABLE
|
||||
case EXPR_BITACCESS:
|
||||
case EXPR_ACCESS:
|
||||
// All access is pure if the parent is pure.
|
||||
return expr_is_pure(expr->access_expr.parent);
|
||||
case EXPR_POISONED:
|
||||
UNREACHABLE
|
||||
case EXPR_MACRO_BODY_EXPANSION:
|
||||
case EXPR_CALL:
|
||||
case EXPR_CATCH_UNWRAP:
|
||||
case EXPR_COMPOUND_LITERAL:
|
||||
case EXPR_COND:
|
||||
case EXPR_DESIGNATOR:
|
||||
case EXPR_DECL:
|
||||
case EXPR_EXPR_BLOCK:
|
||||
case EXPR_OPTIONAL:
|
||||
case EXPR_RETHROW:
|
||||
case EXPR_HASH_IDENT:
|
||||
case EXPR_MACRO_BLOCK:
|
||||
case EXPR_FLATPATH:
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
case EXPR_DESIGNATED_INITIALIZER_LIST:
|
||||
case EXPR_POST_UNARY:
|
||||
case EXPR_SLICE_ASSIGN:
|
||||
case EXPR_SLICE_COPY:
|
||||
case EXPR_TRY_UNWRAP:
|
||||
case EXPR_TRY_UNWRAP_CHAIN:
|
||||
case EXPR_FORCE_UNWRAP:
|
||||
case EXPR_SUBSCRIPT_ASSIGN:
|
||||
return false;
|
||||
case EXPR_CAST:
|
||||
return exprid_is_pure(expr->cast_expr.expr);
|
||||
case EXPR_EXPRESSION_LIST:
|
||||
VECEACH(expr->expression_list, i)
|
||||
{
|
||||
if (!expr_is_pure(expr->expression_list[i])) return false;
|
||||
}
|
||||
return true;
|
||||
case EXPR_TYPEID_INFO:
|
||||
return exprid_is_pure(expr->typeid_info_expr.parent);
|
||||
case EXPR_SLICE:
|
||||
return exprid_is_pure(expr->subscript_expr.expr)
|
||||
&& exprid_is_pure(expr->subscript_expr.range.start)
|
||||
&& exprid_is_pure(expr->subscript_expr.range.end);
|
||||
case EXPR_SUBSCRIPT:
|
||||
case EXPR_SUBSCRIPT_ADDR:
|
||||
return exprid_is_pure(expr->subscript_expr.expr)
|
||||
&& exprid_is_pure(expr->subscript_expr.range.start);
|
||||
case EXPR_TERNARY:
|
||||
return exprid_is_pure(expr->ternary_expr.cond)
|
||||
&& exprid_is_pure(expr->ternary_expr.else_expr)
|
||||
&& exprid_is_pure(expr->ternary_expr.then_expr);
|
||||
case EXPR_ASM:
|
||||
return false;
|
||||
case EXPR_TRY:
|
||||
case EXPR_GROUP:
|
||||
case EXPR_CATCH:
|
||||
return expr_is_pure(expr->inner_expr);
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
|
||||
bool expr_is_simple(Expr *expr)
|
||||
{
|
||||
RETRY:
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_GROUP:
|
||||
expr = expr->inner_expr;
|
||||
goto RETRY;
|
||||
case EXPR_TERNARY:
|
||||
return expr_is_simple(exprptr(expr->ternary_expr.else_expr)) && expr_is_simple(exprptr(expr->ternary_expr.then_expr));
|
||||
case EXPR_RETHROW:
|
||||
expr = expr->rethrow_expr.inner;
|
||||
goto RETRY;
|
||||
default:
|
||||
return true;
|
||||
case EXPR_BINARY:
|
||||
switch (expr->binary_expr.operator)
|
||||
{
|
||||
case BINARYOP_AND:
|
||||
case BINARYOP_OR:
|
||||
case BINARYOP_GT:
|
||||
case BINARYOP_GE:
|
||||
case BINARYOP_LT:
|
||||
case BINARYOP_LE:
|
||||
case BINARYOP_NE:
|
||||
case BINARYOP_EQ:
|
||||
case BINARYOP_ASSIGN:
|
||||
case BINARYOP_ADD_ASSIGN:
|
||||
case BINARYOP_BIT_AND_ASSIGN:
|
||||
case BINARYOP_BIT_OR_ASSIGN:
|
||||
case BINARYOP_BIT_XOR_ASSIGN:
|
||||
case BINARYOP_DIV_ASSIGN:
|
||||
case BINARYOP_MOD_ASSIGN:
|
||||
case BINARYOP_MULT_ASSIGN:
|
||||
case BINARYOP_SHR_ASSIGN:
|
||||
case BINARYOP_SHL_ASSIGN:
|
||||
case BINARYOP_SUB_ASSIGN:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
UNREACHABLE
|
||||
case EXPR_UNARY:
|
||||
switch (expr->unary_expr.operator)
|
||||
{
|
||||
case UNARYOP_NEG:
|
||||
case UNARYOP_BITNEG:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
|
||||
Expr *expr_new(ExprKind kind, SourceSpan start)
|
||||
{
|
||||
Expr *expr = expr_calloc();
|
||||
expr->expr_kind = kind;
|
||||
expr->span = start;
|
||||
return expr;
|
||||
}
|
||||
|
||||
Expr *expr_new_const_int(SourceSpan span, Type *type, uint64_t v, bool narrowable)
|
||||
{
|
||||
Expr *expr = expr_calloc();
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr->span = span;
|
||||
expr->type = type;
|
||||
TypeKind kind = type_flatten(type)->type_kind;
|
||||
expr->const_expr.ixx.i.high = 0;
|
||||
if (type_kind_is_signed(kind))
|
||||
{
|
||||
if (v > (uint64_t)INT64_MAX) expr->const_expr.ixx.i.high = UINT64_MAX;
|
||||
}
|
||||
expr->const_expr.ixx.i.low = v;
|
||||
expr->const_expr.ixx.type = kind;
|
||||
expr->const_expr.const_kind = CONST_INTEGER;
|
||||
expr->const_expr.narrowable = narrowable;
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
return expr;
|
||||
}
|
||||
|
||||
Expr *expr_new_const_typeid(SourceSpan span, Type *type)
|
||||
{
|
||||
Expr *expr = expr_calloc();
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr->span = span;
|
||||
expr->type = type_typeid;
|
||||
expr->const_expr.const_kind = CONST_TYPEID;
|
||||
expr->const_expr.typeid = type;
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
return expr;
|
||||
}
|
||||
|
||||
Expr *expr_new_const_bool(SourceSpan span, Type *type, bool value)
|
||||
{
|
||||
Expr *expr = expr_calloc();
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr->span = span;
|
||||
expr->type = type;
|
||||
assert(type_flatten(type)->type_kind == TYPE_BOOL);
|
||||
expr->const_expr.b = value;
|
||||
expr->const_expr.const_kind = CONST_BOOL;
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
return expr;
|
||||
}
|
||||
|
||||
void expr_rewrite_to_builtin_access(Expr *expr, Expr *parent, BuiltinAccessKind kind, Type *type)
|
||||
{
|
||||
expr->expr_kind = EXPR_BUILTIN_ACCESS;
|
||||
expr->builtin_access_expr.kind = kind;
|
||||
expr->builtin_access_expr.inner = exprid(parent);
|
||||
expr->type = type_add_optional(type, IS_OPTIONAL(parent));
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
}
|
||||
|
||||
Expr *expr_variable(Decl *decl)
|
||||
{
|
||||
if (decl->resolve_status == RESOLVE_DONE)
|
||||
{
|
||||
Expr *expr = expr_new(EXPR_IDENTIFIER, decl->span);
|
||||
expr->identifier_expr.decl = decl;
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
expr->type = decl->type;
|
||||
return expr;
|
||||
}
|
||||
Expr *expr = expr_new(EXPR_IDENTIFIER, decl->span);
|
||||
expr->identifier_expr.ident = decl->name;
|
||||
expr->resolve_status = RESOLVE_NOT_DONE;
|
||||
return expr;
|
||||
}
|
||||
|
||||
void expr_rewrite_to_variable(Expr *expr, Decl *decl)
|
||||
{
|
||||
expr->expr_kind = EXPR_IDENTIFIER;
|
||||
if (decl->resolve_status == RESOLVE_DONE)
|
||||
{
|
||||
expr->identifier_expr.decl = decl;
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
expr->type = decl->type;
|
||||
return;
|
||||
}
|
||||
expr->identifier_expr.ident = decl->name;
|
||||
expr->resolve_status = RESOLVE_NOT_DONE;
|
||||
}
|
||||
|
||||
void expr_rewrite_insert_deref(Expr *original)
|
||||
{
|
||||
// Assume *(&x) => x
|
||||
if (original->expr_kind == EXPR_UNARY && original->unary_expr.operator == UNARYOP_ADDR)
|
||||
{
|
||||
*original = *original->unary_expr.expr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate our new and create our new inner, and overwrite the original.
|
||||
Expr *inner = expr_copy(original);
|
||||
original->expr_kind = EXPR_UNARY;
|
||||
original->type = NULL;
|
||||
original->unary_expr.operator = UNARYOP_DEREF;
|
||||
original->unary_expr.expr = inner;
|
||||
|
||||
// In the case the original is already resolved, we want to resolve the deref as well.
|
||||
if (original->resolve_status == RESOLVE_DONE)
|
||||
{
|
||||
Type *no_fail = type_no_optional(inner->type);
|
||||
assert(no_fail->canonical->type_kind == TYPE_POINTER);
|
||||
|
||||
// Only fold to the canonical type if it wasn't a pointer.
|
||||
Type *pointee = no_fail->type_kind == TYPE_POINTER ? no_fail->pointer : no_fail->canonical->pointer;
|
||||
original->type = type_add_optional(pointee, IS_OPTIONAL(inner));
|
||||
}
|
||||
}
|
||||
|
||||
void expr_rewrite_to_string(Expr *expr_to_rewrite, const char *string)
|
||||
{
|
||||
expr_to_rewrite->expr_kind = EXPR_CONST;
|
||||
expr_to_rewrite->const_expr.const_kind = CONST_STRING;
|
||||
expr_to_rewrite->const_expr.string.chars = (char *)string;
|
||||
ArraySize len = (ArraySize)strlen(string);
|
||||
expr_to_rewrite->const_expr.string.len = len;
|
||||
expr_to_rewrite->resolve_status = RESOLVE_DONE;
|
||||
expr_to_rewrite->type = type_get_ptr(type_get_array(type_char, len));
|
||||
}
|
||||
|
||||
void expr_rewrite_to_binary(Expr *expr_to_rewrite, Expr *left, Expr *right, BinaryOp op)
|
||||
{
|
||||
expr_to_rewrite->binary_expr = (ExprBinary) { .operator = op, .left = exprid(left), .right = exprid(right) };
|
||||
expr_to_rewrite->expr_kind = EXPR_BINARY;
|
||||
}
|
||||
@@ -150,6 +150,7 @@ Float float_from_string(const char *string, char **error)
|
||||
}
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
case 32:
|
||||
kind = TYPE_F32;
|
||||
break;
|
||||
@@ -157,7 +158,6 @@ Float float_from_string(const char *string, char **error)
|
||||
kind = TYPE_F16;
|
||||
break;
|
||||
case 64:
|
||||
case 0:
|
||||
kind = TYPE_F64;
|
||||
break;
|
||||
case 128:
|
||||
|
||||
@@ -34,8 +34,8 @@ static void header_print_type(FILE *file, Type *type)
|
||||
UNREACHABLE
|
||||
case TYPE_BITSTRUCT:
|
||||
TODO
|
||||
case TYPE_FAILABLE:
|
||||
case TYPE_FAILABLE_ANY:
|
||||
case TYPE_OPTIONAL:
|
||||
case TYPE_OPTIONAL_ANY:
|
||||
// If this is reachable then we are not doing the proper lowering.
|
||||
UNREACHABLE
|
||||
case TYPE_VOID:
|
||||
@@ -97,6 +97,7 @@ static void header_print_type(FILE *file, Type *type)
|
||||
case TYPE_FAULTTYPE:
|
||||
OUTPUT("enum %s__", type->decl->extname);
|
||||
return;
|
||||
case TYPE_SCALED_VECTOR:
|
||||
case TYPE_FLEXIBLE_ARRAY:
|
||||
TODO
|
||||
case TYPE_FUNC:
|
||||
|
||||
@@ -360,7 +360,11 @@ static inline bool scan_ident(Lexer *lexer, TokenType normal, TokenType const_to
|
||||
if (!type)
|
||||
{
|
||||
if (!prefix && len == 1) return return_token(lexer, TOKEN_UNDERSCORE, "_");
|
||||
add_error_token(lexer, "An identifier may not consist of only '_' characters.");
|
||||
if (prefix && len == 1)
|
||||
{
|
||||
return add_error_token(lexer, "An identifier was expected after the '%c'.", prefix);
|
||||
}
|
||||
return add_error_token(lexer, "An identifier may not consist of only '_' characters.");
|
||||
}
|
||||
const char* interned_string = symtab_add(lexer->lexing_start, len, hash, &type);
|
||||
if (type == TOKEN_RETURN && lexer->mode == LEX_DOCS) type = TOKEN_IDENT;
|
||||
@@ -927,9 +931,10 @@ static inline bool scan_string(Lexer *lexer)
|
||||
current++;
|
||||
break;
|
||||
}
|
||||
if (c == '\\' && *current == '"')
|
||||
if (c == '\\')
|
||||
{
|
||||
current++;
|
||||
c = *current;
|
||||
if (c != '\n' && c != '\0') current++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1275,7 +1280,7 @@ static bool lexer_scan_token_inner(Lexer *lexer)
|
||||
case '\n':
|
||||
return scan_doc_line(lexer);
|
||||
case '@':
|
||||
if (char_is_letter(peek(lexer)))
|
||||
if (char_is_letter_(peek(lexer)))
|
||||
{
|
||||
return scan_ident(lexer, TOKEN_AT_IDENT, TOKEN_AT_CONST_IDENT, TOKEN_AT_TYPE_IDENT, '@');
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ static void linker_setup_windows(const char ***args_ref, LinkerType linker_type)
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
if (active_target.no_libc) return;
|
||||
if (!active_target.win.sdk)
|
||||
{
|
||||
const char *path = windows_cross_compile_library();
|
||||
@@ -273,7 +274,18 @@ static void linker_setup_windows_gnu(const char ***args_ref, LinkerType linker_t
|
||||
|
||||
static void linker_setup_macos(const char ***args_ref, LinkerType linker_type)
|
||||
{
|
||||
if (linker_type == LINKER_CC) return;
|
||||
add_arg("-framework");
|
||||
add_arg("CoreFoundation");
|
||||
if (linker_type == LINKER_CC)
|
||||
{
|
||||
return;
|
||||
}
|
||||
add_arg("-arch");
|
||||
add_arg(arch_to_linker_arch(platform_target.arch));
|
||||
|
||||
// Skip if no libc.
|
||||
if (active_target.no_libc) return;
|
||||
|
||||
const char *sysroot = active_target.macos.sdk ? active_target.macos.sdk : macos_sysroot();
|
||||
if (!sysroot)
|
||||
{
|
||||
@@ -281,8 +293,6 @@ static void linker_setup_macos(const char ***args_ref, LinkerType linker_type)
|
||||
}
|
||||
DEBUG_LOG("Macos SDK: %s", sysroot);
|
||||
MacSDK *mac_sdk = macos_sysroot_sdk_information(sysroot);
|
||||
add_arg("-arch");
|
||||
add_arg(arch_to_linker_arch(platform_target.arch));
|
||||
add_arg("-lSystem");
|
||||
add_arg("-lm");
|
||||
add_arg("-syslibroot");
|
||||
@@ -291,8 +301,22 @@ static void linker_setup_macos(const char ***args_ref, LinkerType linker_type)
|
||||
if (is_pie(platform_target.reloc_model)) add_arg("-pie");
|
||||
add_arg("-platform_version");
|
||||
add_arg("macos");
|
||||
add_arg(str_printf("%d.0.0", mac_sdk->macos_deploy_target.major));
|
||||
add_arg(str_printf("%d.%d", mac_sdk->macos_deploy_target.major, mac_sdk->macos_deploy_target.minor));
|
||||
if (active_target.macos.min_version)
|
||||
{
|
||||
add_arg(active_target.macos.min_version);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_arg(str_printf("%d.%d.0", mac_sdk->macos_min_deploy_target.major, mac_sdk->macos_min_deploy_target.minor));
|
||||
}
|
||||
if (active_target.macos.sdk_version)
|
||||
{
|
||||
add_arg(active_target.macos.sdk_version);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_arg(str_printf("%d.%d", mac_sdk->macos_deploy_target.major, mac_sdk->macos_deploy_target.minor));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -355,6 +379,7 @@ static void linker_setup_linux(const char ***args_ref, LinkerType linker_type)
|
||||
if (is_no_pie(platform_target.reloc_model)) add_arg("-no-pie");
|
||||
if (is_pie(platform_target.reloc_model)) add_arg("-pie");
|
||||
if (platform_target.arch == ARCH_TYPE_X86_64) add_arg("--eh-frame-hdr");
|
||||
if (active_target.no_libc) return;
|
||||
const char *crt_begin_dir = find_linux_crt_begin();
|
||||
const char *crt_dir = find_linux_crt();
|
||||
if (!crt_begin_dir || !crt_dir)
|
||||
@@ -393,6 +418,9 @@ static void linker_setup_freebsd(const char ***args_ref, LinkerType linker_type)
|
||||
if (is_no_pie(platform_target.reloc_model)) add_arg("-no-pie");
|
||||
if (is_pie(platform_target.reloc_model)) add_arg("-pie");
|
||||
if (platform_target.arch == ARCH_TYPE_X86_64) add_arg("--eh-frame-hdr");
|
||||
|
||||
if (active_target.no_libc) return;
|
||||
|
||||
const char *crt_dir = find_freebsd_crt();
|
||||
if (!crt_dir)
|
||||
{
|
||||
@@ -464,9 +492,17 @@ static bool linker_setup(const char ***args_ref, const char **files_to_link, uns
|
||||
linker_setup_linux(args_ref, linker_type);
|
||||
break;
|
||||
case OS_TYPE_UNKNOWN:
|
||||
error_exit("Linking is not supported for unknown OS.");
|
||||
if (!active_target.no_libc)
|
||||
{
|
||||
error_exit("Linking is not supported for unknown OS.");
|
||||
}
|
||||
break;
|
||||
case OS_TYPE_NONE:
|
||||
error_exit("Linking is not supported for freestanding.");
|
||||
if (!active_target.no_libc)
|
||||
{
|
||||
error_exit("Linking is not supported for freestanding.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
for (unsigned i = 0; i < file_count; i++)
|
||||
{
|
||||
@@ -687,7 +723,10 @@ void platform_linker(const char *output_file, const char **files, unsigned file_
|
||||
vec_add(parts, active_target.cc ? active_target.cc : "cc");
|
||||
append_fpie_pic_options(platform_target.reloc_model, &parts);
|
||||
linker_setup(&parts, files, file_count, output_file, LINKER_CC);
|
||||
vec_add(parts, "-lm");
|
||||
if (!active_target.no_libc)
|
||||
{
|
||||
vec_add(parts, "-lm");
|
||||
}
|
||||
const char *output = concat_string_parts(parts);
|
||||
if (system(output) != 0)
|
||||
{
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "llvm_codegen_internal.h"
|
||||
#include "compiler_tests/benchmark.h"
|
||||
|
||||
#if LLVM_VERSION_MAJOR > 12
|
||||
#include <llvm-c/Error.h>
|
||||
#include <llvm-c/Comdat.h>
|
||||
#include <llvm-c/Linker.h>
|
||||
#include <setjmp.h>
|
||||
typedef struct LLVMOpaquePassBuilderOptions *LLVMPassBuilderOptionsRef;
|
||||
LLVMErrorRef LLVMRunPasses(LLVMModuleRef M, const char *Passes,
|
||||
LLVMTargetMachineRef TM,
|
||||
@@ -14,7 +17,10 @@ LLVMPassBuilderOptionsRef LLVMCreatePassBuilderOptions(void);
|
||||
void LLVMPassBuilderOptionsSetVerifyEach(LLVMPassBuilderOptionsRef Options, LLVMBool VerifyEach);
|
||||
void LLVMPassBuilderOptionsSetDebugLogging(LLVMPassBuilderOptionsRef Options, LLVMBool DebugLogging);
|
||||
void LLVMDisposePassBuilderOptions(LLVMPassBuilderOptionsRef Options);
|
||||
#endif
|
||||
|
||||
static void llvm_emit_constructors_and_destructors(GenContext *c);
|
||||
static void llvm_codegen_setup();
|
||||
static GenContext *llvm_gen_module(Module *module, LLVMContextRef shared_context);
|
||||
|
||||
const char* llvm_version = LLVM_VERSION_STRING;
|
||||
const char* llvm_target = LLVM_DEFAULT_TARGET_TRIPLE;
|
||||
@@ -49,11 +55,24 @@ static void diagnostics_handler(LLVMDiagnosticInfoRef ref, void *context)
|
||||
LLVMDisposeMessage(message);
|
||||
}
|
||||
|
||||
static void gencontext_init(GenContext *context, Module *module)
|
||||
static void gencontext_init(GenContext *context, Module *module, LLVMContextRef shared_context)
|
||||
{
|
||||
assert(LLVMIsMultithreaded());
|
||||
memset(context, 0, sizeof(GenContext));
|
||||
context->context = LLVMContextCreate();
|
||||
LLVMContextSetDiagnosticHandler(context->context, &diagnostics_handler, context);
|
||||
if (shared_context)
|
||||
{
|
||||
context->shared_context = true;
|
||||
context->context = shared_context;
|
||||
}
|
||||
else
|
||||
{
|
||||
context->context = LLVMContextCreate();
|
||||
}
|
||||
if (debug_log)
|
||||
{
|
||||
LLVMContextSetDiagnosticHandler(context->context, &diagnostics_handler, context);
|
||||
}
|
||||
if (!active_target.emit_llvm && !active_target.test_output) LLVMContextSetDiscardValueNames(context->context, true);
|
||||
context->code_module = module;
|
||||
}
|
||||
|
||||
@@ -61,7 +80,7 @@ static void gencontext_destroy(GenContext *context)
|
||||
{
|
||||
assert(llvm_is_global_eval(context));
|
||||
LLVMDisposeBuilder(context->global_builder);
|
||||
LLVMContextDispose(context->context);
|
||||
if (!context->shared_context) LLVMContextDispose(context->context);
|
||||
LLVMDisposeTargetData(context->target_data);
|
||||
LLVMDisposeTargetMachine(context->machine);
|
||||
free(context);
|
||||
@@ -80,6 +99,22 @@ LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ptr, uint
|
||||
return LLVMBuildMemSet(c->builder, ptr, llvm_get_zero(c, type_char), llvm_const_int(c, type_usize, size), align);
|
||||
}
|
||||
|
||||
INLINE void llvm_emit_xtor(GenContext *c, LLVMValueRef *list, const char *name)
|
||||
{
|
||||
if (!list) return;
|
||||
unsigned len = vec_size(list);
|
||||
LLVMTypeRef type = LLVMTypeOf(list[0]);
|
||||
LLVMValueRef array = LLVMConstArray(type, list, len);
|
||||
LLVMValueRef global = LLVMAddGlobal(c->module, LLVMTypeOf(array), name);
|
||||
LLVMSetLinkage(global, LLVMAppendingLinkage);
|
||||
LLVMSetInitializer(global, array);
|
||||
}
|
||||
void llvm_emit_constructors_and_destructors(GenContext *c)
|
||||
{
|
||||
llvm_emit_xtor(c, c->constructors, "llvm.global_ctors");
|
||||
llvm_emit_xtor(c, c->destructors, "llvm.global_dtors");
|
||||
}
|
||||
|
||||
/**
|
||||
* Consider the case when we have int[5] x = { [0] = 1, [1] = 3 }
|
||||
* In this case we want this: { i32 0, i32 2, [8 x i32] zeroinitializer }
|
||||
@@ -306,11 +341,11 @@ void llvm_set_global_tls(Decl *decl)
|
||||
}
|
||||
LLVMSetThreadLocal(decl->backend_ref, true);
|
||||
LLVMSetThreadLocalMode(decl->backend_ref, thread_local_mode);
|
||||
void *failable_ref = decl->var.failable_ref;
|
||||
if (failable_ref)
|
||||
void *optional_ref = decl->var.optional_ref;
|
||||
if (optional_ref)
|
||||
{
|
||||
LLVMSetThreadLocal(failable_ref, true);
|
||||
LLVMSetThreadLocalMode(failable_ref, thread_local_mode);
|
||||
LLVMSetThreadLocal(optional_ref, true);
|
||||
LLVMSetThreadLocalMode(optional_ref, thread_local_mode);
|
||||
}
|
||||
}
|
||||
void llvm_set_internal_linkage(LLVMValueRef alloc)
|
||||
@@ -343,9 +378,9 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl)
|
||||
}
|
||||
if (init_expr)
|
||||
{
|
||||
if (init_expr->expr_kind == EXPR_CONST && init_expr->const_expr.const_kind == CONST_LIST)
|
||||
if (init_expr->expr_kind == EXPR_CONST && init_expr->const_expr.const_kind == CONST_INITIALIZER)
|
||||
{
|
||||
ConstInitializer *list = init_expr->const_expr.list;
|
||||
ConstInitializer *list = init_expr->const_expr.initializer;
|
||||
init_value = llvm_emit_const_initializer(c, list);
|
||||
}
|
||||
else
|
||||
@@ -390,22 +425,22 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl)
|
||||
}
|
||||
llvm_set_global_tls(decl);
|
||||
|
||||
LLVMValueRef failable_ref = decl->var.failable_ref;
|
||||
if (failable_ref)
|
||||
LLVMValueRef optional_ref = decl->var.optional_ref;
|
||||
if (optional_ref)
|
||||
{
|
||||
llvm_set_alignment(failable_ref, type_alloca_alignment(type_anyerr));
|
||||
LLVMSetUnnamedAddress(failable_ref, LLVMGlobalUnnamedAddr);
|
||||
llvm_set_alignment(optional_ref, type_alloca_alignment(type_anyerr));
|
||||
LLVMSetUnnamedAddress(optional_ref, LLVMGlobalUnnamedAddr);
|
||||
}
|
||||
if (init_expr && IS_OPTIONAL(init_expr) && init_expr->expr_kind == EXPR_FAILABLE)
|
||||
if (init_expr && IS_OPTIONAL(init_expr) && init_expr->expr_kind == EXPR_OPTIONAL)
|
||||
{
|
||||
UNREACHABLE
|
||||
}
|
||||
if (decl->visibility != VISIBLE_EXTERN)
|
||||
{
|
||||
LLVMSetInitializer(decl->backend_ref, init_value);
|
||||
if (failable_ref)
|
||||
if (optional_ref)
|
||||
{
|
||||
LLVMSetInitializer(failable_ref, llvm_get_zero(c, type_anyerr));
|
||||
LLVMSetInitializer(optional_ref, llvm_get_zero(c, type_anyerr));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,20 +452,20 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl)
|
||||
{
|
||||
case VISIBLE_MODULE:
|
||||
LLVMSetVisibility(global_ref, LLVMProtectedVisibility);
|
||||
if (failable_ref) LLVMSetVisibility(failable_ref, LLVMProtectedVisibility);
|
||||
if (optional_ref) LLVMSetVisibility(optional_ref, LLVMProtectedVisibility);
|
||||
break;
|
||||
case VISIBLE_PUBLIC:
|
||||
LLVMSetVisibility(global_ref, LLVMDefaultVisibility);
|
||||
if (failable_ref) LLVMSetVisibility(failable_ref, LLVMDefaultVisibility);
|
||||
if (optional_ref) LLVMSetVisibility(optional_ref, LLVMDefaultVisibility);
|
||||
break;
|
||||
case VISIBLE_EXTERN:
|
||||
LLVMSetLinkage(global_ref, LLVMExternalLinkage);
|
||||
if (failable_ref) LLVMSetLinkage(failable_ref, LLVMExternalLinkage);
|
||||
if (optional_ref) LLVMSetLinkage(optional_ref, LLVMExternalLinkage);
|
||||
//LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility);
|
||||
break;
|
||||
case VISIBLE_LOCAL:
|
||||
LLVMSetLinkage(global_ref, LLVMInternalLinkage);
|
||||
if (failable_ref) LLVMSetLinkage(failable_ref, LLVMInternalLinkage);
|
||||
if (optional_ref) LLVMSetLinkage(optional_ref, LLVMInternalLinkage);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -585,13 +620,10 @@ static bool intrinsics_setup = false;
|
||||
LLVMAttributes attribute_id;
|
||||
LLVMIntrinsics intrinsic_id;
|
||||
|
||||
void llvm_codegen_setup()
|
||||
static void llvm_codegen_setup()
|
||||
{
|
||||
if (intrinsics_setup) return;
|
||||
|
||||
//intrinsic_id.sshl_sat = lookup_intrinsic("llvm.sshl.sat");
|
||||
//intrinsic_id.ushl_sat = lookup_intrinsic("llvm.ushl.sat");
|
||||
|
||||
intrinsic_id.abs = lookup_intrinsic("llvm.abs");
|
||||
intrinsic_id.assume = lookup_intrinsic("llvm.assume");
|
||||
intrinsic_id.bitreverse = lookup_intrinsic("llvm.bitreverse");
|
||||
@@ -609,7 +641,9 @@ void llvm_codegen_setup()
|
||||
intrinsic_id.exp2 = lookup_intrinsic("llvm.exp2");
|
||||
intrinsic_id.fabs = lookup_intrinsic("llvm.fabs");
|
||||
intrinsic_id.floor = lookup_intrinsic("llvm.floor");
|
||||
intrinsic_id.flt_rounds = lookup_intrinsic("llvm.flt.rounds");
|
||||
intrinsic_id.fma = lookup_intrinsic("llvm.fma");
|
||||
intrinsic_id.frameaddress = lookup_intrinsic("llvm.frameaddress");
|
||||
intrinsic_id.fshl = lookup_intrinsic("llvm.fshl");
|
||||
intrinsic_id.fshr = lookup_intrinsic("llvm.fshr");
|
||||
intrinsic_id.lifetime_end = lookup_intrinsic("llvm.lifetime.end");
|
||||
@@ -624,19 +658,26 @@ void llvm_codegen_setup()
|
||||
intrinsic_id.maximum = lookup_intrinsic("llvm.maximum");
|
||||
intrinsic_id.maxnum = lookup_intrinsic("llvm.maxnum");
|
||||
intrinsic_id.memcpy = lookup_intrinsic("llvm.memcpy");
|
||||
intrinsic_id.memcpy_inline = lookup_intrinsic("llvm.memcpy.inline");
|
||||
intrinsic_id.memmove = lookup_intrinsic("llvm.memmove");
|
||||
intrinsic_id.memset = lookup_intrinsic("llvm.memset");
|
||||
intrinsic_id.memset_inline = lookup_intrinsic("llvm.memset.inline");
|
||||
intrinsic_id.minimum = lookup_intrinsic("llvm.minimum");
|
||||
intrinsic_id.minnum = lookup_intrinsic("llvm.minnum");
|
||||
intrinsic_id.fmuladd = lookup_intrinsic("llvm.fmuladd");
|
||||
intrinsic_id.nearbyint = lookup_intrinsic("llvm.nearbyint");
|
||||
intrinsic_id.pow = lookup_intrinsic("llvm.pow");
|
||||
intrinsic_id.powi = lookup_intrinsic("llvm.powi");
|
||||
intrinsic_id.prefetch = lookup_intrinsic("llvm.prefetch");
|
||||
intrinsic_id.readcyclecounter = lookup_intrinsic("llvm.readcyclecounter");
|
||||
intrinsic_id.rint = lookup_intrinsic("llvm.rint");
|
||||
intrinsic_id.round = lookup_intrinsic("llvm.round");
|
||||
intrinsic_id.roundeven = lookup_intrinsic("llvm.roundeven");
|
||||
intrinsic_id.sadd_overflow = lookup_intrinsic("llvm.sadd.with.overflow");
|
||||
intrinsic_id.sadd_sat = lookup_intrinsic("llvm.sadd.sat");
|
||||
intrinsic_id.set_rounding = lookup_intrinsic("llvm.set.rounding");
|
||||
intrinsic_id.sin = lookup_intrinsic("llvm.sin");
|
||||
intrinsic_id.sshl_sat = lookup_intrinsic("llvm.sshl.sat");
|
||||
intrinsic_id.smax = lookup_intrinsic("llvm.smax");
|
||||
intrinsic_id.smin = lookup_intrinsic("llvm.smin");
|
||||
intrinsic_id.smul_overflow = lookup_intrinsic("llvm.smul.with.overflow");
|
||||
@@ -651,6 +692,7 @@ void llvm_codegen_setup()
|
||||
intrinsic_id.umin = lookup_intrinsic("llvm.umin");
|
||||
intrinsic_id.umul_overflow = lookup_intrinsic("llvm.umul.with.overflow");
|
||||
intrinsic_id.usub_overflow = lookup_intrinsic("llvm.usub.with.overflow");
|
||||
intrinsic_id.ushl_sat = lookup_intrinsic("llvm.ushl.sat");
|
||||
intrinsic_id.usub_sat = lookup_intrinsic("llvm.usub.sat");
|
||||
intrinsic_id.vector_reduce_fmax = lookup_intrinsic("llvm.vector.reduce.fmax");
|
||||
intrinsic_id.vector_reduce_fmin = lookup_intrinsic("llvm.vector.reduce.fmin");
|
||||
@@ -658,6 +700,13 @@ void llvm_codegen_setup()
|
||||
intrinsic_id.vector_reduce_smin = lookup_intrinsic("llvm.vector.reduce.smin");
|
||||
intrinsic_id.vector_reduce_umax = lookup_intrinsic("llvm.vector.reduce.umax");
|
||||
intrinsic_id.vector_reduce_umin = lookup_intrinsic("llvm.vector.reduce.umin");
|
||||
intrinsic_id.vector_reduce_add = lookup_intrinsic("llvm.vector.reduce.add");
|
||||
intrinsic_id.vector_reduce_fadd = lookup_intrinsic("llvm.vector.reduce.fadd");
|
||||
intrinsic_id.vector_reduce_mul = lookup_intrinsic("llvm.vector.reduce.mul");
|
||||
intrinsic_id.vector_reduce_fmul = lookup_intrinsic("llvm.vector.reduce.fmul");
|
||||
intrinsic_id.vector_reduce_and = lookup_intrinsic("llvm.vector.reduce.and");
|
||||
intrinsic_id.vector_reduce_or = lookup_intrinsic("llvm.vector.reduce.or");
|
||||
intrinsic_id.vector_reduce_xor = lookup_intrinsic("llvm.vector.reduce.xor");
|
||||
|
||||
attribute_id.align = lookup_attribute("align");
|
||||
attribute_id.alwaysinline = lookup_attribute("alwaysinline");
|
||||
@@ -722,23 +771,9 @@ void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value)
|
||||
LLVMSetLinkage(value, LLVMLinkerPrivateLinkage);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void llvm_value_set_bool(BEValue *value, LLVMValueRef llvm_value)
|
||||
{
|
||||
value->value = llvm_value;
|
||||
value->alignment = type_abi_alignment(type_bool);
|
||||
value->kind = BE_BOOLEAN;
|
||||
value->type = type_bool;
|
||||
}
|
||||
|
||||
void llvm_value_set_int(GenContext *c, BEValue *value, Type *type, uint64_t i)
|
||||
{
|
||||
llvm_value_set(value, llvm_const_int(c, type, i), type);
|
||||
@@ -806,49 +841,14 @@ static void llvm_emit_type_decls(GenContext *context, Decl *decl)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void llvm_opt_old(GenContext *c)
|
||||
{
|
||||
LLVMPassManagerBuilderRef pass_manager_builder = LLVMPassManagerBuilderCreate();
|
||||
LLVMPassManagerBuilderSetOptLevel(pass_manager_builder, (unsigned)active_target.optimization_level);
|
||||
LLVMPassManagerBuilderSetSizeLevel(pass_manager_builder, (unsigned)active_target.size_optimization_level);
|
||||
LLVMPassManagerBuilderSetDisableUnrollLoops(pass_manager_builder, active_target.optimization_level == OPTIMIZATION_NONE);
|
||||
if (active_target.optimization_level != OPTIMIZATION_NONE)
|
||||
{
|
||||
LLVMPassManagerBuilderUseInlinerWithThreshold(pass_manager_builder, (unsigned)get_inlining_threshold());
|
||||
}
|
||||
LLVMPassManagerRef pass_manager = LLVMCreatePassManager();
|
||||
LLVMPassManagerRef function_pass_manager = LLVMCreateFunctionPassManagerForModule(c->module);
|
||||
LLVMAddAnalysisPasses(c->machine, function_pass_manager);
|
||||
LLVMAddAnalysisPasses(c->machine, pass_manager);
|
||||
LLVMPassManagerBuilderPopulateModulePassManager(pass_manager_builder, pass_manager);
|
||||
LLVMPassManagerBuilderPopulateFunctionPassManager(pass_manager_builder, function_pass_manager);
|
||||
|
||||
// IMPROVE
|
||||
// In LLVM Opt, LoopVectorize and SLPVectorize settings are part of the PassManagerBuilder
|
||||
// Anything else we need to manually add?
|
||||
|
||||
LLVMPassManagerBuilderDispose(pass_manager_builder);
|
||||
|
||||
// Run function passes
|
||||
LLVMInitializeFunctionPassManager(function_pass_manager);
|
||||
LLVMValueRef current_function = LLVMGetFirstFunction(c->module);
|
||||
while (current_function)
|
||||
{
|
||||
LLVMRunFunctionPassManager(function_pass_manager, current_function);
|
||||
current_function = LLVMGetNextFunction(current_function);
|
||||
}
|
||||
LLVMFinalizeFunctionPassManager(function_pass_manager);
|
||||
LLVMDisposePassManager(function_pass_manager);
|
||||
|
||||
// Run module pass
|
||||
LLVMRunPassManager(pass_manager, c->module);
|
||||
LLVMDisposePassManager(pass_manager);
|
||||
}
|
||||
#if LLVM_VERSION_MAJOR > 12
|
||||
static inline void llvm_opt_new(GenContext *c)
|
||||
static inline void llvm_optimize(GenContext *c)
|
||||
{
|
||||
LLVMPassBuilderOptionsRef options = LLVMCreatePassBuilderOptions();
|
||||
LLVMPassBuilderOptionsSetVerifyEach(options, active_target.emit_llvm);
|
||||
#ifndef NDEBUG
|
||||
LLVMPassBuilderOptionsSetDebugLogging(options, debug_log);
|
||||
#endif
|
||||
const char *passes = NULL;
|
||||
switch (active_target.size_optimization_level)
|
||||
{
|
||||
@@ -884,20 +884,11 @@ static inline void llvm_opt_new(GenContext *c)
|
||||
}
|
||||
LLVMDisposePassBuilderOptions(options);
|
||||
}
|
||||
#endif
|
||||
|
||||
const char *llvm_codegen(void *context)
|
||||
{
|
||||
GenContext *c = context;
|
||||
#if LLVM_VERSION_MAJOR > 12
|
||||
if (active_target.use_new_optimizer)
|
||||
{
|
||||
llvm_opt_new(c);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
llvm_opt_old(c);
|
||||
}
|
||||
llvm_optimize(c);
|
||||
|
||||
// Serialize the LLVM IR, if requested, also verify the IR in this case
|
||||
if (active_target.emit_llvm)
|
||||
@@ -937,7 +928,7 @@ void llvm_add_global_decl(GenContext *c, Decl *decl)
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(decl_get_extname(decl));
|
||||
scratch_buffer_append(".f");
|
||||
decl->var.failable_ref = llvm_add_global(c, scratch_buffer_to_string(), type_anyerr, 0);
|
||||
decl->var.optional_ref = llvm_add_global(c, scratch_buffer_to_string(), type_anyerr, 0);
|
||||
}
|
||||
llvm_set_global_tls(decl);
|
||||
}
|
||||
@@ -947,7 +938,7 @@ LLVMValueRef llvm_get_opt_ref(GenContext *c, Decl *decl)
|
||||
llvm_get_ref(c, decl);
|
||||
decl = decl_flatten(decl);
|
||||
if (decl->decl_kind != DECL_VAR) return NULL;
|
||||
return decl->var.failable_ref;
|
||||
return decl->var.optional_ref;
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl)
|
||||
@@ -1005,81 +996,233 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl)
|
||||
case DECL_TYPEDEF:
|
||||
case DECL_UNION:
|
||||
case DECL_DECLARRAY:
|
||||
case DECL_INITIALIZE:
|
||||
case DECL_FINALIZE:
|
||||
case DECL_BODYPARAM:
|
||||
case DECL_CT_ECHO:
|
||||
case DECL_CT_INCLUDE:
|
||||
UNREACHABLE;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
void *llvm_gen(Module *module)
|
||||
|
||||
static void llvm_gen_test_main(GenContext *c)
|
||||
{
|
||||
Decl *test_runner = global_context.test_func;
|
||||
if (!test_runner)
|
||||
{
|
||||
error_exit("No test runner found.");
|
||||
}
|
||||
assert(!global_context.main && "Main should not be set if a test main is generated.");
|
||||
global_context.main = test_runner;
|
||||
LLVMTypeRef cint = llvm_get_type(c, type_cint);
|
||||
LLVMTypeRef main_type = LLVMFunctionType(cint, NULL, 0, true);
|
||||
LLVMTypeRef runner_type = LLVMFunctionType(c->byte_type, NULL, 0, true);
|
||||
LLVMValueRef func = LLVMAddFunction(c->module, kw_main, main_type);
|
||||
LLVMValueRef other_func = LLVMAddFunction(c->module, test_runner->extname, runner_type);
|
||||
LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(c->context, func, "entry");
|
||||
LLVMBuilderRef builder = LLVMCreateBuilderInContext(c->context);
|
||||
LLVMPositionBuilderAtEnd(builder, entry);
|
||||
LLVMValueRef val = LLVMBuildCall2(builder, runner_type, other_func, NULL, 0, "");
|
||||
val = LLVMBuildSelect(builder, LLVMBuildTrunc(builder, val, c->bool_type, ""),
|
||||
LLVMConstNull(cint), LLVMConstInt(cint, 1, false), "");
|
||||
LLVMBuildRet(builder, val);
|
||||
LLVMDisposeBuilder(builder);
|
||||
gencontext_print_llvm_ir(c);
|
||||
}
|
||||
INLINE GenContext *llvm_gen_tests(Module** modules, unsigned module_count, LLVMContextRef shared_context)
|
||||
{
|
||||
Path *test_path = path_create_from_string("$test", 5, INVALID_SPAN);
|
||||
Module *test_module = compiler_find_or_create_module(test_path, NULL, false);
|
||||
|
||||
GenContext *c = cmalloc(sizeof(GenContext));
|
||||
active_target.debug_info = DEBUG_INFO_NONE;
|
||||
gencontext_init(c, test_module, shared_context);
|
||||
gencontext_begin_module(c);
|
||||
|
||||
LLVMValueRef *names = NULL;
|
||||
LLVMValueRef *decls = NULL;
|
||||
|
||||
LLVMTypeRef opt_test = LLVMFunctionType(llvm_get_type(c, type_anyerr), NULL, 0, false);
|
||||
for (unsigned i = 0; i < module_count; i++)
|
||||
{
|
||||
Module *module = modules[i];
|
||||
FOREACH_BEGIN(Decl *test, module->tests)
|
||||
LLVMValueRef ref;
|
||||
LLVMTypeRef type = opt_test;
|
||||
ref = LLVMAddFunction(c->module, test->extname, type);
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_printf("%s::%s", module->name->module, test->name);
|
||||
LLVMValueRef name = llvm_emit_string_const(c, scratch_buffer_to_string(), ".test.name");
|
||||
vec_add(names, name);
|
||||
vec_add(decls, ref);
|
||||
FOREACH_END();
|
||||
}
|
||||
unsigned test_count = vec_size(decls);
|
||||
LLVMValueRef name_ref;
|
||||
LLVMValueRef decl_ref;
|
||||
LLVMTypeRef chars_type = llvm_get_type(c, type_chars);
|
||||
if (test_count)
|
||||
{
|
||||
LLVMValueRef array_of_names = LLVMConstArray(chars_type, names, test_count);
|
||||
LLVMValueRef array_of_decls = LLVMConstArray(LLVMPointerType(opt_test, 0), decls, test_count);
|
||||
LLVMTypeRef arr_type = LLVMTypeOf(array_of_names);
|
||||
name_ref = llvm_add_global_raw(c, ".test_names", arr_type, 0);
|
||||
decl_ref = llvm_add_global_raw(c, ".test_decls", LLVMTypeOf(array_of_decls), 0);
|
||||
llvm_set_internal_linkage(name_ref);
|
||||
llvm_set_internal_linkage(decl_ref);
|
||||
LLVMSetGlobalConstant(name_ref, 1);
|
||||
LLVMSetGlobalConstant(decl_ref, 1);
|
||||
LLVMSetInitializer(name_ref, array_of_names);
|
||||
LLVMSetInitializer(decl_ref, array_of_decls);
|
||||
name_ref = LLVMBuildBitCast(c->builder, name_ref, llvm_get_ptr_type(c, type_chars), "");
|
||||
decl_ref = LLVMBuildBitCast(c->builder, decl_ref, llvm_get_ptr_type(c, type_voidptr), "");
|
||||
}
|
||||
else
|
||||
{
|
||||
name_ref = LLVMConstNull(llvm_get_ptr_type(c, type_chars));
|
||||
decl_ref = LLVMConstNull(llvm_get_ptr_type(c, type_voidptr));
|
||||
}
|
||||
LLVMValueRef count = llvm_const_int(c, type_usize, test_count);
|
||||
Type *chars_array = type_get_subarray(type_chars);
|
||||
LLVMValueRef name_list = llvm_add_global(c, test_names_var_name, chars_array, type_alloca_alignment(chars_array));
|
||||
LLVMSetGlobalConstant(name_list, 1);
|
||||
LLVMSetInitializer(name_list, llvm_emit_aggregate_two(c, chars_array, name_ref, count));
|
||||
Type *decls_array_type = type_get_subarray(type_voidptr);
|
||||
LLVMValueRef decl_list = llvm_add_global(c, test_fns_var_name, decls_array_type, type_alloca_alignment(decls_array_type));
|
||||
LLVMSetGlobalConstant(decl_list, 1);
|
||||
LLVMSetInitializer(decl_list, llvm_emit_aggregate_two(c, decls_array_type, decl_ref, count));
|
||||
|
||||
if (active_target.type == TARGET_TYPE_TEST)
|
||||
{
|
||||
llvm_gen_test_main(c);
|
||||
}
|
||||
|
||||
if (llvm_use_debug(c))
|
||||
{
|
||||
LLVMDIBuilderFinalize(c->debug.builder);
|
||||
LLVMDisposeDIBuilder(c->debug.builder);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
void **llvm_gen(Module** modules, unsigned module_count)
|
||||
{
|
||||
if (!module_count) return NULL;
|
||||
GenContext ** gen_contexts = NULL;
|
||||
llvm_codegen_setup();
|
||||
if (active_target.single_module)
|
||||
{
|
||||
GenContext *first_context;
|
||||
unsigned first_element;
|
||||
LLVMContextRef context = LLVMGetGlobalContext();
|
||||
for (int i = 0; i < module_count; i++)
|
||||
{
|
||||
GenContext *result = llvm_gen_module(modules[i], context);
|
||||
if (!result) continue;
|
||||
vec_add(gen_contexts, result);
|
||||
}
|
||||
if (!gen_contexts) return NULL;
|
||||
GenContext *first = gen_contexts[0];
|
||||
vec_add(gen_contexts, llvm_gen_tests(modules, module_count, context));
|
||||
unsigned count = vec_size(gen_contexts);
|
||||
for (unsigned i = 1; i < count; i++)
|
||||
{
|
||||
GenContext *other = gen_contexts[i];
|
||||
LLVMLinkModules2(first->module, other->module);
|
||||
gencontext_destroy(other);
|
||||
}
|
||||
vec_resize(gen_contexts, 1);
|
||||
return (void**)gen_contexts;
|
||||
}
|
||||
for (unsigned i = 0; i < module_count; i++)
|
||||
{
|
||||
GenContext *result = llvm_gen_module(modules[i], NULL);
|
||||
if (!result) continue;
|
||||
vec_add(gen_contexts, result);
|
||||
}
|
||||
vec_add(gen_contexts, llvm_gen_tests(modules, module_count, NULL));
|
||||
return (void**)gen_contexts;
|
||||
}
|
||||
|
||||
static GenContext *llvm_gen_module(Module *module, LLVMContextRef shared_context)
|
||||
{
|
||||
if (!vec_size(module->units)) return NULL;
|
||||
assert(intrinsics_setup);
|
||||
GenContext *gen_context = cmalloc(sizeof(GenContext));
|
||||
gencontext_init(gen_context, module);
|
||||
gencontext_init(gen_context, module, shared_context);
|
||||
gencontext_begin_module(gen_context);
|
||||
|
||||
VECEACH(module->units, j)
|
||||
{
|
||||
CompilationUnit *unit = module->units[j];
|
||||
FOREACH_BEGIN(CompilationUnit *unit, module->units)
|
||||
|
||||
gencontext_init_file_emit(gen_context, unit);
|
||||
gen_context->debug.compile_unit = unit->llvm.debug_compile_unit;
|
||||
gen_context->debug.file = unit->llvm.debug_file;
|
||||
|
||||
VECEACH(unit->methods, i)
|
||||
{
|
||||
llvm_emit_function_decl(gen_context, unit->methods[i]);
|
||||
}
|
||||
VECEACH(unit->types, i)
|
||||
{
|
||||
llvm_emit_type_decls(gen_context, unit->types[i]);
|
||||
}
|
||||
VECEACH(unit->enums, i)
|
||||
{
|
||||
llvm_emit_type_decls(gen_context, unit->enums[i]);
|
||||
}
|
||||
VECEACH(unit->functions, i)
|
||||
{
|
||||
Decl *func = unit->functions[i];
|
||||
FOREACH_BEGIN(Decl *initializer, unit->xxlizers)
|
||||
llvm_emit_xxlizer(gen_context, initializer);
|
||||
FOREACH_END();
|
||||
|
||||
FOREACH_BEGIN(Decl *method, unit->methods)
|
||||
llvm_emit_function_decl(gen_context, method);
|
||||
FOREACH_END();
|
||||
|
||||
FOREACH_BEGIN(Decl *type_decl, unit->types)
|
||||
llvm_emit_type_decls(gen_context, type_decl);
|
||||
FOREACH_END();
|
||||
|
||||
FOREACH_BEGIN(Decl *enum_decl, unit->enums)
|
||||
llvm_emit_type_decls(gen_context, enum_decl);
|
||||
FOREACH_END();
|
||||
|
||||
FOREACH_BEGIN(Decl *func, unit->functions)
|
||||
if (func->func_decl.attr_test)
|
||||
{
|
||||
if (!active_target.testing) continue;
|
||||
vec_add(module->tests, func);
|
||||
}
|
||||
llvm_emit_function_decl(gen_context, func);
|
||||
}
|
||||
if (unit->main_function && unit->main_function->is_synthetic)
|
||||
FOREACH_END();
|
||||
|
||||
if (active_target.type != TARGET_TYPE_TEST && unit->main_function && unit->main_function->is_synthetic)
|
||||
{
|
||||
llvm_emit_function_decl(gen_context, unit->main_function);
|
||||
}
|
||||
}
|
||||
|
||||
VECEACH(module->units, j)
|
||||
{
|
||||
CompilationUnit *unit = module->units[j];
|
||||
FOREACH_END();
|
||||
|
||||
FOREACH_BEGIN(CompilationUnit *unit, module->units)
|
||||
|
||||
gen_context->debug.compile_unit = unit->llvm.debug_compile_unit;
|
||||
gen_context->debug.file = unit->llvm.debug_file;
|
||||
|
||||
VECEACH(unit->vars, i)
|
||||
{
|
||||
llvm_get_ref(gen_context, unit->vars[i]);
|
||||
}
|
||||
VECEACH(unit->vars, i)
|
||||
{
|
||||
llvm_emit_global_variable_init(gen_context, unit->vars[i]);
|
||||
}
|
||||
VECEACH(unit->functions, i)
|
||||
{
|
||||
Decl *decl = unit->functions[i];
|
||||
FOREACH_BEGIN(Decl *var, unit->vars)
|
||||
llvm_get_ref(gen_context, var);
|
||||
FOREACH_END();
|
||||
|
||||
FOREACH_BEGIN(Decl *var, unit->vars)
|
||||
llvm_emit_global_variable_init(gen_context, var);
|
||||
FOREACH_END();
|
||||
|
||||
FOREACH_BEGIN(Decl *decl, unit->functions)
|
||||
if (decl->func_decl.attr_test && !active_target.testing) continue;
|
||||
if (decl->func_decl.body) llvm_emit_function_body(gen_context, decl);
|
||||
}
|
||||
if (unit->main_function && unit->main_function->is_synthetic)
|
||||
FOREACH_END();
|
||||
|
||||
if (active_target.type != TARGET_TYPE_TEST && unit->main_function && unit->main_function->is_synthetic)
|
||||
{
|
||||
llvm_emit_function_body(gen_context, unit->main_function);
|
||||
}
|
||||
|
||||
VECEACH(unit->methods, i)
|
||||
{
|
||||
Decl *decl = unit->methods[i];
|
||||
FOREACH_BEGIN(Decl *decl, unit->methods)
|
||||
if (decl->func_decl.body) llvm_emit_function_body(gen_context, decl);
|
||||
}
|
||||
FOREACH_END();
|
||||
|
||||
gencontext_end_file_emit(gen_context, unit);
|
||||
}
|
||||
|
||||
FOREACH_END();
|
||||
|
||||
llvm_emit_constructors_and_destructors(gen_context);
|
||||
|
||||
// EmitDeferred()
|
||||
|
||||
if (llvm_use_debug(gen_context))
|
||||
@@ -1182,6 +1325,11 @@ TypeSize llvm_store_size(GenContext *c, LLVMTypeRef type)
|
||||
return (TypeSize)LLVMStoreSizeOfType(c->target_data, type);
|
||||
}
|
||||
|
||||
TypeSize llvm_alloc_size(GenContext *c, LLVMTypeRef type)
|
||||
{
|
||||
return (TypeSize)aligned_offset((AlignSize)LLVMStoreSizeOfType(c->target_data, type), llvm_abi_alignment(c, type));
|
||||
}
|
||||
|
||||
void llvm_set_catch_exit(GenContext *c, LLVMBasicBlockRef block)
|
||||
{
|
||||
c->catch_block = block;
|
||||
|
||||
751
src/compiler/llvm_codegen_builtins.c
Normal file
751
src/compiler/llvm_codegen_builtins.c
Normal file
@@ -0,0 +1,751 @@
|
||||
// Copyright (c) 2022 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "llvm_codegen_internal.h"
|
||||
|
||||
INLINE void llvm_emit_reverse(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
llvm_emit_expr(c, result_value, args[0]);
|
||||
llvm_value_rvalue(c, result_value);
|
||||
Type *rtype = result_value->type;
|
||||
LLVMValueRef arg1 = result_value->value;
|
||||
LLVMValueRef arg2 = LLVMGetPoison(LLVMTypeOf(arg1));
|
||||
LLVMValueRef buff[128];
|
||||
unsigned elements = rtype->array.len;
|
||||
LLVMValueRef *mask_element = elements > 128 ? MALLOC(sizeof(LLVMValueRef)) : buff;
|
||||
LLVMTypeRef mask_element_type = llvm_get_type(c, type_int);
|
||||
for (unsigned i = 0; i < elements; i++)
|
||||
{
|
||||
mask_element[i] = LLVMConstInt(mask_element_type, elements - i - 1, false);
|
||||
}
|
||||
LLVMValueRef mask = LLVMConstVector(mask_element, elements);
|
||||
llvm_value_set(result_value, LLVMBuildShuffleVector(c->builder, arg1, arg2, mask, "reverse"), rtype);
|
||||
}
|
||||
|
||||
INLINE void llvm_emit_shufflevector(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
unsigned count = vec_size(args);
|
||||
LLVMValueRef arg1;
|
||||
LLVMValueRef arg2;
|
||||
LLVMValueRef mask;
|
||||
llvm_emit_expr(c, result_value, args[0]);
|
||||
llvm_value_rvalue(c, result_value);
|
||||
Type *rtype = result_value->type;
|
||||
arg1 = result_value->value;
|
||||
llvm_emit_expr(c, result_value, args[count - 1]);
|
||||
llvm_value_rvalue(c, result_value);
|
||||
mask = result_value->value;
|
||||
assert(LLVMIsConstant(mask));
|
||||
if (count == 2)
|
||||
{
|
||||
arg2 = LLVMGetPoison(LLVMTypeOf(arg1));
|
||||
}
|
||||
else
|
||||
{
|
||||
llvm_emit_expr(c, result_value, args[1]);
|
||||
llvm_value_rvalue(c, result_value);
|
||||
arg2 = result_value->value;
|
||||
}
|
||||
LLVMValueRef val = LLVMBuildShuffleVector(c->builder, arg1, arg2, mask, "shuffle");
|
||||
llvm_value_set(result_value, val, rtype);
|
||||
return;
|
||||
}
|
||||
|
||||
INLINE void llvm_emit_unreachable(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
{
|
||||
llvm_value_set(result_value, LLVMBuildUnreachable(c->builder), type_void);
|
||||
c->current_block = NULL;
|
||||
c->current_block_is_target = false;
|
||||
LLVMBasicBlockRef after_unreachable = llvm_basic_block_new(c, "after.unreachable");
|
||||
llvm_emit_block(c, after_unreachable);
|
||||
}
|
||||
|
||||
INLINE void llvm_emit_stacktrace(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
{
|
||||
if (!c->debug.enable_stacktrace)
|
||||
{
|
||||
llvm_value_set(result_value, llvm_get_zero(c, type_voidptr), type_voidptr);
|
||||
return;
|
||||
}
|
||||
llvm_value_set(result_value, llvm_emit_bitcast(c, c->debug.stack_slot, type_voidptr), type_voidptr);
|
||||
}
|
||||
|
||||
INLINE void llvm_emit_volatile_store(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
{
|
||||
BEValue value;
|
||||
llvm_emit_expr(c, &value, expr->call_expr.arguments[0]);
|
||||
llvm_emit_expr(c, result_value, expr->call_expr.arguments[1]);
|
||||
llvm_value_rvalue(c, &value);
|
||||
value.kind = BE_ADDRESS;
|
||||
BEValue store_value = *result_value;
|
||||
LLVMValueRef store = llvm_store(c, &value, &store_value);
|
||||
if (store) LLVMSetVolatile(store, true);
|
||||
}
|
||||
|
||||
INLINE void llvm_emit_volatile_load(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
{
|
||||
llvm_emit_expr(c, result_value, expr->call_expr.arguments[0]);
|
||||
llvm_value_rvalue(c, result_value);
|
||||
result_value->kind = BE_ADDRESS;
|
||||
result_value->type = type_lowering(result_value->type->pointer);
|
||||
llvm_value_rvalue(c, result_value);
|
||||
LLVMSetVolatile(result_value->value, true);
|
||||
}
|
||||
|
||||
static inline LLVMValueRef llvm_syscall_asm(GenContext *c, LLVMTypeRef func_type, char *call)
|
||||
{
|
||||
return LLVMGetInlineAsm(func_type, call, strlen(call),
|
||||
scratch_buffer_to_string(), scratch_buffer.len,
|
||||
true, true, LLVMInlineAsmDialectATT, /* can throw */ false);
|
||||
}
|
||||
|
||||
static inline void llvm_syscall_write_regs_to_scratch(const char** registers, unsigned args)
|
||||
{
|
||||
for (unsigned i = 0; i < args; i++)
|
||||
{
|
||||
scratch_buffer_append(",{");
|
||||
scratch_buffer_append(registers[i]);
|
||||
scratch_buffer_append("}");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void llvm_emit_syscall(GenContext *c, BEValue *be_value, Expr *expr)
|
||||
{
|
||||
unsigned arguments = vec_size(expr->call_expr.arguments);
|
||||
assert(arguments < 10 && "Only has room for 10");
|
||||
LLVMValueRef arg_results[10];
|
||||
LLVMTypeRef arg_types[10];
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMTypeRef type = llvm_get_type(c, type_uptr);
|
||||
for (unsigned i = 0; i < arguments; i++)
|
||||
{
|
||||
llvm_emit_expr(c, be_value, args[i]);
|
||||
llvm_value_rvalue(c, be_value);
|
||||
arg_results[i] = be_value->value;
|
||||
arg_types[i] = type;
|
||||
}
|
||||
LLVMTypeRef func_type = LLVMFunctionType(type, arg_types, arguments, false);
|
||||
scratch_buffer_clear();
|
||||
LLVMValueRef inline_asm;
|
||||
switch (platform_target.arch)
|
||||
{
|
||||
case ARCH_TYPE_AARCH64:
|
||||
case ARCH_TYPE_AARCH64_BE:
|
||||
scratch_buffer_append("={x0}");
|
||||
assert(arguments < 8);
|
||||
if (os_is_apple(platform_target.os))
|
||||
{
|
||||
static char const *regs[] = { "x16", "x0", "x1", "x2", "x3", "x4", "x5" };
|
||||
llvm_syscall_write_regs_to_scratch(regs, arguments);
|
||||
}
|
||||
else
|
||||
{
|
||||
static char const *regs[] = { "x8", "x0", "x1", "x2", "x3", "x4", "x5" };
|
||||
llvm_syscall_write_regs_to_scratch(regs, arguments);
|
||||
}
|
||||
inline_asm = llvm_syscall_asm(c, func_type, "svc #0x80");
|
||||
break;
|
||||
case ARCH_TYPE_X86:
|
||||
{
|
||||
scratch_buffer_append("={eax}");
|
||||
assert(arguments < 8);
|
||||
static char const *regs[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
|
||||
llvm_syscall_write_regs_to_scratch(regs, arguments < 6 ? arguments : 6);
|
||||
if (arguments == 7)
|
||||
{
|
||||
scratch_buffer_append(",rm");
|
||||
char *asm_str = "push %[arg6]\npush %%ebp\nmov 4(%%esp), %%ebp\nint $0x80\npop %%ebp\nadd $4, %%esp";
|
||||
inline_asm = llvm_syscall_asm(c, func_type, asm_str);
|
||||
break;
|
||||
}
|
||||
inline_asm = llvm_syscall_asm(c, func_type, "int $0x80");
|
||||
break;
|
||||
}
|
||||
case ARCH_TYPE_X86_64:
|
||||
scratch_buffer_append("={rax}");
|
||||
assert(arguments < 8);
|
||||
{
|
||||
static char const *regs[] = { "rax", "rdi", "rsi", "rdx", "r10", "r8", "r9" };
|
||||
llvm_syscall_write_regs_to_scratch(regs, arguments);
|
||||
}
|
||||
// Check clobbers on different OSes
|
||||
scratch_buffer_append(",~{rcx},~{r11},~{memory}");
|
||||
inline_asm = llvm_syscall_asm(c, func_type, "syscall");
|
||||
break;
|
||||
case ARCH_UNSUPPORTED:
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
LLVMValueRef result = LLVMBuildCall2(c->builder, func_type, inline_asm, arg_results, arguments, "syscall");
|
||||
llvm_value_set(be_value, result, type_uptr);
|
||||
}
|
||||
|
||||
INLINE unsigned llvm_intrinsic_by_type(Type *type, unsigned int_intrinsic, unsigned uint_intrinsic, unsigned float_intrinsic)
|
||||
{
|
||||
type = type_flatten(type);
|
||||
RETRY:
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case ALL_SIGNED_INTS:
|
||||
return int_intrinsic;
|
||||
case TYPE_BOOL:
|
||||
case ALL_UNSIGNED_INTS:
|
||||
return uint_intrinsic;
|
||||
case ALL_FLOATS:
|
||||
return float_intrinsic;
|
||||
case TYPE_VECTOR:
|
||||
type = type->array.base;
|
||||
goto RETRY;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void llvm_emit_intrinsic_args(GenContext *c, Expr **args, LLVMValueRef *slots, unsigned count)
|
||||
{
|
||||
BEValue be_value;
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
{
|
||||
llvm_emit_expr(c, &be_value, args[i]);
|
||||
llvm_value_rvalue(c, &be_value);
|
||||
slots[i] = be_value.value;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE void llvm_emit_memcpy_builtin(GenContext *c, unsigned intrinsic, BEValue *be_value, Expr *expr)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMValueRef arg_slots[4];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, 4);
|
||||
arg_slots[0] = llvm_emit_bitcast(c, arg_slots[0], type_voidptr);
|
||||
arg_slots[1] = llvm_emit_bitcast(c, arg_slots[1], type_voidptr);
|
||||
LLVMTypeRef call_type[3];
|
||||
call_type[0] = call_type[1] = llvm_get_type(c, type_voidptr);
|
||||
call_type[2] = llvm_get_type(c, type_usize);
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, call_type, 3, arg_slots, 4);
|
||||
assert(args[4]->const_expr.const_kind == CONST_INTEGER);
|
||||
assert(args[5]->const_expr.const_kind == CONST_INTEGER);
|
||||
uint64_t dst_align = int_to_u64(args[4]->const_expr.ixx);
|
||||
uint64_t src_align = int_to_u64(args[5]->const_expr.ixx);
|
||||
if (dst_align > 0) llvm_attribute_add_call(c, result, attribute_id.align, 1, dst_align);
|
||||
if (src_align > 0) llvm_attribute_add_call(c, result, attribute_id.align, 2, src_align);
|
||||
llvm_value_set(be_value, result, type_void);
|
||||
}
|
||||
|
||||
INLINE void llvm_emit_memmove_builtin(GenContext *c, BEValue *be_value, Expr *expr)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMValueRef arg_slots[4];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, 4);
|
||||
arg_slots[0] = llvm_emit_bitcast(c, arg_slots[0], type_voidptr);
|
||||
arg_slots[1] = llvm_emit_bitcast(c, arg_slots[1], type_voidptr);
|
||||
LLVMTypeRef call_type[3];
|
||||
call_type[0] = call_type[1] = llvm_get_type(c, type_voidptr);
|
||||
call_type[2] = llvm_get_type(c, type_usize);
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic_id.memmove, call_type, 3, arg_slots, 4);
|
||||
assert(args[4]->const_expr.const_kind == CONST_INTEGER);
|
||||
assert(args[5]->const_expr.const_kind == CONST_INTEGER);
|
||||
uint64_t dst_align = int_to_u64(args[4]->const_expr.ixx);
|
||||
uint64_t src_align = int_to_u64(args[5]->const_expr.ixx);
|
||||
if (dst_align > 0) llvm_attribute_add_call(c, result, attribute_id.align, 1, dst_align);
|
||||
if (src_align > 0) llvm_attribute_add_call(c, result, attribute_id.align, 2, src_align);
|
||||
llvm_value_set(be_value, result, type_void);
|
||||
}
|
||||
|
||||
INLINE void llvm_emit_memset_builtin(GenContext *c, unsigned intrinsic, BEValue *be_value, Expr *expr)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMValueRef arg_slots[4];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, 4);
|
||||
arg_slots[0] = llvm_emit_bitcast(c, arg_slots[0], type_voidptr);
|
||||
LLVMTypeRef call_type[2] = { llvm_get_type(c, type_voidptr), llvm_get_type(c, type_usize) };
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, call_type, 2, arg_slots, 4);
|
||||
assert(args[4]->const_expr.const_kind == CONST_INTEGER);
|
||||
uint64_t dst_align = int_to_u64(args[4]->const_expr.ixx);
|
||||
if (dst_align > 0) llvm_attribute_add_call(c, result, attribute_id.align, 1, dst_align);
|
||||
llvm_value_set(be_value, result, type_void);
|
||||
}
|
||||
|
||||
INLINE void llvm_emit_prefetch(GenContext *c, BEValue *be_value, Expr *expr)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMValueRef arg_slots[4];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, 3);
|
||||
arg_slots[3] = llvm_const_int(c, type_int, 1);
|
||||
LLVMTypeRef call_type[1] = { llvm_get_type(c, type_voidptr) };
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic_id.prefetch, call_type, 1, arg_slots, 4);
|
||||
llvm_value_set(be_value, result, type_void);
|
||||
}
|
||||
|
||||
void llvm_emit_reduce_int_builtin(GenContext *c, unsigned intrinsic, BEValue *be_value, Expr *expr)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMValueRef arg_slots[1];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, 1);
|
||||
LLVMTypeRef call_type[1] = { LLVMTypeOf(arg_slots[0]) };
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, call_type, 1, arg_slots, 1);
|
||||
llvm_value_set(be_value, result, expr->type);
|
||||
}
|
||||
|
||||
void llvm_emit_reduce_float_builtin(GenContext *c, unsigned intrinsic, BEValue *be_value, Expr *expr)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMValueRef arg_slots[2];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, 2);
|
||||
LLVMTypeRef call_type[1] = { LLVMTypeOf(arg_slots[1]) };
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, call_type, 1, arg_slots, 2);
|
||||
llvm_value_set(be_value, result, expr->type);
|
||||
}
|
||||
|
||||
void llvm_emit_int_with_bool_builtin(GenContext *c, unsigned intrinsic, BEValue *be_value, Expr *expr, bool bool_val)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMValueRef arg_slots[2];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, 1);
|
||||
arg_slots[1] = llvm_get_zero_raw(c->bool_type);
|
||||
LLVMTypeRef call_type[1] = { LLVMTypeOf(arg_slots[0]) };
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, call_type, 1, arg_slots, 2);
|
||||
llvm_value_set(be_value, result, expr->type);
|
||||
}
|
||||
|
||||
void llvm_emit_pow_int_builtin(GenContext *c, BEValue *be_value, Expr *expr)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMValueRef arg_slots[2];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, 2);
|
||||
LLVMTypeRef call_type[2] = { LLVMTypeOf(arg_slots[0]), LLVMTypeOf(arg_slots[1]) };
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic_id.powi, call_type, 2, arg_slots, 2);
|
||||
llvm_value_set(be_value, result, expr->type);
|
||||
}
|
||||
|
||||
void llvm_emit_3_variant_builtin(GenContext *c, BEValue *be_value, Expr *expr, unsigned sid, unsigned uid, unsigned fid)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
unsigned count = vec_size(args);
|
||||
assert(count <= 3);
|
||||
LLVMValueRef arg_slots[3];
|
||||
unsigned intrinsic = llvm_intrinsic_by_type(args[0]->type, sid, uid, fid);
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, count);
|
||||
LLVMTypeRef call_type[1] = { LLVMTypeOf(arg_slots[0]) };
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, call_type, 1, arg_slots, count);
|
||||
llvm_value_set(be_value, result, expr->type);
|
||||
}
|
||||
|
||||
void llvm_emit_abs_builtin(GenContext *c, BEValue *be_value, Expr *expr)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMValueRef arg_slots[2];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, 1);
|
||||
unsigned intrinsic = llvm_intrinsic_by_type(args[0]->type, intrinsic_id.abs, intrinsic_id.abs, intrinsic_id.fabs);
|
||||
LLVMTypeRef call_type[1] = { LLVMTypeOf(arg_slots[0]) };
|
||||
LLVMValueRef result;
|
||||
if (intrinsic == intrinsic_id.abs)
|
||||
{
|
||||
arg_slots[1] = llvm_get_zero_raw(c->bool_type);
|
||||
result = llvm_emit_call_intrinsic(c, intrinsic, call_type, 1, arg_slots, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = llvm_emit_call_intrinsic(c, intrinsic, call_type, 1, arg_slots, 1);
|
||||
}
|
||||
llvm_value_set(be_value, result, expr->type);
|
||||
}
|
||||
|
||||
void llvm_emit_simple_builtin(GenContext *c, BEValue *be_value, Expr *expr, unsigned intrinsic)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
unsigned count = vec_size(args);
|
||||
assert(count <= 3);
|
||||
assert(count > 0);
|
||||
LLVMValueRef arg_slots[3];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, count);
|
||||
LLVMTypeRef call_type = LLVMTypeOf(arg_slots[0]);
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, &call_type, 1, arg_slots, count);
|
||||
llvm_value_set(be_value, result, expr->type);
|
||||
}
|
||||
|
||||
static void llvm_emit_overflow_builtin(GenContext *c, BEValue *be_value, Expr *expr, unsigned intrinsic_signed, unsigned intrinsic_unsigned)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMValueRef arg_slots[2];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, 2);
|
||||
BEValue ref;
|
||||
Expr *ret_addr = args[2];
|
||||
llvm_emit_expr(c, &ref, ret_addr);
|
||||
llvm_value_rvalue(c, &ref);
|
||||
// Note that we can make additional improvements here!
|
||||
llvm_value_set_address(&ref, ref.value, ref.type->pointer, type_abi_alignment(ref.type->pointer));
|
||||
LLVMTypeRef call_type[1] = { LLVMTypeOf(arg_slots[0]) };
|
||||
unsigned intrinsic = type_is_signed(type_lowering(args[0]->type)) ? intrinsic_signed : intrinsic_unsigned;
|
||||
LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic, call_type, 1, arg_slots, 2);
|
||||
LLVMValueRef failed = llvm_emit_extract_value(c, result, 1);
|
||||
LLVMValueRef value = llvm_emit_extract_value(c, result, 0);
|
||||
llvm_store_raw(c, &ref, value);
|
||||
llvm_value_set(be_value, failed, type_bool);
|
||||
}
|
||||
|
||||
static void llvm_emit_wrap_builtin(GenContext *c, BEValue *result_value, Expr *expr, BuiltinFunction func)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMValueRef arg_slots[2];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, func == BUILTIN_EXACT_NEG ? 1 : 2);
|
||||
Type *base_type = type_lowering(args[0]->type);
|
||||
if (base_type->type_kind == TYPE_VECTOR) base_type = base_type->array.base;
|
||||
assert(type_is_integer(base_type));
|
||||
bool is_signed = type_is_signed(base_type);
|
||||
LLVMValueRef res;
|
||||
switch (func)
|
||||
{
|
||||
case BUILTIN_EXACT_NEG:
|
||||
res = LLVMBuildNeg(c->builder, arg_slots[0], "eneg");
|
||||
break;
|
||||
case BUILTIN_EXACT_SUB:
|
||||
res = LLVMBuildSub(c->builder, arg_slots[0], arg_slots[1], "esub");
|
||||
break;
|
||||
case BUILTIN_EXACT_ADD:
|
||||
res = LLVMBuildAdd(c->builder, arg_slots[0], arg_slots[1], "eadd");
|
||||
break;
|
||||
case BUILTIN_EXACT_MUL:
|
||||
res = LLVMBuildMul(c->builder, arg_slots[0], arg_slots[1], "emul");
|
||||
break;
|
||||
case BUILTIN_EXACT_DIV:
|
||||
if (type_is_signed(base_type))
|
||||
{
|
||||
res = LLVMBuildSDiv(c->builder, arg_slots[0], arg_slots[1], "esdiv");
|
||||
}
|
||||
else
|
||||
{
|
||||
res = LLVMBuildUDiv(c->builder, arg_slots[0], arg_slots[1], "eudiv");
|
||||
}
|
||||
break;
|
||||
case BUILTIN_EXACT_MOD:
|
||||
if (type_is_signed(base_type))
|
||||
{
|
||||
res = LLVMBuildSRem(c->builder, arg_slots[0], arg_slots[1], "eumod");
|
||||
}
|
||||
else
|
||||
{
|
||||
res = LLVMBuildSDiv(c->builder, arg_slots[0], arg_slots[1], "esmod");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
llvm_value_set(result_value, res, expr->type);
|
||||
}
|
||||
|
||||
static void llvm_emit_veccomp(GenContext *c, BEValue *value, Expr *expr, BuiltinFunction fn)
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
unsigned count = vec_size(args);
|
||||
assert(count == 2);
|
||||
|
||||
LLVMValueRef mask;
|
||||
llvm_emit_expr(c, value, args[0]);
|
||||
llvm_value_rvalue(c, value);
|
||||
LLVMValueRef lhs_value = value->value;
|
||||
llvm_emit_expr(c, value, args[1]);
|
||||
llvm_value_rvalue(c, value);
|
||||
LLVMValueRef rhs_value = value->value;
|
||||
LLVMValueRef res;
|
||||
if (type_flat_is_floatlike(args[0]->type))
|
||||
{
|
||||
switch (fn)
|
||||
{
|
||||
case BUILTIN_VECCOMPEQ:
|
||||
// Unordered?
|
||||
res = LLVMBuildFCmp(c->builder, LLVMRealOEQ, lhs_value, rhs_value, "eq");
|
||||
break;
|
||||
case BUILTIN_VECCOMPNE:
|
||||
// Unordered?
|
||||
res = LLVMBuildFCmp(c->builder, LLVMRealONE, lhs_value, rhs_value, "neq");
|
||||
break;
|
||||
case BUILTIN_VECCOMPGE:
|
||||
res = LLVMBuildFCmp(c->builder, LLVMRealOGE, lhs_value, rhs_value, "ge");
|
||||
break;
|
||||
case BUILTIN_VECCOMPGT:
|
||||
res = LLVMBuildFCmp(c->builder, LLVMRealOGT, lhs_value, rhs_value, "gt");
|
||||
break;
|
||||
case BUILTIN_VECCOMPLE:
|
||||
res = LLVMBuildFCmp(c->builder, LLVMRealOLE, lhs_value, rhs_value, "le");
|
||||
break;
|
||||
case BUILTIN_VECCOMPLT:
|
||||
res = LLVMBuildFCmp(c->builder, LLVMRealOLT, lhs_value, rhs_value, "lt");
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool is_signed = type_is_signed(args[0]->type->array.base);
|
||||
switch (fn)
|
||||
{
|
||||
case BUILTIN_VECCOMPEQ:
|
||||
// Unordered?
|
||||
res = LLVMBuildICmp(c->builder, LLVMIntEQ, lhs_value, rhs_value, "eq");
|
||||
break;
|
||||
case BUILTIN_VECCOMPNE:
|
||||
// Unordered?
|
||||
res = LLVMBuildICmp(c->builder, LLVMIntNE, lhs_value, rhs_value, "neq");
|
||||
break;
|
||||
case BUILTIN_VECCOMPGE:
|
||||
res = LLVMBuildICmp(c->builder, is_signed ? LLVMIntSGE : LLVMIntUGE, lhs_value, rhs_value, "ge");
|
||||
break;
|
||||
case BUILTIN_VECCOMPGT:
|
||||
res = LLVMBuildICmp(c->builder, is_signed ? LLVMIntSGT : LLVMIntUGT, lhs_value, rhs_value, "gt");
|
||||
break;
|
||||
case BUILTIN_VECCOMPLE:
|
||||
res = LLVMBuildICmp(c->builder, is_signed ? LLVMIntSLE : LLVMIntULE, lhs_value, rhs_value, "le");
|
||||
break;
|
||||
case BUILTIN_VECCOMPLT:
|
||||
res = LLVMBuildICmp(c->builder, is_signed ? LLVMIntSLT : LLVMIntULT, lhs_value, rhs_value, "lt");
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
llvm_value_set(value, res, expr->type);
|
||||
return;
|
||||
|
||||
}
|
||||
void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
{
|
||||
BuiltinFunction func = exprptr(expr->call_expr.function)->builtin_expr.builtin;
|
||||
unsigned intrinsic;
|
||||
LLVMValueRef val = NULL;
|
||||
switch (func)
|
||||
{
|
||||
case BUILTIN_UNREACHABLE:
|
||||
llvm_emit_unreachable(c, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_SHUFFLEVECTOR:
|
||||
llvm_emit_shufflevector(c, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_FRAMEADDRESS:
|
||||
{
|
||||
LLVMTypeRef type[2] = { llvm_get_type(c, type_voidptr), llvm_get_type(c, type_int) };
|
||||
LLVMValueRef value = LLVMConstNull(type[1]);
|
||||
value = llvm_emit_call_intrinsic(c, intrinsic_id.frameaddress, type, 1, &value, 1);
|
||||
llvm_value_set(result_value, value, expr->type);
|
||||
return;
|
||||
}
|
||||
case BUILTIN_VECCOMPLT:
|
||||
case BUILTIN_VECCOMPLE:
|
||||
case BUILTIN_VECCOMPNE:
|
||||
case BUILTIN_VECCOMPEQ:
|
||||
case BUILTIN_VECCOMPGT:
|
||||
case BUILTIN_VECCOMPGE:
|
||||
llvm_emit_veccomp(c, result_value, expr, func);
|
||||
return;
|
||||
case BUILTIN_REVERSE:
|
||||
llvm_emit_reverse(c, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_STACKTRACE:
|
||||
llvm_emit_stacktrace(c, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_VOLATILE_STORE:
|
||||
llvm_emit_volatile_store(c, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_VOLATILE_LOAD:
|
||||
llvm_emit_volatile_load(c, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_SYSCALL:
|
||||
llvm_emit_syscall(c, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_MEMCOPY:
|
||||
llvm_emit_memcpy_builtin(c, intrinsic_id.memcpy, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_MEMCOPY_INLINE:
|
||||
llvm_emit_memcpy_builtin(c, intrinsic_id.memcpy_inline, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_MEMMOVE:
|
||||
llvm_emit_memmove_builtin(c, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_MEMSET:
|
||||
llvm_emit_memset_builtin(c, intrinsic_id.memset, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_MEMSET_INLINE:
|
||||
llvm_emit_memset_builtin(c, intrinsic_id.memset_inline, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_SYSCLOCK:
|
||||
llvm_value_set(result_value, llvm_emit_call_intrinsic(c, intrinsic_id.readcyclecounter, NULL, 0, NULL, 0), expr->type);
|
||||
return;
|
||||
case BUILTIN_TRAP:
|
||||
llvm_value_set(result_value, llvm_emit_call_intrinsic(c, intrinsic_id.trap, NULL, 0, NULL, 0), type_void);
|
||||
return;
|
||||
case BUILTIN_PREFETCH:
|
||||
llvm_emit_prefetch(c, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_REDUCE_AND:
|
||||
llvm_emit_reduce_int_builtin(c, intrinsic_id.vector_reduce_and, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_REDUCE_OR:
|
||||
llvm_emit_reduce_int_builtin(c, intrinsic_id.vector_reduce_or, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_REDUCE_MIN:
|
||||
llvm_emit_3_variant_builtin(c, result_value, expr, intrinsic_id.vector_reduce_smin, intrinsic_id.vector_reduce_umin, intrinsic_id.vector_reduce_fmin);
|
||||
return;
|
||||
case BUILTIN_REDUCE_MAX:
|
||||
llvm_emit_3_variant_builtin(c, result_value, expr, intrinsic_id.vector_reduce_smax, intrinsic_id.vector_reduce_umax, intrinsic_id.vector_reduce_fmax);
|
||||
return;
|
||||
case BUILTIN_REDUCE_XOR:
|
||||
llvm_emit_reduce_int_builtin(c, intrinsic_id.vector_reduce_xor, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_REDUCE_ADD:
|
||||
llvm_emit_reduce_int_builtin(c, intrinsic_id.vector_reduce_add, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_REDUCE_MUL:
|
||||
llvm_emit_reduce_int_builtin(c, intrinsic_id.vector_reduce_mul, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_REDUCE_FADD:
|
||||
llvm_emit_reduce_float_builtin(c, intrinsic_id.vector_reduce_fadd, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_REDUCE_FMUL:
|
||||
llvm_emit_reduce_float_builtin(c, intrinsic_id.vector_reduce_fmul, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_EXACT_DIV:
|
||||
case BUILTIN_EXACT_ADD:
|
||||
case BUILTIN_EXACT_MUL:
|
||||
case BUILTIN_EXACT_SUB:
|
||||
case BUILTIN_EXACT_MOD:
|
||||
case BUILTIN_EXACT_NEG:
|
||||
llvm_emit_wrap_builtin(c, result_value, expr, func);
|
||||
return;
|
||||
case BUILTIN_OVERFLOW_ADD:
|
||||
llvm_emit_overflow_builtin(c, result_value, expr, intrinsic_id.sadd_overflow, intrinsic_id.uadd_overflow);
|
||||
return;
|
||||
case BUILTIN_OVERFLOW_SUB:
|
||||
llvm_emit_overflow_builtin(c, result_value, expr, intrinsic_id.ssub_overflow, intrinsic_id.usub_overflow);
|
||||
return;
|
||||
case BUILTIN_OVERFLOW_MUL:
|
||||
llvm_emit_overflow_builtin(c, result_value, expr, intrinsic_id.smul_overflow, intrinsic_id.umul_overflow);
|
||||
return;
|
||||
case BUILTIN_CTTZ:
|
||||
llvm_emit_int_with_bool_builtin(c, intrinsic_id.cttz, result_value, expr, false);
|
||||
return;
|
||||
case BUILTIN_CTLZ:
|
||||
llvm_emit_int_with_bool_builtin(c, intrinsic_id.ctlz, result_value, expr, false);
|
||||
return;
|
||||
case BUILTIN_MAX:
|
||||
llvm_emit_3_variant_builtin(c, result_value, expr, intrinsic_id.smax, intrinsic_id.umax, intrinsic_id.maxnum);
|
||||
return;
|
||||
case BUILTIN_MIN:
|
||||
llvm_emit_3_variant_builtin(c, result_value, expr, intrinsic_id.smin, intrinsic_id.umin, intrinsic_id.minnum);
|
||||
return;
|
||||
case BUILTIN_SAT_SHL:
|
||||
llvm_emit_3_variant_builtin(c, result_value, expr, intrinsic_id.sshl_sat, intrinsic_id.ushl_sat, 0);
|
||||
return;
|
||||
case BUILTIN_SAT_ADD:
|
||||
llvm_emit_3_variant_builtin(c, result_value, expr, intrinsic_id.sadd_sat, intrinsic_id.uadd_sat, 0);
|
||||
return;
|
||||
case BUILTIN_SAT_SUB:
|
||||
llvm_emit_3_variant_builtin(c, result_value, expr, intrinsic_id.ssub_sat, intrinsic_id.usub_sat, 0);
|
||||
return;
|
||||
case BUILTIN_ABS:
|
||||
llvm_emit_abs_builtin(c, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_POW_INT:
|
||||
llvm_emit_pow_int_builtin(c, result_value, expr);
|
||||
return;
|
||||
case BUILTIN_BITREVERSE:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.bitreverse);
|
||||
return;
|
||||
case BUILTIN_BSWAP:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.bswap);
|
||||
return;
|
||||
case BUILTIN_CEIL:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.ceil);
|
||||
return;
|
||||
case BUILTIN_COS:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.cos);
|
||||
return;
|
||||
case BUILTIN_COPYSIGN:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.copysign);
|
||||
return;
|
||||
case BUILTIN_FLOOR:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.floor);
|
||||
return;
|
||||
case BUILTIN_EXP:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.exp);
|
||||
return;
|
||||
case BUILTIN_EXP2:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.exp2);
|
||||
return;
|
||||
case BUILTIN_FMA:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.fma);
|
||||
return;
|
||||
case BUILTIN_FMULADD:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.fmuladd);
|
||||
return;
|
||||
case BUILTIN_FSHL:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.fshl);
|
||||
return;
|
||||
case BUILTIN_FSHR:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.fshr);
|
||||
return;
|
||||
case BUILTIN_GET_ROUNDING_MODE:
|
||||
llvm_value_set(result_value, llvm_emit_call_intrinsic(c, intrinsic_id.flt_rounds, NULL, 0, NULL, 0), expr->type);
|
||||
return;
|
||||
case BUILTIN_LOG:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.log);
|
||||
return;
|
||||
case BUILTIN_LOG2:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.log2);
|
||||
return;
|
||||
case BUILTIN_LOG10:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.log10);
|
||||
return;
|
||||
case BUILTIN_POW:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.pow);
|
||||
return;
|
||||
case BUILTIN_NEARBYINT:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.nearbyint);
|
||||
return;
|
||||
case BUILTIN_POPCOUNT:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.ctpop);
|
||||
return;
|
||||
case BUILTIN_RINT:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.rint);
|
||||
return;
|
||||
case BUILTIN_ROUND:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.round);
|
||||
return;
|
||||
case BUILTIN_ROUNDEVEN:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.roundeven);
|
||||
return;
|
||||
case BUILTIN_SET_ROUNDING_MODE:
|
||||
{
|
||||
Expr **args = expr->call_expr.arguments;
|
||||
LLVMValueRef arg_slots[1];
|
||||
llvm_emit_intrinsic_args(c, args, arg_slots, 1);
|
||||
llvm_value_set(result_value, llvm_emit_call_intrinsic(c, intrinsic_id.set_rounding, NULL, 0, arg_slots, 1), type_void);
|
||||
}
|
||||
return;
|
||||
case BUILTIN_SIN:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.sin);
|
||||
return;
|
||||
case BUILTIN_SQRT:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.sqrt);
|
||||
return;
|
||||
case BUILTIN_TRUNC:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.trunc);
|
||||
return;
|
||||
case BUILTIN_LRINT:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.lrint);
|
||||
return;
|
||||
case BUILTIN_LROUND:
|
||||
llvm_emit_simple_builtin(c, result_value, expr, intrinsic_id.lround);
|
||||
return;
|
||||
case BUILTIN_LLRINT:
|
||||
TODO
|
||||
case BUILTIN_LLROUND:
|
||||
TODO
|
||||
case BUILTIN_NONE:
|
||||
UNREACHABLE
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
@@ -290,7 +290,7 @@ void c_abi_func_create_riscv(FunctionPrototype *prototype)
|
||||
unsigned arg_gprs_left = is_ret_indirect ? gpr - 1 : gpr;
|
||||
unsigned arg_fprs_left = platform_target.riscv.flen ? fpr : 0;
|
||||
|
||||
// If we have a failable, then the return type is a parameter.
|
||||
// If we have an optional, then the return type is a parameter.
|
||||
if (prototype->ret_by_ref)
|
||||
{
|
||||
prototype->ret_by_ref_abi_info = riscv_classify_argument_type(type_get_ptr(type_lowering(prototype->ret_by_ref_type)),
|
||||
|
||||
@@ -388,8 +388,8 @@ static void x64_classify(Type *type, ByteSize offset_base, X64Class *lo_class, X
|
||||
case TYPE_ANYERR:
|
||||
case TYPE_FAULTTYPE:
|
||||
case TYPE_BITSTRUCT:
|
||||
case TYPE_FAILABLE:
|
||||
case TYPE_FAILABLE_ANY:
|
||||
case TYPE_OPTIONAL:
|
||||
case TYPE_OPTIONAL_ANY:
|
||||
case CT_TYPES:
|
||||
UNREACHABLE
|
||||
case TYPE_VOID:
|
||||
@@ -436,6 +436,9 @@ static void x64_classify(Type *type, ByteSize offset_base, X64Class *lo_class, X
|
||||
case TYPE_VECTOR:
|
||||
x64_classify_vector(type, offset_base, current, lo_class, hi_class, named);
|
||||
break;
|
||||
case TYPE_SCALED_VECTOR:
|
||||
*current = CLASS_MEMORY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,8 +583,8 @@ AbiType x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_typ
|
||||
case TYPE_ANYERR:
|
||||
case TYPE_FAULTTYPE:
|
||||
case TYPE_BITSTRUCT:
|
||||
case TYPE_FAILABLE:
|
||||
case TYPE_FAILABLE_ANY:
|
||||
case TYPE_OPTIONAL:
|
||||
case TYPE_OPTIONAL_ANY:
|
||||
case CT_TYPES:
|
||||
UNREACHABLE
|
||||
case TYPE_I128:
|
||||
@@ -593,6 +596,8 @@ AbiType x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_typ
|
||||
case TYPE_UNION:
|
||||
case TYPE_VECTOR:
|
||||
break;
|
||||
case TYPE_SCALED_VECTOR:
|
||||
return (AbiType) { .type = type };
|
||||
}
|
||||
ByteSize size = type_size(source_type);
|
||||
assert(size != source_offset);
|
||||
|
||||
@@ -121,10 +121,12 @@ static bool x86_should_return_type_in_reg(Type *type)
|
||||
case TYPE_ANYERR:
|
||||
case TYPE_BITSTRUCT:
|
||||
case CT_TYPES:
|
||||
case TYPE_FAILABLE:
|
||||
case TYPE_FAILABLE_ANY:
|
||||
case TYPE_OPTIONAL:
|
||||
case TYPE_OPTIONAL_ANY:
|
||||
case TYPE_FLEXIBLE_ARRAY:
|
||||
UNREACHABLE
|
||||
case TYPE_SCALED_VECTOR:
|
||||
return false;
|
||||
case ALL_INTS:
|
||||
case ALL_FLOATS:
|
||||
case TYPE_BOOL:
|
||||
@@ -145,7 +147,7 @@ static bool x86_should_return_type_in_reg(Type *type)
|
||||
Decl** members = type->decl->strukt.members;
|
||||
VECEACH (members, i)
|
||||
{
|
||||
Type *member_type = members[i]->type;
|
||||
Type *member_type = members[i]->type->canonical;
|
||||
if (!x86_should_return_type_in_reg(member_type)) return false;
|
||||
}
|
||||
return true;
|
||||
@@ -586,8 +588,8 @@ static ABIArgInfo *x86_classify_argument(CallABI call, Regs *regs, Type *type)
|
||||
case TYPE_FUNC:
|
||||
case TYPE_TYPEID:
|
||||
case TYPE_BITSTRUCT:
|
||||
case TYPE_FAILABLE:
|
||||
case TYPE_FAILABLE_ANY:
|
||||
case TYPE_OPTIONAL:
|
||||
case TYPE_OPTIONAL_ANY:
|
||||
case CT_TYPES:
|
||||
case TYPE_FLEXIBLE_ARRAY:
|
||||
UNREACHABLE
|
||||
@@ -604,6 +606,8 @@ static ABIArgInfo *x86_classify_argument(CallABI call, Regs *regs, Type *type)
|
||||
case TYPE_ANY:
|
||||
case TYPE_ARRAY:
|
||||
return x86_classify_aggregate(call, regs, type);
|
||||
case TYPE_SCALED_VECTOR:
|
||||
TODO
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
@@ -659,7 +663,7 @@ void c_abi_func_create_x86(FunctionPrototype *prototype)
|
||||
regs.int_regs = 3;
|
||||
}
|
||||
|
||||
// 4. Classify the return type. In the case of failable, we need to classify the failable itself as the
|
||||
// 4. Classify the return type. In the case of optional, we need to classify the optional itself as the
|
||||
// return type.
|
||||
prototype->ret_abi_info = x86_classify_return(prototype->call_abi, ®s, prototype->abi_ret_type);
|
||||
if (prototype->ret_by_ref)
|
||||
@@ -683,7 +687,7 @@ void c_abi_func_create_x86(FunctionPrototype *prototype)
|
||||
FATAL_ERROR("X86 vector call not supported");
|
||||
}
|
||||
prototype->abi_args = x86_create_params(prototype->call_abi, prototype->param_types, ®s);
|
||||
prototype->abi_varargs = x86_create_params(prototype->call_abi, prototype->param_types, ®s);
|
||||
prototype->abi_varargs = x86_create_params(prototype->call_abi, prototype->varargs, ®s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ void llvm_emit_debug_parameter(GenContext *c, Decl *parameter, unsigned index)
|
||||
unsigned col = parameter->span.col;
|
||||
if (col == 0) col = 1;
|
||||
|
||||
parameter->var.backend_debug_ref = LLVMDIBuilderCreateParameterVariable(
|
||||
parameter->var.backend_debug_ref = LLVMDIBuilderCreateParameterVariable(
|
||||
c->debug.builder,
|
||||
c->debug.function,
|
||||
name,
|
||||
@@ -290,20 +290,13 @@ static LLVMMetadataRef llvm_debug_simple_type(GenContext *context, Type *type, i
|
||||
|
||||
static LLVMMetadataRef llvm_debug_pointer_type(GenContext *c, Type *type)
|
||||
{
|
||||
if (!type->pointer->backend_debug_type)
|
||||
{
|
||||
type->backend_debug_type = llvm_debug_forward_comp(c, type, type->name, NULL, NULL, LLVMDIFlagZero);
|
||||
}
|
||||
LLVMMetadataRef real = LLVMDIBuilderCreatePointerType(c->debug.builder,
|
||||
llvm_get_debug_type(c, type->pointer),
|
||||
type_size(type) * 8,
|
||||
type_abi_alignment(type) * 8, 0,
|
||||
type->name, strlen(type->name));
|
||||
if (type->backend_debug_type)
|
||||
{
|
||||
LLVMMetadataReplaceAllUsesWith(type->backend_debug_type, real);
|
||||
}
|
||||
return real;
|
||||
LLVMMetadataRef inner = llvm_get_debug_type(c, type->pointer);
|
||||
if (type->backend_debug_type) return type->backend_debug_type;
|
||||
return LLVMDIBuilderCreatePointerType(c->debug.builder,
|
||||
inner,
|
||||
type_size(type) * 8,
|
||||
type_abi_alignment(type) * 8, 0,
|
||||
type->name, strlen(type->name));
|
||||
}
|
||||
|
||||
static LLVMMetadataRef llvm_debug_enum_type(GenContext *c, Type *type, LLVMMetadataRef scope)
|
||||
@@ -405,6 +398,7 @@ static LLVMMetadataRef llvm_debug_subarray_type(GenContext *c, Type *type)
|
||||
static LLVMMetadataRef llvm_debug_any_type(GenContext *c, Type *type)
|
||||
{
|
||||
LLVMMetadataRef forward = llvm_debug_forward_comp(c, type, type->name, NULL, NULL, LLVMDIFlagZero);
|
||||
|
||||
type->backend_debug_type = forward;
|
||||
|
||||
LLVMMetadataRef elements[2] = {
|
||||
@@ -478,6 +472,7 @@ static LLVMMetadataRef llvm_debug_typedef_type(GenContext *c, Type *type)
|
||||
if (type->backend_debug_type)
|
||||
{
|
||||
LLVMMetadataReplaceAllUsesWith(type->backend_debug_type, real);
|
||||
type->backend_debug_type = real;
|
||||
}
|
||||
return real;
|
||||
}
|
||||
@@ -539,8 +534,8 @@ static inline LLVMMetadataRef llvm_get_debug_type_internal(GenContext *c, Type *
|
||||
case TYPE_TYPEID:
|
||||
case CT_TYPES:
|
||||
UNREACHABLE
|
||||
case TYPE_FAILABLE:
|
||||
case TYPE_FAILABLE_ANY:
|
||||
case TYPE_OPTIONAL:
|
||||
case TYPE_OPTIONAL_ANY:
|
||||
// If this is reachable then we're not doing the proper lowering.
|
||||
UNREACHABLE
|
||||
case TYPE_BOOL:
|
||||
@@ -593,6 +588,8 @@ static inline LLVMMetadataRef llvm_get_debug_type_internal(GenContext *c, Type *
|
||||
return type->backend_debug_type = llvm_debug_errunion_type(c, type);
|
||||
case TYPE_ANY:
|
||||
return type->backend_debug_type = llvm_debug_any_type(c, type);
|
||||
case TYPE_SCALED_VECTOR:
|
||||
TODO
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,10 @@ static void llvm_emit_param_attributes(GenContext *c, LLVMValueRef function, ABI
|
||||
static inline void llvm_emit_return_value(GenContext *context, LLVMValueRef value);
|
||||
static void llvm_expand_from_args(GenContext *c, Type *type, LLVMValueRef ref, unsigned *index, AlignSize alignment);
|
||||
static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIArgInfo *info, unsigned *index);
|
||||
static inline void llvm_emit_parameter(GenContext *context, Decl *decl, ABIArgInfo *abi_info, unsigned *index, unsigned real_index);
|
||||
static inline void llvm_emit_func_parameter(GenContext *context, Decl *decl, ABIArgInfo *abi_info, unsigned *index, unsigned real_index);
|
||||
static inline void llvm_emit_body(GenContext *c, LLVMValueRef function, const char *module_name,
|
||||
const char *function_name,
|
||||
FileId file_id, FunctionPrototype *prototype, Signature *signature, Ast *body);
|
||||
|
||||
bool llvm_emit_check_block_branch(GenContext *context)
|
||||
{
|
||||
@@ -118,13 +121,9 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr
|
||||
case ABI_ARG_IGNORE:
|
||||
return;
|
||||
case ABI_ARG_INDIRECT:
|
||||
{
|
||||
// A simple memcopy, with alignment respected.
|
||||
LLVMValueRef pointer = llvm_get_next_param(c, index);
|
||||
llvm_emit_and_set_decl_alloca(c, decl);
|
||||
llvm_emit_memcpy_to_decl(c, decl, pointer, info->indirect.alignment);
|
||||
// Indirect is caller copied.
|
||||
decl->backend_ref = llvm_get_next_param(c, index);
|
||||
return;
|
||||
}
|
||||
case ABI_ARG_EXPAND_COERCE:
|
||||
{
|
||||
// Create the expand type:
|
||||
@@ -247,7 +246,7 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr
|
||||
}
|
||||
}
|
||||
}
|
||||
static inline void llvm_emit_parameter(GenContext *context, Decl *decl, ABIArgInfo *abi_info, unsigned *index, unsigned real_index)
|
||||
static inline void llvm_emit_func_parameter(GenContext *context, Decl *decl, ABIArgInfo *abi_info, unsigned *index, unsigned real_index)
|
||||
{
|
||||
assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM);
|
||||
|
||||
@@ -273,32 +272,41 @@ static inline void llvm_emit_return_value(GenContext *context, LLVMValueRef valu
|
||||
context->current_block_is_target = false;
|
||||
}
|
||||
|
||||
void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable)
|
||||
|
||||
void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *optional)
|
||||
{
|
||||
FunctionPrototype *prototype = c->cur_func_decl->type->function.prototype;
|
||||
FunctionPrototype *prototype = c->cur_func.prototype;
|
||||
|
||||
// If there is no prototype, this is a static initializer, so bail.
|
||||
if (!prototype)
|
||||
{
|
||||
llvm_emit_return_value(c, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
ABIArgInfo *info = prototype->ret_abi_info;
|
||||
|
||||
// If we have a failable it's always the return argument, so we need to copy
|
||||
// If we have an optional it's always the return argument, so we need to copy
|
||||
// the return value into the return value holder.
|
||||
LLVMValueRef return_out = c->return_out;
|
||||
Type *call_return_type = prototype->abi_ret_type;
|
||||
|
||||
BEValue no_fail;
|
||||
|
||||
// In this case we use the failable as the actual return.
|
||||
if (prototype->is_failable)
|
||||
// In this case we use the optional as the actual return.
|
||||
if (prototype->is_optional)
|
||||
{
|
||||
if (return_value && return_value->value)
|
||||
{
|
||||
llvm_store_to_ptr_aligned(c, c->return_out, return_value, type_alloca_alignment(return_value->type));
|
||||
}
|
||||
return_out = c->failable_out;
|
||||
if (!failable)
|
||||
return_out = c->optional_out;
|
||||
if (!optional)
|
||||
{
|
||||
llvm_value_set(&no_fail, llvm_get_zero(c, type_anyerr), type_anyerr);
|
||||
failable = &no_fail;
|
||||
optional = &no_fail;
|
||||
}
|
||||
return_value = failable;
|
||||
return_value = optional;
|
||||
}
|
||||
assert(return_value || info->kind == ABI_ARG_IGNORE);
|
||||
|
||||
@@ -388,7 +396,7 @@ DIRECT_RETURN:
|
||||
|
||||
void llvm_emit_return_implicit(GenContext *c)
|
||||
{
|
||||
Type *rtype_real = c->cur_func_decl->type->function.prototype->rtype;
|
||||
Type *rtype_real = c->cur_func.prototype ? c->cur_func.prototype->rtype : type_void;
|
||||
if (type_lowering(type_no_optional(rtype_real)) != type_void)
|
||||
{
|
||||
LLVMBuildUnreachable(c->builder);
|
||||
@@ -408,34 +416,47 @@ void llvm_emit_function_body(GenContext *c, Decl *decl)
|
||||
{
|
||||
DEBUG_LOG("Generating function %s.", decl->extname);
|
||||
assert(decl->backend_ref);
|
||||
llvm_emit_body(c,
|
||||
decl->backend_ref,
|
||||
decl->unit->module->name->module,
|
||||
decl->name,
|
||||
decl->span.file_id,
|
||||
decl->type->function.prototype,
|
||||
decl->func_decl.attr_naked ? NULL : &decl->func_decl.signature,
|
||||
astptr(decl->func_decl.body));
|
||||
}
|
||||
|
||||
void llvm_emit_body(GenContext *c, LLVMValueRef function, const char *module_name, const char *function_name,
|
||||
FileId file_id, FunctionPrototype *prototype, Signature *signature, Ast *body)
|
||||
{
|
||||
|
||||
bool emit_debug = llvm_use_debug(c);
|
||||
LLVMValueRef prev_function = c->function;
|
||||
LLVMBuilderRef prev_builder = c->builder;
|
||||
|
||||
|
||||
c->opt_var = NULL;
|
||||
c->catch_block = NULL;
|
||||
|
||||
c->function = decl->backend_ref;
|
||||
c->function = function;
|
||||
if (!function_name) function_name = "anonymous function";
|
||||
if (emit_debug)
|
||||
{
|
||||
c->debug.function = LLVMGetSubprogram(c->function);
|
||||
c->debug.function = LLVMGetSubprogram(function);
|
||||
if (c->debug.enable_stacktrace)
|
||||
{
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_append(decl->unit->module->name->module);
|
||||
scratch_buffer_append(module_name);
|
||||
scratch_buffer_append("::");
|
||||
scratch_buffer_append(decl->name ? decl->name : "$anon");
|
||||
c->debug.func_name = llvm_emit_zstring(c, scratch_buffer_to_string());
|
||||
scratch_buffer_append(function_name);
|
||||
c->debug.func_name = llvm_emit_string_const(c, scratch_buffer_to_string(), ".funcname");
|
||||
|
||||
File *file = source_file_by_id(decl->span.file_id);
|
||||
c->debug.file_name = llvm_emit_zstring(c, file->name);
|
||||
File *file = source_file_by_id(file_id);
|
||||
c->debug.file_name = llvm_emit_string_const(c, file->name, ".filename");
|
||||
}
|
||||
}
|
||||
|
||||
c->cur_func_decl = decl;
|
||||
|
||||
c->cur_func.name = function_name;
|
||||
c->cur_func.prototype = prototype;
|
||||
LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(c->context, c->function, "entry");
|
||||
c->current_block = entry;
|
||||
c->current_block_is_target = true;
|
||||
@@ -446,12 +467,12 @@ void llvm_emit_function_body(GenContext *c, Decl *decl)
|
||||
LLVMValueRef alloca_point = LLVMBuildAlloca(c->builder, LLVMInt32TypeInContext(c->context), "alloca_point");
|
||||
c->alloca_point = alloca_point;
|
||||
|
||||
FunctionPrototype *prototype = decl->type->function.prototype;
|
||||
unsigned arg = 0;
|
||||
|
||||
if (emit_debug)
|
||||
{
|
||||
llvm_debug_scope_push(c, c->debug.function);
|
||||
EMIT_LOC(c, body);
|
||||
if (c->debug.enable_stacktrace)
|
||||
{
|
||||
LLVMTypeRef slot_type = c->debug.stack_type;
|
||||
@@ -477,46 +498,53 @@ void llvm_emit_function_body(GenContext *c, Decl *decl)
|
||||
LLVMValueRef file_name = llvm_emit_struct_gep_raw(c, c->debug.stack_slot, slot_type, 2, alignment, &align_to_use);
|
||||
llvm_store_to_ptr_raw_aligned(c, file_name, c->debug.file_name, align_to_use);
|
||||
c->debug.stack_slot_row = llvm_emit_struct_gep_raw(c, c->debug.stack_slot, slot_type, 3, alignment, &align_to_use);
|
||||
LLVMValueRef last_ptr = NULL;
|
||||
if (function_name != kw_main && function_name != kw_mainstub)
|
||||
{
|
||||
last_ptr = c->debug.last_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
last_ptr = prev_ptr;
|
||||
}
|
||||
llvm_store_to_ptr_raw_aligned(c,
|
||||
c->debug.last_ptr,
|
||||
last_ptr,
|
||||
c->debug.stack_slot,
|
||||
type_alloca_alignment(type_voidptr));
|
||||
}
|
||||
}
|
||||
|
||||
c->failable_out = NULL;
|
||||
c->optional_out = NULL;
|
||||
c->return_out = NULL;
|
||||
if (prototype->ret_abi_info->kind == ABI_ARG_INDIRECT)
|
||||
if (prototype && prototype->ret_abi_info->kind == ABI_ARG_INDIRECT)
|
||||
{
|
||||
if (prototype->is_failable)
|
||||
if (prototype->is_optional)
|
||||
{
|
||||
c->failable_out = LLVMGetParam(c->function, arg++);
|
||||
c->optional_out = LLVMGetParam(c->function, arg++);
|
||||
}
|
||||
else
|
||||
{
|
||||
c->return_out = LLVMGetParam(c->function, arg++);
|
||||
}
|
||||
}
|
||||
if (prototype->ret_by_ref_abi_info)
|
||||
if (prototype && prototype->ret_by_ref_abi_info)
|
||||
{
|
||||
assert(!c->return_out);
|
||||
c->return_out = LLVMGetParam(c->function, arg++);
|
||||
}
|
||||
|
||||
|
||||
if (!decl->func_decl.attr_naked)
|
||||
if (signature)
|
||||
{
|
||||
// Generate LLVMValueRef's for all parameters, so we can use them as local vars in code
|
||||
VECEACH(decl->func_decl.signature.params, i)
|
||||
{
|
||||
llvm_emit_parameter(c, decl->func_decl.signature.params[i], prototype->abi_args[i], &arg, i);
|
||||
}
|
||||
FOREACH_BEGIN_IDX(i, Decl *param, signature->params)
|
||||
llvm_emit_func_parameter(c, param, prototype->abi_args[i], &arg, i);
|
||||
FOREACH_END();
|
||||
}
|
||||
|
||||
LLVMSetCurrentDebugLocation2(c->builder, NULL);
|
||||
|
||||
assert(decl->func_decl.body);
|
||||
AstId current = astptr(decl->func_decl.body)->compound_stmt.first_stmt;
|
||||
AstId current = body->compound_stmt.first_stmt;
|
||||
while (current)
|
||||
{
|
||||
llvm_emit_stmt(c, ast_next(¤t));
|
||||
@@ -606,6 +634,55 @@ static void llvm_emit_param_attributes(GenContext *c, LLVMValueRef function, ABI
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void llvm_emit_xxlizer(GenContext *c, Decl *decl)
|
||||
{
|
||||
Ast *body = astptrzero(decl->xxlizer.init);
|
||||
if (!body)
|
||||
{
|
||||
// Skip if it doesn't have a body.
|
||||
return;
|
||||
}
|
||||
LLVMTypeRef initializer_type = LLVMFunctionType(LLVMVoidTypeInContext(c->context), NULL, 0, false);
|
||||
bool is_finalizer = decl->decl_kind == DECL_FINALIZE;
|
||||
LLVMValueRef **array_ref = is_finalizer ? &c->destructors : &c->constructors;
|
||||
scratch_buffer_clear();
|
||||
scratch_buffer_printf(is_finalizer ? ".static_finalize.%u" : ".static_initialize.%u", vec_size(*array_ref));
|
||||
LLVMValueRef function = LLVMAddFunction(c->module, scratch_buffer_to_string(), initializer_type);
|
||||
LLVMSetLinkage(function, LLVMInternalLinkage);
|
||||
if (llvm_use_debug(c))
|
||||
{
|
||||
uint32_t row = decl->span.row;
|
||||
if (!row) row = 1;
|
||||
LLVMMetadataRef type = LLVMDIBuilderCreateSubroutineType(c->debug.builder, c->debug.file, NULL, 0, 0);
|
||||
|
||||
c->debug.function = LLVMDIBuilderCreateFunction(c->debug.builder,
|
||||
c->debug.file,
|
||||
scratch_buffer.str, scratch_buffer.len,
|
||||
scratch_buffer.str, scratch_buffer.len,
|
||||
c->debug.file,
|
||||
row,
|
||||
type,
|
||||
true,
|
||||
true,
|
||||
row,
|
||||
LLVMDIFlagPrivate,
|
||||
active_target.optimization_level != OPTIMIZATION_NONE);
|
||||
LLVMSetSubprogram(function, c->debug.function);
|
||||
}
|
||||
llvm_emit_body(c,
|
||||
function,
|
||||
decl->unit->module->name->module,
|
||||
is_finalizer ? "[static finalizer]" : "[static initializer]",
|
||||
decl->span.file_id,
|
||||
NULL,
|
||||
NULL,
|
||||
body);
|
||||
unsigned priority = decl->xxlizer.priority;
|
||||
LLVMValueRef vals[3] = { llvm_const_int(c, type_int, priority), function, llvm_get_zero(c, type_voidptr) };
|
||||
vec_add(*array_ref, LLVMConstStructInContext(c->context, vals, 3, false));
|
||||
}
|
||||
|
||||
void llvm_emit_function_decl(GenContext *c, Decl *decl)
|
||||
{
|
||||
assert(decl->decl_kind == DECL_FUNC);
|
||||
@@ -615,7 +692,6 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl)
|
||||
decl->backend_ref = function;
|
||||
FunctionPrototype *prototype = decl->type->function.prototype;
|
||||
|
||||
|
||||
ABIArgInfo *ret_abi_info = prototype->ret_abi_info;
|
||||
llvm_emit_param_attributes(c, function, ret_abi_info, true, 0, 0);
|
||||
unsigned params = vec_size(prototype->param_types);
|
||||
|
||||
@@ -12,13 +12,6 @@
|
||||
#include <llvm-c/Analysis.h>
|
||||
#include <llvm-c/BitWriter.h>
|
||||
#include <llvm-c/DebugInfo.h>
|
||||
#include <llvm-c/Transforms/PassManagerBuilder.h>
|
||||
#include <llvm-c/Transforms/InstCombine.h>
|
||||
#include <llvm-c/Transforms/Vectorize.h>
|
||||
#include <llvm-c/Transforms/Scalar.h>
|
||||
#include <llvm-c/Transforms/IPO.h>
|
||||
#include <llvm-c/Transforms/Utils.h>
|
||||
#include <llvm-c/Comdat.h>
|
||||
#include "dwarf.h"
|
||||
|
||||
#define SLICE_MAX_UNROLL 4
|
||||
@@ -27,8 +20,9 @@ typedef enum
|
||||
{
|
||||
BE_VALUE,
|
||||
BE_ADDRESS,
|
||||
BE_ADDRESS_FAILABLE,
|
||||
BE_ADDRESS_OPTIONAL,
|
||||
BE_BOOLEAN,
|
||||
BE_BOOLVECTOR,
|
||||
} BackendValueKind;
|
||||
|
||||
typedef struct
|
||||
@@ -37,7 +31,7 @@ typedef struct
|
||||
AlignSize alignment;
|
||||
Type *type; // Should never be a distinct or canonical type.
|
||||
LLVMValueRef value;
|
||||
LLVMValueRef failable;
|
||||
LLVMValueRef optional;
|
||||
} BEValue;
|
||||
|
||||
typedef struct
|
||||
@@ -67,8 +61,9 @@ typedef struct
|
||||
} DebugContext;
|
||||
|
||||
|
||||
typedef struct
|
||||
typedef struct GenContext_
|
||||
{
|
||||
bool shared_context;
|
||||
LLVMModuleRef module;
|
||||
LLVMBuilderRef global_builder;
|
||||
LLVMTargetMachineRef machine;
|
||||
@@ -79,6 +74,8 @@ typedef struct
|
||||
LLVMBuilderRef builder;
|
||||
LLVMBasicBlockRef current_block;
|
||||
LLVMBasicBlockRef catch_block;
|
||||
LLVMValueRef *constructors;
|
||||
LLVMValueRef *destructors;
|
||||
const char *ir_filename;
|
||||
const char *object_filename;
|
||||
const char *asm_filename;
|
||||
@@ -89,9 +86,15 @@ typedef struct
|
||||
LLVMTypeRef introspect_type;
|
||||
LLVMTypeRef fault_type;
|
||||
LLVMTypeRef size_type;
|
||||
Decl *panicfn;
|
||||
Decl *cur_code_decl;
|
||||
Decl *cur_func_decl;
|
||||
LLVMTypeRef char_ptr_type;
|
||||
LLVMTypeRef chars_type;
|
||||
Decl *panic_var;
|
||||
struct
|
||||
{
|
||||
const char *name;
|
||||
FunctionPrototype *prototype;
|
||||
Type *rtype;
|
||||
} cur_func;
|
||||
TypeInfo *current_return_type;
|
||||
int block_global_unique_count;
|
||||
int ast_alloca_addr_space;
|
||||
@@ -102,7 +105,7 @@ typedef struct
|
||||
DebugContext debug;
|
||||
Module *code_module;
|
||||
LLVMValueRef return_out;
|
||||
LLVMValueRef failable_out;
|
||||
LLVMValueRef optional_out;
|
||||
BEValue retval;
|
||||
int in_block;
|
||||
bool current_block_is_target : 1;
|
||||
@@ -130,7 +133,10 @@ typedef struct
|
||||
unsigned exp2;
|
||||
unsigned fabs;
|
||||
unsigned floor;
|
||||
unsigned flt_rounds;
|
||||
unsigned fma;
|
||||
unsigned fmuladd;
|
||||
unsigned frameaddress;
|
||||
unsigned fshl;
|
||||
unsigned fshr;
|
||||
unsigned lifetime_end;
|
||||
@@ -145,18 +151,23 @@ typedef struct
|
||||
unsigned maximum;
|
||||
unsigned maxnum;
|
||||
unsigned memcpy;
|
||||
unsigned memcpy_inline;
|
||||
unsigned memmove;
|
||||
unsigned memset;
|
||||
unsigned memset_inline;
|
||||
unsigned minimum;
|
||||
unsigned minnum;
|
||||
unsigned nearbyint;
|
||||
unsigned pow;
|
||||
unsigned powi;
|
||||
unsigned prefetch;
|
||||
unsigned readcyclecounter;
|
||||
unsigned rint;
|
||||
unsigned round;
|
||||
unsigned roundeven;
|
||||
unsigned sadd_overflow;
|
||||
unsigned sadd_sat;
|
||||
unsigned set_rounding;
|
||||
unsigned sin;
|
||||
unsigned smax;
|
||||
unsigned smin;
|
||||
@@ -181,6 +192,13 @@ typedef struct
|
||||
unsigned vector_reduce_smin;
|
||||
unsigned vector_reduce_umax;
|
||||
unsigned vector_reduce_umin;
|
||||
unsigned vector_reduce_add;
|
||||
unsigned vector_reduce_fadd;
|
||||
unsigned vector_reduce_mul;
|
||||
unsigned vector_reduce_fmul;
|
||||
unsigned vector_reduce_and;
|
||||
unsigned vector_reduce_or;
|
||||
unsigned vector_reduce_xor;
|
||||
} LLVMIntrinsics;
|
||||
|
||||
extern LLVMIntrinsics intrinsic_id;
|
||||
@@ -226,8 +244,8 @@ static inline LLVMValueRef decl_optional_ref(Decl *decl)
|
||||
{
|
||||
assert(decl->decl_kind == DECL_VAR);
|
||||
if (decl->var.kind == VARDECL_UNWRAPPED) return decl_optional_ref(decl->var.alias);
|
||||
if (decl->type->type_kind != TYPE_FAILABLE) return NULL;
|
||||
return decl->var.failable_ref;
|
||||
if (decl->type->type_kind != TYPE_OPTIONAL) return NULL;
|
||||
return decl->var.optional_ref;
|
||||
}
|
||||
|
||||
INLINE bool llvm_is_global_eval(GenContext *c);
|
||||
@@ -236,11 +254,10 @@ INLINE bool llvm_is_local_eval(GenContext *c);
|
||||
|
||||
// -- BE value --
|
||||
void llvm_value_addr(GenContext *c, BEValue *value);
|
||||
static inline bool llvm_value_is_addr(BEValue *value) { return value->kind == BE_ADDRESS || value->kind == BE_ADDRESS_FAILABLE; }
|
||||
static inline bool llvm_value_is_addr(BEValue *value) { return value->kind == BE_ADDRESS || value->kind == BE_ADDRESS_OPTIONAL; }
|
||||
static inline bool llvm_value_is_bool(BEValue *value) { return value->kind == BE_BOOLEAN; }
|
||||
bool llvm_value_is_const(BEValue *value);
|
||||
void llvm_value_rvalue(GenContext *context, BEValue *value);
|
||||
void llvm_value_set_bool(BEValue *value, LLVMValueRef llvm_value);
|
||||
void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type);
|
||||
void llvm_value_set_int(GenContext *c, BEValue *value, Type *type, uint64_t i);
|
||||
void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type, AlignSize alignment);
|
||||
@@ -266,6 +283,8 @@ LLVMMetadataRef llvm_get_debug_type(GenContext *c, Type *type);
|
||||
LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type);
|
||||
LLVMTypeRef llvm_get_pointee_type(GenContext *c, Type *any_type);
|
||||
void llvm_emit_function_decl(GenContext *c, Decl *decl);
|
||||
void llvm_emit_xxlizer(GenContext *c, Decl *decl);
|
||||
bool llvm_types_are_similar(LLVMTypeRef original, LLVMTypeRef coerce);
|
||||
INLINE LLVMTypeRef llvm_get_ptr_type(GenContext *c, Type *type);
|
||||
|
||||
// -- Attributes ---
|
||||
@@ -308,6 +327,8 @@ INLINE LLVMValueRef llvm_zext_trunc(GenContext *c, LLVMValueRef data, LLVMTypeRe
|
||||
void llvm_emit_typeid(GenContext *c, BEValue *be_value, Type *type);
|
||||
LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_init);
|
||||
LLVMValueRef llvm_emit_const_padding(GenContext *c, AlignSize size);
|
||||
LLVMValueRef llvm_emit_string_const(GenContext *c, const char *str, const char *extname);
|
||||
LLVMValueRef llvm_emit_empty_string_const(GenContext *c);
|
||||
LLVMValueRef llvm_emit_zstring(GenContext *c, const char *str);
|
||||
LLVMValueRef llvm_emit_zstring_named(GenContext *c, const char *str, const char *extname);
|
||||
INLINE LLVMValueRef llvm_const_int(GenContext *c, Type *type, uint64_t val);
|
||||
@@ -330,7 +351,7 @@ void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef th
|
||||
void llvm_emit_cond_br_raw(GenContext *context, LLVMValueRef b, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block);
|
||||
void llvm_emit_br(GenContext *c, LLVMBasicBlockRef next_block);
|
||||
void llvm_emit_jump_to_optional_exit(GenContext *c, LLVMValueRef opt_value);
|
||||
void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable);
|
||||
void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *optional);
|
||||
void llvm_emit_return_implicit(GenContext *c);
|
||||
|
||||
// -- Blocks --
|
||||
@@ -366,6 +387,7 @@ LLVMValueRef llvm_store_to_ptr_aligned(GenContext *c, LLVMValueRef destination,
|
||||
LLVMValueRef llvm_store_to_ptr_raw_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment);
|
||||
void llvm_store_to_ptr_zero(GenContext *context, LLVMValueRef pointer, Type *type);
|
||||
TypeSize llvm_store_size(GenContext *c, LLVMTypeRef type);
|
||||
TypeSize llvm_alloc_size(GenContext *c, LLVMTypeRef type);
|
||||
|
||||
/// -- Aggregates --
|
||||
INLINE LLVMValueRef llvm_emit_insert_value(GenContext *c, LLVMValueRef agg, LLVMValueRef new_value, ArraySize index);
|
||||
@@ -407,19 +429,21 @@ void llvm_emit_coerce_store(GenContext *c, LLVMValueRef addr, AlignSize alignmen
|
||||
LLVMValueRef llvm_emit_coerce(GenContext *c, LLVMTypeRef coerced, BEValue *value, Type *original_type);
|
||||
INLINE bool call_supports_variadic(CallABI abi);
|
||||
static inline LLVMCallConv llvm_call_convention_from_call(CallABI abi);
|
||||
void llvm_emit_raw_call(GenContext *c, BEValue *result_value, FunctionPrototype *prototype, LLVMTypeRef func_type, LLVMValueRef func, LLVMValueRef *args, unsigned arg_count, int inline_flag, LLVMValueRef error_var, bool sret_return, BEValue *synthetic_return_param);
|
||||
void llvm_emit_parameter(GenContext *c, LLVMValueRef *args, unsigned *arg_count_ref, ABIArgInfo *info, BEValue *be_value, Type *type);
|
||||
|
||||
// -- C3 Lowering --
|
||||
void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr);
|
||||
void llvm_emit_stmt(GenContext *c, Ast *ast);
|
||||
void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name, SourceSpan loc);
|
||||
void llvm_emit_panic_if_true(GenContext *c, BEValue *value, const char *panic_name, SourceSpan loc);
|
||||
void llvm_emit_panic(GenContext *c, const char *message, const char *file, const char *func, unsigned line);
|
||||
void llvm_emit_panic(GenContext *c, const char *message, SourceSpan loc);
|
||||
void llvm_emit_subarray_len(GenContext *context, BEValue *subarray, BEValue *len);
|
||||
void llvm_emit_subarray_pointer(GenContext *context, BEValue *subarray, BEValue *pointer);
|
||||
void llvm_emit_compound_stmt(GenContext *c, Ast *ast);
|
||||
LLVMValueRef llvm_emit_const_bitstruct(GenContext *c, ConstInitializer *initializer);
|
||||
void llvm_emit_function_body(GenContext *context, Decl *decl);
|
||||
BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValueRef failable);
|
||||
BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValueRef optional);
|
||||
INLINE void llvm_emit_exprid(GenContext *c, BEValue *value, ExprId expr);
|
||||
INLINE void llvm_emit_statement_chain(GenContext *c, AstId current);
|
||||
void llvm_emit_initialize_reference_temporary_const(GenContext *c, BEValue *ref, Expr *expr);
|
||||
@@ -429,6 +453,7 @@ LLVMValueRef llvm_emit_call_intrinsic(GenContext *c, unsigned intrinsic, LLVMTyp
|
||||
void llvm_emit_cast(GenContext *c, CastKind cast_kind, Expr *expr, BEValue *value, Type *to_type, Type *from_type);
|
||||
void llvm_emit_local_var_alloca(GenContext *c, Decl *decl);
|
||||
void llvm_emit_local_decl(GenContext *c, Decl *decl, BEValue *value);
|
||||
void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr);
|
||||
|
||||
// -- Optional --
|
||||
LLVMValueRef llvm_emit_is_no_opt(GenContext *c, LLVMValueRef error_value);
|
||||
|
||||
@@ -102,6 +102,11 @@ INLINE LLVMValueRef llvm_emit_lshr(GenContext *c, LLVMValueRef value, LLVMValueR
|
||||
|
||||
INLINE LLVMValueRef llvm_emit_trunc_bool(GenContext *c, LLVMValueRef value)
|
||||
{
|
||||
LLVMTypeRef type = LLVMTypeOf(value);
|
||||
if (LLVMGetTypeKind(type) == LLVMVectorTypeKind)
|
||||
{
|
||||
return LLVMBuildTrunc(c->builder, value, LLVMVectorType(c->bool_type, LLVMGetVectorSize(type)), "");
|
||||
}
|
||||
return LLVMBuildTrunc(c->builder, value, c->bool_type, "");
|
||||
}
|
||||
|
||||
@@ -334,3 +339,4 @@ INLINE void llvm_emit_statement_chain(GenContext *c, AstId current)
|
||||
llvm_emit_stmt(c, ast_next(¤t));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,10 +50,11 @@ void gencontext_begin_module(GenContext *c)
|
||||
if (active_target.asm_file_dir) c->asm_filename = file_append_path(active_target.asm_file_dir, c->asm_filename);
|
||||
}
|
||||
if (active_target.object_file_dir) c->object_filename = file_append_path(active_target.object_file_dir, c->object_filename);
|
||||
c->panicfn = global_context.panic_fn;
|
||||
c->panic_var = global_context.panic_var;
|
||||
c->module = LLVMModuleCreateWithNameInContext(c->code_module->name->module, c->context);
|
||||
c->machine = llvm_target_machine_create();
|
||||
c->target_data = LLVMCreateTargetDataLayout(c->machine);
|
||||
|
||||
LLVMSetModuleDataLayout(c->module, c->target_data);
|
||||
LLVMSetSourceFileName(c->module, c->code_module->name->module, strlen(c->code_module->name->module));
|
||||
LLVMTypeRef options_type = LLVMInt8TypeInContext(c->context);
|
||||
@@ -125,7 +126,9 @@ void gencontext_begin_module(GenContext *c)
|
||||
c->introspect_type = create_introspection_type(c);
|
||||
c->fault_type = create_fault_type(c);
|
||||
c->size_type = llvm_get_type(c, type_usize);
|
||||
if (c->panicfn) c->panicfn->backend_ref = NULL;
|
||||
c->char_ptr_type = LLVMPointerType(c->byte_type, 0);
|
||||
c->chars_type = llvm_get_type(c, type_chars);
|
||||
if (c->panic_var) c->panic_var->backend_ref = NULL;
|
||||
|
||||
if (active_target.debug_info != DEBUG_INFO_NONE)
|
||||
{
|
||||
@@ -140,8 +143,8 @@ void gencontext_begin_module(GenContext *c)
|
||||
{
|
||||
c->debug.stack_type = LLVMStructCreateNamed(c->context, ".$callstack");
|
||||
LLVMTypeRef types[4] = { LLVMPointerType(c->debug.stack_type, 0),
|
||||
LLVMPointerType(c->byte_type, 0),
|
||||
LLVMPointerType(c->byte_type, 0),
|
||||
c->chars_type,
|
||||
c->chars_type,
|
||||
llvm_get_type(c, type_uint) };
|
||||
LLVMStructSetBody(c->debug.stack_type, types, 4, false);
|
||||
c->debug.last_ptr = NULL;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user