Compare commits

..

173 Commits

Author SHA1 Message Date
Christoffer Lerno
a6f2eb22a9 Add "native" for AVX features. 2022-12-29 18:40:38 +01:00
Christoffer Lerno
ef7365224f Added $include. 2022-12-29 16:12:03 +01:00
Christoffer Lerno
f122d290f1 Add macos sdk / min version to pass to the linker. 2022-12-29 01:06:35 +01:00
Christoffer Lerno
90f5c24d4c Fix parsing @_foo 2022-12-28 16:36:41 +01:00
Christoffer Lerno
bcbcc3100f Fix of accidentally added typo. 2022-12-28 00:39:21 +01:00
Christoffer Lerno
f6e798c283 Friendlier int parsing and promotion. 2022-12-28 00:37:52 +01:00
Christoffer Lerno
f622b3bd4c Cleanup of naming change to optional. Fix of builtin optional handling. 2022-12-27 23:55:23 +01:00
Christoffer Lerno
fe54b84aed Version bump 2022-12-27 14:30:43 +01:00
Christoffer Lerno
f46697bc54 Updated casts (#684)
Cast code rework. Remove llvm_set_bool and friends.
2022-12-27 14:16:47 +01:00
Christoffer Lerno
e2b4a19c81 Add python overwrite. 2022-12-23 17:03:38 +01:00
Christoffer Lerno
9e77fe7787 Add python-tk 2022-12-23 16:56:26 +01:00
Christoffer Lerno
7223435df6 Update build for mac. 2022-12-23 14:20:09 +01:00
Christoffer Lerno
fe9ace713b Fix str::concat 2022-12-23 14:06:02 +01:00
Christoffer Lerno
03cdc8e3b1 Add "get_ref" to list. 2022-12-21 15:59:29 +01:00
Christoffer Lerno
352e09970c Math max/min now take multiple arguments. 2022-12-20 16:45:35 +01:00
Christoffer Lerno
e8a8ac8bc1 Fix bit or / xor / and on bool vectors. 2022-12-20 03:24:24 +01:00
Christoffer Lerno
334c004f1e Fix of atan2 2022-12-19 13:00:27 +01:00
Christoffer Lerno
aa534e2b3f Fix to vector comparisons. 2022-12-18 20:58:20 +01:00
Christoffer Lerno
2ee2bc3129 Fix bugs related to distinct types. 2022-12-18 00:21:02 +01:00
Christoffer Lerno
b88e5a8079 Add atan and atan2 2022-12-17 21:29:45 +01:00
Christoffer Lerno
c339261d1e Added vector dot functions. Comparison now yields bool vector. Cast between vector types. 2022-12-17 01:51:03 +01:00
Christoffer Lerno
8008fb2c18 Do not auto-deref subscripting. 2022-12-15 23:46:26 +01:00
Dmitry Atamanov
10219eccb2 Added compile-test command to help. 2022-12-14 23:04:40 +01:00
Christoffer Lerno
1a510207e8 Update string.c3 2022-12-14 23:03:45 +01:00
Nikos Plugachev
a21236d661 Improved string concat 2022-12-14 23:03:45 +01:00
Nikos Plugachev
a749a4d265 Added String.concat, List.clear 2022-12-14 23:03:45 +01:00
Christoffer Lerno
b5afa98507 Updated vector comparison behaviour and semantics. 2022-12-14 23:02:01 +01:00
Christoffer Lerno
abf0f64ac0 Share method extensions across modules by default. Fix bug in string split. 2022-12-14 13:18:01 +01:00
Christoffer Lerno
dcf0b4c580 Fix an issue with pointer debug info creation. Improve generated parameterized name. Version bump. 2022-12-13 19:20:14 +01:00
Christoffer Lerno
a9ed514fe5 Fix bug in defer from macros. Ensure debug location on panic functions. Add getcwd. 2022-12-13 07:50:41 +01:00
Dmitry Atamanov
8b0d409695 Add inlined memcpy and memset builtins and macros. (#668) 2022-12-12 11:45:01 +01:00
Christoffer Lerno
50d2a04c48 Default to threads = cpus(). Correct multithreaded benchmark with threads. 2022-12-11 23:35:02 +01:00
Christoffer Lerno
1864c69f31 Fixes to inc/dec vector element. Improvements to unit tests. Updated task threading. Single threaded by default due to poor LLVM perf. 2022-12-10 04:24:56 +01:00
Christoffer Lerno
af0174f360 Some work on io libs. 2022-12-09 08:45:02 +01:00
Christoffer Lerno
2a4d43d7c7 Fix issue when using ct types as body parameters. 2022-12-09 00:00:33 +01:00
Christoffer Lerno
13cb637cb4 Fix missing early resolution on demand of parameterized types. 2022-12-08 19:48:36 +01:00
Christoffer Lerno
de4bfe470e Attempt supporting setjmp on MSVC 2022-12-07 22:16:09 +01:00
Christoffer Lerno
eaaa5362a5 Output messages at compile time. 2022-12-07 18:48:55 +01:00
Christoffer Lerno
1ea5625183 CT variables now follow CT scopes. It's now allowed to mutate CT variables in deeper runtime scopes. 2022-12-07 16:32:45 +01:00
Christoffer Lerno
f7659776fc Fix problem when taking address of method. 2022-12-07 11:58:21 +01:00
Christoffer Lerno
ed99e09c4e Correctly detect & lowering in the case of &foo[1].b using operator overloading. 2022-12-07 10:53:26 +01:00
Christoffer Lerno
e9181a75e6 Improve errors when a variable fails to properly analyse. 2022-12-06 20:29:07 +01:00
Christoffer Lerno
963b8f28ef Fix of multiple returns in macros retaining ct values. Correctly require ; after endfor etc. 2022-12-06 20:09:40 +01:00
Christoffer Lerno
5721fcc224 Fix of string function. 2022-12-06 17:48:30 +01:00
Christoffer Lerno
287de8f499 Fix issue with aliasing @-macros. 2022-12-05 17:38:27 +01:00
Christoffer Lerno
44dfeb621d Fix body arguments: (@foo(;int x) and mismatch on canonical types) 2022-12-05 16:23:00 +01:00
Christoffer Lerno
eb87eb1987 Allow an expression list be an lvalue if the last value is an lvalue. Fix indexing from back for [] overloads. 2022-12-05 15:42:24 +01:00
Christoffer Lerno
c15fb7460c Add functions for splitting strings. 2022-12-04 23:01:53 +01:00
Christoffer Lerno
927ad2001f Incorrect check for sqrt. 2022-12-04 01:13:47 +01:00
Christoffer Lerno
7647378e7c Fix missing check when macro method incorrectly has a raw vararg argument. 2022-12-03 23:07:37 +01:00
Christoffer Lerno
10b0b5f9c7 Fix map.c3 2022-12-03 22:07:04 +01:00
Christoffer Lerno
299ec1814b Add native string -> int conversions. Fix to getline and add tgetline. 2022-12-01 16:13:52 +01:00
Christoffer Lerno
07700ed2c5 Add str::trim and String::tcopy_str. Version bump. 2022-12-01 13:19:08 +01:00
Christoffer Lerno
0ae586585a Fix .values .names .elements on "fault" types. Version bump. 2022-11-25 11:11:27 +01:00
Christoffer Lerno
285299dcd5 Some simple stdlib tests. 2022-11-20 18:16:21 +01:00
Dmitry Atamanov
2fefed5bda Create simple main.c3 by init command 2022-11-19 14:49:50 +01:00
Christoffer Lerno
da1a45f718 Remove iptrdiff and uptrdiff. Bump version to 0.3.100 2022-11-17 23:44:54 +01:00
Dmitry Atamanov
bbef94b0fd Fix project creation 2022-11-16 21:05:26 +01:00
Christoffer Lerno
c093f16fd0 Fix missing check on foreach indexing. 2022-11-14 15:05:19 +01:00
Dmitry Atamanov
5ff726d8d1 Added $$get_rounding_mode and $$set_rounding_mode builtins. (#655) 2022-11-14 13:07:32 +01:00
Christoffer Lerno
49eacb8824 More support for test. Panic function update. 2022-11-14 11:48:12 +01:00
Christoffer Lerno
450113d161 Fix of mac CI build. 2022-11-14 09:58:07 +01:00
Christoffer Lerno
998c56533b Update test case. 2022-11-14 09:48:34 +01:00
Christoffer Lerno
73619817ba Update panic function and early work on tests. Optimize ABI lowering. 2022-11-03 19:38:34 +01:00
Christoffer Lerno
70f6ad1b27 Added "values" module. 2022-10-31 14:40:58 +01:00
Dmitry Atamanov
e070bf22ee Added pow macros to math module. 2022-10-31 14:09:38 +01:00
Dmitry Atamanov
b086c85d9f Add LLVM 15 to CI 2022-10-31 10:54:32 +01:00
Dmitry Atamanov
66d87b25a3 Added more math macros: cosec, cosech, cosh, cotan, cotanh, hypot, sec, sech, sinh, sqr, sqrt, tan, tanh. 2022-10-30 20:31:32 +01:00
Dmitry Atamanov
d9e81c6035 Fix vector slices assignment 2022-10-30 19:54:14 +01:00
Christoffer Lerno
98ae13c03d Add exact and overflow builtins. 2022-10-28 01:14:10 +02:00
Christoffer Lerno
d9ea953b5c Fix $c >>= 2 2022-10-25 10:50:56 +02:00
Christoffer Lerno
4cdfac092b Add missing \n to program finish on run. 2022-10-24 17:15:51 +02:00
Christoffer Lerno
1ef43cbc13 Remove "directives" folder. 2022-10-24 17:13:24 +02:00
Christoffer Lerno
093bc2089a Fix to project creation. 2022-10-24 17:11:44 +02:00
Christoffer Lerno
9b5e2b8578 Remove fix for LLVM 16 2022-10-24 16:10:26 +02:00
Christoffer Lerno
2d377ada45 Fix in parameter check. 2022-10-23 23:41:03 +02:00
Christoffer Lerno
02374c6aab Generic modules also accepts integers and booleans. 2022-10-23 19:16:38 +02:00
Dmitry Atamanov
155c8862c9 Remove duplicates in help 2022-10-22 16:10:15 +02:00
Christoffer Lerno
f73b507ccb Use project.json instead of .c3p. List project properties. Check project property usage. Change names: lib-dir -> dependency-search-paths, libs -> dependencies, linker-libs -> linked-libraries, linkerlib-dir -> linker-search-paths, csources -> c-sources. Several settings have an xxxx-add, xxxx-override pair for the target, gaining fine grained control over the override. 2022-10-22 15:32:01 +02:00
Christoffer Lerno
26e4662c3b Project updates. 2022-10-22 01:04:43 +02:00
Christoffer Lerno
ede224662c Fix of broken bitstruct init #641. Version bump. 2022-10-21 11:45:34 +02:00
Christoffer Lerno
bd0e8f1ef1 Allow imports anywhere in the module outside of ct statements. 2022-10-20 23:54:36 +02:00
Christoffer Lerno
e6a5f98606 float.min will now give -float.max 2022-10-20 21:54:40 +02:00
Christoffer Lerno
e15dbd4907 Improve conversion functions. 2022-10-20 20:32:33 +02:00
Christoffer Lerno
ae7aa65f35 Add conversion functions for i128 2022-10-20 19:35:29 +02:00
Christoffer Lerno
d13b7ac96a Enable support for int128 across all platforms. 2022-10-20 18:03:02 +02:00
Christoffer Lerno
03fe2b575d Chain optionals with optionals using ??. Version bump. 2022-10-20 12:11:24 +02:00
Christoffer Lerno
f86ef8a743 Remove tscoped. Replace str_index_of with "starts_with". Updated copy_zstring/copy. Fixed utf conversion functions. Initial work on "Path". Lexer fix on \\. ABI fix using distinct types. (bool)"" now works correctly. Bug in $if with switches/loops as the first statement fixed. Version bump. 2022-10-20 10:55:14 +02:00
Christoffer Lerno
7d58ce0dcb Take code from stdin. Version bump. 2022-10-18 19:45:56 +02:00
Christoffer Lerno
f8f249ee2c Update compilation linux. 2022-10-18 16:39:33 +02:00
Christoffer Lerno
0adb15139f Fix bug in implicit fmul. 2022-10-18 14:02:20 +02:00
Christoffer Lerno
76ee384a4c Fix of ternary / elvis where legs are bool or optional. 2022-10-18 14:02:20 +02:00
Christoffer Lerno
b1ed066e55 Fix coerce store / load to properly rely on alloc size like Clang does it. Version bump. 2022-10-15 22:12:52 +02:00
Christoffer Lerno
160659c4e3 Optimize sret / byval. Fixes to types and failables. 2022-10-15 02:45:19 +02:00
Dmitry Atamanov
dfe3128b16 Add --benchmarking and --testing switches. (#591)
Added `--benchmarking` and `--testing` switches. Update env.c3
2022-10-13 14:25:45 +02:00
Christoffer Lerno
8f269a4f13 Improve text. 2022-10-13 14:05:34 +02:00
Christoffer Lerno
1ae251478b Fix templates for init. 2022-10-13 14:03:30 +02:00
Christoffer Lerno
effec3a1f6 Fix of bug where missing return wasn't detected. 2022-10-13 13:34:54 +02:00
Christoffer Lerno
4d08fee30e Add bolt-15 to linux cmake. 2022-10-13 11:25:24 +02:00
Christoffer Lerno
5d9a7ab0a6 Extend "var" to allow type inference on variables. 2022-10-13 09:37:52 +02:00
Christoffer Lerno
5e184f04e7 List now has correct alignment and takes memory allocator initializer. Bugfix of aligned allocations. 2022-10-12 22:35:36 +02:00
Christoffer Lerno
b2b1a3489a Add hashmap function. 2022-10-12 15:21:40 +02:00
Christoffer Lerno
fc41179636 Minor indention change. 2022-10-12 11:53:49 +02:00
Dmitry Atamanov
959c418e8b Add a lot of math functions and macros. (#626) 2022-10-12 11:52:19 +02:00
Christoffer Lerno
9424bba49f Version bump. More generous wildcard length matching and conversions. 2022-10-12 11:46:58 +02:00
Christoffer Lerno
314369d069 Version bump, add -O0+ and -O1+, let stdlib override nostdlib 2022-10-10 22:54:22 +02:00
Christoffer Lerno
ec3d77f4bd Fix for nolibc & linux. 2022-10-10 16:04:48 +02:00
Christoffer Lerno
ab78663f3c Add usz and isz. 2022-10-10 15:44:03 +02:00
Christoffer Lerno
348495b4c8 Added an initial nolibc 2022-10-10 15:44:03 +02:00
Christoffer Lerno
6523982f14 Clarify source and license on msvc script. 2022-10-10 15:02:36 +02:00
Christoffer Lerno
df8595cd64 Fix of bug where using && on a member ref would result in an assert. 2022-10-10 14:51:52 +02:00
Christoffer Lerno
febd11fa95 Fix of crashing bug with member types. Version bump. 2022-10-10 11:55:46 +02:00
Christoffer Lerno
feba7b8ed2 Rename muladd and fmuladd and insert it at codegen. 2022-10-10 11:14:57 +02:00
Dmitry Atamanov
3624c2a72c Added $$muladd built-in. 2022-10-10 11:14:57 +02:00
Christoffer Lerno
d5f965e137 Fix test. 2022-10-08 22:10:56 +02:00
Christoffer Lerno
70a429f832 Fix error using compile time var before assignment. 2022-10-08 21:46:56 +02:00
Dmitry Atamanov
7fa129932d Added $$memmove built-in. 2022-10-08 21:41:11 +02:00
Christoffer Lerno
73ac0b8ea0 Fixed incorrect test. 2022-10-08 14:55:36 +02:00
Christoffer Lerno
407ed5a63d Fix vector init and float promotion. Fix of 1.0f. 2022-10-08 14:30:17 +02:00
Christoffer Lerno
fa064276bc Disallow $offsetof / $alignof on types. Version bump. 2022-10-08 12:41:40 +02:00
Christoffer Lerno
c84f82559c Disallow typeof of member type. 2022-10-07 22:28:30 +02:00
Christoffer Lerno
bb20a38cdb Updated membersof. "Type.kind" renamed "Type.kindof" 2022-10-07 14:41:10 +02:00
Christoffer Lerno
f010f6a926 Add qnameof, nameof, extnameof to types. 2022-10-06 19:31:52 +02:00
Christoffer Lerno
ec1a5d97c9 Support for memberof/returns/params. 2022-10-06 17:18:16 +02:00
Christoffer Lerno
870e716f59 Missing support for bitstruct ".inner" 2022-10-06 15:23:16 +02:00
Christoffer Lerno
d33ff212a7 Fix double @@ in attribute listing. 2022-10-06 14:55:49 +02:00
Christoffer Lerno
ee533c5500 Update listable type properties. 2022-10-06 14:52:44 +02:00
Christoffer Lerno
a281dbe812 Added $$TIME, $$DATE, $$FUNCTION. Builtin defines listed. 2022-10-06 11:57:59 +02:00
Dmitry Atamanov
1d39fc475f Added max and min macros to builtin_comparison module. 2022-10-05 23:13:38 +02:00
Dmitry Atamanov
f5a1894876 Add std::ascii module. (#611)
Added `std::ascii` module.
2022-10-05 23:11:41 +02:00
Christoffer Lerno
db06f99445 "[]=" now works as overload. Improved eval resolution. Added $$FUNCPTR 2022-10-05 22:58:28 +02:00
Christoffer Lerno
05d4ec55f6 Fix of $$FUNC in methods. 2022-10-05 10:17:11 +02:00
Christoffer Lerno
dcfcf460a5 Support LLVM 15 MacOS. 2022-10-05 10:17:11 +02:00
Christoffer Lerno
eb86b83bd7 Prevent ct_eval from returning an rvalue. 2022-10-04 22:41:07 +02:00
Christoffer Lerno
3d844b8722 Fix of printf registration. 2022-10-03 16:01:27 +02:00
Christoffer Lerno
6bd72c2ec4 Improve printf. 2022-10-03 15:42:55 +02:00
Christoffer Lerno
4783946476 Version bump. Updated printf using "Formatter". Fix to initializers. 2022-10-03 13:06:57 +02:00
Christoffer Lerno
9f7ed00f04 Fixes to function pointer conversion. 2022-10-02 00:22:21 +02:00
Christoffer Lerno
f05ffc84d8 Minor fixes. 2022-10-01 22:14:07 +02:00
Christoffer Lerno
55f7046da8 Fix of static init with debug turned on. 2022-10-01 17:53:08 +02:00
Christoffer Lerno
c5b9b6c761 Single module compilation. 2022-09-30 13:56:08 +02:00
Christoffer Lerno
5029dc703e Updated error message. 2022-09-30 08:44:28 +02:00
Christoffer Lerno
cbb731b42b Fix alignment on temp allocators. 2022-09-29 22:18:12 +02:00
Christoffer Lerno
258a6ba97a Bug fixes, addition of hash map implementation. (#605)
* Simple hash map. Fix of bug preventing cast of typeid. Allow declarations in global "$checks". Fix to non-constant default args. Correctly duplicate macro contracts. Allow typeid to add methods. Fix printing of subarrays. Fix bug when printing a function with a module. Fix bug with initializer and creating local variables. Add the compile-only option to the help.
2022-09-29 20:19:31 +02:00
Christoffer Lerno
e1b5b0b60c Implement static finalize / initialize. Version bump. 2022-09-28 17:18:20 +02:00
Christoffer Lerno
58647043f4 Fix bug with $$clz, version update. 2022-09-26 11:48:12 +02:00
Christoffer Lerno
379f9e60bf More checks for $switch 2022-09-25 17:17:56 +02:00
Christoffer Lerno
a4d4c27ca6 General reorganization, set any empty expression statement to nop. Version bump. 2022-09-24 21:22:29 +02:00
Christoffer Lerno
81bea9bad6 Updated untyped lists. 2022-09-24 14:35:58 +02:00
Christoffer Lerno
52f3948026 Cleanup of untyped lists. 2022-09-23 10:47:56 +02:00
Christoffer Lerno
46c182f3d1 Fix of bug in nextcase semantic checking. Cleanup and refactoring. 2022-09-22 16:14:39 +02:00
Christoffer Lerno
cc71b96c38 Rearranging code somewhat. 2022-09-22 00:05:22 +02:00
Christoffer Lerno
ad18d9ba48 Add .hash to integer types. Fixup of make and tmake. Updated map.c3 to become a working example. Fix bug in subarray -> pointer conversion. Search extension methods in std::core. Fix slice <-> slice copy. 2022-09-21 17:26:36 +02:00
Christoffer Lerno
4d5821408d Add @castable and @convertible builtin macros, removes the need $castable and $convertible 2022-09-20 22:18:00 +02:00
Christoffer Lerno
48ee567f81 Add types::is_indexable. 2022-09-20 18:45:43 +02:00
Christoffer Lerno
321c713687 Fix of $checks. 2022-09-20 18:42:39 +02:00
Christoffer Lerno
be5c82cfa6 Add "$checks". Fix where $y++ could appear inside a runtime scope. 2022-09-20 18:32:36 +02:00
Christoffer Lerno
4fa4b2a631 Refactored builtins. Added reduce operations and powi. Version bump. 2022-09-19 14:58:48 +02:00
Christoffer Lerno
9b14340a57 Added prefetch builtin. 2022-09-18 23:27:42 +02:00
Christoffer Lerno
e7fad16d0f Added stringify tests and $typefrom. Bump to 0.3.50. 2022-09-18 20:52:25 +02:00
Christoffer Lerno
62e3b8063e Fix stringify span. 2022-09-18 02:25:22 +02:00
Christoffer Lerno
c7f0f58e82 Fix stringify bug. 2022-09-18 01:26:06 +02:00
Christoffer Lerno
35549c21bc A brainf**k example. 2022-09-18 01:20:17 +02:00
Christoffer Lerno
6220bda4a3 Use @operator([]), @operator(&[]) and @operator([]=) instead of names. 2022-09-17 23:33:27 +02:00
Christoffer Lerno
81a2474f75 Add methods to basic types. Remove "intvec" and "floatvec" overloads. Update bits and math to use the new functionality. Introduces [<*>] and [<>] types. Bump version to 0.3.47 2022-09-17 22:00:28 +02:00
Christoffer Lerno
28f2247602 Fix of mistake in commit. 2022-09-17 13:17:24 +02:00
Jean-Baptiste Perrier
1541354587 Extending JSON utils (#574)
* Adding json free, to_str, removing parser dependency from new_object.
* Adding support for BOOL in json_to_str
* Replace NULL check by assert
* Bugfixing json parser
Co-authored-by: Christoffer Lerno <christoffer.lerno@gmail.com>
2022-09-17 12:20:38 +02:00
Christoffer Lerno
a66c0942f8 Adding saturated builtins. Remove LLVM 12 support. Remove old llvm optimizer use. 2022-09-17 12:03:08 +02:00
Dmitry Atamanov
fd9d300b06 Added Int128BE, UInt128BE, Int128LE and UInt128LE. 2022-09-17 01:53:21 +02:00
Dmitry Atamanov
b60862ec7a Added cpuid, rdtsc and rdtscp. 2022-09-16 23:12:03 +02:00
Christoffer Lerno
c8166f6fdb Adding $$reverse. Bump to 0.3.45 2022-09-16 14:53:17 +02:00
Christoffer Lerno
4d27150952 Adding $$shufflevector. Bump to 0.3.44 2022-09-16 13:39:03 +02:00
Christoffer Lerno
d4aec525f5 Fixes to asm. Added additional x86 instructions. 2022-09-16 00:41:19 +02:00
503 changed files with 33068 additions and 19447 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
View 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
View 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
View 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(&copy)!!;
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
View 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
}
*/

View File

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

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

View File

@@ -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(&param, float_from_variant(current))?;
continue;
case 'E':
param.flags.uppercase = true;
nextcase;
case 'e':
etoa(&param, float_from_variant(current))?;
continue;
case 'G':
param.flags.uppercase = true;
nextcase;
case 'g':
param.flags.adapt_exp = true;
etoa(&param, float_from_variant(current))?;
continue;
case 'c':
out_char(&param, current)?;
continue;
case 's':
out_str(&param, 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(&param, v, is_neg, base)?;
}
// termination
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
return param.idx;
}

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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)?;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,8 +5,8 @@ import std::io;
struct String
{
Allocator allocator;
usize len;
usize capacity;
usz len;
usz capacity;
char* ptr;
}

View File

@@ -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/**"
]
},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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(&copy_struct, source_ast);
copy_end();
return ast;
}
Ast *copy_ast_macro(Ast *source_ast)
{
assert(copy_struct.copy_in_use);
return ast_copy_deep(&copy_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(&copy_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(&copy_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(&copy_struct, source_expr);
copy_begin();
Expr *expr = copy_expr(&copy_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, &copy->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, &copy->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(&copy_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(&copy_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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@@ -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, &regs, 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, &regs);
prototype->abi_varargs = x86_create_params(prototype->call_abi, prototype->param_types, &regs);
prototype->abi_varargs = x86_create_params(prototype->call_abi, prototype->varargs, &regs);
}

View File

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

View File

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

View File

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

View File

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

View File

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