├── .gitignore ├── LICENSE ├── README.md ├── c.c ├── c.h ├── docs ├── CNAME ├── Gemfile ├── _config.yml ├── _data │ └── toc.yml ├── _docs │ ├── example-page.md │ ├── getting-started.md │ └── subfolder │ │ └── example-page.md ├── _includes │ ├── alert.html │ ├── doc.html │ ├── editable.html │ ├── embed │ │ └── discourse.html │ ├── footer.html │ ├── google-analytics.html │ ├── head.html │ ├── headers.html │ ├── navigation.html │ ├── quiz.html │ ├── quiz │ │ └── multiple-choice.html │ ├── scrolltop.html │ ├── sidebar.html │ ├── social.html │ ├── tags.html │ └── toc.html ├── _layouts │ ├── default.html │ ├── page.html │ └── post.html ├── _posts │ ├── 2023-01-24-welcome.md │ ├── 2023-05-08-libinres.md │ ├── 2023-05-21-varfuncs.md │ ├── 2023-05-22-macros.md │ ├── 2023-05-23-varmacs.md │ ├── 2023-05-24-genmacs.md │ ├── 2023-05-25-condcomp.md │ ├── 2023-05-26-reldecls.md │ ├── 2023-05-27-mvsyntax.md │ └── 2023-06-03-mvsem.md ├── assets │ ├── css │ │ ├── main.css │ │ └── palette.css │ ├── img │ │ ├── favicon.png │ │ ├── icon.png │ │ └── splash.png │ └── js │ │ ├── application.a59e2a89.js │ │ ├── application.js │ │ └── modernizr.74668098.js ├── pages │ ├── about.md │ ├── archive.md │ ├── feed.xml │ ├── forum.md │ ├── index.md │ ├── news.md │ └── sitemap.xml └── search │ └── search_index.json ├── examples ├── README.md ├── advent.c ├── binarytrees.c ├── calendar.c ├── fannkuchredux.c ├── nbody.c ├── pi.c ├── spectralnorm.c ├── sudoku.c └── sudoku.txt ├── l.c ├── l.h ├── lib ├── README.md ├── crt.args.wo ├── crt.argv.wo ├── crt.void.wo ├── crt.wo ├── ctype.wo ├── dirent.wo ├── errno.wo ├── fcntl.wo ├── fenv.wo ├── include │ ├── NDEBUG │ │ └── assert.h │ ├── assert.h │ ├── ctype.h │ ├── dirent.h │ ├── errno.h │ ├── fcntl.h │ ├── fenv.h │ ├── float.h │ ├── inttypes.h │ ├── limits.h │ ├── locale.h │ ├── math.h │ ├── stdarg.h │ ├── stdbool.h │ ├── stddef.h │ ├── stdint.h │ ├── stdio.h │ ├── stdlib.h │ ├── string.h │ ├── sys.cdefs.h │ ├── sys.crt.h │ ├── sys.intrs.h │ ├── sys.stat.h │ ├── sys.types.h │ ├── time.h │ ├── unistd.h │ ├── wasi.api.h │ ├── wasm.h │ ├── wasm_simd128.h │ └── wchar.h ├── math.wo ├── src │ ├── README.md │ ├── crt.args.c │ ├── crt.argv.c │ ├── crt.c │ ├── crt.void.c │ ├── ctype.c │ ├── dirent.c │ ├── errno.c │ ├── fcntl.c │ ├── fenv.c │ ├── math.c │ ├── stat.c │ ├── stdio.c │ ├── stdlib.c │ ├── string.c │ ├── time.c │ └── unistd.c ├── stat.wo ├── stdio.wo ├── stdlib.wo ├── string.wo ├── time.wo └── unistd.wo ├── p.c ├── p.h ├── tests ├── README.md ├── math-tests.c └── stdio-tests.c ├── w.c └── w.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # misc 55 | save/ 56 | wcpl 57 | wcpl0 58 | wcpl0.wasm 59 | wcpl1.wasm 60 | wcpl.wasm 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 false-schemers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /c.h: -------------------------------------------------------------------------------- 1 | /* c.h (wcpl compiler) -- esl */ 2 | 3 | #pragma once 4 | 5 | /* globals */ 6 | extern long g_optlvl; /* -O arg */ 7 | extern buf_t *g_ibases; /* module include search bases */ 8 | extern buf_t *g_lbases; /* module object module search bases */ 9 | extern fsbuf_t g_funcsigs; /* unique function signatures */ 10 | extern sym_t g_crt_mod; /* C runtime module */ 11 | extern sym_t g_wasi_mod; /* WASI module */ 12 | extern sym_t g_lm_id; /* id for linear memory */ 13 | extern sym_t g_sp_id; /* id for stack pointer global */ 14 | extern sym_t g_sb_id; /* id for stack base global */ 15 | extern sym_t g_hb_id; /* id for heap base global */ 16 | extern size_t g_sdbaddr; /* static data allocation start */ 17 | extern size_t g_stacksz; /* stack size in bytes */ 18 | extern size_t g_argvbsz; /* argv buf size in bytes */ 19 | 20 | /* static eval value */ 21 | typedef struct seval { 22 | ts_t ts; /* TS_PTR or ts_numerical */ 23 | numval_t val; /* ts_numerical */ 24 | sym_t id; /* TS_PTR (can be 0 if val is 0) */ 25 | } seval_t; 26 | 27 | /* convert function type to a function signature */ 28 | extern funcsig_t *ftn2fsig(node_t *ptn, funcsig_t *pfs); 29 | /* calc size/align for ptn; prn is NULL or reference node for errors, use 0 for lvl */ 30 | extern void measure_type(const node_t *ptn, node_t *prn, size_t *psize, size_t *palign, int lvl); 31 | /* calc offset for ptn.fld; prn is NULL or reference node for errors */ 32 | extern size_t measure_offset(const node_t *ptn, node_t *prn, sym_t fld, node_t **ppftn); 33 | /* evaluate pn expression statically, putting result into pr */ 34 | extern bool static_eval(node_t *pn, buf_t *prib, seval_t *pr); 35 | /* evaluate pen arithmetic expression statically, putting result into prn */ 36 | extern bool arithmetic_eval(node_t *pen, buf_t *prib, node_t *prn); 37 | /* evaluate integer expression pen statically, putting result into pi */ 38 | extern bool arithmetic_eval_to_int(node_t *pen, buf_t *prib, int *pri); 39 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | wcpl.xyz -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | ruby RUBY_VERSION 3 | 4 | # Hello! This is where you manage which Jekyll version is used to run. 5 | # When you want to use a different version, change it below, save the 6 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: 7 | # 8 | # bundle exec jekyll serve 9 | # 10 | # This will help ensure the proper Jekyll version is running. 11 | # Happy Jekylling! 12 | # gem "jekyll", "3.2.1" 13 | 14 | # This is the default theme for new Jekyll sites. You may change this to anything you like. 15 | # gem "minima" 16 | 17 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and 18 | # uncomment the line below. To upgrade, run `bundle update github-pages`. 19 | gem "github-pages", group: :jekyll_plugins 20 | 21 | # If you have any plugins, put them here! 22 | # group :jekyll_plugins do 23 | # gem "jekyll-github-metadata", "~> 1.0" 24 | # end 25 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Jekyll! 2 | # 3 | # This config file is meant for settings that affect your whole blog, values 4 | # which you are expected to set up once and rarely edit after that. If you find 5 | # yourself editing these this file very often, consider using Jekyll's data files 6 | # feature for the data you need to update frequently. 7 | # 8 | # For technical reasons, this file is *NOT* reloaded automatically when you use 9 | # 'jekyll serve'. If you change this file, please restart the server process. 10 | 11 | # Site settings 12 | # These are used to personalize your new site. If you look in the HTML files, 13 | # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. 14 | # You can create any custom variable you would like, and they will be accessible 15 | # in the templates via {{ site.myvariable }}. 16 | 17 | title: WCPL 18 | email: esl@acm.org 19 | author: Der Magen 20 | description: Standalone self-hosted compiler/linker/libc for a subset of C targeting Webassembly/WASI. 21 | 22 | # DO NOT CHANGE THE LINE OF THIS FILE without editing .circleci/circle_urls.sh 23 | # baseurl: "/" # the subpath of your site, e.g. /blog 24 | 25 | # This is mostly for testing 26 | url: "https://wcpl.xyz" # the base hostname & protocol for your site 27 | 28 | # Social (First three Required) 29 | repo: "https://github.com/false-schemers/wcpl" 30 | github_user: "dermagen" 31 | github_repo: "wcpl" 32 | 33 | # Optional 34 | # google-analytics: G-XXXXXXXXXX 35 | # Image and (square) dimension for logo (don't start with /) 36 | # If commented, will use material hat theme 37 | logo: "assets/img/icon.png" 38 | logo_width: 32 39 | color: "#8c1515" # primary color for header, buttons 40 | #logo_width: 34 41 | #color: "#000000" # primary color for header, buttons 42 | 43 | # Build settings 44 | markdown: kramdown 45 | 46 | # If you add tags to pages, you can link them to some external search 47 | # If you want to disable this, comment the URL. 48 | # tag_search_endpoint: https://ask.cyberinfrastructure.org/search?q= 49 | tag_color: danger # danger, success, warning, primary, info, secondary 50 | 51 | # Add a page at /forum to list a set of discourse topics. The site needs 52 | # to enable "embed topics" setting 53 | # https://meta.discourse.org/t/embedding-a-list-of-discourse-topics-in-another-site/125911 54 | # discourse_site: "https://ask.cyberinfrastructure.org" 55 | # discourse_per_page: 10 56 | # discourse_category: "stanford-research-computing" 57 | # discourse_tags: null # comma separated string, leave null to not filter 58 | 59 | accentColor: black # red, purple, green, etc. 60 | themeColor: black # red, purple, green, blue, orange, purple, grey 61 | fixedNav: 'true' # true or false 62 | 63 | permalink: /:year/:title/ 64 | markdown: kramdown 65 | exclude: [_site, CHANGELOG.md, LICENSE, README.md, vendor] 66 | 67 | # Collections 68 | collections: 69 | docs: 70 | output: true 71 | permalink: /:collection/:path 72 | 73 | # Defaults 74 | defaults: 75 | - scope: 76 | path: "_docs" 77 | type: "docs" 78 | values: 79 | layout: page 80 | - 81 | scope: 82 | path: "" 83 | type: "pages" 84 | values: 85 | layout: "page" 86 | - 87 | scope: 88 | path: "posts" 89 | type: "posts" 90 | values: 91 | layout: "post" 92 | -------------------------------------------------------------------------------- /docs/_data/toc.yml: -------------------------------------------------------------------------------- 1 | - title: "About" 2 | url: "about" 3 | - title: "Getting Started" 4 | url: "docs/getting-started" 5 | children: 6 | - title: Installation 7 | url: "docs/getting-started#installation" 8 | - title: Libraries 9 | url: "docs/getting-started#libraries" 10 | - title: Modules 11 | url: "docs/getting-started#modules" 12 | - title: Use 13 | url: "docs/getting-started#use" 14 | - title: "News" 15 | url: "news" 16 | -------------------------------------------------------------------------------- /docs/_docs/example-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A Nested Page 3 | --- 4 | 5 | # A Nested Page 6 | 7 | This is an example of a page that doesn't have a permalink defined, and 8 | is not included in the table of contents (`_data/toc.yml`). This means 9 | that it will render based on it's path. Since it's in `docs/example-page.md`, 10 | the url will be `docs/example-page/`. 11 | 12 | ## Link to a subfolder 13 | 14 | Now let's say we want to link to a subfolder, specifically with this 15 | setup: 16 | 17 | ``` 18 | docs/ 19 | example-page.md (-- we are here 20 | subfolder/ 21 | example-page.md (-- we want to link here 22 | ``` 23 | 24 | You can provide the relative path to the file, like `subfolder/example-page.md` 25 | and Jekyll will handle parsing it. For example: 26 | 27 | - [here is that link](subfolder/example-page.md) 28 | 29 | And {% include doc.html name="here" path="subfolder/example-page" %} is the same link, 30 | but generated with the include statement: 31 | 32 | ``` 33 | {% raw %}{% include doc.html name="here" path="subfolder/example-page" %}{% endraw %} 34 | ``` 35 | 36 | -------------------------------------------------------------------------------- /docs/_docs/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Getting Started 3 | tags: 4 | - github 5 | --- 6 | 7 | # Getting Started 8 | 9 | ## Installation 10 | 11 | There are no prerequisites or dependencies. All you need is a C compiler. 12 | Here's how you can compile WCPL on a Unix box; instructions for other 13 | systems/compilers are similar: 14 | 15 | ``` 16 | cc -o wcpl [wcpl].c 17 | ``` 18 | 19 | ## Libraries 20 | 21 | ### Libraries already implemented 22 | 23 | - `` (C90, header only) 24 | - `` (C90) 25 | - `` (C90 + full WASI error list) 26 | - `` (with WASM limitations) 27 | - `` (C90, header only) 28 | - `` (C99, header only) 29 | - `` (C90, header only) 30 | - `` (C90, header only) 31 | - `` (C99, header only) 32 | - `` (C90, header only) 33 | - `` (C99, header only) 34 | - `` (C90, abridged: no `gets`, `tmpfile`, `tmpnam`) 35 | - `` (C90, abridged: no `system`) 36 | - `` (C90 + some POSIX-like extras) 37 | - `` (C90 + some C99 extras) 38 | - `` (C90 + some POSIX-like extras) 39 | - `` (stub to allow setting utf8 locale) 40 | - `` (header only, internal) 41 | - `` (header only, internal) 42 | - `` (header only, internal -- WASM intrinsics) 43 | - `` (POSIX-like, abridged) 44 | - `` (POSIX-like, abridged) 45 | - `` (POSIX-like, abridged) 46 | - `` (POSIX-like, abridged) 47 | - `` (header only, implemented by host) 48 | 49 | ### Libaries that won't be supported 50 | 51 | - `` (no support in WASM) 52 | - `` (no support in WASI) 53 | 54 | 55 | ## Modules 56 | 57 | WCPL treats each source file and header file as a module with a separate namespace. The name of the module is derived 58 | from the name of the file, so if a source file is named `foo.c`, its header file should be named `foo.h`; both 59 | will contribute to module named `foo`. If needed, the name of the module can be specified directly via `#pragma module` 60 | directive. Please note that module names only affect internal naming scheme used by the linker; `#include` directives 61 | have to use actual header file names (slashes in include file names are replaced by dots, so `#include ` 62 | will look for a file named `sys.etc.h`). In separate compilation scenario, each source module is compiled to a 63 | single object module as described below. 64 | 65 | ### Compilation environment 66 | 67 | - object modules can have the following extensions: `.o`, `.wo` 68 | - system object modules are looked up in library directories specified via `-L` option and `WCPL_LIBRARY_PATH` environment variable 69 | - system headers included as `#include ` can have the following extensions: (none), `.h`, `.wh` 70 | - system headers are looked up in directories given via `-I` option and `WCPL_INCLUDE_PATH` environment variable 71 | - also, system headers are looked up in `include` sub-directories of library directories as specified above 72 | - all bundled libraries (see `lib` folder) are embedded inside the executable, so they need no `-L`/`-I` options 73 | - embedded library files are logged as belonging to `res://` pseudo-directory (a compressed archive inside `l.c`) 74 | - user headers included as `#include "foo"` can have the following extensions: (none), `.h`, `.wh` 75 | - user headers are looked up first in current directory, then in system directories as stated above 76 | - user object modules should be provided explicitly as command line arguments 77 | - lookup directories should end in separator char (`/` on Un*x, `\` on Windows), file name is just appended to it 78 | 79 | 80 | ## Use 81 | 82 | There are two modes of operation: compiling a single file to object file and 83 | compiling and linking a set of source and/or object files into an executable 84 | file. 85 | 86 | ### Single-file compilation 87 | 88 | ``` 89 | wcpl -c -o infile.wo infile.c 90 | ``` 91 | 92 | If `-o` option is not specified, output goes to standard output. 93 | WCPL uses extended WAT format with symbolic identifiers in place of indices as 94 | object file format using symbolic names for relocatable constants. This way, 95 | no custom sections or relocation tables are needed. 96 | 97 | 98 | ### Compilation and linking 99 | 100 | ``` 101 | wcpl -o out.wasm infile1.c infile2.c infile3.wo ... 102 | ``` 103 | 104 | Any mix of source and object files can be given; one of the input files should 105 | contain implementation of the `main()` procedure. Library dependences are automatically 106 | loaded and used. If -o file name argument ends in `.wasm`, linker's output will be 107 | a WASM binary; otherwise, the output is in a regular WAT format with no extensions. 108 | 109 | 110 | ### Running executables 111 | 112 | WCPL can produce executables in both WASM and WAT format. Some WASM runtimes 113 | such as `wasmtime`* allow running WAT files directly and provide better disgnostics 114 | this way, e.g. symbolic stack traces; for others, WASM format should be used. Please 115 | note that WAT files may be easily converted to WASM format with `wat2wasm`** or similar 116 | tools. 117 | 118 | Please read the documentation on your WASM runtime for details on directory/environment 119 | mapping and passing command line arguments. 120 | 121 | ### Profiling executables 122 | 123 | WCPL's executables in WAT format can be profiled via Intel's `vtune` profiler while 124 | running under `wasmtime`* runtime with `--vtune` option. Runtime statistics is dispayed 125 | in terms of the original WCPL functions, so hotspots in WCPL code can be identified. 126 | 127 | ### Additional optimizations 128 | 129 | WASM executables produced by WCPL are quite small but not as fast as ones produced 130 | by industry-scale optimizing compilers such as `clang`. As a rule, executables 131 | produced by WCPL are about as fast as the ones produced by `clang`'s `-O0` mode. 132 | Fortunately, some advanced optimizations can be applied to WASM output post-factum. 133 | One tool that can be used for this purpose is `wasm-opt` from `bynaryen`*** project: 134 | 135 | ``` 136 | $ wcpl -o foo.wasm foo.c 137 | $ wasm-opt -o foo-opt.wasm -O3 foo.wasm 138 | ``` 139 | 140 | ### Self-hosting 141 | 142 | Starting with version 1.0, WCPL can compile its own source code and the resulting WASM 143 | executable produces the same results as the original. Example session using `wasmtime`* 144 | runtime may look something like this: 145 | 146 | ``` 147 | $ cc -o wcpl [wcpl].c 148 | $ ./wcpl -q -o wcpl.wasm [wcpl].c 149 | $ wasmtime --dir=. -- wcpl.wasm -q -o wcpl1.wasm [wcpl].c 150 | $ diff -s wcpl.wasm wcpl1.wasm 151 | Files wcpl.wasm and wcpl1.wasm are identical 152 | ``` 153 | 154 | \* available at https://github.com/bytecodealliance/wasmtime/releases 155 | 156 | \*\* available at https://github.com/WebAssembly/wabt/releases 157 | 158 | \*\*\* available at https://github.com/WebAssembly/binaryen 159 | -------------------------------------------------------------------------------- /docs/_docs/subfolder/example-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A Nested Page 3 | --- 4 | 5 | # A Nested Page 6 | 7 | This is an example of a page that doesn't have a permalink defined, and 8 | is not included in the table of contents (`_data/toc.yml`). 9 | -------------------------------------------------------------------------------- /docs/_includes/alert.html: -------------------------------------------------------------------------------- 1 |
2 |

{% if include.title %}{{ include.title }}{% else %}{{ include.type }}{% endif %}

3 |

{{ include.content }}

4 |
5 | 6 | -------------------------------------------------------------------------------- /docs/_includes/doc.html: -------------------------------------------------------------------------------- 1 | {% if include.name %}{{ include.name }}{% else %}{{ include.path }}{% endif %} 2 | -------------------------------------------------------------------------------- /docs/_includes/editable.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 34 | 60 | -------------------------------------------------------------------------------- /docs/_includes/embed/discourse.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Discussion Forum 8 | 9 | What questions are being asked on {{ site.discourse_site }}? The questions 10 | below are from the {{ site.discourse_category }} category. 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/_includes/footer.html: -------------------------------------------------------------------------------- 1 | 44 | 45 | {% assign slashes = page.url | split: "/" %} 46 | 47 | -------------------------------------------------------------------------------- /docs/_includes/google-analytics.html: -------------------------------------------------------------------------------- 1 | {% if site.google-analytics %} 2 | {% endif %} 9 | -------------------------------------------------------------------------------- /docs/_includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %} 9 | 10 | {% if site.author %}{% endif %} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /docs/_includes/headers.html: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /docs/_includes/navigation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% if page.disable_search %}{% else %}{% endif %} 9 | Skip to content 10 |
11 | 60 |
61 | -------------------------------------------------------------------------------- /docs/_includes/quiz.html: -------------------------------------------------------------------------------- 1 | {% assign quiz = site.data.quizzes[include.file] %} 2 | {% if quiz.randomize == true %}{% assign questions = quiz.questions | sample %}{% else %}{% assign questions = quiz.questions %}{% endif %}{% if quiz.title %}

{{ quiz.title }}

{% endif %}{% for item in questions %} 3 | {% if item.type == "multiple-choice" %}{% include quiz/multiple-choice.html item=item count=forloop.index %}{% endif %}
{% endfor %} 4 | -------------------------------------------------------------------------------- /docs/_includes/quiz/multiple-choice.html: -------------------------------------------------------------------------------- 1 |
2 |

{% if include.item.question %}{{ include.item.question }}{% else %}Question {{ include.count }}{% endif %}

3 |

{% for choice in include.item.items %}{% if choice.correct == true %}{% endif %}{{ forloop.index }}. {{ choice.choice }}{% if choice.correct == true %}{% endif %}
{% endfor %}

4 |
5 | {% if include.item.followup %}{% endif %} 6 | -------------------------------------------------------------------------------- /docs/_includes/scrolltop.html: -------------------------------------------------------------------------------- 1 | 23 | 24 | 25 | 43 | -------------------------------------------------------------------------------- /docs/_includes/sidebar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 61 |
62 |
63 |
64 |
65 |
66 |
67 | 73 |
74 |
75 |
76 | -------------------------------------------------------------------------------- /docs/_includes/social.html: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /docs/_includes/tags.html: -------------------------------------------------------------------------------- 1 | {% if site.tag_search_endpoint %}{% if page.tags %}{% endif %}{% endif %} 3 | -------------------------------------------------------------------------------- /docs/_includes/toc.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 36 | -------------------------------------------------------------------------------- /docs/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% include head.html %} 4 | 5 | 6 | {% include navigation.html %} 7 | 8 |
9 |
10 |
11 | {% include sidebar.html %} 12 |
13 | {{ content }} 14 |
15 |
16 |
17 |
18 | {% include footer.html %} 19 | {% include headers.html %} 20 | {% include tags.html %} 21 | {% include scrolltop.html %} 22 | {% include google-analytics.html %} 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/_layouts/page.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | --- 4 |
5 |
6 | {{ content }} 7 | {% include toc.html %} 8 | {% include editable.html %} 9 |
10 |
11 | -------------------------------------------------------------------------------- /docs/_layouts/post.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | --- 4 | 5 |

{{ page.title }}

6 | {% if page.badges %}{% for badge in page.badges %}{{ badge.tag }}{% endfor %}{% endif %} 7 | 8 | {{ content }} 9 | -------------------------------------------------------------------------------- /docs/_posts/2023-01-24-welcome.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Welcome to WCPL" 3 | date: 2023-01-24 5:52:21 -0500 4 | categories: jekyll update 5 | --- 6 | 7 | This is a test. I will be posting some notes on WCPL design here. 8 | 9 | 10 | 11 | For now, here is a small test of freshly minted support for WASM SIMD and 12 | LLVM-compatible `wasm_simd128.h` header: 13 | 14 | {% highlight C %} 15 | #include 16 | #include 17 | 18 | void multiply_arrays(int* out, int* in_a, int* in_b, int size) 19 | { 20 | for (int i = 0; i < size; i += 4) { 21 | v128_t a = wasm_v128_load(&in_a[i]); 22 | v128_t b = wasm_v128_load(&in_b[i]); 23 | v128_t prod = wasm_i32x4_mul(a, b); 24 | wasm_v128_store(&out[i], prod); 25 | } 26 | } 27 | 28 | int main() 29 | { 30 | int a1[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; 31 | int a2[8] = { 1, 10, 100, 1000, 1000, 100, 10, 1 }; 32 | int a3[8], i; 33 | multiply_arrays(&a3[0], &a2[0], &a1[0], 8); 34 | printf("a3[8] = { %d, %d, %d, %d, %d, %d, %d, %d };\n", 35 | a3[0], a3[1], a3[2], a3[3], a3[4], a3[5], a3[6], a3[7]); 36 | v128_t v1 = wasm_i16x8_const(1, 2, 3, 4, 5, 6, 7, 8); 37 | v128_t v2 = wasm_i16x8_const(10, 20, 30, 40, 50, 60, 70, 80); 38 | v128_t v3 = wasm_i16x8_mul(v1, v2); 39 | a3[0] = wasm_i16x8_extract_lane(v3, 0); 40 | a3[1] = wasm_i16x8_extract_lane(v3, 1); 41 | a3[2] = wasm_i16x8_extract_lane(v3, 2); 42 | a3[3] = wasm_i16x8_extract_lane(v3, 3); 43 | a3[4] = wasm_i16x8_extract_lane(v3, 4); 44 | a3[5] = wasm_i16x8_extract_lane(v3, 5); 45 | a3[6] = wasm_i16x8_extract_lane(v3, 6); 46 | a3[7] = wasm_i16x8_extract_lane(v3, 7); 47 | printf("a3[8] = { %d, %d, %d, %d, %d, %d, %d, %d };\n", 48 | a3[0], a3[1], a3[2], a3[3], a3[4], a3[5], a3[6], a3[7]); 49 | return 0; 50 | } 51 | 52 | #=> prints 'a3[8] = { 1, 20, 300, 4000, 5000, 600, 70, 8 };' to STDOUT. 53 | #=> prints 'a3[8] = { 10, 40, 90, 160, 250, 360, 490, 640 };' to STDOUT. 54 | {% endhighlight %} 55 | 56 | Check out the LLVM's original [wasm_simd128.h][wasm_simd128] for more info. 57 | 58 | [wasm_simd128]: https://github.com/llvm/llvm-project/blob/main/clang/lib/Headers/wasm_simd128.h 59 | -------------------------------------------------------------------------------- /docs/_posts/2023-05-08-libinres.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "New packaging of bundled libraries" 3 | date: 2023-05-08 5:52:21 -0500 4 | categories: wcpl update 5 | --- 6 | 7 | Starting with version `1.04`, all bundled libraries (see `lib` folder) are embedded *inside the executable*, 8 | so using them needs no `-L`/`-I` options. All embedded library files are logged as belonging to `res://` 9 | pseudo-directory (a compressed archive inside `l.c`); `res://lib/` is always used as a fallback search 10 | location, so explicit locations given via `-L` have priority and can override built-in versions if 11 | needed. 12 | 13 | The embedded archive is generated with the help of `baz` archiver (see https://github.com/false-schemers/baz); 14 | the command is: 15 | 16 | ``` 17 | baz cvfz l.c --exclude="src" --exclude="lib/include/NDEBUG" --exclude="README.md" --zopfli=1000 wcpl/lib 18 | ``` 19 | It uses external `zopfli`* compressor to get excellent compression rates for `.wo` files, so the whole 20 | built-in archive adds only about 90K to size of the executable. 21 | 22 | \* [zopfli]: https://github.com/google/zopfli 23 | 24 | -------------------------------------------------------------------------------- /docs/_posts/2023-05-21-varfuncs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Functions with variable arguments, countof()" 3 | date: 2023-05-21 5:52:21 -0500 4 | categories: wcpl update varargs stdarg 5 | --- 6 | 7 | WCPL fully supports `` header defining a type and three macros for 8 | advancing through an array of values supplied as the value of the `...` (ellipsis) 9 | argument. Internally, this argument comes last in the argument list and has the 10 | `va_list` type, defined internally as follows: 11 | 12 | 13 | 14 | {% highlight C %} 15 | typedef union va_arg { 16 | int va_int; 17 | unsigned int va_uint; 18 | long va_long; 19 | unsigned long va_ulong; 20 | long long va_llong; 21 | unsigned long long va_ullong; 22 | size_t va_size; 23 | double va_double; 24 | v128_t va_v128; 25 | void *va_voidptr; 26 | } va_arg_t, *va_list; 27 | {% endhighlight %} 28 | 29 | WCPL has no requirement of the ellipsis argument to be preceded by 'normal' arguments; if needed, 30 | `...` can be the only argument in the argument list. The ellipsis argument can be referenced 31 | in the body of the function as a pointer expression either via WCPL-specific intrinsic `va_etc()` 32 | or via its equivalent pseudo-variable `...`. The following is a legal WCPL code (which, 33 | of course, can return garbage if no arguments are supplied to `foo` by the caller): 34 | 35 | {% highlight C %} 36 | int foo(...) /* WCPL */ 37 | { 38 | return ...[0].va_int; 39 | } 40 | {% endhighlight %} 41 | 42 | It is compiled to the following WASM code: 43 | 44 | ``` 45 | (func $_:foo (export "foo") 46 | (param $ap$ i32) (result i32) 47 | local.get $ap$ 48 | i32.load offset=0 align=4 49 | return 50 | ) 51 | ``` 52 | 53 | The `va_arg` form is an intrinsic (cannot be a macro because WCPL macros can't 54 | have types as parameters): 55 | 56 | `va_arg(ap, type)` is equivalent to `*(type*)ap++` 57 | 58 | The rest of the standard macros for variable argument access are defined as follows: 59 | 60 | {% highlight C %} 61 | #define va_start(ap, v) ((ap) = ...) 62 | #define va_end(ap) ((void)(ap = NULL)) 63 | #define va_copy(dst, src) ((dst) = (src)) 64 | {% endhighlight %} 65 | 66 | 67 | Also, WCPL supports a `countof` intrinsic, which serves three purposes. It can act as 68 | a shortcut for counting the number of elements in an array, so if `a` is an array 69 | expression: 70 | 71 | `countof(a)` is equivalent to `sizeof(a)/sizeof(a[0])` 72 | 73 | Its second purpose is to access the number of the arguments supplied to vararg function: 74 | `countof(...)` returns the corresponding count, which is always stored by the caller in 75 | `va_arg_t`-typed slot preceding the first actual argument in the `...` argument array: 76 | 77 | `countof(...)` is equivalent to `...[-1].va_size` 78 | 79 | This feature makes it possible to write code like this: 80 | 81 | {% highlight C %} 82 | int sumints(...) /* WCPL */ 83 | { 84 | size_t n = countof(...); 85 | int sum = 0; 86 | for (size_t i = 0; i < n; ++i) { 87 | sum += ...[i].va_int; 88 | } 89 | return sum; 90 | } 91 | {% endhighlight %} 92 | 93 | WCPL's `` header defines an additional macro for this scenario: 94 | 95 | {% highlight C %} 96 | #define va_count() (countof(...)) /* WCPL */ 97 | {% endhighlight %} 98 | 99 | The third purpose of `countof()` is related to variadic and generic macros; it will 100 | be described in the next few posts. 101 | -------------------------------------------------------------------------------- /docs/_posts/2023-05-22-macros.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "WCPL macros" 3 | date: 2023-05-22 5:52:21 -0500 4 | categories: wcpl macros 5 | --- 6 | 7 | WCPL has a simple macro mechanism that *mimics* most frequent patterns used by 8 | C macro writers while being implemented via an entirely different mechanism. 9 | While C macros manipulate sequences of tokens, WCPL macros manipulate 10 | complete expressions; substitution is done on the level of abstract syntax trees. 11 | Although it makes it impossible to use some advanced tricks empoyed by C 12 | macro experts, such as introducing unpaired braces, substituting types, 13 | and glueing identifiers from individual parts, most popular macro patterns 14 | still can be employed, and made to behave in the same way between WCPL and C. 15 | 16 | 17 | 18 | WCPL macros have two flavors: expression macros and statement macros. 19 | Expression macros have a complete expression on the right-hand side of the 20 | macro. Formally, it is a `primary_expression` grammar category; informally, it 21 | is either a literal constant (no sign for numerical literals!) or any single 22 | expression enclosed in parentheses, e.g.: 23 | 24 | {% highlight C %} 25 | #define NINE 9 26 | #define MINUS_NINE (-9) /* has to be parenthesized! */ 27 | #define DBL_MAX 1.79769313486231570815e+308 28 | #define stdin (&_iob[0]) 29 | {% endhighlight %} 30 | 31 | Statement macro's right-hand side is single statement wrapped in a "fake do", 32 | i.e. preceded by the `do` keyword and followed by `while (0)` trailer. It may 33 | look strange, but it is indeed a standard C practice, allowing uses of such 34 | macros to be followed by a semicolon, as required by C statement grammar, e.g.: 35 | 36 | {% highlight C %} 37 | #define p2up(x, res) do { \ 38 | size_t c = clz32(x); \ 39 | res = 32-c; \ 40 | } while (0) 41 | {% endhighlight %} 42 | 43 | Please note two additional features in the example above: the name of the macro 44 | being defined can be followed by a parenthesized list of argument names (no space 45 | is allowed between the name and the open parenthesis), and long macro bodies can be 46 | wrapped into multiple lines if internal end-of-line characters are "escaped" 47 | via a backslash. The latter feature is optional in WCPL (it always reads a 48 | complete grammar category, not a token list), but supported for compatibility 49 | with C preprocessor — the macro above works in the same way both in C and WCPL. 50 | 51 | Expression macros can be parameterized as well. Please note that WCPL macro uses, 52 | which look like function calls, can only accept grammatically valid *expressions* 53 | as parameters. There is no such limitation in C, where any sequences of tokens 54 | will do, as long as they are separated by colons. Example: 55 | 56 | {% highlight C %} 57 | /* works in C and WCPL */ 58 | #define getc(p) (--(p)->cnt >= 0 ? (unsigned char)*(p)->ptr++ : _fillbuf(p)) 59 | 60 | /* works in C, but not in WCPL */ 61 | #define cast(type, x) ((type)(x)) /* C can use types as macro parameters */ 62 | {% endhighlight %} 63 | 64 | WCPL macros can only be defined on top level of the module or header file; they 65 | cannot be re-defined if an earlier definition exists, unless they undefined 66 | via `#undef` first: 67 | 68 | {% highlight C %} 69 | #undef NINE 70 | #define NINE "nine" 71 | {% endhighlight %} 72 | 73 | Some built-in macros such as `__WCPL__` cannot be undefined or redefined. 74 | Additional macro features will be described in separate posts. 75 | -------------------------------------------------------------------------------- /docs/_posts/2023-05-23-varmacs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Variadic macros, countof()" 3 | date: 2023-05-23 5:52:21 -0500 4 | categories: wcpl update varargs stdarg 5 | --- 6 | 7 | Although WCPL macros, having a completely different nature from C macros 8 | (expression-based vs. token-based), are limited compared to their C counterparts, 9 | some of advanced features of C macros are supported in a form that allows for 10 | interoperability. One such feature is *variadic macros*, i.e. macros accepting 11 | variable number of arguments. 12 | 13 | 14 | 15 | WCPL implementation of variadic macros is also expression-based. Parameterized 16 | macro can have `...` (ellipsis) as its last parameter; use of such a macro must 17 | provide at least as many parameters as there are non-ellipsis parameters in the 18 | parameter list. Any number of additional ("overflow") parameters can be supplied 19 | in macro use; they will be available for substitution in the body of the macro 20 | (the *replacement list*, in C terms). 21 | 22 | A parameterized macro with `...` as the last parameter is said to *bind* the 23 | `...` name to the list of actual parameters remaining after all non-ellipsis 24 | parameters are assigned. If `...` is bound by the macro, its use in the macro 25 | body refers to "overflow" macro parameters. If `...` is not bound by the macro, 26 | use of `...` in macro body refers to outside binding of `...`, e.g. its use 27 | as the ellipsis parameter of a function. 28 | 29 | In the body of the variadic macro, `...` can be used in any form that looks 30 | like a function call, in any argument position, and any number of times. All 31 | such uses are replaced by actual "overflow" macro parameter expressions. 32 | 33 | C99-like `__VA_ARGS__` name can be used as an alias for `...` in the body 34 | of a variadic macro. This allows for writing of some variadic macros that 35 | will work the same way in WCPL and in C99, e.g.: 36 | 37 | {% highlight C %} 38 | #define debug(msg, ...) (fprintf(stderr, msg, __VA_ARGS__)) 39 | {% endhighlight %} 40 | 41 | In WCPL, one can also use `...` in the body directly: 42 | 43 | {% highlight C %} 44 | #define debug(msg, ...) (fprintf(stderr, msg, ...)) /* WCPL only */ 45 | {% endhighlight %} 46 | 47 | Since in WCPL replacement is done on the expression level, no special 48 | comma-related hacks are needed, so `__VA_OPT__` is not supported. 49 | 50 | There is also a place for `countof(...)` in WCPL variadic macro: as 51 | one would expect, it is replaced by the count of "overflow" arguments 52 | in macro use: 53 | 54 | {% highlight C %} 55 | extern struct list *make_list(size_t cnt, ...); 56 | #define list(...) (make_list(countof(...), ...)) /* WCPL only */ 57 | 58 | /* list(10, 20, 30) expands to make_list(3, 10, 20, 30) */ 59 | {% endhighlight %} 60 | 61 | -------------------------------------------------------------------------------- /docs/_posts/2023-05-24-genmacs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Generic macros, countof()" 3 | date: 2023-05-24 5:52:22 -0500 4 | categories: wcpl update varargs stdarg 5 | --- 6 | 7 | In addition to variadic macros, WCPL supports the `generic` construct that is 8 | traditionally used in so-called *cover* macros that expand to calls of different 9 | functions based on types of their argument(s). The construct itself does not have 10 | to be used in macros, although in practice it rarely appears anywhere else. 11 | 12 | 13 | 14 | The usual C11 syntax for the `generic` construct when used in a macro looks 15 | something like this: 16 | 17 | {% highlight C %} 18 | #define sign(x) _Generic((x), \ 19 | long long: signll(x), \ 20 | long: signl(x), \ 21 | default: signi(x) \ 22 | ) 23 | {% endhighlight %} 24 | 25 | WCPL supports this syntax, provided that the `default` association, if present, 26 | is the last one in the list. The `_Generic` keyword may also be spelled as `generic`. 27 | 28 | If C compatibility is not a concern, a WCPL alternative switch-like syntax can be used 29 | (note that newline-escaping backslashes are optional in BCPL): 30 | 31 | {% highlight C %} 32 | #define sign(x) generic (x) { 33 | case long long: signll(x); 34 | case long: signl(x); 35 | default: signi(x); 36 | } 37 | {% endhighlight %} 38 | 39 | This syntax supports one extra WCPL-specific feature: generic dispatch based on 40 | argument count. It uses the same `countof(...)` construct that was described in the 41 | previous posts: 42 | 43 | {% highlight C %} 44 | #define ATAN(...) generic (countof(...)) { 45 | case 1: atan(...); 46 | case 2: atan2(...); 47 | } 48 | 49 | double foo(double x, double y, double z) { 50 | // expands to atan2(y, x) + atan(z); 51 | return ATAN(y, x) + ATAN(z); 52 | } 53 | {% endhighlight %} 54 | 55 | 56 | The examples below come from WCPL's `` header: 57 | 58 | {% highlight C %} 59 | #define _clz(x) (generic((x), \ 60 | unsigned long long: __builtin_clzll(x), \ 61 | long long: __builtin_clzll(x), \ 62 | default: __builtin_clz(x))) 63 | #define _ctz(x) (generic((x), \ 64 | unsigned long long: __builtin_ctzll(x), \ 65 | long long: __builtin_ctzll(x), \ 66 | default: __builtin_ctz(x))) 67 | #define _popcnt(x) (generic((x), \ 68 | unsigned long long: __builtin_popcountll(x), \ 69 | long long: __builtin_popcountll(x), \ 70 | default: __builtin_popcount(x))) 71 | #define _rotl(x, n) (generic((x), \ 72 | unsigned long long: __builtin_rotleftll(x, n), \ 73 | long long: __builtin_rotleftll(x, n), \ 74 | default: __builtin_rotleft(x, n))) 75 | #define _rotr(x, n) (generic((x), \ 76 | unsigned long long: __builtin_rotrightll(x, n), \ 77 | long long: __builtin_rotrightll(x, n), \ 78 | default: __builtin_rotright(x, n))) 79 | #define _fasu(x) (generic((x), \ 80 | float: __builtin_asuint32(x))) 81 | #define _dasull(x) (generic((x), \ 82 | double: __builtin_asuint64(x))) 83 | #define _uasf(x) (generic((x), \ 84 | unsigned: __builtin_asfloat(x))) 85 | #define _ullasd(x) (generic((x), \ 86 | unsigned long long: __builtin_asdouble(x))) 87 | {% endhighlight %} 88 | 89 | The last four macros use generic dispatch to ensure that no automatic 90 | promotions take place; the compiler will signal an error if a wrong-sized 91 | integer or floating-point expression is used as an argument to the 92 | corresponding builtin. 93 | 94 | -------------------------------------------------------------------------------- /docs/_posts/2023-05-25-condcomp.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Conditional compilation" 3 | date: 2023-05-25 1:52:21 -0500 4 | categories: wcpl ifdef conditional 5 | --- 6 | 7 | To support a wider set of scenarios where WCPL code is also compiled by a regular 8 | C compiler (which is not that hard due to WCPL being basically a subset of C), 9 | WCPL supports a limited set of conditional compilation constructs. The goal was 10 | to allow the insertion of fragments targeting various C compilers and platforms 11 | to be ignored by WCPL compiler on line-by-line basis. 12 | 13 | 14 | 15 | These constructs look similar to what C programmers are used to, but are not 16 | based on defined macros and constant expressions. The *conditions* being tested 17 | for inclusion or exclusion of certain blocks of text are just lexical variations 18 | on the *always true* and *always false* themes. 19 | 20 | The WCPL compiler treats the following starting constructs as *always true*: 21 | 22 | {% highlight C %} 23 | #ifdef __WCPL__ 24 | #if defined(__WCPL__) 25 | #if 1 26 | {% endhighlight %} 27 | 28 | This is the complementary list of *always false* starting constructs: 29 | 30 | {% highlight C %} 31 | #ifndef __WCPL__ 32 | #if !defined(__WCPL__) 33 | #if 0 34 | {% endhighlight %} 35 | 36 | WCPL conditional compilation block should start with one of the above constructs 37 | at the top level of a module or a header file, and end with the matching ending 38 | construct: 39 | 40 | {% highlight C %} 41 | #endif 42 | {% endhighlight %} 43 | 44 | The above constructs can contain any amount of whitespace between tokens, but 45 | should each be limited to a single line. They cannot appear inside a top-level 46 | form, only between the forms. 47 | 48 | There are additional WCPL constructs which may appear between the starting and 49 | ending lines. The text following the *always true* starting line can 50 | contain a *switch to inactive* line that tells WCPL to ignore the rest of the block 51 | until the corresponding `#endif` line. This switch-to-inactive line can have one 52 | of the following two forms: 53 | 54 | {% highlight C %} 55 | #elif // any tokens can follow #elif on the same line 56 | #else 57 | {% endhighlight %} 58 | 59 | The text between the *always true* starting line and the *switch to inactive* 60 | line, or the ending line if there is no switch, is called *active block*. 61 | The text between the *switch to inactive* line and the ending line is called 62 | *inactive block*. The compiler processes the top-level forms from the active 63 | block and skips all the lines of the inactive block, just looking for its 64 | ending line. 65 | 66 | The overall layout of the conditional compilation block that begins with the 67 | *always false* starting line is more complex. The text between its start and 68 | the corresponding ending line can contain multiple potential switch lines, 69 | but WCPL is looking for the *always true* switching line that may take one 70 | of the following forms: 71 | 72 | {% highlight C %} 73 | #elif defined(__WCPL__) 74 | #elif 1 75 | #else 76 | {% endhighlight %} 77 | 78 | Any one of these lines will act as *switch to active* line that would end 79 | current inactive block. The potential switch lines that *do not* switch may 80 | take one of the following forms: 81 | 82 | {% highlight C %} 83 | #elif !defined(__WCPL__) 84 | #elif 0 85 | {% endhighlight %} 86 | 87 | If a *switch to active* line is encountered, WCPL considers the text that 88 | follows to be an active block and continues in the manner described above for 89 | the conditional compilation block that opens with *always true* starting line. 90 | 91 | Effectively, the contents of the conditional compilation block consist 92 | of at most one active block, optionally preceded or followed by inactive 93 | blocks. It is important to note that WCPL's active block can contain 94 | not only regular WCPL top-level forms but also other properly nested 95 | WCPL conditional compilation blocks, recursively. The active parts that 96 | are taken for processing should be sequences of 0 or more top-level 97 | forms; conditional compilation constructs (lines) cannot interrupt 98 | top-level forms by appearing inside them. 99 | 100 | Since inactive blocks are just skipped line-by-line, their contents 101 | does not have to be restricted as much. In fact, WCPL allows any sequence 102 | of C99 tokens, including arbitrary but properly nested C99 conditional 103 | compilation constructs within its inactive blocks. For inactive blocks, 104 | there is no limitation on where nested conditional compilation blocks 105 | appear with respect to other language constructs, and what expressions 106 | are used as conditions (they are skipped by WCPL anyway). 107 | 108 | These sequences usually contain C code targeting other compilers and 109 | platforms, e.g.: 110 | 111 | {% highlight C %} 112 | #if defined(__WCPL__) 113 | // WCPL-specific code 114 | #elif defined(__GNUC__) && defined(__linux) 115 | // Linux-specific code (GCC/Clang) 116 | #elif defined(__GNUC__) && defined(__APPLE__) 117 | // Mac-specific code (GCC/Clang) 118 | #elif defined(_MSC_VER) 119 | // MSVC-specific code 120 | #elif defined(__STDC__) 121 | // code targeting other C compilers 122 | #else 123 | #error "put in your definitions..." 124 | #endif 125 | {% endhighlight %} 126 | 127 | While marginally useful in WCPL-only code, these constructs support 128 | some degree of mix-and-match between code targeting different platforms, 129 | which may be useful for code that needs to be compiled as both a WCPL program 130 | and a C program. 131 | 132 | -------------------------------------------------------------------------------- /docs/_posts/2023-05-26-reldecls.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Relaxed declarations" 3 | date: 2023-05-26 1:52:21 -0500 4 | categories: wcpl ifdef conditional 5 | --- 6 | 7 | This is an announcement of an upcoming feature. 8 | 9 | 10 | 11 | C declaration syntax, when you get used to it, is quite comfortable, but 12 | in is not without issues. One of its problems is inconsistency: the basic schema 13 | of *declaration-specifiers* stating the *base* type, followed by a comma-separated 14 | list of *declarators*, with variable name wrapped in a mixture of type operators, 15 | is convenient, and used for declaring structure and union members as well as variables. 16 | However, a different schema is used for function parameters, where each parameter is 17 | specified completely separately from the previous one: 18 | 19 | {% highlight C %} 20 | /* base type followed by list of declarators for int-derived types: */ 21 | int i, *ip, **ipp, (*pfi)(); 22 | 23 | /* have to repeat 'int' base type for each parameter: */ 24 | extern void foo(int i, int *ip, int **ipp, int (*pfi)()); 25 | 26 | /* but can't repeat it for each variable: */ 27 | int i, int *ip, int **ipp, int (*pfi)()); /* error! */ 28 | 29 | /* this doesn't work either: */ 30 | extern void foo(int i, *ip, **ipp, (*pfi)()); 31 | {% endhighlight %} 32 | 33 | This inconsistency is not annoying, but complicates parsing; it also inhibits 34 | some kinds of parsing for WCPL-specific syntactic constructs, so we have decided 35 | to relax the function parameter syntax to bring it in line with variable 36 | declarations syntax, and vice versa. 37 | 38 | The relaxed syntax uses the same approach for both lists of declarations. The 39 | relaxed form of declaration list starts with *declaration-specifiers* stating 40 | the base type, followed by a comma-separated list of declarators (as in 41 | C syntax for variables and members), *but*, after each comma, a new base 42 | type may be introduced, serving one or more subsequent declarations having 43 | just *declarators* and no base type of their own: 44 | 45 | {% highlight C %} 46 | /* WCPL: base type can be switched mid-list: */ 47 | int i, *ip, double d, **dpp, (*pfd)(), char c, *cp; 48 | 49 | /* WCPL: no need to repeat shared base type for sibling parameters: */ 50 | extern void foo(int i, *ip, double d, **dpp, (*pfd)(), char c, *cp); 51 | {% endhighlight %} 52 | 53 | Also WCPL structure and union declarations are allowed to omit the semicolon 54 | after the last member declaration. As declarations are relaxed too, the following 55 | syntax becomes legal: 56 | 57 | {% highlight C %} 58 | /* WCPL: struct/union syntax can omit semicolons: */ 59 | struct { int i, *ip, char c, *cp, double d } foo; 60 | {% endhighlight %} 61 | 62 | As you can see, the relaxed format is backward-compatible with C, but does 63 | not enforce C-specific dichotomy between variable/member declarations and 64 | parameter lists. Naturally, any new feature can be abused; the fact that 65 | in typedefs and forward declarations of functions parameter names are 66 | optional, allows for these cryptic forms: 67 | 68 | {% highlight C %} 69 | /* WCPL: minimalistic declaration with anonymous parameters: */ 70 | extern void foo(int, *, **, (*)()); 71 | 72 | /* same declaration in its traditional form: */ 73 | extern void foo(int i, int *ip, int **ipp, int (*pfi)()); 74 | {% endhighlight %} 75 | 76 | Taken to the extreme, this may lead to something even more cryptic: 77 | 78 | {% highlight C %} 79 | /* WCPL: truly bizarre declaration with anonymous parameters: */ 80 | extern void foo(int,,,); 81 | 82 | /* same declaration in its traditional form: */ 83 | extern void foo(int, int, int, int); 84 | {% endhighlight %} 85 | 86 | All curiosities aside, relaxing declarations in this way will allow 87 | us to parse the upcoming syntax for multiple return values while still 88 | using single-token lookahead. More on this later... 89 | -------------------------------------------------------------------------------- /docs/_posts/2023-05-27-mvsyntax.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Returning multiple values: syntax" 3 | date: 2023-05-27 1:02:21 -0500 4 | categories: wcpl return multiple values 5 | --- 6 | 7 | This is an announcement of an upcoming feature. 8 | 9 | 10 | 11 | One of experimental, but almost universally supported WASM features is 12 | *multi-value*, i.e. the ability of a WASM function to return more than one 13 | value directly, that is without allocating storage and passing around 14 | references to storage. WCPL uses *return-no-values* variant for `void` 15 | functions, but we would like to be able to support more than one value too. 16 | 17 | To add this feature to WCPL, we will need a way to represent it in the syntax 18 | of the language, and do it unambiguously and without affecting backward 19 | compatibility. This means that we can't use comma-separated values in round 20 | parentheses, because this syntax would be undistinguishable from a use 21 | of comma operator: 22 | 23 | {% highlight C %} 24 | return (true, x+2); /* comma operator! */ 25 | return true, x+2; /* comma operator! */ 26 | {% endhighlight %} 27 | 28 | We can use a keyword as a prefix, or use square brackets instead of 29 | parentheses, but there is a better way: we can repurpose the *display* 30 | notation used to initialize compound values such as structures and 31 | arrays: 32 | 33 | {% highlight C %} 34 | return {true, x+2}; /* looks vaguely familiar */ 35 | {% endhighlight %} 36 | 37 | Ideally, we'd like to be able to use the same notation on the receiving 38 | end, e.g.: 39 | 40 | {% highlight C %} 41 | void foo() { 42 | {bool ok, int val} = ret2(/*...*/); /* receive 2 values from ret2 call */ 43 | /* ... */ 44 | } 45 | {% endhighlight %} 46 | 47 | This would not only look nice and match the multi-value return syntax, but 48 | also be easily distinguishable from block statement, because in both C and 49 | WCPL block statements cannot end in an identifier and be followed by `=`. 50 | 51 | One complication is parsing: block statements can start with declarations, 52 | so we won't be able to tell what we are dealing with until we parse the 53 | first declaration. But, since we already relaxed the declaration syntax, 54 | we now can parse the whole list before we see the closing `}` which tells 55 | us that we're dealing with multi-value declaration, not with a block. 56 | Naturally, a shortcut notation can be used too: 57 | 58 | {% highlight C %} 59 | void bar() { 60 | {bool ok, int x, y} = ret3(/*...*/); /* init 1 bool, 2 int vars */ 61 | /* ... */ 62 | } 63 | {% endhighlight %} 64 | 65 | A similar parsing trick can be used while parsing left-hand-side of an 66 | assignment: if a block consists only of comma-separated argument expressions 67 | ending in `}` followed by `=`, WCPL treats it as a left-hand-side of a 68 | multiple-value assignment, not as a block, e.g.: 69 | 70 | {% highlight C %} 71 | void bar() { 72 | bool ok; int a[2]; 73 | {ok, a[0], a[1]} = ret3(/*...*/); /* assign 1 bool, 2 ints */ 74 | /* ... */ 75 | } 76 | {% endhighlight %} 77 | 78 | The remaining piece of the puzzle is declaring functions and function types. 79 | Fortunately, the same trick works here: we can parse the declaration in 80 | a relaxed way, allowing it to omit names of the variables, and consider 81 | it a *tuple* type if we hit the closing '}'. If, while doing this, we meet 82 | an initializer or step on closing semicolon, we'll know that this is the 83 | first declaration of a block statement (and check that all vars are named). 84 | Here is a complete example: 85 | 86 | {% highlight C %} 87 | {bool, int, int} ret3(int x) { 88 | if (x > 0) return {true, x, -x}; 89 | return { false, 0, 0 }; 90 | } 91 | 92 | void foo(int a) { 93 | {bool ok, int x, y} = ret3(a); 94 | if (ok) printf("OK, x = %d, y = %d\n", x, y); 95 | else printf("FAILURE!\n"); 96 | } 97 | {% endhighlight %} 98 | 99 | There is one corner case that we missed: what if we want to return zero 100 | values? With no declarations inside, '{}' can be either a block statement 101 | or a nullary tuple type. However, this ambiguity is of purely theoretical 102 | nature: we already have `void` type which can serve this purpose well. The 103 | case of singular tuple is indistinguishable from the regular return, 104 | so we can insist on our tuples to have two or more elements without 105 | losing any functionality. 106 | 107 | There is also a question of macros: macro uses look like function calls, 108 | so it would be nice if a form receiving multiple values can have a macro 109 | use on the right side. If such a macro expands into a call, things are easy. 110 | But what if we want to supply the values directly? To support this, we 111 | may allow macros to return displays as if they were not just initializers, 112 | but bona fide expressions, also allowing displays on the right side of 113 | multi-value initialization: 114 | 115 | {% highlight C %} 116 | #define FZZ() ({false, 0, 0)) 117 | 118 | void bar() { 119 | {bool ok, int x, y} = FZZ(); 120 | ... 121 | } 122 | {% endhighlight %} 123 | 124 | More on this later... 125 | -------------------------------------------------------------------------------- /docs/_posts/2023-06-03-mvsem.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Returning multiple values: semantics" 3 | date: 2023-06-03 1:02:21 -0500 4 | categories: wcpl return multiple values 5 | --- 6 | 7 | This is an announcement of an upcoming feature. Please see the previous 8 | post for syntactic aspect of WCPL multiple values support. 9 | 10 | 11 | 12 | Implementing support for returning and accepting multiple values in WCPL is 13 | easy: WASM stack architecture makes collecting and spreading the values as 14 | natural as passing arguments. In fact, the internal mechanism is exactly 15 | the same, so the same code could be used to generate pass-values / return-values 16 | code sequences. WCPL already uses the multiple-values convention by representing 17 | `void` functions as WASM procedures returning no values, so extending it to 18 | cover 2 and more values is straightforward. 19 | 20 | There is one problem though: programmers would expect that multiple values will 21 | behave as single values with respect to type promotion, e.g. if one returns an 22 | `int` and a `float`, but the accepting interface expects, say, a `long long` and a 23 | `double`, WCPL should generate the appropriate conversion/promotion instructions. 24 | It is easy with a single value because instructions work with the value on the 25 | top of the stack, but harder if values deeper in stack need to be promoted. 26 | WASM has no stack manipulation instructions allowing one to reach deeper into 27 | the stack, so WCPL has to generate temporary variables (registers), move the 28 | values from the stack to the variables, and then promote them to the new types 29 | while putting them back on the stack. 30 | 31 | There are also several design decisions that need to be made. WCPL's multiple 32 | values implementation stops short of implementing the following potentially 33 | useful features: 34 | 35 | - Expressions returning multiple values. Current implementation allows the 36 | `return` statement and right-hand-side of the multiple values definition 37 | or assignment to be an initializer-style *display* (a list of expressions 38 | in curly braces) or a call to a function returning multiple values, but 39 | nothing else. 40 | 41 | - Multiple-values casts, e.g. `({double, double})foo()`. Automatic promotions 42 | are supported, but explicit mv casts are not. 43 | 44 | - Dropping "extra" values as a part of an automatic promotion. WCPL supports 45 | "casting to void", i.e. dropping *all* return values when mv function call 46 | is executed in statement context, but does not allow passing N values to a 47 | context expecting K values where K < N unless K is 0. 48 | 49 | - Splicing multiple return values into another call. Current implementation 50 | requires accepted values to be stored in variables and other locations, 51 | but does not allow feeding them into another function call or operation 52 | directly. Some languages allow that, e.g.: `bar(x, foo()..., y)` *splices* 53 | values returned by `foo` into `bar`'s argument list, while `(+)(foo()...)` 54 | adds two values returned by `foo`. 55 | 56 | We will end this post with a complete example, demonstrating most aspects 57 | of multiple values manipulation: 58 | 59 | {% highlight C %} 60 | #include 61 | 62 | {long long, float} foo() 63 | { 64 | return {5, 42}; /* vals promoted */ 65 | } 66 | 67 | {double, float} bar() 68 | { 69 | return foo(); /* first val promoted */ 70 | } 71 | 72 | double foobar() 73 | { 74 | {double a, double b} = foo(); /* both vals promoted */ 75 | return b/a; 76 | } 77 | 78 | double baz() 79 | { 80 | double da[1], d, *pd = &d; 81 | {da[0], *pd} = bar(); /* second val promoted */ 82 | return d/da[0]; 83 | } 84 | 85 | double quux({long long, float}(*pf)()) 86 | { 87 | {double a, double b} = (*pf)(); /* both vals promoted */ 88 | return b/a; 89 | } 90 | 91 | int main() { 92 | foo(); /* ignore results */ 93 | bar(); /* ditto */ 94 | printf("%g, %g, %g\n", foobar(), baz(), quux(&foo)); 95 | return 0; 96 | } 97 | {% endhighlight %} 98 | 99 | Please note that both in function definitions and prototypes the specification 100 | of the types for returned values allows *naming* the values, e.g.: 101 | 102 | {% highlight C %} 103 | /* declaration */ 104 | static {long long position, float weight} foo(); 105 | 106 | /* definition */ 107 | static {long long position, float weight} foo() 108 | { 109 | return {5, 42}; 110 | } 111 | {% endhighlight %} 112 | 113 | We don't recommend this practice for function definitions: the names of 114 | the returned values are just ignored by the compiler, but may confuse the 115 | reader of the code: they are not variables and can neither be read nor 116 | modified. 117 | -------------------------------------------------------------------------------- /docs/assets/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/false-schemers/wcpl/458a542ca81fa7a8fd8c8eed38a32dce8ed45135/docs/assets/img/favicon.png -------------------------------------------------------------------------------- /docs/assets/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/false-schemers/wcpl/458a542ca81fa7a8fd8c8eed38a32dce8ed45135/docs/assets/img/icon.png -------------------------------------------------------------------------------- /docs/assets/img/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/false-schemers/wcpl/458a542ca81fa7a8fd8c8eed38a32dce8ed45135/docs/assets/img/splash.png -------------------------------------------------------------------------------- /docs/assets/js/modernizr.74668098.js: -------------------------------------------------------------------------------- 1 | !function(e,t){for(var n in t)e[n]=t[n]}(window,function(n){var r={};function o(e){if(r[e])return r[e].exports;var t=r[e]={i:e,l:!1,exports:{}};return n[e].call(t.exports,t,t.exports,o),t.l=!0,t.exports}return o.m=n,o.c=r,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)o.d(n,r,function(e){return t[e]}.bind(null,r));return n},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=11)}({11:function(e,t,n){"use strict";n.r(t);n(12)},12:function(e,t){var n;(function(i,d,p){function y(e,t){return typeof e===t}function s(e){var t=S.className,n=c._config.classPrefix||"";if(b&&(t=t.baseVal),c._config.enableJSClass){var r=new RegExp("(^|\\s)"+n+"no-js(\\s|$)");t=t.replace(r,"$1"+n+"js$2")}c._config.enableClasses&&(0{{this_year}} 11 |
    {% endif %} 12 |
  • 13 | {{ post.date | date: "%b %-d, %Y" }}: {{ post.title }} 14 |
  • {% if forloop.last %}
{% else %}{% if this_year != next_year %} 15 | 16 |

{{next_year}}

17 |
    {% endif %}{% endif %}{% endfor %} 18 | -------------------------------------------------------------------------------- /docs/pages/feed.xml: -------------------------------------------------------------------------------- 1 | --- 2 | layout: null 3 | permalink: /feed.xml 4 | --- 5 | 6 | 7 | 8 | {{ site.title | xml_escape }} 9 | {{ site.description | xml_escape }} 10 | {{ site.url }}{{ site.baseurl }}/ 11 | 12 | {{ site.time | date_to_rfc822 }} 13 | {{ site.time | date_to_rfc822 }} 14 | Jekyll v{{ jekyll.version }} 15 | {% for post in site.posts limit:10 %} 16 | 17 | {{ post.title | xml_escape }} 18 | {{ post.content | xml_escape }} 19 | {{ post.date | date_to_rfc822 }} 20 | {{ post.url | prepend: site.baseurl | prepend: site.url }} 21 | {{ post.url | prepend: site.baseurl | prepend: site.url }} 22 | {% for tag in post.tags %} 23 | {{ tag | xml_escape }} 24 | {% endfor %} 25 | {% for cat in post.categories %} 26 | {{ cat | xml_escape }} 27 | {% endfor %} 28 | 29 | {% endfor %} 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/pages/forum.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Discussion Forum 3 | permalink: /forum/ 4 | --- 5 | 6 | {% include embed/discourse.html %} 7 | -------------------------------------------------------------------------------- /docs/pages/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: WCPL 4 | permalink: / 5 | --- 6 | 7 | ![assets/img/splash.png](assets/img/splash.png) 8 | 9 | WCPL is a standalone self-hosted compiler/linker/libc for a subset of C targeting Webassembly and WASI. 10 | 11 | - [About](/about.md) 12 | - [Getting Started](docs/getting-started) 13 | - [News](/news.md) 14 | 15 | -------------------------------------------------------------------------------- /docs/pages/news.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: News 3 | permalink: /news/ 4 | --- 5 | 6 | # News 7 | 8 |

    Subscribe with RSS to keep up with the latest news. 9 | For site changes, see the changelog kept with the code base.

    10 | 11 | 12 | 13 | {% for post in site.posts limit:10 %} 14 |
    15 |

    {{ post.title }}

    16 |
    17 | {% if post.badges %}{% for badge in post.badges %}{{ badge.tag }}{% endfor %}{% endif %} 18 | {{ post.content | split:'' | first }} 19 | {% if post.content contains '' %} 20 | read more 21 | {% endif %} 22 |
    23 |
    24 | {% endfor %} 25 | 26 | Want to see more? See the News Archive. 27 | -------------------------------------------------------------------------------- /docs/pages/sitemap.xml: -------------------------------------------------------------------------------- 1 | --- 2 | layout: null 3 | permalink: /sitemap.xml 4 | --- 5 | 6 | 7 | 8 | 9 | / 10 | {{ "now" | date: "%Y-%m-%d" }} 11 | daily 12 | 13 | {% for section in site.data.toc %} 14 | {{ site.baseurl }}{{ section.url }}/ 15 | {{ "now" | date: "%Y-%m-%d" }} 16 | daily 17 | 18 | {% endfor %} 19 | 20 | -------------------------------------------------------------------------------- /docs/search/search_index.json: -------------------------------------------------------------------------------- 1 | --- 2 | layout: null 3 | --- 4 | {"config":{"lang":["en"],"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{% for page in site.pages %}{% unless page.excluded_in_search %}{% if added %},{% endif %}{% assign added = false %}{"location": "{{ page.url }}", "text": "{{ page.content | strip_html | strip_newlines | slugify: 'ascii' | replace: '-', ' ' }}", "title": "{{ page.title }}"}{% assign added = true %}{% endunless %}{% endfor %}{% for post in site.posts %}{% unless page.excluded_in_search %}{% if added %},{% endif %}{% assign added = false %}{"location": "{{ post.url }}", "text": "{{ post.content | strip_html | strip_newlines | slugify: 'ascii' | replace: '-',' ' }}", "title": "{{ post.title }}"}{% assign added = true %}{% endunless %}{% endfor %}{% for doc in site.docs %}{% unless doc.excluded_in_search %}{% if added %},{% endif %}{% assign added = false %}{"location": "{{ doc.url }}", "text": "{{ doc.content | strip_html | strip_newlines | slugify: 'ascii' | replace: '-',' ' }}", "title": "{{ doc.title }}"}{% assign added = true %}{% endunless %}{% endfor %}]} 5 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | Selected examples of C code ported to WCPL. Please note that all examples remain valid C programs. 5 | 6 | ## Advent 7 | 8 | The first Adventure program developed by Willie Crowtherand Don Woods, 9 | translated from Fortran to CWEB by Don Knuth, and then from CWEB to ANSI C 10 | by Arthur O'Dwyer*. Porting it to WCPL involved the following changes: 11 | 12 | - `goto`-based state machine in the main loop changed to `switch`-based one (main change) 13 | - some implicit conversions needed to be made explicit via casts 14 | - implicit conversions of arrays to pointers are made explicit as `&buf[0]` 15 | 16 | \* available at https://github.com/Quuxplusone/Advent 17 | 18 | ## Pi 19 | 20 | Fabrice Bellard's little program for calculation of decimal digits of Pi 21 | that runs in O(n^2) time, but uses very little memory. 22 | Ported by inserting explicit casts from `long long`s to `int`s. 23 | 24 | ## Calendar 25 | 26 | Simple calendar-printing program of unknown pedigree. Ported by moving 27 | static variable out of function scope. 28 | 29 | ## Sudoku 30 | 31 | Sudoku solver, a popular benchmark. Needs to be called with the 32 | accompanying `sudoku.txt` file as standard input. Ported by removing 33 | `inline` keyword, changing `!= 0` pointer comparison to `!= NULL` and 34 | replacing array arguments such as `int8_t sr[729]` with pointers. 35 | 36 | 37 | ## Binarytrees, Fannkuchredux, Nbody, Spectralnorm 38 | 39 | Benchmark programs from the Benchmarks Game site**. Ported by adding 40 | few explicit casts and moving some static variable out of function scope. 41 | 42 | \*\* see https://benchmarksgame-team.pages.debian.net/benchmarksgame/ 43 | 44 | -------------------------------------------------------------------------------- /examples/binarytrees.c: -------------------------------------------------------------------------------- 1 | /* The Computer Language Benchmarks Game 2 | * http://benchmarksgame.alioth.debian.org/ 3 | 4 | contributed by Kevin Carson 5 | compilation: 6 | gcc -O3 -fomit-frame-pointer -funroll-loops -static binary-trees.c -lm 7 | icc -O3 -ip -unroll -static binary-trees.c -lm 8 | 9 | *reset* 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | typedef struct tn { 18 | struct tn* left; 19 | struct tn* right; 20 | } treeNode; 21 | 22 | 23 | treeNode* NewTreeNode(treeNode* left, treeNode* right) 24 | { 25 | treeNode* new; 26 | 27 | new = (treeNode*)malloc(sizeof(treeNode)); 28 | 29 | new->left = left; 30 | new->right = right; 31 | 32 | return new; 33 | } /* NewTreeNode() */ 34 | 35 | 36 | long ItemCheck(treeNode* tree) 37 | { 38 | if (tree->left == NULL) 39 | return 1; 40 | return 1 + ItemCheck(tree->left) + ItemCheck(tree->right); 41 | } /* ItemCheck() */ 42 | 43 | 44 | treeNode* BottomUpTree(unsigned depth) 45 | { 46 | if (depth > 0) 47 | return NewTreeNode 48 | ( 49 | BottomUpTree(depth - 1), 50 | BottomUpTree(depth - 1) 51 | ); 52 | return NewTreeNode(NULL, NULL); 53 | } /* BottomUpTree() */ 54 | 55 | 56 | void DeleteTree(treeNode* tree) 57 | { 58 | if (tree->left != NULL) 59 | { 60 | DeleteTree(tree->left); 61 | DeleteTree(tree->right); 62 | } 63 | 64 | free(tree); 65 | } /* DeleteTree() */ 66 | 67 | 68 | int main(int argc, char* argv[]) 69 | { 70 | unsigned N, depth, minDepth, maxDepth, stretchDepth; 71 | treeNode *stretchTree, *longLivedTree, *tempTree; 72 | 73 | N = (unsigned)atol(argv[1]); 74 | 75 | minDepth = 4; 76 | 77 | if ((minDepth + 2) > N) 78 | maxDepth = minDepth + 2; 79 | else 80 | maxDepth = N; 81 | 82 | stretchDepth = maxDepth + 1; 83 | 84 | stretchTree = BottomUpTree(stretchDepth); 85 | printf 86 | ( 87 | "stretch tree of depth %u\t check: %li\n", 88 | stretchDepth, 89 | ItemCheck(stretchTree) 90 | ); 91 | 92 | DeleteTree(stretchTree); 93 | 94 | longLivedTree = BottomUpTree(maxDepth); 95 | 96 | for (depth = minDepth; depth <= maxDepth; depth += 2) 97 | { 98 | long i, iterations, check; 99 | 100 | iterations = (long)pow(2, maxDepth - depth + minDepth); 101 | 102 | check = 0; 103 | 104 | for (i = 1; i <= iterations; i++) 105 | { 106 | tempTree = BottomUpTree(depth); 107 | check += ItemCheck(tempTree); 108 | DeleteTree(tempTree); 109 | } /* for(i = 1...) */ 110 | 111 | printf 112 | ( 113 | "%li\t trees of depth %u\t check: %li\n", 114 | iterations, 115 | depth, 116 | check 117 | ); 118 | } /* for(depth = minDepth...) */ 119 | 120 | printf 121 | ( 122 | "long lived tree of depth %u\t check: %li\n", 123 | maxDepth, 124 | ItemCheck(longLivedTree) 125 | ); 126 | 127 | return 0; 128 | } /* main() */ 129 | -------------------------------------------------------------------------------- /examples/calendar.c: -------------------------------------------------------------------------------- 1 | /* C program to print the month by month 2 | * calendar for the given year */ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; 9 | int dayNumber(int day, int month, int year) 10 | { 11 | year -= month < 3; 12 | return (year + year / 4 - year / 100 + year / 400 + t[month - 1] + day) % 7; 13 | } 14 | 15 | char *getMonthName(int monthNumber) 16 | { 17 | char *month; 18 | switch (monthNumber) { 19 | case 0: month = "January"; break; 20 | case 1: month = "February"; break; 21 | case 2: month = "March"; break; 22 | case 3: month = "April"; break; 23 | case 4: month = "May"; break; 24 | case 5: month = "June"; break; 25 | case 6: month = "July"; break; 26 | case 7: month = "August"; break; 27 | case 8: month = "September"; break; 28 | case 9: month = "October"; break; 29 | case 10: month = "November"; break; 30 | case 11: month = "December"; break; 31 | } 32 | return month; 33 | } 34 | 35 | int numberOfDays(int monthNumber, int year) 36 | { 37 | switch (monthNumber) { 38 | case 0: return 31; 39 | case 1: return (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)) ? 29 : 28; 40 | case 3: return 30; 41 | case 5: return 30; 42 | case 8: return 30; 43 | case 10: return 30; 44 | } 45 | return 31; 46 | } 47 | 48 | void printCalendar(int year) 49 | { 50 | printf("\n %d\n\n", year); 51 | int i, j, k; 52 | int current = dayNumber(1, 1, year); 53 | for (i = 0; i < 12; i++) { 54 | char *mn = getMonthName(i); int len = (int)strlen(mn); 55 | int lc = (33-len)/2, rc = (33-len)-lc; 56 | int days = numberOfDays(i, year); 57 | printf("\n %.*s%s%.*s\n", lc, "------------------", mn, rc, "------------------"); 58 | printf(" Sun Mon Tue Wed Thu Fri Sat\n"); 59 | for (k = 0; k < current; k++) 60 | printf(" "); 61 | for (j = 1; j <= days; j++) { 62 | printf("%5d", j); 63 | if (++k > 6) { 64 | k = 0; 65 | printf("\n"); 66 | } 67 | } 68 | if (k) 69 | printf("\n"); 70 | current = k; 71 | } 72 | } 73 | 74 | int main(int argc, char **argv) 75 | { 76 | int year; 77 | if (argc < 2 || (year = atoi(argv[1])) <= 0) { 78 | printf("This program prints calendar for a given year\n" 79 | "usage: %s year\n", argv[0]); 80 | return 1; 81 | } 82 | printCalendar(year); 83 | return 0; 84 | } 85 | 86 | -------------------------------------------------------------------------------- /examples/fannkuchredux.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/false-schemers/wcpl/458a542ca81fa7a8fd8c8eed38a32dce8ed45135/examples/fannkuchredux.c -------------------------------------------------------------------------------- /examples/nbody.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The Computer Language Benchmarks Game 3 | * http://shootout.alioth.debian.org/ 4 | * 5 | * contributed by Christoph Bauer 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #define pi 3.141592653589793 14 | #define solar_mass (4 * pi * pi) 15 | #define days_per_year 365.24 16 | 17 | struct planet { 18 | double x, y, z; 19 | double vx, vy, vz; 20 | double mass; 21 | }; 22 | 23 | void advance(int nbodies, struct planet * bodies, double dt) 24 | { 25 | int i, j; 26 | 27 | for (i = 0; i < nbodies; i++) { 28 | struct planet * b = &(bodies[i]); 29 | for (j = i + 1; j < nbodies; j++) { 30 | struct planet * b2 = &(bodies[j]); 31 | double dx = b->x - b2->x; 32 | double dy = b->y - b2->y; 33 | double dz = b->z - b2->z; 34 | double distance = sqrt(dx * dx + dy * dy + dz * dz); 35 | double mag = dt / (distance * distance * distance); 36 | b->vx -= dx * b2->mass * mag; 37 | b->vy -= dy * b2->mass * mag; 38 | b->vz -= dz * b2->mass * mag; 39 | b2->vx += dx * b->mass * mag; 40 | b2->vy += dy * b->mass * mag; 41 | b2->vz += dz * b->mass * mag; 42 | } 43 | } 44 | for (i = 0; i < nbodies; i++) { 45 | struct planet * b = &(bodies[i]); 46 | b->x += dt * b->vx; 47 | b->y += dt * b->vy; 48 | b->z += dt * b->vz; 49 | } 50 | } 51 | 52 | double energy(int nbodies, struct planet * bodies) 53 | { 54 | double e; 55 | int i, j; 56 | 57 | e = 0.0; 58 | for (i = 0; i < nbodies; i++) { 59 | struct planet * b = &(bodies[i]); 60 | e += 0.5 * b->mass * (b->vx * b->vx + b->vy * b->vy + b->vz * b->vz); 61 | for (j = i + 1; j < nbodies; j++) { 62 | struct planet * b2 = &(bodies[j]); 63 | double dx = b->x - b2->x; 64 | double dy = b->y - b2->y; 65 | double dz = b->z - b2->z; 66 | double distance = sqrt(dx * dx + dy * dy + dz * dz); 67 | e -= (b->mass * b2->mass) / distance; 68 | } 69 | } 70 | return e; 71 | } 72 | 73 | void offset_momentum(int nbodies, struct planet * bodies) 74 | { 75 | double px = 0.0, py = 0.0, pz = 0.0; 76 | int i; 77 | for (i = 0; i < nbodies; i++) { 78 | px += bodies[i].vx * bodies[i].mass; 79 | py += bodies[i].vy * bodies[i].mass; 80 | pz += bodies[i].vz * bodies[i].mass; 81 | } 82 | bodies[0].vx = - px / solar_mass; 83 | bodies[0].vy = - py / solar_mass; 84 | bodies[0].vz = - pz / solar_mass; 85 | } 86 | 87 | #define NBODIES 5 88 | struct planet bodies[NBODIES] = { 89 | { /* sun */ 90 | 0, 0, 0, 0, 0, 0, solar_mass 91 | }, 92 | { /* jupiter */ 93 | 4.84143144246472090e+00, 94 | -1.16032004402742839e+00, 95 | -1.03622044471123109e-01, 96 | 1.66007664274403694e-03 * days_per_year, 97 | 7.69901118419740425e-03 * days_per_year, 98 | -6.90460016972063023e-05 * days_per_year, 99 | 9.54791938424326609e-04 * solar_mass 100 | }, 101 | { /* saturn */ 102 | 8.34336671824457987e+00, 103 | 4.12479856412430479e+00, 104 | -4.03523417114321381e-01, 105 | -2.76742510726862411e-03 * days_per_year, 106 | 4.99852801234917238e-03 * days_per_year, 107 | 2.30417297573763929e-05 * days_per_year, 108 | 2.85885980666130812e-04 * solar_mass 109 | }, 110 | { /* uranus */ 111 | 1.28943695621391310e+01, 112 | -1.51111514016986312e+01, 113 | -2.23307578892655734e-01, 114 | 2.96460137564761618e-03 * days_per_year, 115 | 2.37847173959480950e-03 * days_per_year, 116 | -2.96589568540237556e-05 * days_per_year, 117 | 4.36624404335156298e-05 * solar_mass 118 | }, 119 | { /* neptune */ 120 | 1.53796971148509165e+01, 121 | -2.59193146099879641e+01, 122 | 1.79258772950371181e-01, 123 | 2.68067772490389322e-03 * days_per_year, 124 | 1.62824170038242295e-03 * days_per_year, 125 | -9.51592254519715870e-05 * days_per_year, 126 | 5.15138902046611451e-05 * solar_mass 127 | } 128 | }; 129 | 130 | int main(int argc, char ** argv) 131 | { 132 | int n = atoi(argv[1]); 133 | int i; 134 | 135 | offset_momentum(NBODIES, bodies); 136 | printf ("%.9f\n", energy(NBODIES, bodies)); 137 | for (i = 1; i <= n; i++) 138 | advance(NBODIES, bodies, 0.01); 139 | printf ("%.9f\n", energy(NBODIES, bodies)); 140 | return 0; 141 | } 142 | -------------------------------------------------------------------------------- /examples/pi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Computation of the n'th decimal digit of \pi with very little memory. 3 | * Written by Fabrice Bellard on January 8, 1997. 4 | * 5 | * We use a slightly modified version of the method described by Simon 6 | * Plouffe in "On the Computation of the n'th decimal digit of various 7 | * transcendental numbers" (November 1996). We have modified the algorithm 8 | * to get a running time of O(n^2) instead of O(n^3log(n)^3). 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #define mul_mod(a, b, m) ((int)(((long long)(a) * (long long)(b)) % (m))) 16 | 17 | /* return the inverse of x mod y */ 18 | static int inv_mod(int x, int y) 19 | { 20 | int q, u, v, a, c, t; 21 | u = x; 22 | v = y; 23 | c = 1; 24 | a = 0; 25 | do { 26 | q = v / u; 27 | t = c; 28 | c = a - q * c; 29 | a = t; 30 | t = u; 31 | u = v - q * u; 32 | v = t; 33 | } while (u != 0); 34 | a = a % y; 35 | if (a < 0) 36 | a = y + a; 37 | return a; 38 | } 39 | 40 | /* return (a^b) mod m */ 41 | static int pow_mod(int a, int b, int m) 42 | { 43 | int r, aa; 44 | r = 1; 45 | aa = a; 46 | while (1) { 47 | if (b & 1) 48 | r = mul_mod(r, aa, m); 49 | b = b >> 1; 50 | if (b == 0) 51 | break; 52 | aa = mul_mod(aa, aa, m); 53 | } 54 | return r; 55 | } 56 | 57 | /* return true if n is prime */ 58 | static int is_prime(int n) 59 | { 60 | int r, i; 61 | if ((n % 2) == 0) 62 | return 0; 63 | r = (int)(sqrt(n)); 64 | for (i = 3; i <= r; i += 2) 65 | if ((n % i) == 0) 66 | return 0; 67 | return 1; 68 | } 69 | 70 | /* return the prime number immediatly after n */ 71 | static int next_prime(int n) 72 | { 73 | do n++; while (!is_prime(n)); 74 | return n; 75 | } 76 | 77 | int pi_nth_digit(int n) 78 | { 79 | int av, a, vmax, N, num, den, k, kq, kq2, t, v, s, i; 80 | double sum; 81 | N = (int)((n + 20) * log(10) / log(2)); 82 | sum = 0; 83 | for (a = 3; a <= (2 * N); a = next_prime(a)) { 84 | vmax = (int)(log(2 * N) / log(a)); 85 | av = 1; 86 | for (i = 0; i < vmax; i++) 87 | av = av * a; 88 | s = 0; 89 | num = 1; 90 | den = 1; 91 | v = 0; 92 | kq = 1; 93 | kq2 = 1; 94 | for (k = 1; k <= N; k++) { 95 | t = k; 96 | if (kq >= a) { 97 | do { 98 | t = t / a; 99 | v--; 100 | } while ((t % a) == 0); 101 | kq = 0; 102 | } 103 | kq++; 104 | num = mul_mod(num, t, av); 105 | t = (2 * k - 1); 106 | if (kq2 >= a) { 107 | if (kq2 == a) { 108 | do { 109 | t = t / a; 110 | v++; 111 | } while ((t % a) == 0); 112 | } 113 | kq2 -= a; 114 | } 115 | den = mul_mod(den, t, av); 116 | kq2 += 2; 117 | if (v > 0) { 118 | t = inv_mod(den, av); 119 | t = mul_mod(t, num, av); 120 | t = mul_mod(t, k, av); 121 | for (i = v; i < vmax; i++) 122 | t = mul_mod(t, a, av); 123 | s += t; 124 | if (s >= av) 125 | s -= av; 126 | } 127 | } 128 | t = pow_mod(10, n - 1, av); 129 | s = mul_mod(s, t, av); 130 | sum = fmod(sum + (double)s / (double)av, 1.0); 131 | } 132 | return (int)(sum * 1e9); 133 | } 134 | 135 | int main(int argc, char *argv[]) 136 | { 137 | int nmax, n; 138 | if (argc < 2 || (nmax = atoi(argv[1])) <= 0) { 139 | printf("This program computes n decimal digits of \\pi\n" 140 | "usage: %s n\n" 141 | "where n is the number of digits you want\n", argv[0]); 142 | return 1; 143 | } 144 | printf("3."); 145 | for (n = 1; n <= nmax; n += 9) 146 | printf("%09d", pi_nth_digit(n)); 147 | printf("\n"); 148 | return 0; 149 | } 150 | -------------------------------------------------------------------------------- /examples/spectralnorm.c: -------------------------------------------------------------------------------- 1 | /* The Computer Language Benchmarks Game 2 | * http://benchmarksgame.alioth.debian.org/ 3 | * 4 | * Contributed by Sebastien Loisel 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | double eval_A(int i, int j) { return 1.0/((i+j)*(i+j+1)/2+i+1); } 12 | 13 | void eval_A_times_u(int N, const double u[], double Au[]) 14 | { 15 | int i,j; 16 | for(i=0;i` (C90, header only) 24 | - `` (C90) 25 | - `` (POSIX-like, abridged) 26 | - `` (C90 + full WASI error list) 27 | - `` (POSIX-like, abridged) 28 | - `` (with WASM limitations) 29 | - `` (C90, header only) 30 | - `` (C99, header only) 31 | - `` (C90, header only) 32 | - `` (C90, header only) 33 | - `` (C99, header only) 34 | - `` (C90, header only) 35 | - `` (C99, header only) 36 | - `` (C90, abridged: no `gets`, `tmpfile`, `tmpnam`) 37 | - `` (C90, abridged: no `system`) 38 | - `` (C90 + some POSIX-like extras) 39 | - `` (header only, internal) 40 | - `` (header only, internal) 41 | - `` (POSIX-like, abridged) 42 | - `` (POSIX-like, abridged) 43 | - `` (header only, implemented by host) 44 | - `` (C90 + some C99 extras) 45 | - `` (C90 + some POSIX-like extras) 46 | - `` (stub to allow setting utf8 locale) 47 | 48 | # Modules that won't be supported 49 | 50 | - `` 51 | - `` 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /lib/crt.args.wo: -------------------------------------------------------------------------------- 1 | (module $crt 2 | (import "wasi_snapshot_preview1" "fd_write" (func $wasi_snapshot_preview1:fd_write (param i32) (param i32) (param i32) (param i32) (result i32))) 3 | (import "wasi_snapshot_preview1" "args_get" (func $wasi_snapshot_preview1:args_get (param i32) (param i32) (result i32))) 4 | (import "wasi_snapshot_preview1" "args_sizes_get" (func $wasi_snapshot_preview1:args_sizes_get (param i32) (param i32) (result i32))) 5 | (import "wasi_snapshot_preview1" "proc_exit" (func $wasi_snapshot_preview1:proc_exit (param i32))) 6 | (memory $crt:memory (export "memory") 2) 7 | (global $crt:_argv (export "_argv") (mut i32) (i32.const 0)) 8 | (global $crt:_argc (export "_argc") (mut i32) (i32.const 0)) 9 | (global $crt:onterm_count (export "onterm_count") (mut i32) (i32.const 0)) 10 | (global $crt:sp$ (export "sp$") (mut i32) (i32.const 4242)) 11 | (global $crt:stack_base (export "stack_base") i32 (i32.const 4242)) 12 | (global $crt:heap_base (export "heap_base") i32 (i32.const 4242)) 13 | (data $crt:empty_argv var align=4 size=8 14 | (ref.data $crt:ds5$ 15 | data.put_ref offset=0)) 16 | (data $crt:ds5$ const align=1 size=1) 17 | (data $crt:ds6$ const align=1 "internal error: unable to retrieve command-line arguments\n\00") 18 | (data $crt:onterm_funcs (export "onterm_funcs") var align=4 size=128) 19 | (func $crt:initialize (export "initialize") 20 | (local $error i32) (local $num_ptrs i32) (local $args_buf i32) (local $ptrs_buf i32) (local $n i32) (local $ptrs_end i32) (local $args_ptrs i32) (local $bp$ i32) (local $fp$ i32) (local $i1$ i32) (local $i2$ i32) (local $i3$ i32) 21 | global.get $crt:sp$ 22 | local.set $bp$ 23 | global.get $crt:sp$ 24 | i32.const 32 25 | i32.sub 26 | global.set $crt:sp$ 27 | global.get $crt:sp$ 28 | local.set $fp$ 29 | global.get $crt:_argv 30 | i32.const 0 31 | i32.ne 32 | if 33 | local.get $bp$ 34 | global.set $crt:sp$ 35 | return 36 | else 37 | block $err 38 | local.get $fp$ 39 | local.get $fp$ 40 | i32.const 4 41 | i32.add 42 | call $wasi_snapshot_preview1:args_sizes_get 43 | local.tee $error 44 | i32.const 65535 45 | i32.and 46 | i32.const 0 47 | i32.ne 48 | br_if $err 49 | local.get $fp$ 50 | i32.load offset=0 align=4 51 | i32.const 0 52 | i32.eq 53 | br_if $err 54 | local.get $fp$ 55 | i32.load offset=0 align=4 56 | i32.const 1 57 | i32.add 58 | local.tee $num_ptrs 59 | i32.const 0 60 | i32.eq 61 | br_if $err 62 | global.get $crt:stack_base 63 | local.tee $args_buf 64 | local.get $fp$ 65 | i32.load offset=4 align=4 66 | i32.add 67 | local.tee $ptrs_buf 68 | i32.const 16 69 | i32.rem_u 70 | local.tee $n 71 | i32.const 0 72 | i32.gt_u 73 | if 74 | local.get $ptrs_buf 75 | i32.const 16 76 | local.get $n 77 | i32.sub 78 | i32.add 79 | local.set $ptrs_buf 80 | end 81 | local.get $ptrs_buf 82 | local.get $num_ptrs 83 | i32.const 4 84 | i32.mul 85 | i32.add 86 | local.tee $ptrs_end 87 | global.get $crt:heap_base 88 | i32.gt_u 89 | br_if $err 90 | local.get $ptrs_buf 91 | local.tee $args_ptrs 92 | local.get $args_buf 93 | call $wasi_snapshot_preview1:args_get 94 | local.tee $error 95 | i32.const 65535 96 | i32.and 97 | i32.const 0 98 | i32.ne 99 | br_if $err 100 | local.get $args_ptrs 101 | local.get $fp$ 102 | i32.load offset=0 align=4 103 | i32.const 4 104 | i32.mul 105 | i32.add 106 | local.tee $i1$ 107 | i32.const 0 108 | i32.store offset=0 align=4 109 | local.get $args_ptrs 110 | global.set $crt:_argv 111 | local.get $fp$ 112 | i32.load offset=0 align=4 113 | global.set $crt:_argc 114 | local.get $bp$ 115 | global.set $crt:sp$ 116 | return 117 | end $err 118 | end 119 | local.get $fp$ 120 | i32.const 8 121 | i32.add 122 | local.tee $i2$ 123 | ref.data $crt:ds6$ 124 | i32.store offset=0 align=4 125 | local.get $fp$ 126 | i32.const 8 127 | i32.add 128 | i32.const 4 129 | i32.add 130 | local.tee $i3$ 131 | i32.const 58 132 | i32.store offset=0 align=4 133 | i32.const 2 134 | local.get $fp$ 135 | i32.const 8 136 | i32.add 137 | i32.const 1 138 | local.get $fp$ 139 | i32.const 16 140 | i32.add 141 | call $wasi_snapshot_preview1:fd_write 142 | drop 143 | ref.data $crt:empty_argv 144 | global.set $crt:_argv 145 | i32.const 1 146 | global.set $crt:_argc 147 | local.get $bp$ 148 | global.set $crt:sp$ 149 | return 150 | ) 151 | (func $crt:terminate (export "terminate") 152 | (param $status i32) 153 | (local $fn i32) 154 | block $1$ 155 | loop $2$ 156 | global.get $crt:onterm_count 157 | i32.const 0 158 | i32.gt_u 159 | if 160 | ref.data $crt:onterm_funcs 161 | global.get $crt:onterm_count 162 | i32.const 1 163 | i32.sub 164 | global.set $crt:onterm_count 165 | global.get $crt:onterm_count 166 | i32.const 4 167 | i32.mul 168 | i32.add 169 | i32.load offset=0 align=4 170 | local.tee $fn 171 | call_indirect (param) (result) 172 | br $2$ 173 | end 174 | end $2$ 175 | end $1$ 176 | local.get $status 177 | call $wasi_snapshot_preview1:proc_exit 178 | return 179 | ) 180 | ) 181 | -------------------------------------------------------------------------------- /lib/crt.argv.wo: -------------------------------------------------------------------------------- 1 | (module $crt 2 | (import "wasi_snapshot_preview1" "fd_write" (func $wasi_snapshot_preview1:fd_write (param i32) (param i32) (param i32) (param i32) (result i32))) 3 | (import "wasi_snapshot_preview1" "args_get" (func $wasi_snapshot_preview1:args_get (param i32) (param i32) (result i32))) 4 | (import "wasi_snapshot_preview1" "args_sizes_get" (func $wasi_snapshot_preview1:args_sizes_get (param i32) (param i32) (result i32))) 5 | (import "wasi_snapshot_preview1" "proc_exit" (func $wasi_snapshot_preview1:proc_exit (param i32))) 6 | (import "stdlib" "calloc" (func $stdlib:calloc (param i32) (param i32) (result i32))) 7 | (import "stdlib" "free" (func $stdlib:free (param i32))) 8 | (import "stdlib" "malloc" (func $stdlib:malloc (param i32) (result i32))) 9 | (memory $crt:memory (export "memory") 2) 10 | (global $crt:_argv (export "_argv") (mut i32) (i32.const 0)) 11 | (global $crt:_argc (export "_argc") (mut i32) (i32.const 0)) 12 | (global $crt:onterm_count (export "onterm_count") (mut i32) (i32.const 0)) 13 | (global $crt:sp$ (export "sp$") (mut i32) (i32.const 4242)) 14 | (global $crt:stack_base (export "stack_base") i32 (i32.const 4242)) 15 | (global $crt:heap_base (export "heap_base") i32 (i32.const 4242)) 16 | (data $crt:empty_argv var align=4 size=8 17 | (ref.data $crt:ds3$ 18 | data.put_ref offset=0)) 19 | (data $crt:ds3$ const align=1 size=1) 20 | (data $crt:ds4$ const align=1 "internal error: unable to retrieve command-line arguments\n\00") 21 | (data $crt:onterm_funcs (export "onterm_funcs") var align=4 size=128) 22 | (func $crt:initialize (export "initialize") 23 | (local $error i32) (local $num_ptrs i32) (local $args_buf i32) (local $args_ptrs i32) (local $bp$ i32) (local $fp$ i32) (local $i1$ i32) (local $i2$ i32) (local $i3$ i32) 24 | global.get $crt:sp$ 25 | local.set $bp$ 26 | global.get $crt:sp$ 27 | i32.const 32 28 | i32.sub 29 | global.set $crt:sp$ 30 | global.get $crt:sp$ 31 | local.set $fp$ 32 | global.get $crt:_argv 33 | i32.const 0 34 | i32.ne 35 | if 36 | local.get $bp$ 37 | global.set $crt:sp$ 38 | return 39 | else 40 | block $err 41 | local.get $fp$ 42 | local.get $fp$ 43 | i32.const 4 44 | i32.add 45 | call $wasi_snapshot_preview1:args_sizes_get 46 | local.tee $error 47 | i32.const 65535 48 | i32.and 49 | i32.const 0 50 | i32.ne 51 | br_if $err 52 | local.get $fp$ 53 | i32.load offset=0 align=4 54 | i32.const 0 55 | i32.eq 56 | br_if $err 57 | local.get $fp$ 58 | i32.load offset=0 align=4 59 | i32.const 1 60 | i32.add 61 | local.tee $num_ptrs 62 | i32.const 0 63 | i32.eq 64 | br_if $err 65 | local.get $fp$ 66 | i32.load offset=4 align=4 67 | call $stdlib:malloc 68 | local.tee $args_buf 69 | i32.const 0 70 | i32.eq 71 | br_if $err 72 | local.get $num_ptrs 73 | i32.const 4 74 | call $stdlib:calloc 75 | local.tee $args_ptrs 76 | i32.const 0 77 | i32.eq 78 | if 79 | local.get $args_buf 80 | call $stdlib:free 81 | br $err 82 | end 83 | local.get $args_ptrs 84 | local.get $args_buf 85 | call $wasi_snapshot_preview1:args_get 86 | local.tee $error 87 | i32.const 65535 88 | i32.and 89 | i32.const 0 90 | i32.ne 91 | br_if $err 92 | local.get $args_ptrs 93 | local.get $fp$ 94 | i32.load offset=0 align=4 95 | i32.const 4 96 | i32.mul 97 | i32.add 98 | local.tee $i1$ 99 | i32.const 0 100 | i32.store offset=0 align=4 101 | local.get $args_ptrs 102 | global.set $crt:_argv 103 | local.get $fp$ 104 | i32.load offset=0 align=4 105 | global.set $crt:_argc 106 | local.get $bp$ 107 | global.set $crt:sp$ 108 | return 109 | end $err 110 | end 111 | local.get $fp$ 112 | i32.const 8 113 | i32.add 114 | local.tee $i2$ 115 | ref.data $crt:ds4$ 116 | i32.store offset=0 align=4 117 | local.get $fp$ 118 | i32.const 8 119 | i32.add 120 | i32.const 4 121 | i32.add 122 | local.tee $i3$ 123 | i32.const 58 124 | i32.store offset=0 align=4 125 | i32.const 2 126 | local.get $fp$ 127 | i32.const 8 128 | i32.add 129 | i32.const 1 130 | local.get $fp$ 131 | i32.const 16 132 | i32.add 133 | call $wasi_snapshot_preview1:fd_write 134 | drop 135 | ref.data $crt:empty_argv 136 | global.set $crt:_argv 137 | i32.const 1 138 | global.set $crt:_argc 139 | local.get $bp$ 140 | global.set $crt:sp$ 141 | return 142 | ) 143 | (func $crt:terminate (export "terminate") 144 | (param $status i32) 145 | (local $fn i32) 146 | block $1$ 147 | loop $2$ 148 | global.get $crt:onterm_count 149 | i32.const 0 150 | i32.gt_u 151 | if 152 | ref.data $crt:onterm_funcs 153 | global.get $crt:onterm_count 154 | i32.const 1 155 | i32.sub 156 | global.set $crt:onterm_count 157 | global.get $crt:onterm_count 158 | i32.const 4 159 | i32.mul 160 | i32.add 161 | i32.load offset=0 align=4 162 | local.tee $fn 163 | call_indirect (param) (result) 164 | br $2$ 165 | end 166 | end $2$ 167 | end $1$ 168 | local.get $status 169 | call $wasi_snapshot_preview1:proc_exit 170 | return 171 | ) 172 | ) 173 | -------------------------------------------------------------------------------- /lib/crt.void.wo: -------------------------------------------------------------------------------- 1 | (module $crt 2 | (import "wasi_snapshot_preview1" "proc_exit" (func $wasi_snapshot_preview1:proc_exit (param i32))) 3 | (memory $crt:memory (export "memory") 2) 4 | (global $crt:_argc (export "_argc") (mut i32) (i32.const 1)) 5 | (global $crt:onterm_count (export "onterm_count") (mut i32) (i32.const 0)) 6 | (global $crt:sp$ (export "sp$") (mut i32) (i32.const 4242)) 7 | (global $crt:stack_base (export "stack_base") i32 (i32.const 4242)) 8 | (global $crt:heap_base (export "heap_base") i32 (i32.const 4242)) 9 | (data $crt:_argv (export "_argv") var align=4 size=8 10 | (ref.data $crt:ds1$ 11 | data.put_ref offset=0)) 12 | (data $crt:ds1$ const align=1 size=1) 13 | (data $crt:onterm_funcs (export "onterm_funcs") var align=4 size=128) 14 | (func $crt:initialize (export "initialize") 15 | return 16 | ) 17 | (func $crt:terminate (export "terminate") 18 | (param $status i32) 19 | (local $fn i32) 20 | block $1$ 21 | loop $2$ 22 | global.get $crt:onterm_count 23 | i32.const 0 24 | i32.gt_u 25 | if 26 | ref.data $crt:onterm_funcs 27 | global.get $crt:onterm_count 28 | i32.const 1 29 | i32.sub 30 | global.set $crt:onterm_count 31 | global.get $crt:onterm_count 32 | i32.const 4 33 | i32.mul 34 | i32.add 35 | i32.load offset=0 align=4 36 | local.tee $fn 37 | call_indirect (param) (result) 38 | br $2$ 39 | end 40 | end $2$ 41 | end $1$ 42 | local.get $status 43 | call $wasi_snapshot_preview1:proc_exit 44 | return 45 | ) 46 | ) 47 | -------------------------------------------------------------------------------- /lib/crt.wo: -------------------------------------------------------------------------------- 1 | (module $crt 2 | (memory $crt:memory (export "memory") 2) 3 | (global $crt:sp$ (export "sp$") (mut i32) (i32.const 4242)) 4 | (global $crt:stack_base (export "stack_base") i32 (i32.const 4242)) 5 | (global $crt:heap_base (export "heap_base") i32 (i32.const 4242)) 6 | ) 7 | -------------------------------------------------------------------------------- /lib/ctype.wo: -------------------------------------------------------------------------------- 1 | (module $ctype 2 | (import "crt" "sp$" (global $crt:sp$ (mut i32))) 3 | (import "crt" "memory" (memory $crt:memory 0)) 4 | (func $ctype:isalnum (export "isalnum") 5 | (param $c i32) (result i32) 6 | local.get $c 7 | call $ctype:isalpha 8 | if (result i32) 9 | i32.const 1 10 | else 11 | local.get $c 12 | call $ctype:isdigit 13 | end 14 | return 15 | ) 16 | (func $ctype:isalpha (export "isalpha") 17 | (param $c i32) (result i32) 18 | local.get $c 19 | call $ctype:islower 20 | if (result i32) 21 | i32.const 1 22 | else 23 | local.get $c 24 | call $ctype:isupper 25 | end 26 | return 27 | ) 28 | (func $ctype:iscntrl (export "iscntrl") 29 | (param $c i32) (result i32) 30 | local.get $c 31 | call $ctype:isprint 32 | i32.eqz 33 | return 34 | ) 35 | (func $ctype:isdigit (export "isdigit") 36 | (param $c i32) (result i32) 37 | local.get $c 38 | i32.const 48 39 | i32.sub 40 | i32.const 9 41 | i32.le_u 42 | return 43 | ) 44 | (func $ctype:isgraph (export "isgraph") 45 | (param $c i32) (result i32) 46 | local.get $c 47 | call $ctype:isprint 48 | if (result i32) 49 | local.get $c 50 | call $ctype:isspace 51 | i32.eqz 52 | else 53 | i32.const 0 54 | end 55 | return 56 | ) 57 | (func $ctype:islower (export "islower") 58 | (param $c i32) (result i32) 59 | local.get $c 60 | i32.const 97 61 | i32.ge_s 62 | if (result i32) 63 | local.get $c 64 | i32.const 122 65 | i32.le_s 66 | else 67 | i32.const 0 68 | end 69 | return 70 | ) 71 | (func $ctype:isprint (export "isprint") 72 | (param $c i32) (result i32) 73 | local.get $c 74 | i32.const 32 75 | i32.ge_s 76 | if (result i32) 77 | local.get $c 78 | i32.const 126 79 | i32.le_s 80 | else 81 | i32.const 0 82 | end 83 | return 84 | ) 85 | (func $ctype:ispunct (export "ispunct") 86 | (param $c i32) (result i32) 87 | local.get $c 88 | call $ctype:isprint 89 | if (result i32) 90 | local.get $c 91 | call $ctype:isalnum 92 | i32.eqz 93 | else 94 | i32.const 0 95 | end 96 | if (result i32) 97 | local.get $c 98 | call $ctype:isspace 99 | i32.eqz 100 | else 101 | i32.const 0 102 | end 103 | return 104 | ) 105 | (func $ctype:isspace (export "isspace") 106 | (param $c i32) (result i32) 107 | local.get $c 108 | i32.const 32 109 | i32.eq 110 | if (result i32) 111 | i32.const 1 112 | else 113 | local.get $c 114 | i32.const 10 115 | i32.eq 116 | end 117 | if (result i32) 118 | i32.const 1 119 | else 120 | local.get $c 121 | i32.const 9 122 | i32.eq 123 | end 124 | if (result i32) 125 | i32.const 1 126 | else 127 | local.get $c 128 | i32.const 13 129 | i32.eq 130 | end 131 | return 132 | ) 133 | (func $ctype:isupper (export "isupper") 134 | (param $c i32) (result i32) 135 | local.get $c 136 | i32.const 65 137 | i32.ge_s 138 | if (result i32) 139 | local.get $c 140 | i32.const 90 141 | i32.le_s 142 | else 143 | i32.const 0 144 | end 145 | return 146 | ) 147 | (func $ctype:isxdigit (export "isxdigit") 148 | (param $c i32) (result i32) 149 | local.get $c 150 | call $ctype:isdigit 151 | if (result i32) 152 | i32.const 1 153 | else 154 | local.get $c 155 | i32.const 97 156 | i32.ge_s 157 | if (result i32) 158 | local.get $c 159 | i32.const 102 160 | i32.le_s 161 | else 162 | i32.const 0 163 | end 164 | end 165 | if (result i32) 166 | i32.const 1 167 | else 168 | local.get $c 169 | i32.const 65 170 | i32.ge_s 171 | if (result i32) 172 | local.get $c 173 | i32.const 70 174 | i32.le_s 175 | else 176 | i32.const 0 177 | end 178 | end 179 | return 180 | ) 181 | (func $ctype:isascii (export "isascii") 182 | (param $c i32) (result i32) 183 | local.get $c 184 | i32.const -128 185 | i32.and 186 | i32.eqz 187 | return 188 | ) 189 | (func $ctype:isblank (export "isblank") 190 | (param $c i32) (result i32) 191 | local.get $c 192 | i32.const 9 193 | i32.eq 194 | if (result i32) 195 | i32.const 1 196 | else 197 | local.get $c 198 | i32.const 32 199 | i32.eq 200 | end 201 | return 202 | ) 203 | (func $ctype:toupper (export "toupper") 204 | (param $c i32) (result i32) 205 | local.get $c 206 | call $ctype:islower 207 | if (result i32) 208 | local.get $c 209 | i32.const -33 210 | i32.and 211 | else 212 | local.get $c 213 | end 214 | return 215 | ) 216 | (func $ctype:tolower (export "tolower") 217 | (param $c i32) (result i32) 218 | local.get $c 219 | call $ctype:isupper 220 | if (result i32) 221 | local.get $c 222 | i32.const 32 223 | i32.or 224 | else 225 | local.get $c 226 | end 227 | return 228 | ) 229 | ) 230 | -------------------------------------------------------------------------------- /lib/fenv.wo: -------------------------------------------------------------------------------- 1 | (module $fenv 2 | (import "crt" "sp$" (global $crt:sp$ (mut i32))) 3 | (import "crt" "memory" (memory $crt:memory 0)) 4 | (func $fenv:feclearexcept (export "feclearexcept") 5 | (param $mask i32) 6 | return 7 | ) 8 | (func $fenv:fegetexceptflag (export "fegetexceptflag") 9 | (param $flagp i32) (param $excepts i32) 10 | return 11 | ) 12 | (func $fenv:feraiseexcept (export "feraiseexcept") 13 | (param $excepts i32) 14 | return 15 | ) 16 | (func $fenv:fesetexceptflag (export "fesetexceptflag") 17 | (param $flagp i32) (param $excepts i32) 18 | return 19 | ) 20 | (func $fenv:fetestexcept (export "fetestexcept") 21 | (param $excepts i32) (result i32) 22 | i32.const 0 23 | return 24 | ) 25 | (func $fenv:fegetround (export "fegetround") 26 | (result i32) 27 | i32.const 0 28 | return 29 | ) 30 | (func $fenv:fesetround (export "fesetround") 31 | (param $round i32) (result i32) 32 | i32.const 0 33 | return 34 | ) 35 | (func $fenv:fegetenv (export "fegetenv") 36 | (param $envp i32) 37 | return 38 | ) 39 | (func $fenv:feholdexcept (export "feholdexcept") 40 | (param $envp i32) (result i32) 41 | i32.const 0 42 | return 43 | ) 44 | (func $fenv:fesetenv (export "fesetenv") 45 | (param $envp i32) 46 | return 47 | ) 48 | (func $fenv:feupdateenv (export "feupdateenv") 49 | (param $envp i32) 50 | return 51 | ) 52 | ) 53 | -------------------------------------------------------------------------------- /lib/include/NDEBUG/assert.h: -------------------------------------------------------------------------------- 1 | /* Diagnostics */ 2 | 3 | #pragma once 4 | 5 | /* this is a NDEBUG header */ 6 | /* static_assert is built-in */ 7 | 8 | #define NDEBUG 1 9 | #define assert(e) ((void)0) 10 | -------------------------------------------------------------------------------- /lib/include/assert.h: -------------------------------------------------------------------------------- 1 | /* Diagnostics */ 2 | 3 | #pragma once 4 | 5 | /* this is a regular header */ 6 | /* static_assert is built-in */ 7 | 8 | #define NDEBUG 0 9 | #define assert(e) ((e) ? (void)0 : (void)asm(unreachable)) 10 | -------------------------------------------------------------------------------- /lib/include/ctype.h: -------------------------------------------------------------------------------- 1 | /* Character handling */ 2 | 3 | #pragma once 4 | 5 | extern int isalnum(int c); 6 | extern int isalpha(int c); 7 | extern int iscntrl(int c); 8 | extern int isdigit(int c); 9 | extern int isgraph(int c); 10 | extern int islower(int c); 11 | extern int isprint(int c); 12 | extern int ispunct(int c); 13 | extern int isspace(int c); 14 | extern int isupper(int c); 15 | extern int isxdigit(int c); 16 | extern int toupper(int c); 17 | extern int tolower(int c); 18 | /* nonstandard but common */ 19 | extern int isascii(int c); 20 | extern int isblank(int c); 21 | -------------------------------------------------------------------------------- /lib/include/dirent.h: -------------------------------------------------------------------------------- 1 | /* format of directory entries */ 2 | 3 | #pragma once 4 | #include 5 | 6 | #define DT_UNKNOWN 0 /* = WASI FILETYPE_UNKNOWN */ 7 | #define DT_BLK 1 /* = WASI FILETYPE_BLOCK_DEVICE */ 8 | #define DT_CHR 2 /* = WASI FILETYPE_CHARACTER_DEVICE */ 9 | #define DT_DIR 3 /* = WASI FILETYPE_DIRECTORY */ 10 | #define DT_REG 4 /* = WASI FILETYPE_REGULAR_FILE */ 11 | #define DT_FIFO 6 /* = WASI FILETYPE_SOCKET_STREAM */ 12 | #define DT_LNK 7 /* = WASI FILETYPE_SYMBOLIC_LINK */ 13 | 14 | struct dirent { 15 | ino_t d_ino; 16 | unsigned char d_type; 17 | char d_name[1]; 18 | }; 19 | 20 | typedef struct _DIR DIR; 21 | 22 | extern DIR *fdopendir(int fd); 23 | extern int fdclosedir(DIR *dirp); 24 | extern DIR *opendirat(int dirfd, const char *dirname); 25 | extern DIR *opendir(const char *dirname); 26 | extern int dirfd(DIR *dirp); 27 | extern int closedir(DIR *dirp); 28 | extern struct dirent *readdir(DIR *dirp); 29 | extern void rewinddir(DIR *dirp); 30 | extern void seekdir(DIR *dirp, long loc); 31 | extern long telldir(DIR *dirp); 32 | extern int scandirat(int dirfd, const char *dir, struct dirent ***namelist, 33 | int (*filter)(const struct dirent *), 34 | int (*compar)(const struct dirent **, const struct dirent **)); 35 | extern int scandir(const char *dir, struct dirent ***namelist, 36 | int (*filter)(const struct dirent *), 37 | int (*compar)(const struct dirent **, const struct dirent **)); 38 | extern int alphasort (const struct dirent **a, const struct dirent **b); 39 | 40 | /* NYI: 41 | int readdir_r(DIR *restrict, struct dirent *restrict, struct dirent **restrict); 42 | */ 43 | -------------------------------------------------------------------------------- /lib/include/errno.h: -------------------------------------------------------------------------------- 1 | /* Errors */ 2 | 3 | #pragma once 4 | 5 | #define ESUCCESS 0 6 | #define E2BIG 1 7 | #define EACCES 2 8 | #define EADDRINUSE 3 9 | #define EADDRNOTAVAIL 4 10 | #define EAFNOSUPPORT 5 11 | #define EAGAIN 6 12 | #define EALREADY 7 13 | #define EBADF 8 14 | #define EBADMSG 9 15 | #define EBUSY 10 16 | #define ECANCELED 11 17 | #define ECHILD 12 18 | #define ECONNABORTED 13 19 | #define ECONNREFUSED 14 20 | #define ECONNRESET 15 21 | #define EDEADLK 16 22 | #define EDESTADDRREQ 17 23 | #define EDOM 18 24 | #define EDQUOT 19 25 | #define EEXIST 20 26 | #define EFAULT 21 27 | #define EFBIG 22 28 | #define EHOSTUNREACH 23 29 | #define EIDRM 24 30 | #define EILSEQ 25 31 | #define EINPROGRESS 26 32 | #define EINTR 27 33 | #define EINVAL 28 34 | #define EIO 29 35 | #define EISCONN 30 36 | #define EISDIR 31 37 | #define ELOOP 32 38 | #define EMFILE 33 39 | #define EMLINK 34 40 | #define EMSGSIZE 35 41 | #define EMULTIHOP 36 42 | #define ENAMETOOLONG 37 43 | #define ENETDOWN 38 44 | #define ENETRESET 39 45 | #define ENETUNREACH 40 46 | #define ENFILE 41 47 | #define ENOBUFS 42 48 | #define ENODEV 43 49 | #define ENOENT 44 50 | #define ENOEXEC 45 51 | #define ENOLCK 46 52 | #define ENOLINK 47 53 | #define ENOMEM 48 54 | #define ENOMSG 49 55 | #define ENOPROTOOPT 50 56 | #define ENOSPC 51 57 | #define ENOSYS 52 58 | #define ENOTCONN 53 59 | #define ENOTDIR 54 60 | #define ENOTEMPTY 55 61 | #define ENOTRECOVERABLE 56 62 | #define ENOTSOCK 57 63 | #define ENOTSUP 58 64 | #define ENOTTY 59 65 | #define ENXIO 60 66 | #define EOVERFLOW 61 67 | #define EOWNERDEAD 62 68 | #define EPERM 63 69 | #define EPIPE 64 70 | #define EPROTO 65 71 | #define EPROTONOSUPPORT 66 72 | #define EPROTOTYPE 67 73 | #define ERANGE 68 74 | #define EROFS 69 75 | #define ESPIPE 70 76 | #define ESRCH 71 77 | #define ESTALE 72 78 | #define ETIMEDOUT 73 79 | #define ETXTBSY 74 80 | #define EXDEV 75 81 | #define ENOTCAPABLE 76 82 | 83 | extern int errno; 84 | 85 | #define ERRNO_MAXERR 76 86 | extern const char *_emsg[ERRNO_MAXERR+1]; 87 | -------------------------------------------------------------------------------- /lib/include/fcntl.h: -------------------------------------------------------------------------------- 1 | /* file control options (abridged) */ 2 | 3 | #pragma once 4 | #include 5 | #include 6 | 7 | #define O_APPEND (0x00000001) /* = WASI FDFLAGS_APPEND */ 8 | #define O_DSYNC (0x00000002) /* = WASI FDFLAGS_DSYNC */ 9 | #define O_NONBLOCK (0x00000004) /* = WASI FDFLAGS_NONBLOCK */ 10 | #define O_RSYNC (0x00000008) /* = WASI FDFLAGS_RSYNC */ 11 | #define O_SYNC (0x00000010) /* = WASI FDFLAGS_SYNC */ 12 | #define O_CREAT (0x00001000) /* = WASI OFLAGS_CREAT<<12 */ 13 | #define O_DIRECTORY (0x00002000) /* = WASI OFLAGS_DIRECTORY<<12 */ 14 | #define O_EXCL (0x00004000) /* = WASI OFLAGS_EXCL<<12 */ 15 | #define O_TRUNC (0x00008000) /* = WASI OFLAGS_TRUNC<<12 */ 16 | #define O_NOFOLLOW (0x01000000) 17 | #define O_EXEC (0x02000000) 18 | #define O_RDONLY (0x04000000) 19 | #define O_SEARCH (0x08000000) 20 | #define O_WRONLY (0x10000000) 21 | #define O_RDWR (O_RDONLY|O_WRONLY) 22 | #define O_ACCMODE (O_EXEC|O_RDWR|O_SEARCH) 23 | 24 | #define POSIX_FADV_NORMAL (0) /* = WASI ADVICE_NORMAL */ 25 | #define POSIX_FADV_SEQUENTIAL (1) /* = WASI ADVICE_SEQUENTIAL */ 26 | #define POSIX_FADV_RANDOM (2) /* = WASI ADVICE_RANDOM */ 27 | #define POSIX_FADV_WILLNEED (3) /* = WASI ADVICE_WILLNEED */ 28 | #define POSIX_FADV_DONTNEED (4) /* = WASI ADVICE_DONTNEED */ 29 | #define POSIX_FADV_NOREUSE (5) /* = WASI ADVICE_NOREUSE */ 30 | 31 | #define F_GETFD (1) 32 | #define F_SETFD (2) 33 | #define F_GETFL (3) 34 | #define F_SETFL (4) 35 | 36 | #define FD_CLOEXEC (1) 37 | 38 | #define AT_EACCESS (0x0) 39 | #define AT_SYMLINK_NOFOLLOW (0x1) 40 | #define AT_SYMLINK_FOLLOW (0x2) 41 | #define AT_REMOVEDIR (0x4) 42 | 43 | /* #define AT_FDCWD (-2) -- related *at functionality still missing */ 44 | 45 | extern int open(const char *path, int oflags); 46 | extern int openat(int fd, const char *path, int oflags); 47 | extern int fcntl(int fd, int cmd, ...); 48 | extern int posix_fadvise(int fd, off_t offset, off_t len, int advice); 49 | extern int posix_fallocate(int fd, off_t offset, off_t len); 50 | 51 | /* NYI 52 | extern int open(const char *path, int oflags, mode_t); 53 | extern int openat(int fd, const char *path, int oflags, mode_t); 54 | extern int creat(const char *path, mode_t); 55 | */ 56 | 57 | /* helper function for abspath calls; returns fd or -1 */ 58 | extern int find_relpath(const char *path, char **relpath); 59 | -------------------------------------------------------------------------------- /lib/include/fenv.h: -------------------------------------------------------------------------------- 1 | /* Floating-point environment */ 2 | 3 | #pragma once 4 | 5 | /* NB: WASM does not support one, so this is a fake */ 6 | 7 | #define FE_ALL_EXCEPT 0 8 | #define FE_TONEAREST 0 9 | 10 | typedef unsigned long fexcept_t; 11 | 12 | typedef struct { 13 | unsigned long __cw; 14 | } fenv_t; 15 | 16 | #define FE_DFL_ENV ((const fenv_t *)-1) 17 | 18 | extern void feclearexcept(int excepts); 19 | extern void fegetexceptflag(fexcept_t* flagp, int excepts); 20 | extern void feraiseexcept(int excepts); 21 | extern void fesetexceptflag(const fexcept_t* flagp, int excepts); 22 | extern int fetestexcept(int excepts); 23 | extern int fegetround(void); 24 | extern int fesetround(int round); 25 | extern void fegetenv(fenv_t* envp); 26 | extern int feholdexcept(fenv_t* envp); 27 | extern void fesetenv(const fenv_t* envp); 28 | extern void feupdateenv(const fenv_t* envp); 29 | 30 | -------------------------------------------------------------------------------- /lib/include/float.h: -------------------------------------------------------------------------------- 1 | /* Implementation limits (fp) */ 2 | 3 | #pragma once 4 | 5 | #define DBL_DIG 15 6 | #define DBL_MANT_DIG 53 7 | #define DBL_MAX_10_EXP 308 8 | #define DBL_MAX_EXP 1024 9 | #define DBL_MIN_10_EXP (-307) 10 | #define DBL_MIN_EXP (-1021) 11 | 12 | #define FLT_DIG 6 13 | #define FLT_MANT_DIG 24 14 | #define FLT_MAX_10_EXP 38 15 | #define FLT_MAX_EXP 128 16 | #define FLT_MIN_10_EXP (-37) 17 | #define FLT_MIN_EXP (-125) 18 | 19 | #define FLT_RADIX 2 20 | 21 | #define DBL_MAX 1.79769313486231570815e+308 22 | #define FLT_MAX 3.40282346638528859812e+38F 23 | #define DBL_EPSILON 2.22044604925031308085e-16 24 | #define DBL_MIN 2.22507385850720138309e-308 25 | #define FLT_EPSILON 1.1920928955078125e-07F 26 | #define FLT_MIN 1.17549435082228750797e-38F 27 | -------------------------------------------------------------------------------- /lib/include/inttypes.h: -------------------------------------------------------------------------------- 1 | /* Format macro constants */ 2 | 3 | #pragma once 4 | 5 | #define PRId8 "d" 6 | #define PRId16 "d" 7 | #define PRId32 "d" 8 | #define PRId64 "lld" 9 | 10 | #define PRIdLEAST8 "d" 11 | #define PRIdLEAST16 "d" 12 | #define PRIdLEAST32 "d" 13 | #define PRIdLEAST64 "lld" 14 | 15 | #define PRIdFAST8 "d" 16 | #define PRIdFAST16 "d" 17 | #define PRIdFAST32 "d" 18 | #define PRIdFAST64 "lld" 19 | 20 | #define PRIdMAX "lld" 21 | #define PRIdPTR "ld" 22 | 23 | #define PRIi8 "i" 24 | #define PRIi16 "i" 25 | #define PRIi32 "i" 26 | #define PRIi64 "lli" 27 | 28 | #define PRIiLEAST8 "i" 29 | #define PRIiLEAST16 "i" 30 | #define PRIiLEAST32 "i" 31 | #define PRIiLEAST64 "lli" 32 | 33 | #define PRIiFAST8 "i" 34 | #define PRIiFAST16 "i" 35 | #define PRIiFAST32 "i" 36 | #define PRIiFAST64 "lli" 37 | 38 | #define PRIiMAX "lli" 39 | #define PRIiPTR "li" 40 | 41 | #define PRIo8 "o" 42 | #define PRIo16 "o" 43 | #define PRIo32 "o" 44 | #define PRIo64 "llo" 45 | 46 | #define PRIoLEAST8 "o" 47 | #define PRIoLEAST16 "o" 48 | #define PRIoLEAST32 "o" 49 | #define PRIoLEAST64 "llo" 50 | 51 | #define PRIoFAST8 "o" 52 | #define PRIoFAST16 "o" 53 | #define PRIoFAST32 "o" 54 | #define PRIoFAST64 "llo" 55 | 56 | #define PRIoMAX "llo" 57 | #define PRIoPTR "lo" 58 | 59 | #define PRIu8 "u" 60 | #define PRIu16 "u" 61 | #define PRIu32 "u" 62 | #define PRIu64 "llu" 63 | 64 | #define PRIuLEAST8 "u" 65 | #define PRIuLEAST16 "u" 66 | #define PRIuLEAST32 "u" 67 | #define PRIuLEAST64 "llu" 68 | 69 | #define PRIuFAST8 "u" 70 | #define PRIuFAST16 "u" 71 | #define PRIuFAST32 "u" 72 | #define PRIuFAST64 "llu" 73 | 74 | #define PRIuMAX "llu" 75 | #define PRIuPTR "lu" 76 | 77 | #define PRIx8 "x" 78 | #define PRIx16 "x" 79 | #define PRIx32 "x" 80 | #define PRIx64 "llx" 81 | 82 | #define PRIxLEAST8 "x" 83 | #define PRIxLEAST16 "x" 84 | #define PRIxLEAST32 "x" 85 | #define PRIxLEAST64 "llx" 86 | 87 | #define PRIxFAST8 "x" 88 | #define PRIxFAST16 "x" 89 | #define PRIxFAST32 "x" 90 | #define PRIxFAST64 "llx" 91 | 92 | #define PRIxMAX "llx" 93 | #define PRIxPTR "lx" 94 | 95 | #define PRIX8 "X" 96 | #define PRIX16 "X" 97 | #define PRIX32 "X" 98 | #define PRIX64 "llX" 99 | 100 | #define PRIXLEAST8 "X" 101 | #define PRIXLEAST16 "X" 102 | #define PRIXLEAST32 "X" 103 | #define PRIXLEAST64 "llX" 104 | 105 | #define PRIXFAST8 "X" 106 | #define PRIXFAST16 "X" 107 | #define PRIXFAST32 "X" 108 | #define PRIXFAST64 "llX" 109 | 110 | #define PRIXMAX "llX" 111 | #define PRIXPTR "lX" 112 | 113 | #define SCNd8 "hhd" 114 | #define SCNd16 "hd" 115 | #define SCNd32 "d" 116 | #define SCNd64 "lld" 117 | 118 | #define SCNdLEAST8 "hhd" 119 | #define SCNdLEAST16 "hd" 120 | #define SCNdLEAST32 "d" 121 | #define SCNdLEAST64 "lld" 122 | 123 | #define SCNdFAST8 "hhd" 124 | #define SCNdFAST16 "d" 125 | #define SCNdFAST32 "d" 126 | #define SCNdFAST64 "lld" 127 | 128 | #define SCNdMAX "lld" 129 | #define SCNdPTR "ld" 130 | 131 | #define SCNi8 "hhi" 132 | #define SCNi16 "hi" 133 | #define SCNi32 "i" 134 | #define SCNi64 "lli" 135 | 136 | #define SCNiLEAST8 "hhi" 137 | #define SCNiLEAST16 "hi" 138 | #define SCNiLEAST32 "i" 139 | #define SCNiLEAST64 "lli" 140 | 141 | #define SCNiFAST8 "hhi" 142 | #define SCNiFAST16 "i" 143 | #define SCNiFAST32 "i" 144 | #define SCNiFAST64 "lli" 145 | 146 | #define SCNiMAX "lli" 147 | #define SCNiPTR "li" 148 | 149 | #define SCNo8 "hho" 150 | #define SCNo16 "ho" 151 | #define SCNo32 "o" 152 | #define SCNo64 "llo" 153 | 154 | #define SCNoLEAST8 "hho" 155 | #define SCNoLEAST16 "ho" 156 | #define SCNoLEAST32 "o" 157 | #define SCNoLEAST64 "llo" 158 | 159 | #define SCNoFAST8 "hho" 160 | #define SCNoFAST16 "o" 161 | #define SCNoFAST32 "o" 162 | #define SCNoFAST64 "llo" 163 | 164 | #define SCNoMAX "llo" 165 | #define SCNoPTR "lo" 166 | 167 | #define SCNu8 "hhu" 168 | #define SCNu16 "hu" 169 | #define SCNu32 "u" 170 | #define SCNu64 "llu" 171 | 172 | #define SCNuLEAST8 "hhu" 173 | #define SCNuLEAST16 "hu" 174 | #define SCNuLEAST32 "u" 175 | #define SCNuLEAST64 "llu" 176 | 177 | #define SCNuFAST8 "hhu" 178 | #define SCNuFAST16 "u" 179 | #define SCNuFAST32 "u" 180 | #define SCNuFAST64 "llu" 181 | 182 | #define SCNuMAX "llu" 183 | #define SCNuPTR "lu" 184 | 185 | #define SCNx8 "hhx" 186 | #define SCNx16 "hx" 187 | #define SCNx32 "x" 188 | #define SCNx64 "llx" 189 | 190 | #define SCNxLEAST8 "hhx" 191 | #define SCNxLEAST16 "hx" 192 | #define SCNxLEAST32 "x" 193 | #define SCNxLEAST64 "llx" 194 | 195 | #define SCNxFAST8 "hhx" 196 | #define SCNxFAST16 "x" 197 | #define SCNxFAST32 "x" 198 | #define SCNxFAST64 "llx" 199 | 200 | #define SCNxMAX "llx" 201 | #define SCNxPTR "lx" 202 | -------------------------------------------------------------------------------- /lib/include/limits.h: -------------------------------------------------------------------------------- 1 | /* Implementation limits */ 2 | 3 | #pragma once 4 | 5 | #define CHAR_BIT 8 6 | #define CHAR_MAX 127 7 | #define CHAR_MIN (-128) 8 | #define MB_LEN_MAX 4 /* utf-8 */ 9 | #define INT_MAX 0x7fffffff 10 | #define INT_MIN (-1-0x7fffffff) 11 | #define LONG_MAX INT_MAX /* wasm32 */ 12 | #define LONG_MIN INT_MIN /* wasm32 */ 13 | #define LLONG_MAX 0x7fffffffffffffffLL 14 | #define LLONG_MIN (-LLONG_MAX-1LL) 15 | #define SCHAR_MAX 127 16 | #define SCHAR_MIN (-128) 17 | #define SHRT_MAX 0x7fff 18 | #define SHRT_MIN (-1-0x7fff) 19 | #define UCHAR_MAX 255 20 | #define USHRT_MAX 0xffff 21 | #define UINT_MAX 0xffffffffU 22 | #define ULONG_MAX UINT_MAX /* wasm32 */ 23 | #define ULLONG_MAX 0xffffffffffffffffULL 24 | -------------------------------------------------------------------------------- /lib/include/locale.h: -------------------------------------------------------------------------------- 1 | /* Localization */ 2 | 3 | #pragma once 4 | 5 | /* stub. can be used in dual-use code to align non-WASM 6 | * locale with WASM locale via setlocale(LC_ALL, ".utf8") */ 7 | 8 | #define LC_ALL 0 9 | #define setlocale(lc, loc) ((void)0) 10 | -------------------------------------------------------------------------------- /lib/include/math.h: -------------------------------------------------------------------------------- 1 | /* Mathematics */ 2 | 3 | #pragma once 4 | #include 5 | 6 | /* HUGE_VAL is built-in */ 7 | 8 | #define NAN (_uasf(0x7FC00000U)) 9 | #define INFINITY (_uasf(0x7F800000U)) 10 | 11 | extern double acos(double x); 12 | extern double asin(double x); 13 | extern double atan(double x); 14 | extern double atan2(double x, double y); 15 | extern double cos(double x); 16 | extern double sin(double x); 17 | extern double tan(double x); 18 | extern double cosh(double x); 19 | extern double sinh(double x); 20 | extern double tanh(double x); 21 | extern double exp(double x); 22 | extern double frexp(double value, int *exp); 23 | extern double ldexp(double x, int exp); 24 | extern double log(double x); 25 | extern double log10(double x); 26 | extern double modf(double value, double *iptr); 27 | extern double pow(double x, double y); 28 | extern double sqrt(double x); 29 | extern double ceil(double x); 30 | extern double fabs(double x); 31 | extern double floor(double x); 32 | extern double fmod(double x, double y); 33 | 34 | /* selected C99 additions for doubles */ 35 | enum { FP_NAN, FP_INFINITE, FP_ZERO, FP_SUBNORMAL, FP_NORMAL }; 36 | extern int fpclassify(double x); 37 | extern int isfinite(double x); 38 | extern int isinf(double x); 39 | extern int isnan(double x); 40 | extern int isnormal(double x); 41 | extern int signbit(double x); 42 | extern double copysign(double x, double y); 43 | extern double scalbn(double x, int n); 44 | extern double round(double x); 45 | extern double trunc(double x); 46 | extern double nearbyint(double x); 47 | extern double fmax(double x, double y); 48 | extern double fmin(double x, double y); 49 | extern double expm1(double x); 50 | 51 | 52 | -------------------------------------------------------------------------------- /lib/include/stdarg.h: -------------------------------------------------------------------------------- 1 | /* Variable arguments */ 2 | 3 | #pragma once 4 | 5 | /* va_arg is built-in */ 6 | /* va_arg_t is built-in as 7 | * union va_arg { 8 | * int va_int; 9 | * unsigned int va_uint; 10 | * long va_long; 11 | * unsigned long va_ulong; 12 | * long long va_llong; 13 | * unsigned long long va_ullong; 14 | * size_t va_size; 15 | * double va_double; 16 | * v128_t va_v128; 17 | * void *va_voidptr; 18 | * } 19 | */ 20 | 21 | typedef va_arg_t* va_list; 22 | #define va_count() (countof(...)) /* WCPL */ 23 | #define va_start(ap, v) ((ap) = ...) 24 | #define va_end(ap) ((void)(ap = NULL)) 25 | #define va_copy(dst, src) ((dst) = (src)) 26 | 27 | -------------------------------------------------------------------------------- /lib/include/stdbool.h: -------------------------------------------------------------------------------- 1 | /* Boolean type and values */ 2 | 3 | #pragma once 4 | 5 | /* bool is built-in */ 6 | /* true is built-in */ 7 | /* false is built-in */ 8 | -------------------------------------------------------------------------------- /lib/include/stddef.h: -------------------------------------------------------------------------------- 1 | /* Common definitions */ 2 | 3 | #pragma once 4 | 5 | /* NULL is built-in */ 6 | /* offsetof is built-in */ 7 | /* ptrdiff_t is built-in */ 8 | /* size_t is built-in */ 9 | /* wchar_t is built-in */ 10 | -------------------------------------------------------------------------------- /lib/include/stdint.h: -------------------------------------------------------------------------------- 1 | /* Fixed width integer types */ 2 | 3 | #pragma once 4 | #include 5 | 6 | /* intptr_t is built-in */ 7 | /* uintptr_t is built-in */ 8 | /* INTPTR_MIN is built-in */ 9 | /* INTPTR_MAX is built-in */ 10 | /* UINTPTR_MAX is built-in */ 11 | 12 | #define PTRDIFF_MIN INTPTR_MIN 13 | #define PTRDIFF_MAX INTPTR_MAX 14 | #define SIZE_MAX UINTPTR_MAX 15 | 16 | typedef int8_t int_fast8_t; 17 | typedef int64_t int_fast64_t; 18 | 19 | typedef int8_t int_least8_t; 20 | typedef int16_t int_least16_t; 21 | typedef int32_t int_least32_t; 22 | typedef int64_t int_least64_t; 23 | 24 | typedef uint8_t uint_fast8_t; 25 | typedef uint64_t uint_fast64_t; 26 | 27 | typedef uint8_t uint_least8_t; 28 | typedef uint16_t uint_least16_t; 29 | typedef uint32_t uint_least32_t; 30 | typedef uint64_t uint_least64_t; 31 | 32 | typedef int64_t intmax_t; 33 | typedef uint64_t uintmax_t; 34 | 35 | #define INT8_MIN (-1-0x7f) 36 | #define INT16_MIN (-1-0x7fff) 37 | #define INT32_MIN (-1-0x7fffffff) 38 | #define INT64_MIN (-1-0x7fffffffffffffffLL) 39 | 40 | #define INT8_MAX (0x7f) 41 | #define INT16_MAX (0x7fff) 42 | #define INT32_MAX (0x7fffffff) 43 | #define INT64_MAX (0x7fffffffffffffffLL) 44 | 45 | #define UINT8_MAX (0xff) 46 | #define UINT16_MAX (0xffff) 47 | #define UINT32_MAX (0xffffffffU) 48 | #define UINT64_MAX (0xffffffffffffffffULL) 49 | 50 | #define INTMAX_MIN INT64_MIN 51 | #define INTMAX_MAX INT64_MAX 52 | 53 | #define UINTMAX_MAX UINT64_MAX 54 | 55 | #define INT_FAST8_MIN INT8_MIN 56 | #define INT_FAST64_MIN INT64_MIN 57 | 58 | #define INT_LEAST8_MIN INT8_MIN 59 | #define INT_LEAST16_MIN INT16_MIN 60 | #define INT_LEAST32_MIN INT32_MIN 61 | #define INT_LEAST64_MIN INT64_MIN 62 | 63 | #define INT_FAST8_MAX INT8_MAX 64 | #define INT_FAST64_MAX INT64_MAX 65 | 66 | #define INT_LEAST8_MAX INT8_MAX 67 | #define INT_LEAST16_MAX INT16_MAX 68 | #define INT_LEAST32_MAX INT32_MAX 69 | #define INT_LEAST64_MAX INT64_MAX 70 | 71 | #define UINT_FAST8_MAX UINT8_MAX 72 | #define UINT_FAST64_MAX UINT64_MAX 73 | 74 | #define UINT_LEAST8_MAX UINT8_MAX 75 | #define UINT_LEAST16_MAX UINT16_MAX 76 | #define UINT_LEAST32_MAX UINT32_MAX 77 | #define UINT_LEAST64_MAX UINT64_MAX 78 | 79 | #define INT8_C(c) (c) 80 | #define UINT8_C(c) (c) 81 | #define INT16_C(c) (c) 82 | #define UINT16_C(c) (c) 83 | #define INT32_C(c) ((int)(c)) 84 | #define UINT32_C(c) ((unsigned)(c)) 85 | #define INT64_C(c) ((long long)(c)) 86 | #define UINT64_C(c) ((unsigned long long)(c)) 87 | 88 | -------------------------------------------------------------------------------- /lib/include/stdio.h: -------------------------------------------------------------------------------- 1 | /* Input/output */ 2 | 3 | #pragma once 4 | #include 5 | #include 6 | 7 | #define EOF (-1) 8 | #define BUFSIZ 1024 9 | #define SBFSIZ 8 10 | #define FOPEN_MAX 20 /* max #files open at once */ 11 | #define FILENAME_MAX 260 /* longest file name */ 12 | 13 | typedef long long fpos_t; 14 | 15 | typedef struct _iobuf { 16 | ssize_t cnt; /* characters left */ 17 | unsigned char *base; /* location of buffer */ 18 | unsigned char *ptr; /* next character position */ 19 | unsigned char *end; /* end of buffer */ 20 | int flags; /* set of _IOXXX flags */ 21 | int fd; /* file descriptor (= WASI fd_t) */ 22 | char sbuf[SBFSIZ]; /* 1-mbchar buffer */ 23 | } FILE; 24 | 25 | #define _IOFBF 0000 26 | #define _IOREAD 0001 27 | #define _IOWRT 0002 28 | #define _IONBF 0004 29 | #define _IOEOF 0020 30 | #define _IOERR 0040 31 | #define _IOLBF 0100 32 | #define _IORW 0200 33 | #define _IOMYBUF 0400 34 | 35 | extern FILE _iob[FOPEN_MAX]; 36 | #define stdin (&_iob[0]) 37 | #define stdout (&_iob[1]) 38 | #define stderr (&_iob[2]) 39 | 40 | extern FILE *fopen(const char *filename, const char *mode); 41 | extern FILE *fdopen(int fd, const char *mode); 42 | extern FILE *freopen(const char *filename, const char *mode, FILE *stream); 43 | extern int fclose(FILE *stream); 44 | #define getc(p) (--(p)->cnt >= 0 ? (unsigned char)*(p)->ptr++ : _fillbuf(p)) 45 | #define putc(x, p) (--(p)->cnt >= 0 ? *(p)->ptr++ = (x) : _flushbuf((x), p)) 46 | #define getchar() (getc(stdin)) 47 | #define putchar(x) (putc((x), stdout)) 48 | #define clearerr(p) ((void)((p)->flags &= ~(_IOERR|_IOEOF))) 49 | #define feof(p) ((p)->flags & _IOEOF) 50 | #define ferror(p) ((p)->flags & _IOERR) 51 | #define fileno(p) ((p)->fd) 52 | extern int _fillbuf(FILE *stream); 53 | extern int _flushbuf(int c, FILE *stream); 54 | extern int fgetc(FILE *stream); 55 | extern char *fgets(char *s, int n, FILE *stream); 56 | extern int fputc(int c, FILE *stream); 57 | extern int fputs(const char *s, FILE *stream); 58 | extern int puts(const char *s); 59 | extern int ungetc(int c, FILE *stream); 60 | /* char *gets(char *s) -- obsolete */ 61 | extern size_t fread(void *ptr, size_t size, size_t nobj, FILE *stream); 62 | extern size_t fwrite(const void *ptr, size_t size, size_t nobj, FILE *stream); 63 | extern int fseek(FILE *stream, long offset, int origin); 64 | extern long ftell(FILE *stream); 65 | extern void rewind(FILE *stream); 66 | extern int fgetpos(FILE *stream, fpos_t *ptr); 67 | extern int fsetpos(FILE *stream, const fpos_t *ptr); 68 | extern int fflush(FILE *stream); 69 | extern int setvbuf(FILE *stream, char *buf, int mode, size_t size); 70 | extern void setbuf(FILE *stream, char *buf); 71 | extern int fprintf(FILE *stream, const char *format, ...); 72 | extern int printf(const char *format, ...); 73 | extern int sprintf(char *s, const char *format, ...); 74 | extern int snprintf(char *s, size_t count, const char *format, ...); 75 | extern int vprintf(const char *format, va_list arg); 76 | extern int vfprintf(FILE *stream, const char *format, va_list arg); 77 | extern int vsprintf(char *s, const char *format, va_list arg); 78 | extern int vsnprintf(char *s, size_t count, const char* format, va_list arg); 79 | extern int fscanf(FILE *stream, const char *format, ...); 80 | extern int scanf(const char *format, ...); 81 | extern int sscanf(const char *s, const char *format, ...); 82 | extern int snscanf(const char *s, size_t count, const char *format, ...); 83 | extern int vfscanf(FILE *stream, const char *format, va_list arg); 84 | extern int vscanf(const char *format, va_list arg); 85 | extern int vsscanf(const char *s, const char *format, va_list arg); 86 | extern int vsnscanf(const char *s, size_t count, const char *format, va_list arg); 87 | /* extern FILE *tmpfile(void); -- not in WASI? */ 88 | /* extern char *tmpnam(char s[L_tmpnam]); -- not in WASI? */ 89 | extern int int remove(const char *filename); 90 | extern int rename(const char *oldname, const char *newname); 91 | extern void perror(const char *s); 92 | 93 | /* *at variants */ 94 | extern int renameat(int oldfd, const char *oldpath, int newfd, const char *newpath); 95 | -------------------------------------------------------------------------------- /lib/include/stdlib.h: -------------------------------------------------------------------------------- 1 | /* General utilities */ 2 | 3 | #define EXIT_FAILURE 1 4 | #define EXIT_SUCCESS 0 5 | #define MB_CUR_MAX 4 /* utf-8 */ 6 | /* NULL is built-in */ 7 | #define RAND_MAX 0x7fffffff 8 | typedef struct { int quot, rem; } div_t; 9 | typedef struct { long quot, rem; } ldiv_t; 10 | typedef struct { long long quot, rem; } lldiv_t; 11 | /* size_t is built-in */ 12 | /* wchar_t is built-in */ 13 | 14 | extern double atof(const char *s); 15 | extern int atoi(const char *s); 16 | extern long atol(const char *s); 17 | extern long long atoll(const char *s); 18 | extern double strtod(const char *s, char **end); 19 | extern long strtol(const char *s, char **end, int base); 20 | extern long long strtoll(const char *s, char **end, int base); 21 | extern unsigned long strtoul(const char *s, char **end, int base); 22 | extern unsigned long long strtoull(const char *, char **, int base); 23 | extern int rand(void); 24 | extern void srand(unsigned seed); 25 | extern void *calloc(size_t n, size_t sz); 26 | extern void free(void *ptr); 27 | extern void *malloc(size_t sz); 28 | extern void *realloc(void *ptr, size_t sz); 29 | extern void abort(void); 30 | extern int atexit(void (*func)(void)); 31 | extern void exit(int status); 32 | extern char *getenv(const char *name); 33 | /* int system(const char *cmd) -- not in WASI */ 34 | extern void *bsearch(const void *key, const void *base, 35 | size_t n, size_t sz, int (*) (const void *, const void *)); 36 | extern void qsort(const void *base, 37 | size_t n, size_t sz, int (*) (const void *, const void *)); 38 | extern int abs(int n); 39 | extern long labs(long n); 40 | extern long long llabs(long long n); 41 | extern div_t div(int n, int d); 42 | extern ldiv_t ldiv(long n, long d); 43 | extern lldiv_t lldiv(long long n, long long d); 44 | extern int mblen(const char *s, size_t n); 45 | extern int mbtowc(wchar_t *pwc, const char *s, size_t n); 46 | extern int wctomb(char *s, wchar_t wc); 47 | extern size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n); 48 | extern size_t wcstombs(char *s, const wchar_t *pwcs, size_t n); 49 | 50 | /* internal library bailout w/message */ 51 | extern void _panicf(const char *fmt, ...); 52 | -------------------------------------------------------------------------------- /lib/include/string.h: -------------------------------------------------------------------------------- 1 | /* String handling */ 2 | 3 | #pragma once 4 | 5 | /* NULL is buil-in */ 6 | /* size_t is built-in */ 7 | 8 | extern void *memcpy(void *dst, const void *src, size_t n); 9 | extern void *memmove(void *, const void *, size_t); 10 | extern char *strcpy(char *dst, const char *src); 11 | extern char *strncpy(char *, const char *, size_t); 12 | extern char *strcat(char *, const char *); 13 | extern char *strncat(char *, const char *, size_t); 14 | extern int memcmp(const void *s1, const void *s2, size_t n); 15 | extern int strcmp(const char *, const char *); 16 | extern int strcoll(const char *s1, const char *s2); 17 | extern int strncmp(const char *s1, const char *s2, size_t n); 18 | extern size_t strxfrm(char *dst, const char *src, size_t n); 19 | extern void *memchr(const void *s, int c, size_t n); 20 | extern char *strchr(const char *s, int c); 21 | extern size_t strcspn(const char *s, const char *cs); 22 | extern char *strpbrk(const char *, const char *); 23 | extern char *strrchr(const char *s, int c); 24 | extern size_t strspn(const char *s, const char *cs); 25 | extern char *strstr(const char *s, const char *ss); 26 | extern char *strtok(char *, const char *); 27 | extern char *strtok_r(char *, const char *, char **); 28 | extern void *memset(void *dst, int c, size_t n); 29 | extern size_t strlen(const char *s); 30 | extern const char *strerror(int errno); 31 | /* nonstandard but common */ 32 | extern char *strdup(const char *s); // POSIX, C23 33 | extern char *strndup(const char *s, size_t n); // POSIX, C23 34 | extern void *memccpy(void *d, const void *s, int c, size_t n); // POSIX, C23 35 | extern void *memmem(const void *, size_t, const void *, size_t); 36 | extern size_t strnlen(const char *s, size_t maxlen); 37 | extern void memswap(void *s1, void *s2, size_t n); 38 | extern void bzero(void *s, size_t n); 39 | extern int strcasecmp(const char *s1, const char *s2); // POSIX 40 | extern int strncasecmp(const char *s1, const char *s2, size_t n); // POSIX 41 | 42 | 43 | -------------------------------------------------------------------------------- /lib/include/sys.cdefs.h: -------------------------------------------------------------------------------- 1 | /* common system definitions */ 2 | 3 | #pragma once 4 | 5 | #define SEEK_SET (0) /* = WASI WHENCE_SET */ 6 | #define SEEK_CUR (1) /* = WASI WHENCE_CUR */ 7 | #define SEEK_END (2) /* = WASI WHENCE_END */ 8 | 9 | -------------------------------------------------------------------------------- /lib/include/sys.crt.h: -------------------------------------------------------------------------------- 1 | /* runtime definitions */ 2 | 3 | #pragma once 4 | #pragma module "crt" 5 | 6 | #define ONTERM_MAX 32 7 | typedef void (*onterm_func_t)(void); 8 | extern onterm_func_t onterm_funcs[ONTERM_MAX]; 9 | extern size_t onterm_count; 10 | extern void terminate(int status); 11 | 12 | -------------------------------------------------------------------------------- /lib/include/sys.intrs.h: -------------------------------------------------------------------------------- 1 | /* webassembly intrinsics */ 2 | 3 | #pragma once 4 | 5 | /* extra integer operations */ 6 | #define __builtin_clz(x) ((int(*)(unsigned))asm(i32.clz)(x)) 7 | #define __builtin_ctz(x) ((int(*)(unsigned))asm(i32.ctz)(x)) 8 | #define __builtin_popcount(x) ((int(*)(unsigned))asm(i32.popcnt)(x)) 9 | #define __builtin_rotleft(x, n) ((int(*)(unsigned, unsigned))asm(i32.rotl)(x, n)) 10 | #define __builtin_rotright(x, n) ((int(*)(unsigned, unsigned))asm(i32.rotr)(x, n)) 11 | #define __builtin_clzll(x) ((long long(*)(unsigned long long))asm(i64.clz)(x)) 12 | #define __builtin_ctzll(x) ((long long(*)(unsigned long long))asm(i64.ctz)(x)) 13 | #define __builtin_popcountll(x) ((long long(*)(unsigned long long))asm(i64.popcnt)(x)) 14 | #define __builtin_rotleftll(x, n) ((long long(*)(unsigned long long, unsigned long long))asm(i64.rotl)(x, n)) 15 | #define __builtin_rotrightll(x, n) ((long long(*)(unsigned long long, unsigned long long))asm(i64.rotr)(x, n)) 16 | 17 | /* reinterpret casts */ 18 | #define __builtin_asuint32(x) ((unsigned(*)(float))asm(i32.reinterpret_f32)(x)) 19 | #define __builtin_asfloat(x) ((float(*)(unsigned))asm(f32.reinterpret_i32)(x)) 20 | #define __builtin_asuint64(x) ((unsigned long long(*)(double))asm(i64.reinterpret_f64)(x)) 21 | #define __builtin_asdouble(x) ((double(*)(unsigned long long))asm(f64.reinterpret_i64)(x)) 22 | 23 | /* memory operations (return no value) */ 24 | #define __builtin_memset(d, v, n) ((void(*)(void*, unsigned, size_t))asm(memory.fill)(d, v, n)) 25 | #define __builtin_memcpy(d, s, n) ((void(*)(void*, void*, size_t))asm(memory.copy)(d, s, n)) /* checks overlap */ 26 | #define __builtin_memmove(d, s, n) ((void(*)(void*, void*, size_t))asm(memory.copy)(d, s, n)) /* ditto */ 27 | 28 | /* short aliases, generic versions */ 29 | #define _clz(x) (generic((x), \ 30 | unsigned long long: __builtin_clzll(x), \ 31 | long long: __builtin_clzll(x), \ 32 | default: __builtin_clz(x))) 33 | #define _ctz(x) (generic((x), \ 34 | unsigned long long: __builtin_ctzll(x), \ 35 | long long: __builtin_ctzll(x), \ 36 | default: __builtin_ctz(x))) 37 | #define _popcnt(x) (generic((x), \ 38 | unsigned long long: __builtin_popcountll(x), \ 39 | long long: __builtin_popcountll(x), \ 40 | default: __builtin_popcount(x))) 41 | #define _rotl(x, n) (generic((x), \ 42 | unsigned long long: __builtin_rotleftll(x, n), \ 43 | long long: __builtin_rotleftll(x, n), \ 44 | default: __builtin_rotleft(x, n))) 45 | #define _rotr(x, n) (generic((x), \ 46 | unsigned long long: __builtin_rotrightll(x, n), \ 47 | long long: __builtin_rotrightll(x, n), \ 48 | default: __builtin_rotright(x, n))) 49 | #define _fasu(x) (generic((x), \ 50 | float: __builtin_asuint32(x))) 51 | #define _dasull(x) (generic((x), \ 52 | double: __builtin_asuint64(x))) 53 | #define _uasf(x) (generic((x), \ 54 | unsigned: __builtin_asfloat(x))) 55 | #define _ullasd(x) (generic((x), \ 56 | unsigned long long: __builtin_asdouble(x))) 57 | #define _bfill(d, v, n) (__builtin_memset(d, v, n)) 58 | #define _bzero(d, n) (__builtin_memset(d, 0, n)) 59 | #define _bcopy(d, s, n) (__builtin_memmove(d, s, n)) 60 | -------------------------------------------------------------------------------- /lib/include/sys.stat.h: -------------------------------------------------------------------------------- 1 | /* data returned by the stat function (abridged) */ 2 | 3 | #pragma module "stat" 4 | #pragma once 5 | #include 6 | 7 | #define S_IFBLK (0x6000) 8 | #define S_IFCHR (0x2000) 9 | #define S_IFDIR (0x4000) 10 | #define S_IFLNK (0xa000) 11 | #define S_IFREG (0x8000) 12 | #define S_IFSOCK (0xc000) 13 | #define S_IFIFO (0xc000) 14 | #define S_IFMT (S_IFBLK|S_IFCHR|S_IFDIR|S_IFIFO|S_IFLNK|S_IFREG|S_IFSOCK) 15 | 16 | #define S_IXOTH (0x1) 17 | #define S_IWOTH (0x2) 18 | #define S_IROTH (0x4) 19 | #define S_IRWXO (S_IXOTH|S_IWOTH|S_IROTH) 20 | #define S_IXGRP (0x8) 21 | #define S_IWGRP (0x10) 22 | #define S_IRGRP (0x20) 23 | #define S_IRWXG (S_IXGRP|S_IWGRP|S_IRGRP) 24 | #define S_IXUSR (0x40) 25 | #define S_IWUSR (0x80) 26 | #define S_IRUSR (0x100) 27 | #define S_IRWXU (S_IXUSR|S_IWUSR|S_IRUSR) 28 | #define S_ISVTX (0x200) 29 | #define S_ISGID (0x400) 30 | #define S_ISUID (0x800) 31 | 32 | #define S_ISBLK(m) (((m)&S_IFMT) == S_IFBLK) 33 | #define S_ISCHR(m) (((m)&S_IFMT) == S_IFCHR) 34 | #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) 35 | #define S_ISFIFO(m) (((m)&S_IFMT) == S_IFIFO) 36 | #define S_ISLNK(m) (((m)&S_IFMT) == S_IFLNK) 37 | #define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) 38 | #define S_ISSOCK(m) (((m)&S_IFMT) == S_IFSOCK) 39 | 40 | struct stat { 41 | dev_t st_dev; 42 | ino_t st_ino; 43 | nlink_t st_nlink; 44 | mode_t st_mode; 45 | /* uid_t st_uid; */ 46 | /* gid_t st_gid; */ 47 | /* dev_t st_rdev; */ 48 | off_t st_size; 49 | /* blksize_t st_blksize; */ 50 | /* blkcnt_t st_blocks; */ 51 | time_t st_atime; 52 | time_t st_mtime; 53 | time_t st_ctime; 54 | long st_atime_ns; 55 | long st_mtime_ns; 56 | long st_ctime_ns; 57 | }; 58 | 59 | extern int fstat(int fd, struct stat *ps); 60 | extern int fstatat(int fd, const char *path, struct stat *ps, int atflag); 61 | extern int stat(const char *path, struct stat *ps); 62 | extern int lstat(const char *path, struct stat *ps); 63 | extern int mkdirat(int fd, const char *path, mode_t /* ignored, use S_IRWXU */); 64 | extern int mkdir(const char *path, mode_t /* ignored, use S_IRWXU */); 65 | 66 | /* NYI: 67 | int chmod(const char *, mode_t); 68 | int fchmod(int, mode_t); 69 | int mkfifo(const char *, mode_t); 70 | int mknod(const char *, mode_t, dev_t); 71 | mode_t umask(mode_t); 72 | */ 73 | -------------------------------------------------------------------------------- /lib/include/sys.types.h: -------------------------------------------------------------------------------- 1 | /* common system types */ 2 | 3 | #pragma once 4 | 5 | /* size_t is built-in */ 6 | /* ssize_t is built-in */ 7 | /* intptr_t is built-in */ 8 | typedef int64_t off_t; 9 | typedef uint64_t nlink_t; 10 | typedef uint64_t dev_t; 11 | typedef uint64_t ino_t; 12 | typedef uint32_t mode_t; 13 | typedef uint32_t uid_t; 14 | typedef uint32_t gid_t; 15 | typedef int32_t blksize_t; 16 | typedef int64_t blkcnt_t; 17 | typedef int64_t time_t; 18 | typedef int64_t clock_t; 19 | -------------------------------------------------------------------------------- /lib/include/time.h: -------------------------------------------------------------------------------- 1 | /* Date and time */ 2 | 3 | #pragma once 4 | /* NULL is built-in */ 5 | /* size_t is built-in */ 6 | #include 7 | #include 8 | 9 | #define CLOCKS_PER_SEC (1000000000LL) 10 | 11 | struct tm { 12 | int tm_sec; /* seconds */ 13 | int tm_min; /* minutes */ 14 | int tm_hour; /* hours */ 15 | int tm_mday; /* day of the month */ 16 | int tm_mon; /* month */ 17 | int tm_year; /* year */ 18 | int tm_wday; /* day of the week */ 19 | int tm_yday; /* day in the year */ 20 | int tm_isdst; /* daylight saving time */ 21 | }; 22 | 23 | extern clock_t clock(void); 24 | extern double difftime(time_t t1, time_t t0); 25 | extern time_t mktime(struct tm *ptm); 26 | extern time_t time(time_t *pt); 27 | extern char *asctime(const struct tm *ptm); 28 | extern char *ctime(const time_t *pt); 29 | extern struct tm *gmtime(const time_t *pt); 30 | extern struct tm *localtime(const time_t *pt); 31 | extern size_t strftime(char *s, size_t maxsz, const char *fmt, const struct tm *ptm); 32 | /* selected POSIX-like additions */ 33 | extern void tzset(void); /* relies on TZ env variable */ 34 | extern char *tzname[2]; 35 | extern long timezone; 36 | extern struct tm *gmtime_r(const time_t *pt, struct tm *ptm); 37 | extern char *ctime_r(const time_t *pt, char *buf); 38 | extern char *asctime_r(const struct tm *ptm, char *buf); 39 | extern struct tm *localtime_r(const time_t *pt, struct tm *ptm); 40 | -------------------------------------------------------------------------------- /lib/include/unistd.h: -------------------------------------------------------------------------------- 1 | /* standard symbolic constants and types (abridged) */ 2 | 3 | #pragma once 4 | #include 5 | #include 6 | 7 | #define STDIN_FILENO (0) 8 | #define STDOUT_FILENO (1) 9 | #define STDERR_FILENO (2) 10 | #define F_OK (0) 11 | #define X_OK (1) 12 | #define W_OK (2) 13 | #define R_OK (4) 14 | 15 | extern ssize_t read(int fd, void *buf, size_t n); 16 | extern ssize_t write(int fd, const void *buf, size_t n); 17 | extern ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset); 18 | extern ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset); 19 | extern off_t lseek(int fd, off_t offset, int whence); 20 | extern int ftruncate(int fd, off_t length); 21 | extern int close(int fd); 22 | extern int isatty(int fd); 23 | extern void *sbrk(intptr_t inc); /* inc should be a multiple of 64K */ 24 | extern int access(const char *path, int amode); 25 | extern int unlinkat(int fd, const char *path, int atflag); 26 | extern int unlink(const char *path); 27 | extern int linkat(int oldfd, const char *oldpath, int newfd, const char *newpath, int atflag); 28 | extern int link(const char *oldpath, const char *newpath); 29 | extern int symlinkat(const char *tgtpath, int fd, const char *lnkpath); 30 | extern int symlink(const char *tgtpath, const char *lnkpath); 31 | extern ssize_t readlinkat(int fd, const char *path, char *buf, size_t bufsize); 32 | extern ssize_t readlink(const char *path, char *buf, size_t bufsize); 33 | extern int rmdirat(int fd, const char *path); 34 | extern int int rmdir(const char *path); 35 | extern int fdatasync(int fd); 36 | extern int fsync(int fd); 37 | extern void _exit(int status); 38 | -------------------------------------------------------------------------------- /lib/include/wchar.h: -------------------------------------------------------------------------------- 1 | /* Wide character Input and Output */ 2 | 3 | #pragma once 4 | 5 | /* wchar_t is built-in */ 6 | /* wint_t is built-in */ 7 | 8 | #define WCHAR_MIN (-1-0x7fffffff) /* utf-32 */ 9 | #define WCHAR_MAX (0x7fffffff) /* utf-32 */ 10 | #define WINT_MIN WCHAR_MIN 11 | #define WINT_MAX WCHAR_MAX 12 | 13 | /* no wide-character i/o */ 14 | -------------------------------------------------------------------------------- /lib/src/README.md: -------------------------------------------------------------------------------- 1 | WCPL Library sources 2 | ==================== 3 | 4 | This implementation is mostly derived from `wasi-libc`*, which allows to be licensed 5 | under MIT license (among other options) and algorithms in printed books. 6 | Math routines are ported from Sun's `fdlibm` library. 7 | All attributions in source code are preserved wherever possible. 8 | 9 | \* available at https://github.com/WebAssembly/wasi-libc 10 | -------------------------------------------------------------------------------- /lib/src/crt.args.c: -------------------------------------------------------------------------------- 1 | #pragma module "crt" 2 | #include 3 | #include 4 | 5 | void *stack_base; 6 | void *heap_base; 7 | 8 | char **_argv = NULL; 9 | int _argc = 0; 10 | static char *empty_argv[] = { "", NULL }; 11 | 12 | void initialize(void) 13 | { 14 | if (_argv != NULL) { 15 | return; 16 | } else { 17 | size_t args_count, args_buf_size; 18 | errno_t error = args_sizes_get(&args_count, &args_buf_size); 19 | if (error != ERRNO_SUCCESS) goto err; 20 | if (args_count == 0) goto err; 21 | size_t num_ptrs = args_count + 1; 22 | if (num_ptrs == 0) goto err; 23 | char *args_buf = stack_base; 24 | char *ptrs_buf = args_buf + args_buf_size; 25 | size_t n = (size_t)ptrs_buf % 16; 26 | if (n > 0) ptrs_buf += 16-n; 27 | char *ptrs_end = ptrs_buf + num_ptrs*sizeof(char*); 28 | if (ptrs_end > heap_base) goto err; 29 | char **args_ptrs = (char **)ptrs_buf; 30 | error = args_get((uint8_t **)args_ptrs, (uint8_t *)args_buf); 31 | if (error != ERRNO_SUCCESS) goto err; 32 | args_ptrs[args_count] = NULL; 33 | _argv = args_ptrs; 34 | _argc = (int)args_count; 35 | return; 36 | err:; 37 | } 38 | ciovec_t iov; size_t ret; 39 | iov.buf = (uint8_t*)"internal error: unable to retrieve command-line arguments\n"; 40 | iov.buf_len = 58; 41 | fd_write(2, &iov, 1, &ret); 42 | _argv = &empty_argv[0]; 43 | _argc = 1; 44 | } 45 | 46 | onterm_func_t onterm_funcs[ONTERM_MAX]; 47 | size_t onterm_count = 0; 48 | 49 | void terminate(int status) 50 | { 51 | while (onterm_count > 0) { 52 | onterm_func_t fn = onterm_funcs[--onterm_count]; 53 | (*fn)(); 54 | } 55 | proc_exit((exitcode_t)status); 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/crt.argv.c: -------------------------------------------------------------------------------- 1 | #pragma module "crt" 2 | #include 3 | #include 4 | #include 5 | 6 | char **_argv = NULL; 7 | int _argc = 0; 8 | static char *empty_argv[] = { "", NULL }; 9 | 10 | void initialize(void) 11 | { 12 | if (_argv != NULL) { 13 | return; 14 | } else { 15 | size_t args_count, args_buf_size; 16 | errno_t error = args_sizes_get(&args_count, &args_buf_size); 17 | if (error != ERRNO_SUCCESS) goto err; 18 | if (args_count == 0) goto err; 19 | size_t num_ptrs = args_count + 1; 20 | if (num_ptrs == 0) goto err; 21 | char *args_buf = malloc(args_buf_size); 22 | if (args_buf == NULL) goto err; 23 | char **args_ptrs = calloc(num_ptrs, sizeof(char *)); 24 | if (args_ptrs == NULL) { free(args_buf); goto err; } 25 | error = args_get((uint8_t **)args_ptrs, (uint8_t *)args_buf); 26 | if (error != ERRNO_SUCCESS) goto err; 27 | args_ptrs[args_count] = NULL; 28 | _argv = args_ptrs; 29 | _argc = (int)args_count; 30 | return; 31 | err:; 32 | } 33 | ciovec_t iov; size_t ret; 34 | iov.buf = (uint8_t*)"internal error: unable to retrieve command-line arguments\n"; 35 | iov.buf_len = 58; 36 | fd_write(2, &iov, 1, &ret); 37 | _argv = &empty_argv[0]; 38 | _argc = 1; 39 | } 40 | 41 | onterm_func_t onterm_funcs[ONTERM_MAX]; 42 | size_t onterm_count = 0; 43 | 44 | void terminate(int status) 45 | { 46 | while (onterm_count > 0) { 47 | onterm_func_t fn = onterm_funcs[--onterm_count]; 48 | (*fn)(); 49 | } 50 | proc_exit((exitcode_t)status); 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/crt.c: -------------------------------------------------------------------------------- 1 | #pragma module "crt" 2 | -------------------------------------------------------------------------------- /lib/src/crt.void.c: -------------------------------------------------------------------------------- 1 | #pragma module "crt" 2 | #include 3 | #include 4 | 5 | char *_argv[] = { "", NULL }; 6 | int _argc = 1; 7 | 8 | void initialize(void) 9 | { 10 | } 11 | 12 | onterm_func_t onterm_funcs[ONTERM_MAX]; 13 | size_t onterm_count = 0; 14 | 15 | void terminate(int status) 16 | { 17 | while (onterm_count > 0) { 18 | onterm_func_t fn = onterm_funcs[--onterm_count]; 19 | (*fn)(); 20 | } 21 | proc_exit((exitcode_t)status); 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/ctype.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int isalnum(int c) 4 | { 5 | return isalpha(c) || isdigit(c); 6 | } 7 | 8 | int isalpha(int c) 9 | { 10 | return islower(c) || isupper(c); 11 | } 12 | 13 | int iscntrl(int c) 14 | { 15 | return !isprint(c); 16 | } 17 | 18 | int isdigit(int c) 19 | { 20 | return ((unsigned)c - '0') <= 9; 21 | } 22 | 23 | int isgraph(int c) 24 | { 25 | return isprint(c) && !isspace(c); 26 | } 27 | 28 | int islower(int c) 29 | { 30 | return c >= 'a' && c <= 'z'; 31 | } 32 | 33 | int isprint(int c) 34 | { 35 | return c >= ' ' && c <= '~'; 36 | } 37 | 38 | int ispunct(int c) 39 | { 40 | return isprint(c) && !isalnum(c) && !isspace(c); 41 | } 42 | 43 | int isspace(int c) 44 | { 45 | return c == ' ' || c == '\n' || c == '\t' || c == '\r'; 46 | } 47 | 48 | int isupper(int c) 49 | { 50 | return c >= 'A' && c <= 'Z'; 51 | } 52 | 53 | int isxdigit(int c) 54 | { 55 | return isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); 56 | } 57 | 58 | int isascii(int c) 59 | { 60 | return !(c & ~0x7f); 61 | } 62 | 63 | int isblank(int c) 64 | { 65 | return (c == '\t') || (c == ' '); 66 | } 67 | 68 | int toupper(int c) 69 | { 70 | return islower(c) ? (c & ~32) : c; 71 | } 72 | 73 | int tolower(int c) 74 | { 75 | return isupper(c) ? (c | 32) : c; 76 | } 77 | 78 | 79 | -------------------------------------------------------------------------------- /lib/src/errno.c: -------------------------------------------------------------------------------- 1 | /* Errors */ 2 | 3 | #include 4 | #include 5 | 6 | int errno; 7 | 8 | /* used by perror */ 9 | const char *_emsg[ERRNO_MAXERR+1] = { 10 | "No error occurred", 11 | "Argument list too long", 12 | "Permission denied", 13 | "Address in use", 14 | "Address not available", 15 | "Address family not supported", 16 | "Resource unavailable, or operation would block", 17 | "Connection already in progress", 18 | "Bad file descriptor", 19 | "Bad message", 20 | "Device or resource busy", 21 | "Operation canceled", 22 | "No child processes", 23 | "Connection aborted", 24 | "Connection refused", 25 | "Connection reset", 26 | "Resource deadlock would occur", 27 | "Destination address required", 28 | "Mathematics argument out of domain of function", 29 | "Reserved", 30 | "File exists", 31 | "Bad address", 32 | "File too large", 33 | "Host is unreachable", 34 | "Identifier removed", 35 | "Illegal byte sequence", 36 | "Operation in progress", 37 | "Interrupted function", 38 | "Invalid argument", 39 | "I/O error", 40 | "Socket is connected", 41 | "Is a directory", 42 | "Too many levels of symbolic links", 43 | "File descriptor value too large", 44 | "Too many links", 45 | "Message too large", 46 | "Reserved", 47 | "Filename too long", 48 | "Network is down", 49 | "Connection aborted by network", 50 | "Network unreachable", 51 | "Too many files open in system", 52 | "No buffer space available", 53 | "No such device", 54 | "No such file or directory", 55 | "Executable file format error", 56 | "No locks available", 57 | "Reserved", 58 | "Not enough space", 59 | "No message of the desired type", 60 | "Protocol not available", 61 | "No space left on device", 62 | "Function not supported", 63 | "The socket is not connected", 64 | "Not a directory or a symbolic link to a directory", 65 | "Directory not empty", 66 | "State not recoverable", 67 | "Not a socket", 68 | "Not supported, or operation not supported on socket", 69 | "Inappropriate I/O control operation", 70 | "No such device or address", 71 | "Value too large to be stored in data type", 72 | "Previous owner died", 73 | "Operation not permitted", 74 | "Broken pipe", 75 | "Protocol error", 76 | "Protocol not supported", 77 | "Protocol wrong type for socket", 78 | "Result too large", 79 | "Read-only file system", 80 | "Invalid seek", 81 | "No such process", 82 | "Reserved", 83 | "Connection timed out", 84 | "Text file busy", 85 | "Cross-device link", 86 | "Extension: Capabilities insufficient" 87 | }; 88 | 89 | static_assert(ESUCCESS == ERRNO_SUCCESS); 90 | static_assert(E2BIG == ERRNO_2BIG); 91 | static_assert(EACCES == ERRNO_ACCES); 92 | static_assert(EADDRINUSE == ERRNO_ADDRINUSE); 93 | static_assert(EADDRNOTAVAIL == ERRNO_ADDRNOTAVAIL); 94 | static_assert(EAFNOSUPPORT == ERRNO_AFNOSUPPORT); 95 | static_assert(EAGAIN == ERRNO_AGAIN); 96 | static_assert(EALREADY == ERRNO_ALREADY); 97 | static_assert(EBADF == ERRNO_BADF); 98 | static_assert(EBADMSG == ERRNO_BADMSG); 99 | static_assert(EBUSY == ERRNO_BUSY); 100 | static_assert(ECANCELED == ERRNO_CANCELED); 101 | static_assert(ECHILD == ERRNO_CHILD); 102 | static_assert(ECONNABORTED == ERRNO_CONNABORTED); 103 | static_assert(ECONNREFUSED == ERRNO_CONNREFUSED); 104 | static_assert(ECONNRESET == ERRNO_CONNRESET); 105 | static_assert(EDEADLK == ERRNO_DEADLK); 106 | static_assert(EDESTADDRREQ == ERRNO_DESTADDRREQ); 107 | static_assert(EDOM == ERRNO_DOM); 108 | static_assert(EDQUOT == ERRNO_DQUOT); 109 | static_assert(EEXIST == ERRNO_EXIST); 110 | static_assert(EFAULT == ERRNO_FAULT); 111 | static_assert(EFBIG == ERRNO_FBIG); 112 | static_assert(EHOSTUNREACH == ERRNO_HOSTUNREACH); 113 | static_assert(EIDRM == ERRNO_IDRM); 114 | static_assert(EILSEQ == ERRNO_ILSEQ); 115 | static_assert(EINPROGRESS == ERRNO_INPROGRESS); 116 | static_assert(EINTR == ERRNO_INTR); 117 | static_assert(EINVAL == ERRNO_INVAL); 118 | static_assert(EIO == ERRNO_IO); 119 | static_assert(EISCONN == ERRNO_ISCONN); 120 | static_assert(EISDIR == ERRNO_ISDIR); 121 | static_assert(ELOOP == ERRNO_LOOP); 122 | static_assert(EMFILE == ERRNO_MFILE); 123 | static_assert(EMLINK == ERRNO_MLINK); 124 | static_assert(EMSGSIZE == ERRNO_MSGSIZE); 125 | static_assert(EMULTIHOP == ERRNO_MULTIHOP); 126 | static_assert(ENAMETOOLONG == ERRNO_NAMETOOLONG); 127 | static_assert(ENETDOWN == ERRNO_NETDOWN); 128 | static_assert(ENETRESET == ERRNO_NETRESET); 129 | static_assert(ENETUNREACH == ERRNO_NETUNREACH); 130 | static_assert(ENFILE == ERRNO_NFILE); 131 | static_assert(ENOBUFS == ERRNO_NOBUFS); 132 | static_assert(ENODEV == ERRNO_NODEV); 133 | static_assert(ENOENT == ERRNO_NOENT); 134 | static_assert(ENOEXEC == ERRNO_NOEXEC); 135 | static_assert(ENOLCK == ERRNO_NOLCK); 136 | static_assert(ENOLINK == ERRNO_NOLINK); 137 | static_assert(ENOMEM == ERRNO_NOMEM); 138 | static_assert(ENOMSG == ERRNO_NOMSG); 139 | static_assert(ENOPROTOOPT == ERRNO_NOPROTOOPT); 140 | static_assert(ENOSPC == ERRNO_NOSPC); 141 | static_assert(ENOSYS == ERRNO_NOSYS); 142 | static_assert(ENOTCONN == ERRNO_NOTCONN); 143 | static_assert(ENOTDIR == ERRNO_NOTDIR); 144 | static_assert(ENOTEMPTY == ERRNO_NOTEMPTY); 145 | static_assert(ENOTRECOVERABLE == ERRNO_NOTRECOVERABLE); 146 | static_assert(ENOTSOCK == ERRNO_NOTSOCK); 147 | static_assert(ENOTSUP == ERRNO_NOTSUP); 148 | static_assert(ENOTTY == ERRNO_NOTTY); 149 | static_assert(ENXIO == ERRNO_NXIO); 150 | static_assert(EOVERFLOW == ERRNO_OVERFLOW); 151 | static_assert(EOWNERDEAD == ERRNO_OWNERDEAD); 152 | static_assert(EPERM == ERRNO_PERM); 153 | static_assert(EPIPE == ERRNO_PIPE); 154 | static_assert(EPROTO == ERRNO_PROTO); 155 | static_assert(EPROTONOSUPPORT == ERRNO_PROTONOSUPPORT); 156 | static_assert(EPROTOTYPE == ERRNO_PROTOTYPE); 157 | static_assert(ERANGE == ERRNO_RANGE); 158 | static_assert(EROFS == ERRNO_ROFS); 159 | static_assert(ESPIPE == ERRNO_SPIPE); 160 | static_assert(ESRCH == ERRNO_SRCH); 161 | static_assert(ESTALE == ERRNO_STALE); 162 | static_assert(ETIMEDOUT == ERRNO_TIMEDOUT); 163 | static_assert(ETXTBSY == ERRNO_TXTBSY); 164 | static_assert(EXDEV == ERRNO_XDEV); 165 | static_assert(ENOTCAPABLE == ERRNO_NOTCAPABLE); 166 | -------------------------------------------------------------------------------- /lib/src/fcntl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static_assert(O_APPEND == FDFLAGS_APPEND); 10 | static_assert(O_DSYNC == FDFLAGS_DSYNC); 11 | static_assert(O_NONBLOCK == FDFLAGS_NONBLOCK); 12 | static_assert(O_RSYNC == FDFLAGS_RSYNC); 13 | static_assert(O_SYNC == FDFLAGS_SYNC); 14 | static_assert(O_CREAT == OFLAGS_CREAT << 12); 15 | static_assert(O_DIRECTORY == OFLAGS_DIRECTORY << 12); 16 | static_assert(O_EXCL == OFLAGS_EXCL << 12); 17 | static_assert(O_TRUNC == OFLAGS_TRUNC << 12); 18 | 19 | static_assert(POSIX_FADV_DONTNEED == ADVICE_DONTNEED); 20 | static_assert(POSIX_FADV_NOREUSE == ADVICE_NOREUSE); 21 | static_assert(POSIX_FADV_NORMAL == ADVICE_NORMAL); 22 | static_assert(POSIX_FADV_RANDOM == ADVICE_RANDOM); 23 | static_assert(POSIX_FADV_SEQUENTIAL == ADVICE_SEQUENTIAL); 24 | static_assert(POSIX_FADV_WILLNEED == ADVICE_WILLNEED); 25 | 26 | 27 | typedef struct preopen { 28 | const char *prefix; 29 | fd_t fd; 30 | } preopen_t; 31 | 32 | static preopen_t *preopens; 33 | static size_t num_preopens; 34 | static size_t preopen_capacity; 35 | static int preopens_populated; 36 | static int preopen_sep = '/'; 37 | 38 | static int resize_preopens(void) 39 | { 40 | size_t start_capacity = 4; 41 | size_t old_capacity = preopen_capacity; 42 | size_t new_capacity = old_capacity == 0 ? start_capacity : old_capacity * 2; 43 | 44 | preopen_t *old_preopens = preopens; 45 | preopen_t *new_preopens = calloc(sizeof(preopen_t), new_capacity); 46 | if (new_preopens == NULL) return -1; 47 | 48 | memcpy(new_preopens, old_preopens, num_preopens * sizeof(preopen_t)); 49 | preopens = new_preopens; 50 | preopen_capacity = new_capacity; 51 | free(old_preopens); 52 | 53 | return 0; 54 | } 55 | 56 | static const char *strip_prefixes(const char *path) 57 | { 58 | while (1) { 59 | if (path[0] == preopen_sep) { 60 | path++; 61 | } else if (path[0] == '.' && path[1] == preopen_sep) { 62 | path += 2; 63 | } else if (path[0] == '.' && path[1] == 0) { 64 | path++; 65 | } else { 66 | break; 67 | } 68 | } 69 | return path; 70 | } 71 | 72 | static int register_preopened_fd(fd_t fd, const char *relprefix) 73 | { 74 | if (num_preopens == preopen_capacity && resize_preopens() != 0) return -1; 75 | char *prefix = strdup(strip_prefixes(relprefix)); 76 | if (prefix == NULL) return -1; 77 | preopen_t *pp = &preopens[num_preopens++]; 78 | pp->prefix = prefix; 79 | pp->fd = fd; 80 | return 0; 81 | } 82 | 83 | static int populate_preopens(void) 84 | { 85 | fd_t fd; 86 | if (preopens_populated) return 0; 87 | preopens_populated = true; 88 | for (fd = 3; fd != 0; ++fd) { 89 | prestat_t prestat; 90 | errno_t ret = fd_prestat_get(fd, &prestat); 91 | if (ret == ERRNO_BADF) break; 92 | if (ret != ERRNO_SUCCESS) return 1; 93 | if (prestat.tag == PREOPENTYPE_DIR) { 94 | char *prefix = malloc(prestat.u.dir.pr_name_len + 1); 95 | if (!prefix) return 2; 96 | ret = fd_prestat_dir_name(fd, (uint8_t *)prefix, prestat.u.dir.pr_name_len); 97 | if (ret != ERRNO_SUCCESS) return 3; 98 | prefix[prestat.u.dir.pr_name_len] = '\0'; 99 | if (prestat.u.dir.pr_name_len > 0 && prefix[prestat.u.dir.pr_name_len-1] == '\\') { 100 | preopen_sep = '\\'; /* windows path conventions */ 101 | } 102 | register_preopened_fd(fd, prefix); 103 | free(prefix); 104 | } 105 | } 106 | return 0; 107 | } 108 | 109 | static bool prefix_matches(const char *prefix, size_t prefix_len, const char *path) 110 | { 111 | if (path[0] != preopen_sep && prefix_len == 0) return true; 112 | if (memcmp(path, prefix, prefix_len) != 0) return false; 113 | size_t i = prefix_len; 114 | while (i > 0 && prefix[i-1] == preopen_sep) --i; 115 | char last = path[i]; 116 | return last == preopen_sep || last == '\0'; 117 | } 118 | 119 | static int find_abspath(const char *path, const char **abs_prefix, const char **relative_path) 120 | { 121 | while (*path == preopen_sep) ++path; 122 | size_t match_len = 0, i; 123 | int fd = -1; 124 | for (i = num_preopens; i > 0; --i) { 125 | const preopen_t *pre = &preopens[i-1]; 126 | const char *prefix = pre->prefix; 127 | size_t len = strlen(prefix); 128 | if ((fd == -1 || len > match_len) && prefix_matches(prefix, len, path)) { 129 | fd = pre->fd; 130 | match_len = len; 131 | *abs_prefix = prefix; 132 | } 133 | } 134 | if (fd == -1) { 135 | errno = ENOENT; 136 | return -1; 137 | } 138 | const char *computed = path + match_len; 139 | while (*computed == preopen_sep) ++computed; 140 | if (*computed == '\0') computed = "."; 141 | *relative_path = computed; 142 | return fd; 143 | } 144 | 145 | int find_relpath(const char *path, char **relative_path) 146 | { 147 | const char *abs_prefix; 148 | if (!preopens_populated) populate_preopens(); // todo: check ret value! 149 | return find_abspath(path, &abs_prefix, (const char**)relative_path); 150 | } 151 | 152 | int openat(int fd, const char *path, int oflags) 153 | { 154 | rights_t max = 155 | ~(RIGHTS_FD_DATASYNC | RIGHTS_FD_READ | 156 | RIGHTS_FD_WRITE | RIGHTS_FD_ALLOCATE | 157 | RIGHTS_FD_READDIR | RIGHTS_FD_FILESTAT_SET_SIZE); 158 | switch (oflags & O_ACCMODE) { 159 | case O_RDONLY: 160 | case O_RDWR: 161 | case O_WRONLY: 162 | if ((oflags & O_RDONLY) != 0) { 163 | max |= RIGHTS_FD_READ | RIGHTS_FD_READDIR; 164 | } 165 | if ((oflags & O_WRONLY) != 0) { 166 | max |= RIGHTS_FD_DATASYNC | RIGHTS_FD_WRITE | 167 | RIGHTS_FD_ALLOCATE | 168 | RIGHTS_FD_FILESTAT_SET_SIZE; 169 | } 170 | break; 171 | case O_EXEC: 172 | break; 173 | case O_SEARCH: 174 | break; 175 | default: 176 | errno = EINVAL; 177 | return -1; 178 | } 179 | 180 | fdstat_t fsb_cur; 181 | errno_t error = fd_fdstat_get(fd, &fsb_cur); 182 | if (error != 0) { 183 | errno = (int)error; 184 | return -1; 185 | } 186 | 187 | lookupflags_t lookup_flags = 0; 188 | if ((oflags & O_NOFOLLOW) == 0) 189 | lookup_flags |= LOOKUPFLAGS_SYMLINK_FOLLOW; 190 | 191 | fdflags_t fs_flags = oflags & 0xfff; 192 | rights_t fs_rights_base = max & fsb_cur.fs_rights_inheriting; 193 | rights_t fs_rights_inheriting = fsb_cur.fs_rights_inheriting; 194 | fd_t newfd; 195 | error = path_open(fd, lookup_flags, path, strlen(path), 196 | (oflags_t)((oflags >> 12) & 0xfff), 197 | fs_rights_base, fs_rights_inheriting, fs_flags, 198 | &newfd); 199 | if (error != 0) { 200 | errno = (int)error; 201 | return -1; 202 | } 203 | return newfd; 204 | } 205 | 206 | int open(const char *path, int oflags) 207 | { 208 | char *relpath; 209 | int dirfd = find_relpath(path, &relpath); 210 | if (dirfd == -1) { 211 | errno = ENOTCAPABLE; 212 | return -1; 213 | } 214 | return openat(dirfd, relpath, oflags); 215 | } 216 | 217 | int fcntl(int fd, int cmd, ...) 218 | { 219 | switch (cmd) { 220 | case F_GETFD: 221 | return FD_CLOEXEC; 222 | case F_SETFD: 223 | return 0; 224 | case F_GETFL: { 225 | fdstat_t fds; 226 | errno_t error = fd_fdstat_get(fd, &fds); 227 | if (error != 0) { 228 | errno = error; 229 | return -1; 230 | } 231 | int oflags = fds.fs_flags; 232 | if ((fds.fs_rights_base & (RIGHTS_FD_READ | RIGHTS_FD_READDIR)) != 0) { 233 | if ((fds.fs_rights_base & RIGHTS_FD_WRITE) != 0) { 234 | oflags |= O_RDWR; 235 | } else { 236 | oflags |= O_RDONLY; 237 | } 238 | } else if ((fds.fs_rights_base & RIGHTS_FD_WRITE) != 0) { 239 | oflags |= O_WRONLY; 240 | } else { 241 | oflags |= O_SEARCH; 242 | } 243 | return oflags; 244 | } 245 | case F_SETFL: { 246 | va_list ap; 247 | va_start(ap, cmd); 248 | int flags = va_arg(ap, int); 249 | va_end(ap); 250 | fdflags_t fs_flags = flags & 0xfff; 251 | errno_t error = fd_fdstat_set_flags(fd, fs_flags); 252 | if (error != 0) { 253 | errno = error; 254 | return -1; 255 | } 256 | return 0; 257 | } 258 | } 259 | errno = EINVAL; 260 | return -1; 261 | } 262 | 263 | 264 | int posix_fadvise(int fd, off_t offset, off_t len, int advice) 265 | { 266 | if (offset < 0 || len < 0) 267 | return EINVAL; 268 | return fd_advise(fd, offset, len, (advice_t)advice); 269 | } 270 | 271 | int posix_fallocate(int fd, off_t offset, off_t len) 272 | { 273 | if (offset < 0 || len < 0) 274 | return EINVAL; 275 | return fd_allocate(fd, offset, len); 276 | } 277 | 278 | -------------------------------------------------------------------------------- /lib/src/fenv.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* NB: WASM does not support one, so this is a fake */ 4 | 5 | void feclearexcept(int mask) 6 | { 7 | } 8 | 9 | void fegetexceptflag(fexcept_t* flagp, int excepts) 10 | { 11 | } 12 | 13 | void feraiseexcept(int excepts) 14 | { 15 | } 16 | 17 | void fesetexceptflag(const fexcept_t* flagp, int excepts) 18 | { 19 | } 20 | 21 | int fetestexcept(int excepts) 22 | { 23 | return 0; 24 | } 25 | 26 | int fegetround(void) 27 | { 28 | return FE_TONEAREST; 29 | } 30 | 31 | int fesetround(int round) 32 | { 33 | return 0; 34 | } 35 | 36 | void fegetenv(fenv_t *envp) 37 | { 38 | } 39 | 40 | int feholdexcept(fenv_t* envp) 41 | { 42 | return 0; 43 | } 44 | 45 | void fesetenv(const fenv_t *envp) 46 | { 47 | } 48 | 49 | void feupdateenv(const fenv_t* envp) 50 | { 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/stat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static_assert(S_ISBLK(S_IFBLK)); 9 | static_assert(S_ISCHR(S_IFCHR)); 10 | static_assert(S_ISDIR(S_IFDIR)); 11 | static_assert(S_ISFIFO(S_IFIFO)); 12 | static_assert(S_ISLNK(S_IFLNK)); 13 | static_assert(S_ISREG(S_IFREG)); 14 | static_assert(S_ISSOCK(S_IFSOCK)); 15 | 16 | static void timestamp_to_timespec(timestamp_t timestamp, time_t *pt, long *pns) 17 | { 18 | /* timestamp is in nanoseconds */ 19 | *pt = (time_t)(timestamp / 1000000000); 20 | *pns = (long)(timestamp % 1000000000); 21 | } 22 | 23 | static void to_public_stat(const filestat_t *in, struct stat *out) 24 | { 25 | out->st_dev = in->dev; 26 | out->st_ino = in->ino; 27 | out->st_nlink = in->nlink; 28 | static_assert(sizeof(off_t) == sizeof(filesize_t)); 29 | out->st_size = (off_t)in->size; 30 | timestamp_to_timespec(in->atim, &out->st_atime, &out->st_atime_ns); 31 | timestamp_to_timespec(in->mtim, &out->st_mtime, &out->st_mtime_ns); 32 | timestamp_to_timespec(in->ctim, &out->st_ctime, &out->st_ctime_ns); 33 | switch (in->filetype) { 34 | case FILETYPE_BLOCK_DEVICE: 35 | out->st_mode |= S_IFBLK; 36 | break; 37 | case FILETYPE_CHARACTER_DEVICE: 38 | out->st_mode |= S_IFCHR; 39 | break; 40 | case FILETYPE_DIRECTORY: 41 | out->st_mode |= S_IFDIR; 42 | break; 43 | case FILETYPE_REGULAR_FILE: 44 | out->st_mode |= S_IFREG; 45 | break; 46 | case FILETYPE_SOCKET_DGRAM: 47 | case FILETYPE_SOCKET_STREAM: 48 | out->st_mode |= S_IFSOCK; 49 | break; 50 | case FILETYPE_SYMBOLIC_LINK: 51 | out->st_mode |= S_IFLNK; 52 | break; 53 | } 54 | } 55 | 56 | int fstat(int fd, struct stat *ps) 57 | { 58 | filestat_t fs; 59 | errno_t error = fd_filestat_get(fd, &fs); 60 | if (error != 0) { 61 | errno = (int)error; 62 | return -1; 63 | } 64 | to_public_stat(&fs, ps); 65 | return 0; 66 | } 67 | 68 | int fstatat(int fd, const char *path, struct stat *ps, int atflag) 69 | { 70 | lookupflags_t lflags = 0; 71 | if ((atflag & AT_SYMLINK_NOFOLLOW) == 0) 72 | lflags |= LOOKUPFLAGS_SYMLINK_FOLLOW; 73 | filestat_t fs; 74 | errno_t error = path_filestat_get(fd, lflags, path, strlen(path), &fs); 75 | if (error != 0) { 76 | errno = (int)error; 77 | return -1; 78 | } 79 | to_public_stat(&fs, ps); 80 | return 0; 81 | } 82 | 83 | int stat(const char *path, struct stat *ps) 84 | { 85 | char *relpath; 86 | int dirfd = find_relpath(path, &relpath); 87 | if (dirfd == -1) { 88 | errno = ENOTCAPABLE; 89 | return -1; 90 | } 91 | return fstatat(dirfd, relpath, ps, 0); 92 | } 93 | 94 | int lstat(const char *path, struct stat *ps) 95 | { 96 | char *relpath; 97 | int dirfd = find_relpath(path, &relpath); 98 | if (dirfd == -1) { 99 | errno = ENOTCAPABLE; 100 | return -1; 101 | } 102 | return fstatat(dirfd, relpath, ps, AT_SYMLINK_NOFOLLOW); 103 | } 104 | 105 | int mkdirat(int fd, const char *path, mode_t mode) 106 | { 107 | errno_t error = path_create_directory(fd, path, strlen(path)); 108 | if (error != 0) { 109 | errno = (int)error; 110 | return -1; 111 | } 112 | return 0; 113 | } 114 | 115 | int mkdir(const char *path, mode_t mode) 116 | { 117 | char *relpath; 118 | int dirfd = find_relpath(path, &relpath); 119 | if (dirfd == -1) { 120 | errno = ENOTCAPABLE; 121 | return -1; 122 | } 123 | return mkdirat(dirfd, relpath, mode); 124 | } 125 | -------------------------------------------------------------------------------- /lib/src/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void bzero(void *dst, size_t n) 8 | { 9 | _bzero(dst, n); 10 | } 11 | 12 | void *memccpy(void *dst, const void *src, int c, size_t n) 13 | { 14 | char *q = dst; const char *p = src; 15 | char ch; 16 | while (n--) { 17 | *q++ = ch = *p++; 18 | if (ch == (char)c) return q; 19 | } 20 | return NULL; 21 | } 22 | 23 | void *memchr(const void *s, int c, size_t n) 24 | { 25 | const char *sp = s; 26 | while (n--) { 27 | if (*sp == (char)c) return (void *)sp; 28 | sp++; 29 | } 30 | return NULL; 31 | } 32 | 33 | int memcmp(const void *s1, const void *s2, size_t n) 34 | { 35 | const char *c1 = s1, *c2 = s2; 36 | int d = 0; 37 | while (n--) { 38 | d = (unsigned char)*c1++ - (unsigned char)*c2++; 39 | if (d) break; 40 | } 41 | return d; 42 | } 43 | 44 | void *memcpy(void *dst, const void *src, size_t n) 45 | { 46 | _bcopy(dst, src, n); 47 | return dst; 48 | } 49 | 50 | /* See http://www-igm.univ-mlv.fr/~lecroq/string/ */ 51 | void *memmem(const void *haystack, size_t n, const void *needle, size_t m) 52 | { 53 | const unsigned char *y = (const unsigned char *)haystack; 54 | const unsigned char *x = (const unsigned char *)needle; 55 | size_t j, k, l; 56 | if (m > n || !m || !n) 57 | return NULL; 58 | if (1 != m) { 59 | if (x[0] == x[1]) { 60 | k = 2; l = 1; 61 | } else { 62 | k = 1; l = 2; 63 | } 64 | j = 0; 65 | while (j <= n - m) { 66 | if (x[1] != y[j + 1]) { 67 | j += k; 68 | } else { 69 | if (!memcmp(x + 2, y + j + 2, m - 2) && x[0] == y[j]) 70 | return (void *)&y[j]; 71 | j += l; 72 | } 73 | } 74 | } else { 75 | do { 76 | if (*y == *x) return (void *)y; 77 | y++; 78 | } while (--n); 79 | } 80 | return NULL; 81 | } 82 | 83 | void *memmove(void *dst, const void *src, size_t n) 84 | { 85 | _bcopy(dst, src, n); 86 | return dst; 87 | } 88 | 89 | void *memset(void *dst, int c, size_t n) 90 | { 91 | _bfill(dst, c, n); 92 | return dst; 93 | } 94 | 95 | void memswap(void *m1, void *m2, size_t n) 96 | { 97 | char *p = m1, *q = m2; 98 | while (n--) { 99 | int tmp = *p; 100 | *p = *q; *q = tmp; 101 | p++; q++; 102 | } 103 | } 104 | 105 | int strcasecmp(const char *s1, const char *s2) 106 | { 107 | const unsigned char *c1 = (const unsigned char *)s1; 108 | const unsigned char *c2 = (const unsigned char *)s2; 109 | unsigned char ch; int d = 0; 110 | while (1) { 111 | d = tolower(ch = *c1++) - tolower(*c2++); 112 | if (d || !ch) break; 113 | } 114 | return d; 115 | } 116 | 117 | char *strcat(char *dst, const char *src) 118 | { 119 | strcpy(strchr(dst, '\0'), src); 120 | return dst; 121 | } 122 | 123 | char *strchr(const char *s, int c) 124 | { 125 | while (*s != (char)c) { 126 | if (!*s) return NULL; 127 | s++; 128 | } 129 | return (char *)s; 130 | } 131 | 132 | int strcmp(const char *s1, const char *s2) 133 | { 134 | const unsigned char *c1 = (const unsigned char *)s1; 135 | const unsigned char *c2 = (const unsigned char *)s2; 136 | unsigned char ch; int d = 0; 137 | while (1) { 138 | d = (int)(ch = *c1++) - (int)*c2++; 139 | if (d || !ch) break; 140 | } 141 | return d; 142 | } 143 | 144 | char *strcpy(char *dst, const char *src) 145 | { 146 | char *q = dst, ch; const char *p = src; 147 | do { *q++ = ch = *p++; } while (ch); 148 | return dst; 149 | } 150 | 151 | size_t strcspn(const char *s1, const char *s2) 152 | { 153 | const char *s = s1, *c; 154 | while (*s1) { 155 | for (c = s2; *c; c++) { 156 | if (*s1 == *c) break; 157 | } 158 | if (*c) break; 159 | s1++; 160 | } 161 | return s1 - s; 162 | } 163 | 164 | char *strdup(const char *s) 165 | { 166 | size_t l = strlen(s) + 1; 167 | char *d = malloc(l); 168 | if (d) _bcopy(d, s, l); 169 | return d; 170 | } 171 | 172 | size_t strlen(const char *s) 173 | { 174 | const char *ss = s; 175 | while (*ss) ss++; 176 | return ss - s; 177 | } 178 | 179 | int strncasecmp(const char *s1, const char *s2, size_t n) 180 | { 181 | const unsigned char *c1 = (const unsigned char *)s1; 182 | const unsigned char *c2 = (const unsigned char *)s2; 183 | unsigned char ch; int d = 0; 184 | while (n--) { 185 | d = toupper(ch = *c1++) - toupper(*c2++); 186 | if (d || !ch) break; 187 | } 188 | return d; 189 | } 190 | 191 | char *strncat(char *dst, const char *src, size_t n) 192 | { 193 | char *q = strchr(dst, '\0'); 194 | const char *p = src; 195 | char ch; 196 | while (n--) { 197 | *q++ = ch = *p++; 198 | if (!ch) 199 | return dst; 200 | } 201 | *q = '\0'; 202 | return dst; 203 | } 204 | 205 | int strncmp(const char *s1, const char *s2, size_t n) 206 | { 207 | const unsigned char *c1 = (const unsigned char *)s1; 208 | const unsigned char *c2 = (const unsigned char *)s2; 209 | unsigned char ch; int d = 0; 210 | while (n--) { 211 | d = (int)(ch = *c1++) - (int)*c2++; 212 | if (d || !ch) break; 213 | } 214 | return d; 215 | } 216 | 217 | char *strncpy(char *dst, const char *src, size_t n) 218 | { 219 | char ch, *q = dst; const char *p = src; 220 | while (n) { 221 | n--; *q++ = ch = *p++; 222 | if (!ch) break; 223 | } 224 | /* The specs say strncpy() fills the entire buffer with 0 */ 225 | _bzero(q, n); 226 | return dst; 227 | } 228 | 229 | char *strndup(const char *s, size_t n) 230 | { 231 | size_t sl = strlen(s), l = (n > sl ? sl : n) + 1; 232 | char *d = malloc(l); 233 | if (!d) return NULL; 234 | _bcopy(d, s, l); 235 | d[n] = '\0'; 236 | return d; 237 | } 238 | 239 | size_t strnlen(const char *s, size_t maxlen) 240 | { 241 | const char *ss = s; 242 | while ((maxlen > 0) && *ss) { ss++; maxlen--; } 243 | return ss - s; 244 | } 245 | 246 | char *strpbrk(const char *s1, const char *s2) 247 | { 248 | const char *c = s2; 249 | if (!*s1) return (char *)NULL; 250 | while (*s1) { 251 | for (c = s2; *c; c++) { 252 | if (*s1 == *c) break; 253 | } 254 | if (*c) break; 255 | s1++; 256 | } 257 | if (*c == '\0') s1 = NULL; 258 | return (char *)s1; 259 | } 260 | 261 | char *strrchr(const char *s, int c) 262 | { 263 | const char *found = NULL; 264 | while (*s) { 265 | if (*s == (char)c) found = s; 266 | s++; 267 | } 268 | return (char *)found; 269 | } 270 | 271 | size_t strspn(const char *s1, const char *s2) 272 | { 273 | const char *s = s1, *c; 274 | while (*s1) { 275 | for (c = s2; *c; c++) { 276 | if (*s1 == *c) break; 277 | } 278 | if (*c == '\0') break; 279 | s1++; 280 | } 281 | return s1 - s; 282 | } 283 | 284 | char *strstr(const char *haystack, const char *needle) 285 | { 286 | return (char *)memmem(haystack, strlen(haystack), needle, 287 | strlen(needle)); 288 | } 289 | 290 | static char *strsep(char **stringp, const char *delim) 291 | { 292 | char *s = *stringp, *e; 293 | if (!s) return NULL; 294 | e = strpbrk(s, delim); 295 | if (e) *e++ = '\0'; 296 | *stringp = e; 297 | return s; 298 | } 299 | 300 | char *strtok_r(char *s, const char *delim, char **holder) 301 | { 302 | if (s) *holder = s; 303 | do s = strsep(holder, delim); while (s && !*s); 304 | return s; 305 | } 306 | 307 | static char *strtok_holder; 308 | 309 | char *strtok(char *s, const char *delim) 310 | { 311 | char *holder = s ? s : strtok_holder; 312 | do s = strsep(&holder, delim); while (s && !*s); 313 | strtok_holder = holder; 314 | return s; 315 | } 316 | 317 | int strcoll(const char *s1, const char *s2) 318 | { 319 | /* fake */ 320 | return strcmp(s1, s2); 321 | } 322 | 323 | size_t strxfrm(char *dest, const char *src, size_t n) 324 | { 325 | /* fake */ 326 | strncpy(dest, src, n); 327 | return strlen(src); 328 | } 329 | 330 | const char *strerror(int errno) 331 | { 332 | if (errno < 0 || errno > ERRNO_MAXERR) return "?unknown error"; 333 | return _emsg[errno]; 334 | } -------------------------------------------------------------------------------- /lib/src/unistd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static_assert(SEEK_SET == WHENCE_SET); 10 | static_assert(SEEK_CUR == WHENCE_CUR); 11 | static_assert(SEEK_END == WHENCE_END); 12 | static_assert(sizeof(off_t) == sizeof(filesize_t)); 13 | static_assert(sizeof(off_t) == sizeof(filedelta_t)); 14 | 15 | ssize_t read(int fd, void *buf, size_t nbyte) 16 | { 17 | iovec_t iov; 18 | errno_t err; 19 | size_t bytes_read; 20 | iov.buf = buf; 21 | iov.buf_len = nbyte; 22 | err = fd_read(fd, &iov, 1, &bytes_read); 23 | if (err != 0) { 24 | errno = (err == ERRNO_NOTCAPABLE) ? EBADF : (int)err; 25 | return -1; 26 | } 27 | return (ssize_t)bytes_read; 28 | } 29 | 30 | ssize_t write(int fd, const void *buf, size_t nbyte) 31 | { 32 | ciovec_t iov; errno_t err; size_t bytes_written; 33 | iov.buf = buf; iov.buf_len = nbyte; 34 | err = fd_write(fd, &iov, 1, &bytes_written); 35 | if (err != 0) { 36 | errno = (err == ERRNO_NOTCAPABLE) ? EBADF : (int)err; 37 | return -1; 38 | } 39 | return (ssize_t)bytes_written; 40 | } 41 | 42 | ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset) 43 | { 44 | iovec_t iov; 45 | size_t bytes_read; 46 | errno_t err; 47 | if (offset < 0) { 48 | errno = EINVAL; 49 | return -1; 50 | } 51 | iov.buf = buf; 52 | iov.buf_len = nbyte; 53 | err = fd_pread(fd, &iov, 1, offset, &bytes_read); 54 | if (err != 0) { 55 | fdstat_t fds; 56 | if (err == ERRNO_NOTCAPABLE && fd_fdstat_get(fd, &fds) == 0) { 57 | if ((fds.fs_rights_base & RIGHTS_FD_READ) == 0) err = EBADF; 58 | else err = ESPIPE; 59 | } 60 | errno = (int)err; 61 | return -1; 62 | } 63 | return (ssize_t)bytes_read; 64 | } 65 | 66 | ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset) 67 | { 68 | ciovec_t iov; 69 | size_t bytes_written; 70 | errno_t err; 71 | if (offset < 0) { 72 | errno = EINVAL; 73 | return -1; 74 | } 75 | iov.buf = buf; iov.buf_len = nbyte; 76 | err = fd_pwrite(fd, &iov, 1, offset, &bytes_written); 77 | if (err != 0) { 78 | fdstat_t fds; 79 | if (err == ERRNO_NOTCAPABLE && fd_fdstat_get(fd, &fds) == 0) { 80 | if ((fds.fs_rights_base & RIGHTS_FD_WRITE) == 0) err = EBADF; 81 | else err = ESPIPE; 82 | } 83 | errno = (int)err; 84 | return -1; 85 | } 86 | return (ssize_t)bytes_written; 87 | } 88 | 89 | off_t lseek(int fd, off_t offset, int whence) 90 | { 91 | filesize_t new_offset; 92 | errno_t err = fd_seek(fd, (filedelta_t)offset, (whence_t)whence, &new_offset); 93 | if (err != 0) { 94 | errno = (err == ERRNO_NOTCAPABLE) ? ESPIPE : (int)err; 95 | return -1; 96 | } 97 | return (off_t)new_offset; 98 | } 99 | 100 | int ftruncate(int fd, off_t length) 101 | { 102 | filesize_t sz = (filesize_t)length; 103 | if (length < 0) { 104 | errno = EINVAL; 105 | return -1; 106 | } 107 | errno_t err = fd_filestat_set_size(fd, sz); 108 | if (err != 0) { 109 | errno = (int)err; 110 | return -1; 111 | } 112 | return 0; 113 | } 114 | 115 | int close(int fd) 116 | { 117 | errno_t err = fd_close(fd); 118 | if (err != 0) { 119 | errno = (int)err; 120 | return -1; 121 | } 122 | return 0; 123 | } 124 | 125 | int isatty(int fd) 126 | { 127 | fdstat_t statbuf; 128 | int r = fd_fdstat_get(fd, &statbuf); 129 | if (r != 0) { 130 | errno = r; 131 | return 0; 132 | } 133 | if (statbuf.fs_filetype != FILETYPE_CHARACTER_DEVICE || 134 | (statbuf.fs_rights_base & (RIGHTS_FD_SEEK | RIGHTS_FD_TELL)) != 0) { 135 | errno = (int)ERRNO_NOTTY; 136 | return 0; 137 | } 138 | return 1; 139 | } 140 | 141 | void *sbrk(intptr_t inc) /* inc should be a multiple of 64K */ 142 | { 143 | assert(inc >= 0 && (inc & 0xFFFF) == 0); 144 | if (!inc) return (void *)asm(memory.size, i32.const 16, i32.shl); 145 | size_t new = (size_t)inc >> 16; 146 | size_t old = (size_t)asm(i32.const 0, local.get new, memory.grow); 147 | if (old == SIZE_MAX) { errno = ENOMEM; return (void *)-1; } 148 | return (void *)(old << 16); 149 | } 150 | 151 | int faccessat(int fd, const char *path, int amode, int atflag) 152 | { 153 | if ((amode & ~(F_OK | R_OK | W_OK | X_OK)) != 0 || 154 | (atflag & ~AT_EACCESS) != 0) { 155 | errno = EINVAL; 156 | return -1; 157 | } 158 | lookupflags_t lookup_flags = LOOKUPFLAGS_SYMLINK_FOLLOW; 159 | filestat_t file; 160 | errno_t error = path_filestat_get(fd, lookup_flags, path, strlen(path), &file); 161 | if (error != 0) { 162 | errno = (int)error; 163 | return -1; 164 | } 165 | if (amode != 0) { 166 | fdstat_t directory; 167 | error = fd_fdstat_get(fd, &directory); 168 | if (error != 0) { 169 | errno = (int)error; 170 | return -1; 171 | } 172 | rights_t min = 0; 173 | if ((amode & R_OK) != 0) 174 | min |= file.filetype == FILETYPE_DIRECTORY ? RIGHTS_FD_READDIR : RIGHTS_FD_READ; 175 | if ((amode & W_OK) != 0) 176 | min |= RIGHTS_FD_WRITE; 177 | if ((min & directory.fs_rights_inheriting) != min) { 178 | errno = EACCES; 179 | return -1; 180 | } 181 | } 182 | return 0; 183 | } 184 | 185 | int access(const char *path, int amode) 186 | { 187 | char *relpath; 188 | int dirfd = find_relpath(path, &relpath); 189 | if (dirfd == -1) { 190 | errno = ENOTCAPABLE; 191 | return -1; 192 | } 193 | return faccessat(dirfd, relpath, amode, 0); 194 | } 195 | 196 | int unlinkat(int fd, const char *path, int atflag) 197 | { 198 | errno_t error; 199 | if ((atflag & AT_REMOVEDIR) != 0) 200 | error = path_remove_directory(fd, path, strlen(path)); 201 | else 202 | error = path_unlink_file(fd, path, strlen(path)); 203 | if (error != 0) { 204 | errno = (int)error; 205 | return -1; 206 | } 207 | return 0; 208 | } 209 | 210 | int unlink(const char *path) 211 | { 212 | char *relpath; 213 | int dirfd = find_relpath(path, &relpath); 214 | if (dirfd == -1) { 215 | errno = ENOTCAPABLE; 216 | return -1; 217 | } 218 | return unlinkat(dirfd, relpath, 0); 219 | } 220 | 221 | int linkat(int oldfd, const char *oldpath, int newfd, const char *newpath, int atflag) 222 | { 223 | lookupflags_t lookup_flags = 0; 224 | if ((atflag & AT_SYMLINK_FOLLOW) != 0) 225 | lookup_flags |= LOOKUPFLAGS_SYMLINK_FOLLOW; 226 | errno_t error = path_link(oldfd, lookup_flags, 227 | oldpath, strlen(oldpath), newfd, newpath, strlen(newpath)); 228 | if (error != 0) { 229 | errno = (int)error; 230 | return -1; 231 | } 232 | return 0; 233 | } 234 | 235 | int link(const char *oldpath, const char *newpath) 236 | { 237 | char *oldrelpath; 238 | int olddirfd = find_relpath(oldpath, &oldrelpath); 239 | if (olddirfd != -1) { 240 | char *newrelpath; 241 | int newdirfd = find_relpath(newpath, &newrelpath); 242 | if (newdirfd != -1) 243 | return linkat(olddirfd, oldrelpath, newdirfd, newrelpath, 0); 244 | } 245 | errno = ENOTCAPABLE; 246 | return -1; 247 | } 248 | 249 | int symlinkat(const char *tgtpath, int fd, const char *lnkpath) 250 | { 251 | errno_t error = path_symlink(tgtpath, strlen(tgtpath), fd, lnkpath, strlen(lnkpath)); 252 | if (error != 0) { 253 | errno = (int)error; 254 | return -1; 255 | } 256 | return 0; 257 | } 258 | 259 | int symlink(const char *tgtpath, const char *lnkpath) 260 | { 261 | char *relpath; 262 | int dirfd = find_relpath(lnkpath, &relpath); 263 | if (dirfd == -1) { 264 | errno = ENOTCAPABLE; 265 | return -1; 266 | } 267 | return symlinkat(tgtpath, dirfd, relpath); 268 | } 269 | 270 | ssize_t readlinkat(int fd, const char *path, char *buf, size_t bufsize) 271 | { 272 | size_t bufused; 273 | errno_t error = path_readlink(fd, path, strlen(path), (uint8_t*)buf, bufsize, &bufused); 274 | if (error != 0) { 275 | errno = (int)error; 276 | return -1; 277 | } 278 | return (ssize_t)bufused; 279 | } 280 | 281 | ssize_t readlink(const char *path, char *buf, size_t bufsize) 282 | { 283 | char *relpath; 284 | int dirfd = find_relpath(path, &relpath); 285 | if (dirfd == -1) { 286 | errno = ENOTCAPABLE; 287 | return -1; 288 | } 289 | return readlinkat(dirfd, relpath, buf, bufsize); 290 | } 291 | 292 | int rmdirat(int fd, const char *path) 293 | { 294 | errno_t error = path_remove_directory(fd, path, strlen(path)); 295 | if (error != 0) { 296 | errno = (int)error; 297 | return -1; 298 | } 299 | return 0; 300 | } 301 | 302 | int rmdir(const char *path) 303 | { 304 | char *relpath; 305 | int dirfd = find_relpath(path, &relpath); 306 | if (dirfd == -1) { 307 | errno = ENOTCAPABLE; 308 | return -1; 309 | } 310 | return rmdirat(dirfd, relpath); 311 | } 312 | 313 | int fdatasync(int fd) 314 | { 315 | errno_t error = fd_datasync(fd); 316 | if (error != 0) { 317 | errno = (error == ERRNO_NOTCAPABLE) ? EBADF : (int)error; 318 | return -1; 319 | } 320 | return 0; 321 | } 322 | 323 | int fsync(int fd) 324 | { 325 | errno_t error = fd_sync(fd); 326 | if (error != 0) { 327 | errno = (error == ERRNO_NOTCAPABLE) ? EINVAL : (int)error; 328 | return -1; 329 | } 330 | return 0; 331 | } 332 | 333 | void _exit(int status) 334 | { 335 | proc_exit(status); 336 | } 337 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | Tests 2 | ===== 3 | 4 | Few tests of WCPL compiler/library. 5 | 6 | ## stdio-tests 7 | 8 | Tests of `printf` and `scanf` implementation from the `stdio` module. 9 | Please note that in order to keep library code reasonably small, floating-point 10 | decimal conversions provide only 9 correct digits of precision, and the 11 | tests reflect that. 12 | 13 | ## math-tests 14 | 15 | Tests of numerical routines from the `math` module. 16 | Please note that tests depend on limited-precision decimal conversions, so 17 | test failures do not necessarily mean that math routines are incorrect; 18 | in almost all cases they are correct up to 16th decimal digit. 19 | --------------------------------------------------------------------------------